diff --git a/37_HLSLSamplingTests/CMakeLists.txt b/37_HLSLSamplingTests/CMakeLists.txt new file mode 100644 index 000000000..608d46d8f --- /dev/null +++ b/37_HLSLSamplingTests/CMakeLists.txt @@ -0,0 +1,54 @@ +set(NBL_INCLUDE_SEARCH_DIRECTORIES + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +include(common RESULT_VARIABLE RES) +if(NOT RES) + message(FATAL_ERROR "common.cmake not found. Should be in {repo_root}/cmake directory") +endif() + +nbl_create_executable_project("" "" "${NBL_INCLUDE_SEARCH_DIRECTORIES}" "" "${NBL_EXECUTABLE_PROJECT_CREATION_PCH_TARGET}") + +if(NBL_EMBED_BUILTIN_RESOURCES) + set(_BR_TARGET_ ${EXECUTABLE_NAME}_builtinResourceData) + set(RESOURCE_DIR "app_resources") + + get_filename_component(_SEARCH_DIRECTORIES_ "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) + get_filename_component(_OUTPUT_DIRECTORY_SOURCE_ "${CMAKE_CURRENT_BINARY_DIR}/src" ABSOLUTE) + get_filename_component(_OUTPUT_DIRECTORY_HEADER_ "${CMAKE_CURRENT_BINARY_DIR}/include" ABSOLUTE) + + file(GLOB_RECURSE BUILTIN_RESOURCE_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/${RESOURCE_DIR}" CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${RESOURCE_DIR}/*") + foreach(RES_FILE ${BUILTIN_RESOURCE_FILES}) + LIST_BUILTIN_RESOURCE(RESOURCES_TO_EMBED "${RES_FILE}") + endforeach() + + ADD_CUSTOM_BUILTIN_RESOURCES(${_BR_TARGET_} RESOURCES_TO_EMBED "${_SEARCH_DIRECTORIES_}" "${RESOURCE_DIR}" "nbl::this_example::builtin" "${_OUTPUT_DIRECTORY_HEADER_}" "${_OUTPUT_DIRECTORY_SOURCE_}") + + LINK_BUILTIN_RESOURCES_TO_TARGET(${EXECUTABLE_NAME} ${_BR_TARGET_}) +endif() + +set(OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/auto-gen") + +set(JSON [=[ +[ + { + "INPUT": "app_resources/test_compile.comp.hlsl", + "KEY": "shader", + "COMPILE_OPTIONS": ["-T", "cs_6_8"], + "DEPENDS": [], + "CAPS": [] + } +] +]=]) + +NBL_CREATE_NSC_COMPILE_RULES( + TARGET ${EXECUTABLE_NAME}SPIRV + LINK_TO ${EXECUTABLE_NAME} + BINARY_DIR ${OUTPUT_DIRECTORY} + MOUNT_POINT_DEFINE NBL_THIS_EXAMPLE_BUILD_MOUNT_POINT + COMMON_OPTIONS -I ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VAR KEYS + INCLUDE nbl/this_example/builtin/build/spirv/keys.hpp + NAMESPACE nbl::this_example::builtin::build + INPUTS ${JSON} +) diff --git a/37_HLSLSamplingTests/app_resources/test_compile.comp.hlsl b/37_HLSLSamplingTests/app_resources/test_compile.comp.hlsl new file mode 100644 index 000000000..4ee39f2be --- /dev/null +++ b/37_HLSLSamplingTests/app_resources/test_compile.comp.hlsl @@ -0,0 +1,79 @@ +// Compile test: instantiate sampling types to verify DXC compilation +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace nbl::hlsl; + +[[vk::binding(0, 0)]] RWStructuredBuffer output; + +[numthreads(1, 1, 1)] +void main() +{ + float32_t2 u2 = float32_t2(0.5, 0.5); + float32_t3 u3 = float32_t3(0.5, 0.5, 0.5); + float32_t4 acc = float32_t4(0, 0, 0, 0); + + // concentric mapping (free function) + float32_t2 concentric = sampling::concentricMapping(u2); + acc.xy += concentric; + + // Linear + sampling::Linear lin = sampling::Linear::create(u2); + acc.x += lin.generate(0.5f); + + // Bilinear + sampling::Bilinear bilinear = sampling::Bilinear::create(float32_t4(1, 2, 3, 4)); + float32_t rcpPdf; + float32_t2 bilSample = bilinear.generate(rcpPdf, u2); + acc.xy += bilSample; + acc.z += rcpPdf; + + // UniformHemisphere + acc.xyz += sampling::UniformHemisphere::generate(u2); + + // UniformSphere + acc.xyz += sampling::UniformSphere::generate(u2); + + // ProjectedHemisphere + acc.xyz += sampling::ProjectedHemisphere::generate(u2); + + // ProjectedSphere + acc.xyz += sampling::ProjectedSphere::generate(u3); + + // BoxMullerTransform + sampling::BoxMullerTransform bmt; + bmt.stddev = 1.0; + acc.xy += bmt(u2); + + // SphericalTriangle + shapes::SphericalTriangle shapeTri; + shapeTri.vertex0 = float32_t3(1, 0, 0); + shapeTri.vertex1 = float32_t3(0, 1, 0); + shapeTri.vertex2 = float32_t3(0, 0, 1); + sampling::SphericalTriangle sphTri = sampling::SphericalTriangle::create(shapeTri); + float32_t stRcpPdf; + acc.xyz += sphTri.generate(stRcpPdf, u2); + acc.w += stRcpPdf; + + // SphericalRectangle + shapes::SphericalRectangle shapeRect; + shapeRect.r0 = float32_t3(-0.5, -0.5, -1.0); + sampling::SphericalRectangle sphRect = sampling::SphericalRectangle::create(shapeRect); + float32_t srS; + acc.xy += sphRect.generate(float32_t2(1.0, 1.0), u2, srS); + acc.z += srS; + + // ProjectedSphericalTriangle — skipped: pre-existing bug in computeBilinearPatch(receiverNormal, isBSDF) + sampling::ProjectedSphericalTriangle projTri = sampling::ProjectedSphericalTriangle::create(shapeTri); + float32_t ptRcpPdf; + // acc.xyz += projTri.generate(ptRcpPdf, float32_t3(0.0f, 0.0f, 1.0f), true, u2); + acc.w += ptRcpPdf; + + output[0] = acc; +} diff --git a/37_HLSLSamplingTests/main.cpp b/37_HLSLSamplingTests/main.cpp new file mode 100644 index 000000000..d78a597b5 --- /dev/null +++ b/37_HLSLSamplingTests/main.cpp @@ -0,0 +1,121 @@ +#include + +#include "nbl/examples/examples.hpp" +#include "nbl/this_example/builtin/build/spirv/keys.hpp" + +using namespace nbl; +using namespace core; +using namespace system; +using namespace asset; +using namespace video; +using namespace nbl::hlsl; +using namespace nbl::examples; + +// sampling headers (HLSL/C++ compatible) +#include "nbl/builtin/hlsl/sampling/concentric_mapping.hlsl" +#include "nbl/builtin/hlsl/sampling/linear.hlsl" +#include "nbl/builtin/hlsl/sampling/bilinear.hlsl" +#include "nbl/builtin/hlsl/sampling/uniform_spheres.hlsl" +#include "nbl/builtin/hlsl/sampling/cos_weighted_spheres.hlsl" +#include "nbl/builtin/hlsl/sampling/box_muller_transform.hlsl" +#include "nbl/builtin/hlsl/sampling/spherical_triangle.hlsl" +#include "nbl/builtin/hlsl/sampling/projected_spherical_triangle.hlsl" +#include "nbl/builtin/hlsl/sampling/spherical_rectangle.hlsl" + +// concepts header — include AFTER sampler headers, and only in the test +#include "nbl/builtin/hlsl/sampling/concepts.hlsl" + +class HLSLSamplingTests final : public application_templates::MonoDeviceApplication, public BuiltinResourcesApplication +{ + using device_base_t = application_templates::MonoDeviceApplication; + using asset_base_t = BuiltinResourcesApplication; + +public: + HLSLSamplingTests(const path& _localInputCWD, const path& _localOutputCWD, const path& _sharedInputCWD, const path& _sharedOutputCWD) : + system::IApplicationFramework(_localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD) {} + + inline bool onAppInitialized(smart_refctd_ptr&& system) override + { + if (!device_base_t::onAppInitialized(smart_refctd_ptr(system))) + return false; + + if (!asset_base_t::onAppInitialized(std::move(system))) + return false; + + // test compile with dxc + { + IAssetLoader::SAssetLoadParams lp = {}; + lp.logger = m_logger.get(); + lp.workingDirectory = "app_resources"; + auto key = nbl::this_example::builtin::build::get_spirv_key<"shader">(m_device.get()); + auto bundle = m_assetMgr->getAsset(key.c_str(), lp); + + const auto assets = bundle.getContents(); + if (assets.empty()) + { + m_logger->log("Could not load shader!", ILogger::ELL_ERROR); + return false; + } + + auto shader = IAsset::castDown(assets[0]); + if (!shader) + { + m_logger->log("compile shader test failed!", ILogger::ELL_ERROR); + return false; + } + + m_logger->log("Shader compilation test passed.", ILogger::ELL_INFO); + } + + // ================================================================ + // Compile-time concept verification via static_assert + // ================================================================ + + // --- BasicSampler (level 1) --- generate(domain_type) -> codomain_type + static_assert(sampling::concepts::BasicSampler>); + static_assert(sampling::concepts::BasicSampler>); + static_assert(sampling::concepts::BasicSampler>); + static_assert(sampling::concepts::BasicSampler>); + static_assert(sampling::concepts::BasicSampler>); + + // TODO: remaining samplers need generate(domain_type)->codomain_type overload to satisfy BasicSampler: + // Bilinear - generate takes (rcpPdf, u), needs single-arg overload + // SphericalTriangle - generate takes (rcpPdf, u), needs single-arg overload + // SphericalRectangle - generate takes (extents, uv, S), needs single-arg overload + // BoxMullerTransform - uses operator() instead of generate + // ProjectedSphericalTriangle - generate takes (rcpPdf, normal, isBSDF, u) + // Concentric_mapping - is a free function, needs to be wrapped in a struct with concept type aliases + + // TODO: higher-level concepts require method refactoring: + // --- BackwardDensitySampler (level 3) --- needs generate(domain_type)->sample_type, forwardPdf, backwardPdf + // static_assert(sampling::concepts::BackwardDensitySampler>); + // static_assert(sampling::concepts::BackwardDensitySampler>); + // static_assert(sampling::concepts::BackwardDensitySampler>); + // static_assert(sampling::concepts::BackwardDensitySampler>); + // --- BijectiveSampler (level 4) --- needs + invertGenerate(codomain_type)->inverse_sample_type + // static_assert(sampling::concepts::BijectiveSampler>); + // static_assert(sampling::concepts::BijectiveSampler>); + // static_assert(sampling::concepts::BijectiveSampler>); + // static_assert(sampling::concepts::BijectiveSampler>); + // static_assert(sampling::concepts::BijectiveSampler>); + // static_assert(sampling::concepts::BijectiveSampler>); + // static_assert(sampling::concepts::BijectiveSampler>); + // static_assert(sampling::concepts::BijectiveSampler>); + + + + m_logger->log("All sampling concept tests passed.", ILogger::ELL_INFO); + return true; + } + + void workLoopBody() override {} + + bool keepRunning() override { return false; } + + bool onAppTerminated() override + { + return device_base_t::onAppTerminated(); + } +}; + +NBL_MAIN_FUNC(HLSLSamplingTests) diff --git a/CMakeLists.txt b/CMakeLists.txt index d945c547a..42f43d037 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,7 @@ if(NBL_BUILD_EXAMPLES) add_subdirectory(34_DebugDraw) + add_subdirectory(37_HLSLSamplingTests) add_subdirectory(38_EXRSplit) if (NBL_BUILD_MITSUBA_LOADER) # if (NBL_BUILD_OPTIX)