diff --git a/.gitignore b/.gitignore index 7ee4ff1903e902c4715c6e2b0c3e784ed5755aaf..76a0fa8e507371af6821b220a402666e79c340a3 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,8 @@ cmake-build-release/ # GUI configuration files imgui.ini + +# Generated source and header files for shaders +*.hxx +*.cxx + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 33b70018e368ecc3ad019ea33e57485814eb233a..84a1e902ace668fbee40346cf71e3c4c7a519f2e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,8 @@ variables: RUN: value: "all" - description: "The tests that should run. Possible values: ubuntu, win, all." - GIT_DEPTH: 1 + description: "The tests that should run. Possible values: ubuntu, win-msvc, win-mingw, mac, all." + GIT_DEPTH: 15 stages: - build @@ -17,7 +17,7 @@ build_ubuntu_gcc: - ubuntu-gcc-cached variables: GIT_SUBMODULE_STRATEGY: recursive - timeout: 10m + timeout: 15m retry: 1 script: - mkdir debug @@ -34,13 +34,13 @@ build_ubuntu_gcc: build_win10_msvc: only: variables: - - $RUN =~ /\bwin.*/i || $RUN =~ /\ball.*/i + - $RUN =~ /\bwin-msvc.*/i || $RUN =~ /\ball.*/i stage: build tags: - win10-msvc-cached variables: GIT_SUBMODULE_STRATEGY: recursive - timeout: 10m + timeout: 15m retry: 0 script: - cd 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\' @@ -51,6 +51,42 @@ build_win10_msvc: - cmake -DCMAKE_BUILD_TYPE=Debug .. - cmake --build . +build_win10_mingw: + only: + variables: + - $RUN =~ /\bwin-mingw.*/i || $RUN =~ /\ball.*/i + stage: build + tags: + - win10-mingw-cached + variables: + GIT_SUBMODULE_STRATEGY: recursive + timeout: 15m + retry: 0 + script: + - mkdir debug + - cd debug + - cmake --no-warn-unused-cli -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_C_COMPILER:FILEPATH=C:\msys64\mingw64\bin\x86_64-w64-mingw32-gcc.exe -DCMAKE_CXX_COMPILER:FILEPATH=C:\msys64\mingw64\bin\x86_64-w64-mingw32-g++.exe .. -G "Unix Makefiles" + - cmake --build . -j 8 + +build_mac_clang: + only: + variables: + - $RUN =~ /\bmac.*/i || $RUN =~ /\ball.*/i + stage: build + tags: + - catalina-clang-cached + variables: + GIT_SUBMODULE_STRATEGY: recursive + timeout: 15m + retry: 1 + script: + - mkdir debug + - cd debug + - export LDFLAGS="-L/usr/local/opt/llvm/lib" + - export CPPFLAGS="-I/usr/local/opt/llvm/include" + - cmake -DCMAKE_C_COMPILER="/usr/local/opt/llvm/bin/clang" -DCMAKE_CXX_COMPILER="/usr/local/opt/llvm/bin/clang++" -DCMAKE_BUILD_TYPE=Debug .. + - cmake --build . + deploy_doc_develop: only: variables: diff --git a/.gitmodules b/.gitmodules index cfa32fb462987b3e2f4ffec40caaae37b0ed7285..1e5c26deddea8cb725aae8c84513d1dcd18e4cfb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,6 @@ [submodule "lib/VulkanMemoryAllocator-Hpp"] path = lib/VulkanMemoryAllocator-Hpp url = https://github.com/malte-v/VulkanMemoryAllocator-Hpp.git +[submodule "modules/upscaling/lib/FidelityFX-FSR"] + path = modules/upscaling/lib/FidelityFX-FSR + url = https://github.com/GPUOpen-Effects/FidelityFX-FSR.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ae078a428a8e5e640ed8dc7bcc2f4e58e159c6b..dfafe1cd084d4b324c233d502e301c24a5ee95e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,8 @@ set(vkcv_flags ${CMAKE_CXX_FLAGS}) # enabling warnings in the debug build if (vkcv_build_debug) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set(vkcv_flags ${vkcv_flags} " -Weverything") + #set(vkcv_flags ${vkcv_flags} " -Weverything") + set(vkcv_flags ${vkcv_flags} " -Wextra -Wall") elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(vkcv_flags ${vkcv_flags} " -Wextra -Wall -pedantic") else() diff --git a/config/Libraries.cmake b/config/Libraries.cmake index b0684091d59b659c712aeacecd91e200351e0117..512669ce85a96f8cc94d8181994cfe458fa8b604 100644 --- a/config/Libraries.cmake +++ b/config/Libraries.cmake @@ -3,7 +3,13 @@ set(vkcv_config_lib ${vkcv_config}/lib) set(vkcv_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_lib}) if(NOT WIN32) - set(vkcv_libraries stdc++fs) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(vkcv_libraries stdc++fs) + endif() + + if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + list(APPEND vkcv_flags -Xpreprocessor) + endif() # optimization for loading times list(APPEND vkcv_flags -pthread) @@ -34,6 +40,9 @@ endif () # fix dependencies for different Linux distros (looking at you Ubuntu) include(${vkcv_config_ext}/CheckLibraries.cmake) +# add custom function to include a file like a shader as string +include(${vkcv_config_ext}/IncludeShader.cmake) + # cleanup of compiler definitions aka preprocessor variables if (vkcv_definitions) list(REMOVE_DUPLICATES vkcv_definitions) diff --git a/config/Sources.cmake b/config/Sources.cmake index 7ae106e2538c66179ab1ed50408551c43b785bc3..41cd0c20f2106dc02700d9b23227f3e6c34a057a 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -6,6 +6,9 @@ set(vkcv_sources ${vkcv_include}/vkcv/Core.hpp ${vkcv_source}/vkcv/Core.cpp + + ${vkcv_include}/vkcv/File.hpp + ${vkcv_source}/vkcv/File.cpp ${vkcv_include}/vkcv/PassConfig.hpp ${vkcv_source}/vkcv/PassConfig.cpp diff --git a/config/ext/IncludeShader.cmake b/config/ext/IncludeShader.cmake new file mode 100644 index 0000000000000000000000000000000000000000..e67a8716fb32a953c93a3c6624f0d459a025e950 --- /dev/null +++ b/config/ext/IncludeShader.cmake @@ -0,0 +1,75 @@ + +function(include_shader shader include_dir source_dir) + if (NOT EXISTS ${shader}) + message(WARNING "Shader file does not exist: ${shader}") + else() + get_filename_component(filename ${shader} NAME) + file(SIZE ${shader} filesize) + + set(include_target_file ${include_dir}/${filename}.hxx) + set(source_target_file ${source_dir}/${filename}.cxx) + + if ((EXISTS ${source_target_file}) AND (EXISTS ${include_target_file})) + file(TIMESTAMP ${shader} shader_timestamp "%Y-%m-%dT%H:%M:%S") + file(TIMESTAMP ${source_target_file} source_timestamp "%Y-%m-%dT%H:%M:%S") + + string(COMPARE GREATER ${shader_timestamp} ${source_timestamp} shader_update) + else() + set(shader_update true) + endif() + + if (shader_update) + string(TOUPPER ${filename} varname) + string(REPLACE "." "_" varname ${varname}) + + set(shader_header "#pragma once\n") + string(APPEND shader_header "// This file is auto-generated via cmake, so don't touch it!\n") + string(APPEND shader_header "extern unsigned char ${varname} [${filesize}]\;\n") + string(APPEND shader_header "extern unsigned int ${varname}_LEN\;\n") + string(APPEND shader_header "const std::string ${varname}_SHADER (reinterpret_cast<const char*>(${varname}), ${varname}_LEN)\;") + + file(WRITE ${include_target_file} ${shader_header}) + + find_program(xxd_program "xxd") + + if (EXISTS ${xxd_program}) + get_filename_component(shader_directory ${shader} DIRECTORY) + + add_custom_command( + OUTPUT ${source_target_file} + WORKING_DIRECTORY "${shader_directory}" + COMMAND xxd -i -C "${filename}" "${source_target_file}" + COMMENT "Processing shader into source files: ${shader}" + ) + else() + set(shader_source "// This file is auto-generated via cmake, so don't touch it!\n") + string(APPEND shader_source "unsigned char ${varname}[] = {") + + math(EXPR max_fileoffset "${filesize} - 1" OUTPUT_FORMAT DECIMAL) + + message(STATUS "Processing shader into source files: ${shader}") + + foreach(fileoffset RANGE ${max_fileoffset}) + file(READ ${shader} shader_source_byte OFFSET ${fileoffset} LIMIT 1 HEX) + + math(EXPR offset_modulo "${fileoffset} % 12" OUTPUT_FORMAT DECIMAL) + + if (${offset_modulo} EQUAL 0) + string(APPEND shader_source "\n ") + endif() + + if (${fileoffset} LESS ${max_fileoffset}) + string(APPEND shader_source "0x${shader_source_byte}, ") + else() + string(APPEND shader_source "0x${shader_source_byte}\n") + endif() + endforeach() + + string(APPEND shader_source "}\;\n") + string(APPEND shader_source "unsigned int ${varname}_LEN = ${filesize}\;") + + file(WRITE ${source_target_file} ${shader_source}) + endif() + endif() + endif() +endfunction() diff --git a/config/lib/vma/vma.cpp b/config/lib/vma/vma.cpp index 0928b552c10e23914054c44b8de43df722aa2cf0..307c27f096bd1bae2b1deb2ca5994f132adc92cc 100644 --- a/config/lib/vma/vma.cpp +++ b/config/lib/vma/vma.cpp @@ -3,5 +3,56 @@ #define _DEBUG #endif +#ifndef _MSVC_LANG +#ifdef __MINGW32__ +#include <stdint.h> +#include <stdlib.h> + +class VmaMutex { +public: + VmaMutex() : m_locked(false) {} + + void Lock() { + while (m_locked); + m_locked = true; + } + + void Unlock() { + m_locked = false; + } +private: + bool m_locked; +}; + +#define VMA_MUTEX VmaMutex + +template <typename T> +T* custom_overestimate_malloc(size_t size) { + return new T[size + (sizeof(T) - 1) / sizeof(T)]; +} + +void* custom_aligned_malloc(size_t alignment, size_t size) { + if (alignment > 4) { + return custom_overestimate_malloc<uint64_t>(size); + } else + if (alignment > 2) { + return custom_overestimate_malloc<uint32_t>(size); + } else + if (alignment > 1) { + return custom_overestimate_malloc<uint16_t>(size); + } else { + return custom_overestimate_malloc<uint8_t>(size); + } +} + +void custom_free(void *ptr) { + delete[] reinterpret_cast<uint8_t*>(ptr); +} + +#define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (custom_aligned_malloc(alignment, size)) +#define VMA_SYSTEM_FREE(ptr) (custom_free(ptr)) +#endif +#endif + #define VMA_IMPLEMENTATION #include "vk_mem_alloc.hpp" diff --git a/include/vkcv/Context.hpp b/include/vkcv/Context.hpp index 2ecd9203701510837f49d10c1879efd4890145e9..824713fd1e29cbb8b7e60b22768c0019daaa9938 100644 --- a/include/vkcv/Context.hpp +++ b/include/vkcv/Context.hpp @@ -4,6 +4,7 @@ #include <vk_mem_alloc.hpp> #include "QueueManager.hpp" +#include "DrawcallRecording.hpp" namespace vkcv { @@ -39,9 +40,9 @@ namespace vkcv static Context create(const char *applicationName, uint32_t applicationVersion, - std::vector<vk::QueueFlagBits> queueFlags, - std::vector<const char *> instanceExtensions, - std::vector<const char *> deviceExtensions); + const std::vector<vk::QueueFlagBits>& queueFlags, + const std::vector<const char *>& instanceExtensions, + const std::vector<const char *>& deviceExtensions); private: /** diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index 5db834cbb9976d856594263887847d71d42ce264..66a50e5640783048a41f5ca18dbdffc6f5be679b 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -138,9 +138,9 @@ namespace vkcv static Core create(Window &window, const char *applicationName, uint32_t applicationVersion, - std::vector<vk::QueueFlagBits> queueFlags = {}, - std::vector<const char*> instanceExtensions = {}, - std::vector<const char*> deviceExtensions = {}); + const std::vector<vk::QueueFlagBits>& queueFlags = {}, + const std::vector<const char*>& instanceExtensions = {}, + const std::vector<const char*>& deviceExtensions = {}); /** * Creates a basic vulkan graphics pipeline using @p config from the pipeline config class and returns it using the @p handle. @@ -163,7 +163,7 @@ namespace vkcv */ [[nodiscard]] PipelineHandle createComputePipeline( - const ShaderProgram &config, + const ShaderProgram &shaderProgram, const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts); /** @@ -196,11 +196,13 @@ namespace vkcv * @param minFilter Minimizing filter * @param mipmapMode Mipmapping filter * @param addressMode Address mode + * @param mipLodBias Mip level of detail bias * @return Sampler handle */ [[nodiscard]] SamplerHandle createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, - SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode); + SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode, + float mipLodBias = 0.0f); /** * Creates an #Image with a given format, width, height and depth. @@ -223,9 +225,13 @@ namespace vkcv Multisampling multisampling = Multisampling::None); [[nodiscard]] - uint32_t getImageWidth(ImageHandle imageHandle); + uint32_t getImageWidth(const ImageHandle& image); + [[nodiscard]] - uint32_t getImageHeight(ImageHandle imageHandle); + uint32_t getImageHeight(const ImageHandle& image); + + [[nodiscard]] + vk::Format getImageFormat(const ImageHandle& image); /** TODO: * @param setDescriptions @@ -243,13 +249,21 @@ namespace vkcv bool beginFrame(uint32_t& width, uint32_t& height); void recordDrawcallsToCmdStream( - const CommandStreamHandle cmdStreamHandle, + const CommandStreamHandle cmdStreamHandle, const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, const PushConstants &pushConstants, const std::vector<DrawcallInfo> &drawcalls, const std::vector<ImageHandle> &renderTargets); + void recordMeshShaderDrawcalls( + const CommandStreamHandle cmdStreamHandle, + const PassHandle renderpassHandle, + const PipelineHandle pipelineHandle, + const PushConstants& pushConstantData, + const std::vector<MeshShaderDrawcall>& drawcalls, + const std::vector<ImageHandle>& renderTargets); + void recordComputeDispatchToCmdStream( CommandStreamHandle cmdStream, PipelineHandle computePipeline, @@ -283,16 +297,22 @@ namespace vkcv 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); - void readBufferMemoryBarrier(const CommandStreamHandle cmdStream, const BufferHandle buffer, void *data); - void resolveMSAAImage(CommandStreamHandle cmdStream, ImageHandle src, ImageHandle dst); + 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); + void readBufferMemoryBarrier(const CommandStreamHandle cmdStream, const BufferHandle buffer, void *data); + void resolveMSAAImage(const CommandStreamHandle& cmdStream, const ImageHandle& src, const ImageHandle& dst); + [[nodiscard]] vk::ImageView getSwapchainImageView() const; + + void recordMemoryBarrier(const CommandStreamHandle& cmdStream); + + void recordBlitImage(const CommandStreamHandle& cmdStream, const ImageHandle& src, const ImageHandle& dst, + SamplerFilterType filterType); }; } diff --git a/include/vkcv/DescriptorConfig.hpp b/include/vkcv/DescriptorConfig.hpp index 776322e6270431f9fa52fd7c3cb4551e5b4bf752..767492eb2b27bd8dff56ef2aeb4769c08eed7200 100644 --- a/include/vkcv/DescriptorConfig.hpp +++ b/include/vkcv/DescriptorConfig.hpp @@ -23,7 +23,9 @@ namespace vkcv STORAGE_BUFFER, SAMPLER, IMAGE_SAMPLED, - IMAGE_STORAGE + IMAGE_STORAGE, + UNIFORM_BUFFER_DYNAMIC, + STORAGE_BUFFER_DYNAMIC }; /* diff --git a/include/vkcv/DescriptorWrites.hpp b/include/vkcv/DescriptorWrites.hpp index f28a6c91e189b13413ffefec0f05e5a0a358ee26..28de2ed7fa6b7e71bfa49b67a337f80f2e05ddcf 100644 --- a/include/vkcv/DescriptorWrites.hpp +++ b/include/vkcv/DescriptorWrites.hpp @@ -20,16 +20,15 @@ namespace vkcv { uint32_t mipLevel; }; - struct UniformBufferDescriptorWrite { - inline UniformBufferDescriptorWrite(uint32_t binding, BufferHandle buffer) : binding(binding), buffer(buffer) {}; - uint32_t binding; - BufferHandle buffer; - }; - - struct StorageBufferDescriptorWrite { - inline StorageBufferDescriptorWrite(uint32_t binding, BufferHandle buffer) : binding(binding), buffer(buffer) {}; + struct BufferDescriptorWrite { + inline BufferDescriptorWrite(uint32_t binding, BufferHandle buffer, bool dynamic = false, + uint32_t offset = 0, uint32_t size = 0) : + binding(binding), buffer(buffer), dynamic(dynamic), offset(offset), size(size) {}; uint32_t binding; BufferHandle buffer; + bool dynamic; + uint32_t offset; + uint32_t size; }; struct SamplerDescriptorWrite { @@ -41,8 +40,8 @@ namespace vkcv { struct DescriptorWrites { std::vector<SampledImageDescriptorWrite> sampledImageWrites; std::vector<StorageImageDescriptorWrite> storageImageWrites; - std::vector<UniformBufferDescriptorWrite> uniformBufferWrites; - std::vector<StorageBufferDescriptorWrite> storageBufferWrites; + std::vector<BufferDescriptorWrite> uniformBufferWrites; + std::vector<BufferDescriptorWrite> storageBufferWrites; std::vector<SamplerDescriptorWrite> samplerWrites; }; } \ No newline at end of file diff --git a/include/vkcv/DrawcallRecording.hpp b/include/vkcv/DrawcallRecording.hpp index 572fc2b6b51735bdcd7eb77c1dd9d4a3482a1640..260fbbc6a2a577d0d333656a1eff4f7f3f88cd69 100644 --- a/include/vkcv/DrawcallRecording.hpp +++ b/include/vkcv/DrawcallRecording.hpp @@ -13,21 +13,30 @@ namespace vkcv { vk::Buffer buffer; }; + enum class IndexBitCount{ + Bit16, + Bit32 + }; + struct DescriptorSetUsage { - inline DescriptorSetUsage(uint32_t setLocation, vk::DescriptorSet vulkanHandle) noexcept - : setLocation(setLocation), vulkanHandle(vulkanHandle) {} + inline DescriptorSetUsage(uint32_t setLocation, vk::DescriptorSet vulkanHandle, + const std::vector<uint32_t>& dynamicOffsets = {}) noexcept + : setLocation(setLocation), vulkanHandle(vulkanHandle), dynamicOffsets(dynamicOffsets) {} - const uint32_t setLocation; - const vk::DescriptorSet vulkanHandle; + const uint32_t setLocation; + const vk::DescriptorSet vulkanHandle; + const std::vector<uint32_t> dynamicOffsets; }; struct Mesh { - inline Mesh(std::vector<VertexBufferBinding> vertexBufferBindings, vk::Buffer indexBuffer, size_t indexCount) noexcept - : vertexBufferBindings(vertexBufferBindings), indexBuffer(indexBuffer), indexCount(indexCount){} + inline Mesh(std::vector<VertexBufferBinding> vertexBufferBindings, vk::Buffer indexBuffer, size_t indexCount, IndexBitCount indexBitCount = IndexBitCount::Bit16) noexcept + : vertexBufferBindings(vertexBufferBindings), indexBuffer(indexBuffer), indexCount(indexCount), indexBitCount(indexBitCount){} std::vector<VertexBufferBinding> vertexBufferBindings; vk::Buffer indexBuffer; size_t indexCount; + IndexBitCount indexBitCount; + }; struct DrawcallInfo { @@ -46,4 +55,21 @@ namespace vkcv { const PushConstants &pushConstants, const size_t drawcallIndex); -} \ No newline at end of file + void InitMeshShaderDrawFunctions(vk::Device device); + + struct MeshShaderDrawcall { + inline MeshShaderDrawcall(const std::vector<DescriptorSetUsage> descriptorSets, uint32_t taskCount) + : descriptorSets(descriptorSets), taskCount(taskCount) {} + + std::vector<DescriptorSetUsage> descriptorSets; + uint32_t taskCount; + }; + + void recordMeshShaderDrawcall( + vk::CommandBuffer cmdBuffer, + vk::PipelineLayout pipelineLayout, + const PushConstants& pushConstantData, + const uint32_t pushConstantOffset, + const MeshShaderDrawcall& drawcall, + const uint32_t firstTask); +} diff --git a/include/vkcv/Event.hpp b/include/vkcv/Event.hpp index da5cbc72fbb3eee3a71a35c1da6fe32dff06b057..604e3a444dc3bffd2841cb69cd99746d59af523d 100644 --- a/include/vkcv/Event.hpp +++ b/include/vkcv/Event.hpp @@ -1,7 +1,12 @@ #pragma once #include <functional> + +#ifndef __MINGW32__ #include <mutex> +#endif + +#include <vector> namespace vkcv { @@ -27,7 +32,10 @@ namespace vkcv { private: std::vector< event_function<T...> > m_functions; uint32_t m_id_counter; + +#ifndef __MINGW32__ std::mutex m_mutex; +#endif public: @@ -75,14 +83,18 @@ namespace vkcv { * locks the event so its function handles won't be called */ void lock() { +#ifndef __MINGW32__ m_mutex.lock(); +#endif } /** * unlocks the event so its function handles can be called after locking */ void unlock() { +#ifndef __MINGW32__ m_mutex.unlock(); +#endif } explicit event(bool locked = false) { diff --git a/include/vkcv/File.hpp b/include/vkcv/File.hpp new file mode 100644 index 0000000000000000000000000000000000000000..06f1c48593853147140b2c8c68c675d52c9dfaec --- /dev/null +++ b/include/vkcv/File.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include <filesystem> + +namespace vkcv { + + std::filesystem::path generateTemporaryFilePath(); + + std::filesystem::path generateTemporaryDirectoryPath(); + +} diff --git a/include/vkcv/QueueManager.hpp b/include/vkcv/QueueManager.hpp index ac043b2d351014ea79fcae0d0fc439bb64a87b72..0919d20d8e07fee67ceb2f393c29b4a53c51b857 100644 --- a/include/vkcv/QueueManager.hpp +++ b/include/vkcv/QueueManager.hpp @@ -32,8 +32,8 @@ namespace vkcv { const std::vector<Queue> &getTransferQueues() const; static void queueCreateInfosQueueHandles(vk::PhysicalDevice &physicalDevice, - std::vector<float> &queuePriorities, - std::vector<vk::QueueFlagBits> &queueFlags, + const std::vector<float> &queuePriorities, + const std::vector<vk::QueueFlagBits> &queueFlags, std::vector<vk::DeviceQueueCreateInfo> &queueCreateInfos, std::vector<std::pair<int, int>> &queuePairsGraphics, std::vector<std::pair<int, int>> &queuePairsCompute, diff --git a/include/vkcv/ShaderStage.hpp b/include/vkcv/ShaderStage.hpp index dca395bdba82a2f1cb38bb0a25196cfd3dab8019..3893bdf5f73408847ceb2b076abfb7d0902bb2f9 100644 --- a/include/vkcv/ShaderStage.hpp +++ b/include/vkcv/ShaderStage.hpp @@ -9,7 +9,11 @@ namespace vkcv { TESS_EVAL, GEOMETRY, FRAGMENT, - COMPUTE + COMPUTE, + TASK, + MESH }; + + } diff --git a/lib/VulkanMemoryAllocator-Hpp b/lib/VulkanMemoryAllocator-Hpp index eae6e8d3bd4593e0d7071c85fba2a8f33fbe5dab..3a61240a5354ce56c222969a69825aabb6ba0a21 160000 --- a/lib/VulkanMemoryAllocator-Hpp +++ b/lib/VulkanMemoryAllocator-Hpp @@ -1 +1 @@ -Subproject commit eae6e8d3bd4593e0d7071c85fba2a8f33fbe5dab +Subproject commit 3a61240a5354ce56c222969a69825aabb6ba0a21 diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 802200ad5deb76decbb75e30e1fbd14bff3b7e3b..4b576e7119ebe769eafd1b6abb033b4fb02a3ec1 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -4,6 +4,8 @@ add_subdirectory(asset_loader) add_subdirectory(camera) add_subdirectory(gui) add_subdirectory(material) +add_subdirectory(meshlet) add_subdirectory(scene) add_subdirectory(shader_compiler) add_subdirectory(testing) +add_subdirectory(upscaling) diff --git a/modules/camera/config/GLM.cmake b/modules/camera/config/GLM.cmake index efd6444451100b912aa0b5b4a532dc8f448b0b40..f256ccade8c4f44a89744bb7875371324cf2369d 100644 --- a/modules/camera/config/GLM.cmake +++ b/modules/camera/config/GLM.cmake @@ -4,18 +4,20 @@ 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_includes ${vkcv_camera_lib_path}/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 () endif () + +list(APPEND vkcv_camera_definitions GLM_DEPTH_ZERO_TO_ONE) +list(APPEND vkcv_camera_definitions GLM_FORCE_LEFT_HANDED) + +if ((WIN32) AND (${CMAKE_SIZEOF_VOID_P} MATCHES 4)) + list(APPEND vkcv_camera_definitions GLM_ENABLE_EXPERIMENTAL) +endif() diff --git a/modules/meshlet/CMakeLists.txt b/modules/meshlet/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d576466d3d125d3a19640088a9b5725ac7a46b97 --- /dev/null +++ b/modules/meshlet/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_meshlet) + +# setting c++ standard for the module +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_meshlet_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_meshlet_include ${PROJECT_SOURCE_DIR}/include) + +# Add source and header files to the module +set(vkcv_meshlet_sources + ${vkcv_meshlet_include}/vkcv/meshlet/Meshlet.hpp + ${vkcv_meshlet_source}/vkcv/meshlet/Meshlet.cpp + + ${vkcv_meshlet_include}/vkcv/meshlet/Tipsify.hpp + ${vkcv_meshlet_source}/vkcv/meshlet/Tipsify.cpp + + ${vkcv_meshlet_include}/vkcv/meshlet/Forsyth.hpp + ${vkcv_meshlet_source}/vkcv/meshlet/Forsyth.cpp) + +# adding source files to the module +add_library(vkcv_meshlet STATIC ${vkcv_meshlet_sources}) + + +# link the required libraries to the module +target_link_libraries(vkcv_meshlet vkcv ${vkcv_libraries}) + +# including headers of dependencies and the VkCV framework +target_include_directories(vkcv_meshlet SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include}) + +# add the own include directory for public headers +target_include_directories(vkcv_meshlet BEFORE PUBLIC ${vkcv_meshlet_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(vkcv_meshlet vkcv vkcv_asset_loader vkcv_camera) diff --git a/modules/meshlet/include/vkcv/meshlet/Forsyth.hpp b/modules/meshlet/include/vkcv/meshlet/Forsyth.hpp new file mode 100644 index 0000000000000000000000000000000000000000..43dc9a3b6bb81ea915268de7a7b53b18efd27638 --- /dev/null +++ b/modules/meshlet/include/vkcv/meshlet/Forsyth.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "Meshlet.hpp" + +namespace vkcv::meshlet +{ + /** + * Reorders the index buffer, simulating a LRU cache, so that vertices are grouped together in close triangle patches + * @param idxBuf current IndexBuffer + * @param vertexCount of the mesh + * @return new reordered index buffer to replace the input index buffer + * References: + * https://tomforsyth1000.github.io/papers/fast_vert_cache_opt.html + * https://www.martin.st/thesis/efficient_triangle_reordering.pdf + * https://github.com/vivkin/forsyth/blob/master/forsyth.h + */ + VertexCacheReorderResult forsythReorder(const std::vector<uint32_t> &idxBuf, const size_t vertexCount); +} diff --git a/modules/meshlet/include/vkcv/meshlet/Meshlet.hpp b/modules/meshlet/include/vkcv/meshlet/Meshlet.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9900dffaf28c85753d367ba79bbdf5c19a2cf479 --- /dev/null +++ b/modules/meshlet/include/vkcv/meshlet/Meshlet.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include <vector> +#include <map> +#include <glm/glm.hpp> +#include <vkcv/asset/asset_loader.hpp> + +namespace vkcv::meshlet { + + struct Vertex { + glm::vec3 position; + float padding0; + glm::vec3 normal; + float padding1; + }; + + struct Meshlet { + uint32_t vertexOffset; + uint32_t vertexCount; + uint32_t indexOffset; + uint32_t indexCount; + glm::vec3 meanPosition; + float boundingSphereRadius; + }; + + struct VertexCacheReorderResult { + /** + * @param indexBuffer new indexBuffer + * @param skippedIndices indices that have a spacial break + */ + VertexCacheReorderResult(const std::vector<uint32_t> indexBuffer, const std::vector<uint32_t> skippedIndices) + :indexBuffer(indexBuffer), skippedIndices(skippedIndices) {} + + std::vector<uint32_t> indexBuffer; + std::vector<uint32_t> skippedIndices; + }; + + struct MeshShaderModelData { + std::vector<Vertex> vertices; + std::vector<uint32_t> localIndices; + std::vector<Meshlet> meshlets; + }; + + std::vector<Vertex> convertToVertices( + const std::vector<uint8_t>& vertexData, + const uint64_t vertexCount, + const vkcv::asset::VertexAttribute& positionAttribute, + const vkcv::asset::VertexAttribute& normalAttribute); + + MeshShaderModelData createMeshShaderModelData( + const std::vector<Vertex>& inVertices, + const std::vector<uint32_t>& inIndices, + const std::vector<uint32_t>& deadEndIndices = {}); + + std::vector<uint32_t> assetLoaderIndicesTo32BitIndices( + const std::vector<uint8_t>& indexData, + vkcv::asset::IndexType indexType); + +} \ No newline at end of file diff --git a/modules/meshlet/include/vkcv/meshlet/Tipsify.hpp b/modules/meshlet/include/vkcv/meshlet/Tipsify.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6fb4b37d9c17c82642c3b5e7667c3e8acc50b8c0 --- /dev/null +++ b/modules/meshlet/include/vkcv/meshlet/Tipsify.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "Meshlet.hpp" +#include <algorithm> +#include <iostream> + +namespace vkcv::meshlet { + /** + * reorders the IndexBuffer, so all usages of vertices to triangle are as close as possible + * @param indexBuffer32Bit current IndexBuffer + * @param vertexCount of the mesh + * @param cacheSize of the priority cache <br> + * Recommended: 20. Keep the value between 5 and 50 <br> + * low: more random and patchy<br> + * high: closer vertices have higher chance -> leads to sinuous lines + * @return new IndexBuffer that replaces the input IndexBuffer, and the indices that are skipped + * + * https://gfx.cs.princeton.edu/pubs/Sander_2007_%3ETR/tipsy.pdf + * https://www.martin.st/thesis/efficient_triangle_reordering.pdf + */ + VertexCacheReorderResult tipsifyMesh(const std::vector<uint32_t> &indexBuffer32Bit, + const int vertexCount, const unsigned int cacheSize = 20); +} \ No newline at end of file diff --git a/modules/meshlet/src/vkcv/meshlet/Forsyth.cpp b/modules/meshlet/src/vkcv/meshlet/Forsyth.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fd0f160d65b8db81102f9eb6a9d60cf735999d44 --- /dev/null +++ b/modules/meshlet/src/vkcv/meshlet/Forsyth.cpp @@ -0,0 +1,317 @@ +#include "vkcv/meshlet/Forsyth.hpp" +#include <vkcv/Logger.hpp> +#include <array> +#include <cmath> + +namespace vkcv::meshlet +{ + /* + * CACHE AND VALENCE + * SIZE AND SCORE CONSTANTS + * CHANGE AS NEEDED + */ + + // set these to adjust performance and result quality + const size_t VERTEX_CACHE_SIZE = 8; + const size_t CACHE_FUNCTION_LENGTH = 32; + + // score function constants + const float CACHE_DECAY_POWER = 1.5f; + const float LAST_TRI_SCORE = 0.75f; + + const float VALENCE_BOOST_SCALE = 2.0f; + const float VALENCE_BOOST_POWER = 0.5f; + + // sizes for precalculated tables + // make sure that cache score is always >= vertex_cache_size + const size_t CACHE_SCORE_TABLE_SIZE = 32; + const size_t VALENCE_SCORE_TABLE_SIZE = 32; + + // precalculated tables + std::array<float, CACHE_SCORE_TABLE_SIZE> cachePositionScore = {}; + std::array<float, VALENCE_SCORE_TABLE_SIZE> valenceScore = {}; + + // function to populate the cache position and valence score tables + void initScoreTables() + { + for(size_t i = 0; i < CACHE_SCORE_TABLE_SIZE; i++) + { + float score = 0.0f; + if (i < 3) + { + score = LAST_TRI_SCORE; + } + else + { + const float scaler = 1.0f / static_cast<float>(CACHE_FUNCTION_LENGTH - 3); + score = 1.0f - (i - 3) * scaler; + score = std::pow(score, CACHE_DECAY_POWER); + } + cachePositionScore[i] = score; + } + + for(size_t i = 0; i < VALENCE_SCORE_TABLE_SIZE; i++) + { + const float valenceBoost = std::pow(i, -VALENCE_BOOST_POWER); + const float score = VALENCE_BOOST_SCALE * valenceBoost; + + valenceScore[i] = score; + } + } + + /** + * Return the vertex' score, depending on its current active triangle count and cache position + * Add a valence boost to score, if active triangles are below VALENCE_SCORE_TABLE_SIZE + * @param numActiveTris the active triangles on this vertex + * @param cachePos the vertex' position in the cache + * @return vertex' score + */ + float findVertexScore(uint32_t numActiveTris, int32_t cachePos) + { + if(numActiveTris == 0) + return 0.0f; + + float score = 0.0f; + + if (cachePos >= 0) + score = cachePositionScore[cachePos]; + + if (numActiveTris < VALENCE_SCORE_TABLE_SIZE) + score += valenceScore[numActiveTris]; + + return score; + } + + VertexCacheReorderResult forsythReorder(const std::vector<uint32_t> &idxBuf, const size_t vertexCount) + { + std::vector<uint32_t> skippedIndices; + + initScoreTables(); + + // get the total triangle count from the index buffer + const size_t triangleCount = idxBuf.size() / 3; + + // per-vertex active triangle count + std::vector<uint8_t> numActiveTris(vertexCount, 0); + // iterate over indices, count total occurrences of each vertex + for(const auto index : idxBuf) + { + if(numActiveTris[index] == UINT8_MAX) + { + vkcv_log(LogLevel::ERROR, "Unsupported mesh."); + vkcv_log(LogLevel::ERROR, "Vertex shared by too many triangles."); + return VertexCacheReorderResult({}, {}); + } + + numActiveTris[index]++; + } + + + // allocate remaining vectors + /** + * offsets: contains the vertices' offset into the triangleIndices vector + * Offset itself is the sum of triangles required by the previous vertices + * + * lastScore: the vertices' most recent calculated score + * + * cacheTag: the vertices' most recent cache score + * + * triangleAdded: boolean flags to denote whether a triangle has been processed or not + * + * triangleScore: total score of the three vertices making up the triangle + * + * triangleIndices: indices for the triangles + */ + std::vector<uint32_t> offsets(vertexCount, 0); + std::vector<float> lastScore(vertexCount, 0.0f); + std::vector<int8_t> cacheTag(vertexCount, -1); + + std::vector<bool> triangleAdded(triangleCount, false); + std::vector<float> triangleScore(triangleCount, 0.0f); + + std::vector<int32_t> triangleIndices(idxBuf.size(), 0); + + + // sum the number of active triangles for all previous vertices + // null the number of active triangles afterwards for recalculation in second loop + uint32_t sum = 0; + for(size_t i = 0; i < vertexCount; i++) + { + offsets[i] = sum; + sum += numActiveTris[i]; + numActiveTris[i] = 0; + } + // create the triangle indices, using the newly calculated offsets, and increment numActiveTris + // every vertex should be referenced by a triangle index now + for(size_t i = 0; i < triangleCount; i++) + { + for(size_t j = 0; j < 3; j++) + { + uint32_t v = idxBuf[3 * i + j]; + triangleIndices[offsets[v] + numActiveTris[v]] = static_cast<int32_t>(i); + numActiveTris[v]++; + } + } + + // calculate and initialize the triangle score, by summing the vertices' score + for (size_t i = 0; i < vertexCount; i++) + { + lastScore[i] = findVertexScore(numActiveTris[i], static_cast<int32_t>(cacheTag[i])); + + for(size_t j = 0; j < numActiveTris[i]; j++) + { + triangleScore[triangleIndices[offsets[i] + j]] += lastScore[i]; + } + } + + // find best triangle to start reordering with + int32_t bestTriangle = -1; + float bestScore = -1.0f; + for(size_t i = 0; i < triangleCount; i++) + { + if(triangleScore[i] > bestScore) + { + bestScore = triangleScore[i]; + bestTriangle = static_cast<int32_t>(i); + } + } + + // allocate output triangles + std::vector<int32_t> outTriangles(triangleCount, 0); + uint32_t outPos = 0; + + // initialize cache (with -1) + std::array<int32_t, VERTEX_CACHE_SIZE + 3> cache = {}; + for(auto &element : cache) + { + element = -1; + } + + uint32_t scanPos = 0; + + // begin reordering routine + // output the currently best triangle, as long as there are triangles left to output + while(bestTriangle >= 0) + { + // mark best triangle as added + triangleAdded[bestTriangle] = true; + // output this triangle + outTriangles[outPos++] = bestTriangle; + + // push best triangle's vertices into the cache + for(size_t i = 0; i < 3; i++) + { + uint32_t v = idxBuf[3 * bestTriangle + i]; + + // get vertex' cache position, if its -1, set its position to the end + int8_t endPos = cacheTag[v]; + if(endPos < 0) + endPos = static_cast<int8_t>(VERTEX_CACHE_SIZE + i); + + // shift vertices' cache entries forward by one + for(int8_t j = endPos; j > i; j--) + { + cache[j] = cache[j - 1]; + + // if cache slot is valid vertex, + // update the vertex cache tag accordingly + if (cache[j] >= 0) + cacheTag[cache[j]]++; + } + + // insert current vertex into its new target slot + cache[i] = static_cast<int32_t>(v); + cacheTag[v] = static_cast<int8_t>(i); + + // find current triangle in the list of active triangles + // remove it by moving the last triangle into the slot the current triangle is holding. + for (size_t j = 0; j < numActiveTris[v]; j++) + { + if(triangleIndices[offsets[v] + j] == bestTriangle) + { + triangleIndices[offsets[v] + j] = triangleIndices[offsets[v] + numActiveTris[v] - 1]; + break; + } + } + // shorten the list + numActiveTris[v]--; + } + + // update scores of all triangles in cache + for (size_t i = 0; i < cache.size(); i++) + { + int32_t v = cache[i]; + if (v < 0) + break; + + // this vertex has been pushed outside of the actual cache + if(i >= VERTEX_CACHE_SIZE) + { + cacheTag[v] = -1; + cache[i] = -1; + } + + float newScore = findVertexScore(numActiveTris[v], cacheTag[v]); + float diff = newScore - lastScore[v]; + + for(size_t j = 0; j < numActiveTris[v]; j++) + { + triangleScore[triangleIndices[offsets[v] + j]] += diff; + } + lastScore[v] = newScore; + } + + // find best triangle reference by vertices in cache + bestTriangle = -1; + bestScore = -1.0f; + for(size_t i = 0; i < VERTEX_CACHE_SIZE; i++) + { + if (cache[i] < 0) + break; + + int32_t v = cache[i]; + for(size_t j = 0; j < numActiveTris[v]; j++) + { + int32_t t = triangleIndices[offsets[v] + j]; + if(triangleScore[t] > bestScore) + { + bestTriangle = t; + bestScore = triangleScore[t]; + } + } + } + + // if no triangle was found at all, continue scanning whole list of triangles + if (bestTriangle < 0) + { + for(; scanPos < triangleCount; scanPos++) + { + if(!triangleAdded[scanPos]) + { + bestTriangle = scanPos; + + skippedIndices.push_back(3 * outPos); + + break; + } + } + } + } + + + // convert triangle index array into full triangle list + std::vector<uint32_t> outIndices(idxBuf.size(), 0); + outPos = 0; + for(size_t i = 0; i < triangleCount; i++) + { + int32_t t = outTriangles[i]; + for(size_t j = 0; j < 3; j++) + { + int32_t v = idxBuf[3 * t + j]; + outIndices[outPos++] = static_cast<uint32_t>(v); + } + } + + return VertexCacheReorderResult(outIndices, skippedIndices); + } +} \ No newline at end of file diff --git a/modules/meshlet/src/vkcv/meshlet/Meshlet.cpp b/modules/meshlet/src/vkcv/meshlet/Meshlet.cpp new file mode 100644 index 0000000000000000000000000000000000000000..abcad7207ed5a6f80cb292ab2f7e855d3b4c7797 --- /dev/null +++ b/modules/meshlet/src/vkcv/meshlet/Meshlet.cpp @@ -0,0 +1,167 @@ + +#include "vkcv/meshlet/Meshlet.hpp" +#include <vkcv/Logger.hpp> +#include <cassert> +#include <iostream> + +namespace vkcv::meshlet { + +std::vector<vkcv::meshlet::Vertex> convertToVertices( + const std::vector<uint8_t>& vertexData, + const uint64_t vertexCount, + const vkcv::asset::VertexAttribute& positionAttribute, + const vkcv::asset::VertexAttribute& normalAttribute) { + + assert(positionAttribute.type == vkcv::asset::PrimitiveType::POSITION); + assert(normalAttribute.type == vkcv::asset::PrimitiveType::NORMAL); + + std::vector<vkcv::meshlet::Vertex> vertices; + vertices.reserve(vertexCount); + + const size_t positionStepSize = positionAttribute.stride == 0 ? sizeof(glm::vec3) : positionAttribute.stride; + const size_t normalStepSize = normalAttribute.stride == 0 ? sizeof(glm::vec3) : normalAttribute.stride; + + for (int i = 0; i < vertexCount; i++) { + Vertex v; + + const size_t positionOffset = positionAttribute.offset + positionStepSize * i; + const size_t normalOffset = normalAttribute.offset + normalStepSize * i; + + v.position = *reinterpret_cast<const glm::vec3*>(&(vertexData[positionOffset])); + v.normal = *reinterpret_cast<const glm::vec3*>(&(vertexData[normalOffset])); + vertices.push_back(v); + } + return vertices; +} + +MeshShaderModelData createMeshShaderModelData( + const std::vector<Vertex>& inVertices, + const std::vector<uint32_t>& inIndices, + const std::vector<uint32_t>& deadEndIndices) { + + MeshShaderModelData data; + size_t currentIndex = 0; + + const size_t maxVerticesPerMeshlet = 64; + const size_t maxIndicesPerMeshlet = 126 * 3; + + bool indicesAreLeft = true; + + size_t deadEndIndicesIndex = 0; + + while (indicesAreLeft) { + Meshlet meshlet; + + meshlet.indexCount = 0; + meshlet.vertexCount = 0; + + meshlet.indexOffset = data.localIndices.size(); + meshlet.vertexOffset = data.vertices.size(); + + std::map<uint32_t, uint32_t> globalToLocalIndexMap; + std::vector<uint32_t> globalIndicesOrdered; + + while (true) { + + if (deadEndIndicesIndex < deadEndIndices.size()) { + const uint32_t deadEndIndex = deadEndIndices[deadEndIndicesIndex]; + if (deadEndIndex == currentIndex) { + deadEndIndicesIndex++; + break; + } + } + + indicesAreLeft = currentIndex + 1 <= inIndices.size(); + if (!indicesAreLeft) { + break; + } + + bool enoughSpaceForIndices = meshlet.indexCount + 3 < maxIndicesPerMeshlet; + if (!enoughSpaceForIndices) { + break; + } + + size_t vertexCountToAdd = 0; + for (int i = 0; i < 3; i++) { + const uint32_t globalIndex = inIndices[currentIndex + i]; + const bool containsVertex = globalToLocalIndexMap.find(globalIndex) != globalToLocalIndexMap.end(); + if (!containsVertex) { + vertexCountToAdd++; + } + } + + bool enoughSpaceForVertices = meshlet.vertexCount + vertexCountToAdd < maxVerticesPerMeshlet; + if (!enoughSpaceForVertices) { + break; + } + + for (int i = 0; i < 3; i++) { + const uint32_t globalIndex = inIndices[currentIndex + i]; + + uint32_t localIndex; + const bool indexAlreadyExists = globalToLocalIndexMap.find(globalIndex) != globalToLocalIndexMap.end(); + if (indexAlreadyExists) { + localIndex = globalToLocalIndexMap[globalIndex]; + } + else { + localIndex = globalToLocalIndexMap.size(); + globalToLocalIndexMap[globalIndex] = localIndex; + globalIndicesOrdered.push_back(globalIndex); + } + + data.localIndices.push_back(localIndex); + } + + meshlet.indexCount += 3; + currentIndex += 3; + meshlet.vertexCount += vertexCountToAdd; + } + + for (const uint32_t globalIndex : globalIndicesOrdered) { + const Vertex v = inVertices[globalIndex]; + data.vertices.push_back(v); + } + + // compute mean position + meshlet.meanPosition = glm::vec3(0); + const uint32_t meshletLastVertexIndex = meshlet.vertexOffset + meshlet.vertexCount; + + for (uint32_t vertexIndex = meshlet.vertexOffset; vertexIndex < meshletLastVertexIndex; vertexIndex++) { + const Vertex& v = data.vertices[vertexIndex]; + meshlet.meanPosition += v.position; + } + meshlet.meanPosition /= meshlet.vertexCount; + + // compute bounding sphere radius + meshlet.boundingSphereRadius = 0.f; + for (uint32_t vertexIndex = meshlet.vertexOffset; vertexIndex < meshletLastVertexIndex; vertexIndex++) { + const Vertex& v = data.vertices[vertexIndex]; + const float d = glm::distance(v.position, meshlet.meanPosition); + meshlet.boundingSphereRadius = glm::max(meshlet.boundingSphereRadius, d); + } + + data.meshlets.push_back(meshlet); + } + + return data; +} + +std::vector<uint32_t> assetLoaderIndicesTo32BitIndices(const std::vector<uint8_t>& indexData, vkcv::asset::IndexType indexType) { + std::vector<uint32_t> indices; + if (indexType == vkcv::asset::IndexType::UINT16) { + for (int i = 0; i < indexData.size(); i += 2) { + const uint16_t index16Bit = *reinterpret_cast<const uint16_t *>(&(indexData[i])); + const uint32_t index32Bit = static_cast<uint32_t>(index16Bit); + indices.push_back(index32Bit); + } + } else if (indexType == vkcv::asset::IndexType::UINT32) { + for (int i = 0; i < indexData.size(); i += 4) { + const uint32_t index32Bit = *reinterpret_cast<const uint32_t *>(&(indexData[i])); + indices.push_back(index32Bit); + } + } else { + vkcv_log(vkcv::LogLevel::ERROR, "Unsupported index type"); + } + return indices; +} +} \ No newline at end of file diff --git a/modules/meshlet/src/vkcv/meshlet/Tipsify.cpp b/modules/meshlet/src/vkcv/meshlet/Tipsify.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c5762100bc37eccbe3e4f6b4c94e5f0e580c53c7 --- /dev/null +++ b/modules/meshlet/src/vkcv/meshlet/Tipsify.cpp @@ -0,0 +1,288 @@ + +#include <vkcv/Logger.hpp> +#include "vkcv/meshlet/Tipsify.hpp" +#include <iostream> + +namespace vkcv::meshlet { + + const int maxUsedVertices = 128; + + /** + * modulo operation with maxUsedVertices + * @param number for modulo operation + * @return number between 0 and maxUsedVertices - 1 + */ + int mod( int number ){ + return (number + maxUsedVertices) % maxUsedVertices; + } + + /** + * searches for the next VertexIndex that was used before or returns any vertexIndex if no used was found + * @param livingTriangles + * @param usedVerticeStack + * @param usedVerticeCount + * @param usedVerticeOffset + * @param vertexCount + * @param lowestLivingVertexIndex + * @param currentTriangleIndex + * @param skippedIndices + * @return a VertexIndex to be used as fanningVertexIndex + */ + int skipDeadEnd( + const std::vector<uint8_t> &livingTriangles, + const std::vector<uint32_t> &usedVerticeStack, + int &usedVerticeCount, + int &usedVerticeOffset, + int vertexCount, + int &lowestLivingVertexIndex, + int ¤tTriangleIndex, + std::vector<uint32_t> &skippedIndices) { + + // returns the latest vertex used that has a living triangle + while (mod(usedVerticeCount) != usedVerticeOffset) { + // iterate from the latest to the oldest. + maxUsedVertices to always make it a positive number in the range 0 to maxUsedVertices -1 + int nextVertex = usedVerticeStack[mod(--usedVerticeCount)]; + + if (livingTriangles[nextVertex] > 0) { + return nextVertex; + } + } + // returns any vertexIndex since no last used has a living triangle + while (lowestLivingVertexIndex + 1 < vertexCount) { + lowestLivingVertexIndex++; + if (livingTriangles[lowestLivingVertexIndex] > 0) { + // add index of the vertex to skippedIndices + skippedIndices.push_back(static_cast<uint32_t>(currentTriangleIndex * 3)); + return lowestLivingVertexIndex; + } + } + return -1; + } + + /** + * searches for the best next candidate as a fanningVertexIndex + * @param vertexCount + * @param lowestLivingVertexIndex + * @param cacheSize + * @param possibleCandidates + * @param numPossibleCandidates + * @param lastTimestampCache + * @param currentTimeStamp + * @param livingTriangles + * @param usedVerticeStack + * @param usedVerticeCount + * @param usedVerticeOffset + * @param currentTriangleIndex + * @param skippedIndices + * @return a VertexIndex to be used as fanningVertexIndex + */ + int getNextVertexIndex(int vertexCount, + int &lowestLivingVertexIndex, + int cacheSize, + const std::vector<uint32_t> &possibleCandidates, + int numPossibleCandidates, + const std::vector<uint32_t> &lastTimestampCache, + int currentTimeStamp, + const std::vector<uint8_t> &livingTriangles, + const std::vector<uint32_t> &usedVerticeStack, + int &usedVerticeCount, + int &usedVerticeOffset, + int ¤tTriangleIndex, + std::vector<uint32_t> &skippedIndices) { + int nextVertexIndex = -1; + int maxPriority = -1; + // calculates the next possibleCandidates that is recently used + for (int j = 0; j < numPossibleCandidates; j++) { + int vertexIndex = possibleCandidates[j]; + + // the candidate needs to be not fanned out yet + if (livingTriangles[vertexIndex] > 0) { + int priority = -1; + + // prioritizes recent used vertices, but tries not to pick one that has many triangles -> fills holes better + if ( currentTimeStamp - lastTimestampCache[vertexIndex] + 2 * livingTriangles[vertexIndex] <= + cacheSize) { + priority = currentTimeStamp - lastTimestampCache[vertexIndex]; + } + // select the vertexIndex with the highest priority + if (priority > maxPriority) { + maxPriority = priority; + nextVertexIndex = vertexIndex; + } + } + } + + // if no candidate is alive, try and find another one + if (nextVertexIndex == -1) { + nextVertexIndex = skipDeadEnd( + livingTriangles, + usedVerticeStack, + usedVerticeCount, + usedVerticeOffset, + vertexCount, + lowestLivingVertexIndex, + currentTriangleIndex, + skippedIndices); + } + return nextVertexIndex; + } + + VertexCacheReorderResult tipsifyMesh( + const std::vector<uint32_t> &indexBuffer32Bit, + const int vertexCount, + const unsigned int cacheSize) { + + if (indexBuffer32Bit.empty() || vertexCount <= 0) { + vkcv_log(LogLevel::ERROR, "Invalid Input."); + return VertexCacheReorderResult(indexBuffer32Bit , {}); + } + int triangleCount = indexBuffer32Bit.size() / 3; + + // dynamic array for vertexOccurrence + std::vector<uint8_t> vertexOccurrence(vertexCount, 0); + // count the occurrence of a vertex in all among all triangles + for (size_t i = 0; i < triangleCount * 3; i++) { + vertexOccurrence[indexBuffer32Bit[i]]++; + } + + int sum = 0; + std::vector<uint32_t> offsetVertexOccurrence(vertexCount + 1, 0); + // highest offset for later iteration + int maxOffset = 0; + // calculate the offset of each vertex from the start + for (int i = 0; i < vertexCount; i++) { + offsetVertexOccurrence[i] = sum; + sum += vertexOccurrence[i]; + + if (vertexOccurrence[i] > maxOffset) { + maxOffset = vertexOccurrence[i]; + } + // reset for reuse + vertexOccurrence[i] = 0; + } + offsetVertexOccurrence[vertexCount] = sum; + + // vertexIndexToTriangle = which vertex belongs to which triangle + std::vector<uint32_t> vertexIndexToTriangle(3 * triangleCount, 0); + // vertexOccurrence functions as number of usages in all triangles + // lowestLivingVertexIndex = number of a triangle + for (int i = 0; i < triangleCount; i++) { + // get the pointer to the first vertex of the triangle + // this allows us to iterate over the indexBuffer with the first vertex of the triangle as start + const uint32_t *vertexIndexOfTriangle = &indexBuffer32Bit[i * 3]; + + vertexIndexToTriangle[offsetVertexOccurrence[vertexIndexOfTriangle[0]] + vertexOccurrence[vertexIndexOfTriangle[0]]] = i; + vertexOccurrence[vertexIndexOfTriangle[0]]++; + + vertexIndexToTriangle[offsetVertexOccurrence[vertexIndexOfTriangle[1]] + vertexOccurrence[vertexIndexOfTriangle[1]]] = i; + vertexOccurrence[vertexIndexOfTriangle[1]]++; + + vertexIndexToTriangle[offsetVertexOccurrence[vertexIndexOfTriangle[2]] + vertexOccurrence[vertexIndexOfTriangle[2]]] = i; + vertexOccurrence[vertexIndexOfTriangle[2]]++; + } + + // counts if a triangle still uses this vertex + std::vector<uint8_t> livingVertices = vertexOccurrence; + std::vector<uint32_t> lastTimestampCache(vertexCount, 0); + + // stack of already used vertices, if it'currentTimeStamp full it will write to 0 again + std::vector<uint32_t> usedVerticeStack(maxUsedVertices, 0); + + //currently used vertices + int usedVerticeCount = 0; + // offset if maxUsedVertices was reached and it loops back to 0 + int usedVerticeOffset = 0; + + // saves if a triangle was emitted (used in the IndexBuffer) + std::vector<bool> isEmittedTriangles(triangleCount, false); + + // reordered Triangles that get rewritten to the new IndexBuffer + std::vector<uint32_t> reorderedTriangleIndexBuffer(triangleCount, 0); + + // offset to the latest not used triangleIndex + int triangleOutputOffset = 0; + // vertexIndex to fan out from (fanning VertexIndex) + int currentVertexIndex = 0; + int currentTimeStamp = cacheSize + 1; + int lowestLivingVertexIndex = 0; + + std::vector<uint32_t> possibleCandidates(3 * maxOffset); + + int currentTriangleIndex = 0; + // list of vertex indices where a deadEnd was reached + // useful to know where the mesh is potentially not contiguous + std::vector<uint32_t> skippedIndices; + + // run while not all indices are fanned out, -1 equals all are fanned out + while (currentVertexIndex >= 0) { + // number of possible candidates for a fanning VertexIndex + int numPossibleCandidates = 0; + // offset of currentVertexIndex and the next VertexIndex + int startOffset = offsetVertexOccurrence[currentVertexIndex]; + int endOffset = offsetVertexOccurrence[currentVertexIndex + 1]; + // iterates over every triangle of currentVertexIndex + for (int offset = startOffset; offset < endOffset; offset++) { + int triangleIndex = vertexIndexToTriangle[offset]; + + // checks if the triangle is already emitted + if (!isEmittedTriangles[triangleIndex]) { + + // get the pointer to the first vertex of the triangle + // this allows us to iterate over the indexBuffer with the first vertex of the triangle as start + const uint32_t *vertexIndexOfTriangle = &indexBuffer32Bit[3 * triangleIndex]; + + currentTriangleIndex++; + + // save emitted vertexIndexOfTriangle to reorderedTriangleIndexBuffer and set it to emitted + reorderedTriangleIndexBuffer[triangleOutputOffset++] = triangleIndex; + isEmittedTriangles[triangleIndex] = true; + + // save all vertexIndices of the triangle to reuse as soon as possible + for (int j = 0; j < 3; j++) { + int vertexIndex = vertexIndexOfTriangle[j]; + + //save vertexIndex to reuseStack + usedVerticeStack[mod(usedVerticeCount++)] = vertexIndex; + + // after looping back increase the start, so it only overrides the oldest vertexIndex + if ((mod(usedVerticeCount)) == + (mod(usedVerticeOffset))) { + usedVerticeOffset = mod(usedVerticeOffset + 1); + } + // add vertex to next possibleCandidates as fanning vertex + possibleCandidates[numPossibleCandidates++] = vertexIndex; + + // remove one occurrence of the vertex, since the triangle is used + livingVertices[vertexIndex]--; + + // writes the timestamp (number of iteration) of the last usage, if it wasn't used within the last cacheSize iterations + if (currentTimeStamp - lastTimestampCache[vertexIndex] > cacheSize) { + lastTimestampCache[vertexIndex] = currentTimeStamp; + currentTimeStamp++; + } + } + } + } + + // search for the next vertexIndex to fan out + currentVertexIndex = getNextVertexIndex( + vertexCount, lowestLivingVertexIndex, cacheSize, possibleCandidates, numPossibleCandidates, lastTimestampCache, currentTimeStamp, + livingVertices, usedVerticeStack, usedVerticeCount, usedVerticeOffset, currentTriangleIndex, skippedIndices); + } + + std::vector<uint32_t> reorderedIndexBuffer(3 * triangleCount); + + triangleOutputOffset = 0; + // rewriting the TriangleIndexBuffer to the new IndexBuffer + for (int i = 0; i < triangleCount; i++) { + int triangleIndex = reorderedTriangleIndexBuffer[i]; + // rewriting the triangle index to vertices + for (int j = 0; j < 3; j++) { + int vertexIndex = indexBuffer32Bit[(3 * triangleIndex) + j]; + reorderedIndexBuffer[triangleOutputOffset++] = vertexIndex; + } + } + + return VertexCacheReorderResult(reorderedIndexBuffer, skippedIndices); + } +} \ No newline at end of file diff --git a/modules/scene/CMakeLists.txt b/modules/scene/CMakeLists.txt index 9aa76883a260d26aa6f46d6dabdc8206e4dad387..5edf9a29ad929b3c07b79d4f1ffcb7f1cf2fcd99 100644 --- a/modules/scene/CMakeLists.txt +++ b/modules/scene/CMakeLists.txt @@ -13,7 +13,7 @@ set(vkcv_scene_sources ${vkcv_scene_include}/vkcv/scene/Bounds.hpp ${vkcv_scene_source}/vkcv/scene/Bounds.cpp - ${vkcv_scene_source}/vkcv/scene/Frustum.hpp + ${vkcv_scene_include}/vkcv/scene/Frustum.hpp ${vkcv_scene_source}/vkcv/scene/Frustum.cpp ${vkcv_scene_include}/vkcv/scene/MeshPart.hpp @@ -21,7 +21,7 @@ set(vkcv_scene_sources ${vkcv_scene_include}/vkcv/scene/Mesh.hpp ${vkcv_scene_source}/vkcv/scene/Mesh.cpp - + ${vkcv_scene_include}/vkcv/scene/Node.hpp ${vkcv_scene_source}/vkcv/scene/Node.cpp @@ -42,4 +42,4 @@ target_include_directories(vkcv_scene SYSTEM BEFORE PRIVATE ${vkcv_include} ${vk target_include_directories(vkcv_scene BEFORE PUBLIC ${vkcv_scene_include}) # linking with libraries from all dependencies and the VkCV framework -target_link_libraries(vkcv_scene vkcv vkcv_asset_loader vkcv_material vkcv_camera) \ No newline at end of file +target_link_libraries(vkcv_scene vkcv vkcv_asset_loader vkcv_material vkcv_camera) diff --git a/modules/scene/src/vkcv/scene/Frustum.hpp b/modules/scene/include/vkcv/scene/Frustum.hpp similarity index 100% rename from modules/scene/src/vkcv/scene/Frustum.hpp rename to modules/scene/include/vkcv/scene/Frustum.hpp diff --git a/modules/scene/src/vkcv/scene/Frustum.cpp b/modules/scene/src/vkcv/scene/Frustum.cpp index c800bb1e4baf4d0feef33c073740fb211da7bf63..1f63eb1d07002d24add81872627777048642dcdb 100644 --- a/modules/scene/src/vkcv/scene/Frustum.cpp +++ b/modules/scene/src/vkcv/scene/Frustum.cpp @@ -1,5 +1,5 @@ -#include "Frustum.hpp" +#include "vkcv/scene/Frustum.hpp" namespace vkcv::scene { diff --git a/modules/scene/src/vkcv/scene/Mesh.cpp b/modules/scene/src/vkcv/scene/Mesh.cpp index 53fb81713ed7e14049a21cb91c771d67f2f7086c..af02aedbd71ba4bdfcc30aa7fdcd82796af904f1 100644 --- a/modules/scene/src/vkcv/scene/Mesh.cpp +++ b/modules/scene/src/vkcv/scene/Mesh.cpp @@ -1,7 +1,7 @@ #include "vkcv/scene/Mesh.hpp" #include "vkcv/scene/Scene.hpp" -#include "Frustum.hpp" +#include "vkcv/scene/Frustum.hpp" namespace vkcv::scene { diff --git a/modules/scene/src/vkcv/scene/Node.cpp b/modules/scene/src/vkcv/scene/Node.cpp index 32230099b2f693362bab69d8172a4dee56c4e304..24f62d18e160c7d80f82384829a2130737737ba9 100644 --- a/modules/scene/src/vkcv/scene/Node.cpp +++ b/modules/scene/src/vkcv/scene/Node.cpp @@ -1,7 +1,7 @@ #include "vkcv/scene/Node.hpp" #include "vkcv/scene/Scene.hpp" -#include "Frustum.hpp" +#include "vkcv/scene/Frustum.hpp" #include <algorithm> diff --git a/modules/shader_compiler/CMakeLists.txt b/modules/shader_compiler/CMakeLists.txt index 4b674ec41ed4ea5f42dc73187c212e6a69952cec..6fee42bfb571168cd2371e21e231ce417efa41f0 100644 --- a/modules/shader_compiler/CMakeLists.txt +++ b/modules/shader_compiler/CMakeLists.txt @@ -10,6 +10,9 @@ 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/Compiler.hpp + ${vkcv_shader_compiler_source}/vkcv/shader/Compiler.cpp + ${vkcv_shader_compiler_include}/vkcv/shader/GLSLCompiler.hpp ${vkcv_shader_compiler_source}/vkcv/shader/GLSLCompiler.cpp ) diff --git a/modules/shader_compiler/include/vkcv/shader/Compiler.hpp b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp index d7b7af7178531aea358cecbc8b86a29527173014..5b119ca5c68f997bacfbea6c60d5c965f9a7a54e 100644 --- a/modules/shader_compiler/include/vkcv/shader/Compiler.hpp +++ b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp @@ -1,6 +1,11 @@ #pragma once +#include <filesystem> +#include <string> +#include <unordered_map> + #include <vkcv/Event.hpp> +#include <vkcv/ShaderStage.hpp> namespace vkcv::shader { @@ -8,10 +13,21 @@ namespace vkcv::shader { class Compiler { private: + protected: + std::unordered_map<std::string, std::string> m_defines; + public: + virtual bool compileSource(ShaderStage shaderStage, const char* shaderSource, + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath) = 0; + virtual void compile(ShaderStage shaderStage, const std::filesystem::path& shaderPath, - const ShaderCompiledFunction& compiled, bool update = false) = 0; + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath, bool update) = 0; + + std::string getDefine(const std::string& name) const; + void setDefine(const std::string& name, const std::string& value); }; } diff --git a/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp b/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp index 7105d93a0c3e153bf3abe1d624d0c13c6f09ac6d..eca84def118625e21df1c645cfc71b6bcddf7393 100644 --- a/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp +++ b/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp @@ -7,7 +7,7 @@ namespace vkcv::shader { - class GLSLCompiler { + class GLSLCompiler : public Compiler { private: public: GLSLCompiler(); @@ -20,8 +20,13 @@ namespace vkcv::shader { GLSLCompiler& operator=(const GLSLCompiler& other); GLSLCompiler& operator=(GLSLCompiler&& other) = default; + bool compileSource(ShaderStage shaderStage, const char* shaderSource, + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath); + void compile(ShaderStage shaderStage, const std::filesystem::path& shaderPath, - const ShaderCompiledFunction& compiled, bool update = false); + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath = "", bool update = false) override; }; diff --git a/modules/shader_compiler/src/vkcv/shader/Compiler.cpp b/modules/shader_compiler/src/vkcv/shader/Compiler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f5ec0435ca8b82dc5f328921f43a39338d1be456 --- /dev/null +++ b/modules/shader_compiler/src/vkcv/shader/Compiler.cpp @@ -0,0 +1,14 @@ + +#include "vkcv/shader/Compiler.hpp" + +namespace vkcv::shader { + + std::string Compiler::getDefine(const std::string &name) const { + return m_defines.at(name); + } + + void Compiler::setDefine(const std::string &name, const std::string &value) { + m_defines[name] = value; + } + +} diff --git a/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp index ec358188b8e871da6f4d62ffd397f32bfb795ee2..c8878513bf99054e357f1b076dfe12664be763b3 100644 --- a/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp +++ b/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp @@ -2,16 +2,18 @@ #include "vkcv/shader/GLSLCompiler.hpp" #include <fstream> +#include <strstream> #include <glslang/SPIRV/GlslangToSpv.h> #include <glslang/StandAlone/DirStackFileIncluder.h> +#include <vkcv/File.hpp> #include <vkcv/Logger.hpp> namespace vkcv::shader { static uint32_t s_CompilerCount = 0; - GLSLCompiler::GLSLCompiler() { + GLSLCompiler::GLSLCompiler() : Compiler() { if (s_CompilerCount == 0) { glslang::InitializeProcess(); } @@ -19,7 +21,7 @@ namespace vkcv::shader { s_CompilerCount++; } - GLSLCompiler::GLSLCompiler(const GLSLCompiler &other) { + GLSLCompiler::GLSLCompiler(const GLSLCompiler &other) : Compiler(other) { s_CompilerCount++; } @@ -50,6 +52,10 @@ namespace vkcv::shader { return EShLangFragment; case ShaderStage::COMPUTE: return EShLangCompute; + case ShaderStage::TASK: + return EShLangTaskNV; + case ShaderStage::MESH: + return EShLangMeshNV; default: return EShLangCount; } @@ -197,22 +203,45 @@ namespace vkcv::shader { return true; } - void GLSLCompiler::compile(ShaderStage shaderStage, const std::filesystem::path &shaderPath, - const ShaderCompiledFunction& compiled, bool update) { + bool GLSLCompiler::compileSource(ShaderStage shaderStage, const char* shaderSource, + const ShaderCompiledFunction &compiled, + const std::filesystem::path& includePath) { const EShLanguage language = findShaderLanguage(shaderStage); if (language == EShLangCount) { - vkcv_log(LogLevel::ERROR, "Shader stage not supported (%s)", shaderPath.string().c_str()); - return; + vkcv_log(LogLevel::ERROR, "Shader stage not supported"); + return false; } - const std::vector<char> code = readShaderCode(shaderPath); - glslang::TShader shader (language); glslang::TProgram program; + std::string source (shaderSource); + + if (!m_defines.empty()) { + std::strstream defines; + for (const auto& define : m_defines) { + defines << "#define " << define.first << " " << define.second << std::endl; + } + + defines << '\0'; + + size_t pos = source.find("#version") + 8; + if (pos >= source.length()) { + pos = 0; + } + + const size_t epos = source.find_last_of("#extension", pos) + 10; + if (epos < source.length()) { + pos = epos; + } + + pos = source.find('\n', pos) + 1; + source = source.insert(pos, defines.str()); + } + const char *shaderStrings [1]; - shaderStrings[0] = code.data(); + shaderStrings[0] = source.c_str(); shader.setStrings(shaderStrings, 1); @@ -222,51 +251,53 @@ namespace vkcv::shader { const auto messages = (EShMessages)( EShMsgSpvRules | EShMsgVulkanRules - ); + ); std::string preprocessedGLSL; DirStackFileIncluder includer; - includer.pushExternalLocalDirectory(shaderPath.parent_path().string()); + includer.pushExternalLocalDirectory(includePath.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; + if (!shader.preprocess(&resources, 100, ENoProfile, + false, false, + messages, &preprocessedGLSL, includer)) { + vkcv_log(LogLevel::ERROR, "Shader preprocessing failed {\n%s\n%s\n}", + shader.getInfoLog(), shader.getInfoDebugLog()); + return false; } 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; + vkcv_log(LogLevel::ERROR, "Shader parsing failed {\n%s\n%s\n}", + shader.getInfoLog(), shader.getInfoDebugLog()); + return false; } 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; + vkcv_log(LogLevel::ERROR, "Shader linking failed {\n%s\n%s\n}", + shader.getInfoLog(), shader.getInfoDebugLog()); + return false; } const glslang::TIntermediate* intermediate = program.getIntermediate(language); if (!intermediate) { - vkcv_log(LogLevel::ERROR, "No valid intermediate representation (%s)", shaderPath.string().c_str()); - return; + vkcv_log(LogLevel::ERROR, "No valid intermediate representation"); + return false; } std::vector<uint32_t> spirv; glslang::GlslangToSpv(*intermediate, spirv); - const std::filesystem::path tmp_path (std::tmpnam(nullptr)); + const std::filesystem::path tmp_path = generateTemporaryFilePath(); if (!writeSpirvCode(tmp_path, spirv)) { - vkcv_log(LogLevel::ERROR, "Spir-V could not be written to disk (%s)", shaderPath.string().c_str()); - return; + vkcv_log(LogLevel::ERROR, "Spir-V could not be written to disk"); + return false; } if (compiled) { @@ -274,6 +305,24 @@ namespace vkcv::shader { } std::filesystem::remove(tmp_path); + return true; + } + + void GLSLCompiler::compile(ShaderStage shaderStage, const std::filesystem::path &shaderPath, + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath, bool update) { + const std::vector<char> code = readShaderCode(shaderPath); + bool result; + + if (!includePath.empty()) { + result = compileSource(shaderStage, code.data(), compiled, includePath); + } else { + result = compileSource(shaderStage, code.data(), compiled, shaderPath.parent_path()); + } + + if (!result) { + vkcv_log(LogLevel::ERROR, "Shader compilation failed: (%s)", shaderPath.string().c_str()); + } if (update) { // TODO: Shader hot compilation during runtime diff --git a/modules/upscaling/CMakeLists.txt b/modules/upscaling/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..0767e5c4d2d60c001ac9d6792efcd623456284a8 --- /dev/null +++ b/modules/upscaling/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_upscaling) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_upscaling_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_upscaling_include ${PROJECT_SOURCE_DIR}/include) + +set(vkcv_upscaling_sources + ${vkcv_upscaling_include}/vkcv/upscaling/Upscaling.hpp + ${vkcv_upscaling_source}/vkcv/upscaling/Upscaling.cpp + + ${vkcv_upscaling_include}/vkcv/upscaling/BilinearUpscaling.hpp + ${vkcv_upscaling_source}/vkcv/upscaling/BilinearUpscaling.cpp + + ${vkcv_upscaling_include}/vkcv/upscaling/FSRUpscaling.hpp + ${vkcv_upscaling_source}/vkcv/upscaling/FSRUpscaling.cpp +) + +# Setup some path variables to load libraries +set(vkcv_upscaling_lib lib) +set(vkcv_upscaling_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_upscaling_lib}) + +# Check and load FidelityFX_FSR +include(config/FidelityFX_FSR.cmake) + +# adding source files to the project +add_library(vkcv_upscaling STATIC ${vkcv_upscaling_sources}) + +# link the required libraries to the module +target_link_libraries(vkcv_upscaling ${vkcv_upscaling_libraries} vkcv vkcv_shader_compiler) + +# including headers of dependencies and the VkCV framework +target_include_directories(vkcv_upscaling SYSTEM BEFORE PRIVATE ${vkcv_upscaling_includes} ${vkcv_include} ${vkcv_shader_compiler_include}) + +# add the own include directory for public headers +target_include_directories(vkcv_upscaling BEFORE PUBLIC ${vkcv_upscaling_include}) diff --git a/modules/upscaling/config/FidelityFX_FSR.cmake b/modules/upscaling/config/FidelityFX_FSR.cmake new file mode 100644 index 0000000000000000000000000000000000000000..cc52b4189f781f534a933feb7b782b6bec333e5a --- /dev/null +++ b/modules/upscaling/config/FidelityFX_FSR.cmake @@ -0,0 +1,18 @@ + +if (EXISTS "${vkcv_upscaling_lib_path}/FidelityFX-FSR") + include_shader(${vkcv_upscaling_lib_path}/FidelityFX-FSR/ffx-fsr/ffx_a.h ${vkcv_upscaling_include} ${vkcv_upscaling_source}) + include_shader(${vkcv_upscaling_lib_path}/FidelityFX-FSR/ffx-fsr/ffx_fsr1.h ${vkcv_upscaling_include} ${vkcv_upscaling_source}) + include_shader(${vkcv_upscaling_lib_path}/FidelityFX-FSR/sample/src/VK/FSR_Pass.glsl ${vkcv_upscaling_include} ${vkcv_upscaling_source}) + + list(APPEND vkcv_upscaling_includes ${vkcv_upscaling_lib}/FidelityFX-FSR/ffx-fsr) + + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/ffx_a.h.cxx) + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/ffx_fsr1.h.cxx) + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/FSR_Pass.glsl.cxx) + + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/ffx_a.h.hxx) + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/ffx_fsr1.h.hxx) + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/FSR_Pass.glsl.hxx) +else() + message(WARNING "FidelityFX-FSR is required..! Update the submodules!") +endif () diff --git a/modules/upscaling/include/vkcv/upscaling/BilinearUpscaling.hpp b/modules/upscaling/include/vkcv/upscaling/BilinearUpscaling.hpp new file mode 100644 index 0000000000000000000000000000000000000000..52124dc8e36bee7ef7c00de6afcf3457296a7623 --- /dev/null +++ b/modules/upscaling/include/vkcv/upscaling/BilinearUpscaling.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "Upscaling.hpp" + +namespace vkcv::upscaling { + + class BilinearUpscaling : public Upscaling { + private: + public: + BilinearUpscaling(Core& core); + + void recordUpscaling(const CommandStreamHandle& cmdStream, + const ImageHandle& input, + const ImageHandle& output) override; + + }; + +} diff --git a/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2a1338b85e3ee60a33215157aaaa15817f2db97f --- /dev/null +++ b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include "Upscaling.hpp" + +#include <vkcv/ShaderProgram.hpp> + +namespace vkcv::upscaling { + + enum class FSRQualityMode : int { + NONE = 0, + ULTRA_QUALITY = 1, + QUALITY = 2, + BALANCED = 3, + PERFORMANCE = 4 + }; + + void getFSRResolution(FSRQualityMode mode, + uint32_t outputWidth, uint32_t outputHeight, + uint32_t &inputWidth, uint32_t &inputHeight); + + float getFSRLodBias(FSRQualityMode mode); + + struct FSRConstants { + uint32_t Const0 [4]; + uint32_t Const1 [4]; + uint32_t Const2 [4]; + uint32_t Const3 [4]; + uint32_t Sample [4]; + }; + + class FSRUpscaling : public Upscaling { + private: + PipelineHandle m_easuPipeline; + PipelineHandle m_rcasPipeline; + + DescriptorSetHandle m_easuDescriptorSet; + DescriptorSetHandle m_rcasDescriptorSet; + + Buffer<FSRConstants> m_easuConstants; + Buffer<FSRConstants> m_rcasConstants; + ImageHandle m_intermediateImage; + SamplerHandle m_sampler; + + bool m_hdr; + + /** + * Sharpness will calculate the rcasAttenuation value + * which should be between 0.0f and 2.0f (default: 0.25f). + * + * rcasAttenuation = (1.0f - sharpness) * 2.0f + * + * So the default value for sharpness should be 0.875f. + * + * Beware that 0.0f or any negative value of sharpness will + * disable the rcas pass completely. + */ + float m_sharpness; + + public: + explicit FSRUpscaling(Core& core); + + void recordUpscaling(const CommandStreamHandle& cmdStream, + const ImageHandle& input, + const ImageHandle& output) override; + + [[nodiscard]] + bool isHdrEnabled() const; + + void setHdrEnabled(bool enabled); + + [[nodiscard]] + float getSharpness() const; + + void setSharpness(float sharpness); + + }; + +} diff --git a/modules/upscaling/include/vkcv/upscaling/Upscaling.hpp b/modules/upscaling/include/vkcv/upscaling/Upscaling.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c44e878ad78f0a3359599c76f781371505fd3a85 --- /dev/null +++ b/modules/upscaling/include/vkcv/upscaling/Upscaling.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include <vkcv/Core.hpp> +#include <vkcv/Handles.hpp> + +namespace vkcv::upscaling { + + class Upscaling { + protected: + Core& m_core; + + public: + Upscaling(Core& core); + + ~Upscaling() = default; + + virtual void recordUpscaling(const CommandStreamHandle& cmdStream, + const ImageHandle& input, + const ImageHandle& output) = 0; + + }; + +} diff --git a/modules/upscaling/lib/FidelityFX-FSR b/modules/upscaling/lib/FidelityFX-FSR new file mode 160000 index 0000000000000000000000000000000000000000..bcffc8171efb80e265991301a49670ed755088dd --- /dev/null +++ b/modules/upscaling/lib/FidelityFX-FSR @@ -0,0 +1 @@ +Subproject commit bcffc8171efb80e265991301a49670ed755088dd diff --git a/modules/upscaling/src/vkcv/upscaling/BilinearUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/BilinearUpscaling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c36acf5d050e3f4f19223020357b6c32534a2de --- /dev/null +++ b/modules/upscaling/src/vkcv/upscaling/BilinearUpscaling.cpp @@ -0,0 +1,13 @@ + +#include "vkcv/upscaling/BilinearUpscaling.hpp" + +namespace vkcv::upscaling { + + BilinearUpscaling::BilinearUpscaling(Core &core) : Upscaling(core) {} + + void BilinearUpscaling::recordUpscaling(const CommandStreamHandle &cmdStream, const ImageHandle &input, + const ImageHandle &output) { + m_core.recordBlitImage(cmdStream, input, output, SamplerFilterType::LINEAR); + } + +} diff --git a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..460a6d0b459fe7d1d2a917a62138fea2e5a40908 --- /dev/null +++ b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp @@ -0,0 +1,382 @@ + +#include "vkcv/upscaling/FSRUpscaling.hpp" + +#include <stdint.h> +#include <math.h> + +#define A_CPU 1 +#include <ffx_a.h> +#include <ffx_fsr1.h> + +#include "ffx_a.h.hxx" +#include "ffx_fsr1.h.hxx" +#include "FSR_Pass.glsl.hxx" + +#include <vkcv/File.hpp> +#include <vkcv/Logger.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> + +namespace vkcv::upscaling { + + void getFSRResolution(FSRQualityMode mode, + uint32_t outputWidth, uint32_t outputHeight, + uint32_t &inputWidth, uint32_t &inputHeight) { + float scale; + + switch (mode) { + case FSRQualityMode::ULTRA_QUALITY: + scale = 1.3f; + break; + case FSRQualityMode::QUALITY: + scale = 1.5f; + break; + case FSRQualityMode::BALANCED: + scale = 1.7f; + break; + case FSRQualityMode::PERFORMANCE: + scale = 2.0f; + break; + default: + scale = 1.0f; + break; + } + + inputWidth = static_cast<uint32_t>( + std::round(static_cast<float>(outputWidth) / scale) + ); + + inputHeight = static_cast<uint32_t>( + std::round(static_cast<float>(outputHeight) / scale) + ); + } + + float getFSRLodBias(FSRQualityMode mode) { + switch (mode) { + case FSRQualityMode::ULTRA_QUALITY: + return -0.38f; + case FSRQualityMode::QUALITY: + return -0.58f; + case FSRQualityMode::BALANCED: + return -0.79f; + case FSRQualityMode::PERFORMANCE: + return -1.0f; + default: + return 0.0f; + } + } + + static std::vector<DescriptorBinding> getDescriptorBindings() { + return std::vector<DescriptorBinding>({ + DescriptorBinding( + 0, DescriptorType::UNIFORM_BUFFER_DYNAMIC, + 1, ShaderStage::COMPUTE + ), + DescriptorBinding( + 1, DescriptorType::IMAGE_SAMPLED, + 1, ShaderStage::COMPUTE + ), + DescriptorBinding( + 2, DescriptorType::IMAGE_STORAGE, + 1, ShaderStage::COMPUTE + ), + DescriptorBinding( + 3, DescriptorType::SAMPLER, + 1, ShaderStage::COMPUTE + ) + }); + } + + template<typename T> + bool checkFeatures(const vk::BaseInStructure* base, vk::StructureType type, bool (*check)(const T& features)) { + if (base->sType == type) { + return check(*reinterpret_cast<const T*>(base)); + } else + if (base->pNext) { + return checkFeatures<T>(base->pNext, type, check); + } else { + return false; + } + } + + static bool checkFloat16(const vk::PhysicalDeviceFloat16Int8FeaturesKHR& features) { + return features.shaderFloat16; + } + + static bool check16Storage(const vk::PhysicalDevice16BitStorageFeaturesKHR& features) { + return features.storageBuffer16BitAccess; + } + + static bool writeShaderCode(const std::filesystem::path &shaderPath, const std::string& code) { + std::ofstream file (shaderPath.string(), std::ios::out); + + if (!file.is_open()) { + vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str()); + return false; + } + + file.seekp(0); + file.write(code.c_str(), static_cast<std::streamsize>(code.length())); + file.close(); + + return true; + } + + static bool compileFSRShader(vkcv::shader::GLSLCompiler& compiler, + const shader::ShaderCompiledFunction& compiled) { + std::filesystem::path directory = generateTemporaryDirectoryPath(); + + if (!std::filesystem::create_directory(directory)) { + vkcv_log(LogLevel::ERROR, "The directory could not be created (%s)", directory.string().c_str()); + return false; + } + + if (!writeShaderCode(directory / "ffx_a.h", FFX_A_H_SHADER)) { + return false; + } + + if (!writeShaderCode(directory / "ffx_fsr1.h", FFX_FSR1_H_SHADER)) { + return false; + } + + return compiler.compileSource(vkcv::ShaderStage::COMPUTE, + FSR_PASS_GLSL_SHADER.c_str(), + [&directory, &compiled] (vkcv::ShaderStage shaderStage, + const std::filesystem::path& path) { + if (compiled) { + compiled(shaderStage, path); + } + + std::filesystem::remove_all(directory); + }, directory + ); + } + + FSRUpscaling::FSRUpscaling(Core& core) : + Upscaling(core), + m_easuPipeline(), + m_rcasPipeline(), + m_easuDescriptorSet(m_core.createDescriptorSet(getDescriptorBindings())), + m_rcasDescriptorSet(m_core.createDescriptorSet(getDescriptorBindings())), + m_easuConstants(m_core.createBuffer<FSRConstants>( + BufferType::UNIFORM,1, + BufferMemoryType::HOST_VISIBLE + )), + m_rcasConstants(m_core.createBuffer<FSRConstants>( + BufferType::UNIFORM,1, + BufferMemoryType::HOST_VISIBLE + )), + m_intermediateImage(), + m_sampler(m_core.createSampler( + SamplerFilterType::LINEAR, + SamplerFilterType::LINEAR, + SamplerMipmapMode::NEAREST, + SamplerAddressMode::CLAMP_TO_EDGE + )), + m_hdr(false), + m_sharpness(0.875f) { + vkcv::shader::GLSLCompiler easuCompiler; + vkcv::shader::GLSLCompiler rcasCompiler; + + const auto& features = m_core.getContext().getPhysicalDevice().getFeatures2(); + const bool float16Support = ( + checkFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>( + reinterpret_cast<const vk::BaseInStructure*>(&features), + vk::StructureType::ePhysicalDeviceShaderFloat16Int8FeaturesKHR, + checkFloat16 + ) && + checkFeatures<vk::PhysicalDevice16BitStorageFeaturesKHR>( + reinterpret_cast<const vk::BaseInStructure*>(&features), + vk::StructureType::ePhysicalDevice16BitStorageFeaturesKHR, + check16Storage + ) + ) || (true); // check doesn't work because chain is empty + + if (!float16Support) { + easuCompiler.setDefine("SAMPLE_SLOW_FALLBACK", "1"); + rcasCompiler.setDefine("SAMPLE_SLOW_FALLBACK", "1"); + } + + easuCompiler.setDefine("SAMPLE_EASU", "1"); + rcasCompiler.setDefine("SAMPLE_RCAS", "1"); + + { + ShaderProgram program; + compileFSRShader(easuCompiler, [&program](vkcv::ShaderStage shaderStage, + const std::filesystem::path& path) { + program.addShader(shaderStage, path); + }); + + m_easuPipeline = m_core.createComputePipeline(program, { + m_core.getDescriptorSet(m_easuDescriptorSet).layout + }); + + DescriptorWrites writes; + writes.uniformBufferWrites.emplace_back( + 0, m_easuConstants.getHandle(),true + ); + + writes.samplerWrites.emplace_back(3, m_sampler); + + m_core.writeDescriptorSet(m_easuDescriptorSet, writes); + } + + { + ShaderProgram program; + compileFSRShader(rcasCompiler, [&program](vkcv::ShaderStage shaderStage, + const std::filesystem::path& path) { + program.addShader(shaderStage, path); + }); + + m_rcasPipeline = m_core.createComputePipeline(program, { + m_core.getDescriptorSet(m_rcasDescriptorSet).layout + }); + + DescriptorWrites writes; + writes.uniformBufferWrites.emplace_back( + 0, m_rcasConstants.getHandle(),true + ); + + writes.samplerWrites.emplace_back(3, m_sampler); + + m_core.writeDescriptorSet(m_rcasDescriptorSet, writes); + } + } + + void FSRUpscaling::recordUpscaling(const CommandStreamHandle& cmdStream, + const ImageHandle& input, + const ImageHandle& output) { + const uint32_t inputWidth = m_core.getImageWidth(input); + const uint32_t inputHeight = m_core.getImageHeight(input); + + const uint32_t outputWidth = m_core.getImageWidth(output); + const uint32_t outputHeight = m_core.getImageHeight(output); + + if ((!m_intermediateImage) || + (outputWidth != m_core.getImageWidth(m_intermediateImage)) || + (outputHeight != m_core.getImageHeight(m_intermediateImage))) { + m_intermediateImage = m_core.createImage( + m_core.getImageFormat(output), + outputWidth, outputHeight,1, + false, + true + ).getHandle(); + + m_core.prepareImageForStorage(cmdStream, m_intermediateImage); + } + + const bool rcasEnabled = ( + (m_sharpness > +0.0f) && + ((inputWidth < outputWidth) || (inputHeight < outputHeight)) + ); + + { + FSRConstants consts = {}; + + FsrEasuCon( + consts.Const0, consts.Const1, consts.Const2, consts.Const3, + static_cast<AF1>(inputWidth), static_cast<AF1>(inputHeight), + static_cast<AF1>(inputWidth), static_cast<AF1>(inputHeight), + static_cast<AF1>(outputWidth), static_cast<AF1>(outputHeight) + ); + + consts.Sample[0] = (((m_hdr) && (!rcasEnabled)) ? 1 : 0); + + m_easuConstants.fill(&consts); + } + + static const uint32_t threadGroupWorkRegionDim = 16; + + uint32_t dispatch[3]; + dispatch[0] = (outputWidth + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + dispatch[1] = (outputHeight + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; + dispatch[2] = 1; + + m_core.recordBufferMemoryBarrier(cmdStream, m_easuConstants.getHandle()); + + if (rcasEnabled) { + { + DescriptorWrites writes; + writes.sampledImageWrites.emplace_back(1, input); + writes.storageImageWrites.emplace_back(2, m_intermediateImage); + + m_core.writeDescriptorSet(m_easuDescriptorSet, writes); + } + { + DescriptorWrites writes; + writes.sampledImageWrites.emplace_back(1, m_intermediateImage); + writes.storageImageWrites.emplace_back(2, output); + + m_core.writeDescriptorSet(m_rcasDescriptorSet, writes); + } + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_easuPipeline, + dispatch, + {DescriptorSetUsage(0, m_core.getDescriptorSet( + m_easuDescriptorSet + ).vulkanHandle, { 0 })}, + PushConstants(0) + ); + + { + FSRConstants consts = {}; + + FsrRcasCon(consts.Const0, (1.0f - m_sharpness) * 2.0f); + consts.Sample[0] = (m_hdr ? 1 : 0); + + m_rcasConstants.fill(&consts); + } + + m_core.recordBufferMemoryBarrier(cmdStream, m_rcasConstants.getHandle()); + m_core.prepareImageForSampling(cmdStream, m_intermediateImage); + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_rcasPipeline, + dispatch, + {DescriptorSetUsage(0, m_core.getDescriptorSet( + m_rcasDescriptorSet + ).vulkanHandle, { 0 })}, + PushConstants(0) + ); + + m_core.prepareImageForStorage(cmdStream, m_intermediateImage); + } else { + { + DescriptorWrites writes; + writes.sampledImageWrites.emplace_back(1, input); + writes.storageImageWrites.emplace_back(2, output); + + m_core.writeDescriptorSet(m_easuDescriptorSet, writes); + } + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_easuPipeline, + dispatch, + {DescriptorSetUsage(0, m_core.getDescriptorSet( + m_easuDescriptorSet + ).vulkanHandle, { 0 })}, + PushConstants(0) + ); + } + } + + bool FSRUpscaling::isHdrEnabled() const { + return m_hdr; + } + + void FSRUpscaling::setHdrEnabled(bool enabled) { + m_hdr = enabled; + } + + float FSRUpscaling::getSharpness() const { + return m_sharpness; + } + + void FSRUpscaling::setSharpness(float sharpness) { + m_sharpness = (sharpness < 0.0f ? 0.0f : (sharpness > 1.0f ? 1.0f : sharpness)); + } + +} diff --git a/modules/upscaling/src/vkcv/upscaling/Upscaling.cpp b/modules/upscaling/src/vkcv/upscaling/Upscaling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b0c3dee9b1c799c0e1f07b59b03d3ad46bd453ed --- /dev/null +++ b/modules/upscaling/src/vkcv/upscaling/Upscaling.cpp @@ -0,0 +1,8 @@ + +#include "vkcv/upscaling/Upscaling.hpp" + +namespace vkcv::upscaling { + + Upscaling::Upscaling(Core &core) : m_core(core) {} + +} diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt index 86034ffb07e7c2f40463f711d2027af8aade573f..6f179ebae0dedc1fc10060b14ce11133665a1538 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -5,4 +5,5 @@ add_subdirectory(first_mesh) add_subdirectory(first_scene) add_subdirectory(particle_simulation) add_subdirectory(voxelization) -add_subdirectory(neural_network) \ No newline at end of file +add_subdirectory(mesh_shader) +add_subdirectory(neural_network) diff --git a/projects/mesh_shader/.gitignore b/projects/mesh_shader/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..7e24fd7b853bfb0a29d8b30879ef1cb95ad141c0 --- /dev/null +++ b/projects/mesh_shader/.gitignore @@ -0,0 +1 @@ +first_triangle \ No newline at end of file diff --git a/projects/mesh_shader/CMakeLists.txt b/projects/mesh_shader/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1aa5d5ff3977a47dce75a38329216d550b1b9311 --- /dev/null +++ b/projects/mesh_shader/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.16) +project(mesh_shader) + +# 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(mesh_shader src/main.cpp) + +target_sources(mesh_shader PRIVATE) + +# this should fix the execution path to load local files from the project (for MSVC) +if(MSVC) + set_target_properties(mesh_shader PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set_target_properties(mesh_shader 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(mesh_shader PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) +endif() + +# including headers of dependencies and the VkCV framework +target_include_directories(mesh_shader SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_camera_include} ${vkcv_meshlet_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(mesh_shader vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_testing vkcv_camera vkcv_meshlet vkcv_shader_compiler vkcv_gui) \ No newline at end of file diff --git a/projects/mesh_shader/resources/Bunny/Bunny.glb b/projects/mesh_shader/resources/Bunny/Bunny.glb new file mode 100644 index 0000000000000000000000000000000000000000..181f1f92f1906e1e1ba900768580203efe19e9be --- /dev/null +++ b/projects/mesh_shader/resources/Bunny/Bunny.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8bc6fab11929ca11bdf4e892ffb03b621b10307f705cdea17d82d3dee3b9aae +size 4045836 diff --git a/projects/mesh_shader/resources/monke.glb b/projects/mesh_shader/resources/monke.glb new file mode 100644 index 0000000000000000000000000000000000000000..47d0b9131f15a8f0697318d0a47302c71cad1db8 --- /dev/null +++ b/projects/mesh_shader/resources/monke.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:597584db90a3f51088beea6652d8320e82cb025f9d3d036b89e54ad72c732a06 +size 98612 diff --git a/projects/mesh_shader/resources/shaders/common.inc b/projects/mesh_shader/resources/shaders/common.inc new file mode 100644 index 0000000000000000000000000000000000000000..280ffee215a8b8342b78d1f5558d63a05e16859b --- /dev/null +++ b/projects/mesh_shader/resources/shaders/common.inc @@ -0,0 +1,4 @@ +struct ObjectMatrices{ + mat4 model; + mat4 mvp; +}; \ No newline at end of file diff --git a/projects/mesh_shader/resources/shaders/meshlet.inc b/projects/mesh_shader/resources/shaders/meshlet.inc new file mode 100644 index 0000000000000000000000000000000000000000..0594f62ceead8ffca09b585305075eb6046f3c46 --- /dev/null +++ b/projects/mesh_shader/resources/shaders/meshlet.inc @@ -0,0 +1,8 @@ +struct Meshlet{ + uint vertexOffset; + uint vertexCount; + uint indexOffset; + uint indexCount; + vec3 meanPosition; + float boundingSphereRadius; +}; \ No newline at end of file diff --git a/projects/mesh_shader/resources/shaders/shader.frag b/projects/mesh_shader/resources/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..f4f6982f2089e6c8e102027f3b8763bb38f8e59c --- /dev/null +++ b/projects/mesh_shader/resources/shaders/shader.frag @@ -0,0 +1,32 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 passNormal; +layout(location = 1) in flat uint passTaskIndex; +layout(location = 0) out vec3 outColor; + +uint lowbias32(uint x) +{ + x ^= x >> 16; + x *= 0x7feb352dU; + x ^= x >> 15; + x *= 0x846ca68bU; + x ^= x >> 16; + return x; +} + +float hashToFloat(uint hash){ + return (hash % 255) / 255.f; +} + +vec3 colorFromIndex(uint i){ + return vec3( + hashToFloat(lowbias32(i+0)), + hashToFloat(lowbias32(i+1)), + hashToFloat(lowbias32(i+2))); +} + +void main() { + outColor = normalize(passNormal) * 0.5 + 0.5; + outColor = colorFromIndex(passTaskIndex); +} \ No newline at end of file diff --git a/projects/mesh_shader/resources/shaders/shader.mesh b/projects/mesh_shader/resources/shaders/shader.mesh new file mode 100644 index 0000000000000000000000000000000000000000..30c98610f4776204ff526c57c1f793e371194629 --- /dev/null +++ b/projects/mesh_shader/resources/shaders/shader.mesh @@ -0,0 +1,78 @@ +#version 460 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable +#extension GL_NV_mesh_shader : require + +#include "meshlet.inc" + +layout(local_size_x=32) in; + +layout(triangles) out; +layout(max_vertices=64, max_primitives=126) out; + +layout(location = 0) out vec3 passNormal[]; +layout(location = 1) out uint passTaskIndex[]; + +struct Vertex +{ + vec3 position; float padding0; + vec3 normal; float padding1; +}; + +layout(std430, binding = 0) readonly buffer vertexBuffer +{ + Vertex vertices[]; +}; + +layout(std430, binding = 1) readonly buffer indexBuffer +{ + uint localIndices[]; // breaks for 16 bit indices +}; + +layout(std430, binding = 2) readonly buffer meshletBuffer +{ + Meshlet meshlets[]; +}; + +taskNV in Task { + uint meshletIndices[32]; + mat4 mvp; +} IN; + +void main() { + + uint meshletIndex = IN.meshletIndices[gl_WorkGroupID.x]; + Meshlet meshlet = meshlets[meshletIndex]; + + // set vertices + for(uint i = 0; i < 2; i++){ + + uint workIndex = gl_LocalInvocationID.x + 32 * i; + if(workIndex >= meshlet.vertexCount){ + break; + } + + uint vertexIndex = meshlet.vertexOffset + workIndex; + Vertex vertex = vertices[vertexIndex]; + + gl_MeshVerticesNV[workIndex].gl_Position = IN.mvp * vec4(vertex.position, 1); + passNormal[workIndex] = vertex.normal; + passTaskIndex[workIndex] = meshletIndex; + } + + // set local indices + for(uint i = 0; i < 12; i++){ + + uint workIndex = gl_LocalInvocationID.x + i * 32; + if(workIndex >= meshlet.indexCount){ + break; + } + + uint indexBufferIndex = meshlet.indexOffset + workIndex; + gl_PrimitiveIndicesNV[workIndex] = localIndices[indexBufferIndex]; + } + + if(gl_LocalInvocationID.x == 0){ + gl_PrimitiveCountNV = meshlet.indexCount / 3; + } +} \ No newline at end of file diff --git a/projects/mesh_shader/resources/shaders/shader.task b/projects/mesh_shader/resources/shaders/shader.task new file mode 100644 index 0000000000000000000000000000000000000000..7a692e98e6384767191d76cef940e295ca127d62 --- /dev/null +++ b/projects/mesh_shader/resources/shaders/shader.task @@ -0,0 +1,78 @@ +#version 460 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_NV_mesh_shader : require +#extension GL_GOOGLE_include_directive : enable + +#include "meshlet.inc" +#include "common.inc" + +layout(local_size_x=32) in; + +taskNV out Task { + uint meshletIndices[32]; + mat4 mvp; +} OUT; + +layout( push_constant ) uniform constants{ + uint matrixIndex; + uint meshletCount; +}; + +// TODO: reuse mesh stage binding at location 2 after required fix in framework +layout(std430, binding = 5) readonly buffer meshletBuffer +{ + Meshlet meshlets[]; +}; + +struct Plane{ + vec3 pointOnPlane; + float padding0; + vec3 normal; + float padding1; +}; + +layout(set=0, binding=3, std140) uniform cameraPlaneBuffer{ + Plane cameraPlanes[6]; +}; + +layout(std430, binding = 4) readonly buffer matrixBuffer +{ + ObjectMatrices objectMatrices[]; +}; + +shared uint taskCount; + +bool isSphereInsideFrustum(vec3 spherePos, float sphereRadius, Plane cameraPlanes[6]){ + bool isInside = true; + for(int i = 0; i < 6; i++){ + Plane p = cameraPlanes[i]; + isInside = isInside && dot(p.normal, spherePos - p.pointOnPlane) - sphereRadius < 0; + } + return isInside; +} + +void main() { + + if(gl_LocalInvocationID.x >= meshletCount){ + return; + } + + uint meshletIndex = gl_GlobalInvocationID.x; + Meshlet meshlet = meshlets[meshletIndex]; + + if(gl_LocalInvocationID.x == 0){ + taskCount = 0; + } + + // TODO: scaling support + vec3 meshletPositionWorld = (vec4(meshlet.meanPosition, 1) * objectMatrices[matrixIndex].model).xyz; + if(isSphereInsideFrustum(meshletPositionWorld, meshlet.boundingSphereRadius, cameraPlanes)){ + uint outIndex = atomicAdd(taskCount, 1); + OUT.meshletIndices[outIndex] = gl_GlobalInvocationID.x; + } + + if(gl_LocalInvocationID.x == 0){ + gl_TaskCountNV = taskCount; + OUT.mvp = objectMatrices[matrixIndex].mvp; + } +} \ No newline at end of file diff --git a/projects/mesh_shader/resources/shaders/shader.vert b/projects/mesh_shader/resources/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..fca5057976f995183c040195bdbd592c63f1074e --- /dev/null +++ b/projects/mesh_shader/resources/shaders/shader.vert @@ -0,0 +1,29 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "common.inc" + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; + +layout(location = 0) out vec3 passNormal; +layout(location = 1) out uint dummyOutput; + +layout(std430, binding = 0) readonly buffer matrixBuffer +{ + ObjectMatrices objectMatrices[]; +}; + +layout( push_constant ) uniform constants{ + uint matrixIndex; + uint padding; // pad to same size as mesh shader constants +}; + + +void main() { + gl_Position = objectMatrices[matrixIndex].mvp * vec4(inPosition, 1.0); + passNormal = inNormal; + + dummyOutput = padding * 0; // padding must be used, else compiler shrinks constant size +} \ No newline at end of file diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3a94de5842f3e70625729c9755b8c88048ece2ec --- /dev/null +++ b/projects/mesh_shader/src/main.cpp @@ -0,0 +1,392 @@ +#include <iostream> +#include <vkcv/Core.hpp> +#include <GLFW/glfw3.h> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> + +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/gui/GUI.hpp> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/meshlet/Meshlet.hpp> +#include <vkcv/meshlet/Tipsify.hpp> +#include <vkcv/meshlet/Forsyth.hpp> + +struct Plane { + glm::vec3 pointOnPlane; + float padding0; + glm::vec3 normal; + float padding1; +}; + +struct CameraPlanes { + Plane planes[6]; +}; + +CameraPlanes computeCameraPlanes(const vkcv::camera::Camera& camera) { + const float fov = camera.getFov(); + const glm::vec3 pos = camera.getPosition(); + const float ratio = camera.getRatio(); + const glm::vec3 forward = glm::normalize(camera.getFront()); + float near; + float far; + camera.getNearFar(near, far); + + glm::vec3 up = glm::vec3(0, -1, 0); + glm::vec3 right = glm::normalize(glm::cross(forward, up)); + up = glm::cross(forward, right); + + const glm::vec3 nearCenter = pos + forward * near; + const glm::vec3 farCenter = pos + forward * far; + + const float tanFovHalf = glm::tan(fov / 2); + + const glm::vec3 nearUpCenter = nearCenter + up * tanFovHalf * near; + const glm::vec3 nearDownCenter = nearCenter - up * tanFovHalf * near; + const glm::vec3 nearRightCenter = nearCenter + right * tanFovHalf * near * ratio; + const glm::vec3 nearLeftCenter = nearCenter - right * tanFovHalf * near * ratio; + + const glm::vec3 farUpCenter = farCenter + up * tanFovHalf * far; + const glm::vec3 farDownCenter = farCenter - up * tanFovHalf * far; + const glm::vec3 farRightCenter = farCenter + right * tanFovHalf * far * ratio; + const glm::vec3 farLeftCenter = farCenter - right * tanFovHalf * far * ratio; + + CameraPlanes cameraPlanes; + // near + cameraPlanes.planes[0].pointOnPlane = nearCenter; + cameraPlanes.planes[0].normal = -forward; + // far + cameraPlanes.planes[1].pointOnPlane = farCenter; + cameraPlanes.planes[1].normal = forward; + + // top + cameraPlanes.planes[2].pointOnPlane = nearUpCenter; + cameraPlanes.planes[2].normal = glm::normalize(glm::cross(farUpCenter - nearUpCenter, right)); + // bot + cameraPlanes.planes[3].pointOnPlane = nearDownCenter; + cameraPlanes.planes[3].normal = glm::normalize(glm::cross(right, farDownCenter - nearDownCenter)); + + // right + cameraPlanes.planes[4].pointOnPlane = nearRightCenter; + cameraPlanes.planes[4].normal = glm::normalize(glm::cross(up, farRightCenter - nearRightCenter)); + // left + cameraPlanes.planes[5].pointOnPlane = nearLeftCenter; + cameraPlanes.planes[5].normal = glm::normalize(glm::cross(farLeftCenter - nearLeftCenter, up)); + + return cameraPlanes; +} + +int main(int argc, const char** argv) { + const char* applicationName = "Mesh shader"; + + const int windowWidth = 1280; + const int windowHeight = 720; + vkcv::Window window = vkcv::Window::create( + applicationName, + windowWidth, + windowHeight, + false + ); + + 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", VK_NV_MESH_SHADER_EXTENSION_NAME } + ); + + vkcv::gui::GUI gui (core, window); + + const auto& context = core.getContext(); + const vk::Instance& instance = context.getInstance(); + const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice(); + const vk::Device& device = context.getDevice(); + + vkcv::asset::Scene mesh; + const char* path = argc > 1 ? argv[1] : "resources/Bunny/Bunny.glb"; + vkcv::asset::loadScene(path, mesh); + + assert(!mesh.vertexGroups.empty()); + + 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); + + // format data for mesh shader + 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(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()) }; + + const auto& bunny = mesh.vertexGroups[0]; + std::vector<vkcv::meshlet::Vertex> interleavedVertices = vkcv::meshlet::convertToVertices(bunny.vertexBuffer.data, bunny.numVertices, attributes[0], attributes[1]); + // mesh shader buffers + const auto& assetLoaderIndexBuffer = mesh.vertexGroups[0].indexBuffer; + std::vector<uint32_t> indexBuffer32Bit = vkcv::meshlet::assetLoaderIndicesTo32BitIndices(assetLoaderIndexBuffer.data, assetLoaderIndexBuffer.type); + vkcv::meshlet::VertexCacheReorderResult tipsifyResult = vkcv::meshlet::tipsifyMesh(indexBuffer32Bit, interleavedVertices.size()); + vkcv::meshlet::VertexCacheReorderResult forsythResult = vkcv::meshlet::forsythReorder(indexBuffer32Bit, interleavedVertices.size()); + + const auto meshShaderModelData = createMeshShaderModelData(interleavedVertices, forsythResult.indexBuffer, forsythResult.skippedIndices); + + auto meshShaderVertexBuffer = core.createBuffer<vkcv::meshlet::Vertex>( + vkcv::BufferType::STORAGE, + meshShaderModelData.vertices.size()); + meshShaderVertexBuffer.fill(meshShaderModelData.vertices); + + auto meshShaderIndexBuffer = core.createBuffer<uint32_t>( + vkcv::BufferType::STORAGE, + meshShaderModelData.localIndices.size()); + meshShaderIndexBuffer.fill(meshShaderModelData.localIndices); + + auto meshletBuffer = core.createBuffer<vkcv::meshlet::Meshlet>( + vkcv::BufferType::STORAGE, + meshShaderModelData.meshlets.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL + ); + meshletBuffer.fill(meshShaderModelData.meshlets); + + // attachments + 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 bunnyPassDefinition({ present_color_attachment, depth_attachment }); + vkcv::PassHandle renderPass = core.createPass(bunnyPassDefinition); + + if (!renderPass) + { + std::cout << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::ShaderProgram bunnyShaderProgram{}; + vkcv::shader::GLSLCompiler compiler; + + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert"), + [&bunnyShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + bunnyShaderProgram.addShader(shaderStage, path); + }); + + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"), + [&bunnyShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + bunnyShaderProgram.addShader(shaderStage, path); + }); + + const std::vector<vkcv::VertexAttachment> vertexAttachments = bunnyShaderProgram.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 bunnyLayout (bindings); + + vkcv::DescriptorSetHandle vertexShaderDescriptorSet = core.createDescriptorSet(bunnyShaderProgram.getReflectedDescriptors()[0]); + + const vkcv::PipelineConfig bunnyPipelineDefinition { + bunnyShaderProgram, + (uint32_t)windowWidth, + (uint32_t)windowHeight, + renderPass, + { bunnyLayout }, + { core.getDescriptorSet(vertexShaderDescriptorSet).layout }, + false + }; + + struct ObjectMatrices { + glm::mat4 model; + glm::mat4 mvp; + }; + const size_t objectCount = 1; + vkcv::Buffer<ObjectMatrices> matrixBuffer = core.createBuffer<ObjectMatrices>(vkcv::BufferType::STORAGE, objectCount); + + vkcv::DescriptorWrites vertexShaderDescriptorWrites; + vertexShaderDescriptorWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0, matrixBuffer.getHandle()) }; + core.writeDescriptorSet(vertexShaderDescriptorSet, vertexShaderDescriptorWrites); + + vkcv::PipelineHandle bunnyPipeline = core.createGraphicsPipeline(bunnyPipelineDefinition); + + if (!bunnyPipeline) + { + std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + // mesh shader + vkcv::ShaderProgram meshShaderProgram; + compiler.compile(vkcv::ShaderStage::TASK, std::filesystem::path("resources/shaders/shader.task"), + [&meshShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + meshShaderProgram.addShader(shaderStage, path); + }); + + compiler.compile(vkcv::ShaderStage::MESH, std::filesystem::path("resources/shaders/shader.mesh"), + [&meshShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + meshShaderProgram.addShader(shaderStage, path); + }); + + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"), + [&meshShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + meshShaderProgram.addShader(shaderStage, path); + }); + + uint32_t setID = 0; + vkcv::DescriptorSetHandle meshShaderDescriptorSet = core.createDescriptorSet( meshShaderProgram.getReflectedDescriptors()[setID]); + const vkcv::VertexLayout meshShaderLayout(bindings); + + const vkcv::PipelineConfig meshShaderPipelineDefinition{ + meshShaderProgram, + (uint32_t)windowWidth, + (uint32_t)windowHeight, + renderPass, + {meshShaderLayout}, + {core.getDescriptorSet(meshShaderDescriptorSet).layout}, + false + }; + + vkcv::PipelineHandle meshShaderPipeline = core.createGraphicsPipeline(meshShaderPipelineDefinition); + + if (!meshShaderPipeline) + { + std::cout << "Error. Could not create mesh shader pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::Buffer<CameraPlanes> cameraPlaneBuffer = core.createBuffer<CameraPlanes>(vkcv::BufferType::UNIFORM, 1); + + vkcv::DescriptorWrites meshShaderWrites; + meshShaderWrites.storageBufferWrites = { + vkcv::BufferDescriptorWrite(0, meshShaderVertexBuffer.getHandle()), + vkcv::BufferDescriptorWrite(1, meshShaderIndexBuffer.getHandle()), + vkcv::BufferDescriptorWrite(2, meshletBuffer.getHandle()), + vkcv::BufferDescriptorWrite(4, matrixBuffer.getHandle()), + vkcv::BufferDescriptorWrite(5, meshletBuffer.getHandle()), + }; + meshShaderWrites.uniformBufferWrites = { + vkcv::BufferDescriptorWrite(3, cameraPlaneBuffer.getHandle()), + }; + + core.writeDescriptorSet( meshShaderDescriptorSet, meshShaderWrites); + + vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight, 1, false).getHandle(); + + auto start = std::chrono::system_clock::now(); + + vkcv::ImageHandle swapchainImageHandle = vkcv::ImageHandle::createSwapchainImageHandle(); + + const vkcv::Mesh renderMesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices, vkcv::IndexBitCount::Bit32); + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::camera::CameraManager cameraManager(window); + uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + + cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -2)); + + bool useMeshShader = true; + bool updateFrustumPlanes = true; + + while (window.isWindowOpen()) + { + vkcv::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 = std::chrono::duration_cast<std::chrono::microseconds>(end - start); + start = end; + + cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); + + const vkcv::camera::Camera& camera = cameraManager.getActiveCamera(); + + ObjectMatrices objectMatrices; + objectMatrices.model = *reinterpret_cast<glm::mat4*>(&mesh.meshes.front().modelMatrix); + objectMatrices.mvp = camera.getMVP() * objectMatrices.model; + + matrixBuffer.fill({ objectMatrices }); + + struct PushConstants { + uint32_t matrixIndex; + uint32_t meshletCount; + }; + PushConstants pushConstants{ 0, static_cast<uint32_t>(meshShaderModelData.meshlets.size()) }; + + if (updateFrustumPlanes) { + const CameraPlanes cameraPlanes = computeCameraPlanes(camera); + cameraPlaneBuffer.fill({ cameraPlanes }); + } + + const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + vkcv::PushConstants pushConstantData(sizeof(pushConstants)); + pushConstantData.appendDrawcall(pushConstants); + + if (useMeshShader) { + + vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(meshShaderDescriptorSet).vulkanHandle); + const uint32_t taskCount = (meshShaderModelData.meshlets.size() + 31) / 32; + + core.recordMeshShaderDrawcalls( + cmdStream, + renderPass, + meshShaderPipeline, + pushConstantData, + { vkcv::MeshShaderDrawcall({descriptorUsage}, taskCount)}, + { renderTargets }); + } + else { + + vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(vertexShaderDescriptorSet).vulkanHandle); + + core.recordDrawcallsToCmdStream( + cmdStream, + renderPass, + bunnyPipeline, + pushConstantData, + { vkcv::DrawcallInfo(renderMesh, { descriptorUsage }) }, + { renderTargets }); + } + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + gui.beginGUI(); + + ImGui::Begin("Settings"); + ImGui::Checkbox("Use mesh shader", &useMeshShader); + ImGui::Checkbox("Update frustum culling", &updateFrustumPlanes); + + ImGui::End(); + + gui.endGUI(); + + core.endFrame(); + } + return 0; +} diff --git a/projects/neural_network/src/main.cpp b/projects/neural_network/src/main.cpp index 2645f2077fa4392ce24a38f72fb5deeccb23c2ca..648b025e31cfed2cf06706d61ce9e013ae5a07ca 100644 --- a/projects/neural_network/src/main.cpp +++ b/projects/neural_network/src/main.cpp @@ -55,7 +55,7 @@ int main(int argc, const char** argv) { vkcv::PipelineHandle computePipeline = core.createComputePipeline(computeShaderProgram, { core.getDescriptorSet(computeDescriptorSet).layout }); vkcv::DescriptorWrites computeWrites; - computeWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0,inputBuffer.getHandle()) }; + computeWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0,inputBuffer.getHandle()) }; core.writeDescriptorSet(computeDescriptorSet, computeWrites); if (!computePipeline) diff --git a/projects/particle_simulation/src/BloomAndFlares.cpp b/projects/particle_simulation/src/BloomAndFlares.cpp index 98d53c2a1a2c08d40473858b47aacf34da30f7ed..5961aae664a39dfb9bd597ffa7648c9b67999af4 100644 --- a/projects/particle_simulation/src/BloomAndFlares.cpp +++ b/projects/particle_simulation/src/BloomAndFlares.cpp @@ -263,6 +263,10 @@ void BloomAndFlares::execWholePipeline(const vkcv::CommandStreamHandle &cmdStrea void BloomAndFlares::updateImageDimensions(uint32_t width, uint32_t height) { + if ((width == m_Width) && (height == m_Height)) { + return; + } + m_Width = width; m_Height = height; diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp index 0d83644b866f5f89fb33c68f1d5a79fcee8c028a..07ba6b194ce72dbad15a921ca13a4814c6d4f5df 100644 --- a/projects/particle_simulation/src/main.cpp +++ b/projects/particle_simulation/src/main.cpp @@ -163,13 +163,13 @@ int main(int argc, const char **argv) { particleBuffer.fill(particleSystem.getParticles()); vkcv::DescriptorWrites setWrites; - setWrites.uniformBufferWrites = {vkcv::UniformBufferDescriptorWrite(0,color.getHandle()), - vkcv::UniformBufferDescriptorWrite(1,position.getHandle())}; - setWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(2,particleBuffer.getHandle())}; + setWrites.uniformBufferWrites = {vkcv::BufferDescriptorWrite(0,color.getHandle()), + vkcv::BufferDescriptorWrite(1,position.getHandle())}; + setWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(2,particleBuffer.getHandle())}; core.writeDescriptorSet(descriptorSet, setWrites); vkcv::DescriptorWrites computeWrites; - computeWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0,particleBuffer.getHandle())}; + computeWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0,particleBuffer.getHandle())}; core.writeDescriptorSet(computeDescriptorSet, computeWrites); if (!particlePipeline || !computePipeline) diff --git a/projects/voxelization/CMakeLists.txt b/projects/voxelization/CMakeLists.txt index c962409f2e14994f0c38b923de7b9b1a4d198cab..d2f533b0f9c7313ddcc6046fb29378c3a507d1fe 100644 --- a/projects/voxelization/CMakeLists.txt +++ b/projects/voxelization/CMakeLists.txt @@ -30,7 +30,7 @@ if(MSVC) 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}) +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} ${vkcv_upscaling_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) +target_link_libraries(voxelization vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler vkcv_gui vkcv_upscaling) diff --git a/projects/voxelization/resources/shaders/postEffects.comp b/projects/voxelization/resources/shaders/postEffects.comp new file mode 100644 index 0000000000000000000000000000000000000000..c0f9fe1a764bcdabac5501e2f82692c6f476e9e6 --- /dev/null +++ b/projects/voxelization/resources/shaders/postEffects.comp @@ -0,0 +1,149 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "luma.inc" + +layout(set=0, binding=0) uniform texture2D inTexture; +layout(set=0, binding=1) uniform sampler textureSampler; +layout(set=0, binding=2, rgba8) uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout( push_constant ) uniform constants{ + float time; +}; + +// from: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ +vec3 ACESFilm(vec3 x) +{ + float a = 2.51f; + float b = 0.03f; + float c = 2.43f; + float d = 0.59f; + float e = 0.14f; + return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0, 1); +} + +// From Dave Hoskins: https://www.shadertoy.com/view/4djSRW. +float hash(vec3 p3){ + p3 = fract(p3 * 0.1031); + p3 += dot(p3,p3.yzx + 19.19); + return fract((p3.x + p3.y) * p3.z); +} + +// From iq: https://www.shadertoy.com/view/4sfGzS. +float noise(vec3 x){ + vec3 i = floor(x); + vec3 f = fract(x); + f = f*f*(3.0-2.0*f); + return mix(mix(mix(hash(i+vec3(0, 0, 0)), + hash(i+vec3(1, 0, 0)),f.x), + mix(hash(i+vec3(0, 1, 0)), + hash(i+vec3(1, 1, 0)),f.x),f.y), + mix(mix(hash(i+vec3(0, 0, 1)), + hash(i+vec3(1, 0, 1)),f.x), + mix(hash(i+vec3(0, 1, 1)), + hash(i+vec3(1, 1, 1)),f.x),f.y),f.z); +} + +// From: https://www.shadertoy.com/view/3sGSWVF +// Slightly high-passed continuous value-noise. +float grainSource(vec3 x, float strength, float pitch){ + float center = noise(x); + float v1 = center - noise(vec3( 1, 0, 0)/pitch + x) + 0.5; + float v2 = center - noise(vec3( 0, 1, 0)/pitch + x) + 0.5; + float v3 = center - noise(vec3(-1, 0, 0)/pitch + x) + 0.5; + float v4 = center - noise(vec3( 0,-1, 0)/pitch + x) + 0.5; + + float total = (v1 + v2 + v3 + v4) / 4.0; + return mix(1, 0.5 + total, strength); +} + +vec3 applyGrain(ivec2 uv, vec3 c){ + float grainLift = 0.6; + float grainStrength = 0.4; + float grainTimeFactor = 0.1; + + float timeColorOffset = 1.2; + vec3 grain = vec3( + grainSource(vec3(uv, floor(grainTimeFactor*time)), grainStrength, grainLift), + grainSource(vec3(uv, floor(grainTimeFactor*time + timeColorOffset)), grainStrength, grainLift), + grainSource(vec3(uv, floor(grainTimeFactor*time - timeColorOffset)), grainStrength, grainLift)); + + return c * grain; +} + +vec2 computeDistortedUV(vec2 uv, float aspectRatio){ + uv = uv * 2 - 1; + float r2 = dot(uv, uv); + float k1 = 0.02f; + + float maxR2 = dot(vec2(1), vec2(1)); + float maxFactor = maxR2 * k1; + + // correction only needed for pincushion distortion + maxFactor = min(maxFactor, 0); + + uv /= 1 + r2*k1; + + // correction to avoid going out of [-1, 1] range when using barrel distortion + uv *= 1 + maxFactor; + + return uv * 0.5 + 0.5; +} + +float computeLocalContrast(vec2 uv){ + float lumaMin = 100; + float lumaMax = 0; + + vec2 pixelSize = vec2(1) / textureSize(sampler2D(inTexture, textureSampler), 0); + + for(int x = -1; x <= 1; x++){ + for(int y = -1; y <= 1; y++){ + vec3 c = texture(sampler2D(inTexture, textureSampler), uv + vec2(x, y) * pixelSize).rgb; + float luma = computeLuma(c); + lumaMin = min(lumaMin, luma); + lumaMax = max(lumaMax, luma); + } + } + + return lumaMax - lumaMin; +} + +vec3 computeChromaticAberrationScale(vec2 uv){ + float localContrast = computeLocalContrast(uv); + vec3 colorScales = vec3(-1, 0, 1); + float aberrationScale = 0.004; + vec3 maxScaleFactors = colorScales * aberrationScale; + float factor = clamp(localContrast, 0, 1); + return mix(vec3(0), maxScaleFactors, factor); +} + +vec3 sampleColorChromaticAberration(vec2 uv){ + vec2 toCenter = (vec2(0.5) - uv); + + vec3 scaleFactors = computeChromaticAberrationScale(uv); + + float r = texture(sampler2D(inTexture, textureSampler), uv + toCenter * scaleFactors.r).r; + float g = texture(sampler2D(inTexture, textureSampler), uv + toCenter * scaleFactors.g).g; + float b = texture(sampler2D(inTexture, textureSampler), uv + toCenter * scaleFactors.b).b; + return vec3(r, g, b); +} + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){ + return; + } + ivec2 textureRes = textureSize(sampler2D(inTexture, textureSampler), 0); + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + vec2 uv = vec2(coord) / textureRes; + float aspectRatio = float(textureRes.x) / textureRes.y; + uv = computeDistortedUV(uv, aspectRatio); + + vec3 tonemapped = sampleColorChromaticAberration(uv); + tonemapped = applyGrain(coord, tonemapped); + + vec3 gammaCorrected = pow(tonemapped, vec3(1.f / 2.2f)); + imageStore(outImage, coord, vec4(gammaCorrected, 0.f)); +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/tonemapping.comp b/projects/voxelization/resources/shaders/tonemapping.comp index 8fa07d39ebb56eab857cdccb755a6558f5ae1ec3..ffadc9a71e207f97fec9a8815aa1c61bc709c369 100644 --- a/projects/voxelization/resources/shaders/tonemapping.comp +++ b/projects/voxelization/resources/shaders/tonemapping.comp @@ -1,18 +1,12 @@ #version 440 #extension GL_GOOGLE_include_directive : enable -#include "luma.inc" - layout(set=0, binding=0) uniform texture2D inTexture; layout(set=0, binding=1) uniform sampler textureSampler; layout(set=0, binding=2, rgba8) uniform image2D outImage; layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; -layout( push_constant ) uniform constants{ - float time; -}; - // from: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ vec3 ACESFilm(vec3 x) { @@ -24,112 +18,6 @@ vec3 ACESFilm(vec3 x) return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0, 1); } -// From Dave Hoskins: https://www.shadertoy.com/view/4djSRW. -float hash(vec3 p3){ - p3 = fract(p3 * 0.1031); - p3 += dot(p3,p3.yzx + 19.19); - return fract((p3.x + p3.y) * p3.z); -} - -// From iq: https://www.shadertoy.com/view/4sfGzS. -float noise(vec3 x){ - vec3 i = floor(x); - vec3 f = fract(x); - f = f*f*(3.0-2.0*f); - return mix(mix(mix(hash(i+vec3(0, 0, 0)), - hash(i+vec3(1, 0, 0)),f.x), - mix(hash(i+vec3(0, 1, 0)), - hash(i+vec3(1, 1, 0)),f.x),f.y), - mix(mix(hash(i+vec3(0, 0, 1)), - hash(i+vec3(1, 0, 1)),f.x), - mix(hash(i+vec3(0, 1, 1)), - hash(i+vec3(1, 1, 1)),f.x),f.y),f.z); -} - -// From: https://www.shadertoy.com/view/3sGSWVF -// Slightly high-passed continuous value-noise. -float grainSource(vec3 x, float strength, float pitch){ - float center = noise(x); - float v1 = center - noise(vec3( 1, 0, 0)/pitch + x) + 0.5; - float v2 = center - noise(vec3( 0, 1, 0)/pitch + x) + 0.5; - float v3 = center - noise(vec3(-1, 0, 0)/pitch + x) + 0.5; - float v4 = center - noise(vec3( 0,-1, 0)/pitch + x) + 0.5; - - float total = (v1 + v2 + v3 + v4) / 4.0; - return mix(1, 0.5 + total, strength); -} - -vec3 applyGrain(ivec2 uv, vec3 c){ - float grainLift = 0.6; - float grainStrength = 0.4; - float grainTimeFactor = 0.1; - - float timeColorOffset = 1.2; - vec3 grain = vec3( - grainSource(vec3(uv, floor(grainTimeFactor*time)), grainStrength, grainLift), - grainSource(vec3(uv, floor(grainTimeFactor*time + timeColorOffset)), grainStrength, grainLift), - grainSource(vec3(uv, floor(grainTimeFactor*time - timeColorOffset)), grainStrength, grainLift)); - - return c * grain; -} - -vec2 computeDistortedUV(vec2 uv, float aspectRatio){ - uv = uv * 2 - 1; - float r2 = dot(uv, uv); - float k1 = 0.02f; - - float maxR2 = dot(vec2(1), vec2(1)); - float maxFactor = maxR2 * k1; - - // correction only needed for pincushion distortion - maxFactor = min(maxFactor, 0); - - uv /= 1 + r2*k1; - - // correction to avoid going out of [-1, 1] range when using barrel distortion - uv *= 1 + maxFactor; - - return uv * 0.5 + 0.5; -} - -float computeLocalContrast(vec2 uv){ - float lumaMin = 100; - float lumaMax = 0; - - vec2 pixelSize = vec2(1) / textureSize(sampler2D(inTexture, textureSampler), 0); - - for(int x = -1; x <= 1; x++){ - for(int y = -1; y <= 1; y++){ - vec3 c = texture(sampler2D(inTexture, textureSampler), uv + vec2(x, y) * pixelSize).rgb; - float luma = computeLuma(c); - lumaMin = min(lumaMin, luma); - lumaMax = max(lumaMax, luma); - } - } - - return lumaMax - lumaMin; -} - -vec3 computeChromaticAberrationScale(vec2 uv){ - float localContrast = computeLocalContrast(uv); - vec3 colorScales = vec3(-1, 0, 1); - float aberrationScale = 0.004; - vec3 maxScaleFactors = colorScales * aberrationScale; - float factor = clamp(localContrast, 0, 1); - return mix(vec3(0), maxScaleFactors, factor); -} - -vec3 sampleColorChromaticAberration(vec2 uv){ - vec2 toCenter = (vec2(0.5) - uv); - - vec3 scaleFactors = computeChromaticAberrationScale(uv); - - float r = texture(sampler2D(inTexture, textureSampler), uv + toCenter * scaleFactors.r).r; - float g = texture(sampler2D(inTexture, textureSampler), uv + toCenter * scaleFactors.g).g; - float b = texture(sampler2D(inTexture, textureSampler), uv + toCenter * scaleFactors.b).b; - return vec3(r, g, b); -} - void main(){ if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){ @@ -138,12 +26,9 @@ void main(){ ivec2 textureRes = textureSize(sampler2D(inTexture, textureSampler), 0); ivec2 coord = ivec2(gl_GlobalInvocationID.xy); vec2 uv = vec2(coord) / textureRes; - float aspectRatio = float(textureRes.x) / textureRes.y; - uv = computeDistortedUV(uv, aspectRatio); - vec3 linearColor = sampleColorChromaticAberration(uv); + + vec3 linearColor = texture(sampler2D(inTexture, textureSampler), uv).rgb; vec3 tonemapped = ACESFilm(linearColor); - tonemapped = applyGrain(coord, tonemapped); - - vec3 gammaCorrected = pow(tonemapped, vec3(1.f / 2.2f)); - imageStore(outImage, coord, vec4(gammaCorrected, 0.f)); + + imageStore(outImage, coord, vec4(tonemapped, 0.f)); } \ No newline at end of file diff --git a/projects/voxelization/src/Voxelization.cpp b/projects/voxelization/src/Voxelization.cpp index bbf161ddeb0899a1ce61279b4c476fb19cb906d7..f7e03709c6423ef0e3c43251afb28e887b9be61f 100644 --- a/projects/voxelization/src/Voxelization.cpp +++ b/projects/voxelization/src/Voxelization.cpp @@ -119,10 +119,10 @@ Voxelization::Voxelization( m_voxelizationPipe = m_corePtr->createGraphicsPipeline(voxelizationPipeConfig); vkcv::DescriptorWrites voxelizationDescriptorWrites; - voxelizationDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; + voxelizationDescriptorWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; voxelizationDescriptorWrites.uniformBufferWrites = { - vkcv::UniformBufferDescriptorWrite(1, m_voxelInfoBuffer.getHandle()), - vkcv::UniformBufferDescriptorWrite(3, lightInfoBuffer) + vkcv::BufferDescriptorWrite(1, m_voxelInfoBuffer.getHandle()), + vkcv::BufferDescriptorWrite(3, lightInfoBuffer) }; voxelizationDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(4, shadowMap) }; voxelizationDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(5, shadowSampler) }; @@ -180,7 +180,7 @@ Voxelization::Voxelization( { m_corePtr->getDescriptorSet(m_voxelResetDescriptorSet).layout }); vkcv::DescriptorWrites resetVoxelWrites; - resetVoxelWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; + resetVoxelWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; m_corePtr->writeDescriptorSet(m_voxelResetDescriptorSet, resetVoxelWrites); // buffer to image @@ -192,7 +192,7 @@ Voxelization::Voxelization( { m_corePtr->getDescriptorSet(m_bufferToImageDescriptorSet).layout }); vkcv::DescriptorWrites bufferToImageDescriptorWrites; - bufferToImageDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; + bufferToImageDescriptorWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; bufferToImageDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(1, m_voxelImageIntermediate.getHandle()) }; m_corePtr->writeDescriptorSet(m_bufferToImageDescriptorSet, bufferToImageDescriptorWrites); @@ -205,11 +205,11 @@ Voxelization::Voxelization( { m_corePtr->getDescriptorSet(m_secondaryBounceDescriptorSet).layout }); vkcv::DescriptorWrites secondaryBounceDescriptorWrites; - secondaryBounceDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; + secondaryBounceDescriptorWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; secondaryBounceDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(1, m_voxelImageIntermediate.getHandle()) }; secondaryBounceDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(2, voxelSampler) }; secondaryBounceDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(3, m_voxelImage.getHandle()) }; - secondaryBounceDescriptorWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(4, m_voxelInfoBuffer.getHandle()) }; + secondaryBounceDescriptorWrites.uniformBufferWrites = { vkcv::BufferDescriptorWrite(4, m_voxelInfoBuffer.getHandle()) }; m_corePtr->writeDescriptorSet(m_secondaryBounceDescriptorSet, secondaryBounceDescriptorWrites); } @@ -331,7 +331,7 @@ void Voxelization::renderVoxelVisualisation( voxelVisualisationDescriptorWrite.storageImageWrites = { vkcv::StorageImageDescriptorWrite(0, m_voxelImage.getHandle(), mipLevel) }; voxelVisualisationDescriptorWrite.uniformBufferWrites = - { vkcv::UniformBufferDescriptorWrite(1, m_voxelInfoBuffer.getHandle()) }; + { vkcv::BufferDescriptorWrite(1, m_voxelInfoBuffer.getHandle()) }; m_corePtr->writeDescriptorSet(m_visualisationDescriptorSet, voxelVisualisationDescriptorWrite); uint32_t drawVoxelCount = voxelCount / exp2(mipLevel); diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp index ca9951490e57b4b6afa3bbee986a55342a40582e..e7f9caa493714d30f13f64c292f1b6e51e5170b1 100644 --- a/projects/voxelization/src/main.cpp +++ b/projects/voxelization/src/main.cpp @@ -11,6 +11,8 @@ #include "vkcv/gui/GUI.hpp" #include "ShadowMapping.hpp" #include "BloomAndFlares.hpp" +#include <vkcv/upscaling/FSRUpscaling.hpp> +#include <vkcv/upscaling/BilinearUpscaling.hpp> int main(int argc, const char** argv) { const char* applicationName = "Voxelization"; @@ -27,11 +29,11 @@ int main(int argc, const char** argv) { true ); - bool isFullscreen = false; - int windowedWidthBackup = windowWidth; - int windowedHeightBackup = windowHeight; - int windowedPosXBackup; - int windowedPosYBackup; + bool isFullscreen = false; + uint32_t windowedWidthBackup = windowWidth; + uint32_t windowedHeightBackup = windowHeight; + int windowedPosXBackup; + int windowedPosYBackup; glfwGetWindowPos(window.getWindow(), &windowedPosXBackup, &windowedPosYBackup); window.e_key.add([&](int key, int scancode, int action, int mods) { @@ -85,7 +87,7 @@ int main(int argc, const char** argv) { VK_MAKE_VERSION(0, 0, 1), { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, {}, - { "VK_KHR_swapchain" } + { "VK_KHR_swapchain", "VK_KHR_shader_float16_int8", "VK_KHR_16bit_storage" } ); vkcv::asset::Scene mesh; @@ -393,6 +395,9 @@ int main(int argc, const char** argv) { else { resolvedColorBuffer = colorBuffer; } + + vkcv::ImageHandle swapBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true).getHandle(); + vkcv::ImageHandle swapBuffer2 = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true).getHandle(); const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); @@ -421,6 +426,18 @@ int main(int argc, const char** argv) { vkcv::PipelineHandle tonemappingPipeline = core.createComputePipeline( tonemappingProgram, { core.getDescriptorSet(tonemappingDescriptorSet).layout }); + + // tonemapping compute shader + vkcv::ShaderProgram postEffectsProgram; + compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/postEffects.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + postEffectsProgram.addShader(shaderStage, path); + }); + vkcv::DescriptorSetHandle postEffectsDescriptorSet = core.createDescriptorSet( + postEffectsProgram.getReflectedDescriptors()[0]); + vkcv::PipelineHandle postEffectsPipeline = core.createComputePipeline( + postEffectsProgram, + { core.getDescriptorSet(postEffectsDescriptorSet).layout }); // resolve compute shader vkcv::ShaderProgram resolveProgram; @@ -438,7 +455,8 @@ int main(int argc, const char** argv) { vkcv::SamplerFilterType::NEAREST, vkcv::SamplerFilterType::NEAREST, vkcv::SamplerMipmapMode::NEAREST, - vkcv::SamplerAddressMode::CLAMP_TO_EDGE); + vkcv::SamplerAddressMode::CLAMP_TO_EDGE + ); // model matrices per mesh std::vector<glm::mat4> modelMatrices; @@ -473,7 +491,8 @@ int main(int argc, const char** argv) { vkcv::SamplerFilterType::LINEAR, vkcv::SamplerFilterType::LINEAR, vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::CLAMP_TO_EDGE); + vkcv::SamplerAddressMode::CLAMP_TO_EDGE + ); ShadowMapping shadowMapping(&core, vertexLayout); @@ -511,10 +530,10 @@ int main(int argc, const char** argv) { // write forward pass descriptor set vkcv::DescriptorWrites forwardDescriptorWrites; forwardDescriptorWrites.uniformBufferWrites = { - vkcv::UniformBufferDescriptorWrite(0, shadowMapping.getLightInfoBuffer()), - vkcv::UniformBufferDescriptorWrite(3, cameraPosBuffer.getHandle()), - vkcv::UniformBufferDescriptorWrite(6, voxelization.getVoxelInfoBufferHandle()), - vkcv::UniformBufferDescriptorWrite(7, volumetricSettingsBuffer.getHandle())}; + vkcv::BufferDescriptorWrite(0, shadowMapping.getLightInfoBuffer()), + vkcv::BufferDescriptorWrite(3, cameraPosBuffer.getHandle()), + vkcv::BufferDescriptorWrite(6, voxelization.getVoxelInfoBufferHandle()), + vkcv::BufferDescriptorWrite(7, volumetricSettingsBuffer.getHandle())}; forwardDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(1, shadowMapping.getShadowMap()), vkcv::SampledImageDescriptorWrite(4, voxelization.getVoxelImageHandle()) }; @@ -523,6 +542,27 @@ int main(int argc, const char** argv) { vkcv::SamplerDescriptorWrite(5, voxelSampler) }; core.writeDescriptorSet(forwardShadingDescriptorSet, forwardDescriptorWrites); + vkcv::upscaling::FSRUpscaling upscaling (core); + uint32_t fsrWidth = windowWidth, fsrHeight = windowHeight; + + vkcv::upscaling::FSRQualityMode fsrMode = vkcv::upscaling::FSRQualityMode::NONE; + int fsrModeIndex = static_cast<int>(fsrMode); + + const std::vector<const char*> fsrModeNames = { + "None", + "Ultra Quality", + "Quality", + "Balanced", + "Performance" + }; + + bool fsrMipLoadBiasFlag = true; + bool fsrMipLoadBiasFlagBackup = fsrMipLoadBiasFlag; + + vkcv::upscaling::BilinearUpscaling upscaling1 (core); + + bool bilinearUpscaling = false; + vkcv::gui::GUI gui(core, window); glm::vec2 lightAnglesDegree = glm::vec2(90.f, 0.f); @@ -550,22 +590,72 @@ int main(int argc, const char** argv) { if (!core.beginFrame(swapchainWidth, swapchainHeight)) { continue; } - - if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) { - depthBuffer = core.createImage(depthBufferFormat, swapchainWidth, swapchainHeight, 1, false, false, false, msaa).getHandle(); - colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, colorBufferRequiresStorage, true, msaa).getHandle(); + + uint32_t width, height; + vkcv::upscaling::getFSRResolution( + fsrMode, + swapchainWidth, swapchainHeight, + width, height + ); + + if ((width != fsrWidth) || ((height != fsrHeight)) || (fsrMipLoadBiasFlagBackup != fsrMipLoadBiasFlag)) { + fsrWidth = width; + fsrHeight = height; + fsrMipLoadBiasFlagBackup = fsrMipLoadBiasFlag; + + colorSampler = core.createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT, + fsrMipLoadBiasFlag? vkcv::upscaling::getFSRLodBias(fsrMode) : 0.0f + ); + + for (size_t i = 0; i < scene.materials.size(); i++) { + vkcv::DescriptorWrites setWrites; + setWrites.samplerWrites = { + vkcv::SamplerDescriptorWrite(1, colorSampler), + }; + core.writeDescriptorSet(materialDescriptorSets[i], setWrites); + } + + depthBuffer = core.createImage( + depthBufferFormat, + fsrWidth, fsrHeight, 1, + false, false, false, + msaa + ).getHandle(); + + colorBuffer = core.createImage( + colorBufferFormat, + fsrWidth, fsrHeight, 1, + false, colorBufferRequiresStorage, true, + msaa + ).getHandle(); if (usingMsaa) { - resolvedColorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, true, true).getHandle(); - } - else { + resolvedColorBuffer = core.createImage( + colorBufferFormat, + fsrWidth, fsrHeight, 1, + false, true, true + ).getHandle(); + } else { resolvedColorBuffer = colorBuffer; } - - windowWidth = swapchainWidth; - windowHeight = swapchainHeight; - - bloomFlares.updateImageDimensions(windowWidth, windowHeight); + + swapBuffer = core.createImage( + colorBufferFormat, + fsrWidth, fsrHeight, 1, + false, true + ).getHandle(); + + swapBuffer2 = core.createImage( + colorBufferFormat, + swapchainWidth, swapchainHeight, 1, + false, true + ).getHandle(); + + bloomFlares.updateImageDimensions(swapchainWidth, swapchainHeight); } auto end = std::chrono::system_clock::now(); @@ -575,9 +665,17 @@ int main(int argc, const char** argv) { vkcv::DescriptorWrites tonemappingDescriptorWrites; tonemappingDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, resolvedColorBuffer) }; tonemappingDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, colorSampler) }; - tonemappingDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, swapchainInput) }; + tonemappingDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, swapBuffer) }; core.writeDescriptorSet(tonemappingDescriptorSet, tonemappingDescriptorWrites); + + // update descriptor sets which use swapchain image + vkcv::DescriptorWrites postEffectsDescriptorWrites; + postEffectsDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, swapBuffer2) }; + postEffectsDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, colorSampler) }; + postEffectsDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, swapchainInput) }; + + core.writeDescriptorSet(postEffectsDescriptorSet, postEffectsDescriptorWrites); // update resolve descriptor, color images could be changed vkcv::DescriptorWrites resolveDescriptorWrites; @@ -679,11 +777,18 @@ int main(int argc, const char** argv) { renderTargets); const uint32_t fullscreenLocalGroupSize = 8; - const uint32_t fulsscreenDispatchCount[3] = { - static_cast<uint32_t>(glm::ceil(windowWidth / static_cast<float>(fullscreenLocalGroupSize))), - static_cast<uint32_t>(glm::ceil(windowHeight / static_cast<float>(fullscreenLocalGroupSize))), - 1 - }; + + uint32_t fulsscreenDispatchCount [3]; + + fulsscreenDispatchCount[0] = static_cast<uint32_t>( + glm::ceil(fsrWidth / static_cast<float>(fullscreenLocalGroupSize)) + ); + + fulsscreenDispatchCount[1] = static_cast<uint32_t>( + glm::ceil(fsrHeight / static_cast<float>(fullscreenLocalGroupSize)) + ); + + fulsscreenDispatchCount[2] = 1; if (usingMsaa) { if (msaaCustomResolve) { @@ -706,24 +811,58 @@ int main(int argc, const char** argv) { } } - bloomFlares.execWholePipeline(cmdStream, resolvedColorBuffer, windowWidth, windowHeight, - glm::normalize(cameraManager.getActiveCamera().getFront())); + bloomFlares.execWholePipeline(cmdStream, resolvedColorBuffer, fsrWidth, fsrHeight, + glm::normalize(cameraManager.getActiveCamera().getFront()) + ); - core.prepareImageForStorage(cmdStream, swapchainInput); + core.prepareImageForStorage(cmdStream, swapBuffer); core.prepareImageForSampling(cmdStream, resolvedColorBuffer); - - auto timeSinceStart = std::chrono::duration_cast<std::chrono::microseconds>(end - appStartTime); - float timeF = static_cast<float>(timeSinceStart.count()) * 0.01; - - vkcv::PushConstants timePushConstants (sizeof(timeF)); - timePushConstants.appendDrawcall(timeF); core.recordComputeDispatchToCmdStream( cmdStream, tonemappingPipeline, fulsscreenDispatchCount, - { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(tonemappingDescriptorSet).vulkanHandle) }, - timePushConstants); + { vkcv::DescriptorSetUsage(0, core.getDescriptorSet( + tonemappingDescriptorSet + ).vulkanHandle) }, + vkcv::PushConstants(0) + ); + + core.prepareImageForStorage(cmdStream, swapBuffer2); + core.prepareImageForSampling(cmdStream, swapBuffer); + + if (bilinearUpscaling) { + upscaling1.recordUpscaling(cmdStream, swapBuffer, swapBuffer2); + } else { + upscaling.recordUpscaling(cmdStream, swapBuffer, swapBuffer2); + } + + core.prepareImageForStorage(cmdStream, swapchainInput); + core.prepareImageForSampling(cmdStream, swapBuffer2); + + auto timeSinceStart = std::chrono::duration_cast<std::chrono::microseconds>(end - appStartTime); + float timeF = static_cast<float>(timeSinceStart.count()) * 0.01f; + + vkcv::PushConstants timePushConstants (sizeof(timeF)); + timePushConstants.appendDrawcall(timeF); + + fulsscreenDispatchCount[0] = static_cast<uint32_t>( + glm::ceil(swapchainWidth / static_cast<float>(fullscreenLocalGroupSize)) + ); + + fulsscreenDispatchCount[1] = static_cast<uint32_t>( + glm::ceil(swapchainHeight / static_cast<float>(fullscreenLocalGroupSize)) + ); + + core.recordComputeDispatchToCmdStream( + cmdStream, + postEffectsPipeline, + fulsscreenDispatchCount, + { vkcv::DescriptorSetUsage(0, core.getDescriptorSet( + postEffectsDescriptorSet + ).vulkanHandle) }, + timePushConstants + ); // present and end core.prepareSwapchainImageForPresent(cmdStream); @@ -751,12 +890,25 @@ int main(int argc, const char** argv) { ImGui::DragFloat("Voxelization extent", &voxelizationExtent, 1.f, 0.f); voxelizationExtent = std::max(voxelizationExtent, 1.f); voxelVisualisationMip = std::max(voxelVisualisationMip, 0); - + ImGui::ColorEdit3("Scattering color", &scatteringColor.x); ImGui::DragFloat("Scattering density", &scatteringDensity, 0.0001); ImGui::ColorEdit3("Absorption color", &absorptionColor.x); ImGui::DragFloat("Absorption density", &absorptionDensity, 0.0001); ImGui::DragFloat("Volumetric ambient", &volumetricAmbient, 0.002); + + float fsrSharpness = upscaling.getSharpness(); + + ImGui::Combo("FSR Quality Mode", &fsrModeIndex, fsrModeNames.data(), fsrModeNames.size()); + ImGui::DragFloat("FSR Sharpness", &fsrSharpness, 0.001, 0.0f, 1.0f); + ImGui::Checkbox("FSR Mip Lod Bias", &fsrMipLoadBiasFlag); + ImGui::Checkbox("Bilinear Upscaling", &bilinearUpscaling); + + if ((fsrModeIndex >= 0) && (fsrModeIndex <= 4)) { + fsrMode = static_cast<vkcv::upscaling::FSRQualityMode>(fsrModeIndex); + } + + upscaling.setSharpness(fsrSharpness); if (ImGui::Button("Reload forward pass")) { diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp index 5db50869498600fa8e926c0feff2cb5fda1eb22e..2e30fb961d0b0931e4ff8796dd92b2cbd0b5f734 100644 --- a/src/vkcv/Context.cpp +++ b/src/vkcv/Context.cpp @@ -151,7 +151,7 @@ namespace vkcv * @param check The elements to be checked * @return True, if all elements in "check" are supported */ - bool checkSupport(std::vector<const char*>& supported, std::vector<const char*>& check) + bool checkSupport(const std::vector<const char*>& supported, const std::vector<const char*>& check) { for (auto checkElem : check) { bool found = false; @@ -180,11 +180,20 @@ namespace vkcv return extensions; } + bool isPresentInCharPtrVector(const std::vector<const char*>& v, const char* term){ + for (const auto& entry : v) { + if (strcmp(entry, term) != 0) { + return true; + } + } + return false; + } + Context Context::create(const char *applicationName, uint32_t applicationVersion, - std::vector<vk::QueueFlagBits> queueFlags, - std::vector<const char *> instanceExtensions, - std::vector<const char *> deviceExtensions) { + const std::vector<vk::QueueFlagBits>& queueFlags, + const std::vector<const char *>& instanceExtensions, + const std::vector<const char *>& deviceExtensions) { // check for layer support const std::vector<vk::LayerProperties>& layerProperties = vk::enumerateInstanceLayerProperties(); @@ -223,7 +232,7 @@ namespace vkcv // for GLFW: get all required extensions std::vector<const char*> requiredExtensions = getRequiredExtensions(); - instanceExtensions.insert(instanceExtensions.end(), requiredExtensions.begin(), requiredExtensions.end()); + requiredExtensions.insert(requiredExtensions.end(), instanceExtensions.begin(), instanceExtensions.end()); const vk::ApplicationInfo applicationInfo( applicationName, @@ -238,8 +247,8 @@ namespace vkcv &applicationInfo, 0, nullptr, - static_cast<uint32_t>(instanceExtensions.size()), - instanceExtensions.data() + static_cast<uint32_t>(requiredExtensions.size()), + requiredExtensions.data() ); #ifndef NDEBUG @@ -286,13 +295,39 @@ namespace vkcv deviceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); deviceCreateInfo.ppEnabledLayerNames = validationLayers.data(); #endif - + const bool shaderFloat16 = checkSupport(deviceExtensions, { "VK_KHR_shader_float16_int8" }); + const bool storage16bit = checkSupport(deviceExtensions, { "VK_KHR_16bit_storage" }); + // FIXME: check if device feature is supported - vk::PhysicalDeviceFeatures deviceFeatures; - deviceFeatures.fragmentStoresAndAtomics = true; - deviceFeatures.geometryShader = true; - deviceFeatures.depthClamp = true; - deviceCreateInfo.pEnabledFeatures = &deviceFeatures; + vk::PhysicalDeviceShaderFloat16Int8Features deviceShaderFloat16Int8Features; + deviceShaderFloat16Int8Features.shaderFloat16 = shaderFloat16; + + vk::PhysicalDevice16BitStorageFeatures device16BitStorageFeatures; + device16BitStorageFeatures.storageBuffer16BitAccess = storage16bit; + + vk::PhysicalDeviceFeatures2 deviceFeatures2; + deviceFeatures2.features.fragmentStoresAndAtomics = true; + deviceFeatures2.features.geometryShader = true; + deviceFeatures2.features.depthClamp = true; + deviceFeatures2.features.shaderInt16 = true; + + const bool usingMeshShaders = isPresentInCharPtrVector(deviceExtensions, VK_NV_MESH_SHADER_EXTENSION_NAME); + vk::PhysicalDeviceMeshShaderFeaturesNV meshShadingFeatures; + if (usingMeshShaders) { + meshShadingFeatures.taskShader = true; + meshShadingFeatures.meshShader = true; + deviceFeatures2.setPNext(&meshShadingFeatures); + } + + if (shaderFloat16) { + deviceFeatures2.setPNext(&deviceShaderFloat16Int8Features); + } + + if (storage16bit) { + deviceShaderFloat16Int8Features.setPNext(&device16BitStorageFeatures); + } + + deviceCreateInfo.setPNext(&deviceFeatures2); // Ablauf // qCreateInfos erstellen --> braucht das Device @@ -300,6 +335,11 @@ namespace vkcv // jetzt koennen wir mit dem device die queues erstellen vk::Device device = physicalDevice.createDevice(deviceCreateInfo); + + if (usingMeshShaders) + { + InitMeshShaderDrawFunctions(device); + } QueueManager queueManager = QueueManager::create( device, diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index bdfbdeef1411f9728969fd43c16bbec5a1a8c136..aaf8a7a6aeacc4af7fdfc4b42914dcd54a754e57 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -53,9 +53,9 @@ namespace vkcv Core Core::create(Window &window, const char *applicationName, uint32_t applicationVersion, - std::vector<vk::QueueFlagBits> queueFlags, - std::vector<const char *> instanceExtensions, - std::vector<const char *> deviceExtensions) + const std::vector<vk::QueueFlagBits>& queueFlags, + const std::vector<const char *>& instanceExtensions, + const std::vector<const char *>& deviceExtensions) { Context context = Context::create( applicationName, applicationVersion, @@ -90,8 +90,8 @@ namespace vkcv 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_swapchain(swapChain), m_window(window), - m_swapchain(swapChain), 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)), @@ -118,7 +118,8 @@ namespace vkcv swapchainImageViews, swapChain.getExtent().width, swapChain.getExtent().height, - swapChain.getFormat()); + swapChain.getFormat() + ); } Core::~Core() noexcept { @@ -227,130 +228,246 @@ namespace vkcv return (m_currentSwapchainImageIndex != std::numeric_limits<uint32_t>::max()); } - void Core::recordDrawcallsToCmdStream( - const CommandStreamHandle cmdStreamHandle, - const PassHandle renderpassHandle, - const PipelineHandle pipelineHandle, - const PushConstants &pushConstants, - const std::vector<DrawcallInfo> &drawcalls, - const std::vector<ImageHandle> &renderTargets) { + std::array<uint32_t, 2> getWidthHeightFromRenderTargets( + const std::vector<ImageHandle>& renderTargets, + const Swapchain& swapchain, + const ImageManager& imageManager) { - if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { - return; - } + std::array<uint32_t, 2> widthHeight; - 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; + const auto& swapchainExtent = swapchain.getExtent(); + widthHeight[0] = swapchainExtent.width; + widthHeight[1] = swapchainExtent.height; } else { - width = m_ImageManager->getImageWidth(firstImage); - height = m_ImageManager->getImageHeight(firstImage); + widthHeight[0] = imageManager.getImageWidth(firstImage); + widthHeight[1] = imageManager.getImageHeight(firstImage); } } else { - width = 1; - height = 1; + widthHeight[0] = 1; + widthHeight[1] = 1; } // TODO: validate that width/height match for all attachments + return widthHeight; + } - 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::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height)); + vk::Framebuffer createFramebuffer( + const std::vector<ImageHandle>& renderTargets, + const ImageManager& imageManager, + const Swapchain& swapchain, + vk::RenderPass renderpass, + vk::Device device) { std::vector<vk::ImageView> attachmentsViews; - for (const ImageHandle& handle : renderTargets) { - vk::ImageView targetHandle; - const auto cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle); + for (const ImageHandle handle : renderTargets) { + vk::ImageView targetHandle = imageManager.getVulkanImageView(handle); + attachmentsViews.push_back(targetHandle); + } + + const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, swapchain, imageManager); - targetHandle = m_ImageManager->getVulkanImageView(handle); - const bool isDepthImage = isDepthFormat(m_ImageManager->getImageFormat(handle)); - const vk::ImageLayout targetLayout = + const vk::FramebufferCreateInfo createInfo( + {}, + renderpass, + static_cast<uint32_t>(attachmentsViews.size()), + attachmentsViews.data(), + widthHeight[0], + widthHeight[1], + 1); + + return device.createFramebuffer(createInfo); + } + + void transitionRendertargetsToAttachmentLayout( + const std::vector<ImageHandle>& renderTargets, + ImageManager& imageManager, + const vk::CommandBuffer cmdBuffer) { + + for (const ImageHandle handle : renderTargets) { + vk::ImageView targetHandle = imageManager.getVulkanImageView(handle); + const bool isDepthImage = isDepthFormat(imageManager.getImageFormat(handle)); + const vk::ImageLayout targetLayout = isDepthImage ? vk::ImageLayout::eDepthStencilAttachmentOptimal : vk::ImageLayout::eColorAttachmentOptimal; - m_ImageManager->recordImageLayoutTransition(handle, targetLayout, cmdBuffer); - attachmentsViews.push_back(targetHandle); + imageManager.recordImageLayoutTransition(handle, targetLayout, cmdBuffer); } - - const vk::FramebufferCreateInfo createInfo( - {}, - renderpass, - static_cast<uint32_t>(attachmentsViews.size()), - attachmentsViews.data(), - width, - height, - 1 - ); - - 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 + std::vector<vk::ClearValue> createAttachmentClearValues(const std::vector<AttachmentDescription>& attachments) { + std::vector<vk::ClearValue> clearValues; + for (const auto& attachment : 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 + }); + } + } + return clearValues; + } + + void recordDynamicViewport(vk::CommandBuffer cmdBuffer, uint32_t width, uint32_t height) { + 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}); + vk::Rect2D dynamicScissor({ 0, 0 }, { width, height }); + + cmdBuffer.setViewport(0, 1, &dynamicViewport); + cmdBuffer.setScissor(0, 1, &dynamicScissor); + } + + void Core::recordDrawcallsToCmdStream( + const CommandStreamHandle cmdStreamHandle, + const PassHandle renderpassHandle, + const PipelineHandle pipelineHandle, + const PushConstants &pushConstantData, + const std::vector<DrawcallInfo> &drawcalls, + const std::vector<ImageHandle> &renderTargets) { + + if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { + return; + } + + const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_swapchain, *m_ImageManager); + const auto width = widthHeight[0]; + const auto height = widthHeight[1]; + + 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::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height)); + + vk::CommandBuffer cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle); + transitionRendertargetsToAttachmentLayout(renderTargets, *m_ImageManager, cmdBuffer); + + const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_swapchain, renderpass, m_Context.m_Device); + + if (!framebuffer) { + vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer"); + return; + } SubmitInfo submitInfo; submitInfo.queueType = QueueType::Graphics; submitInfo.signalSemaphores = { m_SyncResources.renderFinished }; 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; + const std::vector<vk::ClearValue> clearValues = createAttachmentClearValues(passConfig.attachments); - if (isDepthFormat(attachment.format)) { - clear = 1.0f; - } + const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data()); + cmdBuffer.beginRenderPass(beginInfo, {}, {}); - clearValues.emplace_back(std::array<float, 4>{ - clear, - clear, - clear, - 1.f - }); - } - } + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); - const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data()); - const vk::SubpassContents subpassContents = {}; - cmdBuffer.beginRenderPass(beginInfo, subpassContents, {}); + const PipelineConfig &pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle); + if(pipeConfig.m_UseDynamicViewport) + { + recordDynamicViewport(cmdBuffer, width, height); + } - cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); + for (int i = 0; i < drawcalls.size(); i++) { + recordDrawcall(drawcalls[i], cmdBuffer, pipelineLayout, pushConstantData, i); + } - const PipelineConfig &pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle); - if (pipeConfig.m_UseDynamicViewport) { - cmdBuffer.setViewport(0, 1, &dynamicViewport); - cmdBuffer.setScissor(0, 1, &dynamicScissor); - } + vk::Rect2D dynamicScissor({0, 0}, {width, height}); + cmdBuffer.endRenderPass(); + }; - for (size_t i = 0; i < drawcalls.size(); i++) { - recordDrawcall(drawcalls[i], cmdBuffer, pipelineLayout, pushConstants, i); - } + auto finishFunction = [framebuffer, this]() + { + m_Context.m_Device.destroy(framebuffer); + }; + + recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction); + } + + void Core::recordMeshShaderDrawcalls( + const CommandStreamHandle cmdStreamHandle, + const PassHandle renderpassHandle, + const PipelineHandle pipelineHandle, + const PushConstants& pushConstantData, + const std::vector<MeshShaderDrawcall>& drawcalls, + const std::vector<ImageHandle>& renderTargets) { + + if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { + return; + } + + const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_swapchain, *m_ImageManager); + const auto width = widthHeight[0]; + const auto height = widthHeight[1]; + + 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::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height)); + + vk::CommandBuffer cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle); + transitionRendertargetsToAttachmentLayout(renderTargets, *m_ImageManager, cmdBuffer); + + const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_swapchain, renderpass, m_Context.m_Device); + + if (!framebuffer) { + vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer"); + return; + } + + SubmitInfo submitInfo; + submitInfo.queueType = QueueType::Graphics; + submitInfo.signalSemaphores = { m_SyncResources.renderFinished }; + + auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) { + + const std::vector<vk::ClearValue> clearValues = createAttachmentClearValues(passConfig.attachments); + + const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data()); + cmdBuffer.beginRenderPass(beginInfo, {}, {}); + + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); - cmdBuffer.endRenderPass(); - }; + const PipelineConfig& pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle); + if (pipeConfig.m_UseDynamicViewport) + { + recordDynamicViewport(cmdBuffer, width, height); + } + + for (int i = 0; i < drawcalls.size(); i++) { + const uint32_t pushConstantOffset = i * pushConstantData.getSizePerDrawcall(); + recordMeshShaderDrawcall( + cmdBuffer, + pipelineLayout, + pushConstantData, + pushConstantOffset, + drawcalls[i], + 0); + } - auto finishFunction = [framebuffer, this]() - { - m_Context.m_Device.destroy(framebuffer); - }; + cmdBuffer.endRenderPass(); + }; + + auto finishFunction = [framebuffer, this]() + { + m_Context.m_Device.destroy(framebuffer); + }; recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction); } @@ -373,7 +490,8 @@ namespace vkcv pipelineLayout, usage.setLocation, { usage.vulkanHandle }, - {}); + usage.dynamicOffsets + ); } if (pushConstants.getSizePerDrawcall() > 0) { cmdBuffer.pushConstants( @@ -476,7 +594,7 @@ namespace vkcv } } - void Core::submitCommandStream(const CommandStreamHandle handle) { + 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 }; @@ -484,8 +602,9 @@ namespace vkcv } SamplerHandle Core::createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, - SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode) { - return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, addressMode); + SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode, + float mipLodBias) { + return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, addressMode, mipLodBias); } Image Core::createImage( @@ -516,14 +635,18 @@ namespace vkcv multisampling); } - uint32_t Core::getImageWidth(ImageHandle imageHandle) + uint32_t Core::getImageWidth(const ImageHandle& image) { - return m_ImageManager->getImageWidth(imageHandle); + return m_ImageManager->getImageWidth(image); } - uint32_t Core::getImageHeight(ImageHandle imageHandle) + uint32_t Core::getImageHeight(const ImageHandle& image) { - return m_ImageManager->getImageHeight(imageHandle); + return m_ImageManager->getImageHeight(image); + } + + vk::Format Core::getImageFormat(const ImageHandle& image) { + return m_ImageManager->getImageFormat(image); } DescriptorSetHandle Core::createDescriptorSet(const std::vector<DescriptorBinding>& bindings) @@ -544,32 +667,32 @@ namespace vkcv return m_DescriptorManager->getDescriptorSet(handle); } - void Core::prepareSwapchainImageForPresent(const CommandStreamHandle cmdStream) { + 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) { + 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) { + 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) { + 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) { + void Core::recordBufferMemoryBarrier(const CommandStreamHandle& cmdStream, const BufferHandle& buffer) { recordCommandsToStream(cmdStream, [buffer, this](const vk::CommandBuffer cmdBuffer) { m_BufferManager->recordBufferMemoryBarrier(buffer, cmdBuffer); }, nullptr); @@ -581,7 +704,7 @@ namespace vkcv }, nullptr); } - void Core::resolveMSAAImage(CommandStreamHandle cmdStream, ImageHandle src, ImageHandle dst) { + void Core::resolveMSAAImage(const CommandStreamHandle& cmdStream, const ImageHandle& src, const ImageHandle& dst) { recordCommandsToStream(cmdStream, [src, dst, this](const vk::CommandBuffer cmdBuffer) { m_ImageManager->recordMSAAResolve(cmdBuffer, src, dst); }, nullptr); @@ -590,5 +713,86 @@ namespace vkcv vk::ImageView Core::getSwapchainImageView() const { return m_ImageManager->getVulkanImageView(vkcv::ImageHandle::createSwapchainImageHandle()); } + + void Core::recordMemoryBarrier(const CommandStreamHandle& cmdStream) { + recordCommandsToStream(cmdStream, [](const vk::CommandBuffer cmdBuffer) { + vk::MemoryBarrier barrier ( + vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eMemoryRead, + vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eMemoryRead + ); + + cmdBuffer.pipelineBarrier( + vk::PipelineStageFlagBits::eAllCommands, + vk::PipelineStageFlagBits::eAllCommands, + vk::DependencyFlags(), + 1, &barrier, + 0, nullptr, + 0, nullptr + ); + }, nullptr); + } + + void Core::recordBlitImage(const CommandStreamHandle& cmdStream, const ImageHandle& src, const ImageHandle& dst, + SamplerFilterType filterType) { + recordCommandsToStream(cmdStream, [&](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordImageLayoutTransition( + src, vk::ImageLayout::eTransferSrcOptimal, cmdBuffer + ); + + m_ImageManager->recordImageLayoutTransition( + dst, vk::ImageLayout::eTransferDstOptimal, cmdBuffer + ); + + const std::array<vk::Offset3D, 2> srcOffsets = { + vk::Offset3D(0, 0, 0), + vk::Offset3D( + m_ImageManager->getImageWidth(src), + m_ImageManager->getImageHeight(src), + 1 + ) + }; + + const std::array<vk::Offset3D, 2> dstOffsets = { + vk::Offset3D(0, 0, 0), + vk::Offset3D( + m_ImageManager->getImageWidth(dst), + m_ImageManager->getImageHeight(dst), + 1 + ) + }; + + const bool srcDepth = isDepthFormat(m_ImageManager->getImageFormat(src)); + const bool dstDepth = isDepthFormat(m_ImageManager->getImageFormat(dst)); + + const vk::ImageBlit blit = vk::ImageBlit( + vk::ImageSubresourceLayers( + srcDepth? + vk::ImageAspectFlagBits::eDepth : + vk::ImageAspectFlagBits::eColor, + 0, 0, 1 + ), + srcOffsets, + vk::ImageSubresourceLayers( + dstDepth? + vk::ImageAspectFlagBits::eDepth : + vk::ImageAspectFlagBits::eColor, + 0, 0, 1 + ), + dstOffsets + ); + + cmdBuffer.blitImage( + m_ImageManager->getVulkanImage(src), + vk::ImageLayout::eTransferSrcOptimal, + m_ImageManager->getVulkanImage(dst), + vk::ImageLayout::eTransferDstOptimal, + 1, + &blit, + filterType == SamplerFilterType::LINEAR? + vk::Filter::eLinear : + vk::Filter::eNearest + ); + }, nullptr); + } } diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp index 07ca97b5ade9b69eed724000d9c7b388818d6725..0df359a15883847c132c429ed2945ac7624fb865 100644 --- a/src/vkcv/DescriptorManager.cpp +++ b/src/vkcv/DescriptorManager.cpp @@ -11,10 +11,14 @@ namespace vkcv * Allocate the set size for the descriptor pools, namely 1000 units of each descriptor type below. * Finally, create an initial pool. */ - 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) }; + 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::DescriptorPoolSize(vk::DescriptorType::eUniformBufferDynamic, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageBufferDynamic, 1000) + }; m_PoolInfo = vk::DescriptorPoolCreateInfo( vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, @@ -105,7 +109,6 @@ namespace vkcv const ImageManager &imageManager, const BufferManager &bufferManager, const SamplerManager &samplerManager) { - vk::DescriptorSet set = m_DescriptorSets[handle.getId()].vulkanHandle; std::vector<vk::DescriptorImageInfo> imageInfos; @@ -153,10 +156,15 @@ namespace vkcv } for (const auto& write : writes.uniformBufferWrites) { + const size_t size = bufferManager.getBufferSize(write.buffer); + const uint32_t offset = std::clamp<uint32_t>(write.offset, 0, size); + const vk::DescriptorBufferInfo bufferInfo( bufferManager.getBuffer(write.buffer), - static_cast<uint32_t>(0), - bufferManager.getBufferSize(write.buffer) + offset, + write.size == 0? size : std::min<uint32_t>( + write.size, size - offset + ) ); bufferInfos.push_back(bufferInfo); @@ -165,6 +173,8 @@ namespace vkcv 0, bufferInfos.size(), write.binding, + write.dynamic? + vk::DescriptorType::eUniformBufferDynamic : vk::DescriptorType::eUniformBuffer }; @@ -172,10 +182,15 @@ namespace vkcv } for (const auto& write : writes.storageBufferWrites) { + const size_t size = bufferManager.getBufferSize(write.buffer); + const uint32_t offset = std::clamp<uint32_t>(write.offset, 0, size); + const vk::DescriptorBufferInfo bufferInfo( bufferManager.getBuffer(write.buffer), - static_cast<uint32_t>(0), - bufferManager.getBufferSize(write.buffer) + offset, + write.size == 0? size : std::min<uint32_t>( + write.size, size - offset + ) ); bufferInfos.push_back(bufferInfo); @@ -184,6 +199,8 @@ namespace vkcv 0, bufferInfos.size(), write.binding, + write.dynamic? + vk::DescriptorType::eStorageBufferDynamic : vk::DescriptorType::eStorageBuffer }; @@ -239,8 +256,12 @@ namespace vkcv { case DescriptorType::UNIFORM_BUFFER: return vk::DescriptorType::eUniformBuffer; + case DescriptorType::UNIFORM_BUFFER_DYNAMIC: + return vk::DescriptorType::eUniformBufferDynamic; case DescriptorType::STORAGE_BUFFER: return vk::DescriptorType::eStorageBuffer; + case DescriptorType::STORAGE_BUFFER_DYNAMIC: + return vk::DescriptorType::eStorageBufferDynamic; case DescriptorType::SAMPLER: return vk::DescriptorType::eSampler; case DescriptorType::IMAGE_SAMPLED: diff --git a/src/vkcv/DrawcallRecording.cpp b/src/vkcv/DrawcallRecording.cpp index 32ed00e98f7ef72f0c391f61924444c26844869b..d89ace3859717f753534402507a713a78bfb6876 100644 --- a/src/vkcv/DrawcallRecording.cpp +++ b/src/vkcv/DrawcallRecording.cpp @@ -1,7 +1,18 @@ #include <vkcv/DrawcallRecording.hpp> +#include <vkcv/Logger.hpp> namespace vkcv { + vk::IndexType getIndexType(IndexBitCount indexByteCount){ + switch (indexByteCount) { + case IndexBitCount::Bit16: return vk::IndexType::eUint16; + case IndexBitCount::Bit32: return vk::IndexType::eUint32; + default: + vkcv_log(LogLevel::ERROR, "unknown Enum"); + return vk::IndexType::eUint16; + } + } + void recordDrawcall( const DrawcallInfo &drawcall, vk::CommandBuffer cmdBuffer, @@ -33,11 +44,59 @@ namespace vkcv { } if (drawcall.mesh.indexBuffer) { - cmdBuffer.bindIndexBuffer(drawcall.mesh.indexBuffer, 0, vk::IndexType::eUint16); //FIXME: choose proper size + cmdBuffer.bindIndexBuffer(drawcall.mesh.indexBuffer, 0, getIndexType(drawcall.mesh.indexBitCount)); cmdBuffer.drawIndexed(drawcall.mesh.indexCount, drawcall.instanceCount, 0, 0, {}); } else { - cmdBuffer.draw(drawcall.mesh.indexCount, 1, 0, 0, {}); + cmdBuffer.draw(drawcall.mesh.indexCount, drawcall.instanceCount, 0, 0, {}); } } + + + + struct MeshShaderFunctions + { + PFN_vkCmdDrawMeshTasksNV cmdDrawMeshTasks = nullptr; + PFN_vkCmdDrawMeshTasksIndirectNV cmdDrawMeshTasksIndirect = nullptr; + PFN_vkCmdDrawMeshTasksIndirectCountNV cmdDrawMeshTasksIndirectCount = nullptr; + } MeshShaderFunctions; + + void InitMeshShaderDrawFunctions(vk::Device device) + { + MeshShaderFunctions.cmdDrawMeshTasks = PFN_vkCmdDrawMeshTasksNV(device.getProcAddr("vkCmdDrawMeshTasksNV")); + MeshShaderFunctions.cmdDrawMeshTasksIndirect = PFN_vkCmdDrawMeshTasksIndirectNV(device.getProcAddr("vkCmdDrawMeshTasksIndirectNV")); + MeshShaderFunctions.cmdDrawMeshTasksIndirectCount = PFN_vkCmdDrawMeshTasksIndirectCountNV (device.getProcAddr( "vkCmdDrawMeshTasksIndirectCountNV")); + } + + void recordMeshShaderDrawcall( + vk::CommandBuffer cmdBuffer, + vk::PipelineLayout pipelineLayout, + const PushConstants& pushConstantData, + const uint32_t pushConstantOffset, + const MeshShaderDrawcall& drawcall, + const uint32_t firstTask) { + + for (const auto& descriptorUsage : drawcall.descriptorSets) { + cmdBuffer.bindDescriptorSets( + vk::PipelineBindPoint::eGraphics, + pipelineLayout, + descriptorUsage.setLocation, + descriptorUsage.vulkanHandle, + nullptr); + } + + // char* cast because void* does not support pointer arithmetic + const void* drawcallPushConstantData = pushConstantOffset + (char*)pushConstantData.getData(); + + if (pushConstantData.getData()) { + cmdBuffer.pushConstants( + pipelineLayout, + vk::ShaderStageFlagBits::eAll, + 0, + pushConstantData.getSizePerDrawcall(), + drawcallPushConstantData); + } + + MeshShaderFunctions.cmdDrawMeshTasks(VkCommandBuffer(cmdBuffer), drawcall.taskCount, firstTask); + } } diff --git a/src/vkcv/File.cpp b/src/vkcv/File.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6006b90f74e0a41f83483f2a1efbe5bda4c4e9f8 --- /dev/null +++ b/src/vkcv/File.cpp @@ -0,0 +1,60 @@ + +#include "vkcv/File.hpp" + +#include <stdlib.h> + +#ifdef _WIN32 +#include <io.h> +#else +#include <unistd.h> +#endif + +#include "vkcv/Logger.hpp" + +namespace vkcv { + + std::filesystem::path generateTemporaryFilePath() { + std::filesystem::path tmp = generateTemporaryDirectoryPath(); + + if (std::filesystem::is_directory(tmp)) { + return std::filesystem::path(tmp.string() + "W"); // add W for Wambo + } else { + return tmp; + } + } + + std::filesystem::path generateTemporaryDirectoryPath() { + std::error_code code; + auto tmp = std::filesystem::temp_directory_path(code); + + if (tmp.empty()) { + tmp = std::filesystem::current_path(); + } + + char name [16] = "vkcv_tmp_XXXXXX"; + +#ifdef _WIN32 + int err = _mktemp_s(name, 16); + + if (err != 0) { + vkcv_log(LogLevel::ERROR, "Temporary file path could not be generated"); + return ""; + } +#else + int fd = mkstemp(name); // creates a file locally + + if (fd == -1) { + vkcv_log(LogLevel::ERROR, "Temporary file path could not be generated"); + return ""; + } + + close(fd); + remove(name); // removes the local file again +#endif + + return tmp / name; + } + + + +} diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp index 646b3211f761f0c71596fc088b39b784d5f39a5c..4d99422118e8d464ea75d9f013b471f3dd40fd8c 100644 --- a/src/vkcv/ImageManager.hpp +++ b/src/vkcv/ImageManager.hpp @@ -13,6 +13,8 @@ #include "vkcv/ImageConfig.hpp" namespace vkcv { + + bool isDepthImageFormat(vk::Format format); class ImageManager { diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp index 8b1f0b68be3a72f60103ca0dd8136f2c923513a5..244f6723f70e5ea938c005b74b286e192d68443c 100644 --- a/src/vkcv/PipelineManager.cpp +++ b/src/vkcv/PipelineManager.cpp @@ -44,95 +44,190 @@ namespace vkcv 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; + 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; } } vk::CompareOp depthTestToVkCompareOp(DepthTest depthTest) { switch (depthTest) { - case(DepthTest::None): return vk::CompareOp::eAlways; - case(DepthTest::Less): return vk::CompareOp::eLess; - case(DepthTest::LessEqual): return vk::CompareOp::eLessOrEqual; - case(DepthTest::Greater): return vk::CompareOp::eGreater; - case(DepthTest::GreatherEqual): return vk::CompareOp::eGreaterOrEqual; - case(DepthTest::Equal): return vk::CompareOp::eEqual; - default: vkcv_log(vkcv::LogLevel::ERROR, "Unknown depth test enum"); return vk::CompareOp::eAlways; + case(DepthTest::None): return vk::CompareOp::eAlways; + case(DepthTest::Less): return vk::CompareOp::eLess; + case(DepthTest::LessEqual): return vk::CompareOp::eLessOrEqual; + case(DepthTest::Greater): return vk::CompareOp::eGreater; + case(DepthTest::GreatherEqual): return vk::CompareOp::eGreaterOrEqual; + case(DepthTest::Equal): return vk::CompareOp::eEqual; + default: vkcv_log(vkcv::LogLevel::ERROR, "Unknown depth test enum"); return vk::CompareOp::eAlways; } } + + vk::ShaderStageFlagBits shaderStageToVkShaderStage(vkcv::ShaderStage stage) { + switch (stage) { + case vkcv::ShaderStage::VERTEX: return vk::ShaderStageFlagBits::eVertex; + case vkcv::ShaderStage::FRAGMENT: return vk::ShaderStageFlagBits::eFragment; + case vkcv::ShaderStage::GEOMETRY: return vk::ShaderStageFlagBits::eGeometry; + case vkcv::ShaderStage::TESS_CONTROL: return vk::ShaderStageFlagBits::eTessellationControl; + case vkcv::ShaderStage::TESS_EVAL: return vk::ShaderStageFlagBits::eTessellationEvaluation; + case vkcv::ShaderStage::COMPUTE: return vk::ShaderStageFlagBits::eCompute; + case vkcv::ShaderStage::TASK: return vk::ShaderStageFlagBits::eTaskNV; + case vkcv::ShaderStage::MESH: return vk::ShaderStageFlagBits::eMeshNV; + default: vkcv_log(vkcv::LogLevel::ERROR, "Unknown shader stage"); return vk::ShaderStageFlagBits::eAll; + } + } + + bool createPipelineShaderStageCreateInfo( + const vkcv::ShaderProgram& shaderProgram, + ShaderStage stage, + vk::Device device, + vk::PipelineShaderStageCreateInfo* outCreateInfo) { + + assert(outCreateInfo); + std::vector<char> code = shaderProgram.getShader(stage).shaderCode; + vk::ShaderModuleCreateInfo vertexModuleInfo({}, code.size(), reinterpret_cast<uint32_t*>(code.data())); + vk::ShaderModule shaderModule; + if (device.createShaderModule(&vertexModuleInfo, nullptr, &shaderModule) != vk::Result::eSuccess) + return false; + + const static auto entryName = "main"; + + *outCreateInfo = vk::PipelineShaderStageCreateInfo( + {}, + shaderStageToVkShaderStage(stage), + shaderModule, + entryName, + nullptr); + return true; + } PipelineHandle PipelineManager::createPipeline(const PipelineConfig &config, PassManager& passManager) { const vk::RenderPass &pass = passManager.getVkPass(config.m_PassHandle); + const bool existsTaskShader = config.m_ShaderProgram.existsShader(ShaderStage::TASK); + const bool existsMeshShader = config.m_ShaderProgram.existsShader(ShaderStage::MESH); + const bool existsVertexShader = config.m_ShaderProgram.existsShader(ShaderStage::VERTEX); + + const bool validGeometryStages = existsVertexShader || (existsTaskShader && existsMeshShader); + const bool existsFragmentShader = config.m_ShaderProgram.existsShader(ShaderStage::FRAGMENT); - if (!(existsVertexShader && existsFragmentShader)) + if (!validGeometryStages) { - vkcv_log(LogLevel::ERROR, "Requires vertex and fragment shader code"); + vkcv_log(LogLevel::ERROR, "Requires vertex or task and mesh shader"); return PipelineHandle(); } - - // vertex shader stage - std::vector<char> vertexCode = config.m_ShaderProgram.getShader(ShaderStage::VERTEX).shaderCode; - vk::ShaderModuleCreateInfo vertexModuleInfo({}, vertexCode.size(), reinterpret_cast<uint32_t*>(vertexCode.data())); - vk::ShaderModule vertexModule{}; - if (m_Device.createShaderModule(&vertexModuleInfo, nullptr, &vertexModule) != vk::Result::eSuccess) + if (!existsFragmentShader) { + vkcv_log(LogLevel::ERROR, "Requires fragment shader code"); return PipelineHandle(); + } - vk::PipelineShaderStageCreateInfo pipelineVertexShaderStageInfo( - {}, - vk::ShaderStageFlagBits::eVertex, - vertexModule, - "main", - nullptr - ); + std::vector<vk::PipelineShaderStageCreateInfo> shaderStages; + auto destroyShaderModules = [&shaderStages, this] { + for (auto stage : shaderStages) { + m_Device.destroyShaderModule(stage.module); + } + shaderStages.clear(); + }; + + if (existsVertexShader) { + vk::PipelineShaderStageCreateInfo createInfo; + const bool success = createPipelineShaderStageCreateInfo( + config.m_ShaderProgram, + vkcv::ShaderStage::VERTEX, + m_Device, + &createInfo); + + if (success) { + shaderStages.push_back(createInfo); + } + else { + destroyShaderModules(); + return PipelineHandle(); + } + } + + if (existsTaskShader) { + vk::PipelineShaderStageCreateInfo createInfo; + const bool success = createPipelineShaderStageCreateInfo( + config.m_ShaderProgram, + vkcv::ShaderStage::TASK, + m_Device, + &createInfo); + + if (success) { + shaderStages.push_back(createInfo); + } + else { + destroyShaderModules(); + return PipelineHandle(); + } + } + + if (existsMeshShader) { + vk::PipelineShaderStageCreateInfo createInfo; + const bool success = createPipelineShaderStageCreateInfo( + config.m_ShaderProgram, + vkcv::ShaderStage::MESH, + m_Device, + &createInfo); + + if (success) { + shaderStages.push_back(createInfo); + } + else { + destroyShaderModules(); + return PipelineHandle(); + } + } // fragment shader stage - std::vector<char> fragCode = config.m_ShaderProgram.getShader(ShaderStage::FRAGMENT).shaderCode; - vk::ShaderModuleCreateInfo fragmentModuleInfo({}, fragCode.size(), reinterpret_cast<uint32_t*>(fragCode.data())); - vk::ShaderModule fragmentModule{}; - if (m_Device.createShaderModule(&fragmentModuleInfo, nullptr, &fragmentModule) != vk::Result::eSuccess) { - m_Device.destroy(vertexModule); - return PipelineHandle(); + vk::PipelineShaderStageCreateInfo createInfo; + const bool success = createPipelineShaderStageCreateInfo( + config.m_ShaderProgram, + vkcv::ShaderStage::FRAGMENT, + m_Device, + &createInfo); + + if (success) { + shaderStages.push_back(createInfo); + } + else { + destroyShaderModules(); + return PipelineHandle(); + } } - vk::PipelineShaderStageCreateInfo pipelineFragmentShaderStageInfo( - {}, - vk::ShaderStageFlagBits::eFragment, - fragmentModule, - "main", - nullptr - ); - // vertex input state // Fill up VertexInputBindingDescription and VertexInputAttributeDescription Containers std::vector<vk::VertexInputAttributeDescription> vertexAttributeDescriptions; std::vector<vk::VertexInputBindingDescription> vertexBindingDescriptions; - const VertexLayout &layout = config.m_VertexLayout; - - // iterate over the layout's specified, mutually exclusive buffer bindings that make up a vertex buffer - for (const auto &vertexBinding : layout.vertexBindings) - { - vertexBindingDescriptions.emplace_back(vertexBinding.bindingLocation, - vertexBinding.stride, - vk::VertexInputRate::eVertex); - - // iterate over the bindings' specified, mutually exclusive vertex input attachments that make up a vertex - for(const auto &vertexAttachment: vertexBinding.vertexAttachments) - { - vertexAttributeDescriptions.emplace_back(vertexAttachment.inputLocation, - vertexBinding.bindingLocation, - vertexFormatToVulkanFormat(vertexAttachment.format), - vertexAttachment.offset % vertexBinding.stride); + if (existsVertexShader) { + const VertexLayout& layout = config.m_VertexLayout; + + // iterate over the layout's specified, mutually exclusive buffer bindings that make up a vertex buffer + for (const auto& vertexBinding : layout.vertexBindings) + { + vertexBindingDescriptions.emplace_back(vertexBinding.bindingLocation, + vertexBinding.stride, + vk::VertexInputRate::eVertex); + + // iterate over the bindings' specified, mutually exclusive vertex input attachments that make up a vertex + for (const auto& vertexAttachment : vertexBinding.vertexAttachments) + { + vertexAttributeDescriptions.emplace_back(vertexAttachment.inputLocation, + vertexBinding.bindingLocation, + vertexFormatToVulkanFormat(vertexAttachment.format), + vertexAttachment.offset % vertexBinding.stride); + + } + } - } - } + } // Handover Containers to PipelineVertexInputStateCreateIngo Struct vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo( @@ -240,8 +335,7 @@ namespace vkcv vk::PipelineLayout vkPipelineLayout{}; if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess) { - m_Device.destroy(vertexModule); - m_Device.destroy(fragmentModule); + destroyShaderModules(); return PipelineHandle(); } @@ -276,25 +370,28 @@ namespace vkcv 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); - } + vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo( + {}, + static_cast<uint32_t>(dynamicStates.size()), + dynamicStates.data()); + + const bool existsGeometryShader = config.m_ShaderProgram.existsShader(vkcv::ShaderStage::GEOMETRY); + if (existsGeometryShader) { + vk::PipelineShaderStageCreateInfo createInfo; + const bool success = createPipelineShaderStageCreateInfo( + config.m_ShaderProgram, + vkcv::ShaderStage::GEOMETRY, + m_Device, + &createInfo); + + if (success) { + shaderStages.push_back(createInfo); + } + else { + destroyShaderModules(); + return PipelineHandle(); + } + } const vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo( {}, @@ -319,20 +416,11 @@ namespace vkcv vk::Pipeline vkPipeline{}; if (m_Device.createGraphicsPipelines(nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &vkPipeline) != vk::Result::eSuccess) { - m_Device.destroy(vertexModule); - m_Device.destroy(fragmentModule); - if (geometryModule) { - m_Device.destroy(geometryModule); - } - m_Device.destroy(); + destroyShaderModules(); return PipelineHandle(); } - m_Device.destroy(vertexModule); - m_Device.destroy(fragmentModule); - if (geometryModule) { - m_Device.destroy(geometryModule); - } + destroyShaderModules(); const uint64_t id = m_Pipelines.size(); m_Pipelines.push_back({ vkPipeline, vkPipelineLayout, config }); @@ -457,4 +545,4 @@ namespace vkcv 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/QueueManager.cpp b/src/vkcv/QueueManager.cpp index b4891c6be387b817b87f059f4155f5708d4f4710..15e958b0de929e53170324ade27a9b3663a15d6a 100644 --- a/src/vkcv/QueueManager.cpp +++ b/src/vkcv/QueueManager.cpp @@ -27,8 +27,8 @@ namespace vkcv { * @throws std::runtime_error If the requested queues from @p queueFlags are not creatable due to insufficient availability. */ void QueueManager::queueCreateInfosQueueHandles(vk::PhysicalDevice &physicalDevice, - std::vector<float> &queuePriorities, - std::vector<vk::QueueFlagBits> &queueFlags, + const std::vector<float> &queuePriorities, + const std::vector<vk::QueueFlagBits> &queueFlags, std::vector<vk::DeviceQueueCreateInfo> &queueCreateInfos, std::vector<std::pair<int, int>> &queuePairsGraphics, std::vector<std::pair<int, int>> &queuePairsCompute, diff --git a/src/vkcv/SamplerManager.cpp b/src/vkcv/SamplerManager.cpp index a6ebb95b5e237dcd06ed8041b3f16489f7339d6a..792e6f16b4a05af41a164a1eda9dd7423594857e 100644 --- a/src/vkcv/SamplerManager.cpp +++ b/src/vkcv/SamplerManager.cpp @@ -17,7 +17,8 @@ namespace vkcv { SamplerHandle SamplerManager::createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, SamplerMipmapMode mipmapMode, - SamplerAddressMode addressMode) { + SamplerAddressMode addressMode, + float mipLodBias) { vk::Filter vkMagFilter; vk::Filter vkMinFilter; vk::SamplerMipmapMode vkMipmapMode; @@ -81,13 +82,13 @@ namespace vkcv { vkAddressMode, vkAddressMode, vkAddressMode, - 0.0f, + mipLodBias, false, 16.0f, false, vk::CompareOp::eAlways, - 0.0f, - 16.0f, + -1000.0f, + 1000.0f, vk::BorderColor::eIntOpaqueBlack, false ); diff --git a/src/vkcv/SamplerManager.hpp b/src/vkcv/SamplerManager.hpp index 511176d4f87633a8691ca730ecc383e2588d8cf0..aea47a03714b417314a09dfc0be855df31fbb557 100644 --- a/src/vkcv/SamplerManager.hpp +++ b/src/vkcv/SamplerManager.hpp @@ -32,7 +32,8 @@ namespace vkcv { SamplerHandle createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, SamplerMipmapMode mipmapMode, - SamplerAddressMode addressMode); + SamplerAddressMode addressMode, + float mipLodBias); [[nodiscard]] vk::Sampler getVulkanSampler(const SamplerHandle& handle) const; diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp index 025cb388c6880cc8132b454c799d39e2b530ceb3..aea00fb10d579aea0dc5be789ced3e6582b868bf 100644 --- a/src/vkcv/Window.cpp +++ b/src/vkcv/Window.cpp @@ -4,7 +4,9 @@ * @brief Window class to handle a basic rendering surface and input */ +#include <vector> #include <GLFW/glfw3.h> + #include "vkcv/Window.hpp" namespace vkcv {