From 15fb982e4b7d1168755838cf26c5bacfe244e242 Mon Sep 17 00:00:00 2001 From: devshgraphicsprogramming Date: Tue, 12 May 2026 08:06:49 +0200 Subject: [PATCH 1/9] commonalize the node copying code --- include/nbl/asset/material_compiler3/CFrontendIR.h | 6 +----- include/nbl/asset/material_compiler3/CNodePool.h | 12 ++++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/nbl/asset/material_compiler3/CFrontendIR.h b/include/nbl/asset/material_compiler3/CFrontendIR.h index 675300dee1..240813506c 100644 --- a/include/nbl/asset/material_compiler3/CFrontendIR.h +++ b/include/nbl/asset/material_compiler3/CFrontendIR.h @@ -161,11 +161,7 @@ class CFrontendIR final : public CNodePool virtual _typed_pointer_type copy(CFrontendIR* ir) const = 0; #define COPY_DEFAULT_IMPL inline _typed_pointer_type copy(CFrontendIR* ir) const override final \ { \ - auto& pool = ir->getObjectPool(); \ - const auto copyH = pool.emplace > >(); \ - if (auto* const copy = pool.deref(copyH); copyH) \ - *copy = *this; \ - return copyH; \ + return CNodePool::copyNode > >(this,ir); \ } // child managment diff --git a/include/nbl/asset/material_compiler3/CNodePool.h b/include/nbl/asset/material_compiler3/CNodePool.h index cb7b9d5ff0..a4f144e0a1 100644 --- a/include/nbl/asset/material_compiler3/CNodePool.h +++ b/include/nbl/asset/material_compiler3/CNodePool.h @@ -118,6 +118,18 @@ class CNodePool : public core::IReferenceCounted protected: inline CNodePool(typename obj_pool_type::creation_params_type&& params) : m_composed(std::move(params)) {} + // does a shallow copy (no need to deref any of the children/deeper references), the pool itself must have a `deepCopy` method for that + template requires std::is_copy_assignable_v + static typed_pointer_type copyNode(const T* src, CNodePool* dstPool) + { + assert(src); + auto& pool = dstPool->getObjectPool(); + const auto copyH = pool.emplace(); + if (auto* const copy=pool.deref(copyH); copyH) + *copy = *src; + return copyH; + } + obj_pool_type m_composed; }; From a794d9fe2ba91cae40863c1cef909b61776f957e Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 12 May 2026 16:13:35 +0700 Subject: [PATCH 2/9] copy methods for true IR nodes, deepCopy for true IR --- .../nbl/asset/material_compiler3/CTrueIR.h | 47 ++++ src/nbl/asset/material_compiler3/CTrueIR.cpp | 206 ++++++++++++++++++ 2 files changed, 253 insertions(+) diff --git a/include/nbl/asset/material_compiler3/CTrueIR.h b/include/nbl/asset/material_compiler3/CTrueIR.h index 7abff85593..b932eb9999 100644 --- a/include/nbl/asset/material_compiler3/CTrueIR.h +++ b/include/nbl/asset/material_compiler3/CTrueIR.h @@ -248,6 +248,12 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! } #define HASH_OPTIONALS_HASH(HANDLE) if (HANDLE) {HASH_REQUIREDS_HASH(HANDLE);} else {hasher << core::blake3_hash_t::EmptyInput();} + virtual _typed_pointer_type copy(CTrueIR* ir) const = 0; +#define COPY_DEFAULT_IMPL inline _typed_pointer_type copy(CTrueIR* ir) const override final \ + { \ + return CNodePool::copyNode > >(this,ir); \ + } + // Each node is final and immutable, has a precomputed hash for the whole subtree beneath it. // Debug info does not form part of the hash, so can get wildly replaced. core::blake3_hash_t hash = core::blake3_hash_t::EmptyInput(); @@ -341,6 +347,15 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! return true; } + inline _typed_pointer_type copy(CTrueIR* ir) const override final + { + auto& pool = ir->getObjectPool(); + const auto copyH = pool.emplace > >(getState()); + if (auto* const copy = pool.deref(copyH); copyH) + *copy = *this; + return copyH; + } + typed_pointer_type child[1] = {{}}; }; // Note that this is not a root node, its a flipped leaf! @@ -366,6 +381,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! typed_pointer_type contributor = {}; // if null then assumed to be 1 typed_pointer_type factor = {}; + + protected: + COPY_DEFAULT_IMPL }; // One BRDF or BTDF component of a layer is represented as // f(w_i,w_o) = Sum_i^N Product_j^{N_i} h_{ij}(w_i,w_o) l_i(w_i,w_o) @@ -405,6 +423,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! typed_pointer_type product = {}; // the rest node is ... _typed_pointer_type rest = {}; + + protected: + COPY_DEFAULT_IMPL }; // For codegen, we can compute total uncorrelated layering by convolving every `h_{ij}(w_i,w_o) l_i(w_i,w_o)` term in the layer above with layer below @@ -442,6 +463,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! _typed_pointer_type coated = {}; // optional, indicates a "sibling" transmission thats next to this one _typed_pointer_type next = {}; + + protected: + COPY_DEFAULT_IMPL }; // The oriented layer is a layer with already all the Etas reciprocated, etc. class COrientedLayer final : public obj_pool_type::INonTrivial, public INode @@ -465,6 +489,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! typed_pointer_type brdfTop = {}; // this node must be non-null until the last layer typed_pointer_type firstTransmission = {}; + + protected: + COPY_DEFAULT_IMPL }; // class IFactorLeaf : public IFactor @@ -653,6 +680,12 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! inline uint8_t getSpectralBins() const override final {return getKnotCount();} NBL_API2 void printDot(std::ostringstream& sstr, const core::string& selfID) const override final; + + protected: + inline _typed_pointer_type copy(CTrueIR* ir) const override final + { + return static_cast(this)->copy(ir->getObjectPool()); + } }; using CSpectralVariableFactor = CSpectralVariable; @@ -700,6 +733,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! // TODO: semantic flags/metadata (symmetries of the profile) NBL_API2 void printDot(std::ostringstream& sstr, const core::string& selfID) const override final; + + protected: + COPY_DEFAULT_IMPL }; // To use a bump map, the Material needs to be provided UVs (which can or can not have associated tangents and smooth normals), but that's the responsibility of backend. @@ -755,6 +791,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(CDeltaTransmission);} inline CDeltaTransmission() = default; + + protected: + COPY_DEFAULT_IMPL }; class IBxDFWithNDF : public IBxDF { @@ -786,6 +825,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! inline COrenNayar() = default; NBL_API2 void printDot(std::ostringstream& sstr, const core::string& selfID) const override final; + + protected: + COPY_DEFAULT_IMPL }; class CCookTorrance final : public obj_pool_type::INonTrivial, public IBxDFWithNDF { @@ -819,6 +861,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! // producing an estimator with just Masking and Shadowing function ratios. The reason is because we can simplify our IR by separating out // BRDFs and BTDFs components into separate expressions, and also importance sample much better. typed_pointer_type orientedRealEta = {}; + + protected: + COPY_DEFAULT_IMPL }; //! Parameter Nodes //! Basic factor nodes @@ -965,6 +1010,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! } return true; } + + uint32_t deepCopy(typed_pointer_type* out, const std::span> orig, const CTrueIR* srcIR=nullptr); // TODO: Optimization passes on the IR // It is the backend's job to handle: diff --git a/src/nbl/asset/material_compiler3/CTrueIR.cpp b/src/nbl/asset/material_compiler3/CTrueIR.cpp index 497bdf9f23..8992965c62 100644 --- a/src/nbl/asset/material_compiler3/CTrueIR.cpp +++ b/src/nbl/asset/material_compiler3/CTrueIR.cpp @@ -110,6 +110,212 @@ CTrueIR::SBasicNodes::SBasicNodes(CTrueIR* ir) } } +uint32_t CTrueIR::deepCopy(typed_pointer_type* out, + const std::span> orig, const CTrueIR* srcIR) +{ + auto& dstPool = getObjectPool(); + // if not explicitly other, then its ours + if (!srcIR) + srcIR = this; + const auto& srcPool = srcIR->getObjectPool(); + + core::vector> stack; + stack.reserve(orig.size() + 32); + for (const auto& o : orig) + stack.push_back(o); + // use a hashmap to not explore whole DAG + core::unordered_map, typed_pointer_type> substitutions; + while (!stack.empty()) + { + const auto entry = stack.back(); + const auto* const node = srcPool.deref(entry); + if (!node) // this is an error + return {}; + const auto nodeType = node->getFinalType(); + if (auto& copyH = substitutions[entry]; !copyH) + { + switch (nodeType) + { + case INode::EFinalType::CFactorCombiner: + { + const auto* combiner = dynamic_cast(node); + const uint8_t childCount = combiner->getState().childCount; + if (childCount) + { + for (uint8_t c = 0; c < childCount; c++) + { + const auto childH = combiner->getChildHandle(c); + if (auto child = srcPool.deref(childH); !child) + continue; // this is not an error + stack.push_back(childH); + } + } + break; + } + case INode::EFinalType::CContributorSum: + { + const auto* contributeSum = dynamic_cast(node); + if (contributeSum) + { + typed_pointer_type children[] = { contributeSum->product, contributeSum->rest }; + for (const auto childH : children) + { + if (auto child = srcPool.deref(childH); !child) + continue; + stack.push_back(childH); + } + } + break; + } + case INode::EFinalType::CCorellatedTransmission: + { + const auto* transmission = dynamic_cast(node); + if (transmission) + { + typed_pointer_type children[] = { transmission->btdf, transmission->brdfBottom, transmission->next }; + for (const auto childH : children) + { + if (auto child = srcPool.deref(childH); !child) + continue; + stack.push_back(childH); + } + //layerStack.push_back(transmission->coated); TODO: how to handle coated? + } + break; + } + case INode::EFinalType::CWeightedContributor: + { + const auto* contributor = dynamic_cast(node); + if (contributor) + { + typed_pointer_type children[] = { contributor->contributor, contributor->factor }; + for (const auto childH : children) + { + if (auto child = srcPool.deref(childH); !child) + continue; + stack.push_back(childH); + } + } + break; + } + case INode::EFinalType::CCookTorrance: + { + const auto* ct = dynamic_cast(node); + if (ct) + if (const auto eta = srcPool.deref(ct->orientedRealEta); eta) + stack.push_back(ct->orientedRealEta); + break; + } + default: + break; + } + + // copy copies everything including child handles + copyH = node->copy(this); + if (!copyH) + return {}; + } + else + { + auto* const copy = dstPool.deref(copyH); + auto setCopyChildren = [&](const typed_pointer_type& src, typed_pointer_type& cop) -> void + { + if (auto child = srcPool.deref(src); child) + { + auto found = substitutions.find(src); + assert(found != substitutions.end()); + cop = _static_cast>(found->second); + } + }; + switch (nodeType) + { + case INode::EFinalType::CFactorCombiner: + { + const auto* combiner = dynamic_cast(node); + const uint8_t childCount = combiner->getState().childCount; + if (childCount) + { + for (uint8_t c = 0; c < childCount; c++) + { + const auto childH = combiner->getChildHandle(c); + if (!childH) + continue; + auto found = substitutions.find(childH); + assert(found != substitutions.end()); + + const auto child = _static_cast>(found->second); + dynamic_cast(copy)->setChildHandle(c, child); + } + } + break; + } + case INode::EFinalType::CContributorSum: + { + const auto* contributeSum = dynamic_cast(node); + auto* copySum = dynamic_cast(copy); + if (contributeSum && copySum) + { + setCopyChildren(contributeSum->product, copySum->product); + setCopyChildren(contributeSum->product, copySum->product); + } + break; + } + case INode::EFinalType::CCorellatedTransmission: + { + const auto* transmission = dynamic_cast(node); + auto* copyTransmission = dynamic_cast(copy); + if (transmission && copyTransmission) + { + setCopyChildren(transmission->btdf, copyTransmission->btdf); + setCopyChildren(transmission->brdfBottom, copyTransmission->brdfBottom); + setCopyChildren(transmission->next, copyTransmission->next); + //layerStack.push_back(transmission->coated); TODO: how to handle coated? + } + break; + } + case INode::EFinalType::CWeightedContributor: + { + const auto* contributor = dynamic_cast(node); + auto* copyContributor = dynamic_cast(copy); + if (contributor && copyContributor) + { + setCopyChildren(contributor->contributor, copyContributor->contributor); + setCopyChildren(contributor->factor, copyContributor->factor); + } + break; + } + case INode::EFinalType::CCookTorrance: + { + const auto* ct = dynamic_cast(node); + auto* copyCt = dynamic_cast(copy); + if (ct && copyCt) + setCopyChildren(ct->orientedRealEta, copyCt->orientedRealEta); + break; + } + default: + break; + } + stack.pop_back(); + } + } + + uint32_t invalidCount = 0; + auto copies = out; + for (const auto& o : orig) + { + auto copy = substitutions[o]; + const auto* const node = dstPool.deref(copy); + if (!node) // this is invalid + { + invalidCount++; + continue; + } + *copies = copy; + copies++; + } + return invalidCount; +} + void CTrueIR::SDotPrinter::operator()(std::ostringstream& output) { output << "digraph {\n"; From d326bc87f45d1ef13ada918e9a8fd06a661e5f83 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Tue, 12 May 2026 16:33:37 +0700 Subject: [PATCH 3/9] don't compact copy output --- src/nbl/asset/material_compiler3/CTrueIR.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/nbl/asset/material_compiler3/CTrueIR.cpp b/src/nbl/asset/material_compiler3/CTrueIR.cpp index 8992965c62..0731fbeb52 100644 --- a/src/nbl/asset/material_compiler3/CTrueIR.cpp +++ b/src/nbl/asset/material_compiler3/CTrueIR.cpp @@ -306,11 +306,9 @@ uint32_t CTrueIR::deepCopy(typed_pointer_type* out, auto copy = substitutions[o]; const auto* const node = dstPool.deref(copy); if (!node) // this is invalid - { invalidCount++; - continue; - } - *copies = copy; + else + *copies = copy; copies++; } return invalidCount; From 9263bf7b9302fc217af321b28f6284e04e28dcaa Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 13 May 2026 15:05:21 +0700 Subject: [PATCH 4/9] change to use virtual methods for children in true IR nodes, refactor changes in deepCopy and print --- .../nbl/asset/material_compiler3/CTrueIR.h | 114 +++++++- src/nbl/asset/material_compiler3/CTrueIR.cpp | 250 ++---------------- 2 files changed, 130 insertions(+), 234 deletions(-) diff --git a/include/nbl/asset/material_compiler3/CTrueIR.h b/include/nbl/asset/material_compiler3/CTrueIR.h index b932eb9999..82ad47ddd3 100644 --- a/include/nbl/asset/material_compiler3/CTrueIR.h +++ b/include/nbl/asset/material_compiler3/CTrueIR.h @@ -12,7 +12,6 @@ #include "nbl/asset/format/EColorSpace.h" #include "nbl/asset/ICPUImageView.h" - namespace nbl::asset::material_compiler3 { @@ -20,6 +19,7 @@ namespace nbl::asset::material_compiler3 // They appear "flipped upside down", its expected our backends will evaluate contributors first, and then bother with the attenuators. class CTrueIR : public CNodePool // TODO: turn into an asset! { + using block_allocator_type = CNodePool::obj_pool_type::block_allocator_type; template using _typed_pointer_type = CNodePool::obj_pool_type::mem_pool_type::typed_pointer_type; @@ -229,11 +229,34 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! return hasher.operator core::blake3_hash_t(); } + // Only sane child count allowed + virtual uint8_t getChildCount() const = 0; + inline _typed_pointer_type getChildHandle(const uint8_t ix) + { + if (ix < getChildCount()) + return getChildHandle_impl(ix); + return {}; + } + inline _typed_pointer_type getChildHandle(const uint8_t ix) const + { + auto retval = const_cast(this)->getChildHandle(ix); + return retval; + } + virtual inline std::string_view getChildName_impl(const uint8_t ix) const { return ""; } virtual inline void printDot(std::ostringstream& sstr, const core::string& selfID) const {} protected: friend class CTrueIR; + // child managment + virtual inline _typed_pointer_type getChildHandle_impl(const uint8_t ix) const { assert(false); return {}; } + inline void setChild(const uint8_t ix, _typed_pointer_type newChild) + { + assert(ix < getChildCount()); + setChild_impl(ix, newChild); + } + virtual inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) { assert(false); } + inline bool recomputeHash(const obj_pool_type& pool) { hash = computeHash(pool); @@ -319,16 +342,12 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! padding = std::bit_cast(state); } + inline uint8_t getChildCount() const override final { return getState().childCount; } + // Only sane child count allowed - inline typed_pointer_type getChildHandle(const uint8_t ix) const - { - if (ix handle) { - if (ix getChildHandle_impl(const uint8_t ix) const override final { return block_allocator_type::_static_cast(child[0]); } + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final { child[0] = block_allocator_type::_static_cast(newChild); } + inline _typed_pointer_type copy(CTrueIR* ir) const override final { auto& pool = ir->getObjectPool(); @@ -374,6 +396,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! public: inline EFinalType getFinalType() const override {return EFinalType::CWeightedContributor;} + inline uint8_t getChildCount() const override final { return 2; } + inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(CWeightedContributor);} inline std::string_view getChildName_impl(const uint8_t ix) const override final { return ix ? "factor" : "contributor"; } @@ -384,6 +408,18 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! protected: COPY_DEFAULT_IMPL + + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final + { + return ix ? block_allocator_type::_static_cast(factor) : block_allocator_type::_static_cast(contributor); + } + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final + { + if (ix) + factor = block_allocator_type::_static_cast(newChild); + else + contributor = block_allocator_type::_static_cast(newChild); + } }; // One BRDF or BTDF component of a layer is represented as // f(w_i,w_o) = Sum_i^N Product_j^{N_i} h_{ij}(w_i,w_o) l_i(w_i,w_o) @@ -416,6 +452,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! public: inline EFinalType getFinalType() const override {return EFinalType::CContributorSum;} + inline uint8_t getChildCount() const override final { return 2; } + inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(CContributorSum);} inline std::string_view getChildName_impl(const uint8_t ix) const override final { return ix ? "rest" : "product"; } @@ -426,6 +464,18 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! protected: COPY_DEFAULT_IMPL + + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final + { + return ix ? block_allocator_type::_static_cast(rest) : block_allocator_type::_static_cast(product); + } + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final + { + if (ix) + rest = block_allocator_type::_static_cast(newChild); + else + product = block_allocator_type::_static_cast(newChild); + } }; // For codegen, we can compute total uncorrelated layering by convolving every `h_{ij}(w_i,w_o) l_i(w_i,w_o)` term in the layer above with layer below @@ -449,6 +499,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! public: inline EFinalType getFinalType() const override {return EFinalType::CCorellatedTransmission;} + inline uint8_t getChildCount() const override final { return 3; } // TODO: or 4? + inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(CCorellatedTransmission);} inline std::string_view getChildName_impl(const uint8_t ix) const override final { return ix ? (ix > 1 ? "next" : "brdfBottom") : "btdf"; } @@ -466,6 +518,24 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! protected: COPY_DEFAULT_IMPL + + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final + { + if (ix > 1) + return block_allocator_type::_static_cast(next); + if (ix) + return block_allocator_type::_static_cast(brdfBottom); + return block_allocator_type::_static_cast(btdf); + } + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final + { + if (ix > 1) + next = block_allocator_type::_static_cast(newChild); + else if (ix) + brdfBottom = block_allocator_type::_static_cast(newChild); + else + btdf = block_allocator_type::_static_cast(newChild); + } }; // The oriented layer is a layer with already all the Etas reciprocated, etc. class COrientedLayer final : public obj_pool_type::INonTrivial, public INode @@ -480,6 +550,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! public: inline EFinalType getFinalType() const override {return EFinalType::COrientedLayer;} + inline uint8_t getChildCount() const override final { return 2; } + inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(COrientedLayer);} // you can set the children later @@ -492,6 +564,18 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! protected: COPY_DEFAULT_IMPL + + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final + { + return ix ? block_allocator_type::_static_cast(firstTransmission) : block_allocator_type::_static_cast(brdfTop); + } + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final + { + if (ix) + firstTransmission = block_allocator_type::_static_cast(newChild); + else + brdfTop = block_allocator_type::_static_cast(newChild); + } }; // class IFactorLeaf : public IFactor @@ -676,6 +760,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! return EFinalType::CSpectralVariable; } + inline uint8_t getChildCount() const override final { return 0; } + // inline uint8_t getSpectralBins() const override final {return getKnotCount();} @@ -715,6 +801,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! public: inline EFinalType getFinalType() const override {return EFinalType::CEmitter;} + inline uint8_t getChildCount() const override final { return 0; } + inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(CEmitter);} inline bool isEmitter() const override {return true;} @@ -788,6 +876,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! public: inline EFinalType getFinalType() const override {return EFinalType::CDeltaTransmission;} + inline uint8_t getChildCount() const override final { return 0; } + inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(CDeltaTransmission);} inline CDeltaTransmission() = default; @@ -820,6 +910,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! inline EFinalType getFinalType() const override {return EFinalType::COrenNayar;} + inline uint8_t getChildCount() const override final { return 0; } + inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(COrenNayar);} inline COrenNayar() = default; @@ -845,7 +937,10 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! public: inline EFinalType getFinalType() const override {return EFinalType::CCookTorrance;} + inline uint8_t getChildCount() const override final { return 1; } + inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(CCookTorrance);} + inline std::string_view getChildName_impl(const uint8_t ix) const override final { return "orientedRealEta"; } inline CCookTorrance() = default; @@ -864,6 +959,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! protected: COPY_DEFAULT_IMPL + + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final { return block_allocator_type::_static_cast(orientedRealEta); } + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final { orientedRealEta = block_allocator_type::_static_cast(newChild); } }; //! Parameter Nodes //! Basic factor nodes diff --git a/src/nbl/asset/material_compiler3/CTrueIR.cpp b/src/nbl/asset/material_compiler3/CTrueIR.cpp index 0731fbeb52..2f20c14763 100644 --- a/src/nbl/asset/material_compiler3/CTrueIR.cpp +++ b/src/nbl/asset/material_compiler3/CTrueIR.cpp @@ -132,169 +132,37 @@ uint32_t CTrueIR::deepCopy(typed_pointer_type* out, if (!node) // this is an error return {}; const auto nodeType = node->getFinalType(); + const auto childCount = node->getChildCount(); if (auto& copyH = substitutions[entry]; !copyH) { - switch (nodeType) + for (uint8_t c = 0; c < childCount; c++) { - case INode::EFinalType::CFactorCombiner: - { - const auto* combiner = dynamic_cast(node); - const uint8_t childCount = combiner->getState().childCount; - if (childCount) - { - for (uint8_t c = 0; c < childCount; c++) - { - const auto childH = combiner->getChildHandle(c); - if (auto child = srcPool.deref(childH); !child) - continue; // this is not an error - stack.push_back(childH); - } - } - break; - } - case INode::EFinalType::CContributorSum: - { - const auto* contributeSum = dynamic_cast(node); - if (contributeSum) - { - typed_pointer_type children[] = { contributeSum->product, contributeSum->rest }; - for (const auto childH : children) - { - if (auto child = srcPool.deref(childH); !child) - continue; - stack.push_back(childH); - } - } - break; - } - case INode::EFinalType::CCorellatedTransmission: - { - const auto* transmission = dynamic_cast(node); - if (transmission) - { - typed_pointer_type children[] = { transmission->btdf, transmission->brdfBottom, transmission->next }; - for (const auto childH : children) - { - if (auto child = srcPool.deref(childH); !child) - continue; - stack.push_back(childH); - } - //layerStack.push_back(transmission->coated); TODO: how to handle coated? - } - break; - } - case INode::EFinalType::CWeightedContributor: - { - const auto* contributor = dynamic_cast(node); - if (contributor) - { - typed_pointer_type children[] = { contributor->contributor, contributor->factor }; - for (const auto childH : children) - { - if (auto child = srcPool.deref(childH); !child) - continue; - stack.push_back(childH); - } - } - break; - } - case INode::EFinalType::CCookTorrance: - { - const auto* ct = dynamic_cast(node); - if (ct) - if (const auto eta = srcPool.deref(ct->orientedRealEta); eta) - stack.push_back(ct->orientedRealEta); - break; - } - default: - break; + const auto childH = node->getChildHandle(c); + if (auto child = srcPool.deref(childH); !child) + continue; // this is not an error + stack.push_back(childH); } + // TODO: how to handle coated in CorrelatedTransmission? + // copy copies everything including child handles copyH = node->copy(this); if (!copyH) - return {}; + continue; // TODO: handle invalid nodes somehow } else { auto* const copy = dstPool.deref(copyH); - auto setCopyChildren = [&](const typed_pointer_type& src, typed_pointer_type& cop) -> void - { - if (auto child = srcPool.deref(src); child) - { - auto found = substitutions.find(src); - assert(found != substitutions.end()); - cop = _static_cast>(found->second); - } - }; - switch (nodeType) - { - case INode::EFinalType::CFactorCombiner: + for (uint8_t c = 0; c < childCount; c++) { - const auto* combiner = dynamic_cast(node); - const uint8_t childCount = combiner->getState().childCount; - if (childCount) - { - for (uint8_t c = 0; c < childCount; c++) - { - const auto childH = combiner->getChildHandle(c); - if (!childH) - continue; - auto found = substitutions.find(childH); - assert(found != substitutions.end()); - - const auto child = _static_cast>(found->second); - dynamic_cast(copy)->setChildHandle(c, child); - } - } - break; - } - case INode::EFinalType::CContributorSum: - { - const auto* contributeSum = dynamic_cast(node); - auto* copySum = dynamic_cast(copy); - if (contributeSum && copySum) - { - setCopyChildren(contributeSum->product, copySum->product); - setCopyChildren(contributeSum->product, copySum->product); - } - break; - } - case INode::EFinalType::CCorellatedTransmission: - { - const auto* transmission = dynamic_cast(node); - auto* copyTransmission = dynamic_cast(copy); - if (transmission && copyTransmission) - { - setCopyChildren(transmission->btdf, copyTransmission->btdf); - setCopyChildren(transmission->brdfBottom, copyTransmission->brdfBottom); - setCopyChildren(transmission->next, copyTransmission->next); - //layerStack.push_back(transmission->coated); TODO: how to handle coated? - } - break; - } - case INode::EFinalType::CWeightedContributor: - { - const auto* contributor = dynamic_cast(node); - auto* copyContributor = dynamic_cast(copy); - if (contributor && copyContributor) - { - setCopyChildren(contributor->contributor, copyContributor->contributor); - setCopyChildren(contributor->factor, copyContributor->factor); - } - break; - } - case INode::EFinalType::CCookTorrance: - { - const auto* ct = dynamic_cast(node); - auto* copyCt = dynamic_cast(copy); - if (ct && copyCt) - setCopyChildren(ct->orientedRealEta, copyCt->orientedRealEta); - break; - } - default: - break; + const auto childH = node->getChildHandle(c); + if (!childH) + continue; + auto found = substitutions.find(childH); + assert(found != substitutions.end()); + copy->setChild(c, found->second); } + // TODO: how to handle coated in CorrelatedTransmission? stack.pop_back(); } } @@ -333,10 +201,12 @@ void CTrueIR::SDotPrinter::operator()(std::ostringstream& output) if (!node) continue; output << "\n\t" << m_ir->getLabelledNodeID(entry); - auto printChildren = [&](std::span> children, const INode* node)->void { - uint32_t childIx = 0u; - for (const auto childHandle : children) + const auto childCount = node->getChildCount(); + if (childCount) + { + for (auto childIx = 0; childIx < childCount; childIx++) { + const auto childHandle = node->getChildHandle(childIx); if (const auto child = m_ir->getObjectPool().deref(childHandle); child) { output << "\n\t" << nodeID << " -> " << m_ir->getNodeID(childHandle) << "[label=\"" << node->getChildName_impl(childIx) << "\"]"; @@ -346,84 +216,12 @@ void CTrueIR::SDotPrinter::operator()(std::ostringstream& output) nodeStack.push_back(childHandle); visitedNodes.insert(childHandle); } - childIx++; } - }; - switch (node->getFinalType()) - { - case INode::EFinalType::CFactorCombiner: - { - const auto* combiner = dynamic_cast(node); - const auto state = combiner->getState(); - const auto childCount = state.childCount; - if (childCount) - { - for (auto childIx = 0; childIx < childCount; childIx++) - { - const auto childHandle = combiner->getChildHandle(childIx); - if (const auto child = m_ir->getObjectPool().deref(childHandle); child) - { - output << "\n\t" << nodeID << " -> " << m_ir->getNodeID(childHandle); - const auto visited = visitedNodes.find(childHandle); - if (visited != visitedNodes.end()) - continue; - nodeStack.push_back(childHandle); - visitedNodes.insert(childHandle); - } - } - } - break; - } - case INode::EFinalType::CContributorSum: - { - const auto* contributeSum = dynamic_cast(node); - if (contributeSum) - { - typed_pointer_type children[] = {contributeSum->product, contributeSum->rest}; - printChildren(children, node); - } - break; } - case INode::EFinalType::CCorellatedTransmission: + if (node->getFinalType() == INode::EFinalType::CCorellatedTransmission) { const auto* transmission = dynamic_cast(node); - if (transmission) - { - typed_pointer_type children[] = { transmission->btdf, transmission->brdfBottom, transmission->next }; - printChildren(children, node); - layerStack.push_back(transmission->coated); - } - break; - } - case INode::EFinalType::CWeightedContributor: - { - const auto* contributor = dynamic_cast(node); - if (contributor) - { - typed_pointer_type children[] = { contributor->contributor, contributor->factor }; - printChildren(children, node); - } - break; - } - case INode::EFinalType::CCookTorrance: - { - const auto* ct = dynamic_cast(node); - if (ct) - { - if (const auto eta = m_ir->getObjectPool().deref(ct->orientedRealEta); eta) - { - output << "\n\t" << nodeID << " -> " << m_ir->getNodeID(ct->orientedRealEta) << "[label=\"orientedRealEta\"]"; - const auto visited = visitedNodes.find(ct->orientedRealEta); - if (visited != visitedNodes.end()) - continue; - nodeStack.push_back(ct->orientedRealEta); - visitedNodes.insert(ct->orientedRealEta); - } - } - break; - } - default: - break; + layerStack.push_back(transmission->coated); } // special printing node->printDot(output, nodeID); From 08508f5123bab1ad33717224f94fb65cde152bb5 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Wed, 13 May 2026 16:34:30 +0700 Subject: [PATCH 5/9] some fixes to trueIR deepCopy + handle invalid node on stack --- src/nbl/asset/material_compiler3/CTrueIR.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/nbl/asset/material_compiler3/CTrueIR.cpp b/src/nbl/asset/material_compiler3/CTrueIR.cpp index 2f20c14763..f327b342b1 100644 --- a/src/nbl/asset/material_compiler3/CTrueIR.cpp +++ b/src/nbl/asset/material_compiler3/CTrueIR.cpp @@ -129,26 +129,27 @@ uint32_t CTrueIR::deepCopy(typed_pointer_type* out, { const auto entry = stack.back(); const auto* const node = srcPool.deref(entry); - if (!node) // this is an error - return {}; - const auto nodeType = node->getFinalType(); + assert(!node); const auto childCount = node->getChildCount(); if (auto& copyH = substitutions[entry]; !copyH) { + uint8_t pushedChildren = 0; for (uint8_t c = 0; c < childCount; c++) { const auto childH = node->getChildHandle(c); if (auto child = srcPool.deref(childH); !child) continue; // this is not an error stack.push_back(childH); + pushedChildren++; } // TODO: how to handle coated in CorrelatedTransmission? // copy copies everything including child handles copyH = node->copy(this); - if (!copyH) - continue; // TODO: handle invalid nodes somehow + if (!copyH) // node invalid so pop stack until copy and all added children removed + for (uint8_t i = 0; i < pushedChildren + 1; i++) + stack.pop_back(); } else { From e944cfff7e7511cc606801f4f6d5ce36942e2254 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 14 May 2026 10:40:57 +0700 Subject: [PATCH 6/9] avoid use of _static_cast in getChild_impl, should return const INode --- .../nbl/asset/material_compiler3/CTrueIR.h | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/include/nbl/asset/material_compiler3/CTrueIR.h b/include/nbl/asset/material_compiler3/CTrueIR.h index 346c321a03..57057a836d 100644 --- a/include/nbl/asset/material_compiler3/CTrueIR.h +++ b/include/nbl/asset/material_compiler3/CTrueIR.h @@ -234,7 +234,7 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! // Only sane child count allowed virtual uint8_t getChildCount() const = 0; - inline _typed_pointer_type getChildHandle(const uint8_t ix) + inline _typed_pointer_type getChildHandle(const uint8_t ix) { if (ix < getChildCount()) return getChildHandle_impl(ix); @@ -252,13 +252,13 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! protected: friend class CTrueIR; // child managment - virtual inline _typed_pointer_type getChildHandle_impl(const uint8_t ix) const { assert(false); return {}; } + virtual inline _typed_pointer_type getChildHandle_impl(const uint8_t ix) const { assert(false); return {}; } inline void setChild(const uint8_t ix, _typed_pointer_type newChild) { assert(ix < getChildCount()); setChild_impl(ix, newChild); } - virtual inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) { assert(false); } + virtual inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) { assert(false); } inline bool recomputeHash(const obj_pool_type& pool) { @@ -369,8 +369,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! return true; } - inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final { return block_allocator_type::_static_cast(child[0]); } - inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final { child[0] = block_allocator_type::_static_cast(newChild); } + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final { return child[ix]; } + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final { child[ix] = block_allocator_type::_static_cast(newChild); } inline _typed_pointer_type copy(CTrueIR* ir) const override final { @@ -412,11 +412,13 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! protected: COPY_DEFAULT_IMPL - inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final { - return ix ? block_allocator_type::_static_cast(factor) : block_allocator_type::_static_cast(contributor); + if (ix) + return factor; + return contributor; } - inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final { if (ix) factor = block_allocator_type::_static_cast(newChild); @@ -468,11 +470,13 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! protected: COPY_DEFAULT_IMPL - inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final { - return ix ? block_allocator_type::_static_cast(rest) : block_allocator_type::_static_cast(product); + if (ix) + return rest; + return product; } - inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final { if (ix) rest = block_allocator_type::_static_cast(newChild); @@ -522,15 +526,15 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! protected: COPY_DEFAULT_IMPL - inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final { if (ix > 1) - return block_allocator_type::_static_cast(next); + return next; if (ix) - return block_allocator_type::_static_cast(brdfBottom); - return block_allocator_type::_static_cast(btdf); + return brdfBottom; + return btdf; } - inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final { if (ix > 1) next = block_allocator_type::_static_cast(newChild); @@ -568,11 +572,14 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! protected: COPY_DEFAULT_IMPL - inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final { - return ix ? block_allocator_type::_static_cast(firstTransmission) : block_allocator_type::_static_cast(brdfTop); + if (ix) + return firstTransmission; + else + return brdfTop; } - inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final { if (ix) firstTransmission = block_allocator_type::_static_cast(newChild); From 82bebe49827adbd571afb07d130ea9dc8e087ebb Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 14 May 2026 11:19:20 +0700 Subject: [PATCH 7/9] getChild methods for new Node derived classes --- .../nbl/asset/material_compiler3/CTrueIR.h | 65 ++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/include/nbl/asset/material_compiler3/CTrueIR.h b/include/nbl/asset/material_compiler3/CTrueIR.h index 57057a836d..f6ddcabeb4 100644 --- a/include/nbl/asset/material_compiler3/CTrueIR.h +++ b/include/nbl/asset/material_compiler3/CTrueIR.h @@ -981,8 +981,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! protected: COPY_DEFAULT_IMPL - inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final { return block_allocator_type::_static_cast(orientedRealEta); } - inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final { orientedRealEta = block_allocator_type::_static_cast(newChild); } + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final { return orientedRealEta; } + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final { orientedRealEta = block_allocator_type::_static_cast(newChild); } }; //! Basic factor nodes // Effective transparency = exp2(log2(perpTransmittance)*thickness/dot(refract(V,X,eta),X)) = exp2(log2(perpTransmittance)*thickness*inversesqrt(1.f+(LdotX-1)*rcpEta)) @@ -1001,6 +1001,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! public: inline EFinalType getFinalType() const override {return EFinalType::CBeer;} + inline uint8_t getChildCount() const override final { return 2; } + inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(CBeer);} inline std::string_view getChildName_impl(const uint8_t ix) const override final {return ix ? "Thickness":"Perpendicular\\nTransmittance";} @@ -1013,6 +1015,23 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! typed_pointer_type thickness = {}; // can be worked out by analyzing what we point to, but not needed uint8_t channels = 3; + + protected: + COPY_DEFAULT_IMPL + + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final + { + if (ix) + return thickness; + return perpTransmittance; + } + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final + { + if (ix) + thickness = block_allocator_type::_static_cast(newChild); + else + perpTransmittance = block_allocator_type::_static_cast(newChild); + } }; class CFresnel final : public obj_pool_type::INonTrivial, public IFactorLeaf { @@ -1035,6 +1054,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! public: inline EFinalType getFinalType() const override {return EFinalType::CFresnel;} + inline uint8_t getChildCount() const override final { return 2; } + inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(CFresnel);} inline std::string_view getChildName_impl(const uint8_t ix) const override final {return ix ? "Imaginary":"Real";} @@ -1049,6 +1070,23 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! uint8_t reciprocateEtas : 1 = false; // can be worked out by analyzing what we point to, but not needed uint8_t channels : 7 = 3; + + protected: + COPY_DEFAULT_IMPL + + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final + { + if (ix) + return orientedImagEta; + return orientedRealEta; + } + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final + { + if (ix) + orientedImagEta = block_allocator_type::_static_cast(newChild); + else + orientedRealEta = block_allocator_type::_static_cast(newChild); + } }; class CThinInfiniteScatterCorrection final : public obj_pool_type::INonTrivial, public IFactorLeaf { @@ -1067,6 +1105,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! public: inline EFinalType getFinalType() const override {return EFinalType::CThinInfiniteScatterCorrection;} + inline uint8_t getChildCount() const override final { return 3; } + inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(CThinInfiniteScatterCorrection);} inline std::string_view getChildName_impl(const uint8_t ix) const override final {return ix ? (ix>1 ? "reflectanceBottom":"extinction"):"reflectanceTop";} @@ -1081,6 +1121,27 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! typed_pointer_type reflectanceBottom = {}; // can be worked out by analyzing what we point to, but not needed uint8_t channels = 3; + + protected: + COPY_DEFAULT_IMPL + + inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final + { + if (ix > 1) + return reflectanceBottom; + if (ix) + return extinction; + return reflectanceTop; + } + inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final + { + if (ix > 1) + reflectanceBottom = block_allocator_type::_static_cast(newChild); + if (ix) + extinction = block_allocator_type::_static_cast(newChild); + else + reflectanceTop = block_allocator_type::_static_cast(newChild); + } }; #undef TYPE_NAME_STR #undef HASH_THE_HASH From b91a1b2c37233b0e3f6b4af0bb535a5a493bac11 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 14 May 2026 11:55:13 +0700 Subject: [PATCH 8/9] handle coated layer in CorrelatedTransmission, missing undef --- .../nbl/asset/material_compiler3/CTrueIR.h | 48 +++++++++++++++---- src/nbl/asset/material_compiler3/CTrueIR.cpp | 8 ---- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/include/nbl/asset/material_compiler3/CTrueIR.h b/include/nbl/asset/material_compiler3/CTrueIR.h index f6ddcabeb4..87d2cddce8 100644 --- a/include/nbl/asset/material_compiler3/CTrueIR.h +++ b/include/nbl/asset/material_compiler3/CTrueIR.h @@ -506,10 +506,23 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! public: inline EFinalType getFinalType() const override {return EFinalType::CCorellatedTransmission;} - inline uint8_t getChildCount() const override final { return 3; } // TODO: or 4? + inline uint8_t getChildCount() const override final { return 4; } inline const std::string_view getTypeName() const override {return TYPE_NAME_STR(CCorellatedTransmission);} - inline std::string_view getChildName_impl(const uint8_t ix) const override final { return ix ? (ix > 1 ? "next" : "brdfBottom") : "btdf"; } + inline std::string_view getChildName_impl(const uint8_t ix) const override final + { + switch (ix) + { + case 1: + return "brdfBottom"; + case 2: + return "coated"; + case 3: + return "next"; + default: + return "btdf"; + } + } // you can set the children later inline CCorellatedTransmission() = default; @@ -528,20 +541,34 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! inline typed_pointer_type getChildHandle_impl(const uint8_t ix) const override final { - if (ix > 1) - return next; - if (ix) + switch (ix) + { + case 1: return brdfBottom; - return btdf; + case 2: + return coated; + case 3: + return next; + default: + return btdf; + } } inline void setChild_impl(const uint8_t ix, _typed_pointer_type newChild) override final { - if (ix > 1) - next = block_allocator_type::_static_cast(newChild); - else if (ix) + switch (ix) + { + case 1: brdfBottom = block_allocator_type::_static_cast(newChild); - else + break; + case 2: + coated = block_allocator_type::_static_cast(newChild); + break; + case 3: + next = block_allocator_type::_static_cast(newChild); + break; + default: btdf = block_allocator_type::_static_cast(newChild); + } } }; // The oriented layer is a layer with already all the Etas reciprocated, etc. @@ -1143,6 +1170,7 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! reflectanceTop = block_allocator_type::_static_cast(newChild); } }; +#undef COPY_DEFAULT_IMPL #undef TYPE_NAME_STR #undef HASH_THE_HASH diff --git a/src/nbl/asset/material_compiler3/CTrueIR.cpp b/src/nbl/asset/material_compiler3/CTrueIR.cpp index f327b342b1..3874b9472b 100644 --- a/src/nbl/asset/material_compiler3/CTrueIR.cpp +++ b/src/nbl/asset/material_compiler3/CTrueIR.cpp @@ -143,8 +143,6 @@ uint32_t CTrueIR::deepCopy(typed_pointer_type* out, pushedChildren++; } - // TODO: how to handle coated in CorrelatedTransmission? - // copy copies everything including child handles copyH = node->copy(this); if (!copyH) // node invalid so pop stack until copy and all added children removed @@ -163,7 +161,6 @@ uint32_t CTrueIR::deepCopy(typed_pointer_type* out, assert(found != substitutions.end()); copy->setChild(c, found->second); } - // TODO: how to handle coated in CorrelatedTransmission? stack.pop_back(); } } @@ -219,11 +216,6 @@ void CTrueIR::SDotPrinter::operator()(std::ostringstream& output) } } } - if (node->getFinalType() == INode::EFinalType::CCorellatedTransmission) - { - const auto* transmission = dynamic_cast(node); - layerStack.push_back(transmission->coated); - } // special printing node->printDot(output, nodeID); } From 972a4850e92ae89d9d0d96eee9dcb6deb842c550 Mon Sep 17 00:00:00 2001 From: keptsecret Date: Thu, 14 May 2026 14:43:20 +0700 Subject: [PATCH 9/9] factor combiner should make a copy of all its children too --- include/nbl/asset/material_compiler3/CTrueIR.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/nbl/asset/material_compiler3/CTrueIR.h b/include/nbl/asset/material_compiler3/CTrueIR.h index 87d2cddce8..2814fec51e 100644 --- a/include/nbl/asset/material_compiler3/CTrueIR.h +++ b/include/nbl/asset/material_compiler3/CTrueIR.h @@ -377,7 +377,14 @@ class CTrueIR : public CNodePool // TODO: turn into an asset! auto& pool = ir->getObjectPool(); const auto copyH = pool.emplace > >(getState()); if (auto* const copy = pool.deref(copyH); copyH) - *copy = *this; + { + for (uint64_t c = 0; c < getState().childCount; c++) + { + auto childHandle = child[c]; + if (auto* const _child = pool.deref(childHandle); _child) + copy->child[c] = block_allocator_type::_static_cast(_child->copy(ir)); + } + } return copyH; }