From 7685d29e3a86ae9c43cfe70254af58d487ff85ed Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 22 May 2026 10:43:38 +0700 Subject: [PATCH 1/6] updated dxc --- 3rdparty/dxc/dxc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/dxc/dxc b/3rdparty/dxc/dxc index c765af7b86..a532a63697 160000 --- a/3rdparty/dxc/dxc +++ b/3rdparty/dxc/dxc @@ -1 +1 @@ -Subproject commit c765af7b863c5af1e0e4d2d61dd7ae0f1f37f546 +Subproject commit a532a63697946c09871f4932e4e0ce6e89a843de From 5c248b4b9b664ba592713d16d66e7049b5ea5f4d Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 22 May 2026 11:27:23 +0700 Subject: [PATCH 2/6] reference backend basic structure + some impl for albedo, generate --- .../CReferenceUnidirectionalPathTracing.h | 89 ++++++ .../nbl/asset/material_compiler3/IBackend.h | 25 ++ src/nbl/CMakeLists.txt | 1 + .../CReferenceUnidirectionalPathTracing.cpp | 280 ++++++++++++++++++ 4 files changed, 395 insertions(+) create mode 100644 include/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h create mode 100644 include/nbl/asset/material_compiler3/IBackend.h create mode 100644 src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp diff --git a/include/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h b/include/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h new file mode 100644 index 0000000000..3d91ea99d3 --- /dev/null +++ b/include/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h @@ -0,0 +1,89 @@ +// Copyright (C) 2018-2026 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_MATERIAL_COMPILER_V3_C_REFERENCE_UNIDIRECTIONAL_PATH_TRACING_H_INCLUDED_ +#define _NBL_ASSET_MATERIAL_COMPILER_V3_C_REFERENCE_UNIDIRECTIONAL_PATH_TRACING_H_INCLUDED_ + +#include "nbl/asset/material_compiler3/IBackend.h" + +namespace nbl::asset::material_compiler3 +{ + +//template +struct OrientedMaterial +{ + uint32_t emitter_id; + uint32_t prefetch_offset; + uint32_t prefetch_count; + uint32_t instr_offset; + uint32_t rem_pdf_count; + uint32_t nprecomp_count; + uint32_t genchoice_count; + + core::blake3_hash_t hash; + + struct stream_t + { + uint32_t first; + uint32_t count; + }; + stream_t get_rem_and_pdf() const { return { instr_offset, rem_pdf_count }; } + stream_t get_gen_choice() const { return { instr_offset + rem_pdf_count, genchoice_count }; } + stream_t get_norm_precomp() const { return { instr_offset + rem_pdf_count + genchoice_count, nprecomp_count }; } + stream_t get_tex_prefetch() const { return { prefetch_offset, prefetch_count }; } +}; + +class CReferenceUnidirectionalPathTracing : public IBackend +{ +public: + struct CResult : public IBackend::IResult + { + // TODO need all this? + //instr_stream::traversal_t instructions; + //instr_stream::tex_prefetch::prefetch_stream_t prefetch_stream; + //core::vector bsdfData; + //core::vector emitterData; + + //bool noPrefetchStream; + //bool noNormPrecompStream; + //bool allIsotropic; + //bool noBSDF; + //uint32_t usedRegisterCount; + //uint32_t globalPrefetchRegCountFlags; + //uint32_t paramTexPresence[instr_stream::SBSDFUnion::MAX_TEXTURES][2]; + //// always same value and the value + //std::pair paramConstants[instr_stream::SBSDFUnion::MAX_TEXTURES]; + + //core::unordered_set opcodes; + //core::unordered_set NDFs; + + //one element for each input IR root node + core::vector materials; + + //has to go after #version and before required user-provided descriptors and functions + std::string fragmentShaderSource_declarations; + //has to go after required user-provided descriptors and functions and before the rest of shader (especially entry point function) + std::string fragmentShaderSource; + }; + + CResult compile(const CTrueIR* ir, const std::span materials); + +private: + std::string getHashAs4UintsString(const CTrueIR::INode* node, const CTrueIR* ir, const std::string& separator = ",") const; + + void getAlbedoHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); + + void getNormalHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); + + void getTransparencyHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); + + void getGenerateHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); + + void getQuotientWeightHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); + + void getEvalWeightHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); +}; + +} + +#endif diff --git a/include/nbl/asset/material_compiler3/IBackend.h b/include/nbl/asset/material_compiler3/IBackend.h new file mode 100644 index 0000000000..9f68a7c607 --- /dev/null +++ b/include/nbl/asset/material_compiler3/IBackend.h @@ -0,0 +1,25 @@ +// Copyright (C) 2018-2026 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_MATERIAL_COMPILER_V3_I_BACKEND_H_INCLUDED_ +#define _NBL_ASSET_MATERIAL_COMPILER_V3_I_BACKEND_H_INCLUDED_ + +#include "nbl/asset/material_compiler3/CTrueIR.h" + +namespace nbl::asset::material_compiler3 +{ + +class IBackend +{ +public: + struct IResult// : public core::IReferenceCounted + { + + }; + + IResult compile(const CTrueIR*, const std::span); +}; + +} + +#endif diff --git a/src/nbl/CMakeLists.txt b/src/nbl/CMakeLists.txt index 502d0c70c8..feb1e664fb 100644 --- a/src/nbl/CMakeLists.txt +++ b/src/nbl/CMakeLists.txt @@ -170,6 +170,7 @@ set(NBL_ASSET_SOURCES # Materials asset/material_compiler3/CFrontendIR.cpp asset/material_compiler3/CTrueIR.cpp + asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp # Shaders asset/utils/ISPIRVOptimizer.cpp diff --git a/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp b/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp new file mode 100644 index 0000000000..950b178fff --- /dev/null +++ b/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp @@ -0,0 +1,280 @@ +// Copyright (C) 2022-2026 - DevSH Graphics Programming Sp. z O.O. +// This file is part of the "Nabla Engine". +// For conditions of distribution and use, see copyright notice in nabla.h +#define _NBL_ASSET_MATERIAL_COMPILER3_C_REFERENCE_UNIDIRECTIONAL_PATH_TRACING_CPP_ +#include "nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h" + +namespace nbl::asset::material_compiler3 +{ + +CReferenceUnidirectionalPathTracing::CResult CReferenceUnidirectionalPathTracing::compile(const CTrueIR* ir, const std::span materialHandles) +{ + CResult res; + + // TODO: handle textures somehow + // TODO: templated structs for types in certain functions, e.g. cache (generate + quotient) + std::ostringstream code; + + // loop through layers in IR and construct materials map? maybe just node map is fine + core::vector compiledBSDFRootNodes; + auto compileBSDFRootNode = [&](const CTrueIR::INode* root) -> void { + getAlbedoHLSLCode(code, root, ir); + // TODO: the other functions + // TODO: loop through the children, also might need to do reverse order or forward declare function signatures + }; + + const auto& materials = ir->getMaterials(); + for (uint32_t i = 0; i < materialHandles.size(); i++) + { + if (materialHandles[i].value == CTrueIR::SMaterialHandle::Invalid) + continue; + + const auto& mat = materials[materialHandles[i].value]; + if (auto node = ir->getObjectPool().deref(mat.front.root); node) + compileBSDFRootNode(node); + if (auto node = ir->getObjectPool().deref(mat.back.root); node) + compileBSDFRootNode(node); + } + + // each layer/node writes as string its own code? or just hash + // 8 functions each node: albedo, normal, aov_throughput, transparency, generate, quotientAndWeight, evalAndWeight, emission + + res.fragmentShaderSource = code.str(); + + return res; +} + +std::string CReferenceUnidirectionalPathTracing::getHashAs4UintsString(const CTrueIR::INode* node, const CTrueIR* ir, const std::string& separator) const +{ + // break up hash into 8 pieces of uint32_t + const auto hash = node->computeHash(ir->getObjectPool()); + uint32_t hashPieces[8]; + for (uint8_t i = 0; i < 8; i++) + for (uint8_t j = 0; j < 4; j++) + hlsl::glsl::bitfieldInsert(hashPieces[i], static_cast(hash.data[4 * i + 0]), j * 8, 8); + std::stringstream hashString; + for (uint8_t i = 0; i < 8; i++) + { + hashString << hashPieces[i]; + if (i < 7) + hashString << separator; + } + return hashString.str(); +} + +void CReferenceUnidirectionalPathTracing::getAlbedoHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir) +{ + switch (node->getFinalType()) + { + case CTrueIR::INode::EFinalType::COrientedLayer: + { + const auto* layer = dynamic_cast(node); + if (!layer) + break; + + const auto hashString = getHashAs4UintsString(node, ir); + sstr << "template<>\nspectral_t albedo<" << hashString << ">()\n{\n"; // TODO: what args needed? uv? + + if (auto childBrdf = ir->getObjectPool().deref(layer->brdfTop); childBrdf) + { + const auto childBrdfHash = getHashAs4UintsString(childBrdf, ir); + sstr << "spectral_t brdf = albedo<" << childBrdfHash << ">();\n"; + } + if (auto childBtdf = ir->getObjectPool().deref(layer->firstTransmission); childBtdf) + { + const auto childBtdfHash = getHashAs4UintsString(childBtdf, ir); + sstr << "spectral_t btdf = albedo<" << childBtdfHash << ">();\n"; + } + + // TODO: mix result + sstr << "spectral_t retval = (brdf + btdf) * 0.5;\n"; + sstr << "return retval;\n}\n"; + break; + } + case CTrueIR::INode::EFinalType::CContributorSum: + { + const auto* sum = dynamic_cast(node); + if (!sum) + break; + + const auto hashString = getHashAs4UintsString(node, ir); + sstr << "template<>\nspectral_t albedo<" << hashString << ">()\n{\n"; // TODO: what args needed? uv? + + if (auto childProduct = ir->getObjectPool().deref(sum->product); childProduct) + { + const auto childProductHash = getHashAs4UintsString(childProduct, ir); + sstr << "spectral_t product = albedo<" << childProductHash << ">();\n"; + // TODO: what to do with child caches? + } + if (auto childRest = ir->getObjectPool().deref(sum->rest); childRest) + { + const auto childRestHash = getHashAs4UintsString(childRest, ir); + sstr << "spectral_t rest = albedo<" << childRestHash << ">();\n"; + // TODO: what to do with child caches? + } + + // TODO: weight sample + sstr << "spectral_t retval = (product + rest) * 0.5;\n"; + sstr << "return retval;\n}\n"; + break; + } + case CTrueIR::INode::EFinalType::CWeightedContributor: + { + // TODO + break; + } + case CTrueIR::INode::EFinalType::CCorellatedTransmission: + { + // TODO + break; + } + case CTrueIR::INode::EFinalType::COrenNayar: + { + const auto* oren_nayar = dynamic_cast(node); + if (!oren_nayar) + break; + + const auto hashString = getHashAs4UintsString(node, ir); + sstr << "template<>\nspectral_t albedo<" << hashString << ">()\n{\n"; // TODO: what args needed? uv? + + auto roughness = oren_nayar->ndfParams.getRougness(); + sstr << "using oren_nayar_t = bxdf::reflection::SOrenNayar;\n"; + sstr << "using creation_t = typename oren_nayar_t::creation_type;\n"; + sstr << "creation_t params;\nparams.A = " << roughness.data()[0].scale << ";\n"; + sstr << "oren_nayar_t bxdf = diffuse_op_type::create(params);\n"; + + sstr << "typename oren_nayar_t::anisocache_type bxdf_cache;\n"; + sstr << "sample_t _sample = bxdf.generate(inter, xi, bxdf_cache);\n"; + // TODO: what to do with child caches? + + sstr << "return hlsl::promote();\n}\n"; + break; + break; + } + case CTrueIR::INode::EFinalType::CCookTorrance: + { + // TODO + break; + } + default: + { + const auto hashString = getHashAs4UintsString(node, ir); + sstr << "template<>\nspectral_t albedo<" << hashString << ">()\n{\nreturn hlsl::promote(0);\n}\n"; // TODO: what args needed? uv? + } + } +} + +void CReferenceUnidirectionalPathTracing::getGenerateHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir) +{ + switch (node->getFinalType()) + { + case CTrueIR::INode::EFinalType::COrientedLayer: + { + const auto* layer = dynamic_cast(node); + if (!layer) + break; + + const auto hashString = getHashAs4UintsString(node, ir); + sstr << "template<>\nsample_t generate<" << hashString; + sstr << ">(NBL_CONST_REF_ARG(aniso_interaction_t) inter, NBL_REF_ARG(rand_t) xi, NBL_REF_ARG(rand_t) xi_extra, NBL_REF_ARG(gen_cache<" // TODO: class type specific cache + << hashString << ">) cache)\n{\n"; + + if (auto childBrdf = ir->getObjectPool().deref(layer->brdfTop); childBrdf) + { + const auto childBrdfHash = getHashAs4UintsString(childBrdf, ir); + sstr << "gen_cache<" << childBrdfHash << "> brdf_cache;\n"; + sstr << "sample_t brdf = generate<" << childBrdfHash << ">(inter, xi, brdf_cache);\n"; + // TODO: what to do with child caches? + } + if (auto childBtdf = ir->getObjectPool().deref(layer->firstTransmission); childBtdf) + { + const auto childBtdfHash = getHashAs4UintsString(childBtdf, ir); + sstr << "gen_cache<" << childBtdfHash << "> btdf_cache;\n"; + sstr << "sample_t btdf = generate<" << childBtdfHash << ">(inter, xi_extra, btdf_cache);\n"; + // TODO: what to do with child caches? + } + + // TODO: do resampled importance sampling HLSL code + + sstr << "return retval;\n}\n"; + break; + } + case CTrueIR::INode::EFinalType::CContributorSum: + { + const auto* sum = dynamic_cast(node); + if (!sum) + break; + + const auto hashString = getHashAs4UintsString(node, ir); + sstr << "template<>\nsample_t generate<" << hashString; + sstr << ">(NBL_CONST_REF_ARG(aniso_interaction_t) inter, NBL_REF_ARG(rand_t) xi, NBL_REF_ARG(gen_cache<" + << hashString << ">) cache)\n{\n"; + + sstr << "uint16_t chosenLobe = 0;\npdf_t choiceRcpPdf = 1.f;\n"; + + if (auto childProduct = ir->getObjectPool().deref(sum->product); childProduct) + { + const auto childProductHash = getHashAs4UintsString(childProduct, ir); + sstr << "gen_cache<" << childProductHash << "> product_cache;\n"; + sstr << "sample_t product = generate<" << childProductHash << ">(inter, xi, product_cache);\n"; + // TODO: what to do with child caches? + } + if (auto childRest = ir->getObjectPool().deref(sum->rest); childRest) + { + const auto childRestHash = getHashAs4UintsString(childRest, ir); + sstr << "gen_cache<" << childRestHash << "> rest_cache;\n"; + sstr << "sample_t rest = generate<" << childRestHash << ">(inter, xi, rest_cache);\n"; + // TODO: what to do with child caches? + } + + // TODO: weight sample + + sstr << "return retval;\n}\n"; + break; + } + case CTrueIR::INode::EFinalType::CWeightedContributor: + { + // TODO + break; + } + case CTrueIR::INode::EFinalType::CCorellatedTransmission: + { + // TODO + break; + } + case CTrueIR::INode::EFinalType::COrenNayar: + { + const auto* oren_nayar = dynamic_cast(node); + if (!oren_nayar) + break; + + const auto hashString = getHashAs4UintsString(node, ir); + sstr << "template<>\nsample_t generate<" << hashString; + sstr << ">(NBL_CONST_REF_ARG(aniso_interaction_t) inter, NBL_REF_ARG(rand_t) xi, NBL_REF_ARG(gen_cache<" + << hashString << ">) cache)\n{\n"; + + auto roughness = oren_nayar->ndfParams.getRougness(); + sstr << "using oren_nayar_t = bxdf::reflection::SOrenNayar;\n"; + sstr << "using creation_t = typename oren_nayar_t::creation_type;\n"; + sstr << "creation_t params;\nparams.A = " << roughness.data()[0].scale << ";\n"; + sstr << "oren_nayar_t bxdf = diffuse_op_type::create(params);\n"; + + sstr << "typename oren_nayar_t::anisocache_type bxdf_cache;\n"; + sstr << "sample_t _sample = bxdf.generate(inter, xi, bxdf_cache);\n"; + // TODO: what to do with child caches? + + sstr << "return _sample;\n}\n"; + break; + break; + } + case CTrueIR::INode::EFinalType::CCookTorrance: + { + // TODO + break; + } + default: + break; + } +} + +} From 5fd455791309bf4392504b4fb38b63814e68dc7a Mon Sep 17 00:00:00 2001 From: keptsecret Date: Fri, 22 May 2026 16:28:39 +0700 Subject: [PATCH 3/6] main compile function of backend does templated function declarations, and loops through node and children --- .../CReferenceUnidirectionalPathTracing.h | 26 +------ .../CReferenceUnidirectionalPathTracing.cpp | 75 +++++++++++++++++-- 2 files changed, 72 insertions(+), 29 deletions(-) diff --git a/include/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h b/include/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h index 3d91ea99d3..755ac7eb1d 100644 --- a/include/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h +++ b/include/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h @@ -38,28 +38,6 @@ class CReferenceUnidirectionalPathTracing : public IBackend public: struct CResult : public IBackend::IResult { - // TODO need all this? - //instr_stream::traversal_t instructions; - //instr_stream::tex_prefetch::prefetch_stream_t prefetch_stream; - //core::vector bsdfData; - //core::vector emitterData; - - //bool noPrefetchStream; - //bool noNormPrecompStream; - //bool allIsotropic; - //bool noBSDF; - //uint32_t usedRegisterCount; - //uint32_t globalPrefetchRegCountFlags; - //uint32_t paramTexPresence[instr_stream::SBSDFUnion::MAX_TEXTURES][2]; - //// always same value and the value - //std::pair paramConstants[instr_stream::SBSDFUnion::MAX_TEXTURES]; - - //core::unordered_set opcodes; - //core::unordered_set NDFs; - - //one element for each input IR root node - core::vector materials; - //has to go after #version and before required user-provided descriptors and functions std::string fragmentShaderSource_declarations; //has to go after required user-provided descriptors and functions and before the rest of shader (especially entry point function) @@ -75,6 +53,8 @@ class CReferenceUnidirectionalPathTracing : public IBackend void getNormalHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); + void getAOVThroughputHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); + void getTransparencyHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); void getGenerateHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); @@ -82,6 +62,8 @@ class CReferenceUnidirectionalPathTracing : public IBackend void getQuotientWeightHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); void getEvalWeightHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); + + void getEmissionHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir); }; } diff --git a/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp b/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp index 950b178fff..6ce11d8a7b 100644 --- a/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp +++ b/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp @@ -13,14 +13,75 @@ CReferenceUnidirectionalPathTracing::CResult CReferenceUnidirectionalPathTracing // TODO: handle textures somehow // TODO: templated structs for types in certain functions, e.g. cache (generate + quotient) + // TODO: where do all the type aliases come from? e.g. sample_t, vector3_t, etc. std::ostringstream code; + // define templates of node functions + util structs + code << "template\nstruct gen_cache;\n"; // cache struct + + code << "template\nspectral_t albedo();\n"; // TODO: what args needed? uv? + code << "template\nvector3_t normal();\n"; // TODO: what args needed? uv? + code << "template\nvector3_t aov_throughput();\n"; // TODO: confirm return value is vec3, what args needed? uv? + code << "template\nvector3_t transparency();\n"; // TODO: return value is vec3? what args needed? + + // TODO: check how many rand numbers, might need to declare template functions by node type as well (because rand numbers vary by generate, etc.) + // if the above is the case, will have to check whether the node-specific template function declaration is included yet, then add before function definition + code << "template\n" + << "sample_t generate(NBL_CONST_REF_ARG(aniso_interaction_t) inter, NBL_REF_ARG(rand_t) xi, NBL_REF_ARG(rand_t) xi_extra, " + << "NBL_REF_ARG(gen_cache) cache);\n"; + + code << "template\n" + << "quotient_weight_t quotientAndWeight(NBL_CONST_REF_ARG(sample_t) _sample, NBL_CONST_REF_ARG(aniso_interaction_t) inter, " + << "NBL_REF_ARG(gen_cache) cache);\n"; + + code << "template\n" + << "eval_weight_t evalAndWeight(NBL_CONST_REF_ARG(sample_t) _sample, NBL_CONST_REF_ARG(aniso_interaction_t) inter);\n"; + + code << "template\nspectral_t emission();\n"; // TODO: what args needed? + // loop through layers in IR and construct materials map? maybe just node map is fine - core::vector compiledBSDFRootNodes; - auto compileBSDFRootNode = [&](const CTrueIR::INode* root) -> void { - getAlbedoHLSLCode(code, root, ir); - // TODO: the other functions - // TODO: loop through the children, also might need to do reverse order or forward declare function signatures + core::vector> nodeStack; + core::unordered_set> visitedNodes; + auto compileBSDFRootNode = [&](CTrueIR::typed_pointer_type rootHandle) -> void { + nodeStack.clear(); + visitedNodes.clear(); + + nodeStack.push_back(rootHandle); + const auto& pool = ir->getObjectPool(); + + while (!nodeStack.empty()) + { + const auto handle = nodeStack.back(); + nodeStack.pop_back(); + const auto* node = pool.deref(handle); + if (!node) + continue; + + getAlbedoHLSLCode(code, node, ir); + getNormalHLSLCode(code, node, ir); + getAOVThroughputHLSLCode(code, node, ir); + getTransparencyHLSLCode(code, node, ir); + getGenerateHLSLCode(code, node, ir); + getEvalWeightHLSLCode(code, node, ir); + getQuotientWeightHLSLCode(code, node, ir); + getEmissionHLSLCode(code, node, ir); + + // TODO: might need to do children first or forward declare function signatures + const auto childCount = node->getChildCount(); + if (childCount) + { + for (auto childIx = 0; childIx < childCount; childIx++) + { + const auto childHandle = node->getChildHandle(childIx); + if (const auto child = pool.deref(childHandle); child) + { + const auto [unused, inserted] = visitedNodes.insert(childHandle); + if (inserted) + nodeStack.push_back(childHandle); + } + } + } + } }; const auto& materials = ir->getMaterials(); @@ -31,9 +92,9 @@ CReferenceUnidirectionalPathTracing::CResult CReferenceUnidirectionalPathTracing const auto& mat = materials[materialHandles[i].value]; if (auto node = ir->getObjectPool().deref(mat.front.root); node) - compileBSDFRootNode(node); + compileBSDFRootNode(mat.front.root); if (auto node = ir->getObjectPool().deref(mat.back.root); node) - compileBSDFRootNode(node); + compileBSDFRootNode(mat.back.root); } // each layer/node writes as string its own code? or just hash From 2aab0eb69caa57bbb0a3908f858cd13fcd2e85f0 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 25 May 2026 11:34:40 +0700 Subject: [PATCH 4/6] change compile to return ptr, minor fixes in combining node child results in albedo --- .../CReferenceUnidirectionalPathTracing.h | 33 +++--------------- .../nbl/asset/material_compiler3/IBackend.h | 6 ++-- .../CReferenceUnidirectionalPathTracing.cpp | 34 +++++++------------ 3 files changed, 20 insertions(+), 53 deletions(-) diff --git a/include/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h b/include/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h index 755ac7eb1d..fda4436916 100644 --- a/include/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h +++ b/include/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.h @@ -8,43 +8,18 @@ namespace nbl::asset::material_compiler3 { - -//template -struct OrientedMaterial -{ - uint32_t emitter_id; - uint32_t prefetch_offset; - uint32_t prefetch_count; - uint32_t instr_offset; - uint32_t rem_pdf_count; - uint32_t nprecomp_count; - uint32_t genchoice_count; - - core::blake3_hash_t hash; - - struct stream_t - { - uint32_t first; - uint32_t count; - }; - stream_t get_rem_and_pdf() const { return { instr_offset, rem_pdf_count }; } - stream_t get_gen_choice() const { return { instr_offset + rem_pdf_count, genchoice_count }; } - stream_t get_norm_precomp() const { return { instr_offset + rem_pdf_count + genchoice_count, nprecomp_count }; } - stream_t get_tex_prefetch() const { return { prefetch_offset, prefetch_count }; } -}; -class CReferenceUnidirectionalPathTracing : public IBackend +class CReferenceUnidirectionalPathTracing final : public IBackend { public: - struct CResult : public IBackend::IResult + class CResult final : public IBackend::IResult { - //has to go after #version and before required user-provided descriptors and functions + public: std::string fragmentShaderSource_declarations; - //has to go after required user-provided descriptors and functions and before the rest of shader (especially entry point function) std::string fragmentShaderSource; }; - CResult compile(const CTrueIR* ir, const std::span materials); + core::smart_refctd_ptr compile(const CTrueIR* ir, const std::span materials); private: std::string getHashAs4UintsString(const CTrueIR::INode* node, const CTrueIR* ir, const std::string& separator = ",") const; diff --git a/include/nbl/asset/material_compiler3/IBackend.h b/include/nbl/asset/material_compiler3/IBackend.h index 9f68a7c607..c0ce755354 100644 --- a/include/nbl/asset/material_compiler3/IBackend.h +++ b/include/nbl/asset/material_compiler3/IBackend.h @@ -9,15 +9,15 @@ namespace nbl::asset::material_compiler3 { -class IBackend +class IBackend : public core::IReferenceCounted { public: - struct IResult// : public core::IReferenceCounted + class IResult : public core::IReferenceCounted { }; - IResult compile(const CTrueIR*, const std::span); + core::smart_refctd_ptr compile(const CTrueIR*, const std::span); }; } diff --git a/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp b/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp index 6ce11d8a7b..a7345bfcf2 100644 --- a/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp +++ b/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp @@ -7,9 +7,9 @@ namespace nbl::asset::material_compiler3 { -CReferenceUnidirectionalPathTracing::CResult CReferenceUnidirectionalPathTracing::compile(const CTrueIR* ir, const std::span materialHandles) + core::smart_refctd_ptr CReferenceUnidirectionalPathTracing::compile(const CTrueIR* ir, const std::span materialHandles) { - CResult res; + auto res = core::make_smart_refctd_ptr(); // TODO: handle textures somehow // TODO: templated structs for types in certain functions, e.g. cache (generate + quotient) @@ -100,7 +100,7 @@ CReferenceUnidirectionalPathTracing::CResult CReferenceUnidirectionalPathTracing // each layer/node writes as string its own code? or just hash // 8 functions each node: albedo, normal, aov_throughput, transparency, generate, quotientAndWeight, evalAndWeight, emission - res.fragmentShaderSource = code.str(); + res->fragmentShaderSource = code.str(); return res; } @@ -147,8 +147,7 @@ void CReferenceUnidirectionalPathTracing::getAlbedoHLSLCode(std::ostringstream& sstr << "spectral_t btdf = albedo<" << childBtdfHash << ">();\n"; } - // TODO: mix result - sstr << "spectral_t retval = (brdf + btdf) * 0.5;\n"; + sstr << "spectral_t retval = brdf + btdf;\n"; sstr << "return retval;\n}\n"; break; } @@ -174,8 +173,7 @@ void CReferenceUnidirectionalPathTracing::getAlbedoHLSLCode(std::ostringstream& // TODO: what to do with child caches? } - // TODO: weight sample - sstr << "spectral_t retval = (product + rest) * 0.5;\n"; + sstr << "spectral_t retval = product + rest;\n"; sstr << "return retval;\n}\n"; break; } @@ -197,24 +195,18 @@ void CReferenceUnidirectionalPathTracing::getAlbedoHLSLCode(std::ostringstream& const auto hashString = getHashAs4UintsString(node, ir); sstr << "template<>\nspectral_t albedo<" << hashString << ">()\n{\n"; // TODO: what args needed? uv? - - auto roughness = oren_nayar->ndfParams.getRougness(); - sstr << "using oren_nayar_t = bxdf::reflection::SOrenNayar;\n"; - sstr << "using creation_t = typename oren_nayar_t::creation_type;\n"; - sstr << "creation_t params;\nparams.A = " << roughness.data()[0].scale << ";\n"; - sstr << "oren_nayar_t bxdf = diffuse_op_type::create(params);\n"; - - sstr << "typename oren_nayar_t::anisocache_type bxdf_cache;\n"; - sstr << "sample_t _sample = bxdf.generate(inter, xi, bxdf_cache);\n"; - // TODO: what to do with child caches? - - sstr << "return hlsl::promote();\n}\n"; - break; + sstr << "return hlsl::promote(0.0);\n}\n"; break; } case CTrueIR::INode::EFinalType::CCookTorrance: { - // TODO + const auto* cook_torrance = dynamic_cast(node); + if (!cook_torrance) + break; + + const auto hashString = getHashAs4UintsString(node, ir); + sstr << "template<>\nspectral_t albedo<" << hashString << ">()\n{\n"; // TODO: what args needed? uv? + sstr << "return hlsl::promote(0.0);\n}\n"; break; } default: From 28d970774dda0abb56d2e456dc9c3973297c982a Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 25 May 2026 15:47:59 +0700 Subject: [PATCH 5/6] complete albedo hlsl impl --- .../CReferenceUnidirectionalPathTracing.cpp | 112 ++++++++++++++++-- 1 file changed, 100 insertions(+), 12 deletions(-) diff --git a/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp b/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp index a7345bfcf2..5c95035650 100644 --- a/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp +++ b/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp @@ -164,46 +164,134 @@ void CReferenceUnidirectionalPathTracing::getAlbedoHLSLCode(std::ostringstream& { const auto childProductHash = getHashAs4UintsString(childProduct, ir); sstr << "spectral_t product = albedo<" << childProductHash << ">();\n"; - // TODO: what to do with child caches? } if (auto childRest = ir->getObjectPool().deref(sum->rest); childRest) { const auto childRestHash = getHashAs4UintsString(childRest, ir); sstr << "spectral_t rest = albedo<" << childRestHash << ">();\n"; - // TODO: what to do with child caches? } sstr << "spectral_t retval = product + rest;\n"; sstr << "return retval;\n}\n"; break; } + case CTrueIR::INode::EFinalType::CFactorCombiner: + { + const auto* combiner = dynamic_cast(node); + if (!combiner) + break; + + const auto hashString = getHashAs4UintsString(node, ir); + sstr << "template<>\nspectral_t albedo<" << hashString << ">()\n{\n"; // TODO: what args needed? uv? + + const auto childCount = combiner->getChildCount(); + + for (uint8_t i = 0; i < childCount; i++) + { + if (auto child = ir->getObjectPool().deref(combiner->getChildHandle(i)); child) + { + const auto childHash = getHashAs4UintsString(child, ir); + sstr << "spectral_t child" << static_cast(i) << " = albedo<" << childHash << ">();\n"; + } + } + + sstr << "spectral_t retval = "; + for (uint8_t i = 0; i < childCount; i++) // TODO: check for invalid children? + sstr << "child" << static_cast(i) << (i < childCount - 1 ? " + " : ""); + sstr << ";\n"; + sstr << "return retval;\n}\n"; + break; + } case CTrueIR::INode::EFinalType::CWeightedContributor: { - // TODO + const auto* contrib = dynamic_cast(node); + if (!contrib) + break; + + const auto hashString = getHashAs4UintsString(node, ir); + sstr << "template<>\nspectral_t albedo<" << hashString << ">()\n{\n"; // TODO: what args needed? uv? + + if (auto childContrib = ir->getObjectPool().deref(contrib->contributor); childContrib) + { + const auto childContribHash = getHashAs4UintsString(childContrib, ir); + sstr << "spectral_t contributor = albedo<" << childContribHash << ">();\n"; + } + if (auto childFactor = ir->getObjectPool().deref(contrib->factor); childFactor) + { + const auto childFactorHash = getHashAs4UintsString(childFactor, ir); + sstr << "spectral_t factor = albedo<" << childFactorHash << ">();\n"; + } + + sstr << "spectral_t retval = contributor + factor;\n"; + sstr << "return retval;\n}\n"; break; } case CTrueIR::INode::EFinalType::CCorellatedTransmission: { - // TODO + const auto* transmission = dynamic_cast(node); + if (!transmission) + break; + + const auto hashString = getHashAs4UintsString(node, ir); + sstr << "template<>\nspectral_t albedo<" << hashString << ">()\n{\n"; // TODO: what args needed? uv? + + if (auto child = ir->getObjectPool().deref(transmission->btdf); child) + { + const auto childHash = getHashAs4UintsString(child, ir); + sstr << "spectral_t btdf = albedo<" << childHash << ">();\n"; + } + if (auto child = ir->getObjectPool().deref(transmission->brdfBottom); child) + { + const auto childHash = getHashAs4UintsString(child, ir); + sstr << "spectral_t brdf = albedo<" << childHash << ">();\n"; + } + if (auto child = ir->getObjectPool().deref(transmission->coated); child) + { + const auto childHash = getHashAs4UintsString(child, ir); + sstr << "spectral_t coated = albedo<" << childHash << ">();\n"; + } + if (auto child = ir->getObjectPool().deref(transmission->next); child) + { + const auto childHash = getHashAs4UintsString(child, ir); + sstr << "spectral_t next = albedo<" << childHash << ">();\n"; + } + + sstr << "spectral_t retval = btdf + brdf + coated + next;\n"; + sstr << "return retval;\n}\n"; break; } - case CTrueIR::INode::EFinalType::COrenNayar: + case CTrueIR::INode::EFinalType::CSpectralVariable: { - const auto* oren_nayar = dynamic_cast(node); - if (!oren_nayar) + const auto* spectral = dynamic_cast(node); + if (!spectral) break; + auto bins = spectral->getSpectralBins(); const auto hashString = getHashAs4UintsString(node, ir); sstr << "template<>\nspectral_t albedo<" << hashString << ">()\n{\n"; // TODO: what args needed? uv? - sstr << "return hlsl::promote(0.0);\n}\n"; + if (bins > 1) + { + sstr << "return spectral_t("; + for (uint8_t i = 0; i < bins; i++) + sstr << spectral->getParameter(i).scale << (i < bins - 1 ? "," : ""); + sstr << ");\n}\n"; + } + else + sstr << "return hlsl::promote(" << spectral->getParameter(0).scale << ");\n}\n"; break; } + case CTrueIR::INode::EFinalType::CEmitter: + [[fallthrough]] + case CTrueIR::INode::EFinalType::COrenNayar: + [[fallthrough]] case CTrueIR::INode::EFinalType::CCookTorrance: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CBeer: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CFresnel: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CThinInfiniteScatterCorrection: { - const auto* cook_torrance = dynamic_cast(node); - if (!cook_torrance) - break; - const auto hashString = getHashAs4UintsString(node, ir); sstr << "template<>\nspectral_t albedo<" << hashString << ">()\n{\n"; // TODO: what args needed? uv? sstr << "return hlsl::promote(0.0);\n}\n"; From efec002942fbfcb3ff0db1e9887f305bce54900c Mon Sep 17 00:00:00 2001 From: keptsecret Date: Mon, 25 May 2026 16:16:50 +0700 Subject: [PATCH 6/6] backend normal hlsl impl --- .../CReferenceUnidirectionalPathTracing.cpp | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp b/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp index 5c95035650..487abfcacf 100644 --- a/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp +++ b/src/nbl/asset/material_compiler3/CReferenceUnidirectionalPathTracing.cpp @@ -19,8 +19,8 @@ namespace nbl::asset::material_compiler3 // define templates of node functions + util structs code << "template\nstruct gen_cache;\n"; // cache struct - code << "template\nspectral_t albedo();\n"; // TODO: what args needed? uv? - code << "template\nvector3_t normal();\n"; // TODO: what args needed? uv? + code << "template\nspectral_t albedo();\n"; // TODO: might add interaction arg + code << "template\nvector3_t normal(NBL_CONST_REF_ARG(aniso_interaction_t) inter);\n"; code << "template\nvector3_t aov_throughput();\n"; // TODO: confirm return value is vec3, what args needed? uv? code << "template\nvector3_t transparency();\n"; // TODO: return value is vec3? what args needed? @@ -305,6 +305,48 @@ void CReferenceUnidirectionalPathTracing::getAlbedoHLSLCode(std::ostringstream& } } +void CReferenceUnidirectionalPathTracing::getNormalHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir) +{ + switch (node->getFinalType()) + { + case CTrueIR::INode::EFinalType::COrientedLayer: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CContributorSum: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CFactorCombiner: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CWeightedContributor: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CCorellatedTransmission: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CSpectralVariable: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CEmitter: + [[fallthrough]] + case CTrueIR::INode::EFinalType::COrenNayar: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CCookTorrance: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CBeer: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CFresnel: + [[fallthrough]] + case CTrueIR::INode::EFinalType::CThinInfiniteScatterCorrection: + [[fallthrough]] + default: + { + const auto hashString = getHashAs4UintsString(node, ir); + sstr << R"===( +template<> +vector3_t normal<)===" << hashString << R"===(>(NBL_CONST_REF_ARG(aniso_interaction_t) inter) +{ + return inter.getN(); +} +)==="; // return shading normal by default + } + } +} + void CReferenceUnidirectionalPathTracing::getGenerateHLSLCode(std::ostringstream& sstr, const CTrueIR::INode* node, const CTrueIR* ir) { switch (node->getFinalType())