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/.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/config/Libraries.cmake b/config/Libraries.cmake index 681f42767b8e1d00da21c7b476712bad98412fbb..512669ce85a96f8cc94d8181994cfe458fa8b604 100644 --- a/config/Libraries.cmake +++ b/config/Libraries.cmake @@ -40,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/include/vkcv/Context.hpp b/include/vkcv/Context.hpp index 3f779572d27dc42f2a29d147d2fbfbd19b0bd929..824713fd1e29cbb8b7e60b22768c0019daaa9938 100644 --- a/include/vkcv/Context.hpp +++ b/include/vkcv/Context.hpp @@ -38,12 +38,11 @@ namespace vkcv [[nodiscard]] const vma::Allocator& getAllocator() const; - static Context create( - const char* applicationName, - uint32_t applicationVersion, - std::vector<vk::QueueFlagBits> queueFlags, - std::vector<const char *> instanceExtensions, - std::vector<const char *> deviceExtensions); + static Context create(const char *applicationName, + uint32_t applicationVersion, + 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 fd2bf56e0766ebc6ae06e76c138f52d4ba41702f..5677dbf6569a182eddba494852d39320f8154711 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 @@ -291,15 +297,21 @@ 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 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 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 60d3430c8d64454592736f0e658c7f8a770ef2e5..260fbbc6a2a577d0d333656a1eff4f7f3f88cd69 100644 --- a/include/vkcv/DrawcallRecording.hpp +++ b/include/vkcv/DrawcallRecording.hpp @@ -19,11 +19,13 @@ namespace vkcv { }; 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 { 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/modules/CMakeLists.txt b/modules/CMakeLists.txt index 72d94364136a04d92d0e4dfa9837048c53664637..4b576e7119ebe769eafd1b6abb033b4fb02a3ec1 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory(meshlet) add_subdirectory(scene) add_subdirectory(shader_compiler) add_subdirectory(testing) +add_subdirectory(upscaling) diff --git a/modules/meshlet/CMakeLists.txt b/modules/meshlet/CMakeLists.txt index 8c6823876d0c7ca2632fa57903427c25986724db..385115de4ab3f6dc7c540fd77e10ddec0ee458b8 100644 --- a/modules/meshlet/CMakeLists.txt +++ b/modules/meshlet/CMakeLists.txt @@ -12,8 +12,8 @@ set(vkcv_meshlet_include ${PROJECT_SOURCE_DIR}/include) 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/Tipsify.hpp + ${vkcv_meshlet_source}/vkcv/meshlet/Tipsify.cpp ) # adding source files to the module diff --git a/modules/meshlet/include/vkcv/meshlet/Tipsify.hpp b/modules/meshlet/include/vkcv/meshlet/Tipsify.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a5bcef76b518eb9629eeb5e90614dfd705edc6ed --- /dev/null +++ b/modules/meshlet/include/vkcv/meshlet/Tipsify.hpp @@ -0,0 +1,22 @@ +#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 + */ + std::vector<uint32_t> 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/Tipsify.cpp b/modules/meshlet/src/vkcv/meshlet/Tipsify.cpp index 281830b9013ee1110338855dc73440e27ab8b563..a41d37c15d81d60c4152c58051ad362ba83c77a6 100644 --- a/modules/meshlet/src/vkcv/meshlet/Tipsify.cpp +++ b/modules/meshlet/src/vkcv/meshlet/Tipsify.cpp @@ -1,4 +1,269 @@ -// -// Created by Dradozer on 20.07.2021. -// +#include <vkcv/Logger.hpp> +#include "vkcv/meshlet/Tipsify.hpp" +#include <iostream> + +const int maxUsedVertices = 128; + +namespace vkcv::meshlet { + + /** + * 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 + * @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) { + + // 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) { + 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 + * @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 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); + } + return nextVertexIndex; + } + + std::vector<uint32_t> 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 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); + + // 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]; + + // 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); + } + + 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 reorderedIndexBuffer; + } +} \ No newline at end of file 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 4a351f41f5fd3186bf3e606ded132c083a63a7e5..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++; } @@ -201,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); @@ -226,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) { @@ -278,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/mesh_shader/resources/shaders/shader.frag b/projects/mesh_shader/resources/shaders/shader.frag index 17bf8f960bb6996f039c4e73e547db802f1ceab4..f4f6982f2089e6c8e102027f3b8763bb38f8e59c 100644 --- a/projects/mesh_shader/resources/shaders/shader.frag +++ b/projects/mesh_shader/resources/shaders/shader.frag @@ -28,5 +28,5 @@ vec3 colorFromIndex(uint i){ void main() { outColor = normalize(passNormal) * 0.5 + 0.5; - //outColor = colorFromIndex(passTaskIndex); + outColor = colorFromIndex(passTaskIndex); } \ No newline at end of file diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp index d7441d0dd9f0a875e70fd01e4182454977d981a8..99529b5305b6979ced4f113ab18c0cc79a53a4e4 100644 --- a/projects/mesh_shader/src/main.cpp +++ b/projects/mesh_shader/src/main.cpp @@ -8,7 +8,7 @@ #include <vkcv/gui/GUI.hpp> #include <vkcv/asset/asset_loader.hpp> #include <vkcv/meshlet/Meshlet.hpp> -//#include <vkcv/meshlet/Tipsify.hpp> +#include <vkcv/meshlet/Tipsify.hpp> struct Plane { glm::vec3 pointOnPlane; @@ -138,10 +138,10 @@ int main(int argc, const char** argv) { 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); -// std::vector<uint32_t> reorderedIndexBuffer32Bit = vkcv::meshlet::tipsifyMesh(indexBuffer32Bit, interleavedVertices.size(), 2); + std::vector<uint32_t> indexBuffer32Bit = vkcv::meshlet::assetLoaderIndicesTo32BitIndices(assetLoaderIndexBuffer.data, assetLoaderIndexBuffer.type); + std::vector<uint32_t> reorderedIndexBuffer32Bit = vkcv::meshlet::tipsifyMesh(indexBuffer32Bit, interleavedVertices.size()); - const auto meshShaderModelData = createMeshShaderModelData(interleavedVertices, indexBuffer32Bit); + const auto meshShaderModelData = createMeshShaderModelData(interleavedVertices, reorderedIndexBuffer32Bit); auto meshShaderVertexBuffer = core.createBuffer<vkcv::meshlet::Vertex>( vkcv::BufferType::STORAGE, @@ -221,7 +221,7 @@ int main(int argc, const char** argv) { vkcv::Buffer<ObjectMatrices> matrixBuffer = core.createBuffer<ObjectMatrices>(vkcv::BufferType::STORAGE, objectCount); vkcv::DescriptorWrites vertexShaderDescriptorWrites; - vertexShaderDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, matrixBuffer.getHandle()) }; + vertexShaderDescriptorWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0, matrixBuffer.getHandle()) }; core.writeDescriptorSet(vertexShaderDescriptorSet, vertexShaderDescriptorWrites); vkcv::PipelineHandle bunnyPipeline = core.createGraphicsPipeline(bunnyPipelineDefinition); @@ -275,14 +275,14 @@ int main(int argc, const char** argv) { vkcv::DescriptorWrites meshShaderWrites; meshShaderWrites.storageBufferWrites = { - vkcv::StorageBufferDescriptorWrite(0, meshShaderVertexBuffer.getHandle()), - vkcv::StorageBufferDescriptorWrite(1, meshShaderIndexBuffer.getHandle()), - vkcv::StorageBufferDescriptorWrite(2, meshletBuffer.getHandle()), - vkcv::StorageBufferDescriptorWrite(4, matrixBuffer.getHandle()), - vkcv::StorageBufferDescriptorWrite(5, meshletBuffer.getHandle()), + 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::UniformBufferDescriptorWrite(3, cameraPlaneBuffer.getHandle()) + vkcv::BufferDescriptorWrite(3, cameraPlaneBuffer.getHandle()), }; core.writeDescriptorSet( meshShaderDescriptorSet, meshShaderWrites); 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 2084700cae62a378b061aab056b3c5bac99b96e4..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; @@ -179,7 +179,7 @@ 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) { @@ -188,13 +188,12 @@ namespace vkcv } 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) { + + Context Context::create(const char *applicationName, + uint32_t applicationVersion, + 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(); @@ -233,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, @@ -248,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 @@ -296,22 +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::PhysicalDeviceFeatures2 deviceFeatures; - deviceFeatures.features.fragmentStoresAndAtomics = true; - deviceFeatures.features.geometryShader = true; - deviceFeatures.features.depthClamp = true; - + 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 meshShading; + vk::PhysicalDeviceMeshShaderFeaturesNV meshShadingFeatures; if (usingMeshShaders) { - meshShading.taskShader = true; - meshShading.meshShader = true; - deviceFeatures.pNext = &meshShading; + meshShadingFeatures.taskShader = true; + meshShadingFeatures.meshShader = true; + deviceFeatures2.setPNext(&meshShadingFeatures); } - - deviceCreateInfo.pNext = &deviceFeatures; + + if (shaderFloat16) { + deviceFeatures2.setPNext(&deviceShaderFloat16Int8Features); + } + + if (storage16bit) { + deviceShaderFloat16Int8Features.setPNext(&device16BitStorageFeatures); + } + + deviceCreateInfo.setPNext(&deviceFeatures2); // Ablauf // qCreateInfos erstellen --> braucht das Device diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 963573a2d1e0766610d4f478e67af3bcbc9956b1..e8e172dd236ac5cb49d0e2caf03599c198a07092 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 { @@ -489,7 +490,8 @@ namespace vkcv pipelineLayout, usage.setLocation, { usage.vulkanHandle }, - {}); + usage.dynamicOffsets + ); } if (pushConstants.getSizePerDrawcall() > 0) { cmdBuffer.pushConstants( @@ -592,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 }; @@ -600,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( @@ -632,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) @@ -660,38 +667,38 @@ 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); } - 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); @@ -700,5 +707,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/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/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;