diff --git a/config/Sources.cmake b/config/Sources.cmake index 6fc477cc9552d0a9a8921151ca4435b894630755..4f673e00d1e42e733534480d6085affd651a8c04 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -29,10 +29,14 @@ set(vkcv_sources ${vkcv_source}/vkcv/ImageManager.hpp ${vkcv_source}/vkcv/ImageManager.cpp + + ${vkcv_include}/vkcv/Logger.hpp ${vkcv_include}/vkcv/SwapChain.hpp ${vkcv_source}/vkcv/SwapChain.cpp - + + ${vkcv_include}/vkcv/ShaderStage.hpp + ${vkcv_include}/vkcv/ShaderProgram.hpp ${vkcv_source}/vkcv/ShaderProgram.cpp @@ -69,4 +73,12 @@ set(vkcv_sources ${vkcv_source}/vkcv/SamplerManager.cpp ${vkcv_include}/vkcv/DescriptorWrites.hpp + + ${vkcv_include}/vkcv/DrawcallRecording.hpp + ${vkcv_source}/vkcv/DrawcallRecording.cpp + + ${vkcv_include}/vkcv/CommandStreamManager.hpp + ${vkcv_source}/vkcv/CommandStreamManager.cpp + + ${vkcv_include}/vkcv/CommandRecordingFunctionTypes.hpp ) diff --git a/config/lib/SPIRV_Cross.cmake b/config/lib/SPIRV_Cross.cmake index 751ee883c47e0eab081a13e5805ced6f2daa7e30..2e705d7d5a006e3851d14d22a57fd667c61c79f5 100644 --- a/config/lib/SPIRV_Cross.cmake +++ b/config/lib/SPIRV_Cross.cmake @@ -6,9 +6,20 @@ if (spirv-cross_FOUND) message(${vkcv_config_msg} " SPIRV Cross - " ${SPIRV_CROSS_VERSION}) else() if (EXISTS "${vkcv_lib_path}/SPIRV-Cross") + set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS OFF CACHE INTERNAL "") + set(SPIRV_CROSS_SHARED OFF CACHE INTERNAL "") + set(SPIRV_CROSS_STATIC ON CACHE INTERNAL "") set(SPIRV_CROSS_CLI OFF CACHE INTERNAL "") set(SPIRV_CROSS_ENABLE_TESTS OFF CACHE INTERNAL "") + + set(SPIRV_CROSS_ENABLE_GLSL ON CACHE INTERNAL "") + set(SPIRV_CROSS_ENABLE_HLSL OFF CACHE INTERNAL "") + set(SPIRV_CROSS_ENABLE_MSL OFF CACHE INTERNAL "") + set(SPIRV_CROSS_ENABLE_CPP ON CACHE INTERNAL "") + set(SPIRV_CROSS_ENABLE_REFLECT OFF CACHE INTERNAL "") set(SPIRV_CROSS_ENABLE_C_API OFF CACHE INTERNAL "") + set(SPIRV_CROSS_ENABLE_UTIL OFF CACHE INTERNAL "") + set(SPIRV_CROSS_SKIP_INSTALL ON CACHE INTERNAL "") add_subdirectory(${vkcv_lib}/SPIRV-Cross) diff --git a/include/vkcv/Buffer.hpp b/include/vkcv/Buffer.hpp index d327067ce409e845bcac5e4c9f56e7de59df89c2..ae935ba9501d4d7776cad7e3ba190a2dd02e5e38 100644 --- a/include/vkcv/Buffer.hpp +++ b/include/vkcv/Buffer.hpp @@ -37,6 +37,11 @@ namespace vkcv { size_t getSize() const { return m_count * sizeof(T); } + + [[nodiscard]] + const vk::Buffer getVulkanHandle() const { + return m_manager->getBuffer(m_handle); + } 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)); diff --git a/include/vkcv/CommandRecordingFunctionTypes.hpp b/include/vkcv/CommandRecordingFunctionTypes.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c236fb2c717afd2a3bafc4b3a22708cdac942ffe --- /dev/null +++ b/include/vkcv/CommandRecordingFunctionTypes.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "vkcv/Event.hpp" +#include <vulkan/vulkan.hpp> + +namespace vkcv { + typedef typename event_function<const vk::CommandBuffer&>::type RecordCommandFunction; + typedef typename event_function<>::type FinishCommandFunction; +} \ No newline at end of file diff --git a/include/vkcv/CommandStreamManager.hpp b/include/vkcv/CommandStreamManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4af2127ccf6271f1076e3dde05304b8f9c556139 --- /dev/null +++ b/include/vkcv/CommandStreamManager.hpp @@ -0,0 +1,55 @@ +#pragma once +#include <vulkan/vulkan.hpp> +#include <vector> +#include "vkcv/Event.hpp" +#include "vkcv/Handles.hpp" +#include "vkcv/CommandRecordingFunctionTypes.hpp" + +namespace vkcv { + + class Core; + + class CommandStreamManager + { + friend class Core; + private: + struct CommandStream { + inline CommandStream(vk::CommandBuffer cmdBuffer, vk::Queue queue, vk::CommandPool cmdPool) + : cmdBuffer(cmdBuffer), cmdPool(cmdPool), queue(queue) {}; + vk::CommandBuffer cmdBuffer; + vk::CommandPool cmdPool; + vk::Queue queue; + std::vector<FinishCommandFunction> callbacks; + }; + + Core* m_core; + std::vector<CommandStream> m_commandStreams; + + CommandStreamManager() noexcept; + + void init(Core* core); + + public: + ~CommandStreamManager() noexcept; + + CommandStreamManager(CommandStreamManager&& other) = delete; + CommandStreamManager(const CommandStreamManager& other) = delete; + + CommandStreamManager& operator=(CommandStreamManager&& other) = delete; + CommandStreamManager& operator=(const CommandStreamManager& other) = delete; + + CommandStreamHandle createCommandStream( + const vk::Queue queue, + vk::CommandPool cmdPool); + + void recordCommandsToStream(const CommandStreamHandle handle, const RecordCommandFunction record); + void addFinishCallbackToStream(const CommandStreamHandle handle, const FinishCommandFunction finish); + void submitCommandStreamSynchronous( + const CommandStreamHandle handle, + std::vector<vk::Semaphore> &waitSemaphores, + std::vector<vk::Semaphore> &signalSemaphores); + + vk::CommandBuffer getStreamCommandBuffer(const CommandStreamHandle handle); + }; + +} \ No newline at end of file diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index 0a50765fedb07f1117b5e0ecaa43c03b362c23ce..4a51b24f5c978daebc5116e20b527252c8063d61 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -22,13 +22,11 @@ #include "Sampler.hpp" #include "DescriptorWrites.hpp" #include "Event.hpp" +#include "DrawcallRecording.hpp" +#include "CommandRecordingFunctionTypes.hpp" namespace vkcv { - struct VertexBufferBinding { - vk::DeviceSize offset; - BufferHandle buffer; - }; // forward declarations class PassManager; @@ -37,15 +35,13 @@ namespace vkcv class BufferManager; class SamplerManager; class ImageManager; + class CommandStreamManager; struct SubmitInfo { QueueType queueType; std::vector<vk::Semaphore> waitSemaphores; std::vector<vk::Semaphore> signalSemaphores; }; - - typedef typename event_function<const vk::CommandBuffer&>::type RecordCommandFunction; - typedef typename event_function<>::type FinishCommandFunction; class Core final { @@ -65,27 +61,30 @@ namespace vkcv Context m_Context; - 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<SamplerManager> m_SamplerManager; - std::unique_ptr<ImageManager> m_ImageManager; + SwapChain m_swapchain; + std::vector<vk::ImageView> m_swapchainImageViews; + std::vector<vk::Image> m_swapchainImages; + std::vector<vk::ImageLayout> m_swapchainImageLayouts; + const Window& m_window; - CommandResources m_CommandResources; - SyncResources m_SyncResources; - uint32_t m_currentSwapchainImageIndex; + 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; + std::unique_ptr<CommandStreamManager> m_CommandStreamManager; - ImageHandle m_DepthImage; + CommandResources m_CommandResources; + SyncResources m_SyncResources; + uint32_t m_currentSwapchainImageIndex; std::function<void(int, int)> e_resizeHandle; static std::vector<vk::ImageView> createImageViews( Context &context, SwapChain& swapChain); + void recordSwapchainImageLayoutTransition(vk::CommandBuffer cmdBuffer, vk::ImageLayout newLayout); + public: /** * Destructor of #Core destroys the Vulkan objects contained in the core's context. @@ -158,6 +157,19 @@ namespace vkcv [[nodiscard]] PipelineHandle createGraphicsPipeline(const PipelineConfig &config); + /** + * Creates a basic vulkan compute pipeline using @p shader program and returns it using the @p handle. + * Fixed Functions for pipeline are set with standard values. + * + * @param shader program that hold the compiles compute shader + * @param handle a handle to return the created vulkan handle + * @return True if pipeline creation was successful, False if not + */ + [[nodiscard]] + PipelineHandle createComputePipeline( + const ShaderProgram &config, + const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts); + /** * Creates a basic vulkan render pass using @p config from the render pass config class and returns it using the @p handle. * Fixed Functions for pipeline are set with standard values. @@ -211,29 +223,30 @@ namespace vkcv * @return */ [[nodiscard]] - ResourcesHandle createResourceDescription(const std::vector<DescriptorSetConfig> &descriptorSets); - void writeResourceDescription(ResourcesHandle handle, size_t setIndex, const DescriptorWrites& writes); + DescriptorSetHandle createDescriptorSet(const std::vector<DescriptorBinding> &bindings); + void writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites& writes); - vk::DescriptorSetLayout getDescriptorSetLayout(ResourcesHandle handle, size_t setIndex); + DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const; /** * @brief start recording command buffers and increment frame index */ - void beginFrame(); - - /** - * @brief render a beautiful triangle - */ - void renderMesh( - const PassHandle &renderpassHandle, - const PipelineHandle &pipelineHandle, - 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); + bool beginFrame(uint32_t& width, uint32_t& height); + + void recordDrawcallsToCmdStream( + const CommandStreamHandle cmdStreamHandle, + const PassHandle renderpassHandle, + const PipelineHandle pipelineHandle, + const PushConstantData &pushConstantData, + const std::vector<DrawcallInfo> &drawcalls, + const std::vector<ImageHandle> &renderTargets); + + void recordComputeDispatchToCmdStream( + CommandStreamHandle cmdStream, + PipelineHandle computePipeline, + const uint32_t dispatchCount[3], + const std::vector<DescriptorSetUsage> &descriptorSetUsages, + const PushConstantData& pushConstantData); /** * @brief end recording and present image @@ -251,6 +264,20 @@ namespace vkcv * @param record Record-command-function * @param finish Finish-command-function or nullptr */ - void submitCommands(const SubmitInfo &submitInfo, const RecordCommandFunction& record, const FinishCommandFunction& finish); + void recordAndSubmitCommands( + const SubmitInfo &submitInfo, + const RecordCommandFunction &record, + const FinishCommandFunction &finish); + + CommandStreamHandle createCommandStream(QueueType queueType); + + void recordCommandsToStream( + const CommandStreamHandle cmdStreamHandle, + const RecordCommandFunction &record, + const FinishCommandFunction &finish); + + void submitCommandStream(const CommandStreamHandle handle); + void prepareSwapchainImageForPresent(const CommandStreamHandle handle); + void prepareImageForSampling(const CommandStreamHandle cmdStream, const ImageHandle image); }; } diff --git a/include/vkcv/DescriptorConfig.hpp b/include/vkcv/DescriptorConfig.hpp index 161273db1ec3bd0be290ecdd1042d12d181d303e..c6d0dfd1bc60988afb8b6a9326a8d50d8a4ea32e 100644 --- a/include/vkcv/DescriptorConfig.hpp +++ b/include/vkcv/DescriptorConfig.hpp @@ -1,8 +1,18 @@ #pragma once -#include <vkcv/ShaderProgram.hpp> + +#include <vulkan/vulkan.hpp> + +#include "vkcv/Handles.hpp" +#include "vkcv/ShaderStage.hpp" namespace vkcv { + struct DescriptorSet + { + vk::DescriptorSet vulkanHandle; + vk::DescriptorSetLayout layout; + }; + /* * All the types of descriptors (resources) that can be retrieved by the shaders */ @@ -24,27 +34,16 @@ namespace vkcv */ struct DescriptorBinding { - DescriptorBinding() = delete; DescriptorBinding( + uint32_t bindingID, DescriptorType descriptorType, uint32_t descriptorCount, ShaderStage shaderStage ) noexcept; - + + uint32_t bindingID; DescriptorType descriptorType; uint32_t descriptorCount; ShaderStage shaderStage; }; - - /* - * One descriptor set struct that contains all the necessary information for the actual creation. - * @param[in] a number of bindings that were created beforehand - * @param[in] the number of (identical) sets that should be created from the attached bindings - */ - struct DescriptorSetConfig - { - explicit DescriptorSetConfig(std::vector<DescriptorBinding> bindings) noexcept; - - std::vector<DescriptorBinding> bindings; - }; } diff --git a/include/vkcv/DrawcallRecording.hpp b/include/vkcv/DrawcallRecording.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0929ad038fb95ec1573e7c76e5ce13adb84ab760 --- /dev/null +++ b/include/vkcv/DrawcallRecording.hpp @@ -0,0 +1,54 @@ +#pragma once +#include <vulkan/vulkan.hpp> +#include <vkcv/Handles.hpp> +#include <vkcv/DescriptorConfig.hpp> + +namespace vkcv { + struct VertexBufferBinding { + inline VertexBufferBinding(vk::DeviceSize offset, vk::Buffer buffer) noexcept + : offset(offset), buffer(buffer) {} + + vk::DeviceSize offset; + vk::Buffer buffer; + }; + + struct DescriptorSetUsage { + inline DescriptorSetUsage(uint32_t setLocation, vk::DescriptorSet vulkanHandle) noexcept + : setLocation(setLocation), vulkanHandle(vulkanHandle) {} + + const uint32_t setLocation; + const vk::DescriptorSet vulkanHandle; + }; + + struct Mesh { + inline Mesh(std::vector<VertexBufferBinding> vertexBufferBindings, vk::Buffer indexBuffer, size_t indexCount) noexcept + : vertexBufferBindings(vertexBufferBindings), indexBuffer(indexBuffer), indexCount(indexCount){} + + std::vector<VertexBufferBinding> vertexBufferBindings; + vk::Buffer indexBuffer; + size_t indexCount; + }; + + struct PushConstantData { + inline PushConstantData(void* data, size_t sizePerDrawcall) : data(data), sizePerDrawcall(sizePerDrawcall) {} + + void* data; + size_t sizePerDrawcall; + }; + + struct DrawcallInfo { + inline DrawcallInfo(const Mesh& mesh, const std::vector<DescriptorSetUsage>& descriptorSets) + : mesh(mesh), descriptorSets(descriptorSets) {} + + Mesh mesh; + std::vector<DescriptorSetUsage> descriptorSets; + }; + + void recordDrawcall( + const DrawcallInfo &drawcall, + vk::CommandBuffer cmdBuffer, + vk::PipelineLayout pipelineLayout, + const PushConstantData &pushConstantData, + const size_t drawcallIndex); + +} \ No newline at end of file diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp index bb0438d3e2482bb119c633a4e987cf28dac2375f..ea05bd212dd9314957e4771070bedb3963b1b2dc 100644 --- a/include/vkcv/Handles.hpp +++ b/include/vkcv/Handles.hpp @@ -79,7 +79,7 @@ namespace vkcv using Handle::Handle; }; - class ResourcesHandle : public Handle { + class DescriptorSetHandle : public Handle { friend class DescriptorManager; private: using Handle::Handle; @@ -93,14 +93,19 @@ namespace vkcv class ImageHandle : public Handle { friend class ImageManager; - private: using Handle::Handle; - + public: [[nodiscard]] bool isSwapchainImage() const; static ImageHandle createSwapchainImageHandle(const HandleDestroyFunction& destroy = nullptr); }; + + class CommandStreamHandle : public Handle { + friend class CommandStreamManager; + private: + using Handle::Handle; + }; } diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp index d76bd12169f622f05f1731b0eb3065d6133a5f2f..a1219ce4147ebbb8ae0650da8a87766f8967874b 100644 --- a/include/vkcv/Image.hpp +++ b/include/vkcv/Image.hpp @@ -9,8 +9,12 @@ #include "Handles.hpp" namespace vkcv { - - class ImageManager; + + // forward declares + class ImageManager; + + bool isDepthFormat(const vk::Format format); + class Image { friend class Core; public: @@ -37,11 +41,9 @@ namespace vkcv { void fill(void* data, size_t size = SIZE_MAX); private: ImageManager* const m_manager; - const ImageHandle m_handle; - const vk::Format m_format; - vk::ImageLayout m_layout; + const ImageHandle m_handle; - Image(ImageManager* manager, const ImageHandle& handle, vk::Format format); + Image(ImageManager* manager, const ImageHandle& handle); static Image create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth); diff --git a/include/vkcv/Logger.hpp b/include/vkcv/Logger.hpp new file mode 100644 index 0000000000000000000000000000000000000000..251b6b528c45ea509dbfcd0cfb7135b77031f1ac --- /dev/null +++ b/include/vkcv/Logger.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include <iostream> + +namespace vkcv { + + enum class LogLevel { + INFO, + WARNING, + ERROR + }; + + constexpr auto getLogOutput(LogLevel level) { + switch (level) { + case LogLevel::INFO: + return stdout; + default: + return stderr; + } + } + + constexpr const char* getLogName(LogLevel level) { + switch (level) { + case LogLevel::INFO: + return "INFO"; + case LogLevel::WARNING: + return "WARNING"; + case LogLevel::ERROR: + return "ERROR"; + default: + return "UNKNOWN"; + } + } + +#ifndef NDEBUG +#ifndef VKCV_DEBUG_MESSAGE_LEN +#define VKCV_DEBUG_MESSAGE_LEN 1024 +#endif + +#ifdef _MSC_VER +#define __PRETTY_FUNCTION__ __FUNCSIG__ +#endif + +#define vkcv_log(level, ...) { \ + char output_message [ \ + VKCV_DEBUG_MESSAGE_LEN \ + ]; \ + std::snprintf( \ + output_message, \ + VKCV_DEBUG_MESSAGE_LEN, \ + __VA_ARGS__ \ + ); \ + std::fprintf( \ + getLogOutput(level), \ + "[%s]: %s [%s, line %d: %s]\n", \ + vkcv::getLogName(level), \ + output_message, \ + __FILE__, \ + __LINE__, \ + __PRETTY_FUNCTION__ \ + ); \ +} + +#else +#define vkcv_log(level, ...) {} +#endif + +} diff --git a/include/vkcv/PassConfig.hpp b/include/vkcv/PassConfig.hpp index d9a5bcd83acca5f5ba86b4e6ce6973acbed89de6..8f3b516d4b4451c513366fbd8469908bccde6a5f 100644 --- a/include/vkcv/PassConfig.hpp +++ b/include/vkcv/PassConfig.hpp @@ -32,23 +32,15 @@ namespace vkcv struct AttachmentDescription { - AttachmentDescription() = delete; AttachmentDescription( - AttachmentLayout initial, - AttachmentLayout in_pass, - AttachmentLayout final, - AttachmentOperation store_op, - AttachmentOperation load_op, - vk::Format format) noexcept; - - AttachmentLayout layout_initial; - AttachmentLayout layout_in_pass; - AttachmentLayout layout_final; + AttachmentOperation store_op, + AttachmentOperation load_op, + vk::Format format) noexcept; AttachmentOperation store_operation; AttachmentOperation load_operation; - vk::Format format; + vk::Format format; }; struct PassConfig diff --git a/include/vkcv/PipelineConfig.hpp b/include/vkcv/PipelineConfig.hpp index a8e1334f3ee5d6774b9fed35edd685f19747814d..729330fcaf7eeac2acfdd1816b86ac29c7d9e30b 100644 --- a/include/vkcv/PipelineConfig.hpp +++ b/include/vkcv/PipelineConfig.hpp @@ -7,9 +7,9 @@ #include <vector> #include <cstdint> -#include "vkcv/Handles.hpp" +#include "Handles.hpp" #include "ShaderProgram.hpp" -#include <vkcv/VertexLayout.hpp> +#include "VertexLayout.hpp" namespace vkcv { @@ -21,22 +21,26 @@ namespace vkcv { * @param shaderProgram shaders of the pipeline * @param height height of the application window * @param width width of the application window - * @param passHandle handle for Render Pass + * @param passHandle handle for render pass + * @param vertexLayout layout of vertex buffer, comprised of its bindings and the bindings' attachments */ PipelineConfig( - const ShaderProgram& shaderProgram, - uint32_t width, - uint32_t height, - PassHandle &passHandle, - const std::vector<VertexAttribute> &vertexAttributes, - const std::vector<vk::DescriptorSetLayout> &descriptorLayouts); + const ShaderProgram& shaderProgram, + uint32_t width, + uint32_t height, + const PassHandle &passHandle, + const VertexLayout &vertexLayouts, + const std::vector<vk::DescriptorSetLayout> &descriptorLayouts, + bool useDynamicViewport); + + ShaderProgram m_ShaderProgram; + uint32_t m_Height; + uint32_t m_Width; + PassHandle m_PassHandle; + VertexLayout m_VertexLayout; + std::vector<vk::DescriptorSetLayout> m_DescriptorLayouts; + bool m_UseDynamicViewport; - 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; }; } \ No newline at end of file diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp index ef5d1f00ea3eeb97d97d8824439ded1ed326f33c..28f746d78477062eae9b0ad88f8c5de71e11efd0 100644 --- a/include/vkcv/ShaderProgram.hpp +++ b/include/vkcv/ShaderProgram.hpp @@ -8,23 +8,16 @@ #include <unordered_map> #include <fstream> #include <iostream> +#include <algorithm> #include <filesystem> #include <vulkan/vulkan.hpp> #include <spirv_cross.hpp> -#include "vkcv/VertexLayout.hpp" +#include "VertexLayout.hpp" +#include "ShaderStage.hpp" +#include "DescriptorConfig.hpp" namespace vkcv { - enum class ShaderStage - { - VERTEX, - TESS_CONTROL, - TESS_EVAL, - GEOMETRY, - FRAGMENT, - COMPUTE - }; - struct Shader { std::vector<char> shaderCode; @@ -55,13 +48,24 @@ namespace vkcv { bool existsShader(ShaderStage shaderStage) const; - void reflectShader(ShaderStage shaderStage); + const std::vector<VertexAttachment> &getVertexAttachments() const; + size_t getPushConstantSize() const; - const VertexLayout &getVertexLayout() const; + const std::vector<std::vector<DescriptorBinding>> &getReflectedDescriptors() const; private: + /** + * Called after successfully adding a shader to the program. + * Fills vertex input attachments and descriptor sets (if present). + * @param shaderStage the stage to reflect data from + */ + void reflectShader(ShaderStage shaderStage); + std::unordered_map<ShaderStage, Shader> m_Shaders; - VertexLayout m_VertexLayout; + // contains all vertex input attachments used in the vertex buffer + std::vector<VertexAttachment> m_VertexAttachments; + std::vector<std::vector<DescriptorBinding>> m_DescriptorSets; + size_t m_pushConstantSize = 0; }; } diff --git a/include/vkcv/ShaderStage.hpp b/include/vkcv/ShaderStage.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dca395bdba82a2f1cb38bb0a25196cfd3dab8019 --- /dev/null +++ b/include/vkcv/ShaderStage.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace vkcv { + + enum class ShaderStage + { + VERTEX, + TESS_CONTROL, + TESS_EVAL, + GEOMETRY, + FRAGMENT, + COMPUTE + }; + +} diff --git a/include/vkcv/SwapChain.hpp b/include/vkcv/SwapChain.hpp index 53f7b783051307c7d327f20f389d7bef0fb9a16c..089205d1633551b4ad9f11d0bdd5540b2bb61bbb 100644 --- a/include/vkcv/SwapChain.hpp +++ b/include/vkcv/SwapChain.hpp @@ -107,7 +107,7 @@ namespace vkcv /** * */ - void recreateSwapchain(); + void signalSwapchainRecreation(); /** * TODO diff --git a/include/vkcv/VertexLayout.hpp b/include/vkcv/VertexLayout.hpp index ee0ad8ef56d5284af2be4c81b7ea2f0d052d5a6f..9f609b48472386cd7628ff40b5fa4b90bc91649a 100644 --- a/include/vkcv/VertexLayout.hpp +++ b/include/vkcv/VertexLayout.hpp @@ -1,29 +1,11 @@ #pragma once -#include <unordered_map> #include <vector> #include <iostream> +#include <string> 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{ + enum class VertexAttachmentFormat{ FLOAT, FLOAT2, FLOAT3, @@ -34,22 +16,51 @@ namespace vkcv{ INT4 }; - uint32_t getFormatSize(VertexFormat format); + uint32_t getFormatSize(VertexAttachmentFormat format); - struct VertexInputAttachment{ - VertexInputAttachment() = delete; - VertexInputAttachment(uint32_t location, uint32_t binding, VertexFormat format, uint32_t offset) noexcept; + struct VertexAttachment{ + friend struct VertexBinding; + /** + * Describes an individual vertex input attribute/attachment. + * @param inputLocation its location in the vertex shader. + * @param name the name referred to in the shader. + * @param format the format (and therefore, the size) this attachment is in. + * The offset is calculated when a collection of attachments forms a binding, hence the friend declaration. + */ + VertexAttachment(uint32_t inputLocation, const std::string &name, VertexAttachmentFormat format) noexcept; + VertexAttachment() = delete; - uint32_t location; - uint32_t binding; - VertexFormat format; - uint32_t offset; + uint32_t inputLocation; + std::string name; + VertexAttachmentFormat format; + uint32_t offset; + }; + + struct VertexBinding{ + /** + * Describes all vertex input attachments _one_ buffer contains to create a vertex buffer binding. + * NOTE: multiple vertex layouts may contain various (mutually exclusive) vertex input attachments + * to form one complete vertex buffer binding! + * @param bindingLocation its entry in the buffers that make up the whole vertex buffer. + * @param attachments the vertex input attachments this specific buffer layout contains. + */ + VertexBinding(uint32_t bindingLocation, const std::vector<VertexAttachment> &attachments) noexcept; + VertexBinding() = delete; + + uint32_t bindingLocation; + uint32_t stride; + std::vector<VertexAttachment> vertexAttachments; }; struct VertexLayout{ + /** + * Describes the complete layout of one vertex, e.g. all of the vertex input attachments used, + * and all of the buffer bindings that refer to the attachments (for when multiple buffers are used). + * @param bindings bindings the complete vertex buffer is comprised of. + */ VertexLayout() noexcept; - VertexLayout(const std::vector<VertexInputAttachment> &inputs) noexcept; - std::unordered_map<uint32_t, VertexInputAttachment> attachmentMap; - uint32_t stride; + VertexLayout(const std::vector<VertexBinding> &bindings) noexcept; + + std::vector<VertexBinding> vertexBindings; }; } \ No newline at end of file diff --git a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp index 24687e846ff9eae3275de357331a825f0b4ed2c3..d687bbf60a03a59eee8c29f9b676f10cc7f2f2b3 100644 --- a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp +++ b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp @@ -8,7 +8,6 @@ #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 @@ -53,6 +52,23 @@ enum PrimitiveMode { /* The indices in the index buffer can be of different bit width. */ enum IndexType { UINT32=0, UINT16=1, UINT8=2 }; +/* 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; + typedef struct { // TODO not yet needed for the first (unlit) triangle } Material; @@ -71,7 +87,7 @@ typedef struct { } indexBuffer; struct { std::vector<uint8_t> data; // binary data of the vertex buffer - std::vector<VertexAttribute> attributes; + std::vector<VertexAttribute> attributes; // description of one } vertexBuffer; struct { float x, y, z; } min; // bounding box lower left struct { float x, y, z; } max; // bounding box upper right diff --git a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp index e660b442d0b9a0208f95c9d753ef19e926bcac44..f3823cc8f3fe54b53835f356dd14a086515118dd 100644 --- a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp +++ b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp @@ -7,6 +7,8 @@ #define STBI_ONLY_JPEG #include <stb_image.h> +#include <vkcv/Logger.hpp> + namespace vkcv::asset { /** @@ -39,11 +41,12 @@ uint8_t convertTypeToInt(const fx::gltf::Accessor::Type type) { * @param path path to file that is responsible for error */ void print_what (const std::exception& e, const std::string &path) { - fprintf(stderr, "ERROR loading file %s: %s\n", path.c_str(), e.what()); + vkcv_log(LogLevel::ERROR, "Loading file %s: %s", + path.c_str(), e.what()); + try { std::rethrow_if_nested(e); } catch (const std::exception& nested) { - std::cerr << "nested: "; print_what(nested, path); } } @@ -121,7 +124,7 @@ int loadMesh(const std::string &path, Mesh &mesh) { const size_t off = indexBufferView.byteOffset; const void *const ptr = ((char*)indexBuffer.data.data()) + off; if (!memcpy(indexBufferData.data(), ptr, indexBufferView.byteLength)) { - std::cerr << "ERROR copying index buffer data.\n"; + vkcv_log(LogLevel::ERROR, "Copying index buffer data"); return 0; } } @@ -136,7 +139,7 @@ int loadMesh(const std::string &path, Mesh &mesh) { const size_t off = 0; const void *const ptr = ((char*)vertexBuffer.data.data()) + off; if (!memcpy(vertexBufferData.data(), ptr, vertexBuffer.byteLength)) { - std::cerr << "ERROR copying vertex buffer data.\n"; + vkcv_log(LogLevel::ERROR, "Copying vertex buffer data"); return 0; } } @@ -150,9 +153,8 @@ int loadMesh(const std::string &path, Mesh &mesh) { case fx::gltf::Accessor::ComponentType::UnsignedInt: indexType = UINT32; break; default: - std::cerr << "ERROR: Index type not supported: " << - static_cast<uint16_t>(indexAccessor.componentType) << - std::endl; + vkcv_log(LogLevel::ERROR, "Index type (%u) not supported", + static_cast<uint16_t>(indexAccessor.componentType)); return 0; } diff --git a/modules/camera/CMakeLists.txt b/modules/camera/CMakeLists.txt index 28080bf2b1cd3bbc88d6c13d7ef26a43d7c3e19a..73f2dd1c81be9c6cadf563f7936bfaba8c1d0025 100644 --- a/modules/camera/CMakeLists.txt +++ b/modules/camera/CMakeLists.txt @@ -11,10 +11,13 @@ set(vkcv_camera_include ${PROJECT_SOURCE_DIR}/include) set(vkcv_camera_sources ${vkcv_camera_include}/vkcv/camera/Camera.hpp ${vkcv_camera_source}/vkcv/camera/Camera.cpp - ${vkcv_camera_include}/vkcv/camera/TrackballCamera.hpp - ${vkcv_camera_source}/vkcv/camera/TrackballCamera.cpp ${vkcv_camera_include}/vkcv/camera/CameraManager.hpp ${vkcv_camera_source}/vkcv/camera/CameraManager.cpp + ${vkcv_camera_include}/vkcv/camera/CameraController.hpp + ${vkcv_camera_include}/vkcv/camera/PilotCameraController.hpp + ${vkcv_camera_source}/vkcv/camera/PilotCameraController.cpp + ${vkcv_camera_include}/vkcv/camera/TrackballCameraController.hpp + ${vkcv_camera_source}/vkcv/camera/TrackballCameraController.cpp ) # adding source files to the project diff --git a/modules/camera/include/vkcv/camera/Camera.hpp b/modules/camera/include/vkcv/camera/Camera.hpp index ff8fda811eaad7a99dbb940601f9e5904025255e..dc9f2dcb3038655f51fb2404abc21f98a2120399 100644 --- a/modules/camera/include/vkcv/camera/Camera.hpp +++ b/modules/camera/include/vkcv/camera/Camera.hpp @@ -1,101 +1,199 @@ #pragma once +#define GLM_DEPTH_ZERO_TO_ONE +#define GLM_FORCE_LEFT_HANDED #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_access.hpp> -namespace vkcv { +namespace vkcv::camera { - class Camera { + /** + * @brief Used to create a camera which governs the view and projection matrices. + */ + class Camera final { protected: glm::mat4 m_view; glm::mat4 m_projection; - int m_width; - int m_height; - float m_near; float m_far; - float m_fov; - float m_ratio; - glm::vec3 m_up; + glm::vec3 m_up; glm::vec3 m_position; - float m_cameraSpeed; + glm::vec3 m_center; + float m_pitch; float m_yaw; - - int m_fov_nsteps; - float m_fov_min; - float m_fov_max; - - bool m_forward; - bool m_backward; - bool m_left; - bool m_right; + + /** + * @brief Sets the view matrix of the camera to @p view + * @param[in] view The view matrix + */ + void setView(const glm::mat4& view); + + /** + * @brief Sets the projection matrix of the camera to @p projection + * @param[in] projection The projection matrix + */ + void setProjection(const glm::mat4& projection); public: - Camera(); - virtual ~Camera(); + /** + * @brief The default constructor of the camera + */ + Camera(); + /** + * @brief The destructor of the camera (default behavior) + */ + ~Camera(); + + /** + * @brief Sets the perspective object according to @p fov, @p ratio, @p near and @p far. This leads to changes in the projection matrix of the camera + * @param[in] fov The desired field of view in radians + * @param[in] ratio The aspect ratio + * @param[in] near Distance to near clipping plane + * @param[in] far Distance to far clipping plane + */ void setPerspective(float fov, float ratio, float near, float far); - const glm::mat4 getView() const; - - void getView(glm::vec3 &x, glm::vec3 &y, glm::vec3 &z, glm::vec3 &pos); - - glm::mat4 updateView(double deltatime); - - void lookAt(glm::vec3 position, glm::vec3 center, glm::vec3 up); - + /** + * @brief Gets the view matrix of the camera + * @return The view matrix of the camera + */ + const glm::mat4& getView() const; + + /** + * @brief Sets the view matrix of the camera according to @p position, @p center and @p up + * @param[in] position The position of the camera + * @param[in] center The target position the camera is looking at + * @param[in] up The vector that defines which direction is 'up' depending on the camera's orientation + */ + void lookAt(const glm::vec3& position, const glm::vec3& center, const glm::vec3& up); + + /** + * @brief Gets the current projection of the camera + * @return The current projection matrix + */ const glm::mat4& getProjection() const; - void setProjection(const glm::mat4 projection); - + /** + * @brief Gets the model-view-projection matrix of the camera with y-axis-correction applied + * @return The model-view-projection matrix + */ + glm::mat4 getMVP() const; + + /** + * @brief Gets the near and far bounds of the view frustum of the camera. + * @param[out] near The near bound of the view frustum + * @param[out] far The far bound of the view frustum + */ void getNearFar(float &near, float &far) const; - void setUp(const glm::vec3 &Up); - + /** + * @brief Gets the current field of view of the camera in radians + * @return[in] The current field of view in radians + */ float getFov() const; + /** + * @brief Sets the field of view of the camera to @p fov in radians + * @param[in] fov The new field of view in radians + */ void setFov(float fov); - - void changeFov(double fov); - - void updateRatio(int width, int height); + /** + * @brief Gets the current aspect ratio of the camera + * @return The current aspect ratio of the camera + */ float getRatio() const; + /** + * @brief Updates the aspect ratio of the camera with @p ratio and, thus, changes the projection matrix + * @param[in] ratio The new aspect ratio of the camera + */ + void setRatio(float ratio); + + /** + * @brief Sets @p near and @p far as new values for the view frustum of the camera. This leads to changes in the projection matrix according to these two values. + * @param[in] near The new near bound of the view frustum + * @param[in] far The new far bound of the view frustum + */ void setNearFar(float near, float far); + /** + * @brief Gets the current front vector of the camera in world space + * @return The current front vector of the camera + */ glm::vec3 getFront() const; - - glm::vec3 getPosition() const; - - void setPosition( glm::vec3 position ); - + + /** + * @brief Sets the front vector of the camera in world space to @p front + * @param[in] front The new front vector of the camera + */ + void setFront(const glm::vec3& front); + + /** + * @brief Gets the current position of the camera in world space + * @return The current position of the camera in world space + */ + const glm::vec3& getPosition() const; + + /** + * @brief Sets the position of the camera to @p position + * @param[in] position The new position of the camera + */ + void setPosition( const glm::vec3& position ); + + /** + * @brief Gets the center point. + * @return The center point. + */ + const glm::vec3& getCenter() const; + + /** + * @brief Sets @p center as the new center point. + * @param[in] center The new center point. + */ + void setCenter(const glm::vec3& center); + + /** + * @brief Gets the pitch value of the camera in degrees. + * @return The pitch value in degrees. + */ float getPitch() const; + /** + * @brief Sets the pitch value of the camera to @p pitch in degrees. + * @param[in] pitch The new pitch value in degrees. + */ void setPitch(float pitch); + /** + * @brief Gets the yaw value of the camera in degrees. + * @return The yaw value in degrees. + */ float getYaw() const; + /** + * @brief Sets the yaw value of the camera to @p yaw. + * @param[in] yaw The new yaw value in degrees. + */ void setYaw(float yaw); - void panView( double xOffset, double yOffset ); - - void updatePosition(double deltatime); - - void moveForward(int action); - - void moveBackward(int action); - - void moveLeft(int action); - - void moveRight(int action); - - + /** + * @brief Gets the up vector. + * @return The up vector. + */ + const glm::vec3& getUp() const; + + /** + * @brief Sets @p up as the new up vector. + * @param[in] up The new up vector. + */ + void setUp(const glm::vec3 &up); }; } diff --git a/modules/camera/include/vkcv/camera/CameraController.hpp b/modules/camera/include/vkcv/camera/CameraController.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5fe7aba586068beff15525617d8e4817662746b7 --- /dev/null +++ b/modules/camera/include/vkcv/camera/CameraController.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "Camera.hpp" +#include "vkcv/Window.hpp" + +namespace vkcv::camera { + + /** + * @brief Used as a base class for defining camera controller classes with different behaviors, e.g. the + * #PilotCameraController. + */ + class CameraController { + + public: + + /** + * @brief The constructor of the #CameraController (default behavior). + */ + CameraController() = default; + + /** + * @brief Updates @p camera in respect to @p deltaTime. + * @param[in] deltaTime The time that has passed since last update. + * @param[in] camera The camera object. + */ + virtual void updateCamera(double deltaTime, Camera &camera) = 0; + + /** + * @brief A callback function for key events. + * @param[in] key The keyboard key. + * @param[in] scancode The platform-specific scancode. + * @param[in] action The key action. + * @param[in] mods The modifier bits. + * @param[in] camera The camera object. + */ + virtual void keyCallback(int key, int scancode, int action, int mods, Camera &camera) = 0; + + /** + * @brief A callback function for mouse scrolling events. + * @param[in] offsetX The offset in horizontal direction. + * @param[in] offsetY The offset in vertical direction. + * @param[in] camera The camera object. + */ + virtual void scrollCallback( double offsetX, double offsetY, Camera &camera) = 0; + + /** + * @brief A callback function for mouse movement events. + * @param[in] x The horizontal mouse position. + * @param[in] y The vertical mouse position. + * @param[in] camera The camera object. + */ + virtual void mouseMoveCallback(double offsetX, double offsetY, Camera &camera) = 0; + + /** + * @brief A callback function for mouse button events. + * @param[in] button The mouse button. + * @param[in] action The button action. + * @param[in] mods The modifier bits. + * @param[in] camera The camera object. + */ + virtual void mouseButtonCallback(int button, int action, int mods, Camera &camera) = 0; + }; + +} \ No newline at end of file diff --git a/modules/camera/include/vkcv/camera/CameraManager.hpp b/modules/camera/include/vkcv/camera/CameraManager.hpp index 4e52eccea25e8544a9a5cc89d0dc74ddd0e023c6..69c4b311a561b9f3ce27e3d9906d68f21e2334ac 100644 --- a/modules/camera/include/vkcv/camera/CameraManager.hpp +++ b/modules/camera/include/vkcv/camera/CameraManager.hpp @@ -1,40 +1,186 @@ #pragma once -#include "TrackballCamera.hpp" +#include "PilotCameraController.hpp" +#include "TrackballCameraController.hpp" +#include "CameraController.hpp" #include "vkcv/Window.hpp" #include <GLFW/glfw3.h> #include <functional> -namespace vkcv{ +namespace vkcv::camera { + /** + * @brief Used for specifying existing types of camera controllers when adding a new controller object to the + * #CameraManager. + */ + enum class ControllerType { + NONE, + PILOT, + TRACKBALL, + TRACE + }; + + /** + * @brief Used for managing an arbitrary amount of camera controllers. + */ class CameraManager{ private: - std::function<void(int, int, int, int)> e_keyHandle; - std::function<void(double, double)> e_mouseMoveHandle; - std::function<void(double, double)> e_mouseScrollHandle; - std::function<void(int, int, int)> e_mouseButtonHandle; - std::function<void(int, int)> e_resizeHandle; - - Window &m_window; - Camera m_camera; - float m_width; - float m_height; -// std::shared_ptr<vkcv::TrackballCamera> m_trackball; - - bool m_roationActive = false; - double m_lastX ; - double m_lastY ; - - void bindCamera(); + std::function<void(int, int, int, int)> m_keyHandle; + std::function<void(double, double)> m_mouseMoveHandle; + std::function<void(double, double)> m_mouseScrollHandle; + std::function<void(int, int, int)> m_mouseButtonHandle; + std::function<void(int, int)> m_resizeHandle; + + Window& m_window; + std::vector<Camera> m_cameras; + std::vector<ControllerType> m_cameraControllerTypes; + uint32_t m_activeCameraIndex; + + PilotCameraController m_pilotController; + TrackballCameraController m_trackController; + + double m_lastX; + double m_lastY; + + /** + * @brief Binds the camera object to the window event handles. + */ + void bindCameraToEvents(); + + /** + * @brief A callback function for key events. Currently, cycling between all existing camera controllers via Tab, + * window closure via Esc and polling key events from the active camera controller are supported. + * @param[in] key The keyboard key. + * @param[in] scancode The platform-specific scancode. + * @param[in] action The key action. + * @param[in] mods The modifier bits. + */ void keyCallback(int key, int scancode, int action, int mods); - void scrollCallback( double offsetX, double offsetY); - void mouseMoveCallback( double offsetX, double offsetY); + + /** + * @brief A callback function for mouse scrolling events. + * @param[in] offsetX The offset in horizontal direction. + * @param[in] offsetY The offset in vertical direction. + */ + void scrollCallback(double offsetX, double offsetY); + + /** + * @brief A callback function for mouse movement events. + * @param[in] x The horizontal mouse position. + * @param[in] y The vertical mouse position. + */ + void mouseMoveCallback(double x, double y); + + /** + * @brief A callback function for mouse button events. + * @param[in] button The mouse button. + * @param[in] action The button action. + * @param[in] mods The modifier bits. + */ void mouseButtonCallback(int button, int action, int mods); + + /** + * @brief A callback function for handling the window resizing event. Each existing camera is resized in respect + * of the window size. + * @param[in] width The new width of the window. + * @param[in] height The new height of the window. + */ void resizeCallback(int width, int height); + + /** + * @brief Gets a camera controller object of specified @p controllerType. + * @param[in] controllerType The type of the camera controller. + * @return The specified camera controller object. + */ + CameraController& getControllerByType(ControllerType controllerType); + + /** + * @briof A method to get the currently active controller for the active camera. + * @return Reference to the active #CameraController + */ + CameraController& getActiveController(); public: - CameraManager(Window &window, float width, float height, glm::vec3 up = glm::vec3(0.0f,-1.0f,0.0f), glm::vec3 position = glm::vec3(0.0f,0.0f,0.0f)); - Camera &getCamera(); + /** + * @brief The constructor of the #CameraManager. + * @param[in] window The window. + * @param[in] width The width of the window. + * @param[in] height The height of the window. + */ + CameraManager(Window &window, float width, float height); + + /** + * @brief The destructor of the #CameraManager. Destroying the #CameraManager leads to deletion of all stored + * camera objects and camera controller objects. + */ + ~CameraManager(); + + /** + * @brief Adds a new camera object to the #CameraManager and binds it to a camera controller object of specified + * @p controllerType. + * @param[in] controllerType The type of the camera controller. + * @return The index of the newly created camera object. + */ + uint32_t addCamera(ControllerType controllerType = ControllerType::NONE); + + /** + * @brief Adds a new camera object to the #CameraManager and binds it to a camera controller object of specified + * @p controllerType. + * @param[in] controllerType The type of the camera controller. + * @param[in] camera The new camera object. + * @return The index of the newly bound camera object. + */ + uint32_t addCamera(ControllerType controllerType, const Camera& camera); + + /** + * @brief Gets the stored camera object located at @p cameraIndex. + * @param[in] cameraIndex The camera index. + * @return The camera object at @p cameraIndex. + * @throws std::runtime_error If @p cameraIndex is not a valid camera index. + */ + Camera& getCamera(uint32_t cameraIndex); + + /** + * @brief Gets the stored camera object set as the active camera. + * @return The active camera. + */ + Camera& getActiveCamera(); + + /** + * @brief Sets the stored camera object located at @p cameraIndex as the active camera. + * @param[in] cameraIndex The camera index. + * @throws std::runtime_error If @p cameraIndex is not a valid camera index. + */ + void setActiveCamera(uint32_t cameraIndex); + + /** + * @brief Gets the index of the stored active camera object. + * @return The active camera index. + */ + uint32_t getActiveCameraIndex() const; + + /** + * @brief Binds a stored camera object located at @p cameraIndex to a camera controller of specified + * @p controllerType. + * @param[in] cameraIndex The camera index. + * @param[in] controllerType The type of the camera controller. + * @throws std::runtime_error If @p cameraIndex is not a valid camera index. + */ + void setControllerType(uint32_t cameraIndex, ControllerType controllerType); + + /** + * @brief Gets the currently bound camera controller type of the stored camera object located at @p cameraIndex. + * @param[in] cameraIndex The camera index. + * @return The type of the camera controller of the specified camera object. + * @throws std::runtime_error If @p cameraIndex is not a valid camera index. + */ + ControllerType getControllerType(uint32_t cameraIndex); + + /** + * @brief Updates all stored camera controllers in respect to @p deltaTime. + * @param[in] deltaTime The time that has passed since last update. + */ + void update(double deltaTime); }; } diff --git a/modules/camera/include/vkcv/camera/PilotCameraController.hpp b/modules/camera/include/vkcv/camera/PilotCameraController.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c6a9f7c7ffa9a3be77f12c29e456291fb8f6b845 --- /dev/null +++ b/modules/camera/include/vkcv/camera/PilotCameraController.hpp @@ -0,0 +1,138 @@ +#pragma once + +#include <vkcv/camera/CameraController.hpp> + +namespace vkcv::camera { + + /** + * @brief Used to move around a camera object in world space. + */ + class PilotCameraController final : public CameraController { + private: + // camera movement indicators + bool m_forward; + bool m_backward; + bool m_upward; + bool m_downward; + bool m_left; + bool m_right; + + bool m_rotationActive; + + float m_cameraSpeed; + + int m_fov_nsteps; + float m_fov_min; + float m_fov_max; + + /** + * @brief Indicates forward movement of the camera depending on the performed @p action. + * @param[in] action The performed action. + */ + void moveForward(int action); + + /** + * @brief Indicates backward movement of the camera depending on the performed @p action. + * @param[in] action The performed action. + */ + void moveBackward(int action); + + /** + * @brief Indicates left movement of the camera depending on the performed @p action. + * @param[in] action The performed action. + */ + void moveLeft(int action); + + /** + * @brief Indicates right movement of the camera depending on the performed @p action. + * @param[in] action The performed action. + */ + void moveRight(int action); + + /** + * @brief Indicates upward movement of the camera depending on the performed @p action. + * @param[in] action The performed action. + */ + void moveUpward(int action); + + /** + * @brief Indicates downward movement of the camera depending on the performed @p action. + * @param[in] action The performed action. + */ + void moveDownward(int action); + + public: + + /** + * @brief The default constructor of the #PilotCameraController. + */ + PilotCameraController(); + + /** + * @brief The destructor of the #PilotCameraController (default behavior). + */ + ~PilotCameraController() = default; + + /** + * @brief Changes the field of view of @p camera with an @p offset in degrees. + * @param[in] offset The offset in degrees. + * @param[in] camera The camera object. + */ + void changeFov(double offset, Camera &camera); + + /** + * @brief Pans the view of @p camera according to the pitch and yaw values and additional offsets @p xOffset + * and @p yOffset. + * @param[in] xOffset The offset added to the yaw value. + * @param[in] yOffset The offset added to the pitch value. + * @param[in] camera The camera object. + */ + void panView(double xOffset, double yOffset, Camera &camera); + + /** + * @brief Updates @p camera in respect to @p deltaTime. + * @param[in] deltaTime The time that has passed since last update. + * @param[in] camera The camera object. + */ + void updateCamera(double deltaTime, Camera &camera); + + /** + * @brief A callback function for key events. Currently, 3D camera movement via W, A, S, D, E, Q are supported. + * @param[in] key The keyboard key. + * @param[in] scancode The platform-specific scancode. + * @param[in] action The key action. + * @param[in] mods The modifier bits. + * @param[in] camera The camera object. + */ + void keyCallback(int key, int scancode, int action, int mods, Camera &camera); + + /** + * @brief A callback function for mouse scrolling events. Currently, this leads to changes in the field of view + * of @p camera. + * @param[in] offsetX The offset in horizontal direction. + * @param[in] offsetY The offset in vertical direction. + * @param[in] camera The camera object. + */ + void scrollCallback(double offsetX, double offsetY, Camera &camera); + + /** + * @brief A callback function for mouse movement events. Currently, this leads to panning the view of the camera, + * if #mouseButtonCallback(int button, int action, int mods) enabled panning. + * @param[in] x The horizontal mouse position + * @param[in] y The vertical mouse position + * @param[in] camera The camera object. + */ + void mouseMoveCallback(double x, double y, Camera &camera); + + /** + * @brief A callback function for mouse button events. Currently, the right mouse button enables panning the + * view of the camera as long as it is pressed. + * @param[in] button The mouse button + * @param[in] action The button action + * @param[in] mods The modifier bits + * @param[in] camera The camera object. + */ + void mouseButtonCallback(int button, int action, int mods, Camera &camera); + }; + +} \ No newline at end of file diff --git a/modules/camera/include/vkcv/camera/TrackballCamera.hpp b/modules/camera/include/vkcv/camera/TrackballCamera.hpp deleted file mode 100644 index c9e269e9f7ad708c68158d5b358efbf37c5bb7a9..0000000000000000000000000000000000000000 --- a/modules/camera/include/vkcv/camera/TrackballCamera.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "Camera.hpp" - -namespace vkcv { - - class TrackballCamera : public vkcv::Camera { - public: - - TrackballCamera( int width, int height, glm::mat4 projection); - - TrackballCamera(int width, int height); - - ~TrackballCamera(); - - float getSensitivity() const; - - void setSensitivity(float sensitivity); - - float getStepSize() const; - - void setStepSize(float stepSize); - - float getTheta() const; - - void setTheta(float theta); - - float getPhi() const; - - void setPhi(float phi); - - float getRadius() const; - - void setRadius(float radius); - - const glm::vec3& getCenter() const; - - void setCenter(const glm::vec3 ¢er); - - private: - float m_sensitivity; - float m_stepSize, m_theta, m_phi, m_radius; - glm::vec3 m_center; - - float m_oldX; - float m_oldY; - }; - -} \ No newline at end of file diff --git a/modules/camera/include/vkcv/camera/TrackballCameraController.hpp b/modules/camera/include/vkcv/camera/TrackballCameraController.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0211043a9c6b862df8e500af190ad1f75a3c78aa --- /dev/null +++ b/modules/camera/include/vkcv/camera/TrackballCameraController.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include "CameraController.hpp" + +namespace vkcv::camera { + + /** + * @brief Used to orbit a camera around its center point. + */ + class TrackballCameraController final : public CameraController { + private: + bool m_rotationActive; + + float m_cameraSpeed; + float m_scrollSensitivity; + float m_radius; + + /** + * @brief Updates the current radius of @p camera in respect to the @p offset. + * @param[in] offset The offset between the old and new radius. + * @param[in] camera The camera object. + */ + void updateRadius(double offset, Camera &camera); + + public: + + /** + * @brief The default constructor of the #TrackballCameraController. + */ + TrackballCameraController(); + + /** + * @brief The destructor of the #TrackballCameraController (default behavior). + */ + ~TrackballCameraController() = default; + + /** + * @brief Sets @p radius as the new radius for orbiting around the camera's center point. + * @param[in] radius The new radius. + */ + void setRadius(const float radius); + + /** + * @brief Pans the view of @p camera according to the pitch and yaw values and additional offsets @p xOffset + * and @p yOffset. + * @param[in] xOffset The offset added to the yaw value. + * @param[in] yOffset The offset added to the pitch value. + * @param[in] camera The camera object. + */ + void panView(double xOffset, double yOffset, Camera &camera); + + /** + * @brief Updates @p camera in respect to @p deltaTime. + * @param[in] deltaTime The time that has passed since last update. + * @param[in] camera The camera object + */ + void updateCamera(double deltaTime, Camera &camera); + + /** + * @brief A callback function for key events. Currently, the trackball camera does not support camera movement. + * It can only orbit around its center point. + * @param[in] key The keyboard key. + * @param[in] scancode The platform-specific scancode. + * @param[in] action The key action. + * @param[in] mods The modifier bits. + * @param[in] camera The camera object. + */ + void keyCallback(int key, int scancode, int action, int mods, Camera &camera); + + /** + * @brief A callback function for mouse scrolling events. Currently, this leads to changes in the field of view + * of the camera object. + * @param[in] offsetX The offset in horizontal direction. + * @param[in] offsetY The offset in vertical direction. + * @param[in] camera The camera object. + */ + void scrollCallback(double offsetX, double offsetY, Camera &camera); + + /** + * @brief A callback function for mouse movement events. Currently, this leads to panning the view of the + * camera, if #mouseButtonCallback(int button, int action, int mods) enabled panning. + * @param[in] xoffset The horizontal mouse position. + * @param[in] yoffset The vertical mouse position. + * @param[in] camera The camera object. + */ + void mouseMoveCallback(double xoffset, double yoffset, Camera &camera); + + /** + * @brief A callback function for mouse button events. Currently, the right mouse button enables panning the + * view of the camera as long as it is pressed. + * @param[in] button The mouse button. + * @param[in] action The button action. + * @param[in] mods The modifier bits. + * @param[in] camera The camera object. + */ + void mouseButtonCallback(int button, int action, int mods, Camera &camera); + + }; + +} \ No newline at end of file diff --git a/modules/camera/src/vkcv/camera/Camera.cpp b/modules/camera/src/vkcv/camera/Camera.cpp index b1d7381e3d548c9edf5d41e8d084c7edb1d02647..eb1857968b2284287691c6ed41ba168db30d3f84 100644 --- a/modules/camera/src/vkcv/camera/Camera.cpp +++ b/modules/camera/src/vkcv/camera/Camera.cpp @@ -1,44 +1,28 @@ #include "vkcv/camera/Camera.hpp" -#include <iostream> -namespace vkcv { +#define _USE_MATH_DEFINES +#include <math.h> - Camera::Camera(){ - m_up = glm::vec3(0.0f, -1.0f, 0.0f); - m_position = glm::vec3(0.0f, 0.0f, 0.0f); - m_cameraSpeed = 2.f; - // front - m_pitch = 0.0; - m_yaw = 180.0; +namespace vkcv::camera { - m_fov_nsteps = 100; - m_fov_min = 10; - m_fov_max = 120; - - m_forward = false; - m_backward = false; - m_left = false; - m_right = false; + Camera::Camera() { + lookAt( + glm::vec3(0.0f, 0.0f, -1.0f), + glm::vec3(0.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 1.0f, 0.0f) + ); + + setFront(glm::normalize(m_center - m_position)); } Camera::~Camera() = default; - void Camera::lookAt(glm::vec3 position, glm::vec3 center, glm::vec3 up){ - m_view = glm::lookAt(position, center, up); - } - - glm::mat4 Camera::updateView(double deltatime){ - updatePosition(deltatime); - return m_view = glm::lookAt(m_position, m_position + getFront() , m_up); - } - - void Camera::getView(glm::vec3 &x, glm::vec3 &y, glm::vec3 &z, glm::vec3 &pos){ - x = glm::vec3( glm::row(m_view, 0)); - y = glm::vec3( glm::row(m_view, 1)); - z = glm::vec3( glm::row(m_view, 2)); - pos = glm::vec3( glm::column(m_view, 3)); - glm::mat3 mat_inv = glm::inverse( glm::mat3(m_view)); - pos = -mat_inv * pos; + void Camera::lookAt(const glm::vec3& position, const glm::vec3& center, const glm::vec3& up) { + m_position = position; + m_center = center; + m_up = up; + + setView(glm::lookAt(position, center, up)); } void Camera::getNearFar( float &near, float &far) const { @@ -46,98 +30,111 @@ namespace vkcv { far = m_far; } - - const glm::mat4 Camera::getView() const { + const glm::mat4& Camera::getView() const { return m_view; } + + void Camera::setView(const glm::mat4 &view) { + m_view = view; + } const glm::mat4& Camera::getProjection() const { return m_projection; } - void Camera::setProjection(const glm::mat4 projection){ - m_projection = projection; + void Camera::setProjection(const glm::mat4& projection) { + m_projection = projection; } - float Camera::getFov() const { - return m_fov; + glm::mat4 Camera::getMVP() const { + const glm::mat4 y_correction ( + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + + return y_correction * m_projection * m_view; } - void Camera::setFov( float fov){ - m_fov = fov; - setPerspective( m_fov, m_ratio, m_near, m_far); + float Camera::getFov() const { + const float tanHalfFovy = 1.0f / m_projection[1][1]; + float halfFovy = std::atan(tanHalfFovy); + + if (halfFovy < 0) { + halfFovy += static_cast<float>(M_PI); + } + + return halfFovy * 2.0f; } - void Camera::changeFov(double offset){ - float fov = m_fov; - float fov_range = m_fov_max - m_fov_min; - float fov_stepsize = glm::radians(fov_range)/m_fov_nsteps; - fov -= (float) offset*fov_stepsize; - if (fov < glm::radians(m_fov_min)) { - fov = glm::radians(m_fov_min); - } - if (fov > glm::radians(m_fov_max)) { - fov = glm::radians(m_fov_max); - } - setFov(fov); + void Camera::setFov( float fov){ + setPerspective(fov, getRatio(), m_near, m_far); } - void Camera::updateRatio( int width, int height){ - m_width = width; - m_height = height; - m_ratio = static_cast<float>(width)/glm::max(height, 1); - setPerspective( m_fov, m_ratio, m_near, m_far); + float Camera::getRatio() const { + const float aspectProduct = 1.0f / m_projection[0][0]; + const float tanHalfFovy = 1.0f / m_projection[1][1]; + + return aspectProduct / tanHalfFovy; } - float Camera::getRatio() const { - return 0.0f; + void Camera::setRatio(float ratio){ + setPerspective( getFov(), ratio, m_near, m_far); } - void Camera::setNearFar( float near, float far){ - m_near = near; - m_far = far; - setPerspective(m_fov, m_ratio, m_near, m_far); + void Camera::setNearFar(float near, float far){ + setPerspective(getFov(), getRatio(), near, far); } - void Camera::setPerspective(float fov, float ratio, float near, float far){ - m_fov = fov; - m_ratio = ratio; - m_near = near; - m_far = far; - m_projection = glm::perspective( m_fov, ratio, m_near, m_far); + void Camera::setPerspective(float fov, float ratio, float near, float far) { + m_near = near; + m_far = far; + setProjection(glm::perspective(fov, ratio, near, far)); } glm::vec3 Camera::getFront() const { glm::vec3 direction; - direction.x = sin(glm::radians(m_yaw)) * cos(glm::radians(m_pitch)); - direction.y = sin(glm::radians(m_pitch)); - direction.z = cos(glm::radians(m_yaw)) * cos(glm::radians(m_pitch)); + direction.x = std::sin(glm::radians(m_yaw)) * std::cos(glm::radians(m_pitch)); + direction.y = std::sin(glm::radians(m_pitch)); + direction.z = std::cos(glm::radians(m_yaw)) * std::cos(glm::radians(m_pitch)); return glm::normalize(direction); } + + void Camera::setFront(const glm::vec3 &front) { + m_pitch = std::atan2(front.y, std::sqrt(front.x * front.x + front.z * front.z)); + m_yaw = std::atan2(front.x, front.z); + } - glm::vec3 Camera::getPosition() const { + const glm::vec3& Camera::getPosition() const { return m_position; } - void Camera::setPosition( glm::vec3 position ){ - m_position = position; + void Camera::setPosition( const glm::vec3& position ){ + lookAt(position, m_center, m_up); } - void Camera::setUp(const glm::vec3 &up) { - m_up = up; + const glm::vec3& Camera::getCenter() const { + return m_center; } + void Camera::setCenter(const glm::vec3& center) { + lookAt(m_position, center, m_up); + } + + const glm::vec3& Camera::getUp() const { + return m_up; + } + + void Camera::setUp(const glm::vec3 &up) { + lookAt(m_position, m_center, up); + } + float Camera::getPitch() const { return m_pitch; } void Camera::setPitch(float pitch) { - if (pitch > 89.0f) { - pitch = 89.0f; - } - if (pitch < -89.0f) { - pitch = -89.0f; - } m_pitch = pitch; } @@ -149,31 +146,4 @@ namespace vkcv { m_yaw = yaw; } - void Camera::panView(double xOffset, double yOffset) { - m_yaw += xOffset; - m_pitch += yOffset; - } - - void Camera::updatePosition(double deltatime ){ - m_position += (m_cameraSpeed * getFront() * static_cast<float> (m_forward) * static_cast<float>(deltatime)); - m_position -= (m_cameraSpeed * getFront() * static_cast<float> (m_backward) * static_cast<float>(deltatime)); - m_position -= (glm::normalize(glm::cross(getFront(), m_up)) * m_cameraSpeed * static_cast<float> (m_left) * static_cast<float>(deltatime)); - m_position += (glm::normalize(glm::cross(getFront(), m_up)) * m_cameraSpeed * static_cast<float> (m_right) * static_cast<float>(deltatime)); - } - - void Camera::moveForward(int action){ - m_forward = static_cast<bool>(action); - } - - void Camera::moveBackward(int action){ - m_backward = static_cast<bool>(action); - } - - void Camera::moveLeft(int action){ - m_left = static_cast<bool>(action); - } - - void Camera::moveRight(int action){ - m_right = static_cast<bool>(action); - } } \ No newline at end of file diff --git a/modules/camera/src/vkcv/camera/CameraManager.cpp b/modules/camera/src/vkcv/camera/CameraManager.cpp index 2631890d646fbf27a4fbb14cfeef706678d8918c..977c61d03dac51598281262acf9609540063a9e4 100644 --- a/modules/camera/src/vkcv/camera/CameraManager.cpp +++ b/modules/camera/src/vkcv/camera/CameraManager.cpp @@ -1,88 +1,156 @@ -#include <iostream> + #include "vkcv/camera/CameraManager.hpp" -namespace vkcv{ +#include <vkcv/Logger.hpp> + +namespace vkcv::camera { - CameraManager::CameraManager(Window &window, float width, float height, glm::vec3 up, glm::vec3 position): - m_window(window), m_width(width), m_height(height) + CameraManager::CameraManager(Window &window, float width, float height) + : m_window(window) { - m_camera.setUp(up); - m_camera.setPosition(position); - m_camera.setPerspective( glm::radians(60.0f), m_width / m_height, 0.1f, 10.f); - m_lastX = width/2.0; - m_lastY = height/2.0; - bindCamera(); + bindCameraToEvents(); + m_activeCameraIndex = 0; + m_lastX = static_cast<float>(window.getWidth()) / 2.0f; + m_lastY = static_cast<float>(window.getHeight()) / 2.0f; } - void CameraManager::bindCamera(){ - e_keyHandle = m_window.e_key.add( [&](int key, int scancode, int action, int mods) { this->keyCallback(key, scancode, action, mods); }); - e_mouseMoveHandle = m_window.e_mouseMove.add( [&]( double offsetX, double offsetY) {this->mouseMoveCallback( offsetX, offsetY);} ); - e_mouseScrollHandle = m_window.e_mouseScroll.add([&](double offsetX, double offsetY) {this->scrollCallback( offsetX, offsetY);} ); - e_mouseButtonHandle = m_window.e_mouseButton.add([&] (int button, int action, int mods) {this->mouseButtonCallback( button, action, mods);}); - e_resizeHandle = m_window.e_resize.add([&] (int width, int height) {this->resizeCallback( width, height);}); + CameraManager::~CameraManager() {} + + void CameraManager::bindCameraToEvents() { + m_keyHandle = m_window.e_key.add( [&](int key, int scancode, int action, int mods) { this->keyCallback(key, scancode, action, mods); }); + m_mouseMoveHandle = m_window.e_mouseMove.add( [&]( double offsetX, double offsetY) {this->mouseMoveCallback( offsetX, offsetY);} ); + m_mouseScrollHandle = m_window.e_mouseScroll.add([&](double offsetX, double offsetY) {this->scrollCallback( offsetX, offsetY);} ); + m_mouseButtonHandle = m_window.e_mouseButton.add([&] (int button, int action, int mods) {this->mouseButtonCallback( button, action, mods);}); + m_resizeHandle = m_window.e_resize.add([&](int width, int height) {this->resizeCallback(width, height);}); + } + + void CameraManager::resizeCallback(int width, int height) { + for (size_t i = 0; i < m_cameras.size(); i++) { + getCamera(i).setRatio(static_cast<float>(width) / static_cast<float>(height));; + } } void CameraManager::mouseButtonCallback(int button, int action, int mods){ - if(button == GLFW_MOUSE_BUTTON_2 && m_roationActive == false && action == GLFW_PRESS){ + if(button == GLFW_MOUSE_BUTTON_2 && action == GLFW_PRESS){ glfwSetInputMode(m_window.getWindow(), GLFW_CURSOR, GLFW_CURSOR_DISABLED); - m_roationActive = true; - }else if(button == GLFW_MOUSE_BUTTON_2 && m_roationActive == true && action == GLFW_RELEASE){ + } + else if(button == GLFW_MOUSE_BUTTON_2 && action == GLFW_RELEASE){ glfwSetInputMode(m_window.getWindow(), GLFW_CURSOR, GLFW_CURSOR_NORMAL); - m_roationActive = false; } + getActiveController().mouseButtonCallback(button, action, mods, getActiveCamera()); } void CameraManager::mouseMoveCallback(double x, double y){ - - float xoffset = x - m_lastX; - float yoffset = m_lastY - y; + auto xoffset = static_cast<float>(x - m_lastX); + auto yoffset = static_cast<float>(y - m_lastY); m_lastX = x; m_lastY = y; + getActiveController().mouseMoveCallback(xoffset, yoffset, getActiveCamera()); + } - if(!m_roationActive){ - return; + void CameraManager::scrollCallback(double offsetX, double offsetY) { + getActiveController().scrollCallback(offsetX, offsetY, getActiveCamera()); + } + + void CameraManager::keyCallback(int key, int scancode, int action, int mods) { + switch (action) { + case GLFW_RELEASE: + switch (key) { + case GLFW_KEY_TAB: + if (m_activeCameraIndex + 1 == m_cameras.size()) { + m_activeCameraIndex = 0; + } + else { + m_activeCameraIndex++; + } + return; + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(m_window.getWindow(), 1); + return; + default: + break; + } + default: + getActiveController().keyCallback(key, scancode, action, mods, getActiveCamera()); + break; } + } + + CameraController& CameraManager::getActiveController() { + const ControllerType type = getControllerType(getActiveCameraIndex()); + return getControllerByType(type); + } + + uint32_t CameraManager::addCamera(ControllerType controllerType) { + Camera camera; + camera.setPerspective(glm::radians(60.0f), m_window.getWidth() / m_window.getHeight(), 0.1f, 10.0f); + return addCamera(controllerType, camera); + } + + uint32_t CameraManager::addCamera(ControllerType controllerType, const Camera &camera) { + const uint32_t index = static_cast<uint32_t>(m_cameras.size()); + m_cameras.push_back(camera); + m_cameraControllerTypes.push_back(controllerType); + return index; + } - float sensitivity = 0.05f; - xoffset *= sensitivity; - yoffset *= sensitivity; + Camera& CameraManager::getCamera(uint32_t cameraIndex) { + if (cameraIndex < 0 || cameraIndex > m_cameras.size() - 1) { + vkcv_log(LogLevel::ERROR, "Invalid camera index: The index must range from 0 to %lu", m_cameras.size()); + return getActiveCamera(); + } + + return m_cameras[cameraIndex]; + } - m_camera.panView( xoffset , yoffset ); + Camera& CameraManager::getActiveCamera() { + return m_cameras[getActiveCameraIndex()]; } - void CameraManager::scrollCallback(double offsetX, double offsetY) { - m_camera.changeFov(offsetY); + void CameraManager::setActiveCamera(uint32_t cameraIndex) { + if (cameraIndex < 0 || cameraIndex > m_cameras.size() - 1) { + vkcv_log(LogLevel::ERROR, "Invalid camera index: The index must range from 0 to %lu", m_cameras.size()); + return; + } + + m_activeCameraIndex = cameraIndex; } - void CameraManager::keyCallback(int key, int scancode, int action, int mods) { + uint32_t CameraManager::getActiveCameraIndex() const { + return m_activeCameraIndex; + } - switch (key) { - case GLFW_KEY_W: - m_camera.moveForward(action); - break; - case GLFW_KEY_S: - m_camera.moveBackward(action); - break; - case GLFW_KEY_A: - m_camera.moveLeft(action); - break; - case GLFW_KEY_D: - m_camera.moveRight(action); - break; - case GLFW_KEY_ESCAPE: - glfwSetWindowShouldClose(m_window.getWindow(), 1); - break; - default: - break; + void CameraManager::setControllerType(uint32_t cameraIndex, ControllerType controllerType) { + if (cameraIndex < 0 || cameraIndex > m_cameras.size() - 1) { + vkcv_log(LogLevel::ERROR, "Invalid camera index: The index must range from 0 to %lu", m_cameras.size()); + return; } + + m_cameraControllerTypes[cameraIndex] = controllerType; } - void CameraManager::resizeCallback(int width, int height){ - m_camera.updateRatio(width, height); + ControllerType CameraManager::getControllerType(uint32_t cameraIndex) { + if (cameraIndex < 0 || cameraIndex > m_cameras.size() - 1) { + vkcv_log(LogLevel::ERROR, "Invalid camera index: The index must range from 0 to %lu", m_cameras.size()); + return ControllerType::NONE; + } + + return m_cameraControllerTypes[cameraIndex]; } - Camera &CameraManager::getCamera(){ - return m_camera; + CameraController& CameraManager::getControllerByType(ControllerType controllerType) { + switch(controllerType) { + case ControllerType::PILOT: + return m_pilotController; + case ControllerType::TRACKBALL: + return m_trackController; + default: + return m_pilotController; + } } + void CameraManager::update(double deltaTime) { + getActiveController().updateCamera(deltaTime, getActiveCamera()); + } + } \ No newline at end of file diff --git a/modules/camera/src/vkcv/camera/PilotCameraController.cpp b/modules/camera/src/vkcv/camera/PilotCameraController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1a50a0efa4b4e75adb81ce869d6b927bd0046758 --- /dev/null +++ b/modules/camera/src/vkcv/camera/PilotCameraController.cpp @@ -0,0 +1,155 @@ +#include "vkcv/camera/PilotCameraController.hpp" + +#include <GLFW/glfw3.h> + +namespace vkcv::camera { + + PilotCameraController::PilotCameraController() { + m_forward = false; + m_backward = false; + m_upward = false; + m_downward = false; + m_left = false; + m_right = false; + + m_rotationActive = false; + + m_cameraSpeed = 2.0f; + + m_fov_nsteps = 100; + m_fov_min = 10; + m_fov_max = 120; + } + + void PilotCameraController::changeFov(double offset, Camera &camera){ + float fov = camera.getFov(); + float fov_range = m_fov_max - m_fov_min; + float fov_stepsize = glm::radians(fov_range) / static_cast<float>(m_fov_nsteps); + fov -= (float) offset*fov_stepsize; + if (fov < glm::radians(m_fov_min)) { + fov = glm::radians(m_fov_min); + } + if (fov > glm::radians(m_fov_max)) { + fov = glm::radians(m_fov_max); + } + camera.setFov(fov); + } + + void PilotCameraController::panView(double xOffset, double yOffset, Camera &camera) { + // handle yaw rotation + float yaw = camera.getYaw() + xOffset; + if (yaw < -180.0f) { + yaw += 360.0f; + } + else if (yaw > 180.0f) { + yaw -= 360.0f; + } + camera.setYaw(yaw); + + // handle pitch rotation + float pitch = camera.getPitch() - yOffset; + if (pitch > 89.0f) { + pitch = 89.0f; + } + if (pitch < -89.0f) { + pitch = -89.0f; + } + camera.setPitch(pitch); + } + + constexpr float getDirectionFactor(bool positive, bool negative) { + return static_cast<float>(positive) - static_cast<float>(negative); + } + + void PilotCameraController::updateCamera(double deltaTime, Camera &camera) { + glm::vec3 position = camera.getPosition(); + + const glm::vec3 front = camera.getFront(); + const glm::vec3 up = camera.getUp(); + const glm::vec3 left = glm::normalize(glm::cross(front, up)); + + const float distance = m_cameraSpeed * static_cast<float>(deltaTime); + + position += distance * getDirectionFactor(m_forward, m_backward) * front; + position += distance * getDirectionFactor(m_left, m_right) * left; + position += distance * getDirectionFactor(m_upward, m_downward) * up; + + camera.lookAt(position, position + front, up); + } + + void PilotCameraController::keyCallback(int key, int scancode, int action, int mods, Camera &camera) { + switch (key) { + case GLFW_KEY_W: + moveForward(action); + break; + case GLFW_KEY_S: + moveBackward(action); + break; + case GLFW_KEY_A: + moveLeft(action); + break; + case GLFW_KEY_D: + moveRight(action); + break; + case GLFW_KEY_E: + moveUpward(action); + break; + case GLFW_KEY_Q: + moveDownward(action); + break; + default: + break; + } + } + + void PilotCameraController::scrollCallback(double offsetX, double offsetY, Camera &camera) { + changeFov(offsetY, camera); + } + + void PilotCameraController::mouseMoveCallback(double xoffset, double yoffset, Camera &camera) { + if(!m_rotationActive){ + return; + } + + float sensitivity = 0.05f; + xoffset *= sensitivity; + yoffset *= sensitivity; + + panView(xoffset , yoffset, camera); + } + + void PilotCameraController::mouseButtonCallback(int button, int action, int mods, Camera &camera) { + if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == false && action == GLFW_PRESS){ + m_rotationActive = true; + } + else if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == true && action == GLFW_RELEASE){ + m_rotationActive = false; + } + } + + + void PilotCameraController::moveForward(int action){ + m_forward = static_cast<bool>(action); + } + + void PilotCameraController::moveBackward(int action){ + m_backward = static_cast<bool>(action); + } + + void PilotCameraController::moveLeft(int action){ + m_left = static_cast<bool>(action); + } + + void PilotCameraController::moveRight(int action){ + m_right = static_cast<bool>(action); + } + + void PilotCameraController::moveUpward(int action){ + m_upward = static_cast<bool>(action); + } + + void PilotCameraController::moveDownward(int action){ + m_downward = static_cast<bool>(action); + } + +} \ No newline at end of file diff --git a/modules/camera/src/vkcv/camera/TrackballCamera.cpp b/modules/camera/src/vkcv/camera/TrackballCamera.cpp deleted file mode 100644 index 3bbb8611dd234499fb9ba08ba87009c8c76660f6..0000000000000000000000000000000000000000 --- a/modules/camera/src/vkcv/camera/TrackballCamera.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "vkcv/camera/TrackballCamera.hpp" - -namespace vkcv{ - - TrackballCamera::TrackballCamera( int width, int height, glm::mat4 projection) - { - setPosition( glm::vec3(0.0f, 0.0f, 5.0) ); - m_center = glm::vec3( 0.0f, 0.0f, 0.0f); - m_up = glm::vec3(0.0f, 1.0f, 0.0f); - - m_width = width; - m_height = height; - - m_sensitivity = 0.010f; - m_stepSize = 0.1f; - m_theta = glm::pi<float>() / 2.0f; - m_phi = 0.f; - m_radius = 1.5; - - m_view = glm::lookAt( m_center + getPosition(), m_center, m_up); - - m_oldX = width/2.f; - m_oldY = height/2.f; - - setProjection(projection); - } - - - TrackballCamera::TrackballCamera(int width, int height) - { - setPosition( glm::vec3(0.0f, 0.0f, 5.0)); - m_center = glm::vec3( 0.0f, 0.0f, 0.0f); - m_up = glm::vec3(0.0f, 1.0f, 0.0f); - - m_width = width; - m_height = height; - - m_sensitivity = 0.010f; - m_stepSize = 0.1f; - m_theta = glm::pi<float>() / 2.0f; - m_phi = 0.f; - m_radius = 1.5; - - m_view = glm::lookAt( m_center + getPosition(), m_center, m_up); - - m_oldX = width/2.f; - m_oldY = height/2.f; - - m_fov = glm::radians(60.f); - m_ratio = m_width / (float) m_height; - m_near = 0.001f; - m_far = 10.f; - glm::mat4 projection = glm::perspective(m_fov, m_ratio, m_near, m_far); - - setProjection(projection); - } - - TrackballCamera::~TrackballCamera() - { - } - - // TODO: Can be done as events... (mouseMove, mouseDown, mouseUp) - /*void TrackballCamera::update( GLFWwindow* window) { - - double x, y; - - glfwGetCursorPos( window, &x, &y); - if (glfwGetMouseButton( window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) - { - float changeX = ((float) x - m_oldX) * m_sensitivity; - float changeY = ((float) y - m_oldY) * m_sensitivity; - - m_theta -= changeY; - if (m_theta < 0.01f) m_theta = 0.01f; - else if (m_theta > glm::pi<float>() - 0.01f) m_theta = glm::pi<float>() - 0.01f; - - m_phi -= changeX; - if (m_phi < 0) m_phi += 2*glm::pi<float>(); - else if (m_phi > 2*glm::pi<float>()) m_phi -= 2*glm::pi<float>(); - } - - m_oldX = (float) x; - m_oldY = (float) y; - - if (glfwGetKey( window, GLFW_KEY_UP) == GLFW_PRESS) - m_radius -= m_stepSize; - if (glfwGetKey( window, GLFW_KEY_DOWN) == GLFW_PRESS) - m_radius += m_stepSize; - if (m_radius < 0.1f) m_radius = 0.1f; - - m_position.x = m_center.x + m_radius * sin(m_theta) * sin(m_phi); - m_position.y = m_center.y + m_radius * cos(m_theta); - m_position.z = m_center.z + m_radius * sin(m_theta) * cos(m_phi); - - m_view = glm::lookAt( m_position, m_center, m_up); - - }*/ - - float TrackballCamera::getSensitivity() const { - return m_sensitivity; - } - - void TrackballCamera::setSensitivity(float sensitivity) { - m_sensitivity = sensitivity; - } - - float TrackballCamera::getStepSize() const { - return m_stepSize; - } - - void TrackballCamera::setStepSize(float stepSize) { - m_stepSize = stepSize; - } - - float TrackballCamera::getTheta() const { - return m_theta; - } - - void TrackballCamera::setTheta(float theta) { - m_theta = theta; - } - - float TrackballCamera::getPhi() const { - return m_phi; - } - - void TrackballCamera::setPhi(float phi) { - m_phi = phi; - } - - float TrackballCamera::getRadius() const { - return m_radius; - } - - void TrackballCamera::setRadius(float radius) { - m_radius = radius; - } - - const glm::vec3& TrackballCamera::getCenter() const { - return m_center; - } - - void TrackballCamera::setCenter(const glm::vec3 ¢er) { - m_center = center; - } -} \ No newline at end of file diff --git a/modules/camera/src/vkcv/camera/TrackballCameraController.cpp b/modules/camera/src/vkcv/camera/TrackballCameraController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..201c6ecdc1c703dbcd53b7dc4b179c86576f2312 --- /dev/null +++ b/modules/camera/src/vkcv/camera/TrackballCameraController.cpp @@ -0,0 +1,100 @@ +#include "vkcv/camera/TrackballCameraController.hpp" + +#include <GLFW/glfw3.h> + +namespace vkcv::camera { + + TrackballCameraController::TrackballCameraController() { + m_rotationActive = false; + m_radius = 3.0f; + m_cameraSpeed = 2.5f; + m_scrollSensitivity = 0.2f; + } + + void TrackballCameraController::setRadius(const float radius) { + if (radius < 0.1f) { + m_radius = 0.1f; + } + else { + m_radius = radius; + } + } + + void TrackballCameraController::panView(double xOffset, double yOffset, Camera &camera) { + // handle yaw rotation + float yaw = camera.getYaw() + xOffset * m_cameraSpeed; + if (yaw < 0.0f) { + yaw += 360.0f; + } + else if (yaw > 360.0f) { + yaw -= 360.0f; + } + camera.setYaw(yaw); + + // handle pitch rotation + float pitch = camera.getPitch() + yOffset * m_cameraSpeed; + if (pitch < 0.0f) { + pitch += 360.0f; + } + else if (pitch > 360.0f) { + pitch -= 360.0f; + } + camera.setPitch(pitch); + } + + void TrackballCameraController::updateRadius(double offset, Camera &camera) { + glm::vec3 cameraPosition = camera.getPosition(); + glm::vec3 cameraCenter = camera.getCenter(); + float radius = glm::length(cameraCenter - cameraPosition); // get current camera radius + setRadius(radius - offset * m_scrollSensitivity); + } + + void TrackballCameraController::updateCamera(double deltaTime, Camera &camera) { + float yaw = camera.getYaw(); + float pitch = camera.getPitch(); + + const glm::vec3 yAxis = glm::vec3(0.0f, 1.0f, 0.0f); + const glm::vec3 xAxis = glm::vec3(1.0f, 0.0f, 0.0f); + + const glm::mat4 rotationY = glm::rotate(glm::mat4(1.0f), glm::radians(yaw), yAxis); + const glm::mat4 rotationX = glm::rotate(rotationY, -glm::radians(pitch), xAxis); + const glm::vec3 translation = glm::vec3( + rotationX * glm::vec4(0.0f, 0.0f, m_radius, 0.0f) + ); + + const glm::vec3 center = camera.getCenter(); + const glm::vec3 position = center + translation; + const glm::vec3 up = glm::vec3( + rotationX * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f) + ); + + camera.lookAt(position, center, up); + } + + void TrackballCameraController::keyCallback(int key, int scancode, int action, int mods, Camera &camera) {} + + void TrackballCameraController::scrollCallback(double offsetX, double offsetY, Camera &camera) { + updateRadius(offsetY, camera); + } + + void TrackballCameraController::mouseMoveCallback(double xoffset, double yoffset, Camera &camera) { + if(!m_rotationActive){ + return; + } + + float sensitivity = 0.05f; + xoffset *= sensitivity; + yoffset *= sensitivity; + + panView(xoffset , yoffset, camera); + } + + void TrackballCameraController::mouseButtonCallback(int button, int action, int mods, Camera &camera) { + if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == false && action == GLFW_PRESS){ + m_rotationActive = true; + } + else if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == true && action == GLFW_RELEASE){ + m_rotationActive = false; + } + } +} \ No newline at end of file diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt index 7ca73a0811df7f1568508b56312ce3348237a695..ccbdaf4101c5dabb3e9d43788e255eab85ad5776 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -2,3 +2,4 @@ # Add new projects/examples here: add_subdirectory(first_triangle) add_subdirectory(first_mesh) +add_subdirectory(cmd_sync_test) \ No newline at end of file diff --git a/projects/cmd_sync_test/.gitignore b/projects/cmd_sync_test/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..16f72da367245ad14a38ee756816f06f8cbbe3d2 --- /dev/null +++ b/projects/cmd_sync_test/.gitignore @@ -0,0 +1 @@ +cmd_sync_test \ No newline at end of file diff --git a/projects/cmd_sync_test/CMakeLists.txt b/projects/cmd_sync_test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..da1d12949d9e8c918d78ab5cb0484106fae69b6a --- /dev/null +++ b/projects/cmd_sync_test/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.16) +project(cmd_sync_test) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# this should fix the execution path to load local files from the project +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +# adding source files to the project +add_executable(cmd_sync_test src/main.cpp) + +# this should fix the execution path to load local files from the project (for MSVC) +if(MSVC) + set_target_properties(cmd_sync_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set_target_properties(cmd_sync_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + + # in addition to setting the output directory, the working directory has to be set + # by default visual studio sets the working directory to the build directory, when using the debugger + set_target_properties(cmd_sync_test PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +endif() + +# including headers of dependencies and the VkCV framework +target_include_directories(cmd_sync_test 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(cmd_sync_test vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera) diff --git a/projects/cmd_sync_test/resources/cube/boards2_vcyc_jpg.jpg b/projects/cmd_sync_test/resources/cube/boards2_vcyc_jpg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248 --- /dev/null +++ b/projects/cmd_sync_test/resources/cube/boards2_vcyc_jpg.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564 +size 1192476 diff --git a/projects/cmd_sync_test/resources/cube/cube.bin b/projects/cmd_sync_test/resources/cube/cube.bin new file mode 100644 index 0000000000000000000000000000000000000000..3303cd8635848bee18e10ab8754d5e4e7218db92 --- /dev/null +++ b/projects/cmd_sync_test/resources/cube/cube.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9bb9b6b8bbe50a0aaa517057f245ee844f80afa7426dacb2aed4128f71629ce4 +size 840 diff --git a/projects/cmd_sync_test/resources/cube/cube.blend b/projects/cmd_sync_test/resources/cube/cube.blend new file mode 100644 index 0000000000000000000000000000000000000000..62ccb2c742094bcfb5ed194ab905bffae86bfd65 --- /dev/null +++ b/projects/cmd_sync_test/resources/cube/cube.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6c1e245f259c610528c9485db6688928faac0ab2addee9e3c2dde7740e4dd09 +size 774920 diff --git a/projects/cmd_sync_test/resources/cube/cube.blend1 b/projects/cmd_sync_test/resources/cube/cube.blend1 new file mode 100644 index 0000000000000000000000000000000000000000..13f21dcca218d7bc7a07a8a9682b5e1d9e607736 --- /dev/null +++ b/projects/cmd_sync_test/resources/cube/cube.blend1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4496f423569b8ca81f3b3a55fad00f925557e0193fb9dbe6cdce7e71fb48f7b +size 774920 diff --git a/projects/cmd_sync_test/resources/cube/cube.glb b/projects/cmd_sync_test/resources/cube/cube.glb new file mode 100644 index 0000000000000000000000000000000000000000..66a42c65e71dcf375e04cc378256024dd3c7834d --- /dev/null +++ b/projects/cmd_sync_test/resources/cube/cube.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:198568b715f397d78f7c358c0f709a419e7fd677e54cdec7c19f71b5ed264897 +size 1194508 diff --git a/projects/cmd_sync_test/resources/cube/cube.gltf b/projects/cmd_sync_test/resources/cube/cube.gltf new file mode 100644 index 0000000000000000000000000000000000000000..428176144843dd06c78fe1d11a6392a0ea02b22d --- /dev/null +++ b/projects/cmd_sync_test/resources/cube/cube.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f82f455647a84ca6242882ae26a79a499d3ce594f8de317ab89488c5b79721ac +size 2823 diff --git a/projects/cmd_sync_test/resources/shaders/compile.bat b/projects/cmd_sync_test/resources/shaders/compile.bat new file mode 100644 index 0000000000000000000000000000000000000000..516c2f2f78001e1a5d182356e7c3fe82d66a45ee --- /dev/null +++ b/projects/cmd_sync_test/resources/shaders/compile.bat @@ -0,0 +1,5 @@ +%VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv +%VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv +%VULKAN_SDK%\Bin32\glslc.exe shadow.vert -o shadow_vert.spv +%VULKAN_SDK%\Bin32\glslc.exe shadow.frag -o shadow_frag.spv +pause \ No newline at end of file diff --git a/projects/cmd_sync_test/resources/shaders/frag.spv b/projects/cmd_sync_test/resources/shaders/frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..ff3110571871d65ce119dc6c5006e7e67aa53546 Binary files /dev/null and b/projects/cmd_sync_test/resources/shaders/frag.spv differ diff --git a/projects/cmd_sync_test/resources/shaders/shader.frag b/projects/cmd_sync_test/resources/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..95f1b3319e1ca5c7c34ff94e5e7198819c0233c1 --- /dev/null +++ b/projects/cmd_sync_test/resources/shaders/shader.frag @@ -0,0 +1,44 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 passNormal; +layout(location = 1) in vec2 passUV; +layout(location = 2) in vec3 passPos; + +layout(location = 0) out vec3 outColor; + +layout(set=0, binding=0) uniform texture2D meshTexture; +layout(set=0, binding=1) uniform sampler textureSampler; +layout(set=0, binding=2) uniform sunBuffer { + vec3 L; float padding; + mat4 lightMatrix; +}; +layout(set=0, binding=3) uniform texture2D shadowMap; +layout(set=0, binding=4) uniform sampler shadowMapSampler; + +float shadowTest(vec3 worldPos){ + vec4 lightPos = lightMatrix * vec4(worldPos, 1); + lightPos /= lightPos.w; + lightPos.xy = lightPos.xy * 0.5 + 0.5; + + if(any(lessThan(lightPos.xy, vec2(0))) || any(greaterThan(lightPos.xy, vec2(1)))){ + return 1; + } + + lightPos.z = clamp(lightPos.z, 0, 1); + + float shadowMapSample = texture(sampler2D(shadowMap, shadowMapSampler), lightPos.xy).r; + float bias = 0.01f; + shadowMapSample += bias; + return shadowMapSample < lightPos.z ? 0 : 1; +} + +void main() { + vec3 N = normalize(passNormal); + vec3 sunColor = vec3(1); + vec3 sun = sunColor * clamp(dot(N, L), 0, 1); + sun *= shadowTest(passPos); + vec3 ambient = vec3(0.1); + vec3 albedo = texture(sampler2D(meshTexture, textureSampler), passUV).rgb; + outColor = albedo * (sun + ambient); +} \ No newline at end of file diff --git a/projects/cmd_sync_test/resources/shaders/shader.vert b/projects/cmd_sync_test/resources/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..0ab82c203806356d0f35dc52c0a6988b286d90d1 --- /dev/null +++ b/projects/cmd_sync_test/resources/shaders/shader.vert @@ -0,0 +1,22 @@ +#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(location = 2) out vec3 passPos; + +layout( push_constant ) uniform constants{ + mat4 mvp; + mat4 model; +}; + +void main() { + gl_Position = mvp * vec4(inPosition, 1.0); + passNormal = inNormal; + passUV = inUV; + passPos = (model * vec4(inPosition, 1)).xyz; +} \ No newline at end of file diff --git a/projects/cmd_sync_test/resources/shaders/shadow.frag b/projects/cmd_sync_test/resources/shaders/shadow.frag new file mode 100644 index 0000000000000000000000000000000000000000..848f853f556660b4900b5db7fb6fc98d57c1cd5b --- /dev/null +++ b/projects/cmd_sync_test/resources/shaders/shadow.frag @@ -0,0 +1,6 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +void main() { + +} \ No newline at end of file diff --git a/projects/cmd_sync_test/resources/shaders/shadow.vert b/projects/cmd_sync_test/resources/shaders/shadow.vert new file mode 100644 index 0000000000000000000000000000000000000000..e0f41d42d575fa64fedbfa04adf89ac0f4aeebe8 --- /dev/null +++ b/projects/cmd_sync_test/resources/shaders/shadow.vert @@ -0,0 +1,12 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 inPosition; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +void main() { + gl_Position = mvp * vec4(inPosition, 1.0); +} \ No newline at end of file diff --git a/projects/cmd_sync_test/resources/shaders/shadow_frag.spv b/projects/cmd_sync_test/resources/shaders/shadow_frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..6be3bd2518a3b1f234e39aea2503ba86cfb3314b Binary files /dev/null and b/projects/cmd_sync_test/resources/shaders/shadow_frag.spv differ diff --git a/projects/cmd_sync_test/resources/shaders/shadow_vert.spv b/projects/cmd_sync_test/resources/shaders/shadow_vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..afaa0824ee9be2c22209d611943c6512587dce24 Binary files /dev/null and b/projects/cmd_sync_test/resources/shaders/shadow_vert.spv differ diff --git a/projects/cmd_sync_test/resources/shaders/vert.spv b/projects/cmd_sync_test/resources/shaders/vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..5e514eef5983927316465679af5461f507497130 Binary files /dev/null and b/projects/cmd_sync_test/resources/shaders/vert.spv differ diff --git a/projects/cmd_sync_test/resources/triangle/Triangle.bin b/projects/cmd_sync_test/resources/triangle/Triangle.bin new file mode 100644 index 0000000000000000000000000000000000000000..57f26ad96592b64377e6aa93823d96a94e6c5022 --- /dev/null +++ b/projects/cmd_sync_test/resources/triangle/Triangle.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:412ebd5f7242c266b4957e7e26be13aa331dbcb7bbb854ab334a2437ae8ed959 +size 104 diff --git a/projects/cmd_sync_test/resources/triangle/Triangle.blend b/projects/cmd_sync_test/resources/triangle/Triangle.blend new file mode 100644 index 0000000000000000000000000000000000000000..2421dc5e1bb029d73a9ec09cc4530c5196851fd7 --- /dev/null +++ b/projects/cmd_sync_test/resources/triangle/Triangle.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:387e544df173219fbf292a64a6656d1d782bbf71a5a9e9fdef0a308f47b05477 +size 758144 diff --git a/projects/cmd_sync_test/resources/triangle/Triangle.glb b/projects/cmd_sync_test/resources/triangle/Triangle.glb new file mode 100644 index 0000000000000000000000000000000000000000..4148620cd6af0dadbc791aa1c52bb5431a40884b --- /dev/null +++ b/projects/cmd_sync_test/resources/triangle/Triangle.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4be087a605212d139416b5352a018283b26b99260cbcddb7013a1beeb331227 +size 980 diff --git a/projects/cmd_sync_test/resources/triangle/Triangle.gltf b/projects/cmd_sync_test/resources/triangle/Triangle.gltf new file mode 100644 index 0000000000000000000000000000000000000000..a188e6ee16a5e8486cf307c7bda8cfd99bdbeea6 --- /dev/null +++ b/projects/cmd_sync_test/resources/triangle/Triangle.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5fc354e040f79cff329e919677b194c75e3a522c6406f75c1108ad9575f12ec +size 2202 diff --git a/projects/cmd_sync_test/src/main.cpp b/projects/cmd_sync_test/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9946a35373576d3690d22bf18a03cd8e52b15e56 --- /dev/null +++ b/projects/cmd_sync_test/src/main.cpp @@ -0,0 +1,308 @@ +#include <iostream> +#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"; + + uint32_t windowWidth = 800; + uint32_t windowHeight = 600; + + vkcv::Window window = vkcv::Window::create( + applicationName, + windowWidth, + windowHeight, + true + ); + + vkcv::camera::CameraManager cameraManager(window, windowWidth, windowHeight); + uint32_t camIndex = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + uint32_t camIndex2 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camIndex).setPosition(glm::vec3(0.f, 0.f, 3.f)); + cameraManager.getCamera(camIndex).setNearFar(0.1f, 30.0f); + cameraManager.getCamera(camIndex).setYaw(180.0f); + + cameraManager.getCamera(camIndex2).setNearFar(0.1f, 30.0f); + + 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"; + int result = vkcv::asset::loadMesh(path, mesh); + + if (result == 1) { + std::cout << "Mesh loading successful!" << std::endl; + } + else { + std::cout << "Mesh loading failed: " << result << std::endl; + return 1; + } + + 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); + + auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes; + + std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) { + return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type); + }); + + const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { + vkcv::VertexBufferBinding(attributes[0].offset, vertexBuffer.getVulkanHandle()), + vkcv::VertexBufferBinding(attributes[1].offset, vertexBuffer.getVulkanHandle()), + vkcv::VertexBufferBinding(attributes[2].offset, vertexBuffer.getVulkanHandle()) }; + + const vkcv::Mesh loadedMesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices); + + // an example attachment for passes that output to the window + const vkcv::AttachmentDescription present_color_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + core.getSwapchainImageFormat() + ); + + const vkcv::AttachmentDescription depth_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + vk::Format::eD32Sfloat + ); + + vkcv::PassConfig firstMeshPassDefinition({ present_color_attachment, depth_attachment }); + vkcv::PassHandle firstMeshPass = core.createPass(firstMeshPassDefinition); + + if (!firstMeshPass) { + std::cout << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::ShaderProgram firstMeshProgram{}; + firstMeshProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/vert.spv")); + firstMeshProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv")); + + const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> bindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + bindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] })); + } + + const vkcv::VertexLayout firstMeshLayout (bindings); + + std::vector<vkcv::DescriptorBinding> descriptorBindings = { firstMeshProgram.getReflectedDescriptors()[0] }; + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorBindings); + + const vkcv::PipelineConfig firstMeshPipelineConfig( + firstMeshProgram, + windowWidth, + windowHeight, + firstMeshPass, + {firstMeshLayout}, + { core.getDescriptorSet(descriptorSet).layout }, + true); + vkcv::PipelineHandle firstMeshPipeline = core.createGraphicsPipeline(firstMeshPipelineConfig); + + if (!firstMeshPipeline) { + 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 + ); + + vkcv::SamplerHandle shadowSampler = core.createSampler( + vkcv::SamplerFilterType::NEAREST, + vkcv::SamplerFilterType::NEAREST, + vkcv::SamplerMipmapMode::NEAREST, + vkcv::SamplerAddressMode::CLAMP_TO_EDGE + ); + + vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight).getHandle(); + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + const vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle); + + const std::vector<glm::vec3> instancePositions = { + glm::vec3( 0.f, -2.f, 0.f), + glm::vec3( 3.f, 0.f, 0.f), + glm::vec3(-3.f, 0.f, 0.f), + glm::vec3( 0.f, 2.f, 0.f), + glm::vec3( 0.f, -5.f, 0.f) + }; + + std::vector<glm::mat4> modelMatrices; + std::vector<vkcv::DrawcallInfo> drawcalls; + std::vector<vkcv::DrawcallInfo> shadowDrawcalls; + for (const auto& position : instancePositions) { + modelMatrices.push_back(glm::translate(glm::mat4(1.f), position)); + drawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, { descriptorUsage })); + shadowDrawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, {})); + } + + modelMatrices.back() *= glm::scale(glm::mat4(1.f), glm::vec3(10.f, 1.f, 10.f)); + + std::vector<std::array<glm::mat4, 2>> mainPassMatrices; + std::vector<glm::mat4> mvpLight; + + vkcv::ShaderProgram shadowShader; + shadowShader.addShader(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow_vert.spv"); + shadowShader.addShader(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow_frag.spv"); + + const vk::Format shadowMapFormat = vk::Format::eD16Unorm; + const std::vector<vkcv::AttachmentDescription> shadowAttachments = { + vkcv::AttachmentDescription(vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, shadowMapFormat) + }; + const vkcv::PassConfig shadowPassConfig(shadowAttachments); + const vkcv::PassHandle shadowPass = core.createPass(shadowPassConfig); + + const uint32_t shadowMapResolution = 1024; + const vkcv::Image shadowMap = core.createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1); + const vkcv::PipelineConfig shadowPipeConfig( + shadowShader, + shadowMapResolution, + shadowMapResolution, + shadowPass, + {firstMeshLayout}, + {}, + false); + const vkcv::PipelineHandle shadowPipe = core.createGraphicsPipeline(shadowPipeConfig); + + struct LightInfo { + glm::vec3 direction; + float padding; + glm::mat4 lightMatrix; + }; + LightInfo lightInfo; + vkcv::Buffer lightBuffer = core.createBuffer<LightInfo>(vkcv::BufferType::UNIFORM, sizeof(glm::vec3)); + + vkcv::DescriptorWrites setWrites; + setWrites.sampledImageWrites = { + vkcv::SampledImageDescriptorWrite(0, texture.getHandle()), + vkcv::SampledImageDescriptorWrite(3, shadowMap.getHandle()) }; + setWrites.samplerWrites = { + vkcv::SamplerDescriptorWrite(1, sampler), + vkcv::SamplerDescriptorWrite(4, shadowSampler) }; + setWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(2, lightBuffer.getHandle()) }; + core.writeDescriptorSet(descriptorSet, setWrites); + + auto start = std::chrono::system_clock::now(); + const auto appStartTime = start; + while (window.isWindowOpen()) { + vkcv::Window::pollEvents(); + + uint32_t swapchainWidth, swapchainHeight; + if (!core.beginFrame(swapchainWidth, swapchainHeight)) { + continue; + } + + if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) { + depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight).getHandle(); + + windowWidth = swapchainWidth; + windowHeight = swapchainHeight; + } + + auto end = std::chrono::system_clock::now(); + auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); + + start = end; + cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); + + auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - appStartTime); + + const float sunTheta = 0.001f * static_cast<float>(duration.count()); + lightInfo.direction = glm::normalize(glm::vec3(std::cos(sunTheta), 1, std::sin(sunTheta))); + + const float shadowProjectionSize = 5.f; + glm::mat4 projectionLight = glm::ortho( + -shadowProjectionSize, + shadowProjectionSize, + -shadowProjectionSize, + shadowProjectionSize, + -shadowProjectionSize, + shadowProjectionSize); + + glm::mat4 vulkanCorrectionMatrix(1.f); + vulkanCorrectionMatrix[2][2] = 0.5; + vulkanCorrectionMatrix[3][2] = 0.5; + projectionLight = vulkanCorrectionMatrix * projectionLight; + + const glm::mat4 viewLight = glm::lookAt(glm::vec3(0), -lightInfo.direction, glm::vec3(0, -1, 0)); + + lightInfo.lightMatrix = projectionLight * viewLight; + lightBuffer.fill({ lightInfo }); + + const glm::mat4 viewProjectionCamera = cameraManager.getActiveCamera().getMVP(); + + mainPassMatrices.clear(); + mvpLight.clear(); + for (const auto& m : modelMatrices) { + mainPassMatrices.push_back({ viewProjectionCamera * m, m }); + mvpLight.push_back(lightInfo.lightMatrix* m); + } + + vkcv::PushConstantData pushConstantData((void*)mainPassMatrices.data(), 2 * sizeof(glm::mat4)); + const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; + + vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4)); + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + core.recordDrawcallsToCmdStream( + cmdStream, + shadowPass, + shadowPipe, + shadowPushConstantData, + shadowDrawcalls, + { shadowMap.getHandle() }); + + core.prepareImageForSampling(cmdStream, shadowMap.getHandle()); + + core.recordDrawcallsToCmdStream( + cmdStream, + firstMeshPass, + firstMeshPipeline, + pushConstantData, + drawcalls, + renderTargets); + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + core.endFrame(); + } + + return 0; +} diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp index 723bc9a5fed150d8be94c4c97ba83cfacccc6d4d..7c32a76dc6f4b2fe320df733e65a54a88e3a42c1 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -8,20 +8,21 @@ int main(int argc, const char** argv) { const char* applicationName = "First Mesh"; + uint32_t windowWidth = 800; + uint32_t windowHeight = 600; + vkcv::Window window = vkcv::Window::create( applicationName, - 800, - 600, + windowWidth, + windowHeight, true ); - vkcv::CameraManager cameraManager(window, static_cast<float>(window.getWidth()), static_cast<float>(window.getHeight())); - vkcv::Core core = vkcv::Core::create( window, applicationName, VK_MAKE_VERSION(0, 0, 1), - { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, + { vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer }, {}, { "VK_KHR_swapchain" } ); @@ -58,65 +59,58 @@ int main(int argc, const char** argv) { // 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); + vkcv::PassConfig firstMeshPassDefinition({ present_color_attachment, depth_attachment }); + vkcv::PassHandle firstMeshPass = core.createPass(firstMeshPassDefinition); - if (!trianglePass) { + if (!firstMeshPass) { 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); + vkcv::ShaderProgram firstMeshProgram{}; + firstMeshProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/vert.spv")); + firstMeshProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv")); auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes; - std::sort(attributes.begin(), attributes.end(), [](const vkcv::VertexAttribute& x, const vkcv::VertexAttribute& y) { + std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::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 }); - - //only exemplary code for testing - for (int i = 0; i < 1001; i++) { - vkcv::ResourcesHandle furtherSets = core.createResourceDescription({ setConfig }); + const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments(); + std::vector<vkcv::VertexBinding> bindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + bindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] })); } - //end of exemplary code + + const vkcv::VertexLayout firstMeshLayout (bindings); + + uint32_t setID = 0; + std::vector<vkcv::DescriptorBinding> descriptorBindings = { firstMeshProgram.getReflectedDescriptors()[setID] }; + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorBindings); - const vkcv::PipelineConfig trianglePipelineDefinition( - triangleShaderProgram, + const vkcv::PipelineConfig firstMeshPipelineConfig( + firstMeshProgram, UINT32_MAX, UINT32_MAX, - trianglePass, - mesh.vertexGroups[0].vertexBuffer.attributes, - { core.getDescriptorSetLayout(set, 0) }); - vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition); + firstMeshPass, + {firstMeshLayout}, + { core.getDescriptorSet(descriptorSet).layout }, + true); + vkcv::PipelineHandle firstMeshPipeline = core.createGraphicsPipeline(firstMeshPipelineConfig); - if (!trianglePipeline) { + if (!firstMeshPipeline) { std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; return EXIT_FAILURE; } @@ -131,42 +125,72 @@ int main(int argc, const char** argv) { 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() } - }; + const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { + vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[0].offset), vertexBuffer.getVulkanHandle()), + vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[1].offset), vertexBuffer.getVulkanHandle()), + vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[2].offset), vertexBuffer.getVulkanHandle()) }; vkcv::DescriptorWrites setWrites; - setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) }; - setWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, sampler) }; - core.writeResourceDescription(set, 0, setWrites); + setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) }; + setWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, sampler) }; + core.writeDescriptorSet(descriptorSet, setWrites); - auto start = std::chrono::system_clock::now(); - while (window.isWindowOpen()) { - vkcv::Window::pollEvents(); + vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight).getHandle(); + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); - if(window.getHeight() == 0 || window.getWidth() == 0) - continue; + const vkcv::Mesh renderMesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices); - core.beginFrame(); + vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle); + vkcv::DrawcallInfo drawcall(renderMesh, { descriptorUsage }); + + vkcv::camera::CameraManager cameraManager(window, windowWidth, windowHeight); + uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -3)); + + auto start = std::chrono::system_clock::now(); + + while (window.isWindowOpen()) { + vkcv::Window::pollEvents(); + + if(window.getHeight() == 0 || window.getWidth() == 0) + continue; + + uint32_t swapchainWidth, swapchainHeight; + if (!core.beginFrame(swapchainWidth, swapchainHeight)) { + continue; + } + + if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) { + depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight).getHandle(); + + windowWidth = swapchainWidth; + windowHeight = swapchainHeight; + } + auto end = std::chrono::system_clock::now(); - auto deltatime = end - start; + auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(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, - sizeof(mvp), - &mvp, - vertexBufferBindings, - indexBuffer.getHandle(), - mesh.vertexGroups[0].numIndices, - set, - 0 - ); + cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); + glm::mat4 mvp = cameraManager.getActiveCamera().getMVP(); + + vkcv::PushConstantData pushConstantData((void*)&mvp, sizeof(glm::mat4)); + + const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + core.recordDrawcallsToCmdStream( + cmdStream, + firstMeshPass, + firstMeshPipeline, + pushConstantData, + { drawcall }, + renderTargets); + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); core.endFrame(); } diff --git a/projects/first_triangle/shaders/comp.spv b/projects/first_triangle/shaders/comp.spv new file mode 100644 index 0000000000000000000000000000000000000000..b414e36b2bea66dab00746298e536d029091e0fd Binary files /dev/null and b/projects/first_triangle/shaders/comp.spv differ diff --git a/projects/first_triangle/shaders/compile.bat b/projects/first_triangle/shaders/compile.bat index b4521235c40fe5fb163bab874560c2f219b7517f..17743a7c49cdfc6e091c43a42a0adb755a731682 100644 --- a/projects/first_triangle/shaders/compile.bat +++ b/projects/first_triangle/shaders/compile.bat @@ -1,3 +1,4 @@ %VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv %VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv +%VULKAN_SDK%\Bin32\glslc.exe shader.comp -o comp.spv pause \ No newline at end of file diff --git a/projects/first_triangle/shaders/shader.comp b/projects/first_triangle/shaders/shader.comp new file mode 100644 index 0000000000000000000000000000000000000000..fad6cd0815f2f09bf92dcc3171e2e3723f5466df --- /dev/null +++ b/projects/first_triangle/shaders/shader.comp @@ -0,0 +1,25 @@ +#version 440 + +layout(std430, binding = 0) buffer testBuffer +{ + float test1[10]; + float test2[10]; + float test3[10]; +}; + +layout( push_constant ) uniform constants{ + float pushConstant; +}; + +layout(local_size_x = 5) in; + +void main(){ + + if(gl_GlobalInvocationID.x >= 10){ + return; + } + + test1[gl_GlobalInvocationID.x] = gl_GlobalInvocationID.x; + test2[gl_GlobalInvocationID.x] = 69; // nice! + test3[gl_GlobalInvocationID.x] = pushConstant; +} \ No newline at end of file diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index 6c3b2272e32ebdc7844ebd16441f6e17c9429495..62d71d9ce3cab388361ac1163b67281ecc465af5 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -16,8 +16,6 @@ int main(int argc, const char** argv) { false ); - vkcv::CameraManager cameraManager(window, windowWidth, windowHeight); - vkcv::Core core = vkcv::Core::create( window, applicationName, @@ -77,9 +75,6 @@ int main(int argc, const char** argv) { // 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()); @@ -93,28 +88,50 @@ int main(int argc, const char** argv) { return EXIT_FAILURE; } + // Graphics Pipeline 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, + (uint32_t)windowWidth, + (uint32_t)windowHeight, trianglePass, {}, - {}); + {}, + false); + vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition); - + if (!trianglePipeline) { std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; return EXIT_FAILURE; } - std::vector<vkcv::VertexBufferBinding> vertexBufferBindings; + // Compute Pipeline + vkcv::ShaderProgram computeShaderProgram{}; + computeShaderProgram.addShader(vkcv::ShaderStage::COMPUTE, std::filesystem::path("shaders/comp.spv")); + + // take care, assuming shader has exactly one descriptor set + vkcv::DescriptorSetHandle computeDescriptorSet = core.createDescriptorSet(computeShaderProgram.getReflectedDescriptors()[0]); + + vkcv::PipelineHandle computePipeline = core.createComputePipeline( + computeShaderProgram, + { core.getDescriptorSet(computeDescriptorSet).layout }); + + struct ComputeTestBuffer { + float test1[10]; + float test2[10]; + float test3[10]; + }; + + vkcv::Buffer computeTestBuffer = core.createBuffer<ComputeTestBuffer>(vkcv::BufferType::STORAGE, 1); + + vkcv::DescriptorWrites computeDescriptorWrites; + computeDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, computeTestBuffer.getHandle()) }; + core.writeDescriptorSet(computeDescriptorSet, computeDescriptorWrites); /* * BufferHandle triangleVertices = core.createBuffer(vertices); @@ -131,27 +148,60 @@ int main(int argc, const char** argv) { * * PipelineHandle trianglePipeline = core.CreatePipeline(trianglePipeline); */ - auto start = std::chrono::system_clock::now(); + auto start = std::chrono::system_clock::now(); + + vkcv::ImageHandle swapchainImageHandle = vkcv::ImageHandle::createSwapchainImageHandle(); + + const vkcv::Mesh renderMesh({}, triangleIndexBuffer.getVulkanHandle(), 3); + vkcv::DrawcallInfo drawcall(renderMesh, {}); + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::camera::CameraManager cameraManager(window, windowWidth, windowHeight); + uint32_t camIndex = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + uint32_t camIndex2 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camIndex).setPosition(glm::vec3(0, 0, -2)); + while (window.isWindowOpen()) { - core.beginFrame(); window.pollEvents(); + + uint32_t swapchainWidth, swapchainHeight; // No resizing = No problem + if (!core.beginFrame(swapchainWidth, swapchainHeight)) { + continue; + } + auto end = std::chrono::system_clock::now(); - auto deltatime = end - start; + auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); start = end; - cameraManager.getCamera().updateView(std::chrono::duration<double>(deltatime).count()); - const glm::mat4 mvp = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView(); + + cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); + glm::mat4 mvp = cameraManager.getActiveCamera().getMVP(); + + vkcv::PushConstantData pushConstantData((void*)&mvp, sizeof(glm::mat4)); + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); - core.renderMesh( + core.recordDrawcallsToCmdStream( + cmdStream, trianglePass, trianglePipeline, - sizeof(mvp), - &mvp, - vertexBufferBindings, - triangleIndexBuffer.getHandle(), - 3, - vkcv::ResourcesHandle(), - 0); + pushConstantData, + { drawcall }, + { swapchainInput }); + + const uint32_t dispatchSize[3] = { 2, 1, 1 }; + const float theMeaningOfLife = 42; + + core.recordComputeDispatchToCmdStream( + cmdStream, + computePipeline, + dispatchSize, + { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(computeDescriptorSet).vulkanHandle) }, + vkcv::PushConstantData((void*)&theMeaningOfLife, sizeof(theMeaningOfLife))); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); core.endFrame(); } diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp index ef874606ed0f30f33e4b1e0720d4f3455a8e137e..6d494c4ec90726d46039007607464378624f1c75 100644 --- a/src/vkcv/BufferManager.cpp +++ b/src/vkcv/BufferManager.cpp @@ -158,7 +158,7 @@ namespace vkcv { SubmitInfo submitInfo; submitInfo.queueType = QueueType::Transfer; - core->submitCommands( + core->recordAndSubmitCommands( submitInfo, [&info, &mapped_size](const vk::CommandBuffer& commandBuffer) { const vk::BufferCopy region ( diff --git a/src/vkcv/CommandResources.cpp b/src/vkcv/CommandResources.cpp index 71c990c3c222f2318c2f5744ff6295f667d9e6f8..a31e6967d85bd099fe5cbbc865b0e062212ca16e 100644 --- a/src/vkcv/CommandResources.cpp +++ b/src/vkcv/CommandResources.cpp @@ -1,6 +1,7 @@ #include "vkcv/CommandResources.hpp" #include <iostream> +#include "vkcv/Logger.hpp" namespace vkcv { @@ -62,7 +63,7 @@ namespace vkcv { return queueManager.getPresentQueue(); } else { - std::cerr << "getQueueForSubmit error: unknown queue type" << std::endl; + vkcv_log(LogLevel::ERROR, "Unknown queue type"); return queueManager.getGraphicsQueues().front(); // graphics is the most general queue } } diff --git a/src/vkcv/CommandStreamManager.cpp b/src/vkcv/CommandStreamManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a5b359b912d9cef36e0b03379d7f0f6f0951381 --- /dev/null +++ b/src/vkcv/CommandStreamManager.cpp @@ -0,0 +1,121 @@ +#include "vkcv/CommandStreamManager.hpp" +#include "vkcv/Core.hpp" + +#include "vkcv/Logger.hpp" + +namespace vkcv { + CommandStreamManager::CommandStreamManager() noexcept : m_core(nullptr){} + + CommandStreamManager::~CommandStreamManager() noexcept { + for (const auto& stream : m_commandStreams) { + if (stream.cmdBuffer && stream.cmdBuffer) { + m_core->getContext().getDevice().freeCommandBuffers(stream.cmdPool, stream.cmdBuffer); + } + } + } + + void CommandStreamManager::init(Core* core) { + if (!core) { + vkcv_log(LogLevel::ERROR, "Requires valid core pointer"); + } + m_core = core; + } + + CommandStreamHandle CommandStreamManager::createCommandStream( + const vk::Queue queue, + vk::CommandPool cmdPool) { + + const vk::CommandBuffer cmdBuffer = allocateCommandBuffer(m_core->getContext().getDevice(), cmdPool); + + CommandStream stream(cmdBuffer, queue, cmdPool); + beginCommandBuffer(stream.cmdBuffer, vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + + // find unused stream + int unusedStreamIndex = -1; + for (int i = 0; i < m_commandStreams.size(); i++) { + if (m_commandStreams[i].cmdBuffer) { + // still in use + } + else { + unusedStreamIndex = i; + break; + } + } + + const bool foundUnusedStream = unusedStreamIndex >= 0; + if (foundUnusedStream) { + m_commandStreams[unusedStreamIndex] = stream; + return CommandStreamHandle(unusedStreamIndex); + } + + CommandStreamHandle handle(m_commandStreams.size()); + m_commandStreams.push_back(stream); + return handle; + } + + void CommandStreamManager::recordCommandsToStream( + const CommandStreamHandle handle, + const RecordCommandFunction record) { + + const size_t id = handle.getId(); + if (id >= m_commandStreams.size()) { + vkcv_log(LogLevel::ERROR, "Requires valid handle"); + return; + } + + CommandStream& stream = m_commandStreams[id]; + record(stream.cmdBuffer); + } + + void CommandStreamManager::addFinishCallbackToStream( + const CommandStreamHandle handle, + const FinishCommandFunction finish) { + + const size_t id = handle.getId(); + if (id >= m_commandStreams.size()) { + vkcv_log(LogLevel::ERROR, "Requires valid handle"); + return; + } + + CommandStream& stream = m_commandStreams[id]; + stream.callbacks.push_back(finish); + } + + void CommandStreamManager::submitCommandStreamSynchronous( + const CommandStreamHandle handle, + std::vector<vk::Semaphore> &waitSemaphores, + std::vector<vk::Semaphore> &signalSemaphores) { + + const size_t id = handle.getId(); + if (id >= m_commandStreams.size()) { + vkcv_log(LogLevel::ERROR, "Requires valid handle"); + return; + } + CommandStream& stream = m_commandStreams[id]; + stream.cmdBuffer.end(); + + const auto device = m_core->getContext().getDevice(); + const vk::Fence waitFence = createFence(device); + submitCommandBufferToQueue(stream.queue, stream.cmdBuffer, waitFence, waitSemaphores, signalSemaphores); + waitForFence(device, waitFence); + device.destroyFence(waitFence); + + device.freeCommandBuffers(stream.cmdPool, stream.cmdBuffer); + stream.cmdBuffer = nullptr; + stream.cmdPool = nullptr; + stream.queue = nullptr; + + for (const auto& finishCallback : stream.callbacks) { + finishCallback(); + } + } + + vk::CommandBuffer CommandStreamManager::getStreamCommandBuffer(const CommandStreamHandle handle) { + const size_t id = handle.getId(); + if (id >= m_commandStreams.size()) { + vkcv_log(LogLevel::ERROR, "Requires valid handle"); + return nullptr; + } + return m_commandStreams[id].cmdBuffer; + } +} \ No newline at end of file diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 27e84ee5cdb062adc0e9f96cdfa7e761e6f24279..44e7111e1f4941ef2f0f8114ac788d7db4a13b5a 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -14,6 +14,9 @@ #include "ImageManager.hpp" #include "DescriptorManager.hpp" #include "ImageLayoutTransitions.hpp" +#include "vkcv/CommandStreamManager.hpp" + +#include "vkcv/Logger.hpp" namespace vkcv { @@ -52,8 +55,8 @@ namespace vkcv return m_Context; } - Core::Core(Context &&context, Window &window, const SwapChain& swapChain, std::vector<vk::ImageView> imageViews, - const CommandResources& commandResources, const SyncResources& syncResources) noexcept : + Core::Core(Context &&context, Window &window, const SwapChain& swapChain, std::vector<vk::ImageView> imageViews, + const CommandResources& commandResources, const SyncResources& syncResources) noexcept : m_Context(std::move(context)), m_window(window), m_swapchain(swapChain), @@ -61,20 +64,25 @@ namespace vkcv m_PassManager{std::make_unique<PassManager>(m_Context.m_Device)}, m_PipelineManager{std::make_unique<PipelineManager>(m_Context.m_Device)}, m_DescriptorManager(std::make_unique<DescriptorManager>(m_Context.m_Device)), - m_BufferManager{std::unique_ptr<BufferManager>(new BufferManager())}, - m_SamplerManager(std::unique_ptr<SamplerManager>(new SamplerManager(m_Context.m_Device))), - m_ImageManager{std::unique_ptr<ImageManager>(new ImageManager(*m_BufferManager))}, + 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_CommandStreamManager{std::unique_ptr<CommandStreamManager>(new CommandStreamManager)}, m_CommandResources(commandResources), m_SyncResources(syncResources) { - m_BufferManager->m_core = this; - m_BufferManager->init(); - - m_ImageManager->m_core = this; + m_BufferManager->m_core = this; + m_BufferManager->init(); + m_CommandStreamManager->init(this); + + m_ImageManager->m_core = this; - e_resizeHandle = window.e_resize.add( [&](int width, int height) { - m_swapchain.recreateSwapchain(); - }); + e_resizeHandle = window.e_resize.add( [&](int width, int height) { + m_swapchain.signalSwapchainRecreation(); + }); + + m_swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); + m_swapchainImageLayouts.resize(m_swapchainImages.size(), vk::ImageLayout::eUndefined); } Core::~Core() noexcept { @@ -95,6 +103,13 @@ namespace vkcv return m_PipelineManager->createPipeline(config, *m_PassManager); } + PipelineHandle Core::createComputePipeline( + const ShaderProgram &shaderProgram, + const std::vector<vk::DescriptorSetLayout>& descriptorSetLayouts) + { + return m_PipelineManager->createComputePipeline(shaderProgram, descriptorSetLayouts); + } + PassHandle Core::createPass(const PassConfig &config) { @@ -103,17 +118,23 @@ namespace vkcv Result Core::acquireSwapchainImage() { uint32_t imageIndex; + + vk::Result result; - const auto& acquireResult = m_Context.getDevice().acquireNextImageKHR( - m_swapchain.getSwapchain(), - std::numeric_limits<uint64_t>::max(), - m_SyncResources.swapchainImageAcquired, - nullptr, - &imageIndex, {} - ); + try { + result = m_Context.getDevice().acquireNextImageKHR( + m_swapchain.getSwapchain(), + std::numeric_limits<uint64_t>::max(), + m_SyncResources.swapchainImageAcquired, + nullptr, + &imageIndex, {} + ); + } catch (vk::OutOfDateKHRError e) { + result = vk::Result::eErrorOutOfDateKHR; + } - if (acquireResult != vk::Result::eSuccess) { - std::cerr << vk::to_string(acquireResult) << std::endl; + if (result != vk::Result::eSuccess) { + vkcv_log(LogLevel::ERROR, "%s", vk::to_string(result).c_str()); return Result::ERROR; } @@ -121,7 +142,7 @@ namespace vkcv return Result::SUCCESS; } - void Core::beginFrame() { + bool Core::beginFrame(uint32_t& width, uint32_t& height) { if (m_swapchain.shouldUpdateSwapchain()) { m_Context.getDevice().waitIdle(); @@ -130,87 +151,105 @@ namespace vkcv m_swapchain.updateSwapchain(m_Context, m_window); m_swapchainImageViews = createImageViews(m_Context, m_swapchain); + m_swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); + + m_swapchainImageLayouts.clear(); + m_swapchainImageLayouts.resize(m_swapchainImages.size(), vk::ImageLayout::eUndefined); } if (acquireSwapchainImage() != Result::SUCCESS) { - std::cerr << "Acquire failed!" << std::endl; + vkcv_log(LogLevel::ERROR, "Acquire failed"); m_currentSwapchainImageIndex = std::numeric_limits<uint32_t>::max(); } m_Context.getDevice().waitIdle(); // TODO: this is a sin against graphics programming, but its getting late - Alex + + const auto& extent = m_swapchain.getExtent(); + + width = extent.width; + height = extent.height; + + return (m_currentSwapchainImageIndex != std::numeric_limits<uint32_t>::max()); } - void Core::renderMesh( - const PassHandle &renderpassHandle, - const PipelineHandle &pipelineHandle, - 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 - ) { + void Core::recordDrawcallsToCmdStream( + const CommandStreamHandle cmdStreamHandle, + const PassHandle renderpassHandle, + const PipelineHandle pipelineHandle, + const PushConstantData &pushConstantData, + const std::vector<DrawcallInfo> &drawcalls, + const std::vector<ImageHandle> &renderTargets) { if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { return; } - - const vk::Extent2D& extent = m_swapchain.getExtent(); - - const uint32_t width = extent.width; - const uint32_t height = extent.height; - const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle); - const PassConfig passConfig = m_PassManager->getPassConfig(renderpassHandle); - - const bool checkForDepthImage = ( - (!m_DepthImage) || - (width != m_ImageManager->getImageWidth(m_DepthImage)) || - (height != m_ImageManager->getImageHeight(m_DepthImage)) - ); - - if (checkForDepthImage) { - for (const auto &attachment : passConfig.attachments) { - if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { - m_DepthImage = m_ImageManager->createImage(width, height, 1, attachment.format); - break; - } + uint32_t width; + uint32_t height; + if (renderTargets.size() > 0) { + const vkcv::ImageHandle firstImage = renderTargets[0]; + if (firstImage.isSwapchainImage()) { + const auto& swapchainExtent = m_swapchain.getExtent(); + width = swapchainExtent.width; + height = swapchainExtent.height; + } + else { + width = m_ImageManager->getImageWidth(firstImage); + height = m_ImageManager->getImageHeight(firstImage); } } - - const vk::ImageView imageView = m_swapchainImageViews[m_currentSwapchainImageIndex]; + else { + width = 1; + height = 1; + } + // TODO: validate that width/height match for all attachments + + const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle); + const PassConfig passConfig = m_PassManager->getPassConfig(renderpassHandle); + const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle); - const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle); - const vkcv::PipelineConfig pipelineConfig = m_PipelineManager->getPipelineConfig(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); - std::vector<vk::ImageView> attachments; - attachments.push_back(imageView); - - if (m_DepthImage) { - attachments.push_back(m_ImageManager->getVulkanImageView(m_DepthImage)); + const vk::ImageView swapchainImageView = m_swapchainImageViews[m_currentSwapchainImageIndex]; + + std::vector<vk::ImageView> attachmentsViews; + for (const ImageHandle handle : renderTargets) { + vk::ImageView targetHandle; + const auto cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle); + if (handle.isSwapchainImage()) { + recordSwapchainImageLayoutTransition(cmdBuffer, vk::ImageLayout::eColorAttachmentOptimal); + targetHandle = m_swapchainImageViews[m_currentSwapchainImageIndex]; + } + else { + targetHandle = m_ImageManager->getVulkanImageView(handle); + const bool isDepthImage = isDepthFormat(m_ImageManager->getImageFormat(handle)); + const vk::ImageLayout targetLayout = + isDepthFormat ? vk::ImageLayout::eDepthStencilAttachmentOptimal : vk::ImageLayout::eColorAttachmentOptimal; + m_ImageManager->recordImageLayoutTransition(handle, targetLayout, cmdBuffer); + } + attachmentsViews.push_back(targetHandle); } vk::Framebuffer framebuffer = nullptr; - const vk::FramebufferCreateInfo createInfo({}, - renderpass, - static_cast<uint32_t>(attachments.size()), - attachments.data(), - width, - height, - 1); + const vk::FramebufferCreateInfo createInfo( + {}, + renderpass, + static_cast<uint32_t>(attachmentsViews.size()), + attachmentsViews.data(), + width, + height, + 1); if(m_Context.m_Device.createFramebuffer(&createInfo, nullptr, &framebuffer) != vk::Result::eSuccess) { - std::cout << "FAILED TO CREATE TEMPORARY FRAMEBUFFER!" << std::endl; + vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer"); return; } vk::Viewport dynamicViewport(0.0f, 0.0f, - static_cast<float>(width), static_cast<float>(height), - 0.0f, 1.0f); + static_cast<float>(width), static_cast<float>(height), + 0.0f, 1.0f); vk::Rect2D dynamicScissor({0, 0}, {width, height}); @@ -227,7 +266,7 @@ namespace vkcv if (attachment.load_operation == AttachmentOperation::CLEAR) { float clear = 0.0f; - if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { + if (isDepthFormat(attachment.format)) { clear = 1.0f; } @@ -246,35 +285,60 @@ namespace vkcv cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); - if(pipelineConfig.m_Height == UINT32_MAX && pipelineConfig.m_Width == UINT32_MAX) + const PipelineConfig &pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle); + if(pipeConfig.m_UseDynamicViewport) { cmdBuffer.setViewport(0, 1, &dynamicViewport); cmdBuffer.setScissor(0, 1, &dynamicScissor); } - 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); + for (int i = 0; i < drawcalls.size(); i++) { + recordDrawcall(drawcalls[i], cmdBuffer, pipelineLayout, pushConstantData, i); } - 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(); }; - auto finishFunction = [&]() + auto finishFunction = [framebuffer, this]() { m_Context.m_Device.destroy(framebuffer); }; - submitCommands(submitInfo, submitFunction, finishFunction); + recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction); + } + + void Core::recordComputeDispatchToCmdStream( + CommandStreamHandle cmdStreamHandle, + PipelineHandle computePipeline, + const uint32_t dispatchCount[3], + const std::vector<DescriptorSetUsage>& descriptorSetUsages, + const PushConstantData& pushConstantData) { + + auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) { + + const auto pipelineLayout = m_PipelineManager->getVkPipelineLayout(computePipeline); + + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, m_PipelineManager->getVkPipeline(computePipeline)); + for (const auto& usage : descriptorSetUsages) { + cmdBuffer.bindDescriptorSets( + vk::PipelineBindPoint::eCompute, + pipelineLayout, + usage.setLocation, + { usage.vulkanHandle }, + {}); + } + if (pushConstantData.sizePerDrawcall > 0) { + cmdBuffer.pushConstants( + pipelineLayout, + vk::ShaderStageFlagBits::eCompute, + 0, + pushConstantData.sizePerDrawcall, + pushConstantData.data); + } + cmdBuffer.dispatch(dispatchCount[0], dispatchCount[1], dispatchCount[2]); + }; + + recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr); } void Core::endFrame() { @@ -289,16 +353,22 @@ namespace vkcv m_SyncResources.renderFinished, m_SyncResources.swapchainImageAcquired }; - vk::Result presentResult; const vk::SwapchainKHR& swapchain = m_swapchain.getSwapchain(); const vk::PresentInfoKHR presentInfo( waitSemaphores, swapchain, - m_currentSwapchainImageIndex, - presentResult); - queueManager.getPresentQueue().handle.presentKHR(presentInfo); - if (presentResult != vk::Result::eSuccess) { - std::cout << "Error: swapchain present failed" << std::endl; + m_currentSwapchainImageIndex); + + vk::Result result; + + try { + result = queueManager.getPresentQueue().handle.presentKHR(presentInfo); + } catch (vk::OutOfDateKHRError e) { + result = vk::Result::eErrorOutOfDateKHR; + } + + if (result != vk::Result::eSuccess) { + vkcv_log(LogLevel::ERROR, "Swapchain present failed (%s)", vk::to_string(result).c_str()); } } @@ -306,7 +376,10 @@ namespace vkcv return m_swapchain.getSwapchainFormat(); } - void Core::submitCommands(const SubmitInfo &submitInfo, const RecordCommandFunction& record, const FinishCommandFunction& finish) + void Core::recordAndSubmitCommands( + const SubmitInfo &submitInfo, + const RecordCommandFunction &record, + const FinishCommandFunction &finish) { const vk::Device& device = m_Context.getDevice(); @@ -329,34 +402,60 @@ namespace vkcv finish(); } } - + + CommandStreamHandle Core::createCommandStream(QueueType queueType) { + + const vk::Device& device = m_Context.getDevice(); + const vkcv::Queue queue = getQueueForSubmit(queueType, m_Context.getQueueManager()); + const vk::CommandPool cmdPool = chooseCmdPool(queue, m_CommandResources); + + return m_CommandStreamManager->createCommandStream(queue.handle, cmdPool); + } + + void Core::recordCommandsToStream( + const CommandStreamHandle cmdStreamHandle, + const RecordCommandFunction &record, + const FinishCommandFunction &finish) { + + m_CommandStreamManager->recordCommandsToStream(cmdStreamHandle, record); + if (finish) { + m_CommandStreamManager->addFinishCallbackToStream(cmdStreamHandle, finish); + } + } + + void Core::submitCommandStream(const CommandStreamHandle handle) { + std::vector<vk::Semaphore> waitSemaphores; + // FIXME: add proper user controllable sync + std::vector<vk::Semaphore> signalSemaphores = { m_SyncResources.renderFinished }; + m_CommandStreamManager->submitCommandStreamSynchronous(handle, waitSemaphores, signalSemaphores); + } + SamplerHandle Core::createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode) { - return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, 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<DescriptorSetConfig> &descriptorSets) + DescriptorSetHandle Core::createDescriptorSet(const std::vector<DescriptorBinding>& bindings) { - return m_DescriptorManager->createResourceDescription(descriptorSets); + return m_DescriptorManager->createDescriptorSet(bindings); } - void Core::writeResourceDescription(ResourcesHandle handle, size_t setIndex, const DescriptorWrites &writes) { - m_DescriptorManager->writeResourceDescription( - handle, - setIndex, + void Core::writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites &writes) { + m_DescriptorManager->writeDescriptorSet( + handle, writes, *m_ImageManager, *m_BufferManager, *m_SamplerManager); } - vk::DescriptorSetLayout Core::getDescriptorSetLayout(ResourcesHandle handle, size_t setIndex) { - return m_DescriptorManager->getDescriptorSetLayout(handle, setIndex); + DescriptorSet Core::getDescriptorSet(const DescriptorSetHandle handle) const { + return m_DescriptorManager->getDescriptorSet(handle); } std::vector<vk::ImageView> Core::createImageViews( Context &context, SwapChain& swapChain){ @@ -380,11 +479,32 @@ namespace vkcv vk::ImageViewType::e2D, swapChain.getSwapchainFormat(), componentMapping, - subResourceRange - ); + subResourceRange); imageViews.push_back(context.getDevice().createImageView(imageViewCreateInfo)); } return imageViews; } + + void Core::recordSwapchainImageLayoutTransition(vk::CommandBuffer cmdBuffer, vk::ImageLayout newLayout) { + auto& imageLayout = m_swapchainImageLayouts[m_currentSwapchainImageIndex]; + const auto transitionBarrier = createSwapchainImageLayoutTransitionBarrier( + m_swapchainImages[m_currentSwapchainImageIndex], + imageLayout, + newLayout); + recordImageBarrier(cmdBuffer, transitionBarrier); + imageLayout = newLayout; + } + + void Core::prepareSwapchainImageForPresent(const CommandStreamHandle handle) { + m_CommandStreamManager->recordCommandsToStream(handle, [&](vk::CommandBuffer cmdBuffer) { + recordSwapchainImageLayoutTransition(cmdBuffer, vk::ImageLayout::ePresentSrcKHR); + }); + } + + void Core::prepareImageForSampling(const CommandStreamHandle cmdStream, const ImageHandle image) { + recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eShaderReadOnlyOptimal, cmdBuffer); + }, nullptr); + } } diff --git a/src/vkcv/DescriptorConfig.cpp b/src/vkcv/DescriptorConfig.cpp index c4f6e326560e91747d206fecc983525a5b7bb6dc..54e879ac7e6ec7825a4c003899e3c264454c547f 100644 --- a/src/vkcv/DescriptorConfig.cpp +++ b/src/vkcv/DescriptorConfig.cpp @@ -1,20 +1,15 @@ #include "vkcv/DescriptorConfig.hpp" -#include <utility> - namespace vkcv { - - DescriptorBinding::DescriptorBinding( - DescriptorType descriptorType, - uint32_t descriptorCount, - ShaderStage shaderStage - ) noexcept : - descriptorType{descriptorType}, - descriptorCount{descriptorCount}, - shaderStage{shaderStage} - {}; - - DescriptorSetConfig::DescriptorSetConfig(std::vector<DescriptorBinding> bindings) noexcept : - bindings{std::move(bindings)} - {}; + DescriptorBinding::DescriptorBinding( + uint32_t bindingID, + DescriptorType descriptorType, + uint32_t descriptorCount, + ShaderStage shaderStage) noexcept + : + bindingID(bindingID), + descriptorType(descriptorType), + descriptorCount(descriptorCount), + shaderStage(shaderStage) {} + } diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp index 2ecb23bd39cd4656c12d1816ece3db404ecdc248..f591daf90b47b57a758b2b24c7fa87b5c33e3c46 100644 --- a/src/vkcv/DescriptorManager.cpp +++ b/src/vkcv/DescriptorManager.cpp @@ -1,12 +1,9 @@ #include "DescriptorManager.hpp" +#include "vkcv/Logger.hpp" + namespace vkcv { - DescriptorManager::ResourceDescription::ResourceDescription(std::vector<vk::DescriptorSet> sets, - std::vector<vk::DescriptorSetLayout> layouts) noexcept : - descriptorSets{std::move(sets)}, - descriptorSetLayouts{std::move(layouts)} - {} DescriptorManager::DescriptorManager(vk::Device device) noexcept: m_Device{ device } { @@ -29,67 +26,63 @@ namespace vkcv DescriptorManager::~DescriptorManager() noexcept { - for (uint64_t id = 0; id < m_ResourceDescriptions.size(); id++) { - destroyResourceDescriptionById(id); + for (uint64_t id = 0; id < m_DescriptorSets.size(); id++) { + destroyDescriptorSetById(id); } + m_DescriptorSets.clear(); for (const auto &pool : m_Pools) { m_Device.destroy(pool); } } - ResourcesHandle DescriptorManager::createResourceDescription(const std::vector<DescriptorSetConfig> &descriptorSets) + DescriptorSetHandle DescriptorManager::createDescriptorSet(const std::vector<DescriptorBinding>& bindings) { - std::vector<vk::DescriptorSet> vk_sets; - std::vector<vk::DescriptorSetLayout> vk_setLayouts; + std::vector<vk::DescriptorSetLayoutBinding> setBindings = {}; - for (const auto &set : descriptorSets) { - std::vector<vk::DescriptorSetLayoutBinding> setBindings = {}; + //create each set's binding + for (uint32_t i = 0; i < bindings.size(); i++) { + vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding( + bindings[i].bindingID, + convertDescriptorTypeFlag(bindings[i].descriptorType), + bindings[i].descriptorCount, + convertShaderStageFlag(bindings[i].shaderStage)); + setBindings.push_back(descriptorSetLayoutBinding); + } - //create each set's binding - for (uint32_t j = 0; j < set.bindings.size(); j++) { - vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding( - j, - convertDescriptorTypeFlag(set.bindings[j].descriptorType), - set.bindings[j].descriptorCount, - convertShaderStageFlag(set.bindings[j].shaderStage)); - setBindings.push_back(descriptorSetLayoutBinding); - } + DescriptorSet set; - //create the descriptor set's layout from the bindings gathered above - vk::DescriptorSetLayoutCreateInfo layoutInfo({}, setBindings); - vk::DescriptorSetLayout layout = nullptr; - if(m_Device.createDescriptorSetLayout(&layoutInfo, nullptr, &layout) != vk::Result::eSuccess) - { - std::cout << "FAILED TO CREATE DESCRIPTOR SET LAYOUT" << std::endl; - return ResourcesHandle(); - }; - vk_setLayouts.push_back(layout); - } - //create and allocate the set(s) based on the layouts that have been gathered above - vk_sets.resize(vk_setLayouts.size()); - vk::DescriptorSetAllocateInfo allocInfo(m_Pools.back(), vk_sets.size(), vk_setLayouts.data()); - auto result = m_Device.allocateDescriptorSets(&allocInfo, vk_sets.data()); + //create the descriptor set's layout from the bindings gathered above + vk::DescriptorSetLayoutCreateInfo layoutInfo({}, setBindings); + if(m_Device.createDescriptorSetLayout(&layoutInfo, nullptr, &set.layout) != vk::Result::eSuccess) + { + vkcv_log(LogLevel::ERROR, "Failed to create descriptor set layout"); + return DescriptorSetHandle(); + }; + + //create and allocate the set based on the layout that have been gathered above + vk::DescriptorSetAllocateInfo allocInfo(m_Pools.back(), 1, &set.layout); + auto result = m_Device.allocateDescriptorSets(&allocInfo, &set.vulkanHandle); if(result != vk::Result::eSuccess) { //create a new descriptor pool if the previous one ran out of memory if (result == vk::Result::eErrorOutOfPoolMemory) { allocateDescriptorPool(); allocInfo.setDescriptorPool(m_Pools.back()); - result = m_Device.allocateDescriptorSets(&allocInfo, vk_sets.data()); + result = m_Device.allocateDescriptorSets(&allocInfo, &set.vulkanHandle); } if (result != vk::Result::eSuccess) { - std::cout << "FAILED TO ALLOCATE DESCRIPTOR SET" << std::endl; - std::cout << vk::to_string(result) << std::endl; - for (const auto& layout : vk_setLayouts) - m_Device.destroy(layout); - - return ResourcesHandle(); + vkcv_log(LogLevel::ERROR, "Failed to create descriptor set (%s)", + vk::to_string(result).c_str()); + + m_Device.destroy(set.layout); + return DescriptorSetHandle(); } }; - const uint64_t id = m_ResourceDescriptions.size(); - m_ResourceDescriptions.emplace_back(vk_sets, vk_setLayouts); - return ResourcesHandle(id, [&](uint64_t id) { destroyResourceDescriptionById(id); }); + const uint64_t id = m_DescriptorSets.size(); + + m_DescriptorSets.push_back(set); + return DescriptorSetHandle(id, [&](uint64_t id) { destroyDescriptorSetById(id); }); } struct WriteDescriptorSetInfo { @@ -99,15 +92,14 @@ namespace vkcv vk::DescriptorType type; }; - void DescriptorManager::writeResourceDescription( - const ResourcesHandle &handle, - size_t setIndex, + void DescriptorManager::writeDescriptorSet( + const DescriptorSetHandle &handle, const DescriptorWrites &writes, const ImageManager &imageManager, const BufferManager &bufferManager, const SamplerManager &samplerManager) { - vk::DescriptorSet set = m_ResourceDescriptions[handle.getId()].descriptorSets[setIndex]; + vk::DescriptorSet set = m_DescriptorSets[handle.getId()].vulkanHandle; std::vector<vk::DescriptorImageInfo> imageInfos; std::vector<vk::DescriptorBufferInfo> bufferInfos; @@ -230,12 +222,8 @@ namespace vkcv m_Device.updateDescriptorSets(vulkanWrites, nullptr); } - vk::DescriptorSet DescriptorManager::getDescriptorSet(const ResourcesHandle &handle, size_t index) const { - return m_ResourceDescriptions[handle.getId()].descriptorSets[index]; - } - - vk::DescriptorSetLayout DescriptorManager::getDescriptorSetLayout(const ResourcesHandle &handle, size_t index) const { - return m_ResourceDescriptions[handle.getId()].descriptorSetLayouts[index]; + DescriptorSet DescriptorManager::getDescriptorSet(const DescriptorSetHandle handle) const { + return m_DescriptorSets[handle.getId()]; } vk::DescriptorType DescriptorManager::convertDescriptorTypeFlag(DescriptorType type) { @@ -252,7 +240,7 @@ namespace vkcv case DescriptorType::IMAGE_STORAGE: return vk::DescriptorType::eStorageImage; default: - std::cerr << "Error: DescriptorManager::convertDescriptorTypeFlag, unknown DescriptorType" << std::endl; + vkcv_log(LogLevel::ERROR, "Unknown DescriptorType"); return vk::DescriptorType::eUniformBuffer; } } @@ -277,25 +265,25 @@ namespace vkcv } } - void DescriptorManager::destroyResourceDescriptionById(uint64_t id) { - if (id >= m_ResourceDescriptions.size()) { + void DescriptorManager::destroyDescriptorSetById(uint64_t id) { + if (id >= m_DescriptorSets.size()) { + vkcv_log(LogLevel::ERROR, "Invalid id"); return; } - auto& resourceDescription = m_ResourceDescriptions[id]; - - for(const auto &layout : resourceDescription.descriptorSetLayouts) { - m_Device.destroyDescriptorSetLayout(layout); + auto& set = m_DescriptorSets[id]; + if (set.layout) { + m_Device.destroyDescriptorSetLayout(set.layout); + set.layout = nullptr; } - - resourceDescription.descriptorSetLayouts.clear(); + // FIXME: descriptor set itself not destroyed } vk::DescriptorPool DescriptorManager::allocateDescriptorPool() { vk::DescriptorPool pool; if (m_Device.createDescriptorPool(&m_PoolInfo, nullptr, &pool) != vk::Result::eSuccess) { - std::cout << "FAILED TO ALLOCATE DESCRIPTOR POOL." << std::endl; + vkcv_log(LogLevel::WARNING, "Failed to allocate descriptor pool"); pool = nullptr; }; m_Pools.push_back(pool); diff --git a/src/vkcv/DescriptorManager.hpp b/src/vkcv/DescriptorManager.hpp index 22042c703256055e3852b7a4729faad39b5d0dbb..d18be64f3b069af68cecce68f6fa623c81f8dfa4 100644 --- a/src/vkcv/DescriptorManager.hpp +++ b/src/vkcv/DescriptorManager.hpp @@ -21,52 +21,29 @@ namespace vkcv explicit DescriptorManager(vk::Device device) noexcept; ~DescriptorManager() noexcept; - /** - * Creates all vk::DescriptorSets and allocates them from the pool. - * DescriptorSets are put inside a ResourceDescription struct. - * Structs are then put into m_ResourceDescriptions. - * @param[in] vector of filled vkcv::DescriptorSet structs - * @return index into that objects a resource handle - */ - ResourcesHandle createResourceDescription(const std::vector<DescriptorSetConfig> & descriptorSets); + DescriptorSetHandle createDescriptorSet(const std::vector<DescriptorBinding> &descriptorBindings); - void writeResourceDescription( - const ResourcesHandle &handle, - size_t setIndex, + void writeDescriptorSet( + const DescriptorSetHandle &handle, const DescriptorWrites &writes, const ImageManager &imageManager, const BufferManager &bufferManager, const SamplerManager &samplerManager); [[nodiscard]] - vk::DescriptorSet getDescriptorSet(const ResourcesHandle &handle, size_t index) const; - [[nodiscard]] - vk::DescriptorSetLayout getDescriptorSetLayout(const ResourcesHandle &handle, size_t index) const; + DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const; private: - vk::Device m_Device; + vk::Device m_Device; std::vector<vk::DescriptorPool> m_Pools; std::vector<vk::DescriptorPoolSize> m_PoolSizes; vk::DescriptorPoolCreateInfo m_PoolInfo; - /** - * Container for all resources requested by the user in one call of createResourceDescription. - * Includes descriptor sets and the respective descriptor set layouts. - */ - struct ResourceDescription - { - ResourceDescription() = delete; - ResourceDescription(std::vector<vk::DescriptorSet> sets, std::vector<vk::DescriptorSetLayout> layouts) noexcept; - - std::vector<vk::DescriptorSet> descriptorSets; - std::vector<vk::DescriptorSetLayout> descriptorSetLayouts; - }; - /** * Contains all the resource descriptions that were requested by the user in calls of createResourceDescription. */ - std::vector<ResourceDescription> m_ResourceDescriptions; + std::vector<DescriptorSet> m_DescriptorSets; /** * Converts the flags of the descriptor types from VulkanCV (vkcv) to Vulkan (vk). @@ -85,7 +62,7 @@ namespace vkcv * Destroys a specific resource description * @param[in] the handle id of the respective resource description */ - void destroyResourceDescriptionById(uint64_t id); + void destroyDescriptorSetById(uint64_t id); /** * creates a descriptor pool based on the poolSizes and poolInfo defined in the constructor diff --git a/src/vkcv/DrawcallRecording.cpp b/src/vkcv/DrawcallRecording.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85b6eeb5fa413223b7b7f10f77b868252912041b --- /dev/null +++ b/src/vkcv/DrawcallRecording.cpp @@ -0,0 +1,41 @@ +#include <vkcv/DrawcallRecording.hpp> + +namespace vkcv { + + void recordDrawcall( + const DrawcallInfo &drawcall, + vk::CommandBuffer cmdBuffer, + vk::PipelineLayout pipelineLayout, + const PushConstantData &pushConstantData, + const size_t drawcallIndex) { + + for (uint32_t i = 0; i < drawcall.mesh.vertexBufferBindings.size(); i++) { + const auto& vertexBinding = drawcall.mesh.vertexBufferBindings[i]; + cmdBuffer.bindVertexBuffers(i, vertexBinding.buffer, vertexBinding.offset); + } + + for (const auto& descriptorUsage : drawcall.descriptorSets) { + cmdBuffer.bindDescriptorSets( + vk::PipelineBindPoint::eGraphics, + pipelineLayout, + descriptorUsage.setLocation, + descriptorUsage.vulkanHandle, + nullptr); + } + + cmdBuffer.bindIndexBuffer(drawcall.mesh.indexBuffer, 0, vk::IndexType::eUint16); //FIXME: choose proper size + + const size_t drawcallPushConstantOffset = drawcallIndex * pushConstantData.sizePerDrawcall; + // char* cast because void* does not support pointer arithmetic + const void* drawcallPushConstantData = drawcallPushConstantOffset + (char*)pushConstantData.data; + + cmdBuffer.pushConstants( + pipelineLayout, + vk::ShaderStageFlagBits::eAll, + 0, + pushConstantData.sizePerDrawcall, + drawcallPushConstantData); + + cmdBuffer.drawIndexed(drawcall.mesh.indexCount, 1, 0, 0, {}); + } +} \ No newline at end of file diff --git a/src/vkcv/Handles.cpp b/src/vkcv/Handles.cpp index 33e0cdba7f30fe6c3576803de1d22f912c935dd0..020489418c8e2db6ce2062d6fd20f06f90a05c37 100644 --- a/src/vkcv/Handles.cpp +++ b/src/vkcv/Handles.cpp @@ -93,7 +93,7 @@ namespace vkcv { } ImageHandle ImageHandle::createSwapchainImageHandle(const HandleDestroyFunction &destroy) { - return ImageHandle(UINT64_MAX - 1, destroy); + return ImageHandle(uint64_t(UINT64_MAX - 1), destroy); } } diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp index 9ce5c25a01957ca43c75c14f6b37a7a3e82feee4..f861daeb1cd7de9697e2f649de444666b8b0e63c 100644 --- a/src/vkcv/Image.cpp +++ b/src/vkcv/Image.cpp @@ -8,13 +8,24 @@ namespace vkcv{ + bool isDepthFormat(const vk::Format format) { + switch (format) { + case(vk::Format::eD16Unorm): return true; + case(vk::Format::eD16UnormS8Uint): return true; + case(vk::Format::eD24UnormS8Uint): return true; + case(vk::Format::eD32Sfloat): return true; + case(vk::Format::eD32SfloatS8Uint): return true; + default: return false; + } + } + 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); + return Image(manager, manager->createImage(width, height, depth, format)); } vk::Format Image::getFormat() const { - return m_format; + return m_manager->getImageFormat(m_handle); } uint32_t Image::getWidth() const { @@ -28,15 +39,10 @@ namespace vkcv{ uint32_t Image::getDepth() const { return m_manager->getImageDepth(m_handle); } - - 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; + m_manager->switchImageLayoutImmediate(m_handle, newLayout); } vkcv::ImageHandle Image::getHandle() const { @@ -47,12 +53,9 @@ namespace vkcv{ m_manager->fillImage(m_handle, data, size); } - Image::Image(ImageManager* manager, const ImageHandle& handle, vk::Format format) : + Image::Image(ImageManager* manager, const ImageHandle& handle) : m_manager(manager), - m_handle(handle), - m_format(format), - m_layout(vk::ImageLayout::eUndefined) - { - } + m_handle(handle) + {} } diff --git a/src/vkcv/ImageLayoutTransitions.cpp b/src/vkcv/ImageLayoutTransitions.cpp index 0b08819489c41c5cde3ceddbb0629a5d2ae3cd30..cb0f90a79d188cd80a5744d8c6ad7718e542d473 100644 --- a/src/vkcv/ImageLayoutTransitions.cpp +++ b/src/vkcv/ImageLayoutTransitions.cpp @@ -1,24 +1,68 @@ #include "ImageLayoutTransitions.hpp" +#include "vkcv/Image.hpp" namespace vkcv { - void transitionImageLayoutImmediate(const vk::CommandBuffer cmdBuffer, const vk::Image image, - const vk::ImageLayout oldLayout, const vk::ImageLayout newLayout) { + vk::ImageMemoryBarrier createImageLayoutTransitionBarrier(const ImageManager::Image &image, vk::ImageLayout newLayout) { - // TODO: proper src and dst masks - const vk::PipelineStageFlags srcStageMask = vk::PipelineStageFlagBits::eAllCommands; - const vk::PipelineStageFlags dstStageMask = vk::PipelineStageFlagBits::eAllCommands; - const vk::DependencyFlags dependecyFlags = {}; + vk::ImageAspectFlags aspectFlags; + if (isDepthFormat(image.m_format)) { + aspectFlags = vk::ImageAspectFlagBits::eDepth; + } + else { + aspectFlags = vk::ImageAspectFlagBits::eColor; + } - // TODO: proper src and dst masks - const vk::AccessFlags srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; - const vk::AccessFlags dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; + vk::ImageSubresourceRange imageSubresourceRange( + aspectFlags, + 0, + image.m_levels, + 0, + image.m_layers + ); - // TODO: proper aspect flags - const vk::ImageAspectFlags aspectFlags = vk::ImageAspectFlagBits::eColor; + // TODO: precise AccessFlagBits, will require a lot of context + return vk::ImageMemoryBarrier( + vk::AccessFlagBits::eMemoryWrite, + vk::AccessFlagBits::eMemoryRead, + image.m_layout, + newLayout, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + image.m_handle, + imageSubresourceRange); + } + + vk::ImageMemoryBarrier createSwapchainImageLayoutTransitionBarrier( + vk::Image vulkanHandle, + vk::ImageLayout oldLayout, + vk::ImageLayout newLayout) { - const vk::ImageSubresourceRange subresourceRange(aspectFlags, 0, 1, 0, 1); - vk::ImageMemoryBarrier imageBarrier(srcAccessMask, dstAccessMask, oldLayout, newLayout, 0, 0, image, subresourceRange); + vk::ImageSubresourceRange imageSubresourceRange( + vk::ImageAspectFlagBits::eColor, + 0, + 1, + 0, + 1); + + // TODO: precise AccessFlagBits, will require a lot of context + return vk::ImageMemoryBarrier( + vk::AccessFlagBits::eMemoryWrite, + vk::AccessFlagBits::eMemoryRead, + oldLayout, + newLayout, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + vulkanHandle, + imageSubresourceRange); + } - cmdBuffer.pipelineBarrier(srcStageMask, dstStageMask, dependecyFlags, 0, nullptr, 0, nullptr, 1, &imageBarrier, {}); + void recordImageBarrier(vk::CommandBuffer cmdBuffer, vk::ImageMemoryBarrier barrier) { + cmdBuffer.pipelineBarrier( + vk::PipelineStageFlagBits::eTopOfPipe, + vk::PipelineStageFlagBits::eBottomOfPipe, + {}, + nullptr, + nullptr, + barrier); } } \ No newline at end of file diff --git a/src/vkcv/ImageLayoutTransitions.hpp b/src/vkcv/ImageLayoutTransitions.hpp index 3dbfbdf6690a0683b30a96f400e7e4b6ec25c379..5c147f133a6492746ad410367e5e627be000d7be 100644 --- a/src/vkcv/ImageLayoutTransitions.hpp +++ b/src/vkcv/ImageLayoutTransitions.hpp @@ -1,7 +1,13 @@ #pragma once #include <vulkan/vulkan.hpp> +#include "ImageManager.hpp" namespace vkcv { - void transitionImageLayoutImmediate(const vk::CommandBuffer cmdBuffer, const vk::Image image, - const vk::ImageLayout oldLayout, const vk::ImageLayout newLayout); + vk::ImageMemoryBarrier createImageLayoutTransitionBarrier(const ImageManager::Image& image, vk::ImageLayout newLayout); + vk::ImageMemoryBarrier createSwapchainImageLayoutTransitionBarrier( + vk::Image vulkanHandle, + vk::ImageLayout oldLayout, + vk::ImageLayout newLayout); + + void recordImageBarrier(vk::CommandBuffer cmdBuffer, vk::ImageMemoryBarrier barrier); } \ No newline at end of file diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp index 4b46b5fc422e1666049b22d422a16fec10fe08aa..1e3d19d02d7e86546d142bb64440364407e81824 100644 --- a/src/vkcv/ImageManager.cpp +++ b/src/vkcv/ImageManager.cpp @@ -5,11 +5,35 @@ */ #include "ImageManager.hpp" #include "vkcv/Core.hpp" +#include "ImageLayoutTransitions.hpp" +#include "vkcv/Logger.hpp" #include <algorithm> namespace vkcv { + ImageManager::Image::Image( + vk::Image handle, + vk::DeviceMemory memory, + vk::ImageView view, + uint32_t width, + uint32_t height, + uint32_t depth, + vk::Format format, + uint32_t layers, + uint32_t levels) + : + m_handle(handle), + m_memory(memory), + m_view(view), + m_width(width), + m_height(height), + m_depth(depth), + m_format(format), + m_layers(layers), + m_levels(levels) + {} + /** * @brief searches memory type index for image allocation, combines requirements of image and application * @param physicalMemoryProperties Memory Properties of physical device @@ -89,6 +113,11 @@ namespace vkcv { } } + if (isDepthFormat) { + imageType = vk::ImageType::e2D; + imageViewType = vk::ImageViewType::e2D; + } + vk::ImageTiling imageTiling = vk::ImageTiling::eOptimal; if (!formatProperties.optimalTilingFeatures) { @@ -166,7 +195,7 @@ namespace vkcv { 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 }); + m_images.push_back(Image(image, memory, view, width, height, depth, format, arrayLayers, mipLevels)); return ImageHandle(id, [&](uint64_t id) { destroyImageById(id); }); } @@ -178,6 +207,7 @@ namespace vkcv { const uint64_t id = handle.getId(); if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); return nullptr; } @@ -190,6 +220,7 @@ namespace vkcv { const uint64_t id = handle.getId(); if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); return nullptr; } @@ -202,6 +233,7 @@ namespace vkcv { const uint64_t id = handle.getId(); if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); return nullptr; } @@ -210,84 +242,53 @@ namespace vkcv { return image.m_view; } - void ImageManager::switchImageLayout(const ImageHandle& handle, vk::ImageLayout oldLayout, vk::ImageLayout newLayout) { + void ImageManager::switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout) { const uint64_t id = handle.getId(); if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); 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 - ); + const auto transitionBarrier = createImageLayoutTransitionBarrier(image, newLayout); SubmitInfo submitInfo; submitInfo.queueType = QueueType::Graphics; - m_core->submitCommands( + m_core->recordAndSubmitCommands( submitInfo, - [sourceStage, destinationStage, imageMemoryBarrier](const vk::CommandBuffer& commandBuffer) { - commandBuffer.pipelineBarrier( - sourceStage, - destinationStage, - {}, - nullptr, - nullptr, - imageMemoryBarrier + [transitionBarrier](const vk::CommandBuffer& commandBuffer) { + // TODO: precise PipelineStageFlagBits, will require a lot of context + commandBuffer.pipelineBarrier( + vk::PipelineStageFlagBits::eTopOfPipe, + vk::PipelineStageFlagBits::eBottomOfPipe, + {}, + nullptr, + nullptr, + transitionBarrier ); }, - nullptr - ); + nullptr); + image.m_layout = newLayout; + } + + void ImageManager::recordImageLayoutTransition( + const ImageHandle& handle, + vk::ImageLayout newLayout, + vk::CommandBuffer cmdBuffer) { + + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); + return; + } + + auto& image = m_images[id]; + const auto transitionBarrier = createImageLayoutTransitionBarrier(image, newLayout); + recordImageBarrier(cmdBuffer, transitionBarrier); + image.m_layout = newLayout; } void ImageManager::fillImage(const ImageHandle& handle, void* data, size_t size) @@ -295,16 +296,15 @@ namespace vkcv { const uint64_t id = handle.getId(); if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); return; } auto& image = m_images[id]; - switchImageLayout( + switchImageLayoutImmediate( handle, - vk::ImageLayout::eUndefined, - vk::ImageLayout::eTransferDstOptimal - ); + vk::ImageLayout::eTransferDstOptimal); uint32_t channels = 4; // TODO: check image.m_format const size_t image_size = ( @@ -324,7 +324,7 @@ namespace vkcv { SubmitInfo submitInfo; submitInfo.queueType = QueueType::Transfer; - m_core->submitCommands( + m_core->recordAndSubmitCommands( submitInfo, [&image, &stagingBuffer](const vk::CommandBuffer& commandBuffer) { vk::ImageAspectFlags aspectFlags; @@ -358,9 +358,8 @@ namespace vkcv { ); }, [&]() { - switchImageLayout( + switchImageLayoutImmediate( handle, - vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal ); } @@ -371,6 +370,7 @@ namespace vkcv { const uint64_t id = handle.getId(); if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); return 0; } @@ -383,6 +383,7 @@ namespace vkcv { const uint64_t id = handle.getId(); if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); return 0; } @@ -395,6 +396,7 @@ namespace vkcv { const uint64_t id = handle.getId(); if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); return 0; } @@ -406,6 +408,7 @@ namespace vkcv { void ImageManager::destroyImageById(uint64_t id) { if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); return; } @@ -429,5 +432,16 @@ namespace vkcv { } } + vk::Format ImageManager::getImageFormat(const ImageHandle& handle) const { + + const uint64_t id = handle.getId(); + + if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); + return vk::Format::eUndefined; + } + + return m_images[id].m_format; + } } \ No newline at end of file diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp index f1bd9d0be774478f62098cbc2833b5c268be2eea..b9fccb25ec16bc1fd9569ab1a94627bd7ff06b18 100644 --- a/src/vkcv/ImageManager.hpp +++ b/src/vkcv/ImageManager.hpp @@ -11,23 +11,38 @@ #include "vkcv/Handles.hpp" namespace vkcv { - + class ImageManager { friend class Core; - private: + public: 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; + 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; + vk::ImageLayout m_layout = vk::ImageLayout::eUndefined; + private: + // struct is public so utility functions can access members, but only ImageManager can create Image + friend ImageManager; + Image( + vk::Image handle, + vk::DeviceMemory memory, + vk::ImageView view, + uint32_t width, + uint32_t height, + uint32_t depth, + vk::Format format, + uint32_t layers, + uint32_t levels); }; + private: Core* m_core; BufferManager& m_bufferManager; @@ -64,8 +79,13 @@ namespace vkcv { [[nodiscard]] vk::ImageView getVulkanImageView(const ImageHandle& handle) const; - - void switchImageLayout(const ImageHandle& handle, vk::ImageLayout oldLayout, vk::ImageLayout newLayout); + + void switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout); + void recordImageLayoutTransition( + const ImageHandle& handle, + vk::ImageLayout newLayout, + vk::CommandBuffer cmdBuffer); + void fillImage(const ImageHandle& handle, void* data, size_t size); [[nodiscard]] @@ -77,5 +97,7 @@ namespace vkcv { [[nodiscard]] uint32_t getImageDepth(const ImageHandle& handle) const; + [[nodiscard]] + vk::Format getImageFormat(const ImageHandle& handle) const; }; } \ No newline at end of file diff --git a/src/vkcv/PassConfig.cpp b/src/vkcv/PassConfig.cpp index ef07d3ee8d6170ae893cd055eefcc971cd1b87a3..602f1d3e2a8100ebd9bbb83772312d3d659abe86 100644 --- a/src/vkcv/PassConfig.cpp +++ b/src/vkcv/PassConfig.cpp @@ -5,15 +5,9 @@ namespace vkcv { AttachmentDescription::AttachmentDescription( - AttachmentLayout initial, - AttachmentLayout in_pass, - AttachmentLayout final, AttachmentOperation store_op, AttachmentOperation load_op, vk::Format format) noexcept : - layout_initial{initial}, - layout_in_pass{in_pass}, - layout_final{final}, store_operation{store_op}, load_operation{load_op}, format(format) diff --git a/src/vkcv/PassManager.cpp b/src/vkcv/PassManager.cpp index 8b59495ae7eaa68b6f42ed2a4786ae688e4a6375..c34b0d3631c48561f42eb7f21ba5578156910f51 100644 --- a/src/vkcv/PassManager.cpp +++ b/src/vkcv/PassManager.cpp @@ -1,4 +1,5 @@ #include "PassManager.hpp" +#include "vkcv/Image.hpp" namespace vkcv { @@ -73,63 +74,63 @@ namespace vkcv for (uint32_t i = 0; i < config.attachments.size(); i++) { // TODO: Renderpass struct should hold proper format information - vk::Format format = config.attachments[i].format; + vk::Format format = config.attachments[i].format; + vk::ImageLayout layout; - if (config.attachments[i].layout_in_pass == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) + if (isDepthFormat(config.attachments[i].format)) { + layout = vk::ImageLayout::eDepthStencilAttachmentOptimal; depthAttachmentReference.attachment = i; - depthAttachmentReference.layout = getVkLayoutFromAttachLayout(config.attachments[i].layout_in_pass); - pDepthAttachment = &depthAttachmentReference; + depthAttachmentReference.layout = layout; + pDepthAttachment = &depthAttachmentReference; } else { - vk::AttachmentReference attachmentRef(i, getVkLayoutFromAttachLayout(config.attachments[i].layout_in_pass)); + layout = vk::ImageLayout::eColorAttachmentOptimal; + vk::AttachmentReference attachmentRef(i, layout); 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) - ); - + {}, + format, + vk::SampleCountFlagBits::e1, + getVKLoadOpFromAttachOp(config.attachments[i].load_operation), + getVkStoreOpFromAttachOp(config.attachments[i].store_operation), + vk::AttachmentLoadOp::eDontCare, + vk::AttachmentStoreOp::eDontCare, + layout, + layout); + attachmentDescriptions.push_back(attachmentDesc); } const vk::SubpassDescription subpassDescription( - {}, - vk::PipelineBindPoint::eGraphics, - 0, - {}, - static_cast<uint32_t>(colorAttachmentReferences.size()), - colorAttachmentReferences.data(), - {}, - pDepthAttachment, - 0, - {} - ); + {}, + vk::PipelineBindPoint::eGraphics, + 0, + {}, + static_cast<uint32_t>(colorAttachmentReferences.size()), + colorAttachmentReferences.data(), + {}, + pDepthAttachment, + 0, + {}); const vk::RenderPassCreateInfo passInfo( - {}, - static_cast<uint32_t>(attachmentDescriptions.size()), - attachmentDescriptions.data(), - 1, - &subpassDescription, - 0, - {} - ); + {}, + static_cast<uint32_t>(attachmentDescriptions.size()), + attachmentDescriptions.data(), + 1, + &subpassDescription, + 0, + {}); vk::RenderPass renderPass = m_Device.createRenderPass(passInfo); - + const uint64_t id = m_Passes.size(); - m_Passes.push_back({ renderPass, config }); - return PassHandle(id, [&](uint64_t id) { destroyPassById(id); }); + m_Passes.push_back({ renderPass, config }); + return PassHandle(id, [&](uint64_t id) { destroyPassById(id); }); } vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const diff --git a/src/vkcv/PipelineConfig.cpp b/src/vkcv/PipelineConfig.cpp index d317258470bde76e8b8ba8e1f9bc684ea469b6c0..3bd2a68cb86f167afecc551dbd664dee8a63eb08 100644 --- a/src/vkcv/PipelineConfig.cpp +++ b/src/vkcv/PipelineConfig.cpp @@ -9,18 +9,20 @@ namespace vkcv { PipelineConfig::PipelineConfig( - const ShaderProgram& shaderProgram, - uint32_t width, - uint32_t height, - PassHandle &passHandle, - const std::vector<VertexAttribute> &vertexAttributes, - const std::vector<vk::DescriptorSetLayout> &descriptorLayouts) + const ShaderProgram& shaderProgram, + uint32_t width, + uint32_t height, + const PassHandle &passHandle, + const VertexLayout &vertexLayout, + const std::vector<vk::DescriptorSetLayout> &descriptorLayouts, + bool useDynamicViewport) : m_ShaderProgram(shaderProgram), m_Height(height), m_Width(width), m_PassHandle(passHandle), - m_vertexAttributes(vertexAttributes), - m_descriptorLayouts(descriptorLayouts) + m_VertexLayout(vertexLayout), + m_DescriptorLayouts(descriptorLayouts), + m_UseDynamicViewport(useDynamicViewport) {} } diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp index 16b08b7a5127769a19b7e0abe47b61f58406bafe..81b7525b160374915b1918c30870b05e619a30a4 100644 --- a/src/vkcv/PipelineManager.cpp +++ b/src/vkcv/PipelineManager.cpp @@ -1,4 +1,6 @@ #include "PipelineManager.hpp" +#include "vkcv/Image.hpp" +#include "vkcv/Logger.hpp" namespace vkcv { @@ -17,17 +19,27 @@ namespace vkcv } // currently assuming default 32 bit formats, no lower precision or normalized variants supported - vk::Format vertexFormatToVulkanFormat(const VertexFormat format) { + vk::Format vertexFormatToVulkanFormat(const VertexAttachmentFormat 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; + case VertexAttachmentFormat::FLOAT: + return vk::Format::eR32Sfloat; + case VertexAttachmentFormat::FLOAT2: + return vk::Format::eR32G32Sfloat; + case VertexAttachmentFormat::FLOAT3: + return vk::Format::eR32G32B32Sfloat; + case VertexAttachmentFormat::FLOAT4: + return vk::Format::eR32G32B32A32Sfloat; + case VertexAttachmentFormat::INT: + return vk::Format::eR32Sint; + case VertexAttachmentFormat::INT2: + return vk::Format::eR32G32Sint; + case VertexAttachmentFormat::INT3: + return vk::Format::eR32G32B32Sint; + case VertexAttachmentFormat::INT4: + return vk::Format::eR32G32B32A32Sint; + default: + vkcv_log(LogLevel::WARNING, "Unknown vertex format"); + return vk::Format::eUndefined; } } @@ -39,7 +51,7 @@ namespace vkcv const bool existsFragmentShader = config.m_ShaderProgram.existsShader(ShaderStage::FRAGMENT); if (!(existsVertexShader && existsFragmentShader)) { - std::cout << "Core::createGraphicsPipeline requires vertex and fragment shader code" << std::endl; + vkcv_log(LogLevel::ERROR, "Requires vertex and fragment shader code"); return PipelineHandle(); } @@ -82,24 +94,24 @@ namespace vkcv 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; + const VertexLayout &layout = config.m_VertexLayout; - 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.emplace_back(location, binding, vertexFormatToVulkanFormat(attachment.format), 0); - vertexBindingDescriptions.emplace_back(vk::VertexInputBindingDescription( - binding, - attribute.stride + getFormatSize(attachment.format), - vk::VertexInputRate::eVertex)); + // iterate over the layout's specified, mutually exclusive buffer bindings that make up a vertex buffer + for (const auto &vertexBinding : layout.vertexBindings) + { + vertexBindingDescriptions.emplace_back(vertexBinding.bindingLocation, + vertexBinding.stride, + vk::VertexInputRate::eVertex); + + // iterate over the bindings' specified, mutually exclusive vertex input attachments that make up a vertex + for(const auto &vertexAttachment: vertexBinding.vertexAttachments) + { + vertexAttributeDescriptions.emplace_back(vertexAttachment.inputLocation, + vertexBinding.bindingLocation, + vertexFormatToVulkanFormat(vertexAttachment.format), + vertexAttachment.offset % vertexBinding.stride); + + } } // Handover Containers to PipelineVertexInputStateCreateIngo Struct @@ -173,14 +185,15 @@ namespace vkcv { 1.f,1.f,1.f,1.f } ); - const size_t matrixPushConstantSize = 4 * 4 * sizeof(float); + const size_t matrixPushConstantSize = config.m_ShaderProgram.getPushConstantSize(); const vk::PushConstantRange pushConstantRange(vk::ShaderStageFlagBits::eAll, 0, matrixPushConstantSize); // pipeline layout vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo( {}, - (config.m_descriptorLayouts), + (config.m_DescriptorLayouts), (pushConstantRange)); + vk::PipelineLayout vkPipelineLayout{}; if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess) { @@ -207,14 +220,14 @@ namespace vkcv const PassConfig& passConfig = passManager.getPassConfig(config.m_PassHandle); for (const auto& attachment : passConfig.attachments) { - if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { + if (isDepthFormat(attachment.format)) { p_depthStencilCreateInfo = &depthStencilCreateInfo; break; } } std::vector<vk::DynamicState> dynamicStates = {}; - if(config.m_Width == UINT32_MAX && config.m_Height == UINT32_MAX) + if(config.m_UseDynamicViewport) { dynamicStates.push_back(vk::DynamicState::eViewport); dynamicStates.push_back(vk::DynamicState::eScissor); @@ -313,4 +326,65 @@ namespace vkcv return m_Configs.at(id); } + PipelineHandle PipelineManager::createComputePipeline( + const ShaderProgram &shaderProgram, + const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts) { + + // Temporally handing over the Shader Program instead of a pipeline config + vk::ShaderModule computeModule{}; + if (createShaderModule(computeModule, shaderProgram, ShaderStage::COMPUTE) != vk::Result::eSuccess) + return PipelineHandle(); + + vk::PipelineShaderStageCreateInfo pipelineComputeShaderStageInfo( + {}, + vk::ShaderStageFlagBits::eCompute, + computeModule, + "main", + nullptr + ); + + vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo({}, descriptorSetLayouts); + + const size_t pushConstantSize = shaderProgram.getPushConstantSize(); + vk::PushConstantRange pushConstantRange(vk::ShaderStageFlagBits::eCompute, 0, pushConstantSize); + if (pushConstantSize > 0) { + pipelineLayoutCreateInfo.setPushConstantRangeCount(1); + pipelineLayoutCreateInfo.setPPushConstantRanges(&pushConstantRange); + } + + vk::PipelineLayout vkPipelineLayout{}; + if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess) + { + m_Device.destroy(computeModule); + return PipelineHandle(); + } + + vk::ComputePipelineCreateInfo computePipelineCreateInfo{}; + computePipelineCreateInfo.stage = pipelineComputeShaderStageInfo; + computePipelineCreateInfo.layout = vkPipelineLayout; + + vk::Pipeline vkPipeline; + if (m_Device.createComputePipelines(nullptr, 1, &computePipelineCreateInfo, nullptr, &vkPipeline)!= vk::Result::eSuccess) + { + m_Device.destroy(computeModule); + return PipelineHandle(); + } + + m_Device.destroy(computeModule); + + const uint64_t id = m_Pipelines.size(); + m_Pipelines.push_back({ vkPipeline, vkPipelineLayout }); + + return PipelineHandle(id, [&](uint64_t id) { destroyPipelineById(id); }); + } + + // There is an issue for refactoring the Pipeline Manager. + // While including Compute Pipeline Creation, some private helper functions where introduced: + + vk::Result PipelineManager::createShaderModule(vk::ShaderModule &module, const ShaderProgram &shaderProgram, const ShaderStage stage) + { + std::vector<char> code = shaderProgram.getShader(stage).shaderCode; + vk::ShaderModuleCreateInfo moduleInfo({}, code.size(), reinterpret_cast<uint32_t*>(code.data())); + return m_Device.createShaderModule(&moduleInfo, nullptr, &module); + } } \ No newline at end of file diff --git a/src/vkcv/PipelineManager.hpp b/src/vkcv/PipelineManager.hpp index e243151f7248c07fa0287bb2eaf698e5080f7f61..634f5f4e6464532306e35fd10d9a1623df6ace16 100644 --- a/src/vkcv/PipelineManager.hpp +++ b/src/vkcv/PipelineManager.hpp @@ -21,7 +21,9 @@ namespace vkcv std::vector<PipelineConfig> m_Configs; void destroyPipelineById(uint64_t id); - + + vk::Result createShaderModule(vk::ShaderModule &module, const ShaderProgram &shaderProgram, ShaderStage stage); + public: PipelineManager() = delete; // no default ctor explicit PipelineManager(vk::Device device) noexcept; // ctor @@ -35,6 +37,10 @@ namespace vkcv PipelineHandle createPipeline(const PipelineConfig &config, PassManager& passManager); + PipelineHandle createComputePipeline( + const ShaderProgram& shaderProgram, + const std::vector<vk::DescriptorSetLayout>& descriptorSetLayouts); + [[nodiscard]] vk::Pipeline getVkPipeline(const PipelineHandle &handle) const; diff --git a/src/vkcv/QueueManager.cpp b/src/vkcv/QueueManager.cpp index c062437553c4c49d72f6d9a4f1160da2e5d41884..df6c74cccf6c4652adc6a4c78802f282ea6ae293 100644 --- a/src/vkcv/QueueManager.cpp +++ b/src/vkcv/QueueManager.cpp @@ -1,10 +1,10 @@ #include <limits> #include <unordered_set> +#include <iostream> #include "vkcv/QueueManager.hpp" - -#include "vkcv/QueueManager.hpp" +#include "vkcv/Logger.hpp" namespace vkcv { @@ -89,7 +89,14 @@ namespace vkcv { } } if (!found) { - throw std::runtime_error("Too many graphics queues were requested than being available!"); + for (int i = 0; i < queueFamilyStatus.size() && !found; i++) { + if (initialQueueFamilyStatus[i][0] > 0) { + queuePairsGraphics.push_back(std::pair(i, 0)); + found = true; + } + } + + vkcv_log(LogLevel::WARNING, "Not enough %s queues", vk::to_string(qFlag).c_str()); } break; case vk::QueueFlagBits::eCompute: @@ -104,7 +111,14 @@ namespace vkcv { } } if (!found) { - throw std::runtime_error("Too many compute queues were requested than being available!"); + for (int i = 0; i < queueFamilyStatus.size() && !found; i++) { + if (initialQueueFamilyStatus[i][1] > 0) { + queuePairsCompute.push_back(std::pair(i, 0)); + found = true; + } + } + + vkcv_log(LogLevel::WARNING, "Not enough %s queues", vk::to_string(qFlag).c_str()); } break; case vk::QueueFlagBits::eTransfer: @@ -119,7 +133,14 @@ namespace vkcv { } } if (!found) { - throw std::runtime_error("Too many transfer queues were requested than being available!"); + for (int i = 0; i < queueFamilyStatus.size() && !found; i++) { + if (initialQueueFamilyStatus[i][2] > 0) { + queuePairsTransfer.push_back(std::pair(i, 0)); + found = true; + } + } + + vkcv_log(LogLevel::WARNING, "Not enough %s queues", vk::to_string(qFlag).c_str()); } break; default: diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp index 5185b8b402eae5cd514689ba51a06e1a437271bf..aa945bb18e7cf04513b41510f1ea993a02e1f46d 100644 --- a/src/vkcv/ShaderProgram.cpp +++ b/src/vkcv/ShaderProgram.cpp @@ -5,6 +5,7 @@ */ #include "vkcv/ShaderProgram.hpp" +#include "vkcv/Logger.hpp" namespace vkcv { /** @@ -17,7 +18,7 @@ namespace vkcv { { std::ifstream file(shaderPath.string(), std::ios::ate | std::ios::binary); if (!file.is_open()) { - std::cout << "The file could not be opened." << std::endl; + vkcv_log(LogLevel::ERROR, "The file could not be opened"); return std::vector<char>{}; } size_t fileSize = (size_t)file.tellg(); @@ -27,18 +28,18 @@ namespace vkcv { return buffer; } - VertexFormat convertFormat(spirv_cross::SPIRType::BaseType basetype, uint32_t vecsize){ + VertexAttachmentFormat convertFormat(spirv_cross::SPIRType::BaseType basetype, uint32_t vecsize){ switch (basetype) { case spirv_cross::SPIRType::Int: switch (vecsize) { case 1: - return VertexFormat::INT; + return VertexAttachmentFormat::INT; case 2: - return VertexFormat::INT2; + return VertexAttachmentFormat::INT2; case 3: - return VertexFormat::INT3; + return VertexAttachmentFormat::INT3; case 4: - return VertexFormat::INT4; + return VertexAttachmentFormat::INT4; default: break; } @@ -46,13 +47,13 @@ namespace vkcv { case spirv_cross::SPIRType::Float: switch (vecsize) { case 1: - return VertexFormat::FLOAT; + return VertexAttachmentFormat::FLOAT; case 2: - return VertexFormat::FLOAT2; + return VertexAttachmentFormat::FLOAT2; case 3: - return VertexFormat::FLOAT3; + return VertexAttachmentFormat::FLOAT3; case 4: - return VertexFormat::FLOAT4; + return VertexAttachmentFormat::FLOAT4; default: break; } @@ -60,27 +61,31 @@ namespace vkcv { default: break; } - std::cout << "Shader Program Reflection: unknown Vertex Format" << std::endl; - return VertexFormat::FLOAT; + + vkcv_log(LogLevel::WARNING, "Unknown vertex format"); + return VertexAttachmentFormat::FLOAT; } ShaderProgram::ShaderProgram() noexcept : m_Shaders{}, - m_VertexLayout{} + m_VertexAttachments{}, + m_DescriptorSets{} {} bool ShaderProgram::addShader(ShaderStage shaderStage, const std::filesystem::path &shaderPath) { - if(m_Shaders.find(shaderStage) != m_Shaders.end()) - std::cout << "Found existing shader stage. Overwriting." << std::endl; + if(m_Shaders.find(shaderStage) != m_Shaders.end()) { + vkcv_log(LogLevel::WARNING, "Overwriting existing shader stage"); + } const std::vector<char> shaderCode = readShaderCode(shaderPath); - if (shaderCode.empty()) - return false; - else - { + + if (shaderCode.empty()) { + return false; + } else { Shader shader{shaderCode, shaderStage}; m_Shaders.insert(std::make_pair(shaderStage, shader)); + reflectShader(shaderStage); return true; } } @@ -103,34 +108,114 @@ namespace vkcv { auto shaderCodeChar = m_Shaders.at(shaderStage).shaderCode; std::vector<uint32_t> shaderCode; - for (uint32_t i = 0; i < shaderCodeChar.size()/4; i++) { + 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; + //reflect vertex input + if (shaderStage == ShaderStage::VERTEX) + { + // spirv-cross API (hopefully) returns the stage_inputs in order + for (uint32_t i = 0; i < resources.stage_inputs.size(); i++) + { + // spirv-cross specific objects + auto& stage_input = resources.stage_inputs[i]; + const spirv_cross::SPIRType& base_type = comp.get_type(stage_input.base_type_id); + + // vertex input location + const uint32_t attachment_loc = comp.get_decoration(stage_input.id, spv::DecorationLocation); + // vertex input name + const std::string attachment_name = stage_input.name; + // vertex input format (implies its size) + const VertexAttachmentFormat attachment_format = convertFormat(base_type.basetype, base_type.vecsize); + + m_VertexAttachments.emplace_back(attachment_loc, attachment_name, attachment_format); + } + } + + //reflect descriptor sets (uniform buffer, storage buffer, sampler, sampled image, storage image) + std::vector<std::pair<uint32_t, DescriptorBinding>> bindings; + int32_t maxSetID = -1; + for (uint32_t i = 0; i < resources.uniform_buffers.size(); i++) { + auto& u = resources.uniform_buffers[i]; + const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id); + std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet), + DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::UNIFORM_BUFFER, base_type.vecsize, shaderStage)); + bindings.push_back(descriptor); + if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) + maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); + } - 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); + for (uint32_t i = 0; i < resources.storage_buffers.size(); i++) { + auto& u = resources.storage_buffers[i]; + const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id); + std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet), + DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::STORAGE_BUFFER, base_type.vecsize, shaderStage)); + bindings.push_back(descriptor); + if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) + maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); + } - 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; - } + for (uint32_t i = 0; i < resources.separate_samplers.size(); i++) { + auto& u = resources.separate_samplers[i]; + const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id); + std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet), + DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::SAMPLER, base_type.vecsize, shaderStage)); + bindings.push_back(descriptor); + if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) + maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); + } + + for (uint32_t i = 0; i < resources.separate_images.size(); i++) { + auto& u = resources.separate_images[i]; + const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id); + std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet), + DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::IMAGE_SAMPLED, base_type.vecsize, shaderStage)); + bindings.push_back(descriptor); + if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) + maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); + + } + + for (uint32_t i = 0; i < resources.storage_images.size(); i++) { + auto& u = resources.storage_images[i]; + const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id); + std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet), + DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::IMAGE_STORAGE, base_type.vecsize, shaderStage)); + bindings.push_back(descriptor); + if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) + maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); + } + if (maxSetID != -1) { + if((int32_t)m_DescriptorSets.size() <= maxSetID) m_DescriptorSets.resize(maxSetID + 1); + for (const auto &binding : bindings) { + m_DescriptorSets[binding.first].push_back(binding.second); + } + } - m_VertexLayout = VertexLayout(inputVec); + //reflect push constants + for (const auto &pushConstantBuffer : resources.push_constant_buffers) { + for (const auto &range : comp.get_active_buffer_ranges(pushConstantBuffer.id)) { + const size_t size = range.range + range.offset; + m_pushConstantSize = std::max(m_pushConstantSize, size); + } } } - const VertexLayout& ShaderProgram::getVertexLayout() const{ - return m_VertexLayout; + const std::vector<VertexAttachment> &ShaderProgram::getVertexAttachments() const + { + return m_VertexAttachments; + } + + const std::vector<std::vector<DescriptorBinding>> &ShaderProgram::getReflectedDescriptors() const + { + return m_DescriptorSets; + } + + size_t ShaderProgram::getPushConstantSize() const + { + return m_pushConstantSize; } } diff --git a/src/vkcv/SwapChain.cpp b/src/vkcv/SwapChain.cpp index 39c310de60db2c3678749f142c926a26ac127b58..b787536b66cfd802dfd435a773a584c875eeb391 100644 --- a/src/vkcv/SwapChain.cpp +++ b/src/vkcv/SwapChain.cpp @@ -244,7 +244,7 @@ namespace vkcv m_Extent = extent2D; } - void SwapChain::recreateSwapchain() { + void SwapChain::signalSwapchainRecreation() { m_RecreationRequired = true; } diff --git a/src/vkcv/VertexLayout.cpp b/src/vkcv/VertexLayout.cpp index b06c6743e1e19a5e282af248ab6b590eb97529fd..fa079a3264ae47b32461bda26485adb97b0be280 100644 --- a/src/vkcv/VertexLayout.cpp +++ b/src/vkcv/VertexLayout.cpp @@ -3,51 +3,60 @@ // #include "vkcv/VertexLayout.hpp" +#include "vkcv/Logger.hpp" namespace vkcv { - uint32_t getFormatSize(VertexFormat format) { + uint32_t getFormatSize(VertexAttachmentFormat format) { switch (format) { - case VertexFormat::FLOAT: + case VertexAttachmentFormat::FLOAT: return 4; - case VertexFormat::FLOAT2: + case VertexAttachmentFormat::FLOAT2: return 8; - case VertexFormat::FLOAT3: + case VertexAttachmentFormat::FLOAT3: return 12; - case VertexFormat::FLOAT4: + case VertexAttachmentFormat::FLOAT4: return 16; - case VertexFormat::INT: + case VertexAttachmentFormat::INT: return 4; - case VertexFormat::INT2: + case VertexAttachmentFormat::INT2: return 8; - case VertexFormat::INT3: + case VertexAttachmentFormat::INT3: return 12; - case VertexFormat::INT4: + case VertexAttachmentFormat::INT4: return 16; default: - break; + vkcv_log(LogLevel::WARNING, "No format given"); + return 0; } - 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}, + VertexAttachment::VertexAttachment(uint32_t inputLocation, const std::string &name, VertexAttachmentFormat format) noexcept: + inputLocation{inputLocation}, + name{name}, format{format}, - offset{offset} - {} - - VertexLayout::VertexLayout() noexcept : - stride{0}, - attachmentMap() + offset{0} {} - 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); + + VertexBinding::VertexBinding(uint32_t bindingLocation, const std::vector<VertexAttachment> &attachments) noexcept : + bindingLocation{bindingLocation}, + stride{0}, + vertexAttachments{attachments} + { + uint32_t offset = 0; + for (auto &attachment : vertexAttachments) + { + offset += getFormatSize(attachment.format); + attachment.offset = offset; } + stride = offset; } + VertexLayout::VertexLayout() noexcept : + vertexBindings{} + {} + + VertexLayout::VertexLayout(const std::vector<VertexBinding> &bindings) noexcept : + vertexBindings{bindings} + {} } \ No newline at end of file