Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ShaderNetwork : Support InternedString attributes in substitutions #1394

Merged
merged 5 commits into from
Nov 9, 2023
Merged
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
12 changes: 11 additions & 1 deletion Changes
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
10.5.x.x (relative to 10.5.3.0)
10.5.x.x (relative to 10.5.4.0)
========



10.5.4.0 (relative to 10.5.3.0)
========

Improvements
------------

- ShaderNetwork : Added support for InternedStringData attributes in string parameter substitutions.
- MurmurHash : Added specialisation for `std::hash`, among other things allowing the use of MurmurHash as a key in `unordered_map`.
- CompoundObject : Defaulted template argument to `Object` in `member()` methods.

10.5.3.0 (relative to 10.5.2.1)
========

Expand Down
4 changes: 2 additions & 2 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ SConsignFile()

ieCoreMilestoneVersion = 10 # for announcing major milestones - may contain all of the below
ieCoreMajorVersion = 5 # backwards-incompatible changes
ieCoreMinorVersion = 3 # new backwards-compatible features
ieCoreMinorVersion = 4 # new backwards-compatible features
ieCorePatchVersion = 0 # bug fixes
ieCoreVersionSuffix = "" # used for alpha/beta releases. Example: "a1", "b2", etc.

Expand Down Expand Up @@ -1226,7 +1226,7 @@ else:
"/Fd${TARGET}.pdb",
],
)

# Reorder build commands so that `/external:I` includes come after `/I` includes.
# Otherwise we'll pick up the Gaffer includes from the build directory, and not
# the ones in the source tree.
Expand Down
6 changes: 3 additions & 3 deletions include/IECore/CompoundObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,17 @@ class IECORE_API CompoundObject : public Object
/// or doesn't match the type specified as the template argument, behavior
/// is defined by the throwExceptions parameter. When this parameter is true a descriptive
/// Exception is thrown, and when false 0 is returned.
template<typename T>
template<typename T = Object>
T *member( const InternedString &name, bool throwExceptions = false );
template<typename T>
template<typename T = Object>
const T *member( const InternedString &name, bool throwExceptions = false ) const;

/// A Convenience function to find an object in members().
/// If the named object doesn't exist, if createIfMissing is true, an object will be added
/// with the type's object factory create method. If false, or the named entry does not match the
/// type specified as the template argument, behavior is defined by the throwExceptions parameter.
/// When this parameter is true a descriptive Exception is thrown, and when false 0 is returned.
template<typename T>
template<typename T = Object>
T *member( const InternedString &name, bool throwExceptions, bool createIfMissing );

/// Returns an instance of CompoundObject which can be shared by everyone - for instance a procedural
Expand Down
2 changes: 1 addition & 1 deletion src/IECoreMaya/ParameterisedHolderModificationCmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ void ParameterisedHolderModificationCmd::storeParametersWithNewValues( const IEC
{
childParameterPath = it->first;
}
storeParametersWithNewValues( it->second.get(), newCompound->member<Object>( it->first ), childParameterPath );
storeParametersWithNewValues( it->second.get(), newCompound->member( it->first ), childParameterPath );
}

