diff --git a/.gitignore b/.gitignore index d2bf98a016f588760241f9dc7f90f6197c458404..7ee4ff1903e902c4715c6e2b0c3e784ed5755aaf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,3 @@ -<<<<<<< HEAD -======= - ->>>>>>> develop # IDE specific files .project .cproject @@ -19,3 +15,6 @@ cmake-build-release/ *.exe *.ilk *.pdb + +# GUI configuration files +imgui.ini diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 318f3e931e557421a5e9275735174cdee7947453..33b70018e368ecc3ad019ea33e57485814eb233a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,7 +14,7 @@ build_ubuntu_gcc: - $RUN =~ /\bubuntu.*/i || $RUN =~ /\ball.*/i stage: build tags: - - ubuntu-gcc + - ubuntu-gcc-cached variables: GIT_SUBMODULE_STRATEGY: recursive timeout: 10m @@ -37,11 +37,11 @@ build_win10_msvc: - $RUN =~ /\bwin.*/i || $RUN =~ /\ball.*/i stage: build tags: - - win10-msvc + - win10-msvc-cached variables: GIT_SUBMODULE_STRATEGY: recursive timeout: 10m - retry: 1 + retry: 0 script: - cd 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\' - .\Launch-VsDevShell.ps1 diff --git a/.gitmodules b/.gitmodules index 983b753744e8767da0ec3c959c32a3766ee346f6..e0aaf2d17c340f98ae875f7e0f1238bfe04f7e5d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,4 +15,10 @@ url = https://github.com/nothings/stb.git [submodule "modules/camera/lib/glm"] path = modules/camera/lib/glm - url = https://github.com/g-truc/glm.git \ No newline at end of file + url = https://github.com/g-truc/glm.git +[submodule "modules/shader_compiler/lib/glslang"] + path = modules/shader_compiler/lib/glslang + url = https://github.com/KhronosGroup/glslang.git +[submodule "modules/gui/lib/imgui"] + path = modules/gui/lib/imgui + url = https://github.com/ocornut/imgui.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a7f978f81313ecc3a657dd670d2a16db3cd4e8d..2ae078a428a8e5e640ed8dc7bcc2f4e58e159c6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,24 +41,24 @@ if (vkcv_build_debug) endif() endif() +# configure everything to use the required dependencies +include(${vkcv_config}/Libraries.cmake) + +# set the compile definitions aka preprocessor variables +add_compile_definitions(${vkcv_definitions}) + # add modules as targets add_subdirectory(modules) # add source files for compilation include(${vkcv_config}/Sources.cmake) -# configure everything to use the required dependencies -include(${vkcv_config}/Libraries.cmake) - message("-- Libraries: [ ${vkcv_libraries} ]") message("-- Flags: [ ${vkcv_flags} ]") # set the compiler flags for the framework set(CMAKE_CXX_FLAGS ${vkcv_flags}) -# set the compile definitions aka preprocessor variables -add_compile_definitions(${vkcv_definitions}) - # create VkCV framework as static library using all source files add_library(vkcv STATIC ${vkcv_sources}) diff --git a/Doxyfile b/Doxyfile index be7d96f6cce1ed0ec753dafeaccb6314b9f6e04f..a657ede81c43b3cae1fc5c17f071aa7dc65add04 100644 --- a/Doxyfile +++ b/Doxyfile @@ -865,7 +865,8 @@ WARN_LOGFILE = # Note: If this tag is empty the current directory is searched. INPUT = src \ - include + include \ + modules # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -953,7 +954,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = lib # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -969,7 +970,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = */lib/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the diff --git a/config/Libraries.cmake b/config/Libraries.cmake index e04aa3575a34632eb75c929bf4640305cd93e298..ec014f84c820abf4988b070d5b733be08c377319 100644 --- a/config/Libraries.cmake +++ b/config/Libraries.cmake @@ -10,6 +10,8 @@ if(NOT WIN32) list(APPEND vkcv_flags -fopenmp) endif() +list(APPEND vkcv_definitions _USE_MATH_DEFINES) + # some formatted printing set(vkcv_config_msg " - Library: ") diff --git a/config/Sources.cmake b/config/Sources.cmake index 8fc0aa9a2605a629596e26d5eeb0772164e6ec7a..4397e4978eb022d267571d185a1f122d053a5ea1 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -29,15 +29,18 @@ 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/Swapchain.hpp + ${vkcv_source}/vkcv/Swapchain.cpp + + ${vkcv_include}/vkcv/ShaderStage.hpp + ${vkcv_include}/vkcv/ShaderProgram.hpp ${vkcv_source}/vkcv/ShaderProgram.cpp ${vkcv_include}/vkcv/PipelineConfig.hpp - ${vkcv_source}/vkcv/PipelineConfig.cpp ${vkcv_source}/vkcv/PipelineManager.hpp ${vkcv_source}/vkcv/PipelineManager.cpp @@ -50,10 +53,7 @@ set(vkcv_sources ${vkcv_include}/vkcv/QueueManager.hpp ${vkcv_source}/vkcv/QueueManager.cpp - - ${vkcv_source}/vkcv/Surface.hpp - ${vkcv_source}/vkcv/Surface.cpp - + ${vkcv_source}/vkcv/ImageLayoutTransitions.hpp ${vkcv_source}/vkcv/ImageLayoutTransitions.cpp @@ -72,4 +72,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/BufferManager.hpp b/include/vkcv/BufferManager.hpp index 322873b348dab7c45d446b5053d61379dfde0b6b..9eb80d70862a79a01593e6fe4c3aabe98d253ac8 100644 --- a/include/vkcv/BufferManager.hpp +++ b/include/vkcv/BufferManager.hpp @@ -44,6 +44,14 @@ namespace vkcv void init(); + /** + * Destroys and deallocates buffer represented by a given + * buffer handle id. + * + * @param id Buffer handle id + */ + void destroyBufferById(uint64_t id); + public: ~BufferManager() noexcept; @@ -123,15 +131,10 @@ namespace vkcv * @param handle Buffer handle */ void unmapBuffer(const BufferHandle& handle); - - /** - * Destroys and deallocates buffer represented by a given - * buffer handle. - * - * @param handle Buffer handle - */ - void destroyBuffer(const BufferHandle& handle); + void recordBufferMemoryBarrier( + const BufferHandle& handle, + vk::CommandBuffer cmdBuffer); }; } 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 cf75b8e754513da774ec1841c8c4cc700b99ee2d..493534f77a0f930cbd0d292a504300f51ebc0f76 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -8,7 +8,7 @@ #include <vulkan/vulkan.hpp> #include "vkcv/Context.hpp" -#include "vkcv/SwapChain.hpp" +#include "vkcv/Swapchain.hpp" #include "vkcv/Window.hpp" #include "vkcv/PassConfig.hpp" #include "vkcv/Handles.hpp" @@ -21,13 +21,12 @@ #include "vkcv/DescriptorConfig.hpp" #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; @@ -36,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 std::function<void(const vk::CommandBuffer& cmdBuffer)> RecordCommandFunction; - typedef std::function<void(void)> FinishCommandFunction; class Core final { @@ -55,38 +52,33 @@ namespace vkcv * * @param context encapsulates various Vulkan objects */ - Core(Context &&context, Window &window, SwapChain swapChain, std::vector<vk::ImageView> imageViews, + Core(Context &&context, Window &window, const Swapchain& swapChain, std::vector<vk::ImageView> imageViews, const CommandResources& commandResources, const SyncResources& syncResources) noexcept; // explicit destruction of default constructor Core() = delete; Result acquireSwapchainImage(); - void destroyTemporaryFramebuffers(); Context m_Context; - SwapChain m_swapchain; - std::vector<vk::ImageView> m_swapchainImageViews; - const Window& m_window; + Swapchain m_swapchain; + 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; + 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; - CommandResources m_CommandResources; - SyncResources m_SyncResources; - uint32_t m_currentSwapchainImageIndex; - std::vector<vk::Framebuffer> m_TemporaryFramebuffers; + CommandResources m_CommandResources; + SyncResources m_SyncResources; + uint32_t m_currentSwapchainImageIndex; - /** - * recreates the swapchain - * @param[in] width new window width - * @param[in] height new window hight - */ - static void recreateSwapchain(int width, int height); + event_handle<int,int> e_resizeHandle; + + static std::vector<vk::ImageView> createSwapchainImageViews( Context &context, Swapchain& swapChain); public: /** @@ -126,6 +118,9 @@ namespace vkcv [[nodiscard]] const Context &getContext() const; + + [[nodiscard]] + const Swapchain& getSwapchain() const; /** * Creates a #Core with given @p applicationName and @p applicationVersion for your application. @@ -160,6 +155,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. @@ -206,46 +214,55 @@ namespace vkcv * @return Image-Object */ [[nodiscard]] - Image createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth = 1); + Image createImage( + vk::Format format, + uint32_t width, + uint32_t height, + uint32_t depth = 1, + bool createMipChain = false, + bool supportStorage = false, + bool supportColorAttachment = false); + + [[nodiscard]] + const uint32_t getImageWidth(ImageHandle imageHandle); + [[nodiscard]] + const uint32_t getImageHeight(ImageHandle imageHandle); /** TODO: * @param setDescriptions * @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 getDescritorSetLayout(ResourcesHandle handle, size_t setIndex); + DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const; /** * @brief start recording command buffers and increment frame index */ - void beginFrame(); + bool beginFrame(uint32_t& width, uint32_t& height); - /** - * @brief render a beautiful triangle - */ - void renderMesh( - const PassHandle renderpassHandle, - const PipelineHandle pipelineHandle, - const int width, - const int height, - const size_t pushConstantSize, - const void* pushConstantData, - const std::vector<VertexBufferBinding> &vertexBufferBindings, - const BufferHandle indexBuffer, - const size_t indexCount, - const vkcv::ResourcesHandle resourceHandle, - const size_t resourceDescriptorSetIndex); + 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 */ void endFrame(); - vk::Format getSwapchainImageFormat(); - /** * Submit a command buffer to any queue of selected type. The recording can be customized by a * custom record-command-function. If the command submission has finished, an optional finish-function @@ -255,6 +272,26 @@ 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 recordAndSubmitCommandsImmediate( + 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); + void prepareImageForStorage(const CommandStreamHandle cmdStream, const ImageHandle image); + void recordImageMemoryBarrier(const CommandStreamHandle cmdStream, const ImageHandle image); + void recordBufferMemoryBarrier(const CommandStreamHandle cmdStream, const BufferHandle buffer); + + vk::ImageView getSwapchainImageView() const; + }; } 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/DescriptorWrites.hpp b/include/vkcv/DescriptorWrites.hpp index d67e8e3233e184b207d109e652adeca43407d7e0..f28a6c91e189b13413ffefec0f05e5a0a358ee26 100644 --- a/include/vkcv/DescriptorWrites.hpp +++ b/include/vkcv/DescriptorWrites.hpp @@ -4,15 +4,20 @@ namespace vkcv { struct SampledImageDescriptorWrite { - inline SampledImageDescriptorWrite(uint32_t binding, ImageHandle image) : binding(binding), image(image) {}; + inline SampledImageDescriptorWrite(uint32_t binding, ImageHandle image, uint32_t mipLevel = 0, bool useGeneralLayout = false) + : binding(binding), image(image), mipLevel(mipLevel), useGeneralLayout(useGeneralLayout) {}; uint32_t binding; ImageHandle image; + uint32_t mipLevel; + bool useGeneralLayout; }; struct StorageImageDescriptorWrite { - inline StorageImageDescriptorWrite(uint32_t binding, ImageHandle image) : binding(binding), image(image) {}; + inline StorageImageDescriptorWrite(uint32_t binding, ImageHandle image, uint32_t mipLevel = 0) + : binding(binding), image(image), mipLevel(mipLevel) {}; uint32_t binding; ImageHandle image; + uint32_t mipLevel; }; struct UniformBufferDescriptorWrite { @@ -36,8 +41,8 @@ namespace vkcv { struct DescriptorWrites { std::vector<SampledImageDescriptorWrite> sampledImageWrites; std::vector<StorageImageDescriptorWrite> storageImageWrites; - std::vector<UniformBufferDescriptorWrite> uniformBufferWrites; - std::vector<StorageBufferDescriptorWrite> storageBufferWrites; - std::vector<SamplerDescriptorWrite> samplerWrites; + std::vector<UniformBufferDescriptorWrite> uniformBufferWrites; + std::vector<StorageBufferDescriptorWrite> storageBufferWrites; + std::vector<SamplerDescriptorWrite> samplerWrites; }; } \ No newline at end of file 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/Event.hpp b/include/vkcv/Event.hpp index 0836e836e84ff7dfc4931a7cedd65497bf9a89cf..da5cbc72fbb3eee3a71a35c1da6fe32dff06b057 100644 --- a/include/vkcv/Event.hpp +++ b/include/vkcv/Event.hpp @@ -1,12 +1,21 @@ #pragma once #include <functional> +#include <mutex> namespace vkcv { + + template<typename... T> + struct event_handle { + uint32_t id; + }; template<typename... T> struct event_function { typedef std::function<void(T...)> type; + + event_handle<T...> handle; + type callback; }; /** @@ -16,7 +25,9 @@ namespace vkcv { template<typename... T> struct event { private: - std::vector<typename event_function<T...>::type> m_handles; + std::vector< event_function<T...> > m_functions; + uint32_t m_id_counter; + std::mutex m_mutex; public: @@ -25,32 +36,60 @@ namespace vkcv { * @param arguments of the given function */ void operator()(T... arguments) { - for (auto &handle : this->m_handles) { - handle(arguments...); - } + lock(); + + for (auto &function : this->m_functions) { + function.callback(arguments...); + } + + unlock(); } /** * adds a function handle to the event to be called - * @param handle of the function + * @param callback of the function + * @return handle of the function */ - typename event_function<T...>::type add(typename event_function<T...>::type handle) { - this->m_handles.push_back(handle); - return handle; + event_handle<T...> add(typename event_function<T...>::type callback) { + event_function<T...> function; + function.handle = { m_id_counter++ }; + function.callback = callback; + this->m_functions.push_back(function); + return function.handle; } /** * removes a function handle of the event * @param handle of the function */ - void remove(typename event_function<T...>::type handle) { - this->m_handles.erase( - remove(this->m_handles.begin(), this->m_handles.end(), handle), - this->m_handles.end() + void remove(event_handle<T...> handle) { + this->m_functions.erase( + std::remove_if(this->m_functions.begin(), this->m_functions.end(), [&handle](auto function){ + return (handle.id == function.handle.id); + }), + this->m_functions.end() ); } + + /** + * locks the event so its function handles won't be called + */ + void lock() { + m_mutex.lock(); + } + + /** + * unlocks the event so its function handles can be called after locking + */ + void unlock() { + m_mutex.unlock(); + } - event() = default; + explicit event(bool locked = false) { + if (locked) { + lock(); + } + } event(const event &other) = delete; diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp index 58f795f0c99a0cd1b05045f9f401a26c0aac1b88..ea05bd212dd9314957e4771070bedb3963b1b2dc 100644 --- a/include/vkcv/Handles.hpp +++ b/include/vkcv/Handles.hpp @@ -7,31 +7,51 @@ #include <iostream> +#include "Event.hpp" + namespace vkcv { + typedef typename event_function<uint64_t>::type HandleDestroyFunction; + class Handle { friend std::ostream& operator << (std::ostream& out, const Handle& handle); private: uint64_t m_id; + uint64_t* m_rc; + + HandleDestroyFunction m_destroy; protected: Handle(); - explicit Handle(uint64_t id); + explicit Handle(uint64_t id, const HandleDestroyFunction& destroy = nullptr); + /** + * Returns the actual handle id of a handle. + * + * @return Handle id + */ [[nodiscard]] uint64_t getId() const; + + /** + * Returns the reference counter of a handle + * + * @return Reference counter + */ + [[nodiscard]] + uint64_t getRC() const; public: - virtual ~Handle() = default; + virtual ~Handle(); - Handle(const Handle& other) = default; - Handle(Handle&& other) = default; + Handle(const Handle& other); + Handle(Handle&& other) noexcept; - Handle& operator=(const Handle& other) = default; - Handle& operator=(Handle&& other) = default; + Handle& operator=(const Handle& other); + Handle& operator=(Handle&& other) noexcept; explicit operator bool() const; bool operator!() const; @@ -59,7 +79,7 @@ namespace vkcv using Handle::Handle; }; - class ResourcesHandle : public Handle { + class DescriptorSetHandle : public Handle { friend class DescriptorManager; private: using Handle::Handle; @@ -73,8 +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 d56fac9c0e442faeff1898443d737df365c9ed45..eac96975886a92e0043bbb97cbe64f76943efa7e 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: @@ -25,28 +29,35 @@ namespace vkcv { [[nodiscard]] uint32_t getDepth() const; - - [[nodiscard]] - vk::ImageLayout getLayout() const; [[nodiscard]] vkcv::ImageHandle getHandle() const; - + + [[nodiscard]] + uint32_t getMipCount() const; + void switchLayout(vk::ImageLayout newLayout); void fill(void* data, size_t size = SIZE_MAX); + void generateMipChainImmediate(); + void recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream); private: - ImageManager* const m_manager; - const ImageHandle m_handle; - const vk::Format m_format; - const uint32_t m_width; - const uint32_t m_height; - const uint32_t m_depth; - vk::ImageLayout m_layout; - - Image(ImageManager* manager, const ImageHandle& handle, vk::Format format, uint32_t width, uint32_t height, uint32_t depth); + // TODO: const qualifier removed, very hacky!!! + // Else you cannot recreate an image. Pls fix. + ImageManager* m_manager; + ImageHandle m_handle; + + Image(ImageManager* manager, const ImageHandle& handle); - static Image create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth); + static Image create( + ImageManager* manager, + vk::Format format, + uint32_t width, + uint32_t height, + uint32_t depth, + uint32_t mipCount, + bool supportStorage, + bool supportColorAttachment); }; diff --git a/include/vkcv/Logger.hpp b/include/vkcv/Logger.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d484711f642506926b1281a830fb2c9caf8240a2 --- /dev/null +++ b/include/vkcv/Logger.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include <stdio.h> + +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 \ + ]; \ + snprintf( \ + output_message, \ + VKCV_DEBUG_MESSAGE_LEN, \ + __VA_ARGS__ \ + ); \ + 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..1e00c5209118469a7dc6ff1eb1e3c98a3c6903a7 100644 --- a/include/vkcv/PipelineConfig.hpp +++ b/include/vkcv/PipelineConfig.hpp @@ -7,36 +7,24 @@ #include <vector> #include <cstdint> -#include "vkcv/Handles.hpp" +#include "Handles.hpp" #include "ShaderProgram.hpp" -#include <vkcv/VertexLayout.hpp> +#include "VertexLayout.hpp" namespace vkcv { - struct PipelineConfig { - /** - * Constructor for the pipeline. Creates a pipeline using @p vertexCode, @p fragmentCode as well as the - * dimensions of the application window @p width and @p height. A handle for the Render Pass is also needed, @p passHandle. - * - * @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 - */ - PipelineConfig( - const ShaderProgram& shaderProgram, - uint32_t width, - uint32_t height, - PassHandle &passHandle, - const std::vector<VertexAttribute> &vertexAttributes, - const std::vector<vk::DescriptorSetLayout> &descriptorLayouts); + enum class PrimitiveTopology{PointList, LineList, TriangleList }; - 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; + struct PipelineConfig { + ShaderProgram m_ShaderProgram; + uint32_t m_Width; + uint32_t m_Height; + PassHandle m_PassHandle; + VertexLayout m_VertexLayout; + std::vector<vk::DescriptorSetLayout> m_DescriptorLayouts; + bool m_UseDynamicViewport; + bool m_UseConservativeRasterization = false; + PrimitiveTopology m_PrimitiveTopology = PrimitiveTopology::TriangleList; }; } \ No newline at end of file diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp index ef5d1f00ea3eeb97d97d8824439ded1ed326f33c..78b1f02169fe630427b9f66150e32078d42b7b3f 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 deleted file mode 100644 index 1087d6364f6f811b741904d4e2b31fcfeb450901..0000000000000000000000000000000000000000 --- a/include/vkcv/SwapChain.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -#include "vulkan/vulkan.hpp" -#include "Context.hpp" -#include "vkcv/Window.hpp" - -namespace vkcv { - class SwapChain final { - private: - - vk::SurfaceKHR m_surface; - vk::SwapchainKHR m_swapchain; - vk::SurfaceFormatKHR m_format; - - uint32_t m_ImageCount; - - /** - * Constructor of a SwapChain object - * glfw is not initialized in this class because ist must be sure that there exists a context first - * glfw is already initialized by the window class - * @param surface used by the swapchain - * @param swapchain to show images in the window - * @param format - */ - SwapChain(vk::SurfaceKHR surface, vk::SwapchainKHR swapchain, vk::SurfaceFormatKHR format, uint32_t imageCount); - - public: - SwapChain(const SwapChain &other) = default; - SwapChain(SwapChain &&other) = default; - - /** - * @return The swapchain linked with the #SwapChain class - * @note The reference to our Swapchain variable is needed for the recreation step - */ - [[nodiscard]] - const vk::SwapchainKHR& getSwapchain() const; - - /** - * gets the current surface object - * @return current surface - */ - [[nodiscard]] - vk::SurfaceKHR getSurface(); - /** - * gets the current surface format - * @return gets the surface format - */ - [[nodiscard]] - vk::SurfaceFormatKHR getSurfaceFormat(); - - /** - * creates a swap chain object out of the given window and the given context - * @param window a wrapper that represents a glfw window - * @param context of the application - * @return returns an object of swapChain - */ - static SwapChain create(const Window &window, const Context &context, const vk::SurfaceKHR surface); - - /** - * Destructor of SwapChain - */ - virtual ~SwapChain(); - - /** - * @return number of images in swapchain - */ - uint32_t getImageCount(); - }; - -} diff --git a/include/vkcv/Swapchain.hpp b/include/vkcv/Swapchain.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b75fc5a87156ea56061e41b4b0974928c83ffa28 --- /dev/null +++ b/include/vkcv/Swapchain.hpp @@ -0,0 +1,122 @@ +#pragma once +#include "vulkan/vulkan.hpp" +#include "Context.hpp" +#include "vkcv/Window.hpp" + +#include <atomic> + +namespace vkcv +{ + class Swapchain final { + private: + friend class Core; + + struct Surface + { + vk::SurfaceKHR handle; + std::vector<vk::SurfaceFormatKHR> formats; + vk::SurfaceCapabilitiesKHR capabilities; + std::vector<vk::PresentModeKHR> presentModes; + }; + + Surface m_Surface; + + vk::SwapchainKHR m_Swapchain; + vk::Format m_Format; + vk::ColorSpaceKHR m_ColorSpace; + vk::PresentModeKHR m_PresentMode; + uint32_t m_ImageCount; + + vk::Extent2D m_Extent; + + std::atomic<bool> m_RecreationRequired; + + /** + * Constructor of a SwapChain object + * glfw is not initialized in this class because ist must be sure that there exists a context first + * glfw is already initialized by the window class + * @param surface used by the swapchain + * @param swapchain to show images in the window + * @param format + */ + // TODO: + Swapchain(const Surface &surface, + vk::SwapchainKHR swapchain, + vk::Format format, + vk::ColorSpaceKHR colorSpace, + vk::PresentModeKHR presentMode, + uint32_t imageCount, + vk::Extent2D extent) noexcept; + + /** + * TODO + * + * @return + */ + bool shouldUpdateSwapchain() const; + + /** + * TODO + * + * context + * window + */ + void updateSwapchain(const Context &context, const Window &window); + + /** + * + */ + void signalSwapchainRecreation(); + + public: + Swapchain(const Swapchain& other); + + /** + * @return The swapchain linked with the #SwapChain class + * @note The reference to our Swapchain variable is needed for the recreation step + */ + [[nodiscard]] + const vk::SwapchainKHR& getSwapchain() const; + + /** + * gets the current surface object + * @return current surface + */ + [[nodiscard]] + vk::SurfaceKHR getSurface() const; + + /** + * gets the chosen swapchain format + * @return gets the chosen swapchain format + */ + [[nodiscard]] + vk::Format getFormat() const; + + /** + * creates a swap chain object out of the given window and the given context + * @param window a wrapper that represents a glfw window + * @param context of the application + * @return returns an object of swapChain + */ + static Swapchain create(const Window &window, const Context &context); + + /** + * Destructor of SwapChain + */ + virtual ~Swapchain(); + + /** + * @return number of images in swapchain + */ + uint32_t getImageCount() const; + + /** + * TODO + * + * @return + */ + [[nodiscard]] + const vk::Extent2D& getExtent() const; + + }; +} diff --git a/include/vkcv/VertexLayout.hpp b/include/vkcv/VertexLayout.hpp index ee0ad8ef56d5284af2be4c81b7ea2f0d052d5a6f..0600b99a24a327605e89b2e8ec304c20dbf7ad2e 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/include/vkcv/Window.hpp b/include/vkcv/Window.hpp index 7428c7c73eb481f7352821faed36257211dfd5bf..7dc6c1b7dc8fef4d5de7de5b0a9976bf714e6ac2 100644 --- a/include/vkcv/Window.hpp +++ b/include/vkcv/Window.hpp @@ -7,23 +7,24 @@ #define NOMINMAX #include <algorithm> + #include "Event.hpp" struct GLFWwindow; namespace vkcv { - class Window final { - private: - GLFWwindow *m_window; - - - /** + class Window { + protected: + GLFWwindow *m_window; + + /** * * @param GLFWwindow of the class */ - explicit Window(GLFWwindow *window); - + explicit Window(GLFWwindow *window); + + private: /** * mouse callback for moving the mouse on the screen * @param[in] window The window that received the event. @@ -40,6 +41,12 @@ namespace vkcv { */ static void onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods); + /** + * @brief A callback function for handling mouse scrolling events. + * @param[in] callbackWindow The window that received the event. + * @param[in] xoffset The extent of horizontal scrolling. + * @param[in] yoffset The extent of vertical scrolling. + */ static void onMouseScrollEvent(GLFWwindow *callbackWindow, double xoffset, double yoffset); /** @@ -59,6 +66,19 @@ namespace vkcv { * @param[in] mods Bit field describing which [modifier keys](@ref mods) were held down. */ static void onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods); + + /** + * char callback for any typed character + * @param[in] window The window that received the event + * @param[in] c The character that got typed + */ + static void onCharEvent(GLFWwindow *callbackWindow, unsigned int c); + + /** + * @brief A callback function for gamepad input events. + * @param gamepadIndex The gamepad index. + */ + static void onGamepadEvent(int gamepadIndex); public: /** @@ -78,11 +98,6 @@ namespace vkcv { [[nodiscard]] bool isWindowOpen() const; - /** - * binds windowEvents to lambda events - */ - void initEvents(); - /** * polls all events on the GLFWwindow */ @@ -96,6 +111,8 @@ namespace vkcv { event< double, double > e_mouseScroll; event< int, int > e_resize; event< int, int, int, int > e_key; + event< unsigned int > e_char; + event< int > e_gamepad; /** * returns the current window @@ -142,4 +159,4 @@ namespace vkcv { virtual ~Window(); }; -} \ No newline at end of file +} diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index d6c372d07f52ed25acaaae60787e258afe4be085..802200ad5deb76decbb75e30e1fbd14bff3b7e3b 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -2,5 +2,8 @@ # Add new modules here: add_subdirectory(asset_loader) add_subdirectory(camera) +add_subdirectory(gui) +add_subdirectory(material) add_subdirectory(scene) +add_subdirectory(shader_compiler) add_subdirectory(testing) diff --git a/modules/asset_loader/CMakeLists.txt b/modules/asset_loader/CMakeLists.txt index d2a9b817ea68c7851fd2123f76b378d8a4d85ac0..c5a1fd0eb9620d3a95af1c52a9e7456337047c7b 100644 --- a/modules/asset_loader/CMakeLists.txt +++ b/modules/asset_loader/CMakeLists.txt @@ -38,3 +38,5 @@ target_include_directories(vkcv_asset_loader SYSTEM BEFORE PRIVATE ${vkcv_asset_ # add the own include directory for public headers target_include_directories(vkcv_asset_loader BEFORE PUBLIC ${vkcv_asset_loader_include}) + +target_compile_definitions(vkcv_asset_loader PUBLIC ${vkcv_asset_loader_definitions}) diff --git a/modules/asset_loader/config/STB.cmake b/modules/asset_loader/config/STB.cmake index da20d3ec07f98c865b4c6e38518f668b226cbfb9..1287d0a9ddda559e061ddd680bc815e24b3e2075 100644 --- a/modules/asset_loader/config/STB.cmake +++ b/modules/asset_loader/config/STB.cmake @@ -1,6 +1,10 @@ if (EXISTS "${vkcv_asset_loader_lib_path}/stb") list(APPEND vkcv_asset_loader_includes ${vkcv_asset_loader_lib}/stb) + + list(APPEND vkcv_asset_loader_definitions STB_IMAGE_IMPLEMENTATION) + list(APPEND vkcv_asset_loader_definitions STBI_ONLY_JPEG) + list(APPEND vkcv_asset_loader_definitions STBI_ONLY_PNG) else() message(WARNING "STB is required..! Update the submodules!") endif () diff --git a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp index 24687e846ff9eae3275de357331a825f0b4ed2c3..4107d57ee97a6efe0475c6d9dbd80d2603e0afe8 100644 --- a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp +++ b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp @@ -1,16 +1,16 @@ #pragma once /** - * @authors Trevor Hollmann + * @authors Trevor Hollmann, Mara Vogt, Susanne Dötsch * @file include/vkcv/asset/asset_loader.h * @brief Interface of the asset loader module for the vkcv framework. */ #include <string> #include <vector> +#include <array> #include <cstdint> -#include <vkcv/VertexLayout.hpp> -/* These macros define limits of the following structs. Implementations can +/** These macros define limits of the following structs. Implementations can * test against these limits when performing sanity checks. The main constraint * expressed is that of the data type: Material indices are identified by a * uint8_t in the VertexGroup struct, so there can't be more than UINT8_MAX @@ -20,17 +20,18 @@ #define MAX_MATERIALS_PER_MESH UINT8_MAX #define MAX_VERTICES_PER_VERTEX_GROUP UINT32_MAX -/* LOADING MESHES +/** LOADING MESHES * The description of meshes is a hierarchy of structures with the Mesh at the * top. * * Each Mesh has an array of one or more vertex groups (called "primitives" in - * glTF parlance) and an array of zero or more Materials. + * glTF parlance). Specifically, it has an array of indices into an array of + * vertex groups defined by the Scene struct. * * Each vertex group describes a part of the meshes vertices by defining how * they should be rendered (as points, lines, triangles), how many indices and * vertices there are, how the content of the vertex buffer is to be - * interpreted and which material from the Meshes materials array should be + * interpreted and which material from the Scenes materials array should be * used for the surface of the vertices. * As a bonus there is also the axis aligned bounding box of the vertices. * @@ -44,20 +45,99 @@ namespace vkcv::asset { -/* This enum matches modes in fx-gltf, the library returns a standard mode +/** This enum matches modes in fx-gltf, the library returns a standard mode * (TRIANGLES) if no mode is given in the file. */ -enum PrimitiveMode { +enum class PrimitiveMode : uint8_t { POINTS=0, LINES, LINELOOP, LINESTRIP, TRIANGLES, TRIANGLESTRIP, TRIANGLEFAN }; -/* The indices in the index buffer can be of different bit width. */ -enum IndexType { UINT32=0, UINT16=1, UINT8=2 }; +/** The indices in the index buffer can be of different bit width. */ +enum class IndexType : uint8_t { UNDEFINED=0, UINT8=1, UINT16=2, UINT32=3 }; + +typedef struct { + // TODO define struct for samplers (low priority) + // NOTE: glTF defines samplers based on OpenGL, which can not be + // directly translated to Vulkan. Specifically, OpenGL (and glTF) + // define a different set of Min/Mag-filters than Vulkan. +} Sampler; + +/** struct for defining the loaded texture */ typedef struct { - // TODO not yet needed for the first (unlit) triangle + int sampler; // index into the sampler array of the Scene + uint8_t channels; // number of channels + uint16_t w, h; // width and height of the texture + std::vector<uint8_t> data; // binary data of the decoded texture +} Texture; + +/** The asset loader module only supports the PBR-MetallicRoughness model for + * materials.*/ +typedef struct { + uint16_t textureMask; // bit mask with active texture targets + // Indices into the Array.textures array + int baseColor, metalRough, normal, occlusion, emissive; + // Scaling factors for each texture target + struct { float r, g, b, a; } baseColorFactor; + float metallicFactor, roughnessFactor; + float normalScale; + float occlusionStrength; + struct { float r, g, b; } emissiveFactor; } Material; -/* This struct represents one (possibly the only) part of a mesh. There is +/** Flags for the bit-mask in the Material struct. To check if a material has a + * certain texture target, you can use the hasTexture() function below, passing + * the material struct and the enum. */ +enum class PBRTextureTarget { + baseColor=1, metalRough=2, normal=4, occlusion=8, emissive=16 +}; + +/** This macro translates the index of an enum in the defined order to an + * integer with a single bit set in the corresponding place. It is used for + * working with the bitmask of texture targets ("textureMask") in the Material + * struct: + * Material mat = ...; + * if (mat.textureMask & bitflag(PBRTextureTarget::baseColor)) {...} + * However, this logic is also encapsulated in the convenience-function + * materialHasTexture() so users of the asset loader module can avoid direct + * contact with bit-level operations. */ +#define bitflag(ENUM) (0x1u << ((unsigned)(ENUM))) + +/** To signal that a certain texture target is active in a Material struct, its + * bit is set in the textureMask. You can use this function to check that: + * Material mat = ...; + * if (materialHasTexture(&mat, baseColor)) {...} */ +bool materialHasTexture(const Material *const m, const PBRTextureTarget t); + +/** 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, + TEXCOORD_1 = 4 +}; + +/** These integer values are used the same way in OpenGL, Vulkan and glTF. This + * enum is not needed for translation, it's only for the programmers + * convenience (easier to read in if/switch statements etc). While this enum + * exists in (almost) the same definition in the fx-gltf library, we want to + * avoid exposing that dependency, thus it is re-defined here. */ +enum class ComponentType : uint16_t { + NONE = 0, INT8 = 5120, UINT8 = 5121, INT16 = 5122, UINT16 = 5123, + UINT32 = 5125, FLOAT32 = 5126 +}; + +/** 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 + ComponentType componentType; // eg. 5126 for float + uint8_t componentCount; // eg. 3 for vec3 +} VertexAttribute; + +/** This struct represents one (possibly the only) part of a mesh. There is * always one vertexBuffer and zero or one indexBuffer (indexed rendering is * common but not always used). If there is no index buffer, this is indicated * by indexBuffer.data being empty. Each vertex buffer can have one or more @@ -71,38 +151,43 @@ 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 - uint8_t materialIndex; // index to one of the meshes materials + int materialIndex; // index to one of the materials } VertexGroup; -/* This struct represents a single mesh loaded from a glTF file. It consists of - * at least one VertexVroup and any number of Materials. */ +/** This struct represents a single mesh as it was loaded from a glTF file. It + * consists of at least one VertexGroup, which then references other resources + * such as Materials. */ typedef struct { std::string name; - std::vector<VertexGroup> vertexGroups; - std::vector<Material> materials; - // FIXME Dirty hack to get one(!) texture for our cube demo - // hardcoded to always have RGBA channel layout - struct { - int w, h, ch; // width, height and channels of image - uint8_t *img; // raw bytes, free after use (deal with it) - } texture_hack; + std::array<float, 16> modelMatrix; + std::vector<int> vertexGroups; } Mesh; +/** The scene struct is simply a collection of objects in the scene as well as + * the resources used by those objects. + * For now the only type of object are the meshes and they are represented in a + * flat array. + * Note that parent-child relations are not yet possible. */ +typedef struct { + std::vector<Mesh> meshes; + std::vector<VertexGroup> vertexGroups; + std::vector<Material> materials; + std::vector<Texture> textures; + std::vector<Sampler> samplers; +} Scene; /** - * In its first iteration the asset loader module will only allow loading - * single meshes, one per glTF file. - * It will later be extended to allow loading entire scenes from glTF files. + * Load every mesh from the glTF file, as well as materials and textures. * - * @param path must be the path to a glTF file containing a single mesh. - * @param mesh is a reference to a Mesh struct that will be filled with the + * @param path must be the path to a glTF or glb file. + * @param scene is a reference to a Scene struct that will be filled with the * content of the glTF file being loaded. * */ -int loadMesh(const std::string &path, Mesh &mesh); +int loadScene(const std::string &path, Scene &scene); } diff --git a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp index e660b442d0b9a0208f95c9d753ef19e926bcac44..c21d0c9f70bc81561e1078b15b8372e6dd4730f5 100644 --- a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp +++ b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp @@ -3,9 +3,9 @@ #include <string.h> // memcpy(3) #include <stdlib.h> // calloc(3) #include <fx/gltf.h> -#define STB_IMAGE_IMPLEMENTATION -#define STBI_ONLY_JPEG #include <stb_image.h> +#include <vkcv/Logger.hpp> +#include <algorithm> namespace vkcv::asset { @@ -39,170 +39,328 @@ 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); } } -int loadMesh(const std::string &path, Mesh &mesh) { - fx::gltf::Document object; - - try { - if (path.rfind(".glb", (path.length()-4)) != std::string::npos) { - object = fx::gltf::LoadFromBinary(path); - } else { - object = fx::gltf::LoadFromText(path); - } - } catch (const std::system_error &err) { - print_what(err, path); - return 0; - } catch (const std::exception &e) { - print_what(e, path); - return 0; - } - - // TODO Temporary restriction: Only one mesh per glTF file allowed - // currently. Later, we want to support whole scenes with more than - // just meshes. - if (object.meshes.size() != 1) return 0; - - fx::gltf::Mesh const &objectMesh = object.meshes[0]; - // TODO We want to support more than one vertex group per mesh - // eventually... right now this is hard-coded to use only the first one - // because we only care about the example triangle and cube - fx::gltf::Primitive const &objectPrimitive = objectMesh.primitives[0]; - fx::gltf::Accessor posAccessor; - - std::vector<VertexAttribute> vertexAttributes; - vertexAttributes.reserve(objectPrimitive.attributes.size()); - - for (auto const & attrib : objectPrimitive.attributes) { - fx::gltf::Accessor accessor = object.accessors[attrib.second]; - VertexAttribute attribute; - - if (attrib.first == "POSITION") { - attribute.type = PrimitiveType::POSITION; - posAccessor = accessor; - } else if (attrib.first == "NORMAL") { - attribute.type = PrimitiveType::NORMAL; - } else if (attrib.first == "TEXCOORD_0") { - attribute.type = PrimitiveType::TEXCOORD_0; - } else { - return 0; - } - - attribute.offset = object.bufferViews[accessor.bufferView].byteOffset; - attribute.length = object.bufferViews[accessor.bufferView].byteLength; - attribute.stride = object.bufferViews[accessor.bufferView].byteStride; - attribute.componentType = static_cast<uint16_t>(accessor.componentType); - - if (convertTypeToInt(accessor.type) != 10) { - attribute.componentCount = convertTypeToInt(accessor.type); - } else { - return 0; - } - - vertexAttributes.push_back(attribute); - } - - // TODO consider the case where there is no index buffer (not all - // meshes have to use indexed rendering) - const fx::gltf::Accessor &indexAccessor = object.accessors[objectPrimitive.indices]; - const fx::gltf::BufferView &indexBufferView = object.bufferViews[indexAccessor.bufferView]; - const fx::gltf::Buffer &indexBuffer = object.buffers[indexBufferView.buffer]; - - std::vector<uint8_t> indexBufferData; - indexBufferData.resize(indexBufferView.byteLength); - { - 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"; - return 0; - } - } - - const fx::gltf::BufferView& vertexBufferView = object.bufferViews[posAccessor.bufferView]; - const fx::gltf::Buffer& vertexBuffer = object.buffers[vertexBufferView.buffer]; - - // FIXME: This only works when all vertex attributes are in one buffer - std::vector<uint8_t> vertexBufferData; - vertexBufferData.resize(vertexBuffer.byteLength); - { - 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"; - return 0; - } - } - - IndexType indexType; - switch(indexAccessor.componentType) { +/** Translate the component type used in the index accessor of fx-gltf to our + * enum for index type. The reason we have defined an incompatible enum that + * needs translation is that only a subset of component types is valid for + * indices and we want to catch these incompatibilities here. */ +enum IndexType getIndexType(const enum fx::gltf::Accessor::ComponentType &t) +{ + switch (t) { case fx::gltf::Accessor::ComponentType::UnsignedByte: - indexType = UINT8; break; + return IndexType::UINT8; case fx::gltf::Accessor::ComponentType::UnsignedShort: - indexType = UINT16; break; + return IndexType::UINT16; case fx::gltf::Accessor::ComponentType::UnsignedInt: - indexType = UINT32; break; + return IndexType::UINT32; default: - std::cerr << "ERROR: Index type not supported: " << - static_cast<uint16_t>(indexAccessor.componentType) << - std::endl; - return 0; + std::cerr << "ERROR: Index type not supported: " << + static_cast<uint16_t>(t) << std::endl; + return IndexType::UNDEFINED; } +} - const size_t numVertexGroups = objectMesh.primitives.size(); - - std::vector<VertexGroup> vertexGroups; - vertexGroups.reserve(numVertexGroups); - - vertexGroups.push_back({ - static_cast<PrimitiveMode>(objectPrimitive.mode), - object.accessors[objectPrimitive.indices].count, - posAccessor.count, - {indexType, indexBufferData}, - {vertexBufferData, vertexAttributes}, - {posAccessor.min[0], posAccessor.min[1], posAccessor.min[2]}, - {posAccessor.max[0], posAccessor.max[1], posAccessor.max[2]}, - static_cast<uint8_t>(objectPrimitive.material) - }); - - std::vector<Material> materials; - - mesh = { - object.meshes[0].name, - vertexGroups, - materials, - 0, 0, 0, NULL - }; - - // FIXME HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK - // fail quietly if there is no texture - if (object.textures.size()) { - const std::string mime_type("image/jpeg"); - const fx::gltf::Texture &tex = object.textures[0]; - const fx::gltf::Image &img = object.images[tex.source]; -#ifndef NDEBUG - printf("texture name=%s sampler=%u source=%u\n", - tex.name.c_str(), tex.sampler, tex.source); - printf("image name=%s uri=%s mime=%s\n", img.name.c_str(), - img.uri.c_str(), img.mimeType.c_str()); -#endif - - size_t pos = path.find_last_of("/"); - auto dir = path.substr(0, pos); - - mesh.texture_hack.img = stbi_load((dir + "/" + img.uri).c_str(), - &mesh.texture_hack.w, &mesh.texture_hack.h, - &mesh.texture_hack.ch, 4); - } - // FIXME HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK - return 1; +/** + * This function computes the modelMatrix out of the data given in the gltf file. It also checks, whether a modelMatrix was given. + * @param translation possible translation vector (default 0,0,0) + * @param scale possible scale vector (default 1,1,1) + * @param rotation possible rotation, given in quaternion (default 0,0,0,1) + * @param matrix possible modelmatrix (default identity) + * @return model Matrix as an array of floats + */ +std::array<float, 16> computeModelMatrix(std::array<float, 3> translation, std::array<float, 3> scale, std::array<float, 4> rotation, std::array<float, 16> matrix){ + std::array<float, 16> modelMatrix = {1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1}; + if (matrix != modelMatrix){ + return matrix; + } else { + // translation + modelMatrix[3] = translation[0]; + modelMatrix[7] = translation[1]; + modelMatrix[11] = translation[2]; + // rotation and scale + auto a = rotation[0]; + auto q1 = rotation[1]; + auto q2 = rotation[2]; + auto q3 = rotation[3]; + + modelMatrix[0] = (2 * (a * a + q1 * q1) - 1) * scale[0]; + modelMatrix[1] = (2 * (q1 * q2 - a * q3)) * scale[1]; + modelMatrix[2] = (2 * (q1 * q3 + a * q2)) * scale[2]; + + modelMatrix[4] = (2 * (q1 * q2 + a * q3)) * scale[0]; + modelMatrix[5] = (2 * (a * a + q2 * q2) - 1) * scale[1]; + modelMatrix[6] = (2 * (q2 * q3 - a * q1)) * scale[2]; + + modelMatrix[8] = (2 * (q1 * q3 - a * q2)) * scale[0]; + modelMatrix[9] = (2 * (q2 * q3 + a * q1)) * scale[1]; + modelMatrix[10] = (2 * (a * a + q3 * q3) - 1) * scale[2]; + + // flip y, because GLTF uses y up, but vulkan -y up + modelMatrix[5] *= -1; + + return modelMatrix; + } + +} + +bool materialHasTexture(const Material *const m, const PBRTextureTarget t) +{ + return m->textureMask & bitflag(t); +} + +int loadScene(const std::string &path, Scene &scene){ + fx::gltf::Document sceneObjects; + + try { + if (path.rfind(".glb", (path.length()-4)) != std::string::npos) { + sceneObjects = fx::gltf::LoadFromBinary(path); + } else { + sceneObjects = fx::gltf::LoadFromText(path); + } + } catch (const std::system_error &err) { + print_what(err, path); + return 0; + } catch (const std::exception &e) { + print_what(e, path); + return 0; + } + size_t pos = path.find_last_of("/"); + auto dir = path.substr(0, pos); + + // file has to contain at least one mesh + if (sceneObjects.meshes.size() == 0) return 0; + + fx::gltf::Accessor posAccessor; + std::vector<VertexAttribute> vertexAttributes; + std::vector<Material> materials; + std::vector<Texture> textures; + std::vector<Sampler> samplers; + std::vector<Mesh> meshes; + std::vector<VertexGroup> vertexGroups; + int groupCount = 0; + + Mesh mesh = {}; + + for(int i = 0; i < sceneObjects.meshes.size(); i++){ + std::vector<int> vertexGroupsIndices; + fx::gltf::Mesh const &objectMesh = sceneObjects.meshes[i]; + + for(int j = 0; j < objectMesh.primitives.size(); j++){ + fx::gltf::Primitive const &objectPrimitive = objectMesh.primitives[j]; + vertexAttributes.clear(); + vertexAttributes.reserve(objectPrimitive.attributes.size()); + + for (auto const & attrib : objectPrimitive.attributes) { + + fx::gltf::Accessor accessor = sceneObjects.accessors[attrib.second]; + VertexAttribute attribute; + + if (attrib.first == "POSITION") { + attribute.type = PrimitiveType::POSITION; + posAccessor = accessor; + } else if (attrib.first == "NORMAL") { + attribute.type = PrimitiveType::NORMAL; + } else if (attrib.first == "TEXCOORD_0") { + attribute.type = PrimitiveType::TEXCOORD_0; + } else if (attrib.first == "TEXCOORD_1") { + attribute.type = PrimitiveType::TEXCOORD_1; + } else { + return 0; + } + + attribute.offset = sceneObjects.bufferViews[accessor.bufferView].byteOffset; + attribute.length = sceneObjects.bufferViews[accessor.bufferView].byteLength; + attribute.stride = sceneObjects.bufferViews[accessor.bufferView].byteStride; + attribute.componentType = static_cast<ComponentType>(accessor.componentType); + + if (convertTypeToInt(accessor.type) != 10) { + attribute.componentCount = convertTypeToInt(accessor.type); + } else { + return 0; + } + + vertexAttributes.push_back(attribute); + } + + IndexType indexType; + std::vector<uint8_t> indexBufferData = {}; + if (objectPrimitive.indices >= 0){ // if there is no index buffer, -1 is returned from fx-gltf + const fx::gltf::Accessor &indexAccessor = sceneObjects.accessors[objectPrimitive.indices]; + const fx::gltf::BufferView &indexBufferView = sceneObjects.bufferViews[indexAccessor.bufferView]; + const fx::gltf::Buffer &indexBuffer = sceneObjects.buffers[indexBufferView.buffer]; + + indexBufferData.resize(indexBufferView.byteLength); + { + const size_t off = indexBufferView.byteOffset; + const void *const ptr = ((char*)indexBuffer.data.data()) + off; + if (!memcpy(indexBufferData.data(), ptr, indexBufferView.byteLength)) { + vkcv_log(LogLevel::ERROR, "Copying index buffer data"); + return 0; + } + } + + indexType = getIndexType(indexAccessor.componentType); + if (indexType == IndexType::UNDEFINED){ + vkcv_log(LogLevel::ERROR, "Index Type undefined."); + return 0; + } + } + + const fx::gltf::BufferView& vertexBufferView = sceneObjects.bufferViews[posAccessor.bufferView]; + const fx::gltf::Buffer& vertexBuffer = sceneObjects.buffers[vertexBufferView.buffer]; + + // only copy relevant part of vertex data + uint32_t relevantBufferOffset = std::numeric_limits<uint32_t>::max(); + uint32_t relevantBufferEnd = 0; + for (const auto &attribute : vertexAttributes) { + relevantBufferOffset = std::min(attribute.offset, relevantBufferOffset); + const uint32_t attributeEnd = attribute.offset + attribute.length; + relevantBufferEnd = std::max(relevantBufferEnd, attributeEnd); // TODO: need to incorporate stride? + } + const uint32_t relevantBufferSize = relevantBufferEnd - relevantBufferOffset; + + // FIXME: This only works when all vertex attributes are in one buffer + std::vector<uint8_t> vertexBufferData; + vertexBufferData.resize(relevantBufferSize); + { + const void *const ptr = ((char*)vertexBuffer.data.data()) + relevantBufferOffset; + if (!memcpy(vertexBufferData.data(), ptr, relevantBufferSize)) { + vkcv_log(LogLevel::ERROR, "Copying vertex buffer data"); + return 0; + } + } + + // make vertex attributes relative to copied section + for (auto &attribute : vertexAttributes) { + attribute.offset -= relevantBufferOffset; + } + + const size_t numVertexGroups = objectMesh.primitives.size(); + vertexGroups.reserve(numVertexGroups); + + vertexGroups.push_back({ + static_cast<PrimitiveMode>(objectPrimitive.mode), + sceneObjects.accessors[objectPrimitive.indices].count, + posAccessor.count, + {indexType, indexBufferData}, + {vertexBufferData, vertexAttributes}, + {posAccessor.min[0], posAccessor.min[1], posAccessor.min[2]}, + {posAccessor.max[0], posAccessor.max[1], posAccessor.max[2]}, + static_cast<uint8_t>(objectPrimitive.material) + }); + + vertexGroupsIndices.push_back(groupCount); + groupCount++; + } + + mesh.name = sceneObjects.meshes[i].name; + mesh.vertexGroups = vertexGroupsIndices; + + meshes.push_back(mesh); + } + + for(int m = 0; m < sceneObjects.nodes.size(); m++) { + meshes[sceneObjects.nodes[m].mesh].modelMatrix = computeModelMatrix(sceneObjects.nodes[m].translation, + sceneObjects.nodes[m].scale, + sceneObjects.nodes[m].rotation, + sceneObjects.nodes[m].matrix); + } + + if (sceneObjects.textures.size() > 0){ + textures.reserve(sceneObjects.textures.size()); + + for(int k = 0; k < sceneObjects.textures.size(); k++){ + const fx::gltf::Texture &tex = sceneObjects.textures[k]; + const fx::gltf::Image &img = sceneObjects.images[tex.source]; + std::string img_uri = dir + "/" + img.uri; + int w, h, c; + uint8_t *data = stbi_load(img_uri.c_str(), &w, &h, &c, 4); + c = 4; // FIXME hardcoded to always have RGBA channel layout + if (!data) { + vkcv_log(LogLevel::ERROR, "Loading texture image data.") + return 0; + } + const size_t byteLen = w * h * c; + + std::vector<uint8_t> imgdata; + imgdata.resize(byteLen); + if (!memcpy(imgdata.data(), data, byteLen)) { + vkcv_log(LogLevel::ERROR, "Copying texture image data") + free(data); + return 0; + } + free(data); + + textures.push_back({ + 0, + static_cast<uint8_t>(c), + static_cast<uint16_t>(w), + static_cast<uint16_t>(h), + imgdata + }); + + } + } + + if (sceneObjects.materials.size() > 0){ + materials.reserve(sceneObjects.materials.size()); + + for (int l = 0; l < sceneObjects.materials.size(); l++){ + fx::gltf::Material material = sceneObjects.materials[l]; + // TODO I think we shouldn't set the index for a texture target if + // it isn't defined. So we need to test first if there is a normal + // texture before assigning material.normalTexture.index. + // About the bitmask: If a normal texture is there, modify the + // materials textureMask like this: + // mat.textureMask |= bitflag(asset::normal); + materials.push_back({ + 0, + material.pbrMetallicRoughness.baseColorTexture.index, + material.pbrMetallicRoughness.metallicRoughnessTexture.index, + material.normalTexture.index, + material.occlusionTexture.index, + material.emissiveTexture.index, + { + material.pbrMetallicRoughness.baseColorFactor[0], + material.pbrMetallicRoughness.baseColorFactor[1], + material.pbrMetallicRoughness.baseColorFactor[2], + material.pbrMetallicRoughness.baseColorFactor[3] + }, + material.pbrMetallicRoughness.metallicFactor, + material.pbrMetallicRoughness.roughnessFactor, + material.normalTexture.scale, + material.occlusionTexture.strength, + { + material.emissiveFactor[0], + material.emissiveFactor[1], + material.emissiveFactor[2] + } + + }); + } + } + + scene = { + meshes, + vertexGroups, + materials, + textures, + samplers + }; + + return 1; } } diff --git a/modules/camera/CMakeLists.txt b/modules/camera/CMakeLists.txt index 28080bf2b1cd3bbc88d6c13d7ef26a43d7c3e19a..60cfca4cf97cef30d989bdab064e20547764041c 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 @@ -33,3 +36,4 @@ target_include_directories(vkcv_camera SYSTEM BEFORE PRIVATE ${vkcv_camera_inclu # add the own include directory for public headers target_include_directories(vkcv_camera BEFORE PUBLIC ${vkcv_camera_include} ${vkcv_camera_includes}) +target_compile_definitions(vkcv_camera PUBLIC ${vkcv_camera_definitions}) diff --git a/modules/camera/config/GLM.cmake b/modules/camera/config/GLM.cmake index c4d14392ef0ea24243a45b19cd8583d90d3267be..efd6444451100b912aa0b5b4a532dc8f448b0b40 100644 --- a/modules/camera/config/GLM.cmake +++ b/modules/camera/config/GLM.cmake @@ -4,11 +4,17 @@ find_package(glm QUIET) if (glm_FOUND) list(APPEND vkcv_camera_includes ${GLM_INCLUDE_DIRS}) list(APPEND vkcv_camera_libraries glm) + + list(APPEND vkcv_camera_definitions GLM_DEPTH_ZERO_TO_ONE) + list(APPEND vkcv_camera_definitions GLM_FORCE_LEFT_HANDED) else() if (EXISTS "${vkcv_camera_lib_path}/glm") add_subdirectory(${vkcv_camera_lib}/glm) list(APPEND vkcv_camera_libraries glm) + + list(APPEND vkcv_camera_definitions GLM_DEPTH_ZERO_TO_ONE) + list(APPEND vkcv_camera_definitions GLM_FORCE_LEFT_HANDED) else() message(WARNING "GLM is required..! Update the submodules!") endif () diff --git a/modules/camera/include/vkcv/camera/Camera.hpp b/modules/camera/include/vkcv/camera/Camera.hpp index 7e177b9a2fbde0890e0c8ea6a1d9a19d6e277c7c..ce32d3f8a0c6ee3e0dd882f24a9ac2d12c14a024 100644 --- a/modules/camera/include/vkcv/camera/Camera.hpp +++ b/modules/camera/include/vkcv/camera/Camera.hpp @@ -4,98 +4,194 @@ #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(float ratio); + /** + * @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..90fc97401851851194ec89a10757bbfb1453990d --- /dev/null +++ b/modules/camera/include/vkcv/camera/CameraController.hpp @@ -0,0 +1,72 @@ +#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; + + /** + * @brief A callback function for gamepad input events. + * @param gamepadIndex The gamepad index. + * @param camera The camera object. + * @param frametime The current frametime. + */ + virtual void gamepadCallback(int gamepadIndex, Camera &camera, double frametime) = 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 9511b752e972afb1e10f41a118433a4e8933fd65..409f9196599be02e4215f3924c1102f0b8c72899 100644 --- a/modules/camera/include/vkcv/camera/CameraManager.hpp +++ b/modules/camera/include/vkcv/camera/CameraManager.hpp @@ -1,38 +1,195 @@ #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)> m_keyHandle; - std::function<void(double, double)> m_mouseMoveHandle; - std::function<void(double, double)> m_mouseScrollHandle; - std::function<void(int, int, int)> m_mouseButtonHandle; - - 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(); + event_handle<int, int, int, int> m_keyHandle; + event_handle<double, double> m_mouseMoveHandle; + event_handle<double, double> m_mouseScrollHandle; + event_handle<int, int, int> m_mouseButtonHandle; + event_handle<int, int> m_resizeHandle; + event_handle<int> m_gamepadHandle; + + 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; + + double m_inputDelayTimer; + double m_frameTime; + + /** + * @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 A callback function for gamepad input events. Currently, inputs are handled only for the first + * connected gamepad! + * @param gamepadIndex The gamepad index. + */ + void gamepadCallback(int gamepadIndex); + + /** + * @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. + */ + CameraManager(Window &window); + + /** + * @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..2b64cdc0dd3045714aba7b3b7c6241af2337c706 --- /dev/null +++ b/modules/camera/include/vkcv/camera/PilotCameraController.hpp @@ -0,0 +1,150 @@ +#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; + + float m_gamepadX; + float m_gamepadY; + float m_gamepadZ; + + 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); + + /** + * @brief A callback function for gamepad input events. + * @param gamepadIndex The gamepad index. + * @param camera The camera object. + * @param frametime The current frametime. + */ + void gamepadCallback(int gamepadIndex, Camera &camera, double frametime); + }; + +} \ 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..4166bda9f6cb62e4c8f1b650557b00c6ec94b2a1 --- /dev/null +++ b/modules/camera/include/vkcv/camera/TrackballCameraController.hpp @@ -0,0 +1,107 @@ +#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); + + /** + * @brief A callback function for gamepad input events. + * @param gamepadIndex The gamepad index. + * @param camera The camera object. + * @param frametime The current frametime. + */ + void gamepadCallback(int gamepadIndex, Camera &camera, double frametime); + }; + +} \ 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 bc8a8498e67a6bd751f5a6ed1d4c4fba0279a68d..18bf94463a0e2c4cb7d64526f4c30835cb451eb2 100644 --- a/modules/camera/src/vkcv/camera/Camera.cpp +++ b/modules/camera/src/vkcv/camera/Camera.cpp @@ -1,44 +1,27 @@ #include "vkcv/camera/Camera.hpp" -#include <iostream> -namespace vkcv { +#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,96 +29,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( float ratio){ - m_ratio = ratio; - 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; } @@ -147,31 +145,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 18f499a2b34b64c1442c5d9e267d6476b8d69199..f129f3a248325957cb56470e2547a0146bc7c971 100644 --- a/modules/camera/src/vkcv/camera/CameraManager.cpp +++ b/modules/camera/src/vkcv/camera/CameraManager.cpp @@ -1,82 +1,194 @@ -#include <iostream> + #include "vkcv/camera/CameraManager.hpp" +#include <vkcv/Logger.hpp> -namespace vkcv{ +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) + : 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; + m_inputDelayTimer = glfwGetTime() + 0.2; + m_frameTime = 0; + } + + CameraManager::~CameraManager() { + m_window.e_key.remove(m_keyHandle); + m_window.e_mouseMove.remove(m_mouseMoveHandle); + m_window.e_mouseScroll.remove(m_mouseScrollHandle); + m_window.e_mouseButton.remove(m_mouseButtonHandle); + m_window.e_resize.remove(m_resizeHandle); + m_window.e_gamepad.remove(m_gamepadHandle); } - void CameraManager::bindCamera(){ + 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);}); + m_gamepadHandle = m_window.e_gamepad.add([&](int gamepadIndex) {this->gamepadCallback(gamepadIndex);}); + } + + void CameraManager::resizeCallback(int width, int height) { + if (glfwGetWindowAttrib(m_window.getWindow(), GLFW_ICONIFIED) == GLFW_FALSE) { + 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()); + } + + void CameraManager::scrollCallback(double offsetX, double offsetY) { + getActiveController().scrollCallback(offsetX, offsetY, getActiveCamera()); + } - if(!m_roationActive){ - return; + 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; } + } + + void CameraManager::gamepadCallback(int gamepadIndex) { + // handle camera switching + GLFWgamepadstate gamepadState; + glfwGetGamepadState(gamepadIndex, &gamepadState); - float sensitivity = 0.05f; - xoffset *= sensitivity; - yoffset *= sensitivity; + double time = glfwGetTime(); + if (time - m_inputDelayTimer > 0.2) { + int switchDirection = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] - gamepadState.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT]; + m_activeCameraIndex += switchDirection; + if (std::greater<int>{}(m_activeCameraIndex, m_cameras.size() - 1)) { + m_activeCameraIndex = 0; + } + else if (std::less<int>{}(m_activeCameraIndex, 0)) { + m_activeCameraIndex = m_cameras.size() - 1; + } + uint32_t triggered = abs(switchDirection); + m_inputDelayTimer = (1-triggered)*m_inputDelayTimer + triggered * time; // Only reset timer, if dpad was pressed - is this cheaper than if-clause? + } - m_camera.panView( xoffset , yoffset ); + getActiveController().gamepadCallback(gamepadIndex, getActiveCamera(), m_frameTime); // handle camera rotation, translation } - void CameraManager::scrollCallback(double offsetX, double offsetY) { - m_camera.changeFov(offsetY); + CameraController& CameraManager::getActiveController() { + const ControllerType type = getControllerType(getActiveCameraIndex()); + return getControllerByType(type); + } + + uint32_t CameraManager::addCamera(ControllerType controllerType) { + const float ratio = static_cast<float>(m_window.getWidth()) / static_cast<float>(m_window.getHeight()); + + Camera camera; + camera.setPerspective(glm::radians(60.0f), ratio, 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; } - void CameraManager::keyCallback(int key, int scancode, int action, int mods) { + 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]; + } - 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; + Camera& CameraManager::getActiveCamera() { + return m_cameras[getActiveCameraIndex()]; + } + + 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; } - Camera &CameraManager::getCamera(){ - return m_camera; + + uint32_t CameraManager::getActiveCameraIndex() const { + return m_activeCameraIndex; } -} \ No newline at end of file + 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; + } + + 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]; + } + + 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) { + m_frameTime = deltaTime; + if (glfwGetWindowAttrib(m_window.getWindow(), GLFW_FOCUSED) == GLFW_TRUE) { + getActiveController().updateCamera(deltaTime, getActiveCamera()); + } + } + +} diff --git a/modules/camera/src/vkcv/camera/PilotCameraController.cpp b/modules/camera/src/vkcv/camera/PilotCameraController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5460858ab48d81252787b3c0141dd72982faca7d --- /dev/null +++ b/modules/camera/src/vkcv/camera/PilotCameraController.cpp @@ -0,0 +1,191 @@ +#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_gamepadX = 0.0f; + m_gamepadY = 0.0f; + m_gamepadZ = 0.0f; + + m_rotationActive = false; + + m_cameraSpeed = 2.5f; + + m_fov_nsteps = 100; + m_fov_min = 10; + m_fov_max = 120; + } + + void PilotCameraController::changeFov(double offset, Camera &camera){ + // update only if there is (valid) input + if (offset == 0.0) { + return; + } + + 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) { + // update only if there is (valid) input + if (xOffset == 0.0 && yOffset == 0.0) { + return; + } + + // handle yaw rotation + float yaw = camera.getYaw() + static_cast<float>(xOffset); + yaw += 360.0f * (yaw < -180.0f) - 360.0f * (yaw > 180.0f); + camera.setYaw(yaw); + + // handle pitch rotation + float pitch = camera.getPitch() - static_cast<float>(yOffset); + pitch = glm::clamp(pitch, -89.0f, 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) + m_gamepadZ) * front; + position += distance * (getDirectionFactor(m_left, m_right) + m_gamepadX) * left; + position += distance * (getDirectionFactor(m_upward, m_downward) + m_gamepadY) * 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::gamepadCallback(int gamepadIndex, Camera &camera, double frametime) { + GLFWgamepadstate gamepadState; + glfwGetGamepadState(gamepadIndex, &gamepadState); + + float sensitivity = 100.0f; + double threshold = 0.1; + + // handle rotations + double stickRightX = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]); + double stickRightY = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]); + + double rightXVal = glm::clamp(std::abs(stickRightX) - threshold, 0.0, 1.0) + * copysign(1.0, stickRightX) * sensitivity * frametime; + double rightYVal = glm::clamp(std::abs(stickRightY) - threshold, 0.0, 1.0) + * copysign(1.0, stickRightY) * sensitivity * frametime; + panView(rightXVal, rightYVal, camera); + + // handle zooming + double zoom = static_cast<double>((gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] + - gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER]) + * sensitivity * frametime); + changeFov(zoom, camera); + + // handle translation + m_gamepadY = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER] - gamepadState.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER]; + float stickLeftX = gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_X]; + float stickLeftY = gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]; + m_gamepadZ = glm::clamp(std::abs(stickLeftY) - threshold, 0.0, 1.0) + * -copysign(1.0, stickLeftY); + m_gamepadX = glm::clamp(std::abs(stickLeftX) - threshold, 0.0, 1.0) + * -copysign(1.0, stickLeftX); + } + + + 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..cdd66cdb7fdd650d5112fe7bb4738f1fcded7783 --- /dev/null +++ b/modules/camera/src/vkcv/camera/TrackballCameraController.cpp @@ -0,0 +1,118 @@ +#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) { + m_radius = 0.1f * (radius < 0.1f) + radius * (1 - (radius < 0.1f)); + } + + void TrackballCameraController::panView(double xOffset, double yOffset, Camera &camera) { + // update only if there is (valid) input + if (xOffset == 0.0 && yOffset == 0.0) { + return; + } + + // handle yaw rotation + float yaw = camera.getYaw() + static_cast<float>(xOffset) * m_cameraSpeed; + yaw += 360.0f * (yaw < 0.0f) - 360.0f * (yaw > 360.0f); + camera.setYaw(yaw); + + // handle pitch rotation + float pitch = camera.getPitch() + static_cast<float>(yOffset) * m_cameraSpeed; + pitch += 360.0f * (pitch < 0.0f) - 360.0f * (pitch > 360.0f); + camera.setPitch(pitch); + } + + void TrackballCameraController::updateRadius(double offset, Camera &camera) { + // update only if there is (valid) input + if (offset == 0.0) { + return; + } + + glm::vec3 cameraPosition = camera.getPosition(); + glm::vec3 cameraCenter = camera.getCenter(); + float radius = glm::length(cameraCenter - cameraPosition); // get current camera radius + setRadius(radius - static_cast<float>(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.025f; + 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; + } + } + + void TrackballCameraController::gamepadCallback(int gamepadIndex, Camera &camera, double frametime) { + GLFWgamepadstate gamepadState; + glfwGetGamepadState(gamepadIndex, &gamepadState); + + float sensitivity = 100.0f; + double threshold = 0.1; + + // handle rotations + double stickRightX = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]); + double stickRightY = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]); + + double rightXVal = glm::clamp((abs(stickRightX)-threshold), 0.0, 1.0) + * std::copysign(1.0, stickRightX) * sensitivity * frametime; + double rightYVal = glm::clamp((abs(stickRightY)-threshold), 0.0, 1.0) + * std::copysign(1.0, stickRightY) * sensitivity * frametime; + panView(rightXVal, rightYVal, camera); + + // handle translation + double stickLeftY = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]); + double leftYVal = glm::clamp((abs(stickLeftY)-threshold), 0.0, 1.0) + * std::copysign(1.0, stickLeftY) * sensitivity * frametime; + updateRadius(-leftYVal, camera); + } +} \ No newline at end of file diff --git a/modules/gui/CMakeLists.txt b/modules/gui/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..3b5202ccfe454f38745c53ac711cc05095ef88a1 --- /dev/null +++ b/modules/gui/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_gui) + +# setting c++ standard for the module +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_gui_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_gui_include ${PROJECT_SOURCE_DIR}/include) + +# Add source and header files to the module +set(vkcv_gui_sources + ${vkcv_gui_include}/vkcv/gui/GUI.hpp + ${vkcv_gui_source}/vkcv/gui/GUI.cpp + ) + +# Setup some path variables to load libraries +set(vkcv_gui_lib lib) +set(vkcv_gui_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_gui_lib}) + +# Check and load IMGUI +include(config/ImGui.cmake) + +# adding source files to the module +add_library(vkcv_gui STATIC ${vkcv_gui_sources} ${vkcv_imgui_sources}) + +# link the required libraries to the module +target_link_libraries(vkcv_gui ${vkcv_gui_libraries} vkcv ${vkcv_libraries}) + +# including headers of dependencies and the VkCV framework +target_include_directories(vkcv_gui SYSTEM BEFORE PRIVATE ${vkcv_gui_includes} ${vkcv_include} ${vkcv_includes}) + +# add the own include directory for public headers +target_include_directories(vkcv_gui BEFORE PUBLIC ${vkcv_gui_include} ${vkcv_imgui_includes}) + +target_compile_definitions(vkcv_gui PUBLIC ${vkcv_gui_defines}) diff --git a/modules/gui/config/ImGui.cmake b/modules/gui/config/ImGui.cmake new file mode 100644 index 0000000000000000000000000000000000000000..90cdafdeee355af9e63723632572799e135b04da --- /dev/null +++ b/modules/gui/config/ImGui.cmake @@ -0,0 +1,27 @@ + +if (EXISTS "${vkcv_gui_lib_path}/imgui") + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/backends/imgui_impl_glfw.cpp) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/backends/imgui_impl_glfw.h) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/backends/imgui_impl_vulkan.cpp) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/backends/imgui_impl_vulkan.h) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imconfig.h) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imgui.cpp) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imgui.h) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imgui_draw.cpp) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imgui_demo.cpp) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imgui_internal.h) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imgui_tables.cpp) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imgui_widgets.cpp) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imstb_rectpack.h) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imstb_textedit.h) + list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/imstb_truetype.h) + + list(APPEND vkcv_imgui_includes ${vkcv_gui_lib}/imgui) + list(APPEND vkcv_imgui_includes ${vkcv_gui_lib}/imgui/backend) + + list(APPEND vkcv_gui_include ${vkcv_gui_lib}) + + list(APPEND vkcv_gui_defines IMGUI_DISABLE_WIN32_FUNCTIONS=1) +else() + message(WARNING "IMGUI is required..! Update the submodules!") +endif () diff --git a/modules/gui/include/vkcv/gui/GUI.hpp b/modules/gui/include/vkcv/gui/GUI.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d1a9986c5f69bfd9d4392bd5ae50f0b1f8b60642 --- /dev/null +++ b/modules/gui/include/vkcv/gui/GUI.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include "imgui/imgui.h" +#include "imgui/backends/imgui_impl_glfw.h" +#include "imgui/backends/imgui_impl_vulkan.h" + +#include <vkcv/Core.hpp> +#include <vkcv/Window.hpp> + +namespace vkcv::gui { + + class GUI final { + private: + Window& m_window; + Core& m_core; + + const Context& m_context; + + ImGuiContext* m_gui_context; + + vk::DescriptorPool m_descriptor_pool; + vk::RenderPass m_render_pass; + + event_handle<int,int,int> f_mouseButton; + event_handle<double,double> f_mouseScroll; + event_handle<int,int,int,int> f_key; + event_handle<unsigned int> f_char; + + public: + /** + * Constructor of a new instance of ImGui management + * + * @param core Valid #Core instance of the framework + * @param window Valid #Window instance of the framework + */ + GUI(Core& core, Window& window); + + GUI(const GUI& other) = delete; + GUI(GUI&& other) = delete; + + GUI& operator=(const GUI& other) = delete; + GUI& operator=(GUI&& other) = delete; + + /** + * Destructor of a #GUI instance + */ + virtual ~GUI(); + + /** + * Sets up a new frame for ImGui to draw + */ + void beginGUI(); + + /** + * Ends a frame for ImGui, renders it and draws it onto + * the currently active swapchain image of the core (ready to present). + */ + void endGUI(); + + }; + +} diff --git a/modules/gui/lib/imgui b/modules/gui/lib/imgui new file mode 160000 index 0000000000000000000000000000000000000000..d5828cd988db525f27128edeadb1a689cd2d7461 --- /dev/null +++ b/modules/gui/lib/imgui @@ -0,0 +1 @@ +Subproject commit d5828cd988db525f27128edeadb1a689cd2d7461 diff --git a/modules/gui/src/vkcv/gui/GUI.cpp b/modules/gui/src/vkcv/gui/GUI.cpp new file mode 100644 index 0000000000000000000000000000000000000000..38bb6894fb2b40c6ab10445f19431f87f7370afc --- /dev/null +++ b/modules/gui/src/vkcv/gui/GUI.cpp @@ -0,0 +1,241 @@ + +#include "vkcv/gui/GUI.hpp" + +#include <GLFW/glfw3.h> +#include <vkcv/Logger.hpp> + +namespace vkcv::gui { + + static void checkVulkanResult(VkResult resultCode) { + if (resultCode == 0) + return; + + const auto result = vk::Result(resultCode); + + vkcv_log(LogLevel::ERROR, "ImGui has a problem with Vulkan! (%s)", vk::to_string(result).c_str()); + } + + GUI::GUI(Core& core, Window& window) : + m_window(window), + m_core(core), + m_context(m_core.getContext()), + m_gui_context(nullptr) { + IMGUI_CHECKVERSION(); + + m_gui_context = ImGui::CreateContext(); + + ImGui_ImplGlfw_InitForVulkan(m_window.getWindow(), false); + + f_mouseButton = m_window.e_mouseButton.add([&](int button, int action, int mods) { + ImGui_ImplGlfw_MouseButtonCallback(m_window.getWindow(), button, action, mods); + }); + + f_mouseScroll = m_window.e_mouseScroll.add([&](double xoffset, double yoffset) { + ImGui_ImplGlfw_ScrollCallback(m_window.getWindow(), xoffset, yoffset); + }); + + f_key = m_window.e_key.add([&](int key, int scancode, int action, int mods) { + ImGui_ImplGlfw_KeyCallback(m_window.getWindow(), key, scancode, action, mods); + }); + + f_char = m_window.e_char.add([&](unsigned int c) { + ImGui_ImplGlfw_CharCallback(m_window.getWindow(), c); + }); + + vk::DescriptorPoolSize pool_sizes[] = { + vk::DescriptorPoolSize(vk::DescriptorType::eSampler, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eCombinedImageSampler, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eSampledImage, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageImage, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eUniformTexelBuffer, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageTexelBuffer, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageBuffer, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eUniformBufferDynamic, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageBufferDynamic, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eInputAttachment, 1000) + }; + + const vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo ( + vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, + static_cast<uint32_t>(1000 * IM_ARRAYSIZE(pool_sizes)), + static_cast<uint32_t>(IM_ARRAYSIZE(pool_sizes)), + pool_sizes + ); + + m_descriptor_pool = m_context.getDevice().createDescriptorPool(descriptorPoolCreateInfo); + + const vk::PhysicalDevice& physicalDevice = m_context.getPhysicalDevice(); + const Swapchain& swapchain = m_core.getSwapchain(); + + const uint32_t graphicsQueueFamilyIndex = ( + m_context.getQueueManager().getGraphicsQueues()[0].familyIndex + ); + + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Instance = static_cast<VkInstance>(m_context.getInstance()); + init_info.PhysicalDevice = static_cast<VkPhysicalDevice>(m_context.getPhysicalDevice()); + init_info.Device = static_cast<VkDevice>(m_context.getDevice()); + init_info.QueueFamily = graphicsQueueFamilyIndex; + init_info.Queue = static_cast<VkQueue>(m_context.getQueueManager().getGraphicsQueues()[0].handle); + init_info.PipelineCache = 0; + init_info.DescriptorPool = static_cast<VkDescriptorPool>(m_descriptor_pool); + init_info.Allocator = nullptr; + init_info.MinImageCount = swapchain.getImageCount(); + init_info.ImageCount = swapchain.getImageCount(); + init_info.CheckVkResultFn = checkVulkanResult; + + const vk::AttachmentDescription attachment ( + vk::AttachmentDescriptionFlags(), + swapchain.getFormat(), + vk::SampleCountFlagBits::e1, + vk::AttachmentLoadOp::eLoad, + vk::AttachmentStoreOp::eStore, + vk::AttachmentLoadOp::eDontCare, + vk::AttachmentStoreOp::eDontCare, + vk::ImageLayout::eUndefined, + vk::ImageLayout::ePresentSrcKHR + ); + + const vk::AttachmentReference attachmentReference ( + 0, + vk::ImageLayout::eColorAttachmentOptimal + ); + + const vk::SubpassDescription subpass ( + vk::SubpassDescriptionFlags(), + vk::PipelineBindPoint::eGraphics, + 0, + nullptr, + 1, + &attachmentReference, + nullptr, + nullptr, + 0, + nullptr + ); + + const vk::SubpassDependency dependency ( + VK_SUBPASS_EXTERNAL, + 0, + vk::PipelineStageFlagBits::eColorAttachmentOutput, + vk::PipelineStageFlagBits::eColorAttachmentOutput, + vk::AccessFlags(), + vk::AccessFlagBits::eColorAttachmentWrite, + vk::DependencyFlags() + ); + + const vk::RenderPassCreateInfo passCreateInfo ( + vk::RenderPassCreateFlags(), + 1, + &attachment, + 1, + &subpass, + 1, + &dependency + ); + + m_render_pass = m_context.getDevice().createRenderPass(passCreateInfo); + + ImGui_ImplVulkan_Init(&init_info, static_cast<VkRenderPass>(m_render_pass)); + + const SubmitInfo submitInfo { QueueType::Graphics, {}, {} }; + + m_core.recordAndSubmitCommandsImmediate(submitInfo, [](const vk::CommandBuffer& commandBuffer) { + ImGui_ImplVulkan_CreateFontsTexture(static_cast<VkCommandBuffer>(commandBuffer)); + }, []() { + ImGui_ImplVulkan_DestroyFontUploadObjects(); + }); + + m_context.getDevice().waitIdle(); + } + + GUI::~GUI() { + m_context.getDevice().waitIdle(); + + ImGui_ImplVulkan_Shutdown(); + + m_context.getDevice().destroyRenderPass(m_render_pass); + m_context.getDevice().destroyDescriptorPool(m_descriptor_pool); + + ImGui_ImplGlfw_Shutdown(); + + m_window.e_mouseButton.remove(f_mouseButton); + m_window.e_mouseScroll.remove(f_mouseScroll); + m_window.e_key.remove(f_key); + m_window.e_char.remove(f_char); + + if (m_gui_context) { + ImGui::DestroyContext(m_gui_context); + } + } + + void GUI::beginGUI() { + const Swapchain& swapchain = m_core.getSwapchain(); + const auto extent = swapchain.getExtent(); + + if ((extent.width > 0) && (extent.height > 0)) { + ImGui_ImplVulkan_SetMinImageCount(swapchain.getImageCount()); + } + + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + } + + void GUI::endGUI() { + ImGui::Render(); + + ImDrawData* drawData = ImGui::GetDrawData(); + + if ((!drawData) || + (drawData->DisplaySize.x <= 0.0f) || + (drawData->DisplaySize.y <= 0.0f)) { + return; + } + + const Swapchain& swapchain = m_core.getSwapchain(); + const auto extent = swapchain.getExtent(); + + const vk::ImageView swapchainImageView = m_core.getSwapchainImageView(); + + const vk::FramebufferCreateInfo framebufferCreateInfo ( + vk::FramebufferCreateFlags(), + m_render_pass, + 1, + &swapchainImageView, + extent.width, + extent.height, + 1 + ); + + const vk::Framebuffer framebuffer = m_context.getDevice().createFramebuffer(framebufferCreateInfo); + + SubmitInfo submitInfo; + submitInfo.queueType = QueueType::Graphics; + + m_core.recordAndSubmitCommandsImmediate(submitInfo, [&](const vk::CommandBuffer& commandBuffer) { + const vk::Rect2D renderArea ( + vk::Offset2D(0, 0), + extent + ); + + const vk::RenderPassBeginInfo beginInfo ( + m_render_pass, + framebuffer, + renderArea, + 0, + nullptr + ); + + commandBuffer.beginRenderPass(beginInfo, vk::SubpassContents::eInline); + + ImGui_ImplVulkan_RenderDrawData(drawData, static_cast<VkCommandBuffer>(commandBuffer)); + + commandBuffer.endRenderPass(); + }, [&]() { + m_context.getDevice().destroyFramebuffer(framebuffer); + }); + } + +} diff --git a/modules/material/CMakeLists.txt b/modules/material/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d5b654cc6d00ce77d93b4666f48b7a5097e674b3 --- /dev/null +++ b/modules/material/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_material) + +# setting c++ standard for the module +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_material_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_material_include ${PROJECT_SOURCE_DIR}/include) + +# Add source and header files to the module +set(vkcv_material_sources + ${vkcv_material_include}/vkcv/material/Material.hpp + ${vkcv_material_source}/vkcv/material/Material.cpp + ${vkcv_material_include}/vkcv/material/PBRMaterial.hpp + ${vkcv_material_source}/vkcv/material/PBRMaterial.cpp +) + +# adding source files to the module +add_library(vkcv_material STATIC ${vkcv_material_sources}) + +# link the required libraries to the module +target_link_libraries(vkcv_material vkcv ${vkcv_libraries}) + +# including headers of dependencies and the VkCV framework +target_include_directories(vkcv_material SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes}) + +# add the own include directory for public headers +target_include_directories(vkcv_material BEFORE PUBLIC ${vkcv_material_include}) diff --git a/modules/material/include/vkcv/material/Material.hpp b/modules/material/include/vkcv/material/Material.hpp new file mode 100644 index 0000000000000000000000000000000000000000..00b492072fa4ef8b7b41f70202d515ee4ac828fa --- /dev/null +++ b/modules/material/include/vkcv/material/Material.hpp @@ -0,0 +1,14 @@ +#pragma once +#include <vkcv/Handles.hpp> + +namespace vkcv::material { + + class Material { + private: + public: + const DescriptorSetHandle m_DescriptorSetHandle; + protected: + Material(const DescriptorSetHandle& setHandle); + }; + +} diff --git a/modules/material/include/vkcv/material/PBRMaterial.hpp b/modules/material/include/vkcv/material/PBRMaterial.hpp new file mode 100644 index 0000000000000000000000000000000000000000..09a5214b0e748a09ef8caefe5bf2b1a69ecbd8e1 --- /dev/null +++ b/modules/material/include/vkcv/material/PBRMaterial.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include <vector> + +#include <vkcv/DescriptorConfig.hpp> +#include <vkcv/Core.hpp> + + +#include "Material.hpp" + +namespace vkcv::material +{ + class PBRMaterial : Material + { + private: + struct vec3 { + float x, y, z; + }; + struct vec4 { + float x, y, z, a; + }; + PBRMaterial(const ImageHandle& colorImg, + const SamplerHandle& colorSmp, + const ImageHandle& normalImg, + const SamplerHandle& normalSmp, + const ImageHandle& metRoughImg, + const SamplerHandle& metRoughSmp, + const ImageHandle& occlusionImg, + const SamplerHandle& occlusionSmp, + const ImageHandle& emissiveImg, + const SamplerHandle& emissiveSmp, + const DescriptorSetHandle& setHandle, + vec4 baseColorFactor, + float metallicFactor, + float roughnessFactor, + float normalScale, + float occlusionStrength, + vec3 emissiveFactor) noexcept; + + + public: + PBRMaterial() = delete; + + const ImageHandle m_ColorTexture; + const SamplerHandle m_ColorSampler; + + const ImageHandle m_NormalTexture; + const SamplerHandle m_NormalSampler; + + const ImageHandle m_MetRoughTexture; + const SamplerHandle m_MetRoughSampler; + + const ImageHandle m_OcclusionTexture; + const SamplerHandle m_OcclusionSampler; + + const ImageHandle m_EmissiveTexture; + const SamplerHandle m_EmissiveSampler; + + // + const vec4 m_BaseColorFactor; + const float m_MetallicFactor; + const float m_RoughnessFactor; + const float m_NormalScale; + const float m_OcclusionStrength; + const vec3 m_EmissiveFactor; + + /* + * Returns the material's necessary descriptor bindings which serves as its descriptor layout + * The binding is in the following order: + * 0 - diffuse texture + * 1 - diffuse sampler + * 2 - normal texture + * 3 - normal sampler + * 4 - metallic roughness texture + * 5 - metallic roughness sampler + * 6 - occlusion texture + * 7 - occlusion sampler + * 8 - emissive texture + * 9 - emissive sampler + */ + static std::vector<DescriptorBinding> getDescriptorBindings() noexcept; + + static PBRMaterial create( + vkcv::Core* core, + ImageHandle &colorImg, + SamplerHandle &colorSmp, + ImageHandle &normalImg, + SamplerHandle &normalSmp, + ImageHandle &metRoughImg, + SamplerHandle &metRoughSmp, + ImageHandle &occlusionImg, + SamplerHandle &occlusionSmp, + ImageHandle &emissiveImg, + SamplerHandle &emissiveSmp, + vec4 baseColorFactor, + float metallicFactor, + float roughnessFactor, + float normalScale, + float occlusionStrength, + vec3 emissiveFactor); + + }; +} \ No newline at end of file diff --git a/modules/material/src/vkcv/material/Material.cpp b/modules/material/src/vkcv/material/Material.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9168bcfbf924e9868ceaaff74aef5d3c6b99739c --- /dev/null +++ b/modules/material/src/vkcv/material/Material.cpp @@ -0,0 +1,12 @@ + +#include "vkcv/material/Material.hpp" + +namespace vkcv::material { + + //TODO + + Material::Material(const DescriptorSetHandle& setHandle) : m_DescriptorSetHandle(setHandle) + { + } + +} diff --git a/modules/material/src/vkcv/material/PBRMaterial.cpp b/modules/material/src/vkcv/material/PBRMaterial.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d27e755c06a39e369d22efc997a0b411d067c132 --- /dev/null +++ b/modules/material/src/vkcv/material/PBRMaterial.cpp @@ -0,0 +1,194 @@ +#include "vkcv/material/PBRMaterial.hpp" + + +namespace vkcv::material +{ + PBRMaterial::PBRMaterial( + const ImageHandle& colorImg, + const SamplerHandle& colorSmp, + const ImageHandle& normalImg, + const SamplerHandle& normalSmp, + const ImageHandle& metRoughImg, + const SamplerHandle& metRoughSmp, + const ImageHandle& occlusionImg, + const SamplerHandle& occlusionSmp, + const ImageHandle& emissiveImg, + const SamplerHandle& emissiveSmp, + const DescriptorSetHandle& setHandle, + vec4 baseColorFactor, + float metallicFactor, + float roughnessFactor, + float normalScale, + float occlusionStrength, + vec3 emissiveFactor) noexcept : + m_ColorTexture(colorImg), + m_ColorSampler(colorSmp), + m_NormalTexture(normalImg), + m_NormalSampler(normalSmp), + m_MetRoughTexture(metRoughImg), + m_MetRoughSampler(metRoughSmp), + m_OcclusionTexture(occlusionImg), + m_OcclusionSampler(occlusionSmp), + m_EmissiveTexture(emissiveImg), + m_EmissiveSampler(emissiveSmp), + Material(setHandle), + m_BaseColorFactor(baseColorFactor), + m_MetallicFactor(metallicFactor), + m_RoughnessFactor(roughnessFactor), + m_NormalScale(normalScale), + m_OcclusionStrength(occlusionStrength), + m_EmissiveFactor(emissiveFactor) + { + } + + std::vector<DescriptorBinding> PBRMaterial::getDescriptorBindings() noexcept + { + static std::vector<DescriptorBinding> bindings; + + if (bindings.empty()) { + bindings.emplace_back(0, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT); + bindings.emplace_back(1, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT); + bindings.emplace_back(2, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT); + bindings.emplace_back(3, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT); + bindings.emplace_back(4, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT); + bindings.emplace_back(5, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT); + bindings.emplace_back(6, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT); + bindings.emplace_back(7, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT); + bindings.emplace_back(8, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT); + bindings.emplace_back(9, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT); + } + + return bindings; + } + + PBRMaterial PBRMaterial::create( + vkcv::Core* core, + ImageHandle& colorImg, + SamplerHandle& colorSmp, + ImageHandle& normalImg, + SamplerHandle& normalSmp, + ImageHandle& metRoughImg, + SamplerHandle& metRoughSmp, + ImageHandle& occlusionImg, + SamplerHandle& occlusionSmp, + ImageHandle& emissiveImg, + SamplerHandle& emissiveSmp, + vec4 baseColorFactor, + float metallicFactor, + float roughnessFactor, + float normalScale, + float occlusionStrength, + vec3 emissiveFactor) + { + //Test if Images and samplers valid, if not use default + if (!colorImg) { + vkcv::Image defaultColor = core->createImage(vk::Format::eR8G8B8A8Srgb, 1, 1); + vec4 colorData{ 228, 51, 255,1 }; + defaultColor.fill(&colorData); + colorImg = defaultColor.getHandle(); + } + if (!normalImg) { + vkcv::Image defaultNormal = core->createImage(vk::Format::eR8G8B8A8Srgb, 1, 1); + vec4 normalData{ 0, 0, 1,0 }; + defaultNormal.fill(&normalData); + normalImg = defaultNormal.getHandle(); + } + if (!metRoughImg) { + vkcv::Image defaultRough = core->createImage(vk::Format::eR8G8B8A8Srgb, 1, 1); + vec4 roughData{ 228, 51, 255,1 }; + defaultRough.fill(&roughData); + metRoughImg = defaultRough.getHandle(); + } + if (!occlusionImg) { + vkcv::Image defaultOcclusion = core->createImage(vk::Format::eR8G8B8A8Srgb, 1, 1); + vec4 occlusionData{ 228, 51, 255,1 }; + defaultOcclusion.fill(&occlusionData); + occlusionImg = defaultOcclusion.getHandle(); + } + if (!emissiveImg) { + vkcv::Image defaultEmissive = core->createImage(vk::Format::eR8G8B8A8Srgb, 1, 1); + vec4 emissiveData{ 0, 0, 0,1 }; + defaultEmissive.fill(&emissiveData); + emissiveImg = defaultEmissive.getHandle(); + } + if (!colorSmp) { + colorSmp = core->createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT + ); + } + if (!normalSmp) { + normalSmp = core->createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT + ); + } + if (!metRoughSmp) { + metRoughSmp = core->createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT + ); + } + if (!occlusionSmp) { + occlusionSmp = core->createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT + ); + } + if (!emissiveSmp) { + emissiveSmp = core->createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT + ); + } + + + + //create descriptorset + vkcv::DescriptorSetHandle descriptorSetHandle = core->createDescriptorSet(getDescriptorBindings()); + //writes + vkcv::DescriptorWrites setWrites; + setWrites.sampledImageWrites = { + vkcv::SampledImageDescriptorWrite(0, colorImg), + vkcv::SampledImageDescriptorWrite(2, normalImg), + vkcv::SampledImageDescriptorWrite(4, metRoughImg), + vkcv::SampledImageDescriptorWrite(6, occlusionImg), + vkcv::SampledImageDescriptorWrite(8, emissiveImg) }; + setWrites.samplerWrites = { + vkcv::SamplerDescriptorWrite(1, colorSmp), + vkcv::SamplerDescriptorWrite(3, normalSmp), + vkcv::SamplerDescriptorWrite(5, metRoughSmp), + vkcv::SamplerDescriptorWrite(7, occlusionSmp), + vkcv::SamplerDescriptorWrite(9, emissiveSmp) }; + core->writeDescriptorSet(descriptorSetHandle, setWrites); + + return PBRMaterial( + colorImg, + colorSmp, + normalImg, + normalSmp, + metRoughImg, + metRoughSmp, + occlusionImg, + occlusionSmp, + emissiveImg, + emissiveSmp, + descriptorSetHandle, + baseColorFactor, + metallicFactor, + roughnessFactor, + normalScale, + occlusionStrength, + emissiveFactor); + } +} \ No newline at end of file diff --git a/modules/shader_compiler/CMakeLists.txt b/modules/shader_compiler/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..4b674ec41ed4ea5f42dc73187c212e6a69952cec --- /dev/null +++ b/modules/shader_compiler/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_shader_compiler) + +# setting c++ standard for the module +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_shader_compiler_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_shader_compiler_include ${PROJECT_SOURCE_DIR}/include) + +# Add source and header files to the module +set(vkcv_shader_compiler_sources + ${vkcv_shader_compiler_include}/vkcv/shader/GLSLCompiler.hpp + ${vkcv_shader_compiler_source}/vkcv/shader/GLSLCompiler.cpp +) + +# adding source files to the module +add_library(vkcv_shader_compiler STATIC ${vkcv_shader_compiler_sources}) + +# Setup some path variables to load libraries +set(vkcv_shader_compiler_lib lib) +set(vkcv_shader_compiler_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_shader_compiler_lib}) + +# Check and load GLSLANG +include(config/GLSLANG.cmake) + +# link the required libraries to the module +target_link_libraries(vkcv_shader_compiler ${vkcv_shader_compiler_libraries} vkcv) + +# including headers of dependencies and the VkCV framework +target_include_directories(vkcv_shader_compiler SYSTEM BEFORE PRIVATE ${vkcv_shader_compiler_includes} ${vkcv_include}) + +# add the own include directory for public headers +target_include_directories(vkcv_shader_compiler BEFORE PUBLIC ${vkcv_shader_compiler_include}) diff --git a/modules/shader_compiler/config/GLSLANG.cmake b/modules/shader_compiler/config/GLSLANG.cmake new file mode 100644 index 0000000000000000000000000000000000000000..50b9fd46bd0db9421c632aa0b80fb8df7e3f2123 --- /dev/null +++ b/modules/shader_compiler/config/GLSLANG.cmake @@ -0,0 +1,28 @@ + +if (EXISTS "${vkcv_shader_compiler_lib_path}/glslang") + set(SKIP_GLSLANG_INSTALL ON CACHE INTERNAL "") + set(ENABLE_SPVREMAPPER OFF CACHE INTERNAL "") + set(ENABLE_GLSLANG_BINARIES OFF CACHE INTERNAL "") + set(ENABLE_GLSLANG_JS OFF CACHE INTERNAL "") + set(ENABLE_GLSLANG_WEBMIN OFF CACHE INTERNAL "") + set(ENABLE_GLSLANG_WEBMIN_DEVEL OFF CACHE INTERNAL "") + set(ENABLE_EMSCRIPTEN_SINGLE_FILE OFF CACHE INTERNAL "") + set(ENABLE_EMSCRIPTEN_ENVIRONMENT_NODE OFF CACHE INTERNAL "") + set(ENABLE_HLSL OFF CACHE INTERNAL "") + set(ENABLE_RTTI OFF CACHE INTERNAL "") + set(ENABLE_EXCEPTIONS OFF CACHE INTERNAL "") + set(ENABLE_OPT OFF CACHE INTERNAL "") + set(ENABLE_PCH OFF CACHE INTERNAL "") + set(ENABLE_CTEST OFF CACHE INTERNAL "") + set(USE_CCACHE OFF CACHE INTERNAL "") + + set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "") + set(BUILD_EXTERNAL OFF CACHE INTERNAL "") + + add_subdirectory(${vkcv_shader_compiler_lib}/glslang) + + list(APPEND vkcv_shader_compiler_libraries glslang SPIRV) + list(APPEND vkcv_shader_compiler_includes ${vkcv_shader_compiler_lib}) +else() + message(WARNING "GLSLANG is required..! Update the submodules!") +endif () diff --git a/modules/shader_compiler/include/vkcv/shader/Compiler.hpp b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d7b7af7178531aea358cecbc8b86a29527173014 --- /dev/null +++ b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include <vkcv/Event.hpp> + +namespace vkcv::shader { + + typedef typename event_function<ShaderStage, const std::filesystem::path&>::type ShaderCompiledFunction; + + class Compiler { + private: + public: + virtual void compile(ShaderStage shaderStage, const std::filesystem::path& shaderPath, + const ShaderCompiledFunction& compiled, bool update = false) = 0; + + }; + +} diff --git a/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp b/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7105d93a0c3e153bf3abe1d624d0c13c6f09ac6d --- /dev/null +++ b/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include <filesystem> + +#include <vkcv/ShaderStage.hpp> +#include "Compiler.hpp" + +namespace vkcv::shader { + + class GLSLCompiler { + private: + public: + GLSLCompiler(); + + GLSLCompiler(const GLSLCompiler& other); + GLSLCompiler(GLSLCompiler&& other) = default; + + ~GLSLCompiler(); + + GLSLCompiler& operator=(const GLSLCompiler& other); + GLSLCompiler& operator=(GLSLCompiler&& other) = default; + + void compile(ShaderStage shaderStage, const std::filesystem::path& shaderPath, + const ShaderCompiledFunction& compiled, bool update = false); + + }; + +} diff --git a/modules/shader_compiler/lib/glslang b/modules/shader_compiler/lib/glslang new file mode 160000 index 0000000000000000000000000000000000000000..fe15158676657bf965e41c32e15ae5db7ea2ab6a --- /dev/null +++ b/modules/shader_compiler/lib/glslang @@ -0,0 +1 @@ +Subproject commit fe15158676657bf965e41c32e15ae5db7ea2ab6a diff --git a/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ec358188b8e871da6f4d62ffd397f32bfb795ee2 --- /dev/null +++ b/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp @@ -0,0 +1,283 @@ + +#include "vkcv/shader/GLSLCompiler.hpp" + +#include <fstream> +#include <glslang/SPIRV/GlslangToSpv.h> +#include <glslang/StandAlone/DirStackFileIncluder.h> + +#include <vkcv/Logger.hpp> + +namespace vkcv::shader { + + static uint32_t s_CompilerCount = 0; + + GLSLCompiler::GLSLCompiler() { + if (s_CompilerCount == 0) { + glslang::InitializeProcess(); + } + + s_CompilerCount++; + } + + GLSLCompiler::GLSLCompiler(const GLSLCompiler &other) { + s_CompilerCount++; + } + + GLSLCompiler::~GLSLCompiler() { + s_CompilerCount--; + + if (s_CompilerCount == 0) { + glslang::FinalizeProcess(); + } + } + + GLSLCompiler &GLSLCompiler::operator=(const GLSLCompiler &other) { + s_CompilerCount++; + return *this; + } + + constexpr EShLanguage findShaderLanguage(ShaderStage shaderStage) { + switch (shaderStage) { + case ShaderStage::VERTEX: + return EShLangVertex; + case ShaderStage::TESS_CONTROL: + return EShLangTessControl; + case ShaderStage::TESS_EVAL: + return EShLangTessEvaluation; + case ShaderStage::GEOMETRY: + return EShLangGeometry; + case ShaderStage::FRAGMENT: + return EShLangFragment; + case ShaderStage::COMPUTE: + return EShLangCompute; + default: + return EShLangCount; + } + } + + static void initResources(TBuiltInResource& resources) { + resources.maxLights = 32; + resources.maxClipPlanes = 6; + resources.maxTextureUnits = 32; + resources.maxTextureCoords = 32; + resources.maxVertexAttribs = 64; + resources.maxVertexUniformComponents = 4096; + resources.maxVaryingFloats = 64; + resources.maxVertexTextureImageUnits = 32; + resources.maxCombinedTextureImageUnits = 80; + resources.maxTextureImageUnits = 32; + resources.maxFragmentUniformComponents = 4096; + resources.maxDrawBuffers = 32; + resources.maxVertexUniformVectors = 128; + resources.maxVaryingVectors = 8; + resources.maxFragmentUniformVectors = 16; + resources.maxVertexOutputVectors = 16; + resources.maxFragmentInputVectors = 15; + resources.minProgramTexelOffset = -8; + resources.maxProgramTexelOffset = 7; + resources.maxClipDistances = 8; + resources.maxComputeWorkGroupCountX = 65535; + resources.maxComputeWorkGroupCountY = 65535; + resources.maxComputeWorkGroupCountZ = 65535; + resources.maxComputeWorkGroupSizeX = 1024; + resources.maxComputeWorkGroupSizeY = 1024; + resources.maxComputeWorkGroupSizeZ = 64; + resources.maxComputeUniformComponents = 1024; + resources.maxComputeTextureImageUnits = 16; + resources.maxComputeImageUniforms = 8; + resources.maxComputeAtomicCounters = 8; + resources.maxComputeAtomicCounterBuffers = 1; + resources.maxVaryingComponents = 60; + resources.maxVertexOutputComponents = 64; + resources.maxGeometryInputComponents = 64; + resources.maxGeometryOutputComponents = 128; + resources.maxFragmentInputComponents = 128; + resources.maxImageUnits = 8; + resources.maxCombinedImageUnitsAndFragmentOutputs = 8; + resources.maxCombinedShaderOutputResources = 8; + resources.maxImageSamples = 0; + resources.maxVertexImageUniforms = 0; + resources.maxTessControlImageUniforms = 0; + resources.maxTessEvaluationImageUniforms = 0; + resources.maxGeometryImageUniforms = 0; + resources.maxFragmentImageUniforms = 8; + resources.maxCombinedImageUniforms = 8; + resources.maxGeometryTextureImageUnits = 16; + resources.maxGeometryOutputVertices = 256; + resources.maxGeometryTotalOutputComponents = 1024; + resources.maxGeometryUniformComponents = 1024; + resources.maxGeometryVaryingComponents = 64; + resources.maxTessControlInputComponents = 128; + resources.maxTessControlOutputComponents = 128; + resources.maxTessControlTextureImageUnits = 16; + resources.maxTessControlUniformComponents = 1024; + resources.maxTessControlTotalOutputComponents = 4096; + resources.maxTessEvaluationInputComponents = 128; + resources.maxTessEvaluationOutputComponents = 128; + resources.maxTessEvaluationTextureImageUnits = 16; + resources.maxTessEvaluationUniformComponents = 1024; + resources.maxTessPatchComponents = 120; + resources.maxPatchVertices = 32; + resources.maxTessGenLevel = 64; + resources.maxViewports = 16; + resources.maxVertexAtomicCounters = 0; + resources.maxTessControlAtomicCounters = 0; + resources.maxTessEvaluationAtomicCounters = 0; + resources.maxGeometryAtomicCounters = 0; + resources.maxFragmentAtomicCounters = 8; + resources.maxCombinedAtomicCounters = 8; + resources.maxAtomicCounterBindings = 1; + resources.maxVertexAtomicCounterBuffers = 0; + resources.maxTessControlAtomicCounterBuffers = 0; + resources.maxTessEvaluationAtomicCounterBuffers = 0; + resources.maxGeometryAtomicCounterBuffers = 0; + resources.maxFragmentAtomicCounterBuffers = 1; + resources.maxCombinedAtomicCounterBuffers = 1; + resources.maxAtomicCounterBufferSize = 16384; + resources.maxTransformFeedbackBuffers = 4; + resources.maxTransformFeedbackInterleavedComponents = 64; + resources.maxCullDistances = 8; + resources.maxCombinedClipAndCullDistances = 8; + resources.maxSamples = 4; + resources.maxMeshOutputVerticesNV = 256; + resources.maxMeshOutputPrimitivesNV = 512; + resources.maxMeshWorkGroupSizeX_NV = 32; + resources.maxMeshWorkGroupSizeY_NV = 1; + resources.maxMeshWorkGroupSizeZ_NV = 1; + resources.maxTaskWorkGroupSizeX_NV = 32; + resources.maxTaskWorkGroupSizeY_NV = 1; + resources.maxTaskWorkGroupSizeZ_NV = 1; + resources.maxMeshViewCountNV = 4; + resources.limits.nonInductiveForLoops = 1; + resources.limits.whileLoops = 1; + resources.limits.doWhileLoops = 1; + resources.limits.generalUniformIndexing = 1; + resources.limits.generalAttributeMatrixVectorIndexing = 1; + resources.limits.generalVaryingIndexing = 1; + resources.limits.generalSamplerIndexing = 1; + resources.limits.generalVariableIndexing = 1; + resources.limits.generalConstantMatrixVectorIndexing = 1; + } + + static std::vector<char> readShaderCode(const std::filesystem::path &shaderPath) { + std::ifstream file (shaderPath.string(), std::ios::ate); + + if (!file.is_open()) { + vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str()); + return std::vector<char>{}; + } + + std::streamsize fileSize = file.tellg(); + std::vector<char> buffer (fileSize + 1); + + file.seekg(0); + file.read(buffer.data(), fileSize); + file.close(); + + buffer[fileSize] = '\0'; + return buffer; + } + + static bool writeSpirvCode(const std::filesystem::path &shaderPath, const std::vector<uint32_t>& spirv) { + std::ofstream file (shaderPath.string(), std::ios::out | std::ios::binary); + + if (!file.is_open()) { + vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str()); + return false; + } + + const auto fileSize = static_cast<std::streamsize>( + sizeof(uint32_t) * spirv.size() + ); + + file.seekp(0); + file.write(reinterpret_cast<const char*>(spirv.data()), fileSize); + file.close(); + + return true; + } + + void GLSLCompiler::compile(ShaderStage shaderStage, const std::filesystem::path &shaderPath, + const ShaderCompiledFunction& compiled, bool update) { + const EShLanguage language = findShaderLanguage(shaderStage); + + if (language == EShLangCount) { + vkcv_log(LogLevel::ERROR, "Shader stage not supported (%s)", shaderPath.string().c_str()); + return; + } + + const std::vector<char> code = readShaderCode(shaderPath); + + glslang::TShader shader (language); + glslang::TProgram program; + + const char *shaderStrings [1]; + shaderStrings[0] = code.data(); + + shader.setStrings(shaderStrings, 1); + + TBuiltInResource resources = {}; + initResources(resources); + + const auto messages = (EShMessages)( + EShMsgSpvRules | + EShMsgVulkanRules + ); + + std::string preprocessedGLSL; + + DirStackFileIncluder includer; + includer.pushExternalLocalDirectory(shaderPath.parent_path().string()); + + if (!shader.preprocess(&resources, 100, ENoProfile, false, false, messages, &preprocessedGLSL, includer)) { + vkcv_log(LogLevel::ERROR, "Shader parsing failed {\n%s\n%s\n} (%s)", + shader.getInfoLog(), shader.getInfoDebugLog(), shaderPath.string().c_str()); + return; + } + + const char* preprocessedCString = preprocessedGLSL.c_str(); + shader.setStrings(&preprocessedCString, 1); + + if (!shader.parse(&resources, 100, false, messages)) { + vkcv_log(LogLevel::ERROR, "Shader parsing failed {\n%s\n%s\n} (%s)", + shader.getInfoLog(), shader.getInfoDebugLog(), shaderPath.string().c_str()); + return; + } + + program.addShader(&shader); + + if (!program.link(messages)) { + vkcv_log(LogLevel::ERROR, "Shader linking failed {\n%s\n%s\n} (%s)", + shader.getInfoLog(), shader.getInfoDebugLog(), shaderPath.string().c_str()); + return; + } + + const glslang::TIntermediate* intermediate = program.getIntermediate(language); + + if (!intermediate) { + vkcv_log(LogLevel::ERROR, "No valid intermediate representation (%s)", shaderPath.string().c_str()); + return; + } + + std::vector<uint32_t> spirv; + glslang::GlslangToSpv(*intermediate, spirv); + + const std::filesystem::path tmp_path (std::tmpnam(nullptr)); + + if (!writeSpirvCode(tmp_path, spirv)) { + vkcv_log(LogLevel::ERROR, "Spir-V could not be written to disk (%s)", shaderPath.string().c_str()); + return; + } + + if (compiled) { + compiled(shaderStage, tmp_path); + } + + std::filesystem::remove(tmp_path); + + if (update) { + // TODO: Shader hot compilation during runtime + } + } + +} diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt index 7ca73a0811df7f1568508b56312ce3348237a695..34fbcb0cf8dd3f1d34efd2cc8424994c7da76e32 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -1,4 +1,7 @@ # Add new projects/examples here: +add_subdirectory(bloom) add_subdirectory(first_triangle) add_subdirectory(first_mesh) +add_subdirectory(first_scene) +add_subdirectory(voxelization) \ No newline at end of file diff --git a/projects/bloom/.gitignore b/projects/bloom/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3643183e0628e666abab193e1dd1d92c1774ac61 --- /dev/null +++ b/projects/bloom/.gitignore @@ -0,0 +1 @@ +bloom \ No newline at end of file diff --git a/projects/bloom/CMakeLists.txt b/projects/bloom/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8171938e7cb430aacce5562af44f628c11c97c54 --- /dev/null +++ b/projects/bloom/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.16) +project(bloom) + +# 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(bloom src/main.cpp) + +target_sources(bloom PRIVATE + src/BloomAndFlares.cpp + src/BloomAndFlares.hpp) + +# this should fix the execution path to load local files from the project (for MSVC) +if(MSVC) + set_target_properties(bloom PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set_target_properties(bloom 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(bloom PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +endif() + +# including headers of dependencies and the VkCV framework +target_include_directories(bloom SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(bloom vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler) diff --git a/projects/bloom/resources/Sponza/Sponza.bin b/projects/bloom/resources/Sponza/Sponza.bin new file mode 100644 index 0000000000000000000000000000000000000000..cfedd26ca5a67b6d0a47d44d13a75e14a141717a --- /dev/null +++ b/projects/bloom/resources/Sponza/Sponza.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b809f7a17687dc99e6f41ca1ea32c06eded8779bf34d16f1f565d750b0ffd68 +size 6347696 diff --git a/projects/bloom/resources/Sponza/Sponza.gltf b/projects/bloom/resources/Sponza/Sponza.gltf new file mode 100644 index 0000000000000000000000000000000000000000..172ea07e21c94465211c860cd805355704cef230 --- /dev/null +++ b/projects/bloom/resources/Sponza/Sponza.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5cc0ecad5c4694088ff820e663619c370421afc1323ac487406e8e9b4735d787 +size 713962 diff --git a/projects/bloom/resources/Sponza/background.png b/projects/bloom/resources/Sponza/background.png new file mode 100644 index 0000000000000000000000000000000000000000..b64def129da38f4e23d89e21b4af1039008a4327 --- /dev/null +++ b/projects/bloom/resources/Sponza/background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5b5f900ff8ed83a31750ec8e428b5b91273794ddcbfc4e4b8a6a7e781f8c686 +size 1417666 diff --git a/projects/bloom/resources/Sponza/chain_texture.png b/projects/bloom/resources/Sponza/chain_texture.png new file mode 100644 index 0000000000000000000000000000000000000000..c1e1768cff78e0614ad707eca8602a4c4edab5e5 --- /dev/null +++ b/projects/bloom/resources/Sponza/chain_texture.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8362cfd472880daeaea37439326a4651d1338680ae69bb2513fc6b17c8de7d4 +size 490895 diff --git a/projects/bloom/resources/Sponza/lion.png b/projects/bloom/resources/Sponza/lion.png new file mode 100644 index 0000000000000000000000000000000000000000..c49c7f0ed31e762e19284d0d3624fbc47664e56b --- /dev/null +++ b/projects/bloom/resources/Sponza/lion.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f882f746c3a9cd51a9c6eedc1189b97668721d91a3fe49232036e789912c652 +size 2088728 diff --git a/projects/bloom/resources/Sponza/spnza_bricks_a_diff.png b/projects/bloom/resources/Sponza/spnza_bricks_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..cde4c7a6511e9a5f03c63ad996437fcdba3ce2df --- /dev/null +++ b/projects/bloom/resources/Sponza/spnza_bricks_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b94219c2f5f943f3f4715c74e7d1038bf0ab3b3b3216a758eaee67f875df0851 +size 1928829 diff --git a/projects/bloom/resources/Sponza/sponza_arch_diff.png b/projects/bloom/resources/Sponza/sponza_arch_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..bcd9bda2918d226039f9e2d03902d377b706fab6 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_arch_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0df2c8a01b2843b1c792b494f7173cdbc4f834840fc2177af3e5d690fceda57 +size 1596151 diff --git a/projects/bloom/resources/Sponza/sponza_ceiling_a_diff.png b/projects/bloom/resources/Sponza/sponza_ceiling_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..59de631ffac4414cabf69b2dc794c46fc187d6cb --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_ceiling_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab6c187a81aa68f4eba30119e17fce2e4882a9ec320f70c90482dbe9da82b1c6 +size 1872074 diff --git a/projects/bloom/resources/Sponza/sponza_column_a_diff.png b/projects/bloom/resources/Sponza/sponza_column_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..01a82432d3f9939bbefe850bdb900f1ff9a3f6db --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_column_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c291507e2808bb83e160ab4b020689817df273baad3713a9ad19ac15fac6826 +size 1840992 diff --git a/projects/bloom/resources/Sponza/sponza_column_b_diff.png b/projects/bloom/resources/Sponza/sponza_column_b_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..10a660cce2a5a9b8997772c746058ce23e7d45d7 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_column_b_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2820b0267c4289c6cedbb42721792a57ef244ec2d0935941011c2a7d3fe88a9b +size 2170433 diff --git a/projects/bloom/resources/Sponza/sponza_column_c_diff.png b/projects/bloom/resources/Sponza/sponza_column_c_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..bc46fd979044a938d3adca7601689e71504e48bf --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_column_c_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0bc993ff59865468ef4530798930c7dfefb07482d71db45bc2a520986b27735 +size 2066950 diff --git a/projects/bloom/resources/Sponza/sponza_curtain_blue_diff.png b/projects/bloom/resources/Sponza/sponza_curtain_blue_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..384c8c2c051160d530eb3ac8b05c9c60752a2d2b --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_curtain_blue_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b85c6bb3cd5105f48d3812ec8e7a1068521ce69e917300d79e136e19d45422fb +size 9510905 diff --git a/projects/bloom/resources/Sponza/sponza_curtain_diff.png b/projects/bloom/resources/Sponza/sponza_curtain_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..af842e9f5fe18c1f609875e00899a6770fa4488b --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_curtain_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:563c56bdbbee395a6ef7f0c51c8ac9223c162e517b4cdba0d4654e8de27c98d8 +size 9189263 diff --git a/projects/bloom/resources/Sponza/sponza_curtain_green_diff.png b/projects/bloom/resources/Sponza/sponza_curtain_green_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..6c9b6391a199407637fa71033d79fb58b8b4f0d7 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_curtain_green_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:238fe1c7f481388d1c1d578c2da8d411b99e8f0030ab62060a306db333124476 +size 8785458 diff --git a/projects/bloom/resources/Sponza/sponza_details_diff.png b/projects/bloom/resources/Sponza/sponza_details_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..12656686362c3e0a297e060491f33bd7351551f9 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_details_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb1223b3bb82f8757e7df25a6891f1239cdd7ec59990340e952fb2d6b7ea570c +size 1522643 diff --git a/projects/bloom/resources/Sponza/sponza_fabric_blue_diff.png b/projects/bloom/resources/Sponza/sponza_fabric_blue_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..879d16ef84722a4fc13e83a771778de326e4bc54 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_fabric_blue_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:467d290bf5d4b2a017da140ba9e244ed8a8a9be5418a9ac9bcb4ad572ae2d7ab +size 2229440 diff --git a/projects/bloom/resources/Sponza/sponza_fabric_diff.png b/projects/bloom/resources/Sponza/sponza_fabric_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..3311287a219d2148620b87fe428fea071688d051 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_fabric_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1594f59cc2848db26add47361f4e665e3d8afa147760ed915d839fea42b20287 +size 2267382 diff --git a/projects/bloom/resources/Sponza/sponza_fabric_green_diff.png b/projects/bloom/resources/Sponza/sponza_fabric_green_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..de110f369004388dae4cd5067c63428db3a07834 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_fabric_green_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:902b87faab221173bf370cea7c74cb9060b4d870ac6316b190dafded1cb12993 +size 2258220 diff --git a/projects/bloom/resources/Sponza/sponza_flagpole_diff.png b/projects/bloom/resources/Sponza/sponza_flagpole_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..5f6e0812a0df80346318baa3cb50a6888afc58f8 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_flagpole_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bfffb62e770959c725d0f3db6dc7dbdd46a380ec55ef884dab94d44ca017b438 +size 1425673 diff --git a/projects/bloom/resources/Sponza/sponza_floor_a_diff.png b/projects/bloom/resources/Sponza/sponza_floor_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..788ed764f79ba724f04a2d603076a5b85013e188 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_floor_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a16f9230fa91f9f31dfca6216ce205f1ef132d44f3b012fbf6efc0fba69770ab +size 1996838 diff --git a/projects/bloom/resources/Sponza/sponza_roof_diff.png b/projects/bloom/resources/Sponza/sponza_roof_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..c5b84261fdd1cc776a94b3ce398c7806b895f9a3 --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_roof_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7fc412138c20da19f8173e53545e771f4652558dff624d4dc67143e40efe562b +size 2320533 diff --git a/projects/bloom/resources/Sponza/sponza_thorn_diff.png b/projects/bloom/resources/Sponza/sponza_thorn_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..7a9142674a7d4a6f94a48c5152cf0300743b597a --- /dev/null +++ b/projects/bloom/resources/Sponza/sponza_thorn_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a73a17c883cd0d0d67cfda2dc4118400a916366c05b9a5ac465f0c8b30fd9c8e +size 635001 diff --git a/projects/bloom/resources/Sponza/vase_dif.png b/projects/bloom/resources/Sponza/vase_dif.png new file mode 100644 index 0000000000000000000000000000000000000000..61236a81cb324af8797b05099cd264cefe189e56 --- /dev/null +++ b/projects/bloom/resources/Sponza/vase_dif.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53d06f52bf9e59df4cf00237707cca76c4f692bda61a62b06a30d321311d6dd9 +size 1842101 diff --git a/projects/bloom/resources/Sponza/vase_hanging.png b/projects/bloom/resources/Sponza/vase_hanging.png new file mode 100644 index 0000000000000000000000000000000000000000..36a3cee71d8213225090c74f8c0dce33b9d44378 --- /dev/null +++ b/projects/bloom/resources/Sponza/vase_hanging.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9d10b4f27a3c9a78d5bac882fdd4b6a6987c262f48fa490670fe5e235951e31 +size 1432804 diff --git a/projects/bloom/resources/Sponza/vase_plant.png b/projects/bloom/resources/Sponza/vase_plant.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad95e702e229f1ebd803e5203a266d15f2c07b9 --- /dev/null +++ b/projects/bloom/resources/Sponza/vase_plant.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2087371ff02212fb7014b6daefa191cf5676d2227193fff261a5d02f554cb8e +size 998089 diff --git a/projects/bloom/resources/Sponza/vase_round.png b/projects/bloom/resources/Sponza/vase_round.png new file mode 100644 index 0000000000000000000000000000000000000000..c17953abc000c44b8991e23c136c2b67348f3d1b --- /dev/null +++ b/projects/bloom/resources/Sponza/vase_round.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa23d48d492d5d4ada2ddb27d1ef22952b214e6eb3b301c65f9d88442723d20a +size 1871399 diff --git a/projects/bloom/resources/shaders/comp.spv b/projects/bloom/resources/shaders/comp.spv new file mode 100644 index 0000000000000000000000000000000000000000..85c7e74cfc0a89917bf6dd1a7ec449368274c1d3 Binary files /dev/null and b/projects/bloom/resources/shaders/comp.spv differ diff --git a/projects/bloom/resources/shaders/composite.comp b/projects/bloom/resources/shaders/composite.comp new file mode 100644 index 0000000000000000000000000000000000000000..190bed0657d70e0217bf654820d0b2b2c58f12c2 --- /dev/null +++ b/projects/bloom/resources/shaders/composite.comp @@ -0,0 +1,38 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D blurImage; +layout(set=0, binding=1) uniform texture2D lensImage; +layout(set=0, binding=2) uniform sampler linearSampler; +layout(set=0, binding=3, r11f_g11f_b10f) uniform image2D colorBuffer; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(colorBuffer)))){ + return; + } + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / textureSize(sampler2D(blurImage, linearSampler), 0); + vec2 UV = pixel_coord.xy * pixel_size; + + vec4 composite_color = vec4(0.0f); + + vec3 blur_color = texture(sampler2D(blurImage, linearSampler), UV).rgb; + vec3 lens_color = texture(sampler2D(lensImage, linearSampler), UV).rgb; + vec3 main_color = imageLoad(colorBuffer, pixel_coord).rgb; + + // composite blur and lens features + float bloom_weight = 0.25f; + float lens_weight = 0.25f; + float main_weight = 1 - (bloom_weight + lens_weight); + + composite_color.rgb = blur_color * bloom_weight + + lens_color * lens_weight + + main_color * main_weight; + + imageStore(colorBuffer, pixel_coord, composite_color); +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/downsample.comp b/projects/bloom/resources/shaders/downsample.comp new file mode 100644 index 0000000000000000000000000000000000000000..2ab00c7c92798769153634f3479c5b7f3fb61d94 --- /dev/null +++ b/projects/bloom/resources/shaders/downsample.comp @@ -0,0 +1,76 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D inBlurImage; +layout(set=0, binding=1) uniform sampler inImageSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform writeonly image2D outBlurImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outBlurImage)))){ + return; + } + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / imageSize(outBlurImage); + vec2 UV = pixel_coord.xy * pixel_size; + vec2 UV_offset = UV + 0.5f * pixel_size; + + vec2 color_fetches[13] = { + // center neighbourhood (RED) + vec2(-1, 1), // LT + vec2(-1, -1), // LB + vec2( 1, -1), // RB + vec2( 1, 1), // RT + + vec2(-2, 2), // LT + vec2( 0, 2), // CT + vec2( 2, 2), // RT + + vec2(0 ,-2), // LC + vec2(0 , 0), // CC + vec2(2, 0), // CR + + vec2(-2, -2), // LB + vec2(0 , -2), // CB + vec2(2 , -2) // RB + }; + + float color_weights[13] = { + // 0.5f + 1.f/8.f, + 1.f/8.f, + 1.f/8.f, + 1.f/8.f, + + // 0.125f + 1.f/32.f, + 1.f/16.f, + 1.f/32.f, + + // 0.25f + 1.f/16.f, + 1.f/8.f, + 1.f/16.f, + + // 0.125f + 1.f/32.f, + 1.f/16.f, + 1.f/32.f + }; + + vec3 sampled_color = vec3(0.0f); + + for(uint i = 0; i < 13; i++) + { + vec2 color_fetch = UV_offset + color_fetches[i] * pixel_size; + vec3 color = texture(sampler2D(inBlurImage, inImageSampler), color_fetch).rgb; + color *= color_weights[i]; + sampled_color += color; + } + + imageStore(outBlurImage, pixel_coord, vec4(sampled_color, 1.f)); +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/gammaCorrection.comp b/projects/bloom/resources/shaders/gammaCorrection.comp new file mode 100644 index 0000000000000000000000000000000000000000..f89ad167c846cca8e80f69d33eda83bd6ed00d46 --- /dev/null +++ b/projects/bloom/resources/shaders/gammaCorrection.comp @@ -0,0 +1,20 @@ +#version 440 + +layout(set=0, binding=0, r11f_g11f_b10f) uniform image2D inImage; +layout(set=0, binding=1, rgba8) uniform image2D outImage; + + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))){ + return; + } + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + vec3 linearColor = imageLoad(inImage, uv).rgb; + // cheap Reinhard tone mapping + linearColor = linearColor/(linearColor + 1.0f); + vec3 gammaCorrected = pow(linearColor, vec3(1.f / 2.2f)); + imageStore(outImage, uv, vec4(gammaCorrected, 0.f)); +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/lensFlares.comp b/projects/bloom/resources/shaders/lensFlares.comp new file mode 100644 index 0000000000000000000000000000000000000000..ce27d8850b709f61332d467914ddc944dc63109f --- /dev/null +++ b/projects/bloom/resources/shaders/lensFlares.comp @@ -0,0 +1,109 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D blurBuffer; +layout(set=0, binding=1) uniform sampler linearSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D lensBuffer; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +vec3 sampleColorChromaticAberration(vec2 _uv) +{ + vec2 toCenter = (vec2(0.5) - _uv); + + vec3 colorScales = vec3(-1, 0, 1); + float aberrationScale = 0.1; + vec3 scaleFactors = colorScales * aberrationScale; + + float r = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.r).r; + float g = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.g).g; + float b = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.b).b; + return vec3(r, g, b); +} + +// _uv assumed to be flipped UV coordinates! +vec3 ghost_vectors(vec2 _uv) +{ + vec2 ghost_vec = (vec2(0.5f) - _uv); + + const uint c_ghost_count = 64; + const float c_ghost_spacing = length(ghost_vec) / c_ghost_count; + + ghost_vec *= c_ghost_spacing; + + vec3 ret_color = vec3(0.0f); + + for (uint i = 0; i < c_ghost_count; ++i) + { + // sample scene color + vec2 s_uv = fract(_uv + ghost_vec * vec2(i)); + vec3 s = sampleColorChromaticAberration(s_uv); + + // tint/weight + float d = distance(s_uv, vec2(0.5)); + float weight = 1.0f - smoothstep(0.0f, 0.75f, d); + s *= weight; + + ret_color += s; + } + + ret_color /= c_ghost_count; + return ret_color; +} + +vec3 halo(vec2 _uv) +{ + const float c_aspect_ratio = float(imageSize(lensBuffer).x) / float(imageSize(lensBuffer).y); + const float c_radius = 0.6f; + const float c_halo_thickness = 0.1f; + + vec2 halo_vec = vec2(0.5) - _uv; + //halo_vec.x /= c_aspect_ratio; + halo_vec = normalize(halo_vec); + //halo_vec.x *= c_aspect_ratio; + + + //vec2 w_uv = (_uv - vec2(0.5, 0.0)) * vec2(c_aspect_ratio, 1.0) + vec2(0.5, 0.0); + vec2 w_uv = _uv; + float d = distance(w_uv, vec2(0.5)); // distance to center + + float distance_to_halo = abs(d - c_radius); + + float halo_weight = 0.0f; + if(abs(d - c_radius) <= c_halo_thickness) + { + float distance_to_border = c_halo_thickness - distance_to_halo; + halo_weight = distance_to_border / c_halo_thickness; + + //halo_weight = clamp((halo_weight / 0.4f), 0.0f, 1.0f); + halo_weight = pow(halo_weight, 2.0f); + + + //halo_weight = 1.0f; + } + + return sampleColorChromaticAberration(_uv + halo_vec) * halo_weight; +} + + + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(lensBuffer)))){ + return; + } + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / imageSize(lensBuffer); + vec2 UV = pixel_coord.xy * pixel_size; + + vec2 flipped_UV = vec2(1.0f) - UV; + + vec3 color = vec3(0.0f); + + color += ghost_vectors(flipped_UV); + color += halo(UV); + color *= 0.5f; + + imageStore(lensBuffer, pixel_coord, vec4(color, 0.0f)); +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/perMeshResources.inc b/projects/bloom/resources/shaders/perMeshResources.inc new file mode 100644 index 0000000000000000000000000000000000000000..95e4fb7c27009965659d14a9c72acfec950c37e3 --- /dev/null +++ b/projects/bloom/resources/shaders/perMeshResources.inc @@ -0,0 +1,2 @@ +layout(set=1, binding=0) uniform texture2D albedoTexture; +layout(set=1, binding=1) uniform sampler textureSampler; \ No newline at end of file diff --git a/projects/bloom/resources/shaders/shader.frag b/projects/bloom/resources/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..3e95b4508f112c1ed9aa4a7050a98fa789dccd09 --- /dev/null +++ b/projects/bloom/resources/shaders/shader.frag @@ -0,0 +1,45 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "perMeshResources.inc" + +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 sunBuffer { + vec3 L; float padding; + mat4 lightMatrix; +}; +layout(set=0, binding=1) uniform texture2D shadowMap; +layout(set=0, binding=2) 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(10); + vec3 sun = sunColor * clamp(dot(N, L), 0, 1); + sun *= shadowTest(passPos); + vec3 ambient = vec3(0.05); + vec3 albedo = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb; + outColor = albedo * (sun + ambient); +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/shader.vert b/projects/bloom/resources/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..926f86af2860cb57c44d2d5ee78712b6ae155e5c --- /dev/null +++ b/projects/bloom/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 = mat3(model) * inNormal; // assuming no weird stuff like shearing or non-uniform scaling + passUV = inUV; + passPos = (model * vec4(inPosition, 1)).xyz; +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/shadow.frag b/projects/bloom/resources/shaders/shadow.frag new file mode 100644 index 0000000000000000000000000000000000000000..848f853f556660b4900b5db7fb6fc98d57c1cd5b --- /dev/null +++ b/projects/bloom/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/bloom/resources/shaders/shadow.vert b/projects/bloom/resources/shaders/shadow.vert new file mode 100644 index 0000000000000000000000000000000000000000..e0f41d42d575fa64fedbfa04adf89ac0f4aeebe8 --- /dev/null +++ b/projects/bloom/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/bloom/resources/shaders/upsample.comp b/projects/bloom/resources/shaders/upsample.comp new file mode 100644 index 0000000000000000000000000000000000000000..0ddeedb5b5af9e476dc19012fed6430544006c0e --- /dev/null +++ b/projects/bloom/resources/shaders/upsample.comp @@ -0,0 +1,45 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D inUpsampleImage; +layout(set=0, binding=1) uniform sampler inImageSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D outUpsampleImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outUpsampleImage)))){ + return; + } + + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / imageSize(outUpsampleImage); + vec2 UV = pixel_coord.xy * pixel_size; + + const float gauss_kernel[3] = {1.f, 2.f, 1.f}; + const float gauss_weight = 16.f; + + vec3 sampled_color = vec3(0.f); + + for(int i = -1; i <= 1; i++) + { + for(int j = -1; j <= 1; j++) + { + vec2 sample_location = UV + vec2(j, i) * pixel_size; + vec3 color = texture(sampler2D(inUpsampleImage, inImageSampler), sample_location).rgb; + color *= gauss_kernel[j+1]; + color *= gauss_kernel[i+1]; + color /= gauss_weight; + + sampled_color += color; + } + } + + //vec3 prev_color = imageLoad(outUpsampleImage, pixel_coord).rgb; + //float bloomRimStrength = 0.75f; // adjust this to change strength of bloom + //sampled_color = mix(prev_color, sampled_color, bloomRimStrength); + + imageStore(outUpsampleImage, pixel_coord, vec4(sampled_color, 1.f)); +} \ No newline at end of file diff --git a/projects/bloom/src/BloomAndFlares.cpp b/projects/bloom/src/BloomAndFlares.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6f26db9de0f2c8334b6dd7e5dd6cf4b6f48baedc --- /dev/null +++ b/projects/bloom/src/BloomAndFlares.cpp @@ -0,0 +1,274 @@ +#include "BloomAndFlares.hpp" +#include <vkcv/shader/GLSLCompiler.hpp> + +BloomAndFlares::BloomAndFlares( + vkcv::Core *p_Core, + vk::Format colorBufferFormat, + uint32_t width, + uint32_t height) : + + p_Core(p_Core), + m_ColorBufferFormat(colorBufferFormat), + m_Width(width), + m_Height(height), + m_LinearSampler(p_Core->createSampler(vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::CLAMP_TO_EDGE)), + m_Blur(p_Core->createImage(colorBufferFormat, width, height, 1, true, true, false)), + m_LensFeatures(p_Core->createImage(colorBufferFormat, width, height, 1, false, true, false)) +{ + vkcv::shader::GLSLCompiler compiler; + + // DOWNSAMPLE + vkcv::ShaderProgram dsProg; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "resources/shaders/downsample.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + dsProg.addShader(shaderStage, path); + }); + for(uint32_t mipLevel = 0; mipLevel < m_Blur.getMipCount(); mipLevel++) + { + m_DownsampleDescSets.push_back( + p_Core->createDescriptorSet(dsProg.getReflectedDescriptors()[0])); + } + m_DownsamplePipe = p_Core->createComputePipeline( + dsProg, { p_Core->getDescriptorSet(m_DownsampleDescSets[0]).layout }); + + // UPSAMPLE + vkcv::ShaderProgram usProg; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "resources/shaders/upsample.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + usProg.addShader(shaderStage, path); + }); + for(uint32_t mipLevel = 0; mipLevel < m_Blur.getMipCount(); mipLevel++) + { + m_UpsampleDescSets.push_back( + p_Core->createDescriptorSet(usProg.getReflectedDescriptors()[0])); + } + m_UpsamplePipe = p_Core->createComputePipeline( + usProg, { p_Core->getDescriptorSet(m_UpsampleDescSets[0]).layout }); + + // LENS FEATURES + vkcv::ShaderProgram lensProg; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "resources/shaders/lensFlares.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + lensProg.addShader(shaderStage, path); + }); + m_LensFlareDescSet = p_Core->createDescriptorSet(lensProg.getReflectedDescriptors()[0]); + m_LensFlarePipe = p_Core->createComputePipeline( + lensProg, { p_Core->getDescriptorSet(m_LensFlareDescSet).layout }); + + // COMPOSITE + vkcv::ShaderProgram compProg; + compiler.compile(vkcv::ShaderStage::COMPUTE, + "resources/shaders/composite.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) + { + compProg.addShader(shaderStage, path); + }); + m_CompositeDescSet = p_Core->createDescriptorSet(compProg.getReflectedDescriptors()[0]); + m_CompositePipe = p_Core->createComputePipeline( + compProg, { p_Core->getDescriptorSet(m_CompositeDescSet).layout }); +} + +void BloomAndFlares::execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStream, + const vkcv::ImageHandle &colorAttachment) +{ + auto dispatchCountX = static_cast<float>(m_Width) / 8.0f; + auto dispatchCountY = static_cast<float>(m_Height) / 8.0f; + // blur dispatch + uint32_t initialDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(dispatchCountX)), + static_cast<uint32_t>(glm::ceil(dispatchCountY)), + 1 + }; + + // downsample dispatch of original color attachment + p_Core->prepareImageForSampling(cmdStream, colorAttachment); + p_Core->prepareImageForStorage(cmdStream, m_Blur.getHandle()); + + vkcv::DescriptorWrites initialDownsampleWrites; + initialDownsampleWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, colorAttachment)}; + initialDownsampleWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; + initialDownsampleWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_Blur.getHandle(), 0) }; + p_Core->writeDescriptorSet(m_DownsampleDescSets[0], initialDownsampleWrites); + + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_DownsamplePipe, + initialDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[0]).vulkanHandle)}, + vkcv::PushConstantData(nullptr, 0)); + + // downsample dispatches of blur buffer's mip maps + float mipDispatchCountX = dispatchCountX; + float mipDispatchCountY = dispatchCountY; + for(uint32_t mipLevel = 1; mipLevel < m_DownsampleDescSets.size(); mipLevel++) + { + // mip descriptor writes + vkcv::DescriptorWrites mipDescriptorWrites; + mipDescriptorWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle(), mipLevel - 1, true)}; + mipDescriptorWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; + mipDescriptorWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_Blur.getHandle(), mipLevel) }; + p_Core->writeDescriptorSet(m_DownsampleDescSets[mipLevel], mipDescriptorWrites); + + // mip dispatch calculation + mipDispatchCountX /= 2.0f; + mipDispatchCountY /= 2.0f; + + uint32_t mipDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(mipDispatchCountX)), + static_cast<uint32_t>(glm::ceil(mipDispatchCountY)), + 1 + }; + + if(mipDispatchCount[0] == 0) + mipDispatchCount[0] = 1; + if(mipDispatchCount[1] == 0) + mipDispatchCount[1] = 1; + + // mip blur dispatch + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_DownsamplePipe, + mipDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[mipLevel]).vulkanHandle)}, + vkcv::PushConstantData(nullptr, 0)); + + // image barrier between mips + p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle()); + } +} + +void BloomAndFlares::execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream) +{ + // upsample dispatch + p_Core->prepareImageForStorage(cmdStream, m_Blur.getHandle()); + + const uint32_t upsampleMipLevels = std::min( + static_cast<uint32_t>(m_UpsampleDescSets.size() - 1), + static_cast<uint32_t>(5) + ); + + // upsample dispatch for each mip map + for(uint32_t mipLevel = upsampleMipLevels; mipLevel > 0; mipLevel--) + { + // mip descriptor writes + vkcv::DescriptorWrites mipUpsampleWrites; + mipUpsampleWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle(), mipLevel, true)}; + mipUpsampleWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; + mipUpsampleWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_Blur.getHandle(), mipLevel - 1) }; + p_Core->writeDescriptorSet(m_UpsampleDescSets[mipLevel], mipUpsampleWrites); + + auto mipDivisor = glm::pow(2.0f, static_cast<float>(mipLevel) - 1.0f); + + auto upsampleDispatchX = static_cast<float>(m_Width) / mipDivisor; + auto upsampleDispatchY = static_cast<float>(m_Height) / mipDivisor; + upsampleDispatchX /= 8.0f; + upsampleDispatchY /= 8.0f; + + const uint32_t upsampleDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(upsampleDispatchX)), + static_cast<uint32_t>(glm::ceil(upsampleDispatchY)), + 1 + }; + + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_UpsamplePipe, + upsampleDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_UpsampleDescSets[mipLevel]).vulkanHandle)}, + vkcv::PushConstantData(nullptr, 0) + ); + // image barrier between mips + p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle()); + } +} + +void BloomAndFlares::execLensFeaturePipe(const vkcv::CommandStreamHandle &cmdStream) +{ + // lens feature generation descriptor writes + p_Core->prepareImageForSampling(cmdStream, m_Blur.getHandle()); + p_Core->prepareImageForStorage(cmdStream, m_LensFeatures.getHandle()); + + vkcv::DescriptorWrites lensFeatureWrites; + lensFeatureWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle(), 0)}; + lensFeatureWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; + lensFeatureWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_LensFeatures.getHandle(), 0)}; + p_Core->writeDescriptorSet(m_LensFlareDescSet, lensFeatureWrites); + + auto dispatchCountX = static_cast<float>(m_Width) / 8.0f; + auto dispatchCountY = static_cast<float>(m_Height) / 8.0f; + // lens feature generation dispatch + uint32_t lensFeatureDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(dispatchCountX)), + static_cast<uint32_t>(glm::ceil(dispatchCountY)), + 1 + }; + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_LensFlarePipe, + lensFeatureDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_LensFlareDescSet).vulkanHandle)}, + vkcv::PushConstantData(nullptr, 0)); +} + +void BloomAndFlares::execCompositePipe(const vkcv::CommandStreamHandle &cmdStream, + const vkcv::ImageHandle &colorAttachment) +{ + p_Core->prepareImageForSampling(cmdStream, m_Blur.getHandle()); + p_Core->prepareImageForSampling(cmdStream, m_LensFeatures.getHandle()); + p_Core->prepareImageForStorage(cmdStream, colorAttachment); + + // bloom composite descriptor write + vkcv::DescriptorWrites compositeWrites; + compositeWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle()), + vkcv::SampledImageDescriptorWrite(1, m_LensFeatures.getHandle())}; + compositeWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(2, m_LinearSampler)}; + compositeWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(3, colorAttachment)}; + p_Core->writeDescriptorSet(m_CompositeDescSet, compositeWrites); + + float dispatchCountX = static_cast<float>(m_Width) / 8.0f; + float dispatchCountY = static_cast<float>(m_Height) / 8.0f; + + uint32_t compositeDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(dispatchCountX)), + static_cast<uint32_t>(glm::ceil(dispatchCountY)), + 1 + }; + + // bloom composite dispatch + p_Core->recordComputeDispatchToCmdStream( + cmdStream, + m_CompositePipe, + compositeDispatchCount, + {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_CompositeDescSet).vulkanHandle)}, + vkcv::PushConstantData(nullptr, 0)); +} + +void BloomAndFlares::execWholePipeline(const vkcv::CommandStreamHandle &cmdStream, + const vkcv::ImageHandle &colorAttachment) +{ + execDownsamplePipe(cmdStream, colorAttachment); + execUpsamplePipe(cmdStream); + execLensFeaturePipe(cmdStream); + execCompositePipe(cmdStream, colorAttachment); +} + +void BloomAndFlares::updateImageDimensions(uint32_t width, uint32_t height) +{ + m_Width = width; + m_Height = height; + + p_Core->getContext().getDevice().waitIdle(); + m_Blur = p_Core->createImage(m_ColorBufferFormat, m_Width, m_Height, 1, true, true, false); + m_LensFeatures = p_Core->createImage(m_ColorBufferFormat, m_Width, m_Height, 1, false, true, false); +} + + diff --git a/projects/bloom/src/BloomAndFlares.hpp b/projects/bloom/src/BloomAndFlares.hpp new file mode 100644 index 0000000000000000000000000000000000000000..756b1ca154ea5232df04eb09a88bb743c5bd28aa --- /dev/null +++ b/projects/bloom/src/BloomAndFlares.hpp @@ -0,0 +1,47 @@ +#pragma once +#include <vkcv/Core.hpp> +#include <glm/glm.hpp> + +class BloomAndFlares{ +public: + BloomAndFlares(vkcv::Core *p_Core, + vk::Format colorBufferFormat, + uint32_t width, + uint32_t height); + + void execWholePipeline(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment); + + void updateImageDimensions(uint32_t width, uint32_t height); + +private: + vkcv::Core *p_Core; + + vk::Format m_ColorBufferFormat; + uint32_t m_Width; + uint32_t m_Height; + + vkcv::SamplerHandle m_LinearSampler; + vkcv::Image m_Blur; + vkcv::Image m_LensFeatures; + + + vkcv::PipelineHandle m_DownsamplePipe; + std::vector<vkcv::DescriptorSetHandle> m_DownsampleDescSets; // per mip desc set + + vkcv::PipelineHandle m_UpsamplePipe; + std::vector<vkcv::DescriptorSetHandle> m_UpsampleDescSets; // per mip desc set + + vkcv::PipelineHandle m_LensFlarePipe; + vkcv::DescriptorSetHandle m_LensFlareDescSet; + + vkcv::PipelineHandle m_CompositePipe; + vkcv::DescriptorSetHandle m_CompositeDescSet; + + void execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment); + void execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream); + void execLensFeaturePipe(const vkcv::CommandStreamHandle &cmdStream); + void execCompositePipe(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment); +}; + + + diff --git a/projects/bloom/src/main.cpp b/projects/bloom/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7a17a51f1c7d638575c0b5aafcdca49b589533ef --- /dev/null +++ b/projects/bloom/src/main.cpp @@ -0,0 +1,419 @@ +#include <iostream> +#include <vkcv/Core.hpp> +#include <GLFW/glfw3.h> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/Logger.hpp> +#include "BloomAndFlares.hpp" +#include <glm/glm.hpp> + +int main(int argc, const char** argv) { + const char* applicationName = "Bloom"; + + uint32_t windowWidth = 1920; + uint32_t windowHeight = 1080; + + vkcv::Window window = vkcv::Window::create( + applicationName, + windowWidth, + windowHeight, + true + ); + + vkcv::camera::CameraManager cameraManager(window); + 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" } + ); + + const char* path = argc > 1 ? argv[1] : "resources/Sponza/Sponza.gltf"; + vkcv::asset::Scene scene; + int result = vkcv::asset::loadScene(path, scene); + + if (result == 1) { + std::cout << "Scene loading successful!" << std::endl; + } + else { + std::cout << "Scene loading failed: " << result << std::endl; + return 1; + } + + // build index and vertex buffers + assert(!scene.vertexGroups.empty()); + std::vector<std::vector<uint8_t>> vBuffers; + std::vector<std::vector<uint8_t>> iBuffers; + + std::vector<vkcv::VertexBufferBinding> vBufferBindings; + std::vector<std::vector<vkcv::VertexBufferBinding>> vertexBufferBindings; + std::vector<vkcv::asset::VertexAttribute> vAttributes; + + for (int i = 0; i < scene.vertexGroups.size(); i++) { + + vBuffers.push_back(scene.vertexGroups[i].vertexBuffer.data); + iBuffers.push_back(scene.vertexGroups[i].indexBuffer.data); + + auto& attributes = scene.vertexGroups[i].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); + }); + } + + std::vector<vkcv::Buffer<uint8_t>> vertexBuffers; + for (const vkcv::asset::VertexGroup& group : scene.vertexGroups) { + vertexBuffers.push_back(core.createBuffer<uint8_t>( + vkcv::BufferType::VERTEX, + group.vertexBuffer.data.size())); + vertexBuffers.back().fill(group.vertexBuffer.data); + } + + std::vector<vkcv::Buffer<uint8_t>> indexBuffers; + for (const auto& dataBuffer : iBuffers) { + indexBuffers.push_back(core.createBuffer<uint8_t>( + vkcv::BufferType::INDEX, + dataBuffer.size())); + indexBuffers.back().fill(dataBuffer); + } + + int vertexBufferIndex = 0; + for (const auto& vertexGroup : scene.vertexGroups) { + for (const auto& attribute : vertexGroup.vertexBuffer.attributes) { + vAttributes.push_back(attribute); + vBufferBindings.push_back(vkcv::VertexBufferBinding(attribute.offset, vertexBuffers[vertexBufferIndex].getVulkanHandle())); + } + vertexBufferBindings.push_back(vBufferBindings); + vBufferBindings.clear(); + vertexBufferIndex++; + } + + const vk::Format colorBufferFormat = vk::Format::eB10G11R11UfloatPack32; + const vkcv::AttachmentDescription color_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + colorBufferFormat + ); + + const vk::Format depthBufferFormat = vk::Format::eD32Sfloat; + const vkcv::AttachmentDescription depth_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + depthBufferFormat + ); + + vkcv::PassConfig forwardPassDefinition({ color_attachment, depth_attachment }); + vkcv::PassHandle forwardPass = core.createPass(forwardPassDefinition); + + vkcv::shader::GLSLCompiler compiler; + + vkcv::ShaderProgram forwardProgram; + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert"), + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + forwardProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"), + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + forwardProgram.addShader(shaderStage, path); + }); + + const std::vector<vkcv::VertexAttachment> vertexAttachments = forwardProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> vertexBindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + vertexBindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] })); + } + const vkcv::VertexLayout vertexLayout (vertexBindings); + + // shadow map + vkcv::SamplerHandle shadowSampler = core.createSampler( + vkcv::SamplerFilterType::NEAREST, + vkcv::SamplerFilterType::NEAREST, + vkcv::SamplerMipmapMode::NEAREST, + vkcv::SamplerAddressMode::CLAMP_TO_EDGE + ); + const vk::Format shadowMapFormat = vk::Format::eD16Unorm; + const uint32_t shadowMapResolution = 1024; + const vkcv::Image shadowMap = core.createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1); + + // light info buffer + 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::DescriptorSetHandle forwardShadingDescriptorSet = + core.createDescriptorSet({ forwardProgram.getReflectedDescriptors()[0] }); + + vkcv::DescriptorWrites forwardDescriptorWrites; + forwardDescriptorWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(0, lightBuffer.getHandle()) }; + forwardDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(1, shadowMap.getHandle()) }; + forwardDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(2, shadowSampler) }; + core.writeDescriptorSet(forwardShadingDescriptorSet, forwardDescriptorWrites); + + vkcv::SamplerHandle colorSampler = core.createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT + ); + + // prepare per mesh descriptor sets + std::vector<vkcv::DescriptorSetHandle> perMeshDescriptorSets; + std::vector<vkcv::Image> sceneImages; + for (const auto& vertexGroup : scene.vertexGroups) { + perMeshDescriptorSets.push_back(core.createDescriptorSet(forwardProgram.getReflectedDescriptors()[1])); + + const auto& material = scene.materials[vertexGroup.materialIndex]; + + int baseColorIndex = material.baseColor; + if (baseColorIndex < 0) { + vkcv_log(vkcv::LogLevel::WARNING, "Material lacks base color"); + baseColorIndex = 0; + } + + vkcv::asset::Texture& sceneTexture = scene.textures[baseColorIndex]; + + sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, sceneTexture.w, sceneTexture.h)); + sceneImages.back().fill(sceneTexture.data.data()); + + vkcv::DescriptorWrites setWrites; + setWrites.sampledImageWrites = { + vkcv::SampledImageDescriptorWrite(0, sceneImages.back().getHandle()) + }; + setWrites.samplerWrites = { + vkcv::SamplerDescriptorWrite(1, colorSampler), + }; + core.writeDescriptorSet(perMeshDescriptorSets.back(), setWrites); + } + + const vkcv::PipelineConfig forwardPipelineConfig { + forwardProgram, + windowWidth, + windowHeight, + forwardPass, + vertexLayout, + { core.getDescriptorSet(forwardShadingDescriptorSet).layout, + core.getDescriptorSet(perMeshDescriptorSets[0]).layout }, + true + }; + + vkcv::PipelineHandle forwardPipeline = core.createGraphicsPipeline(forwardPipelineConfig); + + if (!forwardPipeline) { + std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::ImageHandle depthBuffer = core.createImage(depthBufferFormat, windowWidth, windowHeight).getHandle(); + vkcv::ImageHandle colorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true, true).getHandle(); + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::ShaderProgram shadowShader; + compiler.compile(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow.vert", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shadowShader.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow.frag", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shadowShader.addShader(shaderStage, path); + }); + + 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 vkcv::PipelineConfig shadowPipeConfig{ + shadowShader, + shadowMapResolution, + shadowMapResolution, + shadowPass, + vertexLayout, + {}, + false + }; + const vkcv::PipelineHandle shadowPipe = core.createGraphicsPipeline(shadowPipeConfig); + + std::vector<std::array<glm::mat4, 2>> mainPassMatrices; + std::vector<glm::mat4> mvpLight; + + // gamma correction compute shader + vkcv::ShaderProgram gammaCorrectionProgram; + compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/gammaCorrection.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + gammaCorrectionProgram.addShader(shaderStage, path); + }); + vkcv::DescriptorSetHandle gammaCorrectionDescriptorSet = core.createDescriptorSet(gammaCorrectionProgram.getReflectedDescriptors()[0]); + vkcv::PipelineHandle gammaCorrectionPipeline = core.createComputePipeline(gammaCorrectionProgram, + { core.getDescriptorSet(gammaCorrectionDescriptorSet).layout }); + + BloomAndFlares baf(&core, colorBufferFormat, windowWidth, windowHeight); + + + // model matrices per mesh + std::vector<glm::mat4> modelMatrices; + modelMatrices.resize(scene.vertexGroups.size(), glm::mat4(1.f)); + for (const auto& mesh : scene.meshes) { + const glm::mat4 m = *reinterpret_cast<const glm::mat4*>(&mesh.modelMatrix[0]); + for (const auto& vertexGroupIndex : mesh.vertexGroups) { + modelMatrices[vertexGroupIndex] = m; + } + } + + // prepare drawcalls + std::vector<vkcv::Mesh> meshes; + for (int i = 0; i < scene.vertexGroups.size(); i++) { + vkcv::Mesh mesh( + vertexBufferBindings[i], + indexBuffers[i].getVulkanHandle(), + scene.vertexGroups[i].numIndices); + meshes.push_back(mesh); + } + + std::vector<vkcv::DrawcallInfo> drawcalls; + std::vector<vkcv::DrawcallInfo> shadowDrawcalls; + for (int i = 0; i < meshes.size(); i++) { + drawcalls.push_back(vkcv::DrawcallInfo(meshes[i], { + vkcv::DescriptorSetUsage(0, core.getDescriptorSet(forwardShadingDescriptorSet).vulkanHandle), + vkcv::DescriptorSetUsage(1, core.getDescriptorSet(perMeshDescriptorSets[i]).vulkanHandle) })); + shadowDrawcalls.push_back(vkcv::DrawcallInfo(meshes[i], {})); + } + + 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(depthBufferFormat, swapchainWidth, swapchainHeight).getHandle(); + colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, true, true).getHandle(); + + baf.updateImageDimensions(swapchainWidth, swapchainHeight); + + 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.0001f * static_cast<float>(duration.count()); + lightInfo.direction = glm::normalize(glm::vec3(std::cos(sunTheta), 1, std::sin(sunTheta))); + + const float shadowProjectionSize = 20.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 = { colorBuffer, depthBuffer }; + + const vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4)); + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + // shadow map + core.recordDrawcallsToCmdStream( + cmdStream, + shadowPass, + shadowPipe, + shadowPushConstantData, + shadowDrawcalls, + { shadowMap.getHandle() }); + core.prepareImageForSampling(cmdStream, shadowMap.getHandle()); + + // main pass + core.recordDrawcallsToCmdStream( + cmdStream, + forwardPass, + forwardPipeline, + pushConstantData, + drawcalls, + renderTargets); + + const uint32_t gammaCorrectionLocalGroupSize = 8; + const uint32_t gammaCorrectionDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(static_cast<float>(windowWidth) / static_cast<float>(gammaCorrectionLocalGroupSize))), + static_cast<uint32_t>(glm::ceil(static_cast<float>(windowHeight) / static_cast<float>(gammaCorrectionLocalGroupSize))), + 1 + }; + + baf.execWholePipeline(cmdStream, colorBuffer); + + core.prepareImageForStorage(cmdStream, swapchainInput); + + // gamma correction descriptor write + vkcv::DescriptorWrites gammaCorrectionDescriptorWrites; + gammaCorrectionDescriptorWrites.storageImageWrites = { + vkcv::StorageImageDescriptorWrite(0, colorBuffer), + vkcv::StorageImageDescriptorWrite(1, swapchainInput) }; + core.writeDescriptorSet(gammaCorrectionDescriptorSet, gammaCorrectionDescriptorWrites); + + // gamma correction dispatch + core.recordComputeDispatchToCmdStream( + cmdStream, + gammaCorrectionPipeline, + gammaCorrectionDispatchCount, + { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(gammaCorrectionDescriptorSet).vulkanHandle) }, + vkcv::PushConstantData(nullptr, 0)); + + // present and end + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + core.endFrame(); + } + + return 0; +} diff --git a/projects/cmd_sync_test/src/main.cpp b/projects/cmd_sync_test/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eccc0af7331dc140f3a15ddf12c5645e685abc90 --- /dev/null +++ b/projects/cmd_sync_test/src/main.cpp @@ -0,0 +1,317 @@ +#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); + 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); + + window.initEvents(); + + vkcv::Core core = vkcv::Core::create( + window, + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, + {}, + { "VK_KHR_swapchain" } + ); + + vkcv::asset::Scene mesh; + + const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf"; + int result = vkcv::asset::loadScene(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.getSwapchain().getFormat() + ); + + 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::asset::Texture &tex = mesh.textures[0]; + vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, tex.w, tex.h); + texture.fill(tex.data.data()); + + 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()) { + 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/resources/Szene/Szene.bin b/projects/first_mesh/resources/Szene/Szene.bin new file mode 100644 index 0000000000000000000000000000000000000000..c87d27637516b0bbf864251dd162773f5cc53e06 --- /dev/null +++ b/projects/first_mesh/resources/Szene/Szene.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee4742718f720d589a2a03f5d879f8c50ba9057718d191a43b046eaa9071080d +size 70328 diff --git a/projects/first_mesh/resources/Szene/Szene.gltf b/projects/first_mesh/resources/Szene/Szene.gltf new file mode 100644 index 0000000000000000000000000000000000000000..e5a32b29af5d0a2ac5f497e60b4b92c1873e1df9 --- /dev/null +++ b/projects/first_mesh/resources/Szene/Szene.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75ba834118792ebbacf528a1690c7d04df4b4c8122b9f99a9aa9a9d075d2c86a +size 7421 diff --git a/projects/first_mesh/resources/Szene/boards2_vcyc.jpg b/projects/first_mesh/resources/Szene/boards2_vcyc.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248 --- /dev/null +++ b/projects/first_mesh/resources/Szene/boards2_vcyc.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564 +size 1192476 diff --git a/projects/first_mesh/resources/Szene/boards2_vcyc_jpg.jpg b/projects/first_mesh/resources/Szene/boards2_vcyc_jpg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248 --- /dev/null +++ b/projects/first_mesh/resources/Szene/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/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp index 107cd432e1bc902692bb5b1fa694120ddf24038b..dc43c905784525a34732bc0e66343fbdcc17a639 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -8,32 +8,29 @@ int main(int argc, const char** argv) { const char* applicationName = "First Mesh"; - const int windowWidth = 800; - const int windowHeight = 600; + uint32_t windowWidth = 800; + uint32_t windowHeight = 600; + vkcv::Window window = vkcv::Window::create( applicationName, windowWidth, windowHeight, - false + true ); - vkcv::CameraManager cameraManager(window, windowWidth, windowHeight); - - window.initEvents(); - vkcv::Core core = vkcv::Core::create( window, applicationName, VK_MAKE_VERSION(0, 0, 1), - { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, + { vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer }, {}, { "VK_KHR_swapchain" } ); - vkcv::asset::Mesh mesh; + vkcv::asset::Scene mesh; const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf"; - int result = vkcv::asset::loadMesh(path, mesh); + int result = vkcv::asset::loadScene(path, mesh); if (result == 1) { std::cout << "Mesh loading successful!" << std::endl; @@ -43,7 +40,7 @@ int main(int argc, const char** argv) { return 1; } - assert(mesh.vertexGroups.size() > 0); + assert(!mesh.vertexGroups.empty()); auto vertexBuffer = core.createBuffer<uint8_t>( vkcv::BufferType::VERTEX, mesh.vertexGroups[0].vertexBuffer.data.size(), @@ -62,65 +59,70 @@ 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() + core.getSwapchain().getFormat() ); 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 }); - - const vkcv::PipelineConfig trianglePipelineDefinition( - triangleShaderProgram, - windowWidth, - windowHeight, - trianglePass, - mesh.vertexGroups[0].vertexBuffer.attributes, - { core.getDescritorSetLayout(set, 0) }); - vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition); + 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); + + uint32_t setID = 0; + std::vector<vkcv::DescriptorBinding> descriptorBindings = { firstMeshProgram.getReflectedDescriptors()[setID] }; + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorBindings); + + const vkcv::PipelineConfig firstMeshPipelineConfig { + firstMeshProgram, + UINT32_MAX, + UINT32_MAX, + 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; } - vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, mesh.texture_hack.w, mesh.texture_hack.h); - texture.fill(mesh.texture_hack.img); + // FIXME There should be a test here to make sure there is at least 1 + // texture in the mesh. + vkcv::asset::Texture &tex = mesh.textures[0]; + vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, tex.w, tex.h); + texture.fill(tex.data.data()); + texture.generateMipChainImmediate(); + texture.switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal); vkcv::SamplerHandle sampler = core.createSampler( vkcv::SamplerFilterType::LINEAR, @@ -129,41 +131,73 @@ 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); - auto start = std::chrono::system_clock::now(); + core.writeDescriptorSet(descriptorSet, setWrites); + + vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight, 1, false).getHandle(); + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + const vkcv::Mesh renderMesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices); + + vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle); + vkcv::DrawcallInfo drawcall(renderMesh, { descriptorUsage }); + + vkcv::camera::CameraManager cameraManager(window); + 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()) { - core.beginFrame(); - window.pollEvents(); + 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, - windowWidth, - windowHeight, - 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_scene/.gitignore b/projects/first_scene/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b0d802667a184267e6ee764264e976dfead8e9dd --- /dev/null +++ b/projects/first_scene/.gitignore @@ -0,0 +1 @@ +first_scene diff --git a/projects/first_scene/CMakeLists.txt b/projects/first_scene/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b90739750011a36b4c1d9e0bff7cba986074228 --- /dev/null +++ b/projects/first_scene/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.16) +project(first_scene) + +# 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(first_scene src/main.cpp) + +# this should fix the execution path to load local files from the project (for MSVC) +if(MSVC) + set_target_properties(first_scene PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set_target_properties(first_scene PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + + # in addition to setting the output directory, the working directory has to be set + # by default visual studio sets the working directory to the build directory, when using the debugger + set_target_properties(first_scene PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +endif() + +# including headers of dependencies and the VkCV framework +target_include_directories(first_scene SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(first_scene vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera) diff --git a/projects/first_scene/resources/Cutlery/Cutlery_chrome_BaseColor.png b/projects/first_scene/resources/Cutlery/Cutlery_chrome_BaseColor.png new file mode 100644 index 0000000000000000000000000000000000000000..8258525f22097f2382ec5c26ad0df7eb50718642 --- /dev/null +++ b/projects/first_scene/resources/Cutlery/Cutlery_chrome_BaseColor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ce87f6407ee40ffa60983587aeb52333d59b4b1c01a53e11f4bb227ba1099d9 +size 109 diff --git a/projects/first_scene/resources/Cutlery/Cutlery_chrome_Normal.png b/projects/first_scene/resources/Cutlery/Cutlery_chrome_Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..620fe7621cf35409ae8c04099dc8bc4bbc04343b --- /dev/null +++ b/projects/first_scene/resources/Cutlery/Cutlery_chrome_Normal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:68a0064d457a6f7994814b07d943deda778754128935689874334300ede6161d +size 2332064 diff --git a/projects/first_scene/resources/Cutlery/Cutlery_details_BaseColor.png b/projects/first_scene/resources/Cutlery/Cutlery_details_BaseColor.png new file mode 100644 index 0000000000000000000000000000000000000000..5570e88c569036b9d00155ef6113013aa23f2503 --- /dev/null +++ b/projects/first_scene/resources/Cutlery/Cutlery_details_BaseColor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42c2715635081eb29c4489ce631798b0e9c881460efc0aa63d0e81641a0dcfe9 +size 108 diff --git a/projects/first_scene/resources/Cutlery/Cutlery_details_Normal.png b/projects/first_scene/resources/Cutlery/Cutlery_details_Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..d07681f5bfa25c279321c3074e21e4900c5eb0fa --- /dev/null +++ b/projects/first_scene/resources/Cutlery/Cutlery_details_Normal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15b0133e140899c47ccf35b0f99a7e337e3110ae089f45d27faf9983f3e0a1f7 +size 770758 diff --git a/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_BaseColor.png b/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_BaseColor.png new file mode 100644 index 0000000000000000000000000000000000000000..1845e8a7d586a0d23300ad9d04a82f1d5048fcf5 --- /dev/null +++ b/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_BaseColor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea7c82c0f9e25afa401470df1fb6903f508fa138d21ad30f57a9153b0395b198 +size 521315 diff --git a/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_Normal.png b/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..1c800c0489693b66d6163bdc2156d453b68ca19b --- /dev/null +++ b/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_Normal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:119efbbc020244ff9b7ff16ac9795a6d4b1808d1b90d81d20d2c874d0dc8a924 +size 1693468 diff --git a/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_BaseColor.png b/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_BaseColor.png new file mode 100644 index 0000000000000000000000000000000000000000..36f46ebf25158c78bc26d83860e1c08b2c9e96cf --- /dev/null +++ b/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_BaseColor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99896468c7d47dd5391d585eecf149f420eca3bfec31923c21fa86c45fe02d0f +size 108 diff --git a/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_Normal.png b/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..28c205d4e70867ec58448ca8ba2c2117c611a367 --- /dev/null +++ b/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_Normal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b31618aa5adce4ad476bec2c03718c5ae097250e784344f2d298b8a74c3bfd46 +size 90 diff --git a/projects/first_scene/resources/Cutlery/Plates_Ceramic_BaseColor.png b/projects/first_scene/resources/Cutlery/Plates_Ceramic_BaseColor.png new file mode 100644 index 0000000000000000000000000000000000000000..e0104189a1190c160152101e9e328e81ed89121b --- /dev/null +++ b/projects/first_scene/resources/Cutlery/Plates_Ceramic_BaseColor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6eff6ccd12d8b39d60ae5ee91edd73d4d7838fcb5d9bc6ff0e671bdf009134e9 +size 109 diff --git a/projects/first_scene/resources/Cutlery/Plates_Ceramic_Normal.png b/projects/first_scene/resources/Cutlery/Plates_Ceramic_Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..fa13483d25595ee6b3a00e7fe6ce48aed7b6aaca --- /dev/null +++ b/projects/first_scene/resources/Cutlery/Plates_Ceramic_Normal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe92c40ff4032fdaf10eeafd943657a0c6e0bfb3f38770f5654aa943a660f421 +size 59419 diff --git a/projects/first_scene/resources/Cutlery/Plates_Details_BaseColor-Plates_Details_BaseColor.png b/projects/first_scene/resources/Cutlery/Plates_Details_BaseColor-Plates_Details_BaseColor.png new file mode 100644 index 0000000000000000000000000000000000000000..b91d0ac62fbeabbef1ed78f1343f919836cbca40 --- /dev/null +++ b/projects/first_scene/resources/Cutlery/Plates_Details_BaseColor-Plates_Details_BaseColor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ca7d436a68a2a1237aee6e763b2954f01666b21f1dbd46929a322ea277483d2 +size 779227 diff --git a/projects/first_scene/resources/Cutlery/Plates_Details_Normal.png b/projects/first_scene/resources/Cutlery/Plates_Details_Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..6efd967984ee2e68b89de9e471b93396c13ca69a --- /dev/null +++ b/projects/first_scene/resources/Cutlery/Plates_Details_Normal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b01fc6482054c64d7407b283731e57fce0601a8db28b6781c14fae3c6b30b0fe +size 504362 diff --git a/projects/first_scene/resources/Cutlery/ToffeeJar_Label_BaseColor.png b/projects/first_scene/resources/Cutlery/ToffeeJar_Label_BaseColor.png new file mode 100644 index 0000000000000000000000000000000000000000..d0e0c4f4134cb99d3765d6f078f71efab8861bf1 --- /dev/null +++ b/projects/first_scene/resources/Cutlery/ToffeeJar_Label_BaseColor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df138ee68c1d455652d1b9ae3dd03e93fcd2f6a0d8a1f12e3710f39143088674 +size 1593466 diff --git a/projects/first_scene/resources/Cutlery/ToffeeJar_Label_Normal.png b/projects/first_scene/resources/Cutlery/ToffeeJar_Label_Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..9f310653b5211575da3cab2f6deb47ec6826a936 --- /dev/null +++ b/projects/first_scene/resources/Cutlery/ToffeeJar_Label_Normal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6af5da97cbb25d79aea2dde8dd71ecbd495334fe34e99497ba17821be93fd7fd +size 2696676 diff --git a/projects/first_scene/resources/Cutlery/TransparentGlass_BaseColor.png b/projects/first_scene/resources/Cutlery/TransparentGlass_BaseColor.png new file mode 100644 index 0000000000000000000000000000000000000000..4e4f0fcb312b8f8bd0df965bfe6b8ac62ec5df4d --- /dev/null +++ b/projects/first_scene/resources/Cutlery/TransparentGlass_BaseColor.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2796affdfdcf6bc805176d9f85505680b5ee52eeec625e9eaeea4f0ff3854883 +size 108 diff --git a/projects/first_scene/resources/Cutlery/TransparentGlass_Normal.png b/projects/first_scene/resources/Cutlery/TransparentGlass_Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..28c205d4e70867ec58448ca8ba2c2117c611a367 --- /dev/null +++ b/projects/first_scene/resources/Cutlery/TransparentGlass_Normal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b31618aa5adce4ad476bec2c03718c5ae097250e784344f2d298b8a74c3bfd46 +size 90 diff --git a/projects/first_scene/resources/Cutlery/cutlerySzene.bin b/projects/first_scene/resources/Cutlery/cutlerySzene.bin new file mode 100644 index 0000000000000000000000000000000000000000..ab9a0aa47205e8f3064d2f16a950d4733fb4f472 --- /dev/null +++ b/projects/first_scene/resources/Cutlery/cutlerySzene.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f545b986e0a1ac5bff5d49693a52042aa37878425818f72c69c243da20d1f99d +size 183324 diff --git a/projects/first_scene/resources/Cutlery/cutlerySzene.glb b/projects/first_scene/resources/Cutlery/cutlerySzene.glb new file mode 100644 index 0000000000000000000000000000000000000000..b0c5f345aaaa3f96d7158a0992ee124aae99a69a --- /dev/null +++ b/projects/first_scene/resources/Cutlery/cutlerySzene.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb1bad604192ca36222c0ca485ba87b846ecbd11ee8254327e04e3c993b00116 +size 11150396 diff --git a/projects/first_scene/resources/Cutlery/cutlerySzene.gltf b/projects/first_scene/resources/Cutlery/cutlerySzene.gltf new file mode 100644 index 0000000000000000000000000000000000000000..53e339cda4511f3f1a8670b36469e184aac530e2 --- /dev/null +++ b/projects/first_scene/resources/Cutlery/cutlerySzene.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c77cd60e2327daca1a01044e45f2c38655f7b781bd07985fc0135328a8a96b57 +size 34312 diff --git a/projects/first_scene/resources/Sponza/Sponza.bin b/projects/first_scene/resources/Sponza/Sponza.bin new file mode 100644 index 0000000000000000000000000000000000000000..cfedd26ca5a67b6d0a47d44d13a75e14a141717a --- /dev/null +++ b/projects/first_scene/resources/Sponza/Sponza.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b809f7a17687dc99e6f41ca1ea32c06eded8779bf34d16f1f565d750b0ffd68 +size 6347696 diff --git a/projects/first_scene/resources/Sponza/Sponza.gltf b/projects/first_scene/resources/Sponza/Sponza.gltf new file mode 100644 index 0000000000000000000000000000000000000000..172ea07e21c94465211c860cd805355704cef230 --- /dev/null +++ b/projects/first_scene/resources/Sponza/Sponza.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5cc0ecad5c4694088ff820e663619c370421afc1323ac487406e8e9b4735d787 +size 713962 diff --git a/projects/first_scene/resources/Sponza/background.png b/projects/first_scene/resources/Sponza/background.png new file mode 100644 index 0000000000000000000000000000000000000000..b64def129da38f4e23d89e21b4af1039008a4327 --- /dev/null +++ b/projects/first_scene/resources/Sponza/background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5b5f900ff8ed83a31750ec8e428b5b91273794ddcbfc4e4b8a6a7e781f8c686 +size 1417666 diff --git a/projects/first_scene/resources/Sponza/chain_texture.png b/projects/first_scene/resources/Sponza/chain_texture.png new file mode 100644 index 0000000000000000000000000000000000000000..c1e1768cff78e0614ad707eca8602a4c4edab5e5 --- /dev/null +++ b/projects/first_scene/resources/Sponza/chain_texture.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8362cfd472880daeaea37439326a4651d1338680ae69bb2513fc6b17c8de7d4 +size 490895 diff --git a/projects/first_scene/resources/Sponza/lion.png b/projects/first_scene/resources/Sponza/lion.png new file mode 100644 index 0000000000000000000000000000000000000000..c49c7f0ed31e762e19284d0d3624fbc47664e56b --- /dev/null +++ b/projects/first_scene/resources/Sponza/lion.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f882f746c3a9cd51a9c6eedc1189b97668721d91a3fe49232036e789912c652 +size 2088728 diff --git a/projects/first_scene/resources/Sponza/spnza_bricks_a_diff.png b/projects/first_scene/resources/Sponza/spnza_bricks_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..cde4c7a6511e9a5f03c63ad996437fcdba3ce2df --- /dev/null +++ b/projects/first_scene/resources/Sponza/spnza_bricks_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b94219c2f5f943f3f4715c74e7d1038bf0ab3b3b3216a758eaee67f875df0851 +size 1928829 diff --git a/projects/first_scene/resources/Sponza/sponza_arch_diff.png b/projects/first_scene/resources/Sponza/sponza_arch_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..bcd9bda2918d226039f9e2d03902d377b706fab6 --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_arch_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0df2c8a01b2843b1c792b494f7173cdbc4f834840fc2177af3e5d690fceda57 +size 1596151 diff --git a/projects/first_scene/resources/Sponza/sponza_ceiling_a_diff.png b/projects/first_scene/resources/Sponza/sponza_ceiling_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..59de631ffac4414cabf69b2dc794c46fc187d6cb --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_ceiling_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab6c187a81aa68f4eba30119e17fce2e4882a9ec320f70c90482dbe9da82b1c6 +size 1872074 diff --git a/projects/first_scene/resources/Sponza/sponza_column_a_diff.png b/projects/first_scene/resources/Sponza/sponza_column_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..01a82432d3f9939bbefe850bdb900f1ff9a3f6db --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_column_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c291507e2808bb83e160ab4b020689817df273baad3713a9ad19ac15fac6826 +size 1840992 diff --git a/projects/first_scene/resources/Sponza/sponza_column_b_diff.png b/projects/first_scene/resources/Sponza/sponza_column_b_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..10a660cce2a5a9b8997772c746058ce23e7d45d7 --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_column_b_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2820b0267c4289c6cedbb42721792a57ef244ec2d0935941011c2a7d3fe88a9b +size 2170433 diff --git a/projects/first_scene/resources/Sponza/sponza_column_c_diff.png b/projects/first_scene/resources/Sponza/sponza_column_c_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..bc46fd979044a938d3adca7601689e71504e48bf --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_column_c_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0bc993ff59865468ef4530798930c7dfefb07482d71db45bc2a520986b27735 +size 2066950 diff --git a/projects/first_scene/resources/Sponza/sponza_curtain_blue_diff.png b/projects/first_scene/resources/Sponza/sponza_curtain_blue_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..384c8c2c051160d530eb3ac8b05c9c60752a2d2b --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_curtain_blue_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b85c6bb3cd5105f48d3812ec8e7a1068521ce69e917300d79e136e19d45422fb +size 9510905 diff --git a/projects/first_scene/resources/Sponza/sponza_curtain_diff.png b/projects/first_scene/resources/Sponza/sponza_curtain_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..af842e9f5fe18c1f609875e00899a6770fa4488b --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_curtain_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:563c56bdbbee395a6ef7f0c51c8ac9223c162e517b4cdba0d4654e8de27c98d8 +size 9189263 diff --git a/projects/first_scene/resources/Sponza/sponza_curtain_green_diff.png b/projects/first_scene/resources/Sponza/sponza_curtain_green_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..6c9b6391a199407637fa71033d79fb58b8b4f0d7 --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_curtain_green_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:238fe1c7f481388d1c1d578c2da8d411b99e8f0030ab62060a306db333124476 +size 8785458 diff --git a/projects/first_scene/resources/Sponza/sponza_details_diff.png b/projects/first_scene/resources/Sponza/sponza_details_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..12656686362c3e0a297e060491f33bd7351551f9 --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_details_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb1223b3bb82f8757e7df25a6891f1239cdd7ec59990340e952fb2d6b7ea570c +size 1522643 diff --git a/projects/first_scene/resources/Sponza/sponza_fabric_blue_diff.png b/projects/first_scene/resources/Sponza/sponza_fabric_blue_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..879d16ef84722a4fc13e83a771778de326e4bc54 --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_fabric_blue_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:467d290bf5d4b2a017da140ba9e244ed8a8a9be5418a9ac9bcb4ad572ae2d7ab +size 2229440 diff --git a/projects/first_scene/resources/Sponza/sponza_fabric_diff.png b/projects/first_scene/resources/Sponza/sponza_fabric_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..3311287a219d2148620b87fe428fea071688d051 --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_fabric_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1594f59cc2848db26add47361f4e665e3d8afa147760ed915d839fea42b20287 +size 2267382 diff --git a/projects/first_scene/resources/Sponza/sponza_fabric_green_diff.png b/projects/first_scene/resources/Sponza/sponza_fabric_green_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..de110f369004388dae4cd5067c63428db3a07834 --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_fabric_green_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:902b87faab221173bf370cea7c74cb9060b4d870ac6316b190dafded1cb12993 +size 2258220 diff --git a/projects/first_scene/resources/Sponza/sponza_flagpole_diff.png b/projects/first_scene/resources/Sponza/sponza_flagpole_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..5f6e0812a0df80346318baa3cb50a6888afc58f8 --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_flagpole_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bfffb62e770959c725d0f3db6dc7dbdd46a380ec55ef884dab94d44ca017b438 +size 1425673 diff --git a/projects/first_scene/resources/Sponza/sponza_floor_a_diff.png b/projects/first_scene/resources/Sponza/sponza_floor_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..788ed764f79ba724f04a2d603076a5b85013e188 --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_floor_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a16f9230fa91f9f31dfca6216ce205f1ef132d44f3b012fbf6efc0fba69770ab +size 1996838 diff --git a/projects/first_scene/resources/Sponza/sponza_roof_diff.png b/projects/first_scene/resources/Sponza/sponza_roof_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..c5b84261fdd1cc776a94b3ce398c7806b895f9a3 --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_roof_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7fc412138c20da19f8173e53545e771f4652558dff624d4dc67143e40efe562b +size 2320533 diff --git a/projects/first_scene/resources/Sponza/sponza_thorn_diff.png b/projects/first_scene/resources/Sponza/sponza_thorn_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..7a9142674a7d4a6f94a48c5152cf0300743b597a --- /dev/null +++ b/projects/first_scene/resources/Sponza/sponza_thorn_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a73a17c883cd0d0d67cfda2dc4118400a916366c05b9a5ac465f0c8b30fd9c8e +size 635001 diff --git a/projects/first_scene/resources/Sponza/vase_dif.png b/projects/first_scene/resources/Sponza/vase_dif.png new file mode 100644 index 0000000000000000000000000000000000000000..61236a81cb324af8797b05099cd264cefe189e56 --- /dev/null +++ b/projects/first_scene/resources/Sponza/vase_dif.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53d06f52bf9e59df4cf00237707cca76c4f692bda61a62b06a30d321311d6dd9 +size 1842101 diff --git a/projects/first_scene/resources/Sponza/vase_hanging.png b/projects/first_scene/resources/Sponza/vase_hanging.png new file mode 100644 index 0000000000000000000000000000000000000000..36a3cee71d8213225090c74f8c0dce33b9d44378 --- /dev/null +++ b/projects/first_scene/resources/Sponza/vase_hanging.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9d10b4f27a3c9a78d5bac882fdd4b6a6987c262f48fa490670fe5e235951e31 +size 1432804 diff --git a/projects/first_scene/resources/Sponza/vase_plant.png b/projects/first_scene/resources/Sponza/vase_plant.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad95e702e229f1ebd803e5203a266d15f2c07b9 --- /dev/null +++ b/projects/first_scene/resources/Sponza/vase_plant.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2087371ff02212fb7014b6daefa191cf5676d2227193fff261a5d02f554cb8e +size 998089 diff --git a/projects/first_scene/resources/Sponza/vase_round.png b/projects/first_scene/resources/Sponza/vase_round.png new file mode 100644 index 0000000000000000000000000000000000000000..c17953abc000c44b8991e23c136c2b67348f3d1b --- /dev/null +++ b/projects/first_scene/resources/Sponza/vase_round.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa23d48d492d5d4ada2ddb27d1ef22952b214e6eb3b301c65f9d88442723d20a +size 1871399 diff --git a/projects/first_scene/resources/Szene/Szene.bin b/projects/first_scene/resources/Szene/Szene.bin new file mode 100644 index 0000000000000000000000000000000000000000..c87d27637516b0bbf864251dd162773f5cc53e06 --- /dev/null +++ b/projects/first_scene/resources/Szene/Szene.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee4742718f720d589a2a03f5d879f8c50ba9057718d191a43b046eaa9071080d +size 70328 diff --git a/projects/first_scene/resources/Szene/Szene.gltf b/projects/first_scene/resources/Szene/Szene.gltf new file mode 100644 index 0000000000000000000000000000000000000000..e5a32b29af5d0a2ac5f497e60b4b92c1873e1df9 --- /dev/null +++ b/projects/first_scene/resources/Szene/Szene.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75ba834118792ebbacf528a1690c7d04df4b4c8122b9f99a9aa9a9d075d2c86a +size 7421 diff --git a/projects/first_scene/resources/Szene/boards2_vcyc.jpg b/projects/first_scene/resources/Szene/boards2_vcyc.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248 --- /dev/null +++ b/projects/first_scene/resources/Szene/boards2_vcyc.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564 +size 1192476 diff --git a/projects/first_scene/resources/Szene/boards2_vcyc_jpg.jpg b/projects/first_scene/resources/Szene/boards2_vcyc_jpg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248 --- /dev/null +++ b/projects/first_scene/resources/Szene/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/first_scene/resources/shaders/compile.bat b/projects/first_scene/resources/shaders/compile.bat new file mode 100644 index 0000000000000000000000000000000000000000..b4521235c40fe5fb163bab874560c2f219b7517f --- /dev/null +++ b/projects/first_scene/resources/shaders/compile.bat @@ -0,0 +1,3 @@ +%VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv +%VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv +pause \ No newline at end of file diff --git a/projects/first_scene/resources/shaders/frag.spv b/projects/first_scene/resources/shaders/frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..087e4e22fb2fcec27d99b3ff2aa1a705fe755796 Binary files /dev/null and b/projects/first_scene/resources/shaders/frag.spv differ diff --git a/projects/first_scene/resources/shaders/shader.frag b/projects/first_scene/resources/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..b5494bea7d6497e2e3dcd8559606864a71adb74e --- /dev/null +++ b/projects/first_scene/resources/shaders/shader.frag @@ -0,0 +1,15 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 passNormal; +layout(location = 1) in vec2 passUV; + +layout(location = 0) out vec3 outColor; + +layout(set=0, binding=0) uniform texture2D meshTexture; +layout(set=0, binding=1) uniform sampler textureSampler; + +void main() { + outColor = texture(sampler2D(meshTexture, textureSampler), passUV).rgb; + //outColor = passNormal * 0.5 + 0.5; +} \ No newline at end of file diff --git a/projects/first_scene/resources/shaders/shader.vert b/projects/first_scene/resources/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..76855152253b48b7400f016d063ed4f0e507435e --- /dev/null +++ b/projects/first_scene/resources/shaders/shader.vert @@ -0,0 +1,19 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec2 inUV; + +layout(location = 0) out vec3 passNormal; +layout(location = 1) out vec2 passUV; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +void main() { + gl_Position = mvp * vec4(inPosition, 1.0); + passNormal = inNormal; + passUV = inUV; +} \ No newline at end of file diff --git a/projects/first_scene/resources/shaders/vert.spv b/projects/first_scene/resources/shaders/vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..374c023e14b351eb43cbcda5951cbb8b3d6f96a1 Binary files /dev/null and b/projects/first_scene/resources/shaders/vert.spv differ diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..420400cdd04865ddd48eec7cf6000e36417d8095 --- /dev/null +++ b/projects/first_scene/src/main.cpp @@ -0,0 +1,264 @@ +#include <iostream> +#include <vkcv/Core.hpp> +#include <GLFW/glfw3.h> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/Logger.hpp> + +glm::mat4 arrayTo4x4Matrix(std::array<float,16> array){ + glm::mat4 matrix; + for (int i = 0; i < 4; i++){ + for (int j = 0; j < 4; j++){ + matrix[i][j] = array[j * 4 + i]; + } + } + return matrix; +} + +int main(int argc, const char** argv) { + const char* applicationName = "First Scene"; + + uint32_t windowWidth = 800; + uint32_t windowHeight = 600; + + vkcv::Window window = vkcv::Window::create( + applicationName, + windowWidth, + windowHeight, + true + ); + + vkcv::camera::CameraManager cameraManager(window); + 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)); + cameraManager.getCamera(camIndex0).setNearFar(0.1f, 30.0f); + + cameraManager.getCamera(camIndex1).setNearFar(0.1f, 30.0f); + + vkcv::Core core = vkcv::Core::create( + window, + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer }, + {}, + { "VK_KHR_swapchain" } + ); + + vkcv::asset::Scene scene; + + const char* path = argc > 1 ? argv[1] : "resources/Sponza/Sponza.gltf"; + int result = vkcv::asset::loadScene(path, scene); + + if (result == 1) { + std::cout << "Mesh loading successful!" << std::endl; + } + else { + std::cout << "Mesh loading failed: " << result << std::endl; + return 1; + } + + assert(!scene.vertexGroups.empty()); + std::vector<std::vector<uint8_t>> vBuffers; + std::vector<std::vector<uint8_t>> iBuffers; + + std::vector<vkcv::VertexBufferBinding> vBufferBindings; + std::vector<std::vector<vkcv::VertexBufferBinding>> vertexBufferBindings; + std::vector<vkcv::asset::VertexAttribute> vAttributes; + + for (int i = 0; i < scene.vertexGroups.size(); i++) { + + vBuffers.push_back(scene.vertexGroups[i].vertexBuffer.data); + iBuffers.push_back(scene.vertexGroups[i].indexBuffer.data); + + auto& attributes = scene.vertexGroups[i].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); + }); + } + + std::vector<vkcv::Buffer<uint8_t>> vertexBuffers; + for (const vkcv::asset::VertexGroup& group : scene.vertexGroups) { + vertexBuffers.push_back(core.createBuffer<uint8_t>( + vkcv::BufferType::VERTEX, + group.vertexBuffer.data.size())); + vertexBuffers.back().fill(group.vertexBuffer.data); + } + + std::vector<vkcv::Buffer<uint8_t>> indexBuffers; + for (const auto& dataBuffer : iBuffers) { + indexBuffers.push_back(core.createBuffer<uint8_t>( + vkcv::BufferType::INDEX, + dataBuffer.size())); + indexBuffers.back().fill(dataBuffer); + } + + int vertexBufferIndex = 0; + for (const auto& vertexGroup : scene.vertexGroups) { + for (const auto& attribute : vertexGroup.vertexBuffer.attributes) { + vAttributes.push_back(attribute); + vBufferBindings.push_back(vkcv::VertexBufferBinding(attribute.offset, vertexBuffers[vertexBufferIndex].getVulkanHandle())); + } + vertexBufferBindings.push_back(vBufferBindings); + vBufferBindings.clear(); + vertexBufferIndex++; + } + + const vkcv::AttachmentDescription present_color_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + core.getSwapchain().getFormat() + ); + + const vkcv::AttachmentDescription depth_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + vk::Format::eD32Sfloat + ); + + vkcv::PassConfig scenePassDefinition({ present_color_attachment, depth_attachment }); + vkcv::PassHandle scenePass = core.createPass(scenePassDefinition); + + if (!scenePass) { + std::cout << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::ShaderProgram sceneShaderProgram{}; + sceneShaderProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/vert.spv")); + sceneShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv")); + + const std::vector<vkcv::VertexAttachment> vertexAttachments = sceneShaderProgram.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 sceneLayout(bindings); + + uint32_t setID = 0; + + std::vector<vkcv::DescriptorBinding> descriptorBindings = { sceneShaderProgram.getReflectedDescriptors()[setID] }; + + vkcv::SamplerHandle sampler = core.createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT + ); + + std::vector<vkcv::Image> sceneImages; + std::vector<vkcv::DescriptorSetHandle> descriptorSets; + for (const auto& vertexGroup : scene.vertexGroups) { + descriptorSets.push_back(core.createDescriptorSet(descriptorBindings)); + + const auto& material = scene.materials[vertexGroup.materialIndex]; + + int baseColorIndex = material.baseColor; + if (baseColorIndex < 0) { + vkcv_log(vkcv::LogLevel::WARNING, "Material lacks base color"); + baseColorIndex = 0; + } + + vkcv::asset::Texture& sceneTexture = scene.textures[baseColorIndex]; + + sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, sceneTexture.w, sceneTexture.h)); + sceneImages.back().fill(sceneTexture.data.data()); + + vkcv::DescriptorWrites setWrites; + setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, sceneImages.back().getHandle()) }; + setWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, sampler) }; + core.writeDescriptorSet(descriptorSets.back(), setWrites); + } + + const vkcv::PipelineConfig scenePipelineDefsinition{ + sceneShaderProgram, + UINT32_MAX, + UINT32_MAX, + scenePass, + {sceneLayout}, + { core.getDescriptorSet(descriptorSets[0]).layout }, + true }; + vkcv::PipelineHandle scenePipeline = core.createGraphicsPipeline(scenePipelineDefsinition); + + if (!scenePipeline) { + std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight).getHandle(); + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + std::vector<vkcv::DrawcallInfo> drawcalls; + for(int i = 0; i < scene.vertexGroups.size(); i++){ + vkcv::Mesh renderMesh(vertexBufferBindings[i], indexBuffers[i].getVulkanHandle(), scene.vertexGroups[i].numIndices); + + vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(descriptorSets[i]).vulkanHandle); + + drawcalls.push_back(vkcv::DrawcallInfo(renderMesh, {descriptorUsage})); + } + + std::vector<glm::mat4> modelMatrices; + modelMatrices.resize(scene.vertexGroups.size(), glm::mat4(1.f)); + for (const auto &mesh : scene.meshes) { + const glm::mat4 m = arrayTo4x4Matrix(mesh.modelMatrix); + for (const auto &vertexGroupIndex : mesh.vertexGroups) { + modelMatrices[vertexGroupIndex] = m; + } + } + std::vector<glm::mat4> mvp; + + 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 = std::chrono::duration_cast<std::chrono::microseconds>(end - start); + + start = end; + cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); + glm::mat4 vp = cameraManager.getActiveCamera().getMVP(); + + mvp.clear(); + for (const auto& m : modelMatrices) { + mvp.push_back(vp * m); + } + + vkcv::PushConstantData pushConstantData((void*)mvp.data(), sizeof(glm::mat4)); + + const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + core.recordDrawcallsToCmdStream( + cmdStream, + scenePass, + scenePipeline, + pushConstantData, + drawcalls, + renderTargets); + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + core.endFrame(); + } + + return 0; +} diff --git a/projects/first_triangle/CMakeLists.txt b/projects/first_triangle/CMakeLists.txt index e7c8373e085df6497060b8d1d8164cf740dfb01f..ba8c83c06fc804082e6a0a14c3c0414899ef3057 100644 --- a/projects/first_triangle/CMakeLists.txt +++ b/projects/first_triangle/CMakeLists.txt @@ -22,7 +22,7 @@ if(MSVC) endif() # including headers of dependencies and the VkCV framework -target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_camera_include}) +target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include}) # linking with libraries from all dependencies and the VkCV framework -target_link_libraries(first_triangle vkcv vkcv_testing vkcv_camera) +target_link_libraries(first_triangle vkcv vkcv_testing vkcv_camera vkcv_shader_compiler vkcv_gui) 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/shaders/shader.frag b/projects/first_triangle/shaders/shader.frag index d26446a73020111695aa2c86166205796dfa5e44..080678beb011afe4b03aed3bf7ae7148b77932dc 100644 --- a/projects/first_triangle/shaders/shader.frag +++ b/projects/first_triangle/shaders/shader.frag @@ -4,6 +4,6 @@ layout(location = 0) in vec3 fragColor; layout(location = 0) out vec4 outColor; -void main() { +void main() { outColor = vec4(fragColor, 1.0); } \ No newline at end of file diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index 2c071b727f7e41e7eb4dee17a2da3302f615ffa0..20cfdddf5c1baa9e8727312daa36de94bd56672f 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -4,6 +4,9 @@ #include <vkcv/camera/CameraManager.hpp> #include <chrono> +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/gui/GUI.hpp> + int main(int argc, const char** argv) { const char* applicationName = "First Triangle"; @@ -16,10 +19,6 @@ int main(int argc, const char** argv) { false ); - vkcv::CameraManager cameraManager(window, windowWidth, windowHeight); - - window.initEvents(); - vkcv::Core core = vkcv::Core::create( window, applicationName, @@ -28,6 +27,8 @@ int main(int argc, const char** argv) { {}, { "VK_KHR_swapchain" } ); + + vkcv::gui::GUI gui (core, window); const auto& context = core.getContext(); const vk::Instance& instance = context.getInstance(); @@ -79,12 +80,9 @@ 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()); + core.getSwapchain().getFormat()); vkcv::PassConfig trianglePassDefinition({ present_color_attachment }); vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition); @@ -96,27 +94,58 @@ int main(int argc, const char** argv) { } vkcv::ShaderProgram triangleShaderProgram{}; - triangleShaderProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/vert.spv")); - triangleShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/frag.spv")); - triangleShaderProgram.reflectShader(vkcv::ShaderStage::VERTEX); - triangleShaderProgram.reflectShader(vkcv::ShaderStage::FRAGMENT); + vkcv::shader::GLSLCompiler compiler; + + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/shader.vert"), + [&triangleShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + triangleShaderProgram.addShader(shaderStage, path); + }); + + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/shader.frag"), + [&triangleShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + triangleShaderProgram.addShader(shaderStage, path); + }); - const vkcv::PipelineConfig trianglePipelineDefinition( + 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); @@ -133,29 +162,70 @@ 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); + 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, -2)); + cameraManager.getCamera(camIndex1).setPosition(glm::vec3(0.0f, 0.0f, 0.0f)); + cameraManager.getCamera(camIndex1).setCenter(glm::vec3(0.0f, 0.0f, -1.0f)); + 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, - windowWidth, - windowHeight, - 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); + + gui.beginGUI(); + + ImGui::Begin("Hello world"); + ImGui::Text("This is a test!"); + ImGui::End(); + + gui.endGUI(); core.endFrame(); } diff --git a/projects/voxelization/.gitignore b/projects/voxelization/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f07a22d4e0641a9114e998212f38ca9cb83a9655 --- /dev/null +++ b/projects/voxelization/.gitignore @@ -0,0 +1 @@ +voxelization \ No newline at end of file diff --git a/projects/voxelization/CMakeLists.txt b/projects/voxelization/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..bc87996096226af4e3f3d05c3e10bb287c61cc8d --- /dev/null +++ b/projects/voxelization/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.16) +project(voxelization) + +# 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(voxelization src/main.cpp) + +target_sources(voxelization PRIVATE + src/Voxelization.hpp + src/Voxelization.cpp) + +# this should fix the execution path to load local files from the project (for MSVC) +if(MSVC) + set_target_properties(voxelization PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set_target_properties(voxelization 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(voxelization PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +endif() + +# including headers of dependencies and the VkCV framework +target_include_directories(voxelization SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(voxelization vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler vkcv_gui) diff --git a/projects/voxelization/resources/Sponza/Sponza.bin b/projects/voxelization/resources/Sponza/Sponza.bin new file mode 100644 index 0000000000000000000000000000000000000000..cfedd26ca5a67b6d0a47d44d13a75e14a141717a --- /dev/null +++ b/projects/voxelization/resources/Sponza/Sponza.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b809f7a17687dc99e6f41ca1ea32c06eded8779bf34d16f1f565d750b0ffd68 +size 6347696 diff --git a/projects/voxelization/resources/Sponza/Sponza.gltf b/projects/voxelization/resources/Sponza/Sponza.gltf new file mode 100644 index 0000000000000000000000000000000000000000..172ea07e21c94465211c860cd805355704cef230 --- /dev/null +++ b/projects/voxelization/resources/Sponza/Sponza.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5cc0ecad5c4694088ff820e663619c370421afc1323ac487406e8e9b4735d787 +size 713962 diff --git a/projects/voxelization/resources/Sponza/background.png b/projects/voxelization/resources/Sponza/background.png new file mode 100644 index 0000000000000000000000000000000000000000..b64def129da38f4e23d89e21b4af1039008a4327 --- /dev/null +++ b/projects/voxelization/resources/Sponza/background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5b5f900ff8ed83a31750ec8e428b5b91273794ddcbfc4e4b8a6a7e781f8c686 +size 1417666 diff --git a/projects/voxelization/resources/Sponza/chain_texture.png b/projects/voxelization/resources/Sponza/chain_texture.png new file mode 100644 index 0000000000000000000000000000000000000000..c1e1768cff78e0614ad707eca8602a4c4edab5e5 --- /dev/null +++ b/projects/voxelization/resources/Sponza/chain_texture.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8362cfd472880daeaea37439326a4651d1338680ae69bb2513fc6b17c8de7d4 +size 490895 diff --git a/projects/voxelization/resources/Sponza/lion.png b/projects/voxelization/resources/Sponza/lion.png new file mode 100644 index 0000000000000000000000000000000000000000..c49c7f0ed31e762e19284d0d3624fbc47664e56b --- /dev/null +++ b/projects/voxelization/resources/Sponza/lion.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f882f746c3a9cd51a9c6eedc1189b97668721d91a3fe49232036e789912c652 +size 2088728 diff --git a/projects/voxelization/resources/Sponza/spnza_bricks_a_diff.png b/projects/voxelization/resources/Sponza/spnza_bricks_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..cde4c7a6511e9a5f03c63ad996437fcdba3ce2df --- /dev/null +++ b/projects/voxelization/resources/Sponza/spnza_bricks_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b94219c2f5f943f3f4715c74e7d1038bf0ab3b3b3216a758eaee67f875df0851 +size 1928829 diff --git a/projects/voxelization/resources/Sponza/sponza_arch_diff.png b/projects/voxelization/resources/Sponza/sponza_arch_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..bcd9bda2918d226039f9e2d03902d377b706fab6 --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_arch_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0df2c8a01b2843b1c792b494f7173cdbc4f834840fc2177af3e5d690fceda57 +size 1596151 diff --git a/projects/voxelization/resources/Sponza/sponza_ceiling_a_diff.png b/projects/voxelization/resources/Sponza/sponza_ceiling_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..59de631ffac4414cabf69b2dc794c46fc187d6cb --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_ceiling_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab6c187a81aa68f4eba30119e17fce2e4882a9ec320f70c90482dbe9da82b1c6 +size 1872074 diff --git a/projects/voxelization/resources/Sponza/sponza_column_a_diff.png b/projects/voxelization/resources/Sponza/sponza_column_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..01a82432d3f9939bbefe850bdb900f1ff9a3f6db --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_column_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c291507e2808bb83e160ab4b020689817df273baad3713a9ad19ac15fac6826 +size 1840992 diff --git a/projects/voxelization/resources/Sponza/sponza_column_b_diff.png b/projects/voxelization/resources/Sponza/sponza_column_b_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..10a660cce2a5a9b8997772c746058ce23e7d45d7 --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_column_b_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2820b0267c4289c6cedbb42721792a57ef244ec2d0935941011c2a7d3fe88a9b +size 2170433 diff --git a/projects/voxelization/resources/Sponza/sponza_column_c_diff.png b/projects/voxelization/resources/Sponza/sponza_column_c_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..bc46fd979044a938d3adca7601689e71504e48bf --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_column_c_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0bc993ff59865468ef4530798930c7dfefb07482d71db45bc2a520986b27735 +size 2066950 diff --git a/projects/voxelization/resources/Sponza/sponza_curtain_blue_diff.png b/projects/voxelization/resources/Sponza/sponza_curtain_blue_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..384c8c2c051160d530eb3ac8b05c9c60752a2d2b --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_curtain_blue_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b85c6bb3cd5105f48d3812ec8e7a1068521ce69e917300d79e136e19d45422fb +size 9510905 diff --git a/projects/voxelization/resources/Sponza/sponza_curtain_diff.png b/projects/voxelization/resources/Sponza/sponza_curtain_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..af842e9f5fe18c1f609875e00899a6770fa4488b --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_curtain_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:563c56bdbbee395a6ef7f0c51c8ac9223c162e517b4cdba0d4654e8de27c98d8 +size 9189263 diff --git a/projects/voxelization/resources/Sponza/sponza_curtain_green_diff.png b/projects/voxelization/resources/Sponza/sponza_curtain_green_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..6c9b6391a199407637fa71033d79fb58b8b4f0d7 --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_curtain_green_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:238fe1c7f481388d1c1d578c2da8d411b99e8f0030ab62060a306db333124476 +size 8785458 diff --git a/projects/voxelization/resources/Sponza/sponza_details_diff.png b/projects/voxelization/resources/Sponza/sponza_details_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..12656686362c3e0a297e060491f33bd7351551f9 --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_details_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb1223b3bb82f8757e7df25a6891f1239cdd7ec59990340e952fb2d6b7ea570c +size 1522643 diff --git a/projects/voxelization/resources/Sponza/sponza_fabric_blue_diff.png b/projects/voxelization/resources/Sponza/sponza_fabric_blue_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..879d16ef84722a4fc13e83a771778de326e4bc54 --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_fabric_blue_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:467d290bf5d4b2a017da140ba9e244ed8a8a9be5418a9ac9bcb4ad572ae2d7ab +size 2229440 diff --git a/projects/voxelization/resources/Sponza/sponza_fabric_diff.png b/projects/voxelization/resources/Sponza/sponza_fabric_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..3311287a219d2148620b87fe428fea071688d051 --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_fabric_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1594f59cc2848db26add47361f4e665e3d8afa147760ed915d839fea42b20287 +size 2267382 diff --git a/projects/voxelization/resources/Sponza/sponza_fabric_green_diff.png b/projects/voxelization/resources/Sponza/sponza_fabric_green_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..de110f369004388dae4cd5067c63428db3a07834 --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_fabric_green_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:902b87faab221173bf370cea7c74cb9060b4d870ac6316b190dafded1cb12993 +size 2258220 diff --git a/projects/voxelization/resources/Sponza/sponza_flagpole_diff.png b/projects/voxelization/resources/Sponza/sponza_flagpole_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..5f6e0812a0df80346318baa3cb50a6888afc58f8 --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_flagpole_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bfffb62e770959c725d0f3db6dc7dbdd46a380ec55ef884dab94d44ca017b438 +size 1425673 diff --git a/projects/voxelization/resources/Sponza/sponza_floor_a_diff.png b/projects/voxelization/resources/Sponza/sponza_floor_a_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..788ed764f79ba724f04a2d603076a5b85013e188 --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_floor_a_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a16f9230fa91f9f31dfca6216ce205f1ef132d44f3b012fbf6efc0fba69770ab +size 1996838 diff --git a/projects/voxelization/resources/Sponza/sponza_roof_diff.png b/projects/voxelization/resources/Sponza/sponza_roof_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..c5b84261fdd1cc776a94b3ce398c7806b895f9a3 --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_roof_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7fc412138c20da19f8173e53545e771f4652558dff624d4dc67143e40efe562b +size 2320533 diff --git a/projects/voxelization/resources/Sponza/sponza_thorn_diff.png b/projects/voxelization/resources/Sponza/sponza_thorn_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..7a9142674a7d4a6f94a48c5152cf0300743b597a --- /dev/null +++ b/projects/voxelization/resources/Sponza/sponza_thorn_diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a73a17c883cd0d0d67cfda2dc4118400a916366c05b9a5ac465f0c8b30fd9c8e +size 635001 diff --git a/projects/voxelization/resources/Sponza/vase_dif.png b/projects/voxelization/resources/Sponza/vase_dif.png new file mode 100644 index 0000000000000000000000000000000000000000..61236a81cb324af8797b05099cd264cefe189e56 --- /dev/null +++ b/projects/voxelization/resources/Sponza/vase_dif.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53d06f52bf9e59df4cf00237707cca76c4f692bda61a62b06a30d321311d6dd9 +size 1842101 diff --git a/projects/voxelization/resources/Sponza/vase_hanging.png b/projects/voxelization/resources/Sponza/vase_hanging.png new file mode 100644 index 0000000000000000000000000000000000000000..36a3cee71d8213225090c74f8c0dce33b9d44378 --- /dev/null +++ b/projects/voxelization/resources/Sponza/vase_hanging.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9d10b4f27a3c9a78d5bac882fdd4b6a6987c262f48fa490670fe5e235951e31 +size 1432804 diff --git a/projects/voxelization/resources/Sponza/vase_plant.png b/projects/voxelization/resources/Sponza/vase_plant.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad95e702e229f1ebd803e5203a266d15f2c07b9 --- /dev/null +++ b/projects/voxelization/resources/Sponza/vase_plant.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2087371ff02212fb7014b6daefa191cf5676d2227193fff261a5d02f554cb8e +size 998089 diff --git a/projects/voxelization/resources/Sponza/vase_round.png b/projects/voxelization/resources/Sponza/vase_round.png new file mode 100644 index 0000000000000000000000000000000000000000..c17953abc000c44b8991e23c136c2b67348f3d1b --- /dev/null +++ b/projects/voxelization/resources/Sponza/vase_round.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa23d48d492d5d4ada2ddb27d1ef22952b214e6eb3b301c65f9d88442723d20a +size 1871399 diff --git a/projects/voxelization/resources/cube/boards2_vcyc_jpg.jpg b/projects/voxelization/resources/cube/boards2_vcyc_jpg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2636039e272289c0fba3fa2d88a060b857501248 --- /dev/null +++ b/projects/voxelization/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/voxelization/resources/cube/cube.bin b/projects/voxelization/resources/cube/cube.bin new file mode 100644 index 0000000000000000000000000000000000000000..3303cd8635848bee18e10ab8754d5e4e7218db92 --- /dev/null +++ b/projects/voxelization/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/voxelization/resources/cube/cube.blend b/projects/voxelization/resources/cube/cube.blend new file mode 100644 index 0000000000000000000000000000000000000000..62ccb2c742094bcfb5ed194ab905bffae86bfd65 --- /dev/null +++ b/projects/voxelization/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/voxelization/resources/cube/cube.blend1 b/projects/voxelization/resources/cube/cube.blend1 new file mode 100644 index 0000000000000000000000000000000000000000..13f21dcca218d7bc7a07a8a9682b5e1d9e607736 --- /dev/null +++ b/projects/voxelization/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/voxelization/resources/cube/cube.glb b/projects/voxelization/resources/cube/cube.glb new file mode 100644 index 0000000000000000000000000000000000000000..66a42c65e71dcf375e04cc378256024dd3c7834d --- /dev/null +++ b/projects/voxelization/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/voxelization/resources/cube/cube.gltf b/projects/voxelization/resources/cube/cube.gltf new file mode 100644 index 0000000000000000000000000000000000000000..428176144843dd06c78fe1d11a6392a0ea02b22d --- /dev/null +++ b/projects/voxelization/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/voxelization/resources/shaders/lightInfo.inc b/projects/voxelization/resources/shaders/lightInfo.inc new file mode 100644 index 0000000000000000000000000000000000000000..4345d4f1504d27df7392b34bcaf17efdcfecef33 --- /dev/null +++ b/projects/voxelization/resources/shaders/lightInfo.inc @@ -0,0 +1,6 @@ +struct LightInfo{ + vec3 L; float padding; + vec3 sunColor; + float sunStrength; + mat4 lightMatrix; +}; \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/perMeshResources.inc b/projects/voxelization/resources/shaders/perMeshResources.inc new file mode 100644 index 0000000000000000000000000000000000000000..95e4fb7c27009965659d14a9c72acfec950c37e3 --- /dev/null +++ b/projects/voxelization/resources/shaders/perMeshResources.inc @@ -0,0 +1,2 @@ +layout(set=1, binding=0) uniform texture2D albedoTexture; +layout(set=1, binding=1) uniform sampler textureSampler; \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/shader.frag b/projects/voxelization/resources/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..8653ae5958ce3b42eac6b1eaa6813f85b6ed589c --- /dev/null +++ b/projects/voxelization/resources/shaders/shader.frag @@ -0,0 +1,28 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "perMeshResources.inc" +#include "lightInfo.inc" +#include "shadowMapping.inc" + +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 sunBuffer { + LightInfo lightInfo; +}; +layout(set=0, binding=1) uniform texture2D shadowMap; +layout(set=0, binding=2) uniform sampler shadowMapSampler; + +void main() { + vec3 N = normalize(passNormal); + vec3 sun = lightInfo.sunStrength * lightInfo.sunColor * clamp(dot(N, lightInfo.L), 0, 1); + sun *= shadowTest(passPos, lightInfo, shadowMap, shadowMapSampler); + vec3 ambient = vec3(0.05); + vec3 albedo = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb; + outColor = albedo * (sun + ambient); +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/shader.vert b/projects/voxelization/resources/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..926f86af2860cb57c44d2d5ee78712b6ae155e5c --- /dev/null +++ b/projects/voxelization/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 = mat3(model) * inNormal; // assuming no weird stuff like shearing or non-uniform scaling + passUV = inUV; + passPos = (model * vec4(inPosition, 1)).xyz; +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/shadow.frag b/projects/voxelization/resources/shaders/shadow.frag new file mode 100644 index 0000000000000000000000000000000000000000..848f853f556660b4900b5db7fb6fc98d57c1cd5b --- /dev/null +++ b/projects/voxelization/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/voxelization/resources/shaders/shadow.vert b/projects/voxelization/resources/shaders/shadow.vert new file mode 100644 index 0000000000000000000000000000000000000000..e0f41d42d575fa64fedbfa04adf89ac0f4aeebe8 --- /dev/null +++ b/projects/voxelization/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/voxelization/resources/shaders/shadowMapping.inc b/projects/voxelization/resources/shaders/shadowMapping.inc new file mode 100644 index 0000000000000000000000000000000000000000..1fa34a388c35b96a3316e972ca562d35e2c3cf90 --- /dev/null +++ b/projects/voxelization/resources/shaders/shadowMapping.inc @@ -0,0 +1,16 @@ +float shadowTest(vec3 worldPos, LightInfo lightInfo, texture2D shadowMap, sampler shadowMapSampler){ + vec4 lightPos = lightInfo.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; +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/tonemapping.comp b/projects/voxelization/resources/shaders/tonemapping.comp new file mode 100644 index 0000000000000000000000000000000000000000..2383302fa946e7d92871039daff28232df2eafdd --- /dev/null +++ b/projects/voxelization/resources/shaders/tonemapping.comp @@ -0,0 +1,19 @@ +#version 440 + +layout(set=0, binding=0, r11f_g11f_b10f) uniform image2D inImage; +layout(set=0, binding=1, rgba8) uniform image2D outImage; + + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))){ + return; + } + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + vec3 linearColor = imageLoad(inImage, uv).rgb; + vec3 tonemapped = linearColor / (linearColor + 1); // reinhard tonemapping + vec3 gammaCorrected = pow(tonemapped, vec3(1.f / 2.2f)); + imageStore(outImage, uv, vec4(gammaCorrected, 0.f)); +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxel.inc b/projects/voxelization/resources/shaders/voxel.inc new file mode 100644 index 0000000000000000000000000000000000000000..25c0a82bbc887913a4d69ccdeee2b0d8934828c8 --- /dev/null +++ b/projects/voxelization/resources/shaders/voxel.inc @@ -0,0 +1,42 @@ +struct VoxelInfo{ + vec3 offset; + float extent; +}; + +uint flattenVoxelUVToIndex(ivec3 UV, ivec3 voxelImageSize){ + return UV.x + UV.y * voxelImageSize.x + UV.z * voxelImageSize.x* voxelImageSize.y; +} + +// packed voxel data: +// 1 bit opacity +// 7 bit exposure +// 8 bit blue +// 8 bit green +// 8 bit red +float maxExposure = 16.f; + +uint packVoxelInfo(vec3 color){ + + color = clamp(color, vec3(0), vec3(maxExposure)); + float maxComponent = max(max(max(color.r, color.g), color.b), 1.f); + color /= maxComponent; + + uint opaqueBit = 1 << 31; + uint exposureBits = (0x0000007F & uint(maxComponent / maxExposure * 127)) << 24; + uint redBits = (0x000000FF & uint(color.r * 255)) << 0; + uint greenBits = (0x000000FF & uint(color.g * 255)) << 8; + uint blueBits = (0x000000FF & uint(color.b * 255)) << 16; + return opaqueBit | exposureBits | blueBits | greenBits | redBits; +} + +vec4 unpackVoxelInfo(uint packed){ + vec4 rgba; + rgba.r = (packed >> 0 & 0x000000FF) / 255.f; + rgba.g = (packed >> 8 & 0x000000FF) / 255.f; + rgba.b = (packed >> 16 & 0x000000FF) / 255.f; + rgba.a = packed >> 31; + + rgba.rgb *= (packed >> 24 & 0x0000007F) / 127.f * maxExposure; + + return rgba; +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelBufferToImage.comp b/projects/voxelization/resources/shaders/voxelBufferToImage.comp new file mode 100644 index 0000000000000000000000000000000000000000..5e8298886cb2bacbc81f981e8e90310cdc876d5d --- /dev/null +++ b/projects/voxelization/resources/shaders/voxelBufferToImage.comp @@ -0,0 +1,24 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#include "voxel.inc" + +layout(set=0, binding=0, std430) buffer voxelBuffer{ + uint packedVoxelData[]; +}; + +layout(set=0, binding=1, rgba16f) uniform image3D voxelImage; + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +void main(){ + + ivec3 voxelImageSize = imageSize(voxelImage); + if(any(greaterThanEqual(gl_GlobalInvocationID, voxelImageSize))){ + return; + } + ivec3 UV = ivec3(gl_GlobalInvocationID); + uint flatIndex = flattenVoxelUVToIndex(UV, voxelImageSize); + + vec4 color = unpackVoxelInfo(packedVoxelData[flatIndex]); + imageStore(voxelImage, UV, vec4(color)); +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelReset.comp b/projects/voxelization/resources/shaders/voxelReset.comp new file mode 100644 index 0000000000000000000000000000000000000000..14b78d6584d703be68594e3cb03ebcd47c94b6e0 --- /dev/null +++ b/projects/voxelization/resources/shaders/voxelReset.comp @@ -0,0 +1,19 @@ +#version 450 + +layout(set=0, binding=0) buffer voxelizationBuffer{ + uint isFilled[]; +}; + +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout( push_constant ) uniform constants{ + uint voxelCount; +}; + +void main(){ + + if(gl_GlobalInvocationID.x> voxelCount){ + return; + } + isFilled[gl_GlobalInvocationID.x] = 0; +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelVisualisation.frag b/projects/voxelization/resources/shaders/voxelVisualisation.frag new file mode 100644 index 0000000000000000000000000000000000000000..0b02beb7e848ab20cda4b012f77d1fa664b6ab53 --- /dev/null +++ b/projects/voxelization/resources/shaders/voxelVisualisation.frag @@ -0,0 +1,9 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location=0) in vec3 passColorToFrag; +layout(location=0) out vec3 outColor; + +void main() { + outColor = passColorToFrag; +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelVisualisation.geom b/projects/voxelization/resources/shaders/voxelVisualisation.geom new file mode 100644 index 0000000000000000000000000000000000000000..e98076fcc83a69a903df454cb00267da84e3f223 --- /dev/null +++ b/projects/voxelization/resources/shaders/voxelVisualisation.geom @@ -0,0 +1,104 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(points) in; +layout (triangle_strip, max_vertices = 24) out; + +layout( push_constant ) uniform constants{ + mat4 viewProjection; +}; + +layout(location = 0) in float passCubeHalf[1]; +layout(location = 1) in vec3 passColorToGeom[1]; + + +layout(location = 0) out vec3 passColorToFrag; + +void main() { + float cubeHalf = passCubeHalf[0]; + // right + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, 1, 1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, 1, -1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, -1, 1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, -1, -1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + EndPrimitive(); + // left + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, 1, 1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, 1, -1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, -1, 1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, -1, -1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + EndPrimitive(); + // back + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, 1, -1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, -1, -1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, 1, -1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, -1, -1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + EndPrimitive(); + // front + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, 1, 1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, -1, 1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, 1, 1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, -1, 1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + EndPrimitive(); + // bot + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, 1, 1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, 1, -1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, 1, 1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, 1, -1), 1); + passColorToFrag = passColorToGeom[0]; + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + EndPrimitive(); + // top + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, -1, 1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(1, -1, -1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, -1, 1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + gl_Position = viewProjection * vec4(gl_in[0].gl_Position.xyz + cubeHalf * vec3(-1, -1, -1), 1); + passColorToFrag = passColorToGeom[0]; + EmitVertex(); + EndPrimitive(); +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelVisualisation.vert b/projects/voxelization/resources/shaders/voxelVisualisation.vert new file mode 100644 index 0000000000000000000000000000000000000000..8377143f4f4bbf351d3251df9724d37e1747a4dc --- /dev/null +++ b/projects/voxelization/resources/shaders/voxelVisualisation.vert @@ -0,0 +1,36 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "voxel.inc" + +layout(location = 0) out float passCubeHalf; +layout(location = 1) out vec3 passColorToGeom; + +layout( push_constant ) uniform constants{ + mat4 viewProjection; +}; + +layout(set=0, binding=0, rgba16f) uniform image3D voxelImage; +layout(set=0, binding=1) uniform voxelizationInfo{ + VoxelInfo voxelInfo; +}; + + +void main() { + passCubeHalf = voxelInfo.extent / float(imageSize(voxelImage).x) * 0.5f; + int voxelResolution = imageSize(voxelImage).x; + int slicePixelCount = voxelResolution * voxelResolution; + int z = gl_VertexIndex / slicePixelCount; + int index2D = gl_VertexIndex % slicePixelCount; + int y = index2D / voxelResolution; + int x = index2D % voxelResolution; + vec3 position = (vec3(x, y, z) / voxelResolution - 0.5) * voxelInfo.extent + passCubeHalf + voxelInfo.offset; + gl_Position = vec4(position, 1.0); + + vec4 voxelColor = imageLoad(voxelImage, ivec3(x,y,z)); + if(voxelColor.a == 0){ + gl_Position.x /= 0; // clip + } + passColorToGeom = voxelColor.rgb; +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelization.frag b/projects/voxelization/resources/shaders/voxelization.frag new file mode 100644 index 0000000000000000000000000000000000000000..a49b13185ec26b069661141cfdbbfbbe45d14fd3 --- /dev/null +++ b/projects/voxelization/resources/shaders/voxelization.frag @@ -0,0 +1,57 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "voxel.inc" +#include "perMeshResources.inc" +#include "lightInfo.inc" +#include "shadowMapping.inc" + +layout(location = 0) in vec3 passPos; +layout(location = 1) in vec2 passUV; +layout(location = 2) in vec3 passN; + +layout(set=0, binding=0, std430) buffer voxelizationBuffer{ + uint packedVoxelData[]; +}; + +layout(set=0, binding=1) uniform voxelizationInfo{ + VoxelInfo voxelInfo; +}; + +layout(set=0, binding=2, r8) uniform image3D voxelImage; + +layout(set=0, binding=3) uniform sunBuffer { + LightInfo lightInfo; +}; + +layout(set=0, binding=4) uniform texture2D shadowMap; +layout(set=0, binding=5) uniform sampler shadowMapSampler; + +vec3 worldToVoxelCoordinates(vec3 world, VoxelInfo info){ + return (world - info.offset) / info.extent + 0.5f; +} + +ivec3 voxelCoordinatesToUV(vec3 voxelCoordinates, ivec3 voxelImageResolution){ + return ivec3(voxelCoordinates * voxelImageResolution); +} + +void main() { + vec3 voxelCoordinates = worldToVoxelCoordinates(passPos, voxelInfo); + ivec3 voxelImageSize = imageSize(voxelImage); + ivec3 UV = voxelCoordinatesToUV(voxelCoordinates, voxelImageSize); + if(any(lessThan(UV, ivec3(0))) || any(greaterThanEqual(UV, voxelImageSize))){ + return; + } + uint flatIndex = flattenVoxelUVToIndex(UV, voxelImageSize); + + vec3 albedo = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb; + + vec3 N = normalize(passN); + float NoL = clamp(dot(N, lightInfo.L), 0, 1); + vec3 sun = lightInfo.sunStrength * lightInfo.sunColor * NoL * shadowTest(passPos, lightInfo, shadowMap, shadowMapSampler); + vec3 color = albedo * sun; + color = albedo * sun; + + atomicMax(packedVoxelData[flatIndex], packVoxelInfo(color)); +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelization.geom b/projects/voxelization/resources/shaders/voxelization.geom new file mode 100644 index 0000000000000000000000000000000000000000..56542d960d65db6ca12c5f84837cb0c0a9ff0ded --- /dev/null +++ b/projects/voxelization/resources/shaders/voxelization.geom @@ -0,0 +1,38 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(triangles) in; +layout (triangle_strip, max_vertices = 3) out; + +layout(location = 0) in vec3 passPosIn[3]; +layout(location = 1) in vec2 passUVIn[3]; +layout(location = 2) in vec3 passNIn[3]; + +layout(location = 0) out vec3 passPos; +layout(location = 1) out vec2 passUV; +layout(location = 2) out vec3 passN; + +void main() { + // compute geometric normal, no normalization necessary + vec3 N = cross(passPosIn[0] - passPosIn[1], passPosIn[0] - passPosIn[2]); + N = abs(N); // only interested in the magnitude + + for(int i = 0; i < 3; i++){ + // swizzle position, so biggest side is rasterized + if(N.z > N.x && N.z > N.y){ + gl_Position = gl_in[i].gl_Position.xyzw; + } + else if(N.x > N.y){ + gl_Position = gl_in[i].gl_Position.yzxw; + } + else{ + gl_Position = gl_in[i].gl_Position.xzyw; + } + gl_Position.z = gl_Position.z * 0.5 + 0.5; // xyz are kept in NDC range [-1, 1] so swizzling works, but vulkan needs final z in range [0, 1] + passPos = passPosIn[i]; + passUV = passUVIn[i]; + passN = passNIn[i]; + EmitVertex(); + } + EndPrimitive(); +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelization.vert b/projects/voxelization/resources/shaders/voxelization.vert new file mode 100644 index 0000000000000000000000000000000000000000..1302a42441b5b9c8ea7d24f97d29b684e4d64993 --- /dev/null +++ b/projects/voxelization/resources/shaders/voxelization.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 passPos; +layout(location = 1) out vec2 passUV; +layout(location = 2) out vec3 passN; + +layout( push_constant ) uniform constants{ + mat4 mvp; + mat4 model; +}; + +void main() { + gl_Position = mvp * vec4(inPosition, 1.0); + passPos = (model * vec4(inPosition, 1)).xyz; + passUV = inUV; + passN = inNormal; +} \ No newline at end of file diff --git a/projects/voxelization/resources/triangle/Triangle.bin b/projects/voxelization/resources/triangle/Triangle.bin new file mode 100644 index 0000000000000000000000000000000000000000..57f26ad96592b64377e6aa93823d96a94e6c5022 --- /dev/null +++ b/projects/voxelization/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/voxelization/resources/triangle/Triangle.blend b/projects/voxelization/resources/triangle/Triangle.blend new file mode 100644 index 0000000000000000000000000000000000000000..2421dc5e1bb029d73a9ec09cc4530c5196851fd7 --- /dev/null +++ b/projects/voxelization/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/voxelization/resources/triangle/Triangle.glb b/projects/voxelization/resources/triangle/Triangle.glb new file mode 100644 index 0000000000000000000000000000000000000000..4148620cd6af0dadbc791aa1c52bb5431a40884b --- /dev/null +++ b/projects/voxelization/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/voxelization/resources/triangle/Triangle.gltf b/projects/voxelization/resources/triangle/Triangle.gltf new file mode 100644 index 0000000000000000000000000000000000000000..a188e6ee16a5e8486cf307c7bda8cfd99bdbeea6 --- /dev/null +++ b/projects/voxelization/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/voxelization/src/Voxelization.cpp b/projects/voxelization/src/Voxelization.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a04131cedfdc508e14a3dbd97da642e1af48f8da --- /dev/null +++ b/projects/voxelization/src/Voxelization.cpp @@ -0,0 +1,308 @@ +#include "Voxelization.hpp" +#include <vkcv/shader/GLSLCompiler.hpp> +#include <glm/gtc/matrix_transform.hpp> +#include <algorithm> + +vkcv::ShaderProgram loadVoxelizationShader() { + vkcv::shader::GLSLCompiler compiler; + vkcv::ShaderProgram shader; + compiler.compile(vkcv::ShaderStage::VERTEX, "resources/shaders/voxelization.vert", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::GEOMETRY, "resources/shaders/voxelization.geom", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, "resources/shaders/voxelization.frag", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + return shader; +} + +vkcv::ShaderProgram loadVoxelVisualisationShader() { + vkcv::shader::GLSLCompiler compiler; + vkcv::ShaderProgram shader; + compiler.compile(vkcv::ShaderStage::VERTEX, "resources/shaders/voxelVisualisation.vert", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::GEOMETRY, "resources/shaders/voxelVisualisation.geom", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, "resources/shaders/voxelVisualisation.frag", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + return shader; +} + +vkcv::ShaderProgram loadVoxelResetShader() { + vkcv::shader::GLSLCompiler compiler; + vkcv::ShaderProgram shader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/voxelReset.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + return shader; +} + +vkcv::ShaderProgram loadVoxelBufferToImageShader() { + vkcv::shader::GLSLCompiler compiler; + vkcv::ShaderProgram shader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/voxelBufferToImage.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + return shader; +} + +const uint32_t voxelResolution = 128; +uint32_t voxelCount = voxelResolution * voxelResolution * voxelResolution; +const vk::Format voxelizationDummyFormat = vk::Format::eR8Unorm; + +Voxelization::Voxelization( + vkcv::Core* corePtr, + const Dependencies& dependencies, + vkcv::BufferHandle lightInfoBuffer, + vkcv::ImageHandle shadowMap, + vkcv::SamplerHandle shadowSampler) + : + m_corePtr(corePtr), + m_voxelImage(m_corePtr->createImage(vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, true, true)), + m_dummyRenderTarget(m_corePtr->createImage(voxelizationDummyFormat, voxelResolution, voxelResolution, 1, false, false, true)), + m_voxelInfoBuffer(m_corePtr->createBuffer<VoxelizationInfo>(vkcv::BufferType::UNIFORM, 1)), + m_voxelBuffer(m_corePtr->createBuffer<VoxelBufferContent>(vkcv::BufferType::STORAGE, voxelCount)){ + + const vkcv::ShaderProgram voxelizationShader = loadVoxelizationShader(); + + const vkcv::PassConfig voxelizationPassConfig({vkcv::AttachmentDescription( + vkcv::AttachmentOperation::DONT_CARE, + vkcv::AttachmentOperation::DONT_CARE, + voxelizationDummyFormat) }); + m_voxelizationPass = m_corePtr->createPass(voxelizationPassConfig); + + std::vector<vkcv::DescriptorBinding> voxelizationDescriptorBindings = + { voxelizationShader.getReflectedDescriptors()[0] }; + m_voxelizationDescriptorSet = m_corePtr->createDescriptorSet(voxelizationDescriptorBindings); + + vkcv::DescriptorSetHandle dummyPerMeshDescriptorSet = + m_corePtr->createDescriptorSet({ voxelizationShader.getReflectedDescriptors()[1] }); + + const vkcv::PipelineConfig voxelizationPipeConfig{ + voxelizationShader, + voxelResolution, + voxelResolution, + m_voxelizationPass, + dependencies.vertexLayout, + { + m_corePtr->getDescriptorSet(m_voxelizationDescriptorSet).layout, + m_corePtr->getDescriptorSet(dummyPerMeshDescriptorSet).layout}, + false, + true }; + m_voxelizationPipe = m_corePtr->createGraphicsPipeline(voxelizationPipeConfig); + + vkcv::DescriptorWrites voxelizationDescriptorWrites; + voxelizationDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; + voxelizationDescriptorWrites.uniformBufferWrites = { + vkcv::UniformBufferDescriptorWrite(1, m_voxelInfoBuffer.getHandle()), + vkcv::UniformBufferDescriptorWrite(3, lightInfoBuffer) + }; + voxelizationDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(4, shadowMap) }; + voxelizationDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(5, shadowSampler) }; + voxelizationDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, m_voxelImage.getHandle()) }; + m_corePtr->writeDescriptorSet(m_voxelizationDescriptorSet, voxelizationDescriptorWrites); + + vkcv::ShaderProgram voxelVisualisationShader = loadVoxelVisualisationShader(); + + const std::vector<vkcv::DescriptorBinding> voxelVisualisationDescriptorBindings = + { voxelVisualisationShader.getReflectedDescriptors()[0] }; + m_visualisationDescriptorSet = m_corePtr->createDescriptorSet(voxelVisualisationDescriptorBindings); + + const vkcv::AttachmentDescription voxelVisualisationColorAttachments( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::LOAD, + dependencies.colorBufferFormat + ); + + const vkcv::AttachmentDescription voxelVisualisationDepthAttachments( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::LOAD, + dependencies.depthBufferFormat + ); + + vkcv::PassConfig voxelVisualisationPassDefinition( + { voxelVisualisationColorAttachments, voxelVisualisationDepthAttachments }); + m_visualisationPass = m_corePtr->createPass(voxelVisualisationPassDefinition); + + const vkcv::PipelineConfig voxelVisualisationPipeConfig{ + voxelVisualisationShader, + 0, + 0, + m_visualisationPass, + {}, + { m_corePtr->getDescriptorSet(m_visualisationDescriptorSet).layout }, + true, + false, + vkcv::PrimitiveTopology::PointList }; // points are extended to cubes in the geometry shader + m_visualisationPipe = m_corePtr->createGraphicsPipeline(voxelVisualisationPipeConfig); + + std::vector<uint16_t> voxelIndexData; + for (int i = 0; i < voxelCount; i++) { + voxelIndexData.push_back(i); + } + + const vkcv::DescriptorSetUsage voxelizationDescriptorUsage(0, m_corePtr->getDescriptorSet(m_visualisationDescriptorSet).vulkanHandle); + + vkcv::ShaderProgram resetVoxelShader = loadVoxelResetShader(); + + m_voxelResetDescriptorSet = m_corePtr->createDescriptorSet(resetVoxelShader.getReflectedDescriptors()[0]); + m_voxelResetPipe = m_corePtr->createComputePipeline( + resetVoxelShader, + { m_corePtr->getDescriptorSet(m_voxelResetDescriptorSet).layout }); + + vkcv::DescriptorWrites resetVoxelWrites; + resetVoxelWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; + m_corePtr->writeDescriptorSet(m_voxelResetDescriptorSet, resetVoxelWrites); + + + vkcv::ShaderProgram bufferToImageShader = loadVoxelBufferToImageShader(); + + m_bufferToImageDescriptorSet = m_corePtr->createDescriptorSet(bufferToImageShader.getReflectedDescriptors()[0]); + m_bufferToImagePipe = m_corePtr->createComputePipeline( + bufferToImageShader, + { m_corePtr->getDescriptorSet(m_bufferToImageDescriptorSet).layout }); + + vkcv::DescriptorWrites bufferToImageDescriptorWrites; + bufferToImageDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; + bufferToImageDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(1, m_voxelImage.getHandle()) }; + m_corePtr->writeDescriptorSet(m_bufferToImageDescriptorSet, bufferToImageDescriptorWrites); +} + +void Voxelization::voxelizeMeshes( + vkcv::CommandStreamHandle cmdStream, + const glm::vec3& cameraPosition, + const std::vector<vkcv::Mesh>& meshes, + const std::vector<glm::mat4>& modelMatrices, + const std::vector<vkcv::DescriptorSetHandle>& perMeshDescriptorSets) { + + VoxelizationInfo voxelizationInfo; + voxelizationInfo.extent = m_voxelExtent; + + // move voxel offset with camera in voxel sized steps + const float voxelSize = m_voxelExtent / voxelResolution; + voxelizationInfo.offset = glm::floor(cameraPosition / voxelSize) * voxelSize; + + m_voxelInfoBuffer.fill({ voxelizationInfo }); + + const float voxelizationHalfExtent = 0.5f * m_voxelExtent; + const glm::mat4 voxelizationProjection = glm::ortho( + -voxelizationHalfExtent, + voxelizationHalfExtent, + -voxelizationHalfExtent, + voxelizationHalfExtent, + -voxelizationHalfExtent, + voxelizationHalfExtent); + + const glm::mat4 voxelizationView = glm::translate(glm::mat4(1.f), -voxelizationInfo.offset); + const glm::mat4 voxelizationViewProjection = voxelizationProjection * voxelizationView; + + std::vector<std::array<glm::mat4, 2>> voxelizationMatrices; + for (const auto& m : modelMatrices) { + voxelizationMatrices.push_back({ voxelizationViewProjection * m, m }); + } + + const vkcv::PushConstantData voxelizationPushConstantData((void*)voxelizationMatrices.data(), 2 * sizeof(glm::mat4)); + + // reset voxels + const uint32_t resetVoxelGroupSize = 64; + uint32_t resetVoxelDispatchCount[3]; + resetVoxelDispatchCount[0] = glm::ceil(voxelCount / float(resetVoxelGroupSize)); + resetVoxelDispatchCount[1] = 1; + resetVoxelDispatchCount[2] = 1; + + m_corePtr->prepareImageForStorage(cmdStream, m_voxelImage.getHandle()); + m_corePtr->recordComputeDispatchToCmdStream( + cmdStream, + m_voxelResetPipe, + resetVoxelDispatchCount, + { vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_voxelResetDescriptorSet).vulkanHandle) }, + vkcv::PushConstantData(&voxelCount, sizeof(voxelCount))); + m_corePtr->recordBufferMemoryBarrier(cmdStream, m_voxelBuffer.getHandle()); + + // voxelization + std::vector<vkcv::DrawcallInfo> drawcalls; + for (int i = 0; i < meshes.size(); i++) { + drawcalls.push_back(vkcv::DrawcallInfo( + meshes[i], + { + vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_voxelizationDescriptorSet).vulkanHandle), + vkcv::DescriptorSetUsage(1, m_corePtr->getDescriptorSet(perMeshDescriptorSets[i]).vulkanHandle) + })); + } + + m_corePtr->recordDrawcallsToCmdStream( + cmdStream, + m_voxelizationPass, + m_voxelizationPipe, + voxelizationPushConstantData, + drawcalls, + { m_dummyRenderTarget.getHandle() }); + + // buffer to image + const uint32_t bufferToImageGroupSize[3] = { 4, 4, 4 }; + uint32_t bufferToImageDispatchCount[3]; + for (int i = 0; i < 3; i++) { + bufferToImageDispatchCount[i] = glm::ceil(voxelResolution / float(bufferToImageGroupSize[i])); + } + + m_corePtr->recordComputeDispatchToCmdStream( + cmdStream, + m_bufferToImagePipe, + bufferToImageDispatchCount, + { vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_bufferToImageDescriptorSet).vulkanHandle) }, + vkcv::PushConstantData(nullptr, 0)); + + m_corePtr->recordImageMemoryBarrier(cmdStream, m_voxelImage.getHandle()); + + m_voxelImage.recordMipChainGeneration(cmdStream); +} + +void Voxelization::renderVoxelVisualisation( + vkcv::CommandStreamHandle cmdStream, + const glm::mat4& viewProjectin, + const std::vector<vkcv::ImageHandle>& renderTargets, + uint32_t mipLevel) { + + const vkcv::PushConstantData voxelVisualisationPushConstantData((void*)&viewProjectin, sizeof(glm::mat4)); + + mipLevel = std::clamp(mipLevel, (uint32_t)0, m_voxelImage.getMipCount()-1); + + // write descriptor set + vkcv::DescriptorWrites voxelVisualisationDescriptorWrite; + voxelVisualisationDescriptorWrite.storageImageWrites = + { vkcv::StorageImageDescriptorWrite(0, m_voxelImage.getHandle(), mipLevel) }; + voxelVisualisationDescriptorWrite.uniformBufferWrites = + { vkcv::UniformBufferDescriptorWrite(1, m_voxelInfoBuffer.getHandle()) }; + m_corePtr->writeDescriptorSet(m_visualisationDescriptorSet, voxelVisualisationDescriptorWrite); + + uint32_t drawVoxelCount = voxelCount / exp2(mipLevel); + + const auto drawcall = vkcv::DrawcallInfo( + vkcv::Mesh({}, nullptr, drawVoxelCount), + { vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_visualisationDescriptorSet).vulkanHandle) }); + + m_corePtr->recordDrawcallsToCmdStream( + cmdStream, + m_visualisationPass, + m_visualisationPipe, + voxelVisualisationPushConstantData, + { drawcall }, + renderTargets); +} + +void Voxelization::setVoxelExtent(float extent) { + m_voxelExtent = extent; +} \ No newline at end of file diff --git a/projects/voxelization/src/Voxelization.hpp b/projects/voxelization/src/Voxelization.hpp new file mode 100644 index 0000000000000000000000000000000000000000..25830b171edb9154e37b2d597c2bbbf2daea6b2e --- /dev/null +++ b/projects/voxelization/src/Voxelization.hpp @@ -0,0 +1,67 @@ +#pragma once +#include <vkcv/Core.hpp> +#include <glm/glm.hpp> + +class Voxelization{ +public: + struct Dependencies { + vkcv::VertexLayout vertexLayout; + vk::Format colorBufferFormat; + vk::Format depthBufferFormat; + }; + Voxelization( + vkcv::Core* corePtr, + const Dependencies& dependencies, + vkcv::BufferHandle lightInfoBuffer, + vkcv::ImageHandle shadowMap, + vkcv::SamplerHandle shadowSampler); + + void voxelizeMeshes( + vkcv::CommandStreamHandle cmdStream, + const glm::vec3& cameraPosition, + const std::vector<vkcv::Mesh>& meshes, + const std::vector<glm::mat4>& modelMatrices, + const std::vector<vkcv::DescriptorSetHandle>& perMeshDescriptorSets); + + void renderVoxelVisualisation( + vkcv::CommandStreamHandle cmdStream, + const glm::mat4& viewProjectin, + const std::vector<vkcv::ImageHandle>& renderTargets, + uint32_t mipLevel); + + void setVoxelExtent(float extent); + +private: + vkcv::Core* m_corePtr; + + struct VoxelBufferContent{ + uint32_t isFilled; + }; + + vkcv::Image m_voxelImage; + vkcv::Buffer<VoxelBufferContent> m_voxelBuffer; + + vkcv::Image m_dummyRenderTarget; + vkcv::PassHandle m_voxelizationPass; + vkcv::PipelineHandle m_voxelizationPipe; + vkcv::DescriptorSetHandle m_voxelizationDescriptorSet; + + vkcv::PipelineHandle m_voxelResetPipe; + vkcv::DescriptorSetHandle m_voxelResetDescriptorSet; + + vkcv::PipelineHandle m_bufferToImagePipe; + vkcv::DescriptorSetHandle m_bufferToImageDescriptorSet; + + vkcv::PassHandle m_visualisationPass; + vkcv::PipelineHandle m_visualisationPipe; + + vkcv::DescriptorSetHandle m_visualisationDescriptorSet; + + struct VoxelizationInfo { + glm::vec3 offset; + float extent; + }; + vkcv::Buffer<VoxelizationInfo> m_voxelInfoBuffer; + + float m_voxelExtent = 20.f; +}; \ No newline at end of file diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aabed2180f598c9792a76ddcdcf4a2f400d16334 --- /dev/null +++ b/projects/voxelization/src/main.cpp @@ -0,0 +1,475 @@ +#include <iostream> +#include <vkcv/Core.hpp> +#include <GLFW/glfw3.h> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/Logger.hpp> +#include "Voxelization.hpp" +#include <glm/glm.hpp> +#include "vkcv/gui/GUI.hpp" + +int main(int argc, const char** argv) { + const char* applicationName = "Voxelization"; + + uint32_t windowWidth = 1280; + uint32_t windowHeight = 720; + + vkcv::Window window = vkcv::Window::create( + applicationName, + windowWidth, + windowHeight, + true + ); + + vkcv::camera::CameraManager cameraManager(window); + 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::Scene mesh; + + const char* path = argc > 1 ? argv[1] : "resources/Sponza/Sponza.gltf"; + vkcv::asset::Scene scene; + int result = vkcv::asset::loadScene(path, scene); + + if (result == 1) { + std::cout << "Scene loading successful!" << std::endl; + } + else { + std::cout << "Scene loading failed: " << result << std::endl; + return 1; + } + + // build index and vertex buffers + assert(!scene.vertexGroups.empty()); + std::vector<std::vector<uint8_t>> vBuffers; + std::vector<std::vector<uint8_t>> iBuffers; + + std::vector<vkcv::VertexBufferBinding> vBufferBindings; + std::vector<std::vector<vkcv::VertexBufferBinding>> vertexBufferBindings; + std::vector<vkcv::asset::VertexAttribute> vAttributes; + + for (int i = 0; i < scene.vertexGroups.size(); i++) { + + vBuffers.push_back(scene.vertexGroups[i].vertexBuffer.data); + iBuffers.push_back(scene.vertexGroups[i].indexBuffer.data); + + auto& attributes = scene.vertexGroups[i].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); + }); + } + + std::vector<vkcv::Buffer<uint8_t>> vertexBuffers; + for (const vkcv::asset::VertexGroup& group : scene.vertexGroups) { + vertexBuffers.push_back(core.createBuffer<uint8_t>( + vkcv::BufferType::VERTEX, + group.vertexBuffer.data.size())); + vertexBuffers.back().fill(group.vertexBuffer.data); + } + + std::vector<vkcv::Buffer<uint8_t>> indexBuffers; + for (const auto& dataBuffer : iBuffers) { + indexBuffers.push_back(core.createBuffer<uint8_t>( + vkcv::BufferType::INDEX, + dataBuffer.size())); + indexBuffers.back().fill(dataBuffer); + } + + int vertexBufferIndex = 0; + for (const auto& vertexGroup : scene.vertexGroups) { + for (const auto& attribute : vertexGroup.vertexBuffer.attributes) { + vAttributes.push_back(attribute); + vBufferBindings.push_back(vkcv::VertexBufferBinding(attribute.offset, vertexBuffers[vertexBufferIndex].getVulkanHandle())); + } + vertexBufferBindings.push_back(vBufferBindings); + vBufferBindings.clear(); + vertexBufferIndex++; + } + + const vk::Format colorBufferFormat = vk::Format::eB10G11R11UfloatPack32; + const vkcv::AttachmentDescription color_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + colorBufferFormat + ); + + const vk::Format depthBufferFormat = vk::Format::eD32Sfloat; + const vkcv::AttachmentDescription depth_attachment( + vkcv::AttachmentOperation::STORE, + vkcv::AttachmentOperation::CLEAR, + depthBufferFormat + ); + + vkcv::PassConfig forwardPassDefinition({ color_attachment, depth_attachment }); + vkcv::PassHandle forwardPass = core.createPass(forwardPassDefinition); + + vkcv::shader::GLSLCompiler compiler; + + vkcv::ShaderProgram forwardProgram; + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert"), + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + forwardProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"), + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + forwardProgram.addShader(shaderStage, path); + }); + + const std::vector<vkcv::VertexAttachment> vertexAttachments = forwardProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> vertexBindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + vertexBindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] })); + } + const vkcv::VertexLayout vertexLayout (vertexBindings); + + // shadow map + vkcv::SamplerHandle shadowSampler = core.createSampler( + vkcv::SamplerFilterType::NEAREST, + vkcv::SamplerFilterType::NEAREST, + vkcv::SamplerMipmapMode::NEAREST, + vkcv::SamplerAddressMode::CLAMP_TO_EDGE + ); + const vk::Format shadowMapFormat = vk::Format::eD16Unorm; + const uint32_t shadowMapResolution = 1024; + const vkcv::Image shadowMap = core.createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution); + + // light info buffer + struct LightInfo { + glm::vec3 direction; + float padding; + glm::vec3 sunColor = glm::vec3(1.f); + float sunStrength = 8.f; + glm::mat4 lightMatrix; + }; + LightInfo lightInfo; + vkcv::Buffer lightBuffer = core.createBuffer<LightInfo>(vkcv::BufferType::UNIFORM, sizeof(glm::vec3)); + + vkcv::DescriptorSetHandle forwardShadingDescriptorSet = + core.createDescriptorSet({ forwardProgram.getReflectedDescriptors()[0] }); + + vkcv::DescriptorWrites forwardDescriptorWrites; + forwardDescriptorWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(0, lightBuffer.getHandle()) }; + forwardDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(1, shadowMap.getHandle()) }; + forwardDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(2, shadowSampler) }; + core.writeDescriptorSet(forwardShadingDescriptorSet, forwardDescriptorWrites); + + vkcv::SamplerHandle colorSampler = core.createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT + ); + + // create descriptor sets + std::vector<vkcv::DescriptorSetHandle> materialDescriptorSets; + std::vector<vkcv::Image> sceneImages; + + for (const auto& material : scene.materials) { + int baseColorIndex = material.baseColor; + if (baseColorIndex < 0) { + vkcv_log(vkcv::LogLevel::WARNING, "Material lacks base color"); + baseColorIndex = 0; + } + + materialDescriptorSets.push_back(core.createDescriptorSet(forwardProgram.getReflectedDescriptors()[1])); + + vkcv::asset::Texture& sceneTexture = scene.textures[baseColorIndex]; + + sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, sceneTexture.w, sceneTexture.h, 1, true)); + sceneImages.back().fill(sceneTexture.data.data()); + sceneImages.back().generateMipChainImmediate(); + sceneImages.back().switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal); + + vkcv::DescriptorWrites setWrites; + setWrites.sampledImageWrites = { + vkcv::SampledImageDescriptorWrite(0, sceneImages.back().getHandle()) + }; + setWrites.samplerWrites = { + vkcv::SamplerDescriptorWrite(1, colorSampler), + }; + core.writeDescriptorSet(materialDescriptorSets.back(), setWrites); + } + + std::vector<vkcv::DescriptorSetHandle> perMeshDescriptorSets; + for (const auto& vertexGroup : scene.vertexGroups) { + perMeshDescriptorSets.push_back(materialDescriptorSets[vertexGroup.materialIndex]); + } + + const vkcv::PipelineConfig forwardPipelineConfig { + forwardProgram, + windowWidth, + windowHeight, + forwardPass, + vertexLayout, + { core.getDescriptorSet(forwardShadingDescriptorSet).layout, + core.getDescriptorSet(perMeshDescriptorSets[0]).layout }, + true + }; + + vkcv::PipelineHandle forwardPipeline = core.createGraphicsPipeline(forwardPipelineConfig); + + if (!forwardPipeline) { + std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::ImageHandle depthBuffer = core.createImage(depthBufferFormat, windowWidth, windowHeight).getHandle(); + vkcv::ImageHandle colorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true, true).getHandle(); + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::ShaderProgram shadowShader; + compiler.compile(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow.vert", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shadowShader.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow.frag", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shadowShader.addShader(shaderStage, path); + }); + + 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 vkcv::PipelineConfig shadowPipeConfig{ + shadowShader, + shadowMapResolution, + shadowMapResolution, + shadowPass, + vertexLayout, + {}, + false + }; + const vkcv::PipelineHandle shadowPipe = core.createGraphicsPipeline(shadowPipeConfig); + + std::vector<std::array<glm::mat4, 2>> mainPassMatrices; + std::vector<glm::mat4> mvpLight; + + bool renderVoxelVis = false; + window.e_key.add([&renderVoxelVis](int key ,int scancode, int action, int mods) { + if (key == GLFW_KEY_V && action == GLFW_PRESS) { + renderVoxelVis = !renderVoxelVis; + } + }); + + // tonemapping compute shader + vkcv::ShaderProgram tonemappingProgram; + compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/tonemapping.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + tonemappingProgram.addShader(shaderStage, path); + }); + vkcv::DescriptorSetHandle tonemappingDescriptorSet = core.createDescriptorSet(tonemappingProgram.getReflectedDescriptors()[0]); + vkcv::PipelineHandle tonemappingPipeline = core.createComputePipeline(tonemappingProgram, + { core.getDescriptorSet(tonemappingDescriptorSet).layout }); + + // model matrices per mesh + std::vector<glm::mat4> modelMatrices; + modelMatrices.resize(scene.vertexGroups.size(), glm::mat4(1.f)); + for (const auto& mesh : scene.meshes) { + const glm::mat4 m = *reinterpret_cast<const glm::mat4*>(&mesh.modelMatrix[0]); + for (const auto& vertexGroupIndex : mesh.vertexGroups) { + modelMatrices[vertexGroupIndex] = m; + } + } + + // prepare drawcalls + std::vector<vkcv::Mesh> meshes; + for (int i = 0; i < scene.vertexGroups.size(); i++) { + vkcv::Mesh mesh( + vertexBufferBindings[i], + indexBuffers[i].getVulkanHandle(), + scene.vertexGroups[i].numIndices); + meshes.push_back(mesh); + } + + std::vector<vkcv::DrawcallInfo> drawcalls; + std::vector<vkcv::DrawcallInfo> shadowDrawcalls; + for (int i = 0; i < meshes.size(); i++) { + + drawcalls.push_back(vkcv::DrawcallInfo(meshes[i], { + vkcv::DescriptorSetUsage(0, core.getDescriptorSet(forwardShadingDescriptorSet).vulkanHandle), + vkcv::DescriptorSetUsage(1, core.getDescriptorSet(perMeshDescriptorSets[i]).vulkanHandle) })); + shadowDrawcalls.push_back(vkcv::DrawcallInfo(meshes[i], {})); + } + + Voxelization::Dependencies voxelDependencies; + voxelDependencies.colorBufferFormat = colorBufferFormat; + voxelDependencies.depthBufferFormat = depthBufferFormat; + voxelDependencies.vertexLayout = vertexLayout; + Voxelization voxelization( + &core, + voxelDependencies, + lightBuffer.getHandle(), + shadowMap.getHandle(), + shadowSampler); + + vkcv::gui::GUI gui(core, window); + + glm::vec2 lightAngles(90.f, 0.f); + int voxelVisualisationMip = 0; + float voxelizationExtent = 20.f; + + 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(depthBufferFormat, swapchainWidth, swapchainHeight).getHandle(); + colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, true, true).getHandle(); + + windowWidth = swapchainWidth; + windowHeight = swapchainHeight; + } + + auto end = std::chrono::system_clock::now(); + auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); + + // update descriptor sets which use swapchain image + vkcv::DescriptorWrites tonemappingDescriptorWrites; + tonemappingDescriptorWrites.storageImageWrites = { + vkcv::StorageImageDescriptorWrite(0, colorBuffer), + vkcv::StorageImageDescriptorWrite(1, swapchainInput) }; + core.writeDescriptorSet(tonemappingDescriptorSet, tonemappingDescriptorWrites); + + start = end; + cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); + + glm::vec2 lightAngleRadian = glm::radians(lightAngles); + lightInfo.direction = glm::normalize(glm::vec3( + std::cos(lightAngleRadian.x) * std::cos(lightAngleRadian.y), + std::sin(lightAngleRadian.x), + std::cos(lightAngleRadian.x) * std::sin(lightAngleRadian.y))); + + const float shadowProjectionSize = 20.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 = { colorBuffer, depthBuffer }; + + const vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4)); + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + // shadow map + core.recordDrawcallsToCmdStream( + cmdStream, + shadowPass, + shadowPipe, + shadowPushConstantData, + shadowDrawcalls, + { shadowMap.getHandle() }); + core.prepareImageForSampling(cmdStream, shadowMap.getHandle()); + + voxelization.setVoxelExtent(voxelizationExtent); + voxelization.voxelizeMeshes( + cmdStream, + cameraManager.getActiveCamera().getPosition(), + meshes, + modelMatrices, + perMeshDescriptorSets); + + // main pass + core.recordDrawcallsToCmdStream( + cmdStream, + forwardPass, + forwardPipeline, + pushConstantData, + drawcalls, + renderTargets); + + if (renderVoxelVis) { + voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets, voxelVisualisationMip); + } + + const uint32_t tonemappingLocalGroupSize = 8; + const uint32_t tonemappingDispatchCount[3] = { + static_cast<uint32_t>(glm::ceil(windowWidth / static_cast<float>(tonemappingLocalGroupSize))), + static_cast<uint32_t>(glm::ceil(windowHeight / static_cast<float>(tonemappingLocalGroupSize))), + 1 + }; + + core.prepareImageForStorage(cmdStream, swapchainInput); + core.prepareImageForStorage(cmdStream, colorBuffer); + + core.recordComputeDispatchToCmdStream( + cmdStream, + tonemappingPipeline, + tonemappingDispatchCount, + { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(tonemappingDescriptorSet).vulkanHandle) }, + vkcv::PushConstantData(nullptr, 0)); + + // present and end + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + gui.beginGUI(); + + ImGui::Begin("Settings"); + ImGui::DragFloat2("Light angles", &lightAngles.x); + ImGui::ColorEdit3("Sun color", &lightInfo.sunColor.x); + ImGui::DragFloat("Sun strength", &lightInfo.sunStrength); + ImGui::Checkbox("Draw voxel visualisation", &renderVoxelVis); + ImGui::SliderInt("Visualisation mip", &voxelVisualisationMip, 0, 7); + ImGui::DragFloat("Voxelization extent", &voxelizationExtent, 1.f, 0.f); + voxelVisualisationMip = std::max(voxelVisualisationMip, 0); + ImGui::End(); + + gui.endGUI(); + + core.endFrame(); + } + + return 0; +} diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp index ab34a3df149c96d30c9ee4e0665ba47765d19751..aec96411c5d9e07f200b24fbdcf9fa69e2af53d5 100644 --- a/src/vkcv/BufferManager.cpp +++ b/src/vkcv/BufferManager.cpp @@ -5,6 +5,7 @@ #include "vkcv/BufferManager.hpp" #include "vkcv/Core.hpp" +#include <vkcv/Logger.hpp> namespace vkcv { @@ -23,7 +24,7 @@ namespace vkcv { BufferManager::~BufferManager() noexcept { for (uint64_t id = 0; id < m_buffers.size(); id++) { - destroyBuffer(BufferHandle(id)); + destroyBufferById(id); } } @@ -119,7 +120,7 @@ namespace vkcv { const uint64_t id = m_buffers.size(); m_buffers.push_back({ buffer, memory, size, nullptr, mappable }); - return BufferHandle{ id }; + return BufferHandle(id, [&](uint64_t id) { destroyBufferById(id); }); } struct StagingStepInfo { @@ -158,7 +159,7 @@ namespace vkcv { SubmitInfo submitInfo; submitInfo.queueType = QueueType::Transfer; - core->submitCommands( + core->recordAndSubmitCommandsImmediate( submitInfo, [&info, &mapped_size](const vk::CommandBuffer& commandBuffer) { const vk::BufferCopy region ( @@ -315,9 +316,7 @@ namespace vkcv { buffer.m_mapped = nullptr; } - void BufferManager::destroyBuffer(const BufferHandle& handle) { - const uint64_t id = handle.getId(); - + void BufferManager::destroyBufferById(uint64_t id) { if (id >= m_buffers.size()) { return; } @@ -337,4 +336,33 @@ namespace vkcv { } } + void BufferManager ::recordBufferMemoryBarrier(const BufferHandle& handle, vk::CommandBuffer cmdBuffer) { + + const uint64_t id = handle.getId(); + + if (id >= m_buffers.size()) { + vkcv_log(vkcv::LogLevel::ERROR, "Invalid buffer handle"); + return; + } + + auto& buffer = m_buffers[id]; + + vk::BufferMemoryBarrier memoryBarrier( + vk::AccessFlagBits::eMemoryWrite, + vk::AccessFlagBits::eMemoryRead, + 0, + 0, + buffer.m_handle, + 0, + buffer.m_size); + + cmdBuffer.pipelineBarrier( + vk::PipelineStageFlagBits::eTopOfPipe, + vk::PipelineStageFlagBits::eBottomOfPipe, + {}, + nullptr, + memoryBarrier, + nullptr); + } + } 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/Context.cpp b/src/vkcv/Context.cpp index b53a1a2c2db1008e7c69c880ef1c5a608d879021..ac133d1affc81702ee1a19b3f66810e606bec58d 100644 --- a/src/vkcv/Context.cpp +++ b/src/vkcv/Context.cpp @@ -275,7 +275,13 @@ namespace vkcv deviceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); deviceCreateInfo.ppEnabledLayerNames = validationLayers.data(); #endif - + + // FIXME: check if device feature is supported + vk::PhysicalDeviceFeatures deviceFeatures; + deviceFeatures.fragmentStoresAndAtomics = true; + deviceFeatures.geometryShader = true; + deviceCreateInfo.pEnabledFeatures = &deviceFeatures; + // Ablauf // qCreateInfos erstellen --> braucht das Device // device erstellen diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 0b0d3a24f0398a5efa17a4c33110adabbb97c15e..59f2cf3b652a88854b9ea4a4077f18749b9e6d51 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -13,8 +13,10 @@ #include "SamplerManager.hpp" #include "ImageManager.hpp" #include "DescriptorManager.hpp" -#include "Surface.hpp" #include "ImageLayoutTransitions.hpp" +#include "vkcv/CommandStreamManager.hpp" +#include <cmath> +#include "vkcv/Logger.hpp" namespace vkcv { @@ -32,40 +34,10 @@ namespace vkcv instanceExtensions, deviceExtensions ); - - const vk::SurfaceKHR surface = createSurface( - window.getWindow(), - context.getInstance(), - context.getPhysicalDevice() - ); - - SwapChain swapChain = SwapChain::create(window, context, surface); - - std::vector<vk::Image> swapChainImages = context.getDevice().getSwapchainImagesKHR(swapChain.getSwapchain()); - std::vector<vk::ImageView> imageViews; - imageViews.reserve( swapChainImages.size() ); - //here can be swizzled with vk::ComponentSwizzle if needed - vk::ComponentMapping componentMapping( - vk::ComponentSwizzle::eR, - vk::ComponentSwizzle::eG, - vk::ComponentSwizzle::eB, - vk::ComponentSwizzle::eA ); - - vk::ImageSubresourceRange subResourceRange( vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 ); - for ( auto image : swapChainImages ) - { - vk::ImageViewCreateInfo imageViewCreateInfo( - vk::ImageViewCreateFlags(), - image, - vk::ImageViewType::e2D, - swapChain.getSurfaceFormat().format, - componentMapping, - subResourceRange - ); + Swapchain swapChain = Swapchain::create(window, context); - imageViews.push_back(context.getDevice().createImageView(imageViewCreateInfo)); - } + std::vector<vk::ImageView> swapchainImageViews = createSwapchainImageViews( context, swapChain); const auto& queueManager = context.getQueueManager(); @@ -74,48 +46,59 @@ namespace vkcv const auto commandResources = createCommandResources(context.getDevice(), queueFamilySet); const auto defaultSyncResources = createSyncResources(context.getDevice()); - window.e_resize.add([&](int width, int height){ - recreateSwapchain(width,height); - }); - - return Core(std::move(context) , window, swapChain, imageViews, commandResources, defaultSyncResources); + return Core(std::move(context) , window, swapChain, swapchainImageViews, commandResources, defaultSyncResources); } const Context &Core::getContext() const { return m_Context; } + + const Swapchain& Core::getSwapchain() const { + return m_swapchain; + } - Core::Core(Context &&context, Window &window , 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> swapchainImageViews, + const CommandResources& commandResources, const SyncResources& syncResources) noexcept : m_Context(std::move(context)), m_window(window), m_swapchain(swapChain), - m_swapchainImageViews(imageViews), m_PassManager{std::make_unique<PassManager>(m_Context.m_Device)}, m_PipelineManager{std::make_unique<PipelineManager>(m_Context.m_Device)}, m_DescriptorManager(std::make_unique<DescriptorManager>(m_Context.m_Device)), - m_BufferManager{std::unique_ptr<BufferManager>(new BufferManager())}, - m_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 = m_window.e_resize.add( [&](int width, int height) { + m_swapchain.signalSwapchainRecreation(); + }); + + const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); + m_ImageManager->setSwapchainImages( + swapchainImages, + swapchainImageViews, + swapChain.getExtent().width, + swapChain.getExtent().height, + swapChain.getFormat()); } Core::~Core() noexcept { + m_window.e_resize.remove(e_resizeHandle); + m_Context.getDevice().waitIdle(); - for (auto image : m_swapchainImageViews) { - m_Context.m_Device.destroyImageView(image); - } destroyCommandResources(m_Context.getDevice(), m_CommandResources); destroySyncResources(m_Context.getDevice(), m_SyncResources); - destroyTemporaryFramebuffers(); m_Context.m_Device.destroySwapchainKHR(m_swapchain.getSwapchain()); m_Context.m_Instance.destroySurfaceKHR(m_swapchain.getSurface()); @@ -126,6 +109,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) { @@ -134,16 +124,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) { + if (result != vk::Result::eSuccess) { + vkcv_log(LogLevel::ERROR, "%s", vk::to_string(result).c_str()); return Result::ERROR; } @@ -151,80 +148,111 @@ namespace vkcv return Result::SUCCESS; } - void Core::destroyTemporaryFramebuffers() { - for (const vk::Framebuffer f : m_TemporaryFramebuffers) { - m_Context.getDevice().destroyFramebuffer(f); - } - m_TemporaryFramebuffers.clear(); - } + bool Core::beginFrame(uint32_t& width, uint32_t& height) { + if (m_swapchain.shouldUpdateSwapchain()) { + m_Context.getDevice().waitIdle(); - void Core::beginFrame() { + m_swapchain.updateSwapchain(m_Context, m_window); + const auto swapchainViews = createSwapchainImageViews(m_Context, m_swapchain); + const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); + + m_ImageManager->setSwapchainImages(swapchainImages, swapchainViews, width, height, m_swapchain.getFormat()); + } + if (acquireSwapchainImage() != Result::SUCCESS) { - return; + vkcv_log(LogLevel::ERROR, "Acquire failed"); + + m_currentSwapchainImageIndex = std::numeric_limits<uint32_t>::max(); } - m_Context.getDevice().waitIdle(); // FIMXE: this is a sin against graphics programming, but its getting late - Alex - destroyTemporaryFramebuffers(); - } - - vk::Framebuffer createFramebuffer(const vk::Device device, const vk::RenderPass& renderpass, - const int width, const int height, const std::vector<vk::ImageView>& attachments) { - const vk::FramebufferCreateFlags flags = {}; - const vk::FramebufferCreateInfo createInfo(flags, renderpass, attachments.size(), attachments.data(), width, height, 1); - return device.createFramebuffer(createInfo); + + 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; + + m_ImageManager->setCurrentSwapchainImageIndex(m_currentSwapchainImageIndex); + + return (m_currentSwapchainImageIndex != std::numeric_limits<uint32_t>::max()); } - void Core::renderMesh( - const PassHandle renderpassHandle, - const PipelineHandle pipelineHandle, - const int width, - const int height, - const size_t pushConstantSize, - const void *pushConstantData, - const std::vector<VertexBufferBinding>& vertexBufferBindings, - const BufferHandle indexBuffer, - const size_t indexCount, - const vkcv::ResourcesHandle resourceHandle, - const size_t resourceDescriptorSetIndex - ) { + 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::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle); - const PassConfig passConfig = m_PassManager->getPassConfig(renderpassHandle); - - ImageHandle depthImage; - - for (const auto& attachment : passConfig.attachments) { - if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { - depthImage = m_ImageManager->createImage(width, height, 1, attachment.format); - break; + 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 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 (depthImage) { - attachments.push_back(m_ImageManager->getVulkanImageView(depthImage)); + std::vector<vk::ImageView> attachmentsViews; + for (const ImageHandle handle : renderTargets) { + vk::ImageView targetHandle; + const auto cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle); + + targetHandle = m_ImageManager->getVulkanImageView(handle); + const bool isDepthImage = isDepthFormat(m_ImageManager->getImageFormat(handle)); + const vk::ImageLayout targetLayout = + isDepthImage ? vk::ImageLayout::eDepthStencilAttachmentOptimal : vk::ImageLayout::eColorAttachmentOptimal; + m_ImageManager->recordImageLayoutTransition(handle, targetLayout, cmdBuffer); + attachmentsViews.push_back(targetHandle); } - const vk::Framebuffer framebuffer = createFramebuffer( - m_Context.getDevice(), - renderpass, - width, - height, - attachments + const vk::FramebufferCreateInfo createInfo( + {}, + renderpass, + static_cast<uint32_t>(attachmentsViews.size()), + attachmentsViews.data(), + width, + height, + 1 ); - m_TemporaryFramebuffers.push_back(framebuffer); + vk::Framebuffer framebuffer = m_Context.m_Device.createFramebuffer(createInfo); + + if (!framebuffer) { + 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 + ); + + vk::Rect2D dynamicScissor({0, 0}, {width, height}); auto &bufferManager = m_BufferManager; @@ -232,50 +260,86 @@ namespace vkcv submitInfo.queueType = QueueType::Graphics; submitInfo.signalSemaphores = { m_SyncResources.renderFinished }; - submitCommands(submitInfo, [&](const vk::CommandBuffer& cmdBuffer) { - std::vector<vk::ClearValue> clearValues; - - for (const auto& attachment : passConfig.attachments) { - if (attachment.load_operation == AttachmentOperation::CLEAR) { - float clear = 0.0f; - - if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) { - clear = 1.0f; - } - - clearValues.emplace_back(std::array<float, 4>{ - clear, - clear, - clear, - 1.f - }); - } - } + auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) { + std::vector<vk::ClearValue> clearValues; + + for (const auto& attachment : passConfig.attachments) { + if (attachment.load_operation == AttachmentOperation::CLEAR) { + float clear = 0.0f; + + if (isDepthFormat(attachment.format)) { + clear = 1.0f; + } + + clearValues.emplace_back(std::array<float, 4>{ + clear, + clear, + clear, + 1.f + }); + } + } + + const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data()); + const vk::SubpassContents subpassContents = {}; + cmdBuffer.beginRenderPass(beginInfo, subpassContents, {}); - const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data()); - const vk::SubpassContents subpassContents = {}; - cmdBuffer.beginRenderPass(beginInfo, subpassContents, {}); + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); - cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); + 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)); + for (int i = 0; i < drawcalls.size(); i++) { + recordDrawcall(drawcalls[i], cmdBuffer, pipelineLayout, pushConstantData, i); + } + + cmdBuffer.endRenderPass(); + }; + + auto finishFunction = [framebuffer, this]() + { + m_Context.m_Device.destroy(framebuffer); + }; + + 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 (resourceHandle) { - const vk::DescriptorSet descriptorSet = m_DescriptorManager->getDescriptorSet(resourceHandle, resourceDescriptorSetIndex); - cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 0, descriptorSet, nullptr); + if (pushConstantData.sizePerDrawcall > 0) { + cmdBuffer.pushConstants( + pipelineLayout, + vk::ShaderStageFlagBits::eCompute, + 0, + pushConstantData.sizePerDrawcall, + pushConstantData.data); } - - 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(); - }, [&]() { - m_ImageManager->destroyImage(depthImage); - }); + cmdBuffer.dispatch(dispatchCount[0], dispatchCount[1], dispatchCount[2]); + }; + + recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr); } void Core::endFrame() { @@ -284,36 +348,37 @@ namespace vkcv } const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); - const vk::Image presentImage = swapchainImages[m_currentSwapchainImageIndex]; - + const auto& queueManager = m_Context.getQueueManager(); - std::array<vk::Semaphore, 2> waitSemaphores{ - m_SyncResources.renderFinished, - m_SyncResources.swapchainImageAcquired }; + std::array<vk::Semaphore, 2> waitSemaphores{ + m_SyncResources.renderFinished, + m_SyncResources.swapchainImageAcquired + }; - vk::Result presentResult; const vk::SwapchainKHR& swapchain = m_swapchain.getSwapchain(); const vk::PresentInfoKHR presentInfo( 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()); } } - - vk::Format Core::getSwapchainImageFormat() { - return m_swapchain.getSurfaceFormat().format; - } - - void Core::recreateSwapchain(int width, int height) { - /* boilerplate for #34 */ - std::cout << "Resized to : " << width << " , " << height << std::endl; - } - void Core::submitCommands(const SubmitInfo &submitInfo, const RecordCommandFunction& record, const FinishCommandFunction& finish) + void Core::recordAndSubmitCommandsImmediate( + const SubmitInfo &submitInfo, + const RecordCommandFunction &record, + const FinishCommandFunction &finish) { const vk::Device& device = m_Context.getDevice(); @@ -324,10 +389,12 @@ namespace vkcv beginCommandBuffer(cmdBuffer, vk::CommandBufferUsageFlagBits::eOneTimeSubmit); record(cmdBuffer); cmdBuffer.end(); + + vk::Fence waitFence = createFence(device); - const vk::Fence waitFence = createFence(device); submitCommandBufferToQueue(queue.handle, cmdBuffer, waitFence, submitInfo.waitSemaphores, submitInfo.signalSemaphores); waitForFence(device, waitFence); + device.destroyFence(waitFence); device.freeCommandBuffers(cmdPool, cmdBuffer); @@ -336,33 +403,154 @@ 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); - } - - Image Core::createImage(vk::Format format, uint32_t width, uint32_t height, uint32_t depth) + return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, addressMode); + } + + Image Core::createImage( + vk::Format format, + uint32_t width, + uint32_t height, + uint32_t depth, + bool createMipChain, + bool supportStorage, + bool supportColorAttachment) + { + + uint32_t mipCount = 1; + if (createMipChain) { + mipCount = 1 + (uint32_t)std::floor(std::log2(std::max(width, std::max(height, depth)))); + } + + return Image::create( + m_ImageManager.get(), + format, + width, + height, + depth, + mipCount, + supportStorage, + supportColorAttachment); + } + + const uint32_t Core::getImageWidth(ImageHandle imageHandle) { - return Image::create(m_ImageManager.get(), format, width, height, depth); + return m_ImageManager->getImageWidth(imageHandle); } - ResourcesHandle Core::createResourceDescription(const std::vector<DescriptorSetConfig> &descriptorSets) + const uint32_t Core::getImageHeight(ImageHandle imageHandle) + { + return m_ImageManager->getImageHeight(imageHandle); + } + + 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::getDescritorSetLayout(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::createSwapchainImageViews( Context &context, Swapchain& swapChain){ + std::vector<vk::ImageView> imageViews; + std::vector<vk::Image> swapChainImages = context.getDevice().getSwapchainImagesKHR(swapChain.getSwapchain()); + imageViews.reserve( swapChainImages.size() ); + //here can be swizzled with vk::ComponentSwizzle if needed + vk::ComponentMapping componentMapping( + vk::ComponentSwizzle::eR, + vk::ComponentSwizzle::eG, + vk::ComponentSwizzle::eB, + vk::ComponentSwizzle::eA ); + + vk::ImageSubresourceRange subResourceRange( vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 ); + + for ( auto image : swapChainImages ) + { + vk::ImageViewCreateInfo imageViewCreateInfo( + vk::ImageViewCreateFlags(), + image, + vk::ImageViewType::e2D, + swapChain.getFormat(), + componentMapping, + subResourceRange); + + imageViews.push_back(context.getDevice().createImageView(imageViewCreateInfo)); + } + return imageViews; + } + + void Core::prepareSwapchainImageForPresent(const CommandStreamHandle cmdStream) { + auto swapchainHandle = ImageHandle::createSwapchainImageHandle(); + recordCommandsToStream(cmdStream, [swapchainHandle, this](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordImageLayoutTransition(swapchainHandle, vk::ImageLayout::ePresentSrcKHR, cmdBuffer); + }, nullptr); + } + + 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); + } + + void Core::prepareImageForStorage(const CommandStreamHandle cmdStream, const ImageHandle image) { + recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eGeneral, cmdBuffer); + }, nullptr); + } + + void Core::recordImageMemoryBarrier(const CommandStreamHandle cmdStream, const ImageHandle image) { + recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordImageMemoryBarrier(image, cmdBuffer); + }, nullptr); + } + + void Core::recordBufferMemoryBarrier(const CommandStreamHandle cmdStream, const BufferHandle buffer) { + recordCommandsToStream(cmdStream, [buffer, this](const vk::CommandBuffer cmdBuffer) { + m_BufferManager->recordBufferMemoryBarrier(buffer, cmdBuffer); + }, nullptr); + } + + vk::ImageView Core::getSwapchainImageView() const { + return m_ImageManager->getVulkanImageView(vkcv::ImageHandle::createSwapchainImageHandle()); + } + } 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 cf689b44a3865585fcc2d52620badbb4919da644..8e565a766cd407dc33c0291d3d07b01d6d3066e7 100644 --- a/src/vkcv/DescriptorManager.cpp +++ b/src/vkcv/DescriptorManager.cpp @@ -1,89 +1,88 @@ #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 }, m_NextResourceDescriptionID{ 0 } + m_Device{ device } { /** - * Allocate a set size for the initial pool, namely 1000 units of each descriptor type below. + * Allocate the set size for the descriptor pools, namely 1000 units of each descriptor type below. + * Finally, create an initial pool. */ - const std::vector<vk::DescriptorPoolSize> poolSizes = {vk::DescriptorPoolSize(vk::DescriptorType::eSampler, 1000), - vk::DescriptorPoolSize(vk::DescriptorType::eSampledImage, 1000), - vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, 1000), - vk::DescriptorPoolSize(vk::DescriptorType::eStorageBuffer, 1000)}; + m_PoolSizes = { vk::DescriptorPoolSize(vk::DescriptorType::eSampler, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eSampledImage, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageBuffer, 1000) }; - vk::DescriptorPoolCreateInfo poolInfo({}, - 1000, - static_cast<uint32_t>(poolSizes.size()), - poolSizes.data()); + m_PoolInfo = vk::DescriptorPoolCreateInfo({}, + 1000, + static_cast<uint32_t>(m_PoolSizes.size()), + m_PoolSizes.data()); - if(m_Device.createDescriptorPool(&poolInfo, nullptr, &m_Pool) != vk::Result::eSuccess) - { - std::cout << "FAILED TO ALLOCATED DESCRIPTOR POOL." << std::endl; - m_Pool = nullptr; - }; + allocateDescriptorPool(); } DescriptorManager::~DescriptorManager() noexcept { - for(const auto &resource : m_ResourceDescriptions) - { - for(const auto &layout : resource.descriptorSetLayouts) - m_Device.destroyDescriptorSetLayout(layout); + for (uint64_t id = 0; id < m_DescriptorSets.size(); id++) { + destroyDescriptorSetById(id); } - m_Device.destroy(m_Pool); + 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_Pool, 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) { - 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(); + //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, &set.vulkanHandle); + } + if (result != vk::Result::eSuccess) { + vkcv_log(LogLevel::ERROR, "Failed to create descriptor set (%s)", + vk::to_string(result).c_str()); + + m_Device.destroy(set.layout); + return DescriptorSetHandle(); + } }; - m_ResourceDescriptions.emplace_back(vk_sets, vk_setLayouts); - return ResourcesHandle(m_NextResourceDescriptionID++); + const uint64_t id = m_DescriptorSets.size(); + + m_DescriptorSets.push_back(set); + return DescriptorSetHandle(id, [&](uint64_t id) { destroyDescriptorSetById(id); }); } struct WriteDescriptorSetInfo { @@ -93,15 +92,14 @@ namespace vkcv vk::DescriptorType type; }; - void DescriptorManager::writeResourceDescription( - 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; @@ -109,10 +107,11 @@ namespace vkcv std::vector<WriteDescriptorSetInfo> writeInfos; for (const auto& write : writes.sampledImageWrites) { + vk::ImageLayout layout = write.useGeneralLayout ? vk::ImageLayout::eGeneral : vk::ImageLayout::eShaderReadOnlyOptimal; const vk::DescriptorImageInfo imageInfo( nullptr, - imageManager.getVulkanImageView(write.image), - vk::ImageLayout::eShaderReadOnlyOptimal + imageManager.getVulkanImageView(write.image, write.mipLevel), + layout ); imageInfos.push_back(imageInfo); @@ -130,7 +129,7 @@ namespace vkcv for (const auto& write : writes.storageImageWrites) { const vk::DescriptorImageInfo imageInfo( nullptr, - imageManager.getVulkanImageView(write.image), + imageManager.getVulkanImageView(write.image, write.mipLevel), vk::ImageLayout::eGeneral ); @@ -224,12 +223,8 @@ namespace vkcv m_Device.updateDescriptorSets(vulkanWrites, nullptr); } - vk::DescriptorSet DescriptorManager::getDescriptorSet(ResourcesHandle handle, size_t index) { - return m_ResourceDescriptions[handle.getId()].descriptorSets[index]; - } - - vk::DescriptorSetLayout DescriptorManager::getDescriptorSetLayout(ResourcesHandle handle, size_t index) { - return m_ResourceDescriptions[handle.getId()].descriptorSetLayouts[index]; + DescriptorSet DescriptorManager::getDescriptorSet(const DescriptorSetHandle handle) const { + return m_DescriptorSets[handle.getId()]; } vk::DescriptorType DescriptorManager::convertDescriptorTypeFlag(DescriptorType type) { @@ -246,7 +241,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; } } @@ -270,5 +265,30 @@ namespace vkcv return vk::ShaderStageFlagBits::eAll; } } + + void DescriptorManager::destroyDescriptorSetById(uint64_t id) { + if (id >= m_DescriptorSets.size()) { + vkcv_log(LogLevel::ERROR, "Invalid id"); + return; + } + + auto& set = m_DescriptorSets[id]; + if (set.layout) { + m_Device.destroyDescriptorSetLayout(set.layout); + set.layout = nullptr; + } + // FIXME: descriptor set itself not destroyed + } + + vk::DescriptorPool DescriptorManager::allocateDescriptorPool() { + vk::DescriptorPool pool; + if (m_Device.createDescriptorPool(&m_PoolInfo, nullptr, &pool) != vk::Result::eSuccess) + { + vkcv_log(LogLevel::WARNING, "Failed to allocate descriptor pool"); + pool = nullptr; + }; + m_Pools.push_back(pool); + return pool; + } } \ No newline at end of file diff --git a/src/vkcv/DescriptorManager.hpp b/src/vkcv/DescriptorManager.hpp index 937fb278cda241e9153881d5c27662ac929781cd..d18be64f3b069af68cecce68f6fa623c81f8dfa4 100644 --- a/src/vkcv/DescriptorManager.hpp +++ b/src/vkcv/DescriptorManager.hpp @@ -1,3 +1,8 @@ +/** + * @authors Artur Wasmut, Susanne D�tsch, Simeon Hermann + * @file src/vkcv/DescriptorManager.cpp + * @brief Creation and handling of descriptor sets and the respective descriptor pools + */ #include <vulkan/vulkan.hpp> #include "vkcv/Handles.hpp" @@ -16,50 +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( - ResourcesHandle handle, - size_t setIndex, - const DescriptorWrites& writes, - const ImageManager& imageManager, - const BufferManager& bufferManager, - const SamplerManager& samplerManager); + void writeDescriptorSet( + const DescriptorSetHandle &handle, + const DescriptorWrites &writes, + const ImageManager &imageManager, + const BufferManager &bufferManager, + const SamplerManager &samplerManager); - vk::DescriptorSet getDescriptorSet(ResourcesHandle handle, size_t index); - vk::DescriptorSetLayout getDescriptorSetLayout(ResourcesHandle handle, size_t index); + [[nodiscard]] + DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const; private: - vk::Device m_Device; - vk::DescriptorPool m_Pool; + 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; - // Counter for the vector above - uint64_t m_NextResourceDescriptionID; + std::vector<DescriptorSet> m_DescriptorSets; /** * Converts the flags of the descriptor types from VulkanCV (vkcv) to Vulkan (vk). @@ -73,5 +57,19 @@ namespace vkcv * @return vk flag of the ShaderStage */ static vk::ShaderStageFlagBits convertShaderStageFlag(ShaderStage stage); + + /** + * Destroys a specific resource description + * @param[in] the handle id of the respective resource description + */ + void destroyDescriptorSetById(uint64_t id); + + /** + * creates a descriptor pool based on the poolSizes and poolInfo defined in the constructor + * is called initially in the constructor and then every time the pool runs out memory + * @return a DescriptorPool object + */ + vk::DescriptorPool allocateDescriptorPool(); + }; } \ No newline at end of file diff --git a/src/vkcv/DrawcallRecording.cpp b/src/vkcv/DrawcallRecording.cpp new file mode 100644 index 0000000000000000000000000000000000000000..df7b7bbcb3fe278622cd160593eb750db00ec7b1 --- /dev/null +++ b/src/vkcv/DrawcallRecording.cpp @@ -0,0 +1,45 @@ +#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); + } + + 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); + + if (drawcall.mesh.indexBuffer) { + cmdBuffer.bindIndexBuffer(drawcall.mesh.indexBuffer, 0, vk::IndexType::eUint16); //FIXME: choose proper size + cmdBuffer.drawIndexed(drawcall.mesh.indexCount, 1, 0, 0, {}); + } + else { + cmdBuffer.draw(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 bd465d4e71da31c554d82ceb4b8bce7b0ee04129..020489418c8e2db6ce2062d6fd20f06f90a05c37 100644 --- a/src/vkcv/Handles.cpp +++ b/src/vkcv/Handles.cpp @@ -3,17 +3,75 @@ namespace vkcv { Handle::Handle() : - m_id(UINT64_MAX) + m_id(UINT64_MAX), m_rc(nullptr), m_destroy(nullptr) {} - Handle::Handle(uint64_t id) : - m_id(id) + Handle::Handle(uint64_t id, const HandleDestroyFunction& destroy) : + m_id(id), m_rc(new uint64_t(1)), m_destroy(destroy) {} + Handle::~Handle() { + if ((m_rc) && (--(*m_rc) == 0)) { + if (m_destroy) { + m_destroy(m_id); + } + + delete m_rc; + } + } + + Handle::Handle(const Handle &other) : + m_id(other.m_id), + m_rc(other.m_rc), + m_destroy(other.m_destroy) + { + if (m_rc) { + ++(*m_rc); + } + } + + Handle::Handle(Handle &&other) noexcept : + m_id(other.m_id), + m_rc(other.m_rc), + m_destroy(other.m_destroy) + { + other.m_rc = nullptr; + } + + Handle &Handle::operator=(const Handle &other) { + if (&other == this) { + return *this; + } + + m_id = other.m_id; + m_rc = other.m_rc; + m_destroy = other.m_destroy; + + if (m_rc) { + ++(*m_rc); + } + + return *this; + } + + Handle &Handle::operator=(Handle &&other) noexcept { + m_id = other.m_id; + m_rc = other.m_rc; + m_destroy = other.m_destroy; + + other.m_rc = nullptr; + + return *this; + } + uint64_t Handle::getId() const { return m_id; } + uint64_t Handle::getRC() const { + return m_rc? *m_rc : 0; + } + Handle::operator bool() const { return (m_id < UINT64_MAX); } @@ -24,10 +82,18 @@ namespace vkcv { std::ostream& operator << (std::ostream& out, const Handle& handle) { if (handle) { - return out << "[Handle: " << handle.getId() << "]"; + return out << "[Handle: " << handle.getId() << ":" << handle.getRC() << "]"; } else { return out << "[Handle: none]"; } } + bool ImageHandle::isSwapchainImage() const { + return (getId() == UINT64_MAX - 1); + } + + ImageHandle ImageHandle::createSwapchainImageHandle(const HandleDestroyFunction &destroy) { + return ImageHandle(uint64_t(UINT64_MAX - 1), destroy); + } + } diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp index 7104f784231418da4f1c26364459dbf735fdde97..c48b015335e00f23a892bb96d3e89a2c0877ae61 100644 --- a/src/vkcv/Image.cpp +++ b/src/vkcv/Image.cpp @@ -8,55 +8,74 @@ namespace vkcv{ - Image Image::create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth) + 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, + uint32_t mipCount, + bool supportStorage, + bool supportColorAttachment) { - return Image(manager, manager->createImage(width, height, depth, format), format, width, height, depth); + return Image(manager, manager->createImage(width, height, depth, format, mipCount, supportStorage, supportColorAttachment)); } vk::Format Image::getFormat() const { - return m_format; + return m_manager->getImageFormat(m_handle); } uint32_t Image::getWidth() const { - return m_width; + return m_manager->getImageWidth(m_handle); } uint32_t Image::getHeight() const { - return m_height; + return m_manager->getImageHeight(m_handle); } uint32_t Image::getDepth() const { - return m_depth; - } - - vk::ImageLayout Image::getLayout() const { - return m_layout; + return m_manager->getImageDepth(m_handle); } 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 { return m_handle; } - + + uint32_t Image::getMipCount() const { + return m_manager->getImageMipCount(m_handle); + } + void Image::fill(void *data, size_t size) { m_manager->fillImage(m_handle, data, size); } + + void Image::generateMipChainImmediate() { + m_manager->generateImageMipChainImmediate(m_handle); + } + + void Image::recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream) { + m_manager->recordImageMipChainGenerationToCmdStream(cmdStream, m_handle); + } - Image::Image(ImageManager* manager, const ImageHandle& handle, - vk::Format format, uint32_t width, uint32_t height, uint32_t depth) : + Image::Image(ImageManager* manager, const ImageHandle& handle) : m_manager(manager), - m_handle(handle), - m_format(format), - m_width(width), - m_height(height), - m_depth(depth), - m_layout(vk::ImageLayout::eUndefined) - { - } + m_handle(handle) + {} } diff --git a/src/vkcv/ImageLayoutTransitions.cpp b/src/vkcv/ImageLayoutTransitions.cpp index 0b08819489c41c5cde3ceddbb0629a5d2ae3cd30..8d31c64ccbcbf33e259714f8c581c920738190b4 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_viewPerMip.size(), + 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 3896d6bc4abdd24264ad5d468b49ebf08bd20be7..a3364ce0dfd6f59dc78c85b43570eea25cfc052d 100644 --- a/src/vkcv/ImageManager.cpp +++ b/src/vkcv/ImageManager.cpp @@ -5,11 +5,33 @@ */ #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, + std::vector<vk::ImageView> views, + uint32_t width, + uint32_t height, + uint32_t depth, + vk::Format format, + uint32_t layers) + : + m_handle(handle), + m_memory(memory), + m_viewPerMip(views), + m_width(width), + m_height(height), + m_depth(depth), + m_format(format), + m_layers(layers) + {} + /** * @brief searches memory type index for image allocation, combines requirements of image and application * @param physicalMemoryProperties Memory Properties of physical device @@ -43,7 +65,12 @@ namespace vkcv { ImageManager::~ImageManager() noexcept { for (uint64_t id = 0; id < m_images.size(); id++) { - destroyImage(ImageHandle(id)); + destroyImageById(id); + } + for (const auto swapchainImage : m_swapchainImages) { + for (const auto view : swapchainImage.m_viewPerMip) { + m_core->getContext().getDevice().destroy(view); + } } } @@ -57,7 +84,14 @@ namespace vkcv { } } - ImageHandle ImageManager::createImage(uint32_t width, uint32_t height, uint32_t depth, vk::Format format) + ImageHandle ImageManager::createImage( + uint32_t width, + uint32_t height, + uint32_t depth, + vk::Format format, + uint32_t mipCount, + bool supportStorage, + bool supportColorAttachment) { const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice(); @@ -65,8 +99,14 @@ namespace vkcv { vk::ImageCreateFlags createFlags; vk::ImageUsageFlags imageUsageFlags = ( - vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst + vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc ); + if (supportStorage) { + imageUsageFlags |= vk::ImageUsageFlagBits::eStorage; + } + if (supportColorAttachment) { + imageUsageFlags |= vk::ImageUsageFlagBits::eColorAttachment; + } const bool isDepthFormat = isDepthImageFormat(format); @@ -89,6 +129,11 @@ namespace vkcv { } } + if (isDepthFormat) { + imageType = vk::ImageType::e2D; + imageViewType = vk::ImageViewType::e2D; + } + vk::ImageTiling imageTiling = vk::ImageTiling::eOptimal; if (!formatProperties.optimalTilingFeatures) { @@ -98,11 +143,9 @@ namespace vkcv { imageTiling = vk::ImageTiling::eLinear; } - const vk::ImageFormatProperties imageFormatProperties = physicalDevice.getImageFormatProperties( - format, imageType, imageTiling, imageUsageFlags - ); + const vk::ImageFormatProperties imageFormatProperties = + physicalDevice.getImageFormatProperties(format, imageType, imageTiling, imageUsageFlags); - const uint32_t mipLevels = std::min<uint32_t>(1, imageFormatProperties.maxMipLevels); const uint32_t arrayLayers = std::min<uint32_t>(1, imageFormatProperties.maxArrayLayers); const vk::ImageCreateInfo imageCreateInfo( @@ -110,7 +153,7 @@ namespace vkcv { imageType, format, vk::Extent3D(width, height, depth), - mipLevels, + mipCount, arrayLayers, vk::SampleCountFlagBits::e1, imageTiling, @@ -143,37 +186,49 @@ namespace vkcv { aspectFlags = vk::ImageAspectFlagBits::eColor; } - const vk::ImageViewCreateInfo imageViewCreateInfo ( + std::vector<vk::ImageView> views; + for (int mip = 0; mip < mipCount; mip++) { + const vk::ImageViewCreateInfo imageViewCreateInfo( {}, image, imageViewType, format, vk::ComponentMapping( - vk::ComponentSwizzle::eIdentity, - vk::ComponentSwizzle::eIdentity, - vk::ComponentSwizzle::eIdentity, - vk::ComponentSwizzle::eIdentity + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity ), vk::ImageSubresourceRange( - aspectFlags, - 0, - mipLevels, - 0, - arrayLayers + aspectFlags, + mip, + mipCount - mip, + 0, + arrayLayers ) - ); - - vk::ImageView view = device.createImageView(imageViewCreateInfo); + ); + + views.push_back(device.createImageView(imageViewCreateInfo)); + } const uint64_t id = m_images.size(); - m_images.push_back({ image, memory, view, width, height, depth, format, arrayLayers, mipLevels }); - return ImageHandle(id); + m_images.push_back(Image(image, memory, views, width, height, depth, format, arrayLayers)); + return ImageHandle(id, [&](uint64_t id) { destroyImageById(id); }); + } + + ImageHandle ImageManager::createSwapchainImage() { + return ImageHandle::createSwapchainImageHandle(); } vk::Image ImageManager::getVulkanImage(const ImageHandle &handle) const { + + if (handle.isSwapchainImage()) { + m_swapchainImages[m_currentSwapchainInputImage].m_handle; + } + const uint64_t id = handle.getId(); - if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); return nullptr; } @@ -183,9 +238,15 @@ namespace vkcv { } vk::DeviceMemory ImageManager::getVulkanDeviceMemory(const ImageHandle &handle) const { + + if (handle.isSwapchainImage()) { + vkcv_log(LogLevel::ERROR, "Swapchain image has no memory"); + return nullptr; + } + const uint64_t id = handle.getId(); - if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); return nullptr; } @@ -194,115 +255,130 @@ namespace vkcv { return image.m_memory; } - vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle) const { - const uint64_t id = handle.getId(); + vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle, const size_t mipLevel) const { + if (handle.isSwapchainImage()) { + return m_swapchainImages[m_currentSwapchainInputImage].m_viewPerMip[0]; + } + + const uint64_t id = handle.getId(); if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); return nullptr; } - auto& image = m_images[id]; - - return image.m_view; + const auto& image = m_images[id]; + + if (mipLevel >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Image does not have requested mipLevel"); + return nullptr; + } + + return image.m_viewPerMip[mipLevel]; } - void ImageManager::switchImageLayout(const ImageHandle& handle, vk::ImageLayout oldLayout, vk::ImageLayout newLayout) { - const uint64_t id = handle.getId(); + void ImageManager::switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout) { + uint64_t id = handle.getId(); - if (id >= m_images.size()) { + const bool isSwapchainImage = handle.isSwapchainImage(); + + if (id >= m_images.size() && !isSwapchainImage) { + 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 - ); + auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; + const auto transitionBarrier = createImageLayoutTransitionBarrier(image, newLayout); SubmitInfo submitInfo; submitInfo.queueType = QueueType::Graphics; - m_core->submitCommands( + m_core->recordAndSubmitCommandsImmediate( 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(); + const bool isSwapchainImage = handle.isSwapchainImage(); + + if (id >= m_images.size() && !isSwapchainImage) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); + return; + } + + auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; + const auto transitionBarrier = createImageLayoutTransitionBarrier(image, newLayout); + recordImageBarrier(cmdBuffer, transitionBarrier); + image.m_layout = newLayout; + } + + void ImageManager::recordImageMemoryBarrier( + const ImageHandle& handle, + vk::CommandBuffer cmdBuffer) { + + const uint64_t id = handle.getId(); + const bool isSwapchainImage = handle.isSwapchainImage(); + + if (id >= m_images.size() && !isSwapchainImage) { + std::cerr << "Error: ImageManager::recordImageMemoryBarrier invalid handle" << std::endl; + return; + } + + auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; + const auto transitionBarrier = createImageLayoutTransitionBarrier(image, image.m_layout); + recordImageBarrier(cmdBuffer, transitionBarrier); + } + + constexpr uint32_t getChannelsByFormat(vk::Format format) { + switch (format) { + case vk::Format::eR8Unorm: + return 1; + case vk::Format::eR8G8B8A8Srgb: + return 4; + default: + std::cerr << "Check format instead of guessing, please!" << std::endl; + return 4; + } } void ImageManager::fillImage(const ImageHandle& handle, void* data, size_t size) { const uint64_t id = handle.getId(); + if (handle.isSwapchainImage()) { + vkcv_log(LogLevel::ERROR, "Swapchain image cannot be filled"); + return; + } + 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 + uint32_t channels = getChannelsByFormat(image.m_format); const size_t image_size = ( image.m_width * image.m_height * image.m_depth * channels ); @@ -320,7 +396,7 @@ namespace vkcv { SubmitInfo submitInfo; submitInfo.queueType = QueueType::Transfer; - m_core->submitCommands( + m_core->recordAndSubmitCommandsImmediate( submitInfo, [&image, &stagingBuffer](const vk::CommandBuffer& commandBuffer) { vk::ImageAspectFlags aspectFlags; @@ -354,22 +430,143 @@ namespace vkcv { ); }, [&]() { - switchImageLayout( + switchImageLayoutImmediate( handle, - vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal ); - - m_bufferManager.destroyBuffer(bufferHandle); } ); } - void ImageManager::destroyImage(const ImageHandle& handle) - { + void ImageManager::recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer, const ImageHandle& handle) { + + const auto id = handle.getId(); + if (id >= m_images.size()) { + vkcv_log(vkcv::LogLevel::ERROR, "Invalid image handle"); + return; + } + + auto& image = m_images[id]; + recordImageLayoutTransition(handle, vk::ImageLayout::eGeneral, cmdBuffer); + + vk::ImageAspectFlags aspectMask = isDepthImageFormat(image.m_format) ? + vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor; + + uint32_t srcWidth = image.m_width; + uint32_t srcHeight = image.m_height; + uint32_t srcDepth = image.m_depth; + + auto half = [](uint32_t in) { + return std::max<uint32_t>(in / 2, 1); + }; + + uint32_t dstWidth = half(srcWidth); + uint32_t dstHeight = half(srcHeight); + uint32_t dstDepth = half(srcDepth); + + for (uint32_t srcMip = 0; srcMip < image.m_viewPerMip.size() - 1; srcMip++) { + uint32_t dstMip = srcMip + 1; + vk::ImageBlit region( + vk::ImageSubresourceLayers(aspectMask, srcMip, 0, 1), + { vk::Offset3D(0, 0, 0), vk::Offset3D(srcWidth, srcHeight, srcDepth) }, + vk::ImageSubresourceLayers(aspectMask, dstMip, 0, 1), + { vk::Offset3D(0, 0, 0), vk::Offset3D(dstWidth, dstHeight, dstDepth) }); + + cmdBuffer.blitImage( + image.m_handle, + vk::ImageLayout::eGeneral, + image.m_handle, + vk::ImageLayout::eGeneral, + region, + vk::Filter::eLinear); + + srcWidth = dstWidth; + srcHeight = dstHeight; + srcDepth = dstDepth; + + dstWidth = half(dstWidth); + dstHeight = half(dstHeight); + dstDepth = half(dstDepth); + + recordImageMemoryBarrier(handle, cmdBuffer); + } + } + + void ImageManager::generateImageMipChainImmediate(const ImageHandle& handle) { + + const auto& device = m_core->getContext().getDevice(); + + SubmitInfo submitInfo; + submitInfo.queueType = QueueType::Graphics; + + if (handle.isSwapchainImage()) { + vkcv_log(vkcv::LogLevel::ERROR, "You cannot generate a mip chain for the swapchain, what are you smoking?"); + return; + } + + const auto record = [this, handle](const vk::CommandBuffer cmdBuffer) { + recordImageMipGenerationToCmdBuffer(cmdBuffer, handle); + }; + + m_core->recordAndSubmitCommandsImmediate(submitInfo, record, nullptr); + } + + void ImageManager::recordImageMipChainGenerationToCmdStream( + const vkcv::CommandStreamHandle& cmdStream, + const ImageHandle& handle) { + + const auto record = [this, handle](const vk::CommandBuffer cmdBuffer) { + recordImageMipGenerationToCmdBuffer(cmdBuffer, handle); + }; + m_core->recordCommandsToStream(cmdStream, record, nullptr); + } + + uint32_t ImageManager::getImageWidth(const ImageHandle &handle) const { + const uint64_t id = handle.getId(); + const bool isSwapchainImage = handle.isSwapchainImage(); + + if (id >= m_images.size() && !isSwapchainImage) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); + return 0; + } + + auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; + + return image.m_width; + } + + uint32_t ImageManager::getImageHeight(const ImageHandle &handle) const { + const uint64_t id = handle.getId(); + const bool isSwapchainImage = handle.isSwapchainImage(); + + if (id >= m_images.size() && !isSwapchainImage) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); + return 0; + } + + auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; + + return image.m_height; + } + + uint32_t ImageManager::getImageDepth(const ImageHandle &handle) const { const uint64_t id = handle.getId(); + const bool isSwapchainImage = handle.isSwapchainImage(); + + if (id >= m_images.size() && !isSwapchainImage) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); + return 0; + } + + auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; + return image.m_depth; + } + + void ImageManager::destroyImageById(uint64_t id) + { if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); return; } @@ -377,9 +574,11 @@ namespace vkcv { const vk::Device& device = m_core->getContext().getDevice(); - if (image.m_view) { - device.destroyImageView(image.m_view); - image.m_view = nullptr; + for (auto& view : image.m_viewPerMip) { + if (view) { + device.destroyImageView(view); + view = nullptr; + } } if (image.m_memory) { @@ -393,5 +592,54 @@ namespace vkcv { } } + vk::Format ImageManager::getImageFormat(const ImageHandle& handle) const { + + const uint64_t id = handle.getId(); + const bool isSwapchainFormat = handle.isSwapchainImage(); + + if (id >= m_images.size() && !isSwapchainFormat) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); + return vk::Format::eUndefined; + } + + return isSwapchainFormat ? m_swapchainImages[m_currentSwapchainInputImage].m_format : m_images[id].m_format; + } + + uint32_t ImageManager::getImageMipCount(const ImageHandle& handle) const { + const uint64_t id = handle.getId(); + const bool isSwapchainFormat = handle.isSwapchainImage(); + + if (handle.isSwapchainImage()) { + return 1; + } + + if (id >= m_images.size()) { + vkcv_log(LogLevel::ERROR, "Invalid handle"); + return 0; + } + + return m_images[id].m_viewPerMip.size(); + } + + void ImageManager::setCurrentSwapchainImageIndex(int index) { + m_currentSwapchainInputImage = index; + } + + void ImageManager::setSwapchainImages(const std::vector<vk::Image>& images, std::vector<vk::ImageView> views, + uint32_t width, uint32_t height, vk::Format format) { + + // destroy old views + for (auto image : m_swapchainImages) { + for (const auto& view : image.m_viewPerMip) { + m_core->getContext().getDevice().destroyImageView(view); + } + } + + assert(images.size() == views.size()); + m_swapchainImages.clear(); + for (int i = 0; i < images.size(); i++) { + m_swapchainImages.push_back(Image(images[i], nullptr, { views[i] }, width, height, 1, format, 1)); + } + } } \ No newline at end of file diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp index 7dc6746f37b7d03900302afbd0536b909f9e48fc..ecba7eb5959c1d78a0be41e0b3ac555bffd92d95 100644 --- a/src/vkcv/ImageManager.hpp +++ b/src/vkcv/ImageManager.hpp @@ -11,31 +11,58 @@ #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; + std::vector<vk::ImageView> m_viewPerMip; + uint32_t m_width = 0; + uint32_t m_height = 0; + uint32_t m_depth = 0; + vk::Format m_format; + uint32_t m_layers = 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, + std::vector<vk::ImageView> views, + uint32_t width, + uint32_t height, + uint32_t depth, + vk::Format format, + uint32_t layers); + + Image(); }; + private: Core* m_core; BufferManager& m_bufferManager; std::vector<Image> m_images; + std::vector<Image> m_swapchainImages; + int m_currentSwapchainInputImage; ImageManager(BufferManager& bufferManager) noexcept; + /** + * Destroys and deallocates image represented by a given + * image handle id. + * + * @param id Image handle id + */ + void destroyImageById(uint64_t id); + + void recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer, const ImageHandle& handle); + public: ~ImageManager() noexcept; ImageManager(ImageManager&& other) = delete; @@ -44,7 +71,16 @@ namespace vkcv { ImageManager& operator=(ImageManager&& other) = delete; ImageManager& operator=(const ImageManager& other) = delete; - ImageHandle createImage(uint32_t width, uint32_t height, uint32_t depth, vk::Format format); + ImageHandle createImage( + uint32_t width, + uint32_t height, + uint32_t depth, + vk::Format format, + uint32_t mipCount, + bool supportStorage, + bool supportColorAttachment); + + ImageHandle createSwapchainImage(); [[nodiscard]] vk::Image getVulkanImage(const ImageHandle& handle) const; @@ -53,18 +89,40 @@ namespace vkcv { vk::DeviceMemory getVulkanDeviceMemory(const ImageHandle& handle) const; [[nodiscard]] - vk::ImageView getVulkanImageView(const ImageHandle& handle) const; - - void switchImageLayout(const ImageHandle& handle, vk::ImageLayout oldLayout, vk::ImageLayout newLayout); - void fillImage(const ImageHandle& handle, void* data, size_t size); + vk::ImageView getVulkanImageView(const ImageHandle& handle, const size_t mipLevel = 0) const; - /** - * Destroys and deallocates image represented by a given - * buffer handle. - * - * @param handle Image handle - */ - void destroyImage(const ImageHandle& handle); + void switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout); + void recordImageLayoutTransition( + const ImageHandle& handle, + vk::ImageLayout newLayout, + vk::CommandBuffer cmdBuffer); + + void recordImageMemoryBarrier( + const ImageHandle& handle, + vk::CommandBuffer cmdBuffer); + + void fillImage(const ImageHandle& handle, void* data, size_t size); + void generateImageMipChainImmediate(const ImageHandle& handle); + void recordImageMipChainGenerationToCmdStream(const vkcv::CommandStreamHandle& cmdStream, const ImageHandle& handle); + + [[nodiscard]] + uint32_t getImageWidth(const ImageHandle& handle) const; + + [[nodiscard]] + uint32_t getImageHeight(const ImageHandle& handle) const; + + [[nodiscard]] + uint32_t getImageDepth(const ImageHandle& handle) const; + [[nodiscard]] + vk::Format getImageFormat(const ImageHandle& handle) const; + + [[nodiscard]] + uint32_t getImageMipCount(const ImageHandle& handle) const; + + void setCurrentSwapchainImageIndex(int index); + void setSwapchainImages(const std::vector<vk::Image>& images, std::vector<vk::ImageView> views, + uint32_t width, uint32_t height, vk::Format format); + }; } \ 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 26e5f290d04ebaf16940cd99386253b5ab3622cc..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 { @@ -49,17 +50,14 @@ namespace vkcv PassManager::PassManager(vk::Device device) noexcept : m_Device{device}, - m_Passes{}, - m_NextPassId(0) + m_Passes{} {} PassManager::~PassManager() noexcept { - for(const auto &pass : m_Passes) - m_Device.destroy(pass.m_Handle); - - m_Passes.clear(); - m_NextPassId = 0; + for (uint64_t id = 0; id < m_Passes.size(); id++) { + destroyPassById(id); + } } PassHandle PassManager::createPass(const PassConfig &config) @@ -76,62 +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); - - m_Passes.push_back({ renderPass, config }); - return PassHandle(m_NextPassId++); + + const uint64_t id = m_Passes.size(); + m_Passes.push_back({ renderPass, config }); + return PassHandle(id, [&](uint64_t id) { destroyPassById(id); }); } vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const @@ -160,4 +159,17 @@ namespace vkcv return pass.m_Config; } + void PassManager::destroyPassById(uint64_t id) { + if (id >= m_Passes.size()) { + return; + } + + auto& pass = m_Passes[id]; + + if (pass.m_Handle) { + m_Device.destroy(pass.m_Handle); + pass.m_Handle = nullptr; + } + } + } diff --git a/src/vkcv/PassManager.hpp b/src/vkcv/PassManager.hpp index bfc20fe25ace95bd8d94832b953b6b14ab9cadee..661a8b277ecb446c4bbaeeb63560ffde28c31d99 100644 --- a/src/vkcv/PassManager.hpp +++ b/src/vkcv/PassManager.hpp @@ -17,7 +17,9 @@ namespace vkcv vk::Device m_Device; std::vector<Pass> m_Passes; - uint64_t m_NextPassId; + + void destroyPassById(uint64_t id); + public: PassManager() = delete; // no default ctor explicit PassManager(vk::Device device) noexcept; // ctor diff --git a/src/vkcv/PipelineConfig.cpp b/src/vkcv/PipelineConfig.cpp deleted file mode 100644 index d317258470bde76e8b8ba8e1f9bc684ea469b6c0..0000000000000000000000000000000000000000 --- a/src/vkcv/PipelineConfig.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @authors Mara Vogt, Mark Mints - * @file src/vkcv/Pipeline.cpp - * @brief Pipeline class to handle shader stages - */ - -#include "vkcv/PipelineConfig.hpp" - -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) - : - m_ShaderProgram(shaderProgram), - m_Height(height), - m_Width(width), - m_PassHandle(passHandle), - m_vertexAttributes(vertexAttributes), - m_descriptorLayouts(descriptorLayouts) - {} -} diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp index 1b0d665e9011b9c652a69e35c295a701a6029dd2..df36442efc2992bf16b6e82245ef9753dad95e5d 100644 --- a/src/vkcv/PipelineManager.cpp +++ b/src/vkcv/PipelineManager.cpp @@ -1,43 +1,56 @@ #include "PipelineManager.hpp" +#include "vkcv/Image.hpp" +#include "vkcv/Logger.hpp" namespace vkcv { PipelineManager::PipelineManager(vk::Device device) noexcept : m_Device{device}, - m_Pipelines{}, - m_PipelineLayouts{}, - m_NextPipelineId{0} + m_Pipelines{} {} PipelineManager::~PipelineManager() noexcept { - for(const auto &pipeline: m_Pipelines) - m_Device.destroy(pipeline); - - for(const auto &layout : m_PipelineLayouts) - m_Device.destroy(layout); - - m_Pipelines.clear(); - m_PipelineLayouts.clear(); - m_NextPipelineId = 0; + for (uint64_t id = 0; id < m_Pipelines.size(); id++) { + destroyPipelineById(id); + } } // 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; } } + vk::PrimitiveTopology primitiveTopologyToVulkanPrimitiveTopology(const PrimitiveTopology topology) { + switch (topology) { + case(PrimitiveTopology::PointList): return vk::PrimitiveTopology::ePointList; + case(PrimitiveTopology::LineList): return vk::PrimitiveTopology::eLineList; + case(PrimitiveTopology::TriangleList): return vk::PrimitiveTopology::eTriangleList; + default: std::cout << "Error: Unknown primitive topology type" << std::endl; return vk::PrimitiveTopology::eTriangleList; + } + } + PipelineHandle PipelineManager::createPipeline(const PipelineConfig &config, PassManager& passManager) { const vk::RenderPass &pass = passManager.getVkPass(config.m_PassHandle); @@ -46,7 +59,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(); } @@ -89,24 +102,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); + // 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); - //FIXME: hoping that order is the same and compatible: add explicit mapping and validation - const VertexAttribute attribute = config.m_vertexAttributes[i]; + // 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); - vertexAttributeDescriptions.push_back({location, binding, vertexFormatToVulkanFormat(attachment.format), 0}); - vertexBindingDescriptions.push_back(vk::VertexInputBindingDescription( - binding, - attribute.stride + getFormatSize(attachment.format), - vk::VertexInputRate::eVertex)); + } } // Handover Containers to PipelineVertexInputStateCreateIngo Struct @@ -120,9 +133,9 @@ namespace vkcv // input assembly state vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo( - {}, - vk::PrimitiveTopology::eTriangleList, - false + {}, + primitiveTopologyToVulkanPrimitiveTopology(config.m_PrimitiveTopology), + false ); // viewport state @@ -144,6 +157,14 @@ namespace vkcv 0.f, 1.f ); + vk::PipelineRasterizationConservativeStateCreateInfoEXT conservativeRasterization; + if (config.m_UseConservativeRasterization) { + conservativeRasterization = vk::PipelineRasterizationConservativeStateCreateInfoEXT( + {}, + vk::ConservativeRasterizationModeEXT::eOverestimate, + 0.f); + pipelineRasterizationStateCreateInfo.pNext = &conservativeRasterization; + } // multisample state vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo( @@ -180,14 +201,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) { @@ -214,14 +236,39 @@ 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; } } - - // graphics pipeline create + + std::vector<vk::DynamicState> dynamicStates = {}; + if(config.m_UseDynamicViewport) + { + dynamicStates.push_back(vk::DynamicState::eViewport); + dynamicStates.push_back(vk::DynamicState::eScissor); + } + + vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo({}, + static_cast<uint32_t>(dynamicStates.size()), + dynamicStates.data()); + + // graphics pipeline create std::vector<vk::PipelineShaderStageCreateInfo> shaderStages = { pipelineVertexShaderStageInfo, pipelineFragmentShaderStageInfo }; + + const char *geometryShaderName = "main"; // outside of if to make sure it stays in scope + vk::ShaderModule geometryModule; + if (config.m_ShaderProgram.existsShader(ShaderStage::GEOMETRY)) { + const vkcv::Shader geometryShader = config.m_ShaderProgram.getShader(ShaderStage::GEOMETRY); + const auto& geometryCode = geometryShader.shaderCode; + const vk::ShaderModuleCreateInfo geometryModuleInfo({}, geometryCode.size(), reinterpret_cast<const uint32_t*>(geometryCode.data())); + if (m_Device.createShaderModule(&geometryModuleInfo, nullptr, &geometryModule) != vk::Result::eSuccess) { + return PipelineHandle(); + } + vk::PipelineShaderStageCreateInfo geometryStage({}, vk::ShaderStageFlagBits::eGeometry, geometryModule, geometryShaderName); + shaderStages.push_back(geometryStage); + } + const vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo( {}, static_cast<uint32_t>(shaderStages.size()), @@ -234,7 +281,7 @@ namespace vkcv &pipelineMultisampleStateCreateInfo, p_depthStencilCreateInfo, &pipelineColorBlendStateCreateInfo, - nullptr, + &dynamicStateCreateInfo, vkPipelineLayout, pass, 0, @@ -247,24 +294,140 @@ namespace vkcv { m_Device.destroy(vertexModule); m_Device.destroy(fragmentModule); + if (geometryModule) { + m_Device.destroy(geometryModule); + } + m_Device.destroy(); return PipelineHandle(); } m_Device.destroy(vertexModule); m_Device.destroy(fragmentModule); - - m_Pipelines.push_back(vkPipeline); - m_PipelineLayouts.push_back(vkPipelineLayout); - return PipelineHandle(m_NextPipelineId++); + if (geometryModule) { + m_Device.destroy(geometryModule); + } + + const uint64_t id = m_Pipelines.size(); + m_Pipelines.push_back({ vkPipeline, vkPipelineLayout, config }); + return PipelineHandle(id, [&](uint64_t id) { destroyPipelineById(id); }); } vk::Pipeline PipelineManager::getVkPipeline(const PipelineHandle &handle) const { - return m_Pipelines.at(handle.getId()); + const uint64_t id = handle.getId(); + + if (id >= m_Pipelines.size()) { + return nullptr; + } + + auto& pipeline = m_Pipelines[id]; + + return pipeline.m_handle; } vk::PipelineLayout PipelineManager::getVkPipelineLayout(const PipelineHandle &handle) const { - return m_PipelineLayouts.at(handle.getId()); + const uint64_t id = handle.getId(); + + if (id >= m_Pipelines.size()) { + return nullptr; + } + + auto& pipeline = m_Pipelines[id]; + + return pipeline.m_layout; + } + + void PipelineManager::destroyPipelineById(uint64_t id) { + if (id >= m_Pipelines.size()) { + return; + } + + auto& pipeline = m_Pipelines[id]; + + if (pipeline.m_handle) { + m_Device.destroy(pipeline.m_handle); + pipeline.m_handle = nullptr; + } + + if (pipeline.m_layout) { + m_Device.destroy(pipeline.m_layout); + pipeline.m_layout = nullptr; + } + } + + const PipelineConfig& PipelineManager::getPipelineConfig(const PipelineHandle &handle) const + { + const uint64_t id = handle.getId(); + + if (id >= m_Pipelines.size()) { + static PipelineConfig dummyConfig; + vkcv_log(LogLevel::ERROR, "Invalid handle"); + return dummyConfig; + } + + return m_Pipelines[id].m_config; + } + + 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, PipelineConfig() }); + + 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 896d0df1ce10f56d291ef1accf93f9783cdd9db4..b153eb4632b844e84b92953fe8abf6666a13e0c9 100644 --- a/src/vkcv/PipelineManager.hpp +++ b/src/vkcv/PipelineManager.hpp @@ -11,10 +11,19 @@ namespace vkcv class PipelineManager { private: + struct Pipeline { + vk::Pipeline m_handle; + vk::PipelineLayout m_layout; + PipelineConfig m_config; + }; + vk::Device m_Device; - std::vector<vk::Pipeline> m_Pipelines; - std::vector<vk::PipelineLayout> m_PipelineLayouts; - uint64_t m_NextPipelineId; + std::vector<Pipeline> m_Pipelines; + + 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 @@ -28,9 +37,17 @@ 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; + [[nodiscard]] vk::PipelineLayout getVkPipelineLayout(const PipelineHandle &handle) const; + + [[nodiscard]] + const PipelineConfig &getPipelineConfig(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/SamplerManager.cpp b/src/vkcv/SamplerManager.cpp index 7935bbc16a54f170cb38161629c109d56cf56d3c..a6ebb95b5e237dcd06ed8041b3f16489f7339d6a 100644 --- a/src/vkcv/SamplerManager.cpp +++ b/src/vkcv/SamplerManager.cpp @@ -10,7 +10,7 @@ namespace vkcv { SamplerManager::~SamplerManager() { for (uint64_t id = 0; id < m_samplers.size(); id++) { - destroySampler(SamplerHandle(id)); + destroySamplerById(id); } } @@ -87,7 +87,7 @@ namespace vkcv { false, vk::CompareOp::eAlways, 0.0f, - 1.0f, + 16.0f, vk::BorderColor::eIntOpaqueBlack, false ); @@ -96,7 +96,7 @@ namespace vkcv { const uint64_t id = m_samplers.size(); m_samplers.push_back(sampler); - return SamplerHandle(id); + return SamplerHandle(id, [&](uint64_t id) { destroySamplerById(id); }); } vk::Sampler SamplerManager::getVulkanSampler(const SamplerHandle &handle) const { @@ -109,9 +109,7 @@ namespace vkcv { return m_samplers[id]; } - void SamplerManager::destroySampler(const SamplerHandle &handle) { - const uint64_t id = handle.getId(); - + void SamplerManager::destroySamplerById(uint64_t id) { if (id >= m_samplers.size()) { return; } diff --git a/src/vkcv/SamplerManager.hpp b/src/vkcv/SamplerManager.hpp index 41f58b2f33daaf8b08c785c05c7f14184cf47958..511176d4f87633a8691ca730ecc383e2588d8cf0 100644 --- a/src/vkcv/SamplerManager.hpp +++ b/src/vkcv/SamplerManager.hpp @@ -18,6 +18,8 @@ namespace vkcv { explicit SamplerManager(const vk::Device& device) noexcept; + void destroySamplerById(uint64_t id); + public: ~SamplerManager(); @@ -34,8 +36,6 @@ namespace vkcv { [[nodiscard]] vk::Sampler getVulkanSampler(const SamplerHandle& handle) const; - - void destroySampler(const SamplerHandle& handle); }; diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp index 5185b8b402eae5cd514689ba51a06e1a437271bf..971797d9a42d071a1730ebf31a0b554f92fa361f 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 { /** @@ -13,32 +14,36 @@ namespace vkcv { * @param[in] relative path to the shader code * @return vector of chars as a buffer for the code */ - std::vector<char> readShaderCode(const std::filesystem::path &shaderPath) - { - std::ifstream file(shaderPath.string(), std::ios::ate | std::ios::binary); + std::vector<char> readShaderCode(const std::filesystem::path &shaderPath) { + 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(); std::vector<char> buffer(fileSize); + file.seekg(0); file.read(buffer.data(), fileSize); + file.close(); + 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 +51,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 +65,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 +112,113 @@ 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.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); + } - 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.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); + } - 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_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); - m_VertexLayout = VertexLayout(inputVec); + } + + 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); + } + } + + //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/Surface.cpp b/src/vkcv/Surface.cpp deleted file mode 100644 index 29b6c646dc212cba2cc31f32dca5c4fcc023cd03..0000000000000000000000000000000000000000 --- a/src/vkcv/Surface.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "Surface.hpp" - -#define GLFW_INCLUDE_VULKAN -#include <GLFW/glfw3.h> - -namespace vkcv { - /** - * creates surface and checks availability - * @param window current window for the surface - * @param instance Vulkan-Instance - * @param physicalDevice Vulkan-PhysicalDevice - * @return created surface - */ - vk::SurfaceKHR createSurface(GLFWwindow* window, const vk::Instance& instance, const vk::PhysicalDevice& physicalDevice) { - //create surface - VkSurfaceKHR surface; - if (glfwCreateWindowSurface(VkInstance(instance), window, nullptr, &surface) != VK_SUCCESS) { - throw std::runtime_error("failed to create a window surface!"); - } - vk::Bool32 surfaceSupport = false; - if (physicalDevice.getSurfaceSupportKHR(0, vk::SurfaceKHR(surface), &surfaceSupport) != vk::Result::eSuccess && surfaceSupport != true) { - throw std::runtime_error("surface is not supported by the device!"); - } - - return vk::SurfaceKHR(surface); - } -} diff --git a/src/vkcv/Surface.hpp b/src/vkcv/Surface.hpp deleted file mode 100644 index 74aafeba821334767ac5e13cd33e1d9674e12f5b..0000000000000000000000000000000000000000 --- a/src/vkcv/Surface.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include <vulkan/vulkan.hpp> - -struct GLFWwindow; - -namespace vkcv { - vk::SurfaceKHR createSurface(GLFWwindow* window, const vk::Instance& instance, const vk::PhysicalDevice& physicalDevice); -} \ No newline at end of file diff --git a/src/vkcv/SwapChain.cpp b/src/vkcv/Swapchain.cpp similarity index 52% rename from src/vkcv/SwapChain.cpp rename to src/vkcv/Swapchain.cpp index 3483ae37e718453a99d56d31e025433acb7f4422..2c5b3530c396bc3532aa94cb59a120e3555291bf 100644 --- a/src/vkcv/SwapChain.cpp +++ b/src/vkcv/Swapchain.cpp @@ -1,29 +1,69 @@ -#include <vkcv/SwapChain.hpp> +#include <vkcv/Swapchain.hpp> +#include <utility> -namespace vkcv { +#include <GLFW/glfw3.h> - SwapChain::SwapChain(vk::SurfaceKHR surface, vk::SwapchainKHR swapchain, vk::SurfaceFormatKHR format, uint32_t imageCount) - : m_surface(surface), m_swapchain(swapchain), m_format( format), m_ImageCount(imageCount) +namespace vkcv +{ + /** + * creates surface and checks availability + * @param window current window for the surface + * @param instance Vulkan-Instance + * @param physicalDevice Vulkan-PhysicalDevice + * @return created surface + */ + vk::SurfaceKHR createSurface(GLFWwindow* window, const vk::Instance& instance, const vk::PhysicalDevice& physicalDevice) { + //create surface + VkSurfaceKHR surface; + if (glfwCreateWindowSurface(VkInstance(instance), window, nullptr, &surface) != VK_SUCCESS) { + throw std::runtime_error("failed to create a window surface!"); + } + vk::Bool32 surfaceSupport = false; + if (physicalDevice.getSurfaceSupportKHR(0, vk::SurfaceKHR(surface), &surfaceSupport) != vk::Result::eSuccess && surfaceSupport != true) { + throw std::runtime_error("surface is not supported by the device!"); + } + + return vk::SurfaceKHR(surface); + } + + Swapchain::Swapchain(const Surface &surface, + vk::SwapchainKHR swapchain, + vk::Format format, + vk::ColorSpaceKHR colorSpace, + vk::PresentModeKHR presentMode, + uint32_t imageCount, + vk::Extent2D extent) noexcept : + m_Surface(surface), + m_Swapchain(swapchain), + m_Format(format), + m_ColorSpace(colorSpace), + m_PresentMode(presentMode), + m_ImageCount(imageCount), + m_Extent(extent), + m_RecreationRequired(false) {} + + Swapchain::Swapchain(const Swapchain &other) : + m_Surface(other.m_Surface), + m_Swapchain(other.m_Swapchain), + m_Format(other.m_Format), + m_ColorSpace(other.m_ColorSpace), + m_PresentMode(other.m_PresentMode), + m_ImageCount(other.m_ImageCount), + m_Extent(other.m_Extent), + m_RecreationRequired(other.m_RecreationRequired.load()) + {} - const vk::SwapchainKHR& SwapChain::getSwapchain() const { - return m_swapchain; + const vk::SwapchainKHR& Swapchain::getSwapchain() const { + return m_Swapchain; } - /** - * gets surface of the swapchain - * @return current surface - */ - vk::SurfaceKHR SwapChain::getSurface() { - return m_surface; + vk::SurfaceKHR Swapchain::getSurface() const { + return m_Surface.handle; } - /** - * gets the surface of the swapchain - * @return chosen format - */ - vk::SurfaceFormatKHR SwapChain::getSurfaceFormat(){ - return m_format; + vk::Format Swapchain::getFormat() const{ + return m_Format; } /** @@ -33,7 +73,7 @@ namespace vkcv { * @param window of the current application * @return chosen Extent for the surface */ - vk::Extent2D chooseSwapExtent(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface, const Window &window){ + vk::Extent2D chooseExtent(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface, const Window &window){ vk::SurfaceCapabilitiesKHR surfaceCapabilities; if(physicalDevice.getSurfaceCapabilitiesKHR(surface,&surfaceCapabilities) != vk::Result::eSuccess){ throw std::runtime_error("cannot get surface capabilities. There is an issue with the surface."); @@ -43,15 +83,10 @@ namespace vkcv { static_cast<uint32_t>(window.getWidth()), static_cast<uint32_t>(window.getHeight()) }; + extent2D.width = std::max(surfaceCapabilities.minImageExtent.width, std::min(surfaceCapabilities.maxImageExtent.width, extent2D.width)); extent2D.height = std::max(surfaceCapabilities.minImageExtent.height, std::min(surfaceCapabilities.maxImageExtent.height, extent2D.height)); - if (extent2D.width > surfaceCapabilities.maxImageExtent.width || - extent2D.width < surfaceCapabilities.minImageExtent.width || - extent2D.height > surfaceCapabilities.maxImageExtent.height || - extent2D.height < surfaceCapabilities.minImageExtent.height) { - std::printf("Surface size not matching. Resizing to allowed value."); - } return extent2D; } @@ -61,7 +96,7 @@ namespace vkcv { * @param surface of the swapchain * @return available Format */ - vk::SurfaceFormatKHR chooseSwapSurfaceFormat(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) { + vk::SurfaceFormatKHR chooseSurfaceFormat(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) { uint32_t formatCount; physicalDevice.getSurfaceFormatsKHR(surface, &formatCount, nullptr); std::vector<vk::SurfaceFormatKHR> availableFormats(formatCount); @@ -126,46 +161,101 @@ namespace vkcv { * @param context that keeps instance, physicalDevice and a device. * @return swapchain */ - SwapChain SwapChain::create(const Window &window, const Context &context, const vk::SurfaceKHR surface) { + Swapchain Swapchain::create(const Window &window, const Context &context) { const vk::Instance& instance = context.getInstance(); const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice(); const vk::Device& device = context.getDevice(); - vk::Extent2D extent2D = chooseSwapExtent(physicalDevice, surface, window); - vk::SurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(physicalDevice, surface); - vk::PresentModeKHR presentMode = choosePresentMode(physicalDevice, surface); - uint32_t imageCount = chooseImageCount(physicalDevice, surface); + Surface surface; + surface.handle = createSurface(window.getWindow(), instance, physicalDevice); + surface.formats = physicalDevice.getSurfaceFormatsKHR(surface.handle); + surface.capabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface.handle); + surface.presentModes = physicalDevice.getSurfacePresentModesKHR(surface.handle); + + vk::Extent2D chosenExtent = chooseExtent(physicalDevice, surface.handle, window); + vk::SurfaceFormatKHR chosenSurfaceFormat = chooseSurfaceFormat(physicalDevice, surface.handle); + vk::PresentModeKHR chosenPresentMode = choosePresentMode(physicalDevice, surface.handle); + uint32_t chosenImageCount = chooseImageCount(physicalDevice, surface.handle); vk::SwapchainCreateInfoKHR swapchainCreateInfo( vk::SwapchainCreateFlagsKHR(), //flags - surface, // surface - imageCount, // minImageCount TODO: how many do we need for our application?? "must be less than or equal to the value returned in maxImageCount" -> 3 for Triple Buffering, else 2 for Double Buffering (should be the standard) - surfaceFormat.format, // imageFormat - surfaceFormat.colorSpace, // imageColorSpace - extent2D, // imageExtent + surface.handle, // surface + chosenImageCount, // minImageCount TODO: how many do we need for our application?? "must be less than or equal to the value returned in maxImageCount" -> 3 for Triple Buffering, else 2 for Double Buffering (should be the standard) + chosenSurfaceFormat.format, // imageFormat + chosenSurfaceFormat.colorSpace, // imageColorSpace + chosenExtent, // imageExtent 1, // imageArrayLayers TODO: should we only allow non-stereoscopic applications? yes -> 1, no -> ? "must be greater than 0, less or equal to maxImageArrayLayers" - vk::ImageUsageFlagBits::eColorAttachment, // imageUsage TODO: what attachments? only color? depth? + vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eStorage, // imageUsage TODO: what attachments? only color? depth? vk::SharingMode::eExclusive, // imageSharingMode TODO: which sharing mode? "VK_SHARING_MODE_EXCLUSIV access exclusive to a single queue family, better performance", "VK_SHARING_MODE_CONCURRENT access from multiple queues" 0, // queueFamilyIndexCount, the number of queue families having access to the image(s) of the swapchain when imageSharingMode is VK_SHARING_MODE_CONCURRENT nullptr, // pQueueFamilyIndices, the pointer to an array of queue family indices having access to the images(s) of the swapchain when imageSharingMode is VK_SHARING_MODE_CONCURRENT vk::SurfaceTransformFlagBitsKHR::eIdentity, // preTransform, transformations applied onto the image before display vk::CompositeAlphaFlagBitsKHR::eOpaque, // compositeAlpha, TODO: how to handle transparent pixels? do we need transparency? If no -> opaque - presentMode, // presentMode + chosenPresentMode, // presentMode true, // clipped nullptr // oldSwapchain ); vk::SwapchainKHR swapchain = device.createSwapchainKHR(swapchainCreateInfo); - return SwapChain(surface, swapchain, surfaceFormat, imageCount); + return Swapchain(surface, + swapchain, + chosenSurfaceFormat.format, + chosenSurfaceFormat.colorSpace, + chosenPresentMode, + chosenImageCount, + chosenExtent); + } + + bool Swapchain::shouldUpdateSwapchain() const { + return m_RecreationRequired; + } + + void Swapchain::updateSwapchain(const Context &context, const Window &window) { + if (!m_RecreationRequired.exchange(false)) + return; + + vk::SwapchainKHR oldSwapchain = m_Swapchain; + vk::Extent2D extent2D = chooseExtent(context.getPhysicalDevice(), m_Surface.handle, window); + + vk::SwapchainCreateInfoKHR swapchainCreateInfo( + vk::SwapchainCreateFlagsKHR(), + m_Surface.handle, + m_ImageCount, + m_Format, + m_ColorSpace, + extent2D, + 1, + vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eStorage, + vk::SharingMode::eExclusive, + 0, + nullptr, + vk::SurfaceTransformFlagBitsKHR::eIdentity, + vk::CompositeAlphaFlagBitsKHR::eOpaque, + m_PresentMode, + true, + oldSwapchain + ); + + m_Swapchain = context.getDevice().createSwapchainKHR(swapchainCreateInfo); + context.getDevice().destroySwapchainKHR(oldSwapchain); + + m_Extent = extent2D; } + void Swapchain::signalSwapchainRecreation() { + m_RecreationRequired = true; + } + + const vk::Extent2D& Swapchain::getExtent() const { + return m_Extent; + } - SwapChain::~SwapChain() { + Swapchain::~Swapchain() { // needs to be destroyed by creator } - uint32_t SwapChain::getImageCount() { + uint32_t Swapchain::getImageCount() const { return m_ImageCount; } } 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 diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp index c21271b78f7501721d5c0496d0344dd68e2e7e52..03a58a23b994209c7a0ee195732dc98543f0eddc 100644 --- a/src/vkcv/Window.cpp +++ b/src/vkcv/Window.cpp @@ -5,64 +5,103 @@ */ #include <GLFW/glfw3.h> - #include "vkcv/Window.hpp" namespace vkcv { - static uint32_t s_WindowCount = 0; - - Window::Window(GLFWwindow *window) - : m_window(window) { + static std::vector<GLFWwindow*> s_Windows; + + Window::Window(GLFWwindow *window) : + m_window(window), + e_mouseButton(true), + e_mouseMove(true), + e_mouseScroll(true), + e_resize(true), + e_key(true), + e_char(true), + e_gamepad(true) + { + glfwSetWindowUserPointer(m_window, this); + + // combine Callbacks with Events + glfwSetMouseButtonCallback(m_window, Window::onMouseButtonEvent); + glfwSetCursorPosCallback(m_window, Window::onMouseMoveEvent); + glfwSetWindowSizeCallback(m_window, Window::onResize); + glfwSetKeyCallback(m_window, Window::onKeyEvent); + glfwSetScrollCallback(m_window, Window::onMouseScrollEvent); + glfwSetCharCallback(m_window, Window::onCharEvent); } Window::~Window() { + Window::e_mouseButton.unlock(); + Window::e_mouseMove.unlock(); + Window::e_mouseScroll.unlock(); + Window::e_resize.unlock(); + Window::e_key.unlock(); + Window::e_char.unlock(); + Window::e_gamepad.unlock(); + + s_Windows.erase(std::find(s_Windows.begin(), s_Windows.end(), m_window)); glfwDestroyWindow(m_window); - s_WindowCount--; - if(s_WindowCount == 0) { + if(s_Windows.empty()) { glfwTerminate(); } } Window Window::create( const char *windowTitle, int width, int height, bool resizable) { - if(s_WindowCount == 0) { - glfwInit(); - } - s_WindowCount++; - - width = std::max(width, 1); - height = std::max(height, 1); - - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE); - GLFWwindow *window; - window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr); - - return Window(window); + if(s_Windows.empty()) { + glfwInit(); + } + + width = std::max(width, 1); + height = std::max(height, 1); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE); + GLFWwindow *window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr); + + s_Windows.push_back(window); + + return Window(window); } - void Window::initEvents() { - glfwSetWindowUserPointer(m_window, this); - - // combine Callbacks with Events - glfwSetMouseButtonCallback(m_window, Window::onMouseButtonEvent); - - glfwSetCursorPosCallback(m_window, Window::onMouseMoveEvent); - - glfwSetWindowSizeCallback(m_window, Window::onResize); - - glfwSetKeyCallback(m_window, Window::onKeyEvent); + void Window::pollEvents() { - glfwSetScrollCallback(m_window, Window::onMouseScrollEvent); - } + for (auto glfwWindow : s_Windows) { + auto window = static_cast<Window *>(glfwGetWindowUserPointer(glfwWindow)); + + window->e_mouseButton.unlock(); + window->e_mouseMove.unlock(); + window->e_mouseScroll.unlock(); + window->e_resize.unlock(); + window->e_key.unlock(); + window->e_char.unlock(); + window->e_gamepad.unlock(); + } - void Window::pollEvents() { glfwPollEvents(); + + for (int gamepadIndex = GLFW_JOYSTICK_1; gamepadIndex <= GLFW_JOYSTICK_LAST; gamepadIndex++) { + if (glfwJoystickPresent(gamepadIndex)) { + onGamepadEvent(gamepadIndex); + } + } + + for (auto glfwWindow : s_Windows) { + auto window = static_cast<Window *>(glfwGetWindowUserPointer(glfwWindow)); + + window->e_mouseButton.lock(); + window->e_mouseMove.lock(); + window->e_mouseScroll.lock(); + window->e_resize.lock(); + window->e_key.lock(); + window->e_char.lock(); + window->e_gamepad.lock(); + } } void Window::onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); if (window != nullptr) { @@ -71,7 +110,6 @@ namespace vkcv { } void Window::onMouseMoveEvent(GLFWwindow *callbackWindow, double x, double y) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); if (window != nullptr) { @@ -88,7 +126,6 @@ namespace vkcv { } void Window::onResize(GLFWwindow *callbackWindow, int width, int height) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); if (window != nullptr) { @@ -97,13 +134,33 @@ namespace vkcv { } void Window::onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); if (window != nullptr) { window->e_key(key, scancode, action, mods); } } + + void Window::onCharEvent(GLFWwindow *callbackWindow, unsigned int c) { + auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); + + if (window != nullptr) { + window->e_char(c); + } + } + + void Window::onGamepadEvent(int gamepadIndex) { + int activeWindowIndex = std::find_if(s_Windows.begin(), + s_Windows.end(), + [](GLFWwindow* window){return glfwGetWindowAttrib(window, GLFW_FOCUSED);}) + - s_Windows.begin(); + activeWindowIndex *= (activeWindowIndex < s_Windows.size()); // fixes index getting out of bounds (e.g. if there is no focused window) + auto window = static_cast<Window *>(glfwGetWindowUserPointer(s_Windows[activeWindowIndex])); + + if (window != nullptr) { + window->e_gamepad(gamepadIndex); + } + } bool Window::isWindowOpen() const { return !glfwWindowShouldClose(m_window); @@ -124,4 +181,4 @@ namespace vkcv { GLFWwindow *Window::getWindow() const { return m_window; } -} \ No newline at end of file +}