Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions include/nbl/asset/material_compiler3/CTrueIR.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<INode> copy(CTrueIR* ir) const = 0;
#define COPY_DEFAULT_IMPL inline _typed_pointer_type<INode> copy(CTrueIR* ir) const override final \
{ \
return CNodePool::copyNode<std::remove_const_t<std::remove_pointer_t<decltype(this)> > >(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();
Expand Down Expand Up @@ -341,6 +347,15 @@ class CTrueIR : public CNodePool // TODO: turn into an asset!
return true;
}

inline _typed_pointer_type<INode> copy(CTrueIR* ir) const override final
{
auto& pool = ir->getObjectPool();
const auto copyH = pool.emplace<std::remove_const_t<std::remove_pointer_t<decltype(this)> > >(getState());
if (auto* const copy = pool.deref(copyH); copyH)
*copy = *this;
return copyH;
}

typed_pointer_type<const IFactor> child[1] = {{}};
};
// Note that this is not a root node, its a flipped leaf!
Expand All @@ -366,6 +381,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset!
typed_pointer_type<const IContributor> contributor = {};
// if null then assumed to be 1
typed_pointer_type<const IFactor> 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)
Expand Down Expand Up @@ -405,6 +423,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset!
typed_pointer_type<const CWeightedContributor> product = {};
// the rest node is ...
_typed_pointer_type<const CContributorSum> 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
Expand Down Expand Up @@ -442,6 +463,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset!
_typed_pointer_type<const COrientedLayer> coated = {};
// optional, indicates a "sibling" transmission thats next to this one
_typed_pointer_type<const CCorellatedTransmission> 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
Expand All @@ -465,6 +489,9 @@ class CTrueIR : public CNodePool // TODO: turn into an asset!
typed_pointer_type<const CContributorSum> brdfTop = {};
// this node must be non-null until the last layer
typed_pointer_type<const CCorellatedTransmission> firstTransmission = {};

protected:
COPY_DEFAULT_IMPL
};
//
class IFactorLeaf : public IFactor
Expand Down Expand Up @@ -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<INode> copy(CTrueIR* ir) const override final
{
return static_cast<const CSpectralVariableFactor*>(this)->copy(ir->getObjectPool());
}
};
using CSpectralVariableFactor = CSpectralVariable<ISpectralVariableFactor>;

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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<const CSpectralVariableFactor> orientedRealEta = {};

protected:
COPY_DEFAULT_IMPL
};
//! Parameter Nodes
//! Basic factor nodes
Expand Down Expand Up @@ -965,6 +1010,8 @@ class CTrueIR : public CNodePool // TODO: turn into an asset!
}
return true;
}

uint32_t deepCopy(typed_pointer_type<const INode>* out, const std::span<const typed_pointer_type<const INode>> orig, const CTrueIR* srcIR=nullptr);

// TODO: Optimization passes on the IR
// It is the backend's job to handle:
Expand Down
204 changes: 204 additions & 0 deletions src/nbl/asset/material_compiler3/CTrueIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,210 @@ CTrueIR::SBasicNodes::SBasicNodes(CTrueIR* ir)
}
}

uint32_t CTrueIR::deepCopy(typed_pointer_type<const INode>* out,
const std::span<const typed_pointer_type<const INode>> 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<typed_pointer_type<const INode>> 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<const INode>, typed_pointer_type<INode>> 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<const CFactorCombiner*>(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<const CContributorSum*>(node);
if (contributeSum)
{
typed_pointer_type<const INode> 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<const CCorellatedTransmission*>(node);
if (transmission)
{
typed_pointer_type<const INode> 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<const CWeightedContributor*>(node);
if (contributor)
{
typed_pointer_type<const INode> 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<const CCookTorrance*>(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 = [&]<typename T>(const typed_pointer_type<const T>& src, typed_pointer_type<const T>& cop) -> void
{
if (auto child = srcPool.deref(src); child)
{
auto found = substitutions.find(src);
assert(found != substitutions.end());
cop = _static_cast<typed_pointer_type<const T>>(found->second);
}
};
switch (nodeType)
{
case INode::EFinalType::CFactorCombiner:
{
const auto* combiner = dynamic_cast<const CFactorCombiner*>(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<typed_pointer_type<const IFactor>>(found->second);
dynamic_cast<CFactorCombiner*>(copy)->setChildHandle(c, child);
}
}
break;
}
case INode::EFinalType::CContributorSum:
{
const auto* contributeSum = dynamic_cast<const CContributorSum*>(node);
auto* copySum = dynamic_cast<CContributorSum*>(copy);
if (contributeSum && copySum)
{
setCopyChildren(contributeSum->product, copySum->product);
setCopyChildren(contributeSum->product, copySum->product);
}
break;
}
case INode::EFinalType::CCorellatedTransmission:
{
const auto* transmission = dynamic_cast<const CCorellatedTransmission*>(node);
auto* copyTransmission = dynamic_cast<CCorellatedTransmission*>(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<const CWeightedContributor*>(node);
auto* copyContributor = dynamic_cast<CWeightedContributor*>(copy);
if (contributor && copyContributor)
{
setCopyChildren(contributor->contributor, copyContributor->contributor);
setCopyChildren(contributor->factor, copyContributor->factor);
}
break;
}
case INode::EFinalType::CCookTorrance:
{
const auto* ct = dynamic_cast<const CCookTorrance*>(node);
auto* copyCt = dynamic_cast<CCookTorrance*>(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++;
else
*copies = copy;
copies++;
}
return invalidCount;
}

void CTrueIR::SDotPrinter::operator()(std::ostringstream& output)
{
output << "digraph {\n";
Expand Down
Loading