1- // Copyright (C) 2022-2025 - DevSH Graphics Programming Sp. z O.O.
1+ // Copyright (C) 2022-2025 - DevSH Graphics Programming Sp. z O.O.
22// This file is part of the "Nabla Engine".
33// For conditions of distribution and use, see copyright notice in nabla.h
44#ifndef _NBL_ASSET_MATERIAL_COMPILER_V3_C_FRONTEND_IR_H_INCLUDED_
@@ -113,6 +113,18 @@ class CFrontendIR : public CNodePool
113113 {
114114 return abs (scale)<std::numeric_limits<float >::infinity () && (!view || viewChannel<getFormatChannelCount (view->getCreationParameters ().format ));
115115 }
116+ inline bool operator !=(const SParameter& other) const
117+ {
118+ if (scale!=other.scale )
119+ return true ;
120+ if (viewChannel!=other.viewChannel )
121+ return true ;
122+ // don't compare paddings!
123+ if (view!=other.view )
124+ return true ;
125+ return sampler!=other.sampler ;
126+ }
127+ inline bool operator ==(const SParameter& other) const {return !operator !=(other);}
116128
117129 NBL_API void printDot (std::ostringstream& sstr, const core::string& selfID) const ;
118130
@@ -123,6 +135,7 @@ class CFrontendIR : public CNodePool
123135 uint8_t padding[3 ] = {0 ,0 ,0 };
124136 core::smart_refctd_ptr<const ICPUImageView> view = {};
125137 // shadow comparison functions are ignored
138+ // NOTE: could take only things that matter from the sampler and pack the viewChannel and reduce padding
126139 ICPUSampler::SParams sampler = {};
127140 };
128141 // In the forest, this is not a node, we'll deduplicate later
@@ -132,7 +145,7 @@ class CFrontendIR : public CNodePool
132145 private:
133146 friend class CSpectralVariable ;
134147 template <typename StringConstIterator=const core::string*>
135- inline void printDot (const uint8_t _count, std::ostringstream& sstr, const core::string& selfID, StringConstIterator paramNameBegin={}) const
148+ inline void printDot (const uint8_t _count, std::ostringstream& sstr, const core::string& selfID, StringConstIterator paramNameBegin={}, const bool uvRequired= false ) const
136149 {
137150 bool imageUsed = false ;
138151 for (uint8_t i=0 ; i<_count; i++)
@@ -147,10 +160,10 @@ class CFrontendIR : public CNodePool
147160 else
148161 sstr <<" [label=\" Param " << std::to_string (i) <<" \" ]" ;
149162 }
150- if (imageUsed)
163+ if (uvRequired || imageUsed)
151164 {
152165 const auto uvTransformID = selfID+" _uvTransform" ;
153- sstr << " \n\t " << uvTransformID << " [label=\" " ;
166+ sstr << " \n\t " << uvTransformID << " [label=\" uvSlot = " << std::to_string ( uvSlot ()) << " \\ n " ;
154167 printMatrix (sstr,*reinterpret_cast <const decltype (uvTransform)*>(params+_count));
155168 sstr << " \" ]" ;
156169 sstr << " \n\t " << selfID << " -> " << uvTransformID << " [label=\" UV Transform\" ]" ;
@@ -165,20 +178,21 @@ class CFrontendIR : public CNodePool
165178 return false ;
166179 return true ;
167180 }
168- // Ignored if no modulator textures
181+ // Ignored if no modulator textures and isotropic BxDF
169182 uint8_t & uvSlot () {return params[0 ].padding [0 ];}
170183 const uint8_t & uvSlot () const {return params[0 ].padding [0 ];}
171184 // Note: the padding abuse
172185 static_assert (sizeof (SParameter::padding)>0 );
173186
174187 template <typename StringConstIterator=const core::string*>
175- inline void printDot (std::ostringstream& sstr, const core::string& selfID, StringConstIterator paramNameBegin={}) const
188+ inline void printDot (std::ostringstream& sstr, const core::string& selfID, StringConstIterator paramNameBegin={}, const bool uvRequired= false ) const
176189 {
177- printDot<StringConstIterator>(Count,sstr,selfID,std::forward<StringConstIterator>(paramNameBegin));
190+ printDot<StringConstIterator>(Count,sstr,selfID,std::forward<StringConstIterator>(paramNameBegin),uvRequired );
178191 }
179192
180193 SParameter params[Count] = {};
181194 // identity transform by default, ignored if no UVs
195+ // NOTE: a transform could be applied per-param, whats important that the UV slot remains the smae across all of them.
182196 hlsl::float32_t2x3 uvTransform = hlsl::float32_t2x3(
183197 1 ,0 ,0 ,
184198 0 ,1 ,0
@@ -280,6 +294,7 @@ class CFrontendIR : public CNodePool
280294 virtual _TypedHandle<IExprNode> getChildHandle_impl (const uint8_t ix) const = 0;
281295
282296 virtual inline core::string getLabelSuffix () const {return " " ;}
297+ virtual inline std::string_view getChildName_impl (const uint8_t ix) const {return " " ;}
283298 virtual inline void printDot (std::ostringstream& sstr, const core::string& selfID) const {}
284299 };
285300
@@ -443,6 +458,7 @@ class CFrontendIR : public CNodePool
443458 {
444459 protected:
445460 inline TypedHandle<IExprNode> getChildHandle_impl (const uint8_t ix) const override final {return ix ? rhs:lhs;}
461+ inline std::string_view getChildName_impl (const uint8_t ix) const override final {return ix ? " rhs" :" lhs" ;}
446462
447463 public:
448464 inline uint8_t getChildCount () const override final {return 2 ;}
@@ -493,6 +509,8 @@ class CFrontendIR : public CNodePool
493509 protected:
494510 inline TypedHandle<IExprNode> getChildHandle_impl (const uint8_t ix) const override final {return ix ? (ix!=1 ? extinction:transmittance):reflectance;}
495511 NBL_API bool invalid (const SInvalidCheckArgs& args) const override ;
512+
513+ inline std::string_view getChildName_impl (const uint8_t ix) const override {return ix ? (ix>1 ? " extinction" :" reflectance" ):" transmittance" ;}
496514 inline void printDot (std::ostringstream& sstr, const core::string& selfID) const override
497515 {
498516 sstr << " \n\t " << selfID << " -> " << selfID << " _computeTransmittance [label=\" computeTransmittance = " << (computeTransmittance ? " true" :" false" ) << " \" ]" ;
@@ -563,13 +581,15 @@ class CFrontendIR : public CNodePool
563581 inline uint32_t getSize () const override {return calc_size ();}
564582 inline CBeer () = default;
565583
566- // Effective transparency = exp2(log2(perpTransparency )/dot(refract(V,X,eta),X)) = exp2(log2(perpTransparency )*inversesqrt(1.f+(LdotX-1)*rcpEta))
584+ // Effective transparency = exp2(log2(perpTransmittance )/dot(refract(V,X,eta),X)) = exp2(log2(perpTransmittance )*inversesqrt(1.f+(LdotX-1)*rcpEta))
567585 // Absorption and thickness of the interface combined into a single variable, eta and `LdotX` is taken from the leaf BTDF node.
568586 // With refractions from Dielectrics, we get just `1/LdotX`, for Delta Transmission we get `1/VdotN` since its the same
569- TypedHandle<CSpectralVariable> perpTransparency = {};
587+ TypedHandle<CSpectralVariable> perpTransmittance = {};
570588
571589 protected:
572- inline TypedHandle<IExprNode> getChildHandle_impl (const uint8_t ix) const override {return perpTransparency;}
590+ inline TypedHandle<IExprNode> getChildHandle_impl (const uint8_t ix) const override {return perpTransmittance;}
591+
592+ inline std::string_view getChildName_impl (const uint8_t ix) const override {return " Perpendicular\\ nTransmittance" ;}
573593 NBL_API bool invalid (const SInvalidCheckArgs& args) const override ;
574594 };
575595 // The "oriented" in the Etas means from frontface to backface, so there's no need to reciprocate them when creating matching BTDF for BRDF
@@ -593,6 +613,7 @@ class CFrontendIR : public CNodePool
593613 protected:
594614 inline TypedHandle<IExprNode> getChildHandle_impl (const uint8_t ix) const override {return ix ? orientedImagEta:orientedRealEta;}
595615 NBL_API bool invalid (const SInvalidCheckArgs& args) const override ;
616+ inline std::string_view getChildName_impl (const uint8_t ix) const override {return ix ? " Real" :" Imaginary" ;}
596617 NBL_API void printDot (std::ostringstream& sstr, const core::string& selfID) const override ;
597618 };
598619 // @kept_secret TODO: Thin Film Interference Fresnel
@@ -618,6 +639,19 @@ class CFrontendIR : public CNodePool
618639 param.scale = 0 .f ;
619640 }
620641
642+ // conservative check, checks if we can optimize certain things this way
643+ inline bool definitelyIsotropic () const
644+ {
645+ // a derivative map from a texture allows for anisotropic NDFs at higher mip levels when pre-filtered properly
646+ for (auto i=0 ; i<2 ; i++)
647+ if (getDerivMap ()[i].scale !=0 .f && getDerivMap ()[i].view )
648+ return false ;
649+ // if roughness inputs are not equal (same scale, same texture) then NDF can be anisotropic in places
650+ if (getRougness ()[0 ]!=getRougness ()[1 ])
651+ return false ;
652+ // if a reference stretch is used, stretched triangles can turn the distribution isotropic
653+ return stretchInvariant ();
654+ }
621655 // whether the derivative map and roughness is constant regardless of UV-space texture stretching
622656 inline bool stretchInvariant () const {return !(abs (hlsl::determinant (reference))>std::numeric_limits<float >::min ());}
623657
@@ -704,6 +738,7 @@ class CFrontendIR : public CNodePool
704738 NBL_API bool invalid (const SInvalidCheckArgs& args) const override ;
705739
706740 inline core::string getLabelSuffix () const override {return ndf!=NDF::GGX ? " \\ nNDF = Beckmann" :" \\ nNDF = GGX" ;}
741+ inline std::string_view getChildName_impl (const uint8_t ix) const override {return " Oriented η" ;}
707742 NBL_API void printDot (std::ostringstream& sstr, const core::string& selfID) const override ;
708743 };
709744
0 commit comments