From fe9d7c44e45f353631924599fe30a3e21f756803 Mon Sep 17 00:00:00 2001 From: Michael Murphy Date: Wed, 27 Sep 2023 13:24:51 -0700 Subject: [PATCH 1/2] Updated to v2.1.0 --- .gitlab-ci.yml | 21 - .gitmodules | 3 - CMakeLists.txt | 16 - ChangeLog.md | 27 + README.md | 8 +- RTXGI | 1 - doc/Confidence.md | 4 +- doc/Integration.md | 36 +- doc/NoiseAndBias.md | 16 +- doc/RestirGI.md | 12 +- doc/RtxdiApplicationBridge.md | 6 +- doc/ShaderAPI-RestirGI.md | 4 +- doc/ShaderAPI.md | 120 +- minimal/shaders/GBufferHelpers.hlsli | 2 +- minimal/shaders/HelperFunctions.hlsli | 2 +- minimal/shaders/PrepareLights.hlsl | 2 +- minimal/shaders/PrimaryRays.hlsli | 2 +- minimal/shaders/Render.hlsl | 41 +- minimal/shaders/RtxdiApplicationBridge.hlsli | 6 +- minimal/shaders/SceneGeometry.hlsli | 2 +- minimal/shaders/ShaderParameters.h | 14 +- minimal/src/PrepareLightsPass.cpp | 20 +- minimal/src/PrepareLightsPass.h | 6 +- minimal/src/RenderPass.cpp | 18 +- minimal/src/RenderPass.h | 10 +- minimal/src/RtxdiResources.cpp | 16 +- minimal/src/RtxdiResources.h | 8 +- minimal/src/main.cpp | 25 +- rtxdi-sdk/CMakeLists.txt | 5 + ...ions.hlsli => DIResamplingFunctions.hlsli} | 1267 ++--------------- rtxdi-sdk/include/rtxdi/DIReservoir.hlsli | 342 +++++ .../include/rtxdi/GIResamplingFunctions.hlsli | 74 +- rtxdi-sdk/include/rtxdi/GIReservoir.hlsli | 24 +- .../include/rtxdi/ImportanceSamplingContext.h | 80 ++ .../rtxdi/InitialSamplingFunctions.hlsli | 665 +++++++++ .../include/rtxdi/LocalLightSelection.hlsli | 117 ++ .../include/rtxdi/PresamplingFunctions.hlsli | 253 ++++ rtxdi-sdk/include/rtxdi/RISBuffer.hlsli | 44 + .../include/rtxdi/RISBufferSegmentAllocator.h | 32 + .../rtxdi/RISBufferSegmentParameters.h | 24 + rtxdi-sdk/include/rtxdi/RTXDI.h | 174 --- rtxdi-sdk/include/rtxdi/ReGIR.h | 174 +++ rtxdi-sdk/include/rtxdi/ReGIRParameters.h | 103 ++ rtxdi-sdk/include/rtxdi/ReGIRSampling.hlsli | 241 ++++ rtxdi-sdk/include/rtxdi/ReSTIRDI.h | 162 +++ rtxdi-sdk/include/rtxdi/ReSTIRDIParameters.h | 126 ++ rtxdi-sdk/include/rtxdi/ReSTIRGI.h | 120 ++ rtxdi-sdk/include/rtxdi/ReSTIRGIParameters.h | 125 ++ rtxdi-sdk/include/rtxdi/Reservoir.hlsli | 239 ---- rtxdi-sdk/include/rtxdi/RtxdiHelpers.hlsli | 260 +--- rtxdi-sdk/include/rtxdi/RtxdiParameters.h | 148 +- rtxdi-sdk/include/rtxdi/RtxdiTypes.h | 2 +- rtxdi-sdk/include/rtxdi/RtxdiUtils.h | 31 + rtxdi-sdk/include/rtxdi/UniformSampling.hlsli | 27 + rtxdi-sdk/shaders/ResamplingCompileTest.glsl | 11 +- rtxdi-sdk/shaders/ResamplingCompileTest.hlsl | 13 +- rtxdi-sdk/src/ImportanceSamplingContext.cpp | 155 ++ rtxdi-sdk/src/RISBufferSegmentAllocator.cpp | 34 + rtxdi-sdk/src/RTXDI.cpp | 358 ----- rtxdi-sdk/src/ReGIR.cpp | 264 ++++ rtxdi-sdk/src/ReSTIRDI.cpp | 194 +++ rtxdi-sdk/src/ReSTIRGI.cpp | 134 ++ rtxdi-sdk/src/RtxdiUtils.cpp | 82 ++ shaders/AccumulationPass.hlsl | 2 +- shaders/BRDFPTParameters.h | 43 + shaders/CMakeLists.txt | 16 +- shaders/CompositingPass.hlsl | 27 +- shaders/ConfidencePass.hlsl | 2 +- shaders/DlssExposure.hlsl | 2 +- shaders/FilterGradientsPass.hlsl | 2 +- shaders/GBufferHelpers.hlsli | 2 +- shaders/GlassPass.hlsl | 2 +- shaders/HelperFunctions.hlsli | 2 +- shaders/LightShaping.hlsli | 2 +- shaders/LightingPasses/BrdfRayTracing.hlsl | 27 +- ...Gradients.hlsl => DIComputeGradients.hlsl} | 18 +- ...Resampling.hlsl => DIFusedResampling.hlsl} | 51 +- ...les.hlsl => DIGenerateInitialSamples.hlsl} | 34 +- ...{ShadeSamples.hlsl => DIShadeSamples.hlsl} | 25 +- .../LightingPasses/DISpatialResampling.hlsl | 62 + ...ampling.hlsl => DITemporalResampling.hlsl} | 39 +- shaders/LightingPasses/GIFinalShading.hlsl | 12 +- shaders/LightingPasses/GIFusedResampling.hlsl | 35 +- .../LightingPasses/GISpatialResampling.hlsl | 22 +- .../LightingPasses/GITemporalResampling.hlsl | 31 +- .../PresampleEnvironmentMap.hlsl | 6 +- shaders/LightingPasses/PresampleLights.hlsl | 7 +- shaders/LightingPasses/PresampleReGIR.hlsl | 9 +- .../RtxdiApplicationBridge.hlsli | 26 +- .../ShadeSecondarySurfaces.hlsl | 77 +- shaders/LightingPasses/ShadingHelpers.hlsli | 20 +- shaders/LightingPasses/SpatialResampling.hlsl | 61 - shaders/PrepareLights.hlsl | 2 +- shaders/PreprocessEnvironmentMap.hlsl | 2 +- shaders/RTXGI/DDGIShaderConfig.h | 12 - shaders/RTXGI/ProbeDebug.hlsl | 147 -- shaders/RTXGI/ProbeInstances.hlsl | 68 - shaders/RTXGI/ProbeTrace.hlsl | 258 ---- shaders/RTXGI/RtxgiHelpers.hlsli | 69 - shaders/RenderEnvironmentMap.hlsl | 2 +- shaders/SceneGeometry.hlsli | 2 +- shaders/ShaderParameters.h | 137 +- shaders/Shaders.cfg | 40 +- shaders/VisualizeConfidence.hlsl | 2 +- shaders/VisualizeHdrSignals.hlsl | 14 +- src/AccumulationPass.cpp | 2 +- src/AccumulationPass.h | 2 +- src/CMakeLists.txt | 17 - src/CompositingPass.cpp | 38 +- src/CompositingPass.h | 9 +- src/ConfidencePass.cpp | 2 +- src/ConfidencePass.h | 2 +- src/DLSS-DX12.cpp | 2 +- src/DLSS-VK.cpp | 2 +- src/DLSS.cpp | 2 +- src/DebugViz/DebugVizPasses.cpp | 10 + src/DebugViz/DebugVizPasses.h | 10 + src/DebugViz/PackedDataVizPass.cpp | 10 + src/DebugViz/PackedDataVizPass.h | 10 + src/FilterGradientsPass.cpp | 2 +- src/FilterGradientsPass.h | 2 +- src/GBufferPass.cpp | 2 +- src/GBufferPass.h | 2 +- src/GenerateMipsPass.cpp | 2 +- src/GenerateMipsPass.h | 2 +- src/GlassPass.cpp | 2 +- src/GlassPass.h | 2 +- src/LightingPasses.cpp | 475 +++--- src/LightingPasses.h | 138 +- src/NrdIntegration.cpp | 2 +- src/NrdIntegration.h | 2 +- src/PrepareLightsPass.cpp | 33 +- src/PrepareLightsPass.h | 11 +- src/Profiler.cpp | 3 - src/Profiler.h | 2 +- src/ProfilerSections.h | 2 - src/RayTracingPass.cpp | 2 +- src/RayTracingPass.h | 2 +- src/RenderEnvironmentMapPass.cpp | 2 +- src/RenderEnvironmentMapPass.h | 2 +- src/RenderTargets.cpp | 2 +- src/RenderTargets.h | 2 +- src/RtxdiResources.cpp | 27 +- src/RtxdiResources.h | 12 +- src/Rtxgi-DX12.cpp | 229 --- src/Rtxgi-VK.cpp | 225 --- src/RtxgiIntegration.cpp | 513 ------- src/RtxgiIntegration.h | 193 --- src/SampleScene.cpp | 23 +- src/SampleScene.h | 19 +- src/Testing.cpp | 49 +- src/Testing.h | 2 +- src/UserInterface.cpp | 590 ++++---- src/UserInterface.h | 52 +- src/VisualizationPass.cpp | 8 +- src/VisualizationPass.h | 6 +- src/VulkanExtensions.cpp | 60 - src/VulkanExtensions.h | 21 - src/main.cpp | 241 ++-- 159 files changed, 5316 insertions(+), 5739 deletions(-) delete mode 160000 RTXGI rename rtxdi-sdk/include/rtxdi/{ResamplingFunctions.hlsli => DIResamplingFunctions.hlsli} (51%) create mode 100644 rtxdi-sdk/include/rtxdi/DIReservoir.hlsli create mode 100644 rtxdi-sdk/include/rtxdi/ImportanceSamplingContext.h create mode 100644 rtxdi-sdk/include/rtxdi/InitialSamplingFunctions.hlsli create mode 100644 rtxdi-sdk/include/rtxdi/LocalLightSelection.hlsli create mode 100644 rtxdi-sdk/include/rtxdi/PresamplingFunctions.hlsli create mode 100644 rtxdi-sdk/include/rtxdi/RISBuffer.hlsli create mode 100644 rtxdi-sdk/include/rtxdi/RISBufferSegmentAllocator.h create mode 100644 rtxdi-sdk/include/rtxdi/RISBufferSegmentParameters.h delete mode 100644 rtxdi-sdk/include/rtxdi/RTXDI.h create mode 100644 rtxdi-sdk/include/rtxdi/ReGIR.h create mode 100644 rtxdi-sdk/include/rtxdi/ReGIRParameters.h create mode 100644 rtxdi-sdk/include/rtxdi/ReGIRSampling.hlsli create mode 100644 rtxdi-sdk/include/rtxdi/ReSTIRDI.h create mode 100644 rtxdi-sdk/include/rtxdi/ReSTIRDIParameters.h create mode 100644 rtxdi-sdk/include/rtxdi/ReSTIRGI.h create mode 100644 rtxdi-sdk/include/rtxdi/ReSTIRGIParameters.h delete mode 100644 rtxdi-sdk/include/rtxdi/Reservoir.hlsli create mode 100644 rtxdi-sdk/include/rtxdi/RtxdiUtils.h create mode 100644 rtxdi-sdk/include/rtxdi/UniformSampling.hlsli create mode 100644 rtxdi-sdk/src/ImportanceSamplingContext.cpp create mode 100644 rtxdi-sdk/src/RISBufferSegmentAllocator.cpp delete mode 100644 rtxdi-sdk/src/RTXDI.cpp create mode 100644 rtxdi-sdk/src/ReGIR.cpp create mode 100644 rtxdi-sdk/src/ReSTIRDI.cpp create mode 100644 rtxdi-sdk/src/ReSTIRGI.cpp create mode 100644 rtxdi-sdk/src/RtxdiUtils.cpp create mode 100644 shaders/BRDFPTParameters.h rename shaders/LightingPasses/{ComputeGradients.hlsl => DIComputeGradients.hlsl} (91%) rename shaders/LightingPasses/{FusedResampling.hlsl => DIFusedResampling.hlsl} (56%) rename shaders/LightingPasses/{GenerateInitialSamples.hlsl => DIGenerateInitialSamples.hlsl} (51%) rename shaders/LightingPasses/{ShadeSamples.hlsl => DIShadeSamples.hlsl} (67%) create mode 100644 shaders/LightingPasses/DISpatialResampling.hlsl rename shaders/LightingPasses/{TemporalResampling.hlsl => DITemporalResampling.hlsl} (54%) delete mode 100644 shaders/LightingPasses/SpatialResampling.hlsl delete mode 100644 shaders/RTXGI/DDGIShaderConfig.h delete mode 100644 shaders/RTXGI/ProbeDebug.hlsl delete mode 100644 shaders/RTXGI/ProbeInstances.hlsl delete mode 100644 shaders/RTXGI/ProbeTrace.hlsl delete mode 100644 shaders/RTXGI/RtxgiHelpers.hlsli delete mode 100644 src/Rtxgi-DX12.cpp delete mode 100644 src/Rtxgi-VK.cpp delete mode 100644 src/RtxgiIntegration.cpp delete mode 100644 src/RtxgiIntegration.h delete mode 100644 src/VulkanExtensions.cpp delete mode 100644 src/VulkanExtensions.h diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ee55b83..78a1ab4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,8 +7,6 @@ variables: TEST_REPO_BRANCH: main # Variables that should be specified from the project settings: -# - GITHUB_USERNAME : username for the private dependencies on GitHub -# - GITHUB_ACCESS_TOKEN : access token for the private dependencies on GitHub # - TEST_REPO_URL : URL of the repository with tests, including the username and token parts if necessary # - ENABLE_JOBS : list of jobs to run, such as "build-linux,test-linux" @@ -20,19 +18,6 @@ variables: # - Python 3.8+ and scikit-image -# Add the GitHub credentials for the submodules -.checkout-submodules: &checkout-submodules - - git submodule set-url RTXGI https://${GITHUB_USERNAME}:${GITHUB_ACCESS_TOKEN}@github.com/NVIDIAGameWorks/RTXGI.git - - git submodule set-url NRD https://${GITHUB_USERNAME}:${GITHUB_ACCESS_TOKEN}@github.com/NVIDIAGameWorks/RayTracingDenoiser.git - - git submodule update --init --recursive - - -# Remove the credentials to avoid storing them in the runner working tree -.cleanup-submodules: &cleanup-submodules - - git submodule set-url RTXGI https://github.com/NVIDIAGameWorks/RTXGI.git - - git submodule set-url NRD https://github.com/NVIDIAGameWorks/RayTracingDenoiser.git - - # Clone the tests repository .clone-tests: &clone-tests - git clone -b ${TEST_REPO_BRANCH} ${TEST_REPO_URL} tests @@ -45,14 +30,11 @@ build-linux: rules: - if: '$ENABLE_JOBS =~ /build-linux/' before_script: - - *checkout-submodules - ./update_dependencies.sh script: - mkdir build && cd build - cmake .. -GNinja - ninja - after_script: - - *cleanup-submodules artifacts: name: "rtxdi-linux-${CI_COMMIT_SHORT_SHA}" paths: @@ -65,7 +47,6 @@ build-windows: rules: - if: '$ENABLE_JOBS =~ /build-windows/' before_script: - - *checkout-submodules - ./update_dependencies.bat script: - ./set_vs_vars.ps1 @@ -73,8 +54,6 @@ build-windows: - cd build - cmake .. -GNinja -DRTXDI_CONSOLE_APP=ON - cmake --build . - after_script: - - *cleanup-submodules artifacts: name: "rtxdi-windows-${CI_COMMIT_SHORT_SHA}" paths: diff --git a/.gitmodules b/.gitmodules index e1ae216..5b0e46a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,6 +7,3 @@ [submodule "DLSS"] path = DLSS url = https://github.com/NVIDIA/DLSS.git -[submodule "RTXGI"] - path = RTXGI - url = https://github.com/NVIDIAGameWorks/RTXGI.git diff --git a/CMakeLists.txt b/CMakeLists.txt index fb7270b..6365789 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,22 +70,6 @@ set(DONUT_PATH "${CMAKE_CURRENT_LIST_DIR}/donut") include(NRD.cmake) include(DLSS.cmake) -if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/RTXGI/CMakeLists.txt") - option(RTXDI_WITH_RTXGI "Enable the RTXGI integration" ON) - - if (RTXDI_WITH_RTXGI) - option(RTXGI_DDGI_RESOURCE_MANAGEMENT "" ON) - option(RTXGI_DDGI_USE_SHADER_CONFIG_FILE "" ON) - - if (UNIX) - set(RTXGI_API_VULKAN_SDK ON) - set(Vulkan_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/donut/nvrhi/thirdparty/Vulkan-Headers/include") - endif() - - add_subdirectory(RTXGI) - endif() -endif() - set(GLSLANG_PATH "" CACHE STRING "Path to glslangValidator for GLSL header verification (optional)") # Temporarily use cxxopts from NVRHI diff --git a/ChangeLog.md b/ChangeLog.md index db0940b..af322f0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,32 @@ # RTXDI SDK Change Log + +## 2.1.0 + +**Release highlights:** + +- Major refactor of C++ API that separates the ReSTIR DI, ReGIR, and ReSTIR GI algorithms into distinct contexts. +- Shader functions and structs renamed to reflect partition of algorithms. +- Added `ImportanceSamplingContext` class that collects all 3 algorithms into a central class to ensure shared state is properly managed. +- UI redone for the rtxdi-sample project's ReSTIR DI, ReGIR, and ReSTIR GI sections. + +**Breaking changes:** + +- C++ API changed completely. Algorithms are now organized by a single context each. +- ReSTIR DI reservoir struct renamed from RTXDI_Reservoir to RTXDI_DIReservoir, with corresponding changes to DI reservoir functions. +- ReSTIR DI resampling functions renamed to have a DI infix to distinguish them from their GI counterparts. +- Shader headers broken down into more files. + +**Fixed issued:** + +- Fixed SPIR-V compiler issues. +- Reorganized UI to reflect algorithm/code path. + +**Misc improvements:** + +- Decoupled boiling filter, bias correction, and several other settings for ReSTIR DI and ReSTIR GI. +- Broke down ReSTIR DI local light sampling code into smaller functions for easier reuse and expansion. + ## 2.0.0 **Release highlights:** diff --git a/README.md b/README.md index 16b499e..b1ed221 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # RTXDI SDK and Sample Applications -Version 2.0.0. +Version 2.1.0. [Change Log](ChangeLog.md) @@ -18,7 +18,7 @@ For more information about RTXDI, see the [NVIDIA Developer Page](https://develo [`rtxdi-sdk`](rtxdi-sdk) contains the SDK source code files that are meant to be included into the application build: - [`rtxdi-sdk/include`](rtxdi-sdk/include) has the include files, both for host code and for shaders -- [`rtxdi-sdk/include/rtxdi/ResamplingFunctions.hlsli`](rtxdi-sdk/include/rtxdi/ResamplingFunctions.hlsli) is the main shader include file that contains the resampling implementation +- [`rtxdi-sdk/include/rtxdi/DIResamplingFunctions.hlsli`](rtxdi-sdk/include/rtxdi/DIResamplingFunctions.hlsli) is the main shader include file that contains the resampling implementation - [`rtxdi-sdk/shaders`](rtxdi-sdk/shaders) has the shader files that are supposed to be compiled through whatever means the application normally uses - [`rtxdi-sdk/src`](rtxdi-sdk/src) has the host code with various utility functions for setting up the parameters and resources for resampling @@ -32,8 +32,6 @@ For more information about RTXDI, see the [NVIDIA Developer Page](https://develo [`DLSS`](DLSS) is a submodule with the [Deep Learning Super-Sampling SDK](https://github.com/NVIDIA/DLSS). -[`RTXGI`](RTXGI) is a submodule with the [RTX Global Illumination SDK](https://github.com/NVIDIAGameWorks/RTXGI). - Additional contents delivered through packman: `dxc` is a recent version of DirectX Shader Compiler; @@ -101,6 +99,8 @@ The RTXDI sample applications can run using D3D12 or Vulkan, which is achieved t By default, the sample apps will run using D3D12 on Windows. To start them in Vulkan mode, add `--vk` to the command line. To compile the sample apps without Vulkan support, set the CMake variable `DONUT_WITH_VULKAN` to `OFF` and re-generate the project. +To enable SPIV-V compileation tests, set the `GLSLANG_PATH` variable in CMake to the path to glslangValidator.exe in your Vulkan installation. + ## Integration See the [Integration Guide](doc/Integration.md). diff --git a/RTXGI b/RTXGI deleted file mode 160000 index db03d1c..0000000 --- a/RTXGI +++ /dev/null @@ -1 +0,0 @@ -Subproject commit db03d1c3eb4fb1d898b3cde6885dd75baf478c22 diff --git a/doc/Confidence.md b/doc/Confidence.md index 4ce94ff..fed6b50 100644 --- a/doc/Confidence.md +++ b/doc/Confidence.md @@ -2,7 +2,7 @@ ## Introduction -Modern denoisers, specifically those provided by the [NRD library](https://github.com/NVIDIAGameWorks/RayTracingDenoiser), are spatio-temporal filters. That means they accumulate lighting information over multiple frames in order to produce a stable output signal. While this approach is very effective for noise reduction, it has a major downside: the output signal often reacts to abrupt changes in the input relatively slowly. That can be seen as light turning on and off smoothly instead of instantly, shadows lagging behind the objects that cast them, and illumination from moving lights being smeared. The NRD denoisers include some heuristics to mitigate these effects, but they're only effective to some degree. +Modern denoisers, specifically those provided by the [NRD library](https://github.com/NVIDIAGameWorks/RayTracingDenoiser), are spatiotemporal filters. That means they accumulate lighting information over multiple frames in order to produce a stable output signal. While this approach is very effective for noise reduction, it has a major downside: the output signal often reacts to abrupt changes in the input relatively slowly. That can be seen as light turning on and off smoothly instead of instantly, shadows lagging behind the objects that cast them, and illumination from moving lights being smeared. The NRD denoisers include some heuristics to mitigate these effects, but they're only effective to some degree. Fortunately, it is possible to provide external hints to the denoisers to tell them where scene lighting has changed, and therefore, where the accumulated history should be reset. NRD calls that the "confidence" inputs, and it's a screen-sized single-channel texture that has 1.0 where the lighting is stable and 0.0 where it's changed. There are two confidence channels, one for diffuse lighting and one for specular. The RTXDI sample application implements a fast and effective solution to compute the confidence channels using the light reservoir information and some ray tracing. @@ -36,7 +36,7 @@ First, the gradients are filtered spatially using a wide blur. The blur size and Second, the filtered gradients are normalized, i.e. luminance differences are divided by the absolute luminance values, and then converted into (0-1) confidence using a simple function. This signal can already be fed into the confidence input of the denoiser, but there is one extra trick. -The gradients are often noisy even after the spatial filtering, which sometimes results in patchy history invalidation. More importantly, singular events like a light turning on or off only create nonzero gradients on one frame. The spatio-temporal nature of ReSTIR leads to noisy and locally biased lighting on that first frame after a significant change. If the denoiser history is reset momentarily and then accumulation starts from scratch, that local bias has a significant weight in the history, resulting for example in a "black dip" effect around a light that has turned off. +The gradients are often noisy even after the spatial filtering, which sometimes results in patchy history invalidation. More importantly, singular events like a light turning on or off only create nonzero gradients on one frame. The spatiotemporal nature of ReSTIR leads to noisy and locally biased lighting on that first frame after a significant change. If the denoiser history is reset momentarily and then accumulation starts from scratch, that local bias has a significant weight in the history, resulting for example in a "black dip" effect around a light that has turned off. The solution to both noisy confidence and local bias is simple: we can apply a short-history temporal filter to the confidence input of the denoiser. When the temporal filter is tuned right, deoniser history invalidations happen smoothly over a few frames and not abruptly. The temporal filter can be very simple; refer to [`ConfidencePass.hlsl`](../shaders/ConfidencePass.hlsl) for a reference implementation. diff --git a/doc/Integration.md b/doc/Integration.md index 06ed700..7f385df 100644 --- a/doc/Integration.md +++ b/doc/Integration.md @@ -1,6 +1,20 @@ +# Three Algorithms in One SDK + +The RTXDI SDK originally contained a single algorithm, ReSTIR DI. Over time, however, the SDK has been expanded to include the ReGIR and ReSTIR GI algorithms. These three algorithms are grouped together because they all deal with importance sampling for path tracing. While something like "RTX IS" might therefore be a clearer name today, RTXDI is kept for historical continuity. Here is a summary of the three algorithms: + +- ReSTIR DI, short for **Re**servoir **S**patio**T**emporal **I**mportance **R**esampling for **D**irect **I**llumination, is a screen space light sampling technique used for illuminating primary surfaces during path tracing. It cheaply selects several lights from an initial distribution, such as a uniform or power-based one, then selects one light of that group based on a more expensive distribution, such as one including geometry, BRDF, and visibility terms, and uses that one for illumination. These samples are then resampled over time and across neighboring pixels to increase effective sample count. +- ReGIR, short for Reservoir-based Grid Importance Sampling, is a world space light sampling technique that can be used for illuminating primary or subsequent surfaces. It samples lights from an initial distribution, such as a uniform or power-based one, for each grid cell. Other algorithms, such as ReSTIR DI and ReSTIR GI, can sample lights from this grid if the surface they are illuminating falls in a grid cell. +- ReSTIR GI, short for **Re**servoir **S**patio**T**emporal **I**mportance **R**esampling for **G**lobal **I**llumination, is a screen space light sampling technique used for illuminating secondary surfaces during path tracing. ReSTIR GI requires an initial light sampling technique be implemented by the path tracer for the secondary surface. In the `rtxdi-sample` project, this method is the initial sampling pass from ReSTIR DI, although any technique may be used. This secondary surface sample is then resampled across other pixels in space and time. + +The algorithms in the SDK are implemented largely independent from one another, and the C++-side of each is centered around an accordingly named context class (`ReSTIRDIContext`, `ReGIRContext`, and `ReSTIRGIContext`). However, ReSTIR DI has a close integration of ReGIR for initial sampling. Likewise, the `rtxdi-sample` project uses ReSTIR DI's light sampling functions to illuminate secondary surfaces during multi-bounce path tracing. + +The SDK also provides structs and functions that are shared between multiple algorithms. For example, the `RtxdiParameters.h` header provides structs relating to the light buffer and global runtime parameters. Likewise, the `PresamplingFunctions.hlsli` file provides presampling functions for creating PDF-textures for both local and environment lights, which can, in turn, be sampled from in the initial sampling passes of ReSTIR DI, ReGIR, and ReSTIR GI for improved sampling quality. + +The SDK collects all three algorithm contexts and the shared state and functionality into a single `ImportanceSamplingContext` class. You can use this class as-is, or you can use it as a guide to cherry-pick the individual algorithm contexts and pieces of shared functionality from it that you want for your particular integration. + # Integrating RTXDI -RTXDI requires a deep integration into the renderer. That is necessary because light sampling relies on several key pieces of functionality that are different in each renderer: +The algorithms in the RTXDI SDK require a deep integration into the renderer. This method of integration is necessary because light sampling relies on several key pieces of functionality that are different in each renderer: - Material model - Scene data representation @@ -8,9 +22,9 @@ RTXDI requires a deep integration into the renderer. That is necessary because l - G-buffer access - Graphics API access -To avoid making overcomplicated abstractions or restrictive requirements, RTXDI follows an approach where the application is responsible for all of the above functionality, and RTXDI only provides the light sampling and resampling math that ties into pieces of that functionality through shader callback functions. This makes the integration process fairly involved, but the developers have complete control over the lighting system of their engines and do not have to ship any extra closed-source libraries with it. +To avoid making overcomplicated abstractions or restrictive requirements, the RTXDI SDK follows an approach where the application is responsible for all of the above functionality, and RTXDI only provides the light sampling and resampling math that ties into pieces of that functionality through shader callback functions. This makes the integration process fairly involved, but the developers have complete control over the lighting system of their engines and do not have to ship any extra closed-source libraries with it. -The functionality provided by RTXDI can be separated into shader-side and host-side. Shader-side functionality is provided through the [`ResamplingFunctions.hlsli`](../rtxdi-sdk/include/rtxdi/ResamplingFunctions.hlsli) include file and documented in [Shader API Reference](ShaderAPI.md). Host-side functionality is a provided through the [`RTXDI.h`](../rtxdi-sdk/include/rtxdi/RTXDI.h) include file and the [`RTXDI.cpp`](../rtxdi-sdk/src/RTXDI.cpp) source file, which mostly provide utility functions that compute buffer sizes and fill shader parameter structures. +The functionality provided by RTXDI can be separated into shader-side and host-side. Shader-side functionality for ReSTIR DI is provided through the [`DIResamplingFunctions.hlsli`](../rtxdi-sdk/include/rtxdi/DIResamplingFunctions.hlsli) include file and documented in [Shader API Reference](ShaderAPI.md). Host-side functionality is provided through the [`ReSTIRDI.h`](../rtxdi-sdk/include/rtxdi/ReSTIRDI.h) include file and the [`ReSTIRDI.cpp`](../rtxdi-sdk/src/ReSTIRDI.cpp) source file, which together form the `ReSTIRDIContext` class. ReGIR's context lives in [`ReGIR.h`](../rtxdi-sdk/include/rtxdi/ReGIR.h), and ReSTIR GI's context lives in [`ReSTIRGI.h`](../rtxdi-sdk/include/rtxdi/ReSTIRGI.h) Functionality that is shared between these algorithms is located in [`RtxdiUtils.h`](../rtxdi-sdk/include/rtxdi/RtxdiUtils.h). The application also needs to provide shader-side and host-side functionality for RTXDI to work. Most importantly, it needs to provide the bridge structures functions, or callbacks, that are used by the RTXDI resampling functions. Those functions' and structures' names start with `RAB_` (for "**R**TXDI-**A**pplication **B**ridge"). For the complete list of functions and structures that need to be implemented, see the [Bridge Reference](RtxdiApplicationBridge.md). Then the application needs to assemble the resampling functions into complete shaders to build the desired pipeline and compile those shaders. Finally, at run time, the application needs to collect the light information, create the necessary buffers, and execute the compiled shaders. @@ -33,11 +47,11 @@ The following resources are used by RTXDI sampling and resampling functions, eit - *Light data buffer*: stores information about all lights in the scene. See the [next section](#lightbuffer) to learn more about this buffer. - *Light index mapping buffer*: stores the mapping of light indices from the previous frame to the current frame and the other way around. -- *Neighbor offset buffer*: stores random pixel offsets for spatial resampling. Initialize using `rtxdi::Context::FillNeighborOffsetBuffer` at creation. +- *Neighbor offset buffer*: stores random pixel offsets for spatial resampling. Initialize using `rtxdi::FillNeighborOffsetBuffer` at creation. - *PDF (Probability Density Function) textures*: these textures store the probability for each light or each environment map pixel being sampled. See the [PDF texture section](#pdf-textures) for more information. - *RIS (Resampled Importance Sampling) buffer*: stores results of pre-sampling passes. Each element of the RIS buffer is a `uint2` where the `.x` component stores the light index, and the `.y` component stores the inverse of its selection PDF. - *RIS light data buffer*: stores information about the lights in each element of the RIS buffer. This buffer is optional and its only purpose is to improve performance by making memory accesses more local in the initial sampling pass. -- *Reservoir buffer*: contains several screen-sized arrays storing one `RTXDI_PackedReservoir` structure per pixel each. The number of these arrays is determined by the complexity of the application resampling pipeline, and the number of final samples per pixel. For a simple use case when the application is doing spatio-tempral resampling with one spatial pass and one sample per pixel, two arrays are sufficient. To compute the array size, use the `rtxdi::Context::GetReservoirBufferElementCount` function and not just the product of screen width and height, because the pixels are stored in the buffer in a block-linear layout. +- *Reservoir buffer*: contains several screen-sized arrays storing one `RTXDI_PackedDIReservoir` structure per pixel each. The number of these arrays is determined by the complexity of the application's resampling pipeline, and the number of final samples per pixel. For a simple use case when the application is doing spatiotemporal resampling with one spatial pass and one sample per pixel, two arrays are sufficient. To compute the array size, use the `rtxdi::ReSTIRDIContext::getReservoirBufferParameters().reservoirArrayPitch` field and not just the product of screen width and height, because the pixels are stored in the buffer in a block-linear layout. These buffers are created by the sample application but are not used by RTXDI, they are only necessary to prepare the light buffer: @@ -52,7 +66,7 @@ Collect information about all lights in the scene into a buffer. That includes m - *Mesh lights* (aka emissive meshes) need to be broken down into triangles, so that each emissive triangle becomes a separate light. See [below](#meshlight) for additional information on mesh processing. - *Primitive lights*, such as spheres, disks, directional lights, or dome lights need to be represented in a polymorphic fashion, so that they can all be stored in the same structure, `RAB_LightInfo`. Triangle lights also need to be polymorphic. **NOTE:** Triangles and any lights that have a position and finite power are called "local" lights in RTXDI; directional lights and domes are called "infinite" lights. - *Environment map* can be importance sampled, in which case it must be represented with a single light in the light buffer, and it must have a PDF texture. -- The information about the numbers of lights of each type and their placement in the buffer is provided to RTXDI through the `rtxdi::FrameParameters` structure. +- The information about the numbers of lights of each type and their placement in the buffer is provided to RTXDI through the `RTXDI_LightBufferParameters` structure. The layout of the light buffer used in the sample application is shown here: @@ -76,7 +90,9 @@ In the sample application, the first pass of PDF texture build for local lights ### 4. Fill the constant buffer structure -Call `rtxdi::Context::FillRuntimeParameters` to fill the constant structure `RTXDI_ResamplingRuntimeParameters` that needs to be provided to almost all RTXDI shader functions. Pass that structure through a constant buffer in your application shaders. +Each pass of the ReSTIR DI algorithm requires settings from a correspondingly named `ReSTIRDI_*Parameters` struct. Fetch these from the ReSTIRDI context by calling the appropriate `rtxdi::ReSTIRDIContext::get*()` functions and place them into a constant buffer. Additionally, some settings are shared between stages and are stored in the `rtxdi::ReSTIRDIContext::getRuntimeParams()`, `rtxdi::ReSTIRDIContext::getBufferIndices()`, and `rtxdi::ReSTIRDIContext::getReservoirBufferParameters()` functions; these, too, must be stored in the constant buffer for each pass of the algorithm and passed to the shader functions accordingly. + +The ReGIR and ReSTIR GI contexts likewise provide `get*()` functions for data that needs to be placed in the constant buffer for their respective shader passes. Note that ReSTIR DI and ReSTIR GI can both make use of ReGIR, so you should make sure to keep the ReGIR constants set for both the ReSTIR DI shader passes and the ReSTIR GI shader passes. ### 5. Pre-sample local lights and environment map (Optional) @@ -118,7 +134,7 @@ Some applications might find it useful to implement a fused resampling pipeline, As mentioned above, mesh lights need to be broken down into individual emissive triangles with precomputed average radiance per triangle. Depending on the engine and its material model, this can be problematic. -The sample application implements a basic mesh light processing pipeline with static (non-deformable) meshes and static (non-procedural) materials. First, it goes over all mesh instances in the scene and enumerates those instances which have a material with either an emissive texture attached, or a nonzero emissive color. For each emissive instance, a task is created, and a number of compute shader threads is allocated; that number is equal to the number of triangles in the mesh. +The sample application implements a basic mesh light processing pipeline with static (non-deformable) meshes and static (non-procedural) materials. First, it goes over all mesh instances in the scene and enumerates those instances which have a material with either an emissive texture attached, or a nonzero emissive color. For each emissive instance, a task is created, and a number of compute shader threads are allocated; that number is equal to the number of triangles in the mesh. The mesh light processing compute shader, [`PrepareLights.hlsl`](../shaders/PrepareLights.hlsl),starts by identifying which mesh and triangle the current thread should be processing. This is done by doing a binary search on the task list and matching the thread index to the index ranges occupied by each mesh instance; those indices are allocated on the host before launching the shader. Then the shader loads the triangle information: vertex positions and texture coordinates. Vertex positions are transformed with the instance matrix, and they become the vertices of the created mesh light. For materials which have an emissive texture, the texture is approximately integrated over the area of the triangle using a single anisotropic texture sample, as shown below. @@ -145,9 +161,9 @@ RTXDI SDK includes a solution for world-space sampling called ReGIR, for *Reserv ![ReGIR Spatial Structures](images/ReGIRCellStructures.png) -The spatial structure is rebuilt on every frame. Every cell is populated with a number of lights, typically on the order of hundreds to thousands. Each light in the cell is selected from the local light pool using RIS, with the target distribution function being average irradiance from the light to any surface within the cell. Therefore, the cells are not very selective, i.e. if there are many lights in or around the cell, the cells will not help choose a relevant light from those. But they will help filter out the lights that are too far away, therefore, will significantly reduce the noise in large scenes. +The spatial structure is rebuilt every frame. Every cell is populated with a number of lights, typically on the order of hundreds to thousands. Each light in the cell is selected from the local light pool using RIS, with the target distribution function being average irradiance from the light to any surface within the cell. Therefore, the cells are not very selective, i.e. if there are many lights in or around the cell, the cells will not help choose a relevant light from those. But they will help filter out the lights that are too far away and will therefore significantly reduce the noise in large scenes. -To build the ReGIR spatial structure, run a compute shader that calls the `RTXDI_PresampleLocalLightsForReGIR` function. That shader should execute between the local light presampling pass (if importance sampling is enabled; it should be) and any uses of the structure. To sample from the structure only, call the `RTXDI_SampleLocalLightsFromReGIR` function that returns a reservoir with the selected light. Shadowing is not evaluated by that function. To combine the ReGIR results with sampling from unordered light pools outside of the ReGIR structure, call `RTXDI_SampleLightsForSurface`. +To build the ReGIR spatial structure, run a compute shader that calls the `RTXDI_PresampleLocalLightsForReGIR` function. That shader should execute between the local light presampling pass (if importance sampling is enabled; it should be) and any uses of the structure. To sample from the structure only, call the `RTXDI_SampleLocalLightsFromReGIR` function that returns a reservoir with the selected light. Shadowing is not evaluated by that function. To combine the ReGIR results with sampling from unordered light pools outside of the ReGIR structure, call `RTXDI_SampleLightsForSurface`. Note that ReGIR can also be used as the initial sample generator for screen-space resampling, or ReSTIR. This leads to reduced noise in the initial samples, and in case of large and distributed scenes, can make the difference between a usable output signal and an output signal that has a lot of boiling. This "ReGIR feeds ReSTIR" mode is the default behavior of the sample application. diff --git a/doc/NoiseAndBias.md b/doc/NoiseAndBias.md index e26e39c..6d347e5 100644 --- a/doc/NoiseAndBias.md +++ b/doc/NoiseAndBias.md @@ -4,7 +4,7 @@ Reducing the noise that comes with sampled lighting is the entire point of ReSTI ## Target PDF -Implementing a good target PDF function for surfaces is the most important thing in an RTXDI integration. The target PDF should be proportional to the outgoing radiance of the final material BSDF for the provided light sample, or approximate it as closely as possible. If the PDF is wrong, no amount of samples will get rid of the noise, because the algorithm will essentially pick the lights for a wrong, perhaps nonexistent, surface. However, as long as the PDF does not have any zeros where it should be nonzero, the results will be unbiased and accumulate to a correct picture given sufficient time, no matter how wrong the PDF actually is. For example, a constant 1.0 is a valid unbiased PDF, just a very poor one. +Implementing a good target PDF function for surfaces is the most important thing in an RTXDI integration. The target PDF should be proportional to the outgoing radiance of the final material BSDF for the provided light sample, or approximate it as closely as possible. If the PDF is wrong, no number of samples will get rid of the noise, because the algorithm will essentially pick the lights for a wrong, perhaps nonexistent, surface. However, as long as the PDF does not have any zeros where it should be nonzero, the results will be unbiased and accumulate to a correct picture given sufficient time, no matter how wrong the PDF actually is. For example, a constant 1.0 is a valid unbiased PDF, just a very poor one. The target PDF is defined as `RAB_GetLightSampleTargetPdfForSurface` in the bridge. The sample application just evaluates the final BRDF in that function, which is almost ideal. The only better solution would be to include the visibility term between the surface and the light sample, but that usually involves ray tracing, which makes it impractical: the PDF might be evaluated dozens of times per pixel per frame. Still, if some sort of visibility approximation is available, consider using it, as long as it is conservative - meaning it doesn't say "occluded" for lights that are actually visible. @@ -20,13 +20,13 @@ RTXDI achieves noise reduction by applying a sequence of importance sampling and Talbot summarizes RIS as PDF interpolation: "We can view M, the number of samples, as a distribution interpolation variable. When M = 1, Y (the resulting samples) is distributed according to p (source PDF). As M approaches infinity, the distribution of Y approaches g (target PDF)." Paraphrasing that, the more source samples we process to produce a single target sample, the closer these samples will be distributed to the target PDF, therefore, we'll get less noisy signal. -The two main use cases for importance resampling in RTXDI are when the *target PDF* is proportional to a surface's reflected radiance, and the *source PDF* is either proportional to the light's emissive power or is coming from a previous resampling step. The former case is used in the initial sampling pass; the latter one is used in the subsequent spatio-temporal resampling pases. +The two main use cases for importance resampling in RTXDI are when the *target PDF* is proportional to a surface's reflected radiance, and the *source PDF* is either proportional to the light's emissive power or is coming from a previous resampling step. The former case is used in the initial sampling pass; the latter one is used in the subsequent spatiotemporal resampling pases. -The initial sampling pass has a straightforward way to control the output noise: specify the number of samples taken from each strategy. There are separate sample counts for local lights (either importance sampled or not), for infinite lights, for the environment map, and for ReGIR where it's available. The more samples are taken, the better the results, but the noise reduction is linear, which means the initial sampling pass alone won't produce a good enough picture for a large number of lights in real-time constraints. It's important, however, to have enough initial samples to avoid boiling that appears when spatio-temporal reuse is applied to a very noisy, sparse signal. Using ReGIR can help with the boiling situation in large scenes with multiple rooms or similarly separated locations. An example of this boiling is shown on the animation below, which was rendered by applying uniform sampling to an environment map with a very bright sun. +The initial sampling pass has a straightforward way to control the output noise: specify the number of samples taken from each strategy. There are separate sample counts for local lights (either importance sampled or not), for infinite lights, for the environment map, and for ReGIR where it's available. The more samples are taken, the better the results, but the noise reduction is linear, which means the initial sampling pass alone won't produce a good enough picture for a large number of lights in real-time constraints. It's important, however, to have enough initial samples to avoid boiling that appears when spatiotemporal reuse is applied to a very noisy, sparse signal. Using ReGIR can help with the boiling situation in large scenes with multiple rooms or similarly separated locations. An example of this boiling is shown on the animation below, which was rendered by applying uniform sampling to an environment map with a very bright sun. ![Boiling example](images/Boiling.gif) -The temporal and spatial resampling passes, or a combined version of those - the spatio-temporal pass, are crucial to noise reduction. Using temporal resampling makes the number of light samples evaluated per pixel increase linearly over time, and adding spatial resampling on top changes that sample count function to an exponential one. The more spatial samples you use, the higher is the base of the exponent, and the faster the noise will fade. But increasing the number of spatial samples also results in reduced performance, and using many samples is not necessary in converged areas - typically, 1-3 samples are sufficient. Many spatial samples are useful mostly in areas of disocclusion, and the process of increasing their count is called "disocclusion boost". The `numDisocclusionBoostSamples` parameter of the spatial and spatio-temporal resampling passes controls how many samples to take in areas that do not have sufficient history length, to help those areas converge faster. +The temporal and spatial resampling passes, or a combined version of those - the spatiotemporal pass, are crucial to noise reduction. Using temporal resampling makes the number of light samples evaluated per pixel increase linearly over time, and adding spatial resampling on top changes that sample count function to an exponential one. The more spatial samples you use, the higher is the base of the exponent, and the faster the noise will fade. But increasing the number of spatial samples also results in reduced performance, and using many samples is not necessary in converged areas - typically, 1-3 samples are sufficient. Many spatial samples are useful mostly in areas of disocclusion, and the process of increasing their count is called "disocclusion boost". The `numDisocclusionBoostSamples` parameter of the spatial and spatiotemporal resampling passes controls how many samples to take in areas that do not have sufficient history length, to help those areas converge faster. The effectiveness of disocclusion boost is illustrated on the image below. It shows the same scene location captured when the camera is strafing from right to left, making a portion of the ground appear from behind the pole on every frame. @@ -34,7 +34,7 @@ The effectiveness of disocclusion boost is illustrated on the image below. It sh ## Denoiser friendliness -Modern spatio-temporal denoisers expect the input noisy signal to have certain properties. The signal should obviously have as low variance as possible to avoid lagging or over-blurring, but at the same time it should converge as quickly as possible when accumulated. The convergence requirement means that all relevant lighting components for a surface should be sampled in the fewest number of frames. For traditional sampling schemes like BRDF sampling or regular NEE, driving the sample generation with blue noise is a reasonable solution, and it achieves a good spatial distribution of samples as well. For ReSTIR, it's not so simple, an RNG (Random Number Generator) doesn't determine all characteristics of the output signal. +Modern spatiotemporal denoisers expect the input noisy signal to have certain properties. The signal should obviously have as low variance as possible to avoid lagging or over-blurring, but at the same time it should converge as quickly as possible when accumulated. The convergence requirement means that all relevant lighting components for a surface should be sampled in the fewest number of frames. For traditional sampling schemes like BRDF sampling or regular NEE, driving the sample generation with blue noise is a reasonable solution, and it achieves a good spatial distribution of samples as well. For ReSTIR, it's not so simple, an RNG (Random Number Generator) doesn't determine all characteristics of the output signal. If a single temporal resampling pass is applied, the output noisy signal will appear fairly stable. Too stable, in fact, and a temporal denoiser will not have enough temporal variation to operate effectively. The denoiser will see that the signal in each pixel has low temporal variance, and decide that it doesn't need to be blurred, so the result will be blotchy. The blotches will also fade in and out slowly, together with the noisy signal. Using a shorter history in ReSTIR helps increase temporal variation, but also increases noise. Another problem with using a single temporal pass is that in motion, the light samples tend to stick together and form clusters of bright pixels, which is even more confusing for the denoiser. These situations are illustrated on the animation below. @@ -46,11 +46,11 @@ Adding a spatial resampling pass after temporal resampling improves the noise pa The term "bias" refers to cases when the sampled signal doesn't correctly approximate the full integral, i.e. when increasing the number of samples doesn't make the sampled signal converge to the right answer. In ReSTIR, bias primarily comes from spatiotemporal reuse, that is, when a surface reuses a light sample from another surface that has a different target PDF for the same light sample. That type of bias is visible as darkening around geometric discontinuities. Other types of bias come from visibility reuse: discarding samples after final shading when they are invisible, and reusing the visibility term for the same light sample between different surfaces. These types are visible as darkening or brightening around edges of shadows. -Bias propagates through spatio-temporal reuse, which means the more old reservoirs are included in the solution for a pixel, the more biased it becomes. It's easy to observe that using more spatial samples leads to a stronger bias, and the fused spatio-temporal resampling approach produces more biased results than separate temporal and spatial passes, because it sources all its additional samples from the previous frame. +Bias propagates through spatiotemporal reuse, which means the more old reservoirs are included in the solution for a pixel, the more biased it becomes. It's easy to observe that using more spatial samples leads to a stronger bias, and the fused spatiotemporal resampling approach produces more biased results than separate temporal and spatial passes, because it sources all its additional samples from the previous frame. -### Spatio-temporal reuse bias +### Spatiotemporal reuse bias -RTXDI has three modes for handling the spatio-temporal reuse bias, controlled by the `biasCorrectionMode` parameter of the resampling passes. The lowest setting, `RTXDI_BIAS_CORRECTION_OFF`, uses the simplest form of reservoir normalization, `1/M`. This mode is the fastest, but it also produces the most biased results, which can even lead to temporary brightening in disocclusion areas. The medium-quality setting, `RTXDI_BIAS_CORRECTION_BASIC`, applies MIS-like (Multiple Importance Sampling) normalization that involves computing the target PDF of the selected light sample for each of the source reservoirs used in resampling. While this mode makes the results more stable, it doesn't correct the bias completely. Complete correction is achieved with the `RTXDI_BIAS_CORRECTION_RAY_TRACED` mode, which, in addition to computing the target PDF for each source reservoir, also verifies that the sample could have been produced by that reservoir. The verification is performed using ray tracing, specifically the `RAB_GetConservativeVisibility` function, which means there is a ray traced for each temporal and spatial sample taken. +RTXDI has three modes for handling the spatiotemporal reuse bias, controlled by the `biasCorrectionMode` parameter of the resampling passes. The lowest setting, `RTXDI_BIAS_CORRECTION_OFF`, uses the simplest form of reservoir normalization, `1/M`. This mode is the fastest, but it also produces the most biased results, which can even lead to temporary brightening in disocclusion areas. The medium-quality setting, `RTXDI_BIAS_CORRECTION_BASIC`, applies MIS-like (Multiple Importance Sampling) normalization that involves computing the target PDF of the selected light sample for each of the source reservoirs used in resampling. While this mode makes the results more stable, it doesn't correct the bias completely. Complete correction is achieved with the `RTXDI_BIAS_CORRECTION_RAY_TRACED` mode, which, in addition to computing the target PDF for each source reservoir, also verifies that the sample could have been produced by that reservoir. The verification is performed using ray tracing, specifically the `RAB_GetConservativeVisibility` function, which means there is a ray traced for each temporal and spatial sample taken. One important aspect of bias correction is using correct temporal data. Since the bias correction math needs to know how likely the selected sample was to be generated for the original surface, it needs to compute that probability using the surface data, light data, and visibility data from the previous frame. Previous frame surface data should be easy to get by keeping the previous G-buffer around. Previous frame light data could be more difficult as it requires storing all the light data from the previous frame and computing an index mapping from the current frame to the previous frame. Finally, previous frame visibility requires keeping the previous frame TLAS and BLAS for deformable or dynamic meshes, and in case visibility can be affected by materials such as alpha masks, also keeping the previous material and UV data. Failing to use correct temporal data will result in darkening bias when things are moving, which might be an acceptable compromise to reduce implementation complexity. diff --git a/doc/RestirGI.md b/doc/RestirGI.md index f1eebf8..09f919b 100644 --- a/doc/RestirGI.md +++ b/doc/RestirGI.md @@ -2,13 +2,13 @@ ## What is ReSTIR GI -ReSTIR GI is a spatio-temporal resampling algorithm that is applied to samples of surfaces that produce indirect lighting. For comparison, regular ReSTIR "DI" operates on samples of lights that affect primary surfaces; ReSTIR GI converts every surface found by tracing BRDF-sampled rays from the primary surface into a light sample. +ReSTIR GI is a spatiotemporal resampling algorithm that is applied to samples of surfaces that produce indirect lighting. For comparison, regular ReSTIR DI operates on samples of lights that affect primary surfaces; ReSTIR GI converts every surface found by tracing BRDF-sampled rays from the primary surface into a light sample. ![ReSTIR GI](images/ReSTIRGI.png) ## Integration model -Our implementation of ReSTIR GI follows the same integration model as ReSTIR "DI", with the resampling math encapsulated in a number of high-level functions that call various functions in the bridge for application interoperability. The application is expected to call these high-level functions from its own shaders. +The RTXDI SDK's implementation of ReSTIR GI follows the same integration model as ReSTIR DI, with the resampling math encapsulated in a number of high-level functions that call various functions in the bridge for application interoperability. The application is expected to call these high-level functions from its own shaders. The functions and structures necessary for ReSTIR GI implementation are defined in two header files: [`GIResamplingFunctions.hlsli`](../rtxdi-sdk/include/RTXDI/GIResamplingFunctions.hlsli) and [`GIReservoir.hlsli`](../rtxdi-sdk/include/RTXDI/GIReservoir.hlsli). See the [API reference](ShaderAPI-RestirGI.md) for more information on the ReSTIR GI functions and structures. @@ -33,9 +33,9 @@ The following bridge functions are ReSTIR GI specific: ReSTIR GI only needs two GPU resources to operate: -- *Reservoir buffer*: a structured buffer with `RTXDI_PackedGIReservoir` structures. The number of these structures can be calculated using the `rtxdi::Context::GetReservoirBufferElementCount()` function, multiplied by the number of frame-sized buffers that you need to implement the desired pipeline (typically 1 or 2). +- *Reservoir buffer*: a structured buffer with `RTXDI_PackedGIReservoir` structures. The number of these structures can be calculated using the `rtxdi::ReSTIRGI::getReservoirBufferParameters().reservoirArrayPitch` field, multiplied by the number of frame-sized buffers that you need to implement the desired pipeline (typically 1 or 2). -- *Neighbor offset buffer*: a buffer with offsets used for the spatial and spatio-temporal resampling passes. The same buffer is used for ReSTIR DI, and it can be filled using the `rtxdi::Context::FillNeighborOffsetBuffer(...)` function at creation time. +- *Neighbor offset buffer*: a buffer with offsets used for the spatial and spatiotemporal resampling passes. The same buffer is used for ReSTIR DI, and it can be filled using the `rtxdi::FillNeighborOffsetBuffer(...)` function at creation time. ## Implementation basics @@ -47,7 +47,7 @@ Before an implementation of ReSTIR GI in a renderer can be started, a regular fo Note that the radiance of secondary surfaces is non-directional, which means that ReSTIR GI converts all secondary surfaces into Lambertian reflectors or emitters. It is possible to extend the implementation with some directionality information, such as spherical harmonics or even storing some information about the secondary BRDF and the light(s) used to shade it, but that would make the implementation much more expensive, while the difference will be limited to effects like caustics, and normally ReSTIR GI isn't powerful enough to resolve caustics. For the latter reason, the BRDF of secondary surfaces should be limited to avoid producing sharp caustics, such as by clamping the roughness to higher values. -Once the secondary surfaces or paths have been traced and shaded, the application should create a ReSTIR GI reservoir from it using the `RTXDI_MakeGIReservoir` function. That reservoir can be stored in a reservoir buffer for further resampling, or passed directly into a temporal or a spatio-temporal resampling function. See [ShadeSecondarySurfaces.hlsl](../shaders/LightingPasses/ShadeSecondarySurfaces.hlsl) for an example. +Once the secondary surfaces or paths have been traced and shaded, the application should create a ReSTIR GI reservoir from it using the `RTXDI_MakeGIReservoir` function. That reservoir can be stored in a reservoir buffer for further resampling, or passed directly into a temporal or a spatiotemporal resampling function. See [ShadeSecondarySurfaces.hlsl](../shaders/LightingPasses/ShadeSecondarySurfaces.hlsl) for an example. GI reservoirs created after path tracing can be immediately used for shading, without any resampling. This is a useful mode to verify that the math is correct, that no throughput or visibility term is left behind when ReSTIR GI is inserted into the pipeline. In the basic version (no MIS), the application should take the final GI reservoir for the current pixel and shade the primary surface using that reservoir as a regular emissive surface sample, multiplying the results with `weightSum` from the reservoir. The shading result replaces the original radiance produced by the path tracer. See [GIFinalShading.hlsl](../shaders/LightingPasses/GIFinalShading.hlsl) for an example. @@ -55,6 +55,6 @@ GI reservoirs created after path tracing can be immediately used for shading, wi PrimaryReflectedIndirectRadiance = primarySurface.BRDF(LightVector, ViewVector) * reservoir.radiance * reservoir.weightSum -Once the GI reservoir creation and shading are working correctly, resampling passes can be inserted between these two stages. It can be a combination of temporal and spatial passes, or a fused spatio-temporal pass. In the former case, the [temporal pass](../shaders/LightingPasses/GITemporalResampling.hlsl) can be performed in the same shader that does path tracing and/or initial shading, and the [spatial pass](../shaders/LightingPasses/GISpatialResampling.hlsl) can be performed in the same shader that does final shading - or they can be separate shaders, whichever works faster. The sample application uses the separate approach. In the case of fused [spatio-temporal resampling](../shaders/LightingPasses/GIFusedResampling.hlsl), all passes can be done in the same shader, which is probably the easiest way to integrate ReSTIR GI into a path tracer, but it's likely not great for performance. +Once the GI reservoir creation and shading are working correctly, resampling passes can be inserted between these two stages. It can be a combination of temporal and spatial passes, or a fused spatiotemporal pass. In the former case, the [temporal pass](../shaders/LightingPasses/GITemporalResampling.hlsl) can be performed in the same shader that does path tracing and/or initial shading, and the [spatial pass](../shaders/LightingPasses/GISpatialResampling.hlsl) can be performed in the same shader that does final shading - or they can be separate shaders, whichever works faster. The sample application uses the separate approach. In the case of fused [spatiotemporal resampling](../shaders/LightingPasses/GIFusedResampling.hlsl), all passes can be done in the same shader, which is probably the easiest way to integrate ReSTIR GI into a path tracer, but it's likely not great for performance. In the final shading pass, multiple importance sampling (MIS) can be applied to recover some image quality lost on shiny surfaces. ReSTIR doesn't work well on specular surfaces with low roughness: the results may look like a more stable, but sparser noise pattern than the original signal, and that's bad for denoising. It's better to combine the ReSTIR GI output with its input in a MIS fashion and use the input signal on surfaces with low roughness. An example of such MIS scheme can be found in the sample application, in the [GIFinalShading.hlsl](../shaders/LightingPasses/GIFinalShading.hlsl) file - see the `GetMISWeight` function and its uses. \ No newline at end of file diff --git a/doc/RtxdiApplicationBridge.md b/doc/RtxdiApplicationBridge.md index fb2f6d5..3e38933 100644 --- a/doc/RtxdiApplicationBridge.md +++ b/doc/RtxdiApplicationBridge.md @@ -1,7 +1,7 @@ # RTXDI Application Bridge -The application must implement a number of structures and functions on the shader side that are necessary for the RTXDI resampling functions to operate. These structures and functions must be declared before including the main RTXDI header file, [`ResamplingFunctions.hlsli`](../rtxdi-sdk/include/rtxdi/ResamplingFunctions.hlsli). +The application must implement a number of structures and functions on the shader side that are necessary for the RTXDI resampling functions to operate. These structures and functions must be declared before including the main RTXDI header file, [`DIResamplingFunctions.hlsli`](../rtxdi-sdk/include/rtxdi/DIResamplingFunctions.hlsli). A reference implementation of the bridge functions and structures with support for multiple light types and fractional visibility (translucency) can be found in [`RtxdiApplicationBridge.hlsli`](../shaders/LightingPasses/RtxdiApplicationBridge.hlsli). This implementation uses some functionality defined in other header files, most notably, polymorphic lights are implemented in [`PolymorphicLight.hlsli`](../shaders/PolymorphicLight.hlsli). @@ -164,7 +164,7 @@ This function is used in the spatial resampling functions for ray traced bias co Same visibility ray tracing as [`RAB_GetConservativeVisibility`](#rab_getconservativevisibility) but for surfaces and light samples originating from the previous frame. -When the previous frame TLAS and BLAS data is available, the implementation should use that previous data and the `previousSurface` parameter. When the previous acceleration structures are not available, the implementation should use the `currentSurface` parameter, but that will make the results temporarily biased and, in some cases, more noisy. Specifically, the fused spatio-temporal resampling algorithm will produce very noisy results on animated objects. +When the previous frame TLAS and BLAS data is available, the implementation should use that previous data and the `previousSurface` parameter. When the previous acceleration structures are not available, the implementation should use the `currentSurface` parameter, but that will make the results temporarily biased and, in some cases, more noisy. Specifically, the fused spatiotemporal resampling algorithm will produce very noisy results on animated objects. ## BRDF Sampling Related Functions @@ -183,7 +183,7 @@ Computes the probability of a particular direction being sampled from the enviro ### `RAB_EvaluateLocalLightSourcePdf` -`float RAB_EvaluateLocalLightSourcePdf(RTXDI_ResamplingRuntimeParameters params, uint lightIndex)` +`float RAB_EvaluateLocalLightSourcePdf(uint lightIndex)` Computes the probability of a particular light being sampled from the local light pool with importance sampling, based on the local light PDF texture. diff --git a/doc/ShaderAPI-RestirGI.md b/doc/ShaderAPI-RestirGI.md index 8449d5d..1fce407 100644 --- a/doc/ShaderAPI-RestirGI.md +++ b/doc/ShaderAPI-RestirGI.md @@ -108,7 +108,7 @@ Adds a reservoir with one sample into this reservoir. Returns `true` if the new Performs normalization of the reservoir after streaming. After this function is called, the reservoir's `weightSum` field becomes its inverse PDF that can be used for shading or for further reservoir combinations. -The `normalizationNumerator` and `normalizationDenominator` parameters specify the normalization scale for bias correction. Basic applications like streaming of initial light samples will set the numerator to 1.0 and the denominator to M (the number of samples in the reservoir). Spatio-temporal resampling will normally compute the numerator and denominator by weighing the final selected sample against the original surfaces used in resampling. +The `normalizationNumerator` and `normalizationDenominator` parameters specify the normalization scale for bias correction. Basic applications like streaming of initial light samples will set the numerator to 1.0 and the denominator to M (the number of samples in the reservoir). Spatiotemporal resampling will normally compute the numerator and denominator by weighing the final selected sample against the original surfaces used in resampling. **Note:** unlike with direct lighting reservoirs, the GI reservoirs do not store the target PDF. In order for `RTXDI_FinalizeGIResampling` to work correctly, the denominator must include the target PDF of the selected sample! @@ -206,7 +206,7 @@ For more information on the members of the `RTXDI_GISpatialResamplingParameters` const RTXDI_GISpatioTemporalResamplingParameters stparams, const RTXDI_ResamplingRuntimeParameters params) -Implements the core functionality of a combined spatio-temporal resampling pass. This is similar to a sequence of `RTXDI_GITemporalResampling` and `RTXDI_GISpatialResampling`, with the exception that the input reservoirs are all taken from the previous frame. This function is useful for implementing a lighting solution in a single shader, which generates the initial samples, applies spatio-temporal resampling, and shades the final samples. +Implements the core functionality of a combined spatiotemporal resampling pass. This is similar to a sequence of `RTXDI_GITemporalResampling` and `RTXDI_GISpatialResampling`, with the exception that the input reservoirs are all taken from the previous frame. This function is useful for implementing a lighting solution in a single shader, which generates the initial samples, applies spatiotemporal resampling, and shades the final samples. ### `RTXDI_GIBoilingFilter` diff --git a/doc/ShaderAPI.md b/doc/ShaderAPI.md index b0d2b01..1d58d7c 100644 --- a/doc/ShaderAPI.md +++ b/doc/ShaderAPI.md @@ -1,6 +1,6 @@ # RTXDI Shader API -Most of the RTXDI functionality is implemented in shaders. To use this functionality, include the [`ResamplingFunctions.hlsli`](../rtxdi-sdk/include/rtxdi/ResamplingFunctions.hlsli) file into your shader source code, after defining (or at least declaring) the [bridge functions](RtxdiApplicationBridge.md). +Most of the RTXDI functionality is implemented in shaders. To use this functionality, include the [`DIResamplingFunctions.hlsli`](../rtxdi-sdk/include/rtxdi/DIResamplingFunctions.hlsli) file into your shader source code, after defining (or at least declaring) the [bridge functions](RtxdiApplicationBridge.md). Below is the list of shader structures and functions provided by RTXDI that can be useful to applications. Some internal functions are not shown. @@ -21,11 +21,11 @@ Define this macro to `0` in order to disable the pre-sampling features and requi ### `RTXDI_ENABLE_STORE_RESERVOIR` -Define this macro to `0` in order to remove the `RTXDI_StoreReservoir` function. This is useful in shaders that have read-only access to the light reservoir buffer, e.g. for debugging purposes. +Define this macro to `0` in order to remove the `RTXDI_StoreDIReservoir` function. This is useful in shaders that have read-only access to the light reservoir buffer, e.g. for debugging purposes. ### `RTXDI_LIGHT_RESERVOIR_BUFFER` -Define this macro to a resource name for the reservoir buffer, which should have HLSL type `RWStructuredBuffer`. +Define this macro to a resource name for the reservoir buffer, which should have HLSL type `RWStructuredBuffer`. ### `RTXDI_NEIGHBOR_OFFSETS_BUFFER` @@ -41,17 +41,13 @@ Define this macro to a resource name for the RIS buffer, which should have HLSL ## Structures -### `RTXDI_ResamplingRuntimeParameters` - -This structure contains runtime parameters that are accepted by most functions. It can be passed from the CPU side directly through a constant buffer. It is declared in [`RtxdiParameters.h`](../rtxdi-sdk/include/rtxdi/RtxdiParameters.h), which can be included into shader code as well as host-side C++ code. To fill an instance of `RTXDI_ResamplingRuntimeParameters` with valid data, call the `rtxdi::Context::FillRuntimeParameters(...)` function on every frame. - -### `RTXDI_PackedReservoir` +### `RTXDI_PackedDIReservoir` A compact representation of a single light reservoir that should be stored in a structured buffer. -### `RTXDI_Reservoir` +### `RTXDI_DIReservoir` -This structure represents a single light reservoir that stores the weights, the sample ref, sample count (M), and visibility for reuse. It can be serialized into `RTXDI_PackedReservoir` for storage using the `RTXDI_PackReservoir` function, and deserialized from that representation using the `RTXDI_UnpackReservoir` function. +This structure represents a single light reservoir that stores the weights, the sample ref, sample count (M), and visibility for reuse. It can be serialized into `RTXDI_PackedDIReservoir` for storage using the `RTXDI_PackDIReservoir` function, and deserialized from that representation using the `RTXDI_UnpackDIReservoir` function. ### `RTXDI_SampleParameters` @@ -60,73 +56,73 @@ A collection of parameters describing the overall sampling algorithm, i.e. how m ## Reservoir Functions -### `RTXDI_EmptyReservoir` +### `RTXDI_EmptyDIReservoir` - RTXDI_Reservoir RTXDI_EmptyReservoir() + RTXDI_DIReservoir RTXDI_EmptyDIReservoir() Returns an empty reservoir object. -### `RTXDI_IsValidReservoir` +### `RTXDI_IsValidDIReservoir` - bool RTXDI_IsValidReservoir(const RTXDI_Reservoir reservoir) + bool RTXDI_IsValidDIReservoir(const RTXDI_DIReservoir reservoir) Returns `true` if the provided reservoir contains a valid light sample. -### `RTXDI_GetReservoirLightIndex` +### `RTXDI_GetDIReservoirLightIndex` - uint RTXDI_GetReservoirLightIndex(const RTXDI_Reservoir reservoir) + uint RTXDI_GetDIReservoirLightIndex(const RTXDI_DIReservoir reservoir) -Returns the light index stored in the reservoir. For empty reservoirs it will return 0, which could be a valid light index, so a call to `RTXDI_IsValidReservoir` is necessary to determine if the reservoir is empty or not. +Returns the light index stored in the reservoir. For empty reservoirs it will return 0, which could be a valid light index, so a call to `RTXDI_IsValidDIReservoir` is necessary to determine if the reservoir is empty or not. -### `RTXDI_GetReservoirSampleUV` +### `RTXDI_GetDIReservoirSampleUV` - float2 RTXDI_GetReservoirSampleUV(const RTXDI_Reservoir reservoir) + float2 RTXDI_GetDIReservoirSampleUV(const RTXDI_DIReservoir reservoir) Returns the sample UV stored in the reservoir. -### `RTXDI_GetReservoirInvPdf` +### `RTXDI_GetDIReservoirInvPdf` - float RTXDI_GetReservoirInvPdf(const RTXDI_Reservoir reservoir) + float RTXDI_GetDIReservoirInvPdf(const RTXDI_DIReservoir reservoir) Returns the inverse PDF of the reservoir. This value should be used to scale the results of surface shading using the reservoir. -### `RTXDI_LoadReservoir` +### `RTXDI_LoadDIReservoir` - RTXDI_Reservoir RTXDI_LoadReservoir( - RTXDI_ResamplingRuntimeParameters params, + RTXDI_DIReservoir RTXDI_LoadDIReservoir( + RTXDI_DIReservoirBufferParameters params, uint2 reservoirPosition, uint reservoirArrayIndex) Loads and unpacks a reservoir from the provided reservoir storage buffer. The buffer normally contains multiple 2D arrays of reservoirs, corresponding to screen pixels, so the function takes the reservoir position and array index and translates those to the buffer index. -### `RTXDI_StoreReservoir` +### `RTXDI_StoreDIReservoir` - void RTXDI_StoreReservoir( - const RTXDI_Reservoir reservoir, - RTXDI_ResamplingRuntimeParameters params, + void RTXDI_StoreDIReservoir( + const RTXDI_DIReservoir reservoir, + RTXDI_DIReservoirBufferParameters params, uint2 reservoirPosition, uint reservoirArrayIndex) -Packs and stores the reservoir into the provided reservoir storage buffer. Buffer addressing works similar to `RTXDI_LoadReservoir`. +Packs and stores the reservoir into the provided reservoir storage buffer. Buffer addressing works similar to `RTXDI_LoadDIReservoir`. -### `RTXDI_StoreVisibilityInReservoir` +### `RTXDI_StoreVisibilityInDIReservoir` - void RTXDI_StoreVisibilityInReservoir( - inout RTXDI_Reservoir reservoir, + void RTXDI_StoreVisibilityInDIReservoir( + inout RTXDI_DIReservoir reservoir, float3 visibility, bool discardIfInvisible) Stores the visibility term in a compressed form in the reservoir. This function should be called when a shadow ray is cast between a surface and a light sample in the initial or final shading passes. The `discardIfInvisible` parameter controls whether the reservoir should be reset to an invalid state if the visibility is zero, which reduces noise; it's safe to use that for the initial samples, but discarding samples when their final visibility is zero may result in darkening bias. -### `RTXDI_GetReservoirVisibility` +### `RTXDI_GetDIReservoirVisibility` struct RTXDI_VisibilityReuseParameters { uint maxAge; float maxDistance; }; - bool RTXDI_GetReservoirVisibility( - const RTXDI_Reservoir reservoir, + bool RTXDI_GetDIReservoirVisibility( + const RTXDI_DIReservoir reservoir, const RTXDI_VisibilityReuseParameters params, out float3 o_visibility) @@ -140,7 +136,7 @@ Using higher threshold values for distance and age result in a higher degree of ### `RTXDI_StreamSample` bool RTXDI_StreamSample( - inout RTXDI_Reservoir reservoir, + inout RTXDI_DIReservoir reservoir, uint lightIndex, float2 uv, float random, @@ -151,11 +147,11 @@ Adds one light sample to the reservoir. Returns `true` if the sample was selecte This function implements Algorithm (3) from the ReSTIR paper, "Streaming RIS using weighted reservoir sampling". -### `RTXDI_CombineReservoirs` +### `RTXDI_CombineDIReservoirs` bool RTXDI_CombineReservoirs( - inout RTXDI_Reservoir reservoir, - const RTXDI_Reservoir newReservoir, + inout RTXDI_DIReservoir reservoir, + const RTXDI_DIReservoir newReservoir, float random, float targetPdf) @@ -166,21 +162,21 @@ This function implements Algorithm (4) from the ReSTIR paper, "Combining the str ### `RTXDI_FinalizeResampling` void RTXDI_FinalizeResampling( - inout RTXDI_Reservoir reservoir, + inout RTXDI_DIReservoir reservoir, float normalizationNumerator, float normalizationDenominator) Performs normalization of the reservoir after streaming. After this function is called, the reservoir's `weightSum` field becomes its inverse PDF that can be used for shading or for further reservoir combinations. -The `normalizationNumerator` and `normalizationDenominator` parameters specify the normalization scale for bias correction. Basic applications like streaming of initial light samples will set the numerator to 1.0 and the denominator to M (the number of samples in the reservoir). Spatio-temporal resampling will normally compute the numerator and denominator by weighing the final selected sample against the original surfaces used in resampling. +The `normalizationNumerator` and `normalizationDenominator` parameters specify the normalization scale for bias correction. Basic applications like streaming of initial light samples will set the numerator to 1.0 and the denominator to M (the number of samples in the reservoir). Spatiotemporal resampling will normally compute the numerator and denominator by weighing the final selected sample against the original surfaces used in resampling. This function implements Equation (6) from the ReSTIR paper. ### `RTXDI_InternalSimpleResample` ``` bool RTXDI_InternalSimpleResample( - inout RTXDI_Reservoir reservoir, - const RTXDI_Reservoir newReservoir, + inout RTXDI_DIReservoir reservoir, + const RTXDI_DIReservoir newReservoir, float random, float targetPdf = 1.0, float sampleNormalization = 1.0, @@ -246,7 +242,7 @@ The weights of lights relative to ReGIR cells are computed using the [`RAB_GetLi ### `RTXDI_SampleLocalLights` - RTXDI_Reservoir RTXDI_SampleLocalLights( + RTXDI_DIReservoir RTXDI_SampleLocalLights( inout RAB_RandomSamplerState rng, inout RAB_RandomSamplerState coherentRng, RAB_Surface surface, @@ -260,7 +256,7 @@ The proposals are picked from a RIS buffer tile that's picked using `coherentRng ### `RTXDI_SampleLocalLightsFromReGIR` - RTXDI_Reservoir RTXDI_SampleLocalLightsFromReGIR( + RTXDI_DIReservoir RTXDI_SampleLocalLightsFromReGIR( inout RAB_RandomSamplerState rng, inout RAB_RandomSamplerState coherentRng, RAB_Surface surface, @@ -275,7 +271,7 @@ The ReGIR cells are matched to the surface with jitter applied, and the magnitud ### `RTXDI_SampleInfiniteLights` - RTXDI_Reservoir RTXDI_SampleInfiniteLights( + RTXDI_DIReservoir RTXDI_SampleInfiniteLights( inout RAB_RandomSamplerState rng, RAB_Surface surface, uint numSamples, @@ -286,7 +282,7 @@ Selects one infinite light sample using RIS with `numSamples` proposals weighted ### `RTXDI_SampleEnvironmentMap` - RTXDI_Reservoir RTXDI_SampleEnvironmentMap( + RTXDI_DIReservoir RTXDI_SampleEnvironmentMap( inout RAB_RandomSamplerState rng, inout RAB_RandomSamplerState coherentRng, RAB_Surface surface, @@ -301,7 +297,7 @@ The proposals are picked from a RIS buffer tile, similar to [`RTXDI_SampleLocalL ### `RTXDI_SampleBrdf` ``` - RTXDI_Reservoir RTXDI_SampleBrdf( + RTXDI_DIReservoir RTXDI_SampleBrdf( inout RAB_RandomSamplerState rng, RAB_Surface surface, RTXDI_SampleParameters sampleParams, @@ -317,11 +313,11 @@ Depending on the application provided ray trace function, if a local light is hi ### `RTXDI_StreamNeighborWithPairwiseMIS` ``` - bool RTXDI_StreamNeighborWithPairwiseMIS(inout RTXDI_Reservoir reservoir, + bool RTXDI_StreamNeighborWithPairwiseMIS(inout RTXDI_DIReservoir reservoir, float random, - const RTXDI_Reservoir neighborReservoir, + const RTXDI_DIReservoir neighborReservoir, const RAB_Surface neighborSurface, - const RTXDI_Reservoir canonicalReservor, + const RTXDI_DIReservoir canonicalReservoir, const RAB_Surface canonicalSurface, const uint numberOfNeighborsInStream) ``` @@ -338,9 +334,9 @@ See Chapter 9.1 of https://digitalcommons.dartmouth.edu/dissertations/77/, espec ### `RTXDI_StreamCanonicalWithPairwiseStep` ``` - bool RTXDI_StreamCanonicalWithPairwiseStep(inout RTXDI_Reservoir reservoir, + bool RTXDI_StreamCanonicalWithPairwiseStep(inout RTXDI_DIReservoir reservoir, float random, - const RTXDI_Reservoir canonicalReservoir, + const RTXDI_DIReservoir canonicalReservoir, const RAB_Surface canonicalSurface) ``` @@ -355,7 +351,7 @@ compensates for this overweighting, but it can only happen after all neighbors h ### `RTXDI_SampleLightsForSurface` ``` - RTXDI_Reservoir RTXDI_SampleLightsForSurface( + RTXDI_DIReservoir RTXDI_SampleLightsForSurface( inout RAB_RandomSamplerState rng, inout RAB_RandomSamplerState coherentRng, RAB_Surface surface, @@ -379,10 +375,10 @@ This function is a combination of `RTXDI_SampleLocalLightsFromReGIR` (or `RTXDI_ bool enableVisibilityShortcut; bool enablePermutationSampling; }; - RTXDI_Reservoir RTXDI_TemporalResampling( + RTXDI_DIReservoir RTXDI_TemporalResampling( uint2 pixelPosition, RAB_Surface surface, - RTXDI_Reservoir curSample, + RTXDI_DIReservoir curSample, RAB_RandomSamplerState rng, RTXDI_TemporalResamplingParameters tparams, RTXDI_ResamplingRuntimeParameters params, @@ -408,10 +404,10 @@ For more information on the members of the `RTXDI_TemporalResamplingParameters` float depthThreshold; float normalThreshold; }; - RTXDI_Reservoir RTXDI_SpatialResampling( + RTXDI_DIReservoir RTXDI_SpatialResampling( uint2 pixelPosition, RAB_Surface centerSurface, - RTXDI_Reservoir centerSample, + RTXDI_DIReservoir centerSample, RAB_RandomSamplerState rng, RTXDI_SpatialResamplingParameters sparams, RTXDI_ResamplingRuntimeParameters params, @@ -440,17 +436,17 @@ For more information on the members of the `RTXDI_SpatialResamplingParameters` s bool enableVisibilityShortcut; bool enablePermutationSampling; }; - RTXDI_Reservoir RTXDI_SpatioTemporalResampling( + RTXDI_DIReservoir RTXDI_SpatioTemporalResampling( uint2 pixelPosition, RAB_Surface surface, - RTXDI_Reservoir curSample, + RTXDI_DIReservoir curSample, RAB_RandomSamplerState rng, RTXDI_SpatioTemporalResamplingParameters stparams, RTXDI_ResamplingRuntimeParameters params, out int2 temporalSamplePixelPos, inout RAB_LightSample selectedLightSample) -Implements the core functionality of a combined spatio-temporal resampling pass. This is similar to a sequence of `RTXDI_TemporalResampling` and `RTXDI_SpatialResampling`, with the exception that the input reservoirs are all taken from the previous frame. This function is useful for implementing a lighting solution in a single shader, which generates the initial samples, applies spatio-temporal resampling, and shades the final samples. +Implements the core functionality of a combined spatiotemporal resampling pass. This is similar to a sequence of `RTXDI_TemporalResampling` and `RTXDI_SpatialResampling`, with the exception that the input reservoirs are all taken from the previous frame. This function is useful for implementing a lighting solution in a single shader, which generates the initial samples, applies spatiotemporal resampling, and shades the final samples. ### `RTXDI_BoilingFilter` @@ -458,7 +454,7 @@ Implements the core functionality of a combined spatio-temporal resampling pass. uint2 LocalIndex, float filterStrength, RTXDI_ResamplingRuntimeParameters params, - inout RTXDI_Reservoir state) + inout RTXDI_DIReservoir state) Applies a boiling filter over all threads in the compute shader thread group. This filter attempts to reduce boiling by removing reservoirs whose weight is significantly higher than the weights of their neighbors. Essentially, when some lights are important for a surface but they are also unlikely to be located in the initial sampling pass, ReSTIR will try to hold on to these lights by spreading them around, and if such important lights are sufficiently rare, the result will look like light bubbles appearing and growing, then fading. This filter attempts to detect and remove such rare lights, trading boiling for bias. diff --git a/minimal/shaders/GBufferHelpers.hlsli b/minimal/shaders/GBufferHelpers.hlsli index 7347a05..cfffc92 100644 --- a/minimal/shaders/GBufferHelpers.hlsli +++ b/minimal/shaders/GBufferHelpers.hlsli @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/minimal/shaders/HelperFunctions.hlsli b/minimal/shaders/HelperFunctions.hlsli index 100c407..6fb2a3f 100644 --- a/minimal/shaders/HelperFunctions.hlsli +++ b/minimal/shaders/HelperFunctions.hlsli @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/minimal/shaders/PrepareLights.hlsl b/minimal/shaders/PrepareLights.hlsl index dd497d8..7f9c12b 100644 --- a/minimal/shaders/PrepareLights.hlsl +++ b/minimal/shaders/PrepareLights.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/minimal/shaders/PrimaryRays.hlsli b/minimal/shaders/PrimaryRays.hlsli index 7441f70..5526c40 100644 --- a/minimal/shaders/PrimaryRays.hlsli +++ b/minimal/shaders/PrimaryRays.hlsli @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/minimal/shaders/Render.hlsl b/minimal/shaders/Render.hlsl index d9bfb67..90440ea 100644 --- a/minimal/shaders/Render.hlsl +++ b/minimal/shaders/Render.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -10,17 +10,18 @@ #pragma pack_matrix(row_major) +#define RTXDI_ENABLE_PRESAMPLING 0 #include "RtxdiApplicationBridge.hlsli" -#define RTXDI_ENABLE_PRESAMPLING 0 -#include +#include +#include #include "PrimaryRays.hlsli" [numthreads(RTXDI_SCREEN_SPACE_GROUP_SIZE, RTXDI_SCREEN_SPACE_GROUP_SIZE, 1)] void main(uint2 pixelPosition : SV_DispatchThreadID) { - const RTXDI_ResamplingRuntimeParameters params = g_Const.runtimeParams; + const RTXDI_LightBufferParameters lightBufferParams = g_Const.lightBufferParams; // Trace the primary ray PrimarySurfaceOutput primary = TracePrimaryRay(pixelPosition); @@ -32,7 +33,7 @@ void main(uint2 pixelPosition : SV_DispatchThreadID) u_GBufferDiffuseAlbedo[pixelPosition] = Pack_R11G11B10_UFLOAT(primary.surface.diffuseAlbedo); u_GBufferSpecularRough[pixelPosition] = Pack_R8G8B8A8_Gamma_UFLOAT(float4(primary.surface.specularF0, primary.surface.roughness)); - RTXDI_Reservoir reservoir = RTXDI_EmptyReservoir(); + RTXDI_DIReservoir reservoir = RTXDI_EmptyDIReservoir(); if (RAB_IsSurfaceValid(primary.surface)) { @@ -40,7 +41,6 @@ void main(uint2 pixelPosition : SV_DispatchThreadID) RAB_RandomSamplerState rng = RAB_InitRandomSampler(pixelPosition, 1); RTXDI_SampleParameters sampleParams = RTXDI_InitSampleParameters( - 0, // reGIR samples g_Const.numInitialSamples, // local light samples 0, // infinite light samples 0, // environment map samples @@ -50,14 +50,14 @@ void main(uint2 pixelPosition : SV_DispatchThreadID) // Generate the initial sample RAB_LightSample lightSample = RAB_EmptyLightSample(); - RTXDI_Reservoir localReservoir = RTXDI_SampleLocalLights(rng, rng, primary.surface, - sampleParams, params, lightSample); - RTXDI_CombineReservoirs(reservoir, localReservoir, 0.5, localReservoir.targetPdf); + RTXDI_DIReservoir localReservoir = RTXDI_SampleLocalLights(rng, rng, primary.surface, + sampleParams, ReSTIRDI_LocalLightSamplingMode_UNIFORM, lightBufferParams.localLightBufferRegion, lightSample); + RTXDI_CombineDIReservoirs(reservoir, localReservoir, 0.5, localReservoir.targetPdf); // Resample BRDF samples. RAB_LightSample brdfSample = RAB_EmptyLightSample(); - RTXDI_Reservoir brdfReservoir = RTXDI_SampleBrdf(rng, primary.surface, sampleParams, params, brdfSample); - bool selectBrdf = RTXDI_CombineReservoirs(reservoir, brdfReservoir, RAB_GetNextRandom(rng), brdfReservoir.targetPdf); + RTXDI_DIReservoir brdfReservoir = RTXDI_SampleBrdf(rng, primary.surface, sampleParams, lightBufferParams, brdfSample); + bool selectBrdf = RTXDI_CombineDIReservoirs(reservoir, brdfReservoir, RAB_GetNextRandom(rng), brdfReservoir.targetPdf); if (selectBrdf) { lightSample = brdfSample; @@ -67,13 +67,13 @@ void main(uint2 pixelPosition : SV_DispatchThreadID) reservoir.M = 1; // BRDF was generated with a trace so no need to trace visibility again - if (RTXDI_IsValidReservoir(reservoir) && !selectBrdf) + if (RTXDI_IsValidDIReservoir(reservoir) && !selectBrdf) { // See if the initial sample is visible from the surface if (!RAB_GetConservativeVisibility(primary.surface, lightSample)) { // If not visible, discard the sample (but keep the M) - RTXDI_StoreVisibilityInReservoir(reservoir, 0, true); + RTXDI_StoreVisibilityInDIReservoir(reservoir, 0, true); } } @@ -82,7 +82,7 @@ void main(uint2 pixelPosition : SV_DispatchThreadID) { // Fill out the parameter structure. // Mostly use literal constants for simplicity. - RTXDI_SpatioTemporalResamplingParameters stparams; + RTXDI_DISpatioTemporalResamplingParameters stparams; stparams.screenSpaceMotion = primary.motionVector; stparams.sourceBufferIndex = g_Const.inputBufferIndex; stparams.maxHistoryLength = 20; @@ -94,24 +94,25 @@ void main(uint2 pixelPosition : SV_DispatchThreadID) stparams.samplingRadius = 32; stparams.enableVisibilityShortcut = true; stparams.enablePermutationSampling = true; + stparams.discountNaiveSamples = false; // This variable will receive the position of the sample reused from the previous frame. // It's only needed for gradient evaluation, ignore it here. int2 temporalSamplePixelPos = -1; // Call the resampling function, update the reservoir and lightSample variables - reservoir = RTXDI_SpatioTemporalResampling(pixelPosition, primary.surface, reservoir, - rng, stparams, params, temporalSamplePixelPos, lightSample); + reservoir = RTXDI_DISpatioTemporalResampling(pixelPosition, primary.surface, reservoir, + rng, g_Const.runtimeParams, g_Const.restirDIReservoirBufferParams, stparams, temporalSamplePixelPos, lightSample); } float3 shadingOutput = 0; // Shade the surface with the selected light sample - if (RTXDI_IsValidReservoir(reservoir)) + if (RTXDI_IsValidDIReservoir(reservoir)) { // Compute the correctly weighted reflected radiance shadingOutput = ShadeSurfaceWithLightSample(lightSample, primary.surface) - * RTXDI_GetReservoirInvPdf(reservoir); + * RTXDI_GetDIReservoirInvPdf(reservoir); // Test if the selected light is visible from the surface bool visibility = RAB_GetConservativeVisibility(primary.surface, lightSample); @@ -120,7 +121,7 @@ void main(uint2 pixelPosition : SV_DispatchThreadID) if (!visibility) { shadingOutput = 0; - RTXDI_StoreVisibilityInReservoir(reservoir, 0, true); + RTXDI_StoreVisibilityInDIReservoir(reservoir, 0, true); } } @@ -136,5 +137,5 @@ void main(uint2 pixelPosition : SV_DispatchThreadID) u_ShadingOutput[pixelPosition] = 0; } - RTXDI_StoreReservoir(reservoir, params, pixelPosition, g_Const.outputBufferIndex); + RTXDI_StoreDIReservoir(reservoir, g_Const.restirDIReservoirBufferParams, pixelPosition, g_Const.outputBufferIndex); } \ No newline at end of file diff --git a/minimal/shaders/RtxdiApplicationBridge.hlsli b/minimal/shaders/RtxdiApplicationBridge.hlsli index c1f5d41..4f1abd3 100644 --- a/minimal/shaders/RtxdiApplicationBridge.hlsli +++ b/minimal/shaders/RtxdiApplicationBridge.hlsli @@ -40,7 +40,7 @@ Buffer t_NeighborOffsets : register(t21); StructuredBuffer t_GeometryInstanceToLight : register(t22); // Screen-sized UAVs -RWStructuredBuffer u_LightReservoirs : register(u0); +RWStructuredBuffer u_LightReservoirs : register(u0); RWTexture2D u_ShadingOutput : register(u1); RWTexture2D u_GBufferDepth : register(u2); RWTexture2D u_GBufferNormals : register(u3); @@ -131,10 +131,10 @@ float RAB_EvaluateEnvironmentMapSamplingPdf(float3 L) return 0; } -float RAB_EvaluateLocalLightSourcePdf(RTXDI_ResamplingRuntimeParameters params, uint lightIndex) +float RAB_EvaluateLocalLightSourcePdf(uint lightIndex) { // Uniform pdf - return 1.0 / params.localLightParams.numLocalLights; + return 1.0 / g_Const.lightBufferParams.localLightBufferRegion.numLights; } RayDesc setupVisibilityRay(RAB_Surface surface, RAB_LightSample lightSample, float offset = 0.001) diff --git a/minimal/shaders/SceneGeometry.hlsli b/minimal/shaders/SceneGeometry.hlsli index f8a5664..f910659 100644 --- a/minimal/shaders/SceneGeometry.hlsli +++ b/minimal/shaders/SceneGeometry.hlsli @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/minimal/shaders/ShaderParameters.h b/minimal/shaders/ShaderParameters.h index 1994164..06de21d 100644 --- a/minimal/shaders/ShaderParameters.h +++ b/minimal/shaders/ShaderParameters.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -13,7 +13,7 @@ #include #include -#include +#include #define RTXDI_GRID_BUILD_GROUP_SIZE 256 #define RTXDI_SCREEN_SPACE_GROUP_SIZE 8 @@ -42,16 +42,18 @@ struct ResamplingConstants { PlanarViewConstants view; PlanarViewConstants prevView; - RTXDI_ResamplingRuntimeParameters runtimeParams; - + RTXDI_RuntimeParameters runtimeParams; + RTXDI_LightBufferParameters lightBufferParams; + RTXDI_ReservoirBufferParameters restirDIReservoirBufferParams; + uint frameIndex; uint numInitialSamples; uint numSpatialSamples; - uint pad0; + uint pad1; uint numInitialBRDFSamples; float brdfCutoff; - uint2 pad1; + uint2 pad2; uint enableResampling; uint unbiasedMode; diff --git a/minimal/src/PrepareLightsPass.cpp b/minimal/src/PrepareLightsPass.cpp index 2056196..30ad9a4 100644 --- a/minimal/src/PrepareLightsPass.cpp +++ b/minimal/src/PrepareLightsPass.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -102,10 +102,9 @@ void PrepareLightsPass::CountLightsInScene(uint32_t& numEmissiveMeshes, uint32_t } } -void PrepareLightsPass::Process( - nvrhi::ICommandList* commandList, - rtxdi::FrameParameters& outFrameParameters) +RTXDI_LightBufferParameters PrepareLightsPass::Process(nvrhi::ICommandList* commandList) { + RTXDI_LightBufferParameters outLightBufferParams; commandList->beginMarker("PrepareLights"); std::vector tasks; @@ -145,12 +144,12 @@ void PrepareLightsPass::Process( commandList->writeBuffer(m_GeometryInstanceToLightBuffer, geometryInstanceToLight.data(), geometryInstanceToLight.size() * sizeof(uint32_t)); - outFrameParameters.firstLocalLight = 0; - outFrameParameters.numLocalLights = lightBufferOffset; - outFrameParameters.firstInfiniteLight = 0; - outFrameParameters.numInfiniteLights = 0; - outFrameParameters.environmentLightIndex = RTXDI_INVALID_LIGHT_INDEX; - outFrameParameters.environmentLightPresent = false; + outLightBufferParams.localLightBufferRegion.firstLightIndex = 0; + outLightBufferParams.localLightBufferRegion.numLights = lightBufferOffset; + outLightBufferParams.infiniteLightBufferRegion.firstLightIndex = 0; + outLightBufferParams.infiniteLightBufferRegion.numLights = 0; + outLightBufferParams.environmentLightParams.lightIndex = RTXDI_INVALID_LIGHT_INDEX; + outLightBufferParams.environmentLightParams.lightPresent = false; commandList->writeBuffer(m_TaskBuffer, tasks.data(), tasks.size() * sizeof(PrepareLightsTask)); @@ -166,4 +165,5 @@ void PrepareLightsPass::Process( commandList->dispatch(dm::div_ceil(lightBufferOffset, 256)); commandList->endMarker(); + return outLightBufferParams; } diff --git a/minimal/src/PrepareLightsPass.h b/minimal/src/PrepareLightsPass.h index d4ed322..7f582c5 100644 --- a/minimal/src/PrepareLightsPass.h +++ b/minimal/src/PrepareLightsPass.h @@ -11,7 +11,7 @@ #pragma once #include -#include +#include #include @@ -55,7 +55,5 @@ class PrepareLightsPass void CreateBindingSet(RtxdiResources& resources); void CountLightsInScene(uint32_t& numEmissiveMeshes, uint32_t& numEmissiveTriangles); - void Process( - nvrhi::ICommandList* commandList, - rtxdi::FrameParameters& outFrameParameters); + RTXDI_LightBufferParameters Process(nvrhi::ICommandList* commandList); }; diff --git a/minimal/src/RenderPass.cpp b/minimal/src/RenderPass.cpp index d527981..81bc102 100644 --- a/minimal/src/RenderPass.cpp +++ b/minimal/src/RenderPass.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include using namespace donut::math; #include "../shaders/ShaderParameters.h" @@ -141,30 +141,32 @@ void RenderPass::CreatePipeline() void RenderPass::Render( nvrhi::ICommandList* commandList, - rtxdi::Context& context, + rtxdi::ReSTIRDIContext& context, const donut::engine::IView& view, const donut::engine::IView& previousView, const Settings& localSettings, - const rtxdi::FrameParameters& frameParameters) + const RTXDI_LightBufferParameters& lightBufferParams) { ResamplingConstants constants = {}; - constants.frameIndex = frameParameters.frameIndex; + constants.frameIndex = context.getFrameIndex(); view.FillPlanarViewConstants(constants.view); previousView.FillPlanarViewConstants(constants.prevView); - context.FillRuntimeParameters(constants.runtimeParams, frameParameters); constants.enableResampling = localSettings.enableResampling; constants.unbiasedMode = localSettings.unbiasedMode; constants.numInitialSamples = localSettings.numInitialSamples; constants.numInitialBRDFSamples = localSettings.numInitialBRDFSamples; constants.numSpatialSamples = localSettings.numSpatialSamples; + constants.restirDIReservoirBufferParams = context.getReservoirBufferParameters(); + constants.lightBufferParams = lightBufferParams; + constants.runtimeParams.neighborOffsetMask = context.getStaticParameters().NeighborOffsetCount - 1; + constants.runtimeParams.activeCheckerboardField = 0; - constants.inputBufferIndex = !(frameParameters.frameIndex & 1); - constants.outputBufferIndex = frameParameters.frameIndex & 1; + constants.inputBufferIndex = !(context.getFrameIndex() & 1); + constants.outputBufferIndex = context.getFrameIndex() & 1; commandList->writeBuffer(m_ConstantBuffer, &constants, sizeof(constants)); - commandList->beginMarker("Render"); nvrhi::ComputeState state; diff --git a/minimal/src/RenderPass.h b/minimal/src/RenderPass.h index 98cde5c..33bc1c6 100644 --- a/minimal/src/RenderPass.h +++ b/minimal/src/RenderPass.h @@ -13,6 +13,7 @@ #include #include #include +#include "rtxdi/ReSTIRDIParameters.h" namespace donut::engine { @@ -25,10 +26,7 @@ namespace donut::engine namespace rtxdi { - struct FrameParameters; - class Context; - struct ResamplingSettings; - struct ContextParameters; + class ReSTIRDIContext; } class RenderTargets; @@ -83,11 +81,11 @@ class RenderPass void Render( nvrhi::ICommandList* commandList, - rtxdi::Context& context, + rtxdi::ReSTIRDIContext& context, const donut::engine::IView& view, const donut::engine::IView& previousView, const Settings& localSettings, - const rtxdi::FrameParameters& frameParameters); + const RTXDI_LightBufferParameters& lightBufferParams); void NextFrame(); }; diff --git a/minimal/src/RtxdiResources.cpp b/minimal/src/RtxdiResources.cpp index 8e89458..46cc681 100644 --- a/minimal/src/RtxdiResources.cpp +++ b/minimal/src/RtxdiResources.cpp @@ -9,7 +9,7 @@ **************************************************************************/ #include "RtxdiResources.h" -#include +#include #include @@ -18,7 +18,7 @@ using namespace dm; RtxdiResources::RtxdiResources( nvrhi::IDevice* device, - const rtxdi::Context& context, + const rtxdi::ReSTIRDIContext& context, uint32_t maxEmissiveMeshes, uint32_t maxEmissiveTriangles, uint32_t maxGeometryInstances) @@ -56,7 +56,7 @@ RtxdiResources::RtxdiResources( nvrhi::BufferDesc neighborOffsetBufferDesc; - neighborOffsetBufferDesc.byteSize = context.GetParameters().NeighborOffsetCount * 2; + neighborOffsetBufferDesc.byteSize = context.getStaticParameters().NeighborOffsetCount * 2; neighborOffsetBufferDesc.format = nvrhi::Format::RG8_SNORM; neighborOffsetBufferDesc.canHaveTypedViews = true; neighborOffsetBufferDesc.debugName = "NeighborOffsets"; @@ -66,8 +66,8 @@ RtxdiResources::RtxdiResources( nvrhi::BufferDesc lightReservoirBufferDesc; - lightReservoirBufferDesc.byteSize = sizeof(RTXDI_PackedReservoir) * context.GetReservoirBufferElementCount() * c_NumReservoirBuffers; - lightReservoirBufferDesc.structStride = sizeof(RTXDI_PackedReservoir); + lightReservoirBufferDesc.byteSize = sizeof(RTXDI_PackedDIReservoir) * context.getReservoirBufferParameters().reservoirArrayPitch * rtxdi::c_NumReSTIRDIReservoirBuffers; + lightReservoirBufferDesc.structStride = sizeof(RTXDI_PackedDIReservoir); lightReservoirBufferDesc.initialState = nvrhi::ResourceStates::UnorderedAccess; lightReservoirBufferDesc.keepInitialState = true; lightReservoirBufferDesc.debugName = "LightReservoirBuffer"; @@ -75,15 +75,15 @@ RtxdiResources::RtxdiResources( LightReservoirBuffer = device->createBuffer(lightReservoirBufferDesc); } -void RtxdiResources::InitializeNeighborOffsets(nvrhi::ICommandList* commandList, const rtxdi::Context& context) +void RtxdiResources::InitializeNeighborOffsets(nvrhi::ICommandList* commandList, uint32_t neighborOffsetCount) { if (m_NeighborOffsetsInitialized) return; std::vector offsets; - offsets.resize(context.GetParameters().NeighborOffsetCount* 2); + offsets.resize(neighborOffsetCount * 2); - context.FillNeighborOffsetBuffer(offsets.data()); + rtxdi::FillNeighborOffsetBuffer(offsets.data(), neighborOffsetCount); commandList->writeBuffer(NeighborOffsetsBuffer, offsets.data(), offsets.size()); diff --git a/minimal/src/RtxdiResources.h b/minimal/src/RtxdiResources.h index e2736f5..2a024dd 100644 --- a/minimal/src/RtxdiResources.h +++ b/minimal/src/RtxdiResources.h @@ -14,7 +14,7 @@ namespace rtxdi { - class Context; + class ReSTIRDIContext; } class RtxdiResources @@ -34,16 +34,14 @@ class RtxdiResources RtxdiResources( nvrhi::IDevice* device, - const rtxdi::Context& context, + const rtxdi::ReSTIRDIContext& context, uint32_t maxEmissiveMeshes, uint32_t maxEmissiveTriangles, uint32_t maxMeshInstances); - void InitializeNeighborOffsets(nvrhi::ICommandList* commandList, const rtxdi::Context& context); + void InitializeNeighborOffsets(nvrhi::ICommandList* commandList, uint32_t neighborOffsetCount); uint32_t GetMaxEmissiveMeshes() const { return m_MaxEmissiveMeshes; } uint32_t GetMaxEmissiveTriangles() const { return m_MaxEmissiveTriangles; } uint32_t GetMaxGeometryInstances() const { return m_MaxGeometryInstances; } - - static constexpr uint32_t c_NumReservoirBuffers = 3; }; \ No newline at end of file diff --git a/minimal/src/main.cpp b/minimal/src/main.cpp index 3eb9e37..c1c241e 100644 --- a/minimal/src/main.cpp +++ b/minimal/src/main.cpp @@ -9,7 +9,7 @@ **************************************************************************/ // Include this first just to test the cleanliness -#include +#include #include #include @@ -63,7 +63,7 @@ class SceneRenderer : public app::ApplicationBase engine::PlanarView m_ViewPrevious; engine::BindingCache m_BindingCache; - std::unique_ptr m_RtxdiContext; + std::unique_ptr m_restirDIContext; std::unique_ptr m_PrepareLightsPass; std::unique_ptr m_RenderPass; std::unique_ptr m_RtxdiResources; @@ -236,7 +236,7 @@ class SceneRenderer : public app::ApplicationBase m_BindingCache.Clear(); m_RenderTargets = nullptr; - m_RtxdiContext = nullptr; + m_restirDIContext = nullptr; m_RtxdiResources = nullptr; } @@ -272,13 +272,13 @@ class SceneRenderer : public app::ApplicationBase bool renderTargetsCreated = false; bool rtxdiResourcesCreated = false; - if (!m_RtxdiContext) + if (!m_restirDIContext) { - rtxdi::ContextParameters contextParams; + rtxdi::ReSTIRDIStaticParameters contextParams; contextParams.RenderWidth = fbinfo.width; contextParams.RenderHeight = fbinfo.height; - m_RtxdiContext = std::make_unique(contextParams); + m_restirDIContext = std::make_unique(contextParams); } if (!m_RenderTargets) @@ -294,7 +294,7 @@ class SceneRenderer : public app::ApplicationBase m_PrepareLightsPass->CountLightsInScene(numEmissiveMeshes, numEmissiveTriangles); uint32_t numGeometryInstances = uint32_t(m_Scene->GetSceneGraph()->GetGeometryInstancesCount()); - m_RtxdiResources = std::make_unique(GetDevice(), *m_RtxdiContext, + m_RtxdiResources = std::make_unique(GetDevice(), *m_restirDIContext, numEmissiveMeshes, numEmissiveTriangles, numGeometryInstances); m_PrepareLightsPass->CreateBindingSet(*m_RtxdiResources); @@ -335,22 +335,21 @@ class SceneRenderer : public app::ApplicationBase m_Scene->Refresh(m_CommandList, GetFrameIndex()); // Write the neighbor offset buffer data (only happens once) - m_RtxdiResources->InitializeNeighborOffsets(m_CommandList, *m_RtxdiContext); + m_RtxdiResources->InitializeNeighborOffsets(m_CommandList, m_restirDIContext->getStaticParameters().NeighborOffsetCount); - rtxdi::FrameParameters frameParameters; // The light indexing members of frameParameters are written by PrepareLightsPass below - frameParameters.frameIndex = GetFrameIndex(); + m_restirDIContext->setFrameIndex(GetFrameIndex()); // When the lights are static, there is no need to update them on every frame, // but it's simpler to do so. - m_PrepareLightsPass->Process(m_CommandList, frameParameters); + RTXDI_LightBufferParameters lightBufferParams = m_PrepareLightsPass->Process(m_CommandList); // Call the rendering pass - this includes primary rays, fused resampling, and shading m_RenderPass->Render(m_CommandList, - *m_RtxdiContext, + *m_restirDIContext, m_View, m_ViewPrevious, m_ui.lightingSettings, - frameParameters); + lightBufferParams); // Copy the render pass output to the swap chain m_CommonPasses->BlitTexture(m_CommandList, framebuffer, m_RenderTargets->HdrColor, &m_BindingCache); diff --git a/rtxdi-sdk/CMakeLists.txt b/rtxdi-sdk/CMakeLists.txt index c2bf05e..e7a1de2 100644 --- a/rtxdi-sdk/CMakeLists.txt +++ b/rtxdi-sdk/CMakeLists.txt @@ -8,6 +8,9 @@ set_target_properties(rtxdi-sdk PROPERTIES FOLDER "RTXDI SDK") # Dependencies for the resampling compile tests file(GLOB shader_dependencies "${CMAKE_CURRENT_SOURCE_DIR}/include/rtxdi/*") +cmake_dependent_option(RTXDI_SKIP_SHADER_VALIDATION "Skip shader compilation when building just the rtxdi-sdk project" OFF WIN32 OFF) + +if (NOT RTXDI_SKIP_SHADER_VALIDATION) # ResamplingCompileTest.hlsl for DXIL target @@ -68,3 +71,5 @@ if (NOT ${GLSLANG_PATH} STREQUAL "") message(WARNING "The GLSLANG_PATH variable points to a non-existent file: ${GLSLANG_PATH}") endif() endif() + +endif() diff --git a/rtxdi-sdk/include/rtxdi/ResamplingFunctions.hlsli b/rtxdi-sdk/include/rtxdi/DIResamplingFunctions.hlsli similarity index 51% rename from rtxdi-sdk/include/rtxdi/ResamplingFunctions.hlsli rename to rtxdi-sdk/include/rtxdi/DIResamplingFunctions.hlsli index eb8474a..c9f3809 100644 --- a/rtxdi-sdk/include/rtxdi/ResamplingFunctions.hlsli +++ b/rtxdi-sdk/include/rtxdi/DIResamplingFunctions.hlsli @@ -11,7 +11,7 @@ #ifndef RESAMPLING_FUNCTIONS_HLSLI #define RESAMPLING_FUNCTIONS_HLSLI -#include "Reservoir.hlsli" +#include "DIReservoir.hlsli" // This macro can be defined in the including shader file to reduce code bloat // and/or remove ray tracing calls from temporal and spatial resampling shaders @@ -20,219 +20,18 @@ #define RTXDI_ALLOWED_BIAS_CORRECTION RTXDI_BIAS_CORRECTION_RAY_TRACED #endif -// This macro enables the functions that deal with the RIS buffer and presampling. -#ifndef RTXDI_ENABLE_PRESAMPLING -#define RTXDI_ENABLE_PRESAMPLING 1 -#endif - -#if !RTXDI_ENABLE_PRESAMPLING && (RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED) -#error "ReGIR requires presampling to be enabled" -#endif - -#if RTXDI_ENABLE_PRESAMPLING && !defined(RTXDI_RIS_BUFFER) -#error "RTXDI_RIS_BUFFER must be defined to point to a RWBuffer type resource" -#endif - #ifndef RTXDI_NEIGHBOR_OFFSETS_BUFFER #error "RTXDI_NEIGHBOR_OFFSETS_BUFFER must be defined to point to a Buffer type resource" #endif -struct RTXDI_SampleParameters -{ - uint numRegirSamples; - uint numLocalLightSamples; - uint numInfiniteLightSamples; - uint numEnvironmentMapSamples; - uint numBrdfSamples; - - uint numMisSamples; - float localLightMisWeight; - float environmentMapMisWeight; - float brdfMisWeight; - float brdfCutoff; - float brdfRayMinT; -}; - -// Sample parameters struct -// Defined so that so these can be compile time constants as defined by the user -// brdfCutoff Value in range [0,1] to determine how much to shorten BRDF rays. 0 to disable shortening -RTXDI_SampleParameters RTXDI_InitSampleParameters( - uint numRegirSamples, - uint numLocalLightSamples, - uint numInfiniteLightSamples, - uint numEnvironmentMapSamples, - uint numBrdfSamples, - float brdfCutoff RTXDI_DEFAULT(0.0), - float brdfRayMinT RTXDI_DEFAULT(0.001f)) -{ - RTXDI_SampleParameters result; - result.numRegirSamples = numRegirSamples; - result.numLocalLightSamples = numLocalLightSamples; - result.numInfiniteLightSamples = numInfiniteLightSamples; - result.numEnvironmentMapSamples = numEnvironmentMapSamples; - result.numBrdfSamples = numBrdfSamples; - - result.numMisSamples = numLocalLightSamples + numEnvironmentMapSamples + numBrdfSamples; - result.localLightMisWeight = float(numLocalLightSamples) / result.numMisSamples; - result.environmentMapMisWeight = float(numEnvironmentMapSamples) / result.numMisSamples; - result.brdfMisWeight = float(numBrdfSamples) / result.numMisSamples; - result.brdfCutoff = brdfCutoff; - result.brdfRayMinT = brdfRayMinT; - - return result; -} - -// Heuristic to determine a max visibility ray length from a PDF wrt. solid angle. -float RTXDI_BrdfMaxDistanceFromPdf(float brdfCutoff, float pdf) -{ - const float kRayTMax = 3.402823466e+38F; // FLT_MAX - return brdfCutoff > 0.f ? sqrt((1.f / brdfCutoff - 1.f) * pdf) : kRayTMax; -} - -// Computes the multi importance sampling pdf for brdf and light sample. -// For light and BRDF PDFs wrt solid angle, blend between the two. -// lightSelectionPdf is a dimensionless selection pdf -float RTXDI_LightBrdfMisWeight(RAB_Surface surface, RAB_LightSample lightSample, - float lightSelectionPdf, float lightMisWeight, bool isEnvironmentMap, - RTXDI_SampleParameters sampleParams) -{ - float lightSolidAnglePdf = RAB_LightSampleSolidAnglePdf(lightSample); - if (sampleParams.brdfMisWeight == 0 || RAB_IsAnalyticLightSample(lightSample) || - lightSolidAnglePdf <= 0 || isinf(lightSolidAnglePdf) || isnan(lightSolidAnglePdf)) - { - // BRDF samples disabled or we can't trace BRDF rays MIS with analytical lights - return lightMisWeight * lightSelectionPdf; - } - - float3 lightDir; - float lightDistance; - RAB_GetLightDirDistance(surface, lightSample, lightDir, lightDistance); - - // Compensate for ray shortening due to brdf cutoff, does not apply to environment map sampling - float brdfPdf = RAB_GetSurfaceBrdfPdf(surface, lightDir); - float maxDistance = RTXDI_BrdfMaxDistanceFromPdf(sampleParams.brdfCutoff, brdfPdf); - if (!isEnvironmentMap && lightDistance > maxDistance) - brdfPdf = 0.f; - - // Convert light selection pdf (unitless) to a solid angle measurement - float sourcePdfWrtSolidAngle = lightSelectionPdf * lightSolidAnglePdf; - - // MIS blending against solid angle pdfs. - float blendedPdfWrtSolidangle = lightMisWeight * sourcePdfWrtSolidAngle + sampleParams.brdfMisWeight * brdfPdf; - - // Convert back, RTXDI divides shading again by this term later - return blendedPdfWrtSolidangle / lightSolidAnglePdf; -} - -// Adds a new, non-reservoir light sample into the reservoir, returns true if this sample was selected. -// Algorithm (3) from the ReSTIR paper, Streaming RIS using weighted reservoir sampling. -bool RTXDI_StreamSample( - inout RTXDI_Reservoir reservoir, - uint lightIndex, - float2 uv, - float random, - float targetPdf, - float invSourcePdf) -{ - // What's the current weight - float risWeight = targetPdf * invSourcePdf; - - // Add one sample to the counter - reservoir.M += 1; - - // Update the weight sum - reservoir.weightSum += risWeight; - - // Decide if we will randomly pick this sample - bool selectSample = (random * reservoir.weightSum < risWeight); - - // If we did select this sample, update the relevant data. - // New samples don't have visibility or age information, we can skip that. - if (selectSample) - { - reservoir.lightData = lightIndex | RTXDI_Reservoir_LightValidBit; - reservoir.uvData = uint(saturate(uv.x) * 0xffff) | (uint(saturate(uv.y) * 0xffff) << 16); - reservoir.targetPdf = targetPdf; - } - - return selectSample; -} - -// Adds `newReservoir` into `reservoir`, returns true if the new reservoir's sample was selected. -// This is a very general form, allowing input parameters to specfiy normalization and targetPdf -// rather than computing them from `newReservoir`. Named "internal" since these parameters take -// different meanings (e.g., in RTXDI_CombineReservoirs() or RTXDI_StreamNeighborWithPairwiseMIS()) -bool RTXDI_InternalSimpleResample( - inout RTXDI_Reservoir reservoir, - const RTXDI_Reservoir newReservoir, - float random, - float targetPdf RTXDI_DEFAULT(1.0f), // Usually closely related to the sample normalization, - float sampleNormalization RTXDI_DEFAULT(1.0f), // typically off by some multiplicative factor - float sampleM RTXDI_DEFAULT(1.0f) // In its most basic form, should be newReservoir.M -) -{ - // What's the current weight (times any prior-step RIS normalization factor) - float risWeight = targetPdf * sampleNormalization; - - // Our *effective* candidate pool is the sum of our candidates plus those of our neighbors - reservoir.M += sampleM; - - // Update the weight sum - reservoir.weightSum += risWeight; - - // Decide if we will randomly pick this sample - bool selectSample = (random * reservoir.weightSum < risWeight); - - // If we did select this sample, update the relevant data - if (selectSample) - { - reservoir.lightData = newReservoir.lightData; - reservoir.uvData = newReservoir.uvData; - reservoir.targetPdf = targetPdf; - reservoir.packedVisibility = newReservoir.packedVisibility; - reservoir.spatialDistance = newReservoir.spatialDistance; - reservoir.age = newReservoir.age; - } - - return selectSample; -} - -// Adds `newReservoir` into `reservoir`, returns true if the new reservoir's sample was selected. -// Algorithm (4) from the ReSTIR paper, Combining the streams of multiple reservoirs. -// Normalization - Equation (6) - is postponed until all reservoirs are combined. -bool RTXDI_CombineReservoirs( - inout RTXDI_Reservoir reservoir, - const RTXDI_Reservoir newReservoir, - float random, - float targetPdf) -{ - return RTXDI_InternalSimpleResample( - reservoir, - newReservoir, - random, - targetPdf, - newReservoir.weightSum * newReservoir.M, - newReservoir.M - ); -} - -// Performs normalization of the reservoir after streaming. Equation (6) from the ReSTIR paper. -void RTXDI_FinalizeResampling( - inout RTXDI_Reservoir reservoir, - float normalizationNumerator, - float normalizationDenominator) -{ - float denominator = reservoir.targetPdf * normalizationDenominator; - - reservoir.weightSum = (denominator == 0.0) ? 0.0 : (reservoir.weightSum * normalizationNumerator) / denominator; -} +#define RTXDI_NAIVE_SAMPLING_M_THRESHOLD 2 // A helper used for pairwise MIS computations. This might be able to simplify code elsewhere, too. -float RTXDI_TargetPdfHelper(const RTXDI_Reservoir lightReservoir, const RAB_Surface surface, bool priorFrame RTXDI_DEFAULT(false)) +float RTXDI_TargetPdfHelper(const RTXDI_DIReservoir lightReservoir, const RAB_Surface surface, bool priorFrame RTXDI_DEFAULT(false)) { RAB_LightSample lightSample = RAB_SamplePolymorphicLight( - RAB_LoadLightInfo(RTXDI_GetReservoirLightIndex(lightReservoir), priorFrame), - surface, RTXDI_GetReservoirSampleUV(lightReservoir)); + RAB_LoadLightInfo(RTXDI_GetDIReservoirLightIndex(lightReservoir), priorFrame), + surface, RTXDI_GetDIReservoirSampleUV(lightReservoir)); return RAB_GetLightSampleTargetPdfForSurface(lightSample, surface); } @@ -244,11 +43,11 @@ float RTXDI_TargetPdfHelper(const RTXDI_Reservoir lightReservoir, const RAB_Surf // candidate, after which the MIS is completed by calling RTXDI_StreamCanonicalWithPairwiseStep() once for // the canonical sample. // See Chapter 9.1 of https://digitalcommons.dartmouth.edu/dissertations/77/, especially Eq 9.10 & Algo 8 -bool RTXDI_StreamNeighborWithPairwiseMIS(inout RTXDI_Reservoir reservoir, +bool RTXDI_StreamNeighborWithPairwiseMIS(inout RTXDI_DIReservoir reservoir, float random, - const RTXDI_Reservoir neighborReservoir, + const RTXDI_DIReservoir neighborReservoir, const RAB_Surface neighborSurface, - const RTXDI_Reservoir canonicalReservor, + const RTXDI_DIReservoir canonicalReservor, const RAB_Surface canonicalSurface, const uint numberOfNeighborsInStream) // # neighbors streamed via pairwise MIS before streaming the canonical sample { @@ -286,9 +85,9 @@ bool RTXDI_StreamNeighborWithPairwiseMIS(inout RTXDI_Reservoir reservoir, // Called to finish the process of doing pairwise MIS. This function must be called after all required calls to // RTXDI_StreamNeighborWithPairwiseMIS(), since pairwise MIS overweighs the canonical sample. This function // compensates for this overweighting, but it can only happen after all neighbors have been processed. -bool RTXDI_StreamCanonicalWithPairwiseStep(inout RTXDI_Reservoir reservoir, +bool RTXDI_StreamCanonicalWithPairwiseStep(inout RTXDI_DIReservoir reservoir, float random, - const RTXDI_Reservoir canonicalReservoir, + const RTXDI_DIReservoir canonicalReservoir, const RAB_Surface canonicalSurface) { return RTXDI_InternalSimpleResample(reservoir, canonicalReservoir, random, @@ -297,795 +96,7 @@ bool RTXDI_StreamCanonicalWithPairwiseStep(inout RTXDI_Reservoir reservoir, canonicalReservoir.M); } -void RTXDI_SamplePdfMipmap( - inout RAB_RandomSamplerState rng, - RTXDI_TEX2D pdfTexture, // full mip chain starting from unnormalized sampling pdf in mip 0 - uint2 pdfTextureSize, // dimensions of pdfTexture at mip 0; must be 16k or less - out uint2 position, - out float pdf) -{ - int lastMipLevel = max(0, int(floor(log2(max(pdfTextureSize.x, pdfTextureSize.y)))) - 1); - - position = uint2(0, 0); - pdf = 1.0; - for (int mipLevel = lastMipLevel; mipLevel >= 0; mipLevel--) - { - position *= 2; - - float4 samples; - samples.x = max(0, RTXDI_TEX2D_LOAD(pdfTexture, int2(position.x + 0, position.y + 0), mipLevel).x); - samples.y = max(0, RTXDI_TEX2D_LOAD(pdfTexture, int2(position.x + 0, position.y + 1), mipLevel).x); - samples.z = max(0, RTXDI_TEX2D_LOAD(pdfTexture, int2(position.x + 1, position.y + 0), mipLevel).x); - samples.w = max(0, RTXDI_TEX2D_LOAD(pdfTexture, int2(position.x + 1, position.y + 1), mipLevel).x); - - float weightSum = samples.x + samples.y + samples.z + samples.w; - if (weightSum <= 0) - { - pdf = 0; - return; - } - - samples /= weightSum; - - float rnd = RAB_GetNextRandom(rng); - - int2 selectedOffset; - - if (rnd < samples.x) - { - pdf *= samples.x; - } - else - { - rnd -= samples.x; - - if (rnd < samples.y) - { - position += uint2(0, 1); - pdf *= samples.y; - } - else - { - rnd -= samples.y; - - if (rnd < samples.z) - { - position += uint2(1, 0); - pdf *= samples.z; - } - else - { - position += uint2(1, 1); - pdf *= samples.w; - } - } - } - } -} - -#if RTXDI_ENABLE_PRESAMPLING - -void RTXDI_PresampleLocalLights( - inout RAB_RandomSamplerState rng, - RTXDI_TEX2D pdfTexture, - uint2 pdfTextureSize, - uint tileIndex, - uint sampleInTile, - RTXDI_ResamplingRuntimeParameters params) -{ - uint2 texelPosition; - float pdf; - RTXDI_SamplePdfMipmap(rng, pdfTexture, pdfTextureSize, texelPosition, pdf); - - uint lightIndex = RTXDI_ZCurveToLinearIndex(texelPosition); - - uint risBufferPtr = sampleInTile + tileIndex * params.risBufferParams.tileSize; - - bool compact = false; - float invSourcePdf = 0; - - if (pdf > 0) - { - invSourcePdf = 1.0 / pdf; - - RAB_LightInfo lightInfo = RAB_LoadLightInfo(lightIndex + params.localLightParams.firstLocalLight, false); - compact = RAB_StoreCompactLightInfo(risBufferPtr, lightInfo); - } - - lightIndex += params.localLightParams.firstLocalLight; - - if(compact) { - lightIndex |= RTXDI_LIGHT_COMPACT_BIT; - } - - // Store the index of the light that we found and its inverse pdf. - // Or zero and zero if we somehow found nothing. - RTXDI_RIS_BUFFER[risBufferPtr] = uint2(lightIndex, asuint(invSourcePdf)); -} - -void RTXDI_PresampleEnvironmentMap( - inout RAB_RandomSamplerState rng, - RTXDI_TEX2D pdfTexture, - uint2 pdfTextureSize, - uint tileIndex, - uint sampleInTile, - RTXDI_EnvironmentLightRuntimeParameters params) -{ - uint2 texelPosition; - float pdf; - RTXDI_SamplePdfMipmap(rng, pdfTexture, pdfTextureSize, texelPosition, pdf); - - // Uniform sampling inside the pixels - float2 fPos = float2(texelPosition); - fPos.x += RAB_GetNextRandom(rng); - fPos.y += RAB_GetNextRandom(rng); - - // Convert texel position to UV and pack it - float2 uv = fPos / float2(pdfTextureSize); - uint packedUv = uint(saturate(uv.x) * 0xffff) | (uint(saturate(uv.y) * 0xffff) << 16); - - // Compute the inverse PDF if we found something - float invSourcePdf = (pdf > 0) ? (1.0 / pdf) : 0; - - // Store the result - uint risBufferPtr = params.environmentRisBufferOffset + sampleInTile + tileIndex * params.environmentTileSize; - RTXDI_RIS_BUFFER[risBufferPtr] = uint2(packedUv, asuint(invSourcePdf)); -} - -#endif // RTXDI_ENABLE_PRESAMPLING - -#ifndef RTXDI_TILE_SIZE_IN_PIXELS -#define RTXDI_TILE_SIZE_IN_PIXELS 16 -#endif - -void RTXDI_RandomlySelectLocalLight( - inout RAB_RandomSamplerState rng, - uint firstLocalLight, - uint numLocalLights, -#if RTXDI_ENABLE_PRESAMPLING - bool useRisBuffer, - uint risBufferBase, - uint risBufferCount, -#endif - out RAB_LightInfo lightInfo, - out uint lightIndex, - out float invSourcePdf -) -{ - float rnd = RAB_GetNextRandom(rng); - lightInfo = RAB_EmptyLightInfo(); - bool lightLoaded = false; -#if RTXDI_ENABLE_PRESAMPLING - if (useRisBuffer) - { - uint risSample = min(uint(floor(rnd * risBufferCount)), risBufferCount - 1); - uint risBufferPtr = risSample + risBufferBase; - - uint2 tileData = RTXDI_RIS_BUFFER[risBufferPtr]; - lightIndex = tileData.x & RTXDI_LIGHT_INDEX_MASK; - invSourcePdf = asfloat(tileData.y); - - if ((tileData.x & RTXDI_LIGHT_COMPACT_BIT) != 0) - { - lightInfo = RAB_LoadCompactLightInfo(risBufferPtr); - lightLoaded = true; - } - } - else -#endif - { - lightIndex = min(uint(floor(rnd * numLocalLights)), numLocalLights - 1) + firstLocalLight; - invSourcePdf = float(numLocalLights); - } - - if (!lightLoaded) { - lightInfo = RAB_LoadLightInfo(lightIndex, false); - } -} - -float2 RTXDI_RandomlySelectLocalLightUV(RAB_RandomSamplerState rng) -{ - float2 uv; - uv.x = RAB_GetNextRandom(rng); - uv.y = RAB_GetNextRandom(rng); - return uv; -} - -// Returns false if the blended source PDF == 0, true otherwise -bool RTXDI_StreamLocalLightAtUVIntoReservoir( - inout RAB_RandomSamplerState rng, - RTXDI_SampleParameters sampleParams, - RAB_Surface surface, - uint lightIndex, - float2 uv, - float invSourcePdf, - RAB_LightInfo lightInfo, - inout RTXDI_Reservoir state, - inout RAB_LightSample o_selectedSample) -{ - RAB_LightSample candidateSample = RAB_SamplePolymorphicLight(lightInfo, surface, uv); - float blendedSourcePdf = RTXDI_LightBrdfMisWeight(surface, candidateSample, 1.0 / invSourcePdf, - sampleParams.localLightMisWeight, false, sampleParams); - float targetPdf = RAB_GetLightSampleTargetPdfForSurface(candidateSample, surface); - float risRnd = RAB_GetNextRandom(rng); - - if (blendedSourcePdf == 0) - { - return false; - } - bool selected = RTXDI_StreamSample(state, lightIndex, uv, risRnd, targetPdf, 1.0 / blendedSourcePdf); - - if (selected) { - o_selectedSample = candidateSample; - } - return true; -} - -// SDK internal function that samples the given set of lights generated by RIS -// or the local light pool. The RIS set can come from local light importance presampling or from ReGIR. -RTXDI_Reservoir RTXDI_SampleLocalLightsInternal( - inout RAB_RandomSamplerState rng, - RAB_Surface surface, - RTXDI_SampleParameters sampleParams, - RTXDI_LocalLightRuntimeParameters params, -#if RTXDI_ENABLE_PRESAMPLING - bool useRisBuffer, - uint risBufferBase, - uint risBufferCount, -#endif - out RAB_LightSample o_selectedSample) -{ - RTXDI_Reservoir state = RTXDI_EmptyReservoir(); - o_selectedSample = RAB_EmptyLightSample(); - - if (params.numLocalLights == 0) - return state; - - if (sampleParams.numLocalLightSamples == 0) - return state; - - for (uint i = 0; i < sampleParams.numLocalLightSamples; i++) - { - uint lightIndex; - RAB_LightInfo lightInfo; - float invSourcePdf; - - RTXDI_RandomlySelectLocalLight(rng, params.firstLocalLight, params.numLocalLights, -#if RTXDI_ENABLE_PRESAMPLING - useRisBuffer, risBufferBase, risBufferCount, -#endif - lightInfo, lightIndex, invSourcePdf); - - float2 uv = RTXDI_RandomlySelectLocalLightUV(rng); - bool zeroPdf = RTXDI_StreamLocalLightAtUVIntoReservoir(rng, sampleParams, surface, lightIndex, uv, invSourcePdf, lightInfo, state, o_selectedSample); - if (zeroPdf) - continue; - } - - RTXDI_FinalizeResampling(state, 1.0, sampleParams.numMisSamples); - state.M = 1; - - return state; -} - -// Samples the local light pool for the given surface. -RTXDI_Reservoir RTXDI_SampleLocalLights( - inout RAB_RandomSamplerState rng, - inout RAB_RandomSamplerState coherentRng, - RAB_Surface surface, - RTXDI_SampleParameters sampleParams, - RTXDI_ResamplingRuntimeParameters params, - out RAB_LightSample o_selectedSample) -{ - float tileRnd = RAB_GetNextRandom(coherentRng); - uint tileIndex = uint(tileRnd * params.risBufferParams.tileCount); - uint risBufferBase = tileIndex * params.risBufferParams.tileSize; - - return RTXDI_SampleLocalLightsInternal(rng, surface, sampleParams, params.localLightParams, -#if RTXDI_ENABLE_PRESAMPLING - params.localLightParams.enableLocalLightImportanceSampling != 0, risBufferBase, params.risBufferParams.tileSize, -#endif - o_selectedSample); -} - -void RTXDI_RandomlySelectInfiniteLight( - inout RAB_RandomSamplerState rng, - RTXDI_InfiniteLightRuntimeParameters params, - out RAB_LightInfo lightInfo, - out uint lightIndex, - out float invSourcePdf) -{ - float rnd = RAB_GetNextRandom(rng); - invSourcePdf = float(params.numInfiniteLights); - lightIndex = params.firstInfiniteLight + min(uint(floor(rnd * params.numInfiniteLights)), params.numInfiniteLights - 1); - lightInfo = RAB_LoadLightInfo(lightIndex, false); -} - -float2 RTXDI_RandomlySelectInfiniteLightUV(RAB_RandomSamplerState rng) -{ - float2 uv; - uv.x = RAB_GetNextRandom(rng); - uv.y = RAB_GetNextRandom(rng); - return uv; -} - -void RTXDI_StreamInfiniteLightAtUVIntoReservoir( - inout RAB_RandomSamplerState rng, - RAB_LightInfo lightInfo, - RAB_Surface surface, - uint lightIndex, - float2 uv, - float invSourcePdf, - inout RTXDI_Reservoir state, - inout RAB_LightSample o_selectedSample) -{ - RAB_LightSample candidateSample = RAB_SamplePolymorphicLight(lightInfo, surface, uv); - float targetPdf = RAB_GetLightSampleTargetPdfForSurface(candidateSample, surface); - float risRnd = RAB_GetNextRandom(rng); - bool selected = RTXDI_StreamSample(state, lightIndex, uv, risRnd, targetPdf, invSourcePdf); - - if (selected) - { - o_selectedSample = candidateSample; - } -} - -// Samples the infinite light pool for the given surface. -RTXDI_Reservoir RTXDI_SampleInfiniteLights( - inout RAB_RandomSamplerState rng, - RAB_Surface surface, - uint numSamples, - RTXDI_InfiniteLightRuntimeParameters params, - inout RAB_LightSample o_selectedSample) -{ - RTXDI_Reservoir state = RTXDI_EmptyReservoir(); - o_selectedSample = RAB_EmptyLightSample(); - - if (params.numInfiniteLights == 0) - return state; - - if (numSamples == 0) - return state; - - uint stride = (params.numInfiniteLights + numSamples - 1) / numSamples; - uint randStart = uint(RAB_GetNextRandom(rng) * params.numInfiniteLights); - - for(uint i = 0; i < numSamples; i++) - { - float invSourcePdf; - uint lightIndex; - RAB_LightInfo lightInfo; - - RTXDI_RandomlySelectInfiniteLight(rng, params, lightInfo, lightIndex, invSourcePdf); - float2 uv = RTXDI_RandomlySelectInfiniteLightUV(rng); - RTXDI_StreamInfiniteLightAtUVIntoReservoir(rng, lightInfo, surface, lightIndex, uv, invSourcePdf, state, o_selectedSample); - } - - RTXDI_FinalizeResampling(state, 1.0, state.M); - state.M = 1; - - return state; -} - -#if RTXDI_ENABLE_PRESAMPLING - -void RTXDI_ComputeRISBufferBaseAndCount( - inout RAB_RandomSamplerState coherentRng, - RTXDI_EnvironmentLightRuntimeParameters params, - out uint risBufferBase, - out uint risBufferCount) -{ - float tileRnd = RAB_GetNextRandom(coherentRng); - uint tileIndex = uint(tileRnd * params.environmentTileCount); - risBufferBase = tileIndex * params.environmentTileSize + params.environmentRisBufferOffset; - risBufferCount = params.environmentTileSize; -} - -void RTXDI_SelectEnvironmentLightUV( - inout RAB_RandomSamplerState rng, - uint risBufferCount, - uint risBufferBase, - out float2 uv, - out float invSourcePdf -) -{ - float rnd = RAB_GetNextRandom(rng); - uint risSample = min(uint(floor(rnd * risBufferCount)), risBufferCount - 1); - uint risBufferPtr = risSample + risBufferBase; - uint2 tileData = RTXDI_RIS_BUFFER[risBufferPtr]; - uint packedUv = tileData.x; - - invSourcePdf = asfloat(tileData.y); - uv = float2(packedUv & 0xffff, packedUv >> 16) / float(0xffff); -} - -void RTXDI_StreamEnvironmentLightAtUVIntoReservoir( - inout RAB_RandomSamplerState rng, - RTXDI_SampleParameters sampleParams, - RAB_Surface surface, - RAB_LightInfo lightInfo, - uint environmentLightIndex, - float2 uv, - float invSourcePdf, - inout RTXDI_Reservoir state, - inout RAB_LightSample o_selectedSample) -{ - RAB_LightSample candidateSample = RAB_SamplePolymorphicLight(lightInfo, surface, uv); - float blendedSourcePdf = RTXDI_LightBrdfMisWeight(surface, candidateSample, 1.0 / invSourcePdf, - sampleParams.environmentMapMisWeight, true, sampleParams); - float targetPdf = RAB_GetLightSampleTargetPdfForSurface(candidateSample, surface); - float risRnd = RAB_GetNextRandom(rng); - - bool selected = RTXDI_StreamSample(state, environmentLightIndex, uv, risRnd, targetPdf, 1.0 / blendedSourcePdf); - - if (selected) { - o_selectedSample = candidateSample; - } -} - -RTXDI_Reservoir RTXDI_SampleEnvironmentMap( - inout RAB_RandomSamplerState rng, - inout RAB_RandomSamplerState coherentRng, - RAB_Surface surface, - RTXDI_SampleParameters sampleParams, - RTXDI_EnvironmentLightRuntimeParameters params, - out RAB_LightSample o_selectedSample) -{ - RTXDI_Reservoir state = RTXDI_EmptyReservoir(); - o_selectedSample = RAB_EmptyLightSample(); - - if (params.environmentLightPresent == 0) - return state; - - if (sampleParams.numEnvironmentMapSamples == 0) - return state; - - float tileRnd = RAB_GetNextRandom(coherentRng); - uint tileIndex = uint(tileRnd * params.environmentTileCount); - - uint risBufferBase; - uint risBufferCount; - RTXDI_ComputeRISBufferBaseAndCount(coherentRng, params, risBufferBase, risBufferCount); - - RAB_LightInfo lightInfo = RAB_LoadLightInfo(params.environmentLightIndex, false); - - for (uint i = 0; i < sampleParams.numEnvironmentMapSamples; i++) - { - float2 uv; - float invSourcePdf; - RTXDI_SelectEnvironmentLightUV(rng, risBufferCount, risBufferBase, uv, invSourcePdf); - RTXDI_StreamEnvironmentLightAtUVIntoReservoir(rng, sampleParams, surface, lightInfo, params.environmentLightIndex, uv, invSourcePdf, state, o_selectedSample); - } - - RTXDI_FinalizeResampling(state, 1.0, sampleParams.numMisSamples); - state.M = 1; - - return state; -} - -#endif // RTXDI_ENABLE_PRESAMPLING - -#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED - -// ReGIR grid build pass. -// Each thread populates one light slot in a grid cell. -void RTXDI_PresampleLocalLightsForReGIR( - inout RAB_RandomSamplerState rng, - inout RAB_RandomSamplerState coherentRng, - uint lightSlot, - uint numSamples, - RTXDI_ResamplingRuntimeParameters params) -{ - uint risBufferPtr = params.regirCommon.risBufferOffset + lightSlot; - - if (numSamples == 0) - { - RTXDI_RIS_BUFFER[risBufferPtr] = uint2(0, 0); - return; - } - - uint lightInCell = lightSlot % params.regirCommon.lightsPerCell; - - uint cellIndex = lightSlot / params.regirCommon.lightsPerCell; - - float3 cellCenter; - float cellRadius; - if (!RTXDI_ReGIR_CellIndexToWorldPos(params, int(cellIndex), cellCenter, cellRadius)) - { - RTXDI_RIS_BUFFER[risBufferPtr] = uint2(0, 0); - return; - } - - cellRadius *= (params.regirCommon.samplingJitter + 1.0); - - RAB_LightInfo selectedLightInfo = RAB_EmptyLightInfo(); - uint selectedLight = 0; - float selectedTargetPdf = 0; - float weightSum = 0; - - - float rndTileSample = RAB_GetNextRandom(coherentRng); - uint tileIndex = uint(rndTileSample * params.risBufferParams.tileCount); - - float invNumSamples = 1.0 / float(numSamples); - - for (uint i = 0; i < numSamples; i++) - { - uint rndLight; - RAB_LightInfo lightInfo = RAB_EmptyLightInfo(); - float invSourcePdf; - float rand = RAB_GetNextRandom(rng); - bool lightLoaded = false; - - if (params.localLightParams.enableLocalLightImportanceSampling != 0) - { - uint tileSample = uint(min(rand * params.risBufferParams.tileSize, params.risBufferParams.tileSize - 1)); - uint tilePtr = tileSample + tileIndex * params.risBufferParams.tileSize; - - uint2 tileData = RTXDI_RIS_BUFFER[tilePtr]; - rndLight = tileData.x & RTXDI_LIGHT_INDEX_MASK; - invSourcePdf = asfloat(tileData.y) * invNumSamples; - - if ((tileData.x & RTXDI_LIGHT_COMPACT_BIT) != 0) - { - lightInfo = RAB_LoadCompactLightInfo(tilePtr); - lightLoaded = true; - } - } - else - { - rndLight = uint(min(rand * params.localLightParams.numLocalLights, params.localLightParams.numLocalLights - 1)) + params.localLightParams.firstLocalLight; - invSourcePdf = float(params.localLightParams.numLocalLights) * invNumSamples; - } - - if (!lightLoaded) { - lightInfo = RAB_LoadLightInfo(rndLight, false); - } - - float targetPdf = RAB_GetLightTargetPdfForVolume(lightInfo, cellCenter, cellRadius); - float risRnd = RAB_GetNextRandom(rng); - - float risWeight = targetPdf * invSourcePdf; - weightSum += risWeight; - - if (risRnd * weightSum < risWeight) - { - selectedLightInfo = lightInfo; - selectedLight = rndLight; - selectedTargetPdf = targetPdf; - } - } - - float weight = (selectedTargetPdf > 0) ? weightSum / selectedTargetPdf : 0; - - bool compact = false; - - if (weight > 0) { - compact = RAB_StoreCompactLightInfo(risBufferPtr, selectedLightInfo); - } - - if(compact) { - selectedLight |= RTXDI_LIGHT_COMPACT_BIT; - } - - RTXDI_RIS_BUFFER[risBufferPtr] = uint2(selectedLight, asuint(weight)); -} - -// Sampling lights for a surface from the ReGIR structure or the local light pool. -// If the surface is inside the ReGIR structure, and ReGIR is enabled, and -// numRegirSamples is nonzero, then this function will sample the ReGIR structure. -// Otherwise, it samples the local light pool. -RTXDI_Reservoir RTXDI_SampleLocalLightsFromReGIR( - inout RAB_RandomSamplerState rng, - inout RAB_RandomSamplerState coherentRng, - RAB_Surface surface, - RTXDI_SampleParameters sampleParams, - RTXDI_ResamplingRuntimeParameters params, - out RAB_LightSample o_selectedSample) -{ - RTXDI_Reservoir reservoir = RTXDI_EmptyReservoir(); - o_selectedSample = RAB_EmptyLightSample(); - - if (sampleParams.numRegirSamples == 0 && sampleParams.numLocalLightSamples == 0) - return reservoir; - int cellIndex = -1; - - if (params.regirCommon.enable != 0 && sampleParams.numRegirSamples > 0) - { - float3 cellJitter = float3( - RAB_GetNextRandom(coherentRng), - RAB_GetNextRandom(coherentRng), - RAB_GetNextRandom(coherentRng)); - cellJitter -= 0.5; - - float3 samplingPos = RAB_GetSurfaceWorldPos(surface); - float jitterScale = RTXDI_ReGIR_GetJitterScale(params, samplingPos); - samplingPos += cellJitter * jitterScale; - - cellIndex = RTXDI_ReGIR_WorldPosToCellIndex(params, samplingPos); - } - - uint risBufferBase, risBufferCount, numSamples; - bool useRisBuffer; - - if (cellIndex < 0) - { - float tileRnd = RAB_GetNextRandom(coherentRng); - uint tileIndex = uint(tileRnd * params.risBufferParams.tileCount); - - risBufferBase = tileIndex * params.risBufferParams.tileSize; - risBufferCount = params.risBufferParams.tileSize; - numSamples = sampleParams.numLocalLightSamples; - useRisBuffer = params.localLightParams.enableLocalLightImportanceSampling != 0; - } - else - { - uint cellBase = uint(cellIndex) * params.regirCommon.lightsPerCell; - risBufferBase = cellBase + params.regirCommon.risBufferOffset; - risBufferCount = params.regirCommon.lightsPerCell; - numSamples = sampleParams.numRegirSamples; - useRisBuffer = true; - } - - reservoir = RTXDI_SampleLocalLightsInternal(rng, surface, sampleParams, params.localLightParams, - useRisBuffer, risBufferBase, risBufferCount, o_selectedSample); - - return reservoir; -} - -#endif // (RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED) - -// Samples from the BRDF defined by the given surface -RTXDI_Reservoir RTXDI_SampleBrdf( - inout RAB_RandomSamplerState rng, - RAB_Surface surface, - RTXDI_SampleParameters sampleParams, - RTXDI_ResamplingRuntimeParameters params, - out RAB_LightSample o_selectedSample) -{ - RTXDI_Reservoir state = RTXDI_EmptyReservoir(); - - for (uint i = 0; i < sampleParams.numBrdfSamples; ++i) - { - float lightSourcePdf = 0; - float3 sampleDir; - uint lightIndex = RTXDI_InvalidLightIndex; - float2 randXY = float2(0, 0); - RAB_LightSample candidateSample = RAB_EmptyLightSample(); - - if (RAB_GetSurfaceBrdfSample(surface, rng, sampleDir)) - { - float brdfPdf = RAB_GetSurfaceBrdfPdf(surface, sampleDir); - float maxDistance = RTXDI_BrdfMaxDistanceFromPdf(sampleParams.brdfCutoff, brdfPdf); - - bool hitAnything = RAB_TraceRayForLocalLight(RAB_GetSurfaceWorldPos(surface), sampleDir, - sampleParams.brdfRayMinT, maxDistance, lightIndex, randXY); - - if (lightIndex != RTXDI_InvalidLightIndex) - { - RAB_LightInfo lightInfo = RAB_LoadLightInfo(lightIndex, false); - candidateSample = RAB_SamplePolymorphicLight(lightInfo, surface, randXY); - - if (sampleParams.brdfCutoff > 0.f) - { - // If Mis cutoff is used, we need to evaluate the sample and make sure it actually could have been - // generated by the area sampling technique. This is due to numerical precision. - float3 lightDir; - float lightDistance; - RAB_GetLightDirDistance(surface, candidateSample, lightDir, lightDistance); - - float brdfPdf = RAB_GetSurfaceBrdfPdf(surface, lightDir); - float maxDistance = RTXDI_BrdfMaxDistanceFromPdf(sampleParams.brdfCutoff, brdfPdf); - if (lightDistance > maxDistance) - lightIndex = RTXDI_InvalidLightIndex; - } - - if (lightIndex != RTXDI_InvalidLightIndex) - { - lightSourcePdf = RAB_EvaluateLocalLightSourcePdf(params, lightIndex); - } - } - else if (!hitAnything && params.environmentLightParams.environmentLightPresent != 0) - { - // sample environment light - lightIndex = params.environmentLightParams.environmentLightIndex; - RAB_LightInfo lightInfo = RAB_LoadLightInfo(lightIndex, false); - randXY = RAB_GetEnvironmentMapRandXYFromDir(sampleDir); - candidateSample = RAB_SamplePolymorphicLight(lightInfo, surface, randXY); - lightSourcePdf = RAB_EvaluateEnvironmentMapSamplingPdf(sampleDir); - } - } - - if (lightSourcePdf == 0) - { - // Did not hit a visible light - continue; - } - - bool isEnvMapSample = lightIndex == params.environmentLightParams.environmentLightIndex; - float targetPdf = RAB_GetLightSampleTargetPdfForSurface(candidateSample, surface); - float blendedSourcePdf = RTXDI_LightBrdfMisWeight(surface, candidateSample, lightSourcePdf, - isEnvMapSample ? sampleParams.environmentMapMisWeight : sampleParams.localLightMisWeight, - isEnvMapSample, - sampleParams); - float risRnd = RAB_GetNextRandom(rng); - - bool selected = RTXDI_StreamSample(state, lightIndex, randXY, risRnd, targetPdf, 1.0f / blendedSourcePdf); - if (selected) { - o_selectedSample = candidateSample; - } - } - - RTXDI_FinalizeResampling(state, 1.0, sampleParams.numMisSamples); - state.M = 1; - - return state; -} - -// Samples ReGIR and the local and infinite light pools for a given surface. -RTXDI_Reservoir RTXDI_SampleLightsForSurface( - inout RAB_RandomSamplerState rng, - inout RAB_RandomSamplerState coherentRng, - RAB_Surface surface, - RTXDI_SampleParameters sampleParams, - RTXDI_ResamplingRuntimeParameters params, - out RAB_LightSample o_lightSample) -{ - o_lightSample = RAB_EmptyLightSample(); - - RTXDI_Reservoir localReservoir; - RAB_LightSample localSample = RAB_EmptyLightSample(); - -#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED - // If ReGIR is enabled and the surface is inside the grid, sample the grid. - // Otherwise, fall back to source pool sampling. - localReservoir = RTXDI_SampleLocalLightsFromReGIR(rng, coherentRng, - surface, sampleParams, params, localSample); -#else - localReservoir = RTXDI_SampleLocalLights(rng, coherentRng, surface, - sampleParams, params, localSample); -#endif - - RAB_LightSample infiniteSample = RAB_EmptyLightSample(); - RTXDI_Reservoir infiniteReservoir = RTXDI_SampleInfiniteLights(rng, surface, - sampleParams.numInfiniteLightSamples, params.infiniteLightParams, infiniteSample); - -#if RTXDI_ENABLE_PRESAMPLING - RAB_LightSample environmentSample = RAB_EmptyLightSample(); - RTXDI_Reservoir environmentReservoir = RTXDI_SampleEnvironmentMap(rng, coherentRng, surface, - sampleParams, params.environmentLightParams, environmentSample); -#endif - - RAB_LightSample brdfSample = RAB_EmptyLightSample(); - RTXDI_Reservoir brdfReservoir = RTXDI_SampleBrdf(rng, surface, sampleParams, params, brdfSample); - - RTXDI_Reservoir state = RTXDI_EmptyReservoir(); - RTXDI_CombineReservoirs(state, localReservoir, 0.5, localReservoir.targetPdf); - bool selectInfinite = RTXDI_CombineReservoirs(state, infiniteReservoir, RAB_GetNextRandom(rng), infiniteReservoir.targetPdf); -#if RTXDI_ENABLE_PRESAMPLING - bool selectEnvironment = RTXDI_CombineReservoirs(state, environmentReservoir, RAB_GetNextRandom(rng), environmentReservoir.targetPdf); -#endif - bool selectBrdf = RTXDI_CombineReservoirs(state, brdfReservoir, RAB_GetNextRandom(rng), brdfReservoir.targetPdf); - - RTXDI_FinalizeResampling(state, 1.0, 1.0); - state.M = 1; - - if (selectBrdf) - o_lightSample = brdfSample; - else -#if RTXDI_ENABLE_PRESAMPLING - if (selectEnvironment) - o_lightSample = environmentSample; - else -#endif - if (selectInfinite) - o_lightSample = infiniteSample; - else - o_lightSample = localSample; - - return state; -} #ifdef RTXDI_ENABLE_BOILING_FILTER // Boiling filter that should be applied at the end of the temporal resampling pass. @@ -1096,16 +107,15 @@ RTXDI_Reservoir RTXDI_SampleLightsForSurface( void RTXDI_BoilingFilter( uint2 LocalIndex, float filterStrength, // (0..1] - RTXDI_ResamplingRuntimeParameters params, - inout RTXDI_Reservoir reservoir) + inout RTXDI_DIReservoir reservoir) { - if (RTXDI_BoilingFilterInternal(LocalIndex, filterStrength, params, reservoir.weightSum)) - reservoir = RTXDI_EmptyReservoir(); + if (RTXDI_BoilingFilterInternal(LocalIndex, filterStrength, reservoir.weightSum)) + reservoir = RTXDI_EmptyDIReservoir(); } #endif // RTXDI_ENABLE_BOILING_FILTER // A structure that groups the application-provided settings for temporal resampling. -struct RTXDI_TemporalResamplingParameters +struct RTXDI_DITemporalResamplingParameters { // Screen-space motion vector, computed as (previousPosition - currentPosition). // The X and Y components are measured in pixels. @@ -1143,6 +153,9 @@ struct RTXDI_TemporalResamplingParameters // Enables permuting the pixels sampled from the previous frame in order to add temporal // variation to the output signal and make it more denoiser friendly. bool enablePermutationSampling; + + // Random number for permutation sampling that is the same for all pixels in the frame + uint uniformRandomNumber; }; // Temporal resampling pass. @@ -1154,13 +167,14 @@ struct RTXDI_TemporalResamplingParameters // can also use the current frame BVH if the previous is not available - that will produce more bias. // The selectedLightSample parameter is used to update and return the selected sample; it's optional, // and it's safe to pass a null structure there and ignore the result. -RTXDI_Reservoir RTXDI_TemporalResampling( +RTXDI_DIReservoir RTXDI_DITemporalResampling( uint2 pixelPosition, RAB_Surface surface, - RTXDI_Reservoir curSample, + RTXDI_DIReservoir curSample, inout RAB_RandomSamplerState rng, - RTXDI_TemporalResamplingParameters tparams, - RTXDI_ResamplingRuntimeParameters params, + RTXDI_RuntimeParameters params, + RTXDI_ReservoirBufferParameters reservoirParams, + RTXDI_DITemporalResamplingParameters tparams, out int2 temporalSamplePixelPos, inout RAB_LightSample selectedLightSample) { @@ -1170,19 +184,19 @@ RTXDI_Reservoir RTXDI_TemporalResampling( tparams.biasCorrectionMode = RTXDI_BIAS_CORRECTION_BASIC; } - uint historyLimit = min(RTXDI_PackedReservoir_MaxM, uint(tparams.maxHistoryLength * curSample.M)); + uint historyLimit = min(RTXDI_PackedDIReservoir_MaxM, uint(tparams.maxHistoryLength * curSample.M)); int selectedLightPrevID = -1; - if (RTXDI_IsValidReservoir(curSample)) + if (RTXDI_IsValidDIReservoir(curSample)) { - selectedLightPrevID = RAB_TranslateLightIndex(RTXDI_GetReservoirLightIndex(curSample), true); + selectedLightPrevID = RAB_TranslateLightIndex(RTXDI_GetDIReservoirLightIndex(curSample), true); } temporalSamplePixelPos = int2(-1, -1); - RTXDI_Reservoir state = RTXDI_EmptyReservoir(); - RTXDI_CombineReservoirs(state, curSample, /* random = */ 0.5, curSample.targetPdf); + RTXDI_DIReservoir state = RTXDI_EmptyDIReservoir(); + RTXDI_CombineDIReservoirs(state, curSample, /* random = */ 0.5, curSample.targetPdf); // Backproject this pixel to last frame float3 motion = tparams.screenSpaceMotion; @@ -1215,10 +229,10 @@ RTXDI_Reservoir RTXDI_TemporalResampling( int2 idx = prevPos + offset; if (tparams.enablePermutationSampling && i == 0) { - RTXDI_ApplyPermutationSampling(idx, params.uniformRandomNumber); + RTXDI_ApplyPermutationSampling(idx, tparams.uniformRandomNumber); } - RTXDI_ActivateCheckerboardPixel(idx, true, params); + RTXDI_ActivateCheckerboardPixel(idx, true, params.activeCheckerboardField); // Grab shading / g-buffer data from last frame temporalSurface = RAB_GetGBufferSurface(idx, true); @@ -1246,24 +260,24 @@ RTXDI_Reservoir RTXDI_TemporalResampling( { // Resample the previous frame sample into the current reservoir, but reduce the light's weight // according to the bilinear weight of the current pixel - uint2 prevReservoirPos = RTXDI_PixelPosToReservoirPos(prevPos, params); - RTXDI_Reservoir prevSample = RTXDI_LoadReservoir(params, + uint2 prevReservoirPos = RTXDI_PixelPosToReservoirPos(prevPos, params.activeCheckerboardField); + RTXDI_DIReservoir prevSample = RTXDI_LoadDIReservoir(reservoirParams, prevReservoirPos, tparams.sourceBufferIndex); prevSample.M = min(prevSample.M, historyLimit); prevSample.spatialDistance += spatialOffset; prevSample.age += 1; - uint originalPrevLightID = RTXDI_GetReservoirLightIndex(prevSample); + uint originalPrevLightID = RTXDI_GetDIReservoirLightIndex(prevSample); // Map the light ID from the previous frame into the current frame, if it still exists - if (RTXDI_IsValidReservoir(prevSample)) + if (RTXDI_IsValidDIReservoir(prevSample)) { if (prevSample.age <= 1) { temporalSamplePixelPos = prevPos; } - int mappedLightID = RAB_TranslateLightIndex(RTXDI_GetReservoirLightIndex(prevSample), false); + int mappedLightID = RAB_TranslateLightIndex(RTXDI_GetDIReservoirLightIndex(prevSample), false); if (mappedLightID < 0) { @@ -1274,7 +288,7 @@ RTXDI_Reservoir RTXDI_TemporalResampling( else { // Sample is valid - modify the light ID stored - prevSample.lightData = mappedLightID | RTXDI_Reservoir_LightValidBit; + prevSample.lightData = mappedLightID | RTXDI_DIReservoir_LightValidBit; } } @@ -1282,17 +296,17 @@ RTXDI_Reservoir RTXDI_TemporalResampling( float weightAtCurrent = 0; RAB_LightSample candidateLightSample = RAB_EmptyLightSample(); - if (RTXDI_IsValidReservoir(prevSample)) + if (RTXDI_IsValidDIReservoir(prevSample)) { - const RAB_LightInfo candidateLight = RAB_LoadLightInfo(RTXDI_GetReservoirLightIndex(prevSample), false); + const RAB_LightInfo candidateLight = RAB_LoadLightInfo(RTXDI_GetDIReservoirLightIndex(prevSample), false); candidateLightSample = RAB_SamplePolymorphicLight( - candidateLight, surface, RTXDI_GetReservoirSampleUV(prevSample)); + candidateLight, surface, RTXDI_GetDIReservoirSampleUV(prevSample)); weightAtCurrent = RAB_GetLightSampleTargetPdfForSurface(candidateLightSample, surface); } - bool sampleSelected = RTXDI_CombineReservoirs(state, prevSample, RAB_GetNextRandom(rng), weightAtCurrent); + bool sampleSelected = RTXDI_CombineDIReservoirs(state, prevSample, RAB_GetNextRandom(rng), weightAtCurrent); if(sampleSelected) { selectedPreviousSample = true; @@ -1308,7 +322,7 @@ RTXDI_Reservoir RTXDI_TemporalResampling( float pi = state.targetPdf; float piSum = state.targetPdf * curSample.M; - if (RTXDI_IsValidReservoir(state) && selectedLightPrevID >= 0 && previousM > 0) + if (RTXDI_IsValidDIReservoir(state) && selectedLightPrevID >= 0 && previousM > 0) { float temporalP = 0; @@ -1316,7 +330,7 @@ RTXDI_Reservoir RTXDI_TemporalResampling( // Get the PDF of the sample RIS selected in the first loop, above, *at this neighbor* const RAB_LightSample selectedSampleAtTemporal = RAB_SamplePolymorphicLight( - selectedLightPrev, temporalSurface, RTXDI_GetReservoirSampleUV(state)); + selectedLightPrev, temporalSurface, RTXDI_GetDIReservoirSampleUV(state)); temporalP = RAB_GetLightSampleTargetPdfForSurface(selectedSampleAtTemporal, temporalSurface); @@ -1346,7 +360,7 @@ RTXDI_Reservoir RTXDI_TemporalResampling( } // A structure that groups the application-provided settings for spatial resampling. -struct RTXDI_SpatialResamplingParameters +struct RTXDI_DISpatialResamplingParameters { // The index of the reservoir buffer to pull the spatial samples from. uint sourceBufferIndex; @@ -1383,23 +397,27 @@ struct RTXDI_SpatialResamplingParameters // Enables the comparison of surface materials before taking a surface into resampling. bool enableMaterialSimilarityTest; + + // Prevents samples which are from the current frame or have no reasonable temporal history merged being spread to neighbors + bool discountNaiveSamples; }; // Spatial resampling pass, using pairwise MIS. // Inputs and outputs equivalent to RTXDI_SpatialResampling(), but only uses pairwise MIS. // Can call this directly, or call RTXDI_SpatialResampling() with sparams.biasCorrectionMode // set to RTXDI_BIAS_CORRECTION_PAIRWISE, which simply calls this function. -RTXDI_Reservoir RTXDI_SpatialResamplingWithPairwiseMIS( +RTXDI_DIReservoir RTXDI_DISpatialResamplingWithPairwiseMIS( uint2 pixelPosition, RAB_Surface centerSurface, - RTXDI_Reservoir centerSample, + RTXDI_DIReservoir centerSample, inout RAB_RandomSamplerState rng, - RTXDI_SpatialResamplingParameters sparams, - RTXDI_ResamplingRuntimeParameters params, + RTXDI_RuntimeParameters params, + RTXDI_ReservoirBufferParameters reservoirParams, + RTXDI_DISpatialResamplingParameters sparams, inout RAB_LightSample selectedLightSample) { // Initialize the output reservoir - RTXDI_Reservoir state = RTXDI_EmptyReservoir(); + RTXDI_DIReservoir state = RTXDI_EmptyDIReservoir(); state.canonicalWeight = 0.0f; // How many spatial samples to use? @@ -1419,7 +437,7 @@ RTXDI_Reservoir RTXDI_SpatialResamplingWithPairwiseMIS( int2 idx = int2(pixelPosition)+spatialOffset; idx = RAB_ClampSamplePositionIntoView(idx, false); - RTXDI_ActivateCheckerboardPixel(idx, false, params); + RTXDI_ActivateCheckerboardPixel(idx, false, params.activeCheckerboardField); RAB_Surface neighborSurface = RAB_GetGBufferSurface(idx, false); @@ -1436,10 +454,16 @@ RTXDI_Reservoir RTXDI_SpatialResamplingWithPairwiseMIS( continue; // The surfaces are similar enough so we *can* reuse a neighbor from this pixel, so load it. - RTXDI_Reservoir neighborSample = RTXDI_LoadReservoir(params, - RTXDI_PixelPosToReservoirPos(idx, params), sparams.sourceBufferIndex); + RTXDI_DIReservoir neighborSample = RTXDI_LoadDIReservoir(reservoirParams, + RTXDI_PixelPosToReservoirPos(idx, params.activeCheckerboardField), sparams.sourceBufferIndex); neighborSample.spatialDistance += spatialOffset; + if (RTXDI_IsValidDIReservoir(neighborSample)) + { + if (sparams.discountNaiveSamples && neighborSample.M <= RTXDI_NAIVE_SAMPLING_M_THRESHOLD) + continue; + } + validSpatialSamples++; // If sample has weight 0 due to visibility (or etc), skip the expensive-ish MIS computations @@ -1463,8 +487,8 @@ RTXDI_Reservoir RTXDI_SpatialResamplingWithPairwiseMIS( // Return the selected light sample. This is a redundant lookup and could be optimized away by storing // the selected sample from the stream steps above. selectedLightSample = RAB_SamplePolymorphicLight( - RAB_LoadLightInfo(RTXDI_GetReservoirLightIndex(state), false), - centerSurface, RTXDI_GetReservoirSampleUV(state)); + RAB_LoadLightInfo(RTXDI_GetDIReservoirLightIndex(state), false), + centerSurface, RTXDI_GetDIReservoirSampleUV(state)); return state; } @@ -1477,22 +501,23 @@ RTXDI_Reservoir RTXDI_SpatialResamplingWithPairwiseMIS( // Optionally, one visibility ray is traced for each neighbor being considered, to reduce bias. // The selectedLightSample parameter is used to update and return the selected sample; it's optional, // and it's safe to pass a null structure there and ignore the result. -RTXDI_Reservoir RTXDI_SpatialResampling( +RTXDI_DIReservoir RTXDI_DISpatialResampling( uint2 pixelPosition, RAB_Surface centerSurface, - RTXDI_Reservoir centerSample, + RTXDI_DIReservoir centerSample, inout RAB_RandomSamplerState rng, - RTXDI_SpatialResamplingParameters sparams, - RTXDI_ResamplingRuntimeParameters params, + RTXDI_RuntimeParameters params, + RTXDI_ReservoirBufferParameters reservoirParams, + RTXDI_DISpatialResamplingParameters sparams, inout RAB_LightSample selectedLightSample) { if (sparams.biasCorrectionMode == RTXDI_BIAS_CORRECTION_PAIRWISE) { - return RTXDI_SpatialResamplingWithPairwiseMIS(pixelPosition, centerSurface, - centerSample, rng, sparams, params, selectedLightSample); + return RTXDI_DISpatialResamplingWithPairwiseMIS(pixelPosition, centerSurface, + centerSample, rng, params, reservoirParams, sparams, selectedLightSample); } - RTXDI_Reservoir state = RTXDI_EmptyReservoir(); + RTXDI_DIReservoir state = RTXDI_EmptyDIReservoir(); // This is the weight we'll use (instead of 1/M) to make our estimate unbaised (see paper). float normalizationWeight = 1.0f; @@ -1502,12 +527,12 @@ RTXDI_Reservoir RTXDI_SpatialResampling( RAB_LightInfo selectedLight = RAB_EmptyLightInfo(); - if (RTXDI_IsValidReservoir(centerSample)) + if (RTXDI_IsValidDIReservoir(centerSample)) { - selectedLight = RAB_LoadLightInfo(RTXDI_GetReservoirLightIndex(centerSample), false); + selectedLight = RAB_LoadLightInfo(RTXDI_GetDIReservoirLightIndex(centerSample), false); } - RTXDI_CombineReservoirs(state, centerSample, /* random = */ 0.5f, centerSample.targetPdf); + RTXDI_CombineDIReservoirs(state, centerSample, /* random = */ 0.5f, centerSample.targetPdf); uint startIdx = uint(RAB_GetNextRandom(rng) * params.neighborOffsetMask); @@ -1533,7 +558,7 @@ RTXDI_Reservoir RTXDI_SpatialResampling( idx = RAB_ClampSamplePositionIntoView(idx, false); - RTXDI_ActivateCheckerboardPixel(idx, false, params); + RTXDI_ActivateCheckerboardPixel(idx, false, params.activeCheckerboardField); RAB_Surface neighborSurface = RAB_GetGBufferSurface(idx, false); @@ -1548,9 +573,9 @@ RTXDI_Reservoir RTXDI_SpatialResampling( if (sparams.enableMaterialSimilarityTest && !RAB_AreMaterialsSimilar(centerSurface, neighborSurface)) continue; - uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params); + uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params.activeCheckerboardField); - RTXDI_Reservoir neighborSample = RTXDI_LoadReservoir(params, + RTXDI_DIReservoir neighborSample = RTXDI_LoadDIReservoir(reservoirParams, neighborReservoirPos, sparams.sourceBufferIndex); neighborSample.spatialDistance += spatialOffset; @@ -1561,17 +586,20 @@ RTXDI_Reservoir RTXDI_SpatialResampling( // Load that neighbor's RIS state, do resampling float neighborWeight = 0; RAB_LightSample candidateLightSample = RAB_EmptyLightSample(); - if (RTXDI_IsValidReservoir(neighborSample)) + if (RTXDI_IsValidDIReservoir(neighborSample)) { - candidateLight = RAB_LoadLightInfo(RTXDI_GetReservoirLightIndex(neighborSample), false); + if (sparams.discountNaiveSamples && neighborSample.M <= RTXDI_NAIVE_SAMPLING_M_THRESHOLD) + continue; + + candidateLight = RAB_LoadLightInfo(RTXDI_GetDIReservoirLightIndex(neighborSample), false); candidateLightSample = RAB_SamplePolymorphicLight( - candidateLight, centerSurface, RTXDI_GetReservoirSampleUV(neighborSample)); + candidateLight, centerSurface, RTXDI_GetDIReservoirSampleUV(neighborSample)); neighborWeight = RAB_GetLightSampleTargetPdfForSurface(candidateLightSample, centerSurface); } - if (RTXDI_CombineReservoirs(state, neighborSample, RAB_GetNextRandom(rng), neighborWeight)) + if (RTXDI_CombineDIReservoirs(state, neighborSample, RAB_GetNextRandom(rng), neighborWeight)) { selected = int(i); selectedLight = candidateLight; @@ -1579,7 +607,7 @@ RTXDI_Reservoir RTXDI_SpatialResampling( } } - if (RTXDI_IsValidReservoir(state)) + if (RTXDI_IsValidDIReservoir(state)) { #if RTXDI_ALLOWED_BIAS_CORRECTION >= RTXDI_BIAS_CORRECTION_BASIC if (sparams.biasCorrectionMode >= RTXDI_BIAS_CORRECTION_BASIC) @@ -1601,14 +629,14 @@ RTXDI_Reservoir RTXDI_SpatialResampling( idx = RAB_ClampSamplePositionIntoView(idx, false); - RTXDI_ActivateCheckerboardPixel(idx, false, params); + RTXDI_ActivateCheckerboardPixel(idx, false, params.activeCheckerboardField); // Load our neighbor's G-buffer RAB_Surface neighborSurface = RAB_GetGBufferSurface(idx, false); // Get the PDF of the sample RIS selected in the first loop, above, *at this neighbor* const RAB_LightSample selectedSampleAtNeighbor = RAB_SamplePolymorphicLight( - selectedLight, neighborSurface, RTXDI_GetReservoirSampleUV(state)); + selectedLight, neighborSurface, RTXDI_GetDIReservoirSampleUV(state)); float ps = RAB_GetLightSampleTargetPdfForSurface(selectedSampleAtNeighbor, neighborSurface); @@ -1622,9 +650,9 @@ RTXDI_Reservoir RTXDI_SpatialResampling( } #endif - uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params); + uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params.activeCheckerboardField); - RTXDI_Reservoir neighborSample = RTXDI_LoadReservoir(params, + RTXDI_DIReservoir neighborSample = RTXDI_LoadDIReservoir(reservoirParams, neighborReservoirPos, sparams.sourceBufferIndex); // Select this sample for the (normalization) numerator if this particular neighbor pixel @@ -1650,7 +678,7 @@ RTXDI_Reservoir RTXDI_SpatialResampling( // A structure that groups the application-provided settings for spatio-temporal resampling. -struct RTXDI_SpatioTemporalResamplingParameters +struct RTXDI_DISpatioTemporalResamplingParameters { // Screen-space motion vector, computed as (previousPosition - currentPosition). // The X and Y components are measured in pixels. @@ -1703,23 +731,30 @@ struct RTXDI_SpatioTemporalResamplingParameters // Enables the comparison of surface materials before taking a surface into resampling. bool enableMaterialSimilarityTest; + + // Prevents samples which are from the current frame or have no reasonable temporal history merged being spread to neighbors + bool discountNaiveSamples; + + // Random number for permutation sampling that is the same for all pixels in the frame + uint uniformRandomNumber; }; // Fused spatialtemporal resampling pass, using pairwise MIS. // Inputs and outputs equivalent to RTXDI_SpatioTemporalResampling(), but only uses pairwise MIS. // Can call this directly, or call RTXDI_SpatioTemporalResampling() with sparams.biasCorrectionMode // set to RTXDI_BIAS_CORRECTION_PAIRWISE, which simply calls this function. -RTXDI_Reservoir RTXDI_SpatioTemporalResamplingWithPairwiseMIS( +RTXDI_DIReservoir RTXDI_DISpatioTemporalResamplingWithPairwiseMIS( uint2 pixelPosition, RAB_Surface surface, - RTXDI_Reservoir curSample, + RTXDI_DIReservoir curSample, inout RAB_RandomSamplerState rng, - RTXDI_SpatioTemporalResamplingParameters stparams, - RTXDI_ResamplingRuntimeParameters params, + RTXDI_RuntimeParameters params, + RTXDI_ReservoirBufferParameters reservoirParams, + RTXDI_DISpatioTemporalResamplingParameters stparams, out int2 temporalSamplePixelPos, inout RAB_LightSample selectedLightSample) { - uint historyLimit = min(RTXDI_PackedReservoir_MaxM, uint(stparams.maxHistoryLength * curSample.M)); + uint historyLimit = min(RTXDI_PackedDIReservoir_MaxM, uint(stparams.maxHistoryLength * curSample.M)); // Backproject this pixel to last frame float3 motion = stparams.screenSpaceMotion; @@ -1749,10 +784,10 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResamplingWithPairwiseMIS( centralIdx = prevPos + offset; if (stparams.enablePermutationSampling && i == 0) { - RTXDI_ApplyPermutationSampling(centralIdx, params.uniformRandomNumber); + RTXDI_ApplyPermutationSampling(centralIdx, stparams.uniformRandomNumber); } - RTXDI_ActivateCheckerboardPixel(centralIdx, true, params); + RTXDI_ActivateCheckerboardPixel(centralIdx, true, params.activeCheckerboardField); // Grab shading / g-buffer data from last frame temporalSurface = RAB_GetGBufferSurface(centralIdx, true); @@ -1780,22 +815,22 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResamplingWithPairwiseMIS( int validSamples = 0; // Create an empty reservoir we'll use to accumulate into - RTXDI_Reservoir state = RTXDI_EmptyReservoir(); + RTXDI_DIReservoir state = RTXDI_EmptyDIReservoir(); state.canonicalWeight = 0.0f; // Important this is 0 for temporal // Load the "temporal" reservoir at the temporally backprojected "central" pixel - RTXDI_Reservoir prevSample = RTXDI_LoadReservoir(params, - RTXDI_PixelPosToReservoirPos(centralIdx, params), stparams.sourceBufferIndex); + RTXDI_DIReservoir prevSample = RTXDI_LoadDIReservoir(reservoirParams, + RTXDI_PixelPosToReservoirPos(centralIdx, params.activeCheckerboardField), stparams.sourceBufferIndex); prevSample.M = min(prevSample.M, historyLimit); prevSample.spatialDistance += temporalSpatialOffset; prevSample.age += 1; // Find the prior frame's light in the current frame - int mappedLightID = RAB_TranslateLightIndex(RTXDI_GetReservoirLightIndex(prevSample), false); + int mappedLightID = RAB_TranslateLightIndex(RTXDI_GetDIReservoirLightIndex(prevSample), false); // Kill the reservoir if it doesn't exist in the current frame, otherwise update its ID for this frame prevSample.weightSum = (mappedLightID < 0) ? 0 : prevSample.weightSum; - prevSample.lightData = (mappedLightID < 0) ? 0 : mappedLightID | RTXDI_Reservoir_LightValidBit; + prevSample.lightData = (mappedLightID < 0) ? 0 : mappedLightID | RTXDI_DIReservoir_LightValidBit; // If we found a valid surface by backprojecting our current pixel, stream it through the reservoir. if (foundTemporalSurface && prevSample.M > 0) @@ -1825,7 +860,7 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResamplingWithPairwiseMIS( idx = RAB_ClampSamplePositionIntoView(idx, false); - RTXDI_ActivateCheckerboardPixel(idx, false, params); + RTXDI_ActivateCheckerboardPixel(idx, false, params.activeCheckerboardField); RAB_Surface neighborSurface = RAB_GetGBufferSurface(idx, true); @@ -1842,18 +877,25 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResamplingWithPairwiseMIS( continue; // The surfaces are similar enough so we *can* reuse a neighbor from this pixel, so load it. - RTXDI_Reservoir neighborSample = RTXDI_LoadReservoir(params, - RTXDI_PixelPosToReservoirPos(idx, params), stparams.sourceBufferIndex); + RTXDI_DIReservoir neighborSample = RTXDI_LoadDIReservoir(reservoirParams, + RTXDI_PixelPosToReservoirPos(idx, params.activeCheckerboardField), stparams.sourceBufferIndex); + + if (RTXDI_IsValidDIReservoir(prevSample)) + { + if (stparams.discountNaiveSamples && neighborSample.M <= RTXDI_NAIVE_SAMPLING_M_THRESHOLD) + continue; + } + neighborSample.M = min(neighborSample.M, historyLimit); neighborSample.spatialDistance += spatialOffset; neighborSample.age += 1; // Find the this neighbors light in the current frame (it may have turned off or moved in the ID list) - int mappedLightID = RAB_TranslateLightIndex(RTXDI_GetReservoirLightIndex(neighborSample), false); + int mappedLightID = RAB_TranslateLightIndex(RTXDI_GetDIReservoirLightIndex(neighborSample), false); // Kill the sample if the light doesn't exist in the current frame, otherwise update its ID for this frame neighborSample.weightSum = (mappedLightID < 0) ? 0 : neighborSample.weightSum; - neighborSample.lightData = (mappedLightID < 0) ? 0 : mappedLightID | RTXDI_Reservoir_LightValidBit; + neighborSample.lightData = (mappedLightID < 0) ? 0 : mappedLightID | RTXDI_DIReservoir_LightValidBit; if (mappedLightID < 0) continue; @@ -1879,8 +921,8 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResamplingWithPairwiseMIS( // Return the selected light sample. This is a redundant lookup and could be optimized away by storing // the selected sample from the stream steps above. selectedLightSample = RAB_SamplePolymorphicLight( - RAB_LoadLightInfo(RTXDI_GetReservoirLightIndex(state), false), - surface, RTXDI_GetReservoirSampleUV(state)); + RAB_LoadLightInfo(RTXDI_GetDIReservoirLightIndex(state), false), + surface, RTXDI_GetDIReservoirSampleUV(state)); return state; } @@ -1890,35 +932,36 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResamplingWithPairwiseMIS( // A combination of the temporal and spatial passes that operates only on the previous frame reservoirs. // The selectedLightSample parameter is used to update and return the selected sample; it's optional, // and it's safe to pass a null structure there and ignore the result. -RTXDI_Reservoir RTXDI_SpatioTemporalResampling( +RTXDI_DIReservoir RTXDI_DISpatioTemporalResampling( uint2 pixelPosition, RAB_Surface surface, - RTXDI_Reservoir curSample, + RTXDI_DIReservoir curSample, inout RAB_RandomSamplerState rng, - RTXDI_SpatioTemporalResamplingParameters stparams, - RTXDI_ResamplingRuntimeParameters params, + RTXDI_RuntimeParameters params, + RTXDI_ReservoirBufferParameters reservoirParams, + RTXDI_DISpatioTemporalResamplingParameters stparams, out int2 temporalSamplePixelPos, inout RAB_LightSample selectedLightSample) { if (stparams.biasCorrectionMode == RTXDI_BIAS_CORRECTION_PAIRWISE) { - return RTXDI_SpatioTemporalResamplingWithPairwiseMIS(pixelPosition, surface, - curSample, rng, stparams, params, temporalSamplePixelPos, selectedLightSample); + return RTXDI_DISpatioTemporalResamplingWithPairwiseMIS(pixelPosition, surface, + curSample, rng, params, reservoirParams, stparams, temporalSamplePixelPos, selectedLightSample); } - uint historyLimit = min(RTXDI_PackedReservoir_MaxM, uint(stparams.maxHistoryLength * curSample.M)); + uint historyLimit = min(RTXDI_PackedDIReservoir_MaxM, uint(stparams.maxHistoryLength * curSample.M)); int selectedLightPrevID = -1; - if (RTXDI_IsValidReservoir(curSample)) + if (RTXDI_IsValidDIReservoir(curSample)) { - selectedLightPrevID = RAB_TranslateLightIndex(RTXDI_GetReservoirLightIndex(curSample), true); + selectedLightPrevID = RAB_TranslateLightIndex(RTXDI_GetDIReservoirLightIndex(curSample), true); } temporalSamplePixelPos = int2(-1, -1); - RTXDI_Reservoir state = RTXDI_EmptyReservoir(); - RTXDI_CombineReservoirs(state, curSample, /* random = */ 0.5, curSample.targetPdf); + RTXDI_DIReservoir state = RTXDI_EmptyDIReservoir(); + RTXDI_CombineDIReservoirs(state, curSample, /* random = */ 0.5, curSample.targetPdf); uint startIdx = uint(RAB_GetNextRandom(rng) * params.neighborOffsetMask); @@ -1956,10 +999,10 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResampling( if (stparams.enablePermutationSampling && i == 0) { - RTXDI_ApplyPermutationSampling(idx, params.uniformRandomNumber); + RTXDI_ApplyPermutationSampling(idx, stparams.uniformRandomNumber); } - RTXDI_ActivateCheckerboardPixel(idx, true, params); + RTXDI_ActivateCheckerboardPixel(idx, true, params.activeCheckerboardField); // Grab shading / g-buffer data from last frame temporalSurface = RAB_GetGBufferSurface(idx, true); @@ -2012,7 +1055,7 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResampling( idx = RAB_ClampSamplePositionIntoView(idx, true); - RTXDI_ActivateCheckerboardPixel(idx, true, params); + RTXDI_ActivateCheckerboardPixel(idx, true, params.activeCheckerboardField); temporalSurface = RAB_GetGBufferSurface(idx, true); @@ -2030,26 +1073,32 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResampling( cachedResult |= (1u << uint(i)); - uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params); + uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params.activeCheckerboardField); - RTXDI_Reservoir prevSample = RTXDI_LoadReservoir(params, + RTXDI_DIReservoir prevSample = RTXDI_LoadDIReservoir(reservoirParams, neighborReservoirPos, stparams.sourceBufferIndex); + if (RTXDI_IsValidDIReservoir(prevSample)) + { + if (stparams.discountNaiveSamples && prevSample.M <= RTXDI_NAIVE_SAMPLING_M_THRESHOLD) + continue; + } + prevSample.M = min(prevSample.M, historyLimit); prevSample.spatialDistance += spatialOffset; prevSample.age += 1; - uint originalPrevLightID = RTXDI_GetReservoirLightIndex(prevSample); + uint originalPrevLightID = RTXDI_GetDIReservoirLightIndex(prevSample); // Map the light ID from the previous frame into the current frame, if it still exists - if (RTXDI_IsValidReservoir(prevSample)) + if (RTXDI_IsValidDIReservoir(prevSample)) { if (i == 0 && foundTemporalSurface && prevSample.age <= 1) { temporalSamplePixelPos = idx; } - int mappedLightID = RAB_TranslateLightIndex(RTXDI_GetReservoirLightIndex(prevSample), false); + int mappedLightID = RAB_TranslateLightIndex(RTXDI_GetDIReservoirLightIndex(prevSample), false); if (mappedLightID < 0) { @@ -2060,7 +1109,7 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResampling( else { // Sample is valid - modify the light ID stored - prevSample.lightData = mappedLightID | RTXDI_Reservoir_LightValidBit; + prevSample.lightData = mappedLightID | RTXDI_DIReservoir_LightValidBit; } } @@ -2069,17 +1118,17 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResampling( // Load that neighbor's RIS state, do resampling float neighborWeight = 0; RAB_LightSample candidateLightSample = RAB_EmptyLightSample(); - if (RTXDI_IsValidReservoir(prevSample)) + if (RTXDI_IsValidDIReservoir(prevSample)) { - candidateLight = RAB_LoadLightInfo(RTXDI_GetReservoirLightIndex(prevSample), false); + candidateLight = RAB_LoadLightInfo(RTXDI_GetDIReservoirLightIndex(prevSample), false); candidateLightSample = RAB_SamplePolymorphicLight( - candidateLight, surface, RTXDI_GetReservoirSampleUV(prevSample)); + candidateLight, surface, RTXDI_GetDIReservoirSampleUV(prevSample)); neighborWeight = RAB_GetLightSampleTargetPdfForSurface(candidateLightSample, surface); } - if (RTXDI_CombineReservoirs(state, prevSample, RAB_GetNextRandom(rng), neighborWeight)) + if (RTXDI_CombineDIReservoirs(state, prevSample, RAB_GetNextRandom(rng), neighborWeight)) { selected = i; selectedLightPrevID = int(originalPrevLightID); @@ -2087,7 +1136,7 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResampling( } } - if (RTXDI_IsValidReservoir(state)) + if (RTXDI_IsValidDIReservoir(state)) { #if RTXDI_ALLOWED_BIAS_CORRECTION >= RTXDI_BIAS_CORRECTION_BASIC if (stparams.biasCorrectionMode >= RTXDI_BIAS_CORRECTION_BASIC) @@ -2119,14 +1168,14 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResampling( idx = RAB_ClampSamplePositionIntoView(idx, true); } - RTXDI_ActivateCheckerboardPixel(idx, true, params); + RTXDI_ActivateCheckerboardPixel(idx, true, params.activeCheckerboardField); // Load our neighbor's G-buffer RAB_Surface neighborSurface = RAB_GetGBufferSurface(idx, true); // Get the PDF of the sample RIS selected in the first loop, above, *at this neighbor* const RAB_LightSample selectedSampleAtNeighbor = RAB_SamplePolymorphicLight( - selectedLightPrev, neighborSurface, RTXDI_GetReservoirSampleUV(state)); + selectedLightPrev, neighborSurface, RTXDI_GetDIReservoirSampleUV(state)); float ps = RAB_GetLightSampleTargetPdfForSurface(selectedSampleAtNeighbor, neighborSurface); @@ -2147,9 +1196,9 @@ RTXDI_Reservoir RTXDI_SpatioTemporalResampling( } #endif - uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params); + uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params.activeCheckerboardField); - RTXDI_Reservoir prevSample = RTXDI_LoadReservoir(params, + RTXDI_DIReservoir prevSample = RTXDI_LoadDIReservoir(reservoirParams, neighborReservoirPos, stparams.sourceBufferIndex); prevSample.M = min(prevSample.M, historyLimit); diff --git a/rtxdi-sdk/include/rtxdi/DIReservoir.hlsli b/rtxdi-sdk/include/rtxdi/DIReservoir.hlsli new file mode 100644 index 0000000..6529a76 --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/DIReservoir.hlsli @@ -0,0 +1,342 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef RTXDI_DIRESERVOIR_HLSLI +#define RTXDI_DIRESERVOIR_HLSLI + +#include "RtxdiParameters.h" +#include "RtxdiHelpers.hlsli" + +#ifndef RTXDI_LIGHT_RESERVOIR_BUFFER +#error "RTXDI_LIGHT_RESERVOIR_BUFFER must be defined to point to a RWStructuredBuffer type resource" +#endif + +// Define this macro to 0 if your shader needs read-only access to the reservoirs, +// to avoid compile errors in the RTXDI_StoreDIReservoir function +#ifndef RTXDI_ENABLE_STORE_RESERVOIR +#define RTXDI_ENABLE_STORE_RESERVOIR 1 +#endif + +// This structure represents a single light reservoir that stores the weights, the sample ref, +// sample count (M), and visibility for reuse. It can be serialized into RTXDI_PackedDIReservoir for storage. +struct RTXDI_DIReservoir +{ + // Light index (bits 0..30) and validity bit (31) + uint lightData; + + // Sample UV encoded in 16-bit fixed point format + uint uvData; + + // Overloaded: represents RIS weight sum during streaming, + // then reservoir weight (inverse PDF) after FinalizeResampling + float weightSum; + + // Target PDF of the selected sample + float targetPdf; + + // Number of samples considered for this reservoir (pairwise MIS makes this a float) + float M; + + // Visibility information stored in the reservoir for reuse + uint packedVisibility; + + // Screen-space distance between the current location of the reservoir + // and the location where the visibility information was generated, + // minus the motion vectors applied in temporal resampling + int2 spatialDistance; + + // How many frames ago the visibility information was generated + uint age; + + // Cannonical weight when using pairwise MIS (ignored except during pairwise MIS computations) + float canonicalWeight; +}; + +// Encoding helper constants for RTXDI_PackedDIReservoir.mVisibility +static const uint RTXDI_PackedDIReservoir_VisibilityMask = 0x3ffff; +static const uint RTXDI_PackedDIReservoir_VisibilityChannelMax = 0x3f; +static const uint RTXDI_PackedDIReservoir_VisibilityChannelShift = 6; +static const uint RTXDI_PackedDIReservoir_MShift = 18; +static const uint RTXDI_PackedDIReservoir_MaxM = 0x3fff; + +// Encoding helper constants for RTXDI_PackedDIReservoir.distanceAge +static const uint RTXDI_PackedDIReservoir_DistanceChannelBits = 8; +static const uint RTXDI_PackedDIReservoir_DistanceXShift = 0; +static const uint RTXDI_PackedDIReservoir_DistanceYShift = 8; +static const uint RTXDI_PackedDIReservoir_AgeShift = 16; +static const uint RTXDI_PackedDIReservoir_MaxAge = 0xff; +static const uint RTXDI_PackedDIReservoir_DistanceMask = (1u << RTXDI_PackedDIReservoir_DistanceChannelBits) - 1; +static const int RTXDI_PackedDIReservoir_MaxDistance = int((1u << (RTXDI_PackedDIReservoir_DistanceChannelBits - 1)) - 1); + +// Light index helpers +static const uint RTXDI_DIReservoir_LightValidBit = 0x80000000; +static const uint RTXDI_DIReservoir_LightIndexMask = 0x7FFFFFFF; + +RTXDI_PackedDIReservoir RTXDI_PackDIReservoir(const RTXDI_DIReservoir reservoir) +{ + int2 clampedSpatialDistance = clamp(reservoir.spatialDistance, -RTXDI_PackedDIReservoir_MaxDistance, RTXDI_PackedDIReservoir_MaxDistance); + uint clampedAge = clamp(reservoir.age, 0, RTXDI_PackedDIReservoir_MaxAge); + + RTXDI_PackedDIReservoir data; + data.lightData = reservoir.lightData; + data.uvData = reservoir.uvData; + + data.mVisibility = reservoir.packedVisibility + | (min(uint(reservoir.M), RTXDI_PackedDIReservoir_MaxM) << RTXDI_PackedDIReservoir_MShift); + + data.distanceAge = + ((clampedSpatialDistance.x & RTXDI_PackedDIReservoir_DistanceMask) << RTXDI_PackedDIReservoir_DistanceXShift) + | ((clampedSpatialDistance.y & RTXDI_PackedDIReservoir_DistanceMask) << RTXDI_PackedDIReservoir_DistanceYShift) + | (clampedAge << RTXDI_PackedDIReservoir_AgeShift); + + data.targetPdf = reservoir.targetPdf; + data.weight = reservoir.weightSum; + + return data; +} + +#if RTXDI_ENABLE_STORE_RESERVOIR +void RTXDI_StoreDIReservoir( + const RTXDI_DIReservoir reservoir, + RTXDI_ReservoirBufferParameters reservoirParams, + uint2 reservoirPosition, + uint reservoirArrayIndex) +{ + uint pointer = RTXDI_DIReservoirPositionToPointer(reservoirParams, reservoirPosition, reservoirArrayIndex); + RTXDI_LIGHT_RESERVOIR_BUFFER[pointer] = RTXDI_PackDIReservoir(reservoir); +} +#endif // RTXDI_ENABLE_STORE_RESERVOIR + +RTXDI_DIReservoir RTXDI_EmptyDIReservoir() +{ + RTXDI_DIReservoir s; + s.lightData = 0; + s.uvData = 0; + s.targetPdf = 0; + s.weightSum = 0; + s.M = 0; + s.packedVisibility = 0; + s.spatialDistance = int2(0, 0); + s.age = 0; + s.canonicalWeight = 0; + return s; +} + +RTXDI_DIReservoir RTXDI_UnpackDIReservoir(RTXDI_PackedDIReservoir data) +{ + RTXDI_DIReservoir res; + res.lightData = data.lightData; + res.uvData = data.uvData; + res.targetPdf = data.targetPdf; + res.weightSum = data.weight; + res.M = (data.mVisibility >> RTXDI_PackedDIReservoir_MShift) & RTXDI_PackedDIReservoir_MaxM; + res.packedVisibility = data.mVisibility & RTXDI_PackedDIReservoir_VisibilityMask; + // Sign extend the shift values + res.spatialDistance.x = int(data.distanceAge << (32 - RTXDI_PackedDIReservoir_DistanceXShift - RTXDI_PackedDIReservoir_DistanceChannelBits)) >> (32 - RTXDI_PackedDIReservoir_DistanceChannelBits); + res.spatialDistance.y = int(data.distanceAge << (32 - RTXDI_PackedDIReservoir_DistanceYShift - RTXDI_PackedDIReservoir_DistanceChannelBits)) >> (32 - RTXDI_PackedDIReservoir_DistanceChannelBits); + res.age = (data.distanceAge >> RTXDI_PackedDIReservoir_AgeShift) & RTXDI_PackedDIReservoir_MaxAge; + res.canonicalWeight = 0.0f; + + // Discard reservoirs that have Inf/NaN + if (isinf(res.weightSum) || isnan(res.weightSum)) { + res = RTXDI_EmptyDIReservoir(); + } + + return res; +} + +RTXDI_DIReservoir RTXDI_LoadDIReservoir( + RTXDI_ReservoirBufferParameters reservoirParams, + uint2 reservoirPosition, + uint reservoirArrayIndex) +{ + uint pointer = RTXDI_DIReservoirPositionToPointer(reservoirParams, reservoirPosition, reservoirArrayIndex); + return RTXDI_UnpackDIReservoir(RTXDI_LIGHT_RESERVOIR_BUFFER[pointer]); +} + +void RTXDI_StoreVisibilityInDIReservoir( + inout RTXDI_DIReservoir reservoir, + float3 visibility, + bool discardIfInvisible) +{ + reservoir.packedVisibility = uint(saturate(visibility.x) * RTXDI_PackedDIReservoir_VisibilityChannelMax) + | (uint(saturate(visibility.y) * RTXDI_PackedDIReservoir_VisibilityChannelMax)) << RTXDI_PackedDIReservoir_VisibilityChannelShift + | (uint(saturate(visibility.z) * RTXDI_PackedDIReservoir_VisibilityChannelMax)) << (RTXDI_PackedDIReservoir_VisibilityChannelShift * 2); + + reservoir.spatialDistance = int2(0, 0); + reservoir.age = 0; + + if (discardIfInvisible && visibility.x == 0 && visibility.y == 0 && visibility.z == 0) + { + // Keep M for correct resampling, remove the actual sample + reservoir.lightData = 0; + reservoir.weightSum = 0; + } +} + +// Structure that groups the parameters for RTXDI_GetReservoirVisibility(...) +// Reusing final visibility reduces the number of high-quality shadow rays needed to shade +// the scene, at the cost of somewhat softer or laggier shadows. +struct RTXDI_VisibilityReuseParameters +{ + // Controls the maximum age of the final visibility term, measured in frames, that can be reused from the + // previous frame(s). Higher values result in better performance. + uint maxAge; + + // Controls the maximum distance in screen space between the current pixel and the pixel that has + // produced the final visibility term. The distance does not include the motion vectors. + // Higher values result in better performance and softer shadows. + float maxDistance; +}; + +bool RTXDI_GetDIReservoirVisibility( + const RTXDI_DIReservoir reservoir, + const RTXDI_VisibilityReuseParameters params, + out float3 o_visibility) +{ + if (reservoir.age > 0 && + reservoir.age <= params.maxAge && + length(float2(reservoir.spatialDistance)) < params.maxDistance) + { + o_visibility.x = float(reservoir.packedVisibility & RTXDI_PackedDIReservoir_VisibilityChannelMax) / RTXDI_PackedDIReservoir_VisibilityChannelMax; + o_visibility.y = float((reservoir.packedVisibility >> RTXDI_PackedDIReservoir_VisibilityChannelShift) & RTXDI_PackedDIReservoir_VisibilityChannelMax) / RTXDI_PackedDIReservoir_VisibilityChannelMax; + o_visibility.z = float((reservoir.packedVisibility >> (RTXDI_PackedDIReservoir_VisibilityChannelShift * 2)) & RTXDI_PackedDIReservoir_VisibilityChannelMax) / RTXDI_PackedDIReservoir_VisibilityChannelMax; + + return true; + } + + o_visibility = float3(0, 0, 0); + return false; +} + +bool RTXDI_IsValidDIReservoir(const RTXDI_DIReservoir reservoir) +{ + return reservoir.lightData != 0; +} + +uint RTXDI_GetDIReservoirLightIndex(const RTXDI_DIReservoir reservoir) +{ + return reservoir.lightData & RTXDI_DIReservoir_LightIndexMask; +} + +float2 RTXDI_GetDIReservoirSampleUV(const RTXDI_DIReservoir reservoir) +{ + return float2(reservoir.uvData & 0xffff, reservoir.uvData >> 16) / float(0xffff); +} + +float RTXDI_GetDIReservoirInvPdf(const RTXDI_DIReservoir reservoir) +{ + return reservoir.weightSum; +} + +// Adds a new, non-reservoir light sample into the reservoir, returns true if this sample was selected. +// Algorithm (3) from the ReSTIR paper, Streaming RIS using weighted reservoir sampling. +bool RTXDI_StreamSample( + inout RTXDI_DIReservoir reservoir, + uint lightIndex, + float2 uv, + float random, + float targetPdf, + float invSourcePdf) +{ + // What's the current weight + float risWeight = targetPdf * invSourcePdf; + + // Add one sample to the counter + reservoir.M += 1; + + // Update the weight sum + reservoir.weightSum += risWeight; + + // Decide if we will randomly pick this sample + bool selectSample = (random * reservoir.weightSum < risWeight); + + // If we did select this sample, update the relevant data. + // New samples don't have visibility or age information, we can skip that. + if (selectSample) + { + reservoir.lightData = lightIndex | RTXDI_DIReservoir_LightValidBit; + reservoir.uvData = uint(saturate(uv.x) * 0xffff) | (uint(saturate(uv.y) * 0xffff) << 16); + reservoir.targetPdf = targetPdf; + } + + return selectSample; +} + +// Adds `newReservoir` into `reservoir`, returns true if the new reservoir's sample was selected. +// This is a very general form, allowing input parameters to specfiy normalization and targetPdf +// rather than computing them from `newReservoir`. Named "internal" since these parameters take +// different meanings (e.g., in RTXDI_CombineDIReservoirs() or RTXDI_StreamNeighborWithPairwiseMIS()) +bool RTXDI_InternalSimpleResample( + inout RTXDI_DIReservoir reservoir, + const RTXDI_DIReservoir newReservoir, + float random, + float targetPdf RTXDI_DEFAULT(1.0f), // Usually closely related to the sample normalization, + float sampleNormalization RTXDI_DEFAULT(1.0f), // typically off by some multiplicative factor + float sampleM RTXDI_DEFAULT(1.0f) // In its most basic form, should be newReservoir.M +) +{ + // What's the current weight (times any prior-step RIS normalization factor) + float risWeight = targetPdf * sampleNormalization; + + // Our *effective* candidate pool is the sum of our candidates plus those of our neighbors + reservoir.M += sampleM; + + // Update the weight sum + reservoir.weightSum += risWeight; + + // Decide if we will randomly pick this sample + bool selectSample = (random * reservoir.weightSum < risWeight); + + // If we did select this sample, update the relevant data + if (selectSample) + { + reservoir.lightData = newReservoir.lightData; + reservoir.uvData = newReservoir.uvData; + reservoir.targetPdf = targetPdf; + reservoir.packedVisibility = newReservoir.packedVisibility; + reservoir.spatialDistance = newReservoir.spatialDistance; + reservoir.age = newReservoir.age; + } + + return selectSample; +} + +// Adds `newReservoir` into `reservoir`, returns true if the new reservoir's sample was selected. +// Algorithm (4) from the ReSTIR paper, Combining the streams of multiple reservoirs. +// Normalization - Equation (6) - is postponed until all reservoirs are combined. +bool RTXDI_CombineDIReservoirs( + inout RTXDI_DIReservoir reservoir, + const RTXDI_DIReservoir newReservoir, + float random, + float targetPdf) +{ + return RTXDI_InternalSimpleResample( + reservoir, + newReservoir, + random, + targetPdf, + newReservoir.weightSum * newReservoir.M, + newReservoir.M + ); +} + +// Performs normalization of the reservoir after streaming. Equation (6) from the ReSTIR paper. +void RTXDI_FinalizeResampling( + inout RTXDI_DIReservoir reservoir, + float normalizationNumerator, + float normalizationDenominator) +{ + float denominator = reservoir.targetPdf * normalizationDenominator; + + reservoir.weightSum = (denominator == 0.0) ? 0.0 : (reservoir.weightSum * normalizationNumerator) / denominator; +} + +#endif // RTXDI_DIRESERVOIR_HLSLI diff --git a/rtxdi-sdk/include/rtxdi/GIResamplingFunctions.hlsli b/rtxdi-sdk/include/rtxdi/GIResamplingFunctions.hlsli index c09fce6..ca368c8 100644 --- a/rtxdi-sdk/include/rtxdi/GIResamplingFunctions.hlsli +++ b/rtxdi-sdk/include/rtxdi/GIResamplingFunctions.hlsli @@ -129,9 +129,9 @@ int2 RTXDI_CalculateTemporalResamplingOffset(int sampleIdx, int radius) return int2(tmp0, tmp0 * tmp1) * int2(tmp2, tmp3) * radius; } -int2 RTXDI_CalculateSpatialResamplingOffset(int sampleIdx, float radius, const RTXDI_ResamplingRuntimeParameters params) +int2 RTXDI_CalculateSpatialResamplingOffset(int sampleIdx, float radius, const uint neighborOffsetMask) { - sampleIdx &= int(params.neighborOffsetMask); + sampleIdx &= int(neighborOffsetMask); return int2(float2(RTXDI_NEIGHBOR_OFFSETS_BUFFER[sampleIdx].xy) * radius); } @@ -177,6 +177,9 @@ struct RTXDI_GITemporalResamplingParameters // in case no surface near the motion vector matches the current surface (e.g. disocclusion). // This behavoir makes disocclusion areas less noisy but locally biased, usually darker. bool enableFallbackSampling; + + // Random number for permutation sampling that is the same for all pixels in the frame + uint uniformRandomNumber; }; // Temporal resampling for GI reservoir pass. @@ -185,8 +188,9 @@ RTXDI_GIReservoir RTXDI_GITemporalResampling( const RAB_Surface surface, const RTXDI_GIReservoir inputReservoir, inout RAB_RandomSamplerState rng, - const RTXDI_GITemporalResamplingParameters tparams, - const RTXDI_ResamplingRuntimeParameters params) + const RTXDI_RuntimeParameters params, + const RTXDI_ReservoirBufferParameters reservoirParams, + const RTXDI_GITemporalResamplingParameters tparams) { // Backproject this pixel to last frame int2 prevPos = int2(round(float2(pixelPosition) + tparams.screenSpaceMotion.xy)); @@ -224,10 +228,10 @@ RTXDI_GIReservoir RTXDI_GITemporalResampling( { // Apply permutation sampling for the first (non-jittered) sample, // also for the last (fallback) sample to prevent visible repeating patterns in disocclusions. - RTXDI_ApplyPermutationSampling(idx, params.uniformRandomNumber); + RTXDI_ApplyPermutationSampling(idx, tparams.uniformRandomNumber); } - RTXDI_ActivateCheckerboardPixel(idx, true, params); + RTXDI_ActivateCheckerboardPixel(idx, true, params.activeCheckerboardField); // Grab shading / g-buffer data from last frame temporalSurface = RAB_GetGBufferSurface(idx, true); @@ -254,8 +258,8 @@ RTXDI_GIReservoir RTXDI_GITemporalResampling( } // Read temporal reservoir. - uint2 prevReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params); - temporalReservoir = RTXDI_LoadGIReservoir(params, prevReservoirPos, tparams.sourceBufferIndex); + uint2 prevReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params.activeCheckerboardField); + temporalReservoir = RTXDI_LoadGIReservoir(reservoirParams, prevReservoirPos, tparams.sourceBufferIndex); // Check if the reservoir is a valid one. if (!RTXDI_IsValidGIReservoir(temporalReservoir)) @@ -323,7 +327,7 @@ RTXDI_GIReservoir RTXDI_GITemporalResampling( { float temporalP = RAB_GetGISampleTargetPdfForSurface(curReservoir.position, curReservoir.radiance, temporalSurface); -#if RTXDI_ALLOWED_BIAS_CORRECTION >= RTXDI_BIAS_CORRECTION_RAY_TRACED +#if RTXDI_GI_ALLOWED_BIAS_CORRECTION >= RTXDI_BIAS_CORRECTION_RAY_TRACED if (tparams.biasCorrectionMode == RTXDI_BIAS_CORRECTION_RAY_TRACED && temporalP > 0) { if (!RAB_GetTemporalConservativeVisibility(surface, temporalSurface, curReservoir.position)) @@ -389,8 +393,9 @@ RTXDI_GIReservoir RTXDI_GISpatialResampling( const RAB_Surface surface, const RTXDI_GIReservoir inputReservoir, inout RAB_RandomSamplerState rng, - const RTXDI_GISpatialResamplingParameters sparams, - const RTXDI_ResamplingRuntimeParameters params) + const RTXDI_RuntimeParameters params, + const RTXDI_ReservoirBufferParameters reservoirParams, + const RTXDI_GISpatialResamplingParameters sparams) { const uint numSamples = sparams.numSamples; @@ -417,11 +422,11 @@ RTXDI_GIReservoir RTXDI_GISpatialResampling( for (int i = 0; i < numSamples; ++i) { // Get screen-space location of neighbor - int2 idx = int2(pixelPosition) + RTXDI_CalculateSpatialResamplingOffset(neighborSampleStartIdx + i, sparams.samplingRadius, params); + int2 idx = int2(pixelPosition) + RTXDI_CalculateSpatialResamplingOffset(neighborSampleStartIdx + i, sparams.samplingRadius, params.neighborOffsetMask); idx = RAB_ClampSamplePositionIntoView(idx, false); - RTXDI_ActivateCheckerboardPixel(idx, false, params); + RTXDI_ActivateCheckerboardPixel(idx, false, params.activeCheckerboardField); RAB_Surface neighborSurface = RAB_GetGBufferSurface(idx, false); @@ -440,8 +445,8 @@ RTXDI_GIReservoir RTXDI_GISpatialResampling( continue; } - const uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params); - RTXDI_GIReservoir neighborReservoir = RTXDI_LoadGIReservoir(params, neighborReservoirPos, sparams.sourceBufferIndex); + const uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params.activeCheckerboardField); + RTXDI_GIReservoir neighborReservoir = RTXDI_LoadGIReservoir(reservoirParams, neighborReservoirPos, sparams.sourceBufferIndex); if (!RTXDI_IsValidGIReservoir(neighborReservoir)) { @@ -491,17 +496,17 @@ RTXDI_GIReservoir RTXDI_GISpatialResampling( if ((cachedResult & (1u << uint(i))) == 0) continue; // Get the screen-space location of our neighbor - int2 idx = int2(pixelPosition) + RTXDI_CalculateSpatialResamplingOffset(neighborSampleStartIdx + i, sparams.samplingRadius, params); + int2 idx = int2(pixelPosition) + RTXDI_CalculateSpatialResamplingOffset(neighborSampleStartIdx + i, sparams.samplingRadius, params.neighborOffsetMask); idx = RAB_ClampSamplePositionIntoView(idx, false); - RTXDI_ActivateCheckerboardPixel(idx, false, params); + RTXDI_ActivateCheckerboardPixel(idx, false, params.activeCheckerboardField); // Load our neighbor's G-buffer and its GI reservoir again. RAB_Surface neighborSurface = RAB_GetGBufferSurface(idx, false); - const uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params); - RTXDI_GIReservoir neighborReservoir = RTXDI_LoadGIReservoir(params, neighborReservoirPos, sparams.sourceBufferIndex); + const uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params.activeCheckerboardField); + RTXDI_GIReservoir neighborReservoir = RTXDI_LoadGIReservoir(reservoirParams, neighborReservoirPos, sparams.sourceBufferIndex); // Get the PDF of the sample RIS selected in the first loop, above, *at this neighbor* float ps = RAB_GetGISampleTargetPdfForSurface(curReservoir.position, curReservoir.radiance, neighborSurface); @@ -598,6 +603,9 @@ struct RTXDI_GISpatioTemporalResamplingParameters // in case no surface near the motion vector matches the current surface (e.g. disocclusion). // This behavior makes disocclusion areas less noisy but locally biased, usually darker. bool enableFallbackSampling; + + // Random number for permutation sampling that is the same for all pixels in the frame + uint uniformRandomNumber; }; RTXDI_GIReservoir RTXDI_GISpatioTemporalResampling( @@ -605,8 +613,9 @@ RTXDI_GIReservoir RTXDI_GISpatioTemporalResampling( const RAB_Surface surface, RTXDI_GIReservoir inputReservoir, inout RAB_RandomSamplerState rng, - const RTXDI_GISpatioTemporalResamplingParameters stparams, - const RTXDI_ResamplingRuntimeParameters params) + const RTXDI_RuntimeParameters params, + const RTXDI_ReservoirBufferParameters reservoirParams, + const RTXDI_GISpatioTemporalResamplingParameters stparams) { // Backproject this pixel to last frame int2 prevPos = int2(round(float2(pixelPosition) + stparams.screenSpaceMotion.xy)); @@ -689,7 +698,7 @@ RTXDI_GIReservoir RTXDI_GISpatioTemporalResampling( idx = prevPos; if (stparams.enablePermutationSampling || isFallbackSample) - RTXDI_ApplyPermutationSampling(idx, params.uniformRandomNumber); + RTXDI_ApplyPermutationSampling(idx, stparams.uniformRandomNumber); } else if (isJitteredTemporalSample) { @@ -697,11 +706,11 @@ RTXDI_GIReservoir RTXDI_GISpatioTemporalResampling( } else { - idx = prevPos + RTXDI_CalculateSpatialResamplingOffset(neighborSampleStartIdx + i, stparams.samplingRadius, params); + idx = prevPos + RTXDI_CalculateSpatialResamplingOffset(neighborSampleStartIdx + i, stparams.samplingRadius, params.neighborOffsetMask); idx = RAB_ClampSamplePositionIntoView(idx, true); } - RTXDI_ActivateCheckerboardPixel(idx, true, params); + RTXDI_ActivateCheckerboardPixel(idx, true, params.activeCheckerboardField); RAB_Surface neighborSurface = RAB_GetGBufferSurface(idx, true); @@ -721,8 +730,8 @@ RTXDI_GIReservoir RTXDI_GISpatioTemporalResampling( continue; } - const uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params); - RTXDI_GIReservoir neighborReservoir = RTXDI_LoadGIReservoir(params, neighborReservoirPos, stparams.sourceBufferIndex); + const uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params.activeCheckerboardField); + RTXDI_GIReservoir neighborReservoir = RTXDI_LoadGIReservoir(reservoirParams, neighborReservoirPos, stparams.sourceBufferIndex); if (!RTXDI_IsValidGIReservoir(neighborReservoir)) { @@ -797,7 +806,7 @@ RTXDI_GIReservoir RTXDI_GISpatioTemporalResampling( idx = prevPos; if (stparams.enablePermutationSampling || isFallbackSample) - RTXDI_ApplyPermutationSampling(idx, params.uniformRandomNumber); + RTXDI_ApplyPermutationSampling(idx, stparams.uniformRandomNumber); } else if (isJitteredTemporalSample) { @@ -805,17 +814,17 @@ RTXDI_GIReservoir RTXDI_GISpatioTemporalResampling( } else { - idx = prevPos + RTXDI_CalculateSpatialResamplingOffset(neighborSampleStartIdx + i, stparams.samplingRadius, params); + idx = prevPos + RTXDI_CalculateSpatialResamplingOffset(neighborSampleStartIdx + i, stparams.samplingRadius, params.neighborOffsetMask); idx = RAB_ClampSamplePositionIntoView(idx, true); } - RTXDI_ActivateCheckerboardPixel(idx, true, params); + RTXDI_ActivateCheckerboardPixel(idx, true, params.activeCheckerboardField); // Load our neighbor's G-buffer and its GI reservoir again. RAB_Surface neighborSurface = RAB_GetGBufferSurface(idx, true); - const uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params); - RTXDI_GIReservoir neighborReservoir = RTXDI_LoadGIReservoir(params, neighborReservoirPos, stparams.sourceBufferIndex); + const uint2 neighborReservoirPos = RTXDI_PixelPosToReservoirPos(idx, params.activeCheckerboardField); + RTXDI_GIReservoir neighborReservoir = RTXDI_LoadGIReservoir(reservoirParams, neighborReservoirPos, stparams.sourceBufferIndex); // Clamp history length neighborReservoir.M = min(neighborReservoir.M, stparams.maxHistoryLength); @@ -876,12 +885,11 @@ RTXDI_GIReservoir RTXDI_GISpatioTemporalResampling( void RTXDI_GIBoilingFilter( uint2 LocalIndex, float filterStrength, // (0..1] - RTXDI_ResamplingRuntimeParameters params, inout RTXDI_GIReservoir reservoir) { float weight = RTXDI_Luminance(reservoir.radiance) * reservoir.weightSum; - if (RTXDI_BoilingFilterInternal(LocalIndex, filterStrength, params, weight)) + if (RTXDI_BoilingFilterInternal(LocalIndex, filterStrength, weight)) reservoir = RTXDI_EmptyGIReservoir(); } diff --git a/rtxdi-sdk/include/rtxdi/GIReservoir.hlsli b/rtxdi-sdk/include/rtxdi/GIReservoir.hlsli index c2f1c29..f04b32a 100644 --- a/rtxdi-sdk/include/rtxdi/GIReservoir.hlsli +++ b/rtxdi-sdk/include/rtxdi/GIReservoir.hlsli @@ -11,11 +11,11 @@ #ifndef GI_RESERVOIR_HLSLI #define GI_RESERVOIR_HLSLI -#include "RtxdiParameters.h" +#include "ReSTIRGIParameters.h" #include "RtxdiHelpers.hlsli" // Define this macro to 0 if your shader needs read-only access to the reservoirs, -// to avoid compile errors in the RTXDI_StoreReservoir function +// to avoid compile errors in the RTXDI_StoreDIReservoir function #ifndef RTXDI_ENABLE_STORE_RESERVOIR #define RTXDI_ENABLE_STORE_RESERVOIR 1 #endif @@ -112,21 +112,21 @@ RTXDI_GIReservoir RTXDI_UnpackGIReservoir(RTXDI_PackedGIReservoir data) } RTXDI_GIReservoir RTXDI_LoadGIReservoir( - RTXDI_ResamplingRuntimeParameters params, + RTXDI_ReservoirBufferParameters reservoirParams, uint2 reservoirPosition, uint reservoirArrayIndex) { - uint pointer = RTXDI_ReservoirPositionToPointer(params, reservoirPosition, reservoirArrayIndex); + uint pointer = RTXDI_DIReservoirPositionToPointer(reservoirParams, reservoirPosition, reservoirArrayIndex); return RTXDI_UnpackGIReservoir(RTXDI_GI_RESERVOIR_BUFFER[pointer]); } RTXDI_GIReservoir RTXDI_LoadGIReservoir( - RTXDI_ResamplingRuntimeParameters params, + RTXDI_ReservoirBufferParameters reservoirParams, uint2 reservoirPosition, uint reservoirArrayIndex, out uint miscFlags) { - uint pointer = RTXDI_ReservoirPositionToPointer(params, reservoirPosition, reservoirArrayIndex); + uint pointer = RTXDI_DIReservoirPositionToPointer(reservoirParams, reservoirPosition, reservoirArrayIndex); return RTXDI_UnpackGIReservoir(RTXDI_GI_RESERVOIR_BUFFER[pointer], miscFlags); } @@ -134,33 +134,33 @@ RTXDI_GIReservoir RTXDI_LoadGIReservoir( void RTXDI_StorePackedGIReservoir( const RTXDI_PackedGIReservoir packedGIReservoir, - RTXDI_ResamplingRuntimeParameters params, + ReSTIRGI_ReservoirBufferParameters reservoirParams, uint2 reservoirPosition, uint reservoirArrayIndex) { - uint pointer = RTXDI_ReservoirPositionToPointer(params, reservoirPosition, reservoirArrayIndex); + uint pointer = RTXDI_DIReservoirPositionToPointer(reservoirParams, reservoirPosition, reservoirArrayIndex); RTXDI_GI_RESERVOIR_BUFFER[pointer] = packedGIReservoir; } void RTXDI_StoreGIReservoir( const RTXDI_GIReservoir reservoir, - RTXDI_ResamplingRuntimeParameters params, + ReSTIRGI_ReservoirBufferParameters reservoirParams, uint2 reservoirPosition, uint reservoirArrayIndex) { RTXDI_StorePackedGIReservoir( - RTXDI_PackGIReservoir(reservoir, 0), params, reservoirPosition, reservoirArrayIndex); + RTXDI_PackGIReservoir(reservoir, 0), reservoirParams, reservoirPosition, reservoirArrayIndex); } void RTXDI_StoreGIReservoir( const RTXDI_GIReservoir reservoir, const uint miscFlags, - RTXDI_ResamplingRuntimeParameters params, + ReSTIRGI_ReservoirBufferParameters reservoirParams, uint2 reservoirPosition, uint reservoirArrayIndex) { RTXDI_StorePackedGIReservoir( - RTXDI_PackGIReservoir(reservoir, miscFlags), params, reservoirPosition, reservoirArrayIndex); + RTXDI_PackGIReservoir(reservoir, miscFlags), reservoirParams, reservoirPosition, reservoirArrayIndex); } #endif // RTXDI_ENABLE_STORE_RESERVOIR diff --git a/rtxdi-sdk/include/rtxdi/ImportanceSamplingContext.h b/rtxdi-sdk/include/rtxdi/ImportanceSamplingContext.h new file mode 100644 index 0000000..6992c7b --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/ImportanceSamplingContext.h @@ -0,0 +1,80 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#pragma once + +#include + +#include "rtxdi/ReSTIRDI.h" +#include "rtxdi/ReGIR.h" +#include "rtxdi/ReSTIRGI.h" + +namespace rtxdi +{ + +class RISBufferSegmentAllocator; +struct ReSTIRDIStaticParameters; +struct ReGIRStaticParameters; +struct ReSTIRGIStaticParameters; + +struct ImportanceSamplingContext_StaticParameters +{ + // RIS buffer params for light presampling + RISBufferSegmentParameters localLightRISBufferParams = { 1024, 128 }; + RISBufferSegmentParameters environmentLightRISBufferParams = { 1024, 128 }; + + // Shared options for ReSTIRDI and ReSTIRGI + uint32_t NeighborOffsetCount = 8192; + uint32_t renderWidth = 0; + uint32_t renderHeight = 0; + CheckerboardMode CheckerboardSamplingMode = CheckerboardMode::Off; + + // ReGIR params + ReGIRStaticParameters regirStaticParams = {}; +}; + +class ImportanceSamplingContext +{ +public: + ImportanceSamplingContext(const ImportanceSamplingContext_StaticParameters& isParams); + ~ImportanceSamplingContext(); + + ReSTIRDIContext& getReSTIRDIContext(); + const ReSTIRDIContext& getReSTIRDIContext() const; + ReGIRContext& getReGIRContext(); + const ReGIRContext& getReGIRContext() const; + ReSTIRGIContext& getReSTIRGIContext(); + const ReSTIRGIContext& getReSTIRGIContext() const; + + const RISBufferSegmentAllocator& getRISBufferSegmentAllocator() const; + + const RTXDI_LightBufferParameters& getLightBufferParameters() const; + const RTXDI_RISBufferSegmentParameters& getLocalLightRISBufferSegmentParams() const; + const RTXDI_RISBufferSegmentParameters& getEnvironmentLightRISBufferSegmentParams() const; + uint32_t getNeighborOffsetCount() const; + + bool isLocalLightPowerRISEnabled() const; + bool isReGIREnabled() const; + + void setLightBufferParams(const RTXDI_LightBufferParameters& lightBufferParams); + +private: + std::unique_ptr m_risBufferSegmentAllocator; + std::unique_ptr m_restirDIContext; + std::unique_ptr m_regirContext; + std::unique_ptr m_restirGIContext; + + // Common buffer params + RTXDI_LightBufferParameters m_lightBufferParams; + RTXDI_RISBufferSegmentParameters m_localLightRISBufferSegmentParams; + RTXDI_RISBufferSegmentParameters m_environmentLightRISBufferSegmentParams; +}; + +} \ No newline at end of file diff --git a/rtxdi-sdk/include/rtxdi/InitialSamplingFunctions.hlsli b/rtxdi-sdk/include/rtxdi/InitialSamplingFunctions.hlsli new file mode 100644 index 0000000..6871719 --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/InitialSamplingFunctions.hlsli @@ -0,0 +1,665 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef INITIAL_SAMPLING_FUNCTIONS_HLSLI +#define INITIAL_SAMPLING_FUNCTIONS_HLSLI + +#include "rtxdi/ReSTIRDIParameters.h" +#include "rtxdi/DIReservoir.hlsli" +#include "rtxdi/LocalLightSelection.hlsli" +#ifdef RTXDI_RIS_BUFFER_HLSLI +#include "rtxdi/RISBuffer.hlsli" +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED +#include "rtxdi/ReGIRSampling.hlsli" +#endif +#endif // RTXDI_RIS_BUFFER_HLSLI +#include "rtxdi/UniformSampling.hlsli" + +#ifndef RTXDI_TILE_SIZE_IN_PIXELS +#define RTXDI_TILE_SIZE_IN_PIXELS 16 +#endif + +struct RTXDI_SampleParameters +{ + uint numLocalLightSamples; + uint numInfiniteLightSamples; + uint numEnvironmentMapSamples; + uint numBrdfSamples; + + uint numMisSamples; + float localLightMisWeight; + float environmentMapMisWeight; + float brdfMisWeight; + float brdfCutoff; + float brdfRayMinT; +}; + +// +// MIS functions +// + +// Sample parameters struct +// Defined so that so these can be compile time constants as defined by the user +// brdfCutoff Value in range [0,1] to determine how much to shorten BRDF rays. 0 to disable shortening +RTXDI_SampleParameters RTXDI_InitSampleParameters( + uint numLocalLightSamples, + uint numInfiniteLightSamples, + uint numEnvironmentMapSamples, + uint numBrdfSamples, + float brdfCutoff RTXDI_DEFAULT(0.0), + float brdfRayMinT RTXDI_DEFAULT(0.001f)) +{ + RTXDI_SampleParameters result; + result.numLocalLightSamples = numLocalLightSamples; + result.numInfiniteLightSamples = numInfiniteLightSamples; + result.numEnvironmentMapSamples = numEnvironmentMapSamples; + result.numBrdfSamples = numBrdfSamples; + + result.numMisSamples = numLocalLightSamples + numEnvironmentMapSamples + numBrdfSamples; + result.localLightMisWeight = float(numLocalLightSamples) / result.numMisSamples; + result.environmentMapMisWeight = float(numEnvironmentMapSamples) / result.numMisSamples; + result.brdfMisWeight = float(numBrdfSamples) / result.numMisSamples; + result.brdfCutoff = brdfCutoff; + result.brdfRayMinT = brdfRayMinT; + + return result; +} + +// Heuristic to determine a max visibility ray length from a PDF wrt. solid angle. +float RTXDI_BrdfMaxDistanceFromPdf(float brdfCutoff, float pdf) +{ + const float kRayTMax = 3.402823466e+38F; // FLT_MAX + return brdfCutoff > 0.f ? sqrt((1.f / brdfCutoff - 1.f) * pdf) : kRayTMax; +} + +// Computes the multi importance sampling pdf for brdf and light sample. +// For light and BRDF PDFs wrt solid angle, blend between the two. +// lightSelectionPdf is a dimensionless selection pdf +float RTXDI_LightBrdfMisWeight(RAB_Surface surface, RAB_LightSample lightSample, + float lightSelectionPdf, float lightMisWeight, bool isEnvironmentMap, + RTXDI_SampleParameters sampleParams) +{ + float lightSolidAnglePdf = RAB_LightSampleSolidAnglePdf(lightSample); + if (sampleParams.brdfMisWeight == 0 || RAB_IsAnalyticLightSample(lightSample) || + lightSolidAnglePdf <= 0 || isinf(lightSolidAnglePdf) || isnan(lightSolidAnglePdf)) + { + // BRDF samples disabled or we can't trace BRDF rays MIS with analytical lights + return lightMisWeight * lightSelectionPdf; + } + + float3 lightDir; + float lightDistance; + RAB_GetLightDirDistance(surface, lightSample, lightDir, lightDistance); + + // Compensate for ray shortening due to brdf cutoff, does not apply to environment map sampling + float brdfPdf = RAB_GetSurfaceBrdfPdf(surface, lightDir); + float maxDistance = RTXDI_BrdfMaxDistanceFromPdf(sampleParams.brdfCutoff, brdfPdf); + if (!isEnvironmentMap && lightDistance > maxDistance) + brdfPdf = 0.f; + + // Convert light selection pdf (unitless) to a solid angle measurement + float sourcePdfWrtSolidAngle = lightSelectionPdf * lightSolidAnglePdf; + + // MIS blending against solid angle pdfs. + float blendedPdfWrtSolidangle = lightMisWeight * sourcePdfWrtSolidAngle + sampleParams.brdfMisWeight * brdfPdf; + + // Convert back, RTXDI divides shading again by this term later + return blendedPdfWrtSolidangle / lightSolidAnglePdf; +} + +// +// Local light UV selection and reservoir streaming +// + +float2 RTXDI_RandomlySelectLocalLightUV(inout RAB_RandomSamplerState rng) +{ + float2 uv; + uv.x = RAB_GetNextRandom(rng); + uv.y = RAB_GetNextRandom(rng); + return uv; +} + +bool RTXDI_StreamLocalLightAtUVIntoReservoir( + inout RAB_RandomSamplerState rng, + RTXDI_SampleParameters sampleParams, + RAB_Surface surface, + uint lightIndex, + float2 uv, + float invSourcePdf, + RAB_LightInfo lightInfo, + inout RTXDI_DIReservoir state, + inout RAB_LightSample o_selectedSample) +{ + RAB_LightSample candidateSample = RAB_SamplePolymorphicLight(lightInfo, surface, uv); + float blendedSourcePdf = RTXDI_LightBrdfMisWeight(surface, candidateSample, 1.0 / invSourcePdf, + sampleParams.localLightMisWeight, false, sampleParams); + float targetPdf = RAB_GetLightSampleTargetPdfForSurface(candidateSample, surface); + float risRnd = RAB_GetNextRandom(rng); + + if (blendedSourcePdf == 0) + { + return false; + } + bool selected = RTXDI_StreamSample(state, lightIndex, uv, risRnd, targetPdf, 1.0 / blendedSourcePdf); + + if (selected) { + o_selectedSample = candidateSample; + } + return true; +} + +// +// ReGIR-based RIS for local lights. See PresampleReGIR.hlsl +// + +#if RTXDI_ENABLE_PRESAMPLING +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED + +int RTXDI_CalculateReGIRCellIndex( + inout RAB_RandomSamplerState coherentRng, + ReGIR_Parameters regirParams, + RAB_Surface surface) +{ + int cellIndex = -1; + float3 cellJitter = float3( + RAB_GetNextRandom(coherentRng), + RAB_GetNextRandom(coherentRng), + RAB_GetNextRandom(coherentRng)); + cellJitter -= 0.5; + + float3 samplingPos = RAB_GetSurfaceWorldPos(surface); + float jitterScale = RTXDI_ReGIR_GetJitterScale(regirParams, samplingPos); + samplingPos += cellJitter * jitterScale; + + cellIndex = RTXDI_ReGIR_WorldPosToCellIndex(regirParams, samplingPos); + return cellIndex; +} + +RTXDI_RISTileInfo RTXDI_SelectLocalLightReGIRRISTile( + int cellIndex, + ReGIR_CommonParameters regirCommon) +{ + RTXDI_RISTileInfo tileInfo; + uint cellBase = uint(cellIndex)*regirCommon.lightsPerCell; + tileInfo.risTileOffset = cellBase + regirCommon.risBufferOffset; + tileInfo.risTileSize = regirCommon.lightsPerCell; + return tileInfo; +} + +#endif // RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED +#endif // RTXDI_ENABLE_PRESAMPLING + +#if RTXDI_ENABLE_PRESAMPLING +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED +RTXDI_LocalLightSelectionContext RTXDI_InitializeLocalLightSelectionContextReGIRRIS( + inout RAB_RandomSamplerState coherentRng, + RTXDI_LightBufferRegion localLightBufferRegion, + RTXDI_RISBufferSegmentParameters localLightRISBufferSegmentParams, + ReGIR_Parameters regirParams, + RAB_Surface surface) +{ + RTXDI_LocalLightSelectionContext ctx; + int cellIndex = RTXDI_CalculateReGIRCellIndex(coherentRng, regirParams, surface); + if (cellIndex >= 0) + { + ctx = RTXDI_InitializeLocalLightSelectionContextRIS(RTXDI_SelectLocalLightReGIRRISTile(cellIndex, regirParams.commonParams)); + } + else if (regirParams.commonParams.localLightSamplingFallbackMode == ReSTIRDI_LocalLightSamplingMode_POWER_RIS) + { + ctx = RTXDI_InitializeLocalLightSelectionContextRIS(coherentRng, localLightRISBufferSegmentParams); + } + else + { + ctx = RTXDI_InitializeLocalLightSelectionContextUniform(localLightBufferRegion); + } + return ctx; +} +#endif // RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED +#endif // RTXDI_ENABLE_PRESAMPLING + +RTXDI_LocalLightSelectionContext RTXDI_InitializeLocalLightSelectionContext( + inout RAB_RandomSamplerState coherentRng, + ReSTIRDI_LocalLightSamplingMode localLightSamplingMode, + RTXDI_LightBufferRegion localLightBufferRegion +#if RTXDI_ENABLE_PRESAMPLING + ,RTXDI_RISBufferSegmentParameters localLightRISBufferSegmentParams +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED + ,ReGIR_Parameters regirParams + ,RAB_Surface surface +#endif +#endif + ) +{ + RTXDI_LocalLightSelectionContext ctx; +#if RTXDI_ENABLE_PRESAMPLING +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED + if (localLightSamplingMode == ReSTIRDI_LocalLightSamplingMode_REGIR_RIS) + { + ctx = RTXDI_InitializeLocalLightSelectionContextReGIRRIS(coherentRng, localLightBufferRegion, localLightRISBufferSegmentParams, regirParams, surface); + } + else +#endif // RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED + if (localLightSamplingMode == ReSTIRDI_LocalLightSamplingMode_POWER_RIS) + { + ctx = RTXDI_InitializeLocalLightSelectionContextRIS(coherentRng, localLightRISBufferSegmentParams); + } + else +#endif // RTXDI_ENABLE_PRESAMPLING + { + ctx = RTXDI_InitializeLocalLightSelectionContextUniform(localLightBufferRegion); + } + return ctx; +} + +RTXDI_DIReservoir RTXDI_SampleLocalLightsInternal( + inout RAB_RandomSamplerState rng, + inout RAB_RandomSamplerState coherentRng, + RAB_Surface surface, + RTXDI_SampleParameters sampleParams, + ReSTIRDI_LocalLightSamplingMode localLightSamplingMode, + RTXDI_LightBufferRegion localLightBufferRegion, +#if RTXDI_ENABLE_PRESAMPLING + RTXDI_RISBufferSegmentParameters localLightRISBufferSegmentParams, +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED + ReGIR_Parameters regirParams, +#endif +#endif + out RAB_LightSample o_selectedSample) +{ + RTXDI_DIReservoir state = RTXDI_EmptyDIReservoir(); + + RTXDI_LocalLightSelectionContext lightSelectionContext = RTXDI_InitializeLocalLightSelectionContext(coherentRng, localLightSamplingMode, localLightBufferRegion +#if RTXDI_ENABLE_PRESAMPLING + ,localLightRISBufferSegmentParams +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED + ,regirParams + ,surface +#endif +#endif + ); + + for (uint i = 0; i < sampleParams.numLocalLightSamples; i++) + { + uint lightIndex; + RAB_LightInfo lightInfo; + float invSourcePdf; + + RTXDI_SelectNextLocalLight(lightSelectionContext, rng, lightInfo, lightIndex, invSourcePdf); + float2 uv = RTXDI_RandomlySelectLocalLightUV(rng); + bool zeroPdf = RTXDI_StreamLocalLightAtUVIntoReservoir(rng, sampleParams, surface, lightIndex, uv, invSourcePdf, lightInfo, state, o_selectedSample); + + if (zeroPdf) + continue; + } + + RTXDI_FinalizeResampling(state, 1.0, sampleParams.numMisSamples); + state.M = 1; + + return state; +} + +// +// Local light sampling +// + +RTXDI_DIReservoir RTXDI_SampleLocalLights( + inout RAB_RandomSamplerState rng, + inout RAB_RandomSamplerState coherentRng, + RAB_Surface surface, + RTXDI_SampleParameters sampleParams, + ReSTIRDI_LocalLightSamplingMode localLightSamplingMode, + RTXDI_LightBufferRegion localLightBufferRegion, +#if RTXDI_ENABLE_PRESAMPLING + RTXDI_RISBufferSegmentParameters localLightRISBufferSegmentParams, +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED + ReGIR_Parameters regirParams, +#endif +#endif + out RAB_LightSample o_selectedSample) +{ + o_selectedSample = RAB_EmptyLightSample(); + + if (localLightBufferRegion.numLights == 0) + return RTXDI_EmptyDIReservoir(); + + if (sampleParams.numLocalLightSamples == 0) + return RTXDI_EmptyDIReservoir(); + + return RTXDI_SampleLocalLightsInternal(rng, coherentRng, surface, sampleParams, localLightSamplingMode, localLightBufferRegion, +#if RTXDI_ENABLE_PRESAMPLING + localLightRISBufferSegmentParams, +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED + regirParams, +#endif +#endif + o_selectedSample); +} + +// +// Uniform sampling for infinite lights +// + +float2 RTXDI_RandomlySelectInfiniteLightUV(inout RAB_RandomSamplerState rng) +{ + float2 uv; + uv.x = RAB_GetNextRandom(rng); + uv.y = RAB_GetNextRandom(rng); + return uv; +} + +void RTXDI_StreamInfiniteLightAtUVIntoReservoir( + inout RAB_RandomSamplerState rng, + RAB_LightInfo lightInfo, + RAB_Surface surface, + uint lightIndex, + float2 uv, + float invSourcePdf, + inout RTXDI_DIReservoir state, + inout RAB_LightSample o_selectedSample) +{ + RAB_LightSample candidateSample = RAB_SamplePolymorphicLight(lightInfo, surface, uv); + float targetPdf = RAB_GetLightSampleTargetPdfForSurface(candidateSample, surface); + float risRnd = RAB_GetNextRandom(rng); + bool selected = RTXDI_StreamSample(state, lightIndex, uv, risRnd, targetPdf, invSourcePdf); + + if (selected) + { + o_selectedSample = candidateSample; + } +} + +RTXDI_DIReservoir RTXDI_SampleInfiniteLights( + inout RAB_RandomSamplerState rng, + RAB_Surface surface, + uint numSamples, + RTXDI_LightBufferRegion infiniteLightBufferRegion, + inout RAB_LightSample o_selectedSample) +{ + RTXDI_DIReservoir state = RTXDI_EmptyDIReservoir(); + o_selectedSample = RAB_EmptyLightSample(); + + if (infiniteLightBufferRegion.numLights == 0) + return state; + + if (numSamples == 0) + return state; + + for (uint i = 0; i < numSamples; i++) + { + float invSourcePdf; + uint lightIndex; + RAB_LightInfo lightInfo; + + RTXDI_RandomlySelectLightUniformly(rng, infiniteLightBufferRegion, lightInfo, lightIndex, invSourcePdf); + float2 uv = RTXDI_RandomlySelectInfiniteLightUV(rng); + RTXDI_StreamInfiniteLightAtUVIntoReservoir(rng, lightInfo, surface, lightIndex, uv, invSourcePdf, state, o_selectedSample); + } + + RTXDI_FinalizeResampling(state, 1.0, state.M); + state.M = 1; + + return state; +} + +#if RTXDI_ENABLE_PRESAMPLING + +// +// Power based RIS for the environment map. See PresampleEnvironmentMap.hlsl +// + +void RTXDI_UnpackEnvironmentLightDataFromRISData( + uint2 tileData, + out float2 uv, + out float invSourcePdf +) +{ + uint packedUv = tileData.x; + invSourcePdf = asfloat(tileData.y); + uv = float2(packedUv & 0xffff, packedUv >> 16) / float(0xffff); +} + +void RTXDI_RandomlySelectEnvironmentLightUVFromRISTile( + inout RAB_RandomSamplerState rng, + RTXDI_RISTileInfo risTileInfo, + out float2 uv, + out float invSourcePdf +) +{ + uint2 tileData; + uint risBufferPtr; + RTXDI_RandomlySelectLightDataFromRISTile(rng, risTileInfo, tileData, risBufferPtr); + RTXDI_UnpackEnvironmentLightDataFromRISData(tileData, uv, invSourcePdf); +} + +void RTXDI_StreamEnvironmentLightAtUVIntoReservoir( + inout RAB_RandomSamplerState rng, + RTXDI_SampleParameters sampleParams, + RAB_Surface surface, + RAB_LightInfo lightInfo, + uint environmentLightIndex, + float2 uv, + float invSourcePdf, + inout RTXDI_DIReservoir state, + inout RAB_LightSample o_selectedSample) +{ + RAB_LightSample candidateSample = RAB_SamplePolymorphicLight(lightInfo, surface, uv); + float blendedSourcePdf = RTXDI_LightBrdfMisWeight(surface, candidateSample, 1.0 / invSourcePdf, + sampleParams.environmentMapMisWeight, true, sampleParams); + float targetPdf = RAB_GetLightSampleTargetPdfForSurface(candidateSample, surface); + float risRnd = RAB_GetNextRandom(rng); + + bool selected = RTXDI_StreamSample(state, environmentLightIndex, uv, risRnd, targetPdf, 1.0 / blendedSourcePdf); + + if (selected) { + o_selectedSample = candidateSample; + } +} + +RTXDI_DIReservoir RTXDI_SampleEnvironmentMap( + inout RAB_RandomSamplerState rng, + inout RAB_RandomSamplerState coherentRng, + RAB_Surface surface, + RTXDI_SampleParameters sampleParams, + RTXDI_EnvironmentLightBufferParameters params, + RTXDI_RISBufferSegmentParameters risBufferSegmentParams, + out RAB_LightSample o_selectedSample) +{ + RTXDI_DIReservoir state = RTXDI_EmptyDIReservoir(); + o_selectedSample = RAB_EmptyLightSample(); + + if (params.lightPresent == 0) + return state; + + if (sampleParams.numEnvironmentMapSamples == 0) + return state; + + RTXDI_RISTileInfo risTileInfo = RTXDI_RandomlySelectRISTile(coherentRng, risBufferSegmentParams); + + RAB_LightInfo lightInfo = RAB_LoadLightInfo(params.lightIndex, false); + + for (uint i = 0; i < sampleParams.numEnvironmentMapSamples; i++) + { + float2 uv; + float invSourcePdf; + RTXDI_RandomlySelectEnvironmentLightUVFromRISTile(rng, risTileInfo, uv, invSourcePdf); + RTXDI_StreamEnvironmentLightAtUVIntoReservoir(rng, sampleParams, surface, lightInfo, params.lightIndex, uv, invSourcePdf, state, o_selectedSample); + } + + RTXDI_FinalizeResampling(state, 1.0, sampleParams.numMisSamples); + state.M = 1; + + return state; +} + +#endif // RTXDI_ENABLE_PRESAMPLING + +// +// BRDF sampling: Samples from the BRDF defined by the given surface +// + +RTXDI_DIReservoir RTXDI_SampleBrdf( + inout RAB_RandomSamplerState rng, + RAB_Surface surface, + RTXDI_SampleParameters sampleParams, + RTXDI_LightBufferParameters lightBufferParams, + out RAB_LightSample o_selectedSample) +{ + RTXDI_DIReservoir state = RTXDI_EmptyDIReservoir(); + + for (uint i = 0; i < sampleParams.numBrdfSamples; ++i) + { + float lightSourcePdf = 0; + float3 sampleDir; + uint lightIndex = RTXDI_InvalidLightIndex; + float2 randXY = float2(0, 0); + RAB_LightSample candidateSample = RAB_EmptyLightSample(); + + if (RAB_GetSurfaceBrdfSample(surface, rng, sampleDir)) + { + float brdfPdf = RAB_GetSurfaceBrdfPdf(surface, sampleDir); + float maxDistance = RTXDI_BrdfMaxDistanceFromPdf(sampleParams.brdfCutoff, brdfPdf); + + bool hitAnything = RAB_TraceRayForLocalLight(RAB_GetSurfaceWorldPos(surface), sampleDir, + sampleParams.brdfRayMinT, maxDistance, lightIndex, randXY); + + if (lightIndex != RTXDI_InvalidLightIndex) + { + RAB_LightInfo lightInfo = RAB_LoadLightInfo(lightIndex, false); + candidateSample = RAB_SamplePolymorphicLight(lightInfo, surface, randXY); + + if (sampleParams.brdfCutoff > 0.f) + { + // If Mis cutoff is used, we need to evaluate the sample and make sure it actually could have been + // generated by the area sampling technique. This is due to numerical precision. + float3 lightDir; + float lightDistance; + RAB_GetLightDirDistance(surface, candidateSample, lightDir, lightDistance); + + float brdfPdf = RAB_GetSurfaceBrdfPdf(surface, lightDir); + float maxDistance = RTXDI_BrdfMaxDistanceFromPdf(sampleParams.brdfCutoff, brdfPdf); + if (lightDistance > maxDistance) + lightIndex = RTXDI_InvalidLightIndex; + } + + if (lightIndex != RTXDI_InvalidLightIndex) + { + lightSourcePdf = RAB_EvaluateLocalLightSourcePdf(lightIndex); + } + } + else if (!hitAnything && (lightBufferParams.environmentLightParams.lightPresent != 0)) + { + // sample environment light + lightIndex = lightBufferParams.environmentLightParams.lightIndex; + RAB_LightInfo lightInfo = RAB_LoadLightInfo(lightIndex, false); + randXY = RAB_GetEnvironmentMapRandXYFromDir(sampleDir); + candidateSample = RAB_SamplePolymorphicLight(lightInfo, surface, randXY); + lightSourcePdf = RAB_EvaluateEnvironmentMapSamplingPdf(sampleDir); + } + } + + if (lightSourcePdf == 0) + { + // Did not hit a visible light + continue; + } + + bool isEnvMapSample = lightIndex == lightBufferParams.environmentLightParams.lightIndex; + float targetPdf = RAB_GetLightSampleTargetPdfForSurface(candidateSample, surface); + float blendedSourcePdf = RTXDI_LightBrdfMisWeight(surface, candidateSample, lightSourcePdf, + isEnvMapSample ? sampleParams.environmentMapMisWeight : sampleParams.localLightMisWeight, + isEnvMapSample, + sampleParams); + float risRnd = RAB_GetNextRandom(rng); + + bool selected = RTXDI_StreamSample(state, lightIndex, randXY, risRnd, targetPdf, 1.0f / blendedSourcePdf); + if (selected) { + o_selectedSample = candidateSample; + } + } + + RTXDI_FinalizeResampling(state, 1.0, sampleParams.numMisSamples); + state.M = 1; + + return state; +} + +// Samples the local, infinite, and environment lights for a given surface +RTXDI_DIReservoir RTXDI_SampleLightsForSurface( + inout RAB_RandomSamplerState rng, + inout RAB_RandomSamplerState coherentRng, + RAB_Surface surface, + RTXDI_SampleParameters sampleParams, + RTXDI_LightBufferParameters lightBufferParams, + ReSTIRDI_LocalLightSamplingMode localLightSamplingMode, +#if RTXDI_ENABLE_PRESAMPLING + RTXDI_RISBufferSegmentParameters localLightRISBufferSegmentParams, + RTXDI_RISBufferSegmentParameters environmentLightRISBufferSegmentParams, +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED + ReGIR_Parameters regirParams, +#endif +#endif + out RAB_LightSample o_lightSample) +{ + o_lightSample = RAB_EmptyLightSample(); + + RTXDI_DIReservoir localReservoir; + RAB_LightSample localSample = RAB_EmptyLightSample(); + + localReservoir = RTXDI_SampleLocalLights(rng, coherentRng, surface, + sampleParams, localLightSamplingMode, lightBufferParams.localLightBufferRegion, +#if RTXDI_ENABLE_PRESAMPLING + localLightRISBufferSegmentParams, +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED + regirParams, +#endif +#endif +localSample); + + RAB_LightSample infiniteSample = RAB_EmptyLightSample(); + RTXDI_DIReservoir infiniteReservoir = RTXDI_SampleInfiniteLights(rng, surface, + sampleParams.numInfiniteLightSamples, lightBufferParams.infiniteLightBufferRegion, infiniteSample); + +#if RTXDI_ENABLE_PRESAMPLING + RAB_LightSample environmentSample = RAB_EmptyLightSample(); + RTXDI_DIReservoir environmentReservoir = RTXDI_SampleEnvironmentMap(rng, coherentRng, surface, + sampleParams, lightBufferParams.environmentLightParams, environmentLightRISBufferSegmentParams, environmentSample); +#endif // RTXDI_ENABLE_PRESAMPLING + + RAB_LightSample brdfSample = RAB_EmptyLightSample(); + RTXDI_DIReservoir brdfReservoir = RTXDI_SampleBrdf(rng, surface, sampleParams, lightBufferParams, brdfSample); + + RTXDI_DIReservoir state = RTXDI_EmptyDIReservoir(); + RTXDI_CombineDIReservoirs(state, localReservoir, 0.5, localReservoir.targetPdf); + bool selectInfinite = RTXDI_CombineDIReservoirs(state, infiniteReservoir, RAB_GetNextRandom(rng), infiniteReservoir.targetPdf); +#if RTXDI_ENABLE_PRESAMPLING + bool selectEnvironment = RTXDI_CombineDIReservoirs(state, environmentReservoir, RAB_GetNextRandom(rng), environmentReservoir.targetPdf); +#endif // RTXDI_ENABLE_PRESAMPLING + bool selectBrdf = RTXDI_CombineDIReservoirs(state, brdfReservoir, RAB_GetNextRandom(rng), brdfReservoir.targetPdf); + + RTXDI_FinalizeResampling(state, 1.0, 1.0); + state.M = 1; + + if (selectBrdf) + o_lightSample = brdfSample; + else +#if RTXDI_ENABLE_PRESAMPLING + if (selectEnvironment) + o_lightSample = environmentSample; + else +#endif // RTXDI_ENABLE_PRESAMPLING + if (selectInfinite) + o_lightSample = infiniteSample; + else + o_lightSample = localSample; + + return state; +} + +#endif \ No newline at end of file diff --git a/rtxdi-sdk/include/rtxdi/LocalLightSelection.hlsli b/rtxdi-sdk/include/rtxdi/LocalLightSelection.hlsli new file mode 100644 index 0000000..7656c57 --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/LocalLightSelection.hlsli @@ -0,0 +1,117 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef RTXDI_LOCAL_LIGHT_SELECTION_CONTEXT_HLSLI +#define RTXDI_LOCAL_LIGHT_SELECTION_CONTEXT_HLSLI + +#if RTXDI_ENABLE_PRESAMPLING +#include "rtxdi/RISBuffer.hlsli" +#endif +#include "rtxdi/UniformSampling.hlsli" + +#define RTXDI_LocalLightContextSamplingMode uint +#define RTXDI_LocalLightContextSamplingMode_UNIFORM 0 +#if RTXDI_ENABLE_PRESAMPLING +#define RTXDI_LocalLightContextSamplingMode_RIS 1 +#endif + +struct RTXDI_LocalLightSelectionContext +{ + RTXDI_LocalLightContextSamplingMode mode; + +#if RTXDI_ENABLE_PRESAMPLING + RTXDI_RISTileInfo risTileInfo; +#endif // RTXDI_ENABLE_PRESAMPLING + RTXDI_LightBufferRegion lightBufferRegion; +}; + +RTXDI_LocalLightSelectionContext RTXDI_InitializeLocalLightSelectionContextUniform(RTXDI_LightBufferRegion lightBufferRegion) +{ + RTXDI_LocalLightSelectionContext ctx; + ctx.mode = RTXDI_LocalLightContextSamplingMode_UNIFORM; + ctx.lightBufferRegion = lightBufferRegion; + return ctx; +} + +#if RTXDI_ENABLE_PRESAMPLING +RTXDI_LocalLightSelectionContext RTXDI_InitializeLocalLightSelectionContextRIS(RTXDI_RISTileInfo risTileInfo) +{ + RTXDI_LocalLightSelectionContext ctx; + ctx.mode = RTXDI_LocalLightContextSamplingMode_RIS; + ctx.risTileInfo = risTileInfo; + return ctx; +} + +RTXDI_LocalLightSelectionContext RTXDI_InitializeLocalLightSelectionContextRIS( + inout RAB_RandomSamplerState coherentRng, + RTXDI_RISBufferSegmentParameters risBufferSegmentParams) +{ + RTXDI_LocalLightSelectionContext ctx; + ctx.mode = RTXDI_LocalLightContextSamplingMode_RIS; + ctx.risTileInfo = RTXDI_RandomlySelectRISTile(coherentRng, risBufferSegmentParams); + return ctx; +} + +void RTXDI_UnpackLocalLightFromRISLightData( + uint2 tileData, + uint risBufferPtr, + out RAB_LightInfo lightInfo, + out uint lightIndex, + out float invSourcePdf) +{ + lightIndex = tileData.x & RTXDI_LIGHT_INDEX_MASK; + invSourcePdf = asfloat(tileData.y); + + if ((tileData.x & RTXDI_LIGHT_COMPACT_BIT) != 0) + { + lightInfo = RAB_LoadCompactLightInfo(risBufferPtr); + } + else + { + lightInfo = RAB_LoadLightInfo(lightIndex, false); + } +} + +void RTXDI_RandomlySelectLocalLightFromRISTile( + inout RAB_RandomSamplerState rng, + const RTXDI_RISTileInfo risTileInfo, + out RAB_LightInfo lightInfo, + out uint lightIndex, + out float invSourcePdf) +{ + uint2 risTileData; + uint risBufferPtr; + RTXDI_RandomlySelectLightDataFromRISTile(rng, risTileInfo, risTileData, risBufferPtr); + RTXDI_UnpackLocalLightFromRISLightData(risTileData, risBufferPtr, lightInfo, lightIndex, invSourcePdf); +} +#endif + +void RTXDI_SelectNextLocalLight( + RTXDI_LocalLightSelectionContext ctx, + inout RAB_RandomSamplerState rng, + out RAB_LightInfo lightInfo, + out uint lightIndex, + out float invSourcePdf) +{ + switch (ctx.mode) + { +#if RTXDI_ENABLE_PRESAMPLING + case RTXDI_LocalLightContextSamplingMode_RIS: + RTXDI_RandomlySelectLocalLightFromRISTile(rng, ctx.risTileInfo, lightInfo, lightIndex, invSourcePdf); + break; +#endif // RTXDI_ENABLE_PRESAMPLING + default: + case RTXDI_LocalLightContextSamplingMode_UNIFORM: + RTXDI_RandomlySelectLightUniformly(rng, ctx.lightBufferRegion, lightInfo, lightIndex, invSourcePdf); + break; + } +} + +#endif // RTXDI_LOCAL_LIGHT_SELECTION_CONTEXT_HLSLI \ No newline at end of file diff --git a/rtxdi-sdk/include/rtxdi/PresamplingFunctions.hlsli b/rtxdi-sdk/include/rtxdi/PresamplingFunctions.hlsli new file mode 100644 index 0000000..9665771 --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/PresamplingFunctions.hlsli @@ -0,0 +1,253 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef PRESAMPLING_FUNCTIONS_HLSLI +#define PRESAMPLING_FUNCTIONS_HLSLI + +#include "rtxdi/RtxdiParameters.h" +#include "rtxdi/RtxdiMath.hlsli" +#include "rtxdi/RtxdiHelpers.hlsli" +#include "rtxdi/LocalLightSelection.hlsli" +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED +#include "rtxdi/ReGIRSampling.hlsli" +#endif + +#if RTXDI_ENABLE_PRESAMPLING && !defined(RTXDI_RIS_BUFFER) +#error "RTXDI_RIS_BUFFER must be defined to point to a RWBuffer type resource" +#endif + +#if !RTXDI_ENABLE_PRESAMPLING && (RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED) +#error "ReGIR requires presampling to be enabled" +#endif + +void RTXDI_SamplePdfMipmap( + inout RAB_RandomSamplerState rng, + RTXDI_TEX2D pdfTexture, // full mip chain starting from unnormalized sampling pdf in mip 0 + uint2 pdfTextureSize, // dimensions of pdfTexture at mip 0; must be 16k or less + out uint2 position, + out float pdf) +{ + int lastMipLevel = max(0, int(floor(log2(max(pdfTextureSize.x, pdfTextureSize.y)))) - 1); + + position = uint2(0, 0); + pdf = 1.0; + for (int mipLevel = lastMipLevel; mipLevel >= 0; mipLevel--) + { + position *= 2; + + float4 samples; + samples.x = max(0, RTXDI_TEX2D_LOAD(pdfTexture, int2(position.x + 0, position.y + 0), mipLevel).x); + samples.y = max(0, RTXDI_TEX2D_LOAD(pdfTexture, int2(position.x + 0, position.y + 1), mipLevel).x); + samples.z = max(0, RTXDI_TEX2D_LOAD(pdfTexture, int2(position.x + 1, position.y + 0), mipLevel).x); + samples.w = max(0, RTXDI_TEX2D_LOAD(pdfTexture, int2(position.x + 1, position.y + 1), mipLevel).x); + + float weightSum = samples.x + samples.y + samples.z + samples.w; + if (weightSum <= 0) + { + pdf = 0; + return; + } + + samples /= weightSum; + + float rnd = RAB_GetNextRandom(rng); + + int2 selectedOffset; + + if (rnd < samples.x) + { + pdf *= samples.x; + } + else + { + rnd -= samples.x; + + if (rnd < samples.y) + { + position += uint2(0, 1); + pdf *= samples.y; + } + else + { + rnd -= samples.y; + + if (rnd < samples.z) + { + position += uint2(1, 0); + pdf *= samples.z; + } + else + { + position += uint2(1, 1); + pdf *= samples.w; + } + } + } + } +} + +void RTXDI_PresampleLocalLights( + inout RAB_RandomSamplerState rng, + RTXDI_TEX2D pdfTexture, + uint2 pdfTextureSize, + uint tileIndex, + uint sampleInTile, + RTXDI_LightBufferRegion localLightBufferRegion, + RTXDI_RISBufferSegmentParameters localLightsRISBufferSegmentParams) +{ + uint2 texelPosition; + float pdf; + RTXDI_SamplePdfMipmap(rng, pdfTexture, pdfTextureSize, texelPosition, pdf); + + uint lightIndex = RTXDI_ZCurveToLinearIndex(texelPosition); + + uint risBufferPtr = sampleInTile + tileIndex * localLightsRISBufferSegmentParams.tileSize; + + bool compact = false; + float invSourcePdf = 0; + + if (pdf > 0) + { + invSourcePdf = 1.0 / pdf; + + RAB_LightInfo lightInfo = RAB_LoadLightInfo(lightIndex + localLightBufferRegion.firstLightIndex, false); + compact = RAB_StoreCompactLightInfo(risBufferPtr, lightInfo); + } + + lightIndex += localLightBufferRegion.firstLightIndex; + + if (compact) { + lightIndex |= RTXDI_LIGHT_COMPACT_BIT; + } + + // Store the index of the light that we found and its inverse pdf. + // Or zero and zero if we somehow found nothing. + RTXDI_RIS_BUFFER[risBufferPtr] = uint2(lightIndex, asuint(invSourcePdf)); +} + +void RTXDI_PresampleEnvironmentMap( + inout RAB_RandomSamplerState rng, + RTXDI_TEX2D pdfTexture, + uint2 pdfTextureSize, + uint tileIndex, + uint sampleInTile, + RTXDI_RISBufferSegmentParameters risBufferSegmentParams) +{ + uint2 texelPosition; + float pdf; + RTXDI_SamplePdfMipmap(rng, pdfTexture, pdfTextureSize, texelPosition, pdf); + + // Uniform sampling inside the pixels + float2 fPos = float2(texelPosition); + fPos.x += RAB_GetNextRandom(rng); + fPos.y += RAB_GetNextRandom(rng); + + // Convert texel position to UV and pack it + float2 uv = fPos / float2(pdfTextureSize); + uint packedUv = uint(saturate(uv.x) * 0xffff) | (uint(saturate(uv.y) * 0xffff) << 16); + + // Compute the inverse PDF if we found something + float invSourcePdf = (pdf > 0) ? (1.0 / pdf) : 0; + + // Store the result + uint risBufferPtr = risBufferSegmentParams.bufferOffset + sampleInTile + tileIndex * risBufferSegmentParams.tileSize; + RTXDI_RIS_BUFFER[risBufferPtr] = uint2(packedUv, asuint(invSourcePdf)); +} + +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED + +// ReGIR grid build pass. +// Each thread populates one light slot in a grid cell. +void RTXDI_PresampleLocalLightsForReGIR( + inout RAB_RandomSamplerState rng, + inout RAB_RandomSamplerState coherentRng, + uint lightSlot, + RTXDI_LightBufferRegion localLightBufferRegion, + RTXDI_RISBufferSegmentParameters localLightRISBufferSegmentParams, + ReGIR_Parameters regirParams) +{ + uint risBufferPtr = regirParams.commonParams.risBufferOffset + lightSlot; + + if (regirParams.commonParams.numRegirBuildSamples == 0) + { + RTXDI_RIS_BUFFER[risBufferPtr] = uint2(0, 0); + return; + } + + uint lightInCell = lightSlot % regirParams.commonParams.lightsPerCell; + + uint cellIndex = lightSlot / regirParams.commonParams.lightsPerCell; + + float3 cellCenter; + float cellRadius; + if (!RTXDI_ReGIR_CellIndexToWorldPos(regirParams, int(cellIndex), cellCenter, cellRadius)) + { + RTXDI_RIS_BUFFER[risBufferPtr] = uint2(0, 0); + return; + } + + cellRadius *= (regirParams.commonParams.samplingJitter + 1.0); + + RAB_LightInfo selectedLightInfo = RAB_EmptyLightInfo(); + uint selectedLight = 0; + float selectedTargetPdf = 0; + float weightSum = 0; + + float invNumSamples = 1.0 / float(regirParams.commonParams.numRegirBuildSamples); + + RTXDI_LocalLightSelectionContext ctx; + if (regirParams.commonParams.localLightPresamplingMode == REGIR_LOCAL_LIGHT_PRESAMPLING_MODE_POWER_RIS) + ctx = RTXDI_InitializeLocalLightSelectionContextRIS(coherentRng, localLightRISBufferSegmentParams); + else + ctx = RTXDI_InitializeLocalLightSelectionContextUniform(localLightBufferRegion); + + for (uint i = 0; i < regirParams.commonParams.numRegirBuildSamples; i++) + { + uint rndLight; + RAB_LightInfo lightInfo = RAB_EmptyLightInfo(); + float invSourcePdf; + float rand = RAB_GetNextRandom(rng); + bool lightLoaded = false; + + RTXDI_SelectNextLocalLight(ctx, rng, lightInfo, rndLight, invSourcePdf); + invSourcePdf *= invNumSamples; + + float targetPdf = RAB_GetLightTargetPdfForVolume(lightInfo, cellCenter, cellRadius); + float risRnd = RAB_GetNextRandom(rng); + + float risWeight = targetPdf * invSourcePdf; + weightSum += risWeight; + + if (risRnd * weightSum < risWeight) + { + selectedLightInfo = lightInfo; + selectedLight = rndLight; + selectedTargetPdf = targetPdf; + } + } + + float weight = (selectedTargetPdf > 0) ? weightSum / selectedTargetPdf : 0; + + bool compact = false; + + if (weight > 0) { + compact = RAB_StoreCompactLightInfo(risBufferPtr, selectedLightInfo); + } + + if (compact) { + selectedLight |= RTXDI_LIGHT_COMPACT_BIT; + } + + RTXDI_RIS_BUFFER[risBufferPtr] = uint2(selectedLight, asuint(weight)); +} + +#endif // (RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED) + +#endif // PRESAMPLING_FUNCTIONS_HLSLI \ No newline at end of file diff --git a/rtxdi-sdk/include/rtxdi/RISBuffer.hlsli b/rtxdi-sdk/include/rtxdi/RISBuffer.hlsli new file mode 100644 index 0000000..6f9a034 --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/RISBuffer.hlsli @@ -0,0 +1,44 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef RTXDI_RIS_BUFFER_HLSLI +#define RTXDI_RIS_BUFFER_HLSLI + +struct RTXDI_RISTileInfo +{ + uint risTileOffset; + uint risTileSize; +}; + +void RTXDI_RandomlySelectLightDataFromRISTile( + inout RAB_RandomSamplerState rng, + RTXDI_RISTileInfo bufferInfo, + out uint2 tileData, + out uint risBufferPtr) +{ + float rnd = RAB_GetNextRandom(rng); + uint risSample = min(uint(floor(rnd * bufferInfo.risTileSize)), bufferInfo.risTileSize - 1); + risBufferPtr = risSample + bufferInfo.risTileOffset; + tileData = RTXDI_RIS_BUFFER[risBufferPtr]; +} + +RTXDI_RISTileInfo RTXDI_RandomlySelectRISTile( + inout RAB_RandomSamplerState coherentRng, + RTXDI_RISBufferSegmentParameters params) +{ + RTXDI_RISTileInfo risTileInfo; + float tileRnd = RAB_GetNextRandom(coherentRng); + uint tileIndex = uint(tileRnd * params.tileCount); + risTileInfo.risTileOffset = tileIndex * params.tileSize + params.bufferOffset; + risTileInfo.risTileSize = params.tileSize; + return risTileInfo; +} + +#endif // RTXDI_RIS_BUFFER_HLSLI \ No newline at end of file diff --git a/rtxdi-sdk/include/rtxdi/RISBufferSegmentAllocator.h b/rtxdi-sdk/include/rtxdi/RISBufferSegmentAllocator.h new file mode 100644 index 0000000..9a030a2 --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/RISBufferSegmentAllocator.h @@ -0,0 +1,32 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#pragma once + +#include + +#include "RISBufferSegmentParameters.h" + +namespace rtxdi +{ + +class RISBufferSegmentAllocator +{ +public: + RISBufferSegmentAllocator(); + // Returns starting offset of segment in buffer + uint32_t allocateSegment(uint32_t sizeInElements); + uint32_t getTotalSizeInElements() const; + +private: + uint32_t m_totalSizeInElements; +}; + +} diff --git a/rtxdi-sdk/include/rtxdi/RISBufferSegmentParameters.h b/rtxdi-sdk/include/rtxdi/RISBufferSegmentParameters.h new file mode 100644 index 0000000..aa23af7 --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/RISBufferSegmentParameters.h @@ -0,0 +1,24 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef RTXDI_RIS_BUFFER_SEGMENT_PARAMETERS +#define RTXDI_RIS_BUFFER_SEGMENT_PARAMETERS + +#include "RtxdiTypes.h" + +struct RTXDI_RISBufferSegmentParameters +{ + uint32_t bufferOffset; + uint32_t tileSize; + uint32_t tileCount; + uint32_t pad1; +}; + +#endif // RTXDI_RIS_BUFFER_SEGMENT_PARAMETERS \ No newline at end of file diff --git a/rtxdi-sdk/include/rtxdi/RTXDI.h b/rtxdi-sdk/include/rtxdi/RTXDI.h deleted file mode 100644 index fd63ea4..0000000 --- a/rtxdi-sdk/include/rtxdi/RTXDI.h +++ /dev/null @@ -1,174 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. - # - # NVIDIA CORPORATION and its licensors retain all intellectual property - # and proprietary rights in and to this software, related documentation - # and any modifications thereto. Any use, reproduction, disclosure or - # distribution of this software and related documentation without an express - # license agreement from NVIDIA CORPORATION is strictly prohibited. - **************************************************************************/ - -#pragma once - -#include -#include - -#include "RtxdiParameters.h" - -namespace rtxdi -{ - struct uint3 - { - uint32_t x; - uint32_t y; - uint32_t z; - }; - - struct float3 - { - float x; - float y; - float z; - }; - - // Checkerboard sampling modes match those used in NRD, based on frameIndex: - // Even frame(0) Odd frame(1) ... - // B W W B - // W B B W - // BLACK and WHITE modes define cells with VALID data - enum class CheckerboardMode : uint32_t - { - Off = 0, - Black = 1, - White = 2 - }; - - enum class ReGIRMode : uint32_t - { - Disabled = 0, - Grid = 1, - Onion = 2 - }; - - struct ReGIRContextParameters - { - ReGIRMode Mode = ReGIRMode::Disabled; - - // Common settings - - // Number of light reservoirs computed and stored for each cell. - uint32_t LightsPerCell = 512; - - // Grid mode - - // Grid dimensions along the primary axes, in cells. - uint3 GridSize = { 16, 16, 16 }; - - // Onion mode - - // Number of onion layers that cover the volume around the center - // with high detail. These layers have cell size that is proportional - // to a cubic root of the distance from the center. The number of cells - // in each detail layer is higher than the number of cells in the previous - // detail layer. - // Acceptable values are 0 to RTXDI_ONION_MAX_LAYER_GROUPS. - uint32_t OnionDetailLayers = 5; - - // Number of onion layers that cover the volume after the detail layers. - // Each coverage layer has the same number of cells that is determined - // only by the number of the detail layers. Coverage layers have cell size - // that is proportional to the distance from the center as a linear function. - uint32_t OnionCoverageLayers = 10; - }; - - struct ContextParameters - { - uint32_t TileSize = 1024; - uint32_t TileCount = 128; - uint32_t NeighborOffsetCount = 8192; - uint32_t RenderWidth = 0; - uint32_t RenderHeight = 0; - uint32_t EnvironmentTileSize = 1024; - uint32_t EnvironmentTileCount = 128; - - CheckerboardMode CheckerboardSamplingMode = CheckerboardMode::Off; - - ReGIRContextParameters ReGIR; - }; - - struct FrameParameters - { - // Linear index of the current frame, used to determine the checkerboard field. - uint32_t frameIndex = 0; - - // Index of the first local light in the light buffer. - uint32_t firstLocalLight = 0; - - // Number of local lights available on this frame. - uint32_t numLocalLights = 0; - - // Index of the first infinite light in the light buffer. - uint32_t firstInfiniteLight = 0; - - // Number of infinite lights available on this frame. They must be indexed - // immediately following the local lights. - uint32_t numInfiniteLights = 0; - - // Enables the use of an importance sampled environment map light. - bool environmentLightPresent = false; - - // Index of the importance environment light in the light buffer. - uint32_t environmentLightIndex = RTXDI_INVALID_LIGHT_INDEX; - - // Use image-based importance sampling for local lights - bool enableLocalLightImportanceSampling = false; - - // Size of the smallest ReGIR cell, in world units. - float regirCellSize = 1.f; - - // Scale of jitter applied to surface positions when sampling the ReGIR grid, - // measured in grid cells. The value of 1.0 means plus or minus one grid cell. - // This jitter scale is provided here because it affects both grid construction - // (to determine effective cell radii) and sampling. - float regirSamplingJitter = 1.0f; - - // Center of the ReGIR structure, in world space. - float3 regirCenter{}; - }; - - - class Context - { - private: - ContextParameters m_Params; - - uint32_t m_ReservoirBlockRowPitch = 0; - uint32_t m_ReservoirArrayPitch = 0; - - uint32_t m_RegirCellOffset = 0; - uint32_t m_OnionCells = 0; - std::vector m_OnionLayers; - std::vector m_OnionRings; - float m_OnionCubicRootFactor = 0.f; - float m_OnionLinearFactor = 0.f; - - void InitializeOnion(); - void ComputeOnionJitterCurve(); - - public: - Context(const ContextParameters& params); - const ContextParameters& GetParameters() const; - - uint32_t GetRisBufferElementCount() const; - uint32_t GetReservoirBufferElementCount() const; - uint32_t GetReGIRLightSlotCount() const; - - void FillRuntimeParameters( - RTXDI_ResamplingRuntimeParameters& runtimeParams, - const FrameParameters& frame) const; - - void FillNeighborOffsetBuffer(uint8_t* buffer) const; - }; - - void ComputePdfTextureSize(uint32_t maxItems, uint32_t& outWidth, uint32_t& outHeight, uint32_t& outMipLevels); -} diff --git a/rtxdi-sdk/include/rtxdi/ReGIR.h b/rtxdi-sdk/include/rtxdi/ReGIR.h new file mode 100644 index 0000000..b2d0602 --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/ReGIR.h @@ -0,0 +1,174 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef REGIR_PARAMETERS_H +#define REGIR_PARAMETERS_H + +#include +#include + +#include "ReGIRParameters.h" + +struct RTXDI_RuntimeParameters; + +namespace rtxdi +{ + class RISBufferSegmentAllocator; + + struct uint3 + { + uint32_t x; + uint32_t y; + uint32_t z; + }; + + struct float3 + { + float x; + float y; + float z; + }; + + enum class ReGIRMode : uint32_t + { + Disabled = 0, + Grid = RTXDI_REGIR_GRID, + Onion = RTXDI_REGIR_ONION + }; + + struct ReGIRGridStaticParameters + { + // Grid dimensions along the primary axes, in cells. + uint3 GridSize = { 16, 16, 16 }; + }; + + struct ReGIROnionStaticParameters + { + // Number of onion layers that cover the volume around the center + // with high detail. These layers have cell size that is proportional + // to a cubic root of the distance from the center. The number of cells + // in each detail layer is higher than the number of cells in the previous + // detail layer. + // Acceptable values are 0 to RTXDI_ONION_MAX_LAYER_GROUPS. + uint32_t OnionDetailLayers = 5; + + // Number of onion layers that cover the volume after the detail layers. + // Each coverage layer has the same number of cells that is determined + // only by the number of the detail layers. Coverage layers have cell size + // that is proportional to the distance from the center as a linear function. + uint32_t OnionCoverageLayers = 10; + }; + + // ReGIR parameters that are used to generate ReGIR data structures + // Changing these requires recreating the ReGIR context and the associated buffers + struct ReGIRStaticParameters + { + ReGIRMode Mode = ReGIRMode::Onion; + + // Number of light reservoirs computed and stored for each cell. + uint32_t LightsPerCell = 512; + + ReGIRGridStaticParameters gridParameters; + ReGIROnionStaticParameters onionParameters; + }; + + // ReGIR parameters generated from the ReGIRGridStaticParameters + // Changing these requires changing the ReGIRStaticParameters and + // therefore recreating the ReGIRcontext + struct ReGIRGridCalculatedParameters + { + uint32_t lightSlotCount = 0; + }; + + // ReGIR parameters generated from the ReGIROnionStaticParameters + // Changing these requires changing the ReGIRStaticParameters and + // therefore recreating the ReGIRcontext + struct ReGIROnionCalculatedParameters + { + uint32_t lightSlotCount = 0; + uint32_t regirOnionCells = 0; + std::vector regirOnionLayers; + std::vector regirOnionRings; + float regirOnionCubicRootFactor = 0.f; + float regirOnionLinearFactor = 0.f; + }; + + enum class LocalLightReGIRPresamplingMode : uint32_t + { + Uniform = REGIR_LOCAL_LIGHT_PRESAMPLING_MODE_UNIFORM, + Power_RIS = REGIR_LOCAL_LIGHT_PRESAMPLING_MODE_POWER_RIS + }; + + enum class LocalLightReGIRFallbackSamplingMode : uint32_t + { + Uniform = REGIR_LOCAL_LIGHT_FALLBACK_MODE_UNIFORM, + Power_RIS = REGIR_LOCAL_LIGHT_FALLBACK_MODE_POWER_RIS + }; + + // ReGIR parameters that can be changed at runtime without requiring any + // recreation of buffers or data structures. + struct ReGIRDynamicParameters + { + // Size of the smallest ReGIR cell size in world units + float regirCellSize = 1.0f; + + // Center of the ReGIR structure in world space + float3 center = { 0.0f, 0.0f, 0.0f }; + + // Light sampling mode to use for local light sampling when the surface falls outside the ReGIR grid + LocalLightReGIRFallbackSamplingMode fallbackSamplingMode = LocalLightReGIRFallbackSamplingMode::Power_RIS; + + // Light sampling mode ReGIR uses to select lights to fill the ReGIR RIS buffer. + LocalLightReGIRPresamplingMode presamplingMode = LocalLightReGIRPresamplingMode::Power_RIS; + + // Scale of jitter applied to surface positions when sampling the ReGIR grid, + // measured in grid cells. The value of 1.0 means plus or minus one grid cell. + // This jitter scale is provided here because it affects both grid construction + // (to determine effective cell radii) and sampling. + float regirSamplingJitter = 1.0f; + + // Number of lights samples to take when filling a ReGIR cell. + uint32_t regirNumBuildSamples = 8; + }; + + + // Make this take static ReGIR params, update its dynamic ones + class ReGIRContext + { + public: + ReGIRContext(const ReGIRStaticParameters& params, RISBufferSegmentAllocator& risBufferSegmentAllocator); + + bool isLocalLightPowerRISEnable() const; + + uint32_t getReGIRCellOffset() const; + uint32_t getReGIRLightSlotCount() const; + ReGIRGridCalculatedParameters getReGIRGridCalculatedParameters() const; + ReGIROnionCalculatedParameters getReGIROnionCalculatedParameters() const; + ReGIRDynamicParameters getReGIRDynamicParameters() const; + ReGIRStaticParameters getReGIRStaticParameters() const; + + void setDynamicParameters(const ReGIRDynamicParameters& dynamicParameters); + + private: + void InitializeOnion(const ReGIRStaticParameters& params); + void ComputeOnionJitterCurve(); + void ComputeGridLightSlotCount(); + void AllocateRISBufferSegment(RISBufferSegmentAllocator& risBufferSegmentAllocator); + + uint32_t m_regirCellOffset = 0; + + ReGIRStaticParameters m_regirStaticParameters; + ReGIRDynamicParameters m_regirDynamicParameters; + ReGIROnionCalculatedParameters m_regirOnionCalculatedParameters; + ReGIRGridCalculatedParameters m_regirGridCalculatedParameters; + }; +} + +#endif // REGIR_PARAMETERS_H \ No newline at end of file diff --git a/rtxdi-sdk/include/rtxdi/ReGIRParameters.h b/rtxdi-sdk/include/rtxdi/ReGIRParameters.h new file mode 100644 index 0000000..6c32c3a --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/ReGIRParameters.h @@ -0,0 +1,103 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef RTXDI_REGIR_PARAMETERS_H +#define RTXDI_REGIR_PARAMETERS_H + +#include "RtxdiTypes.h" + +#define RTXDI_ONION_MAX_LAYER_GROUPS 8 +#define RTXDI_ONION_MAX_RINGS 52 + +#define RTXDI_REGIR_DISABLED 0 +#define RTXDI_REGIR_GRID 1 +#define RTXDI_REGIR_ONION 2 + +#ifndef RTXDI_REGIR_MODE +#define RTXDI_REGIR_MODE RTXDI_REGIR_DISABLED +#endif + +struct ReGIR_OnionLayerGroup +{ + float innerRadius; + float outerRadius; + float invLogLayerScale; + int layerCount; + + float invEquatorialCellAngle; + int cellsPerLayer; + int ringOffset; + int ringCount; + + float equatorialCellAngle; + float layerScale; + int layerCellOffset; + int pad1; +}; + +struct ReGIR_OnionRing +{ + float cellAngle; + float invCellAngle; + int cellOffset; + int cellCount; +}; + +#define REGIR_LOCAL_LIGHT_PRESAMPLING_MODE_UNIFORM 0 +#define REGIR_LOCAL_LIGHT_PRESAMPLING_MODE_POWER_RIS 1 + +#define REGIR_LOCAL_LIGHT_FALLBACK_MODE_UNIFORM 0 +#define REGIR_LOCAL_LIGHT_FALLBACK_MODE_POWER_RIS 1 + +struct ReGIR_CommonParameters +{ + uint32_t localLightSamplingFallbackMode; + float centerX; + float centerY; + float centerZ; + + uint32_t risBufferOffset; + uint32_t lightsPerCell; + float cellSize; + float samplingJitter; + + uint32_t localLightPresamplingMode; + uint32_t numRegirBuildSamples; // PresampleReGIR.hlsl -> RTXDI_PresampleLocalLightsForReGIR + uint32_t pad1; + uint32_t pad2; +}; + +struct ReGIR_GridParameters +{ + uint32_t cellsX; + uint32_t cellsY; + uint32_t cellsZ; + uint32_t pad1; +}; + +struct ReGIR_OnionParameters +{ + ReGIR_OnionLayerGroup layers[RTXDI_ONION_MAX_LAYER_GROUPS]; + ReGIR_OnionRing rings[RTXDI_ONION_MAX_RINGS]; + + uint32_t numLayerGroups; + float cubicRootFactor; + float linearFactor; + float pad1; +}; + +struct ReGIR_Parameters +{ + ReGIR_CommonParameters commonParams; + ReGIR_GridParameters gridParams; + ReGIR_OnionParameters onionParams; +}; + +#endif // RTXDI_REGIR_PARAMETERS_H \ No newline at end of file diff --git a/rtxdi-sdk/include/rtxdi/ReGIRSampling.hlsli b/rtxdi-sdk/include/rtxdi/ReGIRSampling.hlsli new file mode 100644 index 0000000..1493197 --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/ReGIRSampling.hlsli @@ -0,0 +1,241 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef RTXDI_REGIR_SAMPLING_FUNCTIONS_HLSLI +#define RTXDI_REGIR_SAMPLING_FUNCTIONS_HLSLI + +#if RTXDI_REGIR_MODE == RTXDI_REGIR_GRID + +float RTXDI_ReGIR_GetJitterScale(ReGIR_Parameters params, float3 worldPos) +{ + return params.commonParams.samplingJitter * params.commonParams.cellSize; +} + +int RTXDI_ReGIR_WorldPosToCellIndex(ReGIR_Parameters params, float3 worldPos) +{ + const float3 gridCenter = float3(params.commonParams.centerX, params.commonParams.centerY, params.commonParams.centerZ); + const int3 gridCellCount = int3(params.gridParams.cellsX, params.gridParams.cellsY, params.gridParams.cellsZ); + const float3 gridOrigin = gridCenter - float3(gridCellCount) * (params.commonParams.cellSize * 0.5); + + int3 gridCell = int3(floor((worldPos - gridOrigin) / params.commonParams.cellSize)); + + if (gridCell.x < 0 || gridCell.y < 0 || gridCell.z < 0 || + gridCell.x >= gridCellCount.x || gridCell.y >= gridCellCount.y || gridCell.z >= gridCellCount.z) + return -1; + + return gridCell.x + (gridCell.y + (gridCell.z * gridCellCount.y)) * gridCellCount.x; +} + +bool RTXDI_ReGIR_CellIndexToWorldPos(ReGIR_Parameters params, int cellIndex, out float3 cellCenter, out float cellRadius) +{ + const float3 gridCenter = float3(params.commonParams.centerX, params.commonParams.centerY, params.commonParams.centerZ); + const int3 gridCellCount = int3(params.gridParams.cellsX, params.gridParams.cellsY, params.gridParams.cellsZ); + const float3 gridOrigin = gridCenter - float3(gridCellCount) * (params.commonParams.cellSize * 0.5); + + uint3 cellPosition; + cellPosition.x = cellIndex; + cellPosition.y = cellPosition.x / params.gridParams.cellsX; + cellPosition.x %= params.gridParams.cellsX; + cellPosition.z = cellPosition.y / params.gridParams.cellsY; + cellPosition.y %= params.gridParams.cellsY; + if (cellPosition.z >= params.gridParams.cellsZ) + { + cellCenter = float3(0.0, 0.0, 0.0); + cellRadius = 0.0; + return false; + } + + cellCenter = (float3(cellPosition) + 0.5) * params.commonParams.cellSize + gridOrigin; + + cellRadius = params.commonParams.cellSize * sqrt(3.0); + + return true; +} + +#elif RTXDI_REGIR_MODE == RTXDI_REGIR_ONION + +float RTXDI_ReGIR_GetJitterScale(ReGIR_Parameters params, float3 worldPos) +{ + const float3 onionCenter = float3(params.commonParams.centerX, params.commonParams.centerY, params.commonParams.centerZ); + const float3 translatedPos = worldPos - onionCenter; + + float distanceToCenter = length(translatedPos) / params.commonParams.cellSize; + float jitterScale = max(1.0, max( + pow(distanceToCenter, 1.0 / 3.0) * params.onionParams.cubicRootFactor, + distanceToCenter * params.onionParams.linearFactor + )); + + return jitterScale * params.commonParams.samplingJitter * params.commonParams.cellSize; +} + +int RTXDI_ReGIR_WorldPosToCellIndex(ReGIR_Parameters params, float3 worldPos) +{ + const float3 onionCenter = float3(params.commonParams.centerX, params.commonParams.centerY, params.commonParams.centerZ); + const float3 translatedPos = worldPos - onionCenter; + + float r, azimuth, elevation; + RTXDI_CartesianToSpherical(translatedPos, r, azimuth, elevation); + azimuth += RTXDI_PI; // Add PI to make sure azimuth doesn't cross zero + + if (r <= params.onionParams.layers[0].innerRadius) + return 0; + + ReGIR_OnionLayerGroup layerGroup; + + int layerGroupIndex; + for (layerGroupIndex = 0; layerGroupIndex < params.onionParams.numLayerGroups; layerGroupIndex++) + { + if (r <= params.onionParams.layers[layerGroupIndex].outerRadius) + { + layerGroup = params.onionParams.layers[layerGroupIndex]; + break; + } + } + + if (layerGroupIndex >= params.onionParams.numLayerGroups) + return -1; + + uint layerIndex = uint(floor(max(0, log(r / layerGroup.innerRadius) * layerGroup.invLogLayerScale))); + layerIndex = min(layerIndex, layerGroup.layerCount - 1); // Guard against numeric errors at the outer shell + + uint ringIndex = uint(floor(abs(elevation) * layerGroup.invEquatorialCellAngle + 0.5)); + ReGIR_OnionRing ring = params.onionParams.rings[layerGroup.ringOffset + ringIndex]; + + if ((layerIndex & 1) != 0) + { + azimuth -= ring.cellAngle * 0.5; // Add some variation to the repetitive layers + if (azimuth < 0) + azimuth += 2 * RTXDI_PI; + } + + int cellIndex = int(floor(azimuth * ring.invCellAngle)); + + int ringCellOffset = ring.cellOffset; + if (elevation < 0 && ringIndex > 0) + ringCellOffset += ring.cellCount; + + return int(cellIndex + ringCellOffset + layerIndex * layerGroup.cellsPerLayer + layerGroup.layerCellOffset); +} + +bool RTXDI_ReGIR_CellIndexToWorldPos(ReGIR_Parameters params, int cellIndex, out float3 cellCenter, out float cellRadius) +{ + const float3 onionCenter = float3(params.commonParams.centerX, params.commonParams.centerY, params.commonParams.centerZ); + + cellCenter = float3(0, 0, 0); + cellRadius = 0; + + if (cellIndex < 0) + return false; + + if (cellIndex == 0) + { + cellCenter = onionCenter; + cellRadius = params.onionParams.layers[0].innerRadius; + return true; + } + + ReGIR_OnionLayerGroup layerGroup; + + cellIndex -= 1; + + int layerGroupIndex; + for (layerGroupIndex = 0; layerGroupIndex < params.onionParams.numLayerGroups; layerGroupIndex++) + { + layerGroup = params.onionParams.layers[layerGroupIndex]; + int cellsPerGroup = layerGroup.cellsPerLayer * layerGroup.layerCount; + + if (cellIndex < cellsPerGroup) + break; + + cellIndex -= cellsPerGroup; + } + + if (layerGroupIndex >= params.onionParams.numLayerGroups) + return false; + + int layerIndex = cellIndex / layerGroup.cellsPerLayer; + cellIndex -= layerIndex * layerGroup.cellsPerLayer; + + ReGIR_OnionRing ring; + + int ringIndex; + for (ringIndex = 0; ringIndex < layerGroup.ringCount; ringIndex++) + { + ring = params.onionParams.rings[layerGroup.ringOffset + ringIndex]; + + if (cellIndex < ring.cellOffset + ring.cellCount * (ringIndex > 0 ? 2 : 1)) + break; + } + + if (ringIndex >= layerGroup.ringCount) + return false; // shouldn't happen + + cellIndex -= ring.cellOffset; + float elevation = float(ringIndex) * layerGroup.equatorialCellAngle; + if (cellIndex >= ring.cellCount) + { + elevation = -elevation; + } + + float azimuth = (float(cellIndex) + 0.5) * ring.cellAngle; + + if ((layerIndex & 1) != 0) + azimuth += ring.cellAngle * 0.5; // Match the variation added in ...WorldPosToCellIndex() + + azimuth -= RTXDI_PI; // Reverse the PI addition in the position -> cell index translation + + float layerInnerRadius = layerGroup.innerRadius * pow(layerGroup.layerScale, layerIndex); + float layerOuterRadius = layerInnerRadius * layerGroup.layerScale; + + float r = (layerInnerRadius + layerOuterRadius) * 0.5; + + cellCenter = RTXDI_SphericalToCartesian(r, azimuth, elevation); + + azimuth += ring.cellAngle * 0.5; + elevation = (elevation == 0) + ? layerGroup.equatorialCellAngle * 0.5 + : (abs(elevation) - layerGroup.equatorialCellAngle * 0.5) * sign(elevation); + + float3 cellCorner = RTXDI_SphericalToCartesian(layerOuterRadius, azimuth, elevation); + + cellRadius = length(cellCorner - cellCenter); + + cellCenter += onionCenter; + + return true; +} + +#endif + +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED + +float3 RTXDI_VisualizeReGIRCells(ReGIR_Parameters params, float3 worldPos) +{ + int cellIndex = RTXDI_ReGIR_WorldPosToCellIndex(params, worldPos); + + uint cellHash = RTXDI_JenkinsHash(cellIndex); + + float3 cellColor; + cellColor.x = (cellHash & 0x7ff) / float(0x7ff); + cellColor.y = ((cellHash >> 11) & 0x7ff) / float(0x7ff); + cellColor.z = ((cellHash >> 22) & 0x3ff) / float(0x3ff); + + float3 cellCenter; + float cellRadius; + bool cellFound = RTXDI_ReGIR_CellIndexToWorldPos(params, cellIndex, cellCenter, cellRadius); + + float distanceToCenter = cellFound ? saturate(length(cellCenter - worldPos) / cellRadius) : 0; + + return distanceToCenter * cellColor; +} + +#endif + +#endif // RTXDI_REGIR_SAMPLING_FUNCTIONS_HLSLI \ No newline at end of file diff --git a/rtxdi-sdk/include/rtxdi/ReSTIRDI.h b/rtxdi-sdk/include/rtxdi/ReSTIRDI.h new file mode 100644 index 0000000..482a985 --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/ReSTIRDI.h @@ -0,0 +1,162 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#pragma once + +#include +#include +#include + +#include "ReSTIRDIParameters.h" +#include "RTXDIUtils.h" + +namespace rtxdi +{ + static constexpr uint32_t c_NumReSTIRDIReservoirBuffers = 3; + + enum class ReSTIRDI_ResamplingMode : uint32_t + { + None, + Temporal, + Spatial, + TemporalAndSpatial, + FusedSpatiotemporal + }; + + struct RISBufferSegmentParameters + { + uint32_t tileSize; + uint32_t tileCount; + }; + + // Parameters used to initialize the ReSTIRDIContext + // Changing any of these requires recreating the context. + struct ReSTIRDIStaticParameters + { + uint32_t NeighborOffsetCount = 8192; + uint32_t RenderWidth = 0; + uint32_t RenderHeight = 0; + + CheckerboardMode CheckerboardSamplingMode = CheckerboardMode::Off; + }; + + constexpr ReSTIRDI_BufferIndices getDefaultReSTIRDIBufferIndices() + { + ReSTIRDI_BufferIndices bufferIndices = {}; + bufferIndices.initialSamplingOutputBufferIndex = 0; + bufferIndices.temporalResamplingInputBufferIndex = 0; + bufferIndices.temporalResamplingOutputBufferIndex = 0; + bufferIndices.spatialResamplingInputBufferIndex = 0; + bufferIndices.spatialResamplingOutputBufferIndex = 0; + bufferIndices.shadingInputBufferIndex = 0; + return bufferIndices; + } + + constexpr ReSTIRDI_InitialSamplingParameters getDefaultReSTIRDIInitialSamplingParams() + { + ReSTIRDI_InitialSamplingParameters params = {}; + params.brdfCutoff = 0.0001f; + params.enableInitialVisibility = true; + params.environmentMapImportanceSampling = 1; + params.localLightSamplingMode = ReSTIRDI_LocalLightSamplingMode::Uniform; + params.numPrimaryBrdfSamples = 1; + params.numPrimaryEnvironmentSamples = 1; + params.numPrimaryInfiniteLightSamples = 1; + params.numPrimaryLocalLightSamples = 8; + return params; + } + + constexpr ReSTIRDI_TemporalResamplingParameters getDefaultReSTIRDITemporalResamplingParams() + { + ReSTIRDI_TemporalResamplingParameters params = {}; + params.boilingFilterStrength = 0.2f; + params.discardInvisibleSamples = false; + params.enableBoilingFilter = true; + params.enablePermutationSampling = true; + params.maxHistoryLength = 20; + params.permutationSamplingThreshold = 0.9f; + params.temporalBiasCorrection = ReSTIRDI_TemporalBiasCorrectionMode::Basic; + params.temporalDepthThreshold = 0.1f; + params.temporalNormalThreshold = 0.5f; + return params; + } + + constexpr ReSTIRDI_SpatialResamplingParameters getDefaultReSTIRDISpatialResamplingParams() + { + ReSTIRDI_SpatialResamplingParameters params = {}; + params.numDisocclusionBoostSamples = 8; + params.numSpatialSamples = 1; + params.spatialBiasCorrection = ReSTIRDI_SpatialBiasCorrectionMode::Basic; + params.spatialDepthThreshold = 0.1f; + params.spatialNormalThreshold = 0.5f; + params.spatialSamplingRadius = 32.0f; + return params; + } + + constexpr ReSTIRDI_ShadingParameters getDefaultReSTIRDIShadingParams() + { + ReSTIRDI_ShadingParameters params = {}; + params.enableDenoiserInputPacking = false; + params.enableFinalVisibility = true; + params.finalVisibilityMaxAge = 4; + params.finalVisibilityMaxDistance = 16.f; + params.reuseFinalVisibility = true; + return params; + } + + // Make this constructor take static RTXDI params, update its dynamic ones + class ReSTIRDIContext + { + public: + ReSTIRDIContext(const ReSTIRDIStaticParameters& params); + + RTXDI_ReservoirBufferParameters getReservoirBufferParameters() const; + ReSTIRDI_ResamplingMode getResamplingMode() const; + RTXDI_RuntimeParameters getRuntimeParams() const; + ReSTIRDI_BufferIndices getBufferIndices() const; + ReSTIRDI_InitialSamplingParameters getInitialSamplingParameters() const; + ReSTIRDI_TemporalResamplingParameters getTemporalResamplingParameters() const; + ReSTIRDI_SpatialResamplingParameters getSpatialResamplingParameters() const; + ReSTIRDI_ShadingParameters getShadingParameters() const; + + uint32_t getFrameIndex() const; + const ReSTIRDIStaticParameters& getStaticParameters() const; + + void setFrameIndex(uint32_t frameIndex); + void setResamplingMode(ReSTIRDI_ResamplingMode resamplingMode); + void setInitialSamplingParameters(const ReSTIRDI_InitialSamplingParameters& initialSamplingParams); + void setTemporalResamplingParameters(const ReSTIRDI_TemporalResamplingParameters& temporalResamplingParams); + void setSpatialResamplingParameters(const ReSTIRDI_SpatialResamplingParameters& spatialResamplingParams); + void setShadingParameters(const ReSTIRDI_ShadingParameters& shadingParams); + + static const uint32_t NumReservoirBuffers; + + private: + uint32_t m_LastFrameOutputReservoir = 0; + uint32_t m_CurrentFrameOutputReservoir = 0; + + uint32_t m_frameIndex; + + ReSTIRDIStaticParameters m_staticParams; + + ReSTIRDI_ResamplingMode m_resamplingMode; + RTXDI_ReservoirBufferParameters m_reservoirBufferParams; + RTXDI_RuntimeParameters m_runtimeParams; + ReSTIRDI_BufferIndices m_bufferIndices; + + ReSTIRDI_InitialSamplingParameters m_initialSamplingParams; + ReSTIRDI_TemporalResamplingParameters m_temporalResamplingParams; + ReSTIRDI_SpatialResamplingParameters m_spatialResamplingParams; + ReSTIRDI_ShadingParameters m_shadingParams; + + void updateBufferIndices(); + void updateCheckerboardField(); + }; +} diff --git a/rtxdi-sdk/include/rtxdi/ReSTIRDIParameters.h b/rtxdi-sdk/include/rtxdi/ReSTIRDIParameters.h new file mode 100644 index 0000000..82dab06 --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/ReSTIRDIParameters.h @@ -0,0 +1,126 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef RTXDI_RESTIRDI_PARAMETERS_H +#define RTXDI_RESTIRDI_PARAMETERS_H + +#include "RtxdiTypes.h" +#include "RtxdiParameters.h" + +#ifdef __cplusplus +enum class ReSTIRDI_LocalLightSamplingMode : uint32_t +{ + Uniform = ReSTIRDI_LocalLightSamplingMode_UNIFORM, + Power_RIS = ReSTIRDI_LocalLightSamplingMode_POWER_RIS, + ReGIR_RIS = ReSTIRDI_LocalLightSamplingMode_REGIR_RIS +}; + +enum class ReSTIRDI_TemporalBiasCorrectionMode : uint32_t +{ + Off = RTXDI_BIAS_CORRECTION_OFF, + Basic = RTXDI_BIAS_CORRECTION_BASIC, + Pairwise = RTXDI_BIAS_CORRECTION_PAIRWISE, + Raytraced = RTXDI_BIAS_CORRECTION_RAY_TRACED +}; + +enum class ReSTIRDI_SpatialBiasCorrectionMode : uint32_t +{ + Off = RTXDI_BIAS_CORRECTION_OFF, + Basic = RTXDI_BIAS_CORRECTION_BASIC, + Pairwise = RTXDI_BIAS_CORRECTION_PAIRWISE, + Raytraced = RTXDI_BIAS_CORRECTION_RAY_TRACED +}; +#else +#define ReSTIRDI_LocalLightSamplingMode uint32_t +#define ReSTIRDI_TemporalBiasCorrectionMode uint32_t +#define ReSTIRDI_SpatialBiasCorrectionMode uint32_t +#endif + +struct ReSTIRDI_BufferIndices +{ + uint32_t initialSamplingOutputBufferIndex; + uint32_t temporalResamplingInputBufferIndex; + uint32_t temporalResamplingOutputBufferIndex; + uint32_t spatialResamplingInputBufferIndex; + + uint32_t spatialResamplingOutputBufferIndex; + uint32_t shadingInputBufferIndex; + uint32_t pad1; + uint32_t pad2; +}; + +struct ReSTIRDI_InitialSamplingParameters +{ + uint32_t numPrimaryLocalLightSamples; + uint32_t numPrimaryInfiniteLightSamples; + uint32_t numPrimaryEnvironmentSamples; + uint32_t numPrimaryBrdfSamples; + + float brdfCutoff; + uint32_t enableInitialVisibility; + uint32_t environmentMapImportanceSampling; // Only used in InitialSamplingFunctions.hlsli via RAB_EvaluateEnvironmentMapSamplingPdf + ReSTIRDI_LocalLightSamplingMode localLightSamplingMode; +}; + +struct ReSTIRDI_TemporalResamplingParameters +{ + float temporalDepthThreshold; + float temporalNormalThreshold; + uint32_t maxHistoryLength; + ReSTIRDI_TemporalBiasCorrectionMode temporalBiasCorrection; + + uint32_t enablePermutationSampling; + float permutationSamplingThreshold; + uint32_t enableBoilingFilter; + float boilingFilterStrength; + + uint32_t discardInvisibleSamples; + uint32_t uniformRandomNumber; + uint32_t pad2; + uint32_t pad3; +}; + +struct ReSTIRDI_SpatialResamplingParameters +{ + float spatialDepthThreshold; + float spatialNormalThreshold; + ReSTIRDI_SpatialBiasCorrectionMode spatialBiasCorrection; + uint32_t numSpatialSamples; + + uint32_t numDisocclusionBoostSamples; + float spatialSamplingRadius; + uint32_t neighborOffsetMask; + uint32_t discountNaiveSamples; +}; + +struct ReSTIRDI_ShadingParameters +{ + uint32_t enableFinalVisibility; + uint32_t reuseFinalVisibility; + uint32_t finalVisibilityMaxAge; + float finalVisibilityMaxDistance; + + uint32_t enableDenoiserInputPacking; + uint32_t pad1; + uint32_t pad2; + uint32_t pad3; +}; + +struct ReSTIRDI_Parameters +{ + RTXDI_ReservoirBufferParameters reservoirBufferParams; + ReSTIRDI_BufferIndices bufferIndices; + ReSTIRDI_InitialSamplingParameters initialSamplingParams; + ReSTIRDI_TemporalResamplingParameters temporalResamplingParams; + ReSTIRDI_SpatialResamplingParameters spatialResamplingParams; + ReSTIRDI_ShadingParameters shadingParams; +}; + +#endif // RTXDI_RESTIRDI_PARAMETERS_H \ No newline at end of file diff --git a/rtxdi-sdk/include/rtxdi/ReSTIRGI.h b/rtxdi-sdk/include/rtxdi/ReSTIRGI.h new file mode 100644 index 0000000..8f9cd3b --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/ReSTIRGI.h @@ -0,0 +1,120 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#pragma once + +#include +#include "rtxdi/ReSTIRGIParameters.h" +#include "rtxdi/RTXDIUtils.h" + +namespace rtxdi +{ + +static constexpr uint32_t c_NumReSTIRGIReservoirBuffers = 2; + +struct ReSTIRGIStaticParameters +{ + uint32_t RenderWidth = 0; + uint32_t RenderHeight = 0; + CheckerboardMode CheckerboardSamplingMode = CheckerboardMode::Off; +}; + +enum class ReSTIRGI_ResamplingMode : uint32_t +{ + None = 0, + Temporal = 1, + Spatial = 2, + TemporalAndSpatial = 3, + FusedSpatiotemporal = 4, +}; + +constexpr ReSTIRGI_BufferIndices getDefaultReSTIRGIBufferIndices() +{ + ReSTIRGI_BufferIndices bufferIndices = {}; + bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex = 0; + bufferIndices.temporalResamplingInputBufferIndex = 0; + bufferIndices.temporalResamplingOutputBufferIndex = 0; + bufferIndices.spatialResamplingInputBufferIndex = 0; + bufferIndices.spatialResamplingOutputBufferIndex = 0; + return bufferIndices; +} + +constexpr ReSTIRGI_TemporalResamplingParameters getDefaultReSTIRGITemporalResamplingParams() +{ + ReSTIRGI_TemporalResamplingParameters params = {}; + params.boilingFilterStrength = 0.2f; + params.depthThreshold = 0.1f; + params.enableBoilingFilter = true; + params.enableFallbackSampling = true; + params.enablePermutationSampling = false; + params.maxHistoryLength = 8; + params.maxReservoirAge = 30; + params.normalThreshold = 0.6f; + params.temporalBiasCorrectionMode = ResTIRGI_TemporalBiasCorrectionMode::Basic; + return params; +} + +constexpr ReSTIRGI_SpatialResamplingParameters getDefaultReSTIRGISpatialResamplingParams() +{ + ReSTIRGI_SpatialResamplingParameters params = {}; + params.numSpatialSamples = 2; + params.spatialBiasCorrectionMode = ResTIRGI_SpatialBiasCorrectionMode::Basic; + params.spatialDepthThreshold = 0.1f; + params.spatialNormalThreshold = 0.6f; + params.spatialSamplingRadius = 32.0f; + return params; +} + +constexpr ReSTIRGI_FinalShadingParameters getDefaultReSTIRGIFinalShadingParams() +{ + ReSTIRGI_FinalShadingParameters params = {}; + params.enableFinalMIS = true; + params.enableFinalVisibility = true; + return params; +} + +class ReSTIRGIContext +{ +public: + ReSTIRGIContext(const ReSTIRGIStaticParameters& params); + + ReSTIRGIStaticParameters getStaticParams() const; + + uint32_t getFrameIndex() const; + RTXDI_ReservoirBufferParameters getReservoirBufferParameters() const; + ReSTIRGI_ResamplingMode getResamplingMode() const; + ReSTIRGI_BufferIndices getBufferIndices() const; + ReSTIRGI_TemporalResamplingParameters getTemporalResamplingParameters() const; + ReSTIRGI_SpatialResamplingParameters getSpatialResamplingParameters() const; + ReSTIRGI_FinalShadingParameters getFinalShadingParameters() const; + + void setFrameIndex(uint32_t frameIndex); + void setResamplingMode(ReSTIRGI_ResamplingMode resamplingMode); + void setTemporalResamplingParameters(const ReSTIRGI_TemporalResamplingParameters& temporalResamplingParams); + void setSpatialResamplingParameters(const ReSTIRGI_SpatialResamplingParameters& spatialResamplingParams); + void setFinalShadingParameters(const ReSTIRGI_FinalShadingParameters& finalShadingParams); + + static uint32_t numReservoirBuffers; + +private: + ReSTIRGIStaticParameters m_staticParams; + + uint32_t m_frameIndex; + RTXDI_ReservoirBufferParameters m_reservoirBufferParams; + ReSTIRGI_ResamplingMode m_resamplingMode; + ReSTIRGI_BufferIndices m_bufferIndices; + ReSTIRGI_TemporalResamplingParameters m_temporalResamplingParams; + ReSTIRGI_SpatialResamplingParameters m_spatialResamplingParams; + ReSTIRGI_FinalShadingParameters m_finalShadingParams; + + void updateBufferIndices(); +}; + +} \ No newline at end of file diff --git a/rtxdi-sdk/include/rtxdi/ReSTIRGIParameters.h b/rtxdi-sdk/include/rtxdi/ReSTIRGIParameters.h new file mode 100644 index 0000000..92b10a2 --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/ReSTIRGIParameters.h @@ -0,0 +1,125 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef RTXDI_RESTIRGI_PARAMETERS_H +#define RTXDI_RESTIRGI_PARAMETERS_H + +#include "RtxdiTypes.h" +#include "RtxdiParameters.h" + +struct RTXDI_PackedGIReservoir +{ +#ifdef __cplusplus + using float3 = float[3]; +#endif + + float3 position; + uint32_t packed_miscData_age_M; // See Reservoir.hlsli about the detail of the bit field. + + uint32_t packed_radiance; // Stored as 32bit LogLUV format. + float weight; + uint32_t packed_normal; // Stored as 2x 16-bit snorms in the octahedral mapping + float unused; +}; + +struct ReSTIRGI_ReservoirBufferParameters +{ + uint32_t reservoirBlockRowPitch; + uint32_t reservoirArrayPitch; + uint32_t pad1; + uint32_t pad2; +}; + +#ifdef __cplusplus +enum class ResTIRGI_TemporalBiasCorrectionMode : uint32_t +{ + Off = RTXDI_BIAS_CORRECTION_OFF, + Basic = RTXDI_BIAS_CORRECTION_BASIC, + // Pairwise is not supported + Raytraced = RTXDI_BIAS_CORRECTION_RAY_TRACED +}; + +enum class ResTIRGI_SpatialBiasCorrectionMode : uint32_t +{ + Off = RTXDI_BIAS_CORRECTION_OFF, + Basic = RTXDI_BIAS_CORRECTION_BASIC, + // Pairwise is not supported + Raytraced = RTXDI_BIAS_CORRECTION_RAY_TRACED +}; +#else +#define ResTIRGI_TemporalBiasCorrectionMode uint32_t +#define ResTIRGI_SpatialBiasCorrectionMode uint32_t +#endif + +// Very similar to RTXDI_TemporalResamplingParameters but it has an extra field +// It's also not the same algo, and we don't want the two to be coupled +struct ReSTIRGI_TemporalResamplingParameters +{ + float depthThreshold; + float normalThreshold; + uint32_t enablePermutationSampling; + uint32_t maxHistoryLength; + + uint32_t maxReservoirAge; + uint32_t enableBoilingFilter; + float boilingFilterStrength; + uint32_t enableFallbackSampling; + + ResTIRGI_TemporalBiasCorrectionMode temporalBiasCorrectionMode;// = ResTIRGI_TemporalBiasCorrectionMode::Basic; + uint32_t uniformRandomNumber; + uint32_t pad2; + uint32_t pad3; +}; + +// See note for ReSTIRGI_TemporalResamplingParameters +struct ReSTIRGI_SpatialResamplingParameters +{ + float spatialDepthThreshold; + float spatialNormalThreshold; + uint32_t numSpatialSamples; + float spatialSamplingRadius; + + ResTIRGI_SpatialBiasCorrectionMode spatialBiasCorrectionMode;// = ResTIRGI_SpatialBiasCorrectionMode::Basic; + uint32_t pad1; + uint32_t pad2; + uint32_t pad3; +}; + +struct ReSTIRGI_FinalShadingParameters +{ + uint32_t enableFinalVisibility;// = true; + uint32_t enableFinalMIS;// = true; + uint32_t pad1; + uint32_t pad2; +}; + +struct ReSTIRGI_BufferIndices +{ + uint32_t secondarySurfaceReSTIRDIOutputBufferIndex; + uint32_t temporalResamplingInputBufferIndex; + uint32_t temporalResamplingOutputBufferIndex; + uint32_t spatialResamplingInputBufferIndex; + + uint32_t spatialResamplingOutputBufferIndex; + uint32_t finalShadingInputBufferIndex; + uint32_t pad1; + uint32_t pad2; +}; + +struct ReSTIRGI_Parameters +{ + RTXDI_ReservoirBufferParameters reservoirBufferParams; + ReSTIRGI_BufferIndices bufferIndices; + ReSTIRGI_TemporalResamplingParameters temporalResamplingParams; + ReSTIRGI_SpatialResamplingParameters spatialResamplingParams; + ReSTIRGI_FinalShadingParameters finalShadingParams; +}; + +#endif // RTXDI_RESTIRGI_PARAMETERS_H diff --git a/rtxdi-sdk/include/rtxdi/Reservoir.hlsli b/rtxdi-sdk/include/rtxdi/Reservoir.hlsli deleted file mode 100644 index a7d1592..0000000 --- a/rtxdi-sdk/include/rtxdi/Reservoir.hlsli +++ /dev/null @@ -1,239 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. - # - # NVIDIA CORPORATION and its licensors retain all intellectual property - # and proprietary rights in and to this software, related documentation - # and any modifications thereto. Any use, reproduction, disclosure or - # distribution of this software and related documentation without an express - # license agreement from NVIDIA CORPORATION is strictly prohibited. - **************************************************************************/ - -#ifndef RESERVOIR_HLSLI -#define RESERVOIR_HLSLI - -#include "RtxdiParameters.h" -#include "RtxdiHelpers.hlsli" - -#ifndef RTXDI_LIGHT_RESERVOIR_BUFFER -#error "RTXDI_LIGHT_RESERVOIR_BUFFER must be defined to point to a RWStructuredBuffer type resource" -#endif - -// Define this macro to 0 if your shader needs read-only access to the reservoirs, -// to avoid compile errors in the RTXDI_StoreReservoir function -#ifndef RTXDI_ENABLE_STORE_RESERVOIR -#define RTXDI_ENABLE_STORE_RESERVOIR 1 -#endif - -// This structure represents a single light reservoir that stores the weights, the sample ref, -// sample count (M), and visibility for reuse. It can be serialized into RTXDI_PackedReservoir for storage. -struct RTXDI_Reservoir -{ - // Light index (bits 0..30) and validity bit (31) - uint lightData; - - // Sample UV encoded in 16-bit fixed point format - uint uvData; - - // Overloaded: represents RIS weight sum during streaming, - // then reservoir weight (inverse PDF) after FinalizeResampling - float weightSum; - - // Target PDF of the selected sample - float targetPdf; - - // Number of samples considered for this reservoir (pairwise MIS makes this a float) - float M; - - // Visibility information stored in the reservoir for reuse - uint packedVisibility; - - // Screen-space distance between the current location of the reservoir - // and the location where the visibility information was generated, - // minus the motion vectors applied in temporal resampling - int2 spatialDistance; - - // How many frames ago the visibility information was generated - uint age; - - // Cannonical weight when using pairwise MIS (ignored except during pairwise MIS computations) - float canonicalWeight; -}; - -// Encoding helper constants for RTXDI_PackedReservoir.mVisibility -static const uint RTXDI_PackedReservoir_VisibilityMask = 0x3ffff; -static const uint RTXDI_PackedReservoir_VisibilityChannelMax = 0x3f; -static const uint RTXDI_PackedReservoir_VisibilityChannelShift = 6; -static const uint RTXDI_PackedReservoir_MShift = 18; -static const uint RTXDI_PackedReservoir_MaxM = 0x3fff; - -// Encoding helper constants for RTXDI_PackedReservoir.distanceAge -static const uint RTXDI_PackedReservoir_DistanceChannelBits = 8; -static const uint RTXDI_PackedReservoir_DistanceXShift = 0; -static const uint RTXDI_PackedReservoir_DistanceYShift = 8; -static const uint RTXDI_PackedReservoir_AgeShift = 16; -static const uint RTXDI_PackedReservoir_MaxAge = 0xff; -static const uint RTXDI_PackedReservoir_DistanceMask = (1u << RTXDI_PackedReservoir_DistanceChannelBits) - 1; -static const int RTXDI_PackedReservoir_MaxDistance = int((1u << (RTXDI_PackedReservoir_DistanceChannelBits - 1)) - 1); - -// Light index helpers -static const uint RTXDI_Reservoir_LightValidBit = 0x80000000; -static const uint RTXDI_Reservoir_LightIndexMask = 0x7FFFFFFF; - -RTXDI_PackedReservoir RTXDI_PackReservoir(const RTXDI_Reservoir reservoir) -{ - int2 clampedSpatialDistance = clamp(reservoir.spatialDistance, -RTXDI_PackedReservoir_MaxDistance, RTXDI_PackedReservoir_MaxDistance); - uint clampedAge = clamp(reservoir.age, 0, RTXDI_PackedReservoir_MaxAge); - - RTXDI_PackedReservoir data; - data.lightData = reservoir.lightData; - data.uvData = reservoir.uvData; - - data.mVisibility = reservoir.packedVisibility - | (min(uint(reservoir.M), RTXDI_PackedReservoir_MaxM) << RTXDI_PackedReservoir_MShift); - - data.distanceAge = - ((clampedSpatialDistance.x & RTXDI_PackedReservoir_DistanceMask) << RTXDI_PackedReservoir_DistanceXShift) - | ((clampedSpatialDistance.y & RTXDI_PackedReservoir_DistanceMask) << RTXDI_PackedReservoir_DistanceYShift) - | (clampedAge << RTXDI_PackedReservoir_AgeShift); - - data.targetPdf = reservoir.targetPdf; - data.weight = reservoir.weightSum; - - return data; -} - -#if RTXDI_ENABLE_STORE_RESERVOIR -void RTXDI_StoreReservoir( - const RTXDI_Reservoir reservoir, - RTXDI_ResamplingRuntimeParameters params, - uint2 reservoirPosition, - uint reservoirArrayIndex) -{ - uint pointer = RTXDI_ReservoirPositionToPointer(params, reservoirPosition, reservoirArrayIndex); - RTXDI_LIGHT_RESERVOIR_BUFFER[pointer] = RTXDI_PackReservoir(reservoir); -} -#endif // RTXDI_ENABLE_STORE_RESERVOIR - -RTXDI_Reservoir RTXDI_EmptyReservoir() -{ - RTXDI_Reservoir s; - s.lightData = 0; - s.uvData = 0; - s.targetPdf = 0; - s.weightSum = 0; - s.M = 0; - s.packedVisibility = 0; - s.spatialDistance = int2(0, 0); - s.age = 0; - s.canonicalWeight = 0; - return s; -} - -RTXDI_Reservoir RTXDI_UnpackReservoir(RTXDI_PackedReservoir data) -{ - RTXDI_Reservoir res; - res.lightData = data.lightData; - res.uvData = data.uvData; - res.targetPdf = data.targetPdf; - res.weightSum = data.weight; - res.M = (data.mVisibility >> RTXDI_PackedReservoir_MShift) & RTXDI_PackedReservoir_MaxM; - res.packedVisibility = data.mVisibility & RTXDI_PackedReservoir_VisibilityMask; - // Sign extend the shift values - res.spatialDistance.x = int(data.distanceAge << (32 - RTXDI_PackedReservoir_DistanceXShift - RTXDI_PackedReservoir_DistanceChannelBits)) >> (32 - RTXDI_PackedReservoir_DistanceChannelBits); - res.spatialDistance.y = int(data.distanceAge << (32 - RTXDI_PackedReservoir_DistanceYShift - RTXDI_PackedReservoir_DistanceChannelBits)) >> (32 - RTXDI_PackedReservoir_DistanceChannelBits); - res.age = (data.distanceAge >> RTXDI_PackedReservoir_AgeShift) & RTXDI_PackedReservoir_MaxAge; - res.canonicalWeight = 0.0f; - - // Discard reservoirs that have Inf/NaN - if (isinf(res.weightSum) || isnan(res.weightSum)) { - res = RTXDI_EmptyReservoir(); - } - - return res; -} - -RTXDI_Reservoir RTXDI_LoadReservoir( - RTXDI_ResamplingRuntimeParameters params, - uint2 reservoirPosition, - uint reservoirArrayIndex) -{ - uint pointer = RTXDI_ReservoirPositionToPointer(params, reservoirPosition, reservoirArrayIndex); - return RTXDI_UnpackReservoir(RTXDI_LIGHT_RESERVOIR_BUFFER[pointer]); -} - -void RTXDI_StoreVisibilityInReservoir( - inout RTXDI_Reservoir reservoir, - float3 visibility, - bool discardIfInvisible) -{ - reservoir.packedVisibility = uint(saturate(visibility.x) * RTXDI_PackedReservoir_VisibilityChannelMax) - | (uint(saturate(visibility.y) * RTXDI_PackedReservoir_VisibilityChannelMax)) << RTXDI_PackedReservoir_VisibilityChannelShift - | (uint(saturate(visibility.z) * RTXDI_PackedReservoir_VisibilityChannelMax)) << (RTXDI_PackedReservoir_VisibilityChannelShift * 2); - - reservoir.spatialDistance = int2(0, 0); - reservoir.age = 0; - - if (discardIfInvisible && visibility.x == 0 && visibility.y == 0 && visibility.z == 0) - { - // Keep M for correct resampling, remove the actual sample - reservoir.lightData = 0; - reservoir.weightSum = 0; - } -} - -// Structure that groups the parameters for RTXDI_GetReservoirVisibility(...) -// Reusing final visibility reduces the number of high-quality shadow rays needed to shade -// the scene, at the cost of somewhat softer or laggier shadows. -struct RTXDI_VisibilityReuseParameters -{ - // Controls the maximum age of the final visibility term, measured in frames, that can be reused from the - // previous frame(s). Higher values result in better performance. - uint maxAge; - - // Controls the maximum distance in screen space between the current pixel and the pixel that has - // produced the final visibility term. The distance does not include the motion vectors. - // Higher values result in better performance and softer shadows. - float maxDistance; -}; - -bool RTXDI_GetReservoirVisibility( - const RTXDI_Reservoir reservoir, - const RTXDI_VisibilityReuseParameters params, - out float3 o_visibility) -{ - if (reservoir.age > 0 && - reservoir.age <= params.maxAge && - length(float2(reservoir.spatialDistance)) < params.maxDistance) - { - o_visibility.x = float(reservoir.packedVisibility & RTXDI_PackedReservoir_VisibilityChannelMax) / RTXDI_PackedReservoir_VisibilityChannelMax; - o_visibility.y = float((reservoir.packedVisibility >> RTXDI_PackedReservoir_VisibilityChannelShift) & RTXDI_PackedReservoir_VisibilityChannelMax) / RTXDI_PackedReservoir_VisibilityChannelMax; - o_visibility.z = float((reservoir.packedVisibility >> (RTXDI_PackedReservoir_VisibilityChannelShift * 2)) & RTXDI_PackedReservoir_VisibilityChannelMax) / RTXDI_PackedReservoir_VisibilityChannelMax; - - return true; - } - - o_visibility = float3(0, 0, 0); - return false; -} - -bool RTXDI_IsValidReservoir(const RTXDI_Reservoir reservoir) -{ - return reservoir.lightData != 0; -} - -uint RTXDI_GetReservoirLightIndex(const RTXDI_Reservoir reservoir) -{ - return reservoir.lightData & RTXDI_Reservoir_LightIndexMask; -} - -float2 RTXDI_GetReservoirSampleUV(const RTXDI_Reservoir reservoir) -{ - return float2(reservoir.uvData & 0xffff, reservoir.uvData >> 16) / float(0xffff); -} - -float RTXDI_GetReservoirInvPdf(const RTXDI_Reservoir reservoir) -{ - return reservoir.weightSum; -} - -#endif // RESERVOIR_HLSLI diff --git a/rtxdi-sdk/include/rtxdi/RtxdiHelpers.hlsli b/rtxdi-sdk/include/rtxdi/RtxdiHelpers.hlsli index 49ea7e3..bb56787 100644 --- a/rtxdi-sdk/include/rtxdi/RtxdiHelpers.hlsli +++ b/rtxdi-sdk/include/rtxdi/RtxdiHelpers.hlsli @@ -16,40 +16,47 @@ bool RTXDI_IsActiveCheckerboardPixel( uint2 pixelPosition, bool previousFrame, - RTXDI_ResamplingRuntimeParameters params) + uint activeCheckerboardField) { - if (params.activeCheckerboardField == 0) + if (activeCheckerboardField == 0) return true; - return ((pixelPosition.x + pixelPosition.y + int(previousFrame)) & 1) == (params.activeCheckerboardField & 1); + return ((pixelPosition.x + pixelPosition.y + int(previousFrame)) & 1) == (activeCheckerboardField & 1); } -void RTXDI_ActivateCheckerboardPixel(inout int2 pixelPosition, bool previousFrame, RTXDI_ResamplingRuntimeParameters params) +void RTXDI_ActivateCheckerboardPixel(inout uint2 pixelPosition, bool previousFrame, uint activeCheckerboardField) { - if (RTXDI_IsActiveCheckerboardPixel(pixelPosition, previousFrame, params)) + if (RTXDI_IsActiveCheckerboardPixel(pixelPosition, previousFrame, activeCheckerboardField)) return; if (previousFrame) - pixelPosition.x += int(params.activeCheckerboardField) * 2 - 3; + pixelPosition.x += int(activeCheckerboardField) * 2 - 3; else pixelPosition.x += (pixelPosition.y & 1) != 0 ? 1 : -1; } -uint2 RTXDI_PixelPosToReservoirPos(uint2 pixelPosition, RTXDI_ResamplingRuntimeParameters params) +void RTXDI_ActivateCheckerboardPixel(inout int2 pixelPosition, bool previousFrame, uint activeCheckerboardField) { - if (params.activeCheckerboardField == 0) + uint2 uPixelPosition = uint2(pixelPosition); + RTXDI_ActivateCheckerboardPixel(uPixelPosition, previousFrame, activeCheckerboardField); + pixelPosition = int2(uPixelPosition); +} + +uint2 RTXDI_PixelPosToReservoirPos(uint2 pixelPosition, uint activeCheckerboardField) +{ + if (activeCheckerboardField == 0) return pixelPosition; return uint2(pixelPosition.x >> 1, pixelPosition.y); } -uint2 RTXDI_ReservoirPosToPixelPos(uint2 reservoirIndex, RTXDI_ResamplingRuntimeParameters params) +uint2 RTXDI_DIReservoirPosToPixelPos(uint2 reservoirIndex, uint activeCheckerboardField) { - if (params.activeCheckerboardField == 0) + if (activeCheckerboardField == 0) return reservoirIndex; uint2 pixelPosition = uint2(reservoirIndex.x << 1, reservoirIndex.y); - pixelPosition.x += ((pixelPosition.y + params.activeCheckerboardField) & 1); + pixelPosition.x += ((pixelPosition.y + activeCheckerboardField) & 1); return pixelPosition; } @@ -65,243 +72,21 @@ void RTXDI_ApplyPermutationSampling(inout int2 prevPixelPos, uint uniformRandomN prevPixelPos -= offset; } -uint RTXDI_ReservoirPositionToPointer( - RTXDI_ResamplingRuntimeParameters params, +uint RTXDI_DIReservoirPositionToPointer( + RTXDI_ReservoirBufferParameters reservoirParams, uint2 reservoirPosition, uint reservoirArrayIndex) { uint2 blockIdx = reservoirPosition / RTXDI_RESERVOIR_BLOCK_SIZE; uint2 positionInBlock = reservoirPosition % RTXDI_RESERVOIR_BLOCK_SIZE; - return reservoirArrayIndex * params.reservoirArrayPitch - + blockIdx.y * params.reservoirBlockRowPitch + return reservoirArrayIndex * reservoirParams.reservoirArrayPitch + + blockIdx.y * reservoirParams.reservoirBlockRowPitch + blockIdx.x * (RTXDI_RESERVOIR_BLOCK_SIZE * RTXDI_RESERVOIR_BLOCK_SIZE) + positionInBlock.y * RTXDI_RESERVOIR_BLOCK_SIZE + positionInBlock.x; } -#if RTXDI_REGIR_MODE == RTXDI_REGIR_GRID - -float RTXDI_ReGIR_GetJitterScale(RTXDI_ResamplingRuntimeParameters params, float3 worldPos) -{ - return params.regirCommon.samplingJitter * params.regirCommon.cellSize; -} - -int RTXDI_ReGIR_WorldPosToCellIndex(RTXDI_ResamplingRuntimeParameters params, float3 worldPos) -{ - const float3 gridCenter = float3(params.regirCommon.centerX, params.regirCommon.centerY, params.regirCommon.centerZ); - const int3 gridCellCount = int3(params.regirGrid.cellsX, params.regirGrid.cellsY, params.regirGrid.cellsZ); - const float3 gridOrigin = gridCenter - float3(gridCellCount) * (params.regirCommon.cellSize * 0.5); - - int3 gridCell = int3(floor((worldPos - gridOrigin) / params.regirCommon.cellSize)); - - if (gridCell.x < 0 || gridCell.y < 0 || gridCell.z < 0 || - gridCell.x >= gridCellCount.x || gridCell.y >= gridCellCount.y || gridCell.z >= gridCellCount.z) - return -1; - - return gridCell.x + (gridCell.y + (gridCell.z * gridCellCount.y)) * gridCellCount.x; -} - -bool RTXDI_ReGIR_CellIndexToWorldPos(RTXDI_ResamplingRuntimeParameters params, int cellIndex, out float3 cellCenter, out float cellRadius) -{ - const float3 gridCenter = float3(params.regirCommon.centerX, params.regirCommon.centerY, params.regirCommon.centerZ); - const int3 gridCellCount = int3(params.regirGrid.cellsX, params.regirGrid.cellsY, params.regirGrid.cellsZ); - const float3 gridOrigin = gridCenter - float3(gridCellCount) * (params.regirCommon.cellSize * 0.5); - - uint3 cellPosition; - cellPosition.x = cellIndex; - cellPosition.y = cellPosition.x / params.regirGrid.cellsX; - cellPosition.x %= params.regirGrid.cellsX; - cellPosition.z = cellPosition.y / params.regirGrid.cellsY; - cellPosition.y %= params.regirGrid.cellsY; - if (cellPosition.z >= params.regirGrid.cellsZ) - return false; - - cellCenter = (float3(cellPosition) + 0.5) * params.regirCommon.cellSize + gridOrigin; - - cellRadius = params.regirCommon.cellSize * sqrt(3.0); - - return true; -} - -#elif RTXDI_REGIR_MODE == RTXDI_REGIR_ONION - -float RTXDI_ReGIR_GetJitterScale(RTXDI_ResamplingRuntimeParameters params, float3 worldPos) -{ - const float3 onionCenter = float3(params.regirCommon.centerX, params.regirCommon.centerY, params.regirCommon.centerZ); - const float3 translatedPos = worldPos - onionCenter; - - float distanceToCenter = length(translatedPos) / params.regirCommon.cellSize; - float jitterScale = max(1.0, max( - pow(distanceToCenter, 1.0 / 3.0) * params.regirOnion.cubicRootFactor, - distanceToCenter * params.regirOnion.linearFactor - )); - - return jitterScale * params.regirCommon.samplingJitter * params.regirCommon.cellSize; -} - -int RTXDI_ReGIR_WorldPosToCellIndex(RTXDI_ResamplingRuntimeParameters params, float3 worldPos) -{ - const float3 onionCenter = float3(params.regirCommon.centerX, params.regirCommon.centerY, params.regirCommon.centerZ); - const float3 translatedPos = worldPos - onionCenter; - - float r, azimuth, elevation; - RTXDI_CartesianToSpherical(translatedPos, r, azimuth, elevation); - azimuth += RTXDI_PI; // Add PI to make sure azimuth doesn't cross zero - - if (r <= params.regirOnion.layers[0].innerRadius) - return 0; - - RTXDI_OnionLayerGroup layerGroup; - - int layerGroupIndex; - for (layerGroupIndex = 0; layerGroupIndex < params.regirOnion.numLayerGroups; layerGroupIndex++) - { - if (r <= params.regirOnion.layers[layerGroupIndex].outerRadius) - { - layerGroup = params.regirOnion.layers[layerGroupIndex]; - break; - } - } - - if (layerGroupIndex >= params.regirOnion.numLayerGroups) - return -1; - - uint layerIndex = uint(floor(max(0, log(r / layerGroup.innerRadius) * layerGroup.invLogLayerScale))); - layerIndex = min(layerIndex, layerGroup.layerCount - 1); // Guard against numeric errors at the outer shell - - uint ringIndex = uint(floor(abs(elevation) * layerGroup.invEquatorialCellAngle + 0.5)); - RTXDI_OnionRing ring = params.regirOnion.rings[layerGroup.ringOffset + ringIndex]; - - if ((layerIndex & 1) != 0) - { - azimuth -= ring.cellAngle * 0.5; // Add some variation to the repetitive layers - if (azimuth < 0) - azimuth += 2 * RTXDI_PI; - } - - int cellIndex = int(floor(azimuth * ring.invCellAngle)); - - int ringCellOffset = ring.cellOffset; - if (elevation < 0 && ringIndex > 0) - ringCellOffset += ring.cellCount; - - return int(cellIndex + ringCellOffset + layerIndex * layerGroup.cellsPerLayer + layerGroup.layerCellOffset); -} - -bool RTXDI_ReGIR_CellIndexToWorldPos(RTXDI_ResamplingRuntimeParameters params, int cellIndex, out float3 cellCenter, out float cellRadius) -{ - const float3 onionCenter = float3(params.regirCommon.centerX, params.regirCommon.centerY, params.regirCommon.centerZ); - - cellCenter = float3(0, 0, 0); - cellRadius = 0; - - if (cellIndex < 0) - return false; - - if (cellIndex == 0) - { - cellCenter = onionCenter; - cellRadius = params.regirOnion.layers[0].innerRadius; - return true; - } - - RTXDI_OnionLayerGroup layerGroup; - - cellIndex -= 1; - - int layerGroupIndex; - for (layerGroupIndex = 0; layerGroupIndex < params.regirOnion.numLayerGroups; layerGroupIndex++) - { - layerGroup = params.regirOnion.layers[layerGroupIndex]; - int cellsPerGroup = layerGroup.cellsPerLayer * layerGroup.layerCount; - - if (cellIndex < cellsPerGroup) - break; - - cellIndex -= cellsPerGroup; - } - - if (layerGroupIndex >= params.regirOnion.numLayerGroups) - return false; - - int layerIndex = cellIndex / layerGroup.cellsPerLayer; - cellIndex -= layerIndex * layerGroup.cellsPerLayer; - - RTXDI_OnionRing ring; - - int ringIndex; - for (ringIndex = 0; ringIndex < layerGroup.ringCount; ringIndex++) - { - ring = params.regirOnion.rings[layerGroup.ringOffset + ringIndex]; - - if (cellIndex < ring.cellOffset + ring.cellCount * (ringIndex > 0 ? 2 : 1)) - break; - } - - if (ringIndex >= layerGroup.ringCount) - return false; // shouldn't happen - - cellIndex -= ring.cellOffset; - float elevation = float(ringIndex) * layerGroup.equatorialCellAngle; - if (cellIndex >= ring.cellCount) - { - elevation = -elevation; - } - - float azimuth = (float(cellIndex) + 0.5) * ring.cellAngle; - - if ((layerIndex & 1) != 0) - azimuth += ring.cellAngle * 0.5; // Match the variation added in ...WorldPosToCellIndex() - - azimuth -= RTXDI_PI; // Reverse the PI addition in the position -> cell index translation - - float layerInnerRadius = layerGroup.innerRadius * pow(layerGroup.layerScale, layerIndex); - float layerOuterRadius = layerInnerRadius * layerGroup.layerScale; - - float r = (layerInnerRadius + layerOuterRadius) * 0.5; - - cellCenter = RTXDI_SphericalToCartesian(r, azimuth, elevation); - - azimuth += ring.cellAngle * 0.5; - elevation = (elevation == 0) - ? layerGroup.equatorialCellAngle * 0.5 - : (abs(elevation) - layerGroup.equatorialCellAngle * 0.5) * sign(elevation); - - float3 cellCorner = RTXDI_SphericalToCartesian(layerOuterRadius, azimuth, elevation); - - cellRadius = length(cellCorner - cellCenter); - - cellCenter += onionCenter; - - return true; -} - -#endif - -#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED - -float3 RTXDI_VisualizeReGIRCells(RTXDI_ResamplingRuntimeParameters params, float3 worldPos) -{ - int cellIndex = RTXDI_ReGIR_WorldPosToCellIndex(params, worldPos); - - uint cellHash = RTXDI_JenkinsHash(cellIndex); - - float3 cellColor; - cellColor.x = (cellHash & 0x7ff) / float(0x7ff); - cellColor.y = ((cellHash >> 11) & 0x7ff) / float(0x7ff); - cellColor.z = ((cellHash >> 22) & 0x3ff) / float(0x3ff); - - float3 cellCenter; - float cellRadius; - bool cellFound = RTXDI_ReGIR_CellIndexToWorldPos(params, cellIndex, cellCenter, cellRadius); - - float distanceToCenter = cellFound ? saturate(length(cellCenter - worldPos) / cellRadius) : 0; - - return distanceToCenter * cellColor; -} -#endif - #ifdef RTXDI_ENABLE_BOILING_FILTER // RTXDI_BOILING_FILTER_GROUP_SIZE must be defined - 16 is a reasonable value #define RTXDI_BOILING_FILTER_MIN_LANE_COUNT 32 @@ -312,7 +97,6 @@ groupshared uint s_count[(RTXDI_BOILING_FILTER_GROUP_SIZE * RTXDI_BOILING_FILTER bool RTXDI_BoilingFilterInternal( uint2 LocalIndex, float filterStrength, // (0..1] - RTXDI_ResamplingRuntimeParameters params, float reservoirWeight) { // Boiling happens when some highly unlikely light is discovered and it is relevant diff --git a/rtxdi-sdk/include/rtxdi/RtxdiParameters.h b/rtxdi-sdk/include/rtxdi/RtxdiParameters.h index 08979a9..c52ea6e 100644 --- a/rtxdi-sdk/include/rtxdi/RtxdiParameters.h +++ b/rtxdi-sdk/include/rtxdi/RtxdiParameters.h @@ -34,17 +34,17 @@ // Use MIS-like normalization with visibility rays. Unbiased. #define RTXDI_BIAS_CORRECTION_RAY_TRACED 3 - -#define RTXDI_ONION_MAX_LAYER_GROUPS 8 -#define RTXDI_ONION_MAX_RINGS 52 - -#define RTXDI_REGIR_DISABLED 0 -#define RTXDI_REGIR_GRID 1 -#define RTXDI_REGIR_ONION 2 - -#ifndef RTXDI_REGIR_MODE -#define RTXDI_REGIR_MODE RTXDI_REGIR_DISABLED -#endif +// Select local lights with equal probability from the light buffer during initial sampling +#define ReSTIRDI_LocalLightSamplingMode_UNIFORM 0 +// Use power based RIS to select local lights during initial sampling +#define ReSTIRDI_LocalLightSamplingMode_POWER_RIS 1 +// Use ReGIR based RIS to select local lights during initial sampling. +#define ReSTIRDI_LocalLightSamplingMode_REGIR_RIS 2 + +// This macro enables the functions that deal with the RIS buffer and presampling. +#ifndef RTXDI_ENABLE_PRESAMPLING +#define RTXDI_ENABLE_PRESAMPLING 1 +#endif #define RTXDI_INVALID_LIGHT_INDEX (0xffffffffu) @@ -52,124 +52,49 @@ static const uint RTXDI_InvalidLightIndex = RTXDI_INVALID_LIGHT_INDEX; #endif -struct RTXDI_OnionLayerGroup -{ - float innerRadius; - float outerRadius; - float invLogLayerScale; - int layerCount; - - float invEquatorialCellAngle; - int cellsPerLayer; - int ringOffset; - int ringCount; - - float equatorialCellAngle; - float layerScale; - int layerCellOffset; - int pad; -}; - -struct RTXDI_OnionRing -{ - float cellAngle; - float invCellAngle; - int cellOffset; - int cellCount; -}; +#include "ReGIRParameters.h" +#include "RISBufferSegmentParameters.h" -struct RTXDI_ReGIRCommonParameters +struct RTXDI_LightBufferRegion { - uint32_t enable; - float centerX; - float centerY; - float centerZ; - - uint32_t risBufferOffset; - uint32_t lightsPerCell; - float cellSize; - float samplingJitter; -}; - -struct RTXDI_ReGIRGridParameters -{ - uint32_t cellsX; - uint32_t cellsY; - uint32_t cellsZ; - uint32_t pad; -}; - -struct RTXDI_ReGIROnionParameters -{ - RTXDI_OnionLayerGroup layers[RTXDI_ONION_MAX_LAYER_GROUPS]; - RTXDI_OnionRing rings[RTXDI_ONION_MAX_RINGS]; - - uint32_t numLayerGroups; - float cubicRootFactor; - float linearFactor; - float pad; -}; - -struct RTXDI_LocalLightRuntimeParameters -{ - uint32_t firstLocalLight; - uint32_t numLocalLights; - uint32_t enableLocalLightImportanceSampling; + uint32_t firstLightIndex; + uint32_t numLights; uint32_t pad1; + uint32_t pad2; }; -struct RTXDI_InfiniteLightRuntimeParameters +struct RTXDI_EnvironmentLightBufferParameters { - uint32_t firstInfiniteLight; - uint32_t numInfiniteLights; + uint32_t lightPresent; + uint32_t lightIndex; uint32_t pad1; uint32_t pad2; }; -struct RTXDI_EnvironmentLightRuntimeParameters +struct RTXDI_RuntimeParameters { - uint32_t environmentLightPresent; - uint32_t environmentLightIndex; - uint32_t environmentRisBufferOffset; - uint32_t environmentTileSize; - - uint32_t environmentTileCount; + uint32_t neighborOffsetMask; // Spatial + uint32_t activeCheckerboardField; // 0 - no checkerboard, 1 - odd pixels, 2 - even pixels uint32_t pad1; uint32_t pad2; - uint32_t pad3; }; -struct RTXDI_RISBufferRuntimeParameters +struct RTXDI_LightBufferParameters { - uint32_t tileSize; - uint32_t tileCount; - uint32_t pad1; - uint32_t pad2; + RTXDI_LightBufferRegion localLightBufferRegion; + RTXDI_LightBufferRegion infiniteLightBufferRegion; + RTXDI_EnvironmentLightBufferParameters environmentLightParams; }; -struct RTXDI_ResamplingRuntimeParameters +struct RTXDI_ReservoirBufferParameters { - RTXDI_LocalLightRuntimeParameters localLightParams; - RTXDI_InfiniteLightRuntimeParameters infiniteLightParams; - RTXDI_EnvironmentLightRuntimeParameters environmentLightParams; - RTXDI_RISBufferRuntimeParameters risBufferParams; - - uint32_t neighborOffsetMask; - uint32_t uniformRandomNumber; - uint32_t activeCheckerboardField; // 0 - no checkerboard, 1 - odd pixels, 2 - even pixels uint32_t reservoirBlockRowPitch; - uint32_t reservoirArrayPitch; uint32_t pad1; uint32_t pad2; - uint32_t pad3; - - RTXDI_ReGIRCommonParameters regirCommon; - RTXDI_ReGIRGridParameters regirGrid; - RTXDI_ReGIROnionParameters regirOnion; }; -struct RTXDI_PackedReservoir +struct RTXDI_PackedDIReservoir { uint32_t lightData; uint32_t uvData; @@ -179,19 +104,4 @@ struct RTXDI_PackedReservoir float weight; }; -struct RTXDI_PackedGIReservoir -{ -#ifdef __cplusplus - using float3 = float[3]; -#endif - - float3 position; - uint32_t packed_miscData_age_M; // See Reservoir.hlsli about the detail of the bit field. - - uint32_t packed_radiance; // Stored as 32bit LogLUV format. - float weight; - uint32_t packed_normal; // Stored as 2x 16-bit snorms in the octahedral mapping - float unused; -}; - #endif // RTXDI_PARAMETERS_H diff --git a/rtxdi-sdk/include/rtxdi/RtxdiTypes.h b/rtxdi-sdk/include/rtxdi/RtxdiTypes.h index a4e4a3c..f6af697 100644 --- a/rtxdi-sdk/include/rtxdi/RtxdiTypes.h +++ b/rtxdi-sdk/include/rtxdi/RtxdiTypes.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/rtxdi-sdk/include/rtxdi/RtxdiUtils.h b/rtxdi-sdk/include/rtxdi/RtxdiUtils.h new file mode 100644 index 0000000..0cae70c --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/RtxdiUtils.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include + +namespace rtxdi +{ + +// Checkerboard sampling modes match those used in NRD, based on frameIndex: +// Even frame(0) Odd frame(1) ... +// B W W B +// W B B W +// BLACK and WHITE modes define cells with VALID data +enum class CheckerboardMode : uint32_t +{ + Off = 0, + Black = 1, + White = 2 +}; + +RTXDI_ReservoirBufferParameters CalculateReservoirBufferParameters(uint32_t renderWidth, uint32_t renderHeight, CheckerboardMode checkerboardMode); + +void ComputePdfTextureSize(uint32_t maxItems, uint32_t& outWidth, uint32_t& outHeight, uint32_t& outMipLevels); + +void FillNeighborOffsetBuffer(uint8_t* buffer, uint32_t neighborOffsetCount); + +// 32 bit Jenkins hash +uint32_t JenkinsHash(uint32_t a); + +} \ No newline at end of file diff --git a/rtxdi-sdk/include/rtxdi/UniformSampling.hlsli b/rtxdi-sdk/include/rtxdi/UniformSampling.hlsli new file mode 100644 index 0000000..793044a --- /dev/null +++ b/rtxdi-sdk/include/rtxdi/UniformSampling.hlsli @@ -0,0 +1,27 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef RTXDI_UNIFORM_SAMPLING +#define RTXDI_UNIFORM_SAMPLING + +void RTXDI_RandomlySelectLightUniformly( + inout RAB_RandomSamplerState rng, + RTXDI_LightBufferRegion region, + out RAB_LightInfo lightInfo, + out uint lightIndex, + out float invSourcePdf) +{ + float rnd = RAB_GetNextRandom(rng); + invSourcePdf = float(region.numLights); + lightIndex = region.firstLightIndex + min(uint(floor(rnd * region.numLights)), region.numLights - 1); + lightInfo = RAB_LoadLightInfo(lightIndex, false); +} + +#endif // RTXDI_UNIFORM_SAMPLING \ No newline at end of file diff --git a/rtxdi-sdk/shaders/ResamplingCompileTest.glsl b/rtxdi-sdk/shaders/ResamplingCompileTest.glsl index 4c4722c..faa43f5 100644 --- a/rtxdi-sdk/shaders/ResamplingCompileTest.glsl +++ b/rtxdi-sdk/shaders/ResamplingCompileTest.glsl @@ -21,7 +21,8 @@ #define RTXDI_GLSL #define RTXDI_REGIR_MODE RTXDI_REGIR_ONION -#include "rtxdi/RtxdiParameters.h" +#include "rtxdi/ReSTIRDIParameters.h" +#include "rtxdi/ReSTIRGIParameters.h" struct RAB_RandomSamplerState { @@ -154,7 +155,7 @@ int RAB_TranslateLightIndex(uint lightIndex, bool currentToPrevious) return -1; } -float RAB_EvaluateLocalLightSourcePdf(RTXDI_ResamplingRuntimeParameters params, uint lightIndex) +float RAB_EvaluateLocalLightSourcePdf(uint lightIndex) { return 0.0; } @@ -218,7 +219,7 @@ layout(set = 0, binding = 0) buffer RIS_BUFFER { }; layout(set = 0, binding = 1) buffer LIGHT_RESERVOIR_BUFFER { - RTXDI_PackedReservoir u_LightReservoirs[]; + RTXDI_PackedDIReservoir u_LightReservoirs[]; }; layout(set = 0, binding = 2) readonly buffer NEIGHBOR_OFFSET_BUFFER { @@ -234,7 +235,9 @@ layout(set = 0, binding = 3) buffer GI_RESERVOIR_BUFFER { #define RTXDI_NEIGHBOR_OFFSETS_BUFFER t_NeighborOffsets #define RTXDI_GI_RESERVOIR_BUFFER u_GIReservoirs -#include "rtxdi/ResamplingFunctions.hlsli" +#include "rtxdi/PresamplingFunctions.hlsli" +#include "rtxdi/InitialSamplingFunctions.hlsli" +#include "rtxdi/DIResamplingFunctions.hlsli" layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; diff --git a/rtxdi-sdk/shaders/ResamplingCompileTest.hlsl b/rtxdi-sdk/shaders/ResamplingCompileTest.hlsl index 954fba3..704f861 100644 --- a/rtxdi-sdk/shaders/ResamplingCompileTest.hlsl +++ b/rtxdi-sdk/shaders/ResamplingCompileTest.hlsl @@ -12,7 +12,8 @@ // that they do not make any undeclared assumptions about the contents of the // user-defined structures and about the functions being available. -#include +#include +#include struct RAB_RandomSamplerState { @@ -53,6 +54,7 @@ void RAB_GetLightDirDistance(RAB_Surface surface, RAB_LightSample lightSample, out float3 o_lightDir, out float o_lightDistance) { + o_lightDistance = 0.f; } bool RAB_GetConservativeVisibility(RAB_Surface surface, RAB_LightSample lightSample) @@ -145,7 +147,7 @@ int RAB_TranslateLightIndex(uint lightIndex, bool currentToPrevious) return -1; } -float RAB_EvaluateLocalLightSourcePdf(RTXDI_ResamplingRuntimeParameters params, uint lightIndex) +float RAB_EvaluateLocalLightSourcePdf(uint lightIndex) { return 0.0; } @@ -172,6 +174,7 @@ float2 RAB_GetEnvironmentMapRandXYFromDir(float3 worldDir) bool RAB_TraceRayForLocalLight(float3 origin, float3 direction, float tMin, float tMax, out uint o_lightIndex, out float2 o_randXY) { + o_lightIndex = 0; return false; } @@ -204,7 +207,7 @@ bool RAB_GetTemporalConservativeVisibility(RAB_Surface currentSurface, RAB_Surfa #define RTXDI_BOILING_FILTER_GROUP_SIZE 16 RWBuffer u_RisBuffer; -RWStructuredBuffer u_LightReservoirs; +RWStructuredBuffer u_LightReservoirs; RWStructuredBuffer u_GIReservoirs; Buffer t_NeighborOffsets; @@ -213,7 +216,9 @@ Buffer t_NeighborOffsets; #define RTXDI_GI_RESERVOIR_BUFFER u_GIReservoirs #define RTXDI_NEIGHBOR_OFFSETS_BUFFER t_NeighborOffsets -#include +#include +#include +#include [numthreads(1, 1, 1)] void main() diff --git a/rtxdi-sdk/src/ImportanceSamplingContext.cpp b/rtxdi-sdk/src/ImportanceSamplingContext.cpp new file mode 100644 index 0000000..1771d35 --- /dev/null +++ b/rtxdi-sdk/src/ImportanceSamplingContext.cpp @@ -0,0 +1,155 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#include "rtxdi/ImportanceSamplingContext.h" + +#include + +#include "rtxdi/RISBufferSegmentAllocator.h" +#include "rtxdi/ReSTIRDI.h" +#include "rtxdi/ReGIR.h" +#include "rtxdi/ReSTIRGI.h" + +namespace +{ + +bool IsNonzeroPowerOf2(uint32_t i) +{ + return ((i & (i - 1)) == 0) && (i > 0); +} + +void debugCheckParameters(const rtxdi::RISBufferSegmentParameters& localLightRISBufferParams, + const rtxdi::RISBufferSegmentParameters& environmentLightRISBufferParams) +{ + assert(IsNonzeroPowerOf2(localLightRISBufferParams.tileSize)); + assert(IsNonzeroPowerOf2(localLightRISBufferParams.tileCount)); + assert(IsNonzeroPowerOf2(environmentLightRISBufferParams.tileSize)); + assert(IsNonzeroPowerOf2(environmentLightRISBufferParams.tileCount)); +} + +} + +namespace rtxdi +{ + +ImportanceSamplingContext::ImportanceSamplingContext(const ImportanceSamplingContext_StaticParameters& isParams) +{ + debugCheckParameters(isParams.localLightRISBufferParams, isParams.environmentLightRISBufferParams); + + m_risBufferSegmentAllocator = std::make_unique(); + m_localLightRISBufferSegmentParams.bufferOffset = m_risBufferSegmentAllocator->allocateSegment(isParams.localLightRISBufferParams.tileCount * isParams.localLightRISBufferParams.tileSize); + m_localLightRISBufferSegmentParams.tileCount = isParams.localLightRISBufferParams.tileCount; + m_localLightRISBufferSegmentParams.tileSize = isParams.localLightRISBufferParams.tileSize; + m_environmentLightRISBufferSegmentParams.bufferOffset = m_risBufferSegmentAllocator->allocateSegment(isParams.environmentLightRISBufferParams.tileCount * isParams.environmentLightRISBufferParams.tileSize); + m_environmentLightRISBufferSegmentParams.tileCount = isParams.environmentLightRISBufferParams.tileCount; + m_environmentLightRISBufferSegmentParams.tileSize = isParams.environmentLightRISBufferParams.tileSize; + + ReSTIRDIStaticParameters restirDIStaticParams; + restirDIStaticParams.CheckerboardSamplingMode = isParams.CheckerboardSamplingMode; + restirDIStaticParams.NeighborOffsetCount = isParams.NeighborOffsetCount; + restirDIStaticParams.RenderWidth = isParams.renderWidth; + restirDIStaticParams.RenderHeight = isParams.renderHeight; + m_restirDIContext = std::make_unique(restirDIStaticParams); + + m_regirContext = std::make_unique(isParams.regirStaticParams, *m_risBufferSegmentAllocator); + + ReSTIRGIStaticParameters restirGIStaticParams; + restirGIStaticParams.CheckerboardSamplingMode = isParams.CheckerboardSamplingMode; + restirGIStaticParams.RenderWidth = isParams.renderWidth; + restirGIStaticParams.RenderHeight = isParams.renderHeight; + m_restirGIContext = std::make_unique(restirGIStaticParams); +} + +ImportanceSamplingContext::~ImportanceSamplingContext() +{ + +} + +ReSTIRDIContext& ImportanceSamplingContext::getReSTIRDIContext() +{ + return *m_restirDIContext; +} + +const ReSTIRDIContext& ImportanceSamplingContext::getReSTIRDIContext() const +{ + return *m_restirDIContext; +} + +ReGIRContext& ImportanceSamplingContext::getReGIRContext() +{ + return *m_regirContext; +} + +const ReGIRContext& ImportanceSamplingContext::getReGIRContext() const +{ + return *m_regirContext; +} + +ReSTIRGIContext& ImportanceSamplingContext::getReSTIRGIContext() +{ + return *m_restirGIContext; +} + +const ReSTIRGIContext& ImportanceSamplingContext::getReSTIRGIContext() const +{ + return *m_restirGIContext; +} + +const RISBufferSegmentAllocator& ImportanceSamplingContext::getRISBufferSegmentAllocator() const +{ + return *m_risBufferSegmentAllocator; +} + +const RTXDI_LightBufferParameters& ImportanceSamplingContext::getLightBufferParameters() const +{ + return m_lightBufferParams; +} + +const RTXDI_RISBufferSegmentParameters& ImportanceSamplingContext::getLocalLightRISBufferSegmentParams() const +{ + return m_localLightRISBufferSegmentParams; +} + +const RTXDI_RISBufferSegmentParameters& ImportanceSamplingContext::getEnvironmentLightRISBufferSegmentParams() const +{ + return m_environmentLightRISBufferSegmentParams; +} + +uint32_t ImportanceSamplingContext::getNeighborOffsetCount() const +{ + return m_restirDIContext->getStaticParameters().NeighborOffsetCount; +} + +bool ImportanceSamplingContext::isLocalLightPowerRISEnabled() const +{ + bool enabled = false; + ReSTIRDI_InitialSamplingParameters iss = m_restirDIContext->getInitialSamplingParameters(); + if (iss.localLightSamplingMode == ReSTIRDI_LocalLightSamplingMode::Power_RIS) + return true; + if (iss.localLightSamplingMode == ReSTIRDI_LocalLightSamplingMode::ReGIR_RIS) + { + if( (m_regirContext->getReGIRDynamicParameters().presamplingMode == LocalLightReGIRPresamplingMode::Power_RIS) || + (m_regirContext->getReGIRDynamicParameters().fallbackSamplingMode == LocalLightReGIRFallbackSamplingMode::Power_RIS)) + return true; + } + return false; +} + +bool ImportanceSamplingContext::isReGIREnabled() const +{ + return (m_restirDIContext->getInitialSamplingParameters().localLightSamplingMode == ReSTIRDI_LocalLightSamplingMode::ReGIR_RIS); +} + +void ImportanceSamplingContext::setLightBufferParams(const RTXDI_LightBufferParameters& lightBufferParams) +{ + m_lightBufferParams = lightBufferParams; +} + +} \ No newline at end of file diff --git a/rtxdi-sdk/src/RISBufferSegmentAllocator.cpp b/rtxdi-sdk/src/RISBufferSegmentAllocator.cpp new file mode 100644 index 0000000..4fc7200 --- /dev/null +++ b/rtxdi-sdk/src/RISBufferSegmentAllocator.cpp @@ -0,0 +1,34 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#include "rtxdi/RISBufferSegmentAllocator.h" + +namespace rtxdi +{ + +RISBufferSegmentAllocator::RISBufferSegmentAllocator() : + m_totalSizeInElements(0) +{ + +} + +uint32_t RISBufferSegmentAllocator::allocateSegment(uint32_t sizeInElements) +{ + uint32_t prevSize = m_totalSizeInElements; + m_totalSizeInElements += sizeInElements; + return prevSize; +} + +uint32_t RISBufferSegmentAllocator::getTotalSizeInElements() const +{ + return m_totalSizeInElements; +} + +} \ No newline at end of file diff --git a/rtxdi-sdk/src/RTXDI.cpp b/rtxdi-sdk/src/RTXDI.cpp deleted file mode 100644 index 3a3389a..0000000 --- a/rtxdi-sdk/src/RTXDI.cpp +++ /dev/null @@ -1,358 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. - # - # NVIDIA CORPORATION and its licensors retain all intellectual property - # and proprietary rights in and to this software, related documentation - # and any modifications thereto. Any use, reproduction, disclosure or - # distribution of this software and related documentation without an express - # license agreement from NVIDIA CORPORATION is strictly prohibited. - **************************************************************************/ - -#include -#include -#include -#include -#include -#include - -#define PRINT_JITTER_CURVE 0 - -#if PRINT_JITTER_CURVE -#define NOMINMAX -#include -#endif - -using namespace rtxdi; - -constexpr float c_pi = 3.1415926535f; - -static bool IsNonzeroPowerOf2(uint32_t i) -{ - return ((i & (i - 1)) == 0) && (i > 0); -} - -rtxdi::Context::Context(const ContextParameters& params) - : m_Params(params) -{ - assert(IsNonzeroPowerOf2(params.TileSize)); - assert(IsNonzeroPowerOf2(params.TileCount)); - assert(params.RenderWidth > 0); - assert(params.RenderHeight > 0); - - uint32_t renderWidth = (params.CheckerboardSamplingMode == CheckerboardMode::Off) - ? params.RenderWidth - : (params.RenderWidth + 1) / 2; - uint32_t renderWidthBlocks = (renderWidth + RTXDI_RESERVOIR_BLOCK_SIZE - 1) / RTXDI_RESERVOIR_BLOCK_SIZE; - uint32_t renderHeightBlocks = (params.RenderHeight + RTXDI_RESERVOIR_BLOCK_SIZE - 1) / RTXDI_RESERVOIR_BLOCK_SIZE; - m_ReservoirBlockRowPitch = renderWidthBlocks * (RTXDI_RESERVOIR_BLOCK_SIZE * RTXDI_RESERVOIR_BLOCK_SIZE); - m_ReservoirArrayPitch = m_ReservoirBlockRowPitch * renderHeightBlocks; - - m_RegirCellOffset = m_Params.TileCount * m_Params.TileSize; - - InitializeOnion(); - ComputeOnionJitterCurve(); -} - -void Context::InitializeOnion() -{ - int numLayerGroups = std::max(1, std::min(RTXDI_ONION_MAX_LAYER_GROUPS, int(m_Params.ReGIR.OnionDetailLayers))); - - float innerRadius = 1.f; - - int totalLayers = 0; - int totalCells = 1; - - for (int layerGroupIndex = 0; layerGroupIndex < numLayerGroups; layerGroupIndex++) - { - const int partitions = layerGroupIndex * 4 + 8; - const int layerCount = (layerGroupIndex < numLayerGroups - 1) ? 1 : int(m_Params.ReGIR.OnionCoverageLayers) + 1; - - const float radiusRatio = (float(partitions) + c_pi) / (float(partitions) - c_pi); - const float outerRadius = innerRadius * powf(radiusRatio, float(layerCount)); - const float equatorialAngle = 2 * c_pi / float(partitions); - - RTXDI_OnionLayerGroup layerGroup{}; - layerGroup.ringOffset = int(m_OnionRings.size()); - layerGroup.innerRadius = innerRadius; - layerGroup.outerRadius = outerRadius; - layerGroup.invLogLayerScale = 1.f / logf(radiusRatio); - layerGroup.invEquatorialCellAngle = 1.f / equatorialAngle; - layerGroup.equatorialCellAngle = equatorialAngle; - layerGroup.ringCount = partitions / 4 + 1; - layerGroup.layerScale = radiusRatio; - layerGroup.layerCellOffset = totalCells; - - RTXDI_OnionRing ring{}; - ring.cellCount = partitions; - ring.cellOffset = 0; - ring.invCellAngle = float(partitions) / (2 * c_pi); - ring.cellAngle = 1.f / ring.invCellAngle; - m_OnionRings.push_back(ring); - - int cellsPerLayer = partitions; - for (int ringIndex = 1; ringIndex < layerGroup.ringCount; ringIndex++) - { - ring.cellCount = std::max(1, int(floorf(float(partitions) * cosf(float(ringIndex) * equatorialAngle)))); - ring.cellOffset = cellsPerLayer; - ring.invCellAngle = float(ring.cellCount) / (2 * c_pi); - ring.cellAngle = 1.f / ring.invCellAngle; - m_OnionRings.push_back(ring); - - cellsPerLayer += ring.cellCount * 2; - } - - layerGroup.cellsPerLayer = cellsPerLayer; - layerGroup.layerCount = layerCount; - m_OnionLayers.push_back(layerGroup); - - innerRadius = outerRadius; - - totalCells += cellsPerLayer * layerCount; - totalLayers += layerCount; - } - - m_OnionCells = totalCells; -} - -static float3 SphericalToCartesian(const float radius, const float azimuth, const float elevation) -{ - return float3{ - radius * cosf(azimuth) * cosf(elevation), - radius * sinf(elevation), - radius * sinf(azimuth) * cosf(elevation) - }; -} - -static float Distance(const float3& a, const float3& b) -{ - float3 d{ a.x - b.x, a.y - b.y, a.z - b.z }; - return sqrtf(d.x * d.x + d.y * d.y + d.z * d.z); -} - -void Context::ComputeOnionJitterCurve() -{ - std::vector cubicRootFactors; - std::vector linearFactors; - - int layerGroupIndex = 0; - for (const auto& layerGroup : m_OnionLayers) - { - for (int layerIndex = 0; layerIndex < layerGroup.layerCount; layerIndex++) - { - const float innerRadius = layerGroup.innerRadius * powf(layerGroup.layerScale, float(layerIndex)); - const float outerRadius = innerRadius * layerGroup.layerScale; - const float middleRadius = (innerRadius + outerRadius) * 0.5f; - float maxCellRadius = 0.f; - - for (int ringIndex = 0; ringIndex < layerGroup.ringCount; ringIndex++) - { - const auto& ring = m_OnionRings[layerGroup.ringOffset + ringIndex]; - - const float middleElevation = layerGroup.equatorialCellAngle * float(ringIndex); - const float vertexElevation = (ringIndex == 0) - ? layerGroup.equatorialCellAngle * 0.5f - : middleElevation - layerGroup.equatorialCellAngle * 0.5f; - - const float middleAzimuth = 0.f; - const float vertexAzimuth = ring.cellAngle; - - const float3 middlePoint = SphericalToCartesian(middleRadius, middleAzimuth, middleElevation); - const float3 vertexPoint = SphericalToCartesian(outerRadius, vertexAzimuth, vertexElevation); - - const float cellRadius = Distance(middlePoint, vertexPoint); - - maxCellRadius = std::max(maxCellRadius, cellRadius); - } - -#if PRINT_JITTER_CURVE - char buf[256]; - sprintf_s(buf, "%.3f,%.3f\n", middleRadius, maxCellRadius); - OutputDebugStringA(buf); -#endif - - if (layerGroupIndex < int(m_OnionLayers.size()) - 1) - { - float cubicRootFactor = maxCellRadius * powf(middleRadius, -1.f / 3.f); - cubicRootFactors.push_back(cubicRootFactor); - } - else - { - float linearFactor = maxCellRadius / middleRadius; - linearFactors.push_back(linearFactor); - } - } - - layerGroupIndex++; - } - - // Compute the median of the cubic root factors, there are some outliers in the curve - if (!cubicRootFactors.empty()) - { - std::sort(cubicRootFactors.begin(), cubicRootFactors.end()); - m_OnionCubicRootFactor = cubicRootFactors[cubicRootFactors.size() / 2]; - } - else - { - m_OnionCubicRootFactor = 0.f; - } - - // Compute the average of the linear factors, they're all the same anyway - float sumOfLinearFactors = std::accumulate(linearFactors.begin(), linearFactors.end(), 0.f); - m_OnionLinearFactor = sumOfLinearFactors / std::max(float(linearFactors.size()), 1.f); -} - -const rtxdi::ContextParameters& rtxdi::Context::GetParameters() const -{ - return m_Params; -} - -uint32_t rtxdi::Context::GetRisBufferElementCount() const -{ - uint32_t size = 0; - size += m_Params.TileCount * m_Params.TileSize; - size += m_Params.EnvironmentTileCount * m_Params.EnvironmentTileSize; - size += GetReGIRLightSlotCount(); - - return size; -} - -uint32_t rtxdi::Context::GetReGIRLightSlotCount() const -{ - switch (m_Params.ReGIR.Mode) - { - case ReGIRMode::Disabled: - return 0; - - case ReGIRMode::Grid: - return m_Params.ReGIR.GridSize.x - * m_Params.ReGIR.GridSize.y - * m_Params.ReGIR.GridSize.z - * m_Params.ReGIR.LightsPerCell; - - case ReGIRMode::Onion: - return m_OnionCells * m_Params.ReGIR.LightsPerCell; - } - - return 0; -} - -uint32_t rtxdi::Context::GetReservoirBufferElementCount() const -{ - return m_ReservoirArrayPitch; -} - -// 32 bit Jenkins hash -static uint32_t JenkinsHash(uint32_t a) -{ - // http://burtleburtle.net/bob/hash/integer.html - a = (a + 0x7ed55d16) + (a << 12); - a = (a ^ 0xc761c23c) ^ (a >> 19); - a = (a + 0x165667b1) + (a << 5); - a = (a + 0xd3a2646c) ^ (a << 9); - a = (a + 0xfd7046c5) + (a << 3); - a = (a ^ 0xb55a4f09) ^ (a >> 16); - return a; -} - -void rtxdi::Context::FillRuntimeParameters( - RTXDI_ResamplingRuntimeParameters& runtimeParams, - const FrameParameters& frame) const -{ - runtimeParams.localLightParams.firstLocalLight = frame.firstLocalLight; - runtimeParams.localLightParams.numLocalLights = frame.numLocalLights; - runtimeParams.infiniteLightParams.firstInfiniteLight = frame.firstInfiniteLight; - runtimeParams.infiniteLightParams.numInfiniteLights = frame.numInfiniteLights; - runtimeParams.environmentLightParams.environmentLightPresent = frame.environmentLightPresent; - runtimeParams.environmentLightParams.environmentLightIndex = frame.environmentLightIndex; - runtimeParams.neighborOffsetMask = m_Params.NeighborOffsetCount - 1; - runtimeParams.risBufferParams.tileSize = m_Params.TileSize; - runtimeParams.risBufferParams.tileCount = m_Params.TileCount; - runtimeParams.localLightParams.enableLocalLightImportanceSampling = frame.enableLocalLightImportanceSampling; - runtimeParams.reservoirBlockRowPitch = m_ReservoirBlockRowPitch; - runtimeParams.reservoirArrayPitch = m_ReservoirArrayPitch; - runtimeParams.environmentLightParams.environmentRisBufferOffset = m_RegirCellOffset + GetReGIRLightSlotCount(); - runtimeParams.environmentLightParams.environmentTileCount = m_Params.EnvironmentTileCount; - runtimeParams.environmentLightParams.environmentTileSize = m_Params.EnvironmentTileSize; - runtimeParams.regirGrid.cellsX = m_Params.ReGIR.GridSize.x; - runtimeParams.regirGrid.cellsY = m_Params.ReGIR.GridSize.y; - runtimeParams.regirGrid.cellsZ = m_Params.ReGIR.GridSize.z; - runtimeParams.regirCommon.risBufferOffset = m_RegirCellOffset; - runtimeParams.regirCommon.lightsPerCell = m_Params.ReGIR.LightsPerCell; - runtimeParams.regirCommon.centerX = frame.regirCenter.x; - runtimeParams.regirCommon.centerY = frame.regirCenter.y; - runtimeParams.regirCommon.centerZ = frame.regirCenter.z; - runtimeParams.regirCommon.cellSize = (m_Params.ReGIR.Mode == ReGIRMode::Onion) - ? frame.regirCellSize * 0.5f // Onion operates with radii, while "size" feels more like diameter - : frame.regirCellSize; - runtimeParams.regirCommon.enable = m_Params.ReGIR.Mode != ReGIRMode::Disabled; - runtimeParams.regirCommon.samplingJitter = std::max(0.f, frame.regirSamplingJitter * 2.f); - runtimeParams.regirOnion.cubicRootFactor = m_OnionCubicRootFactor; - runtimeParams.regirOnion.linearFactor = m_OnionLinearFactor; - runtimeParams.regirOnion.numLayerGroups = uint32_t(m_OnionLayers.size()); - runtimeParams.uniformRandomNumber = JenkinsHash(frame.frameIndex); - - switch (m_Params.CheckerboardSamplingMode) - { - case CheckerboardMode::Black: - runtimeParams.activeCheckerboardField = (frame.frameIndex & 1) ? 1 : 2; - break; - case CheckerboardMode::White: - runtimeParams.activeCheckerboardField = (frame.frameIndex & 1) ? 2 : 1; - break; - default: - runtimeParams.activeCheckerboardField = 0; - } - - assert(m_OnionLayers.size() <= RTXDI_ONION_MAX_LAYER_GROUPS); - for(int group = 0; group < int(m_OnionLayers.size()); group++) - { - runtimeParams.regirOnion.layers[group] = m_OnionLayers[group]; - runtimeParams.regirOnion.layers[group].innerRadius *= runtimeParams.regirCommon.cellSize; - runtimeParams.regirOnion.layers[group].outerRadius *= runtimeParams.regirCommon.cellSize; - } - - assert(m_OnionRings.size() <= RTXDI_ONION_MAX_RINGS); - for (int n = 0; n < int(m_OnionRings.size()); n++) - { - runtimeParams.regirOnion.rings[n] = m_OnionRings[n]; - } -} - -void rtxdi::Context::FillNeighborOffsetBuffer(uint8_t* buffer) const -{ - // Create a sequence of low-discrepancy samples within a unit radius around the origin - // for "randomly" sampling neighbors during spatial resampling - - int R = 250; - const float phi2 = 1.0f / 1.3247179572447f; - uint32_t num = 0; - float u = 0.5f; - float v = 0.5f; - while (num < m_Params.NeighborOffsetCount * 2) { - u += phi2; - v += phi2 * phi2; - if (u >= 1.0f) u -= 1.0f; - if (v >= 1.0f) v -= 1.0f; - - float rSq = (u - 0.5f) * (u - 0.5f) + (v - 0.5f) * (v - 0.5f); - if (rSq > 0.25f) - continue; - - buffer[num++] = int8_t((u - 0.5f) * R); - buffer[num++] = int8_t((v - 0.5f) * R); - } -} - -void rtxdi::ComputePdfTextureSize(uint32_t maxItems, uint32_t& outWidth, uint32_t& outHeight, uint32_t& outMipLevels) -{ - // Compute the size of a power-of-2 rectangle that fits all items, 1 item per pixel - double textureWidth = std::max(1.0, ceil(sqrt(double(maxItems)))); - textureWidth = exp2(ceil(log2(textureWidth))); - double textureHeight = std::max(1.0, ceil(maxItems / textureWidth)); - textureHeight = exp2(ceil(log2(textureHeight))); - double textureMips = std::max(1.0, log2(std::max(textureWidth, textureHeight)) + 1.0); - - outWidth = uint32_t(textureWidth); - outHeight = uint32_t(textureHeight); - outMipLevels = uint32_t(textureMips); -} diff --git a/rtxdi-sdk/src/ReGIR.cpp b/rtxdi-sdk/src/ReGIR.cpp new file mode 100644 index 0000000..d1135f1 --- /dev/null +++ b/rtxdi-sdk/src/ReGIR.cpp @@ -0,0 +1,264 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#include "rtxdi/ReGIR.h" + +#include +#include +#include + +#include "rtxdi/RtxdiParameters.h" +#include "rtxdi/RISBufferSegmentAllocator.h" + +namespace +{ + constexpr float c_pi = 3.1415926535f; +} + +namespace rtxdi +{ + + ReGIRContext::ReGIRContext(const ReGIRStaticParameters& params, RISBufferSegmentAllocator& risBufferSegmentAllocator) : + m_regirCellOffset(0), + m_regirStaticParameters(params) + { + ComputeGridLightSlotCount(); + InitializeOnion(params); + ComputeOnionJitterCurve(); + AllocateRISBufferSegment(risBufferSegmentAllocator); + } + + void ReGIRContext::ComputeGridLightSlotCount() + { + m_regirGridCalculatedParameters.lightSlotCount = m_regirStaticParameters.gridParameters.GridSize.x + * m_regirStaticParameters.gridParameters.GridSize.y + * m_regirStaticParameters.gridParameters.GridSize.z + * m_regirStaticParameters.LightsPerCell; + } + + void ReGIRContext::AllocateRISBufferSegment(RISBufferSegmentAllocator& risBufferSegmentAllocator) + { + switch (m_regirStaticParameters.Mode) + { + default: + case ReGIRMode::Disabled: + m_regirCellOffset = 0; + break; + case ReGIRMode::Grid: + m_regirCellOffset = risBufferSegmentAllocator.allocateSegment(m_regirGridCalculatedParameters.lightSlotCount); + break; + case ReGIRMode::Onion: + m_regirCellOffset = risBufferSegmentAllocator.allocateSegment(m_regirOnionCalculatedParameters.lightSlotCount); + } + } + + void ReGIRContext::InitializeOnion(const ReGIRStaticParameters& params) + { + int numLayerGroups = std::max(1, std::min(RTXDI_ONION_MAX_LAYER_GROUPS, int(params.onionParameters.OnionDetailLayers))); + + float innerRadius = 1.f; + + int totalLayers = 0; + int totalCells = 1; + + for (int layerGroupIndex = 0; layerGroupIndex < numLayerGroups; layerGroupIndex++) + { + const int partitions = layerGroupIndex * 4 + 8; + const int layerCount = (layerGroupIndex < numLayerGroups - 1) ? 1 : int(params.onionParameters.OnionCoverageLayers) + 1; + + const float radiusRatio = (float(partitions) + c_pi) / (float(partitions) - c_pi); + const float outerRadius = innerRadius * powf(radiusRatio, float(layerCount)); + const float equatorialAngle = 2 * c_pi / float(partitions); + + ReGIR_OnionLayerGroup layerGroup{}; + layerGroup.ringOffset = int(m_regirOnionCalculatedParameters.regirOnionRings.size()); + layerGroup.innerRadius = innerRadius; + layerGroup.outerRadius = outerRadius; + layerGroup.invLogLayerScale = 1.f / logf(radiusRatio); + layerGroup.invEquatorialCellAngle = 1.f / equatorialAngle; + layerGroup.equatorialCellAngle = equatorialAngle; + layerGroup.ringCount = partitions / 4 + 1; + layerGroup.layerScale = radiusRatio; + layerGroup.layerCellOffset = totalCells; + + ReGIR_OnionRing ring{}; + ring.cellCount = partitions; + ring.cellOffset = 0; + ring.invCellAngle = float(partitions) / (2 * c_pi); + ring.cellAngle = 1.f / ring.invCellAngle; + m_regirOnionCalculatedParameters.regirOnionRings.push_back(ring); + + int cellsPerLayer = partitions; + for (int ringIndex = 1; ringIndex < layerGroup.ringCount; ringIndex++) + { + ring.cellCount = std::max(1, int(floorf(float(partitions) * cosf(float(ringIndex) * equatorialAngle)))); + ring.cellOffset = cellsPerLayer; + ring.invCellAngle = float(ring.cellCount) / (2 * c_pi); + ring.cellAngle = 1.f / ring.invCellAngle; + m_regirOnionCalculatedParameters.regirOnionRings.push_back(ring); + + cellsPerLayer += ring.cellCount * 2; + } + + layerGroup.cellsPerLayer = cellsPerLayer; + layerGroup.layerCount = layerCount; + m_regirOnionCalculatedParameters.regirOnionLayers.push_back(layerGroup); + + innerRadius = outerRadius; + + totalCells += cellsPerLayer * layerCount; + totalLayers += layerCount; + } + + m_regirOnionCalculatedParameters.regirOnionCells = totalCells; + m_regirOnionCalculatedParameters.lightSlotCount = m_regirOnionCalculatedParameters.regirOnionCells * m_regirStaticParameters.LightsPerCell; + } + + static float3 SphericalToCartesian(const float radius, const float azimuth, const float elevation) + { + return float3{ + radius * cosf(azimuth) * cosf(elevation), + radius * sinf(elevation), + radius * sinf(azimuth) * cosf(elevation) + }; + } + + static float Distance(const float3& a, const float3& b) + { + float3 d{ a.x - b.x, a.y - b.y, a.z - b.z }; + return sqrtf(d.x * d.x + d.y * d.y + d.z * d.z); + } + + void ReGIRContext::ComputeOnionJitterCurve() + { + std::vector cubicRootFactors; + std::vector linearFactors; + + int layerGroupIndex = 0; + for (const auto& layerGroup : m_regirOnionCalculatedParameters.regirOnionLayers) + { + for (int layerIndex = 0; layerIndex < layerGroup.layerCount; layerIndex++) + { + const float innerRadius = layerGroup.innerRadius * powf(layerGroup.layerScale, float(layerIndex)); + const float outerRadius = innerRadius * layerGroup.layerScale; + const float middleRadius = (innerRadius + outerRadius) * 0.5f; + float maxCellRadius = 0.f; + + for (int ringIndex = 0; ringIndex < layerGroup.ringCount; ringIndex++) + { + const auto& ring = m_regirOnionCalculatedParameters.regirOnionRings[layerGroup.ringOffset + ringIndex]; + + const float middleElevation = layerGroup.equatorialCellAngle * float(ringIndex); + const float vertexElevation = (ringIndex == 0) + ? layerGroup.equatorialCellAngle * 0.5f + : middleElevation - layerGroup.equatorialCellAngle * 0.5f; + + const float middleAzimuth = 0.f; + const float vertexAzimuth = ring.cellAngle; + + const float3 middlePoint = SphericalToCartesian(middleRadius, middleAzimuth, middleElevation); + const float3 vertexPoint = SphericalToCartesian(outerRadius, vertexAzimuth, vertexElevation); + + const float cellRadius = Distance(middlePoint, vertexPoint); + + maxCellRadius = std::max(maxCellRadius, cellRadius); + } + +#if PRINT_JITTER_CURVE + char buf[256]; + sprintf_s(buf, "%.3f,%.3f\n", middleRadius, maxCellRadius); + OutputDebugStringA(buf); +#endif + + if (layerGroupIndex < int(m_regirOnionCalculatedParameters.regirOnionLayers.size()) - 1) + { + float cubicRootFactor = maxCellRadius * powf(middleRadius, -1.f / 3.f); + cubicRootFactors.push_back(cubicRootFactor); + } + else + { + float linearFactor = maxCellRadius / middleRadius; + linearFactors.push_back(linearFactor); + } + } + + layerGroupIndex++; + } + + // Compute the median of the cubic root factors, there are some outliers in the curve + if (!cubicRootFactors.empty()) + { + std::sort(cubicRootFactors.begin(), cubicRootFactors.end()); + m_regirOnionCalculatedParameters.regirOnionCubicRootFactor = cubicRootFactors[cubicRootFactors.size() / 2]; + } + else + { + m_regirOnionCalculatedParameters.regirOnionCubicRootFactor = 0.f; + } + + // Compute the average of the linear factors, they're all the same anyway + float sumOfLinearFactors = std::accumulate(linearFactors.begin(), linearFactors.end(), 0.f); + m_regirOnionCalculatedParameters.regirOnionLinearFactor = sumOfLinearFactors / std::max(float(linearFactors.size()), 1.f); + } + + ReGIRGridCalculatedParameters rtxdi::ReGIRContext::getReGIRGridCalculatedParameters() const + { + return m_regirGridCalculatedParameters; + } + + ReGIROnionCalculatedParameters rtxdi::ReGIRContext::getReGIROnionCalculatedParameters() const + { + return m_regirOnionCalculatedParameters; + } + + uint32_t rtxdi::ReGIRContext::getReGIRCellOffset() const + { + return m_regirCellOffset; + } + + uint32_t rtxdi::ReGIRContext::getReGIRLightSlotCount() const + { + switch (m_regirStaticParameters.Mode) + { + case ReGIRMode::Grid: + return m_regirGridCalculatedParameters.lightSlotCount; + break; + case ReGIRMode::Onion: + return m_regirOnionCalculatedParameters.lightSlotCount; + break; + default: + case ReGIRMode::Disabled: + return 0; + break; + } + } + + ReGIRDynamicParameters ReGIRContext::getReGIRDynamicParameters() const + { + return m_regirDynamicParameters; + } + + ReGIRStaticParameters ReGIRContext::getReGIRStaticParameters() const + { + return m_regirStaticParameters; + } + + void ReGIRContext::setDynamicParameters(const ReGIRDynamicParameters& regirDynamicParameters) + { + m_regirDynamicParameters = regirDynamicParameters; + } + + bool ReGIRContext::isLocalLightPowerRISEnable() const + { + return (m_regirDynamicParameters.presamplingMode == LocalLightReGIRPresamplingMode::Power_RIS) || + (m_regirDynamicParameters.fallbackSamplingMode == LocalLightReGIRFallbackSamplingMode::Power_RIS); + } + +} \ No newline at end of file diff --git a/rtxdi-sdk/src/ReSTIRDI.cpp b/rtxdi-sdk/src/ReSTIRDI.cpp new file mode 100644 index 0000000..138d8a7 --- /dev/null +++ b/rtxdi-sdk/src/ReSTIRDI.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#define PRINT_JITTER_CURVE 0 + +#if PRINT_JITTER_CURVE +#define NOMINMAX +#include +#endif + +using namespace rtxdi; + +namespace rtxdi +{ + + +const uint32_t ReSTIRDIContext::NumReservoirBuffers = 3; + +void debugCheckParameters(const ReSTIRDIStaticParameters& params) +{ + assert(params.RenderWidth > 0); + assert(params.RenderHeight > 0); +} + +ReSTIRDIContext::ReSTIRDIContext(const ReSTIRDIStaticParameters& params) : + m_staticParams(params), + m_frameIndex(0), + m_resamplingMode(ReSTIRDI_ResamplingMode::TemporalAndSpatial), + m_reservoirBufferParams(CalculateReservoirBufferParameters(params.RenderWidth, params.RenderHeight, params.CheckerboardSamplingMode)), + m_bufferIndices(getDefaultReSTIRDIBufferIndices()), + m_initialSamplingParams(getDefaultReSTIRDIInitialSamplingParams()), + m_temporalResamplingParams(getDefaultReSTIRDITemporalResamplingParams()), + m_spatialResamplingParams(getDefaultReSTIRDISpatialResamplingParams()), + m_shadingParams(getDefaultReSTIRDIShadingParams()) +{ + debugCheckParameters(params); + updateCheckerboardField(); + m_runtimeParams.neighborOffsetMask = m_staticParams.NeighborOffsetCount - 1; + updateBufferIndices(); +} + +ReSTIRDI_ResamplingMode ReSTIRDIContext::getResamplingMode() const +{ + return m_resamplingMode; +} + +RTXDI_RuntimeParameters ReSTIRDIContext::getRuntimeParams() const +{ + return m_runtimeParams; +} + +RTXDI_ReservoirBufferParameters ReSTIRDIContext::getReservoirBufferParameters() const +{ + return m_reservoirBufferParams; +} + +ReSTIRDI_BufferIndices ReSTIRDIContext::getBufferIndices() const +{ + return m_bufferIndices; +} + +ReSTIRDI_InitialSamplingParameters ReSTIRDIContext::getInitialSamplingParameters() const +{ + return m_initialSamplingParams; +} + +ReSTIRDI_TemporalResamplingParameters ReSTIRDIContext::getTemporalResamplingParameters() const +{ + return m_temporalResamplingParams; +} + +ReSTIRDI_SpatialResamplingParameters ReSTIRDIContext::getSpatialResamplingParameters() const +{ + return m_spatialResamplingParams; +} + +ReSTIRDI_ShadingParameters ReSTIRDIContext::getShadingParameters() const +{ + return m_shadingParams; +} + +const ReSTIRDIStaticParameters& ReSTIRDIContext::getStaticParameters() const +{ + return m_staticParams; +} + +void ReSTIRDIContext::setFrameIndex(uint32_t frameIndex) +{ + m_frameIndex = frameIndex; + m_temporalResamplingParams.uniformRandomNumber = JenkinsHash(m_frameIndex); + m_LastFrameOutputReservoir = m_CurrentFrameOutputReservoir; + updateBufferIndices(); + updateCheckerboardField(); +} + +uint32_t ReSTIRDIContext::getFrameIndex() const +{ + return m_frameIndex; +} + +void ReSTIRDIContext::setResamplingMode(ReSTIRDI_ResamplingMode resamplingMode) +{ + m_resamplingMode = resamplingMode; + updateBufferIndices(); +} + +void ReSTIRDIContext::setInitialSamplingParameters(const ReSTIRDI_InitialSamplingParameters& initialSamplingParams) +{ + m_initialSamplingParams = initialSamplingParams; +} + +void ReSTIRDIContext::setTemporalResamplingParameters(const ReSTIRDI_TemporalResamplingParameters& temporalResamplingParams) +{ + m_temporalResamplingParams = temporalResamplingParams; + m_temporalResamplingParams.uniformRandomNumber = JenkinsHash(m_frameIndex); +} + +void ReSTIRDIContext::setSpatialResamplingParameters(const ReSTIRDI_SpatialResamplingParameters& spatialResamplingParams) +{ + ReSTIRDI_SpatialResamplingParameters srp = spatialResamplingParams; + srp.neighborOffsetMask = m_spatialResamplingParams.neighborOffsetMask; + m_spatialResamplingParams = srp; +} + +void ReSTIRDIContext::setShadingParameters(const ReSTIRDI_ShadingParameters& shadingParams) +{ + m_shadingParams = shadingParams; +} + +void ReSTIRDIContext::updateBufferIndices() +{ + const bool useTemporalResampling = + m_resamplingMode == ReSTIRDI_ResamplingMode::Temporal || + m_resamplingMode == ReSTIRDI_ResamplingMode::TemporalAndSpatial || + m_resamplingMode == ReSTIRDI_ResamplingMode::FusedSpatiotemporal; + + const bool useSpatialResampling = + m_resamplingMode == ReSTIRDI_ResamplingMode::Spatial || + m_resamplingMode == ReSTIRDI_ResamplingMode::TemporalAndSpatial || + m_resamplingMode == ReSTIRDI_ResamplingMode::FusedSpatiotemporal; + + + if (m_resamplingMode == ReSTIRDI_ResamplingMode::FusedSpatiotemporal) + { + m_bufferIndices.initialSamplingOutputBufferIndex = (m_LastFrameOutputReservoir + 1) % ReSTIRDIContext::NumReservoirBuffers; + m_bufferIndices.temporalResamplingInputBufferIndex = m_LastFrameOutputReservoir; + m_bufferIndices.shadingInputBufferIndex = m_bufferIndices.initialSamplingOutputBufferIndex; + } + else + { + m_bufferIndices.initialSamplingOutputBufferIndex = (m_LastFrameOutputReservoir + 1) % ReSTIRDIContext::NumReservoirBuffers; + m_bufferIndices.temporalResamplingInputBufferIndex = m_LastFrameOutputReservoir; + m_bufferIndices.temporalResamplingOutputBufferIndex = (m_bufferIndices.temporalResamplingInputBufferIndex + 1) % ReSTIRDIContext::NumReservoirBuffers; + m_bufferIndices.spatialResamplingInputBufferIndex = useTemporalResampling + ? m_bufferIndices.temporalResamplingOutputBufferIndex + : m_bufferIndices.initialSamplingOutputBufferIndex; + m_bufferIndices.spatialResamplingOutputBufferIndex = (m_bufferIndices.spatialResamplingInputBufferIndex + 1) % ReSTIRDIContext::NumReservoirBuffers; + m_bufferIndices.shadingInputBufferIndex = useSpatialResampling + ? m_bufferIndices.spatialResamplingOutputBufferIndex + : m_bufferIndices.temporalResamplingOutputBufferIndex; + } + m_CurrentFrameOutputReservoir = m_bufferIndices.shadingInputBufferIndex; +} + +void ReSTIRDIContext::updateCheckerboardField() +{ + switch (m_staticParams.CheckerboardSamplingMode) + { + case CheckerboardMode::Black: + m_runtimeParams.activeCheckerboardField = (m_frameIndex & 1) ? 1 : 2; + break; + case CheckerboardMode::White: + m_runtimeParams.activeCheckerboardField = (m_frameIndex & 1) ? 2 : 1; + break; + default: + m_runtimeParams.activeCheckerboardField = 0; + } +} + +} \ No newline at end of file diff --git a/rtxdi-sdk/src/ReSTIRGI.cpp b/rtxdi-sdk/src/ReSTIRGI.cpp new file mode 100644 index 0000000..3a3c237 --- /dev/null +++ b/rtxdi-sdk/src/ReSTIRGI.cpp @@ -0,0 +1,134 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#include "rtxdi/ReSTIRGI.h" + +namespace rtxdi +{ + +ReSTIRGIContext::ReSTIRGIContext(const ReSTIRGIStaticParameters& staticParams) : + m_frameIndex(0), + m_reservoirBufferParams(CalculateReservoirBufferParameters(staticParams.RenderWidth, staticParams.RenderHeight, staticParams.CheckerboardSamplingMode)), + m_staticParams(staticParams), + m_resamplingMode(rtxdi::ReSTIRGI_ResamplingMode::None), + m_bufferIndices(getDefaultReSTIRGIBufferIndices()), + m_temporalResamplingParams(getDefaultReSTIRGITemporalResamplingParams()), + m_spatialResamplingParams(getDefaultReSTIRGISpatialResamplingParams()), + m_finalShadingParams(getDefaultReSTIRGIFinalShadingParams()) +{ +} + +ReSTIRGIStaticParameters ReSTIRGIContext::getStaticParams() const +{ + return m_staticParams; +} + +uint32_t ReSTIRGIContext::getFrameIndex() const +{ + return m_frameIndex; +} + +RTXDI_ReservoirBufferParameters ReSTIRGIContext::getReservoirBufferParameters() const +{ + return m_reservoirBufferParams; +} + +ReSTIRGI_ResamplingMode ReSTIRGIContext::getResamplingMode() const +{ + return m_resamplingMode; +} + +ReSTIRGI_BufferIndices ReSTIRGIContext::getBufferIndices() const +{ + return m_bufferIndices; +} + +ReSTIRGI_TemporalResamplingParameters ReSTIRGIContext::getTemporalResamplingParameters() const +{ + return m_temporalResamplingParams; +} + +ReSTIRGI_SpatialResamplingParameters ReSTIRGIContext::getSpatialResamplingParameters() const +{ + return m_spatialResamplingParams; +} + +ReSTIRGI_FinalShadingParameters ReSTIRGIContext::getFinalShadingParameters() const +{ + return m_finalShadingParams; +} + +void ReSTIRGIContext::setFrameIndex(uint32_t frameIndex) +{ + m_frameIndex = frameIndex; + m_temporalResamplingParams.uniformRandomNumber = JenkinsHash(m_frameIndex); + updateBufferIndices(); +} + +void ReSTIRGIContext::setResamplingMode(ReSTIRGI_ResamplingMode resamplingMode) +{ + m_resamplingMode = resamplingMode; + updateBufferIndices(); +} + +void ReSTIRGIContext::setTemporalResamplingParameters(const ReSTIRGI_TemporalResamplingParameters& temporalResamplingParams) +{ + m_temporalResamplingParams = temporalResamplingParams; + m_temporalResamplingParams.uniformRandomNumber = JenkinsHash(m_frameIndex); +} + +void ReSTIRGIContext::setSpatialResamplingParameters(const ReSTIRGI_SpatialResamplingParameters& spatialResamplingParams) +{ + m_spatialResamplingParams = spatialResamplingParams; +} + +void ReSTIRGIContext::setFinalShadingParameters(const ReSTIRGI_FinalShadingParameters& finalShadingParams) +{ + m_finalShadingParams = finalShadingParams; +} + +void ReSTIRGIContext::updateBufferIndices() +{ + switch (m_resamplingMode) + { + case rtxdi::ReSTIRGI_ResamplingMode::None: + m_bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex = 0; + m_bufferIndices.finalShadingInputBufferIndex = 0; + break; + case rtxdi::ReSTIRGI_ResamplingMode::Temporal: + m_bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex = m_frameIndex & 1; + m_bufferIndices.temporalResamplingInputBufferIndex = !m_bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex; + m_bufferIndices.temporalResamplingOutputBufferIndex = m_bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex; + m_bufferIndices.finalShadingInputBufferIndex = m_bufferIndices.temporalResamplingOutputBufferIndex; + break; + case rtxdi::ReSTIRGI_ResamplingMode::Spatial: + m_bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex = 0; + m_bufferIndices.spatialResamplingInputBufferIndex = 0; + m_bufferIndices.spatialResamplingOutputBufferIndex = 1; + m_bufferIndices.finalShadingInputBufferIndex = 1; + break; + case rtxdi::ReSTIRGI_ResamplingMode::TemporalAndSpatial: + m_bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex = 0; + m_bufferIndices.temporalResamplingInputBufferIndex = 1; + m_bufferIndices.temporalResamplingOutputBufferIndex = 0; + m_bufferIndices.spatialResamplingInputBufferIndex = 0; + m_bufferIndices.spatialResamplingOutputBufferIndex = 1; + m_bufferIndices.finalShadingInputBufferIndex = 1; + break; + case rtxdi::ReSTIRGI_ResamplingMode::FusedSpatiotemporal: + m_bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex = m_frameIndex & 1; + m_bufferIndices.temporalResamplingInputBufferIndex = !m_bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex; + m_bufferIndices.spatialResamplingOutputBufferIndex = m_bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex; + m_bufferIndices.finalShadingInputBufferIndex = m_bufferIndices.spatialResamplingOutputBufferIndex; + break; + } +} + +} \ No newline at end of file diff --git a/rtxdi-sdk/src/RtxdiUtils.cpp b/rtxdi-sdk/src/RtxdiUtils.cpp new file mode 100644 index 0000000..e929c50 --- /dev/null +++ b/rtxdi-sdk/src/RtxdiUtils.cpp @@ -0,0 +1,82 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#include + +#include + +namespace rtxdi +{ + +RTXDI_ReservoirBufferParameters CalculateReservoirBufferParameters(uint32_t renderWidth, uint32_t renderHeight, CheckerboardMode checkerboardMode) +{ + renderWidth = (checkerboardMode == CheckerboardMode::Off) + ? renderWidth + : (renderWidth + 1) / 2; + uint32_t renderWidthBlocks = (renderWidth + RTXDI_RESERVOIR_BLOCK_SIZE - 1) / RTXDI_RESERVOIR_BLOCK_SIZE; + uint32_t renderHeightBlocks = (renderHeight + RTXDI_RESERVOIR_BLOCK_SIZE - 1) / RTXDI_RESERVOIR_BLOCK_SIZE; + RTXDI_ReservoirBufferParameters params; + params.reservoirBlockRowPitch = renderWidthBlocks * (RTXDI_RESERVOIR_BLOCK_SIZE * RTXDI_RESERVOIR_BLOCK_SIZE); + params.reservoirArrayPitch = params.reservoirBlockRowPitch * renderHeightBlocks; + return params; +} + +void ComputePdfTextureSize(uint32_t maxItems, uint32_t& outWidth, uint32_t& outHeight, uint32_t& outMipLevels) +{ + // Compute the size of a power-of-2 rectangle that fits all items, 1 item per pixel + double textureWidth = std::max(1.0, ceil(sqrt(double(maxItems)))); + textureWidth = exp2(ceil(log2(textureWidth))); + double textureHeight = std::max(1.0, ceil(maxItems / textureWidth)); + textureHeight = exp2(ceil(log2(textureHeight))); + double textureMips = std::max(1.0, log2(std::max(textureWidth, textureHeight)) + 1.0); + + outWidth = uint32_t(textureWidth); + outHeight = uint32_t(textureHeight); + outMipLevels = uint32_t(textureMips); +} + +void FillNeighborOffsetBuffer(uint8_t* buffer, uint32_t neighborOffsetCount) +{ + // Create a sequence of low-discrepancy samples within a unit radius around the origin + // for "randomly" sampling neighbors during spatial resampling + + int R = 250; + const float phi2 = 1.0f / 1.3247179572447f; + uint32_t num = 0; + float u = 0.5f; + float v = 0.5f; + while (num < neighborOffsetCount * 2) { + u += phi2; + v += phi2 * phi2; + if (u >= 1.0f) u -= 1.0f; + if (v >= 1.0f) v -= 1.0f; + + float rSq = (u - 0.5f) * (u - 0.5f) + (v - 0.5f) * (v - 0.5f); + if (rSq > 0.25f) + continue; + + buffer[num++] = int8_t((u - 0.5f) * R); + buffer[num++] = int8_t((v - 0.5f) * R); + } +} + +uint32_t JenkinsHash(uint32_t a) +{ + // http://burtleburtle.net/bob/hash/integer.html + a = (a + 0x7ed55d16) + (a << 12); + a = (a ^ 0xc761c23c) ^ (a >> 19); + a = (a + 0x165667b1) + (a << 5); + a = (a + 0xd3a2646c) ^ (a << 9); + a = (a + 0xfd7046c5) + (a << 3); + a = (a ^ 0xb55a4f09) ^ (a >> 16); + return a; +} + +} \ No newline at end of file diff --git a/shaders/AccumulationPass.hlsl b/shaders/AccumulationPass.hlsl index 0212f3d..3e28796 100644 --- a/shaders/AccumulationPass.hlsl +++ b/shaders/AccumulationPass.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/shaders/BRDFPTParameters.h b/shaders/BRDFPTParameters.h new file mode 100644 index 0000000..309ed9d --- /dev/null +++ b/shaders/BRDFPTParameters.h @@ -0,0 +1,43 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#ifndef RTXDI_BRDFPT_PARAMETERS_H +#define RTXDI_BRDFPT_PARAMETERS_H + +#include + +struct BRDFPathTracing_MaterialOverrideParameters +{ + float roughnessOverride; + float metalnessOverride; + float minSecondaryRoughness; + uint32_t pad1; +}; + +// Parameters for running ReSTIR DI on the secondary surface +// Spatial resampling is only enabled if the secondary hit is in the gbuffer (determined via projection) +struct BRDFPathTracing_SecondarySurfaceReSTIRDIParameters +{ + ReSTIRDI_InitialSamplingParameters initialSamplingParams; + ReSTIRDI_SpatialResamplingParameters spatialResamplingParams; +}; + +struct BRDFPathTracing_Parameters +{ + uint32_t enableIndirectEmissiveSurfaces; + uint32_t enableSecondaryResampling; + uint32_t enableReSTIRGI; + uint32_t pad1; + + BRDFPathTracing_MaterialOverrideParameters materialOverrideParams; + BRDFPathTracing_SecondarySurfaceReSTIRDIParameters secondarySurfaceReSTIRDIParams; +}; + +#endif // RTXDI_BRDFPT_PARAMETERS_H \ No newline at end of file diff --git a/shaders/CMakeLists.txt b/shaders/CMakeLists.txt index 6122409..0708b26 100644 --- a/shaders/CMakeLists.txt +++ b/shaders/CMakeLists.txt @@ -1,6 +1,6 @@ include("${DONUT_PATH}/compileshaders.cmake") -file(GLOB shaders "*.h*" "LightingPasses/*.h*" "RTXGI/*.h*" "DebugViz/*.h*" "../rtxdi-sdk/shaders/*.hlsl" "../rtxdi-sdk/include/*.hlsli") +file(GLOB shaders "*.h*" "LightingPasses/*.h*" "DebugViz/*.h*" "../rtxdi-sdk/shaders/*.hlsl" "../rtxdi-sdk/include/*.hlsli") set(project rtxdi-sample) set(folder "RTXDI SDK") @@ -16,12 +16,6 @@ else() set(NRD_OPTIONS --relaxedInclude NRD.hlsli) endif() -if (TARGET RTXGI-D3D12 OR TARGET RTXGI-VK) - set(RTXGI_OPTIONS -D WITH_RTXGI) -else() - set(RTXGI_OPTIONS --relaxedInclude "ddgi/Irradiance.hlsl") -endif() - set (OUTPUT_PATH_BASE "${CMAKE_BINARY_DIR}/bin/shaders/rtxdi-sample") if (WIN32) @@ -38,12 +32,8 @@ if (DONUT_WITH_DX12) --outputExt .bin -I ${DONUT_SHADER_INCLUDE_DIR} -I ${CMAKE_CURRENT_SOURCE_DIR}/../rtxdi-sdk/include - -I ${CMAKE_CURRENT_SOURCE_DIR}/../RTXGI/shaders - -I ${CMAKE_CURRENT_SOURCE_DIR}/RTXGI --relaxedInclude "../Types.h" - --relaxedInclude "rtxgi\\Types.h" ${NRD_OPTIONS} - ${RTXGI_OPTIONS} ${USE_API_OPTION} --compiler ${DXC_PATH}) @@ -63,12 +53,8 @@ if (DONUT_WITH_VULKAN) --outputExt .bin -I ${DONUT_SHADER_INCLUDE_DIR} -I ${CMAKE_CURRENT_SOURCE_DIR}/../rtxdi-sdk/include - -I ${CMAKE_CURRENT_SOURCE_DIR}/../RTXGI/shaders - -I ${CMAKE_CURRENT_SOURCE_DIR}/RTXGI --relaxedInclude "../Types.h" - --relaxedInclude "rtxgi\\Types.h" ${NRD_OPTIONS} - ${RTXGI_OPTIONS} ${USE_API_OPTION} ${NVRHI_DEFAULT_VK_REGISTER_OFFSETS} -D SPIRV diff --git a/shaders/CompositingPass.hlsl b/shaders/CompositingPass.hlsl index 3b87e66..7957b40 100644 --- a/shaders/CompositingPass.hlsl +++ b/shaders/CompositingPass.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -36,13 +36,6 @@ Texture2D t_DenoisedSpecular : register(t8); SamplerState s_EnvironmentSampler : register(s0); -#ifdef WITH_RTXGI -#include "RtxgiHelpers.hlsli" -StructuredBuffer t_DDGIVolumes : register(t10 VK_DESCRIPTOR_SET(2)); -StructuredBuffer t_DDGIVolumeResourceIndices : register(t11 VK_DESCRIPTOR_SET(2)); -SamplerState s_ProbeSampler : register(s10 VK_DESCRIPTOR_SET(2)); -#endif - [numthreads(8, 8, 1)] void main(uint2 globalIdx : SV_DispatchThreadID) { @@ -92,24 +85,6 @@ void main(uint2 globalIdx : SV_DispatchThreadID) compositedColor = diffuse_illumination.rgb; compositedColor += specular_illumination.rgb; compositedColor += emissive.rgb; - -#ifdef WITH_RTXGI - if (g_Const.numRtxgiVolumes) - { - float3 worldPosition = viewDepthToWorldPos(g_Const.view, globalIdx, depth); - - float3 indirectIrradiance = GetIrradianceFromDDGI( - worldPosition, - normal, - g_Const.view.cameraDirectionOrPosition.xyz, - g_Const.numRtxgiVolumes, - t_DDGIVolumes, - t_DDGIVolumeResourceIndices, - s_ProbeSampler); - - compositedColor += diffuseAlbedo * indirectIrradiance; - } -#endif } else { diff --git a/shaders/ConfidencePass.hlsl b/shaders/ConfidencePass.hlsl index 5d3ef9b..1549245 100644 --- a/shaders/ConfidencePass.hlsl +++ b/shaders/ConfidencePass.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/shaders/DlssExposure.hlsl b/shaders/DlssExposure.hlsl index b70edcd..627c76f 100644 --- a/shaders/DlssExposure.hlsl +++ b/shaders/DlssExposure.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/shaders/FilterGradientsPass.hlsl b/shaders/FilterGradientsPass.hlsl index c9f46c5..8d675f3 100644 --- a/shaders/FilterGradientsPass.hlsl +++ b/shaders/FilterGradientsPass.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/shaders/GBufferHelpers.hlsli b/shaders/GBufferHelpers.hlsli index 857b641..c67ed57 100644 --- a/shaders/GBufferHelpers.hlsli +++ b/shaders/GBufferHelpers.hlsli @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/shaders/GlassPass.hlsl b/shaders/GlassPass.hlsl index 5fcedfd..c558140 100644 --- a/shaders/GlassPass.hlsl +++ b/shaders/GlassPass.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/shaders/HelperFunctions.hlsli b/shaders/HelperFunctions.hlsli index daa8ba7..0fc8ee1 100644 --- a/shaders/HelperFunctions.hlsli +++ b/shaders/HelperFunctions.hlsli @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/shaders/LightShaping.hlsli b/shaders/LightShaping.hlsli index ef5d96e..df64e1f 100644 --- a/shaders/LightShaping.hlsli +++ b/shaders/LightShaping.hlsli @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/shaders/LightingPasses/BrdfRayTracing.hlsl b/shaders/LightingPasses/BrdfRayTracing.hlsl index 283941e..30fed8d 100644 --- a/shaders/LightingPasses/BrdfRayTracing.hlsl +++ b/shaders/LightingPasses/BrdfRayTracing.hlsl @@ -12,7 +12,7 @@ #include "RtxdiApplicationBridge.hlsli" -#include +#include #ifdef WITH_NRD #define NRD_HEADER_ONLY @@ -34,7 +34,7 @@ void RayGen() #if !USE_RAY_QUERY uint2 GlobalIndex = DispatchRaysIndex().xy; #endif - uint2 pixelPosition = RTXDI_ReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams); + uint2 pixelPosition = RTXDI_DIReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams.activeCheckerboardField); RAB_Surface surface = RAB_GetGBufferSurface(pixelPosition, false); @@ -128,10 +128,10 @@ void RayGen() uint instanceMask = INSTANCE_MASK_OPAQUE; - if (g_Const.enableAlphaTestedGeometry) + if (g_Const.sceneConstants.enableAlphaTestedGeometry) instanceMask |= INSTANCE_MASK_ALPHA_TESTED; - if (g_Const.enableTransparentGeometry) + if (g_Const.sceneConstants.enableTransparentGeometry) instanceMask |= INSTANCE_MASK_TRANSPARENT; #if USE_RAY_QUERY @@ -172,8 +172,7 @@ void RayGen() InterlockedAdd(u_RayCountBuffer[RAY_COUNT_TRACED(g_PerPassConstants.rayCountBufferIndex)], 1); } - const RTXDI_ResamplingRuntimeParameters params = g_Const.runtimeParams; - uint gbufferIndex = RTXDI_ReservoirPositionToPointer(params, GlobalIndex, 0); + uint gbufferIndex = RTXDI_DIReservoirPositionToPointer(g_Const.restirGI.reservoirBufferParams, GlobalIndex, 0); struct { @@ -188,7 +187,7 @@ void RayGen() // Include the emissive component of surfaces seen with BRDF rays if requested (i.e. when Direct Lighting mode // is set to BRDF) or on delta reflection rays because those bypass ReSTIR GI and direct specular lighting, // and we need to see reflections of lamps and the sky in mirrors. - const bool includeEmissiveComponent = g_Const.enableIndirectEmissiveSurfaces || (isSpecularRay && isDeltaSurface); + const bool includeEmissiveComponent = g_Const.brdfPT.enableIndirectEmissiveSurfaces || (isSpecularRay && isDeltaSurface); if (payload.instanceID != ~0u) { @@ -210,16 +209,16 @@ void RayGen() ms.shadingNormal = getBentNormal(gs.flatNormal, ms.shadingNormal, ray.Direction); - if (g_Const.roughnessOverride >= 0) - ms.roughness = g_Const.roughnessOverride; + if (g_Const.brdfPT.materialOverrideParams.roughnessOverride >= 0) + ms.roughness = g_Const.brdfPT.materialOverrideParams.roughnessOverride; - if (g_Const.metalnessOverride >= 0) + if (g_Const.brdfPT.materialOverrideParams.metalnessOverride >= 0) { - ms.metalness = g_Const.metalnessOverride; + ms.metalness = g_Const.brdfPT.materialOverrideParams.metalnessOverride; getReflectivity(ms.metalness, ms.baseColor, ms.diffuseAlbedo, ms.specularF0); } - ms.roughness = max(ms.roughness, g_Const.minSecondaryRoughness); + ms.roughness = max(ms.roughness, g_Const.brdfPT.materialOverrideParams.minSecondaryRoughness); if (includeEmissiveComponent) radiance += ms.emissiveColor; @@ -233,7 +232,7 @@ void RayGen() } else { - if (g_Const.enableEnvironmentMap && includeEmissiveComponent) + if (g_Const.sceneConstants.enableEnvironmentMap && includeEmissiveComponent) { float3 environmentRadiance = GetEnvironmentRadiance(ray.Direction); radiance += environmentRadiance; @@ -256,7 +255,7 @@ void RayGen() secondaryGBufferData.diffuseAlbedo = Pack_R11G11B10_UFLOAT(secondarySurface.diffuseAlbedo); secondaryGBufferData.specularAndRoughness = Pack_R8G8B8A8_Gamma_UFLOAT(float4(secondarySurface.specularF0, secondarySurface.roughness)); - if (g_Const.enableReSTIRIndirect) + if (g_Const.brdfPT.enableReSTIRGI) { if (isSpecularRay && isDeltaSurface) { diff --git a/shaders/LightingPasses/ComputeGradients.hlsl b/shaders/LightingPasses/DIComputeGradients.hlsl similarity index 91% rename from shaders/LightingPasses/ComputeGradients.hlsl rename to shaders/LightingPasses/DIComputeGradients.hlsl index b1f4087..b8ccb06 100644 --- a/shaders/LightingPasses/ComputeGradients.hlsl +++ b/shaders/LightingPasses/DIComputeGradients.hlsl @@ -12,7 +12,7 @@ #include "RtxdiApplicationBridge.hlsli" -#include +#include #ifdef WITH_NRD #undef WITH_NRD @@ -32,7 +32,7 @@ void RayGen() uint2 GlobalIndex = DispatchRaysIndex().xy; #endif - const RTXDI_ResamplingRuntimeParameters params = g_Const.runtimeParams; + const RTXDI_RuntimeParameters params = g_Const.runtimeParams; // This shader runs one thread per image stratum, i.e. a square of pixels // (RTXDI_GRAD_FACTOR x RTXDI_GRAD_FACTOR) in size. One pixel is selected to @@ -51,7 +51,7 @@ void RayGen() { // Translate the gradient stratum index (GlobalIndex) into reservoir and pixel positions. int2 srcReservoirPos = GlobalIndex * RTXDI_GRAD_FACTOR + int2(xx, yy); - int2 srcPixelPos = RTXDI_ReservoirPosToPixelPos(srcReservoirPos, g_Const.runtimeParams); + int2 srcPixelPos = RTXDI_DIReservoirPosToPixelPos(srcReservoirPos, params.activeCheckerboardField); if (any(srcPixelPos >= int2(g_Const.view.viewportSize))) continue; @@ -59,7 +59,7 @@ void RayGen() // Find the matching pixel in the previous frame - that information is produced // by the temporal resampling or fused resampling shaders. int2 temporalPixelPos = u_TemporalSamplePositions[srcReservoirPos]; - int2 temporalReservoirPos = RTXDI_PixelPosToReservoirPos(temporalPixelPos, g_Const.runtimeParams); + int2 temporalReservoirPos = RTXDI_PixelPosToReservoirPos(temporalPixelPos, params.activeCheckerboardField); // Load the previous frame sampled lighting luminance. // For invalid gradients, temporalPixelPos is negative, and prevLuminance will be 0 @@ -98,15 +98,15 @@ void RayGen() // Translate the pixel pos into reservoir pos - the math the same for both current and prev frames, // unlike the reverse translation that has to take the active checkerboard field into account. - int2 selectedCurrentOrPrevReservoirPos = RTXDI_PixelPosToReservoirPos(selectedCurrentOrPrevPixelPos, g_Const.runtimeParams); + int2 selectedCurrentOrPrevReservoirPos = RTXDI_PixelPosToReservoirPos(selectedCurrentOrPrevPixelPos, params.activeCheckerboardField); // Load the reservoir that was selected for gradient evaluation, either from the current or the previous frame. - RTXDI_Reservoir selectedReservoir = RTXDI_LoadReservoir(params, + RTXDI_DIReservoir selectedReservoir = RTXDI_LoadDIReservoir(g_Const.restirDI.reservoirBufferParams, selectedCurrentOrPrevReservoirPos, - usePrevSample ? g_Const.temporalInputBufferIndex : g_Const.shadeInputBufferIndex); + usePrevSample ? g_Const.restirDI.bufferIndices.temporalResamplingInputBufferIndex : g_Const.restirDI.bufferIndices.shadingInputBufferIndex); // Map the reservoir's light index into the other frame (previous or current) - int selectedMappedLightIndex = RAB_TranslateLightIndex(RTXDI_GetReservoirLightIndex(selectedReservoir), !usePrevSample); + int selectedMappedLightIndex = RAB_TranslateLightIndex(RTXDI_GetDIReservoirLightIndex(selectedReservoir), !usePrevSample); if (selectedMappedLightIndex >= 0) { @@ -142,7 +142,7 @@ void RayGen() // Reconstruct the light sample RAB_LightInfo lightInfo = RAB_LoadLightInfo(selectedMappedLightIndex, !usePrevSample); RAB_LightSample lightSample = RAB_SamplePolymorphicLight(lightInfo, - surface, RTXDI_GetReservoirSampleUV(selectedReservoir)); + surface, RTXDI_GetDIReservoirSampleUV(selectedReservoir)); // Shade the other (previous or current) surface using the other light sample float3 diffuse = 0; diff --git a/shaders/LightingPasses/FusedResampling.hlsl b/shaders/LightingPasses/DIFusedResampling.hlsl similarity index 56% rename from shaders/LightingPasses/FusedResampling.hlsl rename to shaders/LightingPasses/DIFusedResampling.hlsl index d5f98a1..4046342 100644 --- a/shaders/LightingPasses/FusedResampling.hlsl +++ b/shaders/LightingPasses/DIFusedResampling.hlsl @@ -17,7 +17,10 @@ #include "RtxdiApplicationBridge.hlsli" -#include +#include +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED +#include "rtxdi/ReGIRSampling.hlsli" +#endif #ifdef WITH_NRD #define NRD_HEADER_ONLY @@ -38,15 +41,15 @@ void RayGen() uint2 GlobalIndex = DispatchRaysIndex().xy; #endif - const RTXDI_ResamplingRuntimeParameters params = g_Const.runtimeParams; + const RTXDI_RuntimeParameters params = g_Const.runtimeParams; - uint2 pixelPosition = RTXDI_ReservoirPosToPixelPos(GlobalIndex, params); + uint2 pixelPosition = RTXDI_DIReservoirPosToPixelPos(GlobalIndex, params.activeCheckerboardField); RAB_RandomSamplerState rng = RAB_InitRandomSampler(pixelPosition, 2); RAB_Surface surface = RAB_GetGBufferSurface(pixelPosition, false); - RTXDI_Reservoir reservoir = RTXDI_LoadReservoir(params, GlobalIndex, g_Const.initialOutputBufferIndex); + RTXDI_DIReservoir reservoir = RTXDI_LoadDIReservoir(g_Const.restirDI.reservoirBufferParams, GlobalIndex, g_Const.restirDI.bufferIndices.initialSamplingOutputBufferIndex); int2 temporalSamplePixelPos = -1; @@ -54,36 +57,38 @@ void RayGen() motionVector = convertMotionVectorToPixelSpace(g_Const.view, g_Const.prevView, pixelPosition, motionVector); bool usePermutationSampling = false; - if (g_Const.enablePermutationSampling) + if (g_Const.restirDI.temporalResamplingParams.enablePermutationSampling) { // Permutation sampling makes more noise on thin, high-detail objects. usePermutationSampling = !IsComplexSurface(pixelPosition, surface); } - RTXDI_SpatioTemporalResamplingParameters stparams; + RTXDI_DISpatioTemporalResamplingParameters stparams; stparams.screenSpaceMotion = motionVector; - stparams.sourceBufferIndex = g_Const.temporalInputBufferIndex; - stparams.maxHistoryLength = g_Const.maxHistoryLength; - stparams.biasCorrectionMode = g_Const.temporalBiasCorrection; - stparams.depthThreshold = g_Const.temporalDepthThreshold; - stparams.normalThreshold = g_Const.temporalNormalThreshold; - stparams.numSamples = g_Const.numSpatialSamples + 1; - stparams.numDisocclusionBoostSamples = g_Const.numDisocclusionBoostSamples; - stparams.samplingRadius = g_Const.spatialSamplingRadius; - stparams.enableVisibilityShortcut = g_Const.discardInvisibleSamples; + stparams.sourceBufferIndex = g_Const.restirDI.bufferIndices.temporalResamplingInputBufferIndex; + stparams.maxHistoryLength = g_Const.restirDI.temporalResamplingParams.maxHistoryLength; + stparams.biasCorrectionMode = g_Const.restirDI.temporalResamplingParams.temporalBiasCorrection; + stparams.depthThreshold = g_Const.restirDI.temporalResamplingParams.temporalDepthThreshold; + stparams.normalThreshold = g_Const.restirDI.temporalResamplingParams.temporalNormalThreshold; + stparams.numSamples = g_Const.restirDI.spatialResamplingParams.numSpatialSamples + 1; + stparams.numDisocclusionBoostSamples = g_Const.restirDI.spatialResamplingParams.numDisocclusionBoostSamples; + stparams.samplingRadius = g_Const.restirDI.spatialResamplingParams.spatialSamplingRadius; + stparams.enableVisibilityShortcut = g_Const.restirDI.temporalResamplingParams.discardInvisibleSamples; stparams.enablePermutationSampling = usePermutationSampling; stparams.enableMaterialSimilarityTest = true; + stparams.uniformRandomNumber = g_Const.restirDI.temporalResamplingParams.uniformRandomNumber; + stparams.discountNaiveSamples = g_Const.discountNaiveSamples; RAB_LightSample lightSample; - reservoir = RTXDI_SpatioTemporalResampling(pixelPosition, surface, reservoir, - rng, stparams, params, temporalSamplePixelPos, lightSample); + reservoir = RTXDI_DISpatioTemporalResampling(pixelPosition, surface, reservoir, + rng, params, g_Const.restirDI.reservoirBufferParams, stparams, temporalSamplePixelPos, lightSample); u_TemporalSamplePositions[GlobalIndex] = temporalSamplePixelPos; #ifdef RTXDI_ENABLE_BOILING_FILTER - if (g_Const.boilingFilterStrength > 0) + if (g_Const.restirDI.temporalResamplingParams.enableBoilingFilter) { - RTXDI_BoilingFilter(LocalIndex, g_Const.boilingFilterStrength, params, reservoir); + RTXDI_BoilingFilter(LocalIndex, g_Const.restirDI.temporalResamplingParams.boilingFilterStrength, reservoir); } #endif @@ -92,7 +97,7 @@ void RayGen() float lightDistance = 0; float2 currLuminance = 0; - if (RTXDI_IsValidReservoir(reservoir)) + if (RTXDI_IsValidDIReservoir(reservoir)) { // lightSample is produced by the RTXDI_SampleLightsForSurface and RTXDI_SpatioTemporalResampling calls above ShadeSurfaceWithLightSample(reservoir, surface, lightSample, @@ -107,15 +112,15 @@ void RayGen() // Discard the pixels where the visibility was reused, as gradients need actual visibility. u_RestirLuminance[GlobalIndex] = currLuminance * (reservoir.age > 0 ? 0 : 1); - RTXDI_StoreReservoir(reservoir, params, GlobalIndex, g_Const.shadeInputBufferIndex); + RTXDI_StoreDIReservoir(reservoir, g_Const.restirDI.reservoirBufferParams, GlobalIndex, g_Const.restirDI.bufferIndices.shadingInputBufferIndex); #if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED if (g_Const.visualizeRegirCells) { - diffuse *= RTXDI_VisualizeReGIRCells(g_Const.runtimeParams, RAB_GetSurfaceWorldPos(surface)); + diffuse *= RTXDI_VisualizeReGIRCells(g_Const.regir, RAB_GetSurfaceWorldPos(surface)); } #endif StoreShadingOutput(GlobalIndex, pixelPosition, - surface.viewDepth, surface.roughness, diffuse, specular, lightDistance, true, g_Const.enableDenoiserInputPacking); + surface.viewDepth, surface.roughness, diffuse, specular, lightDistance, true, g_Const.restirDI.shadingParams.enableDenoiserInputPacking); } \ No newline at end of file diff --git a/shaders/LightingPasses/GenerateInitialSamples.hlsl b/shaders/LightingPasses/DIGenerateInitialSamples.hlsl similarity index 51% rename from shaders/LightingPasses/GenerateInitialSamples.hlsl rename to shaders/LightingPasses/DIGenerateInitialSamples.hlsl index 740b28b..f109618 100644 --- a/shaders/LightingPasses/GenerateInitialSamples.hlsl +++ b/shaders/LightingPasses/DIGenerateInitialSamples.hlsl @@ -12,7 +12,7 @@ #include "RtxdiApplicationBridge.hlsli" -#include +#include #if USE_RAY_QUERY [numthreads(RTXDI_SCREEN_SPACE_GROUP_SIZE, RTXDI_SCREEN_SPACE_GROUP_SIZE, 1)] @@ -26,9 +26,9 @@ void RayGen() uint2 GlobalIndex = DispatchRaysIndex().xy; #endif - const RTXDI_ResamplingRuntimeParameters params = g_Const.runtimeParams; + const RTXDI_RuntimeParameters params = g_Const.runtimeParams; - uint2 pixelPosition = RTXDI_ReservoirPosToPixelPos(GlobalIndex, params); + uint2 pixelPosition = RTXDI_DIReservoirPosToPixelPos(GlobalIndex, params.activeCheckerboardField); RAB_RandomSamplerState rng = RAB_InitRandomSampler(pixelPosition, 1); RAB_RandomSamplerState tileRng = RAB_InitRandomSampler(pixelPosition / RTXDI_TILE_SIZE_IN_PIXELS, 1); @@ -36,25 +36,31 @@ void RayGen() RAB_Surface surface = RAB_GetGBufferSurface(pixelPosition, false); RTXDI_SampleParameters sampleParams = RTXDI_InitSampleParameters( - g_Const.numPrimaryRegirSamples, - g_Const.numPrimaryLocalLightSamples, - g_Const.numPrimaryInfiniteLightSamples, - g_Const.numPrimaryEnvironmentSamples, - g_Const.numPrimaryBrdfSamples, - g_Const.brdfCutoff, + g_Const.restirDI.initialSamplingParams.numPrimaryLocalLightSamples, + g_Const.restirDI.initialSamplingParams.numPrimaryInfiniteLightSamples, + g_Const.restirDI.initialSamplingParams.numPrimaryEnvironmentSamples, + g_Const.restirDI.initialSamplingParams.numPrimaryBrdfSamples, + g_Const.restirDI.initialSamplingParams.brdfCutoff, 0.001f); RAB_LightSample lightSample; - RTXDI_Reservoir reservoir = RTXDI_SampleLightsForSurface(rng, tileRng, surface, - sampleParams, params, lightSample); + RTXDI_DIReservoir reservoir = RTXDI_SampleLightsForSurface(rng, tileRng, surface, + sampleParams, g_Const.lightBufferParams, g_Const.restirDI.initialSamplingParams.localLightSamplingMode, +#ifdef RTXDI_ENABLE_PRESAMPLING + g_Const.localLightsRISBufferSegmentParams, g_Const.environmentLightRISBufferSegmentParams, +#if RTXDI_REGIR_MODE != RTXDI_REGIR_MODE_DISABLED + g_Const.regir, +#endif +#endif + lightSample); - if (g_Const.enableInitialVisibility && RTXDI_IsValidReservoir(reservoir)) + if (g_Const.restirDI.initialSamplingParams.enableInitialVisibility && RTXDI_IsValidDIReservoir(reservoir)) { if (!RAB_GetConservativeVisibility(surface, lightSample)) { - RTXDI_StoreVisibilityInReservoir(reservoir, 0, true); + RTXDI_StoreVisibilityInDIReservoir(reservoir, 0, true); } } - RTXDI_StoreReservoir(reservoir, params, GlobalIndex, g_Const.initialOutputBufferIndex); + RTXDI_StoreDIReservoir(reservoir, g_Const.restirDI.reservoirBufferParams, GlobalIndex, g_Const.restirDI.bufferIndices.initialSamplingOutputBufferIndex); } \ No newline at end of file diff --git a/shaders/LightingPasses/ShadeSamples.hlsl b/shaders/LightingPasses/DIShadeSamples.hlsl similarity index 67% rename from shaders/LightingPasses/ShadeSamples.hlsl rename to shaders/LightingPasses/DIShadeSamples.hlsl index 423896f..546204f 100644 --- a/shaders/LightingPasses/ShadeSamples.hlsl +++ b/shaders/LightingPasses/DIShadeSamples.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -12,7 +12,10 @@ #include "RtxdiApplicationBridge.hlsli" -#include +#include +#if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED +#include "rtxdi/ReGIRSampling.hlsli" +#endif #ifdef WITH_NRD #define NRD_HEADER_ONLY @@ -33,25 +36,25 @@ void RayGen() uint2 GlobalIndex = DispatchRaysIndex().xy; #endif - const RTXDI_ResamplingRuntimeParameters params = g_Const.runtimeParams; + const RTXDI_RuntimeParameters params = g_Const.runtimeParams; - uint2 pixelPosition = RTXDI_ReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams); + uint2 pixelPosition = RTXDI_DIReservoirPosToPixelPos(GlobalIndex, params.activeCheckerboardField); RAB_Surface surface = RAB_GetGBufferSurface(pixelPosition, false); - RTXDI_Reservoir reservoir = RTXDI_LoadReservoir(params, GlobalIndex, g_Const.shadeInputBufferIndex); + RTXDI_DIReservoir reservoir = RTXDI_LoadDIReservoir(g_Const.restirDI.reservoirBufferParams, GlobalIndex, g_Const.restirDI.bufferIndices.shadingInputBufferIndex); float3 diffuse = 0; float3 specular = 0; float lightDistance = 0; float2 currLuminance = 0; - if (RTXDI_IsValidReservoir(reservoir)) + if (RTXDI_IsValidDIReservoir(reservoir)) { - RAB_LightInfo lightInfo = RAB_LoadLightInfo(RTXDI_GetReservoirLightIndex(reservoir), false); + RAB_LightInfo lightInfo = RAB_LoadLightInfo(RTXDI_GetDIReservoirLightIndex(reservoir), false); RAB_LightSample lightSample = RAB_SamplePolymorphicLight(lightInfo, - surface, RTXDI_GetReservoirSampleUV(reservoir)); + surface, RTXDI_GetDIReservoirSampleUV(reservoir)); bool needToStore = ShadeSurfaceWithLightSample(reservoir, surface, lightSample, /* previousFrameTLAS = */ false, /* enableVisibilityReuse = */ true, diffuse, specular, lightDistance); @@ -62,7 +65,7 @@ void RayGen() if (needToStore) { - RTXDI_StoreReservoir(reservoir, params, GlobalIndex, g_Const.shadeInputBufferIndex); + RTXDI_StoreDIReservoir(reservoir, g_Const.restirDI.reservoirBufferParams, GlobalIndex, g_Const.restirDI.bufferIndices.shadingInputBufferIndex); } } @@ -73,10 +76,10 @@ void RayGen() #if RTXDI_REGIR_MODE != RTXDI_REGIR_DISABLED if (g_Const.visualizeRegirCells) { - diffuse *= RTXDI_VisualizeReGIRCells(g_Const.runtimeParams, RAB_GetSurfaceWorldPos(surface)); + diffuse *= RTXDI_VisualizeReGIRCells(g_Const.regir, RAB_GetSurfaceWorldPos(surface)); } #endif StoreShadingOutput(GlobalIndex, pixelPosition, - surface.viewDepth, surface.roughness, diffuse, specular, lightDistance, true, g_Const.enableDenoiserInputPacking); + surface.viewDepth, surface.roughness, diffuse, specular, lightDistance, true, g_Const.restirDI.shadingParams.enableDenoiserInputPacking); } diff --git a/shaders/LightingPasses/DISpatialResampling.hlsl b/shaders/LightingPasses/DISpatialResampling.hlsl new file mode 100644 index 0000000..9a62fe6 --- /dev/null +++ b/shaders/LightingPasses/DISpatialResampling.hlsl @@ -0,0 +1,62 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + +#pragma pack_matrix(row_major) + +#include "RtxdiApplicationBridge.hlsli" + +#include + +#if USE_RAY_QUERY +[numthreads(RTXDI_SCREEN_SPACE_GROUP_SIZE, RTXDI_SCREEN_SPACE_GROUP_SIZE, 1)] +void main(uint2 GlobalIndex : SV_DispatchThreadID) +#else +[shader("raygeneration")] +void RayGen() +#endif +{ +#if !USE_RAY_QUERY + uint2 GlobalIndex = DispatchRaysIndex().xy; +#endif + + const RTXDI_RuntimeParameters params = g_Const.runtimeParams; + + uint2 pixelPosition = RTXDI_DIReservoirPosToPixelPos(GlobalIndex, params.activeCheckerboardField); + + RAB_RandomSamplerState rng = RAB_InitRandomSampler(pixelPosition, 3); + + RAB_Surface surface = RAB_GetGBufferSurface(pixelPosition, false); + + RTXDI_DIReservoir spatialResult = RTXDI_EmptyDIReservoir(); + + if (RAB_IsSurfaceValid(surface)) + { + RTXDI_DIReservoir centerSample = RTXDI_LoadDIReservoir(g_Const.restirDI.reservoirBufferParams, + GlobalIndex, g_Const.restirDI.bufferIndices.spatialResamplingInputBufferIndex); + + RTXDI_DISpatialResamplingParameters sparams; + sparams.sourceBufferIndex = g_Const.restirDI.bufferIndices.spatialResamplingInputBufferIndex; + sparams.numSamples = g_Const.restirDI.spatialResamplingParams.numSpatialSamples; + sparams.numDisocclusionBoostSamples = g_Const.restirDI.spatialResamplingParams.numDisocclusionBoostSamples; + sparams.targetHistoryLength = g_Const.restirDI.temporalResamplingParams.maxHistoryLength; + sparams.biasCorrectionMode = g_Const.restirDI.spatialResamplingParams.spatialBiasCorrection; + sparams.samplingRadius = g_Const.restirDI.spatialResamplingParams.spatialSamplingRadius; + sparams.depthThreshold = g_Const.restirDI.spatialResamplingParams.spatialDepthThreshold; + sparams.normalThreshold = g_Const.restirDI.spatialResamplingParams.spatialNormalThreshold; + sparams.enableMaterialSimilarityTest = true; + sparams.discountNaiveSamples = g_Const.restirDI.spatialResamplingParams.discountNaiveSamples; + + RAB_LightSample lightSample = (RAB_LightSample)0; + spatialResult = RTXDI_DISpatialResampling(pixelPosition, surface, centerSample, + rng, params, g_Const.restirDI.reservoirBufferParams, sparams, lightSample); + } + + RTXDI_StoreDIReservoir(spatialResult, g_Const.restirDI.reservoirBufferParams, GlobalIndex, g_Const.restirDI.bufferIndices.spatialResamplingOutputBufferIndex); +} \ No newline at end of file diff --git a/shaders/LightingPasses/TemporalResampling.hlsl b/shaders/LightingPasses/DITemporalResampling.hlsl similarity index 54% rename from shaders/LightingPasses/TemporalResampling.hlsl rename to shaders/LightingPasses/DITemporalResampling.hlsl index 8571a41..76b3455 100644 --- a/shaders/LightingPasses/TemporalResampling.hlsl +++ b/shaders/LightingPasses/DITemporalResampling.hlsl @@ -18,7 +18,7 @@ #include "RtxdiApplicationBridge.hlsli" -#include +#include #if USE_RAY_QUERY [numthreads(RTXDI_SCREEN_SPACE_GROUP_SIZE, RTXDI_SCREEN_SPACE_GROUP_SIZE, 1)] @@ -32,56 +32,57 @@ void RayGen() uint2 GlobalIndex = DispatchRaysIndex().xy; #endif - const RTXDI_ResamplingRuntimeParameters params = g_Const.runtimeParams; + const RTXDI_RuntimeParameters params = g_Const.runtimeParams; - uint2 pixelPosition = RTXDI_ReservoirPosToPixelPos(GlobalIndex, params); + uint2 pixelPosition = RTXDI_DIReservoirPosToPixelPos(GlobalIndex, params.activeCheckerboardField); RAB_RandomSamplerState rng = RAB_InitRandomSampler(pixelPosition, 2); RAB_Surface surface = RAB_GetGBufferSurface(pixelPosition, false); bool usePermutationSampling = false; - if (g_Const.enablePermutationSampling) + if (g_Const.restirDI.temporalResamplingParams.enablePermutationSampling) { // Permutation sampling makes more noise on thin, high-detail objects. usePermutationSampling = !IsComplexSurface(pixelPosition, surface); } - RTXDI_Reservoir temporalResult = RTXDI_EmptyReservoir(); + RTXDI_DIReservoir temporalResult = RTXDI_EmptyDIReservoir(); int2 temporalSamplePixelPos = -1; if (RAB_IsSurfaceValid(surface)) { - RTXDI_Reservoir curSample = RTXDI_LoadReservoir(params, - GlobalIndex, g_Const.initialOutputBufferIndex); + RTXDI_DIReservoir curSample = RTXDI_LoadDIReservoir(g_Const.restirDI.reservoirBufferParams, + GlobalIndex, g_Const.restirDI.bufferIndices.initialSamplingOutputBufferIndex); float3 motionVector = t_MotionVectors[pixelPosition].xyz; motionVector = convertMotionVectorToPixelSpace(g_Const.view, g_Const.prevView, pixelPosition, motionVector); - RTXDI_TemporalResamplingParameters tparams; + RTXDI_DITemporalResamplingParameters tparams; tparams.screenSpaceMotion = motionVector; - tparams.sourceBufferIndex = g_Const.temporalInputBufferIndex; - tparams.maxHistoryLength = g_Const.maxHistoryLength; - tparams.biasCorrectionMode = g_Const.temporalBiasCorrection; - tparams.depthThreshold = g_Const.temporalDepthThreshold; - tparams.normalThreshold = g_Const.temporalNormalThreshold; - tparams.enableVisibilityShortcut = g_Const.discardInvisibleSamples; + tparams.sourceBufferIndex = g_Const.restirDI.bufferIndices.temporalResamplingInputBufferIndex; + tparams.maxHistoryLength = g_Const.restirDI.temporalResamplingParams.maxHistoryLength; + tparams.biasCorrectionMode = g_Const.restirDI.temporalResamplingParams.temporalBiasCorrection; + tparams.depthThreshold = g_Const.restirDI.temporalResamplingParams.temporalDepthThreshold; + tparams.normalThreshold = g_Const.restirDI.temporalResamplingParams.temporalNormalThreshold; + tparams.enableVisibilityShortcut = g_Const.restirDI.temporalResamplingParams.discardInvisibleSamples; tparams.enablePermutationSampling = usePermutationSampling; + tparams.uniformRandomNumber = g_Const.restirDI.temporalResamplingParams.uniformRandomNumber; RAB_LightSample selectedLightSample = (RAB_LightSample)0; - temporalResult = RTXDI_TemporalResampling(pixelPosition, surface, curSample, - rng, tparams, params, temporalSamplePixelPos, selectedLightSample); + temporalResult = RTXDI_DITemporalResampling(pixelPosition, surface, curSample, + rng, params, g_Const.restirDI.reservoirBufferParams, tparams, temporalSamplePixelPos, selectedLightSample); } #ifdef RTXDI_ENABLE_BOILING_FILTER - if (g_Const.boilingFilterStrength > 0) + if (g_Const.restirDI.temporalResamplingParams.enableBoilingFilter) { - RTXDI_BoilingFilter(LocalIndex, g_Const.boilingFilterStrength, params, temporalResult); + RTXDI_BoilingFilter(LocalIndex, g_Const.restirDI.temporalResamplingParams.boilingFilterStrength, temporalResult); } #endif u_TemporalSamplePositions[GlobalIndex] = temporalSamplePixelPos; - RTXDI_StoreReservoir(temporalResult, params, GlobalIndex, g_Const.temporalOutputBufferIndex); + RTXDI_StoreDIReservoir(temporalResult, g_Const.restirDI.reservoirBufferParams, GlobalIndex, g_Const.restirDI.bufferIndices.temporalResamplingOutputBufferIndex); } \ No newline at end of file diff --git a/shaders/LightingPasses/GIFinalShading.hlsl b/shaders/LightingPasses/GIFinalShading.hlsl index 4fd1e11..5de25c0 100644 --- a/shaders/LightingPasses/GIFinalShading.hlsl +++ b/shaders/LightingPasses/GIFinalShading.hlsl @@ -38,7 +38,7 @@ float GetMISWeight(const SplitBrdf roughBrdf, const SplitBrdf trueBrdf, const fl RTXDI_GIReservoir LoadInitialSampleReservoir(int2 reservoirPosition, RAB_Surface primarySurface) { - const uint gbufferIndex = RTXDI_ReservoirPositionToPointer(g_Const.runtimeParams, reservoirPosition, 0); + const uint gbufferIndex = RTXDI_DIReservoirPositionToPointer(g_Const.restirGI.reservoirBufferParams, reservoirPosition, 0); const SecondaryGBufferData secondaryGBufferData = u_SecondaryGBuffer[gbufferIndex]; const float3 normal = octToNdirUnorm32(secondaryGBufferData.normal); @@ -60,15 +60,15 @@ void RayGen() #if !USE_RAY_QUERY uint2 GlobalIndex = DispatchRaysIndex().xy; #endif - uint2 pixelPosition = RTXDI_ReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams); + uint2 pixelPosition = RTXDI_DIReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams.activeCheckerboardField); if (any(pixelPosition > int2(g_Const.view.viewportSize))) return; const RAB_Surface primarySurface = RAB_GetGBufferSurface(pixelPosition, false); - const uint2 reservoirPosition = RTXDI_PixelPosToReservoirPos(pixelPosition, g_Const.runtimeParams); - const RTXDI_GIReservoir reservoir = RTXDI_LoadGIReservoir(g_Const.runtimeParams, reservoirPosition, g_Const.shadeInputBufferIndex); + const uint2 reservoirPosition = RTXDI_PixelPosToReservoirPos(pixelPosition, g_Const.runtimeParams.activeCheckerboardField); + const RTXDI_GIReservoir reservoir = RTXDI_LoadGIReservoir(g_Const.restirGI.reservoirBufferParams, reservoirPosition, g_Const.restirGI.bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex); float3 diffuse = 0; float3 specular = 0; @@ -78,7 +78,7 @@ void RayGen() float3 radiance = reservoir.radiance * reservoir.weightSum; float3 visibility = 1.0; - if (g_Const.giEnableFinalVisibility) + if (g_Const.restirGI.finalShadingParams.enableFinalVisibility) { visibility = GetFinalVisibility(SceneBVH, primarySurface, reservoir.position); } @@ -87,7 +87,7 @@ void RayGen() const SplitBrdf brdf = EvaluateBrdf(primarySurface, reservoir.position); - if (g_Const.giEnableFinalMIS) + if (g_Const.restirGI.finalShadingParams.enableFinalMIS) { const RTXDI_GIReservoir initialReservoir = LoadInitialSampleReservoir(reservoirPosition, primarySurface); const SplitBrdf brdf0 = EvaluateBrdf(primarySurface, initialReservoir.position); diff --git a/shaders/LightingPasses/GIFusedResampling.hlsl b/shaders/LightingPasses/GIFusedResampling.hlsl index e59dbc8..27258ef 100644 --- a/shaders/LightingPasses/GIFusedResampling.hlsl +++ b/shaders/LightingPasses/GIFusedResampling.hlsl @@ -30,14 +30,14 @@ void RayGen() #if !USE_RAY_QUERY uint2 GlobalIndex = DispatchRaysIndex().xy; #endif - uint2 pixelPosition = RTXDI_ReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams); + uint2 pixelPosition = RTXDI_DIReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams.activeCheckerboardField); RAB_RandomSamplerState rng = RAB_InitRandomSampler(GlobalIndex, 7); const RAB_Surface primarySurface = RAB_GetGBufferSurface(pixelPosition, false); - const uint2 reservoirPosition = RTXDI_PixelPosToReservoirPos(pixelPosition, g_Const.runtimeParams); - RTXDI_GIReservoir reservoir = RTXDI_LoadGIReservoir(g_Const.runtimeParams, reservoirPosition, g_Const.initialOutputBufferIndex); + const uint2 reservoirPosition = RTXDI_PixelPosToReservoirPos(pixelPosition, g_Const.runtimeParams.activeCheckerboardField); + RTXDI_GIReservoir reservoir = RTXDI_LoadGIReservoir(g_Const.restirGI.reservoirBufferParams, reservoirPosition, g_Const.restirGI.bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex); float3 motionVector = t_MotionVectors[pixelPosition].xyz; motionVector = convertMotionVectorToPixelSpace(g_Const.view, g_Const.prevView, pixelPosition, motionVector); @@ -46,30 +46,31 @@ void RayGen() RTXDI_GISpatioTemporalResamplingParameters stParams; stParams.screenSpaceMotion = motionVector; - stParams.sourceBufferIndex = g_Const.temporalInputBufferIndex; - stParams.maxHistoryLength = g_Const.maxHistoryLength; - stParams.biasCorrectionMode = g_Const.temporalBiasCorrection; - stParams.depthThreshold = g_Const.temporalDepthThreshold; - stParams.normalThreshold = g_Const.temporalNormalThreshold; - stParams.enablePermutationSampling = g_Const.enablePermutationSampling; - stParams.numSpatialSamples = g_Const.numSpatialSamples; - stParams.samplingRadius = g_Const.spatialSamplingRadius; - stParams.enableFallbackSampling = g_Const.enableFallbackSampling; + stParams.sourceBufferIndex = g_Const.restirGI.bufferIndices.temporalResamplingInputBufferIndex; + stParams.maxHistoryLength = g_Const.restirGI.temporalResamplingParams.maxHistoryLength; + stParams.biasCorrectionMode = g_Const.restirGI.temporalResamplingParams.temporalBiasCorrectionMode; + stParams.depthThreshold = g_Const.restirGI.temporalResamplingParams.depthThreshold; + stParams.normalThreshold = g_Const.restirGI.temporalResamplingParams.normalThreshold; + stParams.enablePermutationSampling = g_Const.restirGI.temporalResamplingParams.enablePermutationSampling; + stParams.enableFallbackSampling = g_Const.restirGI.temporalResamplingParams.enableFallbackSampling; + stParams.numSpatialSamples = g_Const.restirGI.spatialResamplingParams.numSpatialSamples; + stParams.samplingRadius = g_Const.restirGI.spatialResamplingParams.spatialSamplingRadius; + stParams.uniformRandomNumber = g_Const.restirGI.temporalResamplingParams.uniformRandomNumber; // Age threshold should vary. // This is to avoid to die a bunch of GI reservoirs at once at a disoccluded area. - stParams.maxReservoirAge = g_Const.giReservoirMaxAge * (0.5 + RAB_GetNextRandom(rng) * 0.5); + stParams.maxReservoirAge = g_Const.restirGI.temporalResamplingParams.maxReservoirAge * (0.5 + RAB_GetNextRandom(rng) * 0.5); // Execute resampling. - reservoir = RTXDI_GISpatioTemporalResampling(pixelPosition, primarySurface, reservoir, rng, stParams, g_Const.runtimeParams); + reservoir = RTXDI_GISpatioTemporalResampling(pixelPosition, primarySurface, reservoir, rng, g_Const.runtimeParams, g_Const.restirGI.reservoirBufferParams, stParams); } #ifdef RTXDI_ENABLE_BOILING_FILTER - if (g_Const.boilingFilterStrength > 0) + if (g_Const.restirGI.temporalResamplingParams.enableBoilingFilter) { - RTXDI_GIBoilingFilter(LocalIndex, g_Const.boilingFilterStrength, g_Const.runtimeParams, reservoir); + RTXDI_GIBoilingFilter(LocalIndex, g_Const.restirGI.temporalResamplingParams.boilingFilterStrength, reservoir); } #endif - RTXDI_StoreGIReservoir(reservoir, g_Const.runtimeParams, reservoirPosition, g_Const.spatialOutputBufferIndex); + RTXDI_StoreGIReservoir(reservoir, g_Const.restirGI.reservoirBufferParams, reservoirPosition, g_Const.restirGI.bufferIndices.spatialResamplingOutputBufferIndex); } diff --git a/shaders/LightingPasses/GISpatialResampling.hlsl b/shaders/LightingPasses/GISpatialResampling.hlsl index 21e81b9..0ce5992 100644 --- a/shaders/LightingPasses/GISpatialResampling.hlsl +++ b/shaders/LightingPasses/GISpatialResampling.hlsl @@ -26,7 +26,7 @@ void RayGen() #if !USE_RAY_QUERY uint2 GlobalIndex = DispatchRaysIndex().xy; #endif - uint2 pixelPosition = RTXDI_ReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams); + uint2 pixelPosition = RTXDI_DIReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams.activeCheckerboardField); if (any(pixelPosition > int2(g_Const.view.viewportSize))) return; @@ -35,22 +35,22 @@ void RayGen() const RAB_Surface primarySurface = RAB_GetGBufferSurface(pixelPosition, false); - const uint2 reservoirPosition = RTXDI_PixelPosToReservoirPos(pixelPosition, g_Const.runtimeParams); - RTXDI_GIReservoir reservoir = RTXDI_LoadGIReservoir(g_Const.runtimeParams, reservoirPosition, g_Const.spatialInputBufferIndex); + const uint2 reservoirPosition = RTXDI_PixelPosToReservoirPos(pixelPosition, g_Const.runtimeParams.activeCheckerboardField); + RTXDI_GIReservoir reservoir = RTXDI_LoadGIReservoir(g_Const.restirGI.reservoirBufferParams, reservoirPosition, g_Const.restirGI.bufferIndices.spatialResamplingInputBufferIndex); if (RAB_IsSurfaceValid(primarySurface)) { RTXDI_GISpatialResamplingParameters sparams; - sparams.sourceBufferIndex = g_Const.spatialInputBufferIndex; - sparams.biasCorrectionMode = g_Const.spatialBiasCorrection; - sparams.depthThreshold = g_Const.spatialDepthThreshold; - sparams.normalThreshold = g_Const.spatialNormalThreshold; - sparams.numSamples = g_Const.numSpatialSamples; - sparams.samplingRadius = g_Const.spatialSamplingRadius; + sparams.sourceBufferIndex = g_Const.restirGI.bufferIndices.spatialResamplingInputBufferIndex; + sparams.biasCorrectionMode = g_Const.restirGI.spatialResamplingParams.spatialBiasCorrectionMode; + sparams.depthThreshold = g_Const.restirGI.spatialResamplingParams.spatialDepthThreshold; + sparams.normalThreshold = g_Const.restirGI.spatialResamplingParams.spatialNormalThreshold; + sparams.numSamples = g_Const.restirGI.spatialResamplingParams.numSpatialSamples; + sparams.samplingRadius = g_Const.restirGI.spatialResamplingParams.spatialSamplingRadius; // Execute resampling. - reservoir = RTXDI_GISpatialResampling(pixelPosition, primarySurface, reservoir, rng, sparams, g_Const.runtimeParams); + reservoir = RTXDI_GISpatialResampling(pixelPosition, primarySurface, reservoir, rng, g_Const.runtimeParams, g_Const.restirGI.reservoirBufferParams, sparams); } - RTXDI_StoreGIReservoir(reservoir, g_Const.runtimeParams, reservoirPosition, g_Const.spatialOutputBufferIndex); + RTXDI_StoreGIReservoir(reservoir, g_Const.restirGI.reservoirBufferParams, reservoirPosition, g_Const.restirGI.bufferIndices.spatialResamplingOutputBufferIndex); } diff --git a/shaders/LightingPasses/GITemporalResampling.hlsl b/shaders/LightingPasses/GITemporalResampling.hlsl index 7e34f2b..b7084be 100644 --- a/shaders/LightingPasses/GITemporalResampling.hlsl +++ b/shaders/LightingPasses/GITemporalResampling.hlsl @@ -30,14 +30,14 @@ void RayGen() #if !USE_RAY_QUERY uint2 GlobalIndex = DispatchRaysIndex().xy; #endif - uint2 pixelPosition = RTXDI_ReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams); + uint2 pixelPosition = RTXDI_DIReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams.activeCheckerboardField); RAB_RandomSamplerState rng = RAB_InitRandomSampler(GlobalIndex, 7); const RAB_Surface primarySurface = RAB_GetGBufferSurface(pixelPosition, false); - const uint2 reservoirPosition = RTXDI_PixelPosToReservoirPos(pixelPosition, g_Const.runtimeParams); - RTXDI_GIReservoir reservoir = RTXDI_LoadGIReservoir(g_Const.runtimeParams, reservoirPosition, g_Const.initialOutputBufferIndex); + const uint2 reservoirPosition = RTXDI_PixelPosToReservoirPos(pixelPosition, g_Const.runtimeParams.activeCheckerboardField); + RTXDI_GIReservoir reservoir = RTXDI_LoadGIReservoir(g_Const.restirGI.reservoirBufferParams, reservoirPosition, g_Const.restirGI.bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex); float3 motionVector = t_MotionVectors[pixelPosition].xyz; motionVector = convertMotionVectorToPixelSpace(g_Const.view, g_Const.prevView, pixelPosition, motionVector); @@ -46,28 +46,29 @@ void RayGen() RTXDI_GITemporalResamplingParameters tParams; tParams.screenSpaceMotion = motionVector; - tParams.sourceBufferIndex = g_Const.temporalInputBufferIndex; - tParams.maxHistoryLength = g_Const.maxHistoryLength; - tParams.biasCorrectionMode = g_Const.temporalBiasCorrection; - tParams.depthThreshold = g_Const.temporalDepthThreshold; - tParams.normalThreshold = g_Const.temporalNormalThreshold; - tParams.enablePermutationSampling = g_Const.enablePermutationSampling; - tParams.enableFallbackSampling = g_Const.enableFallbackSampling; + tParams.sourceBufferIndex = g_Const.restirGI.bufferIndices.temporalResamplingInputBufferIndex; + tParams.maxHistoryLength = g_Const.restirGI.temporalResamplingParams.maxHistoryLength; + tParams.biasCorrectionMode = g_Const.restirGI.temporalResamplingParams.temporalBiasCorrectionMode; + tParams.depthThreshold = g_Const.restirGI.temporalResamplingParams.depthThreshold; + tParams.normalThreshold = g_Const.restirGI.temporalResamplingParams.normalThreshold; + tParams.enablePermutationSampling = g_Const.restirGI.temporalResamplingParams.enablePermutationSampling; + tParams.enableFallbackSampling = g_Const.restirGI.temporalResamplingParams.enableFallbackSampling; + tParams.uniformRandomNumber = g_Const.restirGI.temporalResamplingParams.uniformRandomNumber; // Age threshold should vary. // This is to avoid to die a bunch of GI reservoirs at once at a disoccluded area. - tParams.maxReservoirAge = g_Const.giReservoirMaxAge * (0.5 + RAB_GetNextRandom(rng) * 0.5); + tParams.maxReservoirAge = g_Const.restirGI.temporalResamplingParams.maxReservoirAge * (0.5 + RAB_GetNextRandom(rng) * 0.5); // Execute resampling. - reservoir = RTXDI_GITemporalResampling(pixelPosition, primarySurface, reservoir, rng, tParams, g_Const.runtimeParams); + reservoir = RTXDI_GITemporalResampling(pixelPosition, primarySurface, reservoir, rng, g_Const.runtimeParams, g_Const.restirGI.reservoirBufferParams, tParams); } #ifdef RTXDI_ENABLE_BOILING_FILTER - if (g_Const.boilingFilterStrength > 0) + if (g_Const.restirGI.temporalResamplingParams.enableBoilingFilter) { - RTXDI_GIBoilingFilter(LocalIndex, g_Const.boilingFilterStrength, g_Const.runtimeParams, reservoir); + RTXDI_GIBoilingFilter(LocalIndex, g_Const.restirGI.temporalResamplingParams.boilingFilterStrength, reservoir); } #endif - RTXDI_StoreGIReservoir(reservoir, g_Const.runtimeParams, reservoirPosition, g_Const.temporalOutputBufferIndex); + RTXDI_StoreGIReservoir(reservoir, g_Const.restirGI.reservoirBufferParams, reservoirPosition, g_Const.restirGI.bufferIndices.temporalResamplingOutputBufferIndex); } diff --git a/shaders/LightingPasses/PresampleEnvironmentMap.hlsl b/shaders/LightingPasses/PresampleEnvironmentMap.hlsl index 359a6ef..46f65f7 100644 --- a/shaders/LightingPasses/PresampleEnvironmentMap.hlsl +++ b/shaders/LightingPasses/PresampleEnvironmentMap.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -10,7 +10,7 @@ #include "RtxdiApplicationBridge.hlsli" -#include +#include [numthreads(RTXDI_PRESAMPLING_GROUP_SIZE, 1, 1)] void main(uint2 GlobalIndex : SV_DispatchThreadID) @@ -23,5 +23,5 @@ void main(uint2 GlobalIndex : SV_DispatchThreadID) g_Const.environmentPdfTextureSize, GlobalIndex.y, GlobalIndex.x, - g_Const.runtimeParams.environmentLightParams); + g_Const.environmentLightRISBufferSegmentParams); } \ No newline at end of file diff --git a/shaders/LightingPasses/PresampleLights.hlsl b/shaders/LightingPasses/PresampleLights.hlsl index 90b7129..79ddd8c 100644 --- a/shaders/LightingPasses/PresampleLights.hlsl +++ b/shaders/LightingPasses/PresampleLights.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -10,7 +10,7 @@ #include "RtxdiApplicationBridge.hlsli" -#include +#include [numthreads(RTXDI_PRESAMPLING_GROUP_SIZE, 1, 1)] void main(uint2 GlobalIndex : SV_DispatchThreadID) @@ -23,5 +23,6 @@ void main(uint2 GlobalIndex : SV_DispatchThreadID) g_Const.localLightPdfTextureSize, GlobalIndex.y, GlobalIndex.x, - g_Const.runtimeParams); + g_Const.lightBufferParams.localLightBufferRegion, + g_Const.localLightsRISBufferSegmentParams); } \ No newline at end of file diff --git a/shaders/LightingPasses/PresampleReGIR.hlsl b/shaders/LightingPasses/PresampleReGIR.hlsl index 8a9c68d..65f2e99 100644 --- a/shaders/LightingPasses/PresampleReGIR.hlsl +++ b/shaders/LightingPasses/PresampleReGIR.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -12,16 +12,13 @@ #include "RtxdiApplicationBridge.hlsli" -#include +#include [numthreads(256, 1, 1)] void main(uint GlobalIndex : SV_DispatchThreadID) { - const RTXDI_ResamplingRuntimeParameters params = g_Const.runtimeParams; - RAB_RandomSamplerState rng = RAB_InitRandomSampler(uint2(GlobalIndex & 0xfff, GlobalIndex >> 12), 1); RAB_RandomSamplerState coherentRng = RAB_InitRandomSampler(uint2(GlobalIndex >> 8, 0), 1); - RTXDI_PresampleLocalLightsForReGIR(rng, coherentRng, GlobalIndex, - g_Const.numRegirBuildSamples, params); + RTXDI_PresampleLocalLightsForReGIR(rng, coherentRng, GlobalIndex, g_Const.lightBufferParams.localLightBufferRegion, g_Const.localLightsRISBufferSegmentParams, g_Const.regir); } \ No newline at end of file diff --git a/shaders/LightingPasses/RtxdiApplicationBridge.hlsli b/shaders/LightingPasses/RtxdiApplicationBridge.hlsli index 5348e7c..722f580 100644 --- a/shaders/LightingPasses/RtxdiApplicationBridge.hlsli +++ b/shaders/LightingPasses/RtxdiApplicationBridge.hlsli @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -67,7 +67,7 @@ Texture2D t_LocalLightPdfTexture : register(t24); StructuredBuffer t_GeometryInstanceToLight : register(t25); // Screen-sized UAVs -RWStructuredBuffer u_LightReservoirs : register(u0); +RWStructuredBuffer u_LightReservoirs : register(u0); RWTexture2D u_DiffuseLighting : register(u1); RWTexture2D u_SpecularLighting : register(u2); RWTexture2D u_TemporalSamplePositions : register(u3); @@ -323,13 +323,13 @@ float3 GetFinalVisibility(RaytracingAccelerationStructure accelStruct, RAB_Surfa uint instanceMask = INSTANCE_MASK_OPAQUE; uint rayFlags = RAY_FLAG_NONE; - if (g_Const.enableAlphaTestedGeometry) + if (g_Const.sceneConstants.enableAlphaTestedGeometry) instanceMask |= INSTANCE_MASK_ALPHA_TESTED; - if (g_Const.enableTransparentGeometry) + if (g_Const.sceneConstants.enableTransparentGeometry) instanceMask |= INSTANCE_MASK_TRANSPARENT; - if (!g_Const.enableTransparentGeometry && !g_Const.enableAlphaTestedGeometry) + if (!g_Const.sceneConstants.enableTransparentGeometry && !g_Const.sceneConstants.enableAlphaTestedGeometry) rayFlags |= RAY_FLAG_CULL_NON_OPAQUE; RayPayload payload = (RayPayload)0; @@ -505,7 +505,7 @@ float RAB_GetNextRandom(inout RAB_RandomSamplerState rng) float2 RAB_GetEnvironmentMapRandXYFromDir(float3 worldDir) { float2 uv = directionToEquirectUV(worldDir); - uv.x -= g_Const.environmentRotation; + uv.x -= g_Const.sceneConstants.environmentRotation; uv = frac(uv); return uv; } @@ -514,7 +514,7 @@ float2 RAB_GetEnvironmentMapRandXYFromDir(float3 worldDir) // relative to all the other possible directions, based on the environment map pdf texture. float RAB_EvaluateEnvironmentMapSamplingPdf(float3 L) { - if (!g_Const.environmentMapImportanceSampling) + if (!g_Const.restirDI.initialSamplingParams.environmentMapImportanceSampling) return 1.0; float2 uv = RAB_GetEnvironmentMapRandXYFromDir(L); @@ -536,7 +536,7 @@ float RAB_EvaluateEnvironmentMapSamplingPdf(float3 L) } // Evaluates pdf for a particular light -float RAB_EvaluateLocalLightSourcePdf(RTXDI_ResamplingRuntimeParameters params, uint lightIndex) +float RAB_EvaluateLocalLightSourcePdf(uint lightIndex) { uint2 pdfTextureSize = g_Const.localLightPdfTextureSize.xy; uint2 texelPosition = RTXDI_LinearIndexToZCurve(lightIndex); @@ -766,16 +766,16 @@ bool RAB_AreMaterialsSimilar(RAB_Surface a, RAB_Surface b) float3 GetEnvironmentRadiance(float3 direction) { - if (!g_Const.enableEnvironmentMap) + if (!g_Const.sceneConstants.enableEnvironmentMap) return 0; - Texture2D environmentLatLongMap = t_BindlessTextures[g_Const.environmentMapTextureIndex]; + Texture2D environmentLatLongMap = t_BindlessTextures[g_Const.sceneConstants.environmentMapTextureIndex]; float2 uv = directionToEquirectUV(direction); - uv.x -= g_Const.environmentRotation; + uv.x -= g_Const.sceneConstants.environmentRotation; float3 environmentRadiance = environmentLatLongMap.SampleLevel(s_EnvironmentSampler, uv, 0).rgb; - environmentRadiance *= g_Const.environmentScale; + environmentRadiance *= g_Const.sceneConstants.environmentScale; return environmentRadiance; } @@ -789,7 +789,7 @@ bool IsComplexSurface(int2 pixelPosition, RAB_Surface surface) // Detect that increase here and disable permutation sampling based on a threshold. // Other classification methods can be employed for better quality. float originalRoughness = t_DenoiserNormalRoughness[pixelPosition].a; - return originalRoughness < (surface.roughness * g_Const.permutationSamplingThreshold); + return originalRoughness < (surface.roughness * g_Const.restirDI.temporalResamplingParams.permutationSamplingThreshold); } uint getLightIndex(uint instanceID, uint geometryIndex, uint primitiveIndex) diff --git a/shaders/LightingPasses/ShadeSecondarySurfaces.hlsl b/shaders/LightingPasses/ShadeSecondarySurfaces.hlsl index 9e0649f..ef62af4 100644 --- a/shaders/LightingPasses/ShadeSecondarySurfaces.hlsl +++ b/shaders/LightingPasses/ShadeSecondarySurfaces.hlsl @@ -16,7 +16,8 @@ #include "RtxdiApplicationBridge.hlsli" -#include +#include +#include #include #ifdef WITH_NRD @@ -26,13 +27,6 @@ #include "ShadingHelpers.hlsli" -#ifdef WITH_RTXGI -#include "RtxgiHelpers.hlsli" -StructuredBuffer t_DDGIVolumes : register(t40 VK_DESCRIPTOR_SET(2)); -StructuredBuffer t_DDGIVolumeResourceIndices : register(t41 VK_DESCRIPTOR_SET(2)); -SamplerState s_ProbeSampler : register(s40 VK_DESCRIPTOR_SET(2)); -#endif - static const float c_MaxIndirectRadiance = 10; #if USE_RAY_QUERY @@ -46,7 +40,7 @@ void RayGen() #if !USE_RAY_QUERY uint2 GlobalIndex = DispatchRaysIndex().xy; #endif - uint2 pixelPosition = RTXDI_ReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams); + uint2 pixelPosition = RTXDI_DIReservoirPosToPixelPos(GlobalIndex, g_Const.runtimeParams.activeCheckerboardField); if (any(pixelPosition > int2(g_Const.view.viewportSize))) return; @@ -54,8 +48,8 @@ void RayGen() RAB_RandomSamplerState rng = RAB_InitRandomSampler(GlobalIndex, 6); RAB_RandomSamplerState tileRng = RAB_InitRandomSampler(GlobalIndex / RTXDI_TILE_SIZE_IN_PIXELS, 1); - const RTXDI_ResamplingRuntimeParameters params = g_Const.runtimeParams; - const uint gbufferIndex = RTXDI_ReservoirPositionToPointer(params, GlobalIndex, 0); + const RTXDI_RuntimeParameters params = g_Const.runtimeParams; + const uint gbufferIndex = RTXDI_DIReservoirPositionToPointer(g_Const.restirDI.reservoirBufferParams, GlobalIndex, 0); RAB_Surface primarySurface = RAB_GetGBufferSurface(pixelPosition, false); @@ -87,19 +81,25 @@ void RayGen() if (isValidSecondarySurface && !isEnvironmentMap) { RTXDI_SampleParameters sampleParams = RTXDI_InitSampleParameters( - g_Const.numIndirectRegirSamples, - g_Const.numIndirectLocalLightSamples, - g_Const.numIndirectInfiniteLightSamples, - g_Const.numIndirectEnvironmentSamples, + g_Const.brdfPT.secondarySurfaceReSTIRDIParams.initialSamplingParams.numPrimaryLocalLightSamples, + g_Const.brdfPT.secondarySurfaceReSTIRDIParams.initialSamplingParams.numPrimaryInfiniteLightSamples, + g_Const.brdfPT.secondarySurfaceReSTIRDIParams.initialSamplingParams.numPrimaryEnvironmentSamples, 0, // numBrdfSamples 0.f, // brdfCutoff 0.f); // brdfMinRayT RAB_LightSample lightSample; - RTXDI_Reservoir reservoir = RTXDI_SampleLightsForSurface(rng, tileRng, secondarySurface, - sampleParams, params, lightSample); + RTXDI_DIReservoir reservoir = RTXDI_SampleLightsForSurface(rng, tileRng, secondarySurface, + sampleParams, g_Const.lightBufferParams, g_Const.brdfPT.secondarySurfaceReSTIRDIParams.initialSamplingParams.localLightSamplingMode, +#if RTXDI_ENABLE_PRESAMPLING + g_Const.localLightsRISBufferSegmentParams, g_Const.environmentLightRISBufferSegmentParams, +#if RTXDI_REGIR_MODE != RTXDI_REGIR_MODE_DISABLED + g_Const.regir, +#endif +#endif + lightSample); - if (g_Const.numSecondarySamples) + if (g_Const.brdfPT.enableSecondaryResampling) { // Try to find this secondary surface in the G-buffer. If found, resample the lights // from that G-buffer surface into the reservoir using the spatial resampling function. @@ -112,19 +112,20 @@ void RayGen() int2 secondaryPixelPos = int2(secondaryClipPos.xy * g_Const.view.clipToWindowScale + g_Const.view.clipToWindowBias); secondarySurface.viewDepth = secondaryClipPos.w; - RTXDI_SpatialResamplingParameters sparams; - sparams.sourceBufferIndex = g_Const.shadeInputBufferIndex; - sparams.numSamples = g_Const.numSecondarySamples; + RTXDI_DISpatialResamplingParameters sparams; + sparams.sourceBufferIndex = g_Const.restirDI.bufferIndices.shadingInputBufferIndex; + sparams.numSamples = g_Const.brdfPT.secondarySurfaceReSTIRDIParams.spatialResamplingParams.numSpatialSamples; sparams.numDisocclusionBoostSamples = 0; sparams.targetHistoryLength = 0; - sparams.biasCorrectionMode = g_Const.secondaryBiasCorrection; - sparams.samplingRadius = g_Const.secondarySamplingRadius; - sparams.depthThreshold = g_Const.secondaryDepthThreshold; - sparams.normalThreshold = g_Const.secondaryNormalThreshold; + sparams.biasCorrectionMode = g_Const.brdfPT.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialBiasCorrection; + sparams.samplingRadius = g_Const.brdfPT.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialSamplingRadius; + sparams.depthThreshold = g_Const.brdfPT.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialDepthThreshold; + sparams.normalThreshold = g_Const.brdfPT.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialNormalThreshold; sparams.enableMaterialSimilarityTest = false; + sparams.discountNaiveSamples = false; - reservoir = RTXDI_SpatialResampling(secondaryPixelPos, secondarySurface, reservoir, - rng, sparams, params, lightSample); + reservoir = RTXDI_DISpatialResampling(secondaryPixelPos, secondarySurface, reservoir, + rng, params, g_Const.restirDI.reservoirBufferParams, sparams, lightSample); } } @@ -140,26 +141,10 @@ void RayGen() float indirectLuminance = calcLuminance(radiance); if (indirectLuminance > c_MaxIndirectRadiance) radiance *= c_MaxIndirectRadiance / indirectLuminance; - -#ifdef WITH_RTXGI - if (g_Const.numRtxgiVolumes) - { - float3 indirectIrradiance = GetIrradianceFromDDGI( - secondarySurface.worldPos, - secondarySurface.normal, - primarySurface.worldPos, - g_Const.numRtxgiVolumes, - t_DDGIVolumes, - t_DDGIVolumeResourceIndices, - s_ProbeSampler); - - radiance += indirectIrradiance * secondarySurface.diffuseAlbedo; - } -#endif } bool outputShadingResult = true; - if (g_Const.enableReSTIRIndirect) + if (g_Const.brdfPT.enableReSTIRGI) { RTXDI_GIReservoir reservoir = RTXDI_EmptyGIReservoir(); @@ -173,8 +158,8 @@ void RayGen() reservoir = RTXDI_MakeGIReservoir(secondarySurface.worldPos, secondarySurface.normal, radiance, secondaryGBufferData.pdf); } - uint2 reservoirPosition = RTXDI_PixelPosToReservoirPos(pixelPosition, g_Const.runtimeParams); - RTXDI_StoreGIReservoir(reservoir, g_Const.runtimeParams, reservoirPosition, g_Const.initialOutputBufferIndex); + uint2 reservoirPosition = RTXDI_PixelPosToReservoirPos(pixelPosition, g_Const.runtimeParams.activeCheckerboardField); + RTXDI_StoreGIReservoir(reservoir, g_Const.restirGI.reservoirBufferParams, reservoirPosition, g_Const.restirGI.bufferIndices.secondarySurfaceReSTIRDIOutputBufferIndex); // Save the initial sample radiance for MIS in the final shading pass secondaryGBufferData.emission = outputShadingResult ? 0 : radiance; diff --git a/shaders/LightingPasses/ShadingHelpers.hlsli b/shaders/LightingPasses/ShadingHelpers.hlsli index 65b901a..344d52e 100644 --- a/shaders/LightingPasses/ShadingHelpers.hlsli +++ b/shaders/LightingPasses/ShadingHelpers.hlsli @@ -11,10 +11,10 @@ #ifndef SHADING_HELPERS_HLSLI #define SHADING_HELPERS_HLSLI -#ifdef RESERVOIR_HLSLI +#ifdef RTXDI_DIRESERVOIR_HLSLI bool ShadeSurfaceWithLightSample( - inout RTXDI_Reservoir reservoir, + inout RTXDI_DIReservoir reservoir, RAB_Surface surface, RAB_LightSample lightSample, bool previousFrameTLAS, @@ -31,18 +31,18 @@ bool ShadeSurfaceWithLightSample( return false; bool needToStore = false; - if (g_Const.enableFinalVisibility) + if (g_Const.restirDI.shadingParams.enableFinalVisibility) { float3 visibility = 0; bool visibilityReused = false; - if (g_Const.reuseFinalVisibility && enableVisibilityReuse) + if (g_Const.restirDI.shadingParams.reuseFinalVisibility && enableVisibilityReuse) { RTXDI_VisibilityReuseParameters rparams; - rparams.maxAge = g_Const.finalVisibilityMaxAge; - rparams.maxDistance = g_Const.finalVisibilityMaxDistance; + rparams.maxAge = g_Const.restirDI.shadingParams.finalVisibilityMaxAge; + rparams.maxDistance = g_Const.restirDI.shadingParams.finalVisibilityMaxDistance; - visibilityReused = RTXDI_GetReservoirVisibility(reservoir, rparams, visibility); + visibilityReused = RTXDI_GetDIReservoirVisibility(reservoir, rparams, visibility); } if (!visibilityReused) @@ -51,14 +51,14 @@ bool ShadeSurfaceWithLightSample( visibility = GetFinalVisibility(PrevSceneBVH, surface, lightSample.position); else visibility = GetFinalVisibility(SceneBVH, surface, lightSample.position); - RTXDI_StoreVisibilityInReservoir(reservoir, visibility, g_Const.discardInvisibleSamples); + RTXDI_StoreVisibilityInDIReservoir(reservoir, visibility, g_Const.restirDI.temporalResamplingParams.discardInvisibleSamples); needToStore = true; } lightSample.radiance *= visibility; } - lightSample.radiance *= RTXDI_GetReservoirInvPdf(reservoir) / lightSample.solidAnglePdf; + lightSample.radiance *= RTXDI_GetDIReservoirInvPdf(reservoir) / lightSample.solidAnglePdf; if (any(lightSample.radiance > 0)) { @@ -73,7 +73,7 @@ bool ShadeSurfaceWithLightSample( return needToStore; } -#endif // RESERVOIR_HLSLI +#endif // RTXDI_DIRESERVOIR_HLSLI float3 DemodulateSpecular(float3 surfaceSpecularF0, float3 specular) { diff --git a/shaders/LightingPasses/SpatialResampling.hlsl b/shaders/LightingPasses/SpatialResampling.hlsl deleted file mode 100644 index b836b66..0000000 --- a/shaders/LightingPasses/SpatialResampling.hlsl +++ /dev/null @@ -1,61 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. - # - # NVIDIA CORPORATION and its licensors retain all intellectual property - # and proprietary rights in and to this software, related documentation - # and any modifications thereto. Any use, reproduction, disclosure or - # distribution of this software and related documentation without an express - # license agreement from NVIDIA CORPORATION is strictly prohibited. - **************************************************************************/ - -#pragma pack_matrix(row_major) - -#include "RtxdiApplicationBridge.hlsli" - -#include - -#if USE_RAY_QUERY -[numthreads(RTXDI_SCREEN_SPACE_GROUP_SIZE, RTXDI_SCREEN_SPACE_GROUP_SIZE, 1)] -void main(uint2 GlobalIndex : SV_DispatchThreadID) -#else -[shader("raygeneration")] -void RayGen() -#endif -{ -#if !USE_RAY_QUERY - uint2 GlobalIndex = DispatchRaysIndex().xy; -#endif - - const RTXDI_ResamplingRuntimeParameters params = g_Const.runtimeParams; - - uint2 pixelPosition = RTXDI_ReservoirPosToPixelPos(GlobalIndex, params); - - RAB_RandomSamplerState rng = RAB_InitRandomSampler(pixelPosition, 3); - - RAB_Surface surface = RAB_GetGBufferSurface(pixelPosition, false); - - RTXDI_Reservoir spatialResult = RTXDI_EmptyReservoir(); - - if (RAB_IsSurfaceValid(surface)) - { - RTXDI_Reservoir centerSample = RTXDI_LoadReservoir(params, - GlobalIndex, g_Const.temporalOutputBufferIndex); - - RTXDI_SpatialResamplingParameters sparams; - sparams.sourceBufferIndex = g_Const.spatialInputBufferIndex; - sparams.numSamples = g_Const.numSpatialSamples; - sparams.numDisocclusionBoostSamples = g_Const.numDisocclusionBoostSamples; - sparams.targetHistoryLength = g_Const.maxHistoryLength; - sparams.biasCorrectionMode = g_Const.spatialBiasCorrection; - sparams.samplingRadius = g_Const.spatialSamplingRadius; - sparams.depthThreshold = g_Const.spatialDepthThreshold; - sparams.normalThreshold = g_Const.spatialNormalThreshold; - sparams.enableMaterialSimilarityTest = true; - - RAB_LightSample lightSample = (RAB_LightSample)0; - spatialResult = RTXDI_SpatialResampling(pixelPosition, surface, centerSample, - rng, sparams, params, lightSample); - } - - RTXDI_StoreReservoir(spatialResult, params, GlobalIndex, g_Const.spatialOutputBufferIndex); -} \ No newline at end of file diff --git a/shaders/PrepareLights.hlsl b/shaders/PrepareLights.hlsl index 309dd8e..f93a446 100644 --- a/shaders/PrepareLights.hlsl +++ b/shaders/PrepareLights.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/shaders/PreprocessEnvironmentMap.hlsl b/shaders/PreprocessEnvironmentMap.hlsl index 858020a..70c563a 100644 --- a/shaders/PreprocessEnvironmentMap.hlsl +++ b/shaders/PreprocessEnvironmentMap.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/shaders/RTXGI/DDGIShaderConfig.h b/shaders/RTXGI/DDGIShaderConfig.h deleted file mode 100644 index b9fd585..0000000 --- a/shaders/RTXGI/DDGIShaderConfig.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef RTXGI_COORDINATE_SYSTEM -#define RTXGI_COORDINATE_SYSTEM RTXGI_COORDINATE_SYSTEM_RIGHT -#endif -#define RTXGI_DDGI_BINDLESS_RESOURCES 0 -#define RTXGI_DDGI_BLEND_SHARED_MEMORY 1 -#define RTXGI_DDGI_BLEND_RAYS_PER_PROBE 144 -#define RTXGI_DDGI_DEBUG_BORDER_COPY_INDEXING 0 -#define RTXGI_DDGI_DEBUG_OCTAHEDRAL_INDEXING 0 -#define RTXGI_DDGI_DEBUG_PROBE_INDEXING 0 -#define RTXGI_DDGI_PROBE_NUM_TEXELS 14 -#define RTXGI_DDGI_RESOURCE_MANAGEMENT 1 -#define RTXGI_DDGI_SHADER_REFLECTION 0 diff --git a/shaders/RTXGI/ProbeDebug.hlsl b/shaders/RTXGI/ProbeDebug.hlsl deleted file mode 100644 index 93d1e6b..0000000 --- a/shaders/RTXGI/ProbeDebug.hlsl +++ /dev/null @@ -1,147 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. - # - # NVIDIA CORPORATION and its licensors retain all intellectual property - # and proprietary rights in and to this software, related documentation - # and any modifications thereto. Any use, reproduction, disclosure or - # distribution of this software and related documentation without an express - # license agreement from NVIDIA CORPORATION is strictly prohibited. - **************************************************************************/ - -#pragma pack_matrix(row_major) - -#include "DDGIShaderConfig.h" - -#define HLSL -#include - -#include - -#include "../ShaderParameters.h" -#include "../GBufferHelpers.hlsli" -#include "../SceneGeometry.hlsli" - -ConstantBuffer g_Const : register(b0); -RaytracingAccelerationStructure ProbeBVH : register(t0); -StructuredBuffer t_DDGIVolumes : register(t1); -StructuredBuffer t_DDGIVolumeResourceIndices : register(t2); -Texture2D t_GBufferDepth : register(t3); -RWTexture2D u_CompositedColor : register(u0); -SamplerState s_ProbeSampler : register(s0); - -[numthreads(16, 16, 1)] -void main(uint2 pixelPosition : SV_DispatchThreadID) -{ - float depth = t_GBufferDepth[pixelPosition]; - if (depth == 0) - depth = 1000; - - float3 surfaceWorldPos = viewDepthToWorldPos(g_Const.view, pixelPosition, depth); - - RayDesc ray; - ray.Origin = g_Const.view.cameraDirectionOrPosition.xyz; - ray.Direction = normalize(surfaceWorldPos - ray.Origin); - ray.TMin = 0; - ray.TMax = length(surfaceWorldPos - ray.Origin); - - RayQuery rayQuery; - - rayQuery.TraceRayInline(ProbeBVH, RAY_FLAG_NONE, 0xFF, ray); - - while (rayQuery.Proceed()) - { - if (rayQuery.CandidateType() == CANDIDATE_PROCEDURAL_PRIMITIVE) - { - float3 org = rayQuery.CandidateObjectRayOrigin(); - float3 dir = rayQuery.CandidateObjectRayDirection(); - - // The primitive is a unit sphere (R = 1) centered at 0. - // Compute the intersection of the ray and that sphere. - - // Quadratic equation: at^2 + 2bt + c = 0 - float a = dot(dir, dir); - float b = dot(org, dir); - float c = dot(org, org) - 1.0; - float d = b * b - a * c; - - if (d >= 0) - { - float t = -(b + sqrt(d)) / a; - if (ray.TMin <= t && t <= ray.TMax) - { - rayQuery.CommitProceduralPrimitiveHit(t); - } - } - } - } - - if (rayQuery.CommittedStatus() != COMMITTED_PROCEDURAL_PRIMITIVE_HIT) - return; - - uint probeIndex = rayQuery.CommittedInstanceID(); - - DDGIVolumeResourceIndices resourceIndices = t_DDGIVolumeResourceIndices[g_Const.volumeIndex]; - DDGIVolumeDescGPU DDGIVolume = UnpackDDGIVolumeDescGPU(t_DDGIVolumes[g_Const.volumeIndex]); - - // Fill the volume's resource pointers - Texture2D probeIrradianceTexture = t_BindlessTextures[resourceIndices.irradianceTextureSRV]; - Texture2D probeDataTexture = t_BindlessTextures[resourceIndices.probeDataTextureSRV]; - - // Get the probe's grid coordinates - int3 probeCoords = DDGIGetProbeCoords(probeIndex, DDGIVolume); - - // Get the probe's storage index - probeIndex = DDGIGetScrollingProbeIndex(probeCoords, DDGIVolume); - - float3x4 probeToWorld = rayQuery.CommittedObjectToWorld3x4(); - float3 probeWorldPosition = float3(probeToWorld._m03, probeToWorld._m13, probeToWorld._m23); - float3 hitPosition = ray.Origin + ray.Direction * rayQuery.CommittedRayT(); - float3 sampleDirection = normalize(hitPosition - probeWorldPosition); - - - float2 coords = DDGIGetOctahedralCoordinates(sampleDirection); - - float2 uv = DDGIGetProbeUV(probeIndex, coords, DDGIVolume.probeNumIrradianceTexels, DDGIVolume); - - float3 color = probeIrradianceTexture.SampleLevel(s_ProbeSampler, uv, 0).rgb; - { - // Decode the tone curve - float3 exponent = DDGIVolume.probeIrradianceEncodingGamma * 0.5f; - color = pow(color, exponent); - - // Go back to linear irradiance - color *= color; - - // Multiply by the area of the integration domain (2PI) to complete the irradiance estimate. Divide by PI to normalize for the display. - color *= 2.f; - } - - - if (DDGIVolume.probeClassificationEnabled) - { - const float3 INACTIVE_COLOR = float3(0.f, 0.f, 1.f); // Blue - const float3 ACTIVE_COLOR = float3(0.f, 1.f, 0.f); // Green - - // Adjust probe index for scroll offsets - int storageProbeIndex = DDGIGetScrollingProbeIndex(probeCoords, DDGIVolume); - - uint2 probeStateTexcoord = DDGIGetProbeTexelCoords(storageProbeIndex, DDGIVolume); - - // Probe state border visualization - float probeState = probeDataTexture[probeStateTexcoord].w; - - if (abs(dot(ray.Direction, sampleDirection)) < 0.35f) - { - if (probeState == RTXGI_DDGI_PROBE_STATE_ACTIVE) - { - color = ACTIVE_COLOR * 0.1; - } - else if (probeState == RTXGI_DDGI_PROBE_STATE_INACTIVE) - { - color = INACTIVE_COLOR * 0.1; - } - } - } - - u_CompositedColor[pixelPosition] = float4(color, 0); -} diff --git a/shaders/RTXGI/ProbeInstances.hlsl b/shaders/RTXGI/ProbeInstances.hlsl deleted file mode 100644 index 912d81a..0000000 --- a/shaders/RTXGI/ProbeInstances.hlsl +++ /dev/null @@ -1,68 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. - # - # NVIDIA CORPORATION and its licensors retain all intellectual property - # and proprietary rights in and to this software, related documentation - # and any modifications thereto. Any use, reproduction, disclosure or - # distribution of this software and related documentation without an express - # license agreement from NVIDIA CORPORATION is strictly prohibited. - **************************************************************************/ - -#pragma pack_matrix(row_major) - -#include "DDGIShaderConfig.h" - -#define HLSL -#include - -#include - -#include "../ShaderParameters.h" -#include "../GBufferHelpers.hlsli" -#include "../SceneGeometry.hlsli" - -struct GeometryInstance -{ - float3x4 transform; - uint instanceIndexAndMask; - uint instanceOffsetAndFlags; - uint deviceAddressLow; - uint deviceAddressHigh; -}; - -ConstantBuffer g_Const : register(b0); -StructuredBuffer t_DDGIVolumes : register(t0); -StructuredBuffer t_DDGIVolumeResourceIndices : register(t1); -RWStructuredBuffer u_Instances : register(u0); - -[numthreads(256, 1, 1)] -void main(uint globalIdx : SV_DispatchThreadID) -{ - DDGIVolumeResourceIndices resourceIndices = t_DDGIVolumeResourceIndices[g_Const.volumeIndex]; - DDGIVolumeDescGPU DDGIVolume = UnpackDDGIVolumeDescGPU(t_DDGIVolumes[g_Const.volumeIndex]); - - uint numProbes = DDGIVolume.probeCounts.x * DDGIVolume.probeCounts.y * DDGIVolume.probeCounts.z; - if (globalIdx >= numProbes) - return; - - int3 probeCoords = DDGIGetProbeCoords(globalIdx, DDGIVolume); - - Texture2D probeData = t_BindlessTextures[resourceIndices.probeDataTextureSRV]; - - // Get the probe's world position - float3 probeWorldPosition = DDGIGetProbeWorldPosition(probeCoords, DDGIVolume, probeData); - - float probeScale = min(min(DDGIVolume.probeSpacing.x, DDGIVolume.probeSpacing.y), DDGIVolume.probeSpacing.z) * 0.15; - - GeometryInstance instance; - instance.transform = float3x4( - probeScale, 0, 0, probeWorldPosition.x, - 0, probeScale, 0, probeWorldPosition.y, - 0, 0, probeScale, probeWorldPosition.z); - instance.instanceIndexAndMask = (1 << 24) | globalIdx; - instance.instanceOffsetAndFlags = 0; - instance.deviceAddressLow = g_Const.blasDeviceAddressLow; - instance.deviceAddressHigh = g_Const.blasDeviceAddressHigh; - - u_Instances[globalIdx] = instance; -} diff --git a/shaders/RTXGI/ProbeTrace.hlsl b/shaders/RTXGI/ProbeTrace.hlsl deleted file mode 100644 index dfc4fa7..0000000 --- a/shaders/RTXGI/ProbeTrace.hlsl +++ /dev/null @@ -1,258 +0,0 @@ -/* -* Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. -* -* NVIDIA CORPORATION and its licensors retain all intellectual property -* and proprietary rights in and to this software, related documentation -* and any modifications thereto. Any use, reproduction, disclosure or -* distribution of this software and related documentation without an express -* license agreement from NVIDIA CORPORATION is strictly prohibited. -*/ - -#pragma pack_matrix(row_major) - -#include "DDGIShaderConfig.h" - -// ------------------------------------------------------------------------------------------- - -// Disable specular MIS on direct lighting of the surfaces found by probe rays, -// because there are no BRDF rays shot from these surfaces. -#define RAB_ENABLE_SPECULAR_MIS 0 - -#define HLSL -#include - -#include "../LightingPasses/RtxdiApplicationBridge.hlsli" -#include - -#ifdef WITH_NRD -#undef WITH_NRD -#endif -#include "../LightingPasses/ShadingHelpers.hlsli" - -StructuredBuffer t_DDGIVolumes : register(t40 VK_DESCRIPTOR_SET(2)); -StructuredBuffer t_DDGIVolumeResourceIndices : register(t41 VK_DESCRIPTOR_SET(2)); - -SamplerState s_ProbeSampler : register(s40 VK_DESCRIPTOR_SET(2)); - -static const float c_MaxIndirectRadiance = 10; - -#if USE_RAY_QUERY -[numthreads(16, 16, 1)] -void main(uint2 DispatchIndex : SV_DispatchThreadID) -#else -[shader("raygeneration")] -void RayGen() -#endif -{ -#if !USE_RAY_QUERY - uint2 DispatchIndex = DispatchRaysIndex().xy; -#endif - - int rayIndex = DispatchIndex.x; // index of the current probe ray - int probeIndex = DispatchIndex.y; // index of current probe - - RAB_RandomSamplerState rng = RAB_InitRandomSampler(DispatchIndex, 6); - - // Get the DDGIVolume's index - uint volumeIndex = g_PerPassConstants.rtxgiVolumeIndex; - - // Get the DDGIVolume's constants - DDGIVolumeResourceIndices resourceIndices = t_DDGIVolumeResourceIndices[volumeIndex]; - DDGIVolumeDescGPU DDGIVolume = UnpackDDGIVolumeDescGPU(t_DDGIVolumes[volumeIndex]); - - // Fill the volume's resource pointers - DDGIVolumeResources resources; - resources.probeIrradiance = t_BindlessTextures[resourceIndices.irradianceTextureSRV]; - resources.probeDistance = t_BindlessTextures[resourceIndices.distanceTextureSRV]; - resources.probeData = t_BindlessTextures[resourceIndices.probeDataTextureSRV]; - resources.bilinearSampler = s_ProbeSampler; - - RWTexture2D rayDataTexture = u_BindlessTexturesRW[resourceIndices.rayDataTextureUAV]; - - // Get the probe's grid coordinates - int3 probeCoords = DDGIGetProbeCoords(probeIndex, DDGIVolume); - - // Adjust the probe index for the scroll offsets - probeIndex = DDGIGetScrollingProbeIndex(probeCoords, DDGIVolume); - - int2 rayStoragePosition = int2(rayIndex, probeIndex); - - // Get the probe's state if classification is enabled - // Early out: do not shoot rays when the probe is inactive *unless* it is one of the "fixed" rays used by probe classification - float probeState = RTXGI_DDGI_PROBE_STATE_ACTIVE; - if (DDGIVolume.probeClassificationEnabled) - { - // Get the probe's texel coordinates in the Probe Data texture - int2 probeDataCoords = DDGIGetProbeTexelCoords(probeIndex, DDGIVolume); - - // Get the probe's classification state - probeState = resources.probeData.Load(int3(probeDataCoords, 0)).w; - - // Early out if the probe is inactive and the ray is not one of the fixed rays - if (probeState == RTXGI_DDGI_PROBE_STATE_INACTIVE && rayIndex >= RTXGI_DDGI_NUM_FIXED_RAYS) return; - } - - // Get the probe's world position - // Note: world positions are computed from probe coordinates *not* adjusted for infinite scrolling - float3 probeWorldPosition = DDGIGetProbeWorldPosition(probeCoords, DDGIVolume, resources.probeData); - - // Get a ray direction for this probe - float3 probeRayDirection = DDGIGetProbeRayDirection(rayIndex, DDGIVolume); - - // Setup the probe ray - RayDesc ray; - ray.Origin = probeWorldPosition; - ray.Direction = probeRayDirection; - ray.TMin = 0.f; - ray.TMax = DDGIVolume.probeMaxRayDistance; - - // Trace the Probe Ray - RayPayload payload = (RayPayload)0; - payload.instanceID = ~0u; - payload.throughput = 1.0; - - uint instanceMask = INSTANCE_MASK_OPAQUE; - -#if USE_RAY_QUERY - RayQuery rayQuery; - - rayQuery.TraceRayInline(SceneBVH, 0, instanceMask, ray); - - rayQuery.Proceed(); - - if (rayQuery.CommittedStatus() == COMMITTED_TRIANGLE_HIT) - { - payload.instanceID = rayQuery.CommittedInstanceID(); - payload.geometryIndex = rayQuery.CommittedGeometryIndex(); - payload.primitiveIndex = rayQuery.CommittedPrimitiveIndex(); - payload.barycentrics = rayQuery.CommittedTriangleBarycentrics(); - payload.committedRayT = rayQuery.CommittedRayT(); - payload.frontFace = rayQuery.CommittedTriangleFrontFace(); - } -#else - TraceRay(SceneBVH, RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES | RAY_FLAG_FORCE_OPAQUE, instanceMask, 0, 0, 0, ray, payload); -#endif - - if (g_PerPassConstants.rayCountBufferIndex >= 0) - { - InterlockedAdd(u_RayCountBuffer[RAY_COUNT_TRACED(g_PerPassConstants.rayCountBufferIndex)], 1); - } - - // The ray missed. Set hit distance to a large value and exit early. - if (payload.instanceID == ~0u) - { - float3 environmentRadiance = GetEnvironmentRadiance(ray.Direction); - - DDGIStoreProbeRayMiss(rayDataTexture, rayStoragePosition, DDGIVolume, environmentRadiance); - return; - } - - if (g_PerPassConstants.rayCountBufferIndex >= 0) - { - InterlockedAdd(u_RayCountBuffer[RAY_COUNT_HITS(g_PerPassConstants.rayCountBufferIndex)], 1); - } - - // Hit a surface backface. - if (!payload.frontFace) - { - // Make hit distance negative to mark a backface hit for blending, probe relocation, and probe classification. - DDGIStoreProbeRayBackfaceHit(rayDataTexture, rayStoragePosition, DDGIVolume, payload.committedRayT); - return; - } - - // Early out: a "fixed" ray hit a front facing surface. Fixed rays are not blended since their direction - // is not random and they would bias the irradiance estimate. Don't perform lighting for these rays. - if (DDGIVolume.probeClassificationEnabled && rayIndex < RTXGI_DDGI_NUM_FIXED_RAYS) - { - // Store the ray front face hit distance (only) - DDGIStoreProbeRayFrontfaceHit(rayDataTexture, rayStoragePosition, DDGIVolume, payload.committedRayT); - return; - } - - GeometrySample gs = getGeometryFromHit( - payload.instanceID, - payload.geometryIndex, - payload.primitiveIndex, - payload.barycentrics, - GeomAttr_Normal | GeomAttr_TexCoord | GeomAttr_Position, - t_InstanceData, t_GeometryData, t_MaterialConstants); - - MaterialSample ms = sampleGeometryMaterial(gs, 0, 0, 0, - MatAttr_BaseColor | MatAttr_MetalRough, s_MaterialSampler); - - - RAB_Surface secondarySurface; - secondarySurface.worldPos = ray.Origin + ray.Direction * payload.committedRayT; - secondarySurface.viewDepth = 1.0; // don't care - secondarySurface.normal = (dot(gs.geometryNormal, ray.Direction) < 0) ? gs.geometryNormal : -gs.geometryNormal; - secondarySurface.geoNormal = secondarySurface.normal; - secondarySurface.diffuseAlbedo = ms.diffuseAlbedo; - secondarySurface.specularF0 = ms.specularF0; - secondarySurface.roughness = ms.roughness; - secondarySurface.diffuseProbability = getSurfaceDiffuseProbability(secondarySurface); - secondarySurface.viewDir = normalize(ray.Origin - secondarySurface.worldPos); - - const RTXDI_ResamplingRuntimeParameters params = g_Const.runtimeParams; - - RTXDI_SampleParameters sampleParams = RTXDI_InitSampleParameters( - g_Const.numIndirectRegirSamples, - g_Const.numIndirectLocalLightSamples, - g_Const.numIndirectInfiniteLightSamples, - g_Const.numIndirectEnvironmentSamples, - 0, // numBrdfSamples - 0.f, // brdfCutoff - 0.f); // brdfMinRayT - - RAB_LightSample lightSample = RAB_EmptyLightSample(); - RTXDI_Reservoir reservoir = RTXDI_SampleLightsForSurface(rng, rng, secondarySurface, - sampleParams, params, lightSample); - - float lightSampleScale = (lightSample.solidAnglePdf > 0) ? RTXDI_GetReservoirInvPdf(reservoir) / lightSample.solidAnglePdf : 0; - - // Firefly suppression - float indirectLuminance = calcLuminance(lightSample.radiance) * lightSampleScale; - if(indirectLuminance > c_MaxIndirectRadiance) - lightSampleScale *= c_MaxIndirectRadiance / indirectLuminance; - - float3 indirectDiffuse = 0; - float3 indirectSpecular = 0; - float lightDistance = 0; - ShadeSurfaceWithLightSample(reservoir, secondarySurface, lightSample, /* previousFrameTLAS = */ false, - /* enableVisibilityReuse = */ false, indirectDiffuse, indirectSpecular, lightDistance); - - float3 directLighting = indirectDiffuse * ms.diffuseAlbedo + indirectSpecular; - - - - // Indirect Lighting (recursive) - float3 irradiance = 0.f; - float3 surfaceBias = DDGIGetSurfaceBias(gs.geometryNormal, ray.Direction, DDGIVolume); - - float3 worldPosition = ray.Origin + ray.Direction * payload.committedRayT; - - // Compute volume blending weight - float volumeBlendWeight = DDGIGetVolumeBlendWeight(worldPosition, DDGIVolume); - - // Avoid evaluating irradiance when the surface is outside the volume - if (volumeBlendWeight > 0) - { - // Get irradiance from the DDGIVolume - irradiance = DDGIGetVolumeIrradiance( - worldPosition, - surfaceBias, - gs.geometryNormal, - DDGIVolume, - resources); - - // Attenuate irradiance by the blend weight - irradiance *= volumeBlendWeight; - } - - // Perfectly diffuse reflectors don't exist in the real world. Limit the BRDF - // albedo to a maximum value to account for the energy loss at each bounce. - float maxAlbedo = 0.9f; - - // Store the final ray radiance and hit distance - float3 radiance = directLighting + ((min(ms.diffuseAlbedo, maxAlbedo) / M_PI) * irradiance); - DDGIStoreProbeRayFrontfaceHit(rayDataTexture, rayStoragePosition, DDGIVolume, radiance, payload.committedRayT); -} diff --git a/shaders/RTXGI/RtxgiHelpers.hlsli b/shaders/RTXGI/RtxgiHelpers.hlsli deleted file mode 100644 index 886bb0e..0000000 --- a/shaders/RTXGI/RtxgiHelpers.hlsli +++ /dev/null @@ -1,69 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. - # - # NVIDIA CORPORATION and its licensors retain all intellectual property - # and proprietary rights in and to this software, related documentation - # and any modifications thereto. Any use, reproduction, disclosure or - # distribution of this software and related documentation without an express - # license agreement from NVIDIA CORPORATION is strictly prohibited. - **************************************************************************/ - -#ifndef RTXGI_HELPERS_HLSLI -#define RTXGI_HELPERS_HLSLI - -#include "DDGIShaderConfig.h" - -#define HLSL -#include - -float3 GetIrradianceFromDDGI( - float3 surfacePosition, - float3 surfaceNormal, - float3 cameraPosition, - uint numRtxgiVolumes, - StructuredBuffer Volumes, - StructuredBuffer VolumeResourceIndices, - SamplerState Sampler) -{ - float remainingBlendWeight = 1.0; - float3 blendedIrradiance = 0; - - for (uint volumeIndex = 0; volumeIndex < numRtxgiVolumes; volumeIndex++) - { - DDGIVolumeResourceIndices resourceIndices = VolumeResourceIndices[volumeIndex]; - DDGIVolumeDescGPU DDGIVolume = UnpackDDGIVolumeDescGPU(Volumes[volumeIndex]); - - DDGIVolumeResources resources; - resources.probeIrradiance = t_BindlessTextures[resourceIndices.irradianceTextureSRV]; - resources.probeDistance = t_BindlessTextures[resourceIndices.distanceTextureSRV]; - resources.probeData = t_BindlessTextures[resourceIndices.probeDataTextureSRV]; - resources.bilinearSampler = Sampler; - - float volumeBlendWeight = DDGIGetVolumeBlendWeight(surfacePosition, DDGIVolume); - volumeBlendWeight = saturate(volumeBlendWeight); - - if (volumeBlendWeight > 0) - { - float3 viewDirection = normalize(surfacePosition - cameraPosition); - float3 surfaceBias = DDGIGetSurfaceBias(surfaceNormal, viewDirection, DDGIVolume); - - float3 irradiance = DDGIGetVolumeIrradiance( - surfacePosition, - surfaceBias, - surfaceNormal, - DDGIVolume, - resources); - - blendedIrradiance += irradiance * volumeBlendWeight * remainingBlendWeight; - remainingBlendWeight *= 1.0 - volumeBlendWeight; - - if (remainingBlendWeight == 0.0) - break; - } - } - - return blendedIrradiance; -} - - -#endif // RTXGI_HELPERS_HLSLI \ No newline at end of file diff --git a/shaders/RenderEnvironmentMap.hlsl b/shaders/RenderEnvironmentMap.hlsl index 00a21ab..dc5fdf5 100644 --- a/shaders/RenderEnvironmentMap.hlsl +++ b/shaders/RenderEnvironmentMap.hlsl @@ -1,5 +1,5 @@ /* -* Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. +* Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved. * * NVIDIA CORPORATION and its licensors retain all intellectual property * and proprietary rights in and to this software, related documentation diff --git a/shaders/SceneGeometry.hlsli b/shaders/SceneGeometry.hlsli index 5b9b2f5..f910659 100644 --- a/shaders/SceneGeometry.hlsli +++ b/shaders/SceneGeometry.hlsli @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/shaders/ShaderParameters.h b/shaders/ShaderParameters.h index 6a2c092..549153c 100644 --- a/shaders/ShaderParameters.h +++ b/shaders/ShaderParameters.h @@ -14,7 +14,11 @@ #include #include -#include +#include +#include +#include + +#include "BRDFPTParameters.h" #define TASK_PRIMITIVE_LIGHT_BIT 0x80000000u @@ -144,7 +148,6 @@ struct CompositingConstants float noiseClampHigh; uint checkerboard; - uint numRtxgiVolumes; }; struct AccumulationConstants @@ -156,22 +159,6 @@ struct AccumulationConstants float blendFactor; }; -struct ProbeDebugConstants -{ - PlanarViewConstants view; - uint blasDeviceAddressLow; - uint blasDeviceAddressHigh; - uint volumeIndex; -}; - -struct DDGIVolumeResourceIndices -{ - uint irradianceTextureSRV; - uint distanceTextureSRV; - uint probeDataTextureSRV; - uint rayDataTextureUAV; -}; - struct FilterGradientsConstants { uint2 viewportSize; @@ -194,7 +181,9 @@ struct ConfidenceConstants struct VisualizationConstants { - RTXDI_ResamplingRuntimeParameters runtimeParams; + RTXDI_RuntimeParameters runtimeParams; + RTXDI_ReservoirBufferParameters restirDIReservoirBufferParams; + RTXDI_ReservoirBufferParameters restirGIReservoirBufferParams; int2 outputSize; float2 resolutionScale; @@ -204,11 +193,23 @@ struct VisualizationConstants uint enableAccumulation; }; +struct SceneConstants +{ + uint enableEnvironmentMap; // Global. Affects BRDFRayTracing's GI code, plus RTXDI, ReGIR, etc. + uint environmentMapTextureIndex; // Global + float environmentScale; + float environmentRotation; + + uint enableAlphaTestedGeometry; + uint enableTransparentGeometry; + uint2 pad1; +}; + struct ResamplingConstants { PlanarViewConstants view; PlanarViewConstants prevView; - RTXDI_ResamplingRuntimeParameters runtimeParams; + RTXDI_RuntimeParameters runtimeParams; float4 reblurDiffHitDistParams; float4 reblurSpecHitDistParams; @@ -216,94 +217,36 @@ struct ResamplingConstants uint frameIndex; uint enablePreviousTLAS; uint denoiserMode; - uint numRtxgiVolumes; + uint discountNaiveSamples; uint enableBrdfIndirect; - uint enableBrdfAdditiveBlend; - uint enableAlphaTestedGeometry; - uint enableReSTIRIndirect; - - uint enableTransparentGeometry; - uint enableDenoiserInputPacking; - uint visualizeRegirCells; - float brdfCutoff; + uint enableBrdfAdditiveBlend; + uint enableAccumulation; // StoreShadingOutput + uint pad1; - uint numPrimaryRegirSamples; - uint numPrimaryLocalLightSamples; - uint numPrimaryBrdfSamples; - uint numPrimaryInfiniteLightSamples; - - uint numIndirectRegirSamples; - uint numIndirectLocalLightSamples; - uint numIndirectInfiniteLightSamples; - uint enableIndirectEmissiveSurfaces; - - uint enableInitialVisibility; - uint enableFinalVisibility; - uint initialOutputBufferIndex; - uint enableFallbackSampling; - - uint temporalInputBufferIndex; - uint temporalOutputBufferIndex; - uint spatialInputBufferIndex; - uint spatialOutputBufferIndex; - - uint shadeInputBufferIndex; - uint discardInvisibleSamples; - uint maxHistoryLength; - float boilingFilterStrength; - - float temporalDepthThreshold; - float temporalNormalThreshold; - float spatialDepthThreshold; - float spatialNormalThreshold; - - uint temporalBiasCorrection; - uint spatialBiasCorrection; - uint numSpatialSamples; - uint numDisocclusionBoostSamples; - - float spatialSamplingRadius; - uint reuseFinalVisibility; - uint finalVisibilityMaxAge; - float finalVisibilityMaxDistance; + SceneConstants sceneConstants; - uint2 environmentPdfTextureSize; - uint numPrimaryEnvironmentSamples; - uint numIndirectEnvironmentSamples; - - uint2 localLightPdfTextureSize; - uint numRegirBuildSamples; - uint environmentMapImportanceSampling; + // Common buffer params + RTXDI_LightBufferParameters lightBufferParams; + RTXDI_RISBufferSegmentParameters localLightsRISBufferSegmentParams; + RTXDI_RISBufferSegmentParameters environmentLightRISBufferSegmentParams; - uint enableEnvironmentMap; - uint environmentMapTextureIndex; - float environmentScale; - float environmentRotation; + // Algo-specific params + ReSTIRDI_Parameters restirDI; + ReGIR_Parameters regir; + ReSTIRGI_Parameters restirGI; + BRDFPathTracing_Parameters brdfPT; - uint enablePermutationSampling; - uint enableAccumulation; - uint numSecondarySamples; - uint secondaryBiasCorrection; - - float secondarySamplingRadius; - float secondaryDepthThreshold; - float secondaryNormalThreshold; - float permutationSamplingThreshold; - - float roughnessOverride; - float metalnessOverride; - float minSecondaryRoughness; - uint giReservoirMaxAge; - - uint giEnableFinalVisibility; - uint giEnableFinalMIS; + uint visualizeRegirCells; + uint3 pad2; + + uint2 environmentPdfTextureSize; + uint2 localLightPdfTextureSize; }; struct PerPassConstants { int rayCountBufferIndex; - uint rtxgiVolumeIndex; }; struct SecondaryGBufferData diff --git a/shaders/Shaders.cfg b/shaders/Shaders.cfg index b7c68f5..4a14240 100644 --- a/shaders/Shaders.cfg +++ b/shaders/Shaders.cfg @@ -20,22 +20,23 @@ PrepareLights.hlsl -T cs -E main LightingPasses/PresampleLights.hlsl -T cs -E main LightingPasses/PresampleEnvironmentMap.hlsl -T cs -E main LightingPasses/PresampleReGIR.hlsl -T cs -E main -D RTXDI_REGIR_MODE={RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} -LightingPasses/GenerateInitialSamples.hlsl -T cs -E main -D USE_RAY_QUERY=1 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} -LightingPasses/GenerateInitialSamples.hlsl -T lib -D USE_RAY_QUERY=0 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} -LightingPasses/TemporalResampling.hlsl -T cs -E main -D USE_RAY_QUERY=1 -LightingPasses/TemporalResampling.hlsl -T lib -D USE_RAY_QUERY=0 -LightingPasses/SpatialResampling.hlsl -T cs -E main -D USE_RAY_QUERY=1 -LightingPasses/SpatialResampling.hlsl -T lib -D USE_RAY_QUERY=0 -LightingPasses/ShadeSamples.hlsl -T cs -E main -D USE_RAY_QUERY=1 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} -LightingPasses/ShadeSamples.hlsl -T lib -D USE_RAY_QUERY=0 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} +LightingPasses/DIGenerateInitialSamples.hlsl -T cs -E main -D USE_RAY_QUERY=1 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} +LightingPasses/DIGenerateInitialSamples.hlsl -T lib -D USE_RAY_QUERY=0 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} +LightingPasses/DITemporalResampling.hlsl -T cs -E main -D USE_RAY_QUERY=1 +LightingPasses/DITemporalResampling.hlsl -T lib -D USE_RAY_QUERY=0 +LightingPasses/DISpatialResampling.hlsl -T cs -E main -D USE_RAY_QUERY=1 +LightingPasses/DISpatialResampling.hlsl -T lib -D USE_RAY_QUERY=0 +LightingPasses/DIFusedResampling.hlsl -T cs -E main -D USE_RAY_QUERY=1 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} +LightingPasses/DIFusedResampling.hlsl -T lib -D USE_RAY_QUERY=0 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} +LightingPasses/DIShadeSamples.hlsl -T cs -E main -D USE_RAY_QUERY=1 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} +LightingPasses/DIShadeSamples.hlsl -T lib -D USE_RAY_QUERY=0 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} +LightingPasses/DIComputeGradients.hlsl -T cs -E main -D USE_RAY_QUERY=1 +LightingPasses/DIComputeGradients.hlsl -T lib -D USE_RAY_QUERY=0 + LightingPasses/BrdfRayTracing.hlsl -T cs -E main -D USE_RAY_QUERY=1 LightingPasses/BrdfRayTracing.hlsl -T lib -D USE_RAY_QUERY=0 LightingPasses/ShadeSecondarySurfaces.hlsl -T cs -E main -D USE_RAY_QUERY=1 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} LightingPasses/ShadeSecondarySurfaces.hlsl -T lib -D USE_RAY_QUERY=0 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} -LightingPasses/FusedResampling.hlsl -T cs -E main -D USE_RAY_QUERY=1 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} -LightingPasses/FusedResampling.hlsl -T lib -D USE_RAY_QUERY=0 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} -LightingPasses/ComputeGradients.hlsl -T cs -E main -D USE_RAY_QUERY=1 -LightingPasses/ComputeGradients.hlsl -T lib -D USE_RAY_QUERY=0 FilterGradientsPass.hlsl -T cs -E main ConfidencePass.hlsl -T cs -E main @@ -47,18 +48,3 @@ LightingPasses/GIFusedResampling.hlsl -T cs -E main -D USE_RAY_QUERY=1 LightingPasses/GIFusedResampling.hlsl -T lib -E main -D USE_RAY_QUERY=0 LightingPasses/GIFinalShading.hlsl -T cs -E main -D USE_RAY_QUERY=1 LightingPasses/GIFinalShading.hlsl -T lib -E main -D USE_RAY_QUERY=0 - -#ifdef WITH_RTXGI -RTXGI/ProbeTrace.hlsl -T cs -D USE_RAY_QUERY=1 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} -RTXGI/ProbeTrace.hlsl -T lib -D USE_RAY_QUERY=0 -D RTXDI_REGIR_MODE={RTXDI_REGIR_DISABLED,RTXDI_REGIR_GRID,RTXDI_REGIR_ONION} -RTXGI/ProbeInstances.hlsl -T cs -RTXGI/ProbeDebug.hlsl -T cs -D USE_RAY_QUERY=1 - -../RTXGI/shaders/ddgi/ProbeBlendingCS.hlsl -T cs -E DDGIProbeBlendingCS -o RTXGI -D RTXGI_DDGI_USE_SHADER_CONFIG_FILE=1 -D HLSL=1 -D RTXGI_DDGI_BLEND_RADIANCE={0,1} -D RTXGI_DDGI_BLEND_SCROLL_SHARED_MEMORY=1 -../RTXGI/shaders/ddgi/ProbeBorderUpdateCS.hlsl -T cs -E DDGIProbeBorderRowUpdateCS -o RTXGI -D RTXGI_DDGI_USE_SHADER_CONFIG_FILE=1 -D HLSL=1 -D RTXGI_DDGI_BLEND_RADIANCE={0,1} -../RTXGI/shaders/ddgi/ProbeBorderUpdateCS.hlsl -T cs -E DDGIProbeBorderColumnUpdateCS -o RTXGI -D RTXGI_DDGI_USE_SHADER_CONFIG_FILE=1 -D HLSL=1 -D RTXGI_DDGI_BLEND_RADIANCE={0,1} -../RTXGI/shaders/ddgi/ProbeClassificationCS.hlsl -T cs -E DDGIProbeClassificationCS -o RTXGI -D RTXGI_DDGI_USE_SHADER_CONFIG_FILE=1 -D HLSL=1 -../RTXGI/shaders/ddgi/ProbeClassificationCS.hlsl -T cs -E DDGIProbeClassificationResetCS -o RTXGI -D RTXGI_DDGI_USE_SHADER_CONFIG_FILE=1 -D HLSL=1 -../RTXGI/shaders/ddgi/ProbeRelocationCS.hlsl -T cs -E DDGIProbeRelocationCS -o RTXGI -D RTXGI_DDGI_USE_SHADER_CONFIG_FILE=1 -D HLSL=1 -../RTXGI/shaders/ddgi/ProbeRelocationCS.hlsl -T cs -E DDGIProbeRelocationResetCS -o RTXGI -D RTXGI_DDGI_USE_SHADER_CONFIG_FILE=1 -D HLSL=1 -#endif \ No newline at end of file diff --git a/shaders/VisualizeConfidence.hlsl b/shaders/VisualizeConfidence.hlsl index 24b5e30..e5f7cbb 100644 --- a/shaders/VisualizeConfidence.hlsl +++ b/shaders/VisualizeConfidence.hlsl @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/shaders/VisualizeHdrSignals.hlsl b/shaders/VisualizeHdrSignals.hlsl index 356e518..2092235 100644 --- a/shaders/VisualizeHdrSignals.hlsl +++ b/shaders/VisualizeHdrSignals.hlsl @@ -25,13 +25,13 @@ Texture2D t_Specular : register(t4); Texture2D t_DenoisedDiffuse : register(t5); Texture2D t_DenoisedSpecular : register(t6); Texture2D t_Gradients : register(t7); -StructuredBuffer t_Reservoirs : register(t8); +StructuredBuffer t_Reservoirs : register(t8); StructuredBuffer t_GIReservoirs : register(t9); #define RTXDI_LIGHT_RESERVOIR_BUFFER t_Reservoirs #define RTXDI_GI_RESERVOIR_BUFFER t_GIReservoirs #define RTXDI_ENABLE_STORE_RESERVOIR 0 -#include +#include #include float4 blend(float4 top, float4 bottom) @@ -51,7 +51,7 @@ float4 main(float4 i_position : SV_Position) : SV_Target resolutionScale = 1.0; int2 inputPos = int2(pixelPos.x * resolutionScale.x, g_Const.outputSize.y * resolutionScale.y * 0.5); - int2 reservoirPos = RTXDI_PixelPosToReservoirPos(inputPos, g_Const.runtimeParams); + int2 reservoirPos = RTXDI_PixelPosToReservoirPos(inputPos, g_Const.runtimeParams.activeCheckerboardField); float input = 0; switch(g_Const.visualizationMode) @@ -84,13 +84,13 @@ float4 main(float4 i_position : SV_Position) : SV_Target break; case VIS_MODE_RESERVOIR_WEIGHT: { - RTXDI_Reservoir reservoir = RTXDI_LoadReservoir(g_Const.runtimeParams, reservoirPos, g_Const.inputBufferIndex); + RTXDI_DIReservoir reservoir = RTXDI_LoadDIReservoir(g_Const.restirDIReservoirBufferParams, reservoirPos, g_Const.inputBufferIndex); input = reservoir.weightSum; break; } case VIS_MODE_RESERVOIR_M: { - RTXDI_Reservoir reservoir = RTXDI_LoadReservoir(g_Const.runtimeParams, reservoirPos, g_Const.inputBufferIndex); + RTXDI_DIReservoir reservoir = RTXDI_LoadDIReservoir(g_Const.restirDIReservoirBufferParams, reservoirPos, g_Const.inputBufferIndex); input = reservoir.M; break; } @@ -108,13 +108,13 @@ float4 main(float4 i_position : SV_Position) : SV_Target } case VIS_MODE_GI_WEIGHT: { - RTXDI_GIReservoir reservoir = RTXDI_LoadGIReservoir(g_Const.runtimeParams, reservoirPos, g_Const.inputBufferIndex); + RTXDI_GIReservoir reservoir = RTXDI_LoadGIReservoir(g_Const.restirGIReservoirBufferParams, reservoirPos, g_Const.inputBufferIndex); input = reservoir.weightSum; break; } case VIS_MODE_GI_M: { - RTXDI_GIReservoir reservoir = RTXDI_LoadGIReservoir(g_Const.runtimeParams, reservoirPos, g_Const.inputBufferIndex); + RTXDI_GIReservoir reservoir = RTXDI_LoadGIReservoir(g_Const.restirGIReservoirBufferParams, reservoirPos, g_Const.inputBufferIndex); input = reservoir.M; break; } diff --git a/src/AccumulationPass.cpp b/src/AccumulationPass.cpp index dca41d2..ac96911 100644 --- a/src/AccumulationPass.cpp +++ b/src/AccumulationPass.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/AccumulationPass.h b/src/AccumulationPass.h index 8d31a99..d295b68 100644 --- a/src/AccumulationPass.h +++ b/src/AccumulationPass.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3b8af24..85697fa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,20 +32,3 @@ if (TARGET DLSS) "${DLSS_SHARED_LIBRARY_PATH}" "$/") endif() - -if (TARGET RTXGI-D3D12 OR TARGET RTXGI-VK) - target_compile_definitions(${project} PRIVATE WITH_RTXGI=1) - - if (TARGET RTXGI-D3D12) - target_link_libraries(${project} RTXGI-D3D12) - endif() - - if (TARGET RTXGI-VK) - target_link_libraries(${project} RTXGI-VK) - if (UNIX) - target_link_libraries(${project} vulkan) - else() - target_link_libraries(${project} "${Vulkan_LIBRARY}") - endif() - endif() -endif() diff --git a/src/CompositingPass.cpp b/src/CompositingPass.cpp index b22e068..f516841 100644 --- a/src/CompositingPass.cpp +++ b/src/CompositingPass.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -22,8 +22,6 @@ #include -#include "RtxgiIntegration.h" - using namespace donut::math; #include "../shaders/ShaderParameters.h" @@ -63,18 +61,6 @@ CompositingPass::CompositingPass( }; m_BindingLayout = m_Device->createBindingLayout(bindingLayoutDesc); - -#ifdef WITH_RTXGI - nvrhi::BindingLayoutDesc rtxgiBindingLayoutDesc; - rtxgiBindingLayoutDesc.visibility = nvrhi::ShaderType::Compute; - rtxgiBindingLayoutDesc.bindings = { - nvrhi::BindingLayoutItem::StructuredBuffer_SRV(10), - nvrhi::BindingLayoutItem::StructuredBuffer_SRV(11), - nvrhi::BindingLayoutItem::Sampler(10) - }; - - m_RtxgiBindingLayout = m_Device->createBindingLayout(rtxgiBindingLayoutDesc); -#endif } void CompositingPass::CreatePipeline() @@ -85,13 +71,11 @@ void CompositingPass::CreatePipeline() nvrhi::ComputePipelineDesc pipelineDesc; pipelineDesc.bindingLayouts = { m_BindingLayout, m_BindlessLayout }; - if (m_RtxgiBindingLayout) - pipelineDesc.addBindingLayout(m_RtxgiBindingLayout); pipelineDesc.CS = m_ComputeShader; m_ComputePipeline = m_Device->createComputePipeline(pipelineDesc); } -void CompositingPass::CreateBindingSet(const RenderTargets& renderTargets, const class RtxgiIntegration* rtxgi) +void CompositingPass::CreateBindingSet(const RenderTargets& renderTargets) { nvrhi::BindingSetDesc bindingSetDesc; @@ -121,20 +105,6 @@ void CompositingPass::CreateBindingSet(const RenderTargets& renderTargets, const bindingSetDesc.bindings[3].resourceHandle = renderTargets.PrevGBufferSpecularRough; m_BindingSetOdd = m_Device->createBindingSet(bindingSetDesc, m_BindingLayout); - -#ifdef WITH_RTXGI - assert(rtxgi); - - nvrhi::BindingSetDesc rtxgiBindingSetDesc; - - rtxgiBindingSetDesc.bindings = { - nvrhi::BindingSetItem::StructuredBuffer_SRV(10, rtxgi->GetConstantBuffer()), - nvrhi::BindingSetItem::StructuredBuffer_SRV(11, rtxgi->GetVolumeResourceIndicesBuffer()), - nvrhi::BindingSetItem::Sampler(10, rtxgi->GetProbeSampler()), - }; - - m_RtxgiBindingSet = m_Device->createBindingSet(rtxgiBindingSetDesc, m_RtxgiBindingLayout); -#endif } void CompositingPass::Render( @@ -142,7 +112,6 @@ void CompositingPass::Render( const donut::engine::IView& view, const donut::engine::IView& viewPrev, const uint32_t denoiserMode, - const uint32_t numRtxgiVolumes, const bool checkerboard, const UIData& ui, const EnvironmentLight& environmentLight) @@ -162,13 +131,10 @@ void CompositingPass::Render( constants.noiseMix = ui.noiseMix; constants.noiseClampLow = ui.noiseClampLow; constants.noiseClampHigh = ui.noiseClampHigh; - constants.numRtxgiVolumes = numRtxgiVolumes; commandList->writeBuffer(m_ConstantBuffer, &constants, sizeof(constants)); nvrhi::ComputeState state; state.bindings = { m_BindingSetEven, m_Scene->GetDescriptorTable() }; - if (m_RtxgiBindingSet) - state.addBindingSet(m_RtxgiBindingSet); state.pipeline = m_ComputePipeline; commandList->setComputeState(state); diff --git a/src/CompositingPass.h b/src/CompositingPass.h index ee3ca81..f9e7b2e 100644 --- a/src/CompositingPass.h +++ b/src/CompositingPass.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -33,12 +33,10 @@ class CompositingPass nvrhi::ShaderHandle m_ComputeShader; nvrhi::ComputePipelineHandle m_ComputePipeline; nvrhi::BindingLayoutHandle m_BindingLayout; - nvrhi::BindingLayoutHandle m_RtxgiBindingLayout; nvrhi::BindingLayoutHandle m_BindlessLayout; nvrhi::BindingSetHandle m_BindingSetEven; nvrhi::BindingSetHandle m_BindingSetOdd; - nvrhi::BindingSetHandle m_RtxgiBindingSet; - + nvrhi::BufferHandle m_ConstantBuffer; std::shared_ptr m_ShaderFactory; @@ -55,14 +53,13 @@ class CompositingPass void CreatePipeline(); - void CreateBindingSet(const RenderTargets& renderTargets, const class RtxgiIntegration* rtxgi); + void CreateBindingSet(const RenderTargets& renderTargets); void Render( nvrhi::ICommandList* commandList, const donut::engine::IView& view, const donut::engine::IView& viewPrev, uint32_t denoiserMode, - uint32_t numRtxgiVolumes, bool checkerboard, const UIData& ui, const EnvironmentLight& environmentLight); diff --git a/src/ConfidencePass.cpp b/src/ConfidencePass.cpp index 5f887e1..b281d3a 100644 --- a/src/ConfidencePass.cpp +++ b/src/ConfidencePass.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/ConfidencePass.h b/src/ConfidencePass.h index 5868e43..4f06a91 100644 --- a/src/ConfidencePass.h +++ b/src/ConfidencePass.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/DLSS-DX12.cpp b/src/DLSS-DX12.cpp index e3a7d0e..4c13d63 100644 --- a/src/DLSS-DX12.cpp +++ b/src/DLSS-DX12.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/DLSS-VK.cpp b/src/DLSS-VK.cpp index 30d7114..06dddd2 100644 --- a/src/DLSS-VK.cpp +++ b/src/DLSS-VK.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/DLSS.cpp b/src/DLSS.cpp index 6fb36d0..cc396c2 100644 --- a/src/DLSS.cpp +++ b/src/DLSS.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/DebugViz/DebugVizPasses.cpp b/src/DebugViz/DebugVizPasses.cpp index 0b942ad..a9ccaca 100644 --- a/src/DebugViz/DebugVizPasses.cpp +++ b/src/DebugViz/DebugVizPasses.cpp @@ -1,3 +1,13 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + #include "DebugVizPasses.h" #include "../RenderTargets.h" diff --git a/src/DebugViz/DebugVizPasses.h b/src/DebugViz/DebugVizPasses.h index b07e297..9f54c9b 100644 --- a/src/DebugViz/DebugVizPasses.h +++ b/src/DebugViz/DebugVizPasses.h @@ -1,3 +1,13 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + #pragma once #include diff --git a/src/DebugViz/PackedDataVizPass.cpp b/src/DebugViz/PackedDataVizPass.cpp index 5fbf408..003c715 100644 --- a/src/DebugViz/PackedDataVizPass.cpp +++ b/src/DebugViz/PackedDataVizPass.cpp @@ -1,3 +1,13 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + #include "PackedDataVizPass.h" #include "../RenderTargets.h" diff --git a/src/DebugViz/PackedDataVizPass.h b/src/DebugViz/PackedDataVizPass.h index 10e954d..2c528a2 100644 --- a/src/DebugViz/PackedDataVizPass.h +++ b/src/DebugViz/PackedDataVizPass.h @@ -1,3 +1,13 @@ +/*************************************************************************** + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. + # + # NVIDIA CORPORATION and its licensors retain all intellectual property + # and proprietary rights in and to this software, related documentation + # and any modifications thereto. Any use, reproduction, disclosure or + # distribution of this software and related documentation without an express + # license agreement from NVIDIA CORPORATION is strictly prohibited. + **************************************************************************/ + #pragma once #include diff --git a/src/FilterGradientsPass.cpp b/src/FilterGradientsPass.cpp index bcbf756..9ace989 100644 --- a/src/FilterGradientsPass.cpp +++ b/src/FilterGradientsPass.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/FilterGradientsPass.h b/src/FilterGradientsPass.h index c500043..290fe18 100644 --- a/src/FilterGradientsPass.h +++ b/src/FilterGradientsPass.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/GBufferPass.cpp b/src/GBufferPass.cpp index 3a343da..e47f82f 100644 --- a/src/GBufferPass.cpp +++ b/src/GBufferPass.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/GBufferPass.h b/src/GBufferPass.h index 65d7f40..c480188 100644 --- a/src/GBufferPass.h +++ b/src/GBufferPass.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/GenerateMipsPass.cpp b/src/GenerateMipsPass.cpp index 6ef49e2..b362b8c 100644 --- a/src/GenerateMipsPass.cpp +++ b/src/GenerateMipsPass.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/GenerateMipsPass.h b/src/GenerateMipsPass.h index 8098870..3e50584 100644 --- a/src/GenerateMipsPass.h +++ b/src/GenerateMipsPass.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/GlassPass.cpp b/src/GlassPass.cpp index 450035c..cf859b0 100644 --- a/src/GlassPass.cpp +++ b/src/GlassPass.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/GlassPass.h b/src/GlassPass.h index 406d4b9..5a1fba0 100644 --- a/src/GlassPass.h +++ b/src/GlassPass.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/LightingPasses.cpp b/src/LightingPasses.cpp index 0968127..f85cf90 100644 --- a/src/LightingPasses.cpp +++ b/src/LightingPasses.cpp @@ -21,12 +21,10 @@ #include #include #include -#include +#include #include -#include "RtxgiIntegration.h" - #if WITH_NRD #include #endif @@ -36,6 +34,47 @@ using namespace donut::math; using namespace donut::engine; +BRDFPathTracing_MaterialOverrideParameters getDefaultBRDFPathTracingMaterialOverrideParams() +{ + BRDFPathTracing_MaterialOverrideParameters params = {}; + params.metalnessOverride = 0.5; + params.minSecondaryRoughness = 0.5; + params.roughnessOverride = 0.5; + return params; +} + +BRDFPathTracing_SecondarySurfaceReSTIRDIParameters getDefaultBRDFPathTracingSecondarySurfaceReSTIRDIParams() +{ + BRDFPathTracing_SecondarySurfaceReSTIRDIParameters params = {}; + + params.initialSamplingParams.localLightSamplingMode = ReSTIRDI_LocalLightSamplingMode::ReGIR_RIS; + params.initialSamplingParams.numPrimaryLocalLightSamples = 2; + params.initialSamplingParams.numPrimaryInfiniteLightSamples = 1; + params.initialSamplingParams.numPrimaryEnvironmentSamples = 1; + params.initialSamplingParams.numPrimaryBrdfSamples = 0; + params.initialSamplingParams.brdfCutoff = 0; + params.initialSamplingParams.enableInitialVisibility = false; + + params.spatialResamplingParams.numSpatialSamples = 1; + params.spatialResamplingParams.spatialSamplingRadius = 4.0f; + params.spatialResamplingParams.spatialBiasCorrection = ReSTIRDI_SpatialBiasCorrectionMode::Basic; + params.spatialResamplingParams.numDisocclusionBoostSamples = 0; // Disabled + params.spatialResamplingParams.spatialDepthThreshold = 0.1f; + params.spatialResamplingParams.spatialNormalThreshold = 0.9f; + + return params; +} + +BRDFPathTracing_Parameters getDefaultBRDFPathTracingParams() +{ + BRDFPathTracing_Parameters params; + params.enableIndirectEmissiveSurfaces = false; + params.enableReSTIRGI = false; + params.materialOverrideParams = getDefaultBRDFPathTracingMaterialOverrideParams(); + params.secondarySurfaceReSTIRDIParams = getDefaultBRDFPathTracingSecondarySurfaceReSTIRDIParams(); + return params; +} + LightingPasses::LightingPasses( nvrhi::IDevice* device, std::shared_ptr shaderFactory, @@ -105,24 +144,13 @@ LightingPasses::LightingPasses( m_BindingLayout = m_Device->createBindingLayout(globalBindingLayoutDesc); m_ConstantBuffer = m_Device->createBuffer(nvrhi::utils::CreateVolatileConstantBufferDesc(sizeof(ResamplingConstants), "ResamplingConstants", 16)); - -#ifdef WITH_RTXGI - auto rtxgiBindingLayoutDesc = nvrhi::BindingLayoutDesc() - .setVisibility(nvrhi::ShaderType::Compute | nvrhi::ShaderType::AllRayTracing) - .addItem(nvrhi::BindingLayoutItem::StructuredBuffer_SRV(40)) - .addItem(nvrhi::BindingLayoutItem::StructuredBuffer_SRV(41)) - .addItem(nvrhi::BindingLayoutItem::Sampler(40)); - - m_RtxgiBindingLayout = m_Device->createBindingLayout(rtxgiBindingLayoutDesc); -#endif } void LightingPasses::CreateBindingSet( nvrhi::rt::IAccelStruct* topLevelAS, nvrhi::rt::IAccelStruct* prevTopLevelAS, const RenderTargets& renderTargets, - const RtxdiResources& resources, - const RtxgiIntegration* rtxgi) + const RtxdiResources& resources) { assert(&renderTargets); assert(&resources); @@ -187,17 +215,6 @@ void LightingPasses::CreateBindingSet( m_PrevBindingSet = bindingSet; } -#ifdef WITH_RTXGI - assert(rtxgi); - - auto rtxgiBindingSetDesc = nvrhi::BindingSetDesc() - .addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(40, rtxgi->GetConstantBuffer())) - .addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(41, rtxgi->GetVolumeResourceIndicesBuffer())) - .addItem(nvrhi::BindingSetItem::Sampler(40, rtxgi->GetProbeSampler())); - - m_RtxgiBindingSet = m_Device->createBindingSet(rtxgiBindingSetDesc, m_RtxgiBindingLayout); -#endif - const auto& environmentPdfDesc = resources.EnvironmentPdfTexture->getDesc(); m_EnvironmentPdfTextureSize.x = environmentPdfDesc.width; m_EnvironmentPdfTextureSize.y = environmentPdfDesc.height; @@ -257,11 +274,11 @@ void LightingPasses::ExecuteRayTracingPass(nvrhi::ICommandList* commandList, Ray commandList->endMarker(); } -donut::engine::ShaderMacro LightingPasses::GetRegirMacro(const rtxdi::ContextParameters& contextParameters) +donut::engine::ShaderMacro LightingPasses::GetRegirMacro(const rtxdi::ReGIRStaticParameters& regirStaticParams) { std::string regirMode; - switch (contextParameters.ReGIR.Mode) + switch (regirStaticParams.Mode) { case rtxdi::ReGIRMode::Disabled: regirMode = "RTXDI_REGIR_DISABLED"; @@ -277,34 +294,52 @@ donut::engine::ShaderMacro LightingPasses::GetRegirMacro(const rtxdi::ContextPar return { "RTXDI_REGIR_MODE", regirMode }; } -void LightingPasses::CreatePipelines(const rtxdi::ContextParameters& contextParameters, bool useRayQuery) +void LightingPasses::createPresamplingPipelines() { - std::vector regirMacros = { - GetRegirMacro(contextParameters) - }; - CreateComputePass(m_PresampleLightsPass, "app/LightingPasses/PresampleLights.hlsl", {}); CreateComputePass(m_PresampleEnvironmentMapPass, "app/LightingPasses/PresampleEnvironmentMap.hlsl", {}); +} - if (contextParameters.ReGIR.Mode != rtxdi::ReGIRMode::Disabled) +void LightingPasses::createReGIRPipeline(const rtxdi::ReGIRStaticParameters& regirStaticParams, const std::vector& regirMacros) +{ + if (regirStaticParams.Mode != rtxdi::ReGIRMode::Disabled) { CreateComputePass(m_PresampleReGIR, "app/LightingPasses/PresampleReGIR.hlsl", regirMacros); } +} - m_GenerateInitialSamplesPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/GenerateInitialSamples.hlsl", regirMacros, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); - m_TemporalResamplingPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/TemporalResampling.hlsl", {}, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); - m_SpatialResamplingPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/SpatialResampling.hlsl", {}, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); - m_ShadeSamplesPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/ShadeSamples.hlsl", regirMacros, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); +void LightingPasses::createReSTIRDIPipelines(const std::vector& regirMacros, bool useRayQuery) +{ + m_GenerateInitialSamplesPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/DIGenerateInitialSamples.hlsl", regirMacros, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); + m_TemporalResamplingPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/DITemporalResampling.hlsl", {}, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); + m_SpatialResamplingPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/DISpatialResampling.hlsl", {}, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); + m_ShadeSamplesPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/DIShadeSamples.hlsl", regirMacros, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); m_BrdfRayTracingPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/BrdfRayTracing.hlsl", {}, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); - m_ShadeSecondarySurfacesPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/ShadeSecondarySurfaces.hlsl", regirMacros, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, m_RtxgiBindingLayout, m_BindlessLayout); - m_FusedResamplingPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/FusedResampling.hlsl", regirMacros, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); - m_GradientsPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/ComputeGradients.hlsl", {}, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); + m_ShadeSecondarySurfacesPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/ShadeSecondarySurfaces.hlsl", regirMacros, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); + m_FusedResamplingPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/DIFusedResampling.hlsl", regirMacros, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); + m_GradientsPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/DIComputeGradients.hlsl", {}, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); +} + +void LightingPasses::createReSTIRGIPipelines(bool useRayQuery) +{ m_GITemporalResamplingPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/GITemporalResampling.hlsl", {}, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); m_GISpatialResamplingPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/GISpatialResampling.hlsl", {}, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); m_GIFusedResamplingPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/GIFusedResampling.hlsl", {}, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); m_GIFinalShadingPass.Init(m_Device, *m_ShaderFactory, "app/LightingPasses/GIFinalShading.hlsl", {}, useRayQuery, RTXDI_SCREEN_SPACE_GROUP_SIZE, m_BindingLayout, nullptr, m_BindlessLayout); } +void LightingPasses::CreatePipelines(const rtxdi::ReGIRStaticParameters& regirStaticParams, bool useRayQuery) +{ + std::vector regirMacros = { + GetRegirMacro(regirStaticParams) + }; + + createPresamplingPipelines(); + createReGIRPipeline(regirStaticParams, regirMacros); + createReSTIRDIPipelines(regirMacros, useRayQuery); + createReSTIRGIPipelines(useRayQuery); +} + #if WITH_NRD static void NrdHitDistanceParamsToFloat4(const nrd::HitDistanceParameters* params, dm::float4& out) { @@ -316,26 +351,94 @@ static void NrdHitDistanceParamsToFloat4(const nrd::HitDistanceParameters* param } #endif +void FillReSTIRDIConstants(ReSTIRDI_Parameters& params, const rtxdi::ReSTIRDIContext& restirDIContext, const RTXDI_LightBufferParameters& lightBufferParameters) +{ + params.reservoirBufferParams = restirDIContext.getReservoirBufferParameters(); + params.bufferIndices = restirDIContext.getBufferIndices(); + params.initialSamplingParams = restirDIContext.getInitialSamplingParameters(); + params.initialSamplingParams.environmentMapImportanceSampling = lightBufferParameters.environmentLightParams.lightPresent; + if (!params.initialSamplingParams.environmentMapImportanceSampling) + params.initialSamplingParams.numPrimaryEnvironmentSamples = 0; + params.temporalResamplingParams = restirDIContext.getTemporalResamplingParameters(); + params.spatialResamplingParams = restirDIContext.getSpatialResamplingParameters(); + params.shadingParams = restirDIContext.getShadingParameters(); +} + +void FillReGIRConstants(ReGIR_Parameters& params, const rtxdi::ReGIRContext& regirContext) +{ + auto staticParams = regirContext.getReGIRStaticParameters(); + auto dynamicParams = regirContext.getReGIRDynamicParameters(); + auto gridParams = regirContext.getReGIRGridCalculatedParameters(); + auto onionParams = regirContext.getReGIROnionCalculatedParameters(); + + params.gridParams.cellsX = staticParams.gridParameters.GridSize.x; + params.gridParams.cellsY = staticParams.gridParameters.GridSize.y; + params.gridParams.cellsZ = staticParams.gridParameters.GridSize.z; + + params.commonParams.numRegirBuildSamples = dynamicParams.regirNumBuildSamples; + params.commonParams.risBufferOffset = regirContext.getReGIRCellOffset(); + params.commonParams.lightsPerCell = staticParams.LightsPerCell; + params.commonParams.centerX = dynamicParams.center.x; + params.commonParams.centerY = dynamicParams.center.y; + params.commonParams.centerZ = dynamicParams.center.z; + params.commonParams.cellSize = (staticParams.Mode == rtxdi::ReGIRMode::Onion) + ? dynamicParams.regirCellSize * 0.5f // Onion operates with radii, while "size" feels more like diameter + : dynamicParams.regirCellSize; + params.commonParams.localLightSamplingFallbackMode = static_cast(dynamicParams.fallbackSamplingMode); + params.commonParams.localLightPresamplingMode = static_cast(dynamicParams.presamplingMode); + params.commonParams.samplingJitter = std::max(0.f, dynamicParams.regirSamplingJitter * 2.f); + params.onionParams.cubicRootFactor = onionParams.regirOnionCubicRootFactor; + params.onionParams.linearFactor = onionParams.regirOnionLinearFactor; + params.onionParams.numLayerGroups = uint32_t(onionParams.regirOnionLayers.size()); + + assert(onionParams.regirOnionLayers.size() <= RTXDI_ONION_MAX_LAYER_GROUPS); + for (int group = 0; group < int(onionParams.regirOnionLayers.size()); group++) + { + params.onionParams.layers[group] = onionParams.regirOnionLayers[group]; + params.onionParams.layers[group].innerRadius *= params.commonParams.cellSize; + params.onionParams.layers[group].outerRadius *= params.commonParams.cellSize; + } + + assert(onionParams.regirOnionRings.size() <= RTXDI_ONION_MAX_RINGS); + for (int n = 0; n < int(onionParams.regirOnionRings.size()); n++) + { + params.onionParams.rings[n] = onionParams.regirOnionRings[n]; + } + + params.onionParams.cubicRootFactor = regirContext.getReGIROnionCalculatedParameters().regirOnionCubicRootFactor; +} + +void FillReSTIRGIConstants(ReSTIRGI_Parameters& constants, const rtxdi::ReSTIRGIContext& restirGIContext) +{ + constants.reservoirBufferParams = restirGIContext.getReservoirBufferParameters(); + constants.bufferIndices = restirGIContext.getBufferIndices(); + constants.temporalResamplingParams = restirGIContext.getTemporalResamplingParameters(); + constants.spatialResamplingParams = restirGIContext.getSpatialResamplingParameters(); + constants.finalShadingParams = restirGIContext.getFinalShadingParameters(); +} + +void FillBRDFPTConstants(BRDFPathTracing_Parameters& constants, const GBufferSettings& gbufferSettings, const LightingPasses::RenderSettings& lightingSettings, const RTXDI_LightBufferParameters& lightBufferParameters) +{ + constants = lightingSettings.brdfptParams; + constants.materialOverrideParams.minSecondaryRoughness = lightingSettings.brdfptParams.materialOverrideParams.minSecondaryRoughness; + constants.materialOverrideParams.roughnessOverride = gbufferSettings.enableRoughnessOverride ? gbufferSettings.roughnessOverride : -1.f; + constants.materialOverrideParams.metalnessOverride = gbufferSettings.enableMetalnessOverride ? gbufferSettings.metalnessOverride : -1.f; + constants.secondarySurfaceReSTIRDIParams.initialSamplingParams.environmentMapImportanceSampling = lightBufferParameters.environmentLightParams.lightPresent; + if (!constants.secondarySurfaceReSTIRDIParams.initialSamplingParams.environmentMapImportanceSampling) + constants.secondarySurfaceReSTIRDIParams.initialSamplingParams.numPrimaryEnvironmentSamples = 0; +} + void LightingPasses::FillResamplingConstants( ResamplingConstants& constants, const RenderSettings& lightingSettings, - const rtxdi::FrameParameters& frameParameters) + const rtxdi::ImportanceSamplingContext& isContext) { - const bool useTemporalResampling = - lightingSettings.resamplingMode == ResamplingMode::Temporal || - lightingSettings.resamplingMode == ResamplingMode::TemporalAndSpatial || - lightingSettings.resamplingMode == ResamplingMode::FusedSpatiotemporal; - - const bool useSpatialResampling = - lightingSettings.resamplingMode == ResamplingMode::Spatial || - lightingSettings.resamplingMode == ResamplingMode::TemporalAndSpatial || - lightingSettings.resamplingMode == ResamplingMode::FusedSpatiotemporal; + const RTXDI_LightBufferParameters& lightBufferParameters = isContext.getLightBufferParameters(); constants.enablePreviousTLAS = lightingSettings.enablePreviousTLAS; constants.denoiserMode = lightingSettings.denoiserMode; - constants.enableAlphaTestedGeometry = lightingSettings.enableAlphaTestedGeometry; - constants.enableTransparentGeometry = lightingSettings.enableTransparentGeometry; - constants.enableDenoiserInputPacking = lightingSettings.enableDenoiserInputPacking; + constants.sceneConstants.enableAlphaTestedGeometry = lightingSettings.enableAlphaTestedGeometry; + constants.sceneConstants.enableTransparentGeometry = lightingSettings.enableTransparentGeometry; constants.visualizeRegirCells = lightingSettings.visualizeRegirCells; #if WITH_NRD if (lightingSettings.denoiserMode != DENOISER_MODE_OFF) @@ -345,140 +448,72 @@ void LightingPasses::FillResamplingConstants( } #endif - constants.numPrimaryRegirSamples = lightingSettings.enableReGIR ? lightingSettings.numPrimaryRegirSamples : 0; - constants.numPrimaryLocalLightSamples = lightingSettings.numPrimaryLocalLightSamples; - constants.numPrimaryBrdfSamples = lightingSettings.numPrimaryBrdfSamples; - constants.numPrimaryInfiniteLightSamples = lightingSettings.numPrimaryInfiniteLightSamples; - constants.brdfCutoff = lightingSettings.brdfCutoff; - constants.numIndirectRegirSamples = lightingSettings.enableReGIR ? lightingSettings.numIndirectRegirSamples : 0; - constants.numIndirectLocalLightSamples = lightingSettings.numIndirectLocalLightSamples; - constants.numIndirectInfiniteLightSamples = lightingSettings.numIndirectInfiniteLightSamples; - - constants.enableInitialVisibility = lightingSettings.enableInitialVisibility; - constants.enableFinalVisibility = lightingSettings.enableFinalVisibility; - constants.temporalNormalThreshold = lightingSettings.temporalNormalThreshold; - constants.temporalDepthThreshold = lightingSettings.temporalDepthThreshold; - constants.maxHistoryLength = lightingSettings.maxHistoryLength; - constants.temporalBiasCorrection = lightingSettings.temporalBiasCorrection; - constants.boilingFilterStrength = lightingSettings.enableBoilingFilter ? lightingSettings.boilingFilterStrength : 0.f; - constants.numSpatialSamples = useSpatialResampling ? lightingSettings.numSpatialSamples : 0; - constants.numDisocclusionBoostSamples = useTemporalResampling ? lightingSettings.numDisocclusionBoostSamples : 0; - constants.spatialSamplingRadius = lightingSettings.spatialSamplingRadius; - constants.spatialNormalThreshold = lightingSettings.spatialNormalThreshold; - constants.spatialDepthThreshold = lightingSettings.spatialDepthThreshold; - constants.spatialBiasCorrection = lightingSettings.spatialBiasCorrection; - constants.reuseFinalVisibility = lightingSettings.reuseFinalVisibility; - constants.finalVisibilityMaxAge = lightingSettings.finalVisibilityMaxAge; - constants.finalVisibilityMaxDistance = lightingSettings.finalVisibilityMaxDistance; - constants.discardInvisibleSamples = lightingSettings.discardInvisibleSamples; - constants.numRegirBuildSamples = lightingSettings.numRegirBuildSamples; - constants.enablePermutationSampling = lightingSettings.enablePermutationSampling; - constants.permutationSamplingThreshold = lightingSettings.permutationSamplingThreshold; - - constants.numSecondarySamples = lightingSettings.enableSecondaryResampling ? lightingSettings.numSecondarySamples : 0; - constants.secondaryBiasCorrection = lightingSettings.secondaryBiasCorrection; - constants.secondarySamplingRadius = lightingSettings.secondarySamplingRadius; - constants.secondaryDepthThreshold = lightingSettings.secondaryDepthThreshold; - constants.secondaryNormalThreshold = lightingSettings.secondaryNormalThreshold; - - if (lightingSettings.resamplingMode == ResamplingMode::FusedSpatiotemporal) - { - constants.initialOutputBufferIndex = (m_LastFrameOutputReservoir + 1) % RtxdiResources::c_NumReservoirBuffers; - constants.temporalInputBufferIndex = m_LastFrameOutputReservoir; - constants.shadeInputBufferIndex = constants.initialOutputBufferIndex; - } - else - { - constants.initialOutputBufferIndex = (m_LastFrameOutputReservoir + 1) % RtxdiResources::c_NumReservoirBuffers; - constants.temporalInputBufferIndex = m_LastFrameOutputReservoir; - constants.temporalOutputBufferIndex = (constants.temporalInputBufferIndex + 1) % RtxdiResources::c_NumReservoirBuffers; - constants.spatialInputBufferIndex = useTemporalResampling - ? constants.temporalOutputBufferIndex - : constants.initialOutputBufferIndex; - constants.spatialOutputBufferIndex = (constants.spatialInputBufferIndex + 1) % RtxdiResources::c_NumReservoirBuffers; - constants.shadeInputBufferIndex = useSpatialResampling - ? constants.spatialOutputBufferIndex - : constants.temporalOutputBufferIndex; - } + constants.lightBufferParams = isContext.getLightBufferParameters(); + constants.localLightsRISBufferSegmentParams = isContext.getLocalLightRISBufferSegmentParams(); + constants.environmentLightRISBufferSegmentParams = isContext.getEnvironmentLightRISBufferSegmentParams(); + constants.runtimeParams = isContext.getReSTIRDIContext().getRuntimeParams(); + FillReSTIRDIConstants(constants.restirDI, isContext.getReSTIRDIContext(), isContext.getLightBufferParameters()); + FillReGIRConstants(constants.regir, isContext.getReGIRContext()); + FillReSTIRGIConstants(constants.restirGI, isContext.getReSTIRGIContext()); constants.localLightPdfTextureSize = m_LocalLightPdfTextureSize; - - if (frameParameters.environmentLightPresent) + + if (lightBufferParameters.environmentLightParams.lightPresent) { constants.environmentPdfTextureSize = m_EnvironmentPdfTextureSize; - constants.numPrimaryEnvironmentSamples = lightingSettings.numPrimaryEnvironmentSamples; - constants.numIndirectEnvironmentSamples = lightingSettings.numIndirectEnvironmentSamples; - constants.environmentMapImportanceSampling = 1; } - m_CurrentFrameOutputReservoir = constants.shadeInputBufferIndex; -} - -void LightingPasses::FillConstantBufferForProbeTracing( - nvrhi::ICommandList* commandList, - rtxdi::Context& context, - const RenderSettings& localSettings, - const rtxdi::FrameParameters& frameParameters) -{ - ResamplingConstants constants = {}; - constants.frameIndex = frameParameters.frameIndex; - context.FillRuntimeParameters(constants.runtimeParams, frameParameters); - FillResamplingConstants(constants, localSettings, frameParameters); - - constants.numIndirectRegirSamples = localSettings.enableReGIR ? localSettings.numRtxgiRegirSamples : 0; - constants.numIndirectLocalLightSamples = localSettings.numRtxgiLocalLightSamples; - constants.numIndirectInfiniteLightSamples = localSettings.numRtxgiInfiniteLightSamples; - constants.numIndirectEnvironmentSamples = frameParameters.environmentLightPresent ? localSettings.numRtxgiEnvironmentSamples : 0; - - commandList->writeBuffer(m_ConstantBuffer, &constants, sizeof(constants)); + m_CurrentFrameOutputReservoir = isContext.getReSTIRDIContext().getBufferIndices().shadingInputBufferIndex; } void LightingPasses::PrepareForLightSampling( nvrhi::ICommandList* commandList, - rtxdi::Context& context, + rtxdi::ImportanceSamplingContext& isContext, const donut::engine::IView& view, const donut::engine::IView& previousView, const RenderSettings& localSettings, - const rtxdi::FrameParameters& frameParameters, bool enableAccumulation) { + rtxdi::ReSTIRDIContext& restirDIContext = isContext.getReSTIRDIContext(); + rtxdi::ReGIRContext& regirContext = isContext.getReGIRContext(); + ResamplingConstants constants = {}; - constants.frameIndex = frameParameters.frameIndex; + constants.frameIndex = restirDIContext.getFrameIndex(); view.FillPlanarViewConstants(constants.view); previousView.FillPlanarViewConstants(constants.prevView); - context.FillRuntimeParameters(constants.runtimeParams, frameParameters); - FillResamplingConstants(constants, localSettings, frameParameters); + FillResamplingConstants(constants, localSettings, isContext); constants.enableAccumulation = enableAccumulation; commandList->writeBuffer(m_ConstantBuffer, &constants, sizeof(constants)); - if (frameParameters.enableLocalLightImportanceSampling && - frameParameters.numLocalLights > 0) + auto& lightBufferParams = isContext.getLightBufferParameters(); + + if (isContext.isLocalLightPowerRISEnabled() && + lightBufferParams.localLightBufferRegion.numLights > 0) { dm::int2 presampleDispatchSize = { - dm::div_ceil(context.GetParameters().TileSize, RTXDI_PRESAMPLING_GROUP_SIZE), - int(context.GetParameters().TileCount) + dm::div_ceil(isContext.getLocalLightRISBufferSegmentParams().tileSize, RTXDI_PRESAMPLING_GROUP_SIZE), + int(isContext.getLocalLightRISBufferSegmentParams().tileCount) }; ExecuteComputePass(commandList, m_PresampleLightsPass, "PresampleLights", presampleDispatchSize, ProfilerSection::PresampleLights); } - if (frameParameters.environmentLightPresent) + if (lightBufferParams.environmentLightParams.lightPresent) { dm::int2 presampleDispatchSize = { - dm::div_ceil(context.GetParameters().EnvironmentTileSize, RTXDI_PRESAMPLING_GROUP_SIZE), - int(context.GetParameters().EnvironmentTileCount) + dm::div_ceil(isContext.getEnvironmentLightRISBufferSegmentParams().tileSize, RTXDI_PRESAMPLING_GROUP_SIZE), + int(isContext.getEnvironmentLightRISBufferSegmentParams().tileCount) }; ExecuteComputePass(commandList, m_PresampleEnvironmentMapPass, "PresampleEnvironmentMap", presampleDispatchSize, ProfilerSection::PresampleEnvMap); } - if (context.GetParameters().ReGIR.Mode != rtxdi::ReGIRMode::Disabled && - localSettings.enableReGIR && - frameParameters.numLocalLights > 0) + if (isContext.isReGIREnabled() && + lightBufferParams.localLightBufferRegion.numLights > 0) { dm::int2 worldGridDispatchSize = { - dm::div_ceil(context.GetReGIRLightSlotCount(), RTXDI_GRID_BUILD_GROUP_SIZE), + dm::div_ceil(regirContext.getReGIRLightSlotCount(), RTXDI_GRID_BUILD_GROUP_SIZE), 1 }; @@ -488,7 +523,7 @@ void LightingPasses::PrepareForLightSampling( void LightingPasses::RenderDirectLighting( nvrhi::ICommandList* commandList, - rtxdi::Context& context, + rtxdi::ReSTIRDIContext& context, const donut::engine::IView& view, const RenderSettings& localSettings) { @@ -497,7 +532,7 @@ void LightingPasses::RenderDirectLighting( view.GetViewExtent().height() }; - if (context.GetParameters().CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off) + if (context.getStaticParameters().CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off) dispatchSize.x /= 2; // Run the lighting passes in the necessary sequence: one fused kernel or multiple separate passes. @@ -506,148 +541,81 @@ void LightingPasses::RenderDirectLighting( // because NVRHI misses them, as the binding sets are exactly the same between these passes. // That equality makes NVRHI take a shortcut for performance and it doesn't look at bindings at all. - ExecuteRayTracingPass(commandList, m_GenerateInitialSamplesPass, localSettings.enableRayCounts, "GenerateInitialSamples", dispatchSize, ProfilerSection::InitialSamples); + ExecuteRayTracingPass(commandList, m_GenerateInitialSamplesPass, localSettings.enableRayCounts, "DIGenerateInitialSamples", dispatchSize, ProfilerSection::InitialSamples); - if (localSettings.resamplingMode == ResamplingMode::FusedSpatiotemporal) + if (context.getResamplingMode() == rtxdi::ReSTIRDI_ResamplingMode::FusedSpatiotemporal) { nvrhi::utils::BufferUavBarrier(commandList, m_LightReservoirBuffer); - ExecuteRayTracingPass(commandList, m_FusedResamplingPass, localSettings.enableRayCounts, "FusedResampling", dispatchSize, ProfilerSection::Shading); + ExecuteRayTracingPass(commandList, m_FusedResamplingPass, localSettings.enableRayCounts, "DIFusedResampling", dispatchSize, ProfilerSection::Shading); } else { - if (localSettings.resamplingMode == ResamplingMode::Temporal || localSettings.resamplingMode == ResamplingMode::TemporalAndSpatial) + if (context.getResamplingMode() == rtxdi::ReSTIRDI_ResamplingMode::Temporal || context.getResamplingMode() == rtxdi::ReSTIRDI_ResamplingMode::TemporalAndSpatial) { nvrhi::utils::BufferUavBarrier(commandList, m_LightReservoirBuffer); - ExecuteRayTracingPass(commandList, m_TemporalResamplingPass, localSettings.enableRayCounts, "TemporalResampling", dispatchSize, ProfilerSection::TemporalResampling); + ExecuteRayTracingPass(commandList, m_TemporalResamplingPass, localSettings.enableRayCounts, "DITemporalResampling", dispatchSize, ProfilerSection::TemporalResampling); } - if (localSettings.resamplingMode == ResamplingMode::Spatial || localSettings.resamplingMode == ResamplingMode::TemporalAndSpatial) + if (context.getResamplingMode() == rtxdi::ReSTIRDI_ResamplingMode::Spatial || context.getResamplingMode() == rtxdi::ReSTIRDI_ResamplingMode::TemporalAndSpatial) { nvrhi::utils::BufferUavBarrier(commandList, m_LightReservoirBuffer); - ExecuteRayTracingPass(commandList, m_SpatialResamplingPass, localSettings.enableRayCounts, "SpatialResampling", dispatchSize, ProfilerSection::SpatialResampling); + ExecuteRayTracingPass(commandList, m_SpatialResamplingPass, localSettings.enableRayCounts, "DISpatialResampling", dispatchSize, ProfilerSection::SpatialResampling); } nvrhi::utils::BufferUavBarrier(commandList, m_LightReservoirBuffer); - ExecuteRayTracingPass(commandList, m_ShadeSamplesPass, localSettings.enableRayCounts, "ShadeSamples", dispatchSize, ProfilerSection::Shading); + ExecuteRayTracingPass(commandList, m_ShadeSamplesPass, localSettings.enableRayCounts, "DIShadeSamples", dispatchSize, ProfilerSection::Shading); } if (localSettings.enableGradients) { nvrhi::utils::BufferUavBarrier(commandList, m_LightReservoirBuffer); - ExecuteRayTracingPass(commandList, m_GradientsPass, localSettings.enableRayCounts, "Gradients", (dispatchSize + RTXDI_GRAD_FACTOR - 1) / RTXDI_GRAD_FACTOR, ProfilerSection::Gradients); + ExecuteRayTracingPass(commandList, m_GradientsPass, localSettings.enableRayCounts, "DIGradients", (dispatchSize + RTXDI_GRAD_FACTOR - 1) / RTXDI_GRAD_FACTOR, ProfilerSection::Gradients); } } void LightingPasses::RenderBrdfRays( nvrhi::ICommandList* commandList, - rtxdi::Context& context, + rtxdi::ImportanceSamplingContext& isContext, const donut::engine::IView& view, const donut::engine::IView& previousView, const RenderSettings& localSettings, const GBufferSettings& gbufferSettings, - const rtxdi::FrameParameters& frameParameters, const EnvironmentLight& environmentLight, bool enableIndirect, bool enableAdditiveBlend, bool enableEmissiveSurfaces, - uint32_t numRtxgiVolumes, bool enableAccumulation, - bool enableReStirGI + bool enableReSTIRGI ) { ResamplingConstants constants = {}; view.FillPlanarViewConstants(constants.view); previousView.FillPlanarViewConstants(constants.prevView); - constants.frameIndex = frameParameters.frameIndex; + rtxdi::ReSTIRDIContext& restirDIContext = isContext.getReSTIRDIContext(); + rtxdi::ReSTIRGIContext& restirGIContext = isContext.getReSTIRGIContext(); + + constants.frameIndex = restirDIContext.getFrameIndex(); constants.denoiserMode = localSettings.denoiserMode; constants.enableBrdfIndirect = enableIndirect; constants.enableBrdfAdditiveBlend = enableAdditiveBlend; - constants.enableIndirectEmissiveSurfaces = enableEmissiveSurfaces; - constants.numRtxgiVolumes = numRtxgiVolumes; - constants.enableEnvironmentMap = (environmentLight.textureIndex >= 0); constants.enableAccumulation = enableAccumulation; - constants.environmentMapTextureIndex = (environmentLight.textureIndex >= 0) ? environmentLight.textureIndex : 0; - constants.environmentScale = environmentLight.radianceScale.x; - constants.environmentRotation = environmentLight.rotation; - constants.roughnessOverride = gbufferSettings.enableRoughnessOverride ? gbufferSettings.roughnessOverride : -1.f; - constants.metalnessOverride = gbufferSettings.enableMetalnessOverride ? gbufferSettings.metalnessOverride : -1.f; - constants.minSecondaryRoughness = localSettings.minSecondaryRoughness; - constants.enableFallbackSampling = localSettings.reStirGI.enableFallbackSampling; - constants.giEnableFinalMIS = localSettings.reStirGI.enableFinalMIS; - context.FillRuntimeParameters(constants.runtimeParams, frameParameters); - FillResamplingConstants(constants, localSettings, frameParameters); - - // Override various DI related settings set in FillResamplingConstants - - constants.boilingFilterStrength = localSettings.reStirGI.enableBoilingFilter ? localSettings.reStirGI.boilingFilterStrength : 0.f; - - constants.enableReSTIRIndirect = enableReStirGI; - - // There are 2 sets of GI reservoirs in total. - switch(localSettings.reStirGI.resamplingMode) - { - case ResamplingMode::None: - constants.initialOutputBufferIndex = 0; - constants.shadeInputBufferIndex = 0; - break; - case ResamplingMode::Temporal: - constants.initialOutputBufferIndex = frameParameters.frameIndex & 1; - constants.temporalInputBufferIndex = !constants.initialOutputBufferIndex; - constants.temporalOutputBufferIndex = constants.initialOutputBufferIndex; - constants.shadeInputBufferIndex = constants.temporalOutputBufferIndex; - break; - case ResamplingMode::Spatial: - constants.initialOutputBufferIndex = 0; - constants.spatialInputBufferIndex = 0; - constants.spatialOutputBufferIndex = 1; - constants.shadeInputBufferIndex = 1; - break; - case ResamplingMode::TemporalAndSpatial: - constants.initialOutputBufferIndex = 0; - constants.temporalInputBufferIndex = 1; - constants.temporalOutputBufferIndex = 0; - constants.spatialInputBufferIndex = 0; - constants.spatialOutputBufferIndex = 1; - constants.shadeInputBufferIndex = 1; - break; - case ResamplingMode::FusedSpatiotemporal: - constants.initialOutputBufferIndex = frameParameters.frameIndex & 1; - constants.temporalInputBufferIndex = !constants.initialOutputBufferIndex; - constants.spatialOutputBufferIndex = constants.initialOutputBufferIndex; - constants.shadeInputBufferIndex = constants.spatialOutputBufferIndex; - break; - } - - m_CurrentFrameGIOutputReservoir = constants.shadeInputBufferIndex; - - constants.temporalDepthThreshold = localSettings.reStirGI.depthThreshold; - constants.temporalNormalThreshold = localSettings.reStirGI.normalThreshold; - constants.spatialDepthThreshold = localSettings.reStirGI.depthThreshold; - constants.spatialNormalThreshold = localSettings.reStirGI.normalThreshold; - - constants.giReservoirMaxAge = localSettings.reStirGI.maxReservoirAge; - constants.maxHistoryLength = localSettings.reStirGI.maxHistoryLength; - constants.enablePermutationSampling = localSettings.reStirGI.enablePermutationSampling; - - constants.numSpatialSamples = localSettings.reStirGI.numSpatialSamples; - constants.spatialSamplingRadius = localSettings.reStirGI.samplingRadius; - constants.giEnableFinalVisibility = localSettings.reStirGI.enableFinalVisibility; - - constants.temporalBiasCorrection = localSettings.reStirGI.temporalBiasCorrection; - constants.spatialBiasCorrection = localSettings.reStirGI.spatialBiasCorrection; - // Pairwise bias correction is not supported, fallback to RT for safety (although UI should not allow it anyway) - if (constants.temporalBiasCorrection == RTXDI_BIAS_CORRECTION_PAIRWISE) { - constants.temporalBiasCorrection = RTXDI_BIAS_CORRECTION_RAY_TRACED; - } - if (constants.spatialBiasCorrection == RTXDI_BIAS_CORRECTION_PAIRWISE) { - constants.spatialBiasCorrection = RTXDI_BIAS_CORRECTION_RAY_TRACED; - } + constants.sceneConstants.enableEnvironmentMap = (environmentLight.textureIndex >= 0); + constants.sceneConstants.environmentMapTextureIndex = (environmentLight.textureIndex >= 0) ? environmentLight.textureIndex : 0; + constants.sceneConstants.environmentScale = environmentLight.radianceScale.x; + constants.sceneConstants.environmentRotation = environmentLight.rotation; + FillResamplingConstants(constants, localSettings, isContext); + FillBRDFPTConstants(constants.brdfPT, gbufferSettings, localSettings, isContext.getLightBufferParameters()); + constants.brdfPT.enableIndirectEmissiveSurfaces = enableEmissiveSurfaces; + constants.brdfPT.enableReSTIRGI = enableReSTIRGI; + + ReSTIRGI_BufferIndices restirGIBufferIndices = restirGIContext.getBufferIndices(); + m_CurrentFrameGIOutputReservoir = restirGIBufferIndices.finalShadingInputBufferIndex; commandList->writeBuffer(m_ConstantBuffer, &constants, sizeof(constants)); @@ -656,7 +624,7 @@ void LightingPasses::RenderBrdfRays( view.GetViewExtent().height() }; - if (context.GetParameters().CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off) + if (restirDIContext.getStaticParameters().CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off) dispatchSize.x /= 2; ExecuteRayTracingPass(commandList, m_BrdfRayTracingPass, localSettings.enableRayCounts, "BrdfRayTracingPass", dispatchSize, ProfilerSection::BrdfRays); @@ -666,11 +634,12 @@ void LightingPasses::RenderBrdfRays( // Place an explicit UAV barrier between the passes. See the note on barriers in RenderDirectLighting(...) nvrhi::utils::BufferUavBarrier(commandList, m_SecondarySurfaceBuffer); - ExecuteRayTracingPass(commandList, m_ShadeSecondarySurfacesPass, localSettings.enableRayCounts, "ShadeSecondarySurfaces", dispatchSize, ProfilerSection::ShadeSecondary, m_RtxgiBindingSet); + ExecuteRayTracingPass(commandList, m_ShadeSecondarySurfacesPass, localSettings.enableRayCounts, "ShadeSecondarySurfaces", dispatchSize, ProfilerSection::ShadeSecondary, nullptr); - if (enableReStirGI) + if (enableReSTIRGI) { - if (localSettings.reStirGI.resamplingMode == ResamplingMode::FusedSpatiotemporal) + rtxdi::ReSTIRGI_ResamplingMode resamplingMode = restirGIContext.getResamplingMode(); + if (resamplingMode == rtxdi::ReSTIRGI_ResamplingMode::FusedSpatiotemporal) { nvrhi::utils::BufferUavBarrier(commandList, m_GIReservoirBuffer); @@ -678,16 +647,16 @@ void LightingPasses::RenderBrdfRays( } else { - if (localSettings.reStirGI.resamplingMode == ResamplingMode::Temporal || - localSettings.reStirGI.resamplingMode == ResamplingMode::TemporalAndSpatial) + if (resamplingMode == rtxdi::ReSTIRGI_ResamplingMode::Temporal || + resamplingMode == rtxdi::ReSTIRGI_ResamplingMode::TemporalAndSpatial) { nvrhi::utils::BufferUavBarrier(commandList, m_GIReservoirBuffer); ExecuteRayTracingPass(commandList, m_GITemporalResamplingPass, localSettings.enableRayCounts, "GITemporalResampling", dispatchSize, ProfilerSection::GITemporalResampling, nullptr); } - if (localSettings.reStirGI.resamplingMode == ResamplingMode::Spatial || - localSettings.reStirGI.resamplingMode == ResamplingMode::TemporalAndSpatial) + if (resamplingMode == rtxdi::ReSTIRGI_ResamplingMode::Spatial || + resamplingMode == rtxdi::ReSTIRGI_ResamplingMode::TemporalAndSpatial) { nvrhi::utils::BufferUavBarrier(commandList, m_GIReservoirBuffer); diff --git a/src/LightingPasses.h b/src/LightingPasses.h index c2c18c5..f4f573f 100644 --- a/src/LightingPasses.h +++ b/src/LightingPasses.h @@ -17,7 +17,9 @@ #include #include -#include +#include +#include +#include "../shaders/BRDFPTParameters.h" namespace donut::engine { @@ -30,10 +32,9 @@ namespace donut::engine namespace rtxdi { - struct FrameParameters; - class Context; - struct ResamplingSettings; - struct ContextParameters; + class ReSTIRDIContext; + struct ReGIRStaticParameters; + class ImportanceSamplingContext; } class RenderTargets; @@ -41,7 +42,6 @@ class RtxdiResources; class Profiler; class EnvironmentLight; struct ResamplingConstants; -class RtxgiIntegration; struct GBufferSettings; namespace nrd @@ -52,35 +52,9 @@ namespace nrd // A 32-bit bool type to directly use from the command line parser. typedef int ibool; -enum class ResamplingMode : uint32_t -{ - None = 0, - Temporal = 1, - Spatial = 2, - TemporalAndSpatial = 3, - FusedSpatiotemporal = 4, -}; - -struct ReStirGIParameters -{ - ResamplingMode resamplingMode = ResamplingMode::TemporalAndSpatial; - float depthThreshold = 0.1f; - float normalThreshold = 0.6f; - uint32_t maxReservoirAge = 30; - uint32_t maxHistoryLength = 8; - float samplingRadius = 30.f; - uint32_t numSpatialSamples = 2; - - ibool enableBoilingFilter = true; - float boilingFilterStrength = 0.2f; - - uint32_t temporalBiasCorrection = RTXDI_BIAS_CORRECTION_BASIC; - uint32_t spatialBiasCorrection = RTXDI_BIAS_CORRECTION_BASIC; - ibool enablePermutationSampling = false; - ibool enableFinalVisibility = true; - ibool enableFallbackSampling = true; - ibool enableFinalMIS = true; -}; +BRDFPathTracing_MaterialOverrideParameters getDefaultBRDFPathTracingMaterialOverrideParams(); +BRDFPathTracing_SecondarySurfaceReSTIRDIParameters getDefaultBRDFPathTracingSecondarySurfaceReSTIRDIParams(); +BRDFPathTracing_Parameters getDefaultBRDFPathTracingParams(); class LightingPasses { @@ -109,10 +83,8 @@ class LightingPasses RayTracingPass m_GIFinalShadingPass; nvrhi::BindingLayoutHandle m_BindingLayout; nvrhi::BindingLayoutHandle m_BindlessLayout; - nvrhi::BindingLayoutHandle m_RtxgiBindingLayout; nvrhi::BindingSetHandle m_BindingSet; nvrhi::BindingSetHandle m_PrevBindingSet; - nvrhi::BindingSetHandle m_RtxgiBindingSet; nvrhi::BufferHandle m_ConstantBuffer; nvrhi::BufferHandle m_LightReservoirBuffer; nvrhi::BufferHandle m_SecondarySurfaceBuffer; @@ -135,85 +107,28 @@ class LightingPasses void ExecuteRayTracingPass(nvrhi::ICommandList* commandList, RayTracingPass& pass, bool enableRayCounts, const char* passName, dm::int2 dispatchSize, ProfilerSection::Enum profilerSection, nvrhi::IBindingSet* extraBindingSet = nullptr); public: + struct RenderSettings { - ResamplingMode resamplingMode = ResamplingMode::TemporalAndSpatial; uint32_t denoiserMode = 0; - bool enableDenoiserInputPacking = false; ibool enablePreviousTLAS = true; ibool enableAlphaTestedGeometry = true; ibool enableTransparentGeometry = true; - ibool enableInitialVisibility = true; - ibool enableFinalVisibility = true; ibool enableRayCounts = true; - ibool enablePermutationSampling = true; ibool visualizeRegirCells = false; - - uint32_t numPrimaryRegirSamples = 8; - uint32_t numPrimaryLocalLightSamples = 8; - uint32_t numPrimaryBrdfSamples = 1; - float brdfCutoff = 0; - uint32_t numPrimaryInfiniteLightSamples = 1; - uint32_t numPrimaryEnvironmentSamples = 1; - uint32_t numIndirectRegirSamples = 2; - uint32_t numIndirectLocalLightSamples = 2; - uint32_t numIndirectInfiniteLightSamples = 1; - uint32_t numIndirectEnvironmentSamples = 1; - uint32_t numRtxgiRegirSamples = 8; - uint32_t numRtxgiLocalLightSamples = 8; - uint32_t numRtxgiInfiniteLightSamples = 1; - uint32_t numRtxgiEnvironmentSamples = 1; - - float temporalNormalThreshold = 0.5f; - float temporalDepthThreshold = 0.1f; - uint32_t maxHistoryLength = 20; - uint32_t temporalBiasCorrection = RTXDI_BIAS_CORRECTION_BASIC; - float permutationSamplingThreshold = 0.9f; - - ibool enableBoilingFilter = true; - float boilingFilterStrength = 0.2f; - - uint32_t numSpatialSamples = 1; - uint32_t numDisocclusionBoostSamples = 8; - float spatialSamplingRadius = 32.f; - float spatialNormalThreshold = 0.5f; - float spatialDepthThreshold = 0.1f; - uint32_t spatialBiasCorrection = RTXDI_BIAS_CORRECTION_BASIC; - - ibool reuseFinalVisibility = true; - uint32_t finalVisibilityMaxAge = 4; - float finalVisibilityMaxDistance = 16.f; - - ibool enableSecondaryResampling = true; - uint32_t numSecondarySamples = 1; - float secondarySamplingRadius = 4.f; - float secondaryNormalThreshold = 0.9f; - float secondaryDepthThreshold = 0.1f; - uint32_t secondaryBiasCorrection = RTXDI_BIAS_CORRECTION_BASIC; - - // Roughness of secondary surfaces is clamped to suppress caustics. - float minSecondaryRoughness = 0.5f; - - // Enables discarding the reservoirs if their lights turn out to be occluded in the final pass. - // This mode significantly reduces the noise in the penumbra but introduces bias. That bias can be - // corrected by setting 'enableSpatialBiasCorrection' and 'enableTemporalBiasCorrection' to true. - ibool discardInvisibleSamples = false; - - ibool enableReGIR = true; - uint32_t numRegirBuildSamples = 8; ibool enableGradients = true; float gradientLogDarknessBias = -12.f; float gradientSensitivity = 8.f; float confidenceHistoryLength = 0.75f; + + BRDFPathTracing_Parameters brdfptParams = getDefaultBRDFPathTracingParams(); #if WITH_NRD const nrd::HitDistanceParameters* reblurDiffHitDistanceParams = nullptr; const nrd::HitDistanceParameters* reblurSpecHitDistanceParams = nullptr; #endif - - ReStirGIParameters reStirGI; }; LightingPasses( @@ -224,43 +139,39 @@ class LightingPasses std::shared_ptr profiler, nvrhi::IBindingLayout* bindlessLayout); - void CreatePipelines(const rtxdi::ContextParameters& contextParameters, bool useRayQuery); + void CreatePipelines(const rtxdi::ReGIRStaticParameters& regirStaticParams, bool useRayQuery); void CreateBindingSet( nvrhi::rt::IAccelStruct* topLevelAS, nvrhi::rt::IAccelStruct* prevTopLevelAS, const RenderTargets& renderTargets, - const RtxdiResources& resources, - const RtxgiIntegration* rtxgi); + const RtxdiResources& resources); void PrepareForLightSampling( nvrhi::ICommandList* commandList, - rtxdi::Context& context, + rtxdi::ImportanceSamplingContext& context, const donut::engine::IView& view, const donut::engine::IView& previousView, const RenderSettings& localSettings, - const rtxdi::FrameParameters& frameParameters, bool enableAccumulation); void RenderDirectLighting( nvrhi::ICommandList* commandList, - rtxdi::Context& context, + rtxdi::ReSTIRDIContext& context, const donut::engine::IView& view, const RenderSettings& localSettings); void RenderBrdfRays( nvrhi::ICommandList* commandList, - rtxdi::Context& context, + rtxdi::ImportanceSamplingContext& isContext, const donut::engine::IView& view, const donut::engine::IView& previousView, const RenderSettings& localSettings, const GBufferSettings& gbufferSettings, - const rtxdi::FrameParameters& frameParameters, const EnvironmentLight& environmentLight, bool enableIndirect, bool enableAdditiveBlend, bool enableEmissiveSurfaces, - uint32_t numRtxgiVolumes, bool enableAccumulation, bool enableReStirGI ); @@ -272,17 +183,16 @@ class LightingPasses [[nodiscard]] uint32_t GetOutputReservoirBufferIndex() const { return m_CurrentFrameOutputReservoir; } [[nodiscard]] uint32_t GetGIOutputReservoirBufferIndex() const { return m_CurrentFrameGIOutputReservoir; } - void FillConstantBufferForProbeTracing( - nvrhi::ICommandList* commandList, - rtxdi::Context& context, - const RenderSettings& localSettings, - const rtxdi::FrameParameters& frameParameters); - - static donut::engine::ShaderMacro GetRegirMacro(const rtxdi::ContextParameters& contextParameters); + static donut::engine::ShaderMacro GetRegirMacro(const rtxdi::ReGIRStaticParameters& regirStaticParams); private: void FillResamplingConstants( ResamplingConstants& constants, const RenderSettings& lightingSettings, - const rtxdi::FrameParameters& frameParameters); + const rtxdi::ImportanceSamplingContext& isContext); + + void createPresamplingPipelines(); + void createReGIRPipeline(const rtxdi::ReGIRStaticParameters& regirStaticParams, const std::vector& regirMacros); + void createReSTIRDIPipelines(const std::vector& regirMacros, bool useRayQuery); + void createReSTIRGIPipelines(bool useRayQuery); }; diff --git a/src/NrdIntegration.cpp b/src/NrdIntegration.cpp index 48b8a74..7ff2b58 100644 --- a/src/NrdIntegration.cpp +++ b/src/NrdIntegration.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/NrdIntegration.h b/src/NrdIntegration.h index 6bcf084..cdb0b0f 100644 --- a/src/NrdIntegration.h +++ b/src/NrdIntegration.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/PrepareLightsPass.cpp b/src/PrepareLightsPass.cpp index 3ffcbef..30e7879 100644 --- a/src/PrepareLightsPass.cpp +++ b/src/PrepareLightsPass.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include @@ -339,14 +339,14 @@ static int isInfiniteLight(const donut::engine::Light& light) } } -void PrepareLightsPass::Process( +RTXDI_LightBufferParameters PrepareLightsPass::Process( nvrhi::ICommandList* commandList, - const rtxdi::Context& context, + const rtxdi::ReSTIRDIContext& context, const std::vector>& sceneLights, - bool enableImportanceSampledEnvironmentLight, - rtxdi::FrameParameters& outFrameParameters) + bool enableImportanceSampledEnvironmentLight) { - const rtxdi::ContextParameters& contextParameters = context.GetParameters(); + RTXDI_LightBufferParameters outLightBufferParams = {}; + const rtxdi::ReSTIRDIStaticParameters& contextParameters = context.getStaticParameters(); commandList->beginMarker("PrepareLights"); @@ -402,8 +402,8 @@ void PrepareLightsPass::Process( commandList->writeBuffer(m_GeometryInstanceToLightBuffer, geometryInstanceToLight.data(), geometryInstanceToLight.size() * sizeof(uint32_t)); - outFrameParameters.firstLocalLight = 0; - outFrameParameters.numLocalLights = lightBufferOffset; + outLightBufferParams.localLightBufferRegion.firstLightIndex = 0; + outLightBufferParams.localLightBufferRegion.numLights = lightBufferOffset; auto sortedLights = sceneLights; std::sort(sortedLights.begin(), sortedLights.end(), [](const auto& a, const auto& b) @@ -447,11 +447,11 @@ void PrepareLightsPass::Process( assert(numImportanceSampledEnvironmentLights <= 1); - outFrameParameters.numLocalLights += numFinitePrimLights; - outFrameParameters.firstInfiniteLight = outFrameParameters.numLocalLights; - outFrameParameters.numInfiniteLights = numInfinitePrimLights; - outFrameParameters.environmentLightIndex = outFrameParameters.firstInfiniteLight + outFrameParameters.numInfiniteLights; - outFrameParameters.environmentLightPresent = numImportanceSampledEnvironmentLights; + outLightBufferParams.localLightBufferRegion.numLights += numFinitePrimLights; + outLightBufferParams.infiniteLightBufferRegion.firstLightIndex = outLightBufferParams.localLightBufferRegion.numLights; + outLightBufferParams.infiniteLightBufferRegion.numLights = numInfinitePrimLights; + outLightBufferParams.environmentLightParams.lightIndex = outLightBufferParams.infiniteLightBufferRegion.firstLightIndex + outLightBufferParams.infiniteLightBufferRegion.numLights; + outLightBufferParams.environmentLightParams.lightPresent = numImportanceSampledEnvironmentLights; commandList->writeBuffer(m_TaskBuffer, tasks.data(), tasks.size() * sizeof(PrepareLightsTask)); @@ -483,9 +483,10 @@ void PrepareLightsPass::Process( commandList->endMarker(); - outFrameParameters.firstLocalLight += constants.currentFrameLightOffset; - outFrameParameters.firstInfiniteLight += constants.currentFrameLightOffset; - outFrameParameters.environmentLightIndex += constants.currentFrameLightOffset; + outLightBufferParams.localLightBufferRegion.firstLightIndex += constants.currentFrameLightOffset; + outLightBufferParams.infiniteLightBufferRegion.firstLightIndex += constants.currentFrameLightOffset; + outLightBufferParams.environmentLightParams.lightIndex += constants.currentFrameLightOffset; m_OddFrame = !m_OddFrame; + return outLightBufferParams; } diff --git a/src/PrepareLightsPass.h b/src/PrepareLightsPass.h index 7137656..e021be9 100644 --- a/src/PrepareLightsPass.h +++ b/src/PrepareLightsPass.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include @@ -66,10 +66,9 @@ class PrepareLightsPass void CreateBindingSet(RtxdiResources& resources); void CountLightsInScene(uint32_t& numEmissiveMeshes, uint32_t& numEmissiveTriangles); - void Process( + RTXDI_LightBufferParameters Process( nvrhi::ICommandList* commandList, - const rtxdi::Context& context, + const rtxdi::ReSTIRDIContext& context, const std::vector>& sceneLights, - bool enableImportanceSampledEnvironmentLight, - rtxdi::FrameParameters& outFrameParameters); + bool enableImportanceSampledEnvironmentLight); }; diff --git a/src/Profiler.cpp b/src/Profiler.cpp index 181be27..af8d700 100644 --- a/src/Profiler.cpp +++ b/src/Profiler.cpp @@ -29,8 +29,6 @@ static const char* g_SectionNames[ProfilerSection::Count] = { "Temporal Resampling", "Spatial Resampling", "Shade Primary Surf.", - "RTXGI Probe Tracing", - "RTXGI Probe Updates", "BRDF or MIS Rays", "Shade Secondary Surf.", "GI - Temporal Resampling", @@ -248,7 +246,6 @@ void Profiler::BuildUI(const bool enableRayCounts) for (uint32_t section = 0; section < ProfilerSection::MaterialReadback; section++) { if (section == ProfilerSection::InitialSamples || - section == ProfilerSection::RtxgiProbeTracing || section == ProfilerSection::Gradients || section == ProfilerSection::Frame) ImGui::Separator(); diff --git a/src/Profiler.h b/src/Profiler.h index 4fe32d1..cd6ce68 100644 --- a/src/Profiler.h +++ b/src/Profiler.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/ProfilerSections.h b/src/ProfilerSections.h index bf83a1c..3fdb4b4 100644 --- a/src/ProfilerSections.h +++ b/src/ProfilerSections.h @@ -26,8 +26,6 @@ struct ProfilerSection TemporalResampling, SpatialResampling, Shading, - RtxgiProbeTracing, - RtxgiProbeUpdates, BrdfRays, ShadeSecondary, GITemporalResampling, diff --git a/src/RayTracingPass.cpp b/src/RayTracingPass.cpp index ca073fc..5782d03 100644 --- a/src/RayTracingPass.cpp +++ b/src/RayTracingPass.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/RayTracingPass.h b/src/RayTracingPass.h index b34adfc..719aa87 100644 --- a/src/RayTracingPass.h +++ b/src/RayTracingPass.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/RenderEnvironmentMapPass.cpp b/src/RenderEnvironmentMapPass.cpp index bd1124c..5d5a82f 100644 --- a/src/RenderEnvironmentMapPass.cpp +++ b/src/RenderEnvironmentMapPass.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/RenderEnvironmentMapPass.h b/src/RenderEnvironmentMapPass.h index 80ecd95..f423e77 100644 --- a/src/RenderEnvironmentMapPass.h +++ b/src/RenderEnvironmentMapPass.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/RenderTargets.cpp b/src/RenderTargets.cpp index c0abff5..6d4df8a 100644 --- a/src/RenderTargets.cpp +++ b/src/RenderTargets.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/RenderTargets.h b/src/RenderTargets.h index dd95589..35a7161 100644 --- a/src/RenderTargets.h +++ b/src/RenderTargets.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2020-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/RtxdiResources.cpp b/src/RtxdiResources.cpp index 7c02d10..027c066 100644 --- a/src/RtxdiResources.cpp +++ b/src/RtxdiResources.cpp @@ -9,7 +9,9 @@ **************************************************************************/ #include "RtxdiResources.h" -#include +#include +#include +#include #include @@ -18,7 +20,8 @@ using namespace dm; RtxdiResources::RtxdiResources( nvrhi::IDevice* device, - const rtxdi::Context& context, + const rtxdi::ReSTIRDIContext& context, + const rtxdi::RISBufferSegmentAllocator& risBufferSegmentAllocator, uint32_t maxEmissiveMeshes, uint32_t maxEmissiveTriangles, uint32_t maxPrimitiveLights, @@ -50,7 +53,7 @@ RtxdiResources::RtxdiResources( nvrhi::BufferDesc risBufferDesc; - risBufferDesc.byteSize = sizeof(uint32_t) * 2 * std::max(context.GetRisBufferElementCount(), 1u); // RG32_UINT per element + risBufferDesc.byteSize = sizeof(uint32_t) * 2 * std::max(risBufferSegmentAllocator.getTotalSizeInElements(), 1u); // RG32_UINT per element risBufferDesc.format = nvrhi::Format::RG32_UINT; risBufferDesc.canHaveTypedViews = true; risBufferDesc.initialState = nvrhi::ResourceStates::ShaderResource; @@ -60,7 +63,7 @@ RtxdiResources::RtxdiResources( RisBuffer = device->createBuffer(risBufferDesc); - risBufferDesc.byteSize = sizeof(uint32_t) * 8 * std::max(context.GetRisBufferElementCount(), 1u); // RGBA32_UINT x 2 per element + risBufferDesc.byteSize = sizeof(uint32_t) * 8 * std::max(risBufferSegmentAllocator.getTotalSizeInElements(), 1u); // RGBA32_UINT x 2 per element risBufferDesc.format = nvrhi::Format::RGBA32_UINT; risBufferDesc.debugName = "RisLightDataBuffer"; RisLightDataBuffer = device->createBuffer(risBufferDesc); @@ -100,7 +103,7 @@ RtxdiResources::RtxdiResources( nvrhi::BufferDesc neighborOffsetBufferDesc; - neighborOffsetBufferDesc.byteSize = context.GetParameters().NeighborOffsetCount * 2; + neighborOffsetBufferDesc.byteSize = context.getStaticParameters().NeighborOffsetCount * 2; neighborOffsetBufferDesc.format = nvrhi::Format::RG8_SNORM; neighborOffsetBufferDesc.canHaveTypedViews = true; neighborOffsetBufferDesc.debugName = "NeighborOffsets"; @@ -110,8 +113,8 @@ RtxdiResources::RtxdiResources( nvrhi::BufferDesc lightReservoirBufferDesc; - lightReservoirBufferDesc.byteSize = sizeof(RTXDI_PackedReservoir) * context.GetReservoirBufferElementCount() * c_NumReservoirBuffers; - lightReservoirBufferDesc.structStride = sizeof(RTXDI_PackedReservoir); + lightReservoirBufferDesc.byteSize = sizeof(RTXDI_PackedDIReservoir) * context.getReservoirBufferParameters().reservoirArrayPitch * rtxdi::c_NumReSTIRDIReservoirBuffers; + lightReservoirBufferDesc.structStride = sizeof(RTXDI_PackedDIReservoir); lightReservoirBufferDesc.initialState = nvrhi::ResourceStates::UnorderedAccess; lightReservoirBufferDesc.keepInitialState = true; lightReservoirBufferDesc.debugName = "LightReservoirBuffer"; @@ -120,7 +123,7 @@ RtxdiResources::RtxdiResources( nvrhi::BufferDesc secondaryGBufferDesc; - secondaryGBufferDesc.byteSize = sizeof(SecondaryGBufferData) * context.GetReservoirBufferElementCount(); + secondaryGBufferDesc.byteSize = sizeof(SecondaryGBufferData) * context.getReservoirBufferParameters().reservoirArrayPitch; secondaryGBufferDesc.structStride = sizeof(SecondaryGBufferData); secondaryGBufferDesc.initialState = nvrhi::ResourceStates::UnorderedAccess; secondaryGBufferDesc.keepInitialState = true; @@ -151,7 +154,7 @@ RtxdiResources::RtxdiResources( LocalLightPdfTexture = device->createTexture(localLightPdfDesc); nvrhi::BufferDesc giReservoirBufferDesc; - giReservoirBufferDesc.byteSize = sizeof(RTXDI_PackedGIReservoir) * context.GetReservoirBufferElementCount() * c_NumGIReservoirBuffers; + giReservoirBufferDesc.byteSize = sizeof(RTXDI_PackedGIReservoir) * context.getReservoirBufferParameters().reservoirArrayPitch * rtxdi::c_NumReSTIRGIReservoirBuffers; giReservoirBufferDesc.structStride = sizeof(RTXDI_PackedGIReservoir); giReservoirBufferDesc.initialState = nvrhi::ResourceStates::UnorderedAccess; giReservoirBufferDesc.keepInitialState = true; @@ -160,15 +163,15 @@ RtxdiResources::RtxdiResources( GIReservoirBuffer = device->createBuffer(giReservoirBufferDesc); } -void RtxdiResources::InitializeNeighborOffsets(nvrhi::ICommandList* commandList, const rtxdi::Context& context) +void RtxdiResources::InitializeNeighborOffsets(nvrhi::ICommandList* commandList, uint32_t neighborOffsetCount) { if (m_NeighborOffsetsInitialized) return; std::vector offsets; - offsets.resize(context.GetParameters().NeighborOffsetCount* 2); + offsets.resize(neighborOffsetCount * 2); - context.FillNeighborOffsetBuffer(offsets.data()); + rtxdi::FillNeighborOffsetBuffer(offsets.data(), neighborOffsetCount); commandList->writeBuffer(NeighborOffsetsBuffer, offsets.data(), offsets.size()); diff --git a/src/RtxdiResources.h b/src/RtxdiResources.h index e7ed710..2feaa79 100644 --- a/src/RtxdiResources.h +++ b/src/RtxdiResources.h @@ -14,7 +14,9 @@ namespace rtxdi { - class Context; + class RISBufferSegmentAllocator; + class ReSTIRDIContext; + class ImportanceSamplingContext; } class RtxdiResources @@ -43,7 +45,8 @@ class RtxdiResources RtxdiResources( nvrhi::IDevice* device, - const rtxdi::Context& context, + const rtxdi::ReSTIRDIContext& context, + const rtxdi::RISBufferSegmentAllocator& risBufferSegmentAllocator, uint32_t maxEmissiveMeshes, uint32_t maxEmissiveTriangles, uint32_t maxPrimitiveLights, @@ -51,13 +54,10 @@ class RtxdiResources uint32_t environmentMapWidth, uint32_t environmentMapHeight); - void InitializeNeighborOffsets(nvrhi::ICommandList* commandList, const rtxdi::Context& context); + void InitializeNeighborOffsets(nvrhi::ICommandList* commandList, uint32_t neighborOffsetCount); uint32_t GetMaxEmissiveMeshes() const { return m_MaxEmissiveMeshes; } uint32_t GetMaxEmissiveTriangles() const { return m_MaxEmissiveTriangles; } uint32_t GetMaxPrimitiveLights() const { return m_MaxPrimitiveLights; } uint32_t GetMaxGeometryInstances() const { return m_MaxGeometryInstances; } - - static constexpr uint32_t c_NumReservoirBuffers = 3; - static constexpr uint32_t c_NumGIReservoirBuffers = 2; }; diff --git a/src/Rtxgi-DX12.cpp b/src/Rtxgi-DX12.cpp deleted file mode 100644 index 311ed9c..0000000 --- a/src/Rtxgi-DX12.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. - # - # NVIDIA CORPORATION and its licensors retain all intellectual property - # and proprietary rights in and to this software, related documentation - # and any modifications thereto. Any use, reproduction, disclosure or - # distribution of this software and related documentation without an express - # license agreement from NVIDIA CORPORATION is strictly prohibited. - **************************************************************************/ - -#if defined(WITH_RTXGI) && defined(USE_DX12) - -#include "RtxgiIntegration.h" -#include - -#include -#include -#include - -#include -#include - -#include "Profiler.h" - -#include - -using namespace donut::math; -#include "../shaders/ShaderParameters.h" - -using namespace donut; - -class RtxgiVolumeDX12 final : public RtxgiVolume // NOLINT(cppcoreguidelines-special-member-functions) -{ - std::unique_ptr m_DdgiVolume; - nvrhi::d3d12::DescriptorIndex m_FirstSrvDescriptor = 0; - uint32_t m_NumSrvDescriptors = 0; - -public: - RtxgiVolumeDX12( - nvrhi::IDevice* device, - const std::weak_ptr& parent, - const std::shared_ptr& fs, - const std::shared_ptr& descriptorTable, - const rtxgi::DDGIVolumeDesc& volumeDesc) - : RtxgiVolume(device, parent) - { - rtxgi::d3d12::DDGIVolumeResources volumeResources; - - std::vector> blobs; - - std::vector defines; - defines.push_back({ "RTXGI_DDGI_USE_SHADER_CONFIG_FILE", "1" }); - defines.push_back({ "HLSL", "1" }); - defines.push_back({ "RTXGI_DDGI_BLEND_RADIANCE", "1" }); - defines.push_back({ "RTXGI_DDGI_BLEND_SCROLL_SHARED_MEMORY", "1" }); - LoadShader(volumeResources.managed.probeBlendingIrradianceCS, fs, "/shaders/app/RTXGI/ProbeBlendingCS_DDGIProbeBlendingCS.bin", defines, blobs); - defines.pop_back(); - LoadShader(volumeResources.managed.probeBorderRowUpdateIrradianceCS, fs, "/shaders/app/RTXGI/ProbeBorderUpdateCS_DDGIProbeBorderRowUpdateCS.bin", defines, blobs); - LoadShader(volumeResources.managed.probeBorderColumnUpdateIrradianceCS, fs, "/shaders/app/RTXGI/ProbeBorderUpdateCS_DDGIProbeBorderColumnUpdateCS.bin", defines, blobs); - defines[2].value = "0"; // RTXGI_DDGI_BLEND_RADIANCE - defines.push_back({ "RTXGI_DDGI_BLEND_SCROLL_SHARED_MEMORY", "1" }); - LoadShader(volumeResources.managed.probeBlendingDistanceCS, fs, "/shaders/app/RTXGI/ProbeBlendingCS_DDGIProbeBlendingCS.bin", defines, blobs); - defines.pop_back(); - LoadShader(volumeResources.managed.probeBorderRowUpdateDistanceCS, fs, "/shaders/app/RTXGI/ProbeBorderUpdateCS_DDGIProbeBorderRowUpdateCS.bin", defines, blobs); - LoadShader(volumeResources.managed.probeBorderColumnUpdateDistanceCS, fs, "/shaders/app/RTXGI/ProbeBorderUpdateCS_DDGIProbeBorderColumnUpdateCS.bin", defines, blobs); - defines.pop_back(); // RTXGI_DDGI_BLEND_RADIANCE - LoadShader(volumeResources.managed.probeClassification.updateCS, fs, "/shaders/app/RTXGI/ProbeClassificationCS_DDGIProbeClassificationCS.bin", defines, blobs); - LoadShader(volumeResources.managed.probeClassification.resetCS, fs, "/shaders/app/RTXGI/ProbeClassificationCS_DDGIProbeClassificationResetCS.bin", defines, blobs); - LoadShader(volumeResources.managed.probeRelocation.updateCS, fs, "/shaders/app/RTXGI/ProbeRelocationCS_DDGIProbeRelocationCS.bin", defines, blobs); - LoadShader(volumeResources.managed.probeRelocation.resetCS, fs, "/shaders/app/RTXGI/ProbeRelocationCS_DDGIProbeRelocationResetCS.bin", defines, blobs); - - volumeResources.managed.enabled = true; - volumeResources.managed.device = device->getNativeObject(nvrhi::ObjectTypes::D3D12_Device); - - nvrhi::d3d12::IDevice* nvrhiDevice = device->getNativeObject(nvrhi::ObjectTypes::Nvrhi_D3D12_Device); - assert(nvrhiDevice); - nvrhi::d3d12::IDescriptorHeap* descriptorHeap = nvrhiDevice->getDescriptorHeap(nvrhi::d3d12::DescriptorHeapType::ShaderResrouceView); - assert(descriptorHeap); - - m_NumSrvDescriptors = 1 // CBV - + rtxgi::GetDDGIVolumeNumSRVDescriptors() - + rtxgi::GetDDGIVolumeNumUAVDescriptors(); - m_FirstSrvDescriptor = descriptorHeap->allocateDescriptors(m_NumSrvDescriptors); - - volumeResources.descriptorHeapDesc.heap = descriptorHeap->getShaderVisibleHeap(); - volumeResources.descriptorHeapDesc.constsOffset = m_FirstSrvDescriptor; - volumeResources.descriptorHeapDesc.uavOffset = volumeResources.descriptorHeapDesc.constsOffset + 1; - volumeResources.descriptorHeapDesc.srvOffset = volumeResources.descriptorHeapDesc.uavOffset + rtxgi::GetDDGIVolumeNumUAVDescriptors(); - - auto parentShared = m_Parent.lock(); - assert(parentShared); - - volumeResources.constantsBuffer = parentShared->GetConstantBuffer()->getNativeObject(nvrhi::ObjectTypes::D3D12_Resource); - - m_DdgiVolume = std::make_unique(); - rtxgi::ERTXGIStatus status = m_DdgiVolume->Create(volumeDesc, volumeResources); - - if (status != rtxgi::OK) - { - m_DdgiVolume = nullptr; - return; - } - - - nvrhi::TextureDesc probeTextureDesc; - ID3D12Resource* d3dProbeTexture = m_DdgiVolume->GetProbeRayData(); - D3D12_RESOURCE_DESC d3dProbeTextureDesc = d3dProbeTexture->GetDesc(); - assert(d3dProbeTextureDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D); - probeTextureDesc.debugName = "ProbeRayData"; - probeTextureDesc.format = volumeDesc.probeRayDataFormat == 0 ? nvrhi::Format::RG32_FLOAT : nvrhi::Format::RGBA32_FLOAT; - probeTextureDesc.width = (uint32_t)d3dProbeTextureDesc.Width; - probeTextureDesc.height = d3dProbeTextureDesc.Height; - probeTextureDesc.initialState = nvrhi::ResourceStates::UnorderedAccess; - probeTextureDesc.keepInitialState = true; - probeTextureDesc.isUAV = true; - - m_ProbeRayData = device->createHandleForNativeTexture(nvrhi::ObjectTypes::D3D12_Resource, d3dProbeTexture, probeTextureDesc); - - d3dProbeTexture = m_DdgiVolume->GetProbeIrradiance(); - d3dProbeTextureDesc = d3dProbeTexture->GetDesc(); - assert(d3dProbeTextureDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D); - probeTextureDesc.debugName = "ProbeIrradiance"; - probeTextureDesc.format = volumeDesc.probeIrradianceFormat == 0 ? nvrhi::Format::R10G10B10A2_UNORM : volumeDesc.probeIrradianceFormat == 1 ? nvrhi::Format::RGBA16_FLOAT : nvrhi::Format::RGBA32_FLOAT; - probeTextureDesc.width = (uint32_t)d3dProbeTextureDesc.Width; - probeTextureDesc.height = d3dProbeTextureDesc.Height; - probeTextureDesc.initialState = nvrhi::ResourceStates::ShaderResource; - probeTextureDesc.keepInitialState = true; - probeTextureDesc.isUAV = true; - - m_ProbeIrradiance = device->createHandleForNativeTexture(nvrhi::ObjectTypes::D3D12_Resource, d3dProbeTexture, probeTextureDesc); - - d3dProbeTexture = m_DdgiVolume->GetProbeDistance(); - d3dProbeTextureDesc = d3dProbeTexture->GetDesc(); - assert(d3dProbeTextureDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D); - probeTextureDesc.debugName = "ProbeDistance"; - probeTextureDesc.format = volumeDesc.probeDistanceFormat == 0 ? nvrhi::Format::RG16_FLOAT: nvrhi::Format::RG32_FLOAT; - probeTextureDesc.width = (uint32_t)d3dProbeTextureDesc.Width; - probeTextureDesc.height = d3dProbeTextureDesc.Height; - probeTextureDesc.initialState = nvrhi::ResourceStates::ShaderResource; - probeTextureDesc.keepInitialState = true; - probeTextureDesc.isUAV = true; - - m_ProbeDistance = device->createHandleForNativeTexture(nvrhi::ObjectTypes::D3D12_Resource, d3dProbeTexture, probeTextureDesc); - - d3dProbeTexture = m_DdgiVolume->GetProbeData(); - d3dProbeTextureDesc = d3dProbeTexture->GetDesc(); - assert(d3dProbeTextureDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D); - probeTextureDesc.debugName = "ProbeData"; - probeTextureDesc.format = volumeDesc.probeDataFormat == 0 ? nvrhi::Format::RGBA16_FLOAT : nvrhi::Format::RGBA32_FLOAT; - probeTextureDesc.width = (uint32_t)d3dProbeTextureDesc.Width; - probeTextureDesc.height = d3dProbeTextureDesc.Height; - probeTextureDesc.initialState = nvrhi::ResourceStates::UnorderedAccess; - probeTextureDesc.keepInitialState = true; - probeTextureDesc.isUAV = true; - - m_ProbeData = device->createHandleForNativeTexture(nvrhi::ObjectTypes::D3D12_Resource, d3dProbeTexture, probeTextureDesc); - - - m_IrradianceTextureSRV = descriptorTable->CreateDescriptorHandle(nvrhi::BindingSetItem::Texture_SRV(0, m_ProbeIrradiance)); - m_DistanceTextureSRV = descriptorTable->CreateDescriptorHandle(nvrhi::BindingSetItem::Texture_SRV(0, m_ProbeDistance)); - m_ProbeDataTextureSRV = descriptorTable->CreateDescriptorHandle(nvrhi::BindingSetItem::Texture_SRV(0, m_ProbeData)); - m_RayDataTextureUAV = descriptorTable->CreateDescriptorHandle(nvrhi::BindingSetItem::Texture_UAV(0, m_ProbeRayData)); - } - - ~RtxgiVolumeDX12() override - { - if (m_NumSrvDescriptors > 0) - { - nvrhi::d3d12::IDevice* nvrhiDevice = m_Device->getNativeObject(nvrhi::ObjectTypes::Nvrhi_D3D12_Device); - nvrhi::d3d12::IDescriptorHeap* descriptorHeap = nvrhiDevice->getDescriptorHeap(nvrhi::d3d12::DescriptorHeapType::ShaderResrouceView); - - descriptorHeap->releaseDescriptors(m_FirstSrvDescriptor, m_NumSrvDescriptors); - - m_FirstSrvDescriptor = -1; - m_NumSrvDescriptors = 0; - } - - if (m_DdgiVolume) - { - m_DdgiVolume->Destroy(); - m_DdgiVolume = nullptr; - } - } - - [[nodiscard]] bool IsInitialized() const override { return m_DdgiVolume != nullptr; } - [[nodiscard]] rtxgi::DDGIVolumeBase* GetVolume() override { return m_DdgiVolume.get(); } -}; - -std::shared_ptr RtxgiVolume::CreateDX12( - nvrhi::IDevice* device, - const std::weak_ptr& parent, - const std::shared_ptr& fs, - const std::shared_ptr& descriptorTable, - const rtxgi::DDGIVolumeDesc& volumeDesc) -{ - return std::make_shared(device, parent, fs, descriptorTable, volumeDesc); -} - -void RtxgiVolume::UpdateVolumesDX12(nvrhi::ICommandList* commandList, const std::vector>& volumes) -{ - ID3D12GraphicsCommandList* d3dCommandList = commandList->getNativeObject(nvrhi::ObjectTypes::D3D12_GraphicsCommandList); - - std::vector ddgiVolumes; - - for (const auto& volume : volumes) - { - if (!volume->IsInitialized()) - continue; - - RtxgiVolumeDX12* volumeDx12 = nvrhi::checked_cast(volume.get()); - ddgiVolumes.push_back(static_cast(volumeDx12->GetVolume())); - - commandList->setTextureState(volumeDx12->m_ProbeIrradiance, nvrhi::AllSubresources, nvrhi::ResourceStates::ShaderResource); - commandList->setTextureState(volumeDx12->m_ProbeDistance, nvrhi::AllSubresources, nvrhi::ResourceStates::ShaderResource); - commandList->setTextureState(volumeDx12->m_ProbeData, nvrhi::AllSubresources, nvrhi::ResourceStates::UnorderedAccess); - commandList->setTextureState(volumeDx12->m_ProbeRayData, nvrhi::AllSubresources, nvrhi::ResourceStates::ShaderResource); - } - commandList->commitBarriers(); - - rtxgi::d3d12::UpdateDDGIVolumeProbes(d3dCommandList, uint32_t(ddgiVolumes.size()), ddgiVolumes.data()); - - rtxgi::d3d12::RelocateDDGIVolumeProbes(d3dCommandList, uint32_t(ddgiVolumes.size()), ddgiVolumes.data()); - - rtxgi::d3d12::ClassifyDDGIVolumeProbes(d3dCommandList, uint32_t(ddgiVolumes.size()), ddgiVolumes.data()); - - commandList->clearState(); -} - -#endif // defined(WITH_RTXGI) && defined(USE_DX12) \ No newline at end of file diff --git a/src/Rtxgi-VK.cpp b/src/Rtxgi-VK.cpp deleted file mode 100644 index ef18e46..0000000 --- a/src/Rtxgi-VK.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. - # - # NVIDIA CORPORATION and its licensors retain all intellectual property - # and proprietary rights in and to this software, related documentation - # and any modifications thereto. Any use, reproduction, disclosure or - # distribution of this software and related documentation without an express - # license agreement from NVIDIA CORPORATION is strictly prohibited. - **************************************************************************/ - -#if defined(WITH_RTXGI) && defined(USE_VK) - -#include "RtxgiIntegration.h" -#include - -#include -#include -#include - -#include -#include - -#include "VulkanExtensions.h" -#include "Profiler.h" - -#include - -using namespace donut::math; -#include "../shaders/ShaderParameters.h" - -using namespace donut; - -class RtxgiVolumeVK final : public RtxgiVolume // NOLINT(cppcoreguidelines-special-member-functions) -{ - std::unique_ptr m_DdgiVolume; - - VkDescriptorPool m_descriptorPool = nullptr; - -public: - RtxgiVolumeVK( - nvrhi::IDevice* device, - const std::weak_ptr& parent, - const std::shared_ptr& fs, - const std::shared_ptr& descriptorTable, - const rtxgi::DDGIVolumeDesc& volumeDesc) - : RtxgiVolume(device, parent) - { - VkInstance vkInstance = device->getNativeObject(nvrhi::ObjectTypes::VK_Instance); - VkDevice vkDevice = device->getNativeObject(nvrhi::ObjectTypes::VK_Device); - VkPhysicalDevice vkPhysicalDevice = device->getNativeObject(nvrhi::ObjectTypes::VK_PhysicalDevice); - - LoadInstanceExtensions(vkInstance); - LoadDeviceExtensions(vkDevice); - - rtxgi::vulkan::DDGIVolumeResources volumeResources; - - std::vector> blobs; - - std::vector defines; - defines.push_back({ "RTXGI_DDGI_USE_SHADER_CONFIG_FILE", "1" }); - defines.push_back({ "HLSL", "1" }); - defines.push_back({ "RTXGI_DDGI_BLEND_RADIANCE", "1" }); - defines.push_back({ "RTXGI_DDGI_BLEND_SCROLL_SHARED_MEMORY", "1" }); - LoadShader(volumeResources.managed.probeBlendingIrradianceCS, fs, "/shaders/app/RTXGI/ProbeBlendingCS_DDGIProbeBlendingCS.bin", defines, blobs); - defines.pop_back(); - LoadShader(volumeResources.managed.probeBorderRowUpdateIrradianceCS, fs, "/shaders/app/RTXGI/ProbeBorderUpdateCS_DDGIProbeBorderRowUpdateCS.bin", defines, blobs); - LoadShader(volumeResources.managed.probeBorderColumnUpdateIrradianceCS, fs, "/shaders/app/RTXGI/ProbeBorderUpdateCS_DDGIProbeBorderColumnUpdateCS.bin", defines, blobs); - defines[2].value = "0"; // RTXGI_DDGI_BLEND_RADIANCE - defines.push_back({ "RTXGI_DDGI_BLEND_SCROLL_SHARED_MEMORY", "1" }); - LoadShader(volumeResources.managed.probeBlendingDistanceCS, fs, "/shaders/app/RTXGI/ProbeBlendingCS_DDGIProbeBlendingCS.bin", defines, blobs); - defines.pop_back(); - LoadShader(volumeResources.managed.probeBorderRowUpdateDistanceCS, fs, "/shaders/app/RTXGI/ProbeBorderUpdateCS_DDGIProbeBorderRowUpdateCS.bin", defines, blobs); - LoadShader(volumeResources.managed.probeBorderColumnUpdateDistanceCS, fs, "/shaders/app/RTXGI/ProbeBorderUpdateCS_DDGIProbeBorderColumnUpdateCS.bin", defines, blobs); - defines.pop_back(); // RTXGI_DDGI_BLEND_RADIANCE - LoadShader(volumeResources.managed.probeClassification.updateCS, fs, "/shaders/app/RTXGI/ProbeClassificationCS_DDGIProbeClassificationCS.bin", defines, blobs); - LoadShader(volumeResources.managed.probeClassification.resetCS, fs, "/shaders/app/RTXGI/ProbeClassificationCS_DDGIProbeClassificationResetCS.bin", defines, blobs); - LoadShader(volumeResources.managed.probeRelocation.updateCS, fs, "/shaders/app/RTXGI/ProbeRelocationCS_DDGIProbeRelocationCS.bin", defines, blobs); - LoadShader(volumeResources.managed.probeRelocation.resetCS, fs, "/shaders/app/RTXGI/ProbeRelocationCS_DDGIProbeRelocationResetCS.bin", defines, blobs); - - volumeResources.managed.enabled = true; - volumeResources.managed.device = vkDevice; - volumeResources.managed.physicalDevice = vkPhysicalDevice; - - VkDescriptorPoolSize poolSize = {}; - poolSize.descriptorCount = 5; - - VkDescriptorPoolCreateInfo poolCreateInfo = {}; - poolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolCreateInfo.maxSets = 1; - poolCreateInfo.pPoolSizes = &poolSize; - poolCreateInfo.poolSizeCount = 1; - vkCreateDescriptorPool(volumeResources.managed.device, &poolCreateInfo, nullptr, &m_descriptorPool); - - auto parentShared = m_Parent.lock(); - assert(parentShared); - - volumeResources.managed.descriptorPool = m_descriptorPool; - volumeResources.constantsBuffer = parentShared->GetConstantBuffer()->getNativeObject(nvrhi::ObjectTypes::VK_Buffer); - - nvrhi::CommandListHandle commandList = device->createCommandList(); - commandList->open(); - - VkCommandBuffer vkCmdBuffer = commandList->getNativeObject(nvrhi::ObjectTypes::VK_CommandBuffer); - - m_DdgiVolume = std::make_unique(); - rtxgi::ERTXGIStatus status = m_DdgiVolume->Create(vkCmdBuffer, volumeDesc, volumeResources); - - commandList->close(); - device->executeCommandList(commandList); - - if (status != rtxgi::OK) - { - m_DdgiVolume = nullptr; - return; - } - - - - nvrhi::TextureDesc probeTextureDesc; - VkImage vkProbeTexture = m_DdgiVolume->GetProbeRayData(); - probeTextureDesc.debugName = "ProbeRayData"; - probeTextureDesc.format = volumeDesc.probeRayDataFormat == 0 ? nvrhi::Format::RG32_FLOAT : nvrhi::Format::RGBA32_FLOAT; - probeTextureDesc.width = 1; // we don't know this here, but it shouldn't matter - probeTextureDesc.height = 1; - probeTextureDesc.initialState = nvrhi::ResourceStates::UnorderedAccess; - probeTextureDesc.keepInitialState = true; - probeTextureDesc.isUAV = true; - - m_ProbeRayData = device->createHandleForNativeTexture(nvrhi::ObjectTypes::VK_Image, vkProbeTexture, probeTextureDesc); - - vkProbeTexture = m_DdgiVolume->GetProbeIrradiance(); - probeTextureDesc.debugName = "ProbeIrradiance"; - probeTextureDesc.format = volumeDesc.probeIrradianceFormat == 0 ? nvrhi::Format::R10G10B10A2_UNORM : volumeDesc.probeIrradianceFormat == 1 ? nvrhi::Format::RGBA16_FLOAT : nvrhi::Format::RGBA32_FLOAT; - probeTextureDesc.initialState = nvrhi::ResourceStates::UnorderedAccess; - probeTextureDesc.keepInitialState = true; - probeTextureDesc.isUAV = true; - - m_ProbeIrradiance = device->createHandleForNativeTexture(nvrhi::ObjectTypes::VK_Image, vkProbeTexture, probeTextureDesc); - - vkProbeTexture = m_DdgiVolume->GetProbeDistance(); - probeTextureDesc.debugName = "ProbeDistance"; - probeTextureDesc.format = volumeDesc.probeDistanceFormat == 0 ? nvrhi::Format::RG16_FLOAT: nvrhi::Format::RG32_FLOAT; - probeTextureDesc.initialState = nvrhi::ResourceStates::UnorderedAccess; - probeTextureDesc.keepInitialState = true; - probeTextureDesc.isUAV = true; - - m_ProbeDistance = device->createHandleForNativeTexture(nvrhi::ObjectTypes::VK_Image, vkProbeTexture, probeTextureDesc); - - vkProbeTexture = m_DdgiVolume->GetProbeData(); - probeTextureDesc.debugName = "ProbeData"; - probeTextureDesc.format = volumeDesc.probeDataFormat == 0 ? nvrhi::Format::RGBA16_FLOAT : nvrhi::Format::RGBA32_FLOAT; - probeTextureDesc.initialState = nvrhi::ResourceStates::UnorderedAccess; - probeTextureDesc.keepInitialState = true; - probeTextureDesc.isUAV = true; - - m_ProbeData = device->createHandleForNativeTexture(nvrhi::ObjectTypes::VK_Image, vkProbeTexture, probeTextureDesc); - - - m_IrradianceTextureSRV = descriptorTable->CreateDescriptorHandle(nvrhi::BindingSetItem::Texture_SRV(0, m_ProbeIrradiance)); - m_DistanceTextureSRV = descriptorTable->CreateDescriptorHandle(nvrhi::BindingSetItem::Texture_SRV(0, m_ProbeDistance)); - m_ProbeDataTextureSRV = descriptorTable->CreateDescriptorHandle(nvrhi::BindingSetItem::Texture_SRV(0, m_ProbeData)); - m_RayDataTextureUAV = descriptorTable->CreateDescriptorHandle(nvrhi::BindingSetItem::Texture_UAV(0, m_ProbeRayData)); - } - - ~RtxgiVolumeVK() override - { - if (m_descriptorPool) - { - VkDevice device = m_Device->getNativeObject(nvrhi::ObjectTypes::VK_Device); - vkDestroyDescriptorPool(device, m_descriptorPool, nullptr); - m_descriptorPool = nullptr; - } - - if (m_DdgiVolume) - { - m_DdgiVolume->Destroy(); - m_DdgiVolume = nullptr; - } - } - - [[nodiscard]] bool IsInitialized() const override { return m_DdgiVolume != nullptr; } - [[nodiscard]] rtxgi::DDGIVolumeBase* GetVolume() override { return m_DdgiVolume.get(); } -}; - -std::shared_ptr RtxgiVolume::CreateVK( - nvrhi::IDevice* device, - const std::weak_ptr& parent, - const std::shared_ptr& fs, - const std::shared_ptr& descriptorTable, - const rtxgi::DDGIVolumeDesc& volumeDesc) -{ - return std::make_shared(device, parent, fs, descriptorTable, volumeDesc); -} - -void RtxgiVolume::UpdateVolumesVK(nvrhi::ICommandList* commandList, const std::vector>& volumes) -{ - VkCommandBuffer vkCmdBuffer = commandList->getNativeObject(nvrhi::ObjectTypes::VK_CommandBuffer); - - std::vector ddgiVolumes; - - for (const auto& volume : volumes) - { - if (!volume->IsInitialized()) - continue; - - RtxgiVolumeVK* volumeVk = nvrhi::checked_cast(volume.get()); - ddgiVolumes.push_back(nvrhi::checked_cast(volumeVk->GetVolume())); - - commandList->setTextureState(volumeVk->m_ProbeIrradiance, nvrhi::AllSubresources, nvrhi::ResourceStates::UnorderedAccess); - commandList->setTextureState(volumeVk->m_ProbeDistance, nvrhi::AllSubresources, nvrhi::ResourceStates::UnorderedAccess); - commandList->setTextureState(volumeVk->m_ProbeData, nvrhi::AllSubresources, nvrhi::ResourceStates::UnorderedAccess); - commandList->setTextureState(volumeVk->m_ProbeRayData, nvrhi::AllSubresources, nvrhi::ResourceStates::UnorderedAccess); - } - commandList->commitBarriers(); - - rtxgi::vulkan::UpdateDDGIVolumeProbes(vkCmdBuffer, uint32_t(ddgiVolumes.size()), ddgiVolumes.data()); - - rtxgi::vulkan::RelocateDDGIVolumeProbes(vkCmdBuffer, uint32_t(ddgiVolumes.size()), ddgiVolumes.data()); - - rtxgi::vulkan::ClassifyDDGIVolumeProbes(vkCmdBuffer, uint32_t(ddgiVolumes.size()), ddgiVolumes.data()); - - commandList->clearState(); -} - -#endif // defined(WITH_RTXGI) && defined(USE_VK) \ No newline at end of file diff --git a/src/RtxgiIntegration.cpp b/src/RtxgiIntegration.cpp deleted file mode 100644 index 8c2d549..0000000 --- a/src/RtxgiIntegration.cpp +++ /dev/null @@ -1,513 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. - # - # NVIDIA CORPORATION and its licensors retain all intellectual property - # and proprietary rights in and to this software, related documentation - # and any modifications thereto. Any use, reproduction, disclosure or - # distribution of this software and related documentation without an express - # license agreement from NVIDIA CORPORATION is strictly prohibited. - **************************************************************************/ - -#if defined(WITH_RTXGI) - -#include "RtxgiIntegration.h" -#include -#include -#include - -#include -#include - -#include "../shaders/RTXGI/DDGIShaderConfig.h" -#include - -#include - -#include "Profiler.h" -#include "RenderTargets.h" -#include "UserInterface.h" -#include "SampleScene.h" -#include "donut/engine/View.h" -using namespace donut::math; -#include "../shaders/ShaderParameters.h" - -using namespace donut; - -constexpr uint32_t c_MaxVolumes = 8; - -bool RtxgiVolume::LoadShader(rtxgi::ShaderBytecode& dest, const std::shared_ptr& fs, const char* shaderName, - std::vector defines, std::vector>& blobs) -{ - auto data = fs->readFile(shaderName); - if (!data) - return false; - - if (!ShaderMake::FindPermutationInBlob(data->data(), data->size(), - defines.empty() ? nullptr : defines.data(), uint32_t(defines.size()), - &dest.pData, &dest.size)) - { - auto errorMessage = ShaderMake::FormatShaderNotFoundMessage(data->data(), data->size(), - defines.empty() ? nullptr : defines.data(), uint32_t(defines.size())); - - log::error("%s", errorMessage.c_str()); - - return false; - } - - blobs.push_back(data); - - return true; -} - -RtxgiIntegration::RtxgiIntegration( - nvrhi::IDevice* device, - const std::shared_ptr& fs, - const std::shared_ptr& descriptorTable) - : m_Device(device) - , m_FileSystem(fs) - , m_DescriptorTable(descriptorTable) -{ - // Don't insert the RTXGI internal perf markers because they break the batching of probe updates on the GPU. - rtxgi::SetInsertPerfMarkers(false); -} - -void RtxgiIntegration::InitializePasses( - engine::ShaderFactory& shaderFactory, - nvrhi::IBindingLayout* lightingLayout, - nvrhi::IBindingLayout* bindlessLayout, - const rtxdi::ContextParameters& contextParameters, - bool useRayQuery) -{ - auto samplerDesc = nvrhi::SamplerDesc() - .setAllAddressModes(nvrhi::SamplerAddressMode::Wrap) - .setAllFilters(true); - - m_ProbeSampler = m_Device->createSampler(samplerDesc); - - auto constantBufferDesc = nvrhi::BufferDesc() - .setDebugName("RtxgiConstants") - .setByteSize(sizeof(rtxgi::DDGIVolumeDescGPUPacked) * c_MaxVolumes) - .setStructStride(sizeof(rtxgi::DDGIVolumeDescGPUPacked)) - .setInitialState(nvrhi::ResourceStates::ShaderResource) - .setKeepInitialState(true); - - m_RtxgiConstantBuffer = m_Device->createBuffer(constantBufferDesc); - - auto indexBufferDesc = nvrhi::BufferDesc() - .setDebugName("RtxgiVolumeResourceIndices") - .setByteSize(sizeof(DDGIVolumeResourceIndices) * c_MaxVolumes) - .setStructStride(sizeof(DDGIVolumeResourceIndices)) - .setInitialState(nvrhi::ResourceStates::ShaderResource) - .setKeepInitialState(true); - - m_VolumeResourceIndicesBuffer = m_Device->createBuffer(indexBufferDesc); - - auto setDesc = nvrhi::BindingSetDesc() - .addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(40, m_RtxgiConstantBuffer)) - .addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(41, m_VolumeResourceIndicesBuffer)) - .addItem(nvrhi::BindingSetItem::Sampler(40, m_ProbeSampler)); - - nvrhi::utils::CreateBindingSetAndLayout(m_Device, nvrhi::ShaderType::All, 0, - setDesc, m_ProbeTracingLayout, m_ProbeTracingSet); - - m_ProbeTracingPass = std::make_unique(); - m_ProbeTracingPass->Init(m_Device, shaderFactory, "app/RTXGI/ProbeTrace.hlsl", { LightingPasses::GetRegirMacro(contextParameters) }, - useRayQuery, 16, lightingLayout, m_ProbeTracingLayout, bindlessLayout); - - auto instanceLayoutDesc = nvrhi::BindingLayoutDesc() - .setVisibility(nvrhi::ShaderType::Compute) - .addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(0)) - .addItem(nvrhi::BindingLayoutItem::StructuredBuffer_SRV(0)) - .addItem(nvrhi::BindingLayoutItem::StructuredBuffer_SRV(1)) - .addItem(nvrhi::BindingLayoutItem::StructuredBuffer_UAV(0)); - - m_DebugInstancesBindingLayout = m_Device->createBindingLayout(instanceLayoutDesc); - - auto instanceComputeShader = shaderFactory.CreateShader("app/RTXGI/ProbeInstances.hlsl", "main", nullptr, nvrhi::ShaderType::Compute); - - m_DebugInstancesPipeline = m_Device->createComputePipeline(nvrhi::ComputePipelineDesc() - .addBindingLayout(m_DebugInstancesBindingLayout) - .addBindingLayout(bindlessLayout) - .setComputeShader(instanceComputeShader)); - - auto debugLayoutDesc = nvrhi::BindingLayoutDesc() - .setVisibility(nvrhi::ShaderType::All) - .addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(0)) - .addItem(nvrhi::BindingLayoutItem::RayTracingAccelStruct(0)) - .addItem(nvrhi::BindingLayoutItem::StructuredBuffer_SRV(1)) - .addItem(nvrhi::BindingLayoutItem::StructuredBuffer_SRV(2)) - .addItem(nvrhi::BindingLayoutItem::Texture_SRV(3)) - .addItem(nvrhi::BindingLayoutItem::Texture_UAV(0)) - .addItem(nvrhi::BindingLayoutItem::Sampler(0)); - - m_DebugBindingLayout = m_Device->createBindingLayout(debugLayoutDesc); - - m_DebugPass = std::make_unique(); - m_DebugPass->Init(m_Device, shaderFactory, "app/RTXGI/ProbeDebug.hlsl", {}, true, 16, - m_DebugBindingLayout, nullptr, bindlessLayout); -} - -void RtxgiIntegration::UpdateAllVolumes( - nvrhi::ICommandList* commandList, - nvrhi::IBindingSet* ligthingBindings, - nvrhi::IDescriptorTable* descriptorTable, - Profiler& profiler) -{ - for (const auto& volume : m_Volumes) - { - volume->UpdateConstants(commandList); - } - - for (const auto& volume : m_Volumes) - { - volume->SetResourceStatesForTracing(commandList); - } - commandList->commitBarriers(); - - - commandList->beginMarker("RTXGI Probe Tracing"); - profiler.BeginSection(commandList, ProfilerSection::RtxgiProbeTracing); - - for (const auto& volume : m_Volumes) - { - volume->Trace(commandList, ligthingBindings, descriptorTable); - } - - profiler.EndSection(commandList, ProfilerSection::RtxgiProbeTracing); - commandList->endMarker(); - - - commandList->beginMarker("RTXGI Probe Updates"); - profiler.BeginSection(commandList, ProfilerSection::RtxgiProbeUpdates); -#ifdef USE_DX12 - if (m_Device->getGraphicsAPI() == nvrhi::GraphicsAPI::D3D12) - { - RtxgiVolume::UpdateVolumesDX12(commandList, m_Volumes); - } - else -#endif - { -#ifdef USE_VK - RtxgiVolume::UpdateVolumesVK(commandList, m_Volumes); -#endif - } - profiler.EndSection(commandList, ProfilerSection::RtxgiProbeUpdates); - commandList->endMarker(); -} - -void RtxgiIntegration::InvalidateRenderTargets() -{ - m_DebugBindingSet = nullptr; - m_PrevDebugBindingSet = nullptr; -} - -void RtxgiIntegration::RenderDebug( - nvrhi::ICommandList* commandList, - nvrhi::IDescriptorTable* descriptorTable, - RenderTargets& renderTargets, - int volumeIndex, - const donut::engine::IView& view) -{ - if (volumeIndex < 0 || volumeIndex >= int(m_Volumes.size())) - return; - - const std::shared_ptr& volume = m_Volumes[volumeIndex]; - assert(volume); - - uint32_t numProbes = volume->GetVolume()->GetNumProbes(); - if (numProbes == 0) - return; - - CreateDebugResources(commandList, volume.get(), renderTargets); - - uint64_t blasDeviceAddress = m_ProbeBLAS->getDeviceAddress(); - - ProbeDebugConstants constants = {}; - view.FillPlanarViewConstants(constants.view); - constants.blasDeviceAddressLow = (uint32_t)blasDeviceAddress; - constants.blasDeviceAddressHigh = (uint32_t)(blasDeviceAddress >> 32); - constants.volumeIndex = uint32_t(volumeIndex); - commandList->writeBuffer(m_DebugConstantBuffer, &constants, sizeof(constants)); - - auto instanceState = nvrhi::ComputeState() - .setPipeline(m_DebugInstancesPipeline) - .addBindingSet(m_DebugInstancesBindingSet) - .addBindingSet(descriptorTable); - - commandList->setComputeState(instanceState); - commandList->dispatch(dm::div_ceil(numProbes, 256)); - - commandList->setAccelStructState(m_ProbeBLAS, nvrhi::ResourceStates::AccelStructBuildBlas); - - commandList->buildTopLevelAccelStructFromBuffer(m_DebugTLAS, m_DebugTLASInstanceBuffer, 0, numProbes); - - m_DebugPass->Execute(commandList, view.GetViewExtent().width(), view.GetViewExtent().height(), - m_DebugBindingSet, nullptr, descriptorTable); -} - -void RtxgiIntegration::NextFrame() -{ - std::swap(m_DebugBindingSet, m_PrevDebugBindingSet); -} - -std::shared_ptr RtxgiIntegration::CreateVolume(const RtxgiVolumeParameters& params) -{ - if (m_Volumes.size() >= c_MaxVolumes) - { - log::warning("Cannot create a new RTXGI volume: the maximum supported " - "number of volumes (%d) has been reached.", c_MaxVolumes); - return nullptr; - } - - rtxgi::DDGIVolumeDesc volumeDesc; - volumeDesc.index = uint32_t(m_Volumes.size()); - volumeDesc.name = params.name; - volumeDesc.rngSeed = 0; - volumeDesc.probeCounts = { params.probeCounts.x, params.probeCounts.y, params.probeCounts.z }; - volumeDesc.origin = { params.origin.x, params.origin.y, params.origin.z }; - volumeDesc.probeSpacing = { params.probeSpacing, params.probeSpacing, params.probeSpacing }; - volumeDesc.eulerAngles = { dm::radians(params.eulerAngles.x), dm::radians(params.eulerAngles.y), dm::radians(params.eulerAngles.z) }; - volumeDesc.probeNumRays = RTXGI_DDGI_BLEND_RAYS_PER_PROBE; - volumeDesc.probeRayDataFormat = 1; - volumeDesc.probeIrradianceFormat = 1; - volumeDesc.probeDistanceFormat = 0; - volumeDesc.probeNumIrradianceTexels = RTXGI_DDGI_PROBE_NUM_TEXELS; - volumeDesc.probeNumDistanceTexels = RTXGI_DDGI_PROBE_NUM_TEXELS; - volumeDesc.probeRelocationEnabled = true; - volumeDesc.probeClassificationEnabled = true; - volumeDesc.probeMinFrontfaceDistance = 0.2f; - volumeDesc.movementType = params.scrolling - ? rtxgi::EDDGIVolumeMovementType::Scrolling - : rtxgi::EDDGIVolumeMovementType::Default; - - std::shared_ptr volume; - -#if USE_DX12 - if (m_Device->getGraphicsAPI() == nvrhi::GraphicsAPI::D3D12) - volume = RtxgiVolume::CreateDX12(m_Device, weak_from_this(), m_FileSystem, m_DescriptorTable, volumeDesc); - else -#endif -#if USE_VK - if (m_Device->getGraphicsAPI() == nvrhi::GraphicsAPI::VULKAN) - volume = RtxgiVolume::CreateVK(m_Device, weak_from_this(), m_FileSystem, m_DescriptorTable, volumeDesc); - else -#endif - { - nvrhi::utils::NotSupported(); - return nullptr; - } - - m_Volumes.push_back(volume); - - return volume; -} - -std::shared_ptr RtxgiIntegration::GetVolume(int index) -{ - if (index < 0 || index > int(m_Volumes.size())) - return nullptr; - - return m_Volumes[index]; -} - -void RtxgiIntegration::RemoveVolume(int index) -{ - if (index < 0 || index > int(m_Volumes.size())) - return; - - m_Volumes.erase(m_Volumes.begin() + index); - - for (uint32_t i = 0; i < uint32_t(m_Volumes.size()); ++i) - m_Volumes[i]->GetVolume()->SetIndex(i); -} - -void RtxgiVolume::UpdateConstants(nvrhi::ICommandList* commandList) -{ - if (!IsInitialized()) - return; - - auto parentShared = m_Parent.lock(); - assert(parentShared); - - GetVolume()->Update(); - - rtxgi::DDGIVolumeDescGPUPacked volumeDesc = GetVolume()->GetDescGPUPacked(); - commandList->writeBuffer(parentShared->GetConstantBuffer(), &volumeDesc, sizeof(volumeDesc), - sizeof(volumeDesc) * GetVolume()->GetIndex()); - - if (!m_ResourceIndicesUpdated) - { - DDGIVolumeResourceIndices resourceIndices{}; - resourceIndices.irradianceTextureSRV = m_IrradianceTextureSRV.Get(); - resourceIndices.distanceTextureSRV = m_DistanceTextureSRV.Get(); - resourceIndices.probeDataTextureSRV = m_ProbeDataTextureSRV.Get(); - resourceIndices.rayDataTextureUAV = m_RayDataTextureUAV.Get(); - - commandList->writeBuffer(parentShared->GetVolumeResourceIndicesBuffer(), &resourceIndices, sizeof(resourceIndices), - sizeof(resourceIndices) * GetVolume()->GetIndex()); - - m_ResourceIndicesUpdated = true; - } -} - -void RtxgiVolume::SetResourceStatesForTracing(nvrhi::ICommandList* commandList) const -{ - commandList->setTextureState(m_ProbeIrradiance, nvrhi::AllSubresources, nvrhi::ResourceStates::ShaderResource); - commandList->setTextureState(m_ProbeDistance, nvrhi::AllSubresources, nvrhi::ResourceStates::ShaderResource); - commandList->setTextureState(m_ProbeData, nvrhi::AllSubresources, nvrhi::ResourceStates::ShaderResource); - commandList->setTextureState(m_ProbeRayData, nvrhi::AllSubresources, nvrhi::ResourceStates::UnorderedAccess); -} - -void RtxgiVolume::Trace(nvrhi::ICommandList* commandList, nvrhi::IBindingSet* ligthingBindings, nvrhi::IDescriptorTable* descriptorTable) -{ - if (!IsInitialized()) - return; - - auto parentShared = m_Parent.lock(); - assert(parentShared); - - PerPassConstants pushConstants{}; - pushConstants.rayCountBufferIndex = ProfilerSection::RtxgiProbeTracing; - pushConstants.rtxgiVolumeIndex = GetVolume()->GetIndex(); - - parentShared->GetProbeTracingPass().Execute(commandList, - GetVolume()->GetNumRaysPerProbe(), GetVolume()->GetNumProbes(), - ligthingBindings, parentShared->GetProbeTracingBindingSet(), descriptorTable, - &pushConstants, sizeof(pushConstants)); -} - -void RtxgiVolume::SetOrigin(const dm::float3 origin) -{ - auto* volume = GetVolume(); - volume->SetScrollAnchor({ origin.x, origin.y, origin.z }); -} - -void RtxgiVolume::SetParameters(const RtxgiParameters& params) -{ - auto* volume = GetVolume(); - volume->SetProbeHysteresis(params.hysteresis); - volume->SetProbeBrightnessThreshold(params.brightnessThreshold); - volume->SetProbeIrradianceThreshold(params.irradianceThreshold); - volume->SetProbeRelocationEnabled(params.probeRelocation); - volume->SetProbeClassificationEnabled(params.probeClassification); - volume->SetMinFrontFaceDistance(params.minFrontFaceDistanceFraction * volume->GetDesc().probeSpacing.x); - volume->SetProbeRelocationNeedsReset(params.resetRelocation); -} - -void RtxgiVolume::SetVolumeParameters(const RtxgiVolumeParameters& params) -{ - auto* volume = GetVolume(); - volume->SetProbeSpacing({ params.probeSpacing, params.probeSpacing, params.probeSpacing }); - if (!params.scrolling) - volume->SetOrigin({ params.origin.x, params.origin.y, params.origin.z }); - volume->SetEulerAngles({ dm::radians(params.eulerAngles.x), dm::radians(params.eulerAngles.y), dm::radians(params.eulerAngles.z) }); -} - -void RtxgiIntegration::CreateDebugResources(nvrhi::ICommandList* commandList, RtxgiVolume* volume, RenderTargets& renderTargets) -{ - if (!m_ProbeBLAS) - { - auto blasDesc = nvrhi::rt::AccelStructDesc() - .setDebugName("RtxgiDebugProbe") - .setIsTopLevel(false) - .addBottomLevelGeometry(nvrhi::rt::GeometryDesc() - .setAABBs(nvrhi::rt::GeometryAABBs() - .setCount(1))); - - m_ProbeBLAS = m_Device->createAccelStruct(blasDesc); - - auto bufferDesc = nvrhi::BufferDesc() - .setByteSize(6 * sizeof(float)) - .setIsAccelStructBuildInput(true) - .setInitialState(nvrhi::ResourceStates::Common) - .setKeepInitialState(true); - - nvrhi::BufferHandle buffer = m_Device->createBuffer(bufferDesc); - - float aabb[] = { - -1.f, -1.f, -1.f, - 1.f, 1.f, 1.f - }; - - commandList->writeBuffer(buffer, aabb, sizeof(aabb), 0); - - auto geometryDesc = nvrhi::rt::GeometryDesc() - .setAABBs(nvrhi::rt::GeometryAABBs().setCount(1).setBuffer(buffer)); - - commandList->buildBottomLevelAccelStruct(m_ProbeBLAS, &geometryDesc, 1); - } - - uint32_t numProbesInVolume = volume->GetVolume()->GetNumProbes(); - - - if (!m_DebugTLAS || m_DebugTLAS->getDesc().topLevelMaxInstances < numProbesInVolume) - { - auto tlasDesc = nvrhi::rt::AccelStructDesc() - .setDebugName("RtxgiDebugTLAS") - .setIsTopLevel(true) - .setTopLevelMaxInstances(numProbesInVolume); - - m_DebugTLAS = m_Device->createAccelStruct(tlasDesc); - - auto debugInstanceBufferDesc = nvrhi::BufferDesc() - .setDebugName("RtxgiDebugInstances") - .setByteSize(sizeof(nvrhi::rt::InstanceDesc) * numProbesInVolume) - .setStructStride(sizeof(nvrhi::rt::InstanceDesc)) - .setIsAccelStructBuildInput(true) - .setCanHaveUAVs(true) - .setKeepInitialState(true) - .setInitialState(nvrhi::ResourceStates::UnorderedAccess); - - m_DebugTLASInstanceBuffer = m_Device->createBuffer(debugInstanceBufferDesc); - - m_DebugInstancesBindingSet = nullptr; - m_DebugBindingSet = nullptr; - m_PrevDebugBindingSet = nullptr; - } - - if (!m_DebugConstantBuffer) - { - auto constantBufferDesc = nvrhi::utils::CreateVolatileConstantBufferDesc( - sizeof(ProbeDebugConstants), "ProbeDebugConstants", 16); - m_DebugConstantBuffer = m_Device->createBuffer(constantBufferDesc); - - m_DebugInstancesBindingSet = nullptr; - m_DebugBindingSet = nullptr; - m_PrevDebugBindingSet = nullptr; - } - - if (!m_DebugBindingSet) - { - for (int currentFrame = 0; currentFrame <= 1; currentFrame++) - { - auto debugSetDesc = nvrhi::BindingSetDesc() - .addItem(nvrhi::BindingSetItem::ConstantBuffer(0, m_DebugConstantBuffer)) - .addItem(nvrhi::BindingSetItem::RayTracingAccelStruct(0, m_DebugTLAS)) - .addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(1, m_RtxgiConstantBuffer)) - .addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(2, m_VolumeResourceIndicesBuffer)) - .addItem(nvrhi::BindingSetItem::Texture_SRV(3, currentFrame ? renderTargets.Depth : renderTargets.PrevDepth)) - .addItem(nvrhi::BindingSetItem::Texture_UAV(0, renderTargets.HdrColor)) - .addItem(nvrhi::BindingSetItem::Sampler(0, m_ProbeSampler)); - - nvrhi::BindingSetHandle bindingSet = m_Device->createBindingSet(debugSetDesc, m_DebugBindingLayout); - - if (currentFrame) - m_DebugBindingSet = bindingSet; - else - m_PrevDebugBindingSet = bindingSet; - } - } - - if (!m_DebugInstancesBindingSet) - { - auto instanceBindingSetDesc = nvrhi::BindingSetDesc() - .addItem(nvrhi::BindingSetItem::ConstantBuffer(0, m_DebugConstantBuffer)) - .addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(0, m_RtxgiConstantBuffer)) - .addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(1, m_VolumeResourceIndicesBuffer)) - .addItem(nvrhi::BindingSetItem::StructuredBuffer_UAV(0, m_DebugTLASInstanceBuffer)); - - m_DebugInstancesBindingSet = m_Device->createBindingSet(instanceBindingSetDesc, m_DebugInstancesBindingLayout); - } -} - -#endif // defined(WITH_RTXGI) \ No newline at end of file diff --git a/src/RtxgiIntegration.h b/src/RtxgiIntegration.h deleted file mode 100644 index 96cf796..0000000 --- a/src/RtxgiIntegration.h +++ /dev/null @@ -1,193 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2020-2021, NVIDIA CORPORATION. All rights reserved. - # - # NVIDIA CORPORATION and its licensors retain all intellectual property - # and proprietary rights in and to this software, related documentation - # and any modifications thereto. Any use, reproduction, disclosure or - # distribution of this software and related documentation without an express - # license agreement from NVIDIA CORPORATION is strictly prohibited. - **************************************************************************/ - -#pragma once - -#include -#include -#include -#include -#include - -#include "RayTracingPass.h" - -class RenderTargets; -class Profiler; -class RtxgiIntegration; -struct RtxgiParameters; -struct RtxgiVolumeParameters; - -namespace donut::vfs -{ - class IBlob; - class IFileSystem; -} - -namespace donut::engine -{ - class IView; - class ShaderFactory; -} - -namespace rtxgi -{ - class DDGIVolumeBase; - struct DDGIVolumeDesc; - struct ShaderBytecode; -} - -namespace ShaderMake -{ - struct ShaderConstant; -} - -namespace rtxdi -{ - struct ContextParameters; -} - -class RtxgiVolume // NOLINT(cppcoreguidelines-special-member-functions) -{ -protected: - nvrhi::DeviceHandle m_Device; - std::weak_ptr m_Parent; - - nvrhi::TextureHandle m_ProbeRayData; - nvrhi::TextureHandle m_ProbeIrradiance; - nvrhi::TextureHandle m_ProbeDistance; - nvrhi::TextureHandle m_ProbeData; - - donut::engine::DescriptorHandle m_IrradianceTextureSRV; - donut::engine::DescriptorHandle m_DistanceTextureSRV; - donut::engine::DescriptorHandle m_ProbeDataTextureSRV; - donut::engine::DescriptorHandle m_RayDataTextureUAV; - - bool m_ResourceIndicesUpdated = false; - - static bool LoadShader(rtxgi::ShaderBytecode& dest, const std::shared_ptr& fs, const char* shaderName, - std::vector defines, std::vector>& blobs); - -public: - static std::shared_ptr CreateDX12( - nvrhi::IDevice* device, - const std::weak_ptr& parent, - const std::shared_ptr& fs, - const std::shared_ptr& descriptorTable, - const rtxgi::DDGIVolumeDesc& volumeDesc); - - static std::shared_ptr CreateVK( - nvrhi::IDevice* device, - const std::weak_ptr& parent, - const std::shared_ptr& fs, - const std::shared_ptr& descriptorTable, - const rtxgi::DDGIVolumeDesc& volumeDesc); - - static void UpdateVolumesDX12(nvrhi::ICommandList* commandList, const std::vector>& volumes); - static void UpdateVolumesVK(nvrhi::ICommandList* commandList, const std::vector>& volumes); - - explicit RtxgiVolume(nvrhi::IDevice* device, std::weak_ptr parent) - : m_Device(device) - , m_Parent(std::move(parent)) - { } - - virtual ~RtxgiVolume() = default; - - [[nodiscard]] - virtual bool IsInitialized() const = 0; - - [[nodiscard]] virtual rtxgi::DDGIVolumeBase* GetVolume() = 0; - - void UpdateConstants(nvrhi::ICommandList* commandList); - - void SetResourceStatesForTracing(nvrhi::ICommandList* commandList) const; - - void Trace(nvrhi::ICommandList* commandList, - nvrhi::IBindingSet* ligthingBindings, - nvrhi::IDescriptorTable* descriptorTable); - - void SetOrigin(const dm::float3 origin); - void SetParameters(const RtxgiParameters& params); - void SetVolumeParameters(const RtxgiVolumeParameters& params); -}; - -class RtxgiIntegration : public std::enable_shared_from_this // NOLINT(cppcoreguidelines-special-member-functions) -{ -protected: - nvrhi::DeviceHandle m_Device; - - nvrhi::BufferHandle m_RtxgiConstantBuffer; - nvrhi::BufferHandle m_VolumeResourceIndicesBuffer; - nvrhi::SamplerHandle m_ProbeSampler; - - std::unique_ptr m_ProbeTracingPass; - nvrhi::BindingLayoutHandle m_ProbeTracingLayout; - nvrhi::BindingSetHandle m_ProbeTracingSet; - - nvrhi::rt::AccelStructHandle m_ProbeBLAS; - nvrhi::rt::AccelStructHandle m_DebugTLAS; - std::unique_ptr m_DebugPass; - nvrhi::BufferHandle m_DebugConstantBuffer; - nvrhi::BindingLayoutHandle m_DebugBindingLayout; - nvrhi::BindingSetHandle m_DebugBindingSet; - nvrhi::BindingSetHandle m_PrevDebugBindingSet; - - nvrhi::BufferHandle m_DebugTLASInstanceBuffer; - nvrhi::BindingLayoutHandle m_DebugInstancesBindingLayout; - nvrhi::BindingSetHandle m_DebugInstancesBindingSet; - nvrhi::ComputePipelineHandle m_DebugInstancesPipeline; - - std::shared_ptr m_FileSystem; - std::shared_ptr m_DescriptorTable; - std::vector> m_Volumes; - - void CreateDebugResources(nvrhi::ICommandList* commandList, RtxgiVolume* volume, RenderTargets& renderTargets); - -public: - - RtxgiIntegration( - nvrhi::IDevice* device, - const std::shared_ptr& fs, - const std::shared_ptr& descriptorTable); - - std::shared_ptr CreateVolume(const RtxgiVolumeParameters& params); - std::shared_ptr GetVolume(int index); - void RemoveVolume(int index); - - void RenderDebug( - nvrhi::ICommandList* commandList, - nvrhi::IDescriptorTable* descriptorTable, - RenderTargets& renderTargets, - int volumeIndex, - const donut::engine::IView& view); - - void InitializePasses( - donut::engine::ShaderFactory& shaderFactory, - nvrhi::IBindingLayout* lightingLayout, - nvrhi::IBindingLayout* bindlessLayout, - const rtxdi::ContextParameters& contextParameters, - bool useRayQuery); - - void UpdateAllVolumes( - nvrhi::ICommandList* commandList, - nvrhi::IBindingSet* ligthingBindings, - nvrhi::IDescriptorTable* descriptorTable, - Profiler& profiler); - - void InvalidateRenderTargets(); - - void NextFrame(); - - [[nodiscard]] nvrhi::IBuffer* GetConstantBuffer() const { return m_RtxgiConstantBuffer; } - [[nodiscard]] nvrhi::IBuffer* GetVolumeResourceIndicesBuffer() const { return m_VolumeResourceIndicesBuffer; } - [[nodiscard]] nvrhi::ISampler* GetProbeSampler() const { return m_ProbeSampler; } - [[nodiscard]] RayTracingPass& GetProbeTracingPass() const { return *m_ProbeTracingPass; } - [[nodiscard]] nvrhi::IBindingSet* GetProbeTracingBindingSet() const { return m_ProbeTracingSet; } - [[nodiscard]] int GetNumVolumes() const { return int(m_Volumes.size()); } -}; diff --git a/src/SampleScene.cpp b/src/SampleScene.cpp index 125c852..92cb04e 100644 --- a/src/SampleScene.cpp +++ b/src/SampleScene.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -171,27 +171,6 @@ std::shared_ptr SampleSceneTypeFactory::CreateMesh() return std::make_shared(); } -bool SampleScene::LoadCustomData(Json::Value& rootNode, tf::Executor* executor) -{ - const Json::Value& rtxgiNode = rootNode["rtxgi-volumes"]; - if (rtxgiNode.isArray()) - { - for (const Json::Value& volumeNode : rtxgiNode) - { - RtxgiVolumeParameters volume; - volumeNode["name"] >> volume.name; - volumeNode["scrolling"] >> volume.scrolling; - volumeNode["origin"] >> volume.origin; - volumeNode["probeSpacing"] >> volume.probeSpacing; - volumeNode["eulerAngles"] >> volume.eulerAngles; - volumeNode["probeCounts"] >> volume.probeCounts; - m_RtxgiVolumes.push_back(volume); - } - } - - return Scene::LoadCustomData(rootNode, executor); -} - bool SampleScene::LoadWithExecutor(const std::filesystem::path& jsonFileName, tf::Executor* executor) { if (!Scene::LoadWithExecutor(jsonFileName, executor)) diff --git a/src/SampleScene.h b/src/SampleScene.h index b43782f..c16cc4e 100644 --- a/src/SampleScene.h +++ b/src/SampleScene.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation @@ -94,16 +94,6 @@ class SampleSceneTypeFactory : public donut::engine::SceneTypeFactory std::shared_ptr CreateMesh() override; }; -struct RtxgiVolumeParameters -{ - bool scrolling = false; - float probeSpacing = 1.f; - dm::int3 probeCounts = 1; - dm::float3 origin = 0.f; - dm::float3 eulerAngles = 0.f; - std::string name; -}; - class SampleScene : public donut::engine::Scene { private: @@ -112,8 +102,7 @@ class SampleScene : public donut::engine::Scene std::vector m_TlasInstances; std::shared_ptr m_BenchmarkAnimation; std::shared_ptr m_BenchmarkCamera; - std::vector m_RtxgiVolumes; - + bool m_CanUpdateTLAS = false; bool m_CanUpdatePrevTLAS = false; @@ -121,9 +110,6 @@ class SampleScene : public donut::engine::Scene std::vector m_EnvironmentMaps; -protected: - bool LoadCustomData(Json::Value& rootNode, tf::Executor* executor) override; - public: using Scene::Scene; @@ -142,5 +128,4 @@ class SampleScene : public donut::engine::Scene nvrhi::rt::IAccelStruct* GetPrevTopLevelAS() const { return m_PrevTopLevelAS; } std::vector& GetEnvironmentMaps() { return m_EnvironmentMaps; } - std::vector& GetRtxgiVolumes() { return m_RtxgiVolumes; } }; diff --git a/src/Testing.cpp b/src/Testing.cpp index 43d82b0..3579a14 100644 --- a/src/Testing.cpp +++ b/src/Testing.cpp @@ -89,26 +89,49 @@ std::istream& operator>> (std::istream& is, IndirectLightingMode& mode) return is; } -std::istream& operator>> (std::istream& is, ResamplingMode& mode) +std::istream& operator>> (std::istream& is, rtxdi::ReSTIRDI_ResamplingMode& mode) { std::string s; is >> s; toupper(s); if (s == "NONE") - mode = ResamplingMode::None; + mode = rtxdi::ReSTIRDI_ResamplingMode::None; else if (s == "TEMPORAL") - mode = ResamplingMode::Temporal; + mode = rtxdi::ReSTIRDI_ResamplingMode::Temporal; else if (s == "SPATIAL") - mode = ResamplingMode::Spatial; + mode = rtxdi::ReSTIRDI_ResamplingMode::Spatial; else if (s == "TEMPORAL_SPATIAL") - mode = ResamplingMode::TemporalAndSpatial; + mode = rtxdi::ReSTIRDI_ResamplingMode::TemporalAndSpatial; else if (s == "FUSED") - mode = ResamplingMode::FusedSpatiotemporal; + mode = rtxdi::ReSTIRDI_ResamplingMode::FusedSpatiotemporal; else throw cxxopts::OptionException("Unrecognized value passed to the --gi-mode argument."); - + + return is; +} + +std::istream& operator>> (std::istream& is, rtxdi::ReSTIRGI_ResamplingMode& mode) +{ + std::string s; + is >> s; + toupper(s); + + if (s == "NONE") + mode = rtxdi::ReSTIRGI_ResamplingMode::None; + else if (s == "TEMPORAL") + mode = rtxdi::ReSTIRGI_ResamplingMode::Temporal; + else if (s == "SPATIAL") + mode = rtxdi::ReSTIRGI_ResamplingMode::Spatial; + else if (s == "TEMPORAL_SPATIAL") + mode = rtxdi::ReSTIRGI_ResamplingMode::TemporalAndSpatial; + else if (s == "FUSED") + mode = rtxdi::ReSTIRGI_ResamplingMode::FusedSpatiotemporal; + + else + throw cxxopts::OptionException("Unrecognized value passed to the --gi-mode argument."); + return is; } @@ -157,11 +180,11 @@ void ProcessCommandLine(int argc, char** argv, donut::app::DeviceCreationParamet ("checkerboard", "Use checkerboard rendering", value(checkerboard)) ("d,debug", "Enable the DX12 or Vulkan validation layers", value(deviceParams.enableDebugRuntime)) ("disable-bg-opt", "Disable DX12 driver background optimization", value(args.disableBackgroundOptimization)) - ("direct-resampling", "Direct lighting resampling mode: NONE, TEMPORAL, SPATIAL, TEMPORAL_SPATIAL, FUSED", value(ui.lightingSettings.resamplingMode)) + ("direct-resampling", "Direct lighting resampling mode: NONE, TEMPORAL, SPATIAL, TEMPORAL_SPATIAL, FUSED", value(ui.restirDI.resamplingMode)) ("fullscreen", "Run in full screen", value(deviceParams.startFullscreen)) ("h,help", "Display this help message", value(help)) ("height", "Window height", value(deviceParams.backBufferHeight)) - ("indirect-resampling", "ReSTIR GI resampling mode: NONE, TEMPORAL, SPATIAL, TEMPORAL_SPATIAL, FUSED", value(ui.lightingSettings.reStirGI.resamplingMode)) + ("indirect-resampling", "ReSTIR GI resampling mode: NONE, TEMPORAL, SPATIAL, TEMPORAL_SPATIAL, FUSED", value(ui.restirGI.resamplingMode)) ("noise-mix", "Amount of noise to mix in after denoising", value(ui.noiseMix)) ("pixel-jitter", "Pixel jitter toggle", value(ui.enablePixelJitter)) ("preset", "Rendering settings preset: FAST, MEDIUM, UNBIASED, ULTRA, REFERENCE", value(ui)) @@ -186,12 +209,6 @@ void ProcessCommandLine(int argc, char** argv, donut::app::DeviceCreationParamet ; #endif -#if WITH_RTXGI - options.add_options() - ("rtxgi", "RTXGI toggle", value(ui.rtxgi.enabled)) - ; -#endif - try { options.parse(argc, argv); @@ -250,7 +267,7 @@ void ProcessCommandLine(int argc, char** argv, donut::app::DeviceCreationParamet ui.animationFrame = 0; if (checkerboard) - ui.rtxdiContextParams.CheckerboardSamplingMode = rtxdi::CheckerboardMode::Black; + ui.restirDIStaticParams.CheckerboardSamplingMode = rtxdi::CheckerboardMode::Black; } void ApplicationLogCallback(log::Severity severity, const char* message) diff --git a/src/Testing.h b/src/Testing.h index 549cfef..35460c1 100644 --- a/src/Testing.h +++ b/src/Testing.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. # # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation diff --git a/src/UserInterface.cpp b/src/UserInterface.cpp index 1c96bb2..f97a0df 100644 --- a/src/UserInterface.cpp +++ b/src/UserInterface.cpp @@ -47,12 +47,21 @@ using namespace donut; UIData::UIData() { - rtxdiContextParams.ReGIR.Mode = rtxdi::ReGIRMode::Onion; - taaParams.newFrameWeight = 0.04f; taaParams.maxRadiance = 200.f; taaParams.clampingFactor = 1.3f; + restirDI.resamplingMode = rtxdi::ReSTIRDI_ResamplingMode::TemporalAndSpatial; + restirDI.initialSamplingParams = rtxdi::getDefaultReSTIRDIInitialSamplingParams(); + restirDI.temporalResamplingParams = rtxdi::getDefaultReSTIRDITemporalResamplingParams(); + restirDI.spatialResamplingParams = rtxdi::getDefaultReSTIRDISpatialResamplingParams(); + restirDI.shadingParams = rtxdi::getDefaultReSTIRDIShadingParams(); + + restirGI.resamplingMode = rtxdi::ReSTIRGI_ResamplingMode::TemporalAndSpatial; + restirGI.temporalResamplingParams = rtxdi::getDefaultReSTIRGITemporalResamplingParams(); + restirGI.spatialResamplingParams = rtxdi::getDefaultReSTIRGISpatialResamplingParams(); + restirGI.finalShadingParams = rtxdi::getDefaultReSTIRGIFinalShadingParams(); + ApplyPreset(); #ifdef WITH_NRD @@ -62,7 +71,7 @@ UIData::UIData() void UIData::ApplyPreset() { - bool enableCheckerboardSampling = (rtxdiContextParams.CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off); + bool enableCheckerboardSampling = (restirDIStaticParams.CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off); if (preset != QualityPreset::Custom) lightingSettings = LightingPasses::RenderSettings(); @@ -71,95 +80,115 @@ void UIData::ApplyPreset() { case QualityPreset::Fast: enableCheckerboardSampling = true; - lightingSettings.numPrimaryRegirSamples = 0; - lightingSettings.numPrimaryLocalLightSamples = 4; - lightingSettings.numPrimaryBrdfSamples = 0; - lightingSettings.numPrimaryInfiniteLightSamples = 1; - lightingSettings.enableReGIR = false; - lightingSettings.temporalBiasCorrection = RTXDI_BIAS_CORRECTION_OFF; - lightingSettings.spatialBiasCorrection = RTXDI_BIAS_CORRECTION_OFF; - lightingSettings.numSpatialSamples = 1; - lightingSettings.numDisocclusionBoostSamples = 2; - lightingSettings.discardInvisibleSamples = true; - lightingSettings.reuseFinalVisibility = true; - lightingSettings.enableBoilingFilter = true; - lightingSettings.enableSecondaryResampling = false; + restirDI.resamplingMode = rtxdi::ReSTIRDI_ResamplingMode::TemporalAndSpatial; + restirDI.initialSamplingParams.localLightSamplingMode = ReSTIRDI_LocalLightSamplingMode::Power_RIS; + restirDI.numLocalLightUniformSamples = 4; + restirDI.numLocalLightPowerRISSamples = 4; + restirDI.numLocalLightReGIRRISSamples = 4; + restirDI.initialSamplingParams.numPrimaryLocalLightSamples = restirDI.numLocalLightPowerRISSamples; + restirDI.initialSamplingParams.numPrimaryBrdfSamples = 0; + restirDI.initialSamplingParams.numPrimaryInfiniteLightSamples = 1; + restirDI.temporalResamplingParams.discardInvisibleSamples = true; + restirDI.temporalResamplingParams.enableBoilingFilter = true; + restirDI.temporalResamplingParams.boilingFilterStrength = 0.2f; + restirDI.temporalResamplingParams.temporalBiasCorrection = ReSTIRDI_TemporalBiasCorrectionMode::Off; + restirDI.spatialResamplingParams.spatialBiasCorrection = ReSTIRDI_SpatialBiasCorrectionMode::Off; + restirDI.spatialResamplingParams.numSpatialSamples = 1; + restirDI.spatialResamplingParams.numDisocclusionBoostSamples = 2; + restirDI.shadingParams.reuseFinalVisibility = true; + lightingSettings.brdfptParams.enableSecondaryResampling = false; lightingSettings.enableGradients = false; break; case QualityPreset::Medium: enableCheckerboardSampling = false; - lightingSettings.numPrimaryRegirSamples = 8; - lightingSettings.numPrimaryLocalLightSamples = 8; - lightingSettings.numPrimaryBrdfSamples = 1; - lightingSettings.numPrimaryInfiniteLightSamples = 2; - lightingSettings.enableReGIR = true; - lightingSettings.temporalBiasCorrection = RTXDI_BIAS_CORRECTION_RAY_TRACED; - lightingSettings.spatialBiasCorrection = RTXDI_BIAS_CORRECTION_BASIC; - lightingSettings.numSpatialSamples = 1; - lightingSettings.numDisocclusionBoostSamples = 8; - lightingSettings.discardInvisibleSamples = true; - lightingSettings.reuseFinalVisibility = true; - lightingSettings.enableBoilingFilter = true; - lightingSettings.enableSecondaryResampling = true; - lightingSettings.secondarySamplingRadius = 1.f; - lightingSettings.numSecondarySamples = 1; - lightingSettings.secondaryBiasCorrection = RTXDI_BIAS_CORRECTION_BASIC; + restirDI.resamplingMode = rtxdi::ReSTIRDI_ResamplingMode::TemporalAndSpatial; + restirDI.initialSamplingParams.localLightSamplingMode = ReSTIRDI_LocalLightSamplingMode::ReGIR_RIS; + restirDI.numLocalLightUniformSamples = 8; + restirDI.numLocalLightPowerRISSamples = 8; + restirDI.numLocalLightReGIRRISSamples = 8; + restirDI.initialSamplingParams.numPrimaryLocalLightSamples = restirDI.numLocalLightReGIRRISSamples; + restirDI.initialSamplingParams.numPrimaryBrdfSamples = 1; + restirDI.initialSamplingParams.numPrimaryInfiniteLightSamples = 2; + restirDI.temporalResamplingParams.discardInvisibleSamples = true; + restirDI.temporalResamplingParams.enableBoilingFilter = true; + restirDI.temporalResamplingParams.boilingFilterStrength = 0.2f; + restirDI.temporalResamplingParams.temporalBiasCorrection = ReSTIRDI_TemporalBiasCorrectionMode::Raytraced; + restirDI.spatialResamplingParams.spatialBiasCorrection = ReSTIRDI_SpatialBiasCorrectionMode::Basic; + restirDI.spatialResamplingParams.numSpatialSamples = 1; + restirDI.spatialResamplingParams.numDisocclusionBoostSamples = 8; + restirDI.shadingParams.reuseFinalVisibility = true; + lightingSettings.brdfptParams.enableSecondaryResampling = true; + lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialSamplingRadius = 1.f; + lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.numSpatialSamples = 1; + lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialBiasCorrection = ReSTIRDI_SpatialBiasCorrectionMode::Basic; lightingSettings.enableGradients = true; break; case QualityPreset::Unbiased: enableCheckerboardSampling = false; - lightingSettings.numPrimaryRegirSamples = 16; - lightingSettings.numPrimaryLocalLightSamples = 8; - lightingSettings.numPrimaryBrdfSamples = 1; - lightingSettings.numPrimaryInfiniteLightSamples = 2; - lightingSettings.enableReGIR = true; - lightingSettings.temporalBiasCorrection = RTXDI_BIAS_CORRECTION_RAY_TRACED; - lightingSettings.spatialBiasCorrection = RTXDI_BIAS_CORRECTION_RAY_TRACED; - lightingSettings.numSpatialSamples = 1; - lightingSettings.numDisocclusionBoostSamples = 8; - lightingSettings.discardInvisibleSamples = false; - lightingSettings.reuseFinalVisibility = false; - lightingSettings.enableBoilingFilter = false; - lightingSettings.enableSecondaryResampling = true; - lightingSettings.secondarySamplingRadius = 1.f; - lightingSettings.numSecondarySamples = 1; - lightingSettings.secondaryBiasCorrection = RTXDI_BIAS_CORRECTION_RAY_TRACED; + restirDI.resamplingMode = rtxdi::ReSTIRDI_ResamplingMode::TemporalAndSpatial; + restirDI.initialSamplingParams.localLightSamplingMode = ReSTIRDI_LocalLightSamplingMode::Uniform; + restirDI.numLocalLightUniformSamples = 8; + restirDI.numLocalLightPowerRISSamples = 8; + restirDI.numLocalLightReGIRRISSamples = 16; + restirDI.initialSamplingParams.numPrimaryLocalLightSamples = restirDI.numLocalLightUniformSamples; + restirDI.initialSamplingParams.numPrimaryBrdfSamples = 1; + restirDI.initialSamplingParams.numPrimaryInfiniteLightSamples = 2; + restirDI.temporalResamplingParams.discardInvisibleSamples = false; + restirDI.temporalResamplingParams.enableBoilingFilter = false; + restirDI.temporalResamplingParams.boilingFilterStrength = 0.0f; + restirDI.temporalResamplingParams.temporalBiasCorrection = ReSTIRDI_TemporalBiasCorrectionMode::Raytraced; + restirDI.spatialResamplingParams.spatialBiasCorrection = ReSTIRDI_SpatialBiasCorrectionMode::Raytraced; + restirDI.spatialResamplingParams.numSpatialSamples = 1; + restirDI.spatialResamplingParams.numDisocclusionBoostSamples = 8; + restirDI.shadingParams.reuseFinalVisibility = false; + lightingSettings.brdfptParams.enableSecondaryResampling = true; + lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialSamplingRadius = 1.f; + lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.numSpatialSamples = 1; + lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialBiasCorrection = ReSTIRDI_SpatialBiasCorrectionMode::Raytraced; lightingSettings.enableGradients = true; break; case QualityPreset::Ultra: enableCheckerboardSampling = false; - lightingSettings.numPrimaryRegirSamples = 16; - lightingSettings.numPrimaryLocalLightSamples = 16; - lightingSettings.numPrimaryBrdfSamples = 1; - lightingSettings.numPrimaryInfiniteLightSamples = 16; - lightingSettings.enableReGIR = true; - lightingSettings.temporalBiasCorrection = RTXDI_BIAS_CORRECTION_RAY_TRACED; - lightingSettings.spatialBiasCorrection = RTXDI_BIAS_CORRECTION_RAY_TRACED; - lightingSettings.numSpatialSamples = 4; - lightingSettings.numDisocclusionBoostSamples = 16; - lightingSettings.discardInvisibleSamples = false; - lightingSettings.reuseFinalVisibility = false; - lightingSettings.enableBoilingFilter = false; - lightingSettings.enableSecondaryResampling = true; - lightingSettings.secondarySamplingRadius = 4.f; - lightingSettings.numSecondarySamples = 2; - lightingSettings.secondaryBiasCorrection = RTXDI_BIAS_CORRECTION_RAY_TRACED; + restirDI.resamplingMode = rtxdi::ReSTIRDI_ResamplingMode::TemporalAndSpatial; + restirDI.initialSamplingParams.localLightSamplingMode = ReSTIRDI_LocalLightSamplingMode::ReGIR_RIS; + restirDI.numLocalLightUniformSamples = 16; + restirDI.numLocalLightPowerRISSamples = 16; + restirDI.numLocalLightReGIRRISSamples = 16; + restirDI.initialSamplingParams.numPrimaryLocalLightSamples = restirDI.numLocalLightReGIRRISSamples; + restirDI.initialSamplingParams.localLightSamplingMode = ReSTIRDI_LocalLightSamplingMode::ReGIR_RIS; + restirDI.initialSamplingParams.numPrimaryBrdfSamples = 1; + restirDI.initialSamplingParams.numPrimaryInfiniteLightSamples = 16; + restirDI.temporalResamplingParams.discardInvisibleSamples = false; + restirDI.temporalResamplingParams.enableBoilingFilter = false; + restirDI.temporalResamplingParams.boilingFilterStrength = 0.0f; + restirDI.temporalResamplingParams.temporalBiasCorrection = ReSTIRDI_TemporalBiasCorrectionMode::Raytraced; + restirDI.spatialResamplingParams.spatialBiasCorrection = ReSTIRDI_SpatialBiasCorrectionMode::Raytraced; + restirDI.spatialResamplingParams.numSpatialSamples = 4; + restirDI.spatialResamplingParams.numDisocclusionBoostSamples = 16; + restirDI.shadingParams.reuseFinalVisibility = false; + lightingSettings.brdfptParams.enableSecondaryResampling = true; + lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialSamplingRadius = 4.f; + lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.numSpatialSamples = 2; + lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialBiasCorrection = ReSTIRDI_SpatialBiasCorrectionMode::Raytraced; lightingSettings.enableGradients = true; break; case QualityPreset::Reference: enableCheckerboardSampling = false; - lightingSettings.numPrimaryRegirSamples = 0; - lightingSettings.numPrimaryLocalLightSamples = 16; - lightingSettings.numPrimaryBrdfSamples = 1; - lightingSettings.numPrimaryInfiniteLightSamples = 16; - lightingSettings.enableReGIR = false; - lightingSettings.resamplingMode = ResamplingMode::None; - lightingSettings.enableBoilingFilter = false; - lightingSettings.enableSecondaryResampling = false; + restirDI.resamplingMode = rtxdi::ReSTIRDI_ResamplingMode::None; + restirDI.initialSamplingParams.localLightSamplingMode = ReSTIRDI_LocalLightSamplingMode::Uniform; + restirDI.numLocalLightUniformSamples = 16; + restirDI.numLocalLightPowerRISSamples = 16; + restirDI.numLocalLightReGIRRISSamples = 0; + restirDI.initialSamplingParams.numPrimaryLocalLightSamples = restirDI.numLocalLightUniformSamples; + restirDI.initialSamplingParams.numPrimaryBrdfSamples = 1; + restirDI.initialSamplingParams.numPrimaryInfiniteLightSamples = 16; + restirDI.temporalResamplingParams.enableBoilingFilter = false; + restirDI.temporalResamplingParams.boilingFilterStrength = 0.0f; + lightingSettings.brdfptParams.enableSecondaryResampling = false; lightingSettings.enableGradients = false; break; @@ -168,10 +197,10 @@ void UIData::ApplyPreset() } rtxdi::CheckerboardMode newCheckerboardMode = enableCheckerboardSampling ? rtxdi::CheckerboardMode::Black : rtxdi::CheckerboardMode::Off; - if (newCheckerboardMode != rtxdiContextParams.CheckerboardSamplingMode) + if (newCheckerboardMode != restirDIStaticParams.CheckerboardSamplingMode) { - rtxdiContextParams.CheckerboardSamplingMode = newCheckerboardMode; - resetRtxdiContext = true; + restirDIStaticParams.CheckerboardSamplingMode = newCheckerboardMode; + resetISContext = true; } } @@ -304,47 +333,58 @@ void UserInterface::GeneralRenderingSettings() void UserInterface::SamplingSettings() { - if (ImGui_ColoredTreeNode("Shared ReSTIR Settings", c_ColorRegularHeader)) + if (ImGui_ColoredTreeNode("Static ReSTIR Context Settings", c_ColorRegularHeader)) { - m_ui.resetAccumulation |= ImGui::Checkbox("Importance Sample Local Lights", &m_ui.enableLocalLightImportanceSampling); + ShowHelpMarker("Heavyweight settings (e.g. that dictate buffer sizes) that require recreating the context to change."); m_ui.resetAccumulation |= ImGui::Checkbox("Importance Sample Env. Map", &m_ui.environmentMapImportanceSampling); if (ImGui::TreeNode("RTXDI Context")) { if (ImGui::Button("Apply Settings")) - m_ui.resetRtxdiContext = true; + m_ui.resetISContext = true; - bool enableCheckerboardSampling = (m_ui.rtxdiContextParams.CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off); + // TODO: Pull this out of ReSTIRDIContext and make it global to both ReSTIRDI and ReSTIRGI + bool enableCheckerboardSampling = (m_ui.restirDIStaticParams.CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off); ImGui::Checkbox("Checkerboard Rendering", &enableCheckerboardSampling); - m_ui.rtxdiContextParams.CheckerboardSamplingMode = enableCheckerboardSampling ? rtxdi::CheckerboardMode::Black : rtxdi::CheckerboardMode::Off; - - ImGui::Combo("ReGIR Mode", (int*)&m_ui.rtxdiContextParams.ReGIR.Mode, "Disabled\0Grid\0Onion\0"); - ImGui::DragInt("Lights per Cell", (int*)&m_ui.rtxdiContextParams.ReGIR.LightsPerCell, 1, 32, 8192); - if (m_ui.rtxdiContextParams.ReGIR.Mode == rtxdi::ReGIRMode::Grid) - { - ImGui::DragInt3("Grid Resolution", (int*)&m_ui.rtxdiContextParams.ReGIR.GridSize.x, 1, 1, 64); - } - else if (m_ui.rtxdiContextParams.ReGIR.Mode == rtxdi::ReGIRMode::Onion) - { - ImGui::SliderInt("Onion Layers - Detail", (int*)&m_ui.rtxdiContextParams.ReGIR.OnionDetailLayers, 0, 8); - ImGui::SliderInt("Onion Layers - Coverage", (int*)&m_ui.rtxdiContextParams.ReGIR.OnionCoverageLayers, 0, 20); - } - - ImGui::Text("Total ReGIR Cells: %d", m_ui.regirLightSlotCount / m_ui.rtxdiContextParams.ReGIR.LightsPerCell); + m_ui.restirDIStaticParams.CheckerboardSamplingMode = enableCheckerboardSampling ? rtxdi::CheckerboardMode::Black : rtxdi::CheckerboardMode::Off; ImGui::TreePop(); } - if (ImGui::TreeNode("ReGIR")) + if (ImGui::TreeNode("ReGIR Context")) { - m_ui.resetAccumulation |= ImGui::Checkbox("Use ReGIR", (bool*)&m_ui.lightingSettings.enableReGIR); - m_ui.resetAccumulation |= ImGui::SliderFloat("Cell Size", &m_ui.regirCellSize, 0.1f, 4.f); - m_ui.resetAccumulation |= ImGui::SliderInt("Grid Build Samples", (int*)&m_ui.lightingSettings.numRegirBuildSamples, 0, 32); - m_ui.resetAccumulation |= ImGui::SliderFloat("Sampling Jitter", &m_ui.regirSamplingJitter, 0.0f, 2.f); + if (ImGui::Button("Apply Settings")) + m_ui.resetISContext = true; - ImGui::Checkbox("Freeze Position", &m_ui.freezeRegirPosition); - ImGui::SameLine(0.f, 10.f); - ImGui::Checkbox("Visualize Cells", (bool*)&m_ui.lightingSettings.visualizeRegirCells); + ImGui::DragInt("Lights per Cell", (int*)&m_ui.regirStaticParams.LightsPerCell, 1, 32, 8192); + + static const char* regirShapeOptions[] = { "Grid", "Onion" }; + const char* currentReGIRShapeOption = regirShapeOptions[static_cast(m_ui.regirStaticParams.Mode) - 1]; // Disabled option is skipped + if (ImGui::BeginCombo("ReGIR Mode", currentReGIRShapeOption)) + { + for (int i = 0; i < sizeof(regirShapeOptions) / sizeof(regirShapeOptions[0]); i++) + { + int enumIndex = i + 1; // We skip Disabled here since that's controlled by the ReSTIR DI + ReSTIR GI settings. + bool is_selected = (enumIndex == static_cast(m_ui.regirStaticParams.Mode)); + if (ImGui::Selectable(regirShapeOptions[i], is_selected)) + *(int*)&m_ui.regirStaticParams.Mode = enumIndex; + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + if (m_ui.regirStaticParams.Mode == rtxdi::ReGIRMode::Grid) + { + ImGui::DragInt3("Grid Resolution", (int*)&m_ui.regirStaticParams.gridParameters.GridSize.x, 1, 1, 64); + } + else if (m_ui.regirStaticParams.Mode == rtxdi::ReGIRMode::Onion) + { + ImGui::SliderInt("Onion Layers - Detail", (int*)&m_ui.regirStaticParams.onionParameters.OnionDetailLayers, 0, 8); + ImGui::SliderInt("Onion Layers - Coverage", (int*)&m_ui.regirStaticParams.onionParameters.OnionCoverageLayers, 0, 20); + } + + ImGui::Text("Total ReGIR Cells: %d", m_ui.regirLightSlotCount / m_ui.regirStaticParams.LightsPerCell); ImGui::TreePop(); } @@ -376,7 +416,7 @@ void UserInterface::SamplingSettings() break; case DirectLightingMode::ReStir: ShowHelpMarker( - "Sample the direct lighting using ReSTIR. Optionally, apply indirect diffuse lighting from RTXGI."); + "Sample the direct lighting using ReSTIR."); break; } @@ -393,7 +433,7 @@ void UserInterface::SamplingSettings() if (isUsingReStir) { ImGui::PushItemWidth(180.f); - m_ui.resetAccumulation |= ImGui::Combo("Resampling Mode", (int*)&m_ui.lightingSettings.resamplingMode, + m_ui.resetAccumulation |= ImGui::Combo("Resampling Mode", (int*)&m_ui.restirDI.resamplingMode, "None\0" "Temporal\0" "Spatial\0" @@ -402,32 +442,102 @@ void UserInterface::SamplingSettings() ImGui::PopItemWidth(); ImGui::Separator(); - if (ImGui::TreeNode("Initial Sampling")) + if (ImGui::TreeNode("ReGIR Presampling")) { - samplingSettingsChanged |= ImGui::SliderInt("Initial BRDF Samples", (int*)&m_ui.lightingSettings.numPrimaryBrdfSamples, 0, 8); + ShowHelpMarker("Dynamic ReGIR Settings"); + static const char* regirPresamplingOptions[] = { "Uniform Sampling", "Power RIS" }; + const char* currentPresamplingOption = regirPresamplingOptions[static_cast(m_ui.regirDynamicParameters.presamplingMode)]; + if (ImGui::BeginCombo("ReGIR RIS Presampling Mode", currentPresamplingOption)) + { + for (int i = 0; i < sizeof(regirPresamplingOptions) / sizeof(regirPresamplingOptions[0]); i++) + { + bool is_selected = (i == static_cast(m_ui.regirDynamicParameters.presamplingMode)); + if (ImGui::Selectable(regirPresamplingOptions[i], is_selected)) + *(int*)&m_ui.regirDynamicParameters.presamplingMode = i; + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } ShowHelpMarker( - "Number of rays traced from the surface using BRDF importance sampling to find mesh lights or environment map samples. Helps glossy reflections."); + "Presampling method the ReGIR algorithm uses to select lights"); + m_ui.resetAccumulation |= ImGui::SliderFloat("Cell Size", &m_ui.regirDynamicParameters.regirCellSize, 0.1f, 4.f); + m_ui.resetAccumulation |= ImGui::SliderInt("Grid Build Samples", (int*)&m_ui.regirDynamicParameters.regirNumBuildSamples, 0, 32); + m_ui.resetAccumulation |= ImGui::SliderFloat("Sampling Jitter", &m_ui.regirDynamicParameters.regirSamplingJitter, 0.0f, 2.f); - samplingSettingsChanged |= ImGui::SliderInt("Initial ReGIR Samples", (int*)&m_ui.lightingSettings.numPrimaryRegirSamples, 0, 32); - ShowHelpMarker( - "Number of samples drawn from the local ReGIR cell, if it's available."); + ImGui::Checkbox("Freeze Position", &m_ui.freezeRegirPosition); + ImGui::SameLine(0.f, 10.f); + ImGui::Checkbox("Visualize Cells", (bool*)&m_ui.lightingSettings.visualizeRegirCells); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Initial Sampling")) + { + ImGui::SetNextTreeNodeOpen(true); + if(ImGui::TreeNode("Local Light Sampling")) + { + int* initSamplingMode = (int*)&m_ui.restirDI.initialSamplingParams.localLightSamplingMode; + samplingSettingsChanged |= ImGui::RadioButton("Local Light Uniform Sampling", initSamplingMode, 0); + ShowHelpMarker("Sample local lights uniformly"); + + samplingSettingsChanged |= ImGui::SliderInt("Local Light Uniform Samples", (int*)&m_ui.restirDI.numLocalLightUniformSamples, 0, 32); + ShowHelpMarker( + "Number of samples drawn uniformly from the local light pool."); + + samplingSettingsChanged |= ImGui::RadioButton("Local Light Power RIS", initSamplingMode, 1); + ShowHelpMarker("Sample local lights using power-based RIS"); + + samplingSettingsChanged |= ImGui::SliderInt("Local Light Power RIS Samples", (int*)&m_ui.restirDI.numLocalLightPowerRISSamples, 0, 32); + ShowHelpMarker( + "Number of samples drawn from the local lights power-based RIS buffer."); + + samplingSettingsChanged |= ImGui::RadioButton("Local Light ReGIR RIS", initSamplingMode, 2); + ShowHelpMarker("Sample local lights using ReGIR-based RIS"); + + samplingSettingsChanged |= ImGui::SliderInt("Local Light ReGIR RIS Samples", (int*)&m_ui.restirDI.numLocalLightReGIRRISSamples, 0, 32); + ShowHelpMarker( + "Number of samples drawn from the local lights ReGIR-based RIS buffer"); + + static const char* regirFallbackOptions[] = { "Uniform Sampling", "Power RIS" }; + const char* currentFallbackOption = regirFallbackOptions[static_cast(m_ui.regirDynamicParameters.fallbackSamplingMode)]; + if (ImGui::BeginCombo("ReGIR RIS Fallback Sampling Mode", currentFallbackOption)) + { + for (int i = 0; i < sizeof(regirFallbackOptions) / sizeof(regirFallbackOptions[0]); i++) + { + bool is_selected = (i == static_cast(m_ui.regirDynamicParameters.fallbackSamplingMode)); + if (ImGui::Selectable(regirFallbackOptions[i], is_selected)) + *(int*)&m_ui.regirDynamicParameters.fallbackSamplingMode = i; + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + ShowHelpMarker( + "Sampling method to fall back to for surfaces outside the ReGIR volume"); + + m_ui.resetAccumulation |= samplingSettingsChanged; + + ImGui::TreePop(); + } + - samplingSettingsChanged |= ImGui::SliderInt("Initial Local Light Samples", (int*)&m_ui.lightingSettings.numPrimaryLocalLightSamples, 0, 32); + samplingSettingsChanged |= ImGui::SliderInt("Initial BRDF Samples", (int*)&m_ui.restirDI.initialSamplingParams.numPrimaryBrdfSamples, 0, 8); ShowHelpMarker( - "Number of samples drawn from the local light pool when or where ReGIR is not available."); + "Number of rays traced from the surface using BRDF importance sampling to find mesh lights or environment map samples. Helps glossy reflections."); - samplingSettingsChanged |= ImGui::SliderInt("Initial Infinite Light Samples", (int*)&m_ui.lightingSettings.numPrimaryInfiniteLightSamples, 0, 32); + samplingSettingsChanged |= ImGui::SliderInt("Initial Infinite Light Samples", (int*)&m_ui.restirDI.initialSamplingParams.numPrimaryInfiniteLightSamples, 0, 32); ShowHelpMarker( "Number of samples drawn from the infinite light pool, i.e. the sun light when using " "the procedural environment, and the environment map when it's not importance sampled."); - samplingSettingsChanged |= ImGui::SliderInt("Initial Environment Samples", (int*)&m_ui.lightingSettings.numPrimaryEnvironmentSamples, 0, 32); + samplingSettingsChanged |= ImGui::SliderInt("Initial Environment Samples", (int*)&m_ui.restirDI.initialSamplingParams.numPrimaryEnvironmentSamples, 0, 32); ShowHelpMarker( "Number of samples drawn from the environment map when it is importance sampled."); - samplingSettingsChanged |= ImGui::Checkbox("Enable Initial Visibility", (bool*)&m_ui.lightingSettings.enableInitialVisibility); + samplingSettingsChanged |= ImGui::Checkbox("Enable Initial Visibility", (bool*)&m_ui.restirDI.initialSamplingParams.enableInitialVisibility); - samplingSettingsChanged |= ImGui::SliderFloat("BRDF Sample Cutoff", (float*)&m_ui.lightingSettings.brdfCutoff, 0.0f, 0.1f); + samplingSettingsChanged |= ImGui::SliderFloat("BRDF Sample Cutoff", (float*)&m_ui.restirDI.initialSamplingParams.brdfCutoff, 0.0f, 0.1f); ShowHelpMarker( "Determine how much to shorten BRDF rays. 0 to disable shortening"); @@ -441,13 +551,13 @@ void UserInterface::SamplingSettings() "Use the previous frame TLAS for bias correction rays during temporal resampling and gradient computation. " "Resutls in less biased results under motion and brighter, more complete gradients."); - samplingSettingsChanged |= ImGui::Checkbox("Enable Permutation Sampling", (bool*)&m_ui.lightingSettings.enablePermutationSampling); + samplingSettingsChanged |= ImGui::Checkbox("Enable Permutation Sampling", (bool*)&m_ui.restirDI.temporalResamplingParams.enablePermutationSampling); ShowHelpMarker( "Shuffle the pixels from the previous frame when resampling from them. This makes pixel colors less correllated " "temporally and therefore better suited for temporal accumulation and denoising. Also results in a higher positive " "bias when the Reuse Final Visibility setting is on, which somewhat counteracts the negative bias from spatial resampling."); - samplingSettingsChanged |= ImGui::Combo("Temporal Bias Correction", (int*)&m_ui.lightingSettings.temporalBiasCorrection, "Off\0Basic\0Pairwise\0Ray Traced\0"); + samplingSettingsChanged |= ImGui::Combo("Temporal Bias Correction", (int*)&m_ui.restirDI.temporalResamplingParams.temporalBiasCorrection, "Off\0Basic\0Pairwise\0Ray Traced\0"); ShowHelpMarker( "Off = use the 1/M normalization.\n" "Basic = use the MIS normalization but assume that every sample is visible.\n" @@ -456,21 +566,21 @@ void UserInterface::SamplingSettings() if (m_showAdvancedSamplingSettings) { - samplingSettingsChanged |= ImGui::SliderFloat("Temporal Depth Threshold", &m_ui.lightingSettings.temporalDepthThreshold, 0.f, 1.f); + samplingSettingsChanged |= ImGui::SliderFloat("Temporal Depth Threshold", &m_ui.restirDI.temporalResamplingParams.temporalDepthThreshold, 0.f, 1.f); ShowHelpMarker("Higher values result in accepting temporal samples with depths more different from the current pixel."); - samplingSettingsChanged |= ImGui::SliderFloat("Temporal Normal Threshold", &m_ui.lightingSettings.temporalNormalThreshold, 0.f, 1.f); + samplingSettingsChanged |= ImGui::SliderFloat("Temporal Normal Threshold", &m_ui.restirDI.temporalResamplingParams.temporalNormalThreshold, 0.f, 1.f); ShowHelpMarker("Lower values result in accepting temporal samples with normals more different from the current pixel."); - ImGui::SliderFloat("Permutation Sampling Threshold", &m_ui.lightingSettings.permutationSamplingThreshold, 0.8f, 1.f); + ImGui::SliderFloat("Permutation Sampling Threshold", &m_ui.restirDI.temporalResamplingParams.permutationSamplingThreshold, 0.8f, 1.f); ShowHelpMarker("Higher values result in disabling permutation sampling on less complex surfaces."); } - samplingSettingsChanged |= ImGui::SliderInt("Max History Length", (int*)&m_ui.lightingSettings.maxHistoryLength, 1, 100); + samplingSettingsChanged |= ImGui::SliderInt("Max History Length", (int*)&m_ui.restirDI.temporalResamplingParams.maxHistoryLength, 1, 100); - samplingSettingsChanged |= ImGui::Checkbox("##enableBoilingFilter", (bool*)&m_ui.lightingSettings.enableBoilingFilter); + samplingSettingsChanged |= ImGui::Checkbox("##enableBoilingFilter", (bool*)&m_ui.restirDI.temporalResamplingParams.enableBoilingFilter); ImGui::SameLine(); ImGui::PushItemWidth(69.f); - samplingSettingsChanged |= ImGui::SliderFloat("Boiling Filter", &m_ui.lightingSettings.boilingFilterStrength, 0.f, 1.f); + samplingSettingsChanged |= ImGui::SliderFloat("Boiling Filter", &m_ui.restirDI.temporalResamplingParams.boilingFilterStrength, 0.f, 1.f); ImGui::PopItemWidth(); ShowHelpMarker( "The boiling filter analyzes the neighborhood of each pixel and discards the pixel's reservoir " @@ -481,33 +591,42 @@ void UserInterface::SamplingSettings() if (ImGui::TreeNode("Spatial Resampling")) { - if (m_ui.lightingSettings.resamplingMode != ResamplingMode::FusedSpatiotemporal) + if (m_ui.restirDI.resamplingMode != rtxdi::ReSTIRDI_ResamplingMode::FusedSpatiotemporal) { - samplingSettingsChanged |= ImGui::Combo("Spatial Bias Correction", (int*)&m_ui.lightingSettings.spatialBiasCorrection, "Off\0Basic\0Pairwise\0Ray Traced\0"); + samplingSettingsChanged |= ImGui::Combo("Spatial Bias Correction", (int*)&m_ui.restirDI.spatialResamplingParams.spatialBiasCorrection, "Off\0Basic\0Pairwise\0Ray Traced\0"); ShowHelpMarker( "Off = use the 1/M normalization.\n" "Basic = use the MIS normalization but assume that every sample is visible.\n" "Pairwise = pairwise MIS improves perf and specular quality (assumes every sample is visible).\n" "Ray Traced = use the MIS normalization and verify visibility."); } - samplingSettingsChanged |= ImGui::SliderInt("Spatial Samples", (int*)&m_ui.lightingSettings.numSpatialSamples, 1, 32); + samplingSettingsChanged |= ImGui::SliderInt("Spatial Samples", (int*)&m_ui.restirDI.spatialResamplingParams.numSpatialSamples, 1, 32); - if (m_ui.lightingSettings.resamplingMode == ResamplingMode::TemporalAndSpatial || m_ui.lightingSettings.resamplingMode == ResamplingMode::FusedSpatiotemporal) + if (m_ui.restirDI.resamplingMode == rtxdi::ReSTIRDI_ResamplingMode::TemporalAndSpatial || m_ui.restirDI.resamplingMode == rtxdi::ReSTIRDI_ResamplingMode::FusedSpatiotemporal) { - samplingSettingsChanged |= ImGui::SliderInt("Disocclusion Boost Samples", (int*)&m_ui.lightingSettings.numDisocclusionBoostSamples, 1, 32); + samplingSettingsChanged |= ImGui::SliderInt("Disocclusion Boost Samples", (int*)&m_ui.restirDI.spatialResamplingParams.numDisocclusionBoostSamples, 1, 32); ShowHelpMarker( "The number of spatial samples to take on surfaces which don't have sufficient accumulated history length. " "More samples result in faster convergence in disoccluded regions but increase processing time."); } - samplingSettingsChanged |= ImGui::SliderFloat("Spatial Sampling Radius", &m_ui.lightingSettings.spatialSamplingRadius, 1.f, 32.f); - if (m_showAdvancedSamplingSettings && m_ui.lightingSettings.resamplingMode != ResamplingMode::FusedSpatiotemporal) + samplingSettingsChanged |= ImGui::SliderFloat("Spatial Sampling Radius", &m_ui.restirDI.spatialResamplingParams.spatialSamplingRadius, 1.f, 32.f); + if (m_showAdvancedSamplingSettings && m_ui.restirDI.resamplingMode != rtxdi::ReSTIRDI_ResamplingMode::FusedSpatiotemporal) { - samplingSettingsChanged |= ImGui::SliderFloat("Spatial Depth Threshold", &m_ui.lightingSettings.spatialDepthThreshold, 0.f, 1.f); + samplingSettingsChanged |= ImGui::SliderFloat("Spatial Depth Threshold", &m_ui.restirDI.spatialResamplingParams.spatialDepthThreshold, 0.f, 1.f); ShowHelpMarker("Higher values result in accepting samples with depths more different from the center pixel."); - samplingSettingsChanged |= ImGui::SliderFloat("Spatial Normal Threshold", &m_ui.lightingSettings.spatialNormalThreshold, 0.f, 1.f); + samplingSettingsChanged |= ImGui::SliderFloat("Spatial Normal Threshold", &m_ui.restirDI.spatialResamplingParams.spatialNormalThreshold, 0.f, 1.f); ShowHelpMarker("Lower values result in accepting samples with normals more different from the center pixel."); + + samplingSettingsChanged |= ImGui::Checkbox("Discount Naive Samples", reinterpret_cast(&m_ui.restirDI.spatialResamplingParams.discountNaiveSamples)); + ShowHelpMarker("Prevents samples which are from the current frame or have no reasonable temporal history merged being spread to neighbors."); + } + + if (m_showAdvancedSamplingSettings && m_ui.restirDI.resamplingMode != rtxdi::ReSTIRDI_ResamplingMode::Temporal) + { + samplingSettingsChanged |= ImGui::Checkbox("Discount Naive Samples", (bool*)&m_ui.restirDI.spatialResamplingParams.discountNaiveSamples); + ShowHelpMarker("Prevents samples which are from the current frame or have no reasonable temporal history merged being spread to neighbors."); } ImGui::TreePop(); @@ -515,23 +634,23 @@ void UserInterface::SamplingSettings() if (ImGui::TreeNode("Final Shading")) { - samplingSettingsChanged |= ImGui::Checkbox("Enable Final Visibility", (bool*)&m_ui.lightingSettings.enableFinalVisibility); + samplingSettingsChanged |= ImGui::Checkbox("Enable Final Visibility", (bool*)&m_ui.restirDI.shadingParams.enableFinalVisibility); - samplingSettingsChanged |= ImGui::Checkbox("Discard Invisible Samples", (bool*)&m_ui.lightingSettings.discardInvisibleSamples); + samplingSettingsChanged |= ImGui::Checkbox("Discard Invisible Samples", (bool*)&m_ui.restirDI.temporalResamplingParams.discardInvisibleSamples); ShowHelpMarker( "When a sample is determined to be occluded during final shading, its reservoir is discarded. " "This can significantly reduce noise, but also introduce some bias near shadow boundaries beacuse the reservoirs' M values are kept. " "Also, enabling this option speeds up temporal resampling with Ray Traced bias correction by skipping most of the bias correction rays."); - samplingSettingsChanged |= ImGui::Checkbox("Reuse Final Visibility", (bool*)&m_ui.lightingSettings.reuseFinalVisibility); + samplingSettingsChanged |= ImGui::Checkbox("Reuse Final Visibility", (bool*)&m_ui.restirDI.shadingParams.reuseFinalVisibility); ShowHelpMarker( "Store the fractional final visibility term in the reservoirs and reuse it later if the reservoir is not too old and has not " "moved too far away from its original location. Enable the Advanced Settings option to control the thresholds."); - if (m_ui.lightingSettings.reuseFinalVisibility && m_showAdvancedSamplingSettings) + if (m_ui.restirDI.shadingParams.reuseFinalVisibility && m_showAdvancedSamplingSettings) { - samplingSettingsChanged |= ImGui::SliderFloat("Final Visibility - Max Distance", &m_ui.lightingSettings.finalVisibilityMaxDistance, 0.f, 32.f); - samplingSettingsChanged |= ImGui::SliderInt("Final Visibility - Max Age", (int*)&m_ui.lightingSettings.finalVisibilityMaxAge, 0, 16); + samplingSettingsChanged |= ImGui::SliderFloat("Final Visibility - Max Distance", &m_ui.restirDI.shadingParams.finalVisibilityMaxDistance, 0.f, 32.f); + samplingSettingsChanged |= ImGui::SliderInt("Final Visibility - Max Age", (int*)&m_ui.restirDI.shadingParams.finalVisibilityMaxAge, 0, 16); } ImGui::TreePop(); @@ -560,42 +679,43 @@ void UserInterface::SamplingSettings() case IndirectLightingMode::Brdf: ShowHelpMarker( "Trace BRDF rays from primary surfaces. " - "Shade the surfaces found with BRDF rays using direct light sampling and optionally RTXGI."); + "Shade the surfaces found with BRDF rays using direct light sampling."); break; case IndirectLightingMode::ReStirGI: ShowHelpMarker( "Trace diffuse and specular BRDF rays and resample results with ReSTIR GI. " - "Shade the surfaces found with BRDF rays using direct light sampling and optionally RTXGI."); + "Shade the surfaces found with BRDF rays using direct light sampling."); break; default:; } bool isUsingIndirect = m_ui.indirectLightingMode != IndirectLightingMode::None; - m_ui.resetAccumulation |= ImGui::SliderFloat("Min Secondary Roughness", &m_ui.lightingSettings.minSecondaryRoughness, 0.f, 1.f); + m_ui.resetAccumulation |= ImGui::SliderFloat("Min Secondary Roughness", &m_ui.lightingSettings.brdfptParams.materialOverrideParams.minSecondaryRoughness, 0.f, 1.f); if (isUsingIndirect && ImGui::TreeNode("Secondary Surface Light Sampling")) { - m_ui.resetAccumulation |= ImGui::SliderInt("Indirect ReGIR Samples", (int*)&m_ui.lightingSettings.numIndirectRegirSamples, 0, 32); - m_ui.resetAccumulation |= ImGui::SliderInt("Indirect Local Light Samples", (int*)&m_ui.lightingSettings.numIndirectLocalLightSamples, 0, 32); - m_ui.resetAccumulation |= ImGui::SliderInt("Indirect Inifinite Light Samples", (int*)&m_ui.lightingSettings.numIndirectInfiniteLightSamples, 0, 32); - m_ui.resetAccumulation |= ImGui::SliderInt("Indirect Environment Samples", (int*)&m_ui.lightingSettings.numIndirectEnvironmentSamples, 0, 32); + // TODO: Determine whether to have choice of sampling mode here and in ReSTIR DI. + // Should probably have a single numLocalLightSamples in the struct and have the UI keep track of the 3 different values for each mode + m_ui.resetAccumulation |= ImGui::SliderInt("Indirect Local Light Samples", (int*)&m_ui.lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.initialSamplingParams.numPrimaryLocalLightSamples, 0, 32); + m_ui.resetAccumulation |= ImGui::SliderInt("Indirect Inifinite Light Samples", (int*)&m_ui.lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.initialSamplingParams.numPrimaryInfiniteLightSamples, 0, 32); + m_ui.resetAccumulation |= ImGui::SliderInt("Indirect Environment Samples", (int*)&m_ui.lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.initialSamplingParams.numPrimaryEnvironmentSamples, 0, 32); ImGui::TreePop(); } if (isUsingIndirect && m_ui.directLightingMode == DirectLightingMode::ReStir && ImGui::TreeNode("Reuse Primary Samples")) { - m_ui.resetAccumulation |= ImGui::Checkbox("Reuse RTXDI samples for secondary surf.", (bool*)&m_ui.lightingSettings.enableSecondaryResampling); + m_ui.resetAccumulation |= ImGui::Checkbox("Reuse RTXDI samples for secondary surface", (bool*)&m_ui.lightingSettings.brdfptParams.enableSecondaryResampling); ShowHelpMarker( "When shading a secondary surface, try to find a matching surface in screen space and reuse its light reservoir. " "This feature uses the Spatial Resampling function and has similar controls."); - m_ui.resetAccumulation |= ImGui::Combo("Secondary Bias Correction", (int*)&m_ui.lightingSettings.secondaryBiasCorrection, "Off\0Basic\0Pairwise\0Ray Traced\0"); - m_ui.resetAccumulation |= ImGui::SliderInt("Secondary Samples", (int*)&m_ui.lightingSettings.numSecondarySamples, 1, 4); - m_ui.resetAccumulation |= ImGui::SliderFloat("Secondary Sampling Radius", &m_ui.lightingSettings.secondarySamplingRadius, 0.f, 32.f); - m_ui.resetAccumulation |= ImGui::SliderFloat("Secondary Depth Threshold", &m_ui.lightingSettings.secondaryDepthThreshold, 0.f, 1.f); - m_ui.resetAccumulation |= ImGui::SliderFloat("Secondary Normal Threshold", &m_ui.lightingSettings.secondaryNormalThreshold, 0.f, 1.f); + m_ui.resetAccumulation |= ImGui::Combo("Secondary Bias Correction", (int*)&m_ui.lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialBiasCorrection, "Off\0Basic\0Pairwise\0Ray Traced\0"); + m_ui.resetAccumulation |= ImGui::SliderInt("Secondary Samples", (int*)&m_ui.lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.numSpatialSamples, 1, 4); + m_ui.resetAccumulation |= ImGui::SliderFloat("Secondary Sampling Radius", &m_ui.lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialSamplingRadius, 0.f, 32.f); + m_ui.resetAccumulation |= ImGui::SliderFloat("Secondary Depth Threshold", &m_ui.lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialDepthThreshold, 0.f, 1.f); + m_ui.resetAccumulation |= ImGui::SliderFloat("Secondary Normal Threshold", &m_ui.lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.spatialResamplingParams.spatialNormalThreshold, 0.f, 1.f); ImGui::TreePop(); } @@ -603,7 +723,7 @@ void UserInterface::SamplingSettings() if (m_ui.indirectLightingMode == IndirectLightingMode::ReStirGI) { ImGui::PushItemWidth(180.f); - m_ui.resetAccumulation |= ImGui::Combo("Resampling Mode", (int*)&m_ui.lightingSettings.reStirGI.resamplingMode, + m_ui.resetAccumulation |= ImGui::Combo("Resampling Mode", (int*)&m_ui.restirGI.resamplingMode, "None\0" "Temporal\0" "Spatial\0" @@ -612,123 +732,103 @@ void UserInterface::SamplingSettings() ImGui::PopItemWidth(); ImGui::Separator(); - if (m_ui.lightingSettings.reStirGI.resamplingMode == ResamplingMode::Temporal || - m_ui.lightingSettings.reStirGI.resamplingMode == ResamplingMode::TemporalAndSpatial || - m_ui.lightingSettings.reStirGI.resamplingMode == ResamplingMode::FusedSpatiotemporal) + if ((m_ui.restirGI.resamplingMode == rtxdi::ReSTIRGI_ResamplingMode::Temporal || + m_ui.restirGI.resamplingMode == rtxdi::ReSTIRGI_ResamplingMode::TemporalAndSpatial || + m_ui.restirGI.resamplingMode == rtxdi::ReSTIRGI_ResamplingMode::FusedSpatiotemporal) && + ImGui::TreeNode("Temporal Resampling")) { - m_ui.resetAccumulation |= ImGui::SliderFloat("Depth Threshold", &m_ui.lightingSettings.reStirGI.depthThreshold, 0.001f, 1.f); - m_ui.resetAccumulation |= ImGui::SliderFloat("Normal Threshold", &m_ui.lightingSettings.reStirGI.normalThreshold, 0.001f, 1.f); + m_ui.resetAccumulation |= ImGui::SliderFloat("Temporal Depth Threshold", &m_ui.restirGI.temporalResamplingParams.depthThreshold, 0.001f, 1.f); + m_ui.resetAccumulation |= ImGui::SliderFloat("Temporal Normal Threshold", &m_ui.restirGI.temporalResamplingParams.normalThreshold, 0.001f, 1.f); - m_ui.resetAccumulation |= ImGui::SliderInt("Max reservoir age", (int*)&m_ui.lightingSettings.reStirGI.maxReservoirAge, 1, 100); - m_ui.resetAccumulation |= ImGui::SliderInt("Max history length", (int*)&m_ui.lightingSettings.reStirGI.maxHistoryLength, 1, 100); - m_ui.resetAccumulation |= ImGui::Checkbox("Enable permutation sampling", (bool*)&m_ui.lightingSettings.reStirGI.enablePermutationSampling); - m_ui.resetAccumulation |= ImGui::Checkbox("Enable fallback sampling", (bool*)&m_ui.lightingSettings.reStirGI.enableFallbackSampling); + m_ui.resetAccumulation |= ImGui::SliderInt("Max reservoir age", (int*)&m_ui.restirGI.temporalResamplingParams.maxReservoirAge, 1, 100); + m_ui.resetAccumulation |= ImGui::SliderInt("Max history length", (int*)&m_ui.restirGI.temporalResamplingParams.maxHistoryLength, 1, 100); + m_ui.resetAccumulation |= ImGui::Checkbox("Enable permutation sampling", (bool*)&m_ui.restirGI.temporalResamplingParams.enablePermutationSampling); + m_ui.resetAccumulation |= ImGui::Checkbox("Enable fallback sampling", (bool*)&m_ui.restirGI.temporalResamplingParams.enableFallbackSampling); - const char* biasCorrectionText = (m_ui.lightingSettings.reStirGI.resamplingMode == ResamplingMode::FusedSpatiotemporal) + const char* biasCorrectionText = (m_ui.restirGI.resamplingMode == rtxdi::ReSTIRGI_ResamplingMode::FusedSpatiotemporal) ? "Fused bias correction" : "Temporal bias correction"; - m_ui.resetAccumulation |= ImGui::Combo(biasCorrectionText, (int*)&m_ui.lightingSettings.reStirGI.temporalBiasCorrection, - "Off\0" - "Basic MIS\0" - "RayTraced\0"); + static const char* temporalResamplingOptions[] = { "Off", "Basic MIS", "RayTraced" }; + static const std::map mode2index = { {ResTIRGI_TemporalBiasCorrectionMode::Off, 0}, + {ResTIRGI_TemporalBiasCorrectionMode::Basic, 1}, + {ResTIRGI_TemporalBiasCorrectionMode::Raytraced, 2} }; + static const std::array index2mode = { ResTIRGI_TemporalBiasCorrectionMode::Off, + ResTIRGI_TemporalBiasCorrectionMode::Basic, + ResTIRGI_TemporalBiasCorrectionMode::Raytraced }; + const const char* currentTemporalResamplingModeOption = temporalResamplingOptions[mode2index.at(m_ui.restirGI.temporalResamplingParams.temporalBiasCorrectionMode)]; + if(ImGui::BeginCombo(biasCorrectionText, currentTemporalResamplingModeOption)) + { + for (int i = 0; i < sizeof(temporalResamplingOptions) / sizeof(temporalResamplingOptions[0]); i++) + { + bool is_selected = (index2mode.at(i) == m_ui.restirGI.temporalResamplingParams.temporalBiasCorrectionMode); + if (ImGui::Selectable(temporalResamplingOptions[i], is_selected)) + { + m_ui.resetAccumulation |= true; + m_ui.restirGI.temporalResamplingParams.temporalBiasCorrectionMode = index2mode.at(i); + } + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } - m_ui.resetAccumulation |= ImGui::Checkbox("##enableGIBoilingFilter", (bool*)&m_ui.lightingSettings.reStirGI.enableBoilingFilter); + m_ui.resetAccumulation |= ImGui::Checkbox("##enableGIBoilingFilter", (bool*)&m_ui.restirGI.temporalResamplingParams.enableBoilingFilter); ImGui::SameLine(); ImGui::PushItemWidth(69.f); - m_ui.resetAccumulation |= ImGui::SliderFloat("Boiling Filter##GIBoilingFilter", &m_ui.lightingSettings.reStirGI.boilingFilterStrength, 0.f, 1.f); + m_ui.resetAccumulation |= ImGui::SliderFloat("Boiling Filter##GIBoilingFilter", &m_ui.restirGI.temporalResamplingParams.boilingFilterStrength, 0.f, 1.f); ImGui::PopItemWidth(); - ImGui::Separator(); + ImGui::TreePop(); } - if (m_ui.lightingSettings.reStirGI.resamplingMode == ResamplingMode::Spatial || - m_ui.lightingSettings.reStirGI.resamplingMode == ResamplingMode::TemporalAndSpatial || - m_ui.lightingSettings.reStirGI.resamplingMode == ResamplingMode::FusedSpatiotemporal) + if ((m_ui.restirGI.resamplingMode == rtxdi::ReSTIRGI_ResamplingMode::Spatial || + m_ui.restirGI.resamplingMode == rtxdi::ReSTIRGI_ResamplingMode::TemporalAndSpatial || + m_ui.restirGI.resamplingMode == rtxdi::ReSTIRGI_ResamplingMode::FusedSpatiotemporal) && + ImGui::TreeNode("Spatial Resampling")) { - m_ui.resetAccumulation |= ImGui::SliderInt("Num spatial samples", (int*)&m_ui.lightingSettings.reStirGI.numSpatialSamples, 1, 7); - m_ui.resetAccumulation |= ImGui::SliderFloat("Sampling Radius", (float*)&m_ui.lightingSettings.reStirGI.samplingRadius, 0.01f, 60.0f); + m_ui.resetAccumulation |= ImGui::SliderInt("Num spatial samples", (int*)&m_ui.restirGI.spatialResamplingParams.numSpatialSamples, 1, 7); + m_ui.resetAccumulation |= ImGui::SliderFloat("Sampling Radius", (float*)&m_ui.restirGI.spatialResamplingParams.spatialSamplingRadius, 0.01f, 60.0f); + m_ui.resetAccumulation |= ImGui::SliderFloat("Spatial Depth Threshold", &m_ui.restirGI.spatialResamplingParams.spatialDepthThreshold, 0.001f, 1.f); + m_ui.resetAccumulation |= ImGui::SliderFloat("Spatial Normal Threshold", &m_ui.restirGI.spatialResamplingParams.spatialNormalThreshold, 0.001f, 1.f); - if (m_ui.lightingSettings.reStirGI.resamplingMode != ResamplingMode::FusedSpatiotemporal) + if (m_ui.restirGI.resamplingMode != rtxdi::ReSTIRGI_ResamplingMode::FusedSpatiotemporal) { - m_ui.resetAccumulation |= ImGui::Combo("Spatial bias correction", (int*)&m_ui.lightingSettings.reStirGI.spatialBiasCorrection, - "Off\0" - "Basic MIS\0" - "RayTraced\0"); + static const char* spatialResamplingOptions[] = { "Off", "Basic MIS", "RayTraced" }; + static const std::map mode2index = { {ResTIRGI_SpatialBiasCorrectionMode::Off, 0}, + {ResTIRGI_SpatialBiasCorrectionMode::Basic, 1}, + {ResTIRGI_SpatialBiasCorrectionMode::Raytraced, 2} }; + static const std::array index2mode = {ResTIRGI_SpatialBiasCorrectionMode::Off, + ResTIRGI_SpatialBiasCorrectionMode::Basic, + ResTIRGI_SpatialBiasCorrectionMode::Raytraced }; + const char* currentSpatialResamplingModeOption = spatialResamplingOptions[mode2index.at(m_ui.restirGI.spatialResamplingParams.spatialBiasCorrectionMode)]; + if(ImGui::BeginCombo("Spatial Bias Correction Mode", currentSpatialResamplingModeOption)) + { + for (int i = 0; i < sizeof(spatialResamplingOptions) / sizeof(spatialResamplingOptions[0]); i++) + { + bool is_selected = (index2mode.at(i) == m_ui.restirGI.spatialResamplingParams.spatialBiasCorrectionMode); + if (ImGui::Selectable(spatialResamplingOptions[i], is_selected)) + { + m_ui.resetAccumulation |= true; + m_ui.restirGI.spatialResamplingParams.spatialBiasCorrectionMode = index2mode.at(i); + } + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } } - ImGui::Separator(); + ImGui::TreePop(); } - m_ui.resetAccumulation |= ImGui::Checkbox("Final visibility", (bool*)&m_ui.lightingSettings.reStirGI.enableFinalVisibility); - m_ui.resetAccumulation |= ImGui::Checkbox("Final MIS", (bool*)&m_ui.lightingSettings.reStirGI.enableFinalMIS); + m_ui.resetAccumulation |= ImGui::Checkbox("Final visibility", (bool*)&m_ui.restirGI.finalShadingParams.enableFinalVisibility); + m_ui.resetAccumulation |= ImGui::Checkbox("Final MIS", (bool*)&m_ui.restirGI.finalShadingParams.enableFinalMIS); } ImGui::TreePop(); } ImGui::Separator(); - -#if WITH_RTXGI - if (ImGui_ColoredTreeNode("RTXGI", c_ColorRegularHeader)) - { - ImGui::Checkbox("Enable", (bool*)&m_ui.rtxgi.enabled); - - ImGui::SliderFloat("Hysteresis", &m_ui.rtxgi.hysteresis, 0.9f, 1.0f); - ImGui::SliderFloat("Irradiance Threshold", &m_ui.rtxgi.irradianceThreshold, 0.f, 1.0f); - ImGui::SliderFloat("Brightness Threshold", &m_ui.rtxgi.brightnessThreshold, 0.f, 1.0f); - ImGui::SliderFloat("Min Front Face Distance / Spacing", &m_ui.rtxgi.minFrontFaceDistanceFraction, 0.f, 0.5f); - ImGui::Checkbox("Probe Relocation", &m_ui.rtxgi.probeRelocation); - ImGui::SameLine(); - if (ImGui::Button("Reset")) - m_ui.rtxgi.resetRelocation = true; - ImGui::Checkbox("Probe Classification", &m_ui.rtxgi.probeClassification); - ImGui::Separator(); - - ImGui::SliderInt("ReGIR Samples", (int*)&m_ui.lightingSettings.numRtxgiRegirSamples, 0, 32); - ImGui::SliderInt("Local Light Samples", (int*)&m_ui.lightingSettings.numRtxgiLocalLightSamples, 0, 32); - ImGui::SliderInt("Infinite Light Samples", (int*)&m_ui.lightingSettings.numRtxgiInfiniteLightSamples, 0, 32); - ImGui::SliderInt("Environment Samples", (int*)&m_ui.lightingSettings.numRtxgiEnvironmentSamples, 0, 32); - ImGui::Separator(); - - auto& volumes = m_ui.resources->scene->GetRtxgiVolumes(); - auto* selectedVolume = m_ui.rtxgi.selectedVolumeIndex >= 0 && m_ui.rtxgi.selectedVolumeIndex < int(volumes.size()) - ? &volumes[m_ui.rtxgi.selectedVolumeIndex] : nullptr; - - if (ImGui::BeginCombo("Volume", selectedVolume ? selectedVolume->name.c_str() : "(None)")) - { - for (int index = 0; index < int(volumes.size()); index++) - { - bool selected = m_ui.rtxgi.selectedVolumeIndex == index; - ImGui::Selectable(volumes[index].name.c_str(), &selected); - if (selected) - { - ImGui::SetItemDefaultFocus(); - m_ui.rtxgi.selectedVolumeIndex = index; - selectedVolume = &volumes[index]; - } - } - ImGui::EndCombo(); - } - ImGui::SameLine(); - ImGui::Checkbox("Show Probes", (bool*)&m_ui.rtxgi.showProbes); - - if (selectedVolume) - { - ImGui::SliderFloat("Probe Spacing", &selectedVolume->probeSpacing, 0.1f, 4.0f); - if (!selectedVolume->scrolling) - { - ImGui::PushItemWidth(200.f); - ImGui::DragFloat3("Origin", &selectedVolume->origin.x, 0.01f); - ImGui::DragFloat3("Rotation", &selectedVolume->eulerAngles.x, 1.f, -180.f, 180.f); - ImGui::PopItemWidth(); - } - } - - ImGui::TreePop(); - } - - ImGui::Separator(); -#endif } void UserInterface::PostProcessSettings() diff --git a/src/UserInterface.h b/src/UserInterface.h index 65e6cde..e553864 100644 --- a/src/UserInterface.h +++ b/src/UserInterface.h @@ -10,7 +10,10 @@ #pragma once -#include +#include +#include +#include +#include #include #include @@ -60,20 +63,6 @@ enum class QualityPreset : uint32_t Reference = 5 }; -struct RtxgiParameters -{ - ibool enabled = true; - ibool showProbes = false; - int selectedVolumeIndex = 0; - float hysteresis = 0.99f; - float irradianceThreshold = 0.25f; - float brightnessThreshold = 0.1f; - float minFrontFaceDistanceFraction = 0.1f; - bool probeRelocation = true; - bool probeClassification = true; - bool resetRelocation = false; -}; - enum class AntiAliasingMode : uint32_t { None, @@ -152,12 +141,9 @@ struct UIData int environmentMapDirty = 0; // 1 -> needs to be rendered; 2 -> passes/textures need to be created int environmentMapIndex = -1; bool environmentMapImportanceSampling = true; - bool enableLocalLightImportanceSampling = true; float environmentIntensityBias = 0.f; float environmentRotation = 0.f; - RtxgiParameters rtxgi; - bool enableDenoiser = true; #ifdef WITH_NRD bool usePrePass = false; @@ -182,12 +168,13 @@ struct UIData bool enableFpsLimit = false; uint32_t fpsLimit = 60; - rtxdi::ContextParameters rtxdiContextParams; - bool resetRtxdiContext = false; + rtxdi::ReSTIRDIStaticParameters restirDIStaticParams; + rtxdi::ReGIRStaticParameters regirStaticParams; + rtxdi::ReSTIRGIStaticParameters restirGIStaticParams; + rtxdi::ReGIRDynamicParameters regirDynamicParameters; + bool resetISContext = false; uint32_t regirLightSlotCount = 0; bool freezeRegirPosition = false; - float regirCellSize = 1.f; - float regirSamplingJitter = 1.f; std::optional animationFrame; std::string benchmarkResults; @@ -200,6 +187,27 @@ struct UIData GBufferSettings gbufferSettings; LightingPasses::RenderSettings lightingSettings; + + struct + { + uint32_t numLocalLightUniformSamples = 8; + uint32_t numLocalLightPowerRISSamples = 8; + uint32_t numLocalLightReGIRRISSamples = 8; + rtxdi::ReSTIRDI_ResamplingMode resamplingMode; + ReSTIRDI_InitialSamplingParameters initialSamplingParams; + ReSTIRDI_TemporalResamplingParameters temporalResamplingParams; + ReSTIRDI_SpatialResamplingParameters spatialResamplingParams; + ReSTIRDI_ShadingParameters shadingParams; + } restirDI; + + struct + { + rtxdi::ReSTIRGI_ResamplingMode resamplingMode; + ReSTIRGI_TemporalResamplingParameters temporalResamplingParams; + ReSTIRGI_SpatialResamplingParameters spatialResamplingParams; + ReSTIRGI_FinalShadingParameters finalShadingParams; + } restirGI; + donut::render::TemporalAntiAliasingParameters taaParams; donut::render::TemporalAntiAliasingJitter temporalJitter = donut::render::TemporalAntiAliasingJitter::Halton; diff --git a/src/VisualizationPass.cpp b/src/VisualizationPass.cpp index 7870050..bf27806 100644 --- a/src/VisualizationPass.cpp +++ b/src/VisualizationPass.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include "RenderTargets.h" #include "RtxdiResources.h" @@ -77,8 +77,7 @@ void VisualizationPass::Render( nvrhi::IFramebuffer* framebuffer, const IView& renderView, const IView& upscaledView, - rtxdi::Context& context, - const rtxdi::FrameParameters& frameParameters, + const rtxdi::ImportanceSamplingContext& isContext, uint32_t inputBufferIndex, uint32_t visualizationMode, bool enableAccumulation) @@ -122,7 +121,8 @@ void VisualizationPass::Render( const auto& upscaledViewport = upscaledView.GetViewportState().viewports[0]; constants.resolutionScale.x = renderViewport.width() / upscaledViewport.width(); constants.resolutionScale.y = renderViewport.height() / upscaledViewport.height(); - context.FillRuntimeParameters(constants.runtimeParams, frameParameters); + constants.restirDIReservoirBufferParams = isContext.getReSTIRDIContext().getReservoirBufferParameters(); + constants.restirGIReservoirBufferParams = isContext.getReSTIRGIContext().getReservoirBufferParameters(); constants.visualizationMode = visualizationMode; constants.inputBufferIndex = inputBufferIndex; constants.enableAccumulation = enableAccumulation; diff --git a/src/VisualizationPass.h b/src/VisualizationPass.h index d70882a..6dce5ba 100644 --- a/src/VisualizationPass.h +++ b/src/VisualizationPass.h @@ -17,8 +17,7 @@ namespace rtxdi { - class Context; - struct FrameParameters; + class ImportanceSamplingContext; } namespace donut::engine @@ -63,8 +62,7 @@ class VisualizationPass nvrhi::IFramebuffer* framebuffer, const donut::engine::IView& renderView, const donut::engine::IView& upscaledView, - rtxdi::Context& context, - const rtxdi::FrameParameters& frameParameters, + const rtxdi::ImportanceSamplingContext& context, uint32_t inputBufferIndex, uint32_t visualizationMode, bool enableAccumulation); diff --git a/src/VulkanExtensions.cpp b/src/VulkanExtensions.cpp deleted file mode 100644 index c02b071..0000000 --- a/src/VulkanExtensions.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* -* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. -* -* NVIDIA CORPORATION and its licensors retain all intellectual property -* and proprietary rights in and to this software, related documentation -* and any modifications thereto. Any use, reproduction, disclosure or -* distribution of this software and related documentation without an express -* license agreement from NVIDIA CORPORATION is strictly prohibited. -*/ - -#if defined(WITH_RTXGI) && defined(USE_VK) - -#include "VulkanExtensions.h" - -// WARNING: This way of handling extensions works assuming one and only one device exists; do not call across multiple device objects - -//---------------------------------------------------------------------------------------------------------- -// Debug Utils Extension -//---------------------------------------------------------------------------------------------------------- - -// Debug Utils -PFN_vkSetDebugUtilsObjectNameEXT gvkSetDebugUtilsObjectNameEXT; -PFN_vkCmdBeginDebugUtilsLabelEXT gvkCmdBeginDebugUtilsLabelEXT; -PFN_vkCmdEndDebugUtilsLabelEXT gvkCmdEndDebugUtilsLabelEXT; - -VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectNameEXT(VkDevice device, const VkDebugUtilsObjectNameInfoEXT* pNameInfo) -{ - return gvkSetDebugUtilsObjectNameEXT(device, pNameInfo); -} - -VKAPI_ATTR void VKAPI_CALL vkCmdBeginDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo) -{ - return gvkCmdBeginDebugUtilsLabelEXT(commandBuffer, pLabelInfo); -} - -VKAPI_ATTR void VKAPI_CALL vkCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer) -{ - return gvkCmdEndDebugUtilsLabelEXT(commandBuffer); -} - -//---------------------------------------------------------------------------------------------------------- -// Public Functions -//---------------------------------------------------------------------------------------------------------- - -#define LOAD_INSTANCE_PROC(NAME) g##NAME = (PFN_##NAME)vkGetInstanceProcAddr(instance, #NAME); -#define LOAD_DEVICE_PROC(NAME) g##NAME = (PFN_##NAME)vkGetDeviceProcAddr(device, #NAME); - -void LoadInstanceExtensions(VkInstance instance) -{ -} - -void LoadDeviceExtensions(VkDevice device) -{ - // Debug Utils extension entry points - LOAD_DEVICE_PROC(vkSetDebugUtilsObjectNameEXT) - LOAD_DEVICE_PROC(vkCmdBeginDebugUtilsLabelEXT) - LOAD_DEVICE_PROC(vkCmdEndDebugUtilsLabelEXT) -} - -#endif // defined(WITH_RTXGI) && defined(USE_VK) \ No newline at end of file diff --git a/src/VulkanExtensions.h b/src/VulkanExtensions.h deleted file mode 100644 index 8c0602a..0000000 --- a/src/VulkanExtensions.h +++ /dev/null @@ -1,21 +0,0 @@ -/* -* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. -* -* NVIDIA CORPORATION and its licensors retain all intellectual property -* and proprietary rights in and to this software, related documentation -* and any modifications thereto. Any use, reproduction, disclosure or -* distribution of this software and related documentation without an express -* license agreement from NVIDIA CORPORATION is strictly prohibited. -*/ - -#pragma once - -#if _WIN32 -#define VK_USE_PLATFORM_WIN32_KHR -#elif __linux__ -#define VK_USE_PLATFORM_XLIB_KHR -#endif -#include - -void LoadDeviceExtensions(VkDevice device); -void LoadInstanceExtensions(VkInstance instance); diff --git a/src/main.cpp b/src/main.cpp index 94d78cc..f176d98 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,7 +9,7 @@ **************************************************************************/ // Include this first just to test the cleanliness -#include +#include #include #include @@ -61,10 +61,6 @@ #include "DLSS.h" #endif -#if WITH_RTXGI -#include "RtxgiIntegration.h" -#endif - #ifndef _WIN32 #include #else @@ -106,7 +102,7 @@ class SceneRenderer : public app::ApplicationBase std::shared_ptr m_EnvironmentMap; engine::BindingCache m_BindingCache; - std::unique_ptr m_RtxdiContext; + std::unique_ptr m_isContext; std::unique_ptr m_GBufferPass; std::unique_ptr m_RasterizedGBufferPass; std::unique_ptr m_PostprocessGBufferPass; @@ -136,10 +132,6 @@ class SceneRenderer : public app::ApplicationBase std::unique_ptr m_DLSS; #endif -#if WITH_RTXGI - std::shared_ptr m_RTXGI; -#endif - UIData& m_ui; CommandLineArguments& m_args; uint m_FramesSinceAnimation = 0; @@ -488,19 +480,13 @@ class SceneRenderer : public app::ApplicationBase m_BindingCache.Clear(); m_RenderTargets = nullptr; - m_RtxdiContext = nullptr; + m_isContext = nullptr; m_RtxdiResources = nullptr; m_TemporalAntiAliasingPass = nullptr; m_ToneMappingPass = nullptr; m_BloomPass = nullptr; #if WITH_NRD m_NRD = nullptr; -#endif -#if WITH_RTXGI - if (m_RTXGI) - { - m_RTXGI->InvalidateRenderTargets(); - } #endif } @@ -597,9 +583,6 @@ class SceneRenderer : public app::ApplicationBase m_LocalLightPdfMipmapPass = nullptr; m_VisualizationPass = nullptr; m_DebugVizPasses = nullptr; -#if WITH_RTXGI - m_RTXGI = nullptr; -#endif m_ui.environmentMapDirty = 1; LoadShaders(); @@ -635,33 +618,18 @@ class SceneRenderer : public app::ApplicationBase m_RtxdiResources = nullptr; } - if (!m_RtxdiContext) + if (!m_isContext) { - m_ui.rtxdiContextParams.RenderWidth = renderWidth; - m_ui.rtxdiContextParams.RenderHeight = renderHeight; + rtxdi::ImportanceSamplingContext_StaticParameters isStaticParams; + isStaticParams.CheckerboardSamplingMode = m_ui.restirDIStaticParams.CheckerboardSamplingMode; + isStaticParams.renderHeight = renderHeight; + isStaticParams.renderWidth = renderWidth; + isStaticParams.regirStaticParams = m_ui.regirStaticParams; - m_RtxdiContext = std::make_unique(m_ui.rtxdiContextParams); + m_isContext = std::make_unique(isStaticParams); - m_ui.regirLightSlotCount = m_RtxdiContext->GetReGIRLightSlotCount(); - } - -#if WITH_RTXGI - if (!m_RTXGI) - { - m_RTXGI = std::make_shared(GetDevice(), m_RootFs, m_DescriptorTableManager); - - const bool useRayQuery = GetDevice()->getGraphicsAPI() == nvrhi::GraphicsAPI::D3D12 - ? false // Workaround for an issue with DXC not producing correct code for RayQuery in the probe tracing pass - : m_ui.useRayQuery; - - m_RTXGI->InitializePasses(*m_ShaderFactory, m_LightingPasses->GetBindingLayout(), m_BindlessLayout, m_RtxdiContext->GetParameters(), useRayQuery); - - for (auto& volumeDesc : m_Scene->GetRtxgiVolumes()) - { - m_RTXGI->CreateVolume(volumeDesc); - } + m_ui.regirLightSlotCount = m_isContext->getReGIRContext().getReGIRLightSlotCount(); } -#endif if (!m_RenderTargets) { @@ -683,11 +651,7 @@ class SceneRenderer : public app::ApplicationBase m_RasterizedGBufferPass->CreatePipeline(*m_RenderTargets); -#if WITH_RTXGI - m_CompositingPass->CreateBindingSet(*m_RenderTargets, m_RTXGI.get()); -#else - m_CompositingPass->CreateBindingSet(*m_RenderTargets, nullptr); -#endif + m_CompositingPass->CreateBindingSet(*m_RenderTargets); m_VisualizationPass = nullptr; m_DebugVizPasses = nullptr; @@ -703,7 +667,8 @@ class SceneRenderer : public app::ApplicationBase m_RtxdiResources = std::make_unique( GetDevice(), - *m_RtxdiContext, + m_isContext->getReSTIRDIContext(), + m_isContext->getRISBufferSegmentAllocator(), (numEmissiveMeshes + meshAllocationQuantum - 1) & ~(meshAllocationQuantum - 1), (numEmissiveTriangles + triangleAllocationQuantum - 1) & ~(triangleAllocationQuantum - 1), (numPrimitiveLights + primitiveAllocationQuantum - 1) & ~(primitiveAllocationQuantum - 1), @@ -743,18 +708,13 @@ class SceneRenderer : public app::ApplicationBase m_Scene->GetTopLevelAS(), m_Scene->GetPrevTopLevelAS(), *m_RenderTargets, - *m_RtxdiResources, -#if WITH_RTXGI - m_RTXGI.get()); -#else - nullptr); -#endif + *m_RtxdiResources); } if (rtxdiResourcesCreated || m_ui.reloadShaders) { // Some RTXDI context settings affect the shader permutations - m_LightingPasses->CreatePipelines(m_ui.rtxdiContextParams, m_ui.useRayQuery); + m_LightingPasses->CreatePipelines(m_ui.regirStaticParams, m_ui.useRayQuery); } m_ui.reloadShaders = false; @@ -873,6 +833,60 @@ class SceneRenderer : public app::ApplicationBase } } + void UpdateReGIRContextFromUI() + { + auto& regirContext = m_isContext->getReGIRContext(); + auto dynamicParams = m_ui.regirDynamicParameters; + dynamicParams.center = { m_RegirCenter.x, m_RegirCenter.y, m_RegirCenter.z }; + regirContext.setDynamicParameters(dynamicParams); + } + + void UpdateReSTIRDIContextFromUI() + { + rtxdi::ReSTIRDIContext& restirDIContext = m_isContext->getReSTIRDIContext(); + ReSTIRDI_InitialSamplingParameters initialSamplingParams = m_ui.restirDI.initialSamplingParams; + switch (initialSamplingParams.localLightSamplingMode) + { + default: + case ReSTIRDI_LocalLightSamplingMode::Uniform: + initialSamplingParams.numPrimaryLocalLightSamples = m_ui.restirDI.numLocalLightUniformSamples; + break; + case ReSTIRDI_LocalLightSamplingMode::Power_RIS: + initialSamplingParams.numPrimaryLocalLightSamples = m_ui.restirDI.numLocalLightPowerRISSamples; + break; + case ReSTIRDI_LocalLightSamplingMode::ReGIR_RIS: + initialSamplingParams.numPrimaryLocalLightSamples = m_ui.restirDI.numLocalLightReGIRRISSamples; + break; + } + restirDIContext.setResamplingMode(m_ui.restirDI.resamplingMode); + restirDIContext.setInitialSamplingParameters(initialSamplingParams); + restirDIContext.setTemporalResamplingParameters(m_ui.restirDI.temporalResamplingParams); + restirDIContext.setSpatialResamplingParameters(m_ui.restirDI.spatialResamplingParams); + restirDIContext.setShadingParameters(m_ui.restirDI.shadingParams); + } + + void UpdateReSTIRGIContextFromUI() + { + rtxdi::ReSTIRGIContext& restirGIContext = m_isContext->getReSTIRGIContext(); + restirGIContext.setResamplingMode(m_ui.restirGI.resamplingMode); + restirGIContext.setTemporalResamplingParameters(m_ui.restirGI.temporalResamplingParams); + restirGIContext.setSpatialResamplingParameters(m_ui.restirGI.spatialResamplingParams); + restirGIContext.setFinalShadingParameters(m_ui.restirGI.finalShadingParams); + } + + bool IsLocalLightPowerRISEnabled() + { + if (m_ui.indirectLightingMode == IndirectLightingMode::ReStirGI) + { + ReSTIRDI_InitialSamplingParameters indirectReSTIRDISamplingParams = m_ui.lightingSettings.brdfptParams.secondarySurfaceReSTIRDIParams.initialSamplingParams; + bool enabled = (indirectReSTIRDISamplingParams.localLightSamplingMode == ReSTIRDI_LocalLightSamplingMode::Power_RIS) || + (indirectReSTIRDISamplingParams.localLightSamplingMode == ReSTIRDI_LocalLightSamplingMode::ReGIR_RIS && m_isContext->getReGIRContext().isLocalLightPowerRISEnable()); + if (enabled) + return true; + } + return m_isContext->isLocalLightPowerRISEnabled(); + } + void RenderScene(nvrhi::IFramebuffer* framebuffer) override { if (m_FrameStepMode == FrameStepMode::Wait) @@ -952,17 +966,13 @@ class SceneRenderer : public app::ApplicationBase m_NRD = nullptr; // need to create a new one #endif - if (m_ui.resetRtxdiContext) + if (m_ui.resetISContext) { GetDevice()->waitForIdle(); - m_RtxdiContext = nullptr; + m_isContext = nullptr; m_RtxdiResources = nullptr; -#if WITH_RTXGI - // The RTXGI probe tracing pass depends on the RTXDI context parameters - m_RTXGI = nullptr; -#endif - m_ui.resetRtxdiContext = false; + m_ui.resetISContext = false; } if (m_ui.environmentMapDirty == 2) @@ -984,6 +994,9 @@ class SceneRenderer : public app::ApplicationBase SetupRenderPasses(renderWidth, renderHeight, exposureResetRequired); if (!m_ui.freezeRegirPosition) m_RegirCenter = m_Camera.GetPosition(); + UpdateReSTIRDIContextFromUI(); + UpdateReGIRContextFromUI(); + UpdateReSTIRGIContextFromUI(); #if WITH_DLSS if (!m_ui.dlssAvailable && m_ui.aaMode == AntiAliasingMode::DLSS) m_ui.aaMode = AntiAliasingMode::TAA; @@ -998,15 +1011,12 @@ class SceneRenderer : public app::ApplicationBase m_RenderTargets->NextFrame(); m_GlassPass->NextFrame(); m_Scene->NextFrame(); -#if WITH_RTXGI - m_RTXGI->NextFrame(); -#endif m_DebugVizPasses->NextFrame(); // Advance the TAA jitter offset at half frame rate if accumulation is used with // checkerboard rendering. Otherwise, the jitter pattern resonates with the checkerboard, // and stipple patterns appear in the accumulated results. - if (!((m_ui.aaMode == AntiAliasingMode::Accumulation) && (m_RtxdiContext->GetParameters().CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off) && (GetFrameIndex() & 1))) + if (!((m_ui.aaMode == AntiAliasingMode::Accumulation) && (m_isContext->getReSTIRDIContext().getStaticParameters().CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off) && (GetFrameIndex() & 1))) { m_TemporalAntiAliasingPass->AdvanceFrame(); } @@ -1086,7 +1096,7 @@ class SceneRenderer : public app::ApplicationBase AssignIesProfiles(m_CommandList); m_Scene->RefreshBuffers(m_CommandList, GetFrameIndex()); - m_RtxdiResources->InitializeNeighborOffsets(m_CommandList, *m_RtxdiContext); + m_RtxdiResources->InitializeNeighborOffsets(m_CommandList, m_isContext->getNeighborOffsetCount()); if (m_FramesSinceAnimation < 2) { @@ -1129,26 +1139,28 @@ class SceneRenderer : public app::ApplicationBase m_PostprocessGBufferPass->Render(m_CommandList, m_View); } - rtxdi::FrameParameters frameParameters; // The light indexing members of frameParameters are written by PrepareLightsPass below - frameParameters.frameIndex = effectiveFrameIndex; - frameParameters.regirCenter = { m_RegirCenter.x, m_RegirCenter.y, m_RegirCenter.z }; - frameParameters.regirCellSize = m_ui.regirCellSize; - frameParameters.regirSamplingJitter = m_ui.regirSamplingJitter; - frameParameters.enableLocalLightImportanceSampling = m_ui.enableLocalLightImportanceSampling; + rtxdi::ReSTIRDIContext& restirDIContext = m_isContext->getReSTIRDIContext(); + restirDIContext.setFrameIndex(effectiveFrameIndex); + m_isContext->getReSTIRGIContext().setFrameIndex(effectiveFrameIndex); { ProfilerScope scope(*m_Profiler, m_CommandList, ProfilerSection::MeshProcessing); - m_PrepareLightsPass->Process( + RTXDI_LightBufferParameters lightBufferParams = m_PrepareLightsPass->Process( m_CommandList, - *m_RtxdiContext, + restirDIContext, m_Scene->GetSceneGraph()->GetLights(), - m_EnvironmentMapPdfMipmapPass != nullptr && m_ui.environmentMapImportanceSampling, - frameParameters); + m_EnvironmentMapPdfMipmapPass != nullptr && m_ui.environmentMapImportanceSampling); + m_isContext->setLightBufferParams(lightBufferParams); + + auto initialSamplingParams = restirDIContext.getInitialSamplingParameters(); + initialSamplingParams.environmentMapImportanceSampling = lightBufferParams.environmentLightParams.lightPresent; + m_ui.restirDI.initialSamplingParams.environmentMapImportanceSampling = initialSamplingParams.environmentMapImportanceSampling; + restirDIContext.setInitialSamplingParameters(initialSamplingParams); } - if (m_ui.enableLocalLightImportanceSampling) + if (IsLocalLightPowerRISEnabled()) { ProfilerScope scope(*m_Profiler, m_CommandList, ProfilerSection::LocalLightPdfMap); @@ -1157,7 +1169,7 @@ class SceneRenderer : public app::ApplicationBase #if WITH_NRD - if (m_RtxdiContext->GetParameters().CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off) + if (restirDIContext.getStaticParameters().CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off) { m_ui.reblurSettings.checkerboardMode = nrd::CheckerboardMode::BLACK; m_ui.relaxSettings.checkerboardMode = nrd::CheckerboardMode::BLACK; @@ -1183,40 +1195,7 @@ class SceneRenderer : public app::ApplicationBase if (lightingSettings.denoiserMode == DENOISER_MODE_OFF) lightingSettings.enableGradients = false; -#if WITH_RTXGI - if (m_ui.rtxgi.enabled) - { - m_LightingPasses->FillConstantBufferForProbeTracing(m_CommandList, *m_RtxdiContext, lightingSettings, frameParameters); - - assert(m_RTXGI->GetNumVolumes() == int(m_Scene->GetRtxgiVolumes().size())); - - for (int volumeIndex = 0; volumeIndex < m_RTXGI->GetNumVolumes(); volumeIndex++) - { - auto volume = m_RTXGI->GetVolume(volumeIndex); - assert(volume); - - const auto& params = m_Scene->GetRtxgiVolumes()[volumeIndex]; - volume->SetParameters(m_ui.rtxgi); - volume->SetVolumeParameters(params); - if (params.scrolling) - volume->SetOrigin(m_Camera.GetPosition()); - } - - m_RTXGI->UpdateAllVolumes( - m_CommandList, - m_LightingPasses->GetCurrentBindingSet(), - m_DescriptorTableManager->GetDescriptorTable(), - *m_Profiler); - - m_ui.rtxgi.resetRelocation = false; - } - - const uint32_t numRtxgiVolumes = m_RTXGI && m_ui.rtxgi.enabled ? m_RTXGI->GetNumVolumes() : 0; -#else - const uint32_t numRtxgiVolumes = 0; -#endif - - const bool checkerboard = m_RtxdiContext->GetParameters().CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off; + const bool checkerboard = restirDIContext.getStaticParameters().CheckerboardSamplingMode != rtxdi::CheckerboardMode::Off; bool enableDirectReStirPass = m_ui.directLightingMode == DirectLightingMode::ReStir; bool enableBrdfAndIndirectPass = m_ui.directLightingMode == DirectLightingMode::Brdf || m_ui.indirectLightingMode != IndirectLightingMode::None; @@ -1224,12 +1203,14 @@ class SceneRenderer : public app::ApplicationBase // When indirect lighting is enabled, we don't want ReSTIR to be the NRD front-end, // it should just write out the raw color data. - lightingSettings.enableDenoiserInputPacking = !enableIndirect; + ReSTIRDI_ShadingParameters restirDIShadingParams = m_isContext->getReSTIRDIContext().getShadingParameters(); + restirDIShadingParams.enableDenoiserInputPacking = !enableIndirect; + m_isContext->getReSTIRDIContext().setShadingParameters(restirDIShadingParams); if (!enableDirectReStirPass) { // Secondary resampling can only be done as a post-process of ReSTIR direct lighting - lightingSettings.enableSecondaryResampling = false; + lightingSettings.brdfptParams.enableSecondaryResampling = false; // Gradients are only produced by the direct ReSTIR pass lightingSettings.enableGradients = false; @@ -1238,10 +1219,9 @@ class SceneRenderer : public app::ApplicationBase if (enableDirectReStirPass || enableIndirect) { m_LightingPasses->PrepareForLightSampling(m_CommandList, - *m_RtxdiContext, + *m_isContext, m_View, m_ViewPrevious, lightingSettings, - frameParameters, /* enableAccumulation = */ m_ui.aaMode == AntiAliasingMode::Accumulation); } @@ -1250,7 +1230,7 @@ class SceneRenderer : public app::ApplicationBase m_CommandList->clearTextureFloat(m_RenderTargets->Gradients, nvrhi::AllSubresources, nvrhi::Color(0.f)); m_LightingPasses->RenderDirectLighting(m_CommandList, - *m_RtxdiContext, + restirDIContext, m_View, lightingSettings); @@ -1264,24 +1244,24 @@ class SceneRenderer : public app::ApplicationBase if (enableBrdfAndIndirectPass) { - lightingSettings.enableDenoiserInputPacking = true; + ReSTIRDI_ShadingParameters restirDIShadingParams = m_isContext->getReSTIRDIContext().getShadingParameters(); + restirDIShadingParams.enableDenoiserInputPacking = true; + m_isContext->getReSTIRDIContext().setShadingParameters(restirDIShadingParams); - bool enableReStirGI = m_ui.indirectLightingMode == IndirectLightingMode::ReStirGI; + bool enableReSTIRGI = m_ui.indirectLightingMode == IndirectLightingMode::ReStirGI; m_LightingPasses->RenderBrdfRays( m_CommandList, - *m_RtxdiContext, + *m_isContext, m_View, m_ViewPrevious, lightingSettings, m_ui.gbufferSettings, - frameParameters, *m_EnvironmentLight, /* enableIndirect = */ enableIndirect, /* enableAdditiveBlend = */ enableDirectReStirPass, /* enableEmissiveSurfaces = */ m_ui.directLightingMode == DirectLightingMode::Brdf, - numRtxgiVolumes, /* enableAccumulation = */ m_ui.aaMode == AntiAliasingMode::Accumulation, - enableReStirGI + enableReSTIRGI ); } @@ -1314,7 +1294,6 @@ class SceneRenderer : public app::ApplicationBase m_View, m_ViewPrevious, denoiserMode, - /* numRtxgiVolumes = */ (! enableIndirect) ? numRtxgiVolumes : 0, checkerboard, m_ui, *m_EnvironmentLight); @@ -1330,13 +1309,6 @@ class SceneRenderer : public app::ApplicationBase m_ui.gbufferSettings.materialReadbackPosition); } -#if WITH_RTXGI - if (m_ui.rtxgi.enabled && m_ui.rtxgi.showProbes) - { - m_RTXGI->RenderDebug(m_CommandList, m_DescriptorTableManager->GetDescriptorTable(), *m_RenderTargets, m_ui.rtxgi.selectedVolumeIndex, m_View); - } -#endif - Resolve(m_CommandList, accumulationWeight); if (m_ui.enableBloom) @@ -1442,8 +1414,7 @@ class SceneRenderer : public app::ApplicationBase m_RenderTargets->LdrFramebuffer->GetFramebuffer(m_UpscaledView), m_View, m_UpscaledView, - *m_RtxdiContext, - frameParameters, + *m_isContext, inputBufferIndex, m_ui.visualizationMode, m_ui.aaMode == AntiAliasingMode::Accumulation); From e20285a1fbe0931134894cc8da36298d1feb47ec Mon Sep 17 00:00:00 2001 From: Michael Murphy Date: Mon, 18 Mar 2024 13:16:26 -0700 Subject: [PATCH 2/2] Changed references to RTX Direct Illumination to RTX Dynamic Illumination. --- README.md | 2 +- src/Testing.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b1ed221..8480025 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Version 2.1.0. ## Introduction -**RTX** **D**irect **I**llumination is a framework that facilitates the implementations of efficient direct light sampling in real-time renderers. It is based on the **ReSTIR** algorithm published in the paper called "Spatiotemporal reservoir resampling for real-time ray tracing with dynamic direct lighting" by B. Bitterli et al. +**RTX** **D**ynamic **I**llumination is a framework that facilitates the implementations of efficient direct light sampling in real-time renderers. It is based on the **ReSTIR** algorithm published in the paper called "Spatiotemporal reservoir resampling for real-time ray tracing with dynamic direct lighting" by B. Bitterli et al. Starting with version 2.0, RTXDI also includes **ReSTIR GI** functionality, which allows applications to apply importance resampling to indirect illumination rendered using path tracing. For more information about the indirect illumination algorithm, see the paper called "ReSTIR GI: Path Resampling for Real-Time Path Tracing" by Y. Ouyang et al. The feature is described in more detail in [this document](doc/RestirGI.md). diff --git a/src/Testing.cpp b/src/Testing.cpp index 3579a14..264aeab 100644 --- a/src/Testing.cpp +++ b/src/Testing.cpp @@ -23,7 +23,7 @@ using namespace donut; namespace fs = std::filesystem; -const char* g_ApplicationTitle = "RTX Direct Illumination SDK Sample"; +const char* g_ApplicationTitle = "RTX Dynamic Illumination SDK Sample"; static void toupper(std::string& s) {