const CompoundObject::ObjectMap &newChildren = static_cast<const CompoundObject *>( newValue )->members();
Expand Down
4 changes: 2 additions & 2 deletions src/IECoreNuke/ParameterisedHolder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ int ParameterisedHolder<BaseType>::knob_changed( DD::Image::Knob *knob )
ParameterisedInterface *parameterisedInterface = dynamic_cast<ParameterisedInterface *>( g_getParameterisedResult.get() );
// apply current state
ConstCompoundObjectPtr classSpecifier = runTimeCast<const CompoundObject>( m_classSpecifierKnob->getValue() );
ConstObjectPtr handlerState = classSpecifier->member<Object>( "handlerState" );
ConstObjectPtr handlerState = classSpecifier->member( "handlerState" );
if( handlerState )
{
m_parameterHandler->setState( parameterisedInterface->parameters(), handlerState.get() );
Expand Down Expand Up @@ -525,7 +525,7 @@ void ParameterisedHolder<BaseType>::updateParameterised( bool reload )
// apply the previously stored handler state

ConstCompoundObjectPtr classSpecifier = runTimeCast<const CompoundObject>( m_classSpecifierKnob->getValue() );
ConstObjectPtr handlerState = classSpecifier->member<Object>( "handlerState" );
ConstObjectPtr handlerState = classSpecifier->member( "handlerState" );
if( handlerState )
{
m_parameterHandler->setState( parameterisedInterface->parameters(), handlerState.get() );
Expand Down
24 changes: 16 additions & 8 deletions src/IECoreScene/ShaderNetwork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,14 @@ struct ReplaceFunctor
std::string operator()( const boost::smatch & match )
{
// Search for attribute matching token
const StringData *sourceAttribute = m_attributes->member<StringData>( match[1].str() );
if( sourceAttribute )
const Object *sourceAttribute = m_attributes->member( match[1].str() );
if( auto stringData = runTimeCast<const StringData>( sourceAttribute ) )
{
return sourceAttribute->readable();
return stringData->readable();
}
else if( auto internedStringData = runTimeCast<const InternedStringData>( sourceAttribute ) )
{
return internedStringData->readable().string();
}
else
{
Expand All @@ -86,7 +90,7 @@ boost::regex attributeRegex()
// Extract ATTR_NAME from the pattern <attr:ATTR_NAME>
// Only match if the angle brackets haven't been escaped with a backslash
static boost::regex r( "(?<!\\\\)<attr:([^>]*[^\\\\>])>" );
return r;
return r;
}

bool stringFindSubstitutions( const std::string &target, std::unordered_set< InternedString > &requestedAttributes )
Expand Down Expand Up @@ -428,10 +432,14 @@ class ShaderNetwork::Implementation
update();
for( const auto &a : m_neededSubstitutions )
{
const StringData *sourceAttribute = attributes->member<StringData>( a );
if( sourceAttribute )
const Object *sourceAttribute = attributes->member( a );
if( auto stringData = runTimeCast<const StringData>( sourceAttribute ) )
{
h.append( stringData->readable() );
}
else if( auto internedStringData = runTimeCast<const InternedStringData>( sourceAttribute ) )
{
h.append( sourceAttribute->readable() );
h.append( internedStringData->readable().string() );
}
else
{
Expand Down Expand Up @@ -737,7 +745,7 @@ class ShaderNetwork::Implementation
}
}

m_parmsNeedingSubstitution[ node.handle ] = parmsNeedingSub;
m_parmsNeedingSubstitution[ node.handle ] = parmsNeedingSub;
}

m_hash.append( m_output.shader );
Expand Down
54 changes: 37 additions & 17 deletions test/IECoreScene/ShaderNetworkTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,23 +513,25 @@ def testUniqueHandles( self ) :
{ "test" } | { "test{0}".format( x ) for x in range( 1, 20 ) }
)

def testSubstitutions( self ):
def runSubstitutionTest( shader, attributes ):
n = IECoreScene.ShaderNetwork( shaders = { "s" : s } )
a = IECore.CompoundObject( attributes )
h = IECore.MurmurHash()
n.hashSubstitutions( a, h )
nSubst = n.copy()
nSubst.applySubstitutions( a )
return ( h, nSubst.getShader("s") )
def __hashAndSubstitution( self, shader, attributes ) :

n = IECoreScene.ShaderNetwork( shaders = { "s" : shader } )
a = IECore.CompoundObject( attributes )
h = IECore.MurmurHash()
n.hashSubstitutions( a, h )
nSubst = n.copy()
nSubst.applySubstitutions( a )
return ( h, nSubst.getShader( "s" ) )

def testSubstitutions( self ) :

s = IECoreScene.Shader( "test", "surface",IECore.CompoundData( {
"a" : IECore.StringData( "foo" ),
"b" : IECore.FloatData( 42.42 ),
"c" : IECore.StringVectorData( [ "foo", "bar" ] ),
} ) )

( h, sSubst ) = runSubstitutionTest( s, { "unused" : IECore.StringData( "blah" ) } )
( h, sSubst ) = self.__hashAndSubstitution( s, { "unused" : IECore.StringData( "blah" ) } )
self.assertEqual( h, IECore.MurmurHash() )
self.assertEqual( s, sSubst )

Expand All @@ -538,7 +540,7 @@ def runSubstitutionTest( shader, attributes ):
"b" : IECore.FloatData( 42.42 ),
"c" : IECore.StringVectorData( [ "<attr:bob>", "pre<attr:carol>", "<attr:fred>post", "<attr:bob><attr:carol> <attr:fred>" ] ),
} ) )
( h, sSubst ) = runSubstitutionTest( s, { "unused" : IECore.StringData( "blah" ) } )
( h, sSubst ) = self.__hashAndSubstitution( s, { "unused" : IECore.StringData( "blah" ) } )
# Now that we've got substitutions, the hash should be non-default
self.assertNotEqual( h, IECore.MurmurHash() )

