diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4079e35a9921c08885c0a04eae28449c9fcb629c..318f3e931e557421a5e9275735174cdee7947453 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,13 +1,24 @@ +variables: + RUN: + value: "all" + description: "The tests that should run. Possible values: ubuntu, win, all." + GIT_DEPTH: 1 + stages: - build - deploy build_ubuntu_gcc: + only: + variables: + - $RUN =~ /\bubuntu.*/i || $RUN =~ /\ball.*/i stage: build tags: - ubuntu-gcc variables: GIT_SUBMODULE_STRATEGY: recursive + timeout: 10m + retry: 1 script: - mkdir debug - cd debug @@ -21,11 +32,16 @@ build_ubuntu_gcc: expire_in: never build_win10_msvc: + only: + variables: + - $RUN =~ /\bwin.*/i || $RUN =~ /\ball.*/i stage: build tags: - win10-msvc variables: GIT_SUBMODULE_STRATEGY: recursive + timeout: 10m + retry: 1 script: - cd 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\' - .\Launch-VsDevShell.ps1 @@ -37,6 +53,8 @@ build_win10_msvc: deploy_doc_develop: only: + variables: + - $RUN =~ /\bubuntu.*/i || $RUN =~ /\ball.*/i refs: - develop stage: deploy @@ -52,6 +70,9 @@ deploy_doc_develop: - echo "Check it out at https://vkcv.de/develop" deploy_doc_branch: + only: + variables: + - $RUN =~ /\bubuntu.*/i || $RUN =~ /\ball.*/i except: refs: - develop diff --git a/.gitmodules b/.gitmodules index 4b9737e41ad03ecb661709c24531482f3d0e056d..983b753744e8767da0ec3c959c32a3766ee346f6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,9 @@ [submodule "lib/glfw"] path = lib/glfw url = https://github.com/glfw/glfw.git +[submodule "lib/SPIRV-Cross"] + path = lib/SPIRV-Cross + url = https://github.com/KhronosGroup/SPIRV-Cross.git [submodule "modules/asset_loader/lib/fx-gltf"] path = modules/asset_loader/lib/fx-gltf url = https://github.com/jessey-git/fx-gltf.git @@ -12,4 +15,4 @@ url = https://github.com/nothings/stb.git [submodule "modules/camera/lib/glm"] path = modules/camera/lib/glm - url = https://github.com/g-truc/glm.git + url = https://github.com/g-truc/glm.git \ No newline at end of file diff --git a/README.md b/README.md index 4289a74308776017f099d048cf749c9693e6609f..29c45f2660cb29d8ded6cdac14d1bf2db6b2ffc3 100644 --- a/README.md +++ b/README.md @@ -11,5 +11,17 @@ More information about Git LFS [here](https://git-lfs.github.com/). ## Build + [](https://gitlab.uni-koblenz.de/vulkan2021/vkcv-framework/-/commits/develop) + Git submodules are used for libraries. To download the submodules either clone using `git clone --recurse-submodules` or after `git clone` use `git submodule init` and `git submodule update`. + +## Documentation + +The documentation for the develop-branch can be found here: +https://vkcv.de/develop/ + +The documentation concerning the respective merge request is listed here: +https://vkcv.de/branch/ + +It is automatically generated and uploaded using the CI pipeline. diff --git a/config/Libraries.cmake b/config/Libraries.cmake index 13667936dd100be91ab43b73e6774e7db3da876c..e04aa3575a34632eb75c929bf4640305cd93e298 100644 --- a/config/Libraries.cmake +++ b/config/Libraries.cmake @@ -16,6 +16,7 @@ set(vkcv_config_msg " - Library: ") # load dependencies via separate cmake file include(${vkcv_config_lib}/GLFW.cmake) # glfw-x11 / glfw-wayland # libglfw3-dev include(${vkcv_config_lib}/Vulkan.cmake) # vulkan-intel / vulkan-radeon / nvidia # libvulkan-dev +include(${vkcv_config_lib}/SPIRV_Cross.cmake) # SPIRV-Cross # libspirv_cross_c_shared # cleanup of compiler flags if (vkcv_flags) diff --git a/config/Sources.cmake b/config/Sources.cmake index cb73f57c2ca7278765ef0c8d01989c09a445c7b5..8fc0aa9a2605a629596e26d5eeb0772164e6ec7a 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -24,6 +24,12 @@ set(vkcv_sources ${vkcv_include}/vkcv/BufferManager.hpp ${vkcv_source}/vkcv/BufferManager.cpp + ${vkcv_include}/vkcv/Image.hpp + ${vkcv_source}/vkcv/Image.cpp + + ${vkcv_source}/vkcv/ImageManager.hpp + ${vkcv_source}/vkcv/ImageManager.cpp + ${vkcv_include}/vkcv/SwapChain.hpp ${vkcv_source}/vkcv/SwapChain.cpp @@ -50,9 +56,9 @@ set(vkcv_sources ${vkcv_source}/vkcv/ImageLayoutTransitions.hpp ${vkcv_source}/vkcv/ImageLayoutTransitions.cpp - - ${vkcv_source}/vkcv/Framebuffer.hpp - ${vkcv_source}/vkcv/Framebuffer.cpp + + ${vkcv_include}/vkcv/VertexLayout.hpp + ${vkcv_source}/vkcv/VertexLayout.cpp ${vkcv_include}/vkcv/Event.hpp @@ -61,4 +67,9 @@ set(vkcv_sources ${vkcv_include}/vkcv/DescriptorConfig.hpp ${vkcv_source}/vkcv/DescriptorConfig.cpp + + ${vkcv_source}/vkcv/SamplerManager.hpp + ${vkcv_source}/vkcv/SamplerManager.cpp + + ${vkcv_include}/vkcv/DescriptorWrites.hpp ) diff --git a/config/lib/SPIRV_Cross.cmake b/config/lib/SPIRV_Cross.cmake new file mode 100644 index 0000000000000000000000000000000000000000..751ee883c47e0eab081a13e5805ced6f2daa7e30 --- /dev/null +++ b/config/lib/SPIRV_Cross.cmake @@ -0,0 +1,22 @@ +find_package(spirv_cross_c_shared QUIET) + +if (spirv-cross_FOUND) + list(APPEND vkcv_libraries spirv-cross-cpp) + + message(${vkcv_config_msg} " SPIRV Cross - " ${SPIRV_CROSS_VERSION}) +else() + if (EXISTS "${vkcv_lib_path}/SPIRV-Cross") + set(SPIRV_CROSS_CLI OFF CACHE INTERNAL "") + set(SPIRV_CROSS_ENABLE_TESTS OFF CACHE INTERNAL "") + set(SPIRV_CROSS_ENABLE_C_API OFF CACHE INTERNAL "") + set(SPIRV_CROSS_SKIP_INSTALL ON CACHE INTERNAL "") + + add_subdirectory(${vkcv_lib}/SPIRV-Cross) + + list(APPEND vkcv_libraries spirv-cross-cpp) + + message(${vkcv_config_msg} " SPIRV Cross - " ${SPIRV_CROSS_VERSION}) + else() + message(WARNING "SPIRV-Cross is required..! Update the submodules!") + endif () +endif () \ No newline at end of file diff --git a/include/vkcv/Buffer.hpp b/include/vkcv/Buffer.hpp index 5c21b1ce1d2d84afad6816fd8bf5f66a82b80941..d327067ce409e845bcac5e4c9f56e7de59df89c2 100644 --- a/include/vkcv/Buffer.hpp +++ b/include/vkcv/Buffer.hpp @@ -4,58 +4,76 @@ * @file vkcv/Buffer.hpp * @brief template buffer class, template for type security, implemented here because template classes can't be written in .cpp */ +#include "Handles.hpp" #include "BufferManager.hpp" +#include <vector> + namespace vkcv { template<typename T> class Buffer { + friend class Core; public: // explicit destruction of default constructor Buffer<T>() = delete; - - BufferType getType() { + + [[nodiscard]] + const BufferHandle& getHandle() const { + return m_handle; + } + + [[nodiscard]] + BufferType getType() const { return m_type; }; - size_t getCount() { + [[nodiscard]] + size_t getCount() const { return m_count; } - size_t getSize() { + [[nodiscard]] + size_t getSize() const { return m_count * sizeof(T); } - 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)); + void fill(const T* data, size_t count = 0, size_t offset = 0) { + m_manager->fillBuffer(m_handle, data, count * sizeof(T), offset * sizeof(T)); + } + + void fill(const std::vector<T>& vector, size_t offset = 0) { + fill( static_cast<const T*>(vector.data()), static_cast<size_t>(vector.size()), offset); } + [[nodiscard]] 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))); + return reinterpret_cast<T*>(m_manager->mapBuffer(m_handle, offset * sizeof(T), count * sizeof(T))); } 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); + m_manager->unmapBuffer(m_handle); } private: BufferManager* const m_manager; - const uint64_t m_handle_id; + const BufferHandle m_handle; 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) : + Buffer<T>(BufferManager* manager, BufferHandle handle, BufferType type, size_t count, BufferMemoryType memoryType) : m_manager(manager), - m_handle_id(id), + m_handle(handle), m_type(type), m_count(count), m_memoryType(memoryType) {} + [[nodiscard]] + 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); + } + }; } diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp index abda720f98f649d64b17a15848ae99320e11ae3e..322873b348dab7c45d446b5053d61379dfde0b6b 100644 --- a/include/vkcv/BufferManager.hpp +++ b/include/vkcv/BufferManager.hpp @@ -3,6 +3,8 @@ #include <vector> #include <vulkan/vulkan.hpp> +#include "Handles.hpp" + namespace vkcv { enum class BufferType { @@ -29,14 +31,14 @@ namespace vkcv { vk::Buffer m_handle; vk::DeviceMemory m_memory; - size_t m_size; + size_t m_size = 0; void* m_mapped = nullptr; - bool m_mappable; + bool m_mappable = false; }; Core* m_core; std::vector<Buffer> m_buffers; - uint64_t m_stagingBuffer; + BufferHandle m_stagingBuffer; BufferManager() noexcept; @@ -53,72 +55,82 @@ namespace vkcv /** * Creates and allocates a new buffer and returns its - * unique buffer handle id. + * unique buffer handle. * * @param type Type of buffer * @param size Size of buffer in bytes * @param memoryType Type of buffers memory - * @return New buffer handle id + * @return New buffer handle */ - uint64_t createBuffer(BufferType type, size_t size, BufferMemoryType memoryType); + BufferHandle createBuffer(BufferType type, size_t size, BufferMemoryType memoryType); /** * Returns the Vulkan buffer handle of a buffer - * represented by a given buffer handle id. + * represented by a given buffer handle. * - * @param id Buffer handle id + * @param handle Buffer handle * @return Vulkan buffer handle */ [[nodiscard]] - vk::Buffer getBuffer(uint64_t id) const; + vk::Buffer getBuffer(const BufferHandle& handle) const; + + /** + * Returns the size of a buffer represented + * by a given buffer handle. + * + * @param handle Buffer handle + * @return Size of the buffer + */ + [[nodiscard]] + size_t getBufferSize(const BufferHandle& handle) const; /** * Returns the Vulkan device memory handle of a buffer * represented by a given buffer handle id. * - * @param id Buffer handle id + * @param handle Buffer handle * @return Vulkan device memory handle */ [[nodiscard]] - vk::DeviceMemory getDeviceMemory(uint64_t id) const; + vk::DeviceMemory getDeviceMemory(const BufferHandle& handle) const; /** * Fills a buffer represented by a given buffer - * handle id with custom data. + * handle with custom data. * - * @param id Buffer handle id + * @param handle Buffer handle * @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); + void fillBuffer(const BufferHandle& handle, const void* data, size_t size, size_t offset); /** * Maps memory to a buffer represented by a given - * buffer handle id and returns it. + * buffer handle and returns it. * - * @param id Buffer handle id + * @param handle Buffer handle * @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); + void* mapBuffer(const BufferHandle& handle, size_t offset, size_t size); /** * Unmaps memory from a buffer represented by a given - * buffer handle id. + * buffer handle. * - * @param id Buffer handle id + * @param handle Buffer handle */ - void unmapBuffer(uint64_t id); + void unmapBuffer(const BufferHandle& handle); /** * Destroys and deallocates buffer represented by a given - * buffer handle id. + * buffer handle. * - * @param id Buffer handle id + * @param handle Buffer handle */ - void destroyBuffer(uint64_t id); + void destroyBuffer(const BufferHandle& handle); }; diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index 66db4776d053352d8ccb2eea5e09c3fb77b68561..cf75b8e754513da774ec1841c8c4cc700b99ee2d 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -13,19 +13,29 @@ #include "vkcv/PassConfig.hpp" #include "vkcv/Handles.hpp" #include "vkcv/Buffer.hpp" +#include "vkcv/Image.hpp" #include "vkcv/PipelineConfig.hpp" #include "CommandResources.hpp" #include "SyncResources.hpp" #include "Result.hpp" #include "vkcv/DescriptorConfig.hpp" +#include "Sampler.hpp" +#include "DescriptorWrites.hpp" namespace vkcv { + struct VertexBufferBinding { + vk::DeviceSize offset; + BufferHandle buffer; + }; + // forward declarations class PassManager; class PipelineManager; class DescriptorManager; class BufferManager; + class SamplerManager; + class ImageManager; struct SubmitInfo { QueueType queueType; @@ -55,19 +65,21 @@ namespace vkcv Context m_Context; - SwapChain m_swapchain; - std::vector<vk::ImageView> m_swapchainImageViews; - const Window& m_window; + SwapChain m_swapchain; + std::vector<vk::ImageView> m_swapchainImageViews; + const Window& m_window; - std::unique_ptr<PassManager> m_PassManager; - std::unique_ptr<PipelineManager> m_PipelineManager; - std::unique_ptr<DescriptorManager> m_DescriptorManager; - std::unique_ptr<BufferManager> m_BufferManager; + std::unique_ptr<PassManager> m_PassManager; + std::unique_ptr<PipelineManager> m_PipelineManager; + std::unique_ptr<DescriptorManager> m_DescriptorManager; + std::unique_ptr<BufferManager> m_BufferManager; + std::unique_ptr<SamplerManager> m_SamplerManager; + std::unique_ptr<ImageManager> m_ImageManager; - CommandResources m_CommandResources; - SyncResources m_SyncResources; - uint32_t m_currentSwapchainImageIndex; - std::vector<vk::Framebuffer> m_TemporaryFramebuffers; + CommandResources m_CommandResources; + SyncResources m_SyncResources; + uint32_t m_currentSwapchainImageIndex; + std::vector<vk::Framebuffer> m_TemporaryFramebuffers; /** * recreates the swapchain @@ -160,7 +172,7 @@ namespace vkcv PassHandle createPass(const PassConfig &config); /** - * Creates a #Buffer with data-type T and @p bufferType + * Creates a #Buffer with data-type T and @p bufferType * @param type Type of Buffer created * @param count Count of elements of type T * @param memoryType Type of Buffers memory @@ -170,13 +182,41 @@ namespace vkcv Buffer<T> createBuffer(vkcv::BufferType type, size_t count, BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL) { return Buffer<T>::create(m_BufferManager.get(), type, count, memoryType); } + + /** + * Creates a Sampler with given attributes. + * + * @param magFilter Magnifying filter + * @param minFilter Minimizing filter + * @param mipmapMode Mipmapping filter + * @param addressMode Address mode + * @return Sampler handle + */ + [[nodiscard]] + SamplerHandle createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, + SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode); + + /** + * Creates an #Image with a given format, width, height and depth. + * + * @param format Image format + * @param width Image width + * @param height Image height + * @param depth Image depth + * @return Image-Object + */ + [[nodiscard]] + Image createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth = 1); /** TODO: * @param setDescriptions * @return */ [[nodiscard]] - ResourcesHandle createResourceDescription(const std::vector<DescriptorSet> &descriptorSets); + ResourcesHandle createResourceDescription(const std::vector<DescriptorSetConfig> &descriptorSets); + void writeResourceDescription(ResourcesHandle handle, size_t setIndex, const DescriptorWrites& writes); + + vk::DescriptorSetLayout getDescritorSetLayout(ResourcesHandle handle, size_t setIndex); /** * @brief start recording command buffers and increment frame index @@ -186,8 +226,18 @@ namespace vkcv /** * @brief render a beautiful triangle */ - void renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, - const int width, const int height, const size_t pushConstantSize, const void* pushConstantData); + void renderMesh( + const PassHandle renderpassHandle, + const PipelineHandle pipelineHandle, + const int width, + const int height, + const size_t pushConstantSize, + const void* pushConstantData, + const std::vector<VertexBufferBinding> &vertexBufferBindings, + const BufferHandle indexBuffer, + const size_t indexCount, + const vkcv::ResourcesHandle resourceHandle, + const size_t resourceDescriptorSetIndex); /** * @brief end recording and present image diff --git a/include/vkcv/DescriptorConfig.hpp b/include/vkcv/DescriptorConfig.hpp index 8116259757fe623f97a2814991b226ee27efca50..161273db1ec3bd0be290ecdd1042d12d181d303e 100644 --- a/include/vkcv/DescriptorConfig.hpp +++ b/include/vkcv/DescriptorConfig.hpp @@ -11,7 +11,8 @@ namespace vkcv UNIFORM_BUFFER, STORAGE_BUFFER, SAMPLER, - IMAGE + IMAGE_SAMPLED, + IMAGE_STORAGE }; /* @@ -40,10 +41,9 @@ namespace vkcv * @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 + struct DescriptorSetConfig { - DescriptorSet() = delete; - explicit DescriptorSet(std::vector<DescriptorBinding> bindings) noexcept; + explicit DescriptorSetConfig(std::vector<DescriptorBinding> bindings) noexcept; std::vector<DescriptorBinding> bindings; }; diff --git a/include/vkcv/DescriptorWrites.hpp b/include/vkcv/DescriptorWrites.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d67e8e3233e184b207d109e652adeca43407d7e0 --- /dev/null +++ b/include/vkcv/DescriptorWrites.hpp @@ -0,0 +1,43 @@ +#pragma once +#include "Handles.hpp" +#include <vector> + +namespace vkcv { + struct SampledImageDescriptorWrite { + inline SampledImageDescriptorWrite(uint32_t binding, ImageHandle image) : binding(binding), image(image) {}; + uint32_t binding; + ImageHandle image; + }; + + struct StorageImageDescriptorWrite { + inline StorageImageDescriptorWrite(uint32_t binding, ImageHandle image) : binding(binding), image(image) {}; + uint32_t binding; + ImageHandle image; + }; + + struct UniformBufferDescriptorWrite { + inline UniformBufferDescriptorWrite(uint32_t binding, BufferHandle buffer) : binding(binding), buffer(buffer) {}; + uint32_t binding; + BufferHandle buffer; + }; + + struct StorageBufferDescriptorWrite { + inline StorageBufferDescriptorWrite(uint32_t binding, BufferHandle buffer) : binding(binding), buffer(buffer) {}; + uint32_t binding; + BufferHandle buffer; + }; + + struct SamplerDescriptorWrite { + inline SamplerDescriptorWrite(uint32_t binding, SamplerHandle sampler) : binding(binding), sampler(sampler) {}; + uint32_t binding; + SamplerHandle sampler; + }; + + struct DescriptorWrites { + std::vector<SampledImageDescriptorWrite> sampledImageWrites; + std::vector<StorageImageDescriptorWrite> storageImageWrites; + std::vector<UniformBufferDescriptorWrite> uniformBufferWrites; + std::vector<StorageBufferDescriptorWrite> storageBufferWrites; + std::vector<SamplerDescriptorWrite> samplerWrites; + }; +} \ No newline at end of file diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp index ad48a1ecbd41f75286cc33e88e62699468a5f11f..58f795f0c99a0cd1b05045f9f401a26c0aac1b88 100644 --- a/include/vkcv/Handles.hpp +++ b/include/vkcv/Handles.hpp @@ -5,13 +5,76 @@ * @brief Central header file for all possible handles that the framework will hand out. */ -#include <cstdint> +#include <iostream> namespace vkcv { + + class Handle { + friend std::ostream& operator << (std::ostream& out, const Handle& handle); + + private: + uint64_t m_id; + + protected: + Handle(); + + explicit Handle(uint64_t id); + + [[nodiscard]] + uint64_t getId() const; + + public: + virtual ~Handle() = default; + + Handle(const Handle& other) = default; + Handle(Handle&& other) = default; + + Handle& operator=(const Handle& other) = default; + Handle& operator=(Handle&& other) = default; + + explicit operator bool() const; + bool operator!() const; + + }; + + std::ostream& operator << (std::ostream& out, const Handle& handle); + // Handle returned for any buffer created with the core/context objects - struct BufferHandle {uint64_t id;}; - struct PassHandle {uint64_t id;}; - struct PipelineHandle {uint64_t id;}; - struct ResourcesHandle {uint64_t id;}; + class BufferHandle : public Handle { + friend class BufferManager; + private: + using Handle::Handle; + }; + + class PassHandle : public Handle { + friend class PassManager; + private: + using Handle::Handle; + }; + + class PipelineHandle : public Handle { + friend class PipelineManager; + private: + using Handle::Handle; + }; + + class ResourcesHandle : public Handle { + friend class DescriptorManager; + private: + using Handle::Handle; + }; + + class SamplerHandle : public Handle { + friend class SamplerManager; + private: + using Handle::Handle; + }; + + class ImageHandle : public Handle { + friend class ImageManager; + private: + using Handle::Handle; + }; + } diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d56fac9c0e442faeff1898443d737df365c9ed45 --- /dev/null +++ b/include/vkcv/Image.hpp @@ -0,0 +1,53 @@ +#pragma once +/** + * @authors Lars Hoerttrich + * @file vkcv/Buffer.hpp + * @brief class for image handles + */ +#include "vulkan/vulkan.hpp" + +#include "Handles.hpp" + +namespace vkcv { + + class ImageManager; + class Image { + friend class Core; + public: + [[nodiscard]] + vk::Format getFormat() const; + + [[nodiscard]] + uint32_t getWidth() const; + + [[nodiscard]] + uint32_t getHeight() const; + + [[nodiscard]] + uint32_t getDepth() const; + + [[nodiscard]] + vk::ImageLayout getLayout() const; + + [[nodiscard]] + vkcv::ImageHandle getHandle() const; + + void switchLayout(vk::ImageLayout newLayout); + + void fill(void* data, size_t size = SIZE_MAX); + private: + ImageManager* const m_manager; + const ImageHandle m_handle; + const vk::Format m_format; + const uint32_t m_width; + const uint32_t m_height; + const uint32_t m_depth; + vk::ImageLayout m_layout; + + Image(ImageManager* manager, const ImageHandle& handle, vk::Format format, uint32_t width, uint32_t height, uint32_t depth); + + static Image create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth); + + }; + +} diff --git a/include/vkcv/PassConfig.hpp b/include/vkcv/PassConfig.hpp index b8e80c67c2b70e3a0e6e2732b950ccaed38da3bf..d9a5bcd83acca5f5ba86b4e6ce6973acbed89de6 100644 --- a/include/vkcv/PassConfig.hpp +++ b/include/vkcv/PassConfig.hpp @@ -53,7 +53,6 @@ namespace vkcv struct PassConfig { - PassConfig() = delete; explicit PassConfig(std::vector<AttachmentDescription> attachments) noexcept; std::vector<AttachmentDescription> attachments{}; }; diff --git a/include/vkcv/PipelineConfig.hpp b/include/vkcv/PipelineConfig.hpp index 1ad6be8d1979c8a89f7de9dbe24ff13b5f5bb3fa..a8e1334f3ee5d6774b9fed35edd685f19747814d 100644 --- a/include/vkcv/PipelineConfig.hpp +++ b/include/vkcv/PipelineConfig.hpp @@ -1,27 +1,19 @@ +#pragma once /** * @authors Mara Vogt, Mark Mints * @file src/vkcv/Pipeline.hpp * @brief Pipeline class to handle shader stages */ -#ifndef VKCV_PIPELINECONFIG_HPP -#define VKCV_PIPELINECONFIG_HPP - #include <vector> #include <cstdint> #include "vkcv/Handles.hpp" #include "ShaderProgram.hpp" +#include <vkcv/VertexLayout.hpp> namespace vkcv { - class PipelineConfig { - - public: - /** - * Default constructer is deleted! - */ - PipelineConfig() = delete; - + struct PipelineConfig { /** * Constructor for the pipeline. Creates a pipeline using @p vertexCode, @p fragmentCode as well as the * dimensions of the application window @p width and @p height. A handle for the Render Pass is also needed, @p passHandle. @@ -31,13 +23,20 @@ namespace vkcv { * @param width width of the application window * @param passHandle handle for Render Pass */ - PipelineConfig(const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, PassHandle &passHandle); - - ShaderProgram m_ShaderProgram; - uint32_t m_Height; - uint32_t m_Width; - PassHandle m_PassHandle; + PipelineConfig( + const ShaderProgram& shaderProgram, + uint32_t width, + uint32_t height, + PassHandle &passHandle, + const std::vector<VertexAttribute> &vertexAttributes, + const std::vector<vk::DescriptorSetLayout> &descriptorLayouts); + + ShaderProgram m_ShaderProgram; + uint32_t m_Height; + uint32_t m_Width; + PassHandle m_PassHandle; + std::vector<VertexAttribute> m_vertexAttributes; + std::vector<vk::DescriptorSetLayout> m_descriptorLayouts; }; -} -#endif //VKCV_PIPELINECONFIG_HPP +} \ No newline at end of file diff --git a/include/vkcv/Sampler.hpp b/include/vkcv/Sampler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..007ed5ae4737275ddacbc5881a2c4c202b8806a4 --- /dev/null +++ b/include/vkcv/Sampler.hpp @@ -0,0 +1,22 @@ +#pragma once + +namespace vkcv { + + enum class SamplerFilterType { + NEAREST = 1, + LINEAR = 2 + }; + + enum class SamplerMipmapMode { + NEAREST = 1, + LINEAR = 2 + }; + + enum class SamplerAddressMode { + REPEAT = 1, + MIRRORED_REPEAT = 2, + CLAMP_TO_EDGE = 3, + MIRROR_CLAMP_TO_EDGE = 4 + }; + +} diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp index 172e906fca457c6245855639275054514958b69d..ef5d1f00ea3eeb97d97d8824439ded1ed326f33c 100644 --- a/include/vkcv/ShaderProgram.hpp +++ b/include/vkcv/ShaderProgram.hpp @@ -10,6 +10,8 @@ #include <iostream> #include <filesystem> #include <vulkan/vulkan.hpp> +#include <spirv_cross.hpp> +#include "vkcv/VertexLayout.hpp" namespace vkcv { @@ -53,8 +55,13 @@ namespace vkcv { bool existsShader(ShaderStage shaderStage) const; + void reflectShader(ShaderStage shaderStage); + + const VertexLayout &getVertexLayout() const; + private: std::unordered_map<ShaderStage, Shader> m_Shaders; + VertexLayout m_VertexLayout; }; } diff --git a/include/vkcv/VertexLayout.hpp b/include/vkcv/VertexLayout.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ee0ad8ef56d5284af2be4c81b7ea2f0d052d5a6f --- /dev/null +++ b/include/vkcv/VertexLayout.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include <unordered_map> +#include <vector> +#include <iostream> + +namespace vkcv{ + + /* With these enums, 0 is reserved to signal uninitialized or invalid data. */ + enum class PrimitiveType : uint32_t { + UNDEFINED = 0, + POSITION = 1, + NORMAL = 2, + TEXCOORD_0 = 3 + }; + /* This struct describes one vertex attribute of a vertex buffer. */ + typedef struct { + PrimitiveType type; // POSITION, NORMAL, ... + uint32_t offset; // offset in bytes + uint32_t length; // length of ... in bytes + uint32_t stride; // stride in bytes + uint16_t componentType; // eg. 5126 for float + uint8_t componentCount; // eg. 3 for vec3 + } VertexAttribute; + + enum class VertexFormat{ + FLOAT, + FLOAT2, + FLOAT3, + FLOAT4, + INT, + INT2, + INT3, + INT4 + }; + + uint32_t getFormatSize(VertexFormat format); + + struct VertexInputAttachment{ + VertexInputAttachment() = delete; + VertexInputAttachment(uint32_t location, uint32_t binding, VertexFormat format, uint32_t offset) noexcept; + + uint32_t location; + uint32_t binding; + VertexFormat format; + uint32_t offset; + }; + + struct VertexLayout{ + VertexLayout() noexcept; + VertexLayout(const std::vector<VertexInputAttachment> &inputs) noexcept; + std::unordered_map<uint32_t, VertexInputAttachment> attachmentMap; + uint32_t stride; + }; +} \ No newline at end of file diff --git a/lib/SPIRV-Cross b/lib/SPIRV-Cross new file mode 160000 index 0000000000000000000000000000000000000000..ff61890722a91e97c44940494be5b6eed0d5ff5b --- /dev/null +++ b/lib/SPIRV-Cross @@ -0,0 +1 @@ +Subproject commit ff61890722a91e97c44940494be5b6eed0d5ff5b diff --git a/modules/asset_loader/CMakeLists.txt b/modules/asset_loader/CMakeLists.txt index 8d4c0d6c104187de2d807cceceff529d83d236d6..d2a9b817ea68c7851fd2123f76b378d8a4d85ac0 100644 --- a/modules/asset_loader/CMakeLists.txt +++ b/modules/asset_loader/CMakeLists.txt @@ -31,7 +31,7 @@ include(config/FX_GLTF.cmake) include(config/STB.cmake) # link the required libraries to the module -target_link_libraries(vkcv_asset_loader ${vkcv_asset_loader_libraries}) +target_link_libraries(vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv) # including headers of dependencies and the VkCV framework target_include_directories(vkcv_asset_loader SYSTEM BEFORE PRIVATE ${vkcv_asset_loader_includes}) diff --git a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp index 6df014563a8991e464e02eb10f210c079913d3cb..24687e846ff9eae3275de357331a825f0b4ed2c3 100644 --- a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp +++ b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp @@ -8,6 +8,7 @@ #include <string> #include <vector> #include <cstdint> +#include <vkcv/VertexLayout.hpp> /* These macros define limits of the following structs. Implementations can * test against these limits when performing sanity checks. The main constraint @@ -49,8 +50,6 @@ enum PrimitiveMode { POINTS=0, LINES, LINELOOP, LINESTRIP, TRIANGLES, TRIANGLESTRIP, TRIANGLEFAN }; -/* With these enums, 0 is reserved to signal uninitialized or invalid data. */ -enum PrimitiveType { POSITION=1, NORMAL, TEXCOORD_0 }; /* The indices in the index buffer can be of different bit width. */ enum IndexType { UINT32=0, UINT16=1, UINT8=2 }; @@ -58,16 +57,6 @@ typedef struct { // TODO not yet needed for the first (unlit) triangle } Material; -/* This struct describes one vertex attribute of a vertex buffer. */ -typedef struct { - PrimitiveType type; // POSITION, NORMAL, ... - uint32_t offset; // offset in bytes - uint32_t length; // length of ... in bytes - uint32_t stride; // stride in bytes - uint16_t componentType; // eg. 5126 for float - uint8_t componentCount; // eg. 3 for vec3 -} VertexAttribute; - /* This struct represents one (possibly the only) part of a mesh. There is * always one vertexBuffer and zero or one indexBuffer (indexed rendering is * common but not always used). If there is no index buffer, this is indicated diff --git a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp index d0949cbc3454283b0084c57abf95a06a036c4e9b..e660b442d0b9a0208f95c9d753ef19e926bcac44 100644 --- a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp +++ b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp @@ -85,12 +85,12 @@ int loadMesh(const std::string &path, Mesh &mesh) { VertexAttribute attribute; if (attrib.first == "POSITION") { - attribute.type = POSITION; + attribute.type = PrimitiveType::POSITION; posAccessor = accessor; } else if (attrib.first == "NORMAL") { - attribute.type = NORMAL; + attribute.type = PrimitiveType::NORMAL; } else if (attrib.first == "TEXCOORD_0") { - attribute.type = TEXCOORD_0; + attribute.type = PrimitiveType::TEXCOORD_0; } else { return 0; } @@ -126,15 +126,16 @@ int loadMesh(const std::string &path, Mesh &mesh) { } } - const fx::gltf::BufferView& vertexBufferView = object.bufferViews[posAccessor.bufferView]; - const fx::gltf::Buffer& vertexBuffer = object.buffers[vertexBufferView.buffer]; + const fx::gltf::BufferView& vertexBufferView = object.bufferViews[posAccessor.bufferView]; + const fx::gltf::Buffer& vertexBuffer = object.buffers[vertexBufferView.buffer]; + // FIXME: This only works when all vertex attributes are in one buffer std::vector<uint8_t> vertexBufferData; - vertexBufferData.resize(vertexBufferView.byteLength); + vertexBufferData.resize(vertexBuffer.byteLength); { - const size_t off = vertexBufferView.byteOffset; + const size_t off = 0; const void *const ptr = ((char*)vertexBuffer.data.data()) + off; - if (!memcpy(vertexBufferData.data(), ptr, vertexBufferView.byteLength)) { + if (!memcpy(vertexBufferData.data(), ptr, vertexBuffer.byteLength)) { std::cerr << "ERROR copying vertex buffer data.\n"; return 0; } diff --git a/projects/first_mesh/CMakeLists.txt b/projects/first_mesh/CMakeLists.txt index ae9c5604cb83c6f3a16773e896521117460839f7..eb0f028db38707272f9fbcf61662633f2868eedc 100644 --- a/projects/first_mesh/CMakeLists.txt +++ b/projects/first_mesh/CMakeLists.txt @@ -22,7 +22,7 @@ if(MSVC) endif() # including headers of dependencies and the VkCV framework -target_include_directories(first_mesh SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include}) +target_include_directories(first_mesh SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include}) # linking with libraries from all dependencies and the VkCV framework -target_link_libraries(first_mesh vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries}) +target_link_libraries(first_mesh vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera) diff --git a/projects/first_mesh/resources/shaders/compile.bat b/projects/first_mesh/resources/shaders/compile.bat new file mode 100644 index 0000000000000000000000000000000000000000..b4521235c40fe5fb163bab874560c2f219b7517f --- /dev/null +++ b/projects/first_mesh/resources/shaders/compile.bat @@ -0,0 +1,3 @@ +%VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv +%VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv +pause \ No newline at end of file diff --git a/projects/first_mesh/resources/shaders/frag.spv b/projects/first_mesh/resources/shaders/frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..087e4e22fb2fcec27d99b3ff2aa1a705fe755796 Binary files /dev/null and b/projects/first_mesh/resources/shaders/frag.spv differ diff --git a/projects/first_mesh/resources/shaders/shader.frag b/projects/first_mesh/resources/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..71a1de69c57c0d7b7d4665095410e7acaf8dbd62 --- /dev/null +++ b/projects/first_mesh/resources/shaders/shader.frag @@ -0,0 +1,14 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 passNormal; +layout(location = 1) in vec2 passUV; + +layout(location = 0) out vec3 outColor; + +layout(set=0, binding=0) uniform texture2D meshTexture; +layout(set=0, binding=1) uniform sampler textureSampler; + +void main() { + outColor = texture(sampler2D(meshTexture, textureSampler), passUV).rgb; +} \ No newline at end of file diff --git a/projects/first_mesh/resources/shaders/shader.vert b/projects/first_mesh/resources/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..76855152253b48b7400f016d063ed4f0e507435e --- /dev/null +++ b/projects/first_mesh/resources/shaders/shader.vert @@ -0,0 +1,19 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec2 inUV; + +layout(location = 0) out vec3 passNormal; +layout(location = 1) out vec2 passUV; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +void main() { + gl_Position = mvp * vec4(inPosition, 1.0); + passNormal = inNormal; + passUV = inUV; +} \ No newline at end of file diff --git a/projects/first_mesh/resources/shaders/vert.spv b/projects/first_mesh/resources/shaders/vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..374c023e14b351eb43cbcda5951cbb8b3d6f96a1 Binary files /dev/null and b/projects/first_mesh/resources/shaders/vert.spv differ diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp index b37429e1f7080b70aaab514125f86fbe03f329b5..107cd432e1bc902692bb5b1fa694120ddf24038b 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -1,75 +1,170 @@ #include <iostream> -#include <stdio.h> +#include <vkcv/Core.hpp> +#include <GLFW/glfw3.h> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> #include <vkcv/asset/asset_loader.hpp> - int main(int argc, const char** argv) { + const char* applicationName = "First Mesh"; + + const int windowWidth = 800; + const int windowHeight = 600; + vkcv::Window window = vkcv::Window::create( + applicationName, + windowWidth, + windowHeight, + false + ); + + vkcv::CameraManager cameraManager(window, windowWidth, windowHeight); + + window.initEvents(); + + vkcv::Core core = vkcv::Core::create( + window, + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, + {}, + { "VK_KHR_swapchain" } + ); + vkcv::asset::Mesh mesh; - - const char *path = argc > 1 ? argv[1] : "resources/cube/cube.gltf"; + + const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf"; int result = vkcv::asset::loadMesh(path, mesh); - + if (result == 1) { std::cout << "Mesh loading successful!" << std::endl; - } else { + } + else { std::cout << "Mesh loading failed: " << result << std::endl; return 1; } - /* Demonstration of how to use the vkcv::asset::Mesh struct. */ - const char *primitive_modes[] = { - "points", "lines", "line loop", "line strip", "triangles", - "triangle strip", "triangle fan" - }; - const char *primitive_types[] = { - "unknown", "position", "normal", "texcoord0" + assert(mesh.vertexGroups.size() > 0); + auto vertexBuffer = core.createBuffer<uint8_t>( + vkcv::BufferType::VERTEX, + mesh.vertexGroups[0].vertexBuffer.data.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL + ); + + vertexBuffer.fill(mesh.vertexGroups[0].vertexBuffer.data); + + auto indexBuffer = core.createBuffer<uint8_t>( + vkcv::BufferType::INDEX, + mesh.vertexGroups[0].indexBuffer.data.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL + ); + + indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data); + + // an example attachment for passes that output to the window + const vkcv::AttachmentDescription present_color_attachment( + vkcv::AttachmentLayout::UNDEFINED, + vkcv::AttachmentLayout::COLOR_ATTACHMENT, + vkcv::AttachmentLayout::PRESENTATION, + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + core.getSwapchainImageFormat() + ); + + const vkcv::AttachmentDescription depth_attachment( + vkcv::AttachmentLayout::UNDEFINED, + vkcv::AttachmentLayout::DEPTH_STENCIL_ATTACHMENT, + vkcv::AttachmentLayout::DEPTH_STENCIL_ATTACHMENT, + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + vk::Format::eD32Sfloat + ); + + vkcv::PassConfig trianglePassDefinition({ present_color_attachment, depth_attachment }); + vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition); + + if (!trianglePass) { + std::cout << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::ShaderProgram triangleShaderProgram{}; + triangleShaderProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/vert.spv")); + triangleShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv")); + triangleShaderProgram.reflectShader(vkcv::ShaderStage::VERTEX); + triangleShaderProgram.reflectShader(vkcv::ShaderStage::FRAGMENT); + + auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes; + + std::sort(attributes.begin(), attributes.end(), [](const vkcv::VertexAttribute& x, const vkcv::VertexAttribute& y) { + return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type); + }); + + vkcv::DescriptorSetConfig setConfig({ + vkcv::DescriptorBinding(vkcv::DescriptorType::IMAGE_SAMPLED, 1, vkcv::ShaderStage::FRAGMENT), + vkcv::DescriptorBinding(vkcv::DescriptorType::SAMPLER, 1, vkcv::ShaderStage::FRAGMENT) + }); + vkcv::ResourcesHandle set = core.createResourceDescription({ setConfig }); + + const vkcv::PipelineConfig trianglePipelineDefinition( + triangleShaderProgram, + windowWidth, + windowHeight, + trianglePass, + mesh.vertexGroups[0].vertexBuffer.attributes, + { core.getDescritorSetLayout(set, 0) }); + vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition); + + if (!trianglePipeline) { + std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, mesh.texture_hack.w, mesh.texture_hack.h); + texture.fill(mesh.texture_hack.img); + + vkcv::SamplerHandle sampler = core.createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT + ); + + std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { + { mesh.vertexGroups[0].vertexBuffer.attributes[0].offset, vertexBuffer.getHandle() }, + { mesh.vertexGroups[0].vertexBuffer.attributes[1].offset, vertexBuffer.getHandle() }, + { mesh.vertexGroups[0].vertexBuffer.attributes[2].offset, vertexBuffer.getHandle() } }; - printf("Mesh %s (%s) has %lu vertex group(s) and %lu material(s):\n", - mesh.name.c_str(), path, mesh.vertexGroups.size(), - mesh.materials.size()); - for (size_t i = 0; i < mesh.vertexGroups.size(); i++) { - printf("--- vertex group %lu ---\n", i); - const auto &vg = mesh.vertexGroups[i]; - printf("primitive mode: %d (%s)\n", vg.mode, - primitive_modes[vg.mode]); - printf("index buffer: %lu bytes for %lu indices ", - vg.indexBuffer.data.size(), vg.numIndices); - const auto itype = vg.indexBuffer.type; - printf("(%s @ %p)\n", - itype == vkcv::asset::UINT32 ? "UINT32" : - itype == vkcv::asset::UINT16 ? "UINT16" : - "UINT8", vg.indexBuffer.data.data()); - printf("\tindices: "); - const size_t n = vg.numIndices; - if (vg.indexBuffer.type == vkcv::asset::UINT32) { - uint32_t *idx = (uint32_t*)vg.indexBuffer.data.data(); - for (size_t j = 0; j < n; j++) printf("%u ", idx[j]); - } else - if (vg.indexBuffer.type == vkcv::asset::UINT16) { - uint16_t *idx = (uint16_t*)vg.indexBuffer.data.data(); - for (size_t j = 0; j < n; j++) printf("%u ", idx[j]); - } else - if (vg.indexBuffer.type == vkcv::asset::UINT8) { - uint8_t *idx = (uint8_t*)vg.indexBuffer.data.data(); - for (size_t j = 0; j < n; j++) printf("%u ", idx[j]); - } else { - fprintf(stderr, "ERROR Invalid IndexType: %d\n", - vg.indexBuffer.type); - return 0; - } - printf("\n"); - printf("vertex buffer: %lu bytes for %lu vertices with %lu " - "attributes (starting at %p)\n", - vg.vertexBuffer.data.size(), vg.numVertices, - vg.vertexBuffer.attributes.size(), - vg.vertexBuffer.data.data()); - printf("attributes:\toffset\tlength\tstride\tcomponents\n"); - for (const auto att : vg.vertexBuffer.attributes) { - printf("%11s\t%u\t%u\t%u\t%hhux%hu\n", - primitive_types[att.type], - att.offset, att.length, att.stride, - att.componentCount, att.componentType); - } + + vkcv::DescriptorWrites setWrites; + setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) }; + setWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, sampler) }; + core.writeResourceDescription(set, 0, setWrites); + + auto start = std::chrono::system_clock::now(); + while (window.isWindowOpen()) { + core.beginFrame(); + window.pollEvents(); + auto end = std::chrono::system_clock::now(); + auto deltatime = end - start; + start = end; + cameraManager.getCamera().updateView(std::chrono::duration<double>(deltatime).count()); + const glm::mat4 mvp = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView(); + + core.renderMesh( + trianglePass, + trianglePipeline, + windowWidth, + windowHeight, + sizeof(mvp), + &mvp, + vertexBufferBindings, + indexBuffer.getHandle(), + mesh.vertexGroups[0].numIndices, + set, + 0 + ); + + core.endFrame(); } return 0; diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index 192e3449063c59cda865c87980a9f0740bed75fa..6b4a83c9769d88fcd0310ee3d646b225bc9f0615 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -5,70 +5,79 @@ #include <chrono> int main(int argc, const char** argv) { - const char* applicationName = "First Triangle"; + const char* applicationName = "First Triangle"; const int windowWidth = 800; const int windowHeight = 600; - vkcv::Window window = vkcv::Window::create( + vkcv::Window window = vkcv::Window::create( applicationName, windowWidth, windowHeight, false - ); + ); - vkcv::CameraManager cameraManager(window, windowWidth, windowHeight); - cameraManager.getTrackballCamera().setPosition(glm::vec3(0.0f,0.5f,0.0f)); - cameraManager.getTrackballCamera().setCenter(glm::vec3(0.0f,0.0f,-1.0f)); + vkcv::CameraManager cameraManager(window, windowWidth, windowHeight); - window.initEvents(); + window.initEvents(); vkcv::Core core = vkcv::Core::create( - window, - applicationName, + window, + applicationName, VK_MAKE_VERSION(0, 0, 1), - {vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute}, + { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, {}, - {"VK_KHR_swapchain"} + { "VK_KHR_swapchain" } ); - const auto &context = core.getContext(); + const auto& context = core.getContext(); const vk::Instance& instance = context.getInstance(); const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice(); const vk::Device& device = context.getDevice(); - + struct vec3 { float x, y, z; }; - + const size_t n = 5027; - - auto buffer = core.createBuffer<vec3>(vkcv::BufferType::VERTEX, n, vkcv::BufferMemoryType::DEVICE_LOCAL); - vec3 vec_data [n]; - + + auto testBuffer = 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); - + + testBuffer.fill(vec_data); + + auto triangleIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, n, vkcv::BufferMemoryType::DEVICE_LOCAL); + uint16_t indices[3] = { 0, 1, 2 }; + triangleIndexBuffer.fill(&indices[0], sizeof(indices)); + /*vec3* m = buffer.map(); m[0] = { 0, 0, 0 }; m[1] = { 0, 0, 0 }; m[2] = { 0, 0, 0 }; buffer.unmap();*/ + vkcv::SamplerHandle sampler = core.createSampler( + vkcv::SamplerFilterType::NEAREST, + vkcv::SamplerFilterType::NEAREST, + vkcv::SamplerMipmapMode::NEAREST, + vkcv::SamplerAddressMode::REPEAT + ); + std::cout << "Physical device: " << physicalDevice.getProperties().deviceName << std::endl; switch (physicalDevice.getProperties().vendorID) { - case 0x1002: std::cout << "Running AMD huh? You like underdogs, are you a Linux user?" << std::endl; break; - case 0x10DE: std::cout << "An NVidia GPU, how predictable..." << std::endl; break; - case 0x8086: std::cout << "Poor child, running on an Intel GPU, probably integrated..." - "or perhaps you are from the future with a dedicated one?" << std::endl; break; - case 0x13B5: std::cout << "ARM? What the hell are you running on, next thing I know you're trying to run Vulkan on a leg..." << std::endl; break; - default: std::cout << "Unknown GPU vendor?! Either you're on an exotic system or your driver is broken..." << std::endl; + case 0x1002: std::cout << "Running AMD huh? You like underdogs, are you a Linux user?" << std::endl; break; + case 0x10DE: std::cout << "An NVidia GPU, how predictable..." << std::endl; break; + case 0x8086: std::cout << "Poor child, running on an Intel GPU, probably integrated..." + "or perhaps you are from the future with a dedicated one?" << std::endl; break; + case 0x13B5: std::cout << "ARM? What the hell are you running on, next thing I know you're trying to run Vulkan on a leg..." << std::endl; break; + default: std::cout << "Unknown GPU vendor?! Either you're on an exotic system or your driver is broken..." << std::endl; } - // an example attachment for passes that output to the window + // an example attachment for passes that output to the window const vkcv::AttachmentDescription present_color_attachment( vkcv::AttachmentLayout::UNDEFINED, vkcv::AttachmentLayout::COLOR_ATTACHMENT, @@ -77,10 +86,10 @@ int main(int argc, const char** argv) { vkcv::AttachmentOperation::CLEAR, core.getSwapchainImageFormat()); - vkcv::PassConfig trianglePassDefinition({present_color_attachment}); + vkcv::PassConfig trianglePassDefinition({ present_color_attachment }); vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition); - if (trianglePass.id == 0) + if (!trianglePass) { std::cout << "Error. Could not create renderpass. Exiting." << std::endl; return EXIT_FAILURE; @@ -89,32 +98,25 @@ int main(int argc, const char** argv) { vkcv::ShaderProgram triangleShaderProgram{}; triangleShaderProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/vert.spv")); triangleShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/frag.spv")); + triangleShaderProgram.reflectShader(vkcv::ShaderStage::VERTEX); + triangleShaderProgram.reflectShader(vkcv::ShaderStage::FRAGMENT); - const vkcv::PipelineConfig trianglePipelineDefinition(triangleShaderProgram, windowWidth, windowHeight, trianglePass); + const vkcv::PipelineConfig trianglePipelineDefinition( + triangleShaderProgram, + windowWidth, + windowHeight, + trianglePass, + {}, + {}); vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition); - if (trianglePipeline.id == 0) + + if (!trianglePipeline) { std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; 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; - } + std::vector<vkcv::VertexBufferBinding> vertexBufferBindings; /* * BufferHandle triangleVertices = core.createBuffer(vertices); @@ -144,7 +146,19 @@ int main(int argc, const char** argv) { cameraManager.getTrackballCamera().updateView(std::chrono::duration<double>(deltatime).count()); const glm::mat4 mvp = cameraManager.getTrackballCamera().getProjection() * cameraManager.getTrackballCamera().getView(); - core.renderTriangle(trianglePass, trianglePipeline, windowWidth, windowHeight, sizeof(mvp), &mvp); + core.renderMesh( + trianglePass, + trianglePipeline, + windowWidth, + windowHeight, + sizeof(mvp), + &mvp, + vertexBufferBindings, + triangleIndexBuffer.getHandle(), + 3, + vkcv::ResourcesHandle(), + 0); + core.endFrame(); } return 0; diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp index cb7c7356aef589ee8aef03b904d48021bdb75057..ab34a3df149c96d30c9ee4e0665ba47765d19751 100644 --- a/src/vkcv/BufferManager.cpp +++ b/src/vkcv/BufferManager.cpp @@ -9,7 +9,7 @@ namespace vkcv { BufferManager::BufferManager() noexcept : - m_core(nullptr), m_buffers(), m_stagingBuffer(UINT64_MAX) + m_core(nullptr), m_buffers(), m_stagingBuffer(BufferHandle()) { } @@ -22,37 +22,38 @@ namespace vkcv { } BufferManager::~BufferManager() noexcept { - for (size_t id = 0; id < m_buffers.size(); id++) { - destroyBuffer(id); + for (uint64_t id = 0; id < m_buffers.size(); id++) { + destroyBuffer(BufferHandle(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" + * @brief searches memory type index for buffer allocation, combines requirements of buffer and application * @param physicalMemoryProperties Memory Properties of physical device - * @param typeBits + * @param typeBits Bit field for suitable memory types * @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; + uint32_t searchBufferMemoryType(const vk::PhysicalDeviceMemoryProperties& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) { + const uint32_t memoryCount = physicalMemoryProperties.memoryTypeCount; + for (uint32_t memoryIndex = 0; memoryIndex < memoryCount; ++memoryIndex) { + const uint32_t memoryTypeBits = (1 << memoryIndex); + const bool isRequiredMemoryType = typeBits & memoryTypeBits; + + const vk::MemoryPropertyFlags properties = + physicalMemoryProperties.memoryTypes[memoryIndex].propertyFlags; + const bool hasRequiredProperties = + (properties & requirements) == requirements; + + if (isRequiredMemoryType && hasRequiredProperties) + return static_cast<int32_t>(memoryIndex); } - - return memoryTypeIndex; + + // failed to find memory type + return -1; } - uint64_t BufferManager::createBuffer(BufferType type, size_t size, BufferMemoryType memoryType) { + BufferHandle BufferManager::createBuffer(BufferType type, size_t size, BufferMemoryType memoryType) { vk::BufferCreateFlags createFlags; vk::BufferUsageFlags usageFlags; @@ -69,6 +70,9 @@ namespace vkcv { case BufferType::STAGING: usageFlags = vk::BufferUsageFlagBits::eTransferSrc; break; + case BufferType::INDEX: + usageFlags = vk::BufferUsageFlagBits::eIndexBuffer; + break; default: // TODO: maybe an issue break; @@ -103,7 +107,7 @@ namespace vkcv { break; } - const uint32_t memoryTypeIndex = searchMemoryType( + const uint32_t memoryTypeIndex = searchBufferMemoryType( physicalDevice.getMemoryProperties(), requirements.memoryTypeBits, memoryTypeFlags @@ -115,11 +119,11 @@ namespace vkcv { const uint64_t id = m_buffers.size(); m_buffers.push_back({ buffer, memory, size, nullptr, mappable }); - return id; + return BufferHandle{ id }; } struct StagingStepInfo { - void* data; + const void* data; size_t size; size_t offset; @@ -148,7 +152,7 @@ namespace vkcv { 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); + memcpy(mapped, reinterpret_cast<const char*>(info.data) + info.stagingPosition, mapped_size); device.unmapMemory(info.stagingMemory); SubmitInfo submitInfo; @@ -178,7 +182,9 @@ namespace vkcv { ); } - vk::Buffer BufferManager::getBuffer(uint64_t id) const { + vk::Buffer BufferManager::getBuffer(const BufferHandle& handle) const { + const uint64_t id = handle.getId(); + if (id >= m_buffers.size()) { return nullptr; } @@ -188,7 +194,21 @@ namespace vkcv { return buffer.m_handle; } - vk::DeviceMemory BufferManager::getDeviceMemory(uint64_t id) const { + size_t BufferManager::getBufferSize(const BufferHandle &handle) const { + const uint64_t id = handle.getId(); + + if (id >= m_buffers.size()) { + return 0; + } + + auto& buffer = m_buffers[id]; + + return buffer.m_size; + } + + vk::DeviceMemory BufferManager::getDeviceMemory(const BufferHandle& handle) const { + const uint64_t id = handle.getId(); + if (id >= m_buffers.size()) { return nullptr; } @@ -198,7 +218,9 @@ namespace vkcv { return buffer.m_memory; } - void BufferManager::fillBuffer(uint64_t id, void *data, size_t size, size_t offset) { + void BufferManager::fillBuffer(const BufferHandle& handle, const void *data, size_t size, size_t offset) { + const uint64_t id = handle.getId(); + if (size == 0) { size = SIZE_MAX; } @@ -226,7 +248,7 @@ namespace vkcv { memcpy(mapped, data, max_size); device.unmapMemory(buffer.m_memory); } else { - auto& stagingBuffer = m_buffers[m_stagingBuffer]; + auto& stagingBuffer = m_buffers[ m_stagingBuffer.getId() ]; StagingStepInfo info; info.data = data; @@ -246,7 +268,9 @@ namespace vkcv { } } - void* BufferManager::mapBuffer(uint64_t id, size_t offset, size_t size) { + void* BufferManager::mapBuffer(const BufferHandle& handle, size_t offset, size_t size) { + const uint64_t id = handle.getId(); + if (size == 0) { size = SIZE_MAX; } @@ -272,7 +296,9 @@ namespace vkcv { return buffer.m_mapped; } - void BufferManager::unmapBuffer(uint64_t id) { + void BufferManager::unmapBuffer(const BufferHandle& handle) { + const uint64_t id = handle.getId(); + if (id >= m_buffers.size()) { return; } @@ -289,7 +315,9 @@ namespace vkcv { buffer.m_mapped = nullptr; } - void BufferManager::destroyBuffer(uint64_t id) { + void BufferManager::destroyBuffer(const BufferHandle& handle) { + const uint64_t id = handle.getId(); + if (id >= m_buffers.size()) { return; } @@ -300,10 +328,12 @@ namespace vkcv { if (buffer.m_memory) { device.freeMemory(buffer.m_memory); + buffer.m_memory = nullptr; } if (buffer.m_handle) { device.destroyBuffer(buffer.m_handle); + buffer.m_handle = nullptr; } } diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 0ee414fb974676b1d2df820e69218f5bb247341b..0b0d3a24f0398a5efa17a4c33110adabbb97c15e 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -10,10 +10,11 @@ #include "PassManager.hpp" #include "PipelineManager.hpp" #include "vkcv/BufferManager.hpp" +#include "SamplerManager.hpp" +#include "ImageManager.hpp" #include "DescriptorManager.hpp" #include "Surface.hpp" #include "ImageLayoutTransitions.hpp" -#include "Framebuffer.hpp" namespace vkcv { @@ -95,11 +96,15 @@ namespace vkcv 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_SamplerManager(std::unique_ptr<SamplerManager>(new SamplerManager(m_Context.m_Device))), + m_ImageManager{std::unique_ptr<ImageManager>(new ImageManager(*m_BufferManager))}, m_CommandResources(commandResources), m_SyncResources(syncResources) { m_BufferManager->m_core = this; m_BufferManager->init(); + + m_ImageManager->m_core = this; } Core::~Core() noexcept { @@ -118,8 +123,7 @@ namespace vkcv PipelineHandle Core::createGraphicsPipeline(const PipelineConfig &config) { - const vk::RenderPass &pass = m_PassManager->getVkPass(config.m_PassHandle); - return m_PipelineManager->createPipeline(config, pass); + return m_PipelineManager->createPipeline(config, *m_PassManager); } @@ -161,40 +165,117 @@ namespace vkcv m_Context.getDevice().waitIdle(); // FIMXE: this is a sin against graphics programming, but its getting late - Alex destroyTemporaryFramebuffers(); } + + vk::Framebuffer createFramebuffer(const vk::Device device, const vk::RenderPass& renderpass, + const int width, const int height, const std::vector<vk::ImageView>& attachments) { + const vk::FramebufferCreateFlags flags = {}; + const vk::FramebufferCreateInfo createInfo(flags, renderpass, attachments.size(), attachments.data(), width, height, 1); + return device.createFramebuffer(createInfo); + } - void Core::renderTriangle(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, - const int width, const int height, const size_t pushConstantSize, const void *pushConstantData) { + void Core::renderMesh( + const PassHandle renderpassHandle, + const PipelineHandle pipelineHandle, + const int width, + const int height, + const size_t pushConstantSize, + const void *pushConstantData, + const std::vector<VertexBufferBinding>& vertexBufferBindings, + const BufferHandle indexBuffer, + const size_t indexCount, + const vkcv::ResourcesHandle resourceHandle, + const size_t resourceDescriptorSetIndex + ) { if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { return; } const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle); + const PassConfig passConfig = m_PassManager->getPassConfig(renderpassHandle); + + ImageHandle depthImage; + + for (const auto& attachment : passConfig.attachments) { + if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { + depthImage = m_ImageManager->createImage(width, height, 1, attachment.format); + break; + } + } + 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::Buffer vulkanIndexBuffer = m_BufferManager->getBuffer(indexBuffer); - const vk::Framebuffer framebuffer = createFramebuffer(m_Context.getDevice(), renderpass, width, height, imageView); + std::vector<vk::ImageView> attachments; + attachments.push_back(imageView); + + if (depthImage) { + attachments.push_back(m_ImageManager->getVulkanImageView(depthImage)); + } + + const vk::Framebuffer framebuffer = createFramebuffer( + m_Context.getDevice(), + renderpass, + width, + height, + attachments + ); + m_TemporaryFramebuffers.push_back(framebuffer); + auto &bufferManager = m_BufferManager; + 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); + submitCommands(submitInfo, [&](const vk::CommandBuffer& cmdBuffer) { + std::vector<vk::ClearValue> clearValues; + + for (const auto& attachment : passConfig.attachments) { + if (attachment.load_operation == AttachmentOperation::CLEAR) { + float clear = 0.0f; + + if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { + clear = 1.0f; + } + + clearValues.emplace_back(std::array<float, 4>{ + clear, + clear, + clear, + 1.f + }); + } + } + + const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data()); 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, {}); + + for (uint32_t i = 0; i < vertexBufferBindings.size(); i++) { + const auto &vertexBinding = vertexBufferBindings[i]; + const auto vertexBuffer = bufferManager->getBuffer(vertexBinding.buffer); + cmdBuffer.bindVertexBuffers(i, (vertexBuffer), (vertexBinding.offset)); + } + + if (resourceHandle) { + const vk::DescriptorSet descriptorSet = m_DescriptorManager->getDescriptorSet(resourceHandle, resourceDescriptorSetIndex); + cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 0, descriptorSet, nullptr); + } + + cmdBuffer.bindIndexBuffer(vulkanIndexBuffer, 0, vk::IndexType::eUint16); //FIXME: choose proper size + cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, pushConstantSize, pushConstantData); + cmdBuffer.drawIndexed(indexCount, 1, 0, 0, {}); cmdBuffer.endRenderPass(); - }, nullptr); + }, [&]() { + m_ImageManager->destroyImage(depthImage); + }); } void Core::endFrame() { @@ -255,9 +336,33 @@ namespace vkcv finish(); } } + + SamplerHandle Core::createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, + SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode) { + return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, addressMode); + } + + Image Core::createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth) + { + return Image::create(m_ImageManager.get(), format, width, height, depth); + } - ResourcesHandle Core::createResourceDescription(const std::vector<DescriptorSet> &descriptorSets) + ResourcesHandle Core::createResourceDescription(const std::vector<DescriptorSetConfig> &descriptorSets) { return m_DescriptorManager->createResourceDescription(descriptorSets); } + + void Core::writeResourceDescription(ResourcesHandle handle, size_t setIndex, const DescriptorWrites &writes) { + m_DescriptorManager->writeResourceDescription( + handle, + setIndex, + writes, + *m_ImageManager, + *m_BufferManager, + *m_SamplerManager); + } + + vk::DescriptorSetLayout Core::getDescritorSetLayout(ResourcesHandle handle, size_t setIndex) { + return m_DescriptorManager->getDescriptorSetLayout(handle, setIndex); + } } diff --git a/src/vkcv/DescriptorConfig.cpp b/src/vkcv/DescriptorConfig.cpp index f437ab6833edca09f58499b6d74307606bba8999..c4f6e326560e91747d206fecc983525a5b7bb6dc 100644 --- a/src/vkcv/DescriptorConfig.cpp +++ b/src/vkcv/DescriptorConfig.cpp @@ -14,7 +14,7 @@ namespace vkcv { shaderStage{shaderStage} {}; - DescriptorSet::DescriptorSet(std::vector<DescriptorBinding> bindings) noexcept : + DescriptorSetConfig::DescriptorSetConfig(std::vector<DescriptorBinding> bindings) noexcept : bindings{std::move(bindings)} {}; } diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp index bcebb142af2f29c23e77185112cbb83a827e41ba..cf689b44a3865585fcc2d52620badbb4919da644 100644 --- a/src/vkcv/DescriptorManager.cpp +++ b/src/vkcv/DescriptorManager.cpp @@ -8,7 +8,7 @@ namespace vkcv descriptorSetLayouts{std::move(layouts)} {} DescriptorManager::DescriptorManager(vk::Device device) noexcept: - m_Device{ device }, m_NextResourceDescriptionID{ 1 } + m_Device{ device }, m_NextResourceDescriptionID{ 0 } { /** * Allocate a set size for the initial pool, namely 1000 units of each descriptor type below. @@ -40,7 +40,7 @@ namespace vkcv m_Device.destroy(m_Pool); } - ResourcesHandle DescriptorManager::createResourceDescription(const std::vector<DescriptorSet> &descriptorSets) + ResourcesHandle DescriptorManager::createResourceDescription(const std::vector<DescriptorSetConfig> &descriptorSets) { std::vector<vk::DescriptorSet> vk_sets; std::vector<vk::DescriptorSetLayout> vk_setLayouts; @@ -64,7 +64,7 @@ namespace vkcv if(m_Device.createDescriptorSetLayout(&layoutInfo, nullptr, &layout) != vk::Result::eSuccess) { std::cout << "FAILED TO CREATE DESCRIPTOR SET LAYOUT" << std::endl; - return ResourcesHandle{0}; + return ResourcesHandle(); }; vk_setLayouts.push_back(layout); } @@ -79,12 +79,158 @@ namespace vkcv for(const auto &layout : vk_setLayouts) m_Device.destroy(layout); - return ResourcesHandle{0}; + return ResourcesHandle(); }; m_ResourceDescriptions.emplace_back(vk_sets, vk_setLayouts); - return ResourcesHandle{m_NextResourceDescriptionID++}; + return ResourcesHandle(m_NextResourceDescriptionID++); } + + struct WriteDescriptorSetInfo { + size_t imageInfoIndex; + size_t bufferInfoIndex; + uint32_t binding; + vk::DescriptorType type; + }; + + void DescriptorManager::writeResourceDescription( + ResourcesHandle handle, + size_t setIndex, + const DescriptorWrites &writes, + const ImageManager &imageManager, + const BufferManager &bufferManager, + const SamplerManager &samplerManager) { + + vk::DescriptorSet set = m_ResourceDescriptions[handle.getId()].descriptorSets[setIndex]; + + std::vector<vk::DescriptorImageInfo> imageInfos; + std::vector<vk::DescriptorBufferInfo> bufferInfos; + + std::vector<WriteDescriptorSetInfo> writeInfos; + + for (const auto& write : writes.sampledImageWrites) { + const vk::DescriptorImageInfo imageInfo( + nullptr, + imageManager.getVulkanImageView(write.image), + vk::ImageLayout::eShaderReadOnlyOptimal + ); + + imageInfos.push_back(imageInfo); + + WriteDescriptorSetInfo vulkanWrite = { + imageInfos.size(), + 0, + write.binding, + vk::DescriptorType::eSampledImage, + }; + + writeInfos.push_back(vulkanWrite); + } + + for (const auto& write : writes.storageImageWrites) { + const vk::DescriptorImageInfo imageInfo( + nullptr, + imageManager.getVulkanImageView(write.image), + vk::ImageLayout::eGeneral + ); + + imageInfos.push_back(imageInfo); + + WriteDescriptorSetInfo vulkanWrite = { + imageInfos.size(), + 0, + write.binding, + vk::DescriptorType::eStorageImage + }; + + writeInfos.push_back(vulkanWrite); + } + + for (const auto& write : writes.uniformBufferWrites) { + const vk::DescriptorBufferInfo bufferInfo( + bufferManager.getBuffer(write.buffer), + static_cast<uint32_t>(0), + bufferManager.getBufferSize(write.buffer) + ); + + bufferInfos.push_back(bufferInfo); + + WriteDescriptorSetInfo vulkanWrite = { + 0, + bufferInfos.size(), + write.binding, + vk::DescriptorType::eUniformBuffer + }; + + writeInfos.push_back(vulkanWrite); + } + + for (const auto& write : writes.storageBufferWrites) { + const vk::DescriptorBufferInfo bufferInfo( + bufferManager.getBuffer(write.buffer), + static_cast<uint32_t>(0), + bufferManager.getBufferSize(write.buffer) + ); + + bufferInfos.push_back(bufferInfo); + + WriteDescriptorSetInfo vulkanWrite = { + 0, + bufferInfos.size(), + write.binding, + vk::DescriptorType::eStorageBuffer + }; + + writeInfos.push_back(vulkanWrite); + } + + for (const auto& write : writes.samplerWrites) { + const vk::Sampler& sampler = samplerManager.getVulkanSampler(write.sampler); + + const vk::DescriptorImageInfo imageInfo( + sampler, + nullptr, + vk::ImageLayout::eGeneral + ); + + imageInfos.push_back(imageInfo); + + WriteDescriptorSetInfo vulkanWrite = { + imageInfos.size(), + 0, + write.binding, + vk::DescriptorType::eSampler + }; + + writeInfos.push_back(vulkanWrite); + } + + std::vector<vk::WriteDescriptorSet> vulkanWrites; + + for (const auto& write : writeInfos) { + vk::WriteDescriptorSet vulkanWrite( + set, + write.binding, + static_cast<uint32_t>(0), + 1, + write.type, + (write.imageInfoIndex > 0? &(imageInfos[write.imageInfoIndex - 1]) : nullptr), + (write.bufferInfoIndex > 0? &(bufferInfos[write.bufferInfoIndex - 1]) : nullptr) + ); + + vulkanWrites.push_back(vulkanWrite); + } + + m_Device.updateDescriptorSets(vulkanWrites, nullptr); + } + + vk::DescriptorSet DescriptorManager::getDescriptorSet(ResourcesHandle handle, size_t index) { + return m_ResourceDescriptions[handle.getId()].descriptorSets[index]; + } + + vk::DescriptorSetLayout DescriptorManager::getDescriptorSetLayout(ResourcesHandle handle, size_t index) { + return m_ResourceDescriptions[handle.getId()].descriptorSetLayouts[index]; + } vk::DescriptorType DescriptorManager::convertDescriptorTypeFlag(DescriptorType type) { switch (type) @@ -95,9 +241,12 @@ namespace vkcv return vk::DescriptorType::eStorageBuffer; case DescriptorType::SAMPLER: return vk::DescriptorType::eSampler; - case DescriptorType::IMAGE: + case DescriptorType::IMAGE_SAMPLED: return vk::DescriptorType::eSampledImage; + case DescriptorType::IMAGE_STORAGE: + return vk::DescriptorType::eStorageImage; default: + std::cerr << "Error: DescriptorManager::convertDescriptorTypeFlag, unknown DescriptorType" << std::endl; return vk::DescriptorType::eUniformBuffer; } } diff --git a/src/vkcv/DescriptorManager.hpp b/src/vkcv/DescriptorManager.hpp index 744f557051bee3a6524abcf16822c3faa9c78576..937fb278cda241e9153881d5c27662ac929781cd 100644 --- a/src/vkcv/DescriptorManager.hpp +++ b/src/vkcv/DescriptorManager.hpp @@ -2,6 +2,11 @@ #include "vkcv/Handles.hpp" #include "vkcv/DescriptorConfig.hpp" +#include "vkcv/DescriptorWrites.hpp" + +#include "ImageManager.hpp" +#include "vkcv/BufferManager.hpp" +#include "SamplerManager.hpp" namespace vkcv { @@ -18,11 +23,22 @@ namespace vkcv * @param[in] vector of filled vkcv::DescriptorSet structs * @return index into that objects a resource handle */ - ResourcesHandle createResourceDescription(const std::vector<DescriptorSet> & descriptorSets); + ResourcesHandle createResourceDescription(const std::vector<DescriptorSetConfig> & descriptorSets); + + void writeResourceDescription( + ResourcesHandle handle, + size_t setIndex, + const DescriptorWrites& writes, + const ImageManager& imageManager, + const BufferManager& bufferManager, + const SamplerManager& samplerManager); + + vk::DescriptorSet getDescriptorSet(ResourcesHandle handle, size_t index); + vk::DescriptorSetLayout getDescriptorSetLayout(ResourcesHandle handle, size_t index); private: - vk::Device m_Device; - vk::DescriptorPool m_Pool; + vk::Device m_Device; + vk::DescriptorPool m_Pool; /** diff --git a/src/vkcv/Framebuffer.cpp b/src/vkcv/Framebuffer.cpp deleted file mode 100644 index 3b3e8000668e460d476b211984e9e12249f066c0..0000000000000000000000000000000000000000 --- a/src/vkcv/Framebuffer.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "Framebuffer.hpp" - -namespace vkcv { - vk::Framebuffer createFramebuffer(const vk::Device device, const vk::RenderPass renderpass, - const int width, const int height, const vk::ImageView imageView) { - const vk::FramebufferCreateFlags flags = {}; - const uint32_t attachmentCount = 1; // TODO: proper value - const vk::FramebufferCreateInfo createInfo(flags, renderpass, attachmentCount, &imageView, width, height, 1); - return device.createFramebuffer(createInfo, nullptr, {}); - } -} \ No newline at end of file diff --git a/src/vkcv/Framebuffer.hpp b/src/vkcv/Framebuffer.hpp deleted file mode 100644 index 7d5d718adbde0c3f8eb8d97c539fb73f7771987f..0000000000000000000000000000000000000000 --- a/src/vkcv/Framebuffer.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include <vulkan/vulkan.hpp> - -namespace vkcv{ - vk::Framebuffer createFramebuffer(const vk::Device device, const vk::RenderPass renderpass, - const int width, const int height, const vk::ImageView imageView); -} \ No newline at end of file diff --git a/src/vkcv/Handles.cpp b/src/vkcv/Handles.cpp index 1337a132ae0b8721bd2776e24212f73dfb94ae46..bd465d4e71da31c554d82ceb4b8bce7b0ee04129 100644 --- a/src/vkcv/Handles.cpp +++ b/src/vkcv/Handles.cpp @@ -1 +1,33 @@ #include "vkcv/Handles.hpp" + +namespace vkcv { + + Handle::Handle() : + m_id(UINT64_MAX) + {} + + Handle::Handle(uint64_t id) : + m_id(id) + {} + + uint64_t Handle::getId() const { + return m_id; + } + + Handle::operator bool() const { + return (m_id < UINT64_MAX); + } + + bool Handle::operator!() const { + return (m_id == UINT64_MAX); + } + + std::ostream& operator << (std::ostream& out, const Handle& handle) { + if (handle) { + return out << "[Handle: " << handle.getId() << "]"; + } else { + return out << "[Handle: none]"; + } + } + +} diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7104f784231418da4f1c26364459dbf735fdde97 --- /dev/null +++ b/src/vkcv/Image.cpp @@ -0,0 +1,62 @@ +/** + * @authors Lars Hoerttrich + * @file vkcv/Image.cpp + * @brief class for image handles + */ +#include "vkcv/Image.hpp" +#include "ImageManager.hpp" + +namespace vkcv{ + + Image Image::create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth) + { + return Image(manager, manager->createImage(width, height, depth, format), format, width, height, depth); + } + + vk::Format Image::getFormat() const { + return m_format; + } + + uint32_t Image::getWidth() const { + return m_width; + } + + uint32_t Image::getHeight() const { + return m_height; + } + + uint32_t Image::getDepth() const { + return m_depth; + } + + vk::ImageLayout Image::getLayout() const { + return m_layout; + } + + void Image::switchLayout(vk::ImageLayout newLayout) + { + m_manager->switchImageLayout(m_handle, m_layout, newLayout); + m_layout = newLayout; + } + + vkcv::ImageHandle Image::getHandle() const { + return m_handle; + } + + void Image::fill(void *data, size_t size) { + m_manager->fillImage(m_handle, data, size); + } + + Image::Image(ImageManager* manager, const ImageHandle& handle, + vk::Format format, uint32_t width, uint32_t height, uint32_t depth) : + m_manager(manager), + m_handle(handle), + m_format(format), + m_width(width), + m_height(height), + m_depth(depth), + m_layout(vk::ImageLayout::eUndefined) + { + } + +} diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3896d6bc4abdd24264ad5d468b49ebf08bd20be7 --- /dev/null +++ b/src/vkcv/ImageManager.cpp @@ -0,0 +1,397 @@ +/** + * @authors Lars Hoerttrich + * @file vkcv/ImageManager.cpp + * @brief class creating and managing images + */ +#include "ImageManager.hpp" +#include "vkcv/Core.hpp" + +#include <algorithm> + +namespace vkcv { + + /** + * @brief searches memory type index for image allocation, combines requirements of image and application + * @param physicalMemoryProperties Memory Properties of physical device + * @param typeBits Bit field for suitable memory types + * @param requirements Property flags that are required + * @return memory type index for image + */ + uint32_t searchImageMemoryType(const vk::PhysicalDeviceMemoryProperties& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) { + const uint32_t memoryCount = physicalMemoryProperties.memoryTypeCount; + for (uint32_t memoryIndex = 0; memoryIndex < memoryCount; ++memoryIndex) { + const uint32_t memoryTypeBits = (1 << memoryIndex); + const bool isRequiredMemoryType = typeBits & memoryTypeBits; + + const vk::MemoryPropertyFlags properties = + physicalMemoryProperties.memoryTypes[memoryIndex].propertyFlags; + const bool hasRequiredProperties = + (properties & requirements) == requirements; + + if (isRequiredMemoryType && hasRequiredProperties) + return static_cast<int32_t>(memoryIndex); + } + + // failed to find memory type + return -1; + } + + ImageManager::ImageManager(BufferManager& bufferManager) noexcept : + m_core(nullptr), m_bufferManager(bufferManager), m_images() + { + } + + ImageManager::~ImageManager() noexcept { + for (uint64_t id = 0; id < m_images.size(); id++) { + destroyImage(ImageHandle(id)); + } + } + + bool isDepthImageFormat(vk::Format format) { + if ((format == vk::Format::eD16Unorm) || (format == vk::Format::eD16UnormS8Uint) || + (format == vk::Format::eD24UnormS8Uint) || (format == vk::Format::eD32Sfloat) || + (format == vk::Format::eD32SfloatS8Uint)) { + return true; + } else { + return false; + } + } + + ImageHandle ImageManager::createImage(uint32_t width, uint32_t height, uint32_t depth, vk::Format format) + { + const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice(); + + const vk::FormatProperties formatProperties = physicalDevice.getFormatProperties(format); + + vk::ImageCreateFlags createFlags; + vk::ImageUsageFlags imageUsageFlags = ( + vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst + ); + + const bool isDepthFormat = isDepthImageFormat(format); + + if (isDepthFormat) { + imageUsageFlags |= vk::ImageUsageFlagBits::eDepthStencilAttachment; + } + + const vk::Device& device = m_core->getContext().getDevice(); + + vk::ImageType imageType = vk::ImageType::e3D; + vk::ImageViewType imageViewType = vk::ImageViewType::e3D; + + if (depth <= 1) { + if (height <= 1) { + imageType = vk::ImageType::e1D; + imageViewType = vk::ImageViewType::e1D; + } else { + imageType = vk::ImageType::e2D; + imageViewType = vk::ImageViewType::e2D; + } + } + + vk::ImageTiling imageTiling = vk::ImageTiling::eOptimal; + + if (!formatProperties.optimalTilingFeatures) { + if (!formatProperties.linearTilingFeatures) + return ImageHandle(); + + imageTiling = vk::ImageTiling::eLinear; + } + + const vk::ImageFormatProperties imageFormatProperties = physicalDevice.getImageFormatProperties( + format, imageType, imageTiling, imageUsageFlags + ); + + const uint32_t mipLevels = std::min<uint32_t>(1, imageFormatProperties.maxMipLevels); + const uint32_t arrayLayers = std::min<uint32_t>(1, imageFormatProperties.maxArrayLayers); + + const vk::ImageCreateInfo imageCreateInfo( + createFlags, + imageType, + format, + vk::Extent3D(width, height, depth), + mipLevels, + arrayLayers, + vk::SampleCountFlagBits::e1, + imageTiling, + imageUsageFlags, + vk::SharingMode::eExclusive, + {}, + vk::ImageLayout::eUndefined + ); + + vk::Image image = device.createImage(imageCreateInfo); + + const vk::MemoryRequirements requirements = device.getImageMemoryRequirements(image); + + vk::MemoryPropertyFlags memoryTypeFlags = vk::MemoryPropertyFlagBits::eDeviceLocal; + + const uint32_t memoryTypeIndex = searchImageMemoryType( + physicalDevice.getMemoryProperties(), + requirements.memoryTypeBits, + memoryTypeFlags + ); + + vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryTypeIndex)); + device.bindImageMemory(image, memory, 0); + + vk::ImageAspectFlags aspectFlags; + + if (isDepthFormat) { + aspectFlags = vk::ImageAspectFlagBits::eDepth; + } else { + aspectFlags = vk::ImageAspectFlagBits::eColor; + } + + const vk::ImageViewCreateInfo imageViewCreateInfo ( + {}, + image, + imageViewType, + format, + vk::ComponentMapping( + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity + ), + vk::ImageSubresourceRange( + aspectFlags, + 0, + mipLevels, + 0, + arrayLayers + ) + ); + + vk::ImageView view = device.createImageView(imageViewCreateInfo); + + const uint64_t id = m_images.size(); + m_images.push_back({ image, memory, view, width, height, depth, format, arrayLayers, mipLevels }); + return ImageHandle(id); + } + + vk::Image ImageManager::getVulkanImage(const ImageHandle &handle) const { + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + return nullptr; + } + + auto& image = m_images[id]; + + return image.m_handle; + } + + vk::DeviceMemory ImageManager::getVulkanDeviceMemory(const ImageHandle &handle) const { + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + return nullptr; + } + + auto& image = m_images[id]; + + return image.m_memory; + } + + vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle) const { + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + return nullptr; + } + + auto& image = m_images[id]; + + return image.m_view; + } + + void ImageManager::switchImageLayout(const ImageHandle& handle, vk::ImageLayout oldLayout, vk::ImageLayout newLayout) { + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + return; + } + + auto& image = m_images[id]; + + //alternativly we could use switch case for every variable to set + vk::AccessFlags sourceAccessMask; + vk::PipelineStageFlags sourceStage; + + vk::AccessFlags destinationAccessMask; + vk::PipelineStageFlags destinationStage; + + if ((oldLayout == vk::ImageLayout::eUndefined) && + (newLayout == vk::ImageLayout::eTransferDstOptimal)) + { + destinationAccessMask = vk::AccessFlagBits::eTransferWrite; + + sourceStage = vk::PipelineStageFlagBits::eTopOfPipe; + destinationStage = vk::PipelineStageFlagBits::eTransfer; + } + else if ((oldLayout == vk::ImageLayout::eTransferDstOptimal) && + (newLayout == vk::ImageLayout::eShaderReadOnlyOptimal)) + { + sourceAccessMask = vk::AccessFlagBits::eTransferWrite; + destinationAccessMask = vk::AccessFlagBits::eShaderRead; + + sourceStage = vk::PipelineStageFlagBits::eTransfer; + destinationStage = vk::PipelineStageFlagBits::eFragmentShader; + } + + vk::ImageAspectFlags aspectFlags; + + if (isDepthImageFormat(image.m_format)) { + aspectFlags = vk::ImageAspectFlagBits::eDepth; + } else { + aspectFlags = vk::ImageAspectFlagBits::eColor; + } + + vk::ImageSubresourceRange imageSubresourceRange( + aspectFlags, + 0, + image.m_levels, + 0, + image.m_layers + ); + + vk::ImageMemoryBarrier imageMemoryBarrier( + sourceAccessMask, + destinationAccessMask, + oldLayout, + newLayout, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + image.m_handle, + imageSubresourceRange + ); + + SubmitInfo submitInfo; + submitInfo.queueType = QueueType::Graphics; + + m_core->submitCommands( + submitInfo, + [sourceStage, destinationStage, imageMemoryBarrier](const vk::CommandBuffer& commandBuffer) { + commandBuffer.pipelineBarrier( + sourceStage, + destinationStage, + {}, + nullptr, + nullptr, + imageMemoryBarrier + ); + }, + nullptr + ); + } + + void ImageManager::fillImage(const ImageHandle& handle, void* data, size_t size) + { + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + return; + } + + auto& image = m_images[id]; + + switchImageLayout( + handle, + vk::ImageLayout::eUndefined, + vk::ImageLayout::eTransferDstOptimal + ); + + uint32_t channels = 4; // TODO: check image.m_format + const size_t image_size = ( + image.m_width * image.m_height * image.m_depth * channels + ); + + const size_t max_size = std::min(size, image_size); + + BufferHandle bufferHandle = m_bufferManager.createBuffer( + BufferType::STAGING, max_size, BufferMemoryType::HOST_VISIBLE + ); + + m_bufferManager.fillBuffer(bufferHandle, data, max_size, 0); + + vk::Buffer stagingBuffer = m_bufferManager.getBuffer(bufferHandle); + + SubmitInfo submitInfo; + submitInfo.queueType = QueueType::Transfer; + + m_core->submitCommands( + submitInfo, + [&image, &stagingBuffer](const vk::CommandBuffer& commandBuffer) { + vk::ImageAspectFlags aspectFlags; + + if (isDepthImageFormat(image.m_format)) { + aspectFlags = vk::ImageAspectFlagBits::eDepth; + } else { + aspectFlags = vk::ImageAspectFlagBits::eColor; + } + + const vk::BufferImageCopy region ( + 0, + 0, + 0, + vk::ImageSubresourceLayers( + aspectFlags, + 0, + 0, + image.m_layers + ), + vk::Offset3D(0, 0, 0), + vk::Extent3D(image.m_width, image.m_height, image.m_depth) + ); + + commandBuffer.copyBufferToImage( + stagingBuffer, + image.m_handle, + vk::ImageLayout::eTransferDstOptimal, + 1, + ®ion + ); + }, + [&]() { + switchImageLayout( + handle, + vk::ImageLayout::eTransferDstOptimal, + vk::ImageLayout::eShaderReadOnlyOptimal + ); + + m_bufferManager.destroyBuffer(bufferHandle); + } + ); + } + + void ImageManager::destroyImage(const ImageHandle& handle) + { + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + return; + } + + auto& image = m_images[id]; + + const vk::Device& device = m_core->getContext().getDevice(); + + if (image.m_view) { + device.destroyImageView(image.m_view); + image.m_view = nullptr; + } + + if (image.m_memory) { + device.freeMemory(image.m_memory); + image.m_memory = nullptr; + } + + if (image.m_handle) { + device.destroyImage(image.m_handle); + image.m_handle = nullptr; + } + } + + +} \ No newline at end of file diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7dc6746f37b7d03900302afbd0536b909f9e48fc --- /dev/null +++ b/src/vkcv/ImageManager.hpp @@ -0,0 +1,70 @@ +#pragma once +/** + * @authors Lars Hoerttrich + * @file vkcv/ImageManager.hpp + * @brief class creating and managing images + */ +#include <vector> +#include <vulkan/vulkan.hpp> + +#include "vkcv/BufferManager.hpp" +#include "vkcv/Handles.hpp" + +namespace vkcv { + + class ImageManager + { + friend class Core; + private: + struct Image + { + vk::Image m_handle; + vk::DeviceMemory m_memory; + vk::ImageView m_view; + uint32_t m_width = 0; + uint32_t m_height = 0; + uint32_t m_depth = 0; + vk::Format m_format; + uint32_t m_layers = 1; + uint32_t m_levels = 1; + }; + + Core* m_core; + BufferManager& m_bufferManager; + + std::vector<Image> m_images; + + ImageManager(BufferManager& bufferManager) noexcept; + + public: + ~ImageManager() noexcept; + ImageManager(ImageManager&& other) = delete; + ImageManager(const ImageManager& other) = delete; + + ImageManager& operator=(ImageManager&& other) = delete; + ImageManager& operator=(const ImageManager& other) = delete; + + ImageHandle createImage(uint32_t width, uint32_t height, uint32_t depth, vk::Format format); + + [[nodiscard]] + vk::Image getVulkanImage(const ImageHandle& handle) const; + + [[nodiscard]] + vk::DeviceMemory getVulkanDeviceMemory(const ImageHandle& handle) const; + + [[nodiscard]] + vk::ImageView getVulkanImageView(const ImageHandle& handle) const; + + void switchImageLayout(const ImageHandle& handle, vk::ImageLayout oldLayout, vk::ImageLayout newLayout); + void fillImage(const ImageHandle& handle, void* data, size_t size); + + /** + * Destroys and deallocates image represented by a given + * buffer handle. + * + * @param handle Image handle + */ + void destroyImage(const ImageHandle& handle); + + }; +} \ No newline at end of file diff --git a/src/vkcv/PassManager.cpp b/src/vkcv/PassManager.cpp index 820bf3ff8a84edb9d070c22c60327cf7cb661ee7..26e5f290d04ebaf16940cd99386253b5ab3622cc 100644 --- a/src/vkcv/PassManager.cpp +++ b/src/vkcv/PassManager.cpp @@ -49,17 +49,17 @@ namespace vkcv PassManager::PassManager(vk::Device device) noexcept : m_Device{device}, - m_RenderPasses{}, - m_NextPassId{1} + m_Passes{}, + m_NextPassId(0) {} PassManager::~PassManager() noexcept { - for(const auto &pass : m_RenderPasses) - m_Device.destroy(pass); - - m_RenderPasses.clear(); - m_NextPassId = 1; + for(const auto &pass : m_Passes) + m_Device.destroy(pass.m_Handle); + + m_Passes.clear(); + m_NextPassId = 0; } PassHandle PassManager::createPass(const PassConfig &config) @@ -90,46 +90,74 @@ namespace vkcv colorAttachmentReferences.push_back(attachmentRef); } - vk::AttachmentDescription attachmentDesc({}, - format, - vk::SampleCountFlagBits::e1, - getVKLoadOpFromAttachOp(config.attachments[i].load_operation), - getVkStoreOpFromAttachOp(config.attachments[i].store_operation), - vk::AttachmentLoadOp::eDontCare, - vk::AttachmentStoreOp::eDontCare, - getVkLayoutFromAttachLayout(config.attachments[i].layout_initial), - getVkLayoutFromAttachLayout(config.attachments[i].layout_final)); + vk::AttachmentDescription attachmentDesc( + {}, + format, + vk::SampleCountFlagBits::e1, + getVKLoadOpFromAttachOp(config.attachments[i].load_operation), + getVkStoreOpFromAttachOp(config.attachments[i].store_operation), + vk::AttachmentLoadOp::eDontCare, + vk::AttachmentStoreOp::eDontCare, + getVkLayoutFromAttachLayout(config.attachments[i].layout_initial), + getVkLayoutFromAttachLayout(config.attachments[i].layout_final) + ); + attachmentDescriptions.push_back(attachmentDesc); } - vk::SubpassDescription subpassDescription({}, - vk::PipelineBindPoint::eGraphics, - 0, - {}, - static_cast<uint32_t>(colorAttachmentReferences.size()), - colorAttachmentReferences.data(), - {}, - pDepthAttachment, - 0, - {}); - - vk::RenderPassCreateInfo passInfo({}, - static_cast<uint32_t>(attachmentDescriptions.size()), - attachmentDescriptions.data(), - 1, - &subpassDescription, - 0, - {}); + + const vk::SubpassDescription subpassDescription( + {}, + vk::PipelineBindPoint::eGraphics, + 0, + {}, + static_cast<uint32_t>(colorAttachmentReferences.size()), + colorAttachmentReferences.data(), + {}, + pDepthAttachment, + 0, + {} + ); - vk::RenderPass vkObject{nullptr}; - if(m_Device.createRenderPass(&passInfo, nullptr, &vkObject) != vk::Result::eSuccess) - return PassHandle{0}; + const vk::RenderPassCreateInfo passInfo( + {}, + static_cast<uint32_t>(attachmentDescriptions.size()), + attachmentDescriptions.data(), + 1, + &subpassDescription, + 0, + {} + ); - m_RenderPasses.push_back(vkObject); - return PassHandle{m_NextPassId++}; + vk::RenderPass renderPass = m_Device.createRenderPass(passInfo); + + m_Passes.push_back({ renderPass, config }); + return PassHandle(m_NextPassId++); } vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const { - return m_RenderPasses[handle.id - 1]; + const uint64_t id = handle.getId(); + + if (id >= m_Passes.size()) { + return nullptr; + } + + auto& pass = m_Passes[id]; + + return pass.m_Handle; + } + + const PassConfig& PassManager::getPassConfig(const PassHandle &handle) const { + const uint64_t id = handle.getId(); + + if (id >= m_Passes.size()) { + static PassConfig emptyConfig = PassConfig({}); + return emptyConfig; + } + + auto& pass = m_Passes[id]; + + return pass.m_Config; } + } diff --git a/src/vkcv/PassManager.hpp b/src/vkcv/PassManager.hpp index b6be2cb13d8d24bdb9759f8878917f99e31afbec..bfc20fe25ace95bd8d94832b953b6b14ab9cadee 100644 --- a/src/vkcv/PassManager.hpp +++ b/src/vkcv/PassManager.hpp @@ -10,8 +10,13 @@ namespace vkcv class PassManager { private: + struct Pass { + vk::RenderPass m_Handle; + PassConfig m_Config; + }; + vk::Device m_Device; - std::vector<vk::RenderPass> m_RenderPasses; + std::vector<Pass> m_Passes; uint64_t m_NextPassId; public: PassManager() = delete; // no default ctor @@ -28,5 +33,9 @@ namespace vkcv [[nodiscard]] vk::RenderPass getVkPass(const PassHandle &handle) const; + + [[nodiscard]] + const PassConfig& getPassConfig(const PassHandle &handle) const; + }; } diff --git a/src/vkcv/PipelineConfig.cpp b/src/vkcv/PipelineConfig.cpp index c2b1415f10188f082d8eca576c2143e82f99e7fa..d317258470bde76e8b8ba8e1f9bc684ea469b6c0 100644 --- a/src/vkcv/PipelineConfig.cpp +++ b/src/vkcv/PipelineConfig.cpp @@ -8,10 +8,19 @@ namespace vkcv { - PipelineConfig::PipelineConfig(const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, PassHandle &passHandle): + PipelineConfig::PipelineConfig( + const ShaderProgram& shaderProgram, + uint32_t width, + uint32_t height, + PassHandle &passHandle, + const std::vector<VertexAttribute> &vertexAttributes, + const std::vector<vk::DescriptorSetLayout> &descriptorLayouts) + : m_ShaderProgram(shaderProgram), m_Height(height), m_Width(width), - m_PassHandle(passHandle) + m_PassHandle(passHandle), + m_vertexAttributes(vertexAttributes), + m_descriptorLayouts(descriptorLayouts) {} } diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp index 576cb47397a29184160423fdba6d1a09104a5566..1b0d665e9011b9c652a69e35c295a701a6029dd2 100644 --- a/src/vkcv/PipelineManager.cpp +++ b/src/vkcv/PipelineManager.cpp @@ -7,7 +7,7 @@ namespace vkcv m_Device{device}, m_Pipelines{}, m_PipelineLayouts{}, - m_NextPipelineId{1} + m_NextPipelineId{0} {} PipelineManager::~PipelineManager() noexcept @@ -20,17 +20,34 @@ namespace vkcv m_Pipelines.clear(); m_PipelineLayouts.clear(); - m_NextPipelineId = 1; + m_NextPipelineId = 0; } - PipelineHandle PipelineManager::createPipeline(const PipelineConfig &config, const vk::RenderPass &pass) + // currently assuming default 32 bit formats, no lower precision or normalized variants supported + vk::Format vertexFormatToVulkanFormat(const VertexFormat format) { + switch (format) { + case VertexFormat::FLOAT: return vk::Format::eR32Sfloat; + case VertexFormat::FLOAT2: return vk::Format::eR32G32Sfloat; + case VertexFormat::FLOAT3: return vk::Format::eR32G32B32Sfloat; + case VertexFormat::FLOAT4: return vk::Format::eR32G32B32A32Sfloat; + case VertexFormat::INT: return vk::Format::eR32Sint; + case VertexFormat::INT2: return vk::Format::eR32G32Sint; + case VertexFormat::INT3: return vk::Format::eR32G32B32Sint; + case VertexFormat::INT4: return vk::Format::eR32G32B32A32Sint; + default: std::cerr << "Warning: Unknown vertex format" << std::endl; return vk::Format::eUndefined; + } + } + + PipelineHandle PipelineManager::createPipeline(const PipelineConfig &config, PassManager& passManager) { + const vk::RenderPass &pass = passManager.getVkPass(config.m_PassHandle); + const bool existsVertexShader = config.m_ShaderProgram.existsShader(ShaderStage::VERTEX); const bool existsFragmentShader = config.m_ShaderProgram.existsShader(ShaderStage::FRAGMENT); if (!(existsVertexShader && existsFragmentShader)) { std::cout << "Core::createGraphicsPipeline requires vertex and fragment shader code" << std::endl; - return PipelineHandle{0}; + return PipelineHandle(); } // vertex shader stage @@ -38,7 +55,7 @@ namespace vkcv vk::ShaderModuleCreateInfo vertexModuleInfo({}, vertexCode.size(), reinterpret_cast<uint32_t*>(vertexCode.data())); vk::ShaderModule vertexModule{}; if (m_Device.createShaderModule(&vertexModuleInfo, nullptr, &vertexModule) != vk::Result::eSuccess) - return PipelineHandle{0}; + return PipelineHandle(); vk::PipelineShaderStageCreateInfo pipelineVertexShaderStageInfo( {}, @@ -55,7 +72,7 @@ namespace vkcv if (m_Device.createShaderModule(&fragmentModuleInfo, nullptr, &fragmentModule) != vk::Result::eSuccess) { m_Device.destroy(vertexModule); - return PipelineHandle{0}; + return PipelineHandle(); } vk::PipelineShaderStageCreateInfo pipelineFragmentShaderStageInfo( @@ -67,15 +84,38 @@ namespace vkcv ); // vertex input state - vk::VertexInputBindingDescription vertexInputBindingDescription(0, 12, vk::VertexInputRate::eVertex); - vk::VertexInputAttributeDescription vertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, 0); + // Fill up VertexInputBindingDescription and VertexInputAttributeDescription Containers + std::vector<vk::VertexInputAttributeDescription> vertexAttributeDescriptions; + std::vector<vk::VertexInputBindingDescription> vertexBindingDescriptions; + + VertexLayout layout = config.m_ShaderProgram.getVertexLayout(); + std::unordered_map<uint32_t, VertexInputAttachment> attachments = layout.attachmentMap; + + for (int i = 0; i < attachments.size(); i++) { + VertexInputAttachment &attachment = attachments.at(i); + + uint32_t location = attachment.location; + uint32_t binding = i; + vk::Format vertexFormat = vertexFormatToVulkanFormat(attachment.format); + + //FIXME: hoping that order is the same and compatible: add explicit mapping and validation + const VertexAttribute attribute = config.m_vertexAttributes[i]; + + vertexAttributeDescriptions.push_back({location, binding, vertexFormatToVulkanFormat(attachment.format), 0}); + vertexBindingDescriptions.push_back(vk::VertexInputBindingDescription( + binding, + attribute.stride + getFormatSize(attachment.format), + vk::VertexInputRate::eVertex)); + } + + // Handover Containers to PipelineVertexInputStateCreateIngo Struct vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo( - {}, // no vertex input until vertex buffer is implemented - 0, // 1, - nullptr, // &vertexInputBindingDescription, - 0, // 1, - nullptr // &vertexInputAttributeDescription + {}, + vertexBindingDescriptions.size(), + vertexBindingDescriptions.data(), + vertexAttributeDescriptions.size(), + vertexAttributeDescriptions.data() ); // input assembly state @@ -146,19 +186,43 @@ namespace vkcv // pipeline layout vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo( {}, - {}, + (config.m_descriptorLayouts), (pushConstantRange)); vk::PipelineLayout vkPipelineLayout{}; if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess) { m_Device.destroy(vertexModule); m_Device.destroy(fragmentModule); - return PipelineHandle{0}; + return PipelineHandle(); } - - // graphics pipeline create + + const vk::PipelineDepthStencilStateCreateInfo depthStencilCreateInfo( + vk::PipelineDepthStencilStateCreateFlags(), + true, + true, + vk::CompareOp::eLessOrEqual, + false, + false, + {}, + {}, + 0.0f, + 1.0f + ); + + const vk::PipelineDepthStencilStateCreateInfo* p_depthStencilCreateInfo = nullptr; + + const PassConfig& passConfig = passManager.getPassConfig(config.m_PassHandle); + + for (const auto& attachment : passConfig.attachments) { + if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { + p_depthStencilCreateInfo = &depthStencilCreateInfo; + break; + } + } + + // graphics pipeline create std::vector<vk::PipelineShaderStageCreateInfo> shaderStages = { pipelineVertexShaderStageInfo, pipelineFragmentShaderStageInfo }; - vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo( + const vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo( {}, static_cast<uint32_t>(shaderStages.size()), shaderStages.data(), @@ -168,7 +232,7 @@ namespace vkcv &pipelineViewportStateCreateInfo, &pipelineRasterizationStateCreateInfo, &pipelineMultisampleStateCreateInfo, - nullptr, + p_depthStencilCreateInfo, &pipelineColorBlendStateCreateInfo, nullptr, vkPipelineLayout, @@ -183,7 +247,7 @@ namespace vkcv { m_Device.destroy(vertexModule); m_Device.destroy(fragmentModule); - return PipelineHandle{0}; + return PipelineHandle(); } m_Device.destroy(vertexModule); @@ -191,16 +255,16 @@ namespace vkcv m_Pipelines.push_back(vkPipeline); m_PipelineLayouts.push_back(vkPipelineLayout); - return PipelineHandle{m_NextPipelineId++}; + return PipelineHandle(m_NextPipelineId++); } vk::Pipeline PipelineManager::getVkPipeline(const PipelineHandle &handle) const { - return m_Pipelines.at(handle.id -1); + return m_Pipelines.at(handle.getId()); } vk::PipelineLayout PipelineManager::getVkPipelineLayout(const PipelineHandle &handle) const { - return m_PipelineLayouts.at(handle.id - 1); + return m_PipelineLayouts.at(handle.getId()); } } \ No newline at end of file diff --git a/src/vkcv/PipelineManager.hpp b/src/vkcv/PipelineManager.hpp index b5c0948efa13a4021f424cc576f1403a1ec26ebe..896d0df1ce10f56d291ef1accf93f9783cdd9db4 100644 --- a/src/vkcv/PipelineManager.hpp +++ b/src/vkcv/PipelineManager.hpp @@ -4,6 +4,7 @@ #include <vector> #include "vkcv/Handles.hpp" #include "vkcv/PipelineConfig.hpp" +#include "PassManager.hpp" namespace vkcv { @@ -25,7 +26,7 @@ namespace vkcv PipelineManager & operator=(const PipelineManager &other) = delete; // copy-assign op PipelineManager & operator=(PipelineManager &&other) = delete; // move-assign op - PipelineHandle createPipeline(const PipelineConfig &config, const vk::RenderPass &pass); + PipelineHandle createPipeline(const PipelineConfig &config, PassManager& passManager); [[nodiscard]] vk::Pipeline getVkPipeline(const PipelineHandle &handle) const; diff --git a/src/vkcv/SamplerManager.cpp b/src/vkcv/SamplerManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7935bbc16a54f170cb38161629c109d56cf56d3c --- /dev/null +++ b/src/vkcv/SamplerManager.cpp @@ -0,0 +1,127 @@ + +#include "SamplerManager.hpp" +#include "vkcv/Core.hpp" + +namespace vkcv { + + SamplerManager::SamplerManager(const vk::Device& device) noexcept : + m_device(device), m_samplers() + {} + + SamplerManager::~SamplerManager() { + for (uint64_t id = 0; id < m_samplers.size(); id++) { + destroySampler(SamplerHandle(id)); + } + } + + SamplerHandle SamplerManager::createSampler(SamplerFilterType magFilter, + SamplerFilterType minFilter, + SamplerMipmapMode mipmapMode, + SamplerAddressMode addressMode) { + vk::Filter vkMagFilter; + vk::Filter vkMinFilter; + vk::SamplerMipmapMode vkMipmapMode; + vk::SamplerAddressMode vkAddressMode; + + switch (magFilter) { + case SamplerFilterType::NEAREST: + vkMagFilter = vk::Filter::eNearest; + break; + case SamplerFilterType::LINEAR: + vkMagFilter = vk::Filter::eLinear; + break; + default: + return SamplerHandle(); + } + + switch (minFilter) { + case SamplerFilterType::NEAREST: + vkMinFilter = vk::Filter::eNearest; + break; + case SamplerFilterType::LINEAR: + vkMinFilter = vk::Filter::eLinear; + break; + default: + return SamplerHandle(); + } + + switch (mipmapMode) { + case SamplerMipmapMode::NEAREST: + vkMipmapMode = vk::SamplerMipmapMode::eNearest; + break; + case SamplerMipmapMode::LINEAR: + vkMipmapMode = vk::SamplerMipmapMode::eLinear; + break; + default: + return SamplerHandle(); + } + + switch (addressMode) { + case SamplerAddressMode::REPEAT: + vkAddressMode = vk::SamplerAddressMode::eRepeat; + break; + case SamplerAddressMode::MIRRORED_REPEAT: + vkAddressMode = vk::SamplerAddressMode::eMirroredRepeat; + break; + case SamplerAddressMode::CLAMP_TO_EDGE: + vkAddressMode = vk::SamplerAddressMode::eClampToEdge; + break; + case SamplerAddressMode::MIRROR_CLAMP_TO_EDGE: + vkAddressMode = vk::SamplerAddressMode::eMirrorClampToEdge; + break; + default: + return SamplerHandle(); + } + + const vk::SamplerCreateInfo samplerCreateInfo ( + vk::SamplerCreateFlags(), + vkMagFilter, + vkMinFilter, + vkMipmapMode, + vkAddressMode, + vkAddressMode, + vkAddressMode, + 0.0f, + false, + 16.0f, + false, + vk::CompareOp::eAlways, + 0.0f, + 1.0f, + vk::BorderColor::eIntOpaqueBlack, + false + ); + + const vk::Sampler sampler = m_device.createSampler(samplerCreateInfo); + + const uint64_t id = m_samplers.size(); + m_samplers.push_back(sampler); + return SamplerHandle(id); + } + + vk::Sampler SamplerManager::getVulkanSampler(const SamplerHandle &handle) const { + const uint64_t id = handle.getId(); + + if (id >= m_samplers.size()) { + return nullptr; + } + + return m_samplers[id]; + } + + void SamplerManager::destroySampler(const SamplerHandle &handle) { + const uint64_t id = handle.getId(); + + if (id >= m_samplers.size()) { + return; + } + + auto& sampler = m_samplers[id]; + + if (sampler) { + m_device.destroySampler(sampler); + sampler = nullptr; + } + } + +} diff --git a/src/vkcv/SamplerManager.hpp b/src/vkcv/SamplerManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..41f58b2f33daaf8b08c785c05c7f14184cf47958 --- /dev/null +++ b/src/vkcv/SamplerManager.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include <vector> +#include <vulkan/vulkan.hpp> + +#include "vkcv/Handles.hpp" +#include "vkcv/Sampler.hpp" + +namespace vkcv { + + class Core; + + class SamplerManager { + friend class Core; + private: + vk::Device m_device; + std::vector<vk::Sampler> m_samplers; + + explicit SamplerManager(const vk::Device& device) noexcept; + + public: + ~SamplerManager(); + + SamplerManager(const SamplerManager& other) = delete; + SamplerManager(SamplerManager&& other) = delete; + + SamplerManager& operator=(const SamplerManager& other) = delete; + SamplerManager& operator=(SamplerManager&& other) = delete; + + SamplerHandle createSampler(SamplerFilterType magFilter, + SamplerFilterType minFilter, + SamplerMipmapMode mipmapMode, + SamplerAddressMode addressMode); + + [[nodiscard]] + vk::Sampler getVulkanSampler(const SamplerHandle& handle) const; + + void destroySampler(const SamplerHandle& handle); + + }; + +} diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp index 87ccdefbfec0b4891d3152d30aa6c9f6c8c0d5ea..5185b8b402eae5cd514689ba51a06e1a437271bf 100644 --- a/src/vkcv/ShaderProgram.cpp +++ b/src/vkcv/ShaderProgram.cpp @@ -27,8 +27,46 @@ namespace vkcv { return buffer; } + VertexFormat convertFormat(spirv_cross::SPIRType::BaseType basetype, uint32_t vecsize){ + switch (basetype) { + case spirv_cross::SPIRType::Int: + switch (vecsize) { + case 1: + return VertexFormat::INT; + case 2: + return VertexFormat::INT2; + case 3: + return VertexFormat::INT3; + case 4: + return VertexFormat::INT4; + default: + break; + } + break; + case spirv_cross::SPIRType::Float: + switch (vecsize) { + case 1: + return VertexFormat::FLOAT; + case 2: + return VertexFormat::FLOAT2; + case 3: + return VertexFormat::FLOAT3; + case 4: + return VertexFormat::FLOAT4; + default: + break; + } + break; + default: + break; + } + std::cout << "Shader Program Reflection: unknown Vertex Format" << std::endl; + return VertexFormat::FLOAT; + } + ShaderProgram::ShaderProgram() noexcept : - m_Shaders{} + m_Shaders{}, + m_VertexLayout{} {} bool ShaderProgram::addShader(ShaderStage shaderStage, const std::filesystem::path &shaderPath) @@ -59,4 +97,40 @@ namespace vkcv { else return true; } + + void ShaderProgram::reflectShader(ShaderStage shaderStage) + { + auto shaderCodeChar = m_Shaders.at(shaderStage).shaderCode; + std::vector<uint32_t> shaderCode; + + for (uint32_t i = 0; i < shaderCodeChar.size()/4; i++) { + shaderCode.push_back(((uint32_t*) shaderCodeChar.data())[i]); + } + + spirv_cross::Compiler comp(move(shaderCode)); + spirv_cross::ShaderResources resources = comp.get_shader_resources(); + + if (shaderStage == ShaderStage::VERTEX) { + std::vector<VertexInputAttachment> inputVec; + uint32_t offset = 0; + + for (uint32_t i = 0; i < resources.stage_inputs.size(); i++) { + auto& u = resources.stage_inputs[i]; + const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id); + + VertexInputAttachment input = VertexInputAttachment(comp.get_decoration(u.id, spv::DecorationLocation), + 0, + convertFormat(base_type.basetype, base_type.vecsize), + offset); + inputVec.push_back(input); + offset += base_type.vecsize * base_type.width / 8; + } + + m_VertexLayout = VertexLayout(inputVec); + } + } + + const VertexLayout& ShaderProgram::getVertexLayout() const{ + return m_VertexLayout; + } } diff --git a/src/vkcv/VertexLayout.cpp b/src/vkcv/VertexLayout.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b06c6743e1e19a5e282af248ab6b590eb97529fd --- /dev/null +++ b/src/vkcv/VertexLayout.cpp @@ -0,0 +1,53 @@ +// +// Created by Charlotte on 28.05.2021. +// + +#include "vkcv/VertexLayout.hpp" + +namespace vkcv { + uint32_t getFormatSize(VertexFormat format) { + switch (format) { + case VertexFormat::FLOAT: + return 4; + case VertexFormat::FLOAT2: + return 8; + case VertexFormat::FLOAT3: + return 12; + case VertexFormat::FLOAT4: + return 16; + case VertexFormat::INT: + return 4; + case VertexFormat::INT2: + return 8; + case VertexFormat::INT3: + return 12; + case VertexFormat::INT4: + return 16; + default: + break; + } + std::cout << "VertexLayout: No format given" << std::endl; + return 0; + } + + VertexInputAttachment::VertexInputAttachment(uint32_t location, uint32_t binding, VertexFormat format, uint32_t offset) noexcept: + location{location}, + binding{binding}, + format{format}, + offset{offset} + {} + + VertexLayout::VertexLayout() noexcept : + stride{0}, + attachmentMap() + {} + + VertexLayout::VertexLayout(const std::vector<VertexInputAttachment> &inputs) noexcept { + stride = 0; + for (const auto &input : inputs) { + attachmentMap.insert(std::make_pair(input.location, input)); + stride += getFormatSize(input.format); + } + } + +} \ No newline at end of file