Skip to content

Commit

Permalink
Merge pull request #1394 from johnhaddon/internedStringSubstitution
Browse files Browse the repository at this point in the history
ShaderNetwork : Support InternedString attributes in substitutions
  • Loading branch information
ericmehl authored Nov 9, 2023
2 parents 89dcde0 + b4b8e2c commit a2935e3
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 34 deletions.
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()

0 comments on commit a2935e3

Please sign in to comment.