Expand All @@ -550,12 +552,12 @@ def runSubstitutionTest( shader, attributes ):
self.assertEqual( sSubst.parameters["c"][2], "post" )
self.assertEqual( sSubst.parameters["c"][3], " " )

( h2, sSubst2 ) = runSubstitutionTest( s, { "unused" : IECore.StringData( "blah2" ) } )
( h2, sSubst2 ) = self.__hashAndSubstitution( s, { "unused" : IECore.StringData( "blah2" ) } )
# The attribute being changed has no impact
self.assertEqual( h, h2 )
self.assertEqual( sSubst, sSubst2 )

( h3, sSubst3 ) = runSubstitutionTest( s, { "fred" : IECore.StringData( "CAT" ) } )
( h3, sSubst3 ) = self.__hashAndSubstitution( s, { "fred" : IECore.StringData( "CAT" ) } )
self.assertNotEqual( h, h3 )
self.assertNotEqual( s, sSubst3 )
self.assertEqual( sSubst3.parameters["a"].value, "preCATpost" )
Expand All @@ -564,7 +566,7 @@ def runSubstitutionTest( shader, attributes ):
self.assertEqual( sSubst3.parameters["c"][2], "CATpost" )
self.assertEqual( sSubst3.parameters["c"][3], " CAT" )

( h4, sSubst4 ) = runSubstitutionTest( s, { "fred" : IECore.StringData( "FISH" ) } )
( h4, sSubst4 ) = self.__hashAndSubstitution( s, { "fred" : IECore.StringData( "FISH" ) } )
self.assertNotEqual( h3, h4 )
self.assertEqual( sSubst4.parameters["c"][2], "FISHpost" )

Expand All @@ -573,7 +575,7 @@ def runSubstitutionTest( shader, attributes ):
"bob" : IECore.StringData( "CAT" ),
"carol" : IECore.StringData( "BIRD" )
}
( h5, sSubst5 ) = runSubstitutionTest( s, allAttributes )
( h5, sSubst5 ) = self.__hashAndSubstitution( s, allAttributes )
self.assertNotEqual( h4, h5 )
self.assertEqual( sSubst5.parameters["a"].value, "preFISHpost" )
self.assertEqual( sSubst5.parameters["c"][0], "CAT" )
Expand All @@ -587,14 +589,32 @@ def runSubstitutionTest( shader, attributes ):
"b" : IECore.FloatData( 42.42 ),
"c" : IECore.StringVectorData( [ r"\<attr:bob\>", r"\<attr:carol>", r"<attr:fred\>" ] ),
} ) )
( h6, sSubst6 ) = runSubstitutionTest( s, {} )
( h7, sSubst7 ) = runSubstitutionTest( s, allAttributes )
( h6, sSubst6 ) = self.__hashAndSubstitution( s, {} )
( h7, sSubst7 ) = self.__hashAndSubstitution( s, allAttributes )
self.assertEqual( h6, h7 )
self.assertEqual( sSubst6, sSubst7 )
self.assertEqual( sSubst6.parameters["a"].value, "pre<attr:fred>post" )
self.assertEqual( sSubst6.parameters["c"][0], "<attr:bob>" )
self.assertEqual( sSubst6.parameters["c"][1], "<attr:carol>" )
self.assertEqual( sSubst6.parameters["c"][2], "<attr:fred>" )

def testInternedStringSubstitutions( self ) :

shader = IECoreScene.Shader(
"test", "surface",
IECore.CompoundData( {
"a" : IECore.StringData( "<attr:internedString>" ),
} )
)

hash1, substitutedShader = self.__hashAndSubstitution( shader, { "internedString" : IECore.InternedStringData( "a" ) } )
self.assertNotEqual( hash1, IECore.MurmurHash() )
self.assertEqual( substitutedShader.parameters["a"], IECore.StringData( "a" ) )

hash2, substitutedShader = self.__hashAndSubstitution( shader, { "internedString" : IECore.InternedStringData( "b" ) } )
self.assertNotEqual( hash2, IECore.MurmurHash() )
self.assertNotEqual( hash2, hash1 )
self.assertEqual( substitutedShader.parameters["a"], IECore.StringData( "b" ) )

if __name__ == "__main__":
unittest.main()