diff --git a/.gitmodules b/.gitmodules index ef5fa3cfcc738f2bc8dedb0ec219903e71500f49..e270bddb0a91069af1eff869560e8b817f7cc844 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,3 +34,6 @@ [submodule "lib/Vulkan-Hpp"] path = lib/Vulkan-Hpp url = https://github.com/KhronosGroup/Vulkan-Hpp +[submodule "modules/upscaling/lib/NVIDIAImageScaling"] + path = modules/upscaling/lib/NVIDIAImageScaling + url = https://github.com/NVIDIAGameWorks/NVIDIAImageScaling.git diff --git a/modules/shader_compiler/include/vkcv/shader/Compiler.hpp b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp index d4be7384dfffeeb068a13660004be138e62722a1..cabc2a2628557254f0faed222b6b9f5c27429ad1 100644 --- a/modules/shader_compiler/include/vkcv/shader/Compiler.hpp +++ b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp @@ -35,6 +35,7 @@ namespace vkcv::shader { std::string getDefine(const std::string& name) const; void setDefine(const std::string& name, const std::string& value); + }; } diff --git a/modules/upscaling/CMakeLists.txt b/modules/upscaling/CMakeLists.txt index dec392573d31a7348f8440162410b4fc91757b51..6d6e7b987e2e7e448f1e8c5925dc731aafc4f3d0 100644 --- a/modules/upscaling/CMakeLists.txt +++ b/modules/upscaling/CMakeLists.txt @@ -17,6 +17,9 @@ set(vkcv_upscaling_sources ${vkcv_upscaling_include}/vkcv/upscaling/FSRUpscaling.hpp ${vkcv_upscaling_source}/vkcv/upscaling/FSRUpscaling.cpp + + ${vkcv_upscaling_include}/vkcv/upscaling/NISUpscaling.hpp + ${vkcv_upscaling_source}/vkcv/upscaling/NISUpscaling.cpp ) # Setup some path variables to load libraries @@ -26,6 +29,9 @@ set(vkcv_upscaling_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_upscaling_lib}) # Check and load FidelityFX_FSR include(config/FidelityFX_FSR.cmake) +# Check and load NVIDIAImageScaling +include(config/NVIDIAImageScaling.cmake) + # adding source files to the project add_library(vkcv_upscaling ${vkcv_build_attribute} ${vkcv_upscaling_sources}) diff --git a/modules/upscaling/config/NVIDIAImageScaling.cmake b/modules/upscaling/config/NVIDIAImageScaling.cmake new file mode 100644 index 0000000000000000000000000000000000000000..083891c67e369dc783c79f5ac4386ae15e6e3261 --- /dev/null +++ b/modules/upscaling/config/NVIDIAImageScaling.cmake @@ -0,0 +1,15 @@ + +use_git_submodule("${vkcv_upscaling_lib_path}/NVIDIAImageScaling" nvidia_nis_status) + +if (${nvidia_nis_status}) + include_shader(${vkcv_upscaling_lib_path}/NVIDIAImageScaling/NIS/NIS_Scaler.h ${vkcv_upscaling_include} ${vkcv_upscaling_source}) + include_shader(${vkcv_upscaling_lib_path}/NVIDIAImageScaling/NIS/NIS_Main.glsl ${vkcv_upscaling_include} ${vkcv_upscaling_source}) + + list(APPEND vkcv_upscaling_includes ${vkcv_upscaling_lib}/NVIDIAImageScaling/NIS) + + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/NIS_Scaler.h.cxx) + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/NIS_Main.glsl.cxx) + + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/NIS_Scaler.h.hxx) + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/NIS_Main.glsl.hxx) +endif () diff --git a/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp index 4d3316718bf995f5cc8ccddde8c74575beba7745..7b2e96bb62544db29775faa4473d0feccd2e813c 100644 --- a/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp +++ b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp @@ -30,7 +30,6 @@ namespace vkcv::upscaling { class FSRUpscaling : public Upscaling { private: - ComputePipelineHandle m_easuPipeline; ComputePipelineHandle m_rcasPipeline; diff --git a/modules/upscaling/include/vkcv/upscaling/NISUpscaling.hpp b/modules/upscaling/include/vkcv/upscaling/NISUpscaling.hpp new file mode 100644 index 0000000000000000000000000000000000000000..efab311d4fb452285bb3d88262ddbf3fbd04a810 --- /dev/null +++ b/modules/upscaling/include/vkcv/upscaling/NISUpscaling.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include "Upscaling.hpp" + +#include <vkcv/ShaderProgram.hpp> + +namespace vkcv::upscaling { + + class NISUpscaling : public Upscaling { + private: + ComputePipelineHandle m_scalerPipeline; + + DescriptorSetLayoutHandle m_scalerDescriptorSetLayout; + DescriptorSetHandle m_scalerDescriptorSet; + + Buffer<uint8_t> m_scalerConstants; + SamplerHandle m_sampler; + ImageHandle m_coefScaleImage; + ImageHandle m_coefUsmImage; + + uint32_t m_blockWidth; + uint32_t m_blockHeight; + + bool m_hdr; + float m_sharpness; + + public: + explicit NISUpscaling(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/lib/NVIDIAImageScaling b/modules/upscaling/lib/NVIDIAImageScaling new file mode 160000 index 0000000000000000000000000000000000000000..7a468267104585ce5cd683aebd8e4cb74f826807 --- /dev/null +++ b/modules/upscaling/lib/NVIDIAImageScaling @@ -0,0 +1 @@ +Subproject commit 7a468267104585ce5cd683aebd8e4cb74f826807 diff --git a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp index 0f6dfcdeaa3b1efc6f58c1c2d0eb61bbd2090a80..0a5e08acfd543ac8b2f439b423e97823d272e473 100644 --- a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp +++ b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp @@ -1,3 +1,4 @@ + #include "vkcv/upscaling/FSRUpscaling.hpp" #include <stdint.h> @@ -104,7 +105,6 @@ namespace vkcv::upscaling { descriptorBindings.insert(std::make_pair(3, binding_3)); return descriptorBindings; - } template<typename T> @@ -159,10 +159,11 @@ namespace vkcv::upscaling { return false; } - return compiler.compileSource(vkcv::ShaderStage::COMPUTE, - FSR_PASS_GLSL_SHADER.c_str(), - [&directory, &compiled] (vkcv::ShaderStage shaderStage, - const std::filesystem::path& path) { + 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); } @@ -198,6 +199,7 @@ namespace vkcv::upscaling { SamplerMipmapMode::NEAREST, SamplerAddressMode::CLAMP_TO_EDGE )), + m_hdr(false), m_sharpness(0.875f) { vkcv::shader::GLSLCompiler easuCompiler; diff --git a/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..998d7f83e7a05d456dfe544a87953769181eada4 --- /dev/null +++ b/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp @@ -0,0 +1,278 @@ + +#include "vkcv/upscaling/NISUpscaling.hpp" + +#include <NIS_Config.h> + +#include "NIS_Main.glsl.hxx" +#include "NIS_Scaler.h.hxx" + +#include <vkcv/File.hpp> +#include <vkcv/Logger.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> + +namespace vkcv::upscaling { + + static DescriptorBindings getDescriptorBindings() { + DescriptorBindings descriptorBindings = {}; + + auto binding_0 = DescriptorBinding { + 0, + DescriptorType::UNIFORM_BUFFER_DYNAMIC, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_1 = DescriptorBinding{ + 1, + DescriptorType::SAMPLER, + 1, + ShaderStage::COMPUTE + }; + + auto binding_2 = DescriptorBinding { + 2, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_3 = DescriptorBinding{ + 3, + DescriptorType::IMAGE_STORAGE, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_4 = DescriptorBinding { + 4, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_5 = DescriptorBinding { + 5, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false + }; + + descriptorBindings.insert(std::make_pair(0, binding_0)); + descriptorBindings.insert(std::make_pair(1, binding_1)); + descriptorBindings.insert(std::make_pair(2, binding_2)); + descriptorBindings.insert(std::make_pair(3, binding_3)); + descriptorBindings.insert(std::make_pair(4, binding_4)); + descriptorBindings.insert(std::make_pair(5, binding_5)); + + return descriptorBindings; + } + + static ImageHandle createFilterImage(Core &core, + const void* data) { + const size_t rowPitch = kFilterSize * 2; + const size_t imageSize = rowPitch * kPhaseCount; + + Image image = core.createImage( + vk::Format::eR16G16B16A16Sfloat, + kFilterSize / 4, + kPhaseCount + ); + + image.fill(data, imageSize); + + return image.getHandle(); + } + + 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 compileNISShader(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 / "NIS_Scaler.h", NIS_SCALER_H_SHADER)) { + return false; + } + + return compiler.compileSource( + vkcv::ShaderStage::COMPUTE, + NIS_MAIN_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 + ); + } + + NISUpscaling::NISUpscaling(Core &core) : + Upscaling(core), + m_scalerPipeline(), + + m_scalerDescriptorSetLayout(m_core.createDescriptorSetLayout(getDescriptorBindings())), + m_scalerDescriptorSet(m_core.createDescriptorSet(m_scalerDescriptorSetLayout)), + + m_scalerConstants(m_core.createBuffer<uint8_t>( + BufferType::UNIFORM, sizeof(NISConfig), + BufferMemoryType::HOST_VISIBLE + )), + m_sampler(m_core.createSampler( + SamplerFilterType::LINEAR, + SamplerFilterType::LINEAR, + SamplerMipmapMode::NEAREST, + SamplerAddressMode::CLAMP_TO_EDGE + )), + + m_coefScaleImage(createFilterImage(m_core, coef_scale_fp16)), + m_coefUsmImage(createFilterImage(m_core, coef_usm_fp16)), + + m_blockWidth(0), + m_blockHeight(0), + + m_hdr(false), + m_sharpness(0.875f) { + vkcv::shader::GLSLCompiler scalerCompiler; + + scalerCompiler.setDefine("NIS_SCALER", "1"); + scalerCompiler.setDefine("NIS_GLSL", "1"); + + NISOptimizer optimizer (true, NISGPUArchitecture::NVIDIA_Generic); + + m_blockWidth = optimizer.GetOptimalBlockWidth(); + m_blockHeight = optimizer.GetOptimalBlockHeight(); + + scalerCompiler.setDefine("NIS_BLOCK_WIDTH", std::to_string(m_blockWidth)); + scalerCompiler.setDefine("NIS_BLOCK_HEIGHT", std::to_string(m_blockHeight)); + + const uint32_t threadGroupSize = optimizer.GetOptimalThreadGroupSize(); + + scalerCompiler.setDefine("NIS_THREAD_GROUP_SIZE", std::to_string(threadGroupSize)); + + { + ShaderProgram program; + compileNISShader(scalerCompiler, [&program](vkcv::ShaderStage shaderStage, + const std::filesystem::path& path) { + program.addShader(shaderStage, path); + }); + + m_scalerPipeline = m_core.createComputePipeline({program,{ + m_scalerDescriptorSetLayout + }}); + + + DescriptorWrites writes; + writes.uniformBufferWrites.emplace_back( + 0, m_scalerConstants.getHandle(), true + ); + + writes.samplerWrites.emplace_back(1, m_sampler); + writes.sampledImageWrites.emplace_back(4, m_coefScaleImage); + writes.sampledImageWrites.emplace_back(5, m_coefUsmImage); + + m_core.writeDescriptorSet(m_scalerDescriptorSet, writes); + } + } + + void NISUpscaling::recordUpscaling(const CommandStreamHandle &cmdStream, + const ImageHandle &input, + const ImageHandle &output) { + m_core.recordBeginDebugLabel(cmdStream, "vkcv::upscaling::NISUpscaling", { + 0.0f, 1.0f, 0.0f, 1.0f + }); + + 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); + + NISConfig config {}; + NVScalerUpdateConfig( + config, + m_sharpness, + 0, 0, + inputWidth, + inputHeight, + inputWidth, + inputHeight, + 0, 0, + outputWidth, + outputHeight, + outputWidth, + outputHeight, + m_hdr? NISHDRMode::PQ : NISHDRMode::None + ); + + m_scalerConstants.fill( + reinterpret_cast<uint8_t*>(&config), + sizeof(config) + ); + + uint32_t dispatch[3]; + dispatch[0] = (outputWidth + (m_blockWidth - 1)) / m_blockWidth; + dispatch[1] = (outputHeight + (m_blockHeight - 1)) / m_blockHeight; + dispatch[2] = 1; + + m_core.recordBufferMemoryBarrier(cmdStream, m_scalerConstants.getHandle()); + + { + DescriptorWrites writes; + writes.sampledImageWrites.emplace_back(2, input); + writes.storageImageWrites.emplace_back(3, output); + + m_core.writeDescriptorSet(m_scalerDescriptorSet, writes); + } + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_scalerPipeline, + dispatch, + {DescriptorSetUsage(0, m_scalerDescriptorSet, { 0 })}, + PushConstants(0) + ); + + m_core.recordEndDebugLabel(cmdStream); + } + + bool NISUpscaling::isHdrEnabled() const { + return m_hdr; + } + + void NISUpscaling::setHdrEnabled(bool enabled) { + m_hdr = enabled; + } + + float NISUpscaling::getSharpness() const { + return m_sharpness; + } + + void NISUpscaling::setSharpness(float sharpness) { + m_sharpness = (sharpness < 0.0f ? 0.0f : (sharpness > 1.0f ? 1.0f : sharpness)); + } + +} diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp index dd4e0374e7e18477f69fde79e090e3d4c5689aa5..f01b87a79b287609822635ecc67f7541e1a8e3b6 100644 --- a/projects/voxelization/src/main.cpp +++ b/projects/voxelization/src/main.cpp @@ -10,6 +10,7 @@ #include "ShadowMapping.hpp" #include <vkcv/upscaling/FSRUpscaling.hpp> #include <vkcv/upscaling/BilinearUpscaling.hpp> +#include <vkcv/upscaling/NISUpscaling.hpp> #include <vkcv/effects/BloomAndFlaresEffect.hpp> int main(int argc, const char** argv) { @@ -600,8 +601,15 @@ int main(int argc, const char** argv) { bool fsrMipLoadBiasFlagBackup = fsrMipLoadBiasFlag; vkcv::upscaling::BilinearUpscaling upscaling1 (core); + vkcv::upscaling::NISUpscaling upscaling2 (core); - bool bilinearUpscaling = false; + const std::vector<const char*> modeNames = { + "Bilinear Upscaling", + "FSR Upscaling", + "NIS Upscaling" + }; + + int upscalingMode = 0; vkcv::gui::GUI gui(core, windowHandle); @@ -880,10 +888,18 @@ int main(int argc, const char** argv) { core.prepareImageForSampling(cmdStream, swapBuffer); core.recordEndDebugLabel(cmdStream); - if (bilinearUpscaling) { - upscaling1.recordUpscaling(cmdStream, swapBuffer, swapBuffer2); - } else { - upscaling.recordUpscaling(cmdStream, swapBuffer, swapBuffer2); + switch (upscalingMode) { + case 0: + upscaling1.recordUpscaling(cmdStream, swapBuffer, swapBuffer2); + break; + case 1: + upscaling.recordUpscaling(cmdStream, swapBuffer, swapBuffer2); + break; + case 2: + upscaling2.recordUpscaling(cmdStream, swapBuffer, swapBuffer2); + break; + default: + break; } core.prepareImageForStorage(cmdStream, swapchainInput); @@ -946,18 +962,19 @@ int main(int argc, const char** argv) { ImGui::DragFloat("Absorption density", &absorptionDensity, 0.0001); ImGui::DragFloat("Volumetric ambient", &volumetricAmbient, 0.002); - float fsrSharpness = upscaling.getSharpness(); + float sharpness = upscaling.getSharpness(); ImGui::Combo("FSR Quality Mode", &fsrModeIndex, fsrModeNames.data(), fsrModeNames.size()); - ImGui::DragFloat("FSR Sharpness", &fsrSharpness, 0.001, 0.0f, 1.0f); + ImGui::DragFloat("FSR Sharpness", &sharpness, 0.001, 0.0f, 1.0f); ImGui::Checkbox("FSR Mip Lod Bias", &fsrMipLoadBiasFlag); - ImGui::Checkbox("Bilinear Upscaling", &bilinearUpscaling); + ImGui::Combo("Upscaling Mode", &upscalingMode, modeNames.data(), modeNames.size()); if ((fsrModeIndex >= 0) && (fsrModeIndex <= 4)) { fsrMode = static_cast<vkcv::upscaling::FSRQualityMode>(fsrModeIndex); } - upscaling.setSharpness(fsrSharpness); + upscaling.setSharpness(sharpness); + upscaling2.setSharpness(sharpness); if (ImGui::Button("Reload forward pass")) {