From 4f30a8f0497825939371922d0fc6bb3a1d082007 Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Tue, 31 Mar 2026 17:01:49 +0200 Subject: [PATCH 1/3] Fix EX31 warmup cache and rectangle area variant --- 31_HLSLPathTracer/CMakeLists.txt | 17 +- .../compute.render.linear.entrypoints.hlsl | 21 -- .../hlsl/compute_render_scene_impl.hlsl | 2 +- .../app_resources/hlsl/render_common.hlsl | 18 +- ...t.compute.rectangle.rwmc.linear.proxy.hlsl | 1 - .../pt.compute.triangle.linear.proxy.hlsl | 1 - .../pt.compute.triangle.methods.shared.hlsl | 27 +- ...pt.compute.triangle.rwmc.linear.proxy.hlsl | 1 - .../hlsl/spirv/pt.compute.variant.shared.hlsl | 25 +- .../nbl/this_example/render_variant_info.hpp | 19 +- 31_HLSLPathTracer/main.cpp | 248 +++++++++++++----- 31_HLSLPathTracer/pt.variant_ids.cmake | 3 - 12 files changed, 209 insertions(+), 174 deletions(-) delete mode 100644 31_HLSLPathTracer/app_resources/hlsl/compute.render.linear.entrypoints.hlsl delete mode 100644 31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.rectangle.rwmc.linear.proxy.hlsl delete mode 100644 31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.linear.proxy.hlsl delete mode 100644 31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.rwmc.linear.proxy.hlsl diff --git a/31_HLSLPathTracer/CMakeLists.txt b/31_HLSLPathTracer/CMakeLists.txt index 88f126f56..586cf65a5 100644 --- a/31_HLSLPathTracer/CMakeLists.txt +++ b/31_HLSLPathTracer/CMakeLists.txt @@ -3,7 +3,7 @@ include("${CMAKE_CURRENT_SOURCE_DIR}/pt.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/pt.variant_ids.cmake") if(NBL_BUILD_IMGUI) - # EX31 keeps triangle polygon-method variants as separate precompiled entrypoints. + # EX31 keeps triangle polygon-method variants as separate precompiled persistent entrypoints. # This keeps polygon-method choice compile-time and avoids runtime shader switching on this axis. # On AMD Ryzen 5 5600G with Radeon Graphics (6C/12T), # a Visual Studio Debug x64 full rebuild of the SPIR-V project completed in about 19.789 s. @@ -60,15 +60,12 @@ if(NBL_BUILD_IMGUI) ) # Keep the payload flat and explicit here. Once Nabla PR #988 lands, these per-rule compile axes should move to first-class packaged-variant support there. - PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.sphere.proxy.hlsl" KEY "pt.compute.sphere" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=0" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_SPHERE}" "-DPT_VARIANT_ENTRYPOINT_KIND=${PT_ENTRYPOINT_COMBINED}") - PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.sphere.rwmc.proxy.hlsl" KEY "pt.compute.sphere.rwmc" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=1" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_SPHERE}" "-DPT_VARIANT_ENTRYPOINT_KIND=${PT_ENTRYPOINT_COMBINED}") - PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.triangle.linear.proxy.hlsl" KEY "pt.compute.triangle.linear" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=0" "-DPT_VARIANT_ENTRYPOINT_KIND=${PT_ENTRYPOINT_LINEAR}") - PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.triangle.persistent.proxy.hlsl" KEY "pt.compute.triangle.persistent" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=0" "-DPT_VARIANT_ENTRYPOINT_KIND=${PT_ENTRYPOINT_PERSISTENT}") - PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.triangle.rwmc.linear.proxy.hlsl" KEY "pt.compute.triangle.rwmc.linear" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=1" "-DPT_VARIANT_ENTRYPOINT_KIND=${PT_ENTRYPOINT_LINEAR}") - PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.triangle.rwmc.persistent.proxy.hlsl" KEY "pt.compute.triangle.rwmc.persistent" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=1" "-DPT_VARIANT_ENTRYPOINT_KIND=${PT_ENTRYPOINT_PERSISTENT}") - PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.rectangle.rwmc.linear.proxy.hlsl" KEY "pt.compute.rectangle.rwmc.linear" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=1" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_RECTANGLE}" "-DPT_VARIANT_ENTRYPOINT_KIND=${PT_ENTRYPOINT_LINEAR}") - PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.rectangle.rwmc.persistent.proxy.hlsl" KEY "pt.compute.rectangle.rwmc.persistent" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=1" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_RECTANGLE}" "-DPT_VARIANT_ENTRYPOINT_KIND=${PT_ENTRYPOINT_PERSISTENT}") - PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.rectangle.proxy.hlsl" KEY "pt.compute.rectangle" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=0" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_RECTANGLE}" "-DPT_VARIANT_ENTRYPOINT_KIND=${PT_ENTRYPOINT_COMBINED}") + PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.sphere.proxy.hlsl" KEY "pt.compute.sphere" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=0" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_SPHERE}") + PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.sphere.rwmc.proxy.hlsl" KEY "pt.compute.sphere.rwmc" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=1" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_SPHERE}") + PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.triangle.persistent.proxy.hlsl" KEY "pt.compute.triangle.persistent" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=0") + PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.triangle.rwmc.persistent.proxy.hlsl" KEY "pt.compute.triangle.rwmc.persistent" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=1") + PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.rectangle.rwmc.persistent.proxy.hlsl" KEY "pt.compute.rectangle.rwmc.persistent" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=1" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_RECTANGLE}") + PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.rectangle.proxy.hlsl" KEY "pt.compute.rectangle" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=0" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_RECTANGLE}") PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/resolve.comp.hlsl" KEY "pt.compute.resolve") PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.misc.proxy.hlsl" KEY "pt.misc") PT_FINALIZE_JSON_PAYLOAD(INOUT JSON) diff --git a/31_HLSLPathTracer/app_resources/hlsl/compute.render.linear.entrypoints.hlsl b/31_HLSLPathTracer/app_resources/hlsl/compute.render.linear.entrypoints.hlsl deleted file mode 100644 index 0660cbead..000000000 --- a/31_HLSLPathTracer/app_resources/hlsl/compute.render.linear.entrypoints.hlsl +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef PATH_TRACER_ENTRYPOINT_NAME -#define PATH_TRACER_ENTRYPOINT_NAME main -#endif - -#ifndef PATH_TRACER_ENTRYPOINT_POLYGON_METHOD -#define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD pathtracer_variant_config::EntryPointPolygonMethod -#endif - -#if !PATH_TRACER_ENABLE_LINEAR -#error Linear entrypoint requested while PATH_TRACER_ENABLE_LINEAR is disabled -#endif - -[numthreads(RenderWorkgroupSize, 1, 1)] -[shader("compute")] -void PATH_TRACER_ENTRYPOINT_NAME(uint32_t3 threadID : SV_DispatchThreadID) -{ - pathtracer_render_variant::runLinear(threadID); -} - -#undef PATH_TRACER_ENTRYPOINT_POLYGON_METHOD -#undef PATH_TRACER_ENTRYPOINT_NAME diff --git a/31_HLSLPathTracer/app_resources/hlsl/compute_render_scene_impl.hlsl b/31_HLSLPathTracer/app_resources/hlsl/compute_render_scene_impl.hlsl index 28e5ada2b..880bff2c8 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/compute_render_scene_impl.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/compute_render_scene_impl.hlsl @@ -87,7 +87,7 @@ void tracePixel(int32_t2 coords) } scene_type scene; - scene.updateLight(renderPushConstants.lightMatrix()); + scene.updateLight(renderPushConstants.generalPurposeLightMatrix); typename variant_types::raygen_type rayGen; rayGen.pixOffsetParam = pixOffsetParam; diff --git a/31_HLSLPathTracer/app_resources/hlsl/render_common.hlsl b/31_HLSLPathTracer/app_resources/hlsl/render_common.hlsl index d4e48d4bf..b15614130 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/render_common.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/render_common.hlsl @@ -12,28 +12,14 @@ NBL_CONSTEXPR uint32_t MaxSamplesLog2 = MAX_SAMPLES_LOG2; struct RenderPushConstants { - float32_t3x4 lightMatrix() - { - float32_t4x3 retval; - retval[0] = lightX; - retval[1] = lightY; - retval[2] = cross(lightX,lightY)*lightZscale; - retval[3] = lightPos; - return hlsl::transpose(retval); - } - - uint64_t pSampleSequence; float32_t4x4 invMVP; - float32_t3 lightX; - float32_t3 lightY; - float32_t lightZscale; - float32_t3 lightPos; + float32_t3x4 generalPurposeLightMatrix; + uint64_t pSampleSequence; // TODO: compact a bit and refactor uint32_t sampleCount : MAX_SAMPLES_LOG2; uint32_t depth : MAX_DEPTH_LOG2; uint32_t sequenceSampleCountLog2 : 5; uint32_t unused : 13; - uint32_t unused1; }; #undef MAX_SAMPLES_LOG2 #undef MAX_DEPTH_LOG2 diff --git a/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.rectangle.rwmc.linear.proxy.hlsl b/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.rectangle.rwmc.linear.proxy.hlsl deleted file mode 100644 index 8e04bcfc1..000000000 --- a/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.rectangle.rwmc.linear.proxy.hlsl +++ /dev/null @@ -1 +0,0 @@ -#include "pt.compute.variant.shared.hlsl" diff --git a/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.linear.proxy.hlsl b/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.linear.proxy.hlsl deleted file mode 100644 index 5035e960d..000000000 --- a/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.linear.proxy.hlsl +++ /dev/null @@ -1 +0,0 @@ -#include "pt.compute.triangle.methods.shared.hlsl" diff --git a/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.methods.shared.hlsl b/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.methods.shared.hlsl index 5bb76a3e1..7b4f15689 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.methods.shared.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.methods.shared.hlsl @@ -1,20 +1,11 @@ #pragma once -#if !defined(PT_VARIANT_USE_RWMC) || !defined(PT_VARIANT_ENTRYPOINT_KIND) +#if !defined(PT_VARIANT_USE_RWMC) #error Missing triangle method compile options #endif -#define PT_VARIANT_ENTRYPOINT_LINEAR 1 -#define PT_VARIANT_ENTRYPOINT_PERSISTENT 2 -#if PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_LINEAR -#define PATH_TRACER_ENABLE_LINEAR 1 -#define PATH_TRACER_ENABLE_PERSISTENT 0 -#elif PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_PERSISTENT #define PATH_TRACER_ENABLE_LINEAR 0 #define PATH_TRACER_ENABLE_PERSISTENT 1 -#else -#error Unsupported PT_VARIANT_ENTRYPOINT_KIND -#endif #if PT_VARIANT_USE_RWMC #define PATH_TRACER_VARIANT_USE_RWMC true @@ -34,21 +25,6 @@ #include "scene_triangle_light.hlsl" #include "compute_render_scene_impl.hlsl" -#if PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_LINEAR -#define PATH_TRACER_ENTRYPOINT_NAME main -#define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD PPM_APPROX_PROJECTED_SOLID_ANGLE -#include "compute.render.linear.entrypoints.hlsl" - -#define PATH_TRACER_ENTRYPOINT_NAME mainArea -#define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD PPM_AREA -#include "compute.render.linear.entrypoints.hlsl" - -#define PATH_TRACER_ENTRYPOINT_NAME mainSolidAngle -#define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD PPM_SOLID_ANGLE -#include "compute.render.linear.entrypoints.hlsl" -#endif - -#if PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_PERSISTENT #define PATH_TRACER_ENTRYPOINT_NAME mainPersistent #define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD PPM_APPROX_PROJECTED_SOLID_ANGLE #include "compute.render.persistent.entrypoints.hlsl" @@ -60,7 +36,6 @@ #define PATH_TRACER_ENTRYPOINT_NAME mainPersistentSolidAngle #define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD PPM_SOLID_ANGLE #include "compute.render.persistent.entrypoints.hlsl" -#endif #undef PATH_TRACER_VARIANT_ENABLE_PERSISTENT #undef PATH_TRACER_VARIANT_ENABLE_LINEAR diff --git a/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.rwmc.linear.proxy.hlsl b/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.rwmc.linear.proxy.hlsl deleted file mode 100644 index 5035e960d..000000000 --- a/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.rwmc.linear.proxy.hlsl +++ /dev/null @@ -1 +0,0 @@ -#include "pt.compute.triangle.methods.shared.hlsl" diff --git a/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.variant.shared.hlsl b/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.variant.shared.hlsl index b8923dbd0..c1062391a 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.variant.shared.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.variant.shared.hlsl @@ -1,27 +1,13 @@ #pragma once -#if !defined(PT_VARIANT_USE_RWMC) || !defined(PT_VARIANT_SCENE_KIND) || !defined(PT_VARIANT_ENTRYPOINT_KIND) +#if !defined(PT_VARIANT_USE_RWMC) || !defined(PT_VARIANT_SCENE_KIND) #error Missing path tracer variant compile options #endif #define PT_VARIANT_SCENE_SPHERE 0 #define PT_VARIANT_SCENE_RECTANGLE 2 -#define PT_VARIANT_ENTRYPOINT_COMBINED 0 -#define PT_VARIANT_ENTRYPOINT_LINEAR 1 -#define PT_VARIANT_ENTRYPOINT_PERSISTENT 2 - -#if PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_COMBINED -#define PATH_TRACER_ENABLE_LINEAR 1 -#define PATH_TRACER_ENABLE_PERSISTENT 1 -#elif PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_LINEAR -#define PATH_TRACER_ENABLE_LINEAR 1 -#define PATH_TRACER_ENABLE_PERSISTENT 0 -#elif PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_PERSISTENT #define PATH_TRACER_ENABLE_LINEAR 0 #define PATH_TRACER_ENABLE_PERSISTENT 1 -#else -#error Unsupported PT_VARIANT_ENTRYPOINT_KIND -#endif #if PT_VARIANT_USE_RWMC #define PATH_TRACER_VARIANT_USE_RWMC true @@ -52,13 +38,10 @@ #include "nbl/this_example/render_variant_config.hlsl" #include "compute_render_scene_impl.hlsl" -#if PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_COMBINED -#include "compute.render.linear.entrypoints.hlsl" -#define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD PPM_SOLID_ANGLE #include "compute.render.persistent.entrypoints.hlsl" -#elif PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_LINEAR -#include "compute.render.linear.entrypoints.hlsl" -#else +#if PT_VARIANT_SCENE_KIND == PT_VARIANT_SCENE_RECTANGLE +#define PATH_TRACER_ENTRYPOINT_NAME mainPersistentArea +#define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD PPM_AREA #include "compute.render.persistent.entrypoints.hlsl" #endif diff --git a/31_HLSLPathTracer/include/nbl/this_example/render_variant_info.hpp b/31_HLSLPathTracer/include/nbl/this_example/render_variant_info.hpp index f1fae0a11..1bc2893ef 100644 --- a/31_HLSLPathTracer/include/nbl/this_example/render_variant_info.hpp +++ b/31_HLSLPathTracer/include/nbl/this_example/render_variant_info.hpp @@ -12,9 +12,9 @@ struct SRenderVariantInfo const char* entryPoint; }; -static constexpr const char* getDefaultRenderEntryPointName(const bool persistentWorkGroups) +static constexpr const char* getDefaultRenderEntryPointName(const bool) { - return persistentWorkGroups ? "mainPersistent" : "main"; + return "mainPersistent"; } static constexpr SRenderVariantInfo getRenderVariantInfo(const E_LIGHT_GEOMETRY geometry, const bool persistentWorkGroups, const E_POLYGON_METHOD requestedMethod) @@ -28,15 +28,24 @@ static constexpr SRenderVariantInfo getRenderVariantInfo(const E_LIGHT_GEOMETRY switch (requestedMethod) { case EPM_AREA: - return { EPM_AREA, EPM_AREA, persistentWorkGroups ? "mainPersistentArea" : "mainArea" }; + return { EPM_AREA, EPM_AREA, "mainPersistentArea" }; case EPM_SOLID_ANGLE: - return { EPM_SOLID_ANGLE, EPM_SOLID_ANGLE, persistentWorkGroups ? "mainPersistentSolidAngle" : "mainSolidAngle" }; + return { EPM_SOLID_ANGLE, EPM_SOLID_ANGLE, "mainPersistentSolidAngle" }; case EPM_PROJECTED_SOLID_ANGLE: default: return { EPM_PROJECTED_SOLID_ANGLE, EPM_PROJECTED_SOLID_ANGLE, defaultEntryPoint }; } case ELG_RECTANGLE: - return { EPM_SOLID_ANGLE, EPM_SOLID_ANGLE, defaultEntryPoint }; + switch (requestedMethod) + { + case EPM_AREA: + return { EPM_AREA, EPM_AREA, "mainPersistentArea" }; + case EPM_SOLID_ANGLE: + return { EPM_SOLID_ANGLE, EPM_SOLID_ANGLE, defaultEntryPoint }; + case EPM_PROJECTED_SOLID_ANGLE: + default: + return { EPM_SOLID_ANGLE, EPM_SOLID_ANGLE, defaultEntryPoint }; + } default: return { EPM_PROJECTED_SOLID_ANGLE, EPM_PROJECTED_SOLID_ANGLE, defaultEntryPoint }; } diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index b7281ea22..7ab9c1ff1 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -2,18 +2,16 @@ // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#include "nbl/examples/examples.hpp" -#include "nbl/examples/common/CCachedOwenScrambledSequence.hpp" - #include "argparse/argparse.hpp" #include "nbl/examples/examples.hpp" +#include "nbl/examples/common/CCachedOwenScrambledSequence.hpp" #include "nbl/this_example/path_tracer_pipeline_state.hpp" #include "nbl/this_example/path_tracer_ui.hpp" #include "nbl/this_example/render_variant_info.hpp" #include "nbl/this_example/transform.hpp" #include "nbl/this_example/render_variant_strings.hpp" #include "nbl/ext/FullScreenTriangle/FullScreenTriangle.h" +#include "nbl/ext/ScreenShot/ScreenShot.h" #include "nbl/builtin/hlsl/math/thin_lens_projection.hlsl" @@ -23,7 +21,6 @@ #include "nbl/builtin/hlsl/sampling/quantized_sequence.hlsl" #include "nbl/asset/utils/ISPIRVEntryPointTrimmer.h" #include "nbl/system/ModuleLookupUtils.h" -#include "nbl/examples/common/CCachedOwenScrambledSequence.hpp" #include "app_resources/hlsl/render_common.hlsl" #include "app_resources/hlsl/render_rwmc_common.hlsl" #include "app_resources/hlsl/resolve_common.hlsl" @@ -64,6 +61,8 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui constexpr static inline uint32_t MaxFramesInFlight = 5; static constexpr size_t BinaryToggleCount = 2ull; static constexpr std::string_view BuildConfigName = PATH_TRACER_BUILD_CONFIG_NAME; + static constexpr bool UsePersistentWorkGroups = true; + static constexpr uint32_t CiFramesBeforeCapture = 3u; static constexpr std::string_view RuntimeConfigFilename = "path_tracer.runtime.json"; static inline std::string DefaultImagePathsFile = "envmap/envmap_0.exr"; @@ -392,14 +391,14 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui }; const auto startupGeometry = static_cast(guiControlled.PTPipeline); - if (!ensureRenderShaderLoaded(startupGeometry, guiControlled.usePersistentWorkGroups, guiControlled.useRWMC)) + if (!ensureRenderShaderLoaded(startupGeometry, UsePersistentWorkGroups, guiControlled.useRWMC)) return logFail("Failed to load current precompiled compute shader variant"); if (guiControlled.useRWMC && !ensureResolveShaderLoaded()) return logFail("Failed to load precompiled resolve compute shader"); ensureRenderPipeline( startupGeometry, - guiControlled.usePersistentWorkGroups, + UsePersistentWorkGroups, guiControlled.useRWMC, static_cast(guiControlled.polygonMethod) ); @@ -408,13 +407,10 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui for (auto geometry = 0u; geometry < ELG_COUNT; ++geometry) { - for (const auto persistentWorkGroups : { false, true }) + for (const auto rwmc : { false, true }) { - for (const auto rwmc : { false, true }) - { - if (!ensureRenderShaderLoaded(static_cast(geometry), persistentWorkGroups, rwmc)) - return logFail("Failed to load precompiled compute shader variant"); - } + if (!ensureRenderShaderLoaded(static_cast(geometry), UsePersistentWorkGroups, rwmc)) + return logFail("Failed to load precompiled compute shader variant"); } } if (!ensureResolveShaderLoaded()) @@ -617,7 +613,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui if (!useCascadeCreationParameters) { imgInfo.arrayLayers = 1u; - imgInfo.usage = asset::IImage::EUF_STORAGE_BIT | asset::IImage::EUF_TRANSFER_DST_BIT | asset::IImage::EUF_SAMPLED_BIT; + imgInfo.usage = asset::IImage::EUF_STORAGE_BIT | asset::IImage::EUF_TRANSFER_DST_BIT | asset::IImage::EUF_TRANSFER_SRC_BIT | asset::IImage::EUF_SAMPLED_BIT; } else { @@ -834,7 +830,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui const float panelMargin = 10.f; const auto currentGeometry = static_cast(guiControlled.PTPipeline); const auto requestedMethod = static_cast(guiControlled.polygonMethod); - const auto currentVariant = getRenderVariantInfo(currentGeometry, guiControlled.usePersistentWorkGroups, requestedMethod); + const auto currentVariant = getRenderVariantInfo(currentGeometry, UsePersistentWorkGroups, requestedMethod); const size_t readyRenderPipelines = getReadyRenderPipelineCount(); const size_t totalRenderPipelines = getKnownRenderPipelineCount(); const size_t readyTotalPipelines = readyRenderPipelines + (m_resolvePipelineState.pipeline ? 1ull : 0ull); @@ -871,9 +867,6 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui { "spp", &guiControlled.spp, 1, (0x1u<setWindowSize(m_window.get(), WindowDimensions.x, WindowDimensions.y); m_surface->recreateSwapchain(); @@ -1220,16 +1212,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui pc.pSampleSequence = m_sequenceBuffer->getDeviceAddress(); pc.invMVP = invMVP; - { - const auto matT = hlsl::float32_t4x3(m_lightModelMatrix); - pc.lightX = matT[0]; - pc.lightY = matT[1]; - // Z had a length and a direction, can point colinear or opposite cross product - const auto recon = hlsl::cross(matT[0], matT[1]); - pc.lightZscale = hlsl::sign(hlsl::dot(recon,matT[2]))*hlsl::length(matT[2])/hlsl::length(recon); - pc.lightPos = matT[3]; - assert(pc.lightMatrix()==hlsl::transpose(matT)); - } + pc.generalPurposeLightMatrix = hlsl::float32_t3x4(transpose(m_lightModelMatrix)); pc.sampleCount = guiControlled.spp; pc.depth = guiControlled.depth; pc.sequenceSampleCountLog2 = m_sequenceSamplesLog2; @@ -1299,9 +1282,8 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui { // TODO: shouldn't it be computed only at initialization stage and on window resize? - const uint32_t dispatchSize = guiControlled.usePersistentWorkGroups ? - m_physicalDevice->getLimits().computeOptimalPersistentWorkgroupDispatchSize(WindowDimensions.x * WindowDimensions.y, RenderWorkgroupSize) : - 1 + (WindowDimensions.x * WindowDimensions.y - 1) / RenderWorkgroupSize; + const uint32_t dispatchSize = + m_physicalDevice->getLimits().computeOptimalPersistentWorkgroupDispatchSize(WindowDimensions.x * WindowDimensions.y, RenderWorkgroupSize); IGPUComputePipeline* pipeline = pickPTPipeline(); if (pipeline) @@ -1373,6 +1355,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui m_startupLog.loggedFirstRenderDispatch = true; } } + maybeQueueCiScreenshotRequest(); // TRANSITION m_outImgView to READ (because of descriptorSets0 -> ComputeShader Writes into the image) { @@ -1493,10 +1476,16 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui updateGUIDescriptorSet(); + bool submitSucceeded = false; m_api->startCapture(); if (queue->submit(infos) != IQueue::RESULT::SUCCESS) m_realFrameIx--; + else + submitSucceeded = true; m_api->endCapture(); + + if (submitSucceeded && rendered[0].semaphore) + maybeSaveSceneScreenshot(queue, rendered[0]); } } @@ -1505,7 +1494,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui logStartupEvent("first_render_submit"); m_startupLog.loggedFirstRenderSubmit = true; } - if (m_startupLog.hasPathtraceOutput && !m_pipelineCache.warmup.started) + if (!m_commandLine.ciMode && m_startupLog.hasPathtraceOutput && !m_pipelineCache.warmup.started) { kickoffPipelineWarmup(); } @@ -1518,6 +1507,8 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui inline bool keepRunning() override { + if (m_exitRequested) + return false; if (m_surface->irrecoverable()) return false; @@ -1562,8 +1553,10 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui m_camera.beginInputProcessing(nextPresentationTimestamp); { - const auto& io = ImGui::GetIO(); - mouse.consumeEvents([&](const IMouseEventChannel::range_t& events) -> void + if (!m_commandLine.ciMode) + { + const auto& io = ImGui::GetIO(); + mouse.consumeEvents([&](const IMouseEventChannel::range_t& events) -> void { if (!io.WantCaptureMouse) m_camera.mouseProcess(events); // don't capture the events, only let camera handle them with its impl @@ -1591,6 +1584,10 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui if (e.timeStamp < previousEventTimestamp) continue; + if (e.keyCode == ui::EKC_F12) + if (e.action == ui::SKeyboardEvent::ECA_RELEASED) + requestSceneScreenshot(getNextSceneScreenshotPath(), false); + if (e.keyCode == ui::EKC_H) if (e.action == ui::SKeyboardEvent::ECA_RELEASED) m_showUI = !m_showUI; @@ -1599,6 +1596,12 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui capturedEvents.keyboard.emplace_back(e); } }, m_logger.get()); + } + else + { + mouse.consumeEvents([&](const IMouseEventChannel::range_t&) -> void {}, m_logger.get()); + keyboard.consumeEvents([&](const IKeyboardEventChannel::range_t&) -> void {}, m_logger.get()); + } } m_camera.endInputProcessing(nextPresentationTimestamp); @@ -1656,6 +1659,13 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui bool parseCommandLine() { argparse::ArgumentParser parser("31_hlslpathtracer"); + parser.add_argument("--ci") + .help("Run in CI mode: save a scene screenshot and exit.") + .default_value(false) + .implicit_value(true); + parser.add_argument("--ci-screenshot") + .nargs(1) + .help("Override the CI scene screenshot output path"); parser.add_argument("--pipeline-cache-dir") .nargs(1) .help("Override the PATH_TRACER pipeline cache root directory"); @@ -1673,6 +1683,10 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui return false; } + m_commandLine.ciMode = parser.get("--ci"); + m_commandLine.ciScreenshotPath = localOutputCWD / "31_hlslpathtracer_ci.png"; + if (parser.present("--ci-screenshot")) + m_commandLine.ciScreenshotPath = path(parser.get("--ci-screenshot")); m_commandLine.pipelineCacheDirOverride.reset(); if (parser.present("--pipeline-cache-dir")) m_commandLine.pipelineCacheDirOverride = path(parser.get("--pipeline-cache-dir")); @@ -1680,6 +1694,84 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui return true; } + void requestExit() + { + m_exitRequested = true; + } + + void requestSceneScreenshot(path outputPath, const bool exitAfterCapture) + { + m_sceneScreenshotRequested = true; + m_sceneScreenshotExitAfterCapture = exitAfterCapture; + m_pendingSceneScreenshotPath = std::move(outputPath); + } + + path getNextSceneScreenshotPath() + { + return localOutputCWD / ("31_hlslpathtracer_scene_" + std::to_string(m_sceneScreenshotCounter++) + ".png"); + } + + void maybeQueueCiScreenshotRequest() + { + if (!m_commandLine.ciMode || m_ciScreenshotCaptured || m_sceneScreenshotRequested || !m_startupLog.hasPathtraceOutput) + return; + + ++m_ciRenderableFrameCounter; + if (m_ciRenderableFrameCounter < CiFramesBeforeCapture) + return; + + requestSceneScreenshot(m_commandLine.ciScreenshotPath, true); + } + + void maybeSaveSceneScreenshot(IQueue* const queue, const IQueue::SSubmitInfo::SSemaphoreInfo& rendered) + { + if (!m_sceneScreenshotRequested || !m_pendingSceneScreenshotPath.has_value() || !rendered.semaphore) + return; + + const ISemaphore::SWaitInfo waitInfo[] = + { + { + .semaphore = rendered.semaphore, + .value = rendered.value + } + }; + if (m_device->blockForSemaphores(waitInfo) != ISemaphore::WAIT_RESULT::SUCCESS) + { + m_logger->log("Scene screenshot failed: could not wait for rendered frame.", ILogger::ELL_ERROR); + m_sceneScreenshotRequested = false; + m_pendingSceneScreenshotPath.reset(); + if (m_sceneScreenshotExitAfterCapture) + requestExit(); + return; + } + + const auto screenshotPath = std::move(*m_pendingSceneScreenshotPath); + m_pendingSceneScreenshotPath.reset(); + m_sceneScreenshotRequested = false; + + const bool ok = ext::ScreenShot::createScreenShot( + m_device.get(), + queue, + nullptr, + m_outImgView.get(), + m_assetMgr.get(), + screenshotPath, + asset::IImage::LAYOUT::READ_ONLY_OPTIMAL, + asset::ACCESS_FLAGS::SHADER_READ_BITS); + + if (ok) + m_logger->log("Scene screenshot saved to \"%s\".", ILogger::ELL_INFO, screenshotPath.string().c_str()); + else + m_logger->log("Scene screenshot failed to save.", ILogger::ELL_ERROR); + + if (m_sceneScreenshotExitAfterCapture) + { + m_ciScreenshotCaptured = true; + requestExit(); + } + m_sceneScreenshotExitAfterCapture = false; + } + static std::string hashToHex(const core::blake3_hash_t& hash) { static constexpr char digits[] = "0123456789abcdef"; @@ -1774,6 +1866,16 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui core::blake3_hasher hasher; hasher << std::string_view(shader ? shader->getFilepathHint() : std::string_view{}); hasher << std::string_view(entryPoint); + if (shader) + { + if (const auto* const content = shader->getContent()) + { + auto contentHash = content->getContentHash(); + if (contentHash == ICPUBuffer::INVALID_HASH) + contentHash = content->computeContentHash(); + hasher << contentHash; + } + } return getSpirvCacheDir() / (hashToHex(static_cast(hasher)) + ".spv"); } @@ -2187,6 +2289,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui smart_refctd_ptr loadRenderShader(const E_LIGHT_GEOMETRY geometry, const bool persistentWorkGroups, const bool rwmc) { + (void)persistentWorkGroups; switch (geometry) { case ELG_SPHERE: @@ -2195,17 +2298,11 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui return loadPrecompiledShader(); case ELG_TRIANGLE: if (rwmc) - return persistentWorkGroups ? - loadPrecompiledShader() : - loadPrecompiledShader(); - return persistentWorkGroups ? - loadPrecompiledShader() : - loadPrecompiledShader(); + return loadPrecompiledShader(); + return loadPrecompiledShader(); case ELG_RECTANGLE: if (rwmc) - return persistentWorkGroups ? - loadPrecompiledShader() : - loadPrecompiledShader(); + return loadPrecompiledShader(); return loadPrecompiledShader(); default: return nullptr; @@ -2214,6 +2311,8 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui struct SCommandLineOptions { + bool ciMode = false; + path ciScreenshotPath; std::optional pipelineCacheDirOverride; bool clearPipelineCache = false; }; @@ -2229,7 +2328,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui bool seen[ELG_COUNT][BinaryToggleCount][BinaryToggleCount][EPM_COUNT] = {}; for (auto geometry = 0u; geometry < ELG_COUNT; ++geometry) { - for (auto persistentWorkGroups = 0u; persistentWorkGroups < BinaryToggleCount; ++persistentWorkGroups) + for (const auto persistentWorkGroups : { UsePersistentWorkGroups }) { for (auto rwmc = 0u; rwmc < BinaryToggleCount; ++rwmc) { @@ -2465,54 +2564,61 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui const auto currentMethod = static_cast(guiControlled.polygonMethod); const auto enqueueRenderVariants = [this, currentGeometry](const E_LIGHT_GEOMETRY geometry, const E_POLYGON_METHOD preferredMethod) -> void { - const auto enqueueForMethods = [this, geometry](const std::initializer_list methods, const bool preferPersistent, const bool preferRWMC) -> void + const auto enqueueForMethods = [this, geometry](const std::initializer_list methods, const bool preferRWMC) -> void { - const bool persistentOrder[2] = { preferPersistent, !preferPersistent }; const bool rwmcOrder[2] = { preferRWMC, !preferRWMC }; for (const auto method : methods) { - for (const auto persistentWorkGroups : persistentOrder) + for (const auto rwmc : rwmcOrder) { - for (const auto rwmc : rwmcOrder) - { - enqueueWarmupJob({ - .type = SWarmupJob::E_TYPE::Render, - .geometry = geometry, - .persistentWorkGroups = persistentWorkGroups, - .rwmc = rwmc, - .method = method - }); - } + enqueueWarmupJob({ + .type = SWarmupJob::E_TYPE::Render, + .geometry = geometry, + .persistentWorkGroups = UsePersistentWorkGroups, + .rwmc = rwmc, + .method = method + }); } } }; - const bool preferPersistent = geometry == currentGeometry ? guiControlled.usePersistentWorkGroups : false; const bool preferRWMC = geometry == currentGeometry ? guiControlled.useRWMC : false; switch (geometry) { case ELG_SPHERE: - enqueueForMethods({ EPM_SOLID_ANGLE }, preferPersistent, preferRWMC); + enqueueForMethods({ EPM_SOLID_ANGLE }, preferRWMC); break; case ELG_TRIANGLE: { switch (preferredMethod) { case EPM_AREA: - enqueueForMethods({ EPM_AREA, EPM_SOLID_ANGLE, EPM_PROJECTED_SOLID_ANGLE }, preferPersistent, preferRWMC); + enqueueForMethods({ EPM_AREA, EPM_SOLID_ANGLE, EPM_PROJECTED_SOLID_ANGLE }, preferRWMC); break; case EPM_SOLID_ANGLE: - enqueueForMethods({ EPM_SOLID_ANGLE, EPM_AREA, EPM_PROJECTED_SOLID_ANGLE }, preferPersistent, preferRWMC); + enqueueForMethods({ EPM_SOLID_ANGLE, EPM_AREA, EPM_PROJECTED_SOLID_ANGLE }, preferRWMC); break; case EPM_PROJECTED_SOLID_ANGLE: default: - enqueueForMethods({ EPM_PROJECTED_SOLID_ANGLE, EPM_AREA, EPM_SOLID_ANGLE }, preferPersistent, preferRWMC); + enqueueForMethods({ EPM_PROJECTED_SOLID_ANGLE, EPM_AREA, EPM_SOLID_ANGLE }, preferRWMC); break; } break; } case ELG_RECTANGLE: - enqueueForMethods({ EPM_SOLID_ANGLE }, preferPersistent, preferRWMC); + switch (preferredMethod) + { + case EPM_AREA: + enqueueForMethods({ EPM_AREA, EPM_SOLID_ANGLE, EPM_PROJECTED_SOLID_ANGLE }, preferRWMC); + break; + case EPM_SOLID_ANGLE: + enqueueForMethods({ EPM_SOLID_ANGLE, EPM_AREA, EPM_PROJECTED_SOLID_ANGLE }, preferRWMC); + break; + case EPM_PROJECTED_SOLID_ANGLE: + default: + enqueueForMethods({ EPM_PROJECTED_SOLID_ANGLE, EPM_SOLID_ANGLE, EPM_AREA }, preferRWMC); + break; + } break; default: break; @@ -2546,7 +2652,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui { return ensureRenderPipeline( static_cast(guiControlled.PTPipeline), - guiControlled.usePersistentWorkGroups, + UsePersistentWorkGroups, guiControlled.useRWMC, static_cast(guiControlled.polygonMethod) ); @@ -2604,6 +2710,13 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui Camera m_camera; bool m_showUI; + bool m_exitRequested = false; + bool m_sceneScreenshotRequested = false; + bool m_sceneScreenshotExitAfterCapture = false; + bool m_ciScreenshotCaptured = false; + uint32_t m_ciRenderableFrameCounter = 0u; + uint64_t m_sceneScreenshotCounter = 0ull; + std::optional m_pendingSceneScreenshotPath; video::CDumbPresentationOracle m_oracle; @@ -2620,7 +2733,6 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui int spp = 32; int depth = 3; rwmc::SResolveParameters::SCreateParams rwmcParams; - bool usePersistentWorkGroups = false; bool useRWMC = false; }; GUIControllables guiControlled; diff --git a/31_HLSLPathTracer/pt.variant_ids.cmake b/31_HLSLPathTracer/pt.variant_ids.cmake index db0bf6f12..058ca76d6 100644 --- a/31_HLSLPathTracer/pt.variant_ids.cmake +++ b/31_HLSLPathTracer/pt.variant_ids.cmake @@ -1,6 +1,3 @@ set(PT_SCENE_SPHERE 0) set(PT_SCENE_TRIANGLE 1) set(PT_SCENE_RECTANGLE 2) -set(PT_ENTRYPOINT_COMBINED 0) -set(PT_ENTRYPOINT_LINEAR 1) -set(PT_ENTRYPOINT_PERSISTENT 2) From 6da1ba8115ac66879ca07122e800ffa985011fbe Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Tue, 31 Mar 2026 20:54:05 +0200 Subject: [PATCH 2/3] Fix EX31 RWMC runtime path --- 31_HLSLPathTracer/CMakeLists.txt | 4 +- .../compute.render.linear.entrypoints.hlsl | 21 ++ .../pt.compute.triangle.methods.shared.hlsl | 27 ++- 31_HLSLPathTracer/main.cpp | 187 +++++++++++++++++- 31_HLSLPathTracer/pt.variant_ids.cmake | 1 + 5 files changed, 232 insertions(+), 8 deletions(-) create mode 100644 31_HLSLPathTracer/app_resources/hlsl/compute.render.linear.entrypoints.hlsl diff --git a/31_HLSLPathTracer/CMakeLists.txt b/31_HLSLPathTracer/CMakeLists.txt index 586cf65a5..8990714aa 100644 --- a/31_HLSLPathTracer/CMakeLists.txt +++ b/31_HLSLPathTracer/CMakeLists.txt @@ -62,8 +62,8 @@ if(NBL_BUILD_IMGUI) # Keep the payload flat and explicit here. Once Nabla PR #988 lands, these per-rule compile axes should move to first-class packaged-variant support there. PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.sphere.proxy.hlsl" KEY "pt.compute.sphere" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=0" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_SPHERE}") PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.sphere.rwmc.proxy.hlsl" KEY "pt.compute.sphere.rwmc" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=1" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_SPHERE}") - PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.triangle.persistent.proxy.hlsl" KEY "pt.compute.triangle.persistent" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=0") - PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.triangle.rwmc.persistent.proxy.hlsl" KEY "pt.compute.triangle.rwmc.persistent" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=1") + PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.triangle.persistent.proxy.hlsl" KEY "pt.compute.triangle.persistent" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=0" "-DPT_VARIANT_ENTRYPOINT_KIND=${PT_ENTRYPOINT_PERSISTENT}") + PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.triangle.rwmc.persistent.proxy.hlsl" KEY "pt.compute.triangle.rwmc.persistent" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=1" "-DPT_VARIANT_ENTRYPOINT_KIND=${PT_ENTRYPOINT_PERSISTENT}") PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.rectangle.rwmc.persistent.proxy.hlsl" KEY "pt.compute.rectangle.rwmc.persistent" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=1" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_RECTANGLE}") PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/spirv/pt.compute.rectangle.proxy.hlsl" KEY "pt.compute.rectangle" COMPILE_OPTIONS "-DPT_VARIANT_USE_RWMC=0" "-DPT_VARIANT_SCENE_KIND=${PT_SCENE_RECTANGLE}") PT_APPEND_SPIRV_RULE(VAR JSON INPUT "app_resources/hlsl/resolve.comp.hlsl" KEY "pt.compute.resolve") diff --git a/31_HLSLPathTracer/app_resources/hlsl/compute.render.linear.entrypoints.hlsl b/31_HLSLPathTracer/app_resources/hlsl/compute.render.linear.entrypoints.hlsl new file mode 100644 index 000000000..0660cbead --- /dev/null +++ b/31_HLSLPathTracer/app_resources/hlsl/compute.render.linear.entrypoints.hlsl @@ -0,0 +1,21 @@ +#ifndef PATH_TRACER_ENTRYPOINT_NAME +#define PATH_TRACER_ENTRYPOINT_NAME main +#endif + +#ifndef PATH_TRACER_ENTRYPOINT_POLYGON_METHOD +#define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD pathtracer_variant_config::EntryPointPolygonMethod +#endif + +#if !PATH_TRACER_ENABLE_LINEAR +#error Linear entrypoint requested while PATH_TRACER_ENABLE_LINEAR is disabled +#endif + +[numthreads(RenderWorkgroupSize, 1, 1)] +[shader("compute")] +void PATH_TRACER_ENTRYPOINT_NAME(uint32_t3 threadID : SV_DispatchThreadID) +{ + pathtracer_render_variant::runLinear(threadID); +} + +#undef PATH_TRACER_ENTRYPOINT_POLYGON_METHOD +#undef PATH_TRACER_ENTRYPOINT_NAME diff --git a/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.methods.shared.hlsl b/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.methods.shared.hlsl index 7b4f15689..5bb76a3e1 100644 --- a/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.methods.shared.hlsl +++ b/31_HLSLPathTracer/app_resources/hlsl/spirv/pt.compute.triangle.methods.shared.hlsl @@ -1,11 +1,20 @@ #pragma once -#if !defined(PT_VARIANT_USE_RWMC) +#if !defined(PT_VARIANT_USE_RWMC) || !defined(PT_VARIANT_ENTRYPOINT_KIND) #error Missing triangle method compile options #endif +#define PT_VARIANT_ENTRYPOINT_LINEAR 1 +#define PT_VARIANT_ENTRYPOINT_PERSISTENT 2 +#if PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_LINEAR +#define PATH_TRACER_ENABLE_LINEAR 1 +#define PATH_TRACER_ENABLE_PERSISTENT 0 +#elif PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_PERSISTENT #define PATH_TRACER_ENABLE_LINEAR 0 #define PATH_TRACER_ENABLE_PERSISTENT 1 +#else +#error Unsupported PT_VARIANT_ENTRYPOINT_KIND +#endif #if PT_VARIANT_USE_RWMC #define PATH_TRACER_VARIANT_USE_RWMC true @@ -25,6 +34,21 @@ #include "scene_triangle_light.hlsl" #include "compute_render_scene_impl.hlsl" +#if PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_LINEAR +#define PATH_TRACER_ENTRYPOINT_NAME main +#define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD PPM_APPROX_PROJECTED_SOLID_ANGLE +#include "compute.render.linear.entrypoints.hlsl" + +#define PATH_TRACER_ENTRYPOINT_NAME mainArea +#define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD PPM_AREA +#include "compute.render.linear.entrypoints.hlsl" + +#define PATH_TRACER_ENTRYPOINT_NAME mainSolidAngle +#define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD PPM_SOLID_ANGLE +#include "compute.render.linear.entrypoints.hlsl" +#endif + +#if PT_VARIANT_ENTRYPOINT_KIND == PT_VARIANT_ENTRYPOINT_PERSISTENT #define PATH_TRACER_ENTRYPOINT_NAME mainPersistent #define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD PPM_APPROX_PROJECTED_SOLID_ANGLE #include "compute.render.persistent.entrypoints.hlsl" @@ -36,6 +60,7 @@ #define PATH_TRACER_ENTRYPOINT_NAME mainPersistentSolidAngle #define PATH_TRACER_ENTRYPOINT_POLYGON_METHOD PPM_SOLID_ANGLE #include "compute.render.persistent.entrypoints.hlsl" +#endif #undef PATH_TRACER_VARIANT_ENABLE_PERSISTENT #undef PATH_TRACER_VARIANT_ENABLE_LINEAR diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 7ab9c1ff1..14bd2abf9 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -25,6 +25,8 @@ #include "app_resources/hlsl/render_rwmc_common.hlsl" #include "app_resources/hlsl/resolve_common.hlsl" +#include +#include #include #include #include @@ -144,6 +146,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui if (!parseCommandLine()) return false; + applyEarlyCommandLineOverrides(); // Create renderpass and init surface nbl::video::IGPURenderpass* renderpass; { @@ -1120,6 +1123,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui guiControlled.rwmcParams.base = 8.0f; guiControlled.rwmcParams.minReliableLuma = 1.0f; guiControlled.rwmcParams.kappa = 5.0f; + applyLateCommandLineOverrides(); // do this as late as possible { @@ -1214,6 +1218,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui pc.invMVP = invMVP; pc.generalPurposeLightMatrix = hlsl::float32_t3x4(transpose(m_lightModelMatrix)); pc.sampleCount = guiControlled.spp; + guiControlled.rwmcParams.sampleCount = guiControlled.spp; pc.depth = guiControlled.depth; pc.sequenceSampleCountLog2 = m_sequenceSamplesLog2; if (guiControlled.useRWMC) @@ -1224,6 +1229,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui }; updatePathtracerPushConstants(); bool producedRenderableOutput = false; + bool dispatchedRwmcPathTrace = false; // TRANSITION m_outImgView to GENERAL (because of descriptorSets0 -> ComputeShader Writes into the image) { @@ -1296,13 +1302,14 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui cmdbuf->pushConstants(pipeline->getLayout(), IShader::E_SHADER_STAGE::ESS_COMPUTE, 0, pushConstantsSize, pushConstantsPtr); cmdbuf->dispatch(dispatchSize, 1u, 1u); + dispatchedRwmcPathTrace = guiControlled.useRWMC; producedRenderableOutput = !guiControlled.useRWMC; } } // m_cascadeView synchronization - wait for previous compute shader to write into the cascade // TODO: create this and every other barrier once outside of the loop? - if(guiControlled.useRWMC) + if(guiControlled.useRWMC && dispatchedRwmcPathTrace) { const IGPUCommandBuffer::SImageMemoryBarrier cascadeBarrier[] = { { @@ -1656,6 +1663,70 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui m_logger->log("PATH_TRACER_STARTUP %s_ms=%lld", ILogger::ELL_INFO, eventName, static_cast(elapsedMs)); } + static std::string normalizeCliToken(std::string value) + { + std::string normalized; + normalized.reserve(value.size()); + for (const auto ch : value) + { + if (ch == '-' || ch == '_' || std::isspace(static_cast(ch))) + continue; + normalized.push_back(static_cast(std::tolower(static_cast(ch)))); + } + return normalized; + } + + static std::optional parseGeometryOverride(const std::string& value) + { + const auto normalized = normalizeCliToken(value); + if (normalized == "sphere" || normalized == "elgsphere") + return ELG_SPHERE; + if (normalized == "triangle" || normalized == "elgtriangle") + return ELG_TRIANGLE; + if (normalized == "rectangle" || normalized == "quad" || normalized == "elgrectangle") + return ELG_RECTANGLE; + return std::nullopt; + } + + static std::optional parseMethodOverride(const std::string& value) + { + const auto normalized = normalizeCliToken(value); + if (normalized == "area" || normalized == "epmarea") + return EPM_AREA; + if (normalized == "solidangle" || normalized == "epmsolidangle") + return EPM_SOLID_ANGLE; + if (normalized == "projectedsolidangle" || normalized == "projected" || normalized == "epmprojectedsolidangle") + return EPM_PROJECTED_SOLID_ANGLE; + return std::nullopt; + } + + void applyEarlyCommandLineOverrides() + { + if (m_commandLine.geometryOverride.has_value()) + guiControlled.PTPipeline = static_cast(m_commandLine.geometryOverride.value()); + if (m_commandLine.methodOverride.has_value()) + guiControlled.polygonMethod = static_cast(m_commandLine.methodOverride.value()); + if (m_commandLine.sppOverride.has_value()) + guiControlled.spp = m_commandLine.sppOverride.value(); + if (m_commandLine.depthOverride.has_value()) + guiControlled.depth = m_commandLine.depthOverride.value(); + if (m_commandLine.rwmcOverride.has_value()) + guiControlled.useRWMC = m_commandLine.rwmcOverride.value(); + } + + void applyLateCommandLineOverrides() + { + if (m_commandLine.rwmcStartOverride.has_value()) + guiControlled.rwmcParams.start = m_commandLine.rwmcStartOverride.value(); + if (m_commandLine.rwmcBaseOverride.has_value()) + guiControlled.rwmcParams.base = m_commandLine.rwmcBaseOverride.value(); + if (m_commandLine.rwmcMinReliableLumaOverride.has_value()) + guiControlled.rwmcParams.minReliableLuma = m_commandLine.rwmcMinReliableLumaOverride.value(); + if (m_commandLine.rwmcKappaOverride.has_value()) + guiControlled.rwmcParams.kappa = m_commandLine.rwmcKappaOverride.value(); + guiControlled.rwmcParams.sampleCount = guiControlled.spp; + } + bool parseCommandLine() { argparse::ArgumentParser parser("31_hlslpathtracer"); @@ -1672,6 +1743,34 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui parser.add_argument("--clear-pipeline-cache") .help("Clear the PATH_TRACER cache root before startup") .flag(); + parser.add_argument("--shader") + .nargs(1) + .help("Select startup geometry: sphere, triangle, rectangle"); + parser.add_argument("--method") + .nargs(1) + .help("Select startup method: area, solid-angle, projected-solid-angle"); + parser.add_argument("--spp") + .scan<'i', int>() + .help("Override startup samples per pixel"); + parser.add_argument("--depth") + .scan<'i', int>() + .help("Override startup path depth"); + parser.add_argument("--rwmc") + .help("Enable RWMC at startup") + .default_value(false) + .implicit_value(true); + parser.add_argument("--rwmc-start") + .scan<'g', float>() + .help("Override RWMC start threshold"); + parser.add_argument("--rwmc-base") + .scan<'g', float>() + .help("Override RWMC base"); + parser.add_argument("--rwmc-min-reliable") + .scan<'g', float>() + .help("Override RWMC minimum reliable luma"); + parser.add_argument("--rwmc-kappa") + .scan<'g', float>() + .help("Override RWMC kappa"); try { @@ -1691,6 +1790,65 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui if (parser.present("--pipeline-cache-dir")) m_commandLine.pipelineCacheDirOverride = path(parser.get("--pipeline-cache-dir")); m_commandLine.clearPipelineCache = parser.get("--clear-pipeline-cache"); + m_commandLine.geometryOverride.reset(); + if (parser.present("--shader")) + { + const auto geometryValue = parser.get("--shader"); + m_commandLine.geometryOverride = parseGeometryOverride(geometryValue); + if (!m_commandLine.geometryOverride.has_value()) + { + m_logger->log("Unknown --shader value: %s", ILogger::ELL_ERROR, geometryValue.c_str()); + return false; + } + } + m_commandLine.methodOverride.reset(); + if (parser.present("--method")) + { + const auto methodValue = parser.get("--method"); + m_commandLine.methodOverride = parseMethodOverride(methodValue); + if (!m_commandLine.methodOverride.has_value()) + { + m_logger->log("Unknown --method value: %s", ILogger::ELL_ERROR, methodValue.c_str()); + return false; + } + } + m_commandLine.sppOverride.reset(); + if (parser.present("--spp")) + { + const auto spp = parser.get("--spp"); + if (spp < 1 || spp > static_cast((0x1u << MaxSamplesLog2) - 1u)) + { + m_logger->log("Invalid --spp value: %d", ILogger::ELL_ERROR, spp); + return false; + } + m_commandLine.sppOverride = spp; + } + m_commandLine.depthOverride.reset(); + if (parser.present("--depth")) + { + const auto depth = parser.get("--depth"); + if (depth < 1 || depth > static_cast((0x1u << MaxDepthLog2) - 1u)) + { + m_logger->log("Invalid --depth value: %d", ILogger::ELL_ERROR, depth); + return false; + } + m_commandLine.depthOverride = depth; + } + m_commandLine.rwmcOverride.reset(); + if (parser.is_used("--rwmc")) + m_commandLine.rwmcOverride = parser.get("--rwmc"); + m_commandLine.rwmcStartOverride.reset(); + if (parser.present("--rwmc-start")) + m_commandLine.rwmcStartOverride = parser.get("--rwmc-start"); + m_commandLine.rwmcBaseOverride.reset(); + if (parser.present("--rwmc-base")) + m_commandLine.rwmcBaseOverride = parser.get("--rwmc-base"); + m_commandLine.rwmcMinReliableLumaOverride.reset(); + if (parser.present("--rwmc-min-reliable")) + m_commandLine.rwmcMinReliableLumaOverride = parser.get("--rwmc-min-reliable"); + m_commandLine.rwmcKappaOverride.reset(); + if (parser.present("--rwmc-kappa")) + m_commandLine.rwmcKappaOverride = parser.get("--rwmc-kappa"); return true; } @@ -2080,12 +2238,18 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui if (hasValidatedSpirvMarker(content)) { - m_pipelineCache.trimmedShaders.trimmer->markValidated(content); + { + std::lock_guard lock(m_pipelineCache.trimmedShaders.mutex); + m_pipelineCache.trimmedShaders.trimmer->markValidated(content); + } return true; } - if (!m_pipelineCache.trimmedShaders.trimmer->ensureValidated(content, m_logger.get())) - return false; + { + std::lock_guard lock(m_pipelineCache.trimmedShaders.mutex); + if (!m_pipelineCache.trimmedShaders.trimmer->ensureValidated(content, m_logger.get())) + return false; + } saveValidatedSpirvMarker(content); return true; @@ -2158,7 +2322,11 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui if (!preparedShader) { const core::set entryPoints = { asset::ISPIRVEntryPointTrimmer::EntryPoint{ .name = entryPoint, .stage = hlsl::ShaderStage::ESS_COMPUTE } }; - const auto result = m_pipelineCache.trimmedShaders.trimmer->trim(shaderModule->getContent(), entryPoints, nullptr); + const auto result = [&]() + { + std::lock_guard lock(m_pipelineCache.trimmedShaders.mutex); + return m_pipelineCache.trimmedShaders.trimmer->trim(shaderModule->getContent(), entryPoints, nullptr); + }(); if (!result) { m_logger->log("Failed to prepare trimmed PATH_TRACER shader for %s. Falling back to the original module.", ILogger::ELL_WARNING, entryPoint); @@ -2315,6 +2483,15 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui path ciScreenshotPath; std::optional pipelineCacheDirOverride; bool clearPipelineCache = false; + std::optional geometryOverride; + std::optional methodOverride; + std::optional sppOverride; + std::optional depthOverride; + std::optional rwmcOverride; + std::optional rwmcStartOverride; + std::optional rwmcBaseOverride; + std::optional rwmcMinReliableLumaOverride; + std::optional rwmcKappaOverride; }; size_t getRunningPipelineBuildCount() const diff --git a/31_HLSLPathTracer/pt.variant_ids.cmake b/31_HLSLPathTracer/pt.variant_ids.cmake index 058ca76d6..544b58a4e 100644 --- a/31_HLSLPathTracer/pt.variant_ids.cmake +++ b/31_HLSLPathTracer/pt.variant_ids.cmake @@ -1,3 +1,4 @@ set(PT_SCENE_SPHERE 0) set(PT_SCENE_TRIANGLE 1) set(PT_SCENE_RECTANGLE 2) +set(PT_ENTRYPOINT_PERSISTENT 2) From 895aeab82dc3fa6dfa823b916efa0f286ffeca8a Mon Sep 17 00:00:00 2001 From: Arkadiusz Lachowicz Date: Tue, 31 Mar 2026 21:38:27 +0200 Subject: [PATCH 3/3] Tune EX31 RWMC defaults for brightness parity --- 31_HLSLPathTracer/main.cpp | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/31_HLSLPathTracer/main.cpp b/31_HLSLPathTracer/main.cpp index 14bd2abf9..1e34d2db2 100644 --- a/31_HLSLPathTracer/main.cpp +++ b/31_HLSLPathTracer/main.cpp @@ -874,10 +874,10 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui { "enable", &guiControlled.useRWMC }, }; const this_example::pt_ui::SFloatSliderRow rwmcFloatRows[] = { - { "start", &guiControlled.rwmcParams.start, 0.2f, 16.0f, "%.3f" }, - { "base", &guiControlled.rwmcParams.base, 1.001f, 16.0f, "%.3f" }, - { "min rel.", &guiControlled.rwmcParams.minReliableLuma, 0.01f, 32.0f, "%.3f" }, - { "kappa", &guiControlled.rwmcParams.kappa, 0.1f, 128.0f, "%.3f" }, + { "start", &guiControlled.rwmcParams.start, 0.2f, 128.0f, "%.3f" }, + { "base", &guiControlled.rwmcParams.base, 1.001f, 32.0f, "%.3f" }, + { "min rel.", &guiControlled.rwmcParams.minReliableLuma, 0.01f, 1024.0f, "%.3f" }, + { "kappa", &guiControlled.rwmcParams.kappa, 0.1f, 1024.0f, "%.3f" }, }; const this_example::pt_ui::STextRow diagnosticsRows[] = { { "geometry", system::to_string(currentGeometry) }, @@ -1000,6 +1000,15 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui this_example::pt_ui::checkboxRow(row); for (const auto& row : rwmcFloatRows) this_example::pt_ui::sliderFloatRow(row); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("defaults"); + ImGui::TableSetColumnIndex(1); + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::PushID("rwmc_defaults"); + if (ImGui::Button("Reset RWMC")) + resetRWMCParamsToDefaults(); + ImGui::PopID(); ImGui::EndTable(); } } @@ -1118,11 +1127,7 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui m_camera.mapKeysToArrows(); // set initial rwmc settings - - guiControlled.rwmcParams.start = hlsl::dot(hlsl::transpose(colorspace::scRGBtoXYZ)[1], LightEminence); - guiControlled.rwmcParams.base = 8.0f; - guiControlled.rwmcParams.minReliableLuma = 1.0f; - guiControlled.rwmcParams.kappa = 5.0f; + resetRWMCParamsToDefaults(); applyLateCommandLineOverrides(); // do this as late as possible @@ -1727,6 +1732,15 @@ class HLSLComputePathtracer final : public SimpleWindowedApplication, public Bui guiControlled.rwmcParams.sampleCount = guiControlled.spp; } + void resetRWMCParamsToDefaults() + { + guiControlled.rwmcParams.start = hlsl::dot(hlsl::transpose(colorspace::scRGBtoXYZ)[1], LightEminence); + guiControlled.rwmcParams.base = 8.0f; + guiControlled.rwmcParams.minReliableLuma = 1.0f; + guiControlled.rwmcParams.kappa = 1.0f; + guiControlled.rwmcParams.sampleCount = guiControlled.spp; + } + bool parseCommandLine() { argparse::ArgumentParser parser("31_hlslpathtracer");