Skip to content

Commit 638665a

Browse files
author
devsh
committed
detect cycles of BxDF layers, optimize the DOT Graphviz printing to not print in cycles
1 parent 47300fd commit 638665a

File tree

4 files changed

+65
-24
lines changed

4 files changed

+65
-24
lines changed

examples_tests

include/nbl/asset/material_compiler3/CFrontendIR.h

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -778,21 +778,6 @@ class CFrontendIR : public CNodePool
778778
inline bool CFrontendIR::valid(const TypedHandle<const CLayer> rootHandle, system::logger_opt_ptr logger) const
779779
{
780780
constexpr auto ELL_ERROR = system::ILogger::E_LOG_LEVEL::ELL_ERROR;
781-
782-
core::stack<const CLayer*> layerStack;
783-
auto pushLayer = [&](const TypedHandle<const CLayer> layerHandle)->bool
784-
{
785-
const auto* layer = deref(layerHandle);
786-
if (!layer)
787-
{
788-
logger.log("Layer node %u of type %s not a `CLayer` node!",ELL_ERROR,layerHandle.untyped.value,getTypeName(layerHandle).data());
789-
return false;
790-
}
791-
layerStack.push(layer);
792-
return true;
793-
};
794-
if (!pushLayer(rootHandle))
795-
return false;
796781

797782
enum class SubtreeContributorState : uint8_t
798783
{
@@ -807,6 +792,11 @@ inline bool CFrontendIR::valid(const TypedHandle<const CLayer> rootHandle, syste
807792
SubtreeContributorState contribState = SubtreeContributorState::Required;
808793
};
809794
core::stack<StackEntry> exprStack;
795+
// unused yet
796+
core::unordered_set<TypedHandle<const INode>,HandleHash> visitedNodes;
797+
// should probably size it better, if I knew total node count allocated or live
798+
visitedNodes.reserve(m_rootNodes.size()<<3);
799+
//
810800
auto validateExpression = [&](const TypedHandle<const IExprNode> exprRoot, const bool isBTDF) -> bool
811801
{
812802
if (!exprRoot)
@@ -865,6 +855,8 @@ inline bool CFrontendIR::valid(const TypedHandle<const CLayer> rootHandle, syste
865855
logger.log("Expression too complex, more than %d contributors encountered",ELL_ERROR,MaxContributors);
866856
return false;
867857
}
858+
// cannot optimize with `unordered_set visitedNodes` because we need to check contributor slots, if we really wanted to we could do it with an
859+
// `unordered_map` telling us the contributor slot range remapping (and presence of contributor) but right now it would be premature optimization.
868860
exprStack.push(newEntry);
869861
}
870862
else if (childHandle)
@@ -893,15 +885,30 @@ inline bool CFrontendIR::valid(const TypedHandle<const CLayer> rootHandle, syste
893885
}
894886
return true;
895887
};
896-
while (!layerStack.empty())
888+
889+
core::vector<const CLayer*> layerStack;
890+
auto pushLayer = [&](const TypedHandle<const CLayer> layerHandle)->bool
897891
{
898-
const auto* layer = layerStack.top();
899-
layerStack.pop();
900-
if (layer->coated && !pushLayer(layer->coated))
892+
const auto* layer = deref(layerHandle);
893+
if (!layer)
901894
{
902-
logger.log("\tcoatee %d was specificed but is of wrong type",ELL_ERROR,layer->coated);
895+
logger.log("Layer node %u of type %s not a `CLayer` node!",ELL_ERROR,layerHandle.untyped.value,getTypeName(layerHandle).data());
903896
return false;
904897
}
898+
auto found = std::find(layerStack.begin(),layerStack.end(),layer);
899+
if (found!=layerStack.end())
900+
{
901+
logger.log("Layer node %u is involved in a Cycle!",ELL_ERROR,layerHandle.untyped.value);
902+
return false;
903+
}
904+
layerStack.push_back(layer);
905+
return true;
906+
};
907+
if (!pushLayer(rootHandle))
908+
return false;
909+
while (true)
910+
{
911+
const auto* layer = layerStack.back();
905912
if (!layer->brdfTop && !layer->btdf && !layer->brdfBottom)
906913
{
907914
logger.log("At least one BRDF or BTDF in the Layer is required.",ELL_ERROR);
@@ -913,6 +920,13 @@ inline bool CFrontendIR::valid(const TypedHandle<const CLayer> rootHandle, syste
913920
return false;
914921
if (!validateExpression(layer->brdfBottom,false))
915922
return false;
923+
if (!layer->coated)
924+
break;
925+
if (!pushLayer(layer->coated))
926+
{
927+
logger.log("\tcoatee %d was specificed but is invalid!",ELL_ERROR,layer->coated);
928+
return false;
929+
}
916930
}
917931
return true;
918932
}

include/nbl/asset/material_compiler3/CNodePool.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ class CNodePool : public core::IReferenceCounted
2525
using value_t = uint32_t;
2626
constexpr static inline value_t Invalid = ~value_t(0);
2727

28-
inline operator bool() const {return value!=Invalid;}
28+
explicit inline operator bool() const {return value!=Invalid;}
29+
inline bool operator==(const Handle& other) const {return value==other.value;}
2930

3031
// also serves as a byte offset into the pool
3132
value_t value = Invalid;
@@ -89,7 +90,8 @@ class CNodePool : public core::IReferenceCounted
8990
{
9091
using node_type = T;
9192

92-
inline operator bool() const {return untyped;}
93+
explicit inline operator bool() const {return bool(untyped);}
94+
inline bool operator==(const TypedHandle& other) const {return untyped==other.untyped;}
9395

9496
inline operator const TypedHandle<const T>&() const
9597
{
@@ -117,6 +119,13 @@ class CNodePool : public core::IReferenceCounted
117119
}
118120

119121
protected:
122+
struct HandleHash
123+
{
124+
inline size_t operator()(const TypedHandle<const INode> handle) const
125+
{
126+
return std::hash<Handle::value_t>()(handle.untyped.value);
127+
}
128+
};
120129
// save myself some typing
121130
using refctd_pmr_t = core::smart_refctd_ptr<core::refctd_memory_resource>;
122131

src/nbl/asset/material_compiler3/CFrontendIR.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,10 @@ auto CFrontendIR::createNamedFresnel(const std::string_view name) -> TypedHandle
219219
void CFrontendIR::printDotGraph(std::ostringstream& str) const
220220
{
221221
str << "digraph {\n";
222-
222+
223+
core::unordered_set<TypedHandle<const INode>,HandleHash> visitedNodes;
224+
// should probably size it better, if I knew total node count allocated or live
225+
visitedNodes.reserve(m_rootNodes.size()<<3);
223226
// TODO: track layering depth and indent accordingly?
224227
// assign in reverse because we want materials to print in order
225228
core::vector<TypedHandle<const CLayer>> layerStack(m_rootNodes.rbegin(),m_rootNodes.rend());
@@ -228,6 +231,11 @@ void CFrontendIR::printDotGraph(std::ostringstream& str) const
228231
{
229232
const auto layerHandle = layerStack.back();
230233
layerStack.pop_back();
234+
// don't print layer nodes multiple times
235+
const auto visited = visitedNodes.find(layerHandle);
236+
if (visited!=visitedNodes.end())
237+
continue;
238+
visitedNodes.insert(layerHandle);
231239
const auto* layerNode = deref(layerHandle);
232240
//
233241
const auto layerID = getNodeID(layerHandle);
@@ -242,8 +250,14 @@ void CFrontendIR::printDotGraph(std::ostringstream& str) const
242250
{
243251
if (!root)
244252
return;
253+
// print the link from the layer to the expression
245254
str << "\n\t" << layerID << " -> " << getNodeID(root) << "[label=\"" << edgeLabel << "\"]";
255+
// but not the expression again
256+
const auto visited = visitedNodes.find(root);
257+
if (visited!=visitedNodes.end())
258+
return;
246259
exprStack.push(root);
260+
visitedNodes.insert(root);
247261
};
248262
pushExprRoot(layerNode->brdfTop,"Top BRDF");
249263
pushExprRoot(layerNode->btdf,"BTDF");
@@ -266,7 +280,11 @@ void CFrontendIR::printDotGraph(std::ostringstream& str) const
266280
if (const auto child=deref(childHandle); child)
267281
{
268282
str << getNodeID(childHandle) << " ";
283+
const auto visited = visitedNodes.find(childHandle);
284+
if (visited!=visitedNodes.end())
285+
continue;
269286
exprStack.push(childHandle);
287+
visitedNodes.insert(childHandle);
270288
}
271289
}
272290
str << "}\n";

0 commit comments

Comments
 (0)