diff --git a/.clang-format b/.clang-format index 3f673f4b3..0328d49b1 100644 --- a/.clang-format +++ b/.clang-format @@ -12,6 +12,7 @@ DerivePointerAlignment: false PointerAlignment: Left FixNamespaceComments: true +BinPackArguments: false AllowAllConstructorInitializersOnNextLine: true BreakConstructorInitializers: BeforeComma AlwaysBreakTemplateDeclarations: true @@ -62,6 +63,7 @@ DerivePointerAlignment: false PointerAlignment: Left FixNamespaceComments: true +BinPackArguments: false AllowAllConstructorInitializersOnNextLine: true BreakConstructorInitializers: BeforeComma AlwaysBreakTemplateDeclarations: true diff --git a/.clang-format-ignore b/.clang-format-ignore index d6eeaacbf..88b92226c 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -2,9 +2,10 @@ Source/Falcor/Rendering Source/Falcor/Scene Source/Mogwai Source/plugins/importers/USDImporter -Source/RenderPasses +Source/RenderPasses/PathTracer # Explicitly whitelisted files !Source/Falcor/Rendering/Materials/BSDFs !Source/RenderPasses/DLSSPass/ +!Source/Falcor/Utils/fast_vector.h diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index e0139dfbc..09bc49a69 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -4,15 +4,18 @@ on: pull_request: branches: ["master"] -env: - CMAKE_EXE: tools\.packman\cmake\bin\cmake.exe - CMAKE_BUILD_PRESET: windows-ninja-msvc-ci - CMAKE_BUILD_CONFIG: Release - jobs: windows: - name: Windows/MSVC + name: Windows Ninja/MSVC runs-on: windows-latest + env: + CMAKE_EXE: tools\.packman\cmake\bin\cmake.exe + CMAKE_BUILD_PRESET: windows-ninja-msvc + strategy: + matrix: + include: + - config: Release + - config: Debug steps: - name: Checkout uses: actions/checkout@v3 @@ -30,5 +33,32 @@ jobs: - name: Build run: | %CMAKE_EXE% --preset %CMAKE_BUILD_PRESET% - %CMAKE_EXE% --build build/%CMAKE_BUILD_PRESET% --config %CMAKE_BUILD_CONFIG% + %CMAKE_EXE% --build build/%CMAKE_BUILD_PRESET% --config ${{ matrix.config }} shell: cmd + linux: + name: Linux/GCC + runs-on: ubuntu-22.04 + env: + CMAKE_EXE: ./tools/.packman/cmake/bin/cmake + CMAKE_BUILD_PRESET: linux-gcc + strategy: + matrix: + include: + - config: Release + - config: Debug + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Install system dependencies + run: sudo apt install -y xorg-dev libgtk-3-dev + shell: bash + - name: Setup + run: ./setup.sh + shell: bash + - name: Build + run: | + ${CMAKE_EXE} --preset ${CMAKE_BUILD_PRESET} + ${CMAKE_EXE} --build build/${CMAKE_BUILD_PRESET} --config ${{ matrix.config }} + shell: bash diff --git a/.vscode-default/extensions.json b/.vscode-default/extensions.json index 9102525fb..6f2cc2b1b 100644 --- a/.vscode-default/extensions.json +++ b/.vscode-default/extensions.json @@ -4,6 +4,7 @@ "ms-vscode.cpptools", "ms-vscode.cmake-tools", "josetr.cmake-language-support-vscode", - "shader-slang.slang-language-extension" + "shader-slang.slang-language-extension", + "ms-python.python" ] } diff --git a/.vscode-default/settings.json b/.vscode-default/settings.json index e5d4f0dd0..5265df42f 100644 --- a/.vscode-default/settings.json +++ b/.vscode-default/settings.json @@ -4,5 +4,6 @@ }, "cmake.configureOnEdit": false, "C_Cpp.clang_format_path": "${workspaceFolder}/tools/.packman/clang-format/clang-format", - "slang.format.clangFormatLocation": "${workspaceFolder}/tools/.packman/clang-format/clang-format" + "slang.format.clangFormatLocation": "${workspaceFolder}/tools/.packman/clang-format/clang-format", + "cmake.useProjectStatusView": false } diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a0c25ddc..f4ce72d1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,9 +16,12 @@ project(Falcor # Configuration options # ----------------------------------------------------------------------------- -# Enable/disable reporting Falcor exceptions as fatal errors before actually throwing them. -# This allows to break into the debugger at the location where an exception is thrown. -set(FALCOR_REPORT_EXCEPTION_AS_ERROR OFF CACHE BOOL "Report Falcor exceptions as fatal errors (and break into debugger)") +# Enable/disable asserts. AUTO enables asserts in debug builds. +set(FALCOR_ENABLE_ASSERTS "AUTO" CACHE STRING "Enable asserts") +set_property(CACHE FALCOR_ENABLE_ASSERTS PROPERTY STRINGS AUTO ON OFF) + +# Enable/disable the profiler. +set(FALCOR_ENABLE_PROFILER ON CACHE BOOL "Enable profiler") # Enable/disable using system Python distribution. This requires Python 3.7 to be available. set(FALCOR_USE_SYSTEM_PYTHON OFF CACHE BOOL "Use system Python distribution") @@ -429,6 +432,7 @@ endif() # ----------------------------------------------------------------------------- add_subdirectory(Source/Falcor) +add_subdirectory(Source/Modules) add_subdirectory(Source/Mogwai) add_subdirectory(Source/plugins) add_subdirectory(Source/RenderPasses) diff --git a/README.md b/README.md index d267d45b1..67f88faf0 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,20 @@ Falcor uses the [CMake](https://cmake.org) build system. Additional information ### Visual Studio If you are working with Visual Studio 2022, you can setup a native Visual Studio solution by running `setup_vs2022.bat` after cloning this repository. The solution files are written to `build/windows-vs2022` and the binary output is located in `build/windows-vs2022/bin`. - ### Visual Studio Code -If you are working with Visual Studio Code, run `setup.bat` after cloning this repository. This will setup a VS Code workspace in the `.vscode` folder with sensible defaults (only if `.vscode` does not exist yet). When opening the project folder in VS Code, it will prompt to install recommended extensions. We recommend you do, but at least make sure that _CMake Tools_ is installed. To build Falcor, you can select the configure preset by executing the _CMake: Select Configure Preset_ action (Ctrl+Shift+P). Choose the _Windows Ninja/MSVC_ preset (or one for a different rendering backend). Then simply hit _Build_ (or press F7) to build the project. The binary output is located in `build/windows-ninja-msvc/bin`. +If you are working with Visual Studio Code, run `setup.bat` after cloning this repository. This will setup a VS Code workspace in the `.vscode` folder with sensible defaults (only if `.vscode` does not exist yet). When opening the project folder in VS Code, it will prompt to install recommended extensions. We recommend you do, but at least make sure that _CMake Tools_ is installed. To build Falcor, you can select the configure preset by executing the _CMake: Select Configure Preset_ action (Ctrl+Shift+P). Choose the _Windows Ninja/MSVC_ preset. Then simply hit _Build_ (or press F7) to build the project. The binary output is located in `build/windows-ninja-msvc/bin`. Warning: Do not start VS Code from _Git Bash_, it will modify the `PATH` environment variable to an incompatible format, leading to issues with CMake. +### Linux +Falcor has experimental support for Ubuntu 22.04. To build Falcor on Linux, run `setup.sh` after cloning this repository. You also need to install some system library headers using: + +``` +sudo apt install xorg-dev libgtk-3-dev +``` + +You can use the same instructions for building Falcor as described in the _Visual Studio Code_ section above, simply choose the _Linux/GCC_ preset. + ### Configure Presets Falcor uses _CMake Presets_ store in `CMakePresets.json` to provide a set of commonly used build configurations. You can get the full list of available configure presets running `cmake --list-presets`: @@ -55,14 +63,15 @@ Use `cmake --preset ` to generate the build tree for a given preset An existing build tree can be compiled using `cmake --build build/`. -Note: Some render passes (RTXGI, RTXDI, DLSS in particular) are not fully working with the new Slang GFX backend. +## Falcor In Python +For more information on how to use Falcor as a Python module see [Falcor In Python](docs/falcor-in-python.md). ## Microsoft DirectX 12 Agility SDK Falcor uses the [Microsoft DirectX 12 Agility SDK](https://devblogs.microsoft.com/directx/directx12agility/) to get access to the latest DirectX 12 features. Applications can enable the Agility SDK by putting `FALCOR_EXPORT_D3D12_AGILITY_SDK` in the main `.cpp` file. `Mogwai`, `FalcorTest` and `RenderGraphEditor` have the Agility SDK enabled by default. ## NVAPI -To enable NVAPI support, head over to https://developer.nvidia.com/nvapi and download the latest version of NVAPI (this build is tested against version R520). -Extract the content of the zip file into `external/packman/` and rename `R520-developer` to `nvapi`. +To enable NVAPI support, head over to https://developer.nvidia.com/nvapi and download the latest version of NVAPI (this build is tested against version R535). +Extract the content of the zip file into `external/packman/` and rename `R535-developer` to `nvapi`. ## NSight Aftermath To enable NSight Aftermath support, head over to https://developer.nvidia.com/nsight-aftermath and download the latest version of Aftermath (this build is tested against version 2023.1). @@ -82,17 +91,11 @@ Note: You also need CUDA installed to compile the `OptixDenoiser` render pass, s Falcor ships with the following NVIDIA RTX SDKs: - DLSS (https://github.com/NVIDIA/DLSS) -- RTXGI (https://github.com/NVIDIAGameWorks/RTXGI) - RTXDI (https://github.com/NVIDIAGameWorks/RTXDI) - NRD (https://github.com/NVIDIAGameWorks/RayTracingDenoiser) Note that these SDKs are not under the same license as Falcor, see [LICENSE.md](LICENSE.md) for details. -## Falcor Configuration -`FalcorConfig.h` contains some flags which control Falcor's behavior. -- `FALCOR_ENABLE_LOGGER` - Enable/disable the logger. By default, it is set to `1`. -- `FALCOR_ENABLE_PROFILER` - Enable/disable the internal CPU/GPU profiler. By default, it is set to `1`. - ## Resources - [Falcor](https://github.com/NVIDIAGameWorks/Falcor): Falcor's GitHub page. - [Documentation](./docs/index.md): Additional information and tutorials. diff --git a/Source/Falcor/CMakeLists.txt b/Source/Falcor/CMakeLists.txt index 797c196a0..e2fd9bb0f 100644 --- a/Source/Falcor/CMakeLists.txt +++ b/Source/Falcor/CMakeLists.txt @@ -8,19 +8,17 @@ target_sources(Falcor PRIVATE GlobalState.cpp GlobalState.h - Core/Assert.h + Core/AssetResolver.cpp + Core/AssetResolver.h Core/Enum.h - Core/ErrorHandling.cpp - Core/ErrorHandling.h - Core/Errors.cpp - Core/Errors.h - Core/FalcorConfig.h + Core/Error.cpp + Core/Error.h Core/GLFW.h Core/HotReloadFlags.h + Core/Macros.h Core/Object.cpp Core/Object.h Core/ObjectPython.h - Core/Macros.h Core/Plugin.cpp Core/Plugin.h Core/SampleApp.cpp @@ -32,8 +30,8 @@ target_sources(Falcor PRIVATE Core/Window.cpp Core/Window.h - Core/API/Aftermath.h Core/API/Aftermath.cpp + Core/API/Aftermath.h Core/API/BlendState.cpp Core/API/BlendState.h Core/API/BlitContext.cpp @@ -41,7 +39,6 @@ target_sources(Falcor PRIVATE Core/API/BlitReduction.3d.slang Core/API/Buffer.cpp Core/API/Buffer.h - Core/API/Common.h Core/API/ComputeContext.cpp Core/API/ComputeContext.h Core/API/ComputeStateObject.cpp @@ -54,6 +51,8 @@ target_sources(Falcor PRIVATE Core/API/Device.h Core/API/FBO.cpp Core/API/FBO.h + Core/API/Fence.cpp + Core/API/Fence.h Core/API/FencedPool.h Core/API/Formats.cpp Core/API/Formats.h @@ -61,8 +60,6 @@ target_sources(Falcor PRIVATE Core/API/GFXAPI.h Core/API/GFXHelpers.cpp Core/API/GFXHelpers.h - Core/API/GpuFence.cpp - Core/API/GpuFence.h Core/API/GpuMemoryHeap.cpp Core/API/GpuMemoryHeap.h Core/API/GpuTimer.cpp @@ -77,6 +74,8 @@ target_sources(Falcor PRIVATE Core/API/NvApiExDesc.h Core/API/ParameterBlock.cpp Core/API/ParameterBlock.h + Core/API/PythonHelpers.cpp + Core/API/PythonHelpers.h Core/API/QueryHeap.cpp Core/API/QueryHeap.h Core/API/RasterizerState.cpp @@ -98,11 +97,12 @@ target_sources(Falcor PRIVATE Core/API/Sampler.h Core/API/ShaderResourceType.h Core/API/ShaderTable.h - Core/API/ShaderType.h Core/API/Swapchain.cpp Core/API/Swapchain.h Core/API/Texture.cpp Core/API/Texture.h + Core/API/Types.cpp + Core/API/Types.h Core/API/VAO.cpp Core/API/VAO.h Core/API/VertexLayout.cpp @@ -130,13 +130,8 @@ target_sources(Falcor PRIVATE Core/Platform/PlatformHandles.h Core/Platform/ProgressBar.cpp Core/Platform/ProgressBar.h - Core/Platform/SearchDirectories.h - Core/Program/ComputeProgram.cpp - Core/Program/ComputeProgram.h Core/Program/DefineList.h - Core/Program/GraphicsProgram.cpp - Core/Program/GraphicsProgram.h Core/Program/Program.cpp Core/Program/Program.h Core/Program/ProgramManager.cpp @@ -149,8 +144,6 @@ target_sources(Falcor PRIVATE Core/Program/ProgramVersion.h Core/Program/RtBindingTable.cpp Core/Program/RtBindingTable.h - Core/Program/RtProgram.cpp - Core/Program/RtProgram.h Core/Program/ShaderVar.cpp Core/Program/ShaderVar.h @@ -160,6 +153,18 @@ target_sources(Falcor PRIVATE Core/State/GraphicsState.h Core/State/StateGraph.h + DiffRendering/AggregateGradients.cs.slang + DiffRendering/DiffDebug.slang + DiffRendering/DiffMaterialData.slang + DiffRendering/DiffSceneIO.slang + DiffRendering/DiffSceneQuery.slang + DiffRendering/GradientIOWrapper.slang + DiffRendering/SceneGradientInfo.slang + DiffRendering/SceneGradients.cpp + DiffRendering/SceneGradients.h + DiffRendering/SceneGradients.slang + DiffRendering/SharedTypes.slang + RenderGraph/RenderGraph.cpp RenderGraph/RenderGraph.h RenderGraph/RenderGraphCompiler.cpp @@ -318,7 +323,9 @@ target_sources(Falcor PRIVATE Scene/HitInfoType.slang Scene/Importer.cpp Scene/Importer.h + Scene/ImporterError.h Scene/Intersection.slang + Scene/MeshIO.cs.slang Scene/NullTrace.cs.slang Scene/Raster.slang Scene/Raytracing.slang @@ -329,6 +336,8 @@ target_sources(Falcor PRIVATE Scene/SceneBlock.slang Scene/SceneBuilder.cpp Scene/SceneBuilder.h + Scene/SceneBuilderDump.cpp + Scene/SceneBuilderDump.h Scene/SceneCache.cpp Scene/SceneCache.h Scene/SceneDefines.slangh @@ -406,11 +415,13 @@ target_sources(Falcor PRIVATE Scene/Material/DiffuseSpecularUtils.h Scene/Material/HairMaterial.cpp Scene/Material/HairMaterial.h - Scene/Material/VolumeProperties.slang Scene/Material/Material.cpp Scene/Material/Material.h Scene/Material/MaterialData.slang Scene/Material/MaterialFactory.slang + Scene/Material/MaterialParamLayout.h + Scene/Material/MaterialParamLayout.slang + Scene/Material/MaterialParamLayout.slangh Scene/Material/MaterialSystem.cpp Scene/Material/MaterialSystem.h Scene/Material/MaterialSystem.slang @@ -434,24 +445,29 @@ target_sources(Falcor PRIVATE Scene/Material/RGLMaterial.cpp Scene/Material/RGLMaterial.h Scene/Material/RGLMaterialData.slang + Scene/Material/SerializedMaterialParams.h Scene/Material/ShadingUtils.slang Scene/Material/StandardMaterial.cpp Scene/Material/StandardMaterial.h + Scene/Material/StandardMaterialParamLayout.slang Scene/Material/TextureHandle.slang Scene/Material/TextureSampler.slang + Scene/Material/VolumeProperties.slang - Scene/Material/PBRT/PBRTDiffuseMaterial.h - Scene/Material/PBRT/PBRTDiffuseMaterial.cpp - Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h - Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.cpp - Scene/Material/PBRT/PBRTConductorMaterial.h - Scene/Material/PBRT/PBRTConductorMaterial.cpp - Scene/Material/PBRT/PBRTDielectricMaterial.h - Scene/Material/PBRT/PBRTDielectricMaterial.cpp - Scene/Material/PBRT/PBRTCoatedConductorMaterial.h Scene/Material/PBRT/PBRTCoatedConductorMaterial.cpp - Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h + Scene/Material/PBRT/PBRTCoatedConductorMaterial.h Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.cpp + Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h + Scene/Material/PBRT/PBRTConductorMaterial.cpp + Scene/Material/PBRT/PBRTConductorMaterial.h + Scene/Material/PBRT/PBRTConductorMaterialParamLayout.slang + Scene/Material/PBRT/PBRTDielectricMaterial.cpp + Scene/Material/PBRT/PBRTDielectricMaterial.h + Scene/Material/PBRT/PBRTDiffuseMaterial.cpp + Scene/Material/PBRT/PBRTDiffuseMaterial.h + Scene/Material/PBRT/PBRTDiffuseMaterialParamLayout.slang + Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.cpp + Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.cpp Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.h @@ -524,8 +540,9 @@ target_sources(Falcor PRIVATE Utils/BufferAllocator.h Utils/CryptoUtils.cpp Utils/CryptoUtils.h + Utils/Dictionary.h + Utils/fast_vector.h Utils/HostDeviceShared.slangh - Utils/InternalDictionary.h Utils/Logger.cpp Utils/Logger.h Utils/NumericRange.h @@ -578,7 +595,6 @@ target_sources(Falcor PRIVATE Utils/Debug/PixelDebug.h Utils/Debug/PixelDebug.slang Utils/Debug/PixelDebugTypes.slang - Utils/Debug/ReflectPixelDebugTypes.cs.slang Utils/Debug/WarpProfiler.cpp Utils/Debug/WarpProfiler.h Utils/Debug/WarpProfiler.slang @@ -607,6 +623,7 @@ target_sources(Falcor PRIVATE Utils/Math/BitTricks.slang Utils/Math/Common.h Utils/Math/CubicSpline.h + Utils/Math/DiffMathHelpers.slang Utils/Math/FalcorMath.h Utils/Math/Float16.cpp Utils/Math/Float16.h @@ -671,7 +688,6 @@ target_sources(Falcor PRIVATE Utils/Scripting/Console.h Utils/Scripting/ndarray.cpp Utils/Scripting/ndarray.h - Utils/Scripting/PythonDictionary.h Utils/Scripting/ScriptBindings.cpp Utils/Scripting/ScriptBindings.h Utils/Scripting/Scripting.cpp @@ -707,11 +723,13 @@ target_sources(Falcor PRIVATE Utils/UI/InputTypes.h Utils/UI/PixelZoom.cpp Utils/UI/PixelZoom.h + Utils/UI/PythonUI.cpp + Utils/UI/PythonUI.h Utils/UI/SpectrumUI.cpp Utils/UI/SpectrumUI.h + Utils/UI/TextRenderer.3d.slang Utils/UI/TextRenderer.cpp Utils/UI/TextRenderer.h - Utils/UI/TextRenderer.3d.slang ) @@ -750,6 +768,7 @@ endif() if(FALCOR_HAS_CUDA) target_sources(Falcor PRIVATE + Utils/CudaRuntime.h Utils/CudaUtils.cpp Utils/CudaUtils.h ) @@ -788,6 +807,8 @@ target_compile_options(Falcor # MSVC flags. $<$: /Zi # generate debug symbols + /MP # enable multi-processor compilation + # Configure warnings /WX # warnings as errors /W4 # increase warning level /wd4251 # 'type' : class 'type1' needs to have dll-interface to be used by clients of class 'type2' @@ -806,7 +827,13 @@ target_compile_options(Falcor /wd4389 # signed/unsigned mismatch /wd4459 # declaration of 'identifier' hides global declaration /wd4268 # 'identifier' : 'const' static/global data initialized with compiler generated default constructor fills the object with zeros - /MP # enable multi-processor compilation + /external:templates- # allows warnings from system headers when they occur in a template that's instantiated in your code + # Enable warnings that are disabled with /W4 + /we4263 # 'function': member function does not override any base class virtual member function + /we4264 # 'virtual_function': no override available for virtual member function from base 'class'; function is hidden + /we5038 # data member 'member1' will be initialized after data member 'member2' + /we5259 # 'specialized-type': explicit specialization requires 'template <>' + /we5263 # calling 'std::move' on a temporary object prevents copy elision > # Clang/GCC flags. $<$,$>: @@ -878,6 +905,12 @@ target_link_options(Falcor $<$:/DEBUG> # generate debug information ) +if(FALCOR_ENABLE_ASSERTS STREQUAL "AUTO") + set(FALCOR_ENABLE_ASSERTS_ $,ON,OFF>) +else() + set(FALCOR_ENABLE_ASSERTS_ ${FALCOR_ENABLE_ASSERTS}) +endif() + target_compile_definitions(Falcor PUBLIC $<$:NDEBUG> @@ -893,6 +926,8 @@ target_compile_definitions(Falcor # Clang. $<$:_MSC_EXTENSIONS> # enable MS extensions # Falcor feature flags. + FALCOR_ENABLE_ASSERTS=$ + FALCOR_ENABLE_PROFILER=$ FALCOR_HAS_D3D12=$ FALCOR_HAS_VULKAN=$ FALCOR_HAS_AFTERMATH=$ @@ -903,9 +938,8 @@ target_compile_definitions(Falcor FALCOR_HAS_RTXDI=1 PRIVATE #$<$:_ITERATOR_DEBUG_LEVEL=0> - _PROJECT_DIR_="${CMAKE_CURRENT_SOURCE_DIR}/" + FALCOR_PROJECT_DIR="${CMAKE_SOURCE_DIR}/" FALCOR_DLL - FALCOR_REPORT_EXCEPTION_AS_ERROR=$ $<$:IMGUI_API=__declspec\(dllexport\)> $<$:IMGUI_API=__attribute__\(\(visibility\("default"\)\)\)> ) diff --git a/Source/Falcor/Core/API/Aftermath.cpp b/Source/Falcor/Core/API/Aftermath.cpp index 8d41694b5..eacf6f74d 100644 --- a/Source/Falcor/Core/API/Aftermath.cpp +++ b/Source/Falcor/Core/API/Aftermath.cpp @@ -81,12 +81,12 @@ static std::string getResultString(GFSDK_Aftermath_Result result) // Helper macro for checking Nsight Aftermath results and throwing exception // in case of a failure. -#define AFTERMATH_CHECK_ERROR(FC) \ - do \ - { \ - GFSDK_Aftermath_Result _result = FC; \ - if (!GFSDK_Aftermath_SUCCEED(_result)) \ - throw RuntimeError(getResultString(_result)); \ +#define AFTERMATH_CHECK_ERROR(FC) \ + do \ + { \ + GFSDK_Aftermath_Result _result = FC; \ + if (!GFSDK_Aftermath_SUCCEED(_result)) \ + FALCOR_THROW(getResultString(_result)); \ } while (0) static std::mutex sMutex; @@ -157,8 +157,14 @@ static void writeGpuCrashDumpToFile(const void* pGpuCrashDump, const uint32_t gp // Step 1: Generate the JSON and get the size. uint32_t jsonSize = 0; AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GpuCrashDump_GenerateJSON( - decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO, GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, - shaderDebugInfoLookupCallback, shaderLookupCallback, shaderSourceDebugInfoLookupCallback, nullptr, &jsonSize + decoder, + GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO, + GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, + shaderDebugInfoLookupCallback, + shaderLookupCallback, + shaderSourceDebugInfoLookupCallback, + nullptr, + &jsonSize )); // Step 2: Allocate a buffer and fetch the generated JSON. std::vector json(jsonSize); @@ -331,7 +337,9 @@ void enableAftermath() // Let the Nsight Aftermath library cache shader debug information GFSDK_Aftermath_GpuCrashDumpFeatureFlags_DeferDebugInfoCallbacks, // Callbacks - gpuCrashDumpCallback, shaderDebugInfoCallback, crashDumpDescriptionCallback, + gpuCrashDumpCallback, + shaderDebugInfoCallback, + crashDumpDescriptionCallback, // Do not resolve markers for now (they are embedded with string data) nullptr /* resolveMarkerCallback */, // User data diff --git a/Source/Falcor/Core/API/BlendState.cpp b/Source/Falcor/Core/API/BlendState.cpp index f1aa877ab..ffc8d3a7d 100644 --- a/Source/Falcor/Core/API/BlendState.cpp +++ b/Source/Falcor/Core/API/BlendState.cpp @@ -54,7 +54,7 @@ BlendState::Desc& BlendState::Desc::setRtParams( BlendFunc dstAlphaFunc ) { - checkArgument(rtIndex < mRtDesc.size(), "'rtIndex' ({}) is out of range. Must be smaller than {}.", rtIndex, mRtDesc.size()); + FALCOR_CHECK(rtIndex < mRtDesc.size(), "'rtIndex' ({}) is out of range. Must be smaller than {}.", rtIndex, mRtDesc.size()); mRtDesc[rtIndex].rgbBlendOp = rgbOp; mRtDesc[rtIndex].alphaBlendOp = alphaOp; @@ -73,7 +73,7 @@ BlendState::Desc& BlendState::Desc::setRenderTargetWriteMask( bool writeAlpha ) { - checkArgument(rtIndex < mRtDesc.size(), "'rtIndex' ({}) is out of range. Must be smaller than {}.", rtIndex, mRtDesc.size()); + FALCOR_CHECK(rtIndex < mRtDesc.size(), "'rtIndex' ({}) is out of range. Must be smaller than {}.", rtIndex, mRtDesc.size()); mRtDesc[rtIndex].writeMask.writeRed = writeRed; mRtDesc[rtIndex].writeMask.writeGreen = writeGreen; diff --git a/Source/Falcor/Core/API/BlitContext.cpp b/Source/Falcor/Core/API/BlitContext.cpp index 431d9b260..3c1b6f845 100644 --- a/Source/Falcor/Core/API/BlitContext.cpp +++ b/Source/Falcor/Core/API/BlitContext.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "BlitContext.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/API/Device.h" #include "Core/Program/Program.h" #include "Core/Pass/FullScreenPass.h" @@ -44,7 +44,7 @@ BlitContext::BlitContext(Device* pDevice) {"SRC_INT", "0"}, {"DST_INT", "0"}, }; - Program::Desc d; + ProgramDesc d; d.addShaderLibrary("Core/API/BlitReduction.3d.slang").vsEntry("vsMain").psEntry("psMain"); pPass = FullScreenPass::create(ref(pDevice), d, defines); pPass->breakStrongReferenceToDevice(); @@ -59,29 +59,29 @@ BlitContext::BlitContext(Device* pDevice) prevSrcReftScale = float2(-1.0f); Sampler::Desc desc; - desc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - desc.setReductionMode(Sampler::ReductionMode::Standard); - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); - pLinearSampler = Sampler::create(ref(pDevice), desc); + desc.setAddressingMode(TextureAddressingMode::Clamp, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); + desc.setReductionMode(TextureReductionMode::Standard); + desc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Point); + pLinearSampler = pDevice->createSampler(desc); pLinearSampler->breakStrongReferenceToDevice(); - desc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - pPointSampler = Sampler::create(ref(pDevice), desc); + desc.setFilterMode(TextureFilteringMode::Point, TextureFilteringMode::Point, TextureFilteringMode::Point); + pPointSampler = pDevice->createSampler(desc); pPointSampler->breakStrongReferenceToDevice(); // Min reductions. - desc.setReductionMode(Sampler::ReductionMode::Min); - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); - pLinearMinSampler = Sampler::create(ref(pDevice), desc); + desc.setReductionMode(TextureReductionMode::Min); + desc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Point); + pLinearMinSampler = pDevice->createSampler(desc); pLinearMinSampler->breakStrongReferenceToDevice(); - desc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - pPointMinSampler = Sampler::create(ref(pDevice), desc); + desc.setFilterMode(TextureFilteringMode::Point, TextureFilteringMode::Point, TextureFilteringMode::Point); + pPointMinSampler = pDevice->createSampler(desc); pPointMinSampler->breakStrongReferenceToDevice(); // Max reductions. - desc.setReductionMode(Sampler::ReductionMode::Max); - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); - pLinearMaxSampler = Sampler::create(ref(pDevice), desc); + desc.setReductionMode(TextureReductionMode::Max); + desc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Point); + pLinearMaxSampler = pDevice->createSampler(desc); pLinearMaxSampler->breakStrongReferenceToDevice(); - desc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - pPointMaxSampler = Sampler::create(ref(pDevice), desc); + desc.setFilterMode(TextureFilteringMode::Point, TextureFilteringMode::Point, TextureFilteringMode::Point); + pPointMaxSampler = pDevice->createSampler(desc); pPointMaxSampler->breakStrongReferenceToDevice(); const auto& pDefaultBlockReflection = pPass->getProgram()->getReflector()->getDefaultParameterBlock(); diff --git a/Source/Falcor/Core/API/BlitContext.h b/Source/Falcor/Core/API/BlitContext.h index e64c67225..bf52c3db9 100644 --- a/Source/Falcor/Core/API/BlitContext.h +++ b/Source/Falcor/Core/API/BlitContext.h @@ -54,13 +54,13 @@ struct BlitContext float2 prevSrcReftScale = float2(0, 0); // Variable offsets in constant buffer - UniformShaderVarOffset offsetVarOffset; - UniformShaderVarOffset scaleVarOffset; + TypedShaderVarOffset offsetVarOffset; + TypedShaderVarOffset scaleVarOffset; ProgramReflection::BindLocation texBindLoc; // Parameters for complex blit float4 prevComponentsTransform[4] = {float4(0), float4(0), float4(0), float4(0)}; - UniformShaderVarOffset compTransVarOffset[4]; + TypedShaderVarOffset compTransVarOffset[4]; BlitContext(Device* pDevice); }; diff --git a/Source/Falcor/Core/API/Buffer.cpp b/Source/Falcor/Core/API/Buffer.cpp index cf33e7ffe..e073e7b2a 100644 --- a/Source/Falcor/Core/API/Buffer.cpp +++ b/Source/Falcor/Core/API/Buffer.cpp @@ -29,62 +29,36 @@ #include "Device.h" #include "GFXAPI.h" #include "NativeHandleTraits.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "PythonHelpers.h" +#include "Core/Error.h" #include "Core/ObjectPython.h" #include "Core/Program/Program.h" #include "Core/Program/ShaderVar.h" #include "Utils/Logger.h" #include "Utils/Scripting/ScriptBindings.h" +#include "Utils/Scripting/ndarray.h" -#define GFX_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT (256) -#define GFX_TEXTURE_DATA_PLACEMENT_ALIGNMENT (512) +#if FALCOR_HAS_CUDA +#include "Utils/CudaUtils.h" +#endif namespace Falcor { // TODO: Replace with include? -void getGFXResourceState(Resource::BindFlags flags, gfx::ResourceState& defaultState, gfx::ResourceStateSet& allowedStates); +void getGFXResourceState(ResourceBindFlags flags, gfx::ResourceState& defaultState, gfx::ResourceStateSet& allowedStates); -static ref createStructuredFromType( - ref pDevice, - const ReflectionType* pType, - const std::string& varName, - uint32_t elementCount, - ResourceBindFlags bindFlags, - Buffer::CpuAccess cpuAccess, - const void* pInitData, - bool createCounter -) -{ - const ReflectionResourceType* pResourceType = pType->unwrapArray()->asResourceType(); - if (!pResourceType || pResourceType->getType() != ReflectionResourceType::Type::StructuredBuffer) - { - throw RuntimeError("Can't create a structured buffer from the variable '{}'. The variable is not a structured buffer.", varName); - } - - FALCOR_ASSERT(pResourceType->getSize() <= std::numeric_limits::max()); - return Buffer::createStructured( - pDevice, (uint32_t)pResourceType->getSize(), elementCount, bindFlags, cpuAccess, pInitData, createCounter - ); -} - -static void prepareGFXBufferDesc( - gfx::IBufferResource::Desc& bufDesc, - size_t size, - Resource::BindFlags bindFlags, - Buffer::CpuAccess cpuAccess -) +static void prepareGFXBufferDesc(gfx::IBufferResource::Desc& bufDesc, size_t size, ResourceBindFlags bindFlags, MemoryType memoryType) { bufDesc.sizeInBytes = size; - switch (cpuAccess) + switch (memoryType) { - case Buffer::CpuAccess::None: + case MemoryType::DeviceLocal: bufDesc.memoryType = gfx::MemoryType::DeviceLocal; break; - case Buffer::CpuAccess::Read: + case MemoryType::ReadBack: bufDesc.memoryType = gfx::MemoryType::ReadBack; break; - case Buffer::CpuAccess::Write: + case MemoryType::Upload: bufDesc.memoryType = gfx::MemoryType::Upload; break; default: @@ -92,23 +66,23 @@ static void prepareGFXBufferDesc( break; } getGFXResourceState(bindFlags, bufDesc.defaultState, bufDesc.allowedStates); - bufDesc.isShared = is_set(bindFlags, Buffer::BindFlags::Shared); + bufDesc.isShared = is_set(bindFlags, ResourceBindFlags::Shared); } -// TODO: This is also used in Device -Slang::ComPtr createBuffer( +// TODO: This is also used in GpuMemoryHeap +Slang::ComPtr createBufferResource( ref pDevice, Buffer::State initState, size_t size, - Buffer::BindFlags bindFlags, - Buffer::CpuAccess cpuAccess + ResourceBindFlags bindFlags, + MemoryType memoryType ) { FALCOR_ASSERT(pDevice); // Create the buffer gfx::IBufferResource::Desc bufDesc = {}; - prepareGFXBufferDesc(bufDesc, size, bindFlags, cpuAccess); + prepareGFXBufferDesc(bufDesc, size, bindFlags, memoryType); Slang::ComPtr pApiHandle; FALCOR_GFX_CALL(pDevice->getGfxDevice()->createBufferResource(bufDesc, nullptr, pApiHandle.writeRef())); @@ -117,22 +91,10 @@ Slang::ComPtr createBuffer( return pApiHandle; } -static size_t getBufferDataAlignment(const Buffer* pBuffer) +Buffer::Buffer(ref pDevice, size_t size, ResourceBindFlags bindFlags, MemoryType memoryType, const void* pInitData) + : Resource(pDevice, Type::Buffer, bindFlags, size), mMemoryType(memoryType) { - // This in order of the alignment size - const auto& bindFlags = pBuffer->getBindFlags(); - if (is_set(bindFlags, Buffer::BindFlags::Constant)) - return GFX_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT; - if (is_set(bindFlags, Buffer::BindFlags::Index)) - return sizeof(uint32_t); // This actually depends on the size of the index, but we can handle losing 2 bytes - - return GFX_TEXTURE_DATA_PLACEMENT_ALIGNMENT; -} - -Buffer::Buffer(ref pDevice, size_t size, BindFlags bindFlags, CpuAccess cpuAccess, const void* pInitData) - : Resource(pDevice, Type::Buffer, bindFlags, size), mCpuAccess(cpuAccess) -{ - checkArgument(size > 0, "Can't create GPU buffer of size zero"); + FALCOR_CHECK(size > 0, "Can't create GPU buffer of size zero"); // Check that buffer size is within 4GB limit. Larger buffers are currently not well supported in D3D12. // TODO: Revisit this check in the future. @@ -141,183 +103,88 @@ Buffer::Buffer(ref pDevice, size_t size, BindFlags bindFlags, CpuAccess logWarning("Creating GPU buffer of size {} bytes. Buffers above 4GB are not currently well supported.", size); } - if (mCpuAccess != CpuAccess::None && is_set(mBindFlags, BindFlags::Shared)) + if (mMemoryType != MemoryType::DeviceLocal && is_set(mBindFlags, ResourceBindFlags::Shared)) { - throw RuntimeError("Can't create shared resource with CPU access other than 'None'."); + FALCOR_THROW("Can't create shared resource with CPU access other than 'None'."); } - if (mBindFlags == BindFlags::Constant) + mSize = align_to(mpDevice->getBufferDataAlignment(bindFlags), mSize); + + if (mMemoryType == MemoryType::DeviceLocal) { - mSize = align_to((size_t)GFX_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, mSize); + mState.global = Resource::State::Common; + if (is_set(mBindFlags, ResourceBindFlags::AccelerationStructure)) + mState.global = Resource::State::AccelerationStructure; } - - if (mCpuAccess == CpuAccess::Write) + else if (mMemoryType == MemoryType::Upload) { mState.global = Resource::State::GenericRead; - if (pInitData) // Else the allocation will happen when updating the data - { - mDynamicData = mpDevice->getUploadHeap()->allocate(mSize, getBufferDataAlignment(this)); - mGfxBufferResource = mDynamicData.gfxBufferResource; - mGpuVaOffset = mDynamicData.offset; - } } - else if (mCpuAccess == CpuAccess::Read && mBindFlags == BindFlags::None) + else if (mMemoryType == MemoryType::ReadBack) { mState.global = Resource::State::CopyDest; - mGfxBufferResource = createBuffer(mpDevice, mState.global, mSize, mBindFlags, mCpuAccess); - } - else - { - mState.global = Resource::State::Common; - if (is_set(mBindFlags, BindFlags::AccelerationStructure)) - mState.global = Resource::State::AccelerationStructure; - mGfxBufferResource = createBuffer(mpDevice, mState.global, mSize, mBindFlags, mCpuAccess); } + mGfxBufferResource = createBufferResource(mpDevice, mState.global, mSize, mBindFlags, mMemoryType); + if (pInitData) setBlob(pInitData, 0, size); - mElementCount = uint32_t(size); -} -ref Buffer::create(ref pDevice, size_t size, BindFlags bindFlags, CpuAccess cpuAccess, const void* pInitData) -{ - return ref(new Buffer(pDevice, size, bindFlags, cpuAccess, pInitData)); + mElementCount = uint32_t(size); } -ref Buffer::createTyped( +Buffer::Buffer( ref pDevice, ResourceFormat format, uint32_t elementCount, - BindFlags bindFlags, - CpuAccess cpuAccess, + ResourceBindFlags bindFlags, + MemoryType memoryType, const void* pInitData ) + : Buffer(pDevice, (size_t)getFormatBytesPerBlock(format) * elementCount, bindFlags, memoryType, pInitData) { - size_t size = (size_t)elementCount * getFormatBytesPerBlock(format); - ref pBuffer = create(pDevice, size, bindFlags, cpuAccess, pInitData); - FALCOR_ASSERT(pBuffer); - - pBuffer->mFormat = format; - pBuffer->mElementCount = elementCount; - return pBuffer; + mFormat = format; + mElementCount = elementCount; } -ref Buffer::createStructured( +Buffer::Buffer( ref pDevice, uint32_t structSize, uint32_t elementCount, ResourceBindFlags bindFlags, - CpuAccess cpuAccess, + MemoryType memoryType, const void* pInitData, bool createCounter ) + : Buffer(pDevice, (size_t)structSize * elementCount, bindFlags, memoryType, pInitData) { - size_t size = (size_t)structSize * elementCount; - ref pBuffer = create(pDevice, size, bindFlags, cpuAccess, pInitData); - FALCOR_ASSERT(pBuffer); - - pBuffer->mElementCount = elementCount; - pBuffer->mStructSize = structSize; + mElementCount = elementCount; + mStructSize = structSize; static const uint32_t zero = 0; if (createCounter) { - pBuffer->mpUAVCounter = - Buffer::create(pDevice, sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, &zero); - } - return pBuffer; -} - -ref Buffer::createStructured( - ref pDevice, - const ShaderVar& shaderVar, - uint32_t elementCount, - ResourceBindFlags bindFlags, - CpuAccess cpuAccess, - const void* pInitData, - bool createCounter -) -{ - return createStructuredFromType( - pDevice, shaderVar.getType().get(), "", elementCount, bindFlags, cpuAccess, pInitData, createCounter - ); -} - -ref Buffer::createStructured( - ref pDevice, - const Program* pProgram, - const std::string& name, - uint32_t elementCount, - ResourceBindFlags bindFlags, - CpuAccess cpuAccess, - const void* pInitData, - bool createCounter -) -{ - const auto& pDefaultBlock = pProgram->getReflector()->getDefaultParameterBlock(); - const ReflectionVar* pVar = pDefaultBlock ? pDefaultBlock->getResource(name).get() : nullptr; - if (pVar == nullptr) - { - throw RuntimeError("Can't find a structured buffer named '{}' in the program", name); + mpUAVCounter = make_ref(mpDevice, sizeof(uint32_t), ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, &zero); } - return createStructuredFromType(pDevice, pVar->getType().get(), name, elementCount, bindFlags, cpuAccess, pInitData, createCounter); } -ref Buffer::aliasResource( - ref pDevice, - ref pBaseResource, - GpuAddress offset, - size_t size, - Resource::BindFlags bindFlags -) -{ - FALCOR_ASSERT(pBaseResource); - CpuAccess cpuAccess = pBaseResource->asBuffer() ? pBaseResource->asBuffer()->getCpuAccess() : CpuAccess::None; - checkArgument( - cpuAccess != CpuAccess::None, "'pBaseResource' has CpuAccess:{} which is illegal. Aliased resources must have CpuAccess::None.", - to_string(cpuAccess) - ); - checkArgument( - (pBaseResource->getBindFlags() & bindFlags) != bindFlags, "'bindFlags' ({}) don't match aliased resource bind flags {}.", - to_string(bindFlags), to_string(pBaseResource->getBindFlags()) - ); - if (offset >= pBaseResource->getSize() || (offset + size) >= pBaseResource->getSize()) - { - throw ArgumentError( - "'offset' ({}) and 'size' ({}) don't fit inside the aliased resource size {}.", offset, size, pBaseResource->getSize() - ); - } - - ref pBuffer = create(pDevice, size, bindFlags, CpuAccess::None); - pBuffer->mpAliasedResource = pBaseResource; - pBuffer->mGfxBufferResource = pBaseResource->mGfxBufferResource; - pBuffer->mGpuVaOffset = offset; - return pBuffer; -} - -ref Buffer::createFromResource( - ref pDevice, - gfx::IBufferResource* pResource, - size_t size, - Resource::BindFlags bindFlags, - CpuAccess cpuAccess -) +// TODO: Its wasteful to create a buffer just to replace it afterwards with the supplied one! +Buffer::Buffer(ref pDevice, gfx::IBufferResource* pResource, size_t size, ResourceBindFlags bindFlags, MemoryType memoryType) + : Buffer(pDevice, size, bindFlags, memoryType, nullptr) { FALCOR_ASSERT(pResource); - ref pBuffer = create(pDevice, size, bindFlags, cpuAccess); - pBuffer->mGfxBufferResource = pResource; - return pBuffer; + mGfxBufferResource = pResource; } -ref Buffer::createFromNativeHandle( - ref pDevice, +inline Slang::ComPtr gfxResourceFromNativeHandle( + Device* pDevice, NativeHandle handle, size_t size, - Resource::BindFlags bindFlags, - CpuAccess cpuAccess + ResourceBindFlags bindFlags, + MemoryType memoryType ) { gfx::IBufferResource::Desc bufDesc = {}; - prepareGFXBufferDesc(bufDesc, size, bindFlags, cpuAccess); + prepareGFXBufferDesc(bufDesc, size, bindFlags, memoryType); gfx::InteropHandle gfxNativeHandle = {}; #if FALCOR_HAS_D3D12 @@ -336,29 +203,21 @@ ref Buffer::createFromNativeHandle( #endif if (gfxNativeHandle.api == gfx::InteropHandleAPI::Unknown) - { - // TODO: throw error - } + FALCOR_THROW("Unknown native handle type"); Slang::ComPtr gfxBuffer; FALCOR_GFX_CALL(pDevice->getGfxDevice()->createBufferFromNativeHandle(gfxNativeHandle, bufDesc, gfxBuffer.writeRef())); - return Buffer::createFromResource(pDevice, gfxBuffer, size, bindFlags, cpuAccess); + return gfxBuffer; } +Buffer::Buffer(ref pDevice, NativeHandle handle, size_t size, ResourceBindFlags bindFlags, MemoryType memoryType) + : Buffer(pDevice, gfxResourceFromNativeHandle(pDevice.get(), handle, size, bindFlags, memoryType), size, bindFlags, memoryType) +{} + Buffer::~Buffer() { - if (mpAliasedResource) - return; - - if (mDynamicData.gfxBufferResource) - { - mpDevice->getUploadHeap()->release(mDynamicData); - } - else - { - mpDevice->releaseResource(mGfxBufferResource); - } + mpDevice->releaseResource(mGfxBufferResource); } gfx::IResource* Buffer::getGfxResource() const @@ -398,98 +257,73 @@ ref Buffer::getUAV() void Buffer::setBlob(const void* pData, size_t offset, size_t size) { - if (offset + size > mSize) - { - throw ArgumentError("'offset' ({}) and 'size' ({}) don't fit the buffer size {}.", offset, size, mSize); - } + FALCOR_CHECK(offset + size <= mSize, "'offset' ({}) and 'size' ({}) don't fit the buffer size {}.", offset, size, mSize); - if (mCpuAccess == CpuAccess::Write) + if (mMemoryType == MemoryType::Upload) { - uint8_t* pDst = (uint8_t*)map(MapType::WriteDiscard) + offset; + bool wasMapped = mMappedPtr != nullptr; + uint8_t* pDst = (uint8_t*)map(MapType::Write) + offset; std::memcpy(pDst, pData, size); + if (!wasMapped) + unmap(); + // TODO we should probably use a barrier instead + invalidateViews(); } - else + else if (mMemoryType == MemoryType::DeviceLocal) { mpDevice->getRenderContext()->updateBuffer(this, pData, offset, size); } + else if (mMemoryType == MemoryType::ReadBack) + { + FALCOR_THROW("Cannot set data to a buffer that was created with MemoryType::ReadBack."); + } } -void* Buffer::map(MapType type) +void Buffer::getBlob(void* pData, size_t offset, size_t size) const { - if (type == MapType::Write) + FALCOR_CHECK(offset + size <= mSize, "'offset' ({}) and 'size' ({}) don't fit the buffer size {}.", offset, size, mSize); + + if (mMemoryType == MemoryType::ReadBack) { - checkArgument( - mCpuAccess == CpuAccess::Write, "Trying to map a buffer for write, but it wasn't created with the write permissions." - ); - return mDynamicData.pData; + bool wasMapped = mMappedPtr != nullptr; + const uint8_t* pSrc = (const uint8_t*)map(MapType::Read) + offset; + std::memcpy(pData, pSrc, size); + if (!wasMapped) + unmap(); } - else if (type == MapType::WriteDiscard) + else if (mMemoryType == MemoryType::DeviceLocal) { - checkArgument( - mCpuAccess == CpuAccess::Write, "Trying to map a buffer for write, but it wasn't created with the write permissions." - ); - - // Allocate a new buffer - if (mDynamicData.gfxBufferResource) - { - mpDevice->getUploadHeap()->release(mDynamicData); - } - mDynamicData = mpDevice->getUploadHeap()->allocate(mSize, getBufferDataAlignment(this)); - mGfxBufferResource = mDynamicData.gfxBufferResource; - mGpuVaOffset = mDynamicData.offset; - invalidateViews(); - return mDynamicData.pData; + mpDevice->getRenderContext()->readBuffer(this, pData, offset, size); } - else + else if (mMemoryType == MemoryType::Upload) { - FALCOR_ASSERT(type == MapType::Read); - - if (mCpuAccess == CpuAccess::Write) - { - // Buffers on the upload heap are already mapped, just return the ptr. - FALCOR_ASSERT(mDynamicData.gfxBufferResource); - FALCOR_ASSERT(mDynamicData.pData); - return mDynamicData.pData; - } - else if (mCpuAccess == CpuAccess::Read) - { - FALCOR_ASSERT(mBindFlags == BindFlags::None); - void* pData = nullptr; - FALCOR_GFX_CALL(mGfxBufferResource->map(nullptr, &pData)); - return pData; - } - else - { - // For buffers without CPU access we must copy the contents to a staging buffer. - logWarning( - "Buffer::map() performance warning - using staging resource which require us to flush the pipeline and wait for the GPU to " - "finish its work" - ); - if (mpStagingResource == nullptr) - { - mpStagingResource = Buffer::create(mpDevice, mSize, Buffer::BindFlags::None, Buffer::CpuAccess::Read, nullptr); - } - - // Copy the buffer and flush the pipeline - RenderContext* pContext = mpDevice->getRenderContext(); - FALCOR_ASSERT(mGpuVaOffset == 0); - pContext->copyResource(mpStagingResource.get(), this); - pContext->flush(true); - return mpStagingResource->map(MapType::Read); - } + FALCOR_THROW("Cannot get data from a buffer that was created with MemoryType::Upload."); } } -void Buffer::unmap() +void* Buffer::map(MapType type) const { - // Only unmap read buffers, write buffers are persistently mapped. - if (mpStagingResource) - { - FALCOR_GFX_CALL(mpStagingResource->mGfxBufferResource->unmap(nullptr)); - } - else if (mCpuAccess == CpuAccess::Read) + if (type == MapType::WriteDiscard) + FALCOR_THROW("MapType::WriteDiscard not supported anymore"); + + if (type == MapType::Write && mMemoryType != MemoryType::Upload) + FALCOR_THROW("Trying to map a buffer for writing, but it wasn't created with the write permissions."); + + if (type == MapType::Read && mMemoryType != MemoryType::ReadBack) + FALCOR_THROW("Trying to map a buffer for reading, but it wasn't created with the read permissions."); + + if (!mMappedPtr) + FALCOR_GFX_CALL(mGfxBufferResource->map(nullptr, &mMappedPtr)); + + return mMappedPtr; +} + +void Buffer::unmap() const +{ + if (mMappedPtr) { FALCOR_GFX_CALL(mGfxBufferResource->unmap(nullptr)); + mMappedPtr = nullptr; } } @@ -500,7 +334,7 @@ uint32_t Buffer::getElementSize() const if (mFormat == ResourceFormat::Unknown) return 1; - throw RuntimeError("Inferring element size from resource format is not implemented"); + FALCOR_THROW("Inferring element size from resource format is not implemented"); } bool Buffer::adjustSizeOffsetParams(size_t& size, size_t& offset) const @@ -521,19 +355,120 @@ bool Buffer::adjustSizeOffsetParams(size_t& size, size_t& offset) const uint64_t Buffer::getGpuAddress() const { - // slang-gfx backend does not includ the mGpuVaOffset. - return mGpuVaOffset + mGfxBufferResource->getDeviceAddress(); + return mGfxBufferResource->getDeviceAddress(); } +#if FALCOR_HAS_CUDA +cuda_utils::ExternalMemory* Buffer::getCudaMemory() const +{ + if (!mCudaMemory) + mCudaMemory = make_ref(ref(const_cast(this))); + return mCudaMemory.get(); +} +#endif + +inline pybind11::ndarray buffer_to_numpy(const Buffer& self) +{ + size_t bufferSize = self.getSize(); + void* cpuData = new uint8_t[bufferSize]; + self.getBlob(cpuData, 0, bufferSize); + + pybind11::capsule owner(cpuData, [](void* p) noexcept { delete[] reinterpret_cast(p); }); + + if (auto dtype = resourceFormatToDtype(self.getFormat())) + { + uint32_t channelCount = getFormatChannelCount(self.getFormat()); + if (channelCount == 1) + { + pybind11::size_t shape[1] = {self.getElementCount()}; + return pybind11::ndarray(cpuData, 1, shape, owner, nullptr, *dtype, pybind11::device::cpu::value); + } + else + { + pybind11::size_t shape[2] = {self.getElementCount(), channelCount}; + return pybind11::ndarray(cpuData, 2, shape, owner, nullptr, *dtype, pybind11::device::cpu::value); + } + } + else + { + pybind11::size_t shape[1] = {bufferSize}; + return pybind11::ndarray( + cpuData, 1, shape, owner, nullptr, pybind11::dtype(), pybind11::device::cpu::value + ); + } +} + +inline void buffer_from_numpy(Buffer& self, pybind11::ndarray data) +{ + FALCOR_CHECK(isNdarrayContiguous(data), "numpy array is not contiguous"); + + size_t bufferSize = self.getSize(); + size_t dataSize = getNdarrayByteSize(data); + FALCOR_CHECK(dataSize <= bufferSize, "numpy array is larger than the buffer ({} > {})", dataSize, bufferSize); + + self.setBlob(data.data(), 0, dataSize); +} + +#if FALCOR_HAS_CUDA +inline pybind11::ndarray buffer_to_torch(const Buffer& self, std::vector shape, DataType dtype) +{ + cuda_utils::ExternalMemory* cudaMemory = self.getCudaMemory(); + + return pybind11::ndarray( + cudaMemory->getMappedData(), shape.size(), shape.data(), nullptr, nullptr, dataTypeToDtype(dtype), pybind11::device::cuda::value + ); +} + +inline void buffer_from_torch(Buffer& self, pybind11::ndarray data) +{ + FALCOR_CHECK(isNdarrayContiguous(data), "torch tensor is not contiguous"); + FALCOR_CHECK(data.device_type() == pybind11::device::cuda::value, "torch tensor is not on the device"); + + cuda_utils::ExternalMemory* cudaMemory = self.getCudaMemory(); + size_t dataSize = getNdarrayByteSize(data); + FALCOR_CHECK(dataSize <= cudaMemory->getSize(), "torch tensor is larger than the buffer ({} > {})", dataSize, cudaMemory->getSize()); + + cuda_utils::memcpyDeviceToDevice(cudaMemory->getMappedData(), data.data(), dataSize); +} + +inline void buffer_copy_to_torch(Buffer& self, pybind11::ndarray& data) +{ + FALCOR_CHECK(isNdarrayContiguous(data), "torch tensor is not contiguous"); + FALCOR_CHECK(data.device_type() == pybind11::device::cuda::value, "torch tensor is not on the device"); + + cuda_utils::ExternalMemory* cudaMemory = self.getCudaMemory(); + size_t dataSize = getNdarrayByteSize(data); + FALCOR_CHECK(dataSize >= cudaMemory->getSize(), "torch tensor is smaller than the buffer ({} < {})", dataSize, cudaMemory->getSize()); + + cuda_utils::memcpyDeviceToDevice(data.data(), cudaMemory->getMappedData(), dataSize); +} +#endif + FALCOR_SCRIPT_BINDING(Buffer) { + using namespace pybind11::literals; + + FALCOR_SCRIPT_BINDING_DEPENDENCY(Types) FALCOR_SCRIPT_BINDING_DEPENDENCY(Resource) - pybind11::class_> buffer(m, "Buffer"); + pybind11::falcor_enum(m, "MemoryType"); - pybind11::enum_ cpuAccess(buffer, "CpuAccess"); - cpuAccess.value("None_", Buffer::CpuAccess::None); - cpuAccess.value("Read", Buffer::CpuAccess::Read); - cpuAccess.value("Write", Buffer::CpuAccess::Write); + pybind11::class_> buffer(m, "Buffer"); + buffer.def_property_readonly("memory_type", &Buffer::getMemoryType); + buffer.def_property_readonly("size", &Buffer::getSize); + buffer.def_property_readonly("is_typed", &Buffer::isTyped); + buffer.def_property_readonly("is_structured", &Buffer::isStructured); + buffer.def_property_readonly("format", &Buffer::getFormat); + buffer.def_property_readonly("element_size", &Buffer::getElementSize); + buffer.def_property_readonly("element_count", &Buffer::getElementCount); + buffer.def_property_readonly("struct_size", &Buffer::getStructSize); + + buffer.def("to_numpy", buffer_to_numpy); + buffer.def("from_numpy", buffer_from_numpy, "data"_a); +#if FALCOR_HAS_CUDA + buffer.def("to_torch", buffer_to_torch, "shape"_a, "dtype"_a = DataType::float32); + buffer.def("from_torch", buffer_from_torch, "data"_a); + buffer.def("copy_to_torch", buffer_copy_to_torch, "data"_a); +#endif } } // namespace Falcor diff --git a/Source/Falcor/Core/API/Buffer.h b/Source/Falcor/Core/API/Buffer.h index e454062e0..4e9a449c1 100644 --- a/Source/Falcor/Core/API/Buffer.h +++ b/Source/Falcor/Core/API/Buffer.h @@ -26,8 +26,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "GpuMemoryHeap.h" #include "Core/Macros.h" +#include "Core/Enum.h" #include "Resource.h" #include "ResourceViews.h" @@ -36,6 +36,11 @@ namespace Falcor class Program; struct ShaderVar; +namespace cuda_utils +{ +class ExternalMemory; +}; + namespace detail { /** @@ -109,6 +114,29 @@ CASE(float3, ResourceFormat::RGB32Float); #undef CASE } // namespace detail +/// Buffer memory types. +enum class MemoryType +{ + DeviceLocal, ///< Device local memory. The buffer can be updated using Buffer::setBlob(). + Upload, ///< Upload memory. The buffer can be mapped for CPU writes. + ReadBack, ///< Read-back memory. The buffer can be mapped for CPU reads. + + // NOTE: In older version of Falcor this enum used to be Buffer::CpuAccess. + // Use the following mapping to update your code: + // - CpuAccess::None -> MemoryType::DeviceLocal + // - CpuAccess::Write -> MemoryType::Upload + // - CpuAccess::Read -> MemoryType::ReadBack +}; +FALCOR_ENUM_INFO( + MemoryType, + { + {MemoryType::DeviceLocal, "DeviceLocal"}, + {MemoryType::Upload, "Upload"}, + {MemoryType::ReadBack, "ReadBack"}, + } +); +FALCOR_ENUM_REGISTER(MemoryType); + /** * Low-level buffer object * This class abstracts the API's buffer creation and management @@ -117,184 +145,45 @@ class FALCOR_API Buffer : public Resource { FALCOR_OBJECT(Buffer) public: - /** - * Buffer access flags. - * These flags are hints the driver how the buffer will be used. - */ - enum class CpuAccess - { - None, ///< The CPU can't access the buffer's content. The buffer can be updated using Buffer#updateData() - Write, ///< The buffer can be mapped for CPU writes - Read, ///< The buffer can be mapped for CPU reads - }; - enum class MapType { Read, ///< Map the buffer for read access. - Write, ///< Map the buffer for write access. Buffer had to be created with CpuAccess::Write flag. - WriteDiscard, ///< Map the buffer for write access, discarding the previous content of the entire buffer. Buffer had to be created - ///< with CpuAccess::Write flag. + Write, ///< Map the buffer for write access. + WriteDiscard, ///< Deprecated and not supported. }; - ~Buffer(); - - /** - * Create a new buffer. - * @param[in] size Size of the buffer in bytes. - * @param[in] bindFlags Buffer bind flags. - * @param[in] cpuAccess Flags indicating how the buffer can be updated. - * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer size should be at least 'size' bytes. - * @return A pointer to a new buffer object, or throws an exception if creation failed. - */ - static ref create( - ref pDevice, - size_t size, - Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, - CpuAccess cpuAccess = Buffer::CpuAccess::None, - const void* pInitData = nullptr - ); + /// Constructor for raw buffer. + Buffer(ref pDevice, size_t size, ResourceBindFlags bindFlags, MemoryType memoryType, const void* pInitData); - /** - * Create a new typed buffer. - * @param[in] format Typed buffer format. - * @param[in] elementCount Number of elements. - * @param[in] bindFlags Buffer bind flags. - * @param[in] cpuAccess Flags indicating how the buffer can be updated. - * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer should hold at least 'elementCount' elements. - * @return A pointer to a new buffer object, or throws an exception if creation failed. - */ - static ref createTyped( + /// Constructor for typed buffer. + Buffer( ref pDevice, ResourceFormat format, uint32_t elementCount, - Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, - CpuAccess cpuAccess = Buffer::CpuAccess::None, - const void* pInitData = nullptr + ResourceBindFlags bindFlags, + MemoryType memoryType, + const void* pInitData ); - /** - * Create a new typed buffer. The format is deduced from the template parameter. - * @param[in] elementCount Number of elements. - * @param[in] bindFlags Buffer bind flags. - * @param[in] cpuAccess Flags indicating how the buffer can be updated. - * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer should hold at least 'elementCount' elements. - * @return A pointer to a new buffer object, or throws an exception if creation failed. - */ - template - static ref createTyped( - ref pDevice, - uint32_t elementCount, - Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, - CpuAccess cpuAccess = Buffer::CpuAccess::None, - const T* pInitData = nullptr - ) - { - return createTyped(pDevice, detail::FormatForElementType::kFormat, elementCount, bindFlags, cpuAccess, pInitData); - } - - /** - * Create a new structured buffer. - * @param[in] structSize Size of the struct in bytes. - * @param[in] elementCount Number of elements. - * @param[in] bindFlags Buffer bind flags. - * @param[in] cpuAccess Flags indicating how the buffer can be updated. - * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer should hold at least 'elementCount' elements. - * @param[in] createCounter True if the associated UAV counter should be created. - * @return A pointer to a new buffer object, or throws an exception if creation failed. - */ - static ref createStructured( + /// Constructor for structured buffer. + Buffer( ref pDevice, uint32_t structSize, uint32_t elementCount, - ResourceBindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, - CpuAccess cpuAccess = Buffer::CpuAccess::None, - const void* pInitData = nullptr, - bool createCounter = true + ResourceBindFlags bindFlags, + MemoryType memoryType, + const void* pInitData, + bool createCounter ); - /** - * Create a new structured buffer. - * @param[in] pProgram Program declaring the buffer. - * @param[in] name Variable name in the program. - * @param[in] elementCount Number of elements. - * @param[in] bindFlags Buffer bind flags. - * @param[in] cpuAccess Flags indicating how the buffer can be updated. - * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer should hold at least 'elementCount' elements. - * @param[in] createCounter True if the associated UAV counter should be created. - * @return A pointer to a new buffer object, or throws an exception if creation failed. - */ - static ref createStructured( - ref pDevice, - const Program* pProgram, - const std::string& name, - uint32_t elementCount, - ResourceBindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, - CpuAccess cpuAccess = Buffer::CpuAccess::None, - const void* pInitData = nullptr, - bool createCounter = true - ); + /// Constructor with existing resource. + Buffer(ref pDevice, gfx::IBufferResource* pResource, size_t size, ResourceBindFlags bindFlags, MemoryType memoryType); - /** - * Create a new structured buffer. - * @param[in] shaderVar ShaderVar pointing to the buffer variable. - * @param[in] elementCount Number of elements. - * @param[in] bindFlags Buffer bind flags. - * @param[in] cpuAccess Flags indicating how the buffer can be updated. - * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer should hold at least 'elementCount' elements. - * @param[in] createCounter True if the associated UAV counter should be created. - * @return A pointer to a new buffer object, or throws an exception if creation failed. - */ - static ref createStructured( - ref pDevice, - const ShaderVar& shaderVar, - uint32_t elementCount, - ResourceBindFlags bindFlags = Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, - CpuAccess cpuAccess = Buffer::CpuAccess::None, - const void* pInitData = nullptr, - bool createCounter = true - ); + /// Constructor with native handle. + Buffer(ref pDevice, NativeHandle handle, size_t size, ResourceBindFlags bindFlags, MemoryType memoryType); - static ref aliasResource( - ref pDevice, - ref pBaseResource, - GpuAddress offset, - size_t size, - Resource::BindFlags bindFlags - ); - - /** - * Create a new buffer from an existing resource. - * @param[in] pResource Already allocated resource. - * @param[in] size The size of the buffer in bytes. - * @param[in] bindFlags Buffer bind flags. Flags must match the bind flags of the original resource. - * @param[in] cpuAccess Flags indicating how the buffer can be updated. Flags must match those of the heap the original resource is - * allocated on. - * @return A pointer to a new buffer object, or throws an exception if creation failed. - */ - static ref createFromResource( - ref pDevice, - gfx::IBufferResource* pResource, - size_t size, - Resource::BindFlags bindFlags, - CpuAccess cpuAccess - ); - - /** - * Create a new buffer from an existing native handle. - * @param[in] handle Handle of already allocated resource. - * @param[in] size The size of the buffer in bytes. - * @param[in] bindFlags Buffer bind flags. Flags must match the bind flags of the original resource. - * @param[in] cpuAccess Flags indicating how the buffer can be updated. Flags must match those of the heap the original resource is - * allocated on. - * @return A pointer to a new buffer object, or throws an exception if creation failed. - */ - static ref createFromNativeHandle( - ref pDevice, - NativeHandle handle, - size_t size, - Resource::BindFlags bindFlags, - CpuAccess cpuAccess - ); + /// Destructor. + ~Buffer(); gfx::IBufferResource* getGfxBufferResource() const { return mGfxBufferResource; } @@ -343,9 +232,12 @@ class FALCOR_API Buffer : public Resource virtual void setBlob(const void* pData, size_t offset, size_t size); /** - * Get the offset from the beginning of the GPU resource + * Read the buffer's data + * @param pData Pointer to the destination data. + * @param offset Byte offset into the source buffer, indicating where to start copy from. + * @param size Number of bytes to copy. */ - uint64_t getGpuAddressOffset() const { return mGpuVaOffset; }; + void getBlob(void* pData, size_t offset, size_t size) const; /** * Get the GPU address (this includes the offset) @@ -380,19 +272,16 @@ class FALCOR_API Buffer : public Resource /** * Map the buffer. - * - * The low-level behavior depends on MapType and the CpuAccess flags of the buffer: - * - For CPU accessible buffers, the caller should ensure CPU/GPU memory accesses do not conflict. - * - For GPU-only buffers, map for read will create an internal staging buffer that is safe to read. - * - Mapping a CPU write buffer for WriteDiscard will cause the buffer to be internally re-allocated, - * causing its address range to change and invalidating all previous views to the buffer. + * Only buffers with MemoryType::Upload or MemoryType::ReadBack can be mapped. + * To map a buffer with MemoryType::Upload, use MapType::Write. + * To map a buffer with MemoryType::ReadBack, use MapType::Read. */ - void* map(MapType Type); + void* map(MapType Type) const; /** * Unmap the buffer */ - void unmap(); + void unmap() const; /** * Get safe offset and size values @@ -400,9 +289,9 @@ class FALCOR_API Buffer : public Resource bool adjustSizeOffsetParams(size_t& size, size_t& offset) const; /** - * Get the CPU access flags + * Get the memory type */ - CpuAccess getCpuAccess() const { return mCpuAccess; } + MemoryType getMemoryType() const { return mMemoryType; } /** * Check if this is a typed buffer @@ -420,31 +309,54 @@ class FALCOR_API Buffer : public Resource setBlob(&value, sizeof(T) * index, sizeof(T)); } -protected: - Buffer(ref pDevice, size_t size, BindFlags bindFlags, CpuAccess cpuAccess, const void* pInitData); + template + std::vector getElements(uint32_t firstElement = 0, uint32_t elementCount = 0) const + { + if (elementCount == 0) + elementCount = (mSize / sizeof(T)) - firstElement; + + std::vector data(elementCount); + getBlob(data.data(), firstElement * sizeof(T), elementCount * sizeof(T)); + return data; + } + + template + T getElement(uint32_t index) const + { + T data; + getBlob(&data, index * sizeof(T), sizeof(T)); + return data; + } + +#if FALCOR_HAS_CUDA + cuda_utils::ExternalMemory* getCudaMemory() const; +#endif +protected: Slang::ComPtr mGfxBufferResource; - CpuAccess mCpuAccess; - GpuMemoryHeap::Allocation mDynamicData; - ref mpStagingResource; // For buffers that have both CPU read flag and can be used by the GPU - ref mpAliasedResource; + MemoryType mMemoryType; uint32_t mElementCount = 0; ResourceFormat mFormat = ResourceFormat::Unknown; uint32_t mStructSize = 0; ref mpUAVCounter; // For structured-buffers + mutable void* mMappedPtr = nullptr; + +#if FALCOR_HAS_CUDA + mutable ref mCudaMemory; +#endif }; -inline std::string to_string(Buffer::CpuAccess c) +inline std::string to_string(MemoryType c) { -#define a2s(ca_) \ - case Buffer::CpuAccess::ca_: \ +#define a2s(ca_) \ + case MemoryType::ca_: \ return #ca_; switch (c) { - a2s(None); - a2s(Write); - a2s(Read); + a2s(DeviceLocal); + a2s(Upload); + a2s(ReadBack); default: FALCOR_UNREACHABLE(); return ""; diff --git a/Source/Falcor/Core/API/ComputeContext.cpp b/Source/Falcor/Core/API/ComputeContext.cpp index 0b6e6b0c3..cbdc9ae2f 100644 --- a/Source/Falcor/Core/API/ComputeContext.cpp +++ b/Source/Falcor/Core/API/ComputeContext.cpp @@ -39,24 +39,24 @@ ComputeContext::ComputeContext(Device* pDevice, gfx::ICommandQueue* pQueue) : Co ComputeContext::~ComputeContext() {} -void ComputeContext::dispatch(ComputeState* pState, ComputeVars* pVars, const uint3& dispatchSize) +void ComputeContext::dispatch(ComputeState* pState, ProgramVars* pVars, const uint3& dispatchSize) { pVars->prepareDescriptorSets(this); auto computeEncoder = mpLowLevelData->getComputeCommandEncoder(); FALCOR_GFX_CALL(computeEncoder->bindPipelineWithRootObject(pState->getCSO(pVars)->getGfxPipelineState(), pVars->getShaderObject())); - computeEncoder->dispatchCompute((int)dispatchSize.x, (int)dispatchSize.y, (int)dispatchSize.z); + FALCOR_GFX_CALL(computeEncoder->dispatchCompute((int)dispatchSize.x, (int)dispatchSize.y, (int)dispatchSize.z)); mCommandsPending = true; } -void ComputeContext::dispatchIndirect(ComputeState* pState, ComputeVars* pVars, const Buffer* pArgBuffer, uint64_t argBufferOffset) +void ComputeContext::dispatchIndirect(ComputeState* pState, ProgramVars* pVars, const Buffer* pArgBuffer, uint64_t argBufferOffset) { pVars->prepareDescriptorSets(this); resourceBarrier(pArgBuffer, Resource::State::IndirectArg); auto computeEncoder = mpLowLevelData->getComputeCommandEncoder(); FALCOR_GFX_CALL(computeEncoder->bindPipelineWithRootObject(pState->getCSO(pVars)->getGfxPipelineState(), pVars->getShaderObject())); - computeEncoder->dispatchComputeIndirect(pArgBuffer->getGfxBufferResource(), argBufferOffset); + FALCOR_GFX_CALL(computeEncoder->dispatchComputeIndirect(pArgBuffer->getGfxBufferResource(), argBufferOffset)); mCommandsPending = true; } @@ -99,9 +99,9 @@ void ComputeContext::clearUAVCounter(const ref& pBuffer, uint32_t value) } } -void ComputeContext::flush(bool wait) +void ComputeContext::submit(bool wait) { - CopyContext::flush(wait); + CopyContext::submit(wait); mpLastBoundComputeVars = nullptr; } } // namespace Falcor diff --git a/Source/Falcor/Core/API/ComputeContext.h b/Source/Falcor/Core/API/ComputeContext.h index 6f3f5978f..0580738a6 100644 --- a/Source/Falcor/Core/API/ComputeContext.h +++ b/Source/Falcor/Core/API/ComputeContext.h @@ -36,7 +36,7 @@ namespace Falcor { class ComputeState; -class ComputeVars; +class ProgramVars; class ProgramKernels; class UnorderedAccessView; @@ -56,12 +56,12 @@ class FALCOR_API ComputeContext : public CopyContext * Dispatch a compute task * @param[in] dispatchSize 3D dispatch group size */ - void dispatch(ComputeState* pState, ComputeVars* pVars, const uint3& dispatchSize); + void dispatch(ComputeState* pState, ProgramVars* pVars, const uint3& dispatchSize); /** * Executes a dispatch call. Args to the dispatch call are contained in pArgBuffer */ - void dispatchIndirect(ComputeState* pState, ComputeVars* pVars, const Buffer* pArgBuffer, uint64_t argBufferOffset); + void dispatchIndirect(ComputeState* pState, ProgramVars* pVars, const Buffer* pArgBuffer, uint64_t argBufferOffset); /** * Clear an unordered-access view @@ -87,12 +87,12 @@ class FALCOR_API ComputeContext : public CopyContext /** * Submit the command list */ - virtual void flush(bool wait = false) override; + virtual void submit(bool wait = false) override; protected: ComputeContext(gfx::ICommandQueue* pQueue); - const ComputeVars* mpLastBoundComputeVars = nullptr; + const ProgramVars* mpLastBoundComputeVars = nullptr; }; } // namespace Falcor diff --git a/Source/Falcor/Core/API/ComputeStateObject.cpp b/Source/Falcor/Core/API/ComputeStateObject.cpp index e3ac8e13a..59cb70cc9 100644 --- a/Source/Falcor/Core/API/ComputeStateObject.cpp +++ b/Source/Falcor/Core/API/ComputeStateObject.cpp @@ -36,37 +36,27 @@ namespace Falcor { -bool ComputeStateObject::Desc::operator==(const ComputeStateObject::Desc& other) const -{ - bool b = true; - b = b && (mpProgram == other.mpProgram); - return b; -} -ComputeStateObject::~ComputeStateObject() -{ - mpDevice->releaseResource(mGfxPipelineState); -} - -ComputeStateObject::ComputeStateObject(ref pDevice, const Desc& desc) : mpDevice(pDevice), mDesc(desc) +ComputeStateObject::ComputeStateObject(ref pDevice, ComputeStateObjectDesc desc) + : mpDevice(std::move(pDevice)), mDesc(std::move(desc)) { gfx::ComputePipelineStateDesc computePipelineDesc = {}; - computePipelineDesc.program = mDesc.mpProgram->getGfxProgram(); + computePipelineDesc.program = mDesc.pProgramKernels->getGfxProgram(); #if FALCOR_HAS_D3D12 - if (mDesc.mpD3D12RootSignatureOverride) + if (mDesc.pD3D12RootSignatureOverride) mpDevice->requireD3D12(); if (mpDevice->getType() == Device::Type::D3D12) { computePipelineDesc.d3d12RootSignatureOverride = - mDesc.mpD3D12RootSignatureOverride ? (void*)mDesc.mpD3D12RootSignatureOverride->getApiHandle().GetInterfacePtr() : nullptr; + mDesc.pD3D12RootSignatureOverride ? (void*)mDesc.pD3D12RootSignatureOverride->getApiHandle().GetInterfacePtr() : nullptr; } #endif FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createComputePipelineState(computePipelineDesc, mGfxPipelineState.writeRef())); } -ref ComputeStateObject::create(ref pDevice, const Desc& desc) +ComputeStateObject::~ComputeStateObject() { - return ref(new ComputeStateObject(pDevice, desc)); + mpDevice->releaseResource(mGfxPipelineState); } NativeHandle ComputeStateObject::getNativeHandle() const diff --git a/Source/Falcor/Core/API/ComputeStateObject.h b/Source/Falcor/Core/API/ComputeStateObject.h index 695aa8667..bfeb659ad 100644 --- a/Source/Falcor/Core/API/ComputeStateObject.h +++ b/Source/Falcor/Core/API/ComputeStateObject.h @@ -40,51 +40,31 @@ namespace Falcor class D3D12RootSignature; #endif -class FALCOR_API ComputeStateObject : public Object +struct ComputeStateObjectDesc { - FALCOR_OBJECT(ComputeStateObject) -public: - class FALCOR_API Desc - { - public: - Desc& setProgramKernels(const ref& pProgram) - { - mpProgram = pProgram; - return *this; - } - + ref pProgramKernels; #if FALCOR_HAS_D3D12 - /** - * Set a D3D12 root signature to use instead of the one that comes with the program kernel. - * This function is supported on D3D12 only. - * @param[in] pRootSignature An overriding D3D12RootSignature object to use in the compute state. - */ - Desc& setD3D12RootSignatureOverride(const ref& pRootSignature) - { - mpD3D12RootSignatureOverride = pRootSignature; - return *this; - } + ref pD3D12RootSignatureOverride; #endif - ref getProgramKernels() const { return mpProgram; } - bool operator==(const Desc& other) const; - private: - friend class ComputeStateObject; - ref mpProgram; + bool operator==(const ComputeStateObjectDesc& other) const + { + bool result = true; + result = result && (pProgramKernels == other.pProgramKernels); #if FALCOR_HAS_D3D12 - ref mpD3D12RootSignatureOverride; + result = result && (pD3D12RootSignatureOverride == other.pD3D12RootSignatureOverride); #endif - }; + return result; + } +}; +class FALCOR_API ComputeStateObject : public Object +{ + FALCOR_OBJECT(ComputeStateObject) +public: + ComputeStateObject(ref pDevice, ComputeStateObjectDesc desc); ~ComputeStateObject(); - /** - * Create a compute state object. - * @param[in] desc State object description. - * @return New object, or throws an exception if creation failed. - */ - static ref create(ref pDevice, const Desc& desc); - gfx::IPipelineState* getGfxPipelineState() const { return mGfxPipelineState; } /** @@ -94,13 +74,11 @@ class FALCOR_API ComputeStateObject : public Object */ NativeHandle getNativeHandle() const; - const Desc& getDesc() const { return mDesc; } + const ComputeStateObjectDesc& getDesc() const { return mDesc; } private: - ComputeStateObject(ref pDevice, const Desc& desc); - ref mpDevice; - Desc mDesc; + ComputeStateObjectDesc mDesc; Slang::ComPtr mGfxPipelineState; }; } // namespace Falcor diff --git a/Source/Falcor/Core/API/CopyContext.cpp b/Source/Falcor/Core/API/CopyContext.cpp index cc02322cd..25f5d5b83 100644 --- a/Source/Falcor/Core/API/CopyContext.cpp +++ b/Source/Falcor/Core/API/CopyContext.cpp @@ -29,7 +29,8 @@ #include "Device.h" #include "Texture.h" #include "Buffer.h" -#include "GpuFence.h" +#include "Fence.h" +#include "GFXAPI.h" #include "GFXHelpers.h" #include "NativeHandleTraits.h" #include "Aftermath.h" @@ -37,10 +38,14 @@ #include "Shared/D3D12DescriptorPool.h" #include "Shared/D3D12DescriptorData.h" #endif -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/Logger.h" #include "Utils/Math/Common.h" +#if FALCOR_HAS_CUDA +#include "Utils/CudaUtils.h" +#endif + namespace Falcor { CopyContext::CopyContext(Device* pDevice, gfx::ICommandQueue* pQueue) : mpDevice(pDevice) @@ -62,28 +67,76 @@ Profiler* CopyContext::getProfiler() const return mpDevice->getProfiler(); } -void CopyContext::flush(bool wait) +void CopyContext::submit(bool wait) { if (mCommandsPending) { - mpLowLevelData->flush(); + mpLowLevelData->submitCommandBuffer(); mCommandsPending = false; } else { // We need to signal even if there are no commands to execute. We need this because some resources may have been released since the // last flush(), and unless we signal they will not be released - mpLowLevelData->getFence()->gpuSignal(mpLowLevelData->getCommandQueue()); + signal(mpLowLevelData->getFence().get()); } bindDescriptorHeaps(); if (wait) { - mpLowLevelData->getFence()->syncCpu(); + mpLowLevelData->getFence()->wait(); } } +uint64_t CopyContext::signal(Fence* pFence, uint64_t value) +{ + FALCOR_CHECK(pFence, "'fence' must not be null"); + uint64_t signalValue = pFence->updateSignaledValue(value); + mpLowLevelData->getGfxCommandQueue()->executeCommandBuffers(0, nullptr, pFence->getGfxFence(), signalValue); + return signalValue; +} + +void CopyContext::wait(Fence* pFence, uint64_t value) +{ + FALCOR_CHECK(pFence, "'fence' must not be null"); + uint64_t waitValue = value == Fence::kAuto ? pFence->getSignaledValue() : value; + gfx::IFence* fences[] = {pFence->getGfxFence()}; + uint64_t waitValues[] = {waitValue}; + FALCOR_GFX_CALL(mpLowLevelData->getGfxCommandQueue()->waitForFenceValuesOnDevice(1, fences, waitValues)); +} + +#if FALCOR_HAS_CUDA +void CopyContext::waitForCuda(cudaStream_t stream) +{ + if (mpDevice->getType() == Device::Type::D3D12) + { + mpLowLevelData->getCudaSemaphore()->waitForCuda(this, stream); + } + else + { + // In the past, we used to wait for all CUDA work to be done. + // Since GFX with Vulkan doesn't support shared fences yet, we do the same here. + cuda_utils::deviceSynchronize(); + } +} + +void CopyContext::waitForFalcor(cudaStream_t stream) +{ + if (mpDevice->getType() == Device::Type::D3D12) + { + mpLowLevelData->submitCommandBuffer(); + mpLowLevelData->getCudaSemaphore()->waitForFalcor(this, stream); + } + else + { + // In the past, we used to wait for all work on the command queue to be done. + // Since GFX with Vulkan doesn't support shared fences yet, we do the same here. + submit(true); + } +} +#endif + CopyContext::ReadTextureTask::SharedPtr CopyContext::asyncReadTextureSubresource(const Texture* pTexture, uint32_t subresourceIndex) { return CopyContext::ReadTextureTask::create(this, pTexture, subresourceIndex); @@ -287,7 +340,7 @@ CopyContext::ReadTextureTask::SharedPtr CopyContext::ReadTextureTask::create( uint64_t size = pTexture->getDepth(mipLevel) * rowCount * pThis->mRowSize; // Create buffer - pThis->mpBuffer = Buffer::create(pCtx->getDevice(), size, Buffer::BindFlags::None, Buffer::CpuAccess::Read, nullptr); + pThis->mpBuffer = pCtx->getDevice()->createBuffer(size, ResourceBindFlags::None, MemoryType::ReadBack, nullptr); // Copy from texture to buffer pCtx->resourceBarrier(pTexture, Resource::State::CopySource); @@ -298,45 +351,59 @@ CopyContext::ReadTextureTask::SharedPtr CopyContext::ReadTextureTask::create( srcSubresource.layerCount = 1; srcSubresource.mipLevelCount = 1; encoder->copyTextureToBuffer( - pThis->mpBuffer->getGfxBufferResource(), pThis->mpBuffer->getGpuAddressOffset(), size, pThis->mRowSize, srcTexture, - gfx::ResourceState::CopySource, srcSubresource, gfx::ITextureResource::Offset3D(0, 0, 0), + pThis->mpBuffer->getGfxBufferResource(), + 0, + size, + pThis->mRowSize, + srcTexture, + gfx::ResourceState::CopySource, + srcSubresource, + gfx::ITextureResource::Offset3D(0, 0, 0), gfx::ITextureResource::Extents{ - static_cast(pTexture->getWidth(mipLevel)), static_cast(pTexture->getHeight(mipLevel)), + static_cast(pTexture->getWidth(mipLevel)), + static_cast(pTexture->getHeight(mipLevel)), static_cast(pTexture->getDepth(mipLevel))} ); pCtx->setPendingCommands(true); // Create a fence and signal - pThis->mpFence = GpuFence::create(pCtx->getDevice()); + pThis->mpFence = pCtx->getDevice()->createFence(); pThis->mpFence->breakStrongReferenceToDevice(); - pCtx->flush(false); - pThis->mpFence->gpuSignal(pCtx->getLowLevelData()->getCommandQueue()); + pCtx->submit(false); + pCtx->signal(pThis->mpFence.get()); pThis->mRowCount = (uint32_t)rowCount; pThis->mDepth = pTexture->getDepth(mipLevel); return pThis; } -std::vector CopyContext::ReadTextureTask::getData() +void CopyContext::ReadTextureTask::getData(void* pData, size_t size) const { - mpFence->syncCpu(); - // Get buffer data - std::vector result; - result.resize((size_t)mRowCount * mActualRowSize); - uint8_t* pData = reinterpret_cast(mpBuffer->map(Buffer::MapType::Read)); + FALCOR_ASSERT(size == size_t(mRowCount) * mActualRowSize * mDepth); + + mpFence->wait(); + + uint8_t* pDst = reinterpret_cast(pData); + const uint8_t* pSrc = reinterpret_cast(mpBuffer->map(Buffer::MapType::Read)); for (uint32_t z = 0; z < mDepth; z++) { - const uint8_t* pSrcZ = pData + z * (size_t)mRowSize * mRowCount; - uint8_t* pDstZ = result.data() + z * (size_t)mActualRowSize * mRowCount; + const uint8_t* pSrcZ = pSrc + z * (size_t)mRowSize * mRowCount; + uint8_t* pDstZ = pDst + z * (size_t)mActualRowSize * mRowCount; for (uint32_t y = 0; y < mRowCount; y++) { - const uint8_t* pSrc = pSrcZ + y * (size_t)mRowSize; - uint8_t* pDst = pDstZ + y * (size_t)mActualRowSize; - memcpy(pDst, pSrc, mActualRowSize); + const uint8_t* pSrcY = pSrcZ + y * (size_t)mRowSize; + uint8_t* pDstY = pDstZ + y * (size_t)mActualRowSize; + std::memcpy(pDstY, pSrcY, mActualRowSize); } } mpBuffer->unmap(); +} + +std::vector CopyContext::ReadTextureTask::getData() const +{ + std::vector result(size_t(mRowCount) * mActualRowSize * mDepth); + getData(result.data(), result.size()); return result; } @@ -360,7 +427,7 @@ bool CopyContext::textureBarrier(const Texture* pTexture, Resource::State newSta bool CopyContext::bufferBarrier(const Buffer* pBuffer, Resource::State newState) { FALCOR_ASSERT(pBuffer); - if (pBuffer->getCpuAccess() != Buffer::CpuAccess::None) + if (pBuffer->getMemoryType() != MemoryType::DeviceLocal) return false; bool recorded = false; if (pBuffer->getGlobalState() != newState) @@ -434,10 +501,7 @@ void CopyContext::copyResource(const Resource* pDst, const Resource* pSrc) FALCOR_ASSERT(pSrcBuffer->getSize() <= pDstBuffer->getSize()); - resourceEncoder->copyBuffer( - pDstBuffer->getGfxBufferResource(), pDstBuffer->getGpuAddressOffset(), pSrcBuffer->getGfxBufferResource(), - pSrcBuffer->getGpuAddressOffset(), pSrcBuffer->getSize() - ); + resourceEncoder->copyBuffer(pDstBuffer->getGfxBufferResource(), 0, pSrcBuffer->getGfxBufferResource(), 0, pSrcBuffer->getSize()); } else { @@ -445,9 +509,15 @@ void CopyContext::copyResource(const Resource* pDst, const Resource* pSrc) const Texture* pDstTexture = static_cast(pDst); gfx::SubresourceRange subresourceRange = {}; resourceEncoder->copyTexture( - pDstTexture->getGfxTextureResource(), gfx::ResourceState::CopyDestination, subresourceRange, - gfx::ITextureResource::Offset3D(0, 0, 0), pSrcTexture->getGfxTextureResource(), gfx::ResourceState::CopySource, - subresourceRange, gfx::ITextureResource::Offset3D(0, 0, 0), gfx::ITextureResource::Extents{0, 0, 0} + pDstTexture->getGfxTextureResource(), + gfx::ResourceState::CopyDestination, + subresourceRange, + gfx::ITextureResource::Offset3D(0, 0, 0), + pSrcTexture->getGfxTextureResource(), + gfx::ResourceState::CopySource, + subresourceRange, + gfx::ITextureResource::Offset3D(0, 0, 0), + gfx::ITextureResource::Extents{0, 0, 0} ); } mCommandsPending = true; @@ -473,21 +543,45 @@ void CopyContext::updateBuffer(const Buffer* pBuffer, const void* pData, size_t bufferBarrier(pBuffer, Resource::State::CopyDest); auto resourceEncoder = getLowLevelData()->getResourceCommandEncoder(); - resourceEncoder->uploadBufferData(pBuffer->getGfxBufferResource(), pBuffer->getGpuAddressOffset() + offset, numBytes, (void*)pData); + resourceEncoder->uploadBufferData(pBuffer->getGfxBufferResource(), offset, numBytes, (void*)pData); mCommandsPending = true; } +void CopyContext::readBuffer(const Buffer* pBuffer, void* pData, size_t offset, size_t numBytes) +{ + if (numBytes == 0) + numBytes = pBuffer->getSize() - offset; + + if (pBuffer->adjustSizeOffsetParams(numBytes, offset) == false) + { + logWarning("CopyContext::readBuffer() - size and offset are invalid. Nothing to read."); + return; + } + + const auto& pReadBackHeap = mpDevice->getReadBackHeap(); + + auto allocation = pReadBackHeap->allocate(numBytes); + + bufferBarrier(pBuffer, Resource::State::CopySource); + + auto resourceEncoder = getLowLevelData()->getResourceCommandEncoder(); + resourceEncoder->copyBuffer(allocation.gfxBufferResource, allocation.offset, pBuffer->getGfxBufferResource(), offset, numBytes); + mCommandsPending = true; + submit(true); + + std::memcpy(pData, allocation.pData, numBytes); + + pReadBackHeap->release(allocation); +} + void CopyContext::copyBufferRegion(const Buffer* pDst, uint64_t dstOffset, const Buffer* pSrc, uint64_t srcOffset, uint64_t numBytes) { resourceBarrier(pDst, Resource::State::CopyDest); resourceBarrier(pSrc, Resource::State::CopySource); auto resourceEncoder = getLowLevelData()->getResourceCommandEncoder(); - resourceEncoder->copyBuffer( - pDst->getGfxBufferResource(), pDst->getGpuAddressOffset() + dstOffset, pSrc->getGfxBufferResource(), - pSrc->getGpuAddressOffset() + srcOffset, numBytes - ); + resourceEncoder->copyBuffer(pDst->getGfxBufferResource(), dstOffset, pSrc->getGfxBufferResource(), srcOffset, numBytes); mCommandsPending = true; } @@ -527,9 +621,15 @@ void CopyContext::copySubresourceRegion( auto resourceEncoder = getLowLevelData()->getResourceCommandEncoder(); resourceEncoder->copyTexture( - pDst->getGfxTextureResource(), gfx::ResourceState::CopyDestination, dstSubresource, - gfx::ITextureResource::Offset3D(dstOffset.x, dstOffset.y, dstOffset.z), pSrc->getGfxTextureResource(), - gfx::ResourceState::CopySource, srcSubresource, gfx::ITextureResource::Offset3D(srcOffset.x, srcOffset.y, srcOffset.z), copySize + pDst->getGfxTextureResource(), + gfx::ResourceState::CopyDestination, + dstSubresource, + gfx::ITextureResource::Offset3D(dstOffset.x, dstOffset.y, dstOffset.z), + pSrc->getGfxTextureResource(), + gfx::ResourceState::CopySource, + srcSubresource, + gfx::ITextureResource::Offset3D(srcOffset.x, srcOffset.y, srcOffset.z), + copySize ); mCommandsPending = true; } diff --git a/Source/Falcor/Core/API/CopyContext.h b/Source/Falcor/Core/API/CopyContext.h index c21ea43f7..c3d71657e 100644 --- a/Source/Falcor/Core/API/CopyContext.h +++ b/Source/Falcor/Core/API/CopyContext.h @@ -36,6 +36,11 @@ #include #include +#if FALCOR_HAS_CUDA +struct CUstream_st; +typedef CUstream_st* cudaStream_t; +#endif + namespace Falcor { class Texture; @@ -49,11 +54,12 @@ class FALCOR_API CopyContext public: using SharedPtr = std::shared_ptr; static SharedPtr create(CopyContext* pCtx, const Texture* pTexture, uint32_t subresourceIndex); - std::vector getData(); + void getData(void* pData, size_t size) const; + std::vector getData() const; private: ReadTextureTask() = default; - ref mpFence; + ref mpFence; ref mpBuffer; CopyContext* mpContext; uint32_t mRowCount; @@ -79,7 +85,7 @@ class FALCOR_API CopyContext * Flush the command list. This doesn't reset the command allocator, just submits the commands * @param[in] wait If true, will block execution until the GPU finished processing the commands */ - virtual void flush(bool wait = false); + virtual void submit(bool wait = false); /** * Check if we have pending commands @@ -91,6 +97,41 @@ class FALCOR_API CopyContext */ void setPendingCommands(bool commandsPending) { mCommandsPending = commandsPending; } + /** + * Signal a fence. + * @param pFence The fence to signal. + * @param value The value to signal. If Fence::kAuto, the signaled value will be auto-incremented. + * @return Returns the signaled value. + */ + uint64_t signal(Fence* pFence, uint64_t value = Fence::kAuto); + + /** + * Wait for a fence to be signaled on the device. + * Queues a device-side wait and returns immediately. + * The device will wait until the fence reaches or exceeds the specified value. + * @param pFence The fence to wait for. + * @param value The value to wait for. If Fence::kAuto, wait for the last signaled value. + */ + void wait(Fence* pFence, uint64_t value = Fence::kAuto); + +#if FALCOR_HAS_CUDA + /** + * Wait for the CUDA stream to finish execution. + * Queues a device-side wait on the command queue and adds an async fence + * signal on the CUDA stream. Returns immediately. + * @param stream The CUDA stream to wait for. + */ + void waitForCuda(cudaStream_t stream = 0); + + /** + * Wait for the Falcor command queue to finish execution. + * Queues a device-side signal on the command queue and adds an async fence + * wait on the CUDA stream. Returns immediately. + * @param stream The CUDA stream to wait on. + */ + void waitForFalcor(cudaStream_t stream = 0); +#endif + /** * Insert a resource barrier * if pViewInfo is nullptr, will transition the entire resource. Otherwise, it will only transition the subresource in the view @@ -157,6 +198,22 @@ class FALCOR_API CopyContext */ void updateBuffer(const Buffer* pBuffer, const void* pData, size_t offset = 0, size_t numBytes = 0); + void readBuffer(const Buffer* pBuffer, void* pData, size_t offset = 0, size_t numBytes = 0); + + template + std::vector readBuffer(const Buffer* pBuffer, size_t firstElement = 0, size_t elementCount = 0) + { + if (elementCount == 0) + elementCount = pBuffer->getSize() / sizeof(T); + + size_t offset = firstElement * sizeof(T); + size_t numBytes = elementCount * sizeof(T); + + std::vector result(elementCount); + readBuffer(pBuffer, result.data(), offset, numBytes); + return result; + } + /** * Read texture data synchronously. Calling this command will flush the pipeline and wait for the GPU to finish execution */ diff --git a/Source/Falcor/Core/API/DepthStencilState.cpp b/Source/Falcor/Core/API/DepthStencilState.cpp index 05dc35681..807617ab2 100644 --- a/Source/Falcor/Core/API/DepthStencilState.cpp +++ b/Source/Falcor/Core/API/DepthStencilState.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "DepthStencilState.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/ObjectPython.h" #include "Utils/Scripting/ScriptBindings.h" @@ -51,7 +51,7 @@ DepthStencilState::Desc& DepthStencilState::Desc::setStencilReadMask(uint8_t mas return *this; } -DepthStencilState::Desc& DepthStencilState::Desc::setStencilFunc(Face face, Func func) +DepthStencilState::Desc& DepthStencilState::Desc::setStencilFunc(Face face, ComparisonFunc func) { if (face == Face::FrontAndBack) { diff --git a/Source/Falcor/Core/API/DepthStencilState.h b/Source/Falcor/Core/API/DepthStencilState.h index 678e4d3f0..9cb01b195 100644 --- a/Source/Falcor/Core/API/DepthStencilState.h +++ b/Source/Falcor/Core/API/DepthStencilState.h @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Common.h" +#include "Types.h" #include "Handles.h" #include "Core/Macros.h" #include "Core/Object.h" @@ -50,11 +50,6 @@ class FALCOR_API DepthStencilState : public Object FrontAndBack ///< Front and back-facing primitives }; - /** - * Comparison function - */ - using Func = ComparisonFunc; - /** * Stencil operation */ @@ -75,7 +70,7 @@ class FALCOR_API DepthStencilState : public Object */ struct StencilDesc { - Func func = Func::Disabled; ///< Stencil comparison function + ComparisonFunc func = ComparisonFunc::Disabled; ///< Stencil comparison function StencilOp stencilFailOp = StencilOp::Keep; ///< Stencil operation in case stencil test fails StencilOp depthFailOp = StencilOp::Keep; ///< Stencil operation in case stencil test passes but depth test fails StencilOp depthStencilPassOp = StencilOp::Keep; ///< Stencil operation in case stencil and depth tests pass @@ -101,7 +96,7 @@ class FALCOR_API DepthStencilState : public Object /** * Set the depth-function */ - Desc& setDepthFunc(Func depthFunc) + Desc& setDepthFunc(ComparisonFunc depthFunc) { mDepthFunc = depthFunc; return *this; @@ -140,7 +135,7 @@ class FALCOR_API DepthStencilState : public Object * @param face Chooses the face to apply the function to * @param func Comparison function */ - Desc& setStencilFunc(Face face, Func func); + Desc& setStencilFunc(Face face, ComparisonFunc func); /** * Set the stencil operation @@ -164,7 +159,7 @@ class FALCOR_API DepthStencilState : public Object bool mDepthEnabled = true; bool mStencilEnabled = false; bool mWriteDepth = true; - Func mDepthFunc = Func::Less; + ComparisonFunc mDepthFunc = ComparisonFunc::Less; StencilDesc mStencilFront; StencilDesc mStencilBack; uint8_t mStencilReadMask = (uint8_t)-1; @@ -194,7 +189,7 @@ class FALCOR_API DepthStencilState : public Object /** * Get the depth comparison function */ - Func getDepthFunc() const { return mDesc.mDepthFunc; } + ComparisonFunc getDepthFunc() const { return mDesc.mDepthFunc; } /** * Check if stencil is enabled or disabled diff --git a/Source/Falcor/Core/API/Device.cpp b/Source/Falcor/Core/API/Device.cpp index 3c1790d27..f804bb646 100644 --- a/Source/Falcor/Core/API/Device.cpp +++ b/Source/Falcor/Core/API/Device.cpp @@ -29,19 +29,27 @@ #include "Raytracing.h" #include "GFXHelpers.h" #include "GFXAPI.h" +#include "ComputeStateObject.h" +#include "GraphicsStateObject.h" +#include "RtStateObject.h" #include "NativeHandleTraits.h" #include "Aftermath.h" +#include "PythonHelpers.h" #include "Core/Macros.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/ObjectPython.h" #include "Core/Program/Program.h" #include "Core/Program/ProgramManager.h" +#include "Core/Program/ShaderVar.h" #include "Utils/Logger.h" #include "Utils/StringUtils.h" #include "Utils/Scripting/ScriptBindings.h" #include "Utils/Timing/Profiler.h" +#if FALCOR_HAS_CUDA +#include "Utils/CudaUtils.h" +#endif + #if FALCOR_HAS_D3D12 #include "Core/API/Shared/D3D12DescriptorPool.h" #endif @@ -54,6 +62,8 @@ #include namespace Falcor { +static_assert(sizeof(AdapterLUID) == sizeof(gfx::AdapterLUID)); + static_assert((uint32_t)RayFlags::None == 0); static_assert((uint32_t)RayFlags::ForceOpaque == 0x1); static_assert((uint32_t)RayFlags::ForceNonOpaque == 0x2); @@ -70,6 +80,14 @@ static_assert(getMaxViewportCount() <= 8); static const uint32_t kTransientHeapConstantBufferSize = 16 * 1024 * 1024; +static const size_t kConstantBufferDataPlacementAlignment = 256; +// This actually depends on the size of the index, but we can handle losing 2 bytes +static const size_t kIndexBufferDataPlacementAlignment = 4; + +/// The default Shader Model to use when compiling programs. +/// If not supported, the highest supported shader model will be used instead. +static const ShaderModel kDefaultShaderModel = ShaderModel::SM6_6; + class GFXDebugCallBack : public gfx::IDebugCallback { virtual SLANG_NO_THROW void SLANG_MCALL @@ -134,7 +152,7 @@ class PipelineCreationAPIDispatcher : public gfx::IPipelineCreationAPIDispatcher { if (NvAPI_Initialize() != NVAPI_OK) { - throw RuntimeError("Failed to initialize NVAPI."); + FALCOR_THROW("Failed to initialize NVAPI."); } } @@ -180,7 +198,10 @@ class PipelineCreationAPIDispatcher : public gfx::IPipelineCreationAPIDispatcher createNvApiUavSlotExDesc(psoDesc, space, registerId); const NVAPI_D3D12_PSO_EXTENSION_DESC* ppPSOExtensionsDesc[1] = {&psoDesc.mExtSlotDesc}; auto result = NvAPI_D3D12_CreateComputePipelineState( - pD3D12Device, reinterpret_cast(pipelineDesc), 1, ppPSOExtensionsDesc, + pD3D12Device, + reinterpret_cast(pipelineDesc), + 1, + ppPSOExtensionsDesc, (ID3D12PipelineState**)outPipelineState ); return (result == NVAPI_OK) ? SLANG_OK : SLANG_FAIL; @@ -216,7 +237,10 @@ class PipelineCreationAPIDispatcher : public gfx::IPipelineCreationAPIDispatcher const NVAPI_D3D12_PSO_EXTENSION_DESC* ppPSOExtensionsDesc[1] = {&psoDesc.mExtSlotDesc}; auto result = NvAPI_D3D12_CreateGraphicsPipelineState( - pD3D12Device, reinterpret_cast(pipelineDesc), 1, ppPSOExtensionsDesc, + pD3D12Device, + reinterpret_cast(pipelineDesc), + 1, + ppPSOExtensionsDesc, (ID3D12PipelineState**)outPipelineState ); return (result == NVAPI_OK) ? SLANG_OK : SLANG_FAIL; @@ -232,6 +256,16 @@ class PipelineCreationAPIDispatcher : public gfx::IPipelineCreationAPIDispatcher return SLANG_OK; } + virtual gfx::Result createMeshPipelineState( + gfx::IDevice* device, + slang::IComponentType* program, + void* pipelineDesc, + void** outPipelineState + ) + { + FALCOR_THROW("Mesh pipelines are not supported."); + } + // This method will be called by the gfx layer right before creating a ray tracing state object. virtual gfx::Result beforeCreateRayTracingState(gfx::IDevice* device, slang::IComponentType* program) { @@ -244,7 +278,7 @@ class PipelineCreationAPIDispatcher : public gfx::IPipelineCreationAPIDispatcher { if (NvAPI_D3D12_SetNvShaderExtnSlotSpace(pD3D12Device, registerId, space) != NVAPI_OK) { - throw RuntimeError("Failed to set NvApi extension"); + FALCOR_THROW("Failed to set NvApi extension"); } } @@ -263,7 +297,7 @@ class PipelineCreationAPIDispatcher : public gfx::IPipelineCreationAPIDispatcher { if (NvAPI_D3D12_SetNvShaderExtnSlotSpace(pD3D12Device, 0xFFFFFFFF, 0) != NVAPI_OK) { - throw RuntimeError("Failed to set NvApi extension"); + FALCOR_THROW("Failed to set NvApi extension"); } } return SLANG_OK; @@ -278,7 +312,7 @@ inline Device::Type getDefaultDeviceType() #elif FALCOR_HAS_VULKAN return Device::Type::Vulkan; #else - throw RuntimeError("No default device type"); + FALCOR_THROW("No default device type"); #endif } @@ -293,7 +327,7 @@ inline gfx::DeviceType getGfxDeviceType(Device::Type deviceType) case Device::Type::Vulkan: return gfx::DeviceType::Vulkan; default: - throw RuntimeError("Unknown device type"); + FALCOR_THROW("Unknown device type"); } } @@ -359,17 +393,23 @@ inline Device::SupportedFeatures querySupportedFeatures(gfx::IDevice* pDevice) return result; } -inline Device::ShaderModel querySupportedShaderModel(gfx::IDevice* pDevice) +inline ShaderModel querySupportedShaderModel(gfx::IDevice* pDevice) { struct SMLevel { const char* name; - Device::ShaderModel level; + ShaderModel level; + }; + const SMLevel levels[] = { + {"sm_6_7", ShaderModel::SM6_7}, + {"sm_6_6", ShaderModel::SM6_6}, + {"sm_6_5", ShaderModel::SM6_5}, + {"sm_6_4", ShaderModel::SM6_4}, + {"sm_6_3", ShaderModel::SM6_3}, + {"sm_6_2", ShaderModel::SM6_2}, + {"sm_6_1", ShaderModel::SM6_1}, + {"sm_6_0", ShaderModel::SM6_0}, }; - const SMLevel levels[] = {{"sm_6_7", Device::ShaderModel::SM6_7}, {"sm_6_6", Device::ShaderModel::SM6_6}, - {"sm_6_5", Device::ShaderModel::SM6_5}, {"sm_6_4", Device::ShaderModel::SM6_4}, - {"sm_6_3", Device::ShaderModel::SM6_3}, {"sm_6_2", Device::ShaderModel::SM6_2}, - {"sm_6_1", Device::ShaderModel::SM6_1}, {"sm_6_0", Device::ShaderModel::SM6_0}}; for (auto level : levels) { if (pDevice->hasFeature(level.name)) @@ -377,7 +417,7 @@ inline Device::ShaderModel querySupportedShaderModel(gfx::IDevice* pDevice) return level.level; } } - return Device::ShaderModel::Unknown; + return ShaderModel::Unknown; } Device::Device(const Desc& desc) : mDesc(desc) @@ -401,11 +441,11 @@ Device::Device(const Desc& desc) : mDesc(desc) #if !FALCOR_HAS_D3D12 if (mDesc.type == Type::D3D12) - throw RuntimeError("D3D12 device not supported."); + FALCOR_THROW("D3D12 device not supported."); #endif #if !FALCOR_HAS_VULKAN if (mDesc.type == Type::Vulkan) - throw RuntimeError("Vulkan device not supported."); + FALCOR_THROW("Vulkan device not supported."); #endif gfx::IDevice::Desc gfxDesc = {}; @@ -425,7 +465,7 @@ Device::Device(const Desc& desc) : mDesc(desc) if (std::filesystem::exists(mDesc.shaderCachePath)) { if (!std::filesystem::is_directory(mDesc.shaderCachePath)) - throw RuntimeError("Shader cache path {} exists and is not a directory", mDesc.shaderCachePath); + FALCOR_THROW("Shader cache path {} exists and is not a directory", mDesc.shaderCachePath); } else { @@ -470,7 +510,7 @@ Device::Device(const Desc& desc) : mDesc(desc) // Try to create device on specific GPU. { - gfxDesc.adapterLUID = &gpus[mDesc.gpu].luid; + gfxDesc.adapterLUID = reinterpret_cast(&gpus[mDesc.gpu].luid); if (SLANG_FAILED(gfxCreateDevice(&gfxDesc, mGfxDevice.writeRef()))) logWarning("Failed to create device on GPU {} ({}).", mDesc.gpu, gpus[mDesc.gpu].name); } @@ -480,13 +520,13 @@ Device::Device(const Desc& desc) : mDesc(desc) { gfxDesc.adapterLUID = nullptr; if (SLANG_FAILED(gfxCreateDevice(&gfxDesc, mGfxDevice.writeRef()))) - throw RuntimeError("Failed to create device"); + FALCOR_THROW("Failed to create device"); } const auto& deviceInfo = mGfxDevice->getDeviceInfo(); mInfo.adapterName = deviceInfo.adapterName; + mInfo.adapterLUID = gfxDesc.adapterLUID ? gpus[mDesc.gpu].luid : AdapterLUID(); mInfo.apiName = deviceInfo.apiName; - logInfo("Created GPU device '{}' using '{}' API.", mInfo.adapterName, mInfo.apiName); mLimits = queryLimits(mGfxDevice); mSupportedFeatures = querySupportedFeatures(mGfxDevice); @@ -527,6 +567,7 @@ Device::Device(const Desc& desc) : mDesc(desc) } mSupportedShaderModel = querySupportedShaderModel(mGfxDevice); + mDefaultShaderModel = std::min(kDefaultShaderModel, mSupportedShaderModel); mGpuTimestampFrequency = 1000.0 / (double)mGfxDevice->getDeviceInfo().timestampFrequency; #if FALCOR_HAS_D3D12 @@ -563,13 +604,13 @@ Device::Device(const Desc& desc) : mDesc(desc) transientHeapDesc.constantBufferDescriptorCount = 1000000; transientHeapDesc.accelerationStructureDescriptorCount = 1000000; if (SLANG_FAILED(mGfxDevice->createTransientResourceHeap(transientHeapDesc, mpTransientResourceHeaps[i].writeRef()))) - throw RuntimeError("Failed to create transient resource heap"); + FALCOR_THROW("Failed to create transient resource heap"); } gfx::ICommandQueue::Desc queueDesc = {}; queueDesc.type = gfx::ICommandQueue::QueueType::Graphics; if (SLANG_FAILED(mGfxDevice->createCommandQueue(queueDesc, mGfxCommandQueue.writeRef()))) - throw RuntimeError("Failed to create command queue"); + FALCOR_THROW("Failed to create command queue"); // The Device class contains a bunch of nested resource objects that have strong references to the device. // This is because we want a strong reference to the device when those objects are returned to the user. @@ -584,7 +625,7 @@ Device::Device(const Desc& desc) : mDesc(desc) this->setEnableRefTracking(true); #endif - mpFrameFence = GpuFence::create(ref(this)); + mpFrameFence = createFence(); mpFrameFence->breakStrongReferenceToDevice(); #if FALCOR_HAS_D3D12 @@ -606,26 +647,37 @@ Device::Device(const Desc& desc) : mDesc(desc) mpProfiler = std::make_unique(ref(this)); mpProfiler->breakStrongReferenceToDevice(); - mpDefaultSampler = Sampler::create(ref(this), Sampler::Desc()); + mpDefaultSampler = createSampler(Sampler::Desc()); mpDefaultSampler->breakStrongReferenceToDevice(); - mpUploadHeap = GpuMemoryHeap::create(ref(this), GpuMemoryHeap::Type::Upload, 1024 * 1024 * 2, mpFrameFence); + mpUploadHeap = GpuMemoryHeap::create(ref(this), MemoryType::Upload, 1024 * 1024 * 2, mpFrameFence); mpUploadHeap->breakStrongReferenceToDevice(); + mpReadBackHeap = GpuMemoryHeap::create(ref(this), MemoryType::ReadBack, 1024 * 1024 * 2, mpFrameFence); + mpReadBackHeap->breakStrongReferenceToDevice(); + mpTimestampQueryHeap = QueryHeap::create(ref(this), QueryHeap::Type::Timestamp, 1024 * 1024); mpTimestampQueryHeap->breakStrongReferenceToDevice(); mpRenderContext = std::make_unique(this, mGfxCommandQueue); // TODO: Do we need to flush here or should RenderContext::create() bind the descriptor heaps automatically without flush? See #749. - mpRenderContext->flush(); // This will bind the descriptor heaps. + mpRenderContext->submit(); // This will bind the descriptor heaps. this->decRef(false); + + logInfo( + "Created GPU device '{}' using '{}' API (SM{}.{}).", + mInfo.adapterName, + mInfo.apiName, + getShaderModelMajorVersion(mSupportedShaderModel), + getShaderModelMinorVersion(mSupportedShaderModel) + ); } Device::~Device() { - mpRenderContext->flush(true); + mpRenderContext->submit(true); mpProfiler.reset(); @@ -634,6 +686,7 @@ Device::~Device() mDeferredReleases = decltype(mDeferredReleases)(); mpRenderContext.reset(); mpUploadHeap.reset(); + mpReadBackHeap.reset(); mpTimestampQueryHeap.reset(); for (size_t i = 0; i < kInFlightFrameCount; ++i) mpTransientResourceHeaps[i].setNull(); @@ -650,6 +703,7 @@ Device::~Device() mDeferredReleases = decltype(mDeferredReleases)(); + mGfxDevice.setNull(); #if FALCOR_NVAPI_AVAILABLE @@ -657,6 +711,216 @@ Device::~Device() #endif } +ref Device::createBuffer(size_t size, ResourceBindFlags bindFlags, MemoryType memoryType, const void* pInitData) +{ + return make_ref(ref(this), size, bindFlags, memoryType, pInitData); +} + +ref Device::createTypedBuffer( + ResourceFormat format, + uint32_t elementCount, + ResourceBindFlags bindFlags, + MemoryType memoryType, + const void* pInitData +) +{ + return make_ref(ref(this), format, elementCount, bindFlags, memoryType, pInitData); +} + +ref Device::createStructuredBuffer( + uint32_t structSize, + uint32_t elementCount, + ResourceBindFlags bindFlags, + MemoryType memoryType, + const void* pInitData, + bool createCounter +) +{ + return make_ref(ref(this), structSize, elementCount, bindFlags, memoryType, pInitData, createCounter); +} + +ref Device::createStructuredBuffer( + const ReflectionType* pType, + uint32_t elementCount, + ResourceBindFlags bindFlags, + MemoryType memoryType, + const void* pInitData, + bool createCounter +) +{ + FALCOR_CHECK(pType != nullptr, "Can't create a structured buffer from a nullptr type."); + const ReflectionResourceType* pResourceType = pType->unwrapArray()->asResourceType(); + if (!pResourceType || pResourceType->getType() != ReflectionResourceType::Type::StructuredBuffer) + { + FALCOR_THROW("Can't create a structured buffer from type '{}'.", pType->getClassName()); + } + + FALCOR_ASSERT(pResourceType->getSize() <= std::numeric_limits::max()); + return make_ref( + ref(this), (uint32_t)pResourceType->getSize(), elementCount, bindFlags, memoryType, pInitData, createCounter + ); +} + +ref Device::createStructuredBuffer( + const ShaderVar& shaderVar, + uint32_t elementCount, + ResourceBindFlags bindFlags, + MemoryType memoryType, + const void* pInitData, + bool createCounter +) +{ + return createStructuredBuffer(shaderVar.getType(), elementCount, bindFlags, memoryType, pInitData, createCounter); +} + +ref Device::createBufferFromResource( + gfx::IBufferResource* pResource, + size_t size, + ResourceBindFlags bindFlags, + MemoryType memoryType +) +{ + return make_ref(ref(this), pResource, size, bindFlags, memoryType); +} + +ref Device::createBufferFromNativeHandle(NativeHandle handle, size_t size, ResourceBindFlags bindFlags, MemoryType memoryType) +{ + return make_ref(ref(this), handle, size, bindFlags, memoryType); +} + +ref Device::createTexture1D( + uint32_t width, + ResourceFormat format, + uint32_t arraySize, + uint32_t mipLevels, + const void* pInitData, + ResourceBindFlags bindFlags +) +{ + return make_ref( + ref(this), Resource::Type::Texture1D, format, width, 1, 1, arraySize, mipLevels, 1, bindFlags, pInitData + ); +} + +ref Device::createTexture2D( + uint32_t width, + uint32_t height, + ResourceFormat format, + uint32_t arraySize, + uint32_t mipLevels, + const void* pInitData, + ResourceBindFlags bindFlags +) +{ + return make_ref( + ref(this), Resource::Type::Texture2D, format, width, height, 1, arraySize, mipLevels, 1, bindFlags, pInitData + ); +} + +ref Device::createTexture3D( + uint32_t width, + uint32_t height, + uint32_t depth, + ResourceFormat format, + uint32_t mipLevels, + const void* pInitData, + ResourceBindFlags bindFlags +) +{ + return make_ref( + ref(this), Resource::Type::Texture3D, format, width, height, depth, 1, mipLevels, 1, bindFlags, pInitData + ); +} + +ref Device::createTextureCube( + uint32_t width, + uint32_t height, + ResourceFormat format, + uint32_t arraySize, + uint32_t mipLevels, + const void* pInitData, + ResourceBindFlags bindFlags +) +{ + return make_ref( + ref(this), Resource::Type::TextureCube, format, width, height, 1, arraySize, mipLevels, 1, bindFlags, pInitData + ); +} + +ref Device::createTexture2DMS( + uint32_t width, + uint32_t height, + ResourceFormat format, + uint32_t sampleCount, + uint32_t arraySize, + ResourceBindFlags bindFlags +) +{ + return make_ref( + ref(this), Resource::Type::Texture2DMultisample, format, width, height, 1, arraySize, 1, sampleCount, bindFlags, nullptr + ); +} + +ref Device::createTextureFromResource( + gfx::ITextureResource* pResource, + Texture::Type type, + ResourceFormat format, + uint32_t width, + uint32_t height, + uint32_t depth, + uint32_t arraySize, + uint32_t mipLevels, + uint32_t sampleCount, + ResourceBindFlags bindFlags, + Resource::State initState +) +{ + return make_ref( + ref(this), pResource, type, format, width, height, depth, arraySize, mipLevels, sampleCount, bindFlags, initState + ); +} + +ref Device::createSampler(const Sampler::Desc& desc) +{ + return make_ref(ref(this), desc); +} + +ref Device::createFence(const FenceDesc& desc) +{ + return make_ref(ref(this), desc); +} + +ref Device::createFence(bool shared) +{ + FenceDesc desc; + desc.shared = shared; + return createFence(desc); +} + +ref Device::createComputeStateObject(const ComputeStateObjectDesc& desc) +{ + return make_ref(ref(this), desc); +} + +ref Device::createGraphicsStateObject(const GraphicsStateObjectDesc& desc) +{ + return make_ref(ref(this), desc); +} + +ref Device::createRtStateObject(const RtStateObjectDesc& desc) +{ + return make_ref(ref(this), desc); +} + +size_t Device::getBufferDataAlignment(ResourceBindFlags bindFlags) +{ + if (is_set(bindFlags, ResourceBindFlags::Constant)) + return kConstantBufferDataPlacementAlignment; + if (is_set(bindFlags, ResourceBindFlags::Index)) + return kIndexBufferDataPlacementAlignment; + return 1; +} + void Device::releaseResource(ISlangUnknown* pResource) { if (pResource) @@ -664,7 +928,7 @@ void Device::releaseResource(ISlangUnknown* pResource) // Some static objects get here when the application exits if (this) { - mDeferredReleases.push({mpFrameFence ? mpFrameFence->getCpuValue() : 0, Slang::ComPtr(pResource)}); + mDeferredReleases.push({mpFrameFence ? mpFrameFence->getSignaledValue() : 0, Slang::ComPtr(pResource)}); } } } @@ -682,8 +946,9 @@ bool Device::isShaderModelSupported(ShaderModel shaderModel) const void Device::executeDeferredReleases() { mpUploadHeap->executeDeferredReleases(); - uint64_t gpuValue = mpFrameFence->getGpuValue(); - while (mDeferredReleases.size() && mDeferredReleases.front().fenceValue <= gpuValue) + mpReadBackHeap->executeDeferredReleases(); + uint64_t currentValue = mpFrameFence->getCurrentValue(); + while (mDeferredReleases.size() && mDeferredReleases.front().fenceValue < currentValue) { mDeferredReleases.pop(); } @@ -697,23 +962,23 @@ void Device::executeDeferredReleases() #endif // FALCOR_HAS_D3D12 } -void Device::flushAndSync() +void Device::wait() { - mpRenderContext->flush(true); - mpFrameFence->gpuSignal(mpRenderContext->getLowLevelData()->getCommandQueue()); + mpRenderContext->submit(true); + mpRenderContext->signal(mpFrameFence.get()); executeDeferredReleases(); } void Device::requireD3D12() const { if (getType() != Type::D3D12) - throw RuntimeError("D3D12 device is required."); + FALCOR_THROW("D3D12 device is required."); } void Device::requireVulkan() const { if (getType() != Type::Vulkan) - throw RuntimeError("Vulkan device is required."); + FALCOR_THROW("Vulkan device is required."); } ResourceBindFlags Device::getFormatBindFlags(ResourceFormat format) @@ -766,6 +1031,29 @@ ResourceBindFlags Device::getFormatBindFlags(ResourceFormat format) return flags; } +size_t Device::getTextureRowAlignment() const +{ + size_t alignment = 1; + mGfxDevice->getTextureRowAlignment(&alignment); + return alignment; +} + +#if FALCOR_HAS_CUDA + +bool Device::initCudaDevice() +{ + return getCudaDevice() != nullptr; +} + +cuda_utils::CudaDevice* Device::getCudaDevice() const +{ + if (!mpCudaDevice) + mpCudaDevice = make_ref(this); + return mpCudaDevice.get(); +} + +#endif + void Device::reportLiveObjects() { gfx::gfxReportLiveObjects(); @@ -822,18 +1110,25 @@ bool Device::enableAgilitySDK() return false; } -std::vector Device::getGPUs(Type deviceType) +std::vector Device::getGPUs(Type deviceType) { if (deviceType == Type::Default) deviceType = getDefaultDeviceType(); auto adapters = gfx::gfxGetAdapters(getGfxDeviceType(deviceType)); - std::vector result; + std::vector result; for (gfx::GfxIndex i = 0; i < adapters.getCount(); ++i) - result.push_back(adapters.getAdapters()[i]); + { + const gfx::AdapterInfo& gfxInfo = adapters.getAdapters()[i]; + AdapterInfo info; + info.name = gfxInfo.name; + info.vendorID = gfxInfo.vendorID; + info.deviceID = gfxInfo.deviceID; + info.luid = *reinterpret_cast(&gfxInfo.luid); + result.push_back(info); + } // Move all NVIDIA adapters to the start of the list. std::stable_partition( - result.begin(), result.end(), - [](const gfx::AdapterInfo& info) { return toLowerCase(info.name).find("nvidia") != std::string::npos; } + result.begin(), result.end(), [](const AdapterInfo& info) { return toLowerCase(info.name).find("nvidia") != std::string::npos; } ); return result; } @@ -845,11 +1140,11 @@ gfx::ITransientResourceHeap* Device::getCurrentTransientResourceHeap() void Device::endFrame() { - mpRenderContext->flush(); + mpRenderContext->submit(); // Wait on past frames. - if (mpFrameFence->getCpuValue() >= kInFlightFrameCount) - mpFrameFence->syncCpu(mpFrameFence->getCpuValue() - kInFlightFrameCount); + if (mpFrameFence->getSignaledValue() > kInFlightFrameCount) + mpFrameFence->wait(mpFrameFence->getSignaledValue() - kInFlightFrameCount); // Switch to next transient resource heap. getCurrentTransientResourceHeap()->finish(); @@ -859,7 +1154,7 @@ void Device::endFrame() mpRenderContext->getLowLevelData()->openCommandBuffer(); // Signal frame fence for new frame. - mpFrameFence->gpuSignal(mpRenderContext->getLowLevelData()->getCommandQueue()); + mpRenderContext->signal(mpFrameFence.get()); // Release resources from past frames. executeDeferredReleases(); @@ -898,8 +1193,10 @@ FALCOR_SCRIPT_BINDING(Device) FALCOR_SCRIPT_BINDING_DEPENDENCY(Formats) FALCOR_SCRIPT_BINDING_DEPENDENCY(Buffer) FALCOR_SCRIPT_BINDING_DEPENDENCY(Texture) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Sampler) FALCOR_SCRIPT_BINDING_DEPENDENCY(Profiler) FALCOR_SCRIPT_BINDING_DEPENDENCY(RenderContext) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Program) pybind11::class_> device(m, "Device"); @@ -928,30 +1225,129 @@ FALCOR_SCRIPT_BINDING(Device) return make_ref(desc); } ), - "type"_a = Device::Type::Default, "gpu"_a = 0, "enable_debug_layer"_a = false, "enable_aftermath"_a = false + "type"_a = Device::Type::Default, + "gpu"_a = 0, + "enable_debug_layer"_a = false, + "enable_aftermath"_a = false ); device.def( "create_buffer", - [](ref self, size_t size, ResourceBindFlags bind_flags, Buffer::CpuAccess cpu_access) - { return Buffer::create(self, size, bind_flags, cpu_access); }, - "size"_a, "bind_flags"_a = ResourceBindFlags::None, "cpu_access"_a = Buffer::CpuAccess::None + [](Device& self, size_t size, ResourceBindFlags bind_flags, MemoryType memory_type) + { return self.createBuffer(size, bind_flags, memory_type); }, + "size"_a, + "bind_flags"_a = ResourceBindFlags::None, + "memory_type"_a = MemoryType::DeviceLocal + ); + device.def( + "create_typed_buffer", + [](Device& self, ResourceFormat format, size_t element_count, ResourceBindFlags bind_flags, MemoryType memory_type) + { return self.createTypedBuffer(format, element_count, bind_flags, memory_type, nullptr); }, + "format"_a, + "element_count"_a, + "bind_flags"_a = ResourceBindFlags::None, + "memory_type"_a = MemoryType::DeviceLocal ); + device.def( + "create_structured_buffer", + [](Device& self, size_t struct_size, size_t element_count, ResourceBindFlags bind_flags, MemoryType memory_type, bool create_counter + ) { return self.createStructuredBuffer(struct_size, element_count, bind_flags, memory_type, nullptr, create_counter); }, + "struct_size"_a, + "element_count"_a, + "bind_flags"_a = ResourceBindFlags::None, + "memory_type"_a = MemoryType::DeviceLocal, + "create_counter"_a = false + ); + device.def( "create_texture", - [](ref self, uint32_t width, uint32_t height, uint32_t depth, ResourceFormat format, uint32_t array_size, - uint32_t mip_levels, ResourceBindFlags bind_flags) + [](Device& self, + uint32_t width, + uint32_t height, + uint32_t depth, + ResourceFormat format, + uint32_t array_size, + uint32_t mip_levels, + ResourceBindFlags bind_flags) { if (depth > 0) - return Texture::create3D(self, width, height, depth, format, mip_levels, nullptr, bind_flags); + return self.createTexture3D(width, height, depth, format, mip_levels, nullptr, bind_flags); else if (height > 0) - return Texture::create2D(self, width, height, format, array_size, mip_levels, nullptr, bind_flags); + return self.createTexture2D(width, height, format, array_size, mip_levels, nullptr, bind_flags); + else + return self.createTexture1D(width, format, array_size, mip_levels, nullptr, bind_flags); + }, + "width"_a, + "height"_a = 0, + "depth"_a = 0, + "format"_a = ResourceFormat::Unknown, + "array_size"_a = 1, + "mip_levels"_a = uint32_t(Texture::kMaxPossible), + "bind_flags"_a = ResourceBindFlags::None + ); + + device.def( + "create_sampler", + [](Device& self, + TextureFilteringMode mag_filter, + TextureFilteringMode min_filter, + TextureFilteringMode mip_filter, + uint32_t max_anisotropy, + float min_lod, + float max_lod, + float lod_bias, + ComparisonFunc comparison_func, + TextureReductionMode reduction_mode, + TextureAddressingMode address_mode_u, + TextureAddressingMode address_mode_v, + TextureAddressingMode address_mode_w, + float4 border_color) + { + Sampler::Desc desc; + desc.setFilterMode(mag_filter, min_filter, mip_filter); + desc.setMaxAnisotropy(max_anisotropy); + desc.setLodParams(min_lod, max_lod, lod_bias); + desc.setComparisonFunc(comparison_func); + desc.setReductionMode(reduction_mode); + desc.setAddressingMode(address_mode_u, address_mode_v, address_mode_w); + desc.setBorderColor(border_color); + return self.createSampler(desc); + }, + "mag_filter"_a = TextureFilteringMode::Linear, + "min_filter"_a = TextureFilteringMode::Linear, + "mip_filter"_a = TextureFilteringMode::Linear, + "max_anisotropy"_a = 1, + "min_lod"_a = -1000.0f, + "max_lod"_a = 1000.0f, + "lod_bias"_a = 0.f, + "comparison_func"_a = ComparisonFunc::Disabled, + "reduction_mode"_a = TextureReductionMode::Standard, + "address_mode_u"_a = TextureAddressingMode::Wrap, + "address_mode_v"_a = TextureAddressingMode::Wrap, + "address_mode_w"_a = TextureAddressingMode::Wrap, + "border_color_r"_a = float4(0.f) + ); + + device.def( + "create_program", + [](ref self, std::optional desc, pybind11::dict defines, const pybind11::kwargs& kwargs) + { + if (desc) + { + FALCOR_CHECK(kwargs.empty(), "Either provide a 'desc' or kwargs, but not both."); + return Program::create(self, *desc, defineListFromPython(defines)); + } else - return Texture::create1D(self, width, format, array_size, mip_levels, nullptr, bind_flags); + { + return Program::create(self, programDescFromPython(kwargs), defineListFromPython(defines)); + } }, - "width"_a, "height"_a = 0, "depth"_a = 0, "format"_a = ResourceFormat::Unknown, "array_size"_a = 1, - "mip_levels"_a = uint32_t(Texture::kMaxPossible), "bind_flags"_a = ResourceBindFlags::None + "desc"_a = std::optional(), + "defines"_a = pybind11::dict(), + pybind11::kw_only() ); + device.def("wait", &Device::wait); + device.def_property_readonly("profiler", &Device::getProfiler); device.def_property_readonly("type", &Device::getType); device.def_property_readonly("info", &Device::getInfo); diff --git a/Source/Falcor/Core/API/Device.h b/Source/Falcor/Core/API/Device.h index c7d4f1382..2b227bb0b 100644 --- a/Source/Falcor/Core/API/Device.h +++ b/Source/Falcor/Core/API/Device.h @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once +#include "Types.h" #include "Handles.h" #include "NativeHandle.h" #include "Formats.h" @@ -64,6 +65,40 @@ class ProgramManager; class Profiler; class AftermathContext; +namespace cuda_utils +{ +class CudaDevice; +}; + +/// Holds the adapter LUID (or UUID). +/// Note: The adapter LUID is actually just 8 bytes, but on Linux the LUID is +/// not supported, so we use this to store the 16-byte UUID instead. +struct AdapterLUID +{ + std::array luid; + + AdapterLUID() { luid.fill(0); } + bool isValid() const { return *this != AdapterLUID(); } + bool operator==(const AdapterLUID& other) const { return luid == other.luid; } + bool operator!=(const AdapterLUID& other) const { return luid != other.luid; } + bool operator<(const AdapterLUID& other) const { return luid < other.luid; } +}; + +struct AdapterInfo +{ + /// Descriptive name of the adapter. + std::string name; + + /// Unique identifier for the vendor. + uint32_t vendorID; + + // Unique identifier for the physical device among devices from the vendor. + uint32_t deviceID; + + // Logically unique identifier of the adapter. + AdapterLUID luid; +}; + class FALCOR_API Device : public Object { FALCOR_OBJECT(Device) @@ -82,6 +117,14 @@ class FALCOR_API Device : public Object D3D12, Vulkan, }; + FALCOR_ENUM_INFO( + Type, + { + {Type::Default, "Default"}, + {Type::D3D12, "D3D12"}, + {Type::Vulkan, "Vulkan"}, + } + ); /// Device descriptor. struct Desc @@ -113,6 +156,7 @@ class FALCOR_API Device : public Object struct Info { std::string adapterName; + AdapterLUID adapterLUID; std::string apiName; }; @@ -142,19 +186,6 @@ class FALCOR_API Device : public Object // clang-format on }; - enum class ShaderModel : uint32_t - { - Unknown, - SM6_0, - SM6_1, - SM6_2, - SM6_3, - SM6_4, - SM6_5, - SM6_6, - SM6_7, - }; - /** * Constructor. Throws an exception if creation failed. * @param[in] desc Device configuration descriptor. @@ -162,6 +193,296 @@ class FALCOR_API Device : public Object Device(const Desc& desc); ~Device(); + /** + * Create a new buffer. + * @param[in] size Size of the buffer in bytes. + * @param[in] bindFlags Buffer bind flags. + * @param[in] memoryType Type of memory to use for the buffer. + * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer size should be at least 'size' bytes. + * @return A pointer to a new buffer object, or throws an exception if creation failed. + */ + ref createBuffer( + size_t size, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType memoryType = MemoryType::DeviceLocal, + const void* pInitData = nullptr + ); + + /** + * Create a new typed buffer. + * @param[in] format Typed buffer format. + * @param[in] elementCount Number of elements. + * @param[in] bindFlags Buffer bind flags. + * @param[in] memoryType Type of memory to use for the buffer. + * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer should hold at least 'elementCount' elements. + * @return A pointer to a new buffer object, or throws an exception if creation failed. + */ + ref createTypedBuffer( + ResourceFormat format, + uint32_t elementCount, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType memoryType = MemoryType::DeviceLocal, + const void* pInitData = nullptr + ); + + /** + * Create a new typed buffer. The format is deduced from the template parameter. + * @param[in] elementCount Number of elements. + * @param[in] bindFlags Buffer bind flags. + * @param[in] memoryType Type of memory to use for the buffer. + * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer should hold at least 'elementCount' elements. + * @return A pointer to a new buffer object, or throws an exception if creation failed. + */ + template + ref createTypedBuffer( + uint32_t elementCount, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType memoryType = MemoryType::DeviceLocal, + const T* pInitData = nullptr + ) + { + return createTypedBuffer(detail::FormatForElementType::kFormat, elementCount, bindFlags, memoryType, pInitData); + } + + /** + * Create a new structured buffer. + * @param[in] structSize Size of the struct in bytes. + * @param[in] elementCount Number of elements. + * @param[in] bindFlags Buffer bind flags. + * @param[in] memoryType Type of memory to use for the buffer. + * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer should hold at least 'elementCount' elements. + * @param[in] createCounter True if the associated UAV counter should be created. + * @return A pointer to a new buffer object, or throws an exception if creation failed. + */ + ref createStructuredBuffer( + uint32_t structSize, + uint32_t elementCount, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType memoryType = MemoryType::DeviceLocal, + const void* pInitData = nullptr, + bool createCounter = true + ); + + /** + * Create a new structured buffer. + * @param[in] pType Type of the structured buffer. + * @param[in] elementCount Number of elements. + * @param[in] bindFlags Buffer bind flags. + * @param[in] memoryType Type of memory to use for the buffer. + * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer should hold at least 'elementCount' elements. + * @param[in] createCounter True if the associated UAV counter should be created. + * @return A pointer to a new buffer object, or throws an exception if creation failed. + */ + ref createStructuredBuffer( + const ReflectionType* pType, + uint32_t elementCount, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType memoryType = MemoryType::DeviceLocal, + const void* pInitData = nullptr, + bool createCounter = true + ); + + /** + * Create a new structured buffer. + * @param[in] shaderVar ShaderVar pointing to the buffer variable. + * @param[in] elementCount Number of elements. + * @param[in] bindFlags Buffer bind flags. + * @param[in] memoryType Type of memory to use for the buffer. + * @param[in] pInitData Optional parameter. Initial buffer data. Pointed buffer should hold at least 'elementCount' elements. + * @param[in] createCounter True if the associated UAV counter should be created. + * @return A pointer to a new buffer object, or throws an exception if creation failed. + */ + ref createStructuredBuffer( + const ShaderVar& shaderVar, + uint32_t elementCount, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType memoryType = MemoryType::DeviceLocal, + const void* pInitData = nullptr, + bool createCounter = true + ); + + /** + * Create a new buffer from an existing resource. + * @param[in] pResource Already allocated resource. + * @param[in] size The size of the buffer in bytes. + * @param[in] bindFlags Buffer bind flags. Flags must match the bind flags of the original resource. + * @param[in] memoryType Type of memory to use for the buffer. Flags must match those of the heap the original resource is + * allocated on. + * @return A pointer to a new buffer object, or throws an exception if creation failed. + */ + ref createBufferFromResource(gfx::IBufferResource* pResource, size_t size, ResourceBindFlags bindFlags, MemoryType memoryType); + + /** + * Create a new buffer from an existing native handle. + * @param[in] handle Handle of already allocated resource. + * @param[in] size The size of the buffer in bytes. + * @param[in] bindFlags Buffer bind flags. Flags must match the bind flags of the original resource. + * @param[in] memoryType Type of memory to use for the buffer. Flags must match those of the heap the original resource is + * allocated on. + * @return A pointer to a new buffer object, or throws an exception if creation failed. + */ + ref createBufferFromNativeHandle(NativeHandle handle, size_t size, ResourceBindFlags bindFlags, MemoryType memoryType); + + /** + * Create a 1D texture. + * @param[in] width The width of the texture. + * @param[in] format The format of the texture. + * @param[in] arraySize The array size of the texture. + * @param[in] mipLevels If equal to kMaxPossible then an entire mip chain will be generated from mip level 0. If any other value is + * given then the data for at least that number of miplevels must be provided. + * @param[in] pInitData If different than nullptr, pointer to a buffer containing data to initialize the texture with. + * @param[in] bindFlags The requested bind flags for the resource. + * @return A pointer to a new texture, or throws an exception if creation failed. + */ + ref createTexture1D( + uint32_t width, + ResourceFormat format, + uint32_t arraySize = 1, + uint32_t mipLevels = Resource::kMaxPossible, + const void* pInitData = nullptr, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource + ); + + /** + * Create a 2D texture. + * @param[in] width The width of the texture. + * @param[in] height The height of the texture. + * @param[in] format The format of the texture. + * @param[in] arraySize The array size of the texture. + * @param[in] mipLevels If equal to kMaxPossible then an entire mip chain will be generated from mip level 0. If any other value is + * given then the data for at least that number of miplevels must be provided. + * @param[in] pInitData If different than nullptr, pointer to a buffer containing data to initialize the texture with. + * @param[in] bindFlags The requested bind flags for the resource. + * @return A pointer to a new texture, or throws an exception if creation failed. + */ + ref createTexture2D( + uint32_t width, + uint32_t height, + ResourceFormat format, + uint32_t arraySize = 1, + uint32_t mipLevels = Resource::kMaxPossible, + const void* pInitData = nullptr, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource + ); + + /** + * Create a 3D texture. + * @param[in] width The width of the texture. + * @param[in] height The height of the texture. + * @param[in] depth The depth of the texture. + * @param[in] format The format of the texture. + * @param[in] mipLevels If equal to kMaxPossible then an entire mip chain will be generated from mip level 0. If any other value is + * given then the data for at least that number of miplevels must be provided. + * @param[in] pInitData If different than nullptr, pointer to a buffer containing data to initialize the texture with. + * @param[in] bindFlags The requested bind flags for the resource. + * @return A pointer to a new texture, or throws an exception if creation failed. + */ + ref createTexture3D( + uint32_t width, + uint32_t height, + uint32_t depth, + ResourceFormat format, + uint32_t mipLevels = Resource::kMaxPossible, + const void* pInitData = nullptr, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource + ); + + /** + * Create a cube texture. + * @param[in] width The width of the texture. + * @param[in] height The height of the texture. + * @param[in] format The format of the texture. + * @param[in] arraySize The array size of the texture. + * @param[in] mipLevels If equal to kMaxPossible then an entire mip chain will be generated from mip level 0. If any other value is + * given then the data for at least that number of miplevels must be provided. + * @param[in] pInitData If different than nullptr, pointer to a buffer containing data to initialize the texture with. + * @param[in] bindFlags The requested bind flags for the resource. + * @return A pointer to a new texture, or throws an exception if creation failed. + */ + ref createTextureCube( + uint32_t width, + uint32_t height, + ResourceFormat format, + uint32_t arraySize = 1, + uint32_t mipLevels = Resource::kMaxPossible, + const void* pInitData = nullptr, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource + ); + + /** + * Create a multi-sampled 2D texture. + * @param[in] width The width of the texture. + * @param[in] height The height of the texture. + * @param[in] format The format of the texture. + * @param[in] sampleCount The sample count of the texture. + * @param[in] arraySize The array size of the texture. + * @param[in] bindFlags The requested bind flags for the resource. + * @return A pointer to a new texture, or throws an exception if creation failed. + */ + ref createTexture2DMS( + uint32_t width, + uint32_t height, + ResourceFormat format, + uint32_t sampleCount, + uint32_t arraySize = 1, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource + ); + + /** + * Create a new texture from an resource. + * @param[in] pResource Already allocated resource. + * @param[in] type The type of texture. + * @param[in] format The format of the texture. + * @param[in] width The width of the texture. + * @param[in] height The height of the texture. + * @param[in] depth The depth of the texture. + * @param[in] arraySize The array size of the texture. + * @param[in] mipLevels The number of mip levels. + * @param[in] sampleCount The sample count of the texture. + * @param[in] bindFlags Texture bind flags. Flags must match the bind flags of the original resource. + * @param[in] initState The initial resource state. + * @return A pointer to a new texture, or throws an exception if creation failed. + */ + ref createTextureFromResource( + gfx::ITextureResource* pResource, + Texture::Type type, + ResourceFormat format, + uint32_t width, + uint32_t height, + uint32_t depth, + uint32_t arraySize, + uint32_t mipLevels, + uint32_t sampleCount, + ResourceBindFlags bindFlags, + Resource::State initState + ); + + /** + * Create a new sampler object. + * @param[in] desc Describes sampler settings. + * @return A new object, or throws an exception if creation failed. + */ + ref createSampler(const Sampler::Desc& desc); + + /** + * Create a new fence object. + * @return A new object, or throws an exception if creation failed. + */ + ref createFence(const FenceDesc& desc); + + /** + * Create a new fence object. + * @return A new object, or throws an exception if creation failed. + */ + ref createFence(bool shared = false); + + /// Create a compute state object. + ref createComputeStateObject(const ComputeStateObjectDesc& desc); + /// Create a graphics state object. + ref createGraphicsStateObject(const GraphicsStateObjectDesc& desc); + /// Create a raytracing state object. + ref createRtStateObject(const RtStateObjectDesc& desc); + ProgramManager* getProgramManager() const { return mpProgramManager.get(); } Profiler* getProfiler() const { return mpProfiler.get(); } @@ -199,7 +520,7 @@ class FALCOR_API Device : public Object /** * Flushes pipeline, releases resources, and blocks until completion */ - void flushAndSync(); + void wait(); /** * Get the desc @@ -243,7 +564,10 @@ class FALCOR_API Device : public Object } #endif // FALCOR_HAS_D3D12 + size_t getBufferDataAlignment(ResourceBindFlags bindFlags); + const ref& getUploadHeap() const { return mpUploadHeap; } + const ref& getReadBackHeap() const { return mpReadBackHeap; } const ref& getTimestampQueryHeap() const { return mpTimestampQueryHeap; } void releaseResource(ISlangUnknown* pResource); @@ -271,6 +595,11 @@ class FALCOR_API Device : public Object */ ShaderModel getSupportedShaderModel() const { return mSupportedShaderModel; } + /** + * Return the default shader model to use + */ + ShaderModel getDefaultShaderModel() const { return mDefaultShaderModel; } + gfx::ITransientResourceHeap* getCurrentTransientResourceHeap(); /** @@ -278,6 +607,17 @@ class FALCOR_API Device : public Object */ ResourceBindFlags getFormatBindFlags(ResourceFormat format); + /// Get the texture row memory alignment in bytes. + size_t getTextureRowAlignment() const; + +#if FALCOR_HAS_CUDA + /// Initialize CUDA device sharing the same adapter as the graphics device. + bool initCudaDevice(); + + /// Get the CUDA device sharing the same adapter as the graphics device. + cuda_utils::CudaDevice* getCudaDevice() const; +#endif + /// Report live objects in GFX. /// This is useful for checking clean shutdown where all resources are properly released. static void reportLiveObjects(); @@ -302,7 +642,7 @@ class FALCOR_API Device : public Object /** * Get a list of all available GPUs. */ - static std::vector getGPUs(Type deviceType); + static std::vector getGPUs(Type deviceType); /** * Get the global device mutex. @@ -330,12 +670,13 @@ class FALCOR_API Device : public Object ref mpDefaultSampler; ref mpUploadHeap; + ref mpReadBackHeap; ref mpTimestampQueryHeap; #if FALCOR_HAS_D3D12 ref mpD3D12CpuDescPool; ref mpD3D12GpuDescPool; #endif - ref mpFrameFence; + ref mpFrameFence; std::unique_ptr mpRenderContext; double mGpuTimestampFrequency; @@ -344,6 +685,7 @@ class FALCOR_API Device : public Object Limits mLimits; SupportedFeatures mSupportedFeatures = SupportedFeatures::None; ShaderModel mSupportedShaderModel = ShaderModel::Unknown; + ShaderModel mDefaultShaderModel = ShaderModel::Unknown; #if FALCOR_HAS_AFTERMATH std::unique_ptr mpAftermathContext; @@ -356,6 +698,11 @@ class FALCOR_API Device : public Object std::unique_ptr mpProgramManager; std::unique_ptr mpProfiler; +#if FALCOR_HAS_CUDA + /// CUDA device sharing the same adapter as the graphics device. + mutable ref mpCudaDevice; +#endif + std::mutex mGlobalGfxMutex; }; @@ -365,4 +712,7 @@ inline constexpr uint32_t getMaxViewportCount() } FALCOR_ENUM_CLASS_OPERATORS(Device::SupportedFeatures); + +FALCOR_ENUM_REGISTER(Device::Type); + } // namespace Falcor diff --git a/Source/Falcor/Core/API/FBO.cpp b/Source/Falcor/Core/API/FBO.cpp index a05a2be74..63e38f818 100644 --- a/Source/Falcor/Core/API/FBO.cpp +++ b/Source/Falcor/Core/API/FBO.cpp @@ -28,7 +28,7 @@ #include "FBO.h" #include "Device.h" #include "GFXAPI.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/ObjectPython.h" #include "Utils/Scripting/ScriptBindings.h" @@ -76,40 +76,43 @@ void checkAttachArguments(const Texture* pTexture, uint32_t mipLevel, uint32_t f if (pTexture == nullptr) return; - checkArgument(mipLevel < pTexture->getMipCount(), "'mipLevel' ({}) is out of bounds.", mipLevel); + FALCOR_CHECK(mipLevel < pTexture->getMipCount(), "'mipLevel' ({}) is out of bounds.", mipLevel); if (arraySize != Fbo::kAttachEntireMipLevel) { - checkArgument(arraySize != 0, "'arraySize' must not be zero."); + FALCOR_CHECK(arraySize != 0, "'arraySize' must not be zero."); if (pTexture->getType() == Texture::Type::Texture3D) { - checkArgument( + FALCOR_CHECK( arraySize + firstArraySlice <= pTexture->getDepth(), - "'firstArraySlice' ({}) and 'arraySize' ({}) request depth index that is out of bounds.", firstArraySlice, arraySize + "'firstArraySlice' ({}) and 'arraySize' ({}) request depth index that is out of bounds.", + firstArraySlice, + arraySize ); } else { - checkArgument( + FALCOR_CHECK( arraySize + firstArraySlice <= pTexture->getArraySize(), - "'frstArraySlice' ({}) and 'arraySize' ({}) request array index that is out of bounds.", firstArraySlice, arraySize + "'frstArraySlice' ({}) and 'arraySize' ({}) request array index that is out of bounds.", + firstArraySlice, + arraySize ); } } if (isDepthAttachment) { - checkArgument(isDepthStencilFormat(pTexture->getFormat()), "Depth-stencil texture must have a depth-stencil format."); - checkArgument( - is_set(pTexture->getBindFlags(), Texture::BindFlags::DepthStencil), - "Depth-stencil texture must have the DepthStencil bind flag." + FALCOR_CHECK(isDepthStencilFormat(pTexture->getFormat()), "Depth-stencil texture must have a depth-stencil format."); + FALCOR_CHECK( + is_set(pTexture->getBindFlags(), ResourceBindFlags::DepthStencil), "Depth-stencil texture must have the DepthStencil bind flag." ); } else { - checkArgument(!isDepthStencilFormat(pTexture->getFormat()), "Color texture must not have a depth-stencil format."); - checkArgument( - is_set(pTexture->getBindFlags(), Texture::BindFlags::RenderTarget), "Color texture must have the RenderTarget bind flag." + FALCOR_CHECK(!isDepthStencilFormat(pTexture->getFormat()), "Color texture must not have a depth-stencil format."); + FALCOR_CHECK( + is_set(pTexture->getBindFlags(), ResourceBindFlags::RenderTarget), "Color texture must have the RenderTarget bind flag." ); } } @@ -122,32 +125,32 @@ ref createTexture2D( uint32_t sampleCount, uint32_t arraySize, uint32_t mipLevels, - Texture::BindFlags flags + ResourceBindFlags flags ) { if (format == ResourceFormat::Unknown) { - throw RuntimeError("Can't create Texture2D with an unknown resource format."); + FALCOR_THROW("Can't create Texture2D with an unknown resource format."); } if (sampleCount > 1) { - return Texture::create2DMS(pDevice, w, h, format, sampleCount, arraySize, flags); + return pDevice->createTexture2DMS(w, h, format, sampleCount, arraySize, flags); } else { - return Texture::create2D(pDevice, w, h, format, arraySize, mipLevels, nullptr, flags); + return pDevice->createTexture2D(w, h, format, arraySize, mipLevels, nullptr, flags); } } -Texture::BindFlags getBindFlags(bool isDepth, bool allowUav) +ResourceBindFlags getBindFlags(bool isDepth, bool allowUav) { - Texture::BindFlags flags = Texture::BindFlags::ShaderResource; - flags |= isDepth ? Texture::BindFlags::DepthStencil : Texture::BindFlags::RenderTarget; + ResourceBindFlags flags = ResourceBindFlags::ShaderResource; + flags |= isDepth ? ResourceBindFlags::DepthStencil : ResourceBindFlags::RenderTarget; if (allowUav) { - flags |= Texture::BindFlags::UnorderedAccess; + flags |= ResourceBindFlags::UnorderedAccess; } return flags; } @@ -363,7 +366,7 @@ void Fbo::attachDepthStencilTarget(const ref& pDepthStencil, uint32_t m bool allowUav = false; if (pDepthStencil) { - allowUav = ((pDepthStencil->getBindFlags() & Texture::BindFlags::UnorderedAccess) != Texture::BindFlags::None); + allowUav = ((pDepthStencil->getBindFlags() & ResourceBindFlags::UnorderedAccess) != ResourceBindFlags::None); } mTempDesc.setDepthStencilTarget(pDepthStencil ? pDepthStencil->getFormat() : ResourceFormat::Unknown, allowUav); @@ -372,8 +375,10 @@ void Fbo::attachDepthStencilTarget(const ref& pDepthStencil, uint32_t m void Fbo::attachColorTarget(const ref& pTexture, uint32_t rtIndex, uint32_t mipLevel, uint32_t firstArraySlice, uint32_t arraySize) { - checkArgument( - rtIndex < mColorAttachments.size(), "'rtIndex' ({}) is out of range. Only {} color targets are available.", rtIndex, + FALCOR_CHECK( + rtIndex < mColorAttachments.size(), + "'rtIndex' ({}) is out of range. Only {} color targets are available.", + rtIndex, mColorAttachments.size() ); @@ -394,7 +399,7 @@ void Fbo::attachColorTarget(const ref& pTexture, uint32_t rtIndex, uint bool allowUav = false; if (pTexture) { - allowUav = ((pTexture->getBindFlags() & Texture::BindFlags::UnorderedAccess) != Texture::BindFlags::None); + allowUav = ((pTexture->getBindFlags() & ResourceBindFlags::UnorderedAccess) != ResourceBindFlags::None); } mTempDesc.setColorTarget(rtIndex, pTexture ? pTexture->getFormat() : ResourceFormat::Unknown, allowUav); @@ -427,12 +432,12 @@ void Fbo::validateAttachment(const Attachment& attachment) const if (mTempDesc.getSampleCount() != pTexture->getSampleCount()) { - throw RuntimeError("Error when validating FBO. Different sample counts in attachments."); + FALCOR_THROW("Error when validating FBO. Different sample counts in attachments."); } if (mIsLayered != (attachment.arraySize > 1)) { - throw RuntimeError("Error when validating FBO. Can't bind both layered and non-layered textures."); + FALCOR_THROW("Error when validating FBO. Can't bind both layered and non-layered textures."); } } } @@ -459,7 +464,7 @@ void Fbo::calcAndValidateProperties() const uint32_t expectedCount = mSamplePositionsPixelCount * mTempDesc.getSampleCount(); if (expectedCount != mSamplePositions.size()) { - throw RuntimeError("Error when validating FBO. The sample positions array size has the wrong size."); + FALCOR_THROW("Error when validating FBO. The sample positions array size has the wrong size."); } } @@ -471,8 +476,10 @@ void Fbo::calcAndValidateProperties() const ref Fbo::getColorTexture(uint32_t index) const { - checkArgument( - index < mColorAttachments.size(), "'index' ({}) is out of range. Only {} color slots are available.", index, + FALCOR_CHECK( + index < mColorAttachments.size(), + "'index' ({}) is out of range. Only {} color slots are available.", + index, mColorAttachments.size() ); return mColorAttachments[index].pTexture; @@ -517,11 +524,11 @@ ref Fbo::create2D( { uint32_t sampleCount = fboDesc.getSampleCount(); - checkArgument(width > 0, "'width' must not be zero."); - checkArgument(height > 0, "'height' must not be zero."); - checkArgument(arraySize > 0, "'arraySize' must not be zero."); - checkArgument(mipLevels > 0, "'mipLevels' must not be zero."); - checkArgument(sampleCount == 1 || mipLevels == 1, "Cannot create multi-sampled texture with more than one mip-level."); + FALCOR_CHECK(width > 0, "'width' must not be zero."); + FALCOR_CHECK(height > 0, "'height' must not be zero."); + FALCOR_CHECK(arraySize > 0, "'arraySize' must not be zero."); + FALCOR_CHECK(mipLevels > 0, "'mipLevels' must not be zero."); + FALCOR_CHECK(sampleCount == 1 || mipLevels == 1, "Cannot create multi-sampled texture with more than one mip-level."); ref pFbo = create(pDevice); @@ -530,7 +537,7 @@ ref Fbo::create2D( { if (fboDesc.getColorTargetFormat(i) != ResourceFormat::Unknown) { - Texture::BindFlags flags = getBindFlags(false, fboDesc.isColorTargetUav(i)); + ResourceBindFlags flags = getBindFlags(false, fboDesc.isColorTargetUav(i)); ref pTex = createTexture2D(pDevice, width, height, fboDesc.getColorTargetFormat(i), sampleCount, arraySize, mipLevels, flags); pFbo->attachColorTarget(pTex, i, 0, 0, kAttachEntireMipLevel); @@ -539,7 +546,7 @@ ref Fbo::create2D( if (fboDesc.getDepthStencilFormat() != ResourceFormat::Unknown) { - Texture::BindFlags flags = getBindFlags(true, fboDesc.isDepthStencilUav()); + ResourceBindFlags flags = getBindFlags(true, fboDesc.isDepthStencilUav()); ref pDepth = createTexture2D(pDevice, width, height, fboDesc.getDepthStencilFormat(), sampleCount, arraySize, mipLevels, flags); pFbo->attachDepthStencilTarget(pDepth, 0, 0, kAttachEntireMipLevel); @@ -557,26 +564,26 @@ ref Fbo::createCubemap( uint32_t mipLevels ) { - checkArgument(width > 0, "'width' must not be zero."); - checkArgument(height > 0, "'height' must not be zero."); - checkArgument(arraySize > 0, "'arraySize' must not be zero."); - checkArgument(mipLevels > 0, "'mipLevels' must not be zero."); - checkArgument(fboDesc.getSampleCount() == 1, "Cannot create multi-sampled cube map."); + FALCOR_CHECK(width > 0, "'width' must not be zero."); + FALCOR_CHECK(height > 0, "'height' must not be zero."); + FALCOR_CHECK(arraySize > 0, "'arraySize' must not be zero."); + FALCOR_CHECK(mipLevels > 0, "'mipLevels' must not be zero."); + FALCOR_CHECK(fboDesc.getSampleCount() == 1, "Cannot create multi-sampled cube map."); ref pFbo = create(pDevice); // Create the color targets for (uint32_t i = 0; i < getMaxColorTargetCount(); i++) { - Texture::BindFlags flags = getBindFlags(false, fboDesc.isColorTargetUav(i)); - auto pTex = Texture::createCube(pDevice, width, height, fboDesc.getColorTargetFormat(i), arraySize, mipLevels, nullptr, flags); + ResourceBindFlags flags = getBindFlags(false, fboDesc.isColorTargetUav(i)); + auto pTex = pDevice->createTextureCube(width, height, fboDesc.getColorTargetFormat(i), arraySize, mipLevels, nullptr, flags); pFbo->attachColorTarget(pTex, i, 0, kAttachEntireMipLevel); } if (fboDesc.getDepthStencilFormat() != ResourceFormat::Unknown) { - Texture::BindFlags flags = getBindFlags(true, fboDesc.isDepthStencilUav()); - auto pDepth = Texture::createCube(pDevice, width, height, fboDesc.getDepthStencilFormat(), arraySize, mipLevels, nullptr, flags); + ResourceBindFlags flags = getBindFlags(true, fboDesc.isDepthStencilUav()); + auto pDepth = pDevice->createTextureCube(width, height, fboDesc.getDepthStencilFormat(), arraySize, mipLevels, nullptr, flags); pFbo->attachDepthStencilTarget(pDepth, 0, kAttachEntireMipLevel); } diff --git a/Source/Falcor/Core/API/GpuFence.cpp b/Source/Falcor/Core/API/Fence.cpp similarity index 59% rename from Source/Falcor/Core/API/GpuFence.cpp rename to Source/Falcor/Core/API/Fence.cpp index c4f2aa01d..5a5dd3dc0 100644 --- a/Source/Falcor/Core/API/GpuFence.cpp +++ b/Source/Falcor/Core/API/Fence.cpp @@ -25,76 +25,64 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#include "GpuFence.h" +#include "Fence.h" #include "Device.h" #include "GFXAPI.h" #include "NativeHandleTraits.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" namespace Falcor { -GpuFence::GpuFence(ref pDevice, bool shared) : mpDevice(pDevice), mCpuValue(1) +Fence::Fence(ref pDevice, FenceDesc desc) : mpDevice(pDevice), mDesc(desc) { FALCOR_ASSERT(mpDevice); - gfx::IFence::Desc fenceDesc = {}; - fenceDesc.isShared = shared; - FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createFence(fenceDesc, mGfxFence.writeRef())); + gfx::IFence::Desc gfxDesc = {}; + mSignaledValue = mDesc.initialValue; + gfxDesc.isShared = mDesc.shared; + FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createFence(gfxDesc, mGfxFence.writeRef())); } -GpuFence::~GpuFence() = default; +Fence::~Fence() = default; -ref GpuFence::create(ref pDevice, bool shared) +uint64_t Fence::signal(uint64_t value) { - return ref(new GpuFence(pDevice, shared)); + uint64_t signalValue = updateSignaledValue(value); + FALCOR_GFX_CALL(mGfxFence->setCurrentValue(signalValue)); + return signalValue; } -uint64_t GpuFence::gpuSignal(CommandQueueHandle pQueue) +void Fence::wait(uint64_t value, uint64_t timeoutNs) { - pQueue->executeCommandBuffers(0, nullptr, mGfxFence, mCpuValue); - mCpuValue++; - return mCpuValue - 1; + uint64_t waitValue = value == kAuto ? mSignaledValue : value; + uint64_t currentValue = getCurrentValue(); + if (currentValue >= waitValue) + return; + gfx::IFence* fences[] = {mGfxFence}; + uint64_t waitValues[] = {waitValue}; + FALCOR_GFX_CALL(mpDevice->getGfxDevice()->waitForFences(1, fences, waitValues, true, timeoutNs)); } -void GpuFence::syncGpu(CommandQueueHandle pQueue) +uint64_t Fence::getCurrentValue() { - gfx::IFence* fences[1]{mGfxFence.get()}; - auto waitValue = mCpuValue - 1; - FALCOR_GFX_CALL(pQueue->waitForFenceValuesOnDevice(std::size(fences), fences, &waitValue)); + uint64_t value; + FALCOR_GFX_CALL(mGfxFence->getCurrentValue(&value)); + return value; } -void GpuFence::syncCpu(std::optional val) +uint64_t Fence::updateSignaledValue(uint64_t value) { - auto waitValue = val ? val.value() : mCpuValue - 1; - uint64_t currentValue = 0; - FALCOR_GFX_CALL(mGfxFence->getCurrentValue(¤tValue)); - if (currentValue < waitValue) - { - gfx::IFence* fences[1]{mGfxFence.get()}; - FALCOR_GFX_CALL(mpDevice->getGfxDevice()->waitForFences(std::size(fences), fences, &waitValue, true, -1)); - } + mSignaledValue = value == kAuto ? mSignaledValue + 1 : value; + return mSignaledValue; } -uint64_t GpuFence::getGpuValue() const -{ - uint64_t currentValue = 0; - FALCOR_GFX_CALL(mGfxFence->getCurrentValue(¤tValue)); - return currentValue; -} - -void GpuFence::setGpuValue(uint64_t val) -{ - FALCOR_GFX_CALL(mGfxFence->setCurrentValue(val)); -} - -SharedResourceApiHandle GpuFence::getSharedApiHandle() const +SharedResourceApiHandle Fence::getSharedApiHandle() const { gfx::InteropHandle sharedHandle; FALCOR_GFX_CALL(mGfxFence->getSharedHandle(&sharedHandle)); return (SharedResourceApiHandle)sharedHandle.handleValue; } -NativeHandle GpuFence::getNativeHandle() const +NativeHandle Fence::getNativeHandle() const { gfx::InteropHandle gfxNativeHandle = {}; FALCOR_GFX_CALL(mGfxFence->getNativeHandle(&gfxNativeHandle)); @@ -108,7 +96,7 @@ NativeHandle GpuFence::getNativeHandle() const return {}; } -void GpuFence::breakStrongReferenceToDevice() +void Fence::breakStrongReferenceToDevice() { mpDevice.breakStrongReference(); } diff --git a/Source/Falcor/Core/API/Fence.h b/Source/Falcor/Core/API/Fence.h new file mode 100644 index 000000000..b92c5b72f --- /dev/null +++ b/Source/Falcor/Core/API/Fence.h @@ -0,0 +1,139 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include "fwd.h" +#include "Handles.h" +#include "NativeHandle.h" +#include "Core/Macros.h" +#include "Core/Object.h" +#include + +namespace Falcor +{ + +struct FenceDesc +{ + bool initialValue{0}; + bool shared{false}; +}; + +/** + * This class represents a fence on the device. + * It is used to synchronize host and device execution. + * On the device, the fence is represented by a 64-bit integer. + * On the host, we keep a copy of the last signaled value. + * By default, the fence value is monotonically incremented every time it is signaled. + * + * To synchronize the host with the device, we can do the following: + * + * ref fence = device->createFence(); + * + * // Signal the fence once we have finished all the above work on the device. + * renderContext->signal(fence); + * + * // Wait on the host until is finished. + * fence->wait(); + */ +class FALCOR_API Fence : public Object +{ + FALCOR_OBJECT(Fence) +public: + static constexpr uint64_t kAuto = std::numeric_limits::max(); + static constexpr uint64_t kTimeoutInfinite = std::numeric_limits::max(); + + /// Constructor. + /// Do not use directly, use Device::createFence instead. + Fence(ref pDevice, FenceDesc desc); + + ~Fence(); + + /// Returns the description. + const FenceDesc& getDesc() const { return mDesc; } + + /** + * Signal the fence. + * This signals the fence from the host. + * @param value The value to signal. If kAuto, the signaled value will be auto-incremented. + * @return Returns the signaled value. + */ + uint64_t signal(uint64_t value = kAuto); + + /** + * Wait for the fence to be signaled on the host. + * Blocks the host until the fence reaches or exceeds the specified value. + * @param value The value to wait for. If kAuto, wait for the last signaled value. + * @param timeoutNs The timeout in nanoseconds. If kTimeoutInfinite, the function will block indefinitely. + */ + void wait(uint64_t value = kAuto, uint64_t timeoutNs = kTimeoutInfinite); + + /// Returns the current value on the device. + uint64_t getCurrentValue(); + + /// Returns the latest signaled value (after auto-increment). + uint64_t getSignaledValue() const { return mSignaledValue; } + + /** + * Updates or increments the signaled value. + * This is used before signaling a fence (from the host, on the device or + * from an external source), to update the internal state. + * The passed value is stored, or if value == kAuto, the last signaled + * value is auto-incremented by one. The returned value is what the caller + * should signal to the fence. + * @param value The value to signal. If kAuto, the signaled value will be auto-incremented. + * @return Returns the value to signal to the fence. + */ + uint64_t updateSignaledValue(uint64_t value = kAuto); + + /** + * Get the internal API handle + */ + gfx::IFence* getGfxFence() const { return mGfxFence; } + + /** + * Returns the native API handle: + * - D3D12: ID3D12Fence* + * - Vulkan: currently not supported + */ + NativeHandle getNativeHandle() const; + + /** + * Creates a shared fence API handle. + */ + SharedResourceApiHandle getSharedApiHandle() const; + + Device* getDevice() const { return mpDevice.get(); } + + void breakStrongReferenceToDevice(); + +private: + BreakableReference mpDevice; + FenceDesc mDesc; + Slang::ComPtr mGfxFence; + uint64_t mSignaledValue{0}; +}; +} // namespace Falcor diff --git a/Source/Falcor/Core/API/FencedPool.h b/Source/Falcor/Core/API/FencedPool.h index 44cca0340..cbacd1997 100644 --- a/Source/Falcor/Core/API/FencedPool.h +++ b/Source/Falcor/Core/API/FencedPool.h @@ -26,10 +26,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "GpuFence.h" -#include "Core/Assert.h" +#include "Fence.h" #include "Core/Macros.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/Object.h" #include @@ -49,7 +48,7 @@ class FALCOR_API FencedPool : public Object * @param[in] pUserData Optional ptr to user data passed to the object creation function. * @return A new object, or throws an exception if creation failed. */ - static ref create(ref pFence, NewObjectFuncType newFunc, void* pUserData = nullptr) + static ref create(ref pFence, NewObjectFuncType newFunc, void* pUserData = nullptr) { return new FencedPool(pFence, newFunc, pUserData); } @@ -63,12 +62,12 @@ class FALCOR_API FencedPool : public Object // Retire the active object Data data; data.alloc = mActiveObject; - data.timestamp = mpFence->getCpuValue(); + data.timestamp = mpFence->getSignaledValue(); mQueue.push(data); // The queue is sorted based on time. Check if the first object is free data = mQueue.front(); - if (data.timestamp <= mpFence->getGpuValue()) + if (data.timestamp < mpFence->getCurrentValue()) { mQueue.pop(); } @@ -82,8 +81,7 @@ class FALCOR_API FencedPool : public Object } private: - FencedPool(ref pFence, NewObjectFuncType newFunc, void* pUserData) - : mNewObjFunc(newFunc), mpFence(pFence), mpUserData(pUserData) + FencedPool(ref pFence, NewObjectFuncType newFunc, void* pUserData) : mNewObjFunc(newFunc), mpFence(pFence), mpUserData(pUserData) { FALCOR_ASSERT(pFence && newFunc); mActiveObject = createObject(); @@ -93,7 +91,7 @@ class FALCOR_API FencedPool : public Object { ObjectType pObj = mNewObjFunc(mpUserData); if (pObj == nullptr) - throw RuntimeError("Failed to create new object in fenced pool"); + FALCOR_THROW("Failed to create new object in fenced pool"); return pObj; } @@ -106,7 +104,7 @@ class FALCOR_API FencedPool : public Object ObjectType mActiveObject; NewObjectFuncType mNewObjFunc = nullptr; std::queue mQueue; - ref mpFence; + ref mpFence; void* mpUserData; }; } // namespace Falcor diff --git a/Source/Falcor/Core/API/Formats.h b/Source/Falcor/Core/API/Formats.h index 756c7feb4..7833a2f1f 100644 --- a/Source/Falcor/Core/API/Formats.h +++ b/Source/Falcor/Core/API/Formats.h @@ -26,8 +26,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Assert.h" #include "Core/Macros.h" +#include "Core/Error.h" #include "Core/Enum.h" #include #include diff --git a/Source/Falcor/Core/API/GFXAPI.cpp b/Source/Falcor/Core/API/GFXAPI.cpp index 10fa8488e..168f85e8a 100644 --- a/Source/Falcor/Core/API/GFXAPI.cpp +++ b/Source/Falcor/Core/API/GFXAPI.cpp @@ -26,7 +26,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "GFXAPI.h" -#include "Core/ErrorHandling.h" +#include "Aftermath.h" +#include "Core/Error.h" +#include "Utils/Logger.h" #if FALCOR_HAS_D3D12 #include "dxgi.h" @@ -34,7 +36,7 @@ namespace Falcor { -void gfxReportError(const char* msg, gfx::Result result) +void gfxReportError(const char* api, const char* call, gfx::Result result) { const char* resultStr = nullptr; #if FALCOR_HAS_D3D12 @@ -52,9 +54,14 @@ void gfxReportError(const char* msg, gfx::Result result) } #endif - if (resultStr) - reportFatalError(fmt::format("GFX ERROR: {}\nResult: {} ({})", msg, result, resultStr)); - else - reportFatalError(fmt::format("GFX ERROR: {}\nResult: {}", msg, result)); +#if FALCOR_HAS_AFTERMATH + if (!waitForAftermathDumps()) + logError("Aftermath GPU crash dump generation failed."); +#endif + + std::string fullMsg = resultStr ? fmt::format("{} call '{}' failed with error {} ({}).", api, call, result, resultStr) + : fmt::format("{} call '{}' failed with error {}", api, call, result); + + reportFatalErrorAndTerminate(fullMsg); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/GFXAPI.h b/Source/Falcor/Core/API/GFXAPI.h index 8bdc91910..3cbea5ddd 100644 --- a/Source/Falcor/Core/API/GFXAPI.h +++ b/Source/Falcor/Core/API/GFXAPI.h @@ -34,23 +34,23 @@ #include #include -#define FALCOR_GFX_CALL(a) \ - { \ - gfx::Result result_ = a; \ - if (SLANG_FAILED(result_)) \ - { \ - gfxReportError(#a, result_); \ - } \ +#define FALCOR_GFX_CALL(call) \ + { \ + gfx::Result result_ = call; \ + if (SLANG_FAILED(result_)) \ + { \ + gfxReportError("GFX", #call, result_); \ + } \ } #if FALCOR_HAS_D3D12 -#define FALCOR_D3D_CALL(_a) \ - { \ - HRESULT hr_ = _a; \ - if (FAILED(hr_)) \ - { \ - gfxReportError(#_a, hr_); \ - } \ +#define FALCOR_D3D_CALL(call) \ + { \ + HRESULT result_ = call; \ + if (FAILED(result_)) \ + { \ + gfxReportError("D3D", #call, result_); \ + } \ } #define FALCOR_GET_COM_INTERFACE(_base, _type, _var) \ FALCOR_MAKE_SMART_COM_PTR(_type); \ @@ -62,7 +62,9 @@ namespace Falcor { /** - * Log a message if hr indicates an error + * Report a GFX or D3D error. + * This will throw a RuntimeError exception. */ -FALCOR_API void gfxReportError(const char* msg, gfx::Result result); +FALCOR_API void gfxReportError(const char* api, const char* call, gfx::Result result); + } // namespace Falcor diff --git a/Source/Falcor/Core/API/GFXHelpers.cpp b/Source/Falcor/Core/API/GFXHelpers.cpp index 7c63c1e22..8ea294046 100644 --- a/Source/Falcor/Core/API/GFXHelpers.cpp +++ b/Source/Falcor/Core/API/GFXHelpers.cpp @@ -231,51 +231,51 @@ gfx::ResourceState getGFXResourceState(Resource::State state) } } -void getGFXResourceState(Resource::BindFlags flags, gfx::ResourceState& defaultState, gfx::ResourceStateSet& allowedStates) +void getGFXResourceState(ResourceBindFlags flags, gfx::ResourceState& defaultState, gfx::ResourceStateSet& allowedStates) { defaultState = gfx::ResourceState::General; allowedStates = gfx::ResourceStateSet(defaultState); // setting up the following flags requires Slang gfx resourece states to have integral type - if (is_set(flags, Resource::BindFlags::UnorderedAccess)) + if (is_set(flags, ResourceBindFlags::UnorderedAccess)) { allowedStates.add(gfx::ResourceState::UnorderedAccess); } - if (is_set(flags, Resource::BindFlags::ShaderResource)) + if (is_set(flags, ResourceBindFlags::ShaderResource)) { allowedStates.add(gfx::ResourceState::ShaderResource); } - if (is_set(flags, Resource::BindFlags::RenderTarget)) + if (is_set(flags, ResourceBindFlags::RenderTarget)) { allowedStates.add(gfx::ResourceState::RenderTarget); } - if (is_set(flags, Resource::BindFlags::DepthStencil)) + if (is_set(flags, ResourceBindFlags::DepthStencil)) { allowedStates.add(gfx::ResourceState::DepthWrite); } - if (is_set(flags, Resource::BindFlags::Vertex)) + if (is_set(flags, ResourceBindFlags::Vertex)) { allowedStates.add(gfx::ResourceState::VertexBuffer); allowedStates.add(gfx::ResourceState::AccelerationStructureBuildInput); } - if (is_set(flags, Resource::BindFlags::Index)) + if (is_set(flags, ResourceBindFlags::Index)) { allowedStates.add(gfx::ResourceState::IndexBuffer); allowedStates.add(gfx::ResourceState::AccelerationStructureBuildInput); } - if (is_set(flags, Resource::BindFlags::IndirectArg)) + if (is_set(flags, ResourceBindFlags::IndirectArg)) { allowedStates.add(gfx::ResourceState::IndirectArgument); } - if (is_set(flags, Resource::BindFlags::Constant)) + if (is_set(flags, ResourceBindFlags::Constant)) { allowedStates.add(gfx::ResourceState::ConstantBuffer); } - if (is_set(flags, Resource::BindFlags::AccelerationStructure)) + if (is_set(flags, ResourceBindFlags::AccelerationStructure)) { allowedStates.add(gfx::ResourceState::AccelerationStructure); allowedStates.add(gfx::ResourceState::ShaderResource); diff --git a/Source/Falcor/Core/API/GFXHelpers.h b/Source/Falcor/Core/API/GFXHelpers.h index 759f885a2..e18d2f5ce 100644 --- a/Source/Falcor/Core/API/GFXHelpers.h +++ b/Source/Falcor/Core/API/GFXHelpers.h @@ -37,5 +37,5 @@ namespace Falcor gfx::Format getGFXFormat(ResourceFormat format); gfx::ResourceState getGFXResourceState(Resource::State state); -void getGFXResourceState(Resource::BindFlags flags, gfx::ResourceState& defaultState, gfx::ResourceStateSet& allowedStates); +void getGFXResourceState(ResourceBindFlags flags, gfx::ResourceState& defaultState, gfx::ResourceStateSet& allowedStates); } // namespace Falcor diff --git a/Source/Falcor/Core/API/GpuFence.h b/Source/Falcor/Core/API/GpuFence.h deleted file mode 100644 index 5378375f9..000000000 --- a/Source/Falcor/Core/API/GpuFence.h +++ /dev/null @@ -1,120 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#pragma once -#include "fwd.h" -#include "Handles.h" -#include "NativeHandle.h" -#include "Core/Macros.h" -#include "Core/Object.h" -#include - -namespace Falcor -{ -// TODO(@skallweit): This type should be removed. -// We only keep it to have LowLevelContextData::getCommandQueue() working as before. -using CommandQueueHandle = gfx::ICommandQueue*; - -/** - * This class can be used to synchronize GPU and CPU execution - * It's value monotonically increasing - every time a signal is sent, it will change the value first - */ -class FALCOR_API GpuFence : public Object -{ - FALCOR_OBJECT(GpuFence) -public: - ~GpuFence(); - - /** - * Create a new GPU fence. - * @return A new object, or throws an exception if creation failed. - */ - static ref create(ref pDevice, bool shared = false); - - /** - * Get the internal API handle - */ - gfx::IFence* getGfxFence() const { return mGfxFence; } - - /** - * Returns the native API handle: - * - D3D12: ID3D12Fence* - * - Vulkan: currently not supported - */ - NativeHandle getNativeHandle() const; - - /** - * Get the last value the GPU has signaled - */ - uint64_t getGpuValue() const; - - /** - * Sets the current GPU value to a specific value (signals the fence from the CPU side). - */ - void setGpuValue(uint64_t val); - - /** - * Get the current CPU value - */ - uint64_t getCpuValue() const { return mCpuValue; } - - /** - * Tell the GPU to wait until the fence reaches the last GPU-value signaled (which is (mCpuValue - 1)) - */ - void syncGpu(CommandQueueHandle pQueue); - - /** - * Tell the CPU to wait until the fence reaches the current value - */ - void syncCpu(std::optional val = {}); - - /** - * Insert a signal command into the command queue. This will increase the internal value - */ - uint64_t gpuSignal(CommandQueueHandle pQueue); - - /** - * Must be called to obtain the current value for use in an external semaphore. - */ - uint64_t externalSignal() { return mCpuValue++; } - - /** - * Creates a shared fence API handle. - */ - SharedResourceApiHandle getSharedApiHandle() const; - - void breakStrongReferenceToDevice(); - -private: - GpuFence(ref pDevice, bool shared); - - BreakableReference mpDevice; - Slang::ComPtr mGfxFence; - uint64_t mCpuValue; - mutable SharedResourceApiHandle mSharedApiHandle = 0; -}; -} // namespace Falcor diff --git a/Source/Falcor/Core/API/GpuMemoryHeap.cpp b/Source/Falcor/Core/API/GpuMemoryHeap.cpp index cd7c98f33..db47b8e67 100644 --- a/Source/Falcor/Core/API/GpuMemoryHeap.cpp +++ b/Source/Falcor/Core/API/GpuMemoryHeap.cpp @@ -26,11 +26,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "GpuMemoryHeap.h" -#include "GpuFence.h" +#include "Fence.h" #include "Buffer.h" #include "Device.h" #include "GFXAPI.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/Math/Common.h" namespace Falcor @@ -40,15 +40,15 @@ GpuMemoryHeap::~GpuMemoryHeap() mDeferredReleases = decltype(mDeferredReleases)(); } -GpuMemoryHeap::GpuMemoryHeap(ref pDevice, Type type, size_t pageSize, ref pFence) - : mpDevice(pDevice), mType(type), mpFence(pFence), mPageSize(pageSize) +GpuMemoryHeap::GpuMemoryHeap(ref pDevice, MemoryType memoryType, size_t pageSize, ref pFence) + : mpDevice(pDevice), mMemoryType(memoryType), mpFence(pFence), mPageSize(pageSize) { allocateNewPage(); } -ref GpuMemoryHeap::create(ref pDevice, Type type, size_t pageSize, ref pFence) +ref GpuMemoryHeap::create(ref pDevice, MemoryType memoryType, size_t pageSize, ref pFence) { - return ref(new GpuMemoryHeap(pDevice, type, pageSize, pFence)); + return ref(new GpuMemoryHeap(pDevice, memoryType, pageSize, pFence)); } void GpuMemoryHeap::allocateNewPage() @@ -94,6 +94,7 @@ GpuMemoryHeap::Allocation GpuMemoryHeap::allocate(size_t size, size_t alignment) } data.pageID = mCurrentPageId; + data.size = size; data.offset = currentOffset; data.pData = mpActivePage->pData + currentOffset; data.gfxBufferResource = mpActivePage->gfxBufferResource; @@ -101,10 +102,16 @@ GpuMemoryHeap::Allocation GpuMemoryHeap::allocate(size_t size, size_t alignment) mpActivePage->allocationsCount++; } - data.fenceValue = mpFence->getCpuValue(); + data.fenceValue = mpFence->getSignaledValue(); return data; } +GpuMemoryHeap::Allocation GpuMemoryHeap::allocate(size_t size, ResourceBindFlags bindFlags) +{ + size_t alignment = mpDevice->getBufferDataAlignment(bindFlags); + return allocate(align_to(alignment, size), alignment); +} + void GpuMemoryHeap::release(Allocation& data) { FALCOR_ASSERT(data.gfxBufferResource); @@ -113,8 +120,8 @@ void GpuMemoryHeap::release(Allocation& data) void GpuMemoryHeap::executeDeferredReleases() { - uint64_t gpuVal = mpFence->getGpuValue(); - while (mDeferredReleases.size() && mDeferredReleases.top().fenceValue <= gpuVal) + uint64_t currentValue = mpFence->getCurrentValue(); + while (mDeferredReleases.size() && mDeferredReleases.top().fenceValue < currentValue) { const Allocation& data = mDeferredReleases.top(); if (data.pageID == mCurrentPageId) @@ -143,41 +150,25 @@ void GpuMemoryHeap::executeDeferredReleases() } } -Slang::ComPtr createBuffer( +Slang::ComPtr createBufferResource( ref pDevice, Buffer::State initState, size_t size, - Buffer::BindFlags bindFlags, - Buffer::CpuAccess cpuAccess + ResourceBindFlags bindFlags, + MemoryType memoryType ); namespace { -Buffer::CpuAccess getCpuAccess(GpuMemoryHeap::Type t) -{ - switch (t) - { - case GpuMemoryHeap::Type::Default: - return Buffer::CpuAccess::None; - case GpuMemoryHeap::Type::Upload: - return Buffer::CpuAccess::Write; - case GpuMemoryHeap::Type::Readback: - return Buffer::CpuAccess::Read; - default: - FALCOR_UNREACHABLE(); - return Buffer::CpuAccess::None; - } -} - -Buffer::State getInitState(GpuMemoryHeap::Type t) +Buffer::State getInitState(MemoryType memoryType) { - switch (t) + switch (memoryType) { - case GpuMemoryHeap::Type::Default: + case MemoryType::DeviceLocal: return Buffer::State::Common; - case GpuMemoryHeap::Type::Upload: + case MemoryType::Upload: return Buffer::State::GenericRead; - case GpuMemoryHeap::Type::Readback: + case MemoryType::ReadBack: return Buffer::State::CopyDest; default: FALCOR_UNREACHABLE(); @@ -188,10 +179,14 @@ Buffer::State getInitState(GpuMemoryHeap::Type t) void GpuMemoryHeap::initBasePageData(BaseData& data, size_t size) { - data.gfxBufferResource = createBuffer( - mpDevice, getInitState(mType), size, Buffer::BindFlags::Vertex | Buffer::BindFlags::Index | Buffer::BindFlags::Constant, - getCpuAccess(mType) + data.gfxBufferResource = createBufferResource( + mpDevice, + getInitState(mMemoryType), + size, + ResourceBindFlags::Vertex | ResourceBindFlags::Index | ResourceBindFlags::Constant, + mMemoryType ); + data.size = size; data.offset = 0; FALCOR_GFX_CALL(data.gfxBufferResource->map(nullptr, (void**)&data.pData)); } diff --git a/Source/Falcor/Core/API/GpuMemoryHeap.h b/Source/Falcor/Core/API/GpuMemoryHeap.h index c03b3b32b..cef7cbc69 100644 --- a/Source/Falcor/Core/API/GpuMemoryHeap.h +++ b/Source/Falcor/Core/API/GpuMemoryHeap.h @@ -28,7 +28,9 @@ #pragma once #include "fwd.h" #include "Handles.h" -#include "GpuFence.h" +#include "Resource.h" +#include "Buffer.h" +#include "Fence.h" #include "Core/Macros.h" #include "Core/Object.h" #include @@ -40,18 +42,14 @@ class FALCOR_API GpuMemoryHeap : public Object { FALCOR_OBJECT(GpuMemoryHeap) public: - enum class Type - { - Default, - Upload, - Readback - }; - struct BaseData { Slang::ComPtr gfxBufferResource; + uint32_t size = 0; GpuAddress offset = 0; uint8_t* pData = nullptr; + + uint64_t getGpuAddress() const { return gfxBufferResource->getDeviceAddress() + offset; } }; struct Allocation : public BaseData @@ -67,14 +65,15 @@ class FALCOR_API GpuMemoryHeap : public Object /** * Create a new GPU memory heap. - * @param[in] type The type of heap. + * @param[in] memoryType The memory type of heap. * @param[in] pageSize Page size in bytes. * @param[in] pFence Fence to use for synchronization. * @return A new object, or throws an exception if creation failed. */ - static ref create(ref pDevice, Type type, size_t pageSize, ref pFence); + static ref create(ref pDevice, MemoryType memoryType, size_t pageSize, ref pFence); Allocation allocate(size_t size, size_t alignment = 1); + Allocation allocate(size_t size, ResourceBindFlags bindFlags); void release(Allocation& data); size_t getPageSize() const { return mPageSize; } void executeDeferredReleases(); @@ -82,7 +81,7 @@ class FALCOR_API GpuMemoryHeap : public Object void breakStrongReferenceToDevice(); private: - GpuMemoryHeap(ref pDevice, Type type, size_t pageSize, ref pFence); + GpuMemoryHeap(ref pDevice, MemoryType memoryType, size_t pageSize, ref pFence); struct PageData : public BaseData { @@ -93,8 +92,8 @@ class FALCOR_API GpuMemoryHeap : public Object }; BreakableReference mpDevice; - Type mType; - ref mpFence; + MemoryType mMemoryType; + ref mpFence; size_t mPageSize = 0; size_t mCurrentPageId = 0; PageData::UniquePtr mpActivePage; diff --git a/Source/Falcor/Core/API/GpuTimer.cpp b/Source/Falcor/Core/API/GpuTimer.cpp index bd3a74671..6e829f05a 100644 --- a/Source/Falcor/Core/API/GpuTimer.cpp +++ b/Source/Falcor/Core/API/GpuTimer.cpp @@ -32,7 +32,7 @@ #include "RenderContext.h" #include "GFXAPI.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/ObjectPython.h" #include "Utils/Logger.h" #include "Utils/Scripting/ScriptBindings.h" @@ -49,9 +49,9 @@ GpuTimer::GpuTimer(ref pDevice) : mpDevice(pDevice) { FALCOR_ASSERT(mpDevice); - mpResolveBuffer = Buffer::create(mpDevice, sizeof(uint64_t) * 2, Buffer::BindFlags::None, Buffer::CpuAccess::None, nullptr); + mpResolveBuffer = mpDevice->createBuffer(sizeof(uint64_t) * 2, ResourceBindFlags::None, MemoryType::DeviceLocal, nullptr); mpResolveBuffer->breakStrongReferenceToDevice(); - mpResolveStagingBuffer = Buffer::create(mpDevice, sizeof(uint64_t) * 2, Buffer::BindFlags::None, Buffer::CpuAccess::Read, nullptr); + mpResolveStagingBuffer = mpDevice->createBuffer(sizeof(uint64_t) * 2, ResourceBindFlags::None, MemoryType::ReadBack, nullptr); mpResolveStagingBuffer->breakStrongReferenceToDevice(); // Create timestamp query heap upon first use. @@ -59,7 +59,7 @@ GpuTimer::GpuTimer(ref pDevice) : mpDevice(pDevice) mEnd = mpDevice->getTimestampQueryHeap()->allocate(); if (mStart == QueryHeap::kInvalidIndex || mEnd == QueryHeap::kInvalidIndex) { - throw RuntimeError("Can't create GPU timer, no available timestamp queries."); + FALCOR_THROW("Can't create GPU timer, no available timestamp queries."); } FALCOR_ASSERT(mEnd == (mStart + 1)); } @@ -117,7 +117,7 @@ void GpuTimer::resolve() if (mStatus == Status::Begin) { - throw RuntimeError("GpuTimer::resolve() was called but the GpuTimer::end() wasn't called."); + FALCOR_THROW("GpuTimer::resolve() was called but the GpuTimer::end() wasn't called."); } FALCOR_ASSERT(mStatus == Status::End); diff --git a/Source/Falcor/Core/API/GraphicsStateObject.cpp b/Source/Falcor/Core/API/GraphicsStateObject.cpp index ce34a3fc4..b875984ef 100644 --- a/Source/Falcor/Core/API/GraphicsStateObject.cpp +++ b/Source/Falcor/Core/API/GraphicsStateObject.cpp @@ -158,19 +158,19 @@ void getGFXStencilDesc(gfx::DepthStencilOpDesc& gfxDesc, DepthStencilState::Sten gfxDesc.stencilFunc = getGFXComparisonFunc(desc.func); } -gfx::PrimitiveType getGFXPrimitiveType(GraphicsStateObject::PrimitiveType primitiveType) +gfx::PrimitiveType getGFXPrimitiveType(GraphicsStateObjectDesc::PrimitiveType primitiveType) { switch (primitiveType) { - case Falcor::GraphicsStateObject::PrimitiveType::Undefined: + case GraphicsStateObjectDesc::PrimitiveType::Undefined: return gfx::PrimitiveType::Triangle; - case Falcor::GraphicsStateObject::PrimitiveType::Point: + case GraphicsStateObjectDesc::PrimitiveType::Point: return gfx::PrimitiveType::Point; - case Falcor::GraphicsStateObject::PrimitiveType::Line: + case GraphicsStateObjectDesc::PrimitiveType::Line: return gfx::PrimitiveType::Line; - case Falcor::GraphicsStateObject::PrimitiveType::Triangle: + case GraphicsStateObjectDesc::PrimitiveType::Triangle: return gfx::PrimitiveType::Triangle; - case Falcor::GraphicsStateObject::PrimitiveType::Patch: + case GraphicsStateObjectDesc::PrimitiveType::Patch: return gfx::PrimitiveType::Patch; default: FALCOR_UNREACHABLE(); @@ -226,52 +226,13 @@ ref GraphicsStateObject::spDefaultBlendState; ref GraphicsStateObject::spDefaultRasterizerState; ref GraphicsStateObject::spDefaultDepthStencilState; -bool GraphicsStateObject::Desc::operator==(const GraphicsStateObject::Desc& other) const -{ - bool b = true; - b = b && (mpLayout == other.mpLayout); - b = b && (mFboDesc == other.mFboDesc); - b = b && (mpProgram == other.mpProgram); - b = b && (mSampleMask == other.mSampleMask); - b = b && (mPrimType == other.mPrimType); - - if (mpRasterizerState) - { - b = b && (mpRasterizerState == other.mpRasterizerState); - } - else - { - b = b && (other.mpRasterizerState == nullptr || other.mpRasterizerState == spDefaultRasterizerState); - } - - if (mpBlendState) - { - b = b && (mpBlendState == other.mpBlendState); - } - else - { - b = b && (other.mpBlendState == nullptr || other.mpBlendState == spDefaultBlendState); - } - - if (mpDepthStencilState) - { - b = b && (mpDepthStencilState == other.mpDepthStencilState); - } - else - { - b = b && (other.mpDepthStencilState == nullptr || other.mpDepthStencilState == spDefaultDepthStencilState); - } - - return b; -} - GraphicsStateObject::~GraphicsStateObject() { mpDevice->releaseResource(mGfxPipelineState); mpDevice->releaseResource(mpGFXRenderPassLayout); } -GraphicsStateObject::GraphicsStateObject(ref pDevice, const Desc& desc) : mpDevice(pDevice), mDesc(desc) +GraphicsStateObject::GraphicsStateObject(ref pDevice, const GraphicsStateObjectDesc& desc) : mpDevice(pDevice), mDesc(desc) { if (spDefaultBlendState == nullptr) { @@ -282,16 +243,16 @@ GraphicsStateObject::GraphicsStateObject(ref pDevice, const Desc& desc) } // Initialize default objects - if (!mDesc.mpBlendState) - mDesc.mpBlendState = spDefaultBlendState; - if (!mDesc.mpRasterizerState) - mDesc.mpRasterizerState = spDefaultRasterizerState; - if (!mDesc.mpDepthStencilState) - mDesc.mpDepthStencilState = spDefaultDepthStencilState; + if (!mDesc.pBlendState) + mDesc.pBlendState = spDefaultBlendState; + if (!mDesc.pRasterizerState) + mDesc.pRasterizerState = spDefaultRasterizerState; + if (!mDesc.pDepthStencilState) + mDesc.pDepthStencilState = spDefaultDepthStencilState; gfx::GraphicsPipelineStateDesc gfxDesc = {}; // Set blend state. - auto blendState = mDesc.getBlendState(); + const auto& blendState = mDesc.pBlendState; FALCOR_ASSERT(blendState->getRtCount() <= gfx::kMaxRenderTargetCount); auto& targetBlendDescs = gfxDesc.blend.targets; { @@ -324,7 +285,7 @@ GraphicsStateObject::GraphicsStateObject(ref pDevice, const Desc& desc) // Set depth stencil state. { - auto depthStencilState = mDesc.getDepthStencilState(); + const auto& depthStencilState = mDesc.pDepthStencilState; getGFXStencilDesc(gfxDesc.depthStencil.backFace, depthStencilState->getStencilDesc(Falcor::DepthStencilState::Face::Back)); getGFXStencilDesc(gfxDesc.depthStencil.frontFace, depthStencilState->getStencilDesc(Falcor::DepthStencilState::Face::Front)); gfxDesc.depthStencil.depthFunc = getGFXComparisonFunc(depthStencilState->getDepthFunc()); @@ -338,7 +299,7 @@ GraphicsStateObject::GraphicsStateObject(ref pDevice, const Desc& desc) // Set raterizer state. { - auto rasterState = mDesc.getRasterizerState(); + const auto& rasterState = mDesc.pRasterizerState; gfxDesc.rasterizer.antialiasedLineEnable = rasterState->isLineAntiAliasingEnabled(); gfxDesc.rasterizer.cullMode = getGFXCullMode(rasterState->getCullMode()); gfxDesc.rasterizer.depthBias = rasterState->getDepthBias(); @@ -348,7 +309,7 @@ GraphicsStateObject::GraphicsStateObject(ref pDevice, const Desc& desc) gfxDesc.rasterizer.fillMode = getGFXFillMode(rasterState->getFillMode()); gfxDesc.rasterizer.frontFace = rasterState->isFrontCounterCW() ? gfx::FrontFaceMode::CounterClockwise : gfx::FrontFaceMode::Clockwise; - gfxDesc.rasterizer.multisampleEnable = mDesc.getFboDesc().getSampleCount() != 1; + gfxDesc.rasterizer.multisampleEnable = mDesc.fboDesc.getSampleCount() != 1; gfxDesc.rasterizer.scissorEnable = rasterState->isScissorTestEnabled(); gfxDesc.rasterizer.enableConservativeRasterization = rasterState->isConservativeRasterizationEnabled(); gfxDesc.rasterizer.forcedSampleCount = rasterState->getForcedSampleCount(); @@ -356,14 +317,14 @@ GraphicsStateObject::GraphicsStateObject(ref pDevice, const Desc& desc) // Create input layout. { - auto vertexLayout = mDesc.getVertexLayout(); + const auto& vertexLayout = mDesc.pVertexLayout; if (vertexLayout) { std::vector vertexStreams(vertexLayout->getBufferCount()); std::vector inputElements; for (size_t i = 0; i < vertexLayout->getBufferCount(); ++i) { - auto& bufferLayout = mDesc.getVertexLayout()->getBufferLayout(i); + auto& bufferLayout = mDesc.pVertexLayout->getBufferLayout(i); vertexStreams[i].instanceDataStepRate = bufferLayout->getInstanceStepRate(); vertexStreams[i].slotClass = getGFXInputSlotClass(bufferLayout->getInputClass()); vertexStreams[i].stride = bufferLayout->getStride(); @@ -400,10 +361,10 @@ GraphicsStateObject::GraphicsStateObject(ref pDevice, const Desc& desc) // Create framebuffer layout. gfx::IFramebufferLayout::Desc gfxFbDesc = {}; { - auto fboDesc = mDesc.getFboDesc(); + const auto& fboDesc = mDesc.fboDesc; gfx::IFramebufferLayout::TargetLayout depthAttachment = {}; std::vector attachments(Fbo::getMaxColorTargetCount()); - if (mDesc.getFboDesc().getDepthStencilFormat() != ResourceFormat::Unknown) + if (mDesc.fboDesc.getDepthStencilFormat() != ResourceFormat::Unknown) { depthAttachment.format = getGFXFormat(fboDesc.getDepthStencilFormat()); depthAttachment.sampleCount = fboDesc.getSampleCount(); @@ -433,7 +394,7 @@ GraphicsStateObject::GraphicsStateObject(ref pDevice, const Desc& desc) depthAccess.stencilLoadOp = gfx::IRenderPassLayout::TargetLoadOp::Load; depthAccess.stencilStoreOp = gfx::IRenderPassLayout::TargetStoreOp::Store; depthAccess.storeOp = gfx::IRenderPassLayout::TargetStoreOp::Store; - if (this->mDesc.getFboDesc().getDepthStencilFormat() != ResourceFormat::Unknown) + if (this->mDesc.fboDesc.getDepthStencilFormat() != ResourceFormat::Unknown) { renderPassDesc.depthStencilAccess = &depthAccess; } @@ -451,17 +412,12 @@ GraphicsStateObject::GraphicsStateObject(ref pDevice, const Desc& desc) FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createRenderPassLayout(renderPassDesc, mpGFXRenderPassLayout.writeRef())); } - gfxDesc.primitiveType = getGFXPrimitiveType(mDesc.getPrimitiveType()); - gfxDesc.program = mDesc.getProgramKernels()->getGfxProgram(); + gfxDesc.primitiveType = getGFXPrimitiveType(mDesc.primitiveType); + gfxDesc.program = mDesc.pProgramKernels->getGfxProgram(); FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createGraphicsPipelineState(gfxDesc, mGfxPipelineState.writeRef())); } -ref GraphicsStateObject::create(ref pDevice, const Desc& desc) -{ - return ref(new GraphicsStateObject(pDevice, desc)); -} - void GraphicsStateObject::breakStrongReferenceToDevice() { mpDevice.breakStrongReference(); diff --git a/Source/Falcor/Core/API/GraphicsStateObject.h b/Source/Falcor/Core/API/GraphicsStateObject.h index aafb6be83..66854f63e 100644 --- a/Source/Falcor/Core/API/GraphicsStateObject.h +++ b/Source/Falcor/Core/API/GraphicsStateObject.h @@ -38,10 +38,9 @@ namespace Falcor { -class FALCOR_API GraphicsStateObject : public Object + +struct GraphicsStateObjectDesc { - FALCOR_OBJECT(GraphicsStateObject) -public: static constexpr uint32_t kSampleMaskAll = -1; /** @@ -56,102 +55,48 @@ class FALCOR_API GraphicsStateObject : public Object Patch, }; - class FALCOR_API Desc - { - public: - Desc& setVertexLayout(ref pLayout) - { - mpLayout = pLayout; - return *this; - } - - Desc& setFboFormats(const Fbo::Desc& fboFormats) - { - mFboDesc = fboFormats; - return *this; - } - - Desc& setProgramKernels(ref pProgram) - { - mpProgram = pProgram; - return *this; - } - - Desc& setBlendState(ref pBlendState) - { - mpBlendState = pBlendState; - return *this; - } - - Desc& setRasterizerState(ref pRasterizerState) - { - mpRasterizerState = pRasterizerState; - return *this; - } - - Desc& setDepthStencilState(ref pDepthStencilState) - { - mpDepthStencilState = pDepthStencilState; - return *this; - } - - Desc& setSampleMask(uint32_t sampleMask) - { - mSampleMask = sampleMask; - return *this; - } - - Desc& setPrimitiveType(PrimitiveType type) - { - mPrimType = type; - return *this; - } - - ref getBlendState() const { return mpBlendState; } - ref getRasterizerState() const { return mpRasterizerState; } - ref getDepthStencilState() const { return mpDepthStencilState; } - ref getProgramKernels() const { return mpProgram; } - uint32_t getSampleMask() const { return mSampleMask; } - ref getVertexLayout() const { return mpLayout; } - PrimitiveType getPrimitiveType() const { return mPrimType; } - Fbo::Desc getFboDesc() const { return mFboDesc; } + Fbo::Desc fboDesc; + ref pVertexLayout; + ref pProgramKernels; + ref pRasterizerState; + ref pDepthStencilState; + ref pBlendState; + uint32_t sampleMask = kSampleMaskAll; + PrimitiveType primitiveType = PrimitiveType::Undefined; - bool operator==(const Desc& other) const; - - private: - friend class GraphicsStateObject; - Fbo::Desc mFboDesc; - ref mpLayout; - ref mpProgram; - ref mpRasterizerState; - ref mpDepthStencilState; - ref mpBlendState; - uint32_t mSampleMask = kSampleMaskAll; - PrimitiveType mPrimType = PrimitiveType::Undefined; - }; + bool operator==(const GraphicsStateObjectDesc& other) const + { + bool result = true; + result = result && (fboDesc == other.fboDesc); + result = result && (pVertexLayout == other.pVertexLayout); + result = result && (pProgramKernels == other.pProgramKernels); + result = result && (sampleMask == other.sampleMask); + result = result && (primitiveType == other.primitiveType); + result = result && (pRasterizerState == other.pRasterizerState); + result = result && (pBlendState == other.pBlendState); + result = result && (pDepthStencilState == other.pDepthStencilState); + return result; + } +}; +class FALCOR_API GraphicsStateObject : public Object +{ + FALCOR_OBJECT(GraphicsStateObject) +public: + GraphicsStateObject(ref pDevice, const GraphicsStateObjectDesc& desc); ~GraphicsStateObject(); - /** - * Create a graphics state object. - * @param[in] desc State object description. - * @return New object, or throws an exception if creation failed. - */ - static ref create(ref pDevice, const Desc& desc); - gfx::IPipelineState* getGfxPipelineState() const { return mGfxPipelineState; } - const Desc& getDesc() const { return mDesc; } + const GraphicsStateObjectDesc& getDesc() const { return mDesc; } gfx::IRenderPassLayout* getGFXRenderPassLayout() const { return mpGFXRenderPassLayout.get(); } void breakStrongReferenceToDevice(); private: - GraphicsStateObject(ref pDevice, const Desc& desc); - BreakableReference mpDevice; - Desc mDesc; + GraphicsStateObjectDesc mDesc; Slang::ComPtr mGfxPipelineState; Slang::ComPtr mpGFXInputLayout; diff --git a/Source/Falcor/Core/API/LowLevelContextData.cpp b/Source/Falcor/Core/API/LowLevelContextData.cpp index 13bf64d00..c4353cfa5 100644 --- a/Source/Falcor/Core/API/LowLevelContextData.cpp +++ b/Source/Falcor/Core/API/LowLevelContextData.cpp @@ -30,15 +30,29 @@ #include "GFXAPI.h" #include "NativeHandleTraits.h" +#if FALCOR_HAS_CUDA +#include "Utils/CudaUtils.h" +#endif + #include namespace Falcor { LowLevelContextData::LowLevelContextData(Device* pDevice, gfx::ICommandQueue* pQueue) : mpDevice(pDevice), mpGfxCommandQueue(pQueue) { - mpFence = GpuFence::create(ref(mpDevice)); + mpFence = mpDevice->createFence(); mpFence->breakStrongReferenceToDevice(); - FALCOR_ASSERT(mpFence); + +#if FALCOR_HAS_CUDA + // GFX currently doesn't support shared fences on Vulkan. + if (mpDevice->getType() == Device::Type::D3D12) + { + mpDevice->initCudaDevice(); + mpCudaFence = mpDevice->createFence(true); + mpCudaFence->breakStrongReferenceToDevice(); + mpCudaSemaphore = make_ref(mpCudaFence); + } +#endif openCommandBuffer(); } @@ -95,10 +109,10 @@ void LowLevelContextData::openCommandBuffer() mpCommandBuffer = mGfxCommandBuffer.get(); } -void LowLevelContextData::flush() +void LowLevelContextData::submitCommandBuffer() { closeCommandBuffer(); - mpGfxCommandQueue->executeCommandBuffers(1, mGfxCommandBuffer.readRef(), mpFence->getGfxFence(), mpFence->externalSignal()); + mpGfxCommandQueue->executeCommandBuffers(1, mGfxCommandBuffer.readRef(), mpFence->getGfxFence(), mpFence->updateSignaledValue()); openCommandBuffer(); } diff --git a/Source/Falcor/Core/API/LowLevelContextData.h b/Source/Falcor/Core/API/LowLevelContextData.h index 1492125b3..f84a82563 100644 --- a/Source/Falcor/Core/API/LowLevelContextData.h +++ b/Source/Falcor/Core/API/LowLevelContextData.h @@ -26,16 +26,18 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "GpuFence.h" +#include "Fence.h" #include "Handles.h" #include "NativeHandle.h" #include "Core/Macros.h" namespace Falcor { -// TODO(@skallweit): This type should be removed. -// We only keep it to have LowLevelContextData::getCommandQueue() working as before. -using CommandQueueHandle = gfx::ICommandQueue*; + +namespace cuda_utils +{ +class ExternalSemaphore; +} class FALCOR_API LowLevelContextData { @@ -65,13 +67,16 @@ class FALCOR_API LowLevelContextData */ NativeHandle getCommandBufferNativeHandle() const; - // TODO(@skallweit): This should be removed. - CommandQueueHandle getCommandQueue() const { return mpGfxCommandQueue; } - const ref& getFence() const { return mpFence; } + const ref& getFence() const { return mpFence; } + +#if FALCOR_HAS_CUDA + const ref& getCudaFence() const { return mpCudaFence; } + const ref& getCudaSemaphore() const { return mpCudaSemaphore; } +#endif void closeCommandBuffer(); void openCommandBuffer(); - void flush(); + void submitCommandBuffer(); gfx::IResourceCommandEncoder* getResourceCommandEncoder(); gfx::IComputeCommandEncoder* getComputeCommandEncoder(); @@ -90,7 +95,12 @@ class FALCOR_API LowLevelContextData Device* mpDevice; gfx::ICommandQueue* mpGfxCommandQueue; Slang::ComPtr mGfxCommandBuffer; - ref mpFence; + ref mpFence; + +#if FALCOR_HAS_CUDA + ref mpCudaFence; + ref mpCudaSemaphore; +#endif gfx::ICommandBuffer* mpCommandBuffer = nullptr; bool mIsCommandBufferOpen = false; diff --git a/Source/Falcor/Core/API/NativeHandle.h b/Source/Falcor/Core/API/NativeHandle.h index f081cb20b..b964dd88d 100644 --- a/Source/Falcor/Core/API/NativeHandle.h +++ b/Source/Falcor/Core/API/NativeHandle.h @@ -27,7 +27,7 @@ **************************************************************************/ #pragma once -#include "Core/Assert.h" +#include "Core/Error.h" #include diff --git a/Source/Falcor/Core/API/NvApiExDesc.h b/Source/Falcor/Core/API/NvApiExDesc.h index 280f6e6d4..18bc60dac 100644 --- a/Source/Falcor/Core/API/NvApiExDesc.h +++ b/Source/Falcor/Core/API/NvApiExDesc.h @@ -27,7 +27,7 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/Program/ProgramVersion.h" #if FALCOR_HAS_NVAPI #include diff --git a/Source/Falcor/Core/API/ParameterBlock.cpp b/Source/Falcor/Core/API/ParameterBlock.cpp index 59527e675..fa77b3de0 100644 --- a/Source/Falcor/Core/API/ParameterBlock.cpp +++ b/Source/Falcor/Core/API/ParameterBlock.cpp @@ -29,8 +29,7 @@ #include "Device.h" #include "CopyContext.h" #include "GFXAPI.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/Program/ProgramVersion.h" #include "Utils/Logger.h" @@ -56,10 +55,11 @@ gfx::ShaderOffset getGFXShaderOffset(const ParameterBlock::BindLocation& bindLoc return gfxOffset; } -bool isSrvType(const ref& pType) +bool isSrvType(const ReflectionType* pType) { + FALCOR_ASSERT(pType); auto resourceType = pType->unwrapArray()->asResourceType(); - if (resourceType->getType() == ReflectionResourceType::Type::Sampler || + if (!resourceType || resourceType->getType() == ReflectionResourceType::Type::Sampler || resourceType->getType() == ReflectionResourceType::Type::ConstantBuffer) return false; @@ -75,10 +75,11 @@ bool isSrvType(const ref& pType) } } -bool isUavType(const ref& pType) +bool isUavType(const ReflectionType* pType) { + FALCOR_ASSERT(pType); auto resourceType = pType->unwrapArray()->asResourceType(); - if (resourceType->getType() == ReflectionResourceType::Type::Sampler || + if (!resourceType || resourceType->getType() == ReflectionResourceType::Type::Sampler || resourceType->getType() == ReflectionResourceType::Type::ConstantBuffer) return false; @@ -94,16 +95,36 @@ bool isUavType(const ref& pType) } } -bool isCbvType(const ref& pType) +bool isSamplerType(const ReflectionType* pType) { + FALCOR_ASSERT(pType); auto resourceType = pType->unwrapArray()->asResourceType(); - if (resourceType->getType() == ReflectionResourceType::Type::ConstantBuffer) - { - FALCOR_ASSERT(resourceType->getShaderAccess() == ReflectionResourceType::ShaderAccess::Read); - return true; - } - return false; + return (resourceType && resourceType->getType() == ReflectionResourceType::Type::Sampler); +} + +bool isAccelerationStructureType(const ReflectionType* pType) +{ + FALCOR_ASSERT(pType); + auto resourceType = pType->unwrapArray()->asResourceType(); + return (resourceType && resourceType->getType() == ReflectionResourceType::Type::AccelerationStructure); +} + +bool isParameterBlockType(const ReflectionType* pType) +{ + FALCOR_ASSERT(pType); + auto resourceType = pType->unwrapArray()->asResourceType(); + // Parameter blocks are currently classified as constant buffers. + // See getResourceType() in ProgramReflection.cpp + return (resourceType && resourceType->getType() == ReflectionResourceType::Type::ConstantBuffer); +} + +bool isConstantBufferType(const ReflectionType* pType) +{ + FALCOR_ASSERT(pType); + auto resourceType = pType->unwrapArray()->asResourceType(); + return (resourceType && resourceType->getType() == ReflectionResourceType::Type::ConstantBuffer); } + } // namespace ref ParameterBlock::create( @@ -112,8 +133,7 @@ ref ParameterBlock::create( const ref& pElementType ) { - if (!pElementType) - throw ArgumentError("Can't create a parameter block without type information"); + FALCOR_CHECK(pElementType, "Can't create a parameter block without type information"); auto pReflection = ParameterBlockReflection::create(pProgramVersion.get(), pElementType); return create(pDevice, pReflection); } @@ -142,7 +162,7 @@ ShaderVar ParameterBlock::getRootVar() const return ShaderVar(const_cast(this)); } -ShaderVar ParameterBlock::findMember(const std::string& varName) const +ShaderVar ParameterBlock::findMember(std::string_view varName) const { return getRootVar().findMember(varName); } @@ -157,7 +177,7 @@ size_t ParameterBlock::getElementSize() const return mpReflector->getElementType()->getByteSize(); } -UniformShaderVarOffset ParameterBlock::getVariableOffset(const std::string& varName) const +TypedShaderVarOffset ParameterBlock::getVariableOffset(std::string_view varName) const { return getElementType()->getZeroOffset()[varName]; } @@ -215,7 +235,7 @@ void ParameterBlock::prepareResource(CopyContext* pContext, Resource* pResource, } bool insertBarrier = true; - insertBarrier = (is_set(pResource->getBindFlags(), Resource::BindFlags::AccelerationStructure) == false); + insertBarrier = (is_set(pResource->getBindFlags(), ResourceBindFlags::AccelerationStructure) == false); if (insertBarrier) { insertBarrier = !pContext->resourceBarrier(pResource, isUav ? Resource::State::UnorderedAccess : Resource::State::ShaderResource); @@ -227,38 +247,6 @@ void ParameterBlock::prepareResource(CopyContext* pContext, Resource* pResource, pContext->uavBarrier(pResource); } -// Template specialization to allow setting booleans on a parameter block. -// On the host side a bool is 1B and the device 4B. We cast bools to 32-bit integers here. -// Note that this applies to our boolN vectors as well, which are currently 1B per element. - -template<> -FALCOR_API bool ParameterBlock::setVariable(UniformShaderVarOffset offset, const bool& value) -{ - int32_t v = value ? 1 : 0; - return setVariable(offset, v); -} - -template<> -FALCOR_API bool ParameterBlock::setVariable(UniformShaderVarOffset offset, const bool2& value) -{ - int2 v = {value.x ? 1 : 0, value.y ? 1 : 0}; - return setVariable(offset, v); -} - -template<> -FALCOR_API bool ParameterBlock::setVariable(UniformShaderVarOffset offset, const bool3& value) -{ - int3 v = {value.x ? 1 : 0, value.y ? 1 : 0, value.z ? 1 : 0}; - return setVariable(offset, v); -} - -template<> -FALCOR_API bool ParameterBlock::setVariable(UniformShaderVarOffset offset, const bool4& value) -{ - int4 v = {value.x ? 1 : 0, value.y ? 1 : 0, value.z ? 1 : 0, value.w ? 1 : 0}; - return setVariable(offset, v); -} - ParameterBlock::~ParameterBlock() {} ParameterBlock::ParameterBlock(ref pDevice, const ref& pReflector) @@ -317,54 +305,170 @@ void ParameterBlock::initializeResourceBindings() } } -bool ParameterBlock::setBlob(const void* pSrc, UniformShaderVarOffset offset, size_t size) +void ParameterBlock::setBlob(const void* pSrc, const BindLocation& bindLocation, size_t size) { - gfx::ShaderOffset gfxOffset = getGFXShaderOffset(offset); - return SLANG_SUCCEEDED(mpShaderObject->setData(gfxOffset, pSrc, size)); + if (!isConstantBufferType(bindLocation.getType())) + { + gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); + FALCOR_GFX_CALL(mpShaderObject->setData(gfxOffset, pSrc, size)); + } + else + { + FALCOR_THROW("Error trying to set a blob to a non constant buffer variable."); + } } -bool ParameterBlock::setBlob(const void* pSrc, size_t offset, size_t size) +void ParameterBlock::setBlob(const void* pSrc, size_t offset, size_t size) { gfx::ShaderOffset gfxOffset = {}; gfxOffset.uniformOffset = offset; - return SLANG_SUCCEEDED(mpShaderObject->setData(gfxOffset, pSrc, size)); + FALCOR_GFX_CALL(mpShaderObject->setData(gfxOffset, pSrc, size)); } -bool ParameterBlock::setBuffer(const std::string& name, const ref& pBuffer) +// +// Uniforms +// + +template +void setVariableInternal( + ParameterBlock* pBlock, + const ParameterBlock::BindLocation& bindLocation, + const T& value, + ReflectionBasicType::Type type, + ReflectionBasicType::Type implicitType = ReflectionBasicType::Type::Unknown +) { - auto var = getRootVar()[name]; - return var.setBuffer(pBuffer); + const ReflectionBasicType* basicType = bindLocation.getType()->unwrapArray()->asBasicType(); + if (!basicType) + FALCOR_THROW("Error trying to set a variable that is not a basic type."); + ReflectionBasicType::Type expectedType = basicType->getType(); + // Check types. Allow implicit conversions from signed to unsigned types. + if (type != expectedType && implicitType != expectedType) + FALCOR_THROW( + "Error trying to set a variable with a different type than the one in the program (expected {}, got {}).", + enumToString(expectedType), + enumToString(type) + ); + size_t size = sizeof(T); + size_t expectedSize = basicType->getByteSize(); + if (size != expectedSize) + FALCOR_THROW( + "Error trying to set a variable with a different size than the one in the program (expected {} bytes, got {}).", + expectedSize, + size + ); + auto gfxOffset = getGFXShaderOffset(bindLocation); + FALCOR_GFX_CALL(pBlock->getShaderObject()->setData(gfxOffset, &value, size)); } -bool ParameterBlock::setBuffer(const BindLocation& bindLoc, const ref& pResource) +template +void ParameterBlock::setVariable(const BindLocation& bindLocation, const T& value) +{ + FALCOR_UNIMPLEMENTED(); +} + +#define DEFINE_SET_VARIABLE(ctype, basicType, implicitType) \ + template<> \ + FALCOR_API void ParameterBlock::setVariable(const BindLocation& bindLocation, const ctype& value) \ + { \ + setVariableInternal(this, bindLocation, value, basicType, implicitType); \ + } + +DEFINE_SET_VARIABLE(uint32_t, ReflectionBasicType::Type::Uint, ReflectionBasicType::Type::Int); +DEFINE_SET_VARIABLE(uint2, ReflectionBasicType::Type::Uint2, ReflectionBasicType::Type::Int2); +DEFINE_SET_VARIABLE(uint3, ReflectionBasicType::Type::Uint3, ReflectionBasicType::Type::Int3); +DEFINE_SET_VARIABLE(uint4, ReflectionBasicType::Type::Uint4, ReflectionBasicType::Type::Int4); + +DEFINE_SET_VARIABLE(int32_t, ReflectionBasicType::Type::Int, ReflectionBasicType::Type::Uint); +DEFINE_SET_VARIABLE(int2, ReflectionBasicType::Type::Int2, ReflectionBasicType::Type::Uint2); +DEFINE_SET_VARIABLE(int3, ReflectionBasicType::Type::Int3, ReflectionBasicType::Type::Uint3); +DEFINE_SET_VARIABLE(int4, ReflectionBasicType::Type::Int4, ReflectionBasicType::Type::Uint4); + +DEFINE_SET_VARIABLE(float, ReflectionBasicType::Type::Float, ReflectionBasicType::Type::Unknown); +DEFINE_SET_VARIABLE(float2, ReflectionBasicType::Type::Float2, ReflectionBasicType::Type::Unknown); +DEFINE_SET_VARIABLE(float3, ReflectionBasicType::Type::Float3, ReflectionBasicType::Type::Unknown); +DEFINE_SET_VARIABLE(float4, ReflectionBasicType::Type::Float4, ReflectionBasicType::Type::Unknown); + +// DEFINE_SET_VARIABLE(float1x4, ReflectionBasicType::Type::Float1x4, ReflectionBasicType::Type::Unknown); +DEFINE_SET_VARIABLE(float2x4, ReflectionBasicType::Type::Float2x4, ReflectionBasicType::Type::Unknown); +DEFINE_SET_VARIABLE(float3x4, ReflectionBasicType::Type::Float3x4, ReflectionBasicType::Type::Unknown); +DEFINE_SET_VARIABLE(float4x4, ReflectionBasicType::Type::Float4x4, ReflectionBasicType::Type::Unknown); + +DEFINE_SET_VARIABLE(uint64_t, ReflectionBasicType::Type::Uint64, ReflectionBasicType::Type::Int64); + +#undef DEFINE_SET_VARIABLE + +// Template specialization to allow setting booleans on a parameter block. +// On the host side a bool is 1B and the device 4B. We cast bools to 32-bit integers here. +// Note that this applies to our boolN vectors as well, which are currently 1B per element. + +template<> +FALCOR_API void ParameterBlock::setVariable(const BindLocation& bindLocation, const bool& value) +{ + uint v = value ? 1 : 0; + setVariableInternal(this, bindLocation, v, ReflectionBasicType::Type::Bool); +} + +template<> +FALCOR_API void ParameterBlock::setVariable(const BindLocation& bindLocation, const bool2& value) +{ + uint2 v = {value.x ? 1 : 0, value.y ? 1 : 0}; + setVariableInternal(this, bindLocation, v, ReflectionBasicType::Type::Bool2); +} + +template<> +FALCOR_API void ParameterBlock::setVariable(const BindLocation& bindLocation, const bool3& value) +{ + uint3 v = {value.x ? 1 : 0, value.y ? 1 : 0, value.z ? 1 : 0}; + setVariableInternal(this, bindLocation, v, ReflectionBasicType::Type::Bool3); +} + +template<> +FALCOR_API void ParameterBlock::setVariable(const BindLocation& bindLocation, const bool4& value) +{ + uint4 v = {value.x ? 1 : 0, value.y ? 1 : 0, value.z ? 1 : 0, value.w ? 1 : 0}; + setVariableInternal(this, bindLocation, v, ReflectionBasicType::Type::Bool4); +} + +// +// Buffer +// + +void ParameterBlock::setBuffer(std::string_view name, const ref& pBuffer) +{ + getRootVar()[name].setBuffer(pBuffer); +} + +void ParameterBlock::setBuffer(const BindLocation& bindLoc, const ref& pBuffer) { gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLoc); if (isUavType(bindLoc.getType())) { - auto pUAV = pResource ? pResource->getUAV() : nullptr; + if (pBuffer && !is_set(pBuffer->getBindFlags(), ResourceBindFlags::UnorderedAccess)) + FALCOR_THROW("Trying to bind buffer '{}' created without UnorderedAccess flag as a UAV.", pBuffer->getName()); + auto pUAV = pBuffer ? pBuffer->getUAV() : nullptr; mpShaderObject->setResource(gfxOffset, pUAV ? pUAV->getGfxResourceView() : nullptr); mUAVs[gfxOffset] = pUAV; - mResources[gfxOffset] = pResource; + mResources[gfxOffset] = pBuffer; } else if (isSrvType(bindLoc.getType())) { - auto pSRV = pResource ? pResource->getSRV() : nullptr; + if (pBuffer && !is_set(pBuffer->getBindFlags(), ResourceBindFlags::ShaderResource)) + FALCOR_THROW("Trying to bind buffer '{}' created without ShaderResource flag as an SRV.", pBuffer->getName()); + auto pSRV = pBuffer ? pBuffer->getSRV() : nullptr; mpShaderObject->setResource(gfxOffset, pSRV ? pSRV->getGfxResourceView() : nullptr); mSRVs[gfxOffset] = pSRV; - mResources[gfxOffset] = pResource; + mResources[gfxOffset] = pBuffer; } else { - logError("Error trying to bind resource to non SRV/UAV variable. Ignoring call."); - return false; + FALCOR_THROW("Error trying to bind buffer to a non SRV/UAV variable."); } - return true; } -ref ParameterBlock::getBuffer(const std::string& name) const +ref ParameterBlock::getBuffer(std::string_view name) const { - auto var = getRootVar()[name]; - return var.getBuffer(); + return getRootVar()[name].getBuffer(); } ref ParameterBlock::getBuffer(const BindLocation& bindLoc) const @@ -388,83 +492,26 @@ ref ParameterBlock::getBuffer(const BindLocation& bindLoc) const } else { - logError("Error trying to bind resource to non SRV/UAV variable. Ignoring call."); - return nullptr; + FALCOR_THROW("Error trying to get buffer from a non SRV/UAV variable."); } } -bool ParameterBlock::setParameterBlock(const std::string& name, const ref& pBlock) -{ - auto var = getRootVar()[name]; - return var.setParameterBlock(pBlock); -} +// +// Texture +// -bool ParameterBlock::setParameterBlock(const BindLocation& bindLocation, const ref& pBlock) +void ParameterBlock::setTexture(std::string_view name, const ref& pTexture) { - auto gfxOffset = getGFXShaderOffset(bindLocation); - mParameterBlocks[gfxOffset] = pBlock; - return SLANG_SUCCEEDED(mpShaderObject->setObject(gfxOffset, pBlock ? pBlock->mpShaderObject : nullptr)); -} - -ref ParameterBlock::getParameterBlock(const std::string& name) const -{ - auto var = getRootVar()[name]; - return var.getParameterBlock(); + getRootVar()[name].setTexture(pTexture); } -ref ParameterBlock::getParameterBlock(const BindLocation& bindLocation) const -{ - auto gfxOffset = getGFXShaderOffset(bindLocation); - auto iter = mParameterBlocks.find(gfxOffset); - if (iter == mParameterBlocks.end()) - return nullptr; - return iter->second; -} - -template -bool ParameterBlock::setVariable(UniformShaderVarOffset offset, const VarType& value) -{ - auto gfxOffset = getGFXShaderOffset(offset); - return SLANG_SUCCEEDED(mpShaderObject->setData(gfxOffset, &value, sizeof(VarType))); -} - -#define set_constant_by_offset(_t) template FALCOR_API bool ParameterBlock::setVariable(UniformShaderVarOffset offset, const _t& value) -set_constant_by_offset(uint32_t); -set_constant_by_offset(uint2); -set_constant_by_offset(uint3); -set_constant_by_offset(uint4); - -set_constant_by_offset(int32_t); -set_constant_by_offset(int2); -set_constant_by_offset(int3); -set_constant_by_offset(int4); - -set_constant_by_offset(float); -set_constant_by_offset(float2); -set_constant_by_offset(float3); -set_constant_by_offset(float4); - -set_constant_by_offset(float1x4); -set_constant_by_offset(float2x4); -set_constant_by_offset(float3x4); -set_constant_by_offset(float4x4); - -set_constant_by_offset(uint64_t); - -#undef set_constant_by_offset - -bool ParameterBlock::setTexture(const std::string& name, const ref& pTexture) +void ParameterBlock::setTexture(const BindLocation& bindLocation, const ref& pTexture) { - auto var = getRootVar()[name]; - return var.setTexture(pTexture); -} - -bool ParameterBlock::setTexture(const BindLocation& bindLocation, const ref& pTexture) -{ - const auto& bindingInfo = mpReflector->getResourceRangeBindingInfo(bindLocation.getResourceRangeIndex()); gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); if (isUavType(bindLocation.getType())) { + if (pTexture && !is_set(pTexture->getBindFlags(), ResourceBindFlags::UnorderedAccess)) + FALCOR_THROW("Trying to bind texture '{}' created without UnorderedAccess flag as a UAV.", pTexture->getName()); auto pUAV = pTexture ? pTexture->getUAV() : nullptr; mpShaderObject->setResource(gfxOffset, pUAV ? pUAV->getGfxResourceView() : nullptr); mUAVs[gfxOffset] = pUAV; @@ -472,6 +519,8 @@ bool ParameterBlock::setTexture(const BindLocation& bindLocation, const refgetBindFlags(), ResourceBindFlags::ShaderResource)) + FALCOR_THROW("Trying to bind texture '{}' created without ShaderResource flag as an SRV.", pTexture->getName()); auto pSRV = pTexture ? pTexture->getSRV() : nullptr; mpShaderObject->setResource(gfxOffset, pSRV ? pSRV->getGfxResourceView() : nullptr); mSRVs[gfxOffset] = pSRV; @@ -479,16 +528,13 @@ bool ParameterBlock::setTexture(const BindLocation& bindLocation, const ref ParameterBlock::getTexture(const std::string& name) const +ref ParameterBlock::getTexture(std::string_view name) const { - auto var = getRootVar()[name]; - return var.getTexture(); + return getRootVar()[name].getTexture(); } ref ParameterBlock::getTexture(const BindLocation& bindLocation) const @@ -512,16 +558,19 @@ ref ParameterBlock::getTexture(const BindLocation& bindLocation) const } else { - logError("Error trying to bind resource to non SRV/UAV variable. Ignoring call."); - return nullptr; + FALCOR_THROW("Error trying to get texture from a non SRV/UAV variable."); } } -bool ParameterBlock::setSrv(const BindLocation& bindLocation, const ref& pSrv) +// +// ResourceView +// + +void ParameterBlock::setSrv(const BindLocation& bindLocation, const ref& pSrv) { - gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); if (isSrvType(bindLocation.getType())) { + gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); mpShaderObject->setResource(gfxOffset, pSrv ? pSrv->getGfxResourceView() : nullptr); mSRVs[gfxOffset] = pSrv; // Note: The resource view does not hold a strong reference to the resource, so we need to keep it alive here. @@ -529,17 +578,31 @@ bool ParameterBlock::setSrv(const BindLocation& bindLocation, const ref& pUav) +ref ParameterBlock::getSrv(const BindLocation& bindLocation) const +{ + if (isSrvType(bindLocation.getType())) + { + gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); + auto iter = mSRVs.find(gfxOffset); + if (iter == mSRVs.end()) + return nullptr; + return iter->second; + } + else + { + FALCOR_THROW("Error trying to get an SRV from a non SRV variable."); + } +} + +void ParameterBlock::setUav(const BindLocation& bindLocation, const ref& pUav) { - gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); if (isUavType(bindLocation.getType())) { + gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); mpShaderObject->setResource(gfxOffset, pUav ? pUav->getGfxResourceView() : nullptr); mUAVs[gfxOffset] = pUav; // Note: The resource view does not hold a strong reference to the resource, so we need to keep it alive here. @@ -547,73 +610,143 @@ bool ParameterBlock::setUav(const BindLocation& bindLocation, const ref& pAccl) +ref ParameterBlock::getUav(const BindLocation& bindLocation) const { - gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); - mAccelerationStructures[gfxOffset] = pAccl; - return SLANG_SUCCEEDED(mpShaderObject->setResource(gfxOffset, pAccl ? pAccl->getGfxAccelerationStructure() : nullptr)); + if (isUavType(bindLocation.getType())) + { + gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); + auto iter = mUAVs.find(gfxOffset); + if (iter == mUAVs.end()) + return nullptr; + return iter->second; + } + else + { + FALCOR_THROW("Error trying to get a UAV from a non UAV variable."); + } } -ref ParameterBlock::getSrv(const BindLocation& bindLocation) const +void ParameterBlock::setAccelerationStructure(const BindLocation& bindLocation, const ref& pAccl) { - gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); - auto iter = mSRVs.find(gfxOffset); - if (iter == mSRVs.end()) - return nullptr; - return iter->second; + if (isAccelerationStructureType(bindLocation.getType())) + { + gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); + mAccelerationStructures[gfxOffset] = pAccl; + FALCOR_GFX_CALL(mpShaderObject->setResource(gfxOffset, pAccl ? pAccl->getGfxAccelerationStructure() : nullptr)); + } + else + { + FALCOR_THROW("Error trying to bind an acceleration structure to a non acceleration structure variable."); + } } -ref ParameterBlock::getUav(const BindLocation& bindLocation) const +ref ParameterBlock::getAccelerationStructure(const BindLocation& bindLocation) const { - gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); - auto iter = mUAVs.find(gfxOffset); - if (iter == mUAVs.end()) - return nullptr; - return iter->second; + if (isAccelerationStructureType(bindLocation.getType())) + { + gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); + auto iter = mAccelerationStructures.find(gfxOffset); + if (iter == mAccelerationStructures.end()) + return nullptr; + return iter->second; + } + else + { + FALCOR_THROW("Error trying to get an acceleration structure from a non acceleration structure variable."); + } } -ref ParameterBlock::getAccelerationStructure(const BindLocation& bindLocation) const +// +// Sampler +// + +void ParameterBlock::setSampler(std::string_view name, const ref& pSampler) { - gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); - auto iter = mAccelerationStructures.find(gfxOffset); - if (iter == mAccelerationStructures.end()) - return nullptr; - return iter->second; + getRootVar()[name].setSampler(pSampler); } -bool ParameterBlock::setSampler(const std::string& name, const ref& pSampler) +void ParameterBlock::setSampler(const BindLocation& bindLocation, const ref& pSampler) { - auto var = getRootVar()[name]; - return var.setSampler(pSampler); + if (isSamplerType(bindLocation.getType())) + { + gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); + const ref& pBoundSampler = pSampler ? pSampler : mpDevice->getDefaultSampler(); + mSamplers[gfxOffset] = pBoundSampler; + FALCOR_GFX_CALL(mpShaderObject->setSampler(gfxOffset, pBoundSampler->getGfxSamplerState())); + } + else + { + FALCOR_THROW("Error trying to bind a sampler to a non sampler variable."); + } } -bool ParameterBlock::setSampler(const BindLocation& bindLocation, const ref& pSampler) +ref ParameterBlock::getSampler(std::string_view name) const { - gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); - const ref& pBoundSampler = pSampler ? pSampler : mpDevice->getDefaultSampler(); - mSamplers[gfxOffset] = pBoundSampler; - return SLANG_SUCCEEDED(mpShaderObject->setSampler(gfxOffset, pBoundSampler->getGfxSamplerState())); + return getRootVar()[name].getSampler(); } ref ParameterBlock::getSampler(const BindLocation& bindLocation) const { - gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); - auto iter = mSamplers.find(gfxOffset); - if (iter == mSamplers.end()) - return nullptr; - return iter->second; + if (isSamplerType(bindLocation.getType())) + { + gfx::ShaderOffset gfxOffset = getGFXShaderOffset(bindLocation); + auto iter = mSamplers.find(gfxOffset); + if (iter == mSamplers.end()) + return nullptr; + return iter->second; + } + else + { + FALCOR_THROW("Error trying to get a sampler from a non sampler variable."); + } +} + +// +// ParameterBlock +// + +void ParameterBlock::setParameterBlock(std::string_view name, const ref& pBlock) +{ + getRootVar()[name].setParameterBlock(pBlock); +} + +void ParameterBlock::setParameterBlock(const BindLocation& bindLocation, const ref& pBlock) +{ + if (isParameterBlockType(bindLocation.getType())) + { + auto gfxOffset = getGFXShaderOffset(bindLocation); + mParameterBlocks[gfxOffset] = pBlock; + FALCOR_GFX_CALL(mpShaderObject->setObject(gfxOffset, pBlock ? pBlock->mpShaderObject : nullptr)); + } + else + { + FALCOR_THROW("Error trying to bind a parameter block to a non parameter block variable."); + } } -ref ParameterBlock::getSampler(const std::string& name) const +ref ParameterBlock::getParameterBlock(std::string_view name) const { - auto var = getRootVar()[name]; - return var.getSampler(); + return getRootVar()[name].getParameterBlock(); +} + +ref ParameterBlock::getParameterBlock(const BindLocation& bindLocation) const +{ + if (isParameterBlockType(bindLocation.getType())) + { + auto gfxOffset = getGFXShaderOffset(bindLocation); + auto iter = mParameterBlocks.find(gfxOffset); + if (iter == mParameterBlocks.end()) + return nullptr; + return iter->second; + } + else + { + FALCOR_THROW("Error trying to get a parameter block from a non parameter block variable."); + } } size_t ParameterBlock::getSize() const @@ -644,21 +777,6 @@ bool ParameterBlock::prepareDescriptorSets(CopyContext* pCopyContext) return true; } -const ref& ParameterBlock::getParameterBlock(uint32_t resourceRangeIndex, uint32_t arrayIndex) const -{ - static const ref pNull; - - gfx::ShaderOffset gfxOffset = {}; - gfxOffset.bindingRangeIndex = resourceRangeIndex; - gfxOffset.bindingArrayIndex = arrayIndex; - auto iter = mParameterBlocks.find(gfxOffset); - if (iter == mParameterBlocks.end()) - { - return pNull; - } - return iter->second; -} - void ParameterBlock::collectSpecializationArgs(SpecializationArgs& ioArgs) const {} void const* ParameterBlock::getRawData() const @@ -666,9 +784,4 @@ void const* ParameterBlock::getRawData() const return mpShaderObject->getRawData(); } -ref ParameterBlock::getUnderlyingConstantBuffer() const -{ - throw "unimplemented"; -} - } // namespace Falcor diff --git a/Source/Falcor/Core/API/ParameterBlock.h b/Source/Falcor/Core/API/ParameterBlock.h index ea1cd3131..d2538be97 100644 --- a/Source/Falcor/Core/API/ParameterBlock.h +++ b/Source/Falcor/Core/API/ParameterBlock.h @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -85,6 +86,10 @@ class FALCOR_API ParameterBlock : public Object gfx::IShaderObject* getShaderObject() const { return mpShaderObject.get(); } + // + // Uniforms + // + /** * Set a variable into the block. * The function will validate that the value Type matches the declaration in the shader. If there's a mismatch, an error will be logged @@ -93,9 +98,9 @@ class FALCOR_API ParameterBlock : public Object * @param[in] value Value to set */ template - bool setVariable(const std::string& name, const T& value) + void setVariable(std::string_view name, const T& value) { - return getRootVar()[name].set(value); + getRootVar()[name].set(value); } /** @@ -106,173 +111,214 @@ class FALCOR_API ParameterBlock : public Object * @param[in] value Value to set */ template - bool setVariable(UniformShaderVarOffset offset, const T& value); + void setVariable(const BindLocation& bindLocation, const T& value); template - bool setBlob(UniformShaderVarOffset bindLocation, const T& blob) const + void setBlob(const BindLocation& bindLocation, const T& blob) const { - return setBlob(bindLocation, &blob, sizeof(blob)); + setBlob(bindLocation, &blob, sizeof(blob)); } - bool setBlob(UniformShaderVarOffset offset, const void* pSrc, size_t size) { return setBlob(pSrc, offset, size); } + void setBlob(const BindLocation& bindLocation, const void* pSrc, size_t size) { return setBlob(pSrc, bindLocation, size); } + + void setBlob(const void* pSrc, const BindLocation& bindLocation, size_t size); + void setBlob(const void* pSrc, size_t offset, size_t size); - bool setBlob(const void* pSrc, UniformShaderVarOffset offset, size_t size); - bool setBlob(const void* pSrc, size_t offset, size_t size); + // + // Buffer + // /** - * Bind a buffer by name. - * If the name doesn't exists, the bind flags don't match the shader requirements or the size doesn't match the required size, the call - * will fail. - * @param[in] name The name of the buffer in the program - * @param[in] pBuffer The buffer object - * @return false is the call failed, otherwise true + * Bind a buffer to a variable by name. + * Throws an exception if the variable doesn't exist, there is a type-mismatch or the bind flags don't match the shader requirements. + * @param[in] name The name of the variable to bind to. + * @param[in] pBuffer The buffer object. */ - bool setBuffer(const std::string& name, const ref& pBuffer); + void setBuffer(std::string_view name, const ref& pBuffer); /** - * Bind a buffer object by index - * If the no buffer exists in the specified index or the bind flags don't match the shader requirements or the size doesn't match the - * required size, the call will fail. - * @param[in] bindLocation The bind-location in the block - * @param[in] pBuffer The buffer object - * @return false is the call failed, otherwise true + * Bind a buffer to a variable by bind location. + * Throws an exception if the variable doesn't exist, there is a type-mismatch or the bind flags don't match the shader requirements. + * @param[in] bindLocation The bind location of the variable to bind to. + * @param[in] pBuffer The buffer object. */ - bool setBuffer(const BindLocation& bindLocation, const ref& pBuffer); + void setBuffer(const BindLocation& bindLocation, const ref& pBuffer); /** - * Get a buffer - * @param[in] name The name of the buffer - * @return If the name is valid, a shared pointer to the buffer. Otherwise returns nullptr + * Get the buffer bound to a variable by name. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] name The name of the variable. + * @return The bound buffer or nullptr if none is bound. */ - ref getBuffer(const std::string& name) const; + ref getBuffer(std::string_view name) const; /** - * Get a buffer - * @param[in] bindLocation The bind location of the buffer - * @return If the name is valid, a shared pointer to the buffer. Otherwise returns nullptr + * Get the buffer bound to a variable by bind location. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] bindLocation The bind location of the variable. + * @return The bound buffer or nullptr if none is bound. */ ref getBuffer(const BindLocation& bindLocation) const; + // + // Texture + // + /** - * Bind a parameter block by name. - * If the name doesn't exists or the size doesn't match the required size, the call will fail. - * @param[in] name The name of the parameter block in the program - * @param[in] pBlock The parameter block - * @return false is the call failed, otherwise true + * Bind a texture to a variable by name. + * Throws an exception if the variable doesn't exist, there is a type-mismatch or the bind flags don't match the shader requirements. + * @param[in] name The name of the variable to bind to. + * @param[in] pTexture The texture object. */ - bool setParameterBlock(const std::string& name, const ref& pBlock); + void setTexture(std::string_view name, const ref& pTexture); /** - * Bind a parameter block by index. - * If the no parameter block exists in the specified index or the parameter block size doesn't match the required size, the call will - * fail. - * @param[in] bindLocation The location of the object - * @param[in] pBlock The parameter block - * @return false is the call failed, otherwise true + * Bind a texture to a variable by bind location. + * Throws an exception if the variable doesn't exist, there is a type-mismatch or the bind flags don't match the shader requirements. + * @param[in] bindLocation The bind location of the variable to bind to. + * @param[in] pTexture The texture object. */ - bool setParameterBlock(const BindLocation& bindLocation, const ref& pBlock); + void setTexture(const BindLocation& bindLocation, const ref& pTexture); /** - * Get a parameter block. - * @param[in] name The name of the parameter block - * @return If the name is valid, a shared pointer to the parameter block. Otherwise returns nullptr + * Get the texture bound to a variable by name. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] name The name of the variable. + * @return The bound texture or nullptr if none is bound. */ - ref getParameterBlock(const std::string& name) const; + ref getTexture(std::string_view name) const; /** - * Get a parameter block. - * @param[in] bindLocation The location of the block - * @return If the indices is valid, a shared pointer to the parameter block. Otherwise returns nullptr + * Get the texture bound to a variable by bind location. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] bindLocation The bind location of the variable. + * @return The bound texture or nullptr if none is bound. */ - ref getParameterBlock(const BindLocation& bindLocation) const; + ref getTexture(const BindLocation& bindLocation) const; + + // + // ResourceView + // /** - * Bind a texture. Based on the shader reflection, it will be bound as either an SRV or a UAV - * @param[in] name The name of the texture object in the shader - * @param[in] pTexture The texture object to bind + * Bind an SRV to a variable by name. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] name The name of the variable to bind to. + * @param[in] pSrv The shader-resource-view object. */ - bool setTexture(const std::string& name, const ref& pTexture); - bool setTexture(const BindLocation& bindLocation, const ref& pTexture); + void setSrv(const BindLocation& bindLocation, const ref& pSrv); /** - * Get a texture object. - * @param[in] name The name of the texture - * @return If the name is valid, a shared pointer to the texture object. Otherwise returns nullptr + * Get the SRV bound to a variable by bind location. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] bindLocation The bind location of the variable. + * @return The bound SRV or nullptr if none is bound. */ - ref getTexture(const std::string& name) const; - ref getTexture(const BindLocation& bindLocation) const; + ref getSrv(const BindLocation& bindLocation) const; /** - * Bind an SRV. - * @param[in] bindLocation The bind-location in the block - * @param[in] pSrv The shader-resource-view object to bind + * Bind a UAV to a variable by name. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] name The name of the variable to bind to. + * @param[in] pSrv The unordered-access-view object. */ - bool setSrv(const BindLocation& bindLocation, const ref& pSrv); + void setUav(const BindLocation& bindLocation, const ref& pUav); /** - * Bind a UAV. - * @param[in] bindLocation The bind-location in the block - * @param[in] pSrv The unordered-access-view object to bind + * Get the UAV bound to a variable by bind location. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] bindLocation The bind location of the variable. + * @return The bound UAV or nullptr if none is bound. */ - bool setUav(const BindLocation& bindLocation, const ref& pUav); + ref getUav(const BindLocation& bindLocation) const; /** - * Bind an acceleration structure. - * @param[in] bindLocation The bind-location in the block - * @param[in] pAccl The acceleration structure object to bind - * @return false if the binding location does not accept an acceleration structure, true otherwise. + * Bind an acceleration strcture to a variable by name. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] name The name of the variable to bind to. + * @param[in] pAccel The acceleration structure object. */ - bool setAccelerationStructure(const BindLocation& bindLocation, const ref& pAccl); + void setAccelerationStructure(const BindLocation& bindLocation, const ref& pAccl); /** - * Get an SRV object. - * @param[in] bindLocation The bind-location in the block - * @return If the bind-location is valid, a shared pointer to the SRV. Otherwise returns nullptr + * Get the acceleration structure bound to a variable by bind location. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] bindLocation The bind location of the variable. + * @return The bound acceleration structure or nullptr if none is bound. */ - ref getSrv(const BindLocation& bindLocation) const; + ref getAccelerationStructure(const BindLocation& bindLocation) const; + + // + // Sampler + // /** - * Get a UAV object - * @param[in] bindLocation The bind-location in the block - * @return If the bind-location is valid, a shared pointer to the UAV. Otherwise returns nullptr + * Bind a sampler to a variable by name. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] name The name of the variable to bind to. + * @param[in] pSampler The sampler object. */ - ref getUav(const BindLocation& bindLocation) const; + void setSampler(std::string_view name, const ref& pSampler); /** - * Get an acceleration structure object. - * @param[in] bindLocation The bind-location in the block - * @return If the bind-location is valid, a shared pointer to the acceleration structure. Otherwise returns nullptr + * Bind a sampler to a variable by bind location. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] bindLocation The bind location of the variable to bind to. + * @param[in] pSampler The sampler object. */ - ref getAccelerationStructure(const BindLocation& bindLocation) const; + void setSampler(const BindLocation& bindLocation, const ref& pSampler); /** - * Bind a sampler to the program in the global namespace. - * @param[in] name The name of the sampler object in the shader - * @param[in] pSampler The sampler object to bind - * @return false if the sampler was not found in the program, otherwise true + * Get the sampler bound to a variable by bind location. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] bindLocation The bind location of the variable. + * @return The bound sampler or nullptr if none is bound. */ - bool setSampler(const std::string& name, const ref& pSampler); + ref getSampler(const BindLocation& bindLocation) const; /** - * Bind a sampler to the program in the global namespace. - * @param[in] bindLocation The bind-location in the block - * @param[in] pSampler The sampler object to bind - * @return false if the sampler was not found in the program, otherwise true + * Get the sampler bound to a variable by name. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] name The name of the variable. + * @return The bound sampler or nullptr if none is bound. */ - bool setSampler(const BindLocation& bindLocation, const ref& pSampler); + ref getSampler(std::string_view name) const; + + // + // ParameterBlock + // /** - * Gets a sampler object. - * @param[in] bindLocation The bind-location in the block - * @return If the bind-location is valid, a shared pointer to the sampler. Otherwise returns nullptr + * Bind a parameter block to a variable by name. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] name The name of the variable to bind to. + * @param[in] pBlock The parameter block. */ - ref getSampler(const BindLocation& bindLocation) const; + void setParameterBlock(std::string_view name, const ref& pBlock); + + /** + * Bind a parameter block to a variable by bind location. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] bindLocation The bind location of the variable to bind to. + * @param[in] pBlock The parameter block object. + */ + void setParameterBlock(const BindLocation& bindLocation, const ref& pBlock); + + /** + * Get the parameter block bound to a variable by name. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] name The name of the variable. + * @return The bound parameter block or nullptr if none is bound. + */ + ref getParameterBlock(std::string_view name) const; /** - * Gets a sampler object. - * @return If the name is valid, a shared pointer to the sampler. Otherwise returns nullptr + * Get the parameter block bound to a variable by bind location. + * Throws an exception if the variable doesn't exist or there is a type-mismatch. + * @param[in] bindLocation The bind location of the variable. + * @return The bound parameter block or nullptr if none is bound. */ - ref getSampler(const std::string& name) const; + ref getParameterBlock(const BindLocation& bindLocation) const; /** * Get the parameter block's reflection interface @@ -292,7 +338,7 @@ class FALCOR_API ParameterBlock : public Object /** * Get offset of a uniform variable inside the block, given its name. */ - UniformShaderVarOffset getVariableOffset(const std::string& varName) const; + TypedShaderVarOffset getVariableOffset(std::string_view varName) const; /** * Get an initial var to the contents of this block. @@ -304,7 +350,7 @@ class FALCOR_API ParameterBlock : public Object * * Returns an invalid shader var if no such member is found. */ - ShaderVar findMember(const std::string& varName) const; + ShaderVar findMember(std::string_view varName) const; /** * Try to find a shader var for a member of the block by index. @@ -323,24 +369,11 @@ class FALCOR_API ParameterBlock : public Object bool prepareDescriptorSets(CopyContext* pCopyContext); - const ref& getParameterBlock(uint32_t resourceRangeIndex, uint32_t arrayIndex) const; - - // Delete some functions. If they are not deleted, the compiler will try to convert the uints to string, resulting in runtime error - ref getSampler(uint32_t) = delete; - bool setSampler(uint32_t, ref) = delete; - using SpecializationArgs = std::vector; void collectSpecializationArgs(SpecializationArgs& ioArgs) const; void const* getRawData() const; - /** - * Get the underlying constant buffer that holds the ordinary/uniform data for this block. - * Be cautious with the returned buffer as it can be invalidated any time you set/bind something - * to the parameter block (or one if its internal sub-blocks). - */ - ref getUnderlyingConstantBuffer() const; - protected: ParameterBlock( ref pDevice, @@ -373,8 +406,8 @@ class FALCOR_API ParameterBlock : public Object }; template -bool ShaderVar::setImpl(const T& val) const +void ShaderVar::setImpl(const T& val) const { - return mpBlock->setVariable(mOffset, val); + mpBlock->setVariable(mOffset, val); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/PythonHelpers.cpp b/Source/Falcor/Core/API/PythonHelpers.cpp new file mode 100644 index 000000000..7d933e7f2 --- /dev/null +++ b/Source/Falcor/Core/API/PythonHelpers.cpp @@ -0,0 +1,168 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "PythonHelpers.h" + +namespace Falcor +{ + +pybind11::dlpack::dtype dataTypeToDtype(DataType type) +{ + switch (type) + { + case DataType::int8: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::Int, (uint8_t)8, 1}; + case DataType::int16: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::Int, (uint8_t)16, 1}; + case DataType::int32: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::Int, (uint8_t)32, 1}; + case DataType::int64: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::Int, (uint8_t)64, 1}; + case DataType::uint8: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::UInt, (uint8_t)8, 1}; + case DataType::uint16: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::UInt, (uint8_t)16, 1}; + case DataType::uint32: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::UInt, (uint8_t)32, 1}; + case DataType::uint64: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::UInt, (uint8_t)64, 1}; + case DataType::float16: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::Float, (uint8_t)16, 1}; + case DataType::float32: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::Float, (uint8_t)32, 1}; + case DataType::float64: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::Float, (uint8_t)64, 1}; + } + FALCOR_THROW("Unhandled data type."); +} + +std::optional resourceFormatToDtype(ResourceFormat format) +{ + // Unknown and compressed formats are not supported. + if (format == ResourceFormat::Unknown || isCompressedFormat(format)) + return {}; + + // Formats with different bits per channel are not supported. + uint32_t channelCount = getFormatChannelCount(format); + uint32_t channelBits = getNumChannelBits(format, 0); + for (uint32_t i = 1; i < channelCount; ++i) + if (getNumChannelBits(format, i) != channelBits) + return {}; + + // Only formats with 8, 16, 32, or 64 bits per channel are supported. + if (channelBits != 8 && channelBits != 16 && channelBits != 32 && channelBits != 64) + return {}; + + switch (getFormatType(format)) + { + case FormatType::Float: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::Float, (uint8_t)channelBits, 1}; + case FormatType::Uint: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::UInt, (uint8_t)channelBits, 1}; + case FormatType::Sint: + return pybind11::dlpack::dtype{(uint8_t)pybind11::dlpack::dtype_code::Int, (uint8_t)channelBits, 1}; + } + + return {}; +} + +pybind11::dict defineListToPython(const DefineList& defines) +{ + pybind11::dict dict; + for (const auto& [key, value] : defines) + dict[key.c_str()] = value; + return dict; +} + +DefineList defineListFromPython(const pybind11::dict& dict) +{ + DefineList defines; + for (const auto& [key, value] : dict) + { + if (!pybind11::isinstance(key)) + FALCOR_THROW("Define key must be a string."); + auto keyStr = key.cast(); + if (pybind11::isinstance(value)) + defines.add(keyStr, value.cast()); + else if (pybind11::isinstance(value)) + defines.add(keyStr, value.cast() ? "1" : "0"); + else if (pybind11::isinstance(value)) + defines.add(keyStr, std::to_string(value.cast())); + else + FALCOR_THROW("Define value for key '{}' must be a string, bool, or int.", keyStr); + } + return defines; +} + +pybind11::dict typeConformanceListToPython(const TypeConformanceList& conformances) +{ + pybind11::dict dict; + for (const auto& [key, value] : conformances) + dict[pybind11::make_tuple(key.typeName, key.interfaceName)] = value; + return dict; +} + +TypeConformanceList typeConformanceListFromPython(const pybind11::dict& dict) +{ + TypeConformanceList conformances; + for (const auto& [key, value] : dict) + { + auto [typeName, interfaceType] = key.cast>(); + conformances.add(typeName, interfaceType, value.cast()); + } + return conformances; +} + +ProgramDesc programDescFromPython(const pybind11::kwargs& kwargs) +{ + ProgramDesc desc; + for (const auto& arg : kwargs) + { + std::string key = arg.first.cast(); + const auto& value = arg.second; + + if (key == "file") + desc.addShaderModule().addFile(value.cast()); + else if (key == "string") + desc.addShaderModule().addString(value.cast()); + else if (key == "cs_entry") + desc.csEntry(value.cast()); + else if (key == "type_conformances") + desc.typeConformances = typeConformanceListFromPython(value.cast()); + else if (key == "shader_model") + desc.shaderModel = value.cast(); + else if (key == "compiler_flags") + desc.compilerFlags = value.cast(); + else if (key == "compiler_arguments") + desc.compilerArguments = value.cast>(); + else + FALCOR_THROW("Unknown keyword argument '{}'.", key); + } + return desc; +} + +} // namespace Falcor diff --git a/Source/Falcor/Core/Program/ComputeProgram.cpp b/Source/Falcor/Core/API/PythonHelpers.h similarity index 54% rename from Source/Falcor/Core/Program/ComputeProgram.cpp rename to Source/Falcor/Core/API/PythonHelpers.h index 8b57b203f..3ab576d7d 100644 --- a/Source/Falcor/Core/Program/ComputeProgram.cpp +++ b/Source/Falcor/Core/API/PythonHelpers.h @@ -25,50 +25,65 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#include "ComputeProgram.h" -#include "ProgramManager.h" -#include "Core/ObjectPython.h" -#include "Core/API/ComputeContext.h" -#include "Core/State/ComputeState.h" +#pragma once + +#include "Core/API/Formats.h" +#include "Core/Program/Program.h" #include "Utils/Scripting/ScriptBindings.h" +#include "Utils/Scripting/ndarray.h" + +#include namespace Falcor { -ref ComputeProgram::createFromFile( - ref pDevice, - const std::filesystem::path& path, - const std::string& csEntry, - const DefineList& programDefines, - CompilerFlags flags, - const std::string& shaderModel -) + +inline size_t getDtypeByteSize(pybind11::dlpack::dtype dtype) { - Desc d(path); - if (!shaderModel.empty()) - d.setShaderModel(shaderModel); - d.setCompilerFlags(flags); - d.csEntry(csEntry); - return create(pDevice, d, programDefines); + return (dtype.bits * dtype.lanes) / 8; } -ref ComputeProgram::create(ref pDevice, const Desc& desc, const DefineList& programDefines) +template +size_t getNdarraySize(const pybind11::ndarray& array) { - return ref(new ComputeProgram(pDevice, desc, programDefines)); + size_t size = 1; + for (size_t i = 0; i < array.ndim(); i++) + size *= array.shape(i); + return size; } -ComputeProgram::ComputeProgram(ref pDevice, const Desc& desc, const DefineList& programDefines) - : Program(pDevice, desc, programDefines) -{} - -void ComputeProgram::dispatchCompute(ComputeContext* pContext, ComputeVars* pVars, const uint3& threadGroupCount) +template +size_t getNdarrayByteSize(const pybind11::ndarray& array) { - auto pState = ComputeState::create(mpDevice); - pState->setProgram(ref(this)); - pContext->dispatch(pState.get(), pVars, threadGroupCount); + return getNdarraySize(array) * getDtypeByteSize(array.dtype()); } -FALCOR_SCRIPT_BINDING(ComputeProgram) +template +size_t isNdarrayContiguous(const pybind11::ndarray& array) { - pybind11::class_>(m, "ComputeProgram"); + if (array.ndim() == 0) + return false; + size_t prod = 1; + for (size_t i = array.ndim() - 1;;) + { + if (array.stride(i) != prod) + return false; + prod *= array.shape(i); + if (i == 0) + break; + --i; + } + return true; } + +pybind11::dlpack::dtype dataTypeToDtype(DataType type); +std::optional resourceFormatToDtype(ResourceFormat format); + +pybind11::dict defineListToPython(const DefineList& defines); +DefineList defineListFromPython(const pybind11::dict& dict); + +pybind11::dict typeConformanceListToPython(const TypeConformanceList& conformances); +TypeConformanceList typeConformanceListFromPython(const pybind11::dict& dict); + +ProgramDesc programDescFromPython(const pybind11::kwargs& kwargs); + } // namespace Falcor diff --git a/Source/Falcor/Core/API/QueryHeap.h b/Source/Falcor/Core/API/QueryHeap.h index 84641c272..eaaeba4eb 100644 --- a/Source/Falcor/Core/API/QueryHeap.h +++ b/Source/Falcor/Core/API/QueryHeap.h @@ -28,8 +28,8 @@ #pragma once #include "fwd.h" #include "Handles.h" -#include "Core/Assert.h" #include "Core/Macros.h" +#include "Core/Error.h" #include "Core/Object.h" #include diff --git a/Source/Falcor/Core/API/RenderContext.cpp b/Source/Falcor/Core/API/RenderContext.cpp index 131565fe4..34bf1fafb 100644 --- a/Source/Falcor/Core/API/RenderContext.cpp +++ b/Source/Falcor/Core/API/RenderContext.cpp @@ -174,11 +174,11 @@ void RenderContext::clearTexture(Texture* pTexture, const float4& clearColor) auto bindFlags = pTexture->getBindFlags(); // Select the right clear based on the texture's binding flags - if (is_set(bindFlags, Resource::BindFlags::RenderTarget)) + if (is_set(bindFlags, ResourceBindFlags::RenderTarget)) clearRtv(pTexture->getRTV().get(), clearColor); - else if (is_set(bindFlags, Resource::BindFlags::UnorderedAccess)) + else if (is_set(bindFlags, ResourceBindFlags::UnorderedAccess)) clearUAV(pTexture->getUAV().get(), clearColor); - else if (is_set(bindFlags, Resource::BindFlags::DepthStencil)) + else if (is_set(bindFlags, ResourceBindFlags::DepthStencil)) { if (isStencilFormat(format) && (clearColor.y != 0)) { @@ -196,9 +196,9 @@ void RenderContext::clearTexture(Texture* pTexture, const float4& clearColor) } } -void RenderContext::flush(bool wait) +void RenderContext::submit(bool wait) { - ComputeContext::flush(wait); + ComputeContext::submit(wait); mpLastBoundGraphicsVars = nullptr; } @@ -207,14 +207,21 @@ void RenderContext::blit( const ref& pDst, uint4 srcRect, uint4 dstRect, - Sampler::Filter filter + TextureFilteringMode filter ) { - const Sampler::ReductionMode componentsReduction[] = { - Sampler::ReductionMode::Standard, Sampler::ReductionMode::Standard, Sampler::ReductionMode::Standard, - Sampler::ReductionMode::Standard}; + const TextureReductionMode componentsReduction[] = { + TextureReductionMode::Standard, + TextureReductionMode::Standard, + TextureReductionMode::Standard, + TextureReductionMode::Standard, + }; const float4 componentsTransform[] = { - float4(1.0f, 0.0f, 0.0f, 0.0f), float4(0.0f, 1.0f, 0.0f, 0.0f), float4(0.0f, 0.0f, 1.0f, 0.0f), float4(0.0f, 0.0f, 0.0f, 1.0f)}; + float4(1.0f, 0.0f, 0.0f, 0.0f), + float4(0.0f, 1.0f, 0.0f, 0.0f), + float4(0.0f, 0.0f, 1.0f, 0.0f), + float4(0.0f, 0.0f, 0.0f, 1.0f), + }; blit(pSrc, pDst, srcRect, dstRect, filter, componentsReduction, componentsTransform); } @@ -224,8 +231,8 @@ void RenderContext::blit( const ref& pDst, uint4 srcRect, uint4 dstRect, - Sampler::Filter filter, - const Sampler::ReductionMode componentsReduction[4], + TextureFilteringMode filter, + const TextureReductionMode componentsReduction[4], const float4 componentsTransform[4] ) { @@ -239,7 +246,7 @@ void RenderContext::blit( FALCOR_ASSERT(pSrcResource && pDstResource); if (pSrcResource->getType() == Resource::Type::Buffer || pDstResource->getType() == Resource::Type::Buffer) { - throw ArgumentError("RenderContext::blit does not support buffers"); + FALCOR_THROW("RenderContext::blit does not support buffers"); } const Texture* pSrcTexture = dynamic_cast(pSrcResource); @@ -266,8 +273,8 @@ void RenderContext::blit( // Determine the type of blit. const uint32_t sampleCount = pSrcTexture->getSampleCount(); const bool complexBlit = - !((componentsReduction[0] == Sampler::ReductionMode::Standard) && (componentsReduction[1] == Sampler::ReductionMode::Standard) && - (componentsReduction[2] == Sampler::ReductionMode::Standard) && (componentsReduction[3] == Sampler::ReductionMode::Standard) && + !((componentsReduction[0] == TextureReductionMode::Standard) && (componentsReduction[1] == TextureReductionMode::Standard) && + (componentsReduction[2] == TextureReductionMode::Standard) && (componentsReduction[3] == TextureReductionMode::Standard) && all(componentsTransform[0] == float4(1.0f, 0.0f, 0.0f, 0.0f)) && all(componentsTransform[1] == float4(0.0f, 1.0f, 0.0f, 0.0f)) && all(componentsTransform[2] == float4(0.0f, 0.0f, 1.0f, 0.0f)) && all(componentsTransform[3] == float4(0.0f, 0.0f, 0.0f, 1.0f))); @@ -296,23 +303,23 @@ void RenderContext::blit( // Complex blit doesn't work with multi-sampled textures. if (complexBlit && sampleCount > 1) - throw RuntimeError("RenderContext::blit() does not support sample count > 1 for complex blit"); + FALCOR_THROW("RenderContext::blit() does not support sample count > 1 for complex blit"); // Validate source format. Only single-sampled basic blit handles integer source format. // All variants support casting to integer destination format. if (isIntegerFormat(pSrcTexture->getFormat())) { if (sampleCount > 1) - throw RuntimeError("RenderContext::blit() requires non-integer source format for multi-sampled textures"); + FALCOR_THROW("RenderContext::blit() requires non-integer source format for multi-sampled textures"); else if (complexBlit) - throw RuntimeError("RenderContext::blit() requires non-integer source format for complex blit"); + FALCOR_THROW("RenderContext::blit() requires non-integer source format for complex blit"); } // Blit does not support texture arrays or mip maps. if (!(pSrc->getViewInfo().arraySize == 1 && pSrc->getViewInfo().mipCount == 1) || !(pDst->getViewInfo().arraySize == 1 && pDst->getViewInfo().mipCount == 1)) { - throw RuntimeError("RenderContext::blit() does not support texture arrays or mip maps"); + FALCOR_THROW("RenderContext::blit() does not support texture arrays or mip maps"); } // Configure program. @@ -328,14 +335,14 @@ void RenderContext::blit( ref usedSampler[4]; for (uint32_t i = 0; i < 4; i++) { - FALCOR_ASSERT(componentsReduction[i] != Sampler::ReductionMode::Comparison); // Comparison mode not supported. + FALCOR_ASSERT(componentsReduction[i] != TextureReductionMode::Comparison); // Comparison mode not supported. - if (componentsReduction[i] == Sampler::ReductionMode::Min) - usedSampler[i] = (filter == Sampler::Filter::Linear) ? blitCtx.pLinearMinSampler : blitCtx.pPointMinSampler; - else if (componentsReduction[i] == Sampler::ReductionMode::Max) - usedSampler[i] = (filter == Sampler::Filter::Linear) ? blitCtx.pLinearMaxSampler : blitCtx.pPointMaxSampler; + if (componentsReduction[i] == TextureReductionMode::Min) + usedSampler[i] = (filter == TextureFilteringMode::Linear) ? blitCtx.pLinearMinSampler : blitCtx.pPointMinSampler; + else if (componentsReduction[i] == TextureReductionMode::Max) + usedSampler[i] = (filter == TextureFilteringMode::Linear) ? blitCtx.pLinearMaxSampler : blitCtx.pPointMaxSampler; else - usedSampler[i] = (filter == Sampler::Filter::Linear) ? blitCtx.pLinearSampler : blitCtx.pPointSampler; + usedSampler[i] = (filter == TextureFilteringMode::Linear) ? blitCtx.pLinearSampler : blitCtx.pPointSampler; } blitCtx.pPass->getVars()->setSampler("gSamplerR", usedSampler[0]); @@ -356,7 +363,7 @@ void RenderContext::blit( else { blitCtx.pPass->getVars()->setSampler( - "gSampler", (filter == Sampler::Filter::Linear) ? blitCtx.pLinearSampler : blitCtx.pPointSampler + "gSampler", (filter == TextureFilteringMode::Linear) ? blitCtx.pLinearSampler : blitCtx.pPointSampler ); } @@ -430,7 +437,7 @@ void RenderContext::clearDsv(const DepthStencilView* pDsv, float depth, uint8_t void RenderContext::drawInstanced( GraphicsState* pState, - GraphicsVars* pVars, + ProgramVars* pVars, uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertexLocation, @@ -438,20 +445,20 @@ void RenderContext::drawInstanced( ) { auto encoder = drawCallCommon(pState, pVars); - encoder->drawInstanced(vertexCount, instanceCount, startVertexLocation, startInstanceLocation); + FALCOR_GFX_CALL(encoder->drawInstanced(vertexCount, instanceCount, startVertexLocation, startInstanceLocation)); mCommandsPending = true; } -void RenderContext::draw(GraphicsState* pState, GraphicsVars* pVars, uint32_t vertexCount, uint32_t startVertexLocation) +void RenderContext::draw(GraphicsState* pState, ProgramVars* pVars, uint32_t vertexCount, uint32_t startVertexLocation) { auto encoder = drawCallCommon(pState, pVars); - encoder->draw(vertexCount, startVertexLocation); + FALCOR_GFX_CALL(encoder->draw(vertexCount, startVertexLocation)); mCommandsPending = true; } void RenderContext::drawIndexedInstanced( GraphicsState* pState, - GraphicsVars* pVars, + ProgramVars* pVars, uint32_t indexCount, uint32_t instanceCount, uint32_t startIndexLocation, @@ -460,26 +467,27 @@ void RenderContext::drawIndexedInstanced( ) { auto encoder = drawCallCommon(pState, pVars); - encoder->drawIndexedInstanced(indexCount, instanceCount, startIndexLocation, baseVertexLocation, startInstanceLocation); + FALCOR_GFX_CALL(encoder->drawIndexedInstanced(indexCount, instanceCount, startIndexLocation, baseVertexLocation, startInstanceLocation) + ); mCommandsPending = true; } void RenderContext::drawIndexed( GraphicsState* pState, - GraphicsVars* pVars, + ProgramVars* pVars, uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation ) { auto encoder = drawCallCommon(pState, pVars); - encoder->drawIndexed(indexCount, startIndexLocation, baseVertexLocation); + FALCOR_GFX_CALL(encoder->drawIndexed(indexCount, startIndexLocation, baseVertexLocation)); mCommandsPending = true; } void RenderContext::drawIndirect( GraphicsState* pState, - GraphicsVars* pVars, + ProgramVars* pVars, uint32_t maxCommandCount, const Buffer* pArgBuffer, uint64_t argBufferOffset, @@ -489,16 +497,19 @@ void RenderContext::drawIndirect( { resourceBarrier(pArgBuffer, Resource::State::IndirectArg); auto encoder = drawCallCommon(pState, pVars); - encoder->drawIndirect( - maxCommandCount, pArgBuffer->getGfxBufferResource(), argBufferOffset, pCountBuffer ? pCountBuffer->getGfxBufferResource() : nullptr, + FALCOR_GFX_CALL(encoder->drawIndirect( + maxCommandCount, + pArgBuffer->getGfxBufferResource(), + argBufferOffset, + pCountBuffer ? pCountBuffer->getGfxBufferResource() : nullptr, countBufferOffset - ); + )); mCommandsPending = true; } void RenderContext::drawIndexedIndirect( GraphicsState* pState, - GraphicsVars* pVars, + ProgramVars* pVars, uint32_t maxCommandCount, const Buffer* pArgBuffer, uint64_t argBufferOffset, @@ -508,14 +519,17 @@ void RenderContext::drawIndexedIndirect( { resourceBarrier(pArgBuffer, Resource::State::IndirectArg); auto encoder = drawCallCommon(pState, pVars); - encoder->drawIndexedIndirect( - maxCommandCount, pArgBuffer->getGfxBufferResource(), argBufferOffset, pCountBuffer ? pCountBuffer->getGfxBufferResource() : nullptr, + FALCOR_GFX_CALL(encoder->drawIndexedIndirect( + maxCommandCount, + pArgBuffer->getGfxBufferResource(), + argBufferOffset, + pCountBuffer ? pCountBuffer->getGfxBufferResource() : nullptr, countBufferOffset - ); + )); mCommandsPending = true; } -void RenderContext::raytrace(RtProgram* pProgram, RtProgramVars* pVars, uint32_t width, uint32_t height, uint32_t depth) +void RenderContext::raytrace(Program* pProgram, RtProgramVars* pVars, uint32_t width, uint32_t height, uint32_t depth) { auto pRtso = pProgram->getRtso(pVars); @@ -524,7 +538,7 @@ void RenderContext::raytrace(RtProgram* pProgram, RtProgramVars* pVars, uint32_t auto rtEncoder = mpLowLevelData->getRayTracingCommandEncoder(); FALCOR_GFX_CALL(rtEncoder->bindPipelineWithRootObject(pRtso->getGfxPipelineState(), pVars->getShaderObject())); - rtEncoder->dispatchRays(0, pVars->getShaderTable(), width, height, depth); + FALCOR_GFX_CALL(rtEncoder->dispatchRays(0, pVars->getShaderTable(), width, height, depth)); mCommandsPending = true; } @@ -544,8 +558,12 @@ void RenderContext::resolveSubresource(const ref& pSrc, uint32_t srcSub dstRange.mipLevelCount = 1; resourceEncoder->resolveResource( - pSrc->getGfxTextureResource(), gfx::ResourceState::ResolveSource, srcRange, pDst->getGfxTextureResource(), - gfx::ResourceState::ResolveDestination, dstRange + pSrc->getGfxTextureResource(), + gfx::ResourceState::ResolveSource, + srcRange, + pDst->getGfxTextureResource(), + gfx::ResourceState::ResolveDestination, + dstRange ); mCommandsPending = true; } @@ -561,8 +579,12 @@ void RenderContext::resolveResource(const ref& pSrc, const ref gfx::SubresourceRange dstRange = {}; resourceEncoder->resolveResource( - pSrc->getGfxTextureResource(), gfx::ResourceState::ResolveSource, srcRange, pDst->getGfxTextureResource(), - gfx::ResourceState::ResolveDestination, dstRange + pSrc->getGfxTextureResource(), + gfx::ResourceState::ResolveSource, + srcRange, + pDst->getGfxTextureResource(), + gfx::ResourceState::ResolveDestination, + dstRange ); mCommandsPending = true; } @@ -606,7 +628,7 @@ void RenderContext::copyAccelerationStructure( mCommandsPending = true; } -gfx::IRenderCommandEncoder* RenderContext::drawCallCommon(GraphicsState* pState, GraphicsVars* pVars) +gfx::IRenderCommandEncoder* RenderContext::drawCallCommon(GraphicsState* pState, ProgramVars* pVars) { // Insert barriers for bound resources. pVars->prepareDescriptorSets(this); @@ -647,18 +669,12 @@ gfx::IRenderCommandEncoder* RenderContext::drawCallCommon(GraphicsState* pState, { auto bufferLayout = pVertexLayout->getBufferLayout(i); auto vertexBuffer = pVao->getVertexBuffer(i).get(); - encoder->setVertexBuffer( - i, pVao->getVertexBuffer(i)->getGfxBufferResource(), - bufferLayout->getElementOffset(0) + (uint32_t)vertexBuffer->getGpuAddressOffset() - ); + encoder->setVertexBuffer(i, pVao->getVertexBuffer(i)->getGfxBufferResource(), bufferLayout->getElementOffset(0)); } if (pVao->getIndexBuffer()) { auto indexBuffer = pVao->getIndexBuffer().get(); - encoder->setIndexBuffer( - indexBuffer->getGfxBufferResource(), getGFXFormat(pVao->getIndexBufferFormat()), - (uint32_t)indexBuffer->getGpuAddressOffset() - ); + encoder->setIndexBuffer(indexBuffer->getGfxBufferResource(), getGFXFormat(pVao->getIndexBufferFormat())); } encoder->setPrimitiveTopology(getGFXPrimitiveTopology(pVao->getPrimitiveTopology())); encoder->setViewports( @@ -674,7 +690,33 @@ gfx::IRenderCommandEncoder* RenderContext::drawCallCommon(GraphicsState* pState, FALCOR_SCRIPT_BINDING(CopyContext) { + using namespace pybind11::literals; + + FALCOR_SCRIPT_BINDING_DEPENDENCY(Resource) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Buffer) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Texture) + pybind11::class_ copyContext(m, "CopyContext"); + + copyContext.def("submit", &CopyContext::submit, "wait"_a = false); + +#if FALCOR_HAS_CUDA + copyContext.def( + "wait_for_cuda", + [](CopyContext& self, uint64_t stream = 0) { self.waitForCuda(reinterpret_cast(stream)); }, + "stream"_a = 0 + ); + copyContext.def( + "wait_for_falcor", + [](CopyContext& self, uint64_t stream = 0) { self.waitForFalcor(reinterpret_cast(stream)); }, + "stream"_a = 0 + ); +#endif + + copyContext.def("uav_barrier", &CopyContext::uavBarrier, "resource"_a); + copyContext.def("copy_resource", &CopyContext::copyResource, "dst"_a, "src"_a); + copyContext.def("copy_subresource", &CopyContext::copySubresource, "dst"_a, "dst_subresource_idx"_a, "src"_a, "src_subresource_idx"_a); + copyContext.def("copy_buffer_region", &CopyContext::copyBufferRegion, "dst"_a, "dst_offset"_a, "src"_a, "src_offset"_a, "num_bytes"_a); } FALCOR_SCRIPT_BINDING(ComputeContext) diff --git a/Source/Falcor/Core/API/RenderContext.h b/Source/Falcor/Core/API/RenderContext.h index 061a70aa6..71a7c6cbc 100644 --- a/Source/Falcor/Core/API/RenderContext.h +++ b/Source/Falcor/Core/API/RenderContext.h @@ -42,11 +42,11 @@ namespace Falcor { class GraphicsStateObject; class GraphicsState; -class GraphicsVars; +class ProgramVars; class RenderTargetView; -class RtProgram; +class Program; class RtProgramVars; struct BlitContext; @@ -152,7 +152,7 @@ class FALCOR_API RenderContext : public ComputeContext * @param[in] vertexCount Number of vertices to draw * @param[in] startVertexLocation The location of the first vertex to read from the vertex buffers (offset in vertices) */ - void draw(GraphicsState* pState, GraphicsVars* pVars, uint32_t vertexCount, uint32_t startVertexLocation); + void draw(GraphicsState* pState, ProgramVars* pVars, uint32_t vertexCount, uint32_t startVertexLocation); /** * Ordered instanced draw call. @@ -163,7 +163,7 @@ class FALCOR_API RenderContext : public ComputeContext */ void drawInstanced( GraphicsState* pState, - GraphicsVars* pVars, + ProgramVars* pVars, uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertexLocation, @@ -178,7 +178,7 @@ class FALCOR_API RenderContext : public ComputeContext */ void drawIndexed( GraphicsState* pState, - GraphicsVars* pVars, + ProgramVars* pVars, uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation @@ -194,7 +194,7 @@ class FALCOR_API RenderContext : public ComputeContext */ void drawIndexedInstanced( GraphicsState* pState, - GraphicsVars* pVars, + ProgramVars* pVars, uint32_t indexCount, uint32_t instanceCount, uint32_t startIndexLocation, @@ -214,7 +214,7 @@ class FALCOR_API RenderContext : public ComputeContext */ void drawIndirect( GraphicsState* pState, - GraphicsVars* pVars, + ProgramVars* pVars, uint32_t maxCommandCount, const Buffer* pArgBuffer, uint64_t argBufferOffset, @@ -234,7 +234,7 @@ class FALCOR_API RenderContext : public ComputeContext */ void drawIndexedIndirect( GraphicsState* pState, - GraphicsVars* pVars, + ProgramVars* pVars, uint32_t maxCommandCount, const Buffer* pArgBuffer, uint64_t argBufferOffset, @@ -256,7 +256,7 @@ class FALCOR_API RenderContext : public ComputeContext const ref& pDst, uint4 srcRect = kMaxRect, uint4 dstRect = kMaxRect, - Sampler::Filter = Sampler::Filter::Linear + TextureFilteringMode = TextureFilteringMode::Linear ); /** @@ -276,15 +276,15 @@ class FALCOR_API RenderContext : public ComputeContext const ref& pDst, uint4 srcRect, uint4 dstRect, - Sampler::Filter filter, - const Sampler::ReductionMode componentsReduction[4], + TextureFilteringMode filter, + const TextureReductionMode componentsReduction[4], const float4 componentsTransform[4] ); /** * Submit the command list */ - void flush(bool wait = false) override; + void submit(bool wait = false) override; /** * Tell the render context what it should and shouldn't bind before drawing @@ -311,7 +311,7 @@ class FALCOR_API RenderContext : public ComputeContext * Submit a raytrace command. This function doesn't change the state of the render-context. Graphics/compute vars and state will stay * the same. */ - void raytrace(RtProgram* pProgram, RtProgramVars* pVars, uint32_t width, uint32_t height, uint32_t depth); + void raytrace(Program* pProgram, RtProgramVars* pVars, uint32_t width, uint32_t height, uint32_t depth); /** * Build an acceleration structure. @@ -330,13 +330,13 @@ class FALCOR_API RenderContext : public ComputeContext private: RenderContext(gfx::ICommandQueue* pQueue); - gfx::IRenderCommandEncoder* drawCallCommon(GraphicsState* pState, GraphicsVars* pVars); + gfx::IRenderCommandEncoder* drawCallCommon(GraphicsState* pState, ProgramVars* pVars); std::unique_ptr mpBlitContext; StateBindFlags mBindFlags = StateBindFlags::All; GraphicsStateObject* mpLastBoundGraphicsStateObject = nullptr; - GraphicsVars* mpLastBoundGraphicsVars = nullptr; + ProgramVars* mpLastBoundGraphicsVars = nullptr; }; FALCOR_ENUM_CLASS_OPERATORS(RenderContext::StateBindFlags); diff --git a/Source/Falcor/Core/API/Resource.cpp b/Source/Falcor/Core/API/Resource.cpp index bd399b4fa..66e2cac7c 100644 --- a/Source/Falcor/Core/API/Resource.cpp +++ b/Source/Falcor/Core/API/Resource.cpp @@ -31,7 +31,7 @@ #include "Buffer.h" #include "GFXAPI.h" #include "NativeHandleTraits.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/ObjectPython.h" #include "Utils/Logger.h" #include "Utils/StringUtils.h" @@ -40,7 +40,7 @@ namespace Falcor { -Resource::Resource(ref pDevice, Type type, BindFlags bindFlags, uint64_t size) +Resource::Resource(ref pDevice, Type type, ResourceBindFlags bindFlags, uint64_t size) : mpDevice(pDevice), mType(type), mBindFlags(bindFlags), mSize(size) {} diff --git a/Source/Falcor/Core/API/Resource.h b/Source/Falcor/Core/API/Resource.h index 4c509cb33..54df43f3a 100644 --- a/Source/Falcor/Core/API/Resource.h +++ b/Source/Falcor/Core/API/Resource.h @@ -48,8 +48,6 @@ class FALCOR_API Resource : public Object { FALCOR_OBJECT(Resource) public: - using BindFlags = ResourceBindFlags; - /** * Resource types. Notice there are no array types. Array are controlled using the array size parameter on texture creation. */ @@ -104,7 +102,7 @@ class FALCOR_API Resource : public Object /** * Get the bind flags */ - BindFlags getBindFlags() const { return mBindFlags; } + ResourceBindFlags getBindFlags() const { return mBindFlags; } bool isStateGlobal() const { return mState.isGlobal; } @@ -191,11 +189,11 @@ class FALCOR_API Resource : public Object protected: friend class CopyContext; - Resource(ref pDevice, Type type, BindFlags bindFlags, uint64_t size); + Resource(ref pDevice, Type type, ResourceBindFlags bindFlags, uint64_t size); BreakableReference mpDevice; Type mType; - BindFlags mBindFlags; + ResourceBindFlags mBindFlags; struct { bool isGlobal = true; @@ -207,7 +205,6 @@ class FALCOR_API Resource : public Object void setGlobalState(State newState) const; size_t mSize = 0; ///< Size of the resource in bytes. - GpuAddress mGpuVaOffset = 0; std::string mName; mutable SharedResourceApiHandle mSharedApiHandle = 0; diff --git a/Source/Falcor/Core/API/ResourceViews.cpp b/Source/Falcor/Core/API/ResourceViews.cpp index cf7f00791..067f28be4 100644 --- a/Source/Falcor/Core/API/ResourceViews.cpp +++ b/Source/Falcor/Core/API/ResourceViews.cpp @@ -95,6 +95,7 @@ ref ShaderResourceView::create( uint32_t arraySize ) { + FALCOR_CHECK(is_set(pTexture->getBindFlags(), ResourceBindFlags::ShaderResource), "Texture does not have SRV bind flag set."); Slang::ComPtr handle; gfx::IResourceView::Desc desc = {}; desc.format = getGFXFormat(depthToColorFormat(pTexture->getFormat())); @@ -166,6 +167,7 @@ ref DepthStencilView::create( uint32_t arraySize ) { + FALCOR_CHECK(is_set(pTexture->getBindFlags(), ResourceBindFlags::DepthStencil), "Texture does not have DSV bind flag set."); Slang::ComPtr handle; gfx::IResourceView::Desc desc = {}; desc.format = getGFXFormat(pTexture->getFormat()); @@ -193,6 +195,7 @@ ref UnorderedAccessView::create( uint32_t arraySize ) { + FALCOR_CHECK(is_set(pTexture->getBindFlags(), ResourceBindFlags::UnorderedAccess), "Texture does not have UAV bind flag set."); Slang::ComPtr handle; gfx::IResourceView::Desc desc = {}; desc.format = getGFXFormat(pTexture->getFormat()); @@ -212,7 +215,9 @@ ref UnorderedAccessView::create(Device* pDevice, Buffer* pB desc.type = gfx::IResourceView::Type::UnorderedAccess; fillBufferViewDesc(desc, pBuffer, firstElement, elementCount); FALCOR_GFX_CALL(pDevice->getGfxDevice()->createBufferView( - pBuffer->getGfxBufferResource(), pBuffer->getUAVCounter() ? pBuffer->getUAVCounter()->getGfxBufferResource() : nullptr, desc, + pBuffer->getGfxBufferResource(), + pBuffer->getUAVCounter() ? pBuffer->getUAVCounter()->getGfxBufferResource() : nullptr, + desc, handle.writeRef() )); return ref(new UnorderedAccessView(pDevice, pBuffer, handle, firstElement, elementCount)); @@ -231,6 +236,7 @@ ref RenderTargetView::create( uint32_t arraySize ) { + FALCOR_CHECK(is_set(pTexture->getBindFlags(), ResourceBindFlags::RenderTarget), "Texture does not have RTV bind flag set."); Slang::ComPtr handle; gfx::IResourceView::Desc desc = {}; desc.format = getGFXFormat(pTexture->getFormat()); diff --git a/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.cpp b/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.cpp index 30ab0e5e3..ed68ddf18 100644 --- a/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.cpp +++ b/Source/Falcor/Core/API/RtAccelerationStructurePostBuildInfoPool.cpp @@ -52,7 +52,7 @@ uint64_t RtAccelerationStructurePostBuildInfoPool::getElement(CopyContext* pCont { if (mNeedFlush) { - pContext->flush(true); + pContext->submit(true); mNeedFlush = false; } uint64_t result = 0; diff --git a/Source/Falcor/Core/API/RtStateObject.cpp b/Source/Falcor/Core/API/RtStateObject.cpp index 5300ba127..3de8d7881 100644 --- a/Source/Falcor/Core/API/RtStateObject.cpp +++ b/Source/Falcor/Core/API/RtStateObject.cpp @@ -28,17 +28,12 @@ #include "RtStateObject.h" #include "Device.h" #include "GFXAPI.h" -#include "Core/Program/RtProgram.h" +#include "Core/Program/Program.h" namespace Falcor { -ref RtStateObject::create(ref pDevice, const Desc& desc) -{ - return ref(new RtStateObject(pDevice, desc)); -} - -RtStateObject::RtStateObject(ref pDevice, const Desc& desc) : mpDevice(pDevice), mDesc(desc) +RtStateObject::RtStateObject(ref pDevice, const RtStateObjectDesc& desc) : mpDevice(pDevice), mDesc(desc) { auto pKernels = getKernels(); gfx::RayTracingPipelineStateDesc rtpDesc = {}; @@ -69,11 +64,11 @@ RtStateObject::RtStateObject(ref pDevice, const Desc& desc) : mpDevice(p static_assert((uint32_t)gfx::RayTracingPipelineFlags::SkipTriangles == (uint32_t)RtPipelineFlags::SkipTriangles); rtpDesc.flags = (gfx::RayTracingPipelineFlags::Enum)mDesc.pipelineFlags; - auto rtProgram = dynamic_cast(mDesc.pKernels->getProgramVersion()->getProgram()); + auto rtProgram = dynamic_cast(mDesc.pProgramKernels->getProgramVersion()->getProgram()); FALCOR_ASSERT(rtProgram); - rtpDesc.maxRayPayloadSize = rtProgram->getRtDesc().getMaxPayloadSize(); - rtpDesc.maxAttributeSizeInBytes = rtProgram->getRtDesc().getMaxAttributeSize(); - rtpDesc.program = mDesc.pKernels->getGfxProgram(); + rtpDesc.maxRayPayloadSize = rtProgram->getDesc().maxPayloadSize; + rtpDesc.maxAttributeSizeInBytes = rtProgram->getDesc().maxAttributeSize; + rtpDesc.program = mDesc.pProgramKernels->getGfxProgram(); FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createRayTracingPipelineState(rtpDesc, mGfxPipelineState.writeRef())); @@ -84,4 +79,10 @@ RtStateObject::RtStateObject(ref pDevice, const Desc& desc) : mpDevice(p mEntryPointGroupExportNames.push_back(pEntryPointGroup->getExportName()); } } + +RtStateObject::~RtStateObject() +{ + mpDevice->releaseResource(mGfxPipelineState); +} + } // namespace Falcor diff --git a/Source/Falcor/Core/API/RtStateObject.h b/Source/Falcor/Core/API/RtStateObject.h index c446c8431..7a448fd6f 100644 --- a/Source/Falcor/Core/API/RtStateObject.h +++ b/Source/Falcor/Core/API/RtStateObject.h @@ -37,52 +37,40 @@ namespace Falcor { + +struct RtStateObjectDesc +{ + ref pProgramKernels; + uint32_t maxTraceRecursionDepth = 0; + RtPipelineFlags pipelineFlags = RtPipelineFlags::None; + + bool operator==(const RtStateObjectDesc& other) const + { + bool result = true; + result = result && (pProgramKernels == other.pProgramKernels); + result = result && (maxTraceRecursionDepth == other.maxTraceRecursionDepth); + result = result && (pipelineFlags == other.pipelineFlags); + return result; + } +}; + class FALCOR_API RtStateObject : public Object { FALCOR_OBJECT(RtStateObject) public: - struct Desc - { - ref pKernels; - uint32_t maxTraceRecursionDepth = 0; - RtPipelineFlags pipelineFlags = RtPipelineFlags::None; - - Desc& setKernels(const ref& pKernels_) - { - pKernels = pKernels_; - return *this; - } - Desc& setMaxTraceRecursionDepth(uint32_t maxDepth) - { - maxTraceRecursionDepth = maxDepth; - return *this; - } - Desc& setPipelineFlags(RtPipelineFlags flags) - { - pipelineFlags = flags; - return *this; - } + RtStateObject(ref pDevice, const RtStateObjectDesc& desc); + ~RtStateObject(); - bool operator==(const Desc& other) const - { - return pKernels == other.pKernels && maxTraceRecursionDepth == other.maxTraceRecursionDepth && - pipelineFlags == other.pipelineFlags; - } - }; - - static ref create(ref pDevice, const Desc& desc); gfx::IPipelineState* getGfxPipelineState() const { return mGfxPipelineState; } - const ref& getKernels() const { return mDesc.pKernels; }; + const ref& getKernels() const { return mDesc.pProgramKernels; }; uint32_t getMaxTraceRecursionDepth() const { return mDesc.maxTraceRecursionDepth; } void const* getShaderIdentifier(uint32_t index) const { return mEntryPointGroupExportNames[index].c_str(); } - const Desc& getDesc() const { return mDesc; } + const RtStateObjectDesc& getDesc() const { return mDesc; } private: - RtStateObject(ref pDevice, const Desc& desc); - ref mpDevice; - Desc mDesc; + RtStateObjectDesc mDesc; Slang::ComPtr mGfxPipelineState; std::vector mEntryPointGroupExportNames; }; diff --git a/Source/Falcor/Core/API/Sampler.cpp b/Source/Falcor/Core/API/Sampler.cpp index 142f7e542..7ed504640 100644 --- a/Source/Falcor/Core/API/Sampler.cpp +++ b/Source/Falcor/Core/API/Sampler.cpp @@ -36,19 +36,19 @@ namespace Falcor { namespace { -gfx::TextureAddressingMode getGFXAddressMode(Sampler::AddressMode mode) +gfx::TextureAddressingMode getGFXAddressMode(TextureAddressingMode mode) { switch (mode) { - case Sampler::AddressMode::Border: + case TextureAddressingMode::Border: return gfx::TextureAddressingMode::ClampToBorder; - case Sampler::AddressMode::Clamp: + case TextureAddressingMode::Clamp: return gfx::TextureAddressingMode::ClampToEdge; - case Sampler::AddressMode::Mirror: + case TextureAddressingMode::Mirror: return gfx::TextureAddressingMode::MirrorRepeat; - case Sampler::AddressMode::MirrorOnce: + case TextureAddressingMode::MirrorOnce: return gfx::TextureAddressingMode::MirrorOnce; - case Sampler::AddressMode::Wrap: + case TextureAddressingMode::Wrap: return gfx::TextureAddressingMode::Wrap; default: FALCOR_UNREACHABLE(); @@ -56,13 +56,13 @@ gfx::TextureAddressingMode getGFXAddressMode(Sampler::AddressMode mode) } } -gfx::TextureFilteringMode getGFXFilter(Sampler::Filter filter) +gfx::TextureFilteringMode getGFXFilter(TextureFilteringMode filter) { switch (filter) { - case Sampler::Filter::Linear: + case TextureFilteringMode::Linear: return gfx::TextureFilteringMode::Linear; - case Sampler::Filter::Point: + case TextureFilteringMode::Point: return gfx::TextureFilteringMode::Point; default: FALCOR_UNREACHABLE(); @@ -70,17 +70,17 @@ gfx::TextureFilteringMode getGFXFilter(Sampler::Filter filter) } } -gfx::TextureReductionOp getGFXReductionMode(Sampler::ReductionMode mode) +gfx::TextureReductionOp getGFXReductionMode(TextureReductionMode mode) { switch (mode) { - case Falcor::Sampler::ReductionMode::Standard: + case Falcor::TextureReductionMode::Standard: return gfx::TextureReductionOp::Average; - case Falcor::Sampler::ReductionMode::Comparison: + case Falcor::TextureReductionMode::Comparison: return gfx::TextureReductionOp::Comparison; - case Falcor::Sampler::ReductionMode::Min: + case Falcor::TextureReductionMode::Min: return gfx::TextureReductionOp::Minimum; - case Falcor::Sampler::ReductionMode::Max: + case Falcor::TextureReductionMode::Max: return gfx::TextureReductionOp::Maximum; default: return gfx::TextureReductionOp::Average; @@ -91,7 +91,7 @@ gfx::TextureReductionOp getGFXReductionMode(Sampler::ReductionMode mode) gfx::ComparisonFunc getGFXComparisonFunc(ComparisonFunc func); -Sampler::Sampler(ref pDevice, const Desc& desc) : mpDevice(pDevice), mDesc(desc) +Sampler::Sampler(ref pDevice, const Desc& desc) : mpDevice(std::move(pDevice)), mDesc(desc) { gfx::ISamplerState::Desc gfxDesc = {}; gfxDesc.addressU = getGFXAddressMode(desc.addressModeU); @@ -101,7 +101,7 @@ Sampler::Sampler(ref pDevice, const Desc& desc) : mpDevice(pDevice), mDe static_assert(sizeof(gfxDesc.borderColor) == sizeof(desc.borderColor)); std::memcpy(gfxDesc.borderColor, &desc.borderColor, sizeof(desc.borderColor)); - gfxDesc.comparisonFunc = getGFXComparisonFunc(desc.comparisonMode); + gfxDesc.comparisonFunc = getGFXComparisonFunc(desc.comparisonFunc); gfxDesc.magFilter = getGFXFilter(desc.magFilter); gfxDesc.maxAnisotropy = desc.maxAnisotropy; gfxDesc.maxLOD = desc.maxLod; @@ -109,8 +109,8 @@ Sampler::Sampler(ref pDevice, const Desc& desc) : mpDevice(pDevice), mDe gfxDesc.minLOD = desc.minLod; gfxDesc.mipFilter = getGFXFilter(desc.mipFilter); gfxDesc.mipLODBias = desc.lodBias; - gfxDesc.reductionOp = (desc.comparisonMode != Sampler::ComparisonMode::Disabled) ? gfx::TextureReductionOp::Comparison - : getGFXReductionMode(desc.reductionMode); + gfxDesc.reductionOp = + (desc.comparisonFunc != ComparisonFunc::Disabled) ? gfx::TextureReductionOp::Comparison : getGFXReductionMode(desc.reductionMode); FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createSamplerState(gfxDesc, mGfxSamplerState.writeRef())); } @@ -120,11 +120,6 @@ Sampler::~Sampler() mpDevice->releaseResource(mGfxSamplerState); } -ref Sampler::create(ref pDevice, const Desc& desc) -{ - return ref(new Sampler(pDevice, desc)); -} - NativeHandle Sampler::getNativeHandle() const { gfx::InteropHandle gfxNativeHandle = {}; @@ -152,6 +147,25 @@ void Sampler::breakStrongReferenceToDevice() FALCOR_SCRIPT_BINDING(Sampler) { - pybind11::class_>(m, "Sampler"); + FALCOR_SCRIPT_BINDING_DEPENDENCY(Types) + + pybind11::falcor_enum(m, "TextureFilteringMode"); + pybind11::falcor_enum(m, "TextureAddressingMode"); + pybind11::falcor_enum(m, "TextureReductionMode"); + + pybind11::class_> sampler(m, "Sampler"); + sampler.def_property_readonly("mag_filter", &Sampler::getMagFilter); + sampler.def_property_readonly("min_filter", &Sampler::getMinFilter); + sampler.def_property_readonly("mip_filter", &Sampler::getMipFilter); + sampler.def_property_readonly("max_anisotropy", &Sampler::getMaxAnisotropy); + sampler.def_property_readonly("min_lod", &Sampler::getMinLod); + sampler.def_property_readonly("max_lod", &Sampler::getMaxLod); + sampler.def_property_readonly("lod_bias", &Sampler::getLodBias); + sampler.def_property_readonly("comparison_func", &Sampler::getComparisonFunc); + sampler.def_property_readonly("reduction_mode", &Sampler::getReductionMode); + sampler.def_property_readonly("address_mode_u", &Sampler::getAddressModeU); + sampler.def_property_readonly("address_mode_v", &Sampler::getAddressModeV); + sampler.def_property_readonly("address_mode_w", &Sampler::getAddressModeW); + sampler.def_property_readonly("border_color", &Sampler::getBorderColor); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/Sampler.h b/Source/Falcor/Core/API/Sampler.h index ef2d4ed54..a09fc0334 100644 --- a/Source/Falcor/Core/API/Sampler.h +++ b/Source/Falcor/Core/API/Sampler.h @@ -27,7 +27,7 @@ **************************************************************************/ #pragma once #include "fwd.h" -#include "Common.h" +#include "Types.h" #include "Handles.h" #include "NativeHandle.h" #include "Core/Macros.h" @@ -37,96 +37,95 @@ namespace Falcor { + /** - * Abstract the API sampler state object + * Texture filtering modes. */ -class FALCOR_API Sampler : public Object +enum class TextureFilteringMode { - FALCOR_OBJECT(Sampler) -public: - /** - * Filter mode - */ - enum class Filter - { - Point, - Linear, - }; - - FALCOR_ENUM_INFO( - Filter, - { - {Filter::Point, "Point"}, - {Filter::Linear, "Linear"}, - } - ); + Point, + Linear, +}; - /** - * Addressing mode in case the texture coordinates are out of [0, 1] range - */ - enum class AddressMode +FALCOR_ENUM_INFO( + TextureFilteringMode, { - Wrap, ///< Wrap around - Mirror, ///< Wrap around and mirror on every integer junction - Clamp, ///< Clamp the normalized coordinates to [0, 1] - Border, ///< If out-of-bound, use the sampler's border color - MirrorOnce ///< Same as Mirror, but mirrors only once around 0 - }; + {TextureFilteringMode::Point, "Point"}, + {TextureFilteringMode::Linear, "Linear"}, + } +); +FALCOR_ENUM_REGISTER(TextureFilteringMode); - FALCOR_ENUM_INFO( - AddressMode, - { - {AddressMode::Wrap, "Wrap"}, - {AddressMode::Mirror, "Mirror"}, - {AddressMode::Clamp, "Clamp"}, - {AddressMode::Border, "Border"}, - {AddressMode::MirrorOnce, "MirrorOnce"}, - } - ); +/** + * Addressing mode in case the texture coordinates are out of [0, 1] range. + */ +enum class TextureAddressingMode +{ + Wrap, ///< Wrap around + Mirror, ///< Wrap around and mirror on every integer junction + Clamp, ///< Clamp the normalized coordinates to [0, 1] + Border, ///< If out-of-bound, use the sampler's border color + MirrorOnce ///< Same as Mirror, but mirrors only once around 0 +}; - /** - * Reduction mode - */ - enum class ReductionMode +FALCOR_ENUM_INFO( + TextureAddressingMode, { - Standard, - Comparison, - Min, - Max, - }; + {TextureAddressingMode::Wrap, "Wrap"}, + {TextureAddressingMode::Mirror, "Mirror"}, + {TextureAddressingMode::Clamp, "Clamp"}, + {TextureAddressingMode::Border, "Border"}, + {TextureAddressingMode::MirrorOnce, "MirrorOnce"}, + } +); +FALCOR_ENUM_REGISTER(TextureAddressingMode); - FALCOR_ENUM_INFO( - ReductionMode, - { - {ReductionMode::Standard, "Standard"}, - {ReductionMode::Comparison, "Comparison"}, - {ReductionMode::Min, "Min"}, - {ReductionMode::Max, "Max"}, - } - ); +/** + * Reduction modes. + */ +enum class TextureReductionMode +{ + Standard, + Comparison, + Min, + Max, +}; - /** - * Comparison mode for the sampler. - */ - using ComparisonMode = ComparisonFunc; +FALCOR_ENUM_INFO( + TextureReductionMode, + { + {TextureReductionMode::Standard, "Standard"}, + {TextureReductionMode::Comparison, "Comparison"}, + {TextureReductionMode::Min, "Min"}, + {TextureReductionMode::Max, "Max"}, + } +); +FALCOR_ENUM_REGISTER(TextureReductionMode); +/** + * Abstract the API sampler state object + */ +class FALCOR_API Sampler : public Object +{ + FALCOR_OBJECT(Sampler) +public: /** * Descriptor used to create a new Sampler object */ struct Desc { - Filter magFilter = Filter::Linear; - Filter minFilter = Filter::Linear; - Filter mipFilter = Filter::Linear; + TextureFilteringMode magFilter = TextureFilteringMode::Linear; + TextureFilteringMode minFilter = TextureFilteringMode::Linear; + TextureFilteringMode mipFilter = TextureFilteringMode::Linear; uint32_t maxAnisotropy = 1; float maxLod = 1000; float minLod = -1000; float lodBias = 0; - ComparisonMode comparisonMode = ComparisonMode::Disabled; - ReductionMode reductionMode = ReductionMode::Standard; - AddressMode addressModeU = AddressMode::Wrap; - AddressMode addressModeV = AddressMode::Wrap; - AddressMode addressModeW = AddressMode::Wrap; + ComparisonFunc comparisonFunc = ComparisonFunc::Disabled; + TextureReductionMode reductionMode = TextureReductionMode::Standard; + TextureAddressingMode addressModeU = TextureAddressingMode::Wrap; + TextureAddressingMode addressModeV = TextureAddressingMode::Wrap; + TextureAddressingMode addressModeW = TextureAddressingMode::Wrap; float4 borderColor = float4(0, 0, 0, 0); /** @@ -135,7 +134,7 @@ class FALCOR_API Sampler : public Object * @param[in] magFilter_ Filter mode in case of magnification. * @param[in] mipFilter_ Mip-level sampling mode */ - Desc& setFilterMode(Filter minFilter_, Filter magFilter_, Filter mipFilter_) + Desc& setFilterMode(TextureFilteringMode minFilter_, TextureFilteringMode magFilter_, TextureFilteringMode mipFilter_) { magFilter = magFilter_; minFilter = minFilter_; @@ -167,18 +166,18 @@ class FALCOR_API Sampler : public Object } /** - * Set the sampler comparison mode. + * Set the sampler comparison function. */ - Desc& setComparisonMode(ComparisonMode mode) + Desc& setComparisonFunc(ComparisonFunc func) { - comparisonMode = mode; + comparisonFunc = func; return *this; } /** * Set the sampler reduction mode. */ - Desc& setReductionMode(ReductionMode mode) + Desc& setReductionMode(TextureReductionMode mode) { reductionMode = mode; return *this; @@ -190,7 +189,7 @@ class FALCOR_API Sampler : public Object * @param[in] modeV Addressing mode for V texcoord channel * @param[in] modeW Addressing mode for W texcoord channel */ - Desc& setAddressingMode(AddressMode modeU, AddressMode modeV, AddressMode modeW) + Desc& setAddressingMode(TextureAddressingMode modeU, TextureAddressingMode modeV, TextureAddressingMode modeW) { addressModeU = modeU; addressModeV = modeV; @@ -214,7 +213,7 @@ class FALCOR_API Sampler : public Object { return magFilter == other.magFilter && minFilter == other.minFilter && mipFilter == other.mipFilter && maxAnisotropy == other.maxAnisotropy && maxLod == other.maxLod && minLod == other.minLod && lodBias == other.lodBias && - comparisonMode == other.comparisonMode && reductionMode == other.reductionMode && addressModeU == other.addressModeU && + comparisonFunc == other.comparisonFunc && reductionMode == other.reductionMode && addressModeU == other.addressModeU && addressModeV == other.addressModeV && addressModeW == other.addressModeW && all(borderColor == other.borderColor); } @@ -227,13 +226,6 @@ class FALCOR_API Sampler : public Object Sampler(ref pDevice, const Desc& desc); ~Sampler(); - /** - * Create a new sampler object. - * @param[in] desc Describes sampler settings. - * @return A new object, or throws an exception if creation failed. - */ - static ref create(ref pDevice, const Desc& desc); - /** * Get the sampler state. */ @@ -249,17 +241,17 @@ class FALCOR_API Sampler : public Object /** * Get the magnification filter */ - Filter getMagFilter() const { return mDesc.magFilter; } + TextureFilteringMode getMagFilter() const { return mDesc.magFilter; } /** * Get the minification filter */ - Filter getMinFilter() const { return mDesc.minFilter; } + TextureFilteringMode getMinFilter() const { return mDesc.minFilter; } /** * Get the mip-levels filter */ - Filter getMipFilter() const { return mDesc.mipFilter; } + TextureFilteringMode getMipFilter() const { return mDesc.mipFilter; } /** * Get the maximum anisotropy @@ -282,29 +274,29 @@ class FALCOR_API Sampler : public Object float getLodBias() const { return mDesc.lodBias; } /** - * Get the comparison mode + * Get the comparison function */ - ComparisonMode getComparisonMode() const { return mDesc.comparisonMode; } + ComparisonFunc getComparisonFunc() const { return mDesc.comparisonFunc; } /** * Get the reduction mode */ - ReductionMode getReductionMode() const { return mDesc.reductionMode; } + TextureReductionMode getReductionMode() const { return mDesc.reductionMode; } /** * Get the addressing mode for the U texcoord */ - AddressMode getAddressModeU() const { return mDesc.addressModeU; } + TextureAddressingMode getAddressModeU() const { return mDesc.addressModeU; } /** * Get the addressing mode for the V texcoord */ - AddressMode getAddressModeV() const { return mDesc.addressModeV; } + TextureAddressingMode getAddressModeV() const { return mDesc.addressModeV; } /** * Get the addressing mode for the W texcoord */ - AddressMode getAddressModeW() const { return mDesc.addressModeW; } + TextureAddressingMode getAddressModeW() const { return mDesc.addressModeW; } /** * Get the border color @@ -327,8 +319,4 @@ class FALCOR_API Sampler : public Object friend class Device; }; -FALCOR_ENUM_REGISTER(Sampler::Filter); -FALCOR_ENUM_REGISTER(Sampler::AddressMode); -FALCOR_ENUM_REGISTER(Sampler::ReductionMode); - } // namespace Falcor diff --git a/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.cpp b/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.cpp index 3efae6ea8..cf58fb3c3 100644 --- a/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.cpp +++ b/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.cpp @@ -44,6 +44,18 @@ ref createCbvDescriptor(ref pDevice, const D3D12_CON return handle; } +ref D3D12ConstantBufferView::create(ref pDevice, uint64_t gpuAddress, uint32_t byteSize) +{ + FALCOR_ASSERT(pDevice); + pDevice->requireD3D12(); + + D3D12_CONSTANT_BUFFER_VIEW_DESC desc = {}; + desc.BufferLocation = gpuAddress; + desc.SizeInBytes = byteSize; + + return ref(new D3D12ConstantBufferView(nullptr, createCbvDescriptor(pDevice, desc))); +} + ref D3D12ConstantBufferView::create(ref pDevice, ref pBuffer) { FALCOR_ASSERT(pDevice); diff --git a/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.h b/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.h index 22bbfdecd..f4300bc14 100644 --- a/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.h +++ b/Source/Falcor/Core/API/Shared/D3D12ConstantBufferView.h @@ -42,6 +42,7 @@ class FALCOR_API D3D12ConstantBufferView : public Object { FALCOR_OBJECT(D3D12ConstantBufferView) public: + static ref create(ref pDevice, uint64_t gpuAddress, uint32_t byteSize); static ref create(ref pDevice, ref pBuffer); static ref create(ref pDevice); diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.cpp b/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.cpp index 6b94e6fe8..f9c6f2814 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.cpp +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.cpp @@ -61,7 +61,7 @@ ref D3D12DescriptorHeap::create( desc.NumDescriptors = chunkCount * kDescPerChunk; if (FAILED(pD3D12Device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&pHeap->mApiHandle)))) { - throw RuntimeError("Failed to create descriptor heap"); + FALCOR_THROW("Failed to create descriptor heap"); } pHeap->mCpuHeapStart = pHeap->mApiHandle->GetCPUDescriptorHandleForHeapStart(); @@ -74,7 +74,7 @@ ref D3D12DescriptorHeap::create( D3D12DescriptorHeap::GpuHandle D3D12DescriptorHeap::getBaseGpuHandle() const { if (!mShaderVisible) - throw RuntimeError("getBaseGpuHandle() - heap must be shader visible."); + FALCOR_THROW("getBaseGpuHandle() - heap must be shader visible."); return mGpuHeapStart; } diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.h b/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.h index 0b3ff7ceb..d13fafbed 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.h +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorHeap.h @@ -27,7 +27,7 @@ **************************************************************************/ #pragma once #include "D3D12Handles.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/Object.h" #include "Core/API/fwd.h" #include diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.cpp b/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.cpp index 9b4e22ff0..4bd885839 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.cpp +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.cpp @@ -64,14 +64,14 @@ const D3D12DescriptorPool::ApiHandle& D3D12DescriptorPool::getApiHandle(uint32_t return mpApiData->pHeaps[heapIndex]->getApiHandle(); } -ref D3D12DescriptorPool::create(Device* pDevice, const Desc& desc, ref pFence) +ref D3D12DescriptorPool::create(Device* pDevice, const Desc& desc, ref pFence) { FALCOR_ASSERT(pDevice); pDevice->requireD3D12(); return ref(new D3D12DescriptorPool(pDevice, desc, pFence)); } -D3D12DescriptorPool::D3D12DescriptorPool(Device* pDevice, const Desc& desc, ref pFence) : mDesc(desc), mpFence(pFence) +D3D12DescriptorPool::D3D12DescriptorPool(Device* pDevice, const Desc& desc, ref pFence) : mDesc(desc), mpFence(pFence) { // Find out how many heaps we need static_assert(D3D12DescriptorPool::kTypeCount == 13, "Unexpected desc count, make sure all desc types are supported"); @@ -103,8 +103,8 @@ D3D12DescriptorPool::~D3D12DescriptorPool() = default; void D3D12DescriptorPool::executeDeferredReleases() { - uint64_t gpuVal = mpFence->getGpuValue(); - while (mpDeferredReleases.size() && mpDeferredReleases.top().fenceValue <= gpuVal) + uint64_t currentValue = mpFence->getCurrentValue(); + while (mpDeferredReleases.size() && mpDeferredReleases.top().fenceValue < currentValue) { mpDeferredReleases.pop(); } @@ -114,7 +114,7 @@ void D3D12DescriptorPool::releaseAllocation(std::shared_ptrgetCpuValue(); + d.fenceValue = mpFence->getSignaledValue(); mpDeferredReleases.push(d); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.h b/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.h index 0ce0f6c7c..296ed36c5 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.h +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorPool.h @@ -30,7 +30,7 @@ #include "Core/Macros.h" #include "Core/Object.h" #include "Core/API/ShaderResourceType.h" -#include "Core/API/GpuFence.h" +#include "Core/API/Fence.h" #include #include @@ -85,7 +85,7 @@ class FALCOR_API D3D12DescriptorPool : public Object * @param[in] pFence Fence object for synchronization. * @return A new object, or throws an exception if creation failed. */ - static ref create(Device* pDevice, const Desc& desc, ref pFence); + static ref create(Device* pDevice, const Desc& desc, ref pFence); uint32_t getDescCount(Type type) const { return mDesc.mDescCount[(uint32_t)type]; } uint32_t getTotalDescCount() const { return mDesc.mTotalDescCount; } @@ -96,11 +96,11 @@ class FALCOR_API D3D12DescriptorPool : public Object private: friend class D3D12DescriptorSet; - D3D12DescriptorPool(Device* pDevice, const Desc& desc, ref pFence); + D3D12DescriptorPool(Device* pDevice, const Desc& desc, ref pFence); void releaseAllocation(std::shared_ptr pData); Desc mDesc; std::shared_ptr mpApiData; - ref mpFence; + ref mpFence; struct DeferredRelease { diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.cpp b/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.cpp index 7cc61d248..fd2d73d59 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.cpp +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorSet.cpp @@ -54,7 +54,7 @@ D3D12DescriptorSet::CpuHandle D3D12DescriptorSet::getCpuHandle(uint32_t rangeInd D3D12DescriptorSet::GpuHandle D3D12DescriptorSet::getGpuHandle(uint32_t rangeIndex, uint32_t descInRange) const { - throw RuntimeError("Not supported."); + FALCOR_THROW("Not supported."); } void D3D12DescriptorSet::setCpuHandle(uint32_t rangeIndex, uint32_t descIndex, const CpuHandle& handle) @@ -67,7 +67,7 @@ void D3D12DescriptorSet::setCpuHandle(uint32_t rangeIndex, uint32_t descIndex, c void D3D12DescriptorSet::setSrv(uint32_t rangeIndex, uint32_t descIndex, const ShaderResourceView* pSrv) { auto type = getRange(rangeIndex).type; - checkInvariant( + FALCOR_CHECK( type == Type::TextureSrv || type == Type::RawBufferSrv || type == Type::TypedBufferSrv || type == Type::StructuredBufferSrv || type == Type::AccelerationStructureSrv, "Unexpected descriptor range type in setSrv()" @@ -78,7 +78,7 @@ void D3D12DescriptorSet::setSrv(uint32_t rangeIndex, uint32_t descIndex, const S void D3D12DescriptorSet::setUav(uint32_t rangeIndex, uint32_t descIndex, const UnorderedAccessView* pUav) { auto type = getRange(rangeIndex).type; - checkInvariant( + FALCOR_CHECK( type == Type::TextureUav || type == Type::RawBufferUav || type == Type::TypedBufferUav || type == Type::StructuredBufferUav, "Unexpected descriptor range type in setUav()" ); @@ -87,7 +87,7 @@ void D3D12DescriptorSet::setUav(uint32_t rangeIndex, uint32_t descIndex, const U void D3D12DescriptorSet::setSampler(uint32_t rangeIndex, uint32_t descIndex, const Sampler* pSampler) { - checkInvariant(getRange(rangeIndex).type == Type::Sampler, "Unexpected descriptor range type in setSampler()"); + FALCOR_CHECK(getRange(rangeIndex).type == Type::Sampler, "Unexpected descriptor range type in setSampler()"); setCpuHandle(rangeIndex, descIndex, pSampler->getNativeHandle().as()); } @@ -169,7 +169,7 @@ void D3D12DescriptorSet::bindForCompute(CopyContext* pCtx, const D3D12RootSignat void D3D12DescriptorSet::setCbv(uint32_t rangeIndex, uint32_t descIndex, D3D12ConstantBufferView* pView) { - checkInvariant(getRange(rangeIndex).type == Type::Cbv, "Unexpected descriptor range type in setCbv()"); + FALCOR_CHECK(getRange(rangeIndex).type == Type::Cbv, "Unexpected descriptor range type in setCbv()"); setCpuHandle(rangeIndex, descIndex, pView->getD3D12CpuHeapHandle()); } @@ -230,7 +230,7 @@ D3D12DescriptorSet::D3D12DescriptorSet(ref pDevice, refpAllocation == nullptr) - throw RuntimeError("Failed to create descriptor set"); + FALCOR_THROW("Failed to create descriptor set"); mpApiData->descriptorCount = count; } diff --git a/Source/Falcor/Core/API/Shared/D3D12DescriptorSetLayout.h b/Source/Falcor/Core/API/Shared/D3D12DescriptorSetLayout.h index 795b8f838..b5e96e966 100644 --- a/Source/Falcor/Core/API/Shared/D3D12DescriptorSetLayout.h +++ b/Source/Falcor/Core/API/Shared/D3D12DescriptorSetLayout.h @@ -28,7 +28,7 @@ #pragma once #include "Core/Macros.h" -#include "Core/API/ShaderType.h" +#include "Core/API/Types.h" #include "Core/API/ShaderResourceType.h" #include diff --git a/Source/Falcor/Core/API/Shared/D3D12RootSignature.cpp b/Source/Falcor/Core/API/Shared/D3D12RootSignature.cpp index e793359ed..e1bfbadad 100644 --- a/Source/Falcor/Core/API/Shared/D3D12RootSignature.cpp +++ b/Source/Falcor/Core/API/Shared/D3D12RootSignature.cpp @@ -167,7 +167,7 @@ void convertRootDescriptor(const D3D12RootSignature::RootDescriptorDesc& rootDes desc.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV; break; default: - throw RuntimeError("Unsupported root descriptor type. Only buffer SRV/UAVs supported."); + FALCOR_THROW("Unsupported root descriptor type. Only buffer SRV/UAVs supported."); } desc.Descriptor.RegisterSpace = rootDesc.spaceIndex; @@ -284,12 +284,12 @@ D3D12RootSignature::D3D12RootSignature(ref pDevice, const Desc& desc) : if (FAILED(hr)) { std::string msg = convertBlobToString(pErrorBlob.GetInterfacePtr()); - throw RuntimeError("Failed to create root signature: " + msg); + FALCOR_THROW("Failed to create root signature: " + msg); } if (mSizeInBytes > sizeof(uint32_t) * D3D12_MAX_ROOT_COST) { - throw RuntimeError( + FALCOR_THROW( "Root signature cost is too high. D3D12 root signatures are limited to 64 DWORDs, trying to create a signature with {} DWORDs.", mSizeInBytes / sizeof(uint32_t) ); diff --git a/Source/Falcor/Core/API/Shared/MockedD3D12StagingBuffer.cpp b/Source/Falcor/Core/API/Shared/MockedD3D12StagingBuffer.cpp index 4ea5034ad..64601c0b5 100644 --- a/Source/Falcor/Core/API/Shared/MockedD3D12StagingBuffer.cpp +++ b/Source/Falcor/Core/API/Shared/MockedD3D12StagingBuffer.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "MockedD3D12StagingBuffer.h" +#include "Core/API/Device.h" #include "Core/API/Buffer.h" #include "Core/API/NativeHandleTraits.h" @@ -34,9 +35,7 @@ namespace Falcor void MockedD3D12StagingBuffer::resize(ref pDevice, size_t size) { mData.resize(size); - mpGpuBuffer = Buffer::create( - pDevice, size, Resource::BindFlags::Constant | Resource::BindFlags::ShaderResource, Falcor::Buffer::CpuAccess::Write - ); + mpGpuBuffer = pDevice->createBuffer(size, ResourceBindFlags::Constant | ResourceBindFlags::ShaderResource, Falcor::MemoryType::Upload); } HRESULT __stdcall MockedD3D12StagingBuffer::QueryInterface(REFIID riid, void** ppvObject) diff --git a/Source/Falcor/Core/API/Swapchain.cpp b/Source/Falcor/Core/API/Swapchain.cpp index 6f9d6442a..bce95b882 100644 --- a/Source/Falcor/Core/API/Swapchain.cpp +++ b/Source/Falcor/Core/API/Swapchain.cpp @@ -37,10 +37,10 @@ Swapchain::Swapchain(ref pDevice, const Desc& desc, WindowHandle windowH { FALCOR_ASSERT(mpDevice); - FALCOR_CHECK_ARG_NE((uint32_t)desc.format, (uint32_t)ResourceFormat::Unknown); - FALCOR_CHECK_ARG_GT(desc.width, 0); - FALCOR_CHECK_ARG_GT(desc.height, 0); - FALCOR_CHECK_ARG_GT(desc.imageCount, 0); + FALCOR_CHECK(desc.format != ResourceFormat::Unknown, "Invalid format"); + FALCOR_CHECK(desc.width > 0, "Invalid width"); + FALCOR_CHECK(desc.height > 0, "Invalid height"); + FALCOR_CHECK(desc.imageCount > 0, "Invalid image count"); gfx::ISwapchain::Desc gfxDesc = {}; gfxDesc.format = getGFXFormat(desc.format); @@ -77,11 +77,11 @@ int Swapchain::acquireNextImage() void Swapchain::resize(uint32_t width, uint32_t height) { - FALCOR_CHECK_ARG_GT(width, 0); - FALCOR_CHECK_ARG_GT(height, 0); + FALCOR_CHECK(width > 0, "Invalid width"); + FALCOR_CHECK(height > 0, "Invalid height"); mImages.clear(); - mpDevice->flushAndSync(); + mpDevice->wait(); FALCOR_GFX_CALL(mGfxSwapchain->resize(width, height)); prepareImages(); } @@ -102,9 +102,18 @@ void Swapchain::prepareImages() { Slang::ComPtr resource; FALCOR_GFX_CALL(mGfxSwapchain->getImage(i, resource.writeRef())); - mImages.push_back(Texture::createFromResource( - mpDevice, resource, Texture::Type::Texture2D, mDesc.width, mDesc.height, 1, mDesc.format, 1, 1, 1, Resource::State::Undefined, - Texture::BindFlags::RenderTarget + mImages.push_back(mpDevice->createTextureFromResource( + resource, + Texture::Type::Texture2D, + mDesc.format, + mDesc.width, + mDesc.height, + 1, + 1, + 1, + 1, + ResourceBindFlags::RenderTarget, + Resource::State::Undefined )); } } diff --git a/Source/Falcor/Core/API/Swapchain.h b/Source/Falcor/Core/API/Swapchain.h index 5f44597ab..f3c7594d4 100644 --- a/Source/Falcor/Core/API/Swapchain.h +++ b/Source/Falcor/Core/API/Swapchain.h @@ -69,7 +69,7 @@ class FALCOR_API Swapchain : public Object /// Resizes the back buffers of this swapchain. All render target views and framebuffers /// referencing the back buffer images must be freed before calling this method. - /// Note: This method calls Device::flushAndSync(). + /// Note: This method calls Device::wait(). void resize(uint32_t width, uint32_t height); /// Check if the window is occluded. diff --git a/Source/Falcor/Core/API/Texture.cpp b/Source/Falcor/Core/API/Texture.cpp index a0cac0f59..708fa2a0a 100644 --- a/Source/Falcor/Core/API/Texture.cpp +++ b/Source/Falcor/Core/API/Texture.cpp @@ -31,18 +31,17 @@ #include "RenderContext.h" #include "GFXHelpers.h" #include "GFXAPI.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "PythonHelpers.h" +#include "Core/Error.h" #include "Core/ObjectPython.h" #include "Utils/Logger.h" #include "Utils/Threading.h" #include "Utils/Math/Common.h" #include "Utils/Image/ImageIO.h" #include "Utils/Scripting/ScriptBindings.h" +#include "Utils/Scripting/ndarray.h" #include "Core/Pass/FullScreenPass.h" -#include - #include namespace Falcor @@ -51,51 +50,184 @@ namespace { static constexpr bool kTopDown = true; // Memory layout when loading from file -Texture::BindFlags updateBindFlags( +gfx::IResource::Type getGfxResourceType(Texture::Type type) +{ + switch (type) + { + case Texture::Type::Texture1D: + return gfx::IResource::Type::Texture1D; + case Texture::Type::Texture2D: + case Texture::Type::Texture2DMultisample: + return gfx::IResource::Type::Texture2D; + case Texture::Type::TextureCube: + return gfx::IResource::Type::TextureCube; + case Texture::Type::Texture3D: + return gfx::IResource::Type::Texture3D; + default: + FALCOR_UNREACHABLE(); + return gfx::IResource::Type::Unknown; + } +} + +} // namespace + +Texture::Texture( ref pDevice, - Texture::BindFlags flags, - bool hasInitData, - uint32_t mipLevels, + Type type, ResourceFormat format, - const std::string& texType + uint32_t width, + uint32_t height, + uint32_t depth, + uint32_t arraySize, + uint32_t mipLevels, + uint32_t sampleCount, + ResourceBindFlags bindFlags, + const void* pInitData ) + : Resource(std::move(pDevice), type, bindFlags, 0) + , mFormat(format) + , mWidth(width) + , mHeight(height) + , mDepth(depth) + , mMipLevels(mipLevels) + , mArraySize(arraySize) + , mSampleCount(sampleCount) { - if ((mipLevels == Texture::kMaxPossible) && hasInitData) + FALCOR_ASSERT(mType != Type::Buffer); + FALCOR_ASSERT(mFormat != ResourceFormat::Unknown); + FALCOR_ASSERT(mWidth > 0 && mHeight > 0 && mDepth > 0); + switch (mType) { - flags |= Texture::BindFlags::RenderTarget; + case Resource::Type::Texture1D: + FALCOR_ASSERT(mHeight == 1 && mDepth == 1 && mSampleCount == 1); + break; + case Resource::Type::Texture2D: + FALCOR_ASSERT(mDepth == 1 && mSampleCount == 1); + break; + case Resource::Type::Texture2DMultisample: + FALCOR_ASSERT(mDepth == 1); + break; + case Resource::Type::Texture3D: + FALCOR_ASSERT(mSampleCount == 1); + break; + case Resource::Type::TextureCube: + FALCOR_ASSERT(mDepth == 1 && mSampleCount == 1); + break; + default: + FALCOR_UNREACHABLE(); + break; } - Texture::BindFlags supported = pDevice->getFormatBindFlags(format); - supported |= ResourceBindFlags::Shared; - if ((flags & supported) != flags) + FALCOR_ASSERT(mArraySize > 0 && mMipLevels > 0 && mSampleCount > 0); + + bool autoGenerateMips = pInitData && (mMipLevels == Texture::kMaxPossible); + + if (autoGenerateMips) + mBindFlags |= ResourceBindFlags::RenderTarget; + + if (mMipLevels == kMaxPossible) { - throw RuntimeError( - "Error when creating {} of format {}. The requested bind-flags are not supported. Requested = ({}), supported = ({}).", texType, - to_string(format), to_string(flags), to_string(supported) + uint32_t dims = width | height | depth; + mMipLevels = bitScanReverse(dims) + 1; + } + + mState.perSubresource.resize(mMipLevels * mArraySize, mState.global); + + ResourceBindFlags supported = mpDevice->getFormatBindFlags(mFormat); + supported |= ResourceBindFlags::Shared; + if ((mBindFlags & supported) != mBindFlags) + { + FALCOR_THROW( + "Error when creating {} of format {}. The requested bind-flags are not supported. Requested = ({}), supported = ({}).", + to_string(mType), + to_string(mFormat), + to_string(mBindFlags), + to_string(supported) ); - flags = flags & supported; } - return flags; + gfx::ITextureResource::Desc desc = {}; + + desc.type = getGfxResourceType(mType); + + // Default state and allowed states. + gfx::ResourceState defaultState; + getGFXResourceState(mBindFlags, defaultState, desc.allowedStates); + + // Always set texture to general(common) state upon creation. + desc.defaultState = gfx::ResourceState::General; + + desc.memoryType = gfx::MemoryType::DeviceLocal; + + desc.size.width = align_to(getFormatWidthCompressionRatio(mFormat), mWidth); + desc.size.height = align_to(getFormatHeightCompressionRatio(mFormat), mHeight); + desc.size.depth = mDepth; + + desc.arraySize = mType == Texture::Type::TextureCube ? mArraySize * 6 : mArraySize; + desc.numMipLevels = mMipLevels; + + desc.format = getGFXFormat(mFormat); // lookup can result in Unknown / unsupported format + + desc.sampleDesc.numSamples = mSampleCount; + desc.sampleDesc.quality = 0; + + // Clear value. + gfx::ClearValue clearValue; + if ((mBindFlags & (ResourceBindFlags::RenderTarget | ResourceBindFlags::DepthStencil)) != ResourceBindFlags::None) + { + if ((mBindFlags & ResourceBindFlags::DepthStencil) != ResourceBindFlags::None) + { + clearValue.depthStencil.depth = 1.0f; + } + desc.optimalClearValue = &clearValue; + } + + // Shared resource. + if (is_set(mBindFlags, ResourceBindFlags::Shared)) + { + desc.isShared = true; + } + + // Validate description. + FALCOR_ASSERT(desc.size.width > 0 && desc.size.height > 0); + FALCOR_ASSERT(desc.numMipLevels > 0 && desc.size.depth > 0 && desc.arraySize > 0 && desc.sampleDesc.numSamples > 0); + + // Create & upload resource. + { + // WARNING: This is a hack to allow parallel texture loading in TextureManager. + std::lock_guard lock(mpDevice->getGlobalGfxMutex()); + + FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createTextureResource(desc, nullptr, mGfxTextureResource.writeRef())); + FALCOR_ASSERT(mGfxTextureResource); + + if (pInitData) + { + // Prevent the texture from being destroyed while uploading the data. + incRef(); + uploadInitData(mpDevice->getRenderContext(), pInitData, autoGenerateMips); + decRef(false); + } + } } -} // namespace -ref Texture::createFromResource( +Texture::Texture( ref pDevice, gfx::ITextureResource* pResource, Type type, + ResourceFormat format, uint32_t width, uint32_t height, uint32_t depth, - ResourceFormat format, - uint32_t sampleCount, uint32_t arraySize, uint32_t mipLevels, - State initState, - BindFlags bindFlags + uint32_t sampleCount, + ResourceBindFlags bindFlags, + Resource::State initState ) + : Texture(std::move(pDevice), type, format, width, height, depth, arraySize, mipLevels, sampleCount, bindFlags, nullptr) { FALCOR_ASSERT(pResource); + switch (type) { case Resource::Type::Texture1D: @@ -117,106 +249,22 @@ ref Texture::createFromResource( FALCOR_UNREACHABLE(); break; } - ref pTexture = - ref(new Texture(pDevice, width, height, depth, arraySize, mipLevels, sampleCount, format, type, bindFlags)); - pTexture->mGfxTextureResource = pResource; - pTexture->mState.global = initState; - pTexture->mState.isGlobal = true; - return pTexture; -} -ref Texture::create1D( - ref pDevice, - uint32_t width, - ResourceFormat format, - uint32_t arraySize, - uint32_t mipLevels, - const void* pData, - BindFlags bindFlags -) -{ - bindFlags = updateBindFlags(pDevice, bindFlags, pData != nullptr, mipLevels, format, "Texture1D"); - ref pTexture = ref(new Texture(pDevice, width, 1, 1, arraySize, mipLevels, 1, format, Type::Texture1D, bindFlags)); - pTexture->apiInit(pData, (mipLevels == kMaxPossible)); - return pTexture; -} - -ref Texture::create2D( - ref pDevice, - uint32_t width, - uint32_t height, - ResourceFormat format, - uint32_t arraySize, - uint32_t mipLevels, - const void* pData, - BindFlags bindFlags -) -{ - bindFlags = updateBindFlags(pDevice, bindFlags, pData != nullptr, mipLevels, format, "Texture2D"); - ref pTexture = - ref(new Texture(pDevice, width, height, 1, arraySize, mipLevels, 1, format, Type::Texture2D, bindFlags)); - pTexture->apiInit(pData, (mipLevels == kMaxPossible)); - return pTexture; -} - -ref Texture::create3D( - ref pDevice, - uint32_t width, - uint32_t height, - uint32_t depth, - ResourceFormat format, - uint32_t mipLevels, - const void* pData, - BindFlags bindFlags, - bool isSparse -) -{ - bindFlags = updateBindFlags(pDevice, bindFlags, pData != nullptr, mipLevels, format, "Texture3D"); - ref pTexture = ref(new Texture(pDevice, width, height, depth, 1, mipLevels, 1, format, Type::Texture3D, bindFlags)); - pTexture->apiInit(pData, (mipLevels == kMaxPossible)); - return pTexture; -} - -ref Texture::createCube( - ref pDevice, - uint32_t width, - uint32_t height, - ResourceFormat format, - uint32_t arraySize, - uint32_t mipLevels, - const void* pData, - BindFlags bindFlags -) -{ - bindFlags = updateBindFlags(pDevice, bindFlags, pData != nullptr, mipLevels, format, "TextureCube"); - ref pTexture = - ref(new Texture(pDevice, width, height, 1, arraySize, mipLevels, 1, format, Type::TextureCube, bindFlags)); - pTexture->apiInit(pData, (mipLevels == kMaxPossible)); - return pTexture; + mGfxTextureResource = pResource; + mState.global = initState; + mState.isGlobal = true; } -ref Texture::create2DMS( - ref pDevice, - uint32_t width, - uint32_t height, - ResourceFormat format, - uint32_t sampleCount, - uint32_t arraySize, - BindFlags bindFlags -) +Texture::~Texture() { - bindFlags = updateBindFlags(pDevice, bindFlags, false, 1, format, "Texture2DMultisample"); - ref pTexture = - ref(new Texture(pDevice, width, height, 1, arraySize, 1, sampleCount, format, Type::Texture2DMultisample, bindFlags)); - pTexture->apiInit(nullptr, false); - return pTexture; + mpDevice->releaseResource(mGfxTextureResource); } ref Texture::createMippedFromFiles( ref pDevice, fstd::span paths, bool loadAsSrgb, - Texture::BindFlags bindFlags + ResourceBindFlags bindFlags ) { std::vector mips; @@ -252,8 +300,13 @@ ref Texture::createMippedFromFiles( std::max(mips.back()->getHeight() / 2, 1u) != pBitmap->getHeight()) { logWarning( - "Error loading mip {} from file {}. Image resolution must decrease by half. ({}, {}) != ({}, {})/2", mips.size(), path, - pBitmap->getWidth(), pBitmap->getHeight(), mips.back()->getWidth(), mips.back()->getHeight() + "Error loading mip {} from file {}. Image resolution must decrease by half. ({}, {}) != ({}, {})/2", + mips.size(), + path, + pBitmap->getWidth(), + pBitmap->getHeight(), + mips.back()->getWidth(), + mips.back()->getHeight() ); break; } @@ -284,7 +337,7 @@ ref Texture::createMippedFromFiles( // Create mip mapped latent texture pTex = - Texture::create2D(pDevice, mips[0]->getWidth(), mips[0]->getHeight(), texFormat, 1, mips.size(), combinedData.get(), bindFlags); + pDevice->createTexture2D(mips[0]->getWidth(), mips[0]->getHeight(), texFormat, 1, mips.size(), combinedData.get(), bindFlags); } if (pTex != nullptr) @@ -293,8 +346,12 @@ ref Texture::createMippedFromFiles( // Log debug info. std::string str = fmt::format( - "Loaded texture: size={}x{} mips={} format={} path={}", pTex->getWidth(), pTex->getHeight(), pTex->getMipCount(), - to_string(pTex->getFormat()), fullPathMip0 + "Loaded texture: size={}x{} mips={} format={} path={}", + pTex->getWidth(), + pTex->getHeight(), + pTex->getMipCount(), + to_string(pTex->getFormat()), + fullPathMip0 ); logDebug(str); } @@ -307,31 +364,30 @@ ref Texture::createFromFile( const std::filesystem::path& path, bool generateMipLevels, bool loadAsSrgb, - Texture::BindFlags bindFlags + ResourceBindFlags bindFlags ) { - std::filesystem::path fullPath; - if (!findFileInDataDirectories(path, fullPath)) + if (!std::filesystem::exists(path)) { - logWarning("Error when loading image file. Can't find image file '{}'.", path); + logWarning("Error when loading image file. File '{}' does not exist.", path); return nullptr; } ref pTex; - if (hasExtension(fullPath, "dds")) + if (hasExtension(path, "dds")) { try { - pTex = ImageIO::loadTextureFromDDS(pDevice, fullPath, loadAsSrgb); + pTex = ImageIO::loadTextureFromDDS(pDevice, path, loadAsSrgb); } catch (const std::exception& e) { - logWarning("Error loading '{}': {}", fullPath, e.what()); + logWarning("Error loading '{}': {}", path, e.what()); } } else { - Bitmap::UniqueConstPtr pBitmap = Bitmap::createFromFile(fullPath, kTopDown); + Bitmap::UniqueConstPtr pBitmap = Bitmap::createFromFile(path, kTopDown); if (pBitmap) { ResourceFormat texFormat = pBitmap->getFormat(); @@ -340,21 +396,30 @@ ref Texture::createFromFile( texFormat = linearToSrgbFormat(texFormat); } - pTex = Texture::create2D( - pDevice, pBitmap->getWidth(), pBitmap->getHeight(), texFormat, 1, generateMipLevels ? Texture::kMaxPossible : 1, - pBitmap->getData(), bindFlags + pTex = pDevice->createTexture2D( + pBitmap->getWidth(), + pBitmap->getHeight(), + texFormat, + 1, + generateMipLevels ? Texture::kMaxPossible : 1, + pBitmap->getData(), + bindFlags ); } } if (pTex != nullptr) { - pTex->setSourcePath(fullPath); + pTex->setSourcePath(path); // Log debug info. std::string str = fmt::format( - "Loaded texture: size={}x{} mips={} format={} path={}", pTex->getWidth(), pTex->getHeight(), pTex->getMipCount(), - to_string(pTex->getFormat()), fullPath + "Loaded texture: size={}x{} mips={} format={} path={}", + pTex->getWidth(), + pTex->getHeight(), + pTex->getMipCount(), + to_string(pTex->getFormat()), + path ); logDebug(str); } @@ -362,39 +427,6 @@ ref Texture::createFromFile( return pTex; } -Texture::Texture( - ref pDevice, - uint32_t width, - uint32_t height, - uint32_t depth, - uint32_t arraySize, - uint32_t mipLevels, - uint32_t sampleCount, - ResourceFormat format, - Type type, - BindFlags bindFlags -) - : Resource(pDevice, type, bindFlags, 0) - , mWidth(width) - , mHeight(height) - , mDepth(depth) - , mMipLevels(mipLevels) - , mSampleCount(sampleCount) - , mArraySize(arraySize) - , mFormat(format) -{ - FALCOR_ASSERT(width > 0 && height > 0 && depth > 0); - FALCOR_ASSERT(arraySize > 0 && mipLevels > 0 && sampleCount > 0); - FALCOR_ASSERT(format != ResourceFormat::Unknown); - - if (mMipLevels == kMaxPossible) - { - uint32_t dims = width | height | depth; - mMipLevels = bitScanReverse(dims) + 1; - } - mState.perSubresource.resize(mMipLevels * mArraySize, mState.global); -} - gfx::IResource* Texture::getGfxResource() const { return mGfxTextureResource; @@ -505,6 +537,48 @@ ref Texture::getSRV(uint32_t mostDetailedMip, uint32_t mipCo return findViewCommon(this, mostDetailedMip, mipCount, firstArraySlice, arraySize, mSrvs, createFunc); } +Texture::SubresourceLayout Texture::getSubresourceLayout(uint32_t subresource) const +{ + FALCOR_CHECK(subresource < getSubresourceCount(), "subresource out of range"); + + gfx::ITextureResource* gfxTexture = getGfxTextureResource(); + gfx::FormatInfo gfxFormatInfo; + FALCOR_GFX_CALL(gfx::gfxGetFormatInfo(gfxTexture->getDesc()->format, &gfxFormatInfo)); + + SubresourceLayout layout; + uint32_t mipLevel = getSubresourceMipLevel(subresource); + layout.rowSize = div_round_up(getWidth(mipLevel), uint32_t(gfxFormatInfo.blockWidth)) * gfxFormatInfo.blockSizeInBytes; + layout.rowSizeAligned = align_to(mpDevice->getTextureRowAlignment(), layout.rowSize); + layout.rowCount = div_round_up(getHeight(mipLevel), uint32_t(gfxFormatInfo.blockHeight)); + layout.depth = getDepth(); + + return layout; +} + +void Texture::setSubresourceBlob(uint32_t subresource, const void* pData, size_t size) +{ + FALCOR_CHECK(subresource < getSubresourceCount(), "'subresource' ({}) is out of range ({})", subresource, getSubresourceCount()); + SubresourceLayout layout = getSubresourceLayout(subresource); + FALCOR_CHECK( + size == layout.getTotalByteSize(), "'size' ({}) does not match the subresource size ({})", size, layout.getTotalByteSize() + ); + + mpDevice->getRenderContext()->updateSubresourceData(this, subresource, pData); +} + +void Texture::getSubresourceBlob(uint32_t subresource, void* pData, size_t size) const +{ + FALCOR_CHECK(subresource < getSubresourceCount(), "'subresource' ({}) is out of range ({})", subresource, getSubresourceCount()); + SubresourceLayout layout = getSubresourceLayout(subresource); + FALCOR_CHECK( + size == layout.getTotalByteSize(), "'size' ({}) does not match the subresource size ({})", size, layout.getTotalByteSize() + ); + + auto data = mpDevice->getRenderContext()->readTextureSubresource(this, subresource); + FALCOR_ASSERT(data.size() == size); + std::memcpy(pData, data.data(), data.size()); +} + void Texture::captureToFile( uint32_t mipLevel, uint32_t arraySlice, @@ -516,11 +590,11 @@ void Texture::captureToFile( { if (format == Bitmap::FileFormat::DdsFile) { - throw RuntimeError("Texture::captureToFile does not yet support saving to DDS."); + FALCOR_THROW("Texture::captureToFile does not yet support saving to DDS."); } if (mType != Type::Texture2D) - throw RuntimeError("Texture::captureToFile only supported for 2D textures."); + FALCOR_THROW("Texture::captureToFile only supported for 2D textures."); RenderContext* pContext = mpDevice->getRenderContext(); @@ -532,8 +606,13 @@ void Texture::captureToFile( if (type == FormatType::Float && channels < 3) { - ref pOther = Texture::create2D( - mpDevice, getWidth(mipLevel), getHeight(mipLevel), ResourceFormat::RGBA32Float, 1, 1, nullptr, + ref pOther = mpDevice->createTexture2D( + getWidth(mipLevel), + getHeight(mipLevel), + ResourceFormat::RGBA32Float, + 1, + 1, + nullptr, ResourceBindFlags::RenderTarget | ResourceBindFlags::ShaderResource ); pContext->blit(getSRV(mipLevel, 1, arraySlice, 1), pOther->getRTV(0, 0, 1)); @@ -600,18 +679,24 @@ void Texture::generateMips(RenderContext* pContext, bool minMaxMips) auto rtv = getRTV(m + 1, a, 1); if (!minMaxMips) { - pContext->blit(srv, rtv, RenderContext::kMaxRect, RenderContext::kMaxRect, Sampler::Filter::Linear); + pContext->blit(srv, rtv, RenderContext::kMaxRect, RenderContext::kMaxRect, TextureFilteringMode::Linear); } else { - const Sampler::ReductionMode redModes[] = { - Sampler::ReductionMode::Standard, Sampler::ReductionMode::Min, Sampler::ReductionMode::Max, - Sampler::ReductionMode::Standard}; + const TextureReductionMode redModes[] = { + TextureReductionMode::Standard, + TextureReductionMode::Min, + TextureReductionMode::Max, + TextureReductionMode::Standard, + }; const float4 componentsTransform[] = { - float4(1.0f, 0.0f, 0.0f, 0.0f), float4(0.0f, 1.0f, 0.0f, 0.0f), float4(0.0f, 0.0f, 1.0f, 0.0f), - float4(0.0f, 0.0f, 0.0f, 1.0f)}; + float4(1.0f, 0.0f, 0.0f, 0.0f), + float4(0.0f, 1.0f, 0.0f, 0.0f), + float4(0.0f, 0.0f, 1.0f, 0.0f), + float4(0.0f, 0.0f, 0.0f, 1.0f), + }; pContext->blit( - srv, rtv, RenderContext::kMaxRect, RenderContext::kMaxRect, Sampler::Filter::Linear, redModes, componentsTransform + srv, rtv, RenderContext::kMaxRect, RenderContext::kMaxRect, TextureFilteringMode::Linear, redModes, componentsTransform ); } } @@ -648,25 +733,6 @@ bool Texture::compareDesc(const Texture* pOther) const mIsSparse == pOther->mIsSparse && all(mSparsePageRes == pOther->mSparsePageRes); } -gfx::IResource::Type getResourceType(Texture::Type type) -{ - switch (type) - { - case Texture::Type::Texture1D: - return gfx::IResource::Type::Texture1D; - case Texture::Type::Texture2D: - case Texture::Type::Texture2DMultisample: - return gfx::IResource::Type::Texture2D; - case Texture::Type::TextureCube: - return gfx::IResource::Type::TextureCube; - case Texture::Type::Texture3D: - return gfx::IResource::Type::Texture3D; - default: - FALCOR_UNREACHABLE(); - return gfx::IResource::Type::Unknown; - } -} - uint64_t Texture::getTextureSizeInBytes() const { // get allocation info for resource description @@ -680,181 +746,80 @@ uint64_t Texture::getTextureSizeInBytes() const return outSizeBytes; } -void Texture::apiInit(const void* pData, bool autoGenMips) -{ - // WARNING: This is a hack to allow parallel texture loading in TextureManager. - std::lock_guard lock(mpDevice->getGlobalGfxMutex()); - - // create resource description - gfx::ITextureResource::Desc desc = {}; - - // base description - - // type - desc.type = getResourceType(mType); // same as resource dimension in D3D12 - - // default state and allowed states - gfx::ResourceState defaultState; - getGFXResourceState(mBindFlags, defaultState, desc.allowedStates); - - // Always set texture to general(common) state upon creation. - desc.defaultState = gfx::ResourceState::General; - - desc.memoryType = gfx::MemoryType::DeviceLocal; - // texture resource specific description attributes - - // size - desc.size.width = align_to(getFormatWidthCompressionRatio(mFormat), mWidth); - desc.size.height = align_to(getFormatHeightCompressionRatio(mFormat), mHeight); - desc.size.depth = mDepth; // relevant for Texture3D - - // array size - if (mType == Texture::Type::TextureCube) - { - desc.arraySize = mArraySize * 6; - } - else - { - desc.arraySize = mArraySize; - } - - // mip map levels - desc.numMipLevels = mMipLevels; - - // format - desc.format = getGFXFormat(mFormat); // lookup can result in Unknown / unsupported format - - // sample description - desc.sampleDesc.numSamples = mSampleCount; - desc.sampleDesc.quality = 0; - - // clear value - gfx::ClearValue clearValue; - if ((mBindFlags & (Texture::BindFlags::RenderTarget | Texture::BindFlags::DepthStencil)) != Texture::BindFlags::None) - { - if ((mBindFlags & Texture::BindFlags::DepthStencil) != Texture::BindFlags::None) - { - clearValue.depthStencil.depth = 1.0f; - } - desc.optimalClearValue = &clearValue; - } - - // shared resource - if (is_set(mBindFlags, Resource::BindFlags::Shared)) - { - desc.isShared = true; - } - - // validate description - FALCOR_ASSERT(desc.size.width > 0 && desc.size.height > 0); - FALCOR_ASSERT(desc.numMipLevels > 0 && desc.size.depth > 0 && desc.arraySize > 0 && desc.sampleDesc.numSamples > 0); - - // create resource - FALCOR_GFX_CALL(mpDevice->getGfxDevice()->createTextureResource(desc, nullptr, mGfxTextureResource.writeRef())); - FALCOR_ASSERT(mGfxTextureResource); - - // upload init data through texture class - if (pData) - { - uploadInitData(mpDevice->getRenderContext(), pData, autoGenMips); - } -} - -Texture::~Texture() -{ - mpDevice->releaseResource(mGfxTextureResource); -} - /** * Python binding wrapper for returning the content of a texture as a numpy array. */ -pybind11::array pyTextureGetImage(const Texture& texture, uint32_t mipLevel = 0, uint32_t arraySlice = 0) +inline pybind11::ndarray texture_to_numpy(const Texture& self, uint32_t mip_level, uint32_t array_slice) { - checkArgument( - mipLevel < texture.getMipCount(), "'mipLevel' ({}) is out of bounds. Only {} level(s) available.", mipLevel, texture.getMipCount() + FALCOR_CHECK( + mip_level < self.getMipCount(), "'mip_level' ({}) is out of bounds. Only {} level(s) available.", mip_level, self.getMipCount() ); - checkArgument( - arraySlice < texture.getArraySize(), "'arraySlice' ({}) is out of bounds. Only {} slice(s) available.", arraySlice, - texture.getArraySize() + FALCOR_CHECK( + array_slice < self.getArraySize(), + "'array_slice' ({}) is out of bounds. Only {} slice(s) available.", + array_slice, + self.getArraySize() ); - checkArgument(texture.getSampleCount() == 1, "Texture is multi-sampled."); - ResourceFormat format = texture.getFormat(); - checkArgument(isCompressedFormat(format) == false, "Texture uses a compressed format."); - checkArgument(isStencilFormat(format) == false, "Texture uses a stencil format."); - - FormatType formatType = getFormatType(format); - checkArgument(formatType != FormatType::Unknown, "Texture uses an unknown pixel format."); - - uint32_t channelCount = getFormatChannelCount(format); - checkArgument(channelCount > 0, "Texture has zero channels."); - - // Make sure all channels have the same number of bits. - uint32_t channelBits = getNumChannelBits(format, 0); - for (uint32_t i = 1; i < channelCount; ++i) - checkArgument(getNumChannelBits(format, i) == channelBits, "Texture uses different bit depths per channel."); - checkArgument( - channelBits == 8 || channelBits == 16 || channelBits == 32, "Texture uses bit depth {}. Only 8, 16 and 32 are supported.", - channelBits - ); - - // Generate numpy dtype. - std::string dtype; - switch (formatType) + // Get image dimensions. + uint32_t width = self.getWidth(mip_level); + uint32_t height = self.getHeight(mip_level); + uint32_t depth = self.getDepth(mip_level); + + uint32_t subresource = self.getSubresourceIndex(array_slice, mip_level); + Texture::SubresourceLayout layout = self.getSubresourceLayout(subresource); + + size_t subresourceSize = layout.getTotalByteSize(); + void* cpuData = new uint8_t[subresourceSize]; + self.getSubresourceBlob(subresource, cpuData, subresourceSize); + + pybind11::capsule owner(cpuData, [](void* p) noexcept { delete[] reinterpret_cast(p); }); + + if (auto dtype = resourceFormatToDtype(self.getFormat())) + { + uint32_t channelCount = getFormatChannelCount(self.getFormat()); + std::vector shape; + if (depth > 1) + shape.push_back(depth); + if (height > 1) + shape.push_back(height); + shape.push_back(width); + if (channelCount > 1) + shape.push_back(channelCount); + return pybind11::ndarray( + cpuData, shape.size(), shape.data(), owner, nullptr, *dtype, pybind11::device::cpu::value + ); + } + else { - case FormatType::Float: - dtype = "float"; - break; - case FormatType::Sint: - case FormatType::Snorm: - dtype = "int"; - break; - case FormatType::Uint: - case FormatType::Unorm: - case FormatType::UnormSrgb: - dtype = "uint"; - break; + pybind11::size_t shape[1] = {subresourceSize}; + return pybind11::ndarray( + cpuData, 1, shape, owner, nullptr, pybind11::dtype(), pybind11::device::cpu::value + ); } - dtype += std::to_string(channelBits); - - // Get image dimensions. - uint32_t width = texture.getWidth(mipLevel); - uint32_t height = texture.getHeight(mipLevel); - uint32_t depth = texture.getDepth(mipLevel); - - // Generate numpy array shape. - std::vector shape{width, channelCount}; - if (height > 1) - shape.insert(shape.begin(), height); - if (depth > 1) - shape.insert(shape.begin(), depth); - - pybind11::array result(pybind11::dtype(dtype), shape); - // TODO: We should add support for writing directly to a prepared buffer. - uint32_t subresource = texture.getSubresourceIndex(arraySlice, mipLevel); - auto data = texture.getDevice()->getRenderContext()->readTextureSubresource(&texture, mipLevel); - auto request = result.request(); - FALCOR_ASSERT_EQ(data.size(), request.size * request.itemsize); - std::memcpy(request.ptr, data.data(), data.size()); - - return result; } -/** - * Python binding wrapper for returning the content of texture as raw memory. - */ -std::vector pyTextureGetData(const Texture& texture, uint32_t mipLevel = 0, uint32_t arraySlice = 0) +inline void texture_from_numpy(Texture& self, pybind11::ndarray data, uint32_t mip_level, uint32_t array_slice) { - checkArgument( - mipLevel < texture.getMipCount(), "'mipLevel' ({}) is out of bounds. Only {} level(s) available.", mipLevel, texture.getMipCount() + FALCOR_CHECK( + mip_level < self.getMipCount(), "'mip_level' ({}) is out of bounds. Only {} level(s) available.", mip_level, self.getMipCount() ); - checkArgument( - arraySlice < texture.getArraySize(), "'arraySlice' ({}) is out of bounds. Only {} slice(s) available.", arraySlice, - texture.getArraySize() + FALCOR_CHECK( + array_slice < self.getArraySize(), + "'array_slice' ({}) is out of bounds. Only {} slice(s) available.", + array_slice, + self.getArraySize() ); + FALCOR_CHECK(isNdarrayContiguous(data), "numpy array is not contiguous"); + + uint32_t subresource = self.getSubresourceIndex(array_slice, mip_level); + Texture::SubresourceLayout layout = self.getSubresourceLayout(subresource); - uint32_t subresource = texture.getSubresourceIndex(arraySlice, mipLevel); - return texture.getDevice()->getRenderContext()->readTextureSubresource(&texture, subresource); + size_t subresourceSize = layout.getTotalByteSize(); + size_t dataSize = getNdarrayByteSize(data); + FALCOR_CHECK(dataSize == subresourceSize, "numpy array is doesn't match the subresource size ({} != {})", dataSize, subresourceSize); + + self.setSubresourceBlob(subresource, data.data(), dataSize); } FALCOR_SCRIPT_BINDING(Texture) @@ -864,20 +829,15 @@ FALCOR_SCRIPT_BINDING(Texture) FALCOR_SCRIPT_BINDING_DEPENDENCY(Resource) pybind11::class_> texture(m, "Texture"); + texture.def_property_readonly("format", &Texture::getFormat); texture.def_property_readonly("width", [](const Texture& texture) { return texture.getWidth(); }); texture.def_property_readonly("height", [](const Texture& texture) { return texture.getHeight(); }); texture.def_property_readonly("depth", [](const Texture& texture) { return texture.getDepth(); }); texture.def_property_readonly("mip_count", &Texture::getMipCount); texture.def_property_readonly("array_size", &Texture::getArraySize); - texture.def_property_readonly("samples", &Texture::getSampleCount); - texture.def_property_readonly("format", &Texture::getFormat); - - texture.def("get_image", pyTextureGetImage, "mip_level"_a = 0, "array_slice"_a = 0); - texture.def("get_data", pyTextureGetData, "mip_level"_a = 0, "array_slice"_a = 0); + texture.def_property_readonly("sample_count", &Texture::getSampleCount); - // PYTHONDEPRECATED BEGIN - texture.def("getImage", pyTextureGetImage, "mipLevel"_a = 0, "arraySlice"_a = 0); - texture.def("getData", pyTextureGetData, "mipLevel"_a = 0, "arraySlice"_a = 0); - // PYTHONDEPRECATED END + texture.def("to_numpy", texture_to_numpy, "mip_level"_a = 0, "array_slice"_a = 0); + texture.def("from_numpy", texture_from_numpy, "data"_a, "mip_level"_a = 0, "array_slice"_a = 0); } } // namespace Falcor diff --git a/Source/Falcor/Core/API/Texture.h b/Source/Falcor/Core/API/Texture.h index 2bb1452f4..29f3e347d 100644 --- a/Source/Falcor/Core/API/Texture.h +++ b/Source/Falcor/Core/API/Texture.h @@ -48,6 +48,53 @@ class FALCOR_API Texture : public Resource { FALCOR_OBJECT(Texture) public: + struct SubresourceLayout + { + /// Size of a single row in bytes (unaligned). + size_t rowSize; + /// Size of a single row in bytes (aligned to device texture alignment). + size_t rowSizeAligned; + /// Number of rows. + size_t rowCount; + /// Number of depth slices. + size_t depth; + + /// Get the total size of the subresource in bytes (unaligned). + size_t getTotalByteSize() const { return rowSize * rowCount * depth; } + + /// Get the total size of the subresource in bytes (aligned to device texture alignment). + size_t getTotalByteSizeAligned() const { return rowSizeAligned * rowCount * depth; } + }; + + Texture( + ref pDevice, + Type type, + ResourceFormat format, + uint32_t width, + uint32_t height, + uint32_t depth, + uint32_t arraySize, + uint32_t mipLevels, + uint32_t sampleCount, + ResourceBindFlags bindFlags, + const void* pInitData + ); + + Texture( + ref pDevice, + gfx::ITextureResource* pResource, + Type type, + ResourceFormat format, + uint32_t width, + uint32_t height, + uint32_t depth, + uint32_t arraySize, + uint32_t mipLevels, + uint32_t sampleCount, + ResourceBindFlags bindFlags, + Resource::State initState + ); + ~Texture(); /** @@ -105,151 +152,14 @@ class FALCOR_API Texture : public Resource uint32_t getSubresourceIndex(uint32_t arraySlice, uint32_t mipLevel) const { return mipLevel + arraySlice * mMipLevels; } /** - * Get the resource format - */ - ResourceFormat getFormat() const { return mFormat; } - - /** - * Create a new texture from an resource. - * @param[in] pResource Already allocated resource. - * @param[in] type The type of texture. - * @param[in] width The width of the texture. - * @param[in] height The height of the texture. - * @param[in] depth The depth of the texture. - * @param[in] format The format of the texture. - * @param[in] sampleCount The sample count of the texture. - * @param[in] arraySize The array size of the texture. - * @param[in] mipLevels The number of mip levels. - * @param[in] initState The initial resource state. - * @param[in] bindFlags Texture bind flags. Flags must match the bind flags of the original resource. - * @return A pointer to a new texture, or throws an exception if creation failed. - */ - static ref createFromResource( - ref pDevice, - gfx::ITextureResource* pResource, - Type type, - uint32_t width, - uint32_t height, - uint32_t depth, - ResourceFormat format, - uint32_t sampleCount, - uint32_t arraySize, - uint32_t mipLevels, - State initState, - BindFlags bindFlags - ); - - /** - * Create a 1D texture. - * @param[in] width The width of the texture. - * @param[in] format The format of the texture. - * @param[in] arraySize The array size of the texture. - * @param[in] mipLevels If equal to kMaxPossible then an entire mip chain will be generated from mip level 0. If any other value is - * given then the data for at least that number of miplevels must be provided. - * @param[in] pInitData If different than nullptr, pointer to a buffer containing data to initialize the texture with. - * @param[in] bindFlags The requested bind flags for the resource. - * @return A pointer to a new texture, or throws an exception if creation failed. - */ - static ref create1D( - ref pDevice, - uint32_t width, - ResourceFormat format, - uint32_t arraySize = 1, - uint32_t mipLevels = kMaxPossible, - const void* pInitData = nullptr, - BindFlags bindFlags = BindFlags::ShaderResource - ); - - /** - * Create a 2D texture. - * @param[in] width The width of the texture. - * @param[in] height The height of the texture. - * @param[in] format The format of the texture. - * @param[in] arraySize The array size of the texture. - * @param[in] mipLevels If equal to kMaxPossible then an entire mip chain will be generated from mip level 0. If any other value is - * given then the data for at least that number of miplevels must be provided. - * @param[in] pInitData If different than nullptr, pointer to a buffer containing data to initialize the texture with. - * @param[in] bindFlags The requested bind flags for the resource. - * @return A pointer to a new texture, or throws an exception if creation failed. + * Get the number of subresources */ - static ref create2D( - ref pDevice, - uint32_t width, - uint32_t height, - ResourceFormat format, - uint32_t arraySize = 1, - uint32_t mipLevels = kMaxPossible, - const void* pInitData = nullptr, - BindFlags bindFlags = BindFlags::ShaderResource - ); - - /** - * Create a 3D texture. - * @param[in] width The width of the texture. - * @param[in] height The height of the texture. - * @param[in] depth The depth of the texture. - * @param[in] format The format of the texture. - * @param[in] mipLevels If equal to kMaxPossible then an entire mip chain will be generated from mip level 0. If any other value is - * given then the data for at least that number of miplevels must be provided. - * @param[in] pInitData If different than nullptr, pointer to a buffer containing data to initialize the texture with. - * @param[in] bindFlags The requested bind flags for the resource. - * @param[in] isSparse If true, the texture is created using sparse texture options supported by the API. - * @return A pointer to a new texture, or throws an exception if creation failed. - */ - static ref create3D( - ref pDevice, - uint32_t width, - uint32_t height, - uint32_t depth, - ResourceFormat format, - uint32_t mipLevels = kMaxPossible, - const void* pInitData = nullptr, - BindFlags bindFlags = BindFlags::ShaderResource, - bool isSparse = false - ); + uint32_t getSubresourceCount() const { return mMipLevels * mArraySize; } /** - * Create a cube texture. - * @param[in] width The width of the texture. - * @param[in] height The height of the texture. - * @param[in] format The format of the texture. - * @param[in] arraySize The array size of the texture. - * @param[in] mipLevels If equal to kMaxPossible then an entire mip chain will be generated from mip level 0. If any other value is - * given then the data for at least that number of miplevels must be provided. - * @param[in] pInitData If different than nullptr, pointer to a buffer containing data to initialize the texture with. - * @param[in] bindFlags The requested bind flags for the resource. - * @return A pointer to a new texture, or throws an exception if creation failed. - */ - static ref createCube( - ref pDevice, - uint32_t width, - uint32_t height, - ResourceFormat format, - uint32_t arraySize = 1, - uint32_t mipLevels = kMaxPossible, - const void* pInitData = nullptr, - BindFlags bindFlags = BindFlags::ShaderResource - ); - - /** - * Create a multi-sampled 2D texture. - * @param[in] width The width of the texture. - * @param[in] height The height of the texture. - * @param[in] format The format of the texture. - * @param[in] sampleCount The sample count of the texture. - * @param[in] arraySize The array size of the texture. - * @param[in] bindFlags The requested bind flags for the resource. - * @return A pointer to a new texture, or throws an exception if creation failed. + * Get the resource format */ - static ref create2DMS( - ref pDevice, - uint32_t width, - uint32_t height, - ResourceFormat format, - uint32_t sampleCount, - uint32_t arraySize = 1, - BindFlags bindFlags = BindFlags::ShaderResource - ); + ResourceFormat getFormat() const { return mFormat; } /** * Create a new texture object with mips specified explicitly from individual files. @@ -262,12 +172,12 @@ class FALCOR_API Texture : public Resource ref pDevice, fstd::span paths, bool loadAsSrgb, - Texture::BindFlags bindFlags = BindFlags::ShaderResource + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource ); /** * Create a new texture object from a file. - * @param[in] path File path of the image. Can also include a full path or relative path from a data directory. + * @param[in] path File path of the image (absolute or relative to working directory). * @param[in] generateMipLevels Whether the mip-chain should be generated. * @param[in] loadAsSrgb Load the texture using sRGB format. Only valid for 3 or 4 component textures. * @param[in] bindFlags The bind flags to create the texture with. @@ -278,7 +188,7 @@ class FALCOR_API Texture : public Resource const std::filesystem::path& path, bool generateMipLevels, bool loadAsSrgb, - BindFlags bindFlags = BindFlags::ShaderResource + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource ); gfx::ITextureResource* getGfxTextureResource() const { return mGfxTextureResource; } @@ -338,6 +248,28 @@ class FALCOR_API Texture : public Resource */ ref getUAV(uint32_t mipLevel, uint32_t firstArraySlice = 0, uint32_t arraySize = kMaxPossible); + /** + * Get the data layout of a subresource. + * @param[in] subresource The subresource index. + */ + SubresourceLayout getSubresourceLayout(uint32_t subresource) const; + + /** + * Set the data of a subresource. + * @param[in] subresource The subresource index. + * @param[in] pData The data to write. + * @param[in] size The size of the data (must match the actual subresource size). + */ + void setSubresourceBlob(uint32_t subresource, const void* pData, size_t size); + + /** + * Get the data of a subresource. + * @param[in] subresource The subresource index. + * @param[in] pData The data buffer to read to. + * @param[in] size The size of the data (must match the actual subresource size). + */ + void getSubresourceBlob(uint32_t subresource, void* pData, size_t size) const; + /** * Capture the texture to an image file. * @param[in] mipLevel Requested mip-level @@ -391,19 +323,6 @@ class FALCOR_API Texture : public Resource bool compareDesc(const Texture* pOther) const; protected: - Texture( - ref pDevice, - uint32_t width, - uint32_t height, - uint32_t depth, - uint32_t arraySize, - uint32_t mipLevels, - uint32_t sampleCount, - ResourceFormat format, - Type Type, - BindFlags bindFlags - ); - void apiInit(const void* pData, bool autoGenMips); void uploadInitData(RenderContext* pRenderContext, const void* pData, bool autoGenMips); Slang::ComPtr mGfxTextureResource; @@ -411,13 +330,13 @@ class FALCOR_API Texture : public Resource bool mReleaseRtvsAfterGenMips = true; std::filesystem::path mSourcePath; + ResourceFormat mFormat = ResourceFormat::Unknown; uint32_t mWidth = 0; uint32_t mHeight = 0; uint32_t mDepth = 0; uint32_t mMipLevels = 0; - uint32_t mSampleCount = 0; uint32_t mArraySize = 0; - ResourceFormat mFormat = ResourceFormat::Unknown; + uint32_t mSampleCount = 0; bool mIsSparse = false; int3 mSparsePageRes = int3(0); diff --git a/Source/Falcor/Core/Errors.cpp b/Source/Falcor/Core/API/Types.cpp similarity index 75% rename from Source/Falcor/Core/Errors.cpp rename to Source/Falcor/Core/API/Types.cpp index 0f828f0ee..c5ce0c67a 100644 --- a/Source/Falcor/Core/Errors.cpp +++ b/Source/Falcor/Core/API/Types.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,26 +25,23 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#include "Errors.h" -#include "Platform/OS.h" +#include "Types.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor { -Exception::Exception(const char* what) : mpWhat(std::make_shared(what)) + +FALCOR_SCRIPT_BINDING(Types) { - // Append stack trace. - (*mpWhat) += "\n\nStacktrace:\n" + getStackTrace(3); + pybind11::falcor_enum shaderModel(m, "ShaderModel"); + ScriptBindings::addEnumBinaryOperators(shaderModel); - // Report exception as error. -#if FALCOR_REPORT_EXCEPTION_AS_ERROR - reportFatalError(*mpWhat, false); -#endif -} + pybind11::falcor_enum(m, "DataType"); + // Register attributes on the main module. This is similar to numpy/pytorch. + for (const auto& item : EnumInfo::items()) + m.attr(item.second.c_str()) = item.first; -FALCOR_SCRIPT_BINDING(Errors) -{ - pybind11::register_exception(m, "RuntimeError"); - pybind11::register_exception(m, "ArgumentError"); + pybind11::falcor_enum(m, "ComparisonFunc"); } + } // namespace Falcor diff --git a/Source/Falcor/Core/API/Types.h b/Source/Falcor/Core/API/Types.h new file mode 100644 index 000000000..4326248dd --- /dev/null +++ b/Source/Falcor/Core/API/Types.h @@ -0,0 +1,171 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once + +#include "Core/Enum.h" + +namespace Falcor +{ + +enum class ShaderModel : uint32_t +{ + Unknown = 0, + SM6_0 = 60, + SM6_1 = 61, + SM6_2 = 62, + SM6_3 = 63, + SM6_4 = 64, + SM6_5 = 65, + SM6_6 = 66, + SM6_7 = 67, +}; +FALCOR_ENUM_INFO( + ShaderModel, + { + {ShaderModel::Unknown, "Unknown"}, + {ShaderModel::SM6_0, "SM6_0"}, + {ShaderModel::SM6_1, "SM6_1"}, + {ShaderModel::SM6_2, "SM6_2"}, + {ShaderModel::SM6_3, "SM6_3"}, + {ShaderModel::SM6_4, "SM6_4"}, + {ShaderModel::SM6_5, "SM6_5"}, + {ShaderModel::SM6_6, "SM6_6"}, + {ShaderModel::SM6_7, "SM6_7"}, + } +); +FALCOR_ENUM_REGISTER(ShaderModel); + +inline uint32_t getShaderModelMajorVersion(ShaderModel sm) +{ + return uint32_t(sm) / 10; +} +inline uint32_t getShaderModelMinorVersion(ShaderModel sm) +{ + return uint32_t(sm) % 10; +} + +/** + * Falcor shader types + */ +enum class ShaderType +{ + Vertex, ///< Vertex shader + Pixel, ///< Pixel shader + Geometry, ///< Geometry shader + Hull, ///< Hull shader (AKA Tessellation control shader) + Domain, ///< Domain shader (AKA Tessellation evaluation shader) + Compute, ///< Compute shader + RayGeneration, ///< Ray generation shader + Intersection, ///< Intersection shader + AnyHit, ///< Any hit shader + ClosestHit, ///< Closest hit shader + Miss, ///< Miss shader + Callable, ///< Callable shader + Count ///< Shader Type count +}; +FALCOR_ENUM_INFO( + ShaderType, + { + {ShaderType::Vertex, "Vertex"}, + {ShaderType::Pixel, "Pixel"}, + {ShaderType::Geometry, "Geometry"}, + {ShaderType::Hull, "Hull"}, + {ShaderType::Domain, "Domain"}, + {ShaderType::Compute, "Compute"}, + {ShaderType::RayGeneration, "RayGeneration"}, + {ShaderType::Intersection, "Intersection"}, + {ShaderType::AnyHit, "AnyHit"}, + {ShaderType::ClosestHit, "ClosestHit"}, + {ShaderType::Miss, "Miss"}, + {ShaderType::Callable, "Callable"}, + } +); +FALCOR_ENUM_REGISTER(ShaderType); + +enum class DataType +{ + int8, + int16, + int32, + int64, + uint8, + uint16, + uint32, + uint64, + float16, + float32, + float64, +}; +FALCOR_ENUM_INFO( + DataType, + { + {DataType::int8, "int8"}, + {DataType::int16, "int16"}, + {DataType::int32, "int32"}, + {DataType::int64, "int64"}, + {DataType::uint8, "uint8"}, + {DataType::uint16, "uint16"}, + {DataType::uint32, "uint32"}, + {DataType::uint64, "uint64"}, + {DataType::float16, "float16"}, + {DataType::float32, "float32"}, + {DataType::float64, "float64"}, + } +); +FALCOR_ENUM_REGISTER(DataType); + +enum class ComparisonFunc +{ + Disabled, ///< Comparison is disabled + Never, ///< Comparison always fails + Always, ///< Comparison always succeeds + Less, ///< Passes if source is less than the destination + Equal, ///< Passes if source is equal to the destination + NotEqual, ///< Passes if source is not equal to the destination + LessEqual, ///< Passes if source is less than or equal to the destination + Greater, ///< Passes if source is greater than to the destination + GreaterEqual, ///< Passes if source is greater than or equal to the destination +}; + +FALCOR_ENUM_INFO( + ComparisonFunc, + { + {ComparisonFunc::Disabled, "Disabled"}, + {ComparisonFunc::Never, "Never"}, + {ComparisonFunc::Always, "Always"}, + {ComparisonFunc::Less, "Less"}, + {ComparisonFunc::Equal, "Equal"}, + {ComparisonFunc::NotEqual, "NotEqual"}, + {ComparisonFunc::LessEqual, "LessEqual"}, + {ComparisonFunc::Greater, "Greater"}, + {ComparisonFunc::GreaterEqual, "GreaterEqual"}, + } +); +FALCOR_ENUM_REGISTER(ComparisonFunc); + +} // namespace Falcor diff --git a/Source/Falcor/Core/API/VAO.cpp b/Source/Falcor/Core/API/VAO.cpp index bbbd2538c..2e681c08e 100644 --- a/Source/Falcor/Core/API/VAO.cpp +++ b/Source/Falcor/Core/API/VAO.cpp @@ -39,7 +39,7 @@ Vao::Vao(const BufferVec& pVBs, ref pLayout, ref pIB, Reso ref Vao::create(Topology topology, ref pLayout, const BufferVec& pVBs, ref pIB, ResourceFormat ibFormat) { // TODO: Check number of vertex buffers match with pLayout. - checkArgument( + FALCOR_CHECK( !pIB || (ibFormat == ResourceFormat::R16Uint || ibFormat == ResourceFormat::R32Uint), "'ibFormat' must be R16Uint or R32Uint." ); return ref(new Vao(pVBs, pLayout, pIB, ibFormat, topology)); diff --git a/Source/Falcor/Core/API/VAO.h b/Source/Falcor/Core/API/VAO.h index 252de2f10..eddd8cb4b 100644 --- a/Source/Falcor/Core/API/VAO.h +++ b/Source/Falcor/Core/API/VAO.h @@ -29,7 +29,7 @@ #include "VertexLayout.h" #include "Buffer.h" #include "Core/Macros.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/Object.h" #include diff --git a/Source/Falcor/Core/API/fwd.h b/Source/Falcor/Core/API/fwd.h index c2157454b..63a0f9e4a 100644 --- a/Source/Falcor/Core/API/fwd.h +++ b/Source/Falcor/Core/API/fwd.h @@ -44,6 +44,18 @@ class Texture; class Sampler; +struct FenceDesc; +class Fence; + +struct ComputeStateObjectDesc; +class ComputeStateObject; + +struct GraphicsStateObjectDesc; +class GraphicsStateObject; + +struct RtStateObjectDesc; +class RtStateObject; + class ShaderResourceView; class UnorderedAccessView; diff --git a/Source/Falcor/Core/Assert.h b/Source/Falcor/Core/Assert.h deleted file mode 100644 index 575dab92e..000000000 --- a/Source/Falcor/Core/Assert.h +++ /dev/null @@ -1,91 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#pragma once -#include "ErrorHandling.h" -#include "Errors.h" // TODO: This is only used for RuntimeError, we should consider removing it again. -#include -#include - -#ifdef _DEBUG - -#define FALCOR_ASSERT(a) \ - if (!(a)) \ - { \ - std::string str_ = fmt::format("Assertion failed: {}\n{}:{}", #a, __FILE__, __LINE__); \ - Falcor::reportFatalError(str_); \ - } -#define FALCOR_ASSERT_MSG(a, msg) \ - if (!(a)) \ - { \ - std::string str_ = fmt::format("Assertion failed: {} ({})\n{}:{}", #a, msg, __FILE__, __LINE__); \ - Falcor::reportFatalError(str_); \ - } -#define FALCOR_ASSERT_OP(a, b, OP) \ - if (!(a OP b)) \ - { \ - std::string str_ = fmt::format("Assertion failed: {} {} {} ({} {} {})\n{}:{}", #a, #OP, #b, a, #OP, b, __FILE__, __LINE__); \ - Falcor::reportFatalError(str_); \ - } -#define FALCOR_ASSERT_EQ(a, b) FALCOR_ASSERT_OP(a, b, ==) -#define FALCOR_ASSERT_NE(a, b) FALCOR_ASSERT_OP(a, b, !=) -#define FALCOR_ASSERT_GE(a, b) FALCOR_ASSERT_OP(a, b, >=) -#define FALCOR_ASSERT_GT(a, b) FALCOR_ASSERT_OP(a, b, >) -#define FALCOR_ASSERT_LE(a, b) FALCOR_ASSERT_OP(a, b, <=) -#define FALCOR_ASSERT_LT(a, b) FALCOR_ASSERT_OP(a, b, <) - -#else // _DEBUG - -#define FALCOR_ASSERT(a) \ - {} -#define FALCOR_ASSERT_MSG(a, msg) \ - {} -#define FALCOR_ASSERT_OP(a, b, OP) \ - {} -#define FALCOR_ASSERT_EQ(a, b) FALCOR_ASSERT_OP(a, b, ==) -#define FALCOR_ASSERT_NE(a, b) FALCOR_ASSERT_OP(a, b, !=) -#define FALCOR_ASSERT_GE(a, b) FALCOR_ASSERT_OP(a, b, >=) -#define FALCOR_ASSERT_GT(a, b) FALCOR_ASSERT_OP(a, b, >) -#define FALCOR_ASSERT_LE(a, b) FALCOR_ASSERT_OP(a, b, <=) -#define FALCOR_ASSERT_LT(a, b) FALCOR_ASSERT_OP(a, b, <) - -#endif // _DEBUG - -#define FALCOR_UNIMPLEMENTED() \ - do \ - { \ - FALCOR_ASSERT_MSG(false, "Not implemented"); \ - throw Falcor::RuntimeError("Not implemented"); \ - } while (0) - -#define FALCOR_UNREACHABLE() FALCOR_ASSERT(false) - -#define FALCOR_PRINT(x) \ - do \ - { \ - Falcor::logInfo("{} = {}", #x, x); \ - } while (0) diff --git a/Source/Falcor/Core/AssetResolver.cpp b/Source/Falcor/Core/AssetResolver.cpp new file mode 100644 index 000000000..38e27d9f4 --- /dev/null +++ b/Source/Falcor/Core/AssetResolver.cpp @@ -0,0 +1,182 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "AssetResolver.h" +#include "Core/Platform/OS.h" +#include "Utils/Scripting/ScriptBindings.h" + +namespace Falcor +{ + +AssetResolver::AssetResolver() +{ + mSearchContexts.resize(size_t(AssetCategory::Count)); +} + +std::filesystem::path AssetResolver::resolvePath(const std::filesystem::path& path, AssetCategory category) const +{ + FALCOR_CHECK(category < AssetCategory::Count, "Invalid asset category."); + + // If this is an existing absolute path, or a relative path to the working directory, return it. + std::filesystem::path absolute = std::filesystem::absolute(path); + if (std::filesystem::exists(absolute)) + return std::filesystem::canonical(absolute); + + // Otherwise, try to resolve using search paths. + // First try resolving for the specified asset category. + std::filesystem::path resolved = mSearchContexts[size_t(category)].resolvePath(path); + + // If not resolved, try resolving for the Any asset category. + if (category != AssetCategory::Any && resolved.empty()) + resolved = mSearchContexts[size_t(AssetCategory::Any)].resolvePath(path); + + if (resolved.empty()) + logWarning("Failed to resolve path '{}' for asset type '{}'.", path, category); + + return resolved; +} + +std::vector AssetResolver::resolvePathPattern( + const std::filesystem::path& path, + const std::string& pattern, + bool firstMatchOnly, + AssetCategory category +) const +{ + FALCOR_CHECK(category < AssetCategory::Count, "Invalid asset category."); + + std::regex regex(pattern); + + // If this is an existing absolute path, or a relative path to the working directory, search it. + std::filesystem::path absolute = std::filesystem::absolute(path); + std::vector resolved = globFilesInDirectory(absolute, regex, firstMatchOnly); + if (!resolved.empty()) + return resolved; + + // Otherwise, try to resolve using search paths. + // First try resolving for the specified asset category. + resolved = mSearchContexts[size_t(category)].resolvePathPattern(path, regex, firstMatchOnly); + + // If not resolved, try resolving for the Any asset category. + if (category != AssetCategory::Any && resolved.empty()) + resolved = mSearchContexts[size_t(AssetCategory::Any)].resolvePathPattern(path, regex, firstMatchOnly); + + if (resolved.empty()) + logWarning("Failed to resolve path pattern '{}/{}' for asset type '{}'.", path, pattern, category); + + return resolved; +} + +void AssetResolver::addSearchPath(const std::filesystem::path& path, SearchPathPriority priority, AssetCategory category) +{ + FALCOR_CHECK(path.is_absolute(), "Search path must be absolute."); + FALCOR_CHECK(category < AssetCategory::Count, "Invalid asset category."); + mSearchContexts[size_t(category)].addSearchPath(path, priority); +} + +AssetResolver& AssetResolver::getDefaultResolver() +{ + static AssetResolver defaultResolver; + return defaultResolver; +} + +std::filesystem::path AssetResolver::SearchContext::resolvePath(const std::filesystem::path& path) const +{ + for (const auto& searchPath : searchPaths) + { + std::filesystem::path absolutePath = searchPath / path; + if (std::filesystem::exists(absolutePath)) + return std::filesystem::canonical(absolutePath); + } + + return {}; +} + +std::vector AssetResolver::SearchContext::resolvePathPattern( + const std::filesystem::path& path, + const std::regex& regex, + bool firstMatchOnly +) const +{ + for (const auto& searchPath : searchPaths) + { + std::filesystem::path absolutePath = searchPath / path; + std::vector resolved = globFilesInDirectory(absolutePath, regex, firstMatchOnly); + if (!resolved.empty()) + return resolved; + } + return {}; +} + +void AssetResolver::SearchContext::addSearchPath(const std::filesystem::path& path, SearchPathPriority priority) +{ + FALCOR_ASSERT(path.is_absolute()); + auto it = std::find_if(searchPaths.begin(), searchPaths.end(), [&path](const std::filesystem::path& p) { return isSamePath(path, p); }); + if (it != searchPaths.end()) + searchPaths.erase(it); + switch (priority) + { + case SearchPathPriority::First: + searchPaths.insert(searchPaths.begin(), path); + return; + case SearchPathPriority::Last: + searchPaths.push_back(path); + return; + } + FALCOR_THROW("Invalid search path priority."); +} + +FALCOR_SCRIPT_BINDING(AssetResolver) +{ + using namespace pybind11::literals; + + pybind11::falcor_enum(m, "AssetCategory"); + pybind11::falcor_enum(m, "SearchPathPriority"); + + pybind11::class_ assetResolver(m, "AssetResolver"); + assetResolver.def("resolve_path", &AssetResolver::resolvePath, "path"_a, "category"_a = AssetCategory::Any); + assetResolver.def( + "resolve_path_pattern", + &AssetResolver::resolvePathPattern, + "path"_a, + "pattern"_a, + "first_match_only"_a = false, + "category"_a = AssetCategory::Any + ); + + assetResolver.def( + "add_search_path", + &AssetResolver::addSearchPath, + "path"_a, + "priority"_a = SearchPathPriority::Last, + "category"_a = AssetCategory::Any + ); + + assetResolver.def_property_readonly_static("default_resolver", [](pybind11::object) { return AssetResolver::getDefaultResolver(); }); +} + +} // namespace Falcor diff --git a/Source/Falcor/Core/AssetResolver.h b/Source/Falcor/Core/AssetResolver.h new file mode 100644 index 000000000..b7794565c --- /dev/null +++ b/Source/Falcor/Core/AssetResolver.h @@ -0,0 +1,162 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once + +#include "Macros.h" +#include "Enum.h" +#include +#include +#include +#include + +namespace Falcor +{ + +/// Asset categories. +enum class AssetCategory +{ + Any, + Scene, + Texture, + Count, +}; + +FALCOR_ENUM_INFO( + AssetCategory, + { + {AssetCategory::Any, "Any"}, + {AssetCategory::Scene, "Scene"}, + {AssetCategory::Texture, "Texture"}, + } +); +FALCOR_ENUM_REGISTER(AssetCategory); + +/// Search path priorities. +enum class SearchPathPriority +{ + First, ///< Add to the beginning of the search path list. + Last, ///< Add to the end of the search path list. +}; + +FALCOR_ENUM_INFO( + SearchPathPriority, + { + {SearchPathPriority::First, "First"}, + {SearchPathPriority::Last, "Last"}, + } +); +FALCOR_ENUM_REGISTER(SearchPathPriority); + +/** + * @brief Class for resolving paths to asset files. + * + * The AssetResolver class is used to resolve relative paths of assets to absolute paths. + * Paths are resolved with the following logic: + * - If the path is absolute and exists, it is returned in its canonical form. + * - If the path is relative to the working directory and exists, it is returned in its canonical form. + * - If the path is relative to a search directory and exists, it is returned in its canonical form. + * The resolver supports resolving assets of different categories. Each asset category has its own list of + * search paths. When resolving a path, the resolver will first try to resolve the path + * for the specified category, and if that fails, it will try to resolve it for the \c AssetCategory::Any category. + * If no asset category is specified, the \c AssetCategory::Any category is used by default. + */ +class FALCOR_API AssetResolver +{ +public: + /// Default constructor. + AssetResolver(); + + /** + * Resolve \c path to an existing absolute file path. + * If \c path is absolute or relative to the working directory and exists, it is returned in its canonical form. + * If \c path is relative and resolves to some \c / it is returned. + * If the path cannot be resolved, an empty path is returned. + * @param path Path to resolve. + * @param category Asset category. + * @return The resolved path, or an empty path if the path could not be resolved. + */ + std::filesystem::path resolvePath(const std::filesystem::path& path, AssetCategory category = AssetCategory::Any) const; + + /** + * Resolve \c / to a list of existing absolute file paths. + * If \c path is absolute or relative to the working directory and resolves to some \c / + * then the list of matching files is returned. + * If \c path is relative and resolves to some \c // + * then the list of matching files is returned. + * If the path cannot be resolved, an empty list is returned. + * @param path Path prefix to resolve. + * @param pattern Filename pattern to match (regular expression in ECMAScript format). + * @param firstMatchOnly If true, only the first found match is returned. + * @param category Asset category. + * @return Returns an unordered list of resolved paths, or an empty list if the path could not be resolved. + */ + std::vector resolvePathPattern( + const std::filesystem::path& path, + const std::string& pattern, + bool firstMatchOnly = false, + AssetCategory category = AssetCategory::Any + ) const; + + /** + * Add a search path to the resolver. + * The path needs to be absolute and exist. + * An optional priority can be specified, which determines whether the path is added to the beginning or end of the search path list. + * If the search path already exists, it is moved to the specified priority. + * @param path Path to add. + * @param priority Search path priority. + * @param category Asset category. + */ + void addSearchPath( + const std::filesystem::path& path, + SearchPathPriority priority = SearchPathPriority::Last, + AssetCategory category = AssetCategory::Any + ); + + /// Return the global default asset resolver. + static AssetResolver& getDefaultResolver(); + +private: + struct SearchContext + { + /// List of search paths. Resolving is done by searching these paths in order. + std::vector searchPaths; + + std::filesystem::path resolvePath(const std::filesystem::path& path) const; + + std::vector resolvePathPattern( + const std::filesystem::path& path, + const std::regex& regex, + bool firstMatchOnly + ) const; + + void addSearchPath(const std::filesystem::path& path, SearchPathPriority priority); + }; + + std::vector mSearchContexts; +}; +} // namespace Falcor diff --git a/Source/Falcor/Core/Enum.h b/Source/Falcor/Core/Enum.h index 959594e01..5eef2c40e 100644 --- a/Source/Falcor/Core/Enum.h +++ b/Source/Falcor/Core/Enum.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,7 +27,7 @@ **************************************************************************/ #pragma once -#include "Errors.h" +#include "Error.h" #include "Utils/Logger.h" #include #include @@ -64,7 +64,7 @@ inline const std::string& enumToString(T value) const auto& items = EnumInfo::items(); auto it = std::find_if(items.begin(), items.end(), [value](const auto& item) { return item.first == value; }); if (it == items.end()) - throw RuntimeError("Invalid enum value {}", int(value)); + FALCOR_THROW("Invalid enum value {}", int(value)); return it->second; } @@ -78,7 +78,7 @@ inline T stringToEnum(std::string_view name) const auto& items = EnumInfo::items(); auto it = std::find_if(items.begin(), items.end(), [name](const auto& item) { return item.second == name; }); if (it == items.end()) - throw RuntimeError("Invalid enum name '{}'", name); + FALCOR_THROW("Invalid enum name '{}'", name); return it->first; } @@ -111,7 +111,7 @@ inline std::vector flagsToStringList(T flags) } } if (flags != T(0)) - throw RuntimeError("Invalid enum flags value {}", int(flags)); + FALCOR_THROW("Invalid enum flags value {}", int(flags)); return list; } diff --git a/Source/Falcor/Core/ErrorHandling.cpp b/Source/Falcor/Core/Error.cpp similarity index 53% rename from Source/Falcor/Core/ErrorHandling.cpp rename to Source/Falcor/Core/Error.cpp index 16d725d5a..acbbb071e 100644 --- a/Source/Falcor/Core/ErrorHandling.cpp +++ b/Source/Falcor/Core/Error.cpp @@ -25,116 +25,108 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#include "ErrorHandling.h" +#include "Error.h" #include "Platform/OS.h" #include "Utils/Logger.h" -#include "API/Aftermath.h" +#include "Utils/Scripting/ScriptBindings.h" #include namespace Falcor { -static bool sShowMessageBoxOnError = false; // TODO: REMOVEGLOBAL -void setShowMessageBoxOnError(bool enable) +/// Global error diagnostic flags. +static ErrorDiagnosticFlags gErrorDiagnosticFlags = ErrorDiagnosticFlags::BreakOnThrow | ErrorDiagnosticFlags::BreakOnAssert; + +void throwException(const fstd::source_location& loc, std::string_view msg) { - sShowMessageBoxOnError = enable; + std::string fullMsg = fmt::format("{}\n\n{}:{} ({})", msg, loc.file_name(), loc.line(), loc.function_name()); + + if (is_set(gErrorDiagnosticFlags, ErrorDiagnosticFlags::AppendStackTrace)) + fullMsg += fmt::format("\n\nStacktrace:\n{}", getStackTrace(1)); + + if (is_set(gErrorDiagnosticFlags, ErrorDiagnosticFlags::BreakOnThrow) && isDebuggerPresent()) + debugBreak(); + + throw RuntimeError(fullMsg); } -bool getShowMessageBoxOnError() + +void reportAssertion(const fstd::source_location& loc, std::string_view cond, std::string_view msg) { - return sShowMessageBoxOnError; + std::string fullMsg = fmt::format( + "Assertion failed: {}\n{}{}\n{}:{} ({})", cond, msg, msg.empty() ? "" : "\n", loc.file_name(), loc.line(), loc.function_name() + ); + + if (is_set(gErrorDiagnosticFlags, ErrorDiagnosticFlags::AppendStackTrace)) + fullMsg += fmt::format("\n\nStacktrace:\n{}", getStackTrace(1)); + + if (is_set(gErrorDiagnosticFlags, ErrorDiagnosticFlags::BreakOnAssert) && isDebuggerPresent()) + debugBreak(); + + throw AssertionError(fullMsg); } -void reportError(const std::string& msg) +// +// Error handling. +// + +void setErrorDiagnosticFlags(ErrorDiagnosticFlags flags) { - logError(msg); + gErrorDiagnosticFlags = flags; +} - if (sShowMessageBoxOnError) - { - enum ButtonId - { - Continue, - Debug, - Abort - }; +ErrorDiagnosticFlags getErrorDiagnosticFlags() +{ + return gErrorDiagnosticFlags; +} - // Setup message box buttons - std::vector buttons; - buttons.push_back({Continue, "Continue"}); - if (isDebuggerPresent()) - buttons.push_back({Debug, "Debug"}); - buttons.push_back({Abort, "Abort"}); +void reportErrorAndContinue(std::string_view msg) +{ + logError(msg); - // Show message box - auto result = msgBox("Error", msg, buttons, MsgBoxIcon::Error); - if (result == Continue) - return; - if (result == Debug) - debugBreak(); - } - else + if (is_set(gErrorDiagnosticFlags, ErrorDiagnosticFlags::ShowMessageBoxOnError)) { - if (isDebuggerPresent()) - debugBreak(); + // Show message box + msgBox("Error", std::string(msg), MsgBoxType::Ok, MsgBoxIcon::Error); } - - std::quick_exit(1); } -void reportErrorAndAllowRetry(const std::string& msg) +bool reportErrorAndAllowRetry(std::string_view msg) { logError(msg); - if (sShowMessageBoxOnError) + if (is_set(gErrorDiagnosticFlags, ErrorDiagnosticFlags::ShowMessageBoxOnError)) { enum ButtonId { Retry, - Debug, Abort }; // Setup message box buttons std::vector buttons; buttons.push_back({Retry, "Retry"}); - if (isDebuggerPresent()) - buttons.push_back({Debug, "Debug"}); buttons.push_back({Abort, "Abort"}); // Show message box - auto result = msgBox("Error", msg, buttons, MsgBoxIcon::Error); - if (result == Retry) - return; - if (result == Debug) - debugBreak(); - } - else - { - if (isDebuggerPresent()) - debugBreak(); + auto result = msgBox("Error", std::string(msg), buttons, MsgBoxIcon::Error); + return result == Retry; } - std::quick_exit(1); + return false; } -[[noreturn]] void reportFatalError(const std::string& msg, bool showStackTrace) +[[noreturn]] void reportFatalErrorAndTerminate(std::string_view msg) { // Immediately terminate on re-entry. static std::atomic entered; if (entered.exchange(true) == true) std::quick_exit(1); - std::string extendedMsg = msg; - if (showStackTrace) - extendedMsg += "\n\nStacktrace:\n" + getStackTrace(3); - -#if FALCOR_HAS_AFTERMATH - if (!waitForAftermathDumps()) - extendedMsg += "\n\nAftermath GPU crash dump generation failed.\n\n"; -#endif + std::string fullMsg = fmt::format("{}\n\nStacktrace:\n{}", msg, getStackTrace(3)); - logFatal(extendedMsg); + logFatal(fullMsg); - if (sShowMessageBoxOnError) + if (is_set(gErrorDiagnosticFlags, ErrorDiagnosticFlags::ShowMessageBoxOnError)) { enum ButtonId { @@ -149,7 +141,7 @@ void reportErrorAndAllowRetry(const std::string& msg) buttons.push_back({Abort, "Abort"}); // Show message box - auto result = msgBox("Fatal Error", extendedMsg, buttons, MsgBoxIcon::Error); + auto result = msgBox("Fatal Error", fullMsg, buttons, MsgBoxIcon::Error); if (result == Debug) debugBreak(); } @@ -161,4 +153,11 @@ void reportErrorAndAllowRetry(const std::string& msg) std::quick_exit(1); } + +FALCOR_SCRIPT_BINDING(Error) +{ + pybind11::register_exception(m, "RuntimeError"); + pybind11::register_exception(m, "AssertionError"); +} + } // namespace Falcor diff --git a/Source/Falcor/Core/Error.h b/Source/Falcor/Core/Error.h new file mode 100644 index 000000000..d06d216d6 --- /dev/null +++ b/Source/Falcor/Core/Error.h @@ -0,0 +1,306 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include "Macros.h" +#include // TODO C++20: Replace with +#include // TODO C++20: Replace with +#include +#include +#include +#include +#include + +namespace Falcor +{ + +// +// Exceptions. +// + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4275) // allow dllexport on classes dervied from STL +#endif + +/** + * Base class for all Falcor exceptions. + */ +class FALCOR_API Exception : public std::exception +{ +public: + Exception() noexcept {} + Exception(std::string_view what) : mpWhat(std::make_shared(what)) {} + Exception(const Exception& other) noexcept { mpWhat = other.mpWhat; } + virtual ~Exception() override {} + virtual const char* what() const noexcept override { return mpWhat ? mpWhat->c_str() : ""; } + +protected: + // Message is stored as a reference counted string in order to allow copy constructor to be noexcept. + std::shared_ptr mpWhat; +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +/** + * Exception to be thrown when an error happens at runtime. + */ +class FALCOR_API RuntimeError : public Exception +{ +public: + RuntimeError() noexcept {} + RuntimeError(std::string_view what) : Exception(what) {} + RuntimeError(const RuntimeError& other) noexcept { mpWhat = other.mpWhat; } + virtual ~RuntimeError() override {} +}; + +/** + * Exception to be thrown on FALCOR_ASSERT. + */ +class FALCOR_API AssertionError : public Exception +{ +public: + AssertionError() noexcept {} + AssertionError(std::string_view what) : Exception(what) {} + AssertionError(const AssertionError& other) noexcept { mpWhat = other.mpWhat; } + virtual ~AssertionError() override {} +}; + +// +// Exception helpers. +// + +/// Throw a RuntimeError exception. +/// If ErrorDiagnosticFlags::AppendStackTrace is set, a stack trace will be appended to the exception message. +/// If ErrorDiagnosticFlags::BreakOnThrow is set, the debugger will be broken into (if attached). +[[noreturn]] FALCOR_API void throwException(const fstd::source_location& loc, std::string_view msg); + +namespace detail +{ +/// Overload to allow FALCOR_THROW to be called with a message only. +[[noreturn]] inline void throwException(const fstd::source_location& loc, std::string_view msg) +{ + ::Falcor::throwException(loc, msg); +} + +/// Overload to allow FALCOR_THROW to be called with a format string and arguments. +template +[[noreturn]] inline void throwException(const fstd::source_location& loc, fmt::format_string fmt, Args&&... args) +{ + ::Falcor::throwException(loc, fmt::format(fmt, std::forward(args)...)); +} +} // namespace detail +} // namespace Falcor + +/// Helper for throwing a RuntimeError exception. +/// Accepts either a string or a format string and arguments: +/// FALCOR_THROW("This is an error message."); +/// FALCOR_THROW("Expected {} items, got {}.", expectedCount, actualCount); +#define FALCOR_THROW(...) ::Falcor::detail::throwException(fstd::source_location::current(), __VA_ARGS__) + +/// Helper for throwing a RuntimeError exception if condition isn't met. +/// Accepts either a string or a format string and arguments. +/// FALCOR_CHECK(device != nullptr, "Device is null."); +/// FALCOR_CHECK(count % 3 == 0, "Count must be a multiple of 3, got {}.", count); +#define FALCOR_CHECK(cond, ...) \ + do \ + { \ + if (!(cond)) \ + FALCOR_THROW(__VA_ARGS__); \ + } while (0) + +/// Helper for marking unimplemented functions. +#define FALCOR_UNIMPLEMENTED() FALCOR_THROW("Unimplemented") + +/// Helper for marking unreachable code. +#define FALCOR_UNREACHABLE() FALCOR_THROW("Unreachable") + +// +// Assertions. +// + +namespace Falcor +{ +/// Report an assertion. +/// If ErrorDiagnosticFlags::AppendStackTrace is set, a stack trace will be appended to the exception message. +/// If ErrorDiagnosticFlags::BreakOnAssert is set, the debugger will be broken into (if attached). +[[noreturn]] FALCOR_API void reportAssertion(const fstd::source_location& loc, std::string_view cond, std::string_view msg = {}); + +namespace detail +{ +/// Overload to allow FALCOR_ASSERT to be called without a message. +[[noreturn]] inline void reportAssertion(const fstd::source_location& loc, std::string_view cond) +{ + ::Falcor::reportAssertion(loc, cond); +} + +/// Overload to allow FALCOR_ASSERT to be called with a message only. +[[noreturn]] inline void reportAssertion(const fstd::source_location& loc, std::string_view cond, std::string_view msg) +{ + ::Falcor::reportAssertion(loc, cond, msg); +} + +/// Overload to allow FALCOR_ASSERT to be called with a format string and arguments. +template +[[noreturn]] inline void reportAssertion( + const fstd::source_location& loc, + std::string_view cond, + fmt::format_string fmt, + Args&&... args +) +{ + ::Falcor::reportAssertion(loc, cond, fmt::format(fmt, std::forward(args)...)); +} +} // namespace detail +} // namespace Falcor + +#if FALCOR_ENABLE_ASSERTS + +/// Helper for asserting a condition. +/// Accepts either only the condition, the condition with a string or the condition with a format string and arguments: +/// FALCOR_ASSERT(device != nullptr); +/// FALCOR_ASSERT(device != nullptr, "Device is null."); +/// FALCOR_ASSERT(count % 3 == 0, "Count must be a multiple of 3, got {}.", count); +#define FALCOR_ASSERT(cond, ...) \ + if (!(cond)) \ + { \ + ::Falcor::detail::reportAssertion(fstd::source_location::current(), #cond, ##__VA_ARGS__); \ + } + +/// Helper for asserting a binary comparison between two variables. +/// Automatically prints the compared values. +#define FALCOR_ASSERT_OP(a, b, OP) \ + if (!(a OP b)) \ + { \ + ::Falcor::detail::reportAssertion(fstd::source_location::current(), fmt::format("{} {} {} ({} {} {})", #a, #OP, #b, a, #OP, b)); \ + } + +#define FALCOR_ASSERT_EQ(a, b) FALCOR_ASSERT_OP(a, b, ==) +#define FALCOR_ASSERT_NE(a, b) FALCOR_ASSERT_OP(a, b, !=) +#define FALCOR_ASSERT_GE(a, b) FALCOR_ASSERT_OP(a, b, >=) +#define FALCOR_ASSERT_GT(a, b) FALCOR_ASSERT_OP(a, b, >) +#define FALCOR_ASSERT_LE(a, b) FALCOR_ASSERT_OP(a, b, <=) +#define FALCOR_ASSERT_LT(a, b) FALCOR_ASSERT_OP(a, b, <) + +#else // FALCOR_ENABLE_ASSERTS + +#define FALCOR_ASSERT(cond, ...) \ + {} +#define FALCOR_ASSERT_OP(a, b, OP) \ + {} +#define FALCOR_ASSERT_EQ(a, b) FALCOR_ASSERT_OP(a, b, ==) +#define FALCOR_ASSERT_NE(a, b) FALCOR_ASSERT_OP(a, b, !=) +#define FALCOR_ASSERT_GE(a, b) FALCOR_ASSERT_OP(a, b, >=) +#define FALCOR_ASSERT_GT(a, b) FALCOR_ASSERT_OP(a, b, >) +#define FALCOR_ASSERT_LE(a, b) FALCOR_ASSERT_OP(a, b, <=) +#define FALCOR_ASSERT_LT(a, b) FALCOR_ASSERT_OP(a, b, <) + +#endif // FALCOR_ENABLE_ASSERTS + +// +// Error reporting. +// + +namespace Falcor +{ + +/// Flags controlling the error diagnostic behavior. +enum class ErrorDiagnosticFlags +{ + None = 0, + /// Break into debugger (if attached) when calling FALCOR_THROW. + BreakOnThrow, + /// Break into debugger (if attached) when calling FALCOR_ASSERT. + BreakOnAssert, + /// Append a stack trace to the exception error message when using FALCOR_THROW and FALCOR_ASSERT. + AppendStackTrace = 2, + /// Show a message box when reporting errors using the reportError() functions. + ShowMessageBoxOnError = 4, +}; +FALCOR_ENUM_CLASS_OPERATORS(ErrorDiagnosticFlags); + +/// Set the global error diagnostic flags. +FALCOR_API void setErrorDiagnosticFlags(ErrorDiagnosticFlags flags); + +/// Get the global error diagnostic flags. +FALCOR_API ErrorDiagnosticFlags getErrorDiagnosticFlags(); + +/** + * Report an error by logging it and optionally showing a message box. + * The message box is only shown if ErrorDiagnosticFlags::ShowMessageBoxOnError is set. + * @param msg Error message. + */ +FALCOR_API void reportErrorAndContinue(std::string_view msg); + +/** + * Report an error by logging it and optionally showing a message box with the option to abort or retry. + * The message box is only shown if ErrorDiagnosticFlags::ShowMessageBoxOnError is set. + * If not message box is shown, the function always returns false (i.e. no retry). + * @param msg Error message. + * @return Returns true if the user chose to retry. + */ +FALCOR_API bool reportErrorAndAllowRetry(std::string_view msg); + +/** + * Report a fatal error. + * + * The following actions are taken: + * - The error message is logged together with a stack trace. + * - If ErrorDiagnosticFlags::ShowMessageBoxOnError is set: + * - A message box with the error + stack trace is shown. + * - If a debugger is attached there is a button to break into the debugger. + * - If ErrorDiagnosticFlags::ShowMessageBoxOnError is not set: + * - If a debugger is attached there it is broken into. + * - The application is immediately terminated (std::quick_exit(1)). + * @param msg Error message. + */ +[[noreturn]] FALCOR_API void reportFatalErrorAndTerminate(std::string_view msg); + +/// Helper to run a callback and catch/report all exceptions. +/// This is typically used in main() to guard the entire application. +template +int catchAndReportAllExceptions(CallbackT callback, ResultT errorResult = 1) +{ + ResultT result = errorResult; + try + { + result = callback(); + } + catch (const std::exception& e) + { + reportErrorAndContinue(std::string("Caught an exception:\n\n") + e.what()); + } + catch (...) + { + reportErrorAndContinue("Caught an exception of unknown type!"); + } + return result; +} + +} // namespace Falcor diff --git a/Source/Falcor/Core/ErrorHandling.h b/Source/Falcor/Core/ErrorHandling.h deleted file mode 100644 index 337d348ec..000000000 --- a/Source/Falcor/Core/ErrorHandling.h +++ /dev/null @@ -1,68 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#pragma once -#include "Macros.h" -#include - -namespace Falcor -{ -/** - * Enable/disable showing a message box when reporting an error. - */ -FALCOR_API void setShowMessageBoxOnError(bool enable); - -/** - * Return if showing a message box when reporting an error is enabled/disabled. - */ -FALCOR_API bool getShowMessageBoxOnError(); - -/** - * Report an error by logging it and showing a message box with the option to abort, - * continue or enter the debugger (if one is attached). - * If message boxes are disabled, this will terminate the application after logging the error. - * @param msg Error message. - */ -FALCOR_API void reportError(const std::string& msg); - -/** - * Report an error by logging it and showing a message box with the option to abort, - * retry or enter the debugger (if one is attached). - * If message boxes are disabled, this will terminate the application after logging the error. - * @param msg Error message. - */ -FALCOR_API void reportErrorAndAllowRetry(const std::string& msg); - -/** - * Report a fatal error by logging it and showing a message box with the option to abort - * or enter the debugger (if one is attached). - * If message boxes are disabled, this will terminate the application after logging the error. - * @param msg Error message. - * @param showStackTrace Show a stack trace after the message. - */ -[[noreturn]] FALCOR_API void reportFatalError(const std::string& msg, bool showStackTrace = true); -} // namespace Falcor diff --git a/Source/Falcor/Core/Errors.h b/Source/Falcor/Core/Errors.h deleted file mode 100644 index 4b6b59371..000000000 --- a/Source/Falcor/Core/Errors.h +++ /dev/null @@ -1,184 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#pragma once -#include "Macros.h" -#include // TODO C++20: Replace with -#include -#include -#include -#include -#include - -namespace Falcor -{ - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4275) // allow dllexport on classes dervied from STL -#endif - -/** - * Base class for all Falcor exceptions. - */ -class FALCOR_API Exception : public std::exception -{ -public: - Exception() noexcept {} - - Exception(const char* what); - - Exception(const std::string& what) : Exception(what.c_str()) {} - - template - explicit Exception(fmt::format_string format, Args&&... args) - : Exception(fmt::format(format, std::forward(args)...).c_str()) - {} - - Exception(const Exception& other) noexcept { mpWhat = other.mpWhat; } - - virtual ~Exception() override {} - - virtual const char* what() const noexcept override { return mpWhat ? mpWhat->c_str() : ""; } - -protected: - // Message is stored as a reference counted string in order to allow copy constructor to be noexcept. - std::shared_ptr mpWhat; -}; - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -/** - * Exception to be thrown when an error happens at runtime. - */ -class FALCOR_API RuntimeError : public Exception -{ -public: - RuntimeError() noexcept {} - - RuntimeError(const char* what) : Exception(what) {} - - RuntimeError(const std::string& what) : Exception(what) {} - - template - explicit RuntimeError(fmt::format_string format, Args&&... args) : Exception(format, std::forward(args)...) - {} - - RuntimeError(const RuntimeError& other) noexcept { mpWhat = other.mpWhat; } - - virtual ~RuntimeError() override {} -}; - -/** - * Exception to be thrown when a function argument has an invalid value. - */ -class FALCOR_API ArgumentError : public Exception -{ -public: - ArgumentError() noexcept {} - - ArgumentError(const char* what) : Exception(what) {} - - ArgumentError(const std::string& what) : Exception(what) {} - - template - explicit ArgumentError(fmt::format_string format, Args&&... args) : Exception(format, std::forward(args)...) - {} - - virtual ~ArgumentError() override {} - - ArgumentError(const ArgumentError& other) noexcept { mpWhat = other.mpWhat; } -}; - -/** - * Check that an invariant holds and throw a RuntimeError if it doesn't. - * @param[in] condition Invariant condition that must hold. - * @param[in] format Format string. - * @param[in] ... Arguments. - */ -template -void checkInvariant(bool condition, fmt::format_string format, Args&&... args) -{ - if (!condition) - throw RuntimeError(format, std::forward(args)...); -} - -/** - * Check if a function argument meets some condition and throws an ArgumentError if it doesn't. - * @param[in] condition Argument condition that must hold. - * @param[in] format Format string. - * @param[in] ... Arguments. - */ -template -void checkArgument(bool condition, fmt::format_string format, Args&&... args) -{ - if (!condition) - throw ArgumentError(format, std::forward(args)...); -} -} // namespace Falcor - -// Private macros for the helpers below, not to be used on their own -#define INTERNALFALCOR_CHECK_ARG(cond) \ - if (!(cond)) \ - { \ - std::string str = fmt::format(fmt::runtime("Invalid argument: {} failed\n{}:{}"), #cond, __FILE__, __LINE__); \ - Falcor::checkArgument(false, "{}", str); \ - } - -#define INTERNALFALCOR_CHECK_ARG_MSG(cond, msg, ...) \ - if (!(cond)) \ - { \ - std::string str = fmt::format(fmt::runtime("Invalid argument: {} failed\n" msg "\n"), #cond, ##__VA_ARGS__); \ - str += fmt::format(fmt::runtime("{}:{}"), __FILE__, __LINE__); \ - Falcor::checkArgument(false, "{}", str); \ - } \ - while (0) - -#define INTERNALFALCOR_CHECK_BINOP_MSG(a, b, OP, msg, ...) \ - INTERNALFALCOR_CHECK_ARG_MSG(a OP b, "{} (= {}) " #OP " {} (= {})\n" msg, #a, a, #b, b, ##__VA_ARGS__) - -#define INTERNALFALCOR_CHECK_BINOP(a, b, OP) INTERNALFALCOR_CHECK_ARG_MSG(a OP b, "{} (= {}) " #OP " {} (= {})", #a, a, #b, b) - -// Helper macros to support checkArgument with better messages -#define FALCOR_CHECK_ARG(cond) INTERNALFALCOR_CHECK_ARG(cond) -#define FALCOR_CHECK_ARG_MSG(cond, msg, ...) INTERNALFALCOR_CHECK_ARG_MSG(cond, msg, ##__VA_ARGS__) - -#define FALCOR_CHECK_ARG_EQ_MSG(a, b, msg, ...) INTERNALFALCOR_CHECK_BINOP_MSG(a, b, ==, msg, ##__VA_ARGS__) -#define FALCOR_CHECK_ARG_NE_MSG(a, b, msg, ...) INTERNALFALCOR_CHECK_BINOP_MSG(a, b, !=, msg, ##__VA_ARGS__) -#define FALCOR_CHECK_ARG_LT_MSG(a, b, msg, ...) INTERNALFALCOR_CHECK_BINOP_MSG(a, b, <, msg, ##__VA_ARGS__) -#define FALCOR_CHECK_ARG_LE_MSG(a, b, msg, ...) INTERNALFALCOR_CHECK_BINOP_MSG(a, b, <=, msg, ##__VA_ARGS__) -#define FALCOR_CHECK_ARG_GT_MSG(a, b, msg, ...) INTERNALFALCOR_CHECK_BINOP_MSG(a, b, >, msg, ##__VA_ARGS__) -#define FALCOR_CHECK_ARG_GE_MSG(a, b, msg, ...) INTERNALFALCOR_CHECK_BINOP_MSG(a, b, >=, msg, ##__VA_ARGS__) - -#define FALCOR_CHECK_ARG_EQ(a, b) INTERNALFALCOR_CHECK_BINOP(a, b, ==) -#define FALCOR_CHECK_ARG_NE(a, b) INTERNALFALCOR_CHECK_BINOP(a, b, !=) -#define FALCOR_CHECK_ARG_LT(a, b) INTERNALFALCOR_CHECK_BINOP(a, b, <) -#define FALCOR_CHECK_ARG_LE(a, b) INTERNALFALCOR_CHECK_BINOP(a, b, <=) -#define FALCOR_CHECK_ARG_GT(a, b) INTERNALFALCOR_CHECK_BINOP(a, b, >) -#define FALCOR_CHECK_ARG_GE(a, b) INTERNALFALCOR_CHECK_BINOP(a, b, >=) diff --git a/Source/Falcor/Core/Object.cpp b/Source/Falcor/Core/Object.cpp index 4c8420ce7..340243239 100644 --- a/Source/Falcor/Core/Object.cpp +++ b/Source/Falcor/Core/Object.cpp @@ -26,8 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Object.h" -#include "Assert.h" -#include "ErrorHandling.h" +#include "Error.h" #include #include @@ -56,7 +55,7 @@ void Object::decRef(bool dealloc) const noexcept uint32_t refCount = mRefCount.fetch_sub(1); if (refCount <= 0) { - reportFatalError("Internal error: Object reference count < 0!"); + reportFatalErrorAndTerminate("Internal error: Object reference count < 0!"); } else if (refCount == 1) { diff --git a/Source/Falcor/Core/Object.h b/Source/Falcor/Core/Object.h index c53751c66..221a01d20 100644 --- a/Source/Falcor/Core/Object.h +++ b/Source/Falcor/Core/Object.h @@ -540,7 +540,7 @@ void swap(::Falcor::ref& x, ::Falcor::ref& y) noexcept template struct hash<::Falcor::ref> { - constexpr int operator()(const ::Falcor::ref& r) const { return std::hash()(r.get()); } + constexpr size_t operator()(const ::Falcor::ref& r) const { return std::hash()(r.get()); } }; } // namespace std diff --git a/Source/Falcor/Core/Pass/BaseGraphicsPass.cpp b/Source/Falcor/Core/Pass/BaseGraphicsPass.cpp index f8837a7c9..555b75e09 100644 --- a/Source/Falcor/Core/Pass/BaseGraphicsPass.cpp +++ b/Source/Falcor/Core/Pass/BaseGraphicsPass.cpp @@ -29,35 +29,35 @@ namespace Falcor { -BaseGraphicsPass::BaseGraphicsPass(ref pDevice, const Program::Desc& progDesc, const DefineList& programDefines) : mpDevice(pDevice) +BaseGraphicsPass::BaseGraphicsPass(ref pDevice, const ProgramDesc& progDesc, const DefineList& programDefines) : mpDevice(pDevice) { - auto pProg = GraphicsProgram::create(mpDevice, progDesc, programDefines); + auto pProg = Program::create(mpDevice, progDesc, programDefines); pProg->breakStrongReferenceToDevice(); mpState = GraphicsState::create(mpDevice); mpState->breakStrongReferenceToDevice(); mpState->setProgram(pProg); - mpVars = GraphicsVars::create(mpDevice, pProg.get()); + mpVars = ProgramVars::create(mpDevice, pProg.get()); } void BaseGraphicsPass::addDefine(const std::string& name, const std::string& value, bool updateVars) { mpState->getProgram()->addDefine(name, value); if (updateVars) - mpVars = GraphicsVars::create(mpDevice, mpState->getProgram().get()); + mpVars = ProgramVars::create(mpDevice, mpState->getProgram().get()); } void BaseGraphicsPass::removeDefine(const std::string& name, bool updateVars) { mpState->getProgram()->removeDefine(name); if (updateVars) - mpVars = GraphicsVars::create(mpDevice, mpState->getProgram().get()); + mpVars = ProgramVars::create(mpDevice, mpState->getProgram().get()); } -void BaseGraphicsPass::setVars(const ref& pVars) +void BaseGraphicsPass::setVars(const ref& pVars) { - mpVars = pVars ? pVars : GraphicsVars::create(mpDevice, mpState->getProgram().get()); + mpVars = pVars ? pVars : ProgramVars::create(mpDevice, mpState->getProgram().get()); } void BaseGraphicsPass::breakStrongReferenceToDevice() diff --git a/Source/Falcor/Core/Pass/BaseGraphicsPass.h b/Source/Falcor/Core/Pass/BaseGraphicsPass.h index a38bf6384..2fd1debc1 100644 --- a/Source/Falcor/Core/Pass/BaseGraphicsPass.h +++ b/Source/Falcor/Core/Pass/BaseGraphicsPass.h @@ -29,7 +29,6 @@ #include "Core/Macros.h" #include "Core/Object.h" #include "Core/State/GraphicsState.h" -#include "Core/Program/GraphicsProgram.h" #include "Core/Program/Program.h" #include "Core/Program/ProgramVars.h" #include "Core/Program/ShaderVar.h" @@ -56,7 +55,7 @@ class FALCOR_API BaseGraphicsPass : public Object /** * Get the program */ - ref getProgram() const { return mpState->getProgram(); } + ref getProgram() const { return mpState->getProgram(); } /** * Get the state @@ -66,16 +65,16 @@ class FALCOR_API BaseGraphicsPass : public Object /** * Get the vars */ - const ref& getVars() const { return mpVars; } + const ref& getVars() const { return mpVars; } ShaderVar getRootVar() const { return mpVars->getRootVar(); } /** * Set a vars object. Allows the user to override the internal vars, for example when one wants to share a vars object between different * passes. - * @param[in] pVars The new GraphicsVars object. If this is nullptr, then the pass will automatically create a new GraphicsVars object + * @param[in] pVars The new ProgramVars object. If this is nullptr, then the pass will automatically create a new ProgramVars object */ - void setVars(const ref& pVars); + void setVars(const ref& pVars); void breakStrongReferenceToDevice(); @@ -87,10 +86,10 @@ class FALCOR_API BaseGraphicsPass : public Object * @param[in] programDefines List of macro definitions to set into the program. The macro definitions will be set on all shader stages. * @return A new object, or an exception is thrown if creation failed. */ - BaseGraphicsPass(ref pDevice, const Program::Desc& progDesc, const DefineList& programDefines); + BaseGraphicsPass(ref pDevice, const ProgramDesc& progDesc, const DefineList& programDefines); BreakableReference mpDevice; - ref mpVars; + ref mpVars; ref mpState; }; } // namespace Falcor diff --git a/Source/Falcor/Core/Pass/ComputePass.cpp b/Source/Falcor/Core/Pass/ComputePass.cpp index 7aab07848..0ea9b13fb 100644 --- a/Source/Falcor/Core/Pass/ComputePass.cpp +++ b/Source/Falcor/Core/Pass/ComputePass.cpp @@ -27,17 +27,19 @@ **************************************************************************/ #include "ComputePass.h" #include "Core/API/ComputeContext.h" +#include "Core/API/PythonHelpers.h" #include "Utils/Math/Common.h" +#include "Utils/Scripting/ScriptBindings.h" namespace Falcor { -ComputePass::ComputePass(ref pDevice, const Program::Desc& desc, const DefineList& defines, bool createVars) : mpDevice(pDevice) +ComputePass::ComputePass(ref pDevice, const ProgramDesc& desc, const DefineList& defines, bool createVars) : mpDevice(pDevice) { - auto pProg = ComputeProgram::create(mpDevice, desc, defines); + auto pProg = Program::create(mpDevice, desc, defines); mpState = ComputeState::create(mpDevice); mpState->setProgram(pProg); if (createVars) - mpVars = ComputeVars::create(mpDevice, pProg.get()); + mpVars = ProgramVars::create(mpDevice, pProg.get()); FALCOR_ASSERT(pProg && mpState && (!createVars || mpVars)); } @@ -49,12 +51,12 @@ ref ComputePass::create( bool createVars ) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(path).csEntry(csEntry); return create(pDevice, desc, defines, createVars); } -ref ComputePass::create(ref pDevice, const Program::Desc& desc, const DefineList& defines, bool createVars) +ref ComputePass::create(ref pDevice, const ProgramDesc& desc, const DefineList& defines, bool createVars) { return ref(new ComputePass(pDevice, desc, defines, createVars)); } @@ -77,19 +79,69 @@ void ComputePass::addDefine(const std::string& name, const std::string& value, b { mpState->getProgram()->addDefine(name, value); if (updateVars) - mpVars = ComputeVars::create(mpDevice, mpState->getProgram().get()); + mpVars = ProgramVars::create(mpDevice, mpState->getProgram().get()); } void ComputePass::removeDefine(const std::string& name, bool updateVars) { mpState->getProgram()->removeDefine(name); if (updateVars) - mpVars = ComputeVars::create(mpDevice, mpState->getProgram().get()); + mpVars = ProgramVars::create(mpDevice, mpState->getProgram().get()); } -void ComputePass::setVars(const ref& pVars) +void ComputePass::setVars(const ref& pVars) { - mpVars = pVars ? pVars : ComputeVars::create(mpDevice, mpState->getProgram().get()); + mpVars = pVars ? pVars : ProgramVars::create(mpDevice, mpState->getProgram().get()); FALCOR_ASSERT(mpVars); } + +FALCOR_SCRIPT_BINDING(ComputePass) +{ + using namespace pybind11::literals; + + FALCOR_SCRIPT_BINDING_DEPENDENCY(Device) + FALCOR_SCRIPT_BINDING_DEPENDENCY(ShaderVar) + FALCOR_SCRIPT_BINDING_DEPENDENCY(ComputeContext) + + pybind11::class_> computePass(m, "ComputePass"); + computePass.def( + pybind11::init( + [](ref device, std::optional desc, pybind11::dict defines, const pybind11::kwargs& kwargs) + { + if (desc) + { + FALCOR_CHECK(kwargs.empty(), "Either provide a 'desc' or kwargs, but not both."); + return ComputePass::create(device, *desc, defineListFromPython(defines)); + } + else + { + return ComputePass::create(device, programDescFromPython(kwargs), defineListFromPython(defines)); + } + } + ), + "device"_a, + "desc"_a = std::optional(), + "defines"_a = pybind11::dict(), + pybind11::kw_only() + ); + + computePass.def_property_readonly("program", &ComputePass::getProgram); + computePass.def_property_readonly("root_var", &ComputePass::getRootVar); + computePass.def_property_readonly("globals", &ComputePass::getRootVar); + + computePass.def( + "execute", + [](ComputePass& pass, uint32_t threads_x, uint32_t threads_y, uint32_t threads_z, ComputeContext* compute_context) + { + if (compute_context == nullptr) + compute_context = pass.getDevice()->getRenderContext(); + pass.execute(compute_context, threads_x, threads_y, threads_z); + }, + "threads_x"_a, + "threads_y"_a = 1, + "threads_z"_a = 1, + "compute_context"_a = nullptr + ); +} + } // namespace Falcor diff --git a/Source/Falcor/Core/Pass/ComputePass.h b/Source/Falcor/Core/Pass/ComputePass.h index 42819eeda..42eb8d488 100644 --- a/Source/Falcor/Core/Pass/ComputePass.h +++ b/Source/Falcor/Core/Pass/ComputePass.h @@ -29,7 +29,6 @@ #include "Core/Macros.h" #include "Core/Object.h" #include "Core/State/ComputeState.h" -#include "Core/Program/ComputeProgram.h" #include "Core/Program/Program.h" #include "Core/Program/ProgramVars.h" #include "Core/Program/ShaderVar.h" @@ -69,7 +68,7 @@ class FALCOR_API ComputePass : public Object */ static ref create( ref pDevice, - const Program::Desc& desc, + const ProgramDesc& desc, const DefineList& defines = DefineList(), bool createVars = true ); @@ -107,7 +106,7 @@ class FALCOR_API ComputePass : public Object /** * Get the vars. */ - const ref& getVars() const + const ref& getVars() const { FALCOR_ASSERT(mpVars); return mpVars; @@ -128,25 +127,27 @@ class FALCOR_API ComputePass : public Object /** * Get the program */ - ref getProgram() const { return mpState->getProgram(); } + ref getProgram() const { return mpState->getProgram(); } /** * Set a vars object. Allows the user to override the internal vars, for example when one wants to share a vars object between * different passes. The function throws an exception on error. - * @param[in] pVars The new GraphicsVars object. If this is nullptr, then the pass will automatically create a new vars object. + * @param[in] pVars The new ProgramVars object. If this is nullptr, then the pass will automatically create a new vars object. */ - void setVars(const ref& pVars); + void setVars(const ref& pVars); /** * Get the thread group size from the program */ uint3 getThreadGroupSize() const { return mpState->getProgram()->getReflector()->getThreadGroupSize(); } + const ref& getDevice() const { return mpDevice; } + protected: - ComputePass(ref pDevice, const Program::Desc& desc, const DefineList& defines, bool createVars); + ComputePass(ref pDevice, const ProgramDesc& desc, const DefineList& defines, bool createVars); ref mpDevice; - ref mpVars; + ref mpVars; ref mpState; }; } // namespace Falcor diff --git a/Source/Falcor/Core/Pass/FullScreenPass.cpp b/Source/Falcor/Core/Pass/FullScreenPass.cpp index fe45d5cf9..9a1db7858 100644 --- a/Source/Falcor/Core/Pass/FullScreenPass.cpp +++ b/Source/Falcor/Core/Pass/FullScreenPass.cpp @@ -56,7 +56,7 @@ struct FullScreenPass::SharedData SharedData(ref pDevice) { const uint32_t vbSize = (uint32_t)(sizeof(Vertex) * std::size(kVertices)); - pVertexBuffer = Buffer::create(pDevice, vbSize, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, (void*)kVertices); + pVertexBuffer = pDevice->createBuffer(vbSize, ResourceBindFlags::Vertex, MemoryType::Upload, (void*)kVertices); pVertexBuffer->breakStrongReferenceToDevice(); ref pLayout = VertexLayout::create(); @@ -72,7 +72,7 @@ struct FullScreenPass::SharedData static SharedCache sSharedCache; -FullScreenPass::FullScreenPass(ref pDevice, const Program::Desc& progDesc, const DefineList& programDefines) +FullScreenPass::FullScreenPass(ref pDevice, const ProgramDesc& progDesc, const DefineList& programDefines) : BaseGraphicsPass(pDevice, progDesc, programDefines) { // Get shared VB and VAO. @@ -88,9 +88,9 @@ FullScreenPass::FullScreenPass(ref pDevice, const Program::Desc& progDes FullScreenPass::~FullScreenPass() = default; -ref FullScreenPass::create(ref pDevice, const Program::Desc& desc, const DefineList& defines, uint32_t viewportMask) +ref FullScreenPass::create(ref pDevice, const ProgramDesc& desc, const DefineList& defines, uint32_t viewportMask) { - Program::Desc d = desc; + ProgramDesc d = desc; DefineList defs = defines; std::string gs; @@ -113,7 +113,7 @@ ref FullScreenPass::create( uint32_t viewportMask ) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(path).psEntry("main"); return create(pDevice, desc, defines, viewportMask); } diff --git a/Source/Falcor/Core/Pass/FullScreenPass.h b/Source/Falcor/Core/Pass/FullScreenPass.h index e2250798c..633e8c567 100644 --- a/Source/Falcor/Core/Pass/FullScreenPass.h +++ b/Source/Falcor/Core/Pass/FullScreenPass.h @@ -66,7 +66,7 @@ class FALCOR_API FullScreenPass : public BaseGraphicsPass */ static ref create( ref pDevice, - const Program::Desc& desc, + const ProgramDesc& desc, const DefineList& defines = DefineList(), uint32_t viewportMask = 0 ); @@ -81,7 +81,7 @@ class FALCOR_API FullScreenPass : public BaseGraphicsPass virtual void execute(RenderContext* pRenderContext, const ref& pFbo, bool autoSetVpSc = true) const; protected: - FullScreenPass(ref pDevice, const Program::Desc& progDesc, const DefineList& programDefines); + FullScreenPass(ref pDevice, const ProgramDesc& progDesc, const DefineList& programDefines); private: std::shared_ptr mpSharedData; diff --git a/Source/Falcor/Core/Pass/RasterPass.cpp b/Source/Falcor/Core/Pass/RasterPass.cpp index 272a6f96a..dd5b3d69a 100644 --- a/Source/Falcor/Core/Pass/RasterPass.cpp +++ b/Source/Falcor/Core/Pass/RasterPass.cpp @@ -30,7 +30,7 @@ namespace Falcor { -ref RasterPass::create(ref pDevice, const Program::Desc& desc, const DefineList& defines) +ref RasterPass::create(ref pDevice, const ProgramDesc& desc, const DefineList& defines) { return ref(new RasterPass(pDevice, desc, defines)); } @@ -43,12 +43,12 @@ ref RasterPass::create( const DefineList& defines ) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(path).vsEntry(vsEntry).psEntry(psEntry); return create(pDevice, desc, defines); } -RasterPass::RasterPass(ref pDevice, const Program::Desc& progDesc, const DefineList& programDefines) +RasterPass::RasterPass(ref pDevice, const ProgramDesc& progDesc, const DefineList& programDefines) : BaseGraphicsPass(pDevice, progDesc, programDefines) {} diff --git a/Source/Falcor/Core/Pass/RasterPass.h b/Source/Falcor/Core/Pass/RasterPass.h index 3401775a1..38e1bb58f 100644 --- a/Source/Falcor/Core/Pass/RasterPass.h +++ b/Source/Falcor/Core/Pass/RasterPass.h @@ -64,7 +64,7 @@ class FALCOR_API RasterPass : public BaseGraphicsPass * stages. * @return A new object, or throws an exception if creation failed. */ - static ref create(ref pDevice, const Program::Desc& desc, const DefineList& defines = DefineList()); + static ref create(ref pDevice, const ProgramDesc& desc, const DefineList& defines = DefineList()); /** * Ordered draw call. @@ -82,6 +82,6 @@ class FALCOR_API RasterPass : public BaseGraphicsPass void drawIndexed(RenderContext* pRenderContext, uint32_t indexCount, uint32_t startIndexLocation, int32_t baseVertexLocation); protected: - RasterPass(ref pDevice, const Program::Desc& progDesc, const DefineList& programDefines); + RasterPass(ref pDevice, const ProgramDesc& progDesc, const DefineList& programDefines); }; } // namespace Falcor diff --git a/Source/Falcor/Core/Platform/Linux/Linux.cpp b/Source/Falcor/Core/Platform/Linux/Linux.cpp index f22ed8deb..28a8a7cb1 100644 --- a/Source/Falcor/Core/Platform/Linux/Linux.cpp +++ b/Source/Falcor/Core/Platform/Linux/Linux.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Core/Platform/OS.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/GLFW.h" #include "Utils/Logger.h" #include "Utils/StringUtils.h" @@ -93,7 +93,7 @@ uint32_t msgBox( ) { if (!gtk_init_check(0, nullptr)) - throw RuntimeError("Failed to initialize GTK."); + FALCOR_THROW("Failed to initialize GTK."); GtkWidget* pParent = gtk_window_new(GTK_WINDOW_TOPLEVEL); GtkWidget* pDialog = gtk_message_dialog_new( @@ -144,7 +144,7 @@ size_t executeProcess(const std::string& appName, const std::string& commandLine { if (execv(linuxAppName.c_str(), (char* const*)argv.data())) { - throw RuntimeError("Unable to execute process: {} {}", appName, commandLineArgs); + FALCOR_THROW("Unable to execute process: {} {}", appName, commandLineArgs); } } @@ -206,7 +206,7 @@ const std::filesystem::path& getExecutablePath() char pathStr[PATH_MAX] = {0}; if (readlink("/proc/self/exe", pathStr, PATH_MAX) == -1) { - throw RuntimeError("Failed to get the executable path."); + FALCOR_THROW("Failed to get the executable path."); } return std::filesystem::path(pathStr); }() @@ -222,7 +222,7 @@ const std::filesystem::path& getRuntimeDirectory() Dl_info info; if (dladdr((void*)&getRuntimeDirectory, &info) == 0) { - throw RuntimeError("Failed to get the falcor directory. dladdr() failed."); + FALCOR_THROW("Failed to get the falcor directory. dladdr() failed."); } return std::filesystem::path(info.dli_fname).parent_path(); }() @@ -269,7 +269,7 @@ template bool fileDialogCommon(const FileDialogFilterVec& filters, std::filesystem::path& path) { if (!gtk_init_check(0, nullptr)) - throw RuntimeError("Failed to initialize GTK."); + FALCOR_THROW("Failed to initialize GTK."); GtkWidget* pParent = gtk_window_new(GTK_WINDOW_TOPLEVEL); GtkWidget* pDialog = nullptr; @@ -398,7 +398,7 @@ void setKeyboardInterruptHandler(std::function handler) sigemptyset(&action.sa_mask); action.sa_flags = 0; if (sigaction(SIGINT, &action, nullptr) != 0) - throw RuntimeError("Failed to register keyboard interrupt handler"); + FALCOR_THROW("Failed to register keyboard interrupt handler"); } else if (!handler && data.handler) { @@ -407,7 +407,7 @@ void setKeyboardInterruptHandler(std::function handler) sigemptyset(&action.sa_mask); action.sa_flags = 0; if (sigaction(SIGINT, &action, nullptr) != 0) - throw RuntimeError("Failed to unregister keyboard interrupt handler"); + FALCOR_THROW("Failed to unregister keyboard interrupt handler"); } data.handler = handler; } diff --git a/Source/Falcor/Core/Platform/Linux/ProgressBarLinux.cpp b/Source/Falcor/Core/Platform/Linux/ProgressBarLinux.cpp index eebf1afc5..83679dad6 100644 --- a/Source/Falcor/Core/Platform/Linux/ProgressBarLinux.cpp +++ b/Source/Falcor/Core/Platform/Linux/ProgressBarLinux.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Core/Platform/ProgressBar.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include @@ -109,7 +109,7 @@ void ProgressBar::show(const std::string& msg) close(); if (!gtk_init_check(0, nullptr)) - throw RuntimeError("Failed to initialize GTK."); + FALCOR_THROW("Failed to initialize GTK."); mpWindow = std::make_unique(msg); } diff --git a/Source/Falcor/Core/Platform/LockFile.cpp b/Source/Falcor/Core/Platform/LockFile.cpp index 6f6eb7cd2..2a49c1f72 100644 --- a/Source/Falcor/Core/Platform/LockFile.cpp +++ b/Source/Falcor/Core/Platform/LockFile.cpp @@ -55,8 +55,13 @@ bool LockFile::open(const std::filesystem::path& path) { #if FALCOR_WINDOWS mFileHandle = ::CreateFileW( - path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL + path.c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL ); mIsOpen = mFileHandle != INVALID_HANDLE_VALUE; #elif FALCOR_LINUX diff --git a/Source/Falcor/Core/Platform/MonitorInfo.cpp b/Source/Falcor/Core/Platform/MonitorInfo.cpp index 3d719c047..ba3dbde2a 100644 --- a/Source/Falcor/Core/Platform/MonitorInfo.cpp +++ b/Source/Falcor/Core/Platform/MonitorInfo.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -65,7 +65,12 @@ bool GetMonitorSizeFromEDID(const HKEY hDevRegKey, short& WidthMm, short& Height for (LONG i = 0, retValue = ERROR_SUCCESS; retValue != ERROR_NO_MORE_ITEMS; ++i) { retValue = RegEnumValue( - hDevRegKey, i, &valueName[0], &AcutalValueNameLength, NULL, &dwType, + hDevRegKey, + i, + &valueName[0], + &AcutalValueNameLength, + NULL, + &dwType, EDIDdata, // buffer &edidsize // buffer size ); @@ -266,8 +271,14 @@ void MonitorInfo::displayMonitorInfo() for (const auto& desc : getMonitorDescs()) { fmt::print( - "{}{}: {} x {} pix, {:0.1f} x {:0.1f} in, {:0.2f} ppi\n", desc.identifier, desc.isPrimary ? " (Primary) " : " ", - desc.resolution.x, desc.resolution.y, desc.physicalSize.x, desc.physicalSize.y, desc.ppi + "{}{}: {} x {} pix, {:0.1f} x {:0.1f} in, {:0.2f} ppi\n", + desc.identifier, + desc.isPrimary ? " (Primary) " : " ", + desc.resolution.x, + desc.resolution.y, + desc.physicalSize.x, + desc.physicalSize.y, + desc.ppi ); } } diff --git a/Source/Falcor/Core/Platform/OS.cpp b/Source/Falcor/Core/Platform/OS.cpp index 87a0c29ee..cd5233e2f 100644 --- a/Source/Falcor/Core/Platform/OS.cpp +++ b/Source/Falcor/Core/Platform/OS.cpp @@ -26,8 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "OS.h" -#include "SearchDirectories.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Utils/StringUtils.h" #include "Utils/StringFormatters.h" #include // TODO: Replace with C++20 when available. @@ -39,6 +38,13 @@ namespace Falcor { + +const std::filesystem::path& getProjectDirectory() +{ + static std::filesystem::path directory(FALCOR_PROJECT_DIR); + return directory; +} + const std::filesystem::path& getExecutableDirectory() { static std::filesystem::path directory{getExecutablePath().parent_path()}; @@ -53,13 +59,11 @@ const std::string& getExecutableName() inline std::vector getInitialShaderDirectories() { - std::filesystem::path projectDir(_PROJECT_DIR_); - std::vector developmentDirectories = { // First we search in source folders. - projectDir, - projectDir / "..", - projectDir / ".." / "Tools" / "FalcorTest", + getProjectDirectory() / "Source" / "Falcor", + getProjectDirectory() / "Source", + getProjectDirectory() / "Source" / "Tools" / "FalcorTest", // Then we search in deployment folder (necessary to pickup NVAPI and other third-party shaders). getRuntimeDirectory() / "shaders", }; @@ -75,21 +79,9 @@ static std::vector gShaderDirectories = getInitialShaderD inline std::vector getInitialDataDirectories() { - std::filesystem::path projectDir(_PROJECT_DIR_); - - std::vector developmentDirectories = { - projectDir / ".." / ".." / "data", - getRuntimeDirectory() / "data", - }; + std::vector directories; - std::vector deploymentDirectories = {getRuntimeDirectory() / "data"}; - - std::vector directories = isDevelopmentMode() ? developmentDirectories : deploymentDirectories; - - // Add development media folder. - directories.push_back(projectDir / ".." / ".." / "media"); - - // Add additional media folders. + // Add media folders. if (auto mediaFolders = getEnvironmentVariable("FALCOR_MEDIA_FOLDERS")) { auto folders = splitString(*mediaFolders, ";"); @@ -101,43 +93,6 @@ inline std::vector getInitialDataDirectories() static std::vector gDataDirectories = getInitialDataDirectories(); // TODO: REMOVEGLOBAL -const std::vector& getDataDirectoriesList() -{ - return gDataDirectories; -} - -void addDataDirectory(const std::filesystem::path& dir, bool addToFront) -{ - FALCOR_CHECK_ARG_MSG(!dir.empty(), "Do not add empty directories to search paths"); - - if (std::find_if( - gDataDirectories.begin(), gDataDirectories.end(), [&dir](const std::filesystem::path& d) { return isSamePath(dir, d); } - ) == gDataDirectories.end()) - { - if (addToFront) - { - gDataDirectories.insert(gDataDirectories.begin(), dir); - } - else - { - gDataDirectories.push_back(dir); - } - } -} - -void removeDataDirectory(const std::filesystem::path& dir) -{ - FALCOR_CHECK_ARG_MSG(!dir.empty(), "Do not remove empty directories to search paths"); - - auto it = std::find_if( - gDataDirectories.begin(), gDataDirectories.end(), [&dir](const std::filesystem::path& d) { return isSamePath(dir, d); } - ); - if (it != gDataDirectories.end()) - { - gDataDirectories.erase(it); - } -} - bool isDevelopmentMode() { static bool devMode = []() @@ -154,41 +109,27 @@ bool isSamePath(const std::filesystem::path& lhs, const std::filesystem::path& r return std::filesystem::weakly_canonical(lhs) == std::filesystem::weakly_canonical(rhs); } -bool findFileInDataDirectories(const std::filesystem::path& path, std::filesystem::path& fullPath) +std::filesystem::path findFileInDirectories(const std::filesystem::path& path, fstd::span directories) { - return findFileInDirectories(path, fullPath, getDataDirectoriesList()); -} - -bool findFileInDirectories(const std::filesystem::path& path, std::filesystem::path& fullPath, const SearchDirectories& directories) -{ - // Check if this is an absolute path. - if (path.is_absolute()) - { - if (std::filesystem::exists(path)) - { - fullPath = std::filesystem::canonical(path); - return true; - } - } + // Check if this is an existing an absolute path. + if (path.is_absolute() && std::filesystem::exists(path)) + return std::filesystem::canonical(path); // Search in other paths. - for (const auto& dir : directories.get()) + for (const auto& dir : directories) { - fullPath = dir / path; + std::filesystem::path fullPath = dir / path; if (std::filesystem::exists(fullPath)) - { - fullPath = std::filesystem::canonical(fullPath); - return true; - } + return std::filesystem::canonical(fullPath); } - return false; + return {}; } std::vector globFilesInDirectory( const std::filesystem::path& path, const std::regex& regexPattern, - bool firstHitOnly + bool firstMatchOnly ) { std::vector result; @@ -202,7 +143,7 @@ std::vector globFilesInDirectory( if (std::regex_match(filename, regexPattern)) { result.push_back(entry.path()); - if (firstHitOnly) + if (firstMatchOnly) return result; } } @@ -210,19 +151,11 @@ std::vector globFilesInDirectory( return result; } -std::vector globFilesInDataDirectories( - const std::filesystem::path& path, - const std::regex& regexPattern, - bool firstHitOnly -) -{ - return globFilesInDirectories(path, regexPattern, getDataDirectoriesList(), firstHitOnly); -} std::vector globFilesInDirectories( const std::filesystem::path& path, const std::regex& regexPattern, - const SearchDirectories& directories, - bool firstHitOnly + fstd::span directories, + bool firstMatchOnly ) { std::vector result; @@ -230,19 +163,19 @@ std::vector globFilesInDirectories( // Check if this is an absolute path. if (path.is_absolute()) { - result = globFilesInDirectory(path, regexPattern, firstHitOnly); + result = globFilesInDirectory(path, regexPattern, firstMatchOnly); } else { // Search in other paths. - for (const auto& dir : directories.get()) + for (const auto& dir : directories) { - auto fullPath = dir / path; - std::vector local = globFilesInDirectory(fullPath, regexPattern, firstHitOnly); + std::filesystem::path fullPath = dir / path; + std::vector local = globFilesInDirectory(fullPath, regexPattern, firstMatchOnly); result.reserve(result.size() + local.size()); for (auto&& it : local) result.push_back(std::move(it)); - if (firstHitOnly && !result.empty()) + if (firstMatchOnly && !result.empty()) break; } } @@ -293,27 +226,7 @@ std::filesystem::path findAvailableFilename(const std::string& prefix, const std if (!std::filesystem::exists(path)) return path; } - throw RuntimeError("Failed to find available filename."); -} - -std::filesystem::path stripDataDirectories(const std::filesystem::path& path) -{ - if (path.is_relative()) - return path; - - auto canonicalPath = std::filesystem::weakly_canonical(path); - - for (const auto& dir : gDataDirectories) - { - auto canonicalDir = std::filesystem::weakly_canonical(dir); - - if (hasPrefix(canonicalPath.string(), canonicalDir.string())) - { - return std::filesystem::relative(canonicalPath, canonicalDir); - } - } - - return path; + FALCOR_THROW("Failed to find available filename."); } bool hasExtension(const std::filesystem::path& path, std::string_view ext) @@ -352,7 +265,7 @@ std::filesystem::path getTempFilePath() char* name = std::tmpnam(nullptr); if (name == nullptr) { - throw RuntimeError("Failed to create temporary file path."); + FALCOR_THROW("Failed to create temporary file path."); } return name; } @@ -361,7 +274,7 @@ std::string readFile(const std::filesystem::path& path) { std::ifstream ifs(path, std::ios::binary); if (!ifs) - throw RuntimeError("Failed to read from file '{}'.", path); + FALCOR_THROW("Failed to read from file '{}'.", path); return std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); } @@ -372,7 +285,7 @@ std::string decompressFile(const std::filesystem::path& path) z_stream zs = {}; // MAX_WBITS | 32 to support both zlib or gzip files. if (inflateInit2(&zs, MAX_WBITS | 32) != Z_OK) - throw RuntimeError("inflateInit2 failed while decompressing."); + FALCOR_THROW("inflateInit2 failed while decompressing."); zs.next_in = reinterpret_cast(compressed.data()); zs.avail_in = (uInt)compressed.size(); @@ -403,7 +316,7 @@ std::string decompressFile(const std::filesystem::path& path) // Check for errors. if (ret != Z_STREAM_END) { - throw RuntimeError("Failure to decompress file '{}' (error: {}).", path, ret); + FALCOR_THROW("Failure to decompress file '{}' (error: {}).", path, ret); } return decompressed; @@ -411,6 +324,10 @@ std::string decompressFile(const std::filesystem::path& path) std::string getStackTrace(size_t skip, size_t maxDepth) { + // We need to initialize the resolver before taking the stack trace, + // otherwise we get invalid stack traces. + backward::TraceResolver resolver; + // Capture stack trace. backward::StackTrace st; st.load_here(maxDepth == 0 ? 1000 : maxDepth); @@ -418,7 +335,6 @@ std::string getStackTrace(size_t skip, size_t maxDepth) // We implement our own stack trace formatting here as the default printer in backward is not printing // source locations in a way that is parsable by typical IDEs (file:line). - backward::TraceResolver resolver; resolver.load_stacktrace(st); std::string result; for (size_t i = 0; i < st.size(); ++i) diff --git a/Source/Falcor/Core/Platform/OS.h b/Source/Falcor/Core/Platform/OS.h index 999db7131..4af95eaf3 100644 --- a/Source/Falcor/Core/Platform/OS.h +++ b/Source/Falcor/Core/Platform/OS.h @@ -28,6 +28,7 @@ #pragma once #include "PlatformHandles.h" #include "Core/Macros.h" +#include #include #include #include @@ -36,8 +37,6 @@ namespace Falcor { -class SearchDirectories; - /** * Utility class to start/stop OS services. */ @@ -154,17 +153,12 @@ FALCOR_API uint32_t msgBox( FALCOR_API bool isSamePath(const std::filesystem::path& lhs, const std::filesystem::path& rhs); /** - * Finds a file in one of the data search directories. + * Finds a file in a list of search directories. * @param[in] path The file path to look for. - * @param[in] fullPath If the file was found, the full path to the file. If the file wasn't found, this is invalid. - * @return Returns true if the file was found, false otherwise. + * @param[in] directories List of directories to search in. + * @return Returns the found path, or an empty path if not found. */ -FALCOR_API bool findFileInDataDirectories(const std::filesystem::path& path, std::filesystem::path& fullPath); -FALCOR_API bool findFileInDirectories( - const std::filesystem::path& path, - std::filesystem::path& fullPath, - const SearchDirectories& directories -); +FALCOR_API std::filesystem::path findFileInDirectories(const std::filesystem::path& path, fstd::span directories); /** * Finds all files in given (absolute or relative path), whose filename matches the given regexPattern. @@ -173,13 +167,13 @@ FALCOR_API bool findFileInDirectories( * * @param[in] path Directory path to look in. * @param[in] regexPattern C++ regular extension pattern to match with the files - * @param[in] firstHitOnly Set true when you want only the first file matching the pattern. + * @param[in] firstMatchOnly Set true when you want only the first file matching the pattern. * @return All paths to the resolved files in the form path / . */ FALCOR_API std::vector globFilesInDirectory( const std::filesystem::path& path, const std::regex& regexPattern, - bool firstHitOnly = false + bool firstMatchOnly = false ); /** @@ -188,19 +182,14 @@ FALCOR_API std::vector globFilesInDirectory( * * @param[in] path Directory (sub)path to look in. * @param[in] regexPattern C++ regular extension pattern to match with the files - * @param[in] firstHitOnly Set true when you want only the first file matching the pattern. + * @param[in] firstMatchOnly Set true when you want only the first file matching the pattern. * @return Full paths to all files matching the pattern. */ -FALCOR_API std::vector globFilesInDataDirectories( - const std::filesystem::path& path, - const std::regex& regexPattern, - bool firstHitOnly = false -); FALCOR_API std::vector globFilesInDirectories( const std::filesystem::path& path, const std::regex& regexPattern, - const SearchDirectories& directories, - bool firstHitOnly = false + fstd::span directories, + bool firstMatchOnly = false ); /** @@ -218,12 +207,6 @@ FALCOR_API bool findFileInShaderDirectories(const std::filesystem::path& path, s */ FALCOR_API const std::vector& getShaderDirectoriesList(); -/** - * Given a path, returns the shortest possible path to the file relative to the data directories. - * If the path is not relative to the data directories, return the original path. - */ -FALCOR_API std::filesystem::path stripDataDirectories(const std::filesystem::path& path); - /** * Structure to help with file dialog file-extension filters */ @@ -310,6 +293,13 @@ FALCOR_API bool isProcessRunning(size_t processID); */ FALCOR_API void terminateProcess(size_t processID); +/** + * Get the full path to the Falcor project directory. + * Note: This is only useful during development. + * @return The full path of the project directory. + */ +FALCOR_API const std::filesystem::path& getProjectDirectory(); + /** * Get the full path to the current executable. * @return The full path of the executable. @@ -350,24 +340,6 @@ FALCOR_API const std::filesystem::path& getHomeDirectory(); */ FALCOR_API std::optional getEnvironmentVariable(const std::string& varName); -/** - * Get a list of all recorded data directories. - */ -FALCOR_API const std::vector& getDataDirectoriesList(); - -/** - * Adds a folder to data search directories. Once added, calls to findFileInDataDirectories() will search that directory as well. - * @param[in] dir The new directory to add to the data search directories. - * @param[in] addToFront Add the new directory to the front of the list, making it the highest priority. - */ -FALCOR_API void addDataDirectory(const std::filesystem::path& dir, bool addToFront = false); - -/** - * Removes a folder from the data search directories - * @param[in] dir The directory name to remove from the data search directories. - */ -FALCOR_API void removeDataDirectory(const std::filesystem::path& dir); - /** * Find a new filename based on the supplied parameters. This function doesn't actually create the file, just find an available file name. * @param[in] prefix Requested file prefix. diff --git a/Source/Falcor/Core/Platform/SearchDirectories.h b/Source/Falcor/Core/Platform/SearchDirectories.h deleted file mode 100644 index ec862f28f..000000000 --- a/Source/Falcor/Core/Platform/SearchDirectories.h +++ /dev/null @@ -1,57 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#pragma once -#include -#include -#include - -namespace Falcor -{ -/** - * Set of search directories, kept unique through merges - */ -class SearchDirectories -{ -public: - SearchDirectories() = default; - SearchDirectories(std::vector searchDirectories) : mSearchDirectories(std::move(searchDirectories)) {} - - void merge(const std::vector& searchDirectories) - { - mSearchDirectories.insert(mSearchDirectories.end(), searchDirectories.begin(), searchDirectories.end()); - std::sort(mSearchDirectories.begin(), mSearchDirectories.end()); - mSearchDirectories.erase(std::unique(mSearchDirectories.begin(), mSearchDirectories.end()), mSearchDirectories.end()); - } - - const std::vector& get() const { return mSearchDirectories; } - std::vector& get() { return mSearchDirectories; } - -private: - std::vector mSearchDirectories; -}; -} // namespace Falcor diff --git a/Source/Falcor/Core/Platform/Windows/Windows.cpp b/Source/Falcor/Core/Platform/Windows/Windows.cpp index eee1c2638..0c35e901b 100644 --- a/Source/Falcor/Core/Platform/Windows/Windows.cpp +++ b/Source/Falcor/Core/Platform/Windows/Windows.cpp @@ -26,8 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Core/Platform/OS.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Utils/Logger.h" #include "Utils/StringUtils.h" @@ -48,15 +47,6 @@ #include #include -#define os_call(a) \ - { \ - auto hr_ = a; \ - if (FAILED(hr_)) \ - { \ - reportError(#a); \ - } \ - } - // Always run in Optimus mode on laptops extern "C" { @@ -208,6 +198,16 @@ uint32_t msgBox( config.pButtons = buttonConfigs.data(); config.nDefaultButton = defaultId; config.cxWidth = dialogWidth; + // We want the message box window to be the top most window. + config.pfCallback = [](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData) -> HRESULT + { + if (msg == TDN_CREATED) + { + // Make this dialog the top most window, but don't change position/size. + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } + return 0; + }; // By default return the id of the last button. uint32_t result = buttons.back().id; @@ -341,8 +341,13 @@ bool deleteJunction(const std::filesystem::path& link) { // Open file handle to junction directory. HANDLE handle = CreateFileW( - link.native().c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, - FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL + link.native().c_str(), + GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, + NULL ); if (handle == INVALID_HANDLE_VALUE) return false; @@ -367,7 +372,7 @@ const std::filesystem::path& getExecutablePath() CHAR pathStr[1024]; if (GetModuleFileNameA(nullptr, pathStr, ARRAYSIZE(pathStr)) == 0) { - throw RuntimeError("Failed to get the executable path."); + FALCOR_THROW("Failed to get the executable path."); } return std::filesystem::path(pathStr); }() @@ -385,12 +390,12 @@ const std::filesystem::path& getRuntimeDirectory() GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)&getRuntimeDirectory, &hm ) == 0) { - throw RuntimeError("Failed to get the falcor directory. GetModuleHandle failed, error = {}.", GetLastError()); + FALCOR_THROW("Failed to get the falcor directory. GetModuleHandle failed, error = {}.", GetLastError()); } CHAR pathStr[1024]; if (GetModuleFileNameA(hm, pathStr, sizeof(pathStr)) == 0) { - throw RuntimeError("Failed to get the falcor directory. GetModuleFileNameA failed, error = {}.", GetLastError()); + FALCOR_THROW("Failed to get the falcor directory. GetModuleFileNameA failed, error = {}.", GetLastError()); } return std::filesystem::path(pathStr).parent_path(); }() @@ -406,7 +411,7 @@ const std::filesystem::path& getAppDataDirectory() PWSTR pathStr; if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &pathStr))) { - throw RuntimeError("Failed to get the application data directory."); + FALCOR_THROW("Failed to get the application data directory."); } return std::filesystem::path(pathStr); }() @@ -513,7 +518,8 @@ static bool fileDialogCommon(const FileDialogFilterVec& filters, std::filesystem FilterSpec fs(filters, typeid(DialogType) == typeid(IFileOpenDialog)); DialogType* pDialog; - os_call(CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pDialog))); + if (FAILED(CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pDialog)))) + FALCOR_THROW("Failed to create file dialog."); pDialog->SetOptions(options | FOS_FORCEFILESYSTEM); pDialog->SetFileTypes((uint32_t)fs.size(), fs.data()); pDialog->SetDefaultExtension(fs.data()->pszSpec); @@ -556,7 +562,7 @@ void setWindowIcon(const std::filesystem::path& path, WindowHandle windowHandle) { HANDLE hIcon = LoadImageW(GetModuleHandleW(NULL), path.c_str(), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE); if (!hIcon) - throw RuntimeError("Failed to load icon from '{}'.", path); + FALCOR_THROW("Failed to load icon from '{}'.", path); HWND hWnd = windowHandle ? static_cast(windowHandle) : GetActiveWindow(); SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); } @@ -660,12 +666,12 @@ void setKeyboardInterruptHandler(std::function handler) if (handler && !data.handler) { if (SetConsoleCtrlHandler(consoleCtrlHandler, TRUE) == 0) - throw RuntimeError("Failed to register keyboard interrupt handler"); + FALCOR_THROW("Failed to register keyboard interrupt handler"); } else if (!handler && data.handler) { if (SetConsoleCtrlHandler(consoleCtrlHandler, FALSE) == 0) - throw RuntimeError("Failed to unregister keyboard interrupt handler"); + FALCOR_THROW("Failed to unregister keyboard interrupt handler"); } data.handler = handler; } @@ -691,11 +697,19 @@ size_t executeProcess(const std::string& appName, const std::string& commandLine STARTUPINFOA startupInfo{}; PROCESS_INFORMATION processInformation{}; if (!CreateProcessA( - nullptr, (LPSTR)commandLine.c_str(), nullptr, nullptr, TRUE, NORMAL_PRIORITY_CLASS, nullptr, nullptr, &startupInfo, + nullptr, + (LPSTR)commandLine.c_str(), + nullptr, + nullptr, + TRUE, + NORMAL_PRIORITY_CLASS, + nullptr, + nullptr, + &startupInfo, &processInformation )) { - throw RuntimeError("Unable to execute process: {} {}", appName, commandLineArgs); + FALCOR_THROW("Unable to execute process: {} {}", appName, commandLineArgs); } return reinterpret_cast(processInformation.hProcess); @@ -729,8 +743,13 @@ static void checkFileModifiedStatus(const std::filesystem::path& path, const std auto dir = path.parent_path(); HANDLE hFile = CreateFileW( - dir.native().c_str(), GENERIC_READ | FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL + dir.native().c_str(), + GENERIC_READ | FILE_LIST_DIRECTORY, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, + NULL ); FALCOR_ASSERT(hFile != INVALID_HANDLE_VALUE); @@ -745,8 +764,14 @@ static void checkFileModifiedStatus(const std::filesystem::path& path, const std buffer.resize(1024); if (!ReadDirectoryChangesW( - hFile, buffer.data(), static_cast(sizeof(uint32_t) * buffer.size()), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, 0, - &overlapped, nullptr + hFile, + buffer.data(), + static_cast(sizeof(uint32_t) * buffer.size()), + FALSE, + FILE_NOTIFY_CHANGE_LAST_WRITE, + 0, + &overlapped, + nullptr )) { logError("Failed to read directory changes for shared file '{}'. Aborting monitoring.", path); @@ -831,8 +856,13 @@ void setThreadAffinity(std::thread::native_handle_type thread, uint32_t affinity { LPVOID lpMsgBuf; FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dwError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, + 0, + NULL ); std::wstring err((LPTSTR)lpMsgBuf); logWarning("setThreadAffinity failed with error: {}", wstring_2_string(err)); @@ -855,8 +885,13 @@ void setThreadPriority(std::thread::native_handle_type thread, ThreadPriorityTyp { LPVOID lpMsgBuf; FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwError, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dwError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, + 0, + NULL ); std::wstring err((LPTSTR)lpMsgBuf); logWarning("setThreadPriority failed with error: {}", wstring_2_string(err)); @@ -869,7 +904,7 @@ time_t getFileModifiedTime(const std::filesystem::path& path) struct stat s; if (stat(path.string().c_str(), &s) != 0) { - throw RuntimeError("Can't get file time for '{}'.", path); + FALCOR_THROW("Can't get file time for '{}'.", path); } return s.st_mtime; @@ -945,14 +980,27 @@ void* getProcAddress(SharedLibraryHandle library, const std::string& funcName) return reinterpret_cast(GetProcAddress(static_cast(library), funcName.c_str())); } +static std::mutex sOSServicesInitMutex; +static uint32_t sOSServicesInitCount = 0; + void OSServices::start() { - os_call(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)); + std::lock_guard lock(sOSServicesInitMutex); + if (sOSServicesInitCount++ == 0) + { + if (FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) + FALCOR_THROW("Failed to initialize COM library."); + } } void OSServices::stop() { - CoUninitialize(); + std::lock_guard lock(sOSServicesInitMutex); + uint32_t count = sOSServicesInitCount--; + if (count == 1) + CoUninitialize(); + else if (count == 0) + FALCOR_THROW("OSServices::stop() called more times than OSServices::start()."); } size_t getCurrentRSS() diff --git a/Source/Falcor/Core/Plugin.cpp b/Source/Falcor/Core/Plugin.cpp index 9be5be27f..21fd13a4f 100644 --- a/Source/Falcor/Core/Plugin.cpp +++ b/Source/Falcor/Core/Plugin.cpp @@ -63,17 +63,17 @@ bool PluginManager::loadPlugin(const std::filesystem::path& path) } if (!std::filesystem::exists(path)) - throw RuntimeError("Failed to load plugin library from {}. File not found.", path); + FALCOR_THROW("Failed to load plugin library from {}. File not found.", path); SharedLibraryHandle library = loadSharedLibrary(path); if (library == nullptr) - throw RuntimeError("Failed to load plugin library from {}. Cannot load shared library.", path); + FALCOR_THROW("Failed to load plugin library from {}. Cannot load shared library.", path); using RegisterPluginProc = void (*)(PluginRegistry&); auto registerPluginProc = (RegisterPluginProc)getProcAddress(library, "registerPlugin"); if (registerPluginProc == nullptr) - throw RuntimeError("Failed to load plugin library from {}. Symbol 'registerPlugin' not found.", path); + FALCOR_THROW("Failed to load plugin library from {}. Symbol 'registerPlugin' not found.", path); // Register plugin library. { diff --git a/Source/Falcor/Core/Plugin.h b/Source/Falcor/Core/Plugin.h index 14ad20175..f6b43ebb0 100644 --- a/Source/Falcor/Core/Plugin.h +++ b/Source/Falcor/Core/Plugin.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -28,7 +28,7 @@ #pragma once #include "Core/Macros.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/Platform/OS.h" #include @@ -225,7 +225,7 @@ class FALCOR_API PluginManager std::lock_guard lock(mClassDescsMutex); if (findClassDesc(type) != nullptr) - throw RuntimeError( + FALCOR_THROW( "A plugin class with type name '{}' (base class type '{}') has already been registered.", type, BaseT::getPluginBaseType() ); diff --git a/Source/Falcor/Core/Program/ComputeProgram.h b/Source/Falcor/Core/Program/ComputeProgram.h deleted file mode 100644 index f260158a1..000000000 --- a/Source/Falcor/Core/Program/ComputeProgram.h +++ /dev/null @@ -1,87 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#pragma once -#include "Program.h" -#include "Core/Macros.h" -#include "Utils/Math/Vector.h" -#include -#include -#include - -namespace Falcor -{ -class ComputeContext; -class ComputeVars; - -/** - * Compute program. See GraphicsProgram to manage graphics programs. - */ -class FALCOR_API ComputeProgram : public Program -{ -public: - ~ComputeProgram() = default; - - /** - * Create a new compute program from file. - * Note that this call merely creates a program object. The actual compilation and link happens at a later time. - * @param[in] pDevice GPU device. - * @param[in] path Compute program file path. - * @param[in] csEntry Name of the entry point in the program. - * @param[in] programDefines Optional list of macro definitions to set into the program. - * @param[in] flags Optional program compilation flags. - * @param[in] shaderModel Optional string describing which shader model to use. - * @return A new object, or an exception is thrown if creation failed. - */ - static ref createFromFile( - ref pDevice, - const std::filesystem::path& path, - const std::string& csEntry, - const DefineList& programDefines = DefineList(), - CompilerFlags flags = CompilerFlags::None, - const std::string& shaderModel = "" - ); - - /** - * Create a new compute program. - * Note that this call merely creates a program object. The actual compilation and link happens at a later time. - * @param[in] pDevice GPU device. - * @param[in] desc The program description. - * @param[in] programDefines Optional list of macro definitions to set into the program. - * @return A new object, or an exception is thrown if creation failed. - */ - static ref create(ref pDevice, const Desc& desc, const DefineList& programDefines = DefineList()); - - /** - * Dispatch the program using the argument values set in `pVars`. - */ - virtual void dispatchCompute(ComputeContext* pContext, ComputeVars* pVars, const uint3& threadGroupCount); - -protected: - ComputeProgram(ref pDevice, const Desc& desc, const DefineList& programDefines); -}; -} // namespace Falcor diff --git a/Source/Falcor/Core/Program/GraphicsProgram.h b/Source/Falcor/Core/Program/GraphicsProgram.h deleted file mode 100644 index 95839311b..000000000 --- a/Source/Falcor/Core/Program/GraphicsProgram.h +++ /dev/null @@ -1,78 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#pragma once -#include "Program.h" -#include "Core/Macros.h" -#include -#include -#include - -namespace Falcor -{ -/** - * Graphics program. See ComputeProgram to manage compute programs. - */ -class FALCOR_API GraphicsProgram : public Program -{ -public: - ~GraphicsProgram() = default; - - /** - * Create a new graphics program. - * Note that this call merely creates a program object. The actual compilation and link happens at a later time. - * @param[in] pDevice GPU device. - * @param[in] desc Description of the source files and entry points to use. - * @param[in] programDefines Optional list of macro definitions to set into the program. The macro definitions will be set on all shader - * stages. - * @return A new object, or an exception is thrown if creation failed. - */ - static ref create(ref pDevice, const Desc& desc, const DefineList& programDefines = DefineList()); - - /** - * Create a new graphics program from file. - * @param[in] pDevice GPU device. - * @param[in] path Graphics program file path. - * @param[in] vsEntry Vertex shader entry point. If this string is empty (""), it will use a default vertex shader, which transforms and - * outputs all default vertex attributes. - * @param[in] psEntry Pixel shader entry point - * @param[in] programDefines Optional list of macro definitions to set into the program. The macro definitions will be set on all shader - * stages. - * @return A new object, or an exception is thrown if creation failed. - */ - static ref createFromFile( - ref pDevice, - const std::filesystem::path& path, - const std::string& vsEntry, - const std::string& psEntry, - const DefineList& programDefines = DefineList() - ); - -private: - GraphicsProgram(ref pDevice, const Desc& desc, const DefineList& programDefines); -}; -} // namespace Falcor diff --git a/Source/Falcor/Core/Program/Program.cpp b/Source/Falcor/Core/Program/Program.cpp index cd3dc66f6..d71a1fb09 100644 --- a/Source/Falcor/Core/Program/Program.cpp +++ b/Source/Falcor/Core/Program/Program.cpp @@ -28,13 +28,17 @@ #include "Program.h" #include "ProgramManager.h" #include "ProgramVars.h" +#include "Core/Error.h" #include "Core/ObjectPython.h" #include "Core/Platform/OS.h" #include "Core/API/Device.h" #include "Core/API/ParameterBlock.h" +#include "Core/API/RtStateObject.h" +#include "Core/API/PythonHelpers.h" #include "Utils/StringUtils.h" #include "Utils/Logger.h" #include "Utils/Timing/CpuTimer.h" +#include "Utils/CryptoUtils.h" #include "Utils/Scripting/ScriptBindings.h" #include @@ -43,151 +47,39 @@ namespace Falcor { -const std::string kSupportedShaderModels[] = { - "6_0", "6_1", "6_2", "6_3", "6_4", "6_5", -#if FALCOR_HAS_D3D12_AGILITY_SDK - "6_6", -#endif -}; -Program::Desc::Desc() = default; - -Program::Desc::Desc(const std::filesystem::path& path) -{ - addShaderLibrary(path); -} - -Program::Desc& Program::Desc::setLanguagePrelude(const std::string_view prelude) -{ - mLanguagePrelude = prelude; - return *this; -} - -Program::Desc& Program::Desc::addShaderModule(const ShaderModule& src) -{ - if (src.type == ShaderModule::Type::String && src.createTranslationUnit && src.moduleName.empty()) - { - // Warn if module name is left empty when creating a new translation unit from string. - // This is valid, but unexpected so issue a warning. - logWarning("addShaderModule() - Creating a new translation unit, but missing module name. Is this intended?"); - } - - SourceEntryPoints source(src); - mActiveSource = (int32_t)mSources.size(); - mSources.emplace_back(std::move(source)); - - return *this; -} - -Program::Desc& Program::Desc::addShaderModules(const ShaderModuleList& modules) -{ - for (const auto& module : modules) - { - addShaderModule(module); - } - return *this; -} - -Program::Desc& Program::Desc::beginEntryPointGroup(const std::string& entryPointNameSuffix) -{ - mActiveGroup = (int32_t)mGroups.size(); - mGroups.push_back(EntryPointGroup()); - mGroups[mActiveGroup].nameSuffix = entryPointNameSuffix; - - return *this; -} - -Program::Desc& Program::Desc::entryPoint(ShaderType shaderType, const std::string& name) +void ProgramDesc::finalize() { - checkArgument(!name.empty(), "Missing entry point name."); - - if (mActiveGroup < 0) - { - beginEntryPointGroup(); - } - - uint32_t entryPointIndex = declareEntryPoint(shaderType, name); - mGroups[mActiveGroup].entryPoints.push_back(entryPointIndex); - return *this; + uint32_t globalIndex = 0; + for (auto& entryPointGroup : entryPointGroups) + for (auto& entryPoint : entryPointGroup.entryPoints) + entryPoint.globalIndex = globalIndex++; } -bool Program::Desc::hasEntryPoint(ShaderType stage) const +Program::Program(ref pDevice, ProgramDesc desc, DefineList defineList) + : mpDevice(std::move(pDevice)), mDesc(std::move(desc)), mDefineList(std::move(defineList)), mTypeConformanceList(mDesc.typeConformances) { - for (auto& entryPoint : mEntryPoints) - { - if (entryPoint.stage == stage) - { - return true; - } - } - return false; -} - -Program::Desc& Program::Desc::addTypeConformancesToGroup(const TypeConformanceList& typeConformances) -{ - FALCOR_ASSERT(mActiveGroup >= 0); - mGroups[mActiveGroup].typeConformances.add(typeConformances); - return *this; -} - -uint32_t Program::Desc::declareEntryPoint(ShaderType type, const std::string& name) -{ - FALCOR_ASSERT(!name.empty()); - FALCOR_ASSERT(mActiveGroup >= 0 && mActiveGroup < mGroups.size()); - - if (mActiveSource < 0) - { - throw RuntimeError("Cannot declare an entry point without first adding a source file/library"); - } + mDesc.finalize(); - EntryPoint entryPoint; - entryPoint.stage = type; - entryPoint.name = name; - entryPoint.exportName = name + mGroups[mActiveGroup].nameSuffix; - entryPoint.sourceIndex = mActiveSource; - entryPoint.groupIndex = mActiveGroup; + // If not shader model was requested, use the default shader model for the device. + if (mDesc.shaderModel == ShaderModel::Unknown) + mDesc.shaderModel = mpDevice->getDefaultShaderModel(); - uint32_t index = (uint32_t)mEntryPoints.size(); - mEntryPoints.push_back(entryPoint); - mSources[mActiveSource].entryPoints.push_back(index); + // Check that shader model is supported on the device. + if (!mpDevice->isShaderModelSupported(mDesc.shaderModel)) + FALCOR_THROW("Requested Shader Model {} is not supported by the device", enumToString(mDesc.shaderModel)); - return index; -} - -Program::Desc& Program::Desc::setShaderModel(const std::string& sm) -{ - // Check that the model is supported - bool b = false; - for (size_t i = 0; i < std::size(kSupportedShaderModels); i++) + if (mDesc.hasEntryPoint(ShaderType::RayGeneration)) { - if (kSupportedShaderModels[i] == sm) - { - b = true; - break; - } + if (desc.maxTraceRecursionDepth == uint32_t(-1)) + FALCOR_THROW("Can't create a raytacing program without specifying maximum trace recursion depth"); + if (desc.maxPayloadSize == uint32_t(-1)) + FALCOR_THROW("Can't create a raytacing program without specifying maximum ray payload size"); } - if (b == false) - { - std::string warn = "Unsupported shader-model '" + sm + "' requested. Supported shader-models are "; - for (size_t i = 0; i < std::size(kSupportedShaderModels); i++) - { - warn += kSupportedShaderModels[i]; - warn += (i == kSupportedShaderModels->size() - 1) ? "." : ", "; - } - warn += "\nThis is not an error, but if something goes wrong try using one of the supported models."; - logWarning(warn); - } - - mShaderModel = sm; - return *this; -} + validateEntryPoints(); -Program::Program(ref pDevice, const Desc& desc, const DefineList& defineList) - : mpDevice(pDevice), mDesc(desc), mDefineList(defineList), mTypeConformanceList(desc.mTypeConformances) -{ mpDevice->getProgramManager()->registerProgramForReload(this); - validateEntryPoints(); } Program::~Program() @@ -205,11 +97,14 @@ void Program::validateEntryPoints() const // They don't necessarily have to be, but it could be an indication of the program not created correctly. using NameTypePair = std::pair; std::set entryPointNamesAndTypes; - for (const auto& e : mDesc.mEntryPoints) + for (const auto& group : mDesc.entryPointGroups) { - if (!entryPointNamesAndTypes.insert(NameTypePair(e.exportName, e.stage)).second) + for (const auto& e : group.entryPoints) { - logWarning("Duplicate program entry points '{}' of type '{}'.", e.exportName, to_string(e.stage)); + if (!entryPointNamesAndTypes.insert(NameTypePair(e.exportName, e.type)).second) + { + logWarning("Duplicate program entry points '{}' of type '{}'.", e.exportName, e.type); + } } } } @@ -218,36 +113,37 @@ std::string Program::getProgramDescString() const { std::string desc; - int32_t groupCount = (int32_t)mDesc.mGroups.size(); - - for (size_t i = 0; i < mDesc.mSources.size(); i++) + for (const auto& shaderModule : mDesc.shaderModules) { - const auto& src = mDesc.mSources[i]; - if (i != 0) - desc += " "; - switch (src.getType()) + for (const auto& source : shaderModule.sources) { - case ShaderModule::Type::File: - desc += src.source.filePath.string(); - break; - case ShaderModule::Type::String: - desc += "Created from string"; - break; - default: - FALCOR_UNREACHABLE(); + switch (source.type) + { + case ProgramDesc::ShaderSource::Type::File: + desc += source.path.string(); + break; + case ProgramDesc::ShaderSource::Type::String: + desc += ""; + break; + default: + FALCOR_UNREACHABLE(); + } + desc += " "; } + } - desc += "("; - for (size_t ee = 0; ee < src.entryPoints.size(); ++ee) + desc += "("; + size_t entryPointIndex = 0; + for (const auto& entryPointGroup : mDesc.entryPointGroups) + { + for (const auto& entryPoint : entryPointGroup.entryPoints) { - auto& entryPoint = mDesc.mEntryPoints[src.entryPoints[ee]]; - - if (ee != 0) + if (entryPointIndex++ > 0) desc += ", "; desc += entryPoint.exportName; } - desc += ")"; } + desc += ")"; return desc; } @@ -403,7 +299,7 @@ const ref& Program::getActiveVersion() const // On error we get false, and mActiveProgram points to the last successfully compiled version. if (link() == false) { - throw RuntimeError("Program linkage failed"); + FALCOR_THROW("Program linkage failed"); } else { @@ -430,17 +326,17 @@ bool Program::link() const if (pVersion == nullptr) { - std::string error = "Failed to link program:\n" + getProgramDescString() + "\n\n" + log; - reportErrorAndAllowRetry(error); - - // Continue loop to keep trying... + std::string msg = "Failed to link program:\n" + getProgramDescString() + "\n\n" + log; + bool showMessageBox = is_set(getErrorDiagnosticFlags(), ErrorDiagnosticFlags::ShowMessageBoxOnError); + if (showMessageBox && reportErrorAndAllowRetry(msg)) + continue; + FALCOR_THROW(msg); } else { if (!log.empty()) { - std::string warn = "Warnings in program:\n" + getProgramDescString() + "\n" + log; - logWarning(warn); + logWarning("Warnings in program:\n{}\n{}", getProgramDescString(), log); } mpActiveVersion = pVersion; @@ -462,8 +358,98 @@ void Program::breakStrongReferenceToDevice() mpDevice.breakStrongReference(); } +ref Program::getRtso(RtProgramVars* pVars) +{ + auto pProgramVersion = getActiveVersion(); + auto pProgramKernels = pProgramVersion->getKernels(mpDevice, pVars); + + mRtsoGraph.walk((void*)pProgramKernels.get()); + + ref pRtso = mRtsoGraph.getCurrentNode(); + + if (pRtso == nullptr) + { + RtStateObjectDesc desc; + desc.pProgramKernels = pProgramKernels; + desc.maxTraceRecursionDepth = mDesc.maxTraceRecursionDepth; + desc.pipelineFlags = mDesc.rtPipelineFlags; + + StateGraph::CompareFunc cmpFunc = [&desc](ref pRtso) -> bool { return pRtso && (desc == pRtso->getDesc()); }; + + if (mRtsoGraph.scanForMatchingNode(cmpFunc)) + { + pRtso = mRtsoGraph.getCurrentNode(); + } + else + { + pRtso = mpDevice->createRtStateObject(desc); + mRtsoGraph.setCurrentNodeData(pRtso); + } + } + + return pRtso; +} + FALCOR_SCRIPT_BINDING(Program) { - pybind11::class_>(m, "Program"); + using namespace pybind11::literals; + + FALCOR_SCRIPT_BINDING_DEPENDENCY(Types) + FALCOR_SCRIPT_BINDING_DEPENDENCY(ProgramReflection) + + pybind11::enum_ slangCompilerFlags(m, "SlangCompilerFlags"); + slangCompilerFlags.value("None_", SlangCompilerFlags::None); + slangCompilerFlags.value("TreatWarningsAsErrors", SlangCompilerFlags::TreatWarningsAsErrors); + slangCompilerFlags.value("DumpIntermediates", SlangCompilerFlags::DumpIntermediates); + slangCompilerFlags.value("FloatingPointModeFast", SlangCompilerFlags::FloatingPointModeFast); + slangCompilerFlags.value("FloatingPointModePrecise", SlangCompilerFlags::FloatingPointModePrecise); + slangCompilerFlags.value("GenerateDebugInfo", SlangCompilerFlags::GenerateDebugInfo); + slangCompilerFlags.value("MatrixLayoutColumnMajor", SlangCompilerFlags::MatrixLayoutColumnMajor); + ScriptBindings::addEnumBinaryOperators(slangCompilerFlags); + + pybind11::class_ programDesc(m, "ProgramDesc"); + + pybind11::class_(programDesc, "ShaderModule") + .def("add_file", &ProgramDesc::ShaderModule::addFile, "path"_a) + .def("add_string", &ProgramDesc::ShaderModule::addString, "string"_a, "path"_a = std::filesystem::path()); + + pybind11::class_(programDesc, "EntryPointGroup") + .def_property( + "type_conformances", + [](const ProgramDesc::EntryPointGroup& self) { return typeConformanceListToPython(self.typeConformances); }, + [](ProgramDesc::EntryPointGroup& self, const pybind11::dict& dict) + { return self.typeConformances = typeConformanceListFromPython(dict); } + ); + + programDesc.def(pybind11::init<>()); + programDesc.def_readwrite("shader_model", &ProgramDesc::shaderModel); + programDesc.def_readwrite("compiler_flags", &ProgramDesc::compilerFlags); + programDesc.def_readwrite("compiler_arguments", &ProgramDesc::compilerArguments); + programDesc.def( + "add_shader_module", + pybind11::overload_cast(&ProgramDesc::addShaderModule), + "name"_a = "", + pybind11::return_value_policy::reference + ); + programDesc.def("cs_entry", &ProgramDesc::csEntry, "name"_a); + + pybind11::class_> program(m, "Program"); + + program.def_property_readonly("reflector", &Program::getReflector); + program.def_property( + "defines", + [](const Program& self) { return defineListToPython(self.getDefines()); }, + [](Program& self, const pybind11::dict& dict) { self.setDefines(defineListFromPython(dict)); } + ); + program.def("add_define", &Program::addDefine, "name"_a, "value"_a = ""); + program.def("remove_define", &Program::removeDefine, "name"_a); + program.def_property( + "type_conformances", + [](const Program& self) { return typeConformanceListToPython(self.getTypeConformances()); }, + [](Program& self, const pybind11::dict& dict) { return self.setTypeConformances(typeConformanceListFromPython(dict)); } + ); + program.def("add_type_conformance", &Program::addTypeConformance, "type_name"_a, "interface_type"_a, "id"_a); + program.def("remove_type_conformance", &Program::removeTypeConformance, "type_name"_a, "interface_type"_a); } + } // namespace Falcor diff --git a/Source/Falcor/Core/Program/Program.h b/Source/Falcor/Core/Program/Program.h index 0a8590902..ff73820f2 100644 --- a/Source/Falcor/Core/Program/Program.h +++ b/Source/Falcor/Core/Program/Program.h @@ -31,7 +31,10 @@ #include "Core/Macros.h" #include "Core/Object.h" #include "Core/API/fwd.h" -#include "Core/API/ShaderType.h" +#include "Core/API/Types.h" +#include "Core/API/Raytracing.h" +#include "Core/API/RtStateObject.h" +#include "Core/State/StateGraph.h" #include #include #include @@ -39,336 +42,607 @@ #include #include #include +#include namespace Falcor { + +class RtStateObject; +class RtProgramVars; + /** - * High-level abstraction of a program class. - * This class manages different versions of the same program. Different versions means same shader files, different macro definitions. - * This allows simple usage in case different macros are required - for example static vs. animated models. + * Representing a shader implementation of an interface. + * When linked into a `ProgramVersion`, the specialized shader will contain + * the implementation of the specified type in a dynamic dispatch function. */ -class FALCOR_API Program : public Object +struct TypeConformance +{ + std::string typeName; + std::string interfaceName; + TypeConformance() = default; + TypeConformance(const std::string& typeName_, const std::string& interfaceName_) : typeName(typeName_), interfaceName(interfaceName_) {} + bool operator<(const TypeConformance& other) const + { + return typeName < other.typeName || (typeName == other.typeName && interfaceName < other.interfaceName); + } + bool operator==(const TypeConformance& other) const { return typeName == other.typeName && interfaceName == other.interfaceName; } + struct HashFunction + { + size_t operator()(const TypeConformance& conformance) const + { + size_t hash = std::hash()(conformance.typeName); + hash = hash ^ std::hash()(conformance.interfaceName); + return hash; + } + }; +}; + +class TypeConformanceList : public std::map { - FALCOR_OBJECT(Program) public: - using ArgumentList = std::vector; + /** + * Adds a type conformance. If the type conformance exists, it will be replaced. + * @param[in] typeName The name of the implementation type. + * @param[in] interfaceName The name of the interface type. + * @param[in] id Optional. The id representing the implementation type for this interface. If it is -1, Slang will automatically + * assign a unique Id for the type. + * @return The updated list of type conformances. + */ + TypeConformanceList& add(const std::string& typeName, const std::string& interfaceName, uint32_t id = -1) + { + (*this)[TypeConformance(typeName, interfaceName)] = id; + return *this; + } - enum class CompilerFlags + /** + * Removes a type conformance. If the type conformance doesn't exist, the call will be silently ignored. + * @param[in] typeName The name of the implementation type. + * @param[in] interfaceName The name of the interface type. + * @return The updated list of type conformances. + */ + TypeConformanceList& remove(const std::string& typeName, const std::string& interfaceName) { - None = 0x0, - TreatWarningsAsErrors = 0x1, - DumpIntermediates = 0x2, - FloatingPointModeFast = 0x4, - FloatingPointModePrecise = 0x8, - GenerateDebugInfo = 0x10, - MatrixLayoutColumnMajor = 0x20, // Falcor is using row-major, use this only to compile stand-alone external shaders. - }; + (*this).erase(TypeConformance(typeName, interfaceName)); + return *this; + } /** - * Representing a shader implementation of an interface. - * When linked into a `ProgramVersion`, the specialized shader will contain - * the implementation of the specified type in a dynamic dispatch function. + * Add a type conformance list to the current list */ - struct TypeConformance - { - std::string mTypeName; - std::string mInterfaceName; - TypeConformance() = default; - TypeConformance(const std::string& typeName, const std::string& interfaceName) : mTypeName(typeName), mInterfaceName(interfaceName) - {} - bool operator<(const TypeConformance& other) const - { - return mTypeName < other.mTypeName || (mTypeName == other.mTypeName && mInterfaceName < other.mInterfaceName); - } - bool operator==(const TypeConformance& other) const - { - return mTypeName == other.mTypeName && mInterfaceName == other.mInterfaceName; - } - struct HashFunction + TypeConformanceList& add(const TypeConformanceList& cl) + { + for (const auto& p : cl) + add(p.first.typeName, p.first.interfaceName, p.second); + return *this; + } + + /** + * Remove a type conformance list from the current list + */ + TypeConformanceList& remove(const TypeConformanceList& cl) + { + for (const auto& p : cl) + remove(p.first.typeName, p.first.interfaceName); + return *this; + } + + TypeConformanceList() = default; + TypeConformanceList(std::initializer_list> il) : std::map(il) {} +}; + +enum class SlangCompilerFlags +{ + None = 0x0, + TreatWarningsAsErrors = 0x1, + /// Enable dumping of intermediate artifacts during compilation. + /// Note that if a shader is cached no artifacts are being produced. + /// Delete the `.shadercache` directory in the build directory before dumping. + DumpIntermediates = 0x2, + FloatingPointModeFast = 0x4, + FloatingPointModePrecise = 0x8, + GenerateDebugInfo = 0x10, + MatrixLayoutColumnMajor = 0x20, // Falcor is using row-major, use this only to compile stand-alone external shaders. +}; +FALCOR_ENUM_CLASS_OPERATORS(SlangCompilerFlags); + +/** + * Description of a program to be created. + * This includes the following: + * - shader sources organized into shader modules (each module is compiled as a separate translation unit) + * - entry points organized into entry point groups + * - type conformances (global and per entry point group) + * - compiler options (shader model, flags, etc.) + */ +struct ProgramDesc +{ + struct ShaderID + { + int32_t groupIndex = -1; ///< Entry point group index. + bool isValid() const { return groupIndex >= 0; } + }; + + /// Represents a single piece of shader source code. + /// This can either be a file or a string. + struct ShaderSource + { + enum class Type { - size_t operator()(const TypeConformance& conformance) const - { - size_t hash = std::hash()(conformance.mTypeName); - hash = hash ^ std::hash()(conformance.mInterfaceName); - return hash; - } + File, + String }; + + /// Type of the shader source. + Type type{Type::File}; + + /// Shader source file path. + /// For Type::File this is the actual path to the file. + /// For Type::String this is an optional virtual path used for diagnostics purposes. + std::filesystem::path path; + + /// Shader source string if type == Type::String. + std::string string; + + bool operator==(const ShaderSource& rhs) const { return type == rhs.type && path == rhs.path && string == rhs.string; } }; - class TypeConformanceList : public std::map - { - public: - /** - * Adds a type conformance. If the type conformance exists, it will be replaced. - * @param[in] typeName The name of the implementation type. - * @param[in] interfaceName The name of the interface type. - * @param[in] id Optional. The id representing the implementation type for this interface. If it is -1, Slang will automatically - * assign a unique Id for the type. - * @return The updated list of type conformances. - */ - TypeConformanceList& add(const std::string& typeName, const std::string& interfaceName, uint32_t id = -1) + /// Represents a single shader module made up from a list of sources (files/strings). + /// A shader module corresponds to a single translation unit. + struct ShaderModule + { + /// The name of the shader module. + /// This is the name used by other modules to import this module. + /// If left empty, a name based on a hash from the module sources will be generated. + std::string name; + + /// List of shader sources. + std::vector sources; + + ShaderModule() = default; + explicit ShaderModule(std::string name_) : name(std::move(name_)) {} + + /// Create a shader module description containing a single file. + static ShaderModule fromFile(std::filesystem::path path) { - (*this)[TypeConformance(typeName, interfaceName)] = id; - return *this; + ShaderModule sm; + sm.addFile(std::move(path)); + return sm; } - /** - * Removes a type conformance. If the type conformance doesn't exist, the call will be silently ignored. - * @param[in] typeName The name of the implementation type. - * @param[in] interfaceName The name of the interface type. - * @return The updated list of type conformances. - */ - TypeConformanceList& remove(const std::string& typeName, const std::string& interfaceName) + /// Create a shader module description containing a single string. + static ShaderModule fromString(std::string string, std::filesystem::path path = {}, std::string moduleName = {}) { - (*this).erase(TypeConformance(typeName, interfaceName)); - return *this; + ShaderModule sm(std::move(moduleName)); + sm.addString(std::move(string), std::move(path)); + return sm; } - /** - * Add a type conformance list to the current list - */ - TypeConformanceList& add(const TypeConformanceList& cl) + /// Add a source file to the shader module. + ShaderModule& addFile(std::filesystem::path path) { - for (const auto& p : cl) - add(p.first.mTypeName, p.first.mInterfaceName, p.second); + sources.push_back({ShaderSource::Type::File, std::move(path), {}}); return *this; } - /** - * Remove a type conformance list from the current list - */ - TypeConformanceList& remove(const TypeConformanceList& cl) + /// Add a source string to the shader module. + /// The path is optional and only used for diagnostic purposes. + ShaderModule& addString(std::string string, std::filesystem::path path = {}) { - for (const auto& p : cl) - remove(p.first.mTypeName, p.first.mInterfaceName); + sources.push_back({ShaderSource::Type::String, std::move(path), std::move(string)}); return *this; } - TypeConformanceList() = default; - TypeConformanceList(std::initializer_list> il) : std::map(il) - {} + bool operator==(const ShaderModule& rhs) const { return name == rhs.name && sources == rhs.sources; } }; - /** - * Shader module stored as a string or file. - */ - struct FALCOR_API ShaderModule + /// Represents a single entry point. + struct EntryPoint { - enum class Type - { - String, - File - }; - - ShaderModule(const std::filesystem::path& path, bool createTranslationUnit = true) - : type(Type::File), filePath(path), createTranslationUnit(createTranslationUnit){}; - ShaderModule( - const std::string_view str, - const std::string_view moduleName, - const std::string_view modulePath = "", - bool createTranslationUnit = true - ) - : type(Type::String), str(str), moduleName(moduleName), modulePath(modulePath), createTranslationUnit(createTranslationUnit){}; - - Type type; - std::filesystem::path filePath; ///< File path to shader source. - std::string str; ///< String of shader source. - std::string moduleName; ///< Slang module name for module created from string. If not creating a new translation unit, this can be - ///< left empty. - std::string modulePath; ///< Virtual file path to module created from string. This is just used for diagnostics purposesand can be - ///< left empty. - bool createTranslationUnit; ///< Create new Slang translation unit for the module. + /// Shader type (compute, vertex, pixel, etc.). + ShaderType type; + /// The name of the entry point in the shader source. + std::string name; + /// The name of the entry point in the generated code. + std::string exportName; + + /// Global linear entry point index. This is computed when creating the program. + /// TODO we should look into eventally removing this. + uint32_t globalIndex; }; - using ShaderModuleList = std::vector; - - /** - * Description of a program to be created. - */ - class FALCOR_API Desc - { - public: - /** - * Begin building a description, that initially has no source files or entry points. - */ - Desc(); - - /** - * Begin building a description, based on a single path for source code. - * This is equivalent to: `Desc().addShaderLibrary(path)` - * @param[in] path Path to the source code. - */ - explicit Desc(const std::filesystem::path& path); - - /** - * Add a language specific (e.g. HLSL, GLSL) prelude to the shader. - * @param[in] prelude Source string. - */ - Desc& setLanguagePrelude(const std::string_view prelude); - - /** - * Add a file of source code to use. - * This also sets the given file as the "active" source for subsequent entry points. - * @param[in] path Path to the source code. - * @param[in] createTranslationUnit Whether a new Slang translation unit should be created, otherwise the source is added to the - * previous translation unit. - */ - Desc& addShaderLibrary(const std::filesystem::path& path, bool createTranslationUnit = true) - { - return addShaderModule(ShaderModule(path, createTranslationUnit)); - } + /// Represents a group of entry points. + /// This is mostly used for grouping raytracing hitgroup entry points. + struct EntryPointGroup + { + uint32_t shaderModuleIndex; + TypeConformanceList typeConformances; + std::vector entryPoints; - /** - * Add a string of source code to use. - * This also sets the given string as the "active" source for subsequent entry points. - * If `createTranslationUnit` is false, the source is directly visible to the previously added source. - * If true, a new translation unit is created and the source has to be imported using the supplied `moduleName`. - * Note that the source string has to be added *before* any source that imports it. - * @param[in] shader Source code. - * @param[in] moduleName Slang module name. If not creating a new translation unit, this can be left empty. - * @param[in] modulePath Virtual file path to module created from string. This is just used for diagnostics purposes and can be left - * empty. - * @param[in] createTranslationUnit Whether a new Slang translation unit should be created, otherwise the source is added to the - * previous translation unit. - */ - Desc& addShaderString( - const std::string_view shader, - const std::string_view moduleName, - const std::string_view modulePath = "", - bool createTranslationUnit = true - ) + /// Set the type conformances used for this entry point group. + EntryPointGroup& setTypeConformances(TypeConformanceList conformances) { - return addShaderModule(ShaderModule(shader, moduleName, modulePath, createTranslationUnit)); + typeConformances = std::move(conformances); + return *this; } - /** - * Add a shader module. - * This also sets the given module as "active" for subsequent entry points. - * Note that the module has to be added *before* any module that imports it. - */ - Desc& addShaderModule(const ShaderModule& module); - - /** - * Add a list of shader modules. - * Note that the modules have to be added *before* any module that imports them. - */ - Desc& addShaderModules(const ShaderModuleList& modules); - - /** - * Adds an entry point based on the "active" source. - */ - Desc& entryPoint(ShaderType shaderType, const std::string& name); - - bool hasEntryPoint(ShaderType stage) const; - - Desc& vsEntry(const std::string& name) { return entryPoint(ShaderType::Vertex, name); } - Desc& hsEntry(const std::string& name) { return entryPoint(ShaderType::Hull, name); } - Desc& dsEntry(const std::string& name) { return entryPoint(ShaderType::Domain, name); } - Desc& gsEntry(const std::string& name) { return entryPoint(ShaderType::Geometry, name); } - Desc& psEntry(const std::string& name) { return entryPoint(ShaderType::Pixel, name); } - Desc& csEntry(const std::string& name) { return entryPoint(ShaderType::Compute, name); } - - /** - * Adds a list of type conformances. - * The type conformances are linked into all shaders in the program. - */ - Desc& addTypeConformances(const TypeConformanceList& typeConformances) + /// Add type conformances used for this entry point group. + EntryPointGroup& addTypeConformances(const TypeConformanceList& conformances) { - mTypeConformances.add(typeConformances); + typeConformances.add(conformances); return *this; } - /** - * Set the shader model string. - * This should be `6_0`, `6_1`, `6_2`, `6_3`, `6_4`, or `6_5`. The default is `6_3`. - */ - Desc& setShaderModel(const std::string& sm); - - /** - * Get the compiler flags. - */ - CompilerFlags getCompilerFlags() const { return mShaderFlags; } - - /** - * Set the compiler flags. Replaces any previously set flags. - */ - Desc& setCompilerFlags(CompilerFlags flags) + /// Add an entry point to the group. + EntryPointGroup& addEntryPoint(ShaderType type, std::string_view name, std::string_view exportName = "") { - mShaderFlags = flags; + if (exportName.empty()) + exportName = name; + entryPoints.push_back({type, std::string(name), std::string(exportName)}); return *this; } + }; - /** - * Get additional compiler arguments. - */ - const ArgumentList& getCompilerArguments() const { return mCompilerArguments; } + using ShaderModuleList = std::vector; - /** - * Set additional compiler arguments. Replaces any previously set arguments. - */ - Desc& setCompilerArguments(const ArgumentList& arguments) - { - mCompilerArguments = arguments; - return *this; - } + /// List of shader modules used by the program. + ShaderModuleList shaderModules; - /** - * Add additional compiler arguments. Appended to previously set arguments. - */ - Desc& addCompilerArguments(const ArgumentList& arguments) - { - mCompilerArguments.insert(mCompilerArguments.end(), arguments.begin(), arguments.end()); - return *this; - } + /// List of entry point groups. + std::vector entryPointGroups; - protected: - friend class Program; - friend class RtProgram; - friend class ProgramManager; + /// Global type conformances. + TypeConformanceList typeConformances; - Desc& beginEntryPointGroup(const std::string& entryPointNameSuffix = ""); - Desc& addTypeConformancesToGroup(const TypeConformanceList& typeConformances); - uint32_t declareEntryPoint(ShaderType type, const std::string& name); + /// Shader model. + /// If not specified, the most recent supported shader model is used. + ShaderModel shaderModel{ShaderModel::Unknown}; - struct SourceEntryPoints - { - SourceEntryPoints(const ShaderModule& src) : source(src) {} - ShaderModule::Type getType() const { return source.type; } + /// Compiler flags. + SlangCompilerFlags compilerFlags{SlangCompilerFlags::None}; - ShaderModule source; ///< Shader module source stored as a string or file. - std::vector entryPoints; ///< Indices into `mEntryPoints` for all entry points in the module. - }; + /// List of compiler arguments (as set on the compiler command line). + std::vector compilerArguments; - struct EntryPointGroup - { - std::vector entryPoints; ///< Indices into `mEntryPoints` for all entry points in the group. - TypeConformanceList typeConformances; ///< Type conformances linked into all shaders in the group. - std::string nameSuffix; ///< Suffix added to the entry point names by Slang's code generation. - }; + /// Max trace recursion depth (only used for raytracing programs). + uint32_t maxTraceRecursionDepth = uint32_t(-1); - struct EntryPoint - { - std::string name; ///< Name of the entry point in the shader source. - std::string exportName; ///< Name of the entry point in the generated code. - ShaderType stage; - int32_t sourceIndex; - int32_t groupIndex; ///< Entry point group index. - }; + /// Max ray payload size in bytes (only used for raytracing programs). + uint32_t maxPayloadSize = uint32_t(-1); - std::vector mSources; - std::vector mGroups; - std::vector mEntryPoints; - TypeConformanceList mTypeConformances; ///< Type conformances linked into all shaders in the program. - - int32_t mActiveSource = -1; ///< Current source index. - int32_t mActiveGroup = -1; ///< Current entry point index. - CompilerFlags mShaderFlags = CompilerFlags::None; - ArgumentList mCompilerArguments; - std::string mShaderModel = "6_3"; - std::string mLanguagePrelude; - }; + /// Max attribute size in bytes (only used for raytracing programs). + uint32_t maxAttributeSize = getRaytracingMaxAttributeSize(); + + /// Raytracing pipeline flags (only used for raytracing programs). + RtPipelineFlags rtPipelineFlags = RtPipelineFlags::None; + + /// Add a new empty shader module description. + /// @param[in] name Optional name of the shader module. + /// @return Returns a reference to the newly created shader module for adding sources. + ShaderModule& addShaderModule(std::string name = {}) + { + shaderModules.push_back(ShaderModule(std::move(name))); + return shaderModules.back(); + } + + /// Add an existing shader module description. + ProgramDesc& addShaderModule(ShaderModule shaderModule) + { + shaderModules.push_back(std::move(shaderModule)); + return *this; + } + + /// Add a list of existing shader module descriptions. + ProgramDesc& addShaderModules(const ShaderModuleList& modules) + { + shaderModules.insert(shaderModules.end(), modules.begin(), modules.end()); + return *this; + } + + /// Helper to add a shader module made from a single file. + ProgramDesc& addShaderLibrary(std::filesystem::path path) + { + addShaderModule().addFile(std::move(path)); + return *this; + } - virtual ~Program() = 0; + /// Add a new entry point group. + /// If no `shaderModuleIndex` is not specified, the last shader module added to the program will be used. + /// @param shaderModuleIndex The index of the shader module to use for this entry point group. + /// @return Returns a reference to the newly created entry point group for adding entry points. + EntryPointGroup& addEntryPointGroup(uint32_t shaderModuleIndex = uint32_t(-1)) + { + if (shaderModules.empty()) + FALCOR_THROW("Can't add entry point group without a shader module"); + if (shaderModuleIndex == uint32_t(-1)) + shaderModuleIndex = uint32_t(shaderModules.size()) - 1; + if (shaderModuleIndex >= shaderModules.size()) + FALCOR_THROW("Invalid shader module index"); + entryPointGroups.push_back({shaderModuleIndex}); + return entryPointGroups.back(); + } + + /// Helper for adding an entry point defined in the most recent added shader module. + ProgramDesc& addEntryPoint(ShaderType shaderType, std::string_view name) + { + if (entryPointGroups.empty() || entryPointGroups.back().shaderModuleIndex != shaderModules.size() - 1) + addEntryPointGroup(); + entryPointGroups.back().addEntryPoint(shaderType, name); + return *this; + } + + ProgramDesc& vsEntry(const std::string& name) { return addEntryPoint(ShaderType::Vertex, name); } + ProgramDesc& hsEntry(const std::string& name) { return addEntryPoint(ShaderType::Hull, name); } + ProgramDesc& dsEntry(const std::string& name) { return addEntryPoint(ShaderType::Domain, name); } + ProgramDesc& gsEntry(const std::string& name) { return addEntryPoint(ShaderType::Geometry, name); } + ProgramDesc& psEntry(const std::string& name) { return addEntryPoint(ShaderType::Pixel, name); } + ProgramDesc& csEntry(const std::string& name) { return addEntryPoint(ShaderType::Compute, name); } + + /// Checks if the program description has at least one entry point of a given type. + bool hasEntryPoint(ShaderType stage) const + { + for (const auto& group : entryPointGroups) + for (const auto& entryPoint : group.entryPoints) + if (entryPoint.type == stage) + return true; + return false; + } + + /// Set the global type conformances. + ProgramDesc& setTypeConformances(TypeConformanceList conformances) + { + typeConformances = std::move(conformances); + return *this; + } + + /// Add global type conformances. + ProgramDesc& addTypeConformances(const TypeConformanceList& conformances) + { + typeConformances.add(conformances); + return *this; + } + + /// Set the shader model. + ProgramDesc& setShaderModel(ShaderModel shaderModel_) + { + shaderModel = shaderModel_; + return *this; + } + + /// Set the compiler flags. + ProgramDesc& setCompilerFlags(SlangCompilerFlags flags) + { + compilerFlags = flags; + return *this; + } + + /// Set the compiler arguments (as set on the compiler command line). + ProgramDesc& setCompilerArguments(std::vector args) + { + compilerArguments = std::move(args); + return *this; + } + + /// Add compiler arguments (as set on the compiler command line). + ProgramDesc& addCompilerArguments(const std::vector& args) + { + compilerArguments.insert(compilerArguments.end(), args.begin(), args.end()); + return *this; + } + + // + // Compatibility functions + // + + /** + * Add a raygen shader. + * @param[in] raygen Entry point for the raygen shader. + * @param[in] typeConformances Optional list of type conformances for the raygen shader. + * @param[in] entryPointNameSuffix Optional suffix added to the entry point names in the generated code. + * @return Shader ID for raygen shader. This is used when building the binding table. + */ + ShaderID addRayGen( + const std::string& raygen, + const TypeConformanceList& typeConformances_ = TypeConformanceList(), + const std::string& entryPointNameSuffix = "" + ) + { + FALCOR_CHECK(!raygen.empty(), "'raygen' entry point name must not be empty"); + + auto& group = addEntryPointGroup(); + group.setTypeConformances(typeConformances_); + group.addEntryPoint(ShaderType::RayGeneration, raygen, raygen + entryPointNameSuffix); + + return ShaderID{int32_t(entryPointGroups.size() - 1)}; + } + + /** + * Add a miss shader. + * @param[in] miss Entry point for the miss shader. + * @param[in] typeConformances Optional list of type conformances for the miss shader. + * @param[in] entryPointNameSuffix Optional suffix added to the entry point names in the generated code. + * @return Shader ID for miss shader. This is used when building the binding table. + */ + ShaderID addMiss( + const std::string& miss, + const TypeConformanceList& typeConformances_ = TypeConformanceList(), + const std::string& entryPointNameSuffix = "" + ) + { + FALCOR_CHECK(!miss.empty(), "'miss' entry point name must not be empty"); + + auto& group = addEntryPointGroup(); + group.setTypeConformances(typeConformances_); + group.addEntryPoint(ShaderType::Miss, miss, miss + entryPointNameSuffix); + + return ShaderID{int32_t(entryPointGroups.size() - 1)}; + } + + /** + * Add a hit group. + * A hit group consists of any combination of closest hit, any hit, and intersection shaders. + * Note that a hit group that contains an intersection shader only be used with procedural geometry. + * A hit group that does not contain an intersection shader can only be used with triangle geometry. + * It is valid to create a hit group entirely without entry points. Geometry using it will act + * as an occluder blocking miss shader exuection, but hits will not spawn any shader executions. + * @param[in] closestHit Entry point for the closest hit shader. + * @param[in] anyHit Entry point for the any hit shader. + * @param[in] intersection Entry point for the intersection shader. + * @param[in] typeConformances Optional list of type conformances for the hit group. + * @param[in] entryPointNameSuffix Optional suffix added to the entry point names in the generated code. + * @return Shader ID for hit group. This is used when building the binding table. + */ + ShaderID addHitGroup( + const std::string& closestHit, + const std::string& anyHit = "", + const std::string& intersection = "", + const TypeConformanceList& typeConformances_ = TypeConformanceList(), + const std::string& entryPointNameSuffix = "" + ) + { + FALCOR_CHECK( + !(closestHit.empty() && anyHit.empty() && intersection.empty()), + "At least one of 'closestHit', 'anyHit' or 'intersection' entry point names must not be empty" + ); + + auto& group = addEntryPointGroup(); + group.setTypeConformances(typeConformances_); + if (!closestHit.empty()) + group.addEntryPoint(ShaderType::ClosestHit, closestHit, closestHit + entryPointNameSuffix); + if (!anyHit.empty()) + group.addEntryPoint(ShaderType::AnyHit, anyHit, anyHit + entryPointNameSuffix); + if (!intersection.empty()) + group.addEntryPoint(ShaderType::Intersection, intersection, intersection + entryPointNameSuffix); + + return ShaderID{int32_t(entryPointGroups.size() - 1)}; + } + + /** + * Set the max recursion depth. + * @param[in] maxDepth The maximum ray recursion depth (0 = raygen). + */ + ProgramDesc& setMaxTraceRecursionDepth(uint32_t maxDepth) + { + maxTraceRecursionDepth = maxDepth; + return *this; + } + + /** + * Set the max payload size. + */ + ProgramDesc& setMaxPayloadSize(uint32_t maxPayloadSize_) + { + maxPayloadSize = maxPayloadSize_; + return *this; + } + + /** + * Set the max attribute size. + * @param[in] maxAttributeSize The maximum attribute size in bytes. + */ + ProgramDesc& setMaxAttributeSize(uint32_t maxAttributeSize_) + { + maxAttributeSize = maxAttributeSize_; + return *this; + } + + /** + * Set raytracing pipeline flags. + * These flags are added to any TraceRay() call within this pipeline, and may be used to + * optimize the pipeline for particular primitives types. Requires Tier 1.1 support. + * @param[in] flags Pipeline flags. + */ + ProgramDesc& setRtPipelineFlags(RtPipelineFlags flags) + { + rtPipelineFlags = flags; + return *this; + } + + void finalize(); +}; + +/** + * High-level abstraction of a program class. + * This class manages different versions of the same program. Different versions means same shader files, different macro definitions. + * This allows simple usage in case different macros are required - for example static vs. animated models. + */ +class FALCOR_API Program : public Object +{ + FALCOR_OBJECT(Program) +public: + Program(ref pDevice, ProgramDesc desc, DefineList programDefines); + virtual ~Program() override; + + /** + * Create a new program. + * Note that this call merely creates a program object. The actual compilation and link happens at a later time. + * @param[in] pDevice GPU device. + * @param[in] desc The program description. + * @param[in] programDefines Optional list of macro definitions to set into the program. + * @return A new object, or an exception is thrown if creation failed. + */ + static ref create(ref pDevice, ProgramDesc desc, DefineList programDefines = {}) + { + return make_ref(std::move(pDevice), std::move(desc), std::move(programDefines)); + } + + /** + * Create a new compute program from file. + * Note that this call merely creates a program object. The actual compilation and link happens at a later time. + * @param[in] pDevice GPU device. + * @param[in] path Compute program file path. + * @param[in] csEntry Name of the entry point in the program. + * @param[in] programDefines Optional list of macro definitions to set into the program. + * @param[in] flags Optional program compilation flags. + * @param[in] shaderModel Optional shader model. + * @return A new object, or an exception is thrown if creation failed. + */ + static ref createCompute( + ref pDevice, + const std::filesystem::path& path, + const std::string& csEntry, + DefineList programDefines = {}, + SlangCompilerFlags flags = SlangCompilerFlags::None, + ShaderModel shaderModel = ShaderModel::Unknown + ) + { + ProgramDesc d; + d.addShaderLibrary(path); + if (shaderModel != ShaderModel::Unknown) + d.setShaderModel(shaderModel); + d.setCompilerFlags(flags); + d.csEntry(csEntry); + return create(std::move(pDevice), std::move(d), std::move(programDefines)); + } + + /** + * Create a new graphics program from file. + * @param[in] pDevice GPU device. + * @param[in] path Graphics program file path. + * @param[in] vsEntry Vertex shader entry point. If this string is empty (""), it will use a default vertex shader, which transforms and + * outputs all default vertex attributes. + * @param[in] psEntry Pixel shader entry point + * @param[in] programDefines Optional list of macro definitions to set into the program. + * @param[in] flags Optional program compilation flags. + * @param[in] shaderModel Optional shader model. + * @return A new object, or an exception is thrown if creation failed. + */ + static ref createGraphics( + ref pDevice, + const std::filesystem::path& path, + const std::string& vsEntry, + const std::string& psEntry, + DefineList programDefines = {}, + SlangCompilerFlags flags = SlangCompilerFlags::None, + ShaderModel shaderModel = ShaderModel::Unknown + ) + { + ProgramDesc d; + d.addShaderLibrary(path); + if (shaderModel != ShaderModel::Unknown) + d.setShaderModel(shaderModel); + d.setCompilerFlags(flags); + d.vsEntry(vsEntry).psEntry(psEntry); + return create(std::move(pDevice), std::move(d), std::move(programDefines)); + } /** * Get the API handle of the active program. @@ -423,6 +697,9 @@ class FALCOR_API Program : public Object */ bool setDefines(const DefineList& dl); + /// Get current macro definitions. + const DefineList& getDefines() const { return mDefineList; } + /** * Add a type conformance to the program. * @param[in] typeName The name of the implementation shader type. @@ -447,40 +724,46 @@ class FALCOR_API Program : public Object */ bool setTypeConformances(const TypeConformanceList& conformances); + /// Get current type conformances. + const TypeConformanceList& getTypeConformances() const { return mTypeConformanceList; } + /** * Get the macro definition list of the active program version. */ const DefineList& getDefineList() const { return mDefineList; } + const ProgramDesc& getDesc() const { return mDesc; } + /** * Get the program reflection for the active program. * @return Program reflection object, or an exception is thrown on failure. */ const ref& getReflector() const { return getActiveVersion()->getReflector(); } - uint32_t getEntryPointGroupCount() const { return uint32_t(mDesc.mGroups.size()); } - uint32_t getGroupEntryPointCount(uint32_t groupIndex) const { return (uint32_t)mDesc.mGroups[groupIndex].entryPoints.size(); } + uint32_t getEntryPointGroupCount() const { return uint32_t(mDesc.entryPointGroups.size()); } + uint32_t getGroupEntryPointCount(uint32_t groupIndex) const { return (uint32_t)mDesc.entryPointGroups[groupIndex].entryPoints.size(); } uint32_t getGroupEntryPointIndex(uint32_t groupIndex, uint32_t entryPointIndexInGroup) const { - return mDesc.mGroups[groupIndex].entryPoints[entryPointIndexInGroup]; + return mDesc.entryPointGroups[groupIndex].entryPoints[entryPointIndexInGroup].globalIndex; } void breakStrongReferenceToDevice(); + ref getRtso(RtProgramVars* pVars); + protected: friend class ProgramManager; friend class ProgramVersion; friend class ParameterBlockReflection; - Program(ref pDevice, const Desc& desc, const DefineList& programDefines); - void validateEntryPoints() const; bool link() const; BreakableReference mpDevice; // The description used to create this program - const Desc mDesc; + // TODO we should make this const again + ProgramDesc mDesc; DefineList mDefineList; TypeConformanceList mTypeConformanceList; @@ -509,8 +792,9 @@ class FALCOR_API Program : public Object bool checkIfFilesChanged(); void reset(); -}; -FALCOR_ENUM_CLASS_OPERATORS(Program::CompilerFlags); + using StateGraph = Falcor::StateGraph, void*>; + StateGraph mRtsoGraph; +}; } // namespace Falcor diff --git a/Source/Falcor/Core/Program/ProgramManager.cpp b/Source/Falcor/Core/Program/ProgramManager.cpp index 67e274479..a1f662a53 100644 --- a/Source/Falcor/Core/Program/ProgramManager.cpp +++ b/Source/Falcor/Core/Program/ProgramManager.cpp @@ -70,9 +70,9 @@ inline SlangStage getSlangStage(ShaderType type) } } -inline std::string getSlangProfileString(const std::string& shaderModel) +inline std::string getSlangProfileString(ShaderModel shaderModel) { - return "sm_" + shaderModel; + return fmt::format("sm_{}_{}", getShaderModelMajorVersion(shaderModel), getShaderModelMinorVersion(shaderModel)); } inline bool doSlangReflection( @@ -100,7 +100,21 @@ inline bool doSlangReflection( return true; } -ProgramManager::ProgramManager(Device* pDevice) : mpDevice(pDevice) {} +ProgramManager::ProgramManager(Device* pDevice) : mpDevice(pDevice) +{ + // Set global shader defines + DefineList globalDefines = { + {"FALCOR_NVAPI_AVAILABLE", (FALCOR_NVAPI_AVAILABLE && mpDevice->getType() == Device::Type::D3D12) ? "1" : "0"}, +#if FALCOR_NVAPI_AVAILABLE + {"NV_SHADER_EXTN_SLOT", "u999"}, + {"__SHADER_TARGET_MAJOR", std::to_string(getShaderModelMajorVersion(mpDevice->getSupportedShaderModel()))}, + {"__SHADER_TARGET_MINOR", std::to_string(getShaderModelMinorVersion(mpDevice->getSupportedShaderModel()))}, +#endif + }; + + addGlobalDefines(globalDefines); + +} ref ProgramManager::createProgramVersion(const Program& program, std::string& log) const { @@ -126,25 +140,26 @@ ref ProgramManager::createProgramVersion(const Program& pr // Prepare entry points. std::vector> pSlangEntryPoints; - uint32_t entryPointCount = (uint32_t)program.mDesc.mEntryPoints.size(); - for (uint32_t ee = 0; ee < entryPointCount; ++ee) + for (const auto& entryPointGroup : program.mDesc.entryPointGroups) { - Slang::ComPtr pSlangEntryPoint; - spCompileRequest_getEntryPoint(pSlangRequest, ee, pSlangEntryPoint.writeRef()); - - // Rename entry point in the generated code if the exported name differs from the source name. - // This makes it possible to generate different specializations of the same source entry point, - // for example by setting different type conformances. - const auto& entryPointDesc = program.mDesc.mEntryPoints[ee]; - if (entryPointDesc.exportName != entryPointDesc.name) + for (const auto& entryPoint : entryPointGroup.entryPoints) { - Slang::ComPtr pRenamedEntryPoint; - pSlangEntryPoint->renameEntryPoint(entryPointDesc.exportName.c_str(), pRenamedEntryPoint.writeRef()); - pSlangEntryPoints.push_back(pRenamedEntryPoint); - } - else - { - pSlangEntryPoints.push_back(pSlangEntryPoint); + Slang::ComPtr pSlangEntryPoint; + spCompileRequest_getEntryPoint(pSlangRequest, entryPoint.globalIndex, pSlangEntryPoint.writeRef()); + + // Rename entry point in the generated code if the exported name differs from the source name. + // This makes it possible to generate different specializations of the same source entry point, + // for example by setting different type conformances. + if (entryPoint.exportName != entryPoint.name) + { + Slang::ComPtr pRenamedEntryPoint; + pSlangEntryPoint->renameEntryPoint(entryPoint.exportName.c_str(), pRenamedEntryPoint.writeRef()); + pSlangEntryPoints.push_back(pRenamedEntryPoint); + } + else + { + pSlangEntryPoints.push_back(pSlangEntryPoint); + } } } @@ -219,7 +234,7 @@ ref ProgramManager::createProgramKernels( // Create a composite component type that represents all type conformances // linked into the `ProgramVersion`. - auto createTypeConformanceComponentList = [&](const Program::TypeConformanceList& typeConformances + auto createTypeConformanceComponentList = [&](const TypeConformanceList& typeConformances ) -> std::optional> { Slang::ComPtr pTypeConformancesCompositeComponent; @@ -233,22 +248,24 @@ ref ProgramManager::createProgramKernels( // Look for the type and interface type specified by the type conformance. // If not found we'll log an error and return. - auto slangType = pSlangGlobalScope->getLayout()->findTypeByName(typeConformance.first.mTypeName.c_str()); - auto slangInterfaceType = pSlangGlobalScope->getLayout()->findTypeByName(typeConformance.first.mInterfaceName.c_str()); + auto slangType = pSlangGlobalScope->getLayout()->findTypeByName(typeConformance.first.typeName.c_str()); + auto slangInterfaceType = pSlangGlobalScope->getLayout()->findTypeByName(typeConformance.first.interfaceName.c_str()); if (!slangType) { - log += fmt::format("Type '{}' in type conformance was not found.\n", typeConformance.first.mTypeName.c_str()); + log += fmt::format("Type '{}' in type conformance was not found.\n", typeConformance.first.typeName.c_str()); return {}; } if (!slangInterfaceType) { - log += - fmt::format("Interface type '{}' in type conformance was not found.\n", typeConformance.first.mInterfaceName.c_str()); + log += fmt::format("Interface type '{}' in type conformance was not found.\n", typeConformance.first.interfaceName.c_str()); return {}; } auto res = pSlangSession->createTypeConformanceComponentType( - slangType, slangInterfaceType, pTypeConformanceComponent.writeRef(), (SlangInt)typeConformance.second, + slangType, + slangInterfaceType, + pTypeConformanceComponent.writeRef(), + (SlangInt)typeConformance.second, pSlangDiagnostics.writeRef() ); if (SLANG_FAILED(res)) @@ -270,8 +287,10 @@ ref ProgramManager::createProgramKernels( { Slang::ComPtr pSlangDiagnostics; auto res = pSlangSession->createCompositeComponentType( - &typeConformanceComponentRawPtrList[0], (SlangInt)typeConformanceComponentRawPtrList.size(), - pTypeConformancesCompositeComponent.writeRef(), pSlangDiagnostics.writeRef() + &typeConformanceComponentRawPtrList[0], + (SlangInt)typeConformanceComponentRawPtrList.size(), + pTypeConformancesCompositeComponent.writeRef(), + pSlangDiagnostics.writeRef() ); if (SLANG_FAILED(res)) { @@ -285,10 +304,10 @@ ref ProgramManager::createProgramKernels( // Create one composite component type for the type conformances of each entry point group. // The type conformances for each group is the combination of the global and group type conformances. std::vector> typeConformancesCompositeComponents; - typeConformancesCompositeComponents.reserve(program.getEntryPointGroupCount()); - for (const auto& group : program.mDesc.mGroups) + typeConformancesCompositeComponents.reserve(program.mDesc.entryPointGroups.size()); + for (const auto& group : program.mDesc.entryPointGroups) { - Program::TypeConformanceList typeConformances = program.mTypeConformanceList; + TypeConformanceList typeConformances = program.mTypeConformanceList; typeConformances.add(group.typeConformances); if (auto typeConformanceComponentList = createTypeConformanceComponentList(typeConformances)) typeConformancesCompositeComponents.emplace_back(*typeConformanceComponentList); @@ -296,56 +315,56 @@ ref ProgramManager::createProgramKernels( return nullptr; } - // Create a `IComponentType` for each entry point. - uint32_t allEntryPointCount = uint32_t(program.mDesc.mEntryPoints.size()); - std::vector> pTypeConformanceSpecializedEntryPoints; std::vector pTypeConformanceSpecializedEntryPointsRawPtr; std::vector> pLinkedEntryPoints; - for (uint32_t ee = 0; ee < allEntryPointCount; ++ee) + // Create a `IComponentType` for each entry point. + for (size_t groupIndex = 0; groupIndex < program.mDesc.entryPointGroups.size(); ++groupIndex) { - auto pSlangEntryPoint = programVersion.getSlangEntryPoint(ee); + const auto& entryPointGroup = program.mDesc.entryPointGroups[groupIndex]; - int32_t groupIndex = program.mDesc.mEntryPoints[ee].groupIndex; - FALCOR_ASSERT(groupIndex >= 0 && groupIndex < typeConformancesCompositeComponents.size()); + for (const auto& entryPoint : entryPointGroup.entryPoints) + { + auto pSlangEntryPoint = programVersion.getSlangEntryPoint(entryPoint.globalIndex); - Slang::ComPtr pSlangDiagnostics; + Slang::ComPtr pSlangDiagnostics; - Slang::ComPtr pTypeComformanceSpecializedEntryPoint; - if (typeConformancesCompositeComponents[groupIndex]) - { - slang::IComponentType* componentTypes[] = {pSlangEntryPoint, typeConformancesCompositeComponents[groupIndex]}; - auto res = pSlangSession->createCompositeComponentType( - componentTypes, 2, pTypeComformanceSpecializedEntryPoint.writeRef(), pSlangDiagnostics.writeRef() - ); - if (SLANG_FAILED(res)) + Slang::ComPtr pTypeComformanceSpecializedEntryPoint; + if (typeConformancesCompositeComponents[groupIndex]) { - log += "Slang call createCompositeComponentType() failed.\n"; - return nullptr; + slang::IComponentType* componentTypes[] = {pSlangEntryPoint, typeConformancesCompositeComponents[groupIndex]}; + auto res = pSlangSession->createCompositeComponentType( + componentTypes, 2, pTypeComformanceSpecializedEntryPoint.writeRef(), pSlangDiagnostics.writeRef() + ); + if (SLANG_FAILED(res)) + { + log += "Slang call createCompositeComponentType() failed.\n"; + return nullptr; + } } - } - else - { - pTypeComformanceSpecializedEntryPoint = pSlangEntryPoint; - } - pTypeConformanceSpecializedEntryPoints.push_back(pTypeComformanceSpecializedEntryPoint); - pTypeConformanceSpecializedEntryPointsRawPtr.push_back(pTypeComformanceSpecializedEntryPoint.get()); - - Slang::ComPtr pLinkedSlangEntryPoint; - { - slang::IComponentType* componentTypes[] = {pSpecializedSlangGlobalScope, pTypeComformanceSpecializedEntryPoint}; + else + { + pTypeComformanceSpecializedEntryPoint = pSlangEntryPoint; + } + pTypeConformanceSpecializedEntryPoints.push_back(pTypeComformanceSpecializedEntryPoint); + pTypeConformanceSpecializedEntryPointsRawPtr.push_back(pTypeComformanceSpecializedEntryPoint.get()); - auto res = pSlangSession->createCompositeComponentType( - componentTypes, 2, pLinkedSlangEntryPoint.writeRef(), pSlangDiagnostics.writeRef() - ); - if (SLANG_FAILED(res)) + Slang::ComPtr pLinkedSlangEntryPoint; { - log += "Slang call createCompositeComponentType() failed.\n"; - return nullptr; + slang::IComponentType* componentTypes[] = {pSpecializedSlangGlobalScope, pTypeComformanceSpecializedEntryPoint}; + + auto res = pSlangSession->createCompositeComponentType( + componentTypes, 2, pLinkedSlangEntryPoint.writeRef(), pSlangDiagnostics.writeRef() + ); + if (SLANG_FAILED(res)) + { + log += "Slang call createCompositeComponentType() failed.\n"; + return nullptr; + } } + pLinkedEntryPoints.push_back(pLinkedSlangEntryPoint); } - pLinkedEntryPoints.push_back(pLinkedSlangEntryPoint); } // Once specialization and linking are completed we need to @@ -394,14 +413,12 @@ ref ProgramManager::createProgramKernels( std::vector componentTypesForProgram; componentTypesForProgram.push_back(pSpecializedSlangGlobalScope); - for (uint32_t ee = 0; ee < allEntryPointCount; ++ee) - { - // TODO: Eventually this would need to use the specialized - // (but not linked) version of each entry point. - // - auto pSlangEntryPoint = programVersion.getSlangEntryPoint(ee); - componentTypesForProgram.push_back(pSlangEntryPoint); - } + // TODO: Eventually this would need to use the specialized + // (but not linked) version of each entry point. + // + componentTypesForProgram.insert( + componentTypesForProgram.end(), programVersion.getSlangEntryPoints().begin(), programVersion.getSlangEntryPoints().end() + ); // Add type conformances for all entry point groups. // TODO: Is it correct to put all these in the global scope? @@ -428,16 +445,17 @@ ref ProgramManager::createProgramKernels( // Create kernel objects for each entry point and cache them here. std::vector> allKernels; - for (uint32_t i = 0; i < allEntryPointCount; i++) + for (const auto& entryPointGroup : program.mDesc.entryPointGroups) { - auto pLinkedEntryPoint = pLinkedEntryPoints[i]; - auto entryPointDesc = program.mDesc.mEntryPoints[i]; - - ref kernel = EntryPointKernel::create(pLinkedEntryPoint, entryPointDesc.stage, entryPointDesc.exportName); - if (!kernel) - return nullptr; + for (const auto& entryPoint : entryPointGroup.entryPoints) + { + auto pLinkedEntryPoint = pLinkedEntryPoints[entryPoint.globalIndex]; + ref kernel = EntryPointKernel::create(pLinkedEntryPoint, entryPoint.type, entryPoint.exportName); + if (!kernel) + return nullptr; - allKernels.push_back(std::move(kernel)); + allKernels.push_back(std::move(kernel)); + } } // In order to construct the `ProgramKernels` we need to extract @@ -447,31 +465,37 @@ ref ProgramManager::createProgramKernels( // TODO: Because we aren't actually specializing entry-point groups, // we will again loop over the original unspecialized entry point - // groups from the `Program::Desc`, and assume that they line up + // groups from the `ProgramDesc`, and assume that they line up // one-to-one with the entries in `pLinkedEntryPointGroups`. // - uint32_t entryPointGroupCount = uint32_t(program.mDesc.mGroups.size()); - for (uint32_t gg = 0; gg < entryPointGroupCount; ++gg) + for (size_t groupIndex = 0; groupIndex < program.mDesc.entryPointGroups.size(); ++groupIndex) { - auto entryPointGroupDesc = program.mDesc.mGroups[gg]; + const auto& entryPointGroup = program.mDesc.entryPointGroups[groupIndex]; + // For each entry-point group we will collect the compiled kernel // code for its constituent entry points, using the "linked" // version of the entry-point group. // std::vector> kernels; - for (auto entryPointIndex : entryPointGroupDesc.entryPoints) + for (const auto& entryPoint : entryPointGroup.entryPoints) { - kernels.push_back(allKernels[entryPointIndex]); + kernels.push_back(allKernels[entryPoint.globalIndex]); } - auto pGroupReflector = pReflector->getEntryPointGroup(gg); + auto pGroupReflector = pReflector->getEntryPointGroup(groupIndex); auto pEntryPointGroupKernels = createEntryPointGroupKernels(kernels, pGroupReflector); entryPointGroups.push_back(pEntryPointGroupKernels); } auto descStr = program.getProgramDescString(); ref pProgramKernels = ProgramKernels::create( - mpDevice, &programVersion, pSpecializedSlangGlobalScope, pTypeConformanceSpecializedEntryPointsRawPtr, pReflector, entryPointGroups, - log, descStr + mpDevice, + &programVersion, + pSpecializedSlangGlobalScope, + pTypeConformanceSpecializedEntryPointsRawPtr, + pReflector, + entryPointGroups, + log, + descStr ); timer.update(); @@ -508,7 +532,7 @@ ref ProgramManager::createEntryPointGroupKernels( if (pReflector->getResourceRangeCount() > 0 || pReflector->getRootDescriptorRangeCount() > 0 || pReflector->getParameterBlockSubObjectRangeCount() > 0) { - throw RuntimeError("Local root signatures are not supported for raytracing entry points."); + FALCOR_THROW("Local root signatures are not supported for raytracing entry points."); } std::string exportName = fmt::format("HitGroup{}", mHitGroupID++); return EntryPointGroupKernels::create(EntryPointGroupKernels::Type::RtHitGroup, kernels, exportName); @@ -524,6 +548,18 @@ ref ProgramManager::createEntryPointGroupKernels( return nullptr; } +std::string ProgramManager::getHlslLanguagePrelude() const +{ + Slang::ComPtr prelude; + mpDevice->getSlangGlobalSession()->getLanguagePrelude(SLANG_SOURCE_LANGUAGE_HLSL, prelude.writeRef()); + return std::string(reinterpret_cast(prelude->getBufferPointer()), prelude->getBufferSize()); +} + +void ProgramManager::setHlslLanguagePrelude(const std::string& prelude) +{ + mpDevice->getSlangGlobalSession()->setLanguagePrelude(SLANG_SOURCE_LANGUAGE_HLSL, prelude.c_str()); +} + void ProgramManager::registerProgramForReload(Program* program) { mLoadedPrograms.push_back(program); @@ -608,19 +644,19 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr slang::TargetDesc targetDesc; targetDesc.format = SLANG_TARGET_UNKNOWN; - targetDesc.profile = pSlangGlobalSession->findProfile(getSlangProfileString(program.mDesc.mShaderModel).c_str()); + targetDesc.profile = pSlangGlobalSession->findProfile(getSlangProfileString(program.mDesc.shaderModel).c_str()); if (targetDesc.profile == SLANG_PROFILE_UNKNOWN) - throw RuntimeError("Can't find Slang profile for shader model {}", program.mDesc.mShaderModel); + FALCOR_THROW("Can't find Slang profile for shader model {}", program.mDesc.shaderModel); // Get compiler flags and adjust with forced flags. - Program::CompilerFlags compilerFlags = program.mDesc.getCompilerFlags(); + SlangCompilerFlags compilerFlags = program.mDesc.compilerFlags; compilerFlags &= ~mForcedCompilerFlags.disabled; compilerFlags |= mForcedCompilerFlags.enabled; // Set floating point mode. If no shader compiler flags for this were set, we use Slang's default mode. - bool flagFast = is_set(compilerFlags, Program::CompilerFlags::FloatingPointModeFast); - bool flagPrecise = is_set(compilerFlags, Program::CompilerFlags::FloatingPointModePrecise); + bool flagFast = is_set(compilerFlags, SlangCompilerFlags::FloatingPointModeFast); + bool flagPrecise = is_set(compilerFlags, SlangCompilerFlags::FloatingPointModePrecise); if (flagFast && flagPrecise) { logWarning( @@ -640,6 +676,11 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr targetDesc.forceGLSLScalarBufferLayout = true; + if (getEnvironmentVariable("FALCOR_USE_SLANG_SPIRV_BACKEND") == "1") + { + targetDesc.flags |= SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY; + } + const char* targetMacroName; // Pick the right target based on the current graphics API @@ -672,7 +713,10 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr // Add a `#define`s based on the target and shader model. addSlangDefine(targetMacroName, "1"); - std::string sm = "__SM_" + program.mDesc.mShaderModel + "__"; + // Add a `#define` based on the shader model. + std::string sm = fmt::format( + "__SM_{}_{}__", getShaderModelMajorVersion(program.mDesc.shaderModel), getShaderModelMinorVersion(program.mDesc.shaderModel) + ); addSlangDefine(sm.c_str(), "1"); sessionDesc.preprocessorMacros = slangDefines.data(); @@ -685,7 +729,7 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr // to allow it to compute correct reflection information. Slang then invokes the downstream compiler. // Column major option can be useful when compiling external shader sources that don't depend // on anything Falcor. - bool useColumnMajor = is_set(compilerFlags, Program::CompilerFlags::MatrixLayoutColumnMajor); + bool useColumnMajor = is_set(compilerFlags, SlangCompilerFlags::MatrixLayoutColumnMajor); sessionDesc.defaultMatrixLayoutMode = useColumnMajor ? SLANG_MATRIX_LAYOUT_COLUMN_MAJOR : SLANG_MATRIX_LAYOUT_ROW_MAJOR; Slang::ComPtr pSlangSession; @@ -694,18 +738,6 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr program.mFileTimeMap.clear(); // TODO @skallweit - if (!program.mDesc.mLanguagePrelude.empty()) - { - if (targetDesc.format == SLANG_DXIL) - { - pSlangGlobalSession->setLanguagePrelude(SLANG_SOURCE_LANGUAGE_HLSL, program.mDesc.mLanguagePrelude.c_str()); - } - else - { - throw RuntimeError("Language prelude set for unsupported target {}", targetMacroName); - } - } - SlangCompileRequest* pSlangRequest = nullptr; pSlangSession->createCompileRequest(&pSlangRequest); FALCOR_ASSERT(pSlangRequest); @@ -715,11 +747,11 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr spOverrideDiagnosticSeverity(pSlangRequest, 30081, SLANG_SEVERITY_DISABLED); // implicit conversion // Enable/disable intermediates dump - bool dumpIR = is_set(program.mDesc.getCompilerFlags(), Program::CompilerFlags::DumpIntermediates); + bool dumpIR = is_set(program.mDesc.compilerFlags, SlangCompilerFlags::DumpIntermediates); spSetDumpIntermediates(pSlangRequest, dumpIR); // Set debug level - if (mGenerateDebugInfo || is_set(program.mDesc.getCompilerFlags(), Program::CompilerFlags::GenerateDebugInfo)) + if (mGenerateDebugInfo || is_set(program.mDesc.compilerFlags, SlangCompilerFlags::GenerateDebugInfo)) spSetDebugInfoLevel(pSlangRequest, SLANG_DEBUG_INFO_LEVEL_STANDARD); // Configure any flags for the Slang compilation step @@ -736,7 +768,9 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr // Set additional command line arguments. { std::vector args; - for (const auto& arg : program.mDesc.mCompilerArguments) + for (const auto& arg : mGlobalCompilerArguments) + args.push_back(arg.c_str()); + for (const auto& arg : program.mDesc.compilerArguments) args.push_back(arg.c_str()); #if FALCOR_NVAPI_AVAILABLE // If NVAPI is available, we need to inform slang/dxc where to find it. @@ -748,47 +782,41 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr spProcessCommandLineArguments(pSlangRequest, args.data(), (int)args.size()); } - // Now lets add all our input shader code, one-by-one - int translationUnitsAdded = 0; - int translationUnitIndex = -1; - - for (const auto& src : program.mDesc.mSources) + for (size_t moduleIndex = 0; moduleIndex < program.mDesc.shaderModules.size(); ++moduleIndex) { - // Register new translation unit with Slang if needed. - if (translationUnitIndex < 0 || src.source.createTranslationUnit) - { - // If module name is empty, pass in nullptr to let Slang generate a name internally. - const char* name = !src.source.moduleName.empty() ? src.source.moduleName.c_str() : nullptr; - translationUnitIndex = spAddTranslationUnit(pSlangRequest, SLANG_SOURCE_LANGUAGE_SLANG, name); - FALCOR_ASSERT(translationUnitIndex == translationUnitsAdded); - translationUnitsAdded++; - } - FALCOR_ASSERT(translationUnitIndex >= 0); + const auto& module = program.mDesc.shaderModules[moduleIndex]; + // If module name is empty, pass in nullptr to let Slang generate a name internally. + const char* name = !module.name.empty() ? module.name.c_str() : nullptr; + int translationUnitIndex = spAddTranslationUnit(pSlangRequest, SLANG_SOURCE_LANGUAGE_SLANG, name); + FALCOR_ASSERT(translationUnitIndex == moduleIndex); - // Add source code to the translation unit - if (src.getType() == Program::ShaderModule::Type::File) + for (const auto& source : module.sources) { - // If this is not an HLSL or a SLANG file, display a warning - const auto& path = src.source.filePath; - if (!(hasExtension(path, "hlsl") || hasExtension(path, "slang"))) + // Add source code to the translation unit + if (source.type == ProgramDesc::ShaderSource::Type::File) { - logWarning( - "Compiling a shader file which is not a SLANG file or an HLSL file. This is not an error, but make sure that the file " - "contains valid shaders" - ); + // If this is not an HLSL or a SLANG file, display a warning + const auto& path = source.path; + if (!(hasExtension(path, "hlsl") || hasExtension(path, "slang"))) + { + logWarning( + "Compiling a shader file which is not a SLANG file or an HLSL file. This is not an error, but make sure that the " + "file contains valid shaders" + ); + } + std::filesystem::path fullPath; + if (!findFileInShaderDirectories(path, fullPath)) + { + spDestroyCompileRequest(pSlangRequest); + FALCOR_THROW("Can't find shader file {}", path); + } + spAddTranslationUnitSourceFile(pSlangRequest, translationUnitIndex, fullPath.string().c_str()); } - std::filesystem::path fullPath; - if (!findFileInShaderDirectories(path, fullPath)) + else { - spDestroyCompileRequest(pSlangRequest); - throw RuntimeError("Can't find shader file {}", path); + FALCOR_ASSERT(source.type == ProgramDesc::ShaderSource::Type::String); + spAddTranslationUnitSourceString(pSlangRequest, translationUnitIndex, source.path.string().c_str(), source.string.c_str()); } - spAddTranslationUnitSourceFile(pSlangRequest, translationUnitIndex, fullPath.string().c_str()); - } - else - { - FALCOR_ASSERT(src.getType() == Program::ShaderModule::Type::String); - spAddTranslationUnitSourceString(pSlangRequest, translationUnitIndex, src.source.modulePath.c_str(), src.source.str.c_str()); } } @@ -796,9 +824,12 @@ SlangCompileRequest* ProgramManager::createSlangCompileRequest(const Program& pr // Each entry point references the index of the source // it uses, and luckily, the Slang API can use these // indices directly. - for (auto& entryPoint : program.mDesc.mEntryPoints) + for (const auto& entryPointGroup : program.mDesc.entryPointGroups) { - spAddEntryPoint(pSlangRequest, entryPoint.sourceIndex, entryPoint.name.c_str(), getSlangStage(entryPoint.stage)); + for (const auto& entryPoint : entryPointGroup.entryPoints) + { + spAddEntryPoint(pSlangRequest, entryPointGroup.shaderModuleIndex, entryPoint.name.c_str(), getSlangStage(entryPoint.type)); + } } return pSlangRequest; diff --git a/Source/Falcor/Core/Program/ProgramManager.h b/Source/Falcor/Core/Program/ProgramManager.h index 6af304c36..b997eae36 100644 --- a/Source/Falcor/Core/Program/ProgramManager.h +++ b/Source/Falcor/Core/Program/ProgramManager.h @@ -46,8 +46,8 @@ class FALCOR_API ProgramManager */ struct ForcedCompilerFlags { - Program::CompilerFlags enabled = Program::CompilerFlags::None; ///< Compiler flags forcefully enabled on all shaders - Program::CompilerFlags disabled = Program::CompilerFlags::None; ///< Compiler flags forcefully enabled on all shaders + SlangCompilerFlags enabled = SlangCompilerFlags::None; ///< Compiler flags forcefully enabled on all shaders + SlangCompilerFlags disabled = SlangCompilerFlags::None; ///< Compiler flags forcefully enabled on all shaders }; struct CompilationStats @@ -60,7 +60,7 @@ class FALCOR_API ProgramManager double programKernelsTotalTime = 0.0; }; - Program::Desc applyForcedCompilerFlags(Program::Desc desc) const; + ProgramDesc applyForcedCompilerFlags(ProgramDesc desc) const; void registerProgramForReload(Program* program); void unregisterProgramForReload(Program* program); @@ -78,6 +78,12 @@ class FALCOR_API ProgramManager const ref& pReflector ) const; + /// Get the global HLSL language prelude. + std::string getHlslLanguagePrelude() const; + + /// Set the global HLSL language prelude. + void setHlslLanguagePrelude(const std::string& prelude); + /** * Reload and relink all programs. * @param[in] forceReload Force reloading all programs. @@ -97,6 +103,18 @@ class FALCOR_API ProgramManager */ void removeGlobalDefines(const DefineList& defineList); + /** + * Set compiler arguments applied to all programs. + * @param[in] args Compiler arguments. + */ + void setGlobalCompilerArguments(const std::vector& args) { mGlobalCompilerArguments = args; } + + /** + * Get compiler arguments applied to all programs. + * @return List of compiler arguments. + */ + const std::vector& getGlobalCompilerArguments() const { return mGlobalCompilerArguments; } + /** * Enable/disable global generation of shader debug info. * @param[in] enabled Enable/disable. @@ -135,6 +153,7 @@ class FALCOR_API ProgramManager mutable CompilationStats mCompilationStats; DefineList mGlobalDefineList; + std::vector mGlobalCompilerArguments; bool mGenerateDebugInfo = false; ForcedCompilerFlags mForcedCompilerFlags; diff --git a/Source/Falcor/Core/Program/ProgramReflection.cpp b/Source/Falcor/Core/Program/ProgramReflection.cpp index 0f10f2255..71cd4eca0 100644 --- a/Source/Falcor/Core/Program/ProgramReflection.cpp +++ b/Source/Falcor/Core/Program/ProgramReflection.cpp @@ -30,6 +30,7 @@ #include "ProgramVersion.h" #include "Core/API/Device.h" #include "Utils/StringUtils.h" +#include "Utils/Scripting/ScriptBindings.h" #include @@ -44,10 +45,9 @@ namespace const char* kRootDescriptorAttribute = "root"; } -TypedShaderVarOffset::TypedShaderVarOffset(ref pType, ShaderVarOffset offset) : ShaderVarOffset(offset), mpType(pType) -{} +TypedShaderVarOffset::TypedShaderVarOffset(const ReflectionType* pType, ShaderVarOffset offset) : ShaderVarOffset(offset), mpType(pType) {} -TypedShaderVarOffset TypedShaderVarOffset::operator[](const std::string& name) const +TypedShaderVarOffset TypedShaderVarOffset::operator[](std::string_view name) const { if (!isValid()) return *this; @@ -62,12 +62,7 @@ TypedShaderVarOffset TypedShaderVarOffset::operator[](const std::string& name) c } } - throw RuntimeError("No member named '{}' found.", name); -} - -TypedShaderVarOffset TypedShaderVarOffset::operator[](const char* name) const -{ - return (*this)[std::string(name)]; + FALCOR_THROW("No member named '{}' found.", name); } TypedShaderVarOffset TypedShaderVarOffset::operator[](size_t index) const @@ -77,10 +72,10 @@ TypedShaderVarOffset TypedShaderVarOffset::operator[](size_t index) const TypedShaderVarOffset ReflectionType::getZeroOffset() const { - return TypedShaderVarOffset(ref(this), ShaderVarOffset::kZero); + return TypedShaderVarOffset(this, ShaderVarOffset::kZero); } -TypedShaderVarOffset ReflectionType::getMemberOffset(const std::string& name) const +TypedShaderVarOffset ReflectionType::getMemberOffset(std::string_view name) const { return getZeroOffset()[name]; } @@ -675,7 +670,7 @@ static size_t getRegisterIndexFromPath(const ReflectionPathLink* pPath, SlangPar offset += (uint32_t)pp->pVar->getOffset(category); continue; } - throw RuntimeError("Invalid reflection path"); + FALCOR_THROW("Invalid reflection path"); } return offset; } @@ -706,7 +701,7 @@ static uint32_t getRegisterSpaceFromPath(const ReflectionPathLink* pPath, SlangP continue; } - throw RuntimeError("Invalid reflection path"); + FALCOR_THROW("Invalid reflection path"); } return offset; } @@ -781,7 +776,7 @@ ref reflectResourceType( if (type != ReflectionResourceType::Type::RawBuffer && type != ReflectionResourceType::Type::StructuredBuffer && type != ReflectionResourceType::Type::AccelerationStructure) { - throw RuntimeError( + FALCOR_THROW( "Resource '{}' cannot be bound as root descriptor. Only raw buffers, structured buffers, and acceleration structures are " "supported.", name @@ -789,7 +784,7 @@ ref reflectResourceType( } if (shaderAccess != ReflectionResourceType::ShaderAccess::Read && shaderAccess != ReflectionResourceType::ShaderAccess::ReadWrite) { - throw RuntimeError("Buffer '{}' cannot be bound as root descriptor. Only SRV/UAVs are supported.", name); + FALCOR_THROW("Buffer '{}' cannot be bound as root descriptor. Only SRV/UAVs are supported.", name); } FALCOR_ASSERT( type != ReflectionResourceType::Type::AccelerationStructure || shaderAccess == ReflectionResourceType::ShaderAccess::Read @@ -804,7 +799,7 @@ ref reflectResourceType( if (structuredType == ReflectionResourceType::StructuredType::Append || structuredType == ReflectionResourceType::StructuredType::Consume) { - throw RuntimeError( + FALCOR_THROW( "StructuredBuffer '{}' cannot be bound as root descriptor. Only regular structured buffers are supported, not " "append/consume buffers.", name @@ -1048,7 +1043,7 @@ ref reflectType( return nullptr; case TypeReflection::Kind::GenericTypeParameter: // TODO: How to handle this type? Let it generate an error for now. - throw ArgumentError("Unexpected Slang type"); + FALCOR_THROW("Unexpected Slang type"); default: FALCOR_UNREACHABLE(); } @@ -1646,9 +1641,10 @@ int32_t ReflectionStructType::addMember(const ref& pVar, Re int32_t index = mNameToIndex[pVar->getName()]; if (*pVar != *mMembers[index]) { - throw RuntimeError( + FALCOR_THROW( "Mismatch in variable declarations between different shader stages. Variable name is '{}', struct name is '{}'.", - pVar->getName(), mName + pVar->getName(), + mName ); } return -1; @@ -1815,7 +1811,7 @@ static ShaderResourceType getShaderResourceType(const ReflectionResourceType* pT } } -const ref ParameterBlockReflection::getResource(const std::string& name) const +const ref ParameterBlockReflection::getResource(std::string_view name) const { return getElementType()->findMember(name); } @@ -1957,9 +1953,10 @@ struct ParameterBlockReflectionFinalizer case ParameterBlockReflection::ResourceRangeBindingInfo::Flavor::RootDescriptor: if (range.count > 1) { - throw RuntimeError( + FALCOR_THROW( "Root descriptor at register index {} in space {} is illegal. Root descriptors cannot be arrays.", - rangeBindingInfo.regIndex, rangeBindingInfo.regSpace + rangeBindingInfo.regIndex, + rangeBindingInfo.regSpace ); } pReflector->mRootDescriptorRangeIndices.push_back(rangeIndex); @@ -2043,7 +2040,7 @@ void ParameterBlockReflection::finalize() #endif } -ref ProgramReflection::getParameterBlock(const std::string& name) const +ref ProgramReflection::getParameterBlock(std::string_view name) const { if (name == "") return mpDefaultBlock; @@ -2082,7 +2079,7 @@ TypedShaderVarOffset ReflectionStructType::findMemberByOffset(size_t offset) con return TypedShaderVarOffset::kInvalid; } -ref ReflectionType::findMember(const std::string& name) const +ref ReflectionType::findMember(std::string_view name) const { if (auto pStructType = asStructType()) { @@ -2096,7 +2093,7 @@ ref ReflectionType::findMember(const std::string& name) con return nullptr; } -int32_t ReflectionStructType::getMemberIndex(const std::string& name) const +int32_t ReflectionStructType::getMemberIndex(std::string_view name) const { auto it = mNameToIndex.find(name); if (it == mNameToIndex.end()) @@ -2104,7 +2101,7 @@ int32_t ReflectionStructType::getMemberIndex(const std::string& name) const return it->second; } -const ref& ReflectionStructType::getMember(const std::string& name) const +const ref& ReflectionStructType::getMember(std::string_view name) const { static const ref pNull; auto index = getMemberIndex(name); @@ -2151,7 +2148,7 @@ const ReflectionType* ReflectionType::unwrapArray() const const ReflectionType* pType = this; while (auto pArrayType = pType->asArrayType()) { - pType = pArrayType->getElementType().get(); + pType = pArrayType->getElementType(); } return pType; } @@ -2164,7 +2161,7 @@ uint32_t ReflectionType::getTotalArrayElementCount() const while (auto pArrayType = pType->asArrayType()) { result *= pArrayType->getElementCount(); - pType = pArrayType->getElementType().get(); + pType = pArrayType->getElementType(); } return result; } @@ -2260,12 +2257,12 @@ ReflectionStructType::ReflectionStructType(size_t size, const std::string& name, : ReflectionType(ReflectionType::Kind::Struct, size, pSlangTypeLayout), mName(name) {} -ParameterBlockReflection::BindLocation ParameterBlockReflection::getResourceBinding(const std::string& name) const +ParameterBlockReflection::BindLocation ParameterBlockReflection::getResourceBinding(std::string_view name) const { return getElementType()->getMemberOffset(name); } -const ref ProgramReflection::getResource(const std::string& name) const +const ref ProgramReflection::getResource(std::string_view name) const { return mpDefaultBlock->getResource(name); } @@ -2370,8 +2367,8 @@ bool ReflectionVar::operator==(const ReflectionVar& other) const return true; } -const ProgramReflection::ShaderVariable* getShaderAttribute( - const std::string& name, +inline const ProgramReflection::ShaderVariable* getShaderAttribute( + std::string_view name, const ProgramReflection::VariableMap& varMap, const std::string& funcName ) @@ -2380,28 +2377,29 @@ const ProgramReflection::ShaderVariable* getShaderAttribute( return (it == varMap.end()) ? nullptr : &(it->second); } -const ProgramReflection::ShaderVariable* ProgramReflection::getVertexAttributeBySemantic(const std::string& semantic) const +const ProgramReflection::ShaderVariable* ProgramReflection::getVertexAttributeBySemantic(std::string_view semantic) const { return getShaderAttribute(semantic, mVertAttrBySemantic, "getVertexAttributeBySemantic()"); } -const ProgramReflection::ShaderVariable* ProgramReflection::getVertexAttribute(const std::string& name) const +const ProgramReflection::ShaderVariable* ProgramReflection::getVertexAttribute(std::string_view name) const { return getShaderAttribute(name, mVertAttr, "getVertexAttribute()"); } -const ProgramReflection::ShaderVariable* ProgramReflection::getPixelShaderOutput(const std::string& name) const +const ProgramReflection::ShaderVariable* ProgramReflection::getPixelShaderOutput(std::string_view name) const { return getShaderAttribute(name, mPsOut, "getPixelShaderOutput()"); } -ref ProgramReflection::findType(const std::string& name) const +ref ProgramReflection::findType(std::string_view name) const { auto iter = mMapNameToType.find(name); if (iter != mMapNameToType.end()) return iter->second; - auto pSlangType = mpSlangReflector->findTypeByName(name.c_str()); + std::string nameStr{name}; + auto pSlangType = mpSlangReflector->findTypeByName(nameStr.c_str()); if (!pSlangType) return nullptr; auto pSlangTypeLayout = mpSlangReflector->getTypeLayout(pSlangType); @@ -2410,12 +2408,12 @@ ref ProgramReflection::findType(const std::string& name) const if (!pFalcorTypeLayout) return nullptr; - mMapNameToType.insert(std::make_pair(name, pFalcorTypeLayout)); + mMapNameToType.insert(std::make_pair(std::move(nameStr), pFalcorTypeLayout)); return pFalcorTypeLayout; } -ref ProgramReflection::findMember(const std::string& name) const +ref ProgramReflection::findMember(std::string_view name) const { return mpDefaultBlock->findMember(name); } @@ -2451,4 +2449,199 @@ bool ReflectionInterfaceType::operator==(const ReflectionType& other) const return (*this == *pOtherInterface); } +FALCOR_SCRIPT_BINDING(ReflectionArrayType) +{ + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionType) + + pybind11::class_> reflectionArrayType(m, "ReflectionArrayType"); + reflectionArrayType.def_property_readonly("element_type", &ReflectionArrayType::getElementType); + reflectionArrayType.def_property_readonly("element_count", &ReflectionArrayType::getElementCount); + reflectionArrayType.def_property_readonly("element_byte_stride", &ReflectionArrayType::getElementByteStride); + reflectionArrayType.def( + "__repr__", + [](const ReflectionArrayType& self) + { + return fmt::format( + "ReflectionArrayType(element_type={}, element_count={}, element_byte_stride={})", + ScriptBindings::repr(self.getElementType()), + self.getElementCount(), + self.getElementByteStride() + ); + } + ); +} + +FALCOR_SCRIPT_BINDING(ReflectionStructType) +{ + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionType) + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionVar) + + pybind11::class_> reflectionStructType(m, "ReflectionStructType"); + reflectionStructType.def_property_readonly("name", &ReflectionStructType::getName); + reflectionStructType.def_property_readonly( + "members", + [](const ReflectionStructType& self) + { + pybind11::dict members; + for (size_t i = 0; i < self.getMemberCount(); i++) + { + auto member = self.getMember(i); + members[pybind11::str(member->getName())] = member; + } + return members; + } + ); + reflectionStructType.def( + "__repr__", + [](const ReflectionStructType& self) + { + std::string members; + for (size_t i = 0; i < self.getMemberCount(); i++) + { + if (i > 0) + members += ", "; + members += ScriptBindings::repr(self.getMember(i)); + } + return fmt::format("ReflectionStructType(name={}, members=[{}])", ScriptBindings::repr(self.getName()), members); + } + ); + reflectionStructType.def("__getitem__", [](ReflectionStructType& self, std::string_view name) { return self.getMember(name); }); + reflectionStructType.def("__getitem__", [](ReflectionStructType& self, size_t index) { return self.getMember(index); }); + reflectionStructType.def("__getattr__", [](ReflectionStructType& self, std::string_view name) { return self.getMember(name); }); +} + +FALCOR_SCRIPT_BINDING(ReflectionBasicType) +{ + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionType) + + pybind11::class_> reflectionBasicType(m, "ReflectionBasicType"); + + pybind11::falcor_enum(reflectionBasicType, "Type"); + + reflectionBasicType.def_property_readonly("type", &ReflectionBasicType::getType); + reflectionBasicType.def_property_readonly("is_row_major", &ReflectionBasicType::isRowMajor); + reflectionBasicType.def( + "__repr__", + [](const ReflectionBasicType& self) + { return fmt::format("ReflectionBasicType(type={}, is_row_major={})", ScriptBindings::repr(self.getType()), self.isRowMajor()); } + ); +} + +FALCOR_SCRIPT_BINDING(ReflectionResourceType) +{ + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionType) + + pybind11::class_> reflectionResourceType( + m, "ReflectionResourceType" + ); + + pybind11::falcor_enum(reflectionResourceType, "ShaderAccess"); + pybind11::falcor_enum(reflectionResourceType, "ReturnType"); + pybind11::falcor_enum(reflectionResourceType, "Dimensions"); + pybind11::falcor_enum(reflectionResourceType, "StructuredType"); + pybind11::falcor_enum(reflectionResourceType, "Type"); + + reflectionResourceType.def_property_readonly("type", &ReflectionResourceType::getType); + reflectionResourceType.def_property_readonly("size", &ReflectionResourceType::getSize); + reflectionResourceType.def_property_readonly("dimensions", &ReflectionResourceType::getDimensions); + reflectionResourceType.def_property_readonly("struct_type", &ReflectionResourceType::getStructType); + reflectionResourceType.def_property_readonly("structured_buffer_type", &ReflectionResourceType::getStructuredBufferType); + reflectionResourceType.def_property_readonly("return_type", &ReflectionResourceType::getReturnType); + reflectionResourceType.def_property_readonly("shader_access", &ReflectionResourceType::getShaderAccess); + reflectionResourceType.def( + "__repr__", + [](const ReflectionResourceType& self) + { + return fmt::format( + "ReflectionResourceType(type={}, size={}, dimensions={}, structured_buffer_type={}, return_type={}, shader_access={})", + ScriptBindings::repr(self.getType()), + self.getSize(), + ScriptBindings::repr(self.getDimensions()), + ScriptBindings::repr(self.getStructuredBufferType()), + ScriptBindings::repr(self.getReturnType()), + ScriptBindings::repr(self.getShaderAccess()) + ); + } + ); +} + +FALCOR_SCRIPT_BINDING(ReflectionInterfaceType) +{ + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionType) + + pybind11::class_> reflectionInterfaceType( + m, "ReflectionInterfaceType" + ); + + reflectionInterfaceType.def("__repr__", [](const ReflectionInterfaceType& self) { return "ReflectionInterfaceType()"; }); +} + +FALCOR_SCRIPT_BINDING(ReflectionType) +{ + pybind11::class_> reflectionType(m, "ReflectionType"); + + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionArrayType) + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionStructType) + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionBasicType) + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionResourceType) + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionInterfaceType) + + pybind11::falcor_enum(reflectionType, "Kind"); + + reflectionType.def_property_readonly("kind", &ReflectionType::getKind); + reflectionType.def_property_readonly("as_array_type", &ReflectionType::asArrayType); + reflectionType.def_property_readonly("as_struct_type", &ReflectionType::asStructType); + reflectionType.def_property_readonly("as_basic_type", &ReflectionType::asBasicType); + reflectionType.def_property_readonly("as_resource_type", &ReflectionType::asResourceType); + reflectionType.def_property_readonly("as_interface_type", &ReflectionType::asInterfaceType); + reflectionType.def( + "__repr__", [](const ReflectionType& self) { return fmt::format("ReflectionType(kind={})", ScriptBindings::repr(self.getKind())); } + ); +} + +FALCOR_SCRIPT_BINDING(ReflectionVar) +{ + pybind11::class_> reflectionVar(m, "ReflectionVar"); + + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionType) + + reflectionVar.def_property_readonly("name", &ReflectionVar::getName); + reflectionVar.def_property_readonly("type", &ReflectionVar::getType); + reflectionVar.def_property_readonly("offset", &ReflectionVar::getOffset); + reflectionVar.def( + "__repr__", + [](const ReflectionVar& self) + { + return fmt::format( + "ReflectionVar(name=\"{}\", type={}, offset={})", self.getName(), ScriptBindings::repr(self.getType()), self.getOffset() + ); + } + ); +} + +FALCOR_SCRIPT_BINDING(ParameterBlockReflection) +{ + FALCOR_SCRIPT_BINDING_DEPENDENCY(ReflectionVar) + + pybind11::class_> parameterBlockReflection(m, "ParameterBlockReflection"); + + parameterBlockReflection.def_property_readonly("element_type", &ParameterBlockReflection::getElementType); + + parameterBlockReflection.def( + "__getitem__", [](ParameterBlockReflection& self, std::string_view name) { return self.getResource(name); } + ); + parameterBlockReflection.def( + "__getattr__", [](ParameterBlockReflection& self, std::string_view name) { return self.getResource(name); } + ); +} + +FALCOR_SCRIPT_BINDING(ProgramReflection) +{ + FALCOR_SCRIPT_BINDING_DEPENDENCY(ParameterBlockReflection) + + pybind11::class_> programReflection(m, "ProgramReflection"); + + programReflection.def_property_readonly("default_parameter_block", &ProgramReflection::getDefaultParameterBlock); +} + } // namespace Falcor diff --git a/Source/Falcor/Core/Program/ProgramReflection.h b/Source/Falcor/Core/Program/ProgramReflection.h index 52e6851c2..1d5f953a6 100644 --- a/Source/Falcor/Core/Program/ProgramReflection.h +++ b/Source/Falcor/Core/Program/ProgramReflection.h @@ -26,9 +26,10 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Assert.h" #include "Core/Macros.h" +#include "Core/Error.h" #include "Core/Object.h" +#include "Core/Enum.h" #include "Core/API/ShaderResourceType.h" #include "Utils/Math/Vector.h" #if FALCOR_HAS_D3D12 @@ -39,8 +40,8 @@ #include #include +#include #include -#include namespace Falcor { @@ -531,6 +532,10 @@ struct ShaderVarOffset * Because `TypedShaderVarOffset` inherits from `ShaderVarOffset` it can be used * in all the same places, and also implicitly converts to both * `UniformShaderVarOffset` and `ResourceShaderVarOffset`. + * + * This struct has a non-owning pointer to the type information. + * The caller is responsible for ensuring that the type information remains valid, + * which is typically owned by the `ParameterBlockReflection` object. */ struct TypedShaderVarOffset : ShaderVarOffset { @@ -543,7 +548,7 @@ struct TypedShaderVarOffset : ShaderVarOffset /** * Get the type of the shader variable. */ - ref getType() const { return mpType; } + const ReflectionType* getType() const { return mpType; } /** * Check if `this` represents a valid offset. @@ -553,12 +558,7 @@ struct TypedShaderVarOffset : ShaderVarOffset /** * Look up type and offset of a sub-field with the given `name`. */ - TypedShaderVarOffset operator[](const std::string& name) const; - - /** - * Look up type and offset of a sub-field with the given `name`. - */ - TypedShaderVarOffset operator[](const char*) const; + TypedShaderVarOffset operator[](std::string_view name) const; /** * Look up type and offset of a sub-element or sub-field with the given `index`. @@ -571,10 +571,10 @@ struct TypedShaderVarOffset : ShaderVarOffset * The caller takes responsibility for ensuring that `pType` is a valid type * for the data at `offset`. */ - TypedShaderVarOffset(ref pType, ShaderVarOffset offset); + TypedShaderVarOffset(const ReflectionType* pType, ShaderVarOffset offset); private: - ref mpType; + const ReflectionType* mpType{nullptr}; }; /** @@ -601,6 +601,16 @@ class FALCOR_API ReflectionType : public Object Resource, ///< ReflectionResourceType Interface, ///< ReflectionInterfaceType }; + FALCOR_ENUM_INFO( + Kind, + { + {Kind::Array, "Array"}, + {Kind::Struct, "Struct"}, + {Kind::Basic, "Basic"}, + {Kind::Resource, "Resource"}, + {Kind::Interface, "Interface"}, + } + ); /** * Get the kind of this type. @@ -667,7 +677,7 @@ class FALCOR_API ReflectionType : public Object * * If this type doesn't have fields/members, or doesn't have a field/member matching `name`, then returns null. */ - ref findMember(const std::string& name) const; + ref findMember(std::string_view name) const; /** * Get the (type and) offset of a field/member with the given `name`. @@ -675,7 +685,7 @@ class FALCOR_API ReflectionType : public Object * If this type doesn't have fields/members, or doesn't have a field/member matching `name`, * then logs an error and returns an invalid offset. */ - TypedShaderVarOffset getMemberOffset(const std::string& name) const; + TypedShaderVarOffset getMemberOffset(std::string_view name) const; /** * Find a typed member/element offset corresponding to the given byte offset. @@ -811,7 +821,7 @@ class FALCOR_API ReflectionArrayType : public ReflectionType /** * Get the type of the array elements. */ - const ref& getElementType() const { return mpElementType; } + const ReflectionType* getElementType() const { return mpElementType.get(); } bool operator==(const ReflectionArrayType& other) const; bool operator==(const ReflectionType& other) const override; @@ -854,7 +864,7 @@ class FALCOR_API ReflectionStructType : public ReflectionType /** * Get member by name */ - const ref& getMember(const std::string& name) const; + const ref& getMember(std::string_view name) const; /** * Constant used to indicate that member lookup failed. @@ -866,7 +876,7 @@ class FALCOR_API ReflectionStructType : public ReflectionType * * Returns `kInvalidMemberIndex` if no such member exists. */ - int32_t getMemberIndex(const std::string& name) const; + int32_t getMemberIndex(std::string_view name) const; /** * Find a member based on a byte offset. @@ -902,8 +912,8 @@ class FALCOR_API ReflectionStructType : public ReflectionType private: ReflectionStructType(size_t size, const std::string& name, slang::TypeLayoutReflection* pSlangTypeLayout); - std::vector> mMembers; // Struct members - std::unordered_map mNameToIndex; // Translates from a name to an index in mMembers + std::vector> mMembers; // Struct members + std::map> mNameToIndex; // Translates from a name to an index in mMembers std::string mName; }; @@ -1000,6 +1010,78 @@ class FALCOR_API ReflectionBasicType : public ReflectionType Unknown = -1 }; + FALCOR_ENUM_INFO( + Type, + { + {Type::Bool, "Bool"}, + {Type::Bool2, "Bool2"}, + {Type::Bool3, "Bool3"}, + {Type::Bool4, "Bool4"}, + {Type::Uint8, "Uint8"}, + {Type::Uint8_2, "Uint8_2"}, + {Type::Uint8_3, "Uint8_3"}, + {Type::Uint8_4, "Uint8_4"}, + {Type::Uint16, "Uint16"}, + {Type::Uint16_2, "Uint16_2"}, + {Type::Uint16_3, "Uint16_3"}, + {Type::Uint16_4, "Uint16_4"}, + {Type::Uint, "Uint"}, + {Type::Uint2, "Uint2"}, + {Type::Uint3, "Uint3"}, + {Type::Uint4, "Uint4"}, + {Type::Uint64, "Uint64"}, + {Type::Uint64_2, "Uint64_2"}, + {Type::Uint64_3, "Uint64_3"}, + {Type::Uint64_4, "Uint64_4"}, + {Type::Int8, "Int8"}, + {Type::Int8_2, "Int8_2"}, + {Type::Int8_3, "Int8_3"}, + {Type::Int8_4, "Int8_4"}, + {Type::Int16, "Int16"}, + {Type::Int16_2, "Int16_2"}, + {Type::Int16_3, "Int16_3"}, + {Type::Int16_4, "Int16_4"}, + {Type::Int, "Int"}, + {Type::Int2, "Int2"}, + {Type::Int3, "Int3"}, + {Type::Int4, "Int4"}, + {Type::Int64, "Int64"}, + {Type::Int64_2, "Int64_2"}, + {Type::Int64_3, "Int64_3"}, + {Type::Int64_4, "Int64_4"}, + {Type::Float16, "Float16"}, + {Type::Float16_2, "Float16_2"}, + {Type::Float16_3, "Float16_3"}, + {Type::Float16_4, "Float16_4"}, + {Type::Float16_2x2, "Float16_2x2"}, + {Type::Float16_2x3, "Float16_2x3"}, + {Type::Float16_2x4, "Float16_2x4"}, + {Type::Float16_3x2, "Float16_3x2"}, + {Type::Float16_3x3, "Float16_3x3"}, + {Type::Float16_3x4, "Float16_3x4"}, + {Type::Float16_4x2, "Float16_4x2"}, + {Type::Float16_4x3, "Float16_4x3"}, + {Type::Float16_4x4, "Float16_4x4"}, + {Type::Float, "Float"}, + {Type::Float2, "Float2"}, + {Type::Float3, "Float3"}, + {Type::Float4, "Float4"}, + {Type::Float2x2, "Float2x2"}, + {Type::Float2x3, "Float2x3"}, + {Type::Float2x4, "Float2x4"}, + {Type::Float3x2, "Float3x2"}, + {Type::Float3x3, "Float3x3"}, + {Type::Float3x4, "Float3x4"}, + {Type::Float4x2, "Float4x2"}, + {Type::Float4x3, "Float4x3"}, + {Type::Float4x4, "Float4x4"}, + {Type::Float64, "Float64"}, + {Type::Float64_2, "Float64_2"}, + {Type::Float64_3, "Float64_3"}, + {Type::Float64_4, "Float64_4"}, + {Type::Unknown, "Unknown"}, + } + ); /** * Create a new object @@ -1044,6 +1126,14 @@ class FALCOR_API ReflectionResourceType : public ReflectionType Read, ReadWrite }; + FALCOR_ENUM_INFO( + ShaderAccess, + { + {ShaderAccess::Undefined, "Undefined"}, + {ShaderAccess::Read, "Read"}, + {ShaderAccess::ReadWrite, "ReadWrite"}, + } + ); /** * The expected return type @@ -1056,6 +1146,16 @@ class FALCOR_API ReflectionResourceType : public ReflectionType Int, Uint }; + FALCOR_ENUM_INFO( + ReturnType, + { + {ReturnType::Unknown, "Unknown"}, + {ReturnType::Float, "Float"}, + {ReturnType::Double, "Double"}, + {ReturnType::Int, "Int"}, + {ReturnType::Uint, "Uint"}, + } + ); /** * The resource dimension @@ -1077,6 +1177,23 @@ class FALCOR_API ReflectionResourceType : public ReflectionType Count }; + FALCOR_ENUM_INFO( + Dimensions, + { + {Dimensions::Unknown, "Unknown"}, + {Dimensions::Texture1D, "Texture1D"}, + {Dimensions::Texture2D, "Texture2D"}, + {Dimensions::Texture3D, "Texture3D"}, + {Dimensions::TextureCube, "TextureCube"}, + {Dimensions::Texture1DArray, "Texture1DArray"}, + {Dimensions::Texture2DArray, "Texture2DArray"}, + {Dimensions::Texture2DMS, "Texture2DMS"}, + {Dimensions::Texture2DMSArray, "Texture2DMSArray"}, + {Dimensions::TextureCubeArray, "TextureCubeArray"}, + {Dimensions::AccelerationStructure, "AccelerationStructure"}, + {Dimensions::Buffer, "Buffer"}, + } + ); /** * For structured-buffers, describes the type of the buffer @@ -1089,6 +1206,16 @@ class FALCOR_API ReflectionResourceType : public ReflectionType Append, ///< AppendStructuredBuffer Consume ///< ConsumeStructuredBuffer }; + FALCOR_ENUM_INFO( + StructuredType, + { + {StructuredType::Invalid, "Invalid"}, + {StructuredType::Default, "Default"}, + {StructuredType::Counter, "Counter"}, + {StructuredType::Append, "Append"}, + {StructuredType::Consume, "Consume"}, + } + ); /** * The type of the resource @@ -1103,6 +1230,18 @@ class FALCOR_API ReflectionResourceType : public ReflectionType ConstantBuffer, AccelerationStructure, }; + FALCOR_ENUM_INFO( + Type, + { + {Type::Texture, "Texture"}, + {Type::StructuredBuffer, "StructuredBuffer"}, + {Type::RawBuffer, "RawBuffer"}, + {Type::TypedBuffer, "TypedBuffer"}, + {Type::Sampler, "Sampler"}, + {Type::ConstantBuffer, "ConstantBuffer"}, + {Type::AccelerationStructure, "AccelerationStructure"}, + } + ); /** * Create a new object @@ -1124,7 +1263,7 @@ class FALCOR_API ReflectionResourceType : public ReflectionType /** * Get the struct-type */ - const ref& getStructType() const { return mpStructType; } + const ReflectionType* getStructType() const { return mpStructType.get(); } const ref& getParameterBlockReflector() const { return mpParameterBlockReflector; } void setParameterBlockReflector(const ref& pReflector) { mpParameterBlockReflector = pReflector; } @@ -1224,7 +1363,7 @@ class FALCOR_API ReflectionVar : public Object /** * Get the variable type */ - const ref& getType() const { return mpType; } + const ReflectionType* getType() const { return mpType.get(); } /** * Get the variable offset @@ -1277,12 +1416,12 @@ class FALCOR_API ParameterBlockReflection : public Object /** * Get the variable for a resource in the block */ - const ref getResource(const std::string& name) const; + const ref getResource(std::string_view name) const; /** * Get the bind-location for a resource in the block */ - BindLocation getResourceBinding(const std::string& name) const; + BindLocation getResourceBinding(std::string_view name) const; #if FALCOR_HAS_D3D12 /// Information on how a particular descriptor set should be filled in. @@ -1415,7 +1554,7 @@ class FALCOR_API ParameterBlockReflection : public Object ProgramVersion const* getProgramVersion() const { return mpProgramVersion; } - ref findMember(const std::string& name) const { return getElementType()->findMember(name); } + ref findMember(std::string_view name) const { return getElementType()->findMember(name); } protected: ParameterBlockReflection(ProgramVersion const* pProgramVersion); @@ -1472,8 +1611,6 @@ class FALCOR_API ParameterBlockReflection : public Object ProgramVersion const* mpProgramVersion = nullptr; }; -typedef ParameterBlockReflection ParameterBlockReflection; - class FALCOR_API EntryPointGroupReflection : public ParameterBlockReflection { public: @@ -1504,7 +1641,7 @@ class FALCOR_API ProgramReflection : public Object std::string semanticName; ///> The semantic name of the variable ReflectionBasicType::Type type = ReflectionBasicType::Type::Unknown; ///> The type of the variable }; - using VariableMap = std::unordered_map; + using VariableMap = std::map>; using BindLocation = ParameterBlockReflection::BindLocation; @@ -1534,7 +1671,7 @@ class FALCOR_API ProgramReflection : public Object /** * Get parameter block by name */ - ref getParameterBlock(const std::string& name) const; + ref getParameterBlock(std::string_view name) const; /** * Get the default (unnamed) parameter block. @@ -1554,30 +1691,30 @@ class FALCOR_API ProgramReflection : public Object /** * Get a resource from the default parameter block */ - const ref getResource(const std::string& name) const; + const ref getResource(std::string_view name) const; /** * Search for a vertex attribute by its semantic name */ - const ShaderVariable* getVertexAttributeBySemantic(const std::string& semantic) const; + const ShaderVariable* getVertexAttributeBySemantic(std::string_view semantic) const; /** * Search for a vertex attribute by the variable name */ - const ShaderVariable* getVertexAttribute(const std::string& name) const; + const ShaderVariable* getVertexAttribute(std::string_view name) const; /** * Get a pixel shader output variable */ - const ShaderVariable* getPixelShaderOutput(const std::string& name) const; + const ShaderVariable* getPixelShaderOutput(std::string_view name) const; /** * Look up a type by name. * @return nullptr if the type does not exist. */ - ref findType(const std::string& name) const; + ref findType(std::string_view name) const; - ref findMember(const std::string& name) const; + ref findMember(std::string_view name) const; const std::vector>& getEntryPointGroups() const { return mEntryPointGroups; } @@ -1607,129 +1744,19 @@ class FALCOR_API ProgramReflection : public Object VariableMap mVertAttrBySemantic; slang::ShaderReflection* mpSlangReflector = nullptr; - mutable std::map> mMapNameToType; + mutable std::map, std::less<>> mMapNameToType; std::vector> mEntryPointGroups; std::vector mHashedStrings; }; -inline const std::string to_string(ReflectionBasicType::Type type) -{ -#define type_2_string(a) \ - case ReflectionBasicType::Type::a: \ - return #a; - switch (type) - { - type_2_string(Bool); - type_2_string(Bool2); - type_2_string(Bool3); - type_2_string(Bool4); - type_2_string(Uint); - type_2_string(Uint2); - type_2_string(Uint3); - type_2_string(Uint4); - type_2_string(Int); - type_2_string(Int2); - type_2_string(Int3); - type_2_string(Int4); - type_2_string(Float); - type_2_string(Float2); - type_2_string(Float3); - type_2_string(Float4); - type_2_string(Float2x2); - type_2_string(Float2x3); - type_2_string(Float2x4); - type_2_string(Float3x2); - type_2_string(Float3x3); - type_2_string(Float3x4); - type_2_string(Float4x2); - type_2_string(Float4x3); - type_2_string(Float4x4); - default: - FALCOR_UNREACHABLE(); - return ""; - } -#undef type_2_string -} - -inline const std::string to_string(ReflectionResourceType::ShaderAccess access) -{ -#define access_2_string(a) \ - case ReflectionResourceType::ShaderAccess::a: \ - return #a; - switch (access) - { - access_2_string(Undefined); - access_2_string(Read); - access_2_string(ReadWrite); - default: - FALCOR_UNREACHABLE(); - return ""; - } -#undef access_2_string -} +FALCOR_ENUM_REGISTER(ReflectionType::Kind); +FALCOR_ENUM_REGISTER(ReflectionBasicType::Type); +FALCOR_ENUM_REGISTER(ReflectionResourceType::ShaderAccess); +FALCOR_ENUM_REGISTER(ReflectionResourceType::ReturnType); +FALCOR_ENUM_REGISTER(ReflectionResourceType::Dimensions); +FALCOR_ENUM_REGISTER(ReflectionResourceType::StructuredType); +FALCOR_ENUM_REGISTER(ReflectionResourceType::Type); -inline const std::string to_string(ReflectionResourceType::ReturnType retType) -{ -#define type_2_string(a) \ - case ReflectionResourceType::ReturnType::a: \ - return #a; - switch (retType) - { - type_2_string(Unknown); - type_2_string(Float); - type_2_string(Uint); - type_2_string(Int); - default: - FALCOR_UNREACHABLE(); - return ""; - } -#undef type_2_string -} - -inline const std::string to_string(ReflectionResourceType::Dimensions resource) -{ -#define type_2_string(a) \ - case ReflectionResourceType::Dimensions::a: \ - return #a; - switch (resource) - { - type_2_string(Unknown); - type_2_string(Texture1D); - type_2_string(Texture2D); - type_2_string(Texture3D); - type_2_string(TextureCube); - type_2_string(Texture1DArray); - type_2_string(Texture2DArray); - type_2_string(Texture2DMS); - type_2_string(Texture2DMSArray); - type_2_string(TextureCubeArray); - type_2_string(Buffer); - default: - FALCOR_UNREACHABLE(); - return ""; - } -#undef type_2_string -} - -inline const std::string to_string(ReflectionResourceType::Type type) -{ -#define type_2_string(a) \ - case ReflectionResourceType::Type::a: \ - return #a; - switch (type) - { - type_2_string(Texture); - type_2_string(ConstantBuffer); - type_2_string(StructuredBuffer); - type_2_string(RawBuffer); - type_2_string(TypedBuffer); - type_2_string(Sampler); - default: - FALCOR_UNREACHABLE(); - return ""; - } -#undef type_2_string -} } // namespace Falcor diff --git a/Source/Falcor/Core/Program/ProgramVars.cpp b/Source/Falcor/Core/Program/ProgramVars.cpp index fecea7c1c..cd3077845 100644 --- a/Source/Falcor/Core/Program/ProgramVars.cpp +++ b/Source/Falcor/Core/Program/ProgramVars.cpp @@ -26,8 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "ProgramVars.h" -#include "GraphicsProgram.h" -#include "ComputeProgram.h" +#include "Program.h" #include "Core/API/Device.h" #include "Core/API/ComputeContext.h" #include "Core/API/RenderContext.h" @@ -47,61 +46,28 @@ ProgramVars::ProgramVars(ref pDevice, const ref FALCOR_ASSERT(pReflector); } -GraphicsVars::GraphicsVars(ref pDevice, const ref& pReflector) : ProgramVars(pDevice, pReflector) {} - -ref GraphicsVars::create(ref pDevice, const ref& pReflector) +ref ProgramVars::create(ref pDevice, const ref& pReflector) { - if (pReflector == nullptr) - throw ArgumentError("Can't create a GraphicsVars object without a program reflector"); - return ref(new GraphicsVars(pDevice, pReflector)); + FALCOR_CHECK(pReflector, "Can't create a ProgramVars object without a program reflector"); + return ref(new ProgramVars(pDevice, pReflector)); } -ref GraphicsVars::create(ref pDevice, const GraphicsProgram* pProg) +ref ProgramVars::create(ref pDevice, const Program* pProg) { - if (pProg == nullptr) - throw ArgumentError("Can't create a GraphicsVars object without a program"); + FALCOR_CHECK(pProg, "Can't create a ProgramVars object without a program"); return create(pDevice, pProg->getReflector()); } -ref ComputeVars::create(ref pDevice, const ref& pReflector) -{ - if (pReflector == nullptr) - throw ArgumentError("Can't create a ComputeVars object without a program reflector"); - return ref(new ComputeVars(pDevice, pReflector)); -} - -ref ComputeVars::create(ref pDevice, const ComputeProgram* pProg) -{ - if (pProg == nullptr) - throw ArgumentError("Can't create a ComputeVars object without a program"); - return create(pDevice, pProg->getReflector()); -} - -ComputeVars::ComputeVars(ref pDevice, const ref& pReflector) : ProgramVars(pDevice, pReflector) {} - -void ComputeVars::dispatchCompute(ComputeContext* pContext, const uint3& threadGroupCount) -{ - auto pProgram = dynamic_cast(getReflection()->getProgramVersion()->getProgram()); - FALCOR_ASSERT(pProgram); - pProgram->dispatchCompute(pContext, this, threadGroupCount); -} - -RtProgramVars::RtProgramVars(ref pDevice, const ref& pProgram, const ref& pBindingTable) +RtProgramVars::RtProgramVars(ref pDevice, const ref& pProgram, const ref& pBindingTable) : ProgramVars(pDevice, pProgram->getReflector()), mpShaderTable(pDevice) { - if (pProgram == nullptr) - { - throw ArgumentError("RtProgramVars must have a raytracing program attached to it"); - } - if (pBindingTable == nullptr || !pBindingTable->getRayGen().isValid()) - { - throw ArgumentError("RtProgramVars must have a raygen program attached to it"); - } + FALCOR_CHECK(pProgram, "RtProgramVars must have a raytracing program attached to it"); + FALCOR_CHECK(pBindingTable && pBindingTable->getRayGen().isValid(), "RtProgramVars must have a raygen program attached to it"); init(pBindingTable); } -ref RtProgramVars::create(ref pDevice, const ref& pProgram, const ref& pBindingTable) +ref RtProgramVars::create(ref pDevice, const ref& pProgram, const ref& pBindingTable) { return ref(new RtProgramVars(pDevice, pProgram, pBindingTable)); } @@ -115,11 +81,10 @@ void RtProgramVars::init(const ref& pBindingTable) // groups that are used by the supplied binding table. // FALCOR_ASSERT(mpProgramVersion); - auto pProgram = dynamic_cast(mpProgramVersion->getProgram()); + auto pProgram = dynamic_cast(mpProgramVersion->getProgram()); FALCOR_ASSERT(pProgram); auto pReflector = mpProgramVersion->getReflector(); - auto& rtDesc = pProgram->getRtDesc(); std::set entryPointGroupIndices; // Ray generation and miss programs are easy: we just allocate space diff --git a/Source/Falcor/Core/Program/ProgramVars.h b/Source/Falcor/Core/Program/ProgramVars.h index 5bc0c7700..8ccf7f1f7 100644 --- a/Source/Falcor/Core/Program/ProgramVars.h +++ b/Source/Falcor/Core/Program/ProgramVars.h @@ -36,8 +36,7 @@ namespace Falcor { -class GraphicsProgram; -class ComputeProgram; +class Program; class ComputeContext; /** @@ -47,20 +46,6 @@ class ComputeContext; */ class FALCOR_API ProgramVars : public ParameterBlock { -public: - /** - * Get the program reflection interface - */ - const ref& getReflection() const { return mpReflector; } - -protected: - ProgramVars(ref pDevice, const ref& pReflector); - - ref mpReflector; -}; - -class FALCOR_API GraphicsVars : public ProgramVars -{ public: /** * Create a new graphics vars object. @@ -68,7 +53,7 @@ class FALCOR_API GraphicsVars : public ProgramVars * @param[in] pReflector A program reflection object containing the requested declarations. * @return A new object, or an exception is thrown if creation failed. */ - static ref create(ref pDevice, const ref& pReflector); + static ref create(ref pDevice, const ref& pReflector); /** * Create a new graphics vars object. @@ -76,38 +61,17 @@ class FALCOR_API GraphicsVars : public ProgramVars * @param[in] pProg A program containing the requested declarations. The active version of the program is used. * @return A new object, or an exception is thrown if creation failed. */ - static ref create(ref pDevice, const GraphicsProgram* pProg); + static ref create(ref pDevice, const Program* pProg); -protected: - GraphicsVars(ref pDevice, const ref& pReflector); -}; - -class FALCOR_API ComputeVars : public ProgramVars -{ -public: /** - * Create a new compute vars object. - * @param[in] pDevice GPU device. - * @param[in] pReflector A program reflection object containing the requested declarations. - * @return A new object, or an exception is thrown if creation failed. - */ - static ref create(ref pDevice, const ref& pReflector); - - /** - * Create a new compute vars object. - * @param[in] pDevice GPU device. - * @param[in] pProg A program containing the requested declarations. The active version of the program is used. - * @return A new object, or an exception is thrown if creation failed. - */ - static ref create(ref pDevice, const ComputeProgram* pProg); - - /** - * Dispatch the program using the argument values set in this object. + * Get the program reflection interface */ - void dispatchCompute(ComputeContext* pContext, const uint3& threadGroupCount); + const ref& getReflection() const { return mpReflector; } protected: - ComputeVars(ref pDevice, const ref& pReflector); + ProgramVars(ref pDevice, const ref& pReflector); + + ref mpReflector; }; class RtStateObject; @@ -125,7 +89,7 @@ class FALCOR_API RtProgramVars : public ProgramVars * @param[in] pBindingTable The raytracing binding table. * @return A new object, or an exception is thrown if creation failed. */ - static ref create(ref pDevice, const ref& pProgram, const ref& pBindingTable); + static ref create(ref pDevice, const ref& pProgram, const ref& pBindingTable); bool prepareShaderTable(RenderContext* pCtx, RtStateObject* pRtso); @@ -145,7 +109,7 @@ class FALCOR_API RtProgramVars : public ProgramVars using VarsVector = std::vector; - RtProgramVars(ref pDevice, const ref& pProgram, const ref& pBindingTable); + RtProgramVars(ref pDevice, const ref& pProgram, const ref& pBindingTable); void init(const ref& pBindingTable); diff --git a/Source/Falcor/Core/Program/ProgramVersion.cpp b/Source/Falcor/Core/Program/ProgramVersion.cpp index e1e2db0b8..018b753f3 100644 --- a/Source/Falcor/Core/Program/ProgramVersion.cpp +++ b/Source/Falcor/Core/Program/ProgramVersion.cpp @@ -29,6 +29,7 @@ #include "Program.h" #include "ProgramManager.h" #include "ProgramVars.h" +#include "Core/Error.h" #include "Core/API/Device.h" #include "Core/API/ParameterBlock.h" #include "Utils/Logger.h" @@ -232,8 +233,7 @@ ref ProgramVersion::getKernels(Device* pDevice, ProgramVar if (!log.empty()) { - std::string warn = "Warnings in program:\n" + getName() + "\n" + log; - logWarning(warn); + logWarning("Warnings in program:\n{}\n{}", getName(), log); } mpKernels[specializationKey] = pKernels; @@ -242,11 +242,11 @@ ref ProgramVersion::getKernels(Device* pDevice, ProgramVar else { // Failure - - std::string error = "Failed to link program:\n" + getName() + "\n\n" + log; - reportErrorAndAllowRetry(error); - - // Continue loop to keep trying... + std::string msg = fmt::format("Failed to link program:\n{}\n\n{}", getName(), log); + bool showMessageBox = is_set(getErrorDiagnosticFlags(), ErrorDiagnosticFlags::ShowMessageBoxOnError); + if (showMessageBox && reportErrorAndAllowRetry(msg)) + continue; + FALCOR_THROW(msg); } } } diff --git a/Source/Falcor/Core/Program/ProgramVersion.h b/Source/Falcor/Core/Program/ProgramVersion.h index 354fba2e2..e5879c59f 100644 --- a/Source/Falcor/Core/Program/ProgramVersion.h +++ b/Source/Falcor/Core/Program/ProgramVersion.h @@ -31,7 +31,7 @@ #include "Core/Macros.h" #include "Core/Object.h" #include "Core/API/fwd.h" -#include "Core/API/ShaderType.h" +#include "Core/API/Types.h" #include "Core/API/Handles.h" #include #include @@ -101,7 +101,7 @@ class FALCOR_API EntryPointKernel : public Object Slang::ComPtr pDiagnostics; if (SLANG_FAILED(mLinkedSlangEntryPoint->getEntryPointCode(0, 0, mpBlob.writeRef(), pDiagnostics.writeRef()))) { - throw RuntimeError(std::string("Shader compilation failed. \n") + (const char*)pDiagnostics->getBufferPointer()); + FALCOR_THROW(std::string("Shader compilation failed. \n") + (const char*)pDiagnostics->getBufferPointer()); } } @@ -279,10 +279,10 @@ class ProgramVersion : public Object slang::ISession* getSlangSession() const; slang::IComponentType* getSlangGlobalScope() const; slang::IComponentType* getSlangEntryPoint(uint32_t index) const; + const std::vector>& getSlangEntryPoints() const { return mpSlangEntryPoints; } protected: friend class Program; - friend class RtProgram; friend class ProgramManager; static ref createEmpty(Program* pProgram, slang::IComponentType* pSlangGlobalScope); diff --git a/Source/Falcor/Core/Program/RtBindingTable.cpp b/Source/Falcor/Core/Program/RtBindingTable.cpp index e10c765b7..2a745cd42 100644 --- a/Source/Falcor/Core/Program/RtBindingTable.cpp +++ b/Source/Falcor/Core/Program/RtBindingTable.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "RtBindingTable.h" -#include "Core/Errors.h" +#include "Core/Error.h" namespace Falcor { @@ -46,20 +46,10 @@ ref RtBindingTable::create(uint32_t missCount, uint32_t rayTypeC RtBindingTable::RtBindingTable(uint32_t missCount, uint32_t rayTypeCount, uint32_t geometryCount) : mMissCount(missCount), mRayTypeCount(rayTypeCount), mGeometryCount(geometryCount) { - if (missCount > kMaxMissCount) - { - throw ArgumentError("'missCount' exceeds the maximum supported ({})", kMaxMissCount); - } - if (rayTypeCount > kMaxRayTypeCount) - { - throw ArgumentError("'rayTypeCount' exceeds the maximum supported ({})", kMaxRayTypeCount); - } - + FALCOR_CHECK(missCount <= kMaxMissCount, "'missCount' exceeds the maximum supported ({})", kMaxMissCount); + FALCOR_CHECK(rayTypeCount <= kMaxRayTypeCount, "'rayTypeCount' exceeds the maximum supported ({})", kMaxRayTypeCount); size_t recordCount = 1ull + missCount + rayTypeCount * geometryCount; - if (recordCount > std::numeric_limits::max()) - { - throw ArgumentError("Raytracing binding table is too large"); - } + FALCOR_CHECK(recordCount <= std::numeric_limits::max(), "Raytracing binding table is too large"); // Create the binding table. All entries will be assigned a null shader initially. mShaderTable.resize(recordCount); @@ -72,23 +62,14 @@ void RtBindingTable::setRayGen(ShaderID shaderID) void RtBindingTable::setMiss(uint32_t missIndex, ShaderID shaderID) { - if (missIndex >= mMissCount) - { - throw ArgumentError("'missIndex' is out of range"); - } + FALCOR_CHECK(missIndex < mMissCount, "'missIndex' is out of range"); mShaderTable[getMissOffset(missIndex)] = shaderID; } void RtBindingTable::setHitGroup(uint32_t rayType, uint32_t geometryID, ShaderID shaderID) { - if (rayType >= mRayTypeCount) - { - throw ArgumentError("'rayType' is out of range"); - } - if (geometryID >= mGeometryCount) - { - throw ArgumentError("'geometryID' is out of range"); - } + FALCOR_CHECK(rayType < mRayTypeCount, "'rayType' is out of range"); + FALCOR_CHECK(geometryID < mGeometryCount, "'geometryID' is out of range"); mShaderTable[getHitGroupOffset(rayType, geometryID)] = shaderID; } diff --git a/Source/Falcor/Core/Program/RtBindingTable.h b/Source/Falcor/Core/Program/RtBindingTable.h index 4832e6ded..d288623c5 100644 --- a/Source/Falcor/Core/Program/RtBindingTable.h +++ b/Source/Falcor/Core/Program/RtBindingTable.h @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "RtProgram.h" +#include "Program.h" #include "Core/Macros.h" #include "Core/Object.h" #include "Scene/SceneIDs.h" @@ -43,13 +43,13 @@ namespace Falcor * and the mapping from (rayType, geometryID) to which hit group to execute. * * The user is responsible for creating a binding table for use with a particular - * RtProgram and Scene before creating an RtProgramVars object. + * Program and Scene before creating an RtProgramVars object. */ class FALCOR_API RtBindingTable : public Object { FALCOR_OBJECT(RtBindingTable) public: - using ShaderID = RtProgram::ShaderID; + using ShaderID = ProgramDesc::ShaderID; /** * Create a new binding table. diff --git a/Source/Falcor/Core/Program/RtProgram.cpp b/Source/Falcor/Core/Program/RtProgram.cpp deleted file mode 100644 index bbaf05234..000000000 --- a/Source/Falcor/Core/Program/RtProgram.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#include "RtProgram.h" -#include "ProgramManager.h" -#include "ProgramVars.h" - -#include - -namespace Falcor -{ -void RtProgram::Desc::init() -{ - mBaseDesc.setShaderModel("6_5"); -} - -RtProgram::ShaderID RtProgram::Desc::addRayGen( - const std::string& raygen, - const TypeConformanceList& typeConformances, - const std::string& entryPointNameSuffix -) -{ - checkArgument(!raygen.empty(), "'raygen' entry point name must not be empty"); - - mBaseDesc.beginEntryPointGroup(entryPointNameSuffix); - mBaseDesc.entryPoint(ShaderType::RayGeneration, raygen); - mBaseDesc.addTypeConformancesToGroup(typeConformances); - - mRayGenCount++; - return {mBaseDesc.mActiveGroup}; -} - -RtProgram::ShaderID RtProgram::Desc::addMiss( - const std::string& miss, - const TypeConformanceList& typeConformances, - const std::string& entryPointNameSuffix -) -{ - checkArgument(!miss.empty(), "'miss' entry point name must not be empty"); - - mBaseDesc.beginEntryPointGroup(entryPointNameSuffix); - mBaseDesc.entryPoint(ShaderType::Miss, miss); - mBaseDesc.addTypeConformancesToGroup(typeConformances); - - return {mBaseDesc.mActiveGroup}; -} - -RtProgram::ShaderID RtProgram::Desc::addHitGroup( - const std::string& closestHit, - const std::string& anyHit, - const std::string& intersection, - const TypeConformanceList& typeConformances, - const std::string& entryPointNameSuffix -) -{ - checkArgument( - !(closestHit.empty() && anyHit.empty() && intersection.empty()), - "At least one of 'closestHit', 'anyHit' or 'intersection' entry point names must not be empty" - ); - - mBaseDesc.beginEntryPointGroup(entryPointNameSuffix); - mBaseDesc.addTypeConformancesToGroup(typeConformances); - if (!closestHit.empty()) - { - mBaseDesc.entryPoint(ShaderType::ClosestHit, closestHit); - } - if (!anyHit.empty()) - { - mBaseDesc.entryPoint(ShaderType::AnyHit, anyHit); - } - if (!intersection.empty()) - { - mBaseDesc.entryPoint(ShaderType::Intersection, intersection); - } - - return {mBaseDesc.mActiveGroup}; -} - -ref RtProgram::create(ref pDevice, Desc desc, const DefineList& programDefines) -{ - return ref(new RtProgram(pDevice, desc, programDefines)); -} - -RtProgram::RtProgram(ref pDevice, const RtProgram::Desc& desc, const DefineList& programDefines) - : Program(pDevice, desc.mBaseDesc, programDefines), mRtDesc(desc) -{ - if (desc.mRayGenCount == 0) - { - throw ArgumentError("Can't create an RtProgram without a ray generation shader"); - } - if (desc.mMaxTraceRecursionDepth == -1) - { - throw ArgumentError("Can't create an RtProgram without specifying maximum trace recursion depth"); - } - if (desc.mMaxPayloadSize == -1) - { - throw ArgumentError("Can't create an RtProgram without specifying maximum ray payload size"); - } -} - -ref RtProgram::getRtso(RtProgramVars* pVars) -{ - auto pProgramVersion = getActiveVersion(); - auto pProgramKernels = pProgramVersion->getKernels(mpDevice, pVars); - - mRtsoGraph.walk((void*)pProgramKernels.get()); - - ref pRtso = mRtsoGraph.getCurrentNode(); - - if (pRtso == nullptr) - { - RtStateObject::Desc desc; - desc.setKernels(pProgramKernels); - desc.setMaxTraceRecursionDepth(mRtDesc.mMaxTraceRecursionDepth); - desc.setPipelineFlags(mRtDesc.mPipelineFlags); - - StateGraph::CompareFunc cmpFunc = [&desc](ref pRtso) -> bool { return pRtso && (desc == pRtso->getDesc()); }; - - if (mRtsoGraph.scanForMatchingNode(cmpFunc)) - { - pRtso = mRtsoGraph.getCurrentNode(); - } - else - { - pRtso = RtStateObject::create(mpDevice, desc); - mRtsoGraph.setCurrentNodeData(pRtso); - } - } - - return pRtso; -} -} // namespace Falcor diff --git a/Source/Falcor/Core/Program/RtProgram.h b/Source/Falcor/Core/Program/RtProgram.h deleted file mode 100644 index 526e6389a..000000000 --- a/Source/Falcor/Core/Program/RtProgram.h +++ /dev/null @@ -1,359 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#pragma once -#include "Program.h" -#include "Core/Macros.h" -#include "Core/State/StateGraph.h" -#include "Core/API/Raytracing.h" -#include "Core/API/RtStateObject.h" -#include -#include -#include -#include - -namespace Falcor -{ -class RtProgramVars; - -/** - * Ray tracing program. See GraphicsProgram and ComputeProgram to manage other types of programs. - * - * A ray tracing program consists of one or more raygen shaders, and optionally miss shaders and - * hit groups. Each hit group can consist of a closest hit, any hit, and/or intersection shaders. - * - * The ray tracing program just defines what shaders exist and is not tied to a particular scene. - * A separate RtBindingTable object describes which raygen and miss shaders to run, as well as - * the mapping from scene geometries to hit groups. Not all miss shaders and hit groups need to be - * assigned. Empty entries will be ignored and not trigger a shader execution upon miss/hit. - * - * The user is responsible for creating the program and one or more associated binding tables. - * Note that the same program can be reused with different binding tables. - * For example, the ray tracing program can define the set of all possible entry points, - * and then depending on scene contents, the binding table picks out the ones it needs. - */ -class FALCOR_API RtProgram : public Program -{ -public: - struct ShaderID - { - int32_t groupIndex = -1; ///< Entry point group index. - bool isValid() const { return groupIndex >= 0; } - }; - - /** - * Description of a raytracing program to be created. - */ - class FALCOR_API Desc - { - public: - /** - * Begin building a description, that initially has no source files or entry points. - */ - Desc() { init(); } - - /** - * Begin building a description, based on a single path for source code. - * This is equivalent to: `Desc().addShaderLibrary(path)` - * @param[in] path Path to the source code. - */ - explicit Desc(const std::string& path) : mBaseDesc(path) { init(); } - - /** - * Add a language specific (e.g. HLSL, GLSL) prelude to the shader. - * @param[in] prelude Source string. - */ - Desc& setLanguagePrelude(const std::string_view prelude) - { - mBaseDesc.setLanguagePrelude(prelude); - return *this; - }; - - /** - * Add a file of source code to use. - * This also sets the given file as the "active" source for subsequent entry points. - * @param[in] path Path to the source code. - */ - Desc& addShaderLibrary(const std::string& path) - { - mBaseDesc.addShaderLibrary(path); - return *this; - } - - /** - * Add a string of source code to use. - * This also sets the given string as the "active" source for subsequent entry points. - * Note that the source string has to be added *before* any source that imports it. - * @param[in] shader Source code. - * @param[in] moduleName Slang module name. If not creating a new translation unit, this can be left empty. - * @param[in] modulePath Virtual file path to module created from string. This is just used for diagnostics purposes and can be left - * empty. - * @param[in] createTranslationUnit Whether a new Slang translation unit should be created, otherwise the source is added to the - * previous translation unit. - */ - Desc& addShaderString( - const std::string_view shader, - const std::string_view moduleName, - const std::string_view modulePath = "", - bool createTranslationUnit = true - ) - { - mBaseDesc.addShaderString(shader, moduleName, modulePath, createTranslationUnit); - return *this; - } - - /** - * Add a shader module. - * This also sets the given module as "active" for subsequent entry points. - * Note that the module has to be added *before* any module that imports it. - */ - Desc& addShaderModule(const ShaderModule& module) - { - mBaseDesc.addShaderModule(module); - return *this; - } - - /** - * Add a list of shader modules. - * Note that the modules have to be added *before* any module that imports them. - */ - Desc& addShaderModules(const ShaderModuleList& modules) - { - mBaseDesc.addShaderModules(modules); - return *this; - } - - /** - * Add a raygen shader. - * @param[in] raygen Entry point for the raygen shader. - * @param[in] typeConformances Optional list of type conformances for the raygen shader. - * @param[in] entryPointNameSuffix Optional suffix added to the entry point names in the generated code. - * @return Shader ID for raygen shader. This is used when building the binding table. - */ - ShaderID addRayGen( - const std::string& raygen, - const TypeConformanceList& typeConformances = TypeConformanceList(), - const std::string& entryPointNameSuffix = "" - ); - - /** - * Add a miss shader. - * @param[in] miss Entry point for the miss shader. - * @param[in] typeConformances Optional list of type conformances for the miss shader. - * @param[in] entryPointNameSuffix Optional suffix added to the entry point names in the generated code. - * @return Shader ID for miss shader. This is used when building the binding table. - */ - ShaderID addMiss( - const std::string& miss, - const TypeConformanceList& typeConformances = TypeConformanceList(), - const std::string& entryPointNameSuffix = "" - ); - - /** - * Add a hit group. - * A hit group consists of any combination of closest hit, any hit, and intersection shaders. - * Note that a hit group that contains an intersection shader only be used with procedural geometry. - * A hit group that does not contain an intersection shader can only be used with triangle geometry. - * It is valid to create a hit group entirely without entry points. Geometry using it will act - * as an occluder blocking miss shader exuection, but hits will not spawn any shader executions. - * @param[in] closestHit Entry point for the closest hit shader. - * @param[in] anyHit Entry point for the any hit shader. - * @param[in] intersection Entry point for the intersection shader. - * @param[in] typeConformances Optional list of type conformances for the hit group. - * @param[in] entryPointNameSuffix Optional suffix added to the entry point names in the generated code. - * @return Shader ID for hit group. This is used when building the binding table. - */ - ShaderID addHitGroup( - const std::string& closestHit, - const std::string& anyHit = "", - const std::string& intersection = "", - const TypeConformanceList& typeConformances = TypeConformanceList(), - const std::string& entryPointNameSuffix = "" - ); - - /** - * Get the max recursion depth. - */ - uint32_t getMaxTraceRecursionDepth() const { return mMaxTraceRecursionDepth; } - - /** - * Set the max recursion depth. - * @param[in] maxDepth The maximum ray recursion depth (0 = raygen). - */ - Desc& setMaxTraceRecursionDepth(uint32_t maxDepth) - { - mMaxTraceRecursionDepth = maxDepth; - return *this; - } - - /** - * Get the max payload size. - */ - uint32_t getMaxPayloadSize() const { return mMaxPayloadSize; } - - /** - * Set the max payload size. - * @param[in] maxPayloadSize The maximum ray payload size in bytes. - */ - Desc& setMaxPayloadSize(uint32_t maxPayloadSize) - { - mMaxPayloadSize = maxPayloadSize; - return *this; - } - - /** - * Get the max attribute size. - */ - uint32_t getMaxAttributeSize() const { return mMaxAttributeSize; } - - /** - * Set the max attribute size. - * @param[in] maxAttributeSize The maximum attribute size in bytes. - */ - Desc& setMaxAttributeSize(uint32_t maxAttributeSize) - { - mMaxAttributeSize = maxAttributeSize; - return *this; - } - - /** - * Get raytracing pipeline flags. - */ - RtPipelineFlags getPipelineFlags() const { return mPipelineFlags; } - - /** - * Set raytracing pipeline flags. - * These flags are added to any TraceRay() call within this pipeline, and may be used to - * optimize the pipeline for particular primitives types. Requires Tier 1.1 support. - * @param[in] flags Pipeline flags. - */ - Desc& setPipelineFlags(RtPipelineFlags flags) - { - mPipelineFlags = flags; - return *this; - } - - /** - * Add a list of type conformances. - * The type conformances are linked into all shaders in the program. - * @param[in] typeConformances List of type conformances. - */ - Desc& addTypeConformances(const TypeConformanceList& typeConformances) - { - mBaseDesc.addTypeConformances(typeConformances); - return *this; - } - - /** - * Set the shader model. The default is SM 6.5 for DXR Tier 1.1 support. - */ - Desc& setShaderModel(const std::string& sm) - { - mBaseDesc.setShaderModel(sm); - return *this; - }; - - /** - * Get the compiler flags. - */ - CompilerFlags getCompilerFlags() const { return mBaseDesc.getCompilerFlags(); } - - /** - * Set the compiler flags. Replaces any previously set flags. - */ - Desc& setCompilerFlags(CompilerFlags flags) - { - mBaseDesc.setCompilerFlags(flags); - return *this; - } - - /** - * Get additional compiler arguments. - */ - const ArgumentList& getCompilerArguments() const { return mBaseDesc.getCompilerArguments(); } - - /** - * Set additional compiler arguments. Replaces any previously set arguments. - */ - Desc& setCompilerArguments(const ArgumentList& arguments) - { - mBaseDesc.setCompilerArguments(arguments); - return *this; - } - - /** - * Add additional compiler arguments. Appended to previously set arguments. - */ - Desc& addCompilerArguments(const ArgumentList& arguments) - { - mBaseDesc.addCompilerArguments(arguments); - return *this; - } - - private: - friend class RtProgram; - - void init(); - - Program::Desc mBaseDesc; - uint32_t mRayGenCount = 0; - - // These parameters impact performance and must be explicitly set. - uint32_t mMaxTraceRecursionDepth = -1; - uint32_t mMaxPayloadSize = -1; - uint32_t mMaxAttributeSize = getRaytracingMaxAttributeSize(); - RtPipelineFlags mPipelineFlags = RtPipelineFlags::None; - }; - - /** - * Create a new ray tracing program. - * @param[in] pDevice GPU device. - * @param[in] desc The program description. - * @param[in] programDefines Optional list of macro definitions to set into the program. - * @return A new object, or an exception is thrown if creation failed. - */ - static ref create(ref pDevice, Desc desc, const DefineList& programDefines = DefineList()); - - /** - * Get the raytracing state object for this program. - */ - ref getRtso(RtProgramVars* pVars); - - const Desc& getRtDesc() const { return mRtDesc; } - -private: - RtProgram(const RtProgram&) = delete; - RtProgram& operator=(const RtProgram&) = delete; - - RtProgram(ref pDevice, const Desc& desc, const DefineList& programDefines); - - Desc mRtDesc; - - using StateGraph = Falcor::StateGraph, void*>; - StateGraph mRtsoGraph; -}; -} // namespace Falcor diff --git a/Source/Falcor/Core/Program/ShaderVar.cpp b/Source/Falcor/Core/Program/ShaderVar.cpp index 8933c0170..020cc05e0 100644 --- a/Source/Falcor/Core/Program/ShaderVar.cpp +++ b/Source/Falcor/Core/Program/ShaderVar.cpp @@ -27,23 +27,36 @@ **************************************************************************/ #include "ShaderVar.h" #include "Core/API/ParameterBlock.h" +#include "Utils/Scripting/ScriptBindings.h" namespace Falcor { ShaderVar::ShaderVar() : mpBlock(nullptr) {} ShaderVar::ShaderVar(const ShaderVar& other) : mpBlock(other.mpBlock), mOffset(other.mOffset) {} ShaderVar::ShaderVar(ParameterBlock* pObject, const TypedShaderVarOffset& offset) : mpBlock(pObject), mOffset(offset) {} -ShaderVar::ShaderVar(ParameterBlock* pObject) : mpBlock(pObject), mOffset(pObject->getElementType(), ShaderVarOffset::kZero) {} +ShaderVar::ShaderVar(ParameterBlock* pObject) : mpBlock(pObject), mOffset(pObject->getElementType().get(), ShaderVarOffset::kZero) {} -ShaderVar ShaderVar::findMember(const std::string& name) const +// +// Navigation +// + +ShaderVar ShaderVar::operator[](std::string_view name) const { - if (!isValid()) - return *this; - auto pType = getType(); + FALCOR_CHECK(isValid(), "Cannot lookup on invalid ShaderVar."); + auto result = findMember(name); + FALCOR_CHECK(result.isValid(), "No member named '{}' found.", name); + return result; +} + +ShaderVar ShaderVar::operator[](size_t index) const +{ + FALCOR_CHECK(isValid(), "Cannot lookup on invalid ShaderVar."); + + const ReflectionType* pType = getType(); // If the user is applying `[]` to a `ShaderVar` // that represents a constant buffer (or parameter block) - // then we assume they mean to look up a member + // then we assume they mean to look up an element // inside the buffer/block, and thus implicitly // dereference this `ShaderVar`. // @@ -52,30 +65,46 @@ ShaderVar ShaderVar::findMember(const std::string& name) const switch (pResourceType->getType()) { case ReflectionResourceType::Type::ConstantBuffer: - return getParameterBlock()->getRootVar().findMember(name); + return getParameterBlock()->getRootVar()[index]; default: break; } } - if (auto pStructType = pType->asStructType()) + if (auto pArrayType = pType->asArrayType()) { - if (auto pMember = pStructType->findMember(name)) + auto elementCount = pArrayType->getElementCount(); + if (!elementCount || index < elementCount) { + UniformShaderVarOffset elementUniformLocation = mOffset.getUniform() + index * pArrayType->getElementByteStride(); + ResourceShaderVarOffset elementResourceLocation( + mOffset.getResource().getRangeIndex(), + mOffset.getResource().getArrayIndex() * elementCount + ResourceShaderVarOffset::ArrayIndex(index) + ); + TypedShaderVarOffset newOffset = + TypedShaderVarOffset(pArrayType->getElementType(), ShaderVarOffset(elementUniformLocation, elementResourceLocation)); + return ShaderVar(mpBlock, newOffset); + } + } + else if (auto pStructType = pType->asStructType()) + { + if (index < pStructType->getMemberCount()) + { + auto pMember = pStructType->getMember(index); // Need to apply the offsets from member TypedShaderVarOffset newOffset = TypedShaderVarOffset(pMember->getType(), mOffset + pMember->getBindLocation()); return ShaderVar(mpBlock, newOffset); } } - return ShaderVar(); + FALCOR_THROW("No element or member found at index {}", index); } -ShaderVar ShaderVar::findMember(uint32_t index) const +ShaderVar ShaderVar::findMember(std::string_view name) const { if (!isValid()) return *this; - auto pType = getType(); + const ReflectionType* pType = getType(); // If the user is applying `[]` to a `ShaderVar` // that represents a constant buffer (or parameter block) @@ -88,7 +117,7 @@ ShaderVar ShaderVar::findMember(uint32_t index) const switch (pResourceType->getType()) { case ReflectionResourceType::Type::ConstantBuffer: - return getParameterBlock()->getRootVar().findMember(index); + return getParameterBlock()->getRootVar().findMember(name); default: break; } @@ -96,10 +125,8 @@ ShaderVar ShaderVar::findMember(uint32_t index) const if (auto pStructType = pType->asStructType()) { - if (index < pStructType->getMemberCount()) + if (auto pMember = pStructType->findMember(name)) { - auto pMember = pStructType->getMember(index); - // Need to apply the offsets from member TypedShaderVarOffset newOffset = TypedShaderVarOffset(pMember->getType(), mOffset + pMember->getBindLocation()); return ShaderVar(mpBlock, newOffset); @@ -109,53 +136,15 @@ ShaderVar ShaderVar::findMember(uint32_t index) const return ShaderVar(); } -ShaderVar ShaderVar::operator[](const std::string& name) const -{ - auto result = findMember(name); - if (!result.isValid() && isValid()) - { - throw ArgumentError("No member named '{}' found.", name); - } - return result; -} - -ShaderVar ShaderVar::operator[](std::string_view name) const -{ - return (*this)[std::string(name)]; -} - -ShaderVar ShaderVar::operator[](const char* name) const -{ - // #SHADER_VAR we can use std::string_view to do lookups into the map - // TODO: We should have the ability to do this lookup - // without ever having to construct a `std::string`. - // - // The sticking point is the `ReflectionStructType::findMember` - // operation, which currently needs to do lookup in a `std::map`, - // so that we can't use a `const char*` as the key for lookup - // without incurring the cost of a constructing a `std::string`. - // - // To get around this limitation we'd need to implement a more clever/complicated - // map that uses a raw `const char*` as the key, and then compares keys with - // `strcmp`, while ensuring that the keys actually stored in the map are allocated - // and retained somewhere (which they should be because they are the names of - // the members of the `ReflectionStructType`). - // - // For now we just punt and go with the slow option even - // if the user is using a static string. - // - return (*this)[std::string(name)]; -} - -ShaderVar ShaderVar::operator[](size_t index) const +ShaderVar ShaderVar::findMember(uint32_t index) const { if (!isValid()) return *this; - auto pType = getType(); + const ReflectionType* pType = getType(); // If the user is applying `[]` to a `ShaderVar` // that represents a constant buffer (or parameter block) - // then we assume they mean to look up an element + // then we assume they mean to look up a member // inside the buffer/block, and thus implicitly // dereference this `ShaderVar`. // @@ -164,46 +153,134 @@ ShaderVar ShaderVar::operator[](size_t index) const switch (pResourceType->getType()) { case ReflectionResourceType::Type::ConstantBuffer: - return getParameterBlock()->getRootVar()[index]; + return getParameterBlock()->getRootVar().findMember(index); default: break; } } - if (auto pArrayType = pType->asArrayType()) - { - auto elementCount = pArrayType->getElementCount(); - if (!elementCount || index < elementCount) - { - UniformShaderVarOffset elementUniformLocation = mOffset.getUniform() + index * pArrayType->getElementByteStride(); - ResourceShaderVarOffset elementResourceLocation( - mOffset.getResource().getRangeIndex(), - mOffset.getResource().getArrayIndex() * elementCount + ResourceShaderVarOffset::ArrayIndex(index) - ); - TypedShaderVarOffset newOffset = - TypedShaderVarOffset(pArrayType->getElementType(), ShaderVarOffset(elementUniformLocation, elementResourceLocation)); - return ShaderVar(mpBlock, newOffset); - } - } - else if (auto pStructType = pType->asStructType()) + if (auto pStructType = pType->asStructType()) { if (index < pStructType->getMemberCount()) { auto pMember = pStructType->getMember(index); + // Need to apply the offsets from member TypedShaderVarOffset newOffset = TypedShaderVarOffset(pMember->getType(), mOffset + pMember->getBindLocation()); return ShaderVar(mpBlock, newOffset); } } - throw ArgumentError("No element or member found at index {}", index); + return ShaderVar(); +} + +// +// Variable assignment +// + +void ShaderVar::setBlob(void const* data, size_t size) const +{ + // If the var is pointing at a constant buffer, then assume + // the user actually means to write the blob *into* that buffer. + // + const ReflectionType* pType = getType(); + if (auto pResourceType = pType->asResourceType()) + { + switch (pResourceType->getType()) + { + case ReflectionResourceType::Type::ConstantBuffer: + return getParameterBlock()->getRootVar().setBlob(data, size); + default: + break; + } + } + + mpBlock->setBlob(mOffset, data, size); +} + +// +// Resource binding +// + +void ShaderVar::setBuffer(const ref& pBuffer) const +{ + mpBlock->setBuffer(mOffset, pBuffer); +} + +ref ShaderVar::getBuffer() const +{ + return mpBlock->getBuffer(mOffset); +} + +void ShaderVar::setTexture(const ref& pTexture) const +{ + mpBlock->setTexture(mOffset, pTexture); +} + +ref ShaderVar::getTexture() const +{ + return mpBlock->getTexture(mOffset); +} + +void ShaderVar::setSrv(const ref& pSrv) const +{ + mpBlock->setSrv(mOffset, pSrv); +} + +ref ShaderVar::getSrv() const +{ + return mpBlock->getSrv(mOffset); +} + +void ShaderVar::setUav(const ref& pUav) const +{ + mpBlock->setUav(mOffset, pUav); } +ref ShaderVar::getUav() const +{ + return mpBlock->getUav(mOffset); +} + +void ShaderVar::setAccelerationStructure(const ref& pAccl) const +{ + mpBlock->setAccelerationStructure(mOffset, pAccl); +} + +ref ShaderVar::getAccelerationStructure() const +{ + return mpBlock->getAccelerationStructure(mOffset); +} + +void ShaderVar::setSampler(const ref& pSampler) const +{ + mpBlock->setSampler(mOffset, pSampler); +} + +ref ShaderVar::getSampler() const +{ + return mpBlock->getSampler(mOffset); +} + +void ShaderVar::setParameterBlock(const ref& pBlock) const +{ + mpBlock->setParameterBlock(mOffset, pBlock); +} + +ref ShaderVar::getParameterBlock() const +{ + return mpBlock->getParameterBlock(mOffset); +} + +// +// Offset access +// + ShaderVar ShaderVar::operator[](const TypedShaderVarOffset& offset) const { if (!isValid()) return *this; - auto pType = getType(); + const ReflectionType* pType = getType(); // If the user is applying `[]` to a `ShaderVar` // that represents a constant buffer (or parameter block) @@ -228,7 +305,7 @@ ShaderVar ShaderVar::operator[](const UniformShaderVarOffset& loc) const { if (!isValid()) return *this; - auto pType = getType(); + const ReflectionType* pType = getType(); // If the user is applying `[]` to a `ShaderVar` // that represents a constant buffer (or parameter block) @@ -291,157 +368,116 @@ ShaderVar ShaderVar::operator[](const UniformShaderVarOffset& loc) const } } - throw ArgumentError("No element or member found at offset {}", byteOffset); + FALCOR_THROW("No element or member found at offset {}", byteOffset); } -bool ShaderVar::isValid() const +void const* ShaderVar::getRawData() const { - return mOffset.isValid(); + return (uint8_t*)(mpBlock->getRawData()) + mOffset.getUniform().getByteOffset(); } -bool ShaderVar::setTexture(const ref& pTexture) const +void ShaderVar::setImpl(const ref& pTexture) const { - return mpBlock->setTexture(mOffset, pTexture); + mpBlock->setTexture(mOffset, pTexture); } -bool ShaderVar::setSampler(const ref& pSampler) const +void ShaderVar::setImpl(const ref& pSampler) const { - return mpBlock->setSampler(mOffset, pSampler); + mpBlock->setSampler(mOffset, pSampler); } -bool ShaderVar::setBuffer(const ref& pBuffer) const +void ShaderVar::setImpl(const ref& pBuffer) const { - return mpBlock->setBuffer(mOffset, pBuffer); + mpBlock->setBuffer(mOffset, pBuffer); } -bool ShaderVar::setSrv(const ref& pSrv) const +void ShaderVar::setImpl(const ref& pBlock) const { - return mpBlock->setSrv(mOffset, pSrv); + mpBlock->setParameterBlock(mOffset, pBlock); } -bool ShaderVar::setUav(const ref& pUav) const +FALCOR_SCRIPT_BINDING(ShaderVar) { - return mpBlock->setUav(mOffset, pUav); -} + FALCOR_SCRIPT_BINDING_DEPENDENCY(Buffer) + FALCOR_SCRIPT_BINDING_DEPENDENCY(Texture) -bool ShaderVar::setAccelerationStructure(const ref& pAccl) const -{ - return mpBlock->setAccelerationStructure(mOffset, pAccl); -} + pybind11::class_ shaderVar(m, "ShaderVar"); -bool ShaderVar::setParameterBlock(const ref& pBlock) const -{ - return mpBlock->setParameterBlock(mOffset, pBlock); -} + shaderVar.def("__getitem__", [](ShaderVar& self, std::string_view name) { return self[name]; }); + shaderVar.def("__getattr__", [](ShaderVar& self, std::string_view name) { return self[name]; }); -bool ShaderVar::setImpl(const ref& pTexture) const -{ - return mpBlock->setTexture(mOffset, pTexture); -} +#define def_setter(type) \ + shaderVar.def("__setitem__", [](ShaderVar& self, std::string_view name, type value) { self[name] = value; }); \ + shaderVar.def("__setattr__", [](ShaderVar& self, std::string_view name, type value) { self[name] = value; }); -bool ShaderVar::setImpl(const ref& pSampler) const -{ - return mpBlock->setSampler(mOffset, pSampler); -} + def_setter(ref); + def_setter(ref); + def_setter(ref); -bool ShaderVar::setImpl(const ref& pBuffer) const -{ - return mpBlock->setBuffer(mOffset, pBuffer); -} + def_setter(uint2); + def_setter(uint3); + def_setter(uint4); -bool ShaderVar::setImpl(const ref& pBlock) const -{ - return mpBlock->setParameterBlock(mOffset, pBlock); -} + def_setter(int2); + def_setter(int3); + def_setter(int4); -bool ShaderVar::setBlob(void const* data, size_t size) const -{ - // If the var is pointing at a constant buffer, then assume - // the user actually means to write the blob *into* that buffer. - // - auto pType = getType(); - if (auto pResourceType = pType->asResourceType()) - { - switch (pResourceType->getType()) - { - case ReflectionResourceType::Type::ConstantBuffer: - return getParameterBlock()->getRootVar().setBlob(data, size); - default: - break; - } - } + def_setter(bool); + def_setter(bool2); + def_setter(bool3); + def_setter(bool4); - return mpBlock->setBlob(mOffset, data, size); -} + def_setter(float2); + def_setter(float3); + def_setter(float4); -ShaderVar::operator ref() const -{ - return mpBlock->getBuffer(mOffset); -} - -ShaderVar::operator ref() const -{ - return mpBlock->getTexture(mOffset); -} - -ShaderVar::operator ref() const -{ - return mpBlock->getSampler(mOffset); -} - -ShaderVar::operator UniformShaderVarOffset() const -{ - return mOffset.getUniform(); -} - -ref ShaderVar::getParameterBlock() const -{ - return mpBlock->getParameterBlock(mOffset); -} - -ref ShaderVar::getBuffer() const -{ - return mpBlock->getBuffer(mOffset); -} - -ref ShaderVar::getTexture() const -{ - return mpBlock->getTexture(mOffset); -} - -ref ShaderVar::getSampler() const -{ - return mpBlock->getSampler(mOffset); -} + def_setter(float3x4); + def_setter(float4x4); -ref ShaderVar::getSrv() const -{ - return mpBlock->getSrv(mOffset); -} +#undef def_setter -ref ShaderVar::getUav() const -{ - return mpBlock->getUav(mOffset); -} + // We need to handle integers and floats specially. + // Python only has an `int` and `float` type that can have different bit-width. + // We use reflection data to convert the python types to the correct types before assigning. -ref ShaderVar::getAccelerationStructure() const -{ - return mpBlock->getAccelerationStructure(mOffset); -} + auto set_int = [](ShaderVar& self, std::string_view name, pybind11::int_ value) + { + const ReflectionBasicType* basicType = self[name].getType()->unwrapArray()->asBasicType(); + FALCOR_CHECK(basicType, "Error trying to set a variable that is not a basic type."); + switch (basicType->getType()) + { + case ReflectionBasicType::Type::Int: + self[name] = value.cast(); + break; + case ReflectionBasicType::Type::Uint: + self[name] = value.cast(); + break; + default: + FALCOR_THROW("Error trying to set a variable that is not an integer type."); + break; + } + }; -size_t ShaderVar::getByteOffset() const -{ - return mOffset.getUniform().getByteOffset(); -} + shaderVar.def("__setitem__", set_int); + shaderVar.def("__setattr__", set_int); -ref ShaderVar::getType() const -{ - return mOffset.getType(); -} + auto set_float = [](ShaderVar& self, std::string_view name, pybind11::float_ value) + { + const ReflectionBasicType* basicType = self[name].getType()->unwrapArray()->asBasicType(); + FALCOR_CHECK(basicType, "Error trying to set a variable that is not a basic type."); + switch (basicType->getType()) + { + case ReflectionBasicType::Type::Float: + self[name] = value.cast(); + break; + default: + FALCOR_THROW("Error trying to set a variable that is not an float type."); + break; + } + }; -void const* ShaderVar::getRawData() const -{ - return (uint8_t*)(mpBlock->getRawData()) + mOffset.getUniform().getByteOffset(); + shaderVar.def("__setitem__", set_float); + shaderVar.def("__setattr__", set_float); } } // namespace Falcor diff --git a/Source/Falcor/Core/Program/ShaderVar.h b/Source/Falcor/Core/Program/ShaderVar.h index 850fd3ffa..305111b72 100644 --- a/Source/Falcor/Core/Program/ShaderVar.h +++ b/Source/Falcor/Core/Program/ShaderVar.h @@ -35,7 +35,7 @@ #include "Core/API/RtAccelerationStructure.h" #include "Utils/Math/Vector.h" #include -#include +#include #include namespace Falcor @@ -81,11 +81,6 @@ struct FALCOR_API ShaderVar */ ShaderVar(); - /** - * Create a null/invalid shader variable pointer. - */ - ShaderVar(std::nullptr_t) : ShaderVar(){}; - /** * Copy constructor. */ @@ -94,7 +89,7 @@ struct FALCOR_API ShaderVar /** * Create a shader variable pointer into `pObject` at the given `offset`. */ - ShaderVar(ParameterBlock* pObject, const TypedShaderVarOffset& offset); + explicit ShaderVar(ParameterBlock* pObject, const TypedShaderVarOffset& offset); /** * Create a shader variable pointer to the content of `pObject`. @@ -104,15 +99,14 @@ struct FALCOR_API ShaderVar /** * Check if this shader variable pointer is valid/non-null. */ - bool isValid() const; + bool isValid() const { return mOffset.isValid(); } /** * Get the type data this shader variable points at. * * For an invalid/null shader variable the result will be null. - * */ - ref getType() const; + const ReflectionType* getType() const { return mOffset.getType(); } /** * Get the offset that this shader variable points to inside the parameter block. @@ -121,222 +115,260 @@ struct FALCOR_API ShaderVar /** * Get the byte offset that this shader variable points to inside the parameter block. - * Note: If the type of the value being pointed at includes anything other than ordinary/uniform data, then this byte offset will not - * provide complete enough information to re-create the same `ShaderVar` later. - */ - size_t getByteOffset() const; - - /** - * Get a shader variable pointer to a sub-field. * - * This shader variable must point to a value of `struct` type, with a field matching the given `name`. - * If this shader variable points at a constant buffer or parameter block, then the lookup will proceed in the contents of that block. - * Otherwise an error is logged and an invalid `ShaderVar` is returned. + * Note: If the type of the value being pointed at includes anything other + * than ordinary/uniform data, then this byte offset will not provide + * complete enough information to re-create the same `ShaderVar` later. */ - ShaderVar operator[](const char* name) const; + size_t getByteOffset() const { return mOffset.getUniform().getByteOffset(); } - /** - * Get a shader variable pointer to a sub-field. - * - * This shader variable must point to a value of `struct` type, with a field matching the given `name`. - * If this shader variable points at a constant buffer or parameter block, then the lookup will proceed in the contents of that block. - * Otherwise an error is logged and an invalid `ShaderVar` is returned. - */ - ShaderVar operator[](std::string_view name) const; + // + // Navigation + // /** * Get a shader variable pointer to a sub-field. * - * This shader variable must point to a value of `struct` type, with a field matching the given `name`. - * If this shader variable points at a constant buffer or parameter block, then the lookup will proceed in the contents of that block. - * Otherwise an error is logged and an invalid `ShaderVar` is returned. + * This shader variable must point to a value of `struct` type, + * with a field matching the given `name`. + * + * If this shader variable points at a constant buffer or parameter block, + * then the lookup will proceed in the contents of that block. + * + * If the above doesn't hold, an exception is thrown. */ - ShaderVar operator[](const std::string& name) const; + ShaderVar operator[](std::string_view name) const; /** * Get a shader variable pointer to an element or sub-field. * * This operation is valid in two cases: - * 1) This shader variable points at a value of array type, and the `index` is in range for the array. The result - * is a shader variable that points to a single array element. - * 2) This shader variable points at a value of `struct` type, and `index` is in range for the number of fields in - * the `struct`. The result is a shader variable that points to a single `struct` field. - * If this shader variable points at a constant buffer or parameter block, then the lookup will proceed in the contents of that block. + * 1) This shader variable points at a value of array type, and the `index` + * is in range for the array. + * The result is a shader variable that points to a single array element. + * 2) This shader variable points at a value of `struct` type, and `index` + * is in range for the number of fields in the `struct`. + * The result is a shader variable that points to a single `struct` field. + * If this shader variable points at a constant buffer or parameter block, + * then the lookup will proceed in the contents of that block. * - * Otherwise an error is logged and an invalid `ShaderVar` is returned. + * If the above doesn't hold, an exception is thrown. */ ShaderVar operator[](size_t index) const; /** * Try to get a variable for a member/field. * - * Unlike `operator[]`, a `findMember` operation does not - * log an error if a member of the given name cannot be found. + * Unlike `operator[]`, a `findMember` operation does not throw an exception + * if the variable doesn't exist. Instead, it returns an invalid `ShaderVar`. */ - ShaderVar findMember(const std::string& name) const; + ShaderVar findMember(std::string_view name) const; + + /** + * Returns true if a member/field exists. + */ + bool hasMember(std::string_view name) const { return findMember(name).isValid(); } /** * Try to get a variable for a member/field, by index. * - * Unlike `operator[]`, a `findMember` operation does not - * log an error if a member cannot be found at the given index. + * Unlike `operator[]`, a `findMember` operation does not throw an exception + * if the varible doesn't exist. Instead, it returns an invalid `ShaderVar`. */ ShaderVar findMember(uint32_t index) const; + /** + * Returns true if a member/field exists, by index. + */ + bool hasMember(uint32_t index) const { return findMember(index).isValid(); } + + // + // Variable assignment + // + + /** + * Set the value of the data pointed to by this shader variable. + * Throws an exception if the given `val` does not have a suitable type for + * the value pointed to by this shader variable. + */ + template + void set(const T& val) const + { + setImpl(val); + } + /** * Set the value of the data pointed to by this shader variable. - * Returns `true` if successful. Logs and error and returns `false` if the given `val` does not have a suitable type for the value - * pointed to by this shader variable. + * + * This operator allows assignment syntax to be used in place of + * the `set()` method. The following two statements are equivalent: + * myShaderVar["someField"].set(float4(0)); + * myShaderVar["someField"] = float4(0); + * + * Throws an exception if the given `val` does not have a suitable type for + * the value pointed to by this shader variable. */ template - bool set(const T& val) const + void operator=(const T& val) const { - return setImpl(val); + setImpl(val); } + // + // Uniforms + // + /** - * Assign raw binary data to the pointed-to value. + * Assign raw binary data to the variable. + * + * This operation will only assign to the ordinary/"uniform" data pointed + * to by this shader variable, and will not affect any nested variables + * of texture/buffer/sampler types. * - * This operation will only assign to the ordinary/"uniform" data pointed to by this shader variable, and will not affect any - * nested variables of texture/buffer/sampler types. + * Throws an exception if this variable doesn't point at a parameter block + * or constant buffer. */ - bool setBlob(void const* data, size_t size) const; + void setBlob(void const* data, size_t size) const; /** - * Assign raw binary data to the pointed-to value. + * Assign raw binary data to the variable. * This is a convenience form for `setBlob(&val, sizeof(val)`. */ template - bool setBlob(const T& val) const + void setBlob(const T& val) const { - return setBlob(&val, sizeof(val)); + setBlob(&val, sizeof(val)); } + // + // Resource binding + // + /** - * Set a buffer into this variable - * Logs an error and returns `false` if this variable doesn't point at a buffer + * Bind a buffer to this variable. + * Throws an exception if this variable doesn't point at a buffer or the + * buffer has incompatible bind flags. */ - bool setBuffer(const ref& pBuffer) const; + void setBuffer(const ref& pBuffer) const; /** - * Set the texture that this variable points to. - * Logs an error and returns `false` if this variable doesn't point at a texture. + * Get the buffer bound to this variable. + * Throws an exception if this variable doesn't point at a buffer. */ - bool setTexture(const ref& pTexture) const; + ref getBuffer() const; /** - * Get the texture that this variable points to. - * Logs an error and returns null if this variable doesn't point at a texture. + * Implicit conversion from a shader variable to a buffer. + * This operation allows a bound buffer to be queried using the `[]` syntax: + * pBuffer = pVars["someBuffer"]; */ - ref getTexture() const; + operator ref() const { return getBuffer(); } /** - * Set the sampler that this variable points to. - * Logs an error and returns `false` if this variable doesn't point at a sampler. + * Bind a texture to this variable. + * Throws an exception if this variable doesn't point at a texture or the + * texture has incompatible bind flags. */ - bool setSampler(const ref& pSampler) const; + void setTexture(const ref& pTexture) const; /** - * Get the sampler that this variable points to. - * Logs an error and returns null if this variable doesn't point at a sampler. + * Get the texture bound to this variable. + * Throws an exception if this variable doesn't point at a texture. */ - ref getSampler() const; + ref getTexture() const; /** - * Get the buffer that this variable points to. - * Logs an error and returns nullptr if this variable doesn't point at a buffer. + * Implicit conversion from a shader variable to a texture. + * This operation allows a bound texture to be queried using the `[]` syntax: + * pTexture = pVars["someTexture"]; */ - ref getBuffer() const; + operator ref() const { return getTexture(); } /** - * Set the shader resource view that this variable points to. - * Logs an error and returns `false` if this variable doesn't point at a shader resource view. + * Bind an SRV to this variable. + * Throws an exception if this variable doesn't point at an SRV. */ - bool setSrv(const ref& pSrv) const; + void setSrv(const ref& pSrv) const; /** - * Get the shader resource view that this variable points to. - * Logs an error and returns null if this variable doesn't point at a shader resource view. + * Get the SRV bound to this variable. + * Throws an exception if this variable doesn't point at an SRV. */ ref getSrv() const; /** - * Set the unordered access view that this variable points to. - * Logs an error and returns `false` if this variable doesn't point at an unordered access view. + * Bind a UAV to this variable. + * Throws an exception if this variable doesn't point at a UAV. */ - bool setUav(const ref& pUav) const; + void setUav(const ref& pUav) const; /** - * Get the unordered access view that this variable points to. - * Logs an error and returns null if this variable doesn't point at an unordered access view. + * Get the UAV bound to this variable. + * Throws an exception if this variable doesn't point at a UAV. */ ref getUav() const; /** - * Set the acceleration structure that this variable points to. - * Logs an error and returns `false` if this variable doesn't point at an acceleration structure. + * Bind an acceleration structure to this variable. + * Throws an exception if this variable doesn't point at an acceleration structure. */ - bool setAccelerationStructure(const ref& pAccl) const; + void setAccelerationStructure(const ref& pAccl) const; /** - * Get the acceleration structure that this variable points to. - * Logs an error and returns null if this variable doesn't point at an acceleration structure. + * Get the acceleration structure bound to this variable. + * Throws an exception if this variable doesn't point at an acceleration structure. */ ref getAccelerationStructure() const; /** - * Set the parameter block that this variable points to. - * Logs an error and returns `false` if this variable doesn't point at a parameter block. + * Bind a sampler to this variable. + * Throws an exception if this variable doesn't point at a sampler. */ - bool setParameterBlock(const ref& pBlock) const; + void setSampler(const ref& pSampler) const; /** - * Get the parameter block that this variable points to. - * Logs an error and returns null if this variable doesn't point at a parameter block. + * Get the sampler bound to this variable. + * Throws an exception if this variable doesn't point at a sampler. */ - ref getParameterBlock() const; + ref getSampler() const; /** - * Set the value of the data pointed to by this shader variable. - * - * This operator allows assignment syntax to be used in place of - * the `set()` method. The following two statements are equivalent: - * myShaderVar["someField"].set(float4(0)); - * myShaderVar["someField"] = float4(0); + * Implicit conversion from a shader variable to a sampler. + * This operation allows a bound sampler to be queried using the `[]` syntax: + * pSampler = pVars["someSampler"]; */ - template - void operator=(const T& val) - { - setImpl(val); - } + operator ref() const; /** - * Set the value of the data pointed to by this shader variable. - * - * This operator allows assignment syntax to be used in place of the `set()` method. The following two statements are equivalent: - * - * myShaderVar["someField"].set(float4(0)); - * myShaderVar["someField"] = float4(0); - * - * Note: because this operation modified the data "pointed at" by the shader variable, rather than the shader variable itself, - * assignment is allowed on a `const ShaderVar`. + * Bind a parameter block to this variable. + * Throws an exception if this variable doesn't point at a parameter block. */ - template - void operator=(const T& val) const - { - setImpl(val); - } + void setParameterBlock(const ref& pBlock) const; + + /** + * Get the parameter block bound to this variable. + * Throws an exception if this variable doesn't point at a parameter block. + */ + ref getParameterBlock() const; + + // + // Offset access + // /** * Implicit conversion from a shader variable to its offset information. * - * This operation allows the offset information for a shader variable to be queried easily using the `[]` sugar: + * This operation allows the offset information for a shader variable + * to be queried easily using the `[]` sugar: + * * TypedShaderVarOffset myVarLoc = pVars["myVar"]; * ... * pVars[myVarLoc] = someValue * - * Note that the returned offset information only retains the offset into the leaf-most parameter block (constant buffer or parameter - * block). Users must take care when using an offset that they apply the offset to the correct object: + * Note that the returned offset information only retains the offset into + * the leaf-most parameter block (constant buffer or parameter block). + * Users must take care when using an offset that they apply the offset + * to the correct object: * * auto pPerFrameCB = pVars["PerFrameCB"]; * TypedShaderVarOffset myVarLoc = pPerFrameCB["myVar"]; @@ -348,73 +380,62 @@ struct FALCOR_API ShaderVar /** * Implicit conversion from a shader variable to its offset information. * - * This operation allows the offset information for a shader variable to be queried easily using the `[]` sugar: + * This operation allows the offset information for a shader variable + * to be queried easily using the `[]` sugar: * * UniformShaderVarOffset myVarLoc = pVars["myVar"]; * ... * pVars[myVarLoc] = someValue * - * Note that the returned offset information only retains the offset into the leaf-most parameter block (constant buffer or parameter - * block). Users must take care when using an offset that they apply the offset to the correct object: + * Note that the returned offset information only retains the offset into + * the leaf-most parameter block (constant buffer or parameter block). + * Users must take care when using an offset that they apply the offset + * to the correct object: * * auto pPerFrameCB = pVars["PerFrameCB"]; * UniformShaderVarOffset myVarLoc = pPerFrameCB["myVar"]; * ... * pVars[myVarLoc] = someValue; // CRASH! */ - operator UniformShaderVarOffset() const; + operator UniformShaderVarOffset() const { return mOffset.getUniform(); } /** - * Create a shader variable that points to some pre-computed `offset` from this one. + * Create a shader variable that points to some pre-computed `offset` + * relative to this one. + * + * This operation assumes that the provided `offset` has been appropriately + * computed based on a type that matches what this shader variable points to. * - * This operation assumes that the provided `offset` has been appropriately computed based on a type that matches what this shader - * variable points to. The resulting shader variable will have the type encoded in `offset`, and will have an offset that is the sum of - * this variables offset and the provided `offset`. + * The resulting shader variable will have the type encoded in `offset`, + * and will have an offset that is the sum of this variables offset + * and the provided `offset`. */ ShaderVar operator[](const TypedShaderVarOffset& offset) const; /** - * Create a shader variable that points to some pre-computed `offset` from this one. + * Create a shader variable that points to some pre-computed `offset` + * relative to this one. * - * This operation assumes that the provided `offset` has been appropriately computed based on a type that matches what this shader - * variable points to. + * This operation assumes that the provided `offset` has been appropriately + * computed based on a type that matches what this shader variable points to. * - * Because a `UniformShaderVarOffset` does not encode type information, this operation will search for a field/element matching the - * given `offset` and use its type information in the resulting shader variable. If no appropriate field/element can be found, an error - * will be logged. + * Because a `UniformShaderVarOffset` does not encode type information, + * this operation will search for a field/element matching the given + * `offset` and use its type information in the resulting shader variable. + * If no appropriate field/element can be found, an error will be logged. */ ShaderVar operator[](const UniformShaderVarOffset& offset) const; - /** - * Implicit conversion from a shader variable to a texture. - * This operation allows a bound texture to be queried using the `[]` syntax: - * pTexture = pVars["someTexture"]; - */ - operator ref() const; - - /** - * Implicit conversion from a shader variable to a sampler. - * This operation allows a bound sampler to be queried using the `[]` syntax: - * pSampler = pVars["someSampler"]; - */ - operator ref() const; - - /** - * Implicit conversion from a shader variable to a buffer. - * This operation allows a bound buffer to be queried using the `[]` syntax: - * pBuffer = pVars["someBuffer"]; - */ - operator ref() const; - /** * Get access to the underlying bytes of the variable. * - * This operation must be used with caution; the caller takes all responsibility for validation. + * This operation must be used with caution, + * the caller takes all responsibility for validation. * - * Note: if a caller uses the resulting pointer to write to the variable (e.g., - * by casting away the `const`-ness, then the underlying `ParameterBlock` will - * not automatically be marked dirty, and it is possible that the effects of that - * write will not be visible. + * Note: if a caller uses the resulting pointer to write to the variable + * (e.g. by casting away the `const`-ness, then the underlying + * `ParameterBlock` will not automatically be marked dirty, and it is + * possible that the effects of that write will not be visible. */ void const* getRawData() const; @@ -422,9 +443,11 @@ struct FALCOR_API ShaderVar /** * The parameter block that is being pointed into. * - * Note: this is an unowned pointer, so it is *not* safe to hold onto a `ShaderVar` for long periods - * of time where the object it points into might get released. This is a concession to performance, - * since we do not want to perform reference-counting each and every time a `ShaderVar` gets created or destroyed. + * Note: this is an unowned pointer, so it is *not* safe to hold onto + * a `ShaderVar` for long periods of time where the object it points into + * might get released. This is a concession to performance, since we do not + * want to perform reference-counting each and every time a `ShaderVar` + * gets created or destroyed. */ ParameterBlock* mpBlock; @@ -432,19 +455,16 @@ struct FALCOR_API ShaderVar * The offset into the object where this variable points. * * This field encodes both the offset information and the type of the variable. - * - * TODO(tfoley): This field technically retains a reference count on the type, which shouldn't be - * needed because the original object should keep its type (and thus its field types) alive. */ TypedShaderVarOffset mOffset; - bool setImpl(const ref& pTexture) const; - bool setImpl(const ref& pSampler) const; - bool setImpl(const ref& pBuffer) const; - bool setImpl(const ref& pBlock) const; + void setImpl(const ref& pTexture) const; + void setImpl(const ref& pSampler) const; + void setImpl(const ref& pBuffer) const; + void setImpl(const ref& pBlock) const; template - bool setImpl(const T& val) const; + void setImpl(const T& val) const; }; } // namespace Falcor diff --git a/Source/Falcor/Core/SampleApp.cpp b/Source/Falcor/Core/SampleApp.cpp index 64fb700a7..17dbb99df 100644 --- a/Source/Falcor/Core/SampleApp.cpp +++ b/Source/Falcor/Core/SampleApp.cpp @@ -28,7 +28,9 @@ #include "SampleApp.h" #include "Macros.h" #include "Version.h" +#include "Core/Error.h" #include "Core/Plugin.h" +#include "Core/AssetResolver.h" #include "Core/Program/Program.h" #include "Core/Program/ProgramManager.h" #include "Core/Platform/ProgressBar.h" @@ -87,32 +89,25 @@ SampleApp::SampleApp(const SampleAppConfig& config) if (windowDesc.mode != Window::WindowMode::Minimized) mProgressBar.show("Initializing Falcor"); - setShowMessageBoxOnError(true); + // When not running headless, we want to show message boxes on error by default. + setErrorDiagnosticFlags(getErrorDiagnosticFlags() | ErrorDiagnosticFlags::ShowMessageBoxOnError); } // Create target frame buffer uint2 fboSize = mpWindow ? mpWindow->getClientAreaSize() : uint2(config.windowDesc.width, config.windowDesc.height); mpTargetFBO = Fbo::create2D(mpDevice, fboSize.x, fboSize.y, config.colorFormat, config.depthFormat); - // Populate the data search paths from the config file, only adding those that aren't in already - auto searchDirectories = getSettings().getSearchDirectories("media"); - for (auto& it : searchDirectories.get()) - addDataDirectory(it); - - // Set global shader defines - DefineList globalDefines = { - {"FALCOR_NVAPI_AVAILABLE", (FALCOR_NVAPI_AVAILABLE && mpDevice->getType() == Device::Type::D3D12) ? "1" : "0"}, -#if FALCOR_NVAPI_AVAILABLE - {"NV_SHADER_EXTN_SLOT", "u999"}, -#endif - }; - mpDevice->getProgramManager()->addGlobalDefines(globalDefines); + // Setup asset search paths. + AssetResolver& resolver = AssetResolver::getDefaultResolver(); + resolver.addSearchPath(getProjectDirectory() / "media"); + for (auto& path : Settings::getGlobalSettings().getSearchDirectories("media")) + resolver.addSearchPath(path); mpDevice->getProgramManager()->setGenerateDebugInfoEnabled(config.generateShaderDebugInfo); if (config.shaderPreciseFloat) { mpDevice->getProgramManager()->setForcedCompilerFlags( - {Program::CompilerFlags::FloatingPointModePrecise, Program::CompilerFlags::FloatingPointModeFast} + {SlangCompilerFlags::FloatingPointModePrecise, SlangCompilerFlags::FloatingPointModeFast} ); } @@ -128,7 +123,7 @@ SampleApp::~SampleApp() mpPausedRenderOutput.reset(); mpProfilerUI.reset(); - mpDevice->flushAndSync(); + mpDevice->wait(); Threading::shutdown(); Scripting::shutdown(); @@ -164,17 +159,8 @@ Settings& SampleApp::getSettings() int SampleApp::run() { - try - { - startScripting(); - runInternal(); - } - catch (const std::exception& e) - { - reportError("Caught exception:\n\n" + std::string(e.what())); - mReturnCode = 1; - } - + startScripting(); + runInternal(); return mReturnCode; } @@ -318,7 +304,12 @@ void SampleApp::runInternal() bool screenSizeUI(Gui::Widgets& widget, uint2& screenDims) { static const uint2 resolutions[] = { - {0, 0}, {1280, 720}, {1920, 1080}, {1920, 1200}, {2560, 1440}, {3840, 2160}, + {0, 0}, + {1280, 720}, + {1920, 1080}, + {1920, 1200}, + {2560, 1440}, + {3840, 2160}, }; static const auto initDropDown = [](const uint2 resolutions[], uint32_t count) -> Gui::DropdownList @@ -501,7 +492,7 @@ void SampleApp::renderFrame() { auto srcTexture = mpTargetFBO->getColorTexture(0); mpPausedRenderOutput = - Texture::create2D(mpDevice, srcTexture->getWidth(), srcTexture->getHeight(), srcTexture->getFormat(), 1, 1); + mpDevice->createTexture2D(srcTexture->getWidth(), srcTexture->getHeight(), srcTexture->getFormat(), 1, 1); pRenderContext->copyResource(mpPausedRenderOutput.get(), srcTexture.get()); } else @@ -531,8 +522,7 @@ void SampleApp::renderFrame() const Texture* pSwapchainImage = mpSwapchain->getImage(imageIndex).get(); pRenderContext->copyResource(pSwapchainImage, mpTargetFBO->getColorTexture(0).get()); pRenderContext->resourceBarrier(pSwapchainImage, Resource::State::Present); - pRenderContext->flush(); - FALCOR_PROFILE_CUSTOM(pRenderContext, "present", Profiler::Flags::Internal); + pRenderContext->submit(); mpSwapchain->present(); } diff --git a/Source/Falcor/Core/State/ComputeState.cpp b/Source/Falcor/Core/State/ComputeState.cpp index 4f7bfba30..e19f329c6 100644 --- a/Source/Falcor/Core/State/ComputeState.cpp +++ b/Source/Falcor/Core/State/ComputeState.cpp @@ -43,7 +43,7 @@ ComputeState::ComputeState(ref pDevice) : mpDevice(pDevice) mpCsoGraph = std::make_unique(); } -ref ComputeState::getCSO(const ComputeVars* pVars) +ref ComputeState::getCSO(const ProgramVars* pVars) { auto pProgramKernels = mpProgram ? mpProgram->getActiveVersion()->getKernels(mpDevice.get(), pVars) : nullptr; bool newProgram = (pProgramKernels.get() != mCachedData.pProgramKernels); @@ -57,7 +57,7 @@ ref ComputeState::getCSO(const ComputeVars* pVars) if (pCso == nullptr) { - mDesc.setProgramKernels(pProgramKernels); + mDesc.pProgramKernels = pProgramKernels; ComputeStateGraph::CompareFunc cmpFunc = [&desc = mDesc](ref pCso) -> bool { return pCso && (desc == pCso->getDesc()); }; @@ -68,7 +68,7 @@ ref ComputeState::getCSO(const ComputeVars* pVars) } else { - pCso = ComputeStateObject::create(mpDevice, mDesc); + pCso = mpDevice->createComputeStateObject(mDesc); mpCsoGraph->setCurrentNodeData(pCso); } } diff --git a/Source/Falcor/Core/State/ComputeState.h b/Source/Falcor/Core/State/ComputeState.h index ceffb9e35..300317c84 100644 --- a/Source/Falcor/Core/State/ComputeState.h +++ b/Source/Falcor/Core/State/ComputeState.h @@ -30,12 +30,12 @@ #include "Core/Macros.h" #include "Core/Object.h" #include "Core/API/ComputeStateObject.h" -#include "Core/Program/ComputeProgram.h" +#include "Core/Program/Program.h" #include namespace Falcor { -class ComputeVars; +class ProgramVars; /** * Compute state. @@ -58,7 +58,7 @@ class FALCOR_API ComputeState : public Object /** * Bind a program to the pipeline */ - ComputeState& setProgram(ref pProgram) + ComputeState& setProgram(ref pProgram) { mpProgram = pProgram; return *this; @@ -67,19 +67,19 @@ class FALCOR_API ComputeState : public Object /** * Get the currently bound program */ - ref getProgram() const { return mpProgram; } + ref getProgram() const { return mpProgram; } /** * Get the active compute state object */ - ref getCSO(const ComputeVars* pVars); + ref getCSO(const ProgramVars* pVars); private: ComputeState(ref pDevice); ref mpDevice; - ref mpProgram; - ComputeStateObject::Desc mDesc; + ref mpProgram; + ComputeStateObjectDesc mDesc; struct CachedData { diff --git a/Source/Falcor/Core/State/GraphicsState.cpp b/Source/Falcor/Core/State/GraphicsState.cpp index af548f9df..7c5c5f039 100644 --- a/Source/Falcor/Core/State/GraphicsState.cpp +++ b/Source/Falcor/Core/State/GraphicsState.cpp @@ -33,21 +33,21 @@ namespace Falcor { -static GraphicsStateObject::PrimitiveType topology2Type(Vao::Topology t) +static GraphicsStateObjectDesc::PrimitiveType topology2Type(Vao::Topology t) { switch (t) { case Vao::Topology::PointList: - return GraphicsStateObject::PrimitiveType::Point; + return GraphicsStateObjectDesc::PrimitiveType::Point; case Vao::Topology::LineList: case Vao::Topology::LineStrip: - return GraphicsStateObject::PrimitiveType::Line; + return GraphicsStateObjectDesc::PrimitiveType::Line; case Vao::Topology::TriangleList: case Vao::Topology::TriangleStrip: - return GraphicsStateObject::PrimitiveType::Triangle; + return GraphicsStateObjectDesc::PrimitiveType::Triangle; default: FALCOR_UNREACHABLE(); - return GraphicsStateObject::PrimitiveType::Undefined; + return GraphicsStateObjectDesc::PrimitiveType::Undefined; } } @@ -75,7 +75,7 @@ GraphicsState::GraphicsState(ref pDevice) : mpDevice(pDevice) GraphicsState::~GraphicsState() = default; -ref GraphicsState::getGSO(const GraphicsVars* pVars) +ref GraphicsState::getGSO(const ProgramVars* pVars) { auto pProgramKernels = mpProgram ? mpProgram->getActiveVersion()->getKernels(mpDevice, pVars) : nullptr; bool newProgVersion = pProgramKernels.get() != mCachedData.pProgramKernels; @@ -95,10 +95,10 @@ ref GraphicsState::getGSO(const GraphicsVars* pVars) ref pGso = mpGsoGraph->getCurrentNode(); if (pGso == nullptr) { - mDesc.setProgramKernels(pProgramKernels); - mDesc.setFboFormats(mpFbo ? mpFbo->getDesc() : Fbo::Desc()); - mDesc.setVertexLayout(mpVao->getVertexLayout()); - mDesc.setPrimitiveType(topology2Type(mpVao->getPrimitiveTopology())); + mDesc.pProgramKernels = pProgramKernels; + mDesc.fboDesc = mpFbo ? mpFbo->getDesc() : Fbo::Desc(); + mDesc.pVertexLayout = mpVao->getVertexLayout(); + mDesc.primitiveType = topology2Type(mpVao->getPrimitiveTopology()); GraphicsStateGraph::CompareFunc cmpFunc = [&desc = mDesc](ref pGso) -> bool { return pGso && (desc == pGso->getDesc()); }; @@ -109,7 +109,7 @@ ref GraphicsState::getGSO(const GraphicsVars* pVars) } else { - pGso = GraphicsStateObject::create(mpDevice, mDesc); + pGso = mpDevice->createGraphicsStateObject(mDesc); pGso->breakStrongReferenceToDevice(); mpGsoGraph->setCurrentNodeData(pGso); } @@ -139,7 +139,7 @@ void GraphicsState::pushFbo(const ref& pFbo, bool setVp0Sc0) void GraphicsState::popFbo(bool setVp0Sc0) { - checkInvariant(!mFboStack.empty(), "Empty stack."); + FALCOR_CHECK(!mFboStack.empty(), "Empty stack."); setFbo(mFboStack.top(), setVp0Sc0); mFboStack.pop(); @@ -157,9 +157,9 @@ GraphicsState& GraphicsState::setVao(const ref& pVao) GraphicsState& GraphicsState::setBlendState(ref pBlendState) { - if (mDesc.getBlendState() != pBlendState) + if (mDesc.pBlendState != pBlendState) { - mDesc.setBlendState(pBlendState); + mDesc.pBlendState = pBlendState; mpGsoGraph->walk((void*)pBlendState.get()); } return *this; @@ -167,9 +167,9 @@ GraphicsState& GraphicsState::setBlendState(ref pBlendState) GraphicsState& GraphicsState::setRasterizerState(ref pRasterizerState) { - if (mDesc.getRasterizerState() != pRasterizerState) + if (mDesc.pRasterizerState != pRasterizerState) { - mDesc.setRasterizerState(pRasterizerState); + mDesc.pRasterizerState = pRasterizerState; mpGsoGraph->walk((void*)pRasterizerState.get()); } return *this; @@ -177,9 +177,9 @@ GraphicsState& GraphicsState::setRasterizerState(ref pRasterize GraphicsState& GraphicsState::setSampleMask(uint32_t sampleMask) { - if (mDesc.getSampleMask() != sampleMask) + if (mDesc.sampleMask != sampleMask) { - mDesc.setSampleMask(sampleMask); + mDesc.sampleMask = sampleMask; mpGsoGraph->walk((void*)(uint64_t)sampleMask); } return *this; @@ -187,9 +187,9 @@ GraphicsState& GraphicsState::setSampleMask(uint32_t sampleMask) GraphicsState& GraphicsState::setDepthStencilState(ref pDepthStencilState) { - if (mDesc.getDepthStencilState() != pDepthStencilState) + if (mDesc.pDepthStencilState != pDepthStencilState) { - mDesc.setDepthStencilState(pDepthStencilState); + mDesc.pDepthStencilState = pDepthStencilState; mpGsoGraph->walk((void*)pDepthStencilState.get()); } return *this; @@ -197,7 +197,7 @@ GraphicsState& GraphicsState::setDepthStencilState(ref pDepth void GraphicsState::pushViewport(uint32_t index, const GraphicsState::Viewport& vp, bool setScissors) { - checkArgument(index < mVpStack.size(), "'index' is out of range."); + FALCOR_CHECK(index < mVpStack.size(), "'index' is out of range."); mVpStack[index].push(mViewports[index]); setViewport(index, vp, setScissors); @@ -205,8 +205,8 @@ void GraphicsState::pushViewport(uint32_t index, const GraphicsState::Viewport& void GraphicsState::popViewport(uint32_t index, bool setScissors) { - checkArgument(index < mVpStack.size(), "'index' is out of range."); - checkInvariant(!mVpStack[index].empty(), "Empty stack."); + FALCOR_CHECK(index < mVpStack.size(), "'index' is out of range."); + FALCOR_CHECK(!mVpStack[index].empty(), "Empty stack."); const auto& VP = mVpStack[index].top(); setViewport(index, VP, setScissors); @@ -215,7 +215,7 @@ void GraphicsState::popViewport(uint32_t index, bool setScissors) void GraphicsState::pushScissors(uint32_t index, const GraphicsState::Scissor& sc) { - checkArgument(index < mScStack.size(), "'index' is out of range."); + FALCOR_CHECK(index < mScStack.size(), "'index' is out of range."); mScStack[index].push(mScissors[index]); setScissors(index, sc); @@ -223,8 +223,8 @@ void GraphicsState::pushScissors(uint32_t index, const GraphicsState::Scissor& s void GraphicsState::popScissors(uint32_t index) { - checkArgument(index < mScStack.size(), "'index' is out of range."); - checkInvariant(!mScStack[index].empty(), "Empty stack."); + FALCOR_CHECK(index < mScStack.size(), "'index' is out of range."); + FALCOR_CHECK(!mScStack[index].empty(), "Empty stack."); const auto& sc = mScStack[index].top(); setScissors(index, sc); diff --git a/Source/Falcor/Core/State/GraphicsState.h b/Source/Falcor/Core/State/GraphicsState.h index c3b7568ef..96b3b3d4e 100644 --- a/Source/Falcor/Core/State/GraphicsState.h +++ b/Source/Falcor/Core/State/GraphicsState.h @@ -35,14 +35,14 @@ #include "Core/API/RasterizerState.h" #include "Core/API/BlendState.h" #include "Core/API/GraphicsStateObject.h" -#include "Core/Program/GraphicsProgram.h" +#include "Core/Program/Program.h" #include #include #include namespace Falcor { -class GraphicsVars; +class ProgramVars; /** * Pipeline state. @@ -212,7 +212,7 @@ class FALCOR_API GraphicsState : public Object /** * Bind a program to the pipeline. */ - GraphicsState& setProgram(const ref& pProgram) + GraphicsState& setProgram(const ref& pProgram) { FALCOR_ASSERT(pProgram); mpProgram = pProgram; @@ -222,7 +222,7 @@ class FALCOR_API GraphicsState : public Object /** * Get the currently bound program. */ - ref getProgram() const { return mpProgram; } + ref getProgram() const { return mpProgram; } /** * Set a blend-state. @@ -232,7 +232,7 @@ class FALCOR_API GraphicsState : public Object /** * Get the currently bound blend-state. */ - ref getBlendState() const { return mDesc.getBlendState(); } + ref getBlendState() const { return mDesc.pBlendState; } /** * Set a rasterizer-state. @@ -242,7 +242,7 @@ class FALCOR_API GraphicsState : public Object /** * Get the currently bound rasterizer-state. */ - ref getRasterizerState() const { return mDesc.getRasterizerState(); } + ref getRasterizerState() const { return mDesc.pRasterizerState; } /** * Set a depth-stencil state. @@ -252,7 +252,7 @@ class FALCOR_API GraphicsState : public Object /** * Get the currently bound depth-stencil state. */ - ref getDepthStencilState() const { return mDesc.getDepthStencilState(); } + ref getDepthStencilState() const { return mDesc.pDepthStencilState; } /** * Set the sample mask. @@ -262,17 +262,17 @@ class FALCOR_API GraphicsState : public Object /** * Get the current sample mask. */ - uint32_t getSampleMask() const { return mDesc.getSampleMask(); } + uint32_t getSampleMask() const { return mDesc.sampleMask; } /** * Get the active graphics state object. */ - virtual ref getGSO(const GraphicsVars* pVars); + virtual ref getGSO(const ProgramVars* pVars); /** * Get the desc */ - const GraphicsStateObject::Desc& getDesc() const { return mDesc; } + const GraphicsStateObjectDesc& getDesc() const { return mDesc; } void breakStrongReferenceToDevice(); @@ -282,8 +282,8 @@ class FALCOR_API GraphicsState : public Object BreakableReference mpDevice; ref mpVao; ref mpFbo; - ref mpProgram; - GraphicsStateObject::Desc mDesc; + ref mpProgram; + GraphicsStateObjectDesc mDesc; uint8_t mStencilRef = 0; std::vector mViewports; std::vector mScissors; diff --git a/Source/Falcor/Core/Testbed.cpp b/Source/Falcor/Core/Testbed.cpp index ffba7e3b9..a0ddb3fa0 100644 --- a/Source/Falcor/Core/Testbed.cpp +++ b/Source/Falcor/Core/Testbed.cpp @@ -27,6 +27,7 @@ **************************************************************************/ #include "Testbed.h" #include "Core/ObjectPython.h" +#include "Core/AssetResolver.h" #include "Core/Program/ProgramManager.h" #include "Utils/Scripting/ScriptBindings.h" #include "Utils/Threading.h" @@ -63,6 +64,11 @@ void Testbed::interrupt() mShouldInterrupt = true; } +void Testbed::close() +{ + mShouldClose = true; +} + void Testbed::frame() { FALCOR_ASSERT(mpDevice); @@ -106,6 +112,12 @@ void Testbed::frame() } } + // Blit the current render texture if set. + if (mpRenderTexture) + { + pRenderContext->blit(mpRenderTexture->getSRV(), mpTargetFBO->getRenderTargetView(0)); + } + renderUI(); #if FALCOR_ENABLE_PROFILER @@ -120,8 +132,7 @@ void Testbed::frame() Texture* pSwapchainImage = mpSwapchain->getImage(imageIndex).get(); pRenderContext->copyResource(pSwapchainImage, mpTargetFBO->getColorTexture(0).get()); pRenderContext->resourceBarrier(pSwapchainImage, Resource::State::Present); - pRenderContext->flush(); - FALCOR_PROFILE_CUSTOM(pRenderContext, "present", Profiler::Flags::Internal); + pRenderContext->submit(); mpSwapchain->present(); } @@ -220,12 +231,22 @@ void Testbed::handleKeyboardEvent(const KeyboardEvent& keyEvent) if (keyEvent.type == KeyboardEvent::Type::KeyPressed) { - if (keyEvent.key == Input::Key::Escape) + switch (keyEvent.key) + { + case Input::Key::Escape: interrupt(); - if (keyEvent.key == Input::Key::F2) + close(); + break; + case Input::Key::F2: mUI.showUI = !mUI.showUI; - if (keyEvent.key == Input::Key::P) - mpDevice->getProfiler()->setEnabled(mpDevice->getProfiler()->isEnabled()); + break; + case Input::Key::F5: + mpDevice->getProgramManager()->reloadAllPrograms(); + break; + case Input::Key::P: + mpDevice->getProfiler()->setEnabled(!mpDevice->getProfiler()->isEnabled()); + break; + } } if (mpRenderGraph && mpRenderGraph->onKeyEvent(keyEvent)) @@ -265,13 +286,17 @@ void Testbed::internalInit(const Options& options) OSServices::start(); Threading::start(); - // Populate the data search paths from the config file, only adding those that aren't in already - auto searchDirectories = Settings::getGlobalSettings().getSearchDirectories("media"); - for (auto& it : searchDirectories.get()) - addDataDirectory(it); + // Setup asset search paths. + AssetResolver& resolver = AssetResolver::getDefaultResolver(); + resolver.addSearchPath(getProjectDirectory() / "media"); + for (auto& path : Settings::getGlobalSettings().getSearchDirectories("media")) + resolver.addSearchPath(path); // Create the device. - mpDevice = make_ref(options.deviceDesc); + if (options.pDevice) + mpDevice = options.pDevice; + else + mpDevice = make_ref(options.deviceDesc); // Create the window & swapchain. if (options.createWindow) @@ -292,18 +317,12 @@ void Testbed::internalInit(const Options& options) uint2 fboSize = mpWindow ? mpWindow->getClientAreaSize() : uint2(options.windowDesc.width, options.windowDesc.height); mpTargetFBO = Fbo::create2D(mpDevice, fboSize.x, fboSize.y, options.colorFormat, options.depthFormat); - // Set global shader defines. - DefineList globalDefines = { - {"FALCOR_NVAPI_AVAILABLE", (FALCOR_NVAPI_AVAILABLE && mpDevice->getType() == Device::Type::D3D12) ? "1" : "0"}, -#if FALCOR_NVAPI_AVAILABLE - {"NV_SHADER_EXTN_SLOT", "u999"}, -#endif - }; - mpDevice->getProgramManager()->addGlobalDefines(globalDefines); - // Create the GUI. mpGui = std::make_unique(mpDevice, mpTargetFBO->getWidth(), mpTargetFBO->getHeight(), getDisplayScaleFactor()); + // Create python UI screen. + mpScreen = make_ref(); + mFrameRate.reset(); } @@ -316,10 +335,11 @@ void Testbed::internalShutdown() mpScene.reset(); if (mpDevice) - mpDevice->flushAndSync(); + mpDevice->wait(); Threading::shutdown(); + mpScreen.reset(); mpGui.reset(); mpTargetFBO.reset(); @@ -345,6 +365,9 @@ void Testbed::resizeTargetFBO(uint32_t width, uint32_t height) if (mpRenderGraph) mpRenderGraph->onResize(mpTargetFBO.get()); + + if (mpScene) + mpScene->setCameraAspectRatio(width / (float)height); } void Testbed::renderUI() @@ -370,6 +393,7 @@ void Testbed::renderUI() "ESC - Exit (or return to Python interpreter)\n" "F1 - Show this help screen\n" "F2 - Show/hide UI\n" + "F5 - Reload shaders\n" "P - Enable/disable profiler\n" "\n" ); @@ -388,48 +412,50 @@ void Testbed::renderUI() if (mUI.showFPS) { Gui::Window w( - mpGui.get(), "##FPS", {0, 0}, {10, 10}, + mpGui.get(), + "##FPS", + {0, 0}, + {10, 10}, Gui::WindowFlags::AllowMove | Gui::WindowFlags::AutoResize | Gui::WindowFlags::SetFocus ); w.text(mFrameRate.getMsg()); } - // Profiler. + if (mpRenderGraph) { - if (pProfiler->isEnabled()) - { - bool open = pProfiler->isEnabled(); - Gui::Window profilerWindow(mpGui.get(), "Profiler", open, {800, 350}, {10, 10}); - pProfiler->endEvent(pRenderContext, "renderUI"); // Suspend renderUI profiler event - - if (open) - { - if (!mpProfilerUI) - mpProfilerUI = std::make_unique(pProfiler); - - mpProfilerUI->render(); - pProfiler->startEvent(pRenderContext, "renderUI"); - profilerWindow.release(); - } - - pProfiler->setEnabled(open); - } + Gui::Window w(mpGui.get(), "Render Graph", {300, 300}, {10, 50}); + mpRenderGraph->renderUI(pRenderContext, w); } + if (mpScene) { - Gui::Window w(mpGui.get(), "Render Graph", {300, 300}, {10, 50}); - if (mpRenderGraph) - mpRenderGraph->renderUI(pRenderContext, w); - else - w.text("No render graph loaded"); + Gui::Window w(mpGui.get(), "Scene", {300, 300}, {10, 360}); + mpScene->renderUI(w); } + // Render Python UI. + mpScreen->render(); + } + + // Profiler. + { + if (pProfiler->isEnabled()) { - Gui::Window w(mpGui.get(), "Scene", {300, 300}, {10, 360}); - if (mpScene) - mpScene->renderUI(w); - else - w.text("No scene loaded"); + bool open = pProfiler->isEnabled(); + Gui::Window profilerWindow(mpGui.get(), "Profiler", open, {800, 350}, {10, 10}); + pProfiler->endEvent(pRenderContext, "renderUI"); // Suspend renderUI profiler event + + if (open) + { + if (!mpProfilerUI) + mpProfilerUI = std::make_unique(pProfiler); + + mpProfilerUI->render(); + pProfiler->startEvent(pRenderContext, "renderUI"); + profilerWindow.release(); + } + + pProfiler->setEnabled(open); } } @@ -446,7 +472,7 @@ void Testbed::captureOutput(const std::filesystem::path& path, uint32_t outputIn const std::string outputName = mpRenderGraph->getOutputName(outputIndex); const ref pOutput = mpRenderGraph->getOutput(outputName)->asTexture(); if (!pOutput) - throw RuntimeError("Graph output {} is not a texture", outputName); + FALCOR_THROW("Graph output {} is not a texture", outputName); const ResourceFormat format = pOutput->getFormat(); const uint32_t channels = getFormatChannelCount(format); @@ -547,14 +573,20 @@ void Testbed::captureOutput(const std::filesystem::path& path, uint32_t outputIn if (is_set(mask, TextureChannelFlags::RGB) && isSrgbFormat(format)) { logWarning( - "Graph output {} mask {:#x} extracting single RGB channel from SRGB format may lose precision.", outputName, + "Graph output {} mask {:#x} extracting single RGB channel from SRGB format may lose precision.", + outputName, (uint32_t)mask ); } // Copy color channel into temporary texture. - pTex = Texture::create2D( - mpDevice, pOutput->getWidth(), pOutput->getHeight(), outputFormat, 1, 1, nullptr, + pTex = mpDevice->createTexture2D( + pOutput->getWidth(), + pOutput->getHeight(), + outputFormat, + 1, + 1, + nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess ); mpImageProcessing->copyColorChannel(pRenderContext, pOutput->getSRV(0, 1, 0, 1), pTex->getUAV(), mask); @@ -579,6 +611,7 @@ FALCOR_SCRIPT_BINDING(Testbed) FALCOR_SCRIPT_BINDING_DEPENDENCY(Profiler) FALCOR_SCRIPT_BINDING_DEPENDENCY(Scene) FALCOR_SCRIPT_BINDING_DEPENDENCY(SceneBuilder) + FALCOR_SCRIPT_BINDING_DEPENDENCY(python_ui); using namespace pybind11::literals; @@ -586,10 +619,17 @@ FALCOR_SCRIPT_BINDING(Testbed) testbed.def( pybind11::init( - [](uint32_t width, uint32_t height, bool create_window, Device::Type device_type, uint32_t gpu, bool enable_debug_layers, - bool enable_aftermath) + [](uint32_t width, + uint32_t height, + bool create_window, + Device::Type device_type, + uint32_t gpu, + bool enable_debug_layers, + bool enable_aftermath, + ref device) { Testbed::Options options; + options.pDevice = device; options.windowDesc.width = width; options.windowDesc.height = height; options.createWindow = create_window; @@ -600,15 +640,24 @@ FALCOR_SCRIPT_BINDING(Testbed) return Testbed::create(options); } ), - "width"_a = 1920, "height"_a = 1080, "create_window"_a = false, "device_type"_a = Device::Type::Default, "gpu"_a = 0, - "enable_debug_layers"_a = false, "enable_aftermath"_a = false + "width"_a = 1920, + "height"_a = 1080, + "create_window"_a = false, + "device_type"_a = Device::Type::Default, + "gpu"_a = 0, + "enable_debug_layers"_a = false, + "enable_aftermath"_a = false, + "device"_a = nullptr ); testbed.def("run", &Testbed::run); testbed.def("frame", &Testbed::frame); testbed.def("resize_frame_buffer", &Testbed::resizeFrameBuffer, "width"_a, "height"_a); testbed.def("load_scene", &Testbed::loadScene, "path"_a, "build_flags"_a = SceneBuilder::Flags::Default); testbed.def( - "load_scene_from_string", &Testbed::loadSceneFromString, "scene"_a, "extension"_a = "pyscene", + "load_scene_from_string", + &Testbed::loadSceneFromString, + "scene"_a, + "extension"_a = "pyscene", "build_flags"_a = SceneBuilder::Flags::Default ); testbed.def("create_render_graph", &Testbed::createRenderGraph, "name"_a = ""); @@ -620,31 +669,10 @@ FALCOR_SCRIPT_BINDING(Testbed) testbed.def_property_readonly("scene", &Testbed::getScene); testbed.def_property_readonly("clock", &Testbed::getClock); // PYTHONDEPRECATED testbed.def_property("render_graph", &Testbed::getRenderGraph, &Testbed::setRenderGraph); + testbed.def_property("render_texture", &Testbed::getRenderTexture, &Testbed::setRenderTexture); + testbed.def_property_readonly("screen", &Testbed::getScreen); testbed.def_property("show_ui", &Testbed::getShowUI, &Testbed::setShowUI); - - // PYTHONDEPRECATED BEGIN - testbed.def( - pybind11::init( - [](uint32_t width, uint32_t height, bool createWindow, Device::Type deviceType, uint32_t gpu) - { - Testbed::Options options; - options.windowDesc.width = width; - options.windowDesc.height = height; - options.createWindow = createWindow; - options.deviceDesc.type = deviceType; - options.deviceDesc.gpu = gpu; - return Testbed::create(options); - } - ), - "width"_a = 1920, "height"_a = 1080, "createWindow"_a = false, "deviceType"_a = Device::Type::Default, "gpu"_a = 0 - ); - testbed.def("resizeFrameBuffer", &Testbed::resizeFrameBuffer, "width"_a, "height"_a); - testbed.def("loadScene", &Testbed::loadScene, "path"_a, "build_flags"_a = SceneBuilder::Flags::Default); - testbed.def("createRenderGraph", &Testbed::createRenderGraph, "name"_a = ""); - testbed.def("loadRenderGraph", &Testbed::loadRenderGraph, "path"_a); - testbed.def("captureOutput", &Testbed::captureOutput, "filename"_a, "outputIndex"_a = uint32_t(0)); // PYTHONDEPRECATED - testbed.def_property("renderGraph", &Testbed::getRenderGraph, &Testbed::setRenderGraph); - // PYTHONDEPRECATED END + testbed.def_property_readonly("should_close", &Testbed::shouldClose); } } // namespace Falcor diff --git a/Source/Falcor/Core/Testbed.h b/Source/Falcor/Core/Testbed.h index 78d02f0bf..3d2d27404 100644 --- a/Source/Falcor/Core/Testbed.h +++ b/Source/Falcor/Core/Testbed.h @@ -36,6 +36,7 @@ #include "Scene/SceneBuilder.h" #include "Utils/Image/ImageProcessing.h" #include "Utils/Timing/FrameRate.h" +#include "Utils/UI/PythonUI.h" #include "Utils/Timing/Clock.h" #include #include @@ -53,14 +54,18 @@ class Testbed : public Object, private Window::ICallbacks public: struct Options { - Options() {} // Work around clang++ bug: "error: default member initializer for 'createWindow' needed within definition of enclosing - // class 'Testbed' outside of member functions" + // Note: Empty constructor needed for clang due to the use of the nested struct constructor in the parent constructor. + Options() {} + ref pDevice = nullptr; + /// Desc for creating a new device if an existing device isn't already available. Device::Desc deviceDesc; Window::Desc windowDesc; bool createWindow = false; - ResourceFormat colorFormat = ResourceFormat::BGRA8UnormSrgb; ///< Color format of the frame buffer. - ResourceFormat depthFormat = ResourceFormat::D32Float; ///< Depth buffer format of the frame buffer. + /// Color format of the frame buffer. + ResourceFormat colorFormat = ResourceFormat::BGRA8UnormSrgb; + /// Depth buffer format of the frame buffer. + ResourceFormat depthFormat = ResourceFormat::D32Float; }; static ref create(const Options& options) { return make_ref(options); } @@ -77,6 +82,10 @@ class Testbed : public Object, private Window::ICallbacks /// Interrupt the main loop. void interrupt(); + /// Close the testbed. + /// This results in `shouldClose` to return true on the next call. + void close(); + /// Render a single frame. /// Note: This is called repeatadly when running the main loop. void frame(); @@ -84,8 +93,10 @@ class Testbed : public Object, private Window::ICallbacks /// Resize the main frame buffer. void resizeFrameBuffer(uint32_t width, uint32_t height); + /// Load a scene. void loadScene(const std::filesystem::path& path, SceneBuilder::Flags buildFlags = SceneBuilder::Flags::Default); + /// Load a scene from a string. void loadSceneFromString( const std::string& sceneStr, const std::string extension = "pyscene", @@ -95,16 +106,39 @@ class Testbed : public Object, private Window::ICallbacks ref getScene() const; Clock& getClock(); + /// Create a new render graph. ref createRenderGraph(const std::string& name = ""); + + /// Load a render graph from a file. ref loadRenderGraph(const std::filesystem::path& path); + /// Set the active render graph. void setRenderGraph(const ref& graph); + + /// Get the active render graph. const ref& getRenderGraph() const; + /// Get the python UI screen. + const ref& getScreen() const { return mpScreen; } + + /// Capture the output of a render graph and write it to a file. void captureOutput(const std::filesystem::path& path, uint32_t outputIndex = 0); + /// Set the texture to be rendered on the main window (overrides render graph output). + void setRenderTexture(const ref& texture) { mpRenderTexture = texture; } + + /// Get the texture to be rendered on the main window. + const ref& getRenderTexture() const { return mpRenderTexture; } + + /// Set the UI visibility. + void setShowUI(bool showUI) { mUI.showUI = showUI; } + + /// Get the UI visibility. bool getShowUI() const { return mUI.showUI; } - void setShowUI() { mUI.showUI = true; } + + /// Returns true if the application should terminate. + /// This is true if the window was closed or escape was pressed. + bool shouldClose() const { return mShouldClose || (mpWindow && mpWindow->shouldClose()); } private: // Implementation of Window::ICallbacks @@ -129,10 +163,12 @@ class Testbed : public Object, private Window::ICallbacks ref mpSwapchain; ref mpTargetFBO; std::unique_ptr mpGui; + ref mpScreen; std::unique_ptr mpProfilerUI; ref mpScene; ref mpRenderGraph; + ref mpRenderTexture; std::unique_ptr mpImageProcessing; @@ -140,6 +176,7 @@ class Testbed : public Object, private Window::ICallbacks Clock mClock; bool mShouldInterrupt{false}; + bool mShouldClose{false}; struct { bool showUI = true; diff --git a/Source/Falcor/Core/Version.cpp b/Source/Falcor/Core/Version.cpp index 6facbc5e6..fd7716992 100644 --- a/Source/Falcor/Core/Version.cpp +++ b/Source/Falcor/Core/Version.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -46,7 +46,9 @@ const std::string& getLongVersionString() if (GIT_VERSION_AVAILABLE) { return fmt::format( - "commit: {}, branch: {}{}", GIT_VERSION_COMMIT, GIT_VERSION_BRANCH, + "commit: {}, branch: {}{}", + GIT_VERSION_COMMIT, + GIT_VERSION_BRANCH, GIT_VERSION_DIRTY ? ", contains uncommitted changes" : "" ); } diff --git a/Source/Falcor/Core/Version.h b/Source/Falcor/Core/Version.h index d5b6e862f..808835dc8 100644 --- a/Source/Falcor/Core/Version.h +++ b/Source/Falcor/Core/Version.h @@ -31,7 +31,7 @@ #include -#define FALCOR_MAJOR_VERSION 6 +#define FALCOR_MAJOR_VERSION 7 #define FALCOR_MINOR_VERSION 0 namespace Falcor diff --git a/Source/Falcor/Core/Window.cpp b/Source/Falcor/Core/Window.cpp index 1028b1639..d964e4cc1 100644 --- a/Source/Falcor/Core/Window.cpp +++ b/Source/Falcor/Core/Window.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "Window.h" #include "Macros.h" -#include "Assert.h" +#include "Error.h" #include "ObjectPython.h" #include "GLFW.h" #include "Platform/OS.h" @@ -386,7 +386,7 @@ Window::Window(const Desc& desc, ICallbacks* pCallbacks) if (sWindowCount.fetch_add(1) == 0) { if (glfwInit() == GLFW_FALSE) - throw RuntimeError("Failed to initialize GLFW."); + FALCOR_THROW("Failed to initialize GLFW."); } // Create the window @@ -418,7 +418,7 @@ Window::Window(const Desc& desc, ICallbacks* pCallbacks) mpGLFWWindow = glfwCreateWindow(w, h, desc.title.c_str(), nullptr, nullptr); if (!mpGLFWWindow) { - throw RuntimeError("Failed to create GLFW window."); + FALCOR_THROW("Failed to create GLFW window."); } // Init handles diff --git a/Source/Falcor/DiffRendering/AggregateGradients.cs.slang b/Source/Falcor/DiffRendering/AggregateGradients.cs.slang new file mode 100644 index 000000000..ac815b65e --- /dev/null +++ b/Source/Falcor/DiffRendering/AggregateGradients.cs.slang @@ -0,0 +1,73 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Utils/NVAPI.slangh" + +import DiffRendering.SharedTypes; + +struct GradientsAggregator +{ + uint gradDim; + uint hashSize; + + ByteAddressBuffer tmpGrads; + RWByteAddressBuffer grads; + + void aggregateDirect(uint2 threadID) + { + if (threadID.x >= gradDim || threadID.y >= hashSize) + return; + + uint index = threadID.y * gradDim + threadID.x; + float value = asfloat(tmpGrads.Load(index * 4)); + grads.Store(threadID.x * 4, asuint(value)); + } + + void aggregateHashGrid(uint2 threadID) + { + if (threadID.x >= gradDim || threadID.y >= hashSize) + return; + + uint index = threadID.y * gradDim + threadID.x; + float value = asfloat(tmpGrads.Load(index * 4)); + grads.InterlockedAddF32(threadID.x * 4, value); + } +} + +ParameterBlock gAggregator; + +[numthreads(256, 1, 1)] +void mainDirect(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + gAggregator.aggregateDirect(dispatchThreadID.xy); +} + +[numthreads(64, 8, 1)] +void mainHashGrid(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + gAggregator.aggregateHashGrid(dispatchThreadID.xy); +} diff --git a/Source/Falcor/Utils/Debug/ReflectPixelDebugTypes.cs.slang b/Source/Falcor/DiffRendering/DiffDebug.slang similarity index 93% rename from Source/Falcor/Utils/Debug/ReflectPixelDebugTypes.cs.slang rename to Source/Falcor/DiffRendering/DiffDebug.slang index 44a8bc36f..fb04f183f 100644 --- a/Source/Falcor/Utils/Debug/ReflectPixelDebugTypes.cs.slang +++ b/Source/Falcor/DiffRendering/DiffDebug.slang @@ -25,10 +25,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ +__exported import DiffRendering.SharedTypes; -/** - * Dummy compute program for reflection of the PixelDebug types. - */ -import Utils.Debug.PixelDebug; - -void main() {} +ParameterBlock gDiffDebug; diff --git a/Source/Falcor/DiffRendering/DiffMaterialData.slang b/Source/Falcor/DiffRendering/DiffMaterialData.slang new file mode 100644 index 000000000..d9cd76a6b --- /dev/null +++ b/Source/Falcor/DiffRendering/DiffMaterialData.slang @@ -0,0 +1,82 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +import Scene.Material.ShadingUtils; + +/** + * Stores differentiable parameters used for BSDF evaluation. + */ +struct DiffMaterialData : IDifferentiable +{ + static const uint kMaterialParamCount = 20; + + ShadingFrame sf; + + // Material parameters. + float data[kMaterialParamCount]; + + [Differentiable] + __init() + { + sf = ShadingFrame.createIdentity(); + [ForceUnroll] + for (uint i = 0; i < kMaterialParamCount; i++) + data[i] = 0.f; + } + + [Differentiable] + float read(inout uint offset) { return data[offset++]; } + + [Differentiable] + void read(out vector value, inout uint offset) + { + [ForceUnroll] + for (uint i = 0; i < N; i++) + value[i] = read(offset); + } + + [Differentiable] + vector read(inout uint offset) + { + vector value; + this.read(value, offset); + return value; + } + + [mutating] + [Differentiable] + void write(float value, inout uint offset) { data[offset++] = value; } + + [mutating] + [Differentiable] + void write(vector value, inout uint offset) + { + [ForceUnroll] + for (uint i = 0; i < N; i++) + this.write(value[i], offset); + } +}; diff --git a/Source/Falcor/DiffRendering/DiffSceneIO.slang b/Source/Falcor/DiffRendering/DiffSceneIO.slang new file mode 100644 index 000000000..959ecfa6e --- /dev/null +++ b/Source/Falcor/DiffRendering/DiffSceneIO.slang @@ -0,0 +1,128 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Scene/SceneDefines.slangh" +#include "Utils/Math/MathConstants.slangh" + +import Scene.Scene; +import Scene.SceneTypes; + +__exported import DiffRendering.SceneGradientInfo; +import DiffRendering.DiffDebug; + +RWTexture2D gOutputDColor; + +// Avoid compilation warnings if not running in a differentiable mode. +#ifndef DIFF_MODE +#define DIFF_MODE 0 +#endif + +// Scene IO wrapper for propagating gradients. +struct DiffSceneIO +{ + // Vertex position + [ForwardDerivative(fwd_loadVertexPositionsW)] + [BackwardDerivative(bwd_loadVertexPositionsW)] + [PreferRecompute] + void loadVertexPositionsW(SceneGradientInfo gradInfo, GeometryInstanceID instanceID, uint triangleID, out float3 p[3]) + { + no_diff gScene.getVertexPositionsW(instanceID, triangleID, p); + } + + [Differentiable] + [PreferRecompute] + void fwd_loadVertexPositionsW( + DifferentialPair dpGradInfo, + GeometryInstanceID instanceID, + uint triangleID, + out DifferentialPair dpPos + ) + { + float3 p[3]; + loadVertexPositionsW(dpGradInfo.p, instanceID, triangleID, p); + + float3.Differential dPos[3]; + [ForceUnroll] + for (uint i = 0; i < 3; i++) + dPos[i] = float3(0.f); + +#if DIFF_MODE == 2 // ForwardDiffDebug + // For visualizing a gradient image. + if (dpGradInfo.d.flag.gradMode == GradientMode.ForwardDebug && gDiffDebug.varType == DiffVariableType::GeometryTranslation && + gDiffDebug.id.x == instanceID.index) + { + [ForceUnroll] + for (uint i = 0; i < 3; i++) + dPos[i] = gDiffDebug.grad.xyz; + } +#endif + + dpPos = diffPair(p, dPos); + } + + [Differentiable] + [PreferRecompute] + void bwd_loadVertexPositionsW( + inout DifferentialPair dpGradInfo, + GeometryInstanceID instanceID, + uint triangleID, + in float3.Differential dPos[3] + ) + { +#if DIFF_MODE == 3 // BackwardDiffDebug + // For visualizing a gradient image. + if (dpGradInfo.d.flag.gradMode == GradientMode.Scene && gDiffDebug.varType == DiffVariableType::GeometryTranslation && + gDiffDebug.id.x == instanceID.index) + { + float3 velocity = gDiffDebug.grad.xyz; + float value = dot(velocity, dPos[0]) + dot(velocity, dPos[1]) + dot(velocity, dPos[2]); + gOutputDColor[dpGradInfo.p.pixel] += float4(value, 0.f, 0.f, 0.f); + } +#endif + } + + // TODO: Add custom derivatives for the following functions. + + // Vertex normal + [Differentiable] + void loadVertexNormalsW(SceneGradientInfo gradInfo, GeometryInstanceID instanceID, uint triangleID, out float3 n[3]) + { + uint3 indices = gScene.getIndices(instanceID, triangleID); + float3x3 mat = no_diff gScene.getInverseTransposeWorldMatrix(instanceID); + + [ForceUnroll] + for (int i = 0; i < 3; i++) + { + var v = no_diff gScene.getVertex(indices[i]); + n[i] = normalize(mul(mat, v.normal)); + } + } + + // Camera position + [Differentiable] + float3 loadCameraPositionW(SceneGradientInfo gradInfo) { return no_diff gScene.camera.getPosition(); } +}; diff --git a/Source/Falcor/DiffRendering/DiffSceneQuery.slang b/Source/Falcor/DiffRendering/DiffSceneQuery.slang new file mode 100644 index 000000000..fd90a6936 --- /dev/null +++ b/Source/Falcor/DiffRendering/DiffSceneQuery.slang @@ -0,0 +1,263 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Utils/Math/MathConstants.slangh" + +import Scene.RaytracingInline; +import Utils.Math.MathHelpers; +import Utils.Geometry.GeometryHelpers; +import Utils.Sampling.SampleGenerator; +import Utils.Debug.PixelDebug; +import Rendering.Lights.LightHelpers; +import DiffRendering.DiffSceneIO; + +struct RayAD : IDifferentiable +{ + float3 origin; + float3 direction; + uint2 pixel; + + [Differentiable] + [PreferRecompute] + __init(float3 origin, float3 direction, uint2 pixel) + { + this.origin = origin; + this.direction = direction; + this.pixel = pixel; + } +}; + +// Struct holding info about the differentiable ray-triangle intersection. +struct IntersectionAD : IDifferentiable +{ + GeometryInstanceID instanceID; // Instance ID of the mesh instance that contains the hit triangle. + uint triangleID; // Triangle ID of the hit triangle. + float3 barycentrics; // Barycentrics of the hit point + float3 normalW; // World normal of the hit point + float3 posW; // World position of the hit point + float hitT; // Distance from ray origin to hit point + + [Differentiable] + [PreferRecompute] + __init() + { + this.instanceID = GeometryInstanceID(0, 0); + this.triangleID = 0; + this.barycentrics = float3(0.f); + this.normalW = float3(0.f); + this.posW = float3(0.f); + this.hitT = 0.f; + } +}; + +struct SceneQueryAD : IDifferentiable +{ + DiffSceneIO diffSceneIO; + SceneGradientInfo gradInfo; + + [Differentiable] + __init(DiffSceneIO _diffSceneIO, SceneGradientInfo _gradInfo) + { + diffSceneIO = _diffSceneIO; + gradInfo = _gradInfo; + } + + enum DiffIntersectionMode + { + AttachToRay = 0, + AttachToGeometry = 1, + }; + + // Differentiable `traceRayInline` function. + [Differentiable] + [PreferRecompute] + bool traceRayInlineAD(const RayAD rayAD, out IntersectionAD isect, DiffIntersectionMode mode) + { + Ray ray = Ray(detach(rayAD.origin), detach(rayAD.direction), 0.f, FLT_MAX); + + float hitT = 0.f; + SceneRayQuery<0> sceneRayQuery; + + HitInfo hitInfo; + bool isHit = sceneRayQuery.traceRay(ray, hitInfo, hitT, RAY_FLAG_NONE, 0xff); + + if (!isHit) + { + isect = IntersectionAD(); + return false; + } + + // Convert a non-differentiable HitInfo to a differentiable IntersectionAD. + // This method recomputes barycentrics and normals using differentiable functions. + isect = computeIntersectionAD(hitInfo, rayAD, mode); + + return true; + } + + bool traceVisibilityRay(const Ray ray) + { + SceneRayQuery<0> sceneRayQuery; + return sceneRayQuery.traceVisibilityRay(ray, RAY_FLAG_NONE, 0xff); + } + + // Compute differentiable intersection from non-differentiable HitInfo. + [Differentiable] + [PreferRecompute] + IntersectionAD computeIntersectionAD(const HitInfo hitInfo, const RayAD ray, DiffIntersectionMode mode) + { + TriangleHit hit = hitInfo.getTriangleHit(); + + IntersectionAD isect = IntersectionAD(); + + isect.triangleID = hit.primitiveIndex; + isect.instanceID = hit.instanceID; + + if (mode == DiffIntersectionMode.AttachToRay) + { + float3 barycentrics; + float t; + + isect.posW = loadVertexPosW(hit.instanceID, hit.primitiveIndex, ray, t, barycentrics, isect.normalW); + + isect.hitT = t; + isect.barycentrics = barycentrics; + } + else if (mode == DiffIntersectionMode.AttachToGeometry) + { + // Attached to geometry + + // Fix barycentrics. + isect.barycentrics = no_diff(hit.getBarycentricWeights()); + isect.posW = loadVertexPosW(hit.instanceID, hit.primitiveIndex, isect.barycentrics, isect.normalW); + + // Compute hit T from posW and ray origin. + isect.hitT = length(isect.posW - ray.origin); + } + + // Note: We want to pass to vertex positions through shading normals. It hasn't been implemented yet. + // TODO: Handle shading (vertex) normals correctly. + isect.normalW = computeShadingNormal(isect.instanceID, isect.triangleID, isect.barycentrics); + + return isect; + } + + [Differentiable] + [PreferRecompute] + float3 computeShadingNormal(GeometryInstanceID instanceID, uint primitiveIndex, float3 barycentrics) + { + float3 n[3]; + diffSceneIO.loadVertexNormalsW(gradInfo, instanceID, primitiveIndex, n); + + float3 normal = n[0] * barycentrics.x + n[1] * barycentrics.y + n[2] * barycentrics.z; + + return normal; + } + + // Compute vertex position in world space using fixed barycentrics. + [Differentiable] + [PreferRecompute] + float3 loadVertexPosW(GeometryInstanceID instanceID, uint triangleID, no_diff float3 barycentrics, out float3 faceNormalW) + { + float3 v[3]; + diffSceneIO.loadVertexPositionsW(gradInfo, instanceID, triangleID, v); + + // Compute world position of hit point + float3 posW = v[0] * barycentrics.x + v[1] * barycentrics.y + v[2] * barycentrics.z; + faceNormalW = normalize(cross(v[1] - v[0], v[2] - v[0])); + + return posW; + } + + // Compute vertex position and barycentrics in world space using a differentiable ray. + [Differentiable] + [PreferRecompute] + float3 loadVertexPosW( + GeometryInstanceID instanceID, + uint triangleID, + RayAD ray, + out float t, + out float3 barycentrics, + out float3 faceNormalW + ) + { + float3 v[3]; + diffSceneIO.loadVertexPositionsW(gradInfo, instanceID, triangleID, v); + + t = computeRayDistanceToTriangle(v, ray.origin, ray.direction); + + float3 p = ray.origin + ray.direction * t; + computeBarycentrics(v, p, barycentrics); + + // Compute world position of hit point + float3 posW = v[0] * barycentrics.x + v[1] * barycentrics.y + v[2] * barycentrics.z; + faceNormalW = normalize(cross(v[1] - v[0], v[2] - v[0])); + + return posW; + } + + [Differentiable] + [PreferRecompute] + float3 loadCameraPosition() { return diffSceneIO.loadCameraPositionW(gradInfo); } + + [Differentiable] + [PreferRecompute] + float2 computeCameraRayScreenPos(float3 direction, float2 frameDim) + { + // Compute the normalized ray direction assuming a pinhole camera. + float3 rayDirN = normalizeSafe(direction); + + // TODO: Use differentiable loaders for the camera to propagate + // derivatives through the camera properly. + var camera = gScene.camera; + float3 cameraW = camera.data.cameraW; + float3 cameraU = camera.data.cameraU; + float3 cameraV = camera.data.cameraV; + + float3 rayDirFocalPlane = rayDirN * dot(cameraW, cameraW) / dot(cameraW, rayDirN); + + // Project `rayDirFocalPlane` onto plane by removing `cameraW` component. + float3 rayDirNc = rayDirFocalPlane - cameraW; + float2 ndc = float2(dot(rayDirNc, cameraU), dot(rayDirNc, cameraV)) / float2(dot(cameraU, cameraU), dot(cameraV, cameraV)); + + float2 p = (ndc + float2(1, -1)) / float2(2, -2); + float2 screenPos = p * frameDim - float2(0.5, 0.5); + return screenPos; + } + + [Differentiable] + [PreferRecompute] + float3 computeCameraRayDirection(float2 posScreen, float2 frameDim) + { + float2 p = posScreen / float2(frameDim); + float2 ndc = float2(2, -2) * p + float2(-1, 1); + + // TODO: Use differentiable loaders for the camera frame to propagate gradients through the camera parameters properly. + var camera = gScene.camera; + + return normalizeSafe(ndc.x * camera.data.cameraU + ndc.y * camera.data.cameraV + camera.data.cameraW); + } +} diff --git a/Source/Falcor/DiffRendering/GradientIOWrapper.slang b/Source/Falcor/DiffRendering/GradientIOWrapper.slang new file mode 100644 index 000000000..061ebefd4 --- /dev/null +++ b/Source/Falcor/DiffRendering/GradientIOWrapper.slang @@ -0,0 +1,116 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +import DiffRendering.SceneGradients; +import DiffRendering.DiffDebug; +import Scene.Material.MaterialParamLayout; +import Utils.Math.HashUtils; + +// Avoid compilation warnings if not running in a differentiable mode. +#ifndef DIFF_MODE +#define DIFF_MODE 0 +#endif + +// Simple hash function. +uint hashFunction(uint index, uint hashSize) +{ + return jenkinsHash(index) % hashSize; +} + +/** + * The wrapper function has custom derivatives for setting up the data flow of scene gradients during backpropagation. + * It indicates where to accumulate the gradients by providing `offset` in the gradient buffer. + */ +struct GradientIOWrapper +{ + GradientType gradType; + uint baseOffset; + uint hashIndex; + + __init(GradientType _gradType, uint _baseOffset, uint _hashIndex) + { + this.gradType = _gradType; + this.baseOffset = _baseOffset; + this.hashIndex = _hashIndex; + } + + [ForwardDerivative(__fwd_d_getFloat)] + [BackwardDerivative(__bwd_d_getFloat)] + float getFloat(float val, uint offset) { return val; } + + [TreatAsDifferentiable] + DifferentialPair __fwd_d_getFloat(DifferentialPair dpVal, uint offset) { return diffPair(dpVal.p, 0.f); } + + [TreatAsDifferentiable] + void __bwd_d_getFloat(inout DifferentialPair dpVal, uint offset, float dOut) + { + offset += baseOffset; + gSceneGradients.atomicAddGrad(gradType, offset, hashIndex, dOut); + } + + [ForwardDerivative(__fwd_d_getFloats)] + [BackwardDerivative(__bwd_d_getFloats)] + __generic vector getFloat(vector val, uint offset) { return val; } + + [TreatAsDifferentiable] + __generic DifferentialPair> __fwd_d_getFloats(DifferentialPair> dpVal, uint offset) + { + vector d; + [ForceUnroll] + for (int i = 0; i < N; i++) + d[i] = 0.f; + +#if DIFF_MODE == 2 // ForwardDiffDebug + if (gDiffDebug.varType == DiffVariableType::Material && baseOffset == gDiffDebug.id.x * 20 && offset == gDiffDebug.offset) + { + [ForceUnroll] + for (int i = 0; i < N; i++) + d[i] = gDiffDebug.grad[i]; + } +#endif + + return diffPair(dpVal.p, d); + } + + [TreatAsDifferentiable] + __generic void __bwd_d_getFloats(inout DifferentialPair> dpVal, uint offset, vector dOut) + { + offset += baseOffset; + [ForceUnroll] + for (uint i = 0; i < N; i++) + gSceneGradients.atomicAddGrad(gradType, offset + i, hashIndex, dOut[i]); + } + + [Differentiable] + float getFloat(float val, const MaterialParamLayoutEntry entry) { return getFloat(val, entry.offset); } + + [Differentiable] + __generic vector getFloat(vector val, const MaterialParamLayoutEntry entry) + { + return getFloat(val, entry.offset); + } +}; diff --git a/Source/Falcor/DiffRendering/SceneGradientInfo.slang b/Source/Falcor/DiffRendering/SceneGradientInfo.slang new file mode 100644 index 000000000..f9857da9f --- /dev/null +++ b/Source/Falcor/DiffRendering/SceneGradientInfo.slang @@ -0,0 +1,102 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +import Scene.SceneTypes; + +enum GradientMode +{ + None, // No gradients are propagated (in either direction) for this pass. + Scene, // Pass gradients to scene parameters normally. + ForwardDebug, // Use forward-mode gradients to debug. +}; + +// Create a differentiable flag for higher-order differentiation. +// It is mainly used for: +// 1. Set dFlag = { GradientMode.None } to zero out gradients from/to scene parameters for the Jacobian computation. +// 2. Set dFlag = { GradientMode.ForwardDebug } to pass in forward-mode gradients for debugging. +struct SceneGradientFlag : IDifferentiable +{ + GradientMode gradMode; + + typealias Differential = SceneGradientFlag; + + [ForwardDerivative(__fwd_make)] + [BackwardDerivative(__bwd_make)] + static SceneGradientFlag make(GradientMode mode) + { + SceneGradientFlag info = { mode }; + return info; + } + + static DifferentialPair __fwd_make(GradientMode mode) + { + SceneGradientFlag flag = { mode }; + return diffPair(flag, flag); + } + + static void __bwd_make(GradientMode mode, SceneGradientFlag.Differential dFlag) + { + // Do nothing. + } + + [Differentiable] + static SceneGradientFlag dadd(SceneGradientFlag a, SceneGradientFlag b) + { + // Default to all gradients. + return SceneGradientFlag.make(GradientMode.Scene); + } + + [Differentiable] + static SceneGradientFlag dmul(SceneGradientFlag a, SceneGradientFlag b) + { + // Default to all gradients. + return SceneGradientFlag.make(GradientMode.Scene); + } + + [Differentiable] + static SceneGradientFlag dzero() + { + // Default to all gradients. + return SceneGradientFlag.make(GradientMode.Scene); + } +}; + +struct SceneGradientInfo : IDifferentiable +{ + // Differentiable flag for higher-order differentiation. + SceneGradientFlag flag; + + // Extra data. + uint2 pixel; + + [Differentiable] + __init(SceneGradientFlag _flag, uint2 _pixel) + { + flag = _flag; + pixel = _pixel; + } +}; diff --git a/Source/Falcor/DiffRendering/SceneGradients.cpp b/Source/Falcor/DiffRendering/SceneGradients.cpp new file mode 100644 index 000000000..3da792dcd --- /dev/null +++ b/Source/Falcor/DiffRendering/SceneGradients.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "SceneGradients.h" + +namespace Falcor +{ +namespace +{ +const char kShaderFilename[] = "DiffRendering/SceneGradients.slang"; +const char kSceneGradientsBlockName[] = "gSceneGradients"; +const char kGradsBufferName[] = "grads"; +const char kTmpGradsBufferName[] = "tmpGrads"; + +const char kAggregateShaderFilename[] = "DiffRendering/AggregateGradients.cs.slang"; +} // namespace + +SceneGradients::SceneGradients(ref pDevice, uint2 gradDim, uint2 hashSize, GradientAggregateMode mode) + : mpDevice(pDevice), mGradDim(gradDim), mHashSize(hashSize), mAggregateMode(mode) +{ + createParameterBlock(); + + // Create a pass for aggregating gradients. + ProgramDesc desc; + if (mAggregateMode == GradientAggregateMode::Direct) + desc.addShaderLibrary(kAggregateShaderFilename).csEntry("mainDirect"); + else + desc.addShaderLibrary(kAggregateShaderFilename).csEntry("mainHashGrid"); + + mpAggregatePass = ComputePass::create(mpDevice, desc); +} + +void SceneGradients::createParameterBlock() +{ + DefineList defines; + defines.add("SCENE_GRADIENTS_BLOCK"); + auto pPass = ComputePass::create(mpDevice, kShaderFilename, "main", defines); + auto pReflector = pPass->getProgram()->getReflector()->getParameterBlock(kSceneGradientsBlockName); + FALCOR_ASSERT(pReflector); + + // Create GPU buffers. + auto bindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess; + for (size_t i = 0; i < size_t(GradientType::Count); i++) + { + uint32_t elemCount = std::max(mGradDim[i], 1u); + mpGrads[i] = + mpDevice->createBuffer(elemCount * sizeof(float), bindFlags | ResourceBindFlags::Shared, MemoryType::DeviceLocal, nullptr); + + uint32_t hashSize = std::max(mHashSize[i], 1u); + mpTmpGrads[i] = mpDevice->createBuffer(elemCount * hashSize * sizeof(float), bindFlags, MemoryType::DeviceLocal, nullptr); + } + + // Bind resources to parameter block. + mpSceneGradientsBlock = ParameterBlock::create(mpDevice, pReflector); + auto var = mpSceneGradientsBlock->getRootVar(); + for (size_t i = 0; i < size_t(GradientType::Count); i++) + { + var["gradDim"][i] = mGradDim[i]; + var["hashSize"][i] = mHashSize[i]; + var[kTmpGradsBufferName][i] = mpTmpGrads[i]; + } +} + +void SceneGradients::clearGrads(RenderContext* pRenderContext, GradientType _gradType) +{ + uint32_t gradType = uint32_t(_gradType); + pRenderContext->clearUAV(mpTmpGrads[gradType]->getUAV().get(), uint4(0)); + pRenderContext->clearUAV(mpGrads[gradType]->getUAV().get(), uint4(0)); +} + +void SceneGradients::aggregateGrads(RenderContext* pRenderContext, GradientType _gradType) +{ + uint32_t gradType = uint32_t(_gradType); + + uint32_t hashSize = (mAggregateMode == GradientAggregateMode::Direct ? 1 : mHashSize[gradType]); + + // Bind resources. + auto var = mpAggregatePass->getRootVar()["gAggregator"]; + var["gradDim"] = mGradDim[gradType]; + var["hashSize"] = hashSize; + var[kTmpGradsBufferName] = mpTmpGrads[gradType]; + var[kGradsBufferName] = mpGrads[gradType]; + + // Dispatch. + mpAggregatePass->execute(pRenderContext, uint3(mGradDim[gradType], hashSize, 1)); +} + +inline void aggregate(SceneGradients& self, RenderContext* pRenderContext, GradientType gradType) +{ + self.aggregateGrads(pRenderContext, gradType); +#if FALCOR_HAS_CUDA + pRenderContext->waitForFalcor(); +#endif +} + +FALCOR_SCRIPT_BINDING(SceneGradients) +{ + using namespace pybind11::literals; + + pybind11::falcor_enum(m, "GradientType"); + + pybind11::class_> sg(m, "SceneGradients"); + sg.def_static("create", &SceneGradients::create, "device"_a, "grad_dim"_a, "hash_size"_a); + sg.def("clear", &SceneGradients::clearGrads, "render_context"_a, "grad_type"_a); + sg.def("get_grads_buffer", &SceneGradients::getGradsBuffer, "grad_type"_a); + sg.def("aggregate", aggregate, "render_context"_a, "grad_type"_a); +} +} // namespace Falcor diff --git a/Source/Falcor/DiffRendering/SceneGradients.h b/Source/Falcor/DiffRendering/SceneGradients.h new file mode 100644 index 000000000..ad8198ab8 --- /dev/null +++ b/Source/Falcor/DiffRendering/SceneGradients.h @@ -0,0 +1,75 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include "Core/API/ParameterBlock.h" +#include "RenderGraph/RenderPass.h" +#include "SharedTypes.slang" + +namespace Falcor +{ +class FALCOR_API SceneGradients : public Object +{ + FALCOR_OBJECT(SceneGradients); + +public: + SceneGradients(ref pDevice, uint2 gradDim, uint2 hashSize, GradientAggregateMode mode = GradientAggregateMode::HashGrid); + + static ref create(ref pDevice, uint2 gradDim, uint2 hashSize) + { + return make_ref(pDevice, gradDim, hashSize, GradientAggregateMode::HashGrid); + } + + ~SceneGradients() = default; + + void bindShaderData(const ShaderVar& var) const { var = mpSceneGradientsBlock; } + + void clearGrads(RenderContext* pRenderContext, GradientType gradType); + void aggregateGrads(RenderContext* pRenderContext, GradientType gradType); + + uint32_t getGradDim(GradientType gradType) const { return mGradDim[size_t(gradType)]; } + uint32_t getHashSize(GradientType gradType) const { return mHashSize[size_t(gradType)]; } + + const ref& getTmpGradsBuffer(GradientType gradType) const { return mpTmpGrads[size_t(gradType)]; } + const ref& getGradsBuffer(GradientType gradType) const { return mpGrads[size_t(gradType)]; } + +private: + void createParameterBlock(); + + ref mpDevice; + uint2 mGradDim; + uint2 mHashSize; + GradientAggregateMode mAggregateMode; + + ref mpSceneGradientsBlock; + + ref mpGrads[size_t(GradientType::Count)]; + ref mpTmpGrads[size_t(GradientType::Count)]; + + ref mpAggregatePass; +}; +} // namespace Falcor diff --git a/Source/Falcor/Core/API/Common.h b/Source/Falcor/DiffRendering/SceneGradients.slang similarity index 59% rename from Source/Falcor/Core/API/Common.h rename to Source/Falcor/DiffRendering/SceneGradients.slang index dd6a7a1c6..c4bde19be 100644 --- a/Source/Falcor/Core/API/Common.h +++ b/Source/Falcor/DiffRendering/SceneGradients.slang @@ -25,39 +25,40 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#pragma once +#include "Utils/NVAPI.slangh" -#include "Core/Enum.h" +__exported import DiffRendering.SharedTypes; -namespace Falcor +struct SceneGradients { -enum class ComparisonFunc -{ - Disabled, ///< Comparison is disabled - Never, ///< Comparison always fails - Always, ///< Comparison always succeeds - Less, ///< Passes if source is less than the destination - Equal, ///< Passes if source is equal to the destination - NotEqual, ///< Passes if source is not equal to the destination - LessEqual, ///< Passes if source is less than or equal to the destination - Greater, ///< Passes if source is greater than to the destination - GreaterEqual, ///< Passes if source is greater than or equal to the destination -}; + uint gradDim[(uint)GradientType::Count]; + uint hashSize[(uint)GradientType::Count]; + + // Temporary buffers for keeping gradients before aggregating them. + RWByteAddressBuffer tmpGrads[(uint)GradientType::Count]; + + uint getGradDim(GradientType gradType) { return gradDim[(uint)gradType]; } + + uint getHashSize(GradientType gradType) { return hashSize[(uint)gradType]; } -FALCOR_ENUM_INFO( - ComparisonFunc, + float getGrad(GradientType gradType, uint gradIndex, uint hashIndex) { - {ComparisonFunc::Disabled, "Disabled"}, - {ComparisonFunc::Never, "Never"}, - {ComparisonFunc::Always, "Always"}, - {ComparisonFunc::Less, "Less"}, - {ComparisonFunc::Equal, "Equal"}, - {ComparisonFunc::NotEqual, "NotEqual"}, - {ComparisonFunc::LessEqual, "LessEqual"}, - {ComparisonFunc::Greater, "Greater"}, - {ComparisonFunc::GreaterEqual, "GreaterEqual"}, + uint index = hashIndex * gradDim[(uint)gradType] + gradIndex; + return asfloat(tmpGrads[(uint)gradType].Load(index * 4)); } -); -FALCOR_ENUM_REGISTER(ComparisonFunc); -} // namespace Falcor + void atomicAddGrad(GradientType gradType, uint gradIndex, uint hashIndex, float value) + { + if (gradIndex < gradDim[(uint)gradType]) + { + uint index = hashIndex * gradDim[(uint)gradType] + gradIndex; + tmpGrads[(uint)gradType].InterlockedAddF32(index * 4, value); + } + } +}; + +ParameterBlock gSceneGradients; + +#ifdef SCENE_GRADIENTS_BLOCK +void main() {} +#endif diff --git a/Source/Falcor/DiffRendering/SharedTypes.slang b/Source/Falcor/DiffRendering/SharedTypes.slang new file mode 100644 index 000000000..54a3538c2 --- /dev/null +++ b/Source/Falcor/DiffRendering/SharedTypes.slang @@ -0,0 +1,103 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include "Utils/HostDeviceShared.slangh" + +BEGIN_NAMESPACE_FALCOR + +enum class DiffMode : uint32_t +{ + Primal, // Outputs primal images like path tracers do. + BackwardDiff, // Outputs gradient vectors. + ForwardDiffDebug, // Outputs gradient images w.r.t. a single variable for debugging. + BackwardDiffDebug, // Outputs gradient images w.r.t. a single variable for debugging. +}; + +FALCOR_ENUM_INFO( + DiffMode, + { + { DiffMode::Primal, "Primal" }, + { DiffMode::BackwardDiff, "BackwardDiff" }, + { DiffMode::ForwardDiffDebug, "ForwardDiffDebug" }, + { DiffMode::BackwardDiffDebug, "BackwardDiffDebug" }, + } +); +FALCOR_ENUM_REGISTER(DiffMode); + +// We will probably add more gradient types in the future. +enum class GradientType : uint32_t +{ + Material, + Geometry, + Count, +}; + +FALCOR_ENUM_INFO( + GradientType, + { + { GradientType::Material, "Material" }, + { GradientType::Geometry, "Geometry" }, + } +); +FALCOR_ENUM_REGISTER(GradientType); + +enum class GradientAggregateMode : uint32_t +{ + Direct, + HashGrid, +}; + +// For debugging differentiable path tracers by visualizing gradient images. + +enum class DiffVariableType : uint32_t +{ + None, + Material, + GeometryTranslation, +}; + +FALCOR_ENUM_INFO( + DiffVariableType, + { + { DiffVariableType::None, "None" }, + { DiffVariableType::Material, "Material" }, + { DiffVariableType::GeometryTranslation, "GeometryTranslation" }, + } +); +FALCOR_ENUM_REGISTER(DiffVariableType); + +struct DiffDebugParams +{ + DiffVariableType varType; + uint2 id; + uint32_t offset; + + float4 grad; +}; + +END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Falcor.h b/Source/Falcor/Falcor.h index b8c254032..c74833ac9 100644 --- a/Source/Falcor/Falcor.h +++ b/Source/Falcor/Falcor.h @@ -30,16 +30,13 @@ // Core #include "Core/Macros.h" #include "Core/Version.h" -#include "Core/FalcorConfig.h" -#include "Core/Assert.h" -#include "Core/ErrorHandling.h" -#include "Core/Errors.h" +#include "Core/Error.h" // Core/Platform #include "Core/Platform/OS.h" // Core/API -#include "Core/API/Common.h" +#include "Core/API/Types.h" #include "Core/API/BlendState.h" #include "Core/API/Buffer.h" #include "Core/API/ComputeContext.h" @@ -50,7 +47,7 @@ #include "Core/API/FBO.h" #include "Core/API/FencedPool.h" #include "Core/API/Formats.h" -#include "Core/API/GpuFence.h" +#include "Core/API/Fence.h" #include "Core/API/GpuTimer.h" #include "Core/API/GraphicsStateObject.h" #include "Core/API/IndirectCommands.h" @@ -74,13 +71,10 @@ #include "Core/Platform/ProgressBar.h" // Core/Program -#include "Core/Program/ComputeProgram.h" -#include "Core/Program/GraphicsProgram.h" #include "Core/Program/Program.h" #include "Core/Program/ProgramReflection.h" #include "Core/Program/ProgramVars.h" #include "Core/Program/ProgramVersion.h" -#include "Core/Program/RtProgram.h" // Core/State #include "Core/State/ComputeState.h" diff --git a/Source/Falcor/FalcorPython.cpp b/Source/Falcor/FalcorPython.cpp index 61b77f1f7..78a9eada8 100644 --- a/Source/Falcor/FalcorPython.cpp +++ b/Source/Falcor/FalcorPython.cpp @@ -53,6 +53,7 @@ PYBIND11_MODULE(falcor_ext, m) { if (!isLoadedFromEmbeddedPython()) { + Falcor::Logger::setOutputs(Falcor::Logger::OutputFlags::Console | Falcor::Logger::OutputFlags::DebugWindow); Falcor::Device::enableAgilitySDK(); Falcor::PluginManager::instance().loadAllPlugins(); } diff --git a/Source/Falcor/GlobalState.cpp b/Source/Falcor/GlobalState.cpp index 0a568b28e..b65b5b066 100644 --- a/Source/Falcor/GlobalState.cpp +++ b/Source/Falcor/GlobalState.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "GlobalState.h" -#include "Core/Errors.h" +#include "Core/Error.h" namespace Falcor { @@ -42,10 +42,15 @@ void setActivePythonSceneBuilder(SceneBuilder* pSceneBuilder) SceneBuilder& accessActivePythonSceneBuilder() { if (!spActivePythonSceneBuilder) - throw RuntimeError("This can only be called in a Python scene building context!"); + FALCOR_THROW("This can only be called in a Python scene building context!"); return *spActivePythonSceneBuilder; } +AssetResolver& getActiveAssetResolver() +{ + return spActivePythonSceneBuilder ? spActivePythonSceneBuilder->getAssetResolver() : AssetResolver::getDefaultResolver(); +} + void setActivePythonRenderGraphDevice(ref pDevice) { spActivePythonRenderGraphDevice = pDevice; @@ -59,7 +64,7 @@ ref getActivePythonRenderGraphDevice() ref accessActivePythonRenderGraphDevice() { if (!spActivePythonRenderGraphDevice) - throw RuntimeError("This can only be called from a script executed in Mogwai or when loading a render graph file!"); + FALCOR_THROW("This can only be called from a script executed in Mogwai or when loading a render graph file!"); return spActivePythonRenderGraphDevice; } diff --git a/Source/Falcor/GlobalState.h b/Source/Falcor/GlobalState.h index a4a95996e..214f5619b 100644 --- a/Source/Falcor/GlobalState.h +++ b/Source/Falcor/GlobalState.h @@ -28,6 +28,7 @@ #pragma once #include "Core/Macros.h" +#include "Core/AssetResolver.h" #include "Core/API/Device.h" #include "Scene/SceneBuilder.h" @@ -46,6 +47,7 @@ namespace Falcor FALCOR_API void setActivePythonSceneBuilder(SceneBuilder* pSceneBuilder); FALCOR_API SceneBuilder& accessActivePythonSceneBuilder(); +FALCOR_API AssetResolver& getActiveAssetResolver(); FALCOR_API void setActivePythonRenderGraphDevice(ref pDevice); FALCOR_API ref getActivePythonRenderGraphDevice(); diff --git a/Source/Falcor/RenderGraph/RenderGraph.cpp b/Source/Falcor/RenderGraph/RenderGraph.cpp index 7940ed35a..5b67a5fa0 100644 --- a/Source/Falcor/RenderGraph/RenderGraph.cpp +++ b/Source/Falcor/RenderGraph/RenderGraph.cpp @@ -108,8 +108,8 @@ ref RenderGraph::createPass(const std::string& passName, const std:: uint32_t RenderGraph::addPass(const ref& pPass, const std::string& passName) { - checkArgument(pPass != nullptr, "Added pass must not be null."); - checkArgument(getPassIndex(passName) == kInvalidIndex, "Pass name '{}' already exists.", passName); + FALCOR_CHECK(pPass != nullptr, "Added pass must not be null."); + FALCOR_CHECK(getPassIndex(passName) == kInvalidIndex, "Pass name '{}' already exists.", passName); uint32_t passIndex = mpGraph->addNode(); mNameToIndex[passName] = passIndex; @@ -127,7 +127,7 @@ uint32_t RenderGraph::addPass(const ref& pPass, const std::string& p void RenderGraph::removePass(const std::string& name) { uint32_t index = getPassIndex(name); - checkArgument(index != kInvalidIndex, "Can't remove render pass '{}'. Pass doesn't exist.", name); + FALCOR_CHECK(index != kInvalidIndex, "Can't remove render pass '{}'. Pass doesn't exist.", name); // Unmark graph outputs that belong to this pass. // Because the way std::vector works, we can't call unmarkOutput() immediately, so we store the outputs in a vector @@ -155,7 +155,7 @@ void RenderGraph::updatePass(const std::string& passName, const Properties& prop uint32_t index = getPassIndex(passName); const auto pPassIt = mNodeData.find(index); - checkArgument(pPassIt != mNodeData.end(), "Can't update render pass '{}'. Pass doesn't exist.", passName); + FALCOR_CHECK(pPassIt != mNodeData.end(), "Can't update render pass '{}'. Pass doesn't exist.", passName); // Recreate pass without changing graph using new dictionary auto pOldPass = pPassIt->second.pPass; @@ -174,7 +174,7 @@ const ref& RenderGraph::getPass(const std::string& name) const { uint32_t index = getPassIndex(name); - checkArgument(index != kInvalidIndex, "Can't find render pass '{}'.", name); + FALCOR_CHECK(index != kInvalidIndex, "Can't find render pass '{}'.", name); return mNodeData.at(index).pPass; } @@ -224,15 +224,14 @@ RenderPass* RenderGraph::getRenderPassAndNamePair( nameAndField = parseFieldName(fullname); RenderPass* pPass = getPass(nameAndField.first).get(); - if (pPass == nullptr) - throw ArgumentError("Can't find render pass '{}'.", nameAndField.first); + FALCOR_CHECK(pPass, "Can't find render pass '{}'.", nameAndField.first); RenderPass::CompileData compileData; compileData.defaultTexDims = mCompilerDeps.defaultResourceProps.dims; compileData.defaultTexFormat = mCompilerDeps.defaultResourceProps.format; if (nameAndField.second.size() && checkRenderPassIoExist(pPass, nameAndField.second, input, compileData) == false) - throw ArgumentError("Can't find field named '{}' in render pass '{}'.", nameAndField.second, nameAndField.first); + FALCOR_THROW("Can't find field named '{}' in render pass '{}'.", nameAndField.second, nameAndField.first); return pPass; } @@ -256,10 +255,11 @@ uint32_t RenderGraph::addEdge(const std::string& src, const std::string& dst) newEdge.dstField = dstPair.second; if (checkMatchingEdgeTypes(newEdge.srcField, newEdge.dstField) == false) - throw ArgumentError( + FALCOR_THROW( "Can't add from '{}' to '{}'. One of the nodes is a resource while the other is a pass. Can't tell if you want a " "data-dependency or an execution-dependency", - src, dst + src, + dst ); uint32_t srcIndex = mNameToIndex[srcPair.first]; @@ -277,7 +277,7 @@ uint32_t RenderGraph::addEdge(const std::string& src, const std::string& dst) if (edgeData.dstField == newEdge.dstField) { - throw ArgumentError( + FALCOR_THROW( "Edge destination '{}' is already initialized. Please remove the existing connection before trying to add a new edge.", dst ); @@ -287,9 +287,7 @@ uint32_t RenderGraph::addEdge(const std::string& src, const std::string& dst) // Make sure that this doesn't create a cycle if (DirectedGraphPathDetector::hasPath(*mpGraph, dstIndex, srcIndex)) - throw ArgumentError( - "Can't add the edge from '{}' to '{}'. The edge will create a cycle in the graph which is not allowed.", src, dst - ); + FALCOR_THROW("Can't add the edge from '{}' to '{}'. The edge will create a cycle in the graph which is not allowed.", src, dst); uint32_t e = mpGraph->addEdge(srcIndex, dstIndex); mEdgeData[e] = newEdge; @@ -323,7 +321,7 @@ void RenderGraph::removeEdge(const std::string& src, const std::string& dst) void RenderGraph::removeEdge(uint32_t edgeID) { - checkArgument(mEdgeData.find(edgeID) != mEdgeData.end(), "Can't remove edge with index {}. The edge doesn't exist.", edgeID); + FALCOR_CHECK(mEdgeData.find(edgeID) != mEdgeData.end(), "Can't remove edge with index {}. The edge doesn't exist.", edgeID); mEdgeData.erase(edgeID); mpGraph->removeEdge(edgeID); @@ -421,7 +419,7 @@ void RenderGraph::execute(RenderContext* pRenderContext) { std::string log; if (!compile(pRenderContext, log)) - throw RuntimeError("Failed to compile render graph:\n{}", log); + FALCOR_THROW("Failed to compile render graph:\n{}", log); FALCOR_ASSERT(mpExe); RenderGraphExe::Context c{ @@ -513,7 +511,7 @@ void RenderGraph::setInput(const std::string& name, const ref& pResour { if (mCompilerDeps.externalResources.find(name) == mCompilerDeps.externalResources.end()) { - throw ArgumentError("Trying to remove an external resource named '{}' but the resource wasn't registered before.", name); + FALCOR_THROW("Trying to remove an external resource named '{}' but the resource wasn't registered before.", name); } mCompilerDeps.externalResources.erase(name); } @@ -524,7 +522,7 @@ void RenderGraph::setInput(const std::string& name, const ref& pResour void RenderGraph::markOutput(const std::string& name, TextureChannelFlags mask) { - checkArgument(mask != TextureChannelFlags::None, "Mask must be non-empty"); + FALCOR_CHECK(mask != TextureChannelFlags::None, "Mask must be non-empty"); // Recursive call to handle '*' wildcard. if (name == "*") @@ -588,7 +586,7 @@ bool RenderGraph::isGraphOutput(const std::string& name) const ref RenderGraph::getOutput(const std::string& name) { if (mRecompile) - throw RuntimeError("Can't fetch the output '{}'. The graph wasn't successfuly compiled yet.", name); + FALCOR_THROW("Can't fetch the output '{}'. The graph wasn't successfuly compiled yet.", name); str_pair strPair; getRenderPassAndNamePair(false, name, strPair); @@ -597,7 +595,7 @@ ref RenderGraph::getOutput(const std::string& name) GraphOut thisOutput = {passIndex, strPair.second}; bool isOutput = isGraphOutput(thisOutput); if (!isOutput) - throw RuntimeError("Can't fetch the output '{}'. The resource is wasn't marked as an output.", name); + FALCOR_THROW("Can't fetch the output '{}'. The resource is wasn't marked as an output.", name); return mpExe->getResource(name); } @@ -626,7 +624,7 @@ void RenderGraph::onResize(const Fbo* pTargetFbo) // Store the back-buffer values const Texture* pColor = pTargetFbo ? pTargetFbo->getColorTexture(0).get() : nullptr; if (pColor == nullptr) - throw RuntimeError("Can't resize render graph without a frame buffer."); + FALCOR_THROW("Can't resize render graph without a frame buffer."); // Store the values mCompilerDeps.defaultResourceProps.format = pColor->getFormat(); @@ -710,12 +708,16 @@ FALCOR_SCRIPT_BINDING(RenderGraph) "create_pass", [](RenderGraph& graph, const std::string& pass_name, const std::string& pass_type, pybind11::dict dict = {}) { return graph.createPass(pass_name, pass_type, Properties(dict)); }, - "pass_name"_a, "pass_type"_a, "dict"_a = pybind11::dict() + "pass_name"_a, + "pass_type"_a, + "dict"_a = pybind11::dict() ); renderGraph.def("remove_pass", &RenderGraph::removePass, "name"_a); renderGraph.def( - "update_pass", [](RenderGraph& graph, const std::string& name, pybind11::dict dict) { graph.updatePass(name, Properties(dict)); }, - "name"_a, "dict"_a + "update_pass", + [](RenderGraph& graph, const std::string& name, pybind11::dict dict) { graph.updatePass(name, Properties(dict)); }, + "name"_a, + "dict"_a ); renderGraph.def("add_edge", &RenderGraph::addEdge, "src"_a, "dst"_a); renderGraph.def( @@ -724,6 +726,7 @@ FALCOR_SCRIPT_BINDING(RenderGraph) renderGraph.def("mark_output", &RenderGraph::markOutput, "name"_a, "mask"_a = TextureChannelFlags::RGB); renderGraph.def("unmark_output", &RenderGraph::unmarkOutput, "name"_a); renderGraph.def("get_pass", &RenderGraph::getPass, "name"_a); + renderGraph.def("__getitem__", [](RenderGraph& self, const std::string& name) { return self.getPass(name); }); renderGraph.def("get_output", pybind11::overload_cast(&RenderGraph::getOutput), "name"_a); // PYTHONDEPRECATED BEGIN @@ -732,7 +735,8 @@ FALCOR_SCRIPT_BINDING(RenderGraph) ); renderGraph.def_static( "createFromFile", - [](const std::filesystem::path& path) { return RenderGraph::createFromFile(accessActivePythonRenderGraphDevice(), path); }, "path"_a + [](const std::filesystem::path& path) { return RenderGraph::createFromFile(accessActivePythonRenderGraphDevice(), path); }, + "path"_a ); renderGraph.def("print", [](ref graph) { pybind11::print(RenderGraphExporter::getIR(graph)); }); @@ -741,13 +745,17 @@ FALCOR_SCRIPT_BINDING(RenderGraph) "createPass", [](RenderGraph& graph, const std::string& passName, const std::string& passType, pybind11::dict dict = {}) { return graph.createPass(passName, passType, Properties(dict)); }, - "passName"_a, "passType"_a, "dict"_a = pybind11::dict() + "passName"_a, + "passType"_a, + "dict"_a = pybind11::dict() ); renderGraph.def("addPass", &RenderGraph::addPass, "pass_"_a, "name"_a); renderGraph.def("removePass", &RenderGraph::removePass, "name"_a); renderGraph.def( - "updatePass", [](RenderGraph& graph, const std::string& passName, pybind11::dict d) { graph.updatePass(passName, Properties(d)); }, - "name"_a, "dict"_a + "updatePass", + [](RenderGraph& graph, const std::string& passName, pybind11::dict d) { graph.updatePass(passName, Properties(d)); }, + "name"_a, + "dict"_a ); renderGraph.def("addEdge", &RenderGraph::addEdge, "src"_a, "dst"_a); renderGraph.def( @@ -764,7 +772,7 @@ FALCOR_SCRIPT_BINDING(RenderGraph) { auto pPass = RenderPass::create(type, accessActivePythonRenderGraphDevice(), Properties(d)); if (!pPass) - throw RuntimeError("Can't create a render pass of type '{}'. Make sure the required plugin library was loaded.", type); + FALCOR_THROW("Can't create a render pass of type '{}'. Make sure the required plugin library was loaded.", type); return pPass; }; m.def("createPass", globalCreateRenderPass, "type"_a, "dict"_a = pybind11::dict()); // PYTHONDEPRECATED diff --git a/Source/Falcor/RenderGraph/RenderGraph.h b/Source/Falcor/RenderGraph/RenderGraph.h index 10b1db8a9..e5a174761 100644 --- a/Source/Falcor/RenderGraph/RenderGraph.h +++ b/Source/Falcor/RenderGraph/RenderGraph.h @@ -70,7 +70,7 @@ class FALCOR_API RenderGraph : public Object /** * Create a render graph from loading a python render graph script. * @param[in] pDevice GPU device. - * @param[in] path Path to the script. + * @param[in] path Path to the script (absolute or relative to working directory). * @return New object, or throws an exception if creation failed. */ static ref createFromFile(ref pDevice, const std::filesystem::path& path); @@ -283,7 +283,7 @@ class FALCOR_API RenderGraph : public Object /** * Get the dictionary objects used to communicate app data to the render passes. */ - InternalDictionary& getPassesDictionary() { return mPassesDictionary; } + Dictionary& getPassesDictionary() { return mPassesDictionary; } /** * Get the graph name. @@ -368,7 +368,7 @@ class FALCOR_API RenderGraph : public Object std::unordered_map mNodeData; ///< Map from node ID to render pass name and ptr. std::vector mOutputs; ///< Array of all outputs marked as graph outputs. GRAPH_TODO should this be an unordered set? - InternalDictionary mPassesDictionary; ///< Dictionary used to communicate between passes. + Dictionary mPassesDictionary; ///< Dictionary used to communicate between passes. std::unique_ptr mpExe; ///< Helper for allocating resources and executing the graph. RenderGraphCompiler::Dependencies mCompilerDeps; ///< Data needed by the graph compiler. bool mRecompile = false; ///< Set to true to trigger a recompilation after any graph changes (topology/scene/size/passes/etc.) diff --git a/Source/Falcor/RenderGraph/RenderGraphCompiler.cpp b/Source/Falcor/RenderGraph/RenderGraphCompiler.cpp index 04586180f..e0147525c 100644 --- a/Source/Falcor/RenderGraph/RenderGraphCompiler.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphCompiler.cpp @@ -28,6 +28,7 @@ #include "RenderGraphCompiler.h" #include "RenderGraph.h" #include "RenderPasses/ResolvePass.h" +#include "Core/Error.h" #include "Utils/Algorithm/DirectedGraphTraversal.h" #include "Utils/StringUtils.h" @@ -115,7 +116,7 @@ void RenderGraphCompiler::validateGraph() const err += "Graph must have at least one output.\n"; if (err.size()) - throw RuntimeError(err); + FALCOR_THROW(err); } void RenderGraphCompiler::resolveExecutionOrder() @@ -397,8 +398,13 @@ RenderPass::CompileData RenderGraphCompiler::prepPassCompilationData(const PassD compileData.connectedResources.addInput(resName, "External input resource") .format(pTex->getFormat()) .resourceType( - resourceTypeToFieldType(pTex->getType()), pTex->getWidth(), pTex->getHeight(), pTex->getDepth(), pTex->getSampleCount(), - pTex->getMipCount(), pTex->getArraySize() + resourceTypeToFieldType(pTex->getType()), + pTex->getWidth(), + pTex->getHeight(), + pTex->getDepth(), + pTex->getSampleCount(), + pTex->getMipCount(), + pTex->getArraySize() ); } } @@ -468,11 +474,7 @@ void RenderGraphCompiler::compilePasses(RenderContext* pRenderContext) } } - if (!changed) - { - reportError("Graph compilation failed.\n" + log); - return; - } + FALCOR_CHECK(changed, "Graph compilation failed:\n{}", log); } } } // namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderGraphExe.h b/Source/Falcor/RenderGraph/RenderGraphExe.h index 008fbdfb6..a1372852b 100644 --- a/Source/Falcor/RenderGraph/RenderGraphExe.h +++ b/Source/Falcor/RenderGraph/RenderGraphExe.h @@ -33,7 +33,7 @@ #include "Core/API/Formats.h" #include "Utils/Math/Vector.h" #include "Utils/UI/Gui.h" -#include "Utils/InternalDictionary.h" +#include "Utils/Dictionary.h" #include #include #include @@ -49,7 +49,7 @@ class FALCOR_API RenderGraphExe struct Context { RenderContext* pRenderContext; - InternalDictionary& passesDictionary; + Dictionary& passesDictionary; uint2 defaultTexDims; ResourceFormat defaultTexFormat; }; diff --git a/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp b/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp index 089c2e806..ad9db855d 100644 --- a/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphImportExport.cpp @@ -27,6 +27,7 @@ **************************************************************************/ #include "RenderGraphImportExport.h" #include "RenderGraphIR.h" +#include "Core/AssetResolver.h" #include "Utils/Scripting/Scripting.h" #include @@ -43,16 +44,12 @@ void updateGraphStrings(std::string& graph, std::filesystem::path& path, std::st void runScriptFile(const std::filesystem::path& path, const std::string& custom) { - std::filesystem::path fullPath; - if (findFileInDataDirectories(path, fullPath)) - { - std::string script = readFile(fullPath) + custom; - Scripting::runScript(script); - } - else - { - throw RuntimeError("Can't find the file '{}'", path); - } + std::filesystem::path resolvedPath = AssetResolver::getDefaultResolver().resolvePath(path); + if (resolvedPath.empty()) + FALCOR_THROW("Can't find the file '{}'", path); + + std::string script = readFile(resolvedPath) + custom; + Scripting::runScript(script); } } // namespace diff --git a/Source/Falcor/RenderGraph/RenderGraphUI.cpp b/Source/Falcor/RenderGraph/RenderGraphUI.cpp index 5dc95f254..a3a2f374a 100644 --- a/Source/Falcor/RenderGraph/RenderGraphUI.cpp +++ b/Source/Falcor/RenderGraph/RenderGraphUI.cpp @@ -1202,8 +1202,10 @@ void RenderGraphUI::updatePins(bool addLinks) for (const auto& connectedPin : (inputPins->second)) { if (!mpNodeGraphEditor->isLinkPresent( - mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID), currentPinUI.mGuiPinID, - mpNodeGraphEditor->getNodeFromID(connectedPin.second), connectedPin.first + mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID), + currentPinUI.mGuiPinID, + mpNodeGraphEditor->getNodeFromID(connectedPin.second), + connectedPin.first )) { RenderGraphNode* pNode = static_cast(mpNodeGraphEditor->getNodeFromID(connectedPin.second)); @@ -1229,8 +1231,12 @@ void RenderGraphUI::updatePins(bool addLinks) } mpNodeGraphEditor->addLinkFromGraph( - mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID), currentPinUI.mGuiPinID, - mpNodeGraphEditor->getNodeFromID(connectedPin.second), connectedPin.first, false, edgeColor + mpNodeGraphEditor->getNodeFromID(currentPassUI.mGuiNodeID), + currentPinUI.mGuiPinID, + mpNodeGraphEditor->getNodeFromID(connectedPin.second), + connectedPin.first, + false, + edgeColor ); RenderGraphNode* pDstGraphNode = @@ -1278,8 +1284,10 @@ void RenderGraphUI::updatePins(bool addLinks) uint32_t inputPinID = connectedNodeUI.mNameToIndexOutput.find(currentPinUI.mConnectedPinName)->second; if (!mpNodeGraphEditor->isLinkPresent( - mpNodeGraphEditor->getNodeFromID(connectedNodeUI.mGuiNodeID), inputPinID, - mpNodeGraphEditor->getNodeFromID(inputIDs.second), inputIDs.first + mpNodeGraphEditor->getNodeFromID(connectedNodeUI.mGuiNodeID), + inputPinID, + mpNodeGraphEditor->getNodeFromID(inputIDs.second), + inputIDs.first )) { auto edgeIt = mInputPinStringToLinkID.find(currentPass.first + "." + currentPinName); @@ -1288,7 +1296,8 @@ void RenderGraphUI::updatePins(bool addLinks) removeEdge( mpNodeGraphEditor->getNodeFromID(connectedNodeUI.mGuiNodeID)->getName(), - mpNodeGraphEditor->getNodeFromID(inputIDs.second)->getName(), mpRenderGraph->mEdgeData[edgeID].srcField, + mpNodeGraphEditor->getNodeFromID(inputIDs.second)->getName(), + mpRenderGraph->mEdgeData[edgeID].srcField, mpRenderGraph->mEdgeData[edgeID].dstField ); mpRenderGraph->removeEdge(edgeID); diff --git a/Source/Falcor/RenderGraph/RenderPass.cpp b/Source/Falcor/RenderGraph/RenderPass.cpp index 734d1350d..1ad2270d2 100644 --- a/Source/Falcor/RenderGraph/RenderPass.cpp +++ b/Source/Falcor/RenderGraph/RenderPass.cpp @@ -32,7 +32,7 @@ namespace Falcor RenderData::RenderData( const std::string& passName, ResourceCache& resources, - InternalDictionary& dictionary, + Dictionary& dictionary, const uint2& defaultTexDims, ResourceFormat defaultTexFormat ) diff --git a/Source/Falcor/RenderGraph/RenderPass.h b/Source/Falcor/RenderGraph/RenderPass.h index 9c0efd144..3bc89a748 100644 --- a/Source/Falcor/RenderGraph/RenderPass.h +++ b/Source/Falcor/RenderGraph/RenderPass.h @@ -35,7 +35,7 @@ #include "Core/API/Texture.h" #include "Scene/Scene.h" #include "Utils/Properties.h" -#include "Utils/InternalDictionary.h" +#include "Utils/Dictionary.h" #include "Utils/Scripting/ScriptBindings.h" #include "Utils/UI/Gui.h" #include @@ -75,7 +75,7 @@ class FALCOR_API RenderData /** * Get the global dictionary. You can use it to pass data between different passes */ - InternalDictionary& getDictionary() const { return mDictionary; } + Dictionary& getDictionary() const { return mDictionary; } /** * Get the default dimensions used for Texture2Ds (when `0` is specified as the dimensions in `RenderPassReflection`) @@ -91,14 +91,14 @@ class FALCOR_API RenderData RenderData( const std::string& passName, ResourceCache& resources, - InternalDictionary& dictionary, + Dictionary& dictionary, const uint2& defaultTexDims, ResourceFormat defaultTexFormat ); const std::string& mName; ResourceCache& mResources; - InternalDictionary& mDictionary; + Dictionary& mDictionary; uint2 mDefaultTexDims; ResourceFormat mDefaultTexFormat; diff --git a/Source/Falcor/RenderGraph/RenderPassHelpers.cpp b/Source/Falcor/RenderGraph/RenderPassHelpers.cpp index ce8eba8db..c26d16983 100644 --- a/Source/Falcor/RenderGraph/RenderPassHelpers.cpp +++ b/Source/Falcor/RenderGraph/RenderPassHelpers.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "RenderPassHelpers.h" #include "RenderPass.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/Scripting/ScriptBindings.h" namespace Falcor diff --git a/Source/Falcor/RenderGraph/RenderPassReflection.cpp b/Source/Falcor/RenderGraph/RenderPassReflection.cpp index b726ab87e..cf1dfce7a 100644 --- a/Source/Falcor/RenderGraph/RenderPassReflection.cpp +++ b/Source/Falcor/RenderGraph/RenderPassReflection.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "RenderPassReflection.h" +#include "Core/Error.h" #include "Utils/Logger.h" #include @@ -137,7 +138,7 @@ RenderPassReflection::Field& RenderPassReflection::Field::resourceType( logWarning("RenderPassReflection::Field::resourceType - depth, sampleCount for {} must be either 0 or 1.", to_string(type)); return textureCube(width, height, mipCount, arraySize); default: - throw RuntimeError("RenderPassReflection::Field::resourceType - {} is not a valid Field type", to_string(type)); + FALCOR_THROW("RenderPassReflection::Field::resourceType - {} is not a valid Field type", to_string(type)); } } @@ -146,7 +147,7 @@ RenderPassReflection::Field& RenderPassReflection::Field::format(ResourceFormat mFormat = f; return *this; } -RenderPassReflection::Field& RenderPassReflection::Field::bindFlags(Resource::BindFlags flags) +RenderPassReflection::Field& RenderPassReflection::Field::bindFlags(ResourceBindFlags flags) { mBindFlags = flags; return *this; @@ -176,15 +177,13 @@ bool RenderPassReflection::Field::isValid() const { if (mSampleCount > 1 && mMipCount > 1) { - reportError( - "Trying to create a multisampled RenderPassReflection::Field '" + mName + "' with mip-count larger than 1. This is illegal." - ); + logError("Trying to create a multisampled RenderPassReflection::Field '{}' with mip-count larger than 1. This is illegal.", mName); return false; } if (is_set(mVisibility, Visibility::Internal) && is_set(mFlags, Flags::Optional)) { - reportError("Internal resource can't be optional, since there will never be a graph edge that forces their creation"); + logError("Internal resource can't be optional, since there will never be a graph edge that forces their creation"); return false; } @@ -207,7 +206,7 @@ RenderPassReflection::Field& RenderPassReflection::addField(const Field& field) } else if ((existingF.getVisibility() & field.getVisibility()) != field.getVisibility()) { - reportError( + logWarning( "Trying to add an existing field '" + field.getName() + "' to RenderPassReflection, but the visibility flags mismatch. Overriding the previous definition" ); @@ -270,7 +269,7 @@ RenderPassReflection::Field& RenderPassReflection::Field::merge(const RenderPass auto err = [&](const std::string& msg) { const std::string s = "Can't merge RenderPassReflection::Fields. base(" + getName() + "), newField(" + other.getName() + "). "; - throw RuntimeError(s + msg); + FALCOR_THROW(s + msg); }; if (mType != other.mType) diff --git a/Source/Falcor/RenderGraph/RenderPassReflection.h b/Source/Falcor/RenderGraph/RenderPassReflection.h index d21025f48..6e285e6b6 100644 --- a/Source/Falcor/RenderGraph/RenderPassReflection.h +++ b/Source/Falcor/RenderGraph/RenderPassReflection.h @@ -27,7 +27,7 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/API/Formats.h" #include "Core/API/Resource.h" #include "Core/API/Texture.h" @@ -214,7 +214,7 @@ inline RenderPassReflection::Field::Type resourceTypeToFieldType(Resource::Type case Resource::Type::TextureCube: return RenderPassReflection::Field::Type::TextureCube; default: - throw RuntimeError("resourceTypeToFieldType - No RenderPassReflection::Field::Type exists for Resource::Type::{}", to_string(t)); + FALCOR_THROW("resourceTypeToFieldType - No RenderPassReflection::Field::Type exists for Resource::Type::{}", to_string(t)); } } } // namespace Falcor diff --git a/Source/Falcor/RenderGraph/ResourceCache.cpp b/Source/Falcor/RenderGraph/ResourceCache.cpp index 06cb5ec08..1ca15978e 100644 --- a/Source/Falcor/RenderGraph/ResourceCache.cpp +++ b/Source/Falcor/RenderGraph/ResourceCache.cpp @@ -97,7 +97,7 @@ void ResourceCache::registerField( bool addAlias = (alias.empty() == false); if (addAlias && mNameToIndex.count(alias) == 0) { - throw RuntimeError("Field named '{}' not found. Cannot register '{}' as an alias.", alias, name); + FALCOR_THROW("Field named '{}' not found. Cannot register '{}' as an alias.", alias, name); } // Add a new field @@ -142,11 +142,11 @@ inline ref createResourceForPass( format = field.getFormat() == ResourceFormat::Unknown ? params.format : field.getFormat(); if (resolveBindFlags) { - ResourceBindFlags mask = Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource; + ResourceBindFlags mask = ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource; bool isOutput = is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Output); bool isInternal = is_set(field.getVisibility(), RenderPassReflection::Field::Visibility::Internal); if (isOutput || isInternal) - mask |= Resource::BindFlags::DepthStencil | Resource::BindFlags::RenderTarget; + mask |= ResourceBindFlags::DepthStencil | ResourceBindFlags::RenderTarget; auto supported = pDevice->getFormatBindFlags(format); mask &= supported; bindFlags |= mask; @@ -155,33 +155,33 @@ inline ref createResourceForPass( else // RawBuffer { if (resolveBindFlags) - bindFlags = Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource; + bindFlags = ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource; } ref pResource; switch (field.getType()) { case RenderPassReflection::Field::Type::RawBuffer: - pResource = Buffer::create(pDevice, width, bindFlags, Buffer::CpuAccess::None); + pResource = pDevice->createBuffer(width, bindFlags, MemoryType::DeviceLocal); break; case RenderPassReflection::Field::Type::Texture1D: - pResource = Texture::create1D(pDevice, width, format, arraySize, mipLevels, nullptr, bindFlags); + pResource = pDevice->createTexture1D(width, format, arraySize, mipLevels, nullptr, bindFlags); break; case RenderPassReflection::Field::Type::Texture2D: if (sampleCount > 1) { - pResource = Texture::create2DMS(pDevice, width, height, format, sampleCount, arraySize, bindFlags); + pResource = pDevice->createTexture2DMS(width, height, format, sampleCount, arraySize, bindFlags); } else { - pResource = Texture::create2D(pDevice, width, height, format, arraySize, mipLevels, nullptr, bindFlags); + pResource = pDevice->createTexture2D(width, height, format, arraySize, mipLevels, nullptr, bindFlags); } break; case RenderPassReflection::Field::Type::Texture3D: - pResource = Texture::create3D(pDevice, width, height, depth, format, mipLevels, nullptr, bindFlags); + pResource = pDevice->createTexture3D(width, height, depth, format, mipLevels, nullptr, bindFlags); break; case RenderPassReflection::Field::Type::TextureCube: - pResource = Texture::createCube(pDevice, width, height, format, arraySize, mipLevels, nullptr, bindFlags); + pResource = pDevice->createTextureCube(width, height, format, arraySize, mipLevels, nullptr, bindFlags); break; default: FALCOR_UNREACHABLE(); diff --git a/Source/Falcor/RenderPasses/Shared/Denoising/NRDHelpers.slang b/Source/Falcor/RenderPasses/Shared/Denoising/NRDHelpers.slang index 639669c06..f122e7674 100644 --- a/Source/Falcor/RenderPasses/Shared/Denoising/NRDHelpers.slang +++ b/Source/Falcor/RenderPasses/Shared/Denoising/NRDHelpers.slang @@ -81,7 +81,7 @@ bool isDeltaReflectionAllowedAlongDeltaTransmissionPath(const ShadingData sd) const float metallic = md.specular.b; // Blue component stores metallic in MetalRough mode. const float insideIoR = gScene.materials.evalIoR(sd.materialID); - const float eta = sd.frontFacing ? (sd.IoR / insideIoR) : (insideIoR / sd.IoR); + const float eta = sd.IoR / insideIoR; bool totalInternalReflection = evalFresnelDielectric(eta, sd.frame.toLocal(sd.V).z) == 1.f; bool nonTransmissiveMirror = (md.specularTransmission == 0.f) && (metallic == 1.f); diff --git a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.cpp b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.cpp index ab1a5caf4..edfa62b15 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.cpp +++ b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.h b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.h index c75af5d77..b497330da 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.h +++ b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.h @@ -59,7 +59,7 @@ namespace Falcor /** Bind the light sampler data to a given shader var */ - virtual void setShaderData(const ShaderVar& var) const {} + virtual void bindShaderData(const ShaderVar& var) const {} /** Render the GUI. \return True if settings that affect the rendering have changed. diff --git a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.slang b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.slang index 5cd16132c..640db6f7e 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveLightSampler.slang +++ b/Source/Falcor/Rendering/Lights/EmissiveLightSampler.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Rendering/Lights/EmissiveLightSamplerType.slangh b/Source/Falcor/Rendering/Lights/EmissiveLightSamplerType.slangh index 3af57720c..b8642c8bb 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveLightSamplerType.slangh +++ b/Source/Falcor/Rendering/Lights/EmissiveLightSamplerType.slangh @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Rendering/Lights/EmissivePowerSampler.cpp b/Source/Falcor/Rendering/Lights/EmissivePowerSampler.cpp index db0cd1f62..2fece4622 100644 --- a/Source/Falcor/Rendering/Lights/EmissivePowerSampler.cpp +++ b/Source/Falcor/Rendering/Lights/EmissivePowerSampler.cpp @@ -63,7 +63,7 @@ namespace Falcor return samplerChanged; } - void EmissivePowerSampler::setShaderData(const ShaderVar& var) const + void EmissivePowerSampler::bindShaderData(const ShaderVar& var) const { FALCOR_ASSERT(var.isValid()); @@ -162,7 +162,7 @@ namespace Falcor { float(sum), N, - Buffer::createTyped(mpScene->getDevice(), N), + mpScene->getDevice()->createTypedBuffer(N), }; result.fullTable->setBlob(&fullTable[0], 0, N * sizeof(uint2)); diff --git a/Source/Falcor/Rendering/Lights/EmissivePowerSampler.h b/Source/Falcor/Rendering/Lights/EmissivePowerSampler.h index 11a5fb0dd..bbeb4d197 100644 --- a/Source/Falcor/Rendering/Lights/EmissivePowerSampler.h +++ b/Source/Falcor/Rendering/Lights/EmissivePowerSampler.h @@ -66,7 +66,7 @@ namespace Falcor /** Bind the light sampler data to a given shader variable. \param[in] var Shader variable. */ - virtual void setShaderData(const ShaderVar& var) const override; + virtual void bindShaderData(const ShaderVar& var) const override; protected: /** Generate an alias table diff --git a/Source/Falcor/Rendering/Lights/EmissivePowerSampler.slang b/Source/Falcor/Rendering/Lights/EmissivePowerSampler.slang index a00a0482b..60e30c3fe 100644 --- a/Source/Falcor/Rendering/Lights/EmissivePowerSampler.slang +++ b/Source/Falcor/Rendering/Lights/EmissivePowerSampler.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -60,6 +60,8 @@ struct EmissivePowerSampler : IEmissiveLightSampler */ bool sampleLight(const float3 posW, const float3 normalW, const bool upperHemisphere, inout S sg, out TriangleLightSample ls) { + ls = {}; + if (gScene.lightCollection.isEmpty()) return false; // Randomly pick a triangle out of the global list with uniform probability. diff --git a/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.slang b/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.slang index f81a5aa44..cd073b208 100644 --- a/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.slang +++ b/Source/Falcor/Rendering/Lights/EmissiveUniformSampler.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -54,6 +54,8 @@ struct EmissiveUniformSampler : IEmissiveLightSampler */ bool sampleLight(const float3 posW, const float3 normalW, const bool upperHemisphere, inout S sg, out TriangleLightSample ls) { + ls = {}; + if (gScene.lightCollection.getActiveTriangleCount() == 0) return false; // Randomly pick a triangle out of the global list of active triangles with uniform probability. diff --git a/Source/Falcor/Rendering/Lights/EnvMapSampler.cpp b/Source/Falcor/Rendering/Lights/EnvMapSampler.cpp index 306bec006..39b31d0a0 100644 --- a/Source/Falcor/Rendering/Lights/EnvMapSampler.cpp +++ b/Source/Falcor/Rendering/Lights/EnvMapSampler.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "EnvMapSampler.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/API/RenderContext.h" #include "Core/Pass/ComputePass.h" @@ -52,18 +52,18 @@ namespace Falcor // Create sampler. Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - samplerDesc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - mpImportanceSampler = Sampler::create(mpDevice, samplerDesc); + samplerDesc.setFilterMode(TextureFilteringMode::Point, TextureFilteringMode::Point, TextureFilteringMode::Point); + samplerDesc.setAddressingMode(TextureAddressingMode::Clamp, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); + mpImportanceSampler = mpDevice->createSampler(samplerDesc); // Create hierarchical importance map for sampling. if (!createImportanceMap(mpDevice->getRenderContext(), kDefaultDimension, kDefaultSpp)) { - throw RuntimeError("Failed to create importance map"); + FALCOR_THROW("Failed to create importance map"); } } - void EnvMapSampler::setShaderData(const ShaderVar& var) const + void EnvMapSampler::bindShaderData(const ShaderVar& var) const { FALCOR_ASSERT(var.isValid()); @@ -88,7 +88,7 @@ namespace Falcor FALCOR_ASSERT(mips > 1 && mips <= 12); // Shader constant limits max resolution, increase if needed. // Create importance map. We have to set the RTV flag to be able to use generateMips(). - mpImportanceMap = Texture::create2D(mpDevice, dimension, dimension, ResourceFormat::R32Float, 1, mips, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::RenderTarget | Resource::BindFlags::UnorderedAccess); + mpImportanceMap = mpDevice->createTexture2D(dimension, dimension, ResourceFormat::R32Float, 1, mips, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::RenderTarget | ResourceBindFlags::UnorderedAccess); FALCOR_ASSERT(mpImportanceMap); auto var = mpSetupPass->getRootVar(); diff --git a/Source/Falcor/Rendering/Lights/EnvMapSampler.h b/Source/Falcor/Rendering/Lights/EnvMapSampler.h index 2e0a97289..8270cde71 100644 --- a/Source/Falcor/Rendering/Lights/EnvMapSampler.h +++ b/Source/Falcor/Rendering/Lights/EnvMapSampler.h @@ -52,7 +52,7 @@ namespace Falcor /** Bind the environment map sampler to a given shader variable. \param[in] var Shader variable. */ - void setShaderData(const ShaderVar& var) const; + void bindShaderData(const ShaderVar& var) const; const ref& getEnvMap() const { return mpEnvMap; } diff --git a/Source/Falcor/Rendering/Lights/LightBVH.cpp b/Source/Falcor/Rendering/Lights/LightBVH.cpp index ee74951b5..61121367c 100644 --- a/Source/Falcor/Rendering/Lights/LightBVH.cpp +++ b/Source/Falcor/Rendering/Lights/LightBVH.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "LightBVH.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/API/RenderContext.h" #include "Utils/Timing/Profiler.h" @@ -55,8 +55,8 @@ namespace Falcor // Update all leaf nodes. { auto var = mLeafUpdater->getRootVar()["CB"]; - mpLightCollection->setShaderData(var["gLights"]); - setShaderData(var["gLightBVH"]); + mpLightCollection->bindShaderData(var["gLights"]); + bindShaderData(var["gLightBVH"]); var["gNodeIndices"] = mpNodeIndicesBuffer; const uint32_t nodeCount = mPerDepthRefitEntryInfo.back().count; @@ -70,8 +70,8 @@ namespace Falcor // Update all internal nodes. { auto var = mInternalUpdater->getRootVar()["CB"]; - mpLightCollection->setShaderData(var["gLights"]); - setShaderData(var["gLightBVH"]); + mpLightCollection->bindShaderData(var["gLights"]); + bindShaderData(var["gLightBVH"]); var["gNodeIndices"] = mpNodeIndicesBuffer; // Note that mBVHStats.treeHeight may be 0, in which case there is a single leaf and no internal nodes. @@ -262,7 +262,7 @@ namespace Falcor if (!mpNodeIndicesBuffer || mpNodeIndicesBuffer->getElementCount() < mNodeIndices.size()) { - mpNodeIndicesBuffer = Buffer::createStructured(mpDevice, sizeof(uint32_t), (uint32_t)mNodeIndices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpNodeIndicesBuffer = mpDevice->createStructuredBuffer(sizeof(uint32_t), (uint32_t)mNodeIndices.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpNodeIndicesBuffer->setName("LightBVH::mpNodeIndicesBuffer"); } @@ -275,17 +275,17 @@ namespace Falcor auto var = mLeafUpdater->getRootVar()["CB"]["gLightBVH"]; if (!mpBVHNodesBuffer || mpBVHNodesBuffer->getElementCount() < mNodes.size()) { - mpBVHNodesBuffer = Buffer::createStructured(mpDevice, var["nodes"], (uint32_t)mNodes.size(), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpBVHNodesBuffer = mpDevice->createStructuredBuffer(var["nodes"], (uint32_t)mNodes.size(), ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); mpBVHNodesBuffer->setName("LightBVH::mpBVHNodesBuffer"); } if (!mpTriangleIndicesBuffer || mpTriangleIndicesBuffer->getElementCount() < triangleIndices.size()) { - mpTriangleIndicesBuffer = Buffer::createStructured(mpDevice, var["triangleIndices"], (uint32_t)triangleIndices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpTriangleIndicesBuffer = mpDevice->createStructuredBuffer(var["triangleIndices"], (uint32_t)triangleIndices.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpTriangleIndicesBuffer->setName("LightBVH::mpTriangleIndicesBuffer"); } if (!mpTriangleBitmasksBuffer || mpTriangleBitmasksBuffer->getElementCount() < triangleBitmasks.size()) { - mpTriangleBitmasksBuffer = Buffer::createStructured(mpDevice, var["triangleBitmasks"], (uint32_t)triangleBitmasks.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpTriangleBitmasksBuffer = mpDevice->createStructuredBuffer(var["triangleBitmasks"], (uint32_t)triangleBitmasks.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpTriangleBitmasksBuffer->setName("LightBVH::mpTriangleBitmasksBuffer"); } @@ -309,14 +309,12 @@ namespace Falcor // TODO: This is slow because of the flush. We should copy to a staging buffer // after the data is updated on the GPU and map the staging buffer here instead. - const void* const ptr = mpBVHNodesBuffer->map(Buffer::MapType::Read); FALCOR_ASSERT(mNodes.size() > 0 && mNodes.size() <= mpBVHNodesBuffer->getElementCount()); - std::memcpy(mNodes.data(), ptr, mNodes.size() * sizeof(mNodes[0])); - mpBVHNodesBuffer->unmap(); + mpBVHNodesBuffer->getBlob(mNodes.data(), 0, mNodes.size() * sizeof(PackedNode));; mIsCpuDataValid = true; } - void LightBVH::setShaderData(const ShaderVar& var) const + void LightBVH::bindShaderData(const ShaderVar& var) const { if (isValid()) { diff --git a/Source/Falcor/Rendering/Lights/LightBVH.h b/Source/Falcor/Rendering/Lights/LightBVH.h index 8204e28c3..a1a458d75 100644 --- a/Source/Falcor/Rendering/Lights/LightBVH.h +++ b/Source/Falcor/Rendering/Lights/LightBVH.h @@ -50,7 +50,7 @@ namespace Falcor The data can be both used on the CPU (using traverseBVH() or on the GPU by: 1. import LightBVH; 2. Declare a variable of type LightBVH in your shader. - 3. Call setShaderData() to bind the BVH resources. + 3. Call bindShaderData() to bind the BVH resources. TODO: Rename all things 'triangle' to 'light' as the BVH can be used for other light types. */ @@ -120,7 +120,7 @@ namespace Falcor /** Bind the light BVH into a shader variable. \param[in] var The shader variable to set the data into. */ - void setShaderData(const ShaderVar& var) const; + void bindShaderData(const ShaderVar& var) const; protected: void finalize(); diff --git a/Source/Falcor/Rendering/Lights/LightBVHBuilder.cpp b/Source/Falcor/Rendering/Lights/LightBVHBuilder.cpp index d7acf8bf8..7a3eee0c1 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHBuilder.cpp +++ b/Source/Falcor/Rendering/Lights/LightBVHBuilder.cpp @@ -26,8 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "LightBVHBuilder.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Utils/Logger.h" #include "Utils/Timing/Profiler.h" #include "Utils/Math/MathConstants.slangh" @@ -194,7 +193,7 @@ namespace delta + theta <= oTheta + 1e-3f); if (!dInCone) { - throw RuntimeError("Error in coneUnion(): angle diff {} > spread {}", delta + theta, oTheta); + FALCOR_THROW("Error in coneUnion(): angle diff {} > spread {}", delta + theta, oTheta); } }; checkInside(aDir, aTheta); @@ -268,11 +267,11 @@ namespace Falcor // Validate options. if (mOptions.maxTriangleCountPerLeaf > kMaxLeafTriangleCount) { - throw RuntimeError("Max triangle count per leaf exceeds the maximum supported ({})", kMaxLeafTriangleCount); + FALCOR_THROW("Max triangle count per leaf exceeds the maximum supported ({})", kMaxLeafTriangleCount); } if (data.trianglesData.size() > kMaxLeafTriangleOffset + kMaxLeafTriangleCount) { - throw RuntimeError("Emissive triangle count exceeds the maximum supported ({})", kMaxLeafTriangleOffset + kMaxLeafTriangleCount); + FALCOR_THROW("Emissive triangle count exceeds the maximum supported ({})", kMaxLeafTriangleOffset + kMaxLeafTriangleCount); } // Allocate temporary memory for the BVH build. @@ -392,7 +391,7 @@ namespace Falcor { // This is an unrecoverable error since we use bit masks to represent the traversal path from // the root node to each leaf node in the tree, which is necessary for pdf computation with MIS. - throw RuntimeError("BVH depth of {} reached. Maximum of {} allowed.", depth + 1, kMaxBVHDepth); + FALCOR_THROW("BVH depth of {} reached. Maximum of {} allowed.", depth + 1, kMaxBVHDepth); } uint32_t leftIndex = buildInternal(options, splitHeuristic, bitmask | (0ull << depth), depth + 1, Range(triangleRange.begin, splitResult.triangleIndex), data); @@ -877,7 +876,7 @@ namespace Falcor case SplitHeuristic::BinnedSAOH: return computeSplitWithBinnedSAOH; default: - throw RuntimeError("Unsupported SplitHeuristic: {}", static_cast(heuristic)); + FALCOR_THROW("Unsupported SplitHeuristic: {}", static_cast(heuristic)); } } } diff --git a/Source/Falcor/Rendering/Lights/LightBVHSampler.cpp b/Source/Falcor/Rendering/Lights/LightBVHSampler.cpp index cddd7a2b0..b5c46ddcb 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHSampler.cpp +++ b/Source/Falcor/Rendering/Lights/LightBVHSampler.cpp @@ -26,8 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "LightBVHSampler.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Utils/Timing/Profiler.h" #include #include @@ -80,11 +79,11 @@ namespace Falcor return defines; } - void LightBVHSampler::setShaderData(const ShaderVar& var) const + void LightBVHSampler::bindShaderData(const ShaderVar& var) const { FALCOR_ASSERT(var.isValid()); FALCOR_ASSERT(mpBVH); - mpBVH->setShaderData(var["_lightBVH"]); + mpBVH->bindShaderData(var["_lightBVH"]); } bool LightBVHSampler::renderUI(Gui::Widgets& widgets) @@ -129,6 +128,15 @@ namespace Falcor return optionsChanged; } + void LightBVHSampler::setOptions(const Options& options) + { + if (std::memcmp(&mOptions, &options, sizeof(Options)) != 0) + { + mOptions = options; + mNeedsRebuild = true; + } + } + LightBVHSampler::LightBVHSampler(RenderContext* pRenderContext, ref pScene, const Options& options) : EmissiveLightSampler(EmissiveLightSamplerType::LightBVH, pScene) , mOptions(options) diff --git a/Source/Falcor/Rendering/Lights/LightBVHSampler.h b/Source/Falcor/Rendering/Lights/LightBVHSampler.h index 0e2fc47d9..09d9d83d2 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHSampler.h +++ b/Source/Falcor/Rendering/Lights/LightBVHSampler.h @@ -105,7 +105,7 @@ namespace Falcor /** Bind the light sampler data to a given shader variable. \param[in] var Shader variable. */ - virtual void setShaderData(const ShaderVar& var) const override; + virtual void bindShaderData(const ShaderVar& var) const override; /** Render the GUI. \return True if setting the refresh flag is needed, false otherwise. @@ -116,6 +116,8 @@ namespace Falcor */ const Options& getOptions() const { return mOptions; } + void setOptions(const Options& options); + protected: /// Configuration options. Options mOptions; diff --git a/Source/Falcor/Rendering/Lights/LightBVHSamplerSharedDefinitions.slang b/Source/Falcor/Rendering/Lights/LightBVHSamplerSharedDefinitions.slang index 15c1d66d8..1856af097 100644 --- a/Source/Falcor/Rendering/Lights/LightBVHSamplerSharedDefinitions.slang +++ b/Source/Falcor/Rendering/Lights/LightBVHSamplerSharedDefinitions.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Rendering/Materials/AnisotropicGGX.slang b/Source/Falcor/Rendering/Materials/AnisotropicGGX.slang index acf825dfe..8e8c0eebf 100644 --- a/Source/Falcor/Rendering/Materials/AnisotropicGGX.slang +++ b/Source/Falcor/Rendering/Materials/AnisotropicGGX.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -33,15 +33,16 @@ import Utils.Sampling.SampleGeneratorInterface; /** This class implements sampling and evaluation of an anisotropic GGX. The class uses the visible distribution of normals for sampling. */ -struct AnisotropicGGX +struct AnisotropicGGX : IDifferentiable { float2 alpha; - + + [Differentiable] __init(float2 alpha) { this.alpha = alpha; } - + /** A 'singular' distribution is one which would be more ideally treated as an ideal mirror. It can't be sampled reliably. */ @@ -49,7 +50,8 @@ struct AnisotropicGGX { return all(alpha < 1e-3f); } - + + [Differentiable] float getIsotropicRoughness() { return max(alpha.x, alpha.y); @@ -58,32 +60,35 @@ struct AnisotropicGGX /** Computes the Smith lambda function. This function is symmetric with respect to direction, i.e. smithLambda(v) = smithLambda(-v). The result returned is equivalent to assuming v.z > 0. */ + [Differentiable] float smithLambda(float3 v) { float cosThetaSq = v.z * v.z; float sinThetaSq = max(1.0f - cosThetaSq, 0.0f); float tanThetaSq = sinThetaSq / cosThetaSq; if (isinf(tanThetaSq)) return 0.0f; - + float2 cosSinPhiSq = sinThetaSq == 0.0f ? float2(1.0f, 0.0f) : float2(v.x * v.x, v.y * v.y) / sinThetaSq; float alphaSq = dot(cosSinPhiSq, alpha * alpha); return (sqrt(1.0f + alphaSq * tanThetaSq) - 1.0f) * 0.5f; } - + /** Compute the monostatic Smith shadowing function. This function returns the result assuming w.z > 0. This is what you want for single scattering, but you need to be careful for multiple scattering on microfacets. */ + [Differentiable] float G1(float3 w) { return 1.0f / (1.0f + smithLambda(w)); } - + /** Compute the bistatic Smith shadowing function for reflection. This will be off for refraction, where we would have to use the beta function instead. This is not worth the trouble unless we do multiple scattering on microfacet dielectrics as well. */ + [Differentiable] float G2(float3 wo, float3 wi) { return 1.0f / (1.0f + smithLambda(wo) + smithLambda(wi)); @@ -91,24 +96,26 @@ struct AnisotropicGGX /** Computes the normal distribution function, i.e. D(h) */ + [Differentiable] float evalNDF(float3 h) { float cosThetaSq = h.z * h.z; float sinThetaSq = max(1.0f - cosThetaSq, 0.0f); float tanThetaSq = sinThetaSq / cosThetaSq; if (isinf(tanThetaSq)) return 0.0f; - + float cosThetaQu = cosThetaSq * cosThetaSq; if (cosThetaQu < 1e-16f) return 0.0f; - + float2 cosSinPhiSq = sinThetaSq == 0.0f ? float2(1.0f, 0.0f) : float2(h.x * h.x, h.y * h.y) / sinThetaSq; float2 alphaSq = cosSinPhiSq / (alpha * alpha); float e = tanThetaSq * (alphaSq.x + alphaSq.y); return 1.0f / (M_PI * alpha.x * alpha.y * cosThetaQu * sqr(1.0f + e)); } - + /** Computes the distribution of visible normals, i.e. D_wi(h) */ + [Differentiable] float evalVNDF(float3 w, float3 h) { return w.z == 0.0f ? 0.0f : (G1(w) * abs(dot(w, h)) * evalNDF(h)) / abs(w.z); @@ -120,17 +127,17 @@ struct AnisotropicGGX { float3 wh = normalize(float3(alpha.x * w.x, alpha.y * w.y, w.z)); if (wh.z < 0.0f) wh = -wh; - + float3 T1 = dot(wh.xy, wh.xy) > 0.0f ? normalize(float3(-wh.y, wh.x, 0.0f)) : float3(1.0f, 0.0f, 0.0f); float3 T2 = cross(wh, T1); - + float r = sqrt(sampleNext1D(sg)); float phi = 2.0f * M_PI * sampleNext1D(sg); float t1 = r * cos(phi); float t2 = r * sin(phi); float s = 0.5 * (1.0 + wh.z); t2 = (1.0f - s) * sqrt(1.0f - t1 * t1) + s * t2; - + float3 nh = t1 * T1 + t2 * T2 + sqrt(max(0.0f, 1.0f - t1 * t1 - t2 * t2)) * wh; return normalize(float3(alpha.x * nh.x, alpha.y * nh.y, max(0.0f, nh.z))); } @@ -141,4 +148,4 @@ struct AnisotropicGGX { return evalVNDF(w, h); } -}; \ No newline at end of file +}; diff --git a/Source/Falcor/Rendering/Materials/BSDFIntegrator.cpp b/Source/Falcor/Rendering/Materials/BSDFIntegrator.cpp index 223ef79ac..badd44be4 100644 --- a/Source/Falcor/Rendering/Materials/BSDFIntegrator.cpp +++ b/Source/Falcor/Rendering/Materials/BSDFIntegrator.cpp @@ -26,8 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "BSDFIntegrator.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/API/RenderContext.h" #include "Utils/Logger.h" @@ -47,17 +46,19 @@ namespace Falcor : mpDevice(pDevice) , mpScene(pScene) { - checkArgument(mpScene != nullptr, "'pDevice' must be a valid device"); - checkArgument(pScene != nullptr, "'pScene' must be a valid scene"); + FALCOR_CHECK(pDevice != nullptr, "'pDevice' must be a valid device"); + FALCOR_CHECK(pScene != nullptr, "'pScene' must be a valid scene"); + + if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_6)) + FALCOR_THROW("BSDFIntegrator requires Shader Model 6.6 support"); // Create programs. - Program::Desc desc; - desc.setShaderModel("6_6"); + ProgramDesc desc; desc.addShaderModules(pScene->getShaderModules()); desc.addShaderLibrary(kShaderFile); desc.addTypeConformances(pScene->getTypeConformances()); auto defines = pScene->getSceneDefines(); - Program::Desc descFinal = desc; + ProgramDesc descFinal = desc; desc.csEntry("mainIntegration"); mpIntegrationPass = ComputePass::create(mpDevice, desc, defines); @@ -74,8 +75,6 @@ namespace Falcor uint3 finalGroupSize = mpFinalPass->getThreadGroupSize(); FALCOR_ASSERT(finalGroupSize.x == 256 && finalGroupSize.y == 1 && finalGroupSize.z == 1); FALCOR_ASSERT(finalGroupSize.x == mResultCount); - - mpFence = GpuFence::create(mpDevice); } float3 BSDFIntegrator::integrateIsotropic(RenderContext* pRenderContext, const MaterialID materialID, float cosTheta) @@ -88,8 +87,8 @@ namespace Falcor std::vector BSDFIntegrator::integrateIsotropic(RenderContext* pRenderContext, const MaterialID materialID, const std::vector& cosThetas) { FALCOR_ASSERT(mpScene); - checkArgument(materialID.get() < mpScene->getMaterialCount(), "'materialID' is out of range"); - checkArgument(!cosThetas.empty(), "'cosThetas' array is empty"); + FALCOR_CHECK(materialID.get() < mpScene->getMaterialCount(), "'materialID' is out of range"); + FALCOR_CHECK(!cosThetas.empty(), "'cosThetas' array is empty"); CpuTimer timer; timer.update(); @@ -100,7 +99,7 @@ namespace Falcor if (!mpCosThetaBuffer || mpCosThetaBuffer->getElementCount() < gridCount) { - mpCosThetaBuffer = Buffer::createStructured(mpDevice, sizeof(float), gridCount, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, cosThetas.data(), false); + mpCosThetaBuffer = mpDevice->createStructuredBuffer(sizeof(float), gridCount, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, cosThetas.data(), false); } else { @@ -111,12 +110,12 @@ namespace Falcor uint32_t elemCount = gridCount * mResultCount; if (!mpResultBuffer || mpResultBuffer->getElementCount() < elemCount) { - mpResultBuffer = Buffer::createStructured(mpDevice, sizeof(float3), elemCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpResultBuffer = mpDevice->createStructuredBuffer(sizeof(float3), elemCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); } if (!mpFinalResultBuffer || mpFinalResultBuffer->getElementCount() < gridCount) { - mpFinalResultBuffer = Buffer::createStructured(mpDevice, sizeof(float3), gridCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpStagingBuffer = Buffer::createStructured(mpDevice, sizeof(float3), gridCount, ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr, false); + mpFinalResultBuffer = mpDevice->createStructuredBuffer(sizeof(float3), gridCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); + mpStagingBuffer = mpDevice->createStructuredBuffer(sizeof(float3), gridCount, ResourceBindFlags::None, MemoryType::ReadBack, nullptr, false); } // Execute GPU passes. @@ -126,10 +125,8 @@ namespace Falcor // Copy result to staging buffer. pRenderContext->copyBufferRegion(mpStagingBuffer.get(), 0, mpFinalResultBuffer.get(), 0, sizeof(float3) * gridCount); - // Flush GPU and wait for results to be available. - pRenderContext->flush(false); - mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); - mpFence->syncCpu(); + // Wait for results to be available. + pRenderContext->submit(true); // Read back final results. const float3* finalResults = reinterpret_cast(mpStagingBuffer->map(Buffer::MapType::Read)); @@ -153,7 +150,7 @@ namespace Falcor var["cosThetas"] = mpCosThetaBuffer; var["results"] = mpResultBuffer; - mpIntegrationPass->getRootVar()["gScene"] = mpScene->getParameterBlock(); + mpScene->bindShaderData(mpIntegrationPass->getRootVar()["gScene"]); mpIntegrationPass->execute(pRenderContext, uint3(kGridSize, gridCount)); } @@ -170,7 +167,7 @@ namespace Falcor #if 0 // DEBUG: Final accumulation on the CPU. - const float3* results = reinterpret_cast(mpResultBuffer->map(Buffer::MapType::Read)); + std::vector results = mpResultBuffer->getElements(); for (uint32_t gridIdx = 0; gridIdx < gridCount; gridIdx++) { float3 sum = {}; diff --git a/Source/Falcor/Rendering/Materials/BSDFIntegrator.cs.slang b/Source/Falcor/Rendering/Materials/BSDFIntegrator.cs.slang index 929ec02a3..1df3dd74d 100644 --- a/Source/Falcor/Rendering/Materials/BSDFIntegrator.cs.slang +++ b/Source/Falcor/Rendering/Materials/BSDFIntegrator.cs.slang @@ -68,7 +68,6 @@ struct BSDFIntegrator sd.mtl = gScene.materials.getMaterialHeader(materialID); sd.materialID = materialID; - sd.opacity = 1.f; sd.IoR = 1.f; float cosTheta = saturate(cosThetas[groupID.z]); diff --git a/Source/Falcor/Rendering/Materials/BSDFIntegrator.h b/Source/Falcor/Rendering/Materials/BSDFIntegrator.h index 9f8b99d3f..d6b2fab94 100644 --- a/Source/Falcor/Rendering/Materials/BSDFIntegrator.h +++ b/Source/Falcor/Rendering/Materials/BSDFIntegrator.h @@ -28,7 +28,7 @@ #pragma once #include "Core/Macros.h" #include "Core/API/Buffer.h" -#include "Core/API/GpuFence.h" +#include "Core/API/Fence.h" #include "Core/Pass/ComputePass.h" #include "Utils/Math/Vector.h" #include "Scene/Scene.h" @@ -75,7 +75,6 @@ namespace Falcor ref mpResultBuffer; ///< Buffer for intermediate results. ref mpFinalResultBuffer; ///< Buffer for final results after reduction. ref mpStagingBuffer; ///< Staging buffer for readback of final results. - ref mpFence; ///< Fence for synchronizing readback. uint32_t mResultCount; ///< Number of intermediate results per integration grid. }; } diff --git a/Source/Falcor/Rendering/Materials/BSDFs/DisneyDiffuseBRDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/DisneyDiffuseBRDF.slang index 14c3c51f0..dddaeec37 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/DisneyDiffuseBRDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/DisneyDiffuseBRDF.slang @@ -35,11 +35,12 @@ __exported import Rendering.Materials.IBSDF; * Disney's diffuse reflection. * Based on https ://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf */ -struct DisneyDiffuseBRDF : IBSDF +struct DisneyDiffuseBRDF : IBSDF, IDifferentiable { float3 albedo; ///< Diffuse albedo. float roughness; ///< Roughness before remapping. + [Differentiable] float3 eval(const float3 wi, const float3 wo, inout S sg) { if (min(wi.z, wo.z) < kMinCosTheta) @@ -74,6 +75,7 @@ struct DisneyDiffuseBRDF : IBSDF // private // Returns f(wi, wo) * pi. + [Differentiable] float3 evalWeight(float3 wi, float3 wo) { float3 h = normalize(wi + wo); diff --git a/Source/Falcor/Rendering/Materials/BSDFs/FrostbiteDiffuseBRDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/FrostbiteDiffuseBRDF.slang index c54ad5afa..8ae77faa3 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/FrostbiteDiffuseBRDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/FrostbiteDiffuseBRDF.slang @@ -36,11 +36,12 @@ __exported import Rendering.Materials.IBSDF; * This is Disney's diffuse BRDF with an ad-hoc normalization factor to ensure energy conservation. * Based on https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf */ -struct FrostbiteDiffuseBRDF : IBSDF +struct FrostbiteDiffuseBRDF : IBSDF, IDifferentiable { float3 albedo; ///< Diffuse albedo. float roughness; ///< Roughness before remapping. + [Differentiable] float3 eval(const float3 wi, const float3 wo, inout S sg) { if (min(wi.z, wo.z) < kMinCosTheta) @@ -75,6 +76,7 @@ struct FrostbiteDiffuseBRDF : IBSDF // private // Returns f(wi, wo) * pi. + [Differentiable] float3 evalWeight(float3 wi, float3 wo) { float3 h = normalize(wi + wo); diff --git a/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBRDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBRDF.slang index dff3a9ef4..4a432dc61 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBRDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBRDF.slang @@ -34,12 +34,14 @@ __exported import Rendering.Materials.IBSDF; * Lambertian diffuse reflection. * f_r(wi, wo) = albedo / pi */ -struct LambertDiffuseBRDF : IBSDF +struct LambertDiffuseBRDF : IBSDF, IDifferentiable { float3 albedo; ///< Diffuse albedo. + [Differentiable] __init(float3 albedo) { this.albedo = albedo; } + [Differentiable] float3 eval(const float3 wi, const float3 wo, inout S sg) { if (min(wi.z, wo.z) < kMinCosTheta) diff --git a/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBTDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBTDF.slang index 1d2caecde..d34919b2c 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBTDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/LambertDiffuseBTDF.slang @@ -33,10 +33,11 @@ __exported import Rendering.Materials.IBSDF; /** * Lambertian diffuse transmission. */ -struct LambertDiffuseBTDF : IBSDF +struct LambertDiffuseBTDF : IBSDF, IDifferentiable { float3 albedo; ///< Diffuse albedo. + [Differentiable] float3 eval(const float3 wi, const float3 wo, inout S sg) { if (min(wi.z, -wo.z) < kMinCosTheta) diff --git a/Source/Falcor/Rendering/Materials/BSDFs/SheenBSDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/SheenBSDF.slang index bb45e1661..7f863b5d9 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/SheenBSDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/SheenBSDF.slang @@ -145,11 +145,16 @@ struct SheenBSDF : IBSDF AlbedoContributions evalAlbedo(const float3 wi, const LobeType lobetype) { const float coefficients[11][5] = { - { 1.00519, 164.794, -8.91257, 0.000614987, -1.00615 }, { 1.07632, 121.269, -2.6416, 0.000548297, -1.04411 }, - { 1.20174, 9.90325, -1.32211, 0.0238483, -1.11425 }, { 4.55388, 0.0233967, 1.12007, 0.431478, -5.41286 }, - { 3.67585, 0.104744, -3.88354, 0.155255, -4.3272 }, { 2.84549, 0.182875, -5.17779, 0.132618, -3.40558 }, - { 5.1483, 0.110417, -6.03901, 0.114556, -5.63637 }, { 4.24518, 0.152001, -6.80592, 0.101996, -4.68505 }, - { 4.43179, 0.154597, -5.59283, 0.0928506, -4.83838 }, { 4.52583, 0.157636, -6.06809, 0.0861137, -4.90953 }, + { 1.00519, 164.794, -8.91257, 0.000614987, -1.00615 }, + { 1.07632, 121.269, -2.6416, 0.000548297, -1.04411 }, + { 1.20174, 9.90325, -1.32211, 0.0238483, -1.11425 }, + { 4.55388, 0.0233967, 1.12007, 0.431478, -5.41286 }, + { 3.67585, 0.104744, -3.88354, 0.155255, -4.3272 }, + { 2.84549, 0.182875, -5.17779, 0.132618, -3.40558 }, + { 5.1483, 0.110417, -6.03901, 0.114556, -5.63637 }, + { 4.24518, 0.152001, -6.80592, 0.101996, -4.68505 }, + { 4.43179, 0.154597, -5.59283, 0.0928506, -4.83838 }, + { 4.52583, 0.157636, -6.06809, 0.0861137, -4.90953 }, { 4.53904, 0.160742, -9.04844, 0.0820168, -4.91045 } }; diff --git a/Source/Falcor/Rendering/Materials/BSDFs/SpecularMicrofacet.slang b/Source/Falcor/Rendering/Materials/BSDFs/SpecularMicrofacet.slang index d69dd4b11..4f38eff73 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/SpecularMicrofacet.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/SpecularMicrofacet.slang @@ -46,7 +46,7 @@ __exported import Rendering.Materials.IBSDF; /** * Specular reflection using microfacets. */ -struct SpecularMicrofacetBRDF : IBSDF +struct SpecularMicrofacetBRDF : IBSDF, IDifferentiable { float3 albedo; ///< Specular albedo. float alpha; ///< GGX width parameter. @@ -54,6 +54,7 @@ struct SpecularMicrofacetBRDF : IBSDF bool hasLobe(LobeType lobeType) { return (activeLobes & (uint)lobeType) != 0; } + [Differentiable] float3 eval(const float3 wi, const float3 wo, inout S sg) { if (min(wi.z, wo.z) < kMinCosTheta) @@ -192,7 +193,7 @@ struct SpecularMicrofacetBRDF : IBSDF /** * Specular reflection and transmission using microfacets. */ -struct SpecularMicrofacetBSDF : IBSDF +struct SpecularMicrofacetBSDF : IBSDF, IDifferentiable { float3 transmissionAlbedo; ///< Transmission albedo. float alpha; ///< GGX width parameter. @@ -201,6 +202,7 @@ struct SpecularMicrofacetBSDF : IBSDF bool hasLobe(LobeType lobeType) { return (activeLobes & (uint)lobeType) != 0; } + [Differentiable] float3 eval(const float3 wi, const float3 wo, inout S sg) { if (min(wi.z, abs(wo.z)) < kMinCosTheta) diff --git a/Source/Falcor/Rendering/Materials/BSDFs/StandardBSDF.slang b/Source/Falcor/Rendering/Materials/BSDFs/StandardBSDF.slang index 7bb91f9b0..917e94d0a 100644 --- a/Source/Falcor/Rendering/Materials/BSDFs/StandardBSDF.slang +++ b/Source/Falcor/Rendering/Materials/BSDFs/StandardBSDF.slang @@ -30,6 +30,7 @@ import Scene.Material.MaterialData; import Utils.Color.ColorHelpers; import Rendering.Materials.Fresnel; +import Rendering.Materials.IMaterialInstance; #if DiffuseBrdf == DiffuseBrdfLambert import Rendering.Materials.BSDFs.LambertDiffuseBRDF; #elif DiffuseBrdf == DiffuseBrdfDisney @@ -39,6 +40,7 @@ import Rendering.Materials.BSDFs.FrostbiteDiffuseBRDF; #endif import Rendering.Materials.BSDFs.LambertDiffuseBTDF; import Rendering.Materials.BSDFs.SpecularMicrofacet; +import DiffRendering.DiffMaterialData; __exported import Rendering.Materials.IBSDF; // Enable support for delta reflection/transmission. @@ -54,17 +56,34 @@ static const float kMinGGXAlpha = 0.0064f; * BSDF parameters for the standard MaterialInstance. * These are needed for initializing a `StandardBSDF` instance. */ -struct StandardBSDFData +struct StandardBSDFData : IDifferentiable { - float3 diffuse; ///< Diffuse albedo. - float3 specular; ///< Specular albedo. - float roughness; ///< This is the original roughness, before remapping. - float metallic; ///< Metallic parameter, blends between dielectric and conducting BSDFs. - float eta; ///< Relative index of refraction (incident IoR / transmissive IoR). - float3 transmission; ///< Transmission color. - float diffuseTransmission; ///< Diffuse transmission, blends between diffuse reflection and transmission lobes. - /// Specular transmission, blends between opaque dielectric BRDF and specular transmissive BSDF. - float specularTransmission; + float3 diffuse; ///< Diffuse albedo. + float3 specular; ///< Specular albedo. + float roughness; ///< This is the original roughness, before remapping. + float metallic; ///< Metallic parameter, blends between dielectric and conducting BSDFs. + float eta; ///< Relative index of refraction (incident IoR / transmissive IoR). + float3 transmission; ///< Transmission color. + float diffuseTransmission; ///< Diffuse transmission, blends between diffuse reflection and transmission lobes. + float specularTransmission; ///< Specular transmission, blends between opaque dielectric BRDF and specular transmissive BSDF. + float3 volumeScattering; ///< Volume scattering parameters from the volume. + float volumeAnsiotropy; ///< Mean cosine of the volumetric phase function. + bool hasEntryPointVolumeProperties; ///< Flag that specifies from where sigmaA is taken from (volume or surface). + bool hasSigmaSGreaterZero; ///< Flag that indicates if we have to do albedo conversion + + [Differentiable] + __init(const DiffMaterialData diffData) + { + uint offset = 0; + diffData.read(diffuse, offset); + diffData.read(specular, offset); + roughness = diffData.read(offset); + metallic = diffData.read(offset); + eta = diffData.read(offset); + diffData.read(transmission, offset); + diffuseTransmission = diffData.read(offset); + specularTransmission = diffData.read(offset); + } } /** @@ -73,7 +92,7 @@ struct StandardBSDFData * This consists of a diffuse and specular BRDF. * A specular BSDF is mixed in using the specularTransmission parameter. */ -struct StandardBSDF : IBSDF +struct StandardBSDF : IBSDF, IDifferentiable { #if DiffuseBrdf == DiffuseBrdfLambert LambertDiffuseBRDF diffuseReflection; @@ -90,10 +109,11 @@ struct StandardBSDF : IBSDF float diffTrans; ///< Mix between diffuse BRDF and diffuse BTDF. float specTrans; ///< Mix between dielectric BRDF and specular BSDF. - float pDiffuseReflection; ///< Probability for sampling the diffuse BRDF. - float pDiffuseTransmission; ///< Probability for sampling the diffuse BTDF. - float pSpecularReflection; ///< Probability for sampling the specular BRDF. - float pSpecularTransmission; ///< Probability for sampling the specular BSDF. + // No need to differentiate probabilities. + no_diff float pDiffuseReflection; ///< Probability for sampling the diffuse BRDF. + no_diff float pDiffuseTransmission; ///< Probability for sampling the diffuse BTDF. + no_diff float pSpecularReflection; ///< Probability for sampling the specular BRDF. + no_diff float pSpecularTransmission; ///< Probability for sampling the specular BSDF. /** * Initialize a new instance. @@ -101,6 +121,7 @@ struct StandardBSDF : IBSDF * @param[in] mtl Material header. * @param[in] data BSDF parameters. */ + [Differentiable] __init(const float3 wi, const MaterialHeader mtl, const StandardBSDFData data) { // TODO: Currently specular reflection and transmission lobes are not properly separated. @@ -150,14 +171,16 @@ struct StandardBSDF : IBSDF float diffuseWeight = luminance(data.diffuse); float specularWeight = luminance(evalFresnelSchlick(data.specular, 1.f, wi.z)); - pDiffuseReflection = (activeLobes & (uint)LobeType::DiffuseReflection) ? diffuseWeight * dielectricBSDF * (1.f - diffTrans) : 0.f; - pDiffuseTransmission = (activeLobes & (uint)LobeType::DiffuseTransmission) ? diffuseWeight * dielectricBSDF * diffTrans : 0.f; + pDiffuseReflection = + (activeLobes & (uint)LobeType::DiffuseReflection) ? detach(diffuseWeight * dielectricBSDF * (1.f - diffTrans)) : 0.f; + pDiffuseTransmission = + (activeLobes & (uint)LobeType::DiffuseTransmission) ? detach(diffuseWeight * dielectricBSDF * diffTrans) : 0.f; pSpecularReflection = (activeLobes & ((uint)LobeType::SpecularReflection | (uint)LobeType::DeltaReflection)) - ? specularWeight * (metallicBRDF + dielectricBSDF) + ? detach(specularWeight * (metallicBRDF + dielectricBSDF)) : 0.f; pSpecularTransmission = (activeLobes & ((uint)LobeType::SpecularReflection | (uint)LobeType::DeltaReflection | (uint)LobeType::SpecularTransmission | (uint)LobeType::DeltaTransmission)) - ? specularBSDF + ? detach(specularBSDF) : 0.f; float normFactor = pDiffuseReflection + pDiffuseTransmission + pSpecularReflection + pSpecularTransmission; @@ -201,6 +224,7 @@ struct StandardBSDF : IBSDF return lobeTypes; } + [Differentiable] float3 eval(const float3 wi, const float3 wo, inout S sg) { float3 result = 0.f; diff --git a/Source/Falcor/Rendering/Materials/ClothMaterial.slang b/Source/Falcor/Rendering/Materials/ClothMaterial.slang index fd3332477..3608c3217 100644 --- a/Source/Falcor/Rendering/Materials/ClothMaterial.slang +++ b/Source/Falcor/Rendering/Materials/ClothMaterial.slang @@ -62,11 +62,13 @@ struct ClothMaterial : MaterialBase, IMaterial brdf.roughness = spec.g; // TODO: Linear or squared roughness? brdf.f0 = float3(F0); // TODO: What's appropriate here? +#if defined(SCENE_DIFFUSE_ALBEDO_MULTIPLIER) // DEMO21 - if (Scene::kDiffuseAlbedoMultiplier != 1.f) + if (SCENE_DIFFUSE_ALBEDO_MULTIPLIER != 1.f) { - brdf.diffuseColor = saturate(brdf.diffuseColor * Scene::kDiffuseAlbedoMultiplier); + brdf.diffuseColor = saturate(brdf.diffuseColor * SCENE_DIFFUSE_ALBEDO_MULTIPLIER); } +#endif // Compute final shading frame. ShadingFrame sf = sd.frame; @@ -84,9 +86,10 @@ struct ClothMaterial : MaterialBase, IMaterial return ClothMaterialInstance(sf, brdf); } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) + [Differentiable] + ClothMaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - return ms.sampleTexture(data.texBaseColor, s, v.texC, data.baseColor, lod).a; + diffData = DiffMaterialData(); + return no_diff setupMaterialInstance(ms, sd, lod, hints); } }; diff --git a/Source/Falcor/Rendering/Materials/ClothMaterialInstance.slang b/Source/Falcor/Rendering/Materials/ClothMaterialInstance.slang index 71cf1775d..36b0a0b26 100644 --- a/Source/Falcor/Rendering/Materials/ClothMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/ClothMaterialInstance.slang @@ -33,7 +33,7 @@ import Utils.Math.MathHelpers; /** Implementation of a simple cloth BSDF. */ -struct ClothMaterialInstance : IMaterialInstance +struct ClothMaterialInstance : MaterialInstanceBase, IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. ClothBRDF brdf; @@ -54,6 +54,12 @@ struct ClothMaterialInstance : IMaterialInstance return brdf.eval(wiLocal, woLocal, sg); } + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg) + { + return float3(0.f); + } + bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { return sampleReference(sd, sg, result); diff --git a/Source/Falcor/Rendering/Materials/Fresnel.slang b/Source/Falcor/Rendering/Materials/Fresnel.slang index 709f34f0e..a2b45530c 100644 --- a/Source/Falcor/Rendering/Materials/Fresnel.slang +++ b/Source/Falcor/Rendering/Materials/Fresnel.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -37,16 +37,19 @@ \param[in] cosTheta Cosine of angle between microfacet normal and incident direction (LdotH). \return Fresnel term. */ +[Differentiable] float3 evalFresnelSchlick(float3 f0, float3 f90, float cosTheta) { return f0 + (f90 - f0) * pow(max(1 - cosTheta, 0), 5); // Clamp to avoid NaN if cosTheta = 1+epsilon } +[Differentiable] float evalFresnelSchlick(float f0, float f90, float cosTheta) { return f0 + (f90 - f0) * pow(max(1 - cosTheta, 0), 5); // Clamp to avoid NaN if cosTheta = 1+epsilon } +[Differentiable] float3 evalFresnelGeneralizedSchlick(float3 f0, float3 f90, float exponent, float cosTheta) { return f0 + (f90 - f0) * pow(max(1 - cosTheta, 0), exponent); // Clamp to avoid NaN if cosTheta = 1+epsilon @@ -60,6 +63,7 @@ float3 evalFresnelGeneralizedSchlick(float3 f0, float3 f90, float exponent, floa \param[out] cosThetaT Cosine of angle between negative normal and transmitted direction (0 for total internal reflection). \return Returns Fr(eta, cosThetaI). */ +[Differentiable] float evalFresnelDielectric(float eta, float cosThetaI, out float cosThetaT) { if (cosThetaI < 0) @@ -93,6 +97,7 @@ float evalFresnelDielectric(float eta, float cosThetaI, out float cosThetaT) \param[in] cosThetaI Cosine of angle between normal and incident direction. \return Returns Fr(eta, cosThetaI). */ +[Differentiable] float evalFresnelDielectric(float eta, float cosThetaI) { float cosThetaT; @@ -108,6 +113,7 @@ float evalFresnelDielectric(float eta, float cosThetaI) \param[in] cosThetaI Cosine of angle between normal and incident direction. \return Returns conductor reflectance. */ +[Differentiable] float evalFresnelConductor(float eta, float k, float cosThetaI) { float cosThetaISq = cosThetaI * cosThetaI; @@ -134,6 +140,7 @@ float evalFresnelConductor(float eta, float k, float cosThetaI) \param[in] cosThetaI Cosine of angle between normal and incident direction. \return Returns conductor reflectance. */ +[Differentiable] float3 evalFresnelConductor(float3 eta, float3 k, float cosThetaI) { return float3( diff --git a/Source/Falcor/Rendering/Materials/HairChiang16.slang b/Source/Falcor/Rendering/Materials/HairChiang16.slang index f5a4d047b..3f528877c 100644 --- a/Source/Falcor/Rendering/Materials/HairChiang16.slang +++ b/Source/Falcor/Rendering/Materials/HairChiang16.slang @@ -212,7 +212,8 @@ struct HairChiang16 : IBSDF // vp = v[p]; // } bool done = false; - $for(i in Range(0, kMaxScatterEvents)) + [ForceUnroll] + for (uint i = 0; i < kMaxScatterEvents; i++) { if (!done && u[0].x >= apPdf[i]) { diff --git a/Source/Falcor/Rendering/Materials/HairMaterial.slang b/Source/Falcor/Rendering/Materials/HairMaterial.slang index ea35c3d19..c59e8da71 100644 --- a/Source/Falcor/Rendering/Materials/HairMaterial.slang +++ b/Source/Falcor/Rendering/Materials/HairMaterial.slang @@ -70,6 +70,10 @@ struct HairMaterial : MaterialBase, IMaterial return HairMaterialInstance(sf, d); } - // Alpha testing is not supported by this material. - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { return 1.f; } + [Differentiable] + HairMaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) + { + diffData = DiffMaterialData(); + return no_diff setupMaterialInstance(ms, sd, lod, hints); + } }; diff --git a/Source/Falcor/Rendering/Materials/HairMaterialInstance.slang b/Source/Falcor/Rendering/Materials/HairMaterialInstance.slang index 7f1247b73..d9e936290 100644 --- a/Source/Falcor/Rendering/Materials/HairMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/HairMaterialInstance.slang @@ -40,7 +40,7 @@ import Utils.Math.MathHelpers; By overriding the 'BCSDF' define it is possible to select a single-sided Lambertian diffuse BRDF for debugging purposes (see BCSDFConfig.slangh). */ -struct HairMaterialInstance : IMaterialInstance +struct HairMaterialInstance : MaterialInstanceBase, IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. HairChiang16Data data; @@ -66,6 +66,12 @@ struct HairMaterialInstance : IMaterialInstance #endif } + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg) + { + return float3(0.f); + } + bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { #if BCSDF == HairChiang diff --git a/Source/Falcor/Rendering/Materials/IMaterial.slang b/Source/Falcor/Rendering/Materials/IMaterial.slang index 74e8e35aa..7a3d3c359 100644 --- a/Source/Falcor/Rendering/Materials/IMaterial.slang +++ b/Source/Falcor/Rendering/Materials/IMaterial.slang @@ -65,13 +65,11 @@ interface IMaterial */ MaterialInstance setupMaterialInstance(const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints = (uint)MaterialInstanceHints::None); - /** Evaluate opacity at the given shading point. - \param[in] ms Material system. This provides the resources for opacity evaluation. - \param[in] v Interpolated attributes at the shading point. - \param[in] lod Method to use for computing texture level of detail, must implement the `ITextureSampler` interface. - \return Opacity value in range 0..1. + /** A differentiable version of `setupMaterialInstance`. + This operation returns an additional `DiffMaterialData` structure that contains differentiable parameters. */ - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod); + [Differentiable] + MaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints = (uint)MaterialInstanceHints::None); /** Returns volume properties that are homogeneous for the whole volume (do not depend on the hitpoint). */ diff --git a/Source/Falcor/Rendering/Materials/IMaterialInstance.slang b/Source/Falcor/Rendering/Materials/IMaterialInstance.slang index d4c9d3995..be0445b39 100644 --- a/Source/Falcor/Rendering/Materials/IMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/IMaterialInstance.slang @@ -32,6 +32,9 @@ __exported import Scene.Material.ShadingUtils; __exported import Rendering.Materials.IBSDF; // For kMinCosTheta __exported import Rendering.Materials.LobeType; __exported import Utils.Sampling.SampleGeneratorInterface; +__exported import DiffRendering.DiffMaterialData; +import Rendering.Volumes.PhaseFunction; +import Scene.Material.VolumeProperties; /** Hints passed in when creating a material instance. */ @@ -150,6 +153,16 @@ interface IMaterialInstance */ float3 eval(const ShadingData sd, const float3 wo, inout S sg); + /** Evaluates the material instance with auto-diff. + \param[in] diffData Differentiable parameters. + \param[in] sd Shading data. + \param[in] wo Outgoing direction. + \param[in,out] sg Sample generator. + \return Returns f(wi, wo) * dot(wo, n). + */ + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg); + /** Samples the material instance. \param[in] sd Shading data. \param[in,out] sg Sample generator. @@ -179,4 +192,34 @@ interface IMaterialInstance */ uint getLobeTypes(const ShadingData sd); + + /** Indicates if the volume properties are taken from the material instance or not + \return True if the volume properties are textured. + */ + bool hasVolumeProperties(); + + /** (Textured) volume properties which are stored on the surface instead of the volume + \return Volume properties. + */ + VolumeProperties getVolumeProperties(); +} + +[open] +struct MaterialInstanceBase +{ + bool hasVolumeProperties() + { + return false; + } + + VolumeProperties getVolumeProperties() + { + VolumeProperties result; + result.sigmaA = 0.f; + result.sigmaS = 0.f; + result.phaseFunction = NullPhaseFunction(); + return result; + + } } + diff --git a/Source/Falcor/Rendering/Materials/IsotropicGGX.slang b/Source/Falcor/Rendering/Materials/IsotropicGGX.slang index 3b153da5d..fc69627e7 100644 --- a/Source/Falcor/Rendering/Materials/IsotropicGGX.slang +++ b/Source/Falcor/Rendering/Materials/IsotropicGGX.slang @@ -26,9 +26,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Utils/Math/MathConstants.slangh" - #include "BSDFConfig.slangh" +#include "BSDFConfig.slangh" - /** Evaluates the GGX (Trowbridge-Reitz) normal distribution function (D). +/** Evaluates the GGX (Trowbridge-Reitz) normal distribution function (D). Introduced by Trowbridge and Reitz, "Average irregularity representation of a rough surface for ray reflection", Journal of the Optical Society of America, vol. 65(5), 1975. See the correct normalization factor in Walter et al. https://dl.acm.org/citation.cfm?id=2383874 @@ -42,6 +42,7 @@ \param[in] cosTheta Dot product between shading normal and half vector, in positive hemisphere. \return D(h) */ +[Differentiable] float evalNdfGGX(float alpha, float cosTheta) { float a2 = alpha * alpha; @@ -56,6 +57,7 @@ float evalNdfGGX(float alpha, float cosTheta) \param[in] cosTheta Dot product between shading normal and half vector, in positive hemisphere. \return D(h) * cosTheta */ +[Differentiable] float evalPdfGGX_NDF(float alpha, float cosTheta) { return evalNdfGGX(alpha, cosTheta) * cosTheta; @@ -146,6 +148,7 @@ float3 sampleGGX_VNDF(float alpha, float3 wi, float2 u, out float pdf) \param[in] alphaSqr Squared GGX width parameter. \param[in] cosTheta Dot product between shading normal and evaluated direction, in the positive hemisphere. */ +[Differentiable] float evalG1GGX(float alphaSqr, float cosTheta) { if (cosTheta <= 0) return 0; @@ -160,6 +163,7 @@ float evalG1GGX(float alphaSqr, float cosTheta) \param[in] alphaSqr Squared GGX width parameter. \param[in] cosTheta Dot product between shading normal and the evaluated direction, in the positive hemisphere. */ +[Differentiable] float evalLambdaGGX(float alphaSqr, float cosTheta) { if (cosTheta <= 0) return 0; @@ -176,6 +180,7 @@ float evalLambdaGGX(float alphaSqr, float cosTheta) \param[in] cosThetaO Dot product between shading normal and outgoing direction, in positive hemisphere. \return G(cosThetaI, cosThetaO) */ +[Differentiable] float evalMaskingSmithGGXSeparable(float alpha, float cosThetaI, float cosThetaO) { float alphaSqr = alpha * alpha; @@ -195,6 +200,7 @@ float evalMaskingSmithGGXSeparable(float alpha, float cosThetaI, float cosThetaO \param[in] cosThetaO Dot product between shading normal and outgoing direction, in positive hemisphere. \return G(cosThetaI, cosThetaO) */ +[Differentiable] float evalMaskingSmithGGXCorrelated(float alpha, float cosThetaI, float cosThetaO) { float alphaSqr = alpha * alpha; diff --git a/Source/Falcor/Rendering/Materials/LayeredBSDF.slang b/Source/Falcor/Rendering/Materials/LayeredBSDF.slang index c5f8a8056..4e2f96e03 100644 --- a/Source/Falcor/Rendering/Materials/LayeredBSDF.slang +++ b/Source/Falcor/Rendering/Materials/LayeredBSDF.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Rendering/Materials/MERLMaterial.slang b/Source/Falcor/Rendering/Materials/MERLMaterial.slang index 6024c8ba2..cea11839e 100644 --- a/Source/Falcor/Rendering/Materials/MERLMaterial.slang +++ b/Source/Falcor/Rendering/Materials/MERLMaterial.slang @@ -67,6 +67,10 @@ struct MERLMaterial : MaterialBase, IMaterial return MERLMaterialInstance(sf, data.bufferID, albedo, data.extraData); } - // Alpha testing is not supported by this material. - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { return 1.f; } + [Differentiable] + MERLMaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) + { + diffData = DiffMaterialData(); + return no_diff setupMaterialInstance(ms, sd, lod, hints); + } }; diff --git a/Source/Falcor/Rendering/Materials/MERLMaterialInstance.slang b/Source/Falcor/Rendering/Materials/MERLMaterialInstance.slang index d0feda30a..ecf8ee89f 100644 --- a/Source/Falcor/Rendering/Materials/MERLMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/MERLMaterialInstance.slang @@ -42,7 +42,7 @@ import Scene.Scene; /** Implementation of the BSDF for the measured MERL material. */ -struct MERLMaterialInstance : IMaterialInstance +struct MERLMaterialInstance : MaterialInstanceBase, IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. uint bufferID; ///< Buffer ID in material system where BRDF data is stored. @@ -73,6 +73,12 @@ struct MERLMaterialInstance : IMaterialInstance #endif } + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg) + { + return float3(0.f); + } + bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { float3 wiLocal = sf.toLocal(sd.V); diff --git a/Source/Falcor/Rendering/Materials/MERLMixMaterial.slang b/Source/Falcor/Rendering/Materials/MERLMixMaterial.slang index 6f5d0ee88..aa064301b 100644 --- a/Source/Falcor/Rendering/Materials/MERLMixMaterial.slang +++ b/Source/Falcor/Rendering/Materials/MERLMixMaterial.slang @@ -93,6 +93,10 @@ struct MERLMixMaterial : MaterialBase, IMaterial return MERLMixMaterialInstance(sf, data.bufferID, byteOffset, albedo, brdfIndex, extraData); } - // Alpha testing is not supported by this material. - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { return 1.f; } + [Differentiable] + MERLMixMaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) + { + diffData = DiffMaterialData(); + return no_diff setupMaterialInstance(ms, sd, lod, hints); + } }; diff --git a/Source/Falcor/Rendering/Materials/MERLMixMaterialInstance.slang b/Source/Falcor/Rendering/Materials/MERLMixMaterialInstance.slang index 1783433a9..a512d53bb 100644 --- a/Source/Falcor/Rendering/Materials/MERLMixMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/MERLMixMaterialInstance.slang @@ -40,7 +40,7 @@ import Scene.Scene; /** Implementation of the BSDF for the measured MERLMix material. */ -struct MERLMixMaterialInstance : IMaterialInstance +struct MERLMixMaterialInstance : MaterialInstanceBase, IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. uint bufferID; ///< Buffer ID in material system where BRDF data is stored. @@ -71,6 +71,12 @@ struct MERLMixMaterialInstance : IMaterialInstance return evalLocal(wiLocal, woLocal); } + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg) + { + return float3(0.f); + } + bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { float3 wiLocal = sf.toLocal(sd.V); diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang index ecf9f4439..8b6043f76 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterial.slang @@ -66,9 +66,10 @@ struct PBRTCoatedConductorMaterial : MaterialBase, IMaterial return mi; } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) + [Differentiable] + PBRTCoatedConductorMaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - return ms.sampleTexture(data.texBaseColor, s, v.texC, data.baseColor, lod).a; + diffData = DiffMaterialData(); + return no_diff setupMaterialInstance(ms, sd, lod, hints); } }; diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterialInstance.slang index 830417257..e6fea638c 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedConductorMaterialInstance.slang @@ -31,7 +31,7 @@ import Rendering.Materials.LayeredBSDF; import Rendering.Materials.PBRT.PBRTConductorMaterialInstance; import Rendering.Materials.PBRT.PBRTDielectricMaterialInstance; -struct PBRTCoatedConductorMaterialInstance : IMaterialInstance +struct PBRTCoatedConductorMaterialInstance : MaterialInstanceBase, IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. AnisotropicGGX interfaceD; @@ -54,6 +54,12 @@ struct PBRTCoatedConductorMaterialInstance : IMaterialInstance return LayeredBSDF(top, bottom).eval(wiLocal, woLocal, sg); } + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg) + { + return float3(0.f); + } + bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { float3 wiLocal = sf.toLocal(sd.V); @@ -79,7 +85,7 @@ struct PBRTCoatedConductorMaterialInstance : IMaterialInstance PBRTDielectricBSDF top = {interfaceD, interfaceEta}; PBRTConductorBSDF bottom = {conductorD, conductorEta, conductorK}; - + return LayeredBSDF(top, bottom).evalPdf(wiLocal, woLocal); } diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang index 6aef083db..3d68aedc3 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterial.slang @@ -63,9 +63,10 @@ struct PBRTCoatedDiffuseMaterial : MaterialBase, IMaterial return mi; } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) + [Differentiable] + PBRTCoatedDiffuseMaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - return ms.sampleTexture(data.texBaseColor, s, v.texC, data.baseColor, lod).a; + diffData = DiffMaterialData(); + return no_diff setupMaterialInstance(ms, sd, lod, hints); } }; diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterialInstance.slang index a9f3070fb..bd21e3c35 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTCoatedDiffuseMaterialInstance.slang @@ -31,7 +31,7 @@ import Rendering.Materials.LayeredBSDF; import Rendering.Materials.PBRT.PBRTDielectricMaterialInstance; import Rendering.Materials.PBRT.PBRTDiffuseMaterialInstance; -struct PBRTCoatedDiffuseMaterialInstance : IMaterialInstance +struct PBRTCoatedDiffuseMaterialInstance : MaterialInstanceBase, IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. AnisotropicGGX interfaceD; @@ -52,11 +52,17 @@ struct PBRTCoatedDiffuseMaterialInstance : IMaterialInstance return LayeredBSDF(top, bottom).eval(wiLocal, woLocal, sg); } + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg) + { + return float3(0.f); + } + bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { float3 wiLocal = sf.toLocal(sd.V); float3 woLocal = {}; - + PBRTDielectricBSDF top = {interfaceD, interfaceEta}; PBRTDiffuseBSDF bottom = {diffuseAlbedo}; @@ -77,7 +83,7 @@ struct PBRTCoatedDiffuseMaterialInstance : IMaterialInstance PBRTDielectricBSDF top = {interfaceD, interfaceEta}; PBRTDiffuseBSDF bottom = {diffuseAlbedo}; - + return LayeredBSDF(top, bottom).evalPdf(wiLocal, woLocal); } diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterial.slang index f6b928df3..77404ad2a 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterial.slang @@ -27,6 +27,7 @@ **************************************************************************/ __exported import Rendering.Materials.IMaterial; __exported import Rendering.Materials.PBRT.PBRTConductorMaterialInstance; +import Scene.Material.PBRT.PBRTConductorMaterialParamLayout; import Scene.Material.BasicMaterialData; import Scene.Material.ShadingUtils; @@ -63,9 +64,49 @@ struct PBRTConductorMaterial : MaterialBase, IMaterial return mi; } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) + [Differentiable] + PBRTConductorMaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) { SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - return ms.sampleTexture(data.texBaseColor, s, v.texC, data.baseColor, lod).a; + + uint hashIndex = hashFunction(sd.threadID, gSceneGradients.getHashSize(GradientType::Material)); + GradientIOWrapper gradIO = GradientIOWrapper(GradientType::Material, sd.materialGradOffset, hashIndex); + + // Abuse these colors and assume eta = baseColor, k = transColor + // TODO: Handle texture gradients. + const float4 _baseColor = no_diff ms.sampleTexture(data.texBaseColor, s, sd.uv, data.baseColor, lod); + float3 baseColor = gradIO.getFloat(_baseColor.rgb, PBRTConductorMaterialParamLayout.baseColor); + const float4 _transColor = no_diff ms.sampleTexture(data.texTransmission, s, sd.uv, float4(data.transmission, 1.0f), lod); + float3 transColor = gradIO.getFloat(_transColor.rgb, PBRTConductorMaterialParamLayout.transmissionColor); + const float4 _roughness = no_diff ms.sampleTexture(data.texSpecular, s, sd.uv, data.specular, lod); + float2 roughness = gradIO.getFloat(_roughness.rg, PBRTConductorMaterialParamLayout.roughness); + + // Compute final shading frame. + ShadingFrame sf = sd.frame; + if (isNormalMappingEnabled(hints)) + { + float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; + // TODO: Allow differentiation here. + // We don't support it because differentiating normal maps is harder due to additional discontinuities. + sf = no_diff computeShadingFrameFromNormalMap(sd, data.getNormalMapType(), encodedNormal); + } + flipShadingNormal(sd, sf); + if (isAdjustShadingNormalEnabled(hints)) + { + no_diff adjustShadingNormal(sd, sf); + } + + PBRTConductorMaterialInstance mi = { sf, { AnisotropicGGX(roughness), baseColor, transColor } }; + diffData.sf = sf; + + uint offset = 0; + // 0--3: eta (baseColor) + diffData.write(baseColor, offset); + // 3--6: k (transColor) + diffData.write(transColor, offset); + // 6--8: roughness + diffData.write(roughness, offset); + + return mi; } }; diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterialInstance.slang index bb3189e9d..b3abdd33e 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTConductorMaterialInstance.slang @@ -27,14 +27,27 @@ **************************************************************************/ __exported import Rendering.Materials.IMaterialInstance; __exported import Rendering.Materials.AnisotropicGGX; +__exported import DiffRendering.SceneGradients; +__exported import DiffRendering.GradientIOWrapper; import Rendering.Materials.Fresnel; -struct PBRTConductorBSDF : IBSDF +struct PBRTConductorBSDF : IBSDF, IDifferentiable { AnisotropicGGX D; float3 eta; float3 k; + [Differentiable] + __init(const DiffMaterialData diffData) + { + uint offset = 0; + diffData.read(eta, offset); + diffData.read(k, offset); + float2 roughness = diffData.read<2>(offset); + D = AnisotropicGGX(roughness); + } + + [Differentiable] float3 eval(const float3 wiLocal, const float3 woLocal, inout S sg) { if (D.isSingular() || min(wiLocal.z, woLocal.z) < 0.0f) return float3(0.0f); @@ -100,7 +113,7 @@ struct PBRTConductorBSDF : IBSDF } }; -struct PBRTConductorMaterialInstance : IMaterialInstance +struct PBRTConductorMaterialInstance : MaterialInstanceBase, IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. PBRTConductorBSDF bsdf; @@ -115,6 +128,18 @@ struct PBRTConductorMaterialInstance : IMaterialInstance return bsdf.eval(wiLocal, woLocal, sg); } + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg) + { + float3 wiLocal = diffData.sf.toLocal(sd.V); + float3 woLocal = diffData.sf.toLocal(wo); + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return float3(0.f); + + PBRTConductorBSDF bsdfAD = PBRTConductorBSDF(diffData); + return bsdfAD.eval(wiLocal, woLocal, sg); + } + bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { float3 wiLocal = sf.toLocal(sd.V); diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterial.slang index fb223bdeb..50651e587 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterial.slang @@ -62,9 +62,10 @@ struct PBRTDielectricMaterial : MaterialBase, IMaterial return mi; } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) + [Differentiable] + PBRTDielectricMaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - return ms.sampleTexture(data.texBaseColor, s, v.texC, data.baseColor, lod).a; + diffData = DiffMaterialData(); + return no_diff setupMaterialInstance(ms, sd, lod, hints); } }; diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterialInstance.slang index 3301784a2..409ae63a8 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDielectricMaterialInstance.slang @@ -146,7 +146,7 @@ struct PBRTDielectricBSDF : IBSDF Note that the BSDF is implemented to handle wi/wo over the full sphere. The material is treated as single-sided and the shading normal should not be flipped. */ -struct PBRTDielectricMaterialInstance : IMaterialInstance +struct PBRTDielectricMaterialInstance : MaterialInstanceBase, IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. PBRTDielectricBSDF bsdf; @@ -161,6 +161,12 @@ struct PBRTDielectricMaterialInstance : IMaterialInstance return bsdf.eval(wiLocal, woLocal, sg); } + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg) + { + return float3(0.f); + } + bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { float3 wiLocal = sf.toLocal(sd.V); diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterial.slang index 1bfbb601b..df5768998 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterial.slang @@ -27,6 +27,7 @@ **************************************************************************/ __exported import Rendering.Materials.IMaterial; __exported import Rendering.Materials.PBRT.PBRTDiffuseMaterialInstance; +import Scene.Material.PBRT.PBRTDiffuseMaterialParamLayout; import Scene.Material.BasicMaterialData; import Scene.Material.ShadingUtils; @@ -60,9 +61,39 @@ struct PBRTDiffuseMaterial : MaterialBase, IMaterial return mi; } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) + [Differentiable] + PBRTDiffuseMaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) { SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - return ms.sampleTexture(data.texBaseColor, s, v.texC, data.baseColor, lod).a; + + uint hashIndex = hashFunction(sd.threadID, gSceneGradients.getHashSize(GradientType::Material)); + GradientIOWrapper gradIO = GradientIOWrapper(GradientType::Material, sd.materialGradOffset, hashIndex); + + // TODO: Handle texture gradients. + const float4 _baseColor = no_diff ms.sampleTexture(data.texBaseColor, s, sd.uv, data.baseColor, lod); + float3 baseColor = gradIO.getFloat(_baseColor.rgb, PBRTDiffuseMaterialParamLayout.baseColor); + + // Compute final shading frame. + ShadingFrame sf = sd.frame; + if (isNormalMappingEnabled(hints)) + { + float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; + // TODO: Allow differentiation here. + // We don't support it because differentiating normal maps is harder due to additional discontinuities. + sf = no_diff computeShadingFrameFromNormalMap(sd, data.getNormalMapType(), encodedNormal); + } + flipShadingNormal(sd, sf); + if (isAdjustShadingNormalEnabled(hints)) + { + no_diff adjustShadingNormal(sd, sf); + } + + PBRTDiffuseMaterialInstance mi = { sf, {baseColor} }; + + diffData.sf = sf; + uint offset = 0; + diffData.write(baseColor, offset); + + return mi; } }; diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterialInstance.slang index c21a8b101..776e24fa8 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseMaterialInstance.slang @@ -28,12 +28,15 @@ #include "Utils/Math/MathConstants.slangh" __exported import Rendering.Materials.IMaterialInstance; +__exported import DiffRendering.SceneGradients; +__exported import DiffRendering.GradientIOWrapper; import Utils.Math.MathHelpers; -struct PBRTDiffuseBSDF : IBSDF +struct PBRTDiffuseBSDF : IBSDF, IDifferentiable { float3 albedo; + [Differentiable] float3 eval(const float3 wiLocal, const float3 woLocal, inout S sg) { return albedo * max(woLocal.z, 0.0f) * M_1_PI; @@ -66,7 +69,7 @@ struct PBRTDiffuseBSDF : IBSDF } }; -struct PBRTDiffuseMaterialInstance : IMaterialInstance +struct PBRTDiffuseMaterialInstance : MaterialInstanceBase, IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. PBRTDiffuseBSDF bsdf; @@ -81,6 +84,21 @@ struct PBRTDiffuseMaterialInstance : IMaterialInstance return bsdf.eval(wiLocal, woLocal, sg); } + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg) + { + float3 wiLocal = diffData.sf.toLocal(sd.V); + float3 woLocal = diffData.sf.toLocal(wo); + + if (!isValidHemisphereReflection(sd, sf, wiLocal, woLocal, wo)) return float3(0.f); + + uint offset = 0; + float3 albedo = diffData.read<3>(offset); + PBRTDiffuseBSDF bsdfAD = { albedo }; + + return bsdfAD.eval(wiLocal, woLocal, sg); + } + bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { float3 wiLocal = sf.toLocal(sd.V); diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterial.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterial.slang index 39c58d8a0..87bc52b9c 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterial.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterial.slang @@ -61,9 +61,10 @@ struct PBRTDiffuseTransmissionMaterial : MaterialBase, IMaterial return mi; } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) + [Differentiable] + PBRTDiffuseTransmissionMaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) { - SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - return ms.sampleTexture(data.texBaseColor, s, v.texC, data.baseColor, lod).a; + diffData = DiffMaterialData(); + return no_diff setupMaterialInstance(ms, sd, lod, hints); } }; diff --git a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterialInstance.slang b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterialInstance.slang index dd9972d36..01d745354 100644 --- a/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/PBRT/PBRTDiffuseTransmissionMaterialInstance.slang @@ -35,7 +35,7 @@ import Utils.Math.MathHelpers; Note that the BSDF is implemented to handle wi/wo over the full sphere. The material is treated as single-sided and the shading normal should not be flipped. */ -struct PBRTDiffuseTransmissionMaterialInstance : IMaterialInstance +struct PBRTDiffuseTransmissionMaterialInstance : MaterialInstanceBase, IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. float3 albedoR; @@ -52,6 +52,12 @@ struct PBRTDiffuseTransmissionMaterialInstance : IMaterialInstance return albedo * abs(woLocal.z) * M_1_PI; } + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg) + { + return float3(0.f); + } + bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { float3 wiLocal = sf.toLocal(sd.V); diff --git a/Source/Falcor/Rendering/Materials/RGLAcquisition.cpp b/Source/Falcor/Rendering/Materials/RGLAcquisition.cpp index ee3037ab2..94776c0c0 100644 --- a/Source/Falcor/Rendering/Materials/RGLAcquisition.cpp +++ b/Source/Falcor/Rendering/Materials/RGLAcquisition.cpp @@ -70,10 +70,10 @@ namespace Falcor : mpDevice(pDevice) , mpScene(pScene) { - checkArgument(mpDevice != nullptr, "'pDevice' must be a valid device"); - checkArgument(pScene != nullptr, "'pScene' must be a valid scene"); + FALCOR_CHECK(mpDevice != nullptr, "'pDevice' must be a valid device"); + FALCOR_CHECK(pScene != nullptr, "'pScene' must be a valid scene"); - Program::Desc descBase; + ProgramDesc descBase; descBase.addShaderModules(pScene->getShaderModules()); descBase.addShaderLibrary(kShaderFile); descBase.addTypeConformances(pScene->getTypeConformances()); @@ -97,12 +97,11 @@ namespace Falcor auto createStructured = [&](size_t elemSize, size_t count, const void* srcData = nullptr) { - return Buffer::createStructured( - mpDevice, + return mpDevice->createStructuredBuffer( uint32_t(elemSize), uint32_t(count), ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, - Buffer::CpuAccess::None, + MemoryType::DeviceLocal, srcData, false ); @@ -126,7 +125,7 @@ namespace Falcor void RGLAcquisition::acquireIsotropic(RenderContext* pRenderContext, const MaterialID materialID) { FALCOR_ASSERT(mpScene); - checkArgument(materialID.get() < mpScene->getMaterialCount(), "'materialID' is out of range"); + FALCOR_CHECK(materialID.get() < mpScene->getMaterialCount(), "'materialID' is out of range"); CpuTimer timer; timer.update(); @@ -156,7 +155,7 @@ namespace Falcor var["lumi"] = mpLumiBuffer; var["rgb"] = mpRGBBuffer; - pPass->getRootVar()["gScene"] = mpScene->getParameterBlock(); + mpScene->bindShaderData(pPass->getRootVar()["gScene"]); }; setupParams(mpRetroReflectionPass); @@ -189,11 +188,8 @@ namespace Falcor mpSumSigmaPass->execute(pRenderContext, uint3(kNDFSize.x, kNDFSize.y, 1)); // Normalize NDF and projected areas. - std::vector ndfCPU(kNDFN), sigmaCPU(kNDFN); - std::memcpy( ndfCPU.data(), mpNDFBuffer ->map(Buffer::MapType::Read), kNDFN * sizeof(float)); - std::memcpy(sigmaCPU.data(), mpSigmaBuffer->map(Buffer::MapType::Read), kNDFN * sizeof(float)); - mpNDFBuffer ->unmap(); - mpSigmaBuffer->unmap(); + std::vector ndfCPU = mpNDFBuffer->getElements(0, kNDFN); + std::vector sigmaCPU = mpSigmaBuffer->getElements(0, kNDFN); for (uint y = 0; y < kNDFSize.y; ++y) { // First entry of sigma in each row corresponds to integrating NDF(wm) with theta_i=0 @@ -215,9 +211,8 @@ namespace Falcor // Phase 4: Compute the VNDF and make it samplable. mpComputeVNDFPass->execute(pRenderContext, uint3(kVNDFSize.x, kVNDFSize.y, kVNDFSize.z * kVNDFSize.w)); - const float* vndfBuf = reinterpret_cast(mpVNDFBuffer->map(Buffer::MapType::Read)); - SamplableDistribution4D vndfDist(vndfBuf, kVNDFSize); - mpVNDFBuffer->unmap(); + std::vector vndfBuf = mpVNDFBuffer->getElements(); + SamplableDistribution4D vndfDist(vndfBuf.data(), kVNDFSize); mpVNDFBuffer ->setBlob(vndfDist.getPDF(), 0, sizeof(float) * kVNDFN); mpVNDFCondBuffer->setBlob(vndfDist.getConditional(), 0, sizeof(float) * kVNDFN); mpVNDFMargBuffer->setBlob(vndfDist.getMarginal(), 0, sizeof(float) * kVNDFN / kVNDFSize.z); @@ -233,31 +228,23 @@ namespace Falcor { RGLFile result; - auto theta = mpThetaBuffer->map(Buffer::MapType::Read); - auto phi = mpPhiBuffer ->map(Buffer::MapType::Read); - auto sigma = mpSigmaBuffer->map(Buffer::MapType::Read); - auto ndf = mpNDFBuffer ->map(Buffer::MapType::Read); - auto vndf = mpVNDFBuffer ->map(Buffer::MapType::Read); - auto rgb = mpRGBBuffer ->map(Buffer::MapType::Read); - auto lumi = mpLumiBuffer ->map(Buffer::MapType::Read); + std::vector theta = mpThetaBuffer->getElements(); + std::vector phi = mpPhiBuffer ->getElements(); + std::vector sigma = mpSigmaBuffer->getElements(); + std::vector ndf = mpNDFBuffer ->getElements(); + std::vector vndf = mpVNDFBuffer ->getElements(); + std::vector rgb = mpRGBBuffer ->getElements(); + std::vector lumi = mpLumiBuffer ->getElements(); std::string description = "Virtually measured BRDF"; result.addField("description", RGLFile::UInt8, std::vector{{uint(description.size())}}, description.c_str()); - result.addField("phi_i", RGLFile::Float32, std::vector{{kPhiSize}}, phi); - result.addField("theta_i", RGLFile::Float32, std::vector{{kThetaSize}}, theta); - result.addField("sigma", RGLFile::Float32, std::vector{{kNDFSize.y, kNDFSize.x}}, sigma); - result.addField("ndf", RGLFile::Float32, std::vector{{kNDFSize.y, kNDFSize.x}}, ndf); - result.addField("vndf", RGLFile::Float32, std::vector{{kVNDFSize.x, kVNDFSize.y, kVNDFSize.z, kVNDFSize.w}}, vndf); - result.addField("luminance", RGLFile::Float32, std::vector{{kLumiSize.x, kLumiSize.y, kLumiSize.z, kLumiSize.w}}, lumi); - result.addField("rgb", RGLFile::Float32, std::vector{{kLumiSize.x, kLumiSize.y, 3, kLumiSize.z, kLumiSize.w}}, rgb); - - mpThetaBuffer->unmap(); - mpPhiBuffer ->unmap(); - mpSigmaBuffer->unmap(); - mpNDFBuffer ->unmap(); - mpVNDFBuffer ->unmap(); - mpRGBBuffer ->unmap(); - mpLumiBuffer ->unmap(); + result.addField("phi_i", RGLFile::Float32, std::vector{{kPhiSize}}, phi.data()); + result.addField("theta_i", RGLFile::Float32, std::vector{{kThetaSize}}, theta.data()); + result.addField("sigma", RGLFile::Float32, std::vector{{kNDFSize.y, kNDFSize.x}}, sigma.data()); + result.addField("ndf", RGLFile::Float32, std::vector{{kNDFSize.y, kNDFSize.x}}, ndf.data()); + result.addField("vndf", RGLFile::Float32, std::vector{{kVNDFSize.x, kVNDFSize.y, kVNDFSize.z, kVNDFSize.w}}, vndf.data()); + result.addField("luminance", RGLFile::Float32, std::vector{{kLumiSize.x, kLumiSize.y, kLumiSize.z, kLumiSize.w}}, lumi.data()); + result.addField("rgb", RGLFile::Float32, std::vector{{kLumiSize.x, kLumiSize.y, 3, kLumiSize.z, kLumiSize.w}}, rgb.data()); return result; } diff --git a/Source/Falcor/Rendering/Materials/RGLAcquisition.cs.slang b/Source/Falcor/Rendering/Materials/RGLAcquisition.cs.slang index 5fde9cc96..dea0d41da 100644 --- a/Source/Falcor/Rendering/Materials/RGLAcquisition.cs.slang +++ b/Source/Falcor/Rendering/Materials/RGLAcquisition.cs.slang @@ -42,7 +42,7 @@ struct RGLAcquisition uint2 ndfSize; uint4 vndfSize; uint4 lumiSize; - + RWStructuredBuffer ndfDirections; // Size ndfSize.x * ndfSize.y RWStructuredBuffer retroReflection; // Size ndfSize.x * ndfSize.y RWStructuredBuffer ndfKernel; // Size (ndfSize.x * ndfSize.y) ^ 2 @@ -52,16 +52,16 @@ struct RGLAcquisition RWStructuredBuffer thetas; // Size thetaSize RWStructuredBuffer phis; // Size phiSize RWStructuredBuffer vndf; // Size phiSize * thetaSize * vndfSize.z * vndfSize.w (vndfSize.xy == phiSize, thetaSize) - + ByteAddressBuffer vndfBuf; // Size phiSize * thetaSize * vndfSize.z * vndfSize.w ByteAddressBuffer vndfMarginalBuf; // Size phiSize * thetaSize * vndfSize.z * vndfSize.w ByteAddressBuffer vndfConditionalBuf; // Size phiSize * thetaSize * vndfSize.z * vndfSize.w - + RWBuffer lumi; // Size phiSize * thetaSize * lumiSize.z * lumiSize.w (lumiSize.xy == phiSize, thetaSize) RWBuffer rgb; // Size phiSize * thetaSize * lumiSize.z * lumiSize.w static const uint2 kSampleCount = {8, 8}; - + ShadingData buildShadingData(float3 wi) { ShadingData sd = {}; @@ -71,10 +71,9 @@ struct RGLAcquisition sd.mtl = gScene.materials.getMaterialHeader(materialID); sd.materialID = materialID; - sd.opacity = 1.0f; sd.IoR = 1.0f; sd.V = wi; - + return sd; } @@ -84,23 +83,23 @@ struct RGLAcquisition { return normalize(float3(x.xy, max(x.z, kMinCosTheta * 2.0f))); } - + void measureRetroreflection(const uint2 threadID) { float2 unitI = float2(threadID) / float2(ndfSize - 1); float3 wi = clampDirection(fromSpherical(unitToSpherical(unitI))); - + ShadingData sd = buildShadingData(wi); - + let lod = ExplicitLodTextureSampler(0.f); let mi = gScene.materials.getMaterialInstance(sd, lod); - + // TODO: We should probably take multiple samples if eval is actually probabilistic. // No way currently to find out if eval is deterministic or not. UniformSampleGenerator sg = UniformSampleGenerator(threadID, 0); // The paper measures at lambda=532nm. Just grab the green channel to approximate this. float retro = mi.eval(sd, wi, sg).g; - + uint idx = threadID.x + threadID.y * ndfSize.x; ndfDirections[idx] = wi; retroReflection[idx] = retro; @@ -110,7 +109,7 @@ struct RGLAcquisition { float3 wj = ndfDirections[threadID.y]; float3 wk = ndfDirections[threadID.x]; - + uint N = ndfSize.x * ndfSize.y; ndfKernel[threadID.x + threadID.y * N] = (1.0f / N) * retroReflection[threadID.y] * saturate(dot(wk, wj)); } @@ -119,7 +118,7 @@ struct RGLAcquisition { uint N = ndfSize.x * ndfSize.y; uint idx = threadID.x + threadID.y * ndfSize.x; - + float result = 0.0f; for (int i = 0; i < N; ++i) { @@ -127,25 +126,25 @@ struct RGLAcquisition } ndfTmp[idx] = result; } - + float interpolate(RWStructuredBuffer buf, float2 uv, uint2 size) { uv *= size - 1; int2 xy = clamp(int2(uv), int2(0), size - 2); uv -= xy; - + float v00 = buf[(xy.x + 0) + (xy.y + 0) * size.x]; float v10 = buf[(xy.x + 1) + (xy.y + 0) * size.x]; float v01 = buf[(xy.x + 0) + (xy.y + 1) * size.x]; float v11 = buf[(xy.x + 1) + (xy.y + 1) * size.x]; - + return lerp(lerp(v00, v10, uv.x), lerp(v01, v11, uv.x), uv.y); } float evalNDF (float2 uv) { return interpolate(ndf, uv, ndfSize); } float evalSigma(float2 uv) { return interpolate(sigma, uv, ndfSize); } - + void integrateSigma(const uint3 threadID) { uint N = ndfSize.x * ndfSize.y; @@ -172,7 +171,7 @@ struct RGLAcquisition } } binIntegral *= 1.0f / (kSampleCount.x * kSampleCount.y); - + float partialSum = WaveActiveSum(binIntegral / WaveGetLaneCount()); if (WaveIsFirstLane()) { @@ -186,7 +185,7 @@ struct RGLAcquisition uint N = ndfSize.x * ndfSize.y; uint M = sigmaIntegrationGrid.x * sigmaIntegrationGrid.y / WaveGetLaneCount(); uint base = threadID.x + threadID.y * ndfSize.x; - + float sum = 0.0f; for (int i = 0; i < M; ++i) { @@ -194,7 +193,7 @@ struct RGLAcquisition } sigma[base] = sum / M; } - + // TODO: Only works for the isotropic case. It's not entirely clear from the paper how // to compute the incident parametrization in the anisotropic case. // Only support isotropic measurements for now. Will have to contact authors if anisotropy @@ -217,7 +216,7 @@ struct RGLAcquisition float b = sigma[i - 1]; float u = a == b ? 0.0f : (x - a) / (b - a); u = threadID == 0 ? 0.0f : (i - u) / (ndfSize.x - 1); - + thetas[threadID] = unitToSpherical(float2(u, 0.0f)).x; } @@ -225,55 +224,55 @@ struct RGLAcquisition { float2 sphericalI = float2(thetas[threadID.y], phis[threadID.x]); float2 unitI = sphericalToUnit(sphericalI); - + uint2 idxH = uint2(threadID.z % vndfSize.z, threadID.z / vndfSize.z); float2 unitH = float2(idxH) / float2(vndfSize.zw - 1); - + float3 wi = fromSpherical(sphericalI); float3 h = fromSpherical(unitToSpherical(unitH)); - + uint vndfIdx = (threadID.x * vndfSize.y + threadID.y) * vndfSize.z * vndfSize.w + threadID.z; float jacobian = (2.0f * M_PI * M_PI * unitH.x * length(h.xy)); vndf[vndfIdx] = jacobian * saturate(dot(wi, h)) * evalNDF(unitH) / evalSigma(unitI); } - + void acquireBRDF(const uint3 threadID) { uint2 idxI = threadID.xy; uint2 idxH = uint2(threadID.z % lumiSize.z, threadID.z / lumiSize.z); - + float2 slice = float2(idxI); float2 xi = float2(idxH) / float2(lumiSize.zw - 1); - + let vndf = InterpolatedDistribution2D(vndfSize, vndfMarginalBuf, vndfConditionalBuf, vndfBuf); - + float3 unitHPdf = vndf.sample(slice, xi); float2 unitH = unitHPdf.xy; - + float2 sphericalI = float2(thetas[idxI.y], phis[idxI.x]); float2 sphericalH = unitToSpherical(unitH); - + float2 unitI = sphericalToUnit(sphericalI); - + float3 wi = fromSpherical(sphericalI); float3 h = fromSpherical(sphericalH); float3 wo = h * 2.0f * dot(h, wi) - wi; - + float3 fr; if (wo.z > 0.0f) { wi = clampDirection(wi); wo = clampDirection(wo); ShadingData sd = buildShadingData(wi); - + let lod = ExplicitLodTextureSampler(0.0f); let mi = gScene.materials.getMaterialInstance(sd, lod); - + // TODO: We should probably take multiple samples if eval is actually probabilistic. // No way currently to find out if eval is deterministic or not. UniformSampleGenerator sg = UniformSampleGenerator(uint2(threadID.x * lumiSize.y + threadID.y, threadID.z), 0); - + fr = mi.eval(sd, wo, sg); } else @@ -282,11 +281,11 @@ struct RGLAcquisition } fr *= 4.0f * evalSigma(unitI) / evalNDF(unitH); - + uint sliceSize = lumiSize.z * lumiSize.w; uint lumiBase = (threadID.x * lumiSize.y + threadID.y) * sliceSize + threadID.z; uint rgbBase = (threadID.x * lumiSize.y + threadID.y) * sliceSize * 3 + threadID.z; - + lumi[lumiBase] = luminance(fr); rgb[rgbBase + sliceSize * 0] = fr.x; rgb[rgbBase + sliceSize * 1] = fr.y; @@ -301,7 +300,7 @@ ParameterBlock gAcquisition; void measureRetroreflection(uint3 threadID : SV_DispatchThreadID) { if (any(threadID.xy >= gAcquisition.ndfSize)) return; - + gAcquisition.measureRetroreflection(threadID.xy); } @@ -310,7 +309,7 @@ void buildPowerIterationKernel(uint3 threadID : SV_DispatchThreadID) { int N = gAcquisition.ndfSize.x * gAcquisition.ndfSize.y; if (any(threadID >= N)) return; - + gAcquisition.buildPowerIterationKernel(threadID.xy); } @@ -318,7 +317,7 @@ void buildPowerIterationKernel(uint3 threadID : SV_DispatchThreadID) void powerIteration(uint3 threadID : SV_DispatchThreadID) { if (any(threadID.xy >= gAcquisition.ndfSize)) return; - + gAcquisition.powerIteration(threadID.xy); } @@ -327,7 +326,7 @@ void integrateSigma(uint3 threadID : SV_DispatchThreadID) { int N = gAcquisition.ndfSize.x * gAcquisition.ndfSize.y; if (any(threadID >= int3(gAcquisition.sigmaIntegrationGrid, N))) return; - + gAcquisition.integrateSigma(threadID); } @@ -335,7 +334,7 @@ void integrateSigma(uint3 threadID : SV_DispatchThreadID) void sumSigma(uint3 threadID : SV_DispatchThreadID) { if (any(threadID.xy >= gAcquisition.ndfSize)) return; - + gAcquisition.sumSigma(threadID.xy); } @@ -343,7 +342,7 @@ void sumSigma(uint3 threadID : SV_DispatchThreadID) void computeTheta(uint3 threadID : SV_DispatchThreadID) { if (threadID.x >= gAcquisition.thetaSize) return; - + gAcquisition.computeTheta(threadID.x); } @@ -352,7 +351,7 @@ void computeVNDF(uint3 threadID : SV_DispatchThreadID) { int N = gAcquisition.vndfSize.z * gAcquisition.vndfSize.w; if (any(threadID >= int3(gAcquisition.vndfSize.xy, N))) return; - + gAcquisition.computeVNDF(threadID); } @@ -361,6 +360,6 @@ void acquireBRDF(uint3 threadID : SV_DispatchThreadID) { int N = gAcquisition.lumiSize.z * gAcquisition.lumiSize.w; if (any(threadID >= int3(gAcquisition.lumiSize.xy, N))) return; - + gAcquisition.acquireBRDF(threadID); } diff --git a/Source/Falcor/Rendering/Materials/RGLMaterial.slang b/Source/Falcor/Rendering/Materials/RGLMaterial.slang index 74479fdb8..ca1056c43 100644 --- a/Source/Falcor/Rendering/Materials/RGLMaterial.slang +++ b/Source/Falcor/Rendering/Materials/RGLMaterial.slang @@ -81,6 +81,10 @@ struct RGLMaterial : MaterialBase, IMaterial return RGLMaterialInstance(sf, slice, sigmaEval, albedo, data); } - // Alpha testing is not supported by this material. - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) { return 1.f; } + [Differentiable] + RGLMaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) + { + diffData = DiffMaterialData(); + return no_diff setupMaterialInstance(ms, sd, lod, hints); + } }; diff --git a/Source/Falcor/Rendering/Materials/RGLMaterialInstance.slang b/Source/Falcor/Rendering/Materials/RGLMaterialInstance.slang index 64f444d60..302c60011 100644 --- a/Source/Falcor/Rendering/Materials/RGLMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/RGLMaterialInstance.slang @@ -35,7 +35,7 @@ import Scene.Scene; /** Implementation of the BSDF for the measured RGL material. */ -struct RGLMaterialInstance : IMaterialInstance +struct RGLMaterialInstance : MaterialInstanceBase, IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. float2 slice; @@ -100,6 +100,12 @@ struct RGLMaterialInstance : IMaterialInstance return f; } + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg) + { + return float3(0.f); + } + bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { float3 wiLocal = sf.toLocal(sd.V); diff --git a/Source/Falcor/Rendering/Materials/StandardMaterial.slang b/Source/Falcor/Rendering/Materials/StandardMaterial.slang index e5972aa9c..0c0b3de8f 100644 --- a/Source/Falcor/Rendering/Materials/StandardMaterial.slang +++ b/Source/Falcor/Rendering/Materials/StandardMaterial.slang @@ -27,9 +27,9 @@ **************************************************************************/ __exported import Rendering.Materials.IMaterial; __exported import Rendering.Materials.StandardMaterialInstance; +import Scene.Material.StandardMaterialParamLayout; import Scene.Material.BasicMaterialData; import Scene.Material.ShadingUtils; -import Scene.Scene; /** Implementation of Falcor's standard surface material. @@ -94,11 +94,13 @@ struct StandardMaterial : MaterialBase, IMaterial } #endif +#if defined(SCENE_DIFFUSE_ALBEDO_MULTIPLIER) // DEMO21 - if (Scene::kDiffuseAlbedoMultiplier != 1.f) + if (SCENE_DIFFUSE_ALBEDO_MULTIPLIER != 1.f) { - d.diffuse = saturate(d.diffuse * Scene::kDiffuseAlbedoMultiplier); + d.diffuse = saturate(d.diffuse * SCENE_DIFFUSE_ALBEDO_MULTIPLIER); } +#endif // Compute final shading frame. ShadingFrame sf = sd.frame; @@ -122,12 +124,128 @@ struct StandardMaterial : MaterialBase, IMaterial emission = ms.sampleTexture(data.texEmissive, s, sd.uv, float4(data.emissive, 1), lod).rgb * data.emissiveFactor; } + d.volumeAnsiotropy = data.volumeAnisotropy; + d.volumeScattering = data.volumeScattering; + d.hasEntryPointVolumeProperties = data.getHasEntryPointVolumeProperties(); + d.hasSigmaSGreaterZero = (any(data.volumeScattering) > 0.f); + return StandardMaterialInstance(sf, d, emission); } - float evalOpacity(const MaterialSystem ms, const VertexData v, const ITextureSampler lod) + [Differentiable] + StandardMaterialInstance setupDiffMaterialInstance(out DiffMaterialData diffData, const MaterialSystem ms, const ShadingData sd, const ITextureSampler lod, const uint hints) { SamplerState s = ms.getTextureSampler(header.getDefaultTextureSamplerID()); - return ms.sampleTexture(data.texBaseColor, s, v.texC, data.baseColor, lod).a; + + StandardBSDFData d = {}; + + uint hashIndex = hashFunction(sd.threadID, gSceneGradients.getHashSize(GradientType::Material)); + GradientIOWrapper gradIO = GradientIOWrapper(GradientType::Material, sd.materialGradOffset, hashIndex); + + float16_t _IoR = no_diff header.getIoR(); + float IoR = gradIO.getFloat(float(_IoR), StandardMaterialParamLayout.ior); + d.eta = sd.frontFacing ? (sd.IoR / IoR) : (IoR / sd.IoR); + d.diffuseTransmission = gradIO.getFloat(data.diffuseTransmission, StandardMaterialParamLayout.diffuseTransmission); + d.specularTransmission = gradIO.getFloat(data.specularTransmission, StandardMaterialParamLayout.specularTransmission); + + // TODO: Handle texture gradients. + if (d.diffuseTransmission > 0.f || d.specularTransmission > 0.f) + { + float4 _transmission = no_diff ms.sampleTexture(data.texTransmission, s, sd.uv, float4(data.transmission, 0.f), lod); + d.transmission = gradIO.getFloat(_transmission.rgb, StandardMaterialParamLayout.transmissionColor); + } + + // Calculate the specular reflectance for dielectrics from the IoR, as in the Disney BSDF [Burley 2015]. + // UE4 uses 0.08 multiplied by a default specular value of 0.5, hence F0=0.04 as default. The default IoR=1.5 gives the same result. + float f = (IoR - 1.f) / (IoR + 1.f); + float F0 = f * f; + + // Sample base color. + const float4 _baseColor = no_diff ms.sampleTexture(data.texBaseColor, s, sd.uv, data.baseColor, lod); + float3 baseColor = gradIO.getFloat(_baseColor.rgb, StandardMaterialParamLayout.baseColor); + + // Sample the specular texture. + // Depending on the shading model this encodes the material parameters differently. + const float4 _spec = no_diff ms.sampleTexture(data.texSpecular, s, sd.uv, data.specular, lod); + +#if MATERIAL_SYSTEM_HAS_SPEC_GLOSS_MATERIALS + if (data.getShadingModel() == ShadingModel::MetalRough) +#endif + { + // G - Roughness; B - Metallic + float roughness = gradIO.getFloat(_spec.g, StandardMaterialParamLayout.roughness); + float metallic = gradIO.getFloat(_spec.b, StandardMaterialParamLayout.metallic); + d.diffuse = lerp(baseColor, float3(0), metallic); + d.specular = lerp(float3(F0), baseColor, metallic); + d.roughness = roughness; + d.metallic = metallic; + } +#if MATERIAL_SYSTEM_HAS_SPEC_GLOSS_MATERIALS + else // ShadingModel::SpecGloss + { + d.diffuse = baseColor; + d.specular = _spec.rgb; + d.roughness = 1 - _spec.a; + d.metallic = getMetallic(d.diffuse, d.specular); + } +#endif + +#if defined(SCENE_DIFFUSE_ALBEDO_MULTIPLIER) + // DEMO21 + if (SCENE_DIFFUSE_ALBEDO_MULTIPLIER != 1.f) + { + d.diffuse = saturate(d.diffuse * SCENE_DIFFUSE_ALBEDO_MULTIPLIER); + } +#endif + + // Compute final shading frame. + ShadingFrame sf = sd.frame; + if (isNormalMappingEnabled(hints)) + { + float3 encodedNormal = ms.sampleTexture(data.texNormalMap, s, sd.uv, float4(0.f), lod).rgb; + // TODO: Allow differentiation here. + // We don't support it because differentiating normal maps is harder due to additional discontinuities. + sf = no_diff computeShadingFrameFromNormalMap(sd, data.getNormalMapType(), encodedNormal); + } + flipShadingNormal(sd, sf); + if (isAdjustShadingNormalEnabled(hints)) + { + no_diff adjustShadingNormal(sd, sf); + } + + // Sample the emissive texture. + // The standard material supports uniform emission over the hemisphere. + // Note that the material is only emissive on the front-facing side. + float3 emission = {}; + if (sd.frontFacing) + { + float4 _emission = no_diff ms.sampleTexture(data.texEmissive, s, sd.uv, float4(data.emissive, 1), lod); + emission = gradIO.getFloat(_emission.rgb, StandardMaterialParamLayout.emissiveColor); + emission *= gradIO.getFloat(data.emissiveFactor, StandardMaterialParamLayout.emissiveFactor); + } + + StandardMaterialInstance mi = StandardMaterialInstance(sf, d, emission); + diffData.sf = sf; + + // The data layout below follows the `StandardBSDFData` struct, which is different from the layout of `StandardMaterial`. + uint offset = 0; + // 0--3: diffuse albedo + diffData.write(d.diffuse, offset); + // 3--6: specular albedo + diffData.write(d.specular, offset); + // 6--9: roughness, metallic, eta + diffData.write(d.roughness, offset); + diffData.write(d.metallic, offset); + diffData.write(d.eta, offset); + // 9--12: transmission + diffData.write(d.transmission, offset); + // 12: diffuse transmission + diffData.write(d.diffuseTransmission, offset); + // 13: specular transmission + diffData.write(d.specularTransmission, offset); + // 14--17: emission + diffData.write(emission, offset); + + return mi; } }; diff --git a/Source/Falcor/Rendering/Materials/StandardMaterialInstance.slang b/Source/Falcor/Rendering/Materials/StandardMaterialInstance.slang index 51a6f9bbd..9a6b92514 100644 --- a/Source/Falcor/Rendering/Materials/StandardMaterialInstance.slang +++ b/Source/Falcor/Rendering/Materials/StandardMaterialInstance.slang @@ -29,7 +29,11 @@ __exported import Rendering.Materials.IMaterialInstance; __exported import Rendering.Materials.BSDFs.StandardBSDF; +__exported import DiffRendering.SceneGradients; +__exported import DiffRendering.GradientIOWrapper; import Utils.Math.MathHelpers; +import Rendering.Volumes.PhaseFunction; +import Scene.Material.VolumeProperties; /** Implementation of Falcor's standard surface BSDF. @@ -43,7 +47,7 @@ import Utils.Math.MathHelpers; The BSDF is a linear combination of the above lobes. */ -struct StandardMaterialInstance : IMaterialInstance +struct StandardMaterialInstance : MaterialInstanceBase, IMaterialInstance { ShadingFrame sf; ///< Shading frame in world space. StandardBSDFData data; ///< BSDF parameters. @@ -68,6 +72,20 @@ struct StandardMaterialInstance : IMaterialInstance return bsdf.eval(wiLocal, woLocal, sg); } + [Differentiable] + float3 evalAD(const DiffMaterialData diffData, const ShadingData sd, const float3 wo, inout S sg) + { + float3 wiLocal = diffData.sf.toLocal(sd.V); + float3 woLocal = diffData.sf.toLocal(wo); + + if (!isValidHemisphereReflectionOrTransmission(sd, sf, wiLocal, woLocal, wo)) return float3(0.f); + + StandardBSDFData dataAD = StandardBSDFData(diffData); + StandardBSDF bsdfAD = StandardBSDF(wiLocal, sd.mtl, dataAD); + + return bsdfAD.eval(wiLocal, woLocal, sg); + } + bool sample(const ShadingData sd, inout S sg, out BSDFSample result, bool useImportanceSampling = true) { float3 wiLocal = sf.toLocal(sd.V); @@ -141,6 +159,32 @@ struct StandardMaterialInstance : IMaterialInstance return StandardBSDF::getLobeTypes(data); } + bool hasVolumeProperties() + { + return data.hasEntryPointVolumeProperties; + } + + VolumeProperties getVolumeProperties() + { + VolumeProperties result; + if (data.hasSigmaSGreaterZero) + { + // Convert color to sigmaA. See MxLayeredMaterialInstance fro details + const float g = data.volumeAnsiotropy; + const float3 c = data.diffuse; + const float3 s = 4.09712f + 4.20863f*c - sqrt(9.59217f + 41.6808f * c + 17.7126 * c * c); + const float3 alpha = (1.f - s*s) / (1.f - g * s*s); + + result.sigmaA = select(alpha > 0.f, (1.f - alpha) * (data.volumeScattering) / alpha, 0.f); + } + else + result.sigmaA = 0.f; + + result.sigmaS = data.volumeScattering; + result.phaseFunction = NullPhaseFunction(); // g is taken from the volume and set in InternalPathTracer + return result; + + } // Additional functions @@ -157,6 +201,9 @@ struct StandardMaterialInstance : IMaterialInstance */ bool sampleReference(const ShadingData sd, const float3 wi, out float3 wo, out float pdf, out float3 weight, out uint lobeType, inout S sg) { + weight = {}; + lobeType = {}; + const bool isTransmissive = (getLobeTypes(sd) & (uint)LobeType::Transmission) != 0; wo = sample_cosine_hemisphere_concentric(sampleNext2D(sg), pdf); // pdf = cos(theta) / pi diff --git a/Source/Falcor/Rendering/Materials/TexLODTypes.slang b/Source/Falcor/Rendering/Materials/TexLODTypes.slang index 7510507fc..c079be237 100644 --- a/Source/Falcor/Rendering/Materials/TexLODTypes.slang +++ b/Source/Falcor/Rendering/Materials/TexLODTypes.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Rendering/RTXDI/RTXDI.cpp b/Source/Falcor/Rendering/RTXDI/RTXDI.cpp index eaf50bc6c..aef6e29e1 100644 --- a/Source/Falcor/Rendering/RTXDI/RTXDI.cpp +++ b/Source/Falcor/Rendering/RTXDI/RTXDI.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "RTXDI.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/API/RenderContext.h" #include "Utils/Logger.h" #include "Utils/Math/Common.h" @@ -42,7 +42,6 @@ namespace Falcor const std::string kRTXDIShadersFile = "Rendering/RTXDI/RTXDISetup.cs.slang"; const std::string kLightUpdaterShaderFile = "Rendering/RTXDI/LightUpdater.cs.slang"; const std::string kEnvLightUpdaterShaderFile = "Rendering/RTXDI/EnvLightUpdater.cs.slang"; - const std::string kShaderModel = "6_5"; /** Config setting : Maximum number of unique screen-sized reservoir bufers needed by any RTXDI pipelines we create in this pass. Just controls memory allocation (not really perf). @@ -87,6 +86,9 @@ namespace Falcor , mpDevice(mpScene->getDevice()) , mOptions(options) { + if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_5)) + FALCOR_THROW("RTXDI requires Shader Model 6.5 support."); + mpPixelDebug = std::make_unique(mpDevice); FALCOR_ASSERT(pScene); @@ -155,16 +157,24 @@ namespace Falcor return defines; } - void RTXDI::setShaderData(const ShaderVar& rootVar) + void RTXDI::bindShaderData(const ShaderVar& rootVar) { #if FALCOR_HAS_RTXDI - setShaderDataInternal(rootVar, nullptr); + bindShaderDataInternal(rootVar, nullptr, false); #endif } void RTXDI::beginFrame(RenderContext* pRenderContext, const uint2& frameDim) { #if FALCOR_HAS_RTXDI + // Check for scene changes that require shader recompilation. + // TODO: We may want to reset other data that depends on the scene geometry or materials. + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded) || + is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) + { + mFlags.recompileShaders = true; + } + // Make sure the light collection is created. mpScene->getLightCollection(pRenderContext); @@ -197,7 +207,7 @@ namespace Falcor // Emissive lights. if (is_set(updates, Scene::UpdateFlags::LightCollectionChanged)) mFlags.updateEmissiveLights = true; - if (is_set(updates, Scene::UpdateFlags::MaterialsChanged)) mFlags.updateEmissiveLightsFlux = true; + if (is_set(updates, Scene::UpdateFlags::EmissiveMaterialsChanged)) mFlags.updateEmissiveLightsFlux = true; // Analytic lights. if (is_set(updates, Scene::UpdateFlags::LightCountChanged)) mFlags.updateAnalyticLights = true; if (is_set(updates, Scene::UpdateFlags::LightPropertiesChanged)) mFlags.updateAnalyticLights = true; @@ -281,7 +291,7 @@ namespace Falcor #if FALCOR_HAS_RTXDI - void RTXDI::setShaderDataInternal(const ShaderVar& rootVar, const ref& pMotionVectors) + void RTXDI::bindShaderDataInternal(const ShaderVar& rootVar, const ref& pMotionVectors, bool bindScene) { auto var = rootVar["gRTXDI"]; @@ -340,6 +350,10 @@ namespace Falcor var["localLightPdfTexture"] = mpLocalLightPdfTexture; var["envLightLuminanceTexture"] = mpEnvLightLuminanceTexture; var["envLightPdfTexture"] = mpEnvLightPdfTexture; + + // Bind the scene. + if (bindScene) + mpScene->bindShaderData(rootVar["gScene"]); } void RTXDI::updateLights(RenderContext* pRenderContext) @@ -389,7 +403,7 @@ namespace Falcor // Create GPU buffer for holding light IDs. if (!mLights.analyticLightIDs.empty() && (!mpAnalyticLightIDBuffer || mpAnalyticLightIDBuffer->getElementCount() < mLights.analyticLightIDs.size())) { - mpAnalyticLightIDBuffer = Buffer::createStructured(mpDevice, sizeof(uint32_t), (uint32_t)mLights.analyticLightIDs.size()); + mpAnalyticLightIDBuffer = mpDevice->createStructuredBuffer(sizeof(uint32_t), (uint32_t)mLights.analyticLightIDs.size()); } // Update GPU buffer. @@ -414,7 +428,7 @@ namespace Falcor // Allocate buffer for light infos. if (!mpLightInfoBuffer || mpLightInfoBuffer->getElementCount() < totalLightCount) { - mpLightInfoBuffer = Buffer::createStructured(mpDevice, mpReflectTypes->getRootVar()["lightInfo"], totalLightCount); + mpLightInfoBuffer = mpDevice->createStructuredBuffer(mpReflectTypes->getRootVar()["lightInfo"], totalLightCount); } // Allocate local light PDF texture, which RTXDI uses for importance sampling. @@ -423,9 +437,9 @@ namespace Falcor rtxdi::ComputePdfTextureSize(localLightCount, width, height, mipLevels); if (!mpLocalLightPdfTexture || mpLocalLightPdfTexture->getWidth() != width || mpLocalLightPdfTexture->getHeight() != height || mpLocalLightPdfTexture->getMipCount() != mipLevels) { - mpLocalLightPdfTexture = Texture::create2D(mpDevice, width, height, + mpLocalLightPdfTexture = mpDevice->createTexture2D(width, height, ResourceFormat::R16Float, 1, mipLevels, nullptr, - Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget); + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::RenderTarget); } } @@ -463,7 +477,7 @@ namespace Falcor var["updateEmissiveLightsFlux"] = mFlags.updateEmissiveLightsFlux; var["updateAnalyticLights"] = mFlags.updateAnalyticLights; var["updateAnalyticLightsFlux"] = mFlags.updateAnalyticLightsFlux; - mpUpdateLightsPass->getRootVar()["gScene"] = mpScene->getParameterBlock(); + mpScene->bindShaderData(mpUpdateLightsPass->getRootVar()["gScene"]); mpUpdateLightsPass->execute(pRenderContext, threadCount.x, threadCount.y); } @@ -502,17 +516,17 @@ namespace Falcor // Create luminance texture if it doesn't exist yet or has the wrong dimensions. if (!pLuminanceTexture || pLuminanceTexture->getWidth() != width || pLuminanceTexture->getHeight() != height) { - pLuminanceTexture = Texture::create2D( - mpDevice, width, height, ResourceFormat::R32Float, 1, 1, nullptr, - Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget); + pLuminanceTexture = mpDevice->createTexture2D( + width, height, ResourceFormat::R32Float, 1, 1, nullptr, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::RenderTarget); } // Create pdf texture if it doesn't exist yet or has the wrong dimensions. if (!pPdfTexture || pPdfTexture->getWidth() != width || pPdfTexture->getHeight() != height) { - pPdfTexture = Texture::create2D( - mpDevice, width, height, ResourceFormat::R32Float, 1, Resource::kMaxPossible, nullptr, - Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget); + pPdfTexture = mpDevice->createTexture2D( + width, height, ResourceFormat::R32Float, 1, Resource::kMaxPossible, nullptr, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::RenderTarget); } // Update env light textures. @@ -520,7 +534,7 @@ namespace Falcor var["envLightLuminance"] = pLuminanceTexture; var["envLightPdf"] = pPdfTexture; var["texDim"] = uint2(width, height); - mpUpdateEnvLightPass->getRootVar()["gScene"] = mpScene->getParameterBlock(); + mpScene->bindShaderData(mpUpdateEnvLightPass->getRootVar()["gScene"]); mpUpdateEnvLightPass->execute(pRenderContext, width, height); // Create a mipmap chain for pdf texure. @@ -537,7 +551,7 @@ namespace Falcor // Presample local lights. { auto var = mpPresampleLocalLightsPass->getRootVar(); - setShaderDataInternal(var, nullptr); + bindShaderDataInternal(var, nullptr); mpPresampleLocalLightsPass->execute(pRenderContext, mRTXDIContextParams.TileSize, mRTXDIContextParams.TileCount); } @@ -545,7 +559,7 @@ namespace Falcor if (mLights.envLightPresent) { auto var = mpPresampleEnvLightPass->getRootVar(); - setShaderDataInternal(var, nullptr); + bindShaderDataInternal(var, nullptr); mpPresampleEnvLightPass->execute(pRenderContext, mRTXDIContextParams.EnvironmentTileSize, mRTXDIContextParams.EnvironmentTileCount); } } @@ -558,7 +572,7 @@ namespace Falcor mpPixelDebug->prepareProgram(mpGenerateCandidatesPass->getProgram(), var); var["CB"]["gOutputReservoirID"] = outputReservoirID; - setShaderDataInternal(var, nullptr); + bindShaderDataInternal(var, nullptr); mpGenerateCandidatesPass->execute(pRenderContext, mFrameDim.x, mFrameDim.y); } @@ -570,7 +584,7 @@ namespace Falcor auto var = mpTestCandidateVisibilityPass->getRootVar(); var["CB"]["gOutputReservoirID"] = candidateReservoirID; - setShaderDataInternal(var, nullptr); + bindShaderDataInternal(var, nullptr); mpTestCandidateVisibilityPass->execute(pRenderContext, mFrameDim.x, mFrameDim.y); } @@ -589,7 +603,7 @@ namespace Falcor { var["CB"]["gInputReservoirID"] = inputID; var["CB"]["gOutputReservoirID"] = outputID; - setShaderDataInternal(var, nullptr); + bindShaderDataInternal(var, nullptr); mpSpatialResamplingPass->execute(pRenderContext, mFrameDim.x, mFrameDim.y); // Ping pong our input and output buffers. (Generally between reservoirs 0 & 1). @@ -614,7 +628,7 @@ namespace Falcor var["CB"]["gTemporalReservoirID"] = lastFrameReservoirID; var["CB"]["gInputReservoirID"] = candidateReservoirID; var["CB"]["gOutputReservoirID"] = outputReservoirID; - setShaderDataInternal(var, pMotionVectors); + bindShaderDataInternal(var, pMotionVectors); mpTemporalResamplingPass->execute(pRenderContext, mFrameDim.x, mFrameDim.y); return outputReservoirID; @@ -634,7 +648,7 @@ namespace Falcor var["CB"]["gTemporalReservoirID"] = lastFrameReservoirID; var["CB"]["gInputReservoirID"] = candidateReservoirID; var["CB"]["gOutputReservoirID"] = outputReservoirID; - setShaderDataInternal(var, pMotionVectors); + bindShaderDataInternal(var, pMotionVectors); mpSpatiotemporalResamplingPass->execute(pRenderContext, mFrameDim.x, mFrameDim.y); return outputReservoirID; @@ -657,15 +671,12 @@ namespace Falcor DefineList defines = mpScene->getSceneDefines(); defines.add("RTXDI_INSTALLED", "1"); - Program::Desc desc; + ProgramDesc desc; desc.addShaderModules(mpScene->getShaderModules()); desc.addShaderLibrary(file); - desc.setShaderModel(kShaderModel); desc.csEntry(entryPoint); desc.addTypeConformances(mpScene->getTypeConformances()); ref pPass = ComputePass::create(mpDevice, desc, defines); - pPass->setVars(nullptr); - pPass->getRootVar()["gScene"] = mpScene->getParameterBlock(); return pPass; }; @@ -714,7 +725,7 @@ namespace Falcor uint32_t lightTileSampleCount = std::max(mpRTXDIContext->GetRisBufferElementCount(), 1u); if (!mpLightTileBuffer || mpLightTileBuffer->getElementCount() < lightTileSampleCount) { - mpLightTileBuffer = Buffer::createTyped(mpDevice, ResourceFormat::RG32Uint, lightTileSampleCount); + mpLightTileBuffer = mpDevice->createTypedBuffer(ResourceFormat::RG32Uint, lightTileSampleCount); } // Allocate buffer for compact light info used to improve coherence for presampled light tiles. @@ -722,7 +733,7 @@ namespace Falcor uint32_t elementCount = lightTileSampleCount * 2; if (!mpCompactLightInfoBuffer || mpCompactLightInfoBuffer->getElementCount() < elementCount) { - mpCompactLightInfoBuffer = Buffer::createStructured(mpDevice, mpReflectTypes->getRootVar()["lightInfo"], elementCount); + mpCompactLightInfoBuffer = mpDevice->createStructuredBuffer(mpReflectTypes->getRootVar()["lightInfo"], elementCount); } } @@ -731,7 +742,7 @@ namespace Falcor uint32_t elementCount = mpRTXDIContext->GetReservoirBufferElementCount() * kMaxReservoirs; if (!mpReservoirBuffer || mpReservoirBuffer->getElementCount() < elementCount) { - mpReservoirBuffer = Buffer::createStructured(mpDevice, mpReflectTypes->getRootVar()["reservoirs"], elementCount); + mpReservoirBuffer = mpDevice->createStructuredBuffer(mpReflectTypes->getRootVar()["reservoirs"], elementCount); } } @@ -740,7 +751,7 @@ namespace Falcor uint32_t elementCount = 2 * mFrameDim.x * mFrameDim.y; if (!mpSurfaceDataBuffer || mpSurfaceDataBuffer->getElementCount() < elementCount) { - mpSurfaceDataBuffer = Buffer::createStructured(mpDevice, mpReflectTypes->getRootVar()["surfaceData"], elementCount); + mpSurfaceDataBuffer = mpDevice->createStructuredBuffer(mpReflectTypes->getRootVar()["surfaceData"], elementCount); } } @@ -749,12 +760,11 @@ namespace Falcor { std::vector offsets(2 * (size_t)mRTXDIContextParams.NeighborOffsetCount); mpRTXDIContext->FillNeighborOffsetBuffer(offsets.data()); - mpNeighborOffsetsBuffer = Buffer::createTyped( - mpDevice, + mpNeighborOffsetsBuffer = mpDevice->createTypedBuffer( ResourceFormat::RG8Snorm, mRTXDIContextParams.NeighborOffsetCount, - Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, - Buffer::CpuAccess::None, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType::DeviceLocal, offsets.data()); } } diff --git a/Source/Falcor/Rendering/RTXDI/RTXDI.h b/Source/Falcor/Rendering/RTXDI/RTXDI.h index 3d4c7074a..af4da4276 100644 --- a/Source/Falcor/Rendering/RTXDI/RTXDI.h +++ b/Source/Falcor/Rendering/RTXDI/RTXDI.h @@ -63,7 +63,7 @@ namespace Falcor - When compiling shaders using this module, ensure you add the shader preprocessor defines provided by RTXDI::getDefines(). - When executing shaders using this module, ensure you set the shader data - using RTXDI::setShaderData(). + using RTXDI::bindShaderData(). To render a frame, the following steps need to occur: @@ -245,7 +245,7 @@ namespace Falcor Note: RTXDI is always bound to the global "gRTXDI" variable, so we expect a root shader variable here. \param[in] rootVar The root shader variable to set the data into. */ - void setShaderData(const ShaderVar& rootVar); + void bindShaderData(const ShaderVar& rootVar); /** Begin a frame. Must be called once at the beginning of each frame. @@ -375,7 +375,7 @@ namespace Falcor // Compute pass launches. - void setShaderDataInternal(const ShaderVar& rootVar, const ref& pMotionVectors); + void bindShaderDataInternal(const ShaderVar& rootVar, const ref& pMotionVectors, bool bindScene = true); void updateLights(RenderContext* pRenderContext); void updateEnvLight(RenderContext* pRenderContext); void presampleLights(RenderContext* pRenderContext); diff --git a/Source/Falcor/Rendering/RTXDI/RTXDIApplicationBridge.slangh b/Source/Falcor/Rendering/RTXDI/RTXDIApplicationBridge.slangh index 4edbc681d..4017d0c2c 100644 --- a/Source/Falcor/Rendering/RTXDI/RTXDIApplicationBridge.slangh +++ b/Source/Falcor/Rendering/RTXDI/RTXDIApplicationBridge.slangh @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Rendering/Utils/PixelStats.cpp b/Source/Falcor/Rendering/Utils/PixelStats.cpp index f319900a4..90264febb 100644 --- a/Source/Falcor/Rendering/Utils/PixelStats.cpp +++ b/Source/Falcor/Rendering/Utils/PixelStats.cpp @@ -82,7 +82,7 @@ namespace Falcor if (!mpParallelReduction) { mpParallelReduction = std::make_unique(mpDevice); - mpReductionResult = Buffer::create(mpDevice, (kRayTypeCount + 3) * sizeof(uint4), ResourceBindFlags::None, Buffer::CpuAccess::Read); + mpReductionResult = mpDevice->createBuffer((kRayTypeCount + 3) * sizeof(uint4), ResourceBindFlags::None, MemoryType::ReadBack); } // Prepare stats buffers. @@ -90,11 +90,11 @@ namespace Falcor { for (uint32_t i = 0; i < kRayTypeCount; i++) { - mpStatsRayCount[i] = Texture::create2D(mpDevice, frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpStatsRayCount[i] = mpDevice->createTexture2D(frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); } - mpStatsPathLength = Texture::create2D(mpDevice, frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); - mpStatsPathVertexCount = Texture::create2D(mpDevice, frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); - mpStatsVolumeLookupCount = Texture::create2D(mpDevice, frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpStatsPathLength = mpDevice->createTexture2D(frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpStatsPathVertexCount = mpDevice->createTexture2D(frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpStatsVolumeLookupCount = mpDevice->createTexture2D(frameDim.x, frameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); } for (uint32_t i = 0; i < kRayTypeCount; i++) @@ -115,7 +115,7 @@ namespace Falcor if (mEnabled) { // Create fence first time we need it. - if (!mpFence) mpFence = GpuFence::create(mpDevice); + if (!mpFence) mpFence = mpDevice->createFence(); // Sum of the per-pixel counters. The results are copied to a GPU buffer. for (uint32_t i = 0; i < kRayTypeCount; i++) @@ -127,8 +127,8 @@ namespace Falcor mpParallelReduction->execute(pRenderContext, mpStatsVolumeLookupCount, ParallelReduction::Type::Sum, nullptr, mpReductionResult, (kRayTypeCount + 2) * sizeof(uint4)); // Submit command list and insert signal. - pRenderContext->flush(false); - mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + pRenderContext->submit(false); + pRenderContext->signal(mpFence.get()); mStatsBuffersValid = true; mWaitingForData = true; @@ -227,7 +227,7 @@ namespace Falcor FALCOR_ASSERT(mStatsBuffersValid); if (!mpStatsRayCountTotal || mpStatsRayCountTotal->getWidth() != mFrameDim.x || mpStatsRayCountTotal->getHeight() != mFrameDim.y) { - mpStatsRayCountTotal = Texture::create2D(mpDevice, mFrameDim.x, mFrameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpStatsRayCountTotal = mpDevice->createTexture2D(mFrameDim.x, mFrameDim.y, ResourceFormat::R32Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); } auto var = mpComputeRayCount->getRootVar(); @@ -266,7 +266,7 @@ namespace Falcor if (mWaitingForData) { // Wait for signal. - mpFence->syncCpu(); + mpFence->wait(); mWaitingForData = false; if (mEnabled) diff --git a/Source/Falcor/Rendering/Utils/PixelStats.h b/Source/Falcor/Rendering/Utils/PixelStats.h index e54982fa2..958e18569 100644 --- a/Source/Falcor/Rendering/Utils/PixelStats.h +++ b/Source/Falcor/Rendering/Utils/PixelStats.h @@ -30,7 +30,7 @@ #include "Core/Macros.h" #include "Core/API/Buffer.h" #include "Core/API/Texture.h" -#include "Core/API/GpuFence.h" +#include "Core/API/Fence.h" #include "Core/Pass/ComputePass.h" #include "Utils/UI/Gui.h" #include "Utils/Algorithm/ParallelReduction.h" @@ -115,7 +115,7 @@ namespace Falcor // Internal state std::unique_ptr mpParallelReduction; ///< Helper for parallel reduction on the GPU. ref mpReductionResult; ///< Results buffer for stats readback (CPU mappable). - ref mpFence; ///< GPU fence for sychronizing readback. + ref mpFence; ///< GPU fence for sychronizing readback. // Configuration bool mEnabled = false; ///< Enable pixel statistics. diff --git a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.cpp b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.cpp index 4b7f0af78..ce10beffb 100644 --- a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.cpp +++ b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "GridVolumeSampler.h" -#include "Core/Assert.h" +#include "Core/Error.h" namespace Falcor { @@ -46,7 +46,7 @@ namespace Falcor return defines; } - void GridVolumeSampler::setShaderData(const ShaderVar& var) const + void GridVolumeSampler::bindShaderData(const ShaderVar& var) const { FALCOR_ASSERT(var.isValid()); } diff --git a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.h b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.h index 2da719371..83e792bf0 100644 --- a/Source/Falcor/Rendering/Volumes/GridVolumeSampler.h +++ b/Source/Falcor/Rendering/Volumes/GridVolumeSampler.h @@ -80,7 +80,7 @@ namespace Falcor /** Bind the grid volume sampler to a given shader variable. \param[in] var Shader variable. */ - void setShaderData(const ShaderVar& var) const; + void bindShaderData(const ShaderVar& var) const; /** Render the GUI. \return True if options were changed, false otherwise. @@ -91,6 +91,8 @@ namespace Falcor */ const Options& getOptions() const { return mOptions; } + void setOptions(const Options& options) { mOptions = options; } + protected: ref mpScene; ///< Scene. diff --git a/Source/Falcor/Rendering/Volumes/GridVolumeSamplerParams.slang b/Source/Falcor/Rendering/Volumes/GridVolumeSamplerParams.slang index fc116e6a2..994b8071e 100644 --- a/Source/Falcor/Rendering/Volumes/GridVolumeSamplerParams.slang +++ b/Source/Falcor/Rendering/Volumes/GridVolumeSamplerParams.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Rendering/Volumes/PhaseFunction.slang b/Source/Falcor/Rendering/Volumes/PhaseFunction.slang index 78cbc9d5a..a37a7a706 100644 --- a/Source/Falcor/Rendering/Volumes/PhaseFunction.slang +++ b/Source/Falcor/Rendering/Volumes/PhaseFunction.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -127,7 +127,7 @@ struct HenyeyGreensteinPhaseFunction : IPhaseFunction const float3 B = cross(dir, T); wo = normalize(w.x * T + w.y * B + w.z * dir); - pdf = evalHenyeyGreenstein(cosTheta, g); + pdf = evalHenyeyGreenstein(-cosTheta, g); weight = 1.f; return true; } @@ -140,7 +140,8 @@ struct HenyeyGreensteinPhaseFunction : IPhaseFunction /** Evaluates the anisotropic Henyey-Greenstein phase function. Note: This function reduces to isotropic phase function at g = 0 and has singularities at g = -1 and g = 1. - \param[in] cosTheta Cosine between unscattered and scattered direction. + Convention: both the incident direction and the outgoing direction point away from the scatter point, hence the "+" + \param[in] cosTheta Cosine between incoming and scattered direction. \param[in] g Anisotropy parameter in (-1, 1), where positive values promote forward scattering. */ float evalHenyeyGreenstein(const float cosTheta, const float g) @@ -150,7 +151,8 @@ float evalHenyeyGreenstein(const float cosTheta, const float g) } /** Evaluates the anisotropic Henyey-Greenstein phase function. Note: This function reduces to isotropic phase function at g = 0 and has singularities at g = -1 and g = 1. - \param[in] cosTheta Cosine between unscattered and scattered direction. + Convention: both the incident direction and the outgoing direction point away from the scatter point, hence the "+" + \param[in] cosTheta Cosine between incoming and scattered direction. \param[in] g Anisotropy parameter in (-1, 1), where positive values promote forward scattering. */ float3 evalHenyeyGreenstein(const float cosTheta, const float3 g) @@ -159,7 +161,6 @@ float3 evalHenyeyGreenstein(const float cosTheta, const float3 g) return M_1_4PI * (1 - g * g) / (denom * sqrt(denom)); } -/// This is a dummy code for now, to be filled in by @aweidlich by Nov 18th, 2022, no pressure struct DualHenyeyGreensteinPhaseFunction : IPhaseFunction { float3 meancosine[2]; @@ -184,30 +185,30 @@ struct DualHenyeyGreensteinPhaseFunction : IPhaseFunction const float avgW0 = (w0[0]+w0[1]+w0[2]) / 3.f; const float3 meancosineSelect = u.z <= avgW0 ? meancosine[0] : meancosine[1]; // Taking the average is really not the smartest thing. - const float meancosineHero = (meancosineSelect[0] + meancosineSelect[1] + meancosineSelect[2]) / 3.f; + const float meancosineSelectAvg = (meancosineSelect[0] + meancosineSelect[1] + meancosineSelect[2]) / 3.f; float cosTheta; - if (abs(meancosineHero) < 1e-3f) + if (abs(meancosineSelectAvg) < 1e-3f) { cosTheta = 1.f - 2.f * u.x; } else { - const float sqr = (1.f - meancosineHero * meancosineHero) / (1.f - meancosineHero + 2.f * meancosineHero * u.x); - cosTheta = (1.f + meancosineHero * meancosineHero - sqr * sqr) / (2.f * meancosineHero); + const float sqr = (1.f - meancosineSelectAvg * meancosineSelectAvg) / (1.f - meancosineSelectAvg + 2.f * meancosineSelectAvg * u.x); + cosTheta = (1.f + meancosineSelectAvg * meancosineSelectAvg - sqr * sqr) / (2.f * meancosineSelectAvg); } float selectionWeight = {}; - weight = {}; + float3 pfWeight = {}; if (u.z <= avgW0) { selectionWeight = avgW0; - weight = w0; + pfWeight = w0; } else { selectionWeight = (1.0 - avgW0); - weight = 1.f - w0; + pfWeight = 1.f - w0; } const float sinTheta = sqrt(max(0.f, 1.f - cosTheta * cosTheta)); @@ -219,8 +220,8 @@ struct DualHenyeyGreensteinPhaseFunction : IPhaseFunction const float3 B = cross(dir, T); wo = normalize(w.x * T + w.y * B + w.z * dir); - pdf = evalHenyeyGreenstein(cosTheta, meancosineHero); - weight = (evalHenyeyGreenstein(cosTheta, meancosineSelect) * weight / evalHenyeyGreenstein(dot(wi, wo), meancosineHero)) / selectionWeight; + pdf = evalHenyeyGreenstein(-cosTheta, meancosineSelectAvg); + weight = (evalHenyeyGreenstein(-cosTheta, meancosineSelect) * pfWeight / evalHenyeyGreenstein(-cosTheta, meancosineSelectAvg)) / selectionWeight; return true; } diff --git a/Source/Falcor/Scene/Animation/AnimatedVertexCache.cpp b/Source/Falcor/Scene/Animation/AnimatedVertexCache.cpp index 7e448d084..03f4790c5 100644 --- a/Source/Falcor/Scene/Animation/AnimatedVertexCache.cpp +++ b/Source/Falcor/Scene/Animation/AnimatedVertexCache.cpp @@ -231,12 +231,12 @@ namespace Falcor mpCurveVertexBuffers.resize(mCurveKeyframeTimes.size()); for (uint32_t i = 0; i < mCurveKeyframeTimes.size(); i++) { - mpCurveVertexBuffers[i] = Buffer::createStructured(mpDevice, sizeof(DynamicCurveVertexData), mCurveVertexCount, vbBindFlags, Buffer::CpuAccess::None, nullptr, false); + mpCurveVertexBuffers[i] = mpDevice->createStructuredBuffer(sizeof(DynamicCurveVertexData), mCurveVertexCount, vbBindFlags, MemoryType::DeviceLocal, nullptr, false); mpCurveVertexBuffers[i]->setName("AnimatedVertexCache::mpCurveVertexBuffers[" + std::to_string(i) + "]"); } // Create buffers for previous vertex positions. - mpPrevCurveVertexBuffer = Buffer::createStructured(mpDevice, sizeof(DynamicCurveVertexData), mCurveVertexCount, vbBindFlags, Buffer::CpuAccess::None, nullptr, false); + mpPrevCurveVertexBuffer = mpDevice->createStructuredBuffer(sizeof(DynamicCurveVertexData), mCurveVertexCount, vbBindFlags, MemoryType::DeviceLocal, nullptr, false); mpPrevCurveVertexBuffer->setName("AnimatedVertexCache::mpPrevCurveVertexBuffer"); // Initialize vertex buffers with cached positions. @@ -279,7 +279,7 @@ namespace Falcor // Create curve index buffer. vbBindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess; - mpCurveIndexBuffer = Buffer::create(mpDevice, sizeof(uint32_t) * mCurveIndexCount, vbBindFlags); + mpCurveIndexBuffer = mpDevice->createBuffer(sizeof(uint32_t) * mCurveIndexCount, vbBindFlags); mpCurveIndexBuffer->setName("AnimatedVertexCache::mpCurveIndexBuffer"); // Initialize index buffer. @@ -333,10 +333,10 @@ namespace Falcor mCurvePolyTubeIndexCount += curveMeta.indexCount; } - mpCurvePolyTubeCurveMetadataBuffer = Buffer::createStructured(mpDevice, sizeof(PerCurveMetadata), (uint32_t)curveMetadata.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, curveMetadata.data(), false); + mpCurvePolyTubeCurveMetadataBuffer = mpDevice->createStructuredBuffer(sizeof(PerCurveMetadata), (uint32_t)curveMetadata.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, curveMetadata.data(), false); mpCurvePolyTubeCurveMetadataBuffer->setName("AnimatedVertexCache::mpCurvePolyTubeCurveMetadataBuffer"); - mpCurvePolyTubeMeshMetadataBuffer = Buffer::createStructured(mpDevice, sizeof(PerMeshMetadata), (uint32_t)meshMetadata.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, meshMetadata.data(), false); + mpCurvePolyTubeMeshMetadataBuffer = mpDevice->createStructuredBuffer(sizeof(PerMeshMetadata), (uint32_t)meshMetadata.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, meshMetadata.data(), false); mpCurvePolyTubeMeshMetadataBuffer->setName("AnimatedVertexCache::mpCurvePolyTubeMeshMetadataBuffer"); // Create buffers for vertex positions in curve vertex caches. @@ -344,7 +344,7 @@ namespace Falcor mpCurvePolyTubeVertexBuffers.resize(mCurveKeyframeTimes.size()); for (uint32_t i = 0; i < mCurveKeyframeTimes.size(); i++) { - mpCurvePolyTubeVertexBuffers[i] = Buffer::createStructured(mpDevice, sizeof(DynamicCurveVertexData), mCurvePolyTubeVertexCount, vbBindFlags, Buffer::CpuAccess::None, nullptr, false); + mpCurvePolyTubeVertexBuffers[i] = mpDevice->createStructuredBuffer(sizeof(DynamicCurveVertexData), mCurvePolyTubeVertexCount, vbBindFlags, MemoryType::DeviceLocal, nullptr, false); mpCurvePolyTubeVertexBuffers[i]->setName("AnimatedVertexCache::mpCurvePolyTubeVertexBuffers[" + std::to_string(i) + "]"); } @@ -385,7 +385,7 @@ namespace Falcor // Create curve strand index buffer. vbBindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess; - mpCurvePolyTubeStrandIndexBuffer = Buffer::create(mpDevice, sizeof(uint32_t) * mCurvePolyTubeVertexCount, vbBindFlags); + mpCurvePolyTubeStrandIndexBuffer = mpDevice->createBuffer(sizeof(uint32_t) * mCurvePolyTubeVertexCount, vbBindFlags); mpCurvePolyTubeStrandIndexBuffer->setName("AnimatedVertexCache::mpCurvePolyTubeStrandIndexBuffer"); // Initialize strand index buffer. @@ -458,18 +458,18 @@ namespace Falcor { auto& data = cache.vertexData[i]; size_t index = keyframeOffset + i; - mpMeshVertexBuffers[index] = Buffer::createStructured(mpDevice, sizeof(PackedStaticVertexData), (uint32_t)data.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data(), false); + mpMeshVertexBuffers[index] = mpDevice->createStructuredBuffer(sizeof(PackedStaticVertexData), (uint32_t)data.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, data.data(), false); mpMeshVertexBuffers[index]->setName("AnimatedVertexCache::mpMeshVertexBuffers[" + std::to_string(index) + "]"); } keyframeOffset += (uint32_t)cache.timeSamples.size(); } - mpMeshMetadataBuffer = Buffer::createStructured(mpDevice, sizeof(PerMeshMetadata), (uint32_t)meshMetadata.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, meshMetadata.data(), false); + mpMeshMetadataBuffer = mpDevice->createStructuredBuffer(sizeof(PerMeshMetadata), (uint32_t)meshMetadata.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, meshMetadata.data(), false); mpMeshMetadataBuffer->setName("AnimatedVertexCache::mpMeshMetadataBuffer"); mMeshInterpolationInfo.resize(mCachedMeshes.size()); - mpMeshInterpolationBuffer = Buffer::createStructured(mpDevice, sizeof(InterpolationInfo), (uint32_t)mMeshInterpolationInfo.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpMeshInterpolationBuffer = mpDevice->createStructuredBuffer(sizeof(InterpolationInfo), (uint32_t)mMeshInterpolationInfo.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpMeshInterpolationBuffer->setName("AnimatedVertexCache::mpMeshInterpolationbuffer"); } diff --git a/Source/Falcor/Scene/Animation/Animation.cpp b/Source/Falcor/Scene/Animation/Animation.cpp index 0312b8763..41b91f4f4 100644 --- a/Source/Falcor/Scene/Animation/Animation.cpp +++ b/Source/Falcor/Scene/Animation/Animation.cpp @@ -214,7 +214,7 @@ namespace Falcor } else { - throw ArgumentError("'mode' is unknown interpolation mode"); + FALCOR_THROW("'mode' is unknown interpolation mode"); } } @@ -300,7 +300,7 @@ namespace Falcor { if (k.time == time) return k; } - throw ArgumentError("'time' ({}) does not refer to an existing keyframe", time); + FALCOR_THROW("'time' ({}) does not refer to an existing keyframe", time); } bool Animation::doesKeyframeExists(double time) const diff --git a/Source/Falcor/Scene/Animation/AnimationController.cpp b/Source/Falcor/Scene/Animation/AnimationController.cpp index df1aa575c..ff3ffd6ed 100644 --- a/Source/Falcor/Scene/Animation/AnimationController.cpp +++ b/Source/Falcor/Scene/Animation/AnimationController.cpp @@ -56,13 +56,13 @@ namespace Falcor if (!mLocalMatrices.empty()) { - mpWorldMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mLocalMatrices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpWorldMatricesBuffer = mpDevice->createStructuredBuffer(sizeof(float4x4), (uint32_t)mLocalMatrices.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpWorldMatricesBuffer->setName("AnimationController::mpWorldMatricesBuffer"); - mpPrevWorldMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mLocalMatrices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpPrevWorldMatricesBuffer = mpDevice->createStructuredBuffer(sizeof(float4x4), (uint32_t)mLocalMatrices.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpPrevWorldMatricesBuffer->setName("AnimationController::mpPrevWorldMatricesBuffer"); - mpInvTransposeWorldMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mLocalMatrices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpInvTransposeWorldMatricesBuffer = mpDevice->createStructuredBuffer(sizeof(float4x4), (uint32_t)mLocalMatrices.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpInvTransposeWorldMatricesBuffer->setName("AnimationController::mpInvTransposeWorldMatricesBuffer"); - mpPrevInvTransposeWorldMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mLocalMatrices.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpPrevInvTransposeWorldMatricesBuffer = mpDevice->createStructuredBuffer(sizeof(float4x4), (uint32_t)mLocalMatrices.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpPrevInvTransposeWorldMatricesBuffer->setName("AnimationController::mpPrevInvTransposeWorldMatricesBuffer"); } @@ -79,7 +79,7 @@ namespace Falcor uint32_t staticIndex = skinningVertexData[i].staticIndex; prevVertexData[i].position = staticVertexData[staticIndex].position; } - mpPrevVertexData = Buffer::createStructured(mpDevice, sizeof(PrevVertexData), prevVertexCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, prevVertexData.data(), false); + mpPrevVertexData = mpDevice->createStructuredBuffer(sizeof(PrevVertexData), prevVertexCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, prevVertexData.data(), false); mpPrevVertexData->setName("AnimationController::mpPrevVertexData"); } @@ -218,6 +218,7 @@ namespace Falcor // including transformation matrices, dynamic vertex data etc. if (mFirstUpdate || mEnabled != mPrevEnabled) { + std::fill(mMatricesChanged.begin(), mMatricesChanged.end(), true); initLocalMatrices(); if (mEnabled) { @@ -364,7 +365,7 @@ namespace Falcor void AnimationController::bindBuffers() { - ParameterBlock* pBlock = mpScene->getParameterBlock().get(); + ParameterBlock* pBlock = mpScene->mpSceneBlock.get(); pBlock->setBuffer(kWorldMatrices, mpWorldMatricesBuffer); pBlock->setBuffer(kInverseTransposeWorldMatrices, mpInvTransposeWorldMatricesBuffer); bool usePrev = mEnabled && hasAnimations(); @@ -419,9 +420,9 @@ namespace Falcor // Bind vertex data. FALCOR_ASSERT(staticVertexData.size() <= std::numeric_limits::max()); FALCOR_ASSERT(skinningVertexData.size() <= std::numeric_limits::max()); - mpStaticVertexData = Buffer::createStructured(mpDevice, block["staticData"], (uint32_t)staticVertexData.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, staticVertexData.data(), false); + mpStaticVertexData = mpDevice->createStructuredBuffer(block["staticData"], (uint32_t)staticVertexData.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, staticVertexData.data(), false); mpStaticVertexData->setName("AnimationController::mpStaticVertexData"); - mpSkinningVertexData = Buffer::createStructured(mpDevice, block["skinningData"], (uint32_t)skinningVertexData.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, skinningVertexData.data(), false); + mpSkinningVertexData = mpDevice->createStructuredBuffer(block["skinningData"], (uint32_t)skinningVertexData.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, skinningVertexData.data(), false); mpSkinningVertexData->setName("AnimationController::mpSkinningVertexData"); block["staticData"] = mpStaticVertexData; @@ -431,13 +432,13 @@ namespace Falcor // Bind transforms. FALCOR_ASSERT(mSkinningMatrices.size() < std::numeric_limits::max()); - mpMeshBindMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, mMeshBindMatrices.data(), false); + mpMeshBindMatricesBuffer = mpDevice->createStructuredBuffer(sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, mMeshBindMatrices.data(), false); mpMeshBindMatricesBuffer->setName("AnimationController::mpMeshBindMatricesBuffer"); - mpMeshInvBindMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, meshInvBindMatrices.data(), false); + mpMeshInvBindMatricesBuffer = mpDevice->createStructuredBuffer(sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, meshInvBindMatrices.data(), false); mpMeshInvBindMatricesBuffer->setName("AnimationController::mpMeshInvBindMatricesBuffer"); - mpSkinningMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpSkinningMatricesBuffer = mpDevice->createStructuredBuffer(sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpSkinningMatricesBuffer->setName("AnimationController::mpSkinningMatricesBuffer"); - mpInvTransposeSkinningMatricesBuffer = Buffer::createStructured(mpDevice, sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpInvTransposeSkinningMatricesBuffer = mpDevice->createStructuredBuffer(sizeof(float4x4), (uint32_t)mSkinningMatrices.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpInvTransposeSkinningMatricesBuffer->setName("AnimationController::mpInvTransposeSkinningMatricesBuffer"); block["boneMatrices"].setBuffer(mpSkinningMatricesBuffer); diff --git a/Source/Falcor/Scene/Animation/AnimationController.h b/Source/Falcor/Scene/Animation/AnimationController.h index 9cf3d25c3..15eb4430b 100644 --- a/Source/Falcor/Scene/Animation/AnimationController.h +++ b/Source/Falcor/Scene/Animation/AnimationController.h @@ -144,6 +144,7 @@ namespace Falcor private: friend class SceneBuilder; + friend class Scene; void initLocalMatrices(); void updateLocalMatrices(double time); diff --git a/Source/Falcor/Scene/Animation/Skinning.slang b/Source/Falcor/Scene/Animation/Skinning.slang index e794f9700..da141147a 100644 --- a/Source/Falcor/Scene/Animation/Skinning.slang +++ b/Source/Falcor/Scene/Animation/Skinning.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -156,7 +156,7 @@ void main(uint3 dispatchThreadID : SV_DispatchThreadID) s.position = mul(boneMat, float4(s.position, 1.f)).xyz; s.tangent.xyz = mul((float3x3) boneMat, s.tangent.xyz); - s.normal = mul((float3x3) transpose(invTransposeMat), s.normal); + s.normal = mul((float3x3) invTransposeMat, s.normal); // Copy the previous skinned data PrevVertexData prev; diff --git a/Source/Falcor/Scene/Camera/Camera.cpp b/Source/Falcor/Scene/Camera/Camera.cpp index 240bb799d..9f1286400 100644 --- a/Source/Falcor/Scene/Camera/Camera.cpp +++ b/Source/Falcor/Scene/Camera/Camera.cpp @@ -260,7 +260,7 @@ namespace Falcor return !isInside; } - void Camera::setShaderData(const ShaderVar& var) const + void Camera::bindShaderData(const ShaderVar& var) const { calculateCameraParameters(); var["data"].setBlob(mData); diff --git a/Source/Falcor/Scene/Camera/Camera.h b/Source/Falcor/Scene/Camera/Camera.h index 2615b72ec..a92f7ae58 100644 --- a/Source/Falcor/Scene/Camera/Camera.h +++ b/Source/Falcor/Scene/Camera/Camera.h @@ -249,7 +249,7 @@ namespace Falcor /** Set the camera into a shader var */ - void setShaderData(const ShaderVar& var) const; + void bindShaderData(const ShaderVar& var) const; /** Returns the raw camera data */ diff --git a/Source/Falcor/Scene/Curves/CurveTessellation.cpp b/Source/Falcor/Scene/Curves/CurveTessellation.cpp index 081b4aa7a..8e263baba 100644 --- a/Source/Falcor/Scene/Curves/CurveTessellation.cpp +++ b/Source/Falcor/Scene/Curves/CurveTessellation.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "CurveTessellation.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/Math/Common.h" #include "Utils/Math/MathHelpers.h" #include "Utils/Math/CubicSpline.h" @@ -37,9 +37,9 @@ namespace Falcor { struct StrandArrays { - std::vector controlPoints; - std::vector widths; - std::vector UVs; + fast_vector controlPoints; + fast_vector widths; + fast_vector UVs; uint32_t vertexCount { 0 }; }; @@ -91,6 +91,13 @@ namespace Falcor #endif } + /// Sanitize radius so it is never 0, as non-zero radius is used to distinguish + /// between mesh-from-curves and native mesh, which is used intersection and epsilon calculations. + inline float sanitizeWidth(float w) + { + return std::max(w, (float)std::numeric_limits::min()); + } + void optimizeStrandGeometry(CubicSplineCache& splineCache, const CurveArrays& curveArrays, StrandArrays& strandArrays, StrandArrays& optimizedStrandArrays, uint32_t pointOffset, uint32_t subdivPerSegment, uint32_t keepOneEveryXVerticesPerStrand, float widthScale) { strandArrays.controlPoints.clear(); @@ -127,7 +134,7 @@ namespace Falcor { float t = (float)k / (float)subdivPerSegment; optimizedStrandArrays.controlPoints.push_back(splinePoints.interpolate(j, t)); - optimizedStrandArrays.widths.push_back(kMeshCompensationScale * widthScale * splineWidths.interpolate(j, t)); + optimizedStrandArrays.widths.push_back(sanitizeWidth(kMeshCompensationScale * widthScale * splineWidths.interpolate(j, t))); } tmpCount++; } @@ -135,7 +142,7 @@ namespace Falcor // Always keep the last vertex. optimizedStrandArrays.controlPoints.push_back(splinePoints.interpolate(optimizedStrandArrays.vertexCount - 2, 1.f)); - optimizedStrandArrays.widths.push_back(kMeshCompensationScale * widthScale * splineWidths.interpolate(optimizedStrandArrays.vertexCount - 2, 1.f)); + optimizedStrandArrays.widths.push_back(sanitizeWidth(kMeshCompensationScale * widthScale * splineWidths.interpolate(optimizedStrandArrays.vertexCount - 2, 1.f))); // Texture coordinates. if (curveArrays.UVs) @@ -164,7 +171,7 @@ namespace Falcor { float3 prevFwd; - if (j <= 0 || j >= strandArrays.controlPoints.size()) + if (j <= 0 || j >= strandArrays.controlPoints.size() || strandArrays.controlPoints.size() == 2) { // The forward tangents should be the same, meaning s & t are also the same prevFwd = fwd; @@ -188,10 +195,15 @@ namespace Falcor // Use quaternions to smoothly rotate the other vectors and update s & t vectors. quatf rotQuat = math::quatFromRotationBetweenVectors(prevFwd, fwd); s = mul(rotQuat, s); - t = mul(rotQuat, t); + t = normalize(cross(fwd, s)); + s = normalize(cross(t, fwd)); + + FALCOR_ASSERT_LT(std::abs(length(fwd) - 1.f), 1e-3f); + FALCOR_ASSERT_LT(std::abs(length(s) - 1.f), 1e-3f); + FALCOR_ASSERT_LT(std::abs(length(t) - 1.f), 1e-3f); } - void updateMeshResultBuffers(CurveTessellation::MeshResult& result, const CurveArrays& curveArrays, StrandArrays& optimizedStrandArrays, const float3& fwd, const float3& s, const float3& t, uint32_t pointCountPerCrossSection, const float& widthScale, uint32_t j) + void updateMeshResultBuffers(CurveTessellation::MeshResult& result, const CurveArrays& curveArrays, StrandArrays& optimizedStrandArrays, const float3& fwd, const float3& s, const float3& t, uint32_t pointCountPerCrossSection, uint32_t j) { // Mesh vertices, normals, tangents, and texCrds (if any). for (uint32_t k = 0; k < pointCountPerCrossSection; k++) @@ -287,7 +299,7 @@ namespace Falcor result.indices.push_back((uint32_t)result.points.size()); // Pre-transform curve points. - float4 sph = transformSphere(xform, float4(splinePoints.interpolate(j, t), splineWidths.interpolate(j, t) * 0.5f * widthScale)); + float4 sph = transformSphere(xform, float4(splinePoints.interpolate(j, t), sanitizeWidth(splineWidths.interpolate(j, t) * 0.5f * widthScale))); result.points.push_back(sph.xyz()); result.radius.push_back(sph.w); @@ -297,7 +309,7 @@ namespace Falcor } // Always keep the last vertex. - float4 sph = transformSphere(xform, float4(splinePoints.interpolate(optimizedStrandArrays.vertexCount - 2, 1.f), splineWidths.interpolate(optimizedStrandArrays.vertexCount - 2, 1.f) * 0.5f * widthScale)); + float4 sph = transformSphere(xform, float4(splinePoints.interpolate(optimizedStrandArrays.vertexCount - 2, 1.f), sanitizeWidth(splineWidths.interpolate(optimizedStrandArrays.vertexCount - 2, 1.f) * 0.5f * widthScale))); result.points.push_back(sph.xyz()); result.radius.push_back(sph.w); @@ -377,6 +389,7 @@ namespace Falcor // Build the initial frame. float3 fwd, s, t; fwd = normalize(optimizedStrandArrays.controlPoints[1] - optimizedStrandArrays.controlPoints[0]); + FALCOR_ASSERT_LT(std::abs(length(fwd) - 1.f), 1e-3f); buildFrame(fwd, s, t); // Create mesh. @@ -386,7 +399,7 @@ namespace Falcor updateCurveFrame(optimizedStrandArrays, fwd, s, t, j); // Mesh vertices, normals, tangents, and texCrds (if any). - updateMeshResultBuffers(result, curveArrays, optimizedStrandArrays, fwd, s, t, pointCountPerCrossSection, widthScale, j); + updateMeshResultBuffers(result, curveArrays, optimizedStrandArrays, fwd, s, t, pointCountPerCrossSection, j); // Mesh faces. if (j < optimizedStrandArrays.controlPoints.size() - 1) @@ -398,6 +411,7 @@ namespace Falcor meshVertexOffset += pointCountPerCrossSection * (uint32_t)optimizedStrandArrays.controlPoints.size(); } + return result; } diff --git a/Source/Falcor/Scene/Curves/CurveTessellation.h b/Source/Falcor/Scene/Curves/CurveTessellation.h index 6e2c98d0d..86a330f28 100644 --- a/Source/Falcor/Scene/Curves/CurveTessellation.h +++ b/Source/Falcor/Scene/Curves/CurveTessellation.h @@ -29,6 +29,7 @@ #include "Core/Macros.h" #include "Utils/Math/Matrix.h" #include "Utils/Math/Vector.h" +#include "Utils/fast_vector.h" #include namespace Falcor @@ -41,10 +42,10 @@ namespace Falcor struct SweptSphereResult { uint32_t degree; - std::vector indices; - std::vector points; - std::vector radius; - std::vector texCrds; + fast_vector indices; + fast_vector points; + fast_vector radius; + fast_vector texCrds; }; /** Convert cubic B-splines to a couple of linear swept sphere segments. @@ -67,13 +68,13 @@ namespace Falcor struct MeshResult { - std::vector vertices; - std::vector normals; - std::vector tangents; - std::vector texCrds; - std::vector radii; - std::vector faceVertexCounts; - std::vector faceVertexIndices; + fast_vector vertices; + fast_vector normals; + fast_vector tangents; + fast_vector texCrds; + fast_vector radii; + fast_vector faceVertexCounts; + fast_vector faceVertexIndices; }; /** Tessellate cubic B-splines to a triangular mesh. diff --git a/Source/Falcor/Scene/Displacement/DisplacementMapping.slang b/Source/Falcor/Scene/Displacement/DisplacementMapping.slang index 76b9e009a..47f67011a 100644 --- a/Source/Falcor/Scene/Displacement/DisplacementMapping.slang +++ b/Source/Falcor/Scene/Displacement/DisplacementMapping.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Scene/HitInfo.cpp b/Source/Falcor/Scene/HitInfo.cpp index c5964ab75..2907c27a6 100644 --- a/Source/Falcor/Scene/HitInfo.cpp +++ b/Source/Falcor/Scene/HitInfo.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -83,7 +83,7 @@ namespace Falcor // Check that the final bit allocation fits. if (mPrimitiveIndexBits > 32 || (mTypeBits + mInstanceIDBits) > 32) { - throw RuntimeError("Scene requires > 64 bits for encoding hit info header. This is currently not supported."); + FALCOR_THROW("Scene requires > 64 bits for encoding hit info header. This is currently not supported."); } // Compute size of compressed header in bits. diff --git a/Source/Falcor/Scene/HitInfo.h b/Source/Falcor/Scene/HitInfo.h index 12d84f366..2c92ea584 100644 --- a/Source/Falcor/Scene/HitInfo.h +++ b/Source/Falcor/Scene/HitInfo.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Scene/Importer.cpp b/Source/Falcor/Scene/Importer.cpp index 855d62048..3ac50bb36 100644 --- a/Source/Falcor/Scene/Importer.cpp +++ b/Source/Falcor/Scene/Importer.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -46,9 +46,9 @@ namespace Falcor return extensions; } - void Importer::importSceneFromMemory(const void* buffer, size_t byteSize, std::string_view extension, SceneBuilder& builder, const pybind11::dict& dict) + void Importer::importSceneFromMemory(const void* buffer, size_t byteSize, std::string_view extension, SceneBuilder& builder, const std::map& materialToShortName) { - throw RuntimeError("Not implemented."); + FALCOR_THROW("Not implemented."); } FALCOR_SCRIPT_BINDING(Importer) diff --git a/Source/Falcor/Scene/Importer.h b/Source/Falcor/Scene/Importer.h index 4b79e201d..08ade264d 100644 --- a/Source/Falcor/Scene/Importer.h +++ b/Source/Falcor/Scene/Importer.h @@ -27,8 +27,9 @@ **************************************************************************/ #pragma once #include "SceneBuilder.h" +#include "ImporterError.h" #include "Core/Macros.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/Plugin.h" #include "Core/Platform/OS.h" #include @@ -37,48 +38,8 @@ #include #include -#include - namespace Falcor { - /** Exception thrown during scene import. - Holds the path of the imported asset and a description of the exception. - */ - class FALCOR_API ImporterError : public Exception - { - public: - ImporterError() noexcept - {} - - ImporterError(const std::filesystem::path& path, const char* what) - : Exception(what) - , mpPath(std::make_shared(path)) - {} - - ImporterError(const std::filesystem::path& path, const std::string& what) - : ImporterError(path, what.c_str()) - {} - - template - explicit ImporterError(const std::filesystem::path& path, fmt::format_string format, Args&&... args) - : ImporterError(path, fmt::format(format, std::forward(args)...).c_str()) - {} - - virtual ~ImporterError() override - {} - - ImporterError(const ImporterError& other) noexcept - { - mpWhat = other.mpWhat; - mpPath = other.mpPath; - } - - const std::filesystem::path& path() const noexcept { return *mpPath; } - - private: - std::shared_ptr mpPath; - }; - /** Base class for importers. Importers are bound to a set of file extensions. This allows the right importer to be called when importing an asset file. @@ -103,7 +64,7 @@ namespace Falcor \param[in] dict Optional dictionary. Throws an ImporterError if something went wrong. */ - virtual void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) = 0; + virtual void importScene(const std::filesystem::path& path, SceneBuilder& builder, const std::map& materialToShortName) = 0; /** Import a scene from memory. \param[in] buffer Memory buffer. @@ -113,7 +74,7 @@ namespace Falcor \param[in] dict Optional dictionary. Throws an ImporterError if something went wrong. */ - virtual void importSceneFromMemory(const void* buffer, size_t byteSize, std::string_view extension, SceneBuilder& builder, const pybind11::dict& dict); + virtual void importSceneFromMemory(const void* buffer, size_t byteSize, std::string_view extension, SceneBuilder& builder, const std::map& materialToShortName); // Importer factory diff --git a/Source/Falcor/Scene/ImporterError.h b/Source/Falcor/Scene/ImporterError.h new file mode 100644 index 000000000..3f73a4840 --- /dev/null +++ b/Source/Falcor/Scene/ImporterError.h @@ -0,0 +1,72 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include "Core/Macros.h" +#include "Core/Error.h" +#include "Core/Platform/OS.h" +#include +#include + +/// ImporterError split from the Importer.h file to allow using it without bringing in pybind11 types (via SceneBuilder) + +namespace Falcor +{ + /** Exception thrown during scene import. + Holds the path of the imported asset and a description of the exception. + */ + class FALCOR_API ImporterError : public Exception + { + public: + ImporterError() noexcept + {} + + ImporterError(const std::filesystem::path& path, std::string_view what) + : Exception(what) + , mpPath(std::make_shared(path)) + {} + + template + explicit ImporterError(const std::filesystem::path& path, fmt::format_string format, Args&&... args) + : ImporterError(path, fmt::format(format, std::forward(args)...)) + {} + + virtual ~ImporterError() override + {} + + ImporterError(const ImporterError& other) noexcept + { + mpWhat = other.mpWhat; + mpPath = other.mpPath; + } + + const std::filesystem::path& path() const noexcept { return *mpPath; } + + private: + std::shared_ptr mpPath; + }; +} diff --git a/Source/Falcor/Scene/Intersection.slang b/Source/Falcor/Scene/Intersection.slang index a11a26033..902388074 100644 --- a/Source/Falcor/Scene/Intersection.slang +++ b/Source/Falcor/Scene/Intersection.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Scene/Lights/EnvMap.cpp b/Source/Falcor/Scene/Lights/EnvMap.cpp index a89b022ee..e67bfe2c7 100644 --- a/Source/Falcor/Scene/Lights/EnvMap.cpp +++ b/Source/Falcor/Scene/Lights/EnvMap.cpp @@ -68,6 +68,14 @@ namespace Falcor } } + void EnvMap::setTransform(const float4x4& xform) + { + float3 rotation; + // Extract rotation from the computed transform + math::extractEulerAngleXYZ(xform, rotation.x, rotation.y, rotation.z); + setRotation(math::degrees(rotation)); + } + void EnvMap::setIntensity(float intensity) { mData.intensity = intensity; @@ -78,7 +86,7 @@ namespace Falcor mData.tint = tint; } - void EnvMap::setShaderData(const ShaderVar& var) const + void EnvMap::bindShaderData(const ShaderVar& var) const { FALCOR_ASSERT(var.isValid()); @@ -111,17 +119,17 @@ namespace Falcor EnvMap::EnvMap(ref pDevice, const ref& pTexture) : mpDevice(pDevice) { - checkArgument(mpDevice != nullptr, "'pDevice' must be a valid device"); - checkArgument(pTexture != nullptr, "'pTexture' must be a valid texture"); + FALCOR_CHECK(mpDevice != nullptr, "'pDevice' must be a valid device"); + FALCOR_CHECK(pTexture != nullptr, "'pTexture' must be a valid texture"); mpEnvMap = pTexture; // Create sampler. // The lat-long map wraps around horizontally, but not vertically. Set the sampler to only wrap in U. Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - samplerDesc.setAddressingMode(Sampler::AddressMode::Wrap, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - mpEnvSampler = Sampler::create(mpDevice, samplerDesc); + samplerDesc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Linear); + samplerDesc.setAddressingMode(TextureAddressingMode::Wrap, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); + mpEnvSampler = mpDevice->createSampler(samplerDesc); } FALCOR_SCRIPT_BINDING(EnvMap) @@ -130,7 +138,10 @@ namespace Falcor pybind11::class_> envMap(m, "EnvMap"); auto createFromFile = [](const std::filesystem::path &path) { - return EnvMap::createFromFile(accessActivePythonSceneBuilder().getDevice(), path); + ref envMap = EnvMap::createFromFile(accessActivePythonSceneBuilder().getDevice(), getActiveAssetResolver().resolvePath(path)); + if (!envMap) + FALCOR_THROW("Failed to load environment map from '{}'.", path); + return envMap; }; envMap.def(pybind11::init(createFromFile), "path"_a); // PYTHONDEPRECATED envMap.def_static("createFromFile", createFromFile, "path"_a); diff --git a/Source/Falcor/Scene/Lights/EnvMap.h b/Source/Falcor/Scene/Lights/EnvMap.h index 76113b489..3837ce0e5 100644 --- a/Source/Falcor/Scene/Lights/EnvMap.h +++ b/Source/Falcor/Scene/Lights/EnvMap.h @@ -57,7 +57,7 @@ namespace Falcor /** Create a new environment map from file. \param[in] pDevice GPU device. - \param[in] path The environment map texture file path. + \param[in] path The environment map texture file path (absolute or relative to working directory). \return A new object, or nullptr if the environment map failed to load. */ static ref createFromFile(ref pDevice, const std::filesystem::path& path); @@ -73,6 +73,7 @@ namespace Falcor \param[in] degreesXYZ Rotation angles in degrees for XYZ. */ void setRotation(float3 degreesXYZ); + void setTransform(const float4x4& matrix); /** Get rotation angles. */ @@ -104,7 +105,7 @@ namespace Falcor /** Bind the environment map to a given shader variable. \param[in] var Shader variable. */ - void setShaderData(const ShaderVar& var) const; + void bindShaderData(const ShaderVar& var) const; enum class Changes { diff --git a/Source/Falcor/Scene/Lights/Light.cpp b/Source/Falcor/Scene/Lights/Light.cpp index ee9cdfdc8..3e6b46899 100644 --- a/Source/Falcor/Scene/Lights/Light.cpp +++ b/Source/Falcor/Scene/Lights/Light.cpp @@ -74,7 +74,7 @@ namespace Falcor return getChanges(); } - void Light::setShaderData(const ShaderVar& var) + void Light::bindShaderData(const ShaderVar& var) { #define check_offset(_a) FALCOR_ASSERT(var.getType()->getMemberOffset(#_a).getByteOffset() == offsetof(LightData, _a)) check_offset(dirW); diff --git a/Source/Falcor/Scene/Lights/Light.h b/Source/Falcor/Scene/Lights/Light.h index acf2790ed..f5b653905 100644 --- a/Source/Falcor/Scene/Lights/Light.h +++ b/Source/Falcor/Scene/Lights/Light.h @@ -50,7 +50,7 @@ namespace Falcor /** Set the light parameters into a shader variable. To use this you need to include/import 'ShaderCommon' inside your shader. */ - virtual void setShaderData(const ShaderVar& var); + virtual void bindShaderData(const ShaderVar& var); /** Render UI elements for this light. */ diff --git a/Source/Falcor/Scene/Lights/LightCollection.cpp b/Source/Falcor/Scene/Lights/LightCollection.cpp index 685f1f08a..c13a53280 100644 --- a/Source/Falcor/Scene/Lights/LightCollection.cpp +++ b/Source/Falcor/Scene/Lights/LightCollection.cpp @@ -34,6 +34,8 @@ #include "Utils/Timing/TimeReport.h" #include "Utils/Timing/Profiler.h" +#include + namespace Falcor { static_assert(sizeof(MeshLightData) % 16 == 0, "MeshLightData size should be a multiple of 16"); @@ -67,7 +69,7 @@ namespace Falcor mpTrianglePositionUpdater = ComputePass::create(mpDevice, kUpdateTriangleVerticesFile, "updateTriangleVertices", defines); mpFinalizeIntegration = ComputePass::create(mpDevice, kFinalizeIntegrationFile, "finalizeIntegration", defines); - mpStagingFence = GpuFence::create(mpDevice); + mpStagingFence = mpDevice->createFence(); // Now build the mesh light data. build(pRenderContext, *mpScene); @@ -120,21 +122,20 @@ namespace Falcor // Check for required features. if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::ConservativeRasterizationTier3)) { - throw RuntimeError("LightCollection requires conservative rasterization tier 3 support."); + FALCOR_THROW("LightCollection requires conservative rasterization tier 3 support."); } - if (!mpDevice->isShaderModelSupported(Device::ShaderModel::SM6_6)) + if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_6)) { - throw RuntimeError("LightCollection requires Shader Model 6.6 support."); + FALCOR_THROW("LightCollection requires Shader Model 6.6 support."); } // Create program. auto defines = scene.getSceneDefines(); defines.add("INTEGRATOR_PASS", "1"); - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kEmissiveIntegratorFile).vsEntry("vsMain").gsEntry("gsMain").psEntry("psMain"); - desc.setShaderModel("6_6"); - mIntegrator.pProgram = GraphicsProgram::create(mpDevice, desc, defines); + mIntegrator.pProgram = Program::create(mpDevice, desc, defines); // Create graphics state. mIntegrator.pState = GraphicsState::create(mpDevice); @@ -163,8 +164,8 @@ namespace Falcor // Create sampler for texel fetch. This is identical to material sampler but uses point sampling. Sampler::Desc samplerDesc = mpSamplerState ? mpSamplerState->getDesc() : Sampler::Desc(); - samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - mIntegrator.pPointSampler = Sampler::create(mpDevice, samplerDesc); + samplerDesc.setFilterMode(TextureFilteringMode::Point, TextureFilteringMode::Point, TextureFilteringMode::Point); + mIntegrator.pPointSampler = mpDevice->createSampler(samplerDesc); } void LightCollection::setupMeshLights(const Scene& scene) @@ -208,7 +209,7 @@ namespace Falcor } else if (mpSamplerState != pMaterial->getDefaultTextureSampler()) { - throw RuntimeError("Material '{}' is using a different sampler.", pMaterial->getName()); + FALCOR_THROW("Material '{}' is using a different sampler.", pMaterial->getName()); } } } @@ -261,13 +262,13 @@ namespace Falcor FALCOR_ASSERT(mTriangleCount > 0); // Create GPU buffers. - mpTriangleData = Buffer::createStructured(mpDevice, mpTriangleListBuilder->getRootVar()["gTriangleData"], mTriangleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpTriangleData = mpDevice->createStructuredBuffer(mpTriangleListBuilder->getRootVar()["gTriangleData"], mTriangleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); mpTriangleData->setName("LightCollection::mpTriangleData"); - if (mpTriangleData->getStructSize() != sizeof(PackedEmissiveTriangle)) throw RuntimeError("Struct PackedEmissiveTriangle size mismatch between CPU/GPU"); + if (mpTriangleData->getStructSize() != sizeof(PackedEmissiveTriangle)) FALCOR_THROW("Struct PackedEmissiveTriangle size mismatch between CPU/GPU"); - mpFluxData = Buffer::createStructured(mpDevice, mpFinalizeIntegration->getRootVar()["gFluxData"], mTriangleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpFluxData = mpDevice->createStructuredBuffer(mpFinalizeIntegration->getRootVar()["gFluxData"], mTriangleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); mpFluxData->setName("LightCollection::mpFluxData"); - if (mpFluxData->getStructSize() != sizeof(EmissiveFlux)) throw RuntimeError("Struct EmissiveFlux size mismatch between CPU/GPU"); + if (mpFluxData->getStructSize() != sizeof(EmissiveFlux)) FALCOR_THROW("Struct EmissiveFlux size mismatch between CPU/GPU"); // Compute triangle data (vertices, uv-coordinates, materialID) for all mesh lights. buildTriangleList(pRenderContext, scene); @@ -278,16 +279,15 @@ namespace Falcor // Create buffer for the mesh data if needed. if (!mMeshLights.empty()) { - mpMeshData = Buffer::createStructured( - mpDevice, + mpMeshData = mpDevice->createStructuredBuffer( mpTrianglePositionUpdater->getRootVar()["gMeshData"], uint32_t(mMeshLights.size()), ResourceBindFlags::ShaderResource, - Buffer::CpuAccess::None, nullptr, false); + MemoryType::DeviceLocal, nullptr, false); mpMeshData->setName("LightCollection::mpMeshData"); if (mpMeshData->getStructSize() != sizeof(MeshLightData)) { - throw RuntimeError("Size mismatch for structured buffer of MeshLightData"); + FALCOR_THROW("Size mismatch for structured buffer of MeshLightData"); } size_t meshDataSize = mMeshLights.size() * sizeof(mMeshLights[0]); FALCOR_ASSERT(mpMeshData->getSize() == meshDataSize); @@ -306,7 +306,7 @@ namespace Falcor triangleOffsets[it.instanceID] = it.triangleOffset; } - mpPerMeshInstanceOffset = Buffer::createStructured(mpDevice, sizeof(uint32_t), (uint32_t)triangleOffsets.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, triangleOffsets.data(), false); + mpPerMeshInstanceOffset = mpDevice->createStructuredBuffer(sizeof(uint32_t), (uint32_t)triangleOffsets.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, triangleOffsets.data(), false); mpPerMeshInstanceOffset->setName("LightCollection::mpPerMeshInstanceOffset"); } } @@ -318,11 +318,11 @@ namespace Falcor // Prepare program vars. { - mIntegrator.pVars = GraphicsVars::create(mpDevice, mIntegrator.pProgram.get()); + mIntegrator.pVars = ProgramVars::create(mpDevice, mIntegrator.pProgram.get()); auto var = mIntegrator.pVars->getRootVar(); - var["gScene"] = scene.getParameterBlock(); + scene.bindShaderData(var["gScene"]); var["gPointSampler"] = mIntegrator.pPointSampler; - setShaderData(var["gLightCollection"]); + bindShaderData(var["gLightCollection"]); } // 1st pass: Rasterize emissive triangles in texture space to find maximum texel value. @@ -331,7 +331,7 @@ namespace Falcor { // Allocate intermediate buffer. // Move into a member variable if we're integrating multiple times to avoid re-allocation. - pTexelMax = Buffer::create(mpDevice, mTriangleCount * sizeof(uint32_t), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + pTexelMax = mpDevice->createBuffer(mTriangleCount * sizeof(uint32_t), ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal); pTexelMax->setName("LightCollection: pTexelMax"); pRenderContext->clearUAV(pTexelMax->getUAV().get(), uint4(0)); @@ -354,7 +354,7 @@ namespace Falcor const uint32_t bufSize = mTriangleCount * 4 * sizeof(uint64_t); if (!mIntegrator.pResultBuffer || mIntegrator.pResultBuffer->getSize() < bufSize) { - mIntegrator.pResultBuffer = Buffer::create(mpDevice, bufSize, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + mIntegrator.pResultBuffer = mpDevice->createBuffer(bufSize, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal); mIntegrator.pResultBuffer->setName("LightCollection::mIntegrator::pResultBuffer"); } @@ -374,7 +374,7 @@ namespace Falcor auto var = mpFinalizeIntegration->getRootVar(); // Bind scene. - var["gScene"] = scene.getParameterBlock(); + scene.bindShaderData(var["gScene"]); var["gPointSampler"] = mIntegrator.pPointSampler; var["gTexelMax"] = pTexelMax; @@ -392,18 +392,15 @@ namespace Falcor #if 0 // Output a list of per-triangle results to file for debugging purposes. std::ofstream ofs("flux.txt"); - const float* pMax = (const float*)pTexelMax->map(Buffer::MapType::Read); - const float4* flux = (const float4*)mpFluxData->map(Buffer::MapType::Read); - const uint64_t* result = (const uint64_t*)mIntegrator.pResultBuffer->map(Buffer::MapType::Read); + std::vector texelMax = pTexelMax->getElements(); + std::vector fluxData = mpFluxData->getElements(); + std::vector result = mIntegrator.pResultBuffer->getElements(); for (uint32_t i = 0; i < mpFluxData->getElementCount(); i++) { uint64_t w = result[4 * i + 3]; float weight = w / (float)(1ull << 35); - ofs << std::setw(6) << i << " : max " << std::setw(12) << pMax[i] << " weight " << std::setw(12) << weight << " value " << to_string(flux[i]) << std::endl; + ofs << std::setw(6) << i << " : max " << std::setw(12) << texelMax[i] << " weight " << std::setw(12) << weight << " value " << to_string(fluxData[i]) << std::endl; } - mpFluxData->unmap(); - pTexelMax->unmap(); - mIntegrator.pResultBuffer->unmap(); #endif } @@ -468,7 +465,7 @@ namespace Falcor auto var = mpTriangleListBuilder->getRootVar(); // Bind scene. - var["gScene"] = scene.getParameterBlock(); + scene.bindShaderData(var["gScene"]); // Bind our output buffer. var["gTriangleData"] = mpTriangleData; @@ -527,7 +524,7 @@ namespace Falcor { if (!mpActiveTriangleList || mpActiveTriangleList->getElementCount() < activeCount) { - mpActiveTriangleList = Buffer::createStructured(mpDevice, sizeof(uint32_t), activeCount, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, mActiveTriangleList.data(), false); + mpActiveTriangleList = mpDevice->createStructuredBuffer(sizeof(uint32_t), activeCount, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, mActiveTriangleList.data(), false); mpActiveTriangleList->setName("LightCollection::mpActiveTriangleList"); } else @@ -538,7 +535,7 @@ namespace Falcor if (!mpTriToActiveList || mpTriToActiveList->getElementCount() < triCount) { - mpTriToActiveList = Buffer::createStructured(mpDevice, sizeof(uint32_t), triCount, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, mTriToActiveList.data(), false); + mpTriToActiveList = mpDevice->createStructuredBuffer(sizeof(uint32_t), triCount, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, mTriToActiveList.data(), false); mpTriToActiveList->setName("LightCollection::mpTriToActiveList"); } else @@ -559,7 +556,7 @@ namespace Falcor auto var = mpTrianglePositionUpdater->getRootVar(); // Bind scene. - var["gScene"] = scene.getParameterBlock(); + scene.bindShaderData(var["gScene"]); // Bind our resources. var["gTriangleData"] = mpTriangleData; @@ -574,7 +571,7 @@ namespace Falcor mStagingBufferValid = false; } - void LightCollection::setShaderData(const ShaderVar& var) const + void LightCollection::bindShaderData(const ShaderVar& var) const { FALCOR_ASSERT(var.isValid()); @@ -616,7 +613,7 @@ namespace Falcor const size_t stagingSize = mpTriangleData->getSize() + mpFluxData->getSize(); if (!mpStagingBuffer || mpStagingBuffer->getSize() < stagingSize) { - mpStagingBuffer = Buffer::create(mpDevice, stagingSize, Resource::BindFlags::None, Buffer::CpuAccess::Read); + mpStagingBuffer = mpDevice->createBuffer(stagingSize, ResourceBindFlags::None, MemoryType::ReadBack); mpStagingBuffer->setName("LightCollection::mpStagingBuffer"); mCPUInvalidData = CPUOutOfDateFlags::All; } @@ -637,8 +634,8 @@ namespace Falcor FALCOR_ASSERT(offset == stagingSize); // Submit command list and insert signal. - pRenderContext->flush(false); - mpStagingFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + pRenderContext->submit(false); + pRenderContext->signal(mpStagingFence.get()); // Resize the CPU-side triangle list (array-of-structs) buffer and mark the data as invalid. mMeshLightTriangles.resize(mTriangleCount); @@ -659,7 +656,7 @@ namespace Falcor } // Wait for signal. - mpStagingFence->syncCpu(); + mpStagingFence->wait(); FALCOR_ASSERT(mStagingBufferValid); FALCOR_ASSERT(mpTriangleData && mpFluxData); diff --git a/Source/Falcor/Scene/Lights/LightCollection.h b/Source/Falcor/Scene/Lights/LightCollection.h index 5dc2bb8f3..6f558f940 100644 --- a/Source/Falcor/Scene/Lights/LightCollection.h +++ b/Source/Falcor/Scene/Lights/LightCollection.h @@ -31,9 +31,9 @@ #include "Core/Object.h" #include "Core/API/Buffer.h" #include "Core/API/Sampler.h" -#include "Core/API/GpuFence.h" +#include "Core/API/Fence.h" #include "Core/State/GraphicsState.h" -#include "Core/Program/GraphicsProgram.h" +#include "Core/Program/Program.h" #include "Core/Program/ProgramVars.h" #include "Core/Pass/ComputePass.h" #include "Utils/Math/Vector.h" @@ -139,7 +139,7 @@ namespace Falcor /** Bind the light collection data to a given shader var \param[in] var The shader variable to set the data into. */ - void setShaderData(const ShaderVar& var) const; + void bindShaderData(const ShaderVar& var) const; /** Returns the total number of active (non-culled) triangle lights. */ @@ -223,15 +223,15 @@ namespace Falcor ref mpPerMeshInstanceOffset; ///< Per-mesh instance offset into emissive triangles array (Scene::getMeshInstanceCount() elements). mutable ref mpStagingBuffer; ///< Staging buffer used for retrieving the vertex positions, texture coordinates and light IDs from the GPU. - ref mpStagingFence; ///< Fence used for waiting on the staging buffer being filled in. + ref mpStagingFence; ///< Fence used for waiting on the staging buffer being filled in. ref mpSamplerState; ///< Material sampler for emissive textures. // Shader programs. struct { - ref pProgram; - ref pVars; + ref pProgram; + ref pVars; ref pState; ref pPointSampler; ///< Point sampler for fetching individual texels in integrator. Must use same wrap mode etc. as material sampler. ref pResultBuffer; ///< The output of the integration pass is written here. Using raw buffer for fp32 compatibility. diff --git a/Source/Falcor/Scene/Lights/LightProfile.cpp b/Source/Falcor/Scene/Lights/LightProfile.cpp index 1748b0285..14afa155a 100644 --- a/Source/Falcor/Scene/Lights/LightProfile.cpp +++ b/Source/Falcor/Scene/Lights/LightProfile.cpp @@ -188,16 +188,15 @@ namespace Falcor , mRawData(rawData) {} - ref LightProfile::createFromIesProfile(ref pDevice, const std::filesystem::path& filename, bool normalize) + ref LightProfile::createFromIesProfile(ref pDevice, const std::filesystem::path& path, bool normalize) { - std::filesystem::path fullpath; - if (!findFileInDataDirectories(filename, fullpath)) + std::ifstream ifs(path); + if (!ifs.good()) { - logWarning("Error when loading light profile. Can't find file '{}'", filename); + logWarning("Error when loading light profile. Can't open file '{}'", path); return nullptr; } - std::ifstream ifs(fullpath); std::string str; ifs.seekg(0, std::ios::end); str.reserve(ifs.tellg()); @@ -213,14 +212,14 @@ namespace Falcor case IesStatus::UnsupportedTilt: case IesStatus::WrongDataSize: case IesStatus::InvalidData: - logWarning("Error while loading IES profile from '{}'.", fullpath); + logWarning("Error while loading IES profile from '{}'.", path); return nullptr; } // Stash the normalization factor in data[0], we don't use that anyway numericData[0] = normalize ? (1.f / maxCandelas) : 1.f; - std::string name = fullpath.filename().string(); + std::string name = path.filename().string(); return ref(new LightProfile(pDevice, name, numericData)); } @@ -228,9 +227,9 @@ namespace Falcor void LightProfile::bake(RenderContext* pRenderContext) { auto pBakePass = ComputePass::create(mpDevice, kBakeIesProfileFile, "main"); - auto pBuffer = Buffer::createTyped(mpDevice, (uint32_t)mRawData.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, mRawData.data()); - mpTexture = Texture::create2D(mpDevice, kBakeResolution, kBakeResolution, ResourceFormat::R16Float, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); - auto pFluxTexture = Texture::create2D(mpDevice, kBakeResolution, kBakeResolution, ResourceFormat::R32Float, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + auto pBuffer = mpDevice->createTypedBuffer((uint32_t)mRawData.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, mRawData.data()); + mpTexture = mpDevice->createTexture2D(kBakeResolution, kBakeResolution, ResourceFormat::R16Float, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + auto pFluxTexture = mpDevice->createTexture2D(kBakeResolution, kBakeResolution, ResourceFormat::R32Float, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); auto var = pBakePass->getRootVar(); var["gIesData"] = pBuffer; @@ -245,11 +244,11 @@ namespace Falcor mFluxFactor = fluxFactor.x; Sampler::Desc desc; - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mpSampler = Sampler::create(mpDevice, desc); + desc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Linear); + mpSampler = mpDevice->createSampler(desc); } - void LightProfile::setShaderData(const ShaderVar& var) const + void LightProfile::bindShaderData(const ShaderVar& var) const { var["fluxFactor"] = mFluxFactor; var["texture"] = mpTexture; diff --git a/Source/Falcor/Scene/Lights/LightProfile.h b/Source/Falcor/Scene/Lights/LightProfile.h index d5a76d36b..63895fcc7 100644 --- a/Source/Falcor/Scene/Lights/LightProfile.h +++ b/Source/Falcor/Scene/Lights/LightProfile.h @@ -45,13 +45,13 @@ namespace Falcor { FALCOR_OBJECT(LightProfile) public: - static ref createFromIesProfile(ref pDevice, const std::filesystem::path& filename, bool normalize); + static ref createFromIesProfile(ref pDevice, const std::filesystem::path& path, bool normalize); void bake(RenderContext* pRenderContext); /** Set the light profile into a shader var. */ - void setShaderData(const ShaderVar& var) const; + void bindShaderData(const ShaderVar& var) const; /** Render the UI. */ diff --git a/Source/Falcor/Scene/Lights/MeshLightData.slang b/Source/Falcor/Scene/Lights/MeshLightData.slang index 6153e34ae..c883746e6 100644 --- a/Source/Falcor/Scene/Lights/MeshLightData.slang +++ b/Source/Falcor/Scene/Lights/MeshLightData.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Scene/Material/BasicMaterial.cpp b/Source/Falcor/Scene/Material/BasicMaterial.cpp index fa108a5b2..634baad75 100644 --- a/Source/Falcor/Scene/Material/BasicMaterial.cpp +++ b/Source/Falcor/Scene/Material/BasicMaterial.cpp @@ -29,7 +29,7 @@ #include "MaterialSystem.h" #include "Core/API/Device.h" #include "Core/API/RenderContext.h" -#include "Core/Program/GraphicsProgram.h" +#include "Core/Program/Program.h" #include "Core/Program/ProgramVars.h" #include "Utils/Logger.h" #include "Utils/Math/Common.h" @@ -191,7 +191,6 @@ namespace Falcor { FALCOR_ASSERT(pOwner); - auto flags = Material::UpdateFlags::None; if (mUpdates != Material::UpdateFlags::None) { // Adjust material sidedness based on current parameters. @@ -218,10 +217,14 @@ namespace Falcor mData.setDisplacementMaxSamplerID(pOwner->addTextureSampler(mpDisplacementMaxSampler)); if (mData.flags != prevFlags) mUpdates |= Material::UpdateFlags::DataChanged; - flags |= mUpdates; - mUpdates = Material::UpdateFlags::None; + // Assume any update for an emissive materials can change its emissive properties. + if (mUpdates != Material::UpdateFlags::None && isEmissive()) + mUpdates |= Material::UpdateFlags::EmissiveChanged; } + auto flags = mUpdates; + mUpdates = Material::UpdateFlags::None; + return flags; } @@ -269,10 +272,10 @@ namespace Falcor // Create derived samplers for displacement Min/Max filtering. Sampler::Desc desc = pSampler->getDesc(); desc.setMaxAnisotropy(16); // Set 16x anisotropic filtering for improved min/max precision per triangle. - desc.setReductionMode(Sampler::ReductionMode::Min); - mpDisplacementMinSampler = Sampler::create(mpDevice, desc); - desc.setReductionMode(Sampler::ReductionMode::Max); - mpDisplacementMaxSampler = Sampler::create(mpDevice, desc); + desc.setReductionMode(TextureReductionMode::Min); + mpDisplacementMinSampler = mpDevice->createSampler(desc); + desc.setReductionMode(TextureReductionMode::Max); + mpDisplacementMaxSampler = mpDevice->createSampler(desc); markUpdates(UpdateFlags::ResourcesChanged); } @@ -428,7 +431,7 @@ namespace Falcor break; } default: - throw ArgumentError("'slot' refers to unexpected texture slot {}", (uint32_t)slot); + FALCOR_THROW("'slot' refers to unexpected texture slot {}", (uint32_t)slot); } } @@ -450,8 +453,8 @@ namespace Falcor if (getFormatChannelCount(oldFormat) < 4) { Falcor::ResourceFormat newFormat = ResourceFormat::RGBA16Float; - Resource::BindFlags bf = pDisplacementMap->getBindFlags() | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget; - ref newDisplacementTex = Texture::create2D(mpDevice, pDisplacementMap->getWidth(), pDisplacementMap->getHeight(), newFormat, pDisplacementMap->getArraySize(), Resource::kMaxPossible, nullptr, bf); + ResourceBindFlags bf = pDisplacementMap->getBindFlags() | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::RenderTarget; + ref newDisplacementTex = mpDevice->createTexture2D(pDisplacementMap->getWidth(), pDisplacementMap->getHeight(), newFormat, pDisplacementMap->getArraySize(), Resource::kMaxPossible, nullptr, bf); // Copy base level. uint32_t arraySize = pDisplacementMap->getArraySize(); @@ -459,9 +462,9 @@ namespace Falcor { auto srv = pDisplacementMap->getSRV(0, 1, a, 1); auto rtv = newDisplacementTex->getRTV(0, a, 1); - const Sampler::ReductionMode redModes[] = { Sampler::ReductionMode::Standard, Sampler::ReductionMode::Standard, Sampler::ReductionMode::Standard, Sampler::ReductionMode::Standard }; + const TextureReductionMode redModes[] = { TextureReductionMode::Standard, TextureReductionMode::Standard, TextureReductionMode::Standard, TextureReductionMode::Standard }; const float4 componentsTransform[] = { float4(1.0f, 0.0f, 0.0f, 0.0f), float4(1.0f, 0.0f, 0.0f, 0.0f), float4(1.0f, 0.0f, 0.0f, 0.0f), float4(1.0f, 0.0f, 0.0f, 0.0f) }; - pRenderContext->blit(srv, rtv, RenderContext::kMaxRect, RenderContext::kMaxRect, Sampler::Filter::Linear, redModes, componentsTransform); + pRenderContext->blit(srv, rtv, RenderContext::kMaxRect, RenderContext::kMaxRect, TextureFilteringMode::Linear, redModes, componentsTransform); } pDisplacementMap = newDisplacementTex; @@ -652,7 +655,7 @@ namespace Falcor if (mHeader.isEmissive() != isEmissive) { mHeader.setEmissive(isEmissive); - markUpdates(UpdateFlags::DataChanged); + markUpdates(UpdateFlags::DataChanged | UpdateFlags::EmissiveChanged); } } diff --git a/Source/Falcor/Scene/Material/BasicMaterial.h b/Source/Falcor/Scene/Material/BasicMaterial.h index 4110cc78f..2d7819983 100644 --- a/Source/Falcor/Scene/Material/BasicMaterial.h +++ b/Source/Falcor/Scene/Material/BasicMaterial.h @@ -172,10 +172,12 @@ namespace Falcor /** Set the base color. */ void setBaseColor(const float4& color); + void setBaseColor3(const float3& color) { setBaseColor(float4(color, getBaseColor().w)); } /** Get the base color. */ float4 getBaseColor() const { return (float4)mData.baseColor; } + float3 getBaseColor3() const { return getBaseColor().xyz(); } /** Set the specular parameters. */ diff --git a/Source/Falcor/Scene/Material/BasicMaterialData.slang b/Source/Falcor/Scene/Material/BasicMaterialData.slang index 40dcb1769..0e4a9d904 100644 --- a/Source/Falcor/Scene/Material/BasicMaterialData.slang +++ b/Source/Falcor/Scene/Material/BasicMaterialData.slang @@ -77,10 +77,12 @@ struct BasicMaterialData static constexpr uint kShadingModelBits = 1; static constexpr uint kNormalMapTypeBits = 2; + static constexpr uint kEntryPointVolumePropertiesBits = 1; static constexpr uint kShadingModelOffset = 0; static constexpr uint kNormalMapTypeOffset = kShadingModelOffset + kShadingModelBits; - static constexpr uint kMinSamplerIDOffset = kNormalMapTypeOffset + kNormalMapTypeBits; + static constexpr uint kEntryPointVolumePropertiesOffset = kNormalMapTypeOffset + kNormalMapTypeBits; + static constexpr uint kMinSamplerIDOffset = kEntryPointVolumePropertiesOffset + kEntryPointVolumePropertiesBits; static constexpr uint kMaxSamplerIDOffset = kMinSamplerIDOffset + MaterialHeader::kSamplerIDBits; static constexpr uint kTotalFlagsBits = kMaxSamplerIDOffset + MaterialHeader::kSamplerIDBits; @@ -101,6 +103,9 @@ struct BasicMaterialData */ NormalMapType getNormalMapType() CONST_FUNCTION { return NormalMapType(EXTRACT_BITS(kNormalMapTypeBits, kNormalMapTypeOffset, flags)); } + SETTER_DECL void setHasEntryPointVolumeProperties(bool poe) { flags = PACK_BITS(kEntryPointVolumePropertiesBits, kEntryPointVolumePropertiesOffset, flags, (uint)poe); } + bool getHasEntryPointVolumeProperties() CONST_FUNCTION { return bool(EXTRACT_BITS(kEntryPointVolumePropertiesBits, kEntryPointVolumePropertiesOffset, flags)); } + /** Set displacement map min/max sampler ID. */ SETTER_DECL void setDisplacementMinSamplerID(uint samplerID) { flags = PACK_BITS(MaterialHeader::kSamplerIDBits, kMinSamplerIDOffset, flags, samplerID); } diff --git a/Source/Falcor/Scene/Material/ClothMaterial.cpp b/Source/Falcor/Scene/Material/ClothMaterial.cpp index ff881e35b..d0e885feb 100644 --- a/Source/Falcor/Scene/Material/ClothMaterial.cpp +++ b/Source/Falcor/Scene/Material/ClothMaterial.cpp @@ -45,12 +45,12 @@ namespace Falcor mTextureSlotInfo[(uint32_t)TextureSlot::Normal] = { "normal", TextureChannelFlags::RGB, false }; } - Program::ShaderModuleList ClothMaterial::getShaderModules() const + ProgramDesc::ShaderModuleList ClothMaterial::getShaderModules() const { - return { Program::ShaderModule(kShaderFile) }; + return { ProgramDesc::ShaderModule::fromFile(kShaderFile) }; } - Program::TypeConformanceList ClothMaterial::getTypeConformances() const + TypeConformanceList ClothMaterial::getTypeConformances() const { return { {{"ClothMaterial", "IMaterial"}, (uint32_t)MaterialType::Cloth} }; } diff --git a/Source/Falcor/Scene/Material/ClothMaterial.h b/Source/Falcor/Scene/Material/ClothMaterial.h index 56111df99..8ee4bc39a 100644 --- a/Source/Falcor/Scene/Material/ClothMaterial.h +++ b/Source/Falcor/Scene/Material/ClothMaterial.h @@ -55,8 +55,8 @@ namespace Falcor ClothMaterial(ref pDevice, const std::string& name); - Program::ShaderModuleList getShaderModules() const override; - Program::TypeConformanceList getTypeConformances() const override; + ProgramDesc::ShaderModuleList getShaderModules() const override; + TypeConformanceList getTypeConformances() const override; /** Set the roughness. */ diff --git a/Source/Falcor/Scene/Material/HairMaterial.cpp b/Source/Falcor/Scene/Material/HairMaterial.cpp index 657ee6acd..b83d261c3 100644 --- a/Source/Falcor/Scene/Material/HairMaterial.cpp +++ b/Source/Falcor/Scene/Material/HairMaterial.cpp @@ -44,12 +44,12 @@ namespace Falcor mTextureSlotInfo[(uint32_t)TextureSlot::Specular] = { "specular", TextureChannelFlags::RGB, false }; } - Program::ShaderModuleList HairMaterial::getShaderModules() const + ProgramDesc::ShaderModuleList HairMaterial::getShaderModules() const { - return { Program::ShaderModule(kShaderFile) }; + return { ProgramDesc::ShaderModule::fromFile(kShaderFile) }; } - Program::TypeConformanceList HairMaterial::getTypeConformances() const + TypeConformanceList HairMaterial::getTypeConformances() const { return { {{"HairMaterial", "IMaterial"}, (uint32_t)MaterialType::Hair} }; } diff --git a/Source/Falcor/Scene/Material/HairMaterial.h b/Source/Falcor/Scene/Material/HairMaterial.h index 31f6bd71d..4b22d0148 100644 --- a/Source/Falcor/Scene/Material/HairMaterial.h +++ b/Source/Falcor/Scene/Material/HairMaterial.h @@ -53,8 +53,8 @@ namespace Falcor HairMaterial(ref pDevice, const std::string& name); - Program::ShaderModuleList getShaderModules() const override; - Program::TypeConformanceList getTypeConformances() const override; + ProgramDesc::ShaderModuleList getShaderModules() const override; + TypeConformanceList getTypeConformances() const override; /** Compute sigmaA from eumelanin and pheomelanin concentration. */ diff --git a/Source/Falcor/Scene/Material/MERLFile.cpp b/Source/Falcor/Scene/Material/MERLFile.cpp index ff2c3c113..d1ec7b59c 100644 --- a/Source/Falcor/Scene/Material/MERLFile.cpp +++ b/Source/Falcor/Scene/Material/MERLFile.cpp @@ -53,7 +53,7 @@ namespace Falcor MERLFile::MERLFile(const std::filesystem::path& path) { if (!loadBRDF(path)) - throw RuntimeError("Failed to load MERL BRDF from '{}'", path); + FALCOR_THROW("Failed to load MERL BRDF from '{}'", path); } bool MERLFile::loadBRDF(const std::filesystem::path& path) @@ -147,7 +147,7 @@ namespace Falcor if (!mAlbedoLUT.empty()) return mAlbedoLUT; - checkInvariant(!mDesc.path.empty(), "No BRDF loaded"); + FALCOR_CHECK(!mDesc.path.empty(), "No BRDF loaded"); const auto texPath = mDesc.path.replace_extension("dds"); // Try loading cached albedo lookup table. diff --git a/Source/Falcor/Scene/Material/MERLMaterial.cpp b/Source/Falcor/Scene/Material/MERLMaterial.cpp index e6e5d3881..287dc1298 100644 --- a/Source/Falcor/Scene/Material/MERLMaterial.cpp +++ b/Source/Falcor/Scene/Material/MERLMaterial.cpp @@ -46,20 +46,14 @@ namespace Falcor MERLMaterial::MERLMaterial(ref pDevice, const std::string& name, const std::filesystem::path& path) : Material(pDevice, name, MaterialType::MERL) { - std::filesystem::path fullPath; - if (!findFileInDataDirectories(path, fullPath)) - { - throw RuntimeError("MERLMaterial: Can't find file '{}'.", path); - } - - MERLFile merlFile(fullPath); + MERLFile merlFile(path); init(merlFile); // Create albedo LUT texture. auto lut = merlFile.prepareAlbedoLUT(mpDevice); - checkInvariant(!lut.empty() && sizeof(lut[0]) == sizeof(float4), "Expected albedo LUT in float4 format."); + FALCOR_CHECK(!lut.empty() && sizeof(lut[0]) == sizeof(float4), "Expected albedo LUT in float4 format."); static_assert(MERLFile::kAlbedoLUTFormat == ResourceFormat::RGBA32Float); - mpAlbedoLUT = Texture::create2D(mpDevice, (uint32_t)lut.size(), 1, MERLFile::kAlbedoLUTFormat, 1, 1, lut.data(), ResourceBindFlags::ShaderResource); + mpAlbedoLUT = mpDevice->createTexture2D((uint32_t)lut.size(), 1, MERLFile::kAlbedoLUTFormat, 1, 1, lut.data(), ResourceBindFlags::ShaderResource); } MERLMaterial::MERLMaterial(ref pDevice, const MERLFile& merlFile) @@ -76,15 +70,15 @@ namespace Falcor // Create GPU buffer. const auto& brdf = merlFile.getData(); - checkInvariant(!brdf.empty() && sizeof(brdf[0]) == sizeof(float3), "Expected BRDF data in float3 format."); - mpBRDFData = Buffer::create(mpDevice, brdf.size() * sizeof(brdf[0]), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, brdf.data()); + FALCOR_CHECK(!brdf.empty() && sizeof(brdf[0]) == sizeof(float3), "Expected BRDF data in float3 format."); + mpBRDFData = mpDevice->createBuffer(brdf.size() * sizeof(brdf[0]), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, brdf.data()); // Create sampler for albedo LUT. Sampler::Desc desc; - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Point, Sampler::Filter::Point); - desc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); + desc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Point, TextureFilteringMode::Point); + desc.setAddressingMode(TextureAddressingMode::Clamp, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); desc.setMaxAnisotropy(1); - mpLUTSampler = Sampler::create(mpDevice, desc); + mpLUTSampler = mpDevice->createSampler(desc); markUpdates(Material::UpdateFlags::ResourcesChanged); } @@ -145,12 +139,12 @@ namespace Falcor return true; } - Program::ShaderModuleList MERLMaterial::getShaderModules() const + ProgramDesc::ShaderModuleList MERLMaterial::getShaderModules() const { - return { Program::ShaderModule(kShaderFile) }; + return { ProgramDesc::ShaderModule::fromFile(kShaderFile) }; } - Program::TypeConformanceList MERLMaterial::getTypeConformances() const + TypeConformanceList MERLMaterial::getTypeConformances() const { return { {{"MERLMaterial", "IMaterial"}, (uint32_t)MaterialType::MERL} }; } @@ -164,7 +158,7 @@ namespace Falcor pybind11::class_> material(m, "MERLMaterial"); auto create = [] (const std::string& name, const std::filesystem::path& path) { - return MERLMaterial::create(accessActivePythonSceneBuilder().getDevice(), name, path); + return MERLMaterial::create(accessActivePythonSceneBuilder().getDevice(), name, getActiveAssetResolver().resolvePath(path)); }; material.def(pybind11::init(create), "name"_a, "path"_a); // PYTHONDEPRECATED } diff --git a/Source/Falcor/Scene/Material/MERLMaterial.h b/Source/Falcor/Scene/Material/MERLMaterial.h index 36a2ac98b..384ba59ae 100644 --- a/Source/Falcor/Scene/Material/MERLMaterial.h +++ b/Source/Falcor/Scene/Material/MERLMaterial.h @@ -53,8 +53,8 @@ namespace Falcor Material::UpdateFlags update(MaterialSystem* pOwner) override; bool isEqual(const ref& pOther) const override; MaterialDataBlob getDataBlob() const override { return prepareDataBlob(mData); } - Program::ShaderModuleList getShaderModules() const override; - Program::TypeConformanceList getTypeConformances() const override; + ProgramDesc::ShaderModuleList getShaderModules() const override; + TypeConformanceList getTypeConformances() const override; size_t getMaxBufferCount() const override { return 1; } diff --git a/Source/Falcor/Scene/Material/MERLMixMaterial.cpp b/Source/Falcor/Scene/Material/MERLMixMaterial.cpp index 74e5830fb..74a30d691 100644 --- a/Source/Falcor/Scene/Material/MERLMixMaterial.cpp +++ b/Source/Falcor/Scene/Material/MERLMixMaterial.cpp @@ -49,7 +49,7 @@ namespace Falcor MERLMixMaterial::MERLMixMaterial(ref pDevice, const std::string& name, const std::vector& paths) : Material(pDevice, name, MaterialType::MERLMix) { - checkArgument(!paths.empty(), "MERLMixMaterial: Expected at least one path."); + FALCOR_CHECK(!paths.empty(), "MERLMixMaterial: Expected at least one path."); // Setup texture slots. mTextureSlotInfo[(uint32_t)TextureSlot::Normal] = { "normal", TextureChannelFlags::RGB, false }; @@ -65,7 +65,7 @@ namespace Falcor for (size_t i = 0; i < paths.size(); i++) { if (!merlFile.loadBRDF(paths[i])) - throw RuntimeError("MERLMixMaterial: Failed to load BRDF from '{}'.", paths[i]); + FALCOR_THROW("MERLMixMaterial: Failed to load BRDF from '{}'.", paths[i]); auto& desc = mBRDFs[i]; desc.path = merlFile.getDesc().path; @@ -74,14 +74,14 @@ namespace Falcor // Copy BRDF samples into shared data buffer. const auto& brdf = merlFile.getData(); - checkInvariant(!brdf.empty() && sizeof(brdf[0]) == sizeof(float3), "Expected BRDF data in float3 format."); + FALCOR_CHECK(!brdf.empty() && sizeof(brdf[0]) == sizeof(float3), "Expected BRDF data in float3 format."); desc.byteSize = brdf.size() * sizeof(brdf[0]); desc.byteOffset = buffer.allocate(desc.byteSize); buffer.setBlob(brdf.data(), desc.byteOffset, desc.byteSize); // Copy albedo LUT into shared table. const auto& lut = merlFile.prepareAlbedoLUT(mpDevice); - checkInvariant(lut.size() == MERLMixMaterialData::kAlbedoLUTSize, "MERLMixMaterial: Unexpected albedo LUT size."); + FALCOR_CHECK(lut.size() == MERLMixMaterialData::kAlbedoLUTSize, "MERLMixMaterial: Unexpected albedo LUT size."); albedoLut.insert(albedoLut.end(), lut.begin(), lut.end()); } @@ -89,7 +89,7 @@ namespace Falcor mData.byteStride = mBRDFs.size() > 1 ? mBRDFs[1].byteOffset : 0; for (size_t i = 0; i < mBRDFs.size(); i++) { - checkInvariant(mBRDFs[i].byteOffset == i * mData.byteStride, "MERLMixMaterial: Unexpected stride."); + FALCOR_CHECK(mBRDFs[i].byteOffset == i * mData.byteStride, "MERLMixMaterial: Unexpected stride."); } // Upload extra data for sampling. @@ -104,24 +104,24 @@ namespace Falcor mpBRDFData = buffer.getGPUBuffer(mpDevice); // Create albedo LUT as 2D texture parameterization over (cosTehta, brdfIndex). - mpAlbedoLUT = Texture::create2D(mpDevice, MERLMixMaterialData::kAlbedoLUTSize, mData.brdfCount, MERLFile::kAlbedoLUTFormat, 1, 1, albedoLut.data(), ResourceBindFlags::ShaderResource); + mpAlbedoLUT = mpDevice->createTexture2D(MERLMixMaterialData::kAlbedoLUTSize, mData.brdfCount, MERLFile::kAlbedoLUTFormat, 1, 1, albedoLut.data(), ResourceBindFlags::ShaderResource); // Create sampler for albedo LUT. { Sampler::Desc desc; - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Point, Sampler::Filter::Point); - desc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); + desc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Point, TextureFilteringMode::Point); + desc.setAddressingMode(TextureAddressingMode::Clamp, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); desc.setMaxAnisotropy(1); - mpLUTSampler = Sampler::create(mpDevice, desc); + mpLUTSampler = mpDevice->createSampler(desc); } // Create sampler for index map. Using point sampling as indices are not interpolatable. { Sampler::Desc desc; - desc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - desc.setAddressingMode(Sampler::AddressMode::Wrap, Sampler::AddressMode::Wrap, Sampler::AddressMode::Wrap); + desc.setFilterMode(TextureFilteringMode::Point, TextureFilteringMode::Point, TextureFilteringMode::Point); + desc.setAddressingMode(TextureAddressingMode::Wrap, TextureAddressingMode::Wrap, TextureAddressingMode::Wrap); desc.setMaxAnisotropy(1); - mpIndexSampler = Sampler::create(mpDevice, desc); + mpIndexSampler = mpDevice->createSampler(desc); } updateNormalMapType(); @@ -235,12 +235,12 @@ namespace Falcor return true; } - Program::ShaderModuleList MERLMixMaterial::getShaderModules() const + ProgramDesc::ShaderModuleList MERLMixMaterial::getShaderModules() const { - return { Program::ShaderModule(kShaderFile) }; + return { ProgramDesc::ShaderModule::fromFile(kShaderFile) }; } - Program::TypeConformanceList MERLMixMaterial::getTypeConformances() const + TypeConformanceList MERLMixMaterial::getTypeConformances() const { return { {{"MERLMixMaterial", "IMaterial"}, (uint32_t)MaterialType::MERLMix} }; } @@ -292,7 +292,7 @@ namespace Falcor // Verify that index map is in uncompressed 8-bit unorm format. // The shader requires this because the BRDF index is computed by scaling the unorm value by 255. ResourceFormat format = tex->getFormat(); - checkInvariant(!isSrgbFormat(format), "MERLMixMaterial: Index map must not be in SRGB format."); + FALCOR_CHECK(!isSrgbFormat(format), "MERLMixMaterial: Index map must not be in SRGB format."); switch (format) { @@ -303,7 +303,7 @@ namespace Falcor case ResourceFormat::BGRX8Unorm: break; default: - throw RuntimeError(fmt::format("MERLMixMaterial: Index map unsupported format ({}).", to_string(format))); + FALCOR_THROW(fmt::format("MERLMixMaterial: Index map unsupported format ({}).", to_string(format))); } } } diff --git a/Source/Falcor/Scene/Material/MERLMixMaterial.h b/Source/Falcor/Scene/Material/MERLMixMaterial.h index 4c50e3e7b..4e1d2b545 100644 --- a/Source/Falcor/Scene/Material/MERLMixMaterial.h +++ b/Source/Falcor/Scene/Material/MERLMixMaterial.h @@ -55,8 +55,8 @@ namespace Falcor Material::UpdateFlags update(MaterialSystem* pOwner) override; bool isEqual(const ref& pOther) const override; MaterialDataBlob getDataBlob() const override { return prepareDataBlob(mData); } - Program::ShaderModuleList getShaderModules() const override; - Program::TypeConformanceList getTypeConformances() const override; + ProgramDesc::ShaderModuleList getShaderModules() const override; + TypeConformanceList getTypeConformances() const override; size_t getMaxBufferCount() const override { return 1; } bool setTexture(const TextureSlot slot, const ref& pTexture) override; diff --git a/Source/Falcor/Scene/Material/Material.cpp b/Source/Falcor/Scene/Material/Material.cpp index a07252a1a..4a28c7b31 100644 --- a/Source/Falcor/Scene/Material/Material.cpp +++ b/Source/Falcor/Scene/Material/Material.cpp @@ -28,6 +28,8 @@ #include "Material.h" #include "BasicMaterial.h" #include "MaterialSystem.h" +#include "MaterialTypeRegistry.h" +#include "GlobalState.h" #include "Core/API/Device.h" #include "Utils/Logger.h" #include "Utils/Scripting/ScriptBindings.h" @@ -183,6 +185,9 @@ namespace Falcor mTextureSlotData[(size_t)slot].pTexture = pTexture; markUpdates(UpdateFlags::ResourcesChanged); + if (slot == TextureSlot::Emissive) + markUpdates(UpdateFlags::EmissiveChanged); + return true; } @@ -202,20 +207,17 @@ namespace Falcor return false; } - std::filesystem::path fullPath; - if (findFileInDataDirectories(path, fullPath)) + auto texture = Texture::createFromFile(mpDevice, path, true, useSrgb && getTextureSlotInfo(slot).srgb); + if (texture) { - auto texture = Texture::createFromFile(mpDevice, fullPath, true, useSrgb && getTextureSlotInfo(slot).srgb); - if (texture) - { - setTexture(slot, texture); - // Flush and sync in order to prevent the upload heap from growing too large. Doing so after - // every texture creation is overly conservative, and will likely lead to performance issues - // due to the forced CPU/GPU sync. - mpDevice->flushAndSync(); - return true; - } + setTexture(slot, texture); + // Wait for GPU to finish to prevent the upload heap from growing too large. Doing so after + // every texture creation is overly conservative, and will likely lead to performance issues + // due to the forced CPU/GPU sync. + mpDevice->wait(); + return true; } + return false; } @@ -263,14 +265,14 @@ namespace Falcor { auto h = pOwner->getTextureManager().addTexture(pTexture); FALCOR_ASSERT(h); - handle.setTextureID(h.getID()); - handle.setMode(TextureHandle::Mode::Texture); + handle = h.toGpuHandle(); } else { handle.setMode(TextureHandle::Mode::Uniform); + handle.setUdimEnabled(false); } - handle.setUdimEnabled(false); + FALCOR_ASSERT(!handle.getUdimEnabled()); if (handle != prevHandle) mUpdates |= Material::UpdateFlags::DataChanged; } @@ -279,7 +281,15 @@ namespace Falcor { auto pTexture = getTexture(slot); updateTextureHandle(pOwner, pTexture, handle); - }; + + // The base color texture potentially contains the alpha mask in it's alpha channel. + // Set it as the alpha texture handle in the material header. + if (slot == TextureSlot::BaseColor) + { + mHeader.setAlphaTextureHandle(handle); + mUpdates |= Material::UpdateFlags::DataChanged; + } + } void Material::updateDefaultTextureSamplerID(MaterialSystem* pOwner, const ref& pSampler) { @@ -388,7 +398,35 @@ namespace Falcor material.def("setTexture", &Material::setTexture, "slot"_a, "texture"_a); material.def("getTexture", &Material::getTexture, "slot"_a); - material.def("loadTexture", &Material::loadTexture, "slot"_a, "path"_a, "useSrgb"_a = true); + auto loadTexture = [&](Material& self, Material::TextureSlot slot, const std::filesystem::path& path, bool useSrgb) { + return self.loadTexture(slot, getActiveAssetResolver().resolvePath(path), useSrgb); + }; + material.def("loadTexture", loadTexture, "slot"_a, "path"_a, "useSrgb"_a = true); // PYTHONDEPRECATED + material.def("load_texture", loadTexture, "slot"_a, "path"_a, "use_srgb"_a = true); // PYTHONDEPRECATED material.def("clearTexture", &Material::clearTexture, "slot"_a); + + auto getMaterialParamLayoutDict = [&](MaterialType type) -> pybind11::dict { + MaterialParamLayout layout = getMaterialParamLayout(type); + pybind11::dict dict; + for (const auto& entry : layout) + dict[entry.pythonName] = pybind11::dict("offset"_a = entry.offset, "size"_a = entry.size); + return dict; + }; + + auto getMaterialParamLayoutsDict = [getMaterialParamLayoutDict]() -> pybind11::dict { + pybind11::dict dict; + for (uint32_t i = 0; i < uint32_t(MaterialType::BuiltinCount); ++i) + { + auto type = static_cast(i); + auto name = to_string(type); + dict[name.c_str()] = getMaterialParamLayoutDict(type); + } + return dict; + }; + + material.attr("PARAM_COUNT") = SerializedMaterialParams::kParamCount; + + m.def("get_material_param_layout", getMaterialParamLayoutDict, "type"_a); + m.attr("MATERIAL_PARAM_LAYOUTS") = getMaterialParamLayoutsDict(); } } diff --git a/Source/Falcor/Scene/Material/Material.h b/Source/Falcor/Scene/Material/Material.h index 4aee9123b..719d8ed20 100644 --- a/Source/Falcor/Scene/Material/Material.h +++ b/Source/Falcor/Scene/Material/Material.h @@ -29,8 +29,10 @@ #include "MaterialData.slang" #include "TextureHandle.slang" #include "MaterialTypeRegistry.h" +#include "MaterialParamLayout.h" +#include "SerializedMaterialParams.h" #include "Core/Macros.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/Object.h" #include "Core/API/Formats.h" #include "Core/API/Texture.h" @@ -65,6 +67,7 @@ namespace Falcor DataChanged = 0x2, ///< Material data (parameters) changed. ResourcesChanged = 0x4, ///< Material resources (textures, buffers, samplers) changed. DisplacementChanged = 0x8, ///< Displacement mapping parameters changed (only for materials that support displacement). + EmissiveChanged = 0x10, ///< Material emissive properties changed. }; /** Texture slots available for use. @@ -149,6 +152,11 @@ namespace Falcor */ virtual bool isEmissive() const { return mHeader.isEmissive(); } + /** Returns true if the material is dynamic. + Dynamic materials are updated every frame, otherwise `update()` is called reactively upon changes. + */ + virtual bool isDynamic() const { return false; } + /** Compares material to another material. \param[in] pOther Other material. \return true if all materials properties *except* the name are identical. @@ -187,6 +195,10 @@ namespace Falcor */ virtual float getAlphaThreshold() const { return (float)mHeader.getAlphaThreshold(); } + /** Get the alpha mask texture handle. + */ + virtual TextureHandle getAlphaTextureHandle() const { return mHeader.getAlphaTextureHandle(); } + /** Set the nested priority used for nested dielectrics. */ virtual void setNestedPriority(uint32_t priority); @@ -289,12 +301,12 @@ namespace Falcor The shader modules must be added to any program using the material. \return List of shader modules. */ - virtual Program::ShaderModuleList getShaderModules() const = 0; + virtual ProgramDesc::ShaderModuleList getShaderModules() const = 0; /** Get type conformances for the material. The type conformances must be set on any program using the material. */ - virtual Program::TypeConformanceList getTypeConformances() const = 0; + virtual TypeConformanceList getTypeConformances() const = 0; /** Get shader defines for the material. The defines must be set on any program using the material. @@ -327,6 +339,10 @@ namespace Falcor */ virtual size_t getMaterialInstanceByteSize() const { return 128; } + virtual const MaterialParamLayout& getParamLayout() const { FALCOR_THROW("Material does not have a parameter layout."); } + virtual SerializedMaterialParams serializeParams() const { FALCOR_THROW("Material does not support serializing parameters."); } + virtual void deserializeParams(const SerializedMaterialParams& params) { FALCOR_THROW("Material does not support deserializing parameters."); } + protected: Material(ref pDevice, const std::string& name, MaterialType type); @@ -382,7 +398,7 @@ namespace Falcor tostr(Index); #undef tostr default: - throw ArgumentError("Invalid texture slot"); + FALCOR_THROW("Invalid texture slot"); } } diff --git a/Source/Falcor/Scene/Material/MaterialData.slang b/Source/Falcor/Scene/Material/MaterialData.slang index 7c8b1f22f..be6184e95 100644 --- a/Source/Falcor/Scene/Material/MaterialData.slang +++ b/Source/Falcor/Scene/Material/MaterialData.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -30,8 +30,10 @@ #ifdef HOST_CODE #include "MaterialTypes.slang" +#include "TextureHandle.slang" #else __exported import MaterialTypes; +__exported import TextureHandle; #endif BEGIN_NAMESPACE_FALCOR @@ -183,6 +185,12 @@ struct MaterialHeader /// Get index of refraction. float16_t getIoR() CONST_FUNCTION { return asfloat16((uint16_t)EXTRACT_BITS(kIoRBits, kIoROffset, packedData.z)); } + /// Set the alpha mask texture handle. + SETTER_DECL void setAlphaTextureHandle(TextureHandle handle) { packedData.w = handle.packedData; } + + /// Get the alpha mask texture handle. + TextureHandle getAlphaTextureHandle() CONST_FUNCTION { return TextureHandle(packedData.w); } + #ifdef HOST_CODE friend bool operator==(const MaterialHeader& lhs, const MaterialHeader& rhs); friend bool operator!=(const MaterialHeader& lhs, const MaterialHeader& rhs) { return !(lhs == rhs); } diff --git a/Source/Falcor/Scene/Material/MaterialFactory.slang b/Source/Falcor/Scene/Material/MaterialFactory.slang index f7fd1d1aa..9abc4791f 100644 --- a/Source/Falcor/Scene/Material/MaterialFactory.slang +++ b/Source/Falcor/Scene/Material/MaterialFactory.slang @@ -27,7 +27,6 @@ **************************************************************************/ __exported import Rendering.Materials.IMaterial; import Scene.Material.ShadingUtils; -import Scene.Material.AlphaTest; import Utils.Math.ShadingFrame; /** Extension to add shading and material factory to MaterialSystem. @@ -65,7 +64,7 @@ extension MaterialSystem \return A material instance. */ IMaterialInstance getMaterialInstance(const ShadingData sd, L lod, const uint hints = (uint)MaterialInstanceHints::None) - { + { let material = getMaterial(sd.materialID); return material.setupMaterialInstance(this, sd, lod, hints); } @@ -97,56 +96,13 @@ extension MaterialSystem sd.curveRadius = v.curveRadius; // Material data - const MaterialDataBlob m = materialData[materialID]; - - sd.mtl = m.header; + sd.mtl = materialData[materialID].header; sd.materialID = materialID; // Assume the default IoR for vacuum on the front-facing side. // The renderer may override this for nested dielectrics. sd.IoR = 1.f; - // Create IMaterial instance. - let material = getMaterial(materialID); - - // Query material to evaluate opacity. - sd.opacity = material.evalOpacity(this, v, lod); - return sd; } - - /** Perform alpha test at a hit point and return the result. - \param[in] v Interpolated vertex data. - \param[in] materialID Material ID at the hit. - \param[in] lod Method for computing texture level-of-detail, must implement the `ITextureSampler` interface. - \return True if hit should be ignored/discarded. - */ - bool alphaTest(const VertexData v, const uint materialID, L lod) - { - // Load the material header first and early out if alpha test not supported. - const MaterialHeader header = materialData[materialID].header; - - if (header.getAlphaMode() != AlphaMode::Mask) return false; - - // Create IMaterial instance in order to evaluate opacity. - let material = getMaterial(materialID); - - float alpha = material.evalOpacity(this, v, lod); - float alphaThreshold = header.getAlphaThreshold(); - - return evalAlphaTest(alpha, alphaThreshold, v.posW); - } - - /** Perform alpha test at a hit point and return the result. - This version samples alpha at a fixed level of detail. - \param[in] v Interpolated vertex data. - \param[in] materialID Material ID at the hit. - \param[in] lod Fixed texture level of detail. - \return True if hit should be ignored/discarded. - */ - bool alphaTest(const VertexData v, const uint materialID, const float lod) - { - ExplicitLodTextureSampler explicitLOD = { lod }; - return alphaTest(v, materialID, explicitLOD); - } } diff --git a/Source/Falcor/Scene/Material/MaterialParamLayout.h b/Source/Falcor/Scene/Material/MaterialParamLayout.h new file mode 100644 index 000000000..33cd0d692 --- /dev/null +++ b/Source/Falcor/Scene/Material/MaterialParamLayout.h @@ -0,0 +1,44 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include + +namespace Falcor +{ + +struct MaterialParamLayoutEntry +{ + const char* name; + const char* pythonName; + uint32_t size; + uint32_t offset; +}; + +using MaterialParamLayout = std::vector; + +} // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cs.slang b/Source/Falcor/Scene/Material/MaterialParamLayout.slang similarity index 89% rename from Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cs.slang rename to Source/Falcor/Scene/Material/MaterialParamLayout.slang index d456161e5..68ffdab5e 100644 --- a/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cs.slang +++ b/Source/Falcor/Scene/Material/MaterialParamLayout.slang @@ -25,12 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -RWStructuredBuffer result; -ByteAddressBuffer buffer; - -[numthreads(256, 1, 1)] -void readback(uint3 threadId: SV_DispatchThreadID) +struct MaterialParamLayoutEntry { - uint i = threadId.x; - result[i] = buffer.Load(i * 4); -} + uint offset; +}; diff --git a/Source/Falcor/Scene/Material/MaterialParamLayout.slangh b/Source/Falcor/Scene/Material/MaterialParamLayout.slangh new file mode 100644 index 000000000..852ed0c18 --- /dev/null +++ b/Source/Falcor/Scene/Material/MaterialParamLayout.slangh @@ -0,0 +1,100 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once + +#include "Utils/HostDeviceShared.slangh" + +#ifdef HOST_CODE + +#include "MaterialParamLayout.h" + +namespace Falcor +{ +namespace detail +{ +static constexpr float kMaterialParamEpsilon = 1e-4f; +template +T clampMaterialParam(T value, T min = T(kMaterialParamEpsilon), T max = T(1.f - kMaterialParamEpsilon)) +{ + return math::clamp(value, min, max); +} +} // namespace detail +} // namespace Falcor + +#define _EMIT_MATERIAL_PARAM_LAYOUT_ENTRY(type, offset, name, getter, setter, pythonName, ...) \ + MaterialParamLayoutEntry{#name, pythonName, sizeof(type) / 4, offset}, + +#define _EMIT_MATERIAL_PARAM_SERIALIZE(type, offset, name, getter, setter, pythonName, ...) \ + { \ + type value = material->getter(); \ + params.write(value, offset); \ + } + +#define _EMIT_MATERIAL_PARAM_DESERIALIZE(type, offset, name, getter, setter, pythonName, ...) \ + { \ + type value; \ + params.read(value, offset); \ + if (clamp) \ + value = detail::clampMaterialParam(value, ##__VA_ARGS__); \ + material->setter(value); \ + } + +#define DEFINE_MATERIAL_PARAM_LAYOUT(materialName_, params_) \ + struct materialName_##ParamLayout \ + { \ + static const MaterialParamLayout& layout() \ + { \ + static MaterialParamLayout layout = {params_(_EMIT_MATERIAL_PARAM_LAYOUT_ENTRY)}; \ + return layout; \ + } \ + static SerializedMaterialParams serialize(const materialName_* material) \ + { \ + SerializedMaterialParams params; \ + params.fill(0.f); \ + params_(_EMIT_MATERIAL_PARAM_SERIALIZE); \ + return params; \ + } \ + static void deserialize(materialName_* material, const SerializedMaterialParams& params, bool clamp = true) \ + { \ + params_(_EMIT_MATERIAL_PARAM_DESERIALIZE); \ + } \ + }; + +#else // HOST_CODE + +import Scene.Material.MaterialParamLayout; + +#define _EMIT_MATERIAL_PARAM_FIELD(type, offset, name, getter, setter, pythonName, ...) static MaterialParamLayoutEntry name = {offset}; + +#define DEFINE_MATERIAL_PARAM_LAYOUT(materialName_, params_) \ + struct materialName_##ParamLayout \ + { \ + params_(_EMIT_MATERIAL_PARAM_FIELD) \ + }; + +#endif // HOST_CODE diff --git a/Source/Falcor/Scene/Material/MaterialSystem.cpp b/Source/Falcor/Scene/Material/MaterialSystem.cpp index 39ad6f61e..14000540a 100644 --- a/Source/Falcor/Scene/Material/MaterialSystem.cpp +++ b/Source/Falcor/Scene/Material/MaterialSystem.cpp @@ -65,14 +65,14 @@ namespace Falcor { FALCOR_ASSERT(kMaxSamplerCount <= mpDevice->getLimits().maxShaderVisibleSamplers); - mpFence = GpuFence::create(mpDevice); + mpFence = mpDevice->createFence(); mpTextureManager = std::make_unique(mpDevice, kMaxTextureCount); // Create a default texture sampler. Sampler::Desc desc; - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); + desc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Linear); desc.setMaxAnisotropy(8); - mpDefaultTextureSampler = Sampler::create(mpDevice, desc); + mpDefaultTextureSampler = mpDevice->createSampler(desc); } void MaterialSystem::renderUI(Gui::Widgets& widget) @@ -145,7 +145,7 @@ namespace Falcor // Add sampler. if (mTextureSamplers.size() >= kMaxSamplerCount) { - throw RuntimeError("Too many samplers"); + FALCOR_THROW("Too many samplers"); } const uint32_t samplerID = static_cast(mTextureSamplers.size()); @@ -169,7 +169,7 @@ namespace Falcor FALCOR_ASSERT(!mMaterialsChanged); if (mBuffers.size() >= mBufferDescCount) { - throw RuntimeError("Too many buffers"); + FALCOR_THROW("Too many buffers"); } const uint32_t bufferID = static_cast(mBuffers.size()); @@ -182,7 +182,7 @@ namespace Falcor void MaterialSystem::replaceBuffer(uint32_t id, const ref& pBuffer) { FALCOR_ASSERT(pBuffer); - checkArgument(id < mBuffers.size(), "'id' is out of bounds."); + FALCOR_CHECK(id < mBuffers.size(), "'id' is out of bounds."); mBuffers[id] = pBuffer; mBuffersChanged = true; @@ -197,7 +197,7 @@ namespace Falcor return (uint32_t)std::distance(mTextures3D.begin(), it); if (mTextures3D.size() >= mTexture3DDescCount) - throw RuntimeError("Too many 3D textures"); + FALCOR_THROW("Too many 3D textures"); const uint32_t textureID = static_cast(mTextures3D.size()); @@ -209,7 +209,7 @@ namespace Falcor MaterialID MaterialSystem::addMaterial(const ref& pMaterial) { - checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); + FALCOR_CHECK(pMaterial != nullptr, "'pMaterial' is missing"); // Reuse previously added materials. if (auto it = std::find(mMaterials.begin(), mMaterials.end(), pMaterial); it != mMaterials.end()) @@ -220,7 +220,7 @@ namespace Falcor // Add material. if (mMaterials.size() >= std::numeric_limits::max()) { - throw RuntimeError("Too many materials"); + FALCOR_THROW("Too many materials"); } const MaterialID materialID{ mMaterials.size() }; @@ -236,46 +236,62 @@ namespace Falcor return materialID; } + void MaterialSystem::replaceMaterial(const MaterialID materialID, const ref& pReplacement) + { + FALCOR_CHECK(materialID.isValid() && materialID.get() < mMaterials.size(), "Material ID is invalid."); + FALCOR_CHECK(pReplacement != nullptr, "Replacement material is missing."); + + // Mark descriptors used by the deleted material as reserved. + // This is a workaround until we have dynamically sized descriptor arrays and proper resource tracking. + const auto& prevMtl = mMaterials[materialID.get()]; + mReservedTextureDescCount += prevMtl->getMaxTextureCount(); + mReservedBufferDescCount += prevMtl->getMaxBufferCount(); + mReservedTexture3DDescCount += prevMtl->getMaxTexture3DCount(); + + // Prepare replacement material. + if (pReplacement->getDefaultTextureSampler() == nullptr) + { + pReplacement->setDefaultTextureSampler(mpDefaultTextureSampler); + } + pReplacement->registerUpdateCallback([this](auto flags) { mMaterialUpdates |= flags; }); + + // Replace the material. + mMaterials[materialID.get()] = pReplacement; + mMaterialsChanged = true; + } + void MaterialSystem::replaceMaterial(const ref& pMaterial, const ref& pReplacement) { - checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); - checkArgument(pReplacement != nullptr, "'pReplacement' is missing"); + FALCOR_CHECK(pMaterial != nullptr, "'pMaterial' is missing"); // Find material to replace. if (auto it = std::find(mMaterials.begin(), mMaterials.end(), pMaterial); it != mMaterials.end()) { - *it = pReplacement; - - if (pReplacement->getDefaultTextureSampler() == nullptr) - { - pReplacement->setDefaultTextureSampler(mpDefaultTextureSampler); - } - - pReplacement->registerUpdateCallback([this](auto flags) { mMaterialUpdates |= flags; }); - mMaterialsChanged = true; + auto materialID = MaterialID{ (size_t)std::distance(mMaterials.begin(), it) }; + replaceMaterial(materialID, pReplacement); } else { - throw RuntimeError("Material does not exist"); + FALCOR_THROW("Material does not exist"); } } uint32_t MaterialSystem::getMaterialCountByType(const MaterialType type) const { - checkInvariant(!mMaterialsChanged, "Materials have changed. Call update() first."); + FALCOR_CHECK(!mMaterialsChanged, "Materials have changed. Call update() first."); size_t index = (size_t)type; return (index < mMaterialCountByType.size()) ? mMaterialCountByType[index] : 0; } std::set MaterialSystem::getMaterialTypes() const { - checkInvariant(!mMaterialsChanged, "Materials have changed. Call update() first."); + FALCOR_CHECK(!mMaterialsChanged, "Materials have changed. Call update() first."); return mMaterialTypes; } bool MaterialSystem::hasMaterialType(MaterialType type) const { - checkInvariant(!mMaterialsChanged, "Materials have changed. Call update() first."); + FALCOR_CHECK(!mMaterialsChanged, "Materials have changed. Call update() first."); return mMaterialTypes.find(type) != mMaterialTypes.end(); } @@ -287,7 +303,7 @@ namespace Falcor const ref& MaterialSystem::getMaterial(const MaterialID materialID) const { - checkArgument(materialID.get() < mMaterials.size(), "MaterialID is out of range."); + FALCOR_CHECK(materialID.get() < mMaterials.size(), "MaterialID is out of range."); return mMaterials[materialID.get()]; } @@ -362,19 +378,19 @@ namespace Falcor RenderContext* pRenderContext = mpDevice->getRenderContext(); TextureAnalyzer analyzer(mpDevice); - auto pResults = Buffer::create(mpDevice, textures.size() * TextureAnalyzer::getResultSize(), ResourceBindFlags::UnorderedAccess); + auto pResults = mpDevice->createBuffer(textures.size() * TextureAnalyzer::getResultSize(), ResourceBindFlags::UnorderedAccess); analyzer.analyze(pRenderContext, textures, pResults); // Copy result to staging buffer for readback. // This is mostly to avoid a full flush and the associated perf warning. // We do not have any other useful GPU work, but unrelated GPU tasks can be in flight. - auto pResultsStaging = Buffer::create(mpDevice, textures.size() * TextureAnalyzer::getResultSize(), ResourceBindFlags::None, Buffer::CpuAccess::Read); + auto pResultsStaging = mpDevice->createBuffer(textures.size() * TextureAnalyzer::getResultSize(), ResourceBindFlags::None, MemoryType::ReadBack); pRenderContext->copyResource(pResultsStaging.get(), pResults.get()); - pRenderContext->flush(false); - mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + pRenderContext->submit(false); + pRenderContext->signal(mpFence.get()); // Wait for results to become available. Then optimize the materials. - mpFence->syncCpu(); + mpFence->wait(); const TextureAnalyzer::Result* results = static_cast(pResultsStaging->map(Buffer::MapType::Read)); Material::TextureOptimizationStats stats = {}; @@ -402,10 +418,8 @@ namespace Falcor Material::UpdateFlags MaterialSystem::update(bool forceUpdate) { - Material::UpdateFlags flags = Material::UpdateFlags::None; - // If materials were added/removed since last update, we update all metadata - // and trigger re-creation of the parameter block. + // and trigger re-creation of the parameter block and update of all materials. if (forceUpdate || mMaterialsChanged) { updateMetadata(); @@ -413,27 +427,46 @@ namespace Falcor mpMaterialsBlock = nullptr; mMaterialsChanged = false; + forceUpdate = true; } + // Update all materials. + // Do either a full update of all materials with deferred texture loading, or an update of just the dynamic materials. + // We track per-material update flags along with the combined update flags across all materials. + // Note that materials can record updates in between calls to update() and/or return flags from their update() calls. + Material::UpdateFlags updateFlags = Material::UpdateFlags::None; mMaterialsUpdateFlags.resize(mMaterials.size()); + std::fill(mMaterialsUpdateFlags.begin(), mMaterialsUpdateFlags.end(), Material::UpdateFlags::None); + + auto updateMaterial = [&](const MaterialID materialID) { + auto& pMaterial = getMaterial(materialID); + if (pMaterial->mpDevice != mpDevice) + FALCOR_THROW("Material '{}' was created with a different device than the MaterialSystem.", pMaterial->getName()); + Material::UpdateFlags flags = pMaterial->update(this); + // Record update flags. + mMaterialsUpdateFlags[materialID.get()] = flags; + updateFlags |= flags; + }; - // Update all materials. if (forceUpdate || mMaterialUpdates != Material::UpdateFlags::None) { - // To improve load time of large assets using the MxLayeredMaterial, - // we defer texture loading and execute it in parallel during endDeferredLoading(). mpTextureManager->beginDeferredLoading(); - for (uint32_t materialID = 0; materialID < (uint32_t)mMaterials.size(); ++materialID) - { - auto& pMaterial = mMaterials[materialID]; - if (pMaterial->mpDevice != mpDevice) - throw RuntimeError("Material '{}' was created with a different device than the MaterialSystem.", pMaterial->getName()); - mMaterialsUpdateFlags[materialID] = pMaterial->update(this); - } + for (size_t materialIdx = 0; materialIdx < mMaterials.size(); ++materialIdx) + updateMaterial(MaterialID{ materialIdx }); mpTextureManager->endDeferredLoading(); } + else + { + for (const auto& materialID : mDynamicMaterialIDs) + updateMaterial(materialID); + } + + // Include updates recorded since last update. + // After this point no more material changes are expected. + updateFlags |= mMaterialUpdates; + mMaterialUpdates = Material::UpdateFlags::None; // Create parameter block if needed. if (!mpMaterialsBlock) @@ -442,21 +475,19 @@ namespace Falcor // Set update flags if parameter block changes. // TODO: We may want to introduce MaterialSystem::UpdateFlags instead of re-using the material flags. - flags |= Material::UpdateFlags::DataChanged | Material::UpdateFlags::ResourcesChanged; + updateFlags |= Material::UpdateFlags::DataChanged | Material::UpdateFlags::ResourcesChanged; - forceUpdate = true; // Trigger full upload of all materials + forceUpdate = true; // Trigger full upload of all materials. } - // Upload all modified materials - if (forceUpdate || mMaterialUpdates != Material::UpdateFlags::None) + // Upload all modified materials. + if (forceUpdate || is_set(updateFlags, Material::UpdateFlags::DataChanged)) { for (uint32_t materialID = 0; materialID < (uint32_t)mMaterials.size(); ++materialID) { - if (forceUpdate || mMaterialsUpdateFlags[materialID] != Material::UpdateFlags::None) + if (forceUpdate || is_set(mMaterialsUpdateFlags[materialID], Material::UpdateFlags::DataChanged)) { uploadMaterial(materialID); - - flags |= mMaterialsUpdateFlags[materialID]; } } } @@ -475,10 +506,10 @@ namespace Falcor } // Update textures. - if (forceUpdate || is_set(flags, Material::UpdateFlags::ResourcesChanged)) + if (forceUpdate || is_set(updateFlags, Material::UpdateFlags::ResourcesChanged)) { FALCOR_ASSERT(!mMaterialsChanged); - mpTextureManager->setShaderData(blockVar[kMaterialTexturesName], mTextureDescCount, + mpTextureManager->bindShaderData(blockVar[kMaterialTexturesName], mTextureDescCount, blockVar["udimIndirection"]); } @@ -509,7 +540,7 @@ namespace Falcor // This is done by iterating over all materials to query their properties. // We de-duplicate the result by material type to store the unique set of shader modules and type conformances. // Note that this means the shader code for all materials of the same type is assumed to be identical. - if (forceUpdate || is_set(flags, Material::UpdateFlags::CodeChanged)) + if (forceUpdate || is_set(updateFlags, Material::UpdateFlags::CodeChanged)) { mShaderModules.clear(); mTypeConformances.clear(); @@ -526,24 +557,30 @@ namespace Falcor } } - mMaterialUpdates = Material::UpdateFlags::None; - - return flags; + FALCOR_CHECK(mMaterialUpdates == Material::UpdateFlags::None, "Unexpected material updates."); + return updateFlags; } void MaterialSystem::updateMetadata() { - mTextureDescCount = 0; - mBufferDescCount = 0; - mTexture3DDescCount = 0; + mTextureDescCount = mReservedTextureDescCount; + mBufferDescCount = mReservedBufferDescCount; + mTexture3DDescCount = mReservedTexture3DDescCount; mMaterialCountByType.resize(getMaterialTypeCount()); std::fill(mMaterialCountByType.begin(), mMaterialCountByType.end(), 0); mMaterialTypes.clear(); mHasSpecGlossStandardMaterial = false; + mDynamicMaterialIDs.clear(); - for (const auto& pMaterial : mMaterials) + for (size_t materialIdx = 0; materialIdx < mMaterials.size(); materialIdx++) { + const auto& pMaterial = mMaterials[materialIdx]; + const MaterialID materialID{ materialIdx }; + + if (pMaterial->isDynamic()) + mDynamicMaterialIDs.push_back(materialID); + // Update descriptor counts. These counts will be reported by getDefines(). // TODO: Remove this when unbounded descriptor arrays are supported (#1321). mTextureDescCount += pMaterial->getMaxTextureCount(); @@ -558,12 +595,12 @@ namespace Falcor if (isSpecGloss(pMaterial)) mHasSpecGlossStandardMaterial = true; } - checkInvariant(mMaterialTypes.find(MaterialType::Unknown) == mMaterialTypes.end(), "Unknown material type found. Make sure all material types are registered."); + FALCOR_CHECK(mMaterialTypes.find(MaterialType::Unknown) == mMaterialTypes.end(), "Unknown material type found. Make sure all material types are registered."); } MaterialSystem::MaterialStats MaterialSystem::getStats() const { - checkInvariant(!mMaterialsChanged, "Materials have changed. Call update() first."); + FALCOR_CHECK(!mMaterialsChanged, "Materials have changed. Call update() first."); MaterialStats s = {}; @@ -587,15 +624,14 @@ namespace Falcor return s; } - DefineList MaterialSystem::getDefines() const + void MaterialSystem::getDefines(DefineList& defines) const { - checkInvariant(!mMaterialsChanged, "Materials have changed. Call update() first."); + FALCOR_CHECK(!mMaterialsChanged, "Materials have changed. Call update() first."); size_t materialInstanceByteSize = 0; for (auto& it : mMaterials) materialInstanceByteSize = std::max(materialInstanceByteSize, it->getMaterialInstanceByteSize()); - DefineList defines; defines.add("MATERIAL_SYSTEM_SAMPLER_DESC_COUNT", std::to_string(kMaxSamplerCount)); defines.add("MATERIAL_SYSTEM_TEXTURE_DESC_COUNT", std::to_string(mTextureDescCount)); defines.add("MATERIAL_SYSTEM_BUFFER_DESC_COUNT", std::to_string(mBufferDescCount)); @@ -613,7 +649,7 @@ namespace Falcor { if (auto it = defines.find(name); it != defines.end()) { - checkInvariant(it->second == value, "Mismatching values '{}' and '{}' for material define '{}'.", name, it->second, value); + FALCOR_CHECK(it->second == value, "Mismatching values '{}' and '{}' for material define '{}'.", name, it->second, value); } else { @@ -621,13 +657,10 @@ namespace Falcor } } } - - return defines; } - Program::TypeConformanceList MaterialSystem::getTypeConformances() const + void MaterialSystem::getTypeConformances(TypeConformanceList& typeConformances) const { - Program::TypeConformanceList typeConformances; for (const auto& it : mTypeConformances) { typeConformances.add(it.second); @@ -636,29 +669,34 @@ namespace Falcor typeConformances.add("IsotropicPhaseFunction", "IPhaseFunction", 1); typeConformances.add("HenyeyGreensteinPhaseFunction", "IPhaseFunction", 2); typeConformances.add("DualHenyeyGreensteinPhaseFunction", "IPhaseFunction", 3); - return typeConformances; } - Program::TypeConformanceList MaterialSystem::getTypeConformances(const MaterialType type) const + TypeConformanceList MaterialSystem::getTypeConformances(const MaterialType type) const { auto it = mTypeConformances.find(type); if (it == mTypeConformances.end()) { - throw RuntimeError(fmt::format("No type conformances for material type '{}'.", to_string(type))); + FALCOR_THROW(fmt::format("No type conformances for material type '{}'.", to_string(type))); } return it->second; } - Program::ShaderModuleList MaterialSystem::getShaderModules() const + ProgramDesc::ShaderModuleList MaterialSystem::getShaderModules() const { - checkInvariant(!mMaterialsChanged, "Materials have changed. Call update() first."); + FALCOR_CHECK(!mMaterialsChanged, "Materials have changed. Call update() first."); return mShaderModules; } - const ref& MaterialSystem::getParameterBlock() const + void MaterialSystem::getShaderModules(ProgramDesc::ShaderModuleList& shaderModuleList) const + { + FALCOR_CHECK(!mMaterialsChanged, "Materials have changed. Call update() first."); + shaderModuleList.insert(shaderModuleList.end(), mShaderModules.begin(), mShaderModules.end()); + } + + void MaterialSystem::bindShaderData(const ShaderVar& var) const { - checkInvariant(mpMaterialsBlock != nullptr && !mMaterialsChanged, "Parameter block is not ready. Call update() first."); - return mpMaterialsBlock; + FALCOR_CHECK(mpMaterialsBlock != nullptr && !mMaterialsChanged, "Parameter block is not ready. Call update() first."); + var = mpMaterialsBlock; } void MaterialSystem::createParameterBlock() @@ -681,7 +719,7 @@ namespace Falcor auto byteSize = reflResType->getStructType()->getByteSize(); if (byteSize != sizeof(MaterialDataBlob)) { - throw RuntimeError("MaterialSystem material data buffer has unexpected struct size"); + FALCOR_THROW("MaterialSystem material data buffer has unexpected struct size"); } auto blockVar = mpMaterialsBlock->getRootVar(); @@ -689,7 +727,7 @@ namespace Falcor // Create materials data buffer. if (!mMaterials.empty() && (!mpMaterialDataBuffer || mpMaterialDataBuffer->getElementCount() < mMaterials.size())) { - mpMaterialDataBuffer = Buffer::createStructured(mpDevice, blockVar[kMaterialDataName], (uint32_t)mMaterials.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpMaterialDataBuffer = mpDevice->createStructuredBuffer(blockVar[kMaterialDataName], (uint32_t)mMaterials.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpMaterialDataBuffer->setName("MaterialSystem::mpMaterialDataBuffer"); } diff --git a/Source/Falcor/Scene/Material/MaterialSystem.h b/Source/Falcor/Scene/Material/MaterialSystem.h index d8a81889f..beff7f2ec 100644 --- a/Source/Falcor/Scene/Material/MaterialSystem.h +++ b/Source/Falcor/Scene/Material/MaterialSystem.h @@ -84,32 +84,51 @@ namespace Falcor /** Get shader defines. These need to be set before binding the material system parameter block. - \return List of shader defines. + Adds defines to an existing list, rather than creating a new list. + \param[in,out] defines List of type defines. */ - DefineList getDefines() const; + void getDefines(DefineList& defines) const; + DefineList getDefines() const + { + DefineList result; + getDefines(result); + return result; + } /** Get type conformances for all material types used. These need to be set on a program before using the material system in shaders that need to create a material of *any* type, such as compute or raygen shaders. - \return List of type conformances. + \param[in,out] conformances List of type conformances. */ - Program::TypeConformanceList getTypeConformances() const; + void getTypeConformances(TypeConformanceList& conformances) const; + TypeConformanceList getTypeConformances() const + { + TypeConformanceList typeConformances; + getTypeConformances(typeConformances); + return typeConformances; + } /** Get type conformances for a given material type in use. \param[in] type Material type. \return List of type conformances. */ - Program::TypeConformanceList getTypeConformances(const MaterialType type) const; + TypeConformanceList getTypeConformances(const MaterialType type) const; + + /** Get shader modules for all materials in use. + The shader modules must be added to any program using the material system. + \param[in,out] shaderModuleList List of shader modules. + */ + void getShaderModules(ProgramDesc::ShaderModuleList& shaderModuleList) const; /** Get shader modules for all materials in use. The shader modules must be added to any program using the material system. \return List of shader modules. */ - Program::ShaderModuleList getShaderModules() const; + ProgramDesc::ShaderModuleList getShaderModules() const; - /** Get the parameter block with all material resources. + /** Bind the material system to a shader var. */ - const ref& getParameterBlock() const; + void bindShaderData(const ShaderVar& var) const; /** Set a default texture sampler to use for all materials. */ @@ -164,9 +183,10 @@ namespace Falcor MaterialID addMaterial(const ref& pMaterial); /** Replace a material. - \param pMaterial The material to replace. + \param materialID The ID of the material to replace. \param pReplacement The material to replace it with. */ + void replaceMaterial(const MaterialID materialID, const ref& pReplacement); void replaceMaterial(const ref& pMaterial, const ref& pReplacement); /** Get a list of all materials. @@ -237,17 +257,21 @@ namespace Falcor std::vector> mMaterials; ///< List of all materials. std::vector mMaterialsUpdateFlags; ///< List of all material update flags, after the update() calls std::unique_ptr mpTextureManager; ///< Texture manager holding all material textures. - Program::ShaderModuleList mShaderModules; ///< Shader modules for all materials in use. - std::map mTypeConformances; ///< Type conformances for each material type in use. + ProgramDesc::ShaderModuleList mShaderModules; ///< Shader modules for all materials in use. + std::map mTypeConformances; ///< Type conformances for each material type in use. // Metadata size_t mTextureDescCount = 0; ///< Number of texture descriptors in GPU descriptor array. This variable is for book-keeping until unbounded descriptor arrays are supported (see #1321). size_t mBufferDescCount = 0; ///< Number of buffer descriptors in GPU descriptor array. This variable is for book-keeping until unbounded descriptor arrays are supported (see #1321). size_t mTexture3DDescCount = 0; ///< Number of 3D texture descriptors in GPU descriptor array. This variable is for book-keeping until unbounded descriptor arrays are supported (see #1321). + size_t mReservedTextureDescCount = 0; ///< Number of reserved texture descriptors in GPU descriptor array. This variable is for book-keeping until unbounded descriptor arrays are supported (see #1321). + size_t mReservedBufferDescCount = 0; ///< Number of reserved buffer descriptors in GPU descriptor array. This variable is for book-keeping until unbounded descriptor arrays are supported (see #1321). + size_t mReservedTexture3DDescCount = 0; ///< Number of reserved 3D texture descriptors in GPU descriptor array. This variable is for book-keeping until unbounded descriptor arrays are supported (see #1321). std::vector mMaterialCountByType; ///< Number of materials of each type, indexed by MaterialType. std::set mMaterialTypes; ///< Set of all material types used. bool mHasSpecGlossStandardMaterial = false; ///< True if standard materials using the SpecGloss shading model exist. + std::vector mDynamicMaterialIDs; ///< Material IDs for all dynamic materials. bool mSamplersChanged = false; ///< Flag indicating if samplers were added/removed since last update. bool mBuffersChanged = false; ///< Flag indicating if buffers were added/removed since last update. @@ -257,7 +281,7 @@ namespace Falcor Material::UpdateFlags mMaterialUpdates = Material::UpdateFlags::None; ///< Material updates across all materials since last update. // GPU resources - ref mpFence; + ref mpFence; ref mpMaterialsBlock; ///< Parameter block for binding all material resources. ref mpMaterialDataBuffer; ///< GPU buffer holding all material data. ref mpDefaultTextureSampler; ///< Default texture sampler to use for all materials. diff --git a/Source/Falcor/Scene/Material/MaterialSystem.slang b/Source/Falcor/Scene/Material/MaterialSystem.slang index e01c91969..28d7f32b5 100644 --- a/Source/Falcor/Scene/Material/MaterialSystem.slang +++ b/Source/Falcor/Scene/Material/MaterialSystem.slang @@ -27,6 +27,7 @@ **************************************************************************/ import Scene.Material.TextureHandle; import Scene.Material.TextureSampler; +import Scene.Material.AlphaTest; import Rendering.Volumes.PhaseFunction; import Utils.SlangUtils; __exported import Scene.SceneTypes; @@ -353,6 +354,39 @@ struct MaterialSystem displacementData.samplerStateMin = materialSamplers[displacementMinSamplerID]; displacementData.samplerStateMax = materialSamplers[displacementMaxSamplerID]; } + + /** Perform alpha test at a hit point and return the result. + \param[in] v Interpolated vertex data. + \param[in] materialID Material ID at the hit. + \param[in] lod Method for computing texture level-of-detail, must implement the `ITextureSampler` interface. + \return True if hit should be ignored/discarded. + */ + bool alphaTest(const VertexData v, const uint materialID, L lod) + { + // Load the material header first and early out if alpha test not supported. + const MaterialHeader header = materialData[materialID].header; + + if (header.getAlphaMode() != AlphaMode::Mask) return false; + + SamplerState s = getTextureSampler(header.getDefaultTextureSamplerID()); + float alpha = sampleTexture(header.getAlphaTextureHandle(), s, v.texC, float4(1.f), lod).a; + float alphaThreshold = header.getAlphaThreshold(); + + return evalAlphaTest(alpha, alphaThreshold, v.posW); + } + + /** Perform alpha test at a hit point and return the result. + This version samples alpha at a fixed level of detail. + \param[in] v Interpolated vertex data. + \param[in] materialID Material ID at the hit. + \param[in] lod Fixed texture level of detail. + \return True if hit should be ignored/discarded. + */ + bool alphaTest(const VertexData v, const uint materialID, const float lod) + { + ExplicitLodTextureSampler explicitLOD = { lod }; + return alphaTest(v, materialID, explicitLOD); + } }; #ifdef MATERIAL_SYSTEM_PARAMETER_BLOCK diff --git a/Source/Falcor/Scene/Material/MaterialTextureLoader.h b/Source/Falcor/Scene/Material/MaterialTextureLoader.h index b976ed05e..4b7a5194d 100644 --- a/Source/Falcor/Scene/Material/MaterialTextureLoader.h +++ b/Source/Falcor/Scene/Material/MaterialTextureLoader.h @@ -62,7 +62,7 @@ namespace Falcor { ref pMaterial; Material::TextureSlot textureSlot; - TextureManager::TextureHandle handle; + TextureManager::CpuTextureHandle handle; }; bool mUseSrgb; diff --git a/Source/Falcor/Scene/Material/MaterialTypeRegistry.cpp b/Source/Falcor/Scene/Material/MaterialTypeRegistry.cpp index 74865b749..9b8eca01f 100644 --- a/Source/Falcor/Scene/Material/MaterialTypeRegistry.cpp +++ b/Source/Falcor/Scene/Material/MaterialTypeRegistry.cpp @@ -28,6 +28,14 @@ #include "MaterialTypeRegistry.h" #include "MaterialData.slang" +// Include parameter layouts. +#include "Scene/Material/StandardMaterial.h" +#include "Scene/Material/StandardMaterialParamLayout.slang" +#include "Scene/Material/PBRT/PBRTDiffuseMaterial.h" +#include "Scene/Material/PBRT/PBRTDiffuseMaterialParamLayout.slang" +#include "Scene/Material/PBRT/PBRTConductorMaterial.h" +#include "Scene/Material/PBRT/PBRTConductorMaterialParamLayout.slang" + #include #include @@ -59,7 +67,7 @@ class MaterialTypeRegistry FALCOR_ASSERT_LT(mNextMaterialTypeID, 1u << MaterialHeader::kMaterialTypeBits); if (mNextMaterialTypeID >= (1u << MaterialHeader::kMaterialTypeBits)) { - throw ArgumentError("Registered material {} would receive MaterialType {}, exceeding the maximum limit {} (given by MaterialHeader::kMaterialTypeBits)", + FALCOR_THROW("Registered material {} would receive MaterialType {}, exceeding the maximum limit {} (given by MaterialHeader::kMaterialTypeBits)", typeName, mNextMaterialTypeID, (1u << MaterialHeader::kMaterialTypeBits)); } @@ -76,7 +84,7 @@ class MaterialTypeRegistry auto it = mMaterialType2Name.find(type); if (it != mMaterialType2Name.end()) return it->second; - throw ArgumentError(fmt::format("Invalid material type: {}", int(type))); + FALCOR_THROW("Invalid material type: {}", int(type)); } size_t getTypeCount() const @@ -84,6 +92,21 @@ class MaterialTypeRegistry std::lock_guard l(mMaterialTypeNameMutex); return mNextMaterialTypeID; } + + MaterialParamLayout getMaterialParamLayout(MaterialType type) const + { + switch (type) + { + case MaterialType::Standard: + return StandardMaterialParamLayout::layout(); + case MaterialType::PBRTDiffuse: + return PBRTDiffuseMaterialParamLayout::layout(); + case MaterialType::PBRTConductor: + return PBRTConductorMaterialParamLayout::layout(); + } + return {}; + } + private: MaterialTypeRegistry() { @@ -108,7 +131,7 @@ class MaterialTypeRegistry { mMaterialName2Type[name] = type; } - checkInvariant(mMaterialType2Name.size() == mMaterialName2Type.size(), "Material type names must be unique."); + FALCOR_CHECK(mMaterialType2Name.size() == mMaterialName2Type.size(), "Material type names must be unique."); } mutable std::mutex mMaterialTypeNameMutex; ///< Mutex to registering new material types @@ -134,4 +157,9 @@ size_t getMaterialTypeCount() return MaterialTypeRegistry::get().getTypeCount(); } +MaterialParamLayout getMaterialParamLayout(MaterialType type) +{ + return MaterialTypeRegistry::get().getMaterialParamLayout(type); } + +} // namespace Falcor diff --git a/Source/Falcor/Scene/Material/MaterialTypeRegistry.h b/Source/Falcor/Scene/Material/MaterialTypeRegistry.h index b1cac5071..ec8daae4d 100644 --- a/Source/Falcor/Scene/Material/MaterialTypeRegistry.h +++ b/Source/Falcor/Scene/Material/MaterialTypeRegistry.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -28,6 +28,7 @@ #pragma once #include "Core/Macros.h" #include "MaterialTypes.slang" +#include "MaterialParamLayout.h" #include namespace Falcor @@ -53,6 +54,10 @@ FALCOR_API std::string to_string(MaterialType type); \return Total number of registered material types. */ FALCOR_API size_t getMaterialTypeCount(); + +/** Return the material parameter layout of serialized material parameters (for differentiable materials). +*/ +FALCOR_API MaterialParamLayout getMaterialParamLayout(MaterialType type); } template<> diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.cpp b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.cpp index 6031e2005..f4f11c15d 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.cpp +++ b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.cpp @@ -46,12 +46,12 @@ namespace Falcor mTextureSlotInfo[(uint32_t)TextureSlot::Normal] = { "normal", TextureChannelFlags::RGB, false }; } - Program::ShaderModuleList PBRTCoatedConductorMaterial::getShaderModules() const + ProgramDesc::ShaderModuleList PBRTCoatedConductorMaterial::getShaderModules() const { - return { Program::ShaderModule(kShaderFile) }; + return { ProgramDesc::ShaderModule::fromFile(kShaderFile) }; } - Program::TypeConformanceList PBRTCoatedConductorMaterial::getTypeConformances() const + TypeConformanceList PBRTCoatedConductorMaterial::getTypeConformances() const { return { {{"PBRTCoatedConductorMaterial", "IMaterial"}, (uint32_t)MaterialType::PBRTCoatedConductor} }; } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.h index 2a7f48792..28de9f920 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedConductorMaterial.h @@ -63,8 +63,8 @@ namespace Falcor PBRTCoatedConductorMaterial(ref pDevice, const std::string& name); - Program::ShaderModuleList getShaderModules() const override; - Program::TypeConformanceList getTypeConformances() const override; + ProgramDesc::ShaderModuleList getShaderModules() const override; + TypeConformanceList getTypeConformances() const override; /** Set the roughness. */ diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.cpp b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.cpp index 8e2635b52..a8188ff4e 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.cpp +++ b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.cpp @@ -45,12 +45,12 @@ namespace Falcor mTextureSlotInfo[(uint32_t)TextureSlot::Normal] = { "normal", TextureChannelFlags::RGB, false }; } - Program::ShaderModuleList PBRTCoatedDiffuseMaterial::getShaderModules() const + ProgramDesc::ShaderModuleList PBRTCoatedDiffuseMaterial::getShaderModules() const { - return { Program::ShaderModule(kShaderFile) }; + return { ProgramDesc::ShaderModule::fromFile(kShaderFile) }; } - Program::TypeConformanceList PBRTCoatedDiffuseMaterial::getTypeConformances() const + TypeConformanceList PBRTCoatedDiffuseMaterial::getTypeConformances() const { return { {{"PBRTCoatedDiffuseMaterial", "IMaterial"}, (uint32_t)MaterialType::PBRTCoatedDiffuse} }; } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h index 4995e804d..83565736b 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTCoatedDiffuseMaterial.h @@ -59,8 +59,8 @@ namespace Falcor PBRTCoatedDiffuseMaterial(ref pDevice, const std::string& name); - Program::ShaderModuleList getShaderModules() const override; - Program::TypeConformanceList getTypeConformances() const override; + ProgramDesc::ShaderModuleList getShaderModules() const override; + TypeConformanceList getTypeConformances() const override; /** Set the roughness. */ diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.cpp b/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.cpp index e3f2f56d5..b51725990 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.cpp +++ b/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "PBRTConductorMaterial.h" +#include "PBRTConductorMaterialParamLayout.slang" #include "Utils/Scripting/ScriptBindings.h" #include "GlobalState.h" @@ -46,12 +47,12 @@ namespace Falcor mTextureSlotInfo[(uint32_t)TextureSlot::Normal] = { "normal", TextureChannelFlags::RGB, false }; } - Program::ShaderModuleList PBRTConductorMaterial::getShaderModules() const + ProgramDesc::ShaderModuleList PBRTConductorMaterial::getShaderModules() const { - return { Program::ShaderModule(kShaderFile) }; + return { ProgramDesc::ShaderModule::fromFile(kShaderFile) }; } - Program::TypeConformanceList PBRTConductorMaterial::getTypeConformances() const + TypeConformanceList PBRTConductorMaterial::getTypeConformances() const { return { {{"PBRTConductorMaterial", "IMaterial"}, (uint32_t)MaterialType::PBRTConductor} }; } @@ -73,6 +74,21 @@ namespace Falcor } } + const MaterialParamLayout& PBRTConductorMaterial::getParamLayout() const + { + return PBRTConductorMaterialParamLayout::layout(); + } + + SerializedMaterialParams PBRTConductorMaterial::serializeParams() const + { + return PBRTConductorMaterialParamLayout::serialize(this); + } + + void PBRTConductorMaterial::deserializeParams(const SerializedMaterialParams& params) + { + PBRTConductorMaterialParamLayout::deserialize(this, params); + } + FALCOR_SCRIPT_BINDING(PBRTConductorMaterial) { using namespace pybind11::literals; diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.h index a080ccd8e..a78b4fc58 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterial.h @@ -63,8 +63,8 @@ namespace Falcor PBRTConductorMaterial(ref pDevice, const std::string& name); - Program::ShaderModuleList getShaderModules() const override; - Program::TypeConformanceList getTypeConformances() const override; + ProgramDesc::ShaderModuleList getShaderModules() const override; + TypeConformanceList getTypeConformances() const override; /** Set the roughness. */ @@ -74,6 +74,10 @@ namespace Falcor */ float2 getRoughness() const { return float2(mData.specular[0], mData.specular[1]); } + const MaterialParamLayout& getParamLayout() const override; + SerializedMaterialParams serializeParams() const override; + void deserializeParams(const SerializedMaterialParams& params) override; + protected: void renderSpecularUI(Gui::Widgets& widget) override; }; diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterialParamLayout.slang b/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterialParamLayout.slang new file mode 100644 index 000000000..3d71ec2a8 --- /dev/null +++ b/Source/Falcor/Scene/Material/PBRT/PBRTConductorMaterialParamLayout.slang @@ -0,0 +1,43 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "../MaterialParamLayout.slangh" + +BEGIN_NAMESPACE_FALCOR + +static constexpr float2 kPBRTConductorMinRoughness = float2(0.05f); + +#define MATERIAL_PARAMS(PARAM) \ + PARAM(float3, 0, baseColor, getBaseColor3, setBaseColor3, "eta") \ + PARAM(float3, 3, transmissionColor, getTransmissionColor, setTransmissionColor, "k") \ + PARAM(float2, 6, roughness, getRoughness, setRoughness, "roughness", kPBRTConductorMinRoughness) + +DEFINE_MATERIAL_PARAM_LAYOUT(PBRTConductorMaterial, MATERIAL_PARAMS) + +#undef MATERIAL_PARAMS + +END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.cpp b/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.cpp index f66cc290a..ac545c6eb 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.cpp +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.cpp @@ -44,12 +44,12 @@ namespace Falcor mTextureSlotInfo[(uint32_t)TextureSlot::Normal] = { "normal", TextureChannelFlags::RGB, false }; } - Program::ShaderModuleList PBRTDielectricMaterial::getShaderModules() const + ProgramDesc::ShaderModuleList PBRTDielectricMaterial::getShaderModules() const { - return { Program::ShaderModule(kShaderFile) }; + return { ProgramDesc::ShaderModule::fromFile(kShaderFile) }; } - Program::TypeConformanceList PBRTDielectricMaterial::getTypeConformances() const + TypeConformanceList PBRTDielectricMaterial::getTypeConformances() const { return { {{"PBRTDielectricMaterial", "IMaterial"}, (uint32_t)MaterialType::PBRTDielectric} }; } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.h index ff876a028..0e6a40a1d 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDielectricMaterial.h @@ -57,8 +57,8 @@ namespace Falcor PBRTDielectricMaterial(ref pDevice, const std::string& name); - Program::ShaderModuleList getShaderModules() const override; - Program::TypeConformanceList getTypeConformances() const override; + ProgramDesc::ShaderModuleList getShaderModules() const override; + TypeConformanceList getTypeConformances() const override; /** Set the roughness. */ diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.cpp b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.cpp index 195cec432..93cbe1929 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.cpp +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "PBRTDiffuseMaterial.h" +#include "PBRTDiffuseMaterialParamLayout.slang" #include "Utils/Scripting/ScriptBindings.h" #include "GlobalState.h" @@ -44,16 +45,31 @@ namespace Falcor mTextureSlotInfo[(uint32_t)TextureSlot::Normal] = { "normal", TextureChannelFlags::RGB, false }; } - Program::ShaderModuleList PBRTDiffuseMaterial::getShaderModules() const + ProgramDesc::ShaderModuleList PBRTDiffuseMaterial::getShaderModules() const { - return { Program::ShaderModule(kShaderFile) }; + return { ProgramDesc::ShaderModule::fromFile(kShaderFile) }; } - Program::TypeConformanceList PBRTDiffuseMaterial::getTypeConformances() const + TypeConformanceList PBRTDiffuseMaterial::getTypeConformances() const { return { {{"PBRTDiffuseMaterial", "IMaterial"}, (uint32_t)MaterialType::PBRTDiffuse} }; } + const MaterialParamLayout& PBRTDiffuseMaterial::getParamLayout() const + { + return PBRTDiffuseMaterialParamLayout::layout(); + } + + SerializedMaterialParams PBRTDiffuseMaterial::serializeParams() const + { + return PBRTDiffuseMaterialParamLayout::serialize(this); + } + + void PBRTDiffuseMaterial::deserializeParams(const SerializedMaterialParams& params) + { + PBRTDiffuseMaterialParamLayout::deserialize(this, params); + } + FALCOR_SCRIPT_BINDING(PBRTDiffuseMaterial) { using namespace pybind11::literals; @@ -66,5 +82,13 @@ namespace Falcor return PBRTDiffuseMaterial::create(accessActivePythonSceneBuilder().getDevice(), name); }; material.def(pybind11::init(create), "name"_a = ""); // PYTHONDEPRECATED + material.def(pybind11::init( + [](ref device, const std::string& name) + { + return PBRTDiffuseMaterial::create(device, name); + }), + "device"_a, + "name"_a = "" + ); // PYTHONDEPRECATED } } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.h index fc5431ed8..842573d04 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterial.h @@ -53,7 +53,11 @@ namespace Falcor PBRTDiffuseMaterial(ref pDevice, const std::string& name); - Program::ShaderModuleList getShaderModules() const override; - Program::TypeConformanceList getTypeConformances() const override; + ProgramDesc::ShaderModuleList getShaderModules() const override; + TypeConformanceList getTypeConformances() const override; + + const MaterialParamLayout& getParamLayout() const override; + SerializedMaterialParams serializeParams() const override; + void deserializeParams(const SerializedMaterialParams& params) override; }; } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterialParamLayout.slang b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterialParamLayout.slang new file mode 100644 index 000000000..82002c88a --- /dev/null +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseMaterialParamLayout.slang @@ -0,0 +1,38 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "../MaterialParamLayout.slangh" + +BEGIN_NAMESPACE_FALCOR + +#define MATERIAL_PARAMS(PARAM) PARAM(float3, 0, baseColor, getBaseColor3, setBaseColor3, "diffuse") + +DEFINE_MATERIAL_PARAM_LAYOUT(PBRTDiffuseMaterial, MATERIAL_PARAMS) + +#undef MATERIAL_PARAMS + +END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.cpp b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.cpp index ca92fce93..aac608c2b 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.cpp +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.cpp @@ -45,12 +45,12 @@ namespace Falcor mTextureSlotInfo[(uint32_t)TextureSlot::Normal] = { "normal", TextureChannelFlags::RGB, false }; } - Program::ShaderModuleList PBRTDiffuseTransmissionMaterial::getShaderModules() const + ProgramDesc::ShaderModuleList PBRTDiffuseTransmissionMaterial::getShaderModules() const { - return { Program::ShaderModule(kShaderFile) }; + return { ProgramDesc::ShaderModule::fromFile(kShaderFile) }; } - Program::TypeConformanceList PBRTDiffuseTransmissionMaterial::getTypeConformances() const + TypeConformanceList PBRTDiffuseTransmissionMaterial::getTypeConformances() const { return { {{"PBRTDiffuseTransmissionMaterial", "IMaterial"}, (uint32_t)MaterialType::PBRTDiffuseTransmission} }; } diff --git a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h index 970390e27..19b007eae 100644 --- a/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h +++ b/Source/Falcor/Scene/Material/PBRT/PBRTDiffuseTransmissionMaterial.h @@ -57,7 +57,7 @@ namespace Falcor PBRTDiffuseTransmissionMaterial(ref pDevice, const std::string& name); - Program::ShaderModuleList getShaderModules() const override; - Program::TypeConformanceList getTypeConformances() const override; + ProgramDesc::ShaderModuleList getShaderModules() const override; + TypeConformanceList getTypeConformances() const override; }; } diff --git a/Source/Falcor/Scene/Material/RGLCommon.cpp b/Source/Falcor/Scene/Material/RGLCommon.cpp index 3aa53c5ac..c480e1bcb 100644 --- a/Source/Falcor/Scene/Material/RGLCommon.cpp +++ b/Source/Falcor/Scene/Material/RGLCommon.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "RGLCommon.h" -#include "Core/Assert.h" +#include "Core/Error.h" namespace Falcor { diff --git a/Source/Falcor/Scene/Material/RGLFile.cpp b/Source/Falcor/Scene/Material/RGLFile.cpp index d06cd965a..378327c8c 100644 --- a/Source/Falcor/Scene/Material/RGLFile.cpp +++ b/Source/Falcor/Scene/Material/RGLFile.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "RGLFile.h" -#include "Core/Errors.h" +#include "Core/Error.h" namespace Falcor { @@ -61,7 +61,7 @@ namespace Falcor field.numElems *= shape[i]; } size_t N = fieldSize(type); - if (N == 0) throw RuntimeError("RGLFile::fieldSize: Invalid field type"); + if (N == 0) FALCOR_THROW("RGLFile::fieldSize: Invalid field type"); field.data.reset(new uint8_t[N * field.numElems]); std::memcpy(field.data.get(), data, N * field.numElems); @@ -83,28 +83,28 @@ namespace Falcor if (!thetaI || thetaI->type != Float32 || thetaI->dim != 1) { - throw RuntimeError("theta_i field missing or invalid"); + FALCOR_THROW("theta_i field missing or invalid"); } if (!phiI || phiI->type != Float32 || phiI->dim != 1) { - throw RuntimeError("phi_i field missing or invalid"); + FALCOR_THROW("phi_i field missing or invalid"); } if (!sigma || sigma->type != Float32 || sigma->dim != 2) { - throw RuntimeError("sigma field missing or invalid"); + FALCOR_THROW("sigma field missing or invalid"); } if (!ndf || ndf->type != Float32 || ndf->dim != 2) { - throw RuntimeError("ndf field missing or invalid"); + FALCOR_THROW("ndf field missing or invalid"); } if (!vndf || vndf->type != Float32 || vndf->dim != 4 || vndf->shape[0] != phiI->shape[0] || vndf->shape[1] != thetaI->shape[0]) { - throw RuntimeError("vndf field missing or invalid"); + FALCOR_THROW("vndf field missing or invalid"); } if (!luminance || luminance->type != Float32 || luminance->dim != 4 @@ -112,19 +112,19 @@ namespace Falcor || luminance->shape[1] != thetaI->shape[0] || luminance->shape[2] != luminance->shape[3]) { - throw RuntimeError("luminance field missing or invalid"); + FALCOR_THROW("luminance field missing or invalid"); } if (!rgb || rgb->type != Float32 || rgb->dim != 5 || rgb->shape[0] != phiI->shape[0] || rgb->shape[1] != thetaI->shape[0] || rgb->shape[2] != 3 || rgb->shape[3] != luminance->shape[2] || rgb->shape[4] != luminance->shape[3]) { - throw RuntimeError("rgb field missing or invalid"); + FALCOR_THROW("rgb field missing or invalid"); } if (!description || description->type != UInt8) { - throw RuntimeError("Description field missing or invalid"); + FALCOR_THROW("Description field missing or invalid"); } bool isotropic = phiI->shape[0] <= 2; @@ -151,11 +151,11 @@ namespace Falcor if (strcmp(reinterpret_cast(header), "tensor_file")) { - throw RuntimeError("Invalid file header"); + FALCOR_THROW("Invalid file header"); } if (version[0] != 1 || version[1] != 0) { - throw RuntimeError("Unsupported file version"); + FALCOR_THROW("Unsupported file version"); } for (uint32_t i = 0; i < fieldCount; ++i) @@ -182,7 +182,7 @@ namespace Falcor field.shape.reset(new uint64_t[fieldDim]); readBytes(field.shape.get(), 8 * fieldDim); - if (!in.good()) throw RuntimeError("Error parsing RGL field: File truncated"); + if (!in.good()) FALCOR_THROW("Error parsing RGL field: File truncated"); size_t elemSize = fieldSize(FieldType(fieldType)); if (elemSize == 0) diff --git a/Source/Falcor/Scene/Material/RGLFile.h b/Source/Falcor/Scene/Material/RGLFile.h index d500e86f1..6f1d9f77b 100644 --- a/Source/Falcor/Scene/Material/RGLFile.h +++ b/Source/Falcor/Scene/Material/RGLFile.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -75,7 +75,7 @@ namespace Falcor RGLFile() = default; - /** Loads RGL measured BRDF file and validates contents. Throws RuntimeError on failure. + /** Loads RGL measured BRDF file and validates contents. Throws Falcor::Exception on failure. */ RGLFile(std::ifstream& in); @@ -95,7 +95,7 @@ namespace Falcor /** Make sure all required fields are present and have correct shape and dimensions, then populates mMeasurement field if all fields are correct. - Throws RuntimeError on validation error. + Throws Falcor::Exception on validation error. */ void validate(); diff --git a/Source/Falcor/Scene/Material/RGLMaterial.cpp b/Source/Falcor/Scene/Material/RGLMaterial.cpp index b1948706a..b11de2e76 100644 --- a/Source/Falcor/Scene/Material/RGLMaterial.cpp +++ b/Source/Falcor/Scene/Material/RGLMaterial.cpp @@ -55,15 +55,15 @@ namespace Falcor { if (!loadBRDF(path)) { - throw RuntimeError("RGLMaterial() - Failed to load BRDF from '{}'.", path); + FALCOR_THROW("RGLMaterial() - Failed to load BRDF from '{}'.", path); } // Create resources for albedo lookup table. Sampler::Desc desc; - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Point, Sampler::Filter::Point); - desc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); + desc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Point, TextureFilteringMode::Point); + desc.setAddressingMode(TextureAddressingMode::Clamp, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); desc.setMaxAnisotropy(1); - mpSampler = Sampler::create(mpDevice, desc); + mpSampler = mpDevice->createSampler(desc); prepareAlbedoLUT(mpDevice->getRenderContext()); } @@ -72,7 +72,7 @@ namespace Falcor { widget.text("RGL BRDF " + mBRDFName); widget.text(mBRDFDescription); - widget.tooltip("Full path the BRDF was loaded from:\n" + mFilePath.string(), true); + widget.tooltip("Full path the BRDF was loaded from:\n" + mPath.string(), true); return false; } @@ -128,31 +128,24 @@ namespace Falcor if (!other) return false; if (!isBaseEqual(*other)) return false; - if (mFilePath != other->mFilePath) return false; + if (mPath != other->mPath) return false; return true; } - Program::ShaderModuleList RGLMaterial::getShaderModules() const + ProgramDesc::ShaderModuleList RGLMaterial::getShaderModules() const { - return { Program::ShaderModule(kShaderFile) }; + return { ProgramDesc::ShaderModule::fromFile(kShaderFile) }; } - Program::TypeConformanceList RGLMaterial::getTypeConformances() const + TypeConformanceList RGLMaterial::getTypeConformances() const { return { {{"RGLMaterial", "IMaterial"}, (uint32_t)MaterialType::RGL} }; } bool RGLMaterial::loadBRDF(const std::filesystem::path& path) { - std::filesystem::path fullPath; - if (!findFileInDataDirectories(path, fullPath)) - { - logWarning("RGLMaterial::loadBRDF() - Can't find file '{}'.", path); - return false; - } - - std::ifstream ifs(fullPath, std::ios_base::in | std::ios_base::binary); + std::ifstream ifs(path, std::ios_base::in | std::ios_base::binary); if (!ifs.good()) { logWarning("RGLMaterial::loadBRDF() - Failed to open file '{}'.", path); @@ -193,8 +186,8 @@ namespace Falcor return false; } - mFilePath = fullPath; - mBRDFName = std::filesystem::path(fullPath).stem().string(); + mPath = path; + mBRDFName = std::filesystem::path(path).stem().string(); mBRDFDescription = file->data().description; mData.phiSize = uint(phi->shape[0]); @@ -212,18 +205,18 @@ namespace Falcor SamplableDistribution4D vndfDist(reinterpret_cast(vndf->data.get()), vndfSize); SamplableDistribution4D lumiDist(reinterpret_cast(lumi->data.get()), lumiSize); - mpVNDFMarginalBuf = Buffer::create(mpDevice, prod3(vndfSize) * 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, vndfDist.getMarginal()); - mpLumiMarginalBuf = Buffer::create(mpDevice, prod3(lumiSize) * 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, lumiDist.getMarginal()); - mpVNDFConditionalBuf = Buffer::create(mpDevice, prod4(vndfSize) * 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, vndfDist.getConditional()); - mpLumiConditionalBuf = Buffer::create(mpDevice, prod4(lumiSize) * 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, lumiDist.getConditional()); + mpVNDFMarginalBuf = mpDevice->createBuffer(prod3(vndfSize) * 4, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, vndfDist.getMarginal()); + mpLumiMarginalBuf = mpDevice->createBuffer(prod3(lumiSize) * 4, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, lumiDist.getMarginal()); + mpVNDFConditionalBuf = mpDevice->createBuffer(prod4(vndfSize) * 4, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, vndfDist.getConditional()); + mpLumiConditionalBuf = mpDevice->createBuffer(prod4(lumiSize) * 4, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, lumiDist.getConditional()); - mpThetaBuf = Buffer::create(mpDevice, theta->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, theta->data.get()); - mpPhiBuf = Buffer::create(mpDevice, phi ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, phi ->data.get()); - mpSigmaBuf = Buffer::create(mpDevice, sigma->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, sigma->data.get()); - mpNDFBuf = Buffer::create(mpDevice, ndf ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, ndf ->data.get()); - mpVNDFBuf = Buffer::create(mpDevice, vndf ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, vndfDist.getPDF()); - mpLumiBuf = Buffer::create(mpDevice, lumi ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, lumiDist.getPDF()); - mpRGBBuf = Buffer::create(mpDevice, rgb ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, rgb ->data.get()); + mpThetaBuf = mpDevice->createBuffer(theta->numElems * sizeof(float), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, theta->data.get()); + mpPhiBuf = mpDevice->createBuffer(phi ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, phi ->data.get()); + mpSigmaBuf = mpDevice->createBuffer(sigma->numElems * sizeof(float), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, sigma->data.get()); + mpNDFBuf = mpDevice->createBuffer(ndf ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, ndf ->data.get()); + mpVNDFBuf = mpDevice->createBuffer(vndf ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, vndfDist.getPDF()); + mpLumiBuf = mpDevice->createBuffer(lumi ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, lumiDist.getPDF()); + mpRGBBuf = mpDevice->createBuffer(rgb ->numElems * sizeof(float), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, rgb ->data.get()); markUpdates(Material::UpdateFlags::ResourcesChanged); @@ -234,7 +227,7 @@ namespace Falcor void RGLMaterial::prepareAlbedoLUT(RenderContext* pRenderContext) // TODO { - const auto texPath = mFilePath.replace_extension("dds"); + const auto texPath = mPath.replace_extension("dds"); // Try loading albedo lookup table. if (std::filesystem::is_regular_file(texPath)) @@ -301,7 +294,7 @@ namespace Falcor for (uint32_t i = 0; i < kAlbedoLUTSize; i++) initData[i] = float4(albedos[i], 1.f); // Create albedo LUT texture. - mpAlbedoLUT = Texture::create2D(mpDevice, kAlbedoLUTSize, 1, kAlbedoLUTFormat, 1, 1, initData.data(), ResourceBindFlags::ShaderResource); + mpAlbedoLUT = mpDevice->createTexture2D(kAlbedoLUTSize, 1, kAlbedoLUTFormat, 1, 1, initData.data(), ResourceBindFlags::ShaderResource); } FALCOR_SCRIPT_BINDING(RGLMaterial) @@ -313,7 +306,7 @@ namespace Falcor pybind11::class_> material(m, "RGLMaterial"); auto create = [] (const std::string& name, const std::filesystem::path& path) { - return RGLMaterial::create(accessActivePythonSceneBuilder().getDevice(), name, path); + return RGLMaterial::create(accessActivePythonSceneBuilder().getDevice(), name, getActiveAssetResolver().resolvePath(path)); }; material.def(pybind11::init(create), "name"_a, "path"_a); // PYTHONDEPRECATED material.def(kLoadFile.c_str(), &RGLMaterial::loadBRDF, "path"_a); diff --git a/Source/Falcor/Scene/Material/RGLMaterial.h b/Source/Falcor/Scene/Material/RGLMaterial.h index 655b2ef7b..927fd8868 100644 --- a/Source/Falcor/Scene/Material/RGLMaterial.h +++ b/Source/Falcor/Scene/Material/RGLMaterial.h @@ -51,8 +51,8 @@ namespace Falcor Material::UpdateFlags update(MaterialSystem* pOwner) override; bool isEqual(const ref& pOther) const override; MaterialDataBlob getDataBlob() const override { return prepareDataBlob(mData); } - Program::ShaderModuleList getShaderModules() const override; - Program::TypeConformanceList getTypeConformances() const override; + ProgramDesc::ShaderModuleList getShaderModules() const override; + TypeConformanceList getTypeConformances() const override; virtual size_t getMaxBufferCount() const override { return 12; } @@ -63,7 +63,7 @@ namespace Falcor void prepareAlbedoLUT(RenderContext* pRenderContext); void computeAlbedoLUT(RenderContext* pRenderContext); - std::filesystem::path mFilePath; ///< Full path to the BRDF loaded. + std::filesystem::path mPath; ///< Full path to the BRDF loaded. std::string mBRDFName; ///< This is the file basename without extension. std::string mBRDFDescription; ///< Description of the BRDF given in the BRDF file. diff --git a/Source/Falcor/Scene/Material/RGLMaterialData.slang b/Source/Falcor/Scene/Material/RGLMaterialData.slang index ce99fda51..a94e40841 100644 --- a/Source/Falcor/Scene/Material/RGLMaterialData.slang +++ b/Source/Falcor/Scene/Material/RGLMaterialData.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Core/Program/GraphicsProgram.cpp b/Source/Falcor/Scene/Material/SerializedMaterialParams.h similarity index 64% rename from Source/Falcor/Core/Program/GraphicsProgram.cpp rename to Source/Falcor/Scene/Material/SerializedMaterialParams.h index 2e980f6b2..f5e482369 100644 --- a/Source/Falcor/Core/Program/GraphicsProgram.cpp +++ b/Source/Falcor/Scene/Material/SerializedMaterialParams.h @@ -25,38 +25,46 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#include "GraphicsProgram.h" -#include "ProgramManager.h" -#include "Core/ObjectPython.h" -#include "Core/API/Device.h" -#include "Utils/Scripting/ScriptBindings.h" +#pragma once + +#include "Core/Error.h" +#include "Utils/Math/Vector.h" +#include namespace Falcor { -ref GraphicsProgram::create(ref pDevice, const Desc& desc, const DefineList& programDefines) -{ - return ref(new GraphicsProgram(pDevice, desc, programDefines)); -} - -ref GraphicsProgram::createFromFile( - ref pDevice, - const std::filesystem::path& path, - const std::string& vsEntry, - const std::string& psEntry, - const DefineList& programDefines -) + +struct SerializedMaterialParams : public std::array { - Desc d(path); - d.vsEntry(vsEntry).psEntry(psEntry); - return create(pDevice, d, programDefines); -} + static constexpr size_t kParamCount = 20; -GraphicsProgram::GraphicsProgram(ref pDevice, const Desc& desc, const DefineList& programDefines) - : Program(pDevice, desc, programDefines) -{} + void write(float value, size_t offset) + { + FALCOR_ASSERT(offset <= size()); + (*this)[offset] = value; + } + + template + void write(math::vector value, size_t offset) + { + FALCOR_ASSERT(offset + N <= size()); + for (size_t i = 0; i < N; ++i) + (*this)[offset + i] = value[i]; + } + + void read(float& value, size_t offset) const + { + FALCOR_ASSERT(offset <= size()); + value = (*this)[offset]; + } + + template + void read(math::vector& value, size_t offset) const + { + FALCOR_ASSERT(offset + N <= size()); + for (size_t i = 0; i < N; ++i) + value[i] = (*this)[offset + i]; + } +}; -FALCOR_SCRIPT_BINDING(GraphicsProgram) -{ - pybind11::class_>(m, "GraphicsProgram"); -} } // namespace Falcor diff --git a/Source/Falcor/Scene/Material/ShadingUtils.slang b/Source/Falcor/Scene/Material/ShadingUtils.slang index a53963fc9..46ff3eb75 100644 --- a/Source/Falcor/Scene/Material/ShadingUtils.slang +++ b/Source/Falcor/Scene/Material/ShadingUtils.slang @@ -55,6 +55,7 @@ float3 rgToNormal(float2 rg) } // TODO: this function is broken an may return negative values. +[Differentiable] float getMetallic(float3 diffuse, float3 spec) { // This is based on the way that UE4 and Substance Painter 2 converts base+metallic+specular level to diffuse/spec colors @@ -106,6 +107,7 @@ ShadingFrame computeShadingFrameFromNormalMap(const ShadingData sd, const Normal \param[in] sd Shading data. \param[in,out] sf Shading frame that will be modified. */ +[Differentiable] void flipShadingNormal(const ShadingData sd, inout ShadingFrame sf) { if (!sd.frontFacing && sd.mtl.isDoubleSided()) diff --git a/Source/Falcor/Scene/Material/StandardMaterial.cpp b/Source/Falcor/Scene/Material/StandardMaterial.cpp index c171f8396..8303583db 100644 --- a/Source/Falcor/Scene/Material/StandardMaterial.cpp +++ b/Source/Falcor/Scene/Material/StandardMaterial.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "StandardMaterial.h" +#include "StandardMaterialParamLayout.slang" #include "Utils/Logger.h" #include "Utils/Scripting/ScriptBindings.h" #include "GlobalState.h" @@ -79,6 +80,9 @@ namespace Falcor float emissiveFactor = getEmissiveFactor(); if (widget.var("Emissive factor", emissiveFactor, 0.f, std::numeric_limits::max(), 0.01f)) setEmissiveFactor(emissiveFactor); + bool hasEntryPointVolumeProperties = getHasEntryPointVolumeProperties(); + if (widget.checkbox("Textured absorption coefficient", hasEntryPointVolumeProperties)) setHasEntryPointVolumeProperties(hasEntryPointVolumeProperties); + // Restore update flags. changed |= mUpdates != UpdateFlags::None; markUpdates(prevUpdates | mUpdates); @@ -123,19 +127,19 @@ namespace Falcor } } - Program::ShaderModuleList StandardMaterial::getShaderModules() const + ProgramDesc::ShaderModuleList StandardMaterial::getShaderModules() const { - return { Program::ShaderModule(kShaderFile) }; + return { ProgramDesc::ShaderModule::fromFile(kShaderFile) }; } - Program::TypeConformanceList StandardMaterial::getTypeConformances() const + TypeConformanceList StandardMaterial::getTypeConformances() const { return { {{"StandardMaterial", "IMaterial"}, (uint32_t)MaterialType::Standard} }; } void StandardMaterial::setShadingModel(ShadingModel model) { - checkArgument(model == ShadingModel::MetalRough || model == ShadingModel::SpecGloss, "'model' must be MetalRough or SpecGloss"); + FALCOR_CHECK(model == ShadingModel::MetalRough || model == ShadingModel::SpecGloss, "'model' must be MetalRough or SpecGloss"); if (getShadingModel() != model) { @@ -182,7 +186,7 @@ namespace Falcor if (any(mData.emissive != color)) { mData.emissive = color; - markUpdates(UpdateFlags::DataChanged); + markUpdates(UpdateFlags::DataChanged | UpdateFlags::EmissiveChanged); updateEmissiveFlag(); } } @@ -192,11 +196,53 @@ namespace Falcor if (mData.emissiveFactor != factor) { mData.emissiveFactor = factor; - markUpdates(UpdateFlags::DataChanged); + markUpdates(UpdateFlags::DataChanged | UpdateFlags::EmissiveChanged); updateEmissiveFlag(); } } + void StandardMaterial::setHasEntryPointVolumeProperties(bool hasEntryPointVolumeProperties) + { + if (mData.getHasEntryPointVolumeProperties() != hasEntryPointVolumeProperties) + { + mData.setHasEntryPointVolumeProperties(hasEntryPointVolumeProperties); + markUpdates(UpdateFlags::DataChanged); + } + } + + bool StandardMaterial::getHasEntryPointVolumeProperties() const + { + return getShadingModel() == ShadingModel::SpecGloss ? false : mData.getHasEntryPointVolumeProperties(); + } + + DefineList StandardMaterial::getDefines() const + { + DefineList defines; + + if (mData.getHasEntryPointVolumeProperties()) + defines.add("HAS_MATERIAL_VOLUME_PROPERITES", "1"); + + return defines; + } + + const MaterialParamLayout& StandardMaterial::getParamLayout() const + { + FALCOR_CHECK(getShadingModel() == ShadingModel::MetalRough, "Only MetalRough shading model is supported in parameter layout."); + return StandardMaterialParamLayout::layout(); + } + + SerializedMaterialParams StandardMaterial::serializeParams() const + { + FALCOR_CHECK(getShadingModel() == ShadingModel::MetalRough, "Only MetalRough shading model is supported for serialization."); + return StandardMaterialParamLayout::serialize(this); + } + + void StandardMaterial::deserializeParams(const SerializedMaterialParams& params) + { + FALCOR_CHECK(getShadingModel() == ShadingModel::MetalRough, "Only MetalRough shading model is supported for serialization."); + StandardMaterialParamLayout::deserialize(this, params); + } + FALCOR_SCRIPT_BINDING(StandardMaterial) { using namespace pybind11::literals; @@ -214,6 +260,7 @@ namespace Falcor }; material.def(pybind11::init(create), "name"_a = "", "model"_a = ShadingModel::MetalRough); // PYTHONDEPRECATED + material.def_property("entryPointVolumeProperties", &StandardMaterial::getHasEntryPointVolumeProperties, &StandardMaterial::setHasEntryPointVolumeProperties); material.def_property("roughness", &StandardMaterial::getRoughness, &StandardMaterial::setRoughness); material.def_property("metallic", &StandardMaterial::getMetallic, &StandardMaterial::setMetallic); material.def_property("emissiveColor", &StandardMaterial::getEmissiveColor, &StandardMaterial::setEmissiveColor); diff --git a/Source/Falcor/Scene/Material/StandardMaterial.h b/Source/Falcor/Scene/Material/StandardMaterial.h index 724353c5f..4f15733b9 100644 --- a/Source/Falcor/Scene/Material/StandardMaterial.h +++ b/Source/Falcor/Scene/Material/StandardMaterial.h @@ -81,8 +81,8 @@ namespace Falcor */ bool renderUI(Gui::Widgets& widget) override; - Program::ShaderModuleList getShaderModules() const override; - Program::TypeConformanceList getTypeConformances() const override; + ProgramDesc::ShaderModuleList getShaderModules() const override; + TypeConformanceList getTypeConformances() const override; /** Get the shading model. */ @@ -130,6 +130,16 @@ namespace Falcor mHeader.setEnableLightProfile( enabled ); } + void setHasEntryPointVolumeProperties(bool hasEntryPointVolumeProperties); + + bool getHasEntryPointVolumeProperties() const; + + DefineList getDefines() const override; + + const MaterialParamLayout& getParamLayout() const override; + SerializedMaterialParams serializeParams() const override; + void deserializeParams(const SerializedMaterialParams& params) override; + protected: void updateDeltaSpecularFlag() override; diff --git a/Source/Samples/CudaInterop/FalcorCUDA.h b/Source/Falcor/Scene/Material/StandardMaterialParamLayout.slang similarity index 55% rename from Source/Samples/CudaInterop/FalcorCUDA.h rename to Source/Falcor/Scene/Material/StandardMaterialParamLayout.slang index 8469a8092..2c03b167d 100644 --- a/Source/Samples/CudaInterop/FalcorCUDA.h +++ b/Source/Falcor/Scene/Material/StandardMaterialParamLayout.slang @@ -25,39 +25,23 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#pragma once -#include "Core/API/Texture.h" -#include "Core/API/RenderContext.h" +#include "MaterialParamLayout.slangh" -#include +BEGIN_NAMESPACE_FALCOR -namespace FalcorCUDA -{ +#define MATERIAL_PARAMS(PARAM) \ + PARAM(float3, 0, baseColor, getBaseColor3, setBaseColor3, "base_color") \ + PARAM(float, 3, metallic, getMetallic, setMetallic, "metallic") \ + PARAM(float, 4, roughness, getRoughness, setRoughness, "roughness", 0.05f) \ + PARAM(float, 5, ior, getIndexOfRefraction, setIndexOfRefraction, "ior", 1e-3f, 1e3f) \ + PARAM(float3, 6, transmissionColor, getTransmissionColor, setTransmissionColor, "transmission_color") \ + PARAM(float, 9, diffuseTransmission, getDiffuseTransmission, setDiffuseTransmission, "diffuse_transmission", 0.f) \ + PARAM(float, 10, specularTransmission, getSpecularTransmission, setSpecularTransmission, "specular_transmission", 0.f) \ + PARAM(float3, 11, emissiveColor, getEmissiveColor, setEmissiveColor, "emissive_color") \ + PARAM(float, 14, emissiveFactor, getEmissiveFactor, setEmissiveFactor, "emissive_factor", 1e-8f, 1e8f) -/** - * Initializes the CUDA driver API. Returns true if successful, false otherwise. - */ -bool initCUDA(); +DEFINE_MATERIAL_PARAM_LAYOUT(StandardMaterial, MATERIAL_PARAMS) -/** - * Imports the texture into a CUDA mipmapped array and returns the array in mipmappedArray. This method should only be - * called once per texture resource. - * @param pTex Pointer to the texture being imported - * @param mipmappedArray Reference to the array to import to - * @param usageFlags The requested flags to be bound to the mipmapped array - * @return True if successful, false otherwise - */ -bool importTextureToMipmappedArray(Falcor::ref pTex, cudaMipmappedArray_t& mipmappedArray, uint32_t cudaUsageFlags); +#undef MATERIAL_PARAMS -/** - * Maps a texture to a surface object which can be read and written within a CUDA kernel. - * This method should only be called once per texture on initial load. Store the returned surface object for repeated - * use. - * @param pTex Pointer to the texture being mapped - * @param usageFlags The requested flags to be bound to the underlying mipmapped array that will be used to create the - * surface object - * @return The surface object that the input texture is bound to - */ -cudaSurfaceObject_t mapTextureToSurface(Falcor::ref pTex, uint32_t usageFlags); - -}; // namespace FalcorCUDA +END_NAMESPACE_FALCOR diff --git a/Source/Falcor/Scene/Material/TextureHandle.slang b/Source/Falcor/Scene/Material/TextureHandle.slang index 110a4554f..3355cbe0d 100644 --- a/Source/Falcor/Scene/Material/TextureHandle.slang +++ b/Source/Falcor/Scene/Material/TextureHandle.slang @@ -30,7 +30,7 @@ BEGIN_NAMESPACE_FALCOR -/** Struct representing a material texture handle. +/** Struct representing a material texture handle on the GPU. A texture handle can be in different modes: - 'Uniform' handle refers to a constant value. @@ -58,6 +58,13 @@ struct TextureHandle static constexpr uint kModeOffset = kTextureIDBits; static constexpr uint kUdimEnabledOffset = kModeOffset + kModeBits; +#ifdef HOST_CODE + TextureHandle() = default; + explicit TextureHandle(uint32_t packedData_) : packedData(packedData_) {} +#else + __init(uint32_t packedData) { this.packedData = packedData; } +#endif + /** Set mode. */ SETTER_DECL void setMode(Mode mode) { packedData = PACK_BITS(kModeBits, kModeOffset, packedData, (uint)mode); } @@ -80,7 +87,7 @@ struct TextureHandle /** Set whether the texture uses udim or not. */ - bool getUdimEnabled() { return IS_BIT_SET(packedData, kUdimEnabledOffset); } + bool getUdimEnabled() CONST_FUNCTION { return IS_BIT_SET(packedData, kUdimEnabledOffset); } #ifdef HOST_CODE bool operator==(const TextureHandle& rhs) const { return packedData == rhs.packedData; } diff --git a/Source/Falcor/Scene/Material/TextureSampler.slang b/Source/Falcor/Scene/Material/TextureSampler.slang index 7b2aadbc4..8f85bcfeb 100644 --- a/Source/Falcor/Scene/Material/TextureSampler.slang +++ b/Source/Falcor/Scene/Material/TextureSampler.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Scene/MeshIO.cs.slang b/Source/Falcor/Scene/MeshIO.cs.slang new file mode 100644 index 000000000..4d451790a --- /dev/null +++ b/Source/Falcor/Scene/MeshIO.cs.slang @@ -0,0 +1,101 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +import Scene.SceneTypes; +import Scene.Scene; + +struct MeshLoader +{ + uint vertexCount; + uint vbOffset; + uint triangleCount; + uint ibOffset; + bool use16BitIndices; + + ParameterBlock scene; + + // Output + RWStructuredBuffer positions; + RWStructuredBuffer texcrds; + RWStructuredBuffer triangleIndices; + + void getMeshIndices(uint triangleId) + { + if (triangleId >= triangleCount) return; + uint3 vtxIndices = scene.getLocalIndices(ibOffset, triangleId, use16BitIndices); + triangleIndices[triangleId] = vtxIndices; + } + + void getMeshVertexData(uint vertexId) + { + if (vertexId >= vertexCount) return; + StaticVertexData vtxData = scene.getVertex(vertexId + vbOffset); + positions[vertexId] = vtxData.position; + texcrds[vertexId] = float3(vtxData.texCrd, 0.f); + } +}; + +struct MeshUpdater +{ + uint vertexCount; + uint vbOffset; + + StructuredBuffer positions; + StructuredBuffer normals; + StructuredBuffer tangents; + StructuredBuffer texcrds; + + // Output + RWStructuredBuffer vertexData; + + void setMeshVertexData(uint vertexId) + { + if (vertexId >= vertexCount) return; + StaticVertexData vtxData; + vtxData.position = positions[vertexId]; + vtxData.normal = normals[vertexId]; + vtxData.tangent = float4(tangents[vertexId], 1.f); // Tangent follows the orientation such that `b = cross(n, t)`. + vtxData.texCrd = texcrds[vertexId].xy; + vertexData[vertexId + vbOffset].pack(vtxData); + } +}; + +ParameterBlock meshLoader; +ParameterBlock meshUpdater; + +[numthreads(256, 1, 1)] +void getMeshVerticesAndIndices(uint3 tid: SV_DispatchThreadID) +{ + meshLoader.getMeshIndices(tid.x); + meshLoader.getMeshVertexData(tid.x); +} + +[numthreads(256, 1, 1)] +void setMeshVertices(uint3 tid: SV_DispatchThreadID) +{ + meshUpdater.setMeshVertexData(tid.x); +} diff --git a/Source/Falcor/Scene/RaytracingInline.slang b/Source/Falcor/Scene/RaytracingInline.slang index 6c61dcf95..b13180d13 100644 --- a/Source/Falcor/Scene/RaytracingInline.slang +++ b/Source/Falcor/Scene/RaytracingInline.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -82,7 +82,7 @@ TriangleHit getCandidateTriangleHit(RayQuery rayQuery) // TODO: Pass UseAlphaTest as a template argument. -bool traceSceneRayImpl(const bool useAlphaTest, RayQuery rayQuery, const Ray ray, out HitInfo hit, out float hitT, uint rayFlags, uint instanceInclusionMask) +bool traceSceneRayImpl(const bool useAlphaTest, inout RayQuery rayQuery, const Ray ray, out HitInfo hit, out float hitT, uint rayFlags, uint instanceInclusionMask) { rayQuery.TraceRayInline(gScene.rtAccel, rayFlags, instanceInclusionMask, ray.toRayDesc()); @@ -223,6 +223,7 @@ bool traceSceneRayImpl(const bool useAlphaTest, RayQuery return false; } +[__NoSideEffect] // Required for the performance of reverse-mode auto-diff. bool traceSceneRay(const Ray ray, out HitInfo hit, out float hitT, uint rayFlags, uint instanceInclusionMask) { // TODO: Use a constant expression to derive static ray flags. @@ -247,7 +248,8 @@ bool traceSceneRay(const Ray ray, out HitInfo hit, out f } } -bool traceSceneVisibilityRayImpl(const bool useAlphaTest, RayQuery rayQuery, const Ray ray, uint rayFlags, uint instanceInclusionMask) +[__NoSideEffect] // Required for the performance of reverse-mode auto-diff. +bool traceSceneVisibilityRayImpl(const bool useAlphaTest, inout RayQuery rayQuery, const Ray ray, uint rayFlags, uint instanceInclusionMask) { rayQuery.TraceRayInline(gScene.rtAccel, rayFlags, instanceInclusionMask, ray.toRayDesc()); diff --git a/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.cpp b/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.cpp index 7b3f27b59..4c9b3f727 100644 --- a/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.cpp +++ b/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.cpp @@ -40,12 +40,12 @@ namespace Falcor SharedData(ref pDevice) { Sampler::Desc sdfGridSamplerDesc; - sdfGridSamplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - sdfGridSamplerDesc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - pSampler = Sampler::create(pDevice, sdfGridSamplerDesc); + sdfGridSamplerDesc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Linear); + sdfGridSamplerDesc.setAddressingMode(TextureAddressingMode::Clamp, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); + pSampler = pDevice->createSampler(sdfGridSamplerDesc); RtAABB unitAABB { float3(-0.5f), float3(0.5f) }; - pUnitAABBBuffer = Buffer::create(pDevice, sizeof(RtAABB), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, &unitAABB); + pUnitAABBBuffer = pDevice->createBuffer(sizeof(RtAABB), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, &unitAABB); } }; @@ -79,7 +79,7 @@ namespace Falcor { if (!mPrimitives.empty()) { - throw RuntimeError("An NDSDFGrid instance cannot be created from primitives!"); + FALCOR_THROW("An NDSDFGrid instance cannot be created from primitives!"); } uint32_t lodCount = (uint32_t)mValues.size(); @@ -101,7 +101,7 @@ namespace Falcor } else { - pNDSDFTexture = Texture::create3D(mpDevice, lodWidth, lodWidth, lodWidth, ResourceFormat::R8Snorm, 1, mValues[lod].data()); + pNDSDFTexture = mpDevice->createTexture3D(lodWidth, lodWidth, lodWidth, ResourceFormat::R8Snorm, 1, mValues[lod].data()); } } } @@ -111,11 +111,11 @@ namespace Falcor return mpSharedData->pUnitAABBBuffer; } - void NDSDFGrid::setShaderData(const ShaderVar& var) const + void NDSDFGrid::bindShaderData(const ShaderVar& var) const { if (mNDSDFTextures.empty()) { - throw RuntimeError("NDSDFGrid::setShaderData() can't be called before calling NDSDFGrid::createResources()!"); + FALCOR_THROW("NDSDFGrid::bindShaderData() can't be called before calling NDSDFGrid::createResources()!"); } var["sampler"] = mpSharedData->pSampler; @@ -138,7 +138,7 @@ namespace Falcor if (kCoarsestAllowedGridWidth > mGridWidth) { - throw RuntimeError("NDSDFGrid::setValues() grid width must be larger than {}.", kCoarsestAllowedGridWidth); + FALCOR_THROW("NDSDFGrid::setValues() grid width must be larger than {}.", kCoarsestAllowedGridWidth); } uint32_t lodCount = bitScanReverse(mGridWidth / kCoarsestAllowedGridWidth) + 1; diff --git a/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.h b/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.h index 3dedfc0b7..3f85f60e2 100644 --- a/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.h +++ b/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.h @@ -55,7 +55,7 @@ namespace Falcor virtual void createResources(RenderContext* pRenderContext, bool deleteScratchData = true) override; virtual const ref& getAABBBuffer() const override; virtual uint32_t getAABBCount() const override { return 1; } - virtual void setShaderData(const ShaderVar& var) const override; + virtual void bindShaderData(const ShaderVar& var) const override; protected: virtual void setValuesInternal(const std::vector& cornerValues) override; diff --git a/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.slang b/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.slang index 6de4e0692..7f3a75a7c 100644 --- a/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.slang +++ b/Source/Falcor/Scene/SDFs/NormalizedDenseSDFGrid/NDSDFGrid.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Scene/SDFs/SDF3DPrimitiveFactory.cpp b/Source/Falcor/Scene/SDFs/SDF3DPrimitiveFactory.cpp index 7ba558d3f..03f4efd9b 100644 --- a/Source/Falcor/Scene/SDFs/SDF3DPrimitiveFactory.cpp +++ b/Source/Falcor/Scene/SDFs/SDF3DPrimitiveFactory.cpp @@ -120,7 +120,7 @@ AABB SDF3DPrimitiveFactory::computeAABB(const SDF3DPrimitive& primitive) } break; default: - throw RuntimeError("SDF Primitive has unknown primitive type"); + FALCOR_THROW("SDF Primitive has unknown primitive type"); } float4x4 translate = math::matrixFromTranslation(primitive.translation); diff --git a/Source/Falcor/Scene/SDFs/SDFGrid.cpp b/Source/Falcor/Scene/SDFs/SDFGrid.cpp index be31914ac..02cea748c 100644 --- a/Source/Falcor/Scene/SDFs/SDFGrid.cpp +++ b/Source/Falcor/Scene/SDFs/SDFGrid.cpp @@ -26,12 +26,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "SDFGrid.h" +#include "GlobalState.h" #include "NormalizedDenseSDFGrid/NDSDFGrid.h" #include "SparseVoxelSet/SDFSVS.h" #include "SparseBrickSet/SDFSBS.h" #include "SparseVoxelOctree/SDFSVO.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/API/Device.h" #include "Core/API/RenderContext.h" #include "Utils/Logger.h" @@ -149,7 +149,11 @@ namespace Falcor j[kPrimitiveInvRotationScaleJSONKey].get_to(primitive.invRotationScale); } - SDFGrid::SDFGrid(ref pDevice) : mpDevice(pDevice) {} + SDFGrid::SDFGrid(ref pDevice) : mpDevice(pDevice) + { + if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_5)) + FALCOR_THROW("SDFGrid requires Shader Model 6.5 support."); + } uint32_t SDFGrid::setPrimitives(const std::vector& primitives, uint32_t gridWidth) { @@ -158,7 +162,7 @@ namespace Falcor if (type != Type::SparseBrickSet) { // TODO: Expand the grid to match a grid size that is a power of 2 instead of throwing an exception. - checkArgument(isPowerOf2(gridWidth), "'gridWidth' ({}) must be a power of 2 for SDFGrid type of {}", gridWidth, getTypeName(type)); + FALCOR_CHECK(isPowerOf2(gridWidth), "'gridWidth' ({}) must be a power of 2 for SDFGrid type of {}", gridWidth, getTypeName(type)); } mGridWidth = gridWidth; @@ -190,7 +194,7 @@ namespace Falcor { if (!indexSet.insert(index).second) { - throw RuntimeError("Multiple copies of index {}!", index); + FALCOR_THROW("Multiple copies of index {}!", index); } } @@ -248,7 +252,7 @@ namespace Falcor { if (!indexSet.insert(index).second) { - throw RuntimeError("Multiple copies of index {}!", index); + FALCOR_THROW("Multiple copies of index {}!", index); } } @@ -285,7 +289,7 @@ namespace Falcor Type type = getType(); if (type != Type::SparseBrickSet) { - checkArgument(isPowerOf2(gridWidth), "'gridWidth' ({}) must be a power of 2 for SDFGrid type of {}", gridWidth, getTypeName(type)); + FALCOR_CHECK(isPowerOf2(gridWidth), "'gridWidth' ({}) must be a power of 2 for SDFGrid type of {}", gridWidth, getTypeName(type)); } mGridWidth = gridWidth; @@ -295,26 +299,22 @@ namespace Falcor bool SDFGrid::loadValuesFromFile(const std::filesystem::path& path) { - std::filesystem::path fullPath; - if (findFileInDataDirectories(path, fullPath)) - { - std::ifstream file(fullPath, std::ios::in | std::ios::binary); + std::ifstream file(path, std::ios::in | std::ios::binary); - if (file.is_open()) - { - uint32_t gridWidth; - file.read(reinterpret_cast(&gridWidth), sizeof(uint32_t)); + if (file.is_open()) + { + uint32_t gridWidth; + file.read(reinterpret_cast(&gridWidth), sizeof(uint32_t)); - uint32_t totalValueCount = (gridWidth + 1) * (gridWidth + 1) * (gridWidth + 1); - std::vector cornerValues(totalValueCount, 0.0f); - file.read(reinterpret_cast(cornerValues.data()), totalValueCount * sizeof(float)); + uint32_t totalValueCount = (gridWidth + 1) * (gridWidth + 1) * (gridWidth + 1); + std::vector cornerValues(totalValueCount, 0.0f); + file.read(reinterpret_cast(cornerValues.data()), totalValueCount * sizeof(float)); - file.close(); - setValues(cornerValues, gridWidth); + file.close(); + setValues(cornerValues, gridWidth); - mInitializedWithPrimitives = false; - return true; - } + mInitializedWithPrimitives = false; + return true; } logWarning("SDFGrid::loadValuesFromFile() file '{}' could not be opened!", path); @@ -383,9 +383,7 @@ namespace Falcor uint32_t gridWidthInValues = mGridWidth + 1; uint32_t valueCount = gridWidthInValues * gridWidthInValues * gridWidthInValues; - ref pValuesBuffer = Buffer::createTyped(mpDevice, valueCount); - ref pValuesStagingBuffer = Buffer::createTyped(mpDevice, valueCount, Resource::BindFlags::None, Buffer::CpuAccess::Read); - ref pFence = GpuFence::create(mpDevice); + ref pValuesBuffer = mpDevice->createTypedBuffer(valueCount); auto var = mpEvaluatePrimitivesPass->getRootVar(); var["CB"]["gGridWidth"] = mGridWidth; @@ -394,45 +392,26 @@ namespace Falcor var["gOldValues"] = mHasGridRepresentation ? mpSDFGridTexture : nullptr; var["gValues"] = pValuesBuffer; mpEvaluatePrimitivesPass->execute(pRenderContext, uint3(gridWidthInValues)); - pRenderContext->copyResource(pValuesStagingBuffer.get(), pValuesBuffer.get()); - pRenderContext->flush(false); - pFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); - pFence->syncCpu(); - const float* pValues = reinterpret_cast(pValuesStagingBuffer->map(Buffer::MapType::Read)); + std::vector values = pValuesBuffer->getElements(); std::ofstream file(path, std::ios::out | std::ios::binary); if (file.is_open()) { file.write(reinterpret_cast(&mGridWidth), sizeof(uint32_t)); - file.write(reinterpret_cast(pValues), valueCount * sizeof(float)); + file.write(reinterpret_cast(values.data()), values.size() * sizeof(float)); file.close(); } - pValuesStagingBuffer->unmap(); return true; } - uint32_t SDFGrid::loadPrimitivesFromFile(const std::filesystem::path& path, uint32_t gridWidth, const std::filesystem::path& dir) + uint32_t SDFGrid::loadPrimitivesFromFile(const std::filesystem::path& path, uint32_t gridWidth) { - std::filesystem::path fullPath; - if (dir.empty()) - { - if (!findFileInDataDirectories(path, fullPath)) - { - logWarning("File '{}' could not be found in data directories!", path); - return 0; - } - } - else - { - fullPath = dir / path; - } - - std::ifstream ifs(fullPath); + std::ifstream ifs(path); if (!ifs.good()) { - logWarning("Failed to open SDF grid file '{}' for reading.", fullPath); + logWarning("Failed to open SDF grid file '{}' for reading.", path); return false; } @@ -444,7 +423,7 @@ namespace Falcor } catch (const std::exception& e) { - logWarning("Error when deserializing SDF grid from '{}': {}", fullPath, e.what()); + logWarning("Error when deserializing SDF grid from '{}': {}", path, e.what()); return 0; } @@ -474,7 +453,7 @@ namespace Falcor const SDF3DPrimitive& SDFGrid::getPrimitive(uint32_t primitiveID) const { auto it = mPrimitiveIDToIndex.find(primitiveID); - checkArgument(it != mPrimitiveIDToIndex.end(), "'primitiveID' ({}) is invalid.", primitiveID); + FALCOR_CHECK(it != mPrimitiveIDToIndex.end(), "'primitiveID' ({}) is invalid.", primitiveID); return mPrimitives[it->second]; } @@ -538,8 +517,14 @@ namespace Falcor sdfGrid.def_static("createSVS", [](){ return static_ref_cast(SDFSVS::create(accessActivePythonSceneBuilder().getDevice())); }); // PYTHONDEPRECATED sdfGrid.def_static("createSBS", createSBS); // PYTHONDEPRECATED sdfGrid.def_static("createSVO", [](){ return static_ref_cast(SDFSVO::create(accessActivePythonSceneBuilder().getDevice())); }); // PYTHONDEPRECATED - sdfGrid.def("loadValuesFromFile", &SDFGrid::loadValuesFromFile, "path"_a); - sdfGrid.def("loadPrimitivesFromFile", &SDFGrid::loadPrimitivesFromFile, "path"_a, "gridWidth"_a, "dir"_a = ""); + sdfGrid.def("loadValuesFromFile", + [](SDFGrid& self, const std::filesystem::path& path) { return self.loadValuesFromFile(getActiveAssetResolver().resolvePath(path)); }, + "path"_a + ); // PYTHONDEPRECATED + sdfGrid.def("loadPrimitivesFromFile", + [](SDFGrid& self, const std::filesystem::path& path, uint32_t gridWidth) { return self.loadPrimitivesFromFile(getActiveAssetResolver().resolvePath(path), gridWidth); }, + "path"_a, "gridWidth"_a + ); // PYTHONDEPRECATED sdfGrid.def("generateCheeseValues", &SDFGrid::generateCheeseValues, "gridWidth"_a, "seed"_a); sdfGrid.def_property("name", &SDFGrid::getName, &SDFGrid::setName); } @@ -548,8 +533,8 @@ namespace Falcor { if (!mpEvaluatePrimitivesPass) { - Program::Desc desc; - desc.addShaderLibrary(kEvaluateSDFPrimitivesShaderName).csEntry("main").setShaderModel("6_5"); + ProgramDesc desc; + desc.addShaderLibrary(kEvaluateSDFPrimitivesShaderName).csEntry("main"); mpEvaluatePrimitivesPass = ComputePass::create(mpDevice, desc); } @@ -580,7 +565,7 @@ namespace Falcor void* pData = (void*)&mPrimitives[mPrimitivesExcludedFromBuffer]; if (!mpPrimitivesBuffer || mpPrimitivesBuffer->getElementCount() < count) { - mpPrimitivesBuffer = Buffer::createStructured(mpDevice, sizeof(SDF3DPrimitive), count, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, pData, false); + mpPrimitivesBuffer = mpDevice->createStructuredBuffer(sizeof(SDF3DPrimitive), count, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, pData, false); } else { diff --git a/Source/Falcor/Scene/SDFs/SDFGrid.h b/Source/Falcor/Scene/SDFs/SDFGrid.h index 1eb37bdf0..9b52e013f 100644 --- a/Source/Falcor/Scene/SDFs/SDFGrid.h +++ b/Source/Falcor/Scene/SDFs/SDFGrid.h @@ -144,10 +144,9 @@ namespace Falcor /** Reads primitives from file and initializes the SDF grid. \param[in] path The path to the input file. \param[in] gridWidth The targeted width of the SDF grid, the resulting grid may have a larger width. - \param[in] dir A directory path, if this is empty, the file will be searched for in data directories. \return The number of primitives loaded. */ - uint32_t loadPrimitivesFromFile(const std::filesystem::path& path, uint32_t gridWidth, const std::filesystem::path& dir); + uint32_t loadPrimitivesFromFile(const std::filesystem::path& path, uint32_t gridWidth); /** Write the primitives to file. \param[in] path The path to the output file. @@ -208,7 +207,7 @@ namespace Falcor /** Binds the SDF grid into a given shader var. */ - virtual void setShaderData(const ShaderVar& var) const = 0; + virtual void bindShaderData(const ShaderVar& var) const = 0; /** Return the scaling factor that represent how the grid resolution has changed from when it was loaded. The resolution can change if loaded by the SBS grid and then edited by the SDFEditor. */ diff --git a/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.cpp b/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.cpp index 2afef195c..d4d4961bf 100644 --- a/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.cpp +++ b/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.cpp @@ -65,9 +65,9 @@ namespace Falcor SharedData(ref pDevice) { Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - samplerDesc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - pSampler = Sampler::create(pDevice, samplerDesc); + samplerDesc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Linear); + samplerDesc.setAddressingMode(TextureAddressingMode::Clamp, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); + pSampler = pDevice->createSampler(samplerDesc); } }; @@ -79,7 +79,7 @@ namespace Falcor , mBrickWidth(brickWidth) , mCompressed(compressed) { - checkArgument(!compressed || (brickWidth + 1) % 4 == 0, "'brickWidth' ({}) must be a multiple of 4 minus 1 for compressed SDFSBSs", brickWidth); + FALCOR_CHECK(!compressed || (brickWidth + 1) % 4 == 0, "'brickWidth' ({}) must be a multiple of 4 minus 1 for compressed SDFSBSs", brickWidth); mpSharedData = sSharedCache.acquire(mpDevice.get(), [this]() { return std::make_shared(mpDevice); }); @@ -147,11 +147,11 @@ namespace Falcor allocatePrimitiveBits(); } - void SDFSBS::setShaderData(const ShaderVar& var) const + void SDFSBS::bindShaderData(const ShaderVar& var) const { if (!mpBrickAABBsBuffer || !mpIndirectionTexture || !mpBrickTexture) { - throw RuntimeError("SDFSBS::setShaderData() can't be called before calling SDFSBS::createResources()!"); + FALCOR_THROW("SDFSBS::bindShaderData() can't be called before calling SDFSBS::createResources()!"); } var["aabbs"] = mpBrickAABBsBuffer; @@ -179,7 +179,7 @@ namespace Falcor { if (!mpAssignBrickValidityPass) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kAssignBrickValidityShaderName).csEntry("main"); // For brick widths smaller than 8 brick validation will be performed using group shared memory. @@ -194,13 +194,13 @@ namespace Falcor if (!mpIndirectionTexture || mpIndirectionTexture->getWidth() < mVirtualBricksPerAxis) { - mpIndirectionTexture = Texture::create3D(mpDevice, mVirtualBricksPerAxis, mVirtualBricksPerAxis, mVirtualBricksPerAxis, ResourceFormat::R32Uint, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpIndirectionTexture = mpDevice->createTexture3D(mVirtualBricksPerAxis, mVirtualBricksPerAxis, mVirtualBricksPerAxis, ResourceFormat::R32Uint, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); mpIndirectionTexture->setName("SDFSBS::IndirectionTextureValues"); } if (!mpValidityBuffer || mpValidityBuffer->getElementCount() < virtualBrickCount) { - mpValidityBuffer = Buffer::createStructured(mpDevice, sizeof(uint32_t), virtualBrickCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpValidityBuffer = mpDevice->createStructuredBuffer(sizeof(uint32_t), virtualBrickCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); } else { @@ -225,12 +225,12 @@ namespace Falcor if (!mpIndirectionBuffer || mpIndirectionBuffer->getElementCount() < virtualBrickCount) { - mpIndirectionBuffer = Buffer::createStructured(mpDevice, sizeof(uint32_t), virtualBrickCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpIndirectionBuffer = mpDevice->createStructuredBuffer(sizeof(uint32_t), virtualBrickCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); } if (!mpCountBuffer) { - mpCountBuffer = Buffer::create(mpDevice, 4, ResourceBindFlags::None, Buffer::CpuAccess::None); + mpCountBuffer = mpDevice->createBuffer(4, ResourceBindFlags::None, MemoryType::DeviceLocal); } pRenderContext->copyResource(mpIndirectionBuffer.get(), mpValidityBuffer.get()); @@ -242,7 +242,7 @@ namespace Falcor { if (!mpResetBrickValidityPass) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kResetBrickValidityShaderName).csEntry("main"); mpResetBrickValidityPass = ComputePass::create(mpDevice, desc); } @@ -258,7 +258,7 @@ namespace Falcor { if (!mpCopyIndirectionBufferPass) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kCopyIndirectionBufferShaderName).csEntry("main"); mpCopyIndirectionBufferPass = ComputePass::create(mpDevice, desc); } @@ -274,7 +274,7 @@ namespace Falcor { if (!mpCreateBricksFromSDFieldPass) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kCreateBricksFromSDFieldShaderName).csEntry("main"); mpCreateBricksFromSDFieldPass = ComputePass::create(mpDevice, desc, { {"COMPRESS_BRICKS", mCompressed ? "1" : "0"} }); } @@ -301,16 +301,16 @@ namespace Falcor if (mCompressed) { - mpBrickTexture = Texture::create2D(mpDevice, textureWidth, textureHeight, ResourceFormat::BC4Snorm, 1, 1); + mpBrickTexture = mpDevice->createTexture2D(textureWidth, textureHeight, ResourceFormat::BC4Snorm, 1, 1); // Compression scheme may change the actual width and height to something else. mBrickTextureDimensions = uint2(mpBrickTexture->getWidth(), mpBrickTexture->getHeight()); - mpBrickScratchTexture = Texture::create2D(mpDevice, mBrickTextureDimensions.x / 4, mBrickTextureDimensions.y / 4, ResourceFormat::RG32Int, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess); + mpBrickScratchTexture = mpDevice->createTexture2D(mBrickTextureDimensions.x / 4, mBrickTextureDimensions.y / 4, ResourceFormat::RG32Int, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess); } else { - mpBrickTexture = Texture::create2D(mpDevice, textureWidth, textureHeight, ResourceFormat::R8Snorm, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource); + mpBrickTexture = mpDevice->createTexture2D(textureWidth, textureHeight, ResourceFormat::R8Snorm, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource); mBrickTextureDimensions = uint2(textureWidth, textureHeight); } @@ -318,7 +318,7 @@ namespace Falcor if (!mpBrickAABBsBuffer || mpBrickAABBsBuffer->getElementCount() < mBrickCount) { - mpBrickAABBsBuffer = Buffer::createStructured(mpDevice, sizeof(AABB), mBrickCount, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpBrickAABBsBuffer = mpDevice->createStructuredBuffer(sizeof(AABB), mBrickCount, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); } auto paramBlock = mpCreateBricksFromSDFieldPass->getRootVar()["gParamBlock"]; @@ -377,7 +377,7 @@ namespace Falcor if (!includeValues && mBakePrimitives) { // Create a grid to hold the values if the grid is missing a value representation but request to bake the primitives. - mpSDFGridTexture = Texture::create3D(mpDevice, gridWidthInValues, gridWidthInValues, gridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpSDFGridTexture = mpDevice->createTexture3D(gridWidthInValues, gridWidthInValues, gridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); mpSDFGridTexture->setName("SDFSBS::SDFGridTexture"); pRenderContext->clearUAV(mpSDFGridTexture->getUAV().get(), float4(std::numeric_limits::max())); includeValues = true; @@ -395,7 +395,7 @@ namespace Falcor if (!mpChunkCoordsBuffer || mpChunkCoordsBuffer->getSize() < mBrickCount * sizeof(uint3)) { uint3 initialData(0); - mpChunkCoordsBuffer = Buffer::create(mpDevice, mBrickCount * sizeof(uint3), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, (const void*)&initialData); + mpChunkCoordsBuffer = mpDevice->createBuffer(mBrickCount * sizeof(uint3), ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, (const void*)&initialData); mpChunkCoordsBuffer->setName("SDFSBS::chunkCoordsBuffer"); } createEmptyBrick = true; @@ -421,7 +421,7 @@ namespace Falcor { if (!mpCreateRootChunksFromPrimitives) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kCreateChunksFromPrimitivesShaderName).csEntry("rootEntryPoint"); DefineList defines; @@ -433,7 +433,7 @@ namespace Falcor if (!mpSubdivideChunksUsingPrimitives) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kCreateChunksFromPrimitivesShaderName).csEntry("subdivideEntryPoint"); DefineList defines; @@ -445,7 +445,7 @@ namespace Falcor if (!mpCoarselyPruneEmptyBricks) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kPruneEmptyBricksShaderName).csEntry("coarsePrune"); DefineList defines; @@ -456,7 +456,7 @@ namespace Falcor if (!mpFinelyPruneEmptyBricks) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kPruneEmptyBricksShaderName).csEntry("finePrune"); DefineList defines; @@ -467,7 +467,7 @@ namespace Falcor if (!mpCreateBricksFromChunks) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kCreateBricksFromChunksShaderName).csEntry("main"); DefineList defines; @@ -503,7 +503,7 @@ namespace Falcor uint32_t currentSubChunkCount = currentGridWidth * currentGridWidth * currentGridWidth; if (!mpSubChunkValidityBuffer || mpSubChunkValidityBuffer->getSize() < currentSubChunkCount * sizeof(uint32_t)) { - mpSubChunkValidityBuffer = Buffer::create(mpDevice, currentSubChunkCount * sizeof(uint32_t)); + mpSubChunkValidityBuffer = mpDevice->createBuffer(currentSubChunkCount * sizeof(uint32_t)); mpSubChunkValidityBuffer->setName("SDFSBS::SubChunkValidityBuffer"); } else @@ -513,7 +513,7 @@ namespace Falcor if (!mpSubChunkCoordsBuffer || mpSubChunkCoordsBuffer->getElementCount() < currentSubChunkCount * sizeof(uint3)) { - mpSubChunkCoordsBuffer = Buffer::create(mpDevice, currentSubChunkCount * sizeof(uint3)); + mpSubChunkCoordsBuffer = mpDevice->createBuffer(currentSubChunkCount * sizeof(uint3)); mpSubChunkCoordsBuffer->setName("SDFSBS::SubChunkCoordsBuffer"); } @@ -522,7 +522,7 @@ namespace Falcor auto paramBlock = mpCreateRootChunksFromPrimitives->getRootVar()["gParamBlock"]; paramBlock["primitiveCount"] = primitiveCount - mCurrentBakedPrimitiveCount; paramBlock["currentGridWidth"] = currentGridWidth; - paramBlock["groupCount"] = 1; + paramBlock["groupCount"] = 1u; paramBlock["intervalValues"] = includeValues ? mIntervalSDFieldMaps[subdivisionCount-1] : nullptr; paramBlock["subChunkValidity"] = mpSubChunkValidityBuffer; paramBlock["subChunkCoords"] = mpSubChunkCoordsBuffer; @@ -539,7 +539,7 @@ namespace Falcor if (!mpSubdivisionArgBuffer) { static const DispatchArguments baseIndirectArgs = { 0, 1, 1 }; - mpSubdivisionArgBuffer = Buffer::create(mpDevice, sizeof(DispatchArguments), ResourceBindFlags::IndirectArg, Buffer::CpuAccess::None, &baseIndirectArgs); + mpSubdivisionArgBuffer = mpDevice->createBuffer(sizeof(DispatchArguments), ResourceBindFlags::IndirectArg, MemoryType::DeviceLocal, &baseIndirectArgs); } // Subdivisions. @@ -555,7 +555,7 @@ namespace Falcor // Update chunk buffers for this subdivision. if (!mpChunkIndirectionBuffer || mpChunkIndirectionBuffer->getSize() < mpSubChunkValidityBuffer->getSize()) { - mpChunkIndirectionBuffer = Buffer::create(mpDevice, mpSubChunkValidityBuffer->getSize()); + mpChunkIndirectionBuffer = mpDevice->createBuffer(mpSubChunkValidityBuffer->getSize()); mpChunkIndirectionBuffer->setName("SDFSBS::ChunkIndirectionBuffer"); } @@ -572,7 +572,7 @@ namespace Falcor { if (!mpChunkCoordsBuffer || mpChunkCoordsBuffer->getSize() < currentChunkCount * sizeof(uint3)) { - mpChunkCoordsBuffer = Buffer::create(mpDevice, currentChunkCount * sizeof(uint3)); + mpChunkCoordsBuffer = mpDevice->createBuffer(currentChunkCount * sizeof(uint3)); mpChunkCoordsBuffer->setName("SDFSBS::ChunkCoordsBuffer"); } @@ -586,7 +586,7 @@ namespace Falcor // Clear or realloc sub chunk buffers. if (!mpSubChunkValidityBuffer || mpSubChunkValidityBuffer->getSize() < currentSubChunkCount * sizeof(uint32_t)) { - mpSubChunkValidityBuffer = Buffer::create(mpDevice, currentSubChunkCount * sizeof(uint32_t)); + mpSubChunkValidityBuffer = mpDevice->createBuffer(currentSubChunkCount * sizeof(uint32_t)); mpSubChunkValidityBuffer->setName("SDFSBS::subChunkValidityBuffer"); } else @@ -596,7 +596,7 @@ namespace Falcor if (!mpSubChunkCoordsBuffer || mpSubChunkCoordsBuffer->getElementCount() < currentSubChunkCount * sizeof(uint3)) { - mpSubChunkCoordsBuffer = Buffer::create(mpDevice, currentSubChunkCount * sizeof(uint3)); + mpSubChunkCoordsBuffer = mpDevice->createBuffer(currentSubChunkCount * sizeof(uint3)); mpSubChunkCoordsBuffer->setName("SDFSBS::subChunkCoordsBuffer"); } @@ -625,7 +625,7 @@ namespace Falcor // Update chunk buffer for brick creation. if (!mpChunkIndirectionBuffer || mpChunkIndirectionBuffer->getSize() < mpSubChunkValidityBuffer->getSize()) { - mpChunkIndirectionBuffer = Buffer::create(mpDevice, mpSubChunkValidityBuffer->getSize()); + mpChunkIndirectionBuffer = mpDevice->createBuffer(mpSubChunkValidityBuffer->getSize()); mpChunkIndirectionBuffer->setName("SDFSBS::ChunkIndirectionBuffer"); } @@ -638,7 +638,7 @@ namespace Falcor if (!mpChunkCoordsBuffer || mpChunkCoordsBuffer->getSize() < mBrickCount * sizeof(uint3)) { - mpChunkCoordsBuffer = Buffer::create(mpDevice, mBrickCount * sizeof(uint3)); + mpChunkCoordsBuffer = mpDevice->createBuffer(mBrickCount * sizeof(uint3)); mpChunkCoordsBuffer->setName("SDFSBS::chunkCoordsBuffer"); } @@ -711,14 +711,14 @@ namespace Falcor // Allocate AABB buffer, indirection buffer and brick texture. if (!mpBrickAABBsBuffer || mpBrickAABBsBuffer->getElementCount() < mBrickCount) { - mpBrickAABBsBuffer = Buffer::createStructured(mpDevice, sizeof(AABB), mBrickCount, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpBrickAABBsBuffer = mpDevice->createStructuredBuffer(sizeof(AABB), mBrickCount, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpBrickAABBsBuffer->setName("SDFSBS::BrickAABBsBuffer"); updateFlags |= UpdateFlags::BuffersReallocated; } if (!mpIndirectionTexture || mpIndirectionTexture->getWidth() < mVirtualBricksPerAxis) { - mpIndirectionTexture = Texture::create3D(mpDevice, mVirtualBricksPerAxis, mVirtualBricksPerAxis, mVirtualBricksPerAxis, ResourceFormat::R32Uint, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mpIndirectionTexture = mpDevice->createTexture3D(mVirtualBricksPerAxis, mVirtualBricksPerAxis, mVirtualBricksPerAxis, ResourceFormat::R32Uint, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); mpIndirectionTexture->setName("SDFSBS::IndirectionTextureValuesPrims"); updateFlags |= UpdateFlags::BuffersReallocated; } @@ -740,17 +740,17 @@ namespace Falcor if (mCompressed) { - mpBrickTexture = Texture::create2D(mpDevice, textureWidth, textureHeight, ResourceFormat::BC4Snorm, 1, 1); + mpBrickTexture = mpDevice->createTexture2D(textureWidth, textureHeight, ResourceFormat::BC4Snorm, 1, 1); mpBrickTexture->setName("SDFSBS::BrickTexture"); // Compression scheme may change the actual width and height to something else. mBrickTextureDimensions = uint2(mpBrickTexture->getWidth(), mpBrickTexture->getHeight()); - mpBrickScratchTexture = Texture::create2D(mpDevice, mBrickTextureDimensions.x / 4, mBrickTextureDimensions.y / 4, ResourceFormat::RG32Int, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess); + mpBrickScratchTexture = mpDevice->createTexture2D(mBrickTextureDimensions.x / 4, mBrickTextureDimensions.y / 4, ResourceFormat::RG32Int, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess); } else { - mpBrickTexture = Texture::create2D(mpDevice, textureWidth, textureHeight, ResourceFormat::R8Snorm, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource); + mpBrickTexture = mpDevice->createTexture2D(textureWidth, textureHeight, ResourceFormat::R8Snorm, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource); mpBrickTexture->setName("SDFSBS::BrickTexture"); mBrickTextureDimensions = uint2(textureWidth, textureHeight); @@ -762,7 +762,7 @@ namespace Falcor { if (!mpSDFGridTextureModified || mpSDFGridTextureModified->getWidth() < gridWidthInValues) { - mpSDFGridTextureModified = Texture::create3D(mpDevice, gridWidthInValues, gridWidthInValues, gridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpSDFGridTextureModified = mpDevice->createTexture3D(gridWidthInValues, gridWidthInValues, gridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); mpSDFGridTextureModified->setName("SDFSBS::SDFGridTextureModified"); } @@ -836,13 +836,13 @@ namespace Falcor void SDFSBS::expandSDFGridTexture(RenderContext* pRenderContext, bool deleteScratchData, uint32_t oldGridWidthInValues, uint32_t gridWidthInValues) { - checkArgument(oldGridWidthInValues < gridWidthInValues, "Can only expand SDF grid texture if the old width is smaller than the new width."); + FALCOR_CHECK(oldGridWidthInValues < gridWidthInValues, "Can only expand SDF grid texture if the old width is smaller than the new width."); mResolutionScalingFactor = (float)gridWidthInValues / oldGridWidthInValues; if (!mpExpandSDFieldPass) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kExpandSDFieldShaderName).csEntry("main"); DefineList defines; @@ -855,7 +855,7 @@ namespace Falcor { if (!mpOldSDFGridTexture || mpOldSDFGridTexture->getWidth() != oldGridWidthInValues) { - mpOldSDFGridTexture = Texture::create3D(mpDevice, oldGridWidthInValues, oldGridWidthInValues, oldGridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpOldSDFGridTexture = mpDevice->createTexture3D(oldGridWidthInValues, oldGridWidthInValues, oldGridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); mpOldSDFGridTexture->setName("SDFSBS::OldSDFGridTexture"); } @@ -865,7 +865,7 @@ namespace Falcor // Create destination grid texture to write to. if (!mpSDFGridTexture || mpSDFGridTexture->getWidth() < gridWidthInValues) { - mpSDFGridTexture = Texture::create3D(mpDevice, gridWidthInValues, gridWidthInValues, gridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpSDFGridTexture = mpDevice->createTexture3D(gridWidthInValues, gridWidthInValues, gridWidthInValues, ResourceFormat::R8Snorm, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); mpSDFGridTexture->setName("SDFSBS::SDFGridTexture"); } @@ -895,7 +895,7 @@ namespace Falcor { if (!mpComputeRootIntervalSDFieldFromGridPass) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kComputeIntervalSDFieldFromGridShaderName).csEntry("rootGather"); // For brick widths smaller than 8 interval computation will be performed using group shared memory. @@ -911,7 +911,7 @@ namespace Falcor if (!mpComputeIntervalSDFieldFromGridPass) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kComputeIntervalSDFieldFromGridShaderName).csEntry("chunkGather"); DefineList defines; @@ -931,7 +931,7 @@ namespace Falcor auto& pTexture = mIntervalSDFieldMaps[i]; if (!pTexture || pTexture->getWidth() < width) { - pTexture = Texture::create3D(mpDevice, width, width, width, ResourceFormat::RG8Snorm, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + pTexture = mpDevice->createTexture3D(width, width, width, ResourceFormat::RG8Snorm, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); pTexture->setName("SDFSBS::IntervalValuesMaps[" + std::to_string(i) + "]"); } @@ -990,7 +990,7 @@ namespace Falcor void SDFSBS::createSDFGridTexture(RenderContext* pRenderContext, const std::vector& sdField) { - checkArgument(!sdField.empty(), "Cannot create SDF grid texture from empty values vector"); + FALCOR_CHECK(!sdField.empty(), "Cannot create SDF grid texture from empty values vector"); if (mpSDFGridTexture && mpSDFGridTexture->getWidth() == mGridWidth + 1) { @@ -998,7 +998,7 @@ namespace Falcor } else { - mpSDFGridTexture = Texture::create3D(mpDevice, mGridWidth + 1, mGridWidth + 1, mGridWidth + 1, ResourceFormat::R8Snorm, 1, sdField.data()); + mpSDFGridTexture = mpDevice->createTexture3D(mGridWidth + 1, mGridWidth + 1, mGridWidth + 1, ResourceFormat::R8Snorm, 1, sdField.data()); } mSDFieldUpdated = true; @@ -1011,12 +1011,12 @@ namespace Falcor { if (!mpCountStagingBuffer) { - mpCountStagingBuffer = Buffer::create(mpDevice, 4, ResourceBindFlags::None, Buffer::CpuAccess::Read); + mpCountStagingBuffer = mpDevice->createBuffer(4, ResourceBindFlags::None, MemoryType::ReadBack); } // Copy result to staging buffer. pRenderContext->copyBufferRegion(mpCountStagingBuffer.get(), 0, pBuffer.get(), 0, 4); - pRenderContext->flush(true); + pRenderContext->submit(true); // Read back final results. uint32_t finalResults = *reinterpret_cast(mpCountStagingBuffer->map(Buffer::MapType::Read)); @@ -1028,7 +1028,7 @@ namespace Falcor { if (!mpCompactifyChunks) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kCompactifyChunksShaderName).csEntry("main"); mpCompactifyChunks = ComputePass::create(mpDevice, desc); } diff --git a/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.h b/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.h index 9f91a6ee3..de7aff3a0 100644 --- a/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.h +++ b/Source/Falcor/Scene/SDFs/SparseBrickSet/SDFSBS.h @@ -64,7 +64,7 @@ namespace Falcor virtual const ref& getAABBBuffer() const override { return mpBrickAABBsBuffer; } virtual uint32_t getAABBCount() const override { return mBrickCount; } - virtual void setShaderData(const ShaderVar& var) const override; + virtual void bindShaderData(const ShaderVar& var) const override; virtual float getResolutionScalingFactor() const override { return mResolutionScalingFactor; }; virtual void resetResolutionScalingFactor() override { mResolutionScalingFactor = 1.0f; }; @@ -148,7 +148,7 @@ namespace Falcor ref mpSubChunkValidityBuffer; ref mpSubChunkCoordsBuffer; ref mpSubdivisionArgBuffer; - ref mpReadbackFence; + ref mpReadbackFence; // Scratch data used for building from the SD Field and primitives. ref mpOldSDFGridTexture; diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp index f60046aec..1d18c679b 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp +++ b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.cpp @@ -64,7 +64,7 @@ namespace Falcor SharedData(ref pDevice) { RtAABB unitAABB { float3(-0.5f), float3(0.5f) }; - pUnitAABBBuffer = Buffer::create(pDevice, sizeof(RtAABB), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, &unitAABB); + pUnitAABBBuffer = pDevice->createBuffer(sizeof(RtAABB), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, &unitAABB); } }; @@ -74,7 +74,7 @@ namespace Falcor : SDFGrid(pDevice) { #if !FALCOR_NVAPI_AVAILABLE - throw RuntimeError("SDFSVO requires NVAPI. See installation instructions in README."); + FALCOR_THROW("SDFSVO requires NVAPI. See installation instructions in README."); #endif mpSharedData = sSharedCache.acquire(mpDevice.get(), [this]() { return std::make_shared(mpDevice); }); @@ -94,7 +94,7 @@ namespace Falcor { if (!mPrimitives.empty()) { - throw RuntimeError("An SDFSVO instance cannot be created from primitives!"); + FALCOR_THROW("An SDFSVO instance cannot be created from primitives!"); } // Create source grid texture to read from. @@ -104,23 +104,23 @@ namespace Falcor } else { - mpSDFGridTexture = Texture::create3D(mpDevice, mGridWidth + 1, mGridWidth + 1, mGridWidth + 1, ResourceFormat::R8Snorm, 1, mValues.data()); + mpSDFGridTexture = mpDevice->createTexture3D(mGridWidth + 1, mGridWidth + 1, mGridWidth + 1, ResourceFormat::R8Snorm, 1, mValues.data()); } if (!mpCountSurfaceVoxelsPass) { - Program::Desc desc; - desc.addShaderLibrary(kSDFCountSurfaceVoxelsShaderName).csEntry("main").setShaderModel("6_5"); + ProgramDesc desc; + desc.addShaderLibrary(kSDFCountSurfaceVoxelsShaderName).csEntry("main"); mpCountSurfaceVoxelsPass = ComputePass::create(mpDevice, desc); } if (!mpSurfaceVoxelCounter) { - mpReadbackFence = GpuFence::create(mpDevice); + mpReadbackFence = mpDevice->createFence(); static const uint32_t zero = 0; - mpSurfaceVoxelCounter = Buffer::create(mpDevice, sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, &zero); - mpSurfaceVoxelCounterStagingBuffer = Buffer::create(mpDevice, sizeof(uint32_t), Resource::BindFlags::None, Buffer::CpuAccess::Read); + mpSurfaceVoxelCounter = mpDevice->createBuffer(sizeof(uint32_t), ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, &zero); + mpSurfaceVoxelCounterStagingBuffer = mpDevice->createBuffer(sizeof(uint32_t), ResourceBindFlags::None, MemoryType::ReadBack); } else { @@ -139,11 +139,11 @@ namespace Falcor // Copy surface containing voxels count to staging buffer. pRenderContext->copyResource(mpSurfaceVoxelCounterStagingBuffer.get(), mpSurfaceVoxelCounter.get()); - pRenderContext->flush(false); - mpReadbackFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + pRenderContext->submit(false); + pRenderContext->signal(mpReadbackFence.get()); // Copy surface containing voxels count from staging buffer to CPU. - mpReadbackFence->syncCpu(); + mpReadbackFence->wait(); const uint32_t* pSurfaceContainingVoxels = reinterpret_cast(mpSurfaceVoxelCounterStagingBuffer->map(Buffer::MapType::Read)); std::memcpy(&finestLevelVoxelCount, pSurfaceContainingVoxels, sizeof(uint32_t)); mpSurfaceVoxelCounterStagingBuffer->unmap(); @@ -164,8 +164,8 @@ namespace Falcor if (hashTableCapacity < worstCaseTotalVoxels) { hashTableCapacity = ceilPow2(worstCaseTotalVoxels); - mpHashTableBuffer = Buffer::create(mpDevice, hashTableCapacity * sizeof(SDFSVOHashTableVoxel)); - mpLocationCodesBuffer = Buffer::create(mpDevice, hashTableCapacity * sizeof(uint64_t)); + mpHashTableBuffer = mpDevice->createBuffer(hashTableCapacity * sizeof(SDFSVOHashTableVoxel)); + mpLocationCodesBuffer = mpDevice->createBuffer(hashTableCapacity * sizeof(uint64_t)); } else { @@ -176,8 +176,8 @@ namespace Falcor // Create the building pass for the bottom level. if (!mpBuildFinestLevelFromDistanceTexturePass) { - Program::Desc desc; - desc.addShaderLibrary(kSDFSVOBuildLevelFromTextureShaderName).csEntry("main").setShaderModel("6_5"); + ProgramDesc desc; + desc.addShaderLibrary(kSDFSVOBuildLevelFromTextureShaderName).csEntry("main"); mpBuildFinestLevelFromDistanceTexturePass = ComputePass::create(mpDevice, desc, DefineList({ {"FINEST_LEVEL_PASS", "1"} })); } @@ -199,8 +199,8 @@ namespace Falcor // Create the building pass for the other levels. if (!mpBuildLevelFromDistanceTexturePass) { - Program::Desc desc; - desc.addShaderLibrary(kSDFSVOBuildLevelFromTextureShaderName).csEntry("main").setShaderModel("6_5"); + ProgramDesc desc; + desc.addShaderLibrary(kSDFSVOBuildLevelFromTextureShaderName).csEntry("main"); mpBuildLevelFromDistanceTexturePass = ComputePass::create(mpDevice, desc, DefineList({ {"FINEST_LEVEL_PASS", "0"} })); } @@ -208,8 +208,8 @@ namespace Falcor std::vector voxelCountsPerLevel(mLevelCount, 0); voxelCountsPerLevel.back() = finestLevelVoxelCount; { - mpVoxelCountPerLevelBuffer = Buffer::create(mpDevice, sizeof(uint32_t) * (mLevelCount - 1), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, voxelCountsPerLevel.data()); - mpVoxelCountPerLevelStagingBuffer = Buffer::create(mpDevice, sizeof(uint32_t) * (mLevelCount - 1), Resource::BindFlags::None, Buffer::CpuAccess::Read); + mpVoxelCountPerLevelBuffer = mpDevice->createBuffer(sizeof(uint32_t) * (mLevelCount - 1), ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, voxelCountsPerLevel.data()); + mpVoxelCountPerLevelStagingBuffer = mpDevice->createBuffer(sizeof(uint32_t) * (mLevelCount - 1), ResourceBindFlags::None, MemoryType::ReadBack); } // Create voxels for all the other levels, a voxel is only created if a child voxel has been created for that voxel. @@ -229,7 +229,7 @@ namespace Falcor { uint32_t levelWidth = 1 << l; - cbVar["gLevel"] = l; + cbVar["gLevel"] = (uint32_t)l; cbVar["gLevelWidth"] = levelWidth; mpBuildLevelFromDistanceTexturePass->execute(pRenderContext, levelWidth, levelWidth, levelWidth); } @@ -237,8 +237,8 @@ namespace Falcor // Copy child count to staging buffer. pRenderContext->copyResource(mpVoxelCountPerLevelStagingBuffer.get(), mpVoxelCountPerLevelBuffer.get()); - pRenderContext->flush(false); - mpReadbackFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + pRenderContext->submit(false); + pRenderContext->signal(mpReadbackFence.get()); // Sort the location codes. { @@ -260,8 +260,8 @@ namespace Falcor definesList.add("BIG_FLIP", std::to_string(kSDFSVOLocationCodeBigFlip)); definesList.add("BIG_DISPERSE", std::to_string(kSDFSVOLocationCodeBigDisperse)); - Program::Desc desc; - desc.addShaderLibrary(kSDFSVOLocationCodeSorterShaderName).csEntry("main").setShaderModel("6_5"); + ProgramDesc desc; + desc.addShaderLibrary(kSDFSVOLocationCodeSorterShaderName).csEntry("main"); mpSortLocationCodesPass = ComputePass::create(mpDevice, desc, definesList); } else @@ -309,7 +309,7 @@ namespace Falcor } // Copy child count from staging buffer to CPU. - mpReadbackFence->syncCpu(); + mpReadbackFence->wait(); const uint32_t* pVoxelCountPerLevel = reinterpret_cast(mpVoxelCountPerLevelStagingBuffer->map(Buffer::MapType::Read)); std::memcpy(voxelCountsPerLevel.data(), pVoxelCountPerLevel, sizeof(uint32_t) * (mLevelCount - 1)); mpVoxelCountPerLevelStagingBuffer->unmap(); @@ -326,8 +326,8 @@ namespace Falcor { if (!mpWriteSVOOffsetsPass) { - Program::Desc desc; - desc.addShaderLibrary(kSDFSVOWriteSVOOffsetsShaderName).csEntry("main").setShaderModel("6_5"); + ProgramDesc desc; + desc.addShaderLibrary(kSDFSVOWriteSVOOffsetsShaderName).csEntry("main"); mpWriteSVOOffsetsPass = ComputePass::create(mpDevice, desc); } @@ -349,13 +349,13 @@ namespace Falcor uint32_t requiredSVOSize = mSVOElementCount * sizeof(SDFSVOVoxel); if (!mpSVOBuffer || mpSVOBuffer->getSize() < requiredSVOSize) { - mpSVOBuffer = Buffer::create(mpDevice, requiredSVOSize); + mpSVOBuffer = mpDevice->createBuffer(requiredSVOSize); } if (!mpBuildOctreePass) { - Program::Desc desc; - desc.addShaderLibrary(kSDFSVOBuildOctreeShaderName).csEntry("main").setShaderModel("6_5"); + ProgramDesc desc; + desc.addShaderLibrary(kSDFSVOBuildOctreeShaderName).csEntry("main"); mpBuildOctreePass = ComputePass::create(mpDevice, desc); } @@ -397,9 +397,9 @@ namespace Falcor return mpSharedData->pUnitAABBBuffer; } - void SDFSVO::setShaderData(const ShaderVar& var) const + void SDFSVO::bindShaderData(const ShaderVar& var) const { - if (!mpSVOBuffer) throw RuntimeError("SDFSVO::setShaderData() can't be called before calling SDFSVO::createResources()!"); + if (!mpSVOBuffer) FALCOR_THROW("SDFSVO::bindShaderData() can't be called before calling SDFSVO::createResources()!"); var["svo"] = mpSVOBuffer; var["levelCount"] = mLevelCount; diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.h b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.h index 5dbf9bdc2..b21308431 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.h +++ b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.h @@ -58,7 +58,7 @@ namespace Falcor virtual const ref& getAABBBuffer() const override; virtual uint32_t getAABBCount() const override { return 1; } - virtual void setShaderData(const ShaderVar& var) const override; + virtual void bindShaderData(const ShaderVar& var) const override; protected: virtual void setValuesInternal(const std::vector& cornerValues) override; @@ -93,6 +93,6 @@ namespace Falcor ref mpVoxelCountPerLevelStagingBuffer; ref mpHashTableBuffer; ref mpLocationCodesBuffer; - ref mpReadbackFence; + ref mpReadbackFence; }; } diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.slang b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.slang index b593dfa60..b936ebd1c 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.slang +++ b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVO.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVOBuildLevelFromTexture.cs.slang b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVOBuildLevelFromTexture.cs.slang index 5fbf5c50a..6f71b8280 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVOBuildLevelFromTexture.cs.slang +++ b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVOBuildLevelFromTexture.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -61,7 +61,7 @@ void main(uint3 dispatchThreadID : SV_DispatchThreadID, uint3 groupThreadID : SV #endif const uint3 voxelCoords = dispatchThreadID; - + #if FINEST_LEVEL_PASS // Load the 8 corner values of the voxel. float4 values0xx; @@ -90,7 +90,7 @@ void main(uint3 dispatchThreadID : SV_DispatchThreadID, uint3 groupThreadID : SV gLocationCodes.Store2(slot * 8, locationCode); } #else - const uint2 locationCode = SDFVoxelCommon::encodeLocation(voxelCoords, gLevel); + uint2 locationCode = SDFVoxelCommon::encodeLocation(voxelCoords, gLevel); // Set a valid bit in the 2 unused bits of location code. locationCode.y |= (1 << 31); diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVOHashTable.slang b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVOHashTable.slang index 51c28c90a..2225100a1 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVOHashTable.slang +++ b/Source/Falcor/Scene/SDFs/SparseVoxelOctree/SDFSVOHashTable.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.cpp b/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.cpp index a9112097b..06871842f 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.cpp +++ b/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.cpp @@ -54,7 +54,7 @@ namespace Falcor { if (!mPrimitives.empty()) { - throw RuntimeError("An SDFSVS instance cannot be created from primitives!"); + FALCOR_THROW("An SDFSVS instance cannot be created from primitives!"); } if (mpSDFGridTexture && mpSDFGridTexture->getWidth() == mGridWidth + 1) @@ -63,32 +63,26 @@ namespace Falcor } else { - mpSDFGridTexture = Texture::create3D(mpDevice, mGridWidth + 1, mGridWidth + 1, mGridWidth + 1, ResourceFormat::R8Snorm, 1, mValues.data()); + mpSDFGridTexture = mpDevice->createTexture3D(mGridWidth + 1, mGridWidth + 1, mGridWidth + 1, ResourceFormat::R8Snorm, 1, mValues.data()); } if (!mpCountSurfaceVoxelsPass) { - Program::Desc desc; - desc.addShaderLibrary(kSDFCountSurfaceVoxelsShaderName).csEntry("main").setShaderModel("6_5"); + ProgramDesc desc; + desc.addShaderLibrary(kSDFCountSurfaceVoxelsShaderName).csEntry("main"); mpCountSurfaceVoxelsPass = ComputePass::create(mpDevice, desc); } if (!mpSurfaceVoxelCounter) { static uint32_t zero = 0; - mpSurfaceVoxelCounter = Buffer::create(mpDevice, sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, &zero); - mpSurfaceVoxelCounterStagingBuffer = Buffer::create(mpDevice, sizeof(uint32_t), Resource::BindFlags::None, Buffer::CpuAccess::Read); + mpSurfaceVoxelCounter = mpDevice->createBuffer(sizeof(uint32_t), ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, &zero); } else { pRenderContext->clearUAV(mpSurfaceVoxelCounter->getUAV().get(), uint4(0)); } - if (!mpReadbackFence) - { - mpReadbackFence = GpuFence::create(mpDevice); - } - // Count the number of surface containing voxels in the texture. { auto var = mpCountSurfaceVoxelsPass->getRootVar(); @@ -96,17 +90,7 @@ namespace Falcor var["gSDFGrid"] = mpSDFGridTexture; var["gTotalVoxelCount"] = mpSurfaceVoxelCounter; mpCountSurfaceVoxelsPass->execute(pRenderContext, mGridWidth, mGridWidth, mGridWidth); - - // Copy surface containing voxels count to staging buffer. - pRenderContext->copyResource(mpSurfaceVoxelCounterStagingBuffer.get(), mpSurfaceVoxelCounter.get()); - pRenderContext->flush(false); - mpReadbackFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); - - // Copy surface containing voxels count from staging buffer to CPU. - mpReadbackFence->syncCpu(); - const uint32_t* pSurfaceContainingVoxels = reinterpret_cast(mpSurfaceVoxelCounterStagingBuffer->map(Buffer::MapType::Read)); - std::memcpy(&mVoxelCount, pSurfaceContainingVoxels, sizeof(uint32_t)); - mpSurfaceVoxelCounterStagingBuffer->unmap(); + mVoxelCount = mpSurfaceVoxelCounter->getElement(0); } @@ -114,12 +98,12 @@ namespace Falcor { if (!mpVoxelAABBBuffer || mpVoxelAABBBuffer->getElementCount() < mVoxelCount) { - mpVoxelAABBBuffer = Buffer::createStructured(mpDevice, sizeof(AABB), mVoxelCount); + mpVoxelAABBBuffer = mpDevice->createStructuredBuffer(sizeof(AABB), mVoxelCount); } if (!mpVoxelBuffer || mpVoxelBuffer->getElementCount() < mVoxelCount) { - mpVoxelBuffer = Buffer::createStructured(mpDevice, sizeof(SDFSVSVoxel), mVoxelCount); + mpVoxelBuffer = mpDevice->createStructuredBuffer(sizeof(SDFSVSVoxel), mVoxelCount); } } @@ -127,8 +111,8 @@ namespace Falcor { if (!mpSDFSVSVoxelizerPass) { - Program::Desc desc; - desc.addShaderLibrary(kSDFSVSVoxelizerShaderName).csEntry("main").setShaderModel("6_5"); + ProgramDesc desc; + desc.addShaderLibrary(kSDFSVSVoxelizerShaderName).csEntry("main"); mpSDFSVSVoxelizerPass = ComputePass::create(mpDevice, desc); } @@ -147,19 +131,17 @@ namespace Falcor if (deleteScratchData) { - mpReadbackFence.reset(); mpCountSurfaceVoxelsPass.reset(); mpSurfaceVoxelCounter.reset(); - mpSurfaceVoxelCounterStagingBuffer.reset(); mpSDFGridTexture.reset(); } } - void SDFSVS::setShaderData(const ShaderVar& var) const + void SDFSVS::bindShaderData(const ShaderVar& var) const { if (!mpVoxelBuffer || !mpVoxelAABBBuffer) { - throw RuntimeError("SDFSVS::setShaderData() can't be called before calling SDFSVS::createResources()!"); + FALCOR_THROW("SDFSVS::bindShaderData() can't be called before calling SDFSVS::createResources()!"); } var["virtualGridWidth"] = mGridWidth; diff --git a/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.h b/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.h index bed14a0e0..c0b19be50 100644 --- a/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.h +++ b/Source/Falcor/Scene/SDFs/SparseVoxelSet/SDFSVS.h @@ -53,7 +53,7 @@ namespace Falcor virtual const ref& getAABBBuffer() const override { return mpVoxelAABBBuffer; } virtual uint32_t getAABBCount() const override { return mVoxelCount; } - virtual void setShaderData(const ShaderVar& var) const override; + virtual void bindShaderData(const ShaderVar& var) const override; protected: virtual void setValuesInternal(const std::vector& cornerValues) override; @@ -72,9 +72,7 @@ namespace Falcor ref mpSDFSVSVoxelizerPass; // Scratch data used for building. - ref mpReadbackFence; ref mpSurfaceVoxelCounter; - ref mpSurfaceVoxelCounterStagingBuffer; ref mpSDFGridTexture; }; } diff --git a/Source/Falcor/Scene/Scene.cpp b/Source/Falcor/Scene/Scene.cpp index 25d03e6a0..086aa2a39 100644 --- a/Source/Falcor/Scene/Scene.cpp +++ b/Source/Falcor/Scene/Scene.cpp @@ -29,6 +29,7 @@ #include "SceneDefines.slangh" #include "SceneBuilder.h" #include "Importer.h" +#include "Scene/Material/SerializedMaterialParams.h" #include "Curves/CurveConfig.h" #include "SDFs/SDFGrid.h" #include "SDFs/NormalizedDenseSDFGrid/NDSDFGrid.h" @@ -46,10 +47,13 @@ #include "Utils/Timing/Profiler.h" #include "Utils/UI/InputTypes.h" #include "Utils/Scripting/ScriptWriter.h" +#include "Utils/NumericRange.h" #include #include #include +#include +#include namespace Falcor { @@ -104,6 +108,21 @@ namespace Falcor const std::string kRemoveViewpoint = "removeViewpoint"; const std::string kSelectViewpoint = "selectViewpoint"; + const std::string kMeshIOShaderFilename = "Scene/MeshIO.cs.slang"; + const std::string kMeshLoaderRequiredBufferNames[] = + { + "triangleIndices", + "positions", + "texcrds", + }; + const std::string kMeshUpdaterRequiredBufferNames[] = + { + "positions", + "normals", + "tangents", + "texcrds", + }; + const Gui::DropdownList kUpDirectionList = { { (uint32_t)Scene::UpDirection::XPos, "X+" }, @@ -217,23 +236,23 @@ namespace Falcor { if (mesh.isDynamic()) { - if (mesh.prevVbOffset + mesh.vertexCount > sceneData.prevVertexCount) throw RuntimeError("Cached Mesh Animation: Invalid prevVbOffset"); + if (mesh.prevVbOffset + mesh.vertexCount > sceneData.prevVertexCount) FALCOR_THROW("Cached Mesh Animation: Invalid prevVbOffset"); } } for (const auto &mesh : sceneData.cachedMeshes) { - if (!mMeshDesc[mesh.meshID.get()].isAnimated()) throw RuntimeError("Cached Mesh Animation: Referenced mesh ID is not dynamic"); - if (mesh.timeSamples.size() != mesh.vertexData.size()) throw RuntimeError("Cached Mesh Animation: Time sample count mismatch."); + if (!mMeshDesc[mesh.meshID.get()].isAnimated()) FALCOR_THROW("Cached Mesh Animation: Referenced mesh ID is not dynamic"); + if (mesh.timeSamples.size() != mesh.vertexData.size()) FALCOR_THROW("Cached Mesh Animation: Time sample count mismatch."); for (const auto &vertices : mesh.vertexData) { - if (vertices.size() != mMeshDesc[mesh.meshID.get()].vertexCount) throw RuntimeError("Cached Mesh Animation: Vertex count mismatch."); + if (vertices.size() != mMeshDesc[mesh.meshID.get()].vertexCount) FALCOR_THROW("Cached Mesh Animation: Vertex count mismatch."); } } for (const auto& cache : sceneData.cachedCurves) { if (cache.tessellationMode != CurveTessellationMode::LinearSweptSphere) { - if (!mMeshDesc[cache.geometryID.get()].isAnimated()) throw RuntimeError("Cached Curve Animation: Referenced mesh ID is not dynamic"); + if (!mMeshDesc[cache.geometryID.get()].isAnimated()) FALCOR_THROW("Cached Curve Animation: Referenced mesh ID is not dynamic"); } } @@ -313,12 +332,12 @@ namespace Falcor return defines; } - Program::TypeConformanceList Scene::getTypeConformances() const + TypeConformanceList Scene::getTypeConformances() const { return mTypeConformances; } - Program::ShaderModuleList Scene::getShaderModules() const + ProgramDesc::ShaderModuleList Scene::getShaderModules() const { return mpMaterials->getShaderModules(); } @@ -327,22 +346,22 @@ namespace Falcor { if (!mpLightCollection) { - checkInvariant(mFinalized, "getLightCollection() called before scene is ready for use"); + FALCOR_CHECK(mFinalized, "getLightCollection() called before scene is ready for use"); mpLightCollection = LightCollection::create(mpDevice, pRenderContext, this); - mpLightCollection->setShaderData(mpSceneBlock->getRootVar()["lightCollection"]); + mpLightCollection->bindShaderData(mpSceneBlock->getRootVar()["lightCollection"]); mSceneStats.emissiveMemoryInBytes = mpLightCollection->getMemoryUsageInBytes(); } return mpLightCollection; } - void Scene::rasterize(RenderContext* pRenderContext, GraphicsState* pState, GraphicsVars* pVars, RasterizerState::CullMode cullMode) + void Scene::rasterize(RenderContext* pRenderContext, GraphicsState* pState, ProgramVars* pVars, RasterizerState::CullMode cullMode) { rasterize(pRenderContext, pState, pVars, mFrontClockwiseRS[cullMode], mFrontCounterClockwiseRS[cullMode]); } - void Scene::rasterize(RenderContext* pRenderContext, GraphicsState* pState, GraphicsVars* pVars, const ref& pRasterizerStateCW, const ref& pRasterizerStateCCW) + void Scene::rasterize(RenderContext* pRenderContext, GraphicsState* pState, ProgramVars* pVars, const ref& pRasterizerStateCW, const ref& pRasterizerStateCCW) { FALCOR_PROFILE(pRenderContext, "rasterizeScene"); @@ -384,7 +403,7 @@ namespace Falcor return 8; } - void Scene::raytrace(RenderContext* pRenderContext, RtProgram* pProgram, const ref& pVars, uint3 dispatchDims) + void Scene::raytrace(RenderContext* pRenderContext, Program* pProgram, const ref& pVars, uint3 dispatchDims) { FALCOR_PROFILE(pRenderContext, "raytraceScene"); @@ -413,14 +432,14 @@ namespace Falcor size_t ibSize = sizeof(uint32_t) * indexData.size(); if (ibSize > std::numeric_limits::max()) { - throw RuntimeError("Index buffer size exceeds 4GB"); + FALCOR_THROW("Index buffer size exceeds 4GB"); } ref pIB; if (ibSize > 0) { - ResourceBindFlags ibBindFlags = Resource::BindFlags::Index | ResourceBindFlags::ShaderResource; - pIB = Buffer::create(mpDevice, ibSize, ibBindFlags, Buffer::CpuAccess::None, indexData.data()); + ResourceBindFlags ibBindFlags = ResourceBindFlags::Index | ResourceBindFlags::ShaderResource; + pIB = mpDevice->createBuffer(ibSize, ibBindFlags, MemoryType::DeviceLocal, indexData.data()); } // Create the vertex data structured buffer. @@ -428,14 +447,14 @@ namespace Falcor size_t staticVbSize = sizeof(PackedStaticVertexData) * vertexCount; if (staticVbSize > std::numeric_limits::max()) { - throw RuntimeError("Vertex buffer size exceeds 4GB"); + FALCOR_THROW("Vertex buffer size exceeds 4GB"); } ref pStaticBuffer; if (vertexCount > 0) { ResourceBindFlags vbBindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::Vertex; - pStaticBuffer = Buffer::createStructured(mpDevice, sizeof(PackedStaticVertexData), (uint32_t)vertexCount, vbBindFlags, Buffer::CpuAccess::None, nullptr, false); + pStaticBuffer = mpDevice->createStructuredBuffer(sizeof(PackedStaticVertexData), (uint32_t)vertexCount, vbBindFlags, MemoryType::DeviceLocal, nullptr, false); } Vao::BufferVec pVBs(kVertexBufferCount); @@ -451,13 +470,13 @@ namespace Falcor FALCOR_ASSERT(drawCount <= (1 << 16)); std::vector drawIDs(drawCount); for (uint32_t i = 0; i < drawCount; i++) drawIDs[i] = i; - pDrawIDBuffer = Buffer::create(mpDevice, drawCount * sizeof(uint16_t), ResourceBindFlags::Vertex, Buffer::CpuAccess::None, drawIDs.data()); + pDrawIDBuffer = mpDevice->createBuffer(drawCount * sizeof(uint16_t), ResourceBindFlags::Vertex, MemoryType::DeviceLocal, drawIDs.data()); } else if (drawIDFormat == ResourceFormat::R32Uint) { std::vector drawIDs(drawCount); for (uint32_t i = 0; i < drawCount; i++) drawIDs[i] = i; - pDrawIDBuffer = Buffer::create(mpDevice, drawCount * sizeof(uint32_t), ResourceBindFlags::Vertex, Buffer::CpuAccess::None, drawIDs.data()); + pDrawIDBuffer = mpDevice->createBuffer(drawCount * sizeof(uint32_t), ResourceBindFlags::Vertex, MemoryType::DeviceLocal, drawIDs.data()); } else FALCOR_UNREACHABLE(); @@ -496,14 +515,14 @@ namespace Falcor size_t ibSize = sizeof(uint32_t) * indexData.size(); if (ibSize > std::numeric_limits::max()) { - throw RuntimeError("Curve index buffer size exceeds 4GB"); + FALCOR_THROW("Curve index buffer size exceeds 4GB"); } ref pIB = nullptr; if (ibSize > 0) { - ResourceBindFlags ibBindFlags = Resource::BindFlags::Index | ResourceBindFlags::ShaderResource; - pIB = Buffer::create(mpDevice, ibSize, ibBindFlags, Buffer::CpuAccess::None, indexData.data()); + ResourceBindFlags ibBindFlags = ResourceBindFlags::Index | ResourceBindFlags::ShaderResource; + pIB = mpDevice->createBuffer(ibSize, ibBindFlags, MemoryType::DeviceLocal, indexData.data()); } // Create the vertex data as structured buffers. @@ -511,12 +530,12 @@ namespace Falcor size_t staticVbSize = sizeof(StaticCurveVertexData) * vertexCount; if (staticVbSize > std::numeric_limits::max()) { - throw RuntimeError("Curve vertex buffer exceeds 4GB"); + FALCOR_THROW("Curve vertex buffer exceeds 4GB"); } ResourceBindFlags vbBindFlags = ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::Vertex; // Also upload the curve vertex data. - ref pStaticBuffer = Buffer::createStructured(mpDevice, sizeof(StaticCurveVertexData), (uint32_t)vertexCount, vbBindFlags, Buffer::CpuAccess::None, staticData.data(), false); + ref pStaticBuffer = mpDevice->createStructuredBuffer(sizeof(StaticCurveVertexData), (uint32_t)vertexCount, vbBindFlags, MemoryType::DeviceLocal, staticData.data(), false); // Curves do not need DrawIDBuffer. Vao::BufferVec pVBs(kVertexBufferCount - 1); @@ -540,9 +559,11 @@ namespace Falcor void Scene::createMeshUVTiles(const std::vector& meshDescs, const std::vector& indexData, const std::vector& staticData) { const uint8_t* indexData8 = reinterpret_cast(indexData.data()); + mMeshUVTiles.resize(meshDescs.size()); - for (const MeshDesc& desc : meshDescs) + auto processMeshTile = [&](size_t meshIndex) { + const MeshDesc& desc = meshDescs[meshIndex]; // This tile captures any triangles that span more than one unit square, e.g., for tiled textures Rectangle largeTriangleTile; std::map tiles; @@ -605,17 +626,20 @@ namespace Falcor tile->include(vertices[2].texCrd); } - mMeshUVTiles.push_back(std::vector()); + std::vector& result = mMeshUVTiles[meshIndex]; for (auto& tile : tiles) { if (largeTriangleTile.contains(tile.second)) continue; - mMeshUVTiles.back().push_back(tile.second); + result.push_back(tile.second); } if (largeTriangleTile.valid()) - mMeshUVTiles.back().push_back(largeTriangleTile); - } + result.push_back(largeTriangleTile); + }; + + auto range = NumericRange(0, meshDescs.size()); + std::for_each(std::execution::par_unseq, range.begin(), range.end(), processMeshTile); } void Scene::setSDFGridConfig() @@ -630,7 +654,7 @@ namespace Falcor } else if (mSDFGridConfig.implementation != pSDFGrid->getType()) { - throw RuntimeError("All SDF grids in the same scene must currently be of the same type."); + FALCOR_THROW("All SDF grids in the same scene must currently be of the same type."); } } @@ -732,7 +756,7 @@ namespace Falcor void Scene::createParameterBlock() { // Create parameter block. - ref pProgram = ComputeProgram::createFromFile(mpDevice, "Scene/SceneBlock.slang", "main", getSceneDefines()); + ref pProgram = Program::createCompute(mpDevice, "Scene/SceneBlock.slang", "main", getSceneDefines()); ref pReflection = pProgram->getReflector()->getParameterBlock(kParameterBlockName); FALCOR_ASSERT(pReflection); @@ -743,66 +767,71 @@ namespace Falcor if (!mGeometryInstanceData.empty() && (!mpGeometryInstancesBuffer || mpGeometryInstancesBuffer->getElementCount() < mGeometryInstanceData.size())) { - mpGeometryInstancesBuffer = Buffer::createStructured(mpDevice, var[kGeometryInstanceBufferName], (uint32_t)mGeometryInstanceData.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpGeometryInstancesBuffer = mpDevice->createStructuredBuffer(var[kGeometryInstanceBufferName], (uint32_t)mGeometryInstanceData.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpGeometryInstancesBuffer->setName("Scene::mpGeometryInstancesBuffer"); } if (!mMeshDesc.empty() && (!mpMeshesBuffer || mpMeshesBuffer->getElementCount() < mMeshDesc.size())) { - mpMeshesBuffer = Buffer::createStructured(mpDevice, var[kMeshBufferName], (uint32_t)mMeshDesc.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpMeshesBuffer = mpDevice->createStructuredBuffer(var[kMeshBufferName], (uint32_t)mMeshDesc.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpMeshesBuffer->setName("Scene::mpMeshesBuffer"); } if (!mCurveDesc.empty() && (!mpCurvesBuffer || mpCurvesBuffer->getElementCount() < mCurveDesc.size())) { - mpCurvesBuffer = Buffer::createStructured(mpDevice, var[kCurveBufferName], (uint32_t)mCurveDesc.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpCurvesBuffer = mpDevice->createStructuredBuffer(var[kCurveBufferName], (uint32_t)mCurveDesc.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpCurvesBuffer->setName("Scene::mpCurvesBuffer"); } if (!mLights.empty() && (!mpLightsBuffer || mpLightsBuffer->getElementCount() < mLights.size())) { - mpLightsBuffer = Buffer::createStructured(mpDevice, var[kLightsBufferName], (uint32_t)mLights.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpLightsBuffer = mpDevice->createStructuredBuffer(var[kLightsBufferName], (uint32_t)mLights.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpLightsBuffer->setName("Scene::mpLightsBuffer"); } if (!mGridVolumes.empty() && (!mpGridVolumesBuffer || mpGridVolumesBuffer->getElementCount() < mGridVolumes.size())) { - mpGridVolumesBuffer = Buffer::createStructured(mpDevice, var[kGridVolumesBufferName], (uint32_t)mGridVolumes.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + mpGridVolumesBuffer = mpDevice->createStructuredBuffer(var[kGridVolumesBufferName], (uint32_t)mGridVolumes.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); mpGridVolumesBuffer->setName("Scene::mpGridVolumesBuffer"); } - - // Bind resources to parameter block. - var[kGeometryInstanceBufferName] = mpGeometryInstancesBuffer; - var[kMeshBufferName] = mpMeshesBuffer; - var[kCurveBufferName] = mpCurvesBuffer; - var[kLightsBufferName] = mpLightsBuffer; - var[kGridVolumesBufferName] = mpGridVolumesBuffer; - - // Bind materials parameter block. - var[kMaterialsBlockName].setParameterBlock(mpMaterials->getParameterBlock()); } - void Scene::uploadResources() + void Scene::bindParameterBlock() { - FALCOR_ASSERT(mpAnimationController); - + // This function binds all scene data to the scene parameter block. + // It is called after the parameter block has been created to bind everything. auto var = mpSceneBlock->getRootVar(); - // Upload geometry. + bindGeometry(); + bindProceduralPrimitives(); + bindGridVolumes(); + bindSDFGrids(); + bindLights(); + bindSelectedCamera(); + + mpMaterials->bindShaderData(var[kMaterialsBlockName]); + } + + void Scene::uploadGeometry() + { if (!mMeshDesc.empty()) mpMeshesBuffer->setBlob(mMeshDesc.data(), 0, sizeof(MeshDesc) * mMeshDesc.size()); if (!mCurveDesc.empty()) mpCurvesBuffer->setBlob(mCurveDesc.data(), 0, sizeof(CurveDesc) * mCurveDesc.size()); + } + + void Scene::bindGeometry() + { + auto var = mpSceneBlock->getRootVar(); - auto sdfGridsVar = var[kSDFGridsArrayName]; + var[kMeshBufferName] = mpMeshesBuffer; + var[kCurveBufferName] = mpCurvesBuffer; + var[kGeometryInstanceBufferName] = mpGeometryInstancesBuffer; - for (uint32_t i = 0; i < mSDFGrids.size(); i++) - { - const ref& pGrid = mSDFGrids[i]; - pGrid->setShaderData(sdfGridsVar[i]); - } + FALCOR_ASSERT(mpAnimationController); + mpAnimationController->bindBuffers(); if (mpMeshVao != nullptr) { @@ -819,9 +848,21 @@ namespace Falcor } } - void Scene::uploadSelectedCamera() + void Scene::bindSDFGrids() + { + auto sdfGridsVar = mpSceneBlock->getRootVar()[kSDFGridsArrayName]; + + for (uint32_t i = 0; i < mSDFGrids.size(); i++) + { + const ref& pGrid = mSDFGrids[i]; + pGrid->bindShaderData(sdfGridsVar[i]); + } + } + + void Scene::bindSelectedCamera() { - getCamera()->setShaderData(mpSceneBlock->getRootVar()[kCamera]); + if (!mCameras.empty()) + getCamera()->bindShaderData(mpSceneBlock->getRootVar()[kCamera]); } void Scene::updateBounds() @@ -926,7 +967,7 @@ namespace Falcor if (totalAABBCount > std::numeric_limits::max()) { - throw RuntimeError("Procedural primitive count exceeds the maximum"); + FALCOR_THROW("Procedural primitive count exceeds the maximum"); } // If there are no procedural primitives, clear the CPU buffer and return. @@ -995,7 +1036,7 @@ namespace Falcor // Requires unordered access and will be in Non-Pixel Shader Resource state. if (mpRtAABBBuffer == nullptr || mpRtAABBBuffer->getElementCount() < (uint32_t)mRtAABBRaw.size()) { - mpRtAABBBuffer = Buffer::createStructured(mpDevice, sizeof(RtAABB), (uint32_t)mRtAABBRaw.size(), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, mRtAABBRaw.data(), false); + mpRtAABBBuffer = mpDevice->createStructuredBuffer(sizeof(RtAABB), (uint32_t)mRtAABBRaw.size(), ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, mRtAABBRaw.data(), false); mpRtAABBBuffer->setName("Scene::mpRtAABBBuffer"); // Bind the new buffer to the scene. @@ -1051,10 +1092,10 @@ namespace Falcor mDisplacement.updateTasks.push_back(task); } - mDisplacement.pAABBBuffer = Buffer::createStructured(mpDevice, sizeof(RtAABB), AABBOffset, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + mDisplacement.pAABBBuffer = mpDevice->createStructuredBuffer(sizeof(RtAABB), AABBOffset, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); FALCOR_ASSERT(mDisplacement.updateTasks.size() < std::numeric_limits::max()); - mDisplacement.pUpdateTasksBuffer = Buffer::createStructured(mpDevice, (uint32_t)sizeof(DisplacementUpdateTask), (uint32_t)mDisplacement.updateTasks.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, mDisplacement.updateTasks.data()); + mDisplacement.pUpdateTasksBuffer = mpDevice->createStructuredBuffer((uint32_t)sizeof(DisplacementUpdateTask), (uint32_t)mDisplacement.updateTasks.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, mDisplacement.updateTasks.data()); } FALCOR_ASSERT(!mDisplacement.updateTasks.empty()); @@ -1103,7 +1144,7 @@ namespace Falcor { ref& pSDFGrid = mSDFGrids[sdfGridID]; if (pSDFGrid->mpDevice != mpDevice) - throw RuntimeError("SDFGrid '{}' was created with a different device than the Scene", pSDFGrid->getName()); + FALCOR_THROW("SDFGrid '{}' was created with a different device than the Scene", pSDFGrid->getName()); SDFGrid::UpdateFlags sdfGridUpdateFlags = pSDFGrid->update(pRenderContext); if (is_set(sdfGridUpdateFlags, SDFGrid::UpdateFlags::AABBsChanged)) @@ -1119,7 +1160,7 @@ namespace Falcor if (is_set(sdfGridUpdateFlags, SDFGrid::UpdateFlags::BuffersReallocated)) { updateGeometryStats(); - pSDFGrid->setShaderData(sdfGridsVar[sdfGridID]); + pSDFGrid->bindShaderData(sdfGridsVar[sdfGridID]); updateFlags |= Scene::UpdateFlags::SDFGeometryChanged; } } @@ -1143,7 +1184,7 @@ namespace Falcor { if (mpCustomPrimitivesBuffer == nullptr || mpCustomPrimitivesBuffer->getElementCount() < (uint32_t)mCustomPrimitiveDesc.size()) { - mpCustomPrimitivesBuffer = Buffer::createStructured(mpDevice, var[kCustomPrimitiveBufferName], (uint32_t)mCustomPrimitiveDesc.size(), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, mCustomPrimitiveDesc.data(), false); + mpCustomPrimitivesBuffer = mpDevice->createStructuredBuffer(var[kCustomPrimitiveBufferName], (uint32_t)mCustomPrimitiveDesc.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, mCustomPrimitiveDesc.data(), false); mpCustomPrimitivesBuffer->setName("Scene::mpCustomPrimitivesBuffer"); // Bind the buffer to the scene. @@ -1172,6 +1213,21 @@ namespace Falcor return flags; } + void Scene::bindProceduralPrimitives() + { + auto var = mpSceneBlock->getRootVar(); + + uint32_t customPrimitiveInstanceOffset = getGeometryInstanceCount(); + uint32_t customPrimitiveInstanceCount = getCustomPrimitiveCount(); + + var["customPrimitiveInstanceOffset"] = customPrimitiveInstanceOffset; + var["customPrimitiveInstanceCount"] = customPrimitiveInstanceCount; + var["customPrimitiveAABBOffset"] = mCustomPrimitiveAABBOffset; + + var[kCustomPrimitiveBufferName] = mpCustomPrimitivesBuffer; + var[kProceduralPrimAABBBufferName] = mpRtAABBBuffer; + } + void Scene::updateGeometryTypes() { mGeometryTypes = GeometryTypeFlags(0); @@ -1187,6 +1243,12 @@ namespace Falcor { RenderContext* pRenderContext = mpDevice->getRenderContext(); + // DEMO21: Setup light profile. + if (mpLightProfile) + { + mpLightProfile->bake(mpDevice->getRenderContext()); + } + // Perform setup that affects the scene defines. initSDFGrids(); mHitInfo.init(*this, mUseCompressedHitInfo); @@ -1204,19 +1266,15 @@ namespace Falcor // Prepare and upload resources. // The order of these calls is important as there are dependencies between them. + // First we bind current data to the Scene block to be able to perform the updates below. + // Last we bind the final updated data after all initialization is complete. createParameterBlock(); // Requires scene defines + bindParameterBlock(); // Bind current data. mpAnimationController->animate(pRenderContext, 0); // Requires Scene block to exist updateGeometry(pRenderContext, true); // Requires scene defines updateGeometryInstances(true); - // DEMO21: Setup light profile. - if (mpLightProfile) - { - mpLightProfile->bake(mpDevice->getRenderContext()); - mpLightProfile->setShaderData(mpSceneBlock->getRootVar()[kLightProfile]); - } - updateBounds(); createDrawList(); if (mCameras.size() == 0) @@ -1227,12 +1285,12 @@ namespace Falcor } setCameraController(mCamCtrlType); initializeCameras(); - uploadSelectedCamera(); addViewpoint(); updateLights(true); updateGridVolumes(true); updateEnvMap(true); - uploadResources(); // Upload data after initialization is complete + uploadGeometry(); + bindParameterBlock(); // Bind final data after initialization is complete. // Update stats and UI data. updateGeometryStats(); @@ -1243,7 +1301,7 @@ namespace Falcor // Validate assumption that scene defines didn't change. updateSceneDefines(); - checkInvariant(mSceneDefines == mPrevSceneDefines, "Scene defines changed unexpectedly"); + FALCOR_CHECK(mSceneDefines == mPrevSceneDefines, "Scene defines changed unexpectedly"); mFinalized = true; } @@ -1441,7 +1499,6 @@ namespace Falcor s.tlasMemoryInBytes += tlas.pTlasBuffer->getSize(); s.tlasCount++; } - if (tlas.pInstanceDescs) s.tlasScratchMemoryInBytes += tlas.pInstanceDescs->getSize(); } if (mpTlasScratch) s.tlasScratchMemoryInBytes += mpTlasScratch->getSize(); } @@ -1542,7 +1599,7 @@ namespace Falcor auto cameraChanges = camera->beginFrame(); if (mCameraSwitched || cameraChanges != Camera::Changes::None) { - uploadSelectedCamera(); + bindSelectedCamera(); if (is_set(cameraChanges, Camera::Changes::Movement)) flags |= UpdateFlags::CameraMoved; if ((cameraChanges & (~Camera::Changes::Movement)) != Camera::Changes::None) flags |= UpdateFlags::CameraPropertiesChanged; if (mCameraSwitched) flags |= UpdateFlags::CameraSwitched; @@ -1605,6 +1662,21 @@ namespace Falcor return flags; } + void Scene::bindLights() + { + auto var = mpSceneBlock->getRootVar(); + + var["lightCount"] = (uint32_t)mActiveLights.size(); + var[kLightsBufferName] = mpLightsBuffer; + + if (mpLightCollection) + mpLightCollection->bindShaderData(var["lightCollection"]); + if (mpLightProfile) + mpLightProfile->bindShaderData(var[kLightProfile]); + if (mpEnvMap) + mpEnvMap->bindShaderData(var[kEnvMap]); + } + Scene::UpdateFlags Scene::updateGridVolumes(bool forceUpdate) { GridVolume::UpdateFlags combinedUpdates = GridVolume::UpdateFlags::None; @@ -1613,7 +1685,7 @@ namespace Falcor for (const auto& pGridVolume : mGridVolumes) { if (pGridVolume->mpDevice != mpDevice) - throw RuntimeError("GridVolume '{}' was created with a different device than the Scene.", pGridVolume->getName()); + FALCOR_THROW("GridVolume '{}' was created with a different device than the Scene.", pGridVolume->getName()); updateAnimatable(*pGridVolume, *mpAnimationController, forceUpdate); combinedUpdates |= pGridVolume->getUpdates(); } @@ -1624,11 +1696,7 @@ namespace Falcor // Upload grids. if (forceUpdate) { - auto var = mpSceneBlock->getRootVar()["grids"]; - for (size_t i = 0; i < mGrids.size(); ++i) - { - mGrids[i]->setShaderData(var[i]); - } + bindGridVolumes(); } // Upload volumes and clear updates. @@ -1654,8 +1722,6 @@ namespace Falcor volumeIndex++; } - mpSceneBlock->getRootVar()["gridVolumeCount"] = (uint32_t)mGridVolumes.size(); - UpdateFlags flags = UpdateFlags::None; if (is_set(combinedUpdates, GridVolume::UpdateFlags::TransformChanged)) flags |= UpdateFlags::GridVolumesMoved; if (is_set(combinedUpdates, GridVolume::UpdateFlags::PropertiesChanged)) flags |= UpdateFlags::GridVolumePropertiesChanged; @@ -1665,6 +1731,19 @@ namespace Falcor return flags; } + void Scene::bindGridVolumes() + { + auto var = mpSceneBlock->getRootVar(); + var["gridVolumeCount"] = (uint32_t)mGridVolumes.size(); + var[kGridVolumesBufferName] = mpGridVolumesBuffer; + + auto gridsVar = var["grids"]; + for (size_t i = 0; i < mGrids.size(); ++i) + { + mGrids[i]->bindShaderData(gridsVar[i]); + } + } + Scene::UpdateFlags Scene::updateEnvMap(bool forceUpdate) { UpdateFlags flags = UpdateFlags::None; @@ -1672,12 +1751,12 @@ namespace Falcor if (mpEnvMap) { if (mpEnvMap->mpDevice != mpDevice) - throw RuntimeError("EnvMap was created with a different device than the Scene."); + FALCOR_THROW("EnvMap was created with a different device than the Scene."); auto envMapChanges = mpEnvMap->beginFrame(); if (envMapChanges != EnvMap::Changes::None || mEnvMapChanged || forceUpdate) { if (envMapChanges != EnvMap::Changes::None) flags |= UpdateFlags::EnvMapPropertiesChanged; - mpEnvMap->setShaderData(mpSceneBlock->getRootVar()[kEnvMap]); + mpEnvMap->bindShaderData(mpSceneBlock->getRootVar()[kEnvMap]); } } mSceneStats.envMapMemoryInBytes = mpEnvMap ? mpEnvMap->getMemoryUsageInBytes() : 0; @@ -1705,7 +1784,7 @@ namespace Falcor // Bind materials parameter block to scene. if (mpSceneBlock) { - mpSceneBlock->setParameterBlock(kMaterialsBlockName, mpMaterials->getParameterBlock()); + mpMaterials->bindShaderData(mpSceneBlock->getRootVar()[kMaterialsBlockName]); } // If displacement parameters have changed, we need to trigger displacement update. @@ -1714,6 +1793,12 @@ namespace Falcor mDisplacement.needsUpdate = true; } + // Check if emissive materials have changed. + if (is_set(materialUpdates, Material::UpdateFlags::EmissiveChanged)) + { + flags |= UpdateFlags::EmissiveMaterialsChanged; + } + // Update type conformances. auto prevTypeConformances = mTypeConformances; mTypeConformances = mpMaterials->getTypeConformances(); @@ -1728,6 +1813,8 @@ namespace Falcor flags |= UpdateFlags::ShaderCodeChanged; } + // Update material stats upon any material changes for now. + // This is not strictly needed for most changes and can be optimized if the overhead becomes a problem. updateMaterialStats(); } @@ -1753,6 +1840,24 @@ namespace Falcor return flags; } + void Scene::updateForInverseRendering(RenderContext* pRenderContext, bool isMaterialChanged, bool isMeshChanged) + { + mUpdates = UpdateFlags::None; + if (isMaterialChanged) mUpdates |= updateMaterials(false); + + if (isMeshChanged) mUpdates |= UpdateFlags::MeshesChanged; + pRenderContext->submit(); + + bool blasUpdateRequired = is_set(mUpdates, UpdateFlags::MeshesChanged); + if (mBlasDataValid && blasUpdateRequired) + { + invalidateTlasCache(); + buildBlas(pRenderContext); + } + + // TODO: Update light collection if we allow changing area lights. + } + Scene::UpdateFlags Scene::update(RenderContext* pRenderContext, double currentTime) { // Run scene update callback. @@ -1771,14 +1876,17 @@ namespace Falcor { mUpdates |= UpdateFlags::SceneDefinesChanged; mPrevSceneDefines = mSceneDefines; + mpSceneBlock = nullptr; } - // TODO: If scene defined changed we should re-create the scene parameter block - // here as the defines may affect resource declarations. - // The assumption is currently that resources do not change after scene creation. - // When changing this behavior we should make sure that all accesses to the (new) - // scene block are placed below this point. - checkInvariant(!is_set(mUpdates, UpdateFlags::SceneDefinesChanged), "Scene doesn't yet support modifications that change the scene defines."); + // Recreate scene parameter block if scene defines changed, as the defines may affect resource declarations. + // All access to the (new) scene parameter block should be placed after this point. + if (!mpSceneBlock) + { + logDebug("Recreating scene parameter block"); + createParameterBlock(); + bindParameterBlock(); + } if (mpAnimationController->animate(pRenderContext, currentTime)) { @@ -1809,7 +1917,7 @@ namespace Falcor mUpdates |= updateEnvMap(false); mUpdates |= updateGeometry(pRenderContext, false); mUpdates |= updateSDFGrids(pRenderContext); - pRenderContext->flush(); + pRenderContext->submit(); if (is_set(mUpdates, UpdateFlags::GeometryMoved)) { @@ -1828,10 +1936,22 @@ namespace Falcor } // Update light collection - if (mpLightCollection && mpLightCollection->update(pRenderContext)) + if (mpLightCollection) { - mUpdates |= UpdateFlags::LightCollectionChanged; - mSceneStats.emissiveMemoryInBytes = mpLightCollection->getMemoryUsageInBytes(); + // If emissive material properties changed we recreate the light collection. + // This can be expensive and should be optimized by letting the light collection internally update its data structures. + if (is_set(mUpdates, UpdateFlags::EmissiveMaterialsChanged)) + { + mpLightCollection = nullptr; + getLightCollection(pRenderContext); + mUpdates |= UpdateFlags::LightCollectionChanged; + } + else + { + if (mpLightCollection->update(pRenderContext)) + mUpdates |= UpdateFlags::LightCollectionChanged; + mSceneStats.emissiveMemoryInBytes = mpLightCollection->getMemoryUsageInBytes(); + } } else if (!mpLightCollection) { @@ -1852,7 +1972,7 @@ namespace Falcor // Validate assumption that scene defines didn't change. updateSceneDefines(); - checkInvariant(mSceneDefines == mPrevSceneDefines, "Scene defines changed unexpectedly"); + FALCOR_CHECK(mSceneDefines == mPrevSceneDefines, "Scene defines changed unexpectedly"); return mUpdates; } @@ -2226,6 +2346,12 @@ namespace Falcor } } + const ref& Scene::getCamera() + { + FALCOR_CHECK(mSelectedCamera < mCameras.size(), "Selected camera index {} is invalid.", mSelectedCamera); + return mCameras[mSelectedCamera]; + } + void Scene::selectCamera(uint32_t index) { if (index == mSelectedCamera) return; @@ -2376,9 +2502,11 @@ namespace Falcor std::vector result; if (geometryIdx >= mMeshUVTiles.size()) return result; - checkInvariant(getGeometryType(geometryID) == GeometryType::TriangleMesh || - getGeometryType(geometryID) == GeometryType::DisplacedTriangleMesh - , "Can get UV tiles only from triangle-based meshes only"); + FALCOR_CHECK( + getGeometryType(geometryID) == GeometryType::TriangleMesh || + getGeometryType(geometryID) == GeometryType::DisplacedTriangleMesh, + "Can get UV tiles only from triangle-based meshes only" + ); return mMeshUVTiles[geometryIdx]; } @@ -2393,7 +2521,7 @@ namespace Falcor else if (geometryID.get() < mMeshDesc.size() + mCurveDesc.size()) return GeometryType::Curve; else if (geometryID.get() < mMeshDesc.size() + mCurveDesc.size() + mSDFGridDesc.size()) return GeometryType::SDFGrid; else if (geometryID.get() < mMeshDesc.size() + mCurveDesc.size() + mSDFGridDesc.size() + mCustomPrimitiveDesc.size()) return GeometryType::Custom; - else throw ArgumentError("'geometryID' is invalid."); + else FALCOR_THROW("'geometryID' is invalid."); } uint32_t Scene::getSDFGridGeometryCount() const @@ -2467,14 +2595,14 @@ namespace Falcor } geometryIdx -= (uint32_t)mCustomPrimitiveDesc.size(); - throw ArgumentError("'geometryID' is invalid."); + FALCOR_THROW("'geometryID' is invalid."); } uint32_t Scene::getCustomPrimitiveIndex(GlobalGeometryID geometryID) const { if (getGeometryType(geometryID) != GeometryType::Custom) { - throw ArgumentError("'geometryID' ({}) does not refer to a custom primitive.", geometryID); + FALCOR_THROW("'geometryID' ({}) does not refer to a custom primitive.", geometryID); } size_t customPrimitiveOffset = mMeshDesc.size() + mCurveDesc.size() + mSDFGridDesc.size(); @@ -2486,7 +2614,7 @@ namespace Falcor { if (index >= getCustomPrimitiveCount()) { - throw ArgumentError("'index' ({}) is out of range.", index); + FALCOR_THROW("'index' ({}) is out of range.", index); } return mCustomPrimitiveDesc[index]; } @@ -2495,7 +2623,7 @@ namespace Falcor { if (index >= getCustomPrimitiveCount()) { - throw ArgumentError("'index' ({}) is out of range.", index); + FALCOR_THROW("'index' ({}) is out of range.", index); } return mCustomPrimitiveAABBs[index]; } @@ -2506,7 +2634,7 @@ namespace Falcor FALCOR_ASSERT(mCustomPrimitiveDesc.size() == mCustomPrimitiveAABBs.size()); if (mCustomPrimitiveAABBs.size() > std::numeric_limits::max()) { - throw RuntimeError("Custom primitive count exceeds the maximum"); + FALCOR_THROW("Custom primitive count exceeds the maximum"); } const uint32_t index = (uint32_t)mCustomPrimitiveDesc.size(); @@ -2524,11 +2652,7 @@ namespace Falcor void Scene::removeCustomPrimitives(uint32_t first, uint32_t last) { - if (first > last || last > getCustomPrimitiveCount()) - { - throw ArgumentError("'first' ({}) and 'last' ({}) is not a valid range of custom primitives.", first, last); - } - + FALCOR_CHECK(first <= last && last <= getCustomPrimitiveCount(), "'first' ({}) and 'last' ({}) is not a valid range of custom primitives.", first, last); if (first == last) return; mCustomPrimitiveDesc.erase(mCustomPrimitiveDesc.begin() + first, mCustomPrimitiveDesc.begin() + last); @@ -2546,10 +2670,7 @@ namespace Falcor void Scene::updateCustomPrimitive(uint32_t index, const AABB& aabb) { - if (index >= getCustomPrimitiveCount()) - { - throw ArgumentError("'index' ({}) is out of range.", index); - } + FALCOR_CHECK(index < getCustomPrimitiveCount(), "'index' ({}) is out of range.", index); if (mCustomPrimitiveAABBs[index] != aabb) { @@ -2610,7 +2731,7 @@ namespace Falcor if (drawMeshes.size() > 0) { DrawArgs draw; - draw.pBuffer = Buffer::create(mpDevice, sizeof(drawMeshes[0]) * drawMeshes.size(), Resource::BindFlags::IndirectArg, Buffer::CpuAccess::None, drawMeshes.data()); + draw.pBuffer = mpDevice->createBuffer(sizeof(drawMeshes[0]) * drawMeshes.size(), ResourceBindFlags::IndirectArg, MemoryType::DeviceLocal, drawMeshes.data()); draw.pBuffer->setName("Scene draw buffer"); FALCOR_ASSERT(drawMeshes.size() <= std::numeric_limits::max()); draw.count = (uint32_t)drawMeshes.size(); @@ -2712,7 +2833,7 @@ namespace Falcor for (const auto& m : globalMatrices) transposedMatrices.push_back(transpose(m)); uint32_t float4Count = (uint32_t)transposedMatrices.size() * 4; - mpBlasStaticWorldMatrices = Buffer::createStructured(mpDevice, sizeof(float4), float4Count, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, transposedMatrices.data(), false); + mpBlasStaticWorldMatrices = mpDevice->createStructuredBuffer(sizeof(float4), float4Count, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, transposedMatrices.data(), false); mpBlasStaticWorldMatrices->setName("Scene::mpBlasStaticWorldMatrices"); // Transition the resource to non-pixel shader state as expected by DXR. @@ -2948,7 +3069,7 @@ namespace Falcor // Verify that the total geometry count matches the expectation. size_t totalGeometries = 0; for (const auto& blas : mBlasData) totalGeometries += blas.geomDescs.size(); - if (totalGeometries != getGeometryCount()) throw RuntimeError("Total geometry count mismatch"); + if (totalGeometries != getGeometryCount()) FALCOR_THROW("Total geometry count mismatch"); mBlasDataValid = true; } @@ -3081,10 +3202,10 @@ namespace Falcor { FALCOR_PROFILE(pRenderContext, "buildBlas"); - if (!mBlasDataValid) throw RuntimeError("buildBlas() BLAS data is invalid"); + if (!mBlasDataValid) FALCOR_THROW("buildBlas() BLAS data is invalid"); if (!pRenderContext->getDevice()->isFeatureSupported(Device::SupportedFeatures::Raytracing)) { - throw RuntimeError("Raytracing is not supported by the current device"); + FALCOR_THROW("Raytracing is not supported by the current device"); } // Add barriers for the VB and IB which will be accessed by the build. @@ -3178,11 +3299,11 @@ namespace Falcor // TODO: Save memory by reducing the scratch buffer to the minimum required for the dynamic objects. if (mpBlasScratch == nullptr || mpBlasScratch->getSize() < scratchByteSize) { - mpBlasScratch = Buffer::create(mpDevice, scratchByteSize, Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + mpBlasScratch = mpDevice->createBuffer(scratchByteSize, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal); mpBlasScratch->setName("Scene::mpBlasScratch"); } - ref pResultBuffer = Buffer::create(mpDevice, resultByteSize, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); + ref pResultBuffer = mpDevice->createBuffer(resultByteSize, ResourceBindFlags::AccelerationStructure, MemoryType::DeviceLocal); FALCOR_ASSERT(pResultBuffer && mpBlasScratch); // Create post-build info pool for readback. @@ -3280,7 +3401,7 @@ namespace Falcor } } FALCOR_ASSERT(byteSize <= blas.prebuildInfo.resultDataMaxSize); - if (byteSize == 0) throw RuntimeError("Acceleration structure build failed for BLAS index {}", blasId); + if (byteSize == 0) FALCOR_THROW("Acceleration structure build failed for BLAS index {}", blasId); blas.blasByteSize = align_to(kAccelerationStructureByteAlignment, byteSize); blas.blasByteOffset = group.finalByteSize; @@ -3294,7 +3415,7 @@ namespace Falcor auto& pBlas = group.pBlas; if (pBlas == nullptr || pBlas->getSize() < group.finalByteSize) { - pBlas = Buffer::create(mpDevice, group.finalByteSize, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); + pBlas = mpDevice->createBuffer(group.finalByteSize, ResourceBindFlags::AccelerationStructure, MemoryType::DeviceLocal); pBlas->setName("Scene::mBlasGroups[" + std::to_string(blasGroupIndex) + "].pBlas"); } else @@ -3642,7 +3763,7 @@ namespace Falcor { // Prebuild mTlasPrebuildInfo = RtAccelerationStructure::getPrebuildInfo(mpDevice.get(), inputs); - mpTlasScratch = Buffer::create(mpDevice, mTlasPrebuildInfo.scratchDataSize, Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); + mpTlasScratch = mpDevice->createBuffer(mTlasPrebuildInfo.scratchDataSize, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal); mpTlasScratch->setName("Scene::mpTlasScratch"); // #SCENE This isn't guaranteed according to the spec, and the scratch buffer being stored should be sized differently depending on update mode @@ -3660,47 +3781,35 @@ namespace Falcor // Allocate a new buffer for the TLAS only if the existing buffer isn't big enough. if (!tlas.pTlasBuffer || tlas.pTlasBuffer->getSize() < mTlasPrebuildInfo.resultDataMaxSize) { - tlas.pTlasBuffer = Buffer::create(mpDevice, mTlasPrebuildInfo.resultDataMaxSize, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); + tlas.pTlasBuffer = mpDevice->createBuffer(mTlasPrebuildInfo.resultDataMaxSize, ResourceBindFlags::AccelerationStructure, MemoryType::DeviceLocal); tlas.pTlasBuffer->setName("Scene TLAS buffer"); } } - if (!mInstanceDescs.empty()) - { - // Allocate a new buffer for the TLAS instance desc input only if the existing buffer isn't big enough. - if (!tlas.pInstanceDescs || tlas.pInstanceDescs->getSize() < mInstanceDescs.size() * sizeof(RtInstanceDesc)) - { - tlas.pInstanceDescs = Buffer::create(mpDevice, (uint32_t)mInstanceDescs.size() * sizeof(RtInstanceDesc), Buffer::BindFlags::None, Buffer::CpuAccess::Write, mInstanceDescs.data()); - tlas.pInstanceDescs->setName("Scene instance descs buffer"); - } - else - { - tlas.pInstanceDescs->setBlob(mInstanceDescs.data(), 0, mInstanceDescs.size() * sizeof(RtInstanceDesc)); - } - } RtAccelerationStructure::Desc asCreateDesc = {}; asCreateDesc.setKind(RtAccelerationStructureKind::TopLevel); asCreateDesc.setBuffer(tlas.pTlasBuffer, 0, mTlasPrebuildInfo.resultDataMaxSize); tlas.pTlasObject = RtAccelerationStructure::create(mpDevice, asCreateDesc); } - // Else update instance descs and barrier TLAS buffers + // Else barrier TLAS buffers else { FALCOR_ASSERT(mpAnimationController->hasAnimations() || mpAnimationController->hasAnimatedVertexCaches()); pRenderContext->uavBarrier(tlas.pTlasBuffer.get()); pRenderContext->uavBarrier(mpTlasScratch.get()); - if (tlas.pInstanceDescs) - { - FALCOR_ASSERT(!mInstanceDescs.empty()); - tlas.pInstanceDescs->setBlob(mInstanceDescs.data(), 0, inputs.descCount * sizeof(RtInstanceDesc)); - } asDesc.source = tlas.pTlasObject.get(); // Perform the update in-place } FALCOR_ASSERT(tlas.pTlasBuffer && tlas.pTlasBuffer->getGfxResource() && mpTlasScratch->getGfxResource()); - FALCOR_ASSERT(inputs.descCount == 0 || (tlas.pInstanceDescs && tlas.pInstanceDescs->getGfxResource())); - asDesc.inputs.instanceDescs = tlas.pInstanceDescs ? tlas.pInstanceDescs->getGpuAddress() : 0; + // Upload instance data + if (inputs.descCount > 0) + { + GpuMemoryHeap::Allocation allocation = mpDevice->getUploadHeap()->allocate(inputs.descCount * sizeof(RtInstanceDesc), sizeof(RtInstanceDesc)); + std::memcpy(allocation.pData, mInstanceDescs.data(), inputs.descCount * sizeof(RtInstanceDesc)); + asDesc.inputs.instanceDescs = allocation.getGpuAddress(); + mpDevice->getUploadHeap()->release(allocation); + } asDesc.scratchData = mpTlasScratch->getGpuAddress(); asDesc.dest = tlas.pTlasObject.get(); @@ -3711,10 +3820,6 @@ namespace Falcor } // Create TLAS - if (tlas.pInstanceDescs) - { - pRenderContext->resourceBarrier(tlas.pInstanceDescs.get(), Resource::State::NonPixelShader); - } pRenderContext->buildAccelerationStructure(asDesc, 0, nullptr); pRenderContext->uavBarrier(tlas.pTlasBuffer.get()); @@ -3752,7 +3857,7 @@ namespace Falcor mpSceneBlock->getRootVar()["rtAccel"].setAccelerationStructure(tlasIt->second.pTlasObject); // Bind Scene parameter block. - getCamera()->setShaderData(mpSceneBlock->getRootVar()[kCamera]); // TODO REMOVE: Shouldn't be needed anymore? + getCamera()->bindShaderData(mpSceneBlock->getRootVar()[kCamera]); // TODO REMOVE: Shouldn't be needed anymore? var[kParameterBlockName] = mpSceneBlock; } @@ -3776,56 +3881,10 @@ namespace Falcor NodeID Scene::getParentNodeID(NodeID nodeID) const { - if (nodeID.get() >= mSceneGraph.size()) throw ArgumentError("'nodeID' ({}) is out of range", nodeID); + FALCOR_CHECK(nodeID.get() < mSceneGraph.size(), "'nodeID' ({}) is out of range", nodeID); return mSceneGraph[nodeID.get()].parent; } - void Scene::nullTracePass(RenderContext* pRenderContext, const uint2& dim) - { - auto pDevice = pRenderContext->getDevice(); - - if (!pDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) - { - logWarning("Raytracing Tier 1.1 is not supported by the current device."); - return; - } - - RtAccelerationStructureBuildInputs inputs = {}; - inputs.kind = RtAccelerationStructureKind::TopLevel; - inputs.descCount = 0; - inputs.flags = RtAccelerationStructureBuildFlags::None; - - RtAccelerationStructurePrebuildInfo prebuildInfo = RtAccelerationStructure::getPrebuildInfo(pDevice.get(), inputs); - - auto pScratch = Buffer::create(pDevice, prebuildInfo.scratchDataSize, Buffer::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); - auto pTlasBuffer = Buffer::create(pDevice, prebuildInfo.resultDataMaxSize, Buffer::BindFlags::AccelerationStructure, Buffer::CpuAccess::None); - - RtAccelerationStructure::Desc createDesc = {}; - createDesc.setKind(RtAccelerationStructureKind::TopLevel); - createDesc.setBuffer(pTlasBuffer, 0, prebuildInfo.resultDataMaxSize); - ref tlasObject = RtAccelerationStructure::create(pDevice, createDesc); - - RtAccelerationStructure::BuildDesc asDesc = {}; - asDesc.inputs = inputs; - asDesc.scratchData = pScratch->getGpuAddress(); - asDesc.dest = tlasObject.get(); - - pRenderContext->buildAccelerationStructure(asDesc, 0, nullptr); - pRenderContext->uavBarrier(pTlasBuffer.get()); - - Program::Desc desc; - desc.addShaderLibrary("Scene/NullTrace.cs.slang").csEntry("main").setShaderModel("6_5"); - auto pass = ComputePass::create(pDevice, desc); - auto var = pass->getRootVar(); - var["gOutput"] = Texture::create2D(pDevice, dim.x, dim.y, ResourceFormat::R8Uint, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess); - var["gTlas"].setAccelerationStructure(tlasObject); - - for (size_t i = 0; i < 100; i++) - { - pass->execute(pRenderContext, uint3(dim, 1)); - } - } - void Scene::setEnvMap(ref pEnvMap) { if (mpEnvMap == pEnvMap) return; @@ -4005,6 +4064,52 @@ namespace Falcor mpAnimationController->setNodeEdited(nodeID); } + void Scene::getMeshVerticesAndIndices(MeshID meshID, const std::map>& buffers) + { + if (!mpLoadMeshPass) + mpLoadMeshPass = ComputePass::create(mpDevice, kMeshIOShaderFilename, "getMeshVerticesAndIndices", getSceneDefines()); + const auto& meshDesc = getMesh(meshID); + + // Bind variables. + auto var = mpLoadMeshPass->getRootVar()["meshLoader"]; + var["vertexCount"] = meshDesc.vertexCount; + var["vbOffset"] = meshDesc.vbOffset; + var["triangleCount"] = meshDesc.getTriangleCount(); + var["ibOffset"] = meshDesc.ibOffset; + var["use16BitIndices"] = meshDesc.use16BitIndices(); + bindShaderData(var["scene"]); + for (const auto& name : kMeshLoaderRequiredBufferNames) + { + FALCOR_CHECK(buffers.find(name) != buffers.end(), "Mesh data buffer '{}' is missing.", name); + var[name] = buffers.at(name); + } + + mpLoadMeshPass->execute(mpDevice->getRenderContext(), std::max(meshDesc.vertexCount, meshDesc.getTriangleCount()), 1, 1); + } + + void Scene::setMeshVertices(MeshID meshID, const std::map>& buffers) + { + if (!mpUpdateMeshPass) + mpUpdateMeshPass = ComputePass::create(mpDevice, kMeshIOShaderFilename, "setMeshVertices", getSceneDefines()); + const auto& meshDesc = getMesh(meshID); + + // Bind variables. + auto var = mpUpdateMeshPass->getRootVar()["meshUpdater"]; + var["vertexCount"] = meshDesc.vertexCount; + var["vbOffset"] = meshDesc.vbOffset; + var["vertexData"] = getMeshVao()->getVertexBuffer(kStaticDataBufferIndex); + for (const auto& name : kMeshUpdaterRequiredBufferNames) + { + FALCOR_CHECK(buffers.find(name) != buffers.end(), "Mesh data buffer '{}' is missing.", name); + var[name] = buffers.at(name); + } + + mpUpdateMeshPass->execute(mpDevice->getRenderContext(), meshDesc.vertexCount, 1, 1); + + // Update BLAS/TLAS. + updateForInverseRendering(mpDevice->getRenderContext(), false, true); + } + inline pybind11::dict toPython(const Scene::SceneStats& stats) { pybind11::dict d; @@ -4090,6 +4195,93 @@ namespace Falcor return d; } + /** Get serialized material parameters for a list of materials. + * \param materialIDsBuffer Buffer containing material IDs + * \param paramsBuffer Buffer to write material parameters to + */ + inline void getMaterialParamsPython(Scene& scene, ref materialIDsBuffer, ref paramsBuffer) + { + // Get material IDs from buffer. + FALCOR_CHECK(materialIDsBuffer->getElementSize() == sizeof(uint32_t), "Material IDs buffer must contain uint32_t elements."); + std::vector materialIDs = materialIDsBuffer->getElements(); + + // Fetch material parameters. + std::vector params; + params.reserve(materialIDs.size() * SerializedMaterialParams::kParamCount); + for (uint32_t materialID : materialIDs) + { + SerializedMaterialParams tmpParams = scene.getMaterial(MaterialID(materialID))->serializeParams(); + params.insert(params.end(), tmpParams.begin(), tmpParams.end()); + } + + // Write material parameters to buffer. + FALCOR_CHECK(paramsBuffer->getSize() >= params.size() * sizeof(float), "Material parameter buffer is too small."); + paramsBuffer->setBlob(params.data(), 0, params.size() * sizeof(float)); +#if FALCOR_HAS_CUDA + scene.getDevice()->getRenderContext()->waitForFalcor(); +#endif + } + + /** Set serialized material parameters for a list of materials. + * \param materialIDsBuffer Buffer containing material IDs + * \param paramsBuffer Buffer containing material parameters + */ + inline void setMaterialParamsPython(Scene& scene, ref materialIDsBuffer, ref paramsBuffer) + { + // Get material IDs buffer. + FALCOR_CHECK(materialIDsBuffer->getElementSize() == sizeof(uint32_t), "Material IDs buffer must contain uint32_t elements."); + std::vector materialIDs = materialIDsBuffer->getElements(); + + // Get material parameters from buffer. + std::vector params = paramsBuffer->getElements(0, materialIDs.size() * SerializedMaterialParams::kParamCount); + + // Update material parameters. + size_t ofs = 0; + SerializedMaterialParams tmpParams; + for (uint32_t materialID : materialIDs) + { + std::copy(params.begin() + ofs, params.begin() + ofs + SerializedMaterialParams::kParamCount, tmpParams.begin()); + scene.getMaterial(MaterialID(materialID))->deserializeParams(tmpParams); + ofs += SerializedMaterialParams::kParamCount; + } + + // Need to update scene explicitly without calling `testbed.frame()`. + scene.updateForInverseRendering(scene.getDevice()->getRenderContext(), true, false); +#if FALCOR_HAS_CUDA + scene.getDevice()->getRenderContext()->waitForFalcor(); +#endif + } + + inline void getMeshVerticesAndIndicesPython(Scene& scene, MeshID meshID, const pybind11::dict& dict) + { + std::map> buffers; + for (auto item : dict) + { + std::string name = item.first.cast(); + ref buffer = item.second.cast>(); + buffers[name] = buffer; + } + scene.getMeshVerticesAndIndices(meshID, buffers); +#if FALCOR_HAS_CUDA + scene.getDevice()->getRenderContext()->waitForFalcor(); +#endif + } + + inline void setMeshVerticesPython(Scene& scene, MeshID meshID, const pybind11::dict& dict) + { + std::map> buffers; + for (auto item : dict) + { + std::string name = item.first.cast(); + ref buffer = item.second.cast>(); + buffers[name] = buffer; + } + scene.setMeshVertices(meshID, buffers); +#if FALCOR_HAS_CUDA + scene.getDevice()->getRenderContext()->waitForFalcor(); +#endif + } + FALCOR_SCRIPT_BINDING(Scene) { using namespace pybind11::literals; @@ -4163,19 +4355,34 @@ namespace Falcor // Materials scene.def_property_readonly(kMaterials.c_str(), &Scene::getMaterials); - scene.def(kGetMaterial.c_str(), &Scene::getMaterial, "index"_a); - scene.def(kGetMaterial.c_str(), &Scene::getMaterialByName, "name"_a); + scene.def(kGetMaterial.c_str(), &Scene::getMaterial, "index"_a); // PYTHONDEPRECATED + scene.def(kGetMaterial.c_str(), &Scene::getMaterialByName, "name"_a); // PYTHONDEPRECATED + scene.def("get_material", &Scene::getMaterial, "index"_a); + scene.def("get_material", &Scene::getMaterialByName, "name"_a); scene.def("addMaterial", &Scene::addMaterial, "material"_a); scene.def("getGeometryIDsForMaterial", [](const Scene* scene, const ref& pMaterial) { return scene->getGeometryIDs(pMaterial.get()); }, "material"_a); + scene.def("replace_material", [](const Scene* pScene, uint32_t index, ref replacementMaterial) { + pScene->getMaterialSystem().replaceMaterial(MaterialID{ index }, replacementMaterial); }, "index"_a, "replacement_material"_a); + scene.def("get_material_params", getMaterialParamsPython); + scene.def("set_material_params", setMaterialParamsPython); // Viewpoints scene.def(kAddViewpoint.c_str(), pybind11::overload_cast<>(&Scene::addViewpoint)); // add current camera as viewpoint scene.def(kAddViewpoint.c_str(), pybind11::overload_cast(&Scene::addViewpoint), "position"_a, "target"_a, "up"_a, "cameraIndex"_a = 0); // add specified viewpoint scene.def(kRemoveViewpoint.c_str(), &Scene::removeViewpoint); // remove the selected viewpoint scene.def(kSelectViewpoint.c_str(), &Scene::selectViewpoint, "index"_a); // select a viewpoint by index + + // Meshes + pybind11::class_ meshDesc(m, "MeshDesc"); + meshDesc.def_property_readonly("vertex_count", &MeshDesc::getVertexCount); + meshDesc.def_property_readonly("triangle_count", &MeshDesc::getTriangleCount); + + scene.def("get_mesh", &Scene::getMesh, "mesh_id"_a); + scene.def("get_mesh_vertices_and_indices", getMeshVerticesAndIndicesPython, "mesh_id"_a, "buffers"_a); + scene.def("set_mesh_vertices", setMeshVerticesPython, "mesh_id"_a, "buffers"_a); } } diff --git a/Source/Falcor/Scene/Scene.h b/Source/Falcor/Scene/Scene.h index 6e36d01db..16a1af69e 100644 --- a/Source/Falcor/Scene/Scene.h +++ b/Source/Falcor/Scene/Scene.h @@ -153,6 +153,7 @@ namespace Falcor SceneDefinesChanged = 0x2000000, ///< Scene defines changed. All programs that access the scene must be updated! TypeConformancesChanged = 0x4000000, ///< Type conformances changed. All programs that access the scene must be updated! ShaderCodeChanged = 0x8000000, ///< Shader code changed. All programs that access the scene must be updated! + EmissiveMaterialsChanged = 0x10000000, ///< Emissive materials changed. /// Flags indicating that programs that access the scene need to be recompiled. /// This is needed if defines, type conformances, and/or the shader code has changed. @@ -468,6 +469,12 @@ namespace Falcor */ const ref& getDevice() const { return mpDevice; } + /** Bind the scene to a given shader var. + Note that the scene may change between calls to update(). + The caller should rebind the scene data before executing any program that accesses the scene. + */ + void bindShaderData(const ShaderVar& var) const { var = mpSceneBlock; } + /** Get scene defines. These defines must be set on all programs that access the scene. If the defines change at runtime, the update flag `SceneDefinesChanged` is set. @@ -482,14 +489,14 @@ namespace Falcor The user is responsible to check for this and update all programs that access the scene. \return List of type conformances. */ - Program::TypeConformanceList getTypeConformances() const; + TypeConformanceList getTypeConformances() const; /** Get shader modules required by the scene. The shader modules must be added to any program using the scene. The update() function must have been called before calling this function. \return List of shader modules. */ - Program::ShaderModuleList getShaderModules() const; + ProgramDesc::ShaderModuleList getShaderModules() const; /** Get the current scene statistics. */ @@ -541,7 +548,7 @@ namespace Falcor /** Access the scene's currently selected camera to change properties or to use elsewhere. */ - const ref& getCamera() { return mCameras[mSelectedCamera]; } + const ref& getCamera(); /** Get the camera bounds */ @@ -714,6 +721,18 @@ namespace Falcor */ const MeshDesc& getMesh(MeshID meshID) const { return mMeshDesc[meshID.get()]; } + /** Get mesh vertex and index data. + \param[in] meshID Mesh ID. + \param[in] buffers Map of buffers containing mesh data: "triangleIndices", "positions", and "texcrds" are required. + */ + void getMeshVerticesAndIndices(MeshID meshID, const std::map>& buffers); + + /** Set mesh vertex data and update the acceleration structures. + \param[in] meshID Mesh ID. + \param[in] buffers Map of buffers containing mesh data: "positions", "normals", "tangents", and "texcrds" are required. + */ + void setMeshVertices(MeshID meshID, const std::map>& buffers); + /** Get the number of curves. */ uint32_t getCurveCount() const { return (uint32_t)mCurveDesc.size(); } @@ -954,6 +973,14 @@ namespace Falcor */ UpdateFlags getUpdates() const { return mUpdates; } + /** Update material and geometry for inverse rendering applications. + This is a subset of the update() function. + \param[in] pRenderContext The render context. + \param[in] isMaterialChanged True if material parameters changed. + \param[in] isMeshChanged True if mesh parameters changed. + */ + void updateForInverseRendering(RenderContext* pRenderContext, bool isMaterialChanged, bool isMeshChanged); + /** Render the scene using the rasterizer. Note the rasterizer state bound to 'pState' is ignored. \param[in] pRenderContext Render context. @@ -961,7 +988,7 @@ namespace Falcor \param[in] pVars Graphics vars. \param[in] cullMode Optional rasterizer cull mode. The default is to cull back-facing primitives. */ - void rasterize(RenderContext* pRenderContext, GraphicsState* pState, GraphicsVars* pVars, RasterizerState::CullMode cullMode = RasterizerState::CullMode::Back); + void rasterize(RenderContext* pRenderContext, GraphicsState* pState, ProgramVars* pVars, RasterizerState::CullMode cullMode = RasterizerState::CullMode::Back); /** Render the scene using the rasterizer. This overload uses the supplied rasterizer states. @@ -971,7 +998,7 @@ namespace Falcor \param[in] pRasterizerStateCW Rasterizer state for meshes with clockwise triangle winding. \param[in] pRasterizerStateCCW Rasterizer state for meshes with counter-clockwise triangle winding. Can be the same as for clockwise. */ - void rasterize(RenderContext* pRenderContext, GraphicsState* pState, GraphicsVars* pVars, const ref& pRasterizerStateCW, const ref& pRasterizerStateCCW); + void rasterize(RenderContext* pRenderContext, GraphicsState* pState, ProgramVars* pVars, const ref& pRasterizerStateCW, const ref& pRasterizerStateCCW); /** Get the required raytracing maximum attribute size for this scene. Note: This depends on what types of geometry are used in the scene. @@ -981,7 +1008,7 @@ namespace Falcor /** Render the scene using raytracing. */ - void raytrace(RenderContext* pRenderContext, RtProgram* pProgram, const ref& pVars, uint3 dispatchDims); + void raytrace(RenderContext* pRenderContext, Program* pProgram, const ref& pVars, uint3 dispatchDims); /** Render the UI. */ @@ -1065,10 +1092,6 @@ namespace Falcor */ void toggleAnimations(bool animate); - /** Get the parameter block with all scene resources. - */ - const ref& getParameterBlock() const { return mpSceneBlock; } - /** Set the scene ray tracing resources into a shader var. The acceleration structure is created lazily, which requires the render context. \param[in] pRenderContext Render context. @@ -1094,8 +1117,6 @@ namespace Falcor */ NodeID getParentNodeID(NodeID nodeID) const; - static void nullTracePass(RenderContext* pRenderContext, const uint2& dim); - std::string getScript(const std::string& sceneVar); private: @@ -1125,13 +1146,9 @@ namespace Falcor */ void createParameterBlock(); - /** Uploads scene data to parameter block. + /** Uploads geometry data. */ - void uploadResources(); - - /** Uploads the currently selected camera. - */ - void uploadSelectedCamera(); + void uploadGeometry(); /** Update the scene's global bounding box. */ @@ -1217,6 +1234,14 @@ namespace Falcor void updateLightStats(); void updateGridVolumeStats(); + void bindGeometry(); + void bindProceduralPrimitives(); + void bindGridVolumes(); + void bindSDFGrids(); + void bindLights(); + void bindSelectedCamera(); + void bindParameterBlock(); + Scene(ref pDevice, SceneData&& sceneData); ref mpDevice; ///< GPU device the scene resides on. @@ -1251,6 +1276,10 @@ namespace Falcor std::vector mMeshNames; ///< Mesh names, indxed by mesh ID std::vector mSceneGraph; ///< For each index i, the array element indicates the parent node. Indices are in relation to mLocalToWorldMatrices. + /// For Python bindings of triangle meshes. + ref mpLoadMeshPass; + ref mpUpdateMeshPass; + // Displacement mapping. struct { @@ -1313,7 +1342,7 @@ namespace Falcor RenderSettings mPrevRenderSettings; DefineList mSceneDefines; ///< Current list of defines that need to be set on any program accessing the scene. DefineList mPrevSceneDefines; ///< List of defines for the previous frame. - Program::TypeConformanceList mTypeConformances; ///< Current list of type conformances that need to be set on any program accessing the scene. + TypeConformanceList mTypeConformances; ///< Current list of type conformances that need to be set on any program accessing the scene. UpdateCallback mUpdateCallback; ///< Scene update callback. @@ -1366,7 +1395,6 @@ namespace Falcor { ref pTlasObject; ref pTlasBuffer; - ref pInstanceDescs; ///< Buffer holding instance descs for the TLAS. UpdateMode updateMode = UpdateMode::Rebuild; ///< Update mode this TLAS was created with. }; diff --git a/Source/Falcor/Scene/Scene.slang b/Source/Falcor/Scene/Scene.slang index 3688a001e..dde8238a1 100644 --- a/Source/Falcor/Scene/Scene.slang +++ b/Source/Falcor/Scene/Scene.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,6 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ +#ifndef FALCOR_USE_SCENE2 #include "SceneDefines.slangh" __exported import Scene.HitInfo; @@ -59,7 +60,6 @@ struct Scene { // DEMO21 static bool kUseLightProfile = SCENE_USE_LIGHT_PROFILE; - static float kDiffuseAlbedoMultiplier = SCENE_DIFFUSE_ALBEDO_MULTIPLIER; // Raytracing [root] RaytracingAccelerationStructure rtAccel; @@ -978,3 +978,5 @@ struct Scene }; ParameterBlock gScene; +#else +#endif diff --git a/Source/Falcor/Scene/SceneBuilder.cpp b/Source/Falcor/Scene/SceneBuilder.cpp index 0783f8238..faf823c5e 100644 --- a/Source/Falcor/Scene/SceneBuilder.cpp +++ b/Source/Falcor/Scene/SceneBuilder.cpp @@ -37,9 +37,11 @@ #include "Utils/Scripting/ScriptBindings.h" #include "Utils/Math/MathHelpers.h" #include "Utils/ObjectIDPython.h" +#include "Utils/NumericRange.h" #include #include #include +#include namespace Falcor { @@ -87,7 +89,7 @@ namespace Falcor if (genTangSpaceDefault(&context) == false) { - throw RuntimeError("MikkTSpace failed to generate tangents for the mesh '{}'.", mesh.name); + FALCOR_THROW("MikkTSpace failed to generate tangents for the mesh '{}'.", mesh.name); } return wrapper.mTangents; @@ -206,21 +208,21 @@ namespace Falcor , mSettings(settings) , mFlags(flags) { - mpFence = GpuFence::create(mpDevice); + mAssetResolver = AssetResolver::getDefaultResolver(); mSceneData.pMaterials = std::make_unique(mpDevice); } SceneBuilder::SceneBuilder(ref pDevice, const std::filesystem::path& path, const Settings& settings, Flags flags) : SceneBuilder(pDevice, settings, flags) { - std::filesystem::path fullPath; - if (!findFileInDataDirectories(path, fullPath)) + std::filesystem::path resolvedPath = mAssetResolver.resolvePath(path, AssetCategory::Scene); + if (resolvedPath.empty()) { throw ImporterError(path, "Can't find scene file '{}'.", path); } // Compute scene cache key based on absolute scene path and build flags. - mSceneCacheKey = computeSceneCacheKey(fullPath, flags); + mSceneCacheKey = computeSceneCacheKey(resolvedPath, flags); // Determine if scene cache should be written after import. bool useCache = is_set(flags, Flags::UseCache); @@ -237,7 +239,7 @@ namespace Falcor } catch (const std::exception& e) { - throw ImporterError(fullPath, "Failed to load scene cache: {}", e.what()); + throw ImporterError(resolvedPath, "Failed to load scene cache: {}", e.what()); } } @@ -252,34 +254,52 @@ namespace Falcor SceneBuilder::~SceneBuilder() {} + inline std::map convertDictToMap(const pybind11::dict& dict_) + { + std::map dict; + if (pybind11::isinstance(dict_)) + { + for (auto& it : dict_) + { + if (!pybind11::isinstance(it.first) || !pybind11::isinstance(it.second)) + continue; + dict[pybind11::str(it.first).cast()] = pybind11::str(it.second).cast(); + } + } + return dict; + } + void SceneBuilder::import(const std::filesystem::path& path, const pybind11::dict& dict) { logInfo("Importing scene: {}", path); - std::filesystem::path fullPath; - if (!findFileInDataDirectories(path, fullPath)) + std::map materialToShortName = convertDictToMap(dict); + + std::filesystem::path resolvedPath = mAssetResolver.resolvePath(path, AssetCategory::Scene); + if (resolvedPath.empty()) { throw ImporterError(path, "Can't find scene file '{}'.", path); } - mSceneData.path = fullPath; - if (auto importer = Importer::create(getExtensionFromPath(fullPath))) + mSceneData.path = resolvedPath; + if (auto importer = Importer::create(getExtensionFromPath(resolvedPath))) { - importer->importScene(fullPath, *this, dict); + importer->importScene(resolvedPath, *this, materialToShortName); } else { - throw ImporterError(fullPath, "Unknown file extension."); + throw ImporterError(resolvedPath, "Unknown file extension."); } } void SceneBuilder::importFromMemory(const void* buffer, size_t byteSize, std::string_view extension, const pybind11::dict& dict) { logInfo("Importing scene from memory"); + std::map materialToShortName = convertDictToMap(dict); mSceneData.path = ""; if (auto importer = Importer::create(extension)) { - importer->importSceneFromMemory(buffer, byteSize, extension, *this, dict); + importer->importSceneFromMemory(buffer, byteSize, extension, *this, materialToShortName); } else { @@ -287,6 +307,18 @@ namespace Falcor } } + void SceneBuilder::pushAssetResolver() + { + mAssetResolverStack.push_back(AssetResolver(mAssetResolver)); + } + + void SceneBuilder::popAssetResolver() + { + FALCOR_CHECK(mAssetResolverStack.size() > 0, "Asset resolver stack underflow."); + mAssetResolver = mAssetResolverStack.back(); + mAssetResolverStack.pop_back(); + } + ref SceneBuilder::getScene() { if (mpScene) return mpScene; @@ -379,10 +411,10 @@ namespace Falcor return addProcessedMesh(processMesh(mesh)); } - MeshID SceneBuilder::addTriangleMesh(const ref& pTriangleMesh, const ref& pMaterial) + MeshID SceneBuilder::addTriangleMesh(const ref& pTriangleMesh, const ref& pMaterial, bool isAnimated) { - checkArgument(pTriangleMesh != nullptr, "'pTriangleMesh' is missing"); - checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); + FALCOR_CHECK(pTriangleMesh != nullptr, "'pTriangleMesh' is missing"); + FALCOR_CHECK(pMaterial != nullptr, "'pMaterial' is missing"); Mesh mesh; @@ -397,6 +429,7 @@ namespace Falcor mesh.topology = Vao::Topology::TriangleList; mesh.isFrontFaceCW = pTriangleMesh->getFrontFaceCW(); mesh.pMaterial = pMaterial; + mesh.isAnimated = isAnimated; std::vector positions(vertices.size()); std::vector normals(vertices.size()); @@ -412,7 +445,7 @@ namespace Falcor return addMesh(mesh); } - SceneBuilder::ProcessedMesh SceneBuilder::processMesh(const Mesh& mesh_, MeshAttributeIndices* pAttributeIndices) const + SceneBuilder::ProcessedMesh SceneBuilder::processMesh(const Mesh& mesh_, MeshAttributeIndices* pAttributeIndices, std::vector* pTangents) const { // This function preprocesses a mesh into the final runtime representation. // Note the function needs to be thread safe. The following steps are performed: @@ -430,12 +463,13 @@ namespace Falcor processedMesh.topology = mesh.topology; processedMesh.pMaterial = mesh.pMaterial; processedMesh.isFrontFaceCW = mesh.isFrontFaceCW; + processedMesh.isAnimated = mesh.isAnimated; processedMesh.skeletonNodeId = mesh.skeletonNodeId; // Error checking. auto throw_on_missing_element = [&](const std::string& element) { - throw RuntimeError("Error when adding the mesh '{}' to the scene. The mesh is missing {}.", mesh.name, element); + FALCOR_THROW("Error when adding the mesh '{}' to the scene. The mesh is missing {}.", mesh.name, element); }; auto missing_element_warning = [&](const std::string& element) @@ -443,13 +477,13 @@ namespace Falcor logWarning("The mesh '{}' is missing the element {}. This is not an error, the element will be filled with zeros which may result in incorrect rendering.", mesh.name, element); }; - if (mesh.topology != Vao::Topology::TriangleList) throw RuntimeError("Error when adding the mesh '{}' to the scene. Only triangle list topology is supported.", mesh.name); + if (mesh.topology != Vao::Topology::TriangleList) FALCOR_THROW("Error when adding the mesh '{}' to the scene. Only triangle list topology is supported.", mesh.name); if (mesh.pMaterial == nullptr) throw_on_missing_element("material"); if (mesh.faceCount == 0) throw_on_missing_element("faces"); if (mesh.vertexCount == 0) throw_on_missing_element("vertices"); if (mesh.indexCount == 0 || !mesh.pIndices) throw_on_missing_element("indices"); - if (mesh.indexCount != mesh.faceCount * 3) throw RuntimeError("Error when adding the mesh '{}' to the scene. Unexpected face/vertex count.", mesh.name); + if (mesh.indexCount != mesh.faceCount * 3) FALCOR_THROW("Error when adding the mesh '{}' to the scene. Unexpected face/vertex count.", mesh.name); if (mesh.positions.pData == nullptr) throw_on_missing_element("positions"); if (mesh.normals.pData == nullptr) missing_element_warning("normals"); @@ -462,10 +496,12 @@ namespace Falcor } // Generate tangent space if that's required. - std::vector tangents; + std::vector localTangents; + if (!pTangents) + pTangents = &localTangents; if (!(is_set(mFlags, Flags::UseOriginalTangentSpace) || mesh.useOriginalTangentSpace) || !mesh.tangents.pData) { - generateTangents(mesh, tangents); + generateTangents(mesh, *pTangents); } // Pretransform the texture coordinates, rather than transforming them at runtime. @@ -623,8 +659,8 @@ namespace Falcor } // Copy vertices into processed mesh. - processedMesh.staticData.reserve(vertexCount); - if (mesh.hasBones()) processedMesh.skinningData.reserve(vertexCount); + processedMesh.staticData.resize(vertexCount); + if (mesh.hasBones()) processedMesh.skinningData.resize(vertexCount); for (uint32_t i = 0; i < vertexCount; i++) { @@ -639,7 +675,7 @@ namespace Falcor s.texCrd = v.texCrd; s.tangent = v.tangent; s.curveRadius = v.curveRadius; - processedMesh.staticData.push_back(s); + processedMesh.staticData[i] = s; } if (mesh.hasBones()) @@ -650,14 +686,14 @@ namespace Falcor s.staticIndex = i; // This references the local vertex here and gets updated in addProcessedMesh(). s.bindMatrixID = 0; // This will be initialized in createMeshData(). s.skeletonMatrixID = 0; // This will be initialized in createMeshData(). - processedMesh.skinningData.push_back(s); + processedMesh.skinningData[i] = s; } } return processedMesh; } - void SceneBuilder::generateTangents(Mesh& mesh, std::vector& tangents) const + void SceneBuilder::generateTangents(Mesh& mesh, std::vector& tangents) { tangents = MikkTSpaceWrapper::generateTangents(mesh); if (!tangents.empty()) @@ -665,6 +701,24 @@ namespace Falcor FALCOR_ASSERT(tangents.size() == mesh.indexCount); mesh.tangents.pData = tangents.data(); mesh.tangents.frequency = Mesh::AttributeFrequency::FaceVarying; + + /// MikkTSpace can produces NaN tangents in case of degenerate triangles, + /// e.g. triangles where all three points, normals, and texture coordinates happen to be identical. + /// We are replacing these NaN tangents by arbitrary tangent orthonormal to the vertex normal + if (mesh.tangents.pData) + { + FALCOR_ASSERT(mesh.tangents.frequency == Mesh::AttributeFrequency::FaceVarying); + NumericRange range(0, mesh.indexCount); + std::for_each(std::execution::par_unseq, range.begin(), range.end(), [&](uint32_t fvIndex) + { + if (!any(isnan(mesh.tangents.pData[fvIndex]))) + return; + uint32_t faceIndex = fvIndex / 3; + uint32_t vertexIndex = fvIndex % 3; + float3 normal = mesh.getNormal(faceIndex, vertexIndex); + tangents[fvIndex] = float4(perp_stark(normal), 1.f); + }); + } } else { @@ -684,6 +738,7 @@ namespace Falcor spec.topology = mesh.topology; spec.materialId = addMaterial(mesh.pMaterial); spec.isFrontFaceCW = mesh.isFrontFaceCW; + spec.isAnimated = mesh.isAnimated; spec.skeletonNodeID = mesh.skeletonNodeId; spec.vertexCount = (uint32_t)mesh.staticData.size(); @@ -711,15 +766,22 @@ namespace Falcor if (mMeshes.size() > std::numeric_limits::max()) { - throw RuntimeError("Trying to build a scene that exceeds supported number of meshes"); + FALCOR_THROW("Trying to build a scene that exceeds supported number of meshes"); } return MeshID(mMeshes.size() - 1); } - void SceneBuilder::setCachedMeshes(std::vector&& cachedMeshes) + void SceneBuilder::addCachedMeshes(std::vector&& cachedMeshes) + { + mSceneData.cachedMeshes.reserve(mSceneData.cachedMeshes.size() + cachedMeshes.size()); + for (auto&& it : cachedMeshes) + mSceneData.cachedMeshes.push_back(std::move(it)); + } + + void SceneBuilder::addCachedMesh(CachedMesh&& cachedMesh) { - mSceneData.cachedMeshes = std::move(cachedMeshes); + mSceneData.cachedMeshes.push_back(std::move(cachedMesh)); } void SceneBuilder::addCustomPrimitive(uint32_t userID, const AABB& aabb) @@ -728,7 +790,7 @@ namespace Falcor FALCOR_ASSERT(mSceneData.customPrimitiveDesc.size() == mSceneData.customPrimitiveAABBs.size()); if (mSceneData.customPrimitiveAABBs.size() > std::numeric_limits::max()) { - throw RuntimeError("Custom primitive count exceeds the maximum"); + FALCOR_THROW("Custom primitive count exceeds the maximum"); } CustomPrimitiveDesc desc = {}; @@ -757,7 +819,7 @@ namespace Falcor // Error checking. auto throw_on_missing_element = [&](const std::string& element) { - throw RuntimeError("Error when adding the curve '{}' to the scene. The curve is missing {}.", curve.name, element); + FALCOR_THROW("Error when adding the curve '{}' to the scene. The curve is missing {}.", curve.name, element); }; auto missing_element_warning = [&](const std::string& element) @@ -777,7 +839,7 @@ namespace Falcor // Copy indices and vertices into processed curve. processedCurve.indexData.assign(curve.pIndices, curve.pIndices + curve.indexCount); - processedCurve.staticData.reserve(curve.vertexCount); + processedCurve.staticData.resize(curve.vertexCount); for (uint32_t i = 0; i < curve.vertexCount; i++) { StaticCurveVertexData s; @@ -793,7 +855,7 @@ namespace Falcor s.texCrd = float2(0.f); } - processedCurve.staticData.push_back(s); + processedCurve.staticData[i] = s; } return processedCurve; @@ -820,18 +882,31 @@ namespace Falcor if (mCurves.size() > std::numeric_limits::max()) { - throw RuntimeError("Trying to build a scene that exceeds supported number of curves."); + FALCOR_THROW("Trying to build a scene that exceeds supported number of curves."); } return CurveID(mCurves.size() - 1); } + void SceneBuilder::addCachedCurves(std::vector&& cachedCurves) + { + mSceneData.cachedCurves.reserve(mSceneData.cachedCurves.size() + cachedCurves.size()); + for (auto&& it : cachedCurves) + mSceneData.cachedCurves.push_back(std::move(it)); + } + + void SceneBuilder::addCachedCurve(CachedCurve&& cachedCurve) + { + mSceneData.cachedCurves.push_back(std::move(cachedCurve)); + } + + // SDFs SdfDescID SceneBuilder::addSDFGrid(const ref& pSDFGrid, const ref& pMaterial) { - checkArgument(pSDFGrid != nullptr, "'pSDFGrid' is missing"); - checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); + FALCOR_CHECK(pSDFGrid != nullptr, "'pSDFGrid' is missing"); + FALCOR_CHECK(pMaterial != nullptr, "'pMaterial' is missing"); Scene::SDFGridDesc desc; desc.materialID = addMaterial(pMaterial); @@ -847,25 +922,26 @@ namespace Falcor MaterialID SceneBuilder::addMaterial(const ref& pMaterial) { - checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); + FALCOR_CHECK(pMaterial != nullptr, "'pMaterial' is missing"); return mSceneData.pMaterials->addMaterial(pMaterial); } void SceneBuilder::replaceMaterial(const ref& pMaterial, const ref& pReplacement) { - checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); - checkArgument(pReplacement != nullptr, "'pReplacement' is missing"); + FALCOR_CHECK(pMaterial != nullptr, "'pMaterial' is missing"); + FALCOR_CHECK(pReplacement != nullptr, "'pReplacement' is missing"); mSceneData.pMaterials->replaceMaterial(pMaterial, pReplacement); } void SceneBuilder::loadMaterialTexture(const ref& pMaterial, Material::TextureSlot slot, const std::filesystem::path& path) { - checkArgument(pMaterial != nullptr, "'pMaterial' is missing"); + FALCOR_CHECK(pMaterial != nullptr, "'pMaterial' is missing"); if (!mpMaterialTextureLoader) { mpMaterialTextureLoader.reset(new MaterialTextureLoader(mSceneData.pMaterials->getTextureManager(), !is_set(mFlags, Flags::AssumeLinearSpaceTextures))); } - mpMaterialTextureLoader->loadTexture(pMaterial, slot, path); + std::filesystem::path resolvedPath = mAssetResolver.resolvePath(path); + mpMaterialTextureLoader->loadTexture(pMaterial, slot, resolvedPath); } void SceneBuilder::waitForMaterialTextureLoading() @@ -887,7 +963,7 @@ namespace Falcor VolumeID SceneBuilder::addGridVolume(const ref& pGridVolume, NodeID nodeID) { FALCOR_ASSERT(pGridVolume); - checkArgument(nodeID == NodeID::Invalid() || nodeID.get() < mSceneGraph.size(), "'nodeID' ({}) is out of range", nodeID); + FALCOR_CHECK(nodeID == NodeID::Invalid() || nodeID.get() < mSceneGraph.size(), "'nodeID' ({}) is out of range", nodeID); if (nodeID != NodeID::Invalid()) { @@ -913,7 +989,7 @@ namespace Falcor LightID SceneBuilder::addLight(const ref& pLight) { - checkArgument(pLight != nullptr, "'pLight' is missing"); + FALCOR_CHECK(pLight != nullptr, "'pLight' is missing"); mSceneData.lights.push_back(pLight); FALCOR_ASSERT(mSceneData.lights.size() <= std::numeric_limits::max()); return LightID(mSceneData.lights.size() - 1); @@ -921,14 +997,14 @@ namespace Falcor void SceneBuilder::loadLightProfile(const std::string& filename, bool normalize) { - mSceneData.pLightProfile = LightProfile::createFromIesProfile(mpDevice, std::filesystem::path(filename), normalize); + mSceneData.pLightProfile = LightProfile::createFromIesProfile(mpDevice, mAssetResolver.resolvePath(std::filesystem::path(filename)), normalize); } // Cameras CameraID SceneBuilder::addCamera(const ref& pCamera) { - checkArgument(pCamera != nullptr, "'pCamera' is missing"); + FALCOR_CHECK(pCamera != nullptr, "'pCamera' is missing"); mSceneData.cameras.push_back(pCamera); FALCOR_ASSERT(mSceneData.cameras.size() <= std::numeric_limits::max()); return CameraID(mSceneData.cameras.size() - 1); @@ -949,13 +1025,13 @@ namespace Falcor void SceneBuilder::addAnimation(const ref& pAnimation) { - checkArgument(pAnimation != nullptr, "'pAnimation' is missing"); + FALCOR_CHECK(pAnimation != nullptr, "'pAnimation' is missing"); mSceneData.animations.push_back(pAnimation); } ref SceneBuilder::createAnimation(ref pAnimatable, const std::string& name, double duration) { - checkArgument(pAnimatable != nullptr, "'pAnimatable' is missing"); + FALCOR_CHECK(pAnimatable != nullptr, "'pAnimatable' is missing"); NodeID nodeID = pAnimatable->getNodeID(); @@ -984,7 +1060,7 @@ namespace Falcor { if (!isMatrixValid(m)) { - throw RuntimeError("Node '{}' {} matrix has inf/nan values", node.name, field); + FALCOR_THROW("Node '{}' {} matrix has inf/nan values", node.name, field); } // Check the assumption that transforms are affine. Note that glm is column-major. if (!isMatrixAffine(m)) @@ -1000,8 +1076,8 @@ namespace Falcor internalNode.localToBindPose = validateMatrix(node.localToBindPose, "localToBindPose"); static_assert(NodeID::kInvalidID >= std::numeric_limits::max()); - if (node.parent.isValid() && node.parent.get() >= mSceneGraph.size()) throw RuntimeError("Node parent is out of range"); - if (mSceneGraph.size() >= std::numeric_limits::max()) throw RuntimeError("Scene graph is too large"); + if (node.parent.isValid() && node.parent.get() >= mSceneGraph.size()) FALCOR_THROW("Node parent is out of range"); + if (mSceneGraph.size() >= std::numeric_limits::max()) FALCOR_THROW("Scene graph is too large"); // Add node to scene graph. NodeID newNodeID{ mSceneGraph.size() }; @@ -1013,8 +1089,8 @@ namespace Falcor void SceneBuilder::addMeshInstance(NodeID nodeID, MeshID meshID) { - checkArgument(nodeID.get() < mSceneGraph.size(), "'nodeID' ({}) is out of range", nodeID); - checkArgument(meshID.get() < mMeshes.size(), "'meshID' ({}) is out of range", meshID); + FALCOR_CHECK(nodeID.get() < mSceneGraph.size(), "'nodeID' ({}) is out of range", nodeID); + FALCOR_CHECK(meshID.get() < mMeshes.size(), "'meshID' ({}) is out of range", meshID); mSceneGraph[nodeID.get()].meshes.push_back(meshID); mMeshes[meshID.get()].instances.insert(nodeID); @@ -1022,8 +1098,8 @@ namespace Falcor void SceneBuilder::addCurveInstance(NodeID nodeID, CurveID curveID) { - checkArgument(nodeID.get() < mSceneGraph.size(), "'nodeID' ({}) is out of range", nodeID); - checkArgument(curveID.get() < mCurves.size(), "'curveID' ({}) is out of range", curveID); + FALCOR_CHECK(nodeID.get() < mSceneGraph.size(), "'nodeID' ({}) is out of range", nodeID); + FALCOR_CHECK(curveID.get() < mCurves.size(), "'curveID' ({}) is out of range", curveID); mSceneGraph[nodeID.get()].curves.push_back(curveID); mCurves[curveID.get()].instances.insert(nodeID); @@ -1031,8 +1107,8 @@ namespace Falcor void SceneBuilder::addSDFGridInstance(NodeID nodeID, SdfDescID sdfGridID) { - checkArgument(nodeID.get() < mSceneGraph.size(), "'nodeID' ({}) is out of range", nodeID); - checkArgument(sdfGridID.get() < mSceneData.sdfGridDesc.size(), "'sdfGridID' ({}) is out of range", sdfGridID); + FALCOR_CHECK(nodeID.get() < mSceneGraph.size(), "'nodeID' ({}) is out of range", nodeID); + FALCOR_CHECK(sdfGridID.get() < mSceneData.sdfGridDesc.size(), "'sdfGridID' ({}) is out of range", sdfGridID); Scene::SDFGridDesc& desc = mSceneData.sdfGridDesc[sdfGridID.get()]; mSceneGraph[nodeID.get()].sdfGrids.push_back(desc.sdfGridID); @@ -1266,7 +1342,7 @@ namespace Falcor auto addAnimatable = [this](Animatable* pObject, const std::string& name) { if (auto nodeID = pObject->getNodeID(); nodeID != NodeID::Invalid()) { - if (nodeID.get() >= mSceneGraph.size()) throw RuntimeError("Invalid node ID in animatable object named '{}'", name); + if (nodeID.get() >= mSceneGraph.size()) FALCOR_THROW("Invalid node ID in animatable object named '{}'", name); mSceneGraph[nodeID.get()].animatable.push_back(pObject); } }; @@ -1289,7 +1365,11 @@ namespace Falcor // Initialize any mesh properties that depend on the scene modifications to be finished. // Set mesh properties related to vertex animations - for (auto& m : mMeshes) m.isAnimated = false; + for (auto& mesh : mMeshes) + { + if (mesh.isAnimated) + mesh.prevVertexCount = mesh.staticVertexCount; + } for (auto& cache : mSceneData.cachedMeshes) { auto& mesh = mMeshes[cache.meshID.get()]; @@ -1551,7 +1631,7 @@ namespace Falcor if (it != uniqueStaticNodes.end()) { bool merged = mergeNodes(*it, nodeID); - if (!merged) throw RuntimeError("Unexpectedly failed to merge nodes"); + if (!merged) FALCOR_THROW("Unexpectedly failed to merge nodes"); mergedNodesCount++; } else @@ -1650,7 +1730,7 @@ namespace Falcor // Note that both static and dynamic vertices have to be swapped for dynamic meshes. if (mesh.indexCount == 0) { - throw RuntimeError("SceneBuilder::flipTriangleWinding() is not implemented for non-indexed meshes"); + FALCOR_THROW("SceneBuilder::flipTriangleWinding() is not implemented for non-indexed meshes"); } // Flip winding of indexed mesh by swapping vertex index 0 and 1 for each triangle. @@ -1832,7 +1912,7 @@ namespace Falcor displacedInstancedCount += it.second.size(); } if ((instancedCount + displacedInstancedCount) != instancedMeshCount || - (instancedMeshes.size() + displacedInstancedMeshes.size()) != instancedMeshCount) throw RuntimeError("Error in instanced mesh grouping logic"); + (instancedMeshes.size() + displacedInstancedMeshes.size()) != instancedMeshCount) FALCOR_THROW("Error in instanced mesh grouping logic"); logInfo("Found {} static non-instanced meshes, arranged in 1 mesh group.", staticMeshes.size()); logInfo("Found {} displaced non-instanced meshes, arranged in 1 mesh group.", staticDisplacedMeshes.size()); @@ -1904,11 +1984,11 @@ namespace Falcor // Check if mesh is supported. if (mesh.isDynamic()) { - throw RuntimeError("Cannot split mesh '{}', only non-dynamic meshes supported", mesh.name); + FALCOR_THROW("Cannot split mesh '{}', only non-dynamic meshes supported", mesh.name); } if (mesh.topology != Vao::Topology::TriangleList) { - throw RuntimeError("Cannot split mesh '{}', only triangle list topology supported", mesh.name); + FALCOR_THROW("Cannot split mesh '{}', only triangle list topology supported", mesh.name); } // Early out if mesh is fully on either side of the splitting plane. @@ -2029,7 +2109,7 @@ namespace Falcor void SceneBuilder::splitNonIndexedMesh(const MeshSpec& mesh, MeshSpec& leftMesh, MeshSpec& rightMesh, const int axis, const float pos) { FALCOR_ASSERT(mesh.indexCount == 0 && mesh.indexData.empty()); - throw RuntimeError("SceneBuilder::splitNonIndexedMesh() not implemented"); + FALCOR_THROW("SceneBuilder::splitNonIndexedMesh() not implemented"); } size_t SceneBuilder::countTriangles(const MeshGroup& meshGroup) const @@ -2329,7 +2409,7 @@ namespace Falcor totalStaticVertexCount > std::numeric_limits::max() || totalSkinningVertexCount > std::numeric_limits::max()) { - throw RuntimeError("Trying to build a scene that exceeds supported mesh data size."); + FALCOR_THROW("Trying to build a scene that exceeds supported mesh data size."); } mSceneData.meshIndexData.reserve(totalIndexDataCount); @@ -2409,7 +2489,7 @@ namespace Falcor if (totalIndexDataCount > std::numeric_limits::max() || totalStaticCurveVertexCount > std::numeric_limits::max()) { - throw RuntimeError("Trying to build a scene that exceeds supported curve data size."); + FALCOR_THROW("Trying to build a scene that exceeds supported curve data size."); } mSceneData.curveIndexData.reserve(totalIndexDataCount); @@ -2759,7 +2839,7 @@ namespace Falcor { const auto& curve = mCurves[curveID]; size_t instanceCount = curve.instances.size(); - if (instanceCount > 1) throw RuntimeError("Instanced curves are currently not supported!"); + if (instanceCount > 1) FALCOR_THROW("Instanced curves are currently not supported!"); maxInstanceCount = std::max(maxInstanceCount, instanceCount); auto instIter = curve.instances.cbegin(); for (size_t instanceIdx = 0; instanceIdx < instanceCount; instanceIdx++, instIter++) @@ -2838,6 +2918,7 @@ namespace Falcor FALCOR_SCRIPT_BINDING_DEPENDENCY(AABB) FALCOR_SCRIPT_BINDING_DEPENDENCY(GridVolume) FALCOR_SCRIPT_BINDING_DEPENDENCY(Settings) + FALCOR_SCRIPT_BINDING_DEPENDENCY(AssetResolver) pybind11::enum_ flags(m, "SceneBuilderFlags"); flags.value("Default", SceneBuilder::Flags::Default); @@ -2875,7 +2956,7 @@ namespace Falcor sceneBuilder.def_property("selectedCamera", &SceneBuilder::getSelectedCamera, &SceneBuilder::setSelectedCamera); sceneBuilder.def_property("cameraSpeed", &SceneBuilder::getCameraSpeed, &SceneBuilder::setCameraSpeed); sceneBuilder.def("importScene", &SceneBuilder::import, "path"_a, "dict"_a = pybind11::dict()); - sceneBuilder.def("addTriangleMesh", &SceneBuilder::addTriangleMesh, "triangleMesh"_a, "material"_a); + sceneBuilder.def("addTriangleMesh", &SceneBuilder::addTriangleMesh, "triangleMesh"_a, "material"_a, "isAnimated"_a = false); sceneBuilder.def("addSDFGrid", &SceneBuilder::addSDFGrid, "sdfGrid"_a, "material"_a); sceneBuilder.def("addMaterial", &SceneBuilder::addMaterial, "material"_a); sceneBuilder.def("replaceMaterial", &SceneBuilder::replaceMaterial, "material"_a, "replacement"_a); @@ -2893,7 +2974,7 @@ namespace Falcor sceneBuilder.def("addAnimation", &SceneBuilder::addAnimation, "animation"_a); sceneBuilder.def("createAnimation", &SceneBuilder::createAnimation, "animatable"_a, "name"_a, "duration"_a); sceneBuilder.def("addNode", [] (SceneBuilder* pSceneBuilder, const std::string& name, const Transform& transform, NodeID parent) { - checkArgument(pSceneBuilder, "'pSceneBuilder' is missing"); + FALCOR_CHECK(pSceneBuilder, "'pSceneBuilder' is missing"); SceneBuilder::Node node; node.name = name; node.transform = transform.getMatrix(); @@ -2905,5 +2986,6 @@ namespace Falcor sceneBuilder.def("addCustomPrimitive", &SceneBuilder::addCustomPrimitive); sceneBuilder.def("getSettings", static_cast(&SceneBuilder::getSettings), pybind11::return_value_policy::reference); + sceneBuilder.def_property_readonly("assetResolver", &SceneBuilder::getAssetResolver, pybind11::return_value_policy::reference); } } diff --git a/Source/Falcor/Scene/SceneBuilder.h b/Source/Falcor/Scene/SceneBuilder.h index d42eea906..86d5853f2 100644 --- a/Source/Falcor/Scene/SceneBuilder.h +++ b/Source/Falcor/Scene/SceneBuilder.h @@ -36,6 +36,7 @@ #include "Material/MaterialTextureLoader.h" #include "Core/Macros.h" +#include "Core/AssetResolver.h" #include "Core/API/VAO.h" #include "Utils/Math/AABB.h" #include "Utils/Math/Vector.h" @@ -123,6 +124,7 @@ namespace Falcor Attribute boneWeights; ///< Array of bone weights. This field is optional. If it's set, that means that the mesh is animated, in which case boneIDs is required. bool isFrontFaceCW = false; ///< Indicate whether front-facing side has clockwise winding in object space. + bool isAnimated = false; ///< True if the mesh vertices can be modified during rendering (e.g., skinning or inverse rendering). bool useOriginalTangentSpace = false; ///< Indicate whether to use the original tangent space that was loaded with the mesh. By default, we will ignore it and use MikkTSpace to generate the tangent space. bool mergeDuplicateVertices = true; ///< Indicate whether to merge identical vertices and adjust indices. NodeID skeletonNodeId{ NodeID::Invalid() }; ///< For skinned meshes, the node ID of the skeleton's world transform. If invalid, the skeleton is based on the mesh's own world position (Assimp behavior pre-multiplies instance transform). @@ -140,8 +142,8 @@ namespace Falcor return pIndices[face * 3 + vert]; case AttributeFrequency::FaceVarying: return face * 3 + vert; - default: - FALCOR_UNREACHABLE(); + case AttributeFrequency::None: + return Scene::kInvalidAttributeIndex; } return Scene::kInvalidAttributeIndex; } @@ -179,8 +181,8 @@ namespace Falcor return vertexCount; case AttributeFrequency::FaceVarying: return 3 * faceCount; - default: - FALCOR_UNREACHABLE(); + case AttributeFrequency::None: + return 0; } return 0; } @@ -272,6 +274,7 @@ namespace Falcor uint64_t indexCount = 0; ///< Number of indices, or zero if non-indexed. bool use16BitIndices = false; ///< True if the indices are in 16-bit format. bool isFrontFaceCW = false; ///< Indicate whether front-facing side has clockwise winding in object space. + bool isAnimated = false; ///< True if the mesh vertices can be modified during rendering (e.g., skinning or inverse rendering). std::vector indexData; ///< Vertex indices in either 32-bit or 16-bit format packed tightly, or empty if non-indexed. std::vector staticData; std::vector skinningData; @@ -355,6 +358,15 @@ namespace Falcor */ void importFromMemory(const void* buffer, size_t byteSize, std::string_view extension, const pybind11::dict& dict = pybind11::dict()); + /// Access the current asset resolver (on top of the stack). + AssetResolver& getAssetResolver() { return mAssetResolver; } + + /// Push the state of the asset resolver to the stack. + void pushAssetResolver(); + + /// Pop the state of the asset resolver from the stack. + void popAssetResolver(); + /** Get the scene. Make sure to add all the objects before calling this function \return nullptr if something went wrong, otherwise a new Scene object */ @@ -405,23 +417,25 @@ namespace Falcor /** Add a triangle mesh. \param The triangle mesh to add. \param pMaterial The material to use for the mesh. + \param isAnimated True if the mesh vertices can be modified during rendering (e.g., skinning or inverse rendering). \return The ID of the mesh in the scene. */ - MeshID addTriangleMesh(const ref& pTriangleMesh, const ref& pMaterial); + MeshID addTriangleMesh(const ref& pTriangleMesh, const ref& pMaterial, bool isAnimated = false); /** Pre-process a mesh into the data format that is used in the global scene buffers. Throws an exception if something went wrong. \param mesh The mesh to pre-process. \param pAttributeIndices Optional. If specified, the attribute indices used to create the final mesh vertices will be saved here. + \param pTangents Optional. When specified and processMesh creates tangents for the mesh, the tangents are also stored in this parameter. \return The pre-processed mesh. */ - ProcessedMesh processMesh(const Mesh& mesh, MeshAttributeIndices* pAttributeIndices = nullptr) const; + ProcessedMesh processMesh(const Mesh& mesh, MeshAttributeIndices* pAttributeIndices = nullptr, std::vector* pTangents = nullptr) const; /** Generate tangents for a mesh. \param mesh The mesh to generate tangents for. If successful, the tangent attribute on the mesh will be set to the output vector. \param tangents Output for generated tangents. */ - void generateTangents(Mesh& mesh, std::vector& tangents) const; + static void generateTangents(Mesh& mesh, std::vector& tangents); /** Add a pre-processed mesh. \param mesh The pre-processed mesh. @@ -429,10 +443,11 @@ namespace Falcor */ MeshID addProcessedMesh(const ProcessedMesh& mesh); - /** Set mesh vertex cache for animation. + /** Add mesh vertex cache for animation. \param[in] cachedCurves The mesh vertex cache data (will be moved from). */ - void setCachedMeshes(std::vector&& cachedMeshes); + void addCachedMeshes(std::vector&& cachedMeshes); + void addCachedMesh(CachedMesh&& cachedMesh); // Custom primitives @@ -467,7 +482,8 @@ namespace Falcor /** Set curve vertex cache for animation. \param[in] cachedCurves The dynamic curve vertex cache data. */ - void setCachedCurves(std::vector&& cachedCurves) { mSceneData.cachedCurves = std::move(cachedCurves); } + void addCachedCurves(std::vector&& cachedCurves); + void addCachedCurve(CachedCurve&& cachedCurves); // SDFs @@ -687,7 +703,7 @@ namespace Falcor bool isStatic = false; ///< True if mesh is non-instanced and static (not dynamic or animated). bool isFrontFaceCW = false; ///< Indicate whether front-facing side has clockwise winding in object space. bool isDisplaced = false; ///< True if mesh has displacement map. - bool isAnimated = false; ///< True if mesh has vertex animations. + bool isAnimated = false; ///< True if the mesh vertices can be modified during rendering (e.g., skinning or inverse rendering). AABB boundingBox; ///< Mesh bounding-box in object space. std::set instances; ///< IDs of all nodes that instantiate this mesh. @@ -750,6 +766,9 @@ namespace Falcor Settings mSettings; const Flags mFlags; + AssetResolver mAssetResolver; + std::vector mAssetResolverStack; + Scene::SceneData mSceneData; ref mpScene; SceneCache::Key mSceneCacheKey; @@ -763,7 +782,6 @@ namespace Falcor CurveList mCurves; std::unique_ptr mpMaterialTextureLoader; - ref mpFence; // Helpers bool doesNodeHaveAnimation(NodeID nodeID) const; @@ -820,6 +838,7 @@ namespace Falcor void calculateCurveBoundingBoxes(); friend class SceneCache; + friend class SceneBuilderDump; }; FALCOR_ENUM_CLASS_OPERATORS(SceneBuilder::Flags); diff --git a/Source/Falcor/Scene/SceneBuilderDump.cpp b/Source/Falcor/Scene/SceneBuilderDump.cpp new file mode 100644 index 000000000..e47478092 --- /dev/null +++ b/Source/Falcor/Scene/SceneBuilderDump.cpp @@ -0,0 +1,301 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "SceneBuilderDump.h" +#include "Scene/SceneBuilder.h" +#include "Utils/Math/FNVHash.h" +#include +#include + +/// SceneBuilder printing is split off to its own file to avoid polluting the SceneBuilder.cpp with debug prints + +namespace Falcor +{ + +namespace +{ +std::string printMatrix(const float4x4& m) +{ + if (m == float4x4::identity()) + return "identity"; + return fmt::format("{}", m); +} + +std::string printNodeID(NodeID id) +{ + if (!id.isValid()) + return "N/A"; + return fmt::format("{}", id); +} + +std::string toString(const StaticVertexData& vertex) +{ + return fmt::format("pos: {}; nrm: {}; tan: {}; uvs: {}", vertex.position, vertex.normal, vertex.tangent, vertex.texCrd); +} + +std::string toString(const PackedStaticVertexData& vertex) +{ + return toString(vertex.unpack()); +} + +std::string toString(const StaticCurveVertexData& vertex) +{ + return fmt::format("pos: {}; rad: {}; uvs: {}", vertex.position, vertex.radius, vertex.texCrd); +} + +std::string toString(const DynamicCurveVertexData& vertex) +{ + return fmt::format("pos: {}", vertex.position); +} + +template +uint64_t hash64(const std::vector& v) +{ + return fnvHashArray64(v.data(), v.size() * sizeof(T)); +} +} // namespace + +std::map SceneBuilderDump::getDebugContent(const SceneBuilder& sceneBuilder) +{ + struct FullMeshDesc + { + MeshID id; + const SceneBuilder::MeshSpec* meshSpec; + const CachedMesh* cachedMesh{nullptr}; + const CachedCurve* cachedCurve{nullptr}; // when using polytube hair + std::string deduplicatedName; + }; + + struct FullCurveDesc + { + CurveID id; + const SceneBuilder::CurveSpec* curveSpec; + const CachedCurve* cachedCurve{nullptr}; + std::string deduplicatedName; + }; + + std::vector sortedMeshes(sceneBuilder.mMeshes.size()); + std::vector sortedCurves(sceneBuilder.mCurves.size()); + + for (size_t i = 0; i < sceneBuilder.mMeshes.size(); ++i) + { + sortedMeshes[i].id = MeshID{i}; + sortedMeshes[i].meshSpec = &sceneBuilder.mMeshes[i]; + } + + for (size_t i = 0; i < sceneBuilder.mCurves.size(); ++i) + { + sortedCurves[i].id = CurveID{i}; + sortedCurves[i].curveSpec = &sceneBuilder.mCurves[i]; + } + + for (size_t i = 0; i < sceneBuilder.mSceneData.cachedCurves.size(); ++i) + { + const CachedCurve& cached = sceneBuilder.mSceneData.cachedCurves[i]; + if (cached.tessellationMode == CurveTessellationMode::LinearSweptSphere) + { + sortedCurves[cached.geometryID.get()].cachedCurve = &sceneBuilder.mSceneData.cachedCurves[i]; + } + else + { + sortedMeshes[cached.geometryID.get()].cachedCurve = &sceneBuilder.mSceneData.cachedCurves[i]; + } + } + + for (size_t i = 0; i < sceneBuilder.mSceneData.cachedMeshes.size(); ++i) + { + const CachedMesh& cached = sceneBuilder.mSceneData.cachedMeshes[i]; + sortedMeshes[cached.meshID.get()].cachedCurve = &sceneBuilder.mSceneData.cachedCurves[i]; + } + + std::sort( + sortedMeshes.begin(), + sortedMeshes.end(), + [](const FullMeshDesc& lhs, const FullMeshDesc& rhs) { return lhs.meshSpec->name < rhs.meshSpec->name; } + ); + + std::sort( + sortedCurves.begin(), + sortedCurves.end(), + [](const FullCurveDesc& lhs, const FullCurveDesc& rhs) { return lhs.curveSpec->name < rhs.curveSpec->name; } + ); + + std::map result; + auto deduplicateName = [&](std::string name) -> std::string + { + if (auto it = result.find(name); it == result.end()) + return name; + int counter = 0; + while (true) + { + std::string temp = fmt::format("{}_{}", name, counter); + if (auto it = result.find(temp); it == result.end()) + return temp; + ++counter; + } + }; + + auto sanityName = [&](std::string name) -> std::string + { + auto pos = name.find_first_of("Prototype"); + if (pos == std::string::npos) + return deduplicateName(name); + pos = name.find_first_of("/", pos); + if (pos == std::string::npos) + return deduplicateName(name); + name.erase(name.begin(), name.begin() + pos); + return deduplicateName(name); + }; + + for (auto& it : sortedMeshes) + { + it.deduplicatedName = sanityName(it.meshSpec->name);; + result[it.deduplicatedName] = it.deduplicatedName; + } + for (auto& it : sortedCurves) + { + it.deduplicatedName = sanityName(it.curveSpec->name);; + result[it.deduplicatedName] = it.deduplicatedName; + } + + std::mutex resultMutex; + auto genMesh = [&](int i) + { + const auto& meshDesc = sortedMeshes[i]; + const SceneBuilder::MeshSpec& mesh = *meshDesc.meshSpec; + std::string name = meshDesc.deduplicatedName; + + std::string res; + res += fmt::format("Mesh: {}\n", name); + res += fmt::format(" material: {}\n", sceneBuilder.mSceneData.pMaterials->getMaterial(mesh.materialId)->getName()); + res += fmt::format(" skinned: {}\n", mesh.isSkinned() ? "YES" : "NO"); + res += fmt::format(" mesh.staticData: {}\n", hash64(mesh.staticData)); + // res += fmt::format(" mesh.staticData:\n"); + // for (auto& it : mesh.staticData) + // res += fmt::format(" {}\n", toString(it)); + res += fmt::format(" mesh.indexData: {}\n", hash64(mesh.indexData)); + if (mesh.isSkinned()) + { + NodeID bindMatrixID = *mesh.instances.begin(); + if (bindMatrixID.isValid()) + res += fmt::format(" bindXform: {}\n", printMatrix(sceneBuilder.mSceneGraph[bindMatrixID.get()].meshBind)); + + NodeID skeletonMatrixID = (mesh.skeletonNodeID == NodeID::Invalid()) ? *mesh.instances.begin() : mesh.skeletonNodeID; + if (skeletonMatrixID.isValid()) + res += fmt::format(" skeletonXform: {}\n", printMatrix(sceneBuilder.mSceneGraph[skeletonMatrixID.get()].transform)); + + for (size_t j = 0; j < mesh.skinningData.size(); ++j) + { + const SkinningVertexData& skinningData = mesh.skinningData[j]; + res += fmt::format(" Vertex#{}\n", j); + for (int k = 0; k < 4; ++k) + { + NodeID boneID{skinningData.boneID[k]}; + float weight = skinningData.boneWeight[k]; + if (weight == 0 || !boneID.isValid()) + continue; + res += fmt::format(" Bone#{}\n", k); + res += fmt::format(" boneXform: {} x w{}\n", printMatrix(sceneBuilder.mSceneGraph[boneID.get()].transform), weight); + } + } + } + + if (meshDesc.cachedMesh) + { + res += " Cached meshes:"; + for (auto t : meshDesc.cachedCurve->timeSamples) + res += fmt::format(" {}", t); + res += "\n"; + + for (size_t t = 0; t < meshDesc.cachedMesh->timeSamples.size(); ++t) + res += fmt::format(" t{}: {}\n", t, hash64(meshDesc.cachedMesh->vertexData[t])); + } + + if (meshDesc.cachedCurve) + { + const auto& cached = *meshDesc.cachedCurve; + + res += " Cached curves:\n"; + for (auto t : cached.timeSamples) + res += fmt::format(" {}", t); + res += "\n"; + res += fmt::format(" Index data: {}\n", hash64(cached.indexData)); + + for (size_t t = 0; t < cached.timeSamples.size(); ++t) + res += fmt::format(" t{}: {}\n", t, hash64(cached.vertexData[t])); + } + std::lock_guard lock(resultMutex); + result[name] = std::move(res); + }; + + auto genCurve = [&](int i) + { + const auto& curveDesc = sortedCurves[i]; + const SceneBuilder::CurveSpec& curve = *curveDesc.curveSpec; + std::string name = curveDesc.deduplicatedName; + + std::string res; + res += fmt::format("Curve: {}\n", name); + res += fmt::format(" material: {}\n", sceneBuilder.mSceneData.pMaterials->getMaterial(curve.materialId)->getName()); + res += fmt::format(" staticVertexCount: {}\n", curve.staticVertexCount); + res += fmt::format(" indexCount: {}\n", curve.indexCount); + res += fmt::format(" vertexCount: {}\n", curve.vertexCount); + res += fmt::format(" degree: {}\n", curve.degree); + res += fmt::format(" curve.staticData: {}\n", hash64(curve.staticData)); + res += fmt::format(" curve.indexData: {}\n", hash64(curve.indexData)); + + if (curveDesc.cachedCurve) + { + const auto& cached = *curveDesc.cachedCurve; + + res += " Cached curves:\n"; + for (auto t : cached.timeSamples) + res += fmt::format(" {}", t); + res += "\n"; + res += fmt::format(" Index data: {}\n", hash64(cached.indexData)); + + for (size_t t = 0; t < cached.timeSamples.size(); ++t) + res += fmt::format(" t{}: {}\n", t, hash64(cached.vertexData[t])); + } + + std::lock_guard lock(resultMutex); + result[name] = std::move(res); + }; + + BS::thread_pool threadPool; + + for (size_t i = 0; i < sortedMeshes.size(); ++i) + threadPool.push_task([&,i]{ genMesh(i); }); + for (size_t i = 0; i < sortedCurves.size(); ++i) + threadPool.push_task([&,i]{ genCurve(i); }); + + threadPool.wait_for_tasks(); + + return result; +} + +} // namespace Falcor diff --git a/Source/Falcor/Scene/SceneBuilderDump.h b/Source/Falcor/Scene/SceneBuilderDump.h new file mode 100644 index 000000000..12256a89e --- /dev/null +++ b/Source/Falcor/Scene/SceneBuilderDump.h @@ -0,0 +1,45 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include +#include + +namespace Falcor +{ + +class SceneBuilder; + +/// Used to dump content of SceneBuilder for debugging purposes. +class SceneBuilderDump +{ +public: + /// Returns pairs of geometry and its serialization to text used for debugging. + /// The interface as well as output are unstable and depend on the latest debugging needs + static std::map getDebugContent(const SceneBuilder& sceneBuilder); +}; + +} // namespace Falcor diff --git a/Source/Falcor/Scene/SceneCache.cpp b/Source/Falcor/Scene/SceneCache.cpp index 1209dc69a..47c41e741 100644 --- a/Source/Falcor/Scene/SceneCache.cpp +++ b/Source/Falcor/Scene/SceneCache.cpp @@ -218,7 +218,7 @@ namespace Falcor // Open file. std::ofstream fs(cachePath.c_str(), std::ios_base::binary); - if (fs.bad()) throw RuntimeError("Failed to create scene cache file '{}'.", cachePath); + if (fs.bad()) FALCOR_THROW("Failed to create scene cache file '{}'.", cachePath); // Write header (uncompressed). Header header; @@ -230,7 +230,7 @@ namespace Falcor lz4_stream::basic_ostream zs(fs); OutputStream stream(zs); writeSceneData(stream, sceneData); - if (fs.bad()) throw RuntimeError("Failed to write scene cache file to '{}'.", cachePath); + if (fs.bad()) FALCOR_THROW("Failed to write scene cache file to '{}'.", cachePath); } Scene::SceneData SceneCache::readCache(ref pDevice, const Key& key) @@ -241,18 +241,18 @@ namespace Falcor // Open file. std::ifstream fs(cachePath.c_str(), std::ios_base::binary); - if (fs.bad()) throw RuntimeError("Failed to open scene cache file '{}'.", cachePath); + if (fs.bad()) FALCOR_THROW("Failed to open scene cache file '{}'.", cachePath); // Read header (uncompressed). Header header; fs.read(reinterpret_cast(&header), sizeof(header)); - if (!header.isValid()) throw RuntimeError("Invalid header in scene cache file '{}'.", cachePath); + if (!header.isValid()) FALCOR_THROW("Invalid header in scene cache file '{}'.", cachePath); // Read cache (compressed). lz4_stream::basic_istream zs(fs); InputStream stream(zs); auto sceneData = readSceneData(stream, pDevice); - if (fs.bad()) throw RuntimeError("Failed to read scene cache file from '{}'.", cachePath); + if (fs.bad()) FALCOR_THROW("Failed to read scene cache file from '{}'.", cachePath); return sceneData; } @@ -688,7 +688,7 @@ namespace Falcor // Write data in derived class. if (auto pBasicMaterial = pMaterial->toBasicMaterial()) writeBasicMaterial(stream, pBasicMaterial); - else throw RuntimeError("Unsupported material type"); + else FALCOR_THROW("Unsupported material type"); } void SceneCache::writeBasicMaterial(OutputStream& stream, const ref& pMaterial) @@ -735,7 +735,7 @@ namespace Falcor pMaterial = ClothMaterial::create(pDevice, ""); break; default: - throw RuntimeError("Unsupported material type"); + FALCOR_THROW("Unsupported material type"); } } FALCOR_ASSERT(pMaterial); @@ -763,7 +763,7 @@ namespace Falcor // Read data in derived class. if (auto pBasicMaterial = pMaterial->toBasicMaterial()) readBasicMaterial(stream, materialTextureLoader, pBasicMaterial, pDevice); - else throw RuntimeError("Unsupported material type"); + else FALCOR_THROW("Unsupported material type"); return pMaterial; } @@ -797,7 +797,7 @@ namespace Falcor if (valid) { auto desc = stream.read(); - return Sampler::create(pDevice, desc); + return pDevice->createSampler(desc); } return nullptr; } @@ -883,7 +883,7 @@ namespace Falcor { auto path = stream.read(); auto pEnvMap = EnvMap::createFromFile(pDevice, path); - if (!pEnvMap) throw RuntimeError("Failed to load environment map"); + if (!pEnvMap) FALCOR_THROW("Failed to load environment map"); stream.read(pEnvMap->mData); stream.read(pEnvMap->mRotation); return pEnvMap; @@ -945,6 +945,6 @@ namespace Falcor void SceneCache::readMarker(InputStream& stream, const std::string& id) { auto str = stream.read(); - if (id != str) throw RuntimeError("Found invalid marker"); + if (id != str) FALCOR_THROW("Found invalid marker"); } } diff --git a/Source/Falcor/Scene/SceneIDs.h b/Source/Falcor/Scene/SceneIDs.h index 6dfdb091b..dfc997d0f 100644 --- a/Source/Falcor/Scene/SceneIDs.h +++ b/Source/Falcor/Scene/SceneIDs.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,7 +29,8 @@ #include "Utils/ObjectID.h" #include -namespace Falcor +// Scene1 IDs are now defined in scene1 namespace, so places that need to distinguish multiple MeshIDs can. +namespace Falcor::scene1 { enum class SceneObjectKind { @@ -59,3 +60,9 @@ namespace Falcor using VolumeID = ObjectID; using GlobalGeometryID = ObjectID; } + +// scene1 is by default included in Falcor namespace to keep ensure that everything works are when IDs were directly in Falcor namespace +namespace Falcor +{ +using namespace scene1; +} diff --git a/Source/Falcor/Scene/SceneTypes.slang b/Source/Falcor/Scene/SceneTypes.slang index 396ce459c..731ae9b08 100644 --- a/Source/Falcor/Scene/SceneTypes.slang +++ b/Source/Falcor/Scene/SceneTypes.slang @@ -162,6 +162,11 @@ struct MeshDesc uint materialID; ///< Material ID. uint flags; ///< See MeshFlags. + uint getVertexCount() CONST_FUNCTION + { + return vertexCount; + } + uint getTriangleCount() CONST_FUNCTION { return (indexCount > 0 ? indexCount : vertexCount) / 3; diff --git a/Source/Falcor/Scene/ShadingData.slang b/Source/Falcor/Scene/ShadingData.slang index 765c76def..a0b9a5cd9 100644 --- a/Source/Falcor/Scene/ShadingData.slang +++ b/Source/Falcor/Scene/ShadingData.slang @@ -44,24 +44,27 @@ import Utils.Math.ShadingFrame; The material instance has interfaces for sampling and evaluation, as well as for querying its properties at the hit. */ -struct ShadingData +struct ShadingData : IDifferentiable { // Geometry data - float3 posW; ///< Shading hit position in world space. - float3 V; ///< View direction, -incident direction (-ray.dir) - float2 uv; ///< Texture mapping coordinates. + float3 posW; ///< Shading hit position in world space. + float3 V; ///< View direction, -incident direction (-ray.dir) + float2 uv; ///< Texture mapping coordinates. - ShadingFrame frame; ///< Smooth interpolated shading frame in world space at the shading point. The normal is *not* automatically flipped for backfacing hits. - float3 faceN; ///< Face normal in world space, always on the front-facing side. - float4 tangentW; ///< Geometric tangent (xyz) and sign (w) in world space. This is used for orthogonalization. Not normalized, but it is guaranteed to be nonzero and sign (w) is +-1.0. - bool frontFacing; ///< True if primitive seen from the front-facing side, i.e., dot(V, faceN) >= 0.0. - float curveRadius; ///< Curve cross-sectional radius. Valid only for geometry generated from curves. + ShadingFrame frame; ///< Smooth interpolated shading frame in world space at the shading point. The normal is *not* automatically flipped for backfacing hits. + no_diff float3 faceN; ///< Face normal in world space, always on the front-facing side. + no_diff float4 tangentW; ///< Geometric tangent (xyz) and sign (w) in world space. This is used for orthogonalization. Not normalized, but it is guaranteed to be nonzero and sign (w) is +-1.0. + bool frontFacing; ///< True if primitive seen from the front-facing side, i.e., dot(V, faceN) >= 0.0. + no_diff float curveRadius; ///< Curve cross-sectional radius. Valid only for geometry generated from curves. // Material data - MaterialHeader mtl; ///< Material header data. - uint materialID; ///< Material ID at shading location. - float opacity; ///< Opacity value in [0,1]. This is used for alpha testing. - float IoR; ///< Index of refraction for the medium on the front-facing side (i.e. "outside" the material at the hit). + MaterialHeader mtl; ///< Material header data. + uint materialID; ///< Material ID at shading location. + float IoR; ///< Index of refraction for the medium on the front-facing side (i.e. "outside" the material at the hit). + + uint materialGradOffset; ///< Offset to the material gradient in the gradient buffer. + uint geometryGradOffset; ///< Offset to the geometry gradient in the gradient buffer. + uint threadID; ///< Thread ID for gradient aggregation with a hash grid. // Utility functions @@ -72,7 +75,7 @@ struct ShadingData \param[in] viewside True if the origin should be on the view side (reflection) or false otherwise (transmission). \return Ray origin of the new ray. */ - float3 computeNewRayOrigin(bool viewside = true) + float3 computeRayOrigin(bool viewside = true) { return computeRayOrigin(posW, (frontFacing == viewside) ? faceN : -faceN); } diff --git a/Source/Falcor/Scene/Transform.cpp b/Source/Falcor/Scene/Transform.cpp index bbb61b361..cb7808c8d 100644 --- a/Source/Falcor/Scene/Transform.cpp +++ b/Source/Falcor/Scene/Transform.cpp @@ -105,7 +105,7 @@ namespace Falcor mMatrix = mul(mul(R, S), T); break; case CompositionOrder::Unknown: - throw RuntimeError("Unknown transform composition order."); + FALCOR_THROW("Unknown transform composition order."); break; } mDirty = false; @@ -131,7 +131,7 @@ namespace Falcor case Transform::CompositionOrder::TranslateScaleRotate: return Transform::CompositionOrder::RotateScaleTranslate; case Transform::CompositionOrder::Unknown: - throw RuntimeError("Cannot invert unknown transform composition order."); + FALCOR_THROW("Cannot invert unknown transform composition order."); } return Transform::CompositionOrder::Unknown; } diff --git a/Source/Falcor/Scene/TriangleMesh.cpp b/Source/Falcor/Scene/TriangleMesh.cpp index 331e9a83d..298c94bf0 100644 --- a/Source/Falcor/Scene/TriangleMesh.cpp +++ b/Source/Falcor/Scene/TriangleMesh.cpp @@ -26,7 +26,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "TriangleMesh.h" -#include "Core/Assert.h" +#include "GlobalState.h" +#include "Core/Error.h" #include "Core/Platform/OS.h" #include "Utils/Logger.h" #include "Utils/Scripting/ScriptBindings.h" @@ -192,12 +193,11 @@ namespace Falcor return create(vertices, indices); } - ref TriangleMesh::createFromFile(const std::filesystem::path& path, bool smoothNormals) + ref TriangleMesh::createFromFile(const std::filesystem::path& path, ImportFlags importFlags) { - std::filesystem::path fullPath; - if (!findFileInDataDirectories(path, fullPath)) + if (!std::filesystem::exists(path)) { - logWarning("Error when loading triangle mesh. Can't find mesh file '{}'.", path); + logWarning("Failed to load triangle mesh from '{}': File not found", path); return nullptr; } @@ -206,24 +206,25 @@ namespace Falcor unsigned int flags = aiProcess_FlipUVs | aiProcess_Triangulate | - (smoothNormals ? aiProcess_GenSmoothNormals : aiProcess_GenNormals) | aiProcess_PreTransformVertices; + flags |= is_set(importFlags, ImportFlags::GenSmoothNormals) ? aiProcess_GenSmoothNormals : aiProcess_GenNormals; + flags |= is_set(importFlags, ImportFlags::JoinIdenticalVertices) ? aiProcess_JoinIdenticalVertices : 0; const aiScene* scene = nullptr; - if (hasExtension(fullPath, "gz")) + if (hasExtension(path, "gz")) { - auto decompressed = decompressFile(fullPath); + auto decompressed = decompressFile(path); scene = importer.ReadFileFromMemory(decompressed.data(), decompressed.size(), flags); } else { - scene = importer.ReadFile(fullPath.string().c_str(), flags); + scene = importer.ReadFile(path.string().c_str(), flags); } if (!scene) { - logWarning("Failed to load triangle mesh from '{}': {}", fullPath, importer.GetErrorString()); + logWarning("Failed to load triangle mesh from '{}': {}", path, importer.GetErrorString()); return nullptr; } @@ -262,7 +263,7 @@ namespace Falcor const auto& face = mesh->mFaces[faceIdx]; if (face.mNumIndices != 3) { - logWarning("Failed to load triangle mesh from '{}': Broken face data", fullPath); + logWarning("Failed to load triangle mesh from '{}': Broken face data", path); return nullptr; } for (size_t i = 0; i < 3; ++i) indices.emplace_back((uint32_t)(indexBase + face.mIndices[i])); @@ -272,6 +273,12 @@ namespace Falcor return create(vertices, indices); } + ref TriangleMesh::createFromFile(const std::filesystem::path& path, bool smoothNormals) + { + ImportFlags flags = smoothNormals ? ImportFlags::GenSmoothNormals : ImportFlags::None; + return createFromFile(path, flags); + } + uint32_t TriangleMesh::addVertex(float3 position, float3 normal, float2 texCoord) { mVertices.emplace_back(Vertex{position, normal, texCoord}); @@ -319,6 +326,12 @@ namespace Falcor { using namespace pybind11::literals; + pybind11::enum_ flags(m, "TriangleMeshImportFlags"); + flags.value("Default", TriangleMesh::ImportFlags::Default); + flags.value("GenSmoothNormals", TriangleMesh::ImportFlags::GenSmoothNormals); + flags.value("JoinIdenticalVertices", TriangleMesh::ImportFlags::JoinIdenticalVertices); + ScriptBindings::addEnumBinaryOperators(flags); + pybind11::class_> triangleMesh(m, "TriangleMesh"); pybind11::class_ vertex(triangleMesh, "Vertex"); @@ -337,6 +350,15 @@ namespace Falcor triangleMesh.def_static("createDisk", &TriangleMesh::createDisk, "radius"_a = 1.f, "segments"_a = 32); triangleMesh.def_static("createCube", &TriangleMesh::createCube, "size"_a = float3(1.f)); triangleMesh.def_static("createSphere", &TriangleMesh::createSphere, "radius"_a = 1.f, "segmentsU"_a = 32, "segmentsV"_a = 32); - triangleMesh.def_static("createFromFile", &TriangleMesh::createFromFile, "path"_a, "smoothNormals"_a = false); + triangleMesh.def_static("createFromFile", + [](const std::filesystem::path& path, bool smoothNormals) + { return TriangleMesh::createFromFile(getActiveAssetResolver().resolvePath(path), smoothNormals); }, + "path"_a, "smoothNormals"_a = false + ); // PYTHONDEPRECATED + triangleMesh.def_static("createFromFile", + [](const std::filesystem::path& path, TriangleMesh::ImportFlags importFlags) + { return TriangleMesh::createFromFile(getActiveAssetResolver().resolvePath(path), importFlags); }, + "path"_a, "importFlags"_a + ); // PYTHONDEPRECATED } } diff --git a/Source/Falcor/Scene/TriangleMesh.h b/Source/Falcor/Scene/TriangleMesh.h index 2d55c4052..4ba1a77ae 100644 --- a/Source/Falcor/Scene/TriangleMesh.h +++ b/Source/Falcor/Scene/TriangleMesh.h @@ -46,6 +46,15 @@ namespace Falcor { FALCOR_OBJECT(TriangleMesh) public: + enum class ImportFlags + { + None = 0x0, + GenSmoothNormals = 0x1, + JoinIdenticalVertices = 0x2, + + Default = None + }; + struct Vertex { float3 position; @@ -103,7 +112,16 @@ namespace Falcor /** Creates a triangle mesh from a file. This is using ASSIMP to support a wide variety of asset formats. All geometry found in the asset is pre-transformed and merged into the same triangle mesh. - \param[in] path File path to load mesh from. + \param[in] path File path to load mesh from (absolute or relative to working directory). + \param[in] flags Flags controlling ASSIMP mesh import options. + \return Returns the triangle mesh or nullptr if the mesh failed to load. + */ + static ref createFromFile(const std::filesystem::path& path, ImportFlags flags); + + /** Creates a triangle mesh from a file. + This is using ASSIMP to support a wide variety of asset formats. + All geometry found in the asset is pre-transformed and merged into the same triangle mesh. + \param[in] path File path to load mesh from (absolute or relative to working directory). \param[in] smoothNormals If no normals are defined in the model, generate smooth instead of facet normals. \return Returns the triangle mesh or nullptr if the mesh failed to load. */ @@ -177,4 +195,6 @@ namespace Falcor std::vector mIndices; bool mFrontFaceCW = false; }; + + FALCOR_ENUM_CLASS_OPERATORS(TriangleMesh::ImportFlags); } diff --git a/Source/Falcor/Scene/Volume/Grid.cpp b/Source/Falcor/Scene/Volume/Grid.cpp index fa9e06085..65579de08 100644 --- a/Source/Falcor/Scene/Volume/Grid.cpp +++ b/Source/Falcor/Scene/Volume/Grid.cpp @@ -91,24 +91,23 @@ namespace Falcor ref Grid::createFromFile(ref pDevice, const std::filesystem::path& path, const std::string& gridname) { - std::filesystem::path fullPath; - if (!findFileInDataDirectories(path, fullPath)) + if (!std::filesystem::exists(path)) { - logWarning("Error when loading grid. Can't find grid file '{}'.", path); + logWarning("Error when loading grid. Can't open grid file '{}'.", path); return nullptr; } - if (hasExtension(fullPath, "nvdb")) + if (hasExtension(path, "nvdb")) { - return createFromNanoVDBFile(pDevice, fullPath, gridname); + return createFromNanoVDBFile(pDevice, path, gridname); } - else if (hasExtension(fullPath, "vdb")) + else if (hasExtension(path, "vdb")) { - return createFromOpenVDBFile(pDevice, fullPath, gridname); + return createFromOpenVDBFile(pDevice, path, gridname); } else { - logWarning("Error when loading grid. Unsupported grid file '{}'.", fullPath); + logWarning("Error when loading grid. Unsupported grid file '{}'.", path); return nullptr; } } @@ -125,7 +124,7 @@ namespace Falcor widget.text(oss.str()); } - void Grid::setShaderData(const ShaderVar& var) + void Grid::bindShaderData(const ShaderVar& var) { var["buf"] = mpBuffer; var["rangeTex"] = mBrickedGrid.range; @@ -215,12 +214,11 @@ namespace Falcor } // Keep both NanoVDB and brick textures resident in GPU memory for simplicity for now (~15% increased footprint). - mpBuffer = Buffer::createStructured( - mpDevice, + mpBuffer = mpDevice->createStructuredBuffer( sizeof(uint32_t), uint32_t(div_round_up(mGridHandle.size(), sizeof(uint32_t))), ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource, - Buffer::CpuAccess::None, + MemoryType::DeviceLocal, mGridHandle.data() ); using NanoVDBGridConverter = NanoVDBConverterBC4; @@ -329,7 +327,7 @@ namespace Falcor auto createFromFile = [] (const std::filesystem::path& path, const std::string& gridname) { - return Grid::createFromFile(accessActivePythonSceneBuilder().getDevice(), path, gridname); + return Grid::createFromFile(accessActivePythonSceneBuilder().getDevice(), getActiveAssetResolver().resolvePath(path), gridname); }; grid.def_static("createFromFile", createFromFile, "path"_a, "gridname"_a); // PYTHONDEPRECATED } diff --git a/Source/Falcor/Scene/Volume/Grid.h b/Source/Falcor/Scene/Volume/Grid.h index b4532aa8c..b70b6d086 100644 --- a/Source/Falcor/Scene/Volume/Grid.h +++ b/Source/Falcor/Scene/Volume/Grid.h @@ -83,7 +83,7 @@ namespace Falcor /** Create a grid from a file. Currently only OpenVDB and NanoVDB grids of type float are supported. \param[in] pDevice GPU device. - \param[in] path File path of the grid. Can also include a full path or relative path from a data directory. + \param[in] path File path of the grid (absolute or relative to working directory). \param[in] gridname Name of the grid to load. \return A new grid, or nullptr if the grid failed to load. */ @@ -96,7 +96,7 @@ namespace Falcor /** Bind the grid to a given shader var. \param[in] var The shader variable to set the data into. */ - void setShaderData(const ShaderVar& var); + void bindShaderData(const ShaderVar& var); /** Get the minimum index stored in the grid. */ diff --git a/Source/Falcor/Scene/Volume/GridConverter.h b/Source/Falcor/Scene/Volume/GridConverter.h index 48948f85f..631c55680 100644 --- a/Source/Falcor/Scene/Volume/GridConverter.h +++ b/Source/Falcor/Scene/Volume/GridConverter.h @@ -81,7 +81,7 @@ namespace Falcor case 4: return ResourceFormat::BC4Unorm; case 8: return ResourceFormat::R8Unorm; case 16: return ResourceFormat::R16Unorm; - default: throw RuntimeError("Unsupported bitdepth in NanoVDBToBricksConverter"); + default: FALCOR_THROW("Unsupported bitdepth in NanoVDBToBricksConverter"); } } @@ -291,9 +291,9 @@ namespace Falcor for (int mip = 1; mip < 4; ++mip) computeMip(mip); BrickedGrid bricks; - bricks.range = Texture::create3D(pDevice, mLeafDim[0].x, mLeafDim[0].y, mLeafDim[0].z, ResourceFormat::RG16Float, 4, mRangeData.data(), ResourceBindFlags::ShaderResource, false); - bricks.indirection = Texture::create3D(pDevice, mLeafDim[0].x, mLeafDim[0].y, mLeafDim[0].z, ResourceFormat::RGBA8Uint, 1, mPtrData.data(), ResourceBindFlags::ShaderResource, false); - bricks.atlas = Texture::create3D(pDevice, getAtlasSizePixels().x, getAtlasSizePixels().y, getAtlasSizePixels().z, getAtlasFormat(), 1, mAtlasData.data(), ResourceBindFlags::ShaderResource, false); + bricks.range = pDevice->createTexture3D(mLeafDim[0].x, mLeafDim[0].y, mLeafDim[0].z, ResourceFormat::RG16Float, 4, mRangeData.data(), ResourceBindFlags::ShaderResource); + bricks.indirection = pDevice->createTexture3D(mLeafDim[0].x, mLeafDim[0].y, mLeafDim[0].z, ResourceFormat::RGBA8Uint, 1, mPtrData.data(), ResourceBindFlags::ShaderResource); + bricks.atlas = pDevice->createTexture3D(getAtlasSizePixels().x, getAtlasSizePixels().y, getAtlasSizePixels().z, getAtlasFormat(), 1, mAtlasData.data(), ResourceBindFlags::ShaderResource); double dt = CpuTimer::calcDuration(t0, CpuTimer::getCurrentTimePoint()); logDebug("Converted '{}' in {:.4}ms: mNonEmptyCount {} vs max {}", mpFloatGrid->gridName(), dt, mNonEmptyCount.load(), getAtlasMaxBrick()); diff --git a/Source/Falcor/Scene/Volume/GridVolume.cpp b/Source/Falcor/Scene/Volume/GridVolume.cpp index dd25d1840..02936b44b 100644 --- a/Source/Falcor/Scene/Volume/GridVolume.cpp +++ b/Source/Falcor/Scene/Volume/GridVolume.cpp @@ -146,13 +146,12 @@ namespace Falcor uint32_t GridVolume::loadGridSequence(GridSlot slot, const std::filesystem::path& path, const std::string& gridname, bool keepEmpty) { - std::filesystem::path fullPath; - if (!findFileInDataDirectories(path, fullPath)) + if (!std::filesystem::exists(path)) { - logWarning("Cannot find directory '{}'.", path); + logWarning("'{}' does not exist.", path); return 0; } - if (!std::filesystem::is_directory(fullPath)) + if (!std::filesystem::is_directory(path)) { logWarning("'{}' is not a directory.", path); return 0; @@ -160,7 +159,7 @@ namespace Falcor // Enumerate grid files. std::vector paths; - for (auto it : std::filesystem::directory_iterator(fullPath)) + for (auto it : std::filesystem::directory_iterator(path)) { if (hasExtension(it.path(), "nvdb") || hasExtension(it.path(), "vdb")) paths.push_back(it.path()); } @@ -402,13 +401,26 @@ namespace Falcor return GridVolume::create(accessActivePythonSceneBuilder().getDevice(), name); }; volume.def(pybind11::init(create), "name"_a); // PYTHONDEPRECATED - volume.def("loadGrid", &GridVolume::loadGrid, "slot"_a, "path"_a, "gridname"_a); + volume.def("loadGrid", + [](GridVolume& self, GridVolume::GridSlot slot, const std::filesystem::path& path, const std::string& gridname) + { return self.loadGrid(slot, getActiveAssetResolver().resolvePath(path), gridname); }, + "slot"_a, "path"_a, "gridname"_a + ); // PYTHONDEPRECATED volume.def("loadGridSequence", - pybind11::overload_cast&, const std::string&, bool>(&GridVolume::loadGridSequence), - "slot"_a, "paths"_a, "gridname"_a, "keepEmpty"_a = true); + [](GridVolume& self, GridVolume::GridSlot slot, const std::vector& paths, const std::string& gridname, bool keepEmpty) + { + std::vector resolvedPaths; + for (const auto& path : paths) + resolvedPaths.push_back(getActiveAssetResolver().resolvePath(path)); + return self.loadGridSequence(slot, resolvedPaths, gridname, keepEmpty); + }, + "slot"_a, "paths"_a, "gridname"_a, "keepEmpty"_a = true + ); // PYTHONDEPRECATED volume.def("loadGridSequence", - pybind11::overload_cast(&GridVolume::loadGridSequence), - "slot"_a, "path"_a, "gridnames"_a, "keepEmpty"_a = true); + [](GridVolume& self, GridVolume::GridSlot slot, const std::filesystem::path& path, const std::string& gridname, bool keepEmpty) + { return self.loadGridSequence(slot, getActiveAssetResolver().resolvePath(path), gridname, keepEmpty); }, + "slot"_a, "path"_a, "gridnames"_a, "keepEmpty"_a = true + ); // PYTHONDEPRECATED m.attr("Volume") = m.attr("GridVolume"); // PYTHONDEPRECATED } diff --git a/Source/Falcor/Testing/UnitTest.cpp b/Source/Falcor/Testing/UnitTest.cpp index 493f63d35..5adfac8ec 100644 --- a/Source/Falcor/Testing/UnitTest.cpp +++ b/Source/Falcor/Testing/UnitTest.cpp @@ -97,18 +97,7 @@ class DevicePool { Device::Desc desc = mDefaultDesc; desc.type = deviceType; - ref device = make_ref(desc); - - // Set global shader defines - DefineList globalDefines = { - {"FALCOR_NVAPI_AVAILABLE", (FALCOR_NVAPI_AVAILABLE && device->getType() == Device::Type::D3D12) ? "1" : "0"}, -#if FALCOR_NVAPI_AVAILABLE - {"NV_SHADER_EXTN_SLOT", "u999"}, -#endif - }; - device->getProgramManager()->addGlobalDefines(globalDefines); - - return device; + return make_ref(desc); } auto device = devices.back(); devices.pop_back(); @@ -286,7 +275,7 @@ inline TestResult runTest(const Test& test, DevicePool& devicePool) if (pDevice) { pDevice->endFrame(); - pDevice->flushAndSync(); + pDevice->wait(); devicePool.releaseDevice(std::move(pDevice)); } @@ -486,8 +475,12 @@ inline int32_t runTestsSerial(const RunOptions& options) writeXmlReport(options.xmlReportPath, report); reportLine( - "[==========] {} test{} from {} test suite{} ran. ({} ms total)", testCount, plural(testCount, "s"), suiteCount, - plural(suiteCount, "s"), totalMS + "[==========] {} test{} from {} test suite{} ran. ({} ms total)", + testCount, + plural(testCount, "s"), + suiteCount, + plural(suiteCount, "s"), + totalMS ); reportLine("[ PASSED ] {} test{}.", testCount - failureCount, plural(testCount - failureCount, "s")); if (failureCount > 0) @@ -566,7 +559,8 @@ std::vector enumerateTests() // Sort by suite name first, followed by test name. std::sort( - tests.begin(), tests.end(), + tests.begin(), + tests.end(), [](const Test& a, const Test& b) { if (a.suiteName == b.suiteName) @@ -644,6 +638,9 @@ void UnitTestContext::reportFailure(const std::string& message) return; reportLine("{}", message); mFailureMessages.push_back(message); + + if (isDebuggerPresent()) + debugBreak(); } /////////////////////////////////////////////////////////////////////////// @@ -652,13 +649,13 @@ void GPUUnitTestContext::createProgram( const std::filesystem::path& path, const std::string& entry, const DefineList& programDefines, - Program::CompilerFlags flags, - const std::string& shaderModel, + SlangCompilerFlags flags, + ShaderModel shaderModel, bool createShaderVars ) { // Create program. - mpProgram = ComputeProgram::createFromFile(mpDevice, path, entry, programDefines, flags, shaderModel); + mpProgram = Program::createCompute(mpDevice, path, entry, programDefines, flags, shaderModel); mpState = ComputeState::create(mpDevice); mpState->setProgram(mpProgram); @@ -667,10 +664,10 @@ void GPUUnitTestContext::createProgram( createVars(); } -void GPUUnitTestContext::createProgram(const Program::Desc& desc, const DefineList& programDefines, bool createShaderVars) +void GPUUnitTestContext::createProgram(const ProgramDesc& desc, const DefineList& programDefines, bool createShaderVars) { // Create program. - mpProgram = ComputeProgram::create(mpDevice, desc, programDefines); + mpProgram = Program::create(mpDevice, desc, programDefines); mpState = ComputeState::create(mpDevice); mpState->setProgram(mpProgram); @@ -683,7 +680,7 @@ void GPUUnitTestContext::createVars() { // Create shader variables. ref pReflection = mpProgram->getReflector(); - mpVars = ComputeVars::create(mpDevice, pReflection); + mpVars = ProgramVars::create(mpDevice, pReflection); FALCOR_ASSERT(mpVars); // Try to use shader reflection to query thread group size. @@ -694,8 +691,8 @@ void GPUUnitTestContext::createVars() void GPUUnitTestContext::allocateStructuredBuffer(const std::string& name, uint32_t nElements, const void* pInitData, size_t initDataSize) { - checkInvariant(mpVars != nullptr, "Program vars not created"); - mStructuredBuffers[name] = Buffer::createStructured(mpDevice, mpProgram.get(), name, nElements); + FALCOR_CHECK(mpVars != nullptr, "Program vars not created"); + mStructuredBuffers[name] = mpDevice->createStructuredBuffer(mpVars->getRootVar()[name], nElements); if (pInitData) { ref buffer = mStructuredBuffers[name]; @@ -710,7 +707,7 @@ void GPUUnitTestContext::allocateStructuredBuffer(const std::string& name, uint3 void GPUUnitTestContext::runProgram(const uint3& dimensions) { - checkInvariant(mpVars != nullptr, "Program vars not created"); + FALCOR_CHECK(mpVars != nullptr, "Program vars not created"); for (const auto& buffer : mStructuredBuffers) { mpVars->setBuffer(buffer.first, buffer.second); @@ -741,6 +738,10 @@ CPU_TEST(TestCPUTest) EXPECT_GT(2, 1); EXPECT_LE(2, 2); EXPECT_GE(3, 2); + + // Commented out to not have the debugger break if break-on-exception is enabled. + // EXPECT_THROW({ throw std::runtime_error("Test"); }); + // EXPECT_THROW_AS({ throw std::runtime_error("Test"); }, std::runtime_error); } CPU_TEST(TestSingleEval) diff --git a/Source/Falcor/Testing/UnitTest.h b/Source/Falcor/Testing/UnitTest.h index 95360db10..7370fbbca 100644 --- a/Source/Falcor/Testing/UnitTest.h +++ b/Source/Falcor/Testing/UnitTest.h @@ -26,10 +26,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/State/ComputeState.h" -#include "Core/Program/ComputeProgram.h" +#include "Core/Program/Program.h" #include "Core/Program/ProgramVars.h" #include "Core/Program/ShaderVar.h" #include "Utils/Math/Vector.h" @@ -169,15 +168,15 @@ class FALCOR_API GPUUnitTestContext : public UnitTestContext const std::filesystem::path& path, const std::string& csEntry = "main", const DefineList& programDefines = DefineList(), - Program::CompilerFlags flags = Program::CompilerFlags::None, - const std::string& shaderModel = "", + SlangCompilerFlags flags = SlangCompilerFlags::None, + ShaderModel shaderModel = ShaderModel::Unknown, bool createShaderVars = true ); /** * Create compute program based on program desc and defines. */ - void createProgram(const Program::Desc& desc, const DefineList& programDefines = DefineList(), bool createShaderVars = true); + void createProgram(const ProgramDesc& desc, const DefineList& programDefines = DefineList(), bool createShaderVars = true); /** * (Re-)create the shader variables. Call this if vars were not @@ -187,10 +186,10 @@ class FALCOR_API GPUUnitTestContext : public UnitTestContext void createVars(); /** - * vars returns the ComputeVars for the program for use in binding + * vars returns the ProgramVars for the program for use in binding * textures, etc. */ - ComputeVars& vars() + ProgramVars& vars() { FALCOR_ASSERT(mpVars); return *mpVars; @@ -230,10 +229,7 @@ class FALCOR_API GPUUnitTestContext : public UnitTestContext if (it == mStructuredBuffers.end()) throw ErrorRunningTestException(std::string(bufferName) + ": couldn't find buffer to map"); ref buffer = it->second; - const T* data = reinterpret_cast(buffer->map(Buffer::MapType::Read)); - size_t size = buffer->getSize() / sizeof(T); - std::vector result(data, data + size); - buffer->unmap(); + std::vector result = buffer->getElements(); return result; } @@ -265,14 +261,19 @@ class FALCOR_API GPUUnitTestContext : public UnitTestContext /** * Returns the program. */ - ComputeProgram* getProgram() const { return mpProgram.get(); } + Program* getProgram() const { return mpProgram.get(); } + + /** + * Returns the program vars. + */ + ProgramVars* getVars() const { return mpVars.get(); } private: // Internal state ref mpDevice; ref mpState; - ref mpProgram; - ref mpVars; + ref mpProgram; + ref mpVars; uint3 mThreadGroupSize = {0, 0, 0}; std::map> mStructuredBuffers; @@ -505,8 +506,16 @@ inline std::optional createBinaryMessage(std::string_view lhsStr, s return std::optional{}; size_t maxSize = std::max(lhsStr.size(), rhsStr.size()); return fmt::format( - "Expected: ({}) {} ({}), actual:\n {:<{}} = {}\n {:<{}} = {}", lhsStr, TCmpHelper::asString(), rhsStr, lhsStr, maxSize, lhs, - rhsStr, maxSize, rhs + "Expected: ({}) {} ({}), actual:\n {:<{}} = {}\n {:<{}} = {}", + lhsStr, + TCmpHelper::asString(), + rhsStr, + lhsStr, + maxSize, + lhs, + rhsStr, + maxSize, + rhs ); } @@ -662,4 +671,30 @@ using GPUUnitTestContext = unittest::GPUUnitTestContext; #define ASSERT_LT(lhs, rhs) ASSERT_LT_MSG(lhs, rhs, "") #define ASSERT_GT(lhs, rhs) ASSERT_GT_MSG(lhs, rhs, "") +#define EXPECT_THROW(expression) \ + try \ + { \ + expression; \ + EXPECT(false); \ + } \ + catch (...) \ + { \ + EXPECT(true); \ + } + +#define EXPECT_THROW_AS(expression, type) \ + try \ + { \ + expression; \ + EXPECT(false); \ + } \ + catch (const type&) \ + { \ + EXPECT(true); \ + } \ + catch (...) \ + { \ + EXPECT(false); \ + } + } // namespace Falcor diff --git a/Source/Falcor/Utils/Algorithm/BitonicSort.cpp b/Source/Falcor/Utils/Algorithm/BitonicSort.cpp index b7e894f31..ebbb92bfd 100644 --- a/Source/Falcor/Utils/Algorithm/BitonicSort.cpp +++ b/Source/Falcor/Utils/Algorithm/BitonicSort.cpp @@ -38,7 +38,7 @@ static const char kShaderFilename[] = "Utils/Algorithm/BitonicSort.cs.slang"; BitonicSort::BitonicSort(ref pDevice) : mpDevice(pDevice) { #if !FALCOR_NVAPI_AVAILABLE - throw RuntimeError("BitonicSort requires NVAPI. See installation instructions in README."); + FALCOR_THROW("BitonicSort requires NVAPI. See installation instructions in README."); #endif mSort.pState = ComputeState::create(mpDevice); @@ -46,9 +46,9 @@ BitonicSort::BitonicSort(ref pDevice) : mpDevice(pDevice) DefineList defines; defines.add("CHUNK_SIZE", "256"); // Dummy values just so we can get reflection data. We'll set the actual values in execute(). defines.add("GROUP_SIZE", "256"); - mSort.pProgram = ComputeProgram::createFromFile(mpDevice, kShaderFilename, "main", defines); + mSort.pProgram = Program::createCompute(mpDevice, kShaderFilename, "main", defines); mSort.pState->setProgram(mSort.pProgram); - mSort.pVars = ComputeVars::create(mpDevice, mSort.pProgram.get()); + mSort.pVars = ProgramVars::create(mpDevice, mSort.pProgram.get()); } bool BitonicSort::execute(RenderContext* pRenderContext, ref pData, uint32_t totalSize, uint32_t chunkSize, uint32_t groupSize) @@ -82,8 +82,7 @@ bool BitonicSort::execute(RenderContext* pRenderContext, ref pData, uint var["CB"]["gDispatchX"] = groupsX; // Bind the data. - bool success = mSort.pVars->setBuffer("gData", pData); - FALCOR_ASSERT(success); + var["gData"] = pData; // Execute. pRenderContext->dispatch(mSort.pState.get(), mSort.pVars.get(), {groupsX, groupsY, 1}); diff --git a/Source/Falcor/Utils/Algorithm/BitonicSort.h b/Source/Falcor/Utils/Algorithm/BitonicSort.h index 603a28dd8..aedf441d4 100644 --- a/Source/Falcor/Utils/Algorithm/BitonicSort.h +++ b/Source/Falcor/Utils/Algorithm/BitonicSort.h @@ -28,7 +28,7 @@ #pragma once #include "Core/Macros.h" #include "Core/State/ComputeState.h" -#include "Core/Program/ComputeProgram.h" +#include "Core/Program/Program.h" #include "Core/Program/ProgramVars.h" #include @@ -70,8 +70,8 @@ class FALCOR_API BitonicSort struct { ref pState; - ref pProgram; - ref pVars; + ref pProgram; + ref pVars; } mSort; }; } // namespace Falcor diff --git a/Source/Falcor/Utils/Algorithm/DirectedGraph.h b/Source/Falcor/Utils/Algorithm/DirectedGraph.h index 13f12fa05..5faf2ae29 100644 --- a/Source/Falcor/Utils/Algorithm/DirectedGraph.h +++ b/Source/Falcor/Utils/Algorithm/DirectedGraph.h @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/Logger.h" #include #include diff --git a/Source/Falcor/Utils/Algorithm/DirectedGraphTraversal.h b/Source/Falcor/Utils/Algorithm/DirectedGraphTraversal.h index 733b89180..4e9d58b05 100644 --- a/Source/Falcor/Utils/Algorithm/DirectedGraphTraversal.h +++ b/Source/Falcor/Utils/Algorithm/DirectedGraphTraversal.h @@ -28,6 +28,7 @@ #pragma once #include "DirectedGraph.h" #include "Core/Macros.h" +#include "Core/Error.h" #include #include #include diff --git a/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp b/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp index fec334ece..48228ceba 100644 --- a/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp +++ b/Source/Falcor/Utils/Algorithm/ParallelReduction.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "ParallelReduction.h" #include "ParallelReductionType.slangh" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/API/RenderContext.h" #include "Utils/Math/Common.h" #include "Utils/Timing/Profiler.h" @@ -41,9 +41,9 @@ ParallelReduction::ParallelReduction(ref pDevice) : mpDevice(pDevice) // Create the programs. // Set defines to avoid compiler warnings about undefined macros. Proper values will be assigned at runtime. DefineList defines = {{"REDUCTION_TYPE", "1"}, {"FORMAT_CHANNELS", "1"}, {"FORMAT_TYPE", "1"}}; - mpInitialProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "initialPass", defines); - mpFinalProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "finalPass", defines); - mpVars = ComputeVars::create(mpDevice, mpInitialProgram.get()); + mpInitialProgram = Program::createCompute(mpDevice, kShaderFile, "initialPass", defines); + mpFinalProgram = Program::createCompute(mpDevice, kShaderFile, "finalPass", defines); + mpVars = ProgramVars::create(mpDevice, mpInitialProgram.get()); // Check assumptions on thread group sizes. The initial pass is a 2D dispatch, the final pass a 1D. FALCOR_ASSERT(mpInitialProgram->getReflector()->getThreadGroupSize().z == 1); @@ -59,14 +59,14 @@ void ParallelReduction::allocate(uint32_t elementCount, uint32_t elementSize) if (mpBuffers[0] == nullptr || mpBuffers[0]->getElementCount() < elementCount * elementSize) { // Buffer 0 has one element per tile. - mpBuffers[0] = Buffer::createTyped(mpDevice, elementCount * elementSize); + mpBuffers[0] = mpDevice->createTypedBuffer(elementCount * elementSize); mpBuffers[0]->setName("ParallelReduction::mpBuffers[0]"); // Buffer 1 has one element per N elements in buffer 0. const uint32_t numElem1 = div_round_up(elementCount, mpFinalProgram->getReflector()->getThreadGroupSize().x); if (mpBuffers[1] == nullptr || mpBuffers[1]->getElementCount() < numElem1 * elementSize) { - mpBuffers[1] = Buffer::createTyped(mpDevice, numElem1 * elementSize); + mpBuffers[1] = mpDevice->createTypedBuffer(numElem1 * elementSize); mpBuffers[1]->setName("ParallelReduction::mpBuffers[1]"); } } @@ -87,7 +87,7 @@ void ParallelReduction::execute( // Check texture array/mip/sample count. if (pInput->getArraySize() != 1 || pInput->getMipCount() != 1 || pInput->getSampleCount() != 1) { - throw RuntimeError("ParallelReduction::execute() - Input texture is unsupported."); + FALCOR_THROW("ParallelReduction::execute() - Input texture is unsupported."); } // Check texture format. @@ -106,7 +106,7 @@ void ParallelReduction::execute( formatType = FORMAT_TYPE_UINT; break; default: - throw RuntimeError("ParallelReduction::execute() - Input texture format unsupported."); + FALCOR_THROW("ParallelReduction::execute() - Input texture format unsupported."); } // Check that reduction type T is compatible with the resource format. @@ -117,7 +117,7 @@ void ParallelReduction::execute( (formatType == FORMAT_TYPE_UINT && (!std::is_integral::value || !std::is_unsigned::value))) { - throw RuntimeError("ParallelReduction::execute() - Template type T is not compatible with resource format."); + FALCOR_THROW("ParallelReduction::execute() - Template type T is not compatible with resource format."); } uint32_t reductionType = REDUCTION_TYPE_UNKNOWN; @@ -133,7 +133,7 @@ void ParallelReduction::execute( elementSize = 2; break; default: - throw RuntimeError("ParallelReduction::execute() - Unknown reduction type."); + FALCOR_THROW("ParallelReduction::execute() - Unknown reduction type."); } // Allocate intermediate buffers if needed. @@ -200,7 +200,7 @@ void ParallelReduction::execute( { if (resultOffset + resultSize > pResultBuffer->getSize()) { - throw RuntimeError("ParallelReduction::execute() - Results buffer is too small."); + FALCOR_THROW("ParallelReduction::execute() - Results buffer is too small."); } pRenderContext->copyBufferRegion(pResultBuffer.get(), resultOffset, mpBuffers[inputsBufferIndex].get(), 0, resultSize); @@ -209,10 +209,7 @@ void ParallelReduction::execute( // Read back the result to the CPU. if (pResult) { - const T* pBuf = static_cast(mpBuffers[inputsBufferIndex]->map(Buffer::MapType::Read)); - FALCOR_ASSERT(pBuf); - std::memcpy(pResult, pBuf, resultSize); - mpBuffers[inputsBufferIndex]->unmap(); + mpBuffers[inputsBufferIndex]->getBlob(pResult, 0, resultSize); } } diff --git a/Source/Falcor/Utils/Algorithm/ParallelReduction.h b/Source/Falcor/Utils/Algorithm/ParallelReduction.h index c0c6e6931..1c8737f95 100644 --- a/Source/Falcor/Utils/Algorithm/ParallelReduction.h +++ b/Source/Falcor/Utils/Algorithm/ParallelReduction.h @@ -29,7 +29,7 @@ #include "Core/Macros.h" #include "Core/API/Buffer.h" #include "Core/State/ComputeState.h" -#include "Core/Program/ComputeProgram.h" +#include "Core/Program/Program.h" #include "Core/Program/ProgramVars.h" #include @@ -97,9 +97,9 @@ class FALCOR_API ParallelReduction ref mpDevice; ref mpState; - ref mpInitialProgram; - ref mpFinalProgram; - ref mpVars; + ref mpInitialProgram; + ref mpFinalProgram; + ref mpVars; ref mpBuffers[2]; ///< Intermediate buffers for reduction iterations. }; diff --git a/Source/Falcor/Utils/Algorithm/PrefixSum.cpp b/Source/Falcor/Utils/Algorithm/PrefixSum.cpp index 4754ae66e..8fb626695 100644 --- a/Source/Falcor/Utils/Algorithm/PrefixSum.cpp +++ b/Source/Falcor/Utils/Algorithm/PrefixSum.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "PrefixSum.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/API/RenderContext.h" #include "Utils/Math/Common.h" #include "Utils/Timing/Profiler.h" @@ -43,20 +43,22 @@ PrefixSum::PrefixSum(ref pDevice) : mpDevice(pDevice) { // Create shaders and state. DefineList defines = {{"GROUP_SIZE", std::to_string(kGroupSize)}}; - mpPrefixSumGroupProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "groupScan", defines); - mpPrefixSumGroupVars = ComputeVars::create(mpDevice, mpPrefixSumGroupProgram.get()); - mpPrefixSumFinalizeProgram = ComputeProgram::createFromFile(mpDevice, kShaderFile, "finalizeGroups", defines); - mpPrefixSumFinalizeVars = ComputeVars::create(mpDevice, mpPrefixSumFinalizeProgram.get()); + mpPrefixSumGroupProgram = Program::createCompute(mpDevice, kShaderFile, "groupScan", defines); + mpPrefixSumGroupVars = ProgramVars::create(mpDevice, mpPrefixSumGroupProgram.get()); + mpPrefixSumFinalizeProgram = Program::createCompute(mpDevice, kShaderFile, "finalizeGroups", defines); + mpPrefixSumFinalizeVars = ProgramVars::create(mpDevice, mpPrefixSumFinalizeProgram.get()); mpComputeState = ComputeState::create(mpDevice); // Create and bind buffer for per-group sums and total sum. - mpPrefixGroupSums = Buffer::create( - mpDevice, kGroupSize * sizeof(uint32_t), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, - Buffer::CpuAccess::None, nullptr + mpPrefixGroupSums = mpDevice->createBuffer( + kGroupSize * sizeof(uint32_t), + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType::DeviceLocal, + nullptr ); - mpTotalSum = Buffer::create(mpDevice, sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr); - mpPrevTotalSum = Buffer::create(mpDevice, sizeof(uint32_t), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr); + mpTotalSum = mpDevice->createBuffer(sizeof(uint32_t), ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr); + mpPrevTotalSum = mpDevice->createBuffer(sizeof(uint32_t), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr); { auto var = mpPrefixSumGroupVars->getRootVar(); @@ -153,7 +155,7 @@ void PrefixSum::execute( { if (pTotalSumOffset + 4 > pTotalSumBuffer->getSize()) { - throw RuntimeError("PrefixSum::execute() - Results buffer is too small."); + FALCOR_THROW("PrefixSum::execute() - Results buffer is too small."); } pRenderContext->copyBufferRegion(pTotalSumBuffer.get(), pTotalSumOffset, mpTotalSum.get(), 0, 4); @@ -162,9 +164,7 @@ void PrefixSum::execute( // Read back sum of all elements to the CPU, if requested. if (pTotalSum) { - uint32_t* pMappedTotalSum = (uint32_t*)mpTotalSum->map(Buffer::MapType::Read); - *pTotalSum = *pMappedTotalSum; - mpTotalSum->unmap(); + *pTotalSum = mpTotalSum->getElement(0); } } } // namespace Falcor diff --git a/Source/Falcor/Utils/Algorithm/PrefixSum.h b/Source/Falcor/Utils/Algorithm/PrefixSum.h index c2a904c30..fa45317c9 100644 --- a/Source/Falcor/Utils/Algorithm/PrefixSum.h +++ b/Source/Falcor/Utils/Algorithm/PrefixSum.h @@ -29,7 +29,7 @@ #include "Core/Macros.h" #include "Core/API/Buffer.h" #include "Core/State/ComputeState.h" -#include "Core/Program/ComputeProgram.h" +#include "Core/Program/Program.h" #include "Core/Program/ProgramVars.h" #include @@ -72,11 +72,11 @@ class FALCOR_API PrefixSum ref mpComputeState; - ref mpPrefixSumGroupProgram; - ref mpPrefixSumGroupVars; + ref mpPrefixSumGroupProgram; + ref mpPrefixSumGroupVars; - ref mpPrefixSumFinalizeProgram; - ref mpPrefixSumFinalizeVars; + ref mpPrefixSumFinalizeProgram; + ref mpPrefixSumFinalizeVars; ref mpPrefixGroupSums; ///< Temporary buffer for prefix sum computation. ref mpTotalSum; ///< Temporary buffer for total sum of an iteration. diff --git a/Source/Falcor/Utils/AlignedAllocator.h b/Source/Falcor/Utils/AlignedAllocator.h index 73d11324d..8ee83d470 100644 --- a/Source/Falcor/Utils/AlignedAllocator.h +++ b/Source/Falcor/Utils/AlignedAllocator.h @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/Math/Common.h" #include #include diff --git a/Source/Falcor/Utils/BufferAllocator.cpp b/Source/Falcor/Utils/BufferAllocator.cpp index 5afedda93..cf3473d2c 100644 --- a/Source/Falcor/Utils/BufferAllocator.cpp +++ b/Source/Falcor/Utils/BufferAllocator.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "BufferAllocator.h" +#include "Core/API/Device.h" #include "Utils/Math/Common.h" namespace Falcor @@ -35,9 +36,9 @@ BufferAllocator::BufferAllocator(size_t alignment, size_t elementSize, size_t ca { // Check the different alignment requirements. // Some of these are stricter than they need be and can be relaxed in the future. - checkArgument(alignment == 0 || isPowerOf2(alignment), "Alignment must be a power of two."); - checkArgument(cacheLineSize == 0 || isPowerOf2(cacheLineSize), "Cache line size must be a power of two."); - checkArgument(cacheLineSize == 0 || alignment <= cacheLineSize, "Alignment must be smaller or equal to the cache line size."); + FALCOR_CHECK(alignment == 0 || isPowerOf2(alignment), "Alignment must be a power of two."); + FALCOR_CHECK(cacheLineSize == 0 || isPowerOf2(cacheLineSize), "Cache line size must be a power of two."); + FALCOR_CHECK(cacheLineSize == 0 || alignment <= cacheLineSize, "Alignment must be smaller or equal to the cache line size."); // Check requirements on element size for structured buffers. // The alignment can be smaller or larger than the element size, but they need to be integer multiples. @@ -45,7 +46,7 @@ BufferAllocator::BufferAllocator(size_t alignment, size_t elementSize, size_t ca { size_t a = std::max(alignment, elementSize); size_t b = std::min(alignment, elementSize); - checkArgument(a % b == 0, "Alignment and element size needs to be integer multiples."); + FALCOR_CHECK(a % b == 0, "Alignment and element size needs to be integer multiples."); } } @@ -57,15 +58,15 @@ size_t BufferAllocator::allocate(size_t byteSize) void BufferAllocator::setBlob(const void* pData, size_t byteOffset, size_t byteSize) { - checkArgument(pData != nullptr, "Invalid pointer."); - checkArgument(byteOffset + byteSize <= mBuffer.size(), "Memory region is out of range."); + FALCOR_CHECK(pData != nullptr, "Invalid pointer."); + FALCOR_CHECK(byteOffset + byteSize <= mBuffer.size(), "Memory region is out of range."); std::memcpy(mBuffer.data() + byteOffset, pData, byteSize); markAsDirty(byteOffset, byteSize); } void BufferAllocator::modified(size_t byteOffset, size_t byteSize) { - checkArgument(byteOffset + byteSize <= mBuffer.size(), "Memory region is out of range."); + FALCOR_CHECK(byteOffset + byteSize <= mBuffer.size(), "Memory region is out of range."); markAsDirty(byteOffset, byteSize); } @@ -94,13 +95,13 @@ ref BufferAllocator::getGPUBuffer(ref pDevice) { size_t elemCount = bufSize / mElementSize; FALCOR_ASSERT(elemCount * mElementSize == bufSize); - mpGpuBuffer = Buffer::createStructured( - pDevice, mElementSize, elemCount, mBindFlags, Buffer::CpuAccess::None, nullptr, false /* no UAV counter */ + mpGpuBuffer = pDevice->createStructuredBuffer( + mElementSize, elemCount, mBindFlags, MemoryType::DeviceLocal, nullptr, false /* no UAV counter */ ); } else { - mpGpuBuffer = Buffer::create(pDevice, bufSize, mBindFlags, Buffer::CpuAccess::None, nullptr); + mpGpuBuffer = pDevice->createBuffer(bufSize, mBindFlags, MemoryType::DeviceLocal, nullptr); } mDirty = Range(0, mBuffer.size()); // Mark entire buffer as dirty so the data gets uploaded. diff --git a/Source/Falcor/Utils/Color/ColorHelpers.slang b/Source/Falcor/Utils/Color/ColorHelpers.slang index 015df49b4..eb1a6fb80 100644 --- a/Source/Falcor/Utils/Color/ColorHelpers.slang +++ b/Source/Falcor/Utils/Color/ColorHelpers.slang @@ -38,7 +38,12 @@ BEGIN_NAMESPACE_FALCOR * Returns a relative luminance of an input linear RGB color in the ITU-R BT.709 color space * @param RGBColor linear HDR RGB color in the ITU-R BT.709 color space */ +#ifdef HOST_CODE inline float luminance(float3 rgb) +#else +[Differentiable] +float luminance(float3 rgb) +#endif { return dot(rgb, float3(0.2126f, 0.7152f, 0.0722f)); } @@ -51,9 +56,11 @@ inline float luminance(float3 rgb) float3 RGBtoXYZ_Rec709(float3 c) { static const float3x3 M = { + // clang-format off 0.4123907992659595, 0.3575843393838780, 0.1804807884018343, // row 0 0.2126390058715104, 0.7151686787677559, 0.0721923153607337, // row 1 0.0193308187155918, 0.1191947797946259, 0.9505321522496608, // row 2 + // clang-format off }; return mul(M, c); } @@ -64,9 +71,11 @@ float3 RGBtoXYZ_Rec709(float3 c) float3 XYZtoRGB_Rec709(float3 c) { static const float3x3 M = { + // clang-format off 3.240969941904522, -1.537383177570094, -0.4986107602930032, // row 0 -0.9692436362808803, 1.875967501507721, 0.04155505740717569, // row 1 0.05563007969699373, -0.2039769588889765, 1.056971514242878, // row 2 + // clang-format on }; return mul(M, c); } diff --git a/Source/Falcor/Utils/Color/ColorUtils.h b/Source/Falcor/Utils/Color/ColorUtils.h index 36f279b1e..9b89b3666 100644 --- a/Source/Falcor/Utils/Color/ColorUtils.h +++ b/Source/Falcor/Utils/Color/ColorUtils.h @@ -27,7 +27,7 @@ **************************************************************************/ #pragma once -#include "Core/ErrorHandling.h" +#include "Core/Error.h" #include "Utils/Math/Vector.h" #include "Utils/Math/Matrix.h" @@ -65,44 +65,56 @@ namespace Falcor { // Transform from RGB color in Rec.709 to CIE XYZ. static const float3x3 kColorTransform_RGBtoXYZ_Rec709 = { + // clang-format off 0.4123907992659595, 0.3575843393838780, 0.1804807884018343, // row 0 0.2126390058715104, 0.7151686787677559, 0.0721923153607337, // row 1 0.0193308187155918, 0.1191947797946259, 0.9505321522496608 // row 2 + // clang-format on }; // Transform from XYZ color to RGB in Rec.709. static const float3x3 kColorTransform_XYZtoRGB_Rec709 = { + // clang-format off 3.2409699419045213, -1.5373831775700935, -0.4986107602930033, // row 0 -0.9692436362808798, 1.8759675015077206, 0.0415550574071756, // row 1 0.0556300796969936, -0.2039769588889765, 1.0569715142428784 // row 2 + // clang-format on }; // Transform from CIE XYZ to LMS using the CAT02 transform. static const float3x3 kColorTransform_XYZtoLMS_CAT02 = { + // clang-format off 0.7328, 0.4296, -0.1624, // row 0 -0.7036, 1.6975, 0.0061, // row 1 0.0030, 0.0136, 0.9834 // row 2 + // clang-format on }; // Transform from LMS to CIE XYZ using the inverse CAT02 transform. static const float3x3 kColorTransform_LMStoXYZ_CAT02 = { + // clang-format off 1.096123820835514, -0.278869000218287, 0.182745179382773, // row 0 0.454369041975359, 0.473533154307412, 0.072097803717229, // row 1 -0.009627608738429, -0.005698031216113, 1.015325639954543 // row 2 + // clang-format on }; // Transform from CIE XYZ to LMS using the Bradford transform. static const float3x3 kColorTransform_XYZtoLMS_Bradford = { + // clang-format off 0.8951, 0.2664, -0.1614, // row 0 -0.7502, 1.7135, 0.0367, // row 1 0.0389, -0.0685, 1.0296 // row 2 + // clang-format on }; // Transform from LMS to CIE XYZ using the inverse Bradford transform. static const float3x3 kColorTransform_LMStoXYZ_Bradford = { + // clang-format off 0.98699290546671214, -0.14705425642099013, 0.15996265166373122, // row 0 0.43230526972339445, 0.51836027153677744, 0.04929122821285559, // row 1 -0.00852866457517732, 0.04004282165408486, 0.96848669578754998 // row 2 + // clang-format on }; /** @@ -141,11 +153,7 @@ static float3 xyYtoXYZ(float x, float y, float Y) */ static float3 colorTemperatureToXYZ(float T, float Y = 1.f) { - if (T < 1667.f || T > 25000.f) - { - reportError("colorTemperatureToXYZ() - T is out of range"); - return float3(0, 0, 0); - } + FALCOR_CHECK(T >= 1667.f && T <= 25000.f, "T is out of range."); // We do the computations in double double t = T; diff --git a/Source/Falcor/Utils/Color/SampledSpectrum.h b/Source/Falcor/Utils/Color/SampledSpectrum.h index 2dc2709bf..3556c3e3e 100644 --- a/Source/Falcor/Utils/Color/SampledSpectrum.h +++ b/Source/Falcor/Utils/Color/SampledSpectrum.h @@ -26,8 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Utils/Math/Common.h" #include "Utils/Math/Vector.h" #include @@ -68,8 +67,8 @@ class SampledSpectrum */ SampledSpectrum(float lambdaStart, float lambdaEnd, size_t sampleCount) : mLambdaStart(lambdaStart), mLambdaEnd(lambdaEnd) { - checkArgument(lambdaEnd > lambdaStart, "'lambdaEnd' must be larger than 'lambdaStart'."); - checkArgument(sampleCount > 0, "'sampleCount' must be at least one."); + FALCOR_CHECK(lambdaEnd > lambdaStart, "'lambdaEnd' must be larger than 'lambdaStart'."); + FALCOR_CHECK(sampleCount > 0, "'sampleCount' must be at least one."); mSamples.resize(sampleCount, value_type(0)); } @@ -93,8 +92,8 @@ class SampledSpectrum */ void set(const size_t sampleCount, const value_type* pSamples) { - checkArgument(pSamples != nullptr, "'pSamples' is nullptr."); - checkArgument(sampleCount == mSamples.size(), "Sample count mismatch."); + FALCOR_CHECK(pSamples != nullptr, "'pSamples' is nullptr."); + FALCOR_CHECK(sampleCount == mSamples.size(), "Sample count mismatch."); mSamples.assign(pSamples, pSamples + sampleCount); } @@ -104,7 +103,7 @@ class SampledSpectrum */ void set(const std::vector& samples) { - checkArgument(samples.size() == mSamples.size(), "Sample count mismatch."); + FALCOR_CHECK(samples.size() == mSamples.size(), "Sample count mismatch."); mSamples = samples; } @@ -117,7 +116,7 @@ class SampledSpectrum */ void set(const size_t sampleCount, const value_type* pSamples, const float* pLambdas) { - checkArgument(pSamples != nullptr && pLambdas != nullptr, "'pSamples' or 'pLambdas' is nullptr."); + FALCOR_CHECK(pSamples != nullptr && pLambdas != nullptr, "'pSamples' or 'pLambdas' is nullptr."); FALCOR_UNIMPLEMENTED(); } @@ -129,7 +128,7 @@ class SampledSpectrum */ void set(const std::vector& samples, const std::vector& lambdas) { - checkArgument( + FALCOR_CHECK( !samples.empty() && samples.size() == lambdas.size(), "'samples' and 'lambdas' must be non-empty and of equal length." ); set(samples.size(), samples.data(), lambdas.data()); @@ -200,7 +199,7 @@ float3 SampledSpectrum::toXYZ_CIE1931() const template T SampledSpectrum::eval(const float lambda, const SpectrumInterpolation interpolationType) const { - checkInvariant(interpolationType == SpectrumInterpolation::Linear, "Interpolation type must be 'Linear'"); + FALCOR_CHECK(interpolationType == SpectrumInterpolation::Linear, "Interpolation type must be 'Linear'"); if (lambda < mLambdaStart || lambda > mLambdaEnd) return T(0); float x = ((lambda - mLambdaStart) / (mLambdaEnd - mLambdaStart)) * (size() - 1.0f); diff --git a/Source/Falcor/Utils/Color/Spectrum.cpp b/Source/Falcor/Utils/Color/Spectrum.cpp index 3f7e3cce1..1e305db4a 100644 --- a/Source/Falcor/Utils/Color/Spectrum.cpp +++ b/Source/Falcor/Utils/Color/Spectrum.cpp @@ -26,8 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Spectrum.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include // TODO C++20: Replace with #include @@ -42,12 +41,12 @@ PiecewiseLinearSpectrum::PiecewiseLinearSpectrum(fstd::span wavelen , mValues(values.begin(), values.end()) , mMaxValue(*std::max_element(values.begin(), values.end())) { - checkArgument(wavelengths.size() == values.size(), "'wavelengths' and 'values' need to contain the same number of elements"); + FALCOR_CHECK(wavelengths.size() == values.size(), "'wavelengths' and 'values' need to contain the same number of elements"); } PiecewiseLinearSpectrum PiecewiseLinearSpectrum::fromInterleaved(fstd::span interleaved, bool normalize) { - checkArgument(interleaved.size() % 2 == 0, "'interleaved' must have an even number of elements."); + FALCOR_CHECK(interleaved.size() % 2 == 0, "'interleaved' must have an even number of elements."); size_t count = interleaved.size() / 2; std::vector wavelengths(count); @@ -57,7 +56,7 @@ PiecewiseLinearSpectrum PiecewiseLinearSpectrum::fromInterleaved(fstd::span= wavelengths[i - 1], "'interleaved' must have wavelengths that are monotonic increasing."); + FALCOR_CHECK(i == 0 || wavelengths[i] >= wavelengths[i - 1], "'interleaved' must have wavelengths that are monotonic increasing."); } auto spec = PiecewiseLinearSpectrum(wavelengths, values); @@ -77,7 +76,7 @@ std::optional PiecewiseLinearSpectrum::fromFile(const s void PiecewiseLinearSpectrum::scale(float factor) { - checkArgument(factor >= 0.f, "'factor' ({}) needs to be positive.", factor); + FALCOR_CHECK(factor >= 0.f, "'factor' ({}) needs to be positive.", factor); for (auto& value : mValues) value *= factor; mMaxValue *= factor; diff --git a/Source/Falcor/Utils/Color/SpectrumUtils.h b/Source/Falcor/Utils/Color/SpectrumUtils.h index f852ebc87..8f360532c 100644 --- a/Source/Falcor/Utils/Color/SpectrumUtils.h +++ b/Source/Falcor/Utils/Color/SpectrumUtils.h @@ -28,7 +28,7 @@ #pragma once #include "SampledSpectrum.h" #include "Core/Macros.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/Math/Vector.h" #include "Utils/Color/ColorUtils.h" #include @@ -129,8 +129,11 @@ class FALCOR_API SpectrumUtils ) { return integrate( - spectrum, interpolationType, [](float wavelength) -> float3 { return SpectrumUtils::wavelengthToXYZ_CIE1931(wavelength); }, - componentIndex, integrationSteps + spectrum, + interpolationType, + [](float wavelength) -> float3 { return SpectrumUtils::wavelengthToXYZ_CIE1931(wavelength); }, + componentIndex, + integrationSteps ); } @@ -151,10 +154,12 @@ class FALCOR_API SpectrumUtils ) { return integrate( - spectrum, interpolationType, + spectrum, + interpolationType, [](float wavelength) -> float3 { return SpectrumUtils::wavelengthToXYZ_CIE1931(wavelength) * SpectrumUtils::wavelengthToD65(wavelength); }, - componentIndex, integrationSteps + componentIndex, + integrationSteps ); } diff --git a/Source/Falcor/Utils/CudaRuntime.h b/Source/Falcor/Utils/CudaRuntime.h new file mode 100644 index 000000000..f23db6929 --- /dev/null +++ b/Source/Falcor/Utils/CudaRuntime.h @@ -0,0 +1,63 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once + +/** + * CUDA runtime defines vector types in the global namespace. Some of these + * types clash with the vector types in Falcor, which live in the Falcor::math + * and Falcor namespace. To avoid this clash, we rename the CUDA types here. + * Falcor code should includle this header instead of . + */ + +#define int1 cuda_int1 +#define int2 cuda_int2 +#define int3 cuda_int3 +#define int4 cuda_int4 +#define uint1 cuda_uint1 +#define uint2 cuda_uint2 +#define uint3 cuda_uint3 +#define uint4 cuda_uint4 +#define float1 cuda_float1 +#define float2 cuda_float2 +#define float3 cuda_float3 +#define float4 cuda_float4 + +#include + +#undef int1 +#undef int2 +#undef int3 +#undef int4 +#undef uint1 +#undef uint2 +#undef uint3 +#undef uint4 +#undef float1 +#undef float2 +#undef float3 +#undef float4 diff --git a/Source/Falcor/Utils/CudaUtils.cpp b/Source/Falcor/Utils/CudaUtils.cpp index 60092cfe1..17dd5598d 100644 --- a/Source/Falcor/Utils/CudaUtils.cpp +++ b/Source/Falcor/Utils/CudaUtils.cpp @@ -26,63 +26,15 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "CudaUtils.h" -#include "Core/Errors.h" +#include "Core/Error.h" +#include "Core/API/Device.h" +#include "Core/API/Buffer.h" +#include "Core/API/Texture.h" +#include "Core/API/Fence.h" #include #include #include -// Some debug macros -#define CUDA_CHECK(call) \ - { \ - cudaError_t rc = call; \ - if (rc != cudaSuccess) \ - { \ - std::stringstream txt; \ - cudaError_t err = rc; /*cudaGetLastError();*/ \ - txt << "CUDA Error " << cudaGetErrorName(err) << " (" << cudaGetErrorString(err) << ")"; \ - Falcor::reportFatalError(txt.str()); \ - } \ - } -#define CUDA_CHECK_NOEXCEPT(call) \ - { \ - call; \ - } - -#define CUDA_SYNC_CHECK() \ - { \ - cudaDeviceSynchronize(); \ - cudaError_t error = cudaGetLastError(); \ - if (error != cudaSuccess) \ - { \ - char buf[1024]; \ - sprintf(buf, "error (%s: line %d): %s\n", __FILE__, __LINE__, cudaGetErrorString(error)); \ - Falcor::reportFatalError(std::string(buf)); \ - } \ - } - -#define CU_CHECK_SUCCESS(x) \ - do \ - { \ - CUresult result = x; \ - if (result != CUDA_SUCCESS) \ - { \ - const char* msg; \ - cuGetErrorName(result, &msg); \ - Falcor::reportFatalError("CUDA Error: " #x " failed with error " + std::string(msg)); \ - } \ - } while (0) - -#define CUDA_CHECK_SUCCESS(x) \ - do \ - { \ - cudaError_t result = x; \ - if (result != cudaSuccess) \ - { \ - Falcor::reportFatalError("CUDA Error: " #x " failed with error " + std::string(cudaGetErrorString(result))); \ - return 0; \ - } \ - } while (0) - namespace { uint32_t gNodeMask; @@ -91,133 +43,144 @@ CUcontext gCudaContext; CUstream gCudaStream; } // namespace -unsigned int initCuda(void) +namespace Falcor +{ +namespace cuda_utils { -#if 0 - cudaFree(0); - int32_t numDevices; - cudaGetDeviceCount(&numDevices); - return numDevices; -#endif - CU_CHECK_SUCCESS(cuInit(0)); - int32_t firstGPUID = -1; - cudaDeviceProp prop; - int32_t count; - cudaError_t err = cudaGetDeviceCount(&count); +void deviceSynchronize() +{ + cudaDeviceSynchronize(); + cudaError_t error = cudaGetLastError(); + FALCOR_CHECK(error == cudaSuccess, "Failed to sync CUDA device: {}.", cudaGetErrorString(error)); +} - for (int32_t i = 0; i < count; ++i) - { - err = cudaGetDeviceProperties(&prop, i); - if (prop.major >= 3) - { - firstGPUID = i; - break; - } - } +void* mallocDevice(size_t size) +{ + void* devPtr; + FALCOR_CUDA_CHECK(cudaMalloc(&devPtr, size)); + return devPtr; +} - if (firstGPUID < 0) - { - Falcor::reportFatalError("No CUDA 10 compatible GPU found"); - return false; - } - gNodeMask = prop.luidDeviceNodeMask; - CUDA_CHECK_SUCCESS(cudaSetDevice(firstGPUID)); - CU_CHECK_SUCCESS(cuDeviceGet(&gCudaDevice, firstGPUID)); - CU_CHECK_SUCCESS(cuCtxCreate(&gCudaContext, 0, gCudaDevice)); - CU_CHECK_SUCCESS(cuStreamCreate(&gCudaStream, CU_STREAM_DEFAULT)); - return true; +void freeDevice(void* devPtr) +{ + if (!devPtr) + return; + FALCOR_CUDA_CHECK(cudaFree(devPtr)); } -void setCUDAContext() +void memcpyDeviceToDevice(void* dst, const void* src, size_t count) { - CU_CHECK_SUCCESS(cuCtxSetCurrent(gCudaContext)); + FALCOR_CUDA_CHECK(cudaMemcpy(dst, src, count, cudaMemcpyDeviceToDevice)); } -void syncCudaDevice() +void memcpyHostToDevice(void* dst, const void* src, size_t count) { - cudaDeviceSynchronize(); - cudaError_t error = cudaGetLastError(); - if (error != cudaSuccess) - { - Falcor::reportFatalError("Failed to sync CUDA device"); - } + FALCOR_CUDA_CHECK(cudaMemcpy(dst, src, count, cudaMemcpyHostToDevice)); } -void myCudaMemset(void* devPtr, int value, size_t count) +void memcpyDeviceToHost(void* dst, const void* src, size_t count) { - CUDA_CHECK(cudaMemset(devPtr, value, count)); + FALCOR_CUDA_CHECK(cudaMemcpy(dst, src, count, cudaMemcpyDeviceToHost)); } -void CudaBuffer::allocate(size_t size) +void memsetDevice(void* devPtr, int value, size_t count) { - if (mpDevicePtr) - free(); - mSizeBytes = size; - CUDA_CHECK(cudaMalloc((void**)&mpDevicePtr, mSizeBytes)); + FALCOR_CUDA_CHECK(cudaMemset(devPtr, value, count)); } -void CudaBuffer::resize(size_t size) +cudaExternalMemory_t importExternalMemory(const Buffer* buffer) { - allocate(size); + FALCOR_CHECK(buffer, "'buffer' is nullptr."); + FALCOR_CHECK(is_set(buffer->getBindFlags(), ResourceBindFlags::Shared), "Buffer must be created with ResourceBindFlags::Shared."); + SharedResourceApiHandle sharedHandle = buffer->getSharedApiHandle(); + FALCOR_CHECK(sharedHandle, "Buffer shared handle creation failed."); + + cudaExternalMemoryHandleDesc desc = {}; + switch (buffer->getDevice()->getType()) + { + case Device::Type::D3D12: + desc.type = cudaExternalMemoryHandleTypeD3D12Resource; + break; +#if FALCOR_WINDOWS + case Device::Type::Vulkan: + desc.type = cudaExternalMemoryHandleTypeOpaqueWin32; + break; +#endif + default: + FALCOR_THROW("Unsupported device type '{}'.", buffer->getDevice()->getType()); + } + desc.handle.win32.handle = sharedHandle; + desc.size = buffer->getSize(); + desc.flags = cudaExternalMemoryDedicated; + + cudaExternalMemory_t extMem; + FALCOR_CUDA_CHECK(cudaImportExternalMemory(&extMem, &desc)); + return extMem; } -void CudaBuffer::free(void) +void destroyExternalMemory(cudaExternalMemory_t extMem) { - CUDA_CHECK(cudaFree(mpDevicePtr)); - mpDevicePtr = nullptr; - mSizeBytes = 0; + FALCOR_CUDA_CHECK(cudaDestroyExternalMemory(extMem)); } -template -bool CudaBuffer::download(T* t, size_t count) +void* externalMemoryGetMappedBuffer(cudaExternalMemory_t extMem, size_t offset, size_t size) { - if (!mpDevicePtr) - return false; - if (mSizeBytes <= (count * sizeof(T))) - return false; + cudaExternalMemoryBufferDesc desc = {}; + desc.offset = offset; + desc.size = size; - CUDA_CHECK(cudaMemcpy((void*)t, mpDevicePtr, count * sizeof(T), cudaMemcpyDeviceToHost)); - return true; // might be an error caught by CUDA_CHECK? TODO: process any such error through + void* devPtr = nullptr; + FALCOR_CUDA_CHECK(cudaExternalMemoryGetMappedBuffer(&devPtr, extMem, &desc)); + return devPtr; } -template -bool CudaBuffer::upload(const T* t, size_t count) +cudaExternalSemaphore_t importExternalSemaphore(const Fence* fence) { - if (!mpDevicePtr) - return false; - if (mSizeBytes <= (count * sizeof(T))) - return false; + FALCOR_CHECK(fence, "'fence' is nullptr."); + FALCOR_CHECK(fence->getDesc().shared, "'fence' must be created with shared=true."); + SharedFenceApiHandle sharedHandle = fence->getSharedApiHandle(); + FALCOR_CHECK(sharedHandle, "Fence shared handle creation failed."); - CUDA_CHECK(cudaMemcpy(mpDevicePtr, (void*)t, count * sizeof(T), cudaMemcpyHostToDevice)); - return true; // might be an error caught by CUDA_CHECK? TODO: process any such error through + cudaExternalSemaphoreHandleDesc desc = {}; + switch (fence->getDevice()->getType()) + { + case Device::Type::D3D12: + desc.type = cudaExternalSemaphoreHandleTypeD3D12Fence; + break; + default: + FALCOR_THROW("Unsupported device type '{}'.", fence->getDevice()->getType()); + } + desc.handle.win32.handle = (void*)sharedHandle; + + cudaExternalSemaphore_t extSem; + FALCOR_CUDA_CHECK(cudaImportExternalSemaphore(&extSem, &desc)); + return extSem; } -template -void CudaBuffer::allocAndUpload(const std::vector& vt) +void destroyExternalSemaphore(cudaExternalSemaphore_t extSem) { - allocate(vt.size() * sizeof(T)); - upload((const T*)vt.data(), vt.size()); + FALCOR_CUDA_CHECK(cudaDestroyExternalSemaphore(extSem)); } -void cudaCopyDeviceToDevice(void* dst, const void* src, size_t count) +void signalExternalSemaphore(cudaExternalSemaphore_t extSem, uint64_t value, cudaStream_t stream) { - CUDA_CHECK(cudaMemcpy(dst, src, count, cudaMemcpyDeviceToDevice)); + cudaExternalSemaphoreSignalParams params = {}; + params.params.fence.value = value; + FALCOR_CUDA_CHECK(cudaSignalExternalSemaphoresAsync(&extSem, ¶ms, 1, stream)); } -void cudaCopyHostToDevice(void* dst, const void* src, size_t count) +void waitExternalSemaphore(cudaExternalSemaphore_t extSem, uint64_t value, cudaStream_t stream) { - CUDA_CHECK(cudaMemcpy(dst, src, count, cudaMemcpyHostToDevice)); + cudaExternalSemaphoreWaitParams params = {}; + params.params.fence.value = value; + FALCOR_CUDA_CHECK(cudaWaitExternalSemaphoresAsync(&extSem, ¶ms, 1, stream)); } -void* getSharedDevicePtr(Falcor::SharedResourceApiHandle sharedHandle, uint32_t bytes) +void* getSharedDevicePtr(SharedResourceApiHandle sharedHandle, uint32_t bytes) { // No handle? No pointer! - if (sharedHandle == NULL) - { - Falcor::reportFatalError("FalcorCUDA::importBufferToCudaPointer - texture shared handle creation failed"); - return nullptr; - } + FALCOR_CHECK(sharedHandle, "Texture shared handle creation failed"); // Create the descriptor of our shared memory buffer cudaExternalMemoryHandleDesc externalMemoryHandleDesc; @@ -229,7 +192,7 @@ void* getSharedDevicePtr(Falcor::SharedResourceApiHandle sharedHandle, uint32_t // Get a handle to that memory cudaExternalMemory_t externalMemory; - CUDA_CHECK(cudaImportExternalMemory(&externalMemory, &externalMemoryHandleDesc)); + FALCOR_CUDA_CHECK(cudaImportExternalMemory(&externalMemory, &externalMemoryHandleDesc)); // Create a descriptor for our shared buffer pointer cudaExternalMemoryBufferDesc bufDesc; @@ -238,7 +201,7 @@ void* getSharedDevicePtr(Falcor::SharedResourceApiHandle sharedHandle, uint32_t // Actually map the buffer void* devPtr = nullptr; - CUDA_CHECK(cudaExternalMemoryGetMappedBuffer(&devPtr, externalMemory, &bufDesc)); + FALCOR_CUDA_CHECK(cudaExternalMemoryGetMappedBuffer(&devPtr, externalMemory, &bufDesc)); return devPtr; } @@ -249,3 +212,144 @@ bool freeSharedDevicePtr(void* ptr) return false; return cudaSuccess == cudaFree(ptr); } + +cudaMipmappedArray_t importTextureToMipmappedArray(ref pTex, uint32_t cudaUsageFlags) +{ + SharedResourceApiHandle sharedHandle = pTex->getSharedApiHandle(); + FALCOR_CHECK(sharedHandle, "Texture shared handle creation failed"); + + cudaExternalMemoryHandleDesc externalMemoryHandleDesc; + memset(&externalMemoryHandleDesc, 0, sizeof(externalMemoryHandleDesc)); + + externalMemoryHandleDesc.type = cudaExternalMemoryHandleTypeD3D12Resource; + externalMemoryHandleDesc.handle.win32.handle = sharedHandle; + externalMemoryHandleDesc.size = pTex->getTextureSizeInBytes(); + externalMemoryHandleDesc.flags = cudaExternalMemoryDedicated; + + cudaExternalMemory_t externalMemory; + FALCOR_CUDA_CHECK(cudaImportExternalMemory(&externalMemory, &externalMemoryHandleDesc)); + + // Map mipmapped array onto external memory + cudaExternalMemoryMipmappedArrayDesc mipDesc; + memset(&mipDesc, 0, sizeof(mipDesc)); + auto format = pTex->getFormat(); + mipDesc.formatDesc.x = getNumChannelBits(format, 0); + mipDesc.formatDesc.y = getNumChannelBits(format, 1); + mipDesc.formatDesc.z = getNumChannelBits(format, 2); + mipDesc.formatDesc.w = getNumChannelBits(format, 3); + mipDesc.formatDesc.f = (getFormatType(format) == FormatType::Float) ? cudaChannelFormatKindFloat : cudaChannelFormatKindUnsigned; + mipDesc.extent.depth = 1; + mipDesc.extent.width = pTex->getWidth(); + mipDesc.extent.height = pTex->getHeight(); + mipDesc.flags = cudaUsageFlags; + mipDesc.numLevels = 1; + + cudaMipmappedArray_t mipmappedArray; + FALCOR_CUDA_CHECK(cudaExternalMemoryGetMappedMipmappedArray(&mipmappedArray, externalMemory, &mipDesc)); + return mipmappedArray; +} + +cudaSurfaceObject_t mapTextureToSurface(ref pTex, uint32_t cudaUsageFlags) +{ + // Create a mipmapped array from the texture + cudaMipmappedArray_t mipmap = importTextureToMipmappedArray(pTex, cudaUsageFlags); + + // Grab level 0 + cudaArray_t cudaArray; + FALCOR_CUDA_CHECK(cudaGetMipmappedArrayLevel(&cudaArray, mipmap, 0)); + + // Create cudaSurfObject_t from CUDA array + cudaResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + resDesc.res.array.array = cudaArray; + resDesc.resType = cudaResourceTypeArray; + + cudaSurfaceObject_t surface; + FALCOR_CUDA_CHECK(cudaCreateSurfaceObject(&surface, &resDesc)); + return surface; +} + +inline int32_t findDeviceByLUID(const std::vector& devices, const AdapterLUID& luid) +{ + for (int32_t i = 0; i < devices.size(); ++i) + { +#if FALCOR_WINDOWS + // On Windows, we compare the 8-byte LUID. The LUID is the same for + // D3D12, Vulkan and CUDA. + static_assert(sizeof(cudaDeviceProp::luid) <= sizeof(AdapterLUID::luid)); + if (std::memcmp(devices[i].luid, luid.luid.data(), sizeof(cudaDeviceProp::luid)) == 0) + return i; +#elif FALCOR_LINUX + // On Linux, the LUID is not supported. Instead we compare the 16-byte + // UUID which GFX conveniently returns in-place of the LUID. + static_assert(sizeof(cudaDeviceProp::uuid) <= sizeof(AdapterLUID::luid)); + if (std::memcmp(&devices[i].uuid, luid.luid.data(), sizeof(cudaDeviceProp::uuid)) == 0) + return i; +#endif + } + return -1; +} + +inline int32_t findDeviceByName(const std::vector& devices, std::string_view name) +{ + for (int32_t i = 0; i < devices.size(); ++i) + if (devices[i].name == name) + return i; + return -1; +} + +CudaDevice::CudaDevice(const Device* pDevice) +{ + FALCOR_CHECK(pDevice, "'pDevice' is nullptr."); + FALCOR_CU_CHECK(cuInit(0)); + + // Get a list of all available CUDA devices. + int32_t deviceCount; + FALCOR_CUDA_CHECK(cudaGetDeviceCount(&deviceCount)); + std::vector devices(deviceCount); + for (int32_t i = 0; i < deviceCount; ++i) + FALCOR_CUDA_CHECK(cudaGetDeviceProperties(&devices[i], i)); + + // First we try to find the matching CUDA device by LUID. + int32_t selectedDevice = findDeviceByLUID(devices, pDevice->getInfo().adapterLUID); + if (selectedDevice < 0) + { + logWarning("Failed to find CUDA device by LUID. Falling back to device name."); + // Next we try to find the matching CUDA device by name. + selectedDevice = findDeviceByName(devices, pDevice->getInfo().adapterName); + if (selectedDevice < 0) + { + logWarning("Failed to find CUDA device by name. Falling back to first compatible device."); + // Finally we try to find the first compatible CUDA device. + for (int32_t i = 0; i < devices.size(); ++i) + { + if (devices[i].major >= 7) + { + selectedDevice = i; + break; + } + } + } + } + + if (selectedDevice < 0) + FALCOR_THROW("No compatible CUDA device found."); + + FALCOR_CUDA_CHECK(cudaSetDevice(selectedDevice)); + FALCOR_CU_CHECK(cuDeviceGet(&mCudaDevice, selectedDevice)); + FALCOR_CU_CHECK(cuDevicePrimaryCtxRetain(&mCudaContext, mCudaDevice)); + FALCOR_CU_CHECK(cuStreamCreate(&mCudaStream, CU_STREAM_DEFAULT)); + + const auto& props = devices[selectedDevice]; + logInfo("Created CUDA device '{}' (architecture {}.{}).", props.name, props.major, props.minor); +} + +CudaDevice::~CudaDevice() +{ + FALCOR_CU_CHECK(cuStreamDestroy(mCudaStream)); + FALCOR_CU_CHECK(cuDevicePrimaryCtxRelease(mCudaDevice)); +} + +} // namespace cuda_utils + +} // namespace Falcor diff --git a/Source/Falcor/Utils/CudaUtils.h b/Source/Falcor/Utils/CudaUtils.h index 16e1aabde..289ebf213 100644 --- a/Source/Falcor/Utils/CudaUtils.h +++ b/Source/Falcor/Utils/CudaUtils.h @@ -27,76 +27,205 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" +#include "Core/Object.h" +#include "Core/API/fwd.h" #include "Core/API/Handles.h" -#include "Core/API/Buffer.h" +#include "Core/API/Device.h" +#include "Core/API/Fence.h" + +#include +#include "CudaRuntime.h" // Instead of to avoid name clashes. + #include -// Note: There's some CUDA / Falcor type conflicts. This header includes Falcor-facing functions. -// Including or was a pain we wanted to avoid, so CUDA-specific stuff lives in CudaUtils.cpp +#define FALCOR_CUDA_CHECK(call) \ + { \ + cudaError_t result = call; \ + if (result != cudaSuccess) \ + { \ + const char* errorName = cudaGetErrorName(result); \ + const char* errorString = cudaGetErrorString(result); \ + FALCOR_THROW("CUDA call {} failed with error {} ({}).", #call, errorName, errorString); \ + } \ + } + +#define FALCOR_CU_CHECK(call) \ + do \ + { \ + CUresult result = call; \ + if (result != CUDA_SUCCESS) \ + { \ + const char* errorName; \ + cuGetErrorName(result, &errorName); \ + const char* errorString; \ + cuGetErrorString(result, &errorString); \ + FALCOR_THROW("CUDA call {} failed with error {} ({}).", #call, errorName, errorString); \ + } \ + } while (0) -/// CUDA device pointer +/// CUDA device pointer. typedef unsigned long long CUdeviceptr; +namespace Falcor +{ + +namespace cuda_utils +{ +FALCOR_API void deviceSynchronize(); + +FALCOR_API void* mallocDevice(size_t size); +FALCOR_API void freeDevice(void* devPtr); + +FALCOR_API void memcpyDeviceToDevice(void* dst, const void* src, size_t count); +FALCOR_API void memcpyHostToDevice(void* dst, const void* src, size_t count); +FALCOR_API void memcpyDeviceToHost(void* dst, const void* src, size_t count); + +FALCOR_API void memsetDevice(void* devPtr, int value, size_t count); + +FALCOR_API cudaExternalMemory_t importExternalMemory(const Buffer* buffer); +FALCOR_API void destroyExternalMemory(cudaExternalMemory_t extMem); +FALCOR_API void* externalMemoryGetMappedBuffer(cudaExternalMemory_t extMem, size_t offset, size_t size); + +FALCOR_API cudaExternalSemaphore_t importExternalSemaphore(const Fence* fence); +FALCOR_API void destroyExternalSemaphore(cudaExternalSemaphore_t extSem); +FALCOR_API void signalExternalSemaphore(cudaExternalSemaphore_t extSem, uint64_t value, cudaStream_t stream = 0); +FALCOR_API void waitExternalSemaphore(cudaExternalSemaphore_t extSem, uint64_t value, cudaStream_t stream = 0); + /** * Get CUDA device pointer for shared resource. - * This takes a Windows Handle (e.g., from Falcor::Resource::getSharedApiHandle()) on a resource - * that has been declared "shared" [with Resource::BindFlags::Shared] plus a size of that + * This takes a Windows Handle (e.g., from Resource::getSharedApiHandle()) on a resource + * that has been declared "shared" [with ResourceBindFlags::Shared] plus a size of that * resource in bytes and returns a CUDA device pointer from that resource that can be * passed into OptiX or CUDA. This pointer is a value returned from * cudaExternalMemoryGetMappedBuffer(), so should follow its rules (e.g., the docs claim * you are responsible for calling cudaFree() on this pointer). */ -FALCOR_API void* getSharedDevicePtr(Falcor::SharedResourceApiHandle sharedHandle, uint32_t bytes); +FALCOR_API void* getSharedDevicePtr(SharedResourceApiHandle sharedHandle, uint32_t bytes); /** * Calls cudaFree() on the provided pointer. */ FALCOR_API bool freeSharedDevicePtr(void* ptr); -FALCOR_API unsigned int initCuda(void); +/** + * Imports the texture into a CUDA mipmapped array and returns the array in mipmappedArray. This method should only be + * called once per texture resource. + * @param pTex Pointer to the texture being imported + * @param usageFlags The requested flags to be bound to the mipmapped array + * @return Returns the imported mipmapped array. + */ +FALCOR_API cudaMipmappedArray_t importTextureToMipmappedArray(ref pTex, uint32_t cudaUsageFlags); -FALCOR_API void setCUDAContext(void); +/** + * Maps a texture to a surface object which can be read and written within a CUDA kernel. + * This method should only be called once per texture on initial load. Store the returned surface object for repeated + * use. + * @param pTex Pointer to the texture being mapped + * @param usageFlags The requested flags to be bound to the underlying mipmapped array that will be used to create the + * surface object + * @return The surface object that the input texture is bound to. + */ +FALCOR_API cudaSurfaceObject_t mapTextureToSurface(ref pTex, uint32_t usageFlags); -FALCOR_API void myCudaMemset(void* devPtr, int value, size_t count); +/// Wraps a CUDA device, context and stream. +class FALCOR_API CudaDevice : public Object +{ + FALCOR_OBJECT(cuda_utils::CudaDevice) +public: + /// Constructor. + /// Creates a CUDA device on the same adapter as the Falcor device. + CudaDevice(const Device* pDevice); + ~CudaDevice(); -FALCOR_API void cudaCopyDeviceToDevice(void* dst, const void* src, size_t count); + CUdevice getDevice() const { return mCudaDevice; } + CUcontext getContext() const { return mCudaContext; } + CUstream getStream() const { return mCudaStream; } -FALCOR_API void syncCudaDevice(); +private: + CUdevice mCudaDevice; + CUcontext mCudaContext; + CUstream mCudaStream; +}; -FALCOR_API void cudaCopyHostToDevice(void* dst, const void* src, size_t count); +/// Wraps an external memory resource. +class ExternalMemory : public Object +{ + FALCOR_OBJECT(cuda_utils::ExternalMemory) +public: + ExternalMemory(ref pResource) : mpResource(pResource.get()) + { + FALCOR_CHECK(mpResource, "'resource' is null."); + if (auto pBuffer = pResource->asBuffer()) + { + mExternalMemory = importExternalMemory(pBuffer.get()); + mSize = pBuffer->getSize(); + } + else + { + FALCOR_THROW("'resource' must be a buffer."); + } + } -/** - * Utility class for a GPU/device buffer for use with CUDA. - * Adapted from Ingo Wald's SIGGRAPH 2019 tutorial code for OptiX 7. - */ -class FALCOR_API CudaBuffer + ~ExternalMemory() { destroyExternalMemory(mExternalMemory); } + + size_t getSize() const { return mSize; } + + void* getMappedData() const + { + if (!mMappedData) + mMappedData = externalMemoryGetMappedBuffer(mExternalMemory, 0, mSize); + return mMappedData; + } + +private: + /// Keep a non-owning pointer to the resource. + /// TODO: If available, we should use a weak_ref here. + Resource* mpResource; + cudaExternalMemory_t mExternalMemory; + size_t mSize; + mutable void* mMappedData{nullptr}; +}; + +/// Wraps an external semaphore. +class ExternalSemaphore : public Object { + FALCOR_OBJECT(cuda_utils::ExternalSemaphore) public: - CudaBuffer() {} + ExternalSemaphore(ref pFence) : mpFence(pFence.get()) + { + FALCOR_CHECK(mpFence, "'fence' is null."); + FALCOR_CHECK(mpFence->getDesc().shared, "'fence' must be created with shared=true."); + mExternalSemaphore = importExternalSemaphore(mpFence); + } - CUdeviceptr getDevicePtr() { return (CUdeviceptr)mpDevicePtr; } - size_t getSize() { return mSizeBytes; } + ~ExternalSemaphore() { destroyExternalSemaphore(mExternalSemaphore); } - void allocate(size_t size); - void resize(size_t size); - void free(); + void signal(uint64_t value, cudaStream_t stream = 0) { signalExternalSemaphore(mExternalSemaphore, value, stream); } - template - void allocAndUpload(const std::vector& vt); + void wait(uint64_t value, cudaStream_t stream = 0) { waitExternalSemaphore(mExternalSemaphore, value, stream); } - template - bool download(T* t, size_t count); + void waitForCuda(CopyContext* pCopyContext, cudaStream_t stream = 0, uint64_t value = Fence::kAuto) + { + uint64_t signalValue = mpFence->updateSignaledValue(value); + signal(signalValue, stream); + pCopyContext->wait(mpFence, signalValue); + } - template - bool upload(const T* t, size_t count); + void waitForFalcor(CopyContext* pCopyContext, cudaStream_t stream = 0, uint64_t value = Fence::kAuto) + { + uint64_t signalValue = pCopyContext->signal(mpFence, value); + wait(signalValue, stream); + } private: - size_t mSizeBytes = 0; - void* mpDevicePtr = nullptr; + /// Keep a non-owning pointer to the fence. + /// TODO: If available, we should use a weak_ref here. + Fence* mpFence; + cudaExternalSemaphore_t mExternalSemaphore; }; -namespace Falcor -{ +} // namespace cuda_utils + /** * Structure to encapsulate DX <-> CUDA interop data for a buffer. */ @@ -109,7 +238,7 @@ struct InteropBuffer { if (devicePtr) { - freeSharedDevicePtr((void*)devicePtr); + cuda_utils::freeSharedDevicePtr((void*)devicePtr); devicePtr = (CUdeviceptr)0; } } @@ -120,11 +249,11 @@ inline InteropBuffer createInteropBuffer(ref pDevice, size_t byteSize) InteropBuffer interop; // Create a new DX <-> CUDA shared buffer using the Falcor API to create, then find its CUDA pointer. - interop.buffer = Buffer::create( - pDevice, byteSize, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::Shared - ); - interop.devicePtr = (CUdeviceptr)getSharedDevicePtr(interop.buffer->getSharedApiHandle(), (uint32_t)interop.buffer->getSize()); - checkInvariant(interop.devicePtr != (CUdeviceptr)0, "Failed to create CUDA device ptr for buffer"); + interop.buffer = + pDevice->createBuffer(byteSize, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::Shared); + interop.devicePtr = + (CUdeviceptr)cuda_utils::getSharedDevicePtr(interop.buffer->getSharedApiHandle(), (uint32_t)interop.buffer->getSize()); + FALCOR_CHECK(interop.devicePtr != (CUdeviceptr)0, "Failed to create CUDA device ptr for buffer"); return interop; } diff --git a/Source/Falcor/Utils/Debug/PixelDebug.cpp b/Source/Falcor/Utils/Debug/PixelDebug.cpp index 19a813889..00586a65a 100644 --- a/Source/Falcor/Utils/Debug/PixelDebug.cpp +++ b/Source/Falcor/Utils/Debug/PixelDebug.cpp @@ -28,7 +28,7 @@ #include "PixelDebug.h" #include "Core/API/Device.h" #include "Core/API/RenderContext.h" -#include "Core/Program/ComputeProgram.h" +#include "Core/Program/Program.h" #include "Core/Program/ShaderVar.h" #include "Utils/Logger.h" #include "Utils/UI/InputTypes.h" @@ -40,81 +40,74 @@ namespace Falcor { namespace { -static_assert(sizeof(PixelLogValue) % 16 == 0, "PixelLogValue size should be a multiple of 16B"); - -const char kReflectPixelDebugTypesFile[] = "Utils/Debug/ReflectPixelDebugTypes.cs.slang"; +static_assert(sizeof(PrintRecord) % 16 == 0, "PrintRecord size should be a multiple of 16B"); } // namespace -PixelDebug::PixelDebug(ref pDevice, uint32_t logSize) : mpDevice(pDevice), mLogSize(logSize) {} +PixelDebug::PixelDebug(ref pDevice, uint32_t printCapacity, uint32_t assertCapacity) + : mpDevice(pDevice), mPrintCapacity(printCapacity), mAssertCapacity(assertCapacity) +{} void PixelDebug::beginFrame(RenderContext* pRenderContext, const uint2& frameDim) { + FALCOR_CHECK(!mRunning, "Logging is already running, did you forget to call endFrame()?"); + mFrameDim = frameDim; - if (mRunning) - { - throw RuntimeError("PixelDebug::beginFrame() - Logging is already running, did you forget to call endFrame()?"); - } mRunning = true; // Reset previous data. - mPixelLogData.clear(); - mAssertLogData.clear(); + mPrintData.clear(); + mAssertData.clear(); mDataValid = false; mWaitingForData = false; if (mEnabled) { - // Prepare log buffers. - if (!mpPixelLog || mpPixelLog->getElementCount() != mLogSize) + // Prepare buffers. + if (!mpPrintBuffer) { - // Create program for type reflection. - if (!mpReflectProgram) - mpReflectProgram = ComputeProgram::createFromFile(mpDevice, kReflectPixelDebugTypesFile, "main"); - // Allocate GPU buffers. const ref& pDevice = pRenderContext->getDevice(); - mpPixelLog = Buffer::createStructured(pDevice, mpReflectProgram.get(), "gPixelLog", mLogSize); - if (mpPixelLog->getStructSize() != sizeof(PixelLogValue)) - throw RuntimeError("Struct PixelLogValue size mismatch between CPU/GPU"); - - mpAssertLog = Buffer::createStructured(pDevice, mpReflectProgram.get(), "gAssertLog", mLogSize); - if (mpAssertLog->getStructSize() != sizeof(AssertLogValue)) - throw RuntimeError("Struct AssertLogValue size mismatch between CPU/GPU"); - - // Allocate staging buffers for readback. These are shared, the data is stored consecutively. - mpCounterBuffer = Buffer::create(pDevice, 2 * sizeof(uint32_t), ResourceBindFlags::None, Buffer::CpuAccess::Read); - mpDataBuffer = - Buffer::create(pDevice, mpPixelLog->getSize() + mpAssertLog->getSize(), ResourceBindFlags::None, Buffer::CpuAccess::Read); + mpCounterBuffer = pDevice->createBuffer(sizeof(uint32_t) * 2); + mpPrintBuffer = pDevice->createStructuredBuffer(sizeof(PrintRecord), mPrintCapacity); + mpAssertBuffer = pDevice->createStructuredBuffer(sizeof(AssertRecord), mAssertCapacity); + + // Allocate readback buffer. This buffer is shared for copying all the above buffers to the CPU + mpReadbackBuffer = pDevice->createBuffer( + mpCounterBuffer->getSize() + mpPrintBuffer->getSize() + mpAssertBuffer->getSize(), + ResourceBindFlags::None, + MemoryType::ReadBack + ); } - pRenderContext->clearUAVCounter(mpPixelLog, 0); - pRenderContext->clearUAVCounter(mpAssertLog, 0); + pRenderContext->clearUAV(mpCounterBuffer->getUAV().get(), uint4(0)); } } void PixelDebug::endFrame(RenderContext* pRenderContext) { - if (!mRunning) - { - throw RuntimeError("PixelDebug::endFrame() - Logging is not running, did you forget to call beginFrame()?"); - } + FALCOR_CHECK(mRunning, "Logging is not running, did you forget to call beginFrame()?"); + mRunning = false; if (mEnabled) { // Copy logged data to staging buffers. - pRenderContext->copyBufferRegion(mpCounterBuffer.get(), 0, mpPixelLog->getUAVCounter().get(), 0, 4); - pRenderContext->copyBufferRegion(mpCounterBuffer.get(), 4, mpAssertLog->getUAVCounter().get(), 0, 4); - pRenderContext->copyBufferRegion(mpDataBuffer.get(), 0, mpPixelLog.get(), 0, mpPixelLog->getSize()); - pRenderContext->copyBufferRegion(mpDataBuffer.get(), mpPixelLog->getSize(), mpAssertLog.get(), 0, mpAssertLog->getSize()); + uint32_t dst = 0; + pRenderContext->copyBufferRegion(mpReadbackBuffer.get(), dst, mpCounterBuffer.get(), 0, mpCounterBuffer->getSize()); + dst += mpCounterBuffer->getSize(); + pRenderContext->copyBufferRegion(mpReadbackBuffer.get(), dst, mpPrintBuffer.get(), 0, mpPrintBuffer->getSize()); + dst += mpPrintBuffer->getSize(); + pRenderContext->copyBufferRegion(mpReadbackBuffer.get(), dst, mpAssertBuffer.get(), 0, mpAssertBuffer->getSize()); + dst += mpAssertBuffer->getSize(); + FALCOR_ASSERT(dst == mpReadbackBuffer->getSize()); // Create fence first time we need it. if (!mpFence) - mpFence = GpuFence::create(mpDevice); + mpFence = mpDevice->createFence(); // Submit command list and insert signal. - pRenderContext->flush(false); - mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + pRenderContext->submit(false); + pRenderContext->signal(mpFence.get()); mWaitingForData = true; } @@ -122,16 +115,19 @@ void PixelDebug::endFrame(RenderContext* pRenderContext) void PixelDebug::prepareProgram(const ref& pProgram, const ShaderVar& var) { - FALCOR_ASSERT(mRunning); + FALCOR_CHECK(mRunning, "Logging is not running, did you forget to call beginFrame()?"); if (mEnabled) { pProgram->addDefine("_PIXEL_DEBUG_ENABLED"); - var["gPixelLog"] = mpPixelLog; - var["gAssertLog"] = mpAssertLog; - var["PixelDebugCB"]["gPixelLogSelected"] = mSelectedPixel; - var["PixelDebugCB"]["gPixelLogSize"] = mLogSize; - var["PixelDebugCB"]["gAssertLogSize"] = mLogSize; + + ShaderVar pixelDebug = var["gPixelDebug"]; + pixelDebug["counterBuffer"] = mpCounterBuffer; + pixelDebug["printBuffer"] = mpPrintBuffer; + pixelDebug["assertBuffer"] = mpAssertBuffer; + pixelDebug["printBufferCapacity"] = mPrintCapacity; + pixelDebug["assertBufferCapacity"] = mAssertCapacity; + pixelDebug["selectedPixel"] = mSelectedPixel; const auto& hashedStrings = pProgram->getReflector()->getHashedStrings(); for (const auto& hashedString : hashedStrings) @@ -147,10 +143,7 @@ void PixelDebug::prepareProgram(const ref& pProgram, const ShaderVar& v void PixelDebug::renderUI(Gui::Widgets* widget) { - if (mRunning) - { - throw RuntimeError("PixelDebug::renderUI() - Logging is running, call end() before renderUI()."); - } + FALCOR_CHECK(!mRunning, "Logging is running, call endFrame() before renderUI()."); if (widget) { @@ -177,8 +170,8 @@ void PixelDebug::renderUI(Gui::Widgets* widget) std::ostringstream oss; // Print list of printed values. - oss << "Pixel log:" << (mPixelLogData.empty() ? " \n" : "\n"); - for (auto v : mPixelLogData) + oss << "Pixel log:" << (mPrintData.empty() ? " \n" : "\n"); + for (auto v : mPrintData) { // Print message. auto it = mHashToString.find(v.msgHash); @@ -191,18 +184,18 @@ void PixelDebug::renderUI(Gui::Widgets* widget) for (uint32_t i = 0; i < v.count; i++) { uint32_t bits = v.data[i]; - switch ((PixelLogValueType)v.type) + switch ((PrintValueType)v.type) { - case PixelLogValueType::Bool: + case PrintValueType::Bool: oss << (bits != 0 ? "true" : "false"); break; - case PixelLogValueType::Int: + case PrintValueType::Int: oss << (int32_t)bits; break; - case PixelLogValueType::Uint: + case PrintValueType::Uint: oss << bits; break; - case PixelLogValueType::Float: + case PrintValueType::Float: oss << fstd::bit_cast(bits); break; default: @@ -218,10 +211,10 @@ void PixelDebug::renderUI(Gui::Widgets* widget) } // Print list of asserts. - if (!mAssertLogData.empty()) + if (!mAssertData.empty()) { oss << "\n"; - for (auto v : mAssertLogData) + for (auto v : mAssertData) { oss << "Assert at (" << v.launchIndex.x << ", " << v.launchIndex.y << ", " << v.launchIndex.z << ")"; auto it = mHashToString.find(v.msgHash); @@ -234,7 +227,7 @@ void PixelDebug::renderUI(Gui::Widgets* widget) if (widget) widget->text(oss.str()); - bool isEmpty = mPixelLogData.empty() && mAssertLogData.empty(); + bool isEmpty = mPrintData.empty() && mAssertData.empty(); if (isNewData && !isEmpty) logInfo("\n" + oss.str()); } @@ -259,30 +252,26 @@ bool PixelDebug::copyDataToCPU() if (mWaitingForData) { // Wait for signal. - mpFence->syncCpu(); + mpFence->wait(); mWaitingForData = false; if (mEnabled) { - // Map counter buffer. This tells us how many print() and assert() calls were made. - uint32_t* uavCounters = (uint32_t*)mpCounterBuffer->map(Buffer::MapType::Read); - const uint32_t printCount = std::min(mpPixelLog->getElementCount(), uavCounters[0]); - const uint32_t assertCount = std::min(mpAssertLog->getElementCount(), uavCounters[1]); - mpCounterBuffer->unmap(); - - // Map the data buffer and copy the relevant sections. - uint8_t* pLog = (uint8_t*)mpDataBuffer->map(Buffer::MapType::Read); + // Copy data from readback buffer to CPU buffers. + const uint8_t* data = reinterpret_cast(mpReadbackBuffer->map(Buffer::MapType::Read)); + const uint32_t* counterData = reinterpret_cast(data); + data += mpCounterBuffer->getSize(); + const PrintRecord* printData = reinterpret_cast(data); + data += mpPrintBuffer->getSize(); + const AssertRecord* assertData = reinterpret_cast(data); - mPixelLogData.resize(printCount); - for (uint32_t i = 0; i < printCount; i++) - mPixelLogData[i] = ((PixelLogValue*)pLog)[i]; - pLog += mpPixelLog->getSize(); + const uint32_t printCount = std::min(mpPrintBuffer->getElementCount(), counterData[0]); + const uint32_t assertCount = std::min(mpAssertBuffer->getElementCount(), counterData[1]); - mAssertLogData.resize(assertCount); - for (uint32_t i = 0; i < assertCount; i++) - mAssertLogData[i] = ((AssertLogValue*)pLog)[i]; + mPrintData.assign(printData, printData + printCount); + mAssertData.assign(assertData, assertData + assertCount); - mpDataBuffer->unmap(); + mpReadbackBuffer->unmap(); mDataValid = true; return true; } diff --git a/Source/Falcor/Utils/Debug/PixelDebug.h b/Source/Falcor/Utils/Debug/PixelDebug.h index dc05693e5..69ed4c652 100644 --- a/Source/Falcor/Utils/Debug/PixelDebug.h +++ b/Source/Falcor/Utils/Debug/PixelDebug.h @@ -29,7 +29,7 @@ #include "PixelDebugTypes.slang" #include "Core/Macros.h" #include "Core/API/Buffer.h" -#include "Core/API/GpuFence.h" +#include "Core/API/Fence.h" #include "Core/Program/Program.h" #include "Utils/UI/Gui.h" #include @@ -68,9 +68,10 @@ class FALCOR_API PixelDebug /** * Constructor. Throws an exception on error. * @param[in] pDevice GPU device. - * @param[in] logSize Number of shader print() and assert() statements per frame. + * @param[in] printCapacity Maximum number of shader print() statements per frame. + * @param[in] assertCapacity Maximum number of shader assert() statements per frame. */ - PixelDebug(ref pDevice, uint32_t logSize = 100); + PixelDebug(ref pDevice, uint32_t printCapacity = 100, uint32_t assertCapacity = 100); void beginFrame(RenderContext* pRenderContext, const uint2& frameDim); void endFrame(RenderContext* pRenderContext); @@ -93,11 +94,11 @@ class FALCOR_API PixelDebug // Internal state ref mpDevice; ref mpReflectProgram; ///< Program for reflection of types. - ref mpPixelLog; ///< Pixel log on the GPU with UAV counter. - ref mpAssertLog; ///< Assert log on the GPU with UAV counter. - ref mpCounterBuffer; ///< Staging buffer for async readback of UAV counters. - ref mpDataBuffer; ///< Staging buffer for async readback of logged data. - ref mpFence; ///< GPU fence for sychronizing readback. + ref mpCounterBuffer; ///< Counter buffer (print, assert) on the GPU. + ref mpPrintBuffer; ///< Print buffer on the GPU. + ref mpAssertBuffer; ///< Assert buffer on the GPU. + ref mpReadbackBuffer; ///< Staging buffer for async readback of all data. + ref mpFence; ///< GPU fence for sychronizing readback. // Configuration bool mEnabled = false; ///< Enable debugging features. @@ -112,9 +113,10 @@ class FALCOR_API PixelDebug std::unordered_map mHashToString; ///< Map of string hashes to string values. - std::vector mPixelLogData; ///< Pixel log data read back from the GPU. - std::vector mAssertLogData; ///< Assert log data read back from the GPU. + std::vector mPrintData; ///< Print data read back from the GPU. + std::vector mAssertData; ///< Assert log data read back from the GPU. - const uint32_t mLogSize = 0; ///< Size of the log buffers in elements. + const uint32_t mPrintCapacity = 0; ///< Capacity of the print buffer in elements. + const uint32_t mAssertCapacity = 0; ///< Capacity of the assert buffer in elements. }; } // namespace Falcor diff --git a/Source/Falcor/Utils/Debug/PixelDebug.slang b/Source/Falcor/Utils/Debug/PixelDebug.slang index a286ff2a9..add9cceea 100644 --- a/Source/Falcor/Utils/Debug/PixelDebug.slang +++ b/Source/Falcor/Utils/Debug/PixelDebug.slang @@ -38,25 +38,76 @@ * * The host sets the following defines: * - * _PIXEL_DEBUG_ENABLED Nonzero when pixel debugging is enabled. + * _PIXEL_DEBUG_ENABLED Defined when pixel debugging is enabled. * */ import PixelDebugTypes; -cbuffer PixelDebugCB +struct PixelDebug { - uint2 gPixelLogSelected; // Currently selected pixel to log. - uint gPixelLogSize; // Number of elements in the output buffer. - uint gAssertLogSize; -}; + RWByteAddressBuffer counterBuffer; + RWStructuredBuffer printBuffer; + RWStructuredBuffer assertBuffer; + + uint printBufferCapacity; ///< Capacity of the print buffer. + uint assertBufferCapacity; ///< Capacity of the assert buffer. + + uint2 selectedPixel; ///< Currently selected pixel to log. + + /** + * Add print record. + * @param[in] pixel The pixel the print originated from. + * @param[in] msg Message string. + * @param[in] valueType The type of values encoded into `data` + * @param[in] count The number of components of `valueType` encoded into `data` + * @param[in] data The bits of the value to be printed. + */ + [ForceInline] + void print(uint2 pixel, String msg, PrintValueType valueType, int count, uint4 data) + { + // TODO: Previously this was an early out: + // if (all(pixel == selectedPixel)) return; + // Due to a bug in slang this leads to expontentially growing compile times. + // This will be fixed in the compiler. Once it is, we should revert this workaround. + if (all(pixel == selectedPixel)) + { + uint index = 0; + counterBuffer.InterlockedAdd(0, 1, index); + if (index < printBufferCapacity) + { + PrintRecord rec = {}; + rec.msgHash = getStringHash(msg); + rec.type = (uint)valueType; + rec.count = count; + rec.data = data; + printBuffer[index] = rec; + } + } + } -RWStructuredBuffer gPixelLog; -RWStructuredBuffer gAssertLog; + /** + * Add an assert record. + * @param[in] pixel The pixel the print originated from. + * @param[in] msg Message string. + */ + [ForceInline] + void assert(uint2 pixel, String msg) + { + uint index = 0; + counterBuffer.InterlockedAdd(4, 1, index); + if (index < assertBufferCapacity) + { + AssertRecord rec; + rec.launchIndex = uint3(pixel, 0); + rec.msgHash = getStringHash(msg); + assertBuffer[index] = rec; + } + } +}; -#ifdef _PIXEL_DEBUG_ENABLED +ParameterBlock gPixelDebug; static uint2 gPixelDebugPixel; -#endif void printSetPixel(uint2 pixel) { @@ -71,8 +122,8 @@ interface IPrintable /// Convert this value to a single `uint` holding its bits. uint getPrintableDataVal(); - /// Get the `PixelLogValueType` that represents this type. - static PixelLogValueType getPrintableValueType(); + /// Get the `PrintValueType` that represents this type. + static PrintValueType getPrintableValueType(); } /// Values of type `bool` are printable. @@ -82,9 +133,9 @@ extension bool : IPrintable { return uint(this); } - static PixelLogValueType getPrintableValueType() + static PrintValueType getPrintableValueType() { - return PixelLogValueType::Bool; + return PrintValueType::Bool; } } @@ -95,9 +146,9 @@ extension int : IPrintable { return asuint(this); } - static PixelLogValueType getPrintableValueType() + static PrintValueType getPrintableValueType() { - return PixelLogValueType::Int; + return PrintValueType::Int; } } @@ -108,9 +159,9 @@ extension uint : IPrintable { return this; } - static PixelLogValueType getPrintableValueType() + static PrintValueType getPrintableValueType() { - return PixelLogValueType::Uint; + return PrintValueType::Uint; } } @@ -121,45 +172,14 @@ extension float : IPrintable { return asuint(this); } - static PixelLogValueType getPrintableValueType() + static PrintValueType getPrintableValueType() { - return PixelLogValueType::Float; + return PrintValueType::Float; } } #ifdef _PIXEL_DEBUG_ENABLED -/** - * Add a value to the pixel log if the current pixel is the selected one. - * @param[in] msg A string message to accompany the value. - * @param[in] valueType The type of values encoded into `data` - * @param[in] count The number of components of `valueType` encoded into `data` - * @param[in] data The bits of the value to be printed. - * - * This is a lower-level worker routine used by `print()`. - */ -[ForceInline] -void _print(String msg, PixelLogValueType valueType, int count, uint4 data) -{ - // TODO: Previously this was an early out: - // if (all(gPixelDebugPixel == gPixelLogSelected)) return; - // Due to a bug in slang this leads to expontentially growing compile times. - // This will be fixed in the compiler. Once it is, we should revert this workaround. - if (all(gPixelDebugPixel == gPixelLogSelected)) - { - uint i = gPixelLog.IncrementCounter(); - if (i < gPixelLogSize) - { - PixelLogValue val = {}; - val.msgHash = getStringHash(msg); - val.type = (uint)valueType; - val.count = count; - val.data = data; - gPixelLog[i] = val; - } - } -} - /** * Print a value if the current pixel is the selected one. * @param[in] msg A string message to accompany the value. @@ -170,7 +190,7 @@ void print(String msg, T v) { vector u = 0; u[0] = v.getPrintableDataVal(); - _print(msg, T.getPrintableValueType(), 1, u); + gPixelDebug.print(gPixelDebugPixel, msg, T.getPrintableValueType(), 1, u); } /** @@ -183,10 +203,8 @@ void print(String msg, vector v) { vector u = 0; for (int i = 0; i < N; ++i) - { u[i] = v[i].getPrintableDataVal(); - } - _print(msg, T.getPrintableValueType(), N, u); + gPixelDebug.print(gPixelDebugPixel, msg, T.getPrintableValueType(), N, u); } /** @@ -197,19 +215,10 @@ void print(String msg, vector v) void assert(bool condition, String msg = "") { if (!condition) - { - uint i = gAssertLog.IncrementCounter(); - if (i < gAssertLogSize) - { - AssertLogValue val; - val.launchIndex = uint3(gPixelDebugPixel, 0); - val.msgHash = getStringHash(msg); - gAssertLog[i] = val; - } - } + gPixelDebug.assert(gPixelDebugPixel, msg); } -#else +#else // _PIXEL_DEBUG_ENABLED /** * Define null functions if debugging is disabled. @@ -224,4 +233,4 @@ void print(String msg, T v) {} [ForceInline] void assert(bool condition, String msg = "") {} -#endif // !_PIXEL_DEBUG_ENABLED +#endif // _PIXEL_DEBUG_ENABLED diff --git a/Source/Falcor/Utils/Debug/PixelDebugTypes.slang b/Source/Falcor/Utils/Debug/PixelDebugTypes.slang index 6f96cd30c..7d3bf2129 100644 --- a/Source/Falcor/Utils/Debug/PixelDebugTypes.slang +++ b/Source/Falcor/Utils/Debug/PixelDebugTypes.slang @@ -31,7 +31,7 @@ BEGIN_NAMESPACE_FALCOR /// Define the basic types that print() supports. -enum class PixelLogValueType +enum class PrintValueType { Bool = 0, Int, @@ -39,16 +39,16 @@ enum class PixelLogValueType Float, }; -struct PixelLogValue +struct PrintRecord { uint msgHash; ///< String hash of print message. - uint type; ///< Value type (see PixelLogValueType). + uint type; ///< Value type (see PrintValueType). uint count; ///< Number of components (1-4). uint _pad0; ///< Padding. uint4 data; ///< The data bits. The encoding is determined by the data type. }; -struct AssertLogValue +struct AssertRecord { uint3 launchIndex; ///< Launch index for the assert. uint msgHash; ///< String hash of assert message. diff --git a/Source/Falcor/Utils/Debug/WarpProfiler.cpp b/Source/Falcor/Utils/Debug/WarpProfiler.cpp index 8d09173e9..db06c594d 100644 --- a/Source/Falcor/Utils/Debug/WarpProfiler.cpp +++ b/Source/Falcor/Utils/Debug/WarpProfiler.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "WarpProfiler.h" +#include "Core/API/Device.h" #include "Core/API/RenderContext.h" #include "Core/Program/ShaderVar.h" #include @@ -35,24 +36,22 @@ namespace Falcor WarpProfiler::WarpProfiler(ref pDevice, const uint32_t binCount) : mBinCount(binCount) { - mpFence = GpuFence::create(pDevice); + mpFence = pDevice->createFence(); uint32_t elemCount = binCount * kWarpSize; - mpHistogramBuffer = Buffer::createStructured( - pDevice, 4, elemCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, - false + mpHistogramBuffer = pDevice->createStructuredBuffer( + 4, elemCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false ); - mpHistogramStagingBuffer = - Buffer::createStructured(pDevice, 4, elemCount, ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr, false); + mpHistogramStagingBuffer = pDevice->createStructuredBuffer(4, elemCount, ResourceBindFlags::None, MemoryType::ReadBack, nullptr, false); } -void WarpProfiler::setShaderData(const ShaderVar& var) const +void WarpProfiler::bindShaderData(const ShaderVar& var) const { var["gWarpHistogram"] = mpHistogramBuffer; } void WarpProfiler::begin(RenderContext* pRenderContext) { - checkInvariant(!mActive, "WarpProfiler: begin() already called."); + FALCOR_CHECK(!mActive, "WarpProfiler: begin() already called."); pRenderContext->clearUAV(mpHistogramBuffer->getUAV().get(), uint4(0)); @@ -62,13 +61,13 @@ void WarpProfiler::begin(RenderContext* pRenderContext) void WarpProfiler::end(RenderContext* pRenderContext) { - checkInvariant(mActive, "WarpProfiler: end() called without preceding begin()."); + FALCOR_CHECK(mActive, "WarpProfiler: end() called without preceding begin()."); pRenderContext->copyResource(mpHistogramStagingBuffer.get(), mpHistogramBuffer.get()); // Submit command list and insert signal. - pRenderContext->flush(false); - mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + pRenderContext->submit(false); + pRenderContext->signal(mpFence.get()); mActive = false; mDataWaiting = true; @@ -78,8 +77,8 @@ std::vector WarpProfiler::getWarpHistogram(const uint32_t binIndex, co { readBackData(); - checkArgument(binIndex + binCount <= mBinCount, "WarpProfiler: Bin index out of range."); - checkInvariant(!mHistograms.empty(), "WarpProfiler: No available data. Did you call begin()/end()?"); + FALCOR_CHECK(binIndex + binCount <= mBinCount, "WarpProfiler: Bin index out of range."); + FALCOR_CHECK(!mHistograms.empty(), "WarpProfiler: No available data. Did you call begin()/end()?"); std::vector histogram(kWarpSize, 0); for (size_t i = binIndex; i < binIndex + binCount; i++) @@ -121,8 +120,8 @@ void WarpProfiler::readBackData() if (!mDataWaiting) return; - checkInvariant(!mActive, "WarpProfiler: readBackData() called without preceding before()/end() calls."); - mpFence->syncCpu(); + FALCOR_CHECK(!mActive, "WarpProfiler: readBackData() called without preceding before()/end() calls."); + mpFence->wait(); mHistograms.resize(mBinCount * kWarpSize); const uint32_t* data = reinterpret_cast(mpHistogramStagingBuffer->map(Buffer::MapType::Read)); diff --git a/Source/Falcor/Utils/Debug/WarpProfiler.h b/Source/Falcor/Utils/Debug/WarpProfiler.h index 15efee10e..8cd64c13e 100644 --- a/Source/Falcor/Utils/Debug/WarpProfiler.h +++ b/Source/Falcor/Utils/Debug/WarpProfiler.h @@ -29,7 +29,7 @@ #include "Core/Macros.h" #include "Core/API/Buffer.h" -#include "Core/API/GpuFence.h" +#include "Core/API/Fence.h" #include #include @@ -60,7 +60,7 @@ class FALCOR_API WarpProfiler * This function must be called before the profiler can be used. * @param[in] var Shader vars of the program to set data into. */ - void setShaderData(const ShaderVar& var) const; + void bindShaderData(const ShaderVar& var) const; /** * @brief Begin profiling. @@ -93,7 +93,7 @@ class FALCOR_API WarpProfiler private: void readBackData(); - ref mpFence; + ref mpFence; ref mpHistogramBuffer; ref mpHistogramStagingBuffer; diff --git a/Source/Falcor/Utils/InternalDictionary.h b/Source/Falcor/Utils/Dictionary.h similarity index 92% rename from Source/Falcor/Utils/InternalDictionary.h rename to Source/Falcor/Utils/Dictionary.h index ff29b801c..588e84f68 100644 --- a/Source/Falcor/Utils/InternalDictionary.h +++ b/Source/Falcor/Utils/Dictionary.h @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Errors.h" +#include "Core/Error.h" #include #include #include @@ -34,7 +34,7 @@ namespace Falcor { -class InternalDictionary +class Dictionary { public: class Value @@ -61,8 +61,8 @@ class InternalDictionary using Container = std::unordered_map; - InternalDictionary() = default; - InternalDictionary(const InternalDictionary& d) : mContainer(d.mContainer) {} + Dictionary() = default; + Dictionary(const Dictionary& d) : mContainer(d.mContainer) {} Value& operator[](const std::string& key) { return mContainer[key]; } const Value& operator[](const std::string& key) const { return mContainer.at(key); } @@ -83,8 +83,7 @@ class InternalDictionary T getValue(const std::string& key) { auto it = mContainer.find(key); - if (it == mContainer.end()) - throw ArgumentError("Key '{}' does not exist", key); + FALCOR_CHECK(it != mContainer.end(), "Key '{}' does not exist", key); return it->second; } diff --git a/Source/Falcor/Utils/Geometry/GeometryHelpers.slang b/Source/Falcor/Utils/Geometry/GeometryHelpers.slang index 0bc411665..a875e7bb5 100644 --- a/Source/Falcor/Utils/Geometry/GeometryHelpers.slang +++ b/Source/Falcor/Utils/Geometry/GeometryHelpers.slang @@ -41,10 +41,16 @@ import Utils.Math.MathHelpers; * The method is described in Ray Tracing Gems, Chapter 6, "A Fast and Robust * Method for Avoiding Self-Intersection" by Carsten Wächter and Nikolaus Binder. * + * (Temporarily) We'll substitute a simple identity method for the gradient computation + * since the asfloat(asint(x)) will break gradient propagation. + * * @param[in] pos Ray hit position. * @param[in] normal Face normal of hit surface (normalized). The offset will be in the positive direction. * @return Ray origin of the new ray. */ +[ForwardDerivative(fwd_computeRayOrigin)] +[BackwardDerivative(bwd_computeRayOrigin)] +[PreferRecompute] float3 computeRayOrigin(float3 pos, float3 normal) { @@ -61,6 +67,21 @@ float3 computeRayOrigin(float3 pos, float3 normal) return select(abs(pos) < origin, pos + fOff, iPos); } +[Differentiable] +[PreferRecompute] +DifferentialPair fwd_computeRayOrigin(DifferentialPair pos, DifferentialPair normal) +{ + return DifferentialPair(computeRayOrigin(pos.p, normal.p), pos.d); +} + +[Differentiable] +[PreferRecompute] +void bwd_computeRayOrigin(inout DifferentialPair pos, inout DifferentialPair normal, float3.Differential dOut) +{ + pos = diffPair(pos.p, dOut); + normal = diffPair(normal.p, float3.dzero()); +} + /******************************************************************* Bounding cones *******************************************************************/ @@ -409,6 +430,57 @@ float computeClippedTriangleArea2D(const float2 pos[3], const float2 minPoint, c return 0.5f * area; } +/** + * Computes the distance of a ray hitting a triangle (hit is guaranteed). + * @param[in] v Positions of the three vertices. + * @param[in] origin Ray origin. + * @param[in] direction Ray direction. + * @return Distance of the ray hitting the triangle. + */ +[Differentiable] +[PreferRecompute] +float computeRayDistanceToTriangle(const float3 v[3], const float3 origin, const float3 direction) +{ + float3 edge1 = v[1] - v[0]; + float3 edge2 = v[2] - v[0]; + float3 pvec = cross(direction, edge2); + float det = dot(edge1, pvec); + + float3 tvec = origin - v[0]; + float u = dot(tvec, pvec); + float3 qvec = cross(tvec, edge1); + float v = dot(direction, qvec); + + float t = dot(edge2, qvec) / det; + + return t; +} + +/** + * Computes the barycentric coordinates of a point on a triangle. + * @param[in] v Positions of the three vertices. + * @param[in] p Intersection point. + * @param[out] barycentrics Barycentric coordinates of the intersection. + */ +void computeBarycentrics(const float3 v[3], const float3 p, out float3 barycentrics) +{ + float3 edgep0 = v[0] - p; + float3 edgep1 = v[1] - p; + float3 edgep2 = v[2] - p; + + float area0 = length(cross(edgep1, edgep2)); + float area1 = length(cross(edgep2, edgep0)); + float area2 = length(cross(edgep0, edgep1)); + + float area = area0 + area1 + area2; + + float bx = area0 / area; + float by = area1 / area; + float bz = area2 / area; + + barycentrics = float3(bx, by, bz); +} + /******************************************************************* Curve uv parameterization *******************************************************************/ diff --git a/Source/Falcor/Utils/Geometry/IntersectionHelpers.slang b/Source/Falcor/Utils/Geometry/IntersectionHelpers.slang index 906b13f84..ff08b0b63 100644 --- a/Source/Falcor/Utils/Geometry/IntersectionHelpers.slang +++ b/Source/Falcor/Utils/Geometry/IntersectionHelpers.slang @@ -40,7 +40,16 @@ import Utils.Math.MathHelpers; * @param[out] t Distance to the closest intersection. * @return True if the ray intersects the sphere. */ -bool intersectRaySphere(const float3 rayOrigin, const float3 rayDir, const float3 sphereCenter, const float sphereRadius, out float t) +bool intersectRaySphere( + const float3 rayOrigin, + const float3 rayDir, + const float3 sphereCenter, + const float sphereRadius, + out float t, + const float tMin = 0.f, + const float tMax = 1e36f, + const bool backfaceCull = false +) { t = {}; @@ -55,10 +64,10 @@ bool intersectRaySphere(const float3 rayOrigin, const float3 rayDir, const float return false; // If b and discriminant are both 0, then the ray's origin lies on the sphere - if (b == 0 && discriminant == 0) + if (b == 0 && discriminant == 0 && tMin <= 0) { t = 0.f; - return true; + return (t <= tMax); } // There are two solutions t0 and t1, but one or both may be negative. @@ -68,12 +77,12 @@ bool intersectRaySphere(const float3 rayOrigin, const float3 rayDir, const float float t0 = c / q; float t1 = q / a; - float tc = t0 < 0.f ? t1 : t0; // tc is the closest hit we care about - if (tc < 0.f) + float tc = (t0 < tMin && !backfaceCull) ? t1 : t0; // tc is the closest hit we care about + if (tc < tMin) return false; t = tc; - return true; + return (t <= tMax); } /** diff --git a/Source/Falcor/Utils/HostDeviceShared.slangh b/Source/Falcor/Utils/HostDeviceShared.slangh index d258fb980..8b0fba430 100644 --- a/Source/Falcor/Utils/HostDeviceShared.slangh +++ b/Source/Falcor/Utils/HostDeviceShared.slangh @@ -47,10 +47,13 @@ /******************************************************************* CPU declarations *******************************************************************/ -#define BEGIN_NAMESPACE_FALCOR \ - namespace Falcor \ +#define BEGIN_NAMESPACE(name) \ + namespace name \ { -#define END_NAMESPACE_FALCOR } +#define END_NAMESPACE } +#define BEGIN_NAMESPACE_FALCOR BEGIN_NAMESPACE(Falcor) +#define END_NAMESPACE_FALCOR END_NAMESPACE + #define SETTER_DECL #define CONST_FUNCTION const #define STD_NAMESPACE std:: @@ -82,6 +85,8 @@ namespace Falcor #define constexpr const #define BEGIN_NAMESPACE_FALCOR #define END_NAMESPACE_FALCOR +#define BEGIN_NAMESPACE(name) +#define END_NAMESPACE #define SETTER_DECL [mutating] #define CONST_FUNCTION #define STD_NAMESPACE diff --git a/Source/Falcor/Utils/Image/AsyncTextureLoader.cpp b/Source/Falcor/Utils/Image/AsyncTextureLoader.cpp index 4c6357100..0a6925783 100644 --- a/Source/Falcor/Utils/Image/AsyncTextureLoader.cpp +++ b/Source/Falcor/Utils/Image/AsyncTextureLoader.cpp @@ -45,13 +45,13 @@ AsyncTextureLoader::~AsyncTextureLoader() { terminateWorkers(); - mpDevice->flushAndSync(); + mpDevice->wait(); } std::future> AsyncTextureLoader::loadMippedFromFiles( fstd::span paths, bool loadAsSrgb, - Resource::BindFlags bindFlags, + ResourceBindFlags bindFlags, LoadCallback callback ) { @@ -65,7 +65,7 @@ std::future> AsyncTextureLoader::loadFromFile( const std::filesystem::path& path, bool generateMipLevels, bool loadAsSrgb, - Resource::BindFlags bindFlags, + ResourceBindFlags bindFlags, LoadCallback callback ) { @@ -82,7 +82,7 @@ void AsyncTextureLoader::runWorkers(size_t threadCount) threadCount, [&]() { - mpDevice->flushAndSync(); + mpDevice->wait(); mFlushPending = false; mUploadCounter = 0; } diff --git a/Source/Falcor/Utils/Image/AsyncTextureLoader.h b/Source/Falcor/Utils/Image/AsyncTextureLoader.h index 468074a0d..27c24984b 100644 --- a/Source/Falcor/Utils/Image/AsyncTextureLoader.h +++ b/Source/Falcor/Utils/Image/AsyncTextureLoader.h @@ -75,7 +75,7 @@ class FALCOR_API AsyncTextureLoader std::future> loadMippedFromFiles( fstd::span paths, bool loadAsSRGB, - Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource, LoadCallback callback = {} ); @@ -92,7 +92,7 @@ class FALCOR_API AsyncTextureLoader const std::filesystem::path& path, bool generateMipLevels, bool loadAsSRGB, - Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource, LoadCallback callback = {} ); @@ -106,7 +106,7 @@ class FALCOR_API AsyncTextureLoader std::vector paths; bool generateMipLevels; bool loadAsSRGB; - Resource::BindFlags bindFlags; + ResourceBindFlags bindFlags; LoadCallback callback; std::promise> promise; }; diff --git a/Source/Falcor/Utils/Image/Bitmap.cpp b/Source/Falcor/Utils/Image/Bitmap.cpp index b74d89638..3fbaba20a 100644 --- a/Source/Falcor/Utils/Image/Bitmap.cpp +++ b/Source/Falcor/Utils/Image/Bitmap.cpp @@ -197,20 +197,19 @@ Bitmap::UniqueConstPtr Bitmap::create(uint32_t width, uint32_t height, ResourceF Bitmap::UniqueConstPtr Bitmap::createFromFile(const std::filesystem::path& path, bool isTopDown) { - std::filesystem::path fullPath; - if (!findFileInDataDirectories(path, fullPath)) + if (!std::filesystem::exists(path)) { - logWarning("Error when loading image file. Can't find image file '{}'.", path); + logWarning("Error when loading image file. File '{}' does not exist.", path); return nullptr; } FREE_IMAGE_FORMAT fifFormat = FIF_UNKNOWN; - fifFormat = FreeImage_GetFileType(fullPath.string().c_str(), 0); + fifFormat = FreeImage_GetFileType(path.string().c_str(), 0); if (fifFormat == FIF_UNKNOWN) { // Can't get the format from the file. Use file extension - fifFormat = FreeImage_GetFIFFromFilename(fullPath.string().c_str()); + fifFormat = FreeImage_GetFIFFromFilename(path.string().c_str()); if (fifFormat == FIF_UNKNOWN) { @@ -227,7 +226,7 @@ Bitmap::UniqueConstPtr Bitmap::createFromFile(const std::filesystem::path& path, } // Read file using memory mapped access which is much faster than regular file IO. - MemoryMappedFile file(fullPath, MemoryMappedFile::kWholeFile, MemoryMappedFile::AccessHint::SequentialScan); + MemoryMappedFile file(path, MemoryMappedFile::kWholeFile, MemoryMappedFile::AccessHint::SequentialScan); if (!file.isOpen()) { genWarning("Can't open image file {}", path); @@ -405,7 +404,7 @@ Bitmap::FileFormat Bitmap::getFormatFromFileExtension(const std::string& ext) if (kExtensions[i] == ext) return Bitmap::FileFormat(i); } - throw ArgumentError("Can't find a matching format for file extension '{}'.", ext); + FALCOR_THROW("Can't find a matching format for file extension '{}'.", ext); } FileDialogFilterVec Bitmap::getFileDialogFilters(ResourceFormat format) @@ -477,14 +476,10 @@ void Bitmap::saveImage( void* pData ) { - if (pData == nullptr) - throw ArgumentError("Provided data must not be nullptr."); - + FALCOR_CHECK(pData, "Provided data must not be nullptr."); + FALCOR_CHECK(fileFormat != FileFormat::DdsFile, "Cannot save DDS files. Use ImageIO instead."); if (is_set(exportFlags, ExportFlags::Uncompressed) && is_set(exportFlags, ExportFlags::Lossy)) - throw ArgumentError("Incompatible flags: lossy cannot be combined with uncompressed."); - - if (fileFormat == FileFormat::DdsFile) - throw ArgumentError("Cannot save DDS files. Use ImageIO instead."); + FALCOR_THROW("Incompatible flags: lossy cannot be combined with uncompressed."); int flags = 0; FIBITMAP* pImage = nullptr; @@ -520,21 +515,19 @@ void Bitmap::saveImage( } else if (bytesPerPixel != 16 && bytesPerPixel != 12) { - throw ArgumentError("Only support for 32-bit/channel RGB/RGBA or 16-bit RGBA images as PFM/EXR files."); + FALCOR_THROW("Only support for 32-bit/channel RGB/RGBA or 16-bit RGBA images as PFM/EXR files."); } const bool exportAlpha = is_set(exportFlags, ExportFlags::ExportAlpha); if (fileFormat == Bitmap::FileFormat::PfmFile) { - if (is_set(exportFlags, ExportFlags::Lossy)) - throw ArgumentError("PFM does not support lossy compression mode."); - if (exportAlpha) - throw ArgumentError("PFM does not support alpha channel."); + FALCOR_CHECK(!is_set(exportFlags, ExportFlags::Lossy), "PFM does not support lossy compression mode."); + FALCOR_CHECK(!exportAlpha, "PFM does not support alpha channel."); } if (exportAlpha && bytesPerPixel != 16) - throw ArgumentError("Requesting to export alpha-channel to EXR file, but the resource doesn't have an alpha-channel"); + FALCOR_THROW("Requesting to export alpha-channel to EXR file, but the resource doesn't have an alpha-channel"); // Upload the image manually and flip it vertically bool scanlineCopy = exportAlpha ? bytesPerPixel == 16 : bytesPerPixel == 12; @@ -577,7 +570,14 @@ void Bitmap::saveImage( else { FIBITMAP* pTemp = FreeImage_ConvertFromRawBits( - (BYTE*)pData, width, height, bytesPerPixel * width, bytesPerPixel * 8, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, + (BYTE*)pData, + width, + height, + bytesPerPixel * width, + bytesPerPixel * 8, + FI_RGBA_RED_MASK, + FI_RGBA_GREEN_MASK, + FI_RGBA_BLUE_MASK, isTopDown ); if (is_set(exportFlags, ExportFlags::ExportAlpha) == false || fileFormat == Bitmap::FileFormat::JpegFile) @@ -643,7 +643,7 @@ void Bitmap::saveImage( } if (!FreeImage_Save(toFreeImageFormat(fileFormat), pImage, path.string().c_str(), flags)) - throw RuntimeError("FreeImage failed to save image"); + FALCOR_THROW("FreeImage failed to save image"); FreeImage_Unload(pImage); } diff --git a/Source/Falcor/Utils/Image/Bitmap.h b/Source/Falcor/Utils/Image/Bitmap.h index 2b8df69a8..2c6a9c845 100644 --- a/Source/Falcor/Utils/Image/Bitmap.h +++ b/Source/Falcor/Utils/Image/Bitmap.h @@ -77,8 +77,7 @@ class FALCOR_API Bitmap /** * Create a new object from file. - * @param[in] path Path to load from. If the file can't be found relative to the current directory, Falcor will search for it in the - * common directories. + * @param[in] path Path to load from (absolute or relative to working directory). * @param[in] isTopDown Control the memory layout of the image. If true, the top-left pixel is the first pixel in the buffer, otherwise * the bottom-left pixel is first. * @return If loading was successful, a new object. Otherwise, nullptr. diff --git a/Source/Falcor/Utils/Image/ImageIO.cpp b/Source/Falcor/Utils/Image/ImageIO.cpp index 48a799a4d..b63af9984 100644 --- a/Source/Falcor/Utils/Image/ImageIO.cpp +++ b/Source/Falcor/Utils/Image/ImageIO.cpp @@ -26,7 +26,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "ImageIO.h" -#include "Core/Errors.h" +#include "Core/Error.h" +#include "Core/API/Device.h" #include "Core/API/CopyContext.h" #include "Core/API/NativeFormats.h" #include "Core/Platform/MemoryMappedFile.h" @@ -97,7 +98,7 @@ ImageIO::CompressionMode convertFormatToMode(ResourceFormat format) case ResourceFormat::BC7UnormSrgb: return ImageIO::CompressionMode::BC7; default: - throw RuntimeError("No corresponding compression mode for the provided ResourceFormat."); + FALCOR_THROW("No corresponding compression mode for the provided ResourceFormat."); } } @@ -123,7 +124,7 @@ nvtt::Format convertModeToNvttFormat(ImageIO::CompressionMode mode) case ImageIO::CompressionMode::BC7: return nvtt::Format::Format_BC7; default: - throw RuntimeError("Invalid compression mode."); + FALCOR_THROW("Invalid compression mode."); } } @@ -153,7 +154,7 @@ nvtt::Format convertFormatToNvttFormat(ResourceFormat format) case ResourceFormat::BC7UnormSrgb: return nvtt::Format::Format_BC7; default: - throw RuntimeError("No corresponding NVTT compression format for the specified ResourceFormat."); + FALCOR_THROW("No corresponding NVTT compression format for the specified ResourceFormat."); } } @@ -201,7 +202,7 @@ nvtt::InputFormat convertToNvttInputFormat(ResourceFormat format) return nvtt::InputFormat::InputFormat_RGBA_32F; } - throw RuntimeError("Image is in an unsupported ResourceFormat."); + FALCOR_THROW("Image is in an unsupported ResourceFormat."); } // Check if any of base image dimensions need to be clamped to a multiple of 4. @@ -313,7 +314,7 @@ void setImage( nvtt::Format compressionFormat = convertFormatToNvttFormat(image.format); if (!surface.setImage3D(compressionFormat, (int)image.width, (int)image.height, (int)image.depth, modified.data())) { - throw RuntimeError("Failed to set image data."); + FALCOR_THROW("Failed to set image data."); } } else @@ -321,7 +322,7 @@ void setImage( nvtt::InputFormat inputFormat = convertToNvttInputFormat(image.format); if (!surface.setImage(inputFormat, (int)image.width, (int)image.height, (int)image.depth, modified.data())) { - throw RuntimeError("Failed to set image data."); + FALCOR_THROW("Failed to set image data."); } } @@ -367,11 +368,17 @@ void exportDDS(const std::filesystem::path& path, ExportData& image, ImageIO::Co nvtt::Context context; if (!context.outputHeader( - image.type, image.width, image.height, image.depth, image.mipLevels, image.images[0].isNormalMap(), compressionOptions, + image.type, + image.width, + image.height, + image.depth, + image.mipLevels, + image.images[0].isNormalMap(), + compressionOptions, outputOptions )) { - throw RuntimeError("Failed to output file header."); + FALCOR_THROW("Failed to output file header."); } for (uint32_t f = 0; f < image.faceCount; ++f) @@ -380,7 +387,7 @@ void exportDDS(const std::filesystem::path& path, ExportData& image, ImageIO::Co nvtt::Surface tmp = image.images[faceIndex]; if (!context.compress(tmp, f, 0, compressionOptions, outputOptions)) { - throw RuntimeError("Failed to compress file."); + FALCOR_THROW("Failed to compress file."); } for (uint32_t m = 1; m < image.mipLevels; ++m) { @@ -395,7 +402,7 @@ void exportDDS(const std::filesystem::path& path, ExportData& image, ImageIO::Co if (!context.compress(tmp, f, m, compressionOptions, outputOptions)) { - throw RuntimeError("Failed to compress file."); + FALCOR_THROW("Failed to compress file."); } } } @@ -408,14 +415,14 @@ void readDDSHeader(ImportData& data, const void* pHeaderData, size_t& headerSize auto magic = *static_cast(pHeaderData); if (magic != DDS_MAGIC) { - throw RuntimeError("Unexpected magic number for a DDS file."); + FALCOR_THROW("Unexpected magic number for a DDS file."); } // Check size fields for both the DDS_HEADER and DDS_PIXELFORMAT structs auto pHeader = reinterpret_cast(static_cast(pHeaderData) + sizeof(uint32_t)); if (pHeader->size != sizeof(DDS_HEADER) || pHeader->ddspf.size != sizeof(DDS_PIXELFORMAT)) { - throw RuntimeError("DDS header size mismatch."); + FALCOR_THROW("DDS header size mismatch."); } // Check for the presence of the extended DX10 header and fill in ImportData fields with their corresponding values @@ -428,14 +435,14 @@ void readDDSHeader(ImportData& data, const void* pHeaderData, size_t& headerSize data.hasDX10Header = true; if (headerSize != sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10)) { - throw RuntimeError("DX10 header extension size mismatch."); + FALCOR_THROW("DX10 header extension size mismatch."); } auto pDX10Header = reinterpret_cast(static_cast(pHeaderData) + sizeof(uint32_t) + sizeof(DDS_HEADER)); data.arraySize = pDX10Header->arraySize; if (data.arraySize == 0) { - throw RuntimeError("Array size cannot be zero."); + FALCOR_THROW("Array size cannot be zero."); } data.format = getResourceFormat(pDX10Header->dxgiFormat); switch (pDX10Header->resourceDimension) @@ -467,7 +474,7 @@ void readDDSHeader(ImportData& data, const void* pHeaderData, size_t& headerSize data.type = Resource::Type::Texture3D; break; default: - throw RuntimeError("Unsupported texture dimension."); + FALCOR_THROW("Unsupported texture dimension."); } } else @@ -492,7 +499,7 @@ void readDDSHeader(ImportData& data, const void* pHeaderData, size_t& headerSize { if (!(pHeader->caps2 & DDS_CUBEMAP_ALLFACES)) { - throw RuntimeError("All six faces must be defined for a legacy D3D9 DDS texture cube."); + FALCOR_THROW("All six faces must be defined for a legacy D3D9 DDS texture cube."); } data.arraySize *= 6; data.type = Resource::Type::TextureCube; @@ -518,12 +525,12 @@ void loadDDS(const std::filesystem::path& path, bool loadAsSrgb, ImportData& dat MemoryMappedFile file(path, MemoryMappedFile::kWholeFile, MemoryMappedFile::AccessHint::SequentialScan); if (!file.isOpen()) { - throw RuntimeError("Failed to open file."); + FALCOR_THROW("Failed to open file."); } if (file.getSize() < (sizeof(uint32_t) + sizeof(DDS_HEADER))) { - throw RuntimeError("Failed to read DDS header (file too small)."); + FALCOR_THROW("Failed to read DDS header (file too small)."); } // Read the DDS header @@ -537,7 +544,7 @@ void loadDDS(const std::filesystem::path& path, bool loadAsSrgb, ImportData& dat if (file.getSize() <= headerSize) { - throw RuntimeError("No image data after DDS header."); + FALCOR_THROW("No image data after DDS header."); } // Read image data. @@ -588,17 +595,16 @@ ref ImageIO::loadTextureFromDDS(ref pDevice, const std::filesys switch (data.type) { case Resource::Type::Texture1D: - pTex = Texture::create1D(pDevice, data.width, data.format, data.arraySize, data.mipLevels, data.imageData.data()); + pTex = pDevice->createTexture1D(data.width, data.format, data.arraySize, data.mipLevels, data.imageData.data()); break; case Resource::Type::Texture2D: - pTex = Texture::create2D(pDevice, data.width, data.height, data.format, data.arraySize, data.mipLevels, data.imageData.data()); + pTex = pDevice->createTexture2D(data.width, data.height, data.format, data.arraySize, data.mipLevels, data.imageData.data()); break; case Resource::Type::TextureCube: - pTex = - Texture::createCube(pDevice, data.width, data.height, data.format, data.arraySize / 6, data.mipLevels, data.imageData.data()); + pTex = pDevice->createTextureCube(data.width, data.height, data.format, data.arraySize / 6, data.mipLevels, data.imageData.data()); break; case Resource::Type::Texture3D: - pTex = Texture::create3D(pDevice, data.width, data.height, data.depth, data.format, data.mipLevels, data.imageData.data()); + pTex = pDevice->createTexture3D(data.width, data.height, data.depth, data.format, data.mipLevels, data.imageData.data()); break; default: logWarning("Failed to load DDS image from '{}': Unrecognized texture type.", path); @@ -633,7 +639,7 @@ void ImageIO::saveToDDS(const std::filesystem::path& path, const Bitmap& bitmap, if (getFormatChannelCount(image.format) == 2 && mode != CompressionMode::BC5) { - throw RuntimeError("Only BC5 compression is supported for two channel images."); + FALCOR_THROW("Only BC5 compression is supported for two channel images."); } // The DX spec requires the dimensions of BC encoded textures to be a multiple of 4 at the base resolution. @@ -685,7 +691,7 @@ void ImageIO::saveToDDS(const std::filesystem::path& path, const Bitmap& bitmap, } catch (const RuntimeError& e) { - throw RuntimeError("Failed to save DDS image to '{}': {}", path, e.what()); + FALCOR_THROW("Failed to save DDS image to '{}': {}", path, e.what()); } } @@ -713,7 +719,7 @@ void ImageIO::saveToDDS( if (getFormatChannelCount(image.format) == 2 && mode != CompressionMode::BC5) { - throw RuntimeError("Only BC5 compression is supported for two channel images."); + FALCOR_THROW("Only BC5 compression is supported for two channel images."); } // The DX spec requires the dimensions of BC encoded textures to be a multiple of 4 at the base resolution. @@ -742,7 +748,7 @@ void ImageIO::saveToDDS( image.faceCount = 6; break; default: - throw RuntimeError("Invalid texture type. Only 2D, 3D, and Cube are currently supported."); + FALCOR_THROW("Invalid texture type. Only 2D, 3D, and Cube are currently supported."); } for (uint32_t f = 0; f < image.faceCount; ++f) @@ -797,7 +803,7 @@ void ImageIO::saveToDDS( } catch (const RuntimeError& e) { - throw RuntimeError("Failed to save DDS image to '{}': {}", path, e.what()); + FALCOR_THROW("Failed to save DDS image to '{}': {}", path, e.what()); } } } // namespace Falcor diff --git a/Source/Falcor/Utils/Image/ImageProcessing.cpp b/Source/Falcor/Utils/Image/ImageProcessing.cpp index 2f13a4774..069ca91cd 100644 --- a/Source/Falcor/Utils/Image/ImageProcessing.cpp +++ b/Source/Falcor/Utils/Image/ImageProcessing.cpp @@ -48,9 +48,9 @@ void ImageProcessing::copyColorChannel( // Validate arguments. FALCOR_ASSERT(pSrc && pDst); if (pSrc->getResource()->getType() != Resource::Type::Texture2D) - throw RuntimeError("Source resource type must be Texture2D"); + FALCOR_THROW("Source resource type must be Texture2D"); if (pDst->getResource()->getType() != Resource::Type::Texture2D) - throw RuntimeError("Source resource type must be Texture2D"); + FALCOR_THROW("Source resource type must be Texture2D"); auto pSrcTex = pSrc->getResource()->asTexture(); auto pDstTex = pDst->getResource()->asTexture(); @@ -62,9 +62,9 @@ void ImageProcessing::copyColorChannel( bool dstIsInt = isIntegerFormat(pDstTex->getFormat()); if (any(srcDim != dstDim)) - throw RuntimeError("Source and destination views must have matching dimensions"); + FALCOR_THROW("Source and destination views must have matching dimensions"); if (srcIsInt != dstIsInt) - throw RuntimeError("Source and destination texture must have matching format type"); + FALCOR_THROW("Source and destination texture must have matching format type"); uint channelIndex = 0; switch (srcMask) @@ -82,7 +82,7 @@ void ImageProcessing::copyColorChannel( channelIndex = 3; break; default: - throw RuntimeError("'channelMask' parameter must be a single color channel."); + FALCOR_THROW("'channelMask' parameter must be a single color channel."); } // Prepare and execute program to copy color channel. diff --git a/Source/Falcor/Utils/Image/TextureAnalyzer.cpp b/Source/Falcor/Utils/Image/TextureAnalyzer.cpp index 1505a100b..27045c5e4 100644 --- a/Source/Falcor/Utils/Image/TextureAnalyzer.cpp +++ b/Source/Falcor/Utils/Image/TextureAnalyzer.cpp @@ -131,15 +131,15 @@ void TextureAnalyzer::checkFormatSupport(const ref pInput, uint32_t mip // Validate that input is supported. if (pInput->getDepth() > 1) { - throw RuntimeError("3D textures are not supported"); + FALCOR_THROW("3D textures are not supported"); } if (mipLevel >= pInput->getMipCount() || arraySlice >= pInput->getArraySize()) { - throw RuntimeError("Mip level and/or array slice is out of range"); + FALCOR_THROW("Mip level and/or array slice is out of range"); } if (pInput->getSampleCount() != 1) { - throw RuntimeError("Multi-sampled textures are not supported"); + FALCOR_THROW("Multi-sampled textures are not supported"); } auto format = pInput->getFormat(); @@ -152,10 +152,10 @@ void TextureAnalyzer::checkFormatSupport(const ref pInput, uint32_t mip break; case FormatType::Sint: case FormatType::Uint: - throw RuntimeError("Format {} is not supported", to_string(format)); + FALCOR_THROW("Format {} is not supported", to_string(format)); default: FALCOR_ASSERT(false); - throw RuntimeError("Unknown format type"); + FALCOR_THROW("Unknown format type"); } } } // namespace Falcor diff --git a/Source/Falcor/Utils/Image/TextureManager.cpp b/Source/Falcor/Utils/Image/TextureManager.cpp index 75c3f4b66..a01e37ad6 100644 --- a/Source/Falcor/Utils/Image/TextureManager.cpp +++ b/Source/Falcor/Utils/Image/TextureManager.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "TextureManager.h" +#include "Core/AssetResolver.h" #include "Core/API/Device.h" #include "Utils/Logger.h" #include "Utils/NumericRange.h" @@ -41,7 +42,7 @@ namespace Falcor namespace { const size_t kMaxTextureHandleCount = std::numeric_limits::max(); -static_assert(TextureManager::TextureHandle::kInvalidID >= kMaxTextureHandleCount); +static_assert(TextureManager::CpuTextureHandle::kInvalidID >= kMaxTextureHandleCount); } // namespace TextureManager::TextureManager(ref pDevice, size_t maxTextureCount, size_t threadCount) @@ -50,16 +51,16 @@ TextureManager::TextureManager(ref pDevice, size_t maxTextureCount, size TextureManager::~TextureManager() {} -TextureManager::TextureHandle TextureManager::addTexture(const ref& pTexture) +TextureManager::CpuTextureHandle TextureManager::addTexture(const ref& pTexture) { FALCOR_ASSERT(pTexture); if (pTexture->getType() != Resource::Type::Texture2D || pTexture->getSampleCount() != 1) { - throw ArgumentError("Only single-sample 2D textures can be added"); + FALCOR_THROW("Only single-sample 2D textures can be added"); } std::unique_lock lock(mMutex); - TextureHandle handle; + CpuTextureHandle handle; if (auto it = mTextureToHandle.find(pTexture.get()); it != mTextureToHandle.end()) { @@ -102,13 +103,13 @@ TextureManager::TextureHandle TextureManager::addTexture(const ref& pTe return handle; } -TextureManager::TextureHandle TextureManager::loadUdimTexture( +TextureManager::CpuTextureHandle TextureManager::loadUdimTexture( const std::filesystem::path& path, bool generateMipLevels, bool loadAsSRGB, - Resource::BindFlags bindFlags, + ResourceBindFlags bindFlags, bool async, - const SearchDirectories* searchDirectories, + const AssetResolver* assetResolver, size_t* loadedTextureCount ) { @@ -128,16 +129,16 @@ TextureManager::TextureHandle TextureManager::loadUdimTexture( std::regex udimRegex(filename); std::vector texturePaths; // Find the first directory containing the pattern, in case the UDIM set lives in multiple available directories - if (searchDirectories) - texturePaths = globFilesInDirectories(dirpath, udimRegex, *searchDirectories, true /* firstHitOnly */); + if (assetResolver) + texturePaths = assetResolver->resolvePathPattern(dirpath, filename, true /* firstMatchOnly */); else - texturePaths = globFilesInDataDirectories(dirpath, udimRegex, true /* firstHitOnly */); + texturePaths = globFilesInDirectory(dirpath, udimRegex, true /* firstMatchOnly */); // nothing found, return an invalid handle if (texturePaths.empty()) { logWarning("Can't find UDIM texture files '{}'.", path); - return TextureHandle(); + return CpuTextureHandle(); } // Now load all the files from that directory @@ -148,7 +149,7 @@ TextureManager::TextureHandle TextureManager::loadUdimTexture( *loadedTextureCount = texturePaths.size(); std::vector udimIndices; - std::vector handles; + std::vector handles; size_t maxIndex = 0; for (auto& it : texturePaths) { @@ -163,7 +164,7 @@ TextureManager::TextureHandle TextureManager::loadUdimTexture( udimIndices.push_back(udim); handles.push_back(loadTexture(it, generateMipLevels, loadAsSRGB, bindFlags, async)); - FALCOR_CHECK_ARG_GE_MSG(udim, 1001, "Texture {} is not a valid UDIM texture, as it violates the valid UDIM range of 1001-9999", it); + FALCOR_CHECK(udim >= 1001, "Texture {} is not a valid UDIM texture, as it violates the valid UDIM range of 1001-9999", it); } // UDIM range needs to cover all numbers from 1001 to maxIndex inclusive, so 1001, 1002, 1003 needs 3 indices @@ -175,21 +176,21 @@ TextureManager::TextureHandle TextureManager::loadUdimTexture( mUdimIndirection[rangeStart + index] = handles[i].getID(); } - return TextureManager::TextureHandle(rangeStart, true); + return TextureManager::CpuTextureHandle(rangeStart, true); } -TextureManager::TextureHandle TextureManager::loadTexture( +TextureManager::CpuTextureHandle TextureManager::loadTexture( const std::filesystem::path& path, bool generateMipLevels, bool loadAsSRGB, - Resource::BindFlags bindFlags, + ResourceBindFlags bindFlags, bool async, - const SearchDirectories* searchDirectories, + const AssetResolver* assetResolver, size_t* loadedTextureCount ) { if (path.string().find("") != std::string::npos) - return loadUdimTexture(path, generateMipLevels, loadAsSRGB, bindFlags, async, searchDirectories, loadedTextureCount); + return loadUdimTexture(path, generateMipLevels, loadAsSRGB, bindFlags, async, assetResolver, loadedTextureCount); std::vector paths; auto addPath = [&](const std::filesystem::path& p) @@ -197,10 +198,16 @@ TextureManager::TextureHandle TextureManager::loadTexture( // Find the full path to the texture. std::filesystem::path fullPath; bool found = false; - if (searchDirectories) - found = findFileInDirectories(p, fullPath, *searchDirectories); + if (assetResolver) + { + fullPath = assetResolver->resolvePath(p); + found = !fullPath.empty(); + } else - found = findFileInDataDirectories(p, fullPath); + { + fullPath = p; + found = std::filesystem::exists(fullPath); + } if (found) paths.emplace_back(std::move(fullPath)); @@ -229,7 +236,7 @@ TextureManager::TextureHandle TextureManager::loadTexture( if (loadedTextureCount) *loadedTextureCount = paths.empty() ? 0 : 1; - TextureHandle handle; + CpuTextureHandle handle; if (paths.empty()) { logWarning("Can't find texture file '{}'.", path); @@ -333,7 +340,7 @@ TextureManager::TextureHandle TextureManager::loadTexture( return handle; } -void TextureManager::waitForTextureLoading(const TextureHandle& handle) +void TextureManager::waitForTextureLoading(const CpuTextureHandle& handle) { if (!handle) return; @@ -342,7 +349,7 @@ void TextureManager::waitForTextureLoading(const TextureHandle& handle) std::unique_lock lock(mMutex); mCondition.wait(lock, [&]() { return getDesc(handle).state == TextureState::Loaded; }); - mpDevice->flushAndSync(); + mpDevice->wait(); } void TextureManager::waitForAllTexturesLoading() @@ -351,7 +358,7 @@ void TextureManager::waitForAllTexturesLoading() std::unique_lock lock(mMutex); mCondition.wait(lock, [&]() { return mLoadRequestsInProgress == 0; }); - mpDevice->flushAndSync(); + mpDevice->wait(); } void TextureManager::beginDeferredLoading() @@ -364,7 +371,7 @@ void TextureManager::endDeferredLoading() struct Job { TextureKey key; - TextureHandle handle; + CpuTextureHandle handle; }; // Get a list of textures to load. @@ -385,7 +392,9 @@ void TextureManager::endDeferredLoading() std::atomic texturesLoaded; NumericRange jobRange(0, jobs.size()); std::for_each( - std::execution::par_unseq, jobRange.begin(), jobRange.end(), + std::execution::par_unseq, + jobRange.begin(), + jobRange.end(), [&](size_t i) { const auto& job = jobs[i]; @@ -406,11 +415,11 @@ void TextureManager::endDeferredLoading() { logDebug("Flush"); std::lock_guard lock(mpDevice->getGlobalGfxMutex()); - mpDevice->flushAndSync(); + mpDevice->wait(); } } ); - mpDevice->flushAndSync(); + mpDevice->wait(); // Mark loaded textures and add them to lookup table. for (const auto& job : jobs) @@ -421,7 +430,7 @@ void TextureManager::endDeferredLoading() } } -void TextureManager::removeTexture(const TextureHandle& handle) +void TextureManager::removeTexture(const CpuTextureHandle& handle) { if (handle.isUdim()) { @@ -458,16 +467,14 @@ void TextureManager::removeTexture(const TextureHandle& handle) mFreeList.push_back(handle); } -TextureManager::TextureDesc TextureManager::getTextureDesc(const TextureHandle& handle) const +TextureManager::TextureDesc TextureManager::getTextureDesc(const CpuTextureHandle& handle) const { - if (handle.isUdim()) - return getTextureDesc(resolveUdimTexture(handle)); - if (!handle) return {}; std::lock_guard lock(mMutex); - FALCOR_ASSERT(handle && handle.getID() < mTextureDescs.size()); + FALCOR_CHECK(!handle.isUdim(), "Can't lookup texture desc from handle to UDIM texture. Resolve UDIM first."); + FALCOR_CHECK(handle && handle.getID() < mTextureDescs.size(), "Invalid texture handle."); return mTextureDescs[handle.getID()]; } @@ -477,15 +484,13 @@ size_t TextureManager::getTextureDescCount() const return mTextureDescs.size(); } -void TextureManager::setShaderData(const ShaderVar& texturesVar, const size_t descCount, const ShaderVar& udimsVar) const +void TextureManager::bindShaderData(const ShaderVar& texturesVar, const size_t descCount, const ShaderVar& udimsVar) const { std::lock_guard lock(mMutex); if (mTextureDescs.size() > descCount) { - throw RuntimeError( - "Descriptor array size ({}) is too small for the required number of textures ({})", descCount, mTextureDescs.size() - ); + FALCOR_THROW("Descriptor array size ({}) is too small for the required number of textures ({})", descCount, mTextureDescs.size()); } ref nullTexture; @@ -507,9 +512,13 @@ void TextureManager::setShaderData(const ShaderVar& texturesVar, const size_t de if (!mpUdimIndirection || mUdimIndirection.size() > mpUdimIndirection->getElementCount()) { - mpUdimIndirection = Buffer::createStructured( - mpDevice, sizeof(int32_t), mUdimIndirection.size(), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, - Buffer::CpuAccess::None, mUdimIndirection.data(), false + mpUdimIndirection = mpDevice->createStructuredBuffer( + sizeof(int32_t), + mUdimIndirection.size(), + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType::DeviceLocal, + mUdimIndirection.data(), + false ); mUdimIndirectionDirty = false; } @@ -542,9 +551,9 @@ TextureManager::Stats TextureManager::getStats() const return s; } -TextureManager::TextureHandle TextureManager::addDesc(const TextureDesc& desc) +TextureManager::CpuTextureHandle TextureManager::addDesc(const TextureDesc& desc) { - TextureHandle handle; + CpuTextureHandle handle; // Allocate new texture handle and insert desc. if (!mFreeList.empty()) @@ -557,21 +566,19 @@ TextureManager::TextureHandle TextureManager::addDesc(const TextureDesc& desc) { if (mTextureDescs.size() >= mMaxTextureCount) { - throw RuntimeError("Out of texture handles"); + FALCOR_THROW("Out of texture handles"); } - handle = TextureHandle{static_cast(mTextureDescs.size())}; + handle = CpuTextureHandle{static_cast(mTextureDescs.size())}; mTextureDescs.emplace_back(desc); } return handle; } -TextureManager::TextureDesc& TextureManager::getDesc(const TextureHandle& handle) +TextureManager::TextureDesc& TextureManager::getDesc(const CpuTextureHandle& handle) { - if (handle.isUdim()) - return getDesc(resolveUdimTexture(handle)); - - FALCOR_ASSERT(handle && handle.getID() < mTextureDescs.size()); + FALCOR_CHECK(!handle.isUdim(), "Can't lookup texture desc from handle to UDIM texture. Resolve UDIM first."); + FALCOR_CHECK(handle && handle.getID() < mTextureDescs.size(), "Invalid texture handle."); return mTextureDescs[handle.getID()]; } @@ -618,7 +625,7 @@ void TextureManager::freeUdimRange(size_t rangeStart) mFreeUdimRanges.push_back(rangeStart); } -void TextureManager::removeUdimTexture(const TextureHandle& handle) +void TextureManager::removeUdimTexture(const CpuTextureHandle& handle) { size_t rangeStart = handle.getID(); size_t rangeSize = mUdimIndirectionSize[rangeStart]; @@ -626,7 +633,7 @@ void TextureManager::removeUdimTexture(const TextureHandle& handle) { if (mUdimIndirection[i] < 0) continue; - TextureHandle texHandle(mUdimIndirection[i]); + CpuTextureHandle texHandle(mUdimIndirection[i]); removeTexture(texHandle); mUdimIndirection[i] = -1; } @@ -634,21 +641,32 @@ void TextureManager::removeUdimTexture(const TextureHandle& handle) freeUdimRange(rangeStart); } -TextureManager::TextureHandle TextureManager::resolveUdimTexture(const TextureHandle& handle) const +TextureManager::CpuTextureHandle TextureManager::resolveUdimTexture(const CpuTextureHandle& handle, const float2& uv) const { - return resolveUdimTexture(handle, float2(0.f)); + if (!handle.isUdim()) + return handle; + + // Compute which UDIM ID texture coordinate maps to. + FALCOR_CHECK(uv[0] >= 0.f && uv[0] < 10.f && uv[1] >= 0.f && uv[1] < 10.f, "UDIM texture coordinate ({}) is out of range.", uv); + uint32_t udimID = 1001 + uint32_t(uv[0]) + 10 * uint32_t(uv[1]); + + return resolveUdimTexture(handle, udimID); } -TextureManager::TextureHandle TextureManager::resolveUdimTexture(const TextureHandle& handle, const float2& uv) const +TextureManager::CpuTextureHandle TextureManager::resolveUdimTexture(const CpuTextureHandle& handle, const uint32_t udimID) const { if (!handle.isUdim()) return handle; + + // Check if UDIM ID is valid and within range of the indirection table. + // udimID = 1001 + u + (10 * v) where u,v in 0..9 => valid IDs are 1001..1100. + FALCOR_CHECK(udimID >= 1001 && udimID <= 1100, "Illegal UDIM ID ({}).", udimID); size_t rangeStart = handle.getID(); - size_t udim = uint(uv[0]) + 10 * uint(uv[1]); - FALCOR_ASSERT_LT(udim, mUdimIndirectionSize[rangeStart]); + size_t udim = udimID - 1001; + FALCOR_CHECK(udim < mUdimIndirectionSize[rangeStart], "UDIM ID ({}) is out of range.", udimID); if (mUdimIndirection[rangeStart + udim] >= 0) - return TextureHandle(mUdimIndirection[rangeStart + udim]); - return TextureHandle(); + return CpuTextureHandle(mUdimIndirection[rangeStart + udim]); + return CpuTextureHandle(); } } // namespace Falcor diff --git a/Source/Falcor/Utils/Image/TextureManager.h b/Source/Falcor/Utils/Image/TextureManager.h index 49735c079..f0831866b 100644 --- a/Source/Falcor/Utils/Image/TextureManager.h +++ b/Source/Falcor/Utils/Image/TextureManager.h @@ -32,6 +32,7 @@ #include "Core/API/Resource.h" #include "Core/API/Texture.h" #include "Core/Program/ShaderVar.h" +#include "Scene/Material/TextureHandle.slang" #include #include #include @@ -41,7 +42,7 @@ namespace Falcor { -class SearchDirectories; +class AssetResolver; /** * Multi-threaded texture manager. @@ -74,18 +75,27 @@ class FALCOR_API TextureManager }; /** - * Handle to a managed texture. + * Handle to a managed texture on the CPU side. */ - class TextureHandle + class CpuTextureHandle { public: static constexpr uint32_t kInvalidID = std::numeric_limits::max(); public: - TextureHandle() = default; - explicit TextureHandle(uint32_t id) : mID(id) {} + CpuTextureHandle() = default; + explicit CpuTextureHandle(uint32_t id) : mID(id) {} - explicit TextureHandle(uint32_t id, bool isUdim) : mID(id), mIsUdim(isUdim) {} + explicit CpuTextureHandle(uint32_t id, bool isUdim) : mID(id), mIsUdim(isUdim) {} + + explicit CpuTextureHandle(const TextureHandle& gpuHandle) + { + if (gpuHandle.getMode() == TextureHandle::Mode::Texture) + { + mID = gpuHandle.getTextureID(); + mIsUdim = gpuHandle.getUdimEnabled(); + } + } bool isValid() const { return mID != kInvalidID; } explicit operator bool() const { return isValid(); } @@ -93,7 +103,24 @@ class FALCOR_API TextureManager uint32_t getID() const { return mID; } bool isUdim() const { return mIsUdim; } - bool operator==(const TextureHandle& other) const { return mID == other.mID && mIsUdim == other.mIsUdim; } + bool operator==(const CpuTextureHandle& other) const { return mID == other.mID && mIsUdim == other.mIsUdim; } + + TextureHandle toGpuHandle() const + { + TextureHandle gpuHandle; + if (isValid()) + { + gpuHandle.setTextureID(this->getID()); + gpuHandle.setMode(TextureHandle::Mode::Texture); + gpuHandle.setUdimEnabled(this->isUdim()); + } + else + { + gpuHandle.setMode(TextureHandle::Mode::Uniform); + gpuHandle.setUdimEnabled(false); + } + return gpuHandle; + } private: uint32_t mID{kInvalidID}; @@ -125,7 +152,7 @@ class FALCOR_API TextureManager * @param[in] pTexture The texture resource. * @return Unique handle to the texture. */ - TextureHandle addTexture(const ref& pTexture); + CpuTextureHandle addTexture(const ref& pTexture); /** * Requst loading a texture from file. @@ -137,17 +164,17 @@ class FALCOR_API TextureManager * @param[in] loadAsSRGB Load the texture as sRGB format if supported, otherwise linear color. * @param[in] bindFlags The bind flags for the texture resource. * @param[in] async Load asynchronously, otherwise the function blocks until the texture data is loaded. - * @param[in] searchDirectories Optionally can pass in search directories, will be used instead of the global data directories. + * @param[in] assetResolver Optional asset resolver for resolving file paths. * @param[out] loadedTextureCount Optionally can provided the number of actually loaded textures (2+ can happen with UDIMs) * @return Unique handle to the texture, or an invalid handle if the texture can't be found. */ - TextureHandle loadTexture( + CpuTextureHandle loadTexture( const std::filesystem::path& path, bool generateMipLevels, bool loadAsSRGB, - Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource, bool async = true, - const SearchDirectories* searchDirectories = nullptr, + const AssetResolver* assetResolver = nullptr, size_t* loadedTextureCount = nullptr ); @@ -155,13 +182,13 @@ class FALCOR_API TextureManager * Same as loadTexture, but explicitly handles Udim textures. If the texture isn't Udim, it falls back to loadTexture. * Also, loadTexture will detect UDIM and call loadUdimTexture if needed. */ - TextureHandle loadUdimTexture( + CpuTextureHandle loadUdimTexture( const std::filesystem::path& path, bool generateMipLevels, bool loadAsSRGB, - Resource::BindFlags bindFlags = Resource::BindFlags::ShaderResource, + ResourceBindFlags bindFlags = ResourceBindFlags::ShaderResource, bool async = true, - const SearchDirectories* searchDirectories = nullptr, + const AssetResolver* assetResolver = nullptr, size_t* loadedTextureCount = nullptr ); @@ -170,7 +197,7 @@ class FALCOR_API TextureManager * If the handle is valid, the call blocks until the texture is loaded (or failed to load). * @param[in] handle Texture handle. */ - void waitForTextureLoading(const TextureHandle& handle); + void waitForTextureLoading(const CpuTextureHandle& handle); /** * Waits for all currently requested textures to be loaded. @@ -191,21 +218,33 @@ class FALCOR_API TextureManager * Remove a texture. * @param[in] handle Texture handle. */ - void removeTexture(const TextureHandle& handle); + void removeTexture(const CpuTextureHandle& handle); /** * Get a loaded texture. Call getTextureDesc() for more info. + * This function handles non-UDIM textures. If UDIMs are expected, supply UDIM ID or uv coordinate. * @param[in] handle Texture handle. * @return Texture if loaded, or nullptr if handle doesn't exist or texture isn't yet loaded. */ - ref getTexture(const TextureHandle& handle) const { return getTextureDesc(handle).pTexture; } + ref getTexture(const CpuTextureHandle& handle) const { return getTextureDesc(handle).pTexture; } + ref getTexture(const CpuTextureHandle& handle, const float2& uv) const { return getTextureDesc(handle, uv).pTexture; } + ref getTexture(const CpuTextureHandle& handle, const uint32_t udimID) const { return getTextureDesc(handle, udimID).pTexture; } /** * Get a texture desc. + * This function handles non-UDIM textures. If UDIMs are expected, supply UDIM ID or uv coordinate. * @param[in] handle Texture handle. * @return Texture desc, or invalid desc if handle is invalid. */ - TextureDesc getTextureDesc(const TextureHandle& handle) const; + TextureDesc getTextureDesc(const CpuTextureHandle& handle) const; + TextureDesc getTextureDesc(const CpuTextureHandle& handle, const float2& uv) const + { + return getTextureDesc(resolveUdimTexture(handle, uv)); + } + TextureDesc getTextureDesc(const CpuTextureHandle& handle, const uint32_t udimID) const + { + return getTextureDesc(resolveUdimTexture(handle, udimID)); + } /** * Get texture desc count. @@ -230,7 +269,7 @@ class FALCOR_API TextureManager * @param[in] var Shader var for descriptor array. * @param[in] descCount Size of descriptor array. */ - void setShaderData(const ShaderVar& texturesVar, const size_t descCount, const ShaderVar& udimsVar) const; + void bindShaderData(const ShaderVar& texturesVar, const size_t descCount, const ShaderVar& udimsVar) const; /** * Returns stats for the textures @@ -240,9 +279,9 @@ class FALCOR_API TextureManager private: size_t getUdimRange(size_t requiredSize); void freeUdimRange(size_t rangeStart); - void removeUdimTexture(const TextureHandle& handle); - TextureHandle resolveUdimTexture(const TextureHandle& handle) const; - TextureHandle resolveUdimTexture(const TextureHandle& handle, const float2& uv) const; + void removeUdimTexture(const CpuTextureHandle& handle); + CpuTextureHandle resolveUdimTexture(const CpuTextureHandle& handle, const float2& uv) const; + CpuTextureHandle resolveUdimTexture(const CpuTextureHandle& handle, const uint32_t udimID) const; /** * Key to uniquely identify a managed texture. @@ -252,9 +291,9 @@ class FALCOR_API TextureManager std::vector fullPaths; bool generateMipLevels; bool loadAsSRGB; - Resource::BindFlags bindFlags; + ResourceBindFlags bindFlags; - TextureKey(const std::vector& paths, bool mips, bool srgb, Resource::BindFlags flags) + TextureKey(const std::vector& paths, bool mips, bool srgb, ResourceBindFlags flags) : fullPaths(paths), generateMipLevels(mips), loadAsSRGB(srgb), bindFlags(flags) {} @@ -271,8 +310,8 @@ class FALCOR_API TextureManager } }; - TextureHandle addDesc(const TextureDesc& desc); - TextureDesc& getDesc(const TextureHandle& handle); + CpuTextureHandle addDesc(const TextureDesc& desc); + TextureDesc& getDesc(const CpuTextureHandle& handle); ref mpDevice; @@ -280,10 +319,10 @@ class FALCOR_API TextureManager std::condition_variable mCondition; ///< Condition variable to wait on for loading to finish. // Internal state. Do not access outside of critical section. - std::vector mTextureDescs; ///< Array of all texture descs, indexed by handle ID. - std::vector mFreeList; ///< List of unused handles. - std::map mKeyToHandle; ///< Map from texture key to handle. - std::map mTextureToHandle; ///< Map from texture ptr to handle. + std::vector mTextureDescs; ///< Array of all texture descs, indexed by handle ID. + std::vector mFreeList; ///< List of unused handles. + std::map mKeyToHandle; ///< Map from texture key to handle. + std::map mTextureToHandle; ///< Map from texture ptr to handle. /// Map from UDIM-1001 to an actual textureID, -1 if the texture does not exist (e.g., there is 1001 and 1003, so 1002 [1] == -1) std::vector mUdimIndirection; /// For each udim indirection range, writes (at the first element), how long that range is (there is 0 everywhere else) diff --git a/Source/Falcor/Utils/Logger.cpp b/Source/Falcor/Utils/Logger.cpp index 755f5b378..2af808d4a 100644 --- a/Source/Falcor/Utils/Logger.cpp +++ b/Source/Falcor/Utils/Logger.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Logger.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/Platform/OS.h" #include "Utils/Scripting/ScriptBindings.h" #include @@ -43,7 +43,6 @@ Logger::Level sVerbosity = Logger::Level::Info; Logger::OutputFlags sOutputs = Logger::OutputFlags::Console | Logger::OutputFlags::File | Logger::OutputFlags::DebugWindow; std::filesystem::path sLogFilePath; -#if FALCOR_ENABLE_LOGGER bool sInitialized = false; FILE* sLogFile = nullptr; @@ -89,19 +88,16 @@ void printToLogFile(const std::string& s) std::fflush(sLogFile); } } -#endif } // namespace void Logger::shutdown() { -#if FALCOR_ENABLE_LOGGER if (sLogFile) { fclose(sLogFile); sLogFile = nullptr; sInitialized = false; } -#endif } inline const char* getLogLevelString(Logger::Level level) @@ -153,7 +149,6 @@ class MessageDeduplicator void Logger::log(Level level, const std::string_view msg, Frequency frequency) { std::lock_guard lock(sMutex); -#if FALCOR_ENABLE_LOGGER if (level <= sVerbosity) { std::string s = fmt::format("{} {}\n", getLogLevelString(level), msg); @@ -181,7 +176,6 @@ void Logger::log(Level level, const std::string_view msg, Frequency frequency) printToDebugWindow(s); } } -#endif } void Logger::setVerbosity(Level level) @@ -211,7 +205,6 @@ Logger::OutputFlags Logger::getOutputs() void Logger::setLogFilePath(const std::filesystem::path& path) { std::lock_guard lock(sMutex); -#if FALCOR_ENABLE_LOGGER if (sLogFile) { fclose(sLogFile); @@ -219,9 +212,6 @@ void Logger::setLogFilePath(const std::filesystem::path& path) sInitialized = false; } sLogFilePath = path; -#else - return false; -#endif } std::filesystem::path Logger::getLogFilePath() @@ -251,20 +241,25 @@ FALCOR_SCRIPT_BINDING(Logger) outputFlags.value("DebugWindow", Logger::OutputFlags::DebugWindow); logger.def_property_static( - "verbosity", [](pybind11::object) { return Logger::getVerbosity(); }, + "verbosity", + [](pybind11::object) { return Logger::getVerbosity(); }, [](pybind11::object, Logger::Level verbosity) { Logger::setVerbosity(verbosity); } ); logger.def_property_static( - "outputs", [](pybind11::object) { return Logger::getOutputs(); }, + "outputs", + [](pybind11::object) { return Logger::getOutputs(); }, [](pybind11::object, Logger::OutputFlags outputs) { Logger::setOutputs(outputs); } ); logger.def_property_static( - "log_file_path", [](pybind11::object) { return Logger::getLogFilePath(); }, + "log_file_path", + [](pybind11::object) { return Logger::getLogFilePath(); }, [](pybind11::object, std::filesystem::path path) { Logger::setLogFilePath(path); } ); logger.def_static( - "log", [](Logger::Level level, const std::string_view msg) { Logger::log(level, msg, Logger::Frequency::Always); }, "level"_a, + "log", + [](Logger::Level level, const std::string_view msg) { Logger::log(level, msg, Logger::Frequency::Always); }, + "level"_a, "msg"_a ); } diff --git a/Source/Falcor/Utils/Logger.h b/Source/Falcor/Utils/Logger.h index d6814f46a..70e34b683 100644 --- a/Source/Falcor/Utils/Logger.h +++ b/Source/Falcor/Utils/Logger.h @@ -27,7 +27,6 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" -#include "Core/FalcorConfig.h" #include "Utils/StringFormatters.h" #include #include @@ -37,7 +36,6 @@ namespace Falcor { /** * Container class for logging messages. - * To enable log messages, make sure FALCOR_ENABLE_LOGGER is set to `1` in FalcorConfig.h. * Messages are only printed to the selected outputs if they match the verbosity level. */ class FALCOR_API Logger @@ -111,11 +109,6 @@ class FALCOR_API Logger */ static std::filesystem::path getLogFilePath(); - /** - * Check if the logger is enabled. - */ - static constexpr bool enabled() { return FALCOR_ENABLE_LOGGER != 0; } - /** * Log a message. * @param[in] level Log level. @@ -209,4 +202,11 @@ inline void logFatal(fmt::format_string format, Args&&... args) { Logger::log(Logger::Level::Fatal, fmt::format(format, std::forward(args)...)); } + } // namespace Falcor + +#define FALCOR_PRINT(x) \ + do \ + { \ + ::Falcor::logInfo("{} = {}", #x, x); \ + } while (0) diff --git a/Source/Falcor/Utils/Math/CubicSpline.h b/Source/Falcor/Utils/Math/CubicSpline.h index eb3ad1da4..7b0f507ba 100644 --- a/Source/Falcor/Utils/Math/CubicSpline.h +++ b/Source/Falcor/Utils/Math/CubicSpline.h @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once +#include "Utils/fast_vector.h" #include #include @@ -188,6 +189,6 @@ class CubicSpline { T a, b, c, d; }; - std::vector mCoefficient; + fast_vector mCoefficient; }; } // namespace Falcor diff --git a/Source/Falcor/Utils/Math/DiffMathHelpers.slang b/Source/Falcor/Utils/Math/DiffMathHelpers.slang new file mode 100644 index 000000000..9471ac80a --- /dev/null +++ b/Source/Falcor/Utils/Math/DiffMathHelpers.slang @@ -0,0 +1,46 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ + +/** + * Infinitesimal functions to extract gradient. + */ +[Differentiable] +[PreferRecompute] +[ForceInline] +float infinitesimal(float x) +{ + return x - detach(x); +} + +[Differentiable] +[PreferRecompute] +[ForceInline] +__generic vector infinitesimal(vector x) +{ + return x - detach(x); +} diff --git a/Source/Falcor/Utils/Math/FNVHash.h b/Source/Falcor/Utils/Math/FNVHash.h index a9afd5484..01ddd468b 100644 --- a/Source/Falcor/Utils/Math/FNVHash.h +++ b/Source/Falcor/Utils/Math/FNVHash.h @@ -28,7 +28,7 @@ #pragma once #include "Core/Macros.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include @@ -100,6 +100,18 @@ class FNVHash T get() const { return mHash; } + constexpr bool operator==(const FNVHash& rhs) { return get() == rhs.get(); } + + constexpr bool operator!=(const FNVHash& rhs) { return get() != rhs.get(); } + + constexpr bool operator<=(const FNVHash& rhs) { return get() <= rhs.get(); } + + constexpr bool operator>=(const FNVHash& rhs) { return get() >= rhs.get(); } + + constexpr bool operator<(const FNVHash& rhs) { return get() < rhs.get(); } + + constexpr bool operator>(const FNVHash& rhs) { return get() > rhs.get(); } + private: T mHash = kOffsetBasis; }; diff --git a/Source/Falcor/Utils/Math/FalcorMath.h b/Source/Falcor/Utils/Math/FalcorMath.h index c7f763c0d..87c2b0ee3 100644 --- a/Source/Falcor/Utils/Math/FalcorMath.h +++ b/Source/Falcor/Utils/Math/FalcorMath.h @@ -29,7 +29,7 @@ #include "Vector.h" #include "Matrix.h" #include "Quaternion.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include #include diff --git a/Source/Falcor/Utils/Math/MathHelpers.h b/Source/Falcor/Utils/Math/MathHelpers.h index 3d6625ad0..61d3b5369 100644 --- a/Source/Falcor/Utils/Math/MathHelpers.h +++ b/Source/Falcor/Utils/Math/MathHelpers.h @@ -29,7 +29,7 @@ #include "Vector.h" #include "Matrix.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Utils/Logger.h" namespace Falcor @@ -54,6 +54,21 @@ inline float3 perp_stark(const float3& u) return v; } +/** + * @brief Generates full OrthoNormalBasis based on the normal, without branches or sqrt. + * + * From https://graphics.pixar.com/library/OrthonormalB/paper.pdf + */ +inline void branchlessONB(const float3 n, float3& b1, float3& b2) +{ + // can't use just `sign` because we need 0 to go into -1/+1, but not 0 + float sign = (n.z >= 0 ? 1 : -1); + const float a = -1.0f / (sign + n.z); + const float b = n.x * n.y * a; + b1 = float3(1.0f + sign * n.x * n.x * a, sign * b, -sign * n.x); + b2 = float3(b, sign + n.y * n.y * a, -n.y); +} + /** * Builds a local frame from a unit normal vector. * @param[in] n Unit normal vector. @@ -119,7 +134,7 @@ inline float4x4 validateTransformMatrix(const float4x4& transform) if (!isMatrixValid(newMatrix)) { - throw RuntimeError("Transform matrix has inf/nan values!"); + FALCOR_THROW("Transform matrix has inf/nan values!"); } if (!isMatrixAffine(newMatrix)) diff --git a/Source/Falcor/Utils/Math/MathHelpers.slang b/Source/Falcor/Utils/Math/MathHelpers.slang index 474463be0..5040931d0 100644 --- a/Source/Falcor/Utils/Math/MathHelpers.slang +++ b/Source/Falcor/Utils/Math/MathHelpers.slang @@ -393,6 +393,23 @@ float3 sample_triangle(float2 u) return float3(1.f - b.x - b.y, b.x, b.y); } +/** + * Sample from the von-Mises Fisher distribution. + * @param[in] u Uniform random numbers in [0,1)^2. + * @param[in] kappa VMF concentration parameter. + * @return Sampled direction in the local frame (+z axis up). + */ +[PreferRecompute] +float3 sampleVonMisesFisher(float2 u, float kappa) +{ + float sy = max(1.f - u.y, 1e-6f); + float phi = M_2PI * u.x; + float cosTheta = 1.f + log(exp(-2.f * kappa) * (1.f - sy) + sy) / kappa; + float sinTheta = sqrt(1.f - cosTheta * cosTheta); + float3 d = float3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta); + return d; +} + /****************************************************************************** Motion vectors @@ -471,6 +488,49 @@ float3x3 inverse(float3x3 M) return inv; } +/** + * Inverts a 4x4 matrix. + */ +float4x4 inverse(float4x4 m) +{ + float n11 = m[0][0], n12 = m[1][0], n13 = m[2][0], n14 = m[3][0]; + float n21 = m[0][1], n22 = m[1][1], n23 = m[2][1], n24 = m[3][1]; + float n31 = m[0][2], n32 = m[1][2], n33 = m[2][2], n34 = m[3][2]; + float n41 = m[0][3], n42 = m[1][3], n43 = m[2][3], n44 = m[3][3]; + + float t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44; + float t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44; + float t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44; + float t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; + + float det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; + float idet = 1.0f / det; + + float4x4 ret; + + ret[0][0] = t11 * idet; + ret[0][1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * idet; + ret[0][2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * idet; + ret[0][3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * idet; + + ret[1][0] = t12 * idet; + ret[1][1] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * idet; + ret[1][2] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * idet; + ret[1][3] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * idet; + + ret[2][0] = t13 * idet; + ret[2][1] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * idet; + ret[2][2] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * idet; + ret[2][3] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * idet; + + ret[3][0] = t14 * idet; + ret[3][1] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * idet; + ret[3][2] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * idet; + ret[3][3] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * idet; + + return ret; +} + /** * Generate a vector that is orthogonal to the input vector. * This can be used to invent a tangent frame for meshes that don't have real tangents/bitangents. @@ -491,11 +551,26 @@ float3 perp_stark(float3 u) return v; } +/** + * @brief Generates full OrthoNormalBasis based on the normal, without branches or sqrt. + * + * From https://graphics.pixar.com/library/OrthonormalB/paper.pdf + */ +void branchlessONB(const float3 n, out float3 b1, out float3 b2) +{ + // can't use just `sign` because we need 0 to go into -1/+1, but not 0 + float sign = (n.z >= 0 ? 1 : -1); + const float a = -1.0f / (sign + n.z); + const float b = n.x * n.y * a; + b1 = float3(1.0f + sign * n.x * n.x * a, sign * b, -sign * n.x); + b2 = float3(b, sign + n.y * n.y * a, -n.y); +} + // clang-format off -float sqr(float v) { return v * v; } -float2 sqr(float2 v) { return v * v; } -float3 sqr(float3 v) { return v * v; } -float4 sqr(float4 v) { return v * v; } +[Differentiable] float sqr(float v) { return v * v; } +[Differentiable] float2 sqr(float2 v) { return v * v; } +[Differentiable] float3 sqr(float3 v) { return v * v; } +[Differentiable] float4 sqr(float4 v) { return v * v; } // clang-format on /** @@ -579,8 +654,13 @@ float lgamma(float x_) // Maxiumum error: 2 x 10^-10. const float g = 5.0; const float coeffs[7] = { - 1.000000000190015, 76.18009172947146, -86.50532032941677, 24.01409824083091, - -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5, + 1.000000000190015, + 76.18009172947146, + -86.50532032941677, + 24.01409824083091, + -1.231739572450155, + 0.1208650973866179e-2, + -0.5395239384953e-5, }; bool reflect = x_ < 0.5f; @@ -673,3 +753,16 @@ float3 rotate_z(const float3 v, const float angle) sincos(angle, s, c); return float3(v.x * c - v.y * s, v.x * s + v.y * c, v.z); } + +/** + * Normalize a 3D vector, but return 0 if the vector is zero. + * @param[in] v Original vector. + * @return Normalized vector, or 0 if the vector is zero. + */ +[Differentiable] +[PreferRecompute] +float3 normalizeSafe(float3 v) +{ + float len = length(v); + return len > 0.f ? v / len : float3(0.f); +} diff --git a/Source/Falcor/Utils/Math/MatrixMath.h b/Source/Falcor/Utils/Math/MatrixMath.h index 4c4444a5d..fcae389ba 100644 --- a/Source/Falcor/Utils/Math/MatrixMath.h +++ b/Source/Falcor/Utils/Math/MatrixMath.h @@ -38,7 +38,7 @@ #include "Vector.h" #include "Quaternion.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include @@ -230,8 +230,10 @@ template { T oneOverDet = T(1) / determinant(m); return matrix{ - +m[1][1] * oneOverDet, -m[0][1] * oneOverDet, // row 0 - -m[1][0] * oneOverDet, +m[0][0] * oneOverDet // row 1 + +m[1][1] * oneOverDet, // 0,0 + -m[0][1] * oneOverDet, // 0,1 + -m[1][0] * oneOverDet, // 1,0 + +m[0][0] * oneOverDet, // 1,1 }; } diff --git a/Source/Falcor/Utils/Math/MatrixTypes.h b/Source/Falcor/Utils/Math/MatrixTypes.h index 5d689231f..59bad6eb1 100644 --- a/Source/Falcor/Utils/Math/MatrixTypes.h +++ b/Source/Falcor/Utils/Math/MatrixTypes.h @@ -29,7 +29,7 @@ #include "ScalarTypes.h" #include "VectorTypes.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include diff --git a/Source/Falcor/Utils/Math/MatrixUtils.slang b/Source/Falcor/Utils/Math/MatrixUtils.slang index ab87cacb7..85cb01772 100644 --- a/Source/Falcor/Utils/Math/MatrixUtils.slang +++ b/Source/Falcor/Utils/Math/MatrixUtils.slang @@ -92,3 +92,16 @@ float3 transformVector(xform3x4 aFromB, float3 vectorB) { return mul(aFromB.xform, float4(vectorB, 0.f)).xyz; } + +__generic extension matrix +{ + // Create an identity matrix of size N and type T + static matrix identity() + { + static matrix m = matrix((T)0); + [ForceUnroll] + for (int i = 0; i < N; i++) + m[i][i] = (T)1; + return m; + } +} diff --git a/Source/Falcor/Utils/Math/QuaternionMath.h b/Source/Falcor/Utils/Math/QuaternionMath.h index 8296f5537..cbca6f34d 100644 --- a/Source/Falcor/Utils/Math/QuaternionMath.h +++ b/Source/Falcor/Utils/Math/QuaternionMath.h @@ -38,7 +38,7 @@ #include "ScalarMath.h" #include "VectorMath.h" #include "MathConstants.slangh" -#include "Core/Assert.h" +#include "Core/Error.h" namespace Falcor { diff --git a/Source/Falcor/Utils/Math/ShadingFrame.slang b/Source/Falcor/Utils/Math/ShadingFrame.slang index d025a6522..42958f596 100644 --- a/Source/Falcor/Utils/Math/ShadingFrame.slang +++ b/Source/Falcor/Utils/Math/ShadingFrame.slang @@ -31,7 +31,7 @@ import Utils.Math.MathHelpers; * Shading frame in world space. * The vectors TBN form an orthonormal basis. */ -struct ShadingFrame +struct ShadingFrame : IDifferentiable { float3 T; ///< Shading tangent. Normalized. float3 B; ///< Shading bitangent. Normalized. @@ -43,6 +43,7 @@ struct ShadingFrame * @param[in] Normal in world space. Not normalized. * @param[in] Target tangent in world space (xyz) and handedness sign (w). Not normalized. */ + [Differentiable] __init(const float3 normalW, const float4 tangentW) { this.N = normalize(normalW); @@ -53,6 +54,7 @@ struct ShadingFrame * Create identity shading frame. * @return New shading frame. */ + [Differentiable] static ShadingFrame createIdentity() { ShadingFrame sf; @@ -70,6 +72,7 @@ struct ShadingFrame * @param[out] valid True if a valid tangent space was computed based on the supplied tangent, or false if a tangent space was invented. * @return New shading frame. */ + [Differentiable] static ShadingFrame createSafe(const float3 normalW, const float4 tangentW, out bool valid) { ShadingFrame sf; @@ -83,6 +86,7 @@ struct ShadingFrame * @param[in] v Vector in local space. * @return Vector in world space. */ + [Differentiable] float3 fromLocal(const float3 v) { return T * v.x + B * v.y + N * v.z; } /** @@ -90,6 +94,7 @@ struct ShadingFrame * @param[in] v Vector in world space. * @return Vector in local space. */ + [Differentiable] float3 toLocal(const float3 v) { return float3(dot(v, T), dot(v, B), dot(v, N)); } /** @@ -105,6 +110,7 @@ struct ShadingFrame * @param[in] tangentW Target tangent in world space (xyz) and handedness sign (w). Not normalized. */ [mutating] + [Differentiable] void orthonormalize(const float4 tangentW) { T = normalize(tangentW.xyz - N * dot(tangentW.xyz, N)); @@ -119,6 +125,7 @@ struct ShadingFrame * @return True if a valid tangent space was computed based on the supplied tangent, or false if a tangent space was invented. */ [mutating] + [Differentiable] bool orthonormalizeSafe(const float4 tangentW) { // Check that tangent space exists and can be safely orthonormalized. @@ -141,7 +148,7 @@ struct ShadingFrame } else { - T = perp_stark(N); + T = no_diff perp_stark(N); B = cross(N, T); } diff --git a/Source/Falcor/Utils/NumericRange.h b/Source/Falcor/Utils/NumericRange.h index 404ab9feb..10b306342 100644 --- a/Source/Falcor/Utils/NumericRange.h +++ b/Source/Falcor/Utils/NumericRange.h @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Errors.h" +#include "Core/Error.h" #include #include @@ -66,11 +66,7 @@ class NumericRange::value>::type> T mValue; }; - explicit NumericRange(const T& begin, const T& end) : mBegin(begin), mEnd(end) - { - if (begin > end) - throw ArgumentError("Invalid range"); - } + explicit NumericRange(const T& begin, const T& end) : mBegin(begin), mEnd(end) { FALCOR_CHECK(begin <= end, "Invalid range"); } NumericRange() = delete; NumericRange(const NumericRange&) = delete; NumericRange(NumericRange&& other) = delete; diff --git a/Source/Falcor/Utils/ObjectID.h b/Source/Falcor/Utils/ObjectID.h index 59a2d9de5..130523499 100644 --- a/Source/Falcor/Utils/ObjectID.h +++ b/Source/Falcor/Utils/ObjectID.h @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Assert.h" +#include "Core/Error.h" #include #include diff --git a/Source/Falcor/Utils/PathResolving.cpp b/Source/Falcor/Utils/PathResolving.cpp index 08092ec69..41e2a6cab 100644 --- a/Source/Falcor/Utils/PathResolving.cpp +++ b/Source/Falcor/Utils/PathResolving.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "PathResolving.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/StringUtils.h" #include "Core/Platform/OS.h" diff --git a/Source/Falcor/Utils/Properties.cpp b/Source/Falcor/Utils/Properties.cpp index afd4ebd1e..e3c808f7e 100644 --- a/Source/Falcor/Utils/Properties.cpp +++ b/Source/Falcor/Utils/Properties.cpp @@ -77,9 +77,9 @@ VecT vecFromJson(const json& json, std::string_view name) { auto constexpr length = VecT::length(); if (!json.is_array()) - throw RuntimeError("Property '{}' is not an array.", name); + FALCOR_THROW("Property '{}' is not an array.", name); if (json.size() != length) - throw RuntimeError("Property '{}' has an invalid number of elements.", name); + FALCOR_THROW("Property '{}' has an invalid number of elements.", name); VecT result; for (size_t i = 0; i < length; ++i) json[i].get_to(result[i]); @@ -92,43 +92,43 @@ T valueFromJson(const json& json, std::string_view name) if constexpr (std::is_same_v) { if (!json.is_boolean()) - throw RuntimeError("Property '{}' is not a boolean.", name); + FALCOR_THROW("Property '{}' is not a boolean.", name); return static_cast(json); } else if constexpr (std::is_integral_v && std::is_signed_v) { if (!json.is_number_integer()) - throw RuntimeError("Property '{}' is not an integer.", name); + FALCOR_THROW("Property '{}' is not an integer.", name); return static_cast(json); } else if constexpr (std::is_integral_v && !std::is_signed_v) { - // if (!json.is_number_intenger() !json.is_number_unsigned()) throw RuntimeError("Property '{}' is not an unsigned integer.", name); + // if (!json.is_number_intenger() !json.is_number_unsigned()) FALCOR_THROW("Property '{}' is not an unsigned integer.", name); return static_cast(json); } else if constexpr (std::is_floating_point_v) { // Allow integers to be converted to floating point if (!json.is_number_float() && !json.is_number_integer()) - throw RuntimeError("Property '{}' is not a floating point value or integer.", name); + FALCOR_THROW("Property '{}' is not a floating point value or integer.", name); return static_cast(json); } else if constexpr (std::is_same_v) { if (!json.is_string()) - throw RuntimeError("Property '{}' is not a string.", name); + FALCOR_THROW("Property '{}' is not a string.", name); return static_cast(json); } else if constexpr (std::is_same_v) { if (!json.is_string()) - throw RuntimeError("Property '{}' is not a string/path.", name); + FALCOR_THROW("Property '{}' is not a string/path.", name); return static_cast(json); } else if constexpr (std::is_same_v) { if (!json.is_object()) - throw RuntimeError("Property '{}' is not an object.", name); + FALCOR_THROW("Property '{}' is not an object.", name); return Properties(json); } else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) diff --git a/Source/Falcor/Utils/Properties.h b/Source/Falcor/Utils/Properties.h index 8d9ab784c..8bfe63f11 100644 --- a/Source/Falcor/Utils/Properties.h +++ b/Source/Falcor/Utils/Properties.h @@ -27,7 +27,7 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/Enum.h" #include "Utils/Math/VectorTypes.h" @@ -149,21 +149,21 @@ class FALCOR_API Properties { std::string value; if (!getInternal(name, value)) - throw RuntimeError("Property '{}' does not exist.", name); + FALCOR_THROW("Property '{}' does not exist.", name); return stringToEnum(value); } else if constexpr (detail::has_serialize_v) { Properties props; if (!getInternal(name, props)) - throw RuntimeError("Property '{}' does not exist.", name); + FALCOR_THROW("Property '{}' does not exist.", name); return deserializeFromProperties(props); } else { T value; if (!getInternal(name, value)) - throw RuntimeError("Property '{}' does not exist.", name); + FALCOR_THROW("Property '{}' does not exist.", name); return value; } } diff --git a/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.cpp b/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.cpp index 1830a8361..77c21b972 100644 --- a/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.cpp +++ b/Source/Falcor/Utils/SampleGenerators/StratifiedSamplePattern.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "StratifiedSamplePattern.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/Logger.h" #include diff --git a/Source/Falcor/Utils/Sampling/AliasTable.cpp b/Source/Falcor/Utils/Sampling/AliasTable.cpp index 693a8095e..ff1773c09 100644 --- a/Source/Falcor/Utils/Sampling/AliasTable.cpp +++ b/Source/Falcor/Utils/Sampling/AliasTable.cpp @@ -26,7 +26,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "AliasTable.h" -#include "Core/Errors.h" +#include "Core/Error.h" +#include "Core/API/Device.h" namespace Falcor { @@ -48,13 +49,12 @@ AliasTable::AliasTable(ref pDevice, std::vector weights, std::mt1 { // Use >= since we reserve 0xFFFFFFFFu as an invalid flag marker during construction. if (weights.size() >= std::numeric_limits::max()) - throw RuntimeError("Too many entries for alias table."); + FALCOR_THROW("Too many entries for alias table."); std::uniform_int_distribution rngDist; - mpWeights = Buffer::createStructured( - pDevice, sizeof(float), mCount, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, weights.data() - ); + mpWeights = + pDevice->createStructuredBuffer(sizeof(float), mCount, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, weights.data()); // Our working set / intermediate buffers (underweight & overweight); initialize to "invalid" std::vector lowIdx(mCount, 0xFFFFFFFFu); @@ -133,12 +133,12 @@ AliasTable::AliasTable(ref pDevice, std::vector weights, std::mt1 // correct location in the alias table. // Stash the alias table in our GPU buffer - mpItems = Buffer::createStructured( - pDevice, sizeof(AliasTable::Item), mCount, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, items.data() + mpItems = pDevice->createStructuredBuffer( + sizeof(AliasTable::Item), mCount, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, items.data() ); } -void AliasTable::setShaderData(const ShaderVar& var) const +void AliasTable::bindShaderData(const ShaderVar& var) const { var["items"] = mpItems; var["weights"] = mpWeights; diff --git a/Source/Falcor/Utils/Sampling/AliasTable.h b/Source/Falcor/Utils/Sampling/AliasTable.h index f559e2cbe..3afd78e35 100644 --- a/Source/Falcor/Utils/Sampling/AliasTable.h +++ b/Source/Falcor/Utils/Sampling/AliasTable.h @@ -53,7 +53,7 @@ class FALCOR_API AliasTable * Bind the alias table data to a given shader var. * @param[in] var The shader variable to set the data into. */ - void setShaderData(const ShaderVar& var) const; + void bindShaderData(const ShaderVar& var) const; /** * Get the number of weights in the table. diff --git a/Source/Falcor/Utils/Sampling/SampleGenerator.cpp b/Source/Falcor/Utils/Sampling/SampleGenerator.cpp index 3a11a69e7..a67876615 100644 --- a/Source/Falcor/Utils/Sampling/SampleGenerator.cpp +++ b/Source/Falcor/Utils/Sampling/SampleGenerator.cpp @@ -40,7 +40,7 @@ ref SampleGenerator::create(ref pDevice, uint32_t type) } else { - throw ArgumentError("Can't create SampleGenerator. Unknown type"); + FALCOR_THROW("Can't create SampleGenerator. Unknown type"); } } @@ -65,11 +65,13 @@ void SampleGenerator::registerType(uint32_t type, const std::string& name, std:: void SampleGenerator::registerAll() { registerType( - SAMPLE_GENERATOR_TINY_UNIFORM, "Tiny uniform (32-bit)", + SAMPLE_GENERATOR_TINY_UNIFORM, + "Tiny uniform (32-bit)", [](ref pDevice) { return ref(new SampleGenerator(pDevice, SAMPLE_GENERATOR_TINY_UNIFORM)); } ); registerType( - SAMPLE_GENERATOR_UNIFORM, "Uniform (128-bit)", + SAMPLE_GENERATOR_UNIFORM, + "Uniform (128-bit)", [](ref pDevice) { return ref(new SampleGenerator(pDevice, SAMPLE_GENERATOR_UNIFORM)); } ); } diff --git a/Source/Falcor/Utils/Sampling/SampleGenerator.h b/Source/Falcor/Utils/Sampling/SampleGenerator.h index 96ecffa40..5750bf64e 100644 --- a/Source/Falcor/Utils/Sampling/SampleGenerator.h +++ b/Source/Falcor/Utils/Sampling/SampleGenerator.h @@ -70,7 +70,7 @@ class FALCOR_API SampleGenerator : public Object * Binds the data to a program vars object. * @param[in] pVars ProgramVars of the program to set data into. */ - virtual void setShaderData(const ShaderVar& var) const {} + virtual void bindShaderData(const ShaderVar& var) const {} /** * Render the sampler's UI. @@ -82,7 +82,7 @@ class FALCOR_API SampleGenerator : public Object * This should be called at the beginning of each frame for samplers that do extra setup for each frame. * @param[in] pRenderContext Render context. * @param[in] frameDim Current frame dimension. - * @return Returns true if internal state has changed and setShaderData() should be called before using the sampler. + * @return Returns true if internal state has changed and bindShaderData() should be called before using the sampler. */ virtual bool beginFrame(RenderContext* pRenderContext, const uint2& frameDim) { return false; } diff --git a/Source/Falcor/Utils/Scripting/Console.cpp b/Source/Falcor/Utils/Scripting/Console.cpp index 88fcefc39..18332f28e 100644 --- a/Source/Falcor/Utils/Scripting/Console.cpp +++ b/Source/Falcor/Utils/Scripting/Console.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Console.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/UI/InputTypes.h" #include "Utils/UI/Gui.h" #include "Utils/Scripting/Scripting.h" @@ -107,9 +107,12 @@ void Console::render(Gui* pGui, bool& show) // Stick focus to console text input. ImGui::SetKeyboardFocusHere(); if (ImGui::InputText( - "##console", mCmdBuffer, std::size(mCmdBuffer), + "##console", + mCmdBuffer, + std::size(mCmdBuffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackCharFilter, - &inputTextCallback, this + &inputTextCallback, + this )) { enterCommand(); diff --git a/Source/Falcor/Utils/Scripting/PythonDictionary.h b/Source/Falcor/Utils/Scripting/PythonDictionary.h deleted file mode 100644 index 2fb48d041..000000000 --- a/Source/Falcor/Utils/Scripting/PythonDictionary.h +++ /dev/null @@ -1,164 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#pragma once -#include "Core/Enum.h" -#include -#include -#include -#include -#include - -namespace Falcor -{ -class PythonDictionary -{ -public: - using Container = pybind11::dict; - - PythonDictionary() = default; - PythonDictionary(const Container& c) : mMap(c) {} - - class Value - { - public: - Value(const Container& container, const std::string_view name) : mName(name), mContainer(container){}; - Value(const Container& container = {}) : Value(container, std::string()) {} - - Value& operator=(const Value& rhs) - { - mName = rhs.mName; - mContainer[mName.c_str()] = rhs.mContainer[mName.c_str()]; - return *this; - } - - template - void operator=(const T& t) - { - if constexpr (has_enum_info_v) - mContainer[mName.c_str()] = enumToString(t); - else - mContainer[mName.c_str()] = t; - } - - void operator=(const std::filesystem::path& path) - { - // Convert path to string. Otherwise it's represented as a Python WindowsPath/PosixPath. - *this = path.generic_string(); - } - - template - operator T() const - { - if constexpr (has_enum_info_v) - return stringToEnum(mContainer[mName.c_str()].cast()); - else - return mContainer[mName.c_str()].cast(); - } - - private: - std::string mName; - const Container& mContainer; - }; - - template - class IteratorT - { - public: - IteratorT(ContainerType* pContainer, const pybind11::detail::dict_iterator& it) : mIt(it), mpContainer(pContainer) {} - - bool operator==(const IteratorT& other) const { return other.mIt == mIt; } - bool operator!=(const IteratorT& other) const { return other.mIt != mIt; } - IteratorT& operator++() - { - mIt++; - return *this; - } - IteratorT operator++(int) - { - ++mIt; - return *this; - } - - std::pair operator*() - { - std::string key = mIt->first.cast(); - return {key, Value(*mpContainer, key)}; - } - - private: - pybind11::detail::dict_iterator mIt; - ContainerType* mpContainer; - }; - - using Iterator = IteratorT; - using ConstIterator = IteratorT; - - Value operator[](const std::string_view name) { return Value(mMap, name); } - const Value operator[](const std::string_view name) const { return Value(mMap, name); } - - template - T get(const std::string_view name, const T& def) const - { - if (!mMap.contains(name.data())) - return def; - return mMap[name.data()].cast(); - } - - template - std::optional get(const std::string_view name) const - { - if (!mMap.contains(name.data())) - return std::optional(); - return std::optional(mMap[name.data()].cast()); - } - - // Avoid forcing std::string creation if Value would be just temporary - template - T get(const std::string_view name) const - { - return mMap[name.data()].cast(); - } - - ConstIterator begin() const { return ConstIterator(&mMap, mMap.begin()); } - ConstIterator end() const { return ConstIterator(&mMap, mMap.end()); } - - Iterator begin() { return Iterator(&mMap, mMap.begin()); } - Iterator end() { return Iterator(&mMap, mMap.end()); } - - size_t size() const { return mMap.size(); } - - bool keyExists(const std::string& key) const { return mMap.contains(key.c_str()); } - - pybind11::dict toPython() const { return mMap; } - - std::string toString() const { return pybind11::str(static_cast(mMap)); } - -private: - Container mMap; -}; -} // namespace Falcor diff --git a/Source/Falcor/Utils/Scripting/ScriptBindings.cpp b/Source/Falcor/Utils/Scripting/ScriptBindings.cpp index 0fcd74c29..10658f565 100644 --- a/Source/Falcor/Utils/Scripting/ScriptBindings.cpp +++ b/Source/Falcor/Utils/Scripting/ScriptBindings.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "ScriptBindings.h" +#include "Core/Error.h" #include "Core/Plugin.h" #include "Utils/Scripting/Scripting.h" #include "Utils/Math/Vector.h" @@ -113,7 +114,7 @@ void registerBinding(RegisterBindingFunc f) catch (const std::exception& e) { PyErr_SetString(PyExc_ImportError, e.what()); - reportError(e.what()); + reportErrorAndContinue(e.what()); return; } } @@ -130,7 +131,7 @@ void registerDeferredBinding(const std::string& name, RegisterBindingFunc f) deferredBindings.begin(), deferredBindings.end(), [&name](const DeferredBinding& binding) { return binding.name == name; } ) != deferredBindings.end()) { - throw RuntimeError("A script binding with the name '{}' already exists!", name); + FALCOR_THROW("A script binding with the name '{}' already exists!", name); } deferredBindings.emplace_back(name, f); } @@ -182,6 +183,31 @@ void defineVecType(pybind11::class_& vec) vec.def(pybind11::init(initVector), "x"_a, "y"_a, "z"_a, "w"_a); } + // Initialization from array (to allow implicit conversion from python lists). + auto initArray = [](std::array a) + { + VecT v; + for (size_t i = 0; i < VecT::length(); ++i) + v[i] = a[i]; + return v; + }; + vec.def(pybind11::init(initArray)); + pybind11::implicitly_convertible, VecT>(); + + // Initialization from integer array (to allow implicit conversion from python lists). + if constexpr (std::is_floating_point_v) + { + auto initIntArray = [](std::array a) + { + VecT v; + for (size_t i = 0; i < VecT::length(); ++i) + v[i] = ScalarT(a[i]); + return v; + }; + vec.def(pybind11::init(initIntArray)); + pybind11::implicitly_convertible, VecT>(); + } + // Casting float16_t <-> float vectors. // This allows explicit casts, e.g., float16_t3(c), where c is a float3 in python. if constexpr (std::is_same::value) @@ -229,7 +255,7 @@ void defineVecType(pybind11::class_& vec) [&](pybind11::tuple t) { if (t.size() != length) - throw RuntimeError("Invalid state!"); + FALCOR_THROW("Invalid state!"); VecT v; for (auto i = 0; i < length; ++i) v[i] = t[i].cast(); @@ -315,6 +341,7 @@ void initModule(pybind11::module& m) // float3x3, float4x4 // Note: We register these as simple data types without any operations because semantics may change in the future. pybind11::class_(m, "float3x3"); + pybind11::class_(m, "float3x4"); pybind11::class_(m, "float4x4"); // ObjectID diff --git a/Source/Falcor/Utils/Scripting/ScriptBindings.h b/Source/Falcor/Utils/Scripting/ScriptBindings.h index 6c3339182..a3c3d6c6d 100644 --- a/Source/Falcor/Utils/Scripting/ScriptBindings.h +++ b/Source/Falcor/Utils/Scripting/ScriptBindings.h @@ -26,10 +26,11 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/Enum.h" #include "Core/ObjectPython.h" #include +#include #include #include #include @@ -131,3 +132,29 @@ static std::string repr(const T& value) #endif // _staticlibrary } // namespace Falcor::ScriptBindings + +namespace pybind11 +{ + +template +class falcor_enum : public enum_ +{ +public: + static_assert(::Falcor::has_enum_info_v, "pybind11::falcor_enum<> requires an enumeration type with infos!"); + + using Base = enum_; + + template + explicit falcor_enum(const handle& scope, const char* name, const Extra&... extra) : Base(scope, name, extra...) + { + for (const auto& item : ::Falcor::EnumInfo::items()) + { + const char* value_name = item.second.c_str(); + // Handle reserved Python keywords. + if (item.second == "None") + value_name = "None_"; + Base::value(value_name, item.first); + } + } +}; +} // namespace pybind11 diff --git a/Source/Falcor/Utils/Scripting/ScriptWriter.h b/Source/Falcor/Utils/Scripting/ScriptWriter.h index a8af6f370..b880187d1 100644 --- a/Source/Falcor/Utils/Scripting/ScriptWriter.h +++ b/Source/Falcor/Utils/Scripting/ScriptWriter.h @@ -27,7 +27,6 @@ **************************************************************************/ #pragma once #include "ScriptBindings.h" -#include "PythonDictionary.h" #include "Core/Platform/OS.h" #include #include @@ -84,10 +83,8 @@ class ScriptWriter return var + "." + property + " = " + getArgString(arg) + "\n"; } - static std::string getPathString(std::filesystem::path path, bool stripDataDirs = true) + static std::string getPathString(const std::filesystem::path& path) { - if (stripDataDirs) - path = stripDataDirectories(path); std::string str = path.string(); std::replace(str.begin(), str.end(), '\\', '/'); return str; @@ -100,12 +97,6 @@ std::string ScriptWriter::getArgString(const T& arg) return ScriptBindings::repr(arg); } -template<> -inline std::string ScriptWriter::getArgString(const PythonDictionary& dict) -{ - return dict.toString(); -} - template<> inline std::string ScriptWriter::getArgString(const pybind11::dict& dict) { diff --git a/Source/Falcor/Utils/Scripting/Scripting.cpp b/Source/Falcor/Utils/Scripting/Scripting.cpp index 59b04e8d7..a7e154b61 100644 --- a/Source/Falcor/Utils/Scripting/Scripting.cpp +++ b/Source/Falcor/Utils/Scripting/Scripting.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Scripting.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Utils/StringUtils.h" #include "Utils/StringFormatters.h" #include @@ -66,7 +66,7 @@ void Scripting::start() } catch (const std::exception& e) { - throw RuntimeError("Failed to start the Python interpreter: {}", e.what()); + FALCOR_THROW("Failed to start the Python interpreter: {}", e.what()); } } } @@ -152,7 +152,7 @@ Scripting::RunResult Scripting::runScriptFromFile(const std::filesystem::path& p context.setObject("__file__", nullptr); // There seems to be no API on pybind11::dict to remove a key. return result; } - throw RuntimeError("Failed to run script. Can't find the file '{}'.", path); + FALCOR_THROW("Failed to run script. Can't find the file '{}'.", path); } std::string Scripting::interpretScript(const std::string& script, Context& context) diff --git a/Source/Falcor/Utils/Scripting/ndarray.cpp b/Source/Falcor/Utils/Scripting/ndarray.cpp index 12160fb0f..edaac7fc2 100644 --- a/Source/Falcor/Utils/Scripting/ndarray.cpp +++ b/Source/Falcor/Utils/Scripting/ndarray.cpp @@ -370,7 +370,8 @@ static PyObject* dlpack_from_buffer_protocol(PyObject* o) mt->dltensor.strides = strides.release(); return PyCapsule_New( - mt.release(), "dltensor", + mt.release(), + "dltensor", [](PyObject* o) { error_scope scope; // temporarily save any existing errors @@ -906,12 +907,16 @@ FALCOR_SCRIPT_BINDING(ndarray) printf("ndarray stride [%zu] : %zd\n", i, ndarray.stride(i)); } printf( - "Device ID = %u (cpu=%i, cuda=%i)\n", ndarray.device_id(), int(ndarray.device_type() == pybind11::device::cpu::value), + "Device ID = %u (cpu=%i, cuda=%i)\n", + ndarray.device_id(), + int(ndarray.device_type() == pybind11::device::cpu::value), int(ndarray.device_type() == pybind11::device::cuda::value) ); printf( - "ndarray dtype: int16=%i, uint32=%i, float32=%i\n", ndarray.dtype() == pybind11::dtype(), - ndarray.dtype() == pybind11::dtype(), ndarray.dtype() == pybind11::dtype() + "ndarray dtype: int16=%i, uint32=%i, float32=%i\n", + ndarray.dtype() == pybind11::dtype(), + ndarray.dtype() == pybind11::dtype(), + ndarray.dtype() == pybind11::dtype() ); } ); diff --git a/Source/Falcor/Utils/Settings.cpp b/Source/Falcor/Utils/Settings.cpp index c4ba50243..96a5b9876 100644 --- a/Source/Falcor/Utils/Settings.cpp +++ b/Source/Falcor/Utils/Settings.cpp @@ -130,10 +130,13 @@ void Settings::updateSearchPaths(const nlohmann::json& update) if (searchKind == "standardsearchpath") { - std::vector& current = mStandardSearchDirectories[std::string(category)].get(); + std::vector& current = mStandardSearchDirectories[std::string(category)]; ResolvedPaths result = resolveSearchPaths(current, pathUpdates, std::vector()); - FALCOR_CHECK_ARG_MSG( - result.invalid.empty(), "While processing {}:{}, found invalid paths: {}", searchKind, category, + FALCOR_CHECK( + result.invalid.empty(), + "While processing {}:{}, found invalid paths: {}", + searchKind, + category, joinStrings(result.invalid, ", ") ); current = std::move(result.resolved); @@ -142,16 +145,19 @@ void Settings::updateSearchPaths(const nlohmann::json& update) if (searchKind == "searchpath") { - std::vector& current = mSearchDirectories[std::string(category)].get(); + std::vector& current = mSearchDirectories[std::string(category)]; auto it = mStandardSearchDirectories.find(std::string(category)); ResolvedPaths result; if (it == mStandardSearchDirectories.end()) result = resolveSearchPaths(current, pathUpdates, std::vector()); else - result = resolveSearchPaths(current, pathUpdates, it->second.get()); - FALCOR_CHECK_ARG_MSG( - result.invalid.empty(), "While processing {}:{}, found invalid paths: {}", searchKind, category, + result = resolveSearchPaths(current, pathUpdates, it->second); + FALCOR_CHECK( + result.invalid.empty(), + "While processing {}:{}, found invalid paths: {}", + searchKind, + category, joinStrings(result.invalid, ", ") ); current = std::move(result.resolved); diff --git a/Source/Falcor/Utils/Settings.h b/Source/Falcor/Utils/Settings.h index 56cf8dc5a..feb221ffd 100644 --- a/Source/Falcor/Utils/Settings.h +++ b/Source/Falcor/Utils/Settings.h @@ -27,8 +27,7 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" -#include "Core/Assert.h" -#include "Core/Platform/SearchDirectories.h" +#include "Core/Error.h" #include @@ -163,7 +162,8 @@ class SettingsProperties template std::optional get(const std::string_view name) const { - FALCOR_ASSERT_MSG(!name.empty() && name.data()[name.size()] == 0, "The underlying library requires names to be null terminated"); + // The underlying library requires attribute names to be null terminated + FALCOR_ASSERT(!name.empty() && name.data()[name.size()] == 0); // Handle the : in names as nesting separator { @@ -202,7 +202,8 @@ class SettingsProperties template T get(const std::string_view name, const T& def) const { - FALCOR_ASSERT_MSG(!name.empty() && name.data()[name.size()] == 0, "The underlying library requires names to be null terminated"); + // The underlying library requires attribute names to be null terminated + FALCOR_ASSERT(!name.empty() && name.data()[name.size()] == 0); std::optional opt = get(name); return opt ? *opt : def; @@ -211,7 +212,8 @@ class SettingsProperties // Do we have a property of the given name bool has(const std::string_view name) const { - FALCOR_ASSERT_MSG(!name.empty() && name.data()[name.size()] == 0, "The underlying library requires names to be null terminated"); + // The underlying library requires attribute names to be null terminated + FALCOR_ASSERT(!name.empty() && name.data()[name.size()] == 0); // Handle the : in names as nesting separator { @@ -239,7 +241,8 @@ class SettingsProperties template bool has(const std::string_view name) const { - FALCOR_ASSERT_MSG(!name.empty() && name.data()[name.size()] == 0, "The underlying library requires names to be null terminated"); + // The underlying library requires attribute names to be null terminated + FALCOR_ASSERT(!name.empty() && name.data()[name.size()] == 0); // Handle the : in names as nesting separator { @@ -271,7 +274,8 @@ class SettingsProperties // Do we have a property of the given name bool hasExact(const std::string_view name) const { - FALCOR_ASSERT_MSG(!name.empty() && name.data()[name.size()] == 0, "The underlying library requires names to be null terminated"); + // The underlying library requires attribute names to be null terminated + FALCOR_ASSERT(!name.empty() && name.data()[name.size()] == 0); return mDictionary->contains(name); } @@ -280,7 +284,8 @@ class SettingsProperties template bool hasExact(const std::string_view name) const { - FALCOR_ASSERT_MSG(!name.empty() && name.data()[name.size()] == 0, "The underlying library requires names to be null terminated"); + // The underlying library requires attribute names to be null terminated + FALCOR_ASSERT(!name.empty() && name.data()[name.size()] == 0); if (!has(name)) return false; @@ -297,6 +302,8 @@ class SettingsProperties class FALCOR_API Settings { public: + using SearchDirectories = std::vector; + /// Get the global settings instance. static Settings& getGlobalSettings(); @@ -338,10 +345,8 @@ class FALCOR_API Settings template std::optional getAttribute(const std::string_view shapeName, const std::string_view attributeName) const { - FALCOR_ASSERT_MSG( - !attributeName.empty() && attributeName.data()[attributeName.size()] == 0, - "The underlying library requires attribute names to be null terminated" - ); + // The underlying library requires attribute names to be null terminated + FALCOR_ASSERT(!attributeName.empty() && attributeName.data()[attributeName.size()] == 0); SettingsProperties props(&getActive().mFilteredAttributes); @@ -369,7 +374,7 @@ class FALCOR_API Settings else { nlohmann::json array = *props.get(attributeNameFilter); - FALCOR_ASSERT_MSG(array.is_array(), "Assume it is a list otherwise"); + FALCOR_CHECK(array.is_array(), "Expecting an array"); expression = array[0].get(); if (array.size() > 1) negateRegex = array[1].get(); @@ -384,10 +389,8 @@ class FALCOR_API Settings template T getAttribute(const std::string_view shapeName, const std::string_view attributeName, const T& def) const { - FALCOR_ASSERT_MSG( - !attributeName.empty() && attributeName.data()[attributeName.size()] == 0, - "The underlying library requires attribute names to be null terminated" - ); + // The underlying library requires attribute names to be null terminated + FALCOR_ASSERT(!attributeName.empty() && attributeName.data()[attributeName.size()] == 0); auto opt = getAttribute(shapeName, attributeName); return opt ? *opt : def; diff --git a/Source/Falcor/Utils/StringFormatters.h b/Source/Falcor/Utils/StringFormatters.h index b74671096..5272a69ee 100644 --- a/Source/Falcor/Utils/StringFormatters.h +++ b/Source/Falcor/Utils/StringFormatters.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Falcor/Utils/StringUtils.cpp b/Source/Falcor/Utils/StringUtils.cpp index b7be1c58a..b578256f6 100644 --- a/Source/Falcor/Utils/StringUtils.cpp +++ b/Source/Falcor/Utils/StringUtils.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "StringUtils.h" +#include "Core/Error.h" #include #include @@ -314,7 +315,7 @@ std::vector decodeBase64(const std::string& in) if (inLen == 0) return {}; if (inLen % 4 != 0) - throw ArgumentError("Input data size is not a multiple of 4"); + FALCOR_THROW("Input data size is not a multiple of 4"); size_t outLen = inLen / 4 * 3; if (in[inLen - 1] == '=') diff --git a/Source/Falcor/Utils/StringUtils.h b/Source/Falcor/Utils/StringUtils.h index 47608f160..63f802a7e 100644 --- a/Source/Falcor/Utils/StringUtils.h +++ b/Source/Falcor/Utils/StringUtils.h @@ -27,7 +27,7 @@ **************************************************************************/ #pragma once #include "Core/Macros.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include #include #include diff --git a/Source/Falcor/Utils/Threading.cpp b/Source/Falcor/Utils/Threading.cpp index af8b9debd..dba5b3ee5 100644 --- a/Source/Falcor/Utils/Threading.cpp +++ b/Source/Falcor/Utils/Threading.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Threading.h" -#include "Core/Assert.h" +#include "Core/Error.h" namespace Falcor { @@ -40,24 +40,32 @@ struct ThreadingData } gData; // TODO: REMOVEGLOBAL } // namespace +static std::mutex sThreadingInitMutex; +static uint32_t sThreadingInitCount = 0; + void Threading::start(uint32_t threadCount) { - if (gData.initialized) - return; - - gData.threads.resize(threadCount); - gData.initialized = true; + std::lock_guard lock(sThreadingInitMutex); + if (sThreadingInitCount++ == 0) + { + gData.threads.resize(threadCount); + gData.initialized = true; + } } void Threading::shutdown() { - for (auto& t : gData.threads) + std::lock_guard lock(sThreadingInitMutex); + uint32_t count = sThreadingInitCount--; + if (count == 1) { - if (t.joinable()) - t.join(); + for (auto& t : gData.threads) + if (t.joinable()) + t.join(); + gData.initialized = false; } - - gData.initialized = false; + else if (count == 0) + FALCOR_THROW("Threading::stop() called more times than Threading::start()."); } Threading::Task Threading::dispatchTask(const std::function& func) diff --git a/Source/Falcor/Utils/Timing/Clock.cpp b/Source/Falcor/Utils/Timing/Clock.cpp index 211ab7267..3cee26ab2 100644 --- a/Source/Falcor/Utils/Timing/Clock.cpp +++ b/Source/Falcor/Utils/Timing/Clock.cpp @@ -265,12 +265,12 @@ Clock& Clock::step(int64_t frames) void Clock::renderUI(Gui::Window& w) { - const Texture* pRewind = w.loadImage("framework/images/rewind.jpg"); - const Texture* pPlay = w.loadImage("framework/images/play.jpg"); - const Texture* pPause = w.loadImage("framework/images/pause.jpg"); - const Texture* pStop = w.loadImage("framework/images/stop.jpg"); - const Texture* pNextFrame = w.loadImage("framework/images/next-frame.jpg"); - const Texture* pPrevFrame = w.loadImage("framework/images/prev-frame.jpg"); + const Texture* pRewind = w.loadImage(getRuntimeDirectory() / "data/framework/images/rewind.jpg"); + const Texture* pPlay = w.loadImage(getRuntimeDirectory() / "data/framework/images/play.jpg"); + const Texture* pPause = w.loadImage(getRuntimeDirectory() / "data/framework/images/pause.jpg"); + const Texture* pStop = w.loadImage(getRuntimeDirectory() / "data/framework/images/stop.jpg"); + const Texture* pNextFrame = w.loadImage(getRuntimeDirectory() / "data/framework/images/next-frame.jpg"); + const Texture* pPrevFrame = w.loadImage(getRuntimeDirectory() / "data/framework/images/prev-frame.jpg"); float time = (float)getTime(); float scale = (float)getTimeScale(); diff --git a/Source/Falcor/Utils/Timing/Profiler.cpp b/Source/Falcor/Utils/Timing/Profiler.cpp index 7d8d48994..31b902711 100644 --- a/Source/Falcor/Utils/Timing/Profiler.cpp +++ b/Source/Falcor/Utils/Timing/Profiler.cpp @@ -306,7 +306,7 @@ void Profiler::Capture::finalize() Profiler::Profiler(ref pDevice) : mpDevice(pDevice) { - mpFence = GpuFence::create(mpDevice); + mpFence = mpDevice->createFence(); mpFence->breakStrongReferenceToDevice(); } @@ -378,7 +378,7 @@ void Profiler::endFrame(RenderContext* pRenderContext) // We use a single fence here instead of one per event, which gets too inefficient. // TODO: This code should refactored to batch the resolve and readback of timestamps. if (mFenceValue != uint64_t(-1)) - mpFence->syncCpu(); + mpFence->wait(); for (Event* pEvent : mCurrentFrameEvents) { @@ -386,8 +386,8 @@ void Profiler::endFrame(RenderContext* pRenderContext) } // Flush and insert signal for synchronization of GPU timings. - pRenderContext->flush(false); - mFenceValue = mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + pRenderContext->submit(false); + mFenceValue = pRenderContext->signal(mpFence.get()); if (mpCapture) mpCapture->captureEvents(mCurrentFrameEvents); @@ -446,8 +446,23 @@ ScopedProfilerEvent::~ScopedProfilerEvent() mpRenderContext->getProfiler()->endEvent(mpRenderContext, mName, mFlags); } +/// Implements a Python context manager for profiling events. +class PythonProfilerEvent +{ +public: + PythonProfilerEvent(RenderContext* pRenderContext, std::string_view name) : mpRenderContext(pRenderContext), mName(name) {} + void enter() { mpRenderContext->getProfiler()->startEvent(mpRenderContext, mName); } + void exit(pybind11::object, pybind11::object, pybind11::object) { mpRenderContext->getProfiler()->endEvent(mpRenderContext, mName); } + +private: + RenderContext* mpRenderContext; + std::string mName; +}; + FALCOR_SCRIPT_BINDING(Profiler) { + FALCOR_SCRIPT_BINDING_DEPENDENCY(RenderContext) + using namespace pybind11::literals; auto endCapture = [](Profiler* pProfiler) @@ -466,5 +481,14 @@ FALCOR_SCRIPT_BINDING(Profiler) profiler.def_property_readonly("events", [](const Profiler& profiler) { return toPython(profiler.getEvents()); }); profiler.def("start_capture", &Profiler::startCapture, "reserved_frames"_a = 1000); profiler.def("end_capture", endCapture); + + pybind11::class_(m, "ProfilerEvent") + .def(pybind11::init()) + .def("__enter__", &PythonProfilerEvent::enter) + .def("__exit__", &PythonProfilerEvent::exit); + + profiler.def( + "event", [](Profiler& self, std::string_view name) { return PythonProfilerEvent(self.getDevice()->getRenderContext(), name); } + ); } } // namespace Falcor diff --git a/Source/Falcor/Utils/Timing/Profiler.h b/Source/Falcor/Utils/Timing/Profiler.h index b9411be76..f8dd01da4 100644 --- a/Source/Falcor/Utils/Timing/Profiler.h +++ b/Source/Falcor/Utils/Timing/Profiler.h @@ -29,6 +29,7 @@ #include "CpuTimer.h" #include "Core/Macros.h" #include "Core/API/GpuTimer.h" +#include "Core/API/Fence.h" #include #include #include @@ -156,6 +157,8 @@ class FALCOR_API Profiler */ Profiler(ref pDevice); + const Device* getDevice() const { return mpDevice.get(); } + /** * Check if the profiler is enabled. * @return Returns true if the profiler is enabled. @@ -265,7 +268,7 @@ class FALCOR_API Profiler std::shared_ptr mpCapture; ///< Currently active capture. - ref mpFence; + ref mpFence; uint64_t mFenceValue = uint64_t(-1); }; diff --git a/Source/Falcor/Utils/Timing/ProfilerUI.cpp b/Source/Falcor/Utils/Timing/ProfilerUI.cpp index 9b1ec002c..0daa85134 100644 --- a/Source/Falcor/Utils/Timing/ProfilerUI.cpp +++ b/Source/Falcor/Utils/Timing/ProfilerUI.cpp @@ -152,7 +152,11 @@ void ProfilerUI::render() auto stats = eventData.pEvent->computeCpuTimeStats(); ImGui::BeginTooltip(); ImGui::Text( - "%s\nMin: %.2f\nMax: %.2f\nMean: %.2f\nStdDev: %.2f", eventData.name.c_str(), stats.min, stats.max, stats.mean, + "%s\nMin: %.2f\nMax: %.2f\nMean: %.2f\nStdDev: %.2f", + eventData.name.c_str(), + stats.min, + stats.max, + stats.mean, stats.stdDev ); ImGui::EndTooltip(); @@ -165,8 +169,11 @@ void ProfilerUI::render() bool isGraphShown = mGraphMode == GraphMode::CpuTime; bool isHighlighted = isGraphShown && i == mHighlightIndex; drawBar( - fraction, ImVec2(kBarWidth, kBarHeight), isGraphShown ? eventData.color : kBarColor, - isGraphShown ? eventData.mutedColor : kBarMutedColor, isHighlighted + fraction, + ImVec2(kBarWidth, kBarHeight), + isGraphShown ? eventData.color : kBarColor, + isGraphShown ? eventData.mutedColor : kBarMutedColor, + isHighlighted ); ImGui::Dummy(ImVec2(kBarWidth, ImGui::GetTextLineHeight())); if (ImGui::IsItemHovered()) @@ -187,7 +194,11 @@ void ProfilerUI::render() auto stats = eventData.pEvent->computeGpuTimeStats(); ImGui::BeginTooltip(); ImGui::Text( - "%s\nMin: %.2f\nMax: %.2f\nMean: %.2f\nStdDev: %.2f", eventData.name.c_str(), stats.min, stats.max, stats.mean, + "%s\nMin: %.2f\nMax: %.2f\nMean: %.2f\nStdDev: %.2f", + eventData.name.c_str(), + stats.min, + stats.max, + stats.mean, stats.stdDev ); ImGui::EndTooltip(); @@ -200,8 +211,11 @@ void ProfilerUI::render() bool isGraphShown = mGraphMode == GraphMode::GpuTime; bool isHighlighted = isGraphShown && i == mHighlightIndex; drawBar( - fraction, ImVec2(kBarWidth, kBarHeight), isGraphShown ? eventData.color : kBarColor, - isGraphShown ? eventData.mutedColor : kBarMutedColor, isHighlighted + fraction, + ImVec2(kBarWidth, kBarHeight), + isGraphShown ? eventData.color : kBarColor, + isGraphShown ? eventData.mutedColor : kBarMutedColor, + isHighlighted ); ImGui::Dummy(ImVec2(kBarWidth, ImGui::GetTextLineHeight())); if (ImGui::IsItemHovered()) diff --git a/Source/Falcor/Utils/UI/Font.cpp b/Source/Falcor/Utils/UI/Font.cpp index db31412b1..ca5f63635 100644 --- a/Source/Falcor/Utils/UI/Font.cpp +++ b/Source/Falcor/Utils/UI/Font.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Font.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/API/Texture.h" #include @@ -59,7 +59,7 @@ struct FontCharData Font::Font(ref pDevice, const std::filesystem::path& path) { if (!loadFromFile(pDevice, path)) - throw RuntimeError("Failed to create font resource"); + FALCOR_THROW("Failed to create font resource"); } Font::~Font() = default; diff --git a/Source/Falcor/Utils/UI/Font.h b/Source/Falcor/Utils/UI/Font.h index 77a5db420..3cf6ff360 100644 --- a/Source/Falcor/Utils/UI/Font.h +++ b/Source/Falcor/Utils/UI/Font.h @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/API/fwd.h" #include "Core/API/Texture.h" #include "Utils/Math/Vector.h" diff --git a/Source/Falcor/Utils/UI/Gui.cpp b/Source/Falcor/Utils/UI/Gui.cpp index ab52054ef..3871af88f 100644 --- a/Source/Falcor/Utils/UI/Gui.cpp +++ b/Source/Falcor/Utils/UI/Gui.cpp @@ -32,7 +32,7 @@ #include "Core/API/VertexLayout.h" #include "Core/API/RenderContext.h" #include "Core/State/GraphicsState.h" -#include "Core/Program/GraphicsProgram.h" +#include "Core/Program/Program.h" #include "Core/Program/ProgramVars.h" #include "Utils/Logger.h" #include "Utils/StringUtils.h" @@ -57,12 +57,16 @@ class GuiImpl private: friend class Gui; void init(Gui* pGui, float scaleFactor); - void createVao(uint32_t vertexCount, uint32_t indexCount); + const ref& getNextVao(uint32_t vertexCount, uint32_t indexCount); void compileFonts(); // Helper to create multiple inline text boxes bool addCheckboxes(const char label[], bool* pData, uint32_t numCheckboxes, bool sameLine); + // Use 3 rotating VAOs to avoid stalling the GPU. + // A better way would be to upload the data using an upload heap. + static constexpr uint32_t kVaoCount = 3; + struct ComboData { uint32_t lastVal = -1; @@ -83,13 +87,14 @@ class GuiImpl ref mpDevice; ImGuiContext* mpContext; - ref mpVao; + ref mpVaos[kVaoCount]; + uint32_t mVaoIndex = 0; ref mpLayout; ref mpPipelineState; uint32_t mGroupStackSize = 0; - ref mpProgram; - ref mpProgramVars; + ref mpProgram; + ref mpProgramVars; ParameterBlockReflection::BindLocation mGuiImageLoc; float mScaleFactor = 1.0f; std::unordered_map mFontMap; @@ -338,15 +343,20 @@ GuiImpl::GuiImpl(ref pDevice, float scaleFactor) : mpDevice(pDevice), mS mpPipelineState = GraphicsState::create(mpDevice); // Create the program - mpProgram = GraphicsProgram::createFromFile(mpDevice, "Utils/UI/Gui.slang", "vsMain", "psMain"); - mpProgramVars = GraphicsVars::create(mpDevice, mpProgram->getReflector()); + mpProgram = Program::createGraphics(mpDevice, "Utils/UI/Gui.slang", "vsMain", "psMain"); + mpProgramVars = ProgramVars::create(mpDevice, mpProgram->getReflector()); mpPipelineState->setProgram(mpProgram); // Create the blend state BlendState::Desc blendDesc; blendDesc.setRtBlend(0, true).setRtParams( - 0, BlendState::BlendOp::Add, BlendState::BlendOp::Add, BlendState::BlendFunc::SrcAlpha, BlendState::BlendFunc::OneMinusSrcAlpha, - BlendState::BlendFunc::OneMinusSrcAlpha, BlendState::BlendFunc::Zero + 0, + BlendState::BlendOp::Add, + BlendState::BlendOp::Add, + BlendState::BlendFunc::SrcAlpha, + BlendState::BlendFunc::OneMinusSrcAlpha, + BlendState::BlendFunc::OneMinusSrcAlpha, + BlendState::BlendFunc::Zero ); mpPipelineState->setBlendState(BlendState::create(blendDesc)); @@ -374,7 +384,7 @@ GuiImpl::GuiImpl(ref pDevice, float scaleFactor) : mpDevice(pDevice), mS mGuiImageLoc = mpProgram->getReflector()->getDefaultParameterBlock()->getResourceBinding("guiImage"); } -void GuiImpl::createVao(uint32_t vertexCount, uint32_t indexCount) +const ref& GuiImpl::getNextVao(uint32_t vertexCount, uint32_t indexCount) { static_assert(sizeof(ImDrawIdx) == sizeof(uint16_t), "ImDrawIdx expected size is a word"); FALCOR_ASSERT(vertexCount > 0 && indexCount > 0); @@ -383,28 +393,29 @@ void GuiImpl::createVao(uint32_t vertexCount, uint32_t indexCount) bool createVB = true; bool createIB = true; - if (mpVao) + auto& pVao = mpVaos[mVaoIndex]; + mVaoIndex = (mVaoIndex + 1) % kVaoCount; + + if (pVao) { - FALCOR_ASSERT(mpVao->getVertexBuffer(0) && mpVao->getIndexBuffer()); - createVB = mpVao->getVertexBuffer(0)->getSize() < requiredVbSize; - createIB = mpVao->getIndexBuffer()->getSize() < requiredIbSize; + FALCOR_ASSERT(pVao->getVertexBuffer(0) && pVao->getIndexBuffer()); + createVB = pVao->getVertexBuffer(0)->getSize() < requiredVbSize; + createIB = pVao->getIndexBuffer()->getSize() < requiredIbSize; if (!createIB && !createVB) - { - return; - } + return pVao; } // Need to create a new VAO std::vector> pVB(1); - pVB[0] = createVB - ? Buffer::create( - mpDevice, requiredVbSize + sizeof(ImDrawVert) * 1000, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, nullptr - ) - : mpVao->getVertexBuffer(0); - ref pIB = createIB ? Buffer::create(mpDevice, requiredIbSize, Buffer::BindFlags::Index, Buffer::CpuAccess::Write, nullptr) - : mpVao->getIndexBuffer(); - mpVao = Vao::create(Vao::Topology::TriangleList, mpLayout, pVB, pIB, ResourceFormat::R16Uint); + pVB[0] = + createVB + ? mpDevice->createBuffer(requiredVbSize + sizeof(ImDrawVert) * 1000, ResourceBindFlags::Vertex, MemoryType::Upload, nullptr) + : pVao->getVertexBuffer(0); + ref pIB = + createIB ? mpDevice->createBuffer(requiredIbSize, ResourceBindFlags::Index, MemoryType::Upload, nullptr) : pVao->getIndexBuffer(); + pVao = Vao::create(Vao::Topology::TriangleList, mpLayout, pVB, pIB, ResourceFormat::R16Uint); + return pVao; } void GuiImpl::compileFonts() @@ -414,7 +425,7 @@ void GuiImpl::compileFonts() // Initialize font data ImGui::GetIO().Fonts->GetTexDataAsAlpha8(&pFontData, &width, &height); - ref pTexture = Texture::create2D(mpDevice, width, height, ResourceFormat::R8Unorm, 1, 1, pFontData); + ref pTexture = mpDevice->createTexture2D(width, height, ResourceFormat::R8Unorm, 1, 1, pFontData); mpProgramVars->setTexture("gFont", pTexture); } @@ -837,7 +848,7 @@ const Texture* GuiImpl::loadImage(const std::filesystem::path& path) ref pTex = Texture::createFromFile(mpDevice, path, false, true); if (!pTex) - throw RuntimeError("Failed to load GUI image from '{}'.", path); + FALCOR_THROW("Failed to load GUI image from '{}'.", path); return mLoadedImages.emplace(path, pTex).first->second.get(); } @@ -1204,7 +1215,7 @@ void Gui::addFont(const std::string& name, const std::filesystem::path& path) float size = 14.0f * mpWrapper->mScaleFactor; ImFont* pFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(path.string().c_str(), size); if (!pFont) - throw RuntimeError("Failed to load font from '{}'.", path); + FALCOR_THROW("Failed to load font from '{}'.", path); mpWrapper->mFontMap[name] = pFont; mpWrapper->compileFonts(); } @@ -1252,23 +1263,23 @@ void Gui::render(RenderContext* pContext, const ref& pFbo, float elapsedTim if (pDrawData->CmdListsCount > 0) { // Update the VAO - mpWrapper->createVao(pDrawData->TotalVtxCount, pDrawData->TotalIdxCount); - mpWrapper->mpPipelineState->setVao(mpWrapper->mpVao); + const ref& pVao = mpWrapper->getNextVao(pDrawData->TotalVtxCount, pDrawData->TotalIdxCount); + mpWrapper->mpPipelineState->setVao(pVao); // Upload the data - ImDrawVert* pVerts = (ImDrawVert*)mpWrapper->mpVao->getVertexBuffer(0)->map(Buffer::MapType::WriteDiscard); - uint16_t* pIndices = (uint16_t*)mpWrapper->mpVao->getIndexBuffer()->map(Buffer::MapType::WriteDiscard); + ImDrawVert* pVerts = (ImDrawVert*)pVao->getVertexBuffer(0)->map(Buffer::MapType::Write); + uint16_t* pIndices = (uint16_t*)pVao->getIndexBuffer()->map(Buffer::MapType::Write); for (int n = 0; n < pDrawData->CmdListsCount; n++) { const ImDrawList* pCmdList = pDrawData->CmdLists[n]; - memcpy(pVerts, pCmdList->VtxBuffer.Data, pCmdList->VtxBuffer.Size * sizeof(ImDrawVert)); - memcpy(pIndices, pCmdList->IdxBuffer.Data, pCmdList->IdxBuffer.Size * sizeof(ImDrawIdx)); + std::memcpy(pVerts, pCmdList->VtxBuffer.Data, pCmdList->VtxBuffer.Size * sizeof(ImDrawVert)); + std::memcpy(pIndices, pCmdList->IdxBuffer.Data, pCmdList->IdxBuffer.Size * sizeof(ImDrawIdx)); pVerts += pCmdList->VtxBuffer.Size; pIndices += pCmdList->IdxBuffer.Size; } - mpWrapper->mpVao->getVertexBuffer(0)->unmap(); - mpWrapper->mpVao->getIndexBuffer()->unmap(); + pVao->getVertexBuffer(0)->unmap(); + pVao->getIndexBuffer()->unmap(); mpWrapper->mpPipelineState->setFbo(pFbo); // Setup viewport diff --git a/Source/Falcor/Utils/UI/InputState.cpp b/Source/Falcor/Utils/UI/InputState.cpp index 29dd3555d..efd1ca2fb 100644 --- a/Source/Falcor/Utils/UI/InputState.cpp +++ b/Source/Falcor/Utils/UI/InputState.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "InputState.h" -#include "Core/Assert.h" +#include "Core/Error.h" namespace Falcor { diff --git a/Source/Falcor/Utils/UI/PixelZoom.cpp b/Source/Falcor/Utils/UI/PixelZoom.cpp index d3f01d038..3057833ab 100644 --- a/Source/Falcor/Utils/UI/PixelZoom.cpp +++ b/Source/Falcor/Utils/UI/PixelZoom.cpp @@ -87,7 +87,11 @@ void PixelZoom::render(RenderContext* pCtx, Fbo* backBuffer) float4 srcRect = float4(srcPix.x - offset, srcPix.y - offset, srcPix.x + offset, srcPix.y + offset); float4 dstRect = float4(0, 0, mDstZoomSize, mDstZoomSize); pCtx->blit( - mpSrcBlitFbo->getColorTexture(0)->getSRV(), mpDstBlitFbo->getColorTexture(0)->getRTV(), srcRect, dstRect, Sampler::Filter::Point + mpSrcBlitFbo->getColorTexture(0)->getSRV(), + mpDstBlitFbo->getColorTexture(0)->getRTV(), + srcRect, + dstRect, + TextureFilteringMode::Point ); // blit dst blt fbo into back buffer @@ -96,7 +100,11 @@ void PixelZoom::render(RenderContext* pCtx, Fbo* backBuffer) srcRect = dstRect; dstRect = float4(srcPix.x - offset, srcPix.y - offset, srcPix.x + offset, srcPix.y + offset); pCtx->blit( - mpDstBlitFbo->getColorTexture(0)->getSRV(), backBuffer->getColorTexture(0)->getRTV(), srcRect, dstRect, Sampler::Filter::Point + mpDstBlitFbo->getColorTexture(0)->getSRV(), + backBuffer->getColorTexture(0)->getRTV(), + srcRect, + dstRect, + TextureFilteringMode::Point ); } } diff --git a/Source/Falcor/Utils/UI/PythonUI.cpp b/Source/Falcor/Utils/UI/PythonUI.cpp new file mode 100644 index 000000000..3f1312a39 --- /dev/null +++ b/Source/Falcor/Utils/UI/PythonUI.cpp @@ -0,0 +1,714 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "PythonUI.h" +#include "Utils/Scripting/ScriptBindings.h" +#include "Utils/Math/Vector.h" +#include + +PYBIND11_MAKE_OPAQUE(std::vector>); + +namespace Falcor +{ +namespace python_ui +{ + +/// Scoped push/pop of ImGui ID. +class ScopedID +{ +public: + ScopedID(void* id) { ImGui::PushID(id); } + ~ScopedID() { ImGui::PopID(); } +}; + +/// Scoped begin/end for disabling ImGUI widgets. +class ScopedDisable +{ +public: + ScopedDisable(bool disabled) : m_disabled(disabled) + { + if (disabled) + ImGui::BeginDisabled(); + } + ~ScopedDisable() + { + if (m_disabled) + ImGui::EndDisabled(); + } + +private: + bool m_disabled; +}; + +class Window : public Widget +{ + FALCOR_OBJECT(Window) +public: + Window(Widget* parent, std::string_view title = "", float2 position = float2(10.f, 10.f), float2 size = float2(400.f, 400.f)) + : Widget(parent), m_title(title), m_position(position), m_size(size) + {} + + const std::string& get_title() const { return m_title; } + void set_title(std::string_view title) { m_title = title; } + + float2 get_position() const { return m_position; } + void set_position(const float2& position) + { + m_position = position; + m_set_position = true; + } + + float2 get_size() const { return m_size; } + void set_size(const float2& size) + { + m_size = size; + m_set_size = true; + } + + void show() { set_visible(true); } + void close() { set_visible(false); } + + virtual void render() override + { + if (!m_visible) + return; + + if (m_set_position) + { + ImGui::SetNextWindowPos(ImVec2(m_position.x, m_position.y)); + m_set_position = false; + } + if (m_set_size) + { + ImGui::SetNextWindowSize(ImVec2(m_size.x, m_size.y)); + m_set_size = false; + } + + ScopedID id(this); + if (ImGui::Begin(m_title.c_str(), &m_visible)) + { + auto pos = ImGui::GetWindowPos(); + m_position = float2(pos.x, pos.y); + auto size = ImGui::GetWindowSize(); + m_size = float2(size.x, size.y); + + ImGui::PushItemWidth(300); + Widget::render(); + ImGui::PopItemWidth(); + } + ImGui::End(); + } + +private: + std::string m_title; + float2 m_position; + float2 m_size; + bool m_set_position{true}; + bool m_set_size{true}; +}; + +class Group : public Widget +{ + FALCOR_OBJECT(Group) +public: + Group(Widget* parent, std::string_view label = "") : Widget(parent), m_label(label) {} + + const std::string& get_label() const { return m_label; } + void set_label(std::string_view label) { m_label = label; } + + virtual void render() override + { + // Check if this is a nested group + bool nested = false; + for (Widget* p = get_parent(); p != nullptr; p = p->get_parent()) + if (dynamic_cast(p) != nullptr) + nested = true; + + ScopedID id(this); + ScopedDisable disable(!m_enabled); + + if (nested ? ImGui::TreeNodeEx(m_label.c_str(), ImGuiTreeNodeFlags_DefaultOpen) + : ImGui::CollapsingHeader(m_label.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) + { + Widget::render(); + if (nested) + ImGui::TreePop(); + } + } + +private: + std::string m_label; +}; + +class Text : public Widget +{ + FALCOR_OBJECT(Text) +public: + Text(Widget* parent, std::string_view text = "") : Widget(parent), m_text(text) {} + + const std::string& get_text() const { return m_text; } + void set_text(std::string_view text) { m_text = text; } + + virtual void render() override + { + ScopedID id(this); + ScopedDisable disable(!m_enabled); + ImGui::TextUnformatted(m_text.c_str()); + } + +private: + std::string m_text; +}; + +class ProgressBar : public Widget +{ + FALCOR_OBJECT(ProgressBar) +public: + ProgressBar(Widget* parent, float fraction = 0.f) : Widget(parent), m_fraction(fraction) {} + + float get_fraction() const { return m_fraction; } + void set_fraction(float fraction) { m_fraction = fraction; } + + virtual void render() override + { + ScopedID id(this); + ScopedDisable disable(!m_enabled); + ImGui::ProgressBar(m_fraction); + } + +private: + float m_fraction; +}; + +class Button : public Widget +{ + FALCOR_OBJECT(Button) +public: + using Callback = std::function; + + Button(Widget* parent, std::string_view label = "", Callback callback = {}) : Widget(parent), m_label(label), m_callback(callback) {} + + const std::string& get_label() const { return m_label; } + void set_label(std::string_view label) { m_label = label; } + + Callback get_callback() const { return m_callback; } + void set_callback(Callback callback) { m_callback = callback; } + + virtual void render() override + { + ScopedID id(this); + ScopedDisable disable(!m_enabled); + if (ImGui::Button(m_label.c_str())) + { + if (m_callback) + m_callback(); + } + } + +private: + std::string m_label; + Callback m_callback; +}; + +class Property : public Widget +{ + FALCOR_OBJECT(Property) +public: + using ChangeCallback = std::function; + + Property(Widget* parent, std::string_view label, ChangeCallback change_callback) + : Widget(parent), m_label(label), m_change_callback(change_callback) + {} + + const std::string& get_label() const { return m_label; } + void set_label(std::string_view label) { m_label = label; } + + ChangeCallback get_change_callback() const { return m_change_callback; } + void set_change_callback(ChangeCallback change_callback) { m_change_callback = change_callback; } + +protected: + std::string m_label; + ChangeCallback m_change_callback; +}; + +class Checkbox : public Property +{ + FALCOR_OBJECT(Checkbox) +public: + Checkbox(Widget* parent, std::string_view label = "", ChangeCallback change_callback = {}, bool value = false) + : Property(parent, label, change_callback), m_value(value) + {} + + bool get_value() const { return m_value; } + void set_value(bool value) { m_value = value; } + + virtual void render() override + { + ScopedID id(this); + ScopedDisable disable(!m_enabled); + if (ImGui::Checkbox(m_label.c_str(), &m_value)) + { + if (m_change_callback) + m_change_callback(); + } + } + +private: + bool m_value; +}; + +class Combobox : public Property +{ + FALCOR_OBJECT(Combobox) +public: + Combobox( + Widget* parent, + std::string_view label = "", + ChangeCallback change_callback = {}, + std::vector items = {}, + int value = 0 + ) + : Property(parent, label, change_callback), m_items(items), m_value(value) + {} + + const std::vector& get_items() const { return m_items; } + void set_items(const std::vector& items) { m_items = items; } + + int get_value() const { return m_value; } + void set_value(int value) { m_value = std::clamp(value, 0, (int)m_items.size()); } + + virtual void render() override + { + ScopedID id(this); + ScopedDisable disable(!m_enabled); + if (ImGui::Combo( + m_label.c_str(), + &m_value, + [](void* data, int idx, const char** out_text) -> bool + { + auto& items = *reinterpret_cast*>(data); + if (idx < 0 || idx >= items.size()) + return false; + *out_text = items[idx].c_str(); + return true; + }, + &m_items, + (int)m_items.size() + )) + { + if (m_change_callback) + m_change_callback(); + } + } + +private: + std::vector m_items; + int m_value; +}; + +enum class SliderFlags +{ + None = ImGuiSliderFlags_None, + AlwaysClamp = ImGuiSliderFlags_AlwaysClamp, + Logarithmic = ImGuiSliderFlags_Logarithmic, + NoRoundToFormat = ImGuiSliderFlags_NoRoundToFormat, + NoInput = ImGuiSliderFlags_NoInput, +}; + +template +struct SliderTraits +{}; + +// clang-format off +template<> struct SliderTraits { using type = float; using scalar_type = float; static constexpr bool is_float = true; static constexpr int N = 1; static constexpr const char* default_format = "%.3f"; }; +template<> struct SliderTraits { using type = float2; using scalar_type = float; static constexpr bool is_float = true; static constexpr int N = 2; static constexpr const char* default_format = "%.3f"; }; +template<> struct SliderTraits { using type = float3; using scalar_type = float; static constexpr bool is_float = true; static constexpr int N = 3; static constexpr const char* default_format = "%.3f"; }; +template<> struct SliderTraits { using type = float4; using scalar_type = float; static constexpr bool is_float = true; static constexpr int N = 4; static constexpr const char* default_format = "%.3f"; }; +template<> struct SliderTraits { using type = int; using scalar_type = int; static constexpr bool is_float = false; static constexpr int N = 1; static constexpr const char* default_format = "%d"; }; +template<> struct SliderTraits { using type = int2; using scalar_type = int; static constexpr bool is_float = false; static constexpr int N = 2; static constexpr const char* default_format = "%d"; }; +template<> struct SliderTraits { using type = int3; using scalar_type = int; static constexpr bool is_float = false; static constexpr int N = 3; static constexpr const char* default_format = "%d"; }; +template<> struct SliderTraits { using type = int4; using scalar_type = int; static constexpr bool is_float = false; static constexpr int N = 4; static constexpr const char* default_format = "%d"; }; +// clang-format on + +template +class Drag : public Property +{ + FALCOR_OBJECT(Drag) +public: + using traits = SliderTraits; + using type = typename traits::type; + using scalar_type = typename traits::scalar_type; + static constexpr bool is_float = traits::is_float; + static constexpr int N = traits::N; + static constexpr const char* default_format = traits::default_format; + + Drag( + Widget* parent, + std::string_view label = "", + ChangeCallback change_callback = {}, + type value = type(0), + float speed = 1.f, + scalar_type min = scalar_type(0), + scalar_type max = scalar_type(0), + std::string_view format = default_format, + SliderFlags flags = SliderFlags::None + ) + : Property(parent, label, change_callback), m_value(value), m_speed(speed), m_min(min), m_max(max), m_format(format), m_flags(flags) + {} + + type get_value() const { return m_value; } + void set_value(type value) { m_value = value; } + + scalar_type get_speed() const { return m_speed; } + void set_speed(scalar_type speed) { m_speed = speed; } + + scalar_type get_min() const { return m_min; } + void set_min(scalar_type min) { m_min = min; } + + scalar_type get_max() const { return m_max; } + void set_max(scalar_type max) { m_max = max; } + + const std::string& get_format() const { return m_format; } + void set_format(std::string_view format) { m_format = format; } + + SliderFlags get_flags() const { return m_flags; } + void set_flags(SliderFlags flags) { m_flags = flags; } + + virtual void render() override + { + ScopedID id(this); + ScopedDisable disable(!m_enabled); + bool changed = false; + if constexpr (is_float == true) + { + if constexpr (N == 1) + changed = ImGui::DragFloat(m_label.c_str(), &m_value, m_speed, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + if constexpr (N == 2) + changed = + ImGui::DragFloat2(m_label.c_str(), &m_value.x, m_speed, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + if constexpr (N == 3) + changed = + ImGui::DragFloat3(m_label.c_str(), &m_value.x, m_speed, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + if constexpr (N == 4) + changed = + ImGui::DragFloat4(m_label.c_str(), &m_value.x, m_speed, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + } + else + { + if constexpr (N == 1) + changed = ImGui::DragInt(m_label.c_str(), &m_value, m_speed, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + if constexpr (N == 2) + changed = ImGui::DragInt2(m_label.c_str(), &m_value.x, m_speed, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + if constexpr (N == 3) + changed = ImGui::DragInt3(m_label.c_str(), &m_value.x, m_speed, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + if constexpr (N == 4) + changed = ImGui::DragInt4(m_label.c_str(), &m_value.x, m_speed, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + } + + if (changed && m_change_callback) + m_change_callback(); + } + +private: + type m_value; + float m_speed; + scalar_type m_min; + scalar_type m_max; + std::string m_format; + SliderFlags m_flags; +}; + +using DragFloat = Drag; +using DragFloat2 = Drag; +using DragFloat3 = Drag; +using DragFloat4 = Drag; +using DragInt = Drag; +using DragInt2 = Drag; +using DragInt3 = Drag; +using DragInt4 = Drag; + +template +class Slider : public Property +{ + FALCOR_OBJECT(Slider) +public: + using traits = SliderTraits; + using type = typename traits::type; + using scalar_type = typename traits::scalar_type; + static constexpr bool is_float = traits::is_float; + static constexpr int N = traits::N; + static constexpr const char* default_format = traits::default_format; + + Slider( + Widget* parent, + std::string_view label = "", + ChangeCallback change_callback = {}, + type value = type(0), + scalar_type min = scalar_type(0), + scalar_type max = scalar_type(0), + std::string_view format = default_format, + SliderFlags flags = SliderFlags::None + ) + : Property(parent, label, change_callback), m_value(value), m_min(min), m_max(max), m_format(format), m_flags(flags) + {} + + type get_value() const { return m_value; } + void set_value(type value) { m_value = value; } + + scalar_type get_min() const { return m_min; } + void set_min(scalar_type min) { m_min = min; } + + scalar_type get_max() const { return m_max; } + void set_max(scalar_type max) { m_max = max; } + + const std::string& get_format() const { return m_format; } + void set_format(std::string_view format) { m_format = format; } + + SliderFlags get_flags() const { return m_flags; } + void set_flags(SliderFlags flags) { m_flags = flags; } + + virtual void render() override + { + ScopedID id(this); + ScopedDisable disable(!m_enabled); + bool changed = false; + if constexpr (is_float == true) + { + if constexpr (N == 1) + changed = ImGui::SliderFloat(m_label.c_str(), &m_value, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + if constexpr (N == 2) + changed = ImGui::SliderFloat2(m_label.c_str(), &m_value.x, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + if constexpr (N == 3) + changed = ImGui::SliderFloat3(m_label.c_str(), &m_value.x, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + if constexpr (N == 4) + changed = ImGui::SliderFloat4(m_label.c_str(), &m_value.x, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + } + else + { + if constexpr (N == 1) + changed = ImGui::SliderInt(m_label.c_str(), &m_value, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + if constexpr (N == 2) + changed = ImGui::SliderInt2(m_label.c_str(), &m_value.x, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + if constexpr (N == 3) + changed = ImGui::SliderInt3(m_label.c_str(), &m_value.x, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + if constexpr (N == 4) + changed = ImGui::SliderInt4(m_label.c_str(), &m_value.x, m_min, m_max, m_format.c_str(), (ImGuiSliderFlags)m_flags); + } + + if (changed && m_change_callback) + m_change_callback(); + } + +private: + type m_value; + scalar_type m_min; + scalar_type m_max; + std::string m_format; + SliderFlags m_flags; +}; + +using SliderFloat = Slider; +using SliderFloat2 = Slider; +using SliderFloat3 = Slider; +using SliderFloat4 = Slider; +using SliderInt = Slider; +using SliderInt2 = Slider; +using SliderInt3 = Slider; +using SliderInt4 = Slider; + +template +static void bind_drag(pybind11::module_ m, const char* name) +{ + using namespace pybind11::literals; + + pybind11::class_> drag(m, name); + drag.def( + pybind11::init< + Widget*, + std::string_view, + typename T::ChangeCallback, + typename T::type, + float, + typename T::scalar_type, + typename T::scalar_type, + std::string_view, + SliderFlags>(), + "parent"_a, + "label"_a = "", + "change_callback"_a = typename T::ChangeCallback{}, + "value"_a = typename T::type(0), + "speed"_a = 1.f, + "min"_a = typename T::scalar_type(0), + "max"_a = typename T::scalar_type(0), + "format"_a = T::default_format, + "flags"_a = SliderFlags::None + ); + drag.def_property("value", &T::get_value, &T::set_value); + drag.def_property("speed", &T::get_speed, &T::set_speed); + drag.def_property("min", &T::get_min, &T::set_min); + drag.def_property("max", &T::get_max, &T::set_max); + drag.def_property("format", &T::get_format, &T::set_format); + drag.def_property("flags", &T::get_flags, &T::set_flags); +} + +template +static void bind_slider(pybind11::module_ m, const char* name) +{ + using namespace pybind11::literals; + + pybind11::class_> drag(m, name); + drag.def( + pybind11::init< + Widget*, + std::string_view, + typename T::ChangeCallback, + typename T::type, + typename T::scalar_type, + typename T::scalar_type, + std::string_view, + SliderFlags>(), + "parent"_a, + "label"_a = "", + "change_callback"_a = typename T::ChangeCallback{}, + "value"_a = typename T::type(0), + "min"_a = typename T::scalar_type(0), + "max"_a = typename T::scalar_type(0), + "format"_a = T::default_format, + "flags"_a = SliderFlags::None + ); + drag.def_property("value", &T::get_value, &T::set_value); + drag.def_property("min", &T::get_min, &T::set_min); + drag.def_property("max", &T::get_max, &T::set_max); + drag.def_property("format", &T::get_format, &T::set_format); + drag.def_property("flags", &T::get_flags, &T::set_flags); +} + +FALCOR_SCRIPT_BINDING(python_ui) +{ + using namespace pybind11::literals; + + pybind11::module_ ui = m.def_submodule("ui"); + + pybind11::class_> widget(ui, "Widget"); + + pybind11::bind_vector>>(ui, "WidgetVector"); + + widget.def_property("parent", (Widget * (Widget::*)(void)) & Widget::get_parent, &Widget::set_parent); + widget.def_property_readonly("children", &Widget::get_children); + widget.def_property("visible", &Widget::get_visible, &Widget::set_visible); + widget.def_property("enabled", &Widget::get_enabled, &Widget::set_enabled); + + pybind11::class_> screen(ui, "Screen"); + + pybind11::class_> window(ui, "Window"); + window.def( + pybind11::init(), + "parent"_a, + "title"_a = "", + "position"_a = float2(10.f, 10.f), + "size"_a = float2(400.f, 400.f) + ); + window.def("show", &Window::show); + window.def("close", &Window::close); + window.def_property("title", &Window::get_title, &Window::set_title); + window.def_property("position", &Window::get_position, &Window::set_position); + window.def_property("size", &Window::get_size, &Window::set_size); + + pybind11::class_> group(ui, "Group"); + group.def(pybind11::init(), "parent"_a, "label"_a = ""); + group.def_property("label", &Group::get_label, &Group::set_label); + + pybind11::class_> text(ui, "Text"); + text.def(pybind11::init(), "parent"_a, "text"_a = ""); + text.def_property("text", &Text::get_text, &Text::set_text); + + pybind11::class_> progress_bar(ui, "ProgressBar"); + progress_bar.def(pybind11::init(), "parent"_a, "fraction"_a = 0.f); + progress_bar.def_property("fraction", &ProgressBar::get_fraction, &ProgressBar::set_fraction); + + pybind11::class_> button(ui, "Button"); + button.def( + pybind11::init(), "parent"_a, "label"_a = "", "callback"_a = Button::Callback{} + ); + button.def_property("label", &Button::get_label, &Button::set_label); + button.def_property("callback", &Button::get_callback, &Button::set_callback); + + pybind11::class_> property(ui, "Property"); + property.def_property("label", &Property::get_label, &Property::set_label); + property.def_property("change_callback", &Property::get_change_callback, &Property::set_change_callback); + + pybind11::class_> checkbox(ui, "Checkbox"); + checkbox.def( + pybind11::init(), + "parent"_a, + "label"_a = "", + "change_callback"_a = Checkbox::ChangeCallback{}, + "value"_a = false + ); + checkbox.def_property("value", &Checkbox::get_value, &Checkbox::set_value); + + pybind11::class_> combobox(ui, "Combobox"); + combobox.def( + pybind11::init, int>(), + "parent"_a, + "label"_a = "", + "change_callback"_a = Combobox::ChangeCallback{}, + "items"_a = std::vector{}, + "value"_a = 0 + ); + combobox.def_property("items", &Combobox::get_items, &Combobox::set_items); + combobox.def_property("value", &Combobox::get_value, &Combobox::set_value); + + pybind11::enum_ slider_flags(ui, "SliderFlags"); + slider_flags.value("None_", SliderFlags::None); + slider_flags.value("AlwaysClamp", SliderFlags::AlwaysClamp); + slider_flags.value("Logarithmic", SliderFlags::Logarithmic); + slider_flags.value("NoRoundToFormat", SliderFlags::NoRoundToFormat); + slider_flags.value("NoInput", SliderFlags::NoInput); + + bind_drag(ui, "DragFloat"); + bind_drag(ui, "DragFloat2"); + bind_drag(ui, "DragFloat3"); + bind_drag(ui, "DragFloat4"); + bind_drag(ui, "DragInt"); + bind_drag(ui, "DragInt2"); + bind_drag(ui, "DragInt3"); + bind_drag(ui, "DragInt4"); + + bind_slider(ui, "SliderFloat"); + bind_slider(ui, "SliderFloat2"); + bind_slider(ui, "SliderFloat3"); + bind_slider(ui, "SliderFloat4"); + bind_slider(ui, "SliderInt"); + bind_slider(ui, "SliderInt2"); + bind_slider(ui, "SliderInt3"); + bind_slider(ui, "SliderInt4"); +} + +} // namespace python_ui + +} // namespace Falcor diff --git a/Source/Falcor/Core/API/ShaderType.h b/Source/Falcor/Utils/UI/PythonUI.h similarity index 52% rename from Source/Falcor/Core/API/ShaderType.h rename to Source/Falcor/Utils/UI/PythonUI.h index 2dba23103..4763fcb7a 100644 --- a/Source/Falcor/Core/API/ShaderType.h +++ b/Source/Falcor/Utils/UI/PythonUI.h @@ -26,71 +26,72 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Core/Macros.h" -#include "Core/Assert.h" + +#include "Core/Object.h" + +#include #include +#include +#include namespace Falcor { -/** - * Falcor shader types - */ -enum class ShaderType +namespace python_ui { - Vertex, ///< Vertex shader - Pixel, ///< Pixel shader - Geometry, ///< Geometry shader - Hull, ///< Hull shader (AKA Tessellation control shader) - Domain, ///< Domain shader (AKA Tessellation evaluation shader) - Compute, ///< Compute shader - - RayGeneration, ///< Ray generation shader - Intersection, ///< Intersection shader - AnyHit, ///< Any hit shader - ClosestHit, ///< Closest hit shader - Miss, ///< Miss shader - Callable, ///< Callable shader - Count ///< Shader Type count -}; -/** - * Converts ShaderType enum elements to a string. - * @param[in] type Type to convert to string - * @return Shader type as a string - */ -inline const std::string to_string(ShaderType type) +/// Base class for Python UI widgets. +/// Widgets own their children. +class Widget : public Object { - switch (type) + FALCOR_OBJECT(Widget) +public: + Widget(Widget* parent) : m_parent(parent) + { + if (m_parent) + m_parent->m_children.push_back(ref(this)); + } + + virtual ~Widget() {} + + Widget* get_parent() { return m_parent; } + const Widget* get_parent() const { return m_parent; } + void set_parent(Widget* parent) { m_parent = parent; } + + const std::vector>& get_children() const { return m_children; } + + bool get_visible() const { return m_visible; } + void set_visible(bool visible) { m_visible = visible; } + + bool get_enabled() const { return m_enabled; } + void set_enabled(bool enabled) { m_enabled = enabled; } + + virtual void render() { - case ShaderType::Vertex: - return "vertex"; - case ShaderType::Pixel: - return "pixel"; - case ShaderType::Hull: - return "hull"; - case ShaderType::Domain: - return "domain"; - case ShaderType::Geometry: - return "geometry"; - case ShaderType::Compute: - return "compute"; - case ShaderType::RayGeneration: - return "raygeneration"; - case ShaderType::Intersection: - return "intersection"; - case ShaderType::AnyHit: - return "anyhit"; - case ShaderType::ClosestHit: - return "closesthit"; - case ShaderType::Miss: - return "miss"; - case ShaderType::Callable: - return "callable"; - default: - FALCOR_UNREACHABLE(); - return ""; + if (m_visible) + for (const auto& child : m_children) + child->render(); } -} +protected: + Widget* m_parent; + std::vector> m_children; + bool m_visible{true}; + bool m_enabled{true}; +}; + +/// This is the main widget that represents the screen. +/// It is intended to be used as the parent for \c Window widgets. +class Screen : public Widget +{ + FALCOR_OBJECT(Screen) +public: + Screen() : Widget(nullptr) {} + + virtual void render() override { Widget::render(); } +}; + +// The widgets are implemented in the C++ file as they are only exposed to Python. + +} // namespace python_ui } // namespace Falcor diff --git a/Source/Falcor/Utils/UI/SpectrumUI.cpp b/Source/Falcor/Utils/UI/SpectrumUI.cpp index d8b341089..ca6b9b0a3 100644 --- a/Source/Falcor/Utils/UI/SpectrumUI.cpp +++ b/Source/Falcor/Utils/UI/SpectrumUI.cpp @@ -27,7 +27,7 @@ **************************************************************************/ #include "SpectrumUI.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/Color/SpectrumUtils.h" #include "Utils/Color/ColorHelpers.slang" @@ -140,8 +140,10 @@ void SpectrumUI::drawLine( ) { drawList->AddLine( - ImVec2(canvasPos.x + point0.x, canvasPos.y + point0.y), ImVec2(canvasPos.x + point1.x, canvasPos.y + point1.y), - ImColor(color.x, color.y, color.z, color.w), lineWidth + ImVec2(canvasPos.x + point0.x, canvasPos.y + point0.y), + ImVec2(canvasPos.x + point1.x, canvasPos.y + point1.y), + ImColor(color.x, color.y, color.z, color.w), + lineWidth ); } @@ -338,8 +340,12 @@ void SpectrumUI::drawTextSpectralIntensityAndTicks( ) { const float halfTickSize = 4.0f; - auto renderTextAndTick = [&](ImDrawList* drawList, const float spectralIntensity, const float2& canvasPos, const float2& xAxisRange, - const float2& yAxisRange, const bool first = false) + auto renderTextAndTick = [&](ImDrawList* drawList, + const float spectralIntensity, + const float2& canvasPos, + const float2& xAxisRange, + const float2& yAxisRange, + const bool first = false) { const auto str = fmt::format("{:1.1f}", spectralIntensity); float y = toYCoord(spectralIntensity, yAxisRange); @@ -600,7 +606,11 @@ bool SpectrumUI::render(Gui::Widgets& w, const std::string name, std::vector< if (spectra.size() * numComponents > 1) { changed |= guiGroup.var( - makeUnique("Index to editable curve").c_str(), mEditSpectrumIndex, 0u, uint32_t(spectra.size() * numComponents - 1), 1u, + makeUnique("Index to editable curve").c_str(), + mEditSpectrumIndex, + 0u, + uint32_t(spectra.size() * numComponents - 1), + 1u, true ); } @@ -650,7 +660,9 @@ bool SpectrumUI::render(Gui::Widgets& w, const std::string name, std::vector< // This is the main drawing area of the spectrum visualization. ImGui::BeginChild( makeUnique("Spectrum visualization").c_str(), - ImVec2(ImGui::GetWindowContentRegionWidth() - strSize.x * 3 / 4, float(mDrawAreaHeight)), false, ImGuiWindowFlags_NoScrollWithMouse + ImVec2(ImGui::GetWindowContentRegionWidth() - strSize.x * 3 / 4, float(mDrawAreaHeight)), + false, + ImGuiWindowFlags_NoScrollWithMouse ); { // ImDrawList API uses screen coordinates. diff --git a/Source/Falcor/Utils/UI/TextRenderer.cpp b/Source/Falcor/Utils/UI/TextRenderer.cpp index 361707df7..592f8c152 100644 --- a/Source/Falcor/Utils/UI/TextRenderer.cpp +++ b/Source/Falcor/Utils/UI/TextRenderer.cpp @@ -41,9 +41,13 @@ struct Vertex }; const float2 kVertexPos[] = { - float2(0, 0), float2(0, 1), float2(1, 0), + float2(0, 0), + float2(0, 1), + float2(1, 0), - float2(1, 0), float2(0, 1), float2(1, 1), + float2(1, 0), + float2(0, 1), + float2(1, 1), }; const uint32_t kMaxCharCount = 1000; @@ -64,14 +68,17 @@ ref createVAO(const ref& pVB) TextRenderer::TextRenderer(ref pDevice) : mpDevice(pDevice) { - // Create a vertex buffer - const uint32_t vbSize = (uint32_t)(sizeof(Vertex) * kMaxCharCount * std::size(kVertexPos)); - mpVb = Buffer::create(mpDevice, vbSize, Buffer::BindFlags::Vertex, Buffer::CpuAccess::Write, nullptr); + for (uint32_t i = 0; i < kVaoCount; ++i) + { + // Create a vertex buffer + const uint32_t vbSize = (uint32_t)(sizeof(Vertex) * kMaxCharCount * std::size(kVertexPos)); + ref pVb = mpDevice->createBuffer(vbSize, ResourceBindFlags::Vertex, MemoryType::Upload, nullptr); + mpVaos[i] = createVAO(pVb); + } // Create the RenderState mpPass = RasterPass::create(mpDevice, "Utils/UI/TextRenderer.3d.slang", "vsMain", "psMain"); auto& pState = mpPass->getState(); - pState->setVao(createVAO(mpVb)); // create the depth-state DepthStencilState::Desc dsDesc; @@ -86,8 +93,13 @@ TextRenderer::TextRenderer(ref pDevice) : mpDevice(pDevice) // Blend state BlendState::Desc blendDesc; blendDesc.setRtBlend(0, true).setRtParams( - 0, BlendState::BlendOp::Add, BlendState::BlendOp::Add, BlendState::BlendFunc::SrcAlpha, BlendState::BlendFunc::OneMinusSrcAlpha, - BlendState::BlendFunc::One, BlendState::BlendFunc::One + 0, + BlendState::BlendOp::Add, + BlendState::BlendOp::Add, + BlendState::BlendFunc::SrcAlpha, + BlendState::BlendFunc::OneMinusSrcAlpha, + BlendState::BlendFunc::One, + BlendState::BlendFunc::One ); pState->setBlendState(BlendState::create(blendDesc)); mpFont = std::make_unique(mpDevice, getRuntimeDirectory() / "data/framework/fonts/dejavu-sans-mono-14"); @@ -136,7 +148,9 @@ void TextRenderer::renderText(RenderContext* pRenderContext, const std::string& // Make sure we enough space for the next char FALCOR_ASSERT(text.size() < kMaxCharCount); setCbData(pDstFbo); - Vertex* verts = (Vertex*)mpVb->map(Buffer::MapType::WriteDiscard); + const auto& pVao = mpVaos[mVaoIndex]; + const auto& pVb = pVao->getVertexBuffer(0); + Vertex* verts = reinterpret_cast(pVb->map(Buffer::MapType::Write)); float startX = pos.x; uint32_t vertexCount = 0; // Not the same as text.size(), since some special characters are ignored @@ -170,9 +184,12 @@ void TextRenderer::renderText(RenderContext* pRenderContext, const std::string& } // Submit - mpVb->unmap(); + pVb->unmap(); + mpPass->getState()->setVao(pVao); mpPass->getState()->setFbo(pDstFbo); mpPass->draw(pRenderContext, vertexCount, 0); + + mVaoIndex = (mVaoIndex + 1) % kVaoCount; } } // namespace Falcor diff --git a/Source/Falcor/Utils/UI/TextRenderer.h b/Source/Falcor/Utils/UI/TextRenderer.h index cd8a95158..2d15319d6 100644 --- a/Source/Falcor/Utils/UI/TextRenderer.h +++ b/Source/Falcor/Utils/UI/TextRenderer.h @@ -87,10 +87,15 @@ class FALCOR_API TextRenderer void setCbData(const ref& pDstFbo); void renderText(RenderContext* pRenderContext, const std::string& text, const ref& pDstFbo, float2 pos); + // Use 3 rotating VAOs to avoid stalling the GPU. + // A better way would be to upload the data using an upload heap. + static constexpr uint32_t kVaoCount = 3; + ref mpDevice; Flags mFlags = Flags::Shadowed; float3 mColor = float3(1.f); - ref mpVb; + ref mpVaos[kVaoCount]; + uint32_t mVaoIndex = 0; ref mpPass; std::unique_ptr mpFont; }; diff --git a/Source/Falcor/Utils/fast_vector.h b/Source/Falcor/Utils/fast_vector.h new file mode 100644 index 000000000..85b38bb78 --- /dev/null +++ b/Source/Falcor/Utils/fast_vector.h @@ -0,0 +1,140 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include +#include +#include + +namespace Falcor +{ +/// This is a very rudimentary drop in replacement for STL vector that, +/// unlike MSVC STL vector does not have a global lock on every push_back, +/// which makes Debug code significantly slower when running multithreaded +template +class fast_vector +{ +public: + static_assert(std::is_trivial_v, "Fast vector can only be used on trivial types."); + + fast_vector() = default; + fast_vector(const std::vector& other) { assign(other.begin(), other.end()); } + + fast_vector(const fast_vector& other) { assign(other.begin(), other.end()); } + + fast_vector(fast_vector&& other) + : m_capacity(std::exchange(other.m_capacity, 0)), m_size(std::exchange(other.m_size, 0)), m_data(std::move(other.m_data)) + {} + + fast_vector& operator=(const std::vector& other) + { + assign(other.begin(), other.end()); + return *this; + } + + fast_vector& operator=(const fast_vector& other) + { + assign(other.begin(), other.end()); + return *this; + } + + fast_vector& operator=(fast_vector&& other) + { + m_size = std::exchange(other.m_size, 0); + m_capacity = std::exchange(other.m_capacity, 0); + m_data = std::move(other.m_data); + return *this; + } + + void reserve(size_t capacity) { grow(capacity); } + + void resize(size_t size) + { + grow(size); + m_size = size; + } + + void resize(size_t capacity, const T& value) + { + grow(capacity); + for (size_t i = m_size; i < capacity; ++i) + m_data[i] = value; + m_size = capacity; + } + + void push_back(const T& v) + { + if (m_size + 1 > m_capacity) + grow(m_size + 1); + m_data[m_size++] = v; + } + + template + void assign(FwdIterator b, FwdIterator e) + { + resize(std::distance(b, e)); + for (size_t i = 0; i < size(); ++i, ++b) + m_data[i] = *b; + } + + operator std::vector() { return std::vector(begin(), end()); } + + inline size_t size() const { return m_size; } + inline size_t capacity() const { return m_capacity; } + inline bool empty() const { return m_size == 0; } + + inline void clear() { m_size = 0; } + + inline T* begin() { return data(); } + inline T* end() { return begin() + size(); } + inline const T* begin() const { return data(); } + inline const T* end() const { return begin() + size(); } + + inline T* data() { return m_data.get(); } + inline const T* data() const { return m_data.get(); } + + inline T& operator[](size_t pos) { return m_data[pos]; } + inline const T& operator[](size_t pos) const { return m_data[pos]; } + +private: + void grow(size_t required_size) + { + if (required_size <= m_capacity) + return; + size_t new_size = std::max(m_capacity * 2, required_size); + std::unique_ptr new_data = std::make_unique(new_size); + memcpy(new_data.get(), m_data.get(), m_size * sizeof(T)); + m_data = std::move(new_data); + m_capacity = new_size; + } + +private: + size_t m_capacity{0}; + size_t m_size{0}; + std::unique_ptr m_data; +}; +} // namespace Falcor diff --git a/Source/Modules/CMakeLists.txt b/Source/Modules/CMakeLists.txt new file mode 100644 index 000000000..26a66afcd --- /dev/null +++ b/Source/Modules/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(USDUtils) diff --git a/Source/Modules/USDUtils/CMakeLists.txt b/Source/Modules/USDUtils/CMakeLists.txt new file mode 100644 index 000000000..eb17dc65b --- /dev/null +++ b/Source/Modules/USDUtils/CMakeLists.txt @@ -0,0 +1,43 @@ +add_library(USDUtils STATIC) + +target_sources(USDUtils PRIVATE + PreviewSurfaceConverter/CreateSpecularTexture.cs.slang + PreviewSurfaceConverter/CreateSpecularTransmissionTexture.cs.slang + PreviewSurfaceConverter/PackBaseColorAlpha.cs.slang + PreviewSurfaceConverter/PreviewSurfaceConverter.cpp + PreviewSurfaceConverter/PreviewSurfaceConverter.h + PreviewSurfaceConverter/SampleTexture.slang + PreviewSurfaceConverter/StandardMaterialSpec.h + + Tessellator/IndexedVector.h + Tessellator/Tessellation.cpp + Tessellator/Tessellation.h + + USDHelpers.h + USDScene1Utils.cpp + USDScene1Utils.h + USDUtils.h + USDUtils.cpp +) + +target_link_libraries(USDUtils + PUBLIC + nv-usd Falcor opensubdiv +) + +target_include_directories(USDUtils + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +set_target_properties(USDUtils + PROPERTIES + POSITION_INDEPENDENT_CODE ON + LIBRARY_OUTPUT_DIRECTORY ${FALCOR_RUNTIME_OUTPUT_DIRECTORY} +) + +target_copy_shaders(USDUtils Modules/USDUtils) + +target_source_group(USDUtils "Modules/") + +validate_headers(USDUtils) diff --git a/Source/plugins/importers/USDImporter/CreateSpecularTexture.cs.slang b/Source/Modules/USDUtils/PreviewSurfaceConverter/CreateSpecularTexture.cs.slang similarity index 94% rename from Source/plugins/importers/USDImporter/CreateSpecularTexture.cs.slang rename to Source/Modules/USDUtils/PreviewSurfaceConverter/CreateSpecularTexture.cs.slang index c0976ea98..16282022a 100644 --- a/Source/plugins/importers/USDImporter/CreateSpecularTexture.cs.slang +++ b/Source/Modules/USDUtils/PreviewSurfaceConverter/CreateSpecularTexture.cs.slang @@ -33,7 +33,8 @@ SamplerState sampler; Texture2D metallicTexture; Texture2D roughnessTexture; -cbuffer CB { +cbuffer CB +{ int roughnessChannel; float roughnessValue; int metallicChannel; @@ -41,10 +42,11 @@ cbuffer CB { uint2 outDim; }; -[numthreads(16,16,1)] -void createSpecularTexture(uint3 threadId : SV_DispatchThreadID) +[numthreads(16, 16, 1)] +void createSpecularTexture(uint3 threadId: SV_DispatchThreadID) { - if (any(threadId.xy > outDim)) return; + if (any(threadId.xy > outDim)) + return; float metallic; if (metallicChannel < 0) diff --git a/Source/plugins/importers/USDImporter/CreateSpecularTransmissionTexture.cs.slang b/Source/Modules/USDUtils/PreviewSurfaceConverter/CreateSpecularTransmissionTexture.cs.slang similarity index 92% rename from Source/plugins/importers/USDImporter/CreateSpecularTransmissionTexture.cs.slang rename to Source/Modules/USDUtils/PreviewSurfaceConverter/CreateSpecularTransmissionTexture.cs.slang index 6d7cb6139..63606e9b1 100644 --- a/Source/plugins/importers/USDImporter/CreateSpecularTransmissionTexture.cs.slang +++ b/Source/Modules/USDUtils/PreviewSurfaceConverter/CreateSpecularTransmissionTexture.cs.slang @@ -29,15 +29,17 @@ RWTexture2D outputTexture; Texture2D opacityTexture; -cbuffer CB { +cbuffer CB +{ int opacityChannel; uint2 outDim; }; -[numthreads(16,16,1)] -void createSpecularTransmissionTexture(uint3 threadId : SV_DispatchThreadID) +[numthreads(16, 16, 1)] +void createSpecularTransmissionTexture(uint3 threadId: SV_DispatchThreadID) { - if (any(threadId.xy > outDim)) return; + if (any(threadId.xy > outDim)) + return; float oVal = opacityTexture[threadId.xy][opacityChannel]; float val = saturate(1.f - oVal); diff --git a/Source/plugins/importers/USDImporter/PackBaseColorAlpha.cs.slang b/Source/Modules/USDUtils/PreviewSurfaceConverter/PackBaseColorAlpha.cs.slang similarity index 94% rename from Source/plugins/importers/USDImporter/PackBaseColorAlpha.cs.slang rename to Source/Modules/USDUtils/PreviewSurfaceConverter/PackBaseColorAlpha.cs.slang index 347b9e004..81871aa62 100644 --- a/Source/plugins/importers/USDImporter/PackBaseColorAlpha.cs.slang +++ b/Source/Modules/USDUtils/PreviewSurfaceConverter/PackBaseColorAlpha.cs.slang @@ -33,7 +33,8 @@ SamplerState sampler; Texture2D baseColorTexture; Texture2D opacityTexture; -cbuffer CB { +cbuffer CB +{ int opacityChannel; float opacityValue; int baseColorTextureValid; @@ -41,10 +42,11 @@ cbuffer CB { uint2 outDim; }; -[numthreads(16,16,1)] -void packBaseColorAlpha(uint3 threadId : SV_DispatchThreadID) +[numthreads(16, 16, 1)] +void packBaseColorAlpha(uint3 threadId: SV_DispatchThreadID) { - if (any(threadId.xy > outDim)) return; + if (any(threadId.xy > outDim)) + return; float opacity; if (opacityChannel < 0) diff --git a/Source/plugins/importers/USDImporter/PreviewSurfaceConverter.cpp b/Source/Modules/USDUtils/PreviewSurfaceConverter/PreviewSurfaceConverter.cpp similarity index 88% rename from Source/plugins/importers/USDImporter/PreviewSurfaceConverter.cpp rename to Source/Modules/USDUtils/PreviewSurfaceConverter/PreviewSurfaceConverter.cpp index d4eaf2b49..0b5f43f0f 100644 --- a/Source/plugins/importers/USDImporter/PreviewSurfaceConverter.cpp +++ b/Source/Modules/USDUtils/PreviewSurfaceConverter/PreviewSurfaceConverter.cpp @@ -26,11 +26,12 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "PreviewSurfaceConverter.h" -#include "Utils.h" -#include "USDHelpers.h" #include "Core/API/RenderContext.h" #include "Utils/Image/ImageIO.h" +#include "USDUtils/USDUtils.h" +#include "USDUtils/USDHelpers.h" + BEGIN_DISABLE_USD_WARNINGS #include #include @@ -44,13 +45,13 @@ namespace Falcor { namespace { -const char kSpecTransShaderFile[]("plugins/importers/USDImporter/CreateSpecularTransmissionTexture.cs.slang"); +const char kSpecTransShaderFile[]("Modules/USDUtils/PreviewSurfaceConverter/CreateSpecularTransmissionTexture.cs.slang"); const char kSpecTransShaderEntry[]("createSpecularTransmissionTexture"); -const char kPackAlphaShaderFile[]("plugins/importers/USDImporter/PackBaseColorAlpha.cs.slang"); +const char kPackAlphaShaderFile[]("Modules/USDUtils/PreviewSurfaceConverter/PackBaseColorAlpha.cs.slang"); const char kPackAlphaShaderEntry[]("packBaseColorAlpha"); -const char kSpecularShaderFile[]("plugins/importers/USDImporter/CreateSpecularTexture.cs.slang"); +const char kSpecularShaderFile[]("Modules/USDUtils/PreviewSurfaceConverter/CreateSpecularTexture.cs.slang"); const char kSpecularShaderEntry[]("createSpecularTexture"); inline int32_t getChannelIndex(TextureChannelFlags flags) @@ -74,12 +75,7 @@ inline int32_t getChannelIndex(TextureChannelFlags flags) } // Return the ultimate source of the given input. -UsdShadeInput getSourceInput( - UsdShadeInput input, - UsdShadeConnectableAPI& source, - TfToken& sourceName, - UsdShadeAttributeType& sourceType -) +UsdShadeInput getSourceInput(UsdShadeInput input, UsdShadeConnectableAPI& source, TfToken& sourceName, UsdShadeAttributeType& sourceType) { if (input && input.HasConnectedSource()) { @@ -91,10 +87,7 @@ UsdShadeInput getSourceInput( // If there's a connected source of type asset, return it. for (uint32_t i = 0; i < inputs.size(); ++i) { - logDebug( - "Input '{}' has source '{}'.", input.GetBaseName().GetString(), - inputs[i].GetBaseName().GetString() - ); + logDebug("Input '{}' has source '{}'.", input.GetBaseName().GetString(), inputs[i].GetBaseName().GetString()); SdfValueTypeName typeName(inputs[i].GetTypeName()); if (typeName == SdfValueTypeNames->Asset) { @@ -197,9 +190,7 @@ void PreviewSurfaceConverter::cacheMaterial(const UsdShadeShader& shader, ref pDevice) - : mpDevice(pDevice) +PreviewSurfaceConverter::PreviewSurfaceConverter(ref pDevice) : mpDevice(pDevice) { mpSpecTransPass = ComputePass::create(mpDevice, kSpecTransShaderFile, kSpecTransShaderEntry); @@ -543,12 +526,10 @@ PreviewSurfaceConverter::PreviewSurfaceConverter(ref pDevice) mpSpecularPass = ComputePass::create(mpDevice, kSpecularShaderFile, kSpecularShaderEntry); Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); - samplerDesc.setAddressingMode( - Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp - ); + samplerDesc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Point); + samplerDesc.setAddressingMode(TextureAddressingMode::Clamp, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); - mpSampler = Sampler::create(mpDevice, samplerDesc); + mpSampler = mpDevice->createSampler(samplerDesc); } // Convert textured opacity to textured specular transparency. @@ -572,9 +553,14 @@ ref PreviewSurfaceConverter::createSpecularTransmissionTexture( } uint2 resolution(opacityTexture->getWidth(), opacityTexture->getHeight()); - ref pTexture = Texture::create2D( - mpDevice, resolution.x, resolution.y, ResourceFormat::RGBA8Unorm, 1, Texture::kMaxPossible, nullptr, - Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget + ref pTexture = mpDevice->createTexture2D( + resolution.x, + resolution.y, + ResourceFormat::RGBA8Unorm, + 1, + Texture::kMaxPossible, + nullptr, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::RenderTarget ); auto var = mpSpecTransPass->getRootVar(); @@ -611,19 +597,22 @@ ref PreviewSurfaceConverter::packBaseColorAlpha( } // Set output resolution to the maxium of the input dimensions - uint32_t width = std::max( - (opacityTexture ? opacityTexture->getWidth() : 0), (baseColorTexture ? baseColorTexture->getWidth() : 0) - ); - uint32_t height = std::max( - (opacityTexture ? opacityTexture->getHeight() : 0), (baseColorTexture ? baseColorTexture->getHeight() : 0) - ); + uint32_t width = + std::max((opacityTexture ? opacityTexture->getWidth() : 0), (baseColorTexture ? baseColorTexture->getWidth() : 0)); + uint32_t height = + std::max((opacityTexture ? opacityTexture->getHeight() : 0), (baseColorTexture ? baseColorTexture->getHeight() : 0)); uint2 resolution(width, height); // sRGB format textures can't be bound as UAVs. Render to an RGBA32Float intermediate texture, and then blit to // RGBA8UnormSrgb to preserve precision near zero. - ref pTexture = Texture::create2D( - mpDevice, resolution.x, resolution.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, - Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess + ref pTexture = mpDevice->createTexture2D( + resolution.x, + resolution.y, + ResourceFormat::RGBA32Float, + 1, + 1, + nullptr, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess ); auto var = mpPackAlphaPass->getRootVar(); @@ -640,9 +629,14 @@ ref PreviewSurfaceConverter::packBaseColorAlpha( mpPackAlphaPass->execute(pRenderContext, resolution.x, resolution.y); // Create the output mipmapped sRGB texture - ref pFinal = Texture::create2D( - mpDevice, resolution.x, resolution.y, ResourceFormat::RGBA8UnormSrgb, 1, Texture::kMaxPossible, nullptr, - Resource::BindFlags::ShaderResource | Resource::BindFlags::RenderTarget + ref pFinal = mpDevice->createTexture2D( + resolution.x, + resolution.y, + ResourceFormat::RGBA8UnormSrgb, + 1, + Texture::kMaxPossible, + nullptr, + ResourceBindFlags::ShaderResource | ResourceBindFlags::RenderTarget ); // Blit the intermediate texture to the output texture to perform format conversion @@ -677,17 +671,20 @@ ref PreviewSurfaceConverter::createSpecularTexture( } // Set output resolution to the maxium of the input dimensions - uint32_t width = std::max( - (roughnessTexture ? roughnessTexture->getWidth() : 0), (metallicTexture ? metallicTexture->getWidth() : 0) - ); - uint32_t height = std::max( - (roughnessTexture ? roughnessTexture->getHeight() : 0), (metallicTexture ? metallicTexture->getHeight() : 0) - ); + uint32_t width = + std::max((roughnessTexture ? roughnessTexture->getWidth() : 0), (metallicTexture ? metallicTexture->getWidth() : 0)); + uint32_t height = + std::max((roughnessTexture ? roughnessTexture->getHeight() : 0), (metallicTexture ? metallicTexture->getHeight() : 0)); uint2 resolution(width, height); - ref pTexture = Texture::create2D( - mpDevice, width, height, ResourceFormat::RGBA8Unorm, 1, Texture::kMaxPossible, nullptr, - Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget + ref pTexture = mpDevice->createTexture2D( + width, + height, + ResourceFormat::RGBA8Unorm, + 1, + Texture::kMaxPossible, + nullptr, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::RenderTarget ); auto var = mpSpecularPass->getRootVar(); @@ -736,8 +733,8 @@ ref PreviewSurfaceConverter::loadTexture(const StandardMaterialSpec::Co } { std::scoped_lock lock(mMutex); - return Texture::create2D( - mpDevice, pBitmap->getWidth(), pBitmap->getHeight(), format, 1, Texture::kMaxPossible, pBitmap->getData() + return mpDevice->createTexture2D( + pBitmap->getWidth(), pBitmap->getHeight(), format, 1, Texture::kMaxPossible, pBitmap->getData() ); } } @@ -753,9 +750,7 @@ StandardMaterialSpec PreviewSurfaceConverter::createSpec(const std::string& name for (auto& curInput : shader.GetInputs()) { - logDebug( - "UsdPreviewSurface '{}' has input: {}", shader.GetPath().GetString(), curInput.GetBaseName().GetString() - ); + logDebug("UsdPreviewSurface '{}' has input: {}", shader.GetPath().GetString(), curInput.GetBaseName().GetString()); // Convert the input name to lowercase to allow for mixed capitalization std::string inputName = curInput.GetBaseName(); @@ -769,8 +764,7 @@ StandardMaterialSpec PreviewSurfaceConverter::createSpec(const std::string& name SdfValueTypeName typeName(input.GetTypeName()); logDebug( - "UsdPreviewSurface '{}' input '{}' has type '{}'.", shader.GetPath().GetString(), inputName, - typeName.GetAsToken().GetString() + "UsdPreviewSurface '{}' input '{}' has type '{}'.", shader.GetPath().GetString(), inputName, typeName.GetAsToken().GetString() ); if (inputName == "diffusecolor") @@ -908,11 +902,7 @@ StandardMaterialSpec PreviewSurfaceConverter::createSpec(const std::string& name return spec; } -ref PreviewSurfaceConverter::convert( - const UsdShadeMaterial& material, - const std::string& primName, - RenderContext* pRenderContext -) +ref PreviewSurfaceConverter::convert(const UsdShadeMaterial& material, const std::string& primName, RenderContext* pRenderContext) { TfToken renderContext(""); // Blank implies universal context. Use e.g. "falcor" for a renderer- or // material-specific context. @@ -950,9 +940,7 @@ ref PreviewSurfaceConverter::convert( TfToken id = getAttribute(shader.GetPrim().GetAttribute(UsdShadeTokens->infoId), TfToken()); if (id != TfToken("UsdPreviewSurface")) { - logDebug( - "Material '{}' has a surface output node of type '{}', not UsdPreviewSurface.", primName, id.GetString() - ); + logDebug("Material '{}' has a surface output node of type '{}', not UsdPreviewSurface.", primName, id.GetString()); return nullptr; } @@ -1005,8 +993,7 @@ ref PreviewSurfaceConverter::convert( // If there is either a roughness or metallic texture, convert texture(s) and constant (if any) to an ORM texture. if (metallicTexture || roughnessTexture) { - ref pSpecularTex = - createSpecularTexture(spec.roughness, roughnessTexture, spec.metallic, metallicTexture, pRenderContext); + ref pSpecularTex = createSpecularTexture(spec.roughness, roughnessTexture, spec.metallic, metallicTexture, pRenderContext); pMaterial->setSpecularTexture(pSpecularTex); } else @@ -1023,8 +1010,7 @@ ref PreviewSurfaceConverter::convert( // Pack opacity into the alpha channel if (baseColorTexture || opacityTexture) { - baseColorTexture = - packBaseColorAlpha(spec.baseColor, baseColorTexture, spec.opacity, opacityTexture, pRenderContext); + baseColorTexture = packBaseColorAlpha(spec.baseColor, baseColorTexture, spec.opacity, opacityTexture, pRenderContext); } else { @@ -1041,16 +1027,14 @@ ref PreviewSurfaceConverter::convert( "UsdPreviewSurface '{}' has texture-mapped opacity. Converting to textured specular transmission.", shader.GetPath().GetString() ); - ref transmissionTexture = - createSpecularTransmissionTexture(spec.opacity, opacityTexture, pRenderContext); + ref transmissionTexture = createSpecularTransmissionTexture(spec.opacity, opacityTexture, pRenderContext); pMaterial->setTransmissionTexture(transmissionTexture); pMaterial->setSpecularTransmission(1.f); } else { logDebug( - "UsdPreviewSurface '{}' has uniform opacity. Converting to uniform specular transmission.", - shader.GetPath().GetString() + "UsdPreviewSurface '{}' has uniform opacity. Converting to uniform specular transmission.", shader.GetPath().GetString() ); pMaterial->setSpecularTransmission(1.f - spec.opacity.uniformValue.r); } @@ -1084,8 +1068,7 @@ ref PreviewSurfaceConverter::convert( if (any(spec.emission.textureScale != float4(1.f, 1.f, 1.f, 1.f))) { - if (spec.emission.textureScale.x != spec.emission.textureScale.y || - spec.emission.textureScale.x != spec.emission.textureScale.z || + if (spec.emission.textureScale.x != spec.emission.textureScale.y || spec.emission.textureScale.x != spec.emission.textureScale.z || spec.emission.textureScale.x != spec.emission.textureScale.a) { logWarning( @@ -1107,9 +1090,7 @@ ref PreviewSurfaceConverter::convert( // Compute the inverse of the texture transform, as Falcor assumes // the transform applies to the texture, rather than the texture coordinates, // as UsdPreviewSurface does. - FALCOR_ASSERT( - spec.texTransform.getCompositionOrder() == Transform::CompositionOrder::ScaleRotateTranslate - ); + FALCOR_ASSERT(spec.texTransform.getCompositionOrder() == Transform::CompositionOrder::ScaleRotateTranslate); Transform inv; inv.setTranslation(-spec.texTransform.getTranslation()); float3 scale = spec.texTransform.getScaling(); diff --git a/Source/plugins/importers/USDImporter/PreviewSurfaceConverter.h b/Source/Modules/USDUtils/PreviewSurfaceConverter/PreviewSurfaceConverter.h similarity index 92% rename from Source/plugins/importers/USDImporter/PreviewSurfaceConverter.h rename to Source/Modules/USDUtils/PreviewSurfaceConverter/PreviewSurfaceConverter.h index 96ad085af..58f7df26a 100644 --- a/Source/plugins/importers/USDImporter/PreviewSurfaceConverter.h +++ b/Source/Modules/USDUtils/PreviewSurfaceConverter/PreviewSurfaceConverter.h @@ -26,8 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #pragma once -#include "Utils.h" -#include "USDHelpers.h" #include "Core/API/Texture.h" #include "Core/API/Sampler.h" #include "Core/Pass/ComputePass.h" @@ -35,6 +33,9 @@ #include "Scene/Material/StandardMaterial.h" #include "StandardMaterialSpec.h" +#include "USDUtils/USDUtils.h" +#include "USDUtils/USDHelpers.h" + BEGIN_DISABLE_USD_WARNINGS #include #include @@ -64,11 +65,7 @@ class PreviewSurfaceConverter * \param primName Name of the primitive to which the material is bound (for warning message reporting only). * \return A Falcor material instance representing the UsdPreviewSurface specified in the given material, if any, nullptr otherwise. */ - ref convert( - const pxr::UsdShadeMaterial& material, - const std::string& primName, - RenderContext* pRenderContext - ); + ref convert(const pxr::UsdShadeMaterial& material, const std::string& primName, RenderContext* pRenderContext); private: StandardMaterialSpec createSpec(const std::string& name, const UsdShadeShader& shader) const; @@ -130,9 +127,9 @@ class PreviewSurfaceConverter ///< conversion. std::unordered_map, SpecHash> mSpecMaterialCache; - std::mutex mMutex; ///< Mutex to ensure serial invocation of calls that are not thread safe (e.g., texture creation - ///< and compute program execution). - std::mutex mCacheMutex; ///< Mutex controlling access to the material caches. + std::mutex mMutex; ///< Mutex to ensure serial invocation of calls that are not thread safe (e.g., texture creation + ///< and compute program execution). + std::mutex mCacheMutex; ///< Mutex controlling access to the material caches. std::condition_variable mPrimCacheUpdated; ///< Condition variable for threads waiting on by-prim cache update. std::condition_variable mSpecCacheUpdated; ///< Condition variable for threads waiting on by-spec cache update. }; diff --git a/Source/plugins/importers/USDImporter/SampleTexture.slang b/Source/Modules/USDUtils/PreviewSurfaceConverter/SampleTexture.slang similarity index 100% rename from Source/plugins/importers/USDImporter/SampleTexture.slang rename to Source/Modules/USDUtils/PreviewSurfaceConverter/SampleTexture.slang diff --git a/Source/plugins/importers/USDImporter/StandardMaterialSpec.h b/Source/Modules/USDUtils/PreviewSurfaceConverter/StandardMaterialSpec.h similarity index 82% rename from Source/plugins/importers/USDImporter/StandardMaterialSpec.h rename to Source/Modules/USDUtils/PreviewSurfaceConverter/StandardMaterialSpec.h index 3ad6dc2d8..ec0a5ac30 100644 --- a/Source/plugins/importers/USDImporter/StandardMaterialSpec.h +++ b/Source/Modules/USDUtils/PreviewSurfaceConverter/StandardMaterialSpec.h @@ -66,10 +66,7 @@ struct StandardMaterialSpec float2 translate = float2(0.f, 0.f); float rotate = 0.f; - bool isIdentity() const - { - return rotate == 0.f && scale.x == 1.f && scale.y && 1.f && translate.x == 0.f && translate.y == 0.f; - } + bool isIdentity() const { return rotate == 0.f && scale.x == 1.f && scale.y && 1.f && translate.x == 0.f && translate.y == 0.f; } bool operator==(const TextureTransform& other) const { @@ -96,14 +93,14 @@ struct StandardMaterialSpec channels == o.channels && all(uniformValue == o.uniformValue) && loadSRGB == o.loadSRGB; } - float4 uniformValue = float4(0.f, 0.f, 0.f, 0.f); ///< Uniform value, may only hold a single valid component for - ///< a float, and so on. - std::string texturePath; ///< Path to texture file. - Falcor::Transform texTransform; ///< 2D transformation applied to texture coordinates - float4 textureScale = float4(1.f, 1.f, 1.f, 1.f); ///< Texture value scale; valid only for emissive component. - ///< Alpha may be specified, but is ignored. + float4 uniformValue = float4(0.f, 0.f, 0.f, 0.f); ///< Uniform value, may only hold a single valid component for + ///< a float, and so on. + std::string texturePath; ///< Path to texture file. + Falcor::Transform texTransform; ///< 2D transformation applied to texture coordinates + float4 textureScale = float4(1.f, 1.f, 1.f, 1.f); ///< Texture value scale; valid only for emissive component. + ///< Alpha may be specified, but is ignored. TextureChannelFlags channels = TextureChannelFlags::None; ///< Texture channels that hold the data. - bool loadSRGB = false; ///< If true, texture should be assumed to hold sRGB data. + bool loadSRGB = false; ///< If true, texture should be assumed to hold sRGB data. }; StandardMaterialSpec() {} @@ -121,8 +118,7 @@ struct StandardMaterialSpec if (!input.isTextured()) return; const Falcor::Transform& newTransform = input.texTransform; - if (newTransform.getMatrix() != Falcor::float4x4::identity() && - texTransform.getMatrix() == Falcor::float4x4::identity()) + if (newTransform.getMatrix() != Falcor::float4x4::identity() && texTransform.getMatrix() == Falcor::float4x4::identity()) { texTransform = newTransform; } @@ -138,9 +134,9 @@ struct StandardMaterialSpec bool operator==(const StandardMaterialSpec& o) const { - return texTransform == o.texTransform && baseColor == o.baseColor && normal == o.normal && - metallic == o.metallic && roughness == o.roughness && opacity == o.opacity && emission == o.emission && - disp == o.disp && opacityThreshold == o.opacityThreshold && ior == o.ior; + return texTransform == o.texTransform && baseColor == o.baseColor && normal == o.normal && metallic == o.metallic && + roughness == o.roughness && opacity == o.opacity && emission == o.emission && disp == o.disp && + opacityThreshold == o.opacityThreshold && ior == o.ior; } std::string name; @@ -166,8 +162,7 @@ class SpecHash { size_t hash = 0; hash_combine( - hash, o.texTransform, o.baseColor, o.normal, o.metallic, o.roughness, o.opacity, o.emission, o.disp, - o.opacityThreshold, o.ior + hash, o.texTransform, o.baseColor, o.normal, o.metallic, o.roughness, o.opacity, o.emission, o.disp, o.opacityThreshold, o.ior ); return hash; } @@ -195,9 +190,7 @@ struct hash size_t operator()(const Falcor::Transform& t) const { size_t hash = 0; - Falcor::hash_combine( - hash, t.getTranslation(), t.getScaling(), t.getRotation(), static_cast(t.getCompositionOrder()) - ); + Falcor::hash_combine(hash, t.getTranslation(), t.getScaling(), t.getRotation(), static_cast(t.getCompositionOrder())); return hash; } }; @@ -209,8 +202,7 @@ struct hash { size_t hash = 0; Falcor::hash_combine( - hash, i.uniformValue, i.texturePath, i.texTransform, i.textureScale, static_cast(i.channels), - i.loadSRGB + hash, i.uniformValue, i.texturePath, i.texTransform, i.textureScale, static_cast(i.channels), i.loadSRGB ); return hash; } diff --git a/Source/plugins/importers/USDImporter/IndexedVector.h b/Source/Modules/USDUtils/Tessellator/IndexedVector.h similarity index 95% rename from Source/plugins/importers/USDImporter/IndexedVector.h rename to Source/Modules/USDUtils/Tessellator/IndexedVector.h index d0968f4fc..8f77941ac 100644 --- a/Source/plugins/importers/USDImporter/IndexedVector.h +++ b/Source/Modules/USDUtils/Tessellator/IndexedVector.h @@ -40,7 +40,7 @@ namespace Falcor * @tparam I Index value type * @tparam H Hash object on type T, used to determine data item equivalence */ -template +template> class IndexedVector { public: @@ -90,8 +90,8 @@ class IndexedVector const pxr::VtArray& getIndices() const { return mIndices; } private: - std::unordered_map mIndexMap; + std::unordered_map mIndexMap; pxr::VtArray mValues; pxr::VtArray mIndices; }; -} +} // namespace Falcor diff --git a/Source/plugins/importers/USDImporter/Tessellation.cpp b/Source/Modules/USDUtils/Tessellator/Tessellation.cpp similarity index 83% rename from Source/plugins/importers/USDImporter/Tessellation.cpp rename to Source/Modules/USDUtils/Tessellator/Tessellation.cpp index 631a42e64..1ad503d91 100644 --- a/Source/plugins/importers/USDImporter/Tessellation.cpp +++ b/Source/Modules/USDUtils/Tessellator/Tessellation.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Tessellation.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/Logger.h" #include "Utils/Math/FNVHash.h" #include "IndexedVector.h" @@ -78,13 +78,15 @@ struct GfVec3fHash class MeshIndexer { public: - MeshIndexer(const TfToken& uvInterp) - : mUVInterp(uvInterp) - { - } + MeshIndexer(const TfToken& uvInterp) : mUVInterp(uvInterp) {} // Add a face's worth of facets to the mesh. Assumes that the facets are triangles. - void addFacets(const std::vector& indices, const std::vector& positions, const std::vector& normals, const std::vector& uvs) + void addFacets( + const std::vector& indices, + const std::vector& positions, + const std::vector& normals, + const std::vector& uvs + ) { FALCOR_ASSERT((indices.size() % 3) == 0); @@ -98,16 +100,16 @@ class MeshIndexer for (int i = 0, j = 0; i < positionCount; ++i, j += 3) { - GfVec3f pos(positions[j+0], positions[j+1], positions[j+2]); + GfVec3f pos(positions[j + 0], positions[j + 1], positions[j + 2]); uint32_t idx; if (mPositionSet.append(pos, idx)) { // If this was the first time this position was seen, also push normals, and uvs if they are per-vertex. - mNormals.push_back(GfVec3f(normals[j+0], normals[j+1], normals[j+2])); + mNormals.push_back(GfVec3f(normals[j + 0], normals[j + 1], normals[j + 2])); if (mUVInterp == UsdGeomTokens->vertex || mUVInterp == UsdGeomTokens->varying) { - mUVs.push_back(GfVec2f(uvs[2*i+0], uvs[2*i+1])); + mUVs.push_back(GfVec2f(uvs[2 * i + 0], uvs[2 * i + 1])); } } indexMap.push_back(idx); @@ -120,7 +122,7 @@ class MeshIndexer // If uv attributes are face varying, append to the current index. if (mUVInterp == UsdGeomTokens->faceVarying) { - mUVs.push_back(GfVec2f(uvs[2*idx+0], uvs[2*idx+1])); + mUVs.push_back(GfVec2f(uvs[2 * idx + 0], uvs[2 * idx + 1])); } } @@ -150,12 +152,18 @@ Sdc::Options::FVarLinearInterpolation getFaceVaryingLinearInterpolation(const Us { TfToken interp = getAttribute(geomMesh.GetFaceVaryingLinearInterpolationAttr(), UsdGeomTokens->cornersPlus1); - if (interp == UsdGeomTokens->none) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_NONE; - else if (interp == UsdGeomTokens->cornersOnly) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_ONLY; - else if (interp == UsdGeomTokens->cornersPlus1) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_PLUS1; - else if (interp == UsdGeomTokens->cornersPlus2) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_PLUS2; - else if (interp == UsdGeomTokens->boundaries) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_BOUNDARIES; - else if (interp == UsdGeomTokens->all) return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_ALL; + if (interp == UsdGeomTokens->none) + return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_NONE; + else if (interp == UsdGeomTokens->cornersOnly) + return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_ONLY; + else if (interp == UsdGeomTokens->cornersPlus1) + return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_PLUS1; + else if (interp == UsdGeomTokens->cornersPlus2) + return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_PLUS2; + else if (interp == UsdGeomTokens->boundaries) + return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_BOUNDARIES; + else if (interp == UsdGeomTokens->all) + return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_ALL; logWarning("Unsupported face varying linear interpolation mode '{}' on '{}'.", interp.GetString(), geomMesh.GetPath().GetString()); return Sdc::Options::FVarLinearInterpolation::FVAR_LINEAR_CORNERS_PLUS1; @@ -165,9 +173,12 @@ Sdc::Options::VtxBoundaryInterpolation getVertexBoundaryInterpolation(const UsdG { TfToken interp = getAttribute(geomMesh.GetInterpolateBoundaryAttr(), UsdGeomTokens->edgeAndCorner); - if (interp == UsdGeomTokens->none) return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_NONE; - else if (interp == UsdGeomTokens->edgeOnly) return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_EDGE_ONLY; - else if (interp == UsdGeomTokens->edgeAndCorner) return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_EDGE_AND_CORNER; + if (interp == UsdGeomTokens->none) + return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_NONE; + else if (interp == UsdGeomTokens->edgeOnly) + return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_EDGE_ONLY; + else if (interp == UsdGeomTokens->edgeAndCorner) + return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_EDGE_AND_CORNER; logWarning("Unsupported vertex boundary interpolation mode '{}' on '{}'.", interp.GetString(), geomMesh.GetPath().GetString()); return Sdc::Options::VtxBoundaryInterpolation::VTX_BOUNDARY_EDGE_AND_CORNER; @@ -214,7 +225,8 @@ UsdMeshData triangulate(const pxr::UsdGeomMesh& geomMesh, const UsdMeshData& bas VtIntArray outFaceIndices; int next[2] = {1, 2}; - if (leftHanded) std::swap(next[0], next[1]); + if (leftHanded) + std::swap(next[0], next[1]); // Triangulate each face, f // vertIdx is the offset into faceIndices to the first vertex index for the current face @@ -314,10 +326,12 @@ UsdMeshData triangulate(const pxr::UsdGeomMesh& geomMesh, const UsdMeshData& bas * If refinement (subdivision) is to be applied, OpenSubdiv is used to evalute the surface and related attributes. * If no refinement is to be applied, we use a simple vertex-order-perserving triangulation scheme instead. */ -UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, - const UsdMeshData& baseMesh, - uint32_t maxRefinementLevel, - pxr::VtIntArray& coarseFaceIndices) +UsdMeshData tessellate( + const pxr::UsdGeomMesh& geomMesh, + const UsdMeshData& baseMesh, + uint32_t maxRefinementLevel, + pxr::VtIntArray& coarseFaceIndices +) { if (baseMesh.points.size() == 0 || baseMesh.topology.getNumFaces() == 0 || baseMesh.topology.faceIndices.size() == 0) { @@ -345,10 +359,12 @@ UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, else if (usdScheme == UsdGeomTokens->loop) { // Ensure that the input consists solely of triangles. - auto it = std::find_if(baseMesh.topology.faceIndices.begin(), baseMesh.topology.faceIndices.end(), [](int i) { return i != 3; }); - if (it != baseMesh.topology.faceIndices.end()) + auto it = std::find_if(baseMesh.topology.faceCounts.begin(), baseMesh.topology.faceCounts.end(), [](int i) { return i != 3; }); + if (it != baseMesh.topology.faceCounts.end()) { - logWarning("Cannot apply Loop subdivision to non-triangular mesh '{}'. Unrefined mesh will be used.", geomMesh.GetPath().GetString()); + logWarning( + "Cannot apply Loop subdivision to non-triangular mesh '{}'. Unrefined mesh will be used.", geomMesh.GetPath().GetString() + ); return triangulate(geomMesh, baseMesh, coarseFaceIndices); } scheme = Sdc::SCHEME_LOOP; @@ -359,7 +375,11 @@ UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, } else { - logWarning("Unknown subdivision scheme: '{}' on mesh '{}'. Unrefined mesh will be used.", usdScheme.GetString(), geomMesh.GetPath().GetString()); + logWarning( + "Unknown subdivision scheme: '{}' on mesh '{}'. Unrefined mesh will be used.", + usdScheme.GetString(), + geomMesh.GetPath().GetString() + ); return triangulate(geomMesh, baseMesh, coarseFaceIndices); } @@ -383,7 +403,7 @@ UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, desc.numFaces = baseMesh.topology.getNumFaces(); desc.numVertsPerFace = (const int*)baseMesh.topology.faceCounts.data(); desc.vertIndicesPerFace = (Far::Index*)baseMesh.topology.faceIndices.data(); - desc.fvarChannels = &channels; // Channels will be initialized below if necessary. + desc.fvarChannels = &channels; // Channels will be initialized below if necessary. std::vector uvIndices; VtVec2fArray indexedUVs; @@ -401,7 +421,7 @@ UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, { indexedUVSet.append(uv); } - uvData = (float *)indexedUVSet.getValues().data(); + uvData = (float*)indexedUVSet.getValues().data(); channels.numValues = indexedUVSet.getValues().size(); channels.valueIndices = indexedUVSet.getIndices().data(); ++desc.numFVarChannels; @@ -443,8 +463,10 @@ UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, bool createVaryingSurf = false; Surface* uvSurface = nullptr; - if (uvInterp == UsdGeomTokens->faceVarying) uvSurface = &fvarSurface; - else if (uvInterp == UsdGeomTokens->vertex) uvSurface = &vertexSurface; + if (uvInterp == UsdGeomTokens->faceVarying) + uvSurface = &fvarSurface; + else if (uvInterp == UsdGeomTokens->vertex) + uvSurface = &vertexSurface; else if (uvInterp == UsdGeomTokens->varying) { uvSurface = &varyingSurface; @@ -457,10 +479,14 @@ UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, for (uint32_t f = 0; f < faceCount; ++f) { - surfaceFactory.InitSurfaces(f, &vertexSurface, &fvarSurface, &fvarID, desc.numFVarChannels, createVaryingSurf ? &varyingSurface : nullptr); - if (!vertexSurface.IsValid()) continue; + surfaceFactory.InitSurfaces( + f, &vertexSurface, &fvarSurface, &fvarID, desc.numFVarChannels, createVaryingSurf ? &varyingSurface : nullptr + ); + if (!vertexSurface.IsValid()) + continue; // Fall back to using vertex surface if the uv surface is invalid for whatever reason. - if (uvSurface && !uvSurface->IsValid()) uvSurface = &vertexSurface; + if (uvSurface && !uvSurface->IsValid()) + uvSurface = &vertexSurface; Bfr::Tessellation tessPattern(vertexSurface.GetParameterization(), tessellationRate, tessOptions); @@ -502,13 +528,21 @@ UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, // Compute positions and normals for (int i = 0, j = 0; i < outCoordCount; ++i, j += pointSize) { - vertexSurface.Evaluate(&outCoords[i * 2], facePatchPoints.data(), pointSize, &outPos[j], reinterpret_cast(&du), reinterpret_cast(&dv)); + vertexSurface.Evaluate( + &outCoords[i * 2], + facePatchPoints.data(), + pointSize, + &outPos[j], + reinterpret_cast(&du), + reinterpret_cast(&dv) + ); // Use the partials to construct a normal vector. float3 normal = normalize(cross(du, dv)); - if (leftHanded) normal = -normal; - outNormals[j+0] = normal.x; - outNormals[j+1] = normal.y; - outNormals[j+2] = normal.z; + if (leftHanded) + normal = -normal; + outNormals[j + 0] = normal.x; + outNormals[j + 1] = normal.y; + outNormals[j + 2] = normal.z; } } @@ -519,7 +553,8 @@ UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, meshIndexer.addFacets(outFacets, outPos, outNormals, outUV); // Append the index of each facet's originating coarse face, f. - for (int i = 0; i < facetCount; ++i) coarseFaceIndices.push_back(f); + for (int i = 0; i < facetCount; ++i) + coarseFaceIndices.push_back(f); } UsdMeshData tessellatedMesh; @@ -534,4 +569,4 @@ UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, tessellatedMesh.uvs = meshIndexer.getUVs(); return tessellatedMesh; } -} +} // namespace Falcor diff --git a/Source/plugins/importers/USDImporter/Tessellation.h b/Source/Modules/USDUtils/Tessellator/Tessellation.h similarity index 66% rename from Source/plugins/importers/USDImporter/Tessellation.h rename to Source/Modules/USDUtils/Tessellator/Tessellation.h index d602fcd50..c1b88d466 100644 --- a/Source/plugins/importers/USDImporter/Tessellation.h +++ b/Source/Modules/USDUtils/Tessellator/Tessellation.h @@ -25,9 +25,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#include "USDHelpers.h" -#include "Utils.h" -#include "Core/Assert.h" +#include "Core/Error.h" + +#include "USDUtils/USDUtils.h" +#include "USDUtils/USDHelpers.h" BEGIN_DISABLE_USD_WARNINGS #include @@ -41,20 +42,15 @@ namespace Falcor */ struct MeshTopology { - MeshTopology() { } + MeshTopology() {} MeshTopology(pxr::TfToken scheme, pxr::TfToken orient, VtIntArray& faceCounts, VtIntArray& faceIndices) - : scheme(scheme) - , orient(orient) - , faceCounts(faceCounts) - , faceIndices(faceIndices) - { - - } - pxr::TfToken scheme; ///< Subdivision scheme, "none", "catmullClark", etc. - pxr::TfToken orient; ///< Orientation, nominally "leftHanded" or "rightHanded" - pxr::VtIntArray faceCounts; ///< Per-face number of vertices. - pxr::VtIntArray faceIndices; ///< Per-face-vertex indices. - pxr::VtIntArray holeIndices; ///< Indices of hole faces (sorted, per USD spec). + : scheme(scheme), orient(orient), faceCounts(faceCounts), faceIndices(faceIndices) + {} + pxr::TfToken scheme; ///< Subdivision scheme, "none", "catmullClark", etc. + pxr::TfToken orient; ///< Orientation, nominally "leftHanded" or "rightHanded" + pxr::VtIntArray faceCounts; ///< Per-face number of vertices. + pxr::VtIntArray faceIndices; ///< Per-face-vertex indices. + pxr::VtIntArray holeIndices; ///< Indices of hole faces (sorted, per USD spec). uint32_t getNumFaces() const { return faceCounts.size(); } @@ -65,7 +61,7 @@ struct MeshTopology VtVec3iArray ret; for (uint32_t i = 0; i < faceIndices.size(); i += 3) { - ret.push_back(GfVec3i(faceIndices[i+0], faceIndices[i+1], faceIndices[i+2])); + ret.push_back(GfVec3i(faceIndices[i + 0], faceIndices[i + 1], faceIndices[i + 2])); } return ret; } @@ -76,12 +72,12 @@ struct MeshTopology */ struct UsdMeshData { - MeshTopology topology; ///< Topology - pxr::VtVec3fArray points; ///< Vertex positions - pxr::VtVec3fArray normals; ///< Shading normals - pxr::VtVec2fArray uvs; ///< Texture coordinates - pxr::TfToken normalInterp; ///< Normal interpolation mode (none, vertex, varying, faceVarying) - pxr::TfToken uvInterp; ///< Texture coordinate interpolatoin mode (none, vertex, varying, faceVarying) + MeshTopology topology; ///< Topology + pxr::VtVec3fArray points; ///< Vertex positions + pxr::VtVec3fArray normals; ///< Shading normals + pxr::VtVec2fArray uvs; ///< Texture coordinates + pxr::TfToken normalInterp; ///< Normal interpolation mode (none, vertex, varying, faceVarying) + pxr::TfToken uvInterp; ///< Texture coordinate interpolatoin mode (none, vertex, varying, faceVarying) }; /** @@ -93,8 +89,10 @@ struct UsdMeshData * @param[out] coarseFaceIndices Index of base face from which each output triangle derives. * @return UsdMeshData containing tessellated results; points will be zero-length on failure. */ -UsdMeshData tessellate(const pxr::UsdGeomMesh& geomMesh, - const UsdMeshData& baseMesh, - uint32_t maxRefinementLevel, - pxr::VtIntArray& coarseFaceIndices); -} +UsdMeshData tessellate( + const pxr::UsdGeomMesh& geomMesh, + const UsdMeshData& baseMesh, + uint32_t maxRefinementLevel, + pxr::VtIntArray& coarseFaceIndices +); +} // namespace Falcor diff --git a/Source/plugins/importers/USDImporter/USDHelpers.h b/Source/Modules/USDUtils/USDHelpers.h similarity index 60% rename from Source/plugins/importers/USDImporter/USDHelpers.h rename to Source/Modules/USDUtils/USDHelpers.h index 577051965..1ae209a04 100644 --- a/Source/plugins/importers/USDImporter/USDHelpers.h +++ b/Source/Modules/USDUtils/USDHelpers.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -28,28 +28,20 @@ #pragma once #if defined(_MSC_VER) -#define BEGIN_DISABLE_USD_WARNINGS \ - __pragma( warning(push) ) \ - __pragma( warning(disable : 4003) ) /* Not enough macro arguments */ \ - __pragma( warning(disable : 4244) ) /* Conversion possible loss of data */ \ - __pragma( warning(disable : 4267) ) /* Conversion possible loss of data */ \ - __pragma( warning(disable : 4305) ) /* Truncation double to float */ \ - __pragma( warning(disable : 5033) ) /* 'register' storage class specifier deprecated */ -#define END_DISABLE_USD_WARNINGS \ - __pragma( warning(pop) ) +#define BEGIN_DISABLE_USD_WARNINGS \ + __pragma(warning(push)) __pragma(warning(disable : 4003)) /* Not enough macro arguments */ \ + __pragma(warning(disable : 4244)) /* Conversion possible loss of data */ \ + __pragma(warning(disable : 4267)) /* Conversion possible loss of data */ \ + __pragma(warning(disable : 4305)) /* Truncation double to float */ \ + __pragma(warning(disable : 5033)) /* 'register' storage class specifier deprecated */ +#define END_DISABLE_USD_WARNINGS __pragma(warning(pop)) #elif defined(__clang__) -#define BEGIN_DISABLE_USD_WARNINGS \ - __pragma( clang diagnostic push ) \ - __pragma( clang diagnostic ignored "-Wignored-attributes" ) -#define END_DISABLE_USD_WARNINGS \ - __pragma( clang diagnostic pop ) +#define BEGIN_DISABLE_USD_WARNINGS __pragma(clang diagnostic push) __pragma(clang diagnostic ignored "-Wignored-attributes") +#define END_DISABLE_USD_WARNINGS __pragma(clang diagnostic pop) #elif defined(__GNUC__) -#define BEGIN_DISABLE_USD_WARNINGS \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") - _Pragma("GCC diagnostic ignored \"-Wparentheses\"") -#define END_DISABLE_USD_WARNINGS \ - _Pragma("GCC diagnostic pop") +#define BEGIN_DISABLE_USD_WARNINGS _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") +_Pragma("GCC diagnostic ignored \"-Wparentheses\"") +#define END_DISABLE_USD_WARNINGS _Pragma("GCC diagnostic pop") #else #define BEGIN_DISABLE_USD_WARNINGS #define END_DISABLE_USD_WARNINGS diff --git a/Source/Falcor/Core/FalcorConfig.h b/Source/Modules/USDUtils/USDScene1Utils.cpp similarity index 87% rename from Source/Falcor/Core/FalcorConfig.h rename to Source/Modules/USDUtils/USDScene1Utils.cpp index 23f527499..8c1a694a2 100644 --- a/Source/Falcor/Core/FalcorConfig.h +++ b/Source/Modules/USDUtils/USDScene1Utils.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,4 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -#pragma once - -#define FALCOR_ENABLE_LOGGER 1 // Set this to 1 to enable logging. -#define FALCOR_ENABLE_PROFILER 1 // Set this to 1 to enable CPU/GPU profiling. +#include "USDUtils.h" diff --git a/Source/Modules/USDUtils/USDScene1Utils.h b/Source/Modules/USDUtils/USDScene1Utils.h new file mode 100644 index 000000000..4db795a22 --- /dev/null +++ b/Source/Modules/USDUtils/USDScene1Utils.h @@ -0,0 +1,136 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include "USDHelpers.h" +#include "USDUtils.h" +#include "Scene/SceneBuilder.h" + +BEGIN_DISABLE_USD_WARNINGS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +END_DISABLE_USD_WARNINGS + +#include +#include + +using namespace pxr; + +namespace Falcor +{ +inline SceneBuilder::Node makeNode(const std::string& name, NodeID parentId = NodeID::Invalid()) +{ + return SceneBuilder::Node{name, float4x4::identity(), float4x4::identity(), float4x4::identity(), parentId}; +} + +inline SceneBuilder::Node makeNode( + const std::string& name, + const float4x4& xform, + const float4x4& bindTransform, + NodeID parentId = NodeID::Invalid() +) +{ + return SceneBuilder::Node{name, xform, bindTransform, float4x4::identity(), parentId}; +} + +using AttributeFrequency = SceneBuilder::Mesh::AttributeFrequency; + +inline size_t computeElementCount(AttributeFrequency freq, size_t faceCount, size_t vertexCount) +{ + if (freq == AttributeFrequency::Constant) + { + return 1; + } + else if (freq == AttributeFrequency::Uniform) + { + return faceCount; + } + else if (freq == AttributeFrequency::Vertex) + { + return vertexCount; + } + else if (freq == AttributeFrequency::FaceVarying) + { + return 3 * faceCount; + } + else + { + logError("Unsupported primvar interpolation mode {}.", (uint32_t)freq); + return 0; + } +} + +// Compute the count of per-face elements, based on interpolation type +inline size_t computePerFaceElementCount(AttributeFrequency freq, size_t faceCount) +{ + if (freq == AttributeFrequency::Uniform) + { + return faceCount; + } + else if (freq == AttributeFrequency::FaceVarying) + { + return 3 * faceCount; + } + // Everything else is indexed (vertex, varying), or constant + return 0; +} + +inline AttributeFrequency convertInterpolation(const TfToken& mode) +{ + if (mode == UsdGeomTokens->constant) + { + return AttributeFrequency::Constant; + } + else if (mode == UsdGeomTokens->uniform) + { + return AttributeFrequency::Uniform; + } + else if (mode == UsdGeomTokens->vertex || mode == UsdGeomTokens->varying) + { + // For our purposes, vertex and varying are synonymous. + return AttributeFrequency::Vertex; + } + else if (mode == UsdGeomTokens->faceVarying) + { + return AttributeFrequency::FaceVarying; + } + else + { + logError("Unknown vertex interpolation mode '{}'.", mode.GetString()); + return AttributeFrequency::None; + } +} +} // namespace Falcor diff --git a/Source/Modules/USDUtils/USDUtils.cpp b/Source/Modules/USDUtils/USDUtils.cpp new file mode 100644 index 000000000..8c1a694a2 --- /dev/null +++ b/Source/Modules/USDUtils/USDUtils.cpp @@ -0,0 +1,28 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "USDUtils.h" diff --git a/Source/Modules/USDUtils/USDUtils.h b/Source/Modules/USDUtils/USDUtils.h new file mode 100644 index 000000000..a7f53cfd7 --- /dev/null +++ b/Source/Modules/USDUtils/USDUtils.h @@ -0,0 +1,240 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include "USDHelpers.h" +#include "Core/Error.h" +#include "Utils/Logger.h" +#include "Utils/Math/Vector.h" +#include "Utils/Math/Matrix.h" + +BEGIN_DISABLE_USD_WARNINGS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +END_DISABLE_USD_WARNINGS + +#include +#include + +using namespace pxr; + +namespace Falcor +{ +class UsdObjHash +{ +public: + size_t operator()(const UsdObject& obj) const { return hash_value(obj); } +}; + +struct Float3Hash +{ + size_t operator()(const float3& v) const + { + // Simple hash function that multiplies the integer interpretation of each component by a prime and xors the results + return (*reinterpret_cast(&v.x) * 7727ULL) ^ (*reinterpret_cast(&v.y) * 5521ULL) ^ + (*reinterpret_cast(&v.z) * 6971ULL); + } +}; + +inline float3 toFalcor(const GfVec3f& v) +{ + return float3(v[0], v[1], v[2]); +} + +inline float3 toFalcor(const GfVec3d& v) +{ + return float3(v[0], v[1], v[2]); +} + +inline float3 toFalcor(const GfVec3h& v) +{ + return float3(v[0], v[1], v[2]); +} + +inline float4x4 toFalcor(const GfMatrix4d& m) +{ + // USD uses row-major matrices and row vectors, which are pre-multiplied (v * M) with a matrix to perform a transformation. + // Falcor uses row-major matrices and column vectors, which are post-multiplied (M * v). + // As such, we transpose USD matrices upon import. + return float4x4({ + // clang-format off + m[0][0], m[1][0], m[2][0], m[3][0], + m[0][1], m[1][1], m[2][1], m[3][1], + m[0][2], m[1][2], m[2][2], m[3][2], + m[0][3], m[1][3], m[2][3], m[3][3] + // clang-format on + }); +} + +inline float4x4 toFalcor(const GfMatrix4f& m) +{ + // USD uses row-major matrices and row vectors, which are pre-multiplied (v * M) with a matrix to perform a transformation. + // Falcor uses row-major matrices and column vectors, which are post-multiplied (M * v). + // As such, we transpose USD matrices upon import. + return float4x4({ + // clang-format off + m[0][0], m[1][0], m[2][0], m[3][0], + m[0][1], m[1][1], m[2][1], m[3][1], + m[0][2], m[1][2], m[2][2], m[3][2], + m[0][3], m[1][3], m[2][3], m[3][3] + // clang-format on + }); +} + +inline bool getLocalTransform(const UsdGeomXformable& xformable, float4x4& xform) +{ + bool resets = false; + GfMatrix4d transform; + xformable.GetLocalTransformation(&transform, &resets, UsdTimeCode::EarliestTime()); + xform = toFalcor(transform); + + return resets; +} + +// Helper function to return an attribute value, if defined, or the specified default value if not +template +inline T getAttribute(const UsdAttribute& attrib, const T& def) +{ + T val = def; + if (attrib) + { + attrib.Get(&val, UsdTimeCode::EarliestTime()); + } + return val; +} + +// Helper function that returns the main attribute if it is authored (non-default), +// otherwise it tries to retrieve the fallback attribute and should that fail, the default value. +// This is used to provide compatibility, e.g. when the default disk light radius changed from "radius" to "inputs:radius" +template +inline T getAuthoredAttribute(const UsdAttribute& mainAttrib, const UsdAttribute& fallbackAttrib, const T& def) +{ + T val = def; + if (mainAttrib && mainAttrib.IsAuthored()) + { + mainAttrib.Get(&val, UsdTimeCode::EarliestTime()); + } + else if (fallbackAttrib) + { + fallbackAttrib.Get(&val, UsdTimeCode::EarliestTime()); + } + return val; +} + +template +inline T getAttribute(const UsdPrim& prim, const std::string& attribName, const T& def) +{ + T val = def; + UsdAttribute attrib = prim.GetAttribute(TfToken(attribName)); + if (attrib.IsValid()) + { + attrib.Get(&val, UsdTimeCode::EarliestTime()); + } + return val; +} + +inline TfToken getPurpose(const UsdGeomImageable& prim) +{ + TfToken purpose = UsdGeomTokens->default_; + UsdAttribute purposeAttr = prim.GetPurposeAttr(); + if (purposeAttr) + { + purposeAttr.Get(&purpose, UsdTimeCode::EarliestTime()); + } + return purpose; +} + +inline bool isRenderable(const UsdGeomImageable& geomImageable) +{ + TfToken purpose = getPurpose(geomImageable); + if (purpose == UsdGeomTokens->guide || purpose == UsdGeomTokens->proxy) + { + return false; + } + UsdAttribute visibilityAttr(geomImageable.GetVisibilityAttr()); + if (visibilityAttr) + { + TfToken visibility; + visibilityAttr.Get(&visibility, UsdTimeCode::EarliestTime()); + if (visibility == UsdGeomTokens->invisible) + { + return false; + } + } + + // Determine the inherited visibility. + // USD documentation warns that ComputeVisiblity() can be inefficient, as it walks up + // towards the root every it is called, rather than caching inherited visibility. + // However, there has yet been no indication that this is a meaningful performance issue in practice. + return geomImageable.ComputeVisibility() != UsdGeomTokens->invisible; +} + +inline bool isTimeSampled(const UsdGeomPointBased& geomPointBased) +{ + return geomPointBased.GetPointsAttr().GetNumTimeSamples() > 1; +} + +// Route USD messages through Falcor's logging system +class DiagDelegate : public TfDiagnosticMgr::Delegate +{ +public: + void IssueFatalError(const TfCallContext& context, const std::string& msg) override + { + FALCOR_THROW(msg + " " + context.GetPrettyFunction()); + } + + void IssueError(const TfError& err) override { logError(formatMessage(&err)); } + + void IssueWarning(const TfWarning& warning) override { logWarning(formatMessage(&warning)); } + + void IssueStatus(const TfStatus& status) override { logInfo(formatMessage(&status)); } + +private: + std::string formatMessage(const TfDiagnosticBase* elt) { return elt->GetCommentary(); } +}; + +class ScopeGuard +{ +public: + ScopeGuard(const std::function& func) : m_func(func) {} + + ~ScopeGuard() { m_func(); } + +private: + std::function m_func; +}; +} // namespace Falcor diff --git a/Source/Mogwai/Extensions/Capture/CaptureTrigger.cpp b/Source/Mogwai/Extensions/Capture/CaptureTrigger.cpp index ec9da23a9..eb0d61c5e 100644 --- a/Source/Mogwai/Extensions/Capture/CaptureTrigger.cpp +++ b/Source/Mogwai/Extensions/Capture/CaptureTrigger.cpp @@ -45,7 +45,7 @@ namespace Mogwai uint64_t yEnd = yStart + yCount - 1; if (xStart <= yEnd && yStart <= xEnd) { - throw ArgumentError("This range overlaps an existing range!"); + FALCOR_THROW("This range overlaps an existing range!"); } } @@ -191,7 +191,7 @@ namespace Mogwai std::string CaptureTrigger::getScript(const std::string& var) const { std::string s; - s += ScriptWriter::makeSetProperty(var, kOutputDir, ScriptWriter::getPathString(mOutputDir, false)); + s += ScriptWriter::makeSetProperty(var, kOutputDir, ScriptWriter::getPathString(mOutputDir)); s += ScriptWriter::makeSetProperty(var, kBaseFilename, mBaseFilename); return s; } diff --git a/Source/Mogwai/Extensions/Capture/FrameCapture.cpp b/Source/Mogwai/Extensions/Capture/FrameCapture.cpp index 03ad77959..007560c2b 100644 --- a/Source/Mogwai/Extensions/Capture/FrameCapture.cpp +++ b/Source/Mogwai/Extensions/Capture/FrameCapture.cpp @@ -162,7 +162,7 @@ namespace Mogwai const std::string basename = getOutputNamePrefix(outputName) + std::to_string(mpRenderer->getGlobalClock().getFrame()); const ref pOutput = pGraph->getOutput(outputIndex)->asTexture(); - if (!pOutput) throw RuntimeError("Graph output {} is not a texture", outputName); + if (!pOutput) FALCOR_THROW("Graph output {} is not a texture", outputName); const ResourceFormat format = pOutput->getFormat(); const uint32_t channels = getFormatChannelCount(format); @@ -237,7 +237,7 @@ namespace Mogwai } // Copy color channel into temporary texture. - pTex = Texture::create2D(mpRenderer->getDevice(), pOutput->getWidth(), pOutput->getHeight(), outputFormat, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + pTex = mpRenderer->getDevice()->createTexture2D(pOutput->getWidth(), pOutput->getHeight(), outputFormat, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); mpImageProcessing->copyColorChannel(pRenderContext, pOutput->getSRV(0, 1, 0, 1), pTex->getUAV(), mask); } @@ -260,7 +260,7 @@ namespace Mogwai void FrameCapture::addFrames(const std::string& graphName, const uint64_vec& frames) { auto pGraph = mpRenderer->getGraph(graphName).get(); - if (!pGraph) throw RuntimeError("Can't find a graph named '{}'", graphName); + if (!pGraph) FALCOR_THROW("Can't find a graph named '{}'", graphName); this->addFrames(pGraph, frames); } diff --git a/Source/Mogwai/Mogwai.cpp b/Source/Mogwai/Mogwai.cpp index 80cae6913..773f483ce 100644 --- a/Source/Mogwai/Mogwai.cpp +++ b/Source/Mogwai/Mogwai.cpp @@ -29,6 +29,7 @@ #include "Mogwai.h" #include "MogwaiSettings.h" #include "GlobalState.h" +#include "Core/AssetResolver.h" #include "Scene/Importer.h" #include "RenderGraph/RenderGraphImportExport.h" #include "RenderGraph/RenderPassStandardFlags.h" @@ -77,7 +78,7 @@ namespace Mogwai if (!gExtensions) gExtensions.reset(new std::map()); if (gExtensions->find(name) != gExtensions->end()) { - throw RuntimeError("Extension '{}' is already registered.", name); + FALCOR_THROW("Extension '{}' is already registered.", name); } (*gExtensions)[name] = func; } @@ -85,7 +86,7 @@ namespace Mogwai void Renderer::onShutdown() { resetEditor(); - getDevice()->flushAndSync(); // Need to do that because clearing the graphs will try to release some state objects which might be in use + getDevice()->wait(); // Need to do that because clearing the graphs will try to release some state objects which might be in use mGraphs.clear(); if (mPipedOutput) { @@ -134,8 +135,6 @@ namespace Mogwai // Add scene to recent files only if not in silent mode (which is used during image tests). if (!mOptions.silentMode) mAppData.addRecentScene(mOptions.sceneFile); } - - Scene::nullTracePass(pRenderContext, uint2(1024)); } void Renderer::onOptionsChange() @@ -231,7 +230,11 @@ namespace Mogwai // Get the current output, in case `renderOutputUI()` unmarks it ref pTex = mGraphs[mActiveGraph].pGraph->getOutput(data.currentOutput)->asTexture(); std::string label = data.currentOutput + "##" + mGraphs[mActiveGraph].pGraph->getName(); - if (!pTex) { reportError("Invalid output resource. Is not a texture."); } + if (!pTex) + { + logWarning("Invalid output resource. Is not a texture. Closing window."); + return false; + } uint2 debugSize = (uint2)(float2(winSize) * float2(0.4f, 0.55f)); uint2 debugPos = winSize - debugSize; @@ -382,8 +385,8 @@ namespace Mogwai void Renderer::removeGraph(const std::string& graphName) { auto pGraph = getGraph(graphName); - if (pGraph) removeGraph(pGraph); - else reportError("Can't find a graph named '" + graphName + "'. There's nothing to remove."); + FALCOR_CHECK(pGraph, "Can't find a graph named '{}'.", graphName); + removeGraph(pGraph); } ref Renderer::getGraph(const std::string& graphName) const @@ -456,19 +459,26 @@ namespace Mogwai try { - if (getProgressBar().isActive()) getProgressBar().show("Loading Configuration"); + if (getProgressBar().isActive()) + getProgressBar().show("Loading Configuration"); // Add script directory to search paths (add it to the front to make it highest priority). + AssetResolver oldResolver = AssetResolver::getDefaultResolver(); auto directory = std::filesystem::absolute(path).parent_path(); - addDataDirectory(directory, true); + AssetResolver::getDefaultResolver().addSearchPath(directory, SearchPathPriority::First); Scripting::runScriptFromFile(path); - removeDataDirectory(directory); + // Restore asset resolver. + AssetResolver::getDefaultResolver() = oldResolver; } catch (const std::exception& e) { - reportError(fmt::format("Error when loading configuration file: {}\n{}", path, e.what())); + std::string msg = fmt::format("Error when loading configuration file: {}\n{}", path, e.what()); + if (is_set(getErrorDiagnosticFlags(), ErrorDiagnosticFlags::ShowMessageBoxOnError)) + reportErrorAndContinue(msg); + else + FALCOR_THROW(msg); } } @@ -484,11 +494,7 @@ namespace Mogwai void Renderer::addGraph(const ref& pGraph) { - if (pGraph == nullptr) - { - reportError("Can't add an empty graph"); - return; - } + FALCOR_CHECK(pGraph, "Can't add an empty graph"); // If a graph with the same name already exists, remove it GraphData* pGraphData = nullptr; @@ -544,7 +550,16 @@ namespace Mogwai } catch (const ImporterError &e) { - reportErrorAndAllowRetry(fmt::format("Failed to load scene.\n\nError in {}\n\n{}", e.path(), e.what())); + std::string msg = fmt::format("Failed to load scene.\n\nError in {}\n\n{}", e.path(), e.what()); + if (is_set(getErrorDiagnosticFlags(), ErrorDiagnosticFlags::ShowMessageBoxOnError)) + { + if (reportErrorAndAllowRetry(msg)) + continue; + else + break; + } else { + FALCOR_THROW(msg); + } } } } @@ -568,9 +583,9 @@ namespace Mogwai { // create common texture sampler Sampler::Desc desc; - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); + desc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Linear); desc.setMaxAnisotropy(8); - mpSampler = Sampler::create(getDevice(), desc); + mpSampler = getDevice()->createSampler(desc); } mpScene->getMaterialSystem().setDefaultTextureSampler(mpSampler); } @@ -808,7 +823,7 @@ namespace Mogwai } } -int main(int argc, char** argv) +int runMain(int argc, char** argv) { args::ArgumentParser parser("Mogwai render application."); parser.helpParams.programName = "Mogwai"; @@ -932,15 +947,11 @@ int main(int argc, char** argv) if (useSceneCacheFlag) options.useSceneCache = true; if (rebuildSceneCacheFlag) options.rebuildSceneCache = true; - try - { - Mogwai::Renderer renderer(config, options); - return renderer.run(); - } - catch (const std::exception& e) - { - // Note: This can only trigger from the setup code above. SampleApp::run() handles all exceptions internally. - reportFatalError("Mogwai crashed unexpectedly...\n" + std::string(e.what()), false); - } - return 1; + Mogwai::Renderer renderer(config, options); + return renderer.run(); +} + +int main(int argc, char** argv) +{ + return catchAndReportAllExceptions([&]() { return runMain(argc, argv); }); } diff --git a/Source/Mogwai/MogwaiScripting.cpp b/Source/Mogwai/MogwaiScripting.cpp index 4d96fe138..25612dc9e 100644 --- a/Source/Mogwai/MogwaiScripting.cpp +++ b/Source/Mogwai/MogwaiScripting.cpp @@ -93,6 +93,7 @@ namespace Mogwai if (mpScene) { s += "# Scene\n"; + // In the past we did try to find a relative path to the asset search directories, for now we skip this. s += ScriptWriter::makeMemberFunc(kRendererVar, kLoadScene, ScriptWriter::getPathString(mpScene->getPath())); const std::string sceneVar = kRendererVar + "." + kScene; s += mpScene->getScript(sceneVar); diff --git a/Source/Mogwai/MogwaiSettings.cpp b/Source/Mogwai/MogwaiSettings.cpp index c022b0575..948212a2f 100644 --- a/Source/Mogwai/MogwaiSettings.cpp +++ b/Source/Mogwai/MogwaiSettings.cpp @@ -223,13 +223,17 @@ namespace Mogwai g.text("Program compilation:\n"); const auto& s = mpRenderer->getDevice()->getProgramManager()->getCompilationStats(); + double totalTime, downstreamTime; + mpRenderer->getDevice()->getSlangGlobalSession()->getCompilerElapsedTime(&totalTime, &downstreamTime); std::ostringstream oss; oss << "Program version count: " << s.programVersionCount << std::endl << "Program kernels count: " << s.programKernelsCount << std::endl << "Program version time (total): " << s.programVersionTotalTime << " s" << std::endl << "Program kernels time (total): " << s.programKernelsTotalTime << " s" << std::endl << "Program version time (max): " << s.programVersionMaxTime << " s" << std::endl - << "Program kernels time (max): " << s.programKernelsMaxTime << " s" << std::endl; + << "Program kernels time (max): " << s.programKernelsMaxTime << " s" << std::endl + << "Total shader code-gen time: " << totalTime << " s" << std::endl + << "Downstream compilation time: " << downstreamTime << " s" << std::endl; g.text(oss.str()); if (g.button("Reset")) diff --git a/Source/RenderPasses/AccumulatePass/Accumulate.cs.slang b/Source/RenderPasses/AccumulatePass/Accumulate.cs.slang index ff03a1ab8..c27f196e5 100644 --- a/Source/RenderPasses/AccumulatePass/Accumulate.cs.slang +++ b/Source/RenderPasses/AccumulatePass/Accumulate.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,28 +26,29 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Temporal accumulation render pass. - - There are entry points for each of the three supported accumulation modes. - Note that for the compensated summation mode the shader _must_ be compiled - for precise floating-point operations (no reordering). - - In all modes, the shader writes the current accumulated average to the - output texture. The intermediate buffers are internal to the pass. -*/ +/** + * Temporal accumulation render pass. + * + * There are entry points for each of the three supported accumulation modes. + * Note that for the compensated summation mode the shader _must_ be compiled + * for precise floating-point operations (no reordering). + * + * In all modes, the shader writes the current accumulated average to the + * output texture. The intermediate buffers are internal to the pass. + */ cbuffer PerFrameCB { - uint2 gResolution; - uint gAccumCount; - bool gAccumulate; - bool gMovingAverageMode; + uint2 gResolution; + uint gAccumCount; + bool gAccumulate; + bool gMovingAverageMode; } // Input data to accumulate and accumulated output. #define INPUT_FORMAT_FLOAT 0 -#define INPUT_FORMAT_UINT 1 -#define INPUT_FORMAT_SINT 2 +#define INPUT_FORMAT_UINT 1 +#define INPUT_FORMAT_SINT 2 #if _INPUT_FORMAT == INPUT_FORMAT_FLOAT typedef float4 InputDataType; @@ -63,18 +64,19 @@ Texture2D gCurFrame; RWTexture2D gOutputFrame; // Last frame data, format depends on accumulation mode. -RWTexture2D gLastFrameSum; // If mode is Single or SingleKahan -RWTexture2D gLastFrameCorr; // If mode is SingleKahan -RWTexture2D gLastFrameSumLo; // If mode is Double -RWTexture2D gLastFrameSumHi; // If mode is Double - - -/** Single precision standard summation. -*/ +RWTexture2D gLastFrameSum; // If mode is Single or SingleKahan +RWTexture2D gLastFrameCorr; // If mode is SingleKahan +RWTexture2D gLastFrameSumLo; // If mode is Double +RWTexture2D gLastFrameSumHi; // If mode is Double + +/** + * Single precision standard summation. + */ [numthreads(16, 16, 1)] -void accumulateSingle(uint3 dispatchThreadId : SV_DispatchThreadID) +void accumulateSingle(uint3 dispatchThreadId: SV_DispatchThreadID) { - if (any(dispatchThreadId.xy >= gResolution)) return; + if (any(dispatchThreadId.xy >= gResolution)) + return; const uint2 pixelPos = dispatchThreadId.xy; const float4 curColor = gCurFrame[pixelPos]; @@ -109,12 +111,14 @@ void accumulateSingle(uint3 dispatchThreadId : SV_DispatchThreadID) gOutputFrame[pixelPos] = output; } -/** Single precision compensated summation. -*/ +/** + * Single precision compensated summation. + */ [numthreads(16, 16, 1)] -void accumulateSingleCompensated(uint3 dispatchThreadId : SV_DispatchThreadID) +void accumulateSingleCompensated(uint3 dispatchThreadId: SV_DispatchThreadID) { - if (any(dispatchThreadId.xy >= gResolution)) return; + if (any(dispatchThreadId.xy >= gResolution)) + return; const uint2 pixelPos = dispatchThreadId.xy; const float4 curColor = gCurFrame[pixelPos]; @@ -123,16 +127,19 @@ void accumulateSingleCompensated(uint3 dispatchThreadId : SV_DispatchThreadID) { // Fetch the previous sum and running compensation term. float4 sum = gLastFrameSum[pixelPos]; - float4 c = gLastFrameCorr[pixelPos]; // c measures how large (+) or small (-) the current sum is compared to what it should be. + // c measures how large (+) or small (-) the current sum is compared to what it should be. + float4 c = gLastFrameCorr[pixelPos]; // Adjust current value to minimize the running error. // Compute the new sum by adding the adjusted current value. float4 y = curColor - c; - float4 sumNext = sum + y; // The value we'll see in 'sum' on the next iteration. + // The value we'll see in 'sum' on the next iteration. + float4 sumNext = sum + y; output = sumNext / (gAccumCount + 1); gLastFrameSum[pixelPos] = sumNext; - gLastFrameCorr[pixelPos] = (sumNext - sum) - y; // Store new correction term. + // Store new correction term. + gLastFrameCorr[pixelPos] = (sumNext - sum) - y; } else { @@ -142,12 +149,14 @@ void accumulateSingleCompensated(uint3 dispatchThreadId : SV_DispatchThreadID) gOutputFrame[pixelPos] = output; } -/** Double precision standard summation. -*/ +/** + * Double precision standard summation. + */ [numthreads(16, 16, 1)] -void accumulateDouble(uint3 dispatchThreadId : SV_DispatchThreadID) +void accumulateDouble(uint3 dispatchThreadId: SV_DispatchThreadID) { - if (any(dispatchThreadId.xy >= gResolution)) return; + if (any(dispatchThreadId.xy >= gResolution)) + return; const uint2 pixelPos = dispatchThreadId.xy; const float4 curColor = gCurFrame[pixelPos]; diff --git a/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp b/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp index 413ed1a7f..fac6c2192 100644 --- a/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp +++ b/Source/RenderPasses/AccumulatePass/AccumulatePass.cpp @@ -43,43 +43,52 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr namespace { - const char kShaderFile[] = "RenderPasses/AccumulatePass/Accumulate.cs.slang"; - - const char kInputChannel[] = "input"; - const char kOutputChannel[] = "output"; - - // Serialized parameters - const char kEnabled[] = "enabled"; - const char kOutputFormat[] = "outputFormat"; - const char kOutputSize[] = "outputSize"; - const char kFixedOutputSize[] = "fixedOutputSize"; - const char kAutoReset[] = "autoReset"; - const char kPrecisionMode[] = "precisionMode"; - const char kMaxFrameCount[] = "maxFrameCount"; - const char kOverflowMode[] = "overflowMode"; -} - -AccumulatePass::AccumulatePass(ref pDevice, const Properties& props) - : RenderPass(pDevice) +const char kShaderFile[] = "RenderPasses/AccumulatePass/Accumulate.cs.slang"; + +const char kInputChannel[] = "input"; +const char kOutputChannel[] = "output"; + +// Serialized parameters +const char kEnabled[] = "enabled"; +const char kOutputFormat[] = "outputFormat"; +const char kOutputSize[] = "outputSize"; +const char kFixedOutputSize[] = "fixedOutputSize"; +const char kAutoReset[] = "autoReset"; +const char kPrecisionMode[] = "precisionMode"; +const char kMaxFrameCount[] = "maxFrameCount"; +const char kOverflowMode[] = "overflowMode"; +} // namespace + +AccumulatePass::AccumulatePass(ref pDevice, const Properties& props) : RenderPass(pDevice) { // Deserialize pass from dictionary. for (const auto& [key, value] : props) { - if (key == kEnabled) mEnabled = value; - else if (key == kOutputFormat) mOutputFormat = value; - else if (key == kOutputSize) mOutputSizeSelection = value; - else if (key == kFixedOutputSize) mFixedOutputSize = value; - else if (key == kAutoReset) mAutoReset = value; - else if (key == kPrecisionMode) mPrecisionMode = value; - else if (key == kMaxFrameCount) mMaxFrameCount = value; - else if (key == kOverflowMode) mOverflowMode = value; - else logWarning("Unknown property '{}' in AccumulatePass properties.", key); + if (key == kEnabled) + mEnabled = value; + else if (key == kOutputFormat) + mOutputFormat = value; + else if (key == kOutputSize) + mOutputSizeSelection = value; + else if (key == kFixedOutputSize) + mFixedOutputSize = value; + else if (key == kAutoReset) + mAutoReset = value; + else if (key == kPrecisionMode) + mPrecisionMode = value; + else if (key == kMaxFrameCount) + mMaxFrameCount = value; + else if (key == kOverflowMode) + mOverflowMode = value; + else + logWarning("Unknown property '{}' in AccumulatePass properties.", key); } if (props.has("enableAccumulation")) { logWarning("'enableAccumulation' is deprecated. Use 'enabled' instead."); - if (!props.has(kEnabled)) mEnabled = props["enableAccumulation"]; + if (!props.has(kEnabled)) + mEnabled = props["enableAccumulation"]; } mpState = ComputeState::create(mpDevice); @@ -89,9 +98,11 @@ Properties AccumulatePass::getProperties() const { Properties props; props[kEnabled] = mEnabled; - if (mOutputFormat != ResourceFormat::Unknown) props[kOutputFormat] = mOutputFormat; + if (mOutputFormat != ResourceFormat::Unknown) + props[kOutputFormat] = mOutputFormat; props[kOutputSize] = mOutputSizeSelection; - if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) props[kFixedOutputSize] = mFixedOutputSize; + if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) + props[kFixedOutputSize] = mFixedOutputSize; props[kAutoReset] = mAutoReset; props[kPrecisionMode] = mPrecisionMode; props[kMaxFrameCount] = mMaxFrameCount; @@ -106,7 +117,10 @@ RenderPassReflection AccumulatePass::reflect(const CompileData& compileData) const auto fmt = mOutputFormat != ResourceFormat::Unknown ? mOutputFormat : ResourceFormat::RGBA32Float; reflector.addInput(kInputChannel, "Input data to be temporally accumulated").bindFlags(ResourceBindFlags::ShaderResource); - reflector.addOutput(kOutputChannel, "Output data that is temporally accumulated").bindFlags(ResourceBindFlags::RenderTarget | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource).format(fmt).texture2D(sz.x, sz.y); + reflector.addOutput(kOutputChannel, "Output data that is temporally accumulated") + .bindFlags(ResourceBindFlags::RenderTarget | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource) + .format(fmt) + .texture2D(sz.x, sz.y); return reflector; } @@ -119,7 +133,8 @@ void AccumulatePass::execute(RenderContext* pRenderContext, const RenderData& re auto refreshFlags = dict.getValue(kRenderPassRefreshFlags, RenderPassRefreshFlags::None); // If any refresh flag is set, we reset frame accumulation. - if (refreshFlags != RenderPassRefreshFlags::None) reset(); + if (refreshFlags != RenderPassRefreshFlags::None) + reset(); // Reset accumulation upon all scene changes, except camera jitter and history changes. // TODO: Add UI options to select which changes should trigger reset @@ -134,11 +149,8 @@ void AccumulatePass::execute(RenderContext* pRenderContext, const RenderData& re { auto excluded = Camera::Changes::Jitter | Camera::Changes::History; auto cameraChanges = mpScene->getCamera()->getChanges(); - if ((cameraChanges & ~excluded) != Camera::Changes::None) reset(); - } - if (is_set(sceneUpdates, Scene::UpdateFlags::SDFGeometryChanged)) - { - reset(); + if ((cameraChanges & ~excluded) != Camera::Changes::None) + reset(); } } } @@ -174,7 +186,8 @@ void AccumulatePass::execute(RenderContext* pRenderContext, const RenderData& re } // Verify that output is non-integer format. It shouldn't be since reflect() requests a floating-point format. - if (isIntegerFormat(pDst->getFormat())) throw RuntimeError("AccumulatePass: Output to integer format is not supported"); + if (isIntegerFormat(pDst->getFormat())) + FALCOR_THROW("AccumulatePass: Output to integer format is not supported"); // Issue error and disable pass if unsupported I/O size. The user can hit continue and fix the config or abort. if (mEnabled && !resolutionMatch) @@ -217,22 +230,30 @@ void AccumulatePass::accumulate(RenderContext* pRenderContext, const refgetReflector()); + mpProgram[Precision::Double] = + Program::createCompute(mpDevice, kShaderFile, "accumulateDouble", defines, SlangCompilerFlags::TreatWarningsAsErrors); + mpProgram[Precision::Single] = + Program::createCompute(mpDevice, kShaderFile, "accumulateSingle", defines, SlangCompilerFlags::TreatWarningsAsErrors); + mpProgram[Precision::SingleCompensated] = Program::createCompute( + mpDevice, + kShaderFile, + "accumulateSingleCompensated", + defines, + SlangCompilerFlags::FloatingPointModePrecise | SlangCompilerFlags::TreatWarningsAsErrors + ); + mpVars = ProgramVars::create(mpDevice, mpProgram[mPrecisionMode]->getReflector()); mSrcType = srcType; } @@ -274,17 +295,21 @@ void AccumulatePass::renderUI(Gui::Widgets& widget) { // Controls for output size. // When output size requirements change, we'll trigger a graph recompile to update the render pass I/O sizes. - if (widget.dropdown("Output size", mOutputSizeSelection)) requestRecompile(); + if (widget.dropdown("Output size", mOutputSizeSelection)) + requestRecompile(); if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) { - if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) requestRecompile(); + if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) + requestRecompile(); } - if (bool enabled = isEnabled(); widget.checkbox("Enabled", enabled)) setEnabled(enabled); + if (bool enabled = isEnabled(); widget.checkbox("Enabled", enabled)) + setEnabled(enabled); if (mEnabled) { - if (widget.button("Reset", true)) reset(); + if (widget.button("Reset", true)) + reset(); widget.checkbox("Auto Reset", mAutoReset); widget.tooltip("Reset accumulation automatically upon scene changes and refresh flags."); @@ -334,7 +359,8 @@ void AccumulatePass::setScene(RenderContext* pRenderContext, const ref& p void AccumulatePass::onHotReload(HotReloadFlags reloaded) { // Reset accumulation if programs changed. - if (is_set(reloaded, HotReloadFlags::Program)) reset(); + if (is_set(reloaded, HotReloadFlags::Program)) + reset(); } void AccumulatePass::setEnabled(bool enabled) @@ -365,19 +391,25 @@ void AccumulatePass::prepareAccumulation(RenderContext* pRenderContext, uint32_t // (Re-)create buffer if needed. if (!pBuf || pBuf->getWidth() != width || pBuf->getHeight() != height) { - pBuf = Texture::create2D(mpDevice, width, height, format, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + pBuf = mpDevice->createTexture2D( + width, height, format, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess + ); FALCOR_ASSERT(pBuf); reset(); } // Clear data if accumulation has been reset (either above or somewhere else). if (mFrameCount == 0) { - if (getFormatType(format) == FormatType::Float) pRenderContext->clearUAV(pBuf->getUAV().get(), float4(0.f)); - else pRenderContext->clearUAV(pBuf->getUAV().get(), uint4(0)); + if (getFormatType(format) == FormatType::Float) + pRenderContext->clearUAV(pBuf->getUAV().get(), float4(0.f)); + else + pRenderContext->clearUAV(pBuf->getUAV().get(), uint4(0)); } }; - prepareBuffer(mpLastFrameSum, ResourceFormat::RGBA32Float, mPrecisionMode == Precision::Single || mPrecisionMode == Precision::SingleCompensated); + prepareBuffer( + mpLastFrameSum, ResourceFormat::RGBA32Float, mPrecisionMode == Precision::Single || mPrecisionMode == Precision::SingleCompensated + ); prepareBuffer(mpLastFrameCorr, ResourceFormat::RGBA32Float, mPrecisionMode == Precision::SingleCompensated); prepareBuffer(mpLastFrameSumLo, ResourceFormat::RGBA32Uint, mPrecisionMode == Precision::Double); prepareBuffer(mpLastFrameSumHi, ResourceFormat::RGBA32Uint, mPrecisionMode == Precision::Double); diff --git a/Source/RenderPasses/AccumulatePass/AccumulatePass.h b/Source/RenderPasses/AccumulatePass/AccumulatePass.h index 0d348b348..aa751addc 100644 --- a/Source/RenderPasses/AccumulatePass/AccumulatePass.h +++ b/Source/RenderPasses/AccumulatePass/AccumulatePass.h @@ -32,15 +32,16 @@ using namespace Falcor; -/** Temporal accumulation render pass. - - This pass takes a texture as input and writes the temporally accumulated - result to an output texture. The pass keeps intermediate data internally. - - For accumulating many samples for ground truth rendering etc., fp32 precision - is not always sufficient. The pass supports higher precision modes using - either error compensation (Kahan summation) or double precision math. -*/ +/** + * Temporal accumulation render pass. + * + * This pass takes a texture as input and writes the temporally accumulated + * result to an output texture. The pass keeps intermediate data internally. + * + * For accumulating many samples for ground truth rendering etc., fp32 precision + * is not always sufficient. The pass supports higher precision modes using + * either error compensation (Kahan summation) or double precision math. + */ class AccumulatePass : public RenderPass { public: @@ -68,58 +69,85 @@ class AccumulatePass : public RenderPass enum class Precision : uint32_t { - Double, ///< Standard summation in double precision. - Single, ///< Standard summation in single precision. - SingleCompensated, ///< Compensated summation (Kahan summation) in single precision. + Double, ///< Standard summation in double precision. + Single, ///< Standard summation in single precision. + SingleCompensated, ///< Compensated summation (Kahan summation) in single precision. }; - FALCOR_ENUM_INFO(Precision, { - { Precision::Double, "Double" }, - { Precision::Single, "Single" }, - { Precision::SingleCompensated, "SingleCompensated" }, - }); + FALCOR_ENUM_INFO( + Precision, + { + {Precision::Double, "Double"}, + {Precision::Single, "Single"}, + {Precision::SingleCompensated, "SingleCompensated"}, + } + ); enum class OverflowMode : uint32_t { - Stop, ///< Stop accumulation and retain accumulated image. - Reset, ///< Reset accumulation. - EMA, ///< Switch to exponential moving average accumulation. + Stop, ///< Stop accumulation and retain accumulated image. + Reset, ///< Reset accumulation. + EMA, ///< Switch to exponential moving average accumulation. }; - FALCOR_ENUM_INFO(OverflowMode, { - { OverflowMode::Stop, "Stop" }, - { OverflowMode::Reset, "Reset" }, - { OverflowMode::EMA, "EMA" }, - }); + FALCOR_ENUM_INFO( + OverflowMode, + { + {OverflowMode::Stop, "Stop"}, + {OverflowMode::Reset, "Reset"}, + {OverflowMode::EMA, "EMA"}, + } + ); protected: void prepareAccumulation(RenderContext* pRenderContext, uint32_t width, uint32_t height); void accumulate(RenderContext* pRenderContext, const ref& pSrc, const ref& pDst); // Internal state - ref mpScene; ///< The current scene (or nullptr if no scene). - std::map> mpProgram; ///< Accumulation programs, one per mode. - ref mpVars; ///< Program variables. - ref mpState; - FormatType mSrcType; ///< Format type of the source that gets accumulated. - - uint32_t mFrameCount = 0; ///< Number of accumulated frames. This is reset upon changes. - uint2 mFrameDim = { 0, 0 }; ///< Current frame dimension in pixels. - ref mpLastFrameSum; ///< Last frame running sum. Used in Single and SingleKahan mode. - ref mpLastFrameCorr; ///< Last frame running compensation term. Used in SingleKahan mode. - ref mpLastFrameSumLo; ///< Last frame running sum (lo bits). Used in Double mode. - ref mpLastFrameSumHi; ///< Last frame running sum (hi bits). Used in Double mode. + + /// The current scene (or nullptr if no scene). + ref mpScene; + + /// Accumulation programs, one per mode. + std::map> mpProgram; + ref mpVars; + ref mpState; + + /// Format type of the source that gets accumulated. + FormatType mSrcType; + + /// Number of accumulated frames. This is reset upon changes. + uint32_t mFrameCount = 0; + /// Current frame dimension in pixels. + uint2 mFrameDim = {0, 0}; + /// Last frame running sum. Used in Single and SingleKahan mode. + ref mpLastFrameSum; + /// Last frame running compensation term. Used in SingleKahan mode. + ref mpLastFrameCorr; + /// Last frame running sum (lo bits). Used in Double mode. + ref mpLastFrameSumLo; + /// Last frame running sum (hi bits). Used in Double mode. + ref mpLastFrameSumHi; // UI variables - bool mEnabled = true; ///< True if accumulation is enabled. - bool mAutoReset = true; ///< Reset accumulation automatically upon scene changes and refresh flags. - Precision mPrecisionMode = Precision::Single; - uint32_t mMaxFrameCount = 0; ///< Maximum number of frames to accumulate before triggering overflow. 0 means infinite accumulation. - OverflowMode mOverflowMode = OverflowMode::Stop; ///< What to do after maximum number of frames are accumulated. - - ResourceFormat mOutputFormat = ResourceFormat::Unknown; ///< Output format (uses default when set to ResourceFormat::Unknown). - RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. - uint2 mFixedOutputSize = { 512, 512 }; ///< Output size in pixels when 'Fixed' size is selected. + + /// True if accumulation is enabled. + bool mEnabled = true; + /// Reset accumulation automatically upon scene changes and refresh flags. + bool mAutoReset = true; + + Precision mPrecisionMode = Precision::Single; + /// Maximum number of frames to accumulate before triggering overflow. 0 means infinite accumulation. + uint32_t mMaxFrameCount = 0; + /// What to do after maximum number of frames are accumulated. + OverflowMode mOverflowMode = OverflowMode::Stop; + + /// Output format (uses default when set to ResourceFormat::Unknown). + ResourceFormat mOutputFormat = ResourceFormat::Unknown; + /// Selected output size. + RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; + /// Output size in pixels when 'Fixed' size is selected. + uint2 mFixedOutputSize = {512, 512}; }; FALCOR_ENUM_REGISTER(AccumulatePass::Precision); diff --git a/Source/RenderPasses/BSDFOptimizer/BSDFOptimizer.cpp b/Source/RenderPasses/BSDFOptimizer/BSDFOptimizer.cpp new file mode 100644 index 000000000..97c98b490 --- /dev/null +++ b/Source/RenderPasses/BSDFOptimizer/BSDFOptimizer.cpp @@ -0,0 +1,401 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "BSDFOptimizer.h" +#include "RenderGraph/RenderPassStandardFlags.h" +#include "Rendering/Materials/BSDFConfig.slangh" + +extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) +{ + registry.registerClass(); + ScriptBindings::registerBinding(BSDFOptimizer::registerBindings); +} + +namespace +{ +const char kOptimizerPass[] = "RenderPasses/BSDFOptimizer/BSDFOptimizer.cs.slang"; +const char kViewerPass[] = "RenderPasses/BSDFOptimizer/BSDFViewer.cs.slang"; +const char kOutput[] = "output"; + +const char kViewerParameterBlockName[] = "gBSDFViewer"; + +// Scripting options. +const char kInitMaterialID[] = "initMaterialID"; +const char kRefMaterialID[] = "refMaterialID"; +} // namespace + +BSDFOptimizer::BSDFOptimizer(ref pDevice, const Properties& props) : RenderPass(pDevice) +{ + parseProperties(props); + + mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_UNIFORM); + + mpFence = mpDevice->createFence(); +} + +void BSDFOptimizer::parseProperties(const Properties& props) +{ + for (const auto& [key, value] : props) + { + if (key == kInitMaterialID) + mParams.initMaterialID = value; + else if (key == kRefMaterialID) + mParams.refMaterialID = value; + else + logWarning("Unknown property '{}' in BSDFOptimizer properties.", key); + } +} + +Properties BSDFOptimizer::getProperties() const +{ + Properties props; + props[kInitMaterialID] = mParams.initMaterialID; + props[kRefMaterialID] = mParams.refMaterialID; + return props; +} + +RenderPassReflection BSDFOptimizer::reflect(const CompileData& compileData) +{ + RenderPassReflection r; + r.addOutput(kOutput, "Output buffer").format(ResourceFormat::RGBA32Float).bindFlags(ResourceBindFlags::UnorderedAccess); + return r; +} + +void BSDFOptimizer::compile(RenderContext* pRenderContext, const CompileData& compileData) +{ + mParams.frameDim = compileData.defaultTexDims; + + // Set up viewports. Left: initial material, middle: absolute difference, right: reference material. + uint32_t extent = std::min(mParams.frameDim.x / 3, mParams.frameDim.y); + mParams.bsdfTableDim = uint2(extent, extent); + + uint32_t xOffset = (mParams.frameDim.x - extent * 3) / 2; + uint32_t yOffset = (mParams.frameDim.y - extent) / 2; + + mParams.initViewPortOffset = float2(xOffset, yOffset); + mParams.diffViewPortOffset = float2(xOffset + extent, yOffset); + mParams.refViewPortOffset = float2(xOffset + extent * 2, yOffset); + mParams.viewPortScale = float2(1.f / extent, 1.f / extent); +} + +void BSDFOptimizer::initOptimization() +{ + static const std::map> kLearningRates{ + {MaterialType::PBRTDiffuse, + { + {"diffuse", 1e-2f}, + }}, + {MaterialType::PBRTConductor, + { + {"eta", 1e-2f}, + {"k", 1e-2f}, + {"roughness", 1e-2f}, + }}, + {MaterialType::Standard, + { + {"base_color", 1e-2f}, + {"roughness", 3e-3f}, + {"metallic", 3e-3f}, + }}, + }; + + // Reset BSDF parameters. + mpScene->getMaterial(MaterialID(mParams.initMaterialID))->deserializeParams(mInitBSDFParams); + mParams.frameCount = 0; + + // Initialize current BSDF parameters. + mCurBSDFParams = mInitBSDFParams; + + // Set learning rates and adam optimizer. + std::vector lr(mCurBSDFParams.size(), 0.f); + const auto& pMaterial = mpScene->getMaterial(MaterialID{mParams.initMaterialID}); + + auto learningRateMap = kLearningRates.find(pMaterial->getType()); + if (learningRateMap != kLearningRates.end()) + { + for (const auto& param : pMaterial->getParamLayout()) + { + auto learningRate = learningRateMap->second.find(param.pythonName); + if (learningRate != learningRateMap->second.end()) + { + for (uint32_t i = 0; i < param.size; ++i) + lr[param.offset + i] = learningRate->second; + } + } + } + + mAdam = AdamOptimizer(lr); +} + +void BSDFOptimizer::setScene(RenderContext* pRenderContext, const ref& pScene) +{ + mpScene = pScene; + mMaterialList.clear(); + + if (mpScene == nullptr) + return; + + if (any(mParams.bsdfTableDim == uint2(0))) + setBSDFSliceResolution(128); + + // Create optimizer program. + { + ProgramDesc desc; + desc.addShaderModules(mpScene->getShaderModules()); + desc.addShaderLibrary(kOptimizerPass).csEntry("main"); + desc.addTypeConformances(mpScene->getTypeConformances()); + + DefineList defines; + defines.add(mpSampleGenerator->getDefines()); + defines.add(mpScene->getSceneDefines()); + + mpOptimizerPass = ComputePass::create(mpDevice, desc, defines); + } + + // Create viewer program. + { + ProgramDesc desc; + desc.addShaderModules(mpScene->getShaderModules()); + desc.addShaderLibrary(kViewerPass).csEntry("main"); + desc.addTypeConformances(mpScene->getTypeConformances()); + + DefineList defines; + defines.add(mpSampleGenerator->getDefines()); + defines.add(mpScene->getSceneDefines()); + + mpViewerPass = ComputePass::create(mpDevice, desc, defines); + } + + mpSceneGradients = std::make_unique(mpDevice, uint2(SerializedMaterialParams::kParamCount, 0), uint2(64, 0)); + + // Prepare initial and reference BSDF parameters for optimization. + mInitBSDFParams = mpScene->getMaterial(MaterialID(mParams.initMaterialID))->serializeParams(); + mRefBSDFParams = mpScene->getMaterial(MaterialID(mParams.refMaterialID))->serializeParams(); + + initOptimization(); + + // Prepare UI list of materials. + mMaterialList.reserve(mpScene->getMaterialCount()); + for (uint32_t i = 0; i < mpScene->getMaterialCount(); i++) + { + auto mtl = mpScene->getMaterial(MaterialID{i}); + std::string name = std::to_string(i) + ": " + mtl->getName(); + mMaterialList.push_back({i, name}); + } +} + +void BSDFOptimizer::executeOptimizerPass(RenderContext* pRenderContext) +{ + if (!mpScene) + return; + + mpSceneGradients->clearGrads(pRenderContext, GradientType::Material); + + auto var = mpOptimizerPass->getRootVar(); + var["CB"]["params"].setBlob(mParams); + mpSceneGradients->bindShaderData(var["gSceneGradients"]); + mpScene->bindShaderData(var["gScene"]); + + mpOptimizerPass->execute(pRenderContext, uint3(mParams.bsdfTableDim, 1)); + + mpSceneGradients->aggregateGrads(pRenderContext, GradientType::Material); +} + +void BSDFOptimizer::step(RenderContext* pRenderContext) +{ + auto pBuffer = mpSceneGradients->getGradsBuffer(GradientType::Material); + pBuffer->getBlob(mBSDFGrads.data(), 0, sizeof(float) * mBSDFGrads.size()); + + // Update BSDF parameters. + mAdam.step(mBSDFGrads, mCurBSDFParams); + mpScene->getMaterial(MaterialID(mParams.initMaterialID))->deserializeParams(mCurBSDFParams); +} + +void BSDFOptimizer::executeViewerPass(RenderContext* pRenderContext, const RenderData& renderData) +{ + auto pOutput = renderData.getTexture(kOutput); + if (!mpScene || mpScene->getMaterialCount() == 0) + { + pRenderContext->clearUAV(pOutput->getUAV().get(), uint4(0)); + return; + } + + auto var = mpViewerPass->getRootVar()[kViewerParameterBlockName]; + var["params"].setBlob(mParams); + var["output"] = pOutput; + mpScene->bindShaderData(mpViewerPass->getRootVar()["gScene"]); + + mpViewerPass->execute(pRenderContext, uint3(mParams.frameDim, 1)); +} + +void BSDFOptimizer::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + if (mpScene && is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded)) + { + FALCOR_THROW("This render pass does not support scene changes that require shader recompilation."); + } + + if (mRunOptimization) + { + executeOptimizerPass(pRenderContext); + step(pRenderContext); + + // Stop optimization if the error is small enough. + float relL1Error = 0.f; + for (size_t i = 0; i < mCurBSDFParams.size(); i++) + { + relL1Error += std::abs(mCurBSDFParams[i] - mRefBSDFParams[i]) / std::max(mRefBSDFParams[i], 1e-6f); + } + relL1Error /= mCurBSDFParams.size(); + if (relL1Error < 1e-3f) + { + mRunOptimization = false; + } + } + executeViewerPass(pRenderContext, renderData); + + mParams.frameCount++; +} + +void BSDFOptimizer::renderUI(Gui::Widgets& widget) +{ + if (!mpScene || mpScene->getMaterialCount() == 0) + { + widget.text("No scene/materials loaded."); + return; + } + + if (widget.button("Start optimization")) + { + if (mParams.frameCount > 0) + initOptimization(); + mRunOptimization = true; + } + + if (widget.button("Stop optimization")) + { + mRunOptimization = false; + } + + if (widget.button("Reset optimization")) + { + initOptimization(); + mRunOptimization = false; + } + + auto showMaterial = [&](uint32_t materialID) + { + const auto& pMaterial = mpScene->getMaterial(MaterialID{materialID}); + std::string label = std::to_string(materialID) + ": " + pMaterial->getName(); + if (auto materialGroup = widget.group(label)) + { + pMaterial->renderUI(materialGroup); + } + }; + + if (auto initMtlGroup = widget.group("Initial material", true)) + { + initMtlGroup.tooltip("Choose initial material to optimize.\n"); + + FALCOR_ASSERT(mMaterialList.size() > 0); + if (initMtlGroup.dropdown("Materials", mMaterialList, mParams.initMaterialID)) + { + mInitBSDFParams = mpScene->getMaterial(MaterialID(mParams.initMaterialID))->serializeParams(); + } + showMaterial(mParams.initMaterialID); + } + + if (auto refMtlGroup = widget.group("Reference material", true)) + { + refMtlGroup.tooltip("Choose reference material.\n"); + + FALCOR_ASSERT(mMaterialList.size() > 0); + if (refMtlGroup.dropdown("Materials", mMaterialList, mParams.refMaterialID)) + { + mRefBSDFParams = mpScene->getMaterial(MaterialID(mParams.refMaterialID))->serializeParams(); + } + showMaterial(mParams.refMaterialID); + } +} + +void BSDFOptimizer::AdamOptimizer::step(fstd::span dx, fstd::span x) +{ + if (lr.size() != dx.size() || lr.size() != x.size()) + { + logError("AdamOptimizer::step(): lr, dx, and x must have the same size."); + return; + } + + if (m.empty() || v.empty()) + { + m.resize(dx.size(), 0.f); + v.resize(dx.size(), 0.f); + } + + steps++; + for (size_t i = 0; i < dx.size(); i++) + { + if (lr[i] == 0.f) + continue; // Skip parameters with zero learning rate. + m[i] = beta1 * m[i] + (1.f - beta1) * dx[i]; + v[i] = beta2 * v[i] + (1.f - beta2) * dx[i] * dx[i]; + float mHat = m[i] / (1.f - std::pow(beta1, steps)); + float vHat = v[i] / (1.f - std::pow(beta2, steps)); + float delta = lr[i] * mHat / (std::sqrt(vHat) + epsilon); + x[i] -= delta; + } +} + +// Python bindings. + +uint32_t BSDFOptimizer::getBSDFSliceResolution() const +{ + FALCOR_ASSERT_EQ(mParams.bsdfTableDim.x, mParams.bsdfTableDim.y); + return mParams.bsdfTableDim.x; +} + +void BSDFOptimizer::setBSDFSliceResolution(uint32_t reso) +{ + mParams.bsdfTableDim = uint2(reso, reso); + mParams.viewPortScale = float2(1.f / reso, 1.f / reso); +} + +ref BSDFOptimizer::computeBSDFGrads() +{ + executeOptimizerPass(mpDevice->getRenderContext()); + return mpSceneGradients->getGradsBuffer(GradientType::Material); +} + +void BSDFOptimizer::registerBindings(pybind11::module& m) +{ + pybind11::class_> pass(m, "BSDFOptimizer"); + pass.def_property_readonly("init_material_id", &BSDFOptimizer::getInitMaterialID); + pass.def_property_readonly("ref_material_id", &BSDFOptimizer::getRefMaterialID); + pass.def_property("bsdf_slice_resolution", &BSDFOptimizer::getBSDFSliceResolution, &BSDFOptimizer::setBSDFSliceResolution); + + pass.def("compute_bsdf_grads", &BSDFOptimizer::computeBSDFGrads); +} diff --git a/Source/RenderPasses/BSDFOptimizer/BSDFOptimizer.cs.slang b/Source/RenderPasses/BSDFOptimizer/BSDFOptimizer.cs.slang new file mode 100644 index 000000000..af40893c1 --- /dev/null +++ b/Source/RenderPasses/BSDFOptimizer/BSDFOptimizer.cs.slang @@ -0,0 +1,121 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Utils/Math/MathConstants.slangh" + +import Scene.Shading; +import Scene.Material.ShadingUtils; +import Utils.Sampling.SampleGenerator; +import Utils.Math.BitTricks; +import Utils.Math.MathHelpers; +import BSDFOptimizerParams; +import BSDFOptimizerHelpers; + +cbuffer CB +{ + BSDFOptimizerParams params; +}; + +struct SurfaceData : IDifferentiable +{ + ShadingData sd; + float3 wo; +}; + +[Differentiable] +SurfaceData prepareShadingData(const VertexData v, const float3 viewDir, const uint materialID, const ITextureSampler lod) +{ + SurfaceData data = {}; + + // Setup Falcor's ShadingData based on the selected scene material and lobes. + data.sd = no_diff gScene.materials.prepareShadingData(v, materialID, viewDir, lod); + return data; +} + +[Differentiable] +IMaterialInstance getDiffMaterialInstance(out DiffMaterialData diffData, const ShadingData sd) +{ + let lod = ExplicitLodTextureSampler(0.f); + let material = gScene.materials.getMaterial(sd.materialID); + let mi = material.setupDiffMaterialInstance(diffData, gScene.materials, sd, lod); + return mi; +} + +[Differentiable] +float3 evalBSDFSlice(bool isRef, const uint2 pixel) +{ + float2 uv = no_diff getViewportCoord(pixel, float2(0.f), params.viewPortScale); + let lod = ExplicitLodTextureSampler(0.f); + SampleGenerator sg = SampleGenerator(pixel, params.frameCount); + + // Calculate geometry and incident/outgoing directions. + VertexData v; + float3 viewDir; + float3 lightDir = no_diff calculateSliceGeometry(uv, v, viewDir); + + // Setup shading data. + SurfaceData data = prepareShadingData(v, viewDir, isRef ? params.refMaterialID : params.initMaterialID, lod); + data.wo = detach(lightDir); + + // Set offset for writing gradients. + data.sd.materialGradOffset = 0; + data.sd.threadID = 0; + + // Create differentiable BSDF instance and evaluate BSDF at shading point. + DiffMaterialData diffData = DiffMaterialData(); + let mi = getDiffMaterialInstance(diffData, data.sd); + float3 f = isRef ? no_diff mi.eval(data.sd, data.wo, sg) : mi.evalAD(diffData, data.sd, data.wo, sg); + + // Remove cosine term. + float NdotL = abs(dot(data.sd.frame.N, data.wo)); + f = NdotL > 0.f ? f / NdotL : float3(0); + + return f; +} + +[Differentiable] +float3 computeLoss(const uint2 pixel) +{ + float3 fRef = evalBSDFSlice(true, pixel); + float3 fCur = evalBSDFSlice(false, pixel); + + float3 diff = fCur - fRef; + return 0.5 * diff * diff; +} + +void execute(const uint2 pixel) +{ + if (any(pixel >= params.bsdfTableDim)) + return; + bwd_diff(computeLoss)(pixel, float3(1.f)); +} + +[numthreads(16, 16, 1)] +void main(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + execute(dispatchThreadID.xy); +} diff --git a/Source/RenderPasses/BSDFOptimizer/BSDFOptimizer.h b/Source/RenderPasses/BSDFOptimizer/BSDFOptimizer.h new file mode 100644 index 000000000..ffc4d8a53 --- /dev/null +++ b/Source/RenderPasses/BSDFOptimizer/BSDFOptimizer.h @@ -0,0 +1,117 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include "Falcor.h" +#include "RenderGraph/RenderPass.h" +#include "Utils/Sampling/SampleGenerator.h" +#include "DiffRendering/SceneGradients.h" +#include "BSDFOptimizerParams.slang" +#include + +using namespace Falcor; + +class BSDFOptimizer : public RenderPass +{ +public: + FALCOR_PLUGIN_CLASS(BSDFOptimizer, "BSDFOptimizer", "Optimizing BSDF parameters with differentiable materials."); + + static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } + + BSDFOptimizer(ref pDevice, const Properties& props); + + virtual Properties getProperties() const override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; + virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } + virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } + + static void registerBindings(pybind11::module& m); + uint32_t getInitMaterialID() const { return mParams.initMaterialID; } + uint32_t getRefMaterialID() const { return mParams.refMaterialID; } + uint32_t getBSDFSliceResolution() const; + void setBSDFSliceResolution(uint32_t reso); + ref computeBSDFGrads(); + +private: + void parseProperties(const Properties& props); + + void initOptimization(); + + void executeOptimizerPass(RenderContext* pRenderContext); + void step(RenderContext* pRenderContext); + void executeViewerPass(RenderContext* pRenderContext, const RenderData& renderData); + + struct AdamOptimizer + { + std::vector lr; + float beta1; + float beta2; + float epsilon; + int steps; + + std::vector m; + std::vector v; + + AdamOptimizer() {} + + AdamOptimizer(fstd::span lr, float beta1 = 0.9f, float beta2 = 0.999f, float epsilon = 1e-6f) + : lr(lr.begin(), lr.end()), beta1(beta1), beta2(beta2), epsilon(epsilon), steps(0) + {} + + void step(fstd::span dx, fstd::span x); + }; + + // Internal state + ref mpScene; ///< Loaded scene if any, nullptr otherwise. + std::unique_ptr mpSceneGradients; + + SerializedMaterialParams mInitBSDFParams; + SerializedMaterialParams mRefBSDFParams; + + SerializedMaterialParams mCurBSDFParams; + SerializedMaterialParams mBSDFGrads; + AdamOptimizer mAdam; + + /// Parameters shared with the shaders. + BSDFOptimizerParams mParams; + ref mpSampleGenerator; + bool mOptionsChanged = false; + + /// GPU fence for synchronizing readback. + ref mpFence; + + ref mpOptimizerPass; + ref mpViewerPass; + + // UI variables + Gui::DropdownList mMaterialList; + bool mRunOptimization = false; +}; diff --git a/Source/RenderPasses/BSDFOptimizer/BSDFOptimizerHelpers.slang b/Source/RenderPasses/BSDFOptimizer/BSDFOptimizerHelpers.slang new file mode 100644 index 000000000..75d7aaea1 --- /dev/null +++ b/Source/RenderPasses/BSDFOptimizer/BSDFOptimizerHelpers.slang @@ -0,0 +1,71 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Utils/Math/MathConstants.slangh" + +import Scene.Shading; + +float2 getViewportCoord(uint2 pixel, float2 viewportOffset, float2 viewportScale) +{ + float2 p = pixel + float2(0.5f); + return (p - viewportOffset) * viewportScale; +} + +float3 calculateSliceGeometry(float2 uv, out VertexData v, out float3 viewDir) +{ + // Setup local surface frame as T,B,N. + v.posW = float3(0, 0, 0); + v.normalW = float3(0, 0, 1); + v.tangentW = float4(1, 0, 0, 1); + v.texC = float2(0, 0); // Assume no textures. + v.faceNormalW = v.normalW; + + // Compute dot products. + // These are based on the axes in the 2D slice (theta_h, theta_d) with origin in lower-left corner. + // This is the same format as the slices in Burley et al. 2012, 2015. + float theta_h = uv.x * (M_PI / 2); + float theta_d = (1.f - uv.y) * (M_PI / 2); + + float NdotH = cos(theta_h); + float HdotL = cos(theta_d); // Note: HdotL = HdotV + + // Place the H vector at (0,0,1) to start. + // Compute L, V that are mirrored about the yz-plane. + float3 L = float3(sqrt(1.f - HdotL * HdotL), 0, HdotL); + float3 V = float3(-L.x, 0.f, L.z); + + // Rotate L, V about the x-axis by an angle theta_h. + float cos_h = NdotH; + float sin_h = sqrt(1 - NdotH * NdotH); + L = float3(L.x, cos_h * L.y - sin_h * L.z, sin_h * L.y + cos_h * L.z); + V = float3(V.x, cos_h * V.y - sin_h * V.z, sin_h * V.y + cos_h * V.z); + + // Return vectors. + viewDir = V; + L = normalize(L); + return L; +} diff --git a/Source/RenderPasses/BSDFOptimizer/BSDFOptimizerParams.slang b/Source/RenderPasses/BSDFOptimizer/BSDFOptimizerParams.slang new file mode 100644 index 000000000..6fbf22f20 --- /dev/null +++ b/Source/RenderPasses/BSDFOptimizer/BSDFOptimizerParams.slang @@ -0,0 +1,56 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include "Utils/HostDeviceShared.slangh" + +BEGIN_NAMESPACE_FALCOR + +/** + * BSDFOptimizer parameters shared between host and device. + * Make sure struct layout follows the HLSL packing rules as it is uploaded as a memory blob. + * Do not use bool's as they are 1 byte in Visual Studio, 4 bytes in HLSL. + * https://msdn.microsoft.com/en-us/library/windows/desktop/bb509632(v=vs.85).aspx + */ +struct BSDFOptimizerParams +{ + uint2 frameDim = { 0, 0 }; + uint frameCount = 0; + int _pad0; + + float2 initViewPortOffset; + float2 diffViewPortOffset; + + float2 refViewPortOffset; + float2 viewPortScale; + + uint2 bsdfTableDim = { 0, 0 }; + uint initMaterialID = 0; + uint refMaterialID = 1; +}; + +END_NAMESPACE_FALCOR diff --git a/Source/RenderPasses/BSDFOptimizer/BSDFViewer.cs.slang b/Source/RenderPasses/BSDFOptimizer/BSDFViewer.cs.slang new file mode 100644 index 000000000..b484db6bf --- /dev/null +++ b/Source/RenderPasses/BSDFOptimizer/BSDFViewer.cs.slang @@ -0,0 +1,134 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Utils/Math/MathConstants.slangh" + +import Scene.Shading; +import Scene.Material.ShadingUtils; +import Utils.Sampling.SampleGenerator; +import Utils.Math.BitTricks; +import Utils.Math.MathHelpers; +import BSDFOptimizerParams; +import BSDFOptimizerHelpers; + +struct BSDFViewer +{ + BSDFOptimizerParams params; + RWTexture2D output; + + struct SurfaceData + { + ShadingData sd; + BSDFProperties bsdfProperties; + float3 wo; + }; + + SurfaceData prepareShadingData(const VertexData v, const float3 viewDir, const uint materialID, const ITextureSampler lod) + { + SurfaceData data = {}; + + // Setup Falcor's ShadingData based on the selected scene material and lobes. + data.sd = gScene.materials.prepareShadingData(v, materialID, viewDir, lod); + + return data; + } + + float3 evalBSDFSlice(const float2 uv, const uint materialID, const ITextureSampler lod, inout SampleGenerator sg) + { + // Calculate geometry and incident/outgoing directions. + VertexData v; + float3 viewDir; + float3 lightDir = calculateSliceGeometry(uv, v, viewDir); + + // Setup shading data. + SurfaceData data = prepareShadingData(v, viewDir, materialID, lod); + data.wo = lightDir; + + // Create BSDF instance. + uint hints = 0; + let mi = gScene.materials.getMaterialInstance(data.sd, lod, hints); + data.bsdfProperties = mi.getProperties(data.sd); + + // Evaluate BSDF at shading point. + float3 f = mi.eval(data.sd, data.wo, sg); + + // Remove cosine term. + float NdotL = abs(dot(data.sd.frame.N, data.wo)); + f = NdotL > 0.f ? f / NdotL : float3(0); + + return f; + } + + void execute(const uint2 pixel) + { + if (any(pixel >= params.frameDim)) + return; + + uint viewportID = 0; + float2 viewportOffset = params.initViewPortOffset; + + if (pixel.x >= params.refViewPortOffset.x) + { + viewportID = 2; + viewportOffset = params.refViewPortOffset; + } + else if (pixel.x >= params.diffViewPortOffset.x) + { + viewportID = 1; + viewportOffset = params.diffViewPortOffset; + } + + float2 uv = getViewportCoord(pixel, viewportOffset, params.viewPortScale); + if (any(uv < 0.f || uv >= 1.f)) + { + output[pixel] = float4(0.f, 0.f, 0.f, 1.f); + return; + } + + let lod = ExplicitLodTextureSampler(0.f); + SampleGenerator sg = SampleGenerator(pixel, params.frameCount); + + float3 fInitVal = evalBSDFSlice(uv, params.initMaterialID, lod, sg); + float3 fRefVal = evalBSDFSlice(uv, params.refMaterialID, lod, sg); + float3 fDiffVal = abs(fInitVal - fRefVal); + + if (viewportID == 0) + output[pixel] = float4(fInitVal, 1.f); + else if (viewportID == 1) + output[pixel] = float4(fDiffVal, 1.f); + else + output[pixel] = float4(fRefVal, 1.f); + } +}; + +ParameterBlock gBSDFViewer; + +[numthreads(16, 16, 1)] +void main(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + gBSDFViewer.execute(dispatchThreadID.xy); +} diff --git a/Source/RenderPasses/BSDFOptimizer/CMakeLists.txt b/Source/RenderPasses/BSDFOptimizer/CMakeLists.txt new file mode 100644 index 000000000..bf795855c --- /dev/null +++ b/Source/RenderPasses/BSDFOptimizer/CMakeLists.txt @@ -0,0 +1,14 @@ +add_plugin(BSDFOptimizer) + +target_sources(BSDFOptimizer PRIVATE + BSDFOptimizer.cpp + BSDFOptimizer.cs.slang + BSDFOptimizer.h + BSDFOptimizerHelpers.slang + BSDFOptimizerParams.slang + BSDFViewer.cs.slang +) + +target_copy_shaders(BSDFOptimizer RenderPasses/BSDFOptimizer) + +target_source_group(BSDFOptimizer "RenderPasses") diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp b/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp index 2d669c9d2..461b02d94 100644 --- a/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.cpp @@ -36,20 +36,19 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr namespace { - const char kFileViewerPass[] = "RenderPasses/BSDFViewer/BSDFViewer.cs.slang"; - const char kParameterBlockName[] = "gBSDFViewer"; - const char kOutput[] = "output"; - - // Scripting options. - const char kMaterialID[] = "materialID"; - const char kViewerMode[] = "viewerMode"; - const char kUseEnvMap[] = "useEnvMap"; - const char kTexCoords[] = "texCoords"; - const char kOutputAlbedo[] = "outputAlbedo"; -} - -BSDFViewer::BSDFViewer(ref pDevice, const Properties& props) - : RenderPass(pDevice) +const char kFileViewerPass[] = "RenderPasses/BSDFViewer/BSDFViewer.cs.slang"; +const char kParameterBlockName[] = "gBSDFViewer"; +const char kOutput[] = "output"; + +// Scripting options. +const char kMaterialID[] = "materialID"; +const char kViewerMode[] = "viewerMode"; +const char kUseEnvMap[] = "useEnvMap"; +const char kTexCoords[] = "texCoords"; +const char kOutputAlbedo[] = "outputAlbedo"; +} // namespace + +BSDFViewer::BSDFViewer(ref pDevice, const Properties& props) : RenderPass(pDevice) { parseProperties(props); @@ -57,23 +56,28 @@ BSDFViewer::BSDFViewer(ref pDevice, const Properties& props) mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_UNIFORM); mpPixelDebug = std::make_unique(mpDevice); - mpFence = GpuFence::create(mpDevice); + mpFence = mpDevice->createFence(); } void BSDFViewer::parseProperties(const Properties& props) { for (const auto& [key, value] : props) { - if (key == kMaterialID) mParams.materialID = value; - else if (key == kViewerMode) mParams.viewerMode = value; - else if (key == kUseEnvMap) mUseEnvMap = value; + if (key == kMaterialID) + mParams.materialID = value; + else if (key == kViewerMode) + mParams.viewerMode = value; + else if (key == kUseEnvMap) + mUseEnvMap = value; else if (key == kTexCoords) { mParams.useFixedTexCoords = true; mParams.texCoords = value; } - else if (key == kOutputAlbedo) mParams.outputAlbedo = value; - else logWarning("Unknown property '{}' in BSDFViewer properties.", key); + else if (key == kOutputAlbedo) + mParams.outputAlbedo = value; + else + logWarning("Unknown property '{}' in BSDFViewer properties.", key); } } @@ -83,8 +87,10 @@ Properties BSDFViewer::getProperties() const props[kMaterialID] = mParams.materialID; props[kViewerMode] = mParams.viewerMode; props[kUseEnvMap] = mUseEnvMap; - if (mParams.useFixedTexCoords) props[kTexCoords] = mParams.texCoords; - if (mParams.outputAlbedo != 0) props[kOutputAlbedo] = mParams.outputAlbedo; + if (mParams.useFixedTexCoords) + props[kTexCoords] = mParams.texCoords; + if (mParams.outputAlbedo != 0) + props[kOutputAlbedo] = mParams.outputAlbedo; return props; } @@ -119,7 +125,7 @@ void BSDFViewer::setScene(RenderContext* pRenderContext, const ref& pScen if (mpScene != nullptr) { // Create program. - Program::Desc desc; + ProgramDesc desc; desc.addShaderModules(mpScene->getShaderModules()); desc.addShaderLibrary(kFileViewerPass).csEntry("main"); desc.addTypeConformances(mpScene->getTypeConformances()); @@ -128,11 +134,7 @@ void BSDFViewer::setScene(RenderContext* pRenderContext, const ref& pScen defines.add(mpSampleGenerator->getDefines()); defines.add(mpScene->getSceneDefines()); - mpViewerPass = ComputePass::create(mpDevice, desc, defines, false); - - // Compile program and bind the scene. - mpViewerPass->setVars(nullptr); // Trigger vars creation - mpViewerPass->getRootVar()["gScene"] = mpScene->getParameterBlock(); + mpViewerPass = ComputePass::create(mpDevice, desc, defines); // Setup environment map. mpEnvMap = mpScene->getEnvMap(); @@ -149,9 +151,9 @@ void BSDFViewer::setScene(RenderContext* pRenderContext, const ref& pScen mMaterialList.reserve(materialCount); for (uint32_t i = 0; i < materialCount; i++) { - auto mtl = mpScene->getMaterial(MaterialID{ i }); + auto mtl = mpScene->getMaterial(MaterialID{i}); std::string name = std::to_string(i) + ": " + mtl->getName(); - mMaterialList.push_back({ i, name }); + mMaterialList.push_back({i, name}); } } } @@ -174,15 +176,24 @@ void BSDFViewer::execute(RenderContext* pRenderContext, const RenderData& render return; } + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded)) + { + FALCOR_THROW("This render pass does not support scene changes that require shader recompilation."); + } + // Read back pixel data from the previous frame if available. // This ensures parameters get updated even if the UI wasn't rendered. readPixelData(); // Set compile-time constants. - if (mParams.useDisneyDiffuse) mpViewerPass->addDefine("DiffuseBrdf", "DiffuseBrdfDisney"); - else mpViewerPass->removeDefine("DiffuseBrdf"); - if (mParams.useSeparableMaskingShadowing) mpViewerPass->addDefine("SpecularMaskingFunction", "SpecularMaskingFunctionSmithGGXSeparable"); - else mpViewerPass->removeDefine("SpecularMaskingFunction"); + if (mParams.useDisneyDiffuse) + mpViewerPass->addDefine("DiffuseBrdf", "DiffuseBrdfDisney"); + else + mpViewerPass->removeDefine("DiffuseBrdf"); + if (mParams.useSeparableMaskingShadowing) + mpViewerPass->addDefine("SpecularMaskingFunction", "SpecularMaskingFunctionSmithGGXSeparable"); + else + mpViewerPass->removeDefine("SpecularMaskingFunction"); // Setup constants. mParams.cameraViewportScale = std::tan(math::radians(mParams.cameraFovY / 2.f)) * mParams.cameraDistance; @@ -193,16 +204,21 @@ void BSDFViewer::execute(RenderContext* pRenderContext, const RenderData& render if (!mpPixelDataBuffer) { - mpPixelDataBuffer = Buffer::createStructured(mpDevice, var["pixelData"], 1, ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpPixelStagingBuffer = Buffer::createStructured(mpDevice, var["pixelData"], 1, ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr, false); + mpPixelDataBuffer = mpDevice->createStructuredBuffer( + var["pixelData"], 1, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false + ); + mpPixelStagingBuffer = + mpDevice->createStructuredBuffer(var["pixelData"], 1, ResourceBindFlags::None, MemoryType::ReadBack, nullptr, false); } var["params"].setBlob(mParams); var["outputColor"] = pOutput; var["pixelData"] = mpPixelDataBuffer; - if (mParams.useEnvMap) mpEnvMap->setShaderData(var["envMap"]); - mpSampleGenerator->setShaderData(mpViewerPass->getVars()->getRootVar()); + if (mParams.useEnvMap) + mpEnvMap->bindShaderData(var["envMap"]); + mpSampleGenerator->bindShaderData(mpViewerPass->getRootVar()); + mpScene->bindShaderData(mpViewerPass->getRootVar()["gScene"]); mpPixelDebug->beginFrame(pRenderContext, renderData.getDefaultTextureDims()); mpPixelDebug->prepareProgram(mpViewerPass->getProgram(), mpViewerPass->getRootVar()); @@ -213,8 +229,8 @@ void BSDFViewer::execute(RenderContext* pRenderContext, const RenderData& render // Copy pixel data to staging buffer for readback. // This is to avoid a full flush and the associated perf warning. pRenderContext->copyResource(mpPixelStagingBuffer.get(), mpPixelDataBuffer.get()); - pRenderContext->flush(false); - mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + pRenderContext->submit(false); + pRenderContext->signal(mpFence.get()); mPixelDataAvailable = true; mPixelDataValid = false; @@ -226,7 +242,7 @@ void BSDFViewer::readPixelData() { if (mPixelDataAvailable) { - mpFence->syncCpu(); + mpFence->wait(); FALCOR_ASSERT(mpPixelStagingBuffer); mPixelData = *static_cast(mpPixelStagingBuffer->map(Buffer::MapType::Read)); mpPixelStagingBuffer->unmap(); @@ -254,16 +270,20 @@ void BSDFViewer::renderUI(Gui::Widgets& widget) switch (mParams.viewerMode) { case BSDFViewerMode::Material: - widget.text("The current mode shows a shaded unit sphere.\n" + widget.text( + "The current mode shows a shaded unit sphere.\n" "The coordinate frame is right-handed with xy\n" "pointing right/up and +z towards the viewer.\n" - " "); + " " + ); break; case BSDFViewerMode::Slice: - widget.text("The current mode shows a slice of the BSDF.\n" + widget.text( + "The current mode shows a slice of the BSDF.\n" "The x-axis is theta_h (angle between H and normal)\n" "and y-axis is theta_d (angle between H and wi/wo),\n" - "both in [0,pi/2] with origin in the lower/left."); + "both in [0,pi/2] with origin in the lower/left." + ); break; default: FALCOR_UNREACHABLE(); @@ -271,19 +291,23 @@ void BSDFViewer::renderUI(Gui::Widgets& widget) if (auto mtlGroup = widget.group("Material", true)) { - mtlGroup.tooltip("Choose material in the dropdown below.\n\n" - "Left/right arrow keys step to the previous/next material in the list.", true); + mtlGroup.tooltip( + "Choose material in the dropdown below.\n\n" + "Left/right arrow keys step to the previous/next material in the list.", + true + ); FALCOR_ASSERT(mMaterialList.size() > 0); dirty |= mtlGroup.dropdown("Materials", mMaterialList, mParams.materialID); - auto type = mpScene->getMaterial(MaterialID{ mParams.materialID })->getType(); + auto type = mpScene->getMaterial(MaterialID{mParams.materialID})->getType(); mtlGroup.text("Material type: " + to_string(type)); dirty |= mtlGroup.checkbox("Use normal mapping", mParams.useNormalMapping); mtlGroup.tooltip("This is a hint that normal mapping should be used if supported by the material.", true); dirty |= mtlGroup.checkbox("Fixed tex coords", mParams.useFixedTexCoords); - dirty |= mtlGroup.var("Tex coords", mParams.texCoords, -std::numeric_limits::max(), std::numeric_limits::max(), 0.01f); + dirty |= + mtlGroup.var("Tex coords", mParams.texCoords, -std::numeric_limits::max(), std::numeric_limits::max(), 0.01f); } if (auto bsdfGroup = widget.group("BSDF", true)) @@ -292,14 +316,32 @@ void BSDFViewer::renderUI(Gui::Widgets& widget) dirty |= bsdfGroup.checkbox("Enable specular", mParams.enableSpecular, true); dirty |= bsdfGroup.checkbox("Use Disney' diffuse BRDF", mParams.useDisneyDiffuse); - bsdfGroup.tooltip("When enabled uses the original Disney diffuse BRDF, otherwise use Falcor's default (Frostbite's version).", true); + bsdfGroup.tooltip( + "When enabled uses the original Disney diffuse BRDF, " + "otherwise use Falcor's default (Frostbite's version).", + true + ); dirty |= bsdfGroup.checkbox("Use separable masking-shadowing", mParams.useSeparableMaskingShadowing); - bsdfGroup.tooltip("Use the separable form of Smith's masking-shadowing function which is used by the original Disney BRDF, otherwise use Falcor's default (the correlated form).", true); + bsdfGroup.tooltip( + "Use the separable form of Smith's masking-shadowing function " + "which is used by the original Disney BRDF, otherwise use " + "Falcor's default (the correlated form).", + true + ); dirty |= bsdfGroup.checkbox("Use importance sampling", mParams.useImportanceSampling); - bsdfGroup.tooltip("When enabled uses BSDF importance sampling, otherwise hemispherical cosine-weighted sampling for verification purposes.", true); + bsdfGroup.tooltip( + "When enabled uses BSDF importance sampling, otherwise " + "hemispherical cosine-weighted sampling for verification purposes.", + true + ); dirty |= bsdfGroup.checkbox("Use pdf", mParams.usePdf); - bsdfGroup.tooltip("When enabled evaluates BRDF * NdotL / pdf explicitly for verification purposes.\nOtherwise the weight computed by the importance sampling is used.", true); + bsdfGroup.tooltip( + "When enabled evaluates BRDF * NdotL / pdf explicitly for " + "verification purposes.\nOtherwise the weight computed by the " + "importance sampling is used.", + true + ); bsdfGroup.dummy("#space1", float2(1, 8)); @@ -315,7 +357,11 @@ void BSDFViewer::renderUI(Gui::Widgets& widget) bool specularTransmission = (mParams.outputAlbedo & (uint32_t)AlbedoSelection::SpecularTransmission) != 0; dirty |= bsdfGroup.checkbox("Show albedo", showAlbedo); - bsdfGroup.tooltip("If enabled, the albedo is output instead of reflectance.\nThe checkboxes indicate which albedo components are included in the total.", true); + bsdfGroup.tooltip( + "If enabled, the albedo is output instead of reflectance.\n" + "The checkboxes indicate which albedo components are included in the total.", + true + ); if (showAlbedo) { dirty |= bsdfGroup.checkbox("Diffuse reflection", diffuseReflection); @@ -323,18 +369,22 @@ void BSDFViewer::renderUI(Gui::Widgets& widget) dirty |= bsdfGroup.checkbox("Specular reflection", specularReflection); dirty |= bsdfGroup.checkbox("Specular transmission", specularTransmission); } - mParams.outputAlbedo = (showAlbedo ? (uint32_t)AlbedoSelection::ShowAlbedo : 0) - | (diffuseReflection ? (uint32_t)AlbedoSelection::DiffuseReflection : 0) - | (diffuseTransmission ? (uint32_t)AlbedoSelection::DiffuseTransmission : 0) - | (specularReflection ? (uint32_t)AlbedoSelection::SpecularReflection : 0) - | (specularTransmission ? (uint32_t)AlbedoSelection::SpecularTransmission : 0); + mParams.outputAlbedo = (showAlbedo ? (uint32_t)AlbedoSelection::ShowAlbedo : 0) | + (diffuseReflection ? (uint32_t)AlbedoSelection::DiffuseReflection : 0) | + (diffuseTransmission ? (uint32_t)AlbedoSelection::DiffuseTransmission : 0) | + (specularReflection ? (uint32_t)AlbedoSelection::SpecularReflection : 0) | + (specularTransmission ? (uint32_t)AlbedoSelection::SpecularTransmission : 0); } else if (mParams.viewerMode == BSDFViewerMode::Slice) { bsdfGroup.text("Slice viewer settings:"); dirty |= bsdfGroup.checkbox("Multiply BSDF slice by NdotL", mParams.applyNdotL); - bsdfGroup.tooltip("Note: This setting Only affects the BSDF slice viewer. NdotL is always enabled in other viewer modes.", true); + bsdfGroup.tooltip( + "Note: This setting Only affects the BSDF slice viewer. " + "NdotL is always enabled in other viewer modes.", + true + ); } } @@ -345,23 +395,43 @@ void BSDFViewer::renderUI(Gui::Widgets& widget) lightGroup.tooltip("Not used when environment map is enabled.", true); dirty |= lightGroup.checkbox("Show ground plane", mParams.useGroundPlane); - lightGroup.tooltip("When the ground plane is enabled, incident illumination from the lower hemisphere is zero.", true); + lightGroup.tooltip( + "When the ground plane is enabled, incident illumination " + "from the lower hemisphere is zero.", + true + ); // Directional lighting dirty |= lightGroup.checkbox("Directional light", mParams.useDirectionalLight); - lightGroup.tooltip("When enabled a single directional light source is used, otherwise the light is omnidirectional.", true); + lightGroup.tooltip( + "When enabled a single directional light source is used, " + "otherwise the light is omnidirectional.", + true + ); if (mParams.useDirectionalLight) { mUseEnvMap = false; - dirty |= lightGroup.var("Light direction", mParams.lightDir, -std::numeric_limits::max(), std::numeric_limits::max(), 0.01f, false, "%.4f"); + dirty |= lightGroup.var( + "Light direction", + mParams.lightDir, + -std::numeric_limits::max(), + std::numeric_limits::max(), + 0.01f, + false, + "%.4f" + ); } // Envmap lighting if (mpEnvMap) { dirty |= lightGroup.checkbox(("Environment map: " + mpEnvMap->getPath().string()).c_str(), mUseEnvMap); - lightGroup.tooltip("When enabled the specified environment map is used as light source. Enabling this option turns off directional lighting.", true); + lightGroup.tooltip( + "When enabled the specified environment map is used as light source. " + "Enabling this option turns off directional lighting.", + true + ); if (mUseEnvMap) { @@ -397,8 +467,13 @@ void BSDFViewer::renderUI(Gui::Widgets& widget) if (!mParams.orthographicCamera) { - dirty |= cameraGroup.var("Viewing distance", mParams.cameraDistance, 1.01f, std::numeric_limits::max(), 0.01f, false, "%.2f"); - cameraGroup.tooltip("This is the camera's distance to origin in projective mode. The scene has radius 1.0 so the minimum camera distance has to be > 1.0", true); + dirty |= + cameraGroup.var("Viewing distance", mParams.cameraDistance, 1.01f, std::numeric_limits::max(), 0.01f, false, "%.2f"); + cameraGroup.tooltip( + "This is the camera's distance to origin in projective mode. " + "The scene has radius 1.0 so the minimum camera distance has to be > 1.0", + true + ); dirty |= cameraGroup.var("Vertical FOV (degrees)", mParams.cameraFovY, 1.f, 179.f, 1.f, false, "%.2f"); cameraGroup.tooltip("The allowed range is [1,179] degrees to avoid numerical issues.", true); @@ -414,7 +489,9 @@ void BSDFViewer::renderUI(Gui::Widgets& widget) if (mPixelDataValid) { - pixelGroup.var("texC", mPixelData.texC, -std::numeric_limits::max(), std::numeric_limits::max(), 0.f, false, "%.4f"); + pixelGroup.var( + "texC", mPixelData.texC, -std::numeric_limits::max(), std::numeric_limits::max(), 0.f, false, "%.4f" + ); pixelGroup.var("T", mPixelData.T, -1.f, 1.f, 0.f, false, "%.4f"); pixelGroup.var("B", mPixelData.B, -1.f, 1.f, 0.f, false, "%.4f"); pixelGroup.var("N", mPixelData.N, -1.f, 1.f, 0.f, false, "%.4f"); @@ -428,11 +505,11 @@ void BSDFViewer::renderUI(Gui::Widgets& widget) pixelGroup.var("guideNormal", mPixelData.guideNormal, -1.f, 1.f, 0.f, false, "%.4f"); pixelGroup.var("emission", mPixelData.emission, 0.f, 1.f, 0.f, false, "%.4f"); pixelGroup.var("roughness", mPixelData.roughness, 0.f, 1.f, 0.f, false, "%.4f"); - pixelGroup.var("diffuseReflectionAlbedo", mPixelData.diffuseReflectionAlbedo, 0.f, std::numeric_limits::max(), 0.f, false, "%.4f"); - pixelGroup.var("diffuseTransmissionAlbedo", mPixelData.diffuseTransmissionAlbedo, 0.f, std::numeric_limits::max(), 0.f, false, "%.4f"); - pixelGroup.var("specularReflectionAlbedo", mPixelData.specularReflectionAlbedo, 0.f, std::numeric_limits::max(), 0.f, false, "%.4f"); - pixelGroup.var("specularTransmissionAlbedo", mPixelData.specularTransmissionAlbedo, 0.f, std::numeric_limits::max(), 0.f, false, "%.4f"); - pixelGroup.var("specularReflectance", mPixelData.specularReflectance, 0.f, std::numeric_limits::max(), 0.f, false, "%.4f"); + pixelGroup.var("diffuseReflectionAlbedo", mPixelData.diffuseReflectionAlbedo, 0.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("diffuseTransmissionAlbedo", mPixelData.diffuseTransmissionAlbedo, 0.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("specularReflectionAlbedo", mPixelData.specularReflectionAlbedo, 0.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("specularTransmissionAlbedo", mPixelData.specularTransmissionAlbedo, 0.f, 1.f, 0.f, false, "%.4f"); + pixelGroup.var("specularReflectance", mPixelData.specularReflectance, 0.f, 1.f, 0.f, false, "%.4f"); pixelGroup.checkbox("isTransmissive", mPixelData.isTransmissive); } else @@ -456,7 +533,7 @@ bool BSDFViewer::onMouseEvent(const MouseEvent& mouseEvent) { if (mouseEvent.type == MouseEvent::Type::ButtonDown && mouseEvent.button == Input::MouseButton::Left) { - mParams.selectedPixel = clamp((int2)(mouseEvent.pos * (float2)mParams.frameDim), { 0,0 }, (int2)mParams.frameDim - 1); + mParams.selectedPixel = clamp((int2)(mouseEvent.pos * (float2)mParams.frameDim), {0, 0}, (int2)mParams.frameDim - 1); } return mpPixelDebug->onMouseEvent(mouseEvent); @@ -470,10 +547,13 @@ bool BSDFViewer::onKeyEvent(const KeyboardEvent& keyEvent) { uint32_t id = mParams.materialID; uint32_t lastId = mMaterialList.size() > 0 ? (uint32_t)mMaterialList.size() - 1 : 0; - if (keyEvent.key == Input::Key::Left) id = id > 0 ? id - 1 : lastId; - else if (keyEvent.key == Input::Key::Right) id = id < lastId ? id + 1 : 0; + if (keyEvent.key == Input::Key::Left) + id = id > 0 ? id - 1 : lastId; + else if (keyEvent.key == Input::Key::Right) + id = id < lastId ? id + 1 : 0; - if (id != mParams.materialID) mOptionsChanged = true; // Triggers reset of accumulation + if (id != mParams.materialID) + mOptionsChanged = true; // Triggers reset of accumulation mParams.materialID = id; return true; } diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang b/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang index f49a6d3c6..d1dc24b8b 100644 --- a/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.cs.slang @@ -52,23 +52,25 @@ struct BSDFViewer float3 wo; }; - /** Get normalized viewport coordinate. - The viewport is centered on the image with square aspect and height 1.0. The y-axis points down. - TODO: Change to a more standard definition. - \return Viewport coordinate. - */ + /** + * Get normalized viewport coordinate. + * The viewport is centered on the image with square aspect and height 1.0. The y-axis points down. + * TODO: Change to a more standard definition. + * @return Viewport coordinate. + */ float2 getViewportCoord(uint2 pixel) { float2 p = pixel + float2(0.5f); return (p - params.viewportOffset) * params.viewportScale; } - /** Setup geometric frame of reference for BSDF slice. - \param[in] uv Viewport coordinate in [0,1]. - \param[out] v Interpolated attributes for the point on the sphere. - \param[out] viewDir View direction. - \return Normalized incident direction (light vector). - */ + /** + * Setup geometric frame of reference for BSDF slice. + * @param[in] uv Viewport coordinate in [0,1]. + * @param[out] v Interpolated attributes for the point on the sphere. + * @param[out] viewDir View direction. + * @return Normalized incident direction (light vector). + */ float3 calculateSliceGeometry(float2 uv, out VertexData v, out float3 viewDir) { // Setup local surface frame as T,B,N. @@ -85,7 +87,7 @@ struct BSDFViewer float theta_d = (1.f - uv.y) * (M_PI / 2); float NdotH = cos(theta_h); - float HdotL = cos(theta_d); // Note: HdotL = HdotV + float HdotL = cos(theta_d); // Note: HdotL = HdotV // Place the H vector at (0,0,1) to start. // Compute L, V that are mirrored about the yz-plane. @@ -103,12 +105,13 @@ struct BSDFViewer return normalize(L); } - /** Calculate sphere geometry for the given viewport coordinate. - \param[in] uv Viewport coordinate in [0,1]. - \param[out] v Interpolated attributes for the point on the sphere (if hit). - \param[out] rayDir Ray direction for the camera ray (normalized). - \return True if we're on the sphere. - */ + /** + * Calculate sphere geometry for the given viewport coordinate. + * @param[in] uv Viewport coordinate in [0,1]. + * @param[out] v Interpolated attributes for the point on the sphere (if hit). + * @param[out] rayDir Ray direction for the camera ray (normalized). + * @return True if we're on the sphere. + */ bool calculateSphereGeometry(float2 uv, out VertexData v, out float3 rayDir) { v = {}; @@ -124,7 +127,8 @@ struct BSDFViewer float d = 1.f - p.x * p.x - p.y * p.y; rayDir = float3(0, 0, -1); - if (d < 0.f) return false; + if (d < 0.f) + return false; p.z = sqrt(d); v.posW = p; } @@ -136,7 +140,8 @@ struct BSDFViewer rayDir = normalize(target - origin); float t; - if (!intersectRaySphere(origin, rayDir, float3(0.f), 1.f, t)) return false; + if (!intersectRaySphere(origin, rayDir, float3(0.f), 1.f, t)) + return false; v.posW = origin + t * rayDir; } @@ -162,9 +167,10 @@ struct BSDFViewer return true; } - /** Prepare SurfaceData struct with shading data. - All unused fields are initialized to their default values. - */ + /** + * Prepare SurfaceData struct with shading data. + * All unused fields are initialized to their default values. + */ SurfaceData prepareShadingData(const VertexData v, const float3 viewDir, const ITextureSampler lod) { SurfaceData data = {}; @@ -176,26 +182,30 @@ struct BSDFViewer return data; } - /** Returns the color to use for background pixels. - \param[in] uv Viewport coordinates. - \param[in] dir Normalized ray direction. - */ + /** + * Returns the color to use for background pixels. + * @param[in] uv Viewport coordinates. + * @param[in] dir Normalized ray direction. + */ float3 evalBackground(float2 uv, float3 dir) { if (params.useGroundPlane) { bool hitGround = params.orthographicCamera ? (uv.y >= 0.5f) : (dir.y < 0.f); - if (hitGround) return kGroundPlaneColor; + if (hitGround) + return kGroundPlaneColor; } - if (params.useDirectionalLight) return float3(0); + if (params.useDirectionalLight) + return float3(0); float3 L = params.useEnvMap ? envMap.eval(dir) : params.lightColor; return L * params.lightIntensity; } - /** Evaluates the incident lighting from a given direction. - If directional lighting is enabled, it can be assumed 'dir' is light's direction. - */ + /** + * Evaluates the incident lighting from a given direction. + * If directional lighting is enabled, it can be assumed 'dir' is light's direction. + */ float3 evalLighting(float3 dir) { if (params.useGroundPlane && dir.y < 0.f) @@ -207,20 +217,24 @@ struct BSDFViewer return L * params.lightIntensity; } - /** Returns the BSDF lobe mask for the currently enabled lobes. - */ + /** + * Returns the BSDF lobe mask for the currently enabled lobes. + */ uint getActiveLobes() { uint lobeTypes = 0; - if (params.enableDiffuse) lobeTypes |= (uint)LobeType::DiffuseReflection; - if (params.enableSpecular) lobeTypes |= (uint)LobeType::SpecularReflection | (uint)LobeType::DeltaReflection; + if (params.enableDiffuse) + lobeTypes |= (uint)LobeType::DiffuseReflection; + if (params.enableSpecular) + lobeTypes |= (uint)LobeType::SpecularReflection | (uint)LobeType::DeltaReflection; // TODO: Viewer doesn't support transmission lobes yet return lobeTypes; } - /** Evaluates the BSDF slice for a given viewport coordinate. - \return Evaluated BSDF value. - */ + /** + * Evaluates the BSDF slice for a given viewport coordinate. + * @return Evaluated BSDF value. + */ float3 evalBSDFSlice(const float2 uv, const ITextureSampler lod, inout SurfaceData data, inout SampleGenerator sg) { // Calculate geometry and incident/outgoing directions. @@ -250,14 +264,15 @@ struct BSDFViewer return f; } - /** Samples the BSDF to evaluate incident illumination. - This is done differently depending on the configuration. - \param[in] sd Shading data. - \param[in] mi BSDF instance. - \param[in,out] sg Sample generator. - \param[out] s Generated sample. Only valid if true is returned. - \return True if a sample was generated, false otherwise. - */ + /** + * Samples the BSDF to evaluate incident illumination. + * This is done differently depending on the configuration. + * @param[in] sd Shading data. + * @param[in] mi BSDF instance. + * @param[in,out] sg Sample generator. + * @param[out] s Generated sample. Only valid if true is returned. + * @return True if a sample was generated, false otherwise. + */ bool generateBSDFSample(const ShadingData sd, const IMaterialInstance mi, inout SampleGenerator sg, out BSDFSample s) { if (params.useDirectionalLight) @@ -277,25 +292,31 @@ struct BSDFViewer float3 getAlbedo(const BSDFProperties bsdfProperties) { float3 albedo = {}; - if (params.outputAlbedo & (uint)AlbedoSelection::DiffuseReflection) albedo += bsdfProperties.diffuseReflectionAlbedo; - if (params.outputAlbedo & (uint)AlbedoSelection::DiffuseTransmission) albedo += bsdfProperties.diffuseTransmissionAlbedo; - if (params.outputAlbedo & (uint)AlbedoSelection::SpecularReflection) albedo += bsdfProperties.specularReflectionAlbedo; - if (params.outputAlbedo & (uint)AlbedoSelection::SpecularTransmission) albedo += bsdfProperties.specularTransmissionAlbedo; + if (params.outputAlbedo & (uint)AlbedoSelection::DiffuseReflection) + albedo += bsdfProperties.diffuseReflectionAlbedo; + if (params.outputAlbedo & (uint)AlbedoSelection::DiffuseTransmission) + albedo += bsdfProperties.diffuseTransmissionAlbedo; + if (params.outputAlbedo & (uint)AlbedoSelection::SpecularReflection) + albedo += bsdfProperties.specularReflectionAlbedo; + if (params.outputAlbedo & (uint)AlbedoSelection::SpecularTransmission) + albedo += bsdfProperties.specularTransmissionAlbedo; return albedo; } - /** Evaluates the lit sphere for a given viewport coordinate. - The viewport shows an analytic sphere of the specified material at infinite distance. - When each pixel is evaluated using a random light direction and omnidirectional white light, - the result converges to the total reflectance (integral of BSDF times the dot(N,L) factor. - \return Outgoing radiance value. - */ + /** + * Evaluates the lit sphere for a given viewport coordinate. + * The viewport shows an analytic sphere of the specified material at infinite distance. + * When each pixel is evaluated using a random light direction and omnidirectional white light, + * the result converges to the total reflectance (integral of BSDF times the dot(N,L) factor. + * @return Outgoing radiance value. + */ float3 evalSphere(const float2 uv, const ITextureSampler lod, inout SurfaceData data, inout SampleGenerator sg) { // Calculate the local surface frame. VertexData v; float3 rayDir; - if (!calculateSphereGeometry(uv, v, rayDir)) return evalBackground(uv, rayDir); + if (!calculateSphereGeometry(uv, v, rayDir)) + return evalBackground(uv, rayDir); // Setup shading data. data = prepareShadingData(v, -rayDir, lod); @@ -335,11 +356,13 @@ struct BSDFViewer return output; } - /** BSDF viewer entry point. - */ + /** + * BSDF viewer entry point. + */ void execute(const uint2 pixel) { - if (any(pixel >= params.frameDim)) return; + if (any(pixel >= params.frameDim)) + return; printSetPixel(pixel); @@ -398,7 +421,7 @@ struct BSDFViewer ParameterBlock gBSDFViewer; [numthreads(16, 16, 1)] -void main(uint3 dispatchThreadID : SV_DispatchThreadID) +void main(uint3 dispatchThreadID: SV_DispatchThreadID) { gBSDFViewer.execute(dispatchThreadID.xy); } diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewer.h b/Source/RenderPasses/BSDFViewer/BSDFViewer.h index caaf33c14..7c63482a1 100644 --- a/Source/RenderPasses/BSDFViewer/BSDFViewer.h +++ b/Source/RenderPasses/BSDFViewer/BSDFViewer.h @@ -59,25 +59,36 @@ class BSDFViewer : public RenderPass void readPixelData(); // Internal state - ref mpScene; ///< Loaded scene if any, nullptr otherwise. - ref mpEnvMap; ///< Environment map if loaded, nullptr otherwise. - bool mUseEnvMap = true; ///< Use environment map if available. - BSDFViewerParams mParams; ///< Parameters shared with the shaders. - ref mpSampleGenerator; ///< Random number generator for the integrator. - bool mOptionsChanged = false; + /// Loaded scene if any, nullptr otherwise. + ref mpScene; + /// Environment map if loaded, nullptr otherwise. + ref mpEnvMap; + /// Use environment map if available. + bool mUseEnvMap = true; - ref mpFence; ///< GPU fence for synchronizing readback. - ref mpPixelDataBuffer; ///< Buffer for data for the selected pixel. - ref mpPixelStagingBuffer; ///< Staging buffer for readback of pixel data. - PixelData mPixelData; ///< Pixel data for the selected pixel (if valid). - bool mPixelDataValid = false; - bool mPixelDataAvailable = false; + /// Parameters shared with the shaders. + BSDFViewerParams mParams; + /// Random number generator for the integrator. + ref mpSampleGenerator; + bool mOptionsChanged = false; - std::unique_ptr mpPixelDebug; ///< Utility class for pixel debugging (print in shaders). + /// GPU fence for synchronizing readback. + ref mpFence; + /// Buffer for data for the selected pixel. + ref mpPixelDataBuffer; + /// Staging buffer for readback of pixel data. + ref mpPixelStagingBuffer; + /// Pixel data for the selected pixel (if valid). + PixelData mPixelData; + bool mPixelDataValid = false; + bool mPixelDataAvailable = false; - ref mpViewerPass; + /// Utility class for pixel debugging (print in shaders). + std::unique_ptr mpPixelDebug; + + ref mpViewerPass; // UI variables - Gui::DropdownList mMaterialList; + Gui::DropdownList mMaterialList; }; diff --git a/Source/RenderPasses/BSDFViewer/BSDFViewerParams.slang b/Source/RenderPasses/BSDFViewer/BSDFViewerParams.slang index 50f344c98..540988b67 100644 --- a/Source/RenderPasses/BSDFViewer/BSDFViewerParams.slang +++ b/Source/RenderPasses/BSDFViewer/BSDFViewerParams.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -32,14 +32,17 @@ BEGIN_NAMESPACE_FALCOR enum class BSDFViewerMode : uint32_t { - Material, ///< Rendered view with material sample. - Slice, ///< BSDF slice viewer. + Material, ///< Rendered view with material sample. + Slice, ///< BSDF slice viewer. }; -FALCOR_ENUM_INFO(BSDFViewerMode, { - { BSDFViewerMode::Material, "Material" }, - { BSDFViewerMode::Slice, "Slice" }, -}); +FALCOR_ENUM_INFO( + BSDFViewerMode, + { + { BSDFViewerMode::Material, "Material" }, + { BSDFViewerMode::Slice, "Slice" }, + } +); FALCOR_ENUM_REGISTER(BSDFViewerMode); enum class AlbedoSelection : uint32_t @@ -51,64 +54,98 @@ enum class AlbedoSelection : uint32_t SpecularTransmission = 0x10, }; -/** BSDFViewer parameters shared between host and device. - Make sure struct layout follows the HLSL packing rules as it is uploaded as a memory blob. - Do not use bool's as they are 1 byte in Visual Studio, 4 bytes in HLSL. - https://msdn.microsoft.com/en-us/library/windows/desktop/bb509632(v=vs.85).aspx -*/ +/** + * BSDFViewer parameters shared between host and device. + * Make sure struct layout follows the HLSL packing rules as it is uploaded as a memory blob. + * Do not use bool's as they are 1 byte in Visual Studio, 4 bytes in HLSL. + * https://msdn.microsoft.com/en-us/library/windows/desktop/bb509632(v=vs.85).aspx + */ struct BSDFViewerParams { - uint2 frameDim = { 0, 0 }; ///< Frame buffer dimension in pixels. - uint frameCount = 0; ///< Frames rendered. - BSDFViewerMode viewerMode = BSDFViewerMode::Material; ///< Current viewer mode. - - float2 viewportOffset; ///< Top-left corner of viewport in pixels. - float2 viewportScale; ///< 1/Size of viewport in pixels. + /// Frame buffer dimension in pixels. + uint2 frameDim = { 0, 0 }; + /// Frames rendered. + uint frameCount = 0; + /// Current viewer mode. + BSDFViewerMode viewerMode = BSDFViewerMode::Material; + + /// Top-left corner of viewport in pixels. + float2 viewportOffset; + /// 1/Size of viewport in pixels. + float2 viewportScale; // Material parameters - uint materialID = 0; ///< Scene material ID. - int useNormalMapping = 0; ///< Use normal mapping. - int useFixedTexCoords = 0; ///< Use fixed texture coordinates. - int _pad0; - float2 texCoords = { 0.f, 0.f }; ///< Texture coordinates to use when 'useFixedTexCoords' is true. - float2 _pad1; + /// Scene material ID. + uint materialID = 0; + /// Use normal mapping. + int useNormalMapping = 0; + /// Use fixed texture coordinates. + int useFixedTexCoords = 0; + int _pad0; + + /// Texture coordinates to use when 'useFixedTexCoords' is true. + float2 texCoords = { 0.f, 0.f }; + float2 _pad1; // BSDF settings - int useDisneyDiffuse = 0; ///< Use the original Disney diffuse BRDF, otherwise Falcor's default (Frostbite's version). - int useSeparableMaskingShadowing = 0; ///< Use the separable form of the masking-shadowing function, otherwise Falcor's default (the correlated form). - int useImportanceSampling = 1; ///< Use importance sampling. - int usePdf = 0; ///< Use BRDF sampling pdf explicitly, otherwise the precomputed weight (for debugging). - uint outputAlbedo = 0; ///< Whether to output albedo instead of reflectance. See AlbedoSelection flags. - int enableDiffuse = 1; ///< Enable diffuse lobe in slice viewer. - int enableSpecular = 1; ///< Enable specular lobe in slice viewer. - int applyNdotL = 0; ///< Multiply BSDF by NdotL in slice viewer. + /// Use the original Disney diffuse BRDF, otherwise Falcor's default (Frostbite's version). + int useDisneyDiffuse = 0; + /// Use the separable form of the masking-shadowing function, otherwise Falcor's default (the correlated form). + int useSeparableMaskingShadowing = 0; + /// Use importance sampling. + int useImportanceSampling = 1; + /// Use BRDF sampling pdf explicitly, otherwise the precomputed weight (for debugging). + int usePdf = 0; + + /// Whether to output albedo instead of reflectance. See AlbedoSelection flags. + uint outputAlbedo = 0; + /// Enable diffuse lobe in slice viewer. + int enableDiffuse = 1; + /// Enable specular lobe in slice viewer. + int enableSpecular = 1; + /// Multiply BSDF by NdotL in slice viewer. + int applyNdotL = 0; // Lighting settings - int useGroundPlane = 0; ///< Draw a ground plane. - int useEnvMap = 0; ///< Use environment light (as opposed to omnidirectional). - int2 _pad3; - - float lightIntensity = 1.f; ///< Light intensity, acts as a multiplier for the light color. - float3 lightColor = { 1.f, 1.f, 1.f }; ///< Light color. - - int useDirectionalLight = 0; ///< Use directional light (as opposed to omnidirectional/envmap). - float3 lightDir = { 0.f, 0.f, -1.f }; ///< Light direction to use when 'useDirectionalLight' is true (note: not normalized). + /// Draw a ground plane. + int useGroundPlane = 0; + /// Use environment light (as opposed to omnidirectional). + int useEnvMap = 0; + int2 _pad3; + + /// Light intensity, acts as a multiplier for the light color. + float lightIntensity = 1.f; + /// Light color. + float3 lightColor = { 1.f, 1.f, 1.f }; + + /// Use directional light (as opposed to omnidirectional/envmap). + int useDirectionalLight = 0; + /// Light direction to use when 'useDirectionalLight' is true (note: not normalized). + float3 lightDir = { 0.f, 0.f, -1.f }; // Camera settings - int orthographicCamera = 0; ///< Use orthographic camera. - float cameraDistance = 1.5f; ///< Camera distance from origin in projective mode. Valid range is (1,+inf). - float cameraFovY = 90.f; ///< Camera vertical field-of-view in degrees. - float cameraViewportScale; ///< Camera viewport scale (= tan(fovY/2)*distance) computed at runtime in projective mode. + + /// Use orthographic camera. + int orthographicCamera = 0; + /// Camera distance from origin in projective mode. Valid range is (1,+inf). + float cameraDistance = 1.5f; + /// Camera vertical field-of-view in degrees. + float cameraFovY = 90.f; + /// Camera viewport scale (= tan(fovY/2)*distance) computed at runtime in projective mode. + float cameraViewportScale; // Misc settings - int2 selectedPixel = { 0, 0 }; ///< Pixel coordinates selected for readback. - int2 _pad5; + + /// Pixel coordinates selected for readback. + int2 selectedPixel = { 0, 0 }; + int2 _pad5; }; -/** Struct for readback of per-pixel data. -*/ +/** + * Struct for readback of per-pixel data. + */ struct PixelData { float2 texC; @@ -122,13 +159,13 @@ struct PixelData // BSDF properties float3 guideNormal; float3 emission; - float roughness; + float roughness; float3 diffuseReflectionAlbedo; float3 diffuseTransmissionAlbedo; float3 specularReflectionAlbedo; float3 specularTransmissionAlbedo; float3 specularReflectance; - int isTransmissive; + int isTransmissive; }; END_NAMESPACE_FALCOR diff --git a/Source/RenderPasses/BlitPass/BlitPass.cpp b/Source/RenderPasses/BlitPass/BlitPass.cpp index 482f6b050..e0b53b3c4 100644 --- a/Source/RenderPasses/BlitPass/BlitPass.cpp +++ b/Source/RenderPasses/BlitPass/BlitPass.cpp @@ -29,20 +29,21 @@ namespace { - const char kDst[] = "dst"; - const char kSrc[] = "src"; - const char kFilter[] = "filter"; - const char kOutputFormat[] = "outputFormat"; +const char kDst[] = "dst"; +const char kSrc[] = "src"; +const char kFilter[] = "filter"; +const char kOutputFormat[] = "outputFormat"; - void regBlitPass(pybind11::module& m) - { - pybind11::class_> pass(m, "BlitPass"); - pass.def_property("filter", - [](const BlitPass& self) { return enumToString(self.getFilter()); }, - [](BlitPass& self, const std::string& value) {self.setFilter(stringToEnum(value)); } - ); - } +void regBlitPass(pybind11::module& m) +{ + pybind11::class_> pass(m, "BlitPass"); + pass.def_property( + "filter", + [](const BlitPass& self) { return enumToString(self.getFilter()); }, + [](BlitPass& self, const std::string& value) { self.setFilter(stringToEnum(value)); } + ); } +} // namespace extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { @@ -50,8 +51,7 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regBlitPass); } -BlitPass::BlitPass(ref pDevice, const Properties& props) - : RenderPass(pDevice) +BlitPass::BlitPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { parseProperties(props); } @@ -68,9 +68,12 @@ void BlitPass::parseProperties(const Properties& props) { for (const auto& [key, value] : props) { - if (key == kFilter) setFilter(value); - if (key == kOutputFormat) mOutputFormat = value; - else logWarning("Unknown property '{}' in a BlitPass properties.", key); + if (key == kFilter) + setFilter(value); + if (key == kOutputFormat) + mOutputFormat = value; + else + logWarning("Unknown property '{}' in a BlitPass properties.", key); } } @@ -78,7 +81,8 @@ Properties BlitPass::getProperties() const { Properties props; props[kFilter] = mFilter; - if (mOutputFormat != ResourceFormat::Unknown) props[kOutputFormat] = mOutputFormat; + if (mOutputFormat != ResourceFormat::Unknown) + props[kOutputFormat] = mOutputFormat; return props; } @@ -99,5 +103,6 @@ void BlitPass::execute(RenderContext* pRenderContext, const RenderData& renderDa void BlitPass::renderUI(Gui::Widgets& widget) { - if (auto filter = mFilter; widget.dropdown("Filter", filter)) setFilter(filter); + if (auto filter = mFilter; widget.dropdown("Filter", filter)) + setFilter(filter); } diff --git a/Source/RenderPasses/BlitPass/BlitPass.h b/Source/RenderPasses/BlitPass/BlitPass.h index 827660252..c68b21358 100644 --- a/Source/RenderPasses/BlitPass/BlitPass.h +++ b/Source/RenderPasses/BlitPass/BlitPass.h @@ -31,10 +31,11 @@ using namespace Falcor; -/** Render pass that blits an input texture to an output texture. - - This pass is useful for format conversion. -*/ +/** + * Render pass that blits an input texture to an output texture. + * + * This pass is useful for format conversion. + */ class BlitPass : public RenderPass { public: @@ -50,12 +51,12 @@ class BlitPass : public RenderPass virtual void renderUI(Gui::Widgets& widget) override; // Scripting functions - Sampler::Filter getFilter() const { return mFilter; } - void setFilter(Sampler::Filter filter) { mFilter = filter; } + TextureFilteringMode getFilter() const { return mFilter; } + void setFilter(TextureFilteringMode filter) { mFilter = filter; } private: void parseProperties(const Properties& props); - Sampler::Filter mFilter = Sampler::Filter::Linear; + TextureFilteringMode mFilter = TextureFilteringMode::Linear; ResourceFormat mOutputFormat = ResourceFormat::Unknown; }; diff --git a/Source/RenderPasses/CMakeLists.txt b/Source/RenderPasses/CMakeLists.txt index 9d9db4b62..96a8fb406 100644 --- a/Source/RenderPasses/CMakeLists.txt +++ b/Source/RenderPasses/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(AccumulatePass) add_subdirectory(BlitPass) +add_subdirectory(BSDFOptimizer) add_subdirectory(BSDFViewer) add_subdirectory(DebugPasses) add_subdirectory(DLSSPass) @@ -23,4 +24,5 @@ add_subdirectory(TAA) add_subdirectory(TestPasses) add_subdirectory(ToneMapper) add_subdirectory(Utils) +add_subdirectory(WARDiffPathTracer) add_subdirectory(WhittedRayTracer) diff --git a/Source/RenderPasses/DLSSPass/DLSSPass.cpp b/Source/RenderPasses/DLSSPass/DLSSPass.cpp index d5489f89d..03bb31e24 100644 --- a/Source/RenderPasses/DLSSPass/DLSSPass.cpp +++ b/Source/RenderPasses/DLSSPass/DLSSPass.cpp @@ -80,7 +80,7 @@ DLSSPass::DLSSPass(ref pDevice, const Properties& props) : RenderPass(pD logWarning("Unknown property '{}' in a DLSSPass properties.", key); } - mpExposure = Texture::create2D(mpDevice, 1, 1, ResourceFormat::R32Float, 1, 1); + mpExposure = mpDevice->createTexture2D(1, 1, ResourceFormat::R32Float, 1, 1); } Properties DLSSPass::getProperties() const @@ -189,8 +189,13 @@ void DLSSPass::initializeDLSS(RenderContext* pRenderContext) auto optimalSettings = mpNGXWrapper->queryOptimalSettings(mInputSize, perfQuality); mDLSSOutputSize = uint2(float2(mInputSize) * float2(mInputSize) / float2(optimalSettings.optimalRenderSize)); - mpOutput = Texture::create2D( - mpDevice, mDLSSOutputSize.x, mDLSSOutputSize.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, + mpOutput = mpDevice->createTexture2D( + mDLSSOutputSize.x, + mDLSSOutputSize.y, + ResourceFormat::RGBA32Float, + 1, + 1, + nullptr, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource ); @@ -241,9 +246,9 @@ void DLSSPass::executeInternal(RenderContext* pRenderContext, const RenderData& { auto tex = renderData.getTexture(name); if (!tex) - throw RuntimeError("DLSSPass: Missing input '{}'", name); + FALCOR_THROW("DLSSPass: Missing input '{}'", name); if (tex->getWidth() != mInputSize.x || tex->getHeight() != mInputSize.y) - throw RuntimeError("DLSSPass: Input '{}' has mismatching size. All inputs must be of the same size.", name); + FALCOR_THROW("DLSSPass: Input '{}' has mismatching size. All inputs must be of the same size.", name); return tex; }; @@ -269,7 +274,15 @@ void DLSSPass::executeInternal(RenderContext* pRenderContext, const RenderData& motionVectorScale = inputSize; mpNGXWrapper->evaluateDLSS( - pRenderContext, color.get(), output.get(), motionVectors.get(), depth.get(), mpExposure.get(), false, mSharpness, jitterOffset, + pRenderContext, + color.get(), + output.get(), + motionVectors.get(), + depth.get(), + mpExposure.get(), + false, + mSharpness, + jitterOffset, motionVectorScale ); diff --git a/Source/RenderPasses/DLSSPass/NGXWrapper.cpp b/Source/RenderPasses/DLSSPass/NGXWrapper.cpp index 11bc8926e..4df1af474 100644 --- a/Source/RenderPasses/DLSSPass/NGXWrapper.cpp +++ b/Source/RenderPasses/DLSSPass/NGXWrapper.cpp @@ -44,11 +44,11 @@ #include #include -#define THROW_IF_FAILED(call) \ - { \ - NVSDK_NGX_Result result_ = call; \ - if (NVSDK_NGX_FAILED(result_)) \ - throw RuntimeError(#call " failed with error {}", resultToString(result_)); \ +#define THROW_IF_FAILED(call) \ + { \ + NVSDK_NGX_Result result_ = call; \ + if (NVSDK_NGX_FAILED(result_)) \ + FALCOR_THROW(#call " failed with error {}", resultToString(result_)); \ } namespace Falcor @@ -122,8 +122,14 @@ void NGXWrapper::initializeNGX(const std::filesystem::path& applicationDataPath, case Device::Type::Vulkan: #if FALCOR_HAS_VULKAN result = NVSDK_NGX_VULKAN_Init( - kAppID, applicationDataPath.c_str(), mpDevice->getNativeHandle(0).as(), - mpDevice->getNativeHandle(1).as(), mpDevice->getNativeHandle(2).as(), nullptr, nullptr, &featureInfo + kAppID, + applicationDataPath.c_str(), + mpDevice->getNativeHandle(0).as(), + mpDevice->getNativeHandle(1).as(), + mpDevice->getNativeHandle(2).as(), + nullptr, + nullptr, + &featureInfo ); #endif break; @@ -133,11 +139,11 @@ void NGXWrapper::initializeNGX(const std::filesystem::path& applicationDataPath, { if (result == NVSDK_NGX_Result_FAIL_FeatureNotSupported || result == NVSDK_NGX_Result_FAIL_PlatformError) { - throw RuntimeError("NVIDIA NGX is not available on this hardware/platform " + resultToString(result)); + FALCOR_THROW("NVIDIA NGX is not available on this hardware/platform " + resultToString(result)); } else { - throw RuntimeError("Failed to initialize NGX " + resultToString(result)); + FALCOR_THROW("Failed to initialize NGX " + resultToString(result)); } } @@ -176,7 +182,7 @@ void NGXWrapper::initializeNGX(const std::filesystem::path& applicationDataPath, { message += fmt::format("\nMinimum driver version required: {}.{}", majorVersion, minorVersion); } - throw RuntimeError(message); + FALCOR_THROW(message); } #endif @@ -184,7 +190,7 @@ void NGXWrapper::initializeNGX(const std::filesystem::path& applicationDataPath, result = mpParameters->Get(NVSDK_NGX_Parameter_SuperSampling_Available, &dlssAvailable); if (NVSDK_NGX_FAILED(result) || !dlssAvailable) { - throw RuntimeError("NVIDIA DLSS not available on this hardward/platform " + resultToString(result)); + FALCOR_THROW("NVIDIA DLSS not available on this hardward/platform " + resultToString(result)); } } @@ -192,7 +198,7 @@ void NGXWrapper::shutdownNGX() { if (mInitialized) { - mpDevice->flushAndSync(); + mpDevice->wait(); if (mpFeature != nullptr) releaseDLSS(); @@ -250,24 +256,24 @@ void NGXWrapper::initializeDLSS( case Device::Type::D3D12: { #if FALCOR_HAS_D3D12 - pRenderContext->flush(); + pRenderContext->submit(); ID3D12GraphicsCommandList* pCommandList = pRenderContext->getLowLevelData()->getCommandBufferNativeHandle().as(); THROW_IF_FAILED(NGX_D3D12_CREATE_DLSS_EXT(pCommandList, creationNodeMask, visibilityNodeMask, &mpFeature, mpParameters, &dlssParams) ); - pRenderContext->flush(); + pRenderContext->submit(); #endif break; } case Device::Type::Vulkan: { #if FALCOR_HAS_VULKAN - pRenderContext->flush(); + pRenderContext->submit(); VkCommandBuffer vkCommandBuffer = pRenderContext->getLowLevelData()->getCommandBufferNativeHandle().as(); THROW_IF_FAILED( NGX_VULKAN_CREATE_DLSS_EXT(vkCommandBuffer, creationNodeMask, visibilityNodeMask, &mpFeature, mpParameters, &dlssParams) ); - pRenderContext->flush(); + pRenderContext->submit(); #endif break; } @@ -278,7 +284,7 @@ void NGXWrapper::releaseDLSS() { if (mpFeature) { - mpDevice->flushAndSync(); + mpDevice->wait(); switch (mpDevice->getType()) { @@ -302,8 +308,17 @@ NGXWrapper::OptimalSettings NGXWrapper::queryOptimalSettings(uint2 displaySize, OptimalSettings settings; THROW_IF_FAILED(NGX_DLSS_GET_OPTIMAL_SETTINGS( - mpParameters, displaySize.x, displaySize.y, perfQuality, &settings.optimalRenderSize.x, &settings.optimalRenderSize.y, - &settings.maxRenderSize.x, &settings.maxRenderSize.y, &settings.minRenderSize.x, &settings.minRenderSize.y, &settings.sharpness + mpParameters, + displaySize.x, + displaySize.y, + perfQuality, + &settings.optimalRenderSize.x, + &settings.optimalRenderSize.y, + &settings.maxRenderSize.x, + &settings.maxRenderSize.y, + &settings.minRenderSize.x, + &settings.minRenderSize.y, + &settings.sharpness )); // Depending on what version of DLSS DLL is being used, a sharpness of > 1.f was possible. @@ -377,7 +392,7 @@ bool NGXWrapper::evaluateDLSS( pRenderContext->setPendingCommands(true); pRenderContext->uavBarrier(pResolvedColor); // TODO: Get rid of the flush - pRenderContext->flush(); + pRenderContext->submit(); #endif // FALCOR_HAS_D3D12 break; } @@ -442,7 +457,7 @@ bool NGXWrapper::evaluateDLSS( pRenderContext->setPendingCommands(true); pRenderContext->uavBarrier(pResolvedColor); // TODO: Get rid of the flush - pRenderContext->flush(); + pRenderContext->submit(); #endif break; } diff --git a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapParams.slang b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapParams.slang index 4d113048a..9413743ea 100644 --- a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapParams.slang +++ b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapParams.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -39,14 +39,17 @@ enum class ColorMap : uint32_t Inferno, }; -FALCOR_ENUM_INFO(ColorMap, { - { ColorMap::Grey, "Grey" }, - { ColorMap::Jet, "Jet" }, - { ColorMap::Viridis, "Viridis" }, - { ColorMap::Plasma, "Plasma" }, - { ColorMap::Magma, "Magma" }, - { ColorMap::Inferno, "Inferno" }, -}); +FALCOR_ENUM_INFO( + ColorMap, + { + { ColorMap::Grey, "Grey" }, + { ColorMap::Jet, "Jet" }, + { ColorMap::Viridis, "Viridis" }, + { ColorMap::Plasma, "Plasma" }, + { ColorMap::Magma, "Magma" }, + { ColorMap::Inferno, "Inferno" }, + } +); FALCOR_ENUM_REGISTER(ColorMap); struct ColorMapParams diff --git a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.cpp b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.cpp index c291b8964..e916cd30d 100644 --- a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.cpp +++ b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.cpp @@ -29,29 +29,34 @@ namespace { - const std::string kShaderFile = "RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.ps.slang"; +const std::string kShaderFile = "RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.ps.slang"; - const std::string kInput = "input"; - const std::string kOutput = "output"; +const std::string kInput = "input"; +const std::string kOutput = "output"; - const std::string kColorMap = "colorMap"; - const std::string kChannel = "channel"; - const std::string kAutoRange = "autoRange"; - const std::string kMinValue = "minValue"; - const std::string kMaxValue = "maxValue"; -} +const std::string kColorMap = "colorMap"; +const std::string kChannel = "channel"; +const std::string kAutoRange = "autoRange"; +const std::string kMinValue = "minValue"; +const std::string kMaxValue = "maxValue"; +} // namespace -ColorMapPass::ColorMapPass(ref pDevice, const Properties& props) - : RenderPass(pDevice) +ColorMapPass::ColorMapPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { for (const auto& [key, value] : props) { - if (key == kColorMap) mColorMap = value; - else if (key == kChannel) mChannel = value; - else if (key == kAutoRange) mAutoRange = value; - else if (key == kMinValue) mMinValue = value; - else if (key == kMaxValue) mMaxValue = value; - else logWarning("Unknown property '{}' in a ColorMapPass properties.", key); + if (key == kColorMap) + mColorMap = value; + else if (key == kChannel) + mChannel = value; + else if (key == kAutoRange) + mAutoRange = value; + else if (key == kMinValue) + mMinValue = value; + else if (key == kMaxValue) + mMaxValue = value; + else + logWarning("Unknown property '{}' in a ColorMapPass properties.", key); } mpFbo = Fbo::create(mpDevice); @@ -71,8 +76,8 @@ Properties ColorMapPass::getProperties() const RenderPassReflection ColorMapPass::reflect(const CompileData& compileData) { RenderPassReflection r; - r.addInput(kInput, "Input image").bindFlags(Falcor::Resource::BindFlags::ShaderResource).texture2D(0, 0); - r.addOutput(kOutput, "Output image").bindFlags(Falcor::Resource::BindFlags::RenderTarget).texture2D(0, 0); + r.addInput(kInput, "Input image").bindFlags(Falcor::ResourceBindFlags::ShaderResource).texture2D(0, 0); + r.addOutput(kOutput, "Output image").bindFlags(Falcor::ResourceBindFlags::RenderTarget).texture2D(0, 0); return r; } @@ -85,7 +90,8 @@ void ColorMapPass::execute(RenderContext* pRenderContext, const RenderData& rend if (mAutoRange && inputTexture) { - if (!mpAutoRanging) mpAutoRanging = std::make_unique(mpDevice); + if (!mpAutoRanging) + mpAutoRanging = std::make_unique(mpDevice); if (auto minMax = mpAutoRanging->getMinMax(pRenderContext, inputTexture, mChannel)) { @@ -120,15 +126,15 @@ void ColorMapPass::execute(RenderContext* pRenderContext, const RenderData& rend switch (inputType) { - case FormatType::Uint: - defines.add("_FORMAT", "FORMAT_UINT"); - break; - case FormatType::Sint: - defines.add("_FORMAT", "FORMAT_SINT"); - break; - default: - defines.add("_FORMAT", "FORMAT_FLOAT"); - break; + case FormatType::Uint: + defines.add("_FORMAT", "FORMAT_UINT"); + break; + case FormatType::Sint: + defines.add("_FORMAT", "FORMAT_SINT"); + break; + default: + defines.add("_FORMAT", "FORMAT_FLOAT"); + break; } if (!mpColorMapPass || mRecompile) @@ -161,11 +167,15 @@ void ColorMapPass::renderUI(Gui::Widgets& widget) ColorMapPass::AutoRanging::AutoRanging(ref pDevice) { mpParallelReduction = std::make_unique(pDevice); - mpReductionResult = Buffer::create(pDevice, 32, ResourceBindFlags::None, Buffer::CpuAccess::Read); - mpFence = GpuFence::create(pDevice); + mpReductionResult = pDevice->createBuffer(32, ResourceBindFlags::None, MemoryType::ReadBack); + mpFence = pDevice->createFence(); } -std::optional> ColorMapPass::AutoRanging::getMinMax(RenderContext* pRenderContext, const ref& texture, uint32_t channel) +std::optional> ColorMapPass::AutoRanging::getMinMax( + RenderContext* pRenderContext, + const ref& texture, + uint32_t channel +) { FALCOR_ASSERT(pRenderContext); FALCOR_ASSERT(texture); @@ -177,20 +187,20 @@ std::optional> ColorMapPass::AutoRanging::getMinMax(Re if (mReductionAvailable) { - mpFence->syncCpu(); + mpFence->wait(); const void* values = mpReductionResult->map(Buffer::MapType::Read); switch (formatType) { case FormatType::Uint: - result = { reinterpret_cast(values)[0][channel], reinterpret_cast(values)[1][channel] }; + result = {reinterpret_cast(values)[0][channel], reinterpret_cast(values)[1][channel]}; break; case FormatType::Sint: - result = { reinterpret_cast(values)[0][channel], reinterpret_cast(values)[1][channel] }; + result = {reinterpret_cast(values)[0][channel], reinterpret_cast(values)[1][channel]}; break; default: - result = { reinterpret_cast(values)[0][channel], reinterpret_cast(values)[1][channel] }; + result = {reinterpret_cast(values)[0][channel], reinterpret_cast(values)[1][channel]}; break; } @@ -212,7 +222,7 @@ std::optional> ColorMapPass::AutoRanging::getMinMax(Re break; } - mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + pRenderContext->signal(mpFence.get()); mReductionAvailable = true; return result; diff --git a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.h b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.h index 2dfe969fc..69cef55ed 100644 --- a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.h +++ b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.h @@ -69,7 +69,7 @@ class ColorMapPass : public RenderPass private: std::unique_ptr mpParallelReduction; ref mpReductionResult; - ref mpFence; + ref mpFence; bool mReductionAvailable = false; }; diff --git a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.ps.slang b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.ps.slang index 963a2a2c5..a985fbd35 100644 --- a/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.ps.slang +++ b/Source/RenderPasses/DebugPasses/ColorMapPass/ColorMapPass.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,8 +29,8 @@ import RenderPasses.DebugPasses.ColorMapPass.ColorMapParams; import Utils.Color.ColorMap; #define FORMAT_FLOAT 0 -#define FORMAT_UINT 1 -#define FORMAT_SINT 2 +#define FORMAT_UINT 1 +#define FORMAT_SINT 2 #if _FORMAT == FORMAT_FLOAT Texture2D gTexture; @@ -54,24 +54,24 @@ float3 applyColorMap(float value) { switch (kColorMap) { - case ColorMap::Grey: - return colormapGray(value); - case ColorMap::Jet: - return colormapJet(value); - case ColorMap::Viridis: - return colormapViridis(value); - case ColorMap::Plasma: - return colormapPlasma(value); - case ColorMap::Magma: - return colormapMagma(value); - case ColorMap::Inferno: - return colormapInferno(value); - default: - return 0.f; + case ColorMap::Grey: + return colormapGray(value); + case ColorMap::Jet: + return colormapJet(value); + case ColorMap::Viridis: + return colormapViridis(value); + case ColorMap::Plasma: + return colormapPlasma(value); + case ColorMap::Magma: + return colormapMagma(value); + case ColorMap::Inferno: + return colormapInferno(value); + default: + return 0.f; } } -float4 main(float2 texC : TEXCOORD, float4 pos : SV_Position) : SV_Target0 +float4 main(float2 texC: TEXCOORD, float4 pos: SV_Position) : SV_Target0 { int2 pixelPos = int2(pos.xy); @@ -79,7 +79,8 @@ float4 main(float2 texC : TEXCOORD, float4 pos : SV_Position) : SV_Target0 float value = gTexture.Load(int3(pixelPos, 0))[_CHANNEL]; // Normalize and colorize value. - float normalized = gParams.minValue == gParams.maxValue ? 0.f : saturate((value - gParams.minValue) / (gParams.maxValue - gParams.minValue)); + float normalized = + gParams.minValue == gParams.maxValue ? 0.f : saturate((value - gParams.minValue) / (gParams.maxValue - gParams.minValue)); float3 colorized = applyColorMap(normalized); return float4(colorized, 1.f); diff --git a/Source/RenderPasses/DebugPasses/Comparison.ps.slang b/Source/RenderPasses/DebugPasses/Comparison.ps.slang index f99607439..d32002581 100644 --- a/Source/RenderPasses/DebugPasses/Comparison.ps.slang +++ b/Source/RenderPasses/DebugPasses/Comparison.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,12 +27,12 @@ **************************************************************************/ cbuffer GlobalCB { - int gSplitLocation; // X-value of the current split location to display - uint gDividerSize; // How wide should the divider be? - float4 gDividerColor; // What color should the divider be this frame? - int2 gMousePosition; // What is the current position of the mouse? (Used to position arrows) - bool gDrawArrows; // Should we draw arrows to the left & right of the divider? - uint gLeftBound; // How many pixels from the left side should the comparison window start? + int gSplitLocation; ///< X-value of the current split location to display + uint gDividerSize; ///< How wide should the divider be? + float4 gDividerColor; ///< What color should the divider be this frame? + int2 gMousePosition; ///< What is the current position of the mouse? (Used to position arrows) + bool gDrawArrows; ///< Should we draw arrows to the left & right of the divider? + uint gLeftBound; ///< How many pixels from the left side should the comparison window start? }; Texture2D gLeftInput; @@ -44,10 +44,10 @@ interface ICalcPixelColor float2x4 calcColors(uint2 pixelPos); }; -float4 compare(float2 texC, float4 pos, C calc) +float4 compare(float2 texC, float4 pos, C calc) { // Get the two sides of the image - uint2 pixelPos = (uint2) pos.xy; + uint2 pixelPos = (uint2)pos.xy; float2x4 colors = calc.calcColors(pixelPos); float4 leftColor = colors[0]; float4 rightColor = colors[1]; diff --git a/Source/RenderPasses/DebugPasses/ComparisonPass.cpp b/Source/RenderPasses/DebugPasses/ComparisonPass.cpp index b69f48424..9ae7940f7 100644 --- a/Source/RenderPasses/DebugPasses/ComparisonPass.cpp +++ b/Source/RenderPasses/DebugPasses/ComparisonPass.cpp @@ -29,18 +29,17 @@ namespace { - const std::string kSplitLocation = "splitLocation"; - const std::string kShowTextLabels = "showTextLabels"; - const std::string kLeftLabel = "leftLabel"; - const std::string kRightLabel = "rightLabel"; +const std::string kSplitLocation = "splitLocation"; +const std::string kShowTextLabels = "showTextLabels"; +const std::string kLeftLabel = "leftLabel"; +const std::string kRightLabel = "rightLabel"; - const std::string kLeftInput = "leftInput"; - const std::string kRightInput = "rightInput"; - const std::string kOutput = "output"; -} +const std::string kLeftInput = "leftInput"; +const std::string kRightInput = "rightInput"; +const std::string kOutput = "output"; +} // namespace -ComparisonPass::ComparisonPass(ref pDevice) - : RenderPass(pDevice) +ComparisonPass::ComparisonPass(ref pDevice) : RenderPass(pDevice) { mpTextRenderer = std::make_unique(mpDevice); } @@ -69,7 +68,8 @@ bool ComparisonPass::parseKeyValuePair(const std::string key, const Properties:: mRightLabel = str; return true; } - else return false; + else + return false; } Properties ComparisonPass::getProperties() const @@ -85,9 +85,9 @@ Properties ComparisonPass::getProperties() const RenderPassReflection ComparisonPass::reflect(const CompileData& compileData) { RenderPassReflection r; - r.addInput(kLeftInput, "Left side image").bindFlags(Falcor::Resource::BindFlags::ShaderResource).texture2D(0, 0); - r.addInput(kRightInput, "Right side image").bindFlags(Falcor::Resource::BindFlags::ShaderResource).texture2D(0, 0); - r.addOutput(kOutput, "Output image").bindFlags(Falcor::Resource::BindFlags::RenderTarget).texture2D(0, 0); + r.addInput(kLeftInput, "Left side image").bindFlags(Falcor::ResourceBindFlags::ShaderResource).texture2D(0, 0); + r.addInput(kRightInput, "Right side image").bindFlags(Falcor::ResourceBindFlags::ShaderResource).texture2D(0, 0); + r.addOutput(kOutput, "Output image").bindFlags(Falcor::ResourceBindFlags::RenderTarget).texture2D(0, 0); return r; } @@ -96,10 +96,11 @@ void ComparisonPass::execute(RenderContext* pRenderContext, const RenderData& re // Get references to our input, output, and temporary accumulation texture pLeftSrcTex = renderData.getTexture(kLeftInput); pRightSrcTex = renderData.getTexture(kRightInput); - pDstFbo = Fbo::create(mpDevice, { renderData.getTexture(kOutput) }); + pDstFbo = Fbo::create(mpDevice, {renderData.getTexture(kOutput)}); // If we haven't initialized the split location, split the screen in half by default - if (mSplitLoc < 0) mSplitLoc = 0.5f; + if (mSplitLoc < 0) + mSplitLoc = 0.5f; // Set shader parameters auto var = mpSplitShader->getRootVar(); diff --git a/Source/RenderPasses/DebugPasses/ComparisonPass.h b/Source/RenderPasses/DebugPasses/ComparisonPass.h index 5f5beefb9..7e6e6717e 100644 --- a/Source/RenderPasses/DebugPasses/ComparisonPass.h +++ b/Source/RenderPasses/DebugPasses/ComparisonPass.h @@ -53,14 +53,23 @@ class ComparisonPass : public RenderPass std::unique_ptr mpTextRenderer; // Screen parameters - bool mSwapSides = false; // Is the left input on the left side + + /// Is the left input on the left side + bool mSwapSides = false; // Divider parameters - float mSplitLoc = -1.0f; // Location of the divider as a fraction of screen width, values < 0 are initialized to 0.5 - uint32_t mDividerSize = 2; // Size of the divider (in pixels: 2*mDividerSize+1) + + /// Location of the divider as a fraction of screen width, values < 0 are initialized to 0.5 + float mSplitLoc = -1.0f; + /// Size of the divider (in pixels: 2*mDividerSize+1) + uint32_t mDividerSize = 2; // Label Parameters - bool mShowLabels = false; // Show text labels for two images? - std::string mLeftLabel = "Left side"; // Left label. Set in Python script with "leftLabel" - std::string mRightLabel = "Right side"; // Right label. Set in Python script with "rightLabel" + + /// Show text labels for two images? + bool mShowLabels = false; + /// Left label. Set in Python script with "leftLabel" + std::string mLeftLabel = "Left side"; + /// Right label. Set in Python script with "rightLabel" + std::string mRightLabel = "Right side"; }; diff --git a/Source/RenderPasses/DebugPasses/DebugPasses.cpp b/Source/RenderPasses/DebugPasses/DebugPasses.cpp index ece1ba0af..a6e044973 100644 --- a/Source/RenderPasses/DebugPasses/DebugPasses.cpp +++ b/Source/RenderPasses/DebugPasses/DebugPasses.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetection.ps.slang b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetection.ps.slang index ebdb34985..e76a1e265 100644 --- a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetection.ps.slang +++ b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetection.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,13 +27,15 @@ **************************************************************************/ Texture2D gTexture; -float4 main(float2 texC : TEXCOORD) : SV_TARGET0 +float4 main(float2 texC: TEXCOORD) : SV_TARGET0 { uint2 uv; gTexture.GetDimensions(uv.x, uv.y); int2 xy = int2(uv * texC); float4 value = gTexture.Load(int3(xy, 0)); - if (any(isnan(value))) return float4(1, 0, 0, 1); - else if (any(isinf(value))) return float4(0, 1, 0, 1); + if (any(isnan(value))) + return float4(1, 0, 0, 1); + else if (any(isinf(value))) + return float4(0, 1, 0, 1); return float4(0, 0, 0, 1); } diff --git a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp index a44411afc..55cabc66b 100644 --- a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp +++ b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.cpp @@ -29,15 +29,15 @@ namespace { - const std::string kSrc = "src"; - const std::string kDst = "dst"; - const std::string kFormatWarning = "Non-float format can't represent Inf/NaN values. Expect black output."; -} +const std::string kSrc = "src"; +const std::string kDst = "dst"; +const std::string kFormatWarning = "Non-float format can't represent Inf/NaN values. Expect black output."; +} // namespace -InvalidPixelDetectionPass::InvalidPixelDetectionPass(ref pDevice, const Properties& props) - : RenderPass(pDevice) +InvalidPixelDetectionPass::InvalidPixelDetectionPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { - mpInvalidPixelDetectPass = FullScreenPass::create(mpDevice, "RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetection.ps.slang"); + mpInvalidPixelDetectPass = + FullScreenPass::create(mpDevice, "RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetection.ps.slang"); mpFbo = Fbo::create(mpDevice); } @@ -57,9 +57,8 @@ RenderPassReflection InvalidPixelDetectionPass::reflect(const CompileData& compi uint32_t srcMipCount = edge->getMipCount(); uint32_t srcArraySize = edge->getArraySize(); - auto formatField = [=](RenderPassReflection::Field& f) { - return f.resourceType(srcType, srcWidth, srcHeight, srcDepth, srcSampleCount, srcMipCount, srcArraySize); - }; + auto formatField = [=](RenderPassReflection::Field& f) + { return f.resourceType(srcType, srcWidth, srcHeight, srcDepth, srcSampleCount, srcMipCount, srcArraySize); }; formatField(r.addInput(kSrc, "Input image to be checked")).format(srcFormat); formatField(r.addOutput(kDst, "Output where pixels are red if NaN, green if Inf, and black otherwise")); @@ -75,7 +74,7 @@ RenderPassReflection InvalidPixelDetectionPass::reflect(const CompileData& compi void InvalidPixelDetectionPass::compile(RenderContext* pRenderContext, const CompileData& compileData) { - if (!mReady) throw RuntimeError("InvalidPixelDetectionPass: Missing incoming reflection data"); + FALCOR_CHECK(mReady, "InvalidPixelDetectionPass: Missing incoming reflection data"); } void InvalidPixelDetectionPass::execute(RenderContext* pRenderContext, const RenderData& renderData) @@ -103,7 +102,7 @@ void InvalidPixelDetectionPass::renderUI(Gui::Widgets& widget) if (mFormat != ResourceFormat::Unknown) { - widget.dummy("#space", { 1, 10 }); + widget.dummy("#space", {1, 10}); widget.text("Input format: " + to_string(mFormat)); if (getFormatType(mFormat) != FormatType::Float) { diff --git a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h index b2136da14..3b2cb334a 100644 --- a/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h +++ b/Source/RenderPasses/DebugPasses/InvalidPixelDetectionPass/InvalidPixelDetectionPass.h @@ -35,9 +35,16 @@ using namespace Falcor; class InvalidPixelDetectionPass : public RenderPass { public: - FALCOR_PLUGIN_CLASS(InvalidPixelDetectionPass, "InvalidPixelDetectionPass", "Pass that marks all NaN pixels red and Inf pixels green in an image."); + FALCOR_PLUGIN_CLASS( + InvalidPixelDetectionPass, + "InvalidPixelDetectionPass", + "Pass that marks all NaN pixels red and Inf pixels green in an image." + ); - static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } + static ref create(ref pDevice, const Properties& props) + { + return make_ref(pDevice, props); + } InvalidPixelDetectionPass(ref pDevice, const Properties& props); diff --git a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySide.ps.slang b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySide.ps.slang index 48ebdda3e..157c40d0e 100644 --- a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySide.ps.slang +++ b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySide.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,7 +29,7 @@ import "RenderPasses/DebugPasses/Comparison.ps"; struct SideBySide : ICalcPixelColor { - float2x4 calcColors(uint2 pixelPos) + float2x4 calcColors(uint2 pixelPos) { float2x4 colors; colors[0] = gLeftInput[uint2(pixelPos.x + gLeftBound, pixelPos.y)]; @@ -38,8 +38,8 @@ struct SideBySide : ICalcPixelColor } }; -float4 main(float2 texC : TEXCOORD, float4 pos : SV_Position) : SV_Target0 +float4 main(float2 texC: TEXCOORD, float4 pos: SV_Position) : SV_Target0 { - SideBySide comp = { }; + SideBySide comp = {}; return compare(texC, pos, comp); } diff --git a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp index e9c53d572..e0bb2df37 100644 --- a/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp +++ b/Source/RenderPasses/DebugPasses/SideBySidePass/SideBySidePass.cpp @@ -29,20 +29,20 @@ namespace { - const std::string kImageLeftBound = "imageLeftBound"; +const std::string kImageLeftBound = "imageLeftBound"; - // Where is our shader located? - const std::string kSplitShader = "RenderPasses/DebugPasses/SideBySidePass/SideBySide.ps.slang"; -} +// Where is our shader located? +const std::string kSplitShader = "RenderPasses/DebugPasses/SideBySidePass/SideBySide.ps.slang"; +} // namespace -SideBySidePass::SideBySidePass(ref pDevice, const Properties& props) - : ComparisonPass(pDevice) +SideBySidePass::SideBySidePass(ref pDevice, const Properties& props) : ComparisonPass(pDevice) { createProgram(); for (const auto& [key, value] : props) { - if (key == kImageLeftBound) mImageLeftBound = value; + if (key == kImageLeftBound) + mImageLeftBound = value; else if (!parseKeyValuePair(key, value)) { logWarning("Unknown property '{}' in a SideBySidePass properties.", key); diff --git a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreen.ps.slang b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreen.ps.slang index 7651f60b0..08d76fd8b 100644 --- a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreen.ps.slang +++ b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreen.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -38,8 +38,8 @@ struct InteractiveImageComparison : ICalcPixelColor } }; -float4 main(float2 texC : TEXCOORD, float4 pos : SV_Position) : SV_Target0 +float4 main(float2 texC: TEXCOORD, float4 pos: SV_Position) : SV_Target0 { - InteractiveImageComparison comp = { }; + InteractiveImageComparison comp = {}; return compare(texC, pos, comp); } diff --git a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp index a0ebc74cc..53b5b1340 100644 --- a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp +++ b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.cpp @@ -29,38 +29,39 @@ namespace { - // Divider colors - const float4 kColorUnselected = float4(0, 0, 0, 1); - const float4 kColorSelected = float4(1, 1, 1, 1); - - // A simple character array representing a 16x16 grayscale arrow - const unsigned char kArrowArray[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 87, 13, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 212, 255, 255, 34, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 32, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 78, 255, 255, 255, 255, 33, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 255, 255, 32, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255, 255, 255, 255, 34, 0, - 31, 158, 156, 156, 156, 156, 156, 156, 156, 146, 212, 255, 255, 255, 255, 34, - 241, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, - 241, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 240, - 31, 158, 156, 156, 156, 156, 156, 156, 156, 146, 212, 255, 255, 255, 255, 33, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 255, 255, 255, 255, 34, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 81, 255, 255, 255, 255, 31, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 79, 255, 255, 255 ,255, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 31, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 212, 255, 255, 33, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 87, 12, 0, 0, 0, 0, 0, 0 - }; - - // Where is our shader located? - const std::string kSplitShader = "RenderPasses/DebugPasses/SplitScreenPass/SplitScreen.ps.slang"; -} - -SplitScreenPass::SplitScreenPass(ref pDevice, const Properties& props) - : ComparisonPass(pDevice) +// Divider colors +const float4 kColorUnselected = float4(0, 0, 0, 1); +const float4 kColorSelected = float4(1, 1, 1, 1); + +// A simple character array representing a 16x16 grayscale arrow +const unsigned char kArrowArray[256] = { + // clang-format off + 0, 0, 0, 0, 0, 0, 0, 0, 87, 13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 212,255,255,34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255,255,255,255,32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 78, 255,255,255,255,33, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 81, 255,255,255,255,32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 255,255,255,255,34, 0, + 31, 158,156,156,156,156,156,156,156,146,212,255,255,255,255,34, + 241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,240, + 241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,240, + 31, 158,156,156,156,156,156,156,156,146,212,255,255,255,255,33, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 255,255,255,255,34, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 81, 255,255,255,255,31, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 79, 255,255,255,255,32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 255,255,255,255,31, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 212,255,255,33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 87, 12, 0, 0, 0, 0, 0, 0 + // clang-format on +}; + +// Where is our shader located? +const std::string kSplitShader = "RenderPasses/DebugPasses/SplitScreenPass/SplitScreen.ps.slang"; +} // namespace + +SplitScreenPass::SplitScreenPass(ref pDevice, const Properties& props) : ComparisonPass(pDevice) { - mpArrowTex = Texture::create2D(mpDevice, 16, 16, ResourceFormat::R8Unorm, 1, Texture::kMaxPossible, kArrowArray); + mpArrowTex = mpDevice->createTexture2D(16, 16, ResourceFormat::R8Unorm, 1, Texture::kMaxPossible, kArrowArray); createProgram(); for (const auto& [key, value] : props) @@ -106,8 +107,10 @@ bool SplitScreenPass::onMouseEvent(const MouseEvent& mouseEvent) mDividerGrabbed = true; handled = true; - if (CpuTimer::calcDuration(mTimeOfLastClick, CpuTimer::getCurrentTimePoint()) < 100.0) mSplitLoc = 0.5f; - else mTimeOfLastClick = CpuTimer::getCurrentTimePoint(); + if (CpuTimer::calcDuration(mTimeOfLastClick, CpuTimer::getCurrentTimePoint()) < 100.0) + mSplitLoc = 0.5f; + else + mTimeOfLastClick = CpuTimer::getCurrentTimePoint(); } else if (mDividerGrabbed) { diff --git a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h index a3333dade..b3d18f881 100644 --- a/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h +++ b/Source/RenderPasses/DebugPasses/SplitScreenPass/SplitScreenPass.h @@ -47,15 +47,22 @@ class SplitScreenPass : public ComparisonPass private: virtual void createProgram() override; - ref mpArrowTex; // A texture storing a 16x16 grayscale arrow + + /// A texture storing a 16x16 grayscale arrow + ref mpArrowTex; // Mouse parameters - bool mMouseOverDivider = false; ///< Is the mouse over the divider? - int2 mMousePos = int2(0, 0); ///< Where was mouse in last mouse event processed - bool mDividerGrabbed = false; ///< Are we grabbing the divider? - bool mDrawArrows = false; ///< When hovering over divider, show arrows? + /// Is the mouse over the divider? + bool mMouseOverDivider = false; + /// Where was mouse in last mouse event processed + int2 mMousePos = int2(0, 0); + /// Are we grabbing the divider? + bool mDividerGrabbed = false; + + /// When hovering over divider, show arrows? + bool mDrawArrows = false; - // Double-click detection Parameters - CpuTimer::TimePoint mTimeOfLastClick {}; ///< Time since mouse was last clicked + /// Time of last mouse click (double-click detection) + CpuTimer::TimePoint mTimeOfLastClick{}; }; diff --git a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp index 06608834f..e15f1dbef 100644 --- a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp +++ b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.cpp @@ -26,64 +26,68 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "ErrorMeasurePass.h" +#include "Core/AssetResolver.h" #include namespace { - const std::string kErrorComputationShaderFile = "RenderPasses/ErrorMeasurePass/ErrorMeasurer.cs.slang"; - const std::string kConstantBufferName = "PerFrameCB"; - - // Input channels - const std::string kInputChannelWorldPosition = "WorldPosition"; - const std::string kInputChannelSourceImage = "Source"; - const std::string kInputChannelReferenceImage = "Reference"; - - // Output channel - const std::string kOutputChannelImage = "Output"; - - // Serialized parameters - const std::string kReferenceImagePath = "ReferenceImagePath"; - const std::string kMeasurementsFilePath = "MeasurementsFilePath"; - const std::string kIgnoreBackground = "IgnoreBackground"; - const std::string kComputeSquaredDifference = "ComputeSquaredDifference"; - const std::string kComputeAverage = "ComputeAverage"; - const std::string kUseLoadedReference = "UseLoadedReference"; - const std::string kReportRunningError = "ReportRunningError"; - const std::string kRunningErrorSigma = "RunningErrorSigma"; - const std::string kSelectedOutputId = "SelectedOutputId"; -} +const std::string kErrorComputationShaderFile = "RenderPasses/ErrorMeasurePass/ErrorMeasurer.cs.slang"; +const std::string kConstantBufferName = "PerFrameCB"; + +// Input channels +const std::string kInputChannelWorldPosition = "WorldPosition"; +const std::string kInputChannelSourceImage = "Source"; +const std::string kInputChannelReferenceImage = "Reference"; + +// Output channel +const std::string kOutputChannelImage = "Output"; + +// Serialized parameters +const std::string kReferenceImagePath = "ReferenceImagePath"; +const std::string kMeasurementsFilePath = "MeasurementsFilePath"; +const std::string kIgnoreBackground = "IgnoreBackground"; +const std::string kComputeSquaredDifference = "ComputeSquaredDifference"; +const std::string kComputeAverage = "ComputeAverage"; +const std::string kUseLoadedReference = "UseLoadedReference"; +const std::string kReportRunningError = "ReportRunningError"; +const std::string kRunningErrorSigma = "RunningErrorSigma"; +const std::string kSelectedOutputId = "SelectedOutputId"; +} // namespace extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { registry.registerClass(); } -const Gui::RadioButtonGroup ErrorMeasurePass::sOutputSelectionButtons = -{ - { (uint32_t)OutputId::Source, "Source", true }, - { (uint32_t)OutputId::Reference, "Reference", true }, - { (uint32_t)OutputId::Difference, "Difference", true } -}; +const Gui::RadioButtonGroup ErrorMeasurePass::sOutputSelectionButtons = { + {(uint32_t)OutputId::Source, "Source", true}, + {(uint32_t)OutputId::Reference, "Reference", true}, + {(uint32_t)OutputId::Difference, "Difference", true}}; -const Gui::RadioButtonGroup ErrorMeasurePass::sOutputSelectionButtonsSourceOnly = -{ - { (uint32_t)OutputId::Source, "Source", true } -}; +const Gui::RadioButtonGroup ErrorMeasurePass::sOutputSelectionButtonsSourceOnly = {{(uint32_t)OutputId::Source, "Source", true}}; -ErrorMeasurePass::ErrorMeasurePass(ref pDevice, const Properties& props) - : RenderPass(pDevice) +ErrorMeasurePass::ErrorMeasurePass(ref pDevice, const Properties& props) : RenderPass(pDevice) { for (const auto& [key, value] : props) { - if (key == kReferenceImagePath) mReferenceImagePath = value.operator std::filesystem::path(); - else if (key == kMeasurementsFilePath) mMeasurementsFilePath = value.operator std::filesystem::path(); - else if (key == kIgnoreBackground) mIgnoreBackground = value; - else if (key == kComputeSquaredDifference) mComputeSquaredDifference = value; - else if (key == kComputeAverage) mComputeAverage = value; - else if (key == kUseLoadedReference) mUseLoadedReference = value; - else if (key == kReportRunningError) mReportRunningError = value; - else if (key == kRunningErrorSigma) mRunningErrorSigma = value; - else if (key == kSelectedOutputId) mSelectedOutputId = value; + if (key == kReferenceImagePath) + mReferenceImagePath = value.operator std::filesystem::path(); + else if (key == kMeasurementsFilePath) + mMeasurementsFilePath = value.operator std::filesystem::path(); + else if (key == kIgnoreBackground) + mIgnoreBackground = value; + else if (key == kComputeSquaredDifference) + mComputeSquaredDifference = value; + else if (key == kComputeAverage) + mComputeAverage = value; + else if (key == kUseLoadedReference) + mUseLoadedReference = value; + else if (key == kReportRunningError) + mReportRunningError = value; + else if (key == kRunningErrorSigma) + mRunningErrorSigma = value; + else if (key == kSelectedOutputId) + mSelectedOutputId = value; else { logWarning("Unknown property '{}' in ErrorMeasurePass properties.", key); @@ -92,7 +96,7 @@ ErrorMeasurePass::ErrorMeasurePass(ref pDevice, const Properties& props) // Load/create files (if specified in config). loadReference(); - openMeasurementsFile(); + loadMeasurementsFile(); mpParallelReduction = std::make_unique(mpDevice); mpErrorMeasurerPass = ComputePass::create(mpDevice, kErrorComputationShaderFile); @@ -132,11 +136,17 @@ void ErrorMeasurePass::execute(RenderContext* pRenderContext, const RenderData& // Create the texture for the difference image if this is our first // time through or if the source image resolution has changed. const uint32_t width = pSourceImageTexture->getWidth(), height = pSourceImageTexture->getHeight(); - if (!mpDifferenceTexture || mpDifferenceTexture->getWidth() != width || - mpDifferenceTexture->getHeight() != height) + if (!mpDifferenceTexture || mpDifferenceTexture->getWidth() != width || mpDifferenceTexture->getHeight() != height) { - mpDifferenceTexture = Texture::create2D(mpDevice, width, height, ResourceFormat::RGBA32Float, 1, 1, nullptr, - Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpDifferenceTexture = mpDevice->createTexture2D( + width, + height, + ResourceFormat::RGBA32Float, + 1, + 1, + nullptr, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess + ); FALCOR_ASSERT(mpDifferenceTexture); } @@ -165,7 +175,7 @@ void ErrorMeasurePass::execute(RenderContext* pRenderContext, const RenderData& pRenderContext->blit(mpDifferenceTexture->getSRV(), pOutputImageTexture->getRTV()); break; default: - throw RuntimeError("ErrorMeasurePass: Unhandled OutputId case"); + FALCOR_THROW("ErrorMeasurePass: Unhandled OutputId case"); } saveMeasurementsToFile(); @@ -219,22 +229,20 @@ void ErrorMeasurePass::runReductionPasses(RenderContext* pRenderContext, const R void ErrorMeasurePass::renderUI(Gui::Widgets& widget) { - const auto getFilename = [](const std::filesystem::path& path) - { - return path.empty() ? "N/A" : path.filename().string(); - }; + const auto getFilename = [](const std::filesystem::path& path) { return path.empty() ? "N/A" : path.filename().string(); }; // Create a button for loading the reference image. if (widget.button("Load reference")) { FileDialogFilterVec filters; - filters.push_back({ "exr", "High Dynamic Range" }); - filters.push_back({ "pfm", "Portable Float Map" }); + filters.push_back({"exr", "High Dynamic Range"}); + filters.push_back({"pfm", "Portable Float Map"}); std::filesystem::path path; if (openFileDialog(filters, path)) { mReferenceImagePath = path; - loadReference(); + if (!loadReference()) + msgBox("Error", fmt::format("Failed to load reference image from '{}'.", path), MsgBoxType::Ok, MsgBoxIcon::Error); } } @@ -242,12 +250,13 @@ void ErrorMeasurePass::renderUI(Gui::Widgets& widget) if (widget.button("Set output data file", true)) { FileDialogFilterVec filters; - filters.push_back({ "csv", "CSV Files" }); + filters.push_back({"csv", "CSV Files"}); std::filesystem::path path; if (saveFileDialog(filters, path)) { mMeasurementsFilePath = path; - openMeasurementsFile(); + if (!loadMeasurementsFile()) + msgBox("Error", fmt::format("Failed to save measurements to '{}'.", path), MsgBoxType::Ok, MsgBoxIcon::Error); } } @@ -256,8 +265,11 @@ void ErrorMeasurePass::renderUI(Gui::Widgets& widget) if (mMeasurements.valid) { widget.radioButtons(sOutputSelectionButtons, reinterpret_cast(mSelectedOutputId)); - widget.tooltip("Press 'O' to change output mode; hold 'Shift' to reverse the cycling.\n\n" - "Note: Difference is computed based on current - reference value.", true); + widget.tooltip( + "Press 'O' to change output mode; hold 'Shift' to reverse the cycling.\n\n" + "Note: Difference is computed based on current - reference value.", + true + ); } else { @@ -266,16 +278,25 @@ void ErrorMeasurePass::renderUI(Gui::Widgets& widget) } widget.checkbox("Ignore background", mIgnoreBackground); - widget.tooltip("Do not include background pixels in the error measurements.\n" - "This option requires the optional input '" + std::string(kInputChannelWorldPosition) + "' to be bound", true); + widget.tooltip( + "Do not include background pixels in the error measurements.\n" + "This option requires the optional input '" + + std::string(kInputChannelWorldPosition) + "' to be bound", + true + ); widget.checkbox("Compute L2 error (rather than L1)", mComputeSquaredDifference); widget.checkbox("Compute RGB average", mComputeAverage); - widget.tooltip("When enabled, the average error over the RGB components is computed when creating the difference image.\n" - "The average is computed after squaring the differences when L2 error is selected."); + widget.tooltip( + "When enabled, the average error over the RGB components is computed when creating the difference image.\n" + "The average is computed after squaring the differences when L2 error is selected." + ); widget.checkbox("Use loaded reference image", mUseLoadedReference); - widget.tooltip("Take the reference from the loaded image instead or the input channel.\n\n" - "If the chosen reference doesn't exist, the error measurements are disabled.", true); + widget.tooltip( + "Take the reference from the loaded image instead or the input channel.\n\n" + "If the chosen reference doesn't exist, the error measurements are disabled.", + true + ); // Display the filename of the reference file. const std::string referenceText = "Reference: " + getFilename(mReferenceImagePath); widget.text(referenceText); @@ -304,12 +325,12 @@ void ErrorMeasurePass::renderUI(Gui::Widgets& widget) // Use stream so we can control formatting. std::ostringstream oss; oss << std::scientific; - oss << (mComputeSquaredDifference ? "MSE (avg): " : "L1 error (avg): ") << - (mReportRunningError ? mRunningAvgError : mMeasurements.avgError) << std::endl; - oss << (mComputeSquaredDifference ? "MSE (rgb): " : "L1 error (rgb): ") << - (mReportRunningError ? mRunningError.r : mMeasurements.error.r) << ", " << - (mReportRunningError ? mRunningError.g : mMeasurements.error.g) << ", " << - (mReportRunningError ? mRunningError.b : mMeasurements.error.b); + oss << (mComputeSquaredDifference ? "MSE (avg): " : "L1 error (avg): ") + << (mReportRunningError ? mRunningAvgError : mMeasurements.avgError) << std::endl; + oss << (mComputeSquaredDifference ? "MSE (rgb): " : "L1 error (rgb): ") + << (mReportRunningError ? mRunningError.r : mMeasurements.error.r) << ", " + << (mReportRunningError ? mRunningError.g : mMeasurements.error.g) << ", " + << (mReportRunningError ? mRunningError.b : mMeasurements.error.b); widget.text(oss.str()); } else @@ -332,20 +353,24 @@ bool ErrorMeasurePass::onKeyEvent(const KeyboardEvent& keyEvent) return false; } -void ErrorMeasurePass::loadReference() +bool ErrorMeasurePass::loadReference() { - if (mReferenceImagePath.empty()) return; + if (mReferenceImagePath.empty()) + return false; // TODO: it would be nice to also be able to take the reference image as an input. - mpReferenceTexture = Texture::createFromFile(mpDevice, mReferenceImagePath, false /* no MIPs */, false /* linear color */); + std::filesystem::path resolvedPath = AssetResolver::getDefaultResolver().resolvePath(mReferenceImagePath); + mpReferenceTexture = Texture::createFromFile(mpDevice, resolvedPath, false /* no MIPs */, false /* linear color */); if (!mpReferenceTexture) { - reportError(fmt::format("Failed to load texture from '{}'", mReferenceImagePath)); + logWarning("Failed to load texture from '{}'", mReferenceImagePath); mReferenceImagePath.clear(); + return false; } mUseLoadedReference = mpReferenceTexture != nullptr; - mRunningAvgError = -1.f; // Mark running error values as invalid. + mRunningAvgError = -1.f; // Mark running error values as invalid. + return true; } ref ErrorMeasurePass::getReference(const RenderData& renderData) const @@ -353,15 +378,17 @@ ref ErrorMeasurePass::getReference(const RenderData& renderData) const return mUseLoadedReference ? mpReferenceTexture : renderData.getTexture(kInputChannelReferenceImage); } -void ErrorMeasurePass::openMeasurementsFile() +bool ErrorMeasurePass::loadMeasurementsFile() { - if (mMeasurementsFilePath.empty()) return; + if (mMeasurementsFilePath.empty()) + return false; mMeasurementsFile = std::ofstream(mMeasurementsFilePath, std::ios::trunc); if (!mMeasurementsFile) { - reportError(fmt::format("Failed to open file '{}'.", mMeasurementsFilePath)); + logWarning(fmt::format("Failed to open file '{}'.", mMeasurementsFilePath)); mMeasurementsFilePath.clear(); + return false; } else { @@ -375,11 +402,14 @@ void ErrorMeasurePass::openMeasurementsFile() } mMeasurementsFile << std::scientific; } + + return true; } void ErrorMeasurePass::saveMeasurementsToFile() { - if (!mMeasurementsFile) return; + if (!mMeasurementsFile) + return; FALCOR_ASSERT(mMeasurements.valid); mMeasurementsFile << mMeasurements.avgError << ","; diff --git a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h index f17b68ca2..bea95ab78 100644 --- a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h +++ b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurePass.h @@ -46,11 +46,14 @@ class ErrorMeasurePass : public RenderPass Count }; - FALCOR_ENUM_INFO(OutputId, { - { OutputId::Source, "Source" }, - { OutputId::Reference, "Reference" }, - { OutputId::Difference, "Difference" }, - }); + FALCOR_ENUM_INFO( + OutputId, + { + {OutputId::Source, "Source"}, + {OutputId::Reference, "Reference"}, + {OutputId::Difference, "Difference"}, + } + ); static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } @@ -63,9 +66,9 @@ class ErrorMeasurePass : public RenderPass virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override; private: - void loadReference(); + bool loadReference(); ref getReference(const RenderData& renderData) const; - void openMeasurementsFile(); + bool loadMeasurementsFile(); void saveMeasurementsToFile(); void runDifferencePass(RenderContext* pRenderContext, const RenderData& renderData); @@ -76,32 +79,43 @@ class ErrorMeasurePass : public RenderPass struct { - float3 error; ///< Error (either L1 or MSE) in RGB. - float avgError; ///< Error averaged over color components. - bool valid = false; + float3 error; ///< Error (either L1 or MSE) in RGB. + float avgError; ///< Error averaged over color components. + bool valid = false; } mMeasurements; // Internal state - float3 mRunningError = float3(0.f, 0.f, 0.f); - float mRunningAvgError = -1.f; ///< A negative value indicates that both running error values are invalid. - ref mpReferenceTexture; - ref mpDifferenceTexture; + float3 mRunningError = float3(0.f, 0.f, 0.f); + /// A negative value indicates that both running error values are invalid. + float mRunningAvgError = -1.f; - std::ofstream mMeasurementsFile; + ref mpReferenceTexture; + ref mpDifferenceTexture; - // UI variables - std::filesystem::path mReferenceImagePath; ///< Path to the reference used in the comparison. - std::filesystem::path mMeasurementsFilePath; ///< Path to the output file where measurements are stored (.csv). + std::ofstream mMeasurementsFile; - bool mIgnoreBackground = true; ///< If true, do not measure error on pixels that belong to the background. - bool mComputeSquaredDifference = true; ///< Compute the square difference when creating the difference image. - bool mComputeAverage = false; ///< Compute the average of the RGB components when creating the difference image. - bool mUseLoadedReference = false; ///< If true, use loaded reference image instead of input. - bool mReportRunningError = true; ///< Use exponetial moving average (EMA) for the computed error. - float mRunningErrorSigma = 0.995f; ///< Coefficient used for the exponential moving average. Larger values mean slower response. + // UI variables - OutputId mSelectedOutputId = OutputId::Source; + /// Path to the reference used in the comparison. + std::filesystem::path mReferenceImagePath; + /// Path to the output file where measurements are stored (.csv). + std::filesystem::path mMeasurementsFilePath; + + /// If true, do not measure error on pixels that belong to the background. + bool mIgnoreBackground = true; + /// Compute the square difference when creating the difference image. + bool mComputeSquaredDifference = true; + /// Compute the average of the RGB components when creating the difference image. + bool mComputeAverage = false; + /// If true, use loaded reference image instead of input. + bool mUseLoadedReference = false; + /// Use exponetial moving average (EMA) for the computed error. + bool mReportRunningError = true; + /// Coefficient used for the exponential moving average. Larger values mean slower response. + float mRunningErrorSigma = 0.995f; + + OutputId mSelectedOutputId = OutputId::Source; static const Gui::RadioButtonGroup sOutputSelectionButtons; static const Gui::RadioButtonGroup sOutputSelectionButtonsSourceOnly; diff --git a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurer.cs.slang b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurer.cs.slang index c90215e3c..e7b333edc 100644 --- a/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurer.cs.slang +++ b/Source/RenderPasses/ErrorMeasurePass/ErrorMeasurer.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,35 +25,38 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -Texture2D gReference; -Texture2D gSource; -Texture2D gWorldPosition; +Texture2D gReference; +Texture2D gSource; +Texture2D gWorldPosition; RWTexture2D gResult; cbuffer PerFrameCB { - uint2 gResolution; - uint gIgnoreBackground; - uint gComputeDiffSqr; - uint gComputeAverage; + uint2 gResolution; + uint gIgnoreBackground; + uint gComputeDiffSqr; + uint gComputeAverage; }; [numthreads(16, 16, 1)] -void main( uint3 DTid : SV_DispatchThreadID ) +void main(uint3 DTid: SV_DispatchThreadID) { const uint2 pixel = DTid.xy; - if (any(pixel >= gResolution)) return; + if (any(pixel >= gResolution)) + return; // Determine if we should include this pixel or not. - const float4 worldPos = gWorldPosition[pixel]; - const bool isForeground = worldPos.w != 0.0f; // We're using the w-component to identify valid pixels. + const float4 worldPos = gWorldPosition[pixel]; + const bool isForeground = worldPos.w != 0.0f; // We're using the w-component to identify valid pixels. const bool isPixelValid = !gIgnoreBackground || isForeground; // Compute error based on the current options. float3 diff = isPixelValid ? abs(gSource[pixel].rgb - gReference[pixel].rgb) : float3(0.0f); - if (gComputeDiffSqr) diff *= diff; - if (gComputeAverage) diff.rgb = (diff.r + diff.g + diff.b) / 3.f; + if (gComputeDiffSqr) + diff *= diff; + if (gComputeAverage) + diff.rgb = (diff.r + diff.g + diff.b) / 3.f; gResult[pixel] = float4(diff, 0.0f); } diff --git a/Source/RenderPasses/FLIPPass/ComputeLuminance.cs.slang b/Source/RenderPasses/FLIPPass/ComputeLuminance.cs.slang index c223d54e8..015c0951d 100644 --- a/Source/RenderPasses/FLIPPass/ComputeLuminance.cs.slang +++ b/Source/RenderPasses/FLIPPass/ComputeLuminance.cs.slang @@ -26,23 +26,25 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Compute shader for computing luminance of an RGB image. - */ +/** + * Compute shader for computing luminance of an RGB image. + */ import Utils.Color.ColorHelpers; cbuffer PerFrameCB { - uint2 gResolution; + uint2 gResolution; } Texture2D gInputImage; RWBuffer gOutputLuminance; [numthreads(16, 16, 1)] -void computeLuminance(uint3 dispatchThreadId : SV_DispatchThreadID) +void computeLuminance(uint3 dispatchThreadId: SV_DispatchThreadID) { - if (any(dispatchThreadId.xy >= gResolution)) return; + if (any(dispatchThreadId.xy >= gResolution)) + return; const uint2 pixelPos = dispatchThreadId.xy; const float3 color = gInputImage[pixelPos].xyz; diff --git a/Source/RenderPasses/FLIPPass/FLIPPass.cpp b/Source/RenderPasses/FLIPPass/FLIPPass.cpp index 14fe92616..d05cc6041 100644 --- a/Source/RenderPasses/FLIPPass/FLIPPass.cpp +++ b/Source/RenderPasses/FLIPPass/FLIPPass.cpp @@ -30,38 +30,40 @@ namespace { - const char kFLIPShaderFile[] = "RenderPasses/FLIPPass/FLIPPass.cs.slang"; - const char kComputeLuminanceShaderFile[] = "RenderPasses/FLIPPass/ComputeLuminance.cs.slang"; - - const char kTestImageInput[] = "testImage"; - const char kReferenceImageInput[] = "referenceImage"; - const char kErrorMapOutput[] = "errorMap"; // High-precision FLIP error map - use for computations (not display). - const char kErrorMapDisplayOutput[] = "errorMapDisplay"; // Low-precision FLIP error map - use for display / analysis (not computations). - const char kExposureMapDisplayOutput[] = "exposureMapDisplay"; // Low-precision HDR-FLIP exposure map. - - const char kEnabled[] = "enabled"; - const char kIsHDR[] = "isHDR"; - const char kToneMapper[] = "toneMapper"; - const char kUseCustomExposureParameters[] = "useCustomExposureParameters"; - const char kStartExposure[] = "startExposure"; - const char kStopExposure[] = "stopExposure"; - const char kNumExposures[] = "numExposures"; - const char kUseMagma[] = "useMagma"; - const char kClampInput[] = "clampInput"; - const char kMonitorWidthPixels[] = "monitorWidthPixels"; - const char kMonitorWidthMeters[] = "monitorWidthMeters"; - const char kMonitorDistance[] = "monitorDistanceMeters"; - const char kComputePooledFLIPValues[] = "computePooledFLIPValues"; - const char kUseRealMonitorInfo[] = "useRealMonitorInfo"; -} +const char kFLIPShaderFile[] = "RenderPasses/FLIPPass/FLIPPass.cs.slang"; +const char kComputeLuminanceShaderFile[] = "RenderPasses/FLIPPass/ComputeLuminance.cs.slang"; + +const char kTestImageInput[] = "testImage"; +const char kReferenceImageInput[] = "referenceImage"; +// High-precision FLIP error map - use for computations (not display). +const char kErrorMapOutput[] = "errorMap"; +// Low-precision FLIP error map - use for display / analysis (not computations). +const char kErrorMapDisplayOutput[] = "errorMapDisplay"; +// Low-precision HDR-FLIP exposure map. +const char kExposureMapDisplayOutput[] = "exposureMapDisplay"; + +const char kEnabled[] = "enabled"; +const char kIsHDR[] = "isHDR"; +const char kToneMapper[] = "toneMapper"; +const char kUseCustomExposureParameters[] = "useCustomExposureParameters"; +const char kStartExposure[] = "startExposure"; +const char kStopExposure[] = "stopExposure"; +const char kNumExposures[] = "numExposures"; +const char kUseMagma[] = "useMagma"; +const char kClampInput[] = "clampInput"; +const char kMonitorWidthPixels[] = "monitorWidthPixels"; +const char kMonitorWidthMeters[] = "monitorWidthMeters"; +const char kMonitorDistance[] = "monitorDistanceMeters"; +const char kComputePooledFLIPValues[] = "computePooledFLIPValues"; +const char kUseRealMonitorInfo[] = "useRealMonitorInfo"; +} // namespace extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { registry.registerClass(); } -FLIPPass::FLIPPass(ref pDevice, const Properties& props) - : RenderPass(pDevice) +FLIPPass::FLIPPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { parseProperties(props); @@ -87,8 +89,10 @@ FLIPPass::FLIPPass(ref pDevice, const Properties& props) { // Assume first monitor is used. size_t monitorIndex = 0; - if (monitorDescs[monitorIndex].resolution.x > 0) mMonitorWidthPixels = monitorDescs[0].resolution.x; - if (monitorDescs[monitorIndex].physicalSize.x > 0) mMonitorWidthMeters = monitorDescs[0].physicalSize.x * 0.0254f; //< Convert from inches to meters + if (monitorDescs[monitorIndex].resolution.x > 0) + mMonitorWidthPixels = monitorDescs[0].resolution.x; + if (monitorDescs[monitorIndex].physicalSize.x > 0) + mMonitorWidthMeters = monitorDescs[0].physicalSize.x * 0.0254f; //< Convert from inches to meters } } @@ -117,33 +121,56 @@ void FLIPPass::parseProperties(const Properties& props) // Read settings. for (const auto& [key, value] : props) { - if (key == kEnabled) mEnabled = value; - else if (key == kIsHDR) mIsHDR = value; - else if (key == kToneMapper) mToneMapper = value; - else if (key == kUseCustomExposureParameters) mUseCustomExposureParameters = value; - else if (key == kStartExposure) mStartExposure = value; - else if (key == kStopExposure) mStopExposure = value; - else if (key == kNumExposures) mNumExposures = value; - else if (key == kUseMagma) mUseMagma = value; - else if (key == kClampInput) mClampInput = value; - else if (key == kMonitorWidthPixels) mMonitorWidthPixels = value; - else if (key == kMonitorWidthMeters) mMonitorWidthMeters = value; - else if (key == kMonitorDistance) mMonitorDistanceMeters = value; - else if (key == kComputePooledFLIPValues) mComputePooledFLIPValues = value; - else if (key == kUseRealMonitorInfo) mUseRealMonitorInfo = value; - else logWarning("Unknown property '{}' in a FLIPPass properties.", key); + if (key == kEnabled) + mEnabled = value; + else if (key == kIsHDR) + mIsHDR = value; + else if (key == kToneMapper) + mToneMapper = value; + else if (key == kUseCustomExposureParameters) + mUseCustomExposureParameters = value; + else if (key == kStartExposure) + mStartExposure = value; + else if (key == kStopExposure) + mStopExposure = value; + else if (key == kNumExposures) + mNumExposures = value; + else if (key == kUseMagma) + mUseMagma = value; + else if (key == kClampInput) + mClampInput = value; + else if (key == kMonitorWidthPixels) + mMonitorWidthPixels = value; + else if (key == kMonitorWidthMeters) + mMonitorWidthMeters = value; + else if (key == kMonitorDistance) + mMonitorDistanceMeters = value; + else if (key == kComputePooledFLIPValues) + mComputePooledFLIPValues = value; + else if (key == kUseRealMonitorInfo) + mUseRealMonitorInfo = value; + else + logWarning("Unknown property '{}' in a FLIPPass properties.", key); } } - RenderPassReflection FLIPPass::reflect(const CompileData& compileData) { RenderPassReflection reflector; - reflector.addInput(kTestImageInput, "Test image").bindFlags(Falcor::Resource::BindFlags::ShaderResource).texture2D(0, 0); - reflector.addInput(kReferenceImageInput, "Reference image").bindFlags(Falcor::Resource::BindFlags::ShaderResource).texture2D(0, 0); - reflector.addOutput(kErrorMapOutput, "FLIP error map for computations").format(ResourceFormat::RGBA32Float).bindFlags(Falcor::Resource::BindFlags::UnorderedAccess | Falcor::Resource::BindFlags::ShaderResource).texture2D(0, 0); - reflector.addOutput(kErrorMapDisplayOutput, "FLIP error map for display").format(ResourceFormat::RGBA8UnormSrgb).bindFlags(Falcor::Resource::BindFlags::RenderTarget).texture2D(0, 0); - reflector.addOutput(kExposureMapDisplayOutput, "HDR-FLIP exposure map for display").format(ResourceFormat::RGBA8UnormSrgb).bindFlags(Falcor::Resource::BindFlags::RenderTarget).texture2D(0, 0); + reflector.addInput(kTestImageInput, "Test image").bindFlags(Falcor::ResourceBindFlags::ShaderResource).texture2D(0, 0); + reflector.addInput(kReferenceImageInput, "Reference image").bindFlags(Falcor::ResourceBindFlags::ShaderResource).texture2D(0, 0); + reflector.addOutput(kErrorMapOutput, "FLIP error map for computations") + .format(ResourceFormat::RGBA32Float) + .bindFlags(Falcor::ResourceBindFlags::UnorderedAccess | Falcor::ResourceBindFlags::ShaderResource) + .texture2D(0, 0); + reflector.addOutput(kErrorMapDisplayOutput, "FLIP error map for display") + .format(ResourceFormat::RGBA8UnormSrgb) + .bindFlags(Falcor::ResourceBindFlags::RenderTarget) + .texture2D(0, 0); + reflector.addOutput(kExposureMapDisplayOutput, "HDR-FLIP exposure map for display") + .format(ResourceFormat::RGBA8UnormSrgb) + .bindFlags(Falcor::ResourceBindFlags::RenderTarget) + .texture2D(0, 0); return reflector; } @@ -183,11 +210,11 @@ static void computeMedianMax(const float* values, const uint32_t numValues, floa { std::vector sortedValues(values, values + numValues); std::sort(sortedValues.begin(), sortedValues.end()); - if (numValues & 1) // Odd number of values. + if (numValues & 1) // Odd number of values. { median = sortedValues[numValues / 2]; } - else // Even number of values. + else // Even number of values. { uint32_t medianLocation = numValues / 2 - 1; median = (sortedValues[medianLocation] + sortedValues[medianLocation + 1]) * 0.5f; @@ -200,15 +227,16 @@ void FLIPPass::computeExposureParameters(const float Ymedian, const float Ymax) std::vector tmCoefficients; if (mToneMapper == FLIPToneMapperType::Reinhard) { - tmCoefficients = { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f }; + tmCoefficients = {0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f}; } else if (mToneMapper == FLIPToneMapperType::ACES) { - tmCoefficients = { 0.6f * 0.6f * 2.51f, 0.6f * 0.03f, 0.0f, 0.6f * 0.6f * 2.43f, 0.6f * 0.59f, 0.14f }; // 0.6 is pre-exposure cancellation. + // 0.6 is pre-exposure cancellation. + tmCoefficients = {0.6f * 0.6f * 2.51f, 0.6f * 0.03f, 0.0f, 0.6f * 0.6f * 2.43f, 0.6f * 0.59f, 0.14f}; } else if (mToneMapper == FLIPToneMapperType::Hable) { - tmCoefficients = { 0.231683f, 0.013791f, 0.0f, 0.18f, 0.3f, 0.018f }; + tmCoefficients = {0.231683f, 0.013791f, 0.0f, 0.18f, 0.3f, 0.018f}; } else { @@ -233,7 +261,8 @@ void FLIPPass::computeExposureParameters(const float Ymedian, const float Ymax) void FLIPPass::execute(RenderContext* pRenderContext, const RenderData& renderData) { - if (!mEnabled) return; + if (!mEnabled) + return; // Pick up resources from render graph. const auto& pTestImageInput = renderData.getTexture(kTestImageInput); @@ -253,15 +282,34 @@ void FLIPPass::execute(RenderContext* pRenderContext, const RenderData& renderDa // Refresh internal high precision buffer for FLIP results. uint2 outputResolution = uint2(pReferenceImageInput->getWidth(), pReferenceImageInput->getHeight()); - if (!mpFLIPErrorMapDisplay || !mpExposureMapDisplay || mpFLIPErrorMapDisplay->getWidth() != outputResolution.x || mpFLIPErrorMapDisplay->getHeight() != outputResolution.y) + if (!mpFLIPErrorMapDisplay || !mpExposureMapDisplay || mpFLIPErrorMapDisplay->getWidth() != outputResolution.x || + mpFLIPErrorMapDisplay->getHeight() != outputResolution.y) { - mpFLIPErrorMapDisplay = Texture::create2D(mpDevice, outputResolution.x, outputResolution.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource); - mpExposureMapDisplay = Texture::create2D(mpDevice, outputResolution.x, outputResolution.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource); + mpFLIPErrorMapDisplay = mpDevice->createTexture2D( + outputResolution.x, + outputResolution.y, + ResourceFormat::RGBA32Float, + 1, + 1, + nullptr, + ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource + ); + mpExposureMapDisplay = mpDevice->createTexture2D( + outputResolution.x, + outputResolution.y, + ResourceFormat::RGBA32Float, + 1, + 1, + nullptr, + ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource + ); } if (!mpLuminance) { - mpLuminance = Buffer::create(mpDevice, outputResolution.x * outputResolution.y * sizeof(float), ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None); + mpLuminance = mpDevice->createBuffer( + outputResolution.x * outputResolution.y * sizeof(float), ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal + ); } { @@ -298,12 +346,11 @@ void FLIPPass::execute(RenderContext* pRenderContext, const RenderData& renderDa rootVar["PerFrameCB"]["gResolution"] = outputResolution; // Compute luminance of the reference image. mpComputeLuminancePass->execute(pRenderContext, uint3(outputResolution.x, outputResolution.y, 1u)); - pRenderContext->flush(true); + pRenderContext->submit(true); float Ymedian, Ymax; - const float* luminanceValues = (float*)mpLuminance->map(Buffer::MapType::Read); - computeMedianMax(luminanceValues, outputResolution.x * outputResolution.y, Ymedian, Ymax); - mpLuminance->unmap(); + std::vector luminanceValues = mpLuminance->getElements(0, outputResolution.x * outputResolution.y); + computeMedianMax(luminanceValues.data(), luminanceValues.size(), Ymedian, Ymax); computeExposureParameters(Ymedian, Ymax); } @@ -321,7 +368,7 @@ void FLIPPass::execute(RenderContext* pRenderContext, const RenderData& renderDa float4 FLIPSum, FLIPMinMax[2]; mpParallelReduction->execute(pRenderContext, pErrorMapOutput, ParallelReduction::Type::Sum, &FLIPSum); mpParallelReduction->execute(pRenderContext, pErrorMapOutput, ParallelReduction::Type::MinMax, &FLIPMinMax[0]); - pRenderContext->flush(true); + pRenderContext->submit(true); // Extract metrics from readback values. RGB channels contain magma mapping, and the alpa channel contains FLIP value. mAverageFLIP = FLIPSum.a / (outputResolution.x * outputResolution.y); @@ -340,7 +387,10 @@ void FLIPPass::renderUI(Gui::Widgets& widget) dirty |= widget.checkbox("Clamp input", mClampInput); widget.tooltip("Clamp FLIP input to the expected range ([0,1] for LDR-FLIP and [0, inf) for HDR-FLIP)."); dirty |= widget.checkbox("Input is HDR", mIsHDR); - widget.tooltip("If input has high dynamic range, use HDR-FLIP instead of the default (LDR-FLIP) which only works for low dynamic range input."); + widget.tooltip( + "If input has high dynamic range, use HDR-FLIP instead of the default (LDR-FLIP) " + "which only works for low dynamic range input." + ); if (mIsHDR) { diff --git a/Source/RenderPasses/FLIPPass/FLIPPass.cs.slang b/Source/RenderPasses/FLIPPass/FLIPPass.cs.slang index 605e4d702..027acda86 100644 --- a/Source/RenderPasses/FLIPPass/FLIPPass.cs.slang +++ b/Source/RenderPasses/FLIPPass/FLIPPass.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -63,7 +63,8 @@ static const float gpt = 0.95f; static const float gw = 0.082f; static const float gqf = 0.5f; -static float MaxDistance = pow(HyAB(Hunt(linearRGBToCIELab(float3(0.0f, 1.0f, 0.0f))), Hunt(linearRGBToCIELab(float3(0.0f, 0.0f, 1.0f)))), gqc); +static float MaxDistance = + pow(HyAB(Hunt(linearRGBToCIELab(float3(0.0f, 1.0f, 0.0f))), Hunt(linearRGBToCIELab(float3(0.0f, 0.0f, 1.0f)))), gqc); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -142,7 +143,7 @@ float LDRFLIP(uint2 pixel, float exposure = 0.0f) // Variables for CSF filtering. const float4 abValuesA = { 1.0f, 0.0f, 0.0047f, 1.0e-5f }; // a1, a2, b1, b2 for A. - const float4 abValuesRG = { 1.0f, 0.0f, 0.0053f, 1.0e-5f }; // a1, a2, b1, b2 for RG. + const float4 abValuesRG = { 1.0f, 0.0f, 0.0053f, 1.0e-5f }; // a1, a2, b1, b2 for RG. const float4 abValuesBY = { 34.1f, 13.5f, 0.04f, 0.025f }; // a1, a2, b1, b2 for BY. float3 colorWeight = { 0.0f, 0.0f }; float3 csfKernelSum = float3(0.0f, 0.0f, 0.0f); @@ -167,7 +168,8 @@ float LDRFLIP(uint2 pixel, float exposure = 0.0f) float2 testPointGradient = float2(0.0f, 0.0f); // Use radius of the spatial filter kernel, as it is always greater than or equal to the radius of the feature detection kernel. - int radius = int(ceil(3.0f * sqrt(0.04f / (2.0f * M_PI * M_PI)) * pixelsPerDegree)); // See FLIP paper for explanation of the 0.04 and 3.0 factors. + // See FLIP paper for explanation of the 0.04 and 3.0 factors. + int radius = int(ceil(3.0f * sqrt(0.04f / (2.0f * M_PI * M_PI)) * pixelsPerDegree)); // Prepare point and edge kernel sums for feature detection. for (int y = -radius; y <= radius; y++) @@ -200,7 +202,6 @@ float LDRFLIP(uint2 pixel, float exposure = 0.0f) float3 referenceColor = getPixel(gReferenceImage, pixelNeighbor, exposure); float3 testColor = getPixel(gTestImage, pixelNeighbor, exposure); - // **************** COLOR PIPELINE ******************** // float2 p = float2(x, y) * dx; float dist2 = -(p.x * p.x + p.y * p.y) * M_PI * M_PI; @@ -213,12 +214,14 @@ float LDRFLIP(uint2 pixel, float exposure = 0.0f) referenceColorSum += colorWeight * referenceColor; testColorSum += colorWeight * testColor; - // **************** FEATURE PIPELINE ******************** // float g = exp(-(x * x + y * y) / (2.0f * sigmaFeaturesSquared)); pointWeight = (float2(x * x, y * y) / sigmaFeaturesSquared - 1) * g; - pointNormalization = float2(1.0f, 1.0f) / float2(pointWeight.x >= 0.0f ? positiveKernelSum : negativeKernelSum, pointWeight.y >= 0.0f ? positiveKernelSum : negativeKernelSum); + pointNormalization = float2(1.0f, 1.0f) / float2( + pointWeight.x >= 0.0f ? positiveKernelSum : negativeKernelSum, + pointWeight.y >= 0.0f ? positiveKernelSum : negativeKernelSum + ); edgeWeight = -float2(x, y) * g; edgeNormalization = float2(1.0f, 1.0f) / float2(edgeKernelSum, edgeKernelSum); @@ -268,20 +271,22 @@ float HDRFLIP(uint2 pixel) // Store exposure map. float exposureMapFloatIndex = float(exposureMapIndex) / (gNumExposures - 1.0f); - gExposureMapDisplay[pixel] = float4(sRGBToLinear(ViridisMap[int(exposureMapFloatIndex * 255.0f + 0.5f)]), 1.0f); // See reasoning for sRGB2LinearRGB in main(). + // See reasoning for sRGB2LinearRGB in main(). + gExposureMapDisplay[pixel] = float4(sRGBToLinear(ViridisMap[int(exposureMapFloatIndex * 255.0f + 0.5f)]), 1.0f); return hdrflip; } float FLIP(uint2 pixel) { - if (gIsHDR) return HDRFLIP(pixel); - else return LDRFLIP(pixel); + if (gIsHDR) + return HDRFLIP(pixel); + else + return LDRFLIP(pixel); } - [numthreads(32, 32, 1)] -void main(uint3 globalId : SV_DispatchThreadID, uint3 groupThreadId : SV_GroupThreadId) +void main(uint3 globalId: SV_DispatchThreadID, uint3 groupThreadId: SV_GroupThreadId) { uint2 pixel = globalId.xy; diff --git a/Source/RenderPasses/FLIPPass/FLIPPass.h b/Source/RenderPasses/FLIPPass/FLIPPass.h index 4c7f9dc94..d26504b27 100644 --- a/Source/RenderPasses/FLIPPass/FLIPPass.h +++ b/Source/RenderPasses/FLIPPass/FLIPPass.h @@ -37,14 +37,19 @@ using namespace Falcor; class FLIPPass : public RenderPass { public: - FALCOR_PLUGIN_CLASS(FLIPPass, "FLIPPass", { - "FLIP Metric Pass.\n\n" - "If the input has high dynamic range, check the \"Compute HDR-FLIP\" box below.\n\n" - "The errorMapDisplay shows the FLIP error map. When HDR-FLIP is computed, the user may also show the HDR-FLIP exposure map.\n\n" - "When \"List all output\" is checked, the user may also store the errorMap. This is a high-precision, linear buffer " - "which is transformed to sRGB before display. NOTE: This sRGB transform will make the displayed output look different compared " - "to the errorMapDisplay. The transform is only added before display, however, and will NOT affect the output when it is saved to disk." - }); + FALCOR_PLUGIN_CLASS( + FLIPPass, + "FLIPPass", + {"FLIP Metric Pass.\n\n" + "If the input has high dynamic range, check the \"Compute HDR-FLIP\" box below.\n\n" + "The errorMapDisplay shows the FLIP error map. " + "When HDR-FLIP is computed, the user may also show the HDR-FLIP exposure map.\n\n" + "When \"List all output\" is checked, the user may also store the errorMap. " + "This is a high-precision, linear buffer which is transformed to sRGB before display. " + "NOTE: This sRGB transform will make the displayed output look different compared " + "to the errorMapDisplay. The transform is only added before display, however, " + "and will NOT affect the output when it is saved to disk."} + ); static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } @@ -61,33 +66,58 @@ class FLIPPass : public RenderPass void parseProperties(const Properties& props); private: - bool mEnabled = true; ///< Enables FLIP calculation. + /// Enables FLIP calculation. + bool mEnabled = true; - bool mUseMagma = true; ///< Enable to map FLIP result to magma colormap. - bool mClampInput = false; ///< Enable to clamp FLIP input to the expected range ([0,1] for LDR-FLIP and [0, inf) for HDR-FLIP). - uint mMonitorWidthPixels; ///< Horizontal monitor resolution. - float mMonitorWidthMeters; ///< Width of the monitor in meters. - float mMonitorDistanceMeters; ///< Distance of monitor from the viewer in meters. + /// Enable to map FLIP result to magma colormap. + bool mUseMagma = true; + /// Enable to clamp FLIP input to the expected range ([0,1] for LDR-FLIP and [0, inf) for HDR-FLIP). + bool mClampInput = false; + /// Horizontal monitor resolution. + uint mMonitorWidthPixels; + /// Width of the monitor in meters. + float mMonitorWidthMeters; + /// Distance of monitor from the viewer in meters. + float mMonitorDistanceMeters; - bool mIsHDR = false; ///< Enable to compute HDR-FLIP. - bool mUseCustomExposureParameters = false; ///< Enable to choose custom HDR-FLIP exposure parameters (start exposure, stop exposure, and number of exposures). - FLIPToneMapperType mToneMapper = FLIPToneMapperType::ACES; ///< Mode for controlling adaptive sampling. - float mStartExposure = 0.0f; ///< Start exposure used for HDR-FLIP. - float mStopExposure = 0.0f; ///< Stop exposure used for HDR-FLIP. - float mExposureDelta = 0.0f; ///< Exposure delta used for HDR-FLIP (startExposure + (numExposures - 1) * exposureDelta = stopExposure). - uint32_t mNumExposures = 2; ///< Number of exposures used for HDR-FLIP. + /// Enable to compute HDR-FLIP. + bool mIsHDR = false; + /// Enable to choose custom HDR-FLIP exposure parameters (start exposure, stop exposure, and number of exposures). + bool mUseCustomExposureParameters = false; + /// Mode for controlling adaptive sampling. + FLIPToneMapperType mToneMapper = FLIPToneMapperType::ACES; + /// Start exposure used for HDR-FLIP. + float mStartExposure = 0.0f; + /// Stop exposure used for HDR-FLIP. + float mStopExposure = 0.0f; + /// Exposure delta used for HDR-FLIP (startExposure + (numExposures - 1) * exposureDelta = stopExposure). + float mExposureDelta = 0.0f; + /// Number of exposures used for HDR-FLIP. + uint32_t mNumExposures = 2; - ref mpFLIPErrorMapDisplay; ///< Internal buffer for temporary display output buffer. - ref mpExposureMapDisplay; ///< Internal buffer for the HDR-FLIP exposure map. - ref mpLuminance; ///< Internal buffer for temporary luminance. - ref mpFLIPPass; ///< Compute pass to calculate FLIP. - ref mpComputeLuminancePass; ///< Compute pass for computing the luminance of an image. - std::unique_ptr mpParallelReduction; ///< Helper for parallel reduction on the GPU. + /// Internal buffer for temporary display output buffer. + ref mpFLIPErrorMapDisplay; + /// Internal buffer for the HDR-FLIP exposure map. + ref mpExposureMapDisplay; + /// Internal buffer for temporary luminance. + ref mpLuminance; + /// Compute pass to calculate FLIP. + ref mpFLIPPass; + /// Compute pass for computing the luminance of an image. + ref mpComputeLuminancePass; + /// Helper for parallel reduction on the GPU. + std::unique_ptr mpParallelReduction; - bool mComputePooledFLIPValues = false; ///< Enable to use parallel reduction to compute FLIP mean/min/max across whole frame. - float mAverageFLIP; ///< Average FLIP value across whole frame. - float mMinFLIP; ///< Minimum FLIP value across whole frame. - float mMaxFLIP; ///< Maximum FLIP value across whole frame. - bool mUseRealMonitorInfo = false; ///< When enabled, user-proided monitor data will be overriden by real monitor data from the OS. - bool mRecompile = true; ///< Recompilation flag. + /// Enable to use parallel reduction to compute FLIP mean/min/max across whole frame. + bool mComputePooledFLIPValues = false; + /// Average FLIP value across whole frame. + float mAverageFLIP; + /// Minimum FLIP value across whole frame. + float mMinFLIP; + /// Maximum FLIP value across whole frame. + float mMaxFLIP; + /// When enabled, user-proided monitor data will be overriden by real monitor data from the OS. + bool mUseRealMonitorInfo = false; + /// Recompilation flag. + bool mRecompile = true; }; diff --git a/Source/RenderPasses/FLIPPass/ToneMappers.slang b/Source/RenderPasses/FLIPPass/ToneMappers.slang index c7e0c22a9..75804a3ec 100644 --- a/Source/RenderPasses/FLIPPass/ToneMappers.slang +++ b/Source/RenderPasses/FLIPPass/ToneMappers.slang @@ -26,7 +26,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Enums for FLIP -- see FLIPPass.cs.slang and FLIPPass.cpp|h +/** + * Enums for FLIP -- see FLIPPass.cs.slang and FLIPPass.cpp|h */ #pragma once @@ -34,9 +35,10 @@ BEGIN_NAMESPACE_FALCOR -/** This enum is shared between CPU/GPU. - It enumerates the different tone mapper options for HDR-FLIP. -*/ +/** + * This enum is shared between CPU/GPU. + * It enumerates the different tone mapper options for HDR-FLIP. + */ enum class FLIPToneMapperType : uint32_t { ACES = 0, @@ -44,11 +46,14 @@ enum class FLIPToneMapperType : uint32_t Reinhard = 2, }; -FALCOR_ENUM_INFO(FLIPToneMapperType, { - { FLIPToneMapperType::ACES, "ACES" }, - { FLIPToneMapperType::Hable, "Hable" }, - { FLIPToneMapperType::Reinhard, "Reinhard" }, -}); +FALCOR_ENUM_INFO( + FLIPToneMapperType, + { + { FLIPToneMapperType::ACES, "ACES" }, + { FLIPToneMapperType::Hable, "Hable" }, + { FLIPToneMapperType::Reinhard, "Reinhard" }, + } +); FALCOR_ENUM_REGISTER(FLIPToneMapperType); #ifndef HOST_CODE diff --git a/Source/RenderPasses/GBuffer/GBuffer/DepthPass.3d.slang b/Source/RenderPasses/GBuffer/GBuffer/DepthPass.3d.slang index 58e2d66da..2b50a7d16 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/DepthPass.3d.slang +++ b/Source/RenderPasses/GBuffer/GBuffer/DepthPass.3d.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -32,10 +32,11 @@ VSOut vsMain(VSIn vIn) return defaultVS(vIn); } -void psMain(VSOut vsOut, uint triangleIndex : SV_PrimitiveID) : SV_TARGET +void psMain(VSOut vsOut, uint triangleIndex: SV_PrimitiveID) : SV_TARGET { #if USE_ALPHA_TEST let lod = ImplicitLodTextureSampler(); - if (alphaTest(vsOut, triangleIndex, lod)) discard; + if (alphaTest(vsOut, triangleIndex, lod)) + discard; #endif } diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBuffer.cpp b/Source/RenderPasses/GBuffer/GBuffer/GBuffer.cpp index 6a6f06bd5..b8a081c97 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBuffer.cpp +++ b/Source/RenderPasses/GBuffer/GBuffer/GBuffer.cpp @@ -29,16 +29,16 @@ namespace Falcor { - // Update 'mtlData' channel format if size changes. - // Note: Currently, we only store the first 8 bytes of the material header. - static_assert(sizeof(MaterialHeader) == 16); -} +// Update 'mtlData' channel format if size changes. +// Note: Currently, we only store the first 8 bytes of the material header. +static_assert(sizeof(MaterialHeader) == 16); +} // namespace Falcor // List of primary GBuffer channels. These correspond to the render targets // used in the GBufferRaster pixel shader. Note that channel order should // correspond to SV_TARGET index order. -const ChannelList GBuffer::kGBufferChannels = -{ +const ChannelList GBuffer::kGBufferChannels = { + // clang-format off { "posW", "gPosW", "Position in world space", true /* optional */, ResourceFormat::RGBA32Float }, { "normW", "gNormW", "Shading normal in world space", true /* optional */, ResourceFormat::RGBA32Float }, { "tangentW", "gTangentW", "Shading tangent in world space (xyz) and sign (w)", true /* optional */, ResourceFormat::RGBA32Float }, @@ -47,10 +47,11 @@ const ChannelList GBuffer::kGBufferChannels = { "texGrads", "gTexGrads", "Texture gradients (ddx, ddy)", true /* optional */, ResourceFormat::RGBA16Float }, { "mvec", "gMotionVector", "Motion vector", true /* optional */, ResourceFormat::RG32Float }, { "mtlData", "gMaterialData", "Material data (ID, header.x, header.y, lobes)", true /* optional */, ResourceFormat::RGBA32Uint }, + // clang-format on }; -GBuffer::GBuffer(ref pDevice) - : GBufferBase(pDevice) +GBuffer::GBuffer(ref pDevice) : GBufferBase(pDevice) { - FALCOR_ASSERT(kGBufferChannels.size() == 8); // The list of primary GBuffer channels should contain 8 entries, corresponding to the 8 render targets. + // The list of primary GBuffer channels should contain 8 entries, corresponding to the 8 render targets. + FALCOR_ASSERT(kGBufferChannels.size() == 8); } diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBuffer.h b/Source/RenderPasses/GBuffer/GBuffer/GBuffer.h index b9a9b26c4..035e71897 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBuffer.h +++ b/Source/RenderPasses/GBuffer/GBuffer/GBuffer.h @@ -31,8 +31,9 @@ using namespace Falcor; -/** Base class for the different G-buffer passes. -*/ +/** + * Base class for the different G-buffer passes. + */ class GBuffer : public GBufferBase { protected: diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferHelpers.slang b/Source/RenderPasses/GBuffer/GBuffer/GBufferHelpers.slang index 0e7f2b725..ce267fcf8 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferHelpers.slang +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferHelpers.slang @@ -26,8 +26,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Helper functions for generating the G-buffer. -*/ +/** + * Helper functions for generating the G-buffer. + */ import Scene.SceneTypes; import Scene.ShadingData; @@ -44,7 +45,7 @@ struct GBufferData float4 faceNormalW; float4 guideNormalW; float2 texC; - uint4 mtlData; + uint4 mtlData; // Legacy channels float4 diffuseOpacity; @@ -52,8 +53,9 @@ struct GBufferData float4 emissive; }; -/** Helper function to prepare G-buffer data. -*/ +/** + * Helper function to prepare G-buffer data. + */ GBufferData prepareGBufferData(const ShadingData sd, const VertexData v, const IMaterialInstance mi, const BSDFProperties bsdfProperties) { GBufferData gbuf = {}; @@ -62,18 +64,18 @@ GBufferData prepareGBufferData(const ShadingData sd, const VertexData v, const I // This is needed for correctly orthonormalizing the tangent frame and computing the bitangent in passes that consume the G-buffer data. float bitangentSign = sd.frame.getHandednessSign(); - gbuf.posW = float4(sd.posW, 1.f); - gbuf.normW = float4(sd.frame.N, 0.f); - gbuf.tangentW = v.tangentW; - gbuf.faceNormalW = float4(sd.faceN, 0.f); - gbuf.guideNormalW = float4(bsdfProperties.guideNormal, 0.f); - gbuf.texC = sd.uv; - gbuf.mtlData = uint4(sd.materialID, sd.mtl.packedData.x, sd.mtl.packedData.y, mi.getLobeTypes(sd)); + gbuf.posW = float4(sd.posW, 1.f); + gbuf.normW = float4(sd.frame.N, 0.f); + gbuf.tangentW = v.tangentW; + gbuf.faceNormalW = float4(sd.faceN, 0.f); + gbuf.guideNormalW = float4(bsdfProperties.guideNormal, 0.f); + gbuf.texC = sd.uv; + gbuf.mtlData = uint4(sd.materialID, sd.mtl.packedData.x, sd.mtl.packedData.y, mi.getLobeTypes(sd)); // Setup legacy material channels. - gbuf.diffuseOpacity = float4(bsdfProperties.diffuseReflectionAlbedo, sd.opacity); - gbuf.specRough = float4(bsdfProperties.specularReflectance, bsdfProperties.roughness); - gbuf.emissive = float4(bsdfProperties.emission, 0.f); + gbuf.diffuseOpacity = float4(bsdfProperties.diffuseReflectionAlbedo, 1.f); + gbuf.specRough = float4(bsdfProperties.specularReflectance, bsdfProperties.roughness); + gbuf.emissive = float4(bsdfProperties.emission, 0.f); return gbuf; } diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cpp b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cpp index 95f72af8a..ea7f6f871 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cpp +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cpp @@ -31,47 +31,47 @@ namespace { - const std::string kProgramRaytraceFile = "RenderPasses/GBuffer/GBuffer/GBufferRT.rt.slang"; - const std::string kProgramComputeFile = "RenderPasses/GBuffer/GBuffer/GBufferRT.cs.slang"; - - // Scripting options. - const char kUseTraceRayInline[] = "useTraceRayInline"; - const char kUseDOF[] = "useDOF"; - - // Ray tracing settings that affect the traversal stack size. Set as small as possible. - const uint32_t kMaxPayloadSizeBytes = 4; - const uint32_t kMaxRecursionDepth = 1; - - // Scripting options - const std::string kLODMode = "texLOD"; - - // Additional output channels. - const std::string kVBufferName = "vbuffer"; - const ChannelList kGBufferExtraChannels = - { - { kVBufferName, "gVBuffer", "Visibility buffer", true /* optional */, ResourceFormat::Unknown /* set at runtime */ }, - { "depth", "gDepth", "Depth buffer (NDC)", true /* optional */, ResourceFormat::R32Float }, - { "linearZ", "gLinearZ", "Linear Z and slope", true /* optional */, ResourceFormat::RG32Float }, - { "mvecW", "gMotionVectorW", "Motion vector in world space", true /* optional */, ResourceFormat::RGBA16Float }, - { "normWRoughnessMaterialID", "gNormalWRoughnessMaterialID", "Guide normal in world space, roughness, and material ID", true /* optional */, ResourceFormat::RGB10A2Unorm }, - { "guideNormalW", "gGuideNormalW", "Guide normal in world space", true /* optional */, ResourceFormat::RGBA32Float }, - { "diffuseOpacity", "gDiffOpacity", "Diffuse reflection albedo and opacity", true /* optional */, ResourceFormat::RGBA32Float }, - { "specRough", "gSpecRough", "Specular reflectance and roughness", true /* optional */, ResourceFormat::RGBA32Float }, - { "emissive", "gEmissive", "Emissive color", true /* optional */, ResourceFormat::RGBA32Float }, - { "viewW", "gViewW", "View direction in world space", true /* optional */, ResourceFormat::RGBA32Float }, // TODO: Switch to packed 2x16-bit snorm format. - { "time", "gTime", "Per-pixel execution time", true /* optional */, ResourceFormat::R32Uint }, - { "disocclusion", "gDisocclusion", "Disocclusion mask", true /* optional */, ResourceFormat::R32Float }, - { "mask", "gMask", "Mask", true /* optional */, ResourceFormat::R32Float }, - }; +const std::string kProgramRaytraceFile = "RenderPasses/GBuffer/GBuffer/GBufferRT.rt.slang"; +const std::string kProgramComputeFile = "RenderPasses/GBuffer/GBuffer/GBufferRT.cs.slang"; + +// Scripting options. +const char kUseTraceRayInline[] = "useTraceRayInline"; +const char kUseDOF[] = "useDOF"; + +// Ray tracing settings that affect the traversal stack size. Set as small as possible. +const uint32_t kMaxPayloadSizeBytes = 4; +const uint32_t kMaxRecursionDepth = 1; + +// Scripting options +const std::string kLODMode = "texLOD"; + +// Additional output channels. +const std::string kVBufferName = "vbuffer"; +const ChannelList kGBufferExtraChannels = { + // clang-format off + { kVBufferName, "gVBuffer", "Visibility buffer", true /* optional */, ResourceFormat::Unknown /* set at runtime */ }, + { "depth", "gDepth", "Depth buffer (NDC)", true /* optional */, ResourceFormat::R32Float }, + { "linearZ", "gLinearZ", "Linear Z and slope", true /* optional */, ResourceFormat::RG32Float }, + { "mvecW", "gMotionVectorW", "Motion vector in world space", true /* optional */, ResourceFormat::RGBA16Float }, + { "normWRoughnessMaterialID", "gNormalWRoughnessMaterialID", "Guide normal in world space, roughness, and material ID", true /* optional */, ResourceFormat::RGB10A2Unorm }, + { "guideNormalW", "gGuideNormalW", "Guide normal in world space", true /* optional */, ResourceFormat::RGBA32Float }, + { "diffuseOpacity", "gDiffOpacity", "Diffuse reflection albedo and opacity", true /* optional */, ResourceFormat::RGBA32Float }, + { "specRough", "gSpecRough", "Specular reflectance and roughness", true /* optional */, ResourceFormat::RGBA32Float }, + { "emissive", "gEmissive", "Emissive color", true /* optional */, ResourceFormat::RGBA32Float }, + { "viewW", "gViewW", "View direction in world space", true /* optional */, ResourceFormat::RGBA32Float }, // TODO: Switch to packed 2x16-bit snorm format. + { "time", "gTime", "Per-pixel execution time", true /* optional */, ResourceFormat::R32Uint }, + { "disocclusion", "gDisocclusion", "Disocclusion mask", true /* optional */, ResourceFormat::R32Float }, + { "mask", "gMask", "Mask", true /* optional */, ResourceFormat::R32Float }, + // clang-format on }; +} // namespace -GBufferRT::GBufferRT(ref pDevice, const Properties& props) - : GBuffer(pDevice) +GBufferRT::GBufferRT(ref pDevice, const Properties& props) : GBuffer(pDevice) { + if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_5)) + FALCOR_THROW("GBufferRT requires Shader Model 6.5 support."); if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) - { - throw RuntimeError("GBufferRT: Raytracing Tier 1.1 is not supported by the current device"); - } + FALCOR_THROW("GBufferRT requires Raytracing Tier 1.1 support."); parseProperties(props); @@ -99,12 +99,16 @@ void GBufferRT::execute(RenderContext* pRenderContext, const RenderData& renderD // Update frame dimension based on render pass output. // In this pass all outputs are optional, so we must first find one that exists. ref pOutput; - auto findOutput = [&](const std::string& name) { + auto findOutput = [&](const std::string& name) + { auto pTex = renderData.getTexture(name); - if (pTex && !pOutput) pOutput = pTex; + if (pTex && !pOutput) + pOutput = pTex; }; - for (const auto& channel : kGBufferChannels) findOutput(channel.name); - for (const auto& channel : kGBufferExtraChannels) findOutput(channel.name); + for (const auto& channel : kGBufferChannels) + findOutput(channel.name); + for (const auto& channel : kGBufferExtraChannels) + findOutput(channel.name); if (!pOutput) { @@ -123,7 +127,8 @@ void GBufferRT::execute(RenderContext* pRenderContext, const RenderData& renderD } // Check for scene changes. - if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged) || + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded) || + is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged) || is_set(mpScene->getUpdates(), Scene::UpdateFlags::SDFGridConfigChanged)) { recreatePrograms(); @@ -140,7 +145,8 @@ void GBufferRT::execute(RenderContext* pRenderContext, const RenderData& renderD if (mLODMode == TexLODMode::RayDiffs) { // TODO: Remove this warning when the TexLOD code has been fixed. - // logWarning("GBufferRT::execute() - Ray differentials are not tested for instance transforms that flip the coordinate system handedness. The results may be incorrect."); + // logWarning("GBufferRT::execute() - Ray differentials are not tested for instance transforms that flip the coordinate system + // handedness. The results may be incorrect."); } mUseTraceRayInline ? executeCompute(pRenderContext, renderData) : executeRaytrace(pRenderContext, renderData); @@ -164,12 +170,15 @@ void GBufferRT::renderUI(Gui::Widgets& widget) mOptionsChanged = true; } - if (widget.checkbox("Use depth-of-field", mUseDOF)) { mOptionsChanged = true; } - widget.tooltip("This option enables stochastic depth-of-field when the camera's aperture radius is nonzero. Disable it to force the use of a pinhole camera.", true); + widget.tooltip( + "This option enables stochastic depth-of-field when the camera's aperture radius is nonzero. " + "Disable it to force the use of a pinhole camera.", + true + ); } Properties GBufferRT::getProperties() const @@ -194,9 +203,12 @@ void GBufferRT::parseProperties(const Properties& props) for (const auto& [key, value] : props) { - if (key == kLODMode) mLODMode = value; - else if (key == kUseTraceRayInline) mUseTraceRayInline = value; - else if (key == kUseDOF) mUseDOF = value; + if (key == kLODMode) + mLODMode = value; + else if (key == kUseTraceRayInline) + mUseTraceRayInline = value; + else if (key == kUseDOF) + mUseDOF = value; // TODO: Check for unparsed fields, including those parsed in base classes. } } @@ -218,7 +230,7 @@ void GBufferRT::executeRaytrace(RenderContext* pRenderContext, const RenderData& defines.add(getShaderDefines(renderData)); // Create ray tracing program. - RtProgram::Desc desc; + ProgramDesc desc; desc.addShaderModules(mpScene->getShaderModules()); desc.addShaderLibrary(kProgramRaytraceFile); desc.addTypeConformances(mpScene->getTypeConformances()); @@ -234,35 +246,43 @@ void GBufferRT::executeRaytrace(RenderContext* pRenderContext, const RenderData& // Add hit group with intersection shader for displaced meshes. if (mpScene->hasGeometryType(Scene::GeometryType::DisplacedTriangleMesh)) { - sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::DisplacedTriangleMesh), desc.addHitGroup("displacedTriangleMeshClosestHit", "", "displacedTriangleMeshIntersection")); + sbt->setHitGroup( + 0, + mpScene->getGeometryIDs(Scene::GeometryType::DisplacedTriangleMesh), + desc.addHitGroup("displacedTriangleMeshClosestHit", "", "displacedTriangleMeshIntersection") + ); } // Add hit group with intersection shader for curves (represented as linear swept spheres). if (mpScene->hasGeometryType(Scene::GeometryType::Curve)) { - sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::Curve), desc.addHitGroup("curveClosestHit", "", "curveIntersection")); + sbt->setHitGroup( + 0, mpScene->getGeometryIDs(Scene::GeometryType::Curve), desc.addHitGroup("curveClosestHit", "", "curveIntersection") + ); } // Add hit group with intersection shader for SDF grids. if (mpScene->hasGeometryType(Scene::GeometryType::SDFGrid)) { - sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::SDFGrid), desc.addHitGroup("sdfGridClosestHit", "", "sdfGridIntersection")); + sbt->setHitGroup( + 0, mpScene->getGeometryIDs(Scene::GeometryType::SDFGrid), desc.addHitGroup("sdfGridClosestHit", "", "sdfGridIntersection") + ); } // Add hit groups for for other procedural primitives here. - mRaytrace.pProgram = RtProgram::create(mpDevice, desc, defines); + mRaytrace.pProgram = Program::create(mpDevice, desc, defines); mRaytrace.pVars = RtProgramVars::create(mpDevice, mRaytrace.pProgram, sbt); // Bind static resources. ShaderVar var = mRaytrace.pVars->getRootVar(); - mpSampleGenerator->setShaderData(var); + mpSampleGenerator->bindShaderData(var); } mRaytrace.pProgram->addDefines(getShaderDefines(renderData)); ShaderVar var = mRaytrace.pVars->getRootVar(); - setShaderData(var, renderData); + bindShaderData(var, renderData); // Dispatch the rays. mpScene->raytrace(pRenderContext, mRaytrace.pProgram.get(), mRaytrace.pVars, uint3(mFrameDim, 1)); @@ -273,9 +293,9 @@ void GBufferRT::executeCompute(RenderContext* pRenderContext, const RenderData& // Create compute pass. if (!mpComputePass) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderModules(mpScene->getShaderModules()); - desc.addShaderLibrary(kProgramComputeFile).csEntry("main").setShaderModel("6_5"); + desc.addShaderLibrary(kProgramComputeFile).csEntry("main"); desc.addTypeConformances(mpScene->getTypeConformances()); DefineList defines; @@ -283,18 +303,18 @@ void GBufferRT::executeCompute(RenderContext* pRenderContext, const RenderData& defines.add(mpSampleGenerator->getDefines()); defines.add(getShaderDefines(renderData)); - mpComputePass = ComputePass::create(mpDevice, desc, defines, true); + mpComputePass = ComputePass::create(mpDevice, desc, defines, true); // Bind static resources ShaderVar var = mpComputePass->getRootVar(); mpScene->setRaytracingShaderData(pRenderContext, var); - mpSampleGenerator->setShaderData(var); + mpSampleGenerator->bindShaderData(var); } mpComputePass->getProgram()->addDefines(getShaderDefines(renderData)); ShaderVar var = mpComputePass->getRootVar(); - setShaderData(var, renderData); + bindShaderData(var, renderData); mpComputePass->execute(pRenderContext, uint3(mFrameDim, 1)); } @@ -309,8 +329,10 @@ DefineList GBufferRT::getShaderDefines(const RenderData& renderData) const // Setup ray flags. RayFlags rayFlags = RayFlags::None; - if (mForceCullMode && mCullMode == RasterizerState::CullMode::Front) rayFlags = RayFlags::CullFrontFacingTriangles; - else if (mForceCullMode && mCullMode == RasterizerState::CullMode::Back) rayFlags = RayFlags::CullBackFacingTriangles; + if (mForceCullMode && mCullMode == RasterizerState::CullMode::Front) + rayFlags = RayFlags::CullFrontFacingTriangles; + else if (mForceCullMode && mCullMode == RasterizerState::CullMode::Back) + rayFlags = RayFlags::CullBackFacingTriangles; defines.add("RAY_FLAGS", std::to_string((uint32_t)rayFlags)); // For optional I/O resources, set 'is_valid_' defines to inform the program of which ones it can access. @@ -320,7 +342,7 @@ DefineList GBufferRT::getShaderDefines(const RenderData& renderData) const return defines; } -void GBufferRT::setShaderData(const ShaderVar& var, const RenderData& renderData) +void GBufferRT::bindShaderData(const ShaderVar& var, const RenderData& renderData) { FALCOR_ASSERT(mpScene && mpScene->getCamera()); var["gGBufferRT"]["frameDim"] = mFrameDim; @@ -334,6 +356,8 @@ void GBufferRT::setShaderData(const ShaderVar& var, const RenderData& renderData ref pTex = getOutput(renderData, channel.name); var[channel.texname] = pTex; }; - for (const auto& channel : kGBufferChannels) bind(channel); - for (const auto& channel : kGBufferExtraChannels) bind(channel); + for (const auto& channel : kGBufferChannels) + bind(channel); + for (const auto& channel : kGBufferExtraChannels) + bind(channel); } diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cs.slang b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cs.slang index 3f7ab0029..098ebf41e 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cs.slang +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,10 +31,11 @@ import GBufferRT; ConstantBuffer gGBufferRT; [numthreads(16, 16, 1)] -void main(uint3 dispatchThreadId : SV_DispatchThreadID) +void main(uint3 dispatchThreadId: SV_DispatchThreadID) { uint2 pixel = dispatchThreadId.xy; - if (any(pixel >= gGBufferRT.frameDim)) return; + if (any(pixel >= gGBufferRT.frameDim)) + return; GpuTimer timer; gGBufferRT.beginTime(timer); diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.h b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.h index 335e02b56..7c99de9d1 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.h +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.h @@ -32,9 +32,10 @@ using namespace Falcor; -/** Ray traced G-buffer pass. - This pass renders a fixed set of G-buffer channels using ray tracing. -*/ +/** + * Ray traced G-buffer pass. + * This pass renders a fixed set of G-buffer channels using ray tracing. + */ class GBufferRT : public GBuffer { public: @@ -57,22 +58,26 @@ class GBufferRT : public GBuffer void executeCompute(RenderContext* pRenderContext, const RenderData& renderData); DefineList getShaderDefines(const RenderData& renderData) const; - void setShaderData(const ShaderVar& var, const RenderData& renderData); + void bindShaderData(const ShaderVar& var, const RenderData& renderData); void recreatePrograms(); // Internal state - bool mComputeDOF = false; ///< Flag indicating if depth-of-field is computed for the current frame. + + /// Flag indicating if depth-of-field is computed for the current frame. + bool mComputeDOF = false; ref mpSampleGenerator; // UI variables + TexLODMode mLODMode = TexLODMode::Mip0; bool mUseTraceRayInline = false; - bool mUseDOF = true; ///< Option for enabling depth-of-field when camera's aperture radius is nonzero. + /// Option for enabling depth-of-field when camera's aperture radius is nonzero. + bool mUseDOF = true; // Ray tracing resources struct { - ref pProgram; + ref pProgram; ref pVars; } mRaytrace; diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.rt.slang b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.rt.slang index 48cd2609b..e990a82a2 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.rt.slang +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.rt.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -35,7 +35,8 @@ ConstantBuffer gGBufferRT; struct RayData { - int dummy; // TODO: Passing in an empty payload struct doesn't work. Declare a dummy variable so that the compiler doesn't remove the declaration. + // TODO: Passing in an empty payload struct doesn't work. Declare a dummy variable so that the compiler doesn't remove the declaration. + int dummy; }; // @@ -63,7 +64,8 @@ void anyHit(inout RayData rayData, BuiltInTriangleIntersectionAttributes attribs GeometryInstanceID instanceID = getGeometryInstanceID(); VertexData v = getVertexData(instanceID, PrimitiveIndex(), attribs); const uint materialID = gScene.getMaterialID(instanceID); - if (gScene.materials.alphaTest(v, materialID, 0.f)) IgnoreHit(); + if (gScene.materials.alphaTest(v, materialID, 0.f)) + IgnoreHit(); } } @@ -184,7 +186,16 @@ void rayGen() // Trace ray. const Ray ray = gGBufferRT.generateRay(launchIndex); RayData rayData; - TraceRay(gScene.rtAccel, GBufferRT::kRayFlags, 0xff /* instanceInclusionMask */, 0 /* hitIdx */, rayTypeCount, 0 /* missIdx */, ray.toRayDesc(), rayData); + TraceRay( + gScene.rtAccel, + GBufferRT::kRayFlags, + 0xff /* instanceInclusionMask */, + 0 /* hitIdx */, + rayTypeCount, + 0 /* missIdx */, + ray.toRayDesc(), + rayData + ); gGBufferRT.writeAux(launchIndex, ray); diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.slang b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.slang index d0e431a2b..eece7c03d 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.slang +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRT.slang @@ -43,11 +43,11 @@ RWTexture2D gFaceNormalW; RWTexture2D gTexC; RWTexture2D gTexGrads; RWTexture2D gMotionVector; -RWTexture2D gMaterialData; +RWTexture2D gMaterialData; // GBufferRT channels RWTexture2D gVBuffer; -RWTexture2D gDepth; +RWTexture2D gDepth; RWTexture2D gLinearZ; RWTexture2D gMotionVectorW; RWTexture2D gNormalWRoughnessMaterialID; @@ -56,9 +56,9 @@ RWTexture2D gDiffOpacity; RWTexture2D gSpecRough; RWTexture2D gEmissive; RWTexture2D gViewW; -RWTexture2D gTime; -RWTexture2D gDisocclusion; -RWTexture2D gMask; +RWTexture2D gTime; +RWTexture2D gDisocclusion; +RWTexture2D gMask; #define is_valid(name) (is_valid_##name != 0) @@ -80,9 +80,18 @@ struct GBufferRT uint frameCount; float screenSpacePixelSpreadAngle; - /** Ray differentials for primary hit. Code from RayTracingGems, Chapter 20. - */ - void computeRayDifferentials(const TriangleHit hit, float3 rayDir, float hitT, const Camera camera, float2 invFrameDim, out float2 ddx, out float2 ddy) + /** + * Ray differentials for primary hit. Code from RayTracingGems, Chapter 20. + */ + void computeRayDifferentials( + const TriangleHit hit, + float3 rayDir, + float hitT, + const Camera camera, + float2 invFrameDim, + out float2 ddx, + out float2 ddy + ) { // TODO: Is this code correct for instance transforms that flip the handedness of the coordinate system? @@ -97,9 +106,12 @@ struct GBufferRT float3 cu = cross(e2, d); float3 cv = cross(d, e1); // Assumes a normalized ray direction - float3 dx = camera.data.cameraU * 2.f * invFrameDim.x / camera.data.focalDistance; // dDdx in ray gen - float3 dy = camera.data.cameraV * 2.f * invFrameDim.y / camera.data.focalDistance; // dDdy in ray gen - float3 q = dx * hitT; // Transfer to primary hit + // dDdx in ray gen + float3 dx = camera.data.cameraU * 2.f * invFrameDim.x / camera.data.focalDistance; + // dDdy in ray gen + float3 dy = camera.data.cameraV * 2.f * invFrameDim.y / camera.data.focalDistance; + // Transfer to primary hit + float3 q = dx * hitT; float3 r = dy * hitT; float dudx = k * dot(cu, q); float dudy = k * dot(cu, r); @@ -117,14 +129,24 @@ struct GBufferRT ddy = float2(dsdy, dtdy); } - void computeAnisotropicAxesRayCones(const TriangleHit hit, VertexData v, float3 rayDir, float hitT, float pixelAngle, out float2 ddx, out float2 ddy) + void computeAnisotropicAxesRayCones( + const TriangleHit hit, + VertexData v, + float3 rayDir, + float hitT, + float pixelAngle, + out float2 ddx, + out float2 ddy + ) { float3 positions[3]; float2 texCoords[3]; gScene.getVertexPositionsW(hit.instanceID, hit.primitiveIndex, positions); gScene.getVertexTexCoords(hit.instanceID, hit.primitiveIndex, texCoords); - float coneWidthAtHitPoint = hitT * tan(pixelAngle); // The exact expression is 2.0f * hitT * tan(pixelAngle * 0.5f), but can be approximated as hitT * tan(pixelAngle) due to tan(b) ~= b for small b. + // The exact expression is 2.0f * hitT * tan(pixelAngle * 0.5f), + // but can be approximated as hitT * tan(pixelAngle) due to tan(b) ~= b for small b. + float coneWidthAtHitPoint = hitT * tan(pixelAngle); // Using faceNormal, since it is needed for the barycentric computations inside computeAnisotropicEllipseAxes(). computeAnisotropicEllipseAxes(v.posW, v.faceNormalW, rayDir, coneWidthAtHitPoint, positions, texCoords, v.texC, ddx, ddy); } @@ -248,7 +270,8 @@ struct GBufferRT float2 pixelPos = pixel + float2(0.5f, 0.5f); float4 prevPosH = mul(gScene.camera.data.prevViewProjMatNoJitter, float4(prevPosW, 1.f)); - motionVector = calcMotionVector(pixelPos, prevPosH, frameDim) + float2(gScene.camera.data.jitterX, -gScene.camera.data.jitterY); // Remove camera jitter from motion vector + // Remove camera jitter from motion vector + motionVector = calcMotionVector(pixelPos, prevPosH, frameDim) + float2(gScene.camera.data.jitterX, -gScene.camera.data.jitterY); motionVectorW = float4(prevPosW - sd.posW, 0.f); // Compute disocclusion. @@ -264,23 +287,38 @@ struct GBufferRT const GBufferData gbuf = prepareGBufferData(sd, v, mi, bsdfProperties); // GBuffer channels - if (is_valid(gPosW)) gPosW[pixel] = gbuf.posW; - if (is_valid(gNormW)) gNormW[pixel] = gbuf.normW; - if (is_valid(gTangentW)) gTangentW[pixel] = gbuf.tangentW; - if (is_valid(gFaceNormalW)) gFaceNormalW[pixel] = gbuf.faceNormalW; - if (is_valid(gTexC)) gTexC[pixel] = gbuf.texC; - if (is_valid(gTexGrads)) gTexGrads[pixel] = texGrads; - if (is_valid(gMotionVector)) gMotionVector[pixel] = motionVector; - if (is_valid(gMaterialData)) gMaterialData[pixel] = gbuf.mtlData; + if (is_valid(gPosW)) + gPosW[pixel] = gbuf.posW; + if (is_valid(gNormW)) + gNormW[pixel] = gbuf.normW; + if (is_valid(gTangentW)) + gTangentW[pixel] = gbuf.tangentW; + if (is_valid(gFaceNormalW)) + gFaceNormalW[pixel] = gbuf.faceNormalW; + if (is_valid(gTexC)) + gTexC[pixel] = gbuf.texC; + if (is_valid(gTexGrads)) + gTexGrads[pixel] = texGrads; + if (is_valid(gMotionVector)) + gMotionVector[pixel] = motionVector; + if (is_valid(gMaterialData)) + gMaterialData[pixel] = gbuf.mtlData; // GBufferRT channels - if (is_valid(gMotionVectorW)) gMotionVectorW[pixel] = motionVectorW; - if (is_valid(gGuideNormalW)) gGuideNormalW[pixel] = gbuf.guideNormalW; - if (is_valid(gDiffOpacity)) gDiffOpacity[pixel] = gbuf.diffuseOpacity; - if (is_valid(gSpecRough)) gSpecRough[pixel] = gbuf.specRough; - if (is_valid(gEmissive)) gEmissive[pixel] = gbuf.emissive; - if (is_valid(gDisocclusion)) gDisocclusion[pixel] = disocclusion; - if (is_valid(gMask)) gMask[pixel] = 1.0f; + if (is_valid(gMotionVectorW)) + gMotionVectorW[pixel] = motionVectorW; + if (is_valid(gGuideNormalW)) + gGuideNormalW[pixel] = gbuf.guideNormalW; + if (is_valid(gDiffOpacity)) + gDiffOpacity[pixel] = gbuf.diffuseOpacity; + if (is_valid(gSpecRough)) + gSpecRough[pixel] = gbuf.specRough; + if (is_valid(gEmissive)) + gEmissive[pixel] = gbuf.emissive; + if (is_valid(gDisocclusion)) + gDisocclusion[pixel] = disocclusion; + if (is_valid(gMask)) + gMask[pixel] = 1.0f; if (is_valid(gLinearZ)) { @@ -309,7 +347,8 @@ struct GBufferRT if (is_valid(gNormalWRoughnessMaterialID)) { float3 normal = bsdfProperties.guideNormal; - if (sd.mtl.getMaterialType() == MaterialType::Hair) normal = sd.frame.T; + if (sd.mtl.getMaterialType() == MaterialType::Hair) + normal = sd.frame.T; float2 octNormal = ndir_to_oct_unorm(normal); float roughness = bsdfProperties.roughness; @@ -331,7 +370,8 @@ struct GBufferRT float2 pixelPos = pixel + float2(0.5f, 0.5f); float4 prevPosH = mul(gScene.camera.data.prevViewProjMatNoJitter, float4(worldPos, 1.f)); - return calcMotionVector(pixelPos, prevPosH, frameDim) + float2(gScene.camera.data.jitterX, -gScene.camera.data.jitterY); // Remove camera jitter from motion vector + // Remove camera jitter from motion vector + return calcMotionVector(pixelPos, prevPosH, frameDim) + float2(gScene.camera.data.jitterX, -gScene.camera.data.jitterY); } void writeMiss(const uint2 pixel, const float3 rayOrigin, const float3 rayDir) @@ -342,42 +382,64 @@ struct GBufferRT const float materialID = 0.f; // GBuffer channels - if (is_valid(gPosW)) gPosW[pixel] = {}; - if (is_valid(gNormW)) gNormW[pixel] = {}; - if (is_valid(gTangentW)) gTangentW[pixel] = {}; - if (is_valid(gFaceNormalW)) gFaceNormalW[pixel] = {}; - if (is_valid(gTexC)) gTexC[pixel] = {}; - if (is_valid(gTexGrads)) gTexGrads[pixel] = {}; - if (is_valid(gMotionVector)) gMotionVector[pixel] = mvec; - if (is_valid(gMaterialData)) gMaterialData[pixel] = {}; + if (is_valid(gPosW)) + gPosW[pixel] = {}; + if (is_valid(gNormW)) + gNormW[pixel] = {}; + if (is_valid(gTangentW)) + gTangentW[pixel] = {}; + if (is_valid(gFaceNormalW)) + gFaceNormalW[pixel] = {}; + if (is_valid(gTexC)) + gTexC[pixel] = {}; + if (is_valid(gTexGrads)) + gTexGrads[pixel] = {}; + if (is_valid(gMotionVector)) + gMotionVector[pixel] = mvec; + if (is_valid(gMaterialData)) + gMaterialData[pixel] = {}; // GBufferRT channels - if (is_valid(gVBuffer)) gVBuffer[pixel] = {}; - if (is_valid(gDepth)) gDepth[pixel] = 1.f; - if (is_valid(gLinearZ)) gLinearZ[pixel] = float2(kEnvMapDepth, 0.f); - if (is_valid(gMotionVectorW)) gMotionVectorW[pixel] = {}; - if (is_valid(gNormalWRoughnessMaterialID)) gNormalWRoughnessMaterialID[pixel] = float4(octNormal, roughness, materialID); - if (is_valid(gGuideNormalW)) gGuideNormalW[pixel] = {}; - if (is_valid(gDiffOpacity)) gDiffOpacity[pixel] = {}; - if (is_valid(gSpecRough)) gSpecRough[pixel] = {}; - if (is_valid(gEmissive)) gEmissive[pixel] = {}; - if (is_valid(gDisocclusion)) gDisocclusion[pixel] = 0.f; - if (is_valid(gMask)) gMask[pixel] = 0.0f; + if (is_valid(gVBuffer)) + gVBuffer[pixel] = {}; + if (is_valid(gDepth)) + gDepth[pixel] = 1.f; + if (is_valid(gLinearZ)) + gLinearZ[pixel] = float2(kEnvMapDepth, 0.f); + if (is_valid(gMotionVectorW)) + gMotionVectorW[pixel] = {}; + if (is_valid(gNormalWRoughnessMaterialID)) + gNormalWRoughnessMaterialID[pixel] = float4(octNormal, roughness, materialID); + if (is_valid(gGuideNormalW)) + gGuideNormalW[pixel] = {}; + if (is_valid(gDiffOpacity)) + gDiffOpacity[pixel] = {}; + if (is_valid(gSpecRough)) + gSpecRough[pixel] = {}; + if (is_valid(gEmissive)) + gEmissive[pixel] = {}; + if (is_valid(gDisocclusion)) + gDisocclusion[pixel] = 0.f; + if (is_valid(gMask)) + gMask[pixel] = 0.0f; } void writeAux(uint2 pixel, const Ray ray) { // Write view direction. - if (is_valid(gViewW)) gViewW[pixel] = float4(-ray.dir, 0.f); + if (is_valid(gViewW)) + gViewW[pixel] = float4(-ray.dir, 0.f); } void beginTime(inout GpuTimer timer) { - if (is_valid(gTime)) timer.start(); + if (is_valid(gTime)) + timer.start(); } void endTime(uint2 pixel, inout GpuTimer timer) { - if (is_valid(gTime)) gTime[pixel] = timer.getElapsed(); + if (is_valid(gTime)) + gTime[pixel] = timer.getElapsed(); } }; diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.3d.slang b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.3d.slang index c40d01863..0bb338090 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.3d.slang +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.3d.slang @@ -40,14 +40,14 @@ cbuffer PerFrameCB // GBuffer channels struct GBufferPSOut { - float4 posW : SV_TARGET0; - float4 normW : SV_TARGET1; - float4 tangentW : SV_TARGET2; - float4 faceNormalW : SV_TARGET3; - float2 texC : SV_TARGET4; - float4 texGrads : SV_TARGET5; - float2 mvec : SV_TARGET6; - uint4 mtlData : SV_TARGET7; + float4 posW : SV_TARGET0; + float4 normW : SV_TARGET1; + float4 tangentW : SV_TARGET2; + float4 faceNormalW : SV_TARGET3; + float2 texC : SV_TARGET4; + float4 texGrads : SV_TARGET5; + float2 mvec : SV_TARGET6; + uint4 mtlData : SV_TARGET7; }; // GBufferRaster channels @@ -59,7 +59,7 @@ RasterizerOrderedTexture2D gEmissive; RasterizerOrderedTexture2D gViewW; RasterizerOrderedTexture2D gPosNormalFwidth; RasterizerOrderedTexture2D gLinearZAndDeriv; -RasterizerOrderedTexture2D gMask; +RasterizerOrderedTexture2D gMask; #define is_valid(name) (is_valid_##name != 0) @@ -70,13 +70,16 @@ VSOut vsMain(VSIn vsIn) float2 computeMotionVector(const VSOut vsOut, const int2 ipos) { - float2 pixelPos = ipos + float2(0.5, 0.5); // Current sample in pixel coords. - float4 prevPosH = vsOut.prevPosH; // Sample in previous frame in clip space coords, no jittering applied. - return calcMotionVector(pixelPos, prevPosH, gFrameDim) + float2(gScene.camera.data.jitterX, -gScene.camera.data.jitterY); // Remove camera jitter from motion vector + // Current sample in pixel coords. + float2 pixelPos = ipos + float2(0.5, 0.5); + // Sample in previous frame in clip space coords, no jittering applied. + float4 prevPosH = vsOut.prevPosH; + // Remove camera jitter from motion vector + return calcMotionVector(pixelPos, prevPosH, gFrameDim) + float2(gScene.camera.data.jitterX, -gScene.camera.data.jitterY); } [earlydepthstencil] -GBufferPSOut psMain(VSOut vsOut, uint triangleIndex : SV_PrimitiveID, float3 barycentrics : SV_Barycentrics) +GBufferPSOut psMain(VSOut vsOut, uint triangleIndex: SV_PrimitiveID, float3 barycentrics: SV_Barycentrics) { // Using vOut.posH.xy as pixel coordinate since it has the SV_Position semantic. int2 ipos = int2(vsOut.posH.xy); @@ -86,7 +89,8 @@ GBufferPSOut psMain(VSOut vsOut, uint triangleIndex : SV_PrimitiveID, float3 bar let lod = ImplicitLodTextureSampler(); #if USE_ALPHA_TEST - if (gScene.materials.alphaTest(v, vsOut.materialID, lod)) discard; + if (gScene.materials.alphaTest(v, vsOut.materialID, lod)) + discard; #endif const float3 viewDir = normalize(gScene.camera.getPosition() - v.posW); ShadingData sd = gScene.materials.prepareShadingData(v, vsOut.materialID, viewDir, lod); @@ -108,22 +112,36 @@ GBufferPSOut psMain(VSOut vsOut, uint triangleIndex : SV_PrimitiveID, float3 bar GBufferPSOut psOut = {}; // Store render target outputs. - if (is_valid(gPosW)) psOut.posW = gbuf.posW; - if (is_valid(gNormW)) psOut.normW = gbuf.normW; - if (is_valid(gTangentW)) psOut.tangentW = gbuf.tangentW; - if (is_valid(gFaceNormalW)) psOut.faceNormalW = gbuf.faceNormalW; - if (is_valid(gTexC)) psOut.texC = gbuf.texC; - if (is_valid(gTexGrads)) psOut.texGrads = texGrads; - if (is_valid(gMotionVector)) psOut.mvec = mvec; - if (is_valid(gMaterialData)) psOut.mtlData = gbuf.mtlData; + if (is_valid(gPosW)) + psOut.posW = gbuf.posW; + if (is_valid(gNormW)) + psOut.normW = gbuf.normW; + if (is_valid(gTangentW)) + psOut.tangentW = gbuf.tangentW; + if (is_valid(gFaceNormalW)) + psOut.faceNormalW = gbuf.faceNormalW; + if (is_valid(gTexC)) + psOut.texC = gbuf.texC; + if (is_valid(gTexGrads)) + psOut.texGrads = texGrads; + if (is_valid(gMotionVector)) + psOut.mvec = mvec; + if (is_valid(gMaterialData)) + psOut.mtlData = gbuf.mtlData; // Store UAV outputs. - if (is_valid(gViewW)) gViewW[ipos] = float4(sd.V, 0.f); - if (is_valid(gGuideNormalW)) gGuideNormalW[ipos] = gbuf.guideNormalW; - if (is_valid(gDiffOpacity)) gDiffOpacity[ipos] = gbuf.diffuseOpacity; - if (is_valid(gSpecRough)) gSpecRough[ipos] = gbuf.specRough; - if (is_valid(gEmissive)) gEmissive[ipos] = gbuf.emissive; - if (is_valid(gMask)) gMask[ipos] = 1.f; + if (is_valid(gViewW)) + gViewW[ipos] = float4(sd.V, 0.f); + if (is_valid(gGuideNormalW)) + gGuideNormalW[ipos] = gbuf.guideNormalW; + if (is_valid(gDiffOpacity)) + gDiffOpacity[ipos] = gbuf.diffuseOpacity; + if (is_valid(gSpecRough)) + gSpecRough[ipos] = gbuf.specRough; + if (is_valid(gEmissive)) + gEmissive[ipos] = gbuf.emissive; + if (is_valid(gMask)) + gMask[ipos] = 1.f; // Length of derivatives of position and guide normal. // This is a feature guide for the SVGF denoiser pass. diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.cpp b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.cpp index 42e59b9a1..ef021ab83 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.cpp +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.cpp @@ -31,42 +31,39 @@ namespace { - const std::string kDepthPassProgramFile = "RenderPasses/GBuffer/GBuffer/DepthPass.3d.slang"; - const std::string kGBufferPassProgramFile = "RenderPasses/GBuffer/GBuffer/GBufferRaster.3d.slang"; - const std::string shaderModel = "6_2"; - const RasterizerState::CullMode kDefaultCullMode = RasterizerState::CullMode::Back; - - // Additional output channels. - // TODO: Some are RG32 floats now. I'm sure that all of these could be fp16. - const std::string kVBufferName = "vbuffer"; - const ChannelList kGBufferExtraChannels = - { - { kVBufferName, "gVBuffer", "Visibility buffer", true /* optional */, ResourceFormat::Unknown /* set at runtime */ }, - { "guideNormalW", "gGuideNormalW", "Guide normal in world space", true /* optional */, ResourceFormat::RGBA32Float }, - { "diffuseOpacity", "gDiffOpacity", "Diffuse reflection albedo and opacity", true /* optional */, ResourceFormat::RGBA32Float }, - { "specRough", "gSpecRough", "Specular reflectance and roughness", true /* optional */, ResourceFormat::RGBA32Float }, - { "emissive", "gEmissive", "Emissive color", true /* optional */, ResourceFormat::RGBA32Float }, - { "viewW", "gViewW", "View direction in world space", true /* optional */, ResourceFormat::RGBA32Float }, // TODO: Switch to packed 2x16-bit snorm format. - { "pnFwidth", "gPosNormalFwidth", "Position and guide normal filter width", true /* optional */, ResourceFormat::RG32Float }, - { "linearZ", "gLinearZAndDeriv", "Linear z (and derivative)", true /* optional */, ResourceFormat::RG32Float }, - { "mask", "gMask", "Mask", true /* optional */, ResourceFormat::R32Float }, - }; - - const std::string kDepthName = "depth"; -} - -GBufferRaster::GBufferRaster(ref pDevice, const Properties& props) - : GBuffer(pDevice) +const std::string kDepthPassProgramFile = "RenderPasses/GBuffer/GBuffer/DepthPass.3d.slang"; +const std::string kGBufferPassProgramFile = "RenderPasses/GBuffer/GBuffer/GBufferRaster.3d.slang"; +const RasterizerState::CullMode kDefaultCullMode = RasterizerState::CullMode::Back; + +// Additional output channels. +// TODO: Some are RG32 floats now. I'm sure that all of these could be fp16. +const std::string kVBufferName = "vbuffer"; +const ChannelList kGBufferExtraChannels = { + // clang-format off + { kVBufferName, "gVBuffer", "Visibility buffer", true /* optional */, ResourceFormat::Unknown /* set at runtime */ }, + { "guideNormalW", "gGuideNormalW", "Guide normal in world space", true /* optional */, ResourceFormat::RGBA32Float }, + { "diffuseOpacity", "gDiffOpacity", "Diffuse reflection albedo and opacity", true /* optional */, ResourceFormat::RGBA32Float }, + { "specRough", "gSpecRough", "Specular reflectance and roughness", true /* optional */, ResourceFormat::RGBA32Float }, + { "emissive", "gEmissive", "Emissive color", true /* optional */, ResourceFormat::RGBA32Float }, + { "viewW", "gViewW", "View direction in world space", true /* optional */, ResourceFormat::RGBA32Float }, // TODO: Switch to packed 2x16-bit snorm format. + { "pnFwidth", "gPosNormalFwidth", "Position and guide normal filter width", true /* optional */, ResourceFormat::RG32Float }, + { "linearZ", "gLinearZAndDeriv", "Linear z (and derivative)", true /* optional */, ResourceFormat::RG32Float }, + { "mask", "gMask", "Mask", true /* optional */, ResourceFormat::R32Float }, + // clang-format on +}; + +const std::string kDepthName = "depth"; +} // namespace + +GBufferRaster::GBufferRaster(ref pDevice, const Properties& props) : GBuffer(pDevice) { // Check for required features. + if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_2)) + FALCOR_THROW("GBufferRaster requires Shader Model 6.2 support."); if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::Barycentrics)) - { - throw RuntimeError("GBufferRaster: Pixel shader barycentrics are not supported by the current device"); - } + FALCOR_THROW("GBufferRaster requires pixel shader barycentrics support."); if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RasterizerOrderedViews)) - { - throw RuntimeError("GBufferRaster: Rasterizer ordered views (ROVs) are not supported by the current device"); - } + FALCOR_THROW("GBufferRaster requires rasterizer ordered views (ROVs) support."); parseProperties(props); @@ -76,7 +73,7 @@ GBufferRaster::GBufferRaster(ref pDevice, const Properties& props) // Set depth function DepthStencilState::Desc dsDesc; - dsDesc.setDepthFunc(DepthStencilState::Func::Equal).setDepthWriteMask(false); + dsDesc.setDepthFunc(ComparisonFunc::Equal).setDepthWriteMask(false); ref pDsState = DepthStencilState::create(dsDesc); mGBufferPass.pState->setDepthStencilState(pDsState); @@ -89,12 +86,15 @@ RenderPassReflection GBufferRaster::reflect(const CompileData& compileData) const uint2 sz = RenderPassHelpers::calculateIOSize(mOutputSizeSelection, mFixedOutputSize, compileData.defaultTexDims); // Add the required depth output. This always exists. - reflector.addOutput(kDepthName, "Depth buffer").format(ResourceFormat::D32Float).bindFlags(Resource::BindFlags::DepthStencil).texture2D(sz.x, sz.y); + reflector.addOutput(kDepthName, "Depth buffer") + .format(ResourceFormat::D32Float) + .bindFlags(ResourceBindFlags::DepthStencil) + .texture2D(sz.x, sz.y); // Add all the other outputs. // The default channels are written as render targets, the rest as UAVs as there is way to assign/pack render targets yet. - addRenderPassOutputs(reflector, kGBufferChannels, Resource::BindFlags::RenderTarget, sz); - addRenderPassOutputs(reflector, kGBufferExtraChannels, Resource::BindFlags::UnorderedAccess, sz); + addRenderPassOutputs(reflector, kGBufferChannels, ResourceBindFlags::RenderTarget, sz); + addRenderPassOutputs(reflector, kGBufferExtraChannels, ResourceBindFlags::UnorderedAccess, sz); reflector.getField(kVBufferName)->format(mVBufferFormat); return reflector; @@ -109,49 +109,27 @@ void GBufferRaster::setScene(RenderContext* pRenderContext, const ref& pS { GBuffer::setScene(pRenderContext, pScene); - mDepthPass.pProgram = nullptr; - mDepthPass.pVars = nullptr; - - mGBufferPass.pProgram = nullptr; - mGBufferPass.pVars = nullptr; + recreatePrograms(); if (pScene) { if (pScene->getMeshVao() && pScene->getMeshVao()->getPrimitiveTopology() != Vao::Topology::TriangleList) { - throw RuntimeError("GBufferRaster: Requires triangle list geometry due to usage of SV_Barycentrics."); - } - - // Create depth pass program. - { - Program::Desc desc; - desc.addShaderModules(pScene->getShaderModules()); - desc.addShaderLibrary(kDepthPassProgramFile).vsEntry("vsMain").psEntry("psMain"); - desc.addTypeConformances(pScene->getTypeConformances()); - desc.setShaderModel(shaderModel); - - mDepthPass.pProgram = GraphicsProgram::create(mpDevice, desc, pScene->getSceneDefines()); - mDepthPass.pState->setProgram(mDepthPass.pProgram); - } - - // Create GBuffer pass program. - { - Program::Desc desc; - desc.addShaderModules(pScene->getShaderModules()); - desc.addShaderLibrary(kGBufferPassProgramFile).vsEntry("vsMain").psEntry("psMain"); - desc.addTypeConformances(pScene->getTypeConformances()); - desc.setShaderModel(shaderModel); - - mGBufferPass.pProgram = GraphicsProgram::create(mpDevice, desc, pScene->getSceneDefines()); - mGBufferPass.pState->setProgram(mGBufferPass.pProgram); + FALCOR_THROW("GBufferRaster: Requires triangle list geometry due to usage of SV_Barycentrics."); } } } -void GBufferRaster::onSceneUpdates(RenderContext* pRenderContext, Scene::UpdateFlags sceneUpdates) +void GBufferRaster::recreatePrograms() { + mDepthPass.pProgram = nullptr; + mDepthPass.pVars = nullptr; + mGBufferPass.pProgram = nullptr; + mGBufferPass.pVars = nullptr; } +void GBufferRaster::onSceneUpdates(RenderContext* pRenderContext, Scene::UpdateFlags sceneUpdates) {} + void GBufferRaster::execute(RenderContext* pRenderContext, const RenderData& renderData) { GBuffer::execute(pRenderContext, renderData); @@ -181,16 +159,34 @@ void GBufferRaster::execute(RenderContext* pRenderContext, const RenderData& ren return; } - RasterizerState::CullMode cullMode = mForceCullMode ? mCullMode : kDefaultCullMode; + const RasterizerState::CullMode cullMode = mForceCullMode ? mCullMode : kDefaultCullMode; + + // Check for scene changes. + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded)) + { + recreatePrograms(); + } // Depth pass. { + // Create depth pass program. + if (!mDepthPass.pProgram) + { + ProgramDesc desc; + desc.addShaderModules(mpScene->getShaderModules()); + desc.addShaderLibrary(kDepthPassProgramFile).vsEntry("vsMain").psEntry("psMain"); + desc.addTypeConformances(mpScene->getTypeConformances()); + + mDepthPass.pProgram = Program::create(mpDevice, desc, mpScene->getSceneDefines()); + mDepthPass.pState->setProgram(mDepthPass.pProgram); + } + // Set program defines. mDepthPass.pState->getProgram()->addDefine("USE_ALPHA_TEST", mUseAlphaTest ? "1" : "0"); // Create program vars. if (!mDepthPass.pVars) - mDepthPass.pVars = GraphicsVars::create(mpDevice, mDepthPass.pProgram.get()); + mDepthPass.pVars = ProgramVars::create(mpDevice, mDepthPass.pProgram.get()); mpFbo->attachDepthStencilTarget(pDepth); mDepthPass.pState->setFbo(mpFbo); @@ -200,6 +196,18 @@ void GBufferRaster::execute(RenderContext* pRenderContext, const RenderData& ren // GBuffer pass. { + // Create GBuffer pass program. + if (!mGBufferPass.pProgram) + { + ProgramDesc desc; + desc.addShaderModules(mpScene->getShaderModules()); + desc.addShaderLibrary(kGBufferPassProgramFile).vsEntry("vsMain").psEntry("psMain"); + desc.addTypeConformances(mpScene->getTypeConformances()); + + mGBufferPass.pProgram = Program::create(mpDevice, desc, mpScene->getSceneDefines()); + mGBufferPass.pState->setProgram(mGBufferPass.pProgram); + } + // Set program defines. mGBufferPass.pProgram->addDefine("ADJUST_SHADING_NORMALS", mAdjustShadingNormals ? "1" : "0"); mGBufferPass.pProgram->addDefine("USE_ALPHA_TEST", mUseAlphaTest ? "1" : "0"); @@ -211,7 +219,7 @@ void GBufferRaster::execute(RenderContext* pRenderContext, const RenderData& ren // Create program vars. if (!mGBufferPass.pVars) - mGBufferPass.pVars = GraphicsVars::create(mpDevice, mGBufferPass.pProgram.get()); + mGBufferPass.pVars = ProgramVars::create(mpDevice, mGBufferPass.pProgram.get()); auto var = mGBufferPass.pVars->getRootVar(); diff --git a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.h b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.h index 22400157d..69539d9a4 100644 --- a/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.h +++ b/Source/RenderPasses/GBuffer/GBuffer/GBufferRaster.h @@ -32,9 +32,10 @@ using namespace Falcor; -/** Raster G-buffer pass. - This pass renders a fixed set of G-buffer channels using rasterization. -*/ +/** + * Raster G-buffer pass. + * This pass renders a fixed set of G-buffer channels using rasterization. + */ class GBufferRaster : public GBuffer { public: @@ -51,21 +52,23 @@ class GBufferRaster : public GBuffer virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override; private: + void recreatePrograms(); + // Internal state ref mpFbo; struct { ref pState; - ref pProgram; - ref pVars; + ref pProgram; + ref pVars; } mDepthPass; // Rasterization resources struct { ref pState; - ref pProgram; - ref pVars; + ref pProgram; + ref pVars; } mGBufferPass; }; diff --git a/Source/RenderPasses/GBuffer/GBufferBase.cpp b/Source/RenderPasses/GBuffer/GBufferBase.cpp index 439c205e8..4c39dee3c 100644 --- a/Source/RenderPasses/GBuffer/GBufferBase.cpp +++ b/Source/RenderPasses/GBuffer/GBufferBase.cpp @@ -45,42 +45,52 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr namespace { - // Scripting options. - const char kOutputSize[] = "outputSize"; - const char kFixedOutputSize[] = "fixedOutputSize"; - const char kSamplePattern[] = "samplePattern"; - const char kSampleCount[] = "sampleCount"; - const char kUseAlphaTest[] = "useAlphaTest"; - const char kDisableAlphaTest[] = "disableAlphaTest"; ///< Deprecated for "useAlphaTest". - const char kAdjustShadingNormals[] = "adjustShadingNormals"; - const char kForceCullMode[] = "forceCullMode"; - const char kCullMode[] = "cull"; -} +// Scripting options. +const char kOutputSize[] = "outputSize"; +const char kFixedOutputSize[] = "fixedOutputSize"; +const char kSamplePattern[] = "samplePattern"; +const char kSampleCount[] = "sampleCount"; +const char kUseAlphaTest[] = "useAlphaTest"; +const char kDisableAlphaTest[] = "disableAlphaTest"; ///< Deprecated for "useAlphaTest". +const char kAdjustShadingNormals[] = "adjustShadingNormals"; +const char kForceCullMode[] = "forceCullMode"; +const char kCullMode[] = "cull"; +} // namespace void GBufferBase::parseProperties(const Properties& props) { for (const auto& [key, value] : props) { - if (key == kOutputSize) mOutputSizeSelection = value; - else if (key == kFixedOutputSize) mFixedOutputSize = value; - else if (key == kSamplePattern) mSamplePattern = value; - else if (key == kSampleCount) mSampleCount = value; - else if (key == kUseAlphaTest) mUseAlphaTest = value; - else if (key == kAdjustShadingNormals) mAdjustShadingNormals = value; - else if (key == kForceCullMode) mForceCullMode = value; - else if (key == kCullMode) mCullMode = value; + if (key == kOutputSize) + mOutputSizeSelection = value; + else if (key == kFixedOutputSize) + mFixedOutputSize = value; + else if (key == kSamplePattern) + mSamplePattern = value; + else if (key == kSampleCount) + mSampleCount = value; + else if (key == kUseAlphaTest) + mUseAlphaTest = value; + else if (key == kAdjustShadingNormals) + mAdjustShadingNormals = value; + else if (key == kForceCullMode) + mForceCullMode = value; + else if (key == kCullMode) + mCullMode = value; // TODO: Check for unparsed fields, including those parsed in derived classes. } // Handle deprecated "disableAlphaTest" value. - if (props.has(kDisableAlphaTest) && !props.has(kUseAlphaTest)) mUseAlphaTest = !props[kDisableAlphaTest]; + if (props.has(kDisableAlphaTest) && !props.has(kUseAlphaTest)) + mUseAlphaTest = !props[kDisableAlphaTest]; } Properties GBufferBase::getProperties() const { Properties props; props[kOutputSize] = mOutputSizeSelection; - if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) props[kFixedOutputSize] = mFixedOutputSize; + if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) + props[kFixedOutputSize] = mFixedOutputSize; props[kSamplePattern] = mSamplePattern; props[kSampleCount] = mSampleCount; props[kUseAlphaTest] = mUseAlphaTest; @@ -94,17 +104,23 @@ void GBufferBase::renderUI(Gui::Widgets& widget) { // Controls for output size. // When output size requirements change, we'll trigger a graph recompile to update the render pass I/O sizes. - if (widget.dropdown("Output size", mOutputSizeSelection)) requestRecompile(); + if (widget.dropdown("Output size", mOutputSizeSelection)) + requestRecompile(); if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) { - if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) requestRecompile(); + if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) + requestRecompile(); } // Sample pattern controls. bool updatePattern = widget.dropdown("Sample pattern", mSamplePattern); - widget.tooltip("Selects sample pattern for anti-aliasing over multiple frames.\n\n" - "The camera jitter is set at the start of each frame based on the chosen pattern. All render passes should see the same jitter.\n" - "'Center' disables anti-aliasing by always sampling at the center of the pixel.", true); + widget.tooltip( + "Selects sample pattern for anti-aliasing over multiple frames.\n\n" + "The camera jitter is set at the start of each frame based on the chosen pattern.\n" + "All render passes should see the same jitter.\n" + "'Center' disables anti-aliasing by always sampling at the center of the pixel.", + true + ); if (mSamplePattern != SamplePattern::Center) { updatePattern |= widget.var("Sample count", mSampleCount, 1u); @@ -117,7 +133,7 @@ void GBufferBase::renderUI(Gui::Widgets& widget) } // Misc controls. - mOptionsChanged |= widget.checkbox("Alpha Test", mUseAlphaTest); + mOptionsChanged |= widget.checkbox("Alpha Test", mUseAlphaTest); widget.tooltip("Use alpha testing on non-opaque triangles."); mOptionsChanged |= widget.checkbox("Adjust shading normals", mAdjustShadingNormals); @@ -125,8 +141,12 @@ void GBufferBase::renderUI(Gui::Widgets& widget) // Cull mode controls. mOptionsChanged |= widget.checkbox("Force cull mode", mForceCullMode); - widget.tooltip("Enable this option to override the default cull mode.\n\n" - "Otherwise the default for rasterization is to cull backfacing geometry, and for ray tracing to disable culling.", true); + widget.tooltip( + "Enable this option to override the default cull mode.\n\n" + "Otherwise the default for rasterization is to cull backfacing geometry, " + "and for ray tracing to disable culling.", + true + ); if (mForceCullMode) { @@ -197,13 +217,15 @@ void GBufferBase::updateFrameDim(const uint2 frameDim) mInvFrameDim = 1.f / float2(frameDim); // Update sample generator for camera jitter. - if (mpScene) mpScene->getCamera()->setPatternGenerator(mpSampleGenerator, mInvFrameDim); + if (mpScene) + mpScene->getCamera()->setPatternGenerator(mpSampleGenerator, mInvFrameDim); } void GBufferBase::updateSamplePattern() { mpSampleGenerator = createSamplePattern(mSamplePattern, mSampleCount); - if (mpSampleGenerator) mSampleCount = mpSampleGenerator->getSampleCount(); + if (mpSampleGenerator) + mSampleCount = mpSampleGenerator->getSampleCount(); } ref GBufferBase::getOutput(const RenderData& renderData, const std::string& name) const @@ -213,7 +235,7 @@ ref GBufferBase::getOutput(const RenderData& renderData, const std::str auto pTex = renderData.getTexture(name); if (pTex && (pTex->getWidth() != mFrameDim.x || pTex->getHeight() != mFrameDim.y)) { - throw RuntimeError("GBufferBase: Pass output '{}' has mismatching size. All outputs must be of the same size.", name); + FALCOR_THROW("GBufferBase: Pass output '{}' has mismatching size. All outputs must be of the same size.", name); } return pTex; } diff --git a/Source/RenderPasses/GBuffer/GBufferBase.h b/Source/RenderPasses/GBuffer/GBufferBase.h index db2f43bfc..c53e920cf 100644 --- a/Source/RenderPasses/GBuffer/GBufferBase.h +++ b/Source/RenderPasses/GBuffer/GBufferBase.h @@ -32,8 +32,9 @@ using namespace Falcor; -/** Base class for the different types of G-buffer passes (including V-buffer). -*/ +/** + * Base class for the different types of G-buffer passes (including V-buffer). + */ class GBufferBase : public RenderPass { public: @@ -45,12 +46,15 @@ class GBufferBase : public RenderPass Stratified, }; - FALCOR_ENUM_INFO(SamplePattern, { - { SamplePattern::Center, "Center" }, - { SamplePattern::DirectX, "DirectX" }, - { SamplePattern::Halton, "Halton" }, - { SamplePattern::Stratified, "Stratified" }, - }); + FALCOR_ENUM_INFO( + SamplePattern, + { + {SamplePattern::Center, "Center"}, + {SamplePattern::DirectX, "DirectX"}, + {SamplePattern::Halton, "Halton"}, + {SamplePattern::Stratified, "Stratified"}, + } + ); virtual void renderUI(Gui::Widgets& widget) override; virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; @@ -66,25 +70,39 @@ class GBufferBase : public RenderPass ref getOutput(const RenderData& renderData, const std::string& name) const; // Internal state - ref mpScene; - ref mpSampleGenerator; ///< Sample generator for camera jitter. - uint32_t mFrameCount = 0; ///< Frames rendered since last change of scene. This is used as random seed. - uint2 mFrameDim = {}; ///< Current frame dimension in pixels. Note this may be different from the window size. - float2 mInvFrameDim = {}; - ResourceFormat mVBufferFormat = HitInfo::kDefaultFormat; + ref mpScene; + /// Sample generator for camera jitter. + ref mpSampleGenerator; + + /// Frames rendered since last change of scene. This is used as random seed. + uint32_t mFrameCount = 0; + /// Current frame dimension in pixels. Note this may be different from the window size. + uint2 mFrameDim = {}; + float2 mInvFrameDim = {}; + ResourceFormat mVBufferFormat = HitInfo::kDefaultFormat; // UI variables - RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. - uint2 mFixedOutputSize = { 512, 512 }; ///< Output size in pixels when 'Fixed' size is selected. - SamplePattern mSamplePattern = SamplePattern::Center; ///< Which camera jitter sample pattern to use. - uint32_t mSampleCount = 16; ///< Sample count for camera jitter. - bool mUseAlphaTest = true; ///< Enable alpha test. - bool mAdjustShadingNormals = true; ///< Adjust shading normals. - bool mForceCullMode = false; ///< Force cull mode for all geometry, otherwise set it based on the scene. - RasterizerState::CullMode mCullMode = RasterizerState::CullMode::Back; ///< Cull mode to use for when mForceCullMode is true. - bool mOptionsChanged = false; ///< Indicates whether any options that affect the output have changed since last frame. + /// Selected output size. + RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; + /// Output size in pixels when 'Fixed' size is selected. + uint2 mFixedOutputSize = {512, 512}; + /// Which camera jitter sample pattern to use. + SamplePattern mSamplePattern = SamplePattern::Center; + /// Sample count for camera jitter. + uint32_t mSampleCount = 16; + /// Enable alpha test. + bool mUseAlphaTest = true; + /// Adjust shading normals. + bool mAdjustShadingNormals = true; + /// Force cull mode for all geometry, otherwise set it based on the scene. + bool mForceCullMode = false; + /// Cull mode to use for when mForceCullMode is true. + RasterizerState::CullMode mCullMode = RasterizerState::CullMode::Back; + + /// Indicates whether any options that affect the output have changed since last frame. + bool mOptionsChanged = false; }; FALCOR_ENUM_REGISTER(GBufferBase::SamplePattern); diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cpp b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cpp index b5dcf1905..d25b3db94 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cpp +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cpp @@ -32,38 +32,39 @@ namespace { - const std::string kProgramRaytraceFile = "RenderPasses/GBuffer/VBuffer/VBufferRT.rt.slang"; - const std::string kProgramComputeFile = "RenderPasses/GBuffer/VBuffer/VBufferRT.cs.slang"; - - // Scripting options. - const char kUseTraceRayInline[] = "useTraceRayInline"; - const char kUseDOF[] = "useDOF"; - - // Ray tracing settings that affect the traversal stack size. Set as small as possible. - const uint32_t kMaxPayloadSizeBytes = 4; // TODO: The shader doesn't need a payload, set this to zero if it's possible to pass a null payload to TraceRay() - const uint32_t kMaxRecursionDepth = 1; - - const std::string kVBufferName = "vbuffer"; - const std::string kVBufferDesc = "V-buffer in packed format (indices + barycentrics)"; - - // Additional output channels. - const ChannelList kVBufferExtraChannels = - { - { "depth", "gDepth", "Depth buffer (NDC)", true /* optional */, ResourceFormat::R32Float }, - { "mvec", "gMotionVector", "Motion vector", true /* optional */, ResourceFormat::RG32Float }, - { "viewW", "gViewW", "View direction in world space", true /* optional */, ResourceFormat::RGBA32Float }, // TODO: Switch to packed 2x16-bit snorm format. - { "time", "gTime", "Per-pixel execution time", true /* optional */, ResourceFormat::R32Uint }, - { "mask", "gMask", "Mask", true /* optional */, ResourceFormat::R32Float }, - }; +const std::string kProgramRaytraceFile = "RenderPasses/GBuffer/VBuffer/VBufferRT.rt.slang"; +const std::string kProgramComputeFile = "RenderPasses/GBuffer/VBuffer/VBufferRT.cs.slang"; + +// Scripting options. +const char kUseTraceRayInline[] = "useTraceRayInline"; +const char kUseDOF[] = "useDOF"; + +// Ray tracing settings that affect the traversal stack size. Set as small as possible. +// TODO: The shader doesn't need a payload, set this to zero if it's possible to pass a null payload to TraceRay() +const uint32_t kMaxPayloadSizeBytes = 4; +const uint32_t kMaxRecursionDepth = 1; + +const std::string kVBufferName = "vbuffer"; +const std::string kVBufferDesc = "V-buffer in packed format (indices + barycentrics)"; + +// Additional output channels. +const ChannelList kVBufferExtraChannels = { + // clang-format off + { "depth", "gDepth", "Depth buffer (NDC)", true /* optional */, ResourceFormat::R32Float }, + { "mvec", "gMotionVector", "Motion vector", true /* optional */, ResourceFormat::RG32Float }, + { "viewW", "gViewW", "View direction in world space", true /* optional */, ResourceFormat::RGBA32Float }, // TODO: Switch to packed 2x16-bit snorm format. + { "time", "gTime", "Per-pixel execution time", true /* optional */, ResourceFormat::R32Uint }, + { "mask", "gMask", "Mask", true /* optional */, ResourceFormat::R32Float }, + // clang-format on }; +}; // namespace -VBufferRT::VBufferRT(ref pDevice, const Properties& props) - : GBufferBase(pDevice) +VBufferRT::VBufferRT(ref pDevice, const Properties& props) : GBufferBase(pDevice) { + if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_5)) + FALCOR_THROW("VBufferRT requires Shader Model 6.5 support."); if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) - { - throw RuntimeError("VBufferRT: Raytracing Tier 1.1 is not supported by the current device"); - } + FALCOR_THROW("VBufferRT requires Raytracing Tier 1.1 support."); parseProperties(props); @@ -77,7 +78,10 @@ RenderPassReflection VBufferRT::reflect(const CompileData& compileData) const uint2 sz = RenderPassHelpers::calculateIOSize(mOutputSizeSelection, mFixedOutputSize, compileData.defaultTexDims); // Add the required output. This always exists. - reflector.addOutput(kVBufferName, kVBufferDesc).bindFlags(Resource::BindFlags::UnorderedAccess).format(mVBufferFormat).texture2D(sz.x, sz.y); + reflector.addOutput(kVBufferName, kVBufferDesc) + .bindFlags(ResourceBindFlags::UnorderedAccess) + .format(mVBufferFormat) + .texture2D(sz.x, sz.y); // Add all the other outputs. addRenderPassOutputs(reflector, kVBufferExtraChannels, ResourceBindFlags::UnorderedAccess, sz); @@ -103,7 +107,8 @@ void VBufferRT::execute(RenderContext* pRenderContext, const RenderData& renderD } // Check for scene changes. - if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged) || + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded) || + is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged) || is_set(mpScene->getUpdates(), Scene::UpdateFlags::SDFGridConfigChanged)) { recreatePrograms(); @@ -135,7 +140,11 @@ void VBufferRT::renderUI(Gui::Widgets& widget) { mOptionsChanged = true; } - widget.tooltip("This option enables stochastic depth-of-field when the camera's aperture radius is nonzero. Disable it to force the use of a pinhole camera.", true); + widget.tooltip( + "This option enables stochastic depth-of-field when the camera's aperture radius is nonzero. " + "Disable it to force the use of a pinhole camera.", + true + ); } Properties VBufferRT::getProperties() const @@ -160,8 +169,10 @@ void VBufferRT::parseProperties(const Properties& props) for (const auto& [key, value] : props) { - if (key == kUseTraceRayInline) mUseTraceRayInline = value; - else if (key == kUseDOF) mUseDOF = value; + if (key == kUseTraceRayInline) + mUseTraceRayInline = value; + else if (key == kUseDOF) + mUseDOF = value; // TODO: Check for unparsed fields, including those parsed in base classes. } } @@ -183,7 +194,7 @@ void VBufferRT::executeRaytrace(RenderContext* pRenderContext, const RenderData& defines.add(getShaderDefines(renderData)); // Create ray tracing program. - RtProgram::Desc desc; + ProgramDesc desc; desc.addShaderModules(mpScene->getShaderModules()); desc.addShaderLibrary(kProgramRaytraceFile); desc.addTypeConformances(mpScene->getTypeConformances()); @@ -199,33 +210,41 @@ void VBufferRT::executeRaytrace(RenderContext* pRenderContext, const RenderData& // Add hit group with intersection shader for triangle meshes with displacement maps. if (mpScene->hasGeometryType(Scene::GeometryType::DisplacedTriangleMesh)) { - sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::DisplacedTriangleMesh), desc.addHitGroup("displacedTriangleMeshClosestHit", "", "displacedTriangleMeshIntersection")); + sbt->setHitGroup( + 0, + mpScene->getGeometryIDs(Scene::GeometryType::DisplacedTriangleMesh), + desc.addHitGroup("displacedTriangleMeshClosestHit", "", "displacedTriangleMeshIntersection") + ); } // Add hit group with intersection shader for curves (represented as linear swept spheres). if (mpScene->hasGeometryType(Scene::GeometryType::Curve)) { - sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::Curve), desc.addHitGroup("curveClosestHit", "", "curveIntersection")); + sbt->setHitGroup( + 0, mpScene->getGeometryIDs(Scene::GeometryType::Curve), desc.addHitGroup("curveClosestHit", "", "curveIntersection") + ); } // Add hit group with intersection shader for SDF grids. if (mpScene->hasGeometryType(Scene::GeometryType::SDFGrid)) { - sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::SDFGrid), desc.addHitGroup("sdfGridClosestHit", "", "sdfGridIntersection")); + sbt->setHitGroup( + 0, mpScene->getGeometryIDs(Scene::GeometryType::SDFGrid), desc.addHitGroup("sdfGridClosestHit", "", "sdfGridIntersection") + ); } - mRaytrace.pProgram = RtProgram::create(mpDevice, desc, defines); + mRaytrace.pProgram = Program::create(mpDevice, desc, defines); mRaytrace.pVars = RtProgramVars::create(mpDevice, mRaytrace.pProgram, sbt); // Bind static resources. ShaderVar var = mRaytrace.pVars->getRootVar(); - mpSampleGenerator->setShaderData(var); + mpSampleGenerator->bindShaderData(var); } mRaytrace.pProgram->addDefines(getShaderDefines(renderData)); ShaderVar var = mRaytrace.pVars->getRootVar(); - setShaderData(var, renderData); + bindShaderData(var, renderData); // Dispatch the rays. mpScene->raytrace(pRenderContext, mRaytrace.pProgram.get(), mRaytrace.pVars, uint3(mFrameDim, 1)); @@ -236,9 +255,9 @@ void VBufferRT::executeCompute(RenderContext* pRenderContext, const RenderData& // Create compute pass. if (!mpComputePass) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderModules(mpScene->getShaderModules()); - desc.addShaderLibrary(kProgramComputeFile).csEntry("main").setShaderModel("6_5"); + desc.addShaderLibrary(kProgramComputeFile).csEntry("main"); desc.addTypeConformances(mpScene->getTypeConformances()); DefineList defines; @@ -246,18 +265,18 @@ void VBufferRT::executeCompute(RenderContext* pRenderContext, const RenderData& defines.add(mpSampleGenerator->getDefines()); defines.add(getShaderDefines(renderData)); - mpComputePass = ComputePass::create(mpDevice, desc, defines, true); + mpComputePass = ComputePass::create(mpDevice, desc, defines, true); // Bind static resources ShaderVar var = mpComputePass->getRootVar(); mpScene->setRaytracingShaderData(pRenderContext, var); - mpSampleGenerator->setShaderData(var); + mpSampleGenerator->bindShaderData(var); } mpComputePass->getProgram()->addDefines(getShaderDefines(renderData)); ShaderVar var = mpComputePass->getRootVar(); - setShaderData(var, renderData); + bindShaderData(var, renderData); mpComputePass->execute(pRenderContext, uint3(mFrameDim, 1)); } @@ -270,8 +289,10 @@ DefineList VBufferRT::getShaderDefines(const RenderData& renderData) const // Setup ray flags. RayFlags rayFlags = RayFlags::None; - if (mForceCullMode && mCullMode == RasterizerState::CullMode::Front) rayFlags = RayFlags::CullFrontFacingTriangles; - else if (mForceCullMode && mCullMode == RasterizerState::CullMode::Back) rayFlags = RayFlags::CullBackFacingTriangles; + if (mForceCullMode && mCullMode == RasterizerState::CullMode::Front) + rayFlags = RayFlags::CullFrontFacingTriangles; + else if (mForceCullMode && mCullMode == RasterizerState::CullMode::Back) + rayFlags = RayFlags::CullBackFacingTriangles; defines.add("RAY_FLAGS", std::to_string((uint32_t)rayFlags)); // For optional I/O resources, set 'is_valid_' defines to inform the program of which ones it can access. @@ -280,7 +301,7 @@ DefineList VBufferRT::getShaderDefines(const RenderData& renderData) const return defines; } -void VBufferRT::setShaderData(const ShaderVar& var, const RenderData& renderData) +void VBufferRT::bindShaderData(const ShaderVar& var, const RenderData& renderData) { var["gVBufferRT"]["frameDim"] = mFrameDim; var["gVBufferRT"]["frameCount"] = mFrameCount; @@ -294,5 +315,6 @@ void VBufferRT::setShaderData(const ShaderVar& var, const RenderData& renderData ref pTex = getOutput(renderData, channel.name); var[channel.texname] = pTex; }; - for (const auto& channel : kVBufferExtraChannels) bind(channel); + for (const auto& channel : kVBufferExtraChannels) + bind(channel); } diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cs.slang b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cs.slang index 6cb41b580..146007d57 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cs.slang +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,10 +31,11 @@ import VBufferRT; ConstantBuffer gVBufferRT; [numthreads(16, 16, 1)] -void main(uint3 dispatchThreadId : SV_DispatchThreadID) +void main(uint3 dispatchThreadId: SV_DispatchThreadID) { uint2 pixel = dispatchThreadId.xy; - if (any(pixel >= gVBufferRT.frameDim)) return; + if (any(pixel >= gVBufferRT.frameDim)) + return; GpuTimer timer; gVBufferRT.beginTime(timer); diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.h b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.h index 5824dca17..a2cafe67f 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.h +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.h @@ -31,12 +31,13 @@ using namespace Falcor; -/** Ray traced V-buffer pass. - - This pass renders a visibility buffer using ray tracing. - The visibility buffer encodes the mesh instance ID and primitive index, - as well as the barycentrics at the hit point. -*/ +/** + * Ray traced V-buffer pass. + * + * This pass renders a visibility buffer using ray tracing. + * The visibility buffer encodes the mesh instance ID and primitive index, + * as well as the barycentrics at the hit point. + */ class VBufferRT : public GBufferBase { public: @@ -59,20 +60,24 @@ class VBufferRT : public GBufferBase void executeCompute(RenderContext* pRenderContext, const RenderData& renderData); DefineList getShaderDefines(const RenderData& renderData) const; - void setShaderData(const ShaderVar& var, const RenderData& renderData); + void bindShaderData(const ShaderVar& var, const RenderData& renderData); void recreatePrograms(); // Internal state - bool mComputeDOF = false; ///< Flag indicating if depth-of-field is computed for the current frame. + + /// Flag indicating if depth-of-field is computed for the current frame. + bool mComputeDOF = false; ref mpSampleGenerator; // UI variables + bool mUseTraceRayInline = false; - bool mUseDOF = true; ///< Option for enabling depth-of-field when camera's aperture radius is nonzero. + /// Option for enabling depth-of-field when camera's aperture radius is nonzero. + bool mUseDOF = true; struct { - ref pProgram; + ref pProgram; ref pVars; } mRaytrace; diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.rt.slang b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.rt.slang index 3934d933a..ea59d01dd 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.rt.slang +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.rt.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -36,7 +36,8 @@ ConstantBuffer gVBufferRT; struct RayData { - int dummy; // TODO: Passing in an empty payload struct doesn't work. Declare a dummy variable so that the compiler doesn't remove the declaration. + // TODO: Passing in an empty payload struct doesn't work. Declare a dummy variable so that the compiler doesn't remove the declaration. + int dummy; }; // @@ -64,7 +65,8 @@ void anyHit(inout RayData rayData, BuiltInTriangleIntersectionAttributes attribs GeometryInstanceID instanceID = getGeometryInstanceID(); VertexData v = getVertexData(instanceID, PrimitiveIndex(), attribs); uint materialID = gScene.getMaterialID(instanceID); - if (gScene.materials.alphaTest(v, materialID, 0.f)) IgnoreHit(); + if (gScene.materials.alphaTest(v, materialID, 0.f)) + IgnoreHit(); } } @@ -188,7 +190,16 @@ void rayGen() // Trace ray const Ray ray = gVBufferRT.generateRay(launchIndex); RayData rayData; - TraceRay(gScene.rtAccel, VBufferRT::kRayFlags, 0xff /* instanceInclusionMask */, 0 /* hitIdx */, rayTypeCount, 0 /* missIdx */, ray.toRayDesc(), rayData); + TraceRay( + gScene.rtAccel, + VBufferRT::kRayFlags, + 0xff /* instanceInclusionMask */, + 0 /* hitIdx */, + rayTypeCount, + 0 /* missIdx */, + ray.toRayDesc(), + rayData + ); gVBufferRT.writeAux(launchIndex, ray); diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.slang b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.slang index 4638bffd5..b12989381 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.slang +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRT.slang @@ -32,7 +32,7 @@ import Utils.Math.MathHelpers; import Utils.Sampling.SampleGenerator; RWTexture2D gVBuffer; -RWTexture2D gDepth; +RWTexture2D gDepth; RWTexture2D gMotionVector; RWTexture2D gViewW; RWTexture2D gTime; @@ -101,7 +101,8 @@ struct VBufferRT prevPosW = gScene.getPrevPosWFromSDFGrid(sdfGridHit, v.posW); } - if (hit.getType() == HitType::Triangle || hit.getType() == HitType::DisplacedTriangle || hit.getType() == HitType::Curve || hit.getType() == HitType::SDFGrid) + if (hit.getType() == HitType::Triangle || hit.getType() == HitType::DisplacedTriangle || hit.getType() == HitType::Curve || + hit.getType() == HitType::SDFGrid) { // Compute depth similar to raster (NDC). float4 curPosH = mul(gScene.camera.data.viewProjMatNoJitter, float4(v.posW, 1.f)); @@ -110,36 +111,46 @@ struct VBufferRT // Compute motion vector. float2 pixelPos = pixel + float2(0.5f, 0.5f); float4 prevPosH = mul(gScene.camera.data.prevViewProjMatNoJitter, float4(prevPosW, 1.f)); - motionVector = calcMotionVector(pixelPos, prevPosH, frameDim) + float2(gScene.camera.data.jitterX, -gScene.camera.data.jitterY); // Remove camera jitter from motion vector + // Remove camera jitter from motion vector + motionVector = calcMotionVector(pixelPos, prevPosH, frameDim) + float2(gScene.camera.data.jitterX, -gScene.camera.data.jitterY); } - if (is_valid(gDepth)) gDepth[pixel] = depth; - if (is_valid(gMotionVector)) gMotionVector[pixel] = motionVector; - if (is_valid(gMask)) gMask[pixel] = 1.0f; + if (is_valid(gDepth)) + gDepth[pixel] = depth; + if (is_valid(gMotionVector)) + gMotionVector[pixel] = motionVector; + if (is_valid(gMask)) + gMask[pixel] = 1.0f; } void writeMiss(uint2 pixel, float3 rayOrigin, float3 rayDir) { gVBuffer[pixel] = {}; - if (is_valid(gDepth)) gDepth[pixel] = 1.f; - if (is_valid(gMotionVector)) gMotionVector[pixel] = {}; - if (is_valid(gMask)) gMask[pixel] = 0.0f; + if (is_valid(gDepth)) + gDepth[pixel] = 1.f; + if (is_valid(gMotionVector)) + gMotionVector[pixel] = {}; + if (is_valid(gMask)) + gMask[pixel] = 0.0f; } void writeAux(uint2 pixel, const Ray ray) { // Write view direction. - if (is_valid(gViewW)) gViewW[pixel] = float4(-ray.dir, 0.f); + if (is_valid(gViewW)) + gViewW[pixel] = float4(-ray.dir, 0.f); } void beginTime(inout GpuTimer timer) { - if (is_valid(gTime)) timer.start(); + if (is_valid(gTime)) + timer.start(); } void endTime(uint2 pixel, inout GpuTimer timer) { - if (is_valid(gTime)) gTime[pixel] = timer.getElapsed(); + if (is_valid(gTime)) + gTime[pixel] = timer.getElapsed(); } }; diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.3d.slang b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.3d.slang index b619d1c63..ae0a2105b 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.3d.slang +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.3d.slang @@ -29,7 +29,7 @@ import Scene.Raster; import Utils.Math.MathHelpers; RasterizerOrderedTexture2D gMotionVector; -RasterizerOrderedTexture2D gMask; +RasterizerOrderedTexture2D gMask; cbuffer PerFrameCB { @@ -48,10 +48,10 @@ struct VBufferVSOut float2 texC : TEXCRD; // Per-triangle data - nointerpolation GeometryInstanceID instanceID : INSTANCE_ID; - nointerpolation uint materialID : MATERIAL_ID; + nointerpolation GeometryInstanceID instanceID : INSTANCE_ID; + nointerpolation uint materialID : MATERIAL_ID; - float4 posH : SV_POSITION; ///< Position in world space. + float4 posH : SV_POSITION; ///< Position in world space. #if is_valid(gMotionVector) float4 prevPosH : PREVPOSH; ///< Position in clip space for the previous frame. @@ -87,7 +87,7 @@ VBufferVSOut vsMain(VSIn vsIn) return vsOut; } -VBufferPSOut psMain(VBufferVSOut vsOut, uint triangleIndex : SV_PrimitiveID, float3 barycentrics : SV_Barycentrics) +VBufferPSOut psMain(VBufferVSOut vsOut, uint triangleIndex: SV_PrimitiveID, float3 barycentrics: SV_Barycentrics) { VBufferPSOut psOut; @@ -102,10 +102,12 @@ VBufferPSOut psMain(VBufferVSOut vsOut, uint triangleIndex : SV_PrimitiveID, flo v.texC = vsOut.texC; float lod = 0.f; - if (gScene.materials.alphaTest(v, vsOut.materialID, lod)) discard; + if (gScene.materials.alphaTest(v, vsOut.materialID, lod)) + discard; #endif - if (is_valid(gMask)) gMask[ipos] = 1.f; + if (is_valid(gMask)) + gMask[ipos] = 1.f; // Note on barycentrics: // The barycentric weights provided to pixel shader correspond to vertices A, B, C of the rasterized triangle. @@ -124,9 +126,12 @@ VBufferPSOut psMain(VBufferVSOut vsOut, uint triangleIndex : SV_PrimitiveID, flo #if is_valid(gMotionVector) // Compute motion vector. { - const float2 pixelPos = ipos + float2(0.5, 0.5); // Current sample in pixel coords. - const float4 prevPosH = vsOut.prevPosH; // Sample in previous frame in clip space coords, no jittering applied. - const float2 mv = calcMotionVector(pixelPos, prevPosH, gFrameDim) + float2(gScene.camera.data.jitterX, -gScene.camera.data.jitterY); // Remove camera jitter from motion vector + // Current sample in pixel coords. + const float2 pixelPos = ipos + float2(0.5, 0.5); + // Sample in previous frame in clip space coords, no jittering applied. + const float4 prevPosH = vsOut.prevPosH; + // Remove camera jitter from motion vector + const float2 mv = calcMotionVector(pixelPos, prevPosH, gFrameDim) + float2(gScene.camera.data.jitterX, -gScene.camera.data.jitterY); gMotionVector[ipos] = mv; } #endif diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.cpp b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.cpp index d3936362d..788e8b24f 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.cpp +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.cpp @@ -32,34 +32,31 @@ namespace { - const std::string kProgramFile = "RenderPasses/GBuffer/VBuffer/VBufferRaster.3d.slang"; - const std::string kShaderModel = "6_2"; - const RasterizerState::CullMode kDefaultCullMode = RasterizerState::CullMode::Back; +const std::string kProgramFile = "RenderPasses/GBuffer/VBuffer/VBufferRaster.3d.slang"; +const RasterizerState::CullMode kDefaultCullMode = RasterizerState::CullMode::Back; - const std::string kVBufferName = "vbuffer"; - const std::string kVBufferDesc = "V-buffer in packed format (indices + barycentrics)"; +const std::string kVBufferName = "vbuffer"; +const std::string kVBufferDesc = "V-buffer in packed format (indices + barycentrics)"; - const ChannelList kVBufferExtraChannels = - { - { "mvec", "gMotionVector", "Motion vector", true /* optional */, ResourceFormat::RG32Float }, - { "mask", "gMask", "Mask", true /* optional */, ResourceFormat::R32Float }, - }; +const ChannelList kVBufferExtraChannels = { + // clang-format off + { "mvec", "gMotionVector", "Motion vector", true /* optional */, ResourceFormat::RG32Float }, + { "mask", "gMask", "Mask", true /* optional */, ResourceFormat::R32Float }, + // clang-format on +}; - const std::string kDepthName = "depth"; -} +const std::string kDepthName = "depth"; +} // namespace -VBufferRaster::VBufferRaster(ref pDevice, const Properties& props) - : GBufferBase(pDevice) +VBufferRaster::VBufferRaster(ref pDevice, const Properties& props) : GBufferBase(pDevice) { // Check for required features. + if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_2)) + FALCOR_THROW("VBufferRaster requires Shader Model 6.2 support."); if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::Barycentrics)) - { - throw RuntimeError("VBufferRaster: Pixel shader barycentrics are not supported by the current device"); - } + FALCOR_THROW("VBufferRaster requires pixel shader barycentrics support."); if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RasterizerOrderedViews)) - { - throw RuntimeError("VBufferRaster: Rasterizer ordered views (ROVs) are not supported by the current device"); - } + FALCOR_THROW("VBufferRaster requires rasterizer ordered views (ROVs) support."); parseProperties(props); @@ -68,7 +65,7 @@ VBufferRaster::VBufferRaster(ref pDevice, const Properties& props) // Set depth function DepthStencilState::Desc dsDesc; - dsDesc.setDepthFunc(DepthStencilState::Func::LessEqual).setDepthWriteMask(true); + dsDesc.setDepthFunc(ComparisonFunc::LessEqual).setDepthWriteMask(true); mRaster.pState->setDepthStencilState(DepthStencilState::create(dsDesc)); mpFbo = Fbo::create(mpDevice); @@ -80,11 +77,17 @@ RenderPassReflection VBufferRaster::reflect(const CompileData& compileData) const uint2 sz = RenderPassHelpers::calculateIOSize(mOutputSizeSelection, mFixedOutputSize, compileData.defaultTexDims); // Add the required outputs. These always exist. - reflector.addOutput(kDepthName, "Depth buffer").format(ResourceFormat::D32Float).bindFlags(Resource::BindFlags::DepthStencil).texture2D(sz.x, sz.y); - reflector.addOutput(kVBufferName, kVBufferDesc).bindFlags(Resource::BindFlags::RenderTarget | Resource::BindFlags::UnorderedAccess).format(mVBufferFormat).texture2D(sz.x, sz.y); + reflector.addOutput(kDepthName, "Depth buffer") + .format(ResourceFormat::D32Float) + .bindFlags(ResourceBindFlags::DepthStencil) + .texture2D(sz.x, sz.y); + reflector.addOutput(kVBufferName, kVBufferDesc) + .bindFlags(ResourceBindFlags::RenderTarget | ResourceBindFlags::UnorderedAccess) + .format(mVBufferFormat) + .texture2D(sz.x, sz.y); // Add all the other outputs. - addRenderPassOutputs(reflector, kVBufferExtraChannels, Resource::BindFlags::UnorderedAccess, sz); + addRenderPassOutputs(reflector, kVBufferExtraChannels, ResourceBindFlags::UnorderedAccess, sz); return reflector; } @@ -93,28 +96,23 @@ void VBufferRaster::setScene(RenderContext* pRenderContext, const ref& pS { GBufferBase::setScene(pRenderContext, pScene); - mRaster.pProgram = nullptr; - mRaster.pVars = nullptr; + recreatePrograms(); if (pScene) { if (pScene->getMeshVao() && pScene->getMeshVao()->getPrimitiveTopology() != Vao::Topology::TriangleList) { - throw RuntimeError("VBufferRaster: Requires triangle list geometry due to usage of SV_Barycentrics."); + FALCOR_THROW("VBufferRaster: Requires triangle list geometry due to usage of SV_Barycentrics."); } - - // Create raster program. - Program::Desc desc; - desc.addShaderModules(pScene->getShaderModules()); - desc.addShaderLibrary(kProgramFile).vsEntry("vsMain").psEntry("psMain"); - desc.addTypeConformances(pScene->getTypeConformances()); - desc.setShaderModel(kShaderModel); - - mRaster.pProgram = GraphicsProgram::create(mpDevice, desc, pScene->getSceneDefines()); - mRaster.pState->setProgram(mRaster.pProgram); } } +void VBufferRaster::recreatePrograms() +{ + mRaster.pProgram = nullptr; + mRaster.pVars = nullptr; +} + void VBufferRaster::execute(RenderContext* pRenderContext, const RenderData& renderData) { GBufferBase::execute(pRenderContext, renderData); @@ -138,6 +136,24 @@ void VBufferRaster::execute(RenderContext* pRenderContext, const RenderData& ren return; } + // Check for scene changes. + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded)) + { + recreatePrograms(); + } + + // Create raster program. + if (!mRaster.pProgram) + { + ProgramDesc desc; + desc.addShaderModules(mpScene->getShaderModules()); + desc.addShaderLibrary(kProgramFile).vsEntry("vsMain").psEntry("psMain"); + desc.addTypeConformances(mpScene->getTypeConformances()); + + mRaster.pProgram = Program::create(mpDevice, desc, mpScene->getSceneDefines()); + mRaster.pState->setProgram(mRaster.pProgram); + } + // Set program defines. mRaster.pProgram->addDefine("USE_ALPHA_TEST", mUseAlphaTest ? "1" : "0"); @@ -148,7 +164,7 @@ void VBufferRaster::execute(RenderContext* pRenderContext, const RenderData& ren // Create program vars. if (!mRaster.pVars) { - mRaster.pVars = GraphicsVars::create(mpDevice, mRaster.pProgram.get()); + mRaster.pVars = ProgramVars::create(mpDevice, mRaster.pProgram.get()); } mpFbo->attachColorTarget(pOutput, 0); diff --git a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.h b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.h index c65b5d3f8..05b84755f 100644 --- a/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.h +++ b/Source/RenderPasses/GBuffer/VBuffer/VBufferRaster.h @@ -30,12 +30,13 @@ using namespace Falcor; -/** Rasterized V-buffer pass. - - This pass renders a visibility buffer using ray tracing. - The visibility buffer encodes the mesh instance ID and primitive index, - as well as the barycentrics at the hit point. -*/ +/** + * Rasterized V-buffer pass. + * + * This pass renders a visibility buffer using ray tracing. + * The visibility buffer encodes the mesh instance ID and primitive index, + * as well as the barycentrics at the hit point. + */ class VBufferRaster : public GBufferBase { public: @@ -50,13 +51,15 @@ class VBufferRaster : public GBufferBase void execute(RenderContext* pRenderContext, const RenderData& renderData) override; private: + void recreatePrograms(); + // Internal state ref mpFbo; struct { ref pState; - ref pProgram; - ref pVars; + ref pProgram; + ref pVars; } mRaster; }; diff --git a/Source/RenderPasses/ImageLoader/ImageLoader.cpp b/Source/RenderPasses/ImageLoader/ImageLoader.cpp index 8039209fe..113ad9dd1 100644 --- a/Source/RenderPasses/ImageLoader/ImageLoader.cpp +++ b/Source/RenderPasses/ImageLoader/ImageLoader.cpp @@ -26,45 +26,53 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "ImageLoader.h" +#include "Core/AssetResolver.h" namespace { - const std::string kDst = "dst"; - - const std::string kOutputSize = "outputSize"; - const std::string kOutputFormat = "outputFormat"; - const std::string kImage = "filename"; - const std::string kMips = "mips"; - const std::string kSrgb = "srgb"; - const std::string kArraySlice = "arrayIndex"; - const std::string kMipLevel = "mipLevel"; -} +const std::string kDst = "dst"; + +const std::string kOutputSize = "outputSize"; +const std::string kOutputFormat = "outputFormat"; +const std::string kImage = "filename"; +const std::string kMips = "mips"; +const std::string kSrgb = "srgb"; +const std::string kArraySlice = "arrayIndex"; +const std::string kMipLevel = "mipLevel"; +} // namespace extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { registry.registerClass(); } -ImageLoader::ImageLoader(ref pDevice, const Properties& props) - : RenderPass(pDevice) +ImageLoader::ImageLoader(ref pDevice, const Properties& props) : RenderPass(pDevice) { for (const auto& [key, value] : props) { - if (key == kOutputSize) mOutputSizeSelection = value; - else if (key == kOutputFormat) mOutputFormat = value; - else if (key == kImage) mImagePath = value.operator std::filesystem::path(); - else if (key == kSrgb) mLoadSRGB = value; - else if (key == kMips) mGenerateMips = value; - else if (key == kArraySlice) mArraySlice = value; - else if (key == kMipLevel) mMipLevel = value; - else logWarning("Unknown property '{}' in a ImageLoader properties.", key); + if (key == kOutputSize) + mOutputSizeSelection = value; + else if (key == kOutputFormat) + mOutputFormat = value; + else if (key == kImage) + mImagePath = value.operator std::filesystem::path(); + else if (key == kSrgb) + mLoadSRGB = value; + else if (key == kMips) + mGenerateMips = value; + else if (key == kArraySlice) + mArraySlice = value; + else if (key == kMipLevel) + mMipLevel = value; + else + logWarning("Unknown property '{}' in a ImageLoader properties.", key); } if (!mImagePath.empty()) { if (!loadImage(mImagePath)) { - throw RuntimeError("ImageLoader: Failed to load image from '{}'", mImagePath); + FALCOR_THROW("ImageLoader: Failed to load image from '{}'", mImagePath); } } } @@ -83,8 +91,9 @@ Properties ImageLoader::getProperties() const { Properties props; props[kOutputSize] = mOutputSizeSelection; - if (mOutputFormat != ResourceFormat::Unknown) props[kOutputFormat] = mOutputFormat; - props[kImage] = stripDataDirectories(mImagePath); + if (mOutputFormat != ResourceFormat::Unknown) + props[kOutputFormat] = mOutputFormat; + props[kImage] = mImagePath; props[kMips] = mGenerateMips; props[kSrgb] = mLoadSRGB; props[kArraySlice] = mArraySlice; @@ -94,7 +103,7 @@ Properties ImageLoader::getProperties() const void ImageLoader::compile(RenderContext* pRenderContext, const CompileData& compileData) { - if (!mpTex) throw RuntimeError("ImageLoader: No image loaded"); + FALCOR_CHECK(mpTex, "ImageLoader: No image loaded"); } void ImageLoader::execute(RenderContext* pRenderContext, const RenderData& renderData) @@ -102,7 +111,7 @@ void ImageLoader::execute(RenderContext* pRenderContext, const RenderData& rende const auto& pDstTex = renderData.getTexture(kDst); FALCOR_ASSERT(pDstTex); mOutputFormat = pDstTex->getFormat(); - mOutputSize = { pDstTex->getWidth(), pDstTex->getHeight() }; + mOutputSize = {pDstTex->getWidth(), pDstTex->getHeight()}; if (!mpTex) { @@ -118,11 +127,15 @@ void ImageLoader::execute(RenderContext* pRenderContext, const RenderData& rende void ImageLoader::renderUI(Gui::Widgets& widget) { // When output size requirements change, we'll trigger a graph recompile to update the render pass I/O sizes. - if (widget.dropdown("Output size", mOutputSizeSelection)) requestRecompile(); - widget.tooltip("Specifies the pass output size.\n" + if (widget.dropdown("Output size", mOutputSizeSelection)) + requestRecompile(); + widget.tooltip( + "Specifies the pass output size.\n" "'Default' means that the output is sized based on requirements of connected passes.\n" "'Fixed' means the output is always at the image's native size.\n" - "If the output is of a different size than the native image resolution, the image will be rescaled bilinearly." , true); + "If the output is of a different size than the native image resolution, the image will be rescaled bilinearly.", + true + ); widget.text("Image File: " + mImagePath.string()); bool reloadImage = false; @@ -136,10 +149,12 @@ void ImageLoader::renderUI(Gui::Widgets& widget) if (mpTex) { - if (mpTex->getMipCount() > 1) widget.slider("Mip Level", mMipLevel, 0u, mpTex->getMipCount() - 1); - if (mpTex->getArraySize() > 1) widget.slider("Array Slice", mArraySlice, 0u, mpTex->getArraySize() - 1); + if (mpTex->getMipCount() > 1) + widget.slider("Mip Level", mMipLevel, 0u, mpTex->getMipCount() - 1); + if (mpTex->getArraySize() > 1) + widget.slider("Array Slice", mArraySlice, 0u, mpTex->getArraySize() - 1); - widget.image(mImagePath.string().c_str(), mpTex.get(), { 320, 320 }); + widget.image(mImagePath.string().c_str(), mpTex.get(), {320, 320}); widget.text("Image format: " + to_string(mpTex->getFormat())); widget.text("Image size: (" + std::to_string(mpTex->getWidth()) + ", " + std::to_string(mpTex->getHeight()) + ")"); widget.text("Output format: " + to_string(mOutputFormat)); @@ -149,14 +164,16 @@ void ImageLoader::renderUI(Gui::Widgets& widget) if (reloadImage && !mImagePath.empty()) { uint2 prevSize = {}; - if (mpTex) prevSize = { mpTex->getWidth(), mpTex->getHeight() }; + if (mpTex) + prevSize = {mpTex->getWidth(), mpTex->getHeight()}; if (!loadImage(mImagePath)) { msgBox("Error", fmt::format("Failed to load image from '{}'", mImagePath), MsgBoxType::Ok, MsgBoxIcon::Warning); } - // If output is set to native size and image dimensions have changed, we'll trigger a graph recompile to update the render pass I/O sizes. + // If output is set to native size and image dimensions have changed, + // we'll trigger a graph recompile to update the render pass I/O sizes. if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed && mpTex != nullptr && (mpTex->getWidth() != prevSize.x || mpTex->getHeight() != prevSize.y)) { @@ -167,15 +184,14 @@ void ImageLoader::renderUI(Gui::Widgets& widget) bool ImageLoader::loadImage(const std::filesystem::path& path) { - if (path.empty()) return false; + if (path.empty()) + return false; - // Find the full path of the specified image. - // We retain this for later as the search paths may change during execution. - std::filesystem::path fullPath; - if (findFileInDataDirectories(mImagePath, fullPath)) + std::filesystem::path resolvedPath = AssetResolver::getDefaultResolver().resolvePath(path); + if (std::filesystem::exists(resolvedPath)) { - mImagePath = fullPath; - mpTex = Texture::createFromFile(mpDevice, mImagePath, mGenerateMips, mLoadSRGB); + mImagePath = path; + mpTex = Texture::createFromFile(mpDevice, resolvedPath, mGenerateMips, mLoadSRGB); return mpTex != nullptr; } else diff --git a/Source/RenderPasses/ImageLoader/ImageLoader.h b/Source/RenderPasses/ImageLoader/ImageLoader.h index c7f2d5c18..8f014b34c 100644 --- a/Source/RenderPasses/ImageLoader/ImageLoader.h +++ b/Source/RenderPasses/ImageLoader/ImageLoader.h @@ -50,9 +50,12 @@ class ImageLoader : public RenderPass private: bool loadImage(const std::filesystem::path& path); - RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. - ResourceFormat mOutputFormat = ResourceFormat::Unknown; ///< Current output resource format. - uint2 mOutputSize = {}; ///< Current output size in pixels. + /// Selected output size. + RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; + /// Current output resource format. + ResourceFormat mOutputFormat = ResourceFormat::Unknown; + /// Current output size in pixels. + uint2 mOutputSize = {}; ref mpTex; std::filesystem::path mImagePath; diff --git a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp index 3936cb585..5ac801426 100644 --- a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.cpp @@ -36,33 +36,34 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr namespace { - const char kShaderFile[] = "RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang"; - - // Ray tracing settings that affect the traversal stack size. - // These should be set as small as possible. - const uint32_t kMaxPayloadSizeBytes = 72u; - const uint32_t kMaxRecursionDepth = 2u; - - const char kInputViewDir[] = "viewW"; - - const ChannelList kInputChannels = - { - { "vbuffer", "gVBuffer", "Visibility buffer in packed format" }, - { kInputViewDir, "gViewW", "World-space view direction (xyz float format)", true /* optional */ }, - }; - - const ChannelList kOutputChannels = - { - { "color", "gOutputColor", "Output color (sum of direct and indirect)", false, ResourceFormat::RGBA32Float }, - }; - - const char kMaxBounces[] = "maxBounces"; - const char kComputeDirect[] = "computeDirect"; - const char kUseImportanceSampling[] = "useImportanceSampling"; -} - -MinimalPathTracer::MinimalPathTracer(ref pDevice, const Properties& props) - : RenderPass(pDevice) +const char kShaderFile[] = "RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang"; + +// Ray tracing settings that affect the traversal stack size. +// These should be set as small as possible. +const uint32_t kMaxPayloadSizeBytes = 72u; +const uint32_t kMaxRecursionDepth = 2u; + +const char kInputViewDir[] = "viewW"; + +const ChannelList kInputChannels = { + // clang-format off + { "vbuffer", "gVBuffer", "Visibility buffer in packed format" }, + { kInputViewDir, "gViewW", "World-space view direction (xyz float format)", true /* optional */ }, + // clang-format on +}; + +const ChannelList kOutputChannels = { + // clang-format off + { "color", "gOutputColor", "Output color (sum of direct and indirect)", false, ResourceFormat::RGBA32Float }, + // clang-format on +}; + +const char kMaxBounces[] = "maxBounces"; +const char kComputeDirect[] = "computeDirect"; +const char kUseImportanceSampling[] = "useImportanceSampling"; +} // namespace + +MinimalPathTracer::MinimalPathTracer(ref pDevice, const Properties& props) : RenderPass(pDevice) { parseProperties(props); @@ -75,10 +76,14 @@ void MinimalPathTracer::parseProperties(const Properties& props) { for (const auto& [key, value] : props) { - if (key == kMaxBounces) mMaxBounces = value; - else if (key == kComputeDirect) mComputeDirect = value; - else if (key == kUseImportanceSampling) mUseImportanceSampling = value; - else logWarning("Unknown property '{}' in MinimalPathTracer properties.", key); + if (key == kMaxBounces) + mMaxBounces = value; + else if (key == kComputeDirect) + mComputeDirect = value; + else if (key == kUseImportanceSampling) + mUseImportanceSampling = value; + else + logWarning("Unknown property '{}' in MinimalPathTracer properties.", key); } } @@ -119,14 +124,16 @@ void MinimalPathTracer::execute(RenderContext* pRenderContext, const RenderData& for (auto it : kOutputChannels) { Texture* pDst = renderData.getTexture(it.name).get(); - if (pDst) pRenderContext->clearTexture(pDst); + if (pDst) + pRenderContext->clearTexture(pDst); } return; } - if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded) || + is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) { - throw RuntimeError("MinimalPathTracer: This render pass does not support scene geometry changes."); + FALCOR_THROW("This render pass does not support scene changes that require shader recompilation."); } // Request the light collection if emissive lights are enabled. @@ -159,7 +166,8 @@ void MinimalPathTracer::execute(RenderContext* pRenderContext, const RenderData& // Prepare program vars. This may trigger shader compilation. // The program should have all necessary defines set at this point. - if (!mTracer.pVars) prepareVars(); + if (!mTracer.pVars) + prepareVars(); FALCOR_ASSERT(mTracer.pVars); // Set constants. @@ -175,8 +183,10 @@ void MinimalPathTracer::execute(RenderContext* pRenderContext, const RenderData& var[desc.texname] = renderData.getTexture(desc.name); } }; - for (auto channel : kInputChannels) bind(channel); - for (auto channel : kOutputChannels) bind(channel); + for (auto channel : kInputChannels) + bind(channel); + for (auto channel : kOutputChannels) + bind(channel); // Get dimensions of ray dispatch. const uint2 targetDim = renderData.getDefaultTextureDims(); @@ -229,7 +239,7 @@ void MinimalPathTracer::setScene(RenderContext* pRenderContext, const ref } // Create ray tracing program. - RtProgram::Desc desc; + ProgramDesc desc; desc.addShaderModules(mpScene->getShaderModules()); desc.addShaderLibrary(kShaderFile); desc.setMaxPayloadSize(kMaxPayloadSizeBytes); @@ -244,29 +254,49 @@ void MinimalPathTracer::setScene(RenderContext* pRenderContext, const ref if (mpScene->hasGeometryType(Scene::GeometryType::TriangleMesh)) { - sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), desc.addHitGroup("scatterTriangleMeshClosestHit", "scatterTriangleMeshAnyHit")); - sbt->setHitGroup(1, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), desc.addHitGroup("", "shadowTriangleMeshAnyHit")); + sbt->setHitGroup( + 0, + mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), + desc.addHitGroup("scatterTriangleMeshClosestHit", "scatterTriangleMeshAnyHit") + ); + sbt->setHitGroup( + 1, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), desc.addHitGroup("", "shadowTriangleMeshAnyHit") + ); } if (mpScene->hasGeometryType(Scene::GeometryType::DisplacedTriangleMesh)) { - sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::DisplacedTriangleMesh), desc.addHitGroup("scatterDisplacedTriangleMeshClosestHit", "", "displacedTriangleMeshIntersection")); - sbt->setHitGroup(1, mpScene->getGeometryIDs(Scene::GeometryType::DisplacedTriangleMesh), desc.addHitGroup("", "", "displacedTriangleMeshIntersection")); + sbt->setHitGroup( + 0, + mpScene->getGeometryIDs(Scene::GeometryType::DisplacedTriangleMesh), + desc.addHitGroup("scatterDisplacedTriangleMeshClosestHit", "", "displacedTriangleMeshIntersection") + ); + sbt->setHitGroup( + 1, + mpScene->getGeometryIDs(Scene::GeometryType::DisplacedTriangleMesh), + desc.addHitGroup("", "", "displacedTriangleMeshIntersection") + ); } if (mpScene->hasGeometryType(Scene::GeometryType::Curve)) { - sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::Curve), desc.addHitGroup("scatterCurveClosestHit", "", "curveIntersection")); + sbt->setHitGroup( + 0, mpScene->getGeometryIDs(Scene::GeometryType::Curve), desc.addHitGroup("scatterCurveClosestHit", "", "curveIntersection") + ); sbt->setHitGroup(1, mpScene->getGeometryIDs(Scene::GeometryType::Curve), desc.addHitGroup("", "", "curveIntersection")); } if (mpScene->hasGeometryType(Scene::GeometryType::SDFGrid)) { - sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::SDFGrid), desc.addHitGroup("scatterSdfGridClosestHit", "", "sdfGridIntersection")); + sbt->setHitGroup( + 0, + mpScene->getGeometryIDs(Scene::GeometryType::SDFGrid), + desc.addHitGroup("scatterSdfGridClosestHit", "", "sdfGridIntersection") + ); sbt->setHitGroup(1, mpScene->getGeometryIDs(Scene::GeometryType::SDFGrid), desc.addHitGroup("", "", "sdfGridIntersection")); } - mTracer.pProgram = RtProgram::create(mpDevice, desc, mpScene->getSceneDefines()); + mTracer.pProgram = Program::create(mpDevice, desc, mpScene->getSceneDefines()); } } @@ -285,5 +315,5 @@ void MinimalPathTracer::prepareVars() // Bind utility classes into shared data. auto var = mTracer.pVars->getRootVar(); - mpSampleGenerator->setShaderData(var); + mpSampleGenerator->bindShaderData(var); } diff --git a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h index 9bd146a1f..dc0fe0132 100644 --- a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.h @@ -32,21 +32,25 @@ using namespace Falcor; -/** Minimal path tracer. - - This pass implements a minimal brute-force path tracer. It does purposely - not use any importance sampling or other variance reduction techniques. - The output is unbiased/consistent ground truth images, against which other - renderers can be validated. - - Note that transmission and nested dielectrics are not yet supported. -*/ +/** + * Minimal path tracer. + * + * This pass implements a minimal brute-force path tracer. It does purposely + * not use any importance sampling or other variance reduction techniques. + * The output is unbiased/consistent ground truth images, against which other + * renderers can be validated. + * + * Note that transmission and nested dielectrics are not yet supported. + */ class MinimalPathTracer : public RenderPass { public: FALCOR_PLUGIN_CLASS(MinimalPathTracer, "MinimalPathTracer", "Minimal path tracer."); - static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } + static ref create(ref pDevice, const Properties& props) + { + return make_ref(pDevice, props); + } MinimalPathTracer(ref pDevice, const Properties& props); @@ -63,22 +67,31 @@ class MinimalPathTracer : public RenderPass void prepareVars(); // Internal state - ref mpScene; ///< Current scene. - ref mpSampleGenerator; ///< GPU sample generator. + + /// Current scene. + ref mpScene; + /// GPU sample generator. + ref mpSampleGenerator; // Configuration - uint mMaxBounces = 3; ///< Max number of indirect bounces (0 = none). - bool mComputeDirect = true; ///< Compute direct illumination (otherwise indirect only). - bool mUseImportanceSampling = true; ///< Use importance sampling for materials. + + /// Max number of indirect bounces (0 = none). + uint mMaxBounces = 3; + /// Compute direct illumination (otherwise indirect only). + bool mComputeDirect = true; + /// Use importance sampling for materials. + bool mUseImportanceSampling = true; // Runtime data - uint mFrameCount = 0; ///< Frame count since scene was loaded. - bool mOptionsChanged = false; + + /// Frame count since scene was loaded. + uint mFrameCount = 0; + bool mOptionsChanged = false; // Ray tracing program. struct { - ref pProgram; + ref pProgram; ref pBindingTable; ref pVars; } mTracer; diff --git a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang index 2722571b8..a32209df6 100644 --- a/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang +++ b/Source/RenderPasses/MinimalPathTracer/MinimalPathTracer.rt.slang @@ -26,32 +26,33 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Minimal path tracer. - - The purpose is to use it for validation of more complex renderers. - The implementation here should be kept as simple/naive as possible. - - At each hit point (including the primary hit loaded from the V-buffer), - analytic light sources (point, directional) are sampled uniformly using - 1 shadow ray, and 1 scatter ray is traced to sample the hemisphere. - At hit/miss the scatter ray includes light from emissive surface and - the environment map, respectively. Traversal stops at a fixed path length. - - Each type of light (analytic, emissive, env map) can be individually - enabled/disabled from the host. This clutters the code a bit, but it is - important as not all other renderes may support all three light types. - - The host sets the following defines: - - MAX_BOUNCES Maximum number of indirect bounces (0 means no indirect). - COMPUTE_DIRECT Nonzero if direct illumination should be included. - USE_IMPORTANCE_SAMPLING Nonzero if importance sampling should be used for sampling materials. - USE_ANALYTIC_LIGHTS Nonzero if Falcor's analytic lights should be used. - USE_EMISSIVE_LIGHTS Nonzero if emissive geometry should be used as lights. - USE_ENV_LIGHT Nonzero if env map is available and should be used as light source. - USE_ENV_BACKGROUND Nonzero if env map is available and should be used as background. - is_valid_ 1 if optional I/O buffer with this name should be used. -*/ +/** + * Minimal path tracer. + * + * The purpose is to use it for validation of more complex renderers. + * The implementation here should be kept as simple/naive as possible. + * + * At each hit point (including the primary hit loaded from the V-buffer), + * analytic light sources (point, directional) are sampled uniformly using + * 1 shadow ray, and 1 scatter ray is traced to sample the hemisphere. + * At hit/miss the scatter ray includes light from emissive surface and + * the environment map, respectively. Traversal stops at a fixed path length. + * + * Each type of light (analytic, emissive, env map) can be individually + * enabled/disabled from the host. This clutters the code a bit, but it is + * important as not all other renderes may support all three light types. + * + * The host sets the following defines: + * + * MAX_BOUNCES Maximum number of indirect bounces (0 means no indirect). + * COMPUTE_DIRECT Nonzero if direct illumination should be included. + * USE_IMPORTANCE_SAMPLING Nonzero if importance sampling should be used for sampling materials. + * USE_ANALYTIC_LIGHTS Nonzero if Falcor's analytic lights should be used. + * USE_EMISSIVE_LIGHTS Nonzero if emissive geometry should be used as lights. + * USE_ENV_LIGHT Nonzero if env map is available and should be used as light source. + * USE_ENV_BACKGROUND Nonzero if env map is available and should be used as background. + * is_valid_ 1 if optional I/O buffer with this name should be used. + */ #include "Scene/SceneDefines.slangh" #include "Utils/Math/MathConstants.slangh" @@ -65,8 +66,8 @@ import Rendering.Lights.LightHelpers; cbuffer CB { - uint gFrameCount; // Frame count since scene was loaded. - uint gPRNGDimension; // First available PRNG dimension. + uint gFrameCount; // Frame count since scene was loaded. + uint gPRNGDimension; // First available PRNG dimension. } // Inputs @@ -88,28 +89,31 @@ static const bool kUseEnvBackground = USE_ENV_BACKGROUND; static const float3 kDefaultBackgroundColor = float3(0, 0, 0); static const float kRayTMax = FLT_MAX; -/** Payload for shadow ray. -*/ +/** + * Payload for shadow ray. + */ struct ShadowRayData { bool visible; }; -/** Payload for scatter ray (up to 72B). -*/ +/** + * Payload for scatter ray (up to 72B). + */ struct ScatterRayData { - float3 radiance; ///< Accumulated outgoing radiance from path. - bool terminated; ///< Set to true when path is terminated. - float3 thp; ///< Current path throughput. This is updated at each path vertex. - uint pathLength; ///< Path length in number of path segments (0 at origin, 1 at first secondary hit, etc.). Max 2^31. - float3 origin; ///< Next path segment origin. - float3 direction; ///< Next path segment direction. - - SampleGenerator sg; ///< Per-ray state for the sample generator (up to 16B). - - /** Initializes ray payload with default parameters. - */ + float3 radiance; ///< Accumulated outgoing radiance from path. + bool terminated; ///< Set to true when path is terminated. + float3 thp; ///< Current path throughput. This is updated at each path vertex. + uint pathLength; ///< Path length in number of path segments (0 at origin, 1 at first secondary hit, etc.). Max 2^31. + float3 origin; ///< Next path segment origin. + float3 direction; ///< Next path segment direction. + + SampleGenerator sg; ///< Per-ray state for the sample generator (up to 16B). + + /** + * Initializes ray payload with default parameters. + */ __init(SampleGenerator sg) { this.terminated = false; @@ -122,13 +126,14 @@ struct ScatterRayData } }; -/** Setup ShadingData based on loaded vertex/material attributes for a hit point. - \param[in] hit Hit information. - \param[in] rayOrigin Ray origin. - \param[in] rayDir Normalized ray direction. - \param[in] lod Method for computing texture level-of-detail. - \return ShadingData struct. -*/ +/** + * Setup ShadingData based on loaded vertex/material attributes for a hit point. + * @param[in] hit Hit information. + * @param[in] rayOrigin Ray origin. + * @param[in] rayDir Normalized ray direction. + * @param[in] lod Method for computing texture level-of-detail. + * @return ShadingData struct. + */ ShadingData loadShadingData(const HitInfo hit, const float3 rayOrigin, const float3 rayDir, const ITextureSampler lod) { VertexData v = {}; @@ -172,8 +177,9 @@ ShadingData loadShadingData(const HitInfo hit, const float3 rayOrigin, const flo return sd; } -/** Returns the primary ray's direction. -*/ +/** + * Returns the primary ray's direction. + */ float3 getPrimaryRayDir(uint2 launchIndex, uint2 launchDim, const Camera camera) { if (is_valid(gViewW)) @@ -190,12 +196,13 @@ float3 getPrimaryRayDir(uint2 launchIndex, uint2 launchDim, const Camera camera) } } -/** Traces a shadow ray towards a light source. - \param[in] origin Ray origin for the shadow ray. - \param[in] dir Direction from shading point towards the light source (normalized). - \param[in] distance Distance to the light source. - \return True if light is visible, false otherwise. -*/ +/** + * Traces a shadow ray towards a light source. + * @param[in] origin Ray origin for the shadow ray. + * @param[in] dir Direction from shading point towards the light source (normalized). + * @param[in] distance Distance to the light source. + * @return True if light is visible, false otherwise. + */ bool traceShadowRay(float3 origin, float3 dir, float distance) { RayDesc ray; @@ -205,15 +212,25 @@ bool traceShadowRay(float3 origin, float3 dir, float distance) ray.TMax = distance; ShadowRayData rayData; - rayData.visible = false; // Set to true by miss shader if ray is not terminated before - TraceRay(gScene.rtAccel, RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, 0xff /* instanceInclusionMask */, 1 /* hitIdx */, rayTypeCount, 1 /* missIdx */, ray, rayData); + rayData.visible = false; // Set to true by miss shader if ray is not terminated before + TraceRay( + gScene.rtAccel, + RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, + 0xff /* instanceInclusionMask */, + 1 /* hitIdx */, + rayTypeCount, + 1 /* missIdx */, + ray, + rayData + ); return rayData.visible; } -/** Traces a scatter ray based on ray parameters stored in the ray payload. - \param[in] rayData Describes the ray parameters. The struct is modified based on the result. -*/ +/** + * Traces a scatter ray based on ray parameters stored in the ray payload. + * @param[in] rayData Describes the ray parameters. The struct is modified based on the result. + */ void traceScatterRay(inout ScatterRayData rayData) { RayDesc ray; @@ -222,21 +239,23 @@ void traceScatterRay(inout ScatterRayData rayData) ray.TMin = 0.f; ray.TMax = kRayTMax; - uint rayFlags = 0; // TODO: Set cull mode from the app + uint rayFlags = 0; // TODO: Set cull mode from the app TraceRay(gScene.rtAccel, rayFlags, 0xff /* instanceInclusionMask */, 0 /* hitIdx */, rayTypeCount, 0 /* missIdx */, ray, rayData); } -/** Evaluates the direct illumination from analytic lights. - This function samples Falcor's light list uniformly with one shadow ray. - \param[in] sd Shading data. - \param[in] mi Material instance. - \param[in,out] sg SampleGenerator object. - \return Outgoing radiance in view direction. -*/ +/** + * Evaluates the direct illumination from analytic lights. + * This function samples Falcor's light list uniformly with one shadow ray. + * @param[in] sd Shading data. + * @param[in] mi Material instance. + * @param[in,out] sg SampleGenerator object. + * @return Outgoing radiance in view direction. + */ float3 evalDirectAnalytic(const ShadingData sd, const IMaterialInstance mi, inout SampleGenerator sg) { const uint lightCount = gScene.getLightCount(); - if (lightCount == 0) return float3(0.f); + if (lightCount == 0) + return float3(0.f); // Pick one of the analytic light sources randomly with equal probability. const uint lightIndex = min(uint(sampleNext1D(sg) * lightCount), lightCount - 1); @@ -244,7 +263,8 @@ float3 evalDirectAnalytic(const ShadingData sd, const IMaterialInstance mi, inou // Sample local light source. AnalyticLightSample ls; - if (!sampleLight(sd.posW, gScene.getLight(lightIndex), sg, ls)) return float3(0.f); + if (!sampleLight(sd.posW, gScene.getLight(lightIndex), sg, ls)) + return float3(0.f); // Reject sample if not in the hemisphere of a BSDF lobe. const uint lobeTypes = mi.getLobeTypes(sd); @@ -259,20 +279,22 @@ float3 evalDirectAnalytic(const ShadingData sd, const IMaterialInstance mi, inou // Test visibility by tracing a shadow ray. bool V = traceShadowRay(origin, ls.dir, ls.distance); - if (!V) return float3(0.f); + if (!V) + return float3(0.f); // Evaluate contribution. return mi.eval(sd, ls.dir, sg) * ls.Li * invPdf; } -/** Generate a new scatter ray or terminate. - \param[in] sd Shading data. - \param[in] mi Material instance. - \param[in] isCurveHit True if on curve hit. - \param[in] rayOrigin Ray origin for the new ray. - \param[in,out] rayData Ray payload. - \return True if the path continues. -*/ +/** + * Generate a new scatter ray or terminate. + * @param[in] sd Shading data. + * @param[in] mi Material instance. + * @param[in] isCurveHit True if on curve hit. + * @param[in] rayOrigin Ray origin for the new ray. + * @param[in,out] rayData Ray payload. + * @return True if the path continues. + */ bool generateScatterRay(const ShadingData sd, const IMaterialInstance mi, bool isCurveHit, float3 rayOrigin, inout ScatterRayData rayData) { // Sample material. @@ -282,7 +304,7 @@ bool generateScatterRay(const ShadingData sd, const IMaterialInstance mi, bool i rayData.origin = rayOrigin; if (!isCurveHit && bsdfSample.isLobe(LobeType::Transmission)) { - rayData.origin = sd.computeNewRayOrigin(false); + rayData.origin = sd.computeRayOrigin(false); } rayData.direction = bsdfSample.wo; rayData.thp *= bsdfSample.weight; @@ -292,13 +314,14 @@ bool generateScatterRay(const ShadingData sd, const IMaterialInstance mi, bool i return false; } -/** Process a hit. - Loads the shading data, samples analytic lights and samples a new scatter ray. - Terminates the path if maximum number of bounces is reached. - \param[in] hit Hit info. - \param[in,out] rayData Ray payload. - -*/ +/** + * Process a hit. + * Loads the shading data, samples analytic lights and samples a new scatter ray. + * Terminates the path if maximum number of bounces is reached. + * @param[in] hit Hit info. + * @param[in,out] rayData Ray payload. + * + */ void handleHit(const HitInfo hit, inout ScatterRayData rayData) { const bool isCurveHit = hit.getType() == HitType::Curve; @@ -332,7 +355,7 @@ void handleHit(const HitInfo hit, inout ScatterRayData rayData) } else { - rayOrigin = sd.computeNewRayOrigin(); + rayOrigin = sd.computeRayOrigin(); } // Add contribution of direct light from analytic lights. @@ -353,19 +376,20 @@ void handleHit(const HitInfo hit, inout ScatterRayData rayData) rayData.pathLength++; } -/** This is the main entry point for the minimal path tracer. - - One path per pixel is generated, which is traced into the scene. - The path tracer is written as a for-loop over path segments. - - Built-in light sources (point, directional) are sampled explicitly at each - path vertex. The contributions from area lights (env map and mesh lights) - are explicitly added by the scatter ray hit/miss shaders. - - \param[in] pixel Pixel to trace a path for. - \param[in] frameDim Dimension of the frame in pixels. - \return Returns the estimated color (radiance). -*/ +/** + * This is the main entry point for the minimal path tracer. + * + * One path per pixel is generated, which is traced into the scene. + * The path tracer is written as a for-loop over path segments. + * + * Built-in light sources (point, directional) are sampled explicitly at each + * path vertex. The contributions from area lights (env map and mesh lights) + * are explicitly added by the scatter ray hit/miss shaders. + * + * @param[in] pixel Pixel to trace a path for. + * @param[in] frameDim Dimension of the frame in pixels. + * @return Returns the estimated color (radiance). + */ float3 tracePath(const uint2 pixel, const uint2 frameDim) { float3 outColor = float3(0.f); @@ -392,7 +416,8 @@ float3 tracePath(const uint2 pixel, const uint2 frameDim) // Advance the generator to the first available dimension. // TODO: This is potentially expensive. We may want to store/restore the state from memory if it becomes a problem. - for (uint i = 0; i < gPRNGDimension; i++) sampleNext1D(sg); + for (uint i = 0; i < gPRNGDimension; i++) + sampleNext1D(sg); // Compute ray origin for new rays spawned from the G-buffer. float3 rayOrigin; @@ -403,7 +428,7 @@ float3 tracePath(const uint2 pixel, const uint2 frameDim) } else { - rayOrigin = sd.computeNewRayOrigin(); + rayOrigin = sd.computeRayOrigin(); } if (kComputeDirect) @@ -420,7 +445,8 @@ float3 tracePath(const uint2 pixel, const uint2 frameDim) ScatterRayData rayData = ScatterRayData(sg); // Generate scatter ray. - if (!generateScatterRay(sd, mi, isCurveHit, rayOrigin, rayData)) rayData.terminated = true; + if (!generateScatterRay(sd, mi, isCurveHit, rayOrigin, rayData)) + rayData.terminated = true; // Follow path into the scene and compute its total contribution. for (uint depth = 0; depth <= kMaxBounces && !rayData.terminated; depth++) @@ -478,7 +504,8 @@ void scatterTriangleMeshAnyHit(inout ScatterRayData rayData, BuiltInTriangleInte GeometryInstanceID instanceID = getGeometryInstanceID(); VertexData v = getVertexData(instanceID, PrimitiveIndex(), attribs); const uint materialID = gScene.getMaterialID(instanceID); - if (gScene.materials.alphaTest(v, materialID, 0.f)) IgnoreHit(); + if (gScene.materials.alphaTest(v, materialID, 0.f)) + IgnoreHit(); } [shader("closesthit")] @@ -498,7 +525,8 @@ void shadowTriangleMeshAnyHit(inout ShadowRayData rayData, BuiltInTriangleInters GeometryInstanceID instanceID = getGeometryInstanceID(); VertexData v = getVertexData(instanceID, PrimitiveIndex(), attribs); const uint materialID = gScene.getMaterialID(instanceID); - if (gScene.materials.alphaTest(v, materialID, 0.f)) IgnoreHit(); + if (gScene.materials.alphaTest(v, materialID, 0.f)) + IgnoreHit(); } // diff --git a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cpp b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cpp index 7729b1b39..4eca22d27 100644 --- a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cpp +++ b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cpp @@ -30,68 +30,81 @@ namespace { - const std::string kShaderFile("RenderPasses/ModulateIllumination/ModulateIllumination.cs.slang"); - - const Falcor::ChannelList kInputChannels = - { - { "emission", "gEmission", "Emission", true /* optional */ }, - { "diffuseReflectance", "gDiffuseReflectance", "Diffuse Reflectance", true /* optional */ }, - { "diffuseRadiance", "gDiffuseRadiance", "Diffuse Radiance", true /* optional */ }, - { "specularReflectance", "gSpecularReflectance", "Specular Reflectance", true /* optional */ }, - { "specularRadiance", "gSpecularRadiance", "Specular Radiance", true /* optional */ }, - { "deltaReflectionEmission", "gDeltaReflectionEmission", "Delta Reflection Emission", true /* optional */ }, - { "deltaReflectionReflectance", "gDeltaReflectionReflectance", "Delta Reflection Reflectance", true /* optional */ }, - { "deltaReflectionRadiance", "gDeltaReflectionRadiance", "Delta Reflection Radiance", true /* optional */ }, - { "deltaTransmissionEmission", "gDeltaTransmissionEmission", "Delta Transmission Emission", true /* optional */ }, - { "deltaTransmissionReflectance", "gDeltaTransmissionReflectance", "Delta Transmission Reflectance", true /* optional */ }, - { "deltaTransmissionRadiance", "gDeltaTransmissionRadiance", "Delta Transmission Radiance", true /* optional */ }, - { "residualRadiance", "gResidualRadiance", "Residual Radiance", true /* optional */ }, - }; - - const std::string kOutput = "output"; - - // Serialized parameters. - const char kUseEmission[] = "useEmission"; - const char kUseDiffuseReflectance[] = "useDiffuseReflectance"; - const char kUseDiffuseRadiance[] = "useDiffuseRadiance"; - const char kUseSpecularReflectance[] = "useSpecularReflectance"; - const char kUseSpecularRadiance[] = "useSpecularRadiance"; - const char kUseDeltaReflectionEmission[] = "useDeltaReflectionEmission"; - const char kUseDeltaReflectionReflectance[] = "useDeltaReflectionReflectance"; - const char kUseDeltaReflectionRadiance[] = "useDeltaReflectionRadiance"; - const char kUseDeltaTransmissionEmission[] = "useDeltaTransmissionEmission"; - const char kUseDeltaTransmissionReflectance[] = "useDeltaTransmissionReflectance"; - const char kUseDeltaTransmissionRadiance[] = "useDeltaTransmissionRadiance"; - const char kUseResidualRadiance[] = "useResidualRadiance"; - const char kOutputSize[] = "outputSize"; -} +const std::string kShaderFile("RenderPasses/ModulateIllumination/ModulateIllumination.cs.slang"); + +const Falcor::ChannelList kInputChannels = { + // clang-format off + { "emission", "gEmission", "Emission", true /* optional */ }, + { "diffuseReflectance", "gDiffuseReflectance", "Diffuse Reflectance", true /* optional */ }, + { "diffuseRadiance", "gDiffuseRadiance", "Diffuse Radiance", true /* optional */ }, + { "specularReflectance", "gSpecularReflectance", "Specular Reflectance", true /* optional */ }, + { "specularRadiance", "gSpecularRadiance", "Specular Radiance", true /* optional */ }, + { "deltaReflectionEmission", "gDeltaReflectionEmission", "Delta Reflection Emission", true /* optional */ }, + { "deltaReflectionReflectance", "gDeltaReflectionReflectance", "Delta Reflection Reflectance", true /* optional */ }, + { "deltaReflectionRadiance", "gDeltaReflectionRadiance", "Delta Reflection Radiance", true /* optional */ }, + { "deltaTransmissionEmission", "gDeltaTransmissionEmission", "Delta Transmission Emission", true /* optional */ }, + { "deltaTransmissionReflectance", "gDeltaTransmissionReflectance", "Delta Transmission Reflectance", true /* optional */ }, + { "deltaTransmissionRadiance", "gDeltaTransmissionRadiance", "Delta Transmission Radiance", true /* optional */ }, + { "residualRadiance", "gResidualRadiance", "Residual Radiance", true /* optional */ }, + // clang-format on +}; + +const std::string kOutput = "output"; + +// Serialized parameters. +const char kUseEmission[] = "useEmission"; +const char kUseDiffuseReflectance[] = "useDiffuseReflectance"; +const char kUseDiffuseRadiance[] = "useDiffuseRadiance"; +const char kUseSpecularReflectance[] = "useSpecularReflectance"; +const char kUseSpecularRadiance[] = "useSpecularRadiance"; +const char kUseDeltaReflectionEmission[] = "useDeltaReflectionEmission"; +const char kUseDeltaReflectionReflectance[] = "useDeltaReflectionReflectance"; +const char kUseDeltaReflectionRadiance[] = "useDeltaReflectionRadiance"; +const char kUseDeltaTransmissionEmission[] = "useDeltaTransmissionEmission"; +const char kUseDeltaTransmissionReflectance[] = "useDeltaTransmissionReflectance"; +const char kUseDeltaTransmissionRadiance[] = "useDeltaTransmissionRadiance"; +const char kUseResidualRadiance[] = "useResidualRadiance"; +const char kOutputSize[] = "outputSize"; +} // namespace extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { registry.registerClass(); } -ModulateIllumination::ModulateIllumination(ref pDevice, const Properties& props) - : RenderPass(pDevice) +ModulateIllumination::ModulateIllumination(ref pDevice, const Properties& props) : RenderPass(pDevice) { mpModulateIlluminationPass = ComputePass::create(mpDevice, kShaderFile, "main", DefineList(), false); // Deserialize pass from dictionary. for (const auto& [key, value] : props) { - if (key == kUseEmission) mUseEmission = value; - else if (key == kUseDiffuseReflectance) mUseDiffuseReflectance = value; - else if (key == kUseDiffuseRadiance) mUseDiffuseRadiance = value; - else if (key == kUseSpecularReflectance) mUseSpecularReflectance = value; - else if (key == kUseSpecularRadiance) mUseSpecularRadiance = value; - else if (key == kUseDeltaReflectionEmission) mUseDeltaReflectionEmission = value; - else if (key == kUseDeltaReflectionReflectance) mUseDeltaReflectionReflectance = value; - else if (key == kUseDeltaReflectionRadiance) mUseDeltaReflectionRadiance = value; - else if (key == kUseDeltaTransmissionEmission) mUseDeltaTransmissionEmission = value; - else if (key == kUseDeltaTransmissionReflectance) mUseDeltaTransmissionReflectance = value; - else if (key == kUseDeltaTransmissionRadiance) mUseDeltaTransmissionRadiance = value; - else if (key == kUseResidualRadiance) mUseResidualRadiance = value; - else if (key == kOutputSize) mOutputSizeSelection = value; + if (key == kUseEmission) + mUseEmission = value; + else if (key == kUseDiffuseReflectance) + mUseDiffuseReflectance = value; + else if (key == kUseDiffuseRadiance) + mUseDiffuseRadiance = value; + else if (key == kUseSpecularReflectance) + mUseSpecularReflectance = value; + else if (key == kUseSpecularRadiance) + mUseSpecularRadiance = value; + else if (key == kUseDeltaReflectionEmission) + mUseDeltaReflectionEmission = value; + else if (key == kUseDeltaReflectionReflectance) + mUseDeltaReflectionReflectance = value; + else if (key == kUseDeltaReflectionRadiance) + mUseDeltaReflectionRadiance = value; + else if (key == kUseDeltaTransmissionEmission) + mUseDeltaTransmissionEmission = value; + else if (key == kUseDeltaTransmissionReflectance) + mUseDeltaTransmissionReflectance = value; + else if (key == kUseDeltaTransmissionRadiance) + mUseDeltaTransmissionRadiance = value; + else if (key == kUseResidualRadiance) + mUseResidualRadiance = value; + else if (key == kOutputSize) + mOutputSizeSelection = value; else { logWarning("Unknown property '{}' in ModulateIllumination properties.", key); @@ -126,36 +139,49 @@ RenderPassReflection ModulateIllumination::reflect(const CompileData& compileDat const uint2 sz = RenderPassHelpers::calculateIOSize(mOutputSizeSelection, mFrameDim, compileData.defaultTexDims); // TODO: Allow user to specify output format - reflector.addOutput(kOutput, "output").bindFlags(ResourceBindFlags::UnorderedAccess).format(ResourceFormat::RGBA32Float).texture2D(sz.x, sz.y); + reflector.addOutput(kOutput, "output") + .bindFlags(ResourceBindFlags::UnorderedAccess) + .format(ResourceFormat::RGBA32Float) + .texture2D(sz.x, sz.y); return reflector; } -void ModulateIllumination::compile(RenderContext* pRenderContext, const CompileData& compileData) -{ -} +void ModulateIllumination::compile(RenderContext* pRenderContext, const CompileData& compileData) {} void ModulateIllumination::execute(RenderContext* pRenderContext, const RenderData& renderData) { const auto& pOutput = renderData.getTexture(kOutput); - mFrameDim = { pOutput->getWidth(), pOutput->getHeight() }; + mFrameDim = {pOutput->getWidth(), pOutput->getHeight()}; // For optional I/O resources, set 'is_valid_' defines to inform the program of which ones it can access. // TODO: This should be moved to a more general mechanism using Slang. DefineList defineList = getValidResourceDefines(kInputChannels, renderData); // Override defines. - if (!mUseEmission) defineList["is_valid_gEmission"] = "0"; - if (!mUseDiffuseReflectance) defineList["is_valid_gDiffuseReflectance"] = "0"; - if (!mUseDiffuseRadiance) defineList["is_valid_gDiffuseRadiance"] = "0"; - if (!mUseSpecularReflectance) defineList["is_valid_gSpecularReflectance"] = "0"; - if (!mUseSpecularRadiance) defineList["is_valid_gSpecularRadiance"] = "0"; - if (!mUseDeltaReflectionEmission) defineList["is_valid_gDeltaReflectionEmission"] = "0"; - if (!mUseDeltaReflectionReflectance) defineList["is_valid_gDeltaReflectionReflectance"] = "0"; - if (!mUseDeltaReflectionRadiance) defineList["is_valid_gDeltaReflectionRadiance"] = "0"; - if (!mUseDeltaTransmissionEmission) defineList["is_valid_gDeltaTransmissionEmission"] = "0"; - if (!mUseDeltaTransmissionReflectance) defineList["is_valid_gDeltaTransmissionReflectance"] = "0"; - if (!mUseDeltaTransmissionRadiance) defineList["is_valid_gDeltaTransmissionRadiance"] = "0"; - if (!mUseResidualRadiance) defineList["is_valid_gResidualRadiance"] = "0"; + if (!mUseEmission) + defineList["is_valid_gEmission"] = "0"; + if (!mUseDiffuseReflectance) + defineList["is_valid_gDiffuseReflectance"] = "0"; + if (!mUseDiffuseRadiance) + defineList["is_valid_gDiffuseRadiance"] = "0"; + if (!mUseSpecularReflectance) + defineList["is_valid_gSpecularReflectance"] = "0"; + if (!mUseSpecularRadiance) + defineList["is_valid_gSpecularRadiance"] = "0"; + if (!mUseDeltaReflectionEmission) + defineList["is_valid_gDeltaReflectionEmission"] = "0"; + if (!mUseDeltaReflectionReflectance) + defineList["is_valid_gDeltaReflectionReflectance"] = "0"; + if (!mUseDeltaReflectionRadiance) + defineList["is_valid_gDeltaReflectionRadiance"] = "0"; + if (!mUseDeltaTransmissionEmission) + defineList["is_valid_gDeltaTransmissionEmission"] = "0"; + if (!mUseDeltaTransmissionReflectance) + defineList["is_valid_gDeltaTransmissionReflectance"] = "0"; + if (!mUseDeltaTransmissionRadiance) + defineList["is_valid_gDeltaTransmissionRadiance"] = "0"; + if (!mUseResidualRadiance) + defineList["is_valid_gResidualRadiance"] = "0"; if (mpModulateIlluminationPass->getProgram()->addDefines(defineList)) { @@ -172,13 +198,20 @@ void ModulateIllumination::execute(RenderContext* pRenderContext, const RenderDa ref pTexture = renderData.getTexture(desc.name); if (pTexture && (mFrameDim.x != pTexture->getWidth() || mFrameDim.y != pTexture->getHeight())) { - logError("Texture {} has dim {}x{}, not compatible with the FrameDim {}x{}.", - pTexture->getName(), pTexture->getWidth(), pTexture->getHeight(), mFrameDim.x, mFrameDim.y); + logError( + "Texture {} has dim {}x{}, not compatible with the FrameDim {}x{}.", + pTexture->getName(), + pTexture->getWidth(), + pTexture->getHeight(), + mFrameDim.x, + mFrameDim.y + ); } var[desc.texname] = pTexture; } }; - for (const auto& channel : kInputChannels) bind(channel); + for (const auto& channel : kInputChannels) + bind(channel); var["gOutput"] = renderData.getTexture(kOutput); diff --git a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cs.slang b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cs.slang index e95ee80d5..6fae5016c 100644 --- a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cs.slang +++ b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -50,10 +50,11 @@ Texture2D gResidualRadiance; RWTexture2D gOutput; [numthreads(16, 16, 1)] -void main(uint3 dispatchThreadId : SV_DispatchThreadID) +void main(uint3 dispatchThreadId: SV_DispatchThreadID) { const uint2 pixel = dispatchThreadId.xy; - if (any(pixel >= frameDim)) return; + if (any(pixel >= frameDim)) + return; float4 outputColor = float4(0.f); diff --git a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.h b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.h index 80817c640..98ad1b377 100644 --- a/Source/RenderPasses/ModulateIllumination/ModulateIllumination.h +++ b/Source/RenderPasses/ModulateIllumination/ModulateIllumination.h @@ -37,7 +37,10 @@ class ModulateIllumination : public RenderPass public: FALCOR_PLUGIN_CLASS(ModulateIllumination, "ModulateIllumination", "Modulate illumination pass."); - static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } + static ref create(ref pDevice, const Properties& props) + { + return make_ref(pDevice, props); + } ModulateIllumination(ref pDevice, const Properties& props); @@ -48,21 +51,21 @@ class ModulateIllumination : public RenderPass virtual void renderUI(Gui::Widgets& widget) override; private: - uint2 mFrameDim = { 0, 0 }; - RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. + uint2 mFrameDim = {0, 0}; + RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; - ref mpModulateIlluminationPass; + ref mpModulateIlluminationPass; - bool mUseEmission = true; - bool mUseDiffuseReflectance = true; - bool mUseDiffuseRadiance = true; - bool mUseSpecularReflectance = true; - bool mUseSpecularRadiance = true; - bool mUseDeltaReflectionEmission = true; - bool mUseDeltaReflectionReflectance = true; - bool mUseDeltaReflectionRadiance = true; - bool mUseDeltaTransmissionEmission = true; - bool mUseDeltaTransmissionReflectance = true; - bool mUseDeltaTransmissionRadiance = true; - bool mUseResidualRadiance = true; + bool mUseEmission = true; + bool mUseDiffuseReflectance = true; + bool mUseDiffuseRadiance = true; + bool mUseSpecularReflectance = true; + bool mUseSpecularRadiance = true; + bool mUseDeltaReflectionEmission = true; + bool mUseDeltaReflectionReflectance = true; + bool mUseDeltaReflectionRadiance = true; + bool mUseDeltaTransmissionEmission = true; + bool mUseDeltaTransmissionReflectance = true; + bool mUseDeltaTransmissionRadiance = true; + bool mUseResidualRadiance = true; }; diff --git a/Source/RenderPasses/NRDPass/NRDPass.cpp b/Source/RenderPasses/NRDPass/NRDPass.cpp index 3df92dd62..595841901 100644 --- a/Source/RenderPasses/NRDPass/NRDPass.cpp +++ b/Source/RenderPasses/NRDPass/NRDPass.cpp @@ -33,81 +33,79 @@ namespace { - const char kShaderPackRadiance[] = "RenderPasses/NRDPass/PackRadiance.cs.slang"; - - // Input buffer names. - const char kInputDiffuseRadianceHitDist[] = "diffuseRadianceHitDist"; - const char kInputSpecularRadianceHitDist[] = "specularRadianceHitDist"; - const char kInputSpecularHitDist[] = "specularHitDist"; - const char kInputMotionVectors[] = "mvec"; - const char kInputNormalRoughnessMaterialID[] = "normWRoughnessMaterialID"; - const char kInputViewZ[] = "viewZ"; - const char kInputDeltaPrimaryPosW[] = "deltaPrimaryPosW"; - const char kInputDeltaSecondaryPosW[] = "deltaSecondaryPosW"; - - // Output buffer names. - const char kOutputFilteredDiffuseRadianceHitDist[] = "filteredDiffuseRadianceHitDist"; - const char kOutputFilteredSpecularRadianceHitDist[] = "filteredSpecularRadianceHitDist"; - const char kOutputReflectionMotionVectors[] = "reflectionMvec"; - const char kOutputDeltaMotionVectors[] = "deltaMvec"; - - // Serialized parameters. - - const char kEnabled[] = "enabled"; - const char kMethod[] = "method"; - const char kOutputSize[] = "outputSize"; - - // Common settings. - const char kWorldSpaceMotion[] = "worldSpaceMotion"; - const char kDisocclusionThreshold[] = "disocclusionThreshold"; - - // Pack radiance settings. - const char kMaxIntensity[] = "maxIntensity"; - - // ReLAX diffuse/specular settings. - const char kDiffusePrepassBlurRadius[] = "diffusePrepassBlurRadius"; - const char kSpecularPrepassBlurRadius[] = "specularPrepassBlurRadius"; - const char kDiffuseMaxAccumulatedFrameNum[] = "diffuseMaxAccumulatedFrameNum"; - const char kSpecularMaxAccumulatedFrameNum[] = "specularMaxAccumulatedFrameNum"; - const char kDiffuseMaxFastAccumulatedFrameNum[] = "diffuseMaxFastAccumulatedFrameNum"; - const char kSpecularMaxFastAccumulatedFrameNum[] = "specularMaxFastAccumulatedFrameNum"; - const char kDiffusePhiLuminance[] = "diffusePhiLuminance"; - const char kSpecularPhiLuminance[] = "specularPhiLuminance"; - const char kDiffuseLobeAngleFraction[] = "diffuseLobeAngleFraction"; - const char kSpecularLobeAngleFraction[] = "specularLobeAngleFraction"; - const char kRoughnessFraction[] = "roughnessFraction"; - const char kDiffuseHistoryRejectionNormalThreshold[] = "diffuseHistoryRejectionNormalThreshold"; - const char kSpecularVarianceBoost[] = "specularVarianceBoost"; - const char kSpecularLobeAngleSlack[] = "specularLobeAngleSlack"; - const char kDisocclusionFixEdgeStoppingNormalPower[] = "disocclusionFixEdgeStoppingNormalPower"; - const char kDisocclusionFixMaxRadius[] = "disocclusionFixMaxRadius"; - const char kDisocclusionFixNumFramesToFix[] = "disocclusionFixNumFramesToFix"; - const char kHistoryClampingColorBoxSigmaScale[] = "historyClampingColorBoxSigmaScale"; - const char kSpatialVarianceEstimationHistoryThreshold[] = "spatialVarianceEstimationHistoryThreshold"; - const char kAtrousIterationNum[] = "atrousIterationNum"; - const char kMinLuminanceWeight[] = "minLuminanceWeight"; - const char kDepthThreshold[] = "depthThreshold"; - const char kRoughnessEdgeStoppingRelaxation[] = "roughnessEdgeStoppingRelaxation"; - const char kNormalEdgeStoppingRelaxation[] = "normalEdgeStoppingRelaxation"; - const char kLuminanceEdgeStoppingRelaxation[] = "luminanceEdgeStoppingRelaxation"; - const char kEnableAntiFirefly[] = "enableAntiFirefly"; - const char kEnableReprojectionTestSkippingWithoutMotion[] = "enableReprojectionTestSkippingWithoutMotion"; - const char kEnableSpecularVirtualHistoryClamping[] = "enableSpecularVirtualHistoryClamping"; - const char kEnableRoughnessEdgeStopping[] = "enableRoughnessEdgeStopping"; - const char kEnableMaterialTestForDiffuse[] = "enableMaterialTestForDiffuse"; - const char kEnableMaterialTestForSpecular[] = "enableMaterialTestForSpecular"; - - // Expose only togglable methods. - // There is no reason to expose runtime toggle for other methods. - const Gui::DropdownList kDenoisingMethod = - { - { (uint32_t)NRDPass::DenoisingMethod::RelaxDiffuseSpecular, "ReLAX" }, - { (uint32_t)NRDPass::DenoisingMethod::ReblurDiffuseSpecular, "ReBLUR" }, - }; -} - -NRDPass::NRDPass(ref pDevice, const Properties& props) - : RenderPass(pDevice) +const char kShaderPackRadiance[] = "RenderPasses/NRDPass/PackRadiance.cs.slang"; + +// Input buffer names. +const char kInputDiffuseRadianceHitDist[] = "diffuseRadianceHitDist"; +const char kInputSpecularRadianceHitDist[] = "specularRadianceHitDist"; +const char kInputSpecularHitDist[] = "specularHitDist"; +const char kInputMotionVectors[] = "mvec"; +const char kInputNormalRoughnessMaterialID[] = "normWRoughnessMaterialID"; +const char kInputViewZ[] = "viewZ"; +const char kInputDeltaPrimaryPosW[] = "deltaPrimaryPosW"; +const char kInputDeltaSecondaryPosW[] = "deltaSecondaryPosW"; + +// Output buffer names. +const char kOutputFilteredDiffuseRadianceHitDist[] = "filteredDiffuseRadianceHitDist"; +const char kOutputFilteredSpecularRadianceHitDist[] = "filteredSpecularRadianceHitDist"; +const char kOutputReflectionMotionVectors[] = "reflectionMvec"; +const char kOutputDeltaMotionVectors[] = "deltaMvec"; + +// Serialized parameters. + +const char kEnabled[] = "enabled"; +const char kMethod[] = "method"; +const char kOutputSize[] = "outputSize"; + +// Common settings. +const char kWorldSpaceMotion[] = "worldSpaceMotion"; +const char kDisocclusionThreshold[] = "disocclusionThreshold"; + +// Pack radiance settings. +const char kMaxIntensity[] = "maxIntensity"; + +// ReLAX diffuse/specular settings. +const char kDiffusePrepassBlurRadius[] = "diffusePrepassBlurRadius"; +const char kSpecularPrepassBlurRadius[] = "specularPrepassBlurRadius"; +const char kDiffuseMaxAccumulatedFrameNum[] = "diffuseMaxAccumulatedFrameNum"; +const char kSpecularMaxAccumulatedFrameNum[] = "specularMaxAccumulatedFrameNum"; +const char kDiffuseMaxFastAccumulatedFrameNum[] = "diffuseMaxFastAccumulatedFrameNum"; +const char kSpecularMaxFastAccumulatedFrameNum[] = "specularMaxFastAccumulatedFrameNum"; +const char kDiffusePhiLuminance[] = "diffusePhiLuminance"; +const char kSpecularPhiLuminance[] = "specularPhiLuminance"; +const char kDiffuseLobeAngleFraction[] = "diffuseLobeAngleFraction"; +const char kSpecularLobeAngleFraction[] = "specularLobeAngleFraction"; +const char kRoughnessFraction[] = "roughnessFraction"; +const char kDiffuseHistoryRejectionNormalThreshold[] = "diffuseHistoryRejectionNormalThreshold"; +const char kSpecularVarianceBoost[] = "specularVarianceBoost"; +const char kSpecularLobeAngleSlack[] = "specularLobeAngleSlack"; +const char kDisocclusionFixEdgeStoppingNormalPower[] = "disocclusionFixEdgeStoppingNormalPower"; +const char kDisocclusionFixMaxRadius[] = "disocclusionFixMaxRadius"; +const char kDisocclusionFixNumFramesToFix[] = "disocclusionFixNumFramesToFix"; +const char kHistoryClampingColorBoxSigmaScale[] = "historyClampingColorBoxSigmaScale"; +const char kSpatialVarianceEstimationHistoryThreshold[] = "spatialVarianceEstimationHistoryThreshold"; +const char kAtrousIterationNum[] = "atrousIterationNum"; +const char kMinLuminanceWeight[] = "minLuminanceWeight"; +const char kDepthThreshold[] = "depthThreshold"; +const char kRoughnessEdgeStoppingRelaxation[] = "roughnessEdgeStoppingRelaxation"; +const char kNormalEdgeStoppingRelaxation[] = "normalEdgeStoppingRelaxation"; +const char kLuminanceEdgeStoppingRelaxation[] = "luminanceEdgeStoppingRelaxation"; +const char kEnableAntiFirefly[] = "enableAntiFirefly"; +const char kEnableReprojectionTestSkippingWithoutMotion[] = "enableReprojectionTestSkippingWithoutMotion"; +const char kEnableSpecularVirtualHistoryClamping[] = "enableSpecularVirtualHistoryClamping"; +const char kEnableRoughnessEdgeStopping[] = "enableRoughnessEdgeStopping"; +const char kEnableMaterialTestForDiffuse[] = "enableMaterialTestForDiffuse"; +const char kEnableMaterialTestForSpecular[] = "enableMaterialTestForSpecular"; + +// Expose only togglable methods. +// There is no reason to expose runtime toggle for other methods. +const Gui::DropdownList kDenoisingMethod = { + {(uint32_t)NRDPass::DenoisingMethod::RelaxDiffuseSpecular, "ReLAX"}, + {(uint32_t)NRDPass::DenoisingMethod::ReblurDiffuseSpecular, "ReBLUR"}, +}; +} // namespace + +NRDPass::NRDPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { mpDevice->requireD3D12(); @@ -151,51 +149,88 @@ NRDPass::NRDPass(ref pDevice, const Properties& props) // Deserialize pass from dictionary. for (const auto& [key, value] : props) { - if (key == kEnabled) mEnabled = value; - else if (key == kMethod) mDenoisingMethod = value; - else if (key == kOutputSize) mOutputSizeSelection = value; + if (key == kEnabled) + mEnabled = value; + else if (key == kMethod) + mDenoisingMethod = value; + else if (key == kOutputSize) + mOutputSizeSelection = value; // Common settings. - else if (key == kWorldSpaceMotion) mWorldSpaceMotion = value; - else if (key == kDisocclusionThreshold) mDisocclusionThreshold = value; + else if (key == kWorldSpaceMotion) + mWorldSpaceMotion = value; + else if (key == kDisocclusionThreshold) + mDisocclusionThreshold = value; // Pack radiance settings. - else if (key == kMaxIntensity) mMaxIntensity = value; + else if (key == kMaxIntensity) + mMaxIntensity = value; // ReLAX diffuse/specular settings. else if (mDenoisingMethod == DenoisingMethod::RelaxDiffuseSpecular || mDenoisingMethod == DenoisingMethod::ReblurDiffuseSpecular) { - if (key == kDiffusePrepassBlurRadius) mRelaxDiffuseSpecularSettings.diffusePrepassBlurRadius = value; - else if (key == kSpecularPrepassBlurRadius) mRelaxDiffuseSpecularSettings.specularPrepassBlurRadius = value; - else if (key == kDiffuseMaxAccumulatedFrameNum) mRelaxDiffuseSpecularSettings.diffuseMaxAccumulatedFrameNum = value; - else if (key == kSpecularMaxAccumulatedFrameNum) mRelaxDiffuseSpecularSettings.specularMaxAccumulatedFrameNum = value; - else if (key == kDiffuseMaxFastAccumulatedFrameNum) mRelaxDiffuseSpecularSettings.diffuseMaxFastAccumulatedFrameNum = value; - else if (key == kSpecularMaxFastAccumulatedFrameNum) mRelaxDiffuseSpecularSettings.specularMaxFastAccumulatedFrameNum = value; - else if (key == kDiffusePhiLuminance) mRelaxDiffuseSpecularSettings.diffusePhiLuminance = value; - else if (key == kSpecularPhiLuminance) mRelaxDiffuseSpecularSettings.specularPhiLuminance = value; - else if (key == kDiffuseLobeAngleFraction) mRelaxDiffuseSpecularSettings.diffuseLobeAngleFraction = value; - else if (key == kSpecularLobeAngleFraction) mRelaxDiffuseSpecularSettings.specularLobeAngleFraction = value; - else if (key == kRoughnessFraction) mRelaxDiffuseSpecularSettings.roughnessFraction = value; - else if (key == kDiffuseHistoryRejectionNormalThreshold) mRelaxDiffuseSpecularSettings.diffuseHistoryRejectionNormalThreshold = value; - else if (key == kSpecularVarianceBoost) mRelaxDiffuseSpecularSettings.specularVarianceBoost = value; - else if (key == kSpecularLobeAngleSlack) mRelaxDiffuseSpecularSettings.specularLobeAngleSlack = value; - else if (key == kDisocclusionFixEdgeStoppingNormalPower) mRelaxDiffuseSpecularSettings.disocclusionFixEdgeStoppingNormalPower = value; - else if (key == kDisocclusionFixMaxRadius) mRelaxDiffuseSpecularSettings.disocclusionFixMaxRadius = value; - else if (key == kDisocclusionFixNumFramesToFix) mRelaxDiffuseSpecularSettings.disocclusionFixNumFramesToFix = value; - else if (key == kHistoryClampingColorBoxSigmaScale) mRelaxDiffuseSpecularSettings.historyClampingColorBoxSigmaScale = value; - else if (key == kSpatialVarianceEstimationHistoryThreshold) mRelaxDiffuseSpecularSettings.spatialVarianceEstimationHistoryThreshold = value; - else if (key == kAtrousIterationNum) mRelaxDiffuseSpecularSettings.atrousIterationNum = value; - else if (key == kMinLuminanceWeight) mRelaxDiffuseSpecularSettings.minLuminanceWeight = value; - else if (key == kDepthThreshold) mRelaxDiffuseSpecularSettings.depthThreshold = value; - else if (key == kLuminanceEdgeStoppingRelaxation) mRelaxDiffuseSpecularSettings.luminanceEdgeStoppingRelaxation = value; - else if (key == kNormalEdgeStoppingRelaxation) mRelaxDiffuseSpecularSettings.normalEdgeStoppingRelaxation = value; - else if (key == kRoughnessEdgeStoppingRelaxation) mRelaxDiffuseSpecularSettings.roughnessEdgeStoppingRelaxation = value; - else if (key == kEnableAntiFirefly) mRelaxDiffuseSpecularSettings.enableAntiFirefly = value; - else if (key == kEnableReprojectionTestSkippingWithoutMotion) mRelaxDiffuseSpecularSettings.enableReprojectionTestSkippingWithoutMotion = value; - else if (key == kEnableSpecularVirtualHistoryClamping) mRelaxDiffuseSpecularSettings.enableSpecularVirtualHistoryClamping = value; - else if (key == kEnableRoughnessEdgeStopping) mRelaxDiffuseSpecularSettings.enableRoughnessEdgeStopping = value; - else if (key == kEnableMaterialTestForDiffuse) mRelaxDiffuseSpecularSettings.enableMaterialTestForDiffuse = value; - else if (key == kEnableMaterialTestForSpecular) mRelaxDiffuseSpecularSettings.enableMaterialTestForSpecular = value; + if (key == kDiffusePrepassBlurRadius) + mRelaxDiffuseSpecularSettings.diffusePrepassBlurRadius = value; + else if (key == kSpecularPrepassBlurRadius) + mRelaxDiffuseSpecularSettings.specularPrepassBlurRadius = value; + else if (key == kDiffuseMaxAccumulatedFrameNum) + mRelaxDiffuseSpecularSettings.diffuseMaxAccumulatedFrameNum = value; + else if (key == kSpecularMaxAccumulatedFrameNum) + mRelaxDiffuseSpecularSettings.specularMaxAccumulatedFrameNum = value; + else if (key == kDiffuseMaxFastAccumulatedFrameNum) + mRelaxDiffuseSpecularSettings.diffuseMaxFastAccumulatedFrameNum = value; + else if (key == kSpecularMaxFastAccumulatedFrameNum) + mRelaxDiffuseSpecularSettings.specularMaxFastAccumulatedFrameNum = value; + else if (key == kDiffusePhiLuminance) + mRelaxDiffuseSpecularSettings.diffusePhiLuminance = value; + else if (key == kSpecularPhiLuminance) + mRelaxDiffuseSpecularSettings.specularPhiLuminance = value; + else if (key == kDiffuseLobeAngleFraction) + mRelaxDiffuseSpecularSettings.diffuseLobeAngleFraction = value; + else if (key == kSpecularLobeAngleFraction) + mRelaxDiffuseSpecularSettings.specularLobeAngleFraction = value; + else if (key == kRoughnessFraction) + mRelaxDiffuseSpecularSettings.roughnessFraction = value; + else if (key == kDiffuseHistoryRejectionNormalThreshold) + mRelaxDiffuseSpecularSettings.diffuseHistoryRejectionNormalThreshold = value; + else if (key == kSpecularVarianceBoost) + mRelaxDiffuseSpecularSettings.specularVarianceBoost = value; + else if (key == kSpecularLobeAngleSlack) + mRelaxDiffuseSpecularSettings.specularLobeAngleSlack = value; + else if (key == kDisocclusionFixEdgeStoppingNormalPower) + mRelaxDiffuseSpecularSettings.disocclusionFixEdgeStoppingNormalPower = value; + else if (key == kDisocclusionFixMaxRadius) + mRelaxDiffuseSpecularSettings.disocclusionFixMaxRadius = value; + else if (key == kDisocclusionFixNumFramesToFix) + mRelaxDiffuseSpecularSettings.disocclusionFixNumFramesToFix = value; + else if (key == kHistoryClampingColorBoxSigmaScale) + mRelaxDiffuseSpecularSettings.historyClampingColorBoxSigmaScale = value; + else if (key == kSpatialVarianceEstimationHistoryThreshold) + mRelaxDiffuseSpecularSettings.spatialVarianceEstimationHistoryThreshold = value; + else if (key == kAtrousIterationNum) + mRelaxDiffuseSpecularSettings.atrousIterationNum = value; + else if (key == kMinLuminanceWeight) + mRelaxDiffuseSpecularSettings.minLuminanceWeight = value; + else if (key == kDepthThreshold) + mRelaxDiffuseSpecularSettings.depthThreshold = value; + else if (key == kLuminanceEdgeStoppingRelaxation) + mRelaxDiffuseSpecularSettings.luminanceEdgeStoppingRelaxation = value; + else if (key == kNormalEdgeStoppingRelaxation) + mRelaxDiffuseSpecularSettings.normalEdgeStoppingRelaxation = value; + else if (key == kRoughnessEdgeStoppingRelaxation) + mRelaxDiffuseSpecularSettings.roughnessEdgeStoppingRelaxation = value; + else if (key == kEnableAntiFirefly) + mRelaxDiffuseSpecularSettings.enableAntiFirefly = value; + else if (key == kEnableReprojectionTestSkippingWithoutMotion) + mRelaxDiffuseSpecularSettings.enableReprojectionTestSkippingWithoutMotion = value; + else if (key == kEnableSpecularVirtualHistoryClamping) + mRelaxDiffuseSpecularSettings.enableSpecularVirtualHistoryClamping = value; + else if (key == kEnableRoughnessEdgeStopping) + mRelaxDiffuseSpecularSettings.enableRoughnessEdgeStopping = value; + else if (key == kEnableMaterialTestForDiffuse) + mRelaxDiffuseSpecularSettings.enableMaterialTestForDiffuse = value; + else if (key == kEnableMaterialTestForSpecular) + mRelaxDiffuseSpecularSettings.enableMaterialTestForSpecular = value; else { logWarning("Unknown property '{}' in NRD properties.", key); @@ -203,23 +238,40 @@ NRDPass::NRDPass(ref pDevice, const Properties& props) } else if (mDenoisingMethod == DenoisingMethod::RelaxDiffuse) { - if (key == kDiffusePrepassBlurRadius) mRelaxDiffuseSettings.prepassBlurRadius = value; - else if (key == kDiffuseMaxAccumulatedFrameNum) mRelaxDiffuseSettings.diffuseMaxAccumulatedFrameNum = value; - else if (key == kDiffuseMaxFastAccumulatedFrameNum) mRelaxDiffuseSettings.diffuseMaxFastAccumulatedFrameNum = value; - else if (key == kDiffusePhiLuminance) mRelaxDiffuseSettings.diffusePhiLuminance = value; - else if (key == kDiffuseLobeAngleFraction) mRelaxDiffuseSettings.diffuseLobeAngleFraction = value; - else if (key == kDiffuseHistoryRejectionNormalThreshold) mRelaxDiffuseSettings.diffuseHistoryRejectionNormalThreshold = value; - else if (key == kDisocclusionFixEdgeStoppingNormalPower) mRelaxDiffuseSettings.disocclusionFixEdgeStoppingNormalPower = value; - else if (key == kDisocclusionFixMaxRadius) mRelaxDiffuseSettings.disocclusionFixMaxRadius = value; - else if (key == kDisocclusionFixNumFramesToFix) mRelaxDiffuseSettings.disocclusionFixNumFramesToFix = value; - else if (key == kHistoryClampingColorBoxSigmaScale) mRelaxDiffuseSettings.historyClampingColorBoxSigmaScale = value; - else if (key == kSpatialVarianceEstimationHistoryThreshold) mRelaxDiffuseSettings.spatialVarianceEstimationHistoryThreshold = value; - else if (key == kAtrousIterationNum) mRelaxDiffuseSettings.atrousIterationNum = value; - else if (key == kMinLuminanceWeight) mRelaxDiffuseSettings.minLuminanceWeight = value; - else if (key == kDepthThreshold) mRelaxDiffuseSettings.depthThreshold = value; - else if (key == kEnableAntiFirefly) mRelaxDiffuseSettings.enableAntiFirefly = value; - else if (key == kEnableReprojectionTestSkippingWithoutMotion) mRelaxDiffuseSettings.enableReprojectionTestSkippingWithoutMotion = value; - else if (key == kEnableMaterialTestForDiffuse) mRelaxDiffuseSettings.enableMaterialTest = value; + if (key == kDiffusePrepassBlurRadius) + mRelaxDiffuseSettings.prepassBlurRadius = value; + else if (key == kDiffuseMaxAccumulatedFrameNum) + mRelaxDiffuseSettings.diffuseMaxAccumulatedFrameNum = value; + else if (key == kDiffuseMaxFastAccumulatedFrameNum) + mRelaxDiffuseSettings.diffuseMaxFastAccumulatedFrameNum = value; + else if (key == kDiffusePhiLuminance) + mRelaxDiffuseSettings.diffusePhiLuminance = value; + else if (key == kDiffuseLobeAngleFraction) + mRelaxDiffuseSettings.diffuseLobeAngleFraction = value; + else if (key == kDiffuseHistoryRejectionNormalThreshold) + mRelaxDiffuseSettings.diffuseHistoryRejectionNormalThreshold = value; + else if (key == kDisocclusionFixEdgeStoppingNormalPower) + mRelaxDiffuseSettings.disocclusionFixEdgeStoppingNormalPower = value; + else if (key == kDisocclusionFixMaxRadius) + mRelaxDiffuseSettings.disocclusionFixMaxRadius = value; + else if (key == kDisocclusionFixNumFramesToFix) + mRelaxDiffuseSettings.disocclusionFixNumFramesToFix = value; + else if (key == kHistoryClampingColorBoxSigmaScale) + mRelaxDiffuseSettings.historyClampingColorBoxSigmaScale = value; + else if (key == kSpatialVarianceEstimationHistoryThreshold) + mRelaxDiffuseSettings.spatialVarianceEstimationHistoryThreshold = value; + else if (key == kAtrousIterationNum) + mRelaxDiffuseSettings.atrousIterationNum = value; + else if (key == kMinLuminanceWeight) + mRelaxDiffuseSettings.minLuminanceWeight = value; + else if (key == kDepthThreshold) + mRelaxDiffuseSettings.depthThreshold = value; + else if (key == kEnableAntiFirefly) + mRelaxDiffuseSettings.enableAntiFirefly = value; + else if (key == kEnableReprojectionTestSkippingWithoutMotion) + mRelaxDiffuseSettings.enableReprojectionTestSkippingWithoutMotion = value; + else if (key == kEnableMaterialTestForDiffuse) + mRelaxDiffuseSettings.enableMaterialTest = value; else { logWarning("Unknown property '{}' in NRD properties.", key); @@ -320,8 +372,12 @@ RenderPassReflection NRDPass::reflect(const CompileData& compileData) reflector.addInput(kInputNormalRoughnessMaterialID, "World normal, roughness, and material ID"); reflector.addInput(kInputMotionVectors, "Motion vectors"); - reflector.addOutput(kOutputFilteredDiffuseRadianceHitDist, "Filtered diffuse radiance and hit distance").format(ResourceFormat::RGBA16Float).texture2D(sz.x, sz.y); - reflector.addOutput(kOutputFilteredSpecularRadianceHitDist, "Filtered specular radiance and hit distance").format(ResourceFormat::RGBA16Float).texture2D(sz.x, sz.y); + reflector.addOutput(kOutputFilteredDiffuseRadianceHitDist, "Filtered diffuse radiance and hit distance") + .format(ResourceFormat::RGBA16Float) + .texture2D(sz.x, sz.y); + reflector.addOutput(kOutputFilteredSpecularRadianceHitDist, "Filtered specular radiance and hit distance") + .format(ResourceFormat::RGBA16Float) + .texture2D(sz.x, sz.y); } else if (mDenoisingMethod == DenoisingMethod::RelaxDiffuse) { @@ -330,7 +386,9 @@ RenderPassReflection NRDPass::reflect(const CompileData& compileData) reflector.addInput(kInputNormalRoughnessMaterialID, "World normal, roughness, and material ID"); reflector.addInput(kInputMotionVectors, "Motion vectors"); - reflector.addOutput(kOutputFilteredDiffuseRadianceHitDist, "Filtered diffuse radiance and hit distance").format(ResourceFormat::RGBA16Float).texture2D(sz.x, sz.y); + reflector.addOutput(kOutputFilteredDiffuseRadianceHitDist, "Filtered diffuse radiance and hit distance") + .format(ResourceFormat::RGBA16Float) + .texture2D(sz.x, sz.y); } else if (mDenoisingMethod == DenoisingMethod::SpecularReflectionMv) { @@ -339,7 +397,9 @@ RenderPassReflection NRDPass::reflect(const CompileData& compileData) reflector.addInput(kInputNormalRoughnessMaterialID, "World normal, roughness, and material ID"); reflector.addInput(kInputMotionVectors, "Motion vectors"); - reflector.addOutput(kOutputReflectionMotionVectors, "Reflection motion vectors in screen space").format(ResourceFormat::RG16Float).texture2D(sz.x, sz.y); + reflector.addOutput(kOutputReflectionMotionVectors, "Reflection motion vectors in screen space") + .format(ResourceFormat::RG16Float) + .texture2D(sz.x, sz.y); } else if (mDenoisingMethod == DenoisingMethod::SpecularDeltaMv) { @@ -347,7 +407,9 @@ RenderPassReflection NRDPass::reflect(const CompileData& compileData) reflector.addInput(kInputDeltaSecondaryPosW, "Delta secondary world position"); reflector.addInput(kInputMotionVectors, "Motion vectors"); - reflector.addOutput(kOutputDeltaMotionVectors, "Delta motion vectors in screen space").format(ResourceFormat::RG16Float).texture2D(sz.x, sz.y); + reflector.addOutput(kOutputDeltaMotionVectors, "Delta motion vectors in screen space") + .format(ResourceFormat::RG16Float) + .texture2D(sz.x, sz.y); } else { @@ -368,7 +430,8 @@ void NRDPass::compile(RenderContext* pRenderContext, const CompileData& compileD void NRDPass::execute(RenderContext* pRenderContext, const RenderData& renderData) { - if (!mpScene) return; + if (!mpScene) + return; bool enabled = false; enabled = mEnabled; @@ -381,12 +444,21 @@ void NRDPass::execute(RenderContext* pRenderContext, const RenderData& renderDat { if (mDenoisingMethod == DenoisingMethod::RelaxDiffuseSpecular || mDenoisingMethod == DenoisingMethod::ReblurDiffuseSpecular) { - pRenderContext->blit(renderData.getTexture(kInputDiffuseRadianceHitDist)->getSRV(), renderData.getTexture(kOutputFilteredDiffuseRadianceHitDist)->getRTV()); - pRenderContext->blit(renderData.getTexture(kInputSpecularRadianceHitDist)->getSRV(), renderData.getTexture(kOutputFilteredSpecularRadianceHitDist)->getRTV()); + pRenderContext->blit( + renderData.getTexture(kInputDiffuseRadianceHitDist)->getSRV(), + renderData.getTexture(kOutputFilteredDiffuseRadianceHitDist)->getRTV() + ); + pRenderContext->blit( + renderData.getTexture(kInputSpecularRadianceHitDist)->getSRV(), + renderData.getTexture(kOutputFilteredSpecularRadianceHitDist)->getRTV() + ); } else if (mDenoisingMethod == DenoisingMethod::RelaxDiffuse) { - pRenderContext->blit(renderData.getTexture(kInputDiffuseRadianceHitDist)->getSRV(), renderData.getTexture(kOutputFilteredDiffuseRadianceHitDist)->getRTV()); + pRenderContext->blit( + renderData.getTexture(kInputDiffuseRadianceHitDist)->getSRV(), + renderData.getTexture(kOutputFilteredDiffuseRadianceHitDist)->getRTV() + ); } else if (mDenoisingMethod == DenoisingMethod::SpecularReflectionMv) { @@ -396,7 +468,9 @@ void NRDPass::execute(RenderContext* pRenderContext, const RenderData& renderDat } else { - pRenderContext->blit(renderData.getTexture(kInputMotionVectors)->getSRV(), renderData.getTexture(kOutputReflectionMotionVectors)->getRTV()); + pRenderContext->blit( + renderData.getTexture(kInputMotionVectors)->getSRV(), renderData.getTexture(kOutputReflectionMotionVectors)->getRTV() + ); } } else if (mDenoisingMethod == DenoisingMethod::SpecularDeltaMv) @@ -407,7 +481,9 @@ void NRDPass::execute(RenderContext* pRenderContext, const RenderData& renderDat } else { - pRenderContext->blit(renderData.getTexture(kInputMotionVectors)->getSRV(), renderData.getTexture(kOutputDeltaMotionVectors)->getRTV()); + pRenderContext->blit( + renderData.getTexture(kInputMotionVectors)->getSRV(), renderData.getTexture(kOutputDeltaMotionVectors)->getRTV() + ); } } } @@ -439,6 +515,7 @@ void NRDPass::renderUI(Gui::Widgets& widget) // ReLAX diffuse/specular settings. if (auto group = widget.group("ReLAX Diffuse/Specular")) { + // clang-format off group.text("Prepass:"); group.slider("Specular blur radius", mRelaxDiffuseSpecularSettings.specularPrepassBlurRadius, 0.0f, 100.0f, false, "%.0f"); group.slider("Diffuse blur radius", mRelaxDiffuseSpecularSettings.diffusePrepassBlurRadius, 0.0f, 100.0f, false, "%.0f"); @@ -475,6 +552,7 @@ void NRDPass::renderUI(Gui::Widgets& widget) group.slider("Normal relaxation", mRelaxDiffuseSpecularSettings.normalEdgeStoppingRelaxation, 0.0f, 1.0f, false, "%.2f"); group.slider("Luminance relaxation", mRelaxDiffuseSpecularSettings.luminanceEdgeStoppingRelaxation, 0.0f, 1.0f, false, "%.2f"); group.checkbox("Roughness edge stopping", mRelaxDiffuseSpecularSettings.enableRoughnessEdgeStopping); + // clang-format on } } else if (mDenoisingMethod == DenoisingMethod::RelaxDiffuse) @@ -489,6 +567,7 @@ void NRDPass::renderUI(Gui::Widgets& widget) // ReLAX diffuse settings. if (auto group = widget.group("ReLAX Diffuse")) { + // clang-format off group.text("Prepass:"); group.slider("Diffuse blur radius", mRelaxDiffuseSettings.prepassBlurRadius, 0.0f, 100.0f, false, "%.0f"); group.text("Reprojection:"); @@ -512,6 +591,7 @@ void NRDPass::renderUI(Gui::Widgets& widget) group.slider("Min luminance weight", mRelaxDiffuseSettings.minLuminanceWeight, 0.0f, 1.0f, false, "%.2f"); group.slider("Depth weight (relative fraction)", mRelaxDiffuseSettings.depthThreshold, 0.0f, 0.05f, false, "%.2f"); group.slider("Diffuse lobe angle fraction", mRelaxDiffuseSettings.diffuseLobeAngleFraction, 0.0f, 2.0f, false, "%.1f"); + // clang-format on } } else if (mDenoisingMethod == DenoisingMethod::ReblurDiffuseSpecular) @@ -525,6 +605,7 @@ void NRDPass::renderUI(Gui::Widgets& widget) if (auto group = widget.group("ReBLUR Diffuse/Specular")) { + // clang-format off const float kEpsilon = 0.0001f; if (auto group2 = group.group("Specular lobe trimming")) { @@ -576,6 +657,7 @@ void NRDPass::renderUI(Gui::Widgets& widget) group.checkbox("Performance mode", mReblurSettings.enablePerformanceMode); group.checkbox("Material test for diffuse", mReblurSettings.enableMaterialTestForDiffuse); group.checkbox("Material test for specular", mReblurSettings.enableMaterialTestForSpecular); + // clang-format on } } else if (mDenoisingMethod == DenoisingMethod::SpecularReflectionMv) @@ -612,52 +694,96 @@ static ResourceFormat getFalcorFormat(nrd::Format format) { switch (format) { - case nrd::Format::R8_UNORM: return ResourceFormat::R8Unorm; - case nrd::Format::R8_SNORM: return ResourceFormat::R8Snorm; - case nrd::Format::R8_UINT: return ResourceFormat::R8Uint; - case nrd::Format::R8_SINT: return ResourceFormat::R8Int; - case nrd::Format::RG8_UNORM: return ResourceFormat::RG8Unorm; - case nrd::Format::RG8_SNORM: return ResourceFormat::RG8Snorm; - case nrd::Format::RG8_UINT: return ResourceFormat::RG8Uint; - case nrd::Format::RG8_SINT: return ResourceFormat::RG8Int; - case nrd::Format::RGBA8_UNORM: return ResourceFormat::RGBA8Unorm; - case nrd::Format::RGBA8_SNORM: return ResourceFormat::RGBA8Snorm; - case nrd::Format::RGBA8_UINT: return ResourceFormat::RGBA8Uint; - case nrd::Format::RGBA8_SINT: return ResourceFormat::RGBA8Int; - case nrd::Format::RGBA8_SRGB: return ResourceFormat::RGBA8UnormSrgb; - case nrd::Format::R16_UNORM: return ResourceFormat::R16Unorm; - case nrd::Format::R16_SNORM: return ResourceFormat::R16Snorm; - case nrd::Format::R16_UINT: return ResourceFormat::R16Uint; - case nrd::Format::R16_SINT: return ResourceFormat::R16Int; - case nrd::Format::R16_SFLOAT: return ResourceFormat::R16Float; - case nrd::Format::RG16_UNORM: return ResourceFormat::RG16Unorm; - case nrd::Format::RG16_SNORM: return ResourceFormat::RG16Snorm; - case nrd::Format::RG16_UINT: return ResourceFormat::RG16Uint; - case nrd::Format::RG16_SINT: return ResourceFormat::RG16Int; - case nrd::Format::RG16_SFLOAT: return ResourceFormat::RG16Float; - case nrd::Format::RGBA16_UNORM: return ResourceFormat::RGBA16Unorm; - case nrd::Format::RGBA16_SNORM: return ResourceFormat::Unknown; // Not defined in Falcor - case nrd::Format::RGBA16_UINT: return ResourceFormat::RGBA16Uint; - case nrd::Format::RGBA16_SINT: return ResourceFormat::RGBA16Int; - case nrd::Format::RGBA16_SFLOAT: return ResourceFormat::RGBA16Float; - case nrd::Format::R32_UINT: return ResourceFormat::R32Uint; - case nrd::Format::R32_SINT: return ResourceFormat::R32Int; - case nrd::Format::R32_SFLOAT: return ResourceFormat::R32Float; - case nrd::Format::RG32_UINT: return ResourceFormat::RG32Uint; - case nrd::Format::RG32_SINT: return ResourceFormat::RG32Int; - case nrd::Format::RG32_SFLOAT: return ResourceFormat::RG32Float; - case nrd::Format::RGB32_UINT: return ResourceFormat::RGB32Uint; - case nrd::Format::RGB32_SINT: return ResourceFormat::RGB32Int; - case nrd::Format::RGB32_SFLOAT: return ResourceFormat::RGB32Float; - case nrd::Format::RGBA32_UINT: return ResourceFormat::RGBA32Uint; - case nrd::Format::RGBA32_SINT: return ResourceFormat::RGBA32Int; - case nrd::Format::RGBA32_SFLOAT: return ResourceFormat::RGBA32Float; - case nrd::Format::R10_G10_B10_A2_UNORM: return ResourceFormat::RGB10A2Unorm; - case nrd::Format::R10_G10_B10_A2_UINT: return ResourceFormat::RGB10A2Uint; - case nrd::Format::R11_G11_B10_UFLOAT: return ResourceFormat::R11G11B10Float; - case nrd::Format::R9_G9_B9_E5_UFLOAT: return ResourceFormat::RGB9E5Float; + case nrd::Format::R8_UNORM: + return ResourceFormat::R8Unorm; + case nrd::Format::R8_SNORM: + return ResourceFormat::R8Snorm; + case nrd::Format::R8_UINT: + return ResourceFormat::R8Uint; + case nrd::Format::R8_SINT: + return ResourceFormat::R8Int; + case nrd::Format::RG8_UNORM: + return ResourceFormat::RG8Unorm; + case nrd::Format::RG8_SNORM: + return ResourceFormat::RG8Snorm; + case nrd::Format::RG8_UINT: + return ResourceFormat::RG8Uint; + case nrd::Format::RG8_SINT: + return ResourceFormat::RG8Int; + case nrd::Format::RGBA8_UNORM: + return ResourceFormat::RGBA8Unorm; + case nrd::Format::RGBA8_SNORM: + return ResourceFormat::RGBA8Snorm; + case nrd::Format::RGBA8_UINT: + return ResourceFormat::RGBA8Uint; + case nrd::Format::RGBA8_SINT: + return ResourceFormat::RGBA8Int; + case nrd::Format::RGBA8_SRGB: + return ResourceFormat::RGBA8UnormSrgb; + case nrd::Format::R16_UNORM: + return ResourceFormat::R16Unorm; + case nrd::Format::R16_SNORM: + return ResourceFormat::R16Snorm; + case nrd::Format::R16_UINT: + return ResourceFormat::R16Uint; + case nrd::Format::R16_SINT: + return ResourceFormat::R16Int; + case nrd::Format::R16_SFLOAT: + return ResourceFormat::R16Float; + case nrd::Format::RG16_UNORM: + return ResourceFormat::RG16Unorm; + case nrd::Format::RG16_SNORM: + return ResourceFormat::RG16Snorm; + case nrd::Format::RG16_UINT: + return ResourceFormat::RG16Uint; + case nrd::Format::RG16_SINT: + return ResourceFormat::RG16Int; + case nrd::Format::RG16_SFLOAT: + return ResourceFormat::RG16Float; + case nrd::Format::RGBA16_UNORM: + return ResourceFormat::RGBA16Unorm; + case nrd::Format::RGBA16_SNORM: + return ResourceFormat::Unknown; // Not defined in Falcor + case nrd::Format::RGBA16_UINT: + return ResourceFormat::RGBA16Uint; + case nrd::Format::RGBA16_SINT: + return ResourceFormat::RGBA16Int; + case nrd::Format::RGBA16_SFLOAT: + return ResourceFormat::RGBA16Float; + case nrd::Format::R32_UINT: + return ResourceFormat::R32Uint; + case nrd::Format::R32_SINT: + return ResourceFormat::R32Int; + case nrd::Format::R32_SFLOAT: + return ResourceFormat::R32Float; + case nrd::Format::RG32_UINT: + return ResourceFormat::RG32Uint; + case nrd::Format::RG32_SINT: + return ResourceFormat::RG32Int; + case nrd::Format::RG32_SFLOAT: + return ResourceFormat::RG32Float; + case nrd::Format::RGB32_UINT: + return ResourceFormat::RGB32Uint; + case nrd::Format::RGB32_SINT: + return ResourceFormat::RGB32Int; + case nrd::Format::RGB32_SFLOAT: + return ResourceFormat::RGB32Float; + case nrd::Format::RGBA32_UINT: + return ResourceFormat::RGBA32Uint; + case nrd::Format::RGBA32_SINT: + return ResourceFormat::RGBA32Int; + case nrd::Format::RGBA32_SFLOAT: + return ResourceFormat::RGBA32Float; + case nrd::Format::R10_G10_B10_A2_UNORM: + return ResourceFormat::RGB10A2Unorm; + case nrd::Format::R10_G10_B10_A2_UINT: + return ResourceFormat::RGB10A2Uint; + case nrd::Format::R11_G11_B10_UFLOAT: + return ResourceFormat::R11G11B10Float; + case nrd::Format::R9_G9_B9_E5_UFLOAT: + return ResourceFormat::RGB9E5Float; default: - throw RuntimeError("Unsupported NRD format."); + FALCOR_THROW("Unsupported NRD format."); } } @@ -665,11 +791,16 @@ static nrd::Method getNrdMethod(NRDPass::DenoisingMethod denoisingMethod) { switch (denoisingMethod) { - case NRDPass::DenoisingMethod::RelaxDiffuseSpecular: return nrd::Method::RELAX_DIFFUSE_SPECULAR; - case NRDPass::DenoisingMethod::RelaxDiffuse: return nrd::Method::RELAX_DIFFUSE; - case NRDPass::DenoisingMethod::ReblurDiffuseSpecular: return nrd::Method::REBLUR_DIFFUSE_SPECULAR; - case NRDPass::DenoisingMethod::SpecularReflectionMv: return nrd::Method::SPECULAR_REFLECTION_MV; - case NRDPass::DenoisingMethod::SpecularDeltaMv: return nrd::Method::SPECULAR_DELTA_MV; + case NRDPass::DenoisingMethod::RelaxDiffuseSpecular: + return nrd::Method::RELAX_DIFFUSE_SPECULAR; + case NRDPass::DenoisingMethod::RelaxDiffuse: + return nrd::Method::RELAX_DIFFUSE; + case NRDPass::DenoisingMethod::ReblurDiffuseSpecular: + return nrd::Method::REBLUR_DIFFUSE_SPECULAR; + case NRDPass::DenoisingMethod::SpecularReflectionMv: + return nrd::Method::SPECULAR_REFLECTION_MV; + case NRDPass::DenoisingMethod::SpecularDeltaMv: + return nrd::Method::SPECULAR_DELTA_MV; default: FALCOR_UNREACHABLE(); return nrd::Method::RELAX_DIFFUSE_SPECULAR; @@ -684,7 +815,6 @@ static void copyMatrix(float* dstMatrix, const float4x4& srcMatrix) memcpy(dstMatrix, static_cast(col_major.data()), sizeof(float4x4)); } - void NRDPass::reinit() { // Create a new denoiser instance. @@ -692,10 +822,7 @@ void NRDPass::reinit() const nrd::LibraryDesc& libraryDesc = nrd::GetLibraryDesc(); - const nrd::MethodDesc methods[] = - { - { getNrdMethod(mDenoisingMethod), uint16_t(mScreenSize.x), uint16_t(mScreenSize.y) } - }; + const nrd::MethodDesc methods[] = {{getNrdMethod(mDenoisingMethod), uint16_t(mScreenSize.x), uint16_t(mScreenSize.y)}}; nrd::DenoiserCreationDesc denoiserCreationDesc; denoiserCreationDesc.memoryAllocatorInterface.Allocate = nrdAllocate; @@ -706,7 +833,8 @@ void NRDPass::reinit() nrd::Result res = nrd::CreateDenoiser(denoiserCreationDesc, mpDenoiser); - if (res != nrd::Result::SUCCESS) throw RuntimeError("NRDPass: Failed to create NRD denoiser"); + if (res != nrd::Result::SUCCESS) + FALCOR_THROW("NRDPass: Failed to create NRD denoiser"); createResources(); createPipelines(); @@ -730,7 +858,8 @@ void NRDPass::createPipelines() { SamplersDescriptorSetLayout.addRange(ShaderResourceType::Sampler, denoiserDesc.staticSamplers[j].registerIndex, 1); } - mpSamplersDescriptorSet = D3D12DescriptorSet::create(mpDevice, SamplersDescriptorSetLayout, D3D12DescriptorSetBindingUsage::ExplicitBind); + mpSamplersDescriptorSet = + D3D12DescriptorSet::create(mpDevice, SamplersDescriptorSetLayout, D3D12DescriptorSetBindingUsage::ExplicitBind); // Set sampler descriptors right away. for (uint32_t j = 0; j < denoiserDesc.staticSamplerNum; j++) @@ -754,9 +883,9 @@ void NRDPass::createPipelines() { const nrd::DescriptorRangeDesc& nrdDescriptorRange = nrdPipelineDesc.descriptorRanges[j]; - ShaderResourceType descriptorType = nrdDescriptorRange.descriptorType == nrd::DescriptorType::TEXTURE ? - ShaderResourceType::TextureSrv : - ShaderResourceType::TextureUav; + ShaderResourceType descriptorType = nrdDescriptorRange.descriptorType == nrd::DescriptorType::TEXTURE + ? ShaderResourceType::TextureSrv + : ShaderResourceType::TextureUav; CBVSRVUAVdescriptorSetLayout.addRange(descriptorType, nrdDescriptorRange.baseRegisterIndex, nrdDescriptorRange.descriptorNum); } @@ -778,23 +907,25 @@ void NRDPass::createPipelines() { std::string shaderFileName = "nrd/Shaders/Source/" + std::string(nrdPipelineDesc.shaderFileName) + ".hlsl"; - Program::Desc programDesc; + ProgramDesc programDesc; programDesc.addShaderLibrary(shaderFileName).csEntry(nrdPipelineDesc.shaderEntryPointName); - programDesc.setCompilerFlags(Program::CompilerFlags::MatrixLayoutColumnMajor); + programDesc.setCompilerFlags(SlangCompilerFlags::MatrixLayoutColumnMajor); + // Disable warning 30056: non-short-circuiting `?:` operator is deprecated, use 'select' instead. + programDesc.setCompilerArguments({"-Wno-30056"}); DefineList defines; defines.add("NRD_COMPILER_DXC"); defines.add("NRD_USE_OCT_NORMAL_ENCODING", "1"); defines.add("NRD_USE_MATERIAL_ID", "0"); ref pPass = ComputePass::create(mpDevice, programDesc, defines); - ref pProgram = pPass->getProgram(); + ref pProgram = pPass->getProgram(); ref pProgramKernels = pProgram->getActiveVersion()->getKernels(mpDevice.get(), pPass->getVars().get()); - ComputeStateObject::Desc csoDesc; - csoDesc.setProgramKernels(pProgramKernels); - csoDesc.setD3D12RootSignatureOverride(pRootSig); + ComputeStateObjectDesc csoDesc; + csoDesc.pProgramKernels = pProgramKernels; + csoDesc.pD3D12RootSignatureOverride = pRootSig; - ref pCSO = ComputeStateObject::create(mpDevice, csoDesc); + ref pCSO = mpDevice->createComputeStateObject(csoDesc); mpPasses.push_back(pPass); mpCachedProgramKernels.push_back(pProgramKernels); @@ -809,7 +940,6 @@ void NRDPass::createResources() mpSamplers.clear(); mpPermanentTextures.clear(); mpTransientTextures.clear(); - mpConstantBuffer = nullptr; const nrd::DenoiserDesc& denoiserDesc = nrd::GetDenoiserDesc(*mpDenoiser); const uint32_t poolSize = denoiserDesc.permanentPoolSize + denoiserDesc.transientPoolSize; @@ -819,27 +949,27 @@ void NRDPass::createResources() { const nrd::StaticSamplerDesc& nrdStaticsampler = denoiserDesc.staticSamplers[i]; Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); + samplerDesc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Point); if (nrdStaticsampler.sampler == nrd::Sampler::NEAREST_CLAMP || nrdStaticsampler.sampler == nrd::Sampler::LINEAR_CLAMP) { - samplerDesc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); + samplerDesc.setAddressingMode(TextureAddressingMode::Clamp, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); } else { - samplerDesc.setAddressingMode(Sampler::AddressMode::Mirror, Sampler::AddressMode::Mirror, Sampler::AddressMode::Mirror); + samplerDesc.setAddressingMode(TextureAddressingMode::Mirror, TextureAddressingMode::Mirror, TextureAddressingMode::Mirror); } if (nrdStaticsampler.sampler == nrd::Sampler::NEAREST_CLAMP || nrdStaticsampler.sampler == nrd::Sampler::NEAREST_MIRRORED_REPEAT) { - samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); + samplerDesc.setFilterMode(TextureFilteringMode::Point, TextureFilteringMode::Point, TextureFilteringMode::Point); } else { - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); + samplerDesc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Point); } - mpSamplers.push_back(Sampler::create(mpDevice, samplerDesc)); + mpSamplers.push_back(mpDevice->createSampler(samplerDesc)); } // Texture pool. @@ -848,32 +978,26 @@ void NRDPass::createResources() const bool isPermanent = (i < denoiserDesc.permanentPoolSize); // Get texture desc. - const nrd::TextureDesc& nrdTextureDesc = isPermanent - ? denoiserDesc.permanentPool[i] - : denoiserDesc.transientPool[i - denoiserDesc.permanentPoolSize]; + const nrd::TextureDesc& nrdTextureDesc = + isPermanent ? denoiserDesc.permanentPool[i] : denoiserDesc.transientPool[i - denoiserDesc.permanentPoolSize]; // Create texture. ResourceFormat textureFormat = getFalcorFormat(nrdTextureDesc.format); - ref pTexture = Texture::create2D( - mpDevice, - nrdTextureDesc.width, nrdTextureDesc.height, - textureFormat, 1u, nrdTextureDesc.mipNum, + ref pTexture = mpDevice->createTexture2D( + nrdTextureDesc.width, + nrdTextureDesc.height, + textureFormat, + 1u, + nrdTextureDesc.mipNum, nullptr, - ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess + ); if (isPermanent) mpPermanentTextures.push_back(pTexture); else mpTransientTextures.push_back(pTexture); } - - // Constant buffer. - mpConstantBuffer = Buffer::create( - mpDevice, - denoiserDesc.constantBufferDesc.maxDataSize, - ResourceBindFlags::Constant, - Buffer::CpuAccess::Write, - nullptr); } void NRDPass::executeInternal(RenderContext* pRenderContext, const RenderData& renderData) @@ -987,7 +1111,7 @@ void NRDPass::executeInternal(RenderContext* pRenderContext, const RenderData& r } // Submit the existing command list and start a new one. - pRenderContext->flush(); + pRenderContext->submit(); } void NRDPass::dispatch(RenderContext* pRenderContext, const RenderData& renderData, const nrd::DispatchDesc& dispatchDesc) @@ -999,13 +1123,16 @@ void NRDPass::dispatch(RenderContext* pRenderContext, const RenderData& renderDa mpRootSignatures[dispatchDesc.pipelineIndex]->bindForCompute(pRenderContext); // Upload constants. - mpConstantBuffer->setBlob(dispatchDesc.constantBufferData, 0, dispatchDesc.constantBufferDataSize); + auto cbAllocation = mpDevice->getUploadHeap()->allocate(dispatchDesc.constantBufferDataSize, ResourceBindFlags::Constant); + std::memcpy(cbAllocation.pData, dispatchDesc.constantBufferData, dispatchDesc.constantBufferDataSize); // Create descriptor set for the NRD pass. - ref CBVSRVUAVDescriptorSet = D3D12DescriptorSet::create(mpDevice, mCBVSRVUAVdescriptorSetLayouts[dispatchDesc.pipelineIndex], D3D12DescriptorSetBindingUsage::ExplicitBind); + ref CBVSRVUAVDescriptorSet = D3D12DescriptorSet::create( + mpDevice, mCBVSRVUAVdescriptorSetLayouts[dispatchDesc.pipelineIndex], D3D12DescriptorSetBindingUsage::ExplicitBind + ); // Set CBV. - mpCBV = D3D12ConstantBufferView::create(mpDevice, mpConstantBuffer); + mpCBV = D3D12ConstantBufferView::create(mpDevice, cbAllocation.getGpuAddress(), cbAllocation.size); CBVSRVUAVDescriptorSet->setCbv(0 /* NB: range #0 is CBV range */, denoiserDesc.constantBufferDesc.registerIndex, mpCBV.get()); uint32_t resourceIndex = 0; @@ -1074,7 +1201,8 @@ void NRDPass::dispatch(RenderContext* pRenderContext, const RenderData& renderDa FALCOR_ASSERT(texture); // Set up resource barriers. - Resource::State newState = resource.stateNeeded == nrd::DescriptorType::TEXTURE ? Resource::State::ShaderResource : Resource::State::UnorderedAccess; + Resource::State newState = + resource.stateNeeded == nrd::DescriptorType::TEXTURE ? Resource::State::ShaderResource : Resource::State::UnorderedAccess; for (uint16_t mip = 0; mip < resource.mipNum; mip++) { const ResourceViewInfo viewInfo = ResourceViewInfo(resource.mipOffset + mip, 1, 0, 1); @@ -1085,12 +1213,20 @@ void NRDPass::dispatch(RenderContext* pRenderContext, const RenderData& renderDa if (nrdDescriptorRange.descriptorType == nrd::DescriptorType::TEXTURE) { ref pSRV = texture->getSRV(resource.mipOffset, resource.mipNum, 0, 1); - CBVSRVUAVDescriptorSet->setSrv(descriptorRangeIndex + 1 /* NB: range #0 is CBV range */, nrdDescriptorRange.baseRegisterIndex + descriptorOffset, pSRV.get()); + CBVSRVUAVDescriptorSet->setSrv( + descriptorRangeIndex + 1 /* NB: range #0 is CBV range */, + nrdDescriptorRange.baseRegisterIndex + descriptorOffset, + pSRV.get() + ); } else { ref pUAV = texture->getUAV(resource.mipOffset, 0, 1); - CBVSRVUAVDescriptorSet->setUav(descriptorRangeIndex + 1 /* NB: range #0 is CBV range */, nrdDescriptorRange.baseRegisterIndex + descriptorOffset, pUAV.get()); + CBVSRVUAVDescriptorSet->setUav( + descriptorRangeIndex + 1 /* NB: range #0 is CBV range */, + nrdDescriptorRange.baseRegisterIndex + descriptorOffset, + pUAV.get() + ); } resourceIndex++; @@ -1105,7 +1241,7 @@ void NRDPass::dispatch(RenderContext* pRenderContext, const RenderData& renderDa // Set pipeline state. ref pPass = mpPasses[dispatchDesc.pipelineIndex]; - ref pProgram = pPass->getProgram(); + ref pProgram = pPass->getProgram(); ref pProgramKernels = pProgram->getActiveVersion()->getKernels(mpDevice.get(), pPass->getVars().get()); // Check if anything changed. @@ -1114,20 +1250,23 @@ void NRDPass::dispatch(RenderContext* pRenderContext, const RenderData& renderDa { mpCachedProgramKernels[dispatchDesc.pipelineIndex] = pProgramKernels; - ComputeStateObject::Desc desc; - desc.setProgramKernels(pProgramKernels); - desc.setD3D12RootSignatureOverride(mpRootSignatures[dispatchDesc.pipelineIndex]); + ComputeStateObjectDesc desc; + desc.pProgramKernels = pProgramKernels; + desc.pD3D12RootSignatureOverride = mpRootSignatures[dispatchDesc.pipelineIndex]; - ref pCSO = ComputeStateObject::create(mpDevice, desc); + ref pCSO = mpDevice->createComputeStateObject(desc); mpCSOs[dispatchDesc.pipelineIndex] = pCSO; } - ID3D12GraphicsCommandList* pCommandList = pRenderContext->getLowLevelData()->getCommandBufferNativeHandle().as(); + ID3D12GraphicsCommandList* pCommandList = + pRenderContext->getLowLevelData()->getCommandBufferNativeHandle().as(); ID3D12PipelineState* pPipelineState = mpCSOs[dispatchDesc.pipelineIndex]->getNativeHandle().as(); pCommandList->SetPipelineState(pPipelineState); // Dispatch. pCommandList->Dispatch(dispatchDesc.gridWidth, dispatchDesc.gridHeight, 1); + + mpDevice->getUploadHeap()->release(cbAllocation); } extern "C" FALCOR_API_EXPORT void registerPlugin(PluginRegistry& registry) diff --git a/Source/RenderPasses/NRDPass/NRDPass.h b/Source/RenderPasses/NRDPass/NRDPass.h index 0b17db58c..7da8b08c3 100644 --- a/Source/RenderPasses/NRDPass/NRDPass.h +++ b/Source/RenderPasses/NRDPass/NRDPass.h @@ -52,13 +52,16 @@ class NRDPass : public RenderPass SpecularDeltaMv }; - FALCOR_ENUM_INFO(DenoisingMethod, { - { DenoisingMethod::RelaxDiffuseSpecular, "RelaxDiffuseSpecular" }, - { DenoisingMethod::RelaxDiffuse, "RelaxDiffuse" }, - { DenoisingMethod::ReblurDiffuseSpecular, "ReblurDiffuseSpecular" }, - { DenoisingMethod::SpecularReflectionMv, "SpecularReflectionMv" }, - { DenoisingMethod::SpecularDeltaMv, "SpecularDeltaMv" }, - }); + FALCOR_ENUM_INFO( + DenoisingMethod, + { + {DenoisingMethod::RelaxDiffuseSpecular, "RelaxDiffuseSpecular"}, + {DenoisingMethod::RelaxDiffuse, "RelaxDiffuse"}, + {DenoisingMethod::ReblurDiffuseSpecular, "ReblurDiffuseSpecular"}, + {DenoisingMethod::SpecularReflectionMv, "SpecularReflectionMv"}, + {DenoisingMethod::SpecularDeltaMv, "SpecularDeltaMv"}, + } + ); static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } @@ -75,7 +78,7 @@ class NRDPass : public RenderPass ref mpScene; uint2 mScreenSize{}; uint32_t mFrameIndex = 0; - RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. + RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; void reinit(); void createPipelines(); @@ -105,7 +108,6 @@ class NRDPass : public RenderPass std::vector> mpCSOs; std::vector> mpPermanentTextures; std::vector> mpTransientTextures; - ref mpConstantBuffer; ref mpCBV; float4x4 mPrevViewMatrix; diff --git a/Source/RenderPasses/NRDPass/PackRadiance.cs.slang b/Source/RenderPasses/NRDPass/PackRadiance.cs.slang index 803e89cb7..0ad1f84f6 100644 --- a/Source/RenderPasses/NRDPass/PackRadiance.cs.slang +++ b/Source/RenderPasses/NRDPass/PackRadiance.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -40,11 +40,11 @@ import Utils.Color.ColorHelpers; cbuffer PerImageCB { - float4 gHitDistParams; - float gMaxIntensity; + float4 gHitDistParams; + float gMaxIntensity; - Texture2D gViewZ; - Texture2D gNormalRoughness; + Texture2D gViewZ; + Texture2D gNormalRoughness; RWTexture2D gDiffuseRadianceHitDist; RWTexture2D gSpecularRadianceHitDist; @@ -68,7 +68,7 @@ void clampRadiance(inout float3 diffuseRadiance, inout float3 specularRadiance) } [numthreads(16, 16, 1)] -void main(uint3 dispatchThreadId : SV_DispatchThreadID) +void main(uint3 dispatchThreadId: SV_DispatchThreadID) { int2 ipos = dispatchThreadId.xy; diff --git a/Source/RenderPasses/OptixDenoiser/ConvertBufToTex.ps.slang b/Source/RenderPasses/OptixDenoiser/ConvertBufToTex.ps.slang index 427e3ab90..389546649 100644 --- a/Source/RenderPasses/OptixDenoiser/ConvertBufToTex.ps.slang +++ b/Source/RenderPasses/OptixDenoiser/ConvertBufToTex.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -30,11 +30,9 @@ cbuffer GlobalCB uint gStride; } -Buffer gInBuf; +Buffer gInBuf; -float4 main( - float2 texC : TEXCOORD, - float4 posH : SV_POSITION) : SV_Target0 +float4 main(float2 texC: TEXCOORD, float4 posH: SV_POSITION) : SV_Target0 { uint2 pixel = uint2(posH.xy); uint bufIdx = pixel.x + pixel.y * gStride; diff --git a/Source/RenderPasses/OptixDenoiser/ConvertMotionVectorInputs.cs.slang b/Source/RenderPasses/OptixDenoiser/ConvertMotionVectorInputs.cs.slang index 4778c9d6c..5575ba05e 100644 --- a/Source/RenderPasses/OptixDenoiser/ConvertMotionVectorInputs.cs.slang +++ b/Source/RenderPasses/OptixDenoiser/ConvertMotionVectorInputs.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -36,13 +36,13 @@ cbuffer GlobalCB uint2 gSize; } -Texture2D gInTex; -RWBuffer gOutBuf; +Texture2D gInTex; +RWBuffer gOutBuf; [numthreads(8, 8, 1)] -void main(uint3 thrdId : SV_DispatchThreadID) +void main(uint3 thrdId: SV_DispatchThreadID) { uint bufIdx = thrdId.x + thrdId.y * gStride; float2 optixMVec = gInTex[thrdId.xy].xy * gSize; - gOutBuf[bufIdx] = float2( -optixMVec.x, -optixMVec.y ); + gOutBuf[bufIdx] = float2(-optixMVec.x, -optixMVec.y); } diff --git a/Source/RenderPasses/OptixDenoiser/ConvertNormalsToBuf.cs.slang b/Source/RenderPasses/OptixDenoiser/ConvertNormalsToBuf.cs.slang index 588b19599..14b317054 100644 --- a/Source/RenderPasses/OptixDenoiser/ConvertNormalsToBuf.cs.slang +++ b/Source/RenderPasses/OptixDenoiser/ConvertNormalsToBuf.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -31,15 +31,15 @@ cbuffer GlobalCB float4x4 gViewIT; } -Texture2D gInTex; -RWBuffer gOutBuf; +Texture2D gInTex; +RWBuffer gOutBuf; [numthreads(8, 8, 1)] -void main(uint3 thrdId : SV_DispatchThreadID) +void main(uint3 thrdId: SV_DispatchThreadID) { uint bufIdx = thrdId.x + thrdId.y * gStride; - float3 normal = (gInTex[thrdId.xy].xyz - 0.5f)*2.0f; + float3 normal = (gInTex[thrdId.xy].xyz - 0.5f) * 2.0f; if (length(normal) > 0.01) { normal = mul(gViewIT, float4(normal, 0.0f)).xyz; diff --git a/Source/RenderPasses/OptixDenoiser/ConvertTexToBuf.cs.slang b/Source/RenderPasses/OptixDenoiser/ConvertTexToBuf.cs.slang index 6bdac0a36..1d7bf2f6e 100644 --- a/Source/RenderPasses/OptixDenoiser/ConvertTexToBuf.cs.slang +++ b/Source/RenderPasses/OptixDenoiser/ConvertTexToBuf.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -30,11 +30,11 @@ cbuffer GlobalCB uint gStride; } -Texture2D gInTex; -RWBuffer gOutBuf; +Texture2D gInTex; +RWBuffer gOutBuf; [numthreads(8, 8, 1)] -void main(uint3 thrdId : SV_DispatchThreadID) +void main(uint3 thrdId: SV_DispatchThreadID) { uint bufIdx = thrdId.x + thrdId.y * gStride; gOutBuf[bufIdx] = gInTex[thrdId.xy]; diff --git a/Source/RenderPasses/OptixDenoiser/OptixDenoiser.cpp b/Source/RenderPasses/OptixDenoiser/OptixDenoiser.cpp index 47cbdf03e..5dd2a86a8 100644 --- a/Source/RenderPasses/OptixDenoiser/OptixDenoiser.cpp +++ b/Source/RenderPasses/OptixDenoiser/OptixDenoiser.cpp @@ -27,35 +27,38 @@ **************************************************************************/ #include "OptixDenoiser.h" -FALCOR_ENUM_INFO(OptixDenoiserModelKind, { - { OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_LDR, "LDR" }, - { OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_HDR, "HDR" }, - { OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_AOV, "AOV" }, - { OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_TEMPORAL, "Temporal" }, -}); +FALCOR_ENUM_INFO( + OptixDenoiserModelKind, + { + {OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_LDR, "LDR"}, + {OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_HDR, "HDR"}, + {OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_AOV, "AOV"}, + {OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_TEMPORAL, "Temporal"}, + } +); FALCOR_ENUM_REGISTER(OptixDenoiserModelKind); namespace { - // Names for pass input and output textures - const char kColorInput[] = "color"; - const char kAlbedoInput[] = "albedo"; - const char kNormalInput[] = "normal"; - const char kMotionInput[] = "mvec"; - const char kOutput[] = "output"; - - // Names for configuration options available in Python - const char kEnabled[] = "enabled"; - const char kBlend[] = "blend"; - const char kModel[] = "model"; - const char kDenoiseAlpha[] = "denoiseAlpha"; - - // Locations of shaders used to (re-)format data as needed by OptiX - const std::string kConvertTexToBufFile = "RenderPasses/OptixDenoiser/ConvertTexToBuf.cs.slang"; - const std::string kConvertNormalsToBufFile = "RenderPasses/OptixDenoiser/ConvertNormalsToBuf.cs.slang"; - const std::string kConvertMotionVecFile = "RenderPasses/OptixDenoiser/ConvertMotionVectorInputs.cs.slang"; - const std::string kConvertBufToTexFile = "RenderPasses/OptixDenoiser/ConvertBufToTex.ps.slang"; -}; +// Names for pass input and output textures +const char kColorInput[] = "color"; +const char kAlbedoInput[] = "albedo"; +const char kNormalInput[] = "normal"; +const char kMotionInput[] = "mvec"; +const char kOutput[] = "output"; + +// Names for configuration options available in Python +const char kEnabled[] = "enabled"; +const char kBlend[] = "blend"; +const char kModel[] = "model"; +const char kDenoiseAlpha[] = "denoiseAlpha"; + +// Locations of shaders used to (re-)format data as needed by OptiX +const std::string kConvertTexToBufFile = "RenderPasses/OptixDenoiser/ConvertTexToBuf.cs.slang"; +const std::string kConvertNormalsToBufFile = "RenderPasses/OptixDenoiser/ConvertNormalsToBuf.cs.slang"; +const std::string kConvertMotionVecFile = "RenderPasses/OptixDenoiser/ConvertMotionVectorInputs.cs.slang"; +const std::string kConvertBufToTexFile = "RenderPasses/OptixDenoiser/ConvertBufToTex.ps.slang"; +}; // namespace static void regOptixDenoiser(pybind11::module& m) { @@ -69,20 +72,23 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regOptixDenoiser); } -OptixDenoiser_::OptixDenoiser_(ref pDevice, const Properties& props) - : RenderPass(pDevice) +OptixDenoiser_::OptixDenoiser_(ref pDevice, const Properties& props) : RenderPass(pDevice) { for (const auto& [key, value] : props) { - if (key == kEnabled) mEnabled = value; + if (key == kEnabled) + mEnabled = value; else if (key == kModel) { mDenoiser.modelKind = value; mSelectBestMode = false; } - else if (key == kBlend) mDenoiser.params.blendFactor = value; - else if (key == kDenoiseAlpha) mDenoiser.params.denoiseAlpha = (value ? 1u : 0u); - else logWarning("Unknown property '{}' in a OptixDenoiser properties.", key); + else if (key == kBlend) + mDenoiser.params.blendFactor = value; + else if (key == kDenoiseAlpha) + mDenoiser.params.denoiseAlpha = (value ? 1u : 0u); + else + logWarning("Unknown property '{}' in a OptixDenoiser properties.", key); } mpConvertTexToBuf = ComputePass::create(mpDevice, kConvertTexToBufFile, "main"); @@ -123,10 +129,9 @@ void OptixDenoiser_::setScene(RenderContext* pRenderContext, const ref& p void OptixDenoiser_::compile(RenderContext* pRenderContext, const CompileData& compileData) { - if (!initializeOptix()) - { - throw RuntimeError("OptixDenoiser failed to initialize CUDA and/or OptiX!"); - } + // Initialize OptiX context. + if (!mOptixContext) + mOptixContext = initOptix(mpDevice.get()); // Determine available inputs mHasColorInput = (compileData.connectedResources.getField(kColorInput) != nullptr); @@ -141,9 +146,8 @@ void OptixDenoiser_::compile(RenderContext* pRenderContext, const CompileData& c // If the user specified a denoiser on initialization, respect that. Otherwise, choose the "best" if (mSelectBestMode) { - auto best = mHasMotionInput - ? OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_TEMPORAL - : OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_HDR; + auto best = mHasMotionInput ? OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_TEMPORAL + : OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_HDR; mSelectedModel = best; mDenoiser.modelKind = best; @@ -151,11 +155,11 @@ void OptixDenoiser_::compile(RenderContext* pRenderContext, const CompileData& c // Create a dropdown menu for selecting the denoising mode mModelChoices = {}; - mModelChoices.push_back({ OPTIX_DENOISER_MODEL_KIND_LDR, "LDR denoising" }); - mModelChoices.push_back({ OPTIX_DENOISER_MODEL_KIND_HDR, "HDR denoising" }); + mModelChoices.push_back({OPTIX_DENOISER_MODEL_KIND_LDR, "LDR denoising"}); + mModelChoices.push_back({OPTIX_DENOISER_MODEL_KIND_HDR, "HDR denoising"}); if (mHasMotionInput) { - mModelChoices.push_back({ OPTIX_DENOISER_MODEL_KIND_TEMPORAL, "Temporal denoising" }); + mModelChoices.push_back({OPTIX_DENOISER_MODEL_KIND_TEMPORAL, "Temporal denoising"}); } // (Re-)allocate temporary buffers when render resolution changes @@ -173,8 +177,10 @@ void OptixDenoiser_::compile(RenderContext* pRenderContext, const CompileData& c } // Size intensity and hdrAverage buffers correctly. Only one at a time is used, but these are small, so create them both - if (mDenoiser.intensityBuffer.getSize() != (1 * sizeof(float))) mDenoiser.intensityBuffer.resize(1 * sizeof(float)); - if (mDenoiser.hdrAverageBuffer.getSize() != (3 * sizeof(float))) mDenoiser.hdrAverageBuffer.resize(3 * sizeof(float)); + if (mDenoiser.intensityBuffer.getSize() != (1 * sizeof(float))) + mDenoiser.intensityBuffer.resize(1 * sizeof(float)); + if (mDenoiser.hdrAverageBuffer.getSize() != (3 * sizeof(float))) + mDenoiser.hdrAverageBuffer.resize(3 * sizeof(float)); // Create an intensity GPU buffer to pass to OptiX when appropriate if (!mDenoiser.kernelPredictionMode || !mDenoiser.useAOVs) @@ -182,7 +188,7 @@ void OptixDenoiser_::compile(RenderContext* pRenderContext, const CompileData& c mDenoiser.params.hdrIntensity = mDenoiser.intensityBuffer.getDevicePtr(); mDenoiser.params.hdrAverageColor = static_cast(0); } - else // Create an HDR average color GPU buffer to pass to OptiX when appropriate + else // Create an HDR average color GPU buffer to pass to OptiX when appropriate { mDenoiser.params.hdrIntensity = static_cast(0); mDenoiser.params.hdrAverageColor = mDenoiser.hdrAverageBuffer.getDevicePtr(); @@ -238,16 +244,19 @@ void OptixDenoiser_::allocateStagingBuffer(RenderContext* pRenderContext, Intero falcorFormat = ResourceFormat::RG32Float; break; default: - throw RuntimeError("OptixDenoiser called allocateStagingBuffer() with unsupported format"); + FALCOR_THROW("OptixDenoiser called allocateStagingBuffer() with unsupported format"); } // If we had an existing buffer in this location, free it. - if (interop.devicePtr) freeSharedDevicePtr((void*)interop.devicePtr); + if (interop.devicePtr) + cuda_utils::freeSharedDevicePtr((void*)interop.devicePtr); // Create a new DX <-> CUDA shared buffer using the Falcor API to create, then find its CUDA pointer. - interop.buffer = Buffer::createTyped(mpDevice, falcorFormat, + interop.buffer = mpDevice->createTypedBuffer( + falcorFormat, mBufferSize.x * mBufferSize.y, - Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess | Resource::BindFlags::RenderTarget | Resource::BindFlags::Shared); + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess | ResourceBindFlags::RenderTarget | ResourceBindFlags::Shared + ); interop.devicePtr = (CUdeviceptr)exportBufferToCudaDevice(interop.buffer); // Setup an OptiXImage2D structure so OptiX will used this new buffer for image data @@ -263,7 +272,8 @@ void OptixDenoiser_::freeStagingBuffer(Interop& interop, OptixImage2D& image) { // Free the CUDA memory for this buffer, then set our other references to it to NULL to avoid // accidentally trying to access the freed memory. - if (interop.devicePtr) freeSharedDevicePtr((void*)interop.devicePtr); + if (interop.devicePtr) + cuda_utils::freeSharedDevicePtr((void*)interop.devicePtr); interop.buffer = nullptr; image.data = static_cast(0); } @@ -297,15 +307,20 @@ void OptixDenoiser_::execute(RenderContext* pRenderContext, const RenderData& re } if (mHasNormalInput && mDenoiser.options.guideNormal) { - convertNormalsToBuf(pRenderContext, renderData.getTexture(kNormalInput), mDenoiser.interop.normal.buffer, mBufferSize, transpose(inverse(mpScene->getCamera()->getViewMatrix()))); + convertNormalsToBuf( + pRenderContext, + renderData.getTexture(kNormalInput), + mDenoiser.interop.normal.buffer, + mBufferSize, + transpose(inverse(mpScene->getCamera()->getViewMatrix())) + ); } if (mHasMotionInput && mDenoiser.modelKind == OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_TEMPORAL) { convertMotionVectors(pRenderContext, renderData.getTexture(kMotionInput), mDenoiser.interop.motionVec.buffer, mBufferSize); } - // TODO: Find a better way to synchronize - pRenderContext->flush(true); + pRenderContext->waitForFalcor(); // Compute average intensity, if needed if (mDenoiser.params.hdrIntensity) @@ -341,16 +356,22 @@ void OptixDenoiser_::execute(RenderContext* pRenderContext, const RenderData& re } // Run denoiser - optixDenoiserInvoke(mDenoiser.denoiser, - nullptr, // CUDA stream + optixDenoiserInvoke( + mDenoiser.denoiser, + nullptr, // CUDA stream &mDenoiser.params, - mDenoiser.stateBuffer.getDevicePtr(), mDenoiser.stateBuffer.getSize(), - &mDenoiser.guideLayer, // Our set of normal / albedo / motion vector guides - &mDenoiser.layer, // Array of input or AOV layers (also contains denoised per-layer outputs) - 1u, // Nuumber of layers in the above array - 0u, // (Tile) Input offset X - 0u, // (Tile) Input offset Y - mDenoiser.scratchBuffer.getDevicePtr(), mDenoiser.scratchBuffer.getSize()); + mDenoiser.stateBuffer.getDevicePtr(), + mDenoiser.stateBuffer.getSize(), + &mDenoiser.guideLayer, // Our set of normal / albedo / motion vector guides + &mDenoiser.layer, // Array of input or AOV layers (also contains denoised per-layer outputs) + 1u, // Nuumber of layers in the above array + 0u, // (Tile) Input offset X + 0u, // (Tile) Input offset Y + mDenoiser.scratchBuffer.getDevicePtr(), + mDenoiser.scratchBuffer.getSize() + ); + + pRenderContext->waitForCuda(); // Copy denoised output buffer to texture for Falcor to consume convertBufToTex(pRenderContext, mDenoiser.interop.denoiserOutput.buffer, renderData.getTexture(kOutput), mBufferSize); @@ -384,7 +405,12 @@ void OptixDenoiser_::renderUI(Gui::Widgets& widget) mDenoiser.modelKind = static_cast(mSelectedModel); mRecreateDenoiser = true; } - widget.tooltip("Selects the OptiX denosing model. See OptiX documentation for details.\n\nFor best results:\n LDR assumes inputs [0..1]\n HDR assumes inputs [0..10,000]"); + widget.tooltip( + "Selects the OptiX denosing model. See OptiX documentation for details.\n\n" + "For best results:\n" + " LDR assumes inputs [0..1]\n" + " HDR assumes inputs [0..10,000]" + ); if (mHasAlbedoInput) { @@ -405,7 +431,11 @@ void OptixDenoiser_::renderUI(Gui::Widgets& widget) mDenoiser.options.guideNormal = useNormalGuide ? 1u : 0u; mRecreateDenoiser = true; } - widget.tooltip("Use input, noise-free normal buffer to help guide denoising. (Note: The Optix 7.3 API is unclear on this point, but, correct use of normal guides appears to also require using an albedo guide.)"); + widget.tooltip( + "Use input, noise-free normal buffer to help guide denoising. " + "(Note: The Optix 7.3 API is unclear on this point, but, " + "correct use of normal guides appears to also require using an albedo guide.)" + ); } { @@ -422,22 +452,13 @@ void OptixDenoiser_::renderUI(Gui::Widgets& widget) } } - // Basically a wrapper to handle null Falcor Buffers gracefully, which couldn't // happen in getShareDevicePtr(), due to the bootstrapping that avoids namespace conflicts -void * OptixDenoiser_::exportBufferToCudaDevice(ref &buf) -{ - if (buf == nullptr) return nullptr; - return getSharedDevicePtr(buf->getSharedApiHandle(), (uint32_t)buf->getSize()); -} - -bool OptixDenoiser_::initializeOptix() +void* OptixDenoiser_::exportBufferToCudaDevice(ref& buf) { - if (!mOptixInitialized) - { - mOptixInitialized = initOptix(mOptixContext); - } - return mOptixInitialized; + if (buf == nullptr) + return nullptr; + return cuda_utils::getSharedDevicePtr(buf->getSharedApiHandle(), (uint32_t)buf->getSize()); } void OptixDenoiser_::setupDenoiser() @@ -449,10 +470,7 @@ void OptixDenoiser_::setupDenoiser() } // Create the denoiser - optixDenoiserCreate(mOptixContext, - mDenoiser.modelKind, - &mDenoiser.options, - &mDenoiser.denoiser); + optixDenoiserCreate(mOptixContext, mDenoiser.modelKind, &mDenoiser.options, &mDenoiser.denoiser); // Find out how much memory is needed for the requested denoiser optixDenoiserComputeMemoryResources(mDenoiser.denoiser, mDenoiser.tileWidth, mDenoiser.tileHeight, &mDenoiser.sizes); @@ -462,12 +480,16 @@ void OptixDenoiser_::setupDenoiser() mDenoiser.stateBuffer.resize(mDenoiser.sizes.stateSizeInBytes); // Finish setup of the denoiser - optixDenoiserSetup(mDenoiser.denoiser, + optixDenoiserSetup( + mDenoiser.denoiser, nullptr, - mDenoiser.tileWidth + 2 * mDenoiser.tileOverlap, // Should work with tiling if parameters set appropriately - mDenoiser.tileHeight + 2 * mDenoiser.tileOverlap, // Should work with tiling if parameters set appropriately - mDenoiser.stateBuffer.getDevicePtr(), mDenoiser.stateBuffer.getSize(), - mDenoiser.scratchBuffer.getDevicePtr(), mDenoiser.scratchBuffer.getSize()); + mDenoiser.tileWidth + 2 * mDenoiser.tileOverlap, // Should work with tiling if parameters set appropriately + mDenoiser.tileHeight + 2 * mDenoiser.tileOverlap, // Should work with tiling if parameters set appropriately + mDenoiser.stateBuffer.getDevicePtr(), + mDenoiser.stateBuffer.getSize(), + mDenoiser.scratchBuffer.getDevicePtr(), + mDenoiser.scratchBuffer.getSize() + ); } void OptixDenoiser_::convertMotionVectors(RenderContext* pRenderContext, const ref& tex, const ref& buf, const uint2& size) @@ -489,7 +511,13 @@ void OptixDenoiser_::convertTexToBuf(RenderContext* pRenderContext, const refexecute(pRenderContext, size.x, size.y); } -void OptixDenoiser_::convertNormalsToBuf(RenderContext* pRenderContext, const ref& tex, const ref& buf, const uint2& size, float4x4 viewIT) +void OptixDenoiser_::convertNormalsToBuf( + RenderContext* pRenderContext, + const ref& tex, + const ref& buf, + const uint2& size, + float4x4 viewIT +) { auto var = mpConvertNormalsToBuf->getRootVar(); var["GlobalCB"]["gStride"] = size.x; diff --git a/Source/RenderPasses/OptixDenoiser/OptixDenoiser.h b/Source/RenderPasses/OptixDenoiser/OptixDenoiser.h index 96e390b9a..35d7b5814 100644 --- a/Source/RenderPasses/OptixDenoiser/OptixDenoiser.h +++ b/Source/RenderPasses/OptixDenoiser/OptixDenoiser.h @@ -26,39 +26,41 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Requirements to use this pass: - 1) Have the OptiX 7.3 SDK installed (directly or via packman) - 2) Have NVIDIA driver 465.84 or later. - - When porting this pass, especially to older Falcor forks, it sometimes becomes - dependent on the DLL cudart64_101.dll, which is generally not copied into the binary - directory. Depending on the version of Falcor, not finding all DLL dependencies - causes Mogwai to crash mysteriously in loadLibrary() when loading a render pass DLL. -*/ - -/** Usage: - - A simple encapsulation of the OptiX Denoiser. It is not guaranteed optimal. - In fact, it is definitely suboptimal, as it targets flexibility to use in *any* - Falcor render graph without awareness of any DX <-> OptiX interop requirements. - The pass uses resource copies that could be optimized away, adding some overhead, - though on a RTX 3090, this pass takes about 3ms at 1080p, which seems quite reasonable. - - Using this pass: - * Connect noisy color image to the "color" pass texture - - Can be LDR or HDR. My testing shows the HDR model works just fine - on LDR inputs... so this pass defaults to using HDR. - * (Optionally) connect non-noisy albedo and normals to the "albedo" and - "normal" pass inputs. Think: These come directly from your G-buffer. - * (Optionally) connect non-noisy motion vectors to the "mvec" pass input. - Use image-space motion vectors, as output by the Falcor G-Buffer pass. - * Denoised results get output to the "output" pass texture - * Basic UI controls many OptiX settings, though a few are not yet exposed. - * The following parameters can be used in Python / scripting to control - startup / initial default settings: - - model [OptixDenoiserModel.LDR/HDR/Temporal/AOV] Note: AOVs not yet supported - - denoiseAlpha [True/False]: Should denoising run on alpha channel of input? - - blend [0...1]: Output a blend of denoised and input (0 = denoised, 1 = noisy) +/** + * Requirements to use this pass: + * 1) Have the OptiX 7.3 SDK installed (directly or via packman) + * 2) Have NVIDIA driver 465.84 or later. + * + * When porting this pass, especially to older Falcor forks, it sometimes becomes + * dependent on the DLL cudart64_101.dll, which is generally not copied into the binary + * directory. Depending on the version of Falcor, not finding all DLL dependencies + * causes Mogwai to crash mysteriously in loadLibrary() when loading a render pass DLL. + */ + +/** + * Usage: + * + * A simple encapsulation of the OptiX Denoiser. It is not guaranteed optimal. + * In fact, it is definitely suboptimal, as it targets flexibility to use in *any* + * Falcor render graph without awareness of any DX <-> OptiX interop requirements. + * The pass uses resource copies that could be optimized away, adding some overhead, + * though on a RTX 3090, this pass takes about 3ms at 1080p, which seems quite reasonable. + * + * Using this pass: + * * Connect noisy color image to the "color" pass texture + * - Can be LDR or HDR. My testing shows the HDR model works just fine + * on LDR inputs... so this pass defaults to using HDR. + * * (Optionally) connect non-noisy albedo and normals to the "albedo" and + * "normal" pass inputs. Think: These come directly from your G-buffer. + * * (Optionally) connect non-noisy motion vectors to the "mvec" pass input. + * Use image-space motion vectors, as output by the Falcor G-Buffer pass. + * * Denoised results get output to the "output" pass texture + * * Basic UI controls many OptiX settings, though a few are not yet exposed. + * * The following parameters can be used in Python / scripting to control + * startup / initial default settings: + * - model [OptixDenoiserModel.LDR/HDR/Temporal/AOV] Note: AOVs not yet supported + * - denoiseAlpha [True/False]: Should denoising run on alpha channel of input? + * - blend [0...1]: Output a blend of denoised and input (0 = denoised, 1 = noisy) */ #pragma once @@ -97,114 +99,138 @@ class OptixDenoiser_ : public RenderPass private: ref mpScene; - /** Initializes OptiX & CUDA contexts. Returns true on success (if false, everything else will fail). - */ - bool initializeOptix(); - - /** Call when we need to (re-)create an OptiX denoiser, on initialization or when settings change. - */ + /** + * Call when we need to (re-)create an OptiX denoiser, on initialization or when settings change. + */ void setupDenoiser(); - /** The OptiX denoiser expects inputs and outputs as flat arrays (i.e., not CUDA arrays / textures - with z-order internal memory layout). We can either bang on CUDA/OptiX to support that *or* we can - convert layout on the DX size with a pre-/post-pass to convert to a flat array, then share flat - arrays with OptiX. While conversion is non-optimal, we'd need to do internal blit()s anyways (to - avoid exposing OptiX interop outside this render pass) so this isn't much slower than a better-designed - sharing of GPU memory between DX and OptiX. - */ + /** + * The OptiX denoiser expects inputs and outputs as flat arrays (i.e., not CUDA arrays / textures + * with z-order internal memory layout). We can either bang on CUDA/OptiX to support that *or* we can + * convert layout on the DX size with a pre-/post-pass to convert to a flat array, then share flat + * arrays with OptiX. While conversion is non-optimal, we'd need to do internal blit()s anyways (to + * avoid exposing OptiX interop outside this render pass) so this isn't much slower than a better-designed + * sharing of GPU memory between DX and OptiX. + */ void convertTexToBuf(RenderContext* pRenderContext, const ref& tex, const ref& buf, const uint2& size); - void convertNormalsToBuf(RenderContext* pRenderContext, const ref& tex, const ref& buf, const uint2& size, float4x4 viewIT); + void convertNormalsToBuf( + RenderContext* pRenderContext, + const ref& tex, + const ref& buf, + const uint2& size, + float4x4 viewIT + ); void convertBufToTex(RenderContext* pRenderContext, const ref& buf, const ref& tex, const uint2& size); void convertMotionVectors(RenderContext* pRenderContext, const ref& tex, const ref& buf, const uint2& size); // Options and parameters for the Falcor render pass - bool mEnabled = true; ///< True = using OptiX denoiser, False = pass is a no-op - bool mSelectBestMode = true; ///< Will select best mode automatically (changed to false if the mode is set by Python) - bool mIsFirstFrame = true; ///< True on the first frame after (re-)creating a denoiser - bool mHasColorInput = true; ///< Do we have a color input? - bool mHasAlbedoInput = false; ///< Do we have an albedo guide image for denoising? - bool mHasNormalInput = false; ///< Do we have a normal guide image for denoising? - bool mHasMotionInput = false; ///< Do we have input motion vectors for temporal denoising? - uint2 mBufferSize = uint2(0, 0); ///< Current window / render size - bool mRecreateDenoiser = true; ///< Do we need to (re-)initialize the denoiser before invoking it? + + /// True = using OptiX denoiser, False = pass is a no-op + bool mEnabled = true; + /// Will select best mode automatically (changed to false if the mode is set by Python) + bool mSelectBestMode = true; + /// True on the first frame after (re-)creating a denoiser + bool mIsFirstFrame = true; + /// Do we have a color input? + bool mHasColorInput = true; + /// Do we have an albedo guide image for denoising? + bool mHasAlbedoInput = false; + /// Do we have a normal guide image for denoising? + bool mHasNormalInput = false; + /// Do we have input motion vectors for temporal denoising? + bool mHasMotionInput = false; + /// Current window / render size + uint2 mBufferSize = uint2(0, 0); + /// Do we need to (re-)initialize the denoiser before invoking it? + bool mRecreateDenoiser = true; // GUI helpers for choosing between different OptiX AI denoiser modes - Gui::DropdownList mModelChoices = {}; - uint32_t mSelectedModel = OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_HDR; + + Gui::DropdownList mModelChoices = {}; + uint32_t mSelectedModel = OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_HDR; // Optix context - bool mOptixInitialized = false; - OptixDeviceContext mOptixContext = nullptr; - // Structure to encapsulate DX <-> CUDA interop data for a buffer + OptixDeviceContext mOptixContext = nullptr; + + /// Structure to encapsulate DX <-> CUDA interop data for a buffer struct Interop { - ref buffer; // Falcor buffer - CUdeviceptr devicePtr = (CUdeviceptr)0; // CUDA pointer to buffer + ref buffer; ///< Falcor buffer + CUdeviceptr devicePtr = (CUdeviceptr)0; ///< CUDA pointer to buffer }; // Encapsulte our denoiser parameters, settings, and state. struct { // Various OptiX denoiser parameters and handles. Explicitly initialize everything, just to be sure. - OptixDenoiserOptions options = { 0u, 0u }; - OptixDenoiserModelKind modelKind = OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_HDR; - OptixDenoiser denoiser = nullptr; - OptixDenoiserParams params = { 0u, static_cast(0), 0.0f, static_cast(0) }; - OptixDenoiserSizes sizes = {}; + OptixDenoiserOptions options = {0u, 0u}; + OptixDenoiserModelKind modelKind = OptixDenoiserModelKind::OPTIX_DENOISER_MODEL_KIND_HDR; + OptixDenoiser denoiser = nullptr; + OptixDenoiserParams params = {0u, static_cast(0), 0.0f, static_cast(0)}; + OptixDenoiserSizes sizes = {}; // TODO: Parameters currently set to false and not exposed to the user. These parameters are here to // lay the groundwork for more advanced options in the OptiX denoiser, *however* there has not been // testing or even validation that all parameters are set correctly to enable these settings. - bool kernelPredictionMode = false; - bool useAOVs = false; - uint32_t tileOverlap = 0u; + bool kernelPredictionMode = false; + bool useAOVs = false; + uint32_t tileOverlap = 0u; // If using tiled denoising (not tested), set appropriately, otherwise set these to the input / output image size. - uint32_t tileWidth = 0u; - uint32_t tileHeight = 0u; + uint32_t tileWidth = 0u; + uint32_t tileHeight = 0u; // A wrapper around denoiser inputs for guide normals, albedo, and motion vectors OptixDenoiserGuideLayer guideLayer = {}; // A wrapper around denoiser input color, output color, and prior frame's output (for temporal reuse) - OptixDenoiserLayer layer = {}; + OptixDenoiserLayer layer = {}; // A wrapper around our guide layer interop with DirectX struct Intermediates { - Interop normal; - Interop albedo; - Interop motionVec; - Interop denoiserInput; - Interop denoiserOutput; + Interop normal; + Interop albedo; + Interop motionVec; + Interop denoiserInput; + Interop denoiserOutput; } interop; // GPU memory we need to allocate for the Optix denoiser to play in & store temporaries - CudaBuffer scratchBuffer, stateBuffer, intensityBuffer, hdrAverageBuffer; + CudaBuffer scratchBuffer, stateBuffer, intensityBuffer, hdrAverageBuffer; } mDenoiser; // Our shaders for converting buffers on input and output from OptiX - ref mpConvertTexToBuf; - ref mpConvertNormalsToBuf; - ref mpConvertMotionVectors; - ref mpConvertBufToTex; - ref mpFbo; - - /** Allocate a DX <-> CUDA staging buffer - */ - void allocateStagingBuffer(RenderContext* pRenderContext, Interop& interop, OptixImage2D& image, OptixPixelFormat format = OPTIX_PIXEL_FORMAT_FLOAT4); - - /** Not strictly required, but can be used to deallocate a staging buffer if a user toggles its use off - */ + ref mpConvertTexToBuf; + ref mpConvertNormalsToBuf; + ref mpConvertMotionVectors; + ref mpConvertBufToTex; + ref mpFbo; + + /** + * Allocate a DX <-> CUDA staging buffer + */ + void allocateStagingBuffer( + RenderContext* pRenderContext, + Interop& interop, + OptixImage2D& image, + OptixPixelFormat format = OPTIX_PIXEL_FORMAT_FLOAT4 + ); + + /** + * Not strictly required, but can be used to deallocate a staging buffer if a user toggles its use off + */ void freeStagingBuffer(Interop& interop, OptixImage2D& image); - /** Reallocate all our staging buffers for DX <-> CUDA/Optix interop - */ + /** + * Reallocate all our staging buffers for DX <-> CUDA/Optix interop + */ void reallocateStagingBuffers(RenderContext* pRenderContext); - /** Get a device pointer from a buffer. This wrapper gracefully handles nullptrs (i.e., if buf == nullptr) - */ + /** + * Get a device pointer from a buffer. This wrapper gracefully handles nullptrs (i.e., if buf == nullptr) + */ void* exportBufferToCudaDevice(ref& buf); }; diff --git a/Source/RenderPasses/OptixDenoiser/OptixUtils.cpp b/Source/RenderPasses/OptixDenoiser/OptixUtils.cpp index c1f9a5d3a..d0c10ee22 100644 --- a/Source/RenderPasses/OptixDenoiser/OptixUtils.cpp +++ b/Source/RenderPasses/OptixDenoiser/OptixUtils.cpp @@ -33,26 +33,22 @@ #include // Some debug macros -#define OPTIX_CHECK( call ) \ - { \ - OptixResult res = call; \ - if( res != OPTIX_SUCCESS ) \ - { \ - char buf[1024]; \ - sprintf( buf, "Optix call (%s) failed with code %d (line %d)\n", #call, res, __LINE__ ); \ - Falcor::reportFatalError(std::string(buf)); \ - } \ - } -#define CUDA_CHECK(call) \ - { \ - cudaError_t rc = call; \ - if (rc != cudaSuccess) { \ - std::stringstream txt; \ - cudaError_t err = rc; /*cudaGetLastError();*/ \ - txt << "CUDA Error " << cudaGetErrorName(err) \ - << " (" << cudaGetErrorString(err) << ")"; \ - Falcor::reportFatalError(txt.str()); \ - } \ +#define OPTIX_CHECK(call) \ + { \ + OptixResult result = call; \ + if (result != OPTIX_SUCCESS) \ + { \ + FALCOR_THROW("Optix call {} failed with error {} ({}).", #call, optixGetErrorName(result), optixGetErrorString(result)); \ + } \ + } + +#define CUDA_CHECK(call) \ + { \ + cudaError_t result = call; \ + if (result != cudaSuccess) \ + { \ + FALCOR_THROW("CUDA call {} failed with error {} ({}).", #call, cudaGetErrorName(result), cudaGetErrorString(result)); \ + } \ } void optixLogCallback(unsigned int level, const char* tag, const char* message, void*) @@ -62,44 +58,20 @@ void optixLogCallback(unsigned int level, const char* tag, const char* message, // This initialization now seems verbose / excessive as CUDA and OptiX initialization // has evolved. TODO: Simplify? -bool initOptix(OptixDeviceContext& optixContext) +OptixDeviceContext initOptix(Falcor::Device* pDevice) { - // Initialize CUDA - uint32_t devices = initCuda(); - if (devices <= 0) return false; + FALCOR_CHECK(pDevice->initCudaDevice(), "Failed to initialize CUDA device."); - // Initialize Optix. OPTIX_CHECK(optixInit()); - // Check if we have a valid OptiX function table. If not, return now. - if (!g_optixFunctionTable.optixDeviceContextCreate) return false; - - // Setup which device to work on. Hard coded to device #0 - int32_t deviceId = 0; - CUDA_CHECK(cudaSetDevice(deviceId)); - - // Create a CUDA stream - CUstream stream; - CUDA_CHECK(cudaStreamCreate(&stream)); - - // Get device information - cudaDeviceProp deviceProps; - cudaGetDeviceProperties(&deviceProps, deviceId); - - // Get the current context - CUcontext cudaContext; - CUresult cuRes = cuCtxGetCurrent(&cudaContext); + FALCOR_CHECK(g_optixFunctionTable.optixDeviceContextCreate, "OptiX function table not initialized."); // Build our OptiX context - OPTIX_CHECK(optixDeviceContextCreate(cudaContext, 0, &optixContext)); - - // Given the OPTIX_CHECK() and CUDA_CHECK() wrappers above that simply log errors, - // explicitly check for successful initialization by seeing if we have a non-null context - if (optixContext == nullptr) return false; + OptixDeviceContext optixContext; + OPTIX_CHECK(optixDeviceContextCreate(pDevice->getCudaDevice()->getContext(), 0, &optixContext)); // Tell Optix how to write to our Falcor log. - OPTIX_CHECK(optixDeviceContextSetLogCallback(optixContext, - optixLogCallback, nullptr, 4)); + OPTIX_CHECK(optixDeviceContextSetLogCallback(optixContext, optixLogCallback, nullptr, 4)); - return true; + return optixContext; } diff --git a/Source/RenderPasses/OptixDenoiser/OptixUtils.h b/Source/RenderPasses/OptixDenoiser/OptixUtils.h index 89f2c0ee0..d1aeee19d 100644 --- a/Source/RenderPasses/OptixDenoiser/OptixUtils.h +++ b/Source/RenderPasses/OptixDenoiser/OptixUtils.h @@ -28,13 +28,45 @@ #pragma once #include "Falcor.h" +#include "Utils/CudaRuntime.h" +#include "Utils/CudaUtils.h" #include #include -// Note: There's some CUDA / Falcor type conflicts. This header includes Falcor-facing functions -// for accessing the OptiX denoiser. Including or was a pain -// I wanted to avoid, so CUDA-specific stuff lives in CudaUtils.cpp +/** + * Utility class for a GPU/device buffer for use with CUDA. + * Adapted from Ingo Wald's SIGGRAPH 2019 tutorial code for OptiX 7. + */ +class CudaBuffer +{ +public: + CudaBuffer() {} -// Initializes OptiX. Returns true on success. -bool initOptix(OptixDeviceContext& optixContext); + CUdeviceptr getDevicePtr() { return (CUdeviceptr)mpDevicePtr; } + size_t getSize() { return mSizeBytes; } + + void allocate(size_t size) + { + if (mpDevicePtr) + free(); + mSizeBytes = size; + FALCOR_CUDA_CHECK(cudaMalloc((void**)&mpDevicePtr, mSizeBytes)); + } + + void resize(size_t size) { allocate(size); } + + void free() + { + FALCOR_CUDA_CHECK(cudaFree(mpDevicePtr)); + mpDevicePtr = nullptr; + mSizeBytes = 0; + } + +private: + size_t mSizeBytes = 0; + void* mpDevicePtr = nullptr; +}; + +// Initializes OptiX context. Throws an exception if initialization fails. +OptixDeviceContext initOptix(Falcor::Device* pDevice); diff --git a/Source/RenderPasses/PathTracer/GeneratePaths.cs.slang b/Source/RenderPasses/PathTracer/GeneratePaths.cs.slang index 1d4f9bd8a..907126007 100644 --- a/Source/RenderPasses/PathTracer/GeneratePaths.cs.slang +++ b/Source/RenderPasses/PathTracer/GeneratePaths.cs.slang @@ -142,7 +142,7 @@ struct PathGenerator { // RTXDI uses a simple material model with only diffuse and specular reflection lobes. // We query the BSDF for the diffuse albedo and specular reflectance. - gRTXDI.setSurfaceData(pixel, sd.computeNewRayOrigin(), bsdfProperties.guideNormal, bsdfProperties.diffuseReflectionAlbedo, bsdfProperties.specularReflectance, bsdfProperties.roughness); + gRTXDI.setSurfaceData(pixel, sd.computeRayOrigin(), bsdfProperties.guideNormal, bsdfProperties.diffuseReflectionAlbedo, bsdfProperties.specularReflectance, bsdfProperties.roughness); } } if (!validSurface) gRTXDI.setInvalidSurfaceData(pixel); diff --git a/Source/RenderPasses/PathTracer/Params.slang b/Source/RenderPasses/PathTracer/Params.slang index 43a241388..d4607818d 100644 --- a/Source/RenderPasses/PathTracer/Params.slang +++ b/Source/RenderPasses/PathTracer/Params.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/RenderPasses/PathTracer/PathTracer.cpp b/Source/RenderPasses/PathTracer/PathTracer.cpp index d8e9cce66..094327635 100644 --- a/Source/RenderPasses/PathTracer/PathTracer.cpp +++ b/Source/RenderPasses/PathTracer/PathTracer.cpp @@ -37,8 +37,6 @@ namespace const std::string kResolvePassFilename = "RenderPasses/PathTracer/ResolvePass.cs.slang"; const std::string kReflectTypesFile = "RenderPasses/PathTracer/ReflectTypes.cs.slang"; - const std::string kShaderModel = "6_5"; - // Render pass inputs and outputs. const std::string kInputVBuffer = "vbuffer"; const std::string kInputMotionVectors = "mvec"; @@ -140,11 +138,13 @@ namespace const std::string kPrimaryLodMode = "primaryLodMode"; const std::string kLODBias = "lodBias"; + const std::string kUseNRDDemodulation = "useNRDDemodulation"; + + const std::string kUseSER = "useSER"; + const std::string kOutputSize = "outputSize"; const std::string kFixedOutputSize = "fixedOutputSize"; const std::string kColorFormat = "colorFormat"; - - const std::string kUseNRDDemodulation = "useNRDDemodulation"; } extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) @@ -156,6 +156,7 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr void PathTracer::registerBindings(pybind11::module& m) { pybind11::class_> pass(m, "PathTracer"); + pass.def("reset", &PathTracer::reset); pass.def_property_readonly("pixelStats", &PathTracer::getPixelStats); pass.def_property("useFixedSeed", @@ -171,14 +172,12 @@ void PathTracer::registerBindings(pybind11::module& m) PathTracer::PathTracer(ref pDevice, const Properties& props) : RenderPass(pDevice) { - if (!mpDevice->isShaderModelSupported(Device::ShaderModel::SM6_5)) - { - throw RuntimeError("PathTracer: Shader Model 6.5 is not supported by the current device"); - } + if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_5)) + FALCOR_THROW("PathTracer requires Shader Model 6.5 support."); if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) - { - throw RuntimeError("PathTracer: Raytracing Tier 1.1 is not supported by the current device"); - } + FALCOR_THROW("PathTracer requires Raytracing Tier 1.1 support."); + + mSERSupported = mpDevice->isFeatureSupported(Device::SupportedFeatures::ShaderExecutionReorderingAPI); parseProperties(props); validateOptions(); @@ -188,7 +187,7 @@ PathTracer::PathTracer(ref pDevice, const Properties& props) // Create resolve pass. This doesn't depend on the scene so can be created here. auto defines = mStaticParams.getDefines(*this); - mpResolvePass = ComputePass::create(mpDevice, Program::Desc(kResolvePassFilename).setShaderModel(kShaderModel).csEntry("main"), defines, false); + mpResolvePass = ComputePass::create(mpDevice, ProgramDesc().addShaderLibrary(kResolvePassFilename).csEntry("main"), defines, false); // Note: The other programs are lazily created in updatePrograms() because a scene needs to be present when creating them. @@ -196,6 +195,18 @@ PathTracer::PathTracer(ref pDevice, const Properties& props) mpPixelDebug = std::make_unique(mpDevice); } +void PathTracer::setProperties(const Properties& props) +{ + parseProperties(props); + validateOptions(); + if (auto lightBVHSampler = dynamic_cast(mpEmissiveSampler.get())) + lightBVHSampler->setOptions(mLightBVHOptions); + if (mpRTXDI) + mpRTXDI->setOptions(mRTXDIOptions); + mRecompile = true; + mOptionsChanged = true; +} + void PathTracer::parseProperties(const Properties& props) { for (const auto& [key, value] : props) @@ -234,6 +245,9 @@ void PathTracer::parseProperties(const Properties& props) // Denoising parameters else if (key == kUseNRDDemodulation) mStaticParams.useNRDDemodulation = value; + // Scheduling parameters + else if (key == kUseSER) mStaticParams.useSER = value; + // Output parameters else if (key == kOutputSize) mOutputSizeSelection = value; else if (key == kFixedOutputSize) mFixedOutputSize = value; @@ -305,6 +319,12 @@ void PathTracer::validateOptions() logWarning("Unsupported tex lod mode. Defaulting to Mip0."); mStaticParams.primaryLodMode = TexLODMode::Mip0; } + + if (mStaticParams.useSER && !mSERSupported) + { + logWarning("Shader Execution Reordering (SER) is not supported on this device. Disabling SER."); + mStaticParams.useSER = false; + } } Properties PathTracer::getProperties() const @@ -350,6 +370,9 @@ Properties PathTracer::getProperties() const // Denoising parameters props[kUseNRDDemodulation] = mStaticParams.useNRDDemodulation; + // Scheduling parameters + props[kUseSER] = mStaticParams.useSER; + // Output parameters props[kOutputSize] = mOutputSizeSelection; if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) props[kFixedOutputSize] = mFixedOutputSize; @@ -376,7 +399,7 @@ void PathTracer::setFrameDim(const uint2 frameDim) mParams.frameDim = frameDim; if (mParams.frameDim.x > kMaxFrameDimension || mParams.frameDim.y > kMaxFrameDimension) { - throw RuntimeError("Frame dimensions up to {} pixels width/height are supported.", kMaxFrameDimension); + FALCOR_THROW("Frame dimensions up to {} pixels width/height are supported.", kMaxFrameDimension); } // Tile dimensions have to be powers-of-two. @@ -400,13 +423,7 @@ void PathTracer::setScene(RenderContext* pRenderContext, const ref& pScen // Need to recreate the RTXDI module when the scene changes. mpRTXDI = nullptr; - // Need to recreate the trace passes because the shader binding table changes. - mpTracePass = nullptr; - mpTraceDeltaReflectionPass = nullptr; - mpTraceDeltaTransmissionPass = nullptr; - mpGeneratePaths = nullptr; - mpReflectTypes = nullptr; - + resetPrograms(); resetLighting(); if (mpScene) @@ -417,8 +434,6 @@ void PathTracer::setScene(RenderContext* pRenderContext, const ref& pScen } validateOptions(); - - mRecompile = true; } } @@ -607,6 +622,12 @@ bool PathTracer::renderRenderingUI(Gui::Widgets& widget) widget.tooltip("Global switch for NRD demodulation"); } + if (auto group = widget.group("Scheduling options")) + { + dirty |= widget.checkbox("Use SER", mStaticParams.useSER); + widget.tooltip("Use Shader Execution Reordering (SER) to improve GPU utilization."); + } + if (auto group = widget.group("Output options")) { // Switch to enable/disable path tracer output. @@ -662,28 +683,36 @@ bool PathTracer::onMouseEvent(const MouseEvent& mouseEvent) return mpPixelDebug->onMouseEvent(mouseEvent); } -PathTracer::TracePass::TracePass(ref pDevice, const std::string& name, const std::string& passDefine, const ref& pScene, const DefineList& defines, const Program::TypeConformanceList& globalTypeConformances) +void PathTracer::reset() +{ + mParams.frameCount = 0; +} + +PathTracer::TracePass::TracePass(ref pDevice, const std::string& name, const std::string& passDefine, const ref& pScene, const DefineList& defines, const TypeConformanceList& globalTypeConformances) : name(name) , passDefine(passDefine) { const uint32_t kRayTypeScatter = 0; const uint32_t kMissScatter = 0; - RtProgram::Desc desc; + bool useSER = defines.at("USE_SER") == "1"; + + ProgramDesc desc; desc.addShaderModules(pScene->getShaderModules()); desc.addShaderLibrary(kTracePassFilename); - desc.setShaderModel(kShaderModel); + if (pDevice->getType() == Device::Type::D3D12 && useSER) + desc.addCompilerArguments({ "-Xdxc", "-enable-lifetime-markers" }); desc.setMaxPayloadSize(160); // This is conservative but the required minimum is 140 bytes. desc.setMaxAttributeSize(pScene->getRaytracingMaxAttributeSize()); desc.setMaxTraceRecursionDepth(1); - if (!pScene->hasProceduralGeometry()) desc.setPipelineFlags(RtPipelineFlags::SkipProceduralPrimitives); + if (!pScene->hasProceduralGeometry()) desc.setRtPipelineFlags(RtPipelineFlags::SkipProceduralPrimitives); // Create ray tracing binding table. pBindingTable = RtBindingTable::create(1, 1, pScene->getGeometryCount()); // Specify entry point for raygen and miss shaders. // The raygen shader needs type conformances for *all* materials in the scene. - // The miss shader doesn't need type conformances as it doesn't access any materials. + // The miss shader doesn't need need any type conformances because it does not use materials. pBindingTable->setRayGen(desc.addRayGen("rayGen", globalTypeConformances)); pBindingTable->setMiss(kMissScatter, desc.addMiss("scatterMiss")); @@ -725,7 +754,7 @@ PathTracer::TracePass::TracePass(ref pDevice, const std::string& name, c } } - pProgram = RtProgram::create(pDevice, desc, defines); + pProgram = Program::create(pDevice, desc, defines); } void PathTracer::TracePass::prepareProgram(ref pDevice, const DefineList& defines) @@ -736,62 +765,79 @@ void PathTracer::TracePass::prepareProgram(ref pDevice, const DefineList pVars = RtProgramVars::create(pDevice, pProgram, pBindingTable); } +void PathTracer::resetPrograms() +{ + mpTracePass = nullptr; + mpTraceDeltaReflectionPass = nullptr; + mpTraceDeltaTransmissionPass = nullptr; + mpGeneratePaths = nullptr; + mpReflectTypes = nullptr; + + mRecompile = true; +} + void PathTracer::updatePrograms() { FALCOR_ASSERT(mpScene); if (mRecompile == false) return; + // If we get here, a change that require recompilation of shader programs has occurred. + // This may be due to change of scene defines, type conformances, shader modules, or other changes that require recompilation. + // When type conformances and/or shader modules change, the programs need to be recreated. We assume programs have been reset upon such changes. + // When only defines have changed, it is sufficient to update the existing programs and recreate the program vars. + auto defines = mStaticParams.getDefines(*this); - auto globalTypeConformances = mpScene->getMaterialSystem().getTypeConformances(); + auto globalTypeConformances = mpScene->getTypeConformances(); - // Create trace passes lazily. - if (!mpTracePass) mpTracePass = std::make_unique(mpDevice, "tracePass", "", mpScene, defines, globalTypeConformances); + // Create trace pass. + if (!mpTracePass) + mpTracePass = std::make_unique(mpDevice, "tracePass", "", mpScene, defines, globalTypeConformances); + + mpTracePass->prepareProgram(mpDevice, defines); + + // Create specialized trace passes. if (mOutputNRDAdditionalData) { - if (!mpTraceDeltaReflectionPass) mpTraceDeltaReflectionPass = std::make_unique(mpDevice, "traceDeltaReflectionPass", "DELTA_REFLECTION_PASS", mpScene, defines, globalTypeConformances); - if (!mpTraceDeltaTransmissionPass) mpTraceDeltaTransmissionPass = std::make_unique(mpDevice, "traceDeltaTransmissionPass", "DELTA_TRANSMISSION_PASS", mpScene, defines, globalTypeConformances); - } + if (!mpTraceDeltaReflectionPass) + mpTraceDeltaReflectionPass = std::make_unique(mpDevice, "traceDeltaReflectionPass", "DELTA_REFLECTION_PASS", mpScene, defines, globalTypeConformances); + if (!mpTraceDeltaTransmissionPass) + mpTraceDeltaTransmissionPass = std::make_unique(mpDevice, "traceDeltaTransmissionPass", "DELTA_TRANSMISSION_PASS", mpScene, defines, globalTypeConformances); - // Create program vars for trace programs. - // We only need to set defines for program specialization here. Type conformances have already been setup on construction. - mpTracePass->prepareProgram(mpDevice, defines); - if (mpTraceDeltaReflectionPass) mpTraceDeltaReflectionPass->prepareProgram(mpDevice, defines); - if (mpTraceDeltaTransmissionPass) mpTraceDeltaTransmissionPass->prepareProgram(mpDevice, defines); + mpTraceDeltaReflectionPass->prepareProgram(mpDevice, defines); + mpTraceDeltaTransmissionPass->prepareProgram(mpDevice, defines); + } // Create compute passes. - Program::Desc baseDesc; + ProgramDesc baseDesc; baseDesc.addShaderModules(mpScene->getShaderModules()); baseDesc.addTypeConformances(globalTypeConformances); - baseDesc.setShaderModel(kShaderModel); if (!mpGeneratePaths) { - Program::Desc desc = baseDesc; + ProgramDesc desc = baseDesc; desc.addShaderLibrary(kGeneratePathsFilename).csEntry("main"); mpGeneratePaths = ComputePass::create(mpDevice, desc, defines, false); } if (!mpReflectTypes) { - Program::Desc desc = baseDesc; + ProgramDesc desc = baseDesc; desc.addShaderLibrary(kReflectTypesFile).csEntry("main"); mpReflectTypes = ComputePass::create(mpDevice, desc, defines, false); } - // Perform program specialization. - // Note that we must use set instead of add functions to replace any stale state. - auto prepareProgram = [&](ref program) + auto preparePass = [&](ref pass) { - program->setDefines(defines); - }; - prepareProgram(mpGeneratePaths->getProgram()); - prepareProgram(mpResolvePass->getProgram()); - prepareProgram(mpReflectTypes->getProgram()); + // Note that we must use set instead of add defines to replace any stale state. + pass->getProgram()->setDefines(defines); - // Create program vars for the specialized programs. - mpGeneratePaths->setVars(nullptr); - mpResolvePass->setVars(nullptr); - mpReflectTypes->setVars(nullptr); + // Recreate program vars. This may trigger recompilation if needed. + // Note that program versions are cached, so switching to a previously used specialization is faster. + pass->setVars(nullptr); + }; + preparePass(mpGeneratePaths); + preparePass(mpResolvePass); + preparePass(mpReflectTypes); mVarsChanged = true; mRecompile = false; @@ -816,7 +862,7 @@ void PathTracer::prepareResources(RenderContext* pRenderContext, const RenderDat if (!mpSampleOffset || mpSampleOffset->getWidth() != mParams.frameDim.x || mpSampleOffset->getHeight() != mParams.frameDim.y) { FALCOR_ASSERT(kScreenTileDim.x * kScreenTileDim.y * kMaxSamplesPerPixel <= (1u << 16)); - mpSampleOffset = Texture::create2D(mpDevice, mParams.frameDim.x, mParams.frameDim.y, ResourceFormat::R16Uint, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpSampleOffset = mpDevice->createTexture2D(mParams.frameDim.x, mParams.frameDim.y, ResourceFormat::R16Uint, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); mVarsChanged = true; } } @@ -829,24 +875,24 @@ void PathTracer::prepareResources(RenderContext* pRenderContext, const RenderDat { if (!mpSampleColor || mpSampleColor->getElementCount() < sampleCount || mVarsChanged) { - mpSampleColor = Buffer::createStructured(mpDevice, var["sampleColor"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpSampleColor = mpDevice->createStructuredBuffer(var["sampleColor"], sampleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); mVarsChanged = true; } } if (mOutputGuideData && (!mpSampleGuideData || mpSampleGuideData->getElementCount() < sampleCount || mVarsChanged)) { - mpSampleGuideData = Buffer::createStructured(mpDevice, var["sampleGuideData"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpSampleGuideData = mpDevice->createStructuredBuffer(var["sampleGuideData"], sampleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); mVarsChanged = true; } if (mOutputNRDData && (!mpSampleNRDRadiance || mpSampleNRDRadiance->getElementCount() < sampleCount || mVarsChanged)) { - mpSampleNRDRadiance = Buffer::createStructured(mpDevice, var["sampleNRDRadiance"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpSampleNRDHitDist = Buffer::createStructured(mpDevice, var["sampleNRDHitDist"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpSampleNRDPrimaryHitNeeOnDelta = Buffer::createStructured(mpDevice, var["sampleNRDPrimaryHitNeeOnDelta"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpSampleNRDEmission = Buffer::createStructured(mpDevice, var["sampleNRDEmission"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpSampleNRDReflectance = Buffer::createStructured(mpDevice, var["sampleNRDReflectance"], sampleCount, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpSampleNRDRadiance = mpDevice->createStructuredBuffer(var["sampleNRDRadiance"], sampleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); + mpSampleNRDHitDist = mpDevice->createStructuredBuffer(var["sampleNRDHitDist"], sampleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); + mpSampleNRDPrimaryHitNeeOnDelta = mpDevice->createStructuredBuffer(var["sampleNRDPrimaryHitNeeOnDelta"], sampleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); + mpSampleNRDEmission = mpDevice->createStructuredBuffer(var["sampleNRDEmission"], sampleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); + mpSampleNRDReflectance = mpDevice->createStructuredBuffer(var["sampleNRDReflectance"], sampleCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false); mVarsChanged = true; } } @@ -864,7 +910,7 @@ void PathTracer::preparePathTracer(const RenderData& renderData) // Bind resources. auto var = mpPathTracerBlock->getRootVar(); - setShaderData(var, renderData); + bindShaderData(var, renderData); } void PathTracer::resetLighting() @@ -882,13 +928,14 @@ void PathTracer::resetLighting() void PathTracer::prepareMaterials(RenderContext* pRenderContext) { - // This functions checks for material changes and performs any necessary update. - // For now all we need to do is to trigger a recompile so that the right defines get set. - // In the future, we might want to do additional material-specific setup here. + // This functions checks for scene changes that require shader recompilation. + // Whenever materials or geometry is added/removed to the scene, we reset the shader programs to trigger + // recompilation with the correct defines, type conformances, shader modules, and binding table. - if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::MaterialsChanged)) + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded) || + is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) { - mRecompile = true; + resetPrograms(); } } @@ -959,7 +1006,7 @@ bool PathTracer::prepareLighting(RenderContext* pRenderContext) mpEmissiveSampler = std::make_unique(pRenderContext, mpScene); break; default: - throw RuntimeError("Unknown emissive light sampler type"); + FALCOR_THROW("Unknown emissive light sampler type"); } lightingChanged = true; mRecompile = true; @@ -1031,12 +1078,12 @@ void PathTracer::setNRDData(const ShaderVar& var, const RenderData& renderData) var["deltaTransmissionPosW"] = renderData.getTexture(kOutputNRDDeltaTransmissionPosW); } -void PathTracer::setShaderData(const ShaderVar& var, const RenderData& renderData, bool useLightSampling) const +void PathTracer::bindShaderData(const ShaderVar& var, const RenderData& renderData, bool useLightSampling) const { // Bind static resources that don't change per frame. if (mVarsChanged) { - if (useLightSampling && mpEnvMapSampler) mpEnvMapSampler->setShaderData(var["envMapSampler"]); + if (useLightSampling && mpEnvMapSampler) mpEnvMapSampler->bindShaderData(var["envMapSampler"]); var["sampleOffset"] = mpSampleOffset; // Can be nullptr var["sampleColor"] = mpSampleColor; @@ -1057,7 +1104,7 @@ void PathTracer::setShaderData(const ShaderVar& var, const RenderData& renderDat if (!mFixedSampleCount) { pSampleCount = renderData.getTexture(kInputSampleCount); - if (!pSampleCount) throw RuntimeError("PathTracer: Missing sample count input texture"); + if (!pSampleCount) FALCOR_THROW("PathTracer: Missing sample count input texture"); } var["params"].setBlob(mParams); @@ -1069,7 +1116,7 @@ void PathTracer::setShaderData(const ShaderVar& var, const RenderData& renderDat if (useLightSampling && mpEmissiveSampler) { // TODO: Do we have to bind this every frame? - mpEmissiveSampler->setShaderData(var["emissiveSampler"]); + mpEmissiveSampler->bindShaderData(var["emissiveSampler"]); } } @@ -1245,11 +1292,11 @@ void PathTracer::generatePaths(RenderContext* pRenderContext, const RenderData& // Bind resources. auto var = mpGeneratePaths->getRootVar()["CB"]["gPathGenerator"]; - setShaderData(var, renderData, false); + bindShaderData(var, renderData, false); - mpGeneratePaths->getRootVar()["gScene"] = mpScene->getParameterBlock(); + mpScene->bindShaderData(mpGeneratePaths->getRootVar()["gScene"]); - if (mpRTXDI) mpRTXDI->setShaderData(mpGeneratePaths->getRootVar()); + if (mpRTXDI) mpRTXDI->bindShaderData(mpGeneratePaths->getRootVar()); // Launch one thread per pixel. // The dimensions are padded to whole tiles to allow re-indexing the threads in the shader. @@ -1272,8 +1319,8 @@ void PathTracer::tracePass(RenderContext* pRenderContext, const RenderData& rend auto var = tracePass.pVars->getRootVar(); mpScene->setRaytracingShaderData(pRenderContext, var); - if (mVarsChanged) mpSampleGenerator->setShaderData(var); - if (mpRTXDI) mpRTXDI->setShaderData(var); + if (mVarsChanged) mpSampleGenerator->bindShaderData(var); + if (mpRTXDI) mpRTXDI->bindShaderData(var); mpPixelStats->prepareProgram(tracePass.pProgram, var); mpPixelDebug->prepareProgram(tracePass.pProgram, var); @@ -1356,6 +1403,7 @@ DefineList PathTracer::StaticParams::getDefines(const PathTracer& owner) const defines.add("DISABLE_CAUSTICS", disableCaustics ? "1" : "0"); defines.add("PRIMARY_LOD_MODE", std::to_string((uint32_t)primaryLodMode)); defines.add("USE_NRD_DEMODULATION", useNRDDemodulation ? "1" : "0"); + defines.add("USE_SER", useSER ? "1" : "0"); defines.add("COLOR_FORMAT", std::to_string((uint32_t)colorFormat)); defines.add("MIS_HEURISTIC", std::to_string((uint32_t)misHeuristic)); defines.add("MIS_POWER_EXPONENT", std::to_string(misPowerExponent)); diff --git a/Source/RenderPasses/PathTracer/PathTracer.h b/Source/RenderPasses/PathTracer/PathTracer.h index a42a0878b..579c14bc6 100644 --- a/Source/RenderPasses/PathTracer/PathTracer.h +++ b/Source/RenderPasses/PathTracer/PathTracer.h @@ -53,6 +53,7 @@ class PathTracer : public RenderPass PathTracer(ref pDevice, const Properties& props); + virtual void setProperties(const Properties& props) override; virtual Properties getProperties() const override; virtual RenderPassReflection reflect(const CompileData& compileData) override; virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; @@ -63,6 +64,8 @@ class PathTracer : public RenderPass PixelStats& getPixelStats() { return *mpPixelStats; } + void reset(); + static void registerBindings(pybind11::module& m); private: @@ -70,16 +73,17 @@ class PathTracer : public RenderPass { std::string name; std::string passDefine; - ref pProgram; + ref pProgram; ref pBindingTable; ref pVars; - TracePass(ref pDevice, const std::string& name, const std::string& passDefine, const ref& pScene, const DefineList& defines, const Program::TypeConformanceList& globalTypeConformances); + TracePass(ref pDevice, const std::string& name, const std::string& passDefine, const ref& pScene, const DefineList& defines, const TypeConformanceList& globalTypeConformances); void prepareProgram(ref pDevice, const DefineList& defines); }; void parseProperties(const Properties& props); void validateOptions(); + void resetPrograms(); void updatePrograms(); void setFrameDim(const uint2 frameDim); void prepareResources(RenderContext* pRenderContext, const RenderData& renderData); @@ -89,7 +93,7 @@ class PathTracer : public RenderPass bool prepareLighting(RenderContext* pRenderContext); void prepareRTXDI(RenderContext* pRenderContext); void setNRDData(const ShaderVar& var, const RenderData& renderData) const; - void setShaderData(const ShaderVar& var, const RenderData& renderData, bool useLightSampling = true) const; + void bindShaderData(const ShaderVar& var, const RenderData& renderData, bool useLightSampling = true) const; bool renderRenderingUI(Gui::Widgets& widget); bool renderDebugUI(Gui::Widgets& widget); void renderStatsUI(Gui::Widgets& widget); @@ -129,6 +133,9 @@ class PathTracer : public RenderPass bool disableCaustics = false; ///< Disable sampling of caustics. TexLODMode primaryLodMode = TexLODMode::Mip0; ///< Use filtered texture lookups at the primary hit. + // Scheduling parameters + bool useSER = true; ///< Enable SER (Shader Execution Reordering). + // Output parameters ColorFormat colorFormat = ColorFormat::LogLuvHDR; ///< Color format used for internal per-sample color and denoiser buffers. @@ -148,6 +155,8 @@ class PathTracer : public RenderPass RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. uint2 mFixedOutputSize = { 512, 512 }; ///< Output size in pixels when 'Fixed' size is selected. + bool mSERSupported = false; ///< True if the device supports SER. + // Internal state ref mpScene; ///< The current scene, or nullptr if no scene loaded. ref mpSampleGenerator; ///< GPU pseudo-random sample generator. diff --git a/Source/RenderPasses/PathTracer/PathTracer.slang b/Source/RenderPasses/PathTracer/PathTracer.slang index b1871d0b5..31f512d40 100644 --- a/Source/RenderPasses/PathTracer/PathTracer.slang +++ b/Source/RenderPasses/PathTracer/PathTracer.slang @@ -309,6 +309,55 @@ struct PathTracer logSetPixel(path.getPixel()); } + /** Compute a set of coherence hints for a given path state. + These hints predict the code path taken in the next call to handleHit() with the given path state. + They are used by reordering schedulers to extract coherence. + \param[in] path Path state. + \param[in] isDeltaSpecular Hit material is pure delta specular. + \param[in] isEmissive Hit material is emissive. + \return Returns a bit mask of coherence hints. MSB is highest priority hint. Currently 4 bits are used. + */ + uint getCoherenceHints(const PathState path, const bool isDeltaSpecular, const bool isEmissive) + { + bool terminatedByRussianRoulette = {}; ///< handleHit() will be terminated early due to russian roulette. + bool terminated = {}; ///< handleHit() will be terminated early. + bool samplesLight = {}; ///< handleHit() will sample a light. + bool computesEmissive = {}; ///< handleHit() will compute emission. + + // Determines if handleHit() will return early due to russian roulette. + if (kUseRussianRoulette) + { + // This is under the assumption that the first random number drawn from the sample generator is used for russian roulette. + // Keep in sync with handleHit(). + PathState tmpPath = path; + terminatedByRussianRoulette = terminatePathByRussianRoulette(tmpPath, sampleNext1D(tmpPath.sg)); + } + + // Determines if handleHit() will return early. + terminated = hasFinishedSurfaceBounces(path); + if (kDisableCaustics && path.getBounces(BounceType::Diffuse) > 0 && isDeltaSpecular) + terminated = true; + + // Determines if handleHit() will sample a light. + samplesLight = true; + if (isDeltaSpecular) + samplesLight = false; + if (!kUseLightsInDielectricVolumes && path.isInsideDielectricVolume()) + samplesLight = false; + + // Determines if handleHit() will compute emission. + computesEmissive = isEmissive && path.isLightSamplable(); + if (kUseRTXDI && path.getVertexIndex() == 2 && !isDeltaSpecular) + computesEmissive = false; + + // MSB = highest priority hint. + return + select(computesEmissive, 0x1, 0x0) | + select(samplesLight, 0x2, 0x0) | + select(terminated, 0x4, 0x0) | + select(terminatedByRussianRoulette, 0x8, 0x0); + } + /** Update the path throughouput. \param[in,out] path Path state. \param[in] weight Vertex throughput. @@ -442,7 +491,7 @@ struct PathTracer else { // Compute ray origin for next ray segment. - path.origin = sd.computeNewRayOrigin(false); + path.origin = sd.computeRayOrigin(false); // Update interior list and inside volume flag. if (!sd.mtl.isThinSurface()) @@ -707,7 +756,7 @@ struct PathTracer { path.rejectedHits++; path.interiorList.handleIntersection(sd.materialID, nestedPriority, sd.frontFacing); - path.origin = sd.computeNewRayOrigin(false); + path.origin = sd.computeRayOrigin(false); path.decrementVertexIndex(); } else @@ -889,7 +938,7 @@ struct PathTracer } else { - path.origin = sd.computeNewRayOrigin(); + path.origin = sd.computeRayOrigin(); } // Determine if BSDF has non-delta lobes. diff --git a/Source/RenderPasses/PathTracer/PathTracerNRD.slang b/Source/RenderPasses/PathTracer/PathTracerNRD.slang index 948a3778b..44d6c5ee9 100644 --- a/Source/RenderPasses/PathTracer/PathTracerNRD.slang +++ b/Source/RenderPasses/PathTracer/PathTracerNRD.slang @@ -129,7 +129,7 @@ extension PathTracer } // Compute origin for rays traced from this path vertex. - path.origin = sd.computeNewRayOrigin(); + path.origin = sd.computeRayOrigin(); // Hijack pdf that we don't need. float primaryHitDist = path.pdf; @@ -252,7 +252,7 @@ extension PathTracer } // Compute origin for rays traced from this path vertex. - path.origin = sd.computeNewRayOrigin(); + path.origin = sd.computeRayOrigin(); // Set the active lobes only to delta transmission. sd.mtl.setActiveLobes((uint)LobeType::DeltaTransmission); diff --git a/Source/RenderPasses/PathTracer/TracePass.rt.slang b/Source/RenderPasses/PathTracer/TracePass.rt.slang index 4173a84ba..469f15ada 100644 --- a/Source/RenderPasses/PathTracer/TracePass.rt.slang +++ b/Source/RenderPasses/PathTracer/TracePass.rt.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -37,13 +37,21 @@ import Utils.Math.FormatConversion; import PathTracer; import PathTracerNRD; +import Utils.NVAPI; // TODO(@skallweit) this is currently needed to make g_NvidiaExt visible + ParameterBlock gPathTracer; +#if USE_SER +ReorderingScheduler gScheduler; +#else Scheduler gScheduler; +#endif // Define ray indices. static const uint kRayTypeScatter = 0; static const uint kMissScatter = 0; +struct DummyAttribs { uint dummy; }; + struct PathPayload { uint4 packed[5]; @@ -118,6 +126,261 @@ struct VisibilityQuery : PathTracer::IVisibilityQuery } }; +/** Helper function to create a HitInfo from a HitObject. + */ +HitInfo makeHitInfo(const HitObject hitObject) +{ + HitInfo hit = {}; + + if (hitObject.IsHit()) + { + const GeometryInstanceID instanceID = GeometryInstanceID(hitObject.GetInstanceID(), hitObject.GetGeometryIndex()); + const uint primitiveIndex = hitObject.GetPrimitiveIndex(); + + // Lookup geometry type as we cannot reconstruct that from the HitObject directly. + const GeometryType type = gScene.getGeometryInstanceType(instanceID); + + switch (type) + { + case GeometryType::None: + break; + case GeometryType::TriangleMesh: + { + TriangleHit triangleHit; + triangleHit.instanceID = instanceID; + triangleHit.primitiveIndex = primitiveIndex; + BuiltInTriangleIntersectionAttributes attribs = hitObject.GetAttributes(); + triangleHit.barycentrics = attribs.barycentrics; + hit = HitInfo(triangleHit); + break; + } + case GeometryType::DisplacedTriangleMesh: + { + DisplacedTriangleHit displacedTriangleHit; + displacedTriangleHit.instanceID = instanceID; + displacedTriangleHit.primitiveIndex = primitiveIndex; + DisplacedTriangleMeshIntersector.Attribs attribs = hitObject.GetAttributes(); + displacedTriangleHit.barycentrics = attribs.barycentrics; + displacedTriangleHit.displacement = attribs.displacement; + hit = HitInfo(displacedTriangleHit); + break; + } + case GeometryType::Curve: + { + CurveHit curveHit; + curveHit.instanceID = instanceID; + curveHit.primitiveIndex = primitiveIndex; + CurveIntersector.Attribs attribs = hitObject.GetAttributes(); + curveHit.barycentrics = attribs.barycentrics; + hit = HitInfo(curveHit); + break; + } + case GeometryType::SDFGrid: + { + SDFGridHit sdfGridHit; + sdfGridHit.instanceID = instanceID; + SDFGridHitData attribs = hitObject.GetAttributes(); + sdfGridHit.hitData = attribs; + hit = HitInfo(sdfGridHit); + break; + } + case GeometryType::Custom: + // Unsupported. + break; + } + } + + return hit; +} + +/** Reordering scheduler using SER/HitObject API. + + The API allows to implement the PathTracer::IClosestHitQuery interface, because + after executing ray traversal using HitObject::TraceRay(), control is given back to the + caller before the CHS/MS is explicitly invoked using InvokeHitObject(). + This allows for executing volume distance sampling as part of PathTracer::nextHit. +*/ +struct ReorderingScheduler +{ + /** Handle a miss. + Hit information is encoded in the payload. + Note: This is also called for volume hits. + \param[in,out] payload Payload data. + */ + void handleMiss(inout PathPayload payload) + { + PathState path = PathPayload::unpack(payload); + path.clearHit(); + path.sceneLength = float16_t(kNRDInvalidPathLength); + + gPathTracer.setupPathLogging(path); + gPathTracer.handleMiss(path); + + payload = PathPayload::pack(path); + } + + /** Handle a hit. + \param[in,out] payload Payload data. + */ + void handleHit(inout PathPayload payload) + { + PathState path = PathPayload::unpack(payload); + + gPathTracer.setupPathLogging(path); + VisibilityQuery vq; +#if defined(DELTA_REFLECTION_PASS) + gPathTracer.handleDeltaReflectionHit(path); +#elif defined(DELTA_TRANSMISSION_PASS) + gPathTracer.handleDeltaTransmissionHit(path); +#else + gPathTracer.handleHit(path, vq); +#endif + + payload = PathPayload::pack(path); + } + + /** Traces a path and write result to output. + \param[in] pathID Path ID. + */ + void tracePath(uint pathID) + { + PathState path = {}; + + gPathTracer.generatePath(pathID, path); + gPathTracer.setupPathLogging(path); + + if (path.isHit()) + { + // Handle primary hits. + + // Create a HitObject from a HitInfo. + // The created HitObject does only contain the minimal required data, + // most data is already passed in the path.hit object. + // For volume hits, a miss object is returned(and handled in the handleMiss() function). + HitObject hitObject; + RayDesc dummyRay = {}; + DummyAttribs dummyAttribs = {}; + + switch (path.hit.getType()) + { + case HitType::None: + hitObject = HitObject::MakeMiss(kMissScatter, dummyRay); + break; + case HitType::Volume: + hitObject = HitObject::MakeMiss(kMissScatter, dummyRay); + break; + default: + { + const GeometryInstanceID instanceID = path.hit.getInstanceID(); + const GeometryInstanceData instance = gScene.getGeometryInstance(instanceID); + const uint primitiveIndex = path.hit.getPrimitiveIndex(); + hitObject = HitObject::MakeHit( + gScene.rtAccel, // AccelerationStructure + instance.instanceIndex, // InstanceIndex + instance.geometryIndex, // GeometryIndex + primitiveIndex, // PrimitiveIndex + 0, // HitKind + kRayTypeScatter, // RayContributionToHitGroupIndex + rayTypeCount, // MultiplierForGeometryContributionToHitGroupIndex + dummyRay, // Ray + dummyAttribs // Attributes + ); + break; + } + } + + PathPayload payload = PathPayload::pack(path); + HitObject::Invoke(gScene.rtAccel, hitObject, payload); + path = PathPayload::unpack(payload); + } + else + { + // Note the primary miss has already been handled by the separate path generation pass + // the path tracer runs first. Abort execution here to avoid double work. + return; + } + + // Trace path. + while (path.isActive()) + { + PathPayload payload = {}; + HitObject hitObject; + + { + // Advance to next path vertex. + path.incrementVertexIndex(); + + // Trace ray. + logTraceRay(PixelStatsRayType::ClosestHit); + const Ray ray = path.getScatterRay(); + payload = PathPayload::pack(path); + const uint rayFlags = kUseAlphaTest ? RAY_FLAG_NONE : RAY_FLAG_FORCE_OPAQUE; + hitObject = HitObject::TraceRay( gScene.rtAccel, rayFlags, 0xff /* instanceInclusionMask */, kRayTypeScatter /* hitIdx */, rayTypeCount, kMissScatter, ray.toRayDesc(), payload ); + path = PathPayload::unpack(payload); + + if (hitObject.IsHit()) + { + path.setHit(makeHitInfo(hitObject)); + path.sceneLength += float16_t(hitObject.GetRayDesc().TMax); + } + else + { + path.clearHit(); + path.sceneLength = float16_t(kNRDInvalidPathLength); + } + } + + // Add path coherence hints. + const uint materialID = gScene.getMaterialID(path.hit.getInstanceID()); + const MaterialHeader materialHeader = gScene.materials.getMaterialHeader(materialID); + uint coherenceHints = gPathTracer.getCoherenceHints(path, materialHeader.isDeltaSpecular(), materialHeader.isEmissive()); + + // Reorder and invoke closest hit or miss shader. + ReorderThread(hitObject, coherenceHints, 4); + payload = PathPayload::pack(path); + HitObject::Invoke(gScene.rtAccel, hitObject, payload); + path = PathPayload::unpack(payload); + } + +#if !defined(DELTA_REFLECTION_PASS) && !defined(DELTA_TRANSMISSION_PASS) + gPathTracer.writeOutput(path); +#endif + } + + /** Runs the path tracer for a single pixel. + \param[in] pixel Pixel index. + */ + void run(uint2 pixel) + { + // Determine number of samples to take. + uint samplesRemaining = kSamplesPerPixel; + if (kSamplesPerPixel == 0) + { + samplesRemaining = gPathTracer.sampleCount[pixel]; + } + + // Loop over samples. + while (samplesRemaining > 0) + { + samplesRemaining -= 1; + uint pathID = pixel.x | (pixel.y << 12) | (samplesRemaining << 24); + tracePath(pathID); + + // Use SER to compact active threads. + if (kSamplesPerPixel == 0) + { + bool needReorder = WaveActiveAnyTrue(samplesRemaining == 0); + if (samplesRemaining == 0) break; + if (needReorder) + { + HitObject hitObject = HitObject::MakeNop(); + ReorderThread(hitObject); + } + } + } + } +} + /** Simple scheduler using a fullscreen raytracing dispatch. */ struct Scheduler @@ -273,11 +536,15 @@ void scatterTriangleAnyHit(inout PathPayload payload : SV_RayPayload, BuiltInTri [shader("closesthit")] void scatterTriangleClosestHit(inout PathPayload payload : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) { +#if USE_SER + gScheduler.handleHit(payload); +#else TriangleHit triangleHit; triangleHit.instanceID = getGeometryInstanceID(); triangleHit.primitiveIndex = PrimitiveIndex(); triangleHit.barycentrics = attribs.barycentrics; gScheduler.handleHit(payload, HitInfo(triangleHit), RayTCurrent()); +#endif } // @@ -299,12 +566,16 @@ void displacedTriangleMeshIntersection() [shader("closesthit")] void scatterDisplacedTriangleMeshClosestHit(inout PathPayload payload, DisplacedTriangleMeshIntersector::Attribs attribs) { +#if USE_SER + gScheduler.handleHit(payload); +#else DisplacedTriangleHit displacedTriangleHit; displacedTriangleHit.instanceID = getGeometryInstanceID(); displacedTriangleHit.primitiveIndex = PrimitiveIndex(); displacedTriangleHit.barycentrics = attribs.barycentrics; displacedTriangleHit.displacement = attribs.displacement; gScheduler.handleHit(payload, HitInfo(displacedTriangleHit), RayTCurrent()); +#endif } // @@ -326,11 +597,15 @@ void curveIntersection() [shader("closesthit")] void scatterCurveClosestHit(inout PathPayload payload, CurveIntersector::Attribs attribs) { +#if USE_SER + gScheduler.handleHit(payload); +#else CurveHit curveHit; curveHit.instanceID = getGeometryInstanceID(); curveHit.primitiveIndex = PrimitiveIndex(); curveHit.barycentrics = attribs.barycentrics; gScheduler.handleHit(payload, HitInfo(curveHit), RayTCurrent()); +#endif } @@ -353,10 +628,14 @@ void sdfGridIntersection() [shader("closesthit")] void scatterSdfGridClosestHit(inout PathPayload payload, SDFGridHitData sdfGridHitData) { +#if USE_SER + gScheduler.handleHit(payload); +#else SDFGridHit sdfGridHit; sdfGridHit.instanceID = getGeometryInstanceID(); sdfGridHit.hitData = sdfGridHitData; gScheduler.handleHit(payload, HitInfo(sdfGridHit), RayTCurrent()); +#endif } // diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspector.cs.slang b/Source/RenderPasses/PixelInspectorPass/PixelInspector.cs.slang index 681f7ea39..281f6d489 100644 --- a/Source/RenderPasses/PixelInspectorPass/PixelInspector.cs.slang +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspector.cs.slang @@ -53,7 +53,7 @@ Texture2D gWorldTangent; Texture2D gWorldFaceNormal; Texture2D gTextureCoord; Texture2D gTextureGrads; -Texture2D gMaterialData; +Texture2D gMaterialData; Texture2D gLinearColor; Texture2D gOutputColor; @@ -63,11 +63,12 @@ RWStructuredBuffer gPixelDataBuffer; #define is_valid(name) (is_valid_##name != 0) -/** Helper to create a texture sampler instance. - The method for computing texture level-of-detail depends on the configuration. - \param[in] pixel Current pixel coordinates. - \return Texture sampler instance. -*/ +/** + * Helper to create a texture sampler instance. + * The method for computing texture level-of-detail depends on the configuration. + * @param[in] pixel Current pixel coordinates. + * @return Texture sampler instance. + */ ITextureSampler createTextureSampler(const uint2 pixel) { if (is_valid(gTextureGrads)) @@ -81,8 +82,9 @@ ITextureSampler createTextureSampler(const uint2 pixel) } } -/** Helper to load G-buffer data and prepare shading data. -*/ +/** + * Helper to load G-buffer data and prepare shading data. + */ bool loadShadingData(const float3 viewDir, const ITextureSampler lod, inout ShadingData sd) { float4 worldPos = gWorldPosition[gWorldPositionCoord]; @@ -113,7 +115,7 @@ bool loadShadingData(const float3 viewDir, const ITextureSampler lod, inout Shad } [numthreads(1, 1, 1)] -void main(uint3 DTid : SV_DispatchThreadID) +void main(uint3 DTid: SV_DispatchThreadID) { const uint2 pixel = gSelectedPixel; @@ -144,7 +146,6 @@ void main(uint3 DTid : SV_DispatchThreadID) // Store material data. data.materialID = sd.materialID; data.doubleSided = sd.mtl.isDoubleSided() ? 1 : 0; - data.opacity = sd.opacity; data.IoR = sd.IoR; // Store material instance properties. diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspectorData.slang b/Source/RenderPasses/PixelInspectorPass/PixelInspectorData.slang index b1cd787cb..3e0cfaaf9 100644 --- a/Source/RenderPasses/PixelInspectorPass/PixelInspectorData.slang +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspectorData.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -36,8 +36,9 @@ __exported import Scene.HitInfoType; BEGIN_NAMESPACE_FALCOR -/** Pixel data read out by the PixelInspectorPass. -*/ +/** + * Pixel data read out by the PixelInspectorPass. + */ struct PixelData { static const uint kInvalidIndex = 0xffffffff; @@ -60,14 +61,14 @@ struct PixelData // Material instance properties float3 emission; - float roughness; + float roughness; float3 guideNormal; float3 diffuseReflectionAlbedo; float3 diffuseTransmissionAlbedo; float3 specularReflectionAlbedo; float3 specularTransmissionAlbedo; float3 specularReflectance; - int isTransmissive; + int isTransmissive; // Output data float4 linearColor; @@ -75,7 +76,7 @@ struct PixelData float luminance; // Visibility data - uint hitType; ///< Type of hit, see enum HitType. Only valid if instanceID != kInvalidIndex. + uint hitType; ///< Type of hit, see enum HitType. Only valid if instanceID != kInvalidIndex. uint instanceID; uint primitiveIndex; float2 barycentrics; diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp index 5d4f22630..cf9502569 100644 --- a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.cpp @@ -36,26 +36,26 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr namespace { - const char kShaderFile[] = "RenderPasses/PixelInspectorPass/PixelInspector.cs.slang"; - - const ChannelList kInputChannels = - { - { "posW", "gWorldPosition", "world space position" }, - { "normW", "gWorldShadingNormal", "world space normal", true /* optional */ }, - { "tangentW", "gWorldTangent", "world space tangent", true /* optional */ }, - { "faceNormalW", "gWorldFaceNormal", "face normal in world space", true /* optional */ }, - { "texC", "gTextureCoord", "Texture coordinate", true /* optional */ }, - { "texGrads", "gTextureGrads", "Texture gradients", true /* optional */ }, - { "mtlData", "gMaterialData", "Material data" }, - { "linColor", "gLinearColor", "color pre tone-mapping", true /* optional */ }, - { "outColor", "gOutputColor", "color post tone-mapping", true /* optional */ }, - { "vbuffer", "gVBuffer", "Visibility buffer", true /* optional */ }, - }; - const char kOutputChannel[] = "gPixelDataBuffer"; -} - -PixelInspectorPass::PixelInspectorPass(ref pDevice, const Properties& props) - : RenderPass(pDevice) +const char kShaderFile[] = "RenderPasses/PixelInspectorPass/PixelInspector.cs.slang"; + +const ChannelList kInputChannels = { + // clang-format off + { "posW", "gWorldPosition", "world space position" }, + { "normW", "gWorldShadingNormal", "world space normal", true /* optional */ }, + { "tangentW", "gWorldTangent", "world space tangent", true /* optional */ }, + { "faceNormalW", "gWorldFaceNormal", "face normal in world space", true /* optional */ }, + { "texC", "gTextureCoord", "Texture coordinate", true /* optional */ }, + { "texGrads", "gTextureGrads", "Texture gradients", true /* optional */ }, + { "mtlData", "gMaterialData", "Material data" }, + { "linColor", "gLinearColor", "color pre tone-mapping", true /* optional */ }, + { "outColor", "gOutputColor", "color post tone-mapping", true /* optional */ }, + { "vbuffer", "gVBuffer", "Visibility buffer", true /* optional */ }, + // clang-format on +}; +const char kOutputChannel[] = "gPixelDataBuffer"; +} // namespace + +PixelInspectorPass::PixelInspectorPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { for (auto it : kInputChannels) { @@ -80,21 +80,42 @@ void PixelInspectorPass::execute(RenderContext* pRenderContext, const RenderData mAvailableInputs[it.name] = renderData[it.name] != nullptr; } - if (!mpScene) return; + if (!mpScene) + return; + + // Check for scene changes that require shader recompilation. + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded) || + is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) + { + recreatePrograms(); + } + + // Create program. + if (!mpProgram) + { + ProgramDesc desc; + desc.addShaderModules(mpScene->getShaderModules()); + desc.addShaderLibrary(kShaderFile).csEntry("main"); + desc.addTypeConformances(mpScene->getTypeConformances()); + desc.setCompilerFlags(SlangCompilerFlags::TreatWarningsAsErrors); + + mpProgram = Program::create(mpDevice, desc, mpScene->getSceneDefines()); + mpState->setProgram(mpProgram); + } // For optional I/O resources, set 'is_valid_' defines to inform the program of which ones it can access. mpProgram->addDefines(getValidResourceDefines(kInputChannels, renderData)); if (!mpVars) { - mpVars = ComputeVars::create(mpDevice, mpProgram->getReflector()); - mpPixelDataBuffer = Buffer::createStructured(mpDevice, mpProgram.get(), kOutputChannel, 1); + mpVars = ProgramVars::create(mpDevice, mpProgram->getReflector()); + mpPixelDataBuffer = mpDevice->createStructuredBuffer(mpVars->getRootVar()[kOutputChannel], 1); } auto var = mpVars->getRootVar(); // Bind the scene. - var["gScene"] = mpScene->getParameterBlock(); + mpScene->bindShaderData(var["gScene"]); if (mpScene->getCamera()->getApertureRadius() > 0.f) { @@ -137,7 +158,7 @@ void PixelInspectorPass::execute(RenderContext* pRenderContext, const RenderData var[kOutputChannel] = mpPixelDataBuffer; // Run the inspector program. - pRenderContext->dispatch(mpState.get(), mpVars.get(), { 1u, 1u, 1u }); + pRenderContext->dispatch(mpState.get(), mpVars.get(), {1u, 1u, 1u}); } void PixelInspectorPass::renderUI(Gui::Widgets& widget) @@ -149,26 +170,34 @@ void PixelInspectorPass::renderUI(Gui::Widgets& widget) } FALCOR_ASSERT(mpPixelDataBuffer); - PixelData pixelData = *reinterpret_cast(mpPixelDataBuffer->map(Buffer::MapType::Read)); - mpPixelDataBuffer->unmap(); + PixelData pixelData = mpPixelDataBuffer->getElement(0); // Display the coordinates for the pixel at which information is retrieved. widget.var("Looking at pixel", (int2&)mSelectedPixel, 0); widget.checkbox("Scale inputs to window size", mScaleInputsToWindow); widget.checkbox("Continuously inspect pixels", mUseContinuousPicking); - widget.tooltip("If continuously inspecting pixels, you will always see the data for the pixel currently under your mouse.\n" - "Otherwise, left-mouse click on a pixel to select it.", true); - - const auto displayValues = [&pixelData, &widget, this](const std::vector& inputNames, const std::vector& values, const std::function& displayValues) + widget.tooltip( + "If continuously inspecting pixels, you will always see the data for the pixel currently under your mouse.\n" + "Otherwise, left-mouse click on a pixel to select it.", + true + ); + + const auto displayValues = [&pixelData, &widget, this]( + const std::vector& inputNames, + const std::vector& values, + const std::function& displayValues + ) { bool areAllInputsAvailable = true; - for (const std::string& inputName : inputNames) areAllInputsAvailable = areAllInputsAvailable && mAvailableInputs[inputName]; + for (const std::string& inputName : inputNames) + areAllInputsAvailable = areAllInputsAvailable && mAvailableInputs[inputName]; if (areAllInputsAvailable) { bool areAllInputsInBounds = true; - for (const std::string& inputName : inputNames) areAllInputsInBounds = areAllInputsInBounds && mIsInputInBounds[inputName]; + for (const std::string& inputName : inputNames) + areAllInputsInBounds = areAllInputsInBounds && mIsInputInBounds[inputName]; if (areAllInputsInBounds) { @@ -190,46 +219,93 @@ void PixelInspectorPass::renderUI(Gui::Widgets& widget) // Display output data. if (auto outputGroup = widget.group("Output data", true)) { - bool displayedData = displayValues({ "linColor" }, { "Linear color", "Luminance (cd/m2)" }, [&outputGroup](PixelData& pixelData) { - outputGroup.var("Linear color", pixelData.linearColor, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); - outputGroup.var("Luminance (cd/m2)", pixelData.luminance, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); - }); + bool displayedData = displayValues( + {"linColor"}, + {"Linear color", "Luminance (cd/m2)"}, + [&outputGroup](PixelData& pixelData) + { + outputGroup.var("Linear color", pixelData.linearColor, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); + outputGroup.var("Luminance (cd/m2)", pixelData.luminance, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); + } + ); - displayedData |= displayValues({ "outColor" }, { "Output color" }, [&outputGroup](PixelData& pixelData) { - outputGroup.var("Output color", pixelData.outputColor, 0.f, 1.f, 0.001f, false, "%.6f"); - }); + displayedData |= displayValues( + {"outColor"}, + {"Output color"}, + [&outputGroup](PixelData& pixelData) + { outputGroup.var("Output color", pixelData.outputColor, 0.f, 1.f, 0.001f, false, "%.6f"); } + ); - if (displayedData == false) outputGroup.text("No input data"); + if (displayedData == false) + outputGroup.text("No input data"); } // Display geometry data. if (auto geometryGroup = widget.group("Geometry data", true)) { - displayValues({ "posW" }, { "World position" }, [&geometryGroup](PixelData& pixelData) { - geometryGroup.var("World position", pixelData.posW, std::numeric_limits::lowest(), std::numeric_limits::max(), 0.001f, false, "%.6f"); - }); - - displayValues({ "normW" }, { "Shading normal" }, [&geometryGroup](PixelData& pixelData) { - geometryGroup.var("Shading normal", pixelData.normal, -1.f, 1.f, 0.001f, false, "%.6f"); - }); - - displayValues({ "tangentW" }, { "Shading tangent" }, [&geometryGroup](PixelData& pixelData) { - geometryGroup.var("Shading tangent", pixelData.tangent, -1.f, 1.f, 0.001f, false, "%.6f"); - }); - - displayValues({ "normW", "tangentW" }, { "Shading bitangent" }, [&geometryGroup](PixelData& pixelData) { - geometryGroup.var("Shading bitangent", pixelData.bitangent, -1.f, 1.f, 0.001f, false, "%.6f"); - }); - - displayValues({ "faceNormalW" }, { "Face normal" }, [&geometryGroup](PixelData& pixelData) { - geometryGroup.var("Face normal", pixelData.faceNormal, -1.f, 1.f, 0.001f, false, "%.6f"); - }); + displayValues( + {"posW"}, + {"World position"}, + [&geometryGroup](PixelData& pixelData) + { + geometryGroup.var( + "World position", + pixelData.posW, + std::numeric_limits::lowest(), + std::numeric_limits::max(), + 0.001f, + false, + "%.6f" + ); + } + ); + + displayValues( + {"normW"}, + {"Shading normal"}, + [&geometryGroup](PixelData& pixelData) + { geometryGroup.var("Shading normal", pixelData.normal, -1.f, 1.f, 0.001f, false, "%.6f"); } + ); + + displayValues( + {"tangentW"}, + {"Shading tangent"}, + [&geometryGroup](PixelData& pixelData) + { geometryGroup.var("Shading tangent", pixelData.tangent, -1.f, 1.f, 0.001f, false, "%.6f"); } + ); + + displayValues( + {"normW", "tangentW"}, + {"Shading bitangent"}, + [&geometryGroup](PixelData& pixelData) + { geometryGroup.var("Shading bitangent", pixelData.bitangent, -1.f, 1.f, 0.001f, false, "%.6f"); } + ); + + displayValues( + {"faceNormalW"}, + {"Face normal"}, + [&geometryGroup](PixelData& pixelData) + { geometryGroup.var("Face normal", pixelData.faceNormal, -1.f, 1.f, 0.001f, false, "%.6f"); } + ); geometryGroup.var("View vector", pixelData.view, -1.f, 1.f, 0.001f, false, "%.6f"); - displayValues({ "texC" }, { "Texture coords" }, [&geometryGroup](PixelData& pixelData) { - geometryGroup.var("Texture coord", pixelData.texCoord, std::numeric_limits::lowest(), std::numeric_limits::max(), 0.001f, false, "%.6f"); - }); + displayValues( + {"texC"}, + {"Texture coords"}, + [&geometryGroup](PixelData& pixelData) + { + geometryGroup.var( + "Texture coord", + pixelData.texCoord, + std::numeric_limits::lowest(), + std::numeric_limits::max(), + 0.001f, + false, + "%.6f" + ); + } + ); geometryGroup.checkbox("Front facing", pixelData.frontFacing); } @@ -237,63 +313,142 @@ void PixelInspectorPass::renderUI(Gui::Widgets& widget) // Display material data. if (auto materialGroup = widget.group("Material data", true)) { - const std::vector requiredInputs = { "posW", "texC", "mtlData" }; + const std::vector requiredInputs = {"posW", "texC", "mtlData"}; bool displayedData = false; - displayedData |= displayValues(requiredInputs, { "Material ID" }, [&materialGroup](PixelData& pixelData) { - materialGroup.var("Material ID", pixelData.materialID); - }); - - displayedData |= displayValues(requiredInputs, { "Double sided" }, [&materialGroup](PixelData& pixelData) { - materialGroup.checkbox("Double sided", pixelData.doubleSided); - }); - - displayedData |= displayValues(requiredInputs, { "Opacity" }, [&materialGroup](PixelData& pixelData) { - materialGroup.var("Opacity", pixelData.opacity, 0.f, 1.f, 0.001f, false, "%.6f"); - }); - - displayedData |= displayValues(requiredInputs, { "IoR (outside)" }, [&materialGroup](PixelData& pixelData) { - materialGroup.var("IoR (outside)", pixelData.IoR, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); - }); - - displayedData |= displayValues(requiredInputs, { "Emission" }, [&materialGroup](PixelData& pixelData) { - materialGroup.var("Emission", pixelData.emission, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); - }); - - displayedData |= displayValues(requiredInputs, { "Roughness" }, [&materialGroup](PixelData& pixelData) { - materialGroup.var("Roughness", pixelData.roughness, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); - }); - - displayedData |= displayValues(requiredInputs, { "GuideNormal" }, [&materialGroup](PixelData& pixelData) { - materialGroup.var("GuideNormal", pixelData.guideNormal, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); - }); - - displayedData |= displayValues(requiredInputs, { "DiffuseReflectionAlbedo" }, [&materialGroup](PixelData& pixelData) { - materialGroup.var("DiffuseReflectionAlbedo", pixelData.diffuseReflectionAlbedo, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); - }); - - displayedData |= displayValues(requiredInputs, { "DiffuseTransmissionAlbedo" }, [&materialGroup](PixelData& pixelData) { - materialGroup.var("DiffuseTransmissionAlbedo", pixelData.diffuseTransmissionAlbedo, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); - }); + displayedData |= displayValues( + requiredInputs, + {"Material ID"}, + [&materialGroup](PixelData& pixelData) { materialGroup.var("Material ID", pixelData.materialID); } + ); + + displayedData |= displayValues( + requiredInputs, + {"Double sided"}, + [&materialGroup](PixelData& pixelData) { materialGroup.checkbox("Double sided", pixelData.doubleSided); } + ); + + displayedData |= displayValues( + requiredInputs, + {"Opacity"}, + [&materialGroup](PixelData& pixelData) { materialGroup.var("Opacity", pixelData.opacity, 0.f, 1.f, 0.001f, false, "%.6f"); } + ); + + displayedData |= displayValues( + requiredInputs, + {"IoR (outside)"}, + [&materialGroup](PixelData& pixelData) + { materialGroup.var("IoR (outside)", pixelData.IoR, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); } + ); + + displayedData |= displayValues( + requiredInputs, + {"Emission"}, + [&materialGroup](PixelData& pixelData) + { materialGroup.var("Emission", pixelData.emission, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); } + ); + + displayedData |= displayValues( + requiredInputs, + {"Roughness"}, + [&materialGroup](PixelData& pixelData) + { materialGroup.var("Roughness", pixelData.roughness, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); } + ); + + displayedData |= displayValues( + requiredInputs, + {"GuideNormal"}, + [&materialGroup](PixelData& pixelData) + { materialGroup.var("GuideNormal", pixelData.guideNormal, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); } + ); + + displayedData |= displayValues( + requiredInputs, + {"DiffuseReflectionAlbedo"}, + [&materialGroup](PixelData& pixelData) + { + materialGroup.var( + "DiffuseReflectionAlbedo", + pixelData.diffuseReflectionAlbedo, + 0.f, + std::numeric_limits::max(), + 0.001f, + false, + "%.6f" + ); + } + ); - displayedData |= displayValues(requiredInputs, { "SpecularReflectionAlbedo" }, [&materialGroup](PixelData& pixelData) { - materialGroup.var("SpecularReflectionAlbedo", pixelData.specularReflectionAlbedo, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); - }); + displayedData |= displayValues( + requiredInputs, + {"DiffuseTransmissionAlbedo"}, + [&materialGroup](PixelData& pixelData) + { + materialGroup.var( + "DiffuseTransmissionAlbedo", + pixelData.diffuseTransmissionAlbedo, + 0.f, + std::numeric_limits::max(), + 0.001f, + false, + "%.6f" + ); + } + ); - displayedData |= displayValues(requiredInputs, { "SpecularTransmissionAlbedo" }, [&materialGroup](PixelData& pixelData) { - materialGroup.var("SpecularTransmissionAlbedo", pixelData.specularTransmissionAlbedo, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); - }); + displayedData |= displayValues( + requiredInputs, + {"SpecularReflectionAlbedo"}, + [&materialGroup](PixelData& pixelData) + { + materialGroup.var( + "SpecularReflectionAlbedo", + pixelData.specularReflectionAlbedo, + 0.f, + std::numeric_limits::max(), + 0.001f, + false, + "%.6f" + ); + } + ); - displayedData |= displayValues(requiredInputs, { "SpecularReflectance" }, [&materialGroup](PixelData& pixelData) { - materialGroup.var("SpecularReflectance", pixelData.specularReflectance, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f"); - }); + displayedData |= displayValues( + requiredInputs, + {"SpecularTransmissionAlbedo"}, + [&materialGroup](PixelData& pixelData) + { + materialGroup.var( + "SpecularTransmissionAlbedo", + pixelData.specularTransmissionAlbedo, + 0.f, + std::numeric_limits::max(), + 0.001f, + false, + "%.6f" + ); + } + ); + + displayedData |= displayValues( + requiredInputs, + {"SpecularReflectance"}, + [&materialGroup](PixelData& pixelData) { + materialGroup.var( + "SpecularReflectance", pixelData.specularReflectance, 0.f, std::numeric_limits::max(), 0.001f, false, "%.6f" + ); + } + ); - displayedData |= displayValues(requiredInputs, { "IsTransmissive" }, [&materialGroup](PixelData& pixelData) { - materialGroup.checkbox("IsTransmissive", pixelData.isTransmissive); - }); + displayedData |= displayValues( + requiredInputs, + {"IsTransmissive"}, + [&materialGroup](PixelData& pixelData) { materialGroup.checkbox("IsTransmissive", pixelData.isTransmissive); } + ); - if (displayedData == false) materialGroup.text("No input data"); + if (displayedData == false) + materialGroup.text("No input data"); } // Display visibility data. @@ -308,9 +463,15 @@ void PixelInspectorPass::renderUI(Gui::Widgets& widget) { switch ((HitType)pixelData.hitType) { - case HitType::Triangle: hitType = "Triangle"; break; - case HitType::Curve: hitType = "Curve"; break; - default: hitType = "Unknown"; FALCOR_ASSERT(false); + case HitType::Triangle: + hitType = "Triangle"; + break; + case HitType::Curve: + hitType = "Curve"; + break; + default: + hitType = "Unknown"; + FALCOR_ASSERT(false); } } @@ -346,21 +507,15 @@ void PixelInspectorPass::renderUI(Gui::Widgets& widget) void PixelInspectorPass::setScene(RenderContext* pRenderContext, const ref& pScene) { mpScene = pScene; + + recreatePrograms(); +} + +void PixelInspectorPass::recreatePrograms() +{ mpProgram = nullptr; mpVars = nullptr; mpPixelDataBuffer = nullptr; - - if (mpScene) - { - Program::Desc desc; - desc.addShaderModules(mpScene->getShaderModules()); - desc.addShaderLibrary(kShaderFile).csEntry("main"); - desc.addTypeConformances(mpScene->getTypeConformances()); - desc.setCompilerFlags(Program::CompilerFlags::TreatWarningsAsErrors); - - mpProgram = ComputeProgram::create(mpDevice, desc, mpScene->getSceneDefines()); - mpState->setProgram(mpProgram); - } } bool PixelInspectorPass::onMouseEvent(const MouseEvent& mouseEvent) diff --git a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h index a9dbbd25c..c079cd6d9 100644 --- a/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h +++ b/Source/RenderPasses/PixelInspectorPass/PixelInspectorPass.h @@ -31,17 +31,23 @@ using namespace Falcor; -/** Pass extracting material information for the currently selected pixel. -*/ +/** + * Pass extracting material information for the currently selected pixel. + */ class PixelInspectorPass : public RenderPass { public: - FALCOR_PLUGIN_CLASS(PixelInspectorPass, "PixelInspectorPass", { - "Inspect geometric and material properties at a given pixel.\n" - "Left-mouse click on a pixel to select it.\n" - }); + FALCOR_PLUGIN_CLASS( + PixelInspectorPass, + "PixelInspectorPass", + {"Inspect geometric and material properties at a given pixel.\n" + "Left-mouse click on a pixel to select it.\n"} + ); - static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } + static ref create(ref pDevice, const Properties& props) + { + return make_ref(pDevice, props); + } PixelInspectorPass(ref pDevice, const Properties& props); @@ -52,21 +58,23 @@ class PixelInspectorPass : public RenderPass virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; private: + void recreatePrograms(); + // Internal state - ref mpScene; - ref mpProgram; - ref mpState; - ref mpVars; + ref mpScene; + ref mpProgram; + ref mpState; + ref mpVars; - ref mpPixelDataBuffer; + ref mpPixelDataBuffer; - float2 mCursorPosition = float2(0.0f); - float2 mSelectedCursorPosition = float2(0.0f); + float2 mCursorPosition = float2(0.0f); + float2 mSelectedCursorPosition = float2(0.0f); std::unordered_map mAvailableInputs; std::unordered_map mIsInputInBounds; // UI variables - uint2 mSelectedPixel = uint2(0u); - bool mScaleInputsToWindow = false; - bool mUseContinuousPicking = false; + uint2 mSelectedPixel = uint2(0u); + bool mScaleInputsToWindow = false; + bool mUseContinuousPicking = false; }; diff --git a/Source/RenderPasses/RTXDIPass/FinalShading.cs.slang b/Source/RenderPasses/RTXDIPass/FinalShading.cs.slang index 354e44cf6..17914da45 100644 --- a/Source/RenderPasses/RTXDIPass/FinalShading.cs.slang +++ b/Source/RenderPasses/RTXDIPass/FinalShading.cs.slang @@ -40,8 +40,9 @@ RWTexture2D gSpecularReflectance; #define is_valid(name) (is_valid_##name != 0) -/** Perform shading with final samples from ReSTIR. -*/ +/** + * Perform shading with final samples from ReSTIR. + */ struct FinalShading { // Static configuration. @@ -53,7 +54,8 @@ struct FinalShading void execute(const uint2 pixel) { - if (any(pixel >= frameDim)) return; + if (any(pixel >= frameDim)) + return; float3 color = {}; float3 emission = {}; @@ -83,7 +85,7 @@ struct FinalShading { // Create a DXR 1.1 query object to trace a ray (the <1> means use alpha testing) SceneRayQuery<1> rayQuery; - const Ray ray = Ray(sd.computeNewRayOrigin(), dir, 0.f, distance); + const Ray ray = Ray(sd.computeRayOrigin(), dir, 0.f, distance); if (!rayQuery.traceVisibilityRay(ray, RAY_FLAG_NONE, 0xff)) { valid = false; @@ -136,19 +138,25 @@ struct FinalShading } // Write active outputs. - if (is_valid(gColor)) gColor[pixel] = float4(color, 1.f); - if (is_valid(gEmission)) gEmission[pixel] = float4(emission, 1.f); - if (is_valid(gDiffuseReflectance)) gDiffuseReflectance[pixel] = float4(diffuseReflectance, 1.f); - if (is_valid(gDiffuseIllumination)) gDiffuseIllumination[pixel] = float4(diffuseIllumination, hitT); - if (is_valid(gSpecularReflectance)) gSpecularReflectance[pixel] = float4(specularReflectance, 1.f); - if (is_valid(gSpecularIllumination)) gSpecularIllumination[pixel] = float4(specularIllumination, hitT); + if (is_valid(gColor)) + gColor[pixel] = float4(color, 1.f); + if (is_valid(gEmission)) + gEmission[pixel] = float4(emission, 1.f); + if (is_valid(gDiffuseReflectance)) + gDiffuseReflectance[pixel] = float4(diffuseReflectance, 1.f); + if (is_valid(gDiffuseIllumination)) + gDiffuseIllumination[pixel] = float4(diffuseIllumination, hitT); + if (is_valid(gSpecularReflectance)) + gSpecularReflectance[pixel] = float4(specularReflectance, 1.f); + if (is_valid(gSpecularIllumination)) + gSpecularIllumination[pixel] = float4(specularIllumination, hitT); } }; ParameterBlock gFinalShading; [numthreads(16, 16, 1)] -void main(uint3 dispatchThreadId : SV_DispatchThreadID) +void main(uint3 dispatchThreadId: SV_DispatchThreadID) { gFinalShading.execute(dispatchThreadId.xy); } diff --git a/Source/RenderPasses/RTXDIPass/LoadShadingData.slang b/Source/RenderPasses/RTXDIPass/LoadShadingData.slang index f1da39ebc..687331a53 100644 --- a/Source/RenderPasses/RTXDIPass/LoadShadingData.slang +++ b/Source/RenderPasses/RTXDIPass/LoadShadingData.slang @@ -31,8 +31,9 @@ __exported import Scene.HitInfo; import Scene.Material.ShadingUtils; import Utils.Math.MathHelpers; -/** Determine hints to use when creating the material instance. -*/ +/** + * Determine hints to use when creating the material instance. + */ uint getMaterialInstanceHints() { uint hints = 0; @@ -42,16 +43,24 @@ uint getMaterialInstanceHints() return hints; } -/** Helper for setting up the ShadingData struct based on loaded data. - \param[in] pixel Current pixel coordinates. - \param[in] frameDim Frame dimensions in pixel. - \param[in] camera Current camera. - \param[in] vbuffer VBuffer texture. - \param[in] lod Method for computing texture level-of-detail. - \param[out] sd ShadingData struct. - \return True if the pixel has valid data (not a background pixel). Note sd.V is always valid. -*/ -bool loadShadingData(const uint2 pixel, const uint2 frameDim, const Camera camera, Texture2D vbuffer, const ITextureSampler lod, out ShadingData sd) +/** + * Helper for setting up the ShadingData struct based on loaded data. + * @param[in] pixel Current pixel coordinates. + * @param[in] frameDim Frame dimensions in pixel. + * @param[in] camera Current camera. + * @param[in] vbuffer VBuffer texture. + * @param[in] lod Method for computing texture level-of-detail. + * @param[out] sd ShadingData struct. + * @return True if the pixel has valid data (not a background pixel). Note sd.V is always valid. + */ +bool loadShadingData( + const uint2 pixel, + const uint2 frameDim, + const Camera camera, + Texture2D vbuffer, + const ITextureSampler lod, + out ShadingData sd +) { sd = {}; diff --git a/Source/RenderPasses/RTXDIPass/PrepareSurfaceData.cs.slang b/Source/RenderPasses/RTXDIPass/PrepareSurfaceData.cs.slang index 79185d3f0..677b4188a 100644 --- a/Source/RenderPasses/RTXDIPass/PrepareSurfaceData.cs.slang +++ b/Source/RenderPasses/RTXDIPass/PrepareSurfaceData.cs.slang @@ -29,8 +29,9 @@ import LoadShadingData; import Utils.Color.ColorHelpers; import Rendering.RTXDI.RTXDI; -/** Prepares RTXDI surface data. -*/ +/** + * Prepares RTXDI surface data. + */ struct PrepareSurfaceData { Texture2D vbuffer; @@ -40,7 +41,8 @@ struct PrepareSurfaceData void execute(const uint2 pixel) { - if (any(pixel >= frameDim)) return; + if (any(pixel >= frameDim)) + return; ShadingData sd; let lod = ExplicitLodTextureSampler(0.f); // TODO: Implement texture level-of-detail. @@ -56,7 +58,14 @@ struct PrepareSurfaceData // RTXDI uses a simple material model with only diffuse and specular reflection lobes. // We query the BSDF for the diffuse albedo and specular reflectance, and use their luminances as weights. // Note: Final shading uses the full material model, the simplified model is only used for resampling purposes. - gRTXDI.setSurfaceData(pixel, sd.computeNewRayOrigin(), bsdfProperties.guideNormal, bsdfProperties.diffuseReflectionAlbedo, bsdfProperties.specularReflectance, bsdfProperties.roughness); + gRTXDI.setSurfaceData( + pixel, + sd.computeRayOrigin(), + bsdfProperties.guideNormal, + bsdfProperties.diffuseReflectionAlbedo, + bsdfProperties.specularReflectance, + bsdfProperties.roughness + ); } else { @@ -68,7 +77,7 @@ struct PrepareSurfaceData ParameterBlock gPrepareSurfaceData; [numthreads(16, 16, 1)] -void main(uint3 dispatchThreadId : SV_DispatchThreadID) +void main(uint3 dispatchThreadId: SV_DispatchThreadID) { gPrepareSurfaceData.execute(dispatchThreadId.xy); } diff --git a/Source/RenderPasses/RTXDIPass/RTXDIPass.cpp b/Source/RenderPasses/RTXDIPass/RTXDIPass.cpp index 75f584e8e..22031bd14 100644 --- a/Source/RenderPasses/RTXDIPass/RTXDIPass.cpp +++ b/Source/RenderPasses/RTXDIPass/RTXDIPass.cpp @@ -33,35 +33,35 @@ using namespace Falcor; namespace { - const std::string kPrepareSurfaceDataFile = "RenderPasses/RTXDIPass/PrepareSurfaceData.cs.slang"; - const std::string kFinalShadingFile = "RenderPasses/RTXDIPass/FinalShading.cs.slang"; - - const std::string kShaderModel = "6_5"; - - const std::string kInputVBuffer = "vbuffer"; - const std::string kInputTexGrads = "texGrads"; - const std::string kInputMotionVectors = "mvec"; - - const Falcor::ChannelList kInputChannels = - { - { kInputVBuffer, "gVBuffer", "Visibility buffer in packed format" }, - { kInputTexGrads, "gTextureGrads", "Texture gradients", true /* optional */ }, - { kInputMotionVectors, "gMotionVector", "Motion vector buffer (float format)", true /* optional */ }, - }; - - const Falcor::ChannelList kOutputChannels = - { - { "color", "gColor", "Final color", true /* optional */, ResourceFormat::RGBA32Float }, - { "emission", "gEmission", "Emissive color", true /* optional */, ResourceFormat::RGBA32Float }, - { "diffuseIllumination", "gDiffuseIllumination", "Diffuse illumination", true /* optional */, ResourceFormat::RGBA32Float }, - { "diffuseReflectance", "gDiffuseReflectance", "Diffuse reflectance", true /* optional */, ResourceFormat::RGBA32Float }, - { "specularIllumination", "gSpecularIllumination", "Specular illumination", true /* optional */, ResourceFormat::RGBA32Float }, - { "specularReflectance", "gSpecularReflectance", "Specular reflectance", true /* optional */, ResourceFormat::RGBA32Float }, - }; - - // Scripting options. - const char* kOptions = "options"; -} +const std::string kPrepareSurfaceDataFile = "RenderPasses/RTXDIPass/PrepareSurfaceData.cs.slang"; +const std::string kFinalShadingFile = "RenderPasses/RTXDIPass/FinalShading.cs.slang"; + +const std::string kInputVBuffer = "vbuffer"; +const std::string kInputTexGrads = "texGrads"; +const std::string kInputMotionVectors = "mvec"; + +const Falcor::ChannelList kInputChannels = { + // clang-format off + { kInputVBuffer, "gVBuffer", "Visibility buffer in packed format" }, + { kInputTexGrads, "gTextureGrads", "Texture gradients", true /* optional */ }, + { kInputMotionVectors, "gMotionVector", "Motion vector buffer (float format)", true /* optional */ }, + // clang-format on +}; + +const Falcor::ChannelList kOutputChannels = { + // clang-format off + { "color", "gColor", "Final color", true /* optional */, ResourceFormat::RGBA32Float }, + { "emission", "gEmission", "Emissive color", true /* optional */, ResourceFormat::RGBA32Float }, + { "diffuseIllumination", "gDiffuseIllumination", "Diffuse illumination", true /* optional */, ResourceFormat::RGBA32Float }, + { "diffuseReflectance", "gDiffuseReflectance", "Diffuse reflectance", true /* optional */, ResourceFormat::RGBA32Float }, + { "specularIllumination", "gSpecularIllumination", "Specular illumination", true /* optional */, ResourceFormat::RGBA32Float }, + { "specularReflectance", "gSpecularReflectance", "Specular reflectance", true /* optional */, ResourceFormat::RGBA32Float }, + // clang-format on +}; + +// Scripting options. +const char* kOptions = "options"; +} // namespace // What passes does this DLL expose? Register them here extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) @@ -69,8 +69,7 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); } -RTXDIPass::RTXDIPass(ref pDevice, const Properties& props) - : RenderPass(pDevice) +RTXDIPass::RTXDIPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { parseProperties(props); } @@ -79,8 +78,10 @@ void RTXDIPass::parseProperties(const Properties& props) { for (const auto& [key, value] : props) { - if (key == kOptions) mOptions = value; - else logWarning("Unknown property '{}' in RTXDIPass properties.", key); + if (key == kOptions) + mOptions = value; + else + logWarning("Unknown property '{}' in RTXDIPass properties.", key); } } @@ -103,6 +104,13 @@ void RTXDIPass::execute(RenderContext* pRenderContext, const RenderData& renderD return; } + // Check for scene changes that require shader recompilation. + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded) || + is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) + { + recreatePrograms(); + } + FALCOR_ASSERT(mpRTXDI); const auto& pVBuffer = renderData.getTexture(kInputVBuffer); @@ -137,8 +145,8 @@ void RTXDIPass::setScene(RenderContext* pRenderContext, const ref& pScene { mpScene = pScene; mpRTXDI = nullptr; - mpPrepareSurfaceDataPass = nullptr; - mpFinalShadingPass = nullptr; + + recreatePrograms(); if (mpScene) { @@ -174,10 +182,17 @@ void RTXDIPass::renderUI(Gui::Widgets& widget) if (mpRTXDI) { mOptionsChanged = mpRTXDI->renderUI(widget); - if (mOptionsChanged) mOptions = mpRTXDI->getOptions(); + if (mOptionsChanged) + mOptions = mpRTXDI->getOptions(); } } +void RTXDIPass::recreatePrograms() +{ + mpPrepareSurfaceDataPass = nullptr; + mpFinalShadingPass = nullptr; +} + void RTXDIPass::prepareSurfaceData(RenderContext* pRenderContext, const ref& pVBuffer) { FALCOR_ASSERT(mpRTXDI); @@ -187,9 +202,9 @@ void RTXDIPass::prepareSurfaceData(RenderContext* pRenderContext, const refgetShaderModules()); - desc.addShaderLibrary(kPrepareSurfaceDataFile).setShaderModel(kShaderModel).csEntry("main"); + desc.addShaderLibrary(kPrepareSurfaceDataFile).csEntry("main"); desc.addTypeConformances(mpScene->getTypeConformances()); auto defines = mpScene->getSceneDefines(); @@ -202,8 +217,8 @@ void RTXDIPass::prepareSurfaceData(RenderContext* pRenderContext, const refaddDefine("GBUFFER_ADJUST_SHADING_NORMALS", mGBufferAdjustShadingNormals ? "1" : "0"); auto rootVar = mpPrepareSurfaceDataPass->getRootVar(); - rootVar["gScene"] = mpScene->getParameterBlock(); - mpRTXDI->setShaderData(rootVar); + mpScene->bindShaderData(rootVar["gScene"]); + mpRTXDI->bindShaderData(rootVar); auto var = rootVar["gPrepareSurfaceData"]; var["vbuffer"] = pVBuffer; @@ -221,9 +236,9 @@ void RTXDIPass::finalShading(RenderContext* pRenderContext, const ref& if (!mpFinalShadingPass) { - Program::Desc desc; + ProgramDesc desc; desc.addShaderModules(mpScene->getShaderModules()); - desc.addShaderLibrary(kFinalShadingFile).setShaderModel(kShaderModel).csEntry("main"); + desc.addShaderLibrary(kFinalShadingFile).csEntry("main"); desc.addTypeConformances(mpScene->getTypeConformances()); auto defines = mpScene->getSceneDefines(); @@ -243,8 +258,8 @@ void RTXDIPass::finalShading(RenderContext* pRenderContext, const ref& mpFinalShadingPass->getProgram()->addDefines(getValidResourceDefines(kOutputChannels, renderData)); auto rootVar = mpFinalShadingPass->getRootVar(); - rootVar["gScene"] = mpScene->getParameterBlock(); - mpRTXDI->setShaderData(rootVar); + mpScene->bindShaderData(rootVar["gScene"]); + mpRTXDI->bindShaderData(rootVar); auto var = rootVar["gFinalShading"]; var["vbuffer"] = pVBuffer; @@ -256,7 +271,8 @@ void RTXDIPass::finalShading(RenderContext* pRenderContext, const ref& ref pTex = renderData.getTexture(channel.name); rootVar[channel.texname] = pTex; }; - for (const auto& channel : kOutputChannels) bind(channel); + for (const auto& channel : kOutputChannels) + bind(channel); mpFinalShadingPass->execute(pRenderContext, mFrameDim.x, mFrameDim.y); } diff --git a/Source/RenderPasses/RTXDIPass/RTXDIPass.h b/Source/RenderPasses/RTXDIPass/RTXDIPass.h index 8cfe0dede..37459e562 100644 --- a/Source/RenderPasses/RTXDIPass/RTXDIPass.h +++ b/Source/RenderPasses/RTXDIPass/RTXDIPass.h @@ -33,21 +33,22 @@ using namespace Falcor; -/** This RenderPass provides a simple example of how to use the RTXDI module - available in the "Source/Falcor/Rendering/RTXDI/" directory. - - See the RTXDI.h header for more explicit instructions. - - This pass consists of two compute passes: - - - PrepareSurfaceData.slang takes in a Falcor VBuffer (e.g. from the GBuffer - render pass) and sets up the surface data required by RTXDI to perform - light sampling. - - FinalShading.slang takes the final RTXDI light samples, checks visiblity - and shades the pixels by evaluating the actual material's BSDF. - - Please see the README on how to install the RTXDI SDK. -*/ +/** + * This RenderPass provides a simple example of how to use the RTXDI module + * available in the "Source/Falcor/Rendering/RTXDI/" directory. + * + * See the RTXDI.h header for more explicit instructions. + * + * This pass consists of two compute passes: + * + * - PrepareSurfaceData.slang takes in a Falcor VBuffer (e.g. from the GBuffer + * render pass) and sets up the surface data required by RTXDI to perform + * light sampling. + * - FinalShading.slang takes the final RTXDI light samples, checks visiblity + * and shades the pixels by evaluating the actual material's BSDF. + * + * Please see the README on how to install the RTXDI SDK. + */ class RTXDIPass : public RenderPass { public: @@ -68,19 +69,19 @@ class RTXDIPass : public RenderPass private: void parseProperties(const Properties& props); - + void recreatePrograms(); void prepareSurfaceData(RenderContext* pRenderContext, const ref& pVBuffer); void finalShading(RenderContext* pRenderContext, const ref& pVBuffer, const RenderData& renderData); - ref mpScene; + ref mpScene; - std::unique_ptr mpRTXDI; - RTXDI::Options mOptions; + std::unique_ptr mpRTXDI; + RTXDI::Options mOptions; - ref mpPrepareSurfaceDataPass; - ref mpFinalShadingPass; + ref mpPrepareSurfaceDataPass; + ref mpFinalShadingPass; - uint2 mFrameDim = { 0, 0 }; - bool mOptionsChanged = false; - bool mGBufferAdjustShadingNormals = false; + uint2 mFrameDim = {0, 0}; + bool mOptionsChanged = false; + bool mGBufferAdjustShadingNormals = false; }; diff --git a/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.cpp b/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.cpp index 422521d93..99d0b150d 100644 --- a/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.cpp +++ b/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.cpp @@ -32,10 +32,7 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr registry.registerClass(); } -RenderPassTemplate::RenderPassTemplate(ref pDevice, const Properties& props) - : RenderPass(pDevice) -{ -} +RenderPassTemplate::RenderPassTemplate(ref pDevice, const Properties& props) : RenderPass(pDevice) {} Properties RenderPassTemplate::getProperties() const { @@ -46,8 +43,8 @@ RenderPassReflection RenderPassTemplate::reflect(const CompileData& compileData) { // Define the required resources here RenderPassReflection reflector; - //reflector.addOutput("dst"); - //reflector.addInput("src"); + // reflector.addOutput("dst"); + // reflector.addInput("src"); return reflector; } @@ -57,6 +54,4 @@ void RenderPassTemplate::execute(RenderContext* pRenderContext, const RenderData // auto& pTexture = renderData.getTexture("src"); } -void RenderPassTemplate::renderUI(Gui::Widgets& widget) -{ -} +void RenderPassTemplate::renderUI(Gui::Widgets& widget) {} diff --git a/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.h b/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.h index 2dc956b9f..32f45e419 100644 --- a/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.h +++ b/Source/RenderPasses/RenderPassTemplate/RenderPassTemplate.h @@ -36,7 +36,10 @@ class RenderPassTemplate : public RenderPass public: FALCOR_PLUGIN_CLASS(RenderPassTemplate, "RenderPassTemplate", "Insert pass description here."); - static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } + static ref create(ref pDevice, const Properties& props) + { + return make_ref(pDevice, props); + } RenderPassTemplate(ref pDevice, const Properties& props); diff --git a/Source/RenderPasses/SDFEditor/GUIPass.ps.slang b/Source/RenderPasses/SDFEditor/GUIPass.ps.slang index 9ae0fa61c..2f7882c89 100644 --- a/Source/RenderPasses/SDFEditor/GUIPass.ps.slang +++ b/Source/RenderPasses/SDFEditor/GUIPass.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -38,24 +38,26 @@ import Utils.Math.MatrixUtils; import Marker2DSet; import SDFEditorTypes; - -Texture2D gInputColor; ///< Input color (2D GUI will be blended on top). -Texture2D gVBuffer; ///< Fullscreen V-buffer for the primary hits. -Texture2D gLinearZ; ///< Linear Z constructed from GBuffer. +/// Input color (2D GUI will be blended on top). +Texture2D gInputColor; +/// Fullscreen V-buffer for the primary hits. +Texture2D gVBuffer; +/// Linear Z constructed from GBuffer. +Texture2D gLinearZ; struct GUIPass { uint2 resolution; uint2 mousePos; - Marker2DSet markerSet; - RWStructuredBuffer pickingData; - StructuredBuffer editingPrimitiveData; - Buffer gridInstanceIDs; - SDFBBRenderSettings bbRenderSettings; - uint gridInstanceCount; - uint ui2DActive; - SDFGridPlane gridPlane; - SDFGridPlane symmetryPlane; + Marker2DSet markerSet; + RWStructuredBuffer pickingData; + StructuredBuffer editingPrimitiveData; + Buffer gridInstanceIDs; + SDFBBRenderSettings bbRenderSettings; + uint gridInstanceCount; + uint ui2DActive; + SDFGridPlane gridPlane; + SDFGridPlane symmetryPlane; }; ParameterBlock gGUIPass; @@ -86,7 +88,17 @@ bool iBoundingBox(in float3 ro, in float3 rd, in float3 halfExtent, in float e, return abs(sd) < 0.001f; } -bool intersectGridPlane(float3 rayOrigin, float3 rayDir, float3 pointOnPlane, float3 normal, float3 rightVec, out float distanceToIntersection, float gridWidth = 0.1f, float gridScale = 1.0f, float gridSize = 1.0f) +bool intersectGridPlane( + float3 rayOrigin, + float3 rayDir, + float3 pointOnPlane, + float3 normal, + float3 rightVec, + out float distanceToIntersection, + float gridWidth = 0.1f, + float gridScale = 1.0f, + float gridSize = 1.0f +) { distanceToIntersection = -dot(normal, rayOrigin - pointOnPlane) / dot(normal, rayDir); if (distanceToIntersection < 0.0f) @@ -135,7 +147,8 @@ float4 traceSDFBoundingBox(Ray cameraRay, uint instanceID, float distanceToHitPo if (iBoundingBox(cameraRay.origin, cameraRay.dir, float3(0.5f), gGUIPass.bbRenderSettings.edgeThickness, t)) { float alpha = t > distanceToHitPoint ? 0.2f : 1.0f; - color = (gGUIPass.bbRenderSettings.selectedInstanceID == instanceID) ? float4(0.0f, 1.0f, 0.0f, alpha) : float4(1.0f, 0.0f, 0.0f, alpha); + color = (gGUIPass.bbRenderSettings.selectedInstanceID == instanceID) ? float4(0.0f, 1.0f, 0.0f, alpha) + : float4(1.0f, 0.0f, 0.0f, alpha); } return color; @@ -188,7 +201,17 @@ float4 renderGridPlanes(const Ray cameraRayAtPixel, const HitInfo hit, const flo if (gGUIPass.gridPlane.active == 1) { float distanceToIntersection; - bool hitGridPlane = intersectGridPlane(cameraRayAtPixel.origin, cameraRayAtPixel.dir, gGUIPass.gridPlane.position, gGUIPass.gridPlane.normal, gGUIPass.gridPlane.rightVector, distanceToIntersection, gGUIPass.gridPlane.gridLineWidth, gGUIPass.gridPlane.gridScale, gGUIPass.gridPlane.planeSize); + bool hitGridPlane = intersectGridPlane( + cameraRayAtPixel.origin, + cameraRayAtPixel.dir, + gGUIPass.gridPlane.position, + gGUIPass.gridPlane.normal, + gGUIPass.gridPlane.rightVector, + distanceToIntersection, + gGUIPass.gridPlane.gridLineWidth, + gGUIPass.gridPlane.gridScale, + gGUIPass.gridPlane.planeSize + ); // Compute distance from the current pixel's position on the grid plane the the position of the plane where the mouse is. float distanceToIntersectionAtMouse = gGUIPass.gridPlane.intersect(cameraRayMouse.origin, cameraRayMouse.dir); @@ -211,7 +234,8 @@ float4 renderGridPlanes(const Ray cameraRayAtPixel, const HitInfo hit, const flo { alpha = hitGridPlane ? gGUIPass.gridPlane.color.a : 0.0f; - if (distanceToIntersection > distanceToHitPoint) // Is the point on the grid plane through the current pixel behind already rendered geometry? + if (distanceToIntersection > distanceToHitPoint) // Is the point on the grid plane through the current pixel behind already + // rendered geometry? { alpha = alpha * alphaFactorForHiddenGrid; } @@ -222,14 +246,26 @@ float4 renderGridPlanes(const Ray cameraRayAtPixel, const HitInfo hit, const flo // Mix with circular blob showing where the mouse is. float radius = gGUIPass.gridPlane.planeSize * 0.1f; float falloff = (radius - len) / radius; - float4 circularBlobColor = (len < radius&& gGUIPass.ui2DActive == 0) ? (float4(1.0) - gGUIPass.gridPlane.color) * falloff : float4(0.0); - finalColor = gridColorPlusBackground + circularBlobColor * (distanceToIntersection > distanceToHitPoint ? factorForHiddenBlob : 1.0); + float4 circularBlobColor = + (len < radius && gGUIPass.ui2DActive == 0) ? (float4(1.0) - gGUIPass.gridPlane.color) * falloff : float4(0.0); + finalColor = + gridColorPlusBackground + circularBlobColor * (distanceToIntersection > distanceToHitPoint ? factorForHiddenBlob : 1.0); } if (gGUIPass.symmetryPlane.active == 1) { float distanceToIntersection; - bool hitSymmetryPlane = intersectGridPlane(cameraRayAtPixel.origin, cameraRayAtPixel.dir, gGUIPass.symmetryPlane.position, gGUIPass.symmetryPlane.normal, gGUIPass.symmetryPlane.rightVector, distanceToIntersection, gGUIPass.symmetryPlane.gridLineWidth, gGUIPass.symmetryPlane.gridScale, gGUIPass.symmetryPlane.planeSize); + bool hitSymmetryPlane = intersectGridPlane( + cameraRayAtPixel.origin, + cameraRayAtPixel.dir, + gGUIPass.symmetryPlane.position, + gGUIPass.symmetryPlane.normal, + gGUIPass.symmetryPlane.rightVector, + distanceToIntersection, + gGUIPass.symmetryPlane.gridLineWidth, + gGUIPass.symmetryPlane.gridScale, + gGUIPass.symmetryPlane.planeSize + ); // Compute distance from the current pixel's position on the grid plane the the position of the plane where the mouse is. float distanceToIntersectionAtMouse = gGUIPass.symmetryPlane.intersect(cameraRayMouse.origin, cameraRayMouse.dir); @@ -250,7 +286,7 @@ float4 renderGridPlanes(const Ray cameraRayAtPixel, const HitInfo hit, const flo return finalColor; } -float4 psMain(float2 texC : TEXCOORD) : SV_TARGET +float4 psMain(float2 texC: TEXCOORD) : SV_TARGET { const uint2 pixel = uint2(texC * float2(gGUIPass.resolution)); const float diagonal = length(gGUIPass.resolution); @@ -313,7 +349,14 @@ float4 psMain(float2 texC : TEXCOORD) : SV_TARGET float3 previewColor = float3(1.0f, 0.0f, 0.0f); float alpha = 1.0f; - if (sdfIntersectPrimitive(cameraRay.origin, cameraRay.dir, editingData.primitiveBB.center(), editingData.primitiveBB.extent(), editingData.primitive, t)) + if (sdfIntersectPrimitive( + cameraRay.origin, + cameraRay.dir, + editingData.primitiveBB.center(), + editingData.primitiveBB.extent(), + editingData.primitive, + t + )) { alpha = t > distanceToHitPoint ? hiddenHitAlpha : hitAlpha; hit = true; @@ -321,7 +364,14 @@ float4 psMain(float2 texC : TEXCOORD) : SV_TARGET if (gGUIPass.symmetryPlane.active == 1) { - if (sdfIntersectPrimitive(cameraRay.origin, cameraRay.dir, editingData.symmetryPrimitiveBB.center(), editingData.symmetryPrimitiveBB.extent(), editingData.symmetryPrimitive, t)) + if (sdfIntersectPrimitive( + cameraRay.origin, + cameraRay.dir, + editingData.symmetryPrimitiveBB.center(), + editingData.symmetryPrimitiveBB.extent(), + editingData.symmetryPrimitive, + t + )) { if (t <= distanceToHitPoint) { @@ -338,7 +388,8 @@ float4 psMain(float2 texC : TEXCOORD) : SV_TARGET if (editingData.scalingAxis < uint(SDFEditorAxis::Count)) { - float3 rayOriginPrimLocal = mul(editingData.primitive.invRotationScale, (cameraRay.origin - editingData.primitive.translation)).xyz; + float3 rayOriginPrimLocal = + mul(editingData.primitive.invRotationScale, (cameraRay.origin - editingData.primitive.translation)).xyz; float3 rayDirPrimLocal = normalize(mul(editingData.primitive.invRotationScale, cameraRay.dir).xyz); float axisPreviewT = 0.0f; diff --git a/Source/RenderPasses/SDFEditor/Marker2DSet.cpp b/Source/RenderPasses/SDFEditor/Marker2DSet.cpp index ffa210194..de70ce336 100644 --- a/Source/RenderPasses/SDFEditor/Marker2DSet.cpp +++ b/Source/RenderPasses/SDFEditor/Marker2DSet.cpp @@ -32,7 +32,7 @@ void Falcor::Marker2DSet::addMarker(const Marker2DDataBlob& newMarker) { if (mMarkers.size() >= mMaxMarkerCount) { - throw RuntimeError("Number of markers exceeds the maximum number allowed!"); + FALCOR_THROW("Number of markers exceeds the maximum number allowed!"); } mMarkers.push_back(newMarker); @@ -45,9 +45,16 @@ void Falcor::Marker2DSet::clear() mDirtyBuffer = true; } -void Falcor::Marker2DSet::addSimpleMarker(const SDF2DShapeType markerType, const float size, const float2& pos, const float rotation, const float4& color) +void Falcor::Marker2DSet::addSimpleMarker( + const SDF2DShapeType markerType, + const float size, + const float2& pos, + const float rotation, + const float4& color +) { - Marker2DDataBlob markerBlob;; + Marker2DDataBlob markerBlob; + ; markerBlob.type = markerType; SimpleMarker2DData* pSimpleMarker = reinterpret_cast(markerBlob.payload.data); pSimpleMarker->transform.scale = size; @@ -69,7 +76,13 @@ void Falcor::Marker2DSet::addRoundedLine(const float2& posA, const float2& posB, addMarker(markerBlob); } -void Falcor::Marker2DSet::addVector(const float2& posA, const float2& posB, const float lineWidth, const float arrowHeight, const float4& color) +void Falcor::Marker2DSet::addVector( + const float2& posA, + const float2& posB, + const float lineWidth, + const float arrowHeight, + const float4& color +) { Marker2DDataBlob markerBlob; markerBlob.type = SDF2DShapeType::Vector; @@ -94,7 +107,13 @@ void Falcor::Marker2DSet::addTriangle(const float2& posA, const float2& posB, co addMarker(markerBlob); } -void Falcor::Marker2DSet::addRoundedBox(const float2& pos, const float2& halfSides, const float radius, const float rotation, const float4& color) +void Falcor::Marker2DSet::addRoundedBox( + const float2& pos, + const float2& halfSides, + const float radius, + const float rotation, + const float4& color +) { Marker2DDataBlob markerBlob; markerBlob.type = SDF2DShapeType::RoundedBox; @@ -107,7 +126,17 @@ void Falcor::Marker2DSet::addRoundedBox(const float2& pos, const float2& halfSid addMarker(markerBlob); } -void Falcor::Marker2DSet::addMarkerOpMarker(const SDFOperationType op, const SDF2DShapeType typeA, const float2& posA, const float markerSizeA, const SDF2DShapeType typeB, const float2& posB, const float markerSizeB, const float4& color, const float4 dimmedColor) +void Falcor::Marker2DSet::addMarkerOpMarker( + const SDFOperationType op, + const SDF2DShapeType typeA, + const float2& posA, + const float markerSizeA, + const SDF2DShapeType typeB, + const float2& posB, + const float markerSizeB, + const float4& color, + const float4 dimmedColor +) { Marker2DDataBlob markerBlob; markerBlob.type = SDF2DShapeType::MarkerOpMarker; @@ -124,7 +153,14 @@ void Falcor::Marker2DSet::addMarkerOpMarker(const SDFOperationType op, const SDF addMarker(markerBlob); } -void Falcor::Marker2DSet::addArrowFromTwoTris(const float2& startPos, const float2& endPos, const float headLength, const float headWidth, const float shaftWidth, const float4& color) +void Falcor::Marker2DSet::addArrowFromTwoTris( + const float2& startPos, + const float2& endPos, + const float headLength, + const float headWidth, + const float shaftWidth, + const float4& color +) { Marker2DDataBlob markerBlob; markerBlob.type = SDF2DShapeType::ArrowFromTwoTris; @@ -138,7 +174,16 @@ void Falcor::Marker2DSet::addArrowFromTwoTris(const float2& startPos, const floa addMarker(markerBlob); } -void Falcor::Marker2DSet::addCircleSector(const float2& pos, const float rotation, const float angle, const float minRadius, const float maxRadius, const float4& color, const float4& borderColorXYZThicknessW, ExcludeBorderFlags excludeBorderFlags) +void Falcor::Marker2DSet::addCircleSector( + const float2& pos, + const float rotation, + const float angle, + const float minRadius, + const float maxRadius, + const float4& color, + const float4& borderColorXYZThicknessW, + ExcludeBorderFlags excludeBorderFlags +) { Marker2DDataBlob markerBlob; markerBlob.type = SDF2DShapeType::CircleSector; @@ -154,7 +199,7 @@ void Falcor::Marker2DSet::addCircleSector(const float2& pos, const float rotatio addMarker(markerBlob); } -void Falcor::Marker2DSet::setShaderData(const ShaderVar& var) +void Falcor::Marker2DSet::bindShaderData(const ShaderVar& var) { updateBuffer(); @@ -176,7 +221,14 @@ void Falcor::Marker2DSet::updateBuffer() // Create a new buffer if it does not exist or if the size is too small for the markers. else if (!mpMarkerBuffer || mpMarkerBuffer->getElementCount() < (uint32_t)mMarkers.size()) { - mpMarkerBuffer = Buffer::createStructured(mpDevice, sizeof(Marker2DDataBlob), (uint32_t)mMarkers.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, mMarkers.data(), false); + mpMarkerBuffer = mpDevice->createStructuredBuffer( + sizeof(Marker2DDataBlob), + (uint32_t)mMarkers.size(), + ResourceBindFlags::ShaderResource, + MemoryType::DeviceLocal, + mMarkers.data(), + false + ); mpMarkerBuffer->setName("Marker2DSet::mpMarkerBuffer"); } // Else update the existing buffer. diff --git a/Source/RenderPasses/SDFEditor/Marker2DSet.h b/Source/RenderPasses/SDFEditor/Marker2DSet.h index 588cf547f..c137403c1 100644 --- a/Source/RenderPasses/SDFEditor/Marker2DSet.h +++ b/Source/RenderPasses/SDFEditor/Marker2DSet.h @@ -34,123 +34,163 @@ namespace Falcor { - /** Struct holding a set of Marker2DDataBlob. This is a helper struct that has functions to fill the markers depending on the shape type. - */ - class Marker2DSet - { - public: - Marker2DSet(ref pDevice, uint32_t maxMarkerCount) : mpDevice(pDevice), mMaxMarkerCount(maxMarkerCount) {} - - /** Resets the marker index to the first position. This will allow the next add-calls to add markers from the beginning again. - */ - void clear(); - - /** Add a simple marker with the specified shape, color, and transform. - \param[in] markerType The type of marker to add. - \param[in] size The size of the marker. - \param[in] pos The position of the marker. - \param[in] rotation The rotation of the marker in radians. - \param[in] color The color of the marker. - */ - void addSimpleMarker(const SDF2DShapeType markerType, const float size, const float2& pos, const float rotation, const float4& color); - - /** Add a rouned line as a marker. - \param[in] posA Start position of the line. - \param[in] posB End position of the line. - \param[in] lineWidth The with of the line. - \param[in] color The color of the line. - */ - void addRoundedLine(const float2& posA, const float2& posB, const float lineWidth, const float4& color); - - /** Add a triangle as a marker. - \param[in] posA First corner of the triangle. - \param[in] posB Second corner of the triangle. - \param[in] posC Third corner of the triangle. - \param[in] color The color of the line. - */ - void addTriangle(const float2& posA, const float2& posB, const float2& posC, const float4& color); - - /** Add a rounded box as a marker. - \param[in] pos The position of the rounded box. - \param[in] halfSides Half the length of the rounded box. - \param[in] radius The radius of the corners. - \param[in] rotation Rotation in radians. - \param[in] color The color of the line. - */ - void addRoundedBox(const float2& pos, const float2& halfSides, const float radius, const float rotation, const float4& color); - - /** Add two markers that have some part of them overlap each other and a operation that should be applied between the two. - \param[in] op The operation to use. - \param[in] typeA The shape of the first marker. - \param[in] posA Position of the first marker. - \param[in] markerSizeA Size of the first marker. - \param[in] typeB The shape of the second marker. - \param[in] posB Position of the second marker. - \param[in] markerSizeB Size of the second marker. - \param[in] color Color of the two markers. - \param[in] dimmedColor The dimmed color of the markers. - */ - void addMarkerOpMarker(const SDFOperationType op, const SDF2DShapeType typeA, const float2& posA, const float markerSizeA, const SDF2DShapeType typeB, const float2& posB, const float markerSizeB, const float4& color, const float4 dimmedColor); - - /** Add an arrow marker by using two SDF triangles. - \param[in] startPos Start position of the arrow. - \param[in] endPos End position of the arrow. - \param[in] headLength The length of the arrow head. - \param[in] headWidth The width of the arrow head. - \param[in] shaftWidth The width of the arrow shaft. - \param[in] color Color of the arrow. - */ - void addArrowFromTwoTris(const float2& startPos, const float2& endPos, const float headLength, const float headWidth, const float shaftWidth, const float4& color); - - /** Add a vector represented as an arrow. - \param[in] posA Start position of the arrow. - \param[in] posB End position of the arrow. - \param[in] lineWidth The width of the line. - \param[in] arrowHeight The height of the arrow head. - \param[in] color Color of the vector. - */ - void addVector(const float2& posA, const float2& posB, const float lineWidth, const float arrowHeight, const float4& color); - - /** Add a circle sector. Can also be cut by setting the minRadius to a value larger than zero. - \param[in] pos The center position of the circle sector as if it was a full circle. - \param[in] rotation The rotation of the circle sector. - \param[in] angle The angle of the sircle sector. - \param[in] minRadius The minimum radius where it si going to cut at. Everything with a lower radius than this will not be seen. - \param[in] maxRadius The maximum radius of the circle sector. - \param[in] color Color of the circle sector. - \param[in] borderColorXYZThicknessW Border color in the x, y, and z components and its thickness in the w component. - \param[in] excludeBorderFlags Flags for which borders should be excluded from rendering. - */ - void addCircleSector(const float2& pos, const float rotation, const float angle, const float minRadius, const float maxRadius, const float4& color, const float4& borderColorXYZThicknessW, ExcludeBorderFlags excludeBorderFlags); - - /** Retrive the list of marker objects. - \return The list of markers. - */ - const std::vector& getMarkers() const { return mMarkers; } - - /** Get the buffer that holds all markers in this set. - */ - ref getBuffer() const { return mpMarkerBuffer; } - - /** Set shader data. - */ - void setShaderData(const ShaderVar& var); - - protected: - /** Adds a Marker2D object to the buffer. Throws a runtime error when marker count exceeds the maximum marker count. - */ - void addMarker(const Marker2DDataBlob& newMarker); - - /** Update the GPU buffer with marker data if it has changed. - */ - void updateBuffer(); - - private: - ref mpDevice; - uint32_t mMaxMarkerCount; - std::vector mMarkers; - ref mpMarkerBuffer; - bool mDirtyBuffer = false; - }; -} - +/** + * Struct holding a set of Marker2DDataBlob. This is a helper struct that has functions to fill the markers depending on the shape type. + */ +class Marker2DSet +{ +public: + Marker2DSet(ref pDevice, uint32_t maxMarkerCount) : mpDevice(pDevice), mMaxMarkerCount(maxMarkerCount) {} + + /** + * Resets the marker index to the first position. This will allow the next add-calls to add markers from the beginning again. + */ + void clear(); + + /** + * Add a simple marker with the specified shape, color, and transform. + * @param[in] markerType The type of marker to add. + * @param[in] size The size of the marker. + * @param[in] pos The position of the marker. + * @param[in] rotation The rotation of the marker in radians. + * @param[in] color The color of the marker. + */ + void addSimpleMarker(const SDF2DShapeType markerType, const float size, const float2& pos, const float rotation, const float4& color); + + /** + * Add a rouned line as a marker. + * @param[in] posA Start position of the line. + * @param[in] posB End position of the line. + * @param[in] lineWidth The with of the line. + * @param[in] color The color of the line. + */ + void addRoundedLine(const float2& posA, const float2& posB, const float lineWidth, const float4& color); + + /** + * Add a triangle as a marker. + * @param[in] posA First corner of the triangle. + * @param[in] posB Second corner of the triangle. + * @param[in] posC Third corner of the triangle. + * @param[in] color The color of the line. + */ + void addTriangle(const float2& posA, const float2& posB, const float2& posC, const float4& color); + + /** + * Add a rounded box as a marker. + * @param[in] pos The position of the rounded box. + * @param[in] halfSides Half the length of the rounded box. + * @param[in] radius The radius of the corners. + * @param[in] rotation Rotation in radians. + * @param[in] color The color of the line. + */ + void addRoundedBox(const float2& pos, const float2& halfSides, const float radius, const float rotation, const float4& color); + + /** + * Add two markers that have some part of them overlap each other and a operation that should be applied between the two. + * @param[in] op The operation to use. + * @param[in] typeA The shape of the first marker. + * @param[in] posA Position of the first marker. + * @param[in] markerSizeA Size of the first marker. + * @param[in] typeB The shape of the second marker. + * @param[in] posB Position of the second marker. + * @param[in] markerSizeB Size of the second marker. + * @param[in] color Color of the two markers. + * @param[in] dimmedColor The dimmed color of the markers. + */ + void addMarkerOpMarker( + const SDFOperationType op, + const SDF2DShapeType typeA, + const float2& posA, + const float markerSizeA, + const SDF2DShapeType typeB, + const float2& posB, + const float markerSizeB, + const float4& color, + const float4 dimmedColor + ); + + /** + * Add an arrow marker by using two SDF triangles. + * @param[in] startPos Start position of the arrow. + * @param[in] endPos End position of the arrow. + * @param[in] headLength The length of the arrow head. + * @param[in] headWidth The width of the arrow head. + * @param[in] shaftWidth The width of the arrow shaft. + * @param[in] color Color of the arrow. + */ + void addArrowFromTwoTris( + const float2& startPos, + const float2& endPos, + const float headLength, + const float headWidth, + const float shaftWidth, + const float4& color + ); + + /** + * Add a vector represented as an arrow. + * @param[in] posA Start position of the arrow. + * @param[in] posB End position of the arrow. + * @param[in] lineWidth The width of the line. + * @param[in] arrowHeight The height of the arrow head. + * @param[in] color Color of the vector. + */ + void addVector(const float2& posA, const float2& posB, const float lineWidth, const float arrowHeight, const float4& color); + + /** + * Add a circle sector. Can also be cut by setting the minRadius to a value larger than zero. + * @param[in] pos The center position of the circle sector as if it was a full circle. + * @param[in] rotation The rotation of the circle sector. + * @param[in] angle The angle of the sircle sector. + * @param[in] minRadius The minimum radius where it si going to cut at. Everything with a lower radius than this will not be seen. + * @param[in] maxRadius The maximum radius of the circle sector. + * @param[in] color Color of the circle sector. + * @param[in] borderColorXYZThicknessW Border color in the x, y, and z components and its thickness in the w component. + * @param[in] excludeBorderFlags Flags for which borders should be excluded from rendering. + */ + void addCircleSector( + const float2& pos, + const float rotation, + const float angle, + const float minRadius, + const float maxRadius, + const float4& color, + const float4& borderColorXYZThicknessW, + ExcludeBorderFlags excludeBorderFlags + ); + + /** + * Retrive the list of marker objects. + * @return The list of markers. + */ + const std::vector& getMarkers() const { return mMarkers; } + + /** + * Get the buffer that holds all markers in this set. + */ + ref getBuffer() const { return mpMarkerBuffer; } + + /** + * Set shader data. + */ + void bindShaderData(const ShaderVar& var); + +protected: + /** + * Adds a Marker2D object to the buffer. Throws a runtime error when marker count exceeds the maximum marker count. + */ + void addMarker(const Marker2DDataBlob& newMarker); + + /** + * Update the GPU buffer with marker data if it has changed. + */ + void updateBuffer(); + +private: + ref mpDevice; + uint32_t mMaxMarkerCount; + std::vector mMarkers; + ref mpMarkerBuffer; + bool mDirtyBuffer = false; +}; +} // namespace Falcor diff --git a/Source/RenderPasses/SDFEditor/Marker2DSet.slang b/Source/RenderPasses/SDFEditor/Marker2DSet.slang index 5cbf5408c..6705eaa7d 100644 --- a/Source/RenderPasses/SDFEditor/Marker2DSet.slang +++ b/Source/RenderPasses/SDFEditor/Marker2DSet.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,27 +26,38 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Structs for markers (2D GUI) for the SDF editor. -*/ +/** + * Structs for markers (2D GUI) for the SDF editor. + */ __exported import Marker2DTypes; __exported import Utils.SDF.SDF2DDraw; __exported import Utils.SDF.SDF2DPrimitives; __exported import Utils.SDF.SDFOperations; import Utils.Math.MathHelpers; -/** Struct for a 2D marker that is being set on the CPU side, and sent to the shader for rendering. - It works by holding a blob of data and a type. The blob of data can be set manually or cast to a specific marker type. -*/ +/** + * Struct for a 2D marker that is being set on the CPU side, and sent to the shader for rendering. + * It works by holding a blob of data and a type. The blob of data can be set manually or cast to a specific marker type. + */ +// clang-format off extension Marker2DDataBlob { - /** Internal helper function to calculate the signed distance from a basic marker object and also the blend factor. - \param[in] pixelPos The pixel position. - \param[in] marker The basic marker object. - \param[in,out] smoothFactor The smoothing factor used to calculate the blend factor. - \param[out] distance The signed distance to the marker. - \param[out] blendFactor The blend radius factor. - */ - void calcDistanceandAndBlendFactorFromBasicMarker(const uint2 pixelPos, BasicMarker2D marker, inout float smoothFactor, out float distance, out float blendRadiusFactor) + /** + * Internal helper function to calculate the signed distance from a basic marker object and also the blend + * factor. + * @param[in] pixelPos The pixel position. + * @param[in] marker The basic marker object. + * @param[in,out] smoothFactor The smoothing factor used to calculate the blend factor. + * @param[out] distance The signed distance to the marker. + * @param[out] blendFactor The blend radius factor. + */ + void calcDistanceandAndBlendFactorFromBasicMarker( + const uint2 pixelPos, + BasicMarker2D marker, + inout float smoothFactor, + out float distance, + out float blendRadiusFactor + ) { float2 newPos; switch (marker.type) @@ -72,12 +83,14 @@ extension Marker2DDataBlob } } - /** Render this marker twice so a part of the markers overlap. The interaction is decided by the operation type passed as the first float in the list. - \param[in] pixelPos The pixel position. - \param[in] inColor The color of the shapes. - \param[in] blendRadius The antialising width. - \return The color at the pixel position. - */ + /** + * Render this marker twice so a part of the markers overlap. The interaction is decided by the operation type passed as the first float + * in the list. + * @param[in] pixelPos The pixel position. + * @param[in] inColor The color of the shapes. + * @param[in] blendRadius The antialising width. + * @return The color at the pixel position. + */ float4 renderMarkerOpMarker(const uint2 pixelPos, const float4 inColor, const float blendRadius = 1.0f) { MarkerOpMarker2DData marker = reinterpret(this.payload); @@ -92,7 +105,7 @@ extension Marker2DDataBlob // Draw union of markers first, and then the operation on top. float distanceUnion = sdfUnion(distanceA, distanceB); - float4 newColor = sdfFilled(distanceUnion, 0.0f, blendRadius * blendRadiusFactor, marker.dimmedColor); + float4 newColor = sdfFilled(distanceUnion, 0.0f, blendRadius * blendRadiusFactor, marker.dimmedColor); float4 color = (1.0f - newColor.a) * inColor + newColor.a * newColor; // Draw the operation. @@ -115,22 +128,23 @@ extension Marker2DDataBlob break; } newColor = sdfFilled(distance, 0.0f, blendRadius * blendRadiusFactor, marker.color); - return (1.0f - newColor.a) * color + newColor.a * newColor; + return (1.0f - newColor.a) * color + newColor.a * newColor; } - /** Render an arrow by using two SDF triangles. - \param[in] pixelPos The pixel position. - \param[in] inColor The color of the shapes. - \param[in] blendRadius The antialising width. - \return The color at the pixel position. - */ + /** + * Render an arrow by using two SDF triangles. + * @param[in] pixelPos The pixel position. + * @param[in] inColor The color of the shapes. + * @param[in] blendRadius The antialising width. + * @return The color at the pixel position. + */ float4 renderArrowFromTwoTris(const uint2 pixelPos, const float4 inColor, const float blendRadius = 1.0f) { ArrowFromTwoTrisMarker2DData marker = reinterpret(this.payload); float2 dir = marker.line.positionB - marker.line.positionA; float len = length(dir); - float2 dirN = dir * (1.0 / len); // Normalized. + float2 dirN = dir * (1.0 / len); // Normalized. float2 dirNOrtho = float2(-dirN.y, dirN.x); float2 shaftMeetsHeadPoint = marker.line.positionB - dirN * marker.headLength; @@ -151,12 +165,13 @@ extension Marker2DDataBlob return (1 - newcolor.a) * inColor + newcolor.a * newcolor; } - /** Render a circle sector. - \param[in] pixelPos The pixel position. - \param[in] inColor The color of the shapes. - \param[in] blendRadius The antialising width. - \return The color at the pixel position. - */ + /** + * Render a circle sector. + * @param[in] pixelPos The pixel position. + * @param[in] inColor The color of the shapes. + * @param[in] blendRadius The antialising width. + * @return The color at the pixel position. + */ float4 renderCircleSector(const uint2 pixelPos, const float4 inColor, const float blendRadius = 1.0f) { CircleSectorMarker2DData marker = reinterpret(this.payload); @@ -169,11 +184,11 @@ extension Marker2DDataBlob float2 c = float2(sin(marker.angle), cos(marker.angle)); float circleMin = marker.minRadius - length(newPos); float circleMax = length(newPos) - marker.maxRadius; - float m = length(newPos - c*clamp(dot(c,newPos), marker.minRadius, marker.maxRadius)); + float m = length(newPos - c * clamp(dot(c, newPos), marker.minRadius, marker.maxRadius)); float s = sign(c.y * newPos.x - c.x * newPos.y); - float distance = max(m*s, max(circleMin, circleMax)); + float distance = max(m * s, max(circleMin, circleMax)); float4 newColor = sdfFilled(distance, 0.0f, blendRadius, marker.color); - distance = marker.borderColor.w+10.0f; + distance = marker.borderColor.w + 10.0f; // Calc distance to borders float4 borderColor = float4(marker.borderColor.xyz, newColor.a); @@ -181,15 +196,15 @@ extension Marker2DDataBlob // Top if ((marker.excludeBorders & uint(ExcludeBorderFlags::Top)) == 0) { - float m = length(newPos - c*marker.maxRadius); - distance = max(abs(circleMax), m*s); + float m = length(newPos - c * marker.maxRadius); + distance = max(abs(circleMax), m * s); } // Bottom if ((marker.excludeBorders & uint(ExcludeBorderFlags::Bottom)) == 0) { - float m = length(newPos - c*marker.minRadius); - distance = min(distance, max(abs(circleMin), m*s)); + float m = length(newPos - c * marker.minRadius); + distance = min(distance, max(abs(circleMin), m * s)); } // Sides @@ -197,39 +212,58 @@ extension Marker2DDataBlob bool isRight = (marker.excludeBorders & uint(ExcludeBorderFlags::Right)) == 0; if (isLeft || isRight) { - if (!isLeft) oldPos.x = -oldPos.x; - if (isLeft && isRight) oldPos.x = abs(oldPos.x); - float2 a = c*marker.minRadius; - float2 b = c*marker.maxRadius; + if (!isLeft) + oldPos.x = -oldPos.x; + if (isLeft && isRight) + oldPos.x = abs(oldPos.x); + float2 a = c * marker.minRadius; + float2 b = c * marker.maxRadius; float2 pa = oldPos - a, ba = b - a; float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); - distance = min(distance, length(pa - h*ba)); + distance = min(distance, length(pa - h * ba)); } // Apply border color - newColor = lerp(newColor, borderColor, smoothstep(marker.borderColor.w, marker.borderColor.w-2.0f, abs(distance))); + newColor = lerp(newColor, borderColor, smoothstep(marker.borderColor.w, marker.borderColor.w - 2.0f, abs(distance))); return lerp(inColor, newColor, newColor.a); } - /** Internal helper function to render a simple marker2D object. - */ - float4 sdfDrawSimpleMarker(const SDF sdf, const uint2 pixelPos, const SimpleMarker2DData marker, const float4 backgroundColor, const float blendRadius) + /** + * Internal helper function to render a simple marker2D object. + */ + float4 sdfDrawSimpleMarker( + const SDF sdf, + const uint2 pixelPos, + const SimpleMarker2DData marker, + const float4 backgroundColor, + const float blendRadius + ) { - return sdfDraw(sdf, pixelPos, float2(marker.transform.scale), marker.transform.rotation, marker.transform.translation, marker.color, backgroundColor, blendRadius); + return sdfDraw( + sdf, + pixelPos, + float2(marker.transform.scale), + marker.transform.rotation, + marker.transform.translation, + marker.color, + backgroundColor, + blendRadius + ); } - /** Render the marker. - \param[in] pixelPos The pixel position. - \param[in] inColor The color of the shapes. - \param[in] blendRadius The antialising width. - \return The color at the pixel position. - */ + /** + * Render the marker. + * @param[in] pixelPos The pixel position. + * @param[in] inColor The color of the shapes. + * @param[in] blendRadius The antialising width. + * @return The color at the pixel position. + */ float4 render(const uint2 pixelPos, const float4 inColor, const float blendRadius = 1.0f) { - float4 finalColor = float4(0.f, 0.f, 0.f, 1.f); - if (uint(this.type) >= uint(SDF2DShapeType::Circle) && uint(this.type) <= uint(SDF2DShapeType::Arrow)) // The simple markers which share a lot. + if (uint(this.type) >= uint(SDF2DShapeType::Circle) && uint(this.type) <= uint(SDF2DShapeType::Arrow)) // The simple markers which + // share a lot. { SimpleMarker2DData marker = reinterpret(this.payload); @@ -237,7 +271,16 @@ extension Marker2DDataBlob switch (this.type) { case SDF2DShapeType::Circle: - finalColor = sdfDraw(SDF2DCircle(marker.transform.scale), pixelPos, float2(1.0), marker.transform.rotation, marker.transform.translation, marker.color, inColor, blendRadius); + finalColor = sdfDraw( + SDF2DCircle(marker.transform.scale), + pixelPos, + float2(1.0), + marker.transform.rotation, + marker.transform.translation, + marker.color, + inColor, + blendRadius + ); break; case SDF2DShapeType::Square: finalColor = sdfDrawSimpleMarker(SDF2DSquare(), pixelPos, marker, inColor, blendRadius); @@ -279,22 +322,44 @@ extension Marker2DDataBlob else if (this.type == SDF2DShapeType::RoundedBox) { RoundedBoxMarker2DData marker = reinterpret(this.payload); - finalColor = sdfDraw(SDF2DRoundedBox(marker.halfSides, marker.transform.scale), pixelPos, float2(1.0), marker.transform.rotation, marker.transform.translation, marker.color, inColor, blendRadius); + finalColor = sdfDraw( + SDF2DRoundedBox(marker.halfSides, marker.transform.scale), + pixelPos, + float2(1.0), + marker.transform.rotation, + marker.transform.translation, + marker.color, + inColor, + blendRadius + ); } else if (this.type == SDF2DShapeType::Triangle) { TriangleMarker2DData marker = reinterpret(this.payload); - finalColor = sdfDraw(SDF2DTriangle(marker.positionA, marker.positionB, marker.positionC), pixelPos, marker.color, inColor, blendRadius); + finalColor = + sdfDraw(SDF2DTriangle(marker.positionA, marker.positionB, marker.positionC), pixelPos, marker.color, inColor, blendRadius); } else if (this.type == SDF2DShapeType::RoundedLine) { RoundedLineMarker2DData marker = reinterpret(this.payload); - finalColor = sdfDraw(SDF2DRoundedLine(marker.line.positionA, marker.line.positionB, marker.line.width), pixelPos, marker.color, inColor, blendRadius); + finalColor = sdfDraw( + SDF2DRoundedLine(marker.line.positionA, marker.line.positionB, marker.line.width), + pixelPos, + marker.color, + inColor, + blendRadius + ); } else if (this.type == SDF2DShapeType::Vector) { VectorMarker2DData marker = reinterpret(this.payload); - finalColor = sdfDraw(SDF2DVector(marker.line.positionA, marker.line.positionB, marker.line.width, marker.arrowHeight), pixelPos, marker.color, inColor, blendRadius); + finalColor = sdfDraw( + SDF2DVector(marker.line.positionA, marker.line.positionB, marker.line.width, marker.arrowHeight), + pixelPos, + marker.color, + inColor, + blendRadius + ); } else if (this.type == SDF2DShapeType::MarkerOpMarker) { @@ -308,10 +373,11 @@ extension Marker2DDataBlob { finalColor = renderCircleSector(pixelPos, inColor, blendRadius); } - + return finalColor; } }; +// clang-format off struct Marker2DSet { diff --git a/Source/RenderPasses/SDFEditor/Marker2DTypes.slang b/Source/RenderPasses/SDFEditor/Marker2DTypes.slang index 912a59917..cddf23c1f 100644 --- a/Source/RenderPasses/SDFEditor/Marker2DTypes.slang +++ b/Source/RenderPasses/SDFEditor/Marker2DTypes.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -79,15 +79,15 @@ struct Marker2DPayload struct Marker2DDataBlob { - SDF2DShapeType type; + SDF2DShapeType type; Marker2DPayload payload; }; struct Marker2DTransform { - float scale; - float rotation; - float2 translation; + float scale; + float rotation; + float2 translation; }; struct SimpleMarker2DData @@ -98,9 +98,9 @@ struct SimpleMarker2DData struct Marker2DRoundedLine { - float2 positionA; - float2 positionB; - float width; + float2 positionA; + float2 positionB; + float width; }; struct RoundedLineMarker2DData @@ -163,8 +163,10 @@ struct CircleSectorMarker2DData float maxRadius; float minRadius; float4 color; - float4 borderColor; ///< The color of the border in x,y,z and the border thickness in w. Uses the same alpha as color.a - uint excludeBorders; ///< Flags for which borders should be excluded. Flags can be any of ExcludeBorderFlags OR'ed together. + /// The color of the border in x,y,z and the border thickness in w. Uses the same alpha as color.a + float4 borderColor; + /// Flags for which borders should be excluded. Flags can be any of ExcludeBorderFlags OR'ed together. + uint excludeBorders; }; END_NAMESPACE_FALCOR diff --git a/Source/RenderPasses/SDFEditor/SDFEditor.cpp b/Source/RenderPasses/SDFEditor/SDFEditor.cpp index 11d2aed26..8569945ac 100644 --- a/Source/RenderPasses/SDFEditor/SDFEditor.cpp +++ b/Source/RenderPasses/SDFEditor/SDFEditor.cpp @@ -31,70 +31,69 @@ namespace { - const std::string kInputColorChannel = "inputColor"; - const std::string kInputVBuffer = "vbuffer"; - const std::string kInputDepth = "linearZ"; - const std::string kOutputChannel = "output"; +const std::string kInputColorChannel = "inputColor"; +const std::string kInputVBuffer = "vbuffer"; +const std::string kInputDepth = "linearZ"; +const std::string kOutputChannel = "output"; - const Falcor::ChannelList kInputChannels = - { - { kInputVBuffer, "gVBuffer", "Visibility buffer in packed format", false, ResourceFormat::Unknown }, - { kInputDepth, "gLinearZ", "Linear Z and slope", false, ResourceFormat::RG32Float }, - { kInputColorChannel, "gInputColor", "The input image (2D GUI will be drawn on top)", false, ResourceFormat::RGBA32Float }, - }; +const Falcor::ChannelList kInputChannels = { + {kInputVBuffer, "gVBuffer", "Visibility buffer in packed format", false, ResourceFormat::Unknown}, + {kInputDepth, "gLinearZ", "Linear Z and slope", false, ResourceFormat::RG32Float}, + {kInputColorChannel, "gInputColor", "The input image (2D GUI will be drawn on top)", false, ResourceFormat::RGBA32Float}, +}; - const std::string kGUIPassShaderFilename = "RenderPasses/SDFEditor/GUIPass.ps.slang"; +const std::string kGUIPassShaderFilename = "RenderPasses/SDFEditor/GUIPass.ps.slang"; - const uint32_t kInvalidPrimitiveID = std::numeric_limits::max(); +const uint32_t kInvalidPrimitiveID = std::numeric_limits::max(); - const float4 kLineColor = float4(0.585f, 1.0f, 0.0f, 1.0f); - const float4 kMarkerColor = float4(0.9f, 0.9f, 0.9f, 1.0f); - const float4 kSelectionColor = float4(1.0f, 1.0f, 1.0f, 0.75f); - const float4 kCurrentModeBGColor = float4(0.585f, 1.0f, 0.0f, 0.5f); +const float4 kLineColor = float4(0.585f, 1.0f, 0.0f, 1.0f); +const float4 kMarkerColor = float4(0.9f, 0.9f, 0.9f, 1.0f); +const float4 kSelectionColor = float4(1.0f, 1.0f, 1.0f, 0.75f); +const float4 kCurrentModeBGColor = float4(0.585f, 1.0f, 0.0f, 0.5f); - const float kFadeAwayDuration = 0.25f; // Time (in seconds) when the 2D GUI fades away after the main GUI key has been released. +const float kFadeAwayDuration = 0.25f; // Time (in seconds) when the 2D GUI fades away after the main GUI key has been released. - const float kMarkerSizeFactor = 0.75f; - const float kMarkerSizeFactorSmoothUnion = 0.6f; +const float kMarkerSizeFactor = 0.75f; +const float kMarkerSizeFactorSmoothUnion = 0.6f; - const float kScrollTranslationMultiplier = 0.01f; - const float kMaxOpSmoothingRadius = 0.01f; - const float kMinOpSmoothingRadius = 0.0001f; +const float kScrollTranslationMultiplier = 0.01f; +const float kMaxOpSmoothingRadius = 0.01f; +const float kMinOpSmoothingRadius = 0.0001f; - const float kMinShapeBlobbyness = 0.001f; - const float kMaxShapeBlobbyness = 0.02f; - const float kMinOperationSmoothness = 0.01f; - const float kMaxOperationSmoothness = 0.05f; +const float kMinShapeBlobbyness = 0.001f; +const float kMaxShapeBlobbyness = 0.02f; +const float kMinOperationSmoothness = 0.01f; +const float kMaxOperationSmoothness = 0.05f; - const FileDialogFilterVec kSDFFileExtensionFilters = { { "sdf", "SDF Files"} }; - const FileDialogFilterVec kSDFGridFileExtensionFilters = { { "sdfg", "SDF Grid Files"} }; +const FileDialogFilterVec kSDFFileExtensionFilters = {{"sdf", "SDF Files"}}; +const FileDialogFilterVec kSDFGridFileExtensionFilters = {{"sdfg", "SDF Grid Files"}}; - bool isOperationSmooth(SDFOperationType operationType) +bool isOperationSmooth(SDFOperationType operationType) +{ + switch (operationType) { - switch (operationType) - { - case SDFOperationType::SmoothUnion: - case SDFOperationType::SmoothSubtraction: - case SDFOperationType::SmoothIntersection: - return true; - } - - return false; + case SDFOperationType::SmoothUnion: + case SDFOperationType::SmoothSubtraction: + case SDFOperationType::SmoothIntersection: + return true; } - SDF2DShapeType sdf3DTo2DShape(SDF3DShapeType shapeType) + return false; +} + +SDF2DShapeType sdf3DTo2DShape(SDF3DShapeType shapeType) +{ + switch (shapeType) { - switch (shapeType) - { - case SDF3DShapeType::Sphere: - return SDF2DShapeType::Circle; - case SDF3DShapeType::Box: - return SDF2DShapeType::Square; - default: - return SDF2DShapeType::Circle; - } + case SDF3DShapeType::Sphere: + return SDF2DShapeType::Circle; + case SDF3DShapeType::Box: + return SDF2DShapeType::Square; + default: + return SDF2DShapeType::Circle; } } +} // namespace extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { @@ -107,31 +106,34 @@ void SDFEditor::registerBindings(pybind11::module& m) // None at the moment. } -SDFEditor::SDFEditor(ref pDevice, const Properties& props) - : RenderPass(pDevice) +SDFEditor::SDFEditor(ref pDevice, const Properties& props) : RenderPass(pDevice) { mpFbo = Fbo::create(mpDevice); - mpPickingInfo = Buffer::createStructured(mpDevice, sizeof(SDFPickingInfo), 1, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None); - mpPickingInfoReadBack = Buffer::createStructured(mpDevice, sizeof(SDFPickingInfo), 1, Resource::BindFlags::None, Buffer::CpuAccess::Read); - mpReadbackFence = GpuFence::create(mpDevice); + mpPickingInfo = mpDevice->createStructuredBuffer( + sizeof(SDFPickingInfo), 1, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal + ); + mpPickingInfoReadBack = mpDevice->createStructuredBuffer(sizeof(SDFPickingInfo), 1, ResourceBindFlags::None, MemoryType::ReadBack); + mpReadbackFence = mpDevice->createFence(); mUI2D.pMarker2DSet = std::make_unique(mpDevice, 100); mUI2D.pSelectionWheel = std::make_unique(*mUI2D.pMarker2DSet); - mpSDFEditingDataBuffer = Buffer::createStructured(mpDevice, sizeof(SDFEditingData), 1); + mpSDFEditingDataBuffer = mpDevice->createStructuredBuffer(sizeof(SDFEditingData), 1); mUI2D.symmetryPlane.normal = float3(1.0f, 0.0f, 0.0f); mUI2D.symmetryPlane.rightVector = float3(0.0f, 0.0f, -1.0f); mUI2D.symmetryPlane.color = float4(1.0f, 0.75f, 0.8f, 0.5f); } -void SDFEditor::setShaderData(const ShaderVar& var, const ref& pInputColor, const ref& pVBuffer) +void SDFEditor::bindShaderData(const ShaderVar& var, const ref& pInputColor, const ref& pVBuffer) { mGPUEditingData.editing = mEditingKeyDown; mGPUEditingData.previewEnabled = mPreviewEnabled; mGPUEditingData.instanceID = mCurrentEdit.instanceID; - mGPUEditingData.scalingAxis = uint32_t(mPrimitiveTransformationEdit.state != TransformationState::Scaling ? SDFEditorAxis::Count : mPrimitiveTransformationEdit.axis); + mGPUEditingData.scalingAxis = uint32_t( + mPrimitiveTransformationEdit.state != TransformationState::Scaling ? SDFEditorAxis::Count : mPrimitiveTransformationEdit.axis + ); mGPUEditingData.primitive = mCurrentEdit.primitive; mGPUEditingData.primitiveBB = SDF3DPrimitiveFactory::computeAABB(mCurrentEdit.primitive); mpSDFEditingDataBuffer->setBlob(&mGPUEditingData, 0, sizeof(SDFEditingData)); @@ -140,7 +142,14 @@ void SDFEditor::setShaderData(const ShaderVar& var, const ref& pInputCo { mGridInstanceCount = mpScene->getSDFGridCount(); // This is safe because the SDF Editor only supports SDFGrid type of SBS for now. std::vector instanceIDs = mpScene->getGeometryInstanceIDsByType(Scene::GeometryType::SDFGrid); - mpGridInstanceIDsBuffer = Buffer::createStructured(mpDevice, sizeof(uint32_t), mGridInstanceCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, instanceIDs.data(), false); + mpGridInstanceIDsBuffer = mpDevice->createStructuredBuffer( + sizeof(uint32_t), + mGridInstanceCount, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType::DeviceLocal, + instanceIDs.data(), + false + ); } auto rootVar = mpGUIPass->getRootVar(); @@ -148,7 +157,7 @@ void SDFEditor::setShaderData(const ShaderVar& var, const ref& pInputCo rootVar["gInputColor"] = pInputColor; rootVar["gLinearZ"] = mpEditingLinearZBuffer; rootVar["gVBuffer"] = pVBuffer; - rootVar["gScene"] = mpScene->getParameterBlock(); + mpScene->bindShaderData(rootVar["gScene"]); auto guiPassVar = rootVar["gGUIPass"]; guiPassVar["resolution"] = mFrameDim; @@ -161,19 +170,28 @@ void SDFEditor::setShaderData(const ShaderVar& var, const ref& pInputCo guiPassVar["ui2DActive"] = uint(isMainGUIKeyDown()); guiPassVar["gridPlane"].setBlob(mUI2D.gridPlane); guiPassVar["symmetryPlane"].setBlob(mUI2D.symmetryPlane); - mUI2D.pMarker2DSet->setShaderData(guiPassVar["markerSet"]); + mUI2D.pMarker2DSet->bindShaderData(guiPassVar["markerSet"]); } void SDFEditor::fetchPreviousVBufferAndZBuffer(RenderContext* pRenderContext, ref& pVBuffer, ref& pDepth) { if (!mpEditingVBuffer || mpEditingVBuffer->getWidth() != pVBuffer->getWidth() || mpEditingVBuffer->getHeight() != pVBuffer->getHeight()) { - mpEditingVBuffer = Texture::create2D(mpDevice, pVBuffer->getWidth(), pVBuffer->getHeight(), pVBuffer->getFormat(), 1, 1); + mpEditingVBuffer = mpDevice->createTexture2D(pVBuffer->getWidth(), pVBuffer->getHeight(), pVBuffer->getFormat(), 1, 1); } - if (!mpEditingLinearZBuffer || mpEditingLinearZBuffer->getWidth() != pDepth->getWidth() || mpEditingLinearZBuffer->getHeight() != pDepth->getHeight()) + if (!mpEditingLinearZBuffer || mpEditingLinearZBuffer->getWidth() != pDepth->getWidth() || + mpEditingLinearZBuffer->getHeight() != pDepth->getHeight()) { - mpEditingLinearZBuffer = Texture::create2D(mpDevice, pDepth->getWidth(), pDepth->getHeight(), ResourceFormat::RG32Float, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + mpEditingLinearZBuffer = mpDevice->createTexture2D( + pDepth->getWidth(), + pDepth->getHeight(), + ResourceFormat::RG32Float, + 1, + 1, + nullptr, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess + ); } pRenderContext->copySubresourceRegion(mpEditingVBuffer.get(), 0, pVBuffer.get(), pVBuffer->getSubresourceIndex(0, 0)); @@ -189,10 +207,13 @@ void SDFEditor::setScene(RenderContext* pRenderContext, const ref& pScene { mpScene = pScene; - if (!mpScene) return; + if (!mpScene) + return; mpCamera = mpScene->getCamera(); - mpGUIPass = FullScreenPass::create(mpDevice, Program::Desc(kGUIPassShaderFilename).psEntry("psMain"), mpScene->getSceneDefines()); + mpGUIPass = FullScreenPass::create( + mpDevice, ProgramDesc().addShaderLibrary(kGUIPassShaderFilename).psEntry("psMain"), mpScene->getSceneDefines() + ); // Initialize editing primitive. { @@ -209,7 +230,8 @@ void SDFEditor::setScene(RenderContext* pRenderContext, const ref& pScene if (mpScene->getSDFGridCount() > 0) { std::vector instanceIDs = mpScene->getGeometryInstanceIDsByType(Scene::GeometryType::SDFGrid); - if (instanceIDs.empty()) throw RuntimeError("Scene missing SDFGrid object!"); + if (instanceIDs.empty()) + FALCOR_THROW("Scene missing SDFGrid object!"); mCurrentEdit.instanceID = instanceIDs[0]; GeometryInstanceData instance = mpScene->getGeometryInstance(mCurrentEdit.instanceID); @@ -223,7 +245,8 @@ void SDFEditor::setScene(RenderContext* pRenderContext, const ref& pScene kDefaultShapeBlobbing, kDefaultOperationSmoothing, kDefaultOperationType, - kDefaultTransform); + kDefaultTransform + ); const AnimationController* pAnimationController = mpScene->getAnimationController(); const float4x4& transform = pAnimationController->getGlobalMatrices()[instance.globalMatrixID]; @@ -252,7 +275,8 @@ RenderPassReflection SDFEditor::reflect(const CompileData& compileData) bool SDFEditor::isMainGUIKeyDown() const { bool notTransformingPrimitive = mPrimitiveTransformationEdit.state == TransformationState::None; - return mGUIKeyDown && notTransformingPrimitive && !gridPlaneManipulated() && !symmetryPlaneManipulated() && !mUI2D.keyboardButtonsPressed.undo && !mUI2D.keyboardButtonsPressed.redo; + return mGUIKeyDown && notTransformingPrimitive && !gridPlaneManipulated() && !symmetryPlaneManipulated() && + !mUI2D.keyboardButtonsPressed.undo && !mUI2D.keyboardButtonsPressed.redo; } void SDFEditor::updateEditShapeType() @@ -269,7 +293,8 @@ void SDFEditor::updateEditShapeType() void SDFEditor::updateEditOperationType() { mCurrentEdit.primitive.operationType = mUI2D.currentEditingOperator; - mCurrentEdit.primitive.operationSmoothing = isOperationSmooth(mUI2D.currentEditingOperator) ? 0.5f * (kMaxOperationSmoothness + kMinOperationSmoothness) : 0.0f; + mCurrentEdit.primitive.operationSmoothing = + isOperationSmooth(mUI2D.currentEditingOperator) ? 0.5f * (kMaxOperationSmoothness + kMinOperationSmoothness) : 0.0f; if (mUI2D.symmetryPlane.active) { @@ -286,13 +311,15 @@ void SDFEditor::updateSymmetryPrimitive() const float3& instanceLocalPrimitivePos = mCurrentEdit.primitive.translation; float3 instanceLocalPlanePosition = transformPoint(invInstanceTransform, mUI2D.symmetryPlane.position); float3 instanceLocalPlaneNormal = normalize(transformVector(invInstanceTransform, mUI2D.symmetryPlane.normal)); - float3 projInstanceLocalPrimitivePos = (instanceLocalPrimitivePos - dot(instanceLocalPrimitivePos, instanceLocalPlaneNormal) * instanceLocalPlaneNormal); + float3 projInstanceLocalPrimitivePos = + (instanceLocalPrimitivePos - dot(instanceLocalPrimitivePos, instanceLocalPlaneNormal) * instanceLocalPlaneNormal); float3 projInstanceLocalPlanePos = dot(instanceLocalPlanePosition, instanceLocalPlaneNormal) * instanceLocalPlaneNormal; float3 reflectedInstanceLocalPos; if (length(projInstanceLocalPrimitivePos) > 0.0f) { - reflectedInstanceLocalPos = math::reflect(-(instanceLocalPrimitivePos - projInstanceLocalPlanePos), normalize(projInstanceLocalPrimitivePos)); + reflectedInstanceLocalPos = + math::reflect(-(instanceLocalPrimitivePos - projInstanceLocalPlanePos), normalize(projInstanceLocalPrimitivePos)); } else { @@ -309,7 +336,14 @@ void SDFEditor::updateSymmetryPrimitive() mCurrentEdit.symmetryPrimitive.invRotationScale = inverse(float3x3(symmetricPrimitiveTransform)); } -void SDFEditor::setupPrimitiveAndOperation(const float2& center, const float markerSize, SDF3DShapeType editingPrimitive, SDFOperationType editingOperator, const float4& color, const float alpha) +void SDFEditor::setupPrimitiveAndOperation( + const float2& center, + const float markerSize, + SDF3DShapeType editingPrimitive, + SDFOperationType editingOperator, + const float4& color, + const float alpha +) { const float4 dimmedColor = color * float4(0.6f, 0.6f, 0.6f, 1.0f); float f = markerSize * 0.25f; @@ -320,7 +354,17 @@ void SDFEditor::setupPrimitiveAndOperation(const float2& center, const float mar float2 pos1 = center - dir; float2 pos2 = center + dir; float4 fade = float4(1.0f, 1.0f, 1.0f, alpha); - mUI2D.pMarker2DSet->addMarkerOpMarker(editingOperator, sdf3DTo2DShape(editingPrimitive), pos1, markerSize, sdf3DTo2DShape(editingPrimitive), pos2, markerSize, color * fade, dimmedColor * fade); + mUI2D.pMarker2DSet->addMarkerOpMarker( + editingOperator, + sdf3DTo2DShape(editingPrimitive), + pos1, + markerSize, + sdf3DTo2DShape(editingPrimitive), + pos2, + markerSize, + color * fade, + dimmedColor * fade + ); } void SDFEditor::setupCurrentModes2D() @@ -333,15 +377,21 @@ void SDFEditor::setupCurrentModes2D() const float2 center = float2(side * 0.5f, mFrameDim.y - side * 0.5f) + float2(cornerOffset, -cornerOffset); // Setup a rounded box as a background where the selected shape and operation will be displayed. - mUI2D.pMarker2DSet->addRoundedBox(center, float2(side, side) * 0.5f, roundedRadius, 0.0f, kCurrentModeBGColor); // Add the background box, in which we will draw markers. + mUI2D.pMarker2DSet->addRoundedBox(center, float2(side, side) * 0.5f, roundedRadius, 0.0f, kCurrentModeBGColor); // Add the background + // box, in which we will + // draw markers. // Setup the selected shape and operation markers. float f = mUI2D.currentEditingOperator == SDFOperationType::SmoothUnion ? kMarkerSizeFactorSmoothUnion : kMarkerSizeFactor; setupPrimitiveAndOperation(center, markerSize * f, mUI2D.currentEditingShape, mUI2D.currentEditingOperator, kMarkerColor); - } -void SDFEditor::manipulateGridPlane(SDFGridPlane& gridPlane, SDFGridPlane& previousGridPlane, bool isTranslationKeyDown, bool isConstrainedManipulationKeyDown) +void SDFEditor::manipulateGridPlane( + SDFGridPlane& gridPlane, + SDFGridPlane& previousGridPlane, + bool isTranslationKeyDown, + bool isConstrainedManipulationKeyDown +) { float2 diffPrev = mUI2D.currentMousePosition - mUI2D.prevMousePosition; float2 diffStart = mUI2D.currentMousePosition - mUI2D.startMousePosition; @@ -349,29 +399,41 @@ void SDFEditor::manipulateGridPlane(SDFGridPlane& gridPlane, SDFGridPlane& previ float3 view = normalize(mpCamera->getTarget() - mpCamera->getPosition()); float3 right = cross(view, up); - if (!isTranslationKeyDown) // The user wants rotation of the grid plane. + if (!isTranslationKeyDown) // The user wants rotation of the grid plane. { - if (!isConstrainedManipulationKeyDown) // Rotate plane arbitrarily along right and up vectors. + if (!isConstrainedManipulationKeyDown) // Rotate plane arbitrarily along right and up vectors. { // Rotate plane around the right vector. - rotateGridPlane(diffPrev.y, right, previousGridPlane.normal, previousGridPlane.rightVector, gridPlane.normal, gridPlane.rightVector); + rotateGridPlane( + diffPrev.y, right, previousGridPlane.normal, previousGridPlane.rightVector, gridPlane.normal, gridPlane.rightVector + ); // Rotate plane around the up vector. rotateGridPlane(diffPrev.x, up, gridPlane.normal, gridPlane.rightVector, gridPlane.normal, gridPlane.rightVector); previousGridPlane = gridPlane; } - else // Constraind rotation to the axis with most movement since shift was pressed. + else // Constraind rotation to the axis with most movement since shift was pressed. { - if (std::abs(diffStart.y) > std::abs(diffStart.x)) // Rotate plane only around the right vector. + if (std::abs(diffStart.y) > std::abs(diffStart.x)) // Rotate plane only around the right vector. { - rotateGridPlane(diffStart.y, right, previousGridPlane.normal, previousGridPlane.rightVector, gridPlane.normal, gridPlane.rightVector, false); + rotateGridPlane( + diffStart.y, + right, + previousGridPlane.normal, + previousGridPlane.rightVector, + gridPlane.normal, + gridPlane.rightVector, + false + ); } - else // Rotate plane only around the up vector. + else // Rotate plane only around the up vector. { - rotateGridPlane(diffStart.x, up, previousGridPlane.normal, previousGridPlane.rightVector, gridPlane.normal, gridPlane.rightVector, false); + rotateGridPlane( + diffStart.x, up, previousGridPlane.normal, previousGridPlane.rightVector, gridPlane.normal, gridPlane.rightVector, false + ); } } } - else // The user wants translation of the grid plane. + else // The user wants translation of the grid plane. { if (!isConstrainedManipulationKeyDown) { @@ -381,11 +443,11 @@ void SDFEditor::manipulateGridPlane(SDFGridPlane& gridPlane, SDFGridPlane& previ } else { - if (std::abs(diffStart.y) > std::abs(diffStart.x)) // Translate plane only along the right vector. + if (std::abs(diffStart.y) > std::abs(diffStart.x)) // Translate plane only along the right vector. { translateGridPlane(-diffStart.y, up, previousGridPlane.position, gridPlane.position); } - else // Translate plane only along the up vector. + else // Translate plane only along the up vector. { translateGridPlane(diffStart.x, right, previousGridPlane.position, gridPlane.position); } @@ -393,7 +455,15 @@ void SDFEditor::manipulateGridPlane(SDFGridPlane& gridPlane, SDFGridPlane& previ } } -void SDFEditor::rotateGridPlane(const float mouseDiff, const float3& rotationVector, const float3& inNormal, const float3& inRightVector, float3& outNormal, float3& outRightVector, const bool fromPreviousMouse) +void SDFEditor::rotateGridPlane( + const float mouseDiff, + const float3& rotationVector, + const float3& inNormal, + const float3& inRightVector, + float3& outNormal, + float3& outRightVector, + const bool fromPreviousMouse +) { const float diagonal = length(float2(mFrameDim)); const float maxAngle = float(M_PI) * 0.05f; @@ -405,7 +475,7 @@ void SDFEditor::rotateGridPlane(const float mouseDiff, const float3& rotationVec angle = mouseDiff * std::abs(mouseDiff) * speedFactor; angle = std::clamp(angle, -maxAngle, maxAngle); } - else // From the start position -- which is better when doing constrained rotation (around a single axis). + else // From the start position -- which is better when doing constrained rotation (around a single axis). { const float speedFactor = 2.0f * float(M_PI) * 0.5f / diagonal; angle = mouseDiff * speedFactor; @@ -420,7 +490,8 @@ void SDFEditor::translateGridPlane(const float mouseDiff, const float3& translat { const float diagonal = length(float2(mFrameDim)); const float speedFactor = 0.5f / diagonal; - float translation = mouseDiff * std::abs(mouseDiff) * speedFactor; // Could possibly be improved so that speed depends on scene or size of the grid. + float translation = mouseDiff * std::abs(mouseDiff) * speedFactor; // Could possibly be improved so that speed depends on scene or size + // of the grid. outPosition = outPosition + translation * translationVector; } @@ -472,19 +543,51 @@ void SDFEditor::setup2DGUI() swDesc.maxRadius = 0.7f * radius; swDesc.baseColor = float4(0.13f, 0.13f, 0.1523f, 0.8f * alpha); swDesc.highlightColor = float4(0.4648f, 0.7226f, 0.0f, 0.8f * alpha); - swDesc.sectorGroups = { 2, 4 }; + swDesc.sectorGroups = {2, 4}; swDesc.lineColor = kLineColor * multColor; swDesc.borderWidth = 10.0f; mUI2D.pSelectionWheel->update(mUI2D.currentMousePosition, swDesc); - mUI2D.pMarker2DSet->addSimpleMarker(SDF2DShapeType::Square, markerSize, mUI2D.pSelectionWheel->getCenterPositionOfSector(0, 0), 0.0f, color); - mUI2D.pMarker2DSet->addSimpleMarker(SDF2DShapeType::Circle, markerSize * 0.5f, mUI2D.pSelectionWheel->getCenterPositionOfSector(0, 1), 0.0f, color); - setupPrimitiveAndOperation(mUI2D.pSelectionWheel->getCenterPositionOfSector(1, 0), markerSize * kMarkerSizeFactor, mUI2D.currentEditingShape, SDFOperationType::SmoothSubtraction, kMarkerColor, alpha); - setupPrimitiveAndOperation(mUI2D.pSelectionWheel->getCenterPositionOfSector(1, 1), markerSize * kMarkerSizeFactor, mUI2D.currentEditingShape, SDFOperationType::Subtraction, kMarkerColor, alpha); - setupPrimitiveAndOperation(mUI2D.pSelectionWheel->getCenterPositionOfSector(1, 2), markerSize * kMarkerSizeFactor, mUI2D.currentEditingShape, SDFOperationType::Union, kMarkerColor, alpha); - setupPrimitiveAndOperation(mUI2D.pSelectionWheel->getCenterPositionOfSector(1, 3), markerSize * kMarkerSizeFactorSmoothUnion, mUI2D.currentEditingShape, SDFOperationType::SmoothUnion, kMarkerColor, alpha); + mUI2D.pMarker2DSet->addSimpleMarker( + SDF2DShapeType::Square, markerSize, mUI2D.pSelectionWheel->getCenterPositionOfSector(0, 0), 0.0f, color + ); + mUI2D.pMarker2DSet->addSimpleMarker( + SDF2DShapeType::Circle, markerSize * 0.5f, mUI2D.pSelectionWheel->getCenterPositionOfSector(0, 1), 0.0f, color + ); + setupPrimitiveAndOperation( + mUI2D.pSelectionWheel->getCenterPositionOfSector(1, 0), + markerSize * kMarkerSizeFactor, + mUI2D.currentEditingShape, + SDFOperationType::SmoothSubtraction, + kMarkerColor, + alpha + ); + setupPrimitiveAndOperation( + mUI2D.pSelectionWheel->getCenterPositionOfSector(1, 1), + markerSize * kMarkerSizeFactor, + mUI2D.currentEditingShape, + SDFOperationType::Subtraction, + kMarkerColor, + alpha + ); + setupPrimitiveAndOperation( + mUI2D.pSelectionWheel->getCenterPositionOfSector(1, 2), + markerSize * kMarkerSizeFactor, + mUI2D.currentEditingShape, + SDFOperationType::Union, + kMarkerColor, + alpha + ); + setupPrimitiveAndOperation( + mUI2D.pSelectionWheel->getCenterPositionOfSector(1, 3), + markerSize * kMarkerSizeFactorSmoothUnion, + mUI2D.currentEditingShape, + SDFOperationType::SmoothUnion, + kMarkerColor, + alpha + ); } - auto addMarker = [&](const SDF3DShapeType& type)->void + auto addMarker = [&](const SDF3DShapeType& type) -> void { switch (type) { @@ -500,7 +603,7 @@ void SDFEditor::setup2DGUI() } }; - auto addOperation = [&](const SDFOperationType& type)->void + auto addOperation = [&](const SDFOperationType& type) -> void { switch (type) { @@ -518,8 +621,9 @@ void SDFEditor::setup2DGUI() } }; - static constexpr std::array kShapeTypes = { SDF3DShapeType::Box, SDF3DShapeType::Sphere }; - static constexpr std::array kOperationTypes = { SDFOperationType::SmoothSubtraction, SDFOperationType::Subtraction, SDFOperationType::Union, SDFOperationType::SmoothUnion }; + static constexpr std::array kShapeTypes = {SDF3DShapeType::Box, SDF3DShapeType::Sphere}; + static constexpr std::array kOperationTypes = { + SDFOperationType::SmoothSubtraction, SDFOperationType::Subtraction, SDFOperationType::Union, SDFOperationType::SmoothUnion}; // Check if the user pressed on the different sectors of the wheel. if (isMainGUIKeyDown() && !mUI2D.recordStartingMousePos && any(mUI2D.startMousePosition != mUI2D.currentMousePosition)) @@ -560,7 +664,8 @@ void SDFEditor::setup2DGUI() void SDFEditor::handleActions() { - if (!mpScene) return; + if (!mpScene) + return; const AnimationController* pAnimationController = mpScene->getAnimationController(); const GeometryInstanceData& instance = mpScene->getGeometryInstance(mCurrentEdit.instanceID); @@ -613,12 +718,15 @@ void SDFEditor::handleActions() mInstanceTransformationEdit.startPlanePos = ray.origin + ray.dir * startT; mInstanceTransformationEdit.startMousePos = mUI2D.currentMousePosition; - float3 arbitraryVectorNotOrthToPlane = std::abs(planeNormal.z) < FLT_EPSILON ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f); - mInstanceTransformationEdit.referencePlaneDir = normalize(arbitraryVectorNotOrthToPlane - dot(arbitraryVectorNotOrthToPlane, planeNormal) * planeNormal); + float3 arbitraryVectorNotOrthToPlane = + std::abs(planeNormal.z) < FLT_EPSILON ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f); + mInstanceTransformationEdit.referencePlaneDir = + normalize(arbitraryVectorNotOrthToPlane - dot(arbitraryVectorNotOrthToPlane, planeNormal) * planeNormal); } // Transform the instance if the mouse was moved or scroll was used. - if ((any(mUI2D.currentMousePosition != mUI2D.prevMousePosition) || mInstanceTransformationEdit.prevScrollTotal != mInstanceTransformationEdit.scrollTotal)) + if ((any(mUI2D.currentMousePosition != mUI2D.prevMousePosition) || + mInstanceTransformationEdit.prevScrollTotal != mInstanceTransformationEdit.scrollTotal)) { const float3& startTranslation = mInstanceTransformationEdit.startTransform.getTranslation(); float3 deltaCenter = startTranslation - ray.origin; @@ -642,12 +750,21 @@ void SDFEditor::handleActions() float3 startPlaneDir = normalize(mInstanceTransformationEdit.startPlanePos - startTranslation); float3 currPlaneDir = normalize(p - startTranslation); - float startAngle = std::atan2(dot(planeNormal, cross(startPlaneDir, mInstanceTransformationEdit.referencePlaneDir)), dot(startPlaneDir, mInstanceTransformationEdit.referencePlaneDir)); - float currentAngle = std::atan2(dot(planeNormal, cross(currPlaneDir, mInstanceTransformationEdit.referencePlaneDir)), dot(currPlaneDir, mInstanceTransformationEdit.referencePlaneDir)); + float startAngle = std::atan2( + dot(planeNormal, cross(startPlaneDir, mInstanceTransformationEdit.referencePlaneDir)), + dot(startPlaneDir, mInstanceTransformationEdit.referencePlaneDir) + ); + float currentAngle = std::atan2( + dot(planeNormal, cross(currPlaneDir, mInstanceTransformationEdit.referencePlaneDir)), + dot(currPlaneDir, mInstanceTransformationEdit.referencePlaneDir) + ); float deltaAngle = startAngle - currentAngle; - float3 localPlaneNormal = normalize(transformVector(inverse(mInstanceTransformationEdit.startTransform.getMatrix()), planeNormal)); + float3 localPlaneNormal = + normalize(transformVector(inverse(mInstanceTransformationEdit.startTransform.getMatrix()), planeNormal)); - finalTranform.setRotation(mul(mInstanceTransformationEdit.startTransform.getRotation(), math::quatFromAngleAxis(deltaAngle, localPlaneNormal))); + finalTranform.setRotation( + mul(mInstanceTransformationEdit.startTransform.getRotation(), math::quatFromAngleAxis(deltaAngle, localPlaneNormal)) + ); } // Handle scaling else if (mInstanceTransformationEdit.state == TransformationState::Scaling) @@ -690,7 +807,8 @@ void SDFEditor::handleActions() mPrimitiveTransformationEdit.startPrimitiveTransform.setRotation(primitiveRotation); mPrimitiveTransformationEdit.startPrimitiveTransform.setScaling(primitiveScale); - float3 planeOrigin = transformPoint(mPrimitiveTransformationEdit.startInstanceTransform.getMatrix(), mCurrentEdit.primitive.translation); + float3 planeOrigin = + transformPoint(mPrimitiveTransformationEdit.startInstanceTransform.getMatrix(), mCurrentEdit.primitive.translation); float3 deltaCenter = planeOrigin - ray.origin; float3 planeNormal = -normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); @@ -698,8 +816,10 @@ void SDFEditor::handleActions() mPrimitiveTransformationEdit.startPlanePos = ray.origin + ray.dir * startT; mPrimitiveTransformationEdit.startMousePos = mUI2D.currentMousePosition; - float3 arbitraryVectorNotOrthToPlane = std::abs(planeNormal.z) < FLT_EPSILON ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f); - mPrimitiveTransformationEdit.referencePlaneDir = normalize(arbitraryVectorNotOrthToPlane - dot(arbitraryVectorNotOrthToPlane, planeNormal) * planeNormal); + float3 arbitraryVectorNotOrthToPlane = + std::abs(planeNormal.z) < FLT_EPSILON ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f); + mPrimitiveTransformationEdit.referencePlaneDir = + normalize(arbitraryVectorNotOrthToPlane - dot(arbitraryVectorNotOrthToPlane, planeNormal) * planeNormal); } // Update starting position if the state or axis has changed. else if (mPrimitiveTransformationEdit.state != mPrimitiveTransformationEdit.prevState || mPrimitiveTransformationEdit.axis != mPrimitiveTransformationEdit.prevAxis) @@ -711,7 +831,8 @@ void SDFEditor::handleActions() updateSymmetryPrimitive(); } - float3 planeOrigin = transformPoint(mPrimitiveTransformationEdit.startInstanceTransform.getMatrix(), mCurrentEdit.primitive.translation); + float3 planeOrigin = + transformPoint(mPrimitiveTransformationEdit.startInstanceTransform.getMatrix(), mCurrentEdit.primitive.translation); float3 deltaCenter = planeOrigin - ray.origin; float3 planeNormal = -normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); @@ -719,14 +840,19 @@ void SDFEditor::handleActions() mPrimitiveTransformationEdit.startPlanePos = ray.origin + ray.dir * startT; mPrimitiveTransformationEdit.startMousePos = mUI2D.currentMousePosition; - float3 arbitraryVectorNotOrthToPlane = std::abs(planeNormal.z) < FLT_EPSILON ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f); - mPrimitiveTransformationEdit.referencePlaneDir = normalize(arbitraryVectorNotOrthToPlane - dot(arbitraryVectorNotOrthToPlane, planeNormal) * planeNormal); + float3 arbitraryVectorNotOrthToPlane = + std::abs(planeNormal.z) < FLT_EPSILON ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f); + mPrimitiveTransformationEdit.referencePlaneDir = + normalize(arbitraryVectorNotOrthToPlane - dot(arbitraryVectorNotOrthToPlane, planeNormal) * planeNormal); } // Transform the primitive if the mouse has moved. if (any(mUI2D.currentMousePosition != mUI2D.prevMousePosition)) { - float3 planeOrigin = transformPoint(mPrimitiveTransformationEdit.startInstanceTransform.getMatrix(), mPrimitiveTransformationEdit.startPrimitiveTransform.getTranslation()); + float3 planeOrigin = transformPoint( + mPrimitiveTransformationEdit.startInstanceTransform.getMatrix(), + mPrimitiveTransformationEdit.startPrimitiveTransform.getTranslation() + ); float3 deltaCenter = planeOrigin - ray.origin; float3 planeNormal = -normalize(mpScene->getCamera()->getTarget() - mpScene->getCamera()->getPosition()); @@ -747,12 +873,22 @@ void SDFEditor::handleActions() startPlaneDir = normalize(startPlaneDir); currPlaneDir = normalize(currPlaneDir); - float startAngle = std::atan2(dot(planeNormal, cross(startPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)), dot(startPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)); - float currentAngle = std::atan2(dot(planeNormal, cross(currPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)), dot(currPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)); + float startAngle = std::atan2( + dot(planeNormal, cross(startPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)), + dot(startPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir) + ); + float currentAngle = std::atan2( + dot(planeNormal, cross(currPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir)), + dot(currPlaneDir, mPrimitiveTransformationEdit.referencePlaneDir) + ); float deltaAngle = currentAngle - startAngle; - float3 localPlaneNormal = normalize(transformVector(inverse(mPrimitiveTransformationEdit.startInstanceTransform.getMatrix()), planeNormal)); + float3 localPlaneNormal = + normalize(transformVector(inverse(mPrimitiveTransformationEdit.startInstanceTransform.getMatrix()), planeNormal)); - finalPrimitiveTransform.setRotation(mul(mPrimitiveTransformationEdit.startPrimitiveTransform.getRotation(), math::quatFromAngleAxis(-deltaAngle, localPlaneNormal))); + finalPrimitiveTransform.setRotation( + mul(mPrimitiveTransformationEdit.startPrimitiveTransform.getRotation(), + math::quatFromAngleAxis(-deltaAngle, localPlaneNormal)) + ); mCurrentEdit.primitive.invRotationScale = transpose(inverse(float3x3(finalPrimitiveTransform.getMatrix()))); } @@ -767,12 +903,13 @@ void SDFEditor::handleActions() if (mPrimitiveTransformationEdit.axis == SDFEditorAxis::All) { float3x3 scaleMatrix = matrixFromDiagonal(float3(scale)); - mCurrentEdit.primitive.invRotationScale = transpose(inverse(mul(float3x3(mPrimitiveTransformationEdit.startPrimitiveTransform.getMatrix()), scaleMatrix))); - + mCurrentEdit.primitive.invRotationScale = + transpose(inverse(mul(float3x3(mPrimitiveTransformationEdit.startPrimitiveTransform.getMatrix()), scaleMatrix))); } else if (mPrimitiveTransformationEdit.axis == SDFEditorAxis::OpSmoothing) { - float finalScaling = std::clamp(startPrimitive.operationSmoothing * scale, kMinOpSmoothingRadius, kMaxOpSmoothingRadius); + float finalScaling = + std::clamp(startPrimitive.operationSmoothing * scale, kMinOpSmoothingRadius, kMaxOpSmoothingRadius); mCurrentEdit.primitive.operationSmoothing = finalScaling; } else @@ -780,7 +917,8 @@ void SDFEditor::handleActions() uint32_t axis = uint32_t(mPrimitiveTransformationEdit.axis); float3x3 scaleMtx = float3x3::identity(); scaleMtx[axis][axis] = scale; - mCurrentEdit.primitive.invRotationScale = transpose(inverse(mul(float3x3(mPrimitiveTransformationEdit.startPrimitiveTransform.getMatrix()), scaleMtx))); + mCurrentEdit.primitive.invRotationScale = + transpose(inverse(mul(float3x3(mPrimitiveTransformationEdit.startPrimitiveTransform.getMatrix()), scaleMtx))); } } @@ -806,12 +944,12 @@ void SDFEditor::handleToggleSymmetryPlane() if (mUI2D.symmetryPlane.active) { uint32_t basePrimitiveID; - basePrimitiveID = mCurrentEdit.pSDFGrid->addPrimitives({ mCurrentEdit.symmetryPrimitive }); + basePrimitiveID = mCurrentEdit.pSDFGrid->addPrimitives({mCurrentEdit.symmetryPrimitive}); mCurrentEdit.symmetryPrimitiveID = basePrimitiveID; } else if (mCurrentEdit.primitiveID != kInvalidPrimitiveID) { - mCurrentEdit.pSDFGrid->removePrimitives({ mCurrentEdit.symmetryPrimitiveID }); + mCurrentEdit.pSDFGrid->removePrimitives({mCurrentEdit.symmetryPrimitiveID}); mCurrentEdit.symmetryPrimitiveID = kInvalidPrimitiveID; } } @@ -913,7 +1051,7 @@ void SDFEditor::addEditPrimitive(bool addToCurrentEdit, bool addToHistory) if (mUI2D.symmetryPlane.active) { updateSymmetryPrimitive(); - basePrimitiveID = mCurrentEdit.pSDFGrid->addPrimitives({ mCurrentEdit.primitive, mCurrentEdit.symmetryPrimitive }); + basePrimitiveID = mCurrentEdit.pSDFGrid->addPrimitives({mCurrentEdit.primitive, mCurrentEdit.symmetryPrimitive}); if (addToCurrentEdit) { @@ -939,7 +1077,7 @@ void SDFEditor::addEditPrimitive(bool addToCurrentEdit, bool addToHistory) } else { - basePrimitiveID = mCurrentEdit.pSDFGrid->addPrimitives({ mCurrentEdit.primitive }); + basePrimitiveID = mCurrentEdit.pSDFGrid->addPrimitives({mCurrentEdit.primitive}); if (addToCurrentEdit) { @@ -964,7 +1102,7 @@ void SDFEditor::removeEditPrimitives() { if (mCurrentEdit.primitiveID != kInvalidPrimitiveID && mCurrentEdit.symmetryPrimitiveID != kInvalidPrimitiveID) { - mCurrentEdit.pSDFGrid->removePrimitives({ mCurrentEdit.primitiveID, mCurrentEdit.symmetryPrimitiveID }); + mCurrentEdit.pSDFGrid->removePrimitives({mCurrentEdit.primitiveID, mCurrentEdit.symmetryPrimitiveID}); mCurrentEdit.primitiveID = kInvalidPrimitiveID; mCurrentEdit.symmetryPrimitiveID = kInvalidPrimitiveID; mNonBakedPrimitiveCount -= 2; @@ -974,7 +1112,7 @@ void SDFEditor::removeEditPrimitives() { if (mCurrentEdit.primitiveID != kInvalidPrimitiveID) { - mCurrentEdit.pSDFGrid->removePrimitives({ mCurrentEdit.primitiveID }); + mCurrentEdit.pSDFGrid->removePrimitives({mCurrentEdit.primitiveID}); mCurrentEdit.primitiveID = kInvalidPrimitiveID; mNonBakedPrimitiveCount--; } @@ -983,11 +1121,11 @@ void SDFEditor::removeEditPrimitives() void SDFEditor::updateEditPrimitives() { - mCurrentEdit.pSDFGrid->updatePrimitives({ {mCurrentEdit.primitiveID, mCurrentEdit.primitive} }); + mCurrentEdit.pSDFGrid->updatePrimitives({{mCurrentEdit.primitiveID, mCurrentEdit.primitive}}); if (mUI2D.symmetryPlane.active) { updateSymmetryPrimitive(); - mCurrentEdit.pSDFGrid->updatePrimitives({ {mCurrentEdit.symmetryPrimitiveID, mCurrentEdit.symmetryPrimitive} }); + mCurrentEdit.pSDFGrid->updatePrimitives({{mCurrentEdit.symmetryPrimitiveID, mCurrentEdit.symmetryPrimitive}}); } } @@ -996,7 +1134,8 @@ void SDFEditor::handleToggleEditing() // Toggle the primitive preview by adding a primitive to the current edit or removing it depending on if the editing mode is active. if (mEditingKeyDown) { - if (!handlePicking(mUI2D.currentMousePosition, mCurrentEdit.primitive.translation)) return; + if (!handlePicking(mUI2D.currentMousePosition, mCurrentEdit.primitive.translation)) + return; addEditPrimitive(true, false); } else @@ -1008,7 +1147,8 @@ void SDFEditor::handleToggleEditing() void SDFEditor::handleEditMovement() { // Update the current primitive to the current mouse position projected onto the surface. - if (!handlePicking(mUI2D.currentMousePosition, mCurrentEdit.primitive.translation)) return; + if (!handlePicking(mUI2D.currentMousePosition, mCurrentEdit.primitive.translation)) + return; if (mCurrentEdit.primitiveID != kInvalidPrimitiveID) { @@ -1020,7 +1160,8 @@ void SDFEditor::handleAddPrimitive() { // Add a primitive on the current mouse position projected onto the surface. float3 localPos; - if (!handlePicking(mUI2D.currentMousePosition, localPos)) return; + if (!handlePicking(mUI2D.currentMousePosition, localPos)) + return; mCurrentEdit.primitive.translation = localPos; updateEditPrimitives(); @@ -1034,7 +1175,8 @@ void SDFEditor::handleAddPrimitive() bool SDFEditor::handlePicking(const float2& currentMousePos, float3& localPos) { - if (!mpScene) return false; + if (!mpScene) + return false; // Create picking ray. float3 rayOrigin = mpCamera->getPosition(); @@ -1067,7 +1209,8 @@ bool SDFEditor::handlePicking(const float2& currentMousePos, float3& localPos) void SDFEditor::execute(RenderContext* pRenderContext, const RenderData& renderData) { - if (!mpScene || !mpGUIPass) return; + if (!mpScene || !mpGUIPass) + return; auto pOutput = renderData.getTexture(kOutputChannel); auto pInputColor = renderData.getTexture(kInputColorChannel); @@ -1089,7 +1232,7 @@ void SDFEditor::execute(RenderContext* pRenderContext, const RenderData& renderD } // Wait for the picking info from the previous frame. - mpReadbackFence->syncCpu(); + mpReadbackFence->wait(); mPickingInfo = *reinterpret_cast(mpPickingInfoReadBack->map(Buffer::MapType::Read)); mpPickingInfoReadBack->unmap(); @@ -1098,14 +1241,14 @@ void SDFEditor::execute(RenderContext* pRenderContext, const RenderData& renderD // Set shader data. auto rootVar = mpGUIPass->getRootVar(); - setShaderData(rootVar, pInputColor, pVBuffer); + bindShaderData(rootVar, pInputColor, pVBuffer); mpGUIPass->execute(pRenderContext, mpFbo); // Copy picking info into a staging buffer. pRenderContext->copyResource(mpPickingInfoReadBack.get(), mpPickingInfo.get()); - pRenderContext->flush(false); - mpReadbackFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + pRenderContext->submit(false); + pRenderContext->signal(mpReadbackFence.get()); // Prepare next frame. { @@ -1126,7 +1269,8 @@ void SDFEditor::renderUI(RenderContext* pRenderContext, Gui::Widgets& widget) widget.tooltip( "Abbreviations: Prim=Primitive, Op=Operation, MB = mouse button, LMB = left MB, RMB = right MB, MMB = middle MB\n" "\n" - "To create an SDF from an empty grid, use either the grid plane to place the SDF on, or intersect the grid instance with other geometry and toggle Editing on Other surfaces by pressing 'C'.\n" + "To create an SDF from an empty grid, use either the grid plane to place the SDF on, or intersect the grid instance with other " + "geometry and toggle Editing on Other surfaces by pressing 'C'.\n" "\n" "* Bring up prim type / op selection: Hold Tab\n" "* Select prim type / op in selection : LMB\n" @@ -1196,12 +1340,13 @@ void SDFEditor::renderUI(RenderContext* pRenderContext, Gui::Widgets& widget) if (mUI2D.gridPlane.active) { - widget.var("Plane center", mUI2D.gridPlane.position, -10.0f, 10.0f, 0.05f); // TODO: The limits should ideally be dependent on scene/camera. + // TODO: The limits should ideally be dependent on scene/camera. + widget.var("Plane center", mUI2D.gridPlane.position, -10.0f, 10.0f, 0.05f); widget.direction("Plane normal", mUI2D.gridPlane.normal); widget.direction("Plane right vector", mUI2D.gridPlane.rightVector); - widget.slider("Plane size", mUI2D.gridPlane.planeSize, 0.01f, 2.0f, false, "%2.2f"); // TODO: The limits should ideally be dependent on scene/camera. - widget.slider("Grid line width", mUI2D.gridPlane.gridLineWidth, 0.01f, 0.1f, false, "%2.2f"); // TODO: The limits should ideally be dependent on scene/camera. - widget.slider("Grid scale", mUI2D.gridPlane.gridScale, 0.01f, 50.0f, false, "%2.2f"); // TODO: The limits should ideally be dependent on scene/camera. + widget.slider("Plane size", mUI2D.gridPlane.planeSize, 0.01f, 2.0f, false, "%2.2f"); + widget.slider("Grid line width", mUI2D.gridPlane.gridLineWidth, 0.01f, 0.1f, false, "%2.2f"); + widget.slider("Grid scale", mUI2D.gridPlane.gridScale, 0.01f, 50.0f, false, "%2.2f"); widget.rgbaColor("Grid color", mUI2D.gridPlane.color); } @@ -1209,8 +1354,7 @@ void SDFEditor::renderUI(RenderContext* pRenderContext, Gui::Widgets& widget) if (auto nodeGroup = widget.group("Bounding Boxes", true)) { - static Gui::DropdownList sdfBoundingBoxRenderModes = - { + static Gui::DropdownList sdfBoundingBoxRenderModes = { {uint32_t(SDFBBRenderMode::Disabled), "Disabled"}, {uint32_t(SDFBBRenderMode::RenderAll), "Render All"}, {uint32_t(SDFBBRenderMode::RenderSelectedOnly), "Render Selected"}, @@ -1363,28 +1507,32 @@ bool SDFEditor::onKeyEvent(const KeyboardEvent& keyEvent) case Input::Key::Key1: if (mPrimitiveTransformationEdit.state == TransformationState::Scaling) { - mPrimitiveTransformationEdit.axis = mPrimitiveTransformationEdit.axis == SDFEditorAxis::X ? SDFEditorAxis::All : SDFEditorAxis::X; + mPrimitiveTransformationEdit.axis = + mPrimitiveTransformationEdit.axis == SDFEditorAxis::X ? SDFEditorAxis::All : SDFEditorAxis::X; return true; } break; case Input::Key::Key2: if (mPrimitiveTransformationEdit.state == TransformationState::Scaling) { - mPrimitiveTransformationEdit.axis = mPrimitiveTransformationEdit.axis == SDFEditorAxis::Y ? SDFEditorAxis::All : SDFEditorAxis::Y; + mPrimitiveTransformationEdit.axis = + mPrimitiveTransformationEdit.axis == SDFEditorAxis::Y ? SDFEditorAxis::All : SDFEditorAxis::Y; return true; } break; case Input::Key::Key3: if (mPrimitiveTransformationEdit.state == TransformationState::Scaling) { - mPrimitiveTransformationEdit.axis = mPrimitiveTransformationEdit.axis == SDFEditorAxis::Z ? SDFEditorAxis::All : SDFEditorAxis::Z; + mPrimitiveTransformationEdit.axis = + mPrimitiveTransformationEdit.axis == SDFEditorAxis::Z ? SDFEditorAxis::All : SDFEditorAxis::Z; return true; } break; case Input::Key::Key4: if (mPrimitiveTransformationEdit.state == TransformationState::Scaling) { - mPrimitiveTransformationEdit.axis = mPrimitiveTransformationEdit.axis == SDFEditorAxis::OpSmoothing ? SDFEditorAxis::All : SDFEditorAxis::OpSmoothing; + mPrimitiveTransformationEdit.axis = + mPrimitiveTransformationEdit.axis == SDFEditorAxis::OpSmoothing ? SDFEditorAxis::All : SDFEditorAxis::OpSmoothing; return true; } break; @@ -1527,7 +1675,9 @@ bool SDFEditor::onMouseEvent(const MouseEvent& mouseEvent) mUI2D.previousGridPlane = mUI2D.gridPlane; } - manipulateGridPlane(mUI2D.gridPlane, mUI2D.previousGridPlane, mUI2D.keyboardButtonsPressed.shift, mUI2D.keyboardButtonsPressed.control); + manipulateGridPlane( + mUI2D.gridPlane, mUI2D.previousGridPlane, mUI2D.keyboardButtonsPressed.shift, mUI2D.keyboardButtonsPressed.control + ); } } else if (symmetryPlaneManipulated()) @@ -1551,7 +1701,9 @@ bool SDFEditor::onMouseEvent(const MouseEvent& mouseEvent) mUI2D.previousSymmetryPlane = mUI2D.symmetryPlane; } - manipulateGridPlane(mUI2D.symmetryPlane, mUI2D.previousSymmetryPlane, mUI2D.keyboardButtonsPressed.shift, mUI2D.keyboardButtonsPressed.control); + manipulateGridPlane( + mUI2D.symmetryPlane, mUI2D.previousSymmetryPlane, mUI2D.keyboardButtonsPressed.shift, mUI2D.keyboardButtonsPressed.control + ); } } @@ -1560,11 +1712,13 @@ bool SDFEditor::onMouseEvent(const MouseEvent& mouseEvent) mInstanceTransformationEdit.state = TransformationState::None; mPrimitiveTransformationEdit.state = TransformationState::None; } - else if (mouseEvent.type == MouseEvent::Type::ButtonUp && mouseEvent.button == Input::MouseButton::Right) // Released right mouse right now. + else if (mouseEvent.type == MouseEvent::Type::ButtonUp && mouseEvent.button == Input::MouseButton::Right) // Released right mouse right + // now. { mUI2D.previousGridPlane = mUI2D.gridPlane; } - else if (mouseEvent.type == MouseEvent::Type::ButtonUp && mouseEvent.button == Input::MouseButton::Middle) // Released middle mouse right now. + else if (mouseEvent.type == MouseEvent::Type::ButtonUp && mouseEvent.button == Input::MouseButton::Middle) // Released middle mouse + // right now. { mUI2D.previousSymmetryPlane = mUI2D.symmetryPlane; } diff --git a/Source/RenderPasses/SDFEditor/SDFEditor.h b/Source/RenderPasses/SDFEditor/SDFEditor.h index 0de00f4d6..de6870e49 100644 --- a/Source/RenderPasses/SDFEditor/SDFEditor.h +++ b/Source/RenderPasses/SDFEditor/SDFEditor.h @@ -82,27 +82,26 @@ class SDFEditor : public RenderPass struct UI2D { - bool recordStartingMousePos = false; - float scrollDelta = 0.0f; - KeyboardButtonsPressed keyboardButtonsPressed; - float2 startMousePosition = { 0.0f, 0.0f }; - float2 currentMousePosition = { 0.0f, 0.0f }; - float2 prevMousePosition = { 0.0f, 0.0f }; - CpuTimer timer; - CpuTimer::TimePoint timeOfReleaseMainGUIKey; - bool fadeAwayGUI = false; - bool drawCurrentModes = true; - std::unique_ptr pMarker2DSet; + bool recordStartingMousePos = false; + float scrollDelta = 0.0f; + KeyboardButtonsPressed keyboardButtonsPressed; + float2 startMousePosition = {0.0f, 0.0f}; + float2 currentMousePosition = {0.0f, 0.0f}; + float2 prevMousePosition = {0.0f, 0.0f}; + CpuTimer timer; + CpuTimer::TimePoint timeOfReleaseMainGUIKey; + bool fadeAwayGUI = false; + bool drawCurrentModes = true; + std::unique_ptr pMarker2DSet; std::unique_ptr pSelectionWheel; - float currentBlobbing = 0.0f; - SDF3DShapeType currentEditingShape = SDF3DShapeType::Sphere; - SDFOperationType currentEditingOperator = SDFOperationType::Union; - SDFBBRenderSettings bbRenderSettings; - SDFGridPlane previousGridPlane; - SDFGridPlane gridPlane; - SDFGridPlane previousSymmetryPlane; - SDFGridPlane symmetryPlane; - + float currentBlobbing = 0.0f; + SDF3DShapeType currentEditingShape = SDF3DShapeType::Sphere; + SDFOperationType currentEditingOperator = SDFOperationType::Union; + SDFBBRenderSettings bbRenderSettings; + SDFGridPlane previousGridPlane; + SDFGridPlane gridPlane; + SDFGridPlane previousSymmetryPlane; + SDFGridPlane symmetryPlane; }; struct CurrentEdit @@ -129,16 +128,36 @@ class SDFEditor : public RenderPass }; private: - void setShaderData(const ShaderVar& var, const ref& pInputColor, const ref& pVBuffer); + void bindShaderData(const ShaderVar& var, const ref& pInputColor, const ref& pVBuffer); void fetchPreviousVBufferAndZBuffer(RenderContext* pRenderContext, ref& pVBuffer, ref& pDepth); // 2D GUI functions. void setup2DGUI(); bool isMainGUIKeyDown() const; - void setupPrimitiveAndOperation(const float2& center, const float markerSize, const SDF3DShapeType editingPrimitive, const SDFOperationType editingOperator, const float4& color, const float alpha = 1.0f); + void setupPrimitiveAndOperation( + const float2& center, + const float markerSize, + const SDF3DShapeType editingPrimitive, + const SDFOperationType editingOperator, + const float4& color, + const float alpha = 1.0f + ); void setupCurrentModes2D(); - void manipulateGridPlane(SDFGridPlane& gridPlane, SDFGridPlane& previousGridPlane, bool isTranslationKeyDown, bool isConstrainedManipulationKeyDown); - void rotateGridPlane(const float mouseDiff, const float3& rotationVector, const float3& inNormal, const float3& inRightVector, float3& outNormal, float3& outRightVector, const bool fromPreviousMouse = true); + void manipulateGridPlane( + SDFGridPlane& gridPlane, + SDFGridPlane& previousGridPlane, + bool isTranslationKeyDown, + bool isConstrainedManipulationKeyDown + ); + void rotateGridPlane( + const float mouseDiff, + const float3& rotationVector, + const float3& inNormal, + const float3& inRightVector, + float3& outNormal, + float3& outRightVector, + const bool fromPreviousMouse = true + ); void translateGridPlane(const float mouseDiff, const float3& translationVector, const float3& inPosition, float3& outPosition); bool gridPlaneManipulated() const; bool symmetryPlaneManipulated() const; @@ -165,13 +184,13 @@ class SDFEditor : public RenderPass void bakePrimitives(); private: - ref mpScene; ///< The current scene. - ref mpCamera; ///< The camera. - ref mpGUIPass; ///< A full screen pass drawing the 2D GUI. - ref mpFbo; ///< Frame buffer object. - ref mpEditingVBuffer; ///< A copy of the VBuffer used while moving/adding a primitive. - ref mpEditingLinearZBuffer; ///< A copy of the linear Z buffer used while moving/adding a primitive. - ref mpSDFEditingDataBuffer; ///< A buffer that contain current Edit data for GUI visualization. + ref mpScene; ///< The current scene. + ref mpCamera; ///< The camera. + ref mpGUIPass; ///< A full screen pass drawing the 2D GUI. + ref mpFbo; ///< Frame buffer object. + ref mpEditingVBuffer; ///< A copy of the VBuffer used while moving/adding a primitive. + ref mpEditingLinearZBuffer; ///< A copy of the linear Z buffer used while moving/adding a primitive. + ref mpSDFEditingDataBuffer; ///< A buffer that contain current Edit data for GUI visualization. struct { @@ -180,11 +199,11 @@ class SDFEditor : public RenderPass Transform startInstanceTransform; Transform startPrimitiveTransform; SDF3DPrimitive startPrimitive; - float3 startPlanePos = { 0.0f, 0.0f, 0.0f }; - float3 referencePlaneDir = { 0.0f, 0.0f, 0.0f }; + float3 startPlanePos = {0.0f, 0.0f, 0.0f}; + float3 referencePlaneDir = {0.0f, 0.0f, 0.0f}; SDFEditorAxis axis = SDFEditorAxis::All; SDFEditorAxis prevAxis = SDFEditorAxis::All; - float2 startMousePos = { 0.0f, 0.0f }; + float2 startMousePos = {0.0f, 0.0f}; } mPrimitiveTransformationEdit; struct @@ -192,43 +211,43 @@ class SDFEditor : public RenderPass TransformationState prevState = TransformationState::None; TransformationState state = TransformationState::None; Transform startTransform; - float3 startPlanePos = { 0.0f, 0.0f, 0.0f }; - float3 referencePlaneDir = { 0.0f, 0.0f, 0.0f }; + float3 startPlanePos = {0.0f, 0.0f, 0.0f}; + float3 referencePlaneDir = {0.0f, 0.0f, 0.0f}; float prevScrollTotal = 0.0f; float scrollTotal = 0.0f; - float2 startMousePos = { 0.0f, 0.0f }; + float2 startMousePos = {0.0f, 0.0f}; } mInstanceTransformationEdit; - CurrentEdit mCurrentEdit; - std::vector mPerformedSDFEdits; - std::vector mUndoneSDFEdits; - bool mLMBDown = false; - bool mRMBDown = false; - bool mMMBDown = false; - bool mEditingKeyDown = false; - bool mGUIKeyDown = false; - bool mPreviewEnabled = true; - bool mAllowEditingOnOtherSurfaces = false; - bool mAutoBakingEnabled = true; + CurrentEdit mCurrentEdit; + std::vector mPerformedSDFEdits; + std::vector mUndoneSDFEdits; + bool mLMBDown = false; + bool mRMBDown = false; + bool mMMBDown = false; + bool mEditingKeyDown = false; + bool mGUIKeyDown = false; + bool mPreviewEnabled = true; + bool mAllowEditingOnOtherSurfaces = false; + bool mAutoBakingEnabled = true; - uint2 mFrameDim = { 0, 0 }; - UI2D mUI2D; + uint2 mFrameDim = {0, 0}; + UI2D mUI2D; - ref mpPickingInfo; ///< Buffer for reading back picking info from the GPU. - ref mpPickingInfoReadBack; ///< Staging buffer for reading back picking info from the GPU. - ref mpReadbackFence; ///< GPU fence for synchronizing picking info readback. - SDFPickingInfo mPickingInfo; + ref mpPickingInfo; ///< Buffer for reading back picking info from the GPU. + ref mpPickingInfoReadBack; ///< Staging buffer for reading back picking info from the GPU. + ref mpReadbackFence; ///< GPU fence for synchronizing picking info readback. + SDFPickingInfo mPickingInfo; - SDFEditingData mGPUEditingData; + SDFEditingData mGPUEditingData; - ref mpGridInstanceIDsBuffer; - uint32_t mGridInstanceCount = 0; + ref mpGridInstanceIDsBuffer; + uint32_t mGridInstanceCount = 0; - uint32_t mNonBakedPrimitiveCount = 0; - uint32_t mBakePrimitivesBatchSize = 5; ///< The number of primitives to bake at a time. - uint32_t mPreservedHistoryCount = 100; ///< Primitives that should not be baked. + uint32_t mNonBakedPrimitiveCount = 0; + uint32_t mBakePrimitivesBatchSize = 5; ///< The number of primitives to bake at a time. + uint32_t mPreservedHistoryCount = 100; ///< Primitives that should not be baked. // Undo/Redo - uint32_t mUndoPressedCount = 0; - uint32_t mRedoPressedCount = 0; + uint32_t mUndoPressedCount = 0; + uint32_t mRedoPressedCount = 0; }; diff --git a/Source/RenderPasses/SDFEditor/SDFEditorTypes.slang b/Source/RenderPasses/SDFEditor/SDFEditorTypes.slang index cb9786826..d6dd08dc4 100644 --- a/Source/RenderPasses/SDFEditor/SDFEditorTypes.slang +++ b/Source/RenderPasses/SDFEditor/SDFEditorTypes.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,8 +26,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Structs and enums for GUIPass for the SDFEditor -*/ +/** + * Structs and enums for GUIPass for the SDFEditor + */ #pragma once #include "Utils/HostDeviceShared.slangh" @@ -47,16 +48,16 @@ BEGIN_NAMESPACE_FALCOR struct SDFGridPlane { float3 position = float3(0.0f, 0.0f, 0.0f); - float gridLineWidth = 0.04f; + float gridLineWidth = 0.04f; float3 normal = float3(0.0f, 0.0f, 1.0f); - float gridScale = 25.0f; + float gridScale = 25.0f; float3 rightVector = float3(1.0f, 0.0f, 0.0f); - float planeSize = 0.25f; + float planeSize = 0.25f; float4 color = float4(0.0f, 0.5f, 1.0f, 0.5f); - uint active = 0; // Only render and manipulate the grid if this is true. - uint3 _pad0; + uint active = 0; // Only render and manipulate the grid if this is true. + uint3 _pad0; - float intersect(float3 rayOrigin, float3 rayDir) // Returns the distance from the ray to the plane. + float intersect(float3 rayOrigin, float3 rayDir) // Returns the distance from the ray to the plane. { return -dot(normal, rayOrigin - position) / dot(normal, rayDir); } diff --git a/Source/RenderPasses/SDFEditor/SelectionWheel.cpp b/Source/RenderPasses/SDFEditor/SelectionWheel.cpp index 8af8379a2..93dd161ac 100644 --- a/Source/RenderPasses/SDFEditor/SelectionWheel.cpp +++ b/Source/RenderPasses/SDFEditor/SelectionWheel.cpp @@ -75,21 +75,46 @@ void Falcor::SelectionWheel::update(const float2& mousePos, const Desc& descript // The other sectors. float groupSpacing = sectorIndex == 0 ? halfGroupSpacingAngle : -halfGroupSpacingAngle; float offset = sectorIndex == 0 ? sectorAngle : 0.0f; - addCircleSector(groupAngle * groupIndex + offset, groupAngle - sectorAngle, mDescription.baseColor, borderColor, -groupSpacing, false, sectorIndex == 0 ? ExcludeBorderFlags::Left : ExcludeBorderFlags::Right); + addCircleSector( + groupAngle * groupIndex + offset, + groupAngle - sectorAngle, + mDescription.baseColor, + borderColor, + -groupSpacing, + false, + sectorIndex == 0 ? ExcludeBorderFlags::Left : ExcludeBorderFlags::Right + ); // Highlighted sector. addCircleSector(rotation, sectorAngle, mDescription.highlightColor, borderColor, groupSpacing); } - else // If the selected sector is in the middle, render two circle sectors for the surrounding sectors and one for the selected sector. + else // If the selected sector is in the middle, render two circle sectors for the surrounding sectors and one for the selected + // sector. { // Highlighted sector. addCircleSector(rotation, sectorAngle, mDescription.highlightColor, borderColor); // Left sectors. - addCircleSector(groupAngle * groupIndex, sectorAngle * sectorIndex, mDescription.baseColor, borderColor, halfGroupSpacingAngle, false, ExcludeBorderFlags::Right); + addCircleSector( + groupAngle * groupIndex, + sectorAngle * sectorIndex, + mDescription.baseColor, + borderColor, + halfGroupSpacingAngle, + false, + ExcludeBorderFlags::Right + ); // Right sectors. - addCircleSector(groupAngle * groupIndex + sectorAngle * (sectorIndex + 1), sectorAngle * (lastSectorIndex - sectorIndex), mDescription.baseColor, borderColor, -halfGroupSpacingAngle, false, ExcludeBorderFlags::Left); + addCircleSector( + groupAngle * groupIndex + sectorAngle * (sectorIndex + 1), + sectorAngle * (lastSectorIndex - sectorIndex), + mDescription.baseColor, + borderColor, + -halfGroupSpacingAngle, + false, + ExcludeBorderFlags::Left + ); } } @@ -105,20 +130,21 @@ void Falcor::SelectionWheel::update(const float2& mousePos, const Desc& descript bool Falcor::SelectionWheel::isMouseOnSector(const float2& mousePos, uint32_t groupIndex, uint32_t sectorIndex) { - checkArgument(groupIndex < (uint32_t)mDescription.sectorGroups.size(), "'groupIndex' ({}) is out of bounds.", groupIndex); - checkArgument(sectorIndex < mDescription.sectorGroups[groupIndex], "'sectorIndex' ({}) is out of bounds.", sectorIndex); + FALCOR_CHECK(groupIndex < (uint32_t)mDescription.sectorGroups.size(), "'groupIndex' ({}) is out of bounds.", groupIndex); + FALCOR_CHECK(sectorIndex < mDescription.sectorGroups[groupIndex], "'sectorIndex' ({}) is out of bounds.", sectorIndex); float dirLength, mouseAngle; computeMouseAngleAndDirLength(mousePos, mouseAngle, dirLength); float rotation = getRotationOfSector(groupIndex, sectorIndex); float sectorAngle = getAngleOfSectorInGroup(groupIndex); - return rotation <= mouseAngle && mouseAngle <= (rotation + sectorAngle) && dirLength >= mDescription.minRadius && dirLength <= mDescription.maxRadius; + return rotation <= mouseAngle && mouseAngle <= (rotation + sectorAngle) && dirLength >= mDescription.minRadius && + dirLength <= mDescription.maxRadius; } bool Falcor::SelectionWheel::isMouseOnGroup(const float2& mousePos, uint32_t groupIndex, uint32_t& sectorIndex) { - checkArgument(groupIndex < (uint32_t)mDescription.sectorGroups.size(), "'groupIndex' ({}) is out of bounds.", groupIndex); + FALCOR_CHECK(groupIndex < (uint32_t)mDescription.sectorGroups.size(), "'groupIndex' ({}) is out of bounds.", groupIndex); float dirLength, mouseAngle; computeMouseAngleAndDirLength(mousePos, mouseAngle, dirLength); @@ -127,7 +153,8 @@ bool Falcor::SelectionWheel::isMouseOnGroup(const float2& mousePos, uint32_t gro float sectorAngle = getAngleOfSectorInGroup(groupIndex); float maxRotation = minRotation + sectorAngle * mDescription.sectorGroups[groupIndex]; - bool isInGroup = minRotation <= mouseAngle && mouseAngle <= maxRotation && dirLength >= mDescription.minRadius && dirLength <= mDescription.maxRadius; + bool isInGroup = minRotation <= mouseAngle && mouseAngle <= maxRotation && dirLength >= mDescription.minRadius && + dirLength <= mDescription.maxRadius; if (isInGroup) { @@ -143,8 +170,8 @@ bool Falcor::SelectionWheel::isMouseOnGroup(const float2& mousePos, uint32_t gro float2 Falcor::SelectionWheel::getCenterPositionOfSector(uint32_t groupIndex, uint32_t sectorIndex) { - checkArgument(groupIndex < (uint32_t)mDescription.sectorGroups.size(), "'groupIndex' ({}) is out of bounds.", groupIndex); - checkArgument(sectorIndex < mDescription.sectorGroups[groupIndex], "'sectorIndex' ({}) is out of bounds.", sectorIndex); + FALCOR_CHECK(groupIndex < (uint32_t)mDescription.sectorGroups.size(), "'groupIndex' ({}) is out of bounds.", groupIndex); + FALCOR_CHECK(sectorIndex < mDescription.sectorGroups[groupIndex], "'sectorIndex' ({}) is out of bounds.", sectorIndex); float rotation = getRotationOfSector(groupIndex, sectorIndex); float sectorAngle = getAngleOfSectorInGroup(groupIndex); float angle = rotation + sectorAngle * 0.5f; @@ -154,7 +181,7 @@ float2 Falcor::SelectionWheel::getCenterPositionOfSector(uint32_t groupIndex, ui float Falcor::SelectionWheel::getAngleOfSectorInGroup(uint32_t groupIndex) { - checkArgument(groupIndex < (uint32_t)mDescription.sectorGroups.size(), "'groupIndex' ({}) is out of bounds.", groupIndex); + FALCOR_CHECK(groupIndex < (uint32_t)mDescription.sectorGroups.size(), "'groupIndex' ({}) is out of bounds.", groupIndex); const uint32_t kSectorCount = mDescription.sectorGroups[groupIndex]; float groupAngle = getGroupAngle(); return groupAngle / (float)kSectorCount; @@ -162,8 +189,8 @@ float Falcor::SelectionWheel::getAngleOfSectorInGroup(uint32_t groupIndex) float Falcor::SelectionWheel::getRotationOfSector(uint32_t groupIndex, uint32_t sectorIndex) { - checkArgument(groupIndex < (uint32_t)mDescription.sectorGroups.size(), "'groupIndex' ({}) is out of bounds.", groupIndex); - checkArgument(sectorIndex < mDescription.sectorGroups[groupIndex], "'sectorIndex' ({}) is out of bounds.", sectorIndex); + FALCOR_CHECK(groupIndex < (uint32_t)mDescription.sectorGroups.size(), "'groupIndex' ({}) is out of bounds.", groupIndex); + FALCOR_CHECK(sectorIndex < mDescription.sectorGroups[groupIndex], "'sectorIndex' ({}) is out of bounds.", sectorIndex); float groupAngle = getGroupAngle(); float sectorAngle = getAngleOfSectorInGroup(groupIndex); return groupAngle * groupIndex + sectorAngle * sectorIndex; @@ -193,9 +220,26 @@ void Falcor::SelectionWheel::computeGroupAndSectorIndexFromAngle(float angle, ui sectorIndex = (uint32_t)std::floor((angle - minRotation) / sectorAngle); } -void Falcor::SelectionWheel::addCircleSector(float rotation, float angle, const float4& color, const float4& borderColor, float margin, bool marginOnBothSides, ExcludeBorderFlags excludeBorderFlags) +void Falcor::SelectionWheel::addCircleSector( + float rotation, + float angle, + const float4& color, + const float4& borderColor, + float margin, + bool marginOnBothSides, + ExcludeBorderFlags excludeBorderFlags +) { constexpr float kStartOffset = (float)M_PI / 2.f; - float rotaion = kStartOffset - rotation - angle*0.5f - (marginOnBothSides ? 0.f : margin*0.5f); - mMarker2DSet.addCircleSector(mDescription.position, rotaion, angle - std::fabs(marginOnBothSides ? 2.f * margin : margin), mDescription.minRadius, mDescription.maxRadius, color, borderColor, excludeBorderFlags); + float rotaion = kStartOffset - rotation - angle * 0.5f - (marginOnBothSides ? 0.f : margin * 0.5f); + mMarker2DSet.addCircleSector( + mDescription.position, + rotaion, + angle - std::fabs(marginOnBothSides ? 2.f * margin : margin), + mDescription.minRadius, + mDescription.maxRadius, + color, + borderColor, + excludeBorderFlags + ); } diff --git a/Source/RenderPasses/SDFEditor/SelectionWheel.h b/Source/RenderPasses/SDFEditor/SelectionWheel.h index 1f9c5a4c1..e32db1a38 100644 --- a/Source/RenderPasses/SDFEditor/SelectionWheel.h +++ b/Source/RenderPasses/SDFEditor/SelectionWheel.h @@ -32,52 +32,64 @@ namespace Falcor { - class SelectionWheel - { - public: - static constexpr uint32_t kInvalidIndex = -1; +class SelectionWheel +{ +public: + static constexpr uint32_t kInvalidIndex = -1; - struct Desc - { - std::vector sectorGroups; ///< Describes how many sectors each group should have in the selection wheel. - float2 position; ///< Center position of the selection wheel. - float minRadius; ///< The minimum radius of the selection wheel. - float maxRadius; ///< The maximum radius of the selection wheel. - float4 baseColor; ///< The base color of the selection wheel. - float4 highlightColor; ///< The highlight color for the selected sector. - float4 lineColor; ///< The color of the lines that separate sectors and groups. - float borderWidth; ///< Thickness of the border in pixels. - }; + struct Desc + { + std::vector sectorGroups; ///< Describes how many sectors each group should have in the selection wheel. + float2 position; ///< Center position of the selection wheel. + float minRadius; ///< The minimum radius of the selection wheel. + float maxRadius; ///< The maximum radius of the selection wheel. + float4 baseColor; ///< The base color of the selection wheel. + float4 highlightColor; ///< The highlight color for the selected sector. + float4 lineColor; ///< The color of the lines that separate sectors and groups. + float borderWidth; ///< Thickness of the border in pixels. + }; - SelectionWheel(Marker2DSet& marker2DSet) : mMarker2DSet(marker2DSet) {} + SelectionWheel(Marker2DSet& marker2DSet) : mMarker2DSet(marker2DSet) {} - void update(const float2& mousePos, const Desc& description); + void update(const float2& mousePos, const Desc& description); - bool isMouseOnSector(const float2& mousePos, uint32_t groupIndex, uint32_t sectorIndex); - bool isMouseOnGroup(const float2& mousePos, uint32_t groupIndex, uint32_t& sectorIndex); + bool isMouseOnSector(const float2& mousePos, uint32_t groupIndex, uint32_t sectorIndex); + bool isMouseOnGroup(const float2& mousePos, uint32_t groupIndex, uint32_t& sectorIndex); - float2 getCenterPositionOfSector(uint32_t groupIndex, uint32_t sectorIndex); + float2 getCenterPositionOfSector(uint32_t groupIndex, uint32_t sectorIndex); - float getAngleOfSectorInGroup(uint32_t groupIndex); - float getRotationOfSector(uint32_t groupIndex, uint32_t sectorIndex); - float getGroupAngle(); + float getAngleOfSectorInGroup(uint32_t groupIndex); + float getRotationOfSector(uint32_t groupIndex, uint32_t sectorIndex); + float getGroupAngle(); - private: - void computeMouseAngleAndDirLength(const float2& mousePos, float& mouseAngle, float& dirLength); - void computeGroupAndSectorIndexFromAngle(float mouseAngle, uint32_t& groupIndex, uint32_t& sectorIndex); +private: + void computeMouseAngleAndDirLength(const float2& mousePos, float& mouseAngle, float& dirLength); + void computeGroupAndSectorIndexFromAngle(float mouseAngle, uint32_t& groupIndex, uint32_t& sectorIndex); - /** Specialized add function for this class. When adding a circle sector this will allow for cutting off some parts of the sector's sides. - \param[in] rotation The starting angle of the sector. It is the left side of the sector. - \param[in] angle The angle that describes the circle sector. - \param[in] color The color of the circle sector. - \param[in] margin The cutoff of one of the sides. If positive, it will cut off from the left side, if negative, from the right, and if zero, no cut off. - \param[in] marginOnBothSides If true, does the cut off on both sides instead of one. The margin variable need to be positive if this is true. - \param[in] excludeBorderFlags Flags for which borders should be excluded from rendering. - */ - void addCircleSector(float rotation, float angle, const float4& color, const float4& borderColor, float margin = 0.f, bool marginOnBothSides = false, ExcludeBorderFlags excludeBorderFlags = ExcludeBorderFlags::None); + /** + * Specialized add function for this class. When adding a circle sector this will allow for cutting off some parts of the sector's + * sides. + * @param[in] rotation The starting angle of the sector. It is the left side of the sector. + * @param[in] angle The angle that describes the circle sector. + * @param[in] color The color of the circle sector. + * @param[in] margin The cutoff of one of the sides. If positive, it will cut off from the left side, if negative, from the right, and + * if zero, no cut off. + * @param[in] marginOnBothSides If true, does the cut off on both sides instead of one. The margin variable need to be positive if this + * is true. + * @param[in] excludeBorderFlags Flags for which borders should be excluded from rendering. + */ + void addCircleSector( + float rotation, + float angle, + const float4& color, + const float4& borderColor, + float margin = 0.f, + bool marginOnBothSides = false, + ExcludeBorderFlags excludeBorderFlags = ExcludeBorderFlags::None + ); - private: - Desc mDescription; - Marker2DSet& mMarker2DSet; - }; -} +private: + Desc mDescription; + Marker2DSet& mMarker2DSet; +}; +} // namespace Falcor diff --git a/Source/RenderPasses/SVGFPass/SVGFAtrous.ps.slang b/Source/RenderPasses/SVGFPass/SVGFAtrous.ps.slang index bcc3b7e77..ca9849b36 100644 --- a/Source/RenderPasses/SVGFPass/SVGFAtrous.ps.slang +++ b/Source/RenderPasses/SVGFPass/SVGFAtrous.ps.slang @@ -31,14 +31,14 @@ import SVGFCommon; cbuffer PerImageCB { - Texture2D gAlbedo; - Texture2D gHistoryLength; - Texture2D gIllumination; - Texture2D gLinearZAndNormal; - - int gStepSize; - float gPhiColor; - float gPhiNormal; + Texture2D gAlbedo; + Texture2D gHistoryLength; + Texture2D gIllumination; + Texture2D gLinearZAndNormal; + + int gStepSize; + float gPhiColor; + float gPhiNormal; }; // computes a 3x3 gaussian blur of the variance, centered around @@ -48,8 +48,8 @@ float computeVarianceCenter(int2 ipos) float sum = 0.f; const float kernel[2][2] = { - { 1.0 / 4.0, 1.0 / 8.0 }, - { 1.0 / 8.0, 1.0 / 16.0 } + { 1.0 / 4.0, 1.0 / 8.0 }, + { 1.0 / 8.0, 1.0 / 16.0 }, }; const int radius = 1; @@ -68,10 +68,10 @@ float computeVarianceCenter(int2 ipos) float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 { - const int2 ipos = int2(vsOut.posH.xy); + const int2 ipos = int2(vsOut.posH.xy); const int2 screenSize = getTextureDims(gAlbedo, 0); - const float epsVariance = 1e-10; + const float epsVariance = 1e-10; const float kernelWeights[3] = { 1.0, 2.0 / 3.0, 1.0 / 6.0 }; // constant samplers to prevent the compiler from generating code which @@ -93,20 +93,20 @@ float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 } const float3 nCenter = oct_to_ndir_snorm(gLinearZAndNormal[ipos].zw); - const float phiLIllumination = gPhiColor * sqrt(max(0.0, epsVariance + var.r)); - const float phiDepth = max(zCenter.y, 1e-8) * gStepSize; + const float phiLIllumination = gPhiColor * sqrt(max(0.0, epsVariance + var.r)); + const float phiDepth = max(zCenter.y, 1e-8) * gStepSize; // explicitly store/accumulate center pixel with weight 1 to prevent issues // with the edge-stopping functions - float sumWIllumination = 1.0; - float4 sumIllumination = illuminationCenter; + float sumWIllumination = 1.0; + float4 sumIllumination = illuminationCenter; for (int yy = -2; yy <= 2; yy++) { for (int xx = -2; xx <= 2; xx++) { - const int2 p = ipos + int2(xx, yy) * gStepSize; - const bool inside = all(p >= int2(0,0)) && all(p < screenSize); + const int2 p = ipos + int2(xx, yy) * gStepSize; + const bool inside = all(p >= int2(0, 0)) && all(p < screenSize); const float kernel = kernelWeights[abs(xx)] * kernelWeights[abs(yy)]; @@ -119,15 +119,22 @@ float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 // compute the edge-stopping functions const float2 w = computeWeight( - zCenter.x, zP, phiDepth * length(float2(xx, yy)), - nCenter, nP, gPhiNormal, - lIlluminationCenter, lIlluminationP, phiLIllumination); + zCenter.x, + zP, + phiDepth * length(float2(xx, yy)), + nCenter, + nP, + gPhiNormal, + lIlluminationCenter, + lIlluminationP, + phiLIllumination + ); const float wIllumination = w.x * kernel; // alpha channel contains the variance, therefore the weights need to be squared, see paper for the formula - sumWIllumination += wIllumination; - sumIllumination += float4(wIllumination.xxx, wIllumination * wIllumination) * illuminationP; + sumWIllumination += wIllumination; + sumIllumination += float4(wIllumination.xxx, wIllumination * wIllumination) * illuminationP; } } } diff --git a/Source/RenderPasses/SVGFPass/SVGFCommon.slang b/Source/RenderPasses/SVGFPass/SVGFCommon.slang index 66e17d113..b801b2133 100644 --- a/Source/RenderPasses/SVGFPass/SVGFCommon.slang +++ b/Source/RenderPasses/SVGFPass/SVGFCommon.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -29,29 +29,36 @@ int2 getTextureDims(Texture2D tex, uint mip) { uint w, h; tex.GetDimensions(w, h); - return int2(w,h); + return int2(w, h); } float computeWeight( - float depthCenter, float depthP, float phiDepth, - float3 normalCenter, float3 normalP, float phiNormal, - float luminanceIllumCenter, float luminanceIllumP, float phiIllum) + float depthCenter, + float depthP, + float phiDepth, + float3 normalCenter, + float3 normalP, + float phiNormal, + float luminanceIllumCenter, + float luminanceIllumP, + float phiIllum +) { - const float weightNormal = pow(saturate(dot(normalCenter, normalP)), phiNormal); - const float weightZ = (phiDepth == 0) ? 0.0f : abs(depthCenter - depthP) / phiDepth; - const float weightLillum = abs(luminanceIllumCenter - luminanceIllumP) / phiIllum; + const float weightNormal = pow(saturate(dot(normalCenter, normalP)), phiNormal); + const float weightZ = (phiDepth == 0) ? 0.0f : abs(depthCenter - depthP) / phiDepth; + const float weightLillum = abs(luminanceIllumCenter - luminanceIllumP) / phiIllum; - const float weightIllum = exp(0.0 - max(weightLillum, 0.0) - max(weightZ, 0.0)) * weightNormal; + const float weightIllum = exp(0.0 - max(weightLillum, 0.0) - max(weightZ, 0.0)) * weightNormal; return weightIllum; } struct FullScreenPassVsOut { - float2 texC : TEXCOORD; + float2 texC : TEXCOORD; #ifndef _VIEWPORT_MASK - float4 posH : SV_POSITION; + float4 posH : SV_POSITION; #else - float4 posH : POSITION; + float4 posH : POSITION; #endif }; diff --git a/Source/RenderPasses/SVGFPass/SVGFFilterMoments.ps.slang b/Source/RenderPasses/SVGFPass/SVGFFilterMoments.ps.slang index ff116e748..3059d28dd 100644 --- a/Source/RenderPasses/SVGFPass/SVGFFilterMoments.ps.slang +++ b/Source/RenderPasses/SVGFPass/SVGFFilterMoments.ps.slang @@ -31,13 +31,13 @@ import SVGFCommon; cbuffer PerImageCB { - Texture2D gIllumination; - Texture2D gMoments; - Texture2D gHistoryLength; - Texture2D gLinearZAndNormal; + Texture2D gIllumination; + Texture2D gMoments; + Texture2D gHistoryLength; + Texture2D gLinearZAndNormal; - float gPhiColor; - float gPhiNormal; + float gPhiColor; + float gPhiNormal; }; float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 @@ -50,9 +50,9 @@ float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 if (h < 4.0) // not enough temporal history available { - float sumWIllumination = 0.0; - float3 sumIllumination = float3(0.0, 0.0, 0.0); - float2 sumMoments = float2(0.0, 0.0); + float sumWIllumination = 0.0; + float3 sumIllumination = float3(0.0, 0.0, 0.0); + float2 sumMoments = float2(0.0, 0.0); const float4 illuminationCenter = gIllumination[ipos]; const float lIlluminationCenter = luminance(illuminationCenter.rgb); @@ -64,8 +64,8 @@ float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 return illuminationCenter; } const float3 nCenter = oct_to_ndir_snorm(gLinearZAndNormal[ipos].zw); - const float phiLIllumination = gPhiColor; - const float phiDepth = max(zCenter.y, 1e-8) * 3.0; + const float phiLIllumination = gPhiColor; + const float phiDepth = max(zCenter.y, 1e-8) * 3.0; // compute first and second moment spatially. This code also applies cross-bilateral // filtering on the input illumination. @@ -75,26 +75,33 @@ float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 { for (int xx = -radius; xx <= radius; xx++) { - const int2 p = ipos + int2(xx, yy); - const bool inside = all(p >= int2(0,0)) && all(p < screenSize); - const bool samePixel = (xx ==0 && yy == 0); + const int2 p = ipos + int2(xx, yy); + const bool inside = all(p >= int2(0, 0)) && all(p < screenSize); + const bool samePixel = (xx == 0 && yy == 0); const float kernel = 1.0; if (inside) { const float3 illuminationP = gIllumination[p].rgb; - const float2 momentsP = gMoments[p].xy; + const float2 momentsP = gMoments[p].xy; const float lIlluminationP = luminance(illuminationP.rgb); const float zP = gLinearZAndNormal[p].x; const float3 nP = oct_to_ndir_snorm(gLinearZAndNormal[p].zw); const float w = computeWeight( - zCenter.x, zP, phiDepth * length(float2(xx, yy)), - nCenter, nP, gPhiNormal, - lIlluminationCenter, lIlluminationP, phiLIllumination); + zCenter.x, + zP, + phiDepth * length(float2(xx, yy)), + nCenter, + nP, + gPhiNormal, + lIlluminationCenter, + lIlluminationP, + phiLIllumination + ); sumWIllumination += w; - sumIllumination += illuminationP * w; + sumIllumination += illuminationP * w; sumMoments += momentsP * w; } } @@ -103,8 +110,8 @@ float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 // Clamp sum to >0 to avoid NaNs. sumWIllumination = max(sumWIllumination, 1e-6f); - sumIllumination /= sumWIllumination; - sumMoments /= sumWIllumination; + sumIllumination /= sumWIllumination; + sumMoments /= sumWIllumination; // compute variance using the first and second moments float variance = sumMoments.g - sumMoments.r * sumMoments.r; diff --git a/Source/RenderPasses/SVGFPass/SVGFFinalModulate.ps.slang b/Source/RenderPasses/SVGFPass/SVGFFinalModulate.ps.slang index d0fb3f70b..6344d5451 100644 --- a/Source/RenderPasses/SVGFPass/SVGFFinalModulate.ps.slang +++ b/Source/RenderPasses/SVGFPass/SVGFFinalModulate.ps.slang @@ -29,9 +29,9 @@ import SVGFCommon; cbuffer PerImageCB { - Texture2D gAlbedo; - Texture2D gEmission; - Texture2D gIllumination; + Texture2D gAlbedo; + Texture2D gEmission; + Texture2D gIllumination; }; float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 diff --git a/Source/RenderPasses/SVGFPass/SVGFPackLinearZAndNormal.ps.slang b/Source/RenderPasses/SVGFPass/SVGFPackLinearZAndNormal.ps.slang index b11d767d9..7a9709ffa 100644 --- a/Source/RenderPasses/SVGFPass/SVGFPackLinearZAndNormal.ps.slang +++ b/Source/RenderPasses/SVGFPass/SVGFPackLinearZAndNormal.ps.slang @@ -42,4 +42,3 @@ float4 main(FullScreenPassVsOut vsOut) : SV_TARGET0 const float2 nPacked = ndir_to_oct_snorm(gNormal[ipos].xyz); return float4(gLinearZ[ipos].xy, nPacked.x, nPacked.y); } - diff --git a/Source/RenderPasses/SVGFPass/SVGFPass.cpp b/Source/RenderPasses/SVGFPass/SVGFPass.cpp index 12c0cbc4b..307b0ea7e 100644 --- a/Source/RenderPasses/SVGFPass/SVGFPass.cpp +++ b/Source/RenderPasses/SVGFPass/SVGFPass.cpp @@ -37,61 +37,69 @@ namespace { - // Shader source files - const char kPackLinearZAndNormalShader[] = "RenderPasses/SVGFPass/SVGFPackLinearZAndNormal.ps.slang"; - const char kReprojectShader[] = "RenderPasses/SVGFPass/SVGFReproject.ps.slang"; - const char kAtrousShader[] = "RenderPasses/SVGFPass/SVGFAtrous.ps.slang"; - const char kFilterMomentShader[] = "RenderPasses/SVGFPass/SVGFFilterMoments.ps.slang"; - const char kFinalModulateShader[] = "RenderPasses/SVGFPass/SVGFFinalModulate.ps.slang"; - - // Names of valid entries in the parameter dictionary. - const char kEnabled[] = "Enabled"; - const char kIterations[] = "Iterations"; - const char kFeedbackTap[] = "FeedbackTap"; - const char kVarianceEpsilon[] = "VarianceEpsilon"; - const char kPhiColor[] = "PhiColor"; - const char kPhiNormal[] = "PhiNormal"; - const char kAlpha[] = "Alpha"; - const char kMomentsAlpha[] = "MomentsAlpha"; - - // Input buffer names - const char kInputBufferAlbedo[] = "Albedo"; - const char kInputBufferColor[] = "Color"; - const char kInputBufferEmission[] = "Emission"; - const char kInputBufferWorldPosition[] = "WorldPosition"; - const char kInputBufferWorldNormal[] = "WorldNormal"; - const char kInputBufferPosNormalFwidth[] = "PositionNormalFwidth"; - const char kInputBufferLinearZ[] = "LinearZ"; - const char kInputBufferMotionVector[] = "MotionVec"; - - // Internal buffer names - const char kInternalBufferPreviousLinearZAndNormal[] = "Previous Linear Z and Packed Normal"; - const char kInternalBufferPreviousLighting[] = "Previous Lighting"; - const char kInternalBufferPreviousMoments[] = "Previous Moments"; - - // Output buffer name - const char kOutputBufferFilteredImage[] = "Filtered image"; -} +// Shader source files +const char kPackLinearZAndNormalShader[] = "RenderPasses/SVGFPass/SVGFPackLinearZAndNormal.ps.slang"; +const char kReprojectShader[] = "RenderPasses/SVGFPass/SVGFReproject.ps.slang"; +const char kAtrousShader[] = "RenderPasses/SVGFPass/SVGFAtrous.ps.slang"; +const char kFilterMomentShader[] = "RenderPasses/SVGFPass/SVGFFilterMoments.ps.slang"; +const char kFinalModulateShader[] = "RenderPasses/SVGFPass/SVGFFinalModulate.ps.slang"; + +// Names of valid entries in the parameter dictionary. +const char kEnabled[] = "Enabled"; +const char kIterations[] = "Iterations"; +const char kFeedbackTap[] = "FeedbackTap"; +const char kVarianceEpsilon[] = "VarianceEpsilon"; +const char kPhiColor[] = "PhiColor"; +const char kPhiNormal[] = "PhiNormal"; +const char kAlpha[] = "Alpha"; +const char kMomentsAlpha[] = "MomentsAlpha"; + +// Input buffer names +const char kInputBufferAlbedo[] = "Albedo"; +const char kInputBufferColor[] = "Color"; +const char kInputBufferEmission[] = "Emission"; +const char kInputBufferWorldPosition[] = "WorldPosition"; +const char kInputBufferWorldNormal[] = "WorldNormal"; +const char kInputBufferPosNormalFwidth[] = "PositionNormalFwidth"; +const char kInputBufferLinearZ[] = "LinearZ"; +const char kInputBufferMotionVector[] = "MotionVec"; + +// Internal buffer names +const char kInternalBufferPreviousLinearZAndNormal[] = "Previous Linear Z and Packed Normal"; +const char kInternalBufferPreviousLighting[] = "Previous Lighting"; +const char kInternalBufferPreviousMoments[] = "Previous Moments"; + +// Output buffer name +const char kOutputBufferFilteredImage[] = "Filtered image"; +} // namespace extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { registry.registerClass(); } -SVGFPass::SVGFPass(ref pDevice, const Properties& props) - : RenderPass(pDevice) +SVGFPass::SVGFPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { for (const auto& [key, value] : props) { - if (key == kEnabled) mFilterEnabled = value; - else if (key == kIterations) mFilterIterations = value; - else if (key == kFeedbackTap) mFeedbackTap = value; - else if (key == kVarianceEpsilon) mVarainceEpsilon = value; - else if (key == kPhiColor) mPhiColor = value; - else if (key == kPhiNormal) mPhiNormal = value; - else if (key == kAlpha) mAlpha = value; - else if (key == kMomentsAlpha) mMomentsAlpha = value; - else logWarning("Unknown property '{}' in SVGFPass properties.", key); + if (key == kEnabled) + mFilterEnabled = value; + else if (key == kIterations) + mFilterIterations = value; + else if (key == kFeedbackTap) + mFeedbackTap = value; + else if (key == kVarianceEpsilon) + mVarainceEpsilon = value; + else if (key == kPhiColor) + mPhiColor = value; + else if (key == kPhiNormal) + mPhiNormal = value; + else if (key == kAlpha) + mAlpha = value; + else if (key == kMomentsAlpha) + mMomentsAlpha = value; + else + logWarning("Unknown property '{}' in SVGFPass properties.", key); } mpPackLinearZAndNormal = FullScreenPass::create(mpDevice, kPackLinearZAndNormalShader); @@ -143,16 +151,13 @@ RenderPassReflection SVGFPass::reflect(const CompileData& compileData) reflector.addInternal(kInternalBufferPreviousLinearZAndNormal, "Previous Linear Z and Packed Normal") .format(ResourceFormat::RGBA32Float) - .bindFlags(Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource) - ; + .bindFlags(ResourceBindFlags::RenderTarget | ResourceBindFlags::ShaderResource); reflector.addInternal(kInternalBufferPreviousLighting, "Previous Filtered Lighting") .format(ResourceFormat::RGBA32Float) - .bindFlags(Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource) - ; + .bindFlags(ResourceBindFlags::RenderTarget | ResourceBindFlags::ShaderResource); reflector.addInternal(kInternalBufferPreviousMoments, "Previous Moments") .format(ResourceFormat::RG32Float) - .bindFlags(Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource) - ; + .bindFlags(ResourceBindFlags::RenderTarget | ResourceBindFlags::ShaderResource); reflector.addOutput(kOutputBufferFilteredImage, "Filtered image").format(ResourceFormat::RGBA16Float); @@ -178,9 +183,10 @@ void SVGFPass::execute(RenderContext* pRenderContext, const RenderData& renderDa ref pOutputTexture = renderData.getTexture(kOutputBufferFilteredImage); - FALCOR_ASSERT(mpFilteredIlluminationFbo && - mpFilteredIlluminationFbo->getWidth() == pAlbedoTexture->getWidth() && - mpFilteredIlluminationFbo->getHeight() == pAlbedoTexture->getHeight()); + FALCOR_ASSERT( + mpFilteredIlluminationFbo && mpFilteredIlluminationFbo->getWidth() == pAlbedoTexture->getWidth() && + mpFilteredIlluminationFbo->getHeight() == pAlbedoTexture->getHeight() + ); if (mBuffersNeedClear) { @@ -198,11 +204,16 @@ void SVGFPass::execute(RenderContext* pRenderContext, const RenderData& renderDa // reprojected filtered illumination from the previous frame. // Stores the result as well as initial moments and an updated // per-pixel history length in mpCurReprojFbo. - ref pPrevLinearZAndNormalTexture = - renderData.getTexture(kInternalBufferPreviousLinearZAndNormal); - computeReprojection(pRenderContext, pAlbedoTexture, pColorTexture, pEmissionTexture, - pMotionVectorTexture, pPosNormalFwidthTexture, - pPrevLinearZAndNormalTexture); + ref pPrevLinearZAndNormalTexture = renderData.getTexture(kInternalBufferPreviousLinearZAndNormal); + computeReprojection( + pRenderContext, + pAlbedoTexture, + pColorTexture, + pEmissionTexture, + pMotionVectorTexture, + pPosNormalFwidthTexture, + pPrevLinearZAndNormalTexture + ); // Do a first cross-bilateral filtering of the illumination and // estimate its variance, storing the result into a float4 in @@ -227,8 +238,7 @@ void SVGFPass::execute(RenderContext* pRenderContext, const RenderData& renderDa // Swap resources so we're ready for next frame. std::swap(mpCurReprojFbo, mpPrevReprojFbo); - pRenderContext->blit(mpLinearZAndNormalFbo->getColorTexture(0)->getSRV(), - pPrevLinearZAndNormalTexture->getRTV()); + pRenderContext->blit(mpLinearZAndNormalFbo->getColorTexture(0)->getSRV(), pPrevLinearZAndNormalTexture->getRTV()); } else { @@ -246,7 +256,7 @@ void SVGFPass::allocateFbos(uint2 dim, RenderContext* pRenderContext) desc.setColorTarget(0, Falcor::ResourceFormat::RGBA32Float); // illumination desc.setColorTarget(1, Falcor::ResourceFormat::RG32Float); // moments desc.setColorTarget(2, Falcor::ResourceFormat::R16Float); // history length - mpCurReprojFbo = Fbo::create2D(mpDevice, dim.x, dim.y, desc); + mpCurReprojFbo = Fbo::create2D(mpDevice, dim.x, dim.y, desc); mpPrevReprojFbo = Fbo::create2D(mpDevice, dim.x, dim.y, desc); } @@ -261,11 +271,11 @@ void SVGFPass::allocateFbos(uint2 dim, RenderContext* pRenderContext) // Screen-size FBOs with 1 RGBA32F buffer Fbo::Desc desc; desc.setColorTarget(0, Falcor::ResourceFormat::RGBA32Float); - mpPingPongFbo[0] = Fbo::create2D(mpDevice, dim.x, dim.y, desc); - mpPingPongFbo[1] = Fbo::create2D(mpDevice, dim.x, dim.y, desc); + mpPingPongFbo[0] = Fbo::create2D(mpDevice, dim.x, dim.y, desc); + mpPingPongFbo[1] = Fbo::create2D(mpDevice, dim.x, dim.y, desc); mpFilteredPastFbo = Fbo::create2D(mpDevice, dim.x, dim.y, desc); - mpFilteredIlluminationFbo = Fbo::create2D(mpDevice, dim.x, dim.y, desc); - mpFinalFbo = Fbo::create2D(mpDevice, dim.x, dim.y, desc); + mpFilteredIlluminationFbo = Fbo::create2D(mpDevice, dim.x, dim.y, desc); + mpFinalFbo = Fbo::create2D(mpDevice, dim.x, dim.y, desc); } mBuffersNeedClear = true; @@ -291,8 +301,7 @@ void SVGFPass::clearBuffers(RenderContext* pRenderContext, const RenderData& ren // (It's slightly wasteful to copy linear z here, but having this all // together in a single buffer is a small simplification, since we make a // copy of it to refer to in the next frame.) -void SVGFPass::computeLinearZAndNormal(RenderContext* pRenderContext, ref pLinearZTexture, - ref pWorldNormalTexture) +void SVGFPass::computeLinearZAndNormal(RenderContext* pRenderContext, ref pLinearZTexture, ref pWorldNormalTexture) { auto perImageCB = mpPackLinearZAndNormal->getRootVar()["PerImageCB"]; perImageCB["gLinearZ"] = pLinearZTexture; @@ -301,24 +310,28 @@ void SVGFPass::computeLinearZAndNormal(RenderContext* pRenderContext, refexecute(pRenderContext, mpLinearZAndNormalFbo); } -void SVGFPass::computeReprojection(RenderContext* pRenderContext, ref pAlbedoTexture, - ref pColorTexture, ref pEmissionTexture, - ref pMotionVectorTexture, - ref pPositionNormalFwidthTexture, - ref pPrevLinearZTexture) +void SVGFPass::computeReprojection( + RenderContext* pRenderContext, + ref pAlbedoTexture, + ref pColorTexture, + ref pEmissionTexture, + ref pMotionVectorTexture, + ref pPositionNormalFwidthTexture, + ref pPrevLinearZTexture +) { auto perImageCB = mpReprojection->getRootVar()["PerImageCB"]; // Setup textures for our reprojection shader pass - perImageCB["gMotion"] = pMotionVectorTexture; - perImageCB["gColor"] = pColorTexture; - perImageCB["gEmission"] = pEmissionTexture; - perImageCB["gAlbedo"] = pAlbedoTexture; + perImageCB["gMotion"] = pMotionVectorTexture; + perImageCB["gColor"] = pColorTexture; + perImageCB["gEmission"] = pEmissionTexture; + perImageCB["gAlbedo"] = pAlbedoTexture; perImageCB["gPositionNormalFwidth"] = pPositionNormalFwidthTexture; - perImageCB["gPrevIllum"] = mpFilteredPastFbo->getColorTexture(0); - perImageCB["gPrevMoments"] = mpPrevReprojFbo->getColorTexture(1); - perImageCB["gLinearZAndNormal"] = mpLinearZAndNormalFbo->getColorTexture(0); - perImageCB["gPrevLinearZAndNormal"] = pPrevLinearZTexture; + perImageCB["gPrevIllum"] = mpFilteredPastFbo->getColorTexture(0); + perImageCB["gPrevMoments"] = mpPrevReprojFbo->getColorTexture(1); + perImageCB["gLinearZAndNormal"] = mpLinearZAndNormalFbo->getColorTexture(0); + perImageCB["gPrevLinearZAndNormal"] = pPrevLinearZTexture; perImageCB["gPrevHistoryLength"] = mpPrevReprojFbo->getColorTexture(2); // Setup variables for our reprojection pass @@ -332,13 +345,13 @@ void SVGFPass::computeFilteredMoments(RenderContext* pRenderContext) { auto perImageCB = mpFilterMoments->getRootVar()["PerImageCB"]; - perImageCB["gIllumination"] = mpCurReprojFbo->getColorTexture(0); - perImageCB["gHistoryLength"] = mpCurReprojFbo->getColorTexture(2); - perImageCB["gLinearZAndNormal"] = mpLinearZAndNormalFbo->getColorTexture(0); - perImageCB["gMoments"] = mpCurReprojFbo->getColorTexture(1); + perImageCB["gIllumination"] = mpCurReprojFbo->getColorTexture(0); + perImageCB["gHistoryLength"] = mpCurReprojFbo->getColorTexture(2); + perImageCB["gLinearZAndNormal"] = mpLinearZAndNormalFbo->getColorTexture(0); + perImageCB["gMoments"] = mpCurReprojFbo->getColorTexture(1); - perImageCB["gPhiColor"] = mPhiColor; - perImageCB["gPhiNormal"] = mPhiNormal; + perImageCB["gPhiColor"] = mPhiColor; + perImageCB["gPhiNormal"] = mPhiNormal; mpFilterMoments->execute(pRenderContext, mpPingPongFbo[0]); } @@ -347,11 +360,11 @@ void SVGFPass::computeAtrousDecomposition(RenderContext* pRenderContext, refgetRootVar()["PerImageCB"]; - perImageCB["gAlbedo"] = pAlbedoTexture; + perImageCB["gAlbedo"] = pAlbedoTexture; perImageCB["gHistoryLength"] = mpCurReprojFbo->getColorTexture(2); - perImageCB["gLinearZAndNormal"] = mpLinearZAndNormalFbo->getColorTexture(0); + perImageCB["gLinearZAndNormal"] = mpLinearZAndNormalFbo->getColorTexture(0); - perImageCB["gPhiColor"] = mPhiColor; + perImageCB["gPhiColor"] = mPhiColor; perImageCB["gPhiNormal"] = mPhiNormal; for (int i = 0; i < mFilterIterations; i++) @@ -400,5 +413,6 @@ void SVGFPass::renderUI(Gui::Widgets& widget) dirty |= (int)widget.var("Alpha", mAlpha, 0.0f, 1.0f, 0.001f); dirty |= (int)widget.var("Moments Alpha", mMomentsAlpha, 0.0f, 1.0f, 0.001f); - if (dirty) mBuffersNeedClear = true; + if (dirty) + mBuffersNeedClear = true; } diff --git a/Source/RenderPasses/SVGFPass/SVGFPass.h b/Source/RenderPasses/SVGFPass/SVGFPass.h index e979ee080..3f3cfce47 100644 --- a/Source/RenderPasses/SVGFPass/SVGFPass.h +++ b/Source/RenderPasses/SVGFPass/SVGFPass.h @@ -51,27 +51,30 @@ class SVGFPass : public RenderPass void allocateFbos(uint2 dim, RenderContext* pRenderContext); void clearBuffers(RenderContext* pRenderContext, const RenderData& renderData); - void computeLinearZAndNormal(RenderContext* pRenderContext, ref pLinearZTexture, - ref pWorldNormalTexture); - void computeReprojection(RenderContext* pRenderContext, ref pAlbedoTexture, - ref pColorTexture, ref pEmissionTexture, - ref pMotionVectorTexture, - ref pPositionNormalFwidthTexture, - ref pPrevLinearZAndNormalTexture); + void computeLinearZAndNormal(RenderContext* pRenderContext, ref pLinearZTexture, ref pWorldNormalTexture); + void computeReprojection( + RenderContext* pRenderContext, + ref pAlbedoTexture, + ref pColorTexture, + ref pEmissionTexture, + ref pMotionVectorTexture, + ref pPositionNormalFwidthTexture, + ref pPrevLinearZAndNormalTexture + ); void computeFilteredMoments(RenderContext* pRenderContext); void computeAtrousDecomposition(RenderContext* pRenderContext, ref pAlbedoTexture); bool mBuffersNeedClear = false; // SVGF parameters - bool mFilterEnabled = true; - int32_t mFilterIterations = 4; - int32_t mFeedbackTap = 1; - float mVarainceEpsilon = 1e-4f; - float mPhiColor = 10.0f; - float mPhiNormal = 128.0f; - float mAlpha = 0.05f; - float mMomentsAlpha = 0.2f; + bool mFilterEnabled = true; + int32_t mFilterIterations = 4; + int32_t mFeedbackTap = 1; + float mVarainceEpsilon = 1e-4f; + float mPhiColor = 10.0f; + float mPhiNormal = 128.0f; + float mAlpha = 0.05f; + float mMomentsAlpha = 0.2f; // SVGF passes ref mpPackLinearZAndNormal; diff --git a/Source/RenderPasses/SVGFPass/SVGFReproject.ps.slang b/Source/RenderPasses/SVGFPass/SVGFReproject.ps.slang index 57df764f8..2b7204d27 100644 --- a/Source/RenderPasses/SVGFPass/SVGFReproject.ps.slang +++ b/Source/RenderPasses/SVGFPass/SVGFReproject.ps.slang @@ -38,19 +38,19 @@ bool isNaN(float f) cbuffer PerImageCB { - Texture2D gMotion; - Texture2D gPositionNormalFwidth; - Texture2D gColor; - Texture2D gAlbedo; - Texture2D gEmission; - Texture2D gPrevIllum; - Texture2D gPrevMoments; - Texture2D gLinearZAndNormal; - Texture2D gPrevLinearZAndNormal; - Texture2D gPrevHistoryLength; - - float gAlpha; - float gMomentsAlpha; + Texture2D gMotion; + Texture2D gPositionNormalFwidth; + Texture2D gColor; + Texture2D gAlbedo; + Texture2D gEmission; + Texture2D gPrevIllum; + Texture2D gPrevMoments; + Texture2D gLinearZAndNormal; + Texture2D gPrevLinearZAndNormal; + Texture2D gPrevHistoryLength; + + float gAlpha; + float gMomentsAlpha; }; float3 demodulate(float3 c, float3 albedo) @@ -63,13 +63,16 @@ bool isReprjValid(int2 coord, float Z, float Zprev, float fwidthZ, float3 normal const int2 imageDim = getTextureDims(gColor, 0); // check whether reprojected pixel is inside of the screen - if (any(coord < int2(1,1)) || any(coord > imageDim - int2(1,1))) return false; + if (any(coord < int2(1, 1)) || any(coord > imageDim - int2(1, 1))) + return false; // check if deviation of depths is acceptable - if (abs(Zprev - Z) / (fwidthZ + 1e-2f) > 10.f) return false; + if (abs(Zprev - Z) / (fwidthZ + 1e-2f) > 10.f) + return false; // check normals for compatibility - if (distance(normal, normalPrev) / (fwidthNormal + 1e-2) > 16.0) return false; + if (distance(normal, normalPrev) / (fwidthNormal + 1e-2) > 16.0) + return false; return true; } @@ -83,13 +86,13 @@ bool loadPrevData(float2 posH, out float4 prevIllum, out float2 prevMoments, out const float normalFwidth = gPositionNormalFwidth[ipos].y; // +0.5 to account for texel center offset - const int2 iposPrev = int2(float2(ipos) + motion.xy * imageDim + float2(0.5,0.5)); + const int2 iposPrev = int2(float2(ipos) + motion.xy * imageDim + float2(0.5, 0.5)); float2 depth = gLinearZAndNormal[ipos].xy; float3 normal = oct_to_ndir_snorm(gLinearZAndNormal[ipos].zw); - prevIllum = float4(0,0,0,0); - prevMoments = float2(0,0); + prevIllum = float4(0, 0, 0, 0); + prevMoments = float2(0, 0); bool v[4]; const float2 posPrev = floor(posH.xy) + motion.xy * imageDim; @@ -115,10 +118,7 @@ bool loadPrevData(float2 posH, out float4 prevIllum, out float2 prevMoments, out float y = frac(posPrev.y); // bilinear weights - const float w[4] = { (1 - x) * (1 - y), - x * (1 - y), - (1 - x) * y, - x * y }; + const float w[4] = { (1 - x) * (1 - y), x * (1 - y), (1 - x) * y, x * y }; // perform the actual bilinear interpolation for (int sampleIdx = 0; sampleIdx < 4; sampleIdx++) @@ -126,15 +126,15 @@ bool loadPrevData(float2 posH, out float4 prevIllum, out float2 prevMoments, out const int2 loc = int2(posPrev) + offset[sampleIdx]; if (v[sampleIdx]) { - prevIllum += w[sampleIdx] * gPrevIllum[loc]; + prevIllum += w[sampleIdx] * gPrevIllum[loc]; prevMoments += w[sampleIdx] * gPrevMoments[loc].xy; - sumw += w[sampleIdx]; - } + sumw += w[sampleIdx]; + } } // redistribute weights in case not all taps were used valid = (sumw >= 0.01); - prevIllum = valid ? prevIllum / sumw : float4(0, 0, 0, 0); + prevIllum = valid ? prevIllum / sumw : float4(0, 0, 0, 0); prevMoments = valid ? prevMoments / sumw : float2(0, 0); } @@ -163,7 +163,7 @@ bool loadPrevData(float2 posH, out float4 prevIllum, out float2 prevMoments, out if (nValid > 0) { valid = true; - prevIllum /= nValid; + prevIllum /= nValid; prevMoments /= nValid; } } @@ -175,8 +175,8 @@ bool loadPrevData(float2 posH, out float4 prevIllum, out float2 prevMoments, out } else { - prevIllum = float4(0,0,0,0); - prevMoments = float2(0,0); + prevIllum = float4(0, 0, 0, 0); + prevMoments = float2(0, 0); historyLength = 0; } @@ -192,9 +192,9 @@ float computeVarianceScale(float numSamples, float loopLength, float alpha) struct PS_OUT { - float4 OutIllumination : SV_TARGET0; - float2 OutMoments : SV_TARGET1; - float OutHistoryLength : SV_TARGET2; + float4 OutIllumination : SV_TARGET0; + float2 OutMoments : SV_TARGET1; + float OutHistoryLength : SV_TARGET2; }; PS_OUT main(FullScreenPassVsOut vsOut) @@ -218,7 +218,7 @@ PS_OUT main(FullScreenPassVsOut vsOut) // this adjusts the alpha for the case where insufficient history is available. // It boosts the temporal accumulation to give the samples equal weights in // the beginning. - const float alpha = success ? max(gAlpha, 1.0 / historyLength) : 1.0; + const float alpha = success ? max(gAlpha, 1.0 / historyLength) : 1.0; const float alphaMoments = success ? max(gMomentsAlpha, 1.0 / historyLength) : 1.0; // compute first two moments of luminance @@ -233,11 +233,11 @@ PS_OUT main(FullScreenPassVsOut vsOut) float variance = max(0.f, moments.g - moments.r * moments.r); - //variance *= computeVarianceScale(16, 16, alpha); + // variance *= computeVarianceScale(16, 16, alpha); PS_OUT psOut; // temporal integration of illumination - psOut.OutIllumination = lerp(prevIllumination, float4(illumination, 0), alpha); + psOut.OutIllumination = lerp(prevIllumination, float4(illumination, 0), alpha); // variance is propagated through the alpha channel psOut.OutIllumination.a = variance; psOut.OutMoments = moments; diff --git a/Source/RenderPasses/SceneDebugger/SceneDebugger.cpp b/Source/RenderPasses/SceneDebugger/SceneDebugger.cpp index 2ba1cfd55..322959450 100644 --- a/Source/RenderPasses/SceneDebugger/SceneDebugger.cpp +++ b/Source/RenderPasses/SceneDebugger/SceneDebugger.cpp @@ -29,73 +29,73 @@ namespace { - const char kShaderFile[] = "RenderPasses/SceneDebugger/SceneDebugger.cs.slang"; - const char kShaderModel[] = "6_5"; +const char kShaderFile[] = "RenderPasses/SceneDebugger/SceneDebugger.cs.slang"; - const std::string kOutput = "output"; +const std::string kOutput = "output"; - std::string getModeDesc(SceneDebuggerMode mode) +std::string getModeDesc(SceneDebuggerMode mode) +{ + switch (mode) { - switch (mode) - { - // Geometry - case SceneDebuggerMode::HitType: return - "Hit type in pseudocolor"; - case SceneDebuggerMode::InstanceID: return - "Instance ID in pseudocolor"; - case SceneDebuggerMode::MaterialID: return - "Material ID in pseudocolor"; - case SceneDebuggerMode::PrimitiveID: return - "Primitive ID in pseudocolor"; - case SceneDebuggerMode::GeometryID: return - "Geometry ID in pseudocolor"; - case SceneDebuggerMode::BlasID: return - "Raytracing bottom-level acceleration structure (BLAS) ID in pseudocolor"; - case SceneDebuggerMode::InstancedGeometry: return - "Green = instanced geometry\n" - "Red = non-instanced geometry"; - // Shading data - case SceneDebuggerMode::FaceNormal: return - "Face normal in RGB color"; - case SceneDebuggerMode::ShadingNormal: return - "Shading normal in RGB color"; - case SceneDebuggerMode::ShadingTangent: return - "Shading tangent in RGB color"; - case SceneDebuggerMode::ShadingBitangent: return - "Shading bitangent in RGB color"; - case SceneDebuggerMode::FrontFacingFlag: return - "Green = front-facing\n" - "Red = back-facing"; - case SceneDebuggerMode::BackfacingShadingNormal: return - "Pixels where the shading normal is back-facing with respect to view vector are highlighted"; - case SceneDebuggerMode::TexCoords: return - "Texture coordinates in RG color wrapped to [0,1]"; - // Material properties - case SceneDebuggerMode::GuideNormal: return - "Guide normal in RGB color"; - case SceneDebuggerMode::Roughness: return - "Material roughness estimate"; - case SceneDebuggerMode::FlatShaded: return - "Flat shaded"; - default: - FALCOR_UNREACHABLE(); - return ""; - } + // Geometry + case SceneDebuggerMode::HitType: + return "Hit type in pseudocolor"; + case SceneDebuggerMode::InstanceID: + return "Instance ID in pseudocolor"; + case SceneDebuggerMode::MaterialID: + return "Material ID in pseudocolor"; + case SceneDebuggerMode::PrimitiveID: + return "Primitive ID in pseudocolor"; + case SceneDebuggerMode::GeometryID: + return "Geometry ID in pseudocolor"; + case SceneDebuggerMode::BlasID: + return "Raytracing bottom-level acceleration structure (BLAS) ID in pseudocolor"; + case SceneDebuggerMode::InstancedGeometry: + return "Green = instanced geometry\n" + "Red = non-instanced geometry"; + // Shading data + case SceneDebuggerMode::FaceNormal: + return "Face normal in RGB color"; + case SceneDebuggerMode::ShadingNormal: + return "Shading normal in RGB color"; + case SceneDebuggerMode::ShadingTangent: + return "Shading tangent in RGB color"; + case SceneDebuggerMode::ShadingBitangent: + return "Shading bitangent in RGB color"; + case SceneDebuggerMode::FrontFacingFlag: + return "Green = front-facing\n" + "Red = back-facing"; + case SceneDebuggerMode::BackfacingShadingNormal: + return "Pixels where the shading normal is back-facing with respect to view vector are highlighted"; + case SceneDebuggerMode::TexCoords: + return "Texture coordinates in RG color wrapped to [0,1]"; + // Material properties + case SceneDebuggerMode::GuideNormal: + return "Guide normal in RGB color"; + case SceneDebuggerMode::Roughness: + return "Material roughness estimate"; + case SceneDebuggerMode::FlatShaded: + return "Flat shaded"; + default: + FALCOR_UNREACHABLE(); + return ""; } +} - // Scripting - const char kMode[] = "mode"; - const char kShowVolumes[] = "showVolumes"; +// Scripting +const char kMode[] = "mode"; +const char kShowVolumes[] = "showVolumes"; - void registerBindings(pybind11::module& m) - { - pybind11::class_> pass(m, "SceneDebugger"); - pass.def_property(kMode, - [](const SceneDebugger& self) { return enumToString(self.getMode()); }, - [](SceneDebugger& self, const std::string& value) {self.setMode(stringToEnum(value)); } - ); - } +void registerBindings(pybind11::module& m) +{ + pybind11::class_> pass(m, "SceneDebugger"); + pass.def_property( + kMode, + [](const SceneDebugger& self) { return enumToString(self.getMode()); }, + [](SceneDebugger& self, const std::string& value) { self.setMode(stringToEnum(value)); } + ); } +} // namespace extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { @@ -103,23 +103,25 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr Falcor::ScriptBindings::registerBinding(registerBindings); } -SceneDebugger::SceneDebugger(ref pDevice, const Properties& props) - : RenderPass(pDevice) +SceneDebugger::SceneDebugger(ref pDevice, const Properties& props) : RenderPass(pDevice) { + if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_5)) + FALCOR_THROW("SceneDebugger requires Shader Model 6.5 support."); if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) - { - throw RuntimeError("SceneDebugger: Raytracing Tier 1.1 is not supported by the current device"); - } + FALCOR_THROW("SceneDebugger requires Raytracing Tier 1.1 support."); // Parse dictionary. for (const auto& [key, value] : props) { - if (key == kMode) mParams.mode = (uint32_t)value.operator SceneDebuggerMode(); - else if (key == kShowVolumes) mParams.showVolumes = value; - else logWarning("Unknown property '{}' in a SceneDebugger properties.", key); + if (key == kMode) + mParams.mode = (uint32_t)value.operator SceneDebuggerMode(); + else if (key == kShowVolumes) + mParams.showVolumes = value; + else + logWarning("Unknown property '{}' in a SceneDebugger properties.", key); } - mpFence = GpuFence::create(mpDevice); + mpFence = mpDevice->createFence(); } Properties SceneDebugger::getProperties() const @@ -152,18 +154,24 @@ void SceneDebugger::setScene(RenderContext* pRenderContext, const ref& pS if (mpScene) { // Prepare our programs for the scene. - Program::Desc desc; + ProgramDesc desc; desc.addShaderModules(mpScene->getShaderModules()); desc.addShaderLibrary(kShaderFile).csEntry("main"); desc.addTypeConformances(mpScene->getTypeConformances()); - desc.setShaderModel(kShaderModel); mpDebugPass = ComputePass::create(mpDevice, desc, mpScene->getSceneDefines()); // Create lookup table for mesh to BLAS ID. auto blasIDs = mpScene->getMeshBlasIDs(); if (!blasIDs.empty()) { - mpMeshToBlasID = Buffer::createStructured(mpDevice, sizeof(uint32_t), (uint32_t)blasIDs.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, blasIDs.data(), false); + mpMeshToBlasID = mpDevice->createStructuredBuffer( + sizeof(uint32_t), + (uint32_t)blasIDs.size(), + ResourceBindFlags::ShaderResource, + MemoryType::DeviceLocal, + blasIDs.data(), + false + ); } // Create instance metadata. @@ -173,8 +181,16 @@ void SceneDebugger::setScene(RenderContext* pRenderContext, const ref& pS auto var = mpDebugPass->getRootVar()["CB"]["gSceneDebugger"]; if (!mpPixelData) { - mpPixelData = Buffer::createStructured(mpDevice, var["pixelData"], 1, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); - mpPixelDataStaging = Buffer::createStructured(mpDevice, var["pixelData"], 1, ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr, false); + mpPixelData = mpDevice->createStructuredBuffer( + var["pixelData"], + 1, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType::DeviceLocal, + nullptr, + false + ); + mpPixelDataStaging = + mpDevice->createStructuredBuffer(var["pixelData"], 1, ResourceBindFlags::None, MemoryType::ReadBack, nullptr, false); } var["pixelData"] = mpPixelData; var["meshToBlasID"] = mpMeshToBlasID; @@ -193,7 +209,13 @@ void SceneDebugger::execute(RenderContext* pRenderContext, const RenderData& ren return; } // DEMO21: - //mpScene->getCamera()->setJitter(0.f, 0.f); + // mpScene->getCamera()->setJitter(0.f, 0.f); + + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded) || + is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) + { + FALCOR_THROW("This render pass does not support scene changes that require shader recompilation."); + } mpScene->setRaytracingShaderData(pRenderContext, mpDebugPass->getRootVar()); @@ -204,8 +226,8 @@ void SceneDebugger::execute(RenderContext* pRenderContext, const RenderData& ren mpDebugPass->execute(pRenderContext, uint3(mParams.frameDim, 1)); pRenderContext->copyResource(mpPixelDataStaging.get(), mpPixelData.get()); - pRenderContext->flush(false); - mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); + pRenderContext->submit(false); + pRenderContext->signal(mpFence.get()); mPixelDataAvailable = true; mParams.frameCount++; @@ -213,7 +235,7 @@ void SceneDebugger::execute(RenderContext* pRenderContext, const RenderData& ren void SceneDebugger::renderUI(Gui::Widgets& widget) { - widget.dropdown("Mode", reinterpret_cast(mParams.mode)); + widget.dropdown("Mode", reinterpret_cast(mParams.mode)); widget.tooltip("Selects visualization mode"); widget.checkbox("Clamp to [0,1]", mParams.clamp); @@ -240,26 +262,28 @@ void SceneDebugger::renderUI(Gui::Widgets& widget) widget.textWrapped("Description:\n" + getModeDesc((SceneDebuggerMode)mParams.mode)); // Show data for the currently selected pixel. - widget.dummy("#spacer0", { 1, 20 }); + widget.dummy("#spacer0", {1, 20}); widget.var("Selected pixel", mParams.selectedPixel); renderPixelDataUI(widget); - widget.dummy("#spacer1", { 1, 20 }); + widget.dummy("#spacer1", {1, 20}); widget.text("Scene: " + (mpScene ? mpScene->getPath().string() : "No scene loaded")); } void SceneDebugger::renderPixelDataUI(Gui::Widgets& widget) { - if (!mPixelDataAvailable) return; + if (!mPixelDataAvailable) + return; FALCOR_ASSERT(mpPixelDataStaging); - mpFence->syncCpu(); - const PixelData& data = *reinterpret_cast(mpPixelDataStaging->map(Buffer::MapType::Read)); + mpFence->wait(); + const PixelData data = mpPixelDataStaging->getElement(0); switch ((HitType)data.hitType) { case HitType::Triangle: + { { std::string text; text += fmt::format("Mesh ID: {}\n", data.geometryID); @@ -268,14 +292,14 @@ void SceneDebugger::renderPixelDataUI(Gui::Widgets& widget) text += fmt::format("Material ID: {}\n", data.materialID); text += fmt::format("BLAS ID: {}\n", data.blasID); widget.text(text); - widget.dummy("#spacer2", { 1, 10 }); + widget.dummy("#spacer2", {1, 10}); } // Show mesh details. if (auto g = widget.group("Mesh info"); g.open()) { FALCOR_ASSERT(data.geometryID < mpScene->getMeshCount()); - const auto& mesh = mpScene->getMesh(MeshID{ data.geometryID }); + const auto& mesh = mpScene->getMesh(MeshID{data.geometryID}); std::string text; text += fmt::format("flags: 0x{:08x}\n", mesh.flags); text += fmt::format("materialID: {}\n", mesh.materialID); @@ -309,7 +333,7 @@ void SceneDebugger::renderPixelDataUI(Gui::Widgets& widget) // Print the list of scene graph nodes affecting this mesh instance. std::vector nodes; { - NodeID nodeID{ instance.globalMatrixID }; + NodeID nodeID{instance.globalMatrixID}; while (nodeID != NodeID::Invalid()) { nodes.push_back(nodeID); @@ -331,7 +355,9 @@ void SceneDebugger::renderPixelDataUI(Gui::Widgets& widget) } } break; + } case HitType::Curve: + { { std::string text; text += fmt::format("Curve ID: {}\n", data.geometryID); @@ -339,13 +365,13 @@ void SceneDebugger::renderPixelDataUI(Gui::Widgets& widget) text += fmt::format("Material ID: {}\n", data.materialID); text += fmt::format("BLAS ID: {}\n", data.blasID); widget.text(text); - widget.dummy("#spacer2", { 1, 10 }); + widget.dummy("#spacer2", {1, 10}); } // Show mesh details. if (auto g = widget.group("Curve info"); g.open()) { - const auto& curve = mpScene->getCurve(CurveID{ data.geometryID }); + const auto& curve = mpScene->getCurve(CurveID{data.geometryID}); std::string text; text += fmt::format("degree: {}\n", curve.degree); text += fmt::format("vertexCount: {}\n", curve.vertexCount); @@ -355,7 +381,9 @@ void SceneDebugger::renderPixelDataUI(Gui::Widgets& widget) g.text(text); } break; + } case HitType::SDFGrid: + { { std::string text; text += fmt::format("SDF Grid ID: {}\n", data.geometryID); @@ -363,18 +391,19 @@ void SceneDebugger::renderPixelDataUI(Gui::Widgets& widget) text += fmt::format("Material ID: {}\n", data.materialID); text += fmt::format("BLAS ID: {}\n", data.blasID); widget.text(text); - widget.dummy("#spacer2", { 1, 10 }); + widget.dummy("#spacer2", {1, 10}); } // Show SDF grid details. if (auto g = widget.group("SDF grid info"); g.open()) { - const ref& pSDFGrid = mpScene->getSDFGrid(SdfGridID{ data.geometryID }); + const ref& pSDFGrid = mpScene->getSDFGrid(SdfGridID{data.geometryID}); std::string text; text += fmt::format("gridWidth: {}\n", pSDFGrid->getGridWidth()); g.text(text); } break; + } case HitType::None: widget.text("Background pixel"); break; @@ -408,7 +437,7 @@ void SceneDebugger::renderPixelDataUI(Gui::Widgets& widget) { if (auto g = widget.group("Material info"); g.open()) { - const auto& material = *mpScene->getMaterial(MaterialID{ data.materialID }); + const auto& material = *mpScene->getMaterial(MaterialID{data.materialID}); const auto& header = material.getHeader(); std::string text; text += fmt::format("name: {}\n", material.getName()); @@ -427,8 +456,6 @@ void SceneDebugger::renderPixelDataUI(Gui::Widgets& widget) g.text(text); } } - - mpPixelDataStaging->unmap(); } bool SceneDebugger::onMouseEvent(const MouseEvent& mouseEvent) @@ -455,7 +482,8 @@ void SceneDebugger::initInstanceInfo() // Count number of times each geometry is used. std::vector> instanceCounts((size_t)GeometryType::Count); - for (auto& counts : instanceCounts) counts.resize(mpScene->getGeometryCount()); + for (auto& counts : instanceCounts) + counts.resize(mpScene->getGeometryCount()); for (uint32_t instanceID = 0; instanceID < instanceCount; instanceID++) { @@ -476,5 +504,12 @@ void SceneDebugger::initInstanceInfo() } // Create GPU buffer. - mpInstanceInfo = Buffer::createStructured(mpDevice, sizeof(InstanceInfo), (uint32_t)instanceInfo.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, instanceInfo.data(), false); + mpInstanceInfo = mpDevice->createStructuredBuffer( + sizeof(InstanceInfo), + (uint32_t)instanceInfo.size(), + ResourceBindFlags::ShaderResource, + MemoryType::DeviceLocal, + instanceInfo.data(), + false + ); } diff --git a/Source/RenderPasses/SceneDebugger/SceneDebugger.cs.slang b/Source/RenderPasses/SceneDebugger/SceneDebugger.cs.slang index a85fa7c33..6d0507158 100644 --- a/Source/RenderPasses/SceneDebugger/SceneDebugger.cs.slang +++ b/Source/RenderPasses/SceneDebugger/SceneDebugger.cs.slang @@ -43,11 +43,13 @@ struct SceneDebugger RWTexture2D output; RWStructuredBuffer pixelData; - /** Run scene debugger for the given pixel. - */ + /** + * Run scene debugger for the given pixel. + */ void execute(const uint2 pixel) { - if (any(pixel >= params.frameDim)) return; + if (any(pixel >= params.frameDim)) + return; // Initialize pixel data for the selected pixel. if (all(pixel == params.selectedPixel)) @@ -86,7 +88,8 @@ struct SceneDebugger } // Clamp pixel values if necessary. - if (params.clamp) color = saturate(color); + if (params.clamp) + color = saturate(color); // Write output. output[pixel] = float4(color, 1.f); @@ -94,15 +97,14 @@ struct SceneDebugger float3 remapVector(float3 v) { - if (params.flipSign) v = -v; - if (params.remapRange) v = 0.5f * v + 0.5f; + if (params.flipSign) + v = -v; + if (params.remapRange) + v = 0.5f * v + 0.5f; return v; } - float2 remapVector(float2 v) - { - return remapVector(float3(v, 0)).xy; - } + float2 remapVector(float2 v) { return remapVector(float3(v, 0)).xy; } float3 pseudocolor(uint value) { @@ -303,7 +305,8 @@ struct SceneDebugger float evalGridVolumeTransmittance(GridVolume gridVolume, const float3 pos, const float3 dir, const float minT, const float maxT) { - if (!gridVolume.hasDensityGrid()) return 1.f; + if (!gridVolume.hasDensityGrid()) + return 1.f; // Intersect with volume bounds and get intersection interval along the view ray. AABB bounds = gridVolume.getBounds(); @@ -311,7 +314,8 @@ struct SceneDebugger bool hit = intersectRayAABB(pos, dir, bounds.minPoint, bounds.maxPoint, nearFar); nearFar.x = max(nearFar.x, minT); nearFar.y = min(nearFar.y, maxT); - if (nearFar.x >= nearFar.y) return 1.f; + if (nearFar.x >= nearFar.y) + return 1.f; // Setup access to density grid. Grid densityGrid; @@ -341,10 +345,11 @@ cbuffer CB SceneDebugger gSceneDebugger; } -/** Compute shader entry point for scene debugger. -*/ +/** + * Compute shader entry point for scene debugger. + */ [numthreads(16, 16, 1)] -void main(uint3 dispatchThreadId : SV_DispatchThreadID) +void main(uint3 dispatchThreadId: SV_DispatchThreadID) { gSceneDebugger.execute(dispatchThreadId.xy); } diff --git a/Source/RenderPasses/SceneDebugger/SceneDebugger.h b/Source/RenderPasses/SceneDebugger/SceneDebugger.h index 7e680536d..d51bf21f9 100644 --- a/Source/RenderPasses/SceneDebugger/SceneDebugger.h +++ b/Source/RenderPasses/SceneDebugger/SceneDebugger.h @@ -33,10 +33,11 @@ using namespace Falcor; -/** Scene debugger render pass. - - This pass helps identify asset issues such as incorrect normals. -*/ +/** + * Scene debugger render pass. + * + * This pass helps identify asset issues such as incorrect normals. + */ class SceneDebugger : public RenderPass { public: @@ -64,13 +65,16 @@ class SceneDebugger : public RenderPass void initInstanceInfo(); // Internal state - ref mpScene; - SceneDebuggerParams mParams; - ref mpDebugPass; - ref mpFence; - ref mpPixelData; ///< Buffer for recording pixel data at the selected pixel. - ref mpPixelDataStaging; ///< Readback buffer. - ref mpMeshToBlasID; - ref mpInstanceInfo; - bool mPixelDataAvailable = false; + + ref mpScene; + SceneDebuggerParams mParams; + ref mpDebugPass; + ref mpFence; + /// Buffer for recording pixel data at the selected pixel. + ref mpPixelData; + /// Readback buffer. + ref mpPixelDataStaging; + ref mpMeshToBlasID; + ref mpInstanceInfo; + bool mPixelDataAvailable = false; }; diff --git a/Source/RenderPasses/SceneDebugger/SharedTypes.slang b/Source/RenderPasses/SceneDebugger/SharedTypes.slang index a25ac4518..42318edb6 100644 --- a/Source/RenderPasses/SceneDebugger/SharedTypes.slang +++ b/Source/RenderPasses/SceneDebugger/SharedTypes.slang @@ -54,28 +54,31 @@ enum class SceneDebuggerMode : uint32_t FlatShaded, }; -FALCOR_ENUM_INFO(SceneDebuggerMode, { - // Geometry - { SceneDebuggerMode::HitType, "HitType" }, - { SceneDebuggerMode::InstanceID, "InstanceID" }, - { SceneDebuggerMode::MaterialID, "MaterialID" }, - { SceneDebuggerMode::GeometryID, "GeometryID" }, - { SceneDebuggerMode::BlasID, "BlasID" }, - { SceneDebuggerMode::InstancedGeometry, "InstancedGeometry" }, - { SceneDebuggerMode::PrimitiveID, "PrimitiveID" }, - // Shading data - { SceneDebuggerMode::FaceNormal, "FaceNormal" }, - { SceneDebuggerMode::ShadingNormal, "ShadingNormal" }, - { SceneDebuggerMode::ShadingTangent, "ShadingTangent" }, - { SceneDebuggerMode::ShadingBitangent, "ShadingBitangent" }, - { SceneDebuggerMode::FrontFacingFlag, "FrontFacingFlag" }, - { SceneDebuggerMode::BackfacingShadingNormal, "BackfacingShadingNormal" }, - { SceneDebuggerMode::TexCoords, "TexCoords" }, - // Material properties - { SceneDebuggerMode::GuideNormal, "GuideNormal" }, - { SceneDebuggerMode::Roughness, "Roughness" }, - { SceneDebuggerMode::FlatShaded, "FlatShaded" }, -}); +FALCOR_ENUM_INFO( + SceneDebuggerMode, + { + // Geometry + { SceneDebuggerMode::HitType, "HitType" }, + { SceneDebuggerMode::InstanceID, "InstanceID" }, + { SceneDebuggerMode::MaterialID, "MaterialID" }, + { SceneDebuggerMode::GeometryID, "GeometryID" }, + { SceneDebuggerMode::BlasID, "BlasID" }, + { SceneDebuggerMode::InstancedGeometry, "InstancedGeometry" }, + { SceneDebuggerMode::PrimitiveID, "PrimitiveID" }, + // Shading data + { SceneDebuggerMode::FaceNormal, "FaceNormal" }, + { SceneDebuggerMode::ShadingNormal, "ShadingNormal" }, + { SceneDebuggerMode::ShadingTangent, "ShadingTangent" }, + { SceneDebuggerMode::ShadingBitangent, "ShadingBitangent" }, + { SceneDebuggerMode::FrontFacingFlag, "FrontFacingFlag" }, + { SceneDebuggerMode::BackfacingShadingNormal, "BackfacingShadingNormal" }, + { SceneDebuggerMode::TexCoords, "TexCoords" }, + // Material properties + { SceneDebuggerMode::GuideNormal, "GuideNormal" }, + { SceneDebuggerMode::Roughness, "Roughness" }, + { SceneDebuggerMode::FlatShaded, "FlatShaded" }, + } +); FALCOR_ENUM_REGISTER(SceneDebuggerMode); struct SceneDebuggerParams @@ -89,8 +92,8 @@ struct SceneDebuggerParams int remapRange = true; ///< Remap valid range to [0,1] before output. int clamp = true; ///< Clamp pixel values to [0,1] before output. - int showVolumes = true; ///< Show volumes. - float densityScale = 1.f; ///< Volume density scale factor. + int showVolumes = true; ///< Show volumes. + float densityScale = 1.f; ///< Volume density scale factor. }; struct PixelData @@ -124,7 +127,7 @@ enum class InstanceInfoFlags : uint32_t struct InstanceInfo { - uint flags = 0; ///< Flags as a combination of 'InstanceInfoFlags' flags. + uint flags = 0; ///< Flags as a combination of 'InstanceInfoFlags' flags. }; END_NAMESPACE_FALCOR diff --git a/Source/RenderPasses/SimplePostFX/SimplePostFX.cpp b/Source/RenderPasses/SimplePostFX/SimplePostFX.cpp index f31680b5b..7113ddd79 100644 --- a/Source/RenderPasses/SimplePostFX/SimplePostFX.cpp +++ b/Source/RenderPasses/SimplePostFX/SimplePostFX.cpp @@ -29,30 +29,30 @@ namespace { - const char kSrc[] = "src"; - const char kDst[] = "dst"; +const char kSrc[] = "src"; +const char kDst[] = "dst"; - // Scripting options. - const char kEnabled[] = "enabled"; - const char kOutputSize[] = "outputSize"; - const char kFixedOutputSize[] = "fixedOutputSize"; - const char kWipe[] = "wipe"; - const char kBloomAmount[] = "bloomAmount"; - const char kStarAmount[] = "starAmount"; - const char kStarAngle[] = "starAngle"; - const char kVignetteAmount[] = "vignetteAmount"; - const char kChromaticAberrationAmount[] = "chromaticAberrationAmount"; - const char kBarrelDistortAmount[] = "barrelDistortAmount"; - const char kSaturationCurve[] = "saturationCurve"; - const char kColorOffset[] = "colorOffset"; - const char kColorScale[] = "colorScale"; - const char kColorPower[] = "colorPower"; - const char kColorOffsetScalar[] = "colorOffsetScalar"; - const char kColorScaleScalar[] = "colorScaleScalar"; - const char kColorPowerScalar[] = "colorPowerScalar"; +// Scripting options. +const char kEnabled[] = "enabled"; +const char kOutputSize[] = "outputSize"; +const char kFixedOutputSize[] = "fixedOutputSize"; +const char kWipe[] = "wipe"; +const char kBloomAmount[] = "bloomAmount"; +const char kStarAmount[] = "starAmount"; +const char kStarAngle[] = "starAngle"; +const char kVignetteAmount[] = "vignetteAmount"; +const char kChromaticAberrationAmount[] = "chromaticAberrationAmount"; +const char kBarrelDistortAmount[] = "barrelDistortAmount"; +const char kSaturationCurve[] = "saturationCurve"; +const char kColorOffset[] = "colorOffset"; +const char kColorScale[] = "colorScale"; +const char kColorPower[] = "colorPower"; +const char kColorOffsetScalar[] = "colorOffsetScalar"; +const char kColorScaleScalar[] = "colorScaleScalar"; +const char kColorPowerScalar[] = "colorPowerScalar"; - const char kShaderFile[] = "RenderPasses/SimplePostFX/SimplePostFX.cs.slang"; -} +const char kShaderFile[] = "RenderPasses/SimplePostFX/SimplePostFX.cs.slang"; +} // namespace static void regSimplePostFX(pybind11::module& m) { @@ -80,36 +80,53 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regSimplePostFX); } -SimplePostFX::SimplePostFX(ref pDevice, const Properties& props) - : RenderPass(pDevice) +SimplePostFX::SimplePostFX(ref pDevice, const Properties& props) : RenderPass(pDevice) { // Deserialize pass from dictionary. for (const auto& [key, value] : props) { - if (key == kEnabled) setEnabled(value); - else if (key == kOutputSize) mOutputSizeSelection = value; - else if (key == kFixedOutputSize) mFixedOutputSize = value; - else if (key == kWipe) setWipe(value); - else if (key == kBloomAmount) setBloomAmount(value); - else if (key == kStarAmount) setStarAmount(value); - else if (key == kStarAngle) setStarAngle(value); - else if (key == kVignetteAmount) setVignetteAmount(value); - else if (key == kChromaticAberrationAmount) setChromaticAberrationAmount(value); - else if (key == kBarrelDistortAmount) setBarrelDistortAmount(value); - else if (key == kSaturationCurve) setSaturationCurve(value); - else if (key == kColorOffset) setColorOffset(value); - else if (key == kColorScale) setColorScale(value); - else if (key == kColorPower) setColorPower(value); - else if (key == kColorOffsetScalar) setColorOffsetScalar(value); - else if (key == kColorScaleScalar) setColorScaleScalar(value); - else if (key == kColorPowerScalar) setColorPowerScalar(value); - else logWarning("Unknown property '{}' in SimplePostFX properties.", key); + if (key == kEnabled) + setEnabled(value); + else if (key == kOutputSize) + mOutputSizeSelection = value; + else if (key == kFixedOutputSize) + mFixedOutputSize = value; + else if (key == kWipe) + setWipe(value); + else if (key == kBloomAmount) + setBloomAmount(value); + else if (key == kStarAmount) + setStarAmount(value); + else if (key == kStarAngle) + setStarAngle(value); + else if (key == kVignetteAmount) + setVignetteAmount(value); + else if (key == kChromaticAberrationAmount) + setChromaticAberrationAmount(value); + else if (key == kBarrelDistortAmount) + setBarrelDistortAmount(value); + else if (key == kSaturationCurve) + setSaturationCurve(value); + else if (key == kColorOffset) + setColorOffset(value); + else if (key == kColorScale) + setColorScale(value); + else if (key == kColorPower) + setColorPower(value); + else if (key == kColorOffsetScalar) + setColorOffsetScalar(value); + else if (key == kColorScaleScalar) + setColorScaleScalar(value); + else if (key == kColorPowerScalar) + setColorPowerScalar(value); + else + logWarning("Unknown property '{}' in SimplePostFX properties.", key); } Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); - samplerDesc.setAddressingMode(Sampler::AddressMode::Border, Sampler::AddressMode::Border, Sampler::AddressMode::Border); - mpLinearSampler = Sampler::create(mpDevice, samplerDesc); + samplerDesc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Point); + samplerDesc.setAddressingMode(TextureAddressingMode::Border, TextureAddressingMode::Border, TextureAddressingMode::Border); + mpLinearSampler = mpDevice->createSampler(samplerDesc); DefineList defines; mpDownsamplePass = ComputePass::create(mpDevice, kShaderFile, "downsample", defines); @@ -122,7 +139,8 @@ Properties SimplePostFX::getProperties() const Properties props; props[kEnabled] = getEnabled(); props[kOutputSize] = mOutputSizeSelection; - if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) props[kFixedOutputSize] = mFixedOutputSize; + if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) + props[kFixedOutputSize] = mFixedOutputSize; props[kWipe] = getWipe(); props[kBloomAmount] = getBloomAmount(); props[kStarAmount] = getStarAmount(); @@ -145,8 +163,12 @@ RenderPassReflection SimplePostFX::reflect(const CompileData& compileData) RenderPassReflection reflector; const uint2 sz = RenderPassHelpers::calculateIOSize(mOutputSizeSelection, mFixedOutputSize, compileData.defaultTexDims); - reflector.addInput(kSrc, "Source texture").bindFlags(ResourceBindFlags::ShaderResource);; - reflector.addOutput(kDst, "post-effected output texture").bindFlags(ResourceBindFlags::RenderTarget | ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess).format(ResourceFormat::RGBA32Float).texture2D(sz.x, sz.y); + reflector.addInput(kSrc, "Source texture").bindFlags(ResourceBindFlags::ShaderResource); + ; + reflector.addOutput(kDst, "post-effected output texture") + .bindFlags(ResourceBindFlags::RenderTarget | ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess) + .format(ResourceFormat::RGBA32Float) + .texture2D(sz.x, sz.y); return reflector; } @@ -165,6 +187,7 @@ void SimplePostFX::execute(RenderContext* pRenderContext, const RenderData& rend const uint2 resolution = uint2(pSrc->getWidth(), pSrc->getHeight()); // if we have 'identity' settings, we can just copy input to output + // clang-format off if (getEnabled() == false || getWipe() >= 1.f || ( getBloomAmount() == 0.f && getChromaticAberrationAmount() == 0.f && @@ -182,6 +205,7 @@ void SimplePostFX::execute(RenderContext* pRenderContext, const RenderData& rend pRenderContext->blit(pSrc->getSRV(), pDst->getRTV()); return; } + // clang-format on preparePostFX(pRenderContext, resolution.x, resolution.y); if (getBloomAmount() > 0.f) @@ -192,7 +216,7 @@ void SimplePostFX::execute(RenderContext* pRenderContext, const RenderData& rend var["gLinearSampler"] = mpLinearSampler; for (int level = 0; level < kNumLevels; ++level) { - uint2 res = { std::max(1u , resolution.x >> (level + 1)), std::max(1u , resolution.y >> (level + 1)) }; + uint2 res = {std::max(1u, resolution.x >> (level + 1)), std::max(1u, resolution.y >> (level + 1))}; float2 invres = float2(1.f / res.x, 1.f / res.y); var["PerFrameCB"]["gResolution"] = res; var["PerFrameCB"]["gInvRes"] = invres; @@ -210,13 +234,14 @@ void SimplePostFX::execute(RenderContext* pRenderContext, const RenderData& rend var["gSrc"] = pSrc; for (int level = kNumLevels - 1; level >= 0; --level) { - uint2 res = { std::max(1u , resolution.x >> level), std::max(1u , resolution.y >> level) }; + uint2 res = {std::max(1u, resolution.x >> level), std::max(1u, resolution.y >> level)}; float2 invres = float2(1.f / res.x, 1.f / res.y); var["PerFrameCB"]["gResolution"] = res; var["PerFrameCB"]["gInvRes"] = invres; bool wantStar = level == 1 || level == 2; var["PerFrameCB"]["gStar"] = (wantStar) ? getStarAmount() : 0.f; - if (wantStar) { + if (wantStar) + { float ang = getStarAngle(); var["PerFrameCB"]["gStarDir1"] = float2(std::sin(ang), std::cos(ang)) * invres * 2.f; ang += float(M_PI) / 3.f; @@ -226,7 +251,9 @@ void SimplePostFX::execute(RenderContext* pRenderContext, const RenderData& rend } var["gBloomed"] = mpPyramid[level + 1]; var["gDst"] = mpPyramid[level]; - var["PerFrameCB"]["gInPlace"] = level > 0; // for most levels, we update the pyramid in place. for the last step, we read from the original source since we did not compute it in the downsample passes. + // for most levels, we update the pyramid in place. for the last step, we read + // from the original source since we did not compute it in the downsample passes. + var["PerFrameCB"]["gInPlace"] = level > 0; mpUpsamplePass->execute(pRenderContext, uint3(res, 1)); } } @@ -239,7 +266,8 @@ void SimplePostFX::execute(RenderContext* pRenderContext, const RenderData& rend var["PerFrameCB"]["gVignetteAmount"] = getVignetteAmount(); var["PerFrameCB"]["gChromaticAberrationAmount"] = getChromaticAberrationAmount() * (1.f / 64.f); float barrel = getBarrelDistortAmount() * 0.125f; - var["PerFrameCB"]["gBarrelDistort"] = float2(1.f / (1.f + 4.f * barrel), barrel); // scale factor chosen to keep the corners of a 16:9 viewport fixed + // scale factor chosen to keep the corners of a 16:9 viewport fixed + var["PerFrameCB"]["gBarrelDistort"] = float2(1.f / (1.f + 4.f * barrel), barrel); float3 satcurve = getSaturationCurve(); // fit a quadratic thru the 3 points satcurve.y -= satcurve.x; @@ -275,7 +303,9 @@ void SimplePostFX::preparePostFX(RenderContext* pRenderContext, uint32_t width, uint32_t h = std::max(1u, height >> res); if (!pBuf || pBuf->getWidth() != w || pBuf->getHeight() != h) { - pBuf = Texture::create2D(mpDevice, w, h, ResourceFormat::RGBA16Float, 1, 1, nullptr, Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess); + pBuf = mpDevice->createTexture2D( + w, h, ResourceFormat::RGBA16Float, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess + ); FALCOR_ASSERT(pBuf); } } @@ -286,10 +316,12 @@ void SimplePostFX::renderUI(Gui::Widgets& widget) { // Controls for output size. // When output size requirements change, we'll trigger a graph recompile to update the render pass I/O sizes. - if (widget.dropdown("Output size", mOutputSizeSelection)) requestRecompile(); + if (widget.dropdown("Output size", mOutputSizeSelection)) + requestRecompile(); if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) { - if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) requestRecompile(); + if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) + requestRecompile(); } // PostFX options. @@ -303,7 +335,8 @@ void SimplePostFX::renderUI(Gui::Widgets& widget) group.slider("Vignette", mVignetteAmount, 0.f, 1.f); group.slider("Chromatic Aberration", mChromaticAberrationAmount, 0.f, 1.f); group.slider("Barrel Distortion", mBarrelDistortAmount, 0.f, 1.f); - if (group.button("reset this group")) { + if (group.button("reset this group")) + { mBloomAmount = 0.f; mStarAmount = 0.f; mStarAngle = 0.1f; @@ -317,7 +350,8 @@ void SimplePostFX::renderUI(Gui::Widgets& widget) group.slider("Shadow Saturation", mSaturationCurve.x, 0.f, 2.f); group.slider("Midtone Saturation", mSaturationCurve.y, 0.f, 2.f); group.slider("Hilight Saturation", mSaturationCurve.z, 0.f, 2.f); - if (group.button("reset this group")) { + if (group.button("reset this group")) + { mSaturationCurve = float3(1.f); } } @@ -326,7 +360,8 @@ void SimplePostFX::renderUI(Gui::Widgets& widget) group.slider("Luma Offset (Shadows)", mColorOffsetScalar, -1.f, 1.f); group.slider("Luma Power (Midtones)", mColorPowerScalar, -1.f, 1.f); group.slider("Luma Scale (Hilights)", mColorScaleScalar, -1.f, 1.f); - if (group.button("reset this group")) { + if (group.button("reset this group")) + { mColorOffsetScalar = 0.f; mColorPowerScalar = 0.f; mColorScaleScalar = 0.f; @@ -334,16 +369,20 @@ void SimplePostFX::renderUI(Gui::Widgets& widget) } if (auto group = widget.group("Offset/Power/Scale (color)", true)) { - if (group.button("reset##1")) mColorOffset = float3(0.5f, 0.5f, 0.5f); + if (group.button("reset##1")) + mColorOffset = float3(0.5f, 0.5f, 0.5f); group.rgbColor("Color Offset (Shadows)", mColorOffset, true); - if (group.button("reset##2")) mColorPower = float3(0.5f, 0.5f, 0.5f); + if (group.button("reset##2")) + mColorPower = float3(0.5f, 0.5f, 0.5f); group.rgbColor("Color Power (Midtones)", mColorPower, true); - if (group.button("reset##3")) mColorScale = float3(0.5f, 0.5f, 0.5f); + if (group.button("reset##3")) + mColorScale = float3(0.5f, 0.5f, 0.5f); group.rgbColor("Color Scale (Hilights)", mColorScale, true); - if (group.button("reset this group")) { + if (group.button("reset this group")) + { mColorOffset = float3(0.5f, 0.5f, 0.5f); mColorPower = float3(0.5f, 0.5f, 0.5f); mColorScale = float3(0.5f, 0.5f, 0.5f); diff --git a/Source/RenderPasses/SimplePostFX/SimplePostFX.cs.slang b/Source/RenderPasses/SimplePostFX/SimplePostFX.cs.slang index 7ebe07c3a..dff13290d 100644 --- a/Source/RenderPasses/SimplePostFX/SimplePostFX.cs.slang +++ b/Source/RenderPasses/SimplePostFX/SimplePostFX.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,39 +26,40 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Simple Post FX - - applies vignette, chromatic aberration, barrel distortion, brightness-dependant (de)saturation, offset/scale/power tint per pixel - - also applies bloom which is an energy preserving weighted sum of 'gaussians' plus an optional star shape. - the bloom is calculated by creating an image pyramid fine to coarse, using a 10x10 binomial kernel (implemented via 5x5 bilinear taps) - the downsampling kernel needs to be quite wide to avoid aliasing artefacts - after 8 downsampling steps (so that the final PSF is quite wide - at least 64*10 pixels diameter), we upsample coarse to fine, - at each level blending a small fraction (the 'bloom amount') of the coarser layer with the 'current' layer. - in this way, the final kernel is a blend of gaussians with doubling variance, plus a spike at the middle. - optionally, 6 star like lobes are added by brute force line sampling. +/** + * Simple Post FX + * + * applies vignette, chromatic aberration, barrel distortion, brightness-dependant (de)saturation, offset/scale/power tint per pixel + * + * also applies bloom which is an energy preserving weighted sum of 'gaussians' plus an optional star shape. + * the bloom is calculated by creating an image pyramid fine to coarse, using a 10x10 binomial kernel (implemented via 5x5 bilinear taps) + * the downsampling kernel needs to be quite wide to avoid aliasing artefacts + * after 8 downsampling steps (so that the final PSF is quite wide - at least 64*10 pixels diameter), we upsample coarse to fine, + * at each level blending a small fraction (the 'bloom amount') of the coarser layer with the 'current' layer. + * in this way, the final kernel is a blend of gaussians with doubling variance, plus a spike at the middle. + * optionally, 6 star like lobes are added by brute force line sampling. */ - #include "Utils/Color/ColorHelpers.slang" +#include "Utils/Color/ColorHelpers.slang" cbuffer PerFrameCB { - uint2 gResolution; - float2 gInvRes; - float gBloomAmount; - float gVignetteAmount; - float gChromaticAberrationAmount; - float2 gBarrelDistort; - float3 gSaturationCurve; - float3 gColorOffset; - float3 gColorScale; - float3 gColorPower; - float gStar; - float2 gStarDir1; - float2 gStarDir2; - float2 gStarDir3; - float gWipe; - bool gInPlace; + uint2 gResolution; + float2 gInvRes; + float gBloomAmount; + float gVignetteAmount; + float gChromaticAberrationAmount; + float2 gBarrelDistort; + float3 gSaturationCurve; + float3 gColorOffset; + float3 gColorScale; + float3 gColorPower; + float gStar; + float2 gStarDir1; + float2 gStarDir2; + float2 gStarDir3; + float gWipe; + bool gInPlace; } Texture2D gSrc; @@ -71,9 +72,13 @@ float4 blurFilter2x2(Texture2D src, const float2 uv, float uscale, float // 2x2 bilinear-tap kernel, effective footprint 4x4, with offsets chosen to sample from the binomial 1 3 3 1 uscale *= 0.75f; vscale *= 0.75f; + // clang-format off return 0.25f * ( - src.SampleLevel(gLinearSampler, uv + float2(-uscale, -vscale), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(uscale, -vscale), 0.f) + - src.SampleLevel(gLinearSampler, uv + float2(-uscale, vscale), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(uscale, vscale), 0.f)); + src.SampleLevel(gLinearSampler, uv + float2(-uscale, -vscale), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(uscale, -vscale), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(-uscale, vscale), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(uscale, vscale), 0.f)); + // clang-format on } float4 blurFilter3x3(Texture2D src, const float2 uv, const float uscale, const float vscale) @@ -83,10 +88,18 @@ float4 blurFilter3x3(Texture2D src, const float2 uv, const float uscale, const float x1 = 1.5f + 1.f / (1.f + 5.f), w1 = (1.f + 5.f) / 32.f; const float x1u = x1 * uscale; const float x1v = x1 * vscale; + // clang-format off return - (src.SampleLevel(gLinearSampler, uv + float2(-x1u, -x1v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x1u, -x1v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(-x1u, x1v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x1u, x1v), 0.f)) * (w1 * w1) + - (src.SampleLevel(gLinearSampler, uv + float2(0.f, -x1v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(0.f, x1v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(-x1u, 0.f), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x1u, 0.f), 0.f)) * (w1 * w0) + + (src.SampleLevel(gLinearSampler, uv + float2(-x1u, -x1v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x1u, -x1v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(-x1u, x1v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x1u, x1v), 0.f)) * (w1 * w1) + + (src.SampleLevel(gLinearSampler, uv + float2(0.f, -x1v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(0.f, x1v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(-x1u, 0.f), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x1u, 0.f), 0.f)) * (w1 * w0) + (src.SampleLevel(gLinearSampler, uv + float2(0.f, 0.f), 0.f)) * (w0 * w0); + // clang-format on } float4 blurFilter5x5(Texture2D src, const float2 uv, const float uscale, const float vscale) @@ -97,34 +110,54 @@ float4 blurFilter5x5(Texture2D src, const float2 uv, const float uscale, const float x2 = 3.5f + 1.f / (1.f + 9.f), w2 = (1.f + 10.f) / 512.f; const float x1u = x1 * uscale, x2u = x2 * uscale; const float x1v = x1 * vscale, x2v = x2 * vscale; + // clang-format off float4 s0 = - (src.SampleLevel(gLinearSampler, uv + float2(-x2u, -x2v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x2u, -x2v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(-x2u, x2v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x2u, x2v), 0.f)) * (w2 * w2) + - (src.SampleLevel(gLinearSampler, uv + float2(-x1u, -x2v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x1u, -x2v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(-x1u, x2v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x1u, x2v), 0.f)) * (w1 * w2) + - (src.SampleLevel(gLinearSampler, uv + float2(0.f, -x2v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(0.f, x2v), 0.f)) * (w0 * w2); + (src.SampleLevel(gLinearSampler, uv + float2(-x2u, -x2v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x2u, -x2v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(-x2u, x2v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x2u, x2v), 0.f)) * (w2 * w2) + + (src.SampleLevel(gLinearSampler, uv + float2(-x1u, -x2v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x1u, -x2v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(-x1u, x2v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x1u, x2v), 0.f)) * (w1 * w2) + + (src.SampleLevel(gLinearSampler, uv + float2(0.f, -x2v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(0.f, x2v), 0.f)) * (w0 * w2); float4 s1 = - (src.SampleLevel(gLinearSampler, uv + float2(-x2u, -x1v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x2u, -x1v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(-x2u, x1v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x2u, x1v), 0.f)) * (w2 * w1) + - (src.SampleLevel(gLinearSampler, uv + float2(-x1u, -x1v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x1u, -x1v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(-x1u, x1v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x1u, x1v), 0.f)) * (w1 * w1) + - (src.SampleLevel(gLinearSampler, uv + float2(0.f, -x1v), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(0.f, x1v), 0.f)) * (w0 * w1); + (src.SampleLevel(gLinearSampler, uv + float2(-x2u, -x1v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x2u, -x1v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(-x2u, x1v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x2u, x1v), 0.f)) * (w2 * w1) + + (src.SampleLevel(gLinearSampler, uv + float2(-x1u, -x1v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x1u, -x1v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(-x1u, x1v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x1u, x1v), 0.f)) * (w1 * w1) + + (src.SampleLevel(gLinearSampler, uv + float2(0.f, -x1v), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(0.f, x1v), 0.f)) * (w0 * w1); float4 s2 = - (src.SampleLevel(gLinearSampler, uv + float2(-x2u, 0.f), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x2u, 0.f), 0.f)) * (w2 * w0) + - (src.SampleLevel(gLinearSampler, uv + float2(-x1u, 0.f), 0.f) + src.SampleLevel(gLinearSampler, uv + float2(x1u, 0.f), 0.f)) * (w1 * w0) + + (src.SampleLevel(gLinearSampler, uv + float2(-x2u, 0.f), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x2u, 0.f), 0.f)) * (w2 * w0) + + (src.SampleLevel(gLinearSampler, uv + float2(-x1u, 0.f), 0.f) + + src.SampleLevel(gLinearSampler, uv + float2(x1u, 0.f), 0.f)) * (w1 * w0) + (src.SampleLevel(gLinearSampler, uv + float2(0.f, 0.f), 0.f)) * (w0 * w0); + // clang-format on return s0 + s1 + s2; } [numthreads(16, 16, 1)] -void downsample(uint3 dispatchThreadId : SV_DispatchThreadID) +void downsample(uint3 dispatchThreadId: SV_DispatchThreadID) { - if (any(dispatchThreadId.xy >= gResolution)) return; + if (any(dispatchThreadId.xy >= gResolution)) + return; int2 pixelPos = dispatchThreadId.xy; float2 uv = (pixelPos + 0.5f) * gInvRes; gDst[pixelPos] = blurFilter5x5(gSrc, uv, gInvRes.x * 0.5f, gInvRes.y * 0.5f); } [numthreads(16, 16, 1)] -void upsample(uint3 dispatchThreadId : SV_DispatchThreadID) +void upsample(uint3 dispatchThreadId: SV_DispatchThreadID) { - if (any(dispatchThreadId.xy >= gResolution)) return; + if (any(dispatchThreadId.xy >= gResolution)) + return; int2 pixelPos = dispatchThreadId.xy; float2 uv = (pixelPos + 0.5f) * gInvRes; float4 centertap = blurFilter3x3(gBloomed, uv, gInvRes.x, gInvRes.y); @@ -134,7 +167,9 @@ void upsample(uint3 dispatchThreadId : SV_DispatchThreadID) for (int i = -20; i <= 20; ++i) { float bri = 1.f / (10 + i * i); - float4 c = gBloomed.SampleLevel(gLinearSampler, uv + gStarDir1 * i * 2, 0.f) + gBloomed.SampleLevel(gLinearSampler, uv + gStarDir2 * i * 2, 0.f) + gBloomed.SampleLevel(gLinearSampler, uv + gStarDir3 * i * 2, 0.f); + float4 c = gBloomed.SampleLevel(gLinearSampler, uv + gStarDir1 * i * 2, 0.f) + + gBloomed.SampleLevel(gLinearSampler, uv + gStarDir2 * i * 2, 0.f) + + gBloomed.SampleLevel(gLinearSampler, uv + gStarDir3 * i * 2, 0.f); star += c * bri; } star *= 1.f / 1.793358534742959f; // normalize by the total of the weights of star taps @@ -154,9 +189,10 @@ void upsample(uint3 dispatchThreadId : SV_DispatchThreadID) } [numthreads(16, 16, 1)] -void runPostFX(uint3 dispatchThreadId : SV_DispatchThreadID) +void runPostFX(uint3 dispatchThreadId: SV_DispatchThreadID) { - if (any(dispatchThreadId.xy >= gResolution)) return; + if (any(dispatchThreadId.xy >= gResolution)) + return; const uint2 pixelPos = dispatchThreadId.xy; float2 uv_circular = ((pixelPos + 0.5f) * 2.f - gResolution.xy) * gInvRes.y; // -1 to 1 vertically; more than -1 to 1 horizontally. @@ -185,12 +221,15 @@ void runPostFX(uint3 dispatchThreadId : SV_DispatchThreadID) col *= vignette; float luma = luminance(col); - // squashed_luma is used to partition the luminance range into a shadow-mid-bright, which we then fit a quadratic curve thru the desired saturation + // squashed_luma is used to partition the luminance range into a shadow-mid-bright, which we then fit a quadratic curve thru the desired + // saturation float squashed_luma = saturate(luma / (luma + 0.5f)); - float saturation = max(0.f, gSaturationCurve.z + squashed_luma * gSaturationCurve.y + squashed_luma * squashed_luma * gSaturationCurve.x); + float saturation = + max(0.f, gSaturationCurve.z + squashed_luma * gSaturationCurve.y + squashed_luma * squashed_luma * gSaturationCurve.x); col = lerp(luma, col, saturation); - // ASC-CDL style offset,power,slope (as implemented in blender, see https://blender.stackexchange.com/questions/55231/what-is-the-the-asc-cdl-node) + // ASC-CDL style offset,power,slope (as implemented in blender, see + // https://blender.stackexchange.com/questions/55231/what-is-the-the-asc-cdl-node) col = pow(max(0.f, col * gColorScale + gColorOffset), gColorPower); float4 output = float4(col, 1.); @@ -200,4 +239,3 @@ void runPostFX(uint3 dispatchThreadId : SV_DispatchThreadID) } gDst[pixelPos] = output; } - diff --git a/Source/RenderPasses/SimplePostFX/SimplePostFX.h b/Source/RenderPasses/SimplePostFX/SimplePostFX.h index 56db23657..8eebf4105 100644 --- a/Source/RenderPasses/SimplePostFX/SimplePostFX.h +++ b/Source/RenderPasses/SimplePostFX/SimplePostFX.h @@ -48,21 +48,21 @@ class SimplePostFX : public RenderPass virtual void renderUI(Gui::Widgets& widget) override; virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override {} - bool getEnabled() const { return mEnabled; } - float getWipe() const { return mWipe; } - float getBloomAmount() const { return mBloomAmount; } - float getStarAmount() const { return mStarAmount; } - float getStarAngle() const { return mStarAngle; } - float getVignetteAmount() const { return mVignetteAmount; } - float getChromaticAberrationAmount() const { return mChromaticAberrationAmount; } - float getBarrelDistortAmount() const { return mBarrelDistortAmount; } - float3 getSaturationCurve() const { return mSaturationCurve; } - float3 getColorOffset() const { return mColorOffset; } - float3 getColorScale() const { return mColorScale; } - float3 getColorPower() const { return mColorPower; } - float getColorOffsetScalar() const { return mColorOffsetScalar; } - float getColorScaleScalar() const { return mColorScaleScalar; } - float getColorPowerScalar() const { return mColorPowerScalar; } + bool getEnabled() const { return mEnabled; } + float getWipe() const { return mWipe; } + float getBloomAmount() const { return mBloomAmount; } + float getStarAmount() const { return mStarAmount; } + float getStarAngle() const { return mStarAngle; } + float getVignetteAmount() const { return mVignetteAmount; } + float getChromaticAberrationAmount() const { return mChromaticAberrationAmount; } + float getBarrelDistortAmount() const { return mBarrelDistortAmount; } + float3 getSaturationCurve() const { return mSaturationCurve; } + float3 getColorOffset() const { return mColorOffset; } + float3 getColorScale() const { return mColorScale; } + float3 getColorPower() const { return mColorPower; } + float getColorOffsetScalar() const { return mColorOffsetScalar; } + float getColorScaleScalar() const { return mColorScaleScalar; } + float getColorPowerScalar() const { return mColorPowerScalar; } void setEnabled(bool e) { mEnabled = e; } void setWipe(float v) { mWipe = v; } @@ -85,31 +85,51 @@ class SimplePostFX : public RenderPass const static int kNumLevels = 8; - RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. - uint2 mFixedOutputSize = { 512, 512 }; ///< Output size in pixels when 'Fixed' size is selected. - - ref mpDownsamplePass; - ref mpUpsamplePass; - ref mpPostFXPass; - - ref mpPyramid[kNumLevels + 1]; ///< Image pyramid, fine to coarse, full res down in steps of 4x (16x area). - ref mpLinearSampler; - - float mWipe = 0.f; ///< Wipe across to see the effect without fx. 0<=all effect, 1>= disabled. - bool mEnabled = true; ///< Enable the entire pass. - - float mBloomAmount = 0.f; ///< Amount of bloom. - float mStarAmount = 0.f; ///< how much of a 6 pointed star to add to the bloom kernel. - float mStarAngle = 0.1f; ///< angle of star rays. - float mVignetteAmount = 0.f; ///< Amount of circuit vignetting. - float mChromaticAberrationAmount = 0.f; ///< Amount of radial chromatic aberration. - float mBarrelDistortAmount = 0.f; ///< Amount of Barrel distortion. - float3 mSaturationCurve = float3(1.f, 1.f, 1.f); ///< Saturation amount for shadows, midtones and hilights. - float3 mColorOffset = float3(0.5f, 0.5f, 0.5f); ///< Color offset, tints shadows. - float3 mColorScale = float3(0.5f, 0.5f, 0.5f); ///< Color scale, tints hilights. - float3 mColorPower = float3(0.5f, 0.5f, 0.5f); ///< Color power (gamma), tints midtones. + /// Selected output size. + RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; + /// Output size in pixels when 'Fixed' size is selected. + uint2 mFixedOutputSize = {512, 512}; + + ref mpDownsamplePass; + ref mpUpsamplePass; + ref mpPostFXPass; + + /// Image pyramid, fine to coarse, full res down in steps of 4x (16x area). + ref mpPyramid[kNumLevels + 1]; + ref mpLinearSampler; + + /// Wipe across to see the effect without fx. 0<=all effect, 1>= disabled. + float mWipe = 0.f; + /// Enable the entire pass. + bool mEnabled = true; + + /// Amount of bloom. + float mBloomAmount = 0.f; + /// how much of a 6 pointed star to add to the bloom kernel. + float mStarAmount = 0.f; + /// angle of star rays. + float mStarAngle = 0.1f; + /// Amount of circuit vignetting. + float mVignetteAmount = 0.f; + /// Amount of radial chromatic aberration. + float mChromaticAberrationAmount = 0.f; + /// Amount of Barrel distortion. + float mBarrelDistortAmount = 0.f; + /// Saturation amount for shadows, midtones and hilights. + float3 mSaturationCurve = float3(1.f, 1.f, 1.f); + /// Color offset, tints shadows. + float3 mColorOffset = float3(0.5f, 0.5f, 0.5f); + /// Color scale, tints hilights. + float3 mColorScale = float3(0.5f, 0.5f, 0.5f); + /// Color power (gamma), tints midtones. + float3 mColorPower = float3(0.5f, 0.5f, 0.5f); + // the above colors are also offered as scalars for ease of UI and also to set negative colors. - float mColorOffsetScalar = 0.f; ///< Luma offset, crushes shadows if negative. - float mColorScaleScalar = 0.f; ///< Luma scale, effectively another exposure control. - float mColorPowerScalar = 0.f; ///< Luma power, ie a gamma curve. + + /// Luma offset, crushes shadows if negative. + float mColorOffsetScalar = 0.f; + /// Luma scale, effectively another exposure control. + float mColorScaleScalar = 0.f; + /// Luma power, ie a gamma curve. + float mColorPowerScalar = 0.f; }; diff --git a/Source/RenderPasses/TAA/TAA.cpp b/Source/RenderPasses/TAA/TAA.cpp index 6d48e22af..5b00aa7f0 100644 --- a/Source/RenderPasses/TAA/TAA.cpp +++ b/Source/RenderPasses/TAA/TAA.cpp @@ -29,16 +29,16 @@ namespace { - const std::string kMotionVec = "motionVecs"; - const std::string kColorIn = "colorIn"; - const std::string kColorOut = "colorOut"; +const std::string kMotionVec = "motionVecs"; +const std::string kColorIn = "colorIn"; +const std::string kColorOut = "colorOut"; - const std::string kAlpha = "alpha"; - const std::string kColorBoxSigma = "colorBoxSigma"; - const std::string kAntiFlicker = "antiFlicker"; +const std::string kAlpha = "alpha"; +const std::string kColorBoxSigma = "colorBoxSigma"; +const std::string kAntiFlicker = "antiFlicker"; - const std::string kShaderFilename = "RenderPasses/TAA/TAA.ps.slang"; -} +const std::string kShaderFilename = "RenderPasses/TAA/TAA.ps.slang"; +} // namespace static void regTAA(pybind11::module& m) { @@ -54,21 +54,24 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regTAA); } -TAA::TAA(ref pDevice, const Properties& props) - : RenderPass(pDevice) +TAA::TAA(ref pDevice, const Properties& props) : RenderPass(pDevice) { mpPass = FullScreenPass::create(mpDevice, kShaderFilename); mpFbo = Fbo::create(mpDevice); Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - mpLinearSampler = Sampler::create(mpDevice, samplerDesc); + samplerDesc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Linear); + mpLinearSampler = mpDevice->createSampler(samplerDesc); for (const auto& [key, value] : props) { - if (key == kAlpha) mControls.alpha = value; - else if (key == kColorBoxSigma) mControls.colorBoxSigma = value; - else if (key == kAntiFlicker) mControls.antiFlicker = value; - else logWarning("Unknown property '{}' in a TemporalAA properties.", key); + if (key == kAlpha) + mControls.alpha = value; + else if (key == kColorBoxSigma) + mControls.colorBoxSigma = value; + else if (key == kAntiFlicker) + mControls.antiFlicker = value; + else + logWarning("Unknown property '{}' in a TemporalAA properties.", key); } } @@ -125,7 +128,16 @@ void TAA::allocatePrevColor(const Texture* pColorOut) allocate = allocate || (mpPrevColor->getFormat() != pColorOut->getFormat()); FALCOR_ASSERT(pColorOut->getSampleCount() == 1); - if (allocate) mpPrevColor = Texture::create2D(mpDevice, pColorOut->getWidth(), pColorOut->getHeight(), pColorOut->getFormat(), 1, 1, nullptr, Resource::BindFlags::RenderTarget | Resource::BindFlags::ShaderResource); + if (allocate) + mpPrevColor = mpDevice->createTexture2D( + pColorOut->getWidth(), + pColorOut->getHeight(), + pColorOut->getFormat(), + 1, + 1, + nullptr, + ResourceBindFlags::RenderTarget | ResourceBindFlags::ShaderResource + ); } void TAA::renderUI(Gui::Widgets& widget) diff --git a/Source/RenderPasses/TAA/TAA.h b/Source/RenderPasses/TAA/TAA.h index 5b94b11e9..fe9cf5287 100644 --- a/Source/RenderPasses/TAA/TAA.h +++ b/Source/RenderPasses/TAA/TAA.h @@ -32,8 +32,9 @@ using namespace Falcor; -/** Temporal AA class -*/ +/** + * Temporal AA class + */ class TAA : public RenderPass { public: diff --git a/Source/RenderPasses/TAA/TAA.ps.slang b/Source/RenderPasses/TAA/TAA.ps.slang index a35269b1f..62b65d1a2 100644 --- a/Source/RenderPasses/TAA/TAA.ps.slang +++ b/Source/RenderPasses/TAA/TAA.ps.slang @@ -39,7 +39,6 @@ Texture2D gTexMotionVec; Texture2D gTexPrevColor; SamplerState gSampler; - // Catmull-Rom filtering code from http://vec3.ca/bicubic-filtering-in-fewer-taps/ float3 bicubicSampleCatmullRom(Texture2D tex, SamplerState samp, float2 samplePos, float2 texDim) { @@ -60,6 +59,7 @@ float3 bicubicSampleCatmullRom(Texture2D tex, SamplerState samp, float2 samplePo float2 tc12 = (tc + w2 / w12) * invTextureSize; float2 tc3 = (tc + 2.f) * invTextureSize; + // clang-format off float3 result = tex.SampleLevel(samp, float2(tc0.x, tc0.y), 0.f).rgb * (w0.x * w0.y) + tex.SampleLevel(samp, float2(tc0.x, tc12.y), 0.f).rgb * (w0.x * w12.y) + @@ -70,17 +70,22 @@ float3 bicubicSampleCatmullRom(Texture2D tex, SamplerState samp, float2 samplePo tex.SampleLevel(samp, float2(tc3.x, tc0.y), 0.f).rgb * (w3.x * w0.y) + tex.SampleLevel(samp, float2(tc3.x, tc12.y), 0.f).rgb * (w3.x * w12.y) + tex.SampleLevel(samp, float2(tc3.x, tc3.y), 0.f).rgb * (w3.x * w3.y); - + // clang-format on return result; } - -float4 main(float2 texC : TEXCOORD) : SV_TARGET0 +float4 main(float2 texC: TEXCOORD) : SV_TARGET0 { - const int2 offset[8] = { int2(-1, -1), int2(-1, 1), - int2( 1, -1), int2( 1, 1), - int2( 1, 0), int2( 0, -1), - int2( 0, 1), int2(-1, 0), }; + const int2 offset[8] = { + int2(-1, -1), + int2(-1, 1), + int2(1, -1), + int2(1, 1), + int2(1, 0), + int2(0, -1), + int2(0, 1), + int2(-1, 0), + }; uint2 texDim; uint levels; diff --git a/Source/RenderPasses/TestPasses/TestPyTorchPass.cpp b/Source/RenderPasses/TestPasses/TestPyTorchPass.cpp index 9bd8ab4d3..bcf1faa4c 100644 --- a/Source/RenderPasses/TestPasses/TestPyTorchPass.cpp +++ b/Source/RenderPasses/TestPasses/TestPyTorchPass.cpp @@ -29,7 +29,7 @@ namespace { - const char kShaderFilename[] = "RenderPasses/TestPasses/TestPyTorchPass.cs.slang"; +const char kShaderFilename[] = "RenderPasses/TestPasses/TestPyTorchPass.cs.slang"; } void TestPyTorchPass::registerScriptBindings(pybind11::module& m) @@ -40,29 +40,26 @@ void TestPyTorchPass::registerScriptBindings(pybind11::module& m) pass.def("verifyData", &TestPyTorchPass::verifyData); } -TestPyTorchPass::TestPyTorchPass(ref pDevice, const Properties& props) - : RenderPass(pDevice) +TestPyTorchPass::TestPyTorchPass(ref pDevice, const Properties& props) : RenderPass(pDevice) { { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kShaderFilename).csEntry("writeBuffer"); mpWritePass = ComputePass::create(mpDevice, desc); } { - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary(kShaderFilename).csEntry("readBuffer"); mpReadPass = ComputePass::create(mpDevice, desc); } - mpFence = GpuFence::create(mpDevice); - mpCounterBuffer = Buffer::create(mpDevice, sizeof(uint32_t)); - mpCounterStagingBuffer = Buffer::create(mpDevice, sizeof(uint32_t), ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr); + mpCounterBuffer = mpDevice->createBuffer(sizeof(uint32_t)); + mpCounterStagingBuffer = mpDevice->createBuffer(sizeof(uint32_t), ResourceBindFlags::None, MemoryType::ReadBack, nullptr); #if FALCOR_HAS_CUDA // Initialize CUDA. - // This does not seem to be necessary in order to access shared buffer handles. - // Is it because the python script has created the device prior to calling functions here? - initCuda(); + if (!mpDevice->initCudaDevice()) + FALCOR_THROW("Failed to initialize CUDA device."); #endif } @@ -96,7 +93,7 @@ TestPyTorchPass::PyTorchTensor TestPyTorchPass::generateData(const uint3 dim, co const size_t elemCount = (size_t)dim.x * dim.y * dim.z; const size_t byteSize = elemCount * sizeof(float); - checkInvariant(byteSize <= std::numeric_limits::max(), "Buffer is too large."); + FALCOR_CHECK(byteSize <= std::numeric_limits::max(), "Buffer is too large."); if (mpBuffer == nullptr || mpBuffer->getElementCount() < elemCount) { @@ -104,7 +101,14 @@ TestPyTorchPass::PyTorchTensor TestPyTorchPass::generateData(const uint3 dim, co // Pytorch can access the data in the shared buffer while we generate new data into the data buffer. // It is fine to recreate the buffers here without syncing as the caller is responsible for synchronization. logInfo("Reallocating buffers to size {} bytes", byteSize); - mpBuffer = Buffer::createStructured(mpDevice, sizeof(float), elemCount, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false); + mpBuffer = mpDevice->createStructuredBuffer( + sizeof(float), + elemCount, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType::DeviceLocal, + nullptr, + false + ); mSharedWriteBuffer = createInteropBuffer(mpDevice, byteSize); } @@ -119,21 +123,21 @@ TestPyTorchPass::PyTorchTensor TestPyTorchPass::generateData(const uint3 dim, co // Copy data to shared CUDA buffer. pRenderContext->copyResource(mSharedWriteBuffer.buffer.get(), mpBuffer.get()); - // Flush and wait on fence for synchronization. - pRenderContext->flush(false); - mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); - mpFence->syncCpu(); + // Wait for copy to finish. + pRenderContext->waitForFalcor(); // Construct PyTorch tensor from CUDA buffer. - const size_t shape[3] = { dim.x, dim.y, dim.z }; + const size_t shape[3] = {dim.x, dim.y, dim.z}; const pybind11::dlpack::dtype dtype = pybind11::dtype(); int32_t deviceType = pybind11::device::cuda::value; int32_t deviceId = 0; // TODO: Consistent enumeration of GPU device IDs. - TestPyTorchPass::PyTorchTensor tensor = TestPyTorchPass::PyTorchTensor((void*)mSharedWriteBuffer.devicePtr, 3, shape, pybind11::handle() /* owner */, nullptr /* strides */, dtype, deviceType, deviceId); + TestPyTorchPass::PyTorchTensor tensor = TestPyTorchPass::PyTorchTensor( + (void*)mSharedWriteBuffer.devicePtr, 3, shape, pybind11::handle() /* owner */, nullptr /* strides */, dtype, deviceType, deviceId + ); return tensor; #else - throw RuntimeError("CUDA is not available."); + FALCOR_THROW("CUDA is not available."); #endif } @@ -147,18 +151,13 @@ bool TestPyTorchPass::verifyData(const uint3 dim, const uint32_t offset, TestPyT RenderContext* pRenderContext = mpDevice->getRenderContext(); // Verify that the data is a valid Torch tensor. - if (!data.is_valid() || - data.dtype() != pybind11::dtype() || - data.device_type() != pybind11::device::cuda::value) + if (!data.is_valid() || data.dtype() != pybind11::dtype() || data.device_type() != pybind11::device::cuda::value) { logWarning("Expected CUDA float tensor"); return false; } - if (data.ndim() != 3 || - data.shape(0) != dim.x || - data.shape(1) != dim.y || - data.shape(2) != dim.z) + if (data.ndim() != 3 || data.shape(0) != dim.x || data.shape(1) != dim.y || data.shape(2) != dim.z) { logWarning("Unexpected tensor dimensions (dim {}, expected dim {})", uint3(data.shape(0), data.shape(1), data.shape(2)), dim); return false; @@ -171,12 +170,12 @@ bool TestPyTorchPass::verifyData(const uint3 dim, const uint32_t offset, TestPyT const uint3 stride = { dim[0] > 1 ? data.stride(0) : 0, dim[1] > 1 ? data.stride(1) : 0, - dim[2] > 1 ? data.stride(2) : 0 + dim[2] > 1 ? data.stride(2) : 0, }; const uint3 expectedStride = { dim[0] > 1 ? dim[1] * dim[2] : 0, dim[1] > 1 ? dim[2] : 0, - dim[2] > 1 ? 1 : 0 + dim[2] > 1 ? 1 : 0, }; if (any(stride != expectedStride)) { @@ -187,7 +186,7 @@ bool TestPyTorchPass::verifyData(const uint3 dim, const uint32_t offset, TestPyT // Create shared CUDA/DX buffer for accessing the data. const size_t elemCount = (size_t)dim.x * dim.y * dim.z; const size_t byteSize = elemCount * sizeof(float); - checkInvariant(byteSize <= std::numeric_limits::max(), "Buffer is too large."); + FALCOR_CHECK(byteSize <= std::numeric_limits::max(), "Buffer is too large."); if (mSharedReadBuffer.buffer == nullptr || mSharedReadBuffer.buffer->getSize() < byteSize) { @@ -200,10 +199,10 @@ bool TestPyTorchPass::verifyData(const uint3 dim, const uint32_t offset, TestPyT // Copy to shared CUDA/DX buffer for access from compute pass. CUdeviceptr srcPtr = (CUdeviceptr)data.data(); - cudaCopyDeviceToDevice((void*)mSharedReadBuffer.devicePtr, (void*)srcPtr, byteSize); + cuda_utils::memcpyDeviceToDevice((void*)mSharedReadBuffer.devicePtr, (const void*)srcPtr, byteSize); - // The sync is required for the data to be visible in the DX buffer after the copy. - syncCudaDevice(); + // Wait for CUDA to finish the copy. + pRenderContext->waitForCuda(); pRenderContext->clearUAV(mpCounterBuffer->getUAV().get(), uint4(0)); @@ -220,10 +219,8 @@ bool TestPyTorchPass::verifyData(const uint3 dim, const uint32_t offset, TestPyT // Copy counter to staging buffer for readback. pRenderContext->copyResource(mpCounterStagingBuffer.get(), mpCounterBuffer.get()); - // Flush and wait on fence for synchronization. - pRenderContext->flush(false); - mpFence->gpuSignal(pRenderContext->getLowLevelData()->getCommandQueue()); - mpFence->syncCpu(); + // Wait for results to be available. + pRenderContext->submit(true); const uint32_t counter = *reinterpret_cast(mpCounterStagingBuffer->map(Buffer::MapType::Read)); mpCounterStagingBuffer->unmap(); @@ -237,6 +234,6 @@ bool TestPyTorchPass::verifyData(const uint3 dim, const uint32_t offset, TestPyT return true; #else - throw RuntimeError("CUDA is not available."); + FALCOR_THROW("CUDA is not available."); #endif } diff --git a/Source/RenderPasses/TestPasses/TestPyTorchPass.cs.slang b/Source/RenderPasses/TestPasses/TestPyTorchPass.cs.slang index 1cea9fc0d..f659b1028 100644 --- a/Source/RenderPasses/TestPasses/TestPyTorchPass.cs.slang +++ b/Source/RenderPasses/TestPasses/TestPyTorchPass.cs.slang @@ -41,18 +41,20 @@ uint getIndex(uint3 threadID) } [numthreads(16, 16, 1)] -void writeBuffer(uint3 threadID : SV_DispatchThreadID) +void writeBuffer(uint3 threadID: SV_DispatchThreadID) { - if (any(threadID >= dim)) return; + if (any(threadID >= dim)) + return; uint idx = getIndex(threadID); bufferUav[idx] = (float)(offset + idx); } [numthreads(1, 16, 16)] -void readBuffer(uint3 threadID : SV_DispatchThreadID) +void readBuffer(uint3 threadID: SV_DispatchThreadID) { - if (any(threadID >= dim)) return; + if (any(threadID >= dim)) + return; uint idx = getIndex(threadID); float expectedValue = (float)(offset + idx); diff --git a/Source/RenderPasses/TestPasses/TestPyTorchPass.h b/Source/RenderPasses/TestPasses/TestPyTorchPass.h index b2fa8dc58..c88643ce9 100644 --- a/Source/RenderPasses/TestPasses/TestPyTorchPass.h +++ b/Source/RenderPasses/TestPasses/TestPyTorchPass.h @@ -62,14 +62,17 @@ class TestPyTorchPass : public RenderPass bool verifyData(const uint3 dim, const uint32_t offset, PyTorchTensor data); private: - ref mpBuffer; ///< GPU buffer for generated data. + /// GPU buffer for generated data. + ref mpBuffer; ref mpCounterBuffer; ref mpCounterStagingBuffer; #if FALCOR_HAS_CUDA - InteropBuffer mSharedWriteBuffer; ///< Shared CUDA/DX buffer for passing data from Falcor to PyTorch asynchronously. - InteropBuffer mSharedReadBuffer; ///< Shared CUDA/DX buffer for passing data from PyTorch to Falcor asynchronously. + /// Shared CUDA/Falcor buffer for passing data from Falcor to PyTorch asynchronously. + InteropBuffer mSharedWriteBuffer; + /// Shared CUDA/Falcor buffer for passing data from PyTorch to Falcor asynchronously. + InteropBuffer mSharedReadBuffer; #endif ref mpWritePass; ref mpReadPass; - ref mpFence; + ref mpFence; }; diff --git a/Source/RenderPasses/TestPasses/TestRtProgram.cpp b/Source/RenderPasses/TestPasses/TestRtProgram.cpp index 1f9401e97..d59f81959 100644 --- a/Source/RenderPasses/TestPasses/TestRtProgram.cpp +++ b/Source/RenderPasses/TestPasses/TestRtProgram.cpp @@ -30,18 +30,18 @@ namespace { - const char kShaderFilename[] = "RenderPasses/TestPasses/TestRtProgram.rt.slang"; +const char kShaderFilename[] = "RenderPasses/TestPasses/TestRtProgram.rt.slang"; - // Ray tracing program settings. Set as small values as possible. - const uint32_t kMaxPayloadSizeBytes = 16; - const uint32_t kMaxAttributeSizeBytes = 8; - const uint32_t kMaxRecursionDepth = 1; +// Ray tracing program settings. Set as small values as possible. +const uint32_t kMaxPayloadSizeBytes = 16; +const uint32_t kMaxAttributeSizeBytes = 8; +const uint32_t kMaxRecursionDepth = 1; - const char kMode[] = "mode"; - const char kOutput[] = "output"; +const char kMode[] = "mode"; +const char kOutput[] = "output"; - std::mt19937 rng; -} +std::mt19937 rng; +} // namespace void TestRtProgram::registerScriptBindings(pybind11::module& m) { @@ -51,15 +51,16 @@ void TestRtProgram::registerScriptBindings(pybind11::module& m) pass.def("moveCustomPrimitive", &TestRtProgram::moveCustomPrimitive); } -TestRtProgram::TestRtProgram(ref pDevice, const Properties& props) - : RenderPass(pDevice) +TestRtProgram::TestRtProgram(ref pDevice, const Properties& props) : RenderPass(pDevice) { for (const auto& [key, value] : props) { - if (key == kMode) mMode = value; - else logWarning("Unknown property '{}' in TestRtProgram properties.", key); + if (key == kMode) + mMode = value; + else + logWarning("Unknown property '{}' in TestRtProgram properties.", key); } - if (mMode > 1) throw RuntimeError("mode has to be 0 or 1"); + FALCOR_CHECK(mMode == 0 || mMode == 1, "mode has to be 0 or 1"); } Properties TestRtProgram::getProperties() const @@ -72,7 +73,7 @@ Properties TestRtProgram::getProperties() const RenderPassReflection TestRtProgram::reflect(const CompileData& compileData) { RenderPassReflection reflector; - reflector.addOutput(kOutput, "Output image").bindFlags(Resource::BindFlags::UnorderedAccess).format(ResourceFormat::RGBA32Float); + reflector.addOutput(kOutput, "Output image").bindFlags(ResourceBindFlags::UnorderedAccess).format(ResourceFormat::RGBA32Float); return reflector; } @@ -98,7 +99,7 @@ void TestRtProgram::sceneChanged() // Example creating a ray tracing program using the new interfaces. // - RtProgram::Desc desc; + ProgramDesc desc; desc.addShaderModules(mpScene->getShaderModules()); desc.addShaderLibrary(kShaderFilename); desc.setMaxTraceRecursionDepth(kMaxRecursionDepth); @@ -137,7 +138,7 @@ void TestRtProgram::sceneChanged() // Override specific hit groups for some geometries. for (uint geometryID = 0; geometryID < geometryCount; geometryID++) { - auto type = mpScene->getGeometryType(GlobalGeometryID{ geometryID }); + auto type = mpScene->getGeometryType(GlobalGeometryID{geometryID}); if (type == Scene::GeometryType::TriangleMesh) { @@ -154,7 +155,7 @@ void TestRtProgram::sceneChanged() } else if (type == Scene::GeometryType::Custom) { - uint32_t index = mpScene->getCustomPrimitiveIndex(GlobalGeometryID{ geometryID }); + uint32_t index = mpScene->getCustomPrimitiveIndex(GlobalGeometryID{geometryID}); uint32_t userID = mpScene->getCustomPrimitive(index).userID; // Use non-default material for custom primitives with even userID. @@ -178,14 +179,14 @@ void TestRtProgram::sceneChanged() sbt = RtBindingTable::create(2, 1, geometryCount); // Create type conformances. - Program::TypeConformanceList typeConformances0 = Program::TypeConformanceList{ {{"Mtl0", "IMtl"}, 0u} }; - Program::TypeConformanceList typeConformances1 = Program::TypeConformanceList{ {{"Mtl1", "IMtl"}, 1u} }; - Program::TypeConformanceList typeConformances2 = Program::TypeConformanceList{ {{"Mtl2", "IMtl"}, 2u} }; + TypeConformanceList typeConformances0 = TypeConformanceList{{{"Mtl0", "IMtl"}, 0u}}; + TypeConformanceList typeConformances1 = TypeConformanceList{{{"Mtl1", "IMtl"}, 1u}}; + TypeConformanceList typeConformances2 = TypeConformanceList{{{"Mtl2", "IMtl"}, 2u}}; // Create hit group shaders. // These are using the same entry points but are specialized using different type conformances. // For each specialization we add a name suffix so that each generated entry point has a unique name. - RtProgram::ShaderID mtl[3]; + ProgramDesc::ShaderID mtl[3]; mtl[0] = desc.addHitGroup("closestHit", "anyHit", "", typeConformances0, "Mtl0"); mtl[1] = desc.addHitGroup("closestHit", "anyHit", "", typeConformances1, "Mtl1"); mtl[2] = desc.addHitGroup("closestHit", "anyHit", "", typeConformances2, "Mtl2"); @@ -209,7 +210,7 @@ void TestRtProgram::sceneChanged() defines.add("MODE", std::to_string(mMode)); // Create program and vars. - mRT.pProgram = RtProgram::create(mpDevice, desc, defines); + mRT.pProgram = Program::create(mpDevice, desc, defines); mRT.pVars = RtProgramVars::create(mpDevice, mRT.pProgram, sbt); } @@ -220,11 +221,12 @@ void TestRtProgram::execute(RenderContext* pRenderContext, const RenderData& ren auto pOutput = renderData.getTexture(kOutput); pRenderContext->clearUAV(pOutput->getUAV().get(), float4(0, 0, 0, 1)); - if (!mpScene) return; + if (!mpScene) + return; - // Check for scene geometry changes. - // Such changes require us to re-create the raytracing binding table and vars. - if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) + // Check for scene changes that require shader recompilation. + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded) || + is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) { sceneChanged(); } @@ -298,7 +300,7 @@ void TestRtProgram::addCustomPrimitive() } std::uniform_real_distribution u(0.f, 1.f); - float3 c = { 4.f * u(rng) - 2.f, u(rng), 4.f * u(rng) - 2.f }; + float3 c = {4.f * u(rng) - 2.f, u(rng), 4.f * u(rng) - 2.f}; float r = 0.5f * u(rng) + 0.5f; mpScene->addCustomPrimitive(mUserID++, AABB(c - r, c + r)); diff --git a/Source/RenderPasses/TestPasses/TestRtProgram.h b/Source/RenderPasses/TestPasses/TestRtProgram.h index 01c581bdd..31bcb73ec 100644 --- a/Source/RenderPasses/TestPasses/TestRtProgram.h +++ b/Source/RenderPasses/TestPasses/TestRtProgram.h @@ -68,7 +68,7 @@ class TestRtProgram : public RenderPass struct { - ref pProgram; + ref pProgram; ref pVars; } mRT; }; diff --git a/Source/RenderPasses/TestPasses/TestRtProgram.rt.slang b/Source/RenderPasses/TestPasses/TestRtProgram.rt.slang index b8055a25f..427bcd101 100644 --- a/Source/RenderPasses/TestPasses/TestRtProgram.rt.slang +++ b/Source/RenderPasses/TestPasses/TestRtProgram.rt.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -88,13 +88,14 @@ void miss1(inout RayData rayData) #if MODE == 0 [shader("anyhit")] -void anyHit(inout RayData rayData : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) +void anyHit(inout RayData rayData: SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs: SV_IntersectionAttributes) { // Alpha test for non-opaque geometry. GeometryInstanceID instanceID = getGeometryInstanceID(); VertexData v = getVertexData(instanceID, PrimitiveIndex(), attribs); uint materialID = gScene.getMaterialID(instanceID); - if (gScene.materials.alphaTest(v, materialID, 0.f)) IgnoreHit(); + if (gScene.materials.alphaTest(v, materialID, 0.f)) + IgnoreHit(); } [shader("closesthit")] @@ -140,45 +141,27 @@ interface IMtl struct Mtl0 : IMtl { - float3 getColor() - { - return float3(1, 0, 0); - } + float3 getColor() { return float3(1, 0, 0); } - bool alphaTest(uint2 pixel) - { - return pixel.x % 8 == 0; - } + bool alphaTest(uint2 pixel) { return pixel.x % 8 == 0; } } struct Mtl1 : IMtl { - float3 getColor() - { - return float3(0, 1, 0); - } + float3 getColor() { return float3(0, 1, 0); } - bool alphaTest(uint2 pixel) - { - return pixel.y % 8 == 0; - } + bool alphaTest(uint2 pixel) { return pixel.y % 8 == 0; } } struct Mtl2 : IMtl { - float3 getColor() - { - return float3(0, 0, 1); - } + float3 getColor() { return float3(0, 0, 1); } - bool alphaTest(uint2 pixel) - { - return (pixel.x + pixel.y) % 8 == 0; - } + bool alphaTest(uint2 pixel) { return (pixel.x + pixel.y) % 8 == 0; } } [shader("anyhit")] -void anyHit(inout RayData rayData : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) +void anyHit(inout RayData rayData: SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs: SV_IntersectionAttributes) { GeometryInstanceID instanceID = getGeometryInstanceID(); uint geometryID = gScene.getGeometryInstance(instanceID).geometryID; @@ -192,7 +175,8 @@ void anyHit(inout RayData rayData : SV_RayPayload, BuiltInTriangleIntersectionAt let mtl = createDynamicObject(int(type), dummyData); // Depending on which type was created we'll get a different pattern of discarded pixels. - if (mtl.alphaTest(DispatchRaysIndex().xy)) IgnoreHit(); + if (mtl.alphaTest(DispatchRaysIndex().xy)) + IgnoreHit(); } [shader("closesthit")] diff --git a/Source/RenderPasses/ToneMapper/Luminance.ps.slang b/Source/RenderPasses/ToneMapper/Luminance.ps.slang index 6152a1fc2..648e3a436 100644 --- a/Source/RenderPasses/ToneMapper/Luminance.ps.slang +++ b/Source/RenderPasses/ToneMapper/Luminance.ps.slang @@ -34,7 +34,7 @@ float luminance(float3 color) return dot(color, float3(0.299, 0.587, 0.114)); } -float4 main(float2 texC : TEXCOORD) : SV_TARGET0 +float4 main(float2 texC: TEXCOORD) : SV_TARGET0 { float4 color = gColorTex.Sample(gColorSampler, texC); float logLuminance = log2(max(0.0001, luminance(color.xyz))); diff --git a/Source/RenderPasses/ToneMapper/ToneMapper.cpp b/Source/RenderPasses/ToneMapper/ToneMapper.cpp index 8fb136ce6..8672f53c6 100644 --- a/Source/RenderPasses/ToneMapper/ToneMapper.cpp +++ b/Source/RenderPasses/ToneMapper/ToneMapper.cpp @@ -31,54 +31,54 @@ namespace { - const char kSrc[] = "src"; - const char kDst[] = "dst"; +const char kSrc[] = "src"; +const char kDst[] = "dst"; - // Scripting options. - const char kOutputSize[] = "outputSize"; - const char kOutputFormat[] = "outputFormat"; - const char kFixedOutputSize[] = "fixedOutputSize"; +// Scripting options. +const char kOutputSize[] = "outputSize"; +const char kOutputFormat[] = "outputFormat"; +const char kFixedOutputSize[] = "fixedOutputSize"; - const char kUseSceneMetadata[] = "useSceneMetadata"; - const char kExposureCompensation[] = "exposureCompensation"; - const char kAutoExposure[] = "autoExposure"; - const char kExposureValue[] = "exposureValue"; - const char kFilmSpeed[] = "filmSpeed"; - const char kFNumber[] = "fNumber"; - const char kShutter[] = "shutter"; - const char kExposureMode[] = "exposureMode"; +const char kUseSceneMetadata[] = "useSceneMetadata"; +const char kExposureCompensation[] = "exposureCompensation"; +const char kAutoExposure[] = "autoExposure"; +const char kExposureValue[] = "exposureValue"; +const char kFilmSpeed[] = "filmSpeed"; +const char kFNumber[] = "fNumber"; +const char kShutter[] = "shutter"; +const char kExposureMode[] = "exposureMode"; - const char kWhiteBalance[] = "whiteBalance"; - const char kWhitePoint[] = "whitePoint"; +const char kWhiteBalance[] = "whiteBalance"; +const char kWhitePoint[] = "whitePoint"; - const char kOperator[] = "operator"; - const char kClamp[] = "clamp"; - const char kWhiteMaxLuminance[] = "whiteMaxLuminance"; - const char kWhiteScale[] = "whiteScale"; +const char kOperator[] = "operator"; +const char kClamp[] = "clamp"; +const char kWhiteMaxLuminance[] = "whiteMaxLuminance"; +const char kWhiteScale[] = "whiteScale"; - const char kLuminanceFile[] = "RenderPasses/ToneMapper/Luminance.ps.slang"; - const char kToneMappingFile[] = "RenderPasses/ToneMapper/ToneMapping.ps.slang"; +const char kLuminanceFile[] = "RenderPasses/ToneMapper/Luminance.ps.slang"; +const char kToneMappingFile[] = "RenderPasses/ToneMapper/ToneMapping.ps.slang"; - const float kExposureCompensationMin = -12.f; - const float kExposureCompensationMax = 12.f; +const float kExposureCompensationMin = -12.f; +const float kExposureCompensationMax = 12.f; - const float kFilmSpeedMin = 1.f; - const float kFilmSpeedMax = 6400.f; +const float kFilmSpeedMin = 1.f; +const float kFilmSpeedMax = 6400.f; - const float kFNumberMin = 0.1f; // Minimum fNumber, > 0 to avoid numerical issues (i.e., non-physical values are allowed) - const float kFNumberMax = 100.f; +const float kFNumberMin = 0.1f; // Minimum fNumber, > 0 to avoid numerical issues (i.e., non-physical values are allowed) +const float kFNumberMax = 100.f; - const float kShutterMin = 0.1f; // Min reciprocal shutter time - const float kShutterMax = 10000.f; // Max reciprocal shutter time +const float kShutterMin = 0.1f; // Min reciprocal shutter time +const float kShutterMax = 10000.f; // Max reciprocal shutter time - // EV is ultimately derived from shutter and fNumber; set its range based on that of its inputs - const float kExposureValueMin = std::log2(kShutterMin * kFNumberMin * kFNumberMin); - const float kExposureValueMax = std::log2(kShutterMax * kFNumberMax * kFNumberMax); +// EV is ultimately derived from shutter and fNumber; set its range based on that of its inputs +const float kExposureValueMin = std::log2(kShutterMin * kFNumberMin * kFNumberMin); +const float kExposureValueMax = std::log2(kShutterMax * kFNumberMax * kFNumberMax); - // Note: Color temperatures < ~1905K are out-of-gamut in Rec.709. - const float kWhitePointMin = 1905.f; - const float kWhitePointMax = 25000.f; -} +// Note: Color temperatures < ~1905K are out-of-gamut in Rec.709. +const float kWhitePointMin = 1905.f; +const float kWhitePointMax = 25000.f; +} // namespace static void regToneMapper(pybind11::module& m) { @@ -89,18 +89,20 @@ static void regToneMapper(pybind11::module& m) pass.def_property(kFilmSpeed, &ToneMapper::getFilmSpeed, &ToneMapper::setFilmSpeed); pass.def_property(kWhiteBalance, &ToneMapper::getWhiteBalance, &ToneMapper::setWhiteBalance); pass.def_property(kWhitePoint, &ToneMapper::getWhitePoint, &ToneMapper::setWhitePoint); - pass.def_property(kOperator, + pass.def_property( + kOperator, [](const ToneMapper& self) { return enumToString(self.getOperator()); }, - [](ToneMapper& self, const std::string& value) {self.setOperator(stringToEnum(value)); } + [](ToneMapper& self, const std::string& value) { self.setOperator(stringToEnum(value)); } ); pass.def_property(kClamp, &ToneMapper::getClamp, &ToneMapper::setClamp); pass.def_property(kWhiteMaxLuminance, &ToneMapper::getWhiteMaxLuminance, &ToneMapper::setWhiteMaxLuminance); pass.def_property(kWhiteScale, &ToneMapper::getWhiteScale, &ToneMapper::setWhiteScale); pass.def_property(kFNumber, &ToneMapper::getFNumber, &ToneMapper::setFNumber); pass.def_property(kShutter, &ToneMapper::getShutter, &ToneMapper::setShutter); - pass.def_property(kExposureMode, + pass.def_property( + kExposureMode, [](const ToneMapper& self) { return enumToString(self.getExposureMode()); }, - [](ToneMapper& self, const std::string& value) {self.setExposureMode(stringToEnum(value)); } + [](ToneMapper& self, const std::string& value) { self.setExposureMode(stringToEnum(value)); } ); } @@ -110,8 +112,7 @@ extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registr ScriptBindings::registerBinding(regToneMapper); } -ToneMapper::ToneMapper(ref pDevice, const Properties& props) - : RenderPass(pDevice) +ToneMapper::ToneMapper(ref pDevice, const Properties& props) : RenderPass(pDevice) { parseProperties(props); @@ -121,33 +122,50 @@ ToneMapper::ToneMapper(ref pDevice, const Properties& props) updateWhiteBalanceTransform(); Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Point, Sampler::Filter::Point, Sampler::Filter::Point); - mpPointSampler = Sampler::create(mpDevice, samplerDesc); - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point); - mpLinearSampler = Sampler::create(mpDevice, samplerDesc); + samplerDesc.setFilterMode(TextureFilteringMode::Point, TextureFilteringMode::Point, TextureFilteringMode::Point); + mpPointSampler = mpDevice->createSampler(samplerDesc); + samplerDesc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Point); + mpLinearSampler = mpDevice->createSampler(samplerDesc); } void ToneMapper::parseProperties(const Properties& props) { for (const auto& [key, value] : props) { - if (key == kOutputSize) mOutputSizeSelection = value; - else if (key == kOutputFormat) mOutputFormat = value; - else if (key == kFixedOutputSize) mFixedOutputSize = value; - else if (key == kUseSceneMetadata) mUseSceneMetadata = value; - else if (key == kExposureCompensation) setExposureCompensation(value); - else if (key == kAutoExposure) setAutoExposure(value); - else if (key == kFilmSpeed) setFilmSpeed(value); - else if (key == kWhiteBalance) setWhiteBalance(value); - else if (key == kWhitePoint) setWhitePoint(value); - else if (key == kOperator) setOperator(value); - else if (key == kClamp) setClamp(value); - else if (key == kWhiteMaxLuminance) setWhiteMaxLuminance(value); - else if (key == kWhiteScale) setWhiteScale(value); - else if (key == kFNumber) setFNumber(value); - else if (key == kShutter) setShutter(value); - else if (key == kExposureMode) setExposureMode(value); - else logWarning("Unknown property '{}' in a ToneMapping properties.", key); + if (key == kOutputSize) + mOutputSizeSelection = value; + else if (key == kOutputFormat) + mOutputFormat = value; + else if (key == kFixedOutputSize) + mFixedOutputSize = value; + else if (key == kUseSceneMetadata) + mUseSceneMetadata = value; + else if (key == kExposureCompensation) + setExposureCompensation(value); + else if (key == kAutoExposure) + setAutoExposure(value); + else if (key == kFilmSpeed) + setFilmSpeed(value); + else if (key == kWhiteBalance) + setWhiteBalance(value); + else if (key == kWhitePoint) + setWhitePoint(value); + else if (key == kOperator) + setOperator(value); + else if (key == kClamp) + setClamp(value); + else if (key == kWhiteMaxLuminance) + setWhiteMaxLuminance(value); + else if (key == kWhiteScale) + setWhiteScale(value); + else if (key == kFNumber) + setFNumber(value); + else if (key == kShutter) + setShutter(value); + else if (key == kExposureMode) + setExposureMode(value); + else + logWarning("Unknown property '{}' in a ToneMapping properties.", key); } } @@ -155,8 +173,10 @@ Properties ToneMapper::getProperties() const { Properties props; props[kOutputSize] = mOutputSizeSelection; - if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) props[kFixedOutputSize] = mFixedOutputSize; - if (mOutputFormat != ResourceFormat::Unknown) props[kOutputFormat] = mOutputFormat; + if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) + props[kFixedOutputSize] = mFixedOutputSize; + if (mOutputFormat != ResourceFormat::Unknown) + props[kOutputFormat] = mOutputFormat; props[kUseSceneMetadata] = mUseSceneMetadata; props[kExposureCompensation] = mExposureCompensation; props[kAutoExposure] = mAutoExposure; @@ -263,9 +283,8 @@ void ToneMapper::createLuminanceFbo(const ref& pSrc) if (createFbo == false) { - createFbo = (requiredWidth != mpLuminanceFbo->getWidth()) || - (requiredHeight != mpLuminanceFbo->getHeight()) || - (luminanceFormat != mpLuminanceFbo->getColorTexture(0)->getFormat()); + createFbo = (requiredWidth != mpLuminanceFbo->getWidth()) || (requiredHeight != mpLuminanceFbo->getHeight()) || + (luminanceFormat != mpLuminanceFbo->getColorTexture(0)->getFormat()); } if (createFbo) @@ -286,15 +305,19 @@ void ToneMapper::renderUI(Gui::Widgets& widget) { // Controls for output size. // When output size requirements change, we'll trigger a graph recompile to update the render pass I/O sizes. - if (widget.dropdown("Output size", mOutputSizeSelection)) requestRecompile(); + if (widget.dropdown("Output size", mOutputSizeSelection)) + requestRecompile(); if (mOutputSizeSelection == RenderPassHelpers::IOSize::Fixed) { - if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) requestRecompile(); + if (widget.var("Size in pixels", mFixedOutputSize, 32u, 16384u)) + requestRecompile(); } if (auto exposureGroup = widget.group("Exposure", true)) { - mUpdateToneMapPass |= exposureGroup.var("Exposure Compensation", mExposureCompensation, kExposureCompensationMin, kExposureCompensationMax, 0.1f, false, "%.1f"); + mUpdateToneMapPass |= exposureGroup.var( + "Exposure Compensation", mExposureCompensation, kExposureCompensationMin, kExposureCompensationMax, 0.1f, false, "%.1f" + ); mRecreateToneMapPass |= exposureGroup.checkbox("Auto Exposure", mAutoExposure); @@ -371,9 +394,12 @@ void ToneMapper::setScene(RenderContext* pRenderContext, const ref& pScen { const Scene::Metadata& metadata = pScene->getMetadata(); - if (metadata.filmISO) setFilmSpeed(metadata.filmISO.value()); - if (metadata.fNumber) setFNumber(metadata.fNumber.value()); - if (metadata.shutterSpeed) setShutter(metadata.shutterSpeed.value()); + if (metadata.filmISO) + setFilmSpeed(metadata.filmISO.value()); + if (metadata.fNumber) + setFNumber(metadata.fNumber.value()); + if (metadata.shutterSpeed) + setShutter(metadata.shutterSpeed.value()); } } @@ -491,8 +517,10 @@ void ToneMapper::createToneMapPass() { DefineList defines; defines.add("_TONE_MAPPER_OPERATOR", std::to_string(static_cast(mOperator))); - if (mAutoExposure) defines.add("_TONE_MAPPER_AUTO_EXPOSURE"); - if (mClamp) defines.add("_TONE_MAPPER_CLAMP"); + if (mAutoExposure) + defines.add("_TONE_MAPPER_AUTO_EXPOSURE"); + if (mClamp) + defines.add("_TONE_MAPPER_CLAMP"); mpToneMapPass = FullScreenPass::create(mpDevice, kToneMappingFile, defines); } diff --git a/Source/RenderPasses/ToneMapper/ToneMapper.h b/Source/RenderPasses/ToneMapper/ToneMapper.h index a5ec5f4e0..ff0ba3e52 100644 --- a/Source/RenderPasses/ToneMapper/ToneMapper.h +++ b/Source/RenderPasses/ToneMapper/ToneMapper.h @@ -38,22 +38,27 @@ using namespace Falcor; class ToneMapper : public RenderPass { public: - FALCOR_PLUGIN_CLASS(ToneMapper, "ToneMapper", { - "Tone-map a color-buffer. The resulting buffer is always in the [0, 1] range. The pass supports auto-exposure and eye-adaptation." - }); + FALCOR_PLUGIN_CLASS( + ToneMapper, + "ToneMapper", + {"Tone-map a color-buffer. The resulting buffer is always in the [0, 1] range. The pass supports auto-exposure and eye-adaptation."} + ); using Operator = ToneMapperOperator; enum class ExposureMode { - AperturePriority, // Keep aperture constant when modifying EV - ShutterPriority, // Keep shutter constant when modifying EV + AperturePriority, // Keep aperture constant when modifying EV + ShutterPriority, // Keep shutter constant when modifying EV }; - FALCOR_ENUM_INFO(ExposureMode, { - { ExposureMode::AperturePriority, "AperturePriority" }, - { ExposureMode::ShutterPriority, "ShutterPriority" }, - }); + FALCOR_ENUM_INFO( + ExposureMode, + { + {ExposureMode::AperturePriority, "AperturePriority"}, + {ExposureMode::ShutterPriority, "ShutterPriority"}, + } + ); static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } @@ -112,32 +117,51 @@ class ToneMapper : public RenderPass ref mpPointSampler; ref mpLinearSampler; - RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; ///< Selected output size. - ResourceFormat mOutputFormat = ResourceFormat::Unknown; ///< Output format (uses default when set to ResourceFormat::Unknown). - uint2 mFixedOutputSize = { 512, 512 }; ///< Output size in pixels when 'Fixed' size is selected. - - bool mUseSceneMetadata = true; ///< Use scene metadata for setting up tonemapper when loading a scene. - - float mExposureCompensation = 0.f; ///< Exposure compensation (in F-stops). - bool mAutoExposure = false; ///< Enable auto exposure. - float mExposureValue = 0.0f; ///< Exposure value (EV), derived from fNumber, shutter, and film speed; only used when auto exposure is disabled. - float mFilmSpeed = 100.f; ///< Film speed (ISO), only used when auto exposure is disabled. - float mFNumber = 1.f; ///< Lens speed - float mShutter = 1.f; ///< Reciprocal of shutter time - - bool mWhiteBalance = false; ///< Enable white balance. - float mWhitePoint = 6500.0f; ///< White point (K). - - Operator mOperator = Operator::Aces;///< Tone mapping operator. - bool mClamp = true; ///< Clamp output to [0,1]. - - float mWhiteMaxLuminance = 1.0f; ///< Parameter used in ModifiedReinhard operator. - float mWhiteScale = 11.2f; ///< Parameter used in Uc2Hable operator. + /// Selected output size. + RenderPassHelpers::IOSize mOutputSizeSelection = RenderPassHelpers::IOSize::Default; + /// Output format (uses default when set to ResourceFormat::Unknown). + ResourceFormat mOutputFormat = ResourceFormat::Unknown; + /// Output size in pixels when 'Fixed' size is selected. + uint2 mFixedOutputSize = {512, 512}; + + /// Use scene metadata for setting up tonemapper when loading a scene. + bool mUseSceneMetadata = true; + + /// Exposure compensation (in F-stops). + float mExposureCompensation = 0.f; + /// Enable auto exposure. + bool mAutoExposure = false; + /// Exposure value (EV), derived from fNumber, shutter, and film speed; only used when auto exposure is disabled. + float mExposureValue = 0.0f; + /// Film speed (ISO), only used when auto exposure is disabled. + float mFilmSpeed = 100.f; + /// Lens speed + float mFNumber = 1.f; + /// Reciprocal of shutter time + float mShutter = 1.f; + + /// Enable white balance. + bool mWhiteBalance = false; + /// White point (K). + float mWhitePoint = 6500.0f; + + /// Tone mapping operator. + Operator mOperator = Operator::Aces; + /// Clamp output to [0,1]. + bool mClamp = true; + + /// Parameter used in ModifiedReinhard operator. + float mWhiteMaxLuminance = 1.0f; + /// Parameter used in Uc2Hable operator. + float mWhiteScale = 11.2f; // Pre-computed fields based on above settings - float3x3 mWhiteBalanceTransform; ///< Color balance transform in RGB space. - float3 mSourceWhite; ///< Source illuminant in RGB (the white point to which the image is transformed to conform to). - float3x3 mColorTransform; ///< Final color transform with exposure value baked in. + /// Color balance transform in RGB space. + float3x3 mWhiteBalanceTransform; + /// Source illuminant in RGB (the white point to which the image is transformed to conform to). + float3 mSourceWhite; + /// Final color transform with exposure value baked in. + float3x3 mColorTransform; bool mRecreateToneMapPass = true; bool mUpdateToneMapPass = true; diff --git a/Source/RenderPasses/ToneMapper/ToneMapperParams.slang b/Source/RenderPasses/ToneMapper/ToneMapperParams.slang index a2376f415..5b1e12dd6 100644 --- a/Source/RenderPasses/ToneMapper/ToneMapperParams.slang +++ b/Source/RenderPasses/ToneMapper/ToneMapperParams.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -32,29 +32,33 @@ BEGIN_NAMESPACE_FALCOR enum class ToneMapperOperator : uint32_t { - Linear, ///< Linear mapping - Reinhard, ///< Reinhard operator - ReinhardModified, ///< Reinhard operator with maximum white intensity - HejiHableAlu, ///< John Hable's ALU approximation of Jim Heji's filmic operator - HableUc2, ///< John Hable's filmic tone-mapping used in Uncharted 2 - Aces, ///< Aces Filmic Tone-Mapping + Linear, ///< Linear mapping + Reinhard, ///< Reinhard operator + ReinhardModified, ///< Reinhard operator with maximum white intensity + HejiHableAlu, ///< John Hable's ALU approximation of Jim Heji's filmic operator + HableUc2, ///< John Hable's filmic tone-mapping used in Uncharted 2 + Aces, ///< Aces Filmic Tone-Mapping }; -FALCOR_ENUM_INFO(ToneMapperOperator, { - { ToneMapperOperator::Linear, "Linear" }, - { ToneMapperOperator::Reinhard, "Reinhard" }, - { ToneMapperOperator::ReinhardModified, "ReinhardModified" }, - { ToneMapperOperator::HejiHableAlu, "HejiHableAlu" }, - { ToneMapperOperator::HableUc2, "HableUc2" }, - { ToneMapperOperator::Aces, "Aces" }, -}); +FALCOR_ENUM_INFO( + ToneMapperOperator, + { + { ToneMapperOperator::Linear, "Linear" }, + { ToneMapperOperator::Reinhard, "Reinhard" }, + { ToneMapperOperator::ReinhardModified, "ReinhardModified" }, + { ToneMapperOperator::HejiHableAlu, "HejiHableAlu" }, + { ToneMapperOperator::HableUc2, "HableUc2" }, + { ToneMapperOperator::Aces, "Aces" }, + } +); FALCOR_ENUM_REGISTER(ToneMapperOperator); -/** Tone mapper parameters shared between host and device. - Make sure struct layout follows the HLSL packing rules as it is uploaded as a memory blob. - Do not use bool's as they are 1 byte in Visual Studio, 4 bytes in HLSL. - https://msdn.microsoft.com/en-us/library/windows/desktop/bb509632(v=vs.85).aspx -*/ +/** + * Tone mapper parameters shared between host and device. + * Make sure struct layout follows the HLSL packing rules as it is uploaded as a memory blob. + * Do not use bool's as they are 1 byte in Visual Studio, 4 bytes in HLSL. + * https://msdn.microsoft.com/en-us/library/windows/desktop/bb509632(v=vs.85).aspx + */ struct ToneMapperParams { float whiteScale; diff --git a/Source/RenderPasses/ToneMapper/ToneMapping.ps.slang b/Source/RenderPasses/ToneMapper/ToneMapping.ps.slang index 3dee4fc7c..aa6230733 100644 --- a/Source/RenderPasses/ToneMapper/ToneMapping.ps.slang +++ b/Source/RenderPasses/ToneMapper/ToneMapping.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -74,7 +74,7 @@ float3 toneMapReinhardModified(float3 color) float3 toneMapHejiHableAlu(float3 color) { color = max(float(0).rrr, color - 0.004); - color = (color*(6.2 * color + 0.5)) / (color * (6.2 * color + 1.7) + 0.06); + color = (color * (6.2 * color + 0.5)) / (color * (6.2 * color + 1.7) + 0.06); // Result includes sRGB conversion return pow(color, float3(2.2)); @@ -91,7 +91,7 @@ float3 applyUc2Curve(float3 color) float E = 0.01; // Toe Numerator float F = 0.3; // Toe Denominator - color = ((color * (A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-(E/F); + color = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - (E / F); return color; } @@ -117,7 +117,7 @@ float3 toneMapAces(float3 color) float D = 0.59; float E = 0.14; - color = saturate((color*(A*color+B))/(color*(C*color+D)+E)); + color = saturate((color * (A * color + B)) / (color * (C * color + D) + E)); return color; } @@ -125,24 +125,24 @@ float3 toneMap(float3 color) { switch (kOperator) { - case ToneMapperOperator::Linear: - return toneMapLinear(color); - case ToneMapperOperator::Reinhard: - return toneMapReinhard(color); - case ToneMapperOperator::ReinhardModified: - return toneMapReinhardModified(color); - case ToneMapperOperator::HejiHableAlu: - return toneMapHejiHableAlu(color); - case ToneMapperOperator::HableUc2: - return toneMapHableUc2(color); - case ToneMapperOperator::Aces: - return toneMapAces(color); - default: - return color; + case ToneMapperOperator::Linear: + return toneMapLinear(color); + case ToneMapperOperator::Reinhard: + return toneMapReinhard(color); + case ToneMapperOperator::ReinhardModified: + return toneMapReinhardModified(color); + case ToneMapperOperator::HejiHableAlu: + return toneMapHejiHableAlu(color); + case ToneMapperOperator::HableUc2: + return toneMapHableUc2(color); + case ToneMapperOperator::Aces: + return toneMapAces(color); + default: + return color; } } -float4 main(float2 texC : TEXCOORD) : SV_TARGET0 +float4 main(float2 texC: TEXCOORD) : SV_TARGET0 { float4 color = gColorTex.Sample(gColorSampler, texC); float3 finalColor = color.rgb; diff --git a/Source/RenderPasses/Utils/Composite/Composite.cpp b/Source/RenderPasses/Utils/Composite/Composite.cpp index 5c0fe690a..e8785ee40 100644 --- a/Source/RenderPasses/Utils/Composite/Composite.cpp +++ b/Source/RenderPasses/Utils/Composite/Composite.cpp @@ -30,35 +30,38 @@ namespace { - const std::string kShaderFile("RenderPasses/Utils/Composite/Composite.cs.slang"); +const std::string kShaderFile("RenderPasses/Utils/Composite/Composite.cs.slang"); - const std::string kInputA = "A"; - const std::string kInputB = "B"; - const std::string kOutput = "out"; +const std::string kInputA = "A"; +const std::string kInputB = "B"; +const std::string kOutput = "out"; - const std::string kMode = "mode"; - const std::string kScaleA = "scaleA"; - const std::string kScaleB = "scaleB"; - const std::string kOutputFormat = "outputFormat"; +const std::string kMode = "mode"; +const std::string kScaleA = "scaleA"; +const std::string kScaleB = "scaleB"; +const std::string kOutputFormat = "outputFormat"; - const Gui::DropdownList kModeList = - { - { (uint32_t)Composite::Mode::Add, "Add" }, - { (uint32_t)Composite::Mode::Multiply, "Multiply" }, - }; -} +const Gui::DropdownList kModeList = { + {(uint32_t)Composite::Mode::Add, "Add"}, + {(uint32_t)Composite::Mode::Multiply, "Multiply"}, +}; +} // namespace -Composite::Composite(ref pDevice, const Properties& props) - : RenderPass(pDevice) +Composite::Composite(ref pDevice, const Properties& props) : RenderPass(pDevice) { // Parse dictionary. for (const auto& [key, value] : props) { - if (key == kMode) mMode = value; - else if (key == kScaleA) mScaleA = value; - else if (key == kScaleB) mScaleB = value; - else if (key == kOutputFormat) mOutputFormat = value; - else logWarning("Unknown property '{}' in Composite pass properties.", key); + if (key == kMode) + mMode = value; + else if (key == kScaleA) + mScaleA = value; + else if (key == kScaleB) + mScaleB = value; + else if (key == kOutputFormat) + mOutputFormat = value; + else + logWarning("Unknown property '{}' in Composite pass properties.", key); } // Create resources. @@ -71,7 +74,8 @@ Properties Composite::getProperties() const props[kMode] = mMode; props[kScaleA] = mScaleA; props[kScaleB] = mScaleB; - if (mOutputFormat != ResourceFormat::Unknown) props[kOutputFormat] = mOutputFormat; + if (mOutputFormat != ResourceFormat::Unknown) + props[kOutputFormat] = mOutputFormat; return props; } diff --git a/Source/RenderPasses/Utils/Composite/Composite.cs.slang b/Source/RenderPasses/Utils/Composite/Composite.cs.slang index 8285c68aa..71a366256 100644 --- a/Source/RenderPasses/Utils/Composite/Composite.cs.slang +++ b/Source/RenderPasses/Utils/Composite/Composite.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,8 +27,9 @@ **************************************************************************/ #include "CompositeMode.slangh" -/** Simple composite pass that blends two buffers together. -*/ +/** + * Simple composite pass that blends two buffers together. + */ cbuffer CB { @@ -55,20 +56,21 @@ RWTexture2D output; #endif [numthreads(16, 16, 1)] -void main(uint3 dispatchThreadId : SV_DispatchThreadID) +void main(uint3 dispatchThreadId: SV_DispatchThreadID) { const uint2 pixel = dispatchThreadId.xy; - if (any(pixel >= frameDim)) return; + if (any(pixel >= frameDim)) + return; float4 result = float4(0.f); #if !defined(COMPOSITE_MODE) - #error COMPOSITE_MODE is undefined +#error COMPOSITE_MODE is undefined #elif COMPOSITE_MODE == COMPOSITE_MODE_ADD result = (scaleA * A[pixel]) + (scaleB * B[pixel]); #elif COMPOSITE_MODE == COMPOSITE_MODE_MULTIPLY result = (scaleA * A[pixel]) * (scaleB * B[pixel]); #else - #error COMPOSITE_MODE unknown +#error COMPOSITE_MODE unknown #endif #if OUTPUT_FORMAT != OUTPUT_FORMAT_FLOAT diff --git a/Source/RenderPasses/Utils/Composite/Composite.h b/Source/RenderPasses/Utils/Composite/Composite.h index c20b9edf8..d66eaf555 100644 --- a/Source/RenderPasses/Utils/Composite/Composite.h +++ b/Source/RenderPasses/Utils/Composite/Composite.h @@ -32,30 +32,35 @@ using namespace Falcor; -/** Simple composite pass that blends two buffers together. - - Each input A and B can be independently scaled, and the output C - is computed C = A B, where the blend operation is configurable. - If the output buffer C is of integer format, floating point values - are converted to integers using round-to-nearest-even. -*/ +/** + * Simple composite pass that blends two buffers together. + * + * Each input A and B can be independently scaled, and the output C + * is computed C = A B, where the blend operation is configurable. + * If the output buffer C is of integer format, floating point values + * are converted to integers using round-to-nearest-even. + */ class Composite : public RenderPass { public: FALCOR_PLUGIN_CLASS(Composite, "Composite", "Composite pass."); - /** Composite modes. - */ + /** + * Composite modes. + */ enum class Mode { Add, Multiply, }; - FALCOR_ENUM_INFO(Mode, { - { Mode::Add, "Add" }, - { Mode::Multiply, "Multiply" }, - }); + FALCOR_ENUM_INFO( + Mode, + { + {Mode::Add, "Add"}, + {Mode::Multiply, "Multiply"}, + } + ); static ref create(ref pDevice, const Properties& props) { return make_ref(pDevice, props); } @@ -70,13 +75,13 @@ class Composite : public RenderPass private: DefineList getDefines() const; - uint2 mFrameDim = { 0, 0 }; - Mode mMode = Mode::Add; - float mScaleA = 1.f; - float mScaleB = 1.f; - ResourceFormat mOutputFormat = ResourceFormat::RGBA32Float; + uint2 mFrameDim = {0, 0}; + Mode mMode = Mode::Add; + float mScaleA = 1.f; + float mScaleB = 1.f; + ResourceFormat mOutputFormat = ResourceFormat::RGBA32Float; - ref mCompositePass; + ref mCompositePass; }; FALCOR_ENUM_REGISTER(Composite::Mode); diff --git a/Source/RenderPasses/Utils/Composite/CompositeMode.slangh b/Source/RenderPasses/Utils/Composite/CompositeMode.slangh index 9c787da06..b5571f1e8 100644 --- a/Source/RenderPasses/Utils/Composite/CompositeMode.slangh +++ b/Source/RenderPasses/Utils/Composite/CompositeMode.slangh @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -28,9 +28,9 @@ #pragma once // Type defines shared between host and device. -#define COMPOSITE_MODE_ADD 0 +#define COMPOSITE_MODE_ADD 0 #define COMPOSITE_MODE_MULTIPLY 1 -#define OUTPUT_FORMAT_FLOAT 0 -#define OUTPUT_FORMAT_UINT 1 -#define OUTPUT_FORMAT_SINT 2 +#define OUTPUT_FORMAT_FLOAT 0 +#define OUTPUT_FORMAT_UINT 1 +#define OUTPUT_FORMAT_SINT 2 diff --git a/Source/RenderPasses/Utils/CrossFade/CrossFade.cpp b/Source/RenderPasses/Utils/CrossFade/CrossFade.cpp index 49b33b25a..08be5dc26 100644 --- a/Source/RenderPasses/Utils/CrossFade/CrossFade.cpp +++ b/Source/RenderPasses/Utils/CrossFade/CrossFade.cpp @@ -30,31 +30,36 @@ namespace { - const std::string kShaderFile("RenderPasses/Utils/CrossFade/CrossFade.cs.slang"); +const std::string kShaderFile("RenderPasses/Utils/CrossFade/CrossFade.cs.slang"); - const std::string kInputA = "A"; - const std::string kInputB = "B"; - const std::string kOutput = "out"; +const std::string kInputA = "A"; +const std::string kInputB = "B"; +const std::string kOutput = "out"; - const std::string kOutputFormat = "outputFormat"; - const std::string kEnableAutoFade = "enableAutoFade"; - const std::string kWaitFrameCount = "waitFrameCount"; - const std::string kFadeFrameCount = "fadeFrameCount"; - const std::string kFadeFactor = "fadeFactor"; -} +const std::string kOutputFormat = "outputFormat"; +const std::string kEnableAutoFade = "enableAutoFade"; +const std::string kWaitFrameCount = "waitFrameCount"; +const std::string kFadeFrameCount = "fadeFrameCount"; +const std::string kFadeFactor = "fadeFactor"; +} // namespace -CrossFade::CrossFade(ref pDevice, const Properties& props) - : RenderPass(pDevice) +CrossFade::CrossFade(ref pDevice, const Properties& props) : RenderPass(pDevice) { // Parse dictionary. for (const auto& [key, value] : props) { - if (key == kOutputFormat) mOutputFormat = value; - else if (key == kEnableAutoFade) mEnableAutoFade = value; - else if (key == kWaitFrameCount) mWaitFrameCount = value; - else if (key == kFadeFrameCount) mFadeFrameCount = value; - else if (key == kFadeFactor) mFadeFactor = value; - else logWarning("Unknown property '{}' in CrossFade pass properties.", key); + if (key == kOutputFormat) + mOutputFormat = value; + else if (key == kEnableAutoFade) + mEnableAutoFade = value; + else if (key == kWaitFrameCount) + mWaitFrameCount = value; + else if (key == kFadeFrameCount) + mFadeFrameCount = value; + else if (key == kFadeFactor) + mFadeFactor = value; + else + logWarning("Unknown property '{}' in CrossFade pass properties.", key); } // Create resources. @@ -64,7 +69,8 @@ CrossFade::CrossFade(ref pDevice, const Properties& props) Properties CrossFade::getProperties() const { Properties props; - if (mOutputFormat != ResourceFormat::Unknown) props[kOutputFormat] = mOutputFormat; + if (mOutputFormat != ResourceFormat::Unknown) + props[kOutputFormat] = mOutputFormat; props[kEnableAutoFade] = mEnableAutoFade; props[kWaitFrameCount] = mWaitFrameCount; props[kFadeFrameCount] = mFadeFrameCount; @@ -100,7 +106,8 @@ void CrossFade::execute(RenderContext* pRenderContext, const RenderData& renderD auto refreshFlags = dict.getValue(kRenderPassRefreshFlags, RenderPassRefreshFlags::None); // If any refresh flag is set, we reset frame accumulation. - if (refreshFlags != RenderPassRefreshFlags::None) shouldReset = true; + if (refreshFlags != RenderPassRefreshFlags::None) + shouldReset = true; // Reset accumulation upon all scene changes, except camera jitter and history changes. // TODO: Add UI options to select which changes should trigger reset @@ -115,7 +122,8 @@ void CrossFade::execute(RenderContext* pRenderContext, const RenderData& renderD { auto excluded = Camera::Changes::Jitter | Camera::Changes::History; auto cameraChanges = mpScene->getCamera()->getChanges(); - if ((cameraChanges & ~excluded) != Camera::Changes::None) shouldReset = true; + if ((cameraChanges & ~excluded) != Camera::Changes::None) + shouldReset = true; } if (is_set(sceneUpdates, Scene::UpdateFlags::SDFGeometryChanged)) { @@ -132,9 +140,8 @@ void CrossFade::execute(RenderContext* pRenderContext, const RenderData& renderD mMixFrame++; } - float mix = mEnableAutoFade ? - math::clamp((float(mMixFrame) - mWaitFrameCount) / mFadeFrameCount, 0.f, 1.f) : - math::clamp(mFadeFactor, 0.f, 1.f); + float mix = mEnableAutoFade ? math::clamp((float(mMixFrame) - mWaitFrameCount) / mFadeFrameCount, 0.f, 1.f) + : math::clamp(mFadeFactor, 0.f, 1.f); mScaleA = 1.f - mix; mScaleB = mix; @@ -143,7 +150,7 @@ void CrossFade::execute(RenderContext* pRenderContext, const RenderData& renderD const auto& pOutput = renderData.getTexture(kOutput); FALCOR_ASSERT(pOutput); mOutputFormat = pOutput->getFormat(); - checkInvariant(!isIntegerFormat(mOutputFormat), "Output cannot be an integer format."); + FALCOR_CHECK(!isIntegerFormat(mOutputFormat), "Output cannot be an integer format."); // Bind resources. auto var = mpFadePass->getRootVar(); diff --git a/Source/RenderPasses/Utils/CrossFade/CrossFade.cs.slang b/Source/RenderPasses/Utils/CrossFade/CrossFade.cs.slang index 401b3394f..2bcf75707 100644 --- a/Source/RenderPasses/Utils/CrossFade/CrossFade.cs.slang +++ b/Source/RenderPasses/Utils/CrossFade/CrossFade.cs.slang @@ -40,10 +40,11 @@ Texture2D B; RWTexture2D output; [numthreads(16, 16, 1)] -void main(uint3 dispatchThreadId : SV_DispatchThreadID) +void main(uint3 dispatchThreadId: SV_DispatchThreadID) { const uint2 pixel = dispatchThreadId.xy; - if (any(pixel >= frameDim)) return; + if (any(pixel >= frameDim)) + return; float4 result = (scaleA * A[pixel]) + (scaleB * B[pixel]); diff --git a/Source/RenderPasses/Utils/CrossFade/CrossFade.h b/Source/RenderPasses/Utils/CrossFade/CrossFade.h index b0ad694ac..77c84a514 100644 --- a/Source/RenderPasses/Utils/CrossFade/CrossFade.h +++ b/Source/RenderPasses/Utils/CrossFade/CrossFade.h @@ -31,8 +31,9 @@ using namespace Falcor; -/** Simple pass for time-dependent fading between two buffers. -*/ +/** + * Simple pass for time-dependent fading between two buffers. + */ class CrossFade : public RenderPass { public: @@ -50,17 +51,18 @@ class CrossFade : public RenderPass virtual void renderUI(Gui::Widgets& widget) override; private: - uint2 mFrameDim = { 0, 0 }; - float mScaleA = 1.f; - float mScaleB = 1.f; - ResourceFormat mOutputFormat = ResourceFormat::RGBA32Float; + uint2 mFrameDim = {0, 0}; + float mScaleA = 1.f; + float mScaleB = 1.f; + ResourceFormat mOutputFormat = ResourceFormat::RGBA32Float; - ref mpFadePass; + ref mpFadePass; - ref mpScene; - uint32_t mMixFrame = 0; - bool mEnableAutoFade = true; - uint32_t mWaitFrameCount = 10; - uint32_t mFadeFrameCount = 100; - float mFadeFactor = 0.5f; ///< Fixed fade factor (t) used when auto-fade is disabled. The output is: (1-t)*A + t*B. + ref mpScene; + uint32_t mMixFrame = 0; + bool mEnableAutoFade = true; + uint32_t mWaitFrameCount = 10; + uint32_t mFadeFrameCount = 100; + /// Fixed fade factor (t) used when auto-fade is disabled. The output is: (1-t)*A + t*B. + float mFadeFactor = 0.5f; }; diff --git a/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.cpp b/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.cpp index ba6422264..a7e151c3e 100644 --- a/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.cpp +++ b/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.cpp @@ -30,14 +30,14 @@ namespace { - const char kSrc[] = "src"; - const char kDst[] = "dst"; +const char kSrc[] = "src"; +const char kDst[] = "dst"; - const char kKernelWidth[] = "kernelWidth"; - const char kSigma[] = "sigma"; +const char kKernelWidth[] = "kernelWidth"; +const char kSigma[] = "sigma"; - const char kShaderFilename[] = "RenderPasses/Utils/GaussianBlur/GaussianBlur.ps.slang"; -} +const char kShaderFilename[] = "RenderPasses/Utils/GaussianBlur/GaussianBlur.ps.slang"; +} // namespace void GaussianBlur::registerBindings(pybind11::module& m) { @@ -46,19 +46,22 @@ void GaussianBlur::registerBindings(pybind11::module& m) pass.def_property(kSigma, &GaussianBlur::getSigma, &GaussianBlur::setSigma); } -GaussianBlur::GaussianBlur(ref pDevice, const Properties& props) - : RenderPass(pDevice) +GaussianBlur::GaussianBlur(ref pDevice, const Properties& props) : RenderPass(pDevice) { mpFbo = Fbo::create(mpDevice); Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Point).setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - mpSampler = Sampler::create(mpDevice, samplerDesc); + samplerDesc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Point) + .setAddressingMode(TextureAddressingMode::Clamp, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); + mpSampler = mpDevice->createSampler(samplerDesc); for (const auto& [key, value] : props) { - if (key == kKernelWidth) mKernelWidth = value; - else if (key == kSigma) mSigma = value; - else logWarning("Unknown property '{}' in a GaussianBlur properties.", key); + if (key == kKernelWidth) + mKernelWidth = value; + else if (key == kSigma) + mSigma = value; + else + logWarning("Unknown property '{}' in a GaussianBlur properties.", key); } } @@ -86,9 +89,8 @@ RenderPassReflection GaussianBlur::reflect(const CompileData& compileData) uint32_t srcMipCount = edge->getMipCount(); uint32_t srcArraySize = edge->getArraySize(); - auto formatField = [=](RenderPassReflection::Field& f) { - return f.format(srcFormat).resourceType(srcType, srcWidth, srcHeight, srcDepth, srcSampleCount, srcMipCount, srcArraySize); - }; + auto formatField = [=](RenderPassReflection::Field& f) + { return f.format(srcFormat).resourceType(srcType, srcWidth, srcHeight, srcDepth, srcSampleCount, srcMipCount, srcArraySize); }; formatField(reflector.addInput(kSrc, "input image to be blurred")); formatField(reflector.addOutput(kDst, "output blurred image")); @@ -104,12 +106,13 @@ RenderPassReflection GaussianBlur::reflect(const CompileData& compileData) void GaussianBlur::compile(RenderContext* pRenderContext, const CompileData& compileData) { - if (!mReady) throw RuntimeError("GaussianBlur: Missing incoming reflection information"); + FALCOR_CHECK(mReady, "GaussianBlur: Missing incoming reflection information"); uint32_t arraySize = compileData.connectedResources.getField(kSrc)->getArraySize(); DefineList defines; defines.add("_KERNEL_WIDTH", std::to_string(mKernelWidth)); - if (arraySize > 1) defines.add("_USE_TEX2D_ARRAY"); + if (arraySize > 1) + defines.add("_USE_TEX2D_ARRAY"); uint32_t layerMask = (arraySize > 1) ? ((1 << arraySize) - 1) : 0; defines.add("_HORIZONTAL_BLUR"); @@ -153,10 +156,9 @@ void GaussianBlur::createTmpFbo(const Texture* pSrc) if (createFbo == false) { - createFbo = (pSrc->getWidth() != mpTmpFbo->getWidth()) || - (pSrc->getHeight() != mpTmpFbo->getHeight()) || - (srcFormat != mpTmpFbo->getColorTexture(0)->getFormat()) || - pSrc->getArraySize() != mpTmpFbo->getColorTexture(0)->getArraySize(); + createFbo = (pSrc->getWidth() != mpTmpFbo->getWidth()) || (pSrc->getHeight() != mpTmpFbo->getHeight()) || + (srcFormat != mpTmpFbo->getColorTexture(0)->getFormat()) || + pSrc->getArraySize() != mpTmpFbo->getColorTexture(0)->getArraySize(); } if (createFbo) @@ -169,8 +171,10 @@ void GaussianBlur::createTmpFbo(const Texture* pSrc) void GaussianBlur::renderUI(Gui::Widgets& widget) { - if (widget.var("Kernel Width", (int&)mKernelWidth, 1, 15, 2)) setKernelWidth(mKernelWidth); - if (widget.slider("Sigma", mSigma, 0.001f, mKernelWidth / 2.f)) setSigma(mSigma); + if (widget.var("Kernel Width", (int&)mKernelWidth, 1, 15, 2)) + setKernelWidth(mKernelWidth); + if (widget.slider("Sigma", mSigma, 0.001f, mKernelWidth / 2.f)) + setSigma(mSigma); } void GaussianBlur::setKernelWidth(uint32_t kernelWidth) @@ -188,7 +192,7 @@ void GaussianBlur::setSigma(float sigma) float getCoefficient(float sigma, float kernelWidth, float x) { float sigmaSquared = sigma * sigma; - float p = -(x*x) / (2 * sigmaSquared); + float p = -(x * x) / (2 * sigmaSquared); float e = std::exp(p); float a = 2 * (float)M_PI * sigmaSquared; @@ -206,7 +210,7 @@ void GaussianBlur::updateKernel() sum += (i == 0) ? weights[i] : 2 * weights[i]; } - ref pBuf = Buffer::createTyped(mpDevice, mKernelWidth, Resource::BindFlags::ShaderResource); + ref pBuf = mpDevice->createTypedBuffer(mKernelWidth, ResourceBindFlags::ShaderResource); for (uint32_t i = 0; i <= center; i++) { diff --git a/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.ps.slang b/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.ps.slang index 8d8d72231..837f87075 100644 --- a/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.ps.slang +++ b/Source/RenderPasses/Utils/GaussianBlur/GaussianBlur.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,9 +26,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #ifdef _USE_TEX2D_ARRAY - Texture2DArray gSrcTex; +Texture2DArray gSrcTex; #else - texture2D gSrcTex; +texture2D gSrcTex; #endif SamplerState gSampler; @@ -58,15 +58,17 @@ float4 blur(float2 texC) #error Please define either _HORIZONTAL_BLUR or _VERTICAL_BLUR #endif - const int2 offset = -(_KERNEL_WIDTH / 2) * dir; + const int2 offset = -(_KERNEL_WIDTH / 2) * dir; - float4 c = float4(0,0,0,0); - $for(i in Range(_KERNEL_WIDTH)) + float4 c = float4(0, 0, 0, 0); + + [ForceUnroll] + for (int i = 0; i < _KERNEL_WIDTH; i++) { #ifdef _USE_TEX2D_ARRAY - c += gSrcTex.SampleLevel(gSampler, float3(texC, arrayIndex), 0, offset + i*dir)*weights[i]; + c += gSrcTex.SampleLevel(gSampler, float3(texC, arrayIndex), 0, offset + i * dir) * weights[i]; #else - c += gSrcTex.SampleLevel(gSampler, texC, 0, offset + i*dir)*weights[i]; + c += gSrcTex.SampleLevel(gSampler, texC, 0, offset + i * dir) * weights[i]; #endif } return c; diff --git a/Source/RenderPasses/WARDiffPathTracer/CMakeLists.txt b/Source/RenderPasses/WARDiffPathTracer/CMakeLists.txt new file mode 100644 index 000000000..a2af2e3f6 --- /dev/null +++ b/Source/RenderPasses/WARDiffPathTracer/CMakeLists.txt @@ -0,0 +1,15 @@ +add_plugin(WARDiffPathTracer) + +target_sources(WARDiffPathTracer PRIVATE + Params.slang + PTUtils.slang + StaticParams.slang + WARDiffPathTracer.cpp + WARDiffPathTracer.h + WARDiffPathTracer.rt.slang + WarpedAreaReparam.slang +) + +target_copy_shaders(WARDiffPathTracer RenderPasses/WARDiffPathTracer) + +target_source_group(WARDiffPathTracer "RenderPasses") diff --git a/Source/RenderPasses/WARDiffPathTracer/PTUtils.slang b/Source/RenderPasses/WARDiffPathTracer/PTUtils.slang new file mode 100644 index 000000000..e3f82cf41 --- /dev/null +++ b/Source/RenderPasses/WARDiffPathTracer/PTUtils.slang @@ -0,0 +1,344 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Scene/SceneDefines.slangh" +#include "Utils/Math/MathConstants.slangh" + +import Rendering.Lights.EmissiveLightSampler; +import Rendering.Lights.EmissiveLightSamplerHelpers; +import Rendering.Lights.LightHelpers; +import Scene.Raytracing; +import Scene.Intersection; +import Scene.RaytracingInline; +import Utils.Math.MathHelpers; +import Utils.Geometry.GeometryHelpers; +import Utils.Sampling.SampleGenerator; +import Utils.Debug.PixelDebug; +import DiffRendering.SharedTypes; +import DiffRendering.DiffSceneIO; +import DiffRendering.DiffSceneQuery; + +__exported import StaticParams; +__exported import Params; +import WarpedAreaReparam; + +struct PathData : IDifferentiable +{ + float3 radiance; + float3 thp; + uint length; + bool terminated; + + // For MIS. + no_diff float3 normal; + no_diff float pdf; + + [Differentiable] + __init() + { + this.radiance = float3(0.f); + this.thp = float3(1.f); + this.length = 0; + this.terminated = false; + this.normal = float3(0.f); + this.pdf = 0.f; + } +}; + +struct LightSample +{ + float3 Li; ///< Incident radiance at the shading point (unshadowed). This is already divided by the pdf. + float pdf; ///< Pdf with respect to solid angle at the shading point. + float3 origin; ///< Ray origin for visibility evaluation (offseted to avoid self-intersection). + float distance; ///< Ray distance for visibility evaluation (shortened to avoid self-intersection). + float3 dir; ///< Ray direction for visibility evaluation (normalized). + + // For differentiable evaluation. + float3 lightPos; + float3 lightNormal; + + Ray getVisibilityRay() { return Ray(origin, dir, 0.f, distance); } +}; + +float evalMIS(float n0, float p0, float n1, float p1) +{ + // Power two heuristic + float q0 = (n0 * p0) * (n0 * p0); + float q1 = (n1 * p1) * (n1 * p1); + return q0 / (q0 + q1); +} + +[Differentiable] +[PreferRecompute] +ShadingData loadShadingData( + SceneQueryAD sceneQuery, + HitInfo hit, + IntersectionAD isect, + const float3 rayOrigin, + const float3 rayDir, + const ITextureSampler lod +) +{ + VertexData v = {}; + uint materialID = {}; + + // We only support triangle hits for now. + if (hit.getType() == HitType::Triangle) + { + const TriangleHit triangleHit = hit.getTriangleHit(); + v = gScene.getVertexData(triangleHit); + materialID = gScene.getMaterialID(isect.instanceID); + } + + ShadingData sd = no_diff gScene.materials.prepareShadingData(v, materialID, -rayDir, lod); + + // Overwrite some fields to enable auto-diff. + sd.V = -rayDir; + bool valid; + sd.frame = ShadingFrame::createSafe(isect.normalW, v.tangentW, valid); + + // Set offset value for the material gradient propagation. + sd.materialGradOffset = materialID * DiffMaterialData::kMaterialParamCount; + + return sd; +} + +[Differentiable] +[PreferRecompute] +IMaterialInstance getDiffMaterialInstance(out DiffMaterialData diffData, const ShadingData sd, const ITextureSampler lod) +{ + let material = gScene.materials.getMaterial(sd.materialID); + let mi = material.setupDiffMaterialInstance(diffData, gScene.materials, sd, lod); + return mi; +} + +[Differentiable] +[PreferRecompute] +bool generateScatterRay( + SceneQueryAD sceneQuery, + ShadingData sd, + IMaterialInstance mi, + DiffMaterialData diffData, + IntersectionAD isect, + inout RayAD ray, + inout PathData pathData, + inout SampleGenerator sg, + bool shouldReparameterize = true +) +{ + // Sample material. + BSDFSample bsdfSample = {}; + if (mi.sample(sd, sg, bsdfSample, kUseBSDFSampling)) + { + ray.direction = bsdfSample.wo; + ray.origin = computeRayOrigin(isect.posW, ((sd.frontFacing) ? sd.faceN : -sd.faceN)); + + if (shouldReparameterize) + { + float weight = reparameterizeRay(sceneQuery, ray, sg); + pathData.thp *= weight; + } + + float3 weight = mi.evalAD(diffData, sd, ray.direction, sg) / bsdfSample.pdf; + pathData.thp *= weight; + + // Save normal and pdf for MIS. + pathData.normal = sd.getOrientedFaceNormal(); + pathData.pdf = bsdfSample.pdf; + + return any(weight > 0.f); + } + + return false; +} + +// Generate a light sample on the emissive geometry. +// Assume the area lights are static (we don't allow differentiation of dynamic area lights for now). +[PreferRecompute] +bool generateEmissiveSample( + IntersectionAD isect, + EmissiveLightSampler emissiveSampler, + bool upperHemisphere, + inout SampleGenerator sg, + out LightSample ls +) +{ + ls = {}; // Default initialization to avoid divergence at returns. + if (!kUseEmissiveLights) + return false; + + TriangleLightSample tls; + if (!emissiveSampler.sampleLight(isect.posW, isect.normalW, upperHemisphere, sg, tls)) + return false; + + // Setup returned sample. + ls.Li = tls.pdf > 0.f ? tls.Le / tls.pdf : float3(0); + ls.pdf = tls.pdf; + // Offset shading and light position to avoid self-intersection. + float3 lightPos = computeRayOrigin(tls.posW, tls.normalW); + ls.origin = computeRayOrigin(isect.posW, isect.normalW); + float3 toLight = lightPos - ls.origin; + ls.distance = length(toLight); + ls.dir = normalize(toLight); + + ls.lightPos = tls.posW; + ls.lightNormal = tls.normalW; + + return any(ls.Li > 0.f); +} + +[Differentiable] +[PreferRecompute] +void computeNEE( + SceneQueryAD sceneQuery, + ShadingData sd, + IMaterialInstance mi, + DiffMaterialData diffData, + IntersectionAD isect, + LightSample ls, + inout PathData pathData, + inout SampleGenerator sg, + bool shouldReparameterize = true +) +{ + float3 wo = ls.lightPos - isect.posW; + float dist = length(wo); + wo /= dist; + + RayAD ray = RayAD(computeRayOrigin(isect.posW, ((sd.frontFacing) ? sd.faceN : -sd.faceN)), wo, sceneQuery.gradInfo.pixel); + + float3 thp = pathData.thp; + + if (shouldReparameterize) + { + float weight = reparameterizeRay(sceneQuery, ray, sg); + thp *= weight; + } + + // Note: ls.Li and ls.pdf are already w.r.t. the solid angle. + float areaToSolidAngle = (dist * dist) / dot(ls.lightNormal, -ray.direction); + float3 bsdfValue = mi.evalAD(diffData, sd, ray.direction, sg); + float3 weight = ls.Li * bsdfValue * areaToSolidAngle / detach(areaToSolidAngle); + + if (kUseMIS) + { + float scatterPdf = mi.evalPdf(sd, ls.dir, kUseBSDFSampling); + weight *= no_diff evalMIS(1, ls.pdf, 1, scatterPdf); + } + + pathData.radiance += thp * weight; +} + +[Differentiable] +[PreferRecompute] +void handleHit( + SceneQueryAD sceneQuery, + IntersectionAD isect, + EmissiveLightSampler emissiveSampler, + inout PathData pathData, + inout RayAD ray, + inout SampleGenerator sg, + bool shouldReparameterize = true +) +{ + let lod = ExplicitLodTextureSampler(0.f); + + TriangleHit triHit = { isect.instanceID, isect.triangleID, isect.barycentrics.y, isect.barycentrics.z }; + HitInfo hit = HitInfo(triHit); + + // Load shading data. + ShadingData sd = loadShadingData(sceneQuery, hit, isect, ray.origin, ray.direction, lod); + + // Create differentiable material instance. + DiffMaterialData diffData; + let mi = getDiffMaterialInstance(diffData, sd, lod); + + // Add emitted light. + const bool isPrimaryHit = pathData.length == 0; + if (isPrimaryHit || kUseEmissiveLights && (!kUseNEE || kUseMIS)) + { + float misWeight = 1.f; + + if (!isPrimaryHit && kUseEmissiveLights && kUseNEE && kUseMIS) + { + // Prepare hit point struct with data needed for emissive light PDF evaluation. + TriangleLightHit lightHit; + lightHit.triangleIndex = gScene.lightCollection.getTriangleIndex(triHit.instanceID, triHit.primitiveIndex); + lightHit.posW = sd.posW; + lightHit.normalW = sd.getOrientedFaceNormal(); + + // Evaluate PDF at the hit, had it been generated with light sampling. + // Emissive light samplers have an option to exclusively sample the upper hemisphere. + bool upperHemisphere = true; + float lightPdf = no_diff emissiveSampler.evalPdf(detach(ray.origin), pathData.normal, upperHemisphere, lightHit); + + // Compute MIS weight by combining this with BSDF sampling. + // Note we can assume path.pdf > 0.f since we shouldn't have got here otherwise. + misWeight = no_diff evalMIS(1, pathData.pdf, 1, lightPdf); + } + + // TODO: support differentiable for the emission of lights. + pathData.radiance += pathData.thp * mi.getProperties(sd).emission * misWeight; + } + + // Check whether to terminate based on max depth. + if (pathData.length >= kMaxBounces + 1) + { + pathData.terminated = true; + return; + } + + // Compute NEE. Assume we only have area lights for now. + if (kUseNEE) + { + LightSample ls = {}; + bool validSample = generateEmissiveSample(isect, emissiveSampler, true, sg, ls); + if (validSample) + { + Ray shadowRay = ls.getVisibilityRay(); + bool visible = sceneQuery.traceVisibilityRay(shadowRay); + + if (visible) + { + computeNEE(sceneQuery, sd, mi, diffData, isect, ls, pathData, sg, shouldReparameterize); + } + } + } + + // Compute ray origin for new rays spawned from the hit. + float3 rayOrigin; + + // Generate scatter ray for the next path segment. + // The raygen shader will continue the path based on the returned payload. + if (!generateScatterRay(sceneQuery, sd, mi, diffData, isect, ray, pathData, sg, shouldReparameterize)) + { + pathData.terminated = true; + return; + } + + pathData.length++; +} diff --git a/Source/RenderPasses/WARDiffPathTracer/Params.slang b/Source/RenderPasses/WARDiffPathTracer/Params.slang new file mode 100644 index 000000000..35c3fe6c3 --- /dev/null +++ b/Source/RenderPasses/WARDiffPathTracer/Params.slang @@ -0,0 +1,79 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include "Utils/HostDeviceShared.slangh" + +BEGIN_NAMESPACE_FALCOR + +// Import static specialization constants. +#ifndef HOST_CODE +__exported import RenderPasses.WARDiffPathTracer.StaticParams; +__exported import Utils.Math.BitTricks; +#endif + +// Define path configuration limits. + +/// Maximum supported sample count. We can use tiling to support large sample counts if needed. +static const uint kMaxSamplesPerPixel = 16; +/// Maximum supported frame dimension in pixels along x or y. We can increase the bit allocation if needed. +static const uint kMaxFrameDimension = 4096; +/// Maximum supported number of bounces per bounce category (value 255 is reserved for internal use). +/// The resulting path length may be longer than this. +static const uint kBounceLimit = 254; +/// Maximum number of shadow rays per path vertex for next-event estimation. +static const uint kMaxLightSamplesPerVertex = 8; + +/** + * Path tracer parameters shared between host/device code. + */ +struct WARDiffPathTracerParams +{ + /// Use fixed random seed. This is useful for debugging. + int useFixedSeed = false; + /// The seed to use when 'useFixedSeed' is enabled. + uint fixedSeed = 1; + /// Threshold for asserting on NaNs. + float assertThreshold = 1e9f; + /// A runtime varialbe to control running the backward pass or the primal pass. + uint runBackward = 1; + + // Runtime values + + /// Frame dimension in pixels. + uint2 frameDim = { 0, 0 }; + /// Number of screen-tiles. Screen tiles may extend outside the frame. + uint2 screenTiles = { 0, 0 }; + + /// Frames rendered. This is used as random seed. + uint frameCount = 0; + /// Random seed. This will get updated from the host depending on settings. + uint seed = 0; + uint2 _pad0; +}; + +END_NAMESPACE_FALCOR diff --git a/Source/RenderPasses/WARDiffPathTracer/StaticParams.slang b/Source/RenderPasses/WARDiffPathTracer/StaticParams.slang new file mode 100644 index 000000000..f86e45363 --- /dev/null +++ b/Source/RenderPasses/WARDiffPathTracer/StaticParams.slang @@ -0,0 +1,53 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#ifndef SAMPLES_PER_PIXEL +#error RenderPasses/WARDiffPathTracer/StaticParams.slang is missing defines +#endif + +__exported import DiffRendering.SharedTypes; + +static const uint kSamplesPerPixel = SAMPLES_PER_PIXEL; +static const uint kMaxBounces = MAX_BOUNCES; + +static const DiffMode kDiffMode = DiffMode(DIFF_MODE); + +static const bool kUseBSDFSampling = USE_BSDF_SAMPLING; +static const bool kUseNEE = USE_NEE; +static const bool kUseMIS = USE_MIS; + +static const bool kUseWAR = USE_WAR; +static const uint kAuxSampleCount = AUX_SAMPLE_COUNT; +static const float kLog10vMFConcentration = LOG10_VMF_CONCENTRATION; +static const float kLog10vMFConcentrationScreen = LOG10_VMF_CONCENTRATION_SCREEN; +static const float kBoundaryTermBeta = BOUNDARY_TERM_BETA; +static const bool kUseAntitheticSampling = USE_ANTITHETIC_SAMPLING; +static const float kHarmonicGamma = HARMONIC_GAMMA; + +static const bool kUseEnvLight = USE_ENV_LIGHT; +static const bool kUseAnalyticLights = USE_ANALYTIC_LIGHTS; +static const bool kUseEmissiveLights = USE_EMISSIVE_LIGHTS; diff --git a/Source/RenderPasses/WARDiffPathTracer/WARDiffPathTracer.cpp b/Source/RenderPasses/WARDiffPathTracer/WARDiffPathTracer.cpp new file mode 100644 index 000000000..fdcb2e931 --- /dev/null +++ b/Source/RenderPasses/WARDiffPathTracer/WARDiffPathTracer.cpp @@ -0,0 +1,712 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "WARDiffPathTracer.h" +#include "RenderGraph/RenderPassStandardFlags.h" + +extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) +{ + registry.registerClass(); + ScriptBindings::registerBinding(WARDiffPathTracer::registerBindings); +} + +namespace +{ +const char kShaderFile[] = "RenderPasses/WARDiffPathTracer/WARDiffPathTracer.rt.slang"; + +// Ray tracing settings that affect the traversal stack size. +// These should be set as small as possible. +const uint32_t kMaxPayloadSizeBytes = 72u; +const uint32_t kMaxRecursionDepth = 2u; + +const ChannelList kInputChannels = {}; + +const ChannelList kOutputChannels = { + {"color", "gOutputColor", "Output color (sum of direct and indirect)", false, ResourceFormat::RGBA32Float}, + {"dColor", "gOutputDColor", "Output derivatives computed via auto-diff", false, ResourceFormat::RGBA32Float}, +}; + +const std::string kSamplesPerPixel = "samplesPerPixel"; +const std::string kMaxBounces = "maxBounces"; + +const std::string kDiffMode = "diffMode"; +const std::string kDiffVarName = "diffVarName"; + +const std::string kSampleGenerator = "sampleGenerator"; +const std::string kFixedSeed = "fixedSeed"; +const std::string kUseBSDFSampling = "useBSDFSampling"; +const std::string kUseNEE = "useNEE"; +const std::string kUseMIS = "useMIS"; + +const std::string kUseWAR = "useWAR"; +const std::string kAuxSampleCount = "auxSampleCount"; +const std::string kLog10vMFConcentration = "Log10vMFConcentration"; +const std::string kLog10vMFConcentrationScreen = "Log10vMFConcentrationScreen"; +const std::string kBoundaryTermBeta = "boundaryTermBeta"; +const std::string kUseAntitheticSampling = "useAntitheticSampling"; +} // namespace + +WARDiffPathTracer::WARDiffPathTracer(ref pDevice, const Properties& props) : RenderPass(pDevice) +{ + parseProperties(props); + + // Create a sample generator. + mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_UNIFORM); + mpPixelDebug = std::make_unique(mpDevice); + + FALCOR_ASSERT(mpSampleGenerator); + + // Set differentiable rendering debug parameters if needed. + if (mStaticParams.diffVarName == "CBOX_BUNNY_MATERIAL") + { + // Albedo value with materialID = 0 + setDiffDebugParams(DiffVariableType::Material, uint2(0, 0), 0, float4(1.f, 1.f, 1.f, 0.f)); + } + else if (mStaticParams.diffVarName == "CBOX_BUNNY_TRANSLATION") + { + // Vertical translation with meshID = 0 + setDiffDebugParams(DiffVariableType::GeometryTranslation, uint2(0, 0), 0, float4(0.f, 1.f, 0.f, 0.f)); + } +} + +void WARDiffPathTracer::parseProperties(const Properties& props) +{ + for (const auto& [key, value] : props) + { + // Rendering parameters + if (key == kSamplesPerPixel) + mStaticParams.samplesPerPixel = value; + else if (key == kMaxBounces) + mStaticParams.maxBounces = value; + + // Differentiable rendering parameters + else if (key == kDiffMode) + mStaticParams.diffMode = value; + else if (key == kDiffVarName) + mStaticParams.diffVarName = value.operator std::string(); + + // Sampling parameters + else if (key == kSampleGenerator) + mStaticParams.sampleGenerator = value; + else if (key == kFixedSeed) + { + mParams.fixedSeed = value; + mParams.useFixedSeed = true; + } + else if (key == kUseBSDFSampling) + mStaticParams.useBSDFSampling = value; + else if (key == kUseNEE) + mStaticParams.useNEE = value; + else if (key == kUseMIS) + mStaticParams.useMIS = value; + + // WAR parameters + else if (key == kUseWAR) + mStaticParams.useWAR = value; + else if (key == kAuxSampleCount) + mStaticParams.auxSampleCount = value; + else if (key == kLog10vMFConcentration) + mStaticParams.log10vMFConcentration = value; + else if (key == kLog10vMFConcentrationScreen) + mStaticParams.log10vMFConcentrationScreen = value; + else if (key == kBoundaryTermBeta) + mStaticParams.boundaryTermBeta = value; + else if (key == kUseAntitheticSampling) + mStaticParams.useAntitheticSampling = value; + + else + logWarning("Unknown property '{}' in WARDiffPathTracer properties.", key); + } +} + +Properties WARDiffPathTracer::getProperties() const +{ + Properties props; + + // Rendering parameters + props[kSamplesPerPixel] = mStaticParams.samplesPerPixel; + props[kMaxBounces] = mStaticParams.maxBounces; + + // Differentiable rendering parameters + props[kDiffMode] = mStaticParams.diffMode; + props[kDiffVarName] = mStaticParams.diffVarName; + + // Sampling parameters + props[kSampleGenerator] = mStaticParams.sampleGenerator; + if (mParams.useFixedSeed) + props[kFixedSeed] = mParams.fixedSeed; + props[kUseBSDFSampling] = mStaticParams.useBSDFSampling; + props[kUseNEE] = mStaticParams.useNEE; + props[kUseMIS] = mStaticParams.useMIS; + + // WAR parameters + props[kUseWAR] = mStaticParams.useWAR; + props[kAuxSampleCount] = mStaticParams.auxSampleCount; + props[kLog10vMFConcentration] = mStaticParams.log10vMFConcentration; + props[kLog10vMFConcentrationScreen] = mStaticParams.log10vMFConcentrationScreen; + props[kBoundaryTermBeta] = mStaticParams.boundaryTermBeta; + props[kUseAntitheticSampling] = mStaticParams.useAntitheticSampling; + + return props; +} + +RenderPassReflection WARDiffPathTracer::reflect(const CompileData& compileData) +{ + RenderPassReflection reflector; + + // Define our input/output channels. + addRenderPassInputs(reflector, kInputChannels); + addRenderPassOutputs(reflector, kOutputChannels); + + return reflector; +} + +void WARDiffPathTracer::setFrameDim(const uint2 frameDim) +{ + auto prevFrameDim = mParams.frameDim; + mParams.frameDim = frameDim; + + if (any(mParams.frameDim != prevFrameDim)) + { + mVarsChanged = true; + } +} + +void WARDiffPathTracer::setScene(RenderContext* pRenderContext, const ref& pScene) +{ + mpScene = pScene; + mParams.frameCount = 0; + mParams.frameDim = {}; + + // Need to recreate the trace passes because the shader binding table changes. + mpTracePass = nullptr; + + resetLighting(); + + if (mpScene) + { + if (pScene->hasGeometryType(Scene::GeometryType::Custom)) + { + logError("WARDiffPathTracer: This render pass does not support custom primitives."); + } + + mRecompile = true; + } +} + +void WARDiffPathTracer::execute(RenderContext* pRenderContext, const RenderData& renderData) +{ + if (!beginFrame(pRenderContext, renderData)) + return; + + // Update shader program specialization. + updatePrograms(); + + // Prepare resources. + prepareResources(pRenderContext, renderData); + + // Prepare the differentiable path tracer parameter block. + // This should be called after all resources have been created. + prepareDiffPathTracer(renderData); + + // Trace pass. + FALCOR_ASSERT(mpTracePass); + tracePass(pRenderContext, renderData, *mpTracePass); + + endFrame(pRenderContext, renderData); +} + +void WARDiffPathTracer::renderUI(Gui::Widgets& widget) +{ + bool dirty = false; + + // Rendering options. + dirty |= renderRenderingUI(widget); + + // Debug options. + dirty |= renderDebugUI(widget); + + // If rendering options that modify the output have changed, set flag to indicate that. + // In execute() we will pass the flag to other passes for reset of temporal data etc. + if (dirty) + { + mOptionsChanged = true; + } +} + +bool WARDiffPathTracer::renderRenderingUI(Gui::Widgets& widget) +{ + bool dirty = false; + bool runtimeDirty = false; + + dirty |= widget.var("Samples/pixel", mStaticParams.samplesPerPixel, 1u, kMaxSamplesPerPixel); + widget.tooltip("Number of samples per pixel. One path is traced for each sample."); + + dirty |= widget.var("Max bounces", mStaticParams.maxBounces, 0u, kBounceLimit); + widget.tooltip("Maximum number of surface bounces\n0 = direct only\n1 = one indirect bounce etc."); + + // Differentiable rendering options. + + dirty |= widget.dropdown("Diff mode", mStaticParams.diffMode); + widget.text("Diff variable name: " + mStaticParams.diffVarName); + + dirty |= widget.checkbox("Antithetic sampling", mStaticParams.useAntitheticSampling); + widget.tooltip( + "Use antithetic sampling.\n" + "When enabled, two correlated paths are traced per pixel, one with the original sample and one with the negated sample.\n" + "This can be used to reduce variance in gradient estimation." + ); + + // Sampling options. + + if (widget.dropdown("Sample generator", SampleGenerator::getGuiDropdownList(), mStaticParams.sampleGenerator)) + { + mpSampleGenerator = SampleGenerator::create(mpDevice, mStaticParams.sampleGenerator); + dirty = true; + } + + dirty |= widget.checkbox("BSDF importance sampling", mStaticParams.useBSDFSampling); + widget.tooltip( + "BSDF importance sampling should normally be enabled.\n\n" + "If disabled, cosine-weighted hemisphere sampling is used for debugging purposes" + ); + + dirty |= widget.checkbox("Next-event estimation (NEE)", mStaticParams.useNEE); + widget.tooltip("Use next-event estimation.\nThis option enables direct illumination sampling at each path vertex."); + + if (mStaticParams.useNEE) + { + dirty |= widget.checkbox("Multiple importance sampling (MIS)", mStaticParams.useMIS); + widget.tooltip( + "When enabled, BSDF sampling is combined with light sampling for the environment map and emissive lights.\n" + "Note that MIS has currently no effect on analytic lights." + ); + } + + if (dirty) + mRecompile = true; + return dirty || runtimeDirty; +} + +bool WARDiffPathTracer::renderDebugUI(Gui::Widgets& widget) +{ + bool dirty = false; + + if (auto group = widget.group("Debugging")) + { + dirty |= group.checkbox("Use fixed seed", mParams.useFixedSeed); + group.tooltip( + "Forces a fixed random seed for each frame.\n\n" + "This should produce exactly the same image each frame, which can be useful for debugging." + ); + if (mParams.useFixedSeed) + { + dirty |= group.var("Seed", mParams.fixedSeed); + } + + mpPixelDebug->renderUI(group); + } + + return dirty; +} + +bool WARDiffPathTracer::onMouseEvent(const MouseEvent& mouseEvent) +{ + return mpPixelDebug->onMouseEvent(mouseEvent); +} + +WARDiffPathTracer::TracePass::TracePass( + ref pDevice, + const std::string& name, + const std::string& passDefine, + const ref& pScene, + const DefineList& defines, + const TypeConformanceList& globalTypeConformances +) + : name(name), passDefine(passDefine) +{ + const uint32_t kRayTypeScatter = 0; + const uint32_t kMissScatter = 0; + + ProgramDesc desc; + desc.addShaderModules(pScene->getShaderModules()); + desc.addShaderLibrary(kShaderFile); + desc.setMaxPayloadSize(kMaxPayloadSizeBytes); + desc.setMaxAttributeSize(pScene->getRaytracingMaxAttributeSize()); + desc.setMaxTraceRecursionDepth(kMaxRecursionDepth); + if (!pScene->hasProceduralGeometry()) + desc.setRtPipelineFlags(RtPipelineFlags::SkipProceduralPrimitives); + + // Create ray tracing binding table. + pBindingTable = RtBindingTable::create(0, 1, pScene->getGeometryCount()); + + // Specify entry point for raygen shader. + // The raygen shader needs type conformances for *all* materials in the scene. + pBindingTable->setRayGen(desc.addRayGen("rayGen", globalTypeConformances)); + + pProgram = Program::create(pDevice, desc, defines); +} + +void WARDiffPathTracer::TracePass::prepareProgram(ref pDevice, const DefineList& defines) +{ + FALCOR_ASSERT(pProgram != nullptr && pBindingTable != nullptr); + pProgram->setDefines(defines); + if (!passDefine.empty()) + pProgram->addDefine(passDefine); + pVars = RtProgramVars::create(pDevice, pProgram, pBindingTable); +} + +void WARDiffPathTracer::updatePrograms() +{ + FALCOR_ASSERT(mpScene); + + if (mRecompile == false) + return; + + auto defines = mStaticParams.getDefines(*this); + auto globalTypeConformances = mpScene->getTypeConformances(); + + // Create trace pass. + mpTracePass = std::make_unique(mpDevice, "tracePass", "", mpScene, defines, globalTypeConformances); + mpTracePass->prepareProgram(mpDevice, defines); + + // Create compute passes. + ProgramDesc baseDesc; + baseDesc.addShaderModules(mpScene->getShaderModules()); + baseDesc.addTypeConformances(globalTypeConformances); + + // TODO: Create preprocessing compute passes for WAR. + + mVarsChanged = true; + mRecompile = false; +} + +void WARDiffPathTracer::prepareResources(RenderContext* pRenderContext, const RenderData& renderData) +{ + // TODO: Prepare buffers for WAR. +} + +void WARDiffPathTracer::prepareDiffPathTracer(const RenderData& renderData) +{ + if (!mpDiffPTBlock || mVarsChanged) + { + auto pReflection = mpTracePass->pProgram->getReflector(); + auto pBlockReflection = pReflection->getParameterBlock("gDiffPTData"); + FALCOR_ASSERT(pBlockReflection); + mpDiffPTBlock = ParameterBlock::create(mpDevice, pBlockReflection); + FALCOR_ASSERT(mpDiffPTBlock); + mVarsChanged = true; + } + + // Bind resources. + auto var = mpDiffPTBlock->getRootVar(); + bindShaderData(var, renderData); +} + +void WARDiffPathTracer::resetLighting() +{ + // We only use a uniform emissive sampler for now. + mpEmissiveSampler = nullptr; + mRecompile = true; +} + +void WARDiffPathTracer::prepareMaterials(RenderContext* pRenderContext) +{ + // This functions checks for material changes and performs any necessary update. + // For now all we need to do is to trigger a recompile so that the right defines get set. + // In the future, we might want to do additional material-specific setup here. + + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded)) + { + mRecompile = true; + } +} + +bool WARDiffPathTracer::prepareLighting(RenderContext* pRenderContext) +{ + bool lightingChanged = false; + + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RenderSettingsChanged)) + { + lightingChanged = true; + mRecompile = true; + } + + if (mpScene->useEnvLight()) + { + logError("WARDiffPathTracer: This render pass does not support environment lights."); + } + + // Request the light collection if emissive lights are enabled. + if (mpScene->getRenderSettings().useEmissiveLights) + { + mpScene->getLightCollection(pRenderContext); + } + + if (mpScene->useEmissiveLights()) + { + if (!mpEmissiveSampler) + { + const auto& pLights = mpScene->getLightCollection(pRenderContext); + FALCOR_ASSERT(pLights && pLights->getActiveLightCount(pRenderContext) > 0); + FALCOR_ASSERT(!mpEmissiveSampler); + + // We only use a uniform emissive sampler for now. + // LightBVH seems buggy with the Cornell box bunny example. + mpEmissiveSampler = std::make_unique(pRenderContext, mpScene); + + lightingChanged = true; + mRecompile = true; + } + } + else + { + if (mpEmissiveSampler) + { + // We only use a uniform emissive sampler for now. + mpEmissiveSampler = nullptr; + lightingChanged = true; + mRecompile = true; + } + } + + if (mpEmissiveSampler) + { + lightingChanged |= mpEmissiveSampler->update(pRenderContext); + auto defines = mpEmissiveSampler->getDefines(); + if (mpTracePass && mpTracePass->pProgram->addDefines(defines)) + mRecompile = true; + } + + return lightingChanged; +} + +void WARDiffPathTracer::bindShaderData(const ShaderVar& var, const RenderData& renderData, bool useLightSampling) const +{ + var["params"].setBlob(mParams); + + if (useLightSampling && mpEmissiveSampler) + { + mpEmissiveSampler->bindShaderData(var["emissiveSampler"]); + } +} + +bool WARDiffPathTracer::beginFrame(RenderContext* pRenderContext, const RenderData& renderData) +{ + // Reset output textures. + bool dontClearOutputs = mStaticParams.diffMode == DiffMode::BackwardDiff && mParams.runBackward == 1; + if (!dontClearOutputs) + { + for (const auto& channel : kOutputChannels) + { + auto pTexture = renderData.getTexture(channel.name); + pRenderContext->clearUAV(pTexture->getUAV().get(), float4(0.f)); + } + } + + const auto& pOutputColor = renderData.getTexture("color"); + FALCOR_ASSERT(pOutputColor); + + // Set output frame dimension. + setFrameDim(uint2(pOutputColor->getWidth(), pOutputColor->getHeight())); + + // Validate all I/O sizes match the expected size. + // If not, we'll disable the path tracer to give the user a chance to fix the configuration before re-enabling it. + bool resolutionMismatch = false; + auto validateChannels = [&](const auto& channels) + { + for (const auto& channel : channels) + { + auto pTexture = renderData.getTexture(channel.name); + if (pTexture && (pTexture->getWidth() != mParams.frameDim.x || pTexture->getHeight() != mParams.frameDim.y)) + resolutionMismatch = true; + } + }; + validateChannels(kInputChannels); + validateChannels(kOutputChannels); + + if (mEnabled && resolutionMismatch) + { + logError("WARDiffPathTracer I/O sizes don't match. The pass will be disabled."); + mEnabled = false; + } + + if (mpScene == nullptr || !mEnabled) + { + // Set refresh flag if changes that affect the output have occured. + // This is needed to ensure other passes get notified when the path tracer is enabled/disabled. + if (mOptionsChanged) + { + auto& dict = renderData.getDictionary(); + auto flags = dict.getValue(kRenderPassRefreshFlags, Falcor::RenderPassRefreshFlags::None); + if (mOptionsChanged) + flags |= Falcor::RenderPassRefreshFlags::RenderOptionsChanged; + dict[Falcor::kRenderPassRefreshFlags] = flags; + } + + return false; + } + + // Update materials. + prepareMaterials(pRenderContext); + + // Update the emissive sampler to the current frame. + bool lightingChanged = prepareLighting(pRenderContext); + + // Update refresh flag if changes that affect the output have occured. + auto& dict = renderData.getDictionary(); + if (mOptionsChanged || lightingChanged) + { + auto flags = dict.getValue(kRenderPassRefreshFlags, Falcor::RenderPassRefreshFlags::None); + if (mOptionsChanged) + flags |= Falcor::RenderPassRefreshFlags::RenderOptionsChanged; + if (lightingChanged) + flags |= Falcor::RenderPassRefreshFlags::LightingChanged; + dict[Falcor::kRenderPassRefreshFlags] = flags; + mOptionsChanged = false; + } + + mpPixelDebug->beginFrame(pRenderContext, mParams.frameDim); + + // Update the random seed. + mParams.seed = mParams.useFixedSeed ? mParams.fixedSeed : mParams.frameCount; + return true; +} + +void WARDiffPathTracer::endFrame(RenderContext* pRenderContext, const RenderData& renderData) +{ + mpPixelDebug->endFrame(pRenderContext); + + mVarsChanged = false; + mParams.frameCount++; +} + +void WARDiffPathTracer::tracePass(RenderContext* pRenderContext, const RenderData& renderData, TracePass& tracePass) +{ + FALCOR_PROFILE(pRenderContext, tracePass.name); + + FALCOR_ASSERT(tracePass.pProgram != nullptr && tracePass.pBindingTable != nullptr && tracePass.pVars != nullptr); + + // Bind global resources. + auto var = tracePass.pVars->getRootVar(); + mpScene->setRaytracingShaderData(pRenderContext, var); + + if (mVarsChanged) + mpSampleGenerator->bindShaderData(var); + + mpPixelDebug->prepareProgram(tracePass.pProgram, var); + + // Bind the differentiable path tracer data block; + var["gDiffPTData"] = mpDiffPTBlock; + + var["gDiffDebug"].setBlob(mDiffDebugParams); + var["dLdI"] = mpdLdI; + if (mpSceneGradients) + mpSceneGradients->bindShaderData(var["gSceneGradients"]); + + // Bind I/O buffers. These needs to be done per-frame as the buffers may change anytime. + auto bind = [&](const ChannelDesc& desc) + { + if (!desc.texname.empty()) + { + var[desc.texname] = renderData.getTexture(desc.name); + } + }; + for (auto channel : kInputChannels) + bind(channel); + for (auto channel : kOutputChannels) + bind(channel); + + // Full screen dispatch. + mpScene->raytrace(pRenderContext, tracePass.pProgram.get(), tracePass.pVars, uint3(mParams.frameDim, 1)); +} + +DefineList WARDiffPathTracer::StaticParams::getDefines(const WARDiffPathTracer& owner) const +{ + DefineList defines; + + defines.add("SAMPLES_PER_PIXEL", std::to_string(samplesPerPixel)); + defines.add("MAX_BOUNCES", std::to_string(maxBounces)); + + defines.add("DIFF_MODE", std::to_string((uint32_t)diffMode)); + defines.add(diffVarName); + + defines.add("USE_BSDF_SAMPLING", useBSDFSampling ? "1" : "0"); + defines.add("USE_NEE", useNEE ? "1" : "0"); + defines.add("USE_MIS", useMIS ? "1" : "0"); + + // WAR parameters configuration. + defines.add("USE_WAR", useWAR ? "1" : "0"); + defines.add("AUX_SAMPLE_COUNT", std::to_string(auxSampleCount)); + defines.add("LOG10_VMF_CONCENTRATION", std::to_string(log10vMFConcentration)); + defines.add("LOG10_VMF_CONCENTRATION_SCREEN", std::to_string(log10vMFConcentrationScreen)); + defines.add("BOUNDARY_TERM_BETA", std::to_string(boundaryTermBeta)); + defines.add("USE_ANTITHETIC_SAMPLING", useAntitheticSampling ? "1" : "0"); + defines.add("HARMONIC_GAMMA", std::to_string(harmonicGamma)); + + // Sampling utilities configuration. + FALCOR_ASSERT(owner.mpSampleGenerator); + defines.add(owner.mpSampleGenerator->getDefines()); + + if (owner.mpEmissiveSampler) + defines.add(owner.mpEmissiveSampler->getDefines()); + + // Scene-specific configuration. + const auto& scene = owner.mpScene; + if (scene) + defines.add(scene->getSceneDefines()); + defines.add("USE_ENV_LIGHT", scene && scene->useEnvLight() ? "1" : "0"); + defines.add("USE_ANALYTIC_LIGHTS", scene && scene->useAnalyticLights() ? "1" : "0"); + defines.add("USE_EMISSIVE_LIGHTS", scene && scene->useEmissiveLights() ? "1" : "0"); + + return defines; +} + +void WARDiffPathTracer::registerBindings(pybind11::module& m) +{ + if (!pybind11::hasattr(m, "DiffMode")) + { + pybind11::enum_ diffMode(m, "DiffMode"); + diffMode.value("Primal", DiffMode::Primal); + diffMode.value("BackwardDiff", DiffMode::BackwardDiff); + diffMode.value("ForwardDiffDebug", DiffMode::ForwardDiffDebug); + diffMode.value("BackwardDiffDebug", DiffMode::BackwardDiffDebug); + } + + pybind11::class_> pass(m, "WARDiffPathTracer"); + pass.def_property("scene_gradients", &WARDiffPathTracer::getSceneGradients, &WARDiffPathTracer::setSceneGradients); + pass.def_property("run_backward", &WARDiffPathTracer::getRunBackward, &WARDiffPathTracer::setRunBackward); + pass.def_property("dL_dI", &WARDiffPathTracer::getdLdI, &WARDiffPathTracer::setdLdI); +} + +void WARDiffPathTracer::setDiffDebugParams(DiffVariableType varType, uint2 id, uint32_t offset, float4 grad) +{ + mDiffDebugParams.varType = varType; + mDiffDebugParams.id = id; + mDiffDebugParams.offset = offset; + mDiffDebugParams.grad = grad; +} diff --git a/Source/RenderPasses/WARDiffPathTracer/WARDiffPathTracer.h b/Source/RenderPasses/WARDiffPathTracer/WARDiffPathTracer.h new file mode 100644 index 000000000..939f6bc08 --- /dev/null +++ b/Source/RenderPasses/WARDiffPathTracer/WARDiffPathTracer.h @@ -0,0 +1,202 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#pragma once +#include "Falcor.h" +#include "RenderGraph/RenderPass.h" +#include "RenderGraph/RenderPassHelpers.h" +#include "Utils/Sampling/SampleGenerator.h" +#include "Utils/Debug/PixelDebug.h" +#include "Rendering/Lights/EmissiveUniformSampler.h" +#include "DiffRendering/SceneGradients.h" +#include "DiffRendering/SharedTypes.slang" + +#include "Params.slang" + +using namespace Falcor; + +/** + * Warped-Area Reparameterized differentiable path tracer. + * This pass implements a differentiable path tracer using warped-area reparameterization for handle geometric discontinuities. + */ +class WARDiffPathTracer : public RenderPass +{ +public: + FALCOR_PLUGIN_CLASS( + WARDiffPathTracer, + "WARDiffPathTracer", + "Warped-area reparameterized differentiable path tracer using DXR 1.1 TraceRayInline." + ); + + static ref create(ref pDevice, const Properties& props) + { + return make_ref(pDevice, props); + } + + WARDiffPathTracer(ref pDevice, const Properties& props); + + virtual Properties getProperties() const override; + virtual RenderPassReflection reflect(const CompileData& compileData) override; + virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; + virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; + virtual void renderUI(Gui::Widgets& widget) override; + virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; + virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } + + static void registerBindings(pybind11::module& m); + + // Python bindings + const ref& getSceneGradients() const { return mpSceneGradients; } + void setSceneGradients(const ref& sg) { mpSceneGradients = sg; } + uint32_t getRunBackward() const { return mParams.runBackward; } + void setRunBackward(uint32_t value) { mParams.runBackward = value; } + const ref& getdLdI() const { return mpdLdI; } + void setdLdI(const ref& buf) { mpdLdI = buf; } + + void setDiffDebugParams(DiffVariableType varType, uint2 id, uint32_t offset, float4 grad); + +private: + struct TracePass + { + std::string name; + std::string passDefine; + ref pProgram; + ref pBindingTable; + ref pVars; + + TracePass( + ref pDevice, + const std::string& name, + const std::string& passDefine, + const ref& pScene, + const DefineList& defines, + const TypeConformanceList& globalTypeConformances + ); + void prepareProgram(ref pDevice, const DefineList& defines); + }; + + void parseProperties(const Properties& props); + void updatePrograms(); + void setFrameDim(const uint2 frameDim); + void prepareResources(RenderContext* pRenderContext, const RenderData& renderData); + void prepareDiffPathTracer(const RenderData& renderData); + void resetLighting(); + void prepareMaterials(RenderContext* pRenderContext); + bool prepareLighting(RenderContext* pRenderContext); + void bindShaderData(const ShaderVar& var, const RenderData& renderData, bool useLightSampling = true) const; + bool renderRenderingUI(Gui::Widgets& widget); + bool renderDebugUI(Gui::Widgets& widget); + bool beginFrame(RenderContext* pRenderContext, const RenderData& renderData); + void endFrame(RenderContext* pRenderContext, const RenderData& renderData); + void tracePass(RenderContext* pRenderContext, const RenderData& renderData, TracePass& tracePass); + + /** + * Static configuration. Changing any of these options require shader recompilation. + */ + struct StaticParams + { + // Rendering parameters + + /// Number of samples (paths) per pixel, unless a sample density map is used. + uint32_t samplesPerPixel = 1; + /// Max number of indirect bounces (0 = none). + uint32_t maxBounces = 0; + + // Differentiable rendering parameters + + /// Differentiation mode. + DiffMode diffMode = DiffMode::ForwardDiffDebug; + /// Name of the variable to differentiate. Used for rendering forward-mode gradient images. + std::string diffVarName = ""; + + // Sampling parameters + + /// Pseudorandom sample generator type. + uint32_t sampleGenerator = SAMPLE_GENERATOR_TINY_UNIFORM; + /// Use BRDF importance sampling, otherwise cosine-weighted hemisphere sampling. + bool useBSDFSampling = true; + /// Use next-event estimation (NEE). This enables shadow ray(s) from each path vertex. + bool useNEE = true; + /// Use multiple importance sampling (MIS) when NEE is enabled. + bool useMIS = true; + + // WAR parameters + + /// Use warped-area reparameterization (required if there are geometric discontinuities). + bool useWAR = true; + /// Number of auxiliary samples per primary sample for warped-area reparameterization. + uint32_t auxSampleCount = 16; + /// Log10 of the VMF concentration parameter. + float log10vMFConcentration = 5.f; + /// Log10 of the VMF concentration parameter. + float log10vMFConcentrationScreen = 5.f; + /// Beta parameter for the boundary term. + float boundaryTermBeta = 0.01f; + /// Use antithetic sampling for variance reduction. + bool useAntitheticSampling = true; + /// Gamma parameter for the harmonic weights. + float harmonicGamma = 2.f; + + DefineList getDefines(const WARDiffPathTracer& owner) const; + }; + + // Configuration + + /// Runtime path tracer parameters. + WARDiffPathTracerParams mParams; + /// Static parameters. These are set as compile-time constants in the shaders. + StaticParams mStaticParams; + /// Differentiable rendering debug parameters. + DiffDebugParams mDiffDebugParams; + + /// Switch to enable/disable the path tracer. When disabled the pass outputs are cleared. + bool mEnabled = true; + + // Internal state + + ref mpScene; + ref mpSampleGenerator; + std::unique_ptr mpEmissiveSampler; + std::unique_ptr mpPixelDebug; + + ref mpSceneGradients; + /// Derivatives of loss function w.r.t. image pixel values. + ref mpdLdI; + + ref mpDiffPTBlock; + + // Runtime data + + /// Set to true when program specialization has changed. + bool mRecompile = false; + /// This is set to true whenever the program vars have changed and resources need to be rebound. + bool mVarsChanged = true; + /// True if the config has changed since last frame. + bool mOptionsChanged = false; + + std::unique_ptr mpTracePass; +}; diff --git a/Source/RenderPasses/WARDiffPathTracer/WARDiffPathTracer.rt.slang b/Source/RenderPasses/WARDiffPathTracer/WARDiffPathTracer.rt.slang new file mode 100644 index 000000000..d92ac6ec2 --- /dev/null +++ b/Source/RenderPasses/WARDiffPathTracer/WARDiffPathTracer.rt.slang @@ -0,0 +1,210 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Utils/Math/MathConstants.slangh" + +import Rendering.Lights.EmissiveLightSampler; +import Scene.Raytracing; +import Scene.RaytracingInline; +import Utils.Sampling.SampleGenerator; +import Utils.Debug.PixelDebug; +import DiffRendering.SharedTypes; +import DiffRendering.DiffSceneIO; +import DiffRendering.DiffSceneQuery; +import DiffRendering.DiffDebug; + +import PTUtils; +import WarpedAreaReparam; + +static const float kPixelGaussianSigma = 0.5f; + +struct DiffPTData +{ + WARDiffPathTracerParams params; + EmissiveLightSampler emissiveSampler; +}; + +ParameterBlock gDiffPTData; + +StructuredBuffer dLdI; + +// Outputs +RWTexture2D gOutputColor; + +// Convert square samples to Gaussian samples +float2 sampleGaussian(float2 xy, float sigma) +{ + return (sqrt(-2.f * log(xy.x)) * float2(cos(M_2PI * xy.y), sin(M_2PI * xy.y))) * sigma; +} + +// Evaluate Gaussian at xy +[Differentiable] +[PreferRecompute] +float evalGaussian(float2 xy, no_diff float sigma) +{ + const float alpha = (-1.f / (2.f * sigma * sigma)); + return exp(alpha * dot(xy, xy)) * (1.f / (2.f * M_PI * sigma * sigma)); +} + +[Differentiable] +float3 tracePath(SceneQueryAD sceneQuery, float2 pixel, no_diff float2 pixelCenter, inout SampleGenerator sg) +{ + PathData pathData = PathData(); + IntersectionAD isect = IntersectionAD(); + + // Warped-area reparameterization for primary rays. + if (kUseWAR) + { + float weight = reparameterizeScreenSample(sceneQuery, gDiffPTData.params.frameDim, sg, pixel); + pathData.thp *= weight; + } + + // Use Gaussian pixel filter. + float pixelWeight = evalGaussian(pixel - pixelCenter, kPixelGaussianSigma); + pathData.thp *= pixelWeight / detach(pixelWeight); + + float3 primaryRayOrigin = sceneQuery.loadCameraPosition(); + float3 primaryRayDirection = sceneQuery.computeCameraRayDirection(pixel, gDiffPTData.params.frameDim); + RayAD ray = RayAD(detach(primaryRayOrigin), primaryRayDirection, sceneQuery.gradInfo.pixel); + + if (!sceneQuery.traceRayInlineAD(ray, isect, SceneQueryAD.DiffIntersectionMode.AttachToRay)) + return pathData.radiance; + + [MaxIters(MAX_BOUNCES + 2)] + while (!pathData.terminated) + { + handleHit(sceneQuery, isect, gDiffPTData.emissiveSampler, pathData, ray, sg, kUseWAR); + + if (pathData.terminated) + break; + + if (!sceneQuery.traceRayInlineAD(ray, isect, SceneQueryAD.DiffIntersectionMode.AttachToRay)) + break; + } + + return pathData.radiance; +} + +[Differentiable] +float3 tracePaths(SceneQueryAD sceneQuery, uint2 pixel) +{ + float3 result = float3(0.f); + + var params = gDiffPTData.params; + uint frameSeed = bool(params.useFixedSeed) ? params.fixedSeed : params.frameCount; + + // Antithetic sampling for differentiable rendering (interior term). + SampleGenerator sg = SampleGenerator(pixel, frameSeed / 2); + + // Throw away some samples. + sampleNext2D(sg); + sampleNext2D(sg); + sampleNext2D(sg); + + // Use Gaussian pixel filter. + float2 pixelf = pixel + float2(0.5f, 0.5f); + + float2 uniform2D = no_diff sampleNext2D(sg); + float2 jitter2D = no_diff sampleGaussian(uniform2D, kPixelGaussianSigma); + + // Antithetic sampling for differentiable rendering (interior term). + if (kUseAntitheticSampling && frameSeed % 2 == 0) + jitter2D = -jitter2D; + + // Trace an interior path. + result += tracePath(sceneQuery, pixelf + jitter2D, pixelf, sg); + + return result; +} + +[shader("raygeneration")] +void rayGen() +{ + uint2 pixel = DispatchRaysIndex().xy; + printSetPixel(pixel); + + DiffSceneIO diffSceneIO; + SceneQueryAD sceneQuery = SceneQueryAD(diffSceneIO, SceneGradientInfo(SceneGradientFlag.make(GradientMode.Scene), pixel)); + + if (kDiffMode == DiffMode::Primal) + { + // Primal rendering. + float3 color = tracePaths(sceneQuery, pixel); + gOutputColor[pixel] = float4(color, 1.f); + } + else if (kDiffMode == DiffMode::BackwardDiff) + { + var params = gDiffPTData.params; + float3 color = float3(0.f); + if (params.runBackward == 0) + { + color = tracePaths(sceneQuery, pixel); + gOutputColor[pixel] = float4(color, 1.f); + } + else + { + // Set differential query mode to `Scene` for visualizing gradient images. + SceneQueryAD.Differential dQuery; + dQuery.gradInfo = { SceneGradientFlag.make(GradientMode.Scene) }; + + DifferentialPair dpQuery = diffPair(sceneQuery, dQuery); + + uint pixelIdx = pixel.y * params.frameDim.x + pixel.x; + bwd_diff(tracePaths)(dpQuery, pixel, dLdI[pixelIdx]); + } + } + else if (kDiffMode == DiffMode::ForwardDiffDebug) + { + // Forward-mode differentiable rendering for visualizing gradients. + + // Set differential query mode to `ForwardDebug` for visualizing gradient images. + SceneQueryAD.Differential dQuery; + dQuery.gradInfo = { SceneGradientFlag.make(GradientMode.ForwardDebug) }; + + DifferentialPair dpColor = fwd_diff(tracePaths)(diffPair(sceneQuery, dQuery), pixel); + gOutputColor[pixel] = float4(dpColor.p, 1.f); + + // Clamp gradient values. + float maxValue = gDiffPTData.params.assertThreshold; + bool resIsFinite = (dpColor.d[0] >= -maxValue && dpColor.d[0] <= maxValue) && + (dpColor.d[1] >= -maxValue && dpColor.d[1] <= maxValue) && + (dpColor.d[2] >= -maxValue && dpColor.d[2] <= maxValue); + if (resIsFinite) + gOutputDColor[pixel] = float4(dpColor.d[0], 0.f, 0.f, 1.f); // Only show the red channel. + } + else if (kDiffMode == DiffMode::BackwardDiffDebug) + { + // Reverse-mode differentiable rendering for visualizing gradients. + + // Set differential query mode to `Scene` for visualizing gradient images. + SceneQueryAD.Differential dQuery; + dQuery.gradInfo = { SceneGradientFlag.make(GradientMode.Scene) }; + + DifferentialPair dpQuery = diffPair(sceneQuery, dQuery); + bwd_diff(tracePaths)(dpQuery, pixel, float3(1.f, 0.f, 0.f)); + } +} diff --git a/Source/RenderPasses/WARDiffPathTracer/WarpedAreaReparam.slang b/Source/RenderPasses/WARDiffPathTracer/WarpedAreaReparam.slang new file mode 100644 index 000000000..a1183de2f --- /dev/null +++ b/Source/RenderPasses/WARDiffPathTracer/WarpedAreaReparam.slang @@ -0,0 +1,332 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Utils/Math/MathConstants.slangh" + +import Scene.RaytracingInline; +import Utils.Sampling.SampleGenerator; +import Utils.Math.MathHelpers; +import Utils.Math.DiffMathHelpers; +import Utils.Math.MatrixUtils; +import Utils.Debug.PixelDebug; +import DiffRendering.SharedTypes; +import DiffRendering.DiffSceneIO; +import DiffRendering.DiffSceneQuery; +import StaticParams; +import Params; + +[PreferRecompute] +float computeBoundaryTerm(float3 normalW, float3 direction) +{ + float dotND = dot(normalW, direction); + return dotND * dotND; +} + +[Differentiable] +[PreferRecompute] +[ForwardDerivative(fwd_computeHarmonicWeight)] +float computeHarmonicWeight( + no_diff IntersectionAD isect, + no_diff float3 origin, + no_diff float3 auxDirection, + no_diff float auxSampleY, + no_diff float kappa, + float3 direction +) +{ + float boundaryTerm = no_diff computeBoundaryTerm(isect.normalW, auxDirection); + + float sy = max(1.f - auxSampleY, 1e-6f); + float invVMFDensity = 1.f / ((1.f - sy) * exp(-2.f * kappa) + sy); + + float wDenom = invVMFDensity - 1.f + boundaryTerm; + float wDenomRcp = select(wDenom > 1e-4f, 1.f / wDenom, 0.f); + float w = wDenomRcp * wDenomRcp * wDenomRcp * invVMFDensity; + return w; +} + +[Differentiable] +[PreferRecompute] +DifferentialPair fwd_computeHarmonicWeight( + IntersectionAD isect, + float3 origin, + float3 auxDirection, + float auxSampleY, + float kappa, + DifferentialPair direction +) +{ + float boundaryTerm = no_diff computeBoundaryTerm(isect.normalW, auxDirection); + + float sy = max(1.f - auxSampleY, 1e-6f); + float invVMFDensity = 1.f / ((1.f - sy) * exp(-2.f * kappa) + sy); + + float wDenom = invVMFDensity - 1.f + boundaryTerm; + float wDenomRcp = select(wDenom > 1e-4f, 1.f / wDenom, 0.f); + float w = wDenomRcp * wDenomRcp * wDenomRcp * invVMFDensity; + + float tmp1 = invVMFDensity * w * wDenomRcp * kappa * 3; + // Project `auxDirection` onto the plane of `direction`. + float3 tmp2 = -cross(direction.p, cross(direction.p, direction.d)); + float dW = tmp1 * dot(tmp2, auxDirection); + + return DifferentialPair(w, clamp(dW, -1e10f, 1e10f)); +} + +// Create a wrapper for reflect(). This isn't marked with [Differentiable]. +// (For some reason, we're ignoring detach when differentiating things.) +[PreferRecompute] +float3 _reflect(float3 dir, float3 normal) +{ + return reflect(dir, normal); +} + +// Compute the warped-area sampled intersection for a given input ray. +[Differentiable] +[PreferRecompute] +__generic bool traceAsymptoticWeightedMeanIntersection( + SceneQueryAD sceneQuery, + no_diff float kappa, + float3 origin, + no_diff float3 baseDirection, + float3 direction[N], + out float3 warpedDirection[N], + inout SampleGenerator sg +) +{ + float totalWeight[N]; + float3 totalWarpedDirection[N]; + + // Initialize. + [ForceUnroll] + for (int j = 0; j < N; j++) + { + totalWeight[j] = 0.f; + totalWarpedDirection[j] = float3(0.f); + } + + float rnd[AUX_SAMPLE_COUNT]; + for (uint i = 0; i < kAuxSampleCount; i++) + rnd[i] = no_diff sampleNext1D(sg); + + // Create a local frame based on `baseDirection`. + float3 t = no_diff perp_stark(baseDirection); + ShadingFrame baseDirFrame = ShadingFrame(baseDirection, float4(t, 1.f)); + + // Loop over the number of auxiliary samples. + [MaxIters(AUX_SAMPLE_COUNT)] + for (uint i = 0; i < kAuxSampleCount; i++) + { + float2 sample; + if (kUseAntitheticSampling) + { + uint antitheticIdx = i / 2; + sample = float2(rnd[antitheticIdx * 2], rnd[antitheticIdx * 2 + 1]); + } + else + { + sample = no_diff sampleNext2D(sg); + } + + float3 dirLocal = no_diff sampleVonMisesFisher(sample, kappa); + float3 auxDirection = detach(normalize(baseDirFrame.fromLocal(dirLocal))); + if (kUseAntitheticSampling && i % 2 == 1) + { + // Every other sample, reflect `auxDirection` about `baseDirection`. + auxDirection = no_diff _reflect(-auxDirection, baseDirection); + } + + // Build a new RayAD with `auxDirection`. + RayAD auxRay = RayAD(origin, auxDirection, sceneQuery.gradInfo.pixel); + IntersectionAD auxIsect = IntersectionAD(); + + float3 dirW = auxDirection; + // Trace the ray. + if (sceneQuery.traceRayInlineAD(auxRay, auxIsect, SceneQueryAD.DiffIntersectionMode.AttachToGeometry)) + { + dirW = normalizeSafe(auxIsect.posW - origin); + } + + [ForceUnroll] + for (int j = 0; j < N; j++) + { + float weight = computeHarmonicWeight(auxIsect, origin, auxDirection, sample.y, kappa, direction[j]); + totalWeight[j] += weight; + totalWarpedDirection[j] += float3(weight) * dirW; + } + } + + [ForceUnroll] + for (int j = 0; j < N; j++) + { + // Clamp the total weight to avoid NaNs. + totalWeight[j] = max(totalWeight[j], 1e-8f); + + // Harmonic weighted mean. + warpedDirection[j] = totalWarpedDirection[j] / float3(totalWeight[j]); + } + return totalWeight[0] > 0.f; +} + +// Compute the warped-area reparameterization for (secondary) rays. + +[Differentiable] +[PreferRecompute] +void computeWarpedRay( + SceneQueryAD sceneQuery, + no_diff float kappa, + float3 origin, + float3 baseDirection, + float3 direction[3], + out float3 warpedDirection[3], + inout SampleGenerator sg +) +{ + float3 meanDirection[3]; + if (traceAsymptoticWeightedMeanIntersection<3>(sceneQuery, kappa, origin, baseDirection, direction, meanDirection, sg)) + { + [ForceUnroll] + for (int j = 0; j < 3; j++) + warpedDirection[j] = normalizeSafe(meanDirection[j]); + } + else + { + [ForceUnroll] + for (int j = 0; j < 3; j++) + warpedDirection[j] = direction[j]; + } +} + +[Differentiable] +[PreferRecompute] +float reparameterizeRay(SceneQueryAD sceneQuery, inout RayAD ray, inout SampleGenerator sg) +{ + DifferentialPair dpDirections = DifferentialPair( + { ray.direction, ray.direction, ray.direction }, { float3(1.f, 0.f, 0.f), float3(0.f, 1.f, 0.f), float3(0.f, 0.f, 1.f) } + ); + + // Zero out any gradients from/to the scene for this Jacobian computation. + SceneQueryAD.Differential dQuery = {}; + dQuery.gradInfo = { SceneGradientFlag.make(GradientMode.None) }; + + // Use the same SampleGenerator for correlated samples. + SampleGenerator sgCopy = sg; + + // Compute warped directions and differentials. + DifferentialPair dpWarpedDirections; + float kappa = pow(10.f, kLog10vMFConcentration); + fwd_diff(computeWarpedRay + )(diffPair(sceneQuery, dQuery), + kappa, + diffPair(ray.origin, float3(0.f)), + diffPair(ray.direction, float3(0.f)), + dpDirections, + dpWarpedDirections, + sgCopy); + + float3 warpDir = infinitesimal<3>(dpWarpedDirections.p[0]); // Can use any of dpWarpedDirections.p[j]. + float3 warpDiffXDir = infinitesimal<3>(dpWarpedDirections.d[0]); + float3 warpDiffYDir = infinitesimal<3>(dpWarpedDirections.d[1]); + float3 warpDiffZDir = infinitesimal<3>(dpWarpedDirections.d[2]); + + // Compute Jacobian determinant. + float3x3 jacobian = no_diff float3x3::identity() + float3x3(warpDiffXDir, warpDiffYDir, warpDiffZDir); + float detJ = determinant(jacobian); + + // Update the ray. + ray.direction += warpDir; + return detJ; +} + +// Compute the warped-area reparameterization for (primary) screen samples. + +[Differentiable] +[PreferRecompute] +void computeWarpedPrimarySample( + SceneQueryAD sceneQuery, + no_diff float kappa, + uint2 frameDim, + float2 basePosS, + float2 posS[2], + out float2 warpedPosS[2], + inout SampleGenerator sg +) +{ + float3 primaryRayOrigin = sceneQuery.loadCameraPosition(); + float3 baseRayDir = sceneQuery.computeCameraRayDirection(basePosS, frameDim); + float3 rayDir[2] = { sceneQuery.computeCameraRayDirection(posS[0], frameDim), sceneQuery.computeCameraRayDirection(posS[1], frameDim) }; + + float3 meanDirection[2]; + if (traceAsymptoticWeightedMeanIntersection<2>(sceneQuery, kappa, primaryRayOrigin, baseRayDir, rayDir, meanDirection, sg)) + { + // Project the warped direction onto screen space. + [ForceUnroll] + for (int j = 0; j < 2; j++) + { + float3 warpedDir = normalizeSafe(meanDirection[j]); + warpedPosS[j] = sceneQuery.computeCameraRayScreenPos(warpedDir, frameDim); + } + } + else + { + // If the ray is missed, just return the original sample. + [ForceUnroll] + for (int j = 0; j < 2; j++) + warpedPosS[j] = posS[j]; + } +} + +[Differentiable] +[PreferRecompute] +float reparameterizeScreenSample(SceneQueryAD sceneQuery, uint2 frameDim, SampleGenerator sg, inout float2 posS, ) +{ + // Forward-mode gradients w.r.t. screen-space position. + DifferentialPair dpPosS = DifferentialPair( { posS, posS }, { float2(1.f, 0.f), float2(0.f, 1.f) }); + + // Zero out any gradients from/to the scene for this Jacobian computation. + SceneQueryAD.Differential dQuery = {}; + dQuery.gradInfo = { SceneGradientFlag.make(GradientMode.None) }; + + // Use the same SampleGenerator for correlated samples. + SampleGenerator sgCopy = sg; + + DifferentialPair dpWarpedPosS; + float kappa = pow(10.f, kLog10vMFConcentrationScreen); + fwd_diff(computeWarpedPrimarySample + )(diffPair(sceneQuery, dQuery), kappa, frameDim, diffPair(posS, float2(0.f)), dpPosS, dpWarpedPosS, sgCopy); + + float2 warpPosS = infinitesimal<2>(dpWarpedPosS.p[0]); // Can use either dpWarpedPosS.p[0] or dpWarpedPosS.p[1]. + float2 warpDiffXPosS = infinitesimal<2>(dpWarpedPosS.d[0]); + float2 warpDiffYPosS = infinitesimal<2>(dpWarpedPosS.d[1]); + + // Compute Jacobian determinant. + float2x2 jacobian = no_diff float2x2::identity() + float2x2(warpDiffXPosS, warpDiffYPosS); + float detJ = determinant(jacobian); + + posS += warpPosS; + return detJ; +} diff --git a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.cpp b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.cpp index c67728005..98144e854 100644 --- a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.cpp +++ b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.cpp @@ -31,38 +31,40 @@ namespace { - const char kShaderFile[] = "RenderPasses/WhittedRayTracer/WhittedRayTracer.rt.slang"; - - const char kMaxBounces[] = "maxBounces"; - const char kTexLODMode[] = "texLODMode"; - const char kRayConeMode[] = "rayConeMode"; - const char kRayConeFilterMode[] = "rayConeFilterMode"; - const char kRayDiffFilterMode[] = "rayDiffFilterMode"; - const char kUseRoughnessToVariance[] = "useRoughnessToVariance"; - - // Ray tracing settings that affect the traversal stack size. - // These should be set as small as possible. - const uint32_t kMaxPayloadSizeBytes = 164; - const uint32_t kMaxAttributeSizeBytes = 8; - const uint32_t kMaxRecursionDepth = 2; - - const ChannelList kOutputChannels = - { - { "color", "gOutputColor", "Output color (sum of direct and indirect)", false, ResourceFormat::RGBA32Float }, - }; +const char kShaderFile[] = "RenderPasses/WhittedRayTracer/WhittedRayTracer.rt.slang"; + +const char kMaxBounces[] = "maxBounces"; +const char kTexLODMode[] = "texLODMode"; +const char kRayConeMode[] = "rayConeMode"; +const char kRayConeFilterMode[] = "rayConeFilterMode"; +const char kRayDiffFilterMode[] = "rayDiffFilterMode"; +const char kUseRoughnessToVariance[] = "useRoughnessToVariance"; + +// Ray tracing settings that affect the traversal stack size. +// These should be set as small as possible. +const uint32_t kMaxPayloadSizeBytes = 164; +const uint32_t kMaxAttributeSizeBytes = 8; +const uint32_t kMaxRecursionDepth = 2; + +const ChannelList kOutputChannels = { + // clang-format off + { "color", "gOutputColor", "Output color (sum of direct and indirect)", false, ResourceFormat::RGBA32Float }, + // clang-format on +}; - const ChannelList kInputChannels = - { - { "posW", "gWorldPosition", "World-space position (xyz) and foreground flag (w)" }, - { "normalW", "gWorldShadingNormal", "World-space shading normal (xyz)" }, - { "tangentW", "gWorldShadingTangent", "World-space shading tangent (xyz) and sign (w)" }, - { "faceNormalW", "gWorldFaceNormal", "Face normal in world space (xyz)", }, - { "texC", "gTextureCoord", "Texture coordinate", }, - { "texGrads", "gTextureGrads", "Texture gradients", true /* optional */ }, - { "mtlData", "gMaterialData", "Material data" }, - { "vbuffer", "gVBuffer", "V-buffer buffer in packed format" }, - }; +const ChannelList kInputChannels = { + // clang-format off + { "posW", "gWorldPosition", "World-space position (xyz) and foreground flag (w)" }, + { "normalW", "gWorldShadingNormal", "World-space shading normal (xyz)" }, + { "tangentW", "gWorldShadingTangent", "World-space shading tangent (xyz) and sign (w)" }, + { "faceNormalW", "gWorldFaceNormal", "Face normal in world space (xyz)", }, + { "texC", "gTextureCoord", "Texture coordinate", }, + { "texGrads", "gTextureGrads", "Texture gradients", true /* optional */ }, + { "mtlData", "gMaterialData", "Material data" }, + { "vbuffer", "gVBuffer", "V-buffer buffer in packed format" }, + // clang-format on }; +}; // namespace extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) { @@ -75,19 +77,25 @@ void WhittedRayTracer::registerBindings(pybind11::module& m) pybind11::class_> pass(m, "WhittedRayTracer"); } -WhittedRayTracer::WhittedRayTracer(ref pDevice, const Properties& props) - : RenderPass(pDevice) +WhittedRayTracer::WhittedRayTracer(ref pDevice, const Properties& props) : RenderPass(pDevice) { // Parse dictionary. for (const auto& [key, value] : props) { - if (key == kMaxBounces) mMaxBounces = (uint32_t)value; - else if (key == kTexLODMode) mTexLODMode = value; - else if (key == kRayConeMode) mRayConeMode = value; - else if (key == kRayConeFilterMode) mRayConeFilterMode = value; - else if (key == kRayDiffFilterMode) mRayDiffFilterMode = value; - else if (key == kUseRoughnessToVariance) mUseRoughnessToVariance = value; - else logWarning("Unknown property '{}' in a WhittedRayTracer properties.", key); + if (key == kMaxBounces) + mMaxBounces = (uint32_t)value; + else if (key == kTexLODMode) + mTexLODMode = value; + else if (key == kRayConeMode) + mRayConeMode = value; + else if (key == kRayConeFilterMode) + mRayConeFilterMode = value; + else if (key == kRayDiffFilterMode) + mRayDiffFilterMode = value; + else if (key == kUseRoughnessToVariance) + mUseRoughnessToVariance = value; + else + logWarning("Unknown property '{}' in a WhittedRayTracer properties.", key); } // Create a sample generator. @@ -134,14 +142,17 @@ void WhittedRayTracer::execute(RenderContext* pRenderContext, const RenderData& for (auto it : kOutputChannels) { Texture* pDst = renderData.getTexture(it.name).get(); - if (pDst) pRenderContext->clearTexture(pDst); + if (pDst) + pRenderContext->clearTexture(pDst); } return; } - if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) + // Check for scene changes that require shader recompilation. + if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::RecompileNeeded) || + is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) { - throw RuntimeError("WhittedRayTracer: This render pass does not support scene geometry changes."); + FALCOR_THROW("This render pass does not support scene changes that require shader recompilation."); } setStaticParams(mTracer.pProgram.get()); @@ -153,7 +164,8 @@ void WhittedRayTracer::execute(RenderContext* pRenderContext, const RenderData& // Prepare program vars. This may trigger shader compilation. // The program should have all necessary defines set at this point. - if (!mTracer.pVars) prepareVars(); + if (!mTracer.pVars) + prepareVars(); FALCOR_ASSERT(mTracer.pVars); // Get dimensions of ray dispatch. @@ -175,8 +187,10 @@ void WhittedRayTracer::execute(RenderContext* pRenderContext, const RenderData& var[desc.texname] = renderData.getTexture(desc.name); } }; - for (auto channel : kInputChannels) bind(channel); - for (auto channel : kOutputChannels) bind(channel); + for (auto channel : kInputChannels) + bind(channel); + for (auto channel : kOutputChannels) + bind(channel); // Spawn the rays. mpScene->raytrace(pRenderContext, mTracer.pProgram.get(), mTracer.pVars, uint3(targetDim, 1)); @@ -259,7 +273,7 @@ void WhittedRayTracer::setScene(RenderContext* pRenderContext, const ref& } // Create ray tracing program. - RtProgram::Desc desc; + ProgramDesc desc; desc.addShaderModules(mpScene->getShaderModules()); desc.addShaderLibrary(kShaderFile); desc.addTypeConformances(mpScene->getTypeConformances()); @@ -272,10 +286,12 @@ void WhittedRayTracer::setScene(RenderContext* pRenderContext, const ref& sbt->setRayGen(desc.addRayGen("rayGen")); sbt->setMiss(0, desc.addMiss("scatterMiss")); sbt->setMiss(1, desc.addMiss("shadowMiss")); - sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), desc.addHitGroup("scatterClosestHit", "scatterAnyHit")); + sbt->setHitGroup( + 0, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), desc.addHitGroup("scatterClosestHit", "scatterAnyHit") + ); sbt->setHitGroup(1, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), desc.addHitGroup("", "shadowAnyHit")); - mTracer.pProgram = RtProgram::create(mpDevice, desc, mpScene->getSceneDefines()); + mTracer.pProgram = Program::create(mpDevice, desc, mpScene->getSceneDefines()); } } @@ -292,10 +308,10 @@ void WhittedRayTracer::prepareVars() // Bind utility classes into shared data. auto var = mTracer.pVars->getRootVar(); - mpSampleGenerator->setShaderData(var); + mpSampleGenerator->bindShaderData(var); } -void WhittedRayTracer::setStaticParams(RtProgram* pProgram) const +void WhittedRayTracer::setStaticParams(Program* pProgram) const { DefineList defines; defines.add("MAX_BOUNCES", std::to_string(mMaxBounces)); diff --git a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.h b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.h index 41862b0ec..7eac14e9a 100644 --- a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.h +++ b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.h @@ -30,19 +30,20 @@ #include "RenderGraph/RenderPass.h" #include "RenderGraph/RenderPassHelpers.h" #include "Utils/Sampling/SampleGenerator.h" -#include "Rendering/Materials/TexLODTypes.slang" // Using the enum with Mip0, RayCones, etc +#include "Rendering/Materials/TexLODTypes.slang" // Using the enum with Mip0, RayCones, etc #include "WhittedRayTracerTypes.slang" using namespace Falcor; -/** Whitted ray tracer. - - This pass implements the simplest possible Whitted ray tracer. - - The render pass serves as an example and testbed for texture LOD. - The scene materials are overridden to add ideal specular reflection - and refraction components. Unbiased rendering should not be expected. -*/ +/** + * Whitted ray tracer. + * + * This pass implements the simplest possible Whitted ray tracer. + * + * The render pass serves as an example and testbed for texture LOD. + * The scene materials are overridden to add ideal specular reflection + * and refraction components. Unbiased rendering should not be expected. + */ class WhittedRayTracer : public RenderPass { public: @@ -76,28 +77,42 @@ class WhittedRayTracer : public RenderPass private: void prepareVars(); - void setStaticParams(RtProgram* pProgram) const; + void setStaticParams(Program* pProgram) const; // Internal state - ref mpScene; ///< Current scene. - ref mpSampleGenerator; ///< GPU sample generator. - - uint mMaxBounces = 3; ///< Max number of indirect bounces (0 = none). - TexLODMode mTexLODMode = TexLODMode::Mip0; ///< Which texture LOD mode to use. - RayConeMode mRayConeMode = RayConeMode::Combo; ///< Which variant of ray cones to use. - RayFootprintFilterMode mRayConeFilterMode = RayFootprintFilterMode::Isotropic; ///< Which filter mode to use for ray cones. - RayFootprintFilterMode mRayDiffFilterMode = RayFootprintFilterMode::Isotropic; ///< Which filter mode to use for ray differentials. - bool mVisualizeSurfaceSpread = false; ///< Visualize surface spread angle at the first hit for the ray cones methods. - bool mUseRoughnessToVariance = false; ///< Use roughness to variance to grow ray cones based on BDSF roughness. - bool mUseFresnelAsBRDF = false; ///< Use Fresnel term as BRDF (instead of hacky throughput adjustment) + + /// Current scene. + ref mpScene; + /// GPU sample generator. + ref mpSampleGenerator; + + /// Max number of indirect bounces (0 = none). + uint mMaxBounces = 3; + /// Which texture LOD mode to use. + TexLODMode mTexLODMode = TexLODMode::Mip0; + /// Which variant of ray cones to use. + RayConeMode mRayConeMode = RayConeMode::Combo; + /// Which filter mode to use for ray cones. + RayFootprintFilterMode mRayConeFilterMode = RayFootprintFilterMode::Isotropic; + /// Which filter mode to use for ray differentials. + RayFootprintFilterMode mRayDiffFilterMode = RayFootprintFilterMode::Isotropic; + /// Visualize surface spread angle at the first hit for the ray cones methods. + bool mVisualizeSurfaceSpread = false; + /// Use roughness to variance to grow ray cones based on BDSF roughness. + bool mUseRoughnessToVariance = false; + /// Use Fresnel term as BRDF (instead of hacky throughput adjustment) + bool mUseFresnelAsBRDF = false; + // Runtime data - uint mFrameCount = 0; ///< Frame count since scene was loaded. - bool mOptionsChanged = false; + + /// Frame count since scene was loaded. + uint mFrameCount = 0; + bool mOptionsChanged = false; // Ray tracing program. struct { - ref pProgram; + ref pProgram; ref pBindingTable; ref pVars; } mTracer; diff --git a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.rt.slang b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.rt.slang index 3013829a2..bb4a29dd5 100644 --- a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.rt.slang +++ b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracer.rt.slang @@ -26,25 +26,26 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Whitted ray tracer. - - The purpose is to use it as a baseline for showing how to implement different - texture level-of-detail filtering methods. - - The host sets the following defines: - - MAX_BOUNCES Maximum number of indirect bounces (0 means no indirect). - USE_ANALYTIC_LIGHTS Nonzero if Falcor's analytic lights should be used. - USE_EMISSIVE_LIGHTS Nonzero if emissive geometry should be used as lights. - USE_ENV_LIGHT Nonzero if env map is available and should be used as light source. - USE_ENV_BACKGROUND Nonzero if env map is available and should be used as background. - TEX_LOD_MODE Could be either of Mip0, RayCones, RayDiffs. - RAY_CONE_MODE Could be either Combo or Unified. - VISUALIZE_SURFACE_SPREAD True if we want to visualize the surface spread angle for the ray cone methods. - USE_ROUGHNESS_TO_VARIANCE True if we want to grow the size of the ray cones base on the BRDF roughness. - USE_FRESNEL_AS_BRDF True if we want to use Fresnel term as BRDF. - is_valid_ 1 if optional I/O buffer with this name should be used. -*/ +/** + * Whitted ray tracer. + * + * The purpose is to use it as a baseline for showing how to implement different + * texture level-of-detail filtering methods. + * + * The host sets the following defines: + * + * MAX_BOUNCES Maximum number of indirect bounces (0 means no indirect). + * USE_ANALYTIC_LIGHTS Nonzero if Falcor's analytic lights should be used. + * USE_EMISSIVE_LIGHTS Nonzero if emissive geometry should be used as lights. + * USE_ENV_LIGHT Nonzero if env map is available and should be used as light source. + * USE_ENV_BACKGROUND Nonzero if env map is available and should be used as background. + * TEX_LOD_MODE Could be either of Mip0, RayCones, RayDiffs. + * RAY_CONE_MODE Could be either Combo or Unified. + * VISUALIZE_SURFACE_SPREAD True if we want to visualize the surface spread angle for the ray cone methods. + * USE_ROUGHNESS_TO_VARIANCE True if we want to grow the size of the ray cones base on the BRDF roughness. + * USE_FRESNEL_AS_BRDF True if we want to use Fresnel term as BRDF. + * is_valid_ 1 if optional I/O buffer with this name should be used. + */ #include "Utils/Math/MathConstants.slangh" @@ -59,9 +60,9 @@ import WhittedRayTracerTypes; cbuffer CB { - uint gFrameCount; // Frame count since scene was loaded. - uint gPRNGDimension; // First available PRNG dimension. - float gScreenSpacePixelSpreadAngle; // The angle an "average" pixel spans from camera (texLOD). + uint gFrameCount; // Frame count since scene was loaded. + uint gPRNGDimension; // First available PRNG dimension. + float gScreenSpacePixelSpreadAngle; // The angle an "average" pixel spans from camera (texLOD). } // Inputs @@ -70,9 +71,9 @@ Texture2D gWorldShadingNormal; Texture2D gWorldShadingTangent; Texture2D gWorldFaceNormal; Texture2D gTextureCoord; -Texture2D gTextureGrads; // Optional -Texture2D gMaterialData; -Texture2D gVBuffer; // Optional +Texture2D gTextureGrads; // Optional +Texture2D gMaterialData; +Texture2D gVBuffer; // Optional // Outputs RWTexture2D gOutputColor; @@ -94,8 +95,9 @@ static const bool kUseRoughnessToVariance = USE_ROUGHNESS_TO_VARIANCE; static const bool kVisualizeSurfaceSpread = VISUALIZE_SURFACE_SPREAD; static const bool kUseFresnelAsBRDF = USE_FRESNEL_AS_BRDF; -/** Payload for shadow ray. -*/ +/** + * Payload for shadow ray. + */ struct ShadowRayData { bool visible; @@ -105,37 +107,52 @@ float roughnessToSpread(float roughness) { const float ggxAlpha = roughness * roughness; const float s = ggxAlpha * ggxAlpha; - const float sigma2 = (s / max(0.0001f, (1.0f - s))) * 0.5f; + const float sigma2 = (s / max(0.0001f, (1.0f - s))) * 0.5f; return 2.0f * sqrt(sigma2); } -/** Returns the shading normal flipped for backfacing hits on double-sided materials. - This reproduces the legacy behavior before the conventions were updated. -*/ +/** + * Returns the shading normal flipped for backfacing hits on double-sided materials. + * This reproduces the legacy behavior before the conventions were updated. + */ float3 getFlippedShadingNormal(const ShadingData sd) { return !sd.frontFacing && sd.mtl.isDoubleSided() ? -sd.frame.N : sd.frame.N; } -/** Payload for scatter ray (128B when ray cones are used, 164B when ray diffs are used). -*/ +/** + * Payload for scatter ray (128B when ray cones are used, 164B when ray diffs are used). + */ struct ScatterRayData { - float3 radiance; ///< Accumulated outgoing radiance from path. - bool terminated; ///< Set to true when path is terminated. - bool hasHitRefractiveMaterial; ///> Whether the path has hit some refractive material on the way. - float3 thp; ///< Current path throughput. This is updated at each path vertex. - uint pathLength; ///< Path length in number of path segments (0 at origin, 1 at first secondary hit, etc.). Max 2^31. - float3 origin; ///< Next path segment origin. - float3 direction; ///< Next path segment direction. - // only one (at most) of RayCone and RayDiff will be used (none, if we use Mip0). - RayCone rayCone; ///< Ray cone (2 floats) for texture LOD. - RayDiff rayDiff; ///< Ray differential (12 floats) for texture LOD. - - SampleGenerator sg; ///< Per-ray state for the sample generator (up to 16B). - - /** Initializes ray payload with default parameters. - */ + /// Accumulated outgoing radiance from path. + float3 radiance; + /// Set to true when path is terminated. + bool terminated; + /// Whether the path has hit some refractive material on the way. + bool hasHitRefractiveMaterial; + /// Current path throughput. This is updated at each path vertex. + float3 thp; + /// Path length in number of path segments (0 at origin, 1 at first secondary hit, etc.). Max 2^31. + uint pathLength; + /// Next path segment origin. + float3 origin; + /// Next path segment direction. + float3 direction; + + // only one (at most) of RayCone and RayDiff will be used (none, if we use Mip0). + + /// Ray cone (2 floats) for texture LOD. + RayCone rayCone; + /// Ray differential (12 floats) for texture LOD. + RayDiff rayDiff; + + /// Per-ray state for the sample generator (up to 16B). + SampleGenerator sg; + + /** + * Initializes ray payload with default parameters. + */ __init(SampleGenerator sg) { this.terminated = false; @@ -151,11 +168,12 @@ struct ScatterRayData } }; -/** Helper to create a texture sampler instance. - The method for computing texture level-of-detail depends on the configuration. - \param[in] pixel Current pixel coordinates. - \return Texture sampler instance. -*/ +/** + * Helper to create a texture sampler instance. + * The method for computing texture level-of-detail depends on the configuration. + * @param[in] pixel Current pixel coordinates. + * @return Texture sampler instance. + */ ITextureSampler createTextureSampler(const uint2 pixel) { if (is_valid(gTextureGrads)) @@ -169,8 +187,9 @@ ITextureSampler createTextureSampler(const uint2 pixel) } } -/** Helper to load G-buffer data and prepare shading data. -*/ +/** + * Helper to load G-buffer data and prepare shading data. + */ ShadingData loadShadingData(const uint2 pixel, const float3 posW, const float3 rayDir, const ITextureSampler lod) { // Load G-buffer data. @@ -192,9 +211,10 @@ ShadingData loadShadingData(const uint2 pixel, const float3 posW, const float3 r return gScene.materials.prepareShadingData(v, materialID, -rayDir, lod); } -/** Helper for computing relatice index of refraction (eta) at a hit. - This is computed based on the IoRs of the media outside and inside the material. -*/ +/** + * Helper for computing relatice index of refraction (eta) at a hit. + * This is computed based on the IoRs of the media outside and inside the material. + */ float computeEta(const ShadingData sd) { float insideIoR = gScene.materials.evalIoR(sd.materialID); @@ -202,12 +222,13 @@ float computeEta(const ShadingData sd) return eta; } -/** Traces a shadow ray towards a light source. - \param[in] origin Ray origin for the shadow ray. - \param[in] dir Direction from shading point towards the light source (normalized). - \param[in] distance Distance to the light source. - \return True if light is visible, false otherwise. -*/ +/** + * Traces a shadow ray towards a light source. + * @param[in] origin Ray origin for the shadow ray. + * @param[in] dir Direction from shading point towards the light source (normalized). + * @param[in] distance Distance to the light source. + * @return True if light is visible, false otherwise. + */ bool traceShadowRay(float3 origin, float3 dir, float distance) { RayDesc ray; @@ -218,14 +239,24 @@ bool traceShadowRay(float3 origin, float3 dir, float distance) ShadowRayData rayData; rayData.visible = false; // Set to true by miss shader if ray is not terminated before. - TraceRay(gScene.rtAccel, RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, 0xff /* instanceInclusionMask */, 1 /* hitIdx */, rayTypeCount, 1 /* missIdx */, ray, rayData); + TraceRay( + gScene.rtAccel, + RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, + 0xff /* instanceInclusionMask */, + 1 /* hitIdx */, + rayTypeCount, + 1 /* missIdx */, + ray, + rayData + ); return rayData.visible; } -/** Traces a scatter ray based on ray parameters stored in the ray payload. - \param[in] rayData Describes the ray parameters. The struct is modified based on the result. -*/ +/** + * Traces a scatter ray based on ray parameters stored in the ray payload. + * @param[in] rayData Describes the ray parameters. The struct is modified based on the result. + */ void traceScatterRay(inout ScatterRayData rayData) { RayDesc ray; @@ -234,18 +265,19 @@ void traceScatterRay(inout ScatterRayData rayData) ray.TMin = 0.f; ray.TMax = kRayTMax; - uint rayFlags = 0; // TODO: Set cull mode from the app. + uint rayFlags = 0; // TODO: Set cull mode from the app. TraceRay(gScene.rtAccel, rayFlags, 0xff /* instanceInclusionMask */, 0 /* hitIdx */, rayTypeCount, 0 /* missIdx */, ray, rayData); } -/** Evaluates the direct illumination from all analytic lights. - This function samples Falcor's light list uniformly with one shadow ray per light. - \param[in] sd Shading data. - \param[in] mi Material instance at the shading point. - \param[in] rayOrigin Ray origin for the shadow ray. - \param[in] sg SampleGenerator object. - \return Outgoing radiance in view direction. -*/ +/** + * Evaluates the direct illumination from all analytic lights. + * This function samples Falcor's light list uniformly with one shadow ray per light. + * @param[in] sd Shading data. + * @param[in] mi Material instance at the shading point. + * @param[in] rayOrigin Ray origin for the shadow ray. + * @param[in] sg SampleGenerator object. + * @return Outgoing radiance in view direction. + */ float3 evalDirectAnalytic(const ShadingData sd, const IMaterialInstance mi, const float3 rayOrigin, inout SampleGenerator sg) { // Use all analytic light sources. @@ -264,22 +296,31 @@ float3 evalDirectAnalytic(const ShadingData sd, const IMaterialInstance mi, cons { // Test visibility by tracing a shadow ray. bool V = traceShadowRay(rayOrigin, ls.dir, ls.distance); - contribution += V ? mi.eval(sd, ls.dir, sg) * ls.Li * invPdf : float3(0); + contribution += V ? mi.eval(sd, ls.dir, sg) * ls.Li * invPdf : float3(0); } } return contribution; } -/** Processes a hit point to generate a reflection ray or terminate. - \param[in] sd Shading data. - \param[in] bsdfProperties BSDF properties. - \param[in] rayOrigin Ray origin for the new ray. - \param[in] rayDir Ray direction for incident ray. - \param[in] normal Surface normal to use for reflection (may be different from the shading normal). - \param[in] tir True if total internal reflection. - \param[in,out] rayData Ray payload. -*/ -void generateReflectionRay(const ShadingData sd, const BSDFProperties bsdfProperties, const float3 rayOrigin, const float3 rayDir, const float3 normal, bool tir, inout ScatterRayData rayData) +/** + * Processes a hit point to generate a reflection ray or terminate. + * @param[in] sd Shading data. + * @param[in] bsdfProperties BSDF properties. + * @param[in] rayOrigin Ray origin for the new ray. + * @param[in] rayDir Ray direction for incident ray. + * @param[in] normal Surface normal to use for reflection (may be different from the shading normal). + * @param[in] tir True if total internal reflection. + * @param[in,out] rayData Ray payload. + */ +void generateReflectionRay( + const ShadingData sd, + const BSDFProperties bsdfProperties, + const float3 rayOrigin, + const float3 rayDir, + const float3 normal, + bool tir, + inout ScatterRayData rayData +) { float3 wo = reflect(rayDir, normal); rayData.origin = rayOrigin; @@ -299,7 +340,7 @@ void generateReflectionRay(const ShadingData sd, const BSDFProperties bsdfProper /*********************** Ray index 0: Scatter ray ************************ */ [shader("miss")] -void scatterMiss(inout ScatterRayData rayData : SV_RayPayload) +void scatterMiss(inout ScatterRayData rayData: SV_RayPayload) { // Ray missed the scene. Mark the ray as terminated. rayData.terminated = true; @@ -323,17 +364,21 @@ void scatterMiss(inout ScatterRayData rayData : SV_RayPayload) } [shader("anyhit")] -void scatterAnyHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) +void scatterAnyHit(inout ScatterRayData rayData: SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs: SV_IntersectionAttributes) { // Alpha test for non-opaque geometry. GeometryInstanceID instanceID = getGeometryInstanceID(); VertexData v = getVertexData(instanceID, PrimitiveIndex(), attribs); const uint materialID = gScene.getMaterialID(instanceID); - if (gScene.materials.alphaTest(v, materialID, 0.f)) IgnoreHit(); + if (gScene.materials.alphaTest(v, materialID, 0.f)) + IgnoreHit(); } [shader("closesthit")] -void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) +void scatterClosestHit( + inout ScatterRayData rayData: SV_RayPayload, + BuiltInTriangleIntersectionAttributes attribs: SV_IntersectionAttributes +) { // Evaluate Falcor's material parameters at the hit point. const float3 rayDir = WorldRayDirection(); @@ -365,7 +410,8 @@ void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTria // Propagate (only) to hit point (not setting any surfaceSpreadAngle -- this is cone in the next propagate call, a few lines down). rayData.rayCone = rayData.rayCone.propagateDistance(hitT); - if (kRayConeFilterMode == RayFootprintFilterMode::Isotropic || (kRayConeFilterMode == RayFootprintFilterMode::AnisotropicWhenRefraction && !rayData.hasHitRefractiveMaterial)) + if (kRayConeFilterMode == RayFootprintFilterMode::Isotropic || + (kRayConeFilterMode == RayFootprintFilterMode::AnisotropicWhenRefraction && !rayData.hasHitRefractiveMaterial)) { float lambda = rayData.rayCone.computeLOD(v.coneTexLODValue, rayDir, v.normalW); lod = ExplicitRayConesLodTextureSampler(lambda); @@ -388,22 +434,37 @@ void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTria } else if (kTexLODMode == TexLODMode::RayDiffs) { - float2 dUVdx, dUVdy; // Ray differential variables for the texture lookup. + float2 dUVdx, dUVdy; // Ray differential variables for the texture lookup. float2 dBarydx, dBarydy; float3 geometricNormal = gScene.getFaceNormalW(instanceID, PrimitiveIndex()); - RayDiff newRayDiff = rayData.rayDiff.propagate(rayOrg, rayDir, hitT, geometricNormal); // Propagate the ray differential to the current hit point. + // Propagate the ray differential to the current hit point. + RayDiff newRayDiff = rayData.rayDiff.propagate(rayOrg, rayDir, hitT, geometricNormal); float3 barycentrics; StaticVertexData triangleVertices[3]; v = getVertexData(instanceID, PrimitiveIndex(), attribs, barycentrics, triangleVertices); - float3 unnormalizedN; // Non-normalized interpolated normal for ray differential scatter. - float3 normals[3]; // Non-normalized normals for ray differential scatter. - - prepareRayDiffAtHitPoint(v, triangleVertices, barycentrics, rayDir, gScene.getWorldMatrix(instanceID), gScene.getInverseTransposeWorldMatrix(instanceID), - newRayDiff, unnormalizedN, normals, dBarydx, dBarydy, dUVdx, dUVdy); - - if (kRayDiffFilterMode == RayFootprintFilterMode::Isotropic || (kRayDiffFilterMode == RayFootprintFilterMode::AnisotropicWhenRefraction && !rayData.hasHitRefractiveMaterial)) + float3 unnormalizedN; // Non-normalized interpolated normal for ray differential scatter. + float3 normals[3]; // Non-normalized normals for ray differential scatter. + + prepareRayDiffAtHitPoint( + v, + triangleVertices, + barycentrics, + rayDir, + gScene.getWorldMatrix(instanceID), + gScene.getInverseTransposeWorldMatrix(instanceID), + newRayDiff, + unnormalizedN, + normals, + dBarydx, + dBarydy, + dUVdx, + dUVdy + ); + + if (kRayDiffFilterMode == RayFootprintFilterMode::Isotropic || + (kRayDiffFilterMode == RayFootprintFilterMode::AnisotropicWhenRefraction && !rayData.hasHitRefractiveMaterial)) { // When using ExplicitRayDiffsIsotropicTextureSampler, the texture sampler will compute a single lambda for texture LOD // using the dUVdx and dUVdy. @@ -423,7 +484,9 @@ void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTria } else { - if (!refractRayDifferential(newRayDiff, rayDir, unnormalizedN, normalize(unnormalizedN), dBarydx, dBarydy, normals, eta, refractedRayDir)) + if (!refractRayDifferential( + newRayDiff, rayDir, unnormalizedN, normalize(unnormalizedN), dBarydx, dBarydy, normals, eta, refractedRayDir + )) { reflectRayDifferential(newRayDiff, rayDir, unnormalizedN, v.normalW, dBarydx, dBarydy, normals); tir = true; @@ -467,7 +530,9 @@ void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTria } else // Refraction { - if (!refractRayCone(rayData.rayCone, rayOrg, rayDir, sd.posW, getFlippedShadingNormal(sd), surfaceSpreadAngle, eta, refractedRayDir)) + if (!refractRayCone( + rayData.rayCone, rayOrg, rayDir, sd.posW, getFlippedShadingNormal(sd), surfaceSpreadAngle, eta, refractedRayDir + )) { rayData.rayCone = rayData.rayCone.addToSpreadAngle(surfaceSpreadAngle); tir = true; @@ -489,7 +554,7 @@ void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTria } // Compute ray origin for new rays spawned from the hit. - float3 rayOriginAtHitPoint = sd.computeNewRayOrigin(); + float3 rayOriginAtHitPoint = sd.computeRayOrigin(); // Add contribution of direct light from analytic lights. if (kUseAnalyticLights) @@ -507,13 +572,15 @@ void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTria else { rayData.direction = refractedRayDir; - rayData.origin = sd.computeNewRayOrigin(false); + rayData.origin = sd.computeRayOrigin(false); if (kUseFresnelAsBRDF) { - rayData.thp *= 1.0f - evalFresnelSchlick(bsdfProperties.specularReflectance, 1.0f, max(0.00001f, dot(getFlippedShadingNormal(sd), -rayDir))); + rayData.thp *= + 1.0f - + evalFresnelSchlick(bsdfProperties.specularReflectance, 1.0f, max(0.00001f, dot(getFlippedShadingNormal(sd), -rayDir))); // There's convinient 'evalFresnelDielectric' we could use here, but it's somewhat darker (worse for development) - //rayData.thp *= 1.0f - evalFresnelDielectric(eta, dot(getFlippedShadingNormal(sd), -rayDir)); + // rayData.thp *= 1.0f - evalFresnelDielectric(eta, dot(getFlippedShadingNormal(sd), -rayDir)); } else { @@ -524,7 +591,7 @@ void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTria } rayData.pathLength++; - if (max(max(rayData.thp.x, rayData.thp.y), rayData.thp.z) < 0.01f) // TODO: this is a hack to terminate rays + if (max(max(rayData.thp.x, rayData.thp.y), rayData.thp.z) < 0.01f) // TODO: this is a hack to terminate rays { rayData.terminated = true; return; @@ -534,33 +601,35 @@ void scatterClosestHit(inout ScatterRayData rayData : SV_RayPayload, BuiltInTria /************************** Ray index 1: Shadow ray ************************ */ [shader("miss")] -void shadowMiss(inout ShadowRayData rayData : SV_RayPayload) +void shadowMiss(inout ShadowRayData rayData: SV_RayPayload) { // The miss shader is executed if the ray misses all geometry. Mark as visible. rayData.visible = true; } [shader("anyhit")] -void shadowAnyHit(inout ShadowRayData rayData : SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs : SV_IntersectionAttributes) +void shadowAnyHit(inout ShadowRayData rayData: SV_RayPayload, BuiltInTriangleIntersectionAttributes attribs: SV_IntersectionAttributes) { // Alpha test for non-opaque geometry. GeometryInstanceID instanceID = getGeometryInstanceID(); VertexData v = getVertexData(instanceID, PrimitiveIndex(), attribs); const uint materialID = gScene.getMaterialID(instanceID); - if (gScene.materials.alphaTest(v, materialID, 0.f)) IgnoreHit(); + if (gScene.materials.alphaTest(v, materialID, 0.f)) + IgnoreHit(); } /** ******************************** RayGen ******************************** */ -/** This is the entry point for the Whitted ray tracer. - - One path per pixel is generated, which is traced into the scene. - The path tracer is written as a for-loop over path segments. - - Built-in light sources (point, directional, ..) are sampled explicitly at each - path vertex. The contributions from area lights (env map and mesh lights) - are explicitly added by the scatter ray hit/miss shaders. -*/ +/** + * This is the entry point for the Whitted ray tracer. + * + * One path per pixel is generated, which is traced into the scene. + * The path tracer is written as a for-loop over path segments. + * + * Built-in light sources (point, directional, ..) are sampled explicitly at each + * path vertex. The contributions from area lights (env map and mesh lights) + * are explicitly added by the scatter ray hit/miss shaders. + */ [shader("raygeneration")] void rayGen() { @@ -569,7 +638,7 @@ void rayGen() float3 outColor = float3(0.0f); - const float3 nonNormalizedRayDir = gScene.camera.computeNonNormalizedRayDirPinhole(launchIndex, launchDim); // Used by ray diffs. + const float3 nonNormalizedRayDir = gScene.camera.computeNonNormalizedRayDirPinhole(launchIndex, launchDim); // Used by ray diffs. const float3 rayDir = normalize(nonNormalizedRayDir); const float4 worldPos = gWorldPosition[launchIndex]; @@ -589,10 +658,11 @@ void rayGen() // Advance the generator to the first available dimension. // TODO: This is potentially expensive. We may want to store/restore the state from memory if it becomes a problem. - for (uint i = 0; i < gPRNGDimension; i++) sampleNext1D(sg); + for (uint i = 0; i < gPRNGDimension; i++) + sampleNext1D(sg); // Compute ray origin for new rays spawned from the G-buffer. - const float3 rayOriginAtHitPoint = sd.computeNewRayOrigin(); + const float3 rayOriginAtHitPoint = sd.computeRayOrigin(); // Always output directly emitted light, independent of whether emissive materials are treated as light sources or not. outColor += bsdfProperties.emission; @@ -624,7 +694,9 @@ void rayGen() const float3 barycentrics = triangleHit.getBarycentricWeights(); float2 txcoords[3], dBarydx, dBarydy, dUVdx, dUVdy; - StaticVertexData vertices[3] = { gScene.getVertex(vertexIndices[0]), gScene.getVertex(vertexIndices[1]), gScene.getVertex(vertexIndices[2]) }; + StaticVertexData vertices[3] = { + gScene.getVertex(vertexIndices[0]), gScene.getVertex(vertexIndices[1]), gScene.getVertex(vertexIndices[2]) + }; float curvature = gScene.computeCurvatureIsotropicFirstHit(triangleHit.instanceID, triangleHit.primitiveIndex, rayDir); @@ -632,13 +704,20 @@ void rayGen() { RayDiff rayDiff; float3 dDdx, dDdy, unnormalizedN, normals[3], dNdx, dNdy, edge1, edge2; - prepareVerticesForRayDiffs(rayDir, vertices, worldMat, worldInvTransposeMat, barycentrics, edge1, edge2, normals, unnormalizedN, txcoords); - computeRayDirectionDifferentials(nonNormalizedRayDir, gScene.camera.data.cameraU, gScene.camera.data.cameraV, launchDim, dDdx, dDdy); - RayDiff rd = RayDiff(float3(0.f), float3(0.f), dDdx, dDdy); // Init ray diff. dOdx = 0 , dOdy = 0, and the directions are from above. - rayDiff = rd.propagate(worldPos.xyz, rayDir, hitT, getFlippedShadingNormal(sd)); // Propagate the ray differential to the current hit point. + prepareVerticesForRayDiffs( + rayDir, vertices, worldMat, worldInvTransposeMat, barycentrics, edge1, edge2, normals, unnormalizedN, txcoords + ); + computeRayDirectionDifferentials( + nonNormalizedRayDir, gScene.camera.data.cameraU, gScene.camera.data.cameraV, launchDim, dDdx, dDdy + ); + // Init ray diff. dOdx = 0 , dOdy = 0, and the directions are from above. + RayDiff rd = RayDiff(float3(0.f), float3(0.f), dDdx, dDdy); + // Propagate the ray differential to the current hit point. + rayDiff = rd.propagate(worldPos.xyz, rayDir, hitT, getFlippedShadingNormal(sd)); computeBarycentricDifferentials(rayDiff, rayDir, edge1, edge2, sd.faceN, dBarydx, dBarydy); computeNormalDifferentials(rayDiff, unnormalizedN, dBarydx, dBarydy, normals, dNdx, dNdy); - float3 right = normalize(gScene.camera.computeNonNormalizedRayDirPinhole(launchIndex + uint2(1, 0), launchDim)) - rayDir; + float3 right = + normalize(gScene.camera.computeNonNormalizedRayDirPinhole(launchIndex + uint2(1, 0), launchDim)) - rayDir; float3 up = normalize(gScene.camera.computeNonNormalizedRayDirPinhole(launchIndex + uint2(0, 1), launchDim)) - rayDir; surfaceSpreadAngle = computeScreenSpaceSurfaceSpreadAngle(right, up, dNdx, dNdy); } @@ -646,7 +725,9 @@ void rayGen() { float curvature = gScene.computeCurvatureIsotropicFirstHit(triangleHit.instanceID, triangleHit.primitiveIndex, rayDir); float rayConeWidth = hitT * gScreenSpacePixelSpreadAngle; - surfaceSpreadAngle = computeSpreadAngleFromCurvatureIso(curvature, hitT * gScreenSpacePixelSpreadAngle, rayDir, getFlippedShadingNormal(sd)); + surfaceSpreadAngle = computeSpreadAngleFromCurvatureIso( + curvature, hitT * gScreenSpacePixelSpreadAngle, rayDir, getFlippedShadingNormal(sd) + ); } #if USE_ROUGHNESS_TO_VARIANCE @@ -666,12 +747,15 @@ void rayGen() rayData.rayCone = rc.propagateDistance(hitT); if (eta == 1.0f) // Reflection { - rayData.rayCone = rayData.rayCone.addToSpreadAngle(2.0f * surfaceSpreadAngle); // Reflection needs a factor of 2.0 (see RTG1 article on texture LOD). + // Reflection needs a factor of 2.0 (see RTG1 article on texture LOD). + rayData.rayCone = rayData.rayCone.addToSpreadAngle(2.0f * surfaceSpreadAngle); } else // Refraction { float3 hitPoint = rayOrg + rayDir * hitT; - if (!refractRayCone(rayData.rayCone, rayOrg, rayDir, hitPoint, getFlippedShadingNormal(sd), surfaceSpreadAngle, eta, refractedRayDir)) + if (!refractRayCone( + rayData.rayCone, rayOrg, rayDir, hitPoint, getFlippedShadingNormal(sd), surfaceSpreadAngle, eta, refractedRayDir + )) { rayData.rayCone = rayData.rayCone.addToSpreadAngle(surfaceSpreadAngle); tir = true; @@ -686,11 +770,15 @@ void rayGen() const float3 rayOrg = gScene.camera.getPosition(); const float hitT = length(rayOrg - worldPos.xyz); - computeRayDirectionDifferentials(nonNormalizedRayDir, gScene.camera.data.cameraU, gScene.camera.data.cameraV, launchDim, dDdx, dDdy); + computeRayDirectionDifferentials( + nonNormalizedRayDir, gScene.camera.data.cameraU, gScene.camera.data.cameraV, launchDim, dDdx, dDdy + ); - rayData.rayDiff = RayDiff(float3(0.f), float3(0.f), dDdx, dDdy); // Init ray diff. dOdx = 0 , dOdy = 0, and the directions are from above. + // Init ray diff. dOdx = 0 , dOdy = 0, and the directions are from above. + rayData.rayDiff = RayDiff(float3(0.f), float3(0.f), dDdx, dDdy); - rayData.rayDiff = rayData.rayDiff.propagate(worldPos.xyz, rayDir, hitT, sd.faceN); // Propagate the ray differential to the current hit point. + // Propagate the ray differential to the current hit point. + rayData.rayDiff = rayData.rayDiff.propagate(worldPos.xyz, rayDir, hitT, sd.faceN); const HitInfo hit = HitInfo(gVBuffer[launchIndex]); if (hit.isValid() && hit.getType() == HitType::Triangle) @@ -703,8 +791,14 @@ void rayGen() float3 unnormalizedN, normals[3], dNdx, dNdy, edge1, edge2; float2 txcoords[3], dBarydx, dBarydy, dUVdx, dUVdy; - StaticVertexData vertices[3] = { gScene.getVertex(vertexIndices[0]), gScene.getVertex(vertexIndices[1]), gScene.getVertex(vertexIndices[2]) }; - prepareVerticesForRayDiffs(rayDir, vertices, worldMat, worldInvTransposeMat, barycentrics, edge1, edge2, normals, unnormalizedN, txcoords); + StaticVertexData vertices[3] = { + gScene.getVertex(vertexIndices[0]), + gScene.getVertex(vertexIndices[1]), + gScene.getVertex(vertexIndices[2]), + }; + prepareVerticesForRayDiffs( + rayDir, vertices, worldMat, worldInvTransposeMat, barycentrics, edge1, edge2, normals, unnormalizedN, txcoords + ); computeBarycentricDifferentials(rayData.rayDiff, rayDir, edge1, edge2, sd.faceN, dBarydx, dBarydy); @@ -714,7 +808,17 @@ void rayGen() } else { - if (!refractRayDifferential(rayData.rayDiff, rayDir, unnormalizedN, normalize(unnormalizedN), dBarydx, dBarydy, normals, eta, refractedRayDir)) + if (!refractRayDifferential( + rayData.rayDiff, + rayDir, + unnormalizedN, + normalize(unnormalizedN), + dBarydx, + dBarydy, + normals, + eta, + refractedRayDir + )) { reflectRayDifferential(rayData.rayDiff, rayDir, unnormalizedN, normalize(unnormalizedN), dBarydx, dBarydy, normals); tir = true; @@ -738,14 +842,16 @@ void rayGen() else { rayData.direction = refractedRayDir; - rayData.origin = sd.computeNewRayOrigin(false); + rayData.origin = sd.computeRayOrigin(false); if (kUseFresnelAsBRDF) { - rayData.thp *= 1.0f - evalFresnelSchlick(bsdfProperties.specularReflectance, 1.0f, max(0.00001f, dot(getFlippedShadingNormal(sd), -rayDir))); + rayData.thp *= + 1.0f - + evalFresnelSchlick(bsdfProperties.specularReflectance, 1.0f, max(0.00001f, dot(getFlippedShadingNormal(sd), -rayDir))); // There's convenient 'evalFresnelDielectric' we could use here, but it's somewhat darker (worse for development) - //rayData.thp *= 1.0f - evalFresnelDielectric(eta, dot(getFlippedShadingNormal(sd), -rayDir)); + // rayData.thp *= 1.0f - evalFresnelDielectric(eta, dot(getFlippedShadingNormal(sd), -rayDir)); } else { @@ -771,7 +877,7 @@ void rayGen() // Background pixel. if (kUseEnvBackground) { - float lod = 0.0f; // TexLODMode::Mip0. + float lod = 0.0f; // TexLODMode::Mip0. if (kTexLODMode == TexLODMode::RayCones) { lod = computeEnvironmentMapLOD(gScreenSpacePixelSpreadAngle, gScene.envMap.envMap); @@ -779,7 +885,9 @@ void rayGen() else if (kTexLODMode == TexLODMode::RayDiffs) { float3 dDdx, dDdy; - computeRayDirectionDifferentials(nonNormalizedRayDir, gScene.camera.data.cameraU, gScene.camera.data.cameraV, launchDim, dDdx, dDdy); + computeRayDirectionDifferentials( + nonNormalizedRayDir, gScene.camera.data.cameraU, gScene.camera.data.cameraV, launchDim, dDdx, dDdy + ); lod = computeEnvironmentMapLOD(dDdx, dDdy, gScene.envMap.envMap); } outColor = gScene.envMap.eval(rayDir, lod); diff --git a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracerTypes.slang b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracerTypes.slang index 61472bbe4..1a83e3004 100644 --- a/Source/RenderPasses/WhittedRayTracer/WhittedRayTracerTypes.slang +++ b/Source/RenderPasses/WhittedRayTracer/WhittedRayTracerTypes.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,17 +26,19 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ -/** Enums for WhittedTracer -*/ +/** + * Enums for WhittedTracer + */ #pragma once #include "Utils/HostDeviceShared.slangh" BEGIN_NAMESPACE_FALCOR -/** This enum is shared between CPU/GPU. - It enumerates the different the different filter modes for ray cones. -*/ +/** + * This enum is shared between CPU/GPU. + * It enumerates the different the different filter modes for ray cones. + */ enum class RayFootprintFilterMode : uint32_t { Isotropic = 0, @@ -44,11 +46,14 @@ enum class RayFootprintFilterMode : uint32_t AnisotropicWhenRefraction = 2 }; -FALCOR_ENUM_INFO(RayFootprintFilterMode, { - { RayFootprintFilterMode::Isotropic, "Isotropic" }, - { RayFootprintFilterMode::Anisotropic, "Anisotropic" }, - { RayFootprintFilterMode::AnisotropicWhenRefraction, "AnisotropicWhenRefraction" }, -}); +FALCOR_ENUM_INFO( + RayFootprintFilterMode, + { + { RayFootprintFilterMode::Isotropic, "Isotropic" }, + { RayFootprintFilterMode::Anisotropic, "Anisotropic" }, + { RayFootprintFilterMode::AnisotropicWhenRefraction, "AnisotropicWhenRefraction" }, + } +); FALCOR_ENUM_REGISTER(RayFootprintFilterMode); END_NAMESPACE_FALCOR diff --git a/Source/Samples/CudaInterop/CMakeLists.txt b/Source/Samples/CudaInterop/CMakeLists.txt index 50486f2a5..9168531bc 100644 --- a/Source/Samples/CudaInterop/CMakeLists.txt +++ b/Source/Samples/CudaInterop/CMakeLists.txt @@ -9,8 +9,6 @@ target_sources(CudaInterop PRIVATE CopySurface.h CudaInterop.cpp CudaInterop.h - FalcorCUDA.cpp - FalcorCUDA.h ) set_target_properties(CudaInterop PROPERTIES CUDA_SEPARABLE_COMPILATION ON) diff --git a/Source/Samples/CudaInterop/CudaInterop.cpp b/Source/Samples/CudaInterop/CudaInterop.cpp index d67d47e24..9f16dc6c9 100644 --- a/Source/Samples/CudaInterop/CudaInterop.cpp +++ b/Source/Samples/CudaInterop/CudaInterop.cpp @@ -27,10 +27,14 @@ **************************************************************************/ #include "CudaInterop.h" #include "CopySurface.h" +#include "Core/AssetResolver.h" +#include "Utils/CudaUtils.h" + +FALCOR_EXPORT_D3D12_AGILITY_SDK namespace { -const std::string kTextureFilename = "smoke-puff.png"; +const std::filesystem::path kTexturePath = "test_images/smoke_puff.png"; } CudaInterop::CudaInterop(const SampleAppConfig& config) : SampleApp(config) {} @@ -39,19 +43,21 @@ CudaInterop::~CudaInterop() {} void CudaInterop::onLoad(RenderContext* pRenderContext) { - // Initializes the CUDA driver API. - if (!FalcorCUDA::initCUDA()) - throw RuntimeError("CUDA driver API initialization failed."); + // Initialize CUDA device + if (!getDevice()->initCudaDevice()) + FALCOR_THROW("Failed to initialize CUDA device."); // Create our input and output textures - mpInputTex = Texture::createFromFile(getDevice(), kTextureFilename, false, false, ResourceBindFlags::Shared); + mpInputTex = Texture::createFromFile( + getDevice(), AssetResolver::getDefaultResolver().resolvePath(kTexturePath), false, false, ResourceBindFlags::Shared + ); if (!mpInputTex) - throw RuntimeError("Failed to load texture '{}'", kTextureFilename); + FALCOR_THROW("Failed to load texture '{}'", kTexturePath); mWidth = mpInputTex->getWidth(); mHeight = mpInputTex->getHeight(); - mpOutputTex = Texture::create2D( - getDevice(), mWidth, mHeight, mpInputTex->getFormat(), 1, 1, nullptr, ResourceBindFlags::Shared | ResourceBindFlags::ShaderResource + mpOutputTex = getDevice()->createTexture2D( + mWidth, mHeight, mpInputTex->getFormat(), 1, 1, nullptr, ResourceBindFlags::Shared | ResourceBindFlags::ShaderResource ); // Define our usage flags and then map the textures to CUDA surfaces. Surface values of 0 @@ -59,13 +65,13 @@ void CudaInterop::onLoad(RenderContext* pRenderContext) // mapTextureToSurface() can only be called once per resource. uint32_t usageFlags = cudaArrayColorAttachment; - mInputSurf = FalcorCUDA::mapTextureToSurface(mpInputTex, usageFlags); + mInputSurf = cuda_utils::mapTextureToSurface(mpInputTex, usageFlags); if (mInputSurf == 0) - throw RuntimeError("Input texture to surface mapping failed"); + FALCOR_THROW("Input texture to surface mapping failed"); - mOutputSurf = FalcorCUDA::mapTextureToSurface(mpOutputTex, usageFlags); + mOutputSurf = cuda_utils::mapTextureToSurface(mpOutputTex, usageFlags); if (mOutputSurf == 0) - throw RuntimeError("Output texture to surface mapping failed"); + FALCOR_THROW("Output texture to surface mapping failed"); } void CudaInterop::onFrameRender(RenderContext* pRenderContext, const ref& pTargetFbo) @@ -80,7 +86,7 @@ void CudaInterop::onFrameRender(RenderContext* pRenderContext, const ref& p pRenderContext->blit(mpOutputTex->getSRV(), pTargetFbo->getRenderTargetView(0)); } -int main(int argc, char** argv) +int runMain(int argc, char** argv) { SampleAppConfig config; config.windowDesc.title = "Falcor-Cuda Interop"; @@ -89,3 +95,8 @@ int main(int argc, char** argv) CudaInterop cudaInterop(config); return cudaInterop.run(); } + +int main(int argc, char** argv) +{ + return catchAndReportAllExceptions([&] { return runMain(argc, argv); }); +} diff --git a/Source/Samples/CudaInterop/CudaInterop.h b/Source/Samples/CudaInterop/CudaInterop.h index 2361684e7..a97f5db41 100644 --- a/Source/Samples/CudaInterop/CudaInterop.h +++ b/Source/Samples/CudaInterop/CudaInterop.h @@ -28,8 +28,8 @@ #pragma once #include #include "Falcor.h" -#include "FalcorCUDA.h" #include "Core/SampleApp.h" +#include using namespace Falcor; diff --git a/Source/Samples/CudaInterop/FalcorCUDA.cpp b/Source/Samples/CudaInterop/FalcorCUDA.cpp deleted file mode 100644 index 6901b49b0..000000000 --- a/Source/Samples/CudaInterop/FalcorCUDA.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#include "FalcorCUDA.h" -#include "Core/API/Device.h" -#include - -#define CU_CHECK_SUCCESS(x) \ - do \ - { \ - CUresult result = x; \ - if (result != CUDA_SUCCESS) \ - { \ - const char* msg; \ - cuGetErrorName(result, &msg); \ - reportError("CUDA Error: " #x " failed with error " + std::string(msg)); \ - return 0; \ - } \ - } while (0) - -#define CUDA_CHECK_SUCCESS(x) \ - do \ - { \ - cudaError_t result = x; \ - if (result != cudaSuccess) \ - { \ - reportError("CUDA Error: " #x " failed with error " + std::string(cudaGetErrorString(result))); \ - return 0; \ - } \ - } while (0) - -using namespace Falcor; - -namespace -{ -uint32_t gNodeMask; -CUdevice gCudaDevice; -CUcontext gCudaContext; -CUstream gCudaStream; -} // namespace - -namespace FalcorCUDA -{ - -bool initCUDA() -{ - CU_CHECK_SUCCESS(cuInit(0)); - int32_t firstGPUID = -1; - cudaDeviceProp prop; - int32_t count; - cudaError_t err = cudaGetDeviceCount(&count); - - for (int32_t i = 0; i < count; ++i) - { - err = cudaGetDeviceProperties(&prop, i); - if (prop.major >= 3) - { - firstGPUID = i; - break; - } - } - - if (firstGPUID < 0) - { - reportError("No CUDA 10 compatible GPU found"); - return false; - } - gNodeMask = prop.luidDeviceNodeMask; - CUDA_CHECK_SUCCESS(cudaSetDevice(firstGPUID)); - CU_CHECK_SUCCESS(cuDeviceGet(&gCudaDevice, firstGPUID)); - CU_CHECK_SUCCESS(cuCtxCreate(&gCudaContext, 0, gCudaDevice)); - CU_CHECK_SUCCESS(cuStreamCreate(&gCudaStream, CU_STREAM_DEFAULT)); - return true; -} - -bool importTextureToMipmappedArray(Falcor::ref pTex, cudaMipmappedArray_t& mipmappedArray, uint32_t cudaUsageFlags) -{ - SharedResourceApiHandle sharedHandle = pTex->getSharedApiHandle(); - if (sharedHandle == NULL) - { - reportError("FalcorCUDA::importTextureToMipmappedArray - texture shared handle creation failed"); - return false; - } - - cudaExternalMemoryHandleDesc externalMemoryHandleDesc; - memset(&externalMemoryHandleDesc, 0, sizeof(externalMemoryHandleDesc)); - - externalMemoryHandleDesc.type = cudaExternalMemoryHandleTypeD3D12Resource; - externalMemoryHandleDesc.handle.win32.handle = sharedHandle; - externalMemoryHandleDesc.size = pTex->getTextureSizeInBytes(); - externalMemoryHandleDesc.flags = cudaExternalMemoryDedicated; - - cudaExternalMemory_t externalMemory; - CUDA_CHECK_SUCCESS(cudaImportExternalMemory(&externalMemory, &externalMemoryHandleDesc)); - - // Map mipmapped array onto external memory - cudaExternalMemoryMipmappedArrayDesc mipDesc; - memset(&mipDesc, 0, sizeof(mipDesc)); - auto format = pTex->getFormat(); - mipDesc.formatDesc.x = getNumChannelBits(format, 0); - mipDesc.formatDesc.y = getNumChannelBits(format, 1); - mipDesc.formatDesc.z = getNumChannelBits(format, 2); - mipDesc.formatDesc.w = getNumChannelBits(format, 3); - mipDesc.formatDesc.f = (getFormatType(format) == FormatType::Float) ? cudaChannelFormatKindFloat : cudaChannelFormatKindUnsigned; - mipDesc.extent.depth = 1; - mipDesc.extent.width = pTex->getWidth(); - mipDesc.extent.height = pTex->getHeight(); - mipDesc.flags = cudaUsageFlags; - mipDesc.numLevels = 1; - - CUDA_CHECK_SUCCESS(cudaExternalMemoryGetMappedMipmappedArray(&mipmappedArray, externalMemory, &mipDesc)); - return true; -} - -cudaSurfaceObject_t mapTextureToSurface(ref pTex, uint32_t cudaUsageFlags) -{ - // Create a mipmapped array from the texture - cudaMipmappedArray_t mipmap; - if (!importTextureToMipmappedArray(pTex, mipmap, cudaUsageFlags)) - { - reportError("Failed to import texture into a mipmapped array"); - return 0; - } - - // Grab level 0 - cudaArray_t cudaArray; - CUDA_CHECK_SUCCESS(cudaGetMipmappedArrayLevel(&cudaArray, mipmap, 0)); - - // Create cudaSurfObject_t from CUDA array - cudaResourceDesc resDesc; - memset(&resDesc, 0, sizeof(resDesc)); - resDesc.res.array.array = cudaArray; - resDesc.resType = cudaResourceTypeArray; - - cudaSurfaceObject_t surface; - CUDA_CHECK_SUCCESS(cudaCreateSurfaceObject(&surface, &resDesc)); - return surface; -} - -} // namespace FalcorCUDA diff --git a/Source/Samples/HelloDXR/HelloDXR.cpp b/Source/Samples/HelloDXR/HelloDXR.cpp index a6c32d095..af972e448 100644 --- a/Source/Samples/HelloDXR/HelloDXR.cpp +++ b/Source/Samples/HelloDXR/HelloDXR.cpp @@ -29,6 +29,8 @@ #include "Utils/Math/FalcorMath.h" #include "Utils/UI/TextRenderer.h" +FALCOR_EXPORT_D3D12_AGILITY_SDK + static const float4 kClearColor(0.38f, 0.52f, 0.10f, 1); static const std::string kDefaultScene = "Arcade/Arcade.pyscene"; @@ -40,7 +42,7 @@ void HelloDXR::onLoad(RenderContext* pRenderContext) { if (getDevice()->isFeatureSupported(Device::SupportedFeatures::Raytracing) == false) { - throw RuntimeError("Device does not support raytracing!"); + FALCOR_THROW("Device does not support raytracing!"); } loadScene(kDefaultScene, getTargetFbo().get()); @@ -58,9 +60,8 @@ void HelloDXR::onResize(uint32_t width, uint32_t height) mpCamera->setAspectRatio(aspectRatio); } - mpRtOut = Texture::create2D( - getDevice(), width, height, ResourceFormat::RGBA16Float, 1, 1, nullptr, - Resource::BindFlags::UnorderedAccess | Resource::BindFlags::ShaderResource + mpRtOut = getDevice()->createTexture2D( + width, height, ResourceFormat::RGBA16Float, 1, 1, nullptr, ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource ); } @@ -72,7 +73,9 @@ void HelloDXR::onFrameRender(RenderContext* pRenderContext, const ref& pTar { Scene::UpdateFlags updates = mpScene->update(pRenderContext, getGlobalClock().getTime()); if (is_set(updates, Scene::UpdateFlags::GeometryChanged)) - throw RuntimeError("This sample does not support scene geometry changes."); + FALCOR_THROW("This sample does not support scene geometry changes."); + if (is_set(updates, Scene::UpdateFlags::RecompileNeeded)) + FALCOR_THROW("This sample does not support scene changes that require shader recompilation."); if (mRayTrace) renderRT(pRenderContext, pTargetFbo); @@ -143,7 +146,7 @@ void HelloDXR::loadScene(const std::filesystem::path& path, const Fbo* pTargetFb // Create raster pass. // This utility wraps the creation of the program and vars, and sets the necessary scene defines. - Program::Desc rasterProgDesc; + ProgramDesc rasterProgDesc; rasterProgDesc.addShaderModules(shaderModules); rasterProgDesc.addShaderLibrary("Samples/HelloDXR/HelloDXR.3d.slang").vsEntry("vsMain").psEntry("psMain"); rasterProgDesc.addTypeConformances(typeConformances); @@ -151,17 +154,17 @@ void HelloDXR::loadScene(const std::filesystem::path& path, const Fbo* pTargetFb mpRasterPass = RasterPass::create(getDevice(), rasterProgDesc, defines); // We'll now create a raytracing program. To do that we need to setup two things: - // - A program description (RtProgram::Desc). This holds all shader entry points, compiler flags, macro defintions, + // - A program description (ProgramDesc). This holds all shader entry points, compiler flags, macro defintions, // etc. // - A binding table (RtBindingTable). This maps shaders to geometries in the scene, and sets the ray generation and // miss shaders. // - // After setting up these, we can create the RtProgram and associated RtProgramVars that holds the variable/resource - // bindings. The RtProgram can be reused for different scenes, but RtProgramVars needs to binding table which is + // After setting up these, we can create the Program and associated RtProgramVars that holds the variable/resource + // bindings. The Program can be reused for different scenes, but RtProgramVars needs to binding table which is // Scene-specific and needs to be re-created when switching scene. In this example, we re-create both the program // and vars when a scene is loaded. - RtProgram::Desc rtProgDesc; + ProgramDesc rtProgDesc; rtProgDesc.addShaderModules(shaderModules); rtProgDesc.addShaderLibrary("Samples/HelloDXR/HelloDXR.rt.slang"); rtProgDesc.addTypeConformances(typeConformances); @@ -180,7 +183,7 @@ void HelloDXR::loadScene(const std::filesystem::path& path, const Fbo* pTargetFb sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), primary); sbt->setHitGroup(1, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), shadow); - mpRaytraceProgram = RtProgram::create(getDevice(), rtProgDesc, defines); + mpRaytraceProgram = Program::create(getDevice(), rtProgDesc, defines); mpRtVars = RtProgramVars::create(getDevice(), mpRaytraceProgram, sbt); } @@ -217,7 +220,7 @@ void HelloDXR::renderRT(RenderContext* pRenderContext, const ref& pTargetFb pRenderContext->blit(mpRtOut->getSRV(), pTargetFbo->getRenderTargetView(0)); } -int main(int argc, char** argv) +int runMain(int argc, char** argv) { SampleAppConfig config; config.windowDesc.title = "HelloDXR"; @@ -226,3 +229,8 @@ int main(int argc, char** argv) HelloDXR helloDXR(config); return helloDXR.run(); } + +int main(int argc, char** argv) +{ + return catchAndReportAllExceptions([&]() { return runMain(argc, argv); }); +} diff --git a/Source/Samples/HelloDXR/HelloDXR.h b/Source/Samples/HelloDXR/HelloDXR.h index 1e1cf3397..7514b8223 100644 --- a/Source/Samples/HelloDXR/HelloDXR.h +++ b/Source/Samples/HelloDXR/HelloDXR.h @@ -56,7 +56,7 @@ class HelloDXR : public SampleApp ref mpRasterPass; - ref mpRaytraceProgram; + ref mpRaytraceProgram; ref mpRtVars; ref mpRtOut; diff --git a/Source/Samples/MultiSampling/MultiSampling.3d.slang b/Source/Samples/MultiSampling/MultiSampling.3d.slang index 3b7f8be95..e25dae070 100644 --- a/Source/Samples/MultiSampling/MultiSampling.3d.slang +++ b/Source/Samples/MultiSampling/MultiSampling.3d.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -25,7 +25,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ - struct VSIn { float2 pos : POSITION; diff --git a/Source/Samples/MultiSampling/MultiSampling.cpp b/Source/Samples/MultiSampling/MultiSampling.cpp index eca2c76e4..5f3d90f8f 100644 --- a/Source/Samples/MultiSampling/MultiSampling.cpp +++ b/Source/Samples/MultiSampling/MultiSampling.cpp @@ -27,6 +27,8 @@ **************************************************************************/ #include "MultiSampling.h" +FALCOR_EXPORT_D3D12_AGILITY_SDK + namespace { const uint32_t kTriangleCount = 16; @@ -52,8 +54,8 @@ void MultiSampling::onLoad(RenderContext* pRenderContext) vertices[i * 3 + 1] = float2(cos(theta0), sin(theta0)) * 0.75f; vertices[i * 3 + 2] = float2(cos(theta1), sin(theta1)) * 0.75f; } - auto vertexBuffer = Buffer::createTyped( - getDevice(), kTriangleCount * 3, ResourceBindFlags::ShaderResource | ResourceBindFlags::Vertex, Buffer::CpuAccess::None, vertices + auto vertexBuffer = getDevice()->createTypedBuffer( + kTriangleCount * 3, ResourceBindFlags::ShaderResource | ResourceBindFlags::Vertex, MemoryType::DeviceLocal, vertices ); // Create vertex layout @@ -67,9 +69,8 @@ void MultiSampling::onLoad(RenderContext* pRenderContext) // Create FBO mpFbo = Fbo::create(getDevice()); - ref tex = Texture::create2DMS( - getDevice(), 128, 128, ResourceFormat::RGBA32Float, kSampleCount, 1, - Resource::BindFlags::ShaderResource | Resource::BindFlags::RenderTarget + ref tex = getDevice()->createTexture2DMS( + 128, 128, ResourceFormat::RGBA32Float, kSampleCount, 1, ResourceBindFlags::ShaderResource | ResourceBindFlags::RenderTarget ); mpFbo->attachColorTarget(tex, 0); } @@ -85,7 +86,7 @@ void MultiSampling::onFrameRender(RenderContext* pRenderContext, const ref& pRenderContext->blit(mpFbo->getColorTexture(0)->getSRV(), pTargetFbo->getRenderTargetView(0)); } -int main(int argc, char** argv) +int runMain(int argc, char** argv) { SampleAppConfig config; config.windowDesc.width = 1024; @@ -97,3 +98,8 @@ int main(int argc, char** argv) MultiSampling multiSample(config); return multiSample.run(); } + +int main(int argc, char** argv) +{ + return catchAndReportAllExceptions([&]() { return runMain(argc, argv); }); +} diff --git a/Source/Samples/SampleAppTemplate/SampleAppTemplate.cpp b/Source/Samples/SampleAppTemplate/SampleAppTemplate.cpp index ff906e434..654daf1e9 100644 --- a/Source/Samples/SampleAppTemplate/SampleAppTemplate.cpp +++ b/Source/Samples/SampleAppTemplate/SampleAppTemplate.cpp @@ -91,7 +91,7 @@ void SampleAppTemplate::onHotReload(HotReloadFlags reloaded) // } -int main(int argc, char** argv) +int runMain(int argc, char** argv) { SampleAppConfig config; config.windowDesc.title = "Falcor Project Template"; @@ -100,3 +100,8 @@ int main(int argc, char** argv) SampleAppTemplate project(config); return project.run(); } + +int main(int argc, char** argv) +{ + return catchAndReportAllExceptions([&]() { return runMain(argc, argv); }); +} diff --git a/Source/Samples/ShaderToy/ShaderToy.cpp b/Source/Samples/ShaderToy/ShaderToy.cpp index fd1f16051..784531ddb 100644 --- a/Source/Samples/ShaderToy/ShaderToy.cpp +++ b/Source/Samples/ShaderToy/ShaderToy.cpp @@ -27,6 +27,8 @@ **************************************************************************/ #include "ShaderToy.h" +FALCOR_EXPORT_D3D12_AGILITY_SDK + ShaderToy::ShaderToy(const SampleAppConfig& config) : SampleApp(config) {} ShaderToy::~ShaderToy() {} @@ -48,8 +50,8 @@ void ShaderToy::onLoad(RenderContext* pRenderContext) // Texture sampler Sampler::Desc samplerDesc; - samplerDesc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear).setMaxAnisotropy(8); - mpLinearSampler = Sampler::create(getDevice(), samplerDesc); + samplerDesc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Linear).setMaxAnisotropy(8); + mpLinearSampler = getDevice()->createSampler(samplerDesc); // Load shaders mpMainPass = FullScreenPass::create(getDevice(), "Samples/ShaderToy/Toy.ps.slang"); @@ -73,7 +75,7 @@ void ShaderToy::onFrameRender(RenderContext* pRenderContext, const ref& pTa mpMainPass->execute(pRenderContext, pTargetFbo); } -int main(int argc, char** argv) +int runMain(int argc, char** argv) { SampleAppConfig config; config.windowDesc.width = 1280; @@ -85,3 +87,8 @@ int main(int argc, char** argv) ShaderToy shaderToy(config); return shaderToy.run(); } + +int main(int argc, char** argv) +{ + return catchAndReportAllExceptions([&]() { return runMain(argc, argv); }); +} diff --git a/Source/Samples/Visualization2D/Visualization2D.cpp b/Source/Samples/Visualization2D/Visualization2D.cpp index 5060a417a..f7072825c 100644 --- a/Source/Samples/Visualization2D/Visualization2D.cpp +++ b/Source/Samples/Visualization2D/Visualization2D.cpp @@ -27,6 +27,8 @@ **************************************************************************/ #include "Visualization2D.h" +FALCOR_EXPORT_D3D12_AGILITY_SDK + namespace { const char kMarkerShaderFile[] = "Samples/Visualization2D/Visualization2d.ps.slang"; @@ -160,7 +162,7 @@ void Visualization2D::createRenderPass() } } -int main(int argc, char** argv) +int runMain(int argc, char** argv) { SampleAppConfig config; config.windowDesc.title = "Falcor 2D Visualization"; @@ -172,3 +174,8 @@ int main(int argc, char** argv) Visualization2D visualization2D(config); return visualization2D.run(); } + +int main(int argc, char** argv) +{ + return catchAndReportAllExceptions([&]() { return runMain(argc, argv); }); +} diff --git a/Source/Samples/Visualization2D/VoxelNormals.ps.slang b/Source/Samples/Visualization2D/VoxelNormals.ps.slang index c5c8a5f40..88b4d3256 100644 --- a/Source/Samples/Visualization2D/VoxelNormals.ps.slang +++ b/Source/Samples/Visualization2D/VoxelNormals.ps.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -255,12 +255,38 @@ float4 main(float2 texC: TEXCOORD) : SV_TARGET for (int x = 0; x < numVoxels.x; x++) { drawBox( - coords, x, y, P[x][y], P[x + 1][y], P[x][y + 1], P[x + 1][y + 1], sdf[x][y], sdf[x + 1][y], sdf[x][y + 1], - sdf[x + 1][y + 1], lineSize * 2, blendRadius, false, color + coords, + x, + y, + P[x][y], + P[x + 1][y], + P[x][y + 1], + P[x + 1][y + 1], + sdf[x][y], + sdf[x + 1][y], + sdf[x][y + 1], + sdf[x + 1][y + 1], + lineSize * 2, + blendRadius, + false, + color ); drawBox( - coords, x, y, X[x][y], X[x + 1][y], X[x][y + 1], X[x + 1][y + 1], sdf[x][y], sdf[x + 1][y], sdf[x][y + 1], - sdf[x + 1][y + 1], lineSize * 2, blendRadius, true, color + coords, + x, + y, + X[x][y], + X[x + 1][y], + X[x][y + 1], + X[x + 1][y + 1], + sdf[x][y], + sdf[x + 1][y], + sdf[x][y + 1], + sdf[x + 1][y + 1], + lineSize * 2, + blendRadius, + true, + color ); } } @@ -273,30 +299,45 @@ float4 main(float2 texC: TEXCOORD) : SV_TARGET ); if (warpedStandardNormal.x != 0.0 && warpedStandardNormal.y != 0.0) color = sdfDraw( - SDF2DVector(warpedPosition, warpedPosition + normalize(warpedStandardNormal) * side, lineSize * 2.0, side * 0.075), coords, red, - color, blendRadius + SDF2DVector(warpedPosition, warpedPosition + normalize(warpedStandardNormal) * side, lineSize * 2.0, side * 0.075), + coords, + red, + color, + blendRadius ); if (iShowBorderLines != 0) { color = sdfDraw( - SDF2DRoundedLine(P[0][0] + float2(side * 0.5, 0.0), P[0][numVoxels.y] + float2(side * 0.5, 0.0), lineSize * 0.25), coords, - lightgray, color, blendRadius + SDF2DRoundedLine(P[0][0] + float2(side * 0.5, 0.0), P[0][numVoxels.y] + float2(side * 0.5, 0.0), lineSize * 0.25), + coords, + lightgray, + color, + blendRadius ); color = sdfDraw( SDF2DRoundedLine( P[numVoxels.x - 1][0] + float2(side * 0.5, 0.0), P[numVoxels.x - 1][numVoxels.y] + float2(side * 0.5, 0.0), lineSize * 0.25 ), - coords, lightgray, color, blendRadius + coords, + lightgray, + color, + blendRadius ); color = sdfDraw( - SDF2DRoundedLine(P[0][0] + float2(0.0, side * 0.5), P[numVoxels.x][0] + float2(0.0, side * 0.5), lineSize * 0.25), coords, - lightgray, color, blendRadius + SDF2DRoundedLine(P[0][0] + float2(0.0, side * 0.5), P[numVoxels.x][0] + float2(0.0, side * 0.5), lineSize * 0.25), + coords, + lightgray, + color, + blendRadius ); color = sdfDraw( SDF2DRoundedLine( P[0][numVoxels.y - 1] + float2(0.0, side * 0.5), P[numVoxels.x][numVoxels.y - 1] + float2(0.0, side * 0.5), lineSize * 0.25 ), - coords, lightgray, color, blendRadius + coords, + lightgray, + color, + blendRadius ); } if (iShowBoxAroundPoint != 0) diff --git a/Source/Tools/FalcorTest/CMakeLists.txt b/Source/Tools/FalcorTest/CMakeLists.txt index d5febbee9..325501370 100644 --- a/Source/Tools/FalcorTest/CMakeLists.txt +++ b/Source/Tools/FalcorTest/CMakeLists.txt @@ -5,8 +5,10 @@ target_sources(FalcorTest PRIVATE Tests/Core/AftermathTests.cpp Tests/Core/AftermathTests.cs.slang + Tests/Core/AssetResolverTests.cpp + Tests/Core/BlitTests.cpp + Tests/Core/BlitTests.cs.slang Tests/Core/BufferAccessTests.cpp - Tests/Core/BufferAccessTests.cs.slang Tests/Core/BufferTests.cpp Tests/Core/BufferTests.cs.slang Tests/Core/ConstantBufferTests.cpp @@ -37,6 +39,12 @@ target_sources(FalcorTest PRIVATE Tests/DebugPasses/InvalidPixelDetectionTests.cpp + Tests/DiffRendering/SceneGradientsTest.cpp + Tests/DiffRendering/SceneGradientsTest.cs.slang + + Tests/DiffRendering/Material/DiffMaterialTests.cpp + Tests/DiffRendering/Material/DiffMaterialTests.cs.slang + Tests/Platform/LockFileTests.cpp Tests/Platform/MemoryMappedFileTests.cpp Tests/Platform/MonitorInfoTests.cpp diff --git a/Source/Tools/FalcorTest/FalcorTest.cpp b/Source/Tools/FalcorTest/FalcorTest.cpp index 6094f3b4b..97e7e387e 100644 --- a/Source/Tools/FalcorTest/FalcorTest.cpp +++ b/Source/Tools/FalcorTest/FalcorTest.cpp @@ -25,6 +25,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ +#include "Core/Error.h" #include "Utils/StringUtils.h" #include "Testing/UnitTest.h" @@ -38,7 +39,7 @@ using namespace Falcor; FALCOR_EXPORT_D3D12_AGILITY_SDK -int main(int argc, char** argv) +int runMain(int argc, char** argv) { args::ArgumentParser parser("Falcor unit tests."); parser.helpParams.programName = "FalcorTest"; @@ -161,12 +162,17 @@ int main(int argc, char** argv) } } - try - { - return unittest::runTests(options); - } - catch (const std::exception& e) - { - reportFatalError("FalcorTest crashed unexpectedly...\n" + std::string(e.what()), false); - } + // Setup error diagnostics to not break on exceptions. + // We might have unit tests that check for exceptions, so we want to throw + // them without breaking into the debugger in order to let tests run + // uninterrupted with the debugger attached. The test framework will + // break into the debugger when a test conditions is not met. + setErrorDiagnosticFlags(getErrorDiagnosticFlags() & ~ErrorDiagnosticFlags::BreakOnThrow); + + return unittest::runTests(options); +} + +int main(int argc, char** argv) +{ + return catchAndReportAllExceptions([&]() { return runMain(argc, argv); }); } diff --git a/Source/Tools/FalcorTest/Tests/Core/AftermathTests.cpp b/Source/Tools/FalcorTest/Tests/Core/AftermathTests.cpp index 14366661b..6fdc69a4d 100644 --- a/Source/Tools/FalcorTest/Tests/Core/AftermathTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/AftermathTests.cpp @@ -49,7 +49,7 @@ GPU_TEST(AftermathCatchTDR) ctx.getRenderContext()->addAftermathMarker("after"); - pDevice->flushAndSync(); + pDevice->wait(); // At this point we have lost the device, so submitting another dispatch should terminate the application. ctx.runProgram(1024, 1024); diff --git a/Source/Tools/FalcorTest/Tests/Core/AssetResolverTests.cpp b/Source/Tools/FalcorTest/Tests/Core/AssetResolverTests.cpp new file mode 100644 index 000000000..0c36c675a --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/Core/AssetResolverTests.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Testing/UnitTest.h" +#include "Core/AssetResolver.h" +#include + +namespace Falcor +{ +static const std::filesystem::path kTestRoot = getRuntimeDirectory() / "asset_test_root"; + +static const std::vector kTestFiles = { + "media1/asset1", + "media2/asset1", + "media2/asset2", + "media3/asset1", + "media3/asset2", + "media3/asset3", + "media4/textures/mip0.png", + "media4/textures/mip1.png", + "media4/textures/mip2.png", + "media4/textures/mip3.png", +}; + +static void createTestFiles(UnitTestContext& ctx) +{ + std::error_code err; + std::filesystem::create_directories(kTestRoot, err); + ASSERT(!err); + for (const auto& file : kTestFiles) + { + std::filesystem::path path = kTestRoot / file; + std::filesystem::create_directories(path.parent_path(), err); + ASSERT(!err); + std::ofstream(path).close(); + ASSERT(std::filesystem::exists(path)); + } +} + +static void removeTestFiles(UnitTestContext& ctx) +{ + std::filesystem::remove_all(kTestRoot); + ASSERT(!std::filesystem::exists(kTestRoot)); +} + +CPU_TEST(AssetResolver) +{ + createTestFiles(ctx); + + const std::filesystem::path unresolved; + + // Test resolving absolute paths. + { + AssetResolver resolver; + + resolver.addSearchPath(kTestRoot / "media1"); + EXPECT_EQ(resolver.resolvePath(kTestRoot / "media2/asset1"), kTestRoot / "media2/asset1"); + + auto resolved = resolver.resolvePathPattern(kTestRoot / "media4/textures", R"(mip[0-9]\.png)"); + EXPECT_EQ(resolved.size(), 4); + std::sort(resolved.begin(), resolved.end()); + EXPECT_EQ(resolved[0], kTestRoot / "media4/textures/mip0.png"); + EXPECT_EQ(resolved[1], kTestRoot / "media4/textures/mip1.png"); + EXPECT_EQ(resolved[2], kTestRoot / "media4/textures/mip2.png"); + EXPECT_EQ(resolved[3], kTestRoot / "media4/textures/mip3.png"); + } + + // Test resolving relative paths to working directory. + { + AssetResolver resolver; + + resolver.addSearchPath(kTestRoot / "media1"); + EXPECT_EQ( + resolver.resolvePath(std::filesystem::relative(kTestRoot / "media2/asset1", std::filesystem::current_path())), + kTestRoot / "media2/asset1" + ); + + auto resolved = resolver.resolvePathPattern( + resolver.resolvePath(std::filesystem::relative(kTestRoot / "media4/textures", std::filesystem::current_path())), + R"(mip[0-9]\.png)" + ); + EXPECT_EQ(resolved.size(), 4); + std::sort(resolved.begin(), resolved.end()); + EXPECT_EQ(resolved[0], kTestRoot / "media4/textures/mip0.png"); + EXPECT_EQ(resolved[1], kTestRoot / "media4/textures/mip1.png"); + EXPECT_EQ(resolved[2], kTestRoot / "media4/textures/mip2.png"); + EXPECT_EQ(resolved[3], kTestRoot / "media4/textures/mip3.png"); + } + + // Test resolving with search paths. + { + AssetResolver resolver; + + resolver.addSearchPath(kTestRoot / "media1"); + EXPECT_EQ(resolver.resolvePath("asset1"), kTestRoot / "media1/asset1"); + EXPECT_EQ(resolver.resolvePath("asset2"), unresolved); + EXPECT_EQ(resolver.resolvePath("asset3"), unresolved); + EXPECT_EQ(resolver.resolvePath("asset4"), unresolved); + + resolver.addSearchPath(kTestRoot / "media2"); + EXPECT_EQ(resolver.resolvePath("asset1"), kTestRoot / "media1/asset1"); + EXPECT_EQ(resolver.resolvePath("asset2"), kTestRoot / "media2/asset2"); + EXPECT_EQ(resolver.resolvePath("asset3"), unresolved); + EXPECT_EQ(resolver.resolvePath("asset4"), unresolved); + + resolver.addSearchPath(kTestRoot / "media3"); + EXPECT_EQ(resolver.resolvePath("asset1"), kTestRoot / "media1/asset1"); + EXPECT_EQ(resolver.resolvePath("asset2"), kTestRoot / "media2/asset2"); + EXPECT_EQ(resolver.resolvePath("asset3"), kTestRoot / "media3/asset3"); + EXPECT_EQ(resolver.resolvePath("asset4"), unresolved); + } + + // Test resolving patterns with search paths. + { + AssetResolver resolver; + + resolver.addSearchPath(kTestRoot / "media4"); + auto resolved = resolver.resolvePathPattern("textures", R"(mip[0-9]\.png)"); + EXPECT_EQ(resolved.size(), 4); + std::sort(resolved.begin(), resolved.end()); + EXPECT_EQ(resolved[0], kTestRoot / "media4/textures/mip0.png"); + EXPECT_EQ(resolved[1], kTestRoot / "media4/textures/mip1.png"); + EXPECT_EQ(resolved[2], kTestRoot / "media4/textures/mip2.png"); + EXPECT_EQ(resolved[3], kTestRoot / "media4/textures/mip3.png"); + + resolved = resolver.resolvePathPattern("textures", R"(mip[0-9]\.png)", true); + EXPECT_EQ(resolved.size(), 1); + EXPECT( + resolved[0] == kTestRoot / "media4/textures/mip0.png" || resolved[0] == kTestRoot / "media4/textures/mip1.png" || + resolved[0] == kTestRoot / "media4/textures/mip2.png" || resolved[0] == kTestRoot / "media4/textures/mip3.png" + ); + } + + // Test asset categories. + { + AssetResolver resolver; + + resolver.addSearchPath(kTestRoot / "media3", SearchPathPriority::Last, AssetCategory::Any); + resolver.addSearchPath(kTestRoot / "media2", SearchPathPriority::Last, AssetCategory::Scene); + resolver.addSearchPath(kTestRoot / "media1", SearchPathPriority::Last, AssetCategory::Texture); + + EXPECT_EQ(resolver.resolvePath("asset1", AssetCategory::Any), kTestRoot / "media3/asset1"); + EXPECT_EQ(resolver.resolvePath("asset1", AssetCategory::Scene), kTestRoot / "media2/asset1"); + EXPECT_EQ(resolver.resolvePath("asset1", AssetCategory::Texture), kTestRoot / "media1/asset1"); + + EXPECT_EQ(resolver.resolvePath("asset2", AssetCategory::Any), kTestRoot / "media3/asset2"); + EXPECT_EQ(resolver.resolvePath("asset2", AssetCategory::Scene), kTestRoot / "media2/asset2"); + EXPECT_EQ(resolver.resolvePath("asset2", AssetCategory::Texture), kTestRoot / "media3/asset2"); + + EXPECT_EQ(resolver.resolvePath("asset3", AssetCategory::Any), kTestRoot / "media3/asset3"); + EXPECT_EQ(resolver.resolvePath("asset3", AssetCategory::Scene), kTestRoot / "media3/asset3"); + EXPECT_EQ(resolver.resolvePath("asset3", AssetCategory::Texture), kTestRoot / "media3/asset3"); + } + + // Test search path priorities. + { + AssetResolver resolver; + + resolver.addSearchPath(kTestRoot / "media1"); + EXPECT_EQ(resolver.resolvePath("asset1"), kTestRoot / "media1/asset1"); + resolver.addSearchPath(kTestRoot / "media2", SearchPathPriority::Last); + EXPECT_EQ(resolver.resolvePath("asset1"), kTestRoot / "media1/asset1"); + resolver.addSearchPath(kTestRoot / "media3", SearchPathPriority::First); + EXPECT_EQ(resolver.resolvePath("asset1"), kTestRoot / "media3/asset1"); + } + + removeTestFiles(ctx); +} + +} // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/BlitTests.cpp b/Source/Tools/FalcorTest/Tests/Core/BlitTests.cpp index 512d56d64..63014de61 100644 --- a/Source/Tools/FalcorTest/Tests/Core/BlitTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/BlitTests.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,6 +27,8 @@ **************************************************************************/ #include "Testing/UnitTest.h" +#include + namespace Falcor { namespace @@ -90,9 +92,10 @@ void testBlit(GPUUnitTestContext& ctx, const uint2 srcDim, const uint32_t scale) // Create textures and perform blit. ResourceFormat format = std::is_same_v ? ResourceFormat::RGBA32Float : ResourceFormat::RGBA32Uint; - auto pSrc = Texture::create2D(srcDim.x, srcDim.y, format, 1, 1, srcData.data(), ResourceBindFlags::ShaderResource); - auto pDst = - Texture::create2D(dstDim.x, dstDim.y, format, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::RenderTarget); + auto pSrc = ctx.getDevice()->createTexture2D(srcDim.x, srcDim.y, format, 1, 1, srcData.data(), ResourceBindFlags::ShaderResource); + auto pDst = ctx.getDevice()->createTexture2D( + dstDim.x, dstDim.y, format, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::RenderTarget + ); ctx.getRenderContext()->blit(pSrc->getSRV(), pDst->getRTV()); diff --git a/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cpp b/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cpp index 045f02f9f..2d09f1107 100644 --- a/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/BufferAccessTests.cpp @@ -31,116 +31,128 @@ namespace Falcor { namespace { -const uint32_t elems = 256; +const uint32_t kElementCount = 256; + +std::vector getTestData() +{ + std::vector data(kElementCount); + for (uint32_t i = 0; i < kElementCount; i++) + data[i] = i; + return data; +} + +const std::vector kTestData = getTestData(); /** Create buffer with the given CPU access and elements initialized to 0,1,2,... */ -ref createTestBuffer(ref pDevice, Buffer::CpuAccess cpuAccess, bool initialize = true) +ref createTestBuffer(GPUUnitTestContext& ctx, MemoryType memoryType, bool initialize) { - std::vector initData(elems); - for (uint32_t i = 0; i < elems; i++) - initData[i] = i; - return Buffer::create( - pDevice, elems * sizeof(uint32_t), Resource::BindFlags::ShaderResource, cpuAccess, initialize ? initData.data() : nullptr + return ctx.getDevice()->createBuffer( + kElementCount * sizeof(uint32_t), + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + memoryType, + initialize ? kTestData.data() : nullptr ); } -/** Tests readback from buffer created with the given CPU access flag work. - The test binds the buffer to a compute program which reads back the data. -*/ -void testBufferReadback(GPUUnitTestContext& ctx, Buffer::CpuAccess cpuAccess) +void checkData(GPUUnitTestContext& ctx, const uint32_t* pData) { - auto pBuf = createTestBuffer(ctx.getDevice(), cpuAccess); - - // Run program that copies the buffer elements into result buffer. - ctx.createProgram("Tests/Core/BufferAccessTests.cs.slang", "readback"); - ctx.allocateStructuredBuffer("result", elems); - ctx["buffer"] = pBuf; - ctx.runProgram(elems, 1, 1); + for (uint32_t i = 0; i < kElementCount; i++) + EXPECT_EQ(pData[i], i) << "i = " << i; +} - std::vector result = ctx.readBuffer("result"); - for (uint32_t i = 0; i < elems; i++) - { - EXPECT_EQ(result[i], i) << "i = " << i; - } +void initBufferIndirect(GPUUnitTestContext& ctx, ref pBuffer) +{ + auto pInitData = createTestBuffer(ctx, MemoryType::DeviceLocal, true); + ctx.getRenderContext()->copyResource(pBuffer.get(), pInitData.get()); + ctx.getRenderContext()->submit(true); } -} // namespace -/** Test that initialization of buffer with CPU write access works. - The test copies the data to a staging buffer on the GPU. -*/ -GPU_TEST(CopyBufferCpuAccessWrite) +std::vector readBufferIndirect(GPUUnitTestContext& ctx, ref pBuffer) { - ref pDevice = ctx.getDevice(); + ref pResult = createTestBuffer(ctx, MemoryType::DeviceLocal, false); + ctx.getRenderContext()->copyResource(pResult.get(), pBuffer.get()); + ctx.getRenderContext()->submit(true); + return pResult->getElements(); +} - auto pBuf = createTestBuffer(pDevice, Buffer::CpuAccess::Write); +void checkBufferIndirect(GPUUnitTestContext& ctx, ref pBuffer) +{ + return checkData(ctx, readBufferIndirect(ctx, pBuffer).data()); +} - // Copy buffer to staging buffer on the GPU. - // Note we have to use copyBufferRegion() as our buffer is allocated within a page on the upload heap. - auto pStaging = Buffer::create(pDevice, elems * sizeof(uint32_t), Resource::BindFlags::None, Buffer::CpuAccess::Read); - ctx.getRenderContext()->copyBufferRegion(pStaging.get(), 0, pBuf.get(), 0, elems * sizeof(uint32_t)); +} // namespace - // We have to flush here so that the copy is guaranteed to have finished by the time we map. - // In user code, we normally want to use a GpuFence to signal this instead of a full flush. - ctx.getRenderContext()->flush(true); +GPU_TEST(BufferDeviceLocalWrite) +{ + // Create without init data, then set data using setBlob(). + { + ref pBuffer = createTestBuffer(ctx, MemoryType::DeviceLocal, false); + pBuffer->setBlob(kTestData.data(), 0, kTestData.size() * sizeof(uint32_t)); + checkBufferIndirect(ctx, pBuffer); + } - const uint32_t* result = static_cast(pStaging->map(Buffer::MapType::Read)); - for (uint32_t i = 0; i < elems; i++) + // Create with init data. { - EXPECT_EQ(result[i], i) << "i = " << i; + ref pBuffer = createTestBuffer(ctx, MemoryType::DeviceLocal, true); + checkBufferIndirect(ctx, pBuffer); } - pStaging->unmap(); } -// This test is disabled due to bug in creation of SRV/UAVs for buffers on the upload heap. - -/** Test setBlob() into buffer with CPU write access. - */ -GPU_TEST(SetBlobBufferCpuAccessWrite, "Disabled due to issue with SRV/UAVs for resources on the upload heap (#638)") +GPU_TEST(BufferDeviceLocalRead) { - ref pDevice = ctx.getDevice(); - - auto pBuf = createTestBuffer(pDevice, Buffer::CpuAccess::Write, false); + ref pBuffer = createTestBuffer(ctx, MemoryType::DeviceLocal, false); + initBufferIndirect(ctx, pBuffer); - // Set data into buffer using its setBlob() function. - std::vector initData(elems); - for (uint32_t i = 0; i < elems; i++) - initData[i] = i; - pBuf->setBlob(initData.data(), 0, elems * sizeof(uint32_t)); + std::vector data(kElementCount); + pBuffer->getBlob(data.data(), 0, kElementCount * sizeof(uint32_t)); + checkData(ctx, data.data()); +} - // Run program that copies the buffer elements into result buffer. - ctx.createProgram("Tests/Core/BufferAccessTests.cs.slang", "readback"); - ctx.allocateStructuredBuffer("result", elems); - ctx["buffer"] = pBuf; - ctx.runProgram(elems, 1, 1); +GPU_TEST(BufferUploadWrite) +{ + // Create without init data, then set data using setBlob(). + { + ref pBuffer = createTestBuffer(ctx, MemoryType::Upload, false); + pBuffer->setBlob(kTestData.data(), 0, kTestData.size() * sizeof(uint32_t)); + checkBufferIndirect(ctx, pBuffer); + } - std::vector result = ctx.readBuffer("result"); - for (uint32_t i = 0; i < elems; i++) + // Create with init data. { - EXPECT_EQ(result[i], i) << "i = " << i; + ref pBuffer = createTestBuffer(ctx, MemoryType::Upload, true); + checkBufferIndirect(ctx, pBuffer); } } -/** Test that GPU reads from buffer created without CPU access works. - */ -GPU_TEST(BufferCpuAccessNone) +GPU_TEST(BufferUploadMap) { - testBufferReadback(ctx, Buffer::CpuAccess::None); + ref pBuffer = createTestBuffer(ctx, MemoryType::Upload, false); + uint32_t* pData = reinterpret_cast(pBuffer->map(Buffer::MapType::Write)); + for (uint32_t i = 0; i < kElementCount; ++i) + pData[i] = kTestData[i]; + pBuffer->unmap(); + checkBufferIndirect(ctx, pBuffer); } -/** Test that GPU reads from buffer created with CPU read access works. - */ -GPU_TEST(BufferCpuAccessRead) +GPU_TEST(BufferReadbackRead) { - testBufferReadback(ctx, Buffer::CpuAccess::Read); -} + ref pBuffer = createTestBuffer(ctx, MemoryType::ReadBack, false); + initBufferIndirect(ctx, pBuffer); -// This test is disabled due to bug in creation of SRV/UAVs for buffers on the upload heap. + std::vector data(kElementCount); + pBuffer->getBlob(data.data(), 0, kElementCount * sizeof(uint32_t)); + checkData(ctx, data.data()); +} -/** Test that GPU reads from buffer created with CPU write access works. - */ -GPU_TEST(BufferCpuAccessWrite, "Disabled due to issue with SRV/UAVs for resources on the upload heap (#638)") +GPU_TEST(BufferReadbackMap) { - testBufferReadback(ctx, Buffer::CpuAccess::Write); + ref pBuffer = createTestBuffer(ctx, MemoryType::ReadBack, false); + initBufferIndirect(ctx, pBuffer); + + const uint32_t* pData = reinterpret_cast(pBuffer->map(Buffer::MapType::Read)); + checkData(ctx, pData); + pBuffer->unmap(); } + } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp b/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp index 27a5355bf..a14aeca08 100644 --- a/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/BufferTests.cpp @@ -67,11 +67,11 @@ void testBuffer(GPUUnitTestContext& ctx, uint32_t numElems, uint32_t index = 0, // Create test buffer. ref pBuffer; if constexpr (type == Type::ByteAddressBuffer) - pBuffer = Buffer::create(pDevice, numElems * sizeof(uint32_t), ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None); + pBuffer = pDevice->createBuffer(numElems * sizeof(uint32_t), ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal); else if constexpr (type == Type::TypedBuffer) - pBuffer = Buffer::createTyped(pDevice, numElems, ResourceBindFlags::UnorderedAccess); + pBuffer = pDevice->createTypedBuffer(numElems, ResourceBindFlags::UnorderedAccess); else if constexpr (type == Type::StructuredBuffer) - pBuffer = Buffer::createStructured(pDevice, ctx.getProgram(), "buffer", numElems, ResourceBindFlags::UnorderedAccess); + pBuffer = pDevice->createStructuredBuffer(ctx.getVars()->getRootVar()["buffer"], numElems, ResourceBindFlags::UnorderedAccess); ctx["buffer"] = pBuffer; @@ -165,18 +165,59 @@ GPU_TEST(BufferUpdate) const uint4 a = {1, 2, 3, 4}; const uint4 b = {5, 6, 7, 8}; - auto pBuffer = Buffer::create(ctx.getDevice(), 16); + auto pBuffer = ctx.getDevice()->createBuffer(16); pBuffer->setBlob(&a, 0, 16); - uint4 resA = *reinterpret_cast(pBuffer->map(Buffer::MapType::Read)); - pBuffer->unmap(); + uint4 resA = pBuffer->getElement(0); EXPECT_EQ(a, resA); pBuffer->setBlob(&b, 0, 16); - uint4 resB = *reinterpret_cast(pBuffer->map(Buffer::MapType::Read)); - pBuffer->unmap(); + uint4 resB = pBuffer->getElement(0); EXPECT_EQ(b, resB); } + +GPU_TEST(BufferWrite) +{ + auto testWrite = [&ctx](uint4 testData, bool useInitData) + { + ref bufA = + ctx.getDevice()->createBuffer(16, ResourceBindFlags::None, MemoryType::Upload, useInitData ? &testData : nullptr); + ref bufB = ctx.getDevice()->createBuffer(16, ResourceBindFlags::None); + + if (!useInitData) + { + uint4* data = reinterpret_cast(bufA->map(Buffer::MapType::Write)); + std::memcpy(data, &testData, 16); + bufA->unmap(); + } + + ctx.getDevice()->getRenderContext()->copyResource(bufB.get(), bufA.get()); + + { + uint4 result = bufB->getElement(0); + EXPECT_EQ(result, testData); + } + + uint4 testData2 = testData * 10u; + + { + uint4* data = reinterpret_cast(bufA->map(Buffer::MapType::Write)); + std::memcpy(data, &testData2, 16); + bufA->unmap(); + } + + ctx.getDevice()->getRenderContext()->copyResource(bufB.get(), bufA.get()); + + { + uint4 result = bufB->getElement(0); + EXPECT_EQ(result, testData2); + } + }; + + testWrite(uint4(1, 2, 3, 4), false); + testWrite(uint4(3, 4, 5, 6), true); +} + } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/ConstantBufferTests.cpp b/Source/Tools/FalcorTest/Tests/Core/ConstantBufferTests.cpp index 6f4bf4fb5..65b989db1 100644 --- a/Source/Tools/FalcorTest/Tests/Core/ConstantBufferTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/ConstantBufferTests.cpp @@ -36,7 +36,7 @@ GPU_TEST(BuiltinConstantBuffer1) ctx.createProgram("Tests/Core/ConstantBufferTests.cs.slang", "testCbuffer1"); ctx.allocateStructuredBuffer("result", 3); ctx["CB"]["params1"]["a"] = 1; - ctx["CB"]["params1"]["b"] = 3; + ctx["CB"]["params1"]["b"] = 3u; ctx["CB"]["params1"]["c"] = 5.5f; ctx.runProgram(1, 1, 1); @@ -53,7 +53,7 @@ GPU_TEST(BuiltinConstantBuffer2) ctx.createProgram("Tests/Core/ConstantBufferTests.cs.slang", "testCbuffer2"); ctx.allocateStructuredBuffer("result", 3); ctx["params2"]["a"] = 1; - ctx["params2"]["b"] = 3; + ctx["params2"]["b"] = 3u; ctx["params2"]["c"] = 5.5f; ctx.runProgram(1, 1, 1); diff --git a/Source/Tools/FalcorTest/Tests/Core/CoreTests.cpp b/Source/Tools/FalcorTest/Tests/Core/CoreTests.cpp index 14a72855e..d4e315b51 100644 --- a/Source/Tools/FalcorTest/Tests/Core/CoreTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/CoreTests.cpp @@ -36,8 +36,8 @@ GPU_TEST(TransientHeapRecycling) size_t M = 1024 * 1024 * 1024; std::vector cpuBuf(M, 0); - ref A = Buffer::create(pDevice, M, ResourceBindFlags::None, Buffer::CpuAccess::Read, cpuBuf.data()); - ref B = Buffer::create(pDevice, 4, ResourceBindFlags::None, Buffer::CpuAccess::None); + ref A = pDevice->createBuffer(M, ResourceBindFlags::None, MemoryType::DeviceLocal, cpuBuf.data()); + ref B = pDevice->createBuffer(4, ResourceBindFlags::None, MemoryType::DeviceLocal); // Progress through N frames (and transient heaps), ending up using the // same transient heap as is used for uploading the data to buffer A. @@ -49,8 +49,8 @@ GPU_TEST(TransientHeapRecycling) // The following commands will trigger a TDR even if the validation error // is missed. pRenderContext->copyBufferRegion(B.get(), 0, A.get(), 0, 4); - pRenderContext->flush(true); - A->map(Buffer::MapType::Read); - A->unmap(); + pRenderContext->submit(true); + // A->map(Buffer::MapType::Read); + // A->unmap(); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/DDSReadTests.cpp b/Source/Tools/FalcorTest/Tests/Core/DDSReadTests.cpp index 2f65b3b93..49d646bce 100644 --- a/Source/Tools/FalcorTest/Tests/Core/DDSReadTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/DDSReadTests.cpp @@ -85,8 +85,13 @@ void testDDS(GPUUnitTestContext& ctx, const std::string& testName, ResourceForma // Create uncompressed destination texture ref pSrcTex = pDDSTex; ResourceFormat destFormat = ResourceFormat::RGBA32Float; - auto pDst = Texture::create2D( - pDevice, pDDSTex->getWidth(), pDDSTex->getHeight(), destFormat, 1, 1, nullptr, + auto pDst = pDevice->createTexture2D( + pDDSTex->getWidth(), + pDDSTex->getHeight(), + destFormat, + 1, + 1, + nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::RenderTarget ); @@ -116,26 +121,24 @@ void testDDS(GPUUnitTestContext& ctx, const std::string& testName, ResourceForma { // Create texture from difference data std::vector diff = ctx.readBuffer("difference"); - ref pDiffTex(Texture::create2D(pDevice, dstDim.x, dstDim.y, ResourceFormat::RGBA32Float, 1, 1, diff.data())); + ref pDiffTex(pDevice->createTexture2D(dstDim.x, dstDim.y, ResourceFormat::RGBA32Float, 1, 1, diff.data())); // Analyze difference texture TextureAnalyzer analyzer(pDevice); - auto pResultBuffer = Buffer::create( - pDevice, TextureAnalyzer::getResultSize(), ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess - ); + auto pResultBuffer = + pDevice->createBuffer(TextureAnalyzer::getResultSize(), ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); analyzer.analyze(ctx.getRenderContext(), pDiffTex, 0, 0, pResultBuffer); - const TextureAnalyzer::Result* result = static_cast(pResultBuffer->map(Buffer::MapType::Read)); + TextureAnalyzer::Result result = pResultBuffer->getElement(0); // Expect difference image to be uniform 0. - EXPECT(result->isConstant(TextureChannelFlags::Red)); - EXPECT(result->isConstant(TextureChannelFlags::Green)); - EXPECT(result->isConstant(TextureChannelFlags::Blue)); - EXPECT(result->isConstant(TextureChannelFlags::Alpha)); - EXPECT_EQ(result->value.r, 0.f); - EXPECT_EQ(result->value.g, 0.f); - EXPECT_EQ(result->value.b, 0.f); - EXPECT_EQ(result->value.a, 0.f); - pResultBuffer->unmap(); + EXPECT(result.isConstant(TextureChannelFlags::Red)); + EXPECT(result.isConstant(TextureChannelFlags::Green)); + EXPECT(result.isConstant(TextureChannelFlags::Blue)); + EXPECT(result.isConstant(TextureChannelFlags::Alpha)); + EXPECT_EQ(result.value.r, 0.f); + EXPECT_EQ(result.value.g, 0.f); + EXPECT_EQ(result.value.b, 0.f); + EXPECT_EQ(result.value.a, 0.f); } else { @@ -143,8 +146,14 @@ void testDDS(GPUUnitTestContext& ctx, const std::string& testName, ResourceForma std::vector result = ctx.readBuffer("result"); Bitmap::UniqueConstPtr resultBitmap(Bitmap::create(dstDim.x, dstDim.y, ResourceFormat::RGBA8Unorm, result.data())); Bitmap::saveImage( - refPath, dstDim.x, dstDim.y, Bitmap::FileFormat::PngFile, Bitmap::ExportFlags::Uncompressed | Bitmap::ExportFlags::ExportAlpha, - ResourceFormat::RGBA8Unorm, false, result.data() + refPath, + dstDim.x, + dstDim.y, + Bitmap::FileFormat::PngFile, + Bitmap::ExportFlags::Uncompressed | Bitmap::ExportFlags::ExportAlpha, + ResourceFormat::RGBA8Unorm, + false, + result.data() ); } } diff --git a/Source/Tools/FalcorTest/Tests/Core/EnumTests.cpp b/Source/Tools/FalcorTest/Tests/Core/EnumTests.cpp index 2b72f1234..2c0ad3d77 100644 --- a/Source/Tools/FalcorTest/Tests/Core/EnumTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/EnumTests.cpp @@ -1,4 +1,30 @@ - +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ #include "Testing/UnitTest.h" #include "Core/Enum.h" @@ -80,26 +106,10 @@ CPU_TEST(EnumInfo) EXPECT(stringToEnum("C") == TestEnum::C); // Converting unregistered values throws. - try - { - enumToString(TestEnum(-1)); - EXPECT(false); - } - catch (const RuntimeError&) - { - EXPECT(true); - } + EXPECT_THROW(enumToString(TestEnum(-1))); // Converting unregistered strings throws. - try - { - stringToEnum("D"); - EXPECT(false); - } - catch (const RuntimeError&) - { - EXPECT(true); - } + EXPECT_THROW(stringToEnum("D")); EXPECT_TRUE(enumHasValue("X")); EXPECT_TRUE(enumHasValue("Y")); @@ -119,15 +129,7 @@ CPU_TEST(EnumInfo) EXPECT(flagsToStringList(TestFlags::A | TestFlags::B | TestFlags::C) == std::vector({"A", "B", "C"})); // Converting unregistered values throws. - try - { - flagsToStringList(TestFlags(-1)); - EXPECT(false); - } - catch (const RuntimeError&) - { - EXPECT(true); - } + EXPECT_THROW(flagsToStringList(TestFlags(-1))); EXPECT(stringListToFlags({}) == TestFlags{0}); EXPECT(stringListToFlags({"A"}) == TestFlags::A); @@ -136,14 +138,6 @@ CPU_TEST(EnumInfo) EXPECT(stringListToFlags({"A", "B", "C"}) == (TestFlags::A | TestFlags::B | TestFlags::C)); // Converting unregistered strings throws. - try - { - stringListToFlags({"D"}); - EXPECT(false); - } - catch (const RuntimeError&) - { - EXPECT(true); - } + EXPECT_THROW(stringListToFlags({"D"})); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/LargeBuffer.cpp b/Source/Tools/FalcorTest/Tests/Core/LargeBuffer.cpp index ee7eb094b..9a83e7769 100644 --- a/Source/Tools/FalcorTest/Tests/Core/LargeBuffer.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/LargeBuffer.cpp @@ -46,39 +46,39 @@ void testCopyRegion(GPUUnitTestContext& ctx, size_t bufferSize) // Initialize small buffers with known data. for (size_t i = 0; i < data.size(); i++) data[i] = 0xcdcdcdcd; - auto pDefaultData = Buffer::create(pDevice, testSize, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data()); + auto pDefaultData = pDevice->createBuffer(testSize, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, data.data()); for (size_t i = 0; i < data.size(); i++) data[i] = r(); - auto pTestData = Buffer::create(pDevice, testSize, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data()); - auto pReadback = Buffer::create(pDevice, testSize, ResourceBindFlags::None, Buffer::CpuAccess::Read, nullptr); + auto pTestData = pDevice->createBuffer(testSize, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, data.data()); + auto pReadback = pDevice->createBuffer(testSize, ResourceBindFlags::None, MemoryType::ReadBack, nullptr); // Create large buffer. - auto pBuffer = Buffer::create(pDevice, bufferSize, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr); + auto pBuffer = pDevice->createBuffer(bufferSize, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr); EXPECT(pBuffer); EXPECT_EQ(bufferSize, pBuffer->getSize()); // Default initialize the end of the large buffer. const uint64_t dstOffset = pBuffer->getSize() - testSize; ctx.getRenderContext()->copyBufferRegion(pBuffer.get(), dstOffset, pDefaultData.get(), 0ull, testSize); - ctx.getRenderContext()->flush(true); // For safety's sake + ctx.getRenderContext()->submit(true); // For safety's sake // Copy the test data into the end of the large buffer. ctx.getRenderContext()->copyBufferRegion(pBuffer.get(), dstOffset, pTestData.get(), 0ull, testSize); - ctx.getRenderContext()->flush(true); // For safety's sake + ctx.getRenderContext()->submit(true); // For safety's sake // For >4GB buffers, also default initialize at the destination offset cast to 32-bit *after* the copy above. // This is to make sure that copyBufferRegion() aren't actually truncating the offset internally. if (dstOffset + testSize > (1ull << 32)) { ctx.getRenderContext()->copyBufferRegion(pBuffer.get(), (uint32_t)dstOffset, pDefaultData.get(), 0ull, testSize); - ctx.getRenderContext()->flush(true); // For safety's sake + ctx.getRenderContext()->submit(true); // For safety's sake } // Copy the end of the large buffer into a readback buffer. ctx.getRenderContext()->copyBufferRegion(pReadback.get(), 0ull, pBuffer.get(), dstOffset, testSize); // Flush and wait for the result. - ctx.getRenderContext()->flush(true); + ctx.getRenderContext()->submit(true); // Check the result. const uint32_t* result = static_cast(pReadback->map(Buffer::MapType::Read)); @@ -106,25 +106,25 @@ void testReadRaw(GPUUnitTestContext& ctx, bool useRootDesc, size_t bufferSize) const size_t testSize = data.size() * sizeof(data[0]); for (size_t i = 0; i < data.size(); i++) data[i] = 0xcdcdcdcd; - auto pDefaultData = Buffer::create(pDevice, testSize, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data()); + auto pDefaultData = pDevice->createBuffer(testSize, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, data.data()); for (size_t i = 0; i < data.size(); i++) data[i] = r(); - auto pTestData = Buffer::create(pDevice, testSize, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data()); + auto pTestData = pDevice->createBuffer(testSize, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, data.data()); // Create large buffer. - auto pBuffer = Buffer::create(pDevice, bufferSize, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr); + auto pBuffer = pDevice->createBuffer(bufferSize, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr); EXPECT(pBuffer); // Copy the test data into the end of the large buffer. const uint64_t dstOffset = pBuffer->getSize() - testSize; ctx.getRenderContext()->copyBufferRegion(pBuffer.get(), dstOffset, pTestData.get(), 0ull, testSize); - ctx.getRenderContext()->flush(true); // For safety's sake + ctx.getRenderContext()->submit(true); // For safety's sake // For >4GB buffers, also default initialize at the destination offset cast to 32-bit *after* the copy above. if (dstOffset + testSize > (1ull << 32)) { ctx.getRenderContext()->copyBufferRegion(pBuffer.get(), (uint32_t)dstOffset, pDefaultData.get(), 0ull, testSize); - ctx.getRenderContext()->flush(true); // For safety's sake + ctx.getRenderContext()->submit(true); // For safety's sake } // Run compute program to read from the large buffer. @@ -160,27 +160,27 @@ void testReadStructured(GPUUnitTestContext& ctx, bool useRootDesc, size_t buffer const size_t testSize = data.size() * sizeof(data[0]); for (size_t i = 0; i < data.size(); i++) data[i] = uint4(0xcdcdcdcd); - auto pDefaultData = Buffer::create(pDevice, testSize, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data()); + auto pDefaultData = pDevice->createBuffer(testSize, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, data.data()); for (size_t i = 0; i < data.size(); i++) data[i] = uint4(r()); - auto pTestData = Buffer::create(pDevice, testSize, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data()); + auto pTestData = pDevice->createBuffer(testSize, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, data.data()); // Create large buffer. - auto pBuffer = Buffer::createStructured( - pDevice, sizeof(uint4), (uint32_t)elemCount, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false + auto pBuffer = pDevice->createStructuredBuffer( + sizeof(uint4), (uint32_t)elemCount, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false ); EXPECT(pBuffer); // Copy the test data into the end of the large buffer. const uint64_t dstOffset = pBuffer->getSize() - testSize; ctx.getRenderContext()->copyBufferRegion(pBuffer.get(), dstOffset, pTestData.get(), 0ull, testSize); - ctx.getRenderContext()->flush(true); // For safety's sake + ctx.getRenderContext()->submit(true); // For safety's sake // For >4GB buffers, also default initialize at the destination offset cast to 32-bit *after* the copy above. if (dstOffset + testSize > (1ull << 32)) { ctx.getRenderContext()->copyBufferRegion(pBuffer.get(), (uint32_t)dstOffset, pDefaultData.get(), 0ull, testSize); - ctx.getRenderContext()->flush(true); // For safety's sake + ctx.getRenderContext()->submit(true); // For safety's sake } // Run compute program to read from the large buffer. @@ -216,27 +216,27 @@ void testReadStructuredUint(GPUUnitTestContext& ctx, bool useRootDesc, size_t bu const size_t testSize = data.size() * sizeof(data[0]); for (size_t i = 0; i < data.size(); i++) data[i] = 0xcdcdcdcd; - auto pDefaultData = Buffer::create(pDevice, testSize, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data()); + auto pDefaultData = pDevice->createBuffer(testSize, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, data.data()); for (size_t i = 0; i < data.size(); i++) data[i] = r(); - auto pTestData = Buffer::create(pDevice, testSize, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data()); + auto pTestData = pDevice->createBuffer(testSize, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, data.data()); // Create large buffer. - auto pBuffer = Buffer::createStructured( - pDevice, sizeof(uint32_t), (uint32_t)elemCount, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false + auto pBuffer = pDevice->createStructuredBuffer( + sizeof(uint32_t), (uint32_t)elemCount, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false ); EXPECT(pBuffer); // Copy the test data into the end of the large buffer. const uint64_t dstOffset = pBuffer->getSize() - testSize; ctx.getRenderContext()->copyBufferRegion(pBuffer.get(), dstOffset, pTestData.get(), 0ull, testSize); - ctx.getRenderContext()->flush(true); // For safety's sake + ctx.getRenderContext()->submit(true); // For safety's sake // For >4GB buffers, also default initialize at the destination offset cast to 32-bit *after* the copy above. if (dstOffset + testSize > (1ull << 32)) { ctx.getRenderContext()->copyBufferRegion(pBuffer.get(), (uint32_t)dstOffset, pDefaultData.get(), 0ull, testSize); - ctx.getRenderContext()->flush(true); // For safety's sake + ctx.getRenderContext()->submit(true); // For safety's sake } // Run compute program to read from the large buffer. diff --git a/Source/Tools/FalcorTest/Tests/Core/ParamBlockCB.cpp b/Source/Tools/FalcorTest/Tests/Core/ParamBlockCB.cpp index dbbde4bf6..7f8b6d0f9 100644 --- a/Source/Tools/FalcorTest/Tests/Core/ParamBlockCB.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/ParamBlockCB.cpp @@ -46,5 +46,6 @@ GPU_TEST(ParamBlockCB) ctx.runProgram(1, 1, 1); std::vector result = ctx.readBuffer("result"); + EXPECT_EQ(result[0], 42.1f); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cpp b/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cpp index 6f11e06fa..e8edb283d 100644 --- a/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/ResourceAliasing.cpp @@ -38,9 +38,8 @@ GPU_TEST(BufferAliasing_Read) std::vector initData(N); for (size_t i = 0; i < initData.size(); i++) initData[i] = (float)i; - auto pBuffer = Buffer::create( - pDevice, initData.size() * sizeof(float), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, initData.data() - ); + auto pBuffer = + pDevice->createBuffer(initData.size() * sizeof(float), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, initData.data()); ctx.createProgram("Tests/Core/ResourceAliasing.cs.slang", "testRead"); ctx.allocateStructuredBuffer("result", N * 3); @@ -70,9 +69,11 @@ GPU_TEST(BufferAliasing_ReadWrite) std::vector initData(N * 3); for (size_t i = 0; i < initData.size(); i++) initData[i] = (float)i; - auto pBuffer = Buffer::create( - pDevice, initData.size() * sizeof(float), Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess, - Buffer::CpuAccess::None, initData.data() + auto pBuffer = pDevice->createBuffer( + initData.size() * sizeof(float), + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType::DeviceLocal, + initData.data() ); ctx.createProgram("Tests/Core/ResourceAliasing.cs.slang", "testReadWrite"); @@ -84,14 +85,13 @@ GPU_TEST(BufferAliasing_ReadWrite) ctx.runProgram(N, 1, 1); - const float* result = reinterpret_cast(pBuffer->map(Buffer::MapType::Read)); + std::vector result = pBuffer->getElements(); for (size_t i = 0; i < N; i++) { EXPECT_EQ(result[i], (float)(N - i)) << "i = " << i; EXPECT_EQ(result[i + N], (float)(N - i)) << "i = " << i; EXPECT_EQ(result[i + 2 * N], (float)(N - i)) << "i = " << i; } - pBuffer->unmap(); } GPU_TEST(BufferAliasing_StructRead, "Disabled because version fails") @@ -103,8 +103,8 @@ GPU_TEST(BufferAliasing_StructRead, "Disabled because version fails") std::vector initData(N); for (size_t i = 0; i < initData.size(); i++) initData[i] = (float)i; - auto pBuffer = Buffer::createStructured( - pDevice, initData.size() * sizeof(float), 1, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, initData.data(), false + auto pBuffer = pDevice->createStructuredBuffer( + initData.size() * sizeof(float), 1, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, initData.data(), false ); ctx.createProgram("Tests/Core/ResourceAliasing.cs.slang", "testStructRead"); diff --git a/Source/Tools/FalcorTest/Tests/Core/RootBufferParamBlockTests.cpp b/Source/Tools/FalcorTest/Tests/Core/RootBufferParamBlockTests.cpp index 5a2270026..75dcd6f3c 100644 --- a/Source/Tools/FalcorTest/Tests/Core/RootBufferParamBlockTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/RootBufferParamBlockTests.cpp @@ -43,18 +43,18 @@ const std::string kGlobalRootBufferName = "globalTestBuffer"; std::mt19937 rng; auto dist = std::uniform_int_distribution(0, 100); -void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) +void testRootBuffer(GPUUnitTestContext& ctx, ShaderModel shaderModel, bool useUav) { ref pDevice = ctx.getDevice(); auto nextRandom = [&]() -> uint32_t { return dist(rng); }; DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; - Program::CompilerFlags compilerFlags = Program::CompilerFlags::None; + SlangCompilerFlags compilerFlags = SlangCompilerFlags::None; // Create parameter block based on reflection of a dummy program. // This is to ensure that the register index/space here do not match those of the final program. - Program::Desc reflDesc; + ProgramDesc reflDesc; reflDesc.addShaderLibrary(kReflectionProgram).csEntry("main"); auto pReflectionProgram = ComputePass::create(ctx.getDevice(), reflDesc, defines); EXPECT(pReflectionProgram != nullptr); @@ -74,9 +74,8 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo bufA[j].resize(kNumElems); for (uint32_t i = 0; i < kNumElems; i++) bufA[j][i] = nextRandom(); - block["bufA"][j] = Buffer::create( - pDevice, kNumElems * sizeof(uint32_t), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, bufA[j].data() - ); + block["bufA"][j] = + pDevice->createBuffer(kNumElems * sizeof(uint32_t), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, bufA[j].data()); } std::vector bufB[3]; for (uint32_t j = 0; j < 3; j++) @@ -85,7 +84,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo for (uint32_t i = 0; i < kNumElems; i++) bufB[j][i] = (float)nextRandom(); block["bufB"][j] = - Buffer::createTyped(pDevice, kNumElems, Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, bufB[j].data()); + pDevice->createTypedBuffer(kNumElems, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, bufB[j].data()); } std::vector bufC[4]; for (uint32_t j = 0; j < 4; j++) @@ -94,7 +93,7 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo for (uint32_t i = 0; i < kNumElems; i++) bufC[j][i] = nextRandom(); block["bufC"][j] = - Buffer::createTyped(pDevice, kNumElems, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, bufC[j].data()); + pDevice->createTypedBuffer(kNumElems, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, bufC[j].data()); } // Bind root buffer to the parameter block. @@ -102,12 +101,13 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo { for (uint32_t i = 0; i < kNumElems; i++) testBuffer[i] = nextRandom(); - auto pTestBuffer = Buffer::create( - pDevice, kNumElems * sizeof(uint32_t), useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, - Buffer::CpuAccess::None, testBuffer.data() + auto pTestBuffer = pDevice->createBuffer( + kNumElems * sizeof(uint32_t), + useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, + MemoryType::DeviceLocal, + testBuffer.data() ); - bool ret = pParamBlock->setBuffer(kRootBufferName, pTestBuffer); - EXPECT(ret); + pParamBlock->setBuffer(kRootBufferName, pTestBuffer); ref pBoundBuffer = pParamBlock->getBuffer(kRootBufferName); EXPECT_EQ(pBoundBuffer, pTestBuffer); @@ -126,17 +126,18 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo globalBufA.resize(kNumElems); for (uint32_t i = 0; i < kNumElems; i++) globalBufA[i] = nextRandom(); - var["globalBufA"] = Buffer::createTyped( - pDevice, kNumElems, Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, globalBufA.data() - ); + var["globalBufA"] = + pDevice->createTypedBuffer(kNumElems, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, globalBufA.data()); } std::vector globalTestBuffer(kNumElems); { for (uint32_t i = 0; i < kNumElems; i++) globalTestBuffer[i] = nextRandom(); - var[kGlobalRootBufferName] = Buffer::create( - pDevice, kNumElems * sizeof(uint32_t), useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, - Buffer::CpuAccess::None, globalTestBuffer.data() + var[kGlobalRootBufferName] = pDevice->createBuffer( + kNumElems * sizeof(uint32_t), + useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, + MemoryType::DeviceLocal, + globalTestBuffer.data() ); } @@ -167,21 +168,21 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo GPU_TEST(RootBufferParamBlockSRV_6_0) { - testRootBuffer(ctx, "6_0", false); + testRootBuffer(ctx, ShaderModel::SM6_0, false); } GPU_TEST(RootBufferParamBlockUAV_6_0) { - testRootBuffer(ctx, "6_0", true); + testRootBuffer(ctx, ShaderModel::SM6_0, true); } GPU_TEST(RootBufferParamBlockSRV_6_3) { - testRootBuffer(ctx, "6_3", false); + testRootBuffer(ctx, ShaderModel::SM6_3, false); } GPU_TEST(RootBufferParamBlockUAV_6_3) { - testRootBuffer(ctx, "6_3", true); + testRootBuffer(ctx, ShaderModel::SM6_3, true); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/RootBufferStructTests.cpp b/Source/Tools/FalcorTest/Tests/Core/RootBufferStructTests.cpp index e7615e5e4..b11f3cca4 100644 --- a/Source/Tools/FalcorTest/Tests/Core/RootBufferStructTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/RootBufferStructTests.cpp @@ -38,14 +38,14 @@ const std::string kRootBufferName = "rootBuf"; std::mt19937 rng; auto dist = std::uniform_int_distribution(0, 100); -void testRootBufferInStruct(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) +void testRootBufferInStruct(GPUUnitTestContext& ctx, ShaderModel shaderModel, bool useUav) { ref pDevice = ctx.getDevice(); auto nextRandom = [&]() -> uint32_t { return dist(rng); }; DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; - Program::CompilerFlags compilerFlags = Program::CompilerFlags::None; + SlangCompilerFlags compilerFlags = SlangCompilerFlags::None; ctx.createProgram("Tests/Core/RootBufferStructTests.cs.slang", "main", defines, compilerFlags, shaderModel); ctx.allocateStructuredBuffer("result", kNumElems); @@ -58,14 +58,14 @@ void testRootBufferInStruct(GPUUnitTestContext& ctx, const std::string& shaderMo for (uint32_t i = 0; i < kNumElems; i++) buf[i] = nextRandom(); data["buf"] = - Buffer::createTyped(pDevice, kNumElems, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, buf.data()); + pDevice->createTypedBuffer(kNumElems, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, buf.data()); } std::vector rwBuf(kNumElems); { for (uint32_t i = 0; i < kNumElems; i++) rwBuf[i] = nextRandom(); data["rwBuf"] = - Buffer::createTyped(pDevice, kNumElems, ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, rwBuf.data()); + pDevice->createTypedBuffer(kNumElems, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, rwBuf.data()); } // Test binding structured buffer to root descriptor inside struct in CB. @@ -74,9 +74,13 @@ void testRootBufferInStruct(GPUUnitTestContext& ctx, const std::string& shaderMo for (uint32_t i = 0; i < kNumElems; i++) rootBuf[i] = nextRandom(); - auto pRootBuffer = Buffer::createStructured( - pDevice, data[kRootBufferName], kNumElems, useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, - Buffer::CpuAccess::None, rootBuf.data(), false /* no UAV counter */ + auto pRootBuffer = pDevice->createStructuredBuffer( + data[kRootBufferName], + kNumElems, + useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, + MemoryType::DeviceLocal, + rootBuf.data(), + false /* no UAV counter */ ); data[kRootBufferName] = pRootBuffer; @@ -102,21 +106,21 @@ void testRootBufferInStruct(GPUUnitTestContext& ctx, const std::string& shaderMo GPU_TEST(RootBufferStructSRV_6_0) { - testRootBufferInStruct(ctx, "6_0", false); + testRootBufferInStruct(ctx, ShaderModel::SM6_0, false); } GPU_TEST(RootBufferStructUAV_6_0) { - testRootBufferInStruct(ctx, "6_0", true); + testRootBufferInStruct(ctx, ShaderModel::SM6_0, true); } GPU_TEST(RootBufferStructSRV_6_3) { - testRootBufferInStruct(ctx, "6_3", false); + testRootBufferInStruct(ctx, ShaderModel::SM6_3, false); } GPU_TEST(RootBufferStructUAV_6_3) { - testRootBufferInStruct(ctx, "6_3", true); + testRootBufferInStruct(ctx, ShaderModel::SM6_3, true); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/RootBufferTests.cpp b/Source/Tools/FalcorTest/Tests/Core/RootBufferTests.cpp index 24a9ac28f..e79428110 100644 --- a/Source/Tools/FalcorTest/Tests/Core/RootBufferTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/RootBufferTests.cpp @@ -47,14 +47,14 @@ struct S uint32_t b; }; -void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) +void testRootBuffer(GPUUnitTestContext& ctx, ShaderModel shaderModel, bool useUav) { ref pDevice = ctx.getDevice(); auto nextRandom = [&]() -> uint32_t { return dist(rng); }; DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; - Program::CompilerFlags compilerFlags = Program::CompilerFlags::None; + SlangCompilerFlags compilerFlags = SlangCompilerFlags::None; ctx.createProgram("Tests/Core/RootBufferTests.cs.slang", "main", defines, compilerFlags, shaderModel); ctx.allocateStructuredBuffer("result", kNumElems); @@ -68,8 +68,8 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo { for (uint32_t i = 0; i < kNumElems; i++) rawBuffer[i] = nextRandom(); - var["rawBuffer"] = Buffer::create( - pDevice, kNumElems * sizeof(uint32_t), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, rawBuffer.data() + var["rawBuffer"] = pDevice->createBuffer( + kNumElems * sizeof(uint32_t), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, rawBuffer.data() ); } @@ -77,8 +77,8 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo { for (uint32_t i = 0; i < kNumElems; i++) structBuffer[i] = {nextRandom() + 0.5f, nextRandom()}; - var["structBuffer"] = Buffer::createStructured( - pDevice, var["structBuffer"], kNumElems, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, structBuffer.data() + var["structBuffer"] = pDevice->createStructuredBuffer( + var["structBuffer"], kNumElems, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, structBuffer.data() ); } @@ -86,8 +86,8 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo { for (uint32_t i = 0; i < kNumElems; i++) typedBufferUint[i] = nextRandom(); - var["typedBufferUint"] = Buffer::createTyped( - pDevice, kNumElems, ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, typedBufferUint.data() + var["typedBufferUint"] = pDevice->createTypedBuffer( + kNumElems, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, typedBufferUint.data() ); } @@ -95,8 +95,8 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo { for (uint32_t i = 0; i < kNumElems; i++) typedBufferFloat4[i] = {nextRandom() * 0.25f, nextRandom() * 0.5f, nextRandom() * 0.75f, float(nextRandom())}; - var["typedBufferFloat4"] = Buffer::createTyped( - pDevice, kNumElems, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, typedBufferFloat4.data() + var["typedBufferFloat4"] = pDevice->createTypedBuffer( + kNumElems, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, typedBufferFloat4.data() ); } @@ -105,9 +105,11 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo { for (uint32_t i = 0; i < kNumElems; i++) testBuffer[i] = nextRandom(); - auto pTestBuffer = Buffer::create( - pDevice, kNumElems * sizeof(uint32_t), useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, - Buffer::CpuAccess::None, testBuffer.data() + auto pTestBuffer = pDevice->createBuffer( + kNumElems * sizeof(uint32_t), + useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, + MemoryType::DeviceLocal, + testBuffer.data() ); var[kRootBufferName] = pTestBuffer; @@ -141,12 +143,11 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo for (uint32_t i = 0; i < kNumElems; i++) rawBuffer[i] = nextRandom(); var["rawBuffer"] = - Buffer::create(pDevice, kNumElems * sizeof(uint32_t), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, rawBuffer.data()); + pDevice->createBuffer(kNumElems * sizeof(uint32_t), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, rawBuffer.data()); for (uint32_t i = 0; i < kNumElems; i++) typedBufferFloat4[i] = {nextRandom() * 0.25f, nextRandom() * 0.5f, nextRandom() * 0.75f, float(nextRandom())}; - var["typedBufferFloat4"] = Buffer::createTyped( - pDevice, kNumElems, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, typedBufferFloat4.data() - ); + var["typedBufferFloat4"] = + pDevice->createTypedBuffer(kNumElems, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, typedBufferFloat4.data()); var["CB"]["c0"] = ++c0; ctx.runProgram(kNumElems, 1, 1); @@ -156,9 +157,11 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo { for (uint32_t i = 0; i < kNumElems; i++) testBuffer[i] = nextRandom(); - auto pTestBuffer = Buffer::create( - pDevice, kNumElems * sizeof(uint32_t), useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, - Buffer::CpuAccess::None, testBuffer.data() + auto pTestBuffer = pDevice->createBuffer( + kNumElems * sizeof(uint32_t), + useUav ? ResourceBindFlags::UnorderedAccess : ResourceBindFlags::ShaderResource, + MemoryType::DeviceLocal, + testBuffer.data() ); var[kRootBufferName] = pTestBuffer; @@ -173,21 +176,21 @@ void testRootBuffer(GPUUnitTestContext& ctx, const std::string& shaderModel, boo GPU_TEST(RootBufferSRV_6_0) { - testRootBuffer(ctx, "6_0", false); + testRootBuffer(ctx, ShaderModel::SM6_0, false); } GPU_TEST(RootBufferUAV_6_0) { - testRootBuffer(ctx, "6_0", true); + testRootBuffer(ctx, ShaderModel::SM6_0, true); } GPU_TEST(RootBufferSRV_6_3) { - testRootBuffer(ctx, "6_3", false); + testRootBuffer(ctx, ShaderModel::SM6_3, false); } GPU_TEST(RootBufferUAV_6_3) { - testRootBuffer(ctx, "6_3", true); + testRootBuffer(ctx, ShaderModel::SM6_3, true); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Core/TextureTests.cpp b/Source/Tools/FalcorTest/Tests/Core/TextureTests.cpp index c4f6ee139..9d6207ebe 100644 --- a/Source/Tools/FalcorTest/Tests/Core/TextureTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Core/TextureTests.cpp @@ -36,8 +36,8 @@ GPU_TEST(RWTexture3D) { ref pDevice = ctx.getDevice(); - auto pTex = Texture::create3D( - pDevice, 16, 16, 16, ResourceFormat::R32Uint, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess + auto pTex = pDevice->createTexture3D( + 16, 16, 16, ResourceFormat::R32Uint, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess ); EXPECT(pTex); @@ -96,8 +96,13 @@ GPU_TEST(TextureMinMaxMip) } // Create texture. - auto pTex = Texture::create2D( - pDevice, texWidth, texHeight, ResourceFormat::RGBA8Unorm, 1, Resource::kMaxPossible, textureBase.data(), + auto pTex = pDevice->createTexture2D( + texWidth, + texHeight, + ResourceFormat::RGBA8Unorm, + 1, + Resource::kMaxPossible, + textureBase.data(), ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess ); EXPECT(pTex) << "Texture was not created"; @@ -192,11 +197,11 @@ GPU_TEST(Texture_Load8Bit) // Create texture in BGRX8Unorm format. // This is what Bitmap::createFromFile currently returns (see BitmapTests.cpp). - auto texUnorm = Texture::create2D(pDevice, 256, 1, ResourceFormat::BGRX8Unorm, 1, 1, data); + auto texUnorm = pDevice->createTexture2D(256, 1, ResourceFormat::BGRX8Unorm, 1, 1, data); EXPECT(texUnorm != nullptr); // Create texture in RGBA8Uint format. - auto texUint = Texture::create2D(pDevice, 256, 1, ResourceFormat::RGBA8Uint, 1, 1, data); + auto texUint = pDevice->createTexture2D(256, 1, ResourceFormat::RGBA8Uint, 1, 1, data); EXPECT(texUint != nullptr); ctx.createProgram("Tests/Core/TextureLoadTests.cs.slang", "testLoadFormat"); diff --git a/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp b/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp index 3380ff457..aef5f4afd 100644 --- a/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp +++ b/Source/Tools/FalcorTest/Tests/DebugPasses/InvalidPixelDetectionTests.cpp @@ -50,11 +50,11 @@ GPU_TEST(InvalidPixelDetectionPass) RenderContext* pRenderContext = ctx.getRenderContext(); ref pTargetFbo = Fbo::create2D(pDevice, 2, 4, ResourceFormat::BGRA8UnormSrgb); - ref pInput = Texture::create2D(pDevice, 2, 4, ResourceFormat::R32Float, 1, Resource::kMaxPossible, pInitData); + ref pInput = pDevice->createTexture2D(2, 4, ResourceFormat::R32Float, 1, Resource::kMaxPossible, pInitData); ref pGraph = RenderGraph::create(ctx.getDevice(), "Invalid Pixel Detection"); ref pPass = RenderPass::create("InvalidPixelDetectionPass", ctx.getDevice()); if (!pPass) - throw RuntimeError("Could not create render pass 'InvalidPixelDetectionPass'"); + FALCOR_THROW("Could not create render pass 'InvalidPixelDetectionPass'"); pGraph->addPass(pPass, "InvalidPixelDetectionPass"); pGraph->setInput("InvalidPixelDetectionPass.src", pInput); pGraph->markOutput("InvalidPixelDetectionPass.dst"); diff --git a/Source/Tools/FalcorTest/Tests/DiffRendering/Material/DiffMaterialTests.cpp b/Source/Tools/FalcorTest/Tests/DiffRendering/Material/DiffMaterialTests.cpp new file mode 100644 index 000000000..f3d9fca39 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/DiffRendering/Material/DiffMaterialTests.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Testing/UnitTest.h" +#include "Scene/Scene.h" +#include "Scene/Material/PBRT/PBRTDiffuseMaterial.h" +#include "DiffRendering/SceneGradients.h" +#include + +namespace Falcor +{ +namespace +{ +const char kShaderFile[] = "Tests/DiffRendering/Material/DiffMaterialTests.cs.slang"; + +struct BsdfConfig +{ + float3 wi; + float3 wo; + float4 baseColor; +}; + +void testDiffPBRTDiffuse(GPUUnitTestContext& ctx, const BsdfConfig& bsdfConfig) +{ + // Create material. + ref pMaterial = PBRTDiffuseMaterial::create(ctx.getDevice(), "PBRTDiffuse"); + pMaterial->setBaseColor(bsdfConfig.baseColor); + + // Create and update scene containing the material. + Scene::SceneData sceneData; + sceneData.pMaterials = std::make_unique(ctx.getDevice()); + MaterialID materialID = sceneData.pMaterials->addMaterial(pMaterial); + + ref pScene = Scene::create(ctx.getDevice(), std::move(sceneData)); + auto updateFlags = pScene->update(ctx.getRenderContext(), 0.0); + + // Create scene gradients. + const uint32_t gradDim = 3; + std::unique_ptr pSceneGradients = std::make_unique(ctx.getDevice(), uint2(gradDim), uint2(1)); + + // Create program. + ProgramDesc desc; + desc.addShaderModules(pScene->getShaderModules()); + desc.addShaderLibrary(kShaderFile); + desc.addTypeConformances(pScene->getTypeConformances()); + desc.csEntry("testDiffPBRTDiffuse"); + ctx.createProgram(desc, pScene->getSceneDefines()); + + pScene->bindShaderData(ctx["gScene"]); + pSceneGradients->bindShaderData(ctx["gSceneGradients"]); + ctx["CB"]["gWi"] = bsdfConfig.wi; + ctx["CB"]["gWo"] = bsdfConfig.wo; + ctx.allocateStructuredBuffer("materialGrad", 3); + ctx.allocateStructuredBuffer("geometryGrad", 3); + ctx.runProgram(1, 1, 1); + + // Material gradient w.r.t. the diffuse albedo. + std::vector materialGrad = ctx.readBuffer("materialGrad"); + const float kExpectedMaterialGrad[] = {0.3003115f, 0.3003115f, 0.3003115f}; + + // Geometry gradient w.r.t. the outgoing direction wo. + std::vector geometryGrad = ctx.readBuffer("geometryGrad"); + const float kExpectedGeometryGrad[] = {0.f, 0.f, 0.5411268f}; + + for (uint32_t i = 0; i < 3; i++) + { + float l1 = std::abs(materialGrad[i] - kExpectedMaterialGrad[i]); + EXPECT_LE(l1, 1e-3f); + + l1 = std::abs(geometryGrad[i] - kExpectedGeometryGrad[i]); + EXPECT_LE(l1, 1e-3f); + } +} +} // namespace + +// Disabled on Vulkan for now as the compiler generates invalid code for atomic add. +GPU_TEST(DiffPBRTDiffuse, Device::Type::D3D12) +{ + BsdfConfig bsdfConfig = { + normalize(float3(0.3f, 0.2f, 0.8f)), // wi + normalize(float3(-0.1f, -0.3f, 0.9f)), // wo + float4(0.9f, 0.6f, 0.2f, 1.f), // baseColor + }; + testDiffPBRTDiffuse(ctx, bsdfConfig); +} +} // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/DiffRendering/Material/DiffMaterialTests.cs.slang b/Source/Tools/FalcorTest/Tests/DiffRendering/Material/DiffMaterialTests.cs.slang new file mode 100644 index 000000000..47c42f9e8 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/DiffRendering/Material/DiffMaterialTests.cs.slang @@ -0,0 +1,99 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Utils/Math/MathConstants.slangh" + +import Scene.Shading; +import Utils.Sampling.UniformSampleGenerator; +import DiffRendering.SceneGradients; +import DiffRendering.GradientIOWrapper; + +RWStructuredBuffer materialGrad; +RWStructuredBuffer geometryGrad; + +cbuffer CB +{ + float3 gWi; + float3 gWo; +} + +[Differentiable] +IMaterialInstance getDiffMaterialInstance(out DiffMaterialData diffData, const ShadingData sd) +{ + let lod = ExplicitLodTextureSampler(0.f); + let material = gScene.materials.getMaterial(sd.materialID); + let mi = material.setupDiffMaterialInstance(diffData, gScene.materials, sd, lod); + return mi; +} + +[Differentiable] +float3 evalAD_PBRTDiffuse(uint2 pixelID) +{ + // Setup shading data struct. + ShadingData sd = {}; + sd.frame = ShadingFrame::createIdentity(); + sd.faceN = detach(sd.frame.N); + sd.frontFacing = true; + + uint materialID = 0; + sd.mtl = gScene.materials.getMaterialHeader(materialID); + sd.materialID = materialID; + sd.IoR = 1.f; + + sd.V = gWi; + + sd.materialGradOffset = 0; + sd.geometryGradOffset = 0; + sd.threadID = 0; + + // Create MaterialInstance. + DiffMaterialData diffData = DiffMaterialData(); + IMaterialInstance mi = getDiffMaterialInstance(diffData, sd); + + UniformSampleGenerator sg = UniformSampleGenerator(pixelID, 0); + + // Setup differentiable wo. + uint hashIndex = hashFunction(sd.threadID, gSceneGradients.getHashSize(GradientType::Geometry)); + GradientIOWrapper gradIO = GradientIOWrapper(GradientType::Geometry, sd.geometryGradOffset, hashIndex); + float3 wo = gradIO.getFloat(gWo, 0); + + float3 value = mi.evalAD(diffData, sd, wo, sg); + return value; +} + +[numthreads(1, 1, 1)] +void testDiffPBRTDiffuse(uint3 threadID: SV_DispatchThreadID) +{ + bwd_diff(evalAD_PBRTDiffuse)(threadID.xy, float3(1.f)); + + [ForceUnroll] + for (uint i = 0; i < 3; i++) + { + materialGrad[i] = gSceneGradients.getGrad(GradientType::Material, i, 0); + geometryGrad[i] = gSceneGradients.getGrad(GradientType::Geometry, i, 0); + } +} diff --git a/Source/Tools/FalcorTest/Tests/DiffRendering/SceneGradientsTest.cpp b/Source/Tools/FalcorTest/Tests/DiffRendering/SceneGradientsTest.cpp new file mode 100644 index 000000000..1836f25d2 --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/DiffRendering/SceneGradientsTest.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +#include "Testing/UnitTest.h" +#include "DiffRendering/SceneGradients.h" + +namespace Falcor +{ +namespace +{ +const char kShaderFile[] = "Tests/DiffRendering/SceneGradientsTest.cs.slang"; + +void testAggregateGradients(GPUUnitTestContext& ctx, const uint32_t hashSize) +{ + // We create a gradient vector with dimension = 3. + // We add 10^i to the i-th element for 1024 times (using atomic add). + // So the expected value of the i-th element is 1024 * (10^i). + + const uint32_t gradDim = 3; + const uint32_t elemCount = 1024; + + ref pDevice = ctx.getDevice(); + RenderContext* pRenderContext = pDevice->getRenderContext(); + + std::unique_ptr pSceneGradients = std::make_unique(pDevice, uint2(gradDim), uint2(hashSize)); + pSceneGradients->clearGrads(pRenderContext, GradientType::Material); + + ctx.createProgram(kShaderFile, "atomicAdd"); + ctx["CB"]["sz"] = uint2(gradDim, elemCount); + ctx["CB"]["hashSize"] = hashSize; + pSceneGradients->bindShaderData(ctx["gSceneGradients"]); + ctx.runProgram(gradDim, elemCount, 1); + + pSceneGradients->aggregateGrads(pRenderContext, GradientType::Material); + + ctx.createProgram(kShaderFile, "testAggregateGradients"); + ctx["CB"]["sz"] = uint2(gradDim, elemCount); + ctx["grads"] = pSceneGradients->getGradsBuffer(GradientType::Material); + ctx.allocateStructuredBuffer("result", gradDim); + ctx.runProgram(gradDim, 1, 1); + + std::vector result = ctx.readBuffer("result"); + for (uint32_t i = 0; i < gradDim; ++i) + { + float refValue = elemCount * std::pow(10.f, i); + float relAbsDiff = std::abs(result[i] - refValue) / refValue; + EXPECT_LE(relAbsDiff, 1e-6f); + } +} +} // namespace + +// Disabled on Vulkan for now as the compiler generates invalid code. +GPU_TEST(AggregateGradients, Device::Type::D3D12) +{ + testAggregateGradients(ctx, 64); +} +} // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/DiffRendering/SceneGradientsTest.cs.slang b/Source/Tools/FalcorTest/Tests/DiffRendering/SceneGradientsTest.cs.slang new file mode 100644 index 000000000..9ef5faeeb --- /dev/null +++ b/Source/Tools/FalcorTest/Tests/DiffRendering/SceneGradientsTest.cs.slang @@ -0,0 +1,54 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ +import DiffRendering.SceneGradients; + +cbuffer CB +{ + uint2 sz; + uint hashSize; +} + +ByteAddressBuffer grads; +RWStructuredBuffer result; + +[numthreads(4, 16, 1)] +void atomicAdd(uint3 threadID: SV_DispatchThreadID) +{ + if (any(threadID.xy >= sz)) + return; + gSceneGradients.atomicAddGrad(GradientType::Material, threadID.x, threadID.y % hashSize, pow(10.f, float(threadID.x))); +} + +[numthreads(4, 1, 1)] +void testAggregateGradients(uint3 threadID: SV_DispatchThreadID) +{ + if (threadID.x >= sz.x) + return; + float value = asfloat(grads.Load(threadID.x * 4)); + result[threadID.x] = value; +} diff --git a/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cpp b/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cpp index ee844e9cc..e4503ac72 100644 --- a/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cpp @@ -118,8 +118,8 @@ void setupSamplingTest(GPUUnitTestContext& ctx, const SamplingTestSpec& spec, co float theta = M_PI * spec.incidentAngles[i] / 180.f; testWi.push_back({std::sin(theta), 0.f, std::cos(theta)}); } - auto pTestWiBuffer = Buffer::createStructured( - pDevice, var["testWi"], testCount, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, testWi.data() + auto pTestWiBuffer = pDevice->createStructuredBuffer( + var["testWi"], testCount, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, testWi.data() ); var["testWi"] = pTestWiBuffer; } @@ -134,26 +134,23 @@ std::vector> tabulateHistogram(GPUUnitTestContext& ctx, cons setupSamplingTest(ctx, spec, "tabulateHistogram"); auto var = ctx.vars().getRootVar()["gMicrofacetSamplingTest"]; - auto pHistogramBuffer = Buffer::createStructured(pDevice, var["histogramSampling"], testCount * binCount); + auto pHistogramBuffer = pDevice->createStructuredBuffer(var["histogramSampling"], testCount * binCount); var["histogramSampling"] = pHistogramBuffer; ctx.getRenderContext()->clearUAV(pHistogramBuffer->getUAV().get(), uint4(0)); ctx.runProgram(spec.sampleCount / spec.threadSampleCount, 1, testCount); - auto pHistogramData = reinterpret_cast(pHistogramBuffer->map(Buffer::MapType::Read)); - + std::vector histogramData = pHistogramBuffer->getElements(); std::vector> histograms; for (uint32_t testIndex = 0; testIndex < testCount; ++testIndex) { std::vector histogram(binCount); for (uint32_t i = 0; i < binCount; ++i) - histogram[i] = (double)pHistogramData[testIndex * binCount + i]; + histogram[i] = (double)histogramData[testIndex * binCount + i]; histograms.push_back(histogram); } - pHistogramBuffer->unmap(); - return histograms; } @@ -167,23 +164,20 @@ std::vector> tabulatePdf(GPUUnitTestContext& ctx, const Samp setupSamplingTest(ctx, spec, "tabulatePdf"); auto var = ctx.vars().getRootVar()["gMicrofacetSamplingTest"]; - auto pHistogramBuffer = Buffer::createStructured(pDevice, var["histogramPdf"], testCount * binCount); + auto pHistogramBuffer = pDevice->createStructuredBuffer(var["histogramPdf"], testCount * binCount); var["histogramPdf"] = pHistogramBuffer; ctx.runProgram(spec.phiBinCount, spec.cosThetaBinCount, testCount); - auto pHistogramData = reinterpret_cast(pHistogramBuffer->map(Buffer::MapType::Read)); - + std::vector histogramData = pHistogramBuffer->getElements(); std::vector> histograms; for (uint32_t testIndex = 0; testIndex < testCount; ++testIndex) { size_t offset = testIndex * binCount; - histograms.push_back(std::vector(pHistogramData + offset, pHistogramData + offset + binCount)); + histograms.push_back(std::vector(histogramData.data() + offset, histogramData.data() + offset + binCount)); } - pHistogramBuffer->unmap(); - return histograms; } diff --git a/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cs.slang b/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cs.slang index f170b14c3..e032efe24 100644 --- a/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cs.slang +++ b/Source/Tools/FalcorTest/Tests/Rendering/Materials/MicrofacetTests.cs.slang @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Tools/FalcorTest/Tests/Sampling/AliasTableTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/AliasTableTests.cpp index 614ca854e..23f6293bd 100644 --- a/Source/Tools/FalcorTest/Tests/Sampling/AliasTableTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Sampling/AliasTableTests.cpp @@ -80,7 +80,7 @@ void testAliasTable(GPUUnitTestContext& ctx, uint32_t N, std::vector spec ctx.createProgram("Tests/Sampling/AliasTableTests.cs.slang", "testAliasTableSample"); ctx.allocateStructuredBuffer("sampleResult", resultCount); ctx.allocateStructuredBuffer("random", randomCount, random.data()); - aliasTable.setShaderData(ctx["CB"]["aliasTable"]); + aliasTable.bindShaderData(ctx["CB"]["aliasTable"]); ctx["CB"]["resultCount"] = resultCount; ctx.runProgram(resultCount); @@ -125,7 +125,7 @@ void testAliasTable(GPUUnitTestContext& ctx, uint32_t N, std::vector spec // Setup and run GPU test. ctx.createProgram("Tests/Sampling/AliasTableTests.cs.slang", "testAliasTableWeight"); ctx.allocateStructuredBuffer("weightResult", resultCount); - aliasTable.setShaderData(ctx["CB"]["aliasTable"]); + aliasTable.bindShaderData(ctx["CB"]["aliasTable"]); ctx["CB"]["resultCount"] = resultCount; ctx.runProgram(resultCount); diff --git a/Source/Tools/FalcorTest/Tests/Sampling/PointSetsTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/PointSetsTests.cpp index b890e52e7..5057d2fe8 100644 --- a/Source/Tools/FalcorTest/Tests/Sampling/PointSetsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Sampling/PointSetsTests.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp index e717e41e9..00db6985b 100644 --- a/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Sampling/PseudorandomTests.cpp @@ -112,7 +112,7 @@ ref createSeed(ref pDevice, size_t elements, std::vector pSeedBuf = - Buffer::create(pDevice, seed.size() * sizeof(seed[0]), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, seed.data()); + pDevice->createBuffer(seed.size() * sizeof(seed[0]), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, seed.data()); FALCOR_ASSERT(pSeedBuf); return pSeedBuf; } diff --git a/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp b/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp index 18f12cfa2..ed9037145 100644 --- a/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Sampling/SampleGeneratorTests.cpp @@ -78,10 +78,10 @@ void testSampleGenerator(GPUUnitTestContext& ctx, uint32_t type, const double me // Setup GPU test. // We defer the creation of the vars until after shader specialization. auto defines = pSampleGenerator->getDefines(); - ctx.createProgram(kShaderFile, "test", defines, Program::CompilerFlags::None, "6_2"); + ctx.createProgram(kShaderFile, "test", defines, SlangCompilerFlags::None, ShaderModel::SM6_2); pSampleGenerator->beginFrame(ctx.getRenderContext(), uint2(kDispatchDim.x, kDispatchDim.y)); - pSampleGenerator->setShaderData(ctx.vars().getRootVar()); + pSampleGenerator->bindShaderData(ctx.vars().getRootVar()); const size_t numSamples = kDispatchDim.x * kDispatchDim.y * kDispatchDim.z * kDimensions; ctx.allocateStructuredBuffer("result", uint32_t(numSamples)); diff --git a/Source/Tools/FalcorTest/Tests/Scene/EnvMapTests.cpp b/Source/Tools/FalcorTest/Tests/Scene/EnvMapTests.cpp index 0eb1fde90..125fc5133 100644 --- a/Source/Tools/FalcorTest/Tests/Scene/EnvMapTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Scene/EnvMapTests.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Testing/UnitTest.h" +#include "Core/AssetResolver.h" #include "Scene/Lights/EnvMap.h" #include "Rendering/Lights/EnvMapSampler.h" @@ -33,8 +34,8 @@ namespace Falcor { namespace { -// This file is located in the media/ directory fetched by packman. -const char kEnvMapFile[] = "test_scenes/envmaps/20050806-03_hd.hdr"; +// TODO: This is not ideal, we should only access files in the runtime directory. +const std::filesystem::path kEnvMapPath = getProjectDirectory() / "media/test_scenes/envmaps/20050806-03_hd.hdr"; } // namespace GPU_TEST(EnvMap) @@ -42,7 +43,7 @@ GPU_TEST(EnvMap) // Test loading a light probe. // This call runs setup code on the GPU to precompute the importance map. // If it succeeds, we at least know the code compiles and run. - ref pEnvMap = EnvMap::createFromFile(ctx.getDevice(), kEnvMapFile); + ref pEnvMap = EnvMap::createFromFile(ctx.getDevice(), kEnvMapPath); EXPECT_NE(pEnvMap, nullptr); if (pEnvMap == nullptr) return; diff --git a/Source/Tools/FalcorTest/Tests/Scene/Material/BSDFTests.cpp b/Source/Tools/FalcorTest/Tests/Scene/Material/BSDFTests.cpp index 0e5959035..268a8ee60 100644 --- a/Source/Tools/FalcorTest/Tests/Scene/Material/BSDFTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Scene/Material/BSDFTests.cpp @@ -126,14 +126,14 @@ void setupSamplingTest(GPUUnitTestContext& ctx, const SamplingTestSpec& spec, co var["binSampleCount"] = spec.binSampleCount; auto testWi = BsdfConfig::getWi(spec.bsdfConfigs); - auto pTestWiBuffer = Buffer::createStructured( - pDevice, var["testWi"], testCount, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, testWi.data() + auto pTestWiBuffer = pDevice->createStructuredBuffer( + var["testWi"], testCount, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, testWi.data() ); var["testWi"] = pTestWiBuffer; auto testParams = BsdfConfig::getParams(spec.bsdfConfigs); - auto pTestParamsBuffer = Buffer::createStructured( - pDevice, var["testParams"], testCount, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, testParams.data() + auto pTestParamsBuffer = pDevice->createStructuredBuffer( + var["testParams"], testCount, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, testParams.data() ); var["testParams"] = pTestParamsBuffer; } @@ -148,26 +148,23 @@ std::vector> tabulateHistogram(GPUUnitTestContext& ctx, cons setupSamplingTest(ctx, spec, "tabulateHistogram"); auto var = ctx["SamplingTestCB"]["gSamplingTest"]; - auto pHistogramBuffer = Buffer::createStructured(pDevice, var["histogramSampling"], testCount * binCount); + auto pHistogramBuffer = pDevice->createStructuredBuffer(var["histogramSampling"], testCount * binCount); var["histogramSampling"] = pHistogramBuffer; ctx.getRenderContext()->clearUAV(pHistogramBuffer->getUAV().get(), uint4(0)); ctx.runProgram(spec.sampleCount / spec.threadSampleCount, 1, testCount); - auto pHistogramData = reinterpret_cast(pHistogramBuffer->map(Buffer::MapType::Read)); - + std::vector histogramData = pHistogramBuffer->getElements(); std::vector> histograms; for (uint32_t testIndex = 0; testIndex < testCount; ++testIndex) { std::vector histogram(binCount); for (uint32_t i = 0; i < binCount; ++i) - histogram[i] = (double)pHistogramData[testIndex * binCount + i]; + histogram[i] = (double)histogramData[testIndex * binCount + i]; histograms.push_back(histogram); } - pHistogramBuffer->unmap(); - return histograms; } @@ -181,23 +178,20 @@ std::vector> tabulatePdf(GPUUnitTestContext& ctx, const Samp setupSamplingTest(ctx, spec, "tabulatePdf"); auto var = ctx["SamplingTestCB"]["gSamplingTest"]; - auto pHistogramBuffer = Buffer::createStructured(pDevice, var["histogramPdf"], testCount * binCount); + auto pHistogramBuffer = pDevice->createStructuredBuffer(var["histogramPdf"], testCount * binCount); var["histogramPdf"] = pHistogramBuffer; ctx.runProgram(spec.phiBinCount, spec.cosThetaBinCount, testCount); - auto pHistogramData = reinterpret_cast(pHistogramBuffer->map(Buffer::MapType::Read)); - + std::vector histogramData = pHistogramBuffer->getElements(); std::vector> histograms; for (uint32_t testIndex = 0; testIndex < testCount; ++testIndex) { size_t offset = testIndex * binCount; - histograms.push_back(std::vector(pHistogramData + offset, pHistogramData + offset + binCount)); + histograms.push_back(std::vector(histogramData.data() + offset, histogramData.data() + offset + binCount)); } - pHistogramBuffer->unmap(); - return histograms; } @@ -214,18 +208,18 @@ std::vector, std::vector>> tabulateWeightA setupSamplingTest(ctx, spec, "tabulateWeightAndPdfError"); auto var = ctx["SamplingTestCB"]["gSamplingTest"]; - auto pHistogramWeightBuffer = Buffer::createStructured(pDevice, var["histogramWeightError"], testCount * binCount); + auto pHistogramWeightBuffer = pDevice->createStructuredBuffer(var["histogramWeightError"], testCount * binCount); var["histogramWeightError"] = pHistogramWeightBuffer; ctx.getRenderContext()->clearUAV(pHistogramWeightBuffer->getUAV().get(), uint4(0)); - auto pHistogramPdfBuffer = Buffer::createStructured(pDevice, var["histogramPdfError"], testCount * binCount); + auto pHistogramPdfBuffer = pDevice->createStructuredBuffer(var["histogramPdfError"], testCount * binCount); var["histogramPdfError"] = pHistogramPdfBuffer; ctx.getRenderContext()->clearUAV(pHistogramPdfBuffer->getUAV().get(), uint4(0)); ctx.runProgram(spec.sampleCount / spec.threadSampleCount, 1, testCount); - auto pHistogramWeightData = reinterpret_cast(pHistogramWeightBuffer->map(Buffer::MapType::Read)); - auto pHistogramPdfData = reinterpret_cast(pHistogramPdfBuffer->map(Buffer::MapType::Read)); + std::vector histogramWeightData = pHistogramWeightBuffer->getElements(); + std::vector histogramPdfData = pHistogramPdfBuffer->getElements(); std::vector, std::vector>> histograms; @@ -233,18 +227,15 @@ std::vector, std::vector>> tabulateWeightA { std::vector histogramWeight(binCount); for (uint32_t i = 0; i < binCount; ++i) - histogramWeight[i] = (double)pHistogramWeightData[testIndex * binCount + i]; + histogramWeight[i] = (double)histogramWeightData[testIndex * binCount + i]; std::vector histogramPdf(binCount); for (uint32_t i = 0; i < binCount; ++i) - histogramPdf[i] = (double)pHistogramPdfData[testIndex * binCount + i]; + histogramPdf[i] = (double)histogramPdfData[testIndex * binCount + i]; histograms.emplace_back(histogramWeight, histogramPdf); } - pHistogramWeightBuffer->unmap(); - pHistogramPdfBuffer->unmap(); - return histograms; } @@ -321,14 +312,15 @@ GPU_TEST(TestBsdf_DisneyDiffuseBRDF) const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); testSampling( - ctx, {"Rendering.Materials.BSDFs.DisneyDiffuseBRDF", - "DisneyDiffuseBRDF", - "bsdf.albedo = float3(1.f); bsdf.roughness = 0.5f;", - { - {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, - {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, - {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, - }} + ctx, + {"Rendering.Materials.BSDFs.DisneyDiffuseBRDF", + "DisneyDiffuseBRDF", + "bsdf.albedo = float3(1.f); bsdf.roughness = 0.5f;", + { + {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, + {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, + {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, + }} ); } @@ -339,14 +331,15 @@ GPU_TEST(TestBsdf_FrostbiteDiffuseBRDF) const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); testSampling( - ctx, {"Rendering.Materials.BSDFs.FrostbiteDiffuseBRDF", - "FrostbiteDiffuseBRDF", - "bsdf.albedo = float3(1.f); bsdf.roughness = 0.5f;", - { - {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, - {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, - {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, - }} + ctx, + {"Rendering.Materials.BSDFs.FrostbiteDiffuseBRDF", + "FrostbiteDiffuseBRDF", + "bsdf.albedo = float3(1.f); bsdf.roughness = 0.5f;", + { + {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, + {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, + {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, + }} ); } @@ -357,14 +350,15 @@ GPU_TEST(TestBsdf_LambertDiffuseBRDF) const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); testSampling( - ctx, {"Rendering.Materials.BSDFs.LambertDiffuseBRDF", - "LambertDiffuseBRDF", - "bsdf.albedo = float3(1.f);", - { - {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, - {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, - {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, - }} + ctx, + {"Rendering.Materials.BSDFs.LambertDiffuseBRDF", + "LambertDiffuseBRDF", + "bsdf.albedo = float3(1.f);", + { + {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, + {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, + {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, + }} ); } @@ -375,14 +369,15 @@ GPU_TEST(TestBsdf_LambertDiffuseBTDF) const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); testSampling( - ctx, {"Rendering.Materials.BSDFs.LambertDiffuseBTDF", - "LambertDiffuseBTDF", - "bsdf.albedo = float3(1.f);", - { - {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, - {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, - {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, - }} + ctx, + {"Rendering.Materials.BSDFs.LambertDiffuseBTDF", + "LambertDiffuseBTDF", + "bsdf.albedo = float3(1.f);", + { + {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, + {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, + {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, + }} ); } @@ -393,14 +388,15 @@ GPU_TEST(TestBsdf_OrenNayarBRDF) const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); testSampling( - ctx, {"Rendering.Materials.BSDFs.OrenNayarBRDF", - "OrenNayarBRDF", - "bsdf.albedo = float3(1.f); bsdf.roughness = 0.5f", - { - {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, - {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, - {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, - }} + ctx, + {"Rendering.Materials.BSDFs.OrenNayarBRDF", + "OrenNayarBRDF", + "bsdf.albedo = float3(1.f); bsdf.roughness = 0.5f", + { + {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, + {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, + {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, + }} ); } @@ -411,14 +407,15 @@ GPU_TEST(TestBsdf_SimpleBTDF, "Disabled, sampling test makes no sense for diract const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); testSampling( - ctx, {"Rendering.Materials.BSDFs.SimpleBTDF", - "SimpleBTDF", - "bsdf.transmittance = float3(1.f);", - { - {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, - {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, - {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, - }} + ctx, + {"Rendering.Materials.BSDFs.SimpleBTDF", + "SimpleBTDF", + "bsdf.transmittance = float3(1.f);", + { + {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, + {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, + {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, + }} ); } @@ -429,17 +426,18 @@ GPU_TEST(TestBsdf_SpecularMicrofacetBRDF) const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); testSampling( - ctx, {"Rendering.Materials.BSDFs.SpecularMicrofacet", - "SpecularMicrofacetBRDF", - "bsdf.albedo = float3(1.f); bsdf.activeLobes = 0xff; bsdf.alpha = params.x;", - { - {"smooth_perp", perp, {0.05f, 0.f, 0.f, 0.f}}, - {"smooth_oblique", oblique, {0.05f, 0.f, 0.f, 0.f}}, - {"smooth_grazing", grazing, {0.05f, 0.f, 0.f, 0.f}}, - {"rough_perp", perp, {0.5f, 0.f, 0.f, 0.f}}, - {"rough_oblique", oblique, {0.5f, 0.f, 0.f, 0.f}}, - {"rough_grazing", grazing, {0.5f, 0.f, 0.f, 0.f}}, - }} + ctx, + {"Rendering.Materials.BSDFs.SpecularMicrofacet", + "SpecularMicrofacetBRDF", + "bsdf.albedo = float3(1.f); bsdf.activeLobes = 0xff; bsdf.alpha = params.x;", + { + {"smooth_perp", perp, {0.05f, 0.f, 0.f, 0.f}}, + {"smooth_oblique", oblique, {0.05f, 0.f, 0.f, 0.f}}, + {"smooth_grazing", grazing, {0.05f, 0.f, 0.f, 0.f}}, + {"rough_perp", perp, {0.5f, 0.f, 0.f, 0.f}}, + {"rough_oblique", oblique, {0.5f, 0.f, 0.f, 0.f}}, + {"rough_grazing", grazing, {0.5f, 0.f, 0.f, 0.f}}, + }} ); } @@ -450,23 +448,24 @@ GPU_TEST(TestBsdf_SpecularMicrofacetBSDF, "Disabled, not passing") const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); testSampling( - ctx, {"Rendering.Materials.BSDFs.SpecularMicrofacet", - "SpecularMicrofacetBSDF", - "bsdf.activeLobes = 0xff; bsdf.transmissionAlbedo = float3(1.f); bsdf.alpha = params.x; bsdf.eta = params.y;", - { - {"to_glass_smooth_perp", perp, {0.05f, 0.67f, 0.f, 0.f}}, - {"to_glass_smooth_oblique", oblique, {0.05f, 0.67f, 0.f, 0.f}}, - {"to_glass_smooth_grazing", grazing, {0.05f, 0.67f, 0.f, 0.f}}, - {"to_glass_rough_perp", perp, {0.5f, 0.67f, 0.f, 0.f}}, - {"to_glass_rough_oblique", oblique, {0.5f, 0.67f, 0.f, 0.f}}, - {"to_glass_rough_grazing", grazing, {0.5f, 0.67f, 0.f, 0.f}}, - {"from_glass_smooth_perp", perp, {0.05f, 1.5f, 0.f, 0.f}}, - {"from_glass_smooth_oblique", oblique, {0.05f, 1.5f, 0.f, 0.f}}, - {"from_glass_smooth_grazing", grazing, {0.05f, 1.5f, 0.f, 0.f}}, - {"from_glass_rough_perp", perp, {0.5f, 1.5f, 0.f, 0.f}}, - {"from_glass_rough_oblique", oblique, {0.5f, 1.5f, 0.f, 0.f}}, - {"from_glass_rough_grazing", grazing, {0.5f, 1.5f, 0.f, 0.f}}, - }} + ctx, + {"Rendering.Materials.BSDFs.SpecularMicrofacet", + "SpecularMicrofacetBSDF", + "bsdf.activeLobes = 0xff; bsdf.transmissionAlbedo = float3(1.f); bsdf.alpha = params.x; bsdf.eta = params.y;", + { + {"to_glass_smooth_perp", perp, {0.05f, 0.67f, 0.f, 0.f}}, + {"to_glass_smooth_oblique", oblique, {0.05f, 0.67f, 0.f, 0.f}}, + {"to_glass_smooth_grazing", grazing, {0.05f, 0.67f, 0.f, 0.f}}, + {"to_glass_rough_perp", perp, {0.5f, 0.67f, 0.f, 0.f}}, + {"to_glass_rough_oblique", oblique, {0.5f, 0.67f, 0.f, 0.f}}, + {"to_glass_rough_grazing", grazing, {0.5f, 0.67f, 0.f, 0.f}}, + {"from_glass_smooth_perp", perp, {0.05f, 1.5f, 0.f, 0.f}}, + {"from_glass_smooth_oblique", oblique, {0.05f, 1.5f, 0.f, 0.f}}, + {"from_glass_smooth_grazing", grazing, {0.05f, 1.5f, 0.f, 0.f}}, + {"from_glass_rough_perp", perp, {0.5f, 1.5f, 0.f, 0.f}}, + {"from_glass_rough_oblique", oblique, {0.5f, 1.5f, 0.f, 0.f}}, + {"from_glass_rough_grazing", grazing, {0.5f, 1.5f, 0.f, 0.f}}, + }} ); } @@ -477,14 +476,15 @@ GPU_TEST(TestBsdf_SheenBSDF) const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); testSampling( - ctx, {"Rendering.Materials.BSDFs.SheenBSDF", - "SheenBSDF", - "bsdf.color = float3(1.f); bsdf.roughness = 0.5f", - { - {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, - {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, - {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, - }} + ctx, + {"Rendering.Materials.BSDFs.SheenBSDF", + "SheenBSDF", + "bsdf.color = float3(1.f); bsdf.roughness = 0.5f", + { + {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, + {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, + {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, + }} ); } @@ -495,14 +495,15 @@ GPU_TEST(TestBsdf_DiffuseSpecularBRDF) const float3 grazing = normalize(float3(0.f, 1.f, 0.01f)); testSampling( - ctx, {"Rendering.Materials.BSDFs.DiffuseSpecularBRDF", - "DiffuseSpecularBRDF", - "bsdf.diffuse = float3(0.5f); bsdf.specular = float3(0.04f); bsdf.roughness = 0.5f", - { - {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, - {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, - {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, - }} + ctx, + {"Rendering.Materials.BSDFs.DiffuseSpecularBRDF", + "DiffuseSpecularBRDF", + "bsdf.diffuse = float3(0.5f); bsdf.specular = float3(0.04f); bsdf.roughness = 0.5f", + { + {"perp", perp, {0.f, 0.f, 0.f, 0.f}}, + {"oblique", oblique, {0.f, 0.f, 0.f, 0.f}}, + {"grazing", grazing, {0.f, 0.f, 0.f, 0.f}}, + }} ); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Scene/Material/HairChiang16Tests.cpp b/Source/Tools/FalcorTest/Tests/Scene/Material/HairChiang16Tests.cpp index a1a57a2b9..bd850fe74 100644 --- a/Source/Tools/FalcorTest/Tests/Scene/Material/HairChiang16Tests.cpp +++ b/Source/Tools/FalcorTest/Tests/Scene/Material/HairChiang16Tests.cpp @@ -56,14 +56,14 @@ void testWhiteFurnace(GPUUnitTestContext& ctx, const std::string& funcName, cons // Setup GPU test. auto defines = pSampleGenerator->getDefines(); - ctx.createProgram(kShaderFile, funcName, defines, Program::CompilerFlags::None, "6_2"); + ctx.createProgram(kShaderFile, funcName, defines, SlangCompilerFlags::None, ShaderModel::SM6_2); - pSampleGenerator->setShaderData(ctx.vars().getRootVar()); + pSampleGenerator->bindShaderData(ctx.vars().getRootVar()); ctx.allocateStructuredBuffer("roughness", testCount, testRoughness.data(), testRoughness.size() * sizeof(float2)); ctx.allocateStructuredBuffer("result", testCount); ctx["TestCB"]["resultSize"] = testCount; - ctx["TestCB"]["sampleCount"] = 300000; + ctx["TestCB"]["sampleCount"] = 300000u; ctx.runProgram(testCount); @@ -107,7 +107,7 @@ GPU_TEST(HairChiang16_PbrtReference) // Setup GPU test. auto defines = pSampleGenerator->getDefines(); - ctx.createProgram(kShaderFile, "testPbrtReference", defines, Program::CompilerFlags::None, "6_2"); + ctx.createProgram(kShaderFile, "testPbrtReference", defines, SlangCompilerFlags::None, ShaderModel::SM6_2); ctx.allocateStructuredBuffer("gBetaM", testCount, buf.data()); ctx.allocateStructuredBuffer("gBetaN", testCount, buf.data() + testCount); @@ -164,9 +164,9 @@ GPU_TEST(HairChiang16_ImportanceSamplingWeights) // Setup GPU test. auto defines = pSampleGenerator->getDefines(); - ctx.createProgram(kShaderFile, "testImportanceSamplingWeights", defines, Program::CompilerFlags::None, "6_2"); + ctx.createProgram(kShaderFile, "testImportanceSamplingWeights", defines, SlangCompilerFlags::None, ShaderModel::SM6_2); - pSampleGenerator->setShaderData(ctx.vars().getRootVar()); + pSampleGenerator->bindShaderData(ctx.vars().getRootVar()); ctx.allocateStructuredBuffer("roughness", testCount, testRoughness.data(), testRoughness.size() * sizeof(float2)); ctx.allocateStructuredBuffer("result", testCount * sampleCount); @@ -213,9 +213,9 @@ GPU_TEST(HairChiang16_SamplingConsistency) // Setup GPU test. auto defines = pSampleGenerator->getDefines(); - ctx.createProgram(kShaderFile, "testSamplingConsistency", defines, Program::CompilerFlags::None, "6_2"); + ctx.createProgram(kShaderFile, "testSamplingConsistency", defines, SlangCompilerFlags::None, ShaderModel::SM6_2); - pSampleGenerator->setShaderData(ctx.vars().getRootVar()); + pSampleGenerator->bindShaderData(ctx.vars().getRootVar()); ctx.allocateStructuredBuffer("roughness", testCount, testRoughness.data(), testRoughness.size() * sizeof(float2)); ctx.allocateStructuredBuffer("result", testCount); diff --git a/Source/Tools/FalcorTest/Tests/Scene/Material/MERLFileTests.cpp b/Source/Tools/FalcorTest/Tests/Scene/Material/MERLFileTests.cpp index eee349773..ce8c1b170 100644 --- a/Source/Tools/FalcorTest/Tests/Scene/Material/MERLFileTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Scene/Material/MERLFileTests.cpp @@ -26,6 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "Testing/UnitTest.h" +#include "Core/AssetResolver.h" #include "Scene/Material/MERLFile.h" #include "Scene/Material/MERLMaterialData.slang" @@ -33,13 +34,11 @@ namespace Falcor { GPU_TEST(MERLFile) { - const std::filesystem::path path = "test_scenes/materials/data/gray-lambert.binary"; - - std::filesystem::path fullPath; - ASSERT(findFileInDataDirectories(path, fullPath)); + // TODO: This is not ideal, we should only access files in the runtime directory. + const std::filesystem::path path = getProjectDirectory() / "media/test_scenes/materials/data/gray-lambert.binary"; MERLFile merlFile; - bool result = merlFile.loadBRDF(fullPath); + bool result = merlFile.loadBRDF(path); ASSERT(result); const auto desc = merlFile.getDesc(); diff --git a/Source/Tools/FalcorTest/Tests/Slang/CastFloat16.cpp b/Source/Tools/FalcorTest/Tests/Slang/CastFloat16.cpp index 72930629f..8dead80c3 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/CastFloat16.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/CastFloat16.cpp @@ -42,15 +42,15 @@ GPU_TEST(CastFloat16) { ref pDevice = ctx.getDevice(); - ctx.createProgram("Tests/Slang/CastFloat16.cs.slang", "testCastFloat16", DefineList(), Program::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/CastFloat16.cs.slang", "testCastFloat16", DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_5); ctx.allocateStructuredBuffer("result", kNumElems); std::vector elems(kNumElems * 2); for (auto& v : elems) v = f32tof16(float(u(r))); auto var = ctx.vars().getRootVar(); - var["data"] = Buffer::createStructured( - pDevice, var["data"], (uint32_t)elems.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, elems.data() + var["data"] = pDevice->createStructuredBuffer( + var["data"], (uint32_t)elems.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, elems.data() ); ctx.runProgram(kNumElems, 1, 1); diff --git a/Source/Tools/FalcorTest/Tests/Slang/Float16Tests.cpp b/Source/Tools/FalcorTest/Tests/Slang/Float16Tests.cpp index 913f56c33..e6e55d57d 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/Float16Tests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/Float16Tests.cpp @@ -33,30 +33,33 @@ namespace Falcor { namespace { -std::vector kShaderModels = { - {"6_2"}, - {"6_3"}, +std::vector kShaderModels = { + {ShaderModel::SM6_2}, + {ShaderModel::SM6_3}, }; const uint32_t kNumElems = 256; std::mt19937 r; std::uniform_real_distribution u; -void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) +void test(GPUUnitTestContext& ctx, ShaderModel shaderModel, bool useUav) { ref pDevice = ctx.getDevice(); DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; - ctx.createProgram("Tests/Slang/Float16Tests.cs.slang", "testFloat16", defines, Program::CompilerFlags::None, shaderModel); + ctx.createProgram("Tests/Slang/Float16Tests.cs.slang", "testFloat16", defines, SlangCompilerFlags::None, shaderModel); ctx.allocateStructuredBuffer("result", kNumElems); std::vector elems(kNumElems); for (auto& v : elems) v = f32tof16(float(u(r))); auto var = ctx.vars().getRootVar(); - auto pBuf = Buffer::createStructured( - pDevice, var["data"], kNumElems, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, + auto pBuf = pDevice->createStructuredBuffer( + var["data"], + kNumElems, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType::DeviceLocal, elems.data() ); var["data"] = pBuf; @@ -67,7 +70,7 @@ void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kNumElems; i++) { - EXPECT_EQ(result[i], elems[i]) << "i = " << i << " shaderModel=" << shaderModel; + EXPECT_EQ(result[i], elems[i]) << "i = " << i << " shaderModel=" << enumToString(shaderModel); } } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Slang/Float64Tests.cpp b/Source/Tools/FalcorTest/Tests/Slang/Float64Tests.cpp index b10509b8a..08b9b40c6 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/Float64Tests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/Float64Tests.cpp @@ -34,30 +34,33 @@ namespace Falcor { namespace { -std::vector kShaderModels = { - {"6_2"}, - {"6_3"}, +std::vector kShaderModels = { + {ShaderModel::SM6_2}, + {ShaderModel::SM6_3}, }; const uint32_t kNumElems = 256; std::mt19937 r; std::uniform_real_distribution u; -void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) +void test(GPUUnitTestContext& ctx, ShaderModel shaderModel, bool useUav) { ref pDevice = ctx.getDevice(); DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; - ctx.createProgram("Tests/Slang/Float64Tests.cs.slang", "testFloat64", defines, Program::CompilerFlags::None, shaderModel); + ctx.createProgram("Tests/Slang/Float64Tests.cs.slang", "testFloat64", defines, SlangCompilerFlags::None, shaderModel); ctx.allocateStructuredBuffer("result", kNumElems); std::vector elems(kNumElems); for (auto& v : elems) v = fstd::bit_cast(u(r)); auto var = ctx.vars().getRootVar(); - auto pBuf = Buffer::createStructured( - pDevice, var["data"], kNumElems, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, + auto pBuf = pDevice->createStructuredBuffer( + var["data"], + kNumElems, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType::DeviceLocal, elems.data() ); var["data"] = pBuf; @@ -68,7 +71,7 @@ void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) std::vector result = ctx.readBuffer("result"); for (uint32_t i = 0; i < kNumElems; i++) { - EXPECT_EQ(result[i], elems[i]) << "i = " << i << " shaderModel=" << shaderModel; + EXPECT_EQ(result[i], elems[i]) << "i = " << i << " shaderModel=" << enumToString(shaderModel); } } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Slang/InheritanceTests.cpp b/Source/Tools/FalcorTest/Tests/Slang/InheritanceTests.cpp index b1c963036..8c354500f 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/InheritanceTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/InheritanceTests.cpp @@ -65,7 +65,9 @@ GPU_TEST(Inheritance_ManualCreate) DefineList defines; defines.add("NUM_TESTS", std::to_string(kNumTests)); - ctx.createProgram("Tests/Slang/InheritanceTests.cs.slang", "testInheritanceManual", defines, Program::CompilerFlags::None, "6_5"); + ctx.createProgram( + "Tests/Slang/InheritanceTests.cs.slang", "testInheritanceManual", defines, SlangCompilerFlags::None, ShaderModel::SM6_5 + ); ctx.allocateStructuredBuffer("resultsInt", kNumTests); ctx.allocateStructuredBuffer("resultsFloat", kNumTests); @@ -86,14 +88,14 @@ GPU_TEST(Inheritance_ManualCreate) } auto var = ctx.vars().getRootVar(); - var["testType"] = Buffer::createStructured( - pDevice, var["testType"], (uint32_t)testType.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, testType.data() + var["testType"] = pDevice->createStructuredBuffer( + var["testType"], (uint32_t)testType.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, testType.data() ); - var["testValue"] = Buffer::createStructured( - pDevice, var["testValue"], (uint32_t)testValue.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, testValue.data() + var["testValue"] = pDevice->createStructuredBuffer( + var["testValue"], (uint32_t)testValue.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, testValue.data() ); - var["data"] = Buffer::createStructured( - pDevice, var["data"], (uint32_t)testType.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data() + var["data"] = pDevice->createStructuredBuffer( + var["data"], (uint32_t)testType.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, data.data() ); ctx.runProgram(kNumTests, 1, 1); @@ -115,12 +117,12 @@ GPU_TEST(Inheritance_ConformanceCreate) DefineList defines; defines.add("NUM_TESTS", std::to_string(kNumTests)); - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary("Tests/Slang/InheritanceTests.cs.slang"); desc.csEntry("testInheritanceConformance"); - desc.setShaderModel("6_5"); + desc.setShaderModel(ShaderModel::SM6_5); - Program::TypeConformanceList typeConformancess{ + TypeConformanceList typeConformancess{ {{"TestV0SubNeg", "ITestInterface"}, 0}, {{"TestV1DefDef", "ITestInterface"}, 1}, {{"TestV2DefNeg", "ITestInterface"}, 2}, @@ -149,14 +151,14 @@ GPU_TEST(Inheritance_ConformanceCreate) } auto var = ctx.vars().getRootVar(); - var["testType"] = Buffer::createStructured( - pDevice, var["testType"], (uint32_t)testType.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, testType.data() + var["testType"] = pDevice->createStructuredBuffer( + var["testType"], (uint32_t)testType.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, testType.data() ); - var["testValue"] = Buffer::createStructured( - pDevice, var["testValue"], (uint32_t)testValue.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, testValue.data() + var["testValue"] = pDevice->createStructuredBuffer( + var["testValue"], (uint32_t)testValue.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, testValue.data() ); - var["data"] = Buffer::createStructured( - pDevice, var["data"], (uint32_t)testType.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data() + var["data"] = pDevice->createStructuredBuffer( + var["data"], (uint32_t)testType.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, data.data() ); ctx.runProgram(kNumTests, 1, 1); @@ -178,7 +180,7 @@ GPU_TEST(Inheritance_ConformanceCreate) // defines.add("NUM_TESTS", std::to_string(kNumTests)); // defines.add("COMPILE_WITH_ERROR", "1"); -// ctx.createProgram("Tests/Slang/InheritanceTests.cs.slang", "testInheritance", defines, Program::CompilerFlags::None, "6_5"); +// ctx.createProgram("Tests/Slang/InheritanceTests.cs.slang", "testInheritance", defines, SlangCompilerFlags::None, ShaderModel::SM6_5); // } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/Int64Tests.cpp b/Source/Tools/FalcorTest/Tests/Slang/Int64Tests.cpp index 23c529cea..c3a4c0283 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/Int64Tests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/Int64Tests.cpp @@ -32,31 +32,34 @@ namespace Falcor { namespace { -std::vector kShaderModels = { - {"6_0"}, - {"6_1"}, - {"6_2"}, - {"6_3"}, +std::vector kShaderModels = { + {ShaderModel::SM6_0}, + {ShaderModel::SM6_1}, + {ShaderModel::SM6_2}, + {ShaderModel::SM6_3}, }; const uint32_t kNumElems = 256; std::mt19937 r; -void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) +void test(GPUUnitTestContext& ctx, ShaderModel shaderModel, bool useUav) { ref pDevice = ctx.getDevice(); DefineList defines = {{"USE_UAV", useUav ? "1" : "0"}}; - ctx.createProgram("Tests/Slang/Int64Tests.cs.slang", "testInt64", defines, Program::CompilerFlags::None, shaderModel); + ctx.createProgram("Tests/Slang/Int64Tests.cs.slang", "testInt64", defines, SlangCompilerFlags::None, shaderModel); ctx.allocateStructuredBuffer("result", kNumElems * 2); std::vector elems(kNumElems); for (auto& v : elems) v = ((uint64_t)r() << 32) | r(); auto var = ctx.vars().getRootVar(); - auto pBuf = Buffer::createStructured( - pDevice, var["data"], kNumElems, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, + auto pBuf = pDevice->createStructuredBuffer( + var["data"], + kNumElems, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, + MemoryType::DeviceLocal, elems.data() ); var["data"] = pBuf; @@ -70,7 +73,7 @@ void test(GPUUnitTestContext& ctx, const std::string& shaderModel, bool useUav) uint32_t lo = result[2 * i]; uint32_t hi = result[2 * i + 1]; uint64_t res = ((uint64_t)hi << 32) | lo; - EXPECT_EQ(res, elems[i]) << "i = " << i << " shaderModel=" << shaderModel; + EXPECT_EQ(res, elems[i]) << "i = " << i << " shaderModel=" << enumToString(shaderModel); } } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Slang/NestedStructs.cpp b/Source/Tools/FalcorTest/Tests/Slang/NestedStructs.cpp index c0ef76bde..f9fa50a4b 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/NestedStructs.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/NestedStructs.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -42,19 +42,19 @@ GPU_TEST(NestedStructs) ShaderVar var = ctx.vars().getRootVar()["CB"]; var["a"] = 1.1f; - var["s3"]["a"] = 17; + var["s3"]["a"] = 17u; var["s3"]["b"] = true; var["s3"]["s2"]["a"] = bool3(true, false, true); var["s3"]["s2"]["s1"]["a"] = float2(9.3f, 2.1f); - var["s3"]["s2"]["s1"]["b"] = 23; + var["s3"]["s2"]["s1"]["b"] = 23u; var["s3"]["s2"]["b"] = 0.99f; var["s3"]["s2"]["c"] = uint2(4, 8); var["s3"]["c"] = float3(0.1f, 0.2f, 0.3f); var["s3"]["s1"]["a"] = float2(1.88f, 1.99f); - var["s3"]["s1"]["b"] = 711; + var["s3"]["s1"]["b"] = 711u; var["s2"]["a"] = bool3(false, true, false); var["s2"]["s1"]["a"] = float2(0.55f, 8.31f); - var["s2"]["s1"]["b"] = 431; + var["s2"]["s1"]["b"] = 431u; var["s2"]["b"] = 1.65f; var["s2"]["c"] = uint2(7, 3); diff --git a/Source/Tools/FalcorTest/Tests/Slang/ShaderModel.cpp b/Source/Tools/FalcorTest/Tests/Slang/ShaderModel.cpp index 889d85d88..8dc3b0200 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/ShaderModel.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/ShaderModel.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -33,9 +33,9 @@ namespace { const uint32_t kNumElems = 256; -void test(GPUUnitTestContext& ctx, const std::string& shaderModel) +void test(GPUUnitTestContext& ctx, ShaderModel shaderModel) { - ctx.createProgram("Tests/Slang/ShaderModel.cs.slang", "main", DefineList(), Program::CompilerFlags::None, shaderModel); + ctx.createProgram("Tests/Slang/ShaderModel.cs.slang", "main", DefineList(), SlangCompilerFlags::None, shaderModel); ctx.allocateStructuredBuffer("result", kNumElems); ctx.runProgram(kNumElems, 1, 1); @@ -49,38 +49,38 @@ void test(GPUUnitTestContext& ctx, const std::string& shaderModel) GPU_TEST(ShaderModel6_0) { - test(ctx, "6_0"); + test(ctx, ShaderModel::SM6_0); } GPU_TEST(ShaderModel6_1) { - test(ctx, "6_1"); + test(ctx, ShaderModel::SM6_1); } GPU_TEST(ShaderModel6_2) { - test(ctx, "6_2"); + test(ctx, ShaderModel::SM6_2); } GPU_TEST(ShaderModel6_3) { - test(ctx, "6_3"); + test(ctx, ShaderModel::SM6_3); } GPU_TEST(ShaderModel6_4) { - test(ctx, "6_4"); + test(ctx, ShaderModel::SM6_4); } GPU_TEST(ShaderModel6_5) { - test(ctx, "6_5"); + test(ctx, ShaderModel::SM6_5); } #if FALCOR_HAS_D3D12_AGILITY_SDK GPU_TEST(ShaderModel6_6, Device::Type::D3D12) { - test(ctx, "6_6"); + test(ctx, ShaderModel::SM6_6); } #endif } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/ShaderString.cpp b/Source/Tools/FalcorTest/Tests/Slang/ShaderString.cpp index 8c0ea95a9..063e075ed 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/ShaderString.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/ShaderString.cpp @@ -72,9 +72,9 @@ GPU_TEST(ShaderStringInline) // Create program with generated code placed inline in the same translation // unit as the entry point. - Program::Desc desc; - desc.addShaderLibrary("Tests/Slang/ShaderStringInline.cs.slang").csEntry("main"); - desc.addShaderString(kShaderModuleA, "ModuleA", "", false); + ProgramDesc desc; + desc.addShaderModule().addFile("Tests/Slang/ShaderStringInline.cs.slang").addString(kShaderModuleA); + desc.csEntry("main"); ctx.createProgram(desc, DefineList()); ctx.allocateStructuredBuffer("result", kSize); @@ -85,12 +85,11 @@ GPU_TEST(ShaderStringInline) for (auto& v : values) v = r(); - auto buf = Buffer::create( - pDevice, values.size() * sizeof(uint32_t), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, values.data() - ); + auto buf = + pDevice->createBuffer(values.size() * sizeof(uint32_t), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, values.data()); auto var = ctx.vars().getRootVar(); var["gTest"]["moduleA"]["buf"] = buf; - var["gTest"]["moduleA"]["c"] = 991; + var["gTest"]["moduleA"]["c"] = 991u; // Run program and validate results. ctx.runProgram(kSize, 1, 1); @@ -106,9 +105,10 @@ GPU_TEST(ShaderStringModule) { // Create program with generated code placed in another translation unit. // The generated code is imported as a module using a relative path. - Program::Desc desc; - desc.addShaderString(kShaderModuleD, "GeneratedModule", "Tests/Slang/GeneratedModule.slang", true); - desc.addShaderLibrary("Tests/Slang/ShaderStringModule.cs.slang").csEntry("main"); + ProgramDesc desc; + desc.addShaderModule("GeneratedModule").addString(kShaderModuleD, "Tests/Slang/GeneratedModule.slang"); + desc.addShaderModule().addFile("Tests/Slang/ShaderStringModule.cs.slang"); + desc.csEntry("main"); ctx.createProgram(desc, DefineList()); ctx.allocateStructuredBuffer("result", kSize); @@ -127,9 +127,9 @@ GPU_TEST(ShaderStringImport) { // Create program with generated code placed inline in the same translation // unit as the entry point. The generated code imports another module using an absolute path. - Program::Desc desc; - desc.addShaderLibrary("Tests/Slang/ShaderStringImport.cs.slang").csEntry("main"); - desc.addShaderString(kShaderModuleC, "ModuleC", "", false); + ProgramDesc desc; + desc.addShaderModule().addFile("Tests/Slang/ShaderStringImport.cs.slang").addString(kShaderModuleC); + desc.csEntry("main"); ctx.createProgram(desc, DefineList()); ctx.allocateStructuredBuffer("result", kSize); @@ -149,9 +149,9 @@ GPU_TEST(ShaderStringImportDuplicate, "Duplicate import not working") // Create program with generated code placed inline in the same translation // unit as the entry point. The generated code imports another module using an absolute path. // The main translation unit imports the same module. This currently does not work. - Program::Desc desc; - desc.addShaderLibrary("Tests/Slang/ShaderStringImport.cs.slang").csEntry("main"); - desc.addShaderString(kShaderModuleC, "ModuleC", "", false); + ProgramDesc desc; + desc.addShaderModule().addFile("Tests/Slang/ShaderStringImport.cs.slang").addString(kShaderModuleC); + desc.csEntry("main"); ctx.createProgram(desc, {{"IMPORT_FROM_MAIN", "1"}}); ctx.allocateStructuredBuffer("result", kSize); @@ -170,9 +170,10 @@ GPU_TEST(ShaderStringImported) { // Create program with generated code placed in a new translation unit. // The program imports a module that imports the generated module. - Program::Desc desc; - desc.addShaderString(kShaderModuleD, "GeneratedModule", "Tests/Slang/GeneratedModule.slang", true); - desc.addShaderLibrary("Tests/Slang/ShaderStringImported.cs.slang").csEntry("main"); + ProgramDesc desc; + desc.addShaderModule("GeneratedModule").addString(kShaderModuleD, "Tests/Slang/GeneratedModule.slang"); + desc.addShaderModule().addFile("Tests/Slang/ShaderStringImported.cs.slang"); + desc.csEntry("main"); ctx.createProgram(desc, DefineList()); ctx.allocateStructuredBuffer("result", kSize); @@ -194,11 +195,12 @@ GPU_TEST(ShaderStringDynamicObject) // Create program with generated code placed in a new translation unit. // The program imports a module that imports the generated module. // The generated code is called from a dynamically created object. - Program::Desc desc; - desc.addShaderString(kShaderModuleD, "GeneratedModule", "Tests/Slang/GeneratedModule.slang", true); - desc.addShaderLibrary("Tests/Slang/ShaderStringDynamic.cs.slang").csEntry("main"); + ProgramDesc desc; + desc.addShaderModule("GeneratedModule").addString(kShaderModuleD); + desc.addShaderModule().addFile("Tests/Slang/ShaderStringDynamic.cs.slang"); + desc.csEntry("main"); - Program::TypeConformanceList typeConformances = Program::TypeConformanceList{{{"DynamicType", "IDynamicType"}, typeID}}; + TypeConformanceList typeConformances = TypeConformanceList{{{"DynamicType", "IDynamicType"}, typeID}}; desc.addTypeConformances(typeConformances); ctx.createProgram(desc, DefineList()); diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangExtension.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangExtension.cpp index 0d033a0b4..446a01009 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangExtension.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangExtension.cpp @@ -33,7 +33,7 @@ GPU_TEST(Slang_Extension) { ref pDevice = ctx.getDevice(); - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary("Tests/Slang/SlangExtension.cs.slang").csEntry("main"); ctx.createProgram(desc, DefineList()); ctx.allocateStructuredBuffer("result", 6); diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangExtension.cs.slang b/Source/Tools/FalcorTest/Tests/Slang/SlangExtension.cs.slang index dd2aeb445..e540dbfed 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangExtension.cs.slang +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangExtension.cs.slang @@ -25,7 +25,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ - RWStructuredBuffer result; struct Test1 diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangInheritance.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangInheritance.cpp index 70baad254..d2b728936 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangInheritance.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangInheritance.cpp @@ -32,7 +32,7 @@ namespace Falcor { GPU_TEST(SlangStructInheritanceReflection, "Not working yet") { - ctx.createProgram("Tests/Slang/SlangInheritance.cs.slang", "main", DefineList(), Program::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/SlangInheritance.cs.slang", "main", DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_5); // Reflection of struct A. auto typeA = ctx.getProgram()->getReflector()->findType("A"); @@ -80,13 +80,13 @@ GPU_TEST(SlangStructInheritanceLayout) { ref pDevice = ctx.getDevice(); - ctx.createProgram("Tests/Slang/SlangInheritance.cs.slang", "main", DefineList(), Program::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/SlangInheritance.cs.slang", "main", DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_5); ShaderVar var = ctx.vars().getRootVar(); // TODO: Use built-in buffer when reflection of struct inheritance works (see #1306). // ctx.allocateStructuredBuffer("result", 1); - auto pResult = Buffer::createStructured( - pDevice, 16, 1, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, nullptr, false + auto pResult = pDevice->createStructuredBuffer( + 16, 1, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false ); var["result"] = pResult; @@ -96,8 +96,8 @@ GPU_TEST(SlangStructInheritanceLayout) initData[2] = asuint(5.11f); initData[3] = asuint(7.99f); - var["data"] = Buffer::createTyped( - pDevice, (uint32_t)initData.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, initData.data() + var["data"] = pDevice->createTypedBuffer( + (uint32_t)initData.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, initData.data() ); ctx.runProgram(); @@ -110,8 +110,7 @@ GPU_TEST(SlangStructInheritanceLayout) EXPECT_EQ(offsetof(B, scalar), 0); EXPECT_EQ(offsetof(B, vector), 4); - // std::vector result = ctx.readBuffer("result"); - const uint32_t* result = (const uint32_t*)pResult->map(Buffer::MapType::Read); + std::vector result = pResult->getElements(); // Check struct fields read back from the GPU. // Slang uses the same struct layout as the host. diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangMutatingTests.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangMutatingTests.cpp index 3436140f7..ea50e1104 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangMutatingTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangMutatingTests.cpp @@ -33,12 +33,12 @@ GPU_TEST(SlangMutating) { ref pDevice = ctx.getDevice(); - ctx.createProgram("Tests/Slang/SlangMutatingTests.cs.slang", "main", DefineList(), Program::CompilerFlags::None, "6_3"); + ctx.createProgram("Tests/Slang/SlangMutatingTests.cs.slang", "main", DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_3); ctx.allocateStructuredBuffer("result", 1); ShaderVar var = ctx.vars().getRootVar(); uint4 v = {11, 22, 33, 44}; - var["buffer"] = Buffer::createTyped(pDevice, 1, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, &v); + var["buffer"] = pDevice->createTypedBuffer(1, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, &v); ctx.runProgram(); diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangReinterpretCast.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangReinterpretCast.cpp index 42e5d5ce3..b5fced152 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangReinterpretCast.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangReinterpretCast.cpp @@ -48,7 +48,7 @@ GPU_TEST(SlangReinterpretCast) { ref pDevice = ctx.getDevice(); - ctx.createProgram("Tests/Slang/SlangReinterpretCast.cs.slang", "main", DefineList(), Program::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/SlangReinterpretCast.cs.slang", "main", DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_5); ctx.allocateStructuredBuffer("resultA", kElems); ctx.allocateStructuredBuffer("resultB", kElems); ctx.allocateStructuredBuffer("resultC", kElems); @@ -76,7 +76,7 @@ GPU_TEST(SlangReinterpretCast) ShaderVar var = ctx.vars().getRootVar(); var["data"] = - Buffer::createStructured(pDevice, sizeof(A), kElems, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, data.data()); + pDevice->createStructuredBuffer(sizeof(A), kElems, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, data.data()); ctx.runProgram(kElems); diff --git a/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cpp b/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cpp index f289d040d..f7dccd583 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/SlangTests.cpp @@ -39,9 +39,9 @@ namespace Falcor { namespace { -void testEnum(GPUUnitTestContext& ctx, const std::string& shaderModel) +void testEnum(GPUUnitTestContext& ctx, ShaderModel shaderModel) { - ctx.createProgram("Tests/Slang/SlangTests.cs.slang", "testEnum", DefineList(), Program::CompilerFlags::None, shaderModel); + ctx.createProgram("Tests/Slang/SlangTests.cs.slang", "testEnum", DefineList(), SlangCompilerFlags::None, shaderModel); ctx.allocateStructuredBuffer("result", 12); ctx.runProgram(1, 1, 1); @@ -78,9 +78,9 @@ uint64_t asuint64(double a) */ GPU_TEST(SlangEnum) { - testEnum(ctx, ""); // Use default shader model for the unit test system - testEnum(ctx, "6_0"); - testEnum(ctx, "6_3"); + testEnum(ctx, ShaderModel::Unknown); // Use default shader model for the unit test system + testEnum(ctx, ShaderModel::SM6_0); + testEnum(ctx, ShaderModel::SM6_3); } /** Test fixed-width scalar type support including 16-bit types (shader model 6.2+). @@ -93,7 +93,7 @@ GPU_TEST(SlangScalarTypes) { const uint32_t maxTests = 100; - ctx.createProgram("Tests/Slang/SlangTests.cs.slang", "testScalarTypes", DefineList(), Program::CompilerFlags::None, "6_2"); + ctx.createProgram("Tests/Slang/SlangTests.cs.slang", "testScalarTypes", DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_2); ctx.allocateStructuredBuffer("result", maxTests); ctx.runProgram(1, 1, 1); @@ -163,10 +163,10 @@ GPU_TEST(SlangDefaultInitializers) const uint32_t maxTests = 100, usedTests = 43; std::vector initData(maxTests, -1); - auto test = [&](const std::string& shaderModel) + auto test = [&](ShaderModel shaderModel) { ctx.createProgram( - "Tests/Slang/SlangTests.cs.slang", "testDefaultInitializers", DefineList(), Program::CompilerFlags::None, shaderModel + "Tests/Slang/SlangTests.cs.slang", "testDefaultInitializers", DefineList(), SlangCompilerFlags::None, shaderModel ); ctx.allocateStructuredBuffer("result", maxTests, initData.data(), initData.size() * sizeof(initData[0])); ctx.runProgram(1, 1, 1); @@ -179,20 +179,20 @@ GPU_TEST(SlangDefaultInitializers) if (i == 42) expected = (uint32_t)Type3::C; - EXPECT_EQ(result[i], expected) << "i = " << i << " (sm" << shaderModel << ")"; + EXPECT_EQ(result[i], expected) << "i = " << i << " (" << enumToString(shaderModel) << ")"; } }; // Test the default shader model, followed by specific models. - test(""); - test("6_0"); - test("6_1"); - test("6_2"); - test("6_3"); - test("6_5"); + test(ShaderModel::Unknown); + test(ShaderModel::SM6_0); + test(ShaderModel::SM6_1); + test(ShaderModel::SM6_2); + test(ShaderModel::SM6_3); + test(ShaderModel::SM6_5); #if FALCOR_HAS_D3D12_AGILITY_SDK if (ctx.getDevice()->getType() == Device::Type::D3D12) - test("6_6"); + test(ShaderModel::SM6_6); #endif } diff --git a/Source/Tools/FalcorTest/Tests/Slang/StructuredBufferMatrix.cpp b/Source/Tools/FalcorTest/Tests/Slang/StructuredBufferMatrix.cpp index c77f4bfe5..4cfe4b228 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/StructuredBufferMatrix.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/StructuredBufferMatrix.cpp @@ -36,15 +36,12 @@ void runTest2(GPUUnitTestContext& ctx, DefineList defines) { ref pDevice = ctx.getDevice(); - ctx.createProgram( - "Tests/Slang/StructuredBufferMatrix.cs.slang", "testStructuredBufferMatrixLoad2", defines, - Program::CompilerFlags::DumpIntermediates, "6_5" - ); + ctx.createProgram("Tests/Slang/StructuredBufferMatrix.cs.slang", "testStructuredBufferMatrixLoad2", defines); ctx.allocateStructuredBuffer("result", 16); auto var = ctx.vars().getRootVar(); auto pData = - Buffer::createStructured(pDevice, var["data2"], 1, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + pDevice->createStructuredBuffer(var["data2"], 1, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); EXPECT_EQ(pData->getElementCount(), 1); EXPECT_EQ(pData->getElementSize(), 32); @@ -71,14 +68,12 @@ GPU_TEST(StructuredBufferMatrixLoad1) { ref pDevice = ctx.getDevice(); - ctx.createProgram( - "Tests/Slang/StructuredBufferMatrix.cs.slang", "testStructuredBufferMatrixLoad1", DefineList(), Program::CompilerFlags::None, "6_5" - ); + ctx.createProgram("Tests/Slang/StructuredBufferMatrix.cs.slang", "testStructuredBufferMatrixLoad1"); ctx.allocateStructuredBuffer("result", 32); auto var = ctx.vars().getRootVar(); auto pData = - Buffer::createStructured(pDevice, var["data1"], 1, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullptr, false); + pDevice->createStructuredBuffer(var["data1"], 1, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false); EXPECT_EQ(pData->getElementCount(), 1); EXPECT_EQ(pData->getElementSize(), 100); diff --git a/Source/Tools/FalcorTest/Tests/Slang/TemplatedLoad.cpp b/Source/Tools/FalcorTest/Tests/Slang/TemplatedLoad.cpp index 81557b015..69009c8e0 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/TemplatedLoad.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/TemplatedLoad.cpp @@ -50,12 +50,12 @@ void test(GPUUnitTestContext& ctx, const std::string& entryPoint, const size_t n std::vector elems = generateData(n); - ctx.createProgram("Tests/Slang/TemplatedLoad.cs.slang", entryPoint, DefineList(), Program::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/TemplatedLoad.cs.slang", entryPoint, DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_5); ctx.allocateStructuredBuffer("result", (uint32_t)elems.size()); auto var = ctx.vars().getRootVar(); var["data"] = - Buffer::create(pDevice, elems.size() * sizeof(elems[0]), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, elems.data()); + pDevice->createBuffer(elems.size() * sizeof(elems[0]), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, elems.data()); ctx.runProgram(1, 1, 1); diff --git a/Source/Tools/FalcorTest/Tests/Slang/TraceRayFlags.cpp b/Source/Tools/FalcorTest/Tests/Slang/TraceRayFlags.cpp index 0b640e826..7d959e359 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/TraceRayFlags.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/TraceRayFlags.cpp @@ -46,17 +46,17 @@ void testRayFlags(GPUUnitTestContext& ctx, bool useDXR_1_1) }; DefineList defines; - std::string shaderModel = "6_3"; + ShaderModel shaderModel = ShaderModel::SM6_3; if (useDXR_1_1) { expected.push_back((uint32_t)RayFlags::SkipTriangles); expected.push_back((uint32_t)RayFlags::SkipProceduralPrimitives); defines.add("DXR_1_1"); - shaderModel = "6_5"; + shaderModel = ShaderModel::SM6_5; } - ctx.createProgram("Tests/Slang/TraceRayFlags.cs.slang", "testRayFlags", defines, Program::CompilerFlags::None, shaderModel); + ctx.createProgram("Tests/Slang/TraceRayFlags.cs.slang", "testRayFlags", defines, SlangCompilerFlags::None, shaderModel); ctx.allocateStructuredBuffer("result", (uint32_t)expected.size()); ctx.runProgram(1, 1, 1); diff --git a/Source/Tools/FalcorTest/Tests/Slang/TraceRayInline.cpp b/Source/Tools/FalcorTest/Tests/Slang/TraceRayInline.cpp index fea680166..d6030784e 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/TraceRayInline.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/TraceRayInline.cpp @@ -32,6 +32,8 @@ namespace Falcor GPU_TEST(testTraceRayInlineAPI) { // We don't actually run the program, just make sure it compiles. - ctx.createProgram("Tests/Slang/TraceRayInline.cs.slang", "testTraceRayInlineAPI", DefineList(), Program::CompilerFlags::None, "6_5"); + ctx.createProgram( + "Tests/Slang/TraceRayInline.cs.slang", "testTraceRayInlineAPI", DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_5 + ); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Slang/TraceRayInline.cs.slang b/Source/Tools/FalcorTest/Tests/Slang/TraceRayInline.cs.slang index 30e4c29e7..556328055 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/TraceRayInline.cs.slang +++ b/Source/Tools/FalcorTest/Tests/Slang/TraceRayInline.cs.slang @@ -56,7 +56,8 @@ void testTraceRayInlineAPI(uint3 threadID: SV_DispatchThreadID) rayQuery.TraceRayInline( gAccelerationStructure, rayFlags, // OR'd with flags above - instanceMask, ray + instanceMask, + ray ); // Proceed() below is where behind-the-scenes traversal happens, diff --git a/Source/Tools/FalcorTest/Tests/Slang/UnboundedDescriptorArray.cpp b/Source/Tools/FalcorTest/Tests/Slang/UnboundedDescriptorArray.cpp index b0b970bfd..840961629 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/UnboundedDescriptorArray.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/UnboundedDescriptorArray.cpp @@ -35,14 +35,14 @@ GPU_TEST(UnboundedDescriptorArray, "Unbounded arrays are not yet supported") const uint32_t kTexCount = 4; - ctx.createProgram("Tests/Slang/UnboundedDescriptorArray.cs.slang", "main", DefineList(), Program::CompilerFlags::None, "6_5"); + ctx.createProgram("Tests/Slang/UnboundedDescriptorArray.cs.slang", "main", DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_5); ctx.allocateStructuredBuffer("result", kTexCount); auto var = ctx.vars().getRootVar()["resources"]; for (size_t i = 0; i < kTexCount; i++) { float initData = (float)(i + 1); - var["textures"][i] = Texture::create2D(pDevice, 1, 1, ResourceFormat::R32Float, 1, 1, &initData); + var["textures"][i] = pDevice->createTexture2D(1, 1, ResourceFormat::R32Float, 1, 1, &initData); } ctx.runProgram(kTexCount, 1, 1); diff --git a/Source/Tools/FalcorTest/Tests/Slang/WaveOps.cpp b/Source/Tools/FalcorTest/Tests/Slang/WaveOps.cpp index 0ef6dd08c..c590dbeec 100644 --- a/Source/Tools/FalcorTest/Tests/Slang/WaveOps.cpp +++ b/Source/Tools/FalcorTest/Tests/Slang/WaveOps.cpp @@ -115,12 +115,12 @@ void testWaveMinMax(GPUUnitTestContext& ctx, bool conditional) ref pDevice = ctx.getDevice(); DefineList defines = {{"CONDITIONAL", conditional ? "1" : "0"}}; - ctx.createProgram(kShaderFilename, "testWaveMinMax", defines, Program::CompilerFlags::None, "6_0"); + ctx.createProgram(kShaderFilename, "testWaveMinMax", defines, SlangCompilerFlags::None, ShaderModel::SM6_0); ctx.allocateStructuredBuffer("result", kNumElems * 2); auto var = ctx.vars().getRootVar(); uint32_t zero = 0; - auto pLaneCount = Buffer::createTyped(pDevice, 1, ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, &zero); + auto pLaneCount = pDevice->createTypedBuffer(1, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, &zero); var["laneCount"] = pLaneCount; std::uniform_real_distribution u(0.f, 1.f); @@ -131,17 +131,16 @@ void testWaveMinMax(GPUUnitTestContext& ctx, bool conditional) for (size_t j = 0; j < 32; j++) testData[i + j] = offset + 2.f * u(rng) - 1.f; } - var["testData"] = Buffer::createTyped( - pDevice, kNumElems, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, (uint32_t*)testData.data() + var["testData"] = pDevice->createTypedBuffer( + kNumElems, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, (uint32_t*)testData.data() ); ctx.runProgram(kNumElems, 1, 1); // Get the lane count. We abort the test if it is an unsupported count. - const uint32_t laneCount = *(const uint32_t*)pLaneCount->map(Buffer::MapType::Read); - pLaneCount->unmap(); + uint32_t laneCount = pLaneCount->getElement(0); if (laneCount < 4 || laneCount > 128) - throw RuntimeError("Unsupported wave lane count"); + FALCOR_THROW("Unsupported wave lane count"); // Verify results of wave min/max. std::vector expectedResult = computeMinMaxResult(testData, laneCount, conditional); @@ -159,19 +158,16 @@ uint32_t queryLaneCount(GPUUnitTestContext& ctx) { ref pDevice = ctx.getDevice(); - ctx.createProgram(kShaderFilename, "testWaveGetLaneCount", DefineList(), Program::CompilerFlags::None, "6_0"); + ctx.createProgram(kShaderFilename, "testWaveGetLaneCount", DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_0); auto var = ctx.vars().getRootVar(); uint32_t zero = 0; - auto pLaneCount = Buffer::createTyped(pDevice, 1, ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, &zero); + auto pLaneCount = pDevice->createTypedBuffer(1, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, &zero); var["laneCount"] = pLaneCount; ctx.runProgram(1, 1, 1); - const uint32_t laneCount = *(const uint32_t*)pLaneCount->map(Buffer::MapType::Read); - pLaneCount->unmap(); - - return laneCount; + return pLaneCount->getElement(0); } } // namespace @@ -187,26 +183,25 @@ GPU_TEST(WaveMatch, Device::Type::D3D12) { ref pDevice = ctx.getDevice(); - ctx.createProgram(kShaderFilename, "testWaveMatch", DefineList(), Program::CompilerFlags::None, "6_5"); + ctx.createProgram(kShaderFilename, "testWaveMatch", DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_5); ctx.allocateStructuredBuffer("result", kNumElems); auto var = ctx.vars().getRootVar(); uint32_t zero = 0; - auto pLaneCount = Buffer::createTyped(pDevice, 1, ResourceBindFlags::UnorderedAccess, Buffer::CpuAccess::None, &zero); + auto pLaneCount = pDevice->createTypedBuffer(1, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, &zero); var["laneCount"] = pLaneCount; std::vector matchData = generateMatchData(kNumElems); FALCOR_ASSERT(matchData.size() == kNumElems); var["testData"] = - Buffer::createTyped(pDevice, kNumElems, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, matchData.data()); + pDevice->createTypedBuffer(kNumElems, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, matchData.data()); ctx.runProgram(kNumElems, 1, 1); // Get the lane count. We abort the test if it is an unsupported count. - const uint32_t laneCount = *(const uint32_t*)pLaneCount->map(Buffer::MapType::Read); - pLaneCount->unmap(); + const uint32_t laneCount = pLaneCount->getElement(0); if (laneCount < 4 || laneCount > 128) - throw RuntimeError("Unsupported wave lane count"); + FALCOR_THROW("Unsupported wave lane count"); // Verify results of wave match. std::vector expectedResult = computeMatchResult(matchData, laneCount); @@ -249,12 +244,12 @@ GPU_TEST(WaveMaxSimpleFloat, "Disabled due to compiler issues") for (size_t i = 0; i < testData.size(); i++) testData[i] = (float)i - 15; - ctx.createProgram(kShaderFilename, "testWaveMaxSimpleFloat", DefineList(), Program::CompilerFlags::None, "6_0"); + ctx.createProgram(kShaderFilename, "testWaveMaxSimpleFloat", DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_0); ctx.allocateStructuredBuffer("result", 32); auto var = ctx.vars().getRootVar(); var["testData"] = - Buffer::createTyped(pDevice, 32, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, (uint32_t*)testData.data()); + pDevice->createTypedBuffer(32, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, (uint32_t*)testData.data()); ctx.runProgram(32, 1, 1); // Verify result. @@ -283,12 +278,12 @@ GPU_TEST(WaveMaxSimpleInt) for (size_t i = 0; i < testData.size(); i++) testData[i] = (int)i - 15; - ctx.createProgram(kShaderFilename, "testWaveMaxSimpleInt", DefineList(), Program::CompilerFlags::None, "6_0"); + ctx.createProgram(kShaderFilename, "testWaveMaxSimpleInt", DefineList(), SlangCompilerFlags::None, ShaderModel::SM6_0); ctx.allocateStructuredBuffer("result", 32); auto var = ctx.vars().getRootVar(); var["testData"] = - Buffer::createTyped(pDevice, 32, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, (uint32_t*)testData.data()); + pDevice->createTypedBuffer(32, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, (uint32_t*)testData.data()); ctx.runProgram(32, 1, 1); // Verify result. diff --git a/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cpp index 90fc94c36..dc070fc76 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/AABBTests.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp index 2f286a7a5..3596aa3b7 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/BitTricksTests.cpp @@ -64,7 +64,7 @@ GPU_TEST(BitInterleave) it = r(); ref pTestDataBuffer = - Buffer::create(pDevice, n * sizeof(uint32_t), Resource::BindFlags::ShaderResource, Buffer::CpuAccess::None, testData.data()); + pDevice->createBuffer(n * sizeof(uint32_t), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, testData.data()); // Setup and run GPU test. ctx.createProgram("Tests/Utils/BitTricksTests.cs.slang", "testBitInterleave"); diff --git a/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp index e1d7a5e07..3ecb9b697 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/BitonicSortTests.cpp @@ -56,7 +56,7 @@ void testGpuSort(GPUUnitTestContext& ctx, BitonicSort& bitonicSort, const uint32 it = r(); ref pTestDataBuffer = - Buffer::create(pDevice, n * sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, testData.data()); + pDevice->createBuffer(n * sizeof(uint32_t), ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, testData.data()); // Execute sort on the GPU. uint32_t groupSize = std::max(chunkSize, 256u); @@ -67,13 +67,11 @@ void testGpuSort(GPUUnitTestContext& ctx, BitonicSort& bitonicSort, const uint32 bitonicSortRef(testData, chunkSize); // Compare results. - const uint32_t* result = (const uint32_t*)pTestDataBuffer->map(Buffer::MapType::Read); - FALCOR_ASSERT(result); + std::vector result = pTestDataBuffer->getElements(0); for (uint32_t i = 0; i < n; i++) { EXPECT_EQ(testData[i], result[i]) << "i = " << i; } - pTestDataBuffer->unmap(); } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Utils/BufferAllocatorTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/BufferAllocatorTests.cpp index 2fd00a763..c83c7d067 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/BufferAllocatorTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/BufferAllocatorTests.cpp @@ -118,13 +118,11 @@ GPU_TEST(BufferAllocatorNoAlign) EXPECT_EQ(pBuffer->getSize(), 156); // Size should be padded to the next 4B boundary. const uint8_t* ref = buf.getStartPointer(); - const uint8_t* ptr = reinterpret_cast(pBuffer->map(Buffer::MapType::Read)); + std::vector data = pBuffer->getElements(0, buf.getSize()); for (size_t i = 0; i < buf.getSize(); i++) { - EXPECT_EQ((uintptr_t)ptr[i], (uintptr_t)ref[i]) << "i=" << i; + EXPECT_EQ((uintptr_t)data[i], (uintptr_t)ref[i]) << "i=" << i; } - - pBuffer->unmap(); }; validateGpuBuffer(); @@ -238,12 +236,11 @@ GPU_TEST(BufferAllocatorStructNoAlign) EXPECT_EQ(pBuffer->getStructSize(), 16); EXPECT_EQ(pBuffer->getSize(), 80); // Size should be padded to a whole number of structs. - const float* data = reinterpret_cast(pBuffer->map(Buffer::MapType::Read)); + std::vector data = pBuffer->getElements(0, 17); for (size_t i = 0; i < 17; i++) { EXPECT_EQ(data[i], (float)i + 1); } - pBuffer->unmap(); } } @@ -308,12 +305,11 @@ GPU_TEST(BufferAllocatorStructAlign) EXPECT_EQ(pBuffer->getSize(), 224); // Size should be padded to a whole number of structs. const float* ref = reinterpret_cast(buf.getStartPointer()); - const float* data = reinterpret_cast(pBuffer->map(Buffer::MapType::Read)); + std::vector data = pBuffer->getElements(0, buf.getSize() / 4); for (size_t i = 0; i < buf.getSize() / 4; i++) { EXPECT_EQ(data[i], ref[i]); } - pBuffer->unmap(); } } diff --git a/Source/Tools/FalcorTest/Tests/Utils/Debug/WarpProfilerTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/Debug/WarpProfilerTests.cpp index 5e8ed73b3..6427461b4 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/Debug/WarpProfilerTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/Debug/WarpProfilerTests.cpp @@ -34,13 +34,13 @@ GPU_TEST(WarpProfiler, Device::Type::D3D12) { WarpProfiler profiler(ctx.getDevice(), 4); - Program::Desc desc; + ProgramDesc desc; desc.addShaderLibrary("Tests/Utils/Debug/WarpProfilerTests.cs.slang").csEntry("main"); - desc.setShaderModel("6_5"); // Minimum required shader model. + desc.setShaderModel(ShaderModel::SM6_5); // Minimum required shader model. ctx.createProgram(desc); auto var = ctx.vars().getRootVar(); - profiler.setShaderData(var); + profiler.bindShaderData(var); profiler.begin(ctx.getRenderContext()); ctx.runProgram(256, 256, 16); // Launch 2^20 threads = 32768 warps. diff --git a/Source/Tools/FalcorTest/Tests/Utils/GeometryHelpersTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/GeometryHelpersTests.cpp index c0b4cfab4..d69e35d9c 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/GeometryHelpersTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/GeometryHelpersTests.cpp @@ -82,9 +82,9 @@ void runBBoxTestComputeShader(GPUUnitTestContext& ctx, const BBoxTestCase* testC { ref pDevice = ctx.getDevice(); - ref pOriginBuffer = Buffer::createTyped(pDevice, nTests); - ref pAABBMinBuffer = Buffer::createTyped(pDevice, nTests); - ref pAABBMaxBuffer = Buffer::createTyped(pDevice, nTests); + ref pOriginBuffer = pDevice->createTypedBuffer(nTests); + ref pAABBMinBuffer = pDevice->createTypedBuffer(nTests); + ref pAABBMaxBuffer = pDevice->createTypedBuffer(nTests); for (int i = 0; i < nTests; ++i) { @@ -205,7 +205,7 @@ void testRandomBBoxes(GPUUnitTestContext& ctx, const char* entrypoint) } } // namespace -// This test is currently disabled for Vulkan as the float precision is not exact (Program::CompilerFlags::FloatingPointModePrecise not +// This test is currently disabled for Vulkan as the float precision is not exact (SlangCompilerFlags::FloatingPointModePrecise not // supported?). GPU_TEST(ComputeRayOrigin, Device::Type::D3D12) { @@ -226,7 +226,7 @@ GPU_TEST(ComputeRayOrigin, Device::Type::D3D12) } // Setup and run GPU test. - ctx.createProgram(kShaderFilename, "testComputeRayOrigin", DefineList(), Program::CompilerFlags::FloatingPointModePrecise); + ctx.createProgram(kShaderFilename, "testComputeRayOrigin", DefineList(), SlangCompilerFlags::FloatingPointModePrecise); ctx.allocateStructuredBuffer("result", nTests); ctx.allocateStructuredBuffer("pos", nTests, testPositions.data(), testPositions.size() * sizeof(float3)); ctx.allocateStructuredBuffer("normal", nTests, testNormals.data(), testNormals.size() * sizeof(float3)); @@ -288,7 +288,7 @@ GPU_TEST(SphereSubtendedAngle) }; int nTests = sizeof(testCases) / sizeof(testCases[0]); - ref pTestCaseBuffer = Buffer::createTyped(pDevice, nTests); + ref pTestCaseBuffer = pDevice->createTypedBuffer(nTests); for (int i = 0; i < nTests; ++i) { @@ -459,15 +459,15 @@ GPU_TEST(ComputeClippedTriangleArea2D) pos.push_back(float3(tests[i].p[2], 0.f)); } FALCOR_ASSERT(pos.size() == 3 * tests.size()); - return Buffer::createStructured( - pDevice, sizeof(float3), (uint32_t)pos.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, pos.data(), false + return pDevice->createStructuredBuffer( + sizeof(float3), (uint32_t)pos.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, pos.data(), false ); }; auto createAABBBuffer = [pDevice](const std::vector& aabb) { - return Buffer::createStructured( - pDevice, sizeof(AABB2D), (uint32_t)aabb.size(), ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, aabb.data(), false + return pDevice->createStructuredBuffer( + sizeof(AABB2D), (uint32_t)aabb.size(), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, aabb.data(), false ); }; diff --git a/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cpp index fcd008249..021fd51e7 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/HalfUtilsTests.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -322,7 +322,11 @@ GPU_TEST(FP16RoundingModeGPU, "Disabled due to lacking fp16 library (#391)") // The computation of the quantized value using 'y = f16tof32(f32tof16(x))' gets optimized to 'y = x' in the shader, despite the global // precise flag. ctx.createProgram( - "Tests/Utils/HalfUtilsTests.cs.slang", "testFP16RoundingMode", DefineList(), Program::CompilerFlags::FloatingPointModePrecise, "6_2" + "Tests/Utils/HalfUtilsTests.cs.slang", + "testFP16RoundingMode", + DefineList(), + SlangCompilerFlags::FloatingPointModePrecise, + ShaderModel::SM6_2 ); ctx.allocateStructuredBuffer("inputFloat", (uint32_t)input.size(), input.data(), input.size() * sizeof(decltype(input)::value_type)); ctx.allocateStructuredBuffer("resultFloat", (uint32_t)expected.size()); diff --git a/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cpp index 8de681259..388800f6d 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/HashUtilsTests.cpp @@ -56,7 +56,7 @@ GPU_TEST(JenkinsHash_CompareToCPU) ref pDevice = ctx.getDevice(); // Allocate results buffer (64k dwords). - ref pResultBuffer = Buffer::createTyped(pDevice, 1 << 16, ResourceBindFlags::UnorderedAccess); + ref pResultBuffer = pDevice->createTypedBuffer(1 << 16, ResourceBindFlags::UnorderedAccess); ctx.getRenderContext()->clearUAV(pResultBuffer->getUAV().get(), uint4(0)); // Setup and run GPU test. @@ -65,13 +65,11 @@ GPU_TEST(JenkinsHash_CompareToCPU) ctx.runProgram(1 << 16, 1, 1); // Verify that the generated hashes match the CPU version. - const uint32_t* result = (const uint32_t*)pResultBuffer->map(Buffer::MapType::Read); - FALCOR_ASSERT(result); + std::vector result = pResultBuffer->getElements(); for (uint32_t i = 0; i < pResultBuffer->getElementCount(); i++) { EXPECT_EQ(result[i], jenkinsHash(i)) << "i = " << i; } - pResultBuffer->unmap(); } #ifdef RUN_PERFECT_HASH_TESTS @@ -101,7 +99,7 @@ GPU_TEST(JenkinsHash_PerfectHashGPU, "Disabled for performance reasons") ref pDevice = ctx.getDevice(); // Allocate results buffer (2^27 dwords). - ref pResultBuffer = Buffer::createTyped(pDevice, 1 << 27, ResourceBindFlags::UnorderedAccess); + ref pResultBuffer = pDevice->createTypedBuffer(1 << 27, ResourceBindFlags::UnorderedAccess); ctx.getRenderContext()->clearUAV(pResultBuffer->getUAV().get(), uint4(0)); // Setup and run GPU test. @@ -110,12 +108,10 @@ GPU_TEST(JenkinsHash_PerfectHashGPU, "Disabled for performance reasons") ctx.runProgram(1 << 16, 1 << 16, 1); // Verify that all possible 32-bit hashes has occured (all bits set). - const uint32_t* result = (const uint32_t*)pResultBuffer->map(Buffer::MapType::Read); - FALCOR_ASSERT(result); - for (uint32_t i = 0; i < pResultBuffer->getElementCount(); i++) + std::vector result = pResultBuffer->getElements(); + for (uint32_t i = 0; i < result.size(); i++) { EXPECT_EQ(result[i], 0xffffffff) << "i = " << i; } - pResultBuffer->unmap(); } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Utils/ImageProcessing.cpp b/Source/Tools/FalcorTest/Tests/Utils/ImageProcessing.cpp index efda80987..29c2d587e 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/ImageProcessing.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/ImageProcessing.cpp @@ -61,9 +61,9 @@ void testCopyColorChannel( // Create test textures. auto data = generateTestData(width * height * srcChannels); - auto pSrc = Texture::create2D(pDevice, width, height, srcFormat, 1, 1, data.data(), ResourceBindFlags::ShaderResource); - auto pDst = Texture::create2D( - pDevice, width, height, dstFormat, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess + auto pSrc = pDevice->createTexture2D(width, height, srcFormat, 1, 1, data.data(), ResourceBindFlags::ShaderResource); + auto pDst = pDevice->createTexture2D( + width, height, dstFormat, 1, 1, nullptr, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess ); // Test copying from color channel i=0..3. diff --git a/Source/Tools/FalcorTest/Tests/Utils/MatrixTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/MatrixTests.cpp index 83cfb75da..1aeedd351 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/MatrixTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/MatrixTests.cpp @@ -179,10 +179,12 @@ CPU_TEST(Matrix_transpose) CPU_TEST(Matrix_translate) { float4x4 m({ - 1, 0, 0, 10, // - 0, -1, 0, 20, // - 0, 0, 1, 30, // - 0, 0, 0, 1 // + // clang-format off + 1, 0, 0, 10, + 0, -1, 0, 20, + 0, 0, 1, 30, + 0, 0, 0, 1 + // clang-format on }); float4x4 m2 = translate(m, float3(1, 2, 3)); EXPECT_ALMOST_EQ(m2[0], float4(1, 0, 0, 11)); @@ -194,10 +196,12 @@ CPU_TEST(Matrix_translate) CPU_TEST(Matrix_rotate) { float4x4 m({ - 1, 0, 0, 10, // - 0, -1, 0, 20, // - 0, 0, 1, 30, // - 0, 0, 0, 1 // + // clang-format off + 1, 0, 0, 10, + 0, -1, 0, 20, + 0, 0, 1, 30, + 0, 0, 0, 1 + // clang-format on }); float4x4 m2 = rotate(m, math::radians(90.f), float3(0, 1, 0)); EXPECT_ALMOST_EQ(m2[0], float4(0, 0, 1, 10)); @@ -209,10 +213,12 @@ CPU_TEST(Matrix_rotate) CPU_TEST(Matrix_scale) { float4x4 m({ - 1, 0, 0, 10, // - 0, -1, 0, 20, // - 0, 0, 1, 30, // - 0, 0, 0, 1 // + // clang-format off + 1, 0, 0, 10, + 0, -1, 0, 20, + 0, 0, 1, 30, + 0, 0, 0, 1 + // clang-format on }); float4x4 m2 = scale(m, float3(2, 3, 4)); EXPECT_ALMOST_EQ(m2[0], float4(2, 0, 0, 10)); @@ -280,10 +286,12 @@ CPU_TEST(Matrix_extractEulerAngleXYZ) { // float4x4 m = math::matrixFromRotationXYZ(math::radians(45.f), math::radians(45.f), math::radians(45.f)); float4x4 m = float4x4({ - 0.5f, -0.5f, 0.707107f, 0.f, // - 0.853553f, 0.146446f, -0.5f, 0.f, // - 0.146446f, 0.853553f, 0.5f, 0.f, // - 0.f, 0.f, 0.f, 1.f // + // clang-format off + 0.5f, -0.5f, 0.707107f, 0.f, + 0.853553f, 0.146446f, -0.5f, 0.f, + 0.146446f, 0.853553f, 0.5f, 0.f, + 0.f, 0.f, 0.f, 1.f + // clang-format on }); float3 angles; math::extractEulerAngleXYZ(m, angles.x, angles.y, angles.z); @@ -293,10 +301,12 @@ CPU_TEST(Matrix_extractEulerAngleXYZ) { // float4x4 m = math::matrixFromRotationXYZ(math::radians(20.f), math::radians(40.f), math::radians(60.f)); float4x4 m = float4x4({ - 0.383022f, -0.663414f, 0.642787f, 0.f, // - 0.923720f, 0.279453f, -0.262002f, 0.f, // - -0.005813f, 0.694109f, 0.719846f, 0.f, // - 0.f, 0.f, 0.f, 1.f // + // clang-format off + 0.383022f, -0.663414f, 0.642787f, 0.f, + 0.923720f, 0.279453f, -0.262002f, 0.f, + -0.005813f, 0.694109f, 0.719846f, 0.f, + 0.f, 0.f, 0.f, 1.f + // clang-format on }); float3 angles; math::extractEulerAngleXYZ(m, angles.x, angles.y, angles.z); @@ -306,8 +316,13 @@ CPU_TEST(Matrix_extractEulerAngleXYZ) CPU_TEST(Matrix_decompose) { - const auto testDecompose = [&](float4x4 m, float3 expectedScale, quatf expectedOrientation, float3 expectedTranslation, - float3 expectedSkew, float4 expectedPerspective, bool expectedResult = true) + const auto testDecompose = [&](float4x4 m, + float3 expectedScale, + quatf expectedOrientation, + float3 expectedTranslation, + float3 expectedSkew, + float4 expectedPerspective, + bool expectedResult = true) { float3 scale; quatf orientation; @@ -350,10 +365,12 @@ CPU_TEST(Matrix_decompose) // Scale only testDecompose( float4x4({ - 2.f, 0.f, 0.f, 0.f, // row 0 - 0.f, 3.f, 0.f, 0.f, // row 1 - 0.f, 0.f, 4.f, 0.f, // row 2 - 0.f, 0.f, 0.f, 1.f // row 3 + // clang-format off + 2.f, 0.f, 0.f, 0.f, + 0.f, 3.f, 0.f, 0.f, + 0.f, 0.f, 4.f, 0.f, + 0.f, 0.f, 0.f, 1.f + // clang-format on }), float3(2.f, 3.f, 4.f), // scale quatf::identity(), // orientation @@ -366,10 +383,12 @@ CPU_TEST(Matrix_decompose) // float4x4 m = math::matrixFromRotationX(math::radians(45.f)); testDecompose( float4x4({ - 1.f, 0.f, 0.f, 0.f, // row 0 - 0.f, 0.707107f, -0.707107f, 0.f, // row 1 - 0.f, 0.707107f, 0.707107f, 0.f, // row 2 - 0.f, 0.f, 0.f, 1.f // row 3 + // clang-format off + 1.f, 0.f, 0.f, 0.f, + 0.f, 0.707107f, -0.707107f, 0.f, + 0.f, 0.707107f, 0.707107f, 0.f, + 0.f, 0.f, 0.f, 1.f + // clang-format on }), float3(1.f, 1.f, 1.f), // scale quatf(0.382683f, 0.f, 0.f, 0.92388f), // orientation @@ -381,10 +400,12 @@ CPU_TEST(Matrix_decompose) // Translation only testDecompose( float4x4({ - 1.f, 0.f, 0.f, 1.f, // row 0 - 0.f, 1.f, 0.f, 2.f, // row 1 - 0.f, 0.f, 1.f, 3.f, // row 2 - 0.f, 0.f, 0.f, 1.f // row 3 + // clang-format off + 1.f, 0.f, 0.f, 1.f, + 0.f, 1.f, 0.f, 2.f, + 0.f, 0.f, 1.f, 3.f, + 0.f, 0.f, 0.f, 1.f + // clang-format off }), float3(1.f, 1.f, 1.f), // scale quatf::identity(), // orientation @@ -396,10 +417,12 @@ CPU_TEST(Matrix_decompose) // Skew only testDecompose( float4x4({ - 1.f, 2.f, 3.f, 0.f, // row 0 - 0.f, 1.f, 4.f, 0.f, // row 1 - 0.f, 0.f, 1.f, 0.f, // row 2 - 0.f, 0.f, 0.f, 1.f // row 3 + // clang-format off + 1.f, 2.f, 3.f, 0.f, + 0.f, 1.f, 4.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, 0.f, 0.f, 1.f + // clang-format on }), float3(1.f, 1.f, 1.f), // scale quatf::identity(), // orientation @@ -411,10 +434,12 @@ CPU_TEST(Matrix_decompose) // Perspective only testDecompose( float4x4({ - 1.f, 0.f, 0.f, 0.f, // row 0 - 0.f, 1.f, 0.f, 0.f, // row 1 - 0.f, 0.f, 1.f, 0.f, // row 2 - 0.1f, 0.2f, 0.3f, 1.f // row 3 + // clang-format off + 1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.1f, 0.2f, 0.3f, 1.f + // clang-format on }), float3(1.f, 1.f, 1.f), // scale quatf::identity(), // orientation @@ -430,10 +455,12 @@ CPU_TEST(Matrix_decompose) m = mul(math::matrixFromTranslation(float3(1.f, 2.f, 3.f)), m); testDecompose( float4x4({ - 2.f, 0.f, 0.f, 1.f, // row 0 - 0.f, 2.12132f, -2.82843f, 2.f, // row 1 - 0.f, 2.12132f, 2.82843f, 3.f, // row 2 - 0.f, 0.f, 0.f, 1.f // row 3 + // clang-format off + 2.f, 0.f, 0.f, 1.f, + 0.f, 2.12132f, -2.82843f, 2.f, + 0.f, 2.12132f, 2.82843f, 3.f, + 0.f, 0.f, 0.f, 1.f + // clang-format on }), float3(2.f, 3.f, 4.f), // scale quatf(0.382683f, 0.f, 0.f, 0.92388f), // orientation diff --git a/Source/Tools/FalcorTest/Tests/Utils/PackedFormatsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/PackedFormatsTests.cpp index 0893de8ba..788150c0b 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/PackedFormatsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/PackedFormatsTests.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions diff --git a/Source/Tools/FalcorTest/Tests/Utils/ParallelReductionTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/ParallelReductionTests.cpp index b6b11f250..f1b33ba61 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/ParallelReductionTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/ParallelReductionTests.cpp @@ -188,26 +188,24 @@ void testReduction(GPUUnitTestContext& ctx, ParallelReduction& reduction, Resour } // Create a texture with test data. - ref pTexture = Texture::create2D(pDevice, width, height, format, 1, 1, pInitData.get()); + ref pTexture = pDevice->createTexture2D(width, height, format, 1, 1, pInitData.get()); // Test Sum operation. { // Allocate buffer for the result on the GPU. DataType nullValue = {}; - ref pResultBuffer = Buffer::create(pDevice, 16, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, &nullValue); + ref pResultBuffer = pDevice->createBuffer(16, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, &nullValue); // Perform reduction operation. DataType result; reduction.execute(ctx.getRenderContext(), pTexture, ParallelReduction::Type::Sum, &result, pResultBuffer, 0); // Verify that returned result is identical to result stored to GPU buffer. - DataType* resultBuffer = (DataType*)pResultBuffer->map(Buffer::MapType::Read); - FALCOR_ASSERT(resultBuffer); + DataType resultBuffer = pResultBuffer->getElement(0); for (uint32_t i = 0; i < 4; i++) { - EXPECT_EQ((*resultBuffer)[i], result[i % 4]) << "i = " << i; + EXPECT_EQ(resultBuffer[i], result[i % 4]) << "i = " << i; } - pResultBuffer->unmap(); // Compare result to reference value computed on the CPU. for (uint32_t i = 0; i < 4; i++) @@ -238,15 +236,14 @@ void testReduction(GPUUnitTestContext& ctx, ParallelReduction& reduction, Resour { // Allocate buffer for the result on the GPU. DataType nullValues[2] = {{}, {}}; - ref pResultBuffer = Buffer::create(pDevice, 32, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, nullValues); + ref pResultBuffer = pDevice->createBuffer(32, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullValues); // Perform reduction operation. DataType result[2]; reduction.execute(ctx.getRenderContext(), pTexture, ParallelReduction::Type::MinMax, result, pResultBuffer, 0); // Verify that returned result is identical to result stored to GPU buffer. - DataType* resultBuffer = (DataType*)pResultBuffer->map(Buffer::MapType::Read); - FALCOR_ASSERT(resultBuffer); + std::vector resultBuffer = pResultBuffer->getElements(); for (uint32_t i = 0; i < 2; i++) { for (uint32_t j = 0; j < 4; j++) @@ -254,7 +251,6 @@ void testReduction(GPUUnitTestContext& ctx, ParallelReduction& reduction, Resour EXPECT_EQ(resultBuffer[i][j], result[i][j]) << "i = " << i << " j = " << j; } } - pResultBuffer->unmap(); // Compare result to reference value computed on the CPU. for (uint32_t i = 0; i < 4; i++) diff --git a/Source/Tools/FalcorTest/Tests/Utils/PrefixSumTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/PrefixSumTests.cpp index 59933ff76..758686b1a 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/PrefixSumTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/PrefixSumTests.cpp @@ -59,13 +59,13 @@ void testPrefixSum(GPUUnitTestContext& ctx, PrefixSum& prefixSum, uint32_t numEl for (auto& it : testData) it = r() % maxVal; - ref pTestDataBuffer = Buffer::create( - pDevice, numElems * sizeof(uint32_t), Resource::BindFlags::UnorderedAccess, Buffer::CpuAccess::None, testData.data() - ); + ref pTestDataBuffer = + pDevice->createBuffer(numElems * sizeof(uint32_t), ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, testData.data()); // Allocate buffer for the total sum on the GPU. uint32_t nullValue = 0; - ref pSumBuffer = Buffer::create(pDevice, 4, ResourceBindFlags::ShaderResource, Buffer::CpuAccess::None, &nullValue); + ref pSumBuffer = + pDevice->createBuffer(sizeof(uint32_t), ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, &nullValue); // Execute prefix sum on the GPU. uint32_t sum = 0; @@ -77,18 +77,14 @@ void testPrefixSum(GPUUnitTestContext& ctx, PrefixSum& prefixSum, uint32_t numEl // Compare results. EXPECT_EQ(sum, refSum); - uint32_t* resultSum = (uint32_t*)pSumBuffer->map(Buffer::MapType::Read); - FALCOR_ASSERT(resultSum); - EXPECT_EQ(resultSum[0], refSum); - pSumBuffer->unmap(); + uint32_t resultSum = pSumBuffer->getElement(0); + EXPECT_EQ(resultSum, refSum); - const uint32_t* result = (const uint32_t*)pTestDataBuffer->map(Buffer::MapType::Read); - FALCOR_ASSERT(result); + std::vector result = pTestDataBuffer->getElements(); for (uint32_t i = 0; i < numElems; i++) { EXPECT_EQ(testData[i], result[i]) << "i = " << i; } - pTestDataBuffer->unmap(); } } // namespace diff --git a/Source/Tools/FalcorTest/Tests/Utils/PropertiesTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/PropertiesTests.cpp index 774365929..308f53f2a 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/PropertiesTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/PropertiesTests.cpp @@ -101,17 +101,7 @@ void testPropertyType(CPUUnitTestContext& ctx, const T& checkValue, const T& dif // Test Properties::get(name) EXPECT_EQ(props.get("value"), checkValue); - { - try - { - props.get("value2"); - EXPECT(false); - } - catch (const RuntimeError&) - { - EXPECT(true); - } - } + EXPECT_THROW(props.get("value2")); // Test Properties::get(name, def) EXPECT_EQ(props.get("value", differentValue), checkValue); diff --git a/Source/Tools/FalcorTest/Tests/Utils/SettingsTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/SettingsTests.cpp index 03229fde7..70e746b74 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/SettingsTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/SettingsTests.cpp @@ -172,38 +172,9 @@ CPU_TEST(Settings_OptionsTypes) EXPECT_EQ(result[0], validTuple[0]); EXPECT_EQ(result[1], validTuple[1]); - bool wrongTypeString = false; - try - { - options.get("string", int(3)); - } - catch (const Falcor::SettingsProperties::TypeError&) - { - wrongTypeString = true; - } - EXPECT(wrongTypeString); - - bool wrongTypeInt = false; - try - { - options.get("int", std::string("test")); - } - catch (const Falcor::SettingsProperties::TypeError&) - { - wrongTypeInt = true; - } - EXPECT(wrongTypeInt); - - bool wrongTypeArray = false; - try - { - options.get("int[2]", float(0.f)); - } - catch (const Falcor::SettingsProperties::TypeError&) - { - wrongTypeArray = true; - } - EXPECT(wrongTypeArray); + EXPECT_THROW_AS(options.get("string", int(3)), Falcor::SettingsProperties::TypeError); + EXPECT_THROW_AS(options.get("int", std::string("test")), Falcor::SettingsProperties::TypeError); + EXPECT_THROW_AS(options.get("int[2]", float(0.f)), Falcor::SettingsProperties::TypeError); } CPU_TEST(Settings_OptionsOverride) @@ -357,44 +328,44 @@ CPU_TEST(Settings_UpdatePathsColon) pybind11::dict pyDict; pyDict["standardsearchpath:media"] = C_DRIVE "/media"; settings.addOptions(pyDict); - ASSERT_EQ(settings.getSearchDirectories("media").get().size(), 1); - EXPECT_EQ(settings.getSearchDirectories("media").get()[0], std::filesystem::weakly_canonical(C_DRIVE "/media")); + ASSERT_EQ(settings.getSearchDirectories("media").size(), 1); + EXPECT_EQ(settings.getSearchDirectories("media")[0], std::filesystem::weakly_canonical(C_DRIVE "/media")); } { pybind11::dict pyDict; pyDict["standardsearchpath:media"] = C_DRIVE "/media/different"; settings.addOptions(pyDict); - ASSERT_EQ(settings.getSearchDirectories("media").get().size(), 1); - EXPECT_EQ(settings.getSearchDirectories("media").get()[0], std::filesystem::weakly_canonical(C_DRIVE "/media/different")); + ASSERT_EQ(settings.getSearchDirectories("media").size(), 1); + EXPECT_EQ(settings.getSearchDirectories("media")[0], std::filesystem::weakly_canonical(C_DRIVE "/media/different")); } { pybind11::dict pyDict; pyDict["standardsearchpath:media"] = "&;" C_DRIVE "/media/two"; settings.addOptions(pyDict); - ASSERT_EQ(settings.getSearchDirectories("media").get().size(), 2); - EXPECT_EQ(settings.getSearchDirectories("media").get()[0], std::filesystem::weakly_canonical(C_DRIVE "/media/different")); - EXPECT_EQ(settings.getSearchDirectories("media").get()[1], std::filesystem::weakly_canonical(C_DRIVE "/media/two")); + ASSERT_EQ(settings.getSearchDirectories("media").size(), 2); + EXPECT_EQ(settings.getSearchDirectories("media")[0], std::filesystem::weakly_canonical(C_DRIVE "/media/different")); + EXPECT_EQ(settings.getSearchDirectories("media")[1], std::filesystem::weakly_canonical(C_DRIVE "/media/two")); } { pybind11::dict pyDict; pyDict["searchpath:media"] = "&;" C_DRIVE "/media/three"; settings.addOptions(pyDict); - ASSERT_EQ(settings.getSearchDirectories("media").get().size(), 1); - EXPECT_EQ(settings.getSearchDirectories("media").get()[0], std::filesystem::weakly_canonical(C_DRIVE "/media/three")); + ASSERT_EQ(settings.getSearchDirectories("media").size(), 1); + EXPECT_EQ(settings.getSearchDirectories("media")[0], std::filesystem::weakly_canonical(C_DRIVE "/media/three")); } { pybind11::dict pyDict; pyDict["searchpath:media"] = "&;@;" C_DRIVE "/media/four"; settings.addOptions(pyDict); - ASSERT_EQ(settings.getSearchDirectories("media").get().size(), 4); - EXPECT_EQ(settings.getSearchDirectories("media").get()[0], std::filesystem::weakly_canonical(C_DRIVE "/media/three")); - EXPECT_EQ(settings.getSearchDirectories("media").get()[1], std::filesystem::weakly_canonical(C_DRIVE "/media/different")); - EXPECT_EQ(settings.getSearchDirectories("media").get()[2], std::filesystem::weakly_canonical(C_DRIVE "/media/two")); - EXPECT_EQ(settings.getSearchDirectories("media").get()[3], std::filesystem::weakly_canonical(C_DRIVE "/media/four")); + ASSERT_EQ(settings.getSearchDirectories("media").size(), 4); + EXPECT_EQ(settings.getSearchDirectories("media")[0], std::filesystem::weakly_canonical(C_DRIVE "/media/three")); + EXPECT_EQ(settings.getSearchDirectories("media")[1], std::filesystem::weakly_canonical(C_DRIVE "/media/different")); + EXPECT_EQ(settings.getSearchDirectories("media")[2], std::filesystem::weakly_canonical(C_DRIVE "/media/two")); + EXPECT_EQ(settings.getSearchDirectories("media")[3], std::filesystem::weakly_canonical(C_DRIVE "/media/four")); } } @@ -406,8 +377,8 @@ CPU_TEST(Settings_UpdatePathsSeparate) pyDict["standardsearchpath"] = pybind11::dict(); pyDict["standardsearchpath"]["media"] = C_DRIVE "/media"; settings.addOptions(pyDict); - ASSERT_EQ(settings.getSearchDirectories("media").get().size(), 1); - EXPECT_EQ(settings.getSearchDirectories("media").get()[0], std::filesystem::weakly_canonical(C_DRIVE "/media")); + ASSERT_EQ(settings.getSearchDirectories("media").size(), 1); + EXPECT_EQ(settings.getSearchDirectories("media")[0], std::filesystem::weakly_canonical(C_DRIVE "/media")); } { @@ -415,8 +386,8 @@ CPU_TEST(Settings_UpdatePathsSeparate) pyDict["standardsearchpath"] = pybind11::dict(); pyDict["standardsearchpath"]["media"] = C_DRIVE "/media/different"; settings.addOptions(pyDict); - ASSERT_EQ(settings.getSearchDirectories("media").get().size(), 1); - EXPECT_EQ(settings.getSearchDirectories("media").get()[0], std::filesystem::weakly_canonical(C_DRIVE "/media/different")); + ASSERT_EQ(settings.getSearchDirectories("media").size(), 1); + EXPECT_EQ(settings.getSearchDirectories("media")[0], std::filesystem::weakly_canonical(C_DRIVE "/media/different")); } { @@ -424,9 +395,9 @@ CPU_TEST(Settings_UpdatePathsSeparate) pyDict["standardsearchpath"] = pybind11::dict(); pyDict["standardsearchpath"]["media"] = "&;" C_DRIVE "/media/two"; settings.addOptions(pyDict); - ASSERT_EQ(settings.getSearchDirectories("media").get().size(), 2); - EXPECT_EQ(settings.getSearchDirectories("media").get()[0], std::filesystem::weakly_canonical(C_DRIVE "/media/different")); - EXPECT_EQ(settings.getSearchDirectories("media").get()[1], std::filesystem::weakly_canonical(C_DRIVE "/media/two")); + ASSERT_EQ(settings.getSearchDirectories("media").size(), 2); + EXPECT_EQ(settings.getSearchDirectories("media")[0], std::filesystem::weakly_canonical(C_DRIVE "/media/different")); + EXPECT_EQ(settings.getSearchDirectories("media")[1], std::filesystem::weakly_canonical(C_DRIVE "/media/two")); } { @@ -434,8 +405,8 @@ CPU_TEST(Settings_UpdatePathsSeparate) pyDict["searchpath"] = pybind11::dict(); pyDict["searchpath"]["media"] = "&;" C_DRIVE "/media/three"; settings.addOptions(pyDict); - ASSERT_EQ(settings.getSearchDirectories("media").get().size(), 1); - EXPECT_EQ(settings.getSearchDirectories("media").get()[0], std::filesystem::weakly_canonical(C_DRIVE "/media/three")); + ASSERT_EQ(settings.getSearchDirectories("media").size(), 1); + EXPECT_EQ(settings.getSearchDirectories("media")[0], std::filesystem::weakly_canonical(C_DRIVE "/media/three")); } { @@ -443,11 +414,11 @@ CPU_TEST(Settings_UpdatePathsSeparate) pyDict["searchpath"] = pybind11::dict(); pyDict["searchpath"]["media"] = "&;@;" C_DRIVE "/media/four"; settings.addOptions(pyDict); - ASSERT_EQ(settings.getSearchDirectories("media").get().size(), 4); - EXPECT_EQ(settings.getSearchDirectories("media").get()[0], std::filesystem::weakly_canonical(C_DRIVE "/media/three")); - EXPECT_EQ(settings.getSearchDirectories("media").get()[1], std::filesystem::weakly_canonical(C_DRIVE "/media/different")); - EXPECT_EQ(settings.getSearchDirectories("media").get()[2], std::filesystem::weakly_canonical(C_DRIVE "/media/two")); - EXPECT_EQ(settings.getSearchDirectories("media").get()[3], std::filesystem::weakly_canonical(C_DRIVE "/media/four")); + ASSERT_EQ(settings.getSearchDirectories("media").size(), 4); + EXPECT_EQ(settings.getSearchDirectories("media")[0], std::filesystem::weakly_canonical(C_DRIVE "/media/three")); + EXPECT_EQ(settings.getSearchDirectories("media")[1], std::filesystem::weakly_canonical(C_DRIVE "/media/different")); + EXPECT_EQ(settings.getSearchDirectories("media")[2], std::filesystem::weakly_canonical(C_DRIVE "/media/two")); + EXPECT_EQ(settings.getSearchDirectories("media")[3], std::filesystem::weakly_canonical(C_DRIVE "/media/four")); } } } // namespace Falcor diff --git a/Source/Tools/FalcorTest/Tests/Utils/TextureAnalyzerTests.cpp b/Source/Tools/FalcorTest/Tests/Utils/TextureAnalyzerTests.cpp index e3b9b0e3c..8e4b42214 100644 --- a/Source/Tools/FalcorTest/Tests/Utils/TextureAnalyzerTests.cpp +++ b/Source/Tools/FalcorTest/Tests/Utils/TextureAnalyzerTests.cpp @@ -130,14 +130,14 @@ GPU_TEST(TextureAnalyzer) std::vector> textures(kNumTests); for (size_t i = 0; i < kNumTests; i++) { - std::string fn = "tests/texture" + std::to_string(i + 1) + (i < kNumPNGs ? ".png" : ".exr"); - textures[i] = Texture::createFromFile(pDevice, fn, false, false); + std::filesystem::path path = getRuntimeDirectory() / fmt::format("data/tests/texture{}.{}", i + 1, i < kNumPNGs ? "png" : "exr"); + textures[i] = Texture::createFromFile(pDevice, path, false, false); if (!textures[i]) - throw RuntimeError("Failed to load {}", fn); + FALCOR_THROW("Failed to load {}", path); } // Analyze textures. - auto pResult = Buffer::create(pDevice, kNumTests * kResultSize, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); + auto pResult = pDevice->createBuffer(kNumTests * kResultSize, ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess); EXPECT(pResult); ctx.getRenderContext()->clearUAV(pResult->getUAV().get(), uint4(0)); @@ -149,7 +149,7 @@ GPU_TEST(TextureAnalyzer) auto verify = [&ctx](ref pResult) { // Verify results. - const TextureAnalyzer::Result* result = static_cast(pResult->map(Buffer::MapType::Read)); + std::vector result = pResult->getElements(); for (size_t i = 0; i < kNumTests; i++) { EXPECT_EQ(result[i].mask, kExpectedResult[i].mask) << "i = " << i; @@ -179,7 +179,6 @@ GPU_TEST(TextureAnalyzer) EXPECT_EQ(result[i].isNaN(TextureChannelFlags::RGBA), (rangeFlags & (uint32_t)TextureAnalyzer::Result::RangeFlags::NaN) != 0) << "i = " << i; } - pResult->unmap(); }; verify(pResult); diff --git a/Source/Tools/ImageCompare/ImageCompare.cpp b/Source/Tools/ImageCompare/ImageCompare.cpp index b6b85fdce..59714782b 100644 --- a/Source/Tools/ImageCompare/ImageCompare.cpp +++ b/Source/Tools/ImageCompare/ImageCompare.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -100,8 +100,14 @@ class Image auto image = create(FreeImage_GetWidth(floatBitmap), FreeImage_GetHeight(floatBitmap)); int bytesPerPixel = 4 * sizeof(float); FreeImage_ConvertToRawBits( - reinterpret_cast(image->getData()), floatBitmap, bytesPerPixel * image->getWidth(), bytesPerPixel * 8, FI_RGBA_RED_MASK, - FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, true + reinterpret_cast(image->getData()), + floatBitmap, + bytesPerPixel * image->getWidth(), + bytesPerPixel * 8, + FI_RGBA_RED_MASK, + FI_RGBA_GREEN_MASK, + FI_RGBA_BLUE_MASK, + true ); FreeImage_Unload(floatBitmap); @@ -436,8 +442,12 @@ int main(int argc, char** argv) } bool success = compareImages( - args::get(image1), args::get(image2), metric, thresholdFlag ? args::get(thresholdFlag) : 0.f, - alphaFlag ? args::get(alphaFlag) : false, heatMapFlag ? args::get(heatMapFlag) : "" + args::get(image1), + args::get(image2), + metric, + thresholdFlag ? args::get(thresholdFlag) : 0.f, + alphaFlag ? args::get(alphaFlag) : false, + heatMapFlag ? args::get(heatMapFlag) : "" ); return success ? 0 : 1; } diff --git a/Source/Tools/RenderGraphEditor/RenderGraphEditor.cpp b/Source/Tools/RenderGraphEditor/RenderGraphEditor.cpp index 4e06675f7..0a7c2e8c1 100644 --- a/Source/Tools/RenderGraphEditor/RenderGraphEditor.cpp +++ b/Source/Tools/RenderGraphEditor/RenderGraphEditor.cpp @@ -50,7 +50,7 @@ namespace { const std::string kViewerExecutableName = "Mogwai"; const std::string kScriptSwitch = "--script"; -const std::string kDefaultPassIcon = "framework/images/pass-icon.png"; +const std::filesystem::path kDefaultPassIcon = getRuntimeDirectory() / "data/framework/images/pass-icon.png"; } // namespace RenderGraphEditor::RenderGraphEditor(const SampleAppConfig& config, const Options& options) @@ -79,7 +79,7 @@ void RenderGraphEditor::onLoad(RenderContext* pRenderContext) { mpDefaultIconTex = Texture::createFromFile(getDevice(), kDefaultPassIcon, false, false); if (!mpDefaultIconTex) - throw RuntimeError("Failed to load icon"); + FALCOR_THROW("Failed to load icon"); PluginManager::instance().loadAllPlugins(); @@ -352,7 +352,8 @@ void RenderGraphEditor::onGuiRender(Gui* pGui) catch (const std::exception& e) { openViewer = msgBox( - "Error", std::string("Graph is invalid :\n ") + e.what() + "\n Are you sure you want to attempt preview?", + "Error", + std::string("Graph is invalid :\n ") + e.what() + "\n Are you sure you want to attempt preview?", MsgBoxType::OkCancel ) == MsgBoxButton::Ok; } diff --git a/Source/plugins/importers/AssimpImporter/AssimpImporter.cpp b/Source/plugins/importers/AssimpImporter/AssimpImporter.cpp index 22d3079bf..09bc42858 100644 --- a/Source/plugins/importers/AssimpImporter/AssimpImporter.cpp +++ b/Source/plugins/importers/AssimpImporter/AssimpImporter.cpp @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "AssimpImporter.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/API/Device.h" #include "Utils/Logger.h" #include "Utils/StringUtils.h" @@ -79,10 +79,9 @@ enum class ImportMode GLTF2, }; -float4x4 aiCast(const aiMatrix4x4& aiMat) +float4x4 aiCast(const aiMatrix4x4& ai) { - float4x4 m{aiMat.a1, aiMat.a2, aiMat.a3, aiMat.a4, aiMat.b1, aiMat.b2, aiMat.b3, aiMat.b4, - aiMat.c1, aiMat.c2, aiMat.c3, aiMat.c4, aiMat.d1, aiMat.d2, aiMat.d3, aiMat.d4}; + float4x4 m{ai.a1, ai.a2, ai.a3, ai.a4, ai.b1, ai.b2, ai.b3, ai.b4, ai.c1, ai.c2, ai.c3, ai.c4, ai.d1, ai.d2, ai.d3, ai.d4}; return m; } @@ -246,7 +245,8 @@ void createAnimation(ImporterData& data, const aiAnimation* pAiAnim, ImportMode for (uint32_t j = 0; j < data.getNodeInstanceCount(pAiNode->mNodeName.C_Str()); j++) { ref pAnimation = Animation::create( - std::string(pAiNode->mNodeName.C_Str()) + "." + std::to_string(j), data.getFalcorNodeID(pAiNode->mNodeName.C_Str(), j), + std::string(pAiNode->mNodeName.C_Str()) + "." + std::to_string(j), + data.getFalcorNodeID(pAiNode->mNodeName.C_Str(), j), durationInSeconds ); animations.push_back(pAnimation); @@ -588,7 +588,9 @@ void createMeshes(ImporterData& data) std::vector processedMeshes(meshes.size()); auto range = NumericRange(0, meshes.size()); std::for_each( - std::execution::par, range.begin(), range.end(), + std::execution::par, + range.begin(), + range.end(), [&](size_t i) { const aiMesh* pAiMesh = meshes[i]; @@ -1069,17 +1071,23 @@ void dumpAssimpData(ImporterData& data) for (uint32_t j = 0; j < pChannel->mNumPositionKeys; j++) out += fmt::format( - " position key[{}]: time {}, value {}\n", j, pChannel->mPositionKeys[j].mTime, + " position key[{}]: time {}, value {}\n", + j, + pChannel->mPositionKeys[j].mTime, aiCast(pChannel->mPositionKeys[j].mValue) ); for (uint32_t j = 0; j < pChannel->mNumRotationKeys; j++) out += fmt::format( - " rotation key[{}]: time {}, value {}\n", j, pChannel->mRotationKeys[j].mTime, + " rotation key[{}]: time {}, value {}\n", + j, + pChannel->mRotationKeys[j].mTime, aiCast(pChannel->mRotationKeys[j].mValue) ); for (uint32_t j = 0; j < pChannel->mNumScalingKeys; j++) out += fmt::format( - " scaling key[{}]: time {}, value {}\n", j, pChannel->mScalingKeys[j].mTime, + " scaling key[{}]: time {}, value {}\n", + j, + pChannel->mScalingKeys[j].mTime, aiCast(pChannel->mScalingKeys[j].mValue) ); } @@ -1181,7 +1189,11 @@ std::unique_ptr AssimpImporter::create() return std::make_unique(); } -void AssimpImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) +void AssimpImporter::importScene( + const std::filesystem::path& path, + SceneBuilder& builder, + const std::map& materialToShortName +) { importInternal(nullptr, 0, path, builder); } @@ -1191,7 +1203,7 @@ void AssimpImporter::importSceneFromMemory( size_t byteSize, std::string_view extension, SceneBuilder& builder, - const pybind11::dict& dict + const std::map& materialToShortName ) { importInternal(buffer, byteSize, {}, builder); diff --git a/Source/plugins/importers/AssimpImporter/AssimpImporter.h b/Source/plugins/importers/AssimpImporter/AssimpImporter.h index ed276bf89..71fc1a69d 100644 --- a/Source/plugins/importers/AssimpImporter/AssimpImporter.h +++ b/Source/plugins/importers/AssimpImporter/AssimpImporter.h @@ -50,14 +50,18 @@ class AssimpImporter : public Importer static std::unique_ptr create(); - void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) override; + void importScene( + const std::filesystem::path& path, + SceneBuilder& builder, + const std::map& materialToShortName + ) override; void importSceneFromMemory( const void* buffer, size_t byteSize, std::string_view extension, SceneBuilder& builder, - const pybind11::dict& dict + const std::map& materialToShortName ) override; }; diff --git a/Source/plugins/importers/PBRTImporter/Builder.cpp b/Source/plugins/importers/PBRTImporter/Builder.cpp index f1eb8f58a..74c8e0c1d 100644 --- a/Source/plugins/importers/PBRTImporter/Builder.cpp +++ b/Source/plugins/importers/PBRTImporter/Builder.cpp @@ -33,7 +33,7 @@ #include "Builder.h" #include "Helpers.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/Logger.h" namespace Falcor::pbrt @@ -141,7 +141,7 @@ const MaterialSceneEntity& BasicScene::getMaterial(const MaterialRef& materialRe } else { - throw RuntimeError("Expected valid material reference (index or name)."); + FALCOR_THROW("Expected valid material reference (index or name)."); } } @@ -382,8 +382,15 @@ void BasicSceneBuilder::onShape(const std::string& name, ParsedParameterVector p } ShapeSceneEntity shape( - name, std::move(dict), loc, getTransform(), mGraphicsState.reverseOrientation, mGraphicsState.currentMaterial, areaLightIndex, - mGraphicsState.currentInsideMedium, mGraphicsState.currentOutsideMedium + name, + std::move(dict), + loc, + getTransform(), + mGraphicsState.reverseOrientation, + mGraphicsState.currentMaterial, + areaLightIndex, + mGraphicsState.currentInsideMedium, + mGraphicsState.currentOutsideMedium ); if (mpActiveInstanceDefinition) diff --git a/Source/plugins/importers/PBRTImporter/Builder.h b/Source/plugins/importers/PBRTImporter/Builder.h index 632e8004e..30a26c3e3 100644 --- a/Source/plugins/importers/PBRTImporter/Builder.h +++ b/Source/plugins/importers/PBRTImporter/Builder.h @@ -34,7 +34,7 @@ #pragma once #include "Types.h" #include "Parser.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Utils/Math/Matrix.h" #include @@ -186,7 +186,13 @@ struct ShapeSceneEntity : public TransformedSceneEntity return fmt::format( "ShapeSceneEntity(name='{}', params={}, transform={}, reverseOrientation={}, " "materialRef={}. lightIndex={}, insideMedium='{}', outsideMedium='{}')", - name, params.toString(), to_string(transform), reverseOrientation, to_string(materialRef), lightIndex, insideMedium, + name, + params.toString(), + to_string(transform), + reverseOrientation, + to_string(materialRef), + lightIndex, + insideMedium, outsideMedium ); } diff --git a/Source/plugins/importers/PBRTImporter/EnvMapConverter.h b/Source/plugins/importers/PBRTImporter/EnvMapConverter.h index 54e86bf44..6fa87bfd5 100644 --- a/Source/plugins/importers/PBRTImporter/EnvMapConverter.h +++ b/Source/plugins/importers/PBRTImporter/EnvMapConverter.h @@ -46,9 +46,9 @@ class EnvMapConverter mpComputePass = ComputePass::create(mpDevice, "plugins/importers/PBRTImporter/EnvMapConverter.cs.slang"); Sampler::Desc desc; - desc.setFilterMode(Sampler::Filter::Linear, Sampler::Filter::Linear, Sampler::Filter::Linear); - desc.setAddressingMode(Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp, Sampler::AddressMode::Clamp); - mpSampler = Sampler::create(mpDevice, desc); + desc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Linear); + desc.setAddressingMode(TextureAddressingMode::Clamp, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); + mpSampler = mpDevice->createSampler(desc); } /** @@ -65,9 +65,14 @@ class EnvMapConverter uint2 dstDim{pSrcTexture->getWidth() * 2, pSrcTexture->getHeight()}; - ref pDstTexture = Texture::create2D( - mpDevice, dstDim.x, dstDim.y, ResourceFormat::RGBA32Float, 1, 1, nullptr, - Resource::BindFlags::ShaderResource | Resource::BindFlags::UnorderedAccess + ref pDstTexture = mpDevice->createTexture2D( + dstDim.x, + dstDim.y, + ResourceFormat::RGBA32Float, + 1, + 1, + nullptr, + ResourceBindFlags::ShaderResource | ResourceBindFlags::UnorderedAccess ); auto vars = mpComputePass->getRootVar()["gEnvMapConverter"]; diff --git a/Source/plugins/importers/PBRTImporter/Helpers.h b/Source/plugins/importers/PBRTImporter/Helpers.h index d9cf22724..7dce0535a 100644 --- a/Source/plugins/importers/PBRTImporter/Helpers.h +++ b/Source/plugins/importers/PBRTImporter/Helpers.h @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -27,7 +27,7 @@ **************************************************************************/ #pragma once #include "Types.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Utils/Logger.h" #include #include @@ -38,14 +38,14 @@ namespace Falcor::pbrt template [[noreturn]] inline void throwError(fmt::format_string format, Args&&... args) { - throw RuntimeError(format, std::forward(args)...); + FALCOR_THROW(format, std::forward(args)...); } template [[noreturn]] inline void throwError(const FileLoc& loc, fmt::format_string format, Args&&... args) { auto msg = fmt::format(format, std::forward(args)...); - throw RuntimeError("{}: {}", loc.toString(), msg); + FALCOR_THROW("{}: {}", loc.toString(), msg); } template diff --git a/Source/plugins/importers/PBRTImporter/LoopSubdivide.cpp b/Source/plugins/importers/PBRTImporter/LoopSubdivide.cpp index edce40e54..71388bb8f 100644 --- a/Source/plugins/importers/PBRTImporter/LoopSubdivide.cpp +++ b/Source/plugins/importers/PBRTImporter/LoopSubdivide.cpp @@ -32,8 +32,7 @@ // SPDX: Apache-2.0 #include "LoopSubdivide.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include #include @@ -88,7 +87,7 @@ struct SDFace if (v[i] == vert) return i; } - throw RuntimeError("Basic logic error in SDFace::vnum()."); + FALCOR_THROW("Basic logic error in SDFace::vnum()."); } SDFace* nextFace(SDVertex* vert) const { return f[vnum(vert)]; } @@ -102,7 +101,7 @@ struct SDFace if (v[i] != v0 && v[i] != v1) return v[i]; } - throw RuntimeError("Basic logic error in SDFace::otherVert()"); + FALCOR_THROW("Basic logic error in SDFace::otherVert()"); } SDVertex* v[3]; diff --git a/Source/plugins/importers/PBRTImporter/PBRTImporter.cpp b/Source/plugins/importers/PBRTImporter/PBRTImporter.cpp index 696371b8a..a8fa6f771 100644 --- a/Source/plugins/importers/PBRTImporter/PBRTImporter.cpp +++ b/Source/plugins/importers/PBRTImporter/PBRTImporter.cpp @@ -64,8 +64,7 @@ #include "Helpers.h" #include "LoopSubdivide.h" #include "EnvMapConverter.h" -#include "Core/Assert.h" -#include "Core/Errors.h" +#include "Core/Error.h" #include "Core/API/Device.h" #include "Utils/Settings.h" #include "Utils/Logger.h" @@ -94,17 +93,21 @@ namespace Falcor namespace pbrt { const float4x4 kYtoZ = { - 1.f, 0.f, 0.f, 0.f, // - 0.f, 0.f, 1.f, 0.f, // - 0.f, 1.f, 0.f, 0.f, // - 0.f, 0.f, 0.f, 1.f, // + // clang-format off + 1.f, 0.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 0.f, 1.f, + // clang-format on }; const float4x4 kInvertZ = { - 1.f, 0.f, 0.f, 0.f, // - 0.f, 1.f, 0.f, 0.f, // - 0.f, 0.f, -1.f, 0.f, // - 0.f, 0.f, 0.f, 1.f, // + // clang-format off + 1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, -1.f, 0.f, + 0.f, 0.f, 0.f, 1.f, + // clang-format on }; /** @@ -292,7 +295,7 @@ float3 spectrumToRGB(const Spectrum& spectrum, SpectrumType spectrumType) } else { - throw RuntimeError("Unhandled spectrum variant."); + FALCOR_THROW("Unhandled spectrum variant."); } } @@ -440,7 +443,9 @@ float2 getRoughness( { float2 fallback{0.5f}; logWarning( - entity.loc, "Non-constant roughness is currently not supported. Using constant roughness of {},{} instead.", fallback.x, + entity.loc, + "Non-constant roughness is currently not supported. Using constant roughness of {},{} instead.", + fallback.x, fallback.y ); return fallback; @@ -710,7 +715,7 @@ Light createLight(BuilderContext& ctx, const LightSceneEntity& entity) // Falcor doesn't have constant infinite emitter. // We create a one pixel env map for now. float4 data{spectrumToRGB(L[0], SpectrumType::Illuminant), 0.f}; - auto pTexture = Texture::create2D(ctx.builder.getDevice(), 1, 1, ResourceFormat::RGBA32Float, 1, Texture::kMaxPossible, &data); + auto pTexture = ctx.builder.getDevice()->createTexture2D(1, 1, ResourceFormat::RGBA32Float, 1, Texture::kMaxPossible, &data); auto pEnvMap = EnvMap::create(ctx.builder.getDevice(), pTexture); light.pEnvMap = pEnvMap; @@ -997,7 +1002,8 @@ Falcor::ref createMaterial(BuilderContext& ctx, const Material { auto pPBRTMaterial = PBRTDiffuseMaterial::create(ctx.builder.getDevice(), entity.name); assignSpectrumTexture( - reflectance, [&](float3 rgb) { pPBRTMaterial->setBaseColor(float4(rgb, 1.f)); }, + reflectance, + [&](float3 rgb) { pPBRTMaterial->setBaseColor(float4(rgb, 1.f)); }, [&](Falcor::ref pTexture) { pPBRTMaterial->setBaseColorTexture(pTexture); } ); pPBRTMaterial->setDoubleSided(true); @@ -1009,7 +1015,8 @@ Falcor::ref createMaterial(BuilderContext& ctx, const Material pStandardMaterial->setMetallic(0.f); pStandardMaterial->setRoughness(1.f); assignSpectrumTexture( - reflectance, [&](float3 rgb) { pStandardMaterial->setBaseColor(float4(rgb, 1.f)); }, + reflectance, + [&](float3 rgb) { pStandardMaterial->setBaseColor(float4(rgb, 1.f)); }, [&](Falcor::ref pTexture) { pStandardMaterial->setBaseColorTexture(pTexture); } ); pStandardMaterial->setDoubleSided(true); @@ -1034,7 +1041,8 @@ Falcor::ref createMaterial(BuilderContext& ctx, const Material auto pPBRTMaterial = PBRTCoatedDiffuseMaterial::create(ctx.builder.getDevice(), entity.name); pPBRTMaterial->setRoughness(roughness); assignSpectrumTexture( - reflectance, [&](float3 rgb) { pPBRTMaterial->setBaseColor(float4(rgb, 1.f)); }, + reflectance, + [&](float3 rgb) { pPBRTMaterial->setBaseColor(float4(rgb, 1.f)); }, [&](Falcor::ref pTexture) { pPBRTMaterial->setBaseColorTexture(pTexture); } ); pPBRTMaterial->setDoubleSided(true); @@ -1048,7 +1056,8 @@ Falcor::ref createMaterial(BuilderContext& ctx, const Material pStandardMaterial->setMetallic(0.f); pStandardMaterial->setRoughness(std::sqrt(roughness)); assignSpectrumTexture( - reflectance, [&](float3 rgb) { pStandardMaterial->setBaseColor(float4(rgb, 1.f)); }, + reflectance, + [&](float3 rgb) { pStandardMaterial->setBaseColor(float4(rgb, 1.f)); }, [&](Falcor::ref pTexture) { pStandardMaterial->setBaseColorTexture(pTexture); } ); pStandardMaterial->setDoubleSided(true); @@ -1119,8 +1128,16 @@ Falcor::ref createMaterial(BuilderContext& ctx, const Material else { warnUnsupportedParameters( - params, {"interface.roughness", "interface.uroughness", "interface.vroughness", "interface.eta", "maxdepth", "nsamples", - "g", "albedo", "displacement"} + params, + {"interface.roughness", + "interface.uroughness", + "interface.vroughness", + "interface.eta", + "maxdepth", + "nsamples", + "g", + "albedo", + "displacement"} ); float3 specularAlbedo = getConductorSpecularAlbedo(ctx, entity, "conductor.reflectance", "conductor.eta", "conductor.k"); @@ -1196,11 +1213,13 @@ Falcor::ref createMaterial(BuilderContext& ctx, const Material { auto pPBRTMaterial = PBRTDiffuseTransmissionMaterial::create(ctx.builder.getDevice(), entity.name); assignSpectrumTexture( - reflectance, [&](float3 rgb) { pPBRTMaterial->setBaseColor(float4(rgb, 1.f)); }, + reflectance, + [&](float3 rgb) { pPBRTMaterial->setBaseColor(float4(rgb, 1.f)); }, [&](Falcor::ref pTexture) { pPBRTMaterial->setBaseColorTexture(pTexture); } ); assignSpectrumTexture( - transmittance, [&](float3 rgb) { pPBRTMaterial->setTransmissionColor(rgb); }, + transmittance, + [&](float3 rgb) { pPBRTMaterial->setTransmissionColor(rgb); }, [&](Falcor::ref pTexture) { pPBRTMaterial->setTransmissionTexture(pTexture); } ); pMaterial = pPBRTMaterial; @@ -1212,11 +1231,13 @@ Falcor::ref createMaterial(BuilderContext& ctx, const Material pStandardMaterial->setRoughness(1.f); pStandardMaterial->setDiffuseTransmission(0.5f); assignSpectrumTexture( - reflectance, [&](float3 rgb) { pStandardMaterial->setBaseColor(float4(rgb, 1.f)); }, + reflectance, + [&](float3 rgb) { pStandardMaterial->setBaseColor(float4(rgb, 1.f)); }, [&](Falcor::ref pTexture) { pStandardMaterial->setBaseColorTexture(pTexture); } ); assignSpectrumTexture( - transmittance, [&](float3 rgb) { pStandardMaterial->setTransmissionColor(rgb); }, + transmittance, + [&](float3 rgb) { pStandardMaterial->setTransmissionColor(rgb); }, [&](Falcor::ref pTexture) { pStandardMaterial->setTransmissionTexture(pTexture); } ); pStandardMaterial->setDoubleSided(true); @@ -1274,7 +1295,8 @@ Falcor::ref createMaterial(BuilderContext& ctx, const Material logWarning(entity.loc, "Ignoring 'pheomelanin' parameter since 'reflectance' was provided."); assignSpectrumTexture( - *reflectance, [&](float3 constant) { pHairMaterial->setBaseColor(float4(constant, 1.f)); }, + *reflectance, + [&](float3 constant) { pHairMaterial->setBaseColor(float4(constant, 1.f)); }, [&](Falcor::ref pTexture) { pHairMaterial->setBaseColorTexture(pTexture); } ); } @@ -1663,8 +1685,17 @@ std::variant createCurveGeometry(BuilderContext if (mode == CurveTessellationMode::LinearSweptSphere) { auto result = CurveTessellation::convertToLinearSweptSphere( - curveAggregate.strands.size(), curveAggregate.strands.data(), curveAggregate.points.data(), curveAggregate.widths.data(), - nullptr, 1, subdivPerSegment, 1, 1, 1.f, float4x4::identity() + curveAggregate.strands.size(), + curveAggregate.strands.data(), + curveAggregate.points.data(), + curveAggregate.widths.data(), + nullptr, + 1, + subdivPerSegment, + 1, + 1, + 1.f, + float4x4::identity() ); Falcor::SceneBuilder::Curve curve; @@ -1684,8 +1715,16 @@ std::variant createCurveGeometry(BuilderContext if (mode == CurveTessellationMode::PolyTube) { result = CurveTessellation::convertToPolytube( - curveAggregate.strands.size(), curveAggregate.strands.data(), curveAggregate.points.data(), curveAggregate.widths.data(), - nullptr, subdivPerSegment, 1, 1, 1.f, 4 + curveAggregate.strands.size(), + curveAggregate.strands.data(), + curveAggregate.points.data(), + curveAggregate.widths.data(), + nullptr, + subdivPerSegment, + 1, + 1, + 1.f, + 4 ); } else @@ -1872,7 +1911,11 @@ std::unique_ptr PBRTImporter::create() return std::make_unique(); } -void PBRTImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) +void PBRTImporter::importScene( + const std::filesystem::path& path, + SceneBuilder& builder, + const std::map& materialToShortName +) { if (!path.is_absolute()) throw ImporterError(path, "Expected absolute path."); diff --git a/Source/plugins/importers/PBRTImporter/PBRTImporter.h b/Source/plugins/importers/PBRTImporter/PBRTImporter.h index 517791e5e..9f24a3e7b 100644 --- a/Source/plugins/importers/PBRTImporter/PBRTImporter.h +++ b/Source/plugins/importers/PBRTImporter/PBRTImporter.h @@ -43,7 +43,11 @@ class PBRTImporter : public Importer static std::unique_ptr create(); - void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) override; + void importScene( + const std::filesystem::path& path, + SceneBuilder& builder, + const std::map& materialToShortName + ) override; }; } // namespace Falcor diff --git a/Source/plugins/importers/PBRTImporter/Parameters.cpp b/Source/plugins/importers/PBRTImporter/Parameters.cpp index c77462411..22f16f9a5 100644 --- a/Source/plugins/importers/PBRTImporter/Parameters.cpp +++ b/Source/plugins/importers/PBRTImporter/Parameters.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ #include "Parameters.h" #include "Helpers.h" -#include "Core/Assert.h" +#include "Core/Error.h" namespace Falcor::pbrt { @@ -206,7 +206,7 @@ FileLoc ParameterDictionary::getParameterLoc(const std::string& name) const { auto p = findParameter(name); if (!p) - throw RuntimeError("Parameter not found!"); + FALCOR_THROW("Parameter not found!"); return p->loc; } @@ -507,7 +507,9 @@ std::vector ParameterDictionary::extractSpectrumArray(const ParsedPara if (param.type == "rgb") { return returnArray( - param, param.floats, 3, + param, + param.floats, + 3, [¶m](const Float* v) -> Spectrum { Falcor::float3 rgb(v[0], v[1], v[2]); @@ -531,7 +533,9 @@ std::vector ParameterDictionary::extractSpectrumArray(const ParsedPara } size_t sampleCount = param.floats.size() / 2; return returnArray( - param, param.floats, param.floats.size(), + param, + param.floats, + param.floats.size(), [¶m, sampleCount](const Float* v) -> Spectrum { std::vector wavelengths(sampleCount), values(sampleCount); @@ -540,8 +544,11 @@ std::vector ParameterDictionary::extractSpectrumArray(const ParsedPara if (i > 0 && v[2 * i] <= wavelengths[i - 1]) { throwError( - param.loc, "Spectrum description invalid: at %d'th entry, wavelengths aren't increasing: %f >= %f.", i - 1, - wavelengths[i - 1], v[2 * i] + param.loc, + "Spectrum description invalid: at %d'th entry, wavelengths aren't increasing: %f >= %f.", + i - 1, + wavelengths[i - 1], + v[2 * i] ); } wavelengths[i] = v[2 * i]; @@ -554,7 +561,9 @@ std::vector ParameterDictionary::extractSpectrumArray(const ParsedPara else if (param.type == "spectrum" && !param.strings.empty()) { return returnArray( - param, param.strings, 1, + param, + param.strings, + 1, [¶m, &resolver](const std::string* s) -> Spectrum { auto namedSpectrum = Spectra::getNamedSpectrum(*s); diff --git a/Source/plugins/importers/PBRTImporter/Parser.cpp b/Source/plugins/importers/PBRTImporter/Parser.cpp index 7dd294786..859ee46e4 100644 --- a/Source/plugins/importers/PBRTImporter/Parser.cpp +++ b/Source/plugins/importers/PBRTImporter/Parser.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -33,7 +33,7 @@ #include "Parser.h" #include "Helpers.h" -#include "Core/Assert.h" +#include "Core/Error.h" #include "Core/Platform/OS.h" #include "Utils/Logger.h" diff --git a/Source/plugins/importers/PythonImporter/PythonImporter.cpp b/Source/plugins/importers/PythonImporter/PythonImporter.cpp index 4cb36a907..1c7296ecb 100644 --- a/Source/plugins/importers/PythonImporter/PythonImporter.cpp +++ b/Source/plugins/importers/PythonImporter/PythonImporter.cpp @@ -60,8 +60,8 @@ static std::optional parseLegacyHeader(const std::string& script) /// Set of currently imported paths, used to avoid recursion. TODO: REMOVEGLOBAL static std::set sImportPaths; -/// Stack of import directories to properly handle adding/removing data search paths. TODO: REMOVEGLOBAL -static std::vector sImportDirectories; +/// Keeps track of how many recursive importers are in process. TODO: REMOVEGLOBAL +static size_t sImportDepth; /** * This class is used to handle nested imports through RAII. @@ -72,21 +72,21 @@ static std::vector sImportDirectories; class ScopedImport { public: - ScopedImport(const std::filesystem::path& path, SceneBuilder& builder) - : mScopedSettings(builder.getSettings()), mPath(path), mDirectory(path.parent_path()) + ScopedImport(SceneBuilder& builder, const std::filesystem::path& path) : mBuilder(builder), mPath(path) { if (!path.empty()) { FALCOR_ASSERT(path.is_absolute()); sImportPaths.emplace(mPath); - sImportDirectories.push_back(mDirectory); - // Add directory to search directories (add it to the front to make it highest priority). - addDataDirectory(mDirectory, true); + // Add base directory to search paths. + mBuilder.pushAssetResolver(); + mBuilder.getAssetResolver().addSearchPath(path.parent_path(), SearchPathPriority::First); } // Set global scene builder as workaround to support old Python API. - setActivePythonSceneBuilder(&builder); + setActivePythonSceneBuilder(&mBuilder); + sImportDepth++; } ~ScopedImport() { @@ -95,27 +95,18 @@ class ScopedImport auto erased = sImportPaths.erase(mPath); FALCOR_ASSERT(erased == 1); - FALCOR_ASSERT(sImportDirectories.size() > 0); - sImportDirectories.pop_back(); - - // Remove script directory from search path (only if not needed by the outer importer). - if (std::find(sImportDirectories.begin(), sImportDirectories.end(), mDirectory) == sImportDirectories.end()) - { - removeDataDirectory(mDirectory); - } + mBuilder.popAssetResolver(); } // Unset global scene builder. - if (sImportDirectories.size() == 0) - { + FALCOR_ASSERT(sImportDepth > 0); + if (--sImportDepth == 0) setActivePythonSceneBuilder(nullptr); - } } private: - ScopedSettings mScopedSettings; + SceneBuilder& mBuilder; std::filesystem::path mPath; - std::filesystem::path mDirectory; }; static bool isRecursiveImport(const std::filesystem::path& path) @@ -130,7 +121,11 @@ std::unique_ptr PythonImporter::create() return std::make_unique(); } -void PythonImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) +void PythonImporter::importScene( + const std::filesystem::path& path, + SceneBuilder& builder, + const std::map& materialToShortName +) { if (!path.is_absolute()) throw ImporterError(path, "Expected absolute path."); @@ -149,12 +144,12 @@ void PythonImporter::importSceneFromMemory( size_t byteSize, std::string_view extension, SceneBuilder& builder, - const pybind11::dict& dict + const std::map& materialToShortName ) { - checkArgument(extension == "pyscene", "Unexpected format."); - checkArgument(buffer != nullptr, "Missing buffer."); - checkArgument(byteSize > 0, "Empty buffer."); + FALCOR_CHECK(extension == "pyscene", "Unexpected format."); + FALCOR_CHECK(buffer != nullptr, "Missing buffer."); + FALCOR_CHECK(byteSize > 0, "Empty buffer."); const std::string script(static_cast(buffer), byteSize); @@ -169,7 +164,7 @@ void PythonImporter::importInternal(const std::string& script, const std::filesy // Keep track of this import and add script directory to data search directories. // We use RAII here to make sure the scope is properly removed when throwing an exception. - ScopedImport scopedImport(path, builder); + ScopedImport scopedImport(builder, path); // Execute script. try diff --git a/Source/plugins/importers/PythonImporter/PythonImporter.h b/Source/plugins/importers/PythonImporter/PythonImporter.h index 6f3daf6d6..99ef944b1 100644 --- a/Source/plugins/importers/PythonImporter/PythonImporter.h +++ b/Source/plugins/importers/PythonImporter/PythonImporter.h @@ -40,13 +40,17 @@ class PythonImporter : public Importer static std::unique_ptr create(); - void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) override; + void importScene( + const std::filesystem::path& path, + SceneBuilder& builder, + const std::map& materialToShortName + ) override; void importSceneFromMemory( const void* buffer, size_t byteSize, std::string_view extension, SceneBuilder& builder, - const pybind11::dict& dict + const std::map& materialToShortName ) override; private: diff --git a/Source/plugins/importers/USDImporter/CMakeLists.txt b/Source/plugins/importers/USDImporter/CMakeLists.txt index 2b3e5cbde..88734b8ab 100644 --- a/Source/plugins/importers/USDImporter/CMakeLists.txt +++ b/Source/plugins/importers/USDImporter/CMakeLists.txt @@ -5,25 +5,13 @@ endif() add_plugin(USDImporter) target_sources(USDImporter PRIVATE - CreateSpecularTexture.cs.slang - CreateSpecularTransmissionTexture.cs.slang ImporterContext.cpp ImporterContext.h - IndexedVector.h - PackBaseColorAlpha.cs.slang - PreviewSurfaceConverter.cpp - PreviewSurfaceConverter.h - SampleTexture.slang - StandardMaterialSpec.h - Tessellation.cpp - Tessellation.h - USDHelpers.h USDImporter.cpp USDImporter.h - Utils.h ) -target_link_libraries(USDImporter PRIVATE nv-usd opensubdiv) +target_link_libraries(USDImporter PRIVATE nv-usd USDUtils) target_copy_shaders(USDImporter plugins/importers/USDImporter) diff --git a/Source/plugins/importers/USDImporter/ImporterContext.cpp b/Source/plugins/importers/USDImporter/ImporterContext.cpp index d814c8c15..037ffd006 100644 --- a/Source/plugins/importers/USDImporter/ImporterContext.cpp +++ b/Source/plugins/importers/USDImporter/ImporterContext.cpp @@ -26,7 +26,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "ImporterContext.h" -#include "USDHelpers.h" #include "Core/API/Device.h" #include "Utils/NumericRange.h" #include "Scene/Importer.h" @@ -34,15 +33,13 @@ #include "Scene/Material/HairMaterial.h" #include "Scene/Material/StandardMaterial.h" #include "Utils/Settings.h" -#include "Tessellation.h" - -#include +#include "USDUtils/USDHelpers.h" +#include "USDUtils/USDUtils.h" +#include "USDUtils/USDScene1Utils.h" +#include "USDUtils/Tessellator/Tessellation.h" #include -#include -#include - BEGIN_DISABLE_USD_WARNINGS #include #include @@ -108,14 +105,14 @@ namespace Falcor // the starting index into each stored with each GeomSubset. struct MeshGeomData { - std::vector triangulatedIndices; // Triangle point indices - std::vector points; // Vertex positions (always indexed) - std::vector normals; // Normals; indexed iff normalInterp is vertex or varying - std::vector tangents; // Tangents; optional and always indexed - std::vector texCrds; // Texture coordinates, indexed iff texCrdsInterp is vertex or varying - std::vector curveRadii; // Curve widths (always indexed; available if mesh was generated for a curve tessellated into triangles) - std::vector jointIndices; // Bone indices - std::vector jointWeights; // Bone weights corresponding to the bones referenced in jointIndices + fast_vector triangulatedIndices; // Triangle point indices + fast_vector points; // Vertex positions (always indexed) + fast_vector normals; // Normals; indexed iff normalInterp is vertex or varying + fast_vector tangents; // Tangents; optional and always indexed + fast_vector texCrds; // Texture coordinates, indexed iff texCrdsInterp is vertex or varying + fast_vector curveRadii; // Curve widths (always indexed; available if mesh was generated for a curve tessellated into triangles) + fast_vector jointIndices; // Bone indices + fast_vector jointWeights; // Bone weights corresponding to the bones referenced in jointIndices std::vector geomSubsets; // Geometry subset data; one entry for entire mesh if no subsets are present AttributeFrequency normalInterp; // Normal interpolation mode AttributeFrequency texCrdsInterp; // Texture coordinate interpolation mode @@ -127,10 +124,10 @@ namespace Falcor struct CurveGeomData { uint32_t degree; // Polynomial degree of curve; linear (1) by default - std::vector indices; // First point indices of curve segments - std::vector points; // Vertex positions - std::vector radius; // Radius of spheres at curve ends - std::vector texCrds; // Texture coordinates + fast_vector indices; // First point indices of curve segments + fast_vector points; // Vertex positions + fast_vector radius; // Radius of spheres at curve ends + fast_vector texCrds; // Texture coordinates std::string id; // Name; equal to name of UsdGeomBasisCurves UsdShadeMaterial material; // Bound material }; @@ -452,7 +449,7 @@ namespace Falcor // one subset, either explicitly, or to the implied "catch-all" subset. // Construct mapping from face idx to subset idx - VtIntArray faceMap(tessellatedMesh.topology.getNumFaces(), -1); + VtIntArray faceMap(baseMesh.topology.getNumFaces(), -1); for (uint32_t i = 0; i < subsetCount; ++i) { const UsdGeomSubset& geomSubset = geomSubsets[i]; @@ -474,7 +471,7 @@ namespace Falcor } // Unmapped faces are assigned to a separate catchall subset - VtIntArray unassignedIndices = UsdGeomSubset::GetUnassignedIndices(geomSubsets, tessellatedMesh.topology.getNumFaces()); + VtIntArray unassignedIndices = UsdGeomSubset::GetUnassignedIndices(geomSubsets, baseMesh.topology.getNumFaces()); if (unassignedIndices.size() > 0) { for (int idx : unassignedIndices) @@ -921,6 +918,7 @@ namespace Falcor // Convert geom data to keyframe data for the mesh at a particular sample FALCOR_ASSERT(geomData.geomSubsets.size() == mesh.processedMeshes.size()); FALCOR_ASSERT(!mesh.meshIDs.empty()); // Mesh should have been added to builder already + std::vector tempTangents; for (size_t i = 0; i < geomData.geomSubsets.size(); ++i) { // Create mesh to set up data according to subsets @@ -932,7 +930,9 @@ namespace Falcor if (sbMesh.tangents.pData == nullptr) { - ctx.builder.generateTangents(sbMesh, geomData.tangents); + tempTangents.clear(); + ctx.builder.generateTangents(sbMesh, tempTangents); + geomData.tangents.assign(tempTangents.begin(), tempTangents.end()); } auto& indices = mesh.attributeIndices[i]; @@ -1128,20 +1128,8 @@ namespace Falcor } ); - // Gather keyframe data from all meshes - size_t totalMeshes = 0; - for (auto& m : ctx.meshes) totalMeshes += m.cachedMeshes.size(); - std::vector cachedMeshes; - cachedMeshes.reserve(totalMeshes); - for (auto& m : ctx.meshes) - { - for (auto& c : m.cachedMeshes) - { - cachedMeshes.push_back(std::move(c)); - } - } - ctx.builder.setCachedMeshes(std::move(cachedMeshes)); + ctx.builder.addCachedMeshes(std::move(m.cachedMeshes)); } timeReport.measure("Process meshes"); @@ -1295,7 +1283,7 @@ namespace Falcor // Add curve vertex cache (only has positions) to scene builder. for (auto& curve : ctx.curves) ctx.addCachedCurve(curve); - ctx.builder.setCachedCurves(std::move(ctx.cachedCurves)); + ctx.builder.addCachedCurves(std::move(ctx.cachedCurves)); timeReport.measure("Process curves"); @@ -1583,7 +1571,7 @@ namespace Falcor std::lock_guard lock(materialMutex); builder.addMaterial(std::move(pMaterial)); - localDict[materialName] = materialName; + localMaterialToShortName[materialName] = materialName; addMesh(mesh.GetPrim()); addGeomInstance(lightPrim.GetName(), mesh.GetPrim(), float4x4::identity(), float4x4::identity()); @@ -1737,6 +1725,9 @@ namespace Falcor void ImporterContext::addCachedCurve(Curve& curve) { + if (curve.timeSamples.size() <= 1) + return; + CachedCurve cachedCurve; cachedCurve.tessellationMode = curve.tessellationMode; cachedCurve.geometryID = curve.geometryID; @@ -1791,10 +1782,10 @@ namespace Falcor cachedCurves.push_back(cachedCurve); } - ImporterContext::ImporterContext(const std::filesystem::path& stagePath, UsdStageRefPtr pStage, SceneBuilder& builder, const pybind11::dict& dict, TimeReport& timeReport, bool useInstanceProxies /*= false*/) + ImporterContext::ImporterContext(const std::filesystem::path& stagePath, UsdStageRefPtr pStage, SceneBuilder& builder, const std::map& materialToShortName, TimeReport& timeReport, bool useInstanceProxies /*= false*/) : stagePath(stagePath) , pStage(pStage) - , dict(dict) + , materialToShortName(materialToShortName) , timeReport(timeReport) , builder(builder) , useInstanceProxies(useInstanceProxies) @@ -2188,7 +2179,7 @@ namespace Falcor { if (curves.size() >= std::numeric_limits::max()) { - throw RuntimeError("Curve count exceeds the maximum"); + FALCOR_THROW("Curve count exceeds the maximum"); } uint32_t index = (uint32_t)curves.size(); @@ -2301,10 +2292,10 @@ namespace Falcor // Load bone data UsdSkelTopology skelTopo = skelQuery.GetTopology(); - VtArray bindTransform; + VtArray bindTransform; skelQuery.GetJointWorldBindTransforms(&bindTransform); - VtArray restTransform; + VtArray restTransform; skelQuery.ComputeJointLocalTransforms(&restTransform, UsdTimeCode::EarliestTime(), true); for (size_t i = 0; i < joints.size(); i++) @@ -2412,12 +2403,12 @@ namespace Falcor { if (rootXformNodeId != NodeID::Invalid()) { - throw RuntimeError("ImporterContext::setRootXform() - Root xform has already been set."); + FALCOR_THROW("ImporterContext::setRootXform() - Root xform has already been set."); } if (!nodeStack.empty()) { - throw RuntimeError("ImporterContext::setRootXform() - node stack must be empty."); + FALCOR_THROW("ImporterContext::setRootXform() - node stack must be empty."); } rootXform = xform; diff --git a/Source/plugins/importers/USDImporter/ImporterContext.h b/Source/plugins/importers/USDImporter/ImporterContext.h index 5b0fd8cb7..4d5b2b6b3 100644 --- a/Source/plugins/importers/USDImporter/ImporterContext.h +++ b/Source/plugins/importers/USDImporter/ImporterContext.h @@ -27,9 +27,6 @@ **************************************************************************/ #pragma once -#include "Utils.h" -#include "USDHelpers.h" -#include "PreviewSurfaceConverter.h" #include "Scene/SceneIDs.h" #include "Scene/SceneBuilder.h" #include "Scene/Animation/Animation.h" @@ -38,6 +35,11 @@ #include "Utils/Math/Matrix.h" #include "Utils/Timing/TimeReport.h" +#include "USDUtils/USDUtils.h" +#include "USDUtils/USDHelpers.h" +#include "USDUtils/PreviewSurfaceConverter/PreviewSurfaceConverter.h" + + BEGIN_DISABLE_USD_WARNINGS #include #include @@ -47,8 +49,6 @@ BEGIN_DISABLE_USD_WARNINGS #include END_DISABLE_USD_WARNINGS -#include - #include #include #include @@ -236,7 +236,7 @@ namespace Falcor // Importer data and helper functions struct ImporterContext { - ImporterContext(const std::filesystem::path& stagePath, UsdStageRefPtr pStage, SceneBuilder& builder, const pybind11::dict& dict, TimeReport& timeReport, bool useInstanceProxies = false); + ImporterContext(const std::filesystem::path& stagePath, UsdStageRefPtr pStage, SceneBuilder& builder, const std::map& materialToShortName, TimeReport& timeReport, bool useInstanceProxies = false); // Get pointer to default material for the given prim, based on its type, creating it if it doesn't already exist. // Thread-safe. @@ -339,8 +339,8 @@ namespace Falcor std::filesystem::path stagePath; ///< Path of the USD stage being imported. UsdStageRefPtr pStage; ///< USD stage being imported. - const pybind11::dict& dict; ///< Input map from material path to material short name. - std::map localDict; ///< Local input map from material path to + const std::map& materialToShortName; ///< Input map from material path to material short name. + std::map localMaterialToShortName; ///< Local input map from material path to TimeReport& timeReport; ///< Timer object to use when importing. SceneBuilder& builder; ///< Scene builder for this import session. std::vector nodeStack; ///< Stack of SceneBuilder node IDs diff --git a/Source/plugins/importers/USDImporter/USDImporter.cpp b/Source/plugins/importers/USDImporter/USDImporter.cpp index cc697543f..6b96393c6 100644 --- a/Source/plugins/importers/USDImporter/USDImporter.cpp +++ b/Source/plugins/importers/USDImporter/USDImporter.cpp @@ -26,13 +26,14 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **************************************************************************/ #include "USDImporter.h" -#include "USDHelpers.h" #include "ImporterContext.h" #include "Core/Platform/OS.h" #include "Utils/Timing/TimeReport.h" #include "Utils/Settings.h" #include "Scene/Importer.h" +#include "USDUtils/USDHelpers.h" + #include BEGIN_DISABLE_USD_WARNINGS @@ -307,7 +308,7 @@ namespace Falcor return std::make_unique(); } - void USDImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) + void USDImporter::importScene(const std::filesystem::path& path, SceneBuilder& builder, const std::map& materialToShortName) { if (!path.is_absolute()) throw ImporterError(path, "Expected absolute path."); @@ -332,8 +333,11 @@ namespace Falcor timeReport.measure("Open stage"); - Falcor::addDataDirectory(path.parent_path()); - ImporterContext ctx(path, pStage, builder, dict, timeReport); + // Add base directory to search paths. + builder.pushAssetResolver(); + builder.getAssetResolver().addSearchPath(path.parent_path(), SearchPathPriority::First); + + ImporterContext ctx(path, pStage, builder, materialToShortName, timeReport); // Falcor uses meter scene unit; scale if necessary. Note that Omniverse uses cm by default. ctx.metersPerUnit = float(UsdGeomGetStageMetersPerUnit(pStage)); @@ -420,6 +424,8 @@ namespace Falcor } timeReport.printToLog(); + + builder.popAssetResolver(); } extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) diff --git a/Source/plugins/importers/USDImporter/USDImporter.h b/Source/plugins/importers/USDImporter/USDImporter.h index 6b41dd522..069383ddf 100644 --- a/Source/plugins/importers/USDImporter/USDImporter.h +++ b/Source/plugins/importers/USDImporter/USDImporter.h @@ -39,6 +39,6 @@ namespace Falcor static std::unique_ptr create(); - void importScene(const std::filesystem::path& path, SceneBuilder& builder, const pybind11::dict& dict) override; + void importScene(const std::filesystem::path& path, SceneBuilder& builder, const std::map& materialToShortName) override; }; } diff --git a/Source/plugins/importers/USDImporter/Utils.h b/Source/plugins/importers/USDImporter/Utils.h deleted file mode 100644 index c0fef0fc0..000000000 --- a/Source/plugins/importers/USDImporter/Utils.h +++ /dev/null @@ -1,320 +0,0 @@ -/*************************************************************************** - # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: - # * Redistributions of source code must retain the above copyright - # notice, this list of conditions and the following disclaimer. - # * Redistributions in binary form must reproduce the above copyright - # notice, this list of conditions and the following disclaimer in the - # documentation and/or other materials provided with the distribution. - # * Neither the name of NVIDIA CORPORATION nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY - # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - **************************************************************************/ -#pragma once -#include "USDHelpers.h" -#include "Utils/Logger.h" -#include "Utils/Math/Vector.h" -#include "Scene/SceneBuilder.h" - -BEGIN_DISABLE_USD_WARNINGS -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -END_DISABLE_USD_WARNINGS - -#include -#include - -using namespace pxr; - -namespace Falcor -{ - class UsdObjHash - { - public: - size_t operator()(const UsdObject& obj) const - { - return hash_value(obj); - } - }; - - struct Float3Hash - { - size_t operator()(const float3& v) const - { - // Simple hash function that multiplies the integer interpretation of each component by a prime and xors the results - return (*reinterpret_cast(&v.x) * 7727ULL) ^ (*reinterpret_cast(&v.y) * 5521ULL) ^ (*reinterpret_cast(&v.z) * 6971ULL); - } - }; - - inline float3 toFalcor(const GfVec3f& v) - { - return float3(v[0], v[1], v[2]); - } - - inline float3 toFalcor(const GfVec3d& v) - { - return float3(v[0], v[1], v[2]); - } - - inline float3 toFalcor(const GfVec3h& v) - { - return float3(v[0], v[1], v[2]); - } - - inline float4x4 toFalcor(const GfMatrix4d& m) - { - // USD uses row-major matrices and row vectors, which are pre-multiplied (v * M) with a matrix to perform a transformation. - // Falcor uses row-major matrices and column vectors, which are post-multiplied (M * v). - // As such, we transpose USD matrices upon import. - // (Same as for GLM, except we have to write the values actually transposed, rather than using the col-major layout of the constructor) - return float4x4({ - m[0][0], m[1][0], m[2][0], m[3][0], - m[0][1], m[1][1], m[2][1], m[3][1], - m[0][2], m[1][2], m[2][2], m[3][2], - m[0][3], m[1][3], m[2][3], m[3][3] - }); - } - - inline SceneBuilder::Node makeNode(const std::string& name, NodeID parentId = NodeID::Invalid()) - { - return SceneBuilder::Node{name, float4x4::identity(), float4x4::identity(), float4x4::identity(), parentId}; - } - - inline SceneBuilder::Node makeNode(const std::string& name, const float4x4& xform, const float4x4& bindTransform, NodeID parentId = NodeID::Invalid()) - { - return SceneBuilder::Node{name, xform, bindTransform, float4x4::identity(), parentId}; - } - - inline bool getLocalTransform(const UsdGeomXformable& xformable, float4x4& xform) - { - bool resets = false; - GfMatrix4d transform; - xformable.GetLocalTransformation(&transform, &resets, UsdTimeCode::EarliestTime()); - xform = toFalcor(transform); - - return resets; - } - - // Helper function to return an attribute value, if defined, or the specified default value if not - template - inline T getAttribute(const UsdAttribute& attrib, const T& def) - { - T val = def; - if (attrib) - { - attrib.Get(&val, UsdTimeCode::EarliestTime()); - } - return val; - } - - // Helper function that returns the main attribute if it is authored (non-default), - // otherwise it tries to retrieve the fallback attribute and should that fail, the default value. - // This is used to provide compatibility, e.g. when the default disk light radius changed from "radius" to "inputs:radius" - template - inline T getAuthoredAttribute(const UsdAttribute& mainAttrib, const UsdAttribute& fallbackAttrib, const T& def) - { - T val = def; - if (mainAttrib && mainAttrib.IsAuthored()) - { - mainAttrib.Get(&val, UsdTimeCode::EarliestTime()); - } - else if(fallbackAttrib) - { - fallbackAttrib.Get(&val, UsdTimeCode::EarliestTime()); - } - return val; - } - - template - inline T getAttribute(const UsdPrim& prim, const std::string& attribName, const T& def) - { - T val = def; - UsdAttribute attrib = prim.GetAttribute(TfToken(attribName)); - if (attrib.IsValid()) - { - attrib.Get(&val, UsdTimeCode::EarliestTime()); - } - return val; - } - - using AttributeFrequency = SceneBuilder::Mesh::AttributeFrequency; - - inline size_t computeElementCount(AttributeFrequency freq, size_t faceCount, size_t vertexCount) - { - if (freq == AttributeFrequency::Constant) - { - return 1; - } - else if (freq == AttributeFrequency::Uniform) - { - return faceCount; - } - else if (freq == AttributeFrequency::Vertex) - { - return vertexCount; - } - else if (freq == AttributeFrequency::FaceVarying) - { - return 3 * faceCount; - } - else - { - logError("Unsupported primvar interpolation mode {}.", (uint32_t)freq); - return 0; - } - } - - // Compute the count of per-face elements, based on interpolation type - inline size_t computePerFaceElementCount(AttributeFrequency freq, size_t faceCount) - { - if (freq == AttributeFrequency::Uniform) - { - return faceCount; - } - else if (freq == AttributeFrequency::FaceVarying) - { - return 3 * faceCount; - } - // Everything else is indexed (vertex, varying), or constant - return 0; - } - - inline AttributeFrequency convertInterpolation(const TfToken& mode) - { - if (mode == UsdGeomTokens->constant) - { - return AttributeFrequency::Constant; - } - else if (mode == UsdGeomTokens->uniform) - { - return AttributeFrequency::Uniform; - } - else if (mode == UsdGeomTokens->vertex || mode == UsdGeomTokens->varying) - { - // For our purposes, vertex and varying are synonymous. - return AttributeFrequency::Vertex; - } - else if (mode == UsdGeomTokens->faceVarying) - { - return AttributeFrequency::FaceVarying; - } - else - { - logError("Unknown vertex interpolation mode '{}'.", mode.GetString()); - return AttributeFrequency::None; - } - } - - inline TfToken getPurpose(const UsdGeomImageable & prim) - { - TfToken purpose = UsdGeomTokens->default_; - UsdAttribute purposeAttr = prim.GetPurposeAttr(); - if (purposeAttr) - { - purposeAttr.Get(&purpose, UsdTimeCode::EarliestTime()); - } - return purpose; - } - - inline bool isRenderable(const UsdGeomImageable& geomImageable) - { - TfToken purpose = getPurpose(geomImageable); - if (purpose == UsdGeomTokens->guide || purpose == UsdGeomTokens->proxy) - { - return false; - } - UsdAttribute visibilityAttr(geomImageable.GetVisibilityAttr()); - if (visibilityAttr) - { - TfToken visibility; - visibilityAttr.Get(&visibility, UsdTimeCode::EarliestTime()); - if (visibility == UsdGeomTokens->invisible) - { - return false; - } - } - - // Determine the inherited visibility. - // USD documentation warns that ComputeVisiblity() can be inefficient, as it walks up - // towards the root every it is called, rather than caching inherited visibility. - // However, there has yet been no indication that this is a meaningful performance issue in practice. - return geomImageable.ComputeVisibility() != UsdGeomTokens->invisible; - } - - inline bool isTimeSampled(const UsdGeomPointBased& geomPointBased) - { - return geomPointBased.GetPointsAttr().GetNumTimeSamples() > 1; - } - - // Route USD messages through Falcor's logging system - class DiagDelegate : public TfDiagnosticMgr::Delegate - { - public: - - void IssueFatalError(const TfCallContext& context, const std::string& msg) override - { - reportFatalError(msg + " " + context.GetPrettyFunction()); - } - - void IssueError(const TfError& err) override - { - logError(formatMessage(&err)); - } - - void IssueWarning(const TfWarning& warning) override - { - logWarning(formatMessage(&warning)); - } - - void IssueStatus(const TfStatus& status) override - { - logInfo(formatMessage(&status)); - } - - private: - std::string formatMessage(const TfDiagnosticBase* elt) - { - return elt->GetCommentary(); - } - }; - - class ScopeGuard - { - public: - ScopeGuard(const std::function& func) - : m_func(func) - { - } - - ~ScopeGuard() - { - m_func(); - } - private: - std::function m_func; - }; -} diff --git a/build_scripts/generate_stubs.py b/build_scripts/generate_stubs.py index 2eecd1861..8bea3315f 100644 --- a/build_scripts/generate_stubs.py +++ b/build_scripts/generate_stubs.py @@ -1,4 +1,5 @@ import sys +import os from pybind11_stubgen import main @@ -9,3 +10,7 @@ ) package_dir = sys.argv[1] main(["-o", package_dir, "--ignore-invalid=all", "--skip-signature-downgrade", "--no-setup-py", "--root-module-suffix=", "falcor"]) + # pybind11_stubgen doesn't generate aliases for submodules, so we add them manually here + with open(os.path.join(package_dir, "falcor", "__init__.pyi"), "a") as f: + for submodule in ["ui"]: + f.write(f"{submodule} = falcor.falcor_ext.{submodule}\n") diff --git a/dependencies.xml b/dependencies.xml index 78797968b..893d5db43 100644 --- a/dependencies.xml +++ b/dependencies.xml @@ -14,7 +14,7 @@ - + @@ -23,8 +23,8 @@ - - + + @@ -47,7 +47,6 @@ - @@ -60,6 +59,6 @@ - + diff --git a/docs/development/error-handling.md b/docs/development/error-handling.md index 542c2cc53..deca70c00 100644 --- a/docs/development/error-handling.md +++ b/docs/development/error-handling.md @@ -19,6 +19,7 @@ Exceptions is the primary error handling method because it offers several advant * The exception is passed up the stack until the application handles it or the program terminates. Intermediate layers automatically let the error propagate. * The exception stack-unwinding destroys all objects in scope according to well-defined rules. * There is a clean separation between the code that detects the error and the code that handles the error. +* Exceptions can pass the Python API boundary in a well defined way. Note that error reporting is a separate topic from error handling and is discussed further down. All failure conditions that occur must be appropriately detected and handled. @@ -27,8 +28,9 @@ Note that error reporting is a separate topic from error handling and is discuss * Use asserts to check for logic errors in C++ code; errors that should never occur and are a result of programmer mistakes rather than runtime conditions. * Use `static_assert()` to check for logic errors at compile time if possible, for example, type checking of template arguments, or checking struct sizes. * Use `FALCOR_ASSERT()` to check for logic errors at runtime. +* Use `FALCOR_ASSERT_EQ`, `FALCOR_ASSERT_NE`, `FALCOR_ASSERT_GE`, `FALCOR_ASSERT_GT`, `FALCOR_ASSERT_LE`, `FALCOR_ASSERT_LT` to do binary comparison between two values. These assert macros will print messages that not only show the tested condition but also the values that lead to failure. * Use asserts generously. Even trivially correct code might be affected by changes elsewhere. -* Make a habit of running the application in Debug mode regularly to make sure that no asserts trigger. +* Assertions are only enabled in Debug builds by default. Make a habit of running the application in Debug mode regularly to make sure that no asserts trigger. ## Exceptions @@ -39,16 +41,41 @@ Note that error reporting is a separate topic from error handling and is discuss * Throw exceptions by value, catch them by reference. * Do not catch exceptions in user code, other than in rare cases. Falcor's default error handler logs the error and terminates the application, which is often the most reasonable action. -Falcor provides its own set of exception classes. The root for all exceptions is `Exception` (inheriting from `std::exception`). Currently Falcor has three exception types: - -- `RuntimeError` used as a general exception to indicate runtime errors. -- `ArgumentError` used to indicate invalid arguments passed to a function. -- `ImporterError` used to indicate errors when importing assets. - -In addition some helper functions are provided to check for common conditions: - -- `checkInvariant(condition, fmt, ...)` checks that an invariant holds and throws a `RuntimeError` when it is violated. -- `checkArgument(condition, fmt, ...)` checks that a function argument fulfills some condition and throws an `ArgumentError` if it doesn't. +Falcor provides its own exception classes (inherited from `std::exception`) and some utility macros for improved development experience. The main exception class is `Exception`. For simplicity, we try to avoid using many different categories of exceptions. The main exception should always be thrown using the `FALCOR_THROW(fmt, ...)` macro which supports format strings for formatting the error message. This macro has the advantage of being able to break into the debugger if one is attached. This is convenient during development as we automatically break into the debugger before throwning the exception, making it easy to find the code/conditions responsible for the exception. Alternatively one can use the debugger _break on exception_ functionality but this can be cumbersome to use as it breaks on **all** exceptions, both local and external code. The `FALCOR_THROW` macro also logs a stack trace of where the exception is thrown in case no debugger is attached, which helps post-mortem debugging. + +In addition to `FALCOR_THROW` there is also a `FALCOR_CHECK(cond, fmt, ...)` macro which can be used to check conditions, including checking for valid arguments as well as for runtime invariants. The macro is simply defined as: + +```c++ +#define FALCOR_CHECK(cond, fmt, ...) \ + if (!(cond)) \ + FALCOR_THROW(fmt, ##__VA_ARGS__) +``` + +## Assertions vs. Exceptions + +Sometimes the line between using _assertions_ and _exceptions_ can be blurry. In general, _exceptions_ should be used rigorously on all public facing API, that is especially true for functions exposed through the Python API. Internal code can rely more heavily on assertions. Here is a simple example: + +```c++ +// This is the public facing function. +// We throw an exception if the data is not in a valid format. +void processTriplets(std::span data) +{ + FALCOR_CHECK( + data.size() % 3 == 0, + "'data' needs to contain multiples of 3 values but got size={}.", + data.size() + ); + processTripletsInternal(data) +} + +// This is the internal function. +// We assume the data to be in the correct format, but we still check our +// precondition to help finding logical errors during development/refactoring. +void processTripletsInternal(std::span data) +{ + FALCOR_ASSERT(data.size() % 3 == 0); +} +``` ## Return values diff --git a/docs/falcor-in-python.md b/docs/falcor-in-python.md new file mode 100644 index 000000000..092c7dcf6 --- /dev/null +++ b/docs/falcor-in-python.md @@ -0,0 +1,106 @@ +### [Index](./index.md) | Falcor In Python + +-------- + +# Falcor In Python + +## Introduction + +Falcor has supported Python scripting for a long time. However, scripts have always +been executed in the embedded interpreter in form of either _render scripts_ +(creating render graphs, high-level scripting of Mogwai) or through `.pyscene` +files for scene construction. + +Recent versions of Falcor can also be used from Python directly by using the +_Falcor Python extension_. This enables Falcor to leverage the power of the +entire Python ecosystem, combining it with popular frameworks such as +[NumPy](https://numpy.org/) or [PyTorch](https://pytorch.org/). +The current implementation is still fairly limited and cannot access all of +Falcor's functionality, but enough API surface is exposed to enable first +machine learning applications. + +## Python Environment + +To use the _Falcor Python extension_, we need to setup a Python environment. +We suggest to use [Conda](https://docs.conda.io/) for that, because it is +supported on both Windows and Linux and is a popular choice for managing +Python virtual environments (especially on Window). + +To setup the environment, download the latest Python 3.10 version of Miniconda +from https://docs.conda.io/projects/miniconda/en/latest/miniconda-other-installer-links.html + +Note: The Python version is important here. Falcor ships with a prebuilt binary +version of Python that is used during the build. As long as the same version is +used in the Python environment, there should be binary compatibility. If you +want to build Falcor for a different Python version, set the +`FALCOR_USE_SYSTEM_PYTHON` CMake variable to `ON` and make sure CMake can find +the Python binaries to be used (i.e. run CMake configure within a shell that has +the Python environment activated). + +After installation, start the newly installed Miniconda terminal. + +To install a basic Falcor/NumPy/PyTorch environment, you can setup the +`falcor-pytorch` environment from the [environment.yml](/environment.yml) file +in the root directory of this repository: + +``` +conda env create -f environment.yml +``` + +After installation you can _activate_ the `falcor-pytorch` environment using: + +``` +conda activate falcor-pytorch +``` + +To make the _Falcor Python extension_ available in the environment, switch to +the binary output directory of Falcor (i.e. `build/windows-ninja-msvc/bin/Release`) +and run `setpath.bat` (or `setpath.ps1` for PowerShell, `setpath.sh` on Linux). +This will setup the Python and binary paths to allow loading the extension. + +With all that you should be able to successfully load the `falcor` module in +a the Python interpreter: + +``` +>>> import falcor +(Info) Loaded 49 plugin(s) in 0.19s +>>> +``` + +You can also run the [examples](#examples) listed at the end of this document. + +## IDE (Integrated Development Environment) Support + +The Falcor build system generates _Python interface stub files_ for the Python +extension, which contains typing information for all the exported Python bindings. +This typing information enables code completion among other things for much improved +developer experience. + +We suggest to use VS Code for writing Python code, as it is very capable IDE +for both Python and C++ development. In order to have VS Code operate in the +Python environment we set up in the previous section, it is best to start it +from within the Miniconda terminal by running `code`. This will open VS Code +with all the Python/system paths already set up. + +## Examples + +There are a few examples available to illustrate the basics of how to work with +Falcor from Python: + +- [scripts/python/balls/balls.py](/scripts/python/balls/balls.py): +This illustrates how to use compute shaders from Python and render some moving +2D circles to the screen. +- [scripts/python/gaussian2d/gaussian2d.py](/scripts/python/gaussian2d/gaussian2d.py): +This illustrates how to use differentiable Slang for fitting 2d gaussians to +represent a 2d image. Slang is used to implement a differentiable 2d gaussian +renderer, implementing the backwards pass using Slang's auto differentiation. +The example also illustrates PyTorch/CUDA interop by running the optimization +loop from PyTorch. + +## Agility SDK Limitations + +Falcor uses Microsoft Agility SDK to get access to the latest features in D3D12. +To make Agility SDK work within the Falcor Python extension make sure to: +- Enable "Developer Mode" in the "For developers" configuration panel in Windows. +- Make sure the Python interpreter executable is installed on the same physical + drive as the Falcor Python extension. diff --git a/docs/index.md b/docs/index.md index f2f906f81..805ce2ba9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,6 +2,7 @@ - [README](../README.md) - [Getting Started](./getting-started.md) +- [Falcor In Python](./falcor-in-python.md) - [Tutorials](./tutorials/index.md) - [Usage](./usage/index.md) - [Development](./development/index.md) diff --git a/docs/usage/scripting.md b/docs/usage/scripting.md index 143a0b9b2..bdaef6cf9 100644 --- a/docs/usage/scripting.md +++ b/docs/usage/scripting.md @@ -2,6 +2,8 @@ -------- +**Note:** This document is not updated and does not reflect the current state of the Python API anymore. + # Mogwai Scripting Reference ## Sample API diff --git a/environment.yml b/environment.yml new file mode 100644 index 000000000..8f8953593 --- /dev/null +++ b/environment.yml @@ -0,0 +1,14 @@ +name: falcor-pytorch +channels: + - pytorch + - nvidia + - conda-forge +dependencies: + - numpy + - python=3.10 + - pytorch-cuda=11.8 + - torchaudio + - torchvision + - pip + - pip: + - pyexr diff --git a/external/include/BS_thread_pool.hpp b/external/include/BS_thread_pool.hpp new file mode 100644 index 000000000..a60ef3e2e --- /dev/null +++ b/external/include/BS_thread_pool.hpp @@ -0,0 +1,819 @@ +#pragma once + +/** + * @file BS_thread_pool.hpp + * @author Barak Shoshany (baraksh@gmail.com) (http://baraksh.com) + * @version 3.5.0 + * @date 2023-05-25 + * @copyright Copyright (c) 2023 Barak Shoshany. Licensed under the MIT license. If you found this project useful, please consider starring it on GitHub! If you use this library in software of any kind, please provide a link to the GitHub repository https://github.com/bshoshany/thread-pool in the source code and documentation. If you use this library in published research, please cite it as follows: Barak Shoshany, "A C++17 Thread Pool for High-Performance Scientific Computing", doi:10.5281/zenodo.4742687, arXiv:2105.00613 (May 2021) + * + * @brief BS::thread_pool: a fast, lightweight, and easy-to-use C++17 thread pool library. This header file contains the entire library, including the main BS::thread_pool class and the helper classes BS::multi_future, BS::blocks, BS:synced_stream, and BS::timer. + */ + +#define BS_THREAD_POOL_VERSION "v3.5.0 (2023-05-25)" + +#include // std::chrono +#include // std::condition_variable +#include // std::current_exception +#include // std::bind, std::function, std::invoke +#include // std::future, std::promise +#include // std::cout, std::endl, std::flush, std::ostream +#include // std::make_shared, std::make_unique, std::shared_ptr, std::unique_ptr +#include // std::mutex, std::scoped_lock, std::unique_lock +#include // std::queue +#include // std::thread +#include // std::common_type_t, std::conditional_t, std::decay_t, std::invoke_result_t, std::is_void_v +#include // std::forward, std::move, std::swap +#include // std::vector + +namespace BS +{ +/** + * @brief A convenient shorthand for the type of std::thread::hardware_concurrency(). Should evaluate to unsigned int. + */ +using concurrency_t = std::invoke_result_t; + +// ============================================================================================= // +// Begin class multi_future // + +/** + * @brief A helper class to facilitate waiting for and/or getting the results of multiple futures at once. + * + * @tparam T The return type of the futures. + */ +template +class [[nodiscard]] multi_future +{ +public: + /** + * @brief Construct a multi_future object with the given number of futures. + * + * @param num_futures_ The desired number of futures to store. + */ + multi_future(const size_t num_futures_ = 0) : futures(num_futures_) {} + + /** + * @brief Get the results from all the futures stored in this multi_future object, rethrowing any stored exceptions. + * + * @return If the futures return void, this function returns void as well. Otherwise, it returns a vector containing the results. + */ + [[nodiscard]] std::conditional_t, void, std::vector> get() + { + if constexpr (std::is_void_v) + { + for (size_t i = 0; i < futures.size(); ++i) + futures[i].get(); + return; + } + else + { + std::vector results(futures.size()); + for (size_t i = 0; i < futures.size(); ++i) + results[i] = futures[i].get(); + return results; + } + } + + /** + * @brief Get a reference to one of the futures stored in this multi_future object. + * + * @param i The index of the desired future. + * @return The future. + */ + [[nodiscard]] std::future& operator[](const size_t i) + { + return futures[i]; + } + + /** + * @brief Append a future to this multi_future object. + * + * @param future The future to append. + */ + void push_back(std::future future) + { + futures.push_back(std::move(future)); + } + + /** + * @brief Get the number of futures stored in this multi_future object. + * + * @return The number of futures. + */ + [[nodiscard]] size_t size() const + { + return futures.size(); + } + + /** + * @brief Wait for all the futures stored in this multi_future object. + */ + void wait() const + { + for (size_t i = 0; i < futures.size(); ++i) + futures[i].wait(); + } + +private: + /** + * @brief A vector to store the futures. + */ + std::vector> futures; +}; + +// End class multi_future // +// ============================================================================================= // + +// ============================================================================================= // +// Begin class blocks // + +/** + * @brief A helper class to divide a range into blocks. Used by parallelize_loop() and push_loop(). + * + * @tparam T1 The type of the first index in the range. Should be a signed or unsigned integer. + * @tparam T2 The type of the index after the last index in the range. Should be a signed or unsigned integer. If T1 is not the same as T2, a common type will be automatically inferred. + * @tparam T The common type of T1 and T2. + */ +template > +class [[nodiscard]] blocks +{ +public: + /** + * @brief Construct a blocks object with the given specifications. + * + * @param first_index_ The first index in the range. + * @param index_after_last_ The index after the last index in the range. + * @param num_blocks_ The desired number of blocks to divide the range into. + */ + blocks(const T1 first_index_, const T2 index_after_last_, const size_t num_blocks_) : first_index(static_cast(first_index_)), index_after_last(static_cast(index_after_last_)), num_blocks(num_blocks_) + { + if (index_after_last < first_index) + std::swap(index_after_last, first_index); + total_size = static_cast(index_after_last - first_index); + block_size = static_cast(total_size / num_blocks); + if (block_size == 0) + { + block_size = 1; + num_blocks = (total_size > 1) ? total_size : 1; + } + } + + /** + * @brief Get the first index of a block. + * + * @param i The block number. + * @return The first index. + */ + [[nodiscard]] T start(const size_t i) const + { + return static_cast(i * block_size) + first_index; + } + + /** + * @brief Get the index after the last index of a block. + * + * @param i The block number. + * @return The index after the last index. + */ + [[nodiscard]] T end(const size_t i) const + { + return (i == num_blocks - 1) ? index_after_last : (static_cast((i + 1) * block_size) + first_index); + } + + /** + * @brief Get the number of blocks. Note that this may be different than the desired number of blocks that was passed to the constructor. + * + * @return The number of blocks. + */ + [[nodiscard]] size_t get_num_blocks() const + { + return num_blocks; + } + + /** + * @brief Get the total number of indices in the range. + * + * @return The total number of indices. + */ + [[nodiscard]] size_t get_total_size() const + { + return total_size; + } + +private: + /** + * @brief The size of each block (except possibly the last block). + */ + size_t block_size = 0; + + /** + * @brief The first index in the range. + */ + T first_index = 0; + + /** + * @brief The index after the last index in the range. + */ + T index_after_last = 0; + + /** + * @brief The number of blocks. + */ + size_t num_blocks = 0; + + /** + * @brief The total number of indices in the range. + */ + size_t total_size = 0; +}; + +// End class blocks // +// ============================================================================================= // + +// ============================================================================================= // +// Begin class thread_pool // + +/** + * @brief A fast, lightweight, and easy-to-use C++17 thread pool class. + */ +class [[nodiscard]] thread_pool +{ +public: + // ============================ + // Constructors and destructors + // ============================ + + /** + * @brief Construct a new thread pool. + * + * @param thread_count_ The number of threads to use. The default value is the total number of hardware threads available, as reported by the implementation. This is usually determined by the number of cores in the CPU. If a core is hyperthreaded, it will count as two threads. + */ + thread_pool(const concurrency_t thread_count_ = 0) : thread_count(determine_thread_count(thread_count_)), threads(std::make_unique(determine_thread_count(thread_count_))) + { + create_threads(); + } + + /** + * @brief Destruct the thread pool. Waits for all tasks to complete, then destroys all threads. Note that if the pool is paused, then any tasks still in the queue will never be executed. + */ + ~thread_pool() + { + wait_for_tasks(); + destroy_threads(); + } + + // ======================= + // Public member functions + // ======================= + + /** + * @brief Get the number of tasks currently waiting in the queue to be executed by the threads. + * + * @return The number of queued tasks. + */ + [[nodiscard]] size_t get_tasks_queued() const + { + const std::scoped_lock tasks_lock(tasks_mutex); + return tasks.size(); + } + + /** + * @brief Get the number of tasks currently being executed by the threads. + * + * @return The number of running tasks. + */ + [[nodiscard]] size_t get_tasks_running() const + { + const std::scoped_lock tasks_lock(tasks_mutex); + return tasks_running; + } + + /** + * @brief Get the total number of unfinished tasks: either still waiting in the queue, or running in a thread. Note that get_tasks_total() == get_tasks_queued() + get_tasks_running(). + * + * @return The total number of tasks. + */ + [[nodiscard]] size_t get_tasks_total() const + { + const std::scoped_lock tasks_lock(tasks_mutex); + return tasks_running + tasks.size(); + } + + /** + * @brief Get the number of threads in the pool. + * + * @return The number of threads. + */ + [[nodiscard]] concurrency_t get_thread_count() const + { + return thread_count; + } + + /** + * @brief Check whether the pool is currently paused. + * + * @return true if the pool is paused, false if it is not paused. + */ + [[nodiscard]] bool is_paused() const + { + const std::scoped_lock tasks_lock(tasks_mutex); + return paused; + } + + /** + * @brief Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue. Returns a multi_future object that contains the futures for all of the blocks. + * + * @tparam F The type of the function to loop through. + * @tparam T1 The type of the first index in the loop. Should be a signed or unsigned integer. + * @tparam T2 The type of the index after the last index in the loop. Should be a signed or unsigned integer. If T1 is not the same as T2, a common type will be automatically inferred. + * @tparam T The common type of T1 and T2. + * @tparam R The return value of the loop function F (can be void). + * @param first_index The first index in the loop. + * @param index_after_last The index after the last index in the loop. The loop will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = first_index; i < index_after_last; ++i)". Note that if index_after_last == first_index, no blocks will be submitted. + * @param loop The function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; ++i)". + * @param num_blocks The maximum number of blocks to split the loop into. The default is to use the number of threads in the pool. + * @return A multi_future object that can be used to wait for all the blocks to finish. If the loop function returns a value, the multi_future object can also be used to obtain the values returned by each block. + */ + template , typename R = std::invoke_result_t, T, T>, std::enable_if_t, T, T>, bool> = true> + [[nodiscard]] multi_future parallelize_loop(const T1 first_index, const T2 index_after_last, F&& loop, const size_t num_blocks = 0) + { + blocks blks(first_index, index_after_last, num_blocks ? num_blocks : thread_count); + if (blks.get_total_size() > 0) + { + multi_future mf(blks.get_num_blocks()); + for (size_t i = 0; i < blks.get_num_blocks(); ++i) + mf[i] = submit(std::forward(loop), blks.start(i), blks.end(i)); + return mf; + } + else + { + return multi_future(); + } + } + + /** + * @brief Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue. Returns a multi_future object that contains the futures for all of the blocks. This overload is used for the special case where the first index is 0. + * + * @tparam F The type of the function to loop through. + * @tparam T The type of the loop indices. Should be a signed or unsigned integer. + * @tparam R The return value of the loop function F (can be void). + * @param index_after_last The index after the last index in the loop. The loop will iterate from 0 to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = 0; i < index_after_last; ++i)". Note that if index_after_last == 0, no blocks will be submitted. + * @param loop The function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; ++i)". + * @param num_blocks The maximum number of blocks to split the loop into. The default is to use the number of threads in the pool. + * @return A multi_future object that can be used to wait for all the blocks to finish. If the loop function returns a value, the multi_future object can also be used to obtain the values returned by each block. + */ + template , T, T>> + [[nodiscard]] multi_future parallelize_loop(const T index_after_last, F&& loop, const size_t num_blocks = 0) + { + return parallelize_loop(0, index_after_last, std::forward(loop), num_blocks); + } + + /** + * @brief Pause the pool. The workers will temporarily stop retrieving new tasks out of the queue, although any tasks already executed will keep running until they are finished. + */ + void pause() + { + const std::scoped_lock tasks_lock(tasks_mutex); + paused = true; + } + + /** + * @brief Purge all the tasks waiting in the queue. Tasks that are currently running will not be affected, but any tasks still waiting in the queue will be discarded, and will never be executed by the threads. Please note that there is no way to restore the purged tasks. + */ + void purge() + { + const std::scoped_lock tasks_lock(tasks_mutex); + while (!tasks.empty()) + tasks.pop(); + } + + /** + * @brief Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue. Does not return a multi_future, so the user must use wait_for_tasks() or some other method to ensure that the loop finishes executing, otherwise bad things will happen. + * + * @tparam F The type of the function to loop through. + * @tparam T1 The type of the first index in the loop. Should be a signed or unsigned integer. + * @tparam T2 The type of the index after the last index in the loop. Should be a signed or unsigned integer. If T1 is not the same as T2, a common type will be automatically inferred. + * @tparam T The common type of T1 and T2. + * @param first_index The first index in the loop. + * @param index_after_last The index after the last index in the loop. The loop will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = first_index; i < index_after_last; ++i)". Note that if index_after_last == first_index, no blocks will be submitted. + * @param loop The function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; ++i)". + * @param num_blocks The maximum number of blocks to split the loop into. The default is to use the number of threads in the pool. + */ + template > + void push_loop(const T1 first_index, const T2 index_after_last, F&& loop, const size_t num_blocks = 0) + { + blocks blks(first_index, index_after_last, num_blocks ? num_blocks : thread_count); + if (blks.get_total_size() > 0) + { + for (size_t i = 0; i < blks.get_num_blocks(); ++i) + push_task(std::forward(loop), blks.start(i), blks.end(i)); + } + } + + /** + * @brief Parallelize a loop by automatically splitting it into blocks and submitting each block separately to the queue. Does not return a multi_future, so the user must use wait_for_tasks() or some other method to ensure that the loop finishes executing, otherwise bad things will happen. This overload is used for the special case where the first index is 0. + * + * @tparam F The type of the function to loop through. + * @tparam T The type of the loop indices. Should be a signed or unsigned integer. + * @param index_after_last The index after the last index in the loop. The loop will iterate from 0 to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = 0; i < index_after_last; ++i)". Note that if index_after_last == 0, no blocks will be submitted. + * @param loop The function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; ++i)". + * @param num_blocks The maximum number of blocks to split the loop into. The default is to use the number of threads in the pool. + */ + template + void push_loop(const T index_after_last, F&& loop, const size_t num_blocks = 0) + { + push_loop(0, index_after_last, std::forward(loop), num_blocks); + } + + /** + * @brief Push a function with zero or more arguments, but no return value, into the task queue. Does not return a future, so the user must use wait_for_tasks() or some other method to ensure that the task finishes executing, otherwise bad things will happen. + * + * @tparam F The type of the function. + * @tparam A The types of the arguments. + * @param task The function to push. + * @param args The zero or more arguments to pass to the function. Note that if the task is a class member function, the first argument must be a pointer to the object, i.e. &object (or this), followed by the actual arguments. + */ + template + void push_task(F&& task, A&&... args) + { + { + const std::scoped_lock tasks_lock(tasks_mutex); + tasks.push(std::bind(std::forward(task), std::forward(args)...)); // cppcheck-suppress ignoredReturnValue + } + task_available_cv.notify_one(); + } + + /** + * @brief Reset the number of threads in the pool. Waits for all currently running tasks to be completed, then destroys all threads in the pool and creates a new thread pool with the new number of threads. Any tasks that were waiting in the queue before the pool was reset will then be executed by the new threads. If the pool was paused before resetting it, the new pool will be paused as well. + * + * @param thread_count_ The number of threads to use. The default value is the total number of hardware threads available, as reported by the implementation. This is usually determined by the number of cores in the CPU. If a core is hyperthreaded, it will count as two threads. + */ + void reset(const concurrency_t thread_count_ = 0) + { + std::unique_lock tasks_lock(tasks_mutex); + const bool was_paused = paused; + paused = true; + tasks_lock.unlock(); + wait_for_tasks(); + destroy_threads(); + thread_count = determine_thread_count(thread_count_); + threads = std::make_unique(thread_count); + paused = was_paused; + create_threads(); + } + + /** + * @brief Submit a function with zero or more arguments into the task queue. If the function has a return value, get a future for the eventual returned value. If the function has no return value, get an std::future which can be used to wait until the task finishes. + * + * @tparam F The type of the function. + * @tparam A The types of the zero or more arguments to pass to the function. + * @tparam R The return type of the function (can be void). + * @param task The function to submit. + * @param args The zero or more arguments to pass to the function. Note that if the task is a class member function, the first argument must be a pointer to the object, i.e. &object (or this), followed by the actual arguments. + * @return A future to be used later to wait for the function to finish executing and/or obtain its returned value if it has one. + */ + template , std::decay_t...>> + [[nodiscard]] std::future submit(F&& task, A&&... args) + { + std::shared_ptr> task_promise = std::make_shared>(); + push_task( + [task_function = std::bind(std::forward(task), std::forward(args)...), task_promise] + { + try + { + if constexpr (std::is_void_v) + { + std::invoke(task_function); + task_promise->set_value(); + } + else + { + task_promise->set_value(std::invoke(task_function)); + } + } + catch (...) + { + try + { + task_promise->set_exception(std::current_exception()); + } + catch (...) + { + } + } + }); + return task_promise->get_future(); + } + + /** + * @brief Unpause the pool. The workers will resume retrieving new tasks out of the queue. + */ + void unpause() + { + const std::scoped_lock tasks_lock(tasks_mutex); + paused = false; + } + + /** + * @brief Wait for tasks to be completed. Normally, this function waits for all tasks, both those that are currently running in the threads and those that are still waiting in the queue. However, if the pool is paused, this function only waits for the currently running tasks (otherwise it would wait forever). Note: To wait for just one specific task, use submit() instead, and call the wait() member function of the generated future. + */ + void wait_for_tasks() + { + std::unique_lock tasks_lock(tasks_mutex); + waiting = true; + tasks_done_cv.wait(tasks_lock, [this] { return !tasks_running && (paused || tasks.empty()); }); + waiting = false; + } + + /** + * @brief Wait for tasks to be completed, but stop waiting after the specified duration has passed. + * + * @tparam R An arithmetic type representing the number of ticks to wait. + * @tparam P An std::ratio representing the length of each tick in seconds. + * @param duration The time duration to wait. + * @return true if all tasks finished running, false if the duration expired but some tasks are still running. + */ + template + bool wait_for_tasks_duration(const std::chrono::duration& duration) + { + std::unique_lock tasks_lock(tasks_mutex); + waiting = true; + const bool status = tasks_done_cv.wait_for(tasks_lock, duration, [this] { return !tasks_running && (paused || tasks.empty()); }); + waiting = false; + return status; + } + + /** + * @brief Wait for tasks to be completed, but stop waiting after the specified time point has been reached. + * + * @tparam C The type of the clock used to measure time. + * @tparam D An std::chrono::duration type used to indicate the time point. + * @param timeout_time The time point at which to stop waiting. + * @return true if all tasks finished running, false if the time point was reached but some tasks are still running. + */ + template + bool wait_for_tasks_until(const std::chrono::time_point& timeout_time) + { + std::unique_lock tasks_lock(tasks_mutex); + waiting = true; + const bool status = tasks_done_cv.wait_until(tasks_lock, timeout_time, [this] { return !tasks_running && (paused || tasks.empty()); }); + waiting = false; + return status; + } + +private: + // ======================== + // Private member functions + // ======================== + + /** + * @brief Create the threads in the pool and assign a worker to each thread. + */ + void create_threads() + { + { + const std::scoped_lock tasks_lock(tasks_mutex); + workers_running = true; + } + for (concurrency_t i = 0; i < thread_count; ++i) + { + threads[i] = std::thread(&thread_pool::worker, this); + } + } + + /** + * @brief Destroy the threads in the pool. + */ + void destroy_threads() + { + { + const std::scoped_lock tasks_lock(tasks_mutex); + workers_running = false; + } + task_available_cv.notify_all(); + for (concurrency_t i = 0; i < thread_count; ++i) + { + threads[i].join(); + } + } + + /** + * @brief Determine how many threads the pool should have, based on the parameter passed to the constructor or reset(). + * + * @param thread_count_ The parameter passed to the constructor or reset(). If the parameter is a positive number, then the pool will be created with this number of threads. If the parameter is non-positive, or a parameter was not supplied (in which case it will have the default value of 0), then the pool will be created with the total number of hardware threads available, as obtained from std::thread::hardware_concurrency(). If the latter returns a non-positive number for some reason, then the pool will be created with just one thread. + * @return The number of threads to use for constructing the pool. + */ + [[nodiscard]] concurrency_t determine_thread_count(const concurrency_t thread_count_) const + { + if (thread_count_ > 0) + return thread_count_; + else + { + if (std::thread::hardware_concurrency() > 0) + return std::thread::hardware_concurrency(); + else + return 1; + } + } + + /** + * @brief A worker function to be assigned to each thread in the pool. Waits until it is notified by push_task() that a task is available, and then retrieves the task from the queue and executes it. Once the task finishes, the worker notifies wait_for_tasks() in case it is waiting. + */ + void worker() + { + std::function task; + while (true) + { + std::unique_lock tasks_lock(tasks_mutex); + task_available_cv.wait(tasks_lock, [this] { return !tasks.empty() || !workers_running; }); + if (!workers_running) + break; + if (paused) + continue; + task = std::move(tasks.front()); + tasks.pop(); + ++tasks_running; + tasks_lock.unlock(); + task(); + tasks_lock.lock(); + --tasks_running; + if (waiting && !tasks_running && (paused || tasks.empty())) + tasks_done_cv.notify_all(); + } + } + + // ============ + // Private data + // ============ + + /** + * @brief A flag indicating whether the workers should pause. When set to true, the workers temporarily stop retrieving new tasks out of the queue, although any tasks already executed will keep running until they are finished. When set to false again, the workers resume retrieving tasks. + */ + bool paused = false; + + /** + * @brief A condition variable to notify worker() that a new task has become available. + */ + std::condition_variable task_available_cv = {}; + + /** + * @brief A condition variable to notify wait_for_tasks() that the tasks are done. + */ + std::condition_variable tasks_done_cv = {}; + + /** + * @brief A queue of tasks to be executed by the threads. + */ + std::queue> tasks = {}; + + /** + * @brief A counter for the total number of currently running tasks. + */ + size_t tasks_running = 0; + + /** + * @brief A mutex to synchronize access to the task queue by different threads. + */ + mutable std::mutex tasks_mutex = {}; + + /** + * @brief The number of threads in the pool. + */ + concurrency_t thread_count = 0; + + /** + * @brief A smart pointer to manage the memory allocated for the threads. + */ + std::unique_ptr threads = nullptr; + + /** + * @brief A flag indicating that wait_for_tasks() is active and expects to be notified whenever a task is done. + */ + bool waiting = false; + + /** + * @brief A flag indicating to the workers to keep running. When set to false, the workers terminate permanently. + */ + bool workers_running = false; +}; + +// End class thread_pool // +// ============================================================================================= // + +// ============================================================================================= // +// Begin class synced_stream // + +/** + * @brief A helper class to synchronize printing to an output stream by different threads. + */ +class [[nodiscard]] synced_stream +{ +public: + /** + * @brief Construct a new synced stream. + * + * @param out_stream_ The output stream to print to. The default value is std::cout. + */ + synced_stream(std::ostream& out_stream_ = std::cout) : out_stream(out_stream_) {} + + /** + * @brief Print any number of items into the output stream. Ensures that no other threads print to this stream simultaneously, as long as they all exclusively use the same synced_stream object to print. + * + * @tparam T The types of the items + * @param items The items to print. + */ + template + void print(T&&... items) + { + const std::scoped_lock lock(stream_mutex); + (out_stream << ... << std::forward(items)); + } + + /** + * @brief Print any number of items into the output stream, followed by a newline character. Ensures that no other threads print to this stream simultaneously, as long as they all exclusively use the same synced_stream object to print. + * + * @tparam T The types of the items + * @param items The items to print. + */ + template + void println(T&&... items) + { + print(std::forward(items)..., '\n'); + } + + /** + * @brief A stream manipulator to pass to a synced_stream (an explicit cast of std::endl). Prints a newline character to the stream, and then flushes it. Should only be used if flushing is desired, otherwise '\n' should be used instead. + */ + inline static std::ostream& (&endl)(std::ostream&) = static_cast(std::endl); + + /** + * @brief A stream manipulator to pass to a synced_stream (an explicit cast of std::flush). Used to flush the stream. + */ + inline static std::ostream& (&flush)(std::ostream&) = static_cast(std::flush); + +private: + /** + * @brief The output stream to print to. + */ + std::ostream& out_stream; + + /** + * @brief A mutex to synchronize printing. + */ + mutable std::mutex stream_mutex = {}; +}; + +// End class synced_stream // +// ============================================================================================= // + +// ============================================================================================= // +// Begin class timer // + +/** + * @brief A helper class to measure execution time for benchmarking purposes. + */ +class [[nodiscard]] timer +{ +public: + /** + * @brief Start (or restart) measuring time. + */ + void start() + { + start_time = std::chrono::steady_clock::now(); + } + + /** + * @brief Stop measuring time and store the elapsed time since start(). + */ + void stop() + { + elapsed_time = std::chrono::steady_clock::now() - start_time; + } + + /** + * @brief Get the number of milliseconds that have elapsed between start() and stop(). + * + * @return The number of milliseconds. + */ + [[nodiscard]] std::chrono::milliseconds::rep ms() const + { + return (std::chrono::duration_cast(elapsed_time)).count(); + } + +private: + /** + * @brief The time point when measuring started. + */ + std::chrono::time_point start_time = std::chrono::steady_clock::now(); + + /** + * @brief The duration that has elapsed between start() and stop(). + */ + std::chrono::duration elapsed_time = std::chrono::duration::zero(); +}; + +// End class timer // +// ============================================================================================= // + +} // namespace BS diff --git a/external/include/fstd/source_location.h b/external/include/fstd/source_location.h new file mode 100644 index 000000000..475508c2d --- /dev/null +++ b/external/include/fstd/source_location.h @@ -0,0 +1,69 @@ +#pragma once + +#include + +namespace fstd +{ +struct source_location +{ +public: +#if defined(__clang__) && (__clang_major__ >= 9) + static constexpr source_location current( + const char* fileName = __builtin_FILE(), + const char* functionName = __builtin_FUNCTION(), + const uint_least32_t lineNumber = __builtin_LINE(), + const uint_least32_t columnOffset = __builtin_COLUMN() + ) noexcept +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + static constexpr source_location current( + const char* fileName = __builtin_FILE(), + const char* functionName = __builtin_FUNCTION(), + const uint_least32_t lineNumber = __builtin_LINE(), + const uint_least32_t columnOffset = 0 + ) noexcept +#elif defined(_MSC_VER) && (_MSC_VER >= 1927) + static constexpr source_location current( + const char* fileName = __builtin_FILE(), + const char* functionName = __builtin_FUNCTION(), + const uint_least32_t lineNumber = __builtin_LINE(), + const uint_least32_t columnOffset = __builtin_COLUMN() + ) noexcept +#else + static constexpr source_location current( + const char* fileName = "unsupported", + const char* functionName = "unsupported", + const uint_least32_t lineNumber = 0, + const uint_least32_t columnOffset = 0 + ) noexcept +#endif + { + return source_location(fileName, functionName, lineNumber, columnOffset); + } + + source_location(const source_location&) = default; + source_location(source_location&&) = default; + + constexpr const char* file_name() const noexcept { return fileName; } + + constexpr const char* function_name() const noexcept { return functionName; } + + constexpr uint_least32_t line() const noexcept { return lineNumber; } + + constexpr std::uint_least32_t column() const noexcept { return columnOffset; } + +private: + constexpr source_location( + const char* fileName, + const char* functionName, + const uint_least32_t lineNumber, + const uint_least32_t columnOffset + ) noexcept + : fileName(fileName), functionName(functionName), lineNumber(lineNumber), columnOffset(columnOffset) + {} + + const char* fileName; + const char* functionName; + const std::uint_least32_t lineNumber; + const std::uint_least32_t columnOffset; +}; +} // namespace fstd diff --git a/scripts/inv-rendering/bsdf_optimization/run_bsdf_optimizer.py b/scripts/inv-rendering/bsdf_optimization/run_bsdf_optimizer.py new file mode 100644 index 000000000..7ef112f80 --- /dev/null +++ b/scripts/inv-rendering/bsdf_optimization/run_bsdf_optimizer.py @@ -0,0 +1,170 @@ +import torch +import falcor + + +class BSDFFunction(torch.autograd.Function): + @staticmethod + def forward(ctx, x, context): + ctx.context = context + device: falcor.Device = context["device"] + scene: falcor.Scene = context["scene"] + material_ids_buffer: falcor.Buffer = context["material_ids_buffer"] + material_params_buffer: falcor.Buffer = context["material_params_buffer"] + + # Set material parameters + material_params_buffer.from_torch(x.detach()) + device.render_context.wait_for_cuda() + scene.set_material_params(material_ids_buffer, material_params_buffer) + device.render_context.wait_for_falcor() + + # Compute loss + params_ref = context["params_ref"] + diff = x - params_ref + loss = torch.abs(diff).sum() + return loss + + @staticmethod + def backward(ctx, grad_output): + context = ctx.context + device: falcor.Device = context["device"] + bsdf_optimizer: falcor.BSDFOptimizer = context["bsdf_optimizer"] + + grad_buffer = bsdf_optimizer.compute_bsdf_grads() + device.render_context.wait_for_falcor() + grad = grad_buffer.to_torch([falcor.Material.PARAM_COUNT], falcor.float32) + + return (grad, None) + + +class BSDFEvalModule(torch.nn.Module): + def __init__( + self, + device: falcor.Device, + scene: falcor.Scene, + bsdf_optimizer: falcor.BSDFOptimizer, + material_ids_buffer: falcor.Buffer, + material_params_buffer: falcor.Buffer, + params_ref, + ): + super(BSDFEvalModule, self).__init__() + self.context = dict( + device=device, + scene=scene, + bsdf_optimizer=bsdf_optimizer, + material_ids_buffer=material_ids_buffer, + material_params_buffer=material_params_buffer, + params_ref=params_ref, + ) + + def forward(self, x): + return BSDFFunction.apply(x, self.context) + + +def main(): + # Create torch CUDA device + print("Creating CUDA device") + if not torch.cuda.is_available(): + raise RuntimeError("CUDA not available") + torch_device = torch.device("cuda:0") + print(torch_device) + torch.set_default_tensor_type(torch.cuda.FloatTensor) + + # Create testbed + testbed = falcor.Testbed() + device = testbed.device + render_graph = testbed.create_render_graph("BSDFOptimizer") + # Set material IDs here + init_material_id: falcor.ObjectID = 0 + ref_material_id: falcor.ObjectID = 1 + bsdf_optimizer: falcor.BSDFOptimizer = render_graph.create_pass( + "bsdf_optimizer", + "BSDFOptimizer", + {"initMaterialID": init_material_id, "refMaterialID": ref_material_id}, + ) + testbed.render_graph = render_graph + testbed.load_scene("test_scenes/bsdf_optimizer.pyscene") + scene = testbed.scene + + material_ids_buffer = device.create_structured_buffer( + struct_size=4, + element_count=1, + bind_flags=falcor.ResourceBindFlags.ShaderResource + | falcor.ResourceBindFlags.UnorderedAccess + | falcor.ResourceBindFlags.Shared, + ) + material_params_buffer = device.create_structured_buffer( + struct_size=4, + element_count=falcor.Material.PARAM_COUNT, + bind_flags=falcor.ResourceBindFlags.ShaderResource + | falcor.ResourceBindFlags.UnorderedAccess + | falcor.ResourceBindFlags.Shared, + ) + + init_material_id = bsdf_optimizer.init_material_id + ref_material_id = bsdf_optimizer.ref_material_id + + def read_material_params(material_id : falcor.ObjectID): + # Set up `material_ids_buffer` + material_ids = torch.tensor([material_id], dtype=torch.int32) + material_ids_buffer.from_torch(material_ids) + device.render_context.wait_for_cuda() + + scene.get_material_params(material_ids_buffer, material_params_buffer) + device.render_context.wait_for_falcor() + + # Fetch parameters from `material_params_buffer` + params = torch.tensor([0.0] * falcor.Material.PARAM_COUNT) + material_params_buffer.copy_to_torch(params) + + device.render_context.wait_for_cuda() + + return params + + # Get material parameters + params_ref = read_material_params(ref_material_id) + params_init = read_material_params(init_material_id) + + print("Material type:", scene.get_material(ref_material_id).type) + print("Initial material params:", params_init) + print("Reference material params:", params_ref) + + # Create optimizer + params_init.requires_grad_() + optimizer = torch.optim.Adam( + [ + {"params": params_init, "lr": 1e-2}, + ], + eps=1e-6, + ) + + # Workaround to fix "If capturable=False, state_steps should not be CUDA tensors" + for param_group in optimizer.param_groups: + param_group["capturable"] = True + + bsdf_optimizer.bsdf_slice_resolution = 256 + bsdf_eval = BSDFEvalModule( + device=device, + scene=scene, + bsdf_optimizer=bsdf_optimizer, + material_ids_buffer=material_ids_buffer, + material_params_buffer=material_params_buffer, + params_ref=params_ref, + ) + + # Optimization + iter_count = 200 + for i in range(iter_count): + optimizer.zero_grad() + loss = bsdf_eval(params_init) + loss.backward() + + optimizer.step() + + if i % 10 == 0 or i == iter_count - 1: + print("Iter {:d}: loss = {:f}".format(i, loss.item())) + + print("Optimized material params:", params_init) + + +if __name__ == "__main__": + main() diff --git a/scripts/inv-rendering/common.py b/scripts/inv-rendering/common.py new file mode 100644 index 000000000..6d4b8ddc5 --- /dev/null +++ b/scripts/inv-rendering/common.py @@ -0,0 +1,106 @@ +import os +import sys +import argparse +import signal +import sys +from glob import glob +from pathlib import Path +import torch +import falcor +import numpy as np +import pyexr as exr +import json +from PIL import Image +import io + + +def load_scene(testbed: falcor.Testbed, scene_path: Path, aspect_ratio=1.0): + flags = ( + falcor.SceneBuilderFlags.DontMergeMaterials + | falcor.SceneBuilderFlags.RTDontMergeDynamic + | falcor.SceneBuilderFlags.DontOptimizeMaterials + ) + testbed.load_scene(scene_path, flags) + testbed.scene.camera.aspectRatio = aspect_ratio + testbed.scene.renderSettings.useAnalyticLights = False + testbed.scene.renderSettings.useEnvLight = False + return testbed.scene + + +def create_testbed(reso: (int, int)): + device_id = 0 + testbed = falcor.Testbed( + width=reso[0], height=reso[1], create_window=True, gpu=device_id + ) + testbed.show_ui = False + testbed.clock.time = 0 + testbed.clock.pause() + return testbed + + +def create_passes(testbed: falcor.Testbed, max_bounces: int, use_war: bool): + # Rendering graph of the WAR differentiable path tracer. + render_graph = testbed.create_render_graph("WARDiffPathTracer") + primal_accumulate_pass = render_graph.create_pass( + "PrimalAccumulatePass", + "AccumulatePass", + {"enabled": True, "precisionMode": "Single"}, + ) + grad_accumulate_pass = render_graph.create_pass( + "GradAccumulatePass", + "AccumulatePass", + {"enabled": True, "precisionMode": "Single"}, + ) + war_diff_pt_pass = render_graph.create_pass( + "WARDiffPathTracer", + "WARDiffPathTracer", + { + "samplesPerPixel": 1, + "maxBounces": max_bounces, + "diffMode": "BackwardDiff", + "useWAR": use_war, + }, + ) + render_graph.add_edge("WARDiffPathTracer.color", "PrimalAccumulatePass.input") + render_graph.add_edge("WARDiffPathTracer.dColor", "GradAccumulatePass.input") + render_graph.mark_output("PrimalAccumulatePass.output") + render_graph.mark_output("GradAccumulatePass.output") + + passes = { + "primal_accumulate": primal_accumulate_pass, + "grad_accumulate": grad_accumulate_pass, + "war_diff_pt": war_diff_pt_pass, + } + + testbed.render_graph = render_graph + return passes + + +def render_primal(spp: int, testbed: falcor.Testbed, passes): + passes["war_diff_pt"].run_backward = 0 + passes["primal_accumulate"].reset() + for i in range(spp): + testbed.frame() + + img = testbed.render_graph.get_output("PrimalAccumulatePass.output").to_numpy() + img = torch.from_numpy(img[:, :, :3]).cuda() + return img + + +def render_grad(spp: int, testbed: falcor.Testbed, passes, dL_dI_buffer, grad_type): + passes["war_diff_pt"].run_backward = 1 + passes["war_diff_pt"].dL_dI = dL_dI_buffer + + scene_gradients = passes["war_diff_pt"].scene_gradients + scene_gradients.clear(testbed.device.render_context, grad_type) + + for _ in range(spp): + testbed.frame() + + scene_gradients.aggregate(testbed.device.render_context, grad_type) + + grad_buffer = scene_gradients.get_grads_buffer(grad_type) + grad = torch.tensor([0] * (grad_buffer.size // 4), dtype=torch.float32) + grad_buffer.copy_to_torch(grad) + testbed.device.render_context.wait_for_cuda() + return grad / float(spp) diff --git a/scripts/inv-rendering/loss.py b/scripts/inv-rendering/loss.py new file mode 100644 index 000000000..74d775c7b --- /dev/null +++ b/scripts/inv-rendering/loss.py @@ -0,0 +1,63 @@ +import torch +import math + + +def compute_render_loss_L2(img, target_img, weight=1.0): + diff = img - target_img + diff = diff.nan_to_num(nan=0) + loss = 0.5 * torch.square(diff).sum() + return loss * weight + + +def compute_render_loss_L1(img, target_img, weight=1.0): + diff = img - target_img + diff = diff.nan_to_num(nan=0) + diff = diff.abs() + loss = diff.sum() + return loss * weight + + +def downsample(input): + if input.size(0) % 2 == 1: + input = torch.cat((input, torch.unsqueeze(input[-1, :], 0)), dim=0) + if input.size(1) % 2 == 1: + input = torch.cat((input, torch.unsqueeze(input[:, -1], 1)), dim=1) + return ( + input[0::2, 0::2, :] + + input[1::2, 0::2, :] + + input[0::2, 1::2, :] + + input[1::2, 1::2, :] + ) * 0.25 + + +def build_pyramid(img): + level = int(min(math.log2(img.shape[0]), math.log2(img.shape[1]))) + 1 + level = min(5, level) + imgs = [] + for i in range(level): + imgs.append(img) + if i < level - 1: + img = downsample(img) + return imgs + + +def compute_render_loss_pyramid_L1(img, target_pyramid, weight=1.0): + img_pyramid = build_pyramid(img) + level = len(img_pyramid) + loss = 0.0 + for i in range(level): + loss = loss + compute_render_loss_L1( + img_pyramid[i], target_pyramid[i], weight + ) * (4.0**i) + return loss + + +def compute_render_loss_pyramid_L2(img, target_pyramid, weight=1.0): + img_pyramid = build_pyramid(img) + level = len(img_pyramid) + loss = 0.0 + for i in range(level): + loss = loss + compute_render_loss_L2( + img_pyramid[i], target_pyramid[i], weight + ) * (4.0**i) + return loss diff --git a/scripts/inv-rendering/material_optimization/diff_render_module.py b/scripts/inv-rendering/material_optimization/diff_render_module.py new file mode 100644 index 000000000..929ffbb60 --- /dev/null +++ b/scripts/inv-rendering/material_optimization/diff_render_module.py @@ -0,0 +1,140 @@ +import torch +import falcor + +import sys +sys.path.append("..") +import common +import material_utils + + +class DiffRenderFunction(torch.autograd.Function): + @staticmethod + def forward( + ctx, + standard_base_color, + standard_metallic, + standard_roughness, + diffuse_color, + conductor_eta, + conductor_k, + conductor_roughness, + context, + ): + ctx.context = context + + testbed : falcor.Testbed = context["testbed"] + passes : dict[str, falcor.RenderPass] = context["passes"] + scene : falcor.Scene = context["scene"] + params = context["params"] + material_params_dicts = params["init_material_dicts"] + + # Update material parameters. + new_dicts = { + falcor.MaterialType.Standard: { + "base_color": standard_base_color.detach(), + "metallic": standard_metallic.detach(), + "roughness": standard_roughness.detach(), + "idx": material_params_dicts[falcor.MaterialType.Standard]["idx"], + }, + falcor.MaterialType.PBRTDiffuse: { + "diffuse": diffuse_color.detach(), + "idx": material_params_dicts[falcor.MaterialType.PBRTDiffuse]["idx"], + }, + falcor.MaterialType.PBRTConductor: { + "eta": conductor_eta.detach(), + "k": conductor_k.detach(), + "roughness": conductor_roughness.detach(), + "idx": material_params_dicts[falcor.MaterialType.PBRTConductor]["idx"], + }, + } + + # Convert material parameter dictionaries to flattened raw parameters for Falcor. + material_params_raw = material_utils.dicts_to_raw_params( + testbed.scene, params["material_ids"], new_dicts, params["init_material_raw"] + ) + + material_ids_buffer : falcor.Buffer = context["buffers"]["material_ids"] + material_params_buffer : falcor.Buffer = context["buffers"]["material_params"] + + # Set material parameters for Falcor. + material_params_buffer.from_torch(material_params_raw) + testbed.device.render_context.wait_for_cuda() + scene.set_material_params(material_ids_buffer, material_params_buffer) + + # Falcor forward rendering. + img = common.render_primal(context["spp"]["primal"], testbed, passes) + return img.detach() + + @staticmethod + def backward(ctx, grad_output): + context = ctx.context + testbed : falcor.Testbed = context["testbed"] + passes = context["passes"] + dL_dI_buffer = context["buffers"]["dL_dI"] + + # Set `dL_dI_buffer`. + dL_dI_buffer.from_torch(grad_output) + testbed.device.render_context.wait_for_cuda() + + # Falcor differentiable rendering. + grad_raw = common.render_grad( + context["spp"]["grad"], + testbed, + passes, + dL_dI_buffer, + falcor.GradientType.Material, + ) + grad = material_utils.raw_params_to_dicts(testbed.scene, context["params"]["material_ids"], grad_raw) + + return ( + grad[falcor.MaterialType.Standard]["base_color"], + grad[falcor.MaterialType.Standard]["metallic"], + grad[falcor.MaterialType.Standard]["roughness"], + grad[falcor.MaterialType.PBRTDiffuse]["diffuse"], + grad[falcor.MaterialType.PBRTConductor]["eta"], + grad[falcor.MaterialType.PBRTConductor]["k"], + grad[falcor.MaterialType.PBRTConductor]["roughness"], + None, + ) + + +class DiffRenderModule(torch.nn.Module): + def __init__( + self, + testbed, + passes, + scene, + params, + buffers, + spp, + ): + super(DiffRenderModule, self).__init__() + self.context = dict( + testbed=testbed, + passes=passes, + scene=scene, + params=params, + buffers=buffers, + spp=spp, + ) + + def forward( + self, + standard_base_color, + standard_metallic, + standard_roughness, + diffuse_color, + conductor_eta, + conductor_k, + conductor_roughness, + ): + return DiffRenderFunction.apply( + standard_base_color, + standard_metallic, + standard_roughness, + diffuse_color, + conductor_eta, + conductor_k, + conductor_roughness, + self.context, + ) diff --git a/scripts/inv-rendering/material_optimization/sphere_materials_example.py b/scripts/inv-rendering/material_optimization/sphere_materials_example.py new file mode 100644 index 000000000..2fd4e5137 --- /dev/null +++ b/scripts/inv-rendering/material_optimization/sphere_materials_example.py @@ -0,0 +1,156 @@ +import torch +import falcor +import time +import numpy as np +import pyexr as exr +import sys +import os +import dataclasses + +sys.path.append("..") +import common +import material_utils +from diff_render_module import DiffRenderModule + + +@dataclasses.dataclass +class SphereMaterialsExample: + width: int = 720 + height: int = 432 + + M: int = 7 # Size of the sphere array + material_count: int = 7 * 7 * 7 # Number of materials + niter: int = 1000 # Number of iterations + + spp_ref: int = 2048 # Sample count for the reference images + spp_primal: int = 32 # Sample count for the primal (forward) pass + spp_grad: int = 32 # Sample count for the gradient (backward) pass + + output_dir: str = ( # Output directory + "../results/sphere_materials/" + ) + + ref_scene_filepath: str = ( + "inv_rendering_scenes/spheres_material_ref.pyscene" + ) + + init_scene_filepath: str = ( + "inv_rendering_scenes/spheres_material_init.pyscene" + ) + + # Initialize material optimization. + def __post_init__(self): + # Set up Falcor-Python and render passes. + self.testbed = common.create_testbed([self.width, self.height]) + device = self.testbed.device + self.passes = common.create_passes(self.testbed, max_bounces=4, use_war=False) + + # Load the reference scene. + ref_scene = common.load_scene( + self.testbed, + self.ref_scene_filepath, + self.width / self.height, + ) + + # Set up scene manager and gradients. + scene_gradients = falcor.SceneGradients.create( + device=device, + grad_dim=falcor.uint2( + self.material_count * falcor.Material.PARAM_COUNT, 0 + ), + hash_size=falcor.uint2(256, 1), + ) + self.passes["war_diff_pt"].scene_gradients = scene_gradients + + # Create Falcor buffers for Falcor-PyTorch communication. + material_ids_buffer = device.create_structured_buffer( + struct_size=4, + element_count=self.material_count, + bind_flags=falcor.ResourceBindFlags.ShaderResource + | falcor.ResourceBindFlags.UnorderedAccess + | falcor.ResourceBindFlags.Shared, + ) + material_params_buffer = device.create_structured_buffer( + struct_size=4, + element_count=self.material_count * falcor.Material.PARAM_COUNT, + bind_flags=falcor.ResourceBindFlags.ShaderResource + | falcor.ResourceBindFlags.UnorderedAccess + | falcor.ResourceBindFlags.Shared, + ) + dL_dI_buffer = device.create_structured_buffer( + struct_size=12, # float3 + element_count=self.width * self.height, + bind_flags=falcor.ResourceBindFlags.ShaderResource + | falcor.ResourceBindFlags.UnorderedAccess + | falcor.ResourceBindFlags.Shared, + ) + buffers = { + "material_ids": material_ids_buffer, + "material_params": material_params_buffer, + "dL_dI": dL_dI_buffer, + } + + # Set up `material_ids_buffer`. + material_ids = torch.arange(self.material_count, dtype=torch.int32) + material_ids_buffer.from_torch(material_ids) + device.render_context.wait_for_cuda() + + def falcor_to_torch(buffer: falcor.Buffer, dtype=torch.float32): + params = torch.tensor([0] * buffer.element_count, dtype=dtype) + buffer.copy_to_torch(params) + device.render_context.wait_for_cuda() + return params + + # Get reference material parameters. + ref_scene.get_material_params(material_ids_buffer, material_params_buffer) + device.render_context.wait_for_falcor() + self.ref_raw_material_params = falcor_to_torch(material_params_buffer) + self.ref_material_params = material_utils.raw_params_to_dicts( + ref_scene, material_ids, self.ref_raw_material_params + ) + + # Generate reference image. + spp = { + "ref": self.spp_ref, + "primal": self.spp_primal, + "grad": self.spp_grad, + } + self.ref_img = common.render_primal(spp["ref"], self.testbed, self.passes) + print("Output ref image with shape", self.ref_img.shape) + if not os.path.exists(self.output_dir): + os.makedirs(self.output_dir) + exr.write(os.path.join(self.output_dir, "ref.exr"), self.ref_img.cpu().numpy()) + + # Load the initial scene. + init_scene = common.load_scene( + self.testbed, + self.init_scene_filepath, + self.width / self.height, + ) + # Get initial material parameters. + init_scene.get_material_params(material_ids_buffer, material_params_buffer) + device.render_context.wait_for_falcor() + self.init_raw_material_params = falcor_to_torch(material_params_buffer) + self.init_material_params = material_utils.raw_params_to_dicts( + init_scene, material_ids, self.init_raw_material_params + ) + + params = { + "init_material_dicts": self.init_material_params, + "init_material_raw": self.init_raw_material_params, + "material_ids": material_ids, + } + + init_img = common.render_primal(spp["ref"], self.testbed, self.passes) + print("Output init image with shape", init_img.shape) + exr.write(os.path.join(self.output_dir, "init.exr"), init_img.cpu().numpy()) + + # Set up differentiable render module. + self.diff_render = DiffRenderModule( + self.testbed, + self.passes, + init_scene, + params, + buffers, + spp, + ) diff --git a/scripts/inv-rendering/material_optimization/train_sphere_materials.py b/scripts/inv-rendering/material_optimization/train_sphere_materials.py new file mode 100644 index 000000000..c6cc61c37 --- /dev/null +++ b/scripts/inv-rendering/material_optimization/train_sphere_materials.py @@ -0,0 +1,128 @@ +import torch +import falcor +import time +import numpy as np +import pyexr as exr +import sys +import os +import dataclasses +import datetime +import glob +import argparse + +sys.path.append("..") +import common +import material_utils +from loss import compute_render_loss_L1, compute_render_loss_L2 +from sphere_materials_example import SphereMaterialsExample + + +def main(args): + # Create torch CUDA device + device = torch.device("cuda:0") + print(device) + torch.set_default_tensor_type(torch.cuda.FloatTensor) + + # Create an inverse rendering example that optimizes sphere materials. + inv_ex = SphereMaterialsExample() + + # Set up PyTorch optimizer. + learning_rates = { + falcor.MaterialType.Standard: { + "base_color": 1e-2, + "roughness": 3e-3, + "metallic": 3e-3, + }, + falcor.MaterialType.PBRTDiffuse: { + "diffuse": 1e-2, + }, + falcor.MaterialType.PBRTConductor: { + "eta": 1e-2, + "k": 1e-2, + "roughness": 1e-2, + }, + } + + params_dicts = inv_ex.init_material_params + params_list = [] + for material_type in params_dicts: + for key in params_dicts[material_type]: + if key == "idx": + continue + params_dicts[material_type][key].requires_grad_() + params_list.append({ + "params": params_dicts[material_type][key], + "lr": learning_rates[material_type][key], + }) + + optimizer = torch.optim.Adam(params_list, eps=1e-6) + + # Workaround to fix "If capturable=False, state_steps should not be CUDA tensors". + for param_group in optimizer.param_groups: + param_group["capturable"] = True + + # Run optimization. + img_error = [] + param_error = [] + for i_iter in range(inv_ex.niter): + optimizer.zero_grad() + loss = 0.0 + now = datetime.datetime.now() + + # Render. + cur_img = inv_ex.diff_render( + params_dicts[falcor.MaterialType.Standard]["base_color"], + params_dicts[falcor.MaterialType.Standard]["metallic"], + params_dicts[falcor.MaterialType.Standard]["roughness"], + params_dicts[falcor.MaterialType.PBRTDiffuse]["diffuse"], + params_dicts[falcor.MaterialType.PBRTConductor]["eta"], + params_dicts[falcor.MaterialType.PBRTConductor]["k"], + params_dicts[falcor.MaterialType.PBRTConductor]["roughness"], + ) + cur_img[cur_img.isnan()] = 0.0 + + img_loss = compute_render_loss_L2(cur_img, inv_ex.ref_img) + loss += img_loss.item() + img_error.append(img_loss.item()) + + # Backpropagate gradients. + img_loss.backward() + + end = datetime.datetime.now() - now + time_iter = end.seconds + end.microseconds / 1e6 + + # Print stats + print( + "[INFO] iter = {:d}, image loss = {:.3f}, time = {:.3f}s".format( + i_iter, loss, time_iter + ) + ) + + optimizer.step() + params_dicts = material_utils.clamp_material_params(params_dicts) + + param_loss = material_utils.compute_loss_params(params_dicts, inv_ex.ref_material_params) + param_error.append(param_loss.item()) + print("Parameter loss:", param_loss.item()) + + # Export images + if i_iter % 10 == 0 or i_iter == inv_ex.niter - 1: + if i_iter <= 50 or i_iter % 50 == 0 or i_iter == inv_ex.niter - 1: + cur_img = common.render_primal(1024, inv_ex.testbed, inv_ex.passes) + exr.write( + os.path.join(inv_ex.output_dir, "iter_{:d}.exr".format(i_iter)), + cur_img.detach().cpu().numpy(), + ) + material_utils.output_material_params( + os.path.join(inv_ex.output_dir, "iter_{:d}.npy".format(i_iter)), + params_dicts, + ) + + np.savetxt(os.path.join(inv_ex.output_dir, "loss_image.txt"), img_error) + np.savetxt(os.path.join(inv_ex.output_dir, "loss_parameter.txt"), param_error) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + args = parser.parse_args() + main(args) diff --git a/scripts/inv-rendering/material_utils.py b/scripts/inv-rendering/material_utils.py new file mode 100644 index 000000000..2a67e872c --- /dev/null +++ b/scripts/inv-rendering/material_utils.py @@ -0,0 +1,133 @@ +import torch +import falcor +import numpy as np + +from loss import compute_render_loss_L2 + + +def raw_params_to_dicts(scene : falcor.Scene, material_ids, raw_params): + """ + @param[in] scene: falcor.Scene. + @param[in] material_ids: torch.IntTensor[M], material ids. + @param[in] raw_params: torch.FloatTensor[20 * M], flattened material parameters from Falcor. + @return parameter dictionaries. + """ + device = torch.device("cuda:0") + params_dicts = { + falcor.MaterialType.Standard: { + "base_color": torch.zeros(0, device=device), + "metallic": torch.zeros(0, device=device), + "roughness": torch.zeros(0, device=device), + "idx": [], + }, + falcor.MaterialType.PBRTDiffuse: { + "diffuse": torch.zeros(0, device=device), + "idx": [], + }, + falcor.MaterialType.PBRTConductor: { + "eta": torch.zeros(0, device=device), + "k": torch.zeros(0, device=device), + "roughness": torch.zeros(0, device=device), + "idx": [], + } + } + + material_param_size = raw_params.shape[0] // material_ids.shape[0] + for i in range(material_ids.shape[0]): + material_type = scene.get_material(material_ids[i]).type + params = params_dicts[material_type] + layout = falcor.get_material_param_layout(material_type) + for key in params: + if key == "idx": + params[key].append(i) + else: + offset = layout[key]["offset"] + size = layout[key]["size"] + params[key] = torch.concat( + ( + params[key], + raw_params[i * material_param_size + offset : i * material_param_size + offset + size], + ) + ) + + return params_dicts + + +def dicts_to_raw_params(scene, material_ids, params_dicts, input_raw_params): + """ + @param[in] scene: falcor.Scene. + @param[in] material_ids: torch.IntTensor[M], material ids. + @param[in] params_dicts: parameter dictionaries. + @param[in] input_raw_params: torch.FloatTensor[20 * M], original flattened material parameters. + @return updated flattened material parameters for Falcor + """ + material_parameter_count = input_raw_params.shape[0] // material_ids.shape[0] + res = input_raw_params.clone() + for material_type in params_dicts: + layout = falcor.get_material_param_layout(material_type) + params = params_dicts[material_type] + for i in range(len(params["idx"])): + idx = params["idx"][i] + for key in params: + if key == "idx": + continue + offset = layout[key]["offset"] + size = layout[key]["size"] + res[ + idx * material_parameter_count + offset : idx * material_parameter_count + offset + size + ] = params[key][i * size : i * size + size] + + return res + + +def compute_loss_params(params_dicts, ref_params_dicts): + res = 0.0 + for material_type in params_dicts: + params = params_dicts[material_type] + for key in params: + if key == "idx": + continue + res += compute_render_loss_L2( + params[key].detach(), + ref_params_dicts[material_type][key], + ) + return res + + +def output_material_params(filename, params_dicts): + res = {} + for material_type in params_dicts: + if material_type == falcor.MaterialType.Standard: + type_name = "Standard" + elif material_type == falcor.MaterialType.PBRTDiffuse: + type_name = "PBRTDiffuse" + elif material_type == falcor.MaterialType.PBRTConductor: + type_name = "PBRTConductor" + else: + raise RuntimeError("Unknown material type") + + res[type_name] = {} + params = params_dicts[material_type] + for key in params: + if key == "idx": + res[type_name][key] = np.array(params[key], dtype=np.int32) + else: + res[type_name][key] = params[key].detach().cpu().numpy() + np.save(filename, res) + + +def clamp_material_params(params_dicts): + for material_type in params_dicts: + params = params_dicts[material_type] + if len(params["idx"]) == 0: + continue + + for key in params: + if key == "idx": + continue + elif key == "roughness": + params[key].data.copy_(torch.clamp(params[key], 0.1, 0.9999)) + else: + params[key].data.copy_(torch.clamp(params[key], 0.0001, 0.9999)) + + return params_dicts diff --git a/scripts/inv-rendering/mesh_utils.py b/scripts/inv-rendering/mesh_utils.py new file mode 100644 index 000000000..ef3d61ac3 --- /dev/null +++ b/scripts/inv-rendering/mesh_utils.py @@ -0,0 +1,196 @@ +import torch +import falcor +import numpy as np + +EPS = 1e-10 + + +def dot(x: torch.Tensor, y: torch.Tensor) -> torch.Tensor: + return torch.sum(x * y, dim=-1, keepdim=True) + + +def length(x: torch.Tensor) -> torch.Tensor: + return torch.sqrt(dot(x, x)) + + +def length_safe(x: torch.Tensor, eps: float = EPS) -> torch.Tensor: + return torch.sqrt(torch.clamp(dot(x, x), min=eps*eps)) # Clamp to avoid NaN gradients because grad(sqrt(0)) = NaN. + + +def normalize_safe(x: torch.Tensor, eps: float = EPS) -> torch.Tensor: + return x / length_safe(x, eps) + + +class Mesh: + def __init__( + self, + tri_idx=None, + v_pos=None, + v_norm=None, + v_tangent=None, + v_texcrd=None, + ): + self.tri_idx = tri_idx + self.v_pos = v_pos + self.v_norm = v_norm + self.v_tangent = v_tangent + self.v_texcrd = v_texcrd + + self.buffers = { + "triangleIndices": None, + "positions": None, + "normals": None, + "tangents": None, + "texcrds": None, + } + + def init_falcor( + self, + device: falcor.Device, + scene: falcor.Scene, + vertex_count: int, + triangle_count: int, + ): + for key in self.buffers: + elem_count = triangle_count if key == "triangleIndices" else vertex_count + if ( + self.buffers[key] is None + or self.buffers[key].element_count < elem_count + ): + self.buffers[key] = device.create_structured_buffer( + struct_size=12, + element_count=elem_count, + bind_flags=falcor.ResourceBindFlags.ShaderResource + | falcor.ResourceBindFlags.UnorderedAccess + | falcor.ResourceBindFlags.Shared, + ) + + def load_from_falcor(self, testbed: falcor.Testbed, mesh_id: int): + scene = testbed.scene + device = testbed.device + mesh = scene.get_mesh(mesh_id) + + self.init_falcor( + device, scene, mesh.vertex_count, mesh.triangle_count + ) + scene.get_mesh_vertices_and_indices(mesh_id, self.buffers) + + # Copy from Falcor to PyTorch. + self.tri_idx = torch.zeros([mesh.triangle_count, 3], dtype=torch.int32) + self.buffers["triangleIndices"].copy_to_torch(self.tri_idx) + + self.v_pos = torch.zeros([mesh.vertex_count, 3], dtype=torch.float32) + self.buffers["positions"].copy_to_torch(self.v_pos) + + self.v_texcrd = torch.zeros([mesh.vertex_count, 3], dtype=torch.float32) + self.buffers["texcrds"].copy_to_torch(self.v_texcrd) + + device.render_context.wait_for_cuda() + + def update_to_falcor(self, testbed: falcor.Testbed, mesh_id: int): + scene = testbed.scene + device = testbed.device + + # Copy from PyTorch to Falcor. + self.buffers["positions"].from_torch(self.v_pos.detach()) + self.buffers["normals"].from_torch(self.v_norm.detach()) + self.buffers["tangents"].from_torch(self.v_tangent.detach()) + self.buffers["texcrds"].from_torch(self.v_texcrd.detach()) + device.render_context.wait_for_cuda() + + # Bind shader data. + scene.set_mesh_vertices(mesh_id, self.buffers) + + def compute_shading_frame(self): + self.compute_normals() + self.compute_tangents() + + # From nvdiffrec. + # Compute smooth vertex normals. + def compute_normals(self): + idx = [ + self.tri_idx[:, 0].type(torch.int64), + self.tri_idx[:, 1].type(torch.int64), + self.tri_idx[:, 2].type(torch.int64), + ] + pos = [self.v_pos[idx[0], :], self.v_pos[idx[1], :], self.v_pos[idx[2], :]] + face_normals = torch.cross(pos[1] - pos[0], pos[2] - pos[0]) + + # Splat face normals to vertices. + v_normals = torch.zeros_like(self.v_pos) + v_normals.scatter_add_(0, idx[0][:, None].repeat(1, 3), face_normals) + v_normals.scatter_add_(0, idx[1][:, None].repeat(1, 3), face_normals) + v_normals.scatter_add_(0, idx[2][:, None].repeat(1, 3), face_normals) + + # Normalize, replace zero (degenerated) normals with some default value. + v_normals = torch.where( + length(v_normals) > EPS, + v_normals, + torch.tensor([0.0, 0.0, 1.0], dtype=torch.float32, device="cuda"), + ) + v_normals = normalize_safe(v_normals) + + if torch.is_anomaly_enabled(): + assert torch.all(torch.isfinite(v_normals)) + + self.v_norm = v_normals + + # From nvdiffrec. + # Compute tangent space from texture map coordinates. + # Follows http://www.mikktspace.com/ conventions. + def compute_tangents(self): + idx = [ + self.tri_idx[:, 0].type(torch.int64), + self.tri_idx[:, 1].type(torch.int64), + self.tri_idx[:, 2].type(torch.int64), + ] + pos = [self.v_pos[idx[0], :], self.v_pos[idx[1], :], self.v_pos[idx[2], :]] + texcrd = [ + self.v_texcrd[idx[0], :], + self.v_texcrd[idx[1], :], + self.v_texcrd[idx[2], :], + ] + + v_tangents = torch.zeros_like(self.v_norm) + + # Compute tangent space for each triangle. + uve1 = texcrd[1] - texcrd[0] + uve2 = texcrd[2] - texcrd[0] + pe1 = pos[1] - pos[0] + pe2 = pos[2] - pos[0] + + nom = pe1 * uve2[:, 1:2] - pe2 * uve1[:, 1:2] + denom = uve1[:, 0:1] * uve2[:, 1:2] - uve1[:, 1:2] * uve2[:, 0:1] + + # Avoid division by zerofor degenerated texture coordinates. + tang = nom / torch.where( + denom > 0.0, torch.clamp(denom, min=EPS), torch.clamp(denom, max=-EPS) + ) + + # Update all 3 vertices. + for i in range(3): + t_idx = idx[i][:, None].repeat(1, 3) + v_tangents.scatter_add_(0, t_idx, tang) + + # Normalize, replace zero (degenerated) tangents with some default value. + default_tangents = torch.where( + dot( + self.v_norm, + torch.tensor([1.0, 0.0, 0.0], dtype=torch.float32, device="cuda"), + ) + > 0.9999, + torch.tensor([0.0, 1.0, 0.0], dtype=torch.float32, device="cuda"), + torch.tensor([1.0, 0.0, 0.0], dtype=torch.float32, device="cuda"), + ) + v_tangents = torch.where(length(v_tangents) > EPS, v_tangents, default_tangents) + v_tangents = normalize_safe(v_tangents) + + # Make sure tangent is orthogonal to normal. + v_tangents = normalize_safe( + v_tangents - self.v_norm * dot(self.v_norm, v_tangents) + ) + + if torch.is_anomaly_enabled(): + assert torch.all(torch.isfinite(v_tangents)) + + self.v_tangent = v_tangents diff --git a/scripts/python/TinyBC/BCTypes.slang b/scripts/python/TinyBC/BCTypes.slang new file mode 100644 index 000000000..092252bd0 --- /dev/null +++ b/scripts/python/TinyBC/BCTypes.slang @@ -0,0 +1,225 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ + + // This struct holds the BC interpolation weights for a 4x4 texel tile +struct BCWeights : IDifferentiable +{ + float w[16]; + + __init(float _w) + { + for (int i = 0; i < 16; i++) + w[i] = _w; + } + + __init(float _w[16]) + { + for (int i = 0; i < 16; i++) + w[i] = _w[i]; + } + + [mutating] + void clamp() + { + for (int i = 0; i < 16; i++) + w[i] = clamp(w[i], 0, 1); + } + + [mutating] + void quantize() + { + // 3b for the 1st weight + w[0] = round(w[0] * 7) / 7; + // and 4b for the others + for (int i = 1; i < 15; i++) + w[i] = round(w[i] * 15) / 15; + } + + BCWeights rsqrt() + { + BCWeights r; + for (int i = 0; i < 16; i++) + r.w[i] = rsqrt(w[i]); + return r; + } +} + +BCWeights operator+(float a, BCWeights b) +{ + BCWeights r; + for (int i = 0; i < 16; i++) + r.w[i] = a + b.w[i]; + return r; +} + +BCWeights operator+(BCWeights a, BCWeights b) +{ + BCWeights r; + for (int i = 0; i < 16; i++) + r.w[i] = a.w[i] + b.w[i]; + return r; +} + +BCWeights operator-(BCWeights a, BCWeights b) +{ + BCWeights r; + for (int i = 0; i < 16; i++) + r.w[i] = a.w[i] - b.w[i]; + return r; +} + +BCWeights operator*(BCWeights a, BCWeights b) +{ + BCWeights r; + for (int i = 0; i < 16; i++) + r.w[i] = a.w[i] * b.w[i]; + return r; +} + +BCWeights operator*(float a, BCWeights b) +{ + BCWeights r; + for (int i = 0; i < 16; i++) + r.w[i] = a * b.w[i]; + return r; +} + + // This struct holds all of the data for a 4x4 texel tile +struct BCTile : IDifferentiable +{ + BCWeights weights; + float4 minEndPoints; + float4 maxEndPoints; + + __init() + { + weights = BCWeights(0.f); + minEndPoints = 0.f; + maxEndPoints = 0.f; + } + + __init(BCTile.Differential tile) + { + weights = BCWeights(tile.weights.w); + minEndPoints = tile.minEndPoints; + maxEndPoints = tile.maxEndPoints; + } + + __init(float _weight, float4 _minEndPoints, float4 _maxEndPoints) + { + weights = BCWeights(_weight); + minEndPoints = _minEndPoints; + maxEndPoints = _maxEndPoints; + } + + [mutating] + void clamp() + { + weights.clamp(); + minEndPoints = clamp(minEndPoints, 0, 1); + maxEndPoints = clamp(maxEndPoints, 0, 1); + } + + [mutating] + void quantize() + { + weights.quantize(); + minEndPoints = round(minEndPoints * 127) / 127; + maxEndPoints = round(maxEndPoints * 127) / 127; + } + + BCTile rsqrt() + { + BCTile r; + r.weights = weights.rsqrt(); + r.minEndPoints = rsqrt(minEndPoints); + r.maxEndPoints = rsqrt(maxEndPoints); + return r; + } + + [BackwardDifferentiable] + float4 decode(int i) + { + return (1 - weights.w[i]) * minEndPoints + weights.w[i] * maxEndPoints; + } + + // Use 6b interpolation to match API specs + float4 decodeExact(int i) + { + let b = uint(round(64.f * saturate(weights.w[i]))); + let a = 64u - b; + uint4 uMinEndPoints = uint4(127.f * minEndPoints) << 1u; + uint4 uMaxEndPoints = uint4(127.f * maxEndPoints) << 1u; + return (32u + (a * uMinEndPoints + b * uMaxEndPoints) >> 6) / 255.f; + } +} + +BCTile operator*(BCTile a, BCTile b) +{ + BCTile r; + r.weights = a.weights * b.weights; + r.minEndPoints = a.minEndPoints * b.minEndPoints; + r.maxEndPoints = a.maxEndPoints * b.maxEndPoints; + return r; +} + +BCTile operator*(float a, BCTile b) +{ + BCTile r; + r.weights = a * b.weights; + r.minEndPoints = a * b.minEndPoints; + r.maxEndPoints = a * b.maxEndPoints; + return r; +} + +BCTile operator+(float a, BCTile b) +{ + BCTile r; + r.weights = a + b.weights; + r.minEndPoints = a + b.minEndPoints; + r.maxEndPoints = a + b.maxEndPoints; + return r; +} + +BCTile operator+(BCTile a, BCTile b) +{ + BCTile r; + r.weights = a.weights + b.weights; + r.minEndPoints = a.minEndPoints + b.minEndPoints; + r.maxEndPoints = a.maxEndPoints + b.maxEndPoints; + return r; +} + +BCTile operator-(BCTile a, BCTile b) +{ + BCTile r; + r.weights = a.weights - b.weights; + r.minEndPoints = a.minEndPoints - b.minEndPoints; + r.maxEndPoints = a.maxEndPoints - b.maxEndPoints; + return r; +} diff --git a/scripts/python/TinyBC/TinyBC.cs.slang b/scripts/python/TinyBC/TinyBC.cs.slang new file mode 100644 index 000000000..b227de07e --- /dev/null +++ b/scripts/python/TinyBC/TinyBC.cs.slang @@ -0,0 +1,125 @@ +/*************************************************************************** + # Copyright (c) 2015-23, NVIDIA CORPORATION. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the distribution. + # * Neither the name of NVIDIA CORPORATION nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **************************************************************************/ + +import BCTypes; + +static bool kUseAdam = USE_ADAM; +static int kNumOptimizationSteps = NUM_OPTIMIZATION_STEPS; + +uniform float lr; +uniform float adamBeta1; +uniform float adamBeta2; +uniform int2 textureDim; + +// Uncompressed input texture +Texture2D gInputTex; + +// Decoded BC texture +RWTexture2D gDecodedTex; + +[BackwardDifferentiable] +void l2Loss(BCTile tile, no_diff float4 target[16], out float loss[16]) +{ + [ForceUnroll] + for (int i = 0; i < 16; i++) + { + let delta = target[i] - tile.decode(i); + loss[i] = dot(delta, delta); + } +} + +[numthreads(4, 8, 1)] +void encoder(int3 dispatchThreadId : SV_DispatchThreadID) +{ + let texel = 4 * dispatchThreadId.xy; + if (any(texel >= textureDim)) return; + + // Adam moments + BCTile m1, m2; + + // Load target values and initialize with mid-point interpolation + // and minmax endpoints computed over the tile + float4 target[16]; + BCTile tile = BCTile(0.5, float4(1), float4(0)); + for (int i = 0; i < 16; i++) + { + target[i] = gInputTex[texel + int2(i % 4, i / 4)]; + tile.minEndPoints = min(tile.minEndPoints, target[i]); + tile.maxEndPoints = max(tile.maxEndPoints, target[i]); + } + + // To compress the texture we perform a number of gradient descent steps + // to joint-optimize interpolation weights and endpoints for each tile. + // Eventually the interpolation weights are quantized and frozen, + // while the endpoints are fine tuned for a few more steps. + bool frozenWeight = false; + let endPointsFineTuninigThres = 0.85f; + for (int s = 0; s < kNumOptimizationSteps; s++) + { + // Forward pass - compute error + float loss[16]; + l2Loss(tile, target, loss); + + // Backward pass - compute gradients + var dp_tile = diffPair(tile); + __bwd_diff(l2Loss)(dp_tile, target, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}); + + // Use Adam optimizer to update gradients + BCTile grad = BCTile(dp_tile.d); + if (kUseAdam) + { + m1 = adamBeta1 * m1 + (1 - adamBeta1) * grad; + m2 = adamBeta2 * m2 + (1 - adamBeta2) * (grad * grad); + let unbiased_m1 = rcp(1.f - pow(adamBeta1, 1 + s)) * m1; + let unbiased_m2 = rcp(1.f - pow(adamBeta2, 1 + s)) * m2; + grad = unbiased_m1 * (1E-10f + unbiased_m2).rsqrt(); + } + + // If weights are frozen zero their gradient + if (frozenWeight) + grad.weights = BCWeights(0); + + // Update BC tile via gradient descent & clamp its values to a valid range + tile = tile - lr * grad; + tile.clamp(); + + // Time to freeze the BC weights? + if (frozenWeight == false && float(s) / kNumOptimizationSteps >= endPointsFineTuninigThres) + { + // Quantize & freeze weights + // We will continue to fine tune the endpoints for a few more steps + frozenWeight = true; + tile.weights.quantize(); + } + } + + // Quantize & write out decoded tile + tile.quantize(); + for (int i = 0; i < 16; i++) + gDecodedTex[texel + int2(i % 4, i / 4)] = tile.decodeExact(i);; +} diff --git a/scripts/python/TinyBC/TinyBC.py b/scripts/python/TinyBC/TinyBC.py new file mode 100644 index 000000000..a70403be1 --- /dev/null +++ b/scripts/python/TinyBC/TinyBC.py @@ -0,0 +1,88 @@ +import sys +import time +import falcor +import argparse +import numpy as np + +from PIL import Image +from pathlib import Path + +# Create GPU device +device = falcor.Device() + +# Build and parse command line +parser = argparse.ArgumentParser(description="Slang-based BC7 - mode 6 compressor") +parser.add_argument("input_path", help="Path to the input texture.") +parser.add_argument("-o", "--output_path", help="Optional path to save the decoded BC7 texture.") +parser.add_argument("-s", "--opt_steps", type=int, default=100, help="Number of optimization (gradient descene) steps.") +parser.add_argument("-b", "--benchmark", action="store_true", help="Run in benchmark mode to measure processing time.") +args = parser.parse_args() + +# Load texture +try: + img = Image.open(args.input_path).convert("RGBA") + texture = np.asarray(img).astype(np.float32) / 255.0 + texture = np.clip(texture, 0, 1) + w, h, _ = texture.shape + print(f"\nTexture dimensions: {w}x{h}") +except Exception as e: + print(f"\nError loading the texture: {e}") + sys.exit(1) + +input_tex = device.create_texture( + format=falcor.ResourceFormat.RGBA32Float, + width=w, height=h, mip_levels=1, + bind_flags=falcor.ResourceBindFlags.ShaderResource) +input_tex.from_numpy(texture) + +# Create output texture +decoded_tex = device.create_texture( + format=falcor.ResourceFormat.RGBA32Float, + width=w, height=h, mip_levels=1, + bind_flags=falcor.ResourceBindFlags.UnorderedAccess, +) + +# Create a compute pass for our BC7-mode6 texture encoder +encoder = falcor.ComputePass( + device, + file=Path(__file__).parent / "TinyBC.cs.slang", cs_entry="encoder", + defines={"USE_ADAM": "true", "NUM_OPTIMIZATION_STEPS": str(args.opt_steps)} +) + +# Set constants for the compute shader +encoder.globals.gInputTex = input_tex +encoder.globals.gDecodedTex = decoded_tex +encoder.globals.lr = 0.1 +encoder.globals.adamBeta1 = 0.9 +encoder.globals.adamBeta2 = 0.999 +encoder.globals.textureDim = falcor.int2(w, h) + +# When running in benchmark mode amortize overheads over many runs to measure more accurate GPU times +num_iters = 1000 if args.benchmark else 1 + +# Compress! +start_time = time.time() +for i in range(num_iters): + # Compress input texture using BC7 mode 6, and output decompressed result + encoder.execute(threads_x=w // 4, threads_y=h // 4) + +# Calculate and print performance metrics +if args.benchmark: + device.render_context.submit(True) + comp_time_in_sec = (time.time() - start_time) / num_iters + textures_per_sec = 1 / comp_time_in_sec + giga_texels_per_sec = w * h * textures_per_sec / 1E9 + print(f"\nBenchmark mode:") + print(f" - Number of optimization passes: {args.opt_steps}") + print(f" - Compression time: {1E3 * comp_time_in_sec:.4g} ms --> {giga_texels_per_sec:.4g} GTexels/s") + +# Calculate and print PSNR +decoded_texture = decoded_tex.to_numpy() +mse = np.mean((input_tex.to_numpy() - decoded_tex.to_numpy()) ** 2) +psnr = 20 * np.log10(1.0 / np.sqrt(mse)) +print(f"\nPSNR: {psnr:.4g}") + +# Output decoded texture +if args.output_path: + img = Image.fromarray((255 * decoded_texture).astype(np.uint8), 'RGBA') + img.save(args.output_path) diff --git a/scripts/python/balls/balls.py b/scripts/python/balls/balls.py new file mode 100644 index 000000000..5086c2c58 --- /dev/null +++ b/scripts/python/balls/balls.py @@ -0,0 +1,83 @@ +""" +This is a simple example of using Falcor for a compute shader simulation. +""" + +import falcor +from pathlib import Path +import time +import numpy as np + +DIR = Path(__file__).parent + +BALL_COUNT = 100 +BALL_RADIUS = 0.1 +RESOLUTION = 1024 + +# create testbed instance with a window +testbed = falcor.Testbed(create_window=True, width=1024, height=1024) +device = testbed.device + +# create a structured buffer to hold our ball postitions (float2) and velocities (float2) +balls = device.create_structured_buffer( + struct_size=16, + element_count=BALL_COUNT, + bind_flags=falcor.ResourceBindFlags.ShaderResource + | falcor.ResourceBindFlags.UnorderedAccess, +) + +# create a texture to render to +texture = device.create_texture( + format=falcor.ResourceFormat.RGBA32Float, + width=RESOLUTION, + height=RESOLUTION, + mip_levels=1, + bind_flags=falcor.ResourceBindFlags.UnorderedAccess + | falcor.ResourceBindFlags.ShaderResource, +) + +# assign the texture to be rendered in the testbed window +testbed.render_texture = texture + +# disable the testbed UI +testbed.show_ui = False + +# create a compute pass for updating balls +update_balls = falcor.ComputePass( + device, file=DIR / "balls_update.cs.slang", cs_entry="main" +) +update_balls.globals.g_balls = balls +update_balls.globals.g_ball_count = BALL_COUNT + +# create a compute pass for rendering balls +render_balls = falcor.ComputePass( + device, file=DIR / "balls_render.cs.slang", cs_entry="main" +) +render_balls.globals.g_balls = balls +render_balls.globals.g_ball_count = BALL_COUNT +render_balls.globals.g_ball_radius = BALL_RADIUS +render_balls.globals.g_output = texture +render_balls.globals.g_resolution = RESOLUTION + +# initialize balls with random position and velocities +balls.from_numpy(np.random.rand(BALL_COUNT, 4).astype(np.float32) * 2 - 1) + +prev_time = time.time() + +profiler = device.profiler + +# main loop +while not testbed.should_close: + # compute seconds since last frame + cur_time = time.time() + dt = cur_time - prev_time + prev_time = cur_time + + # update and render the balls + update_balls.globals.g_dt = dt + with profiler.event("update_balls"): + update_balls.execute(threads_x=BALL_COUNT) + with profiler.event("render_balls"): + render_balls.execute(threads_x=RESOLUTION, threads_y=RESOLUTION) + + # present the rendered texture + testbed.frame() diff --git a/scripts/python/balls/balls_render.cs.slang b/scripts/python/balls/balls_render.cs.slang new file mode 100644 index 000000000..937655615 --- /dev/null +++ b/scripts/python/balls/balls_render.cs.slang @@ -0,0 +1,42 @@ +import Utils.Math.HashUtils; + +struct Ball { + float2 pos; + float2 vel; +}; + +StructuredBuffer g_balls; +uniform uint g_ball_count; +uniform float g_ball_radius; +RWTexture2D g_output; +uniform uint g_resolution; + +float3 pseudocolor(uint value) +{ + uint h = jenkinsHash(value); + return (uint3(h, h >> 8, h >> 16) & 0xff) / 255.f; +} + +[numthreads(16, 16, 1)] +void main(uint3 thread_id: SV_DispatchThreadID) +{ + uint2 pixel = thread_id.xy; + if (any(pixel >= g_resolution)) + return; + + float3 color = float3(0.f); + + float2 pos = float2(pixel) / float(g_resolution) * 2.f - 1.f; + + for (uint idx = 0; idx < g_ball_count; ++idx) + { + Ball ball = g_balls[idx]; + float d = distance(ball.pos, pos); + if (d < g_ball_radius) + { + color += pseudocolor(idx) * smoothstep(g_ball_radius, 0.8f * g_ball_radius, d); + } + } + + g_output[pixel] = float4(color, 1.f); +} diff --git a/scripts/python/balls/balls_update.cs.slang b/scripts/python/balls/balls_update.cs.slang new file mode 100644 index 000000000..08b5e521c --- /dev/null +++ b/scripts/python/balls/balls_update.cs.slang @@ -0,0 +1,33 @@ +struct Ball +{ + float2 pos; + float2 vel; +}; + +RWStructuredBuffer g_balls; +uniform uint g_ball_count; +uniform float g_dt; + +[numthreads(128, 1, 1)] +void main(uint3 thread_id: SV_DispatchThreadID) +{ + uint idx = thread_id.x; + if (idx >= g_ball_count) + return; + + Ball ball = g_balls[idx]; + + ball.pos += ball.vel * g_dt; + if (ball.pos.x < -1.f || ball.pos.x > 1.f) + { + ball.pos.x = sign(ball.pos.x); + ball.vel.x *= -1.f; + } + if (ball.pos.y < -1.f || ball.pos.y > 1.f) + { + ball.pos.y = sign(ball.pos.y); + ball.vel.y *= -1.f; + } + + g_balls[idx] = ball; +} diff --git a/scripts/python/gaussian2d/gaussian2d.py b/scripts/python/gaussian2d/gaussian2d.py new file mode 100644 index 000000000..018e6301b --- /dev/null +++ b/scripts/python/gaussian2d/gaussian2d.py @@ -0,0 +1,196 @@ +""" +This is a simple example of using Falcor + DiffSlang + pytorch. +It learns to represent an image by a set of (additive) 2D gaussians. +A Falcor compute pass is used to render the gaussians to an image (forward). +DiffSlang is used to compute the gradients of the gaussians with respect to the +image (backward). +pytorch is used to optimize the gaussians to match a target image. +""" + +import falcor +from pathlib import Path +import torch +import numpy as np +from PIL import Image + +DIR = Path(__file__).parent + +TARGET_IMAGE = DIR / "../../../media/test_images/monalisa.jpg" + +BLOB_COUNT = 1024 * 4 +RESOLUTION = 1024 +ITERATIONS = 4000 + + +class Splat2D: + def __init__(self, device: falcor.Device): + self.device = device + + self.blobs_buf = device.create_structured_buffer( + struct_size=32, + element_count=BLOB_COUNT, + bind_flags=falcor.ResourceBindFlags.ShaderResource + | falcor.ResourceBindFlags.UnorderedAccess + | falcor.ResourceBindFlags.Shared, + ) + + self.grad_blobs_buf = device.create_structured_buffer( + struct_size=32, + element_count=BLOB_COUNT, + bind_flags=falcor.ResourceBindFlags.ShaderResource + | falcor.ResourceBindFlags.UnorderedAccess + | falcor.ResourceBindFlags.Shared, + ) + + self.image_buf = device.create_structured_buffer( + struct_size=12, + element_count=RESOLUTION * RESOLUTION, + bind_flags=falcor.ResourceBindFlags.ShaderResource + | falcor.ResourceBindFlags.UnorderedAccess + | falcor.ResourceBindFlags.Shared, + ) + + self.grad_image_buf = device.create_structured_buffer( + struct_size=12, + element_count=RESOLUTION * RESOLUTION, + bind_flags=falcor.ResourceBindFlags.ShaderResource + | falcor.ResourceBindFlags.UnorderedAccess + | falcor.ResourceBindFlags.Shared, + ) + + self.forward_pass = falcor.ComputePass( + device, file=DIR / "splat2d.cs.slang", cs_entry="forward_main" + ) + + self.backward_pass = falcor.ComputePass( + device, file=DIR / "splat2d.cs.slang", cs_entry="backward_main" + ) + + def forward(self, blobs): + self.blobs_buf.from_torch(blobs.detach()) + self.device.render_context.wait_for_cuda() + vars = self.forward_pass.globals.forward + vars.blobs = self.blobs_buf + vars.blob_count = BLOB_COUNT + vars.image = self.image_buf + vars.resolution = falcor.uint2(RESOLUTION, RESOLUTION) + self.forward_pass.execute(threads_x=RESOLUTION, threads_y=RESOLUTION) + self.device.render_context.wait_for_falcor() + return self.image_buf.to_torch([RESOLUTION, RESOLUTION, 3], falcor.float32) + + def backward(self, blobs, grad_intensities): + self.grad_blobs_buf.from_torch(torch.zeros([BLOB_COUNT, 8]).cuda()) + self.blobs_buf.from_torch(blobs.detach()) + self.grad_image_buf.from_torch(grad_intensities.detach()) + self.device.render_context.wait_for_cuda() + vars = self.backward_pass.globals.backward + vars.blobs = self.blobs_buf + vars.grad_blobs = self.grad_blobs_buf + vars.blob_count = BLOB_COUNT + vars.grad_image = self.grad_image_buf + vars.resolution = falcor.uint2(RESOLUTION, RESOLUTION) + self.backward_pass.execute(threads_x=RESOLUTION, threads_y=RESOLUTION) + self.device.render_context.wait_for_falcor() + return self.grad_blobs_buf.to_torch([BLOB_COUNT, 8], falcor.float32) + + +class Splat2DFunction(torch.autograd.Function): + @staticmethod + def forward(ctx, blobs): + image = splat2d.forward(blobs) + ctx.save_for_backward(blobs) + return image + + @staticmethod + def backward(ctx, grad_intensities): + blobs = ctx.saved_tensors[0] + grad_blobs = splat2d.backward(blobs, grad_intensities) + return grad_blobs + + +class Splat2DModule(torch.nn.Module): + def __init__(self): + super().__init__() + + def forward(self, blobs): + return Splat2DFunction.apply(blobs) + + +# create testbed instance with a window +testbed = falcor.Testbed(create_window=True, width=RESOLUTION, height=RESOLUTION) +testbed.show_ui = False +device = testbed.device + +# create a texture for displaying the result in the window +testbed.render_texture = device.create_texture( + format=falcor.ResourceFormat.RGB32Float, + width=RESOLUTION, + height=RESOLUTION, + mip_levels=1, + bind_flags=falcor.ResourceBindFlags.ShaderResource, +) + +# setup 2D splatting function +splat2d = Splat2D(device) +Splat2DFunction.splat2d = splat2d + +# setup gaussian parameters +blob_positions = torch.rand([BLOB_COUNT, 2]).cuda() +blob_scales = torch.log(torch.ones([BLOB_COUNT, 2]).cuda() * 0.005) +blob_rotations = torch.rand([BLOB_COUNT, 1]).cuda() * (2 * np.pi) +blob_colors = torch.rand([BLOB_COUNT, 3]).cuda() * 0.5 + 0.25 + +params = (blob_positions, blob_scales, blob_colors, blob_rotations) +for param in params: + param.requires_grad = True + +# load target image +image = Image.open(TARGET_IMAGE).resize([RESOLUTION, RESOLUTION]).convert("RGB") +target = np.asarray(image).astype(np.float32) / 255.0 +target_cuda = torch.from_numpy(target).cuda() + +# torch.autograd.gradcheck(Splat2DFunction.apply, (blobs), fast_mode=True) + +model = Splat2DModule() + + +def optimize(): + optimizer = torch.optim.Adam(params, lr=0.01) + sigmoid = torch.nn.Sigmoid() + for iteration in range(ITERATIONS): + optimizer.zero_grad() + + blobs = torch.concat( + ( + blob_positions, + torch.exp(blob_scales), + sigmoid(blob_colors), + blob_rotations, + ), + dim=1, + ) + image = model.forward(blobs) + loss = torch.nn.functional.l1_loss(image, target_cuda) + # loss = torch.nn.functional.mse_loss(image, target_cuda) + loss.backward() + optimizer.step() + + if iteration % 5 == 0: + print(f"iteration={iteration}, loss={loss.item()}") + render_image = image.detach() + # render_image = target_cuda.detach() + # render_image = torch.lerp(image.detach(), target_cuda.detach(), 0.5) + # render_image = torch.abs(image.detach() - target_cuda.detach()) + render_image = torch.pow(render_image, 2.2) + testbed.render_texture.from_numpy(render_image.cpu().numpy()) + testbed.frame() + if testbed.should_close: + break + + +# run optimization +optimize() + +# display image until window is closed +while not testbed.should_close: + testbed.frame() diff --git a/scripts/python/gaussian2d/splat2d.cs.slang b/scripts/python/gaussian2d/splat2d.cs.slang new file mode 100644 index 000000000..55a0dc26c --- /dev/null +++ b/scripts/python/gaussian2d/splat2d.cs.slang @@ -0,0 +1,127 @@ +#include "Utils/NVAPI.slangh" + +static const float kSkipDistance = 0.1; + +struct Blob : IDifferentiable { + float2 pos; + float2 scale; + float3 color; + float rotation; +}; + +[BackwardDifferentiable] +float2x2 inverse(float2x2 M) +{ + float2x2 inv; + float invdet = 1.0f / determinant(M); + inv[0][0] = M[1][1] * invdet; + inv[1][1] = M[0][0] * invdet; + inv[0][1] = -M[0][1] * invdet; + inv[1][0] = -M[1][0] * invdet; + return inv; +} + +[BackwardDifferentiable] +float2x2 rotation_matrix(float angle) +{ + float c = cos(angle); + float s = sin(angle); + return float2x2(c, -s, s, c); +} + +[BackwardDifferentiable] +float2x2 scale_matrix(float2 scale) +{ + return float2x2(scale.x, 0, 0, scale.y); +} + +[BackwardDifferentiable] +float gaussian(float2 x, float2x2 sigma) +{ + float2x2 sigma_inv = inverse(sigma); + return exp(-0.5 * dot(x, mul(sigma_inv, x))); +} + +[BackwardDifferentiable] +float3 eval(no_diff float2 pos, Blob blob) { + float2x2 R = rotation_matrix(blob.rotation); + float2x2 S = scale_matrix(max(blob.scale, 0.001)); + float2x2 sigma = mul(mul(R, S), mul(transpose(S), transpose(R))); + return gaussian(pos - blob.pos, sigma) * blob.color; +} + +struct Forward { + StructuredBuffer blobs; + uint blob_count; + RWStructuredBuffer image; + uint2 resolution; + + void forward(uint2 idx) + { + if (any(idx >= resolution)) + return; + + float2 pos = float2(idx) / resolution; + float3 color = float3(0); + for (uint i = 0; i < blob_count; ++i) { + // if (kSkipDistance > 0 && distance(pos, blobs[i].pos) > kSkipDistance) + // continue; + color += eval(pos, blobs[i]); + } + image[idx.y * resolution.x + idx.x] = color; + } +} + +struct Backward { + StructuredBuffer blobs; + RWByteAddressBuffer grad_blobs; + uint blob_count; + StructuredBuffer grad_image; + uint2 resolution; + + void write_grad(uint offset, float value) + { + float sum = WaveActiveSum(value); + if (WaveIsFirstLane()) + grad_blobs.InterlockedAddF32(offset, sum); + } + + void backward(uint2 idx) + { + if (any(idx >= resolution)) + return; + + no_diff float2 pos = float2(idx) / resolution; + + for (uint i = 0; i < blob_count; ++i) { + var d_blob = diffPair(blobs[i]); + float3 d_image = grad_image[idx.y * resolution.x + idx.x]; + if (kSkipDistance > 0 && distance(pos, blobs[i].pos) > kSkipDistance) + continue; + bwd_diff(eval)(pos, d_blob, d_image); + write_grad(i * 32, d_blob.d.pos.x); + write_grad(i * 32 + 4, d_blob.d.pos.y); + write_grad(i * 32 + 8, d_blob.d.scale.x); + write_grad(i * 32 + 12, d_blob.d.scale.y); + write_grad(i * 32 + 16, d_blob.d.color.r); + write_grad(i * 32 + 20, d_blob.d.color.g); + write_grad(i * 32 + 24, d_blob.d.color.b); + write_grad(i * 32 + 28, d_blob.d.rotation); + } + } +} + +ParameterBlock forward; +ParameterBlock backward; + +[numthreads(16, 16, 1)] +void forward_main(uint3 tid: SV_DispatchThreadID) +{ + forward.forward(tid.xy); +} + +[numthreads(16, 16, 1)] +void backward_main(uint3 tid: SV_DispatchThreadID) +{ + backward.backward(tid.xy); +} diff --git a/scripts/python/test_pytorch.py b/scripts/python/test_pytorch.py index 87f2aa250..4dd912f0c 100644 --- a/scripts/python/test_pytorch.py +++ b/scripts/python/test_pytorch.py @@ -1,12 +1,10 @@ import torch -import numpy as np import falcor -from falcor import * -from time import time import random -def createTensor(dim, offset, device): - data = torch.zeros(dim, dtype=torch.float32, device=torch.device('cpu')) + +def create_tensor(dim, offset, device): + data = torch.zeros(dim, dtype=torch.float32, device=torch.device("cpu")) for k in range(dim[0]): for j in range(dim[1]): for i in range(dim[2]): @@ -14,30 +12,33 @@ def createTensor(dim, offset, device): data[k][j][i] = idx + offset return data.to(device) -def testTensorToFalcor(device, test_pass, iterations=10): - print('Testing passing tensors to Falcor') + +def test_tensor_to_falcor(device, test_pass, iterations=10): + print("Testing passing tensors to Falcor") for offset in range(iterations): dim = random.sample(range(1, 32), 3) - data = createTensor(dim, offset, device) - #torch.cuda.synchronize(device) # Does not seem to be necessary + data = create_tensor(dim, offset, device) - res = test_pass.verifyData(uint3(dim[0],dim[1],dim[2]), offset, data) + res = test_pass.verifyData(falcor.uint3(dim[0], dim[1], dim[2]), offset, data) if not res: - raise RuntimeError(f'Test {offset} to pass tensor to Falcor failed') + raise RuntimeError(f"Test {offset} to pass tensor to Falcor failed") + -def testTensorFromFalcor(test_pass, iterations=10): - print('Testing passing tensors from Falcor') +def test_tensor_from_falcor(test_pass, iterations=10): + print("Testing passing tensors from Falcor") for offset in range(iterations): dim = random.sample(range(1, 32), 3) - data = test_pass.generateData(uint3(dim[0],dim[1],dim[2]), offset) + data = test_pass.generateData(falcor.uint3(dim[0], dim[1], dim[2]), offset) # Check the returned tensor if not isinstance(data, torch.Tensor): - raise RuntimeError('Expected torch.Tensor object') + raise RuntimeError("Expected torch.Tensor object") if not data.is_cuda or data.dtype != torch.float32: - raise RuntimeError('Expected CUDA float tensor') + raise RuntimeError("Expected CUDA float tensor") if list(data.size()) != dim: - raise RuntimeError(f'Unexpected tensor dimensions (dim {list(data.size())}, expected dim {dim})') + raise RuntimeError( + f"Unexpected tensor dimensions (dim {list(data.size())}, expected dim {dim})" + ) d = data.to("cpu") count = 0 @@ -50,32 +51,38 @@ def testTensorFromFalcor(test_pass, iterations=10): elemCount = dim[0] * dim[1] * dim[2] if count != elemCount: - raise RuntimeError(f'Unexpected tensor data ({counter} out of {elemCount} values correct)", , elemCount)') + raise RuntimeError( + f'Unexpected tensor data ({count} out of {elemCount} values correct)", , elemCount)' + ) + def main(): random.seed(1) # Create torch CUDA device - print('Creating CUDA device') + print("Creating CUDA device") if not torch.cuda.is_available(): - raise RuntimeError('CUDA not available') + raise RuntimeError("CUDA not available") device = torch.device("cuda:0") print(device) torch.set_default_tensor_type(torch.cuda.FloatTensor) # Create testbed testbed = falcor.Testbed() - render_graph = testbed.createRenderGraph("TestPybind") - test_pass = render_graph.createPass("test_pybind_pass", "TestPyTorchPass", {}) - testbed.renderGraph = render_graph + render_graph = testbed.create_render_graph("TestPybind") + test_pass = render_graph.create_pass( + "test_pybind_pass", "TestPyTorchPass", {} + ) + testbed.render_graph = render_graph # Test passing tensors to Falcor - testTensorToFalcor(device, test_pass, 100) + test_tensor_to_falcor(device, test_pass, 100) # Test passing tensors from Falcor - testTensorFromFalcor(test_pass, 100) + test_tensor_from_falcor(test_pass, 100) + + print("SUCCESS!") - print('SUCCESS!') -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/scripts/python/test_replace_material.py b/scripts/python/test_replace_material.py new file mode 100644 index 000000000..802ca1817 --- /dev/null +++ b/scripts/python/test_replace_material.py @@ -0,0 +1,41 @@ +import falcor + +def setup_renderpass(testbed): + render_graph = testbed.create_render_graph("PathTracer") + render_graph.create_pass("PathTracer", "PathTracer", {'samplesPerPixel': 1}) + render_graph.create_pass("VBufferRT", "VBufferRT", {'samplePattern': 'Stratified', 'sampleCount': 16, 'useAlphaTest': True}) + render_graph.create_pass("AccumulatePass", "AccumulatePass", {'enabled': True, 'precisionMode': 'Single'}) + render_graph.add_edge("VBufferRT.vbuffer", "PathTracer.vbuffer") + render_graph.add_edge("PathTracer.color", "AccumulatePass.input") + render_graph.mark_output("AccumulatePass.output") + testbed.render_graph = render_graph + +def main(): + falcor.Logger.verbosity = falcor.Logger.Level.Info + + scene_path = 'test_scenes/cornell_box.pyscene' + + # Create device and setup renderer. + device = falcor.Device(type=falcor.DeviceType.D3D12, gpu=0, enable_debug_layer=False) + testbed = falcor.Testbed(width=1920, height=1080, create_window=True, device=device) + setup_renderpass(testbed) + + # Load scene. + testbed.load_scene(scene_path) + testbed.frame() + + # Create replacement materials. + mat1 = falcor.PBRTDiffuseMaterial(device, "PBRT diffuse") + mat1.load_texture(falcor.MaterialTextureSlot.BaseColor, 'test_scenes/textures/checker_tile_base_color.png') + mat2 = falcor.NeuralMaterial(device, "Neural material", "test_scenes/materials/neural/material_4.json") + + # Replace materials. + print('Replacing materials ...') + testbed.scene.replace_material(0, mat1) + testbed.scene.replace_material(1, mat2) + + testbed.run() + + +if __name__ == "__main__": + main() diff --git a/scripts/python/ui/ui_demo.py b/scripts/python/ui/ui_demo.py new file mode 100644 index 000000000..939dd3b4e --- /dev/null +++ b/scripts/python/ui/ui_demo.py @@ -0,0 +1,354 @@ +""" +This is a simple example of using UI elements in Python. +""" + +import falcor +from falcor import ui +import threading + + +class WidgetWindow: + """ + Demonstrates all the widgets currently available in Falcor. + """ + + def __init__(self, screen: ui.Screen): + self.window = window = ui.Window( + parent=screen, title="Widgets", position=[900, 10], size=[500, 1000] + ) + self.widgets = [] + + basic_group = ui.Group(parent=window, label="Basic widgets") + + text = ui.Text(parent=basic_group, text="Text widget\nwith multiple lines\nof plain text") + + button = ui.Button( + parent=basic_group, + label="Button widget", + callback=lambda: print("Button clicked!"), + ) + + checkbox = ui.Checkbox( + parent=basic_group, + label="Checkbox widget", + value=True, + change_callback=lambda: print( + f"Checkbox value changed to {checkbox.value}" + ), + ) + + combobox = ui.Combobox( + parent=basic_group, + label="Combobox widget", + items=["Item 1", "Item 2", "Item 3"], + value=1, + change_callback=lambda: print( + f"Combobox value changed to {combobox.value} ({combobox.items[combobox.value]})" + ), + ) + + progress_bar = ui.ProgressBar(parent=basic_group, fraction=0.5) + + self.widgets += [text, button, checkbox, combobox, progress_bar] + + drag_group = ui.Group(parent=window, label="Drag widgets") + + drag_int = ui.DragInt( + parent=drag_group, + label="DragInt widget", + value=10, + change_callback=lambda: print(f"DragInt value changed to {drag_int.value}"), + ) + + drag_int2 = ui.DragInt2( + parent=drag_group, + label="DragInt2 widget", + value=[10, 20], + change_callback=lambda: print( + f"DragInt2 value changed to {drag_int2.value}" + ), + ) + + drag_int3 = ui.DragInt3( + parent=drag_group, + label="DragInt3 widget", + value=[10, 20, 30], + change_callback=lambda: print( + f"DragInt3 value changed to {drag_int3.value}" + ), + ) + + drag_int4 = ui.DragInt4( + parent=drag_group, + label="DragInt4 widget", + value=[10, 20, 30, 40], + change_callback=lambda: print( + f"DragInt4 value changed to {drag_int4.value}" + ), + ) + + drag_float = ui.DragFloat( + parent=drag_group, + label="DragFloat widget", + value=0.5, + speed=0.1, + change_callback=lambda: print( + f"DragFloat value changed to {drag_float.value}" + ), + ) + + drag_float2 = ui.DragFloat2( + parent=drag_group, + label="DragFloat2 widget", + value=[0.5, 0.6], + speed=0.1, + change_callback=lambda: print( + f"DragFloat2 value changed to {drag_float2.value}" + ), + ) + + drag_float3 = ui.DragFloat3( + parent=drag_group, + label="DragFloat3 widget", + value=[0.5, 0.6, 0.7], + speed=0.1, + change_callback=lambda: print( + f"DragFloat3 value changed to {drag_float3.value}" + ), + ) + + drag_float4 = ui.DragFloat4( + parent=drag_group, + label="DragFloat4 widget", + value=[0.5, 0.6, 0.7, 0.8], + speed=0.1, + change_callback=lambda: print( + f"DragFloat4 value changed to {drag_float4.value}" + ), + ) + + self.widgets += [ + drag_int, + drag_int2, + drag_int3, + drag_int4, + drag_float, + drag_float2, + drag_float3, + drag_float4, + ] + + slider_group = ui.Group(parent=window, label="Slider widgets") + + slider_int = ui.SliderInt( + parent=slider_group, + label="SliderInt widget", + value=0, + min=-100, + max=100, + change_callback=lambda: print( + f"SliderInt value changed to {slider_int.value}" + ), + ) + + slider_int2 = ui.SliderInt2( + parent=slider_group, + label="SliderInt2 widget", + value=falcor.int2(0), + min=-100, + max=100, + change_callback=lambda: print( + f"SliderInt2 value changed to {slider_int2.value}" + ), + ) + + slider_int3 = ui.SliderInt3( + parent=slider_group, + label="SliderInt3 widget", + value=falcor.int3(0), + min=-100, + max=100, + change_callback=lambda: print( + f"SliderInt3 value changed to {slider_int3.value}" + ), + ) + + slider_int4 = ui.SliderInt4( + parent=slider_group, + label="SliderInt4 widget", + value=falcor.int4(0), + min=-100, + max=100, + change_callback=lambda: print( + f"SliderInt4 value changed to {slider_int4.value}" + ), + ) + + slider_float = ui.SliderFloat( + parent=slider_group, + label="SliderFloat widget", + value=0, + min=-100, + max=100, + change_callback=lambda: print( + f"SliderFloat value changed to {slider_float.value}" + ), + ) + + slider_float2 = ui.SliderFloat2( + parent=slider_group, + label="SliderFloat2 widget", + value=falcor.float2(0), + min=-100, + max=100, + change_callback=lambda: print( + f"SliderFloat2 value changed to {slider_float2.value}" + ), + ) + + slider_float3 = ui.SliderFloat3( + parent=slider_group, + label="SliderFloat3 widget", + value=falcor.float3(0), + min=-100, + max=100, + change_callback=lambda: print( + f"SliderFloat3 value changed to {slider_float3.value}" + ), + ) + + slider_float4 = ui.SliderFloat4( + parent=slider_group, + label="SliderFloat4 widget", + value=falcor.float4(0), + min=-100, + max=100, + change_callback=lambda: print( + f"SliderFloat4 value changed to {slider_float4.value}" + ), + ) + + self.widgets += [ + slider_int, + slider_int2, + slider_int3, + slider_int4, + slider_float, + slider_float2, + slider_float3, + slider_float4, + ] + + empty_group = ui.Group(parent=window, label="Empty group") + + self.widgets += [empty_group] + + ui.Button( + parent=window, + label="Disable all widgets", + callback=lambda: self.set_widgets_enabled(False), + ) + ui.Button( + parent=window, + label="Enable all widgets", + callback=lambda: self.set_widgets_enabled(True), + ) + + def set_widgets_enabled(self, enabled: bool): + for widget in self.widgets: + widget.enabled = enabled + + +class DemoWindow: + def __init__(self, screen: ui.Screen, widget_window: WidgetWindow): + self.widget_window = widget_window + self.window = window = ui.Window( + parent=screen, title="Demo Window", position=[300, 10], size=[500, 1000] + ) + + ui.Text(parent=window, text="\nControl the widget window from here:") + + # Buttons to show/close widget window + ui.Button( + parent=window, + label="Show widget window", + callback=lambda: self.widget_window.window.show(), + ) + ui.Button( + parent=window, + label="Close widget window", + callback=lambda: self.widget_window.window.close(), + ) + + ui.Text(parent=window, text="\nControl the position/size of this window:") + + # Create button to move this window + def move_window(): + self.window.position = [50, 50] + + ui.Button(parent=window, label="Move window to [50, 50]", callback=move_window) + + # Create button to resize this window, specify callback after creating button + def resize_window(): + self.window.size = [500, 500] + + button = ui.Button(parent=window, label="Resize window to [500, 500]") + button.callback = resize_window + + ui.Text(parent=window, text="\nA simple counter button:") + + # Simple counter button + self.counter = 0 + self.counter_button = ui.Button( + parent=window, label="Clicked 0 times", callback=self.count + ) + + ui.Text( + parent=window, text="\nA simple button to start/stop some background task:" + ) + + # Start/stop button that changes label + self.running = False + self.timer = None + self.start_stop_button = ui.Button( + parent=window, label="Start", callback=self.start_stop + ) + + # Progress bar + self.progress_bar = ui.ProgressBar(parent=window, fraction=0) + + def count(self): + self.counter += 1 + self.counter_button.label = f"Clicked {self.counter} times" + + def timer_callback(self): + self.timer = None + if self.running: + self.progress_bar.fraction += 0.01 + if self.progress_bar.fraction < 1: + self.timer = threading.Timer(0.05, self.timer_callback) + self.timer.start() + + def start_stop(self): + if self.running: + self.running = False + self.start_stop_button.label = "Start" + self.progress_bar.fraction = 0 + if self.timer: + self.timer.cancel() + self.timer = None + else: + self.running = True + self.start_stop_button.label = "Stop" + self.timer = threading.Timer(0.05, self.timer_callback) + self.timer.start() + + +# Create testbed and windows +testbed = falcor.Testbed(create_window=True, width=1920, height=1080) +widget_window = WidgetWindow(screen=testbed.screen) +demo_window = DemoWindow(screen=testbed.screen, widget_window=widget_window) + +# We need to run frame-by-frame to have Python's timer working +while not testbed.should_close: + testbed.frame() diff --git a/tests/image_tests/helpers.py b/tests/image_tests/helpers.py index 902274b95..05de983e3 100644 --- a/tests/image_tests/helpers.py +++ b/tests/image_tests/helpers.py @@ -1,3 +1,5 @@ +import falcor + def render_frames(m, name, frames=[1], framerate=60, resolution=[640,360]): m.resizeFrameBuffer(*resolution) m.ui = False @@ -12,4 +14,6 @@ def render_frames(m, name, frames=[1], framerate=60, resolution=[640,360]): frame += 1 m.clock.frame = frame m.renderFrame() + if "IMAGE_TEST_RUN_ONLY" in falcor.__dict__: + continue m.frameCapture.capture() diff --git a/tests/image_tests/renderpasses/graphs/WARDiffPathTracerMaterialFwd.py b/tests/image_tests/renderpasses/graphs/WARDiffPathTracerMaterialFwd.py new file mode 100644 index 000000000..2084bf869 --- /dev/null +++ b/tests/image_tests/renderpasses/graphs/WARDiffPathTracerMaterialFwd.py @@ -0,0 +1,27 @@ +from falcor import * + +def render_graph_WARDiffPathTracer(): + g = RenderGraph("WARDiffPathTracer") + WARDiffPathTracer = createPass("WARDiffPathTracer", {"maxBounces": 3, "samplesPerPixel": 1, "diffMode": "ForwardDiffDebug", "diffVarName": "CBOX_BUNNY_MATERIAL"}) + g.addPass(WARDiffPathTracer, "WARDiffPathTracer") + + AccumulatePassPrimal = createPass("AccumulatePass", {"enabled": True, "precisionMode": "Single"}) + g.addPass(AccumulatePassPrimal, "AccumulatePassPrimal") + + AccumulatePassDiff = createPass("AccumulatePass", {"enabled": True, 'precisionMode': "Single"}) + g.addPass(AccumulatePassDiff, "AccumulatePassDiff") + ColorMapPassDiff = createPass("ColorMapPass", {"minValue": -4.0, "maxValue": 4.0, "autoRange": False}) + g.addPass(ColorMapPassDiff, "ColorMapPassDiff") + + g.addEdge("WARDiffPathTracer.color", "AccumulatePassPrimal.input") + g.addEdge("WARDiffPathTracer.dColor", "AccumulatePassDiff.input") + g.addEdge("AccumulatePassDiff.output", "ColorMapPassDiff.input") + + g.markOutput("AccumulatePassDiff.output") + g.markOutput("ColorMapPassDiff.output") + g.markOutput("AccumulatePassPrimal.output") + return g + +WARDiffPathTracer = render_graph_WARDiffPathTracer() +try: m.addGraph(WARDiffPathTracer) +except NameError: None diff --git a/tests/image_tests/renderpasses/graphs/WARDiffPathTracerTranslationBwd.py b/tests/image_tests/renderpasses/graphs/WARDiffPathTracerTranslationBwd.py new file mode 100644 index 000000000..ea2ae4cde --- /dev/null +++ b/tests/image_tests/renderpasses/graphs/WARDiffPathTracerTranslationBwd.py @@ -0,0 +1,26 @@ +from falcor import * + +def render_graph_WARDiffPathTracer(): + g = RenderGraph("WARDiffPathTracer") + WARDiffPathTracer = createPass("WARDiffPathTracer", {"maxBounces": 0, "samplesPerPixel": 1, "diffMode": "BackwardDiffDebug", "diffVarName": "CBOX_BUNNY_TRANSLATION"}) + g.addPass(WARDiffPathTracer, "WARDiffPathTracer") + + AccumulatePassPrimal = createPass("AccumulatePass", {"enabled": True, "precisionMode": "Single"}) + g.addPass(AccumulatePassPrimal, "AccumulatePassPrimal") + + AccumulatePassDiff = createPass("AccumulatePass", {"enabled": True, 'precisionMode': "Single"}) + g.addPass(AccumulatePassDiff, "AccumulatePassDiff") + ColorMapPassDiff = createPass("ColorMapPass", {"minValue": -4.0, "maxValue": 4.0, "autoRange": False}) + g.addPass(ColorMapPassDiff, "ColorMapPassDiff") + + g.addEdge("WARDiffPathTracer.color", "AccumulatePassPrimal.input") + g.addEdge("WARDiffPathTracer.dColor", "AccumulatePassDiff.input") + g.addEdge("AccumulatePassDiff.output", "ColorMapPassDiff.input") + + g.markOutput("AccumulatePassDiff.output") + g.markOutput("ColorMapPassDiff.output") + return g + +WARDiffPathTracer = render_graph_WARDiffPathTracer() +try: m.addGraph(WARDiffPathTracer) +except NameError: None diff --git a/tests/image_tests/renderpasses/graphs/WARDiffPathTracerTranslationFwd.py b/tests/image_tests/renderpasses/graphs/WARDiffPathTracerTranslationFwd.py new file mode 100644 index 000000000..25336b0a1 --- /dev/null +++ b/tests/image_tests/renderpasses/graphs/WARDiffPathTracerTranslationFwd.py @@ -0,0 +1,27 @@ +from falcor import * + +def render_graph_WARDiffPathTracer(): + g = RenderGraph("WARDiffPathTracer") + WARDiffPathTracer = createPass("WARDiffPathTracer", {"maxBounces": 0, "samplesPerPixel": 1, "diffMode": "ForwardDiffDebug", "diffVarName": "CBOX_BUNNY_TRANSLATION"}) + g.addPass(WARDiffPathTracer, "WARDiffPathTracer") + + AccumulatePassPrimal = createPass("AccumulatePass", {"enabled": True, "precisionMode": "Single"}) + g.addPass(AccumulatePassPrimal, "AccumulatePassPrimal") + + AccumulatePassDiff = createPass("AccumulatePass", {"enabled": True, 'precisionMode': "Single"}) + g.addPass(AccumulatePassDiff, "AccumulatePassDiff") + ColorMapPassDiff = createPass("ColorMapPass", {"minValue": -4.0, "maxValue": 4.0, "autoRange": False}) + g.addPass(ColorMapPassDiff, "ColorMapPassDiff") + + g.addEdge("WARDiffPathTracer.color", "AccumulatePassPrimal.input") + g.addEdge("WARDiffPathTracer.dColor", "AccumulatePassDiff.input") + g.addEdge("AccumulatePassDiff.output", "ColorMapPassDiff.input") + + g.markOutput("AccumulatePassDiff.output") + g.markOutput("ColorMapPassDiff.output") + g.markOutput("AccumulatePassPrimal.output") + return g + +WARDiffPathTracer = render_graph_WARDiffPathTracer() +try: m.addGraph(WARDiffPathTracer) +except NameError: None diff --git a/tests/image_tests/renderpasses/test_DLSSPass.py b/tests/image_tests/renderpasses/test_DLSSPass.py index 9ffd00384..0b3417920 100644 --- a/tests/image_tests/renderpasses/test_DLSSPass.py +++ b/tests/image_tests/renderpasses/test_DLSSPass.py @@ -1,4 +1,6 @@ IMAGE_TEST = { + # The test uses GBufferRaster which is not supported on Vulkan. + "device_types": ["d3d12"], 'tolerance': 1e-7 } @@ -13,7 +15,7 @@ from falcor import * m.addGraph(g) -m.loadScene('Cerberus/Standard/Cerberus.pyscene') +m.loadScene('test_scenes/cesium_man/CesiumMan.pyscene') # default render_frames(m, 'default', frames=[64, 128, 192, 256]) diff --git a/tests/image_tests/renderpasses/test_MVecRT.py b/tests/image_tests/renderpasses/test_MVecRT.py index 276bd2eae..21adda295 100644 --- a/tests/image_tests/renderpasses/test_MVecRT.py +++ b/tests/image_tests/renderpasses/test_MVecRT.py @@ -1,10 +1,14 @@ +IMAGE_TEST = { + "device_types": ["d3d12", "vulkan"] +} + import sys sys.path.append('..') from helpers import render_frames from graphs.MVecRT import MVecRT as g from falcor import * -sceneFile = 'Cerberus/Standard/Cerberus.pyscene' +sceneFile = 'test_scenes/cesium_man/CesiumMan.pyscene' m.addGraph(g) m.loadScene(sceneFile) diff --git a/tests/image_tests/renderpasses/test_MVecRaster.py b/tests/image_tests/renderpasses/test_MVecRaster.py index b74fb140b..6c61c5019 100644 --- a/tests/image_tests/renderpasses/test_MVecRaster.py +++ b/tests/image_tests/renderpasses/test_MVecRaster.py @@ -4,7 +4,7 @@ from graphs.MVecRaster import MVecRaster as g from falcor import * -sceneFile = 'Cerberus/Standard/Cerberus.pyscene' +sceneFile = 'test_scenes/cesium_man/CesiumMan.pyscene' m.addGraph(g) m.loadScene(sceneFile) diff --git a/tests/image_tests/renderpasses/test_PathTracer.py b/tests/image_tests/renderpasses/test_PathTracer.py index 030ea2cfb..7c7a8238a 100644 --- a/tests/image_tests/renderpasses/test_PathTracer.py +++ b/tests/image_tests/renderpasses/test_PathTracer.py @@ -1,3 +1,7 @@ +IMAGE_TEST = { + "device_types": ["d3d12", "vulkan"] +} + import sys sys.path.append('..') from helpers import render_frames @@ -8,6 +12,7 @@ m.loadScene('Arcade/Arcade.pyscene') # default +g["PathTracer"].set_properties({"useSER": False}) render_frames(m, 'default', frames=[128]) exit() diff --git a/tests/image_tests/renderpasses/test_PathTracerAdaptive.py b/tests/image_tests/renderpasses/test_PathTracerAdaptive.py index 99ae6e047..ae2f75a41 100644 --- a/tests/image_tests/renderpasses/test_PathTracerAdaptive.py +++ b/tests/image_tests/renderpasses/test_PathTracerAdaptive.py @@ -1,3 +1,8 @@ +IMAGE_TEST = { + "platforms": ["windows-x86_64"], + "device_types": ["d3d12", "vulkan"] +} + import sys sys.path.append('..') from helpers import render_frames diff --git a/tests/image_tests/renderpasses/test_PathTracerDielectrics.py b/tests/image_tests/renderpasses/test_PathTracerDielectrics.py index 41ba01d4f..222186579 100644 --- a/tests/image_tests/renderpasses/test_PathTracerDielectrics.py +++ b/tests/image_tests/renderpasses/test_PathTracerDielectrics.py @@ -1,3 +1,7 @@ +IMAGE_TEST = { + "device_types": ["d3d12", "vulkan"] +} + import sys sys.path.append('..') from helpers import render_frames diff --git a/tests/image_tests/renderpasses/test_PathTracerMaterials.py b/tests/image_tests/renderpasses/test_PathTracerMaterials.py index 54aa21403..ab4dcae60 100644 --- a/tests/image_tests/renderpasses/test_PathTracerMaterials.py +++ b/tests/image_tests/renderpasses/test_PathTracerMaterials.py @@ -1,5 +1,10 @@ +IMAGE_TEST = { + "device_types": ["d3d12", "vulkan"] +} + import sys -sys.path.append('..') + +sys.path.append("..") from helpers import render_frames from graphs.PathTracerMaterials import PathTracerMaterials as g from falcor import * @@ -7,23 +12,24 @@ m.addGraph(g) # Test variations of the standard material -m.loadScene('test_scenes/material_test.pyscene') -render_frames(m, 'default', frames=[1,256]) +m.loadScene("test_scenes/material_test.pyscene") +render_frames(m, "default", frames=[1, 256]) # Test different material types -m.loadScene('test_scenes/materials/materials.pyscene') -render_frames(m, 'types', frames=[1,256]) +m.loadScene("test_scenes/materials/materials.pyscene") +render_frames(m, "types", frames=[1, 256]) # Test for light leaks -m.loadScene('test_scenes/materials/light_leaks.pyscene') -render_frames(m, 'leaks', frames=[1,256]) +m.loadScene("test_scenes/materials/light_leaks.pyscene") +render_frames(m, "leaks", frames=[1, 256]) # Test alpha testing -m.loadScene('test_scenes/alpha_test/alpha_test.pyscene') -render_frames(m, 'alpha', frames=[1,64]) +m.loadScene("test_scenes/alpha_test/alpha_test.pyscene") +render_frames(m, "alpha", frames=[1, 64]) # Test disabling alpha testing on secondary hits -g.updatePass('PathTracer', {'samplesPerPixel': 1, 'maxSurfaceBounces': 3, 'useAlphaTest': False}) -render_frames(m, 'noalpha', frames=[1,64]) +g["PathTracer"].reset() +g["PathTracer"].set_properties({"useAlphaTest": False}) +render_frames(m, "noalpha", frames=[1, 64]) exit() diff --git a/tests/image_tests/renderpasses/test_RTXDI.py b/tests/image_tests/renderpasses/test_RTXDI.py index de0cc2da4..f1b552f71 100644 --- a/tests/image_tests/renderpasses/test_RTXDI.py +++ b/tests/image_tests/renderpasses/test_RTXDI.py @@ -1,3 +1,7 @@ +IMAGE_TEST = { + "device_types": ["d3d12", "vulkan"] +} + import sys sys.path.append('..') from helpers import render_frames diff --git a/tests/image_tests/renderpasses/test_SimplePostFX.py b/tests/image_tests/renderpasses/test_SimplePostFX.py index ed2799a8c..55bb12c86 100644 --- a/tests/image_tests/renderpasses/test_SimplePostFX.py +++ b/tests/image_tests/renderpasses/test_SimplePostFX.py @@ -1,3 +1,7 @@ +IMAGE_TEST = { + "device_types": ["d3d12", "vulkan"] +} + import sys sys.path.append('..') from helpers import render_frames diff --git a/tests/image_tests/renderpasses/test_Skinning.py b/tests/image_tests/renderpasses/test_Skinning.py index 45d0cbc4c..2ef16c57d 100644 --- a/tests/image_tests/renderpasses/test_Skinning.py +++ b/tests/image_tests/renderpasses/test_Skinning.py @@ -1,3 +1,7 @@ +IMAGE_TEST = { + "device_types": ["d3d12", "vulkan"] +} + import sys sys.path.append('..') from helpers import render_frames @@ -5,7 +9,7 @@ from falcor import * m.addGraph(g) -m.loadScene('Cerberus/Standard/Cerberus.pyscene') +m.loadScene('test_scenes/cesium_man/CesiumMan.pyscene') # default render_frames(m, 'default', frames=[1,16,64]) diff --git a/tests/image_tests/renderpasses/test_VBufferRT.py b/tests/image_tests/renderpasses/test_VBufferRT.py index 0dc939ae5..7d41825b6 100644 --- a/tests/image_tests/renderpasses/test_VBufferRT.py +++ b/tests/image_tests/renderpasses/test_VBufferRT.py @@ -1,3 +1,7 @@ +IMAGE_TEST = { + "device_types": ["d3d12", "vulkan"] +} + import sys sys.path.append('..') from helpers import render_frames diff --git a/tests/image_tests/renderpasses/test_VBufferRTInline.py b/tests/image_tests/renderpasses/test_VBufferRTInline.py index 5c6591012..7516a64e0 100644 --- a/tests/image_tests/renderpasses/test_VBufferRTInline.py +++ b/tests/image_tests/renderpasses/test_VBufferRTInline.py @@ -1,4 +1,5 @@ IMAGE_TEST = { + "device_types": ["d3d12", "vulkan"], 'tolerance': 5e-7 } diff --git a/tests/image_tests/renderpasses/test_WARDiffPathTracerMaterialFwd.py b/tests/image_tests/renderpasses/test_WARDiffPathTracerMaterialFwd.py new file mode 100644 index 000000000..899940570 --- /dev/null +++ b/tests/image_tests/renderpasses/test_WARDiffPathTracerMaterialFwd.py @@ -0,0 +1,18 @@ +IMAGE_TEST = { + 'tolerance': 1e-8 +} + +import sys +sys.path.append('..') +from helpers import render_frames +from graphs.WARDiffPathTracerMaterialFwd import WARDiffPathTracer as g +from falcor import * + +m.addGraph(g) +flags = SceneBuilderFlags.DontMergeMaterials | SceneBuilderFlags.RTDontMergeDynamic | SceneBuilderFlags.DontOptimizeMaterials +m.loadScene("test_scenes/bunny_war_diff_pt.pyscene", buildFlags=flags) + +# default +render_frames(m, 'default', frames=[1024], resolution=[512, 512]) + +exit() diff --git a/tests/image_tests/renderpasses/test_WARDiffPathTracerTranslationBwd.py b/tests/image_tests/renderpasses/test_WARDiffPathTracerTranslationBwd.py new file mode 100644 index 000000000..c925872b5 --- /dev/null +++ b/tests/image_tests/renderpasses/test_WARDiffPathTracerTranslationBwd.py @@ -0,0 +1,18 @@ +IMAGE_TEST = { + 'tolerance': 1e-8 +} + +import sys +sys.path.append('..') +from helpers import render_frames +from graphs.WARDiffPathTracerTranslationBwd import WARDiffPathTracer as g +from falcor import * + +m.addGraph(g) +flags = SceneBuilderFlags.DontMergeMaterials | SceneBuilderFlags.RTDontMergeDynamic | SceneBuilderFlags.DontOptimizeMaterials +m.loadScene("test_scenes/bunny_war_diff_pt.pyscene", buildFlags=flags) + +# default +render_frames(m, 'default', frames=[1024], resolution=[512, 512]) + +exit() diff --git a/tests/image_tests/renderpasses/test_WARDiffPathTracerTranslationFwd.py b/tests/image_tests/renderpasses/test_WARDiffPathTracerTranslationFwd.py new file mode 100644 index 000000000..20cc50041 --- /dev/null +++ b/tests/image_tests/renderpasses/test_WARDiffPathTracerTranslationFwd.py @@ -0,0 +1,18 @@ +IMAGE_TEST = { + 'tolerance': 1e-8 +} + +import sys +sys.path.append('..') +from helpers import render_frames +from graphs.WARDiffPathTracerTranslationFwd import WARDiffPathTracer as g +from falcor import * + +m.addGraph(g) +flags = SceneBuilderFlags.DontMergeMaterials | SceneBuilderFlags.RTDontMergeDynamic | SceneBuilderFlags.DontOptimizeMaterials +m.loadScene("test_scenes/bunny_war_diff_pt.pyscene", buildFlags=flags) + +# default +render_frames(m, 'default', frames=[1024], resolution=[512, 512]) + +exit() diff --git a/tests/image_tests/renderscripts/test_SceneDebugger.py b/tests/image_tests/renderscripts/test_SceneDebugger.py index bab90087d..3132aca25 100644 --- a/tests/image_tests/renderscripts/test_SceneDebugger.py +++ b/tests/image_tests/renderscripts/test_SceneDebugger.py @@ -1,3 +1,7 @@ +IMAGE_TEST = { + "device_types": ["d3d12", "vulkan"] +} + import sys sys.path.append('..') from falcor import * diff --git a/tests/image_tests/scene/scenes/NDSDFGrid.pyscene b/tests/image_tests/scene/scenes/NDSDFGrid.pyscene index f86090aa5..59c00203e 100644 --- a/tests/image_tests/scene/scenes/NDSDFGrid.pyscene +++ b/tests/image_tests/scene/scenes/NDSDFGrid.pyscene @@ -20,8 +20,3 @@ camera.position = float3(1, 1, 1) camera.target = float3(0, 0, 0) camera.up = float3(0, 1, 0) sceneBuilder.addCamera(camera) - -# Setup envmap - -sceneBuilder.envMap = EnvMap("Arcade/BlueSky.png") -sceneBuilder.envMap.intensity = 1 \ No newline at end of file diff --git a/tests/image_tests/scene/scenes/SDFSBS.pyscene b/tests/image_tests/scene/scenes/SDFSBS.pyscene index 335b410e7..ade35259d 100644 --- a/tests/image_tests/scene/scenes/SDFSBS.pyscene +++ b/tests/image_tests/scene/scenes/SDFSBS.pyscene @@ -20,8 +20,3 @@ camera.position = float3(1, 1, 1) camera.target = float3(0, 0, 0) camera.up = float3(0, 1, 0) sceneBuilder.addCamera(camera) - -# Setup envmap - -sceneBuilder.envMap = EnvMap("Arcade/BlueSky.png") -sceneBuilder.envMap.intensity = 1 \ No newline at end of file diff --git a/tests/image_tests/scene/scenes/SDFSVO.pyscene b/tests/image_tests/scene/scenes/SDFSVO.pyscene index 6785cac88..a6c97aa9d 100644 --- a/tests/image_tests/scene/scenes/SDFSVO.pyscene +++ b/tests/image_tests/scene/scenes/SDFSVO.pyscene @@ -20,8 +20,3 @@ camera.position = float3(1, 1, 1) camera.target = float3(0, 0, 0) camera.up = float3(0, 1, 0) sceneBuilder.addCamera(camera) - -# Setup envmap - -sceneBuilder.envMap = EnvMap("Arcade/BlueSky.png") -sceneBuilder.envMap.intensity = 1 \ No newline at end of file diff --git a/tests/image_tests/scene/scenes/SDFSVS.pyscene b/tests/image_tests/scene/scenes/SDFSVS.pyscene index 513505e75..d802c673c 100644 --- a/tests/image_tests/scene/scenes/SDFSVS.pyscene +++ b/tests/image_tests/scene/scenes/SDFSVS.pyscene @@ -20,8 +20,3 @@ camera.position = float3(1, 1, 1) camera.target = float3(0, 0, 0) camera.up = float3(0, 1, 0) sceneBuilder.addCamera(camera) - -# Setup envmap - -sceneBuilder.envMap = EnvMap("Arcade/BlueSky.png") -sceneBuilder.envMap.intensity = 1 \ No newline at end of file diff --git a/tests/image_tests/scene/scenes/Volumes.pyscene b/tests/image_tests/scene/scenes/Volumes.pyscene deleted file mode 100644 index 7c5a24d81..000000000 --- a/tests/image_tests/scene/scenes/Volumes.pyscene +++ /dev/null @@ -1,24 +0,0 @@ -# Create volumes - -sphereVolume = GridVolume('Sphere') -sphereVolume.densityGrid = Grid.createSphere(1.0, 0.01) -sphereVolume.densityScale = 0.5 -sceneBuilder.addGridVolume(sphereVolume) - -boxVolume = GridVolume('Box') -boxVolume.densityGrid = Grid.createBox(1.0, 1.0, 1.0, 0.01) -boxVolume.densityScale = 2.5 -sceneBuilder.addGridVolume(boxVolume) - -# Create camera - -camera = Camera() -camera.position = float3(1, 1, 1) -camera.target = float3(0, 0, 0) -camera.up = float3(0, 1, 0) -sceneBuilder.addCamera(camera) - -# Setup envmap - -sceneBuilder.envMap = EnvMap("Arcade/BlueSky.png") -sceneBuilder.envMap.intensity = 1 diff --git a/tests/image_tests/scene/scenes/smoke.pyscene b/tests/image_tests/scene/scenes/smoke.pyscene deleted file mode 100644 index adbfc4eaf..000000000 --- a/tests/image_tests/scene/scenes/smoke.pyscene +++ /dev/null @@ -1,20 +0,0 @@ -# Create volumes - -smokeVolume = GridVolume('smoke') -smokeVolume.loadGrid(GridVolume.GridSlot.Density, 'test_scenes/volumes/smoke.vdb', 'density') -smokeVolume.densityScale = 0.5 -smokeVolume.albedo = float3(0.5, 0.5, 0.5) -sceneBuilder.addGridVolume(smokeVolume) - -# Create camera - -camera = Camera() -camera.position = float3(25, 0, 55) -camera.target = float3(0, 25, 0) -camera.up = float3(0, 1, 0) -sceneBuilder.addCamera(camera) - -# Setup envmap - -sceneBuilder.envMap = EnvMap("Arcade/BlueSky.png") -sceneBuilder.envMap.intensity = 1.5 diff --git a/tests/image_tests/scene/test_CameraAnimation.py b/tests/image_tests/scene/test_CameraAnimation.py index 259a36782..72c87384b 100644 --- a/tests/image_tests/scene/test_CameraAnimation.py +++ b/tests/image_tests/scene/test_CameraAnimation.py @@ -5,7 +5,7 @@ from falcor import * m.addGraph(g) -m.loadScene("grey_and_white_room/grey_and_white_room.fbx") +m.loadScene("test_scenes/grey_and_white_room/grey_and_white_room.fbx") # default render_frames(m, 'default', frames=[1,16,64,128,256]) diff --git a/tests/image_tests/scene/test_SceneCache.py b/tests/image_tests/scene/test_SceneCache.py index 2d33f6835..43287b50f 100644 --- a/tests/image_tests/scene/test_SceneCache.py +++ b/tests/image_tests/scene/test_SceneCache.py @@ -1,6 +1,5 @@ import sys sys.path.append('..') -import os from helpers import render_frames from graphs.SceneDebugger import SceneDebugger as SceneDebuggerGraph from graphs.PathTracer import PathTracer as PathTracerGraph @@ -15,18 +14,18 @@ render_frames(m, 'arcade.cached', frames=[64]) # grey_and_white_room -m.loadScene('grey_and_white_room/grey_and_white_room.fbx', SceneBuilderFlags.RebuildCache) +m.loadScene('test_scenes/grey_and_white_room/grey_and_white_room.fbx', SceneBuilderFlags.RebuildCache) render_frames(m, 'grey_and_white_room', frames=[64]) -m.loadScene('grey_and_white_room/grey_and_white_room.fbx', SceneBuilderFlags.UseCache) +m.loadScene('test_scenes/grey_and_white_room/grey_and_white_room.fbx', SceneBuilderFlags.UseCache) render_frames(m, 'grey_and_white_room.cached', frames=[64]) m.removeGraph(PathTracerGraph) m.addGraph(SceneDebuggerGraph) # volumes -m.loadScene(os.path.abspath('scenes/Volumes.pyscene'), SceneBuilderFlags.RebuildCache) +m.loadScene('test_scenes/two_volumes.pyscene', SceneBuilderFlags.RebuildCache) render_frames(m, 'volumes', frames=[1]) -m.loadScene(os.path.abspath('scenes/Volumes.pyscene'), SceneBuilderFlags.UseCache) +m.loadScene('test_scenes/two_volumes.pyscene', SceneBuilderFlags.UseCache) render_frames(m, 'volumes.cached', frames=[1]) exit() diff --git a/tests/image_tests/scene/test_Volumes.py b/tests/image_tests/scene/test_Volumes.py index 61d7125a7..37358deec 100644 --- a/tests/image_tests/scene/test_Volumes.py +++ b/tests/image_tests/scene/test_Volumes.py @@ -1,12 +1,11 @@ import sys sys.path.append('..') -import os from helpers import render_frames from graphs.SceneDebugger import SceneDebugger as g from falcor import * m.addGraph(g) -m.loadScene(os.path.abspath('scenes/Volumes.pyscene')) +m.loadScene('test_scenes/two_volumes.pyscene') # default render_frames(m, 'default', frames=[64]) diff --git a/tests/python_tests/core/test_device.py b/tests/python_tests/core/test_device.py index 759017790..45395f8fe 100644 --- a/tests/python_tests/core/test_device.py +++ b/tests/python_tests/core/test_device.py @@ -1,29 +1,178 @@ +import sys import os import unittest import falcor +import numpy as np + +sys.path.append(os.path.dirname(os.path.dirname(os.path.relpath(__file__)))) +from helpers import for_each_device_type -if os.name == 'nt': - DEVICE_TYPES=[falcor.DeviceType.D3D12, falcor.DeviceType.Vulkan] -else: - DEVICE_TYPES=[falcor.DeviceType.Vulkan] class TestDevice(unittest.TestCase): + @for_each_device_type + def test_device(self, device: falcor.Device): + print( + f""" +info.adapter_name={device.info.adapter_name} +info.api_name={device.info.api_name} +limits.max_compute_dispatch_thread_groups={device.limits.max_compute_dispatch_thread_groups} +limits.max_shader_visible_samplers={device.limits.max_shader_visible_samplers} +""" + ) + + @for_each_device_type + def test_buffer(self, device: falcor.Device): + a = device.create_buffer(256) + b = device.create_buffer(256) + + a_host = np.linspace(0, 255, 256, dtype=np.uint8) + b_host = np.zeros(256, dtype=np.uint8) + a.from_numpy(a_host) + b.from_numpy(b_host) + a_device = a.to_numpy() + b_device = b.to_numpy() + self.assertEqual(a_device.shape, (256,)) + self.assertEqual(a_device.dtype, np.uint8) + self.assertTrue(np.all(a_device == a_host)) + self.assertEqual(b_device.shape, (256,)) + self.assertEqual(b_device.dtype, np.uint8) + self.assertTrue(np.all(b_device == b_host)) + + device.render_context.copy_resource(b, a) + b_device = b.to_numpy() + self.assertTrue(np.all(b_device == a_host)) + + @for_each_device_type + def test_typed_buffer_float(self, device: falcor.Device): + a = device.create_typed_buffer( + format=falcor.ResourceFormat.R32Float, element_count=1024 + ) + b = device.create_typed_buffer( + format=falcor.ResourceFormat.R32Float, element_count=1024 + ) + + a_host = np.linspace(0, 1, 1024, dtype=np.float32) + b_host = np.linspace(1, 0, 1024, dtype=np.float32) + a.from_numpy(a_host) + b.from_numpy(b_host) + a_device = a.to_numpy() + b_device = b.to_numpy() + self.assertEqual(a_device.shape, (1024,)) + self.assertEqual(a_device.dtype, np.float32) + self.assertTrue(np.all(a_device == a_host)) + self.assertEqual(b_device.shape, (1024,)) + self.assertEqual(b_device.dtype, np.float32) + self.assertTrue(np.all(b_device == b_host)) + + device.render_context.copy_resource(b, a) + b_device = b.to_numpy() + self.assertTrue(np.all(b_device == a_host)) + + @for_each_device_type + def test_typed_buffer_float16_2(self, device: falcor.Device): + a = device.create_typed_buffer( + format=falcor.ResourceFormat.RG16Float, element_count=1024 + ) + b = device.create_typed_buffer( + format=falcor.ResourceFormat.RG16Float, element_count=1024 + ) + + a_host = np.linspace(0, 1, 2048, dtype=np.float16) + b_host = np.linspace(1, 0, 2048, dtype=np.float16) + a.from_numpy(a_host) + b.from_numpy(b_host) + a_device = a.to_numpy() + b_device = b.to_numpy() + self.assertEqual(a_device.shape, (1024, 2)) + self.assertEqual(a_device.dtype, np.float16) + self.assertTrue(np.all(a_device.flatten() == a_host)) + self.assertEqual(b_device.shape, (1024, 2)) + self.assertEqual(b_device.dtype, np.float16) + self.assertTrue(np.all(b_device.flatten() == b_host)) + + device.render_context.copy_resource(b, a) + b_device = b.to_numpy() + self.assertTrue(np.all(b_device.flatten() == a_host)) + + @for_each_device_type + def test_texture_1d(self, device: falcor.Device): + a = device.create_texture(width=128, format=falcor.ResourceFormat.R8Uint) + b = device.create_texture(width=128, format=falcor.ResourceFormat.R8Uint) + a_host = np.linspace(0, 255, 128, dtype=np.uint8) + b_host = np.linspace(255, 0, 128, dtype=np.uint8) + a.from_numpy(a_host) + b.from_numpy(b_host) + a_device = a.to_numpy() + b_device = b.to_numpy() + self.assertEqual(a_device.shape, (128,)) + self.assertEqual(a_device.dtype, np.uint8) + self.assertTrue(np.all(a_device == a_host)) + self.assertEqual(b_device.shape, (128,)) + self.assertEqual(b_device.dtype, np.uint8) + self.assertTrue(np.all(b_device == b_host)) + + device.render_context.copy_resource(b, a) + b_device = b.to_numpy() + self.assertTrue(np.all(b_device == a_host)) - def test_create(self): - for device_type in DEVICE_TYPES: - with self.subTest(): - print(f"device_type={device_type}") + @for_each_device_type + def test_texture_2d(self, device: falcor.Device): + a = device.create_texture( + width=128, height=64, format=falcor.ResourceFormat.RGBA32Float + ) + b = device.create_texture( + width=128, height=64, format=falcor.ResourceFormat.RGBA32Float + ) + a_host = np.reshape( + np.linspace(0, 1, 128 * 64 * 4, dtype=np.float32), (64, 128, 4) + ) + b_host = np.reshape( + np.linspace(1, 0, 128 * 64 * 4, dtype=np.float32), (64, 128, 4) + ) + a.from_numpy(a_host) + b.from_numpy(b_host) + a_device = a.to_numpy() + b_device = b.to_numpy() + self.assertEqual(a_device.shape, (64, 128, 4)) + self.assertEqual(a_device.dtype, np.float32) + self.assertTrue(np.all(a_device == a_host)) + self.assertEqual(b_device.shape, (64, 128, 4)) + self.assertEqual(b_device.dtype, np.float32) + self.assertTrue(np.all(b_device == b_host)) - device = falcor.Device(type=device_type) - self.assertTrue(device is not None) + device.render_context.copy_resource(b, a) + b_device = b.to_numpy() + self.assertTrue(np.all(b_device == a_host)) - print(f"info.adapter_name={device.info.adapter_name}") - print(f"info.api_name={device.info.api_name}") + @for_each_device_type + def test_texture_3d(self, device: falcor.Device): + a = device.create_texture( + width=128, height=64, depth=32, format=falcor.ResourceFormat.RG16Float + ) + b = device.create_texture( + width=128, height=64, depth=32, format=falcor.ResourceFormat.RG16Float + ) + a_host = np.reshape( + np.linspace(0, 1, 128 * 64 * 32 * 2, dtype=np.float16), (32, 64, 128, 2) + ) + b_host = np.reshape( + np.linspace(1, 0, 128 * 64 * 32 * 2, dtype=np.float16), (32, 64, 128, 2) + ) + a.from_numpy(a_host) + b.from_numpy(b_host) + a_device = a.to_numpy() + b_device = b.to_numpy() + self.assertEqual(a_device.shape, (32, 64, 128, 2)) + self.assertEqual(a_device.dtype, np.float16) + self.assertTrue(np.all(a_device == a_host)) + self.assertEqual(b_device.shape, (32, 64, 128, 2)) + self.assertEqual(b_device.dtype, np.float16) + self.assertTrue(np.all(b_device == b_host)) - print(f"limits.max_compute_dispatch_thread_groups={device.limits.max_compute_dispatch_thread_groups}") - print(f"limits.max_shader_visible_samplers={device.limits.max_shader_visible_samplers}") + device.render_context.copy_resource(b, a) + b_device = b.to_numpy() + self.assertTrue(np.all(b_device == a_host)) - del device -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/python_tests/helpers.py b/tests/python_tests/helpers.py new file mode 100644 index 000000000..f89adf926 --- /dev/null +++ b/tests/python_tests/helpers.py @@ -0,0 +1,35 @@ +import os +import falcor + +class DeviceCache: + """ + A cache of device instances, one for each device type. + """ + + def __init__(self): + self.devices = {} + + def get(self, device_type): + if device_type not in self.devices: + self.devices[device_type] = falcor.Device(type=device_type) + return self.devices[device_type] + + +device_cache = DeviceCache() + +if os.name == "nt": + DEVICE_TYPES = [falcor.DeviceType.D3D12, falcor.DeviceType.Vulkan] +else: + DEVICE_TYPES = [falcor.DeviceType.Vulkan] + +def for_each_device_type(func): + """ + A decorator that runs a test function for each device type. + """ + + def wrapper(*args, **kwargs): + for device_type in DEVICE_TYPES: + with args[0].subTest(): + func(*args, **kwargs, device=device_cache.get(device_type)) + + return wrapper diff --git a/tests/run_image_tests.sh b/tests/run_image_tests.sh old mode 100644 new mode 100755 diff --git a/tests/run_python_tests.sh b/tests/run_python_tests.sh old mode 100644 new mode 100755 diff --git a/tests/run_unit_tests.sh b/tests/run_unit_tests.sh old mode 100644 new mode 100755 diff --git a/tests/testing/build_falcor.py b/tests/testing/build_falcor.py index e3d785361..91609de66 100644 --- a/tests/testing/build_falcor.py +++ b/tests/testing/build_falcor.py @@ -25,7 +25,7 @@ def build_falcor(env, rebuild=False): def main(): parser = argparse.ArgumentParser(description='Utility for building Falcor.') parser.add_argument('-c', '--config', type=str, action='store', help=f'Build configuration') - parser.add_argument('-e', '--environment', type=str, action='store', help='Environment', default=config.DEFAULT_ENVIRONMENT) + parser.add_argument('-e', '--environment', type=str, action='store', help='Environment', default=None) parser.add_argument('--rebuild', action='store_true', help='Force rebuild') parser.add_argument('--list-configs', action='store_true', help='List available build configurations.') args = parser.parse_args() diff --git a/tests/testing/core/config.py b/tests/testing/core/config.py index f21002435..15f2a3fa6 100644 --- a/tests/testing/core/config.py +++ b/tests/testing/core/config.py @@ -5,7 +5,10 @@ import os # Default environment configuration file. -DEFAULT_ENVIRONMENT="environment/default.json" +DEFAULT_ENVIRONMENT="tests/environment/default.json" + +# Default platforms to run image tests on. +DEFAULT_PLATFORMS = ["windows-x86_64", "linux-x86_64"] # Default image comparison tolerance. DEFAULT_TOLERANCE = 0.0 @@ -81,6 +84,7 @@ } if os.name == 'nt': + PLATFORM = "windows-x86_64" # Executables. CMAKE_EXE = "tools/.packman/cmake/bin/cmake.exe" FALCOR_LIB = 'Falcor.dll' @@ -92,6 +96,7 @@ SUPPORTED_DEVICE_TYPES = ["d3d12", "vulkan"] elif os.name == 'posix': + PLATFORM = "linux-x86_64" # Executables. CMAKE_EXE = "tools/.packman/cmake/bin/cmake" FALCOR_LIB = 'libFalcor.so' diff --git a/tests/testing/core/environment.py b/tests/testing/core/environment.py index e7bf29393..02cb08c36 100644 --- a/tests/testing/core/environment.py +++ b/tests/testing/core/environment.py @@ -57,6 +57,12 @@ def __init__(self, json_file, build_config): ''' Loads the environment from the JSON file and sets up derived variables. ''' + + self.project_dir = Path(__file__).parents[3].resolve() + + if json_file == None: + json_file = self.project_dir / config.DEFAULT_ENVIRONMENT + # Load JSON config. if not Path(json_file).exists(): raise Exception(f'Environment config file "{json_file}" not found.') @@ -90,7 +96,6 @@ def __init__(self, json_file, build_config): # Setup environment variables. self.name = env['name'] - self.project_dir = Path(__file__).parents[3].resolve() self.build_dir = self.project_dir / config.BUILD_CONFIGS[build_config]['build_dir'] self.cmake_exe = self.project_dir / config.CMAKE_EXE # Ideally this information would be parsed from CMakePresets.json, rather than this roundabout way diff --git a/tests/testing/core/helpers.py b/tests/testing/core/helpers.py index 2286a565c..5f62bced3 100644 --- a/tests/testing/core/helpers.py +++ b/tests/testing/core/helpers.py @@ -5,33 +5,31 @@ import os import re import subprocess -import time import socket from urllib.parse import urlparse -class GitError(Exception): - pass - def get_git_head_branch(path): ''' Return the git HEAD branch name by reading from .git/HEAD file. + If .git/HEAD does not exist, return 'unknown'. ''' try: head = open(os.path.join(path, '.git/HEAD')).read() # HEAD either contains a reference to refs/heads or a sha1 return re.search(r'(ref: refs\/heads\/)?(.*)$', head).group(2) except (IOError, OSError, AttributeError) as e: - raise GitError(e) + return 'unknown' def get_git_remote_origin(path, remote='origin'): ''' Return the git remote origin by reading from .git/config file. + If .git/config does not exist, return 'unknown'. ''' try: config = open(os.path.join(path, '.git/config')).read() return re.search(r'^\[remote \"%s\"\].*\n.*url = (.*)$' % (remote), config, flags=re.MULTILINE).group(1) except (IOError, OSError, AttributeError) as e: - raise GitError(e) + return 'unknown' def get_hostname(): ''' diff --git a/tests/testing/run_image_tests.py b/tests/testing/run_image_tests.py index 677341796..da6d9b169 100644 --- a/tests/testing/run_image_tests.py +++ b/tests/testing/run_image_tests.py @@ -166,7 +166,7 @@ def collect_images(self, image_dir): files = filter(lambda f: f.suffix.lower() in config.IMAGE_EXTENSIONS, files) return list(files) - def generate_images(self, output_dir, mogwai_exe): + def generate_images(self, output_dir, mogwai_exe, run_only=False): ''' Run Mogwai to generate a set of images and store them in output_dir. Returns a tuple containing the result code and a list of messages. @@ -190,6 +190,10 @@ def generate_images(self, output_dir, mogwai_exe): # Write helper script to run test. generate_file = output_dir / 'generate.py' with open(generate_file, 'w') as f: + # Hack to pass run only flag to helpers.py + if run_only: + f.write("import falcor\n") + f.write(f'falcor.__dict__["IMAGE_TEST_RUN_ONLY"]=True\n') f.write(f'm.frameCapture.outputDir = r"{output_dir}"\n') f.write(f'm.script(r"{relative_to_cwd(self.script_file)}")\n') @@ -221,7 +225,7 @@ def generate_images(self, output_dir, mogwai_exe): return Test.Result.FAILED, errors + [f'{mogwai_exe} exited with return code {p.returncode}'], rerun_env # Bail out if no images have been generated. - if len(self.collect_images(output_dir)) == 0: + if not run_only and len(self.collect_images(output_dir)) == 0: return Test.Result.FAILED, ['Test did not generate any images.'], rerun_env return Test.Result.PASSED, [], rerun_env @@ -302,7 +306,7 @@ def compare_images(self, ref_dir, result_dir, image_compare_exe): return result, messages, image_reports - def run(self, compare_only, ref_dir, result_dir, mogwai_exe, image_compare_exe): + def run(self, run_only, compare_only, ref_dir, result_dir, mogwai_exe, image_compare_exe): ''' Run the image test. First, result images are generated (unless compare_only is True). @@ -324,10 +328,10 @@ def run(self, compare_only, ref_dir, result_dir, mogwai_exe, image_compare_exe): # Generate results images. if not compare_only: - result, messages, rerun_env = self.generate_images(result_dir, mogwai_exe) + result, messages, rerun_env = self.generate_images(result_dir, mogwai_exe, run_only) # Compare to references. - if result == Test.Result.PASSED: + if not run_only and result == Test.Result.PASSED: result, messages, report['images'] = self.compare_images(ref_dir, result_dir, image_compare_exe) # Finish report. @@ -408,7 +412,7 @@ def generate_refs(env, tests, ref_dir, process_controller): return success -def run_test(env, test, compare_only, ref_dir, result_dir, min_tolerance, process_controller): +def run_test(env, test, run_only, compare_only, ref_dir, result_dir, min_tolerance, process_controller): if process_controller.is_interrupted(): return with print_mutex: @@ -416,11 +420,11 @@ def run_test(env, test, compare_only, ref_dir, result_dir, min_tolerance, proces test.tolerance = max(test.tolerance, min_tolerance) test.process_controller = process_controller start_time = time.time() - result, messages = test.run(compare_only, ref_dir, result_dir, env.mogwai_exe, env.image_compare_exe) + result, messages = test.run(run_only, compare_only, ref_dir, result_dir, env.mogwai_exe, env.image_compare_exe) elapsed_time = time.time() - start_time return {"name": test.name, "elapsed_time": elapsed_time, "result": result, "messages": messages} -def run_tests(env, tests, compare_only, ref_dir, result_dir, min_tolerance, xml_report, process_controller): +def run_tests(env, tests, run_only, compare_only, ref_dir, result_dir, min_tolerance, xml_report, process_controller): ''' Runs a set of tests, stores them into result_dir and compares them to ref_dir. ''' @@ -439,7 +443,7 @@ def run_tests(env, tests, compare_only, ref_dir, result_dir, min_tolerance, xml_ try: # Run tests on #CPU - 2 (to retain some performance control) with concurrent.futures.ThreadPoolExecutor(process_controller.thread_count) as executor: - futures = {executor.submit(run_test, env, test, compare_only, ref_dir, result_dir, min_tolerance, process_controller) for test in tests} + futures = {executor.submit(run_test, env, test, run_only, compare_only, ref_dir, result_dir, min_tolerance, process_controller) for test in tests} try: for future in concurrent.futures.as_completed(futures): run_result = future.result() @@ -529,6 +533,11 @@ def collect_tests(root_dir, filter_regex, tags): print(e) sys.exit(1) + # Check if test is enabled for current platform. + platforms = header.get("platforms", config.DEFAULT_PLATFORMS) + if not config.PLATFORM in platforms: + continue + # For now we default to d3d12 as we bring in image tests on vulkan. # We should later switch this default to ["d3d12", "vulkan"]. device_types = header.get("device_types", ["d3d12"]) @@ -574,7 +583,7 @@ def main(): default_processes_count = min(config.DEFAULT_PROCESS_COUNT, multiprocessing.cpu_count()) parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('--environment', type=str, action='store', help=f'Environment (default: {config.DEFAULT_ENVIRONMENT})', default=config.DEFAULT_ENVIRONMENT) + parser.add_argument('--environment', type=str, action='store', help=f'Environment', default=None) parser.add_argument('--config', type=str, action='store', help=f'Build configuration (default: {default_config})', default=default_config) parser.add_argument('--list-configs', action='store_true', help='List available build configurations.') @@ -583,6 +592,7 @@ def main(): parser.add_argument('-f', '--filter', type=str, action='store', help='Regular expression for filtering tests to run') parser.add_argument('-x', '--xml-report', type=str, action='store', help='XML report output file') parser.add_argument('-b', '--ref-branch', help='Reference branch to compare against (defaults to master branch)', default='master') + parser.add_argument('--run-only', action='store_true', help='Run tests without comparing images') parser.add_argument('--compare-only', action='store_true', help='Compare previous results against references without generating new images') parser.add_argument('--tolerance', type=float, action='store', help='Override tolerance to be at least this value.', default=config.DEFAULT_TOLERANCE) parser.add_argument('--parallel', type=int, action='store', help='Set the number of Mogwai processes to be used in parallel', default=default_processes_count) @@ -651,7 +661,7 @@ def main(): sys.exit(1) # Give some instructions on how to acquire reference images if not available. - if not ref_dir.exists(): + if not args.run_only and not ref_dir.exists(): print(ref_dir) print(colored(f'\n!!! Reference images for "{args.ref_branch}" branch are not available !!!', 'red')) print('') @@ -668,7 +678,7 @@ def main(): sys.exit(1) # Run tests. - if not run_tests(env, tests, args.compare_only, ref_dir, result_dir, args.tolerance, args.xml_report, process_controller): + if not run_tests(env, tests, args.run_only, args.compare_only, ref_dir, result_dir, args.tolerance, args.xml_report, process_controller): sys.exit(1) sys.exit(0) diff --git a/tests/testing/run_python_tests.py b/tests/testing/run_python_tests.py index 4cc1d2413..f4c983c40 100644 --- a/tests/testing/run_python_tests.py +++ b/tests/testing/run_python_tests.py @@ -61,8 +61,8 @@ def main(): "--environment", type=str, action="store", - help=f"Environment (default: {config.DEFAULT_ENVIRONMENT})", - default=config.DEFAULT_ENVIRONMENT, + help=f"Environment", + default=None, ) parser.add_argument( "--config", diff --git a/tests/testing/run_unit_tests.py b/tests/testing/run_unit_tests.py index bac1bf91e..0da28063c 100644 --- a/tests/testing/run_unit_tests.py +++ b/tests/testing/run_unit_tests.py @@ -31,7 +31,7 @@ def main(): parser = argparse.ArgumentParser(description=__doc__, add_help=False) parser.add_argument('-h', '--help', action='store_true', help='Show this help message and exit') - parser.add_argument('--environment', type=str, action='store', help=f'Environment (default: {config.DEFAULT_ENVIRONMENT})', default=config.DEFAULT_ENVIRONMENT) + parser.add_argument('--environment', type=str, action='store', help=f'Environment', default=None) parser.add_argument('--config', type=str, action='store', help=f'Build configuration (default: {default_config})', default=default_config) parser.add_argument('--list-configs', action='store_true', help='List available build configurations') args, passthrough_args = parser.parse_known_args() diff --git a/tests/testing/view_image_tests.py b/tests/testing/view_image_tests.py index 5a3f19a99..b2ce02089 100644 --- a/tests/testing/view_image_tests.py +++ b/tests/testing/view_image_tests.py @@ -392,7 +392,7 @@ def catch_all(path): def main(): parser = argparse.ArgumentParser(description="Utility for viewing results of image tests.") - parser.add_argument('-e', '--environment', type=str, action='store', help='Environment', default=config.DEFAULT_ENVIRONMENT) + parser.add_argument('-e', '--environment', type=str, action='store', help='Environment', default=None) parser.add_argument('--host', type=str, action='store', help='Server hostname', default='localhost') parser.add_argument('--port', type=int, action='store', help='Server port', default=8080) parser.add_argument('--debug', action='store_true', help='Debug mode') diff --git a/tools/packman/packman b/tools/packman/packman old mode 100644 new mode 100755 diff --git a/tools/pymacro.py b/tools/pymacro.py old mode 100755 new mode 100644 diff --git a/tools/run_clang_format.py b/tools/run_clang_format.py old mode 100755 new mode 100644