Skip to content

Commit

Permalink
Arnold ShaderNetworkAlgo : Add attributeName argument to Substituti…
Browse files Browse the repository at this point in the history
…onFunction

This provides the name of the attribute from which the target ShaderNetwork was retrieved. This is an ABI break, which we would never normally contemplate in a minor release. But we only introduced the API in the previous version, and we have determined that without this change it is not fit for purpose. Full backwards compatibility can't be provided without significant overhead and additional fragility, and nor can we wait for a major version to make this change. The purpose of the ABI guarantee is to prevent breakage, but in this case we believe breakage to be vanishingly unlikely, due to the recency of introduction and esoteric nature of the API. So, we are permitting ourselves this one exception in the belief that it does no harm.
  • Loading branch information
johnhaddon committed Jan 16, 2025
1 parent 12aae90 commit d8f3604
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 58 deletions.
4 changes: 4 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ API

- TweakPlug : Added `applyElementwiseTweak()` method, for tweaking elements of a `*VectorData`.
- IECoreArnold, IECoreDelight : Added support for config files installed on `GAFFER_STARTUP_PATHS`.
- IECoreArnold::ShaderNetworkAlgo : Added `attributeName` arguments to `SubstitutionFunction` and `SubstitutionHashFunction`. This is an ABI break, which would not normally be allowed without a change of major version. We are making a rare exception in this case, with the following justifications :
- The API is esoteric and was introduced extremely recently, so we believe nobody to be depending on it yet.
- Without the ABI change, the API isn't usable for its original intended purpose anyway.
- Backward compatibility is not trivial to maintain in this case.

1.5.2.0 (relative to 1.5.1.0)
=======
Expand Down
8 changes: 4 additions & 4 deletions include/IECoreArnold/ShaderNetworkAlgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,13 @@ IECOREARNOLD_API void convertUSDShaders( IECoreScene::ShaderNetwork *shaderNetwo

/// A function that performs substitutions on a shader network, given the full
/// inherited `attributes` for an object. Must be threadsafe.
using SubstitutionFunction = void (*)( IECoreScene::ShaderNetwork *shaderNetwork, const IECore::CompoundObject *attributes );
using SubstitutionFunction = void (*)( IECoreScene::ShaderNetwork *shaderNetwork, IECore::InternedString attributeName, const IECore::CompoundObject *attributes );
/// A function that appends to `hash` to uniquely identify the work that will be
/// performed by a SubstitutionFunction. Particular attention must be paid to
/// the performance of any such function, as it will be called frequently. If a
/// substitution will be a no-op, then nothing should be appended to the hash.
/// Must be threadsafe.
using SubstitutionHashFunction = void (*)( const IECoreScene::ShaderNetwork *shaderNetwork, const IECore::CompoundObject *attributes, IECore::MurmurHash &hash );
using SubstitutionHashFunction = void (*)( const IECoreScene::ShaderNetwork *shaderNetwork, IECore::InternedString attributeName, const IECore::CompoundObject *attributes, IECore::MurmurHash &hash );

/// Registers a just-in-time substitution to be performed on shader
/// networks before the shader is translated to Arnold.
Expand All @@ -110,9 +110,9 @@ IECOREARNOLD_API void registerSubstitution( const std::string &name, Substitutio
IECOREARNOLD_API void deregisterSubstitution( const std::string &name );

/// Hashes all the currently registered substitutions for `shaderNetwork`.
IECOREARNOLD_API void hashSubstitutions( const IECoreScene::ShaderNetwork *shaderNetwork, const IECore::CompoundObject *attributes, IECore::MurmurHash &hash );
IECOREARNOLD_API void hashSubstitutions( const IECoreScene::ShaderNetwork *shaderNetwork, IECore::InternedString attributeName, const IECore::CompoundObject *attributes, IECore::MurmurHash &hash );
/// Applies all the currently registered substitutions to `shaderNetwork`.
IECOREARNOLD_API void applySubstitutions( IECoreScene::ShaderNetwork *shaderNetwork, const IECore::CompoundObject *attributes );
IECOREARNOLD_API void applySubstitutions( IECoreScene::ShaderNetwork *shaderNetwork, IECore::InternedString attributeName, const IECore::CompoundObject *attributes );

} // namespace ShaderNetworkAlgo

Expand Down
98 changes: 50 additions & 48 deletions src/IECoreArnold/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -828,13 +828,13 @@ class ShaderCache : public IECore::RefCounted
}

// Can be called concurrently with other get() calls.
ArnoldShaderPtr get( const IECoreScene::ShaderNetwork *shader, const IECore::CompoundObject *attributes )
ArnoldShaderPtr get( const IECoreScene::ShaderNetwork *shader, IECore::InternedString attributeName, const IECore::CompoundObject *attributes )
{
IECore::MurmurHash h = shader->Object::hash();
IECore::MurmurHash hSubst;
if( attributes )
{
ShaderNetworkAlgo::hashSubstitutions( shader, attributes, hSubst );
ShaderNetworkAlgo::hashSubstitutions( shader, attributeName, attributes, hSubst );
h.append( hSubst );
}

Expand All @@ -851,7 +851,7 @@ class ShaderCache : public IECore::RefCounted
if( hSubst != IECore::MurmurHash() )
{
IECoreScene::ShaderNetworkPtr substitutedShader = shader->copy();
ShaderNetworkAlgo::applySubstitutions( substitutedShader.get(), attributes );
ShaderNetworkAlgo::applySubstitutions( substitutedShader.get(), attributeName, attributes );
writeAccessor->second = new ArnoldShader( substitutedShader.get(), m_nodeDeleter, m_universe, namePrefix, m_parentNode );
}
else
Expand All @@ -862,6 +862,25 @@ class ShaderCache : public IECore::RefCounted
return writeAccessor->second;
}

// Convenience function that search for a shader network with one of the
// specified attributes names, converting the first one found.
ArnoldShaderPtr get( const vector<IECore::InternedString> &attributeNames, const IECore::CompoundObject *attributes )
{
for( auto name : attributeNames )
{
auto it = attributes->members().find( name );
if( it == attributes->members().end() )
{
continue;
}
if( auto shader = reportedCast<const IECoreScene::ShaderNetwork>( it->second.get(), "attribute", name ) )
{
return get( shader, name, attributes );
}
}
return nullptr;
}

// _Must_ be called before `render()` launches Arnold, and must not be
// called concurrently with anything. This removes shaders that are no
// longer in use during interactive renders, and ensures all
Expand Down Expand Up @@ -921,17 +940,12 @@ namespace
// Forward declaration
bool isConvertedProcedural( const AtNode *node );

IECore::InternedString g_surfaceShaderAttributeName( "surface" );
IECore::InternedString g_volumeShaderAttributeName( "volume" );
IECore::InternedString g_lightShaderAttributeName( "light" );
IECore::InternedString g_doubleSidedAttributeName( "doubleSided" );
IECore::InternedString g_setsAttributeName( "sets" );
IECore::InternedString g_automaticInstancingAttributeName( "gaffer:automaticInstancing" );
IECore::InternedString g_muteLightAttributeName( "light:mute" );

IECore::InternedString g_oslSurfaceShaderAttributeName( "osl:surface" );
IECore::InternedString g_oslShaderAttributeName( "osl:shader" );

IECore::InternedString g_cameraVisibilityAttributeName( "ai:visibility:camera" );
IECore::InternedString g_shadowVisibilityAttributeName( "ai:visibility:shadow" );
IECore::InternedString g_shadowGroup( "ai:visibility:shadow_group" );
Expand All @@ -951,11 +965,7 @@ IECore::InternedString g_specularTransmitVisibilityAutoBumpAttributeName( "ai:au
IECore::InternedString g_volumeVisibilityAutoBumpAttributeName( "ai:autobump_visibility:volume" );
IECore::InternedString g_subsurfaceVisibilityAutoBumpAttributeName( "ai:autobump_visibility:subsurface" );

IECore::InternedString g_arnoldSurfaceShaderAttributeName( "ai:surface" );
IECore::InternedString g_arnoldVolumeShaderAttributeName( "ai:volume" );
IECore::InternedString g_arnoldLightShaderAttributeName( "ai:light" );
IECore::InternedString g_arnoldFilterMapAttributeName( "ai:filtermap" );
IECore::InternedString g_arnoldUVRemapAttributeName( "ai:uv_remap" );
IECore::InternedString g_arnoldLightFilterShaderAttributeName( "ai:lightFilter:filter" );

IECore::InternedString g_arnoldReceiveShadowsAttributeName( "ai:receive_shadows" );
Expand Down Expand Up @@ -985,7 +995,6 @@ IECore::InternedString g_polyMeshSubdivFrustumIgnoreAttributeName( "ai:polymesh:
IECore::InternedString g_polyMeshSubdividePolygonsAttributeName( "ai:polymesh:subdivide_polygons" );
IECore::InternedString g_polyMeshSubdivUVSmoothingAttributeName( "ai:polymesh:subdiv_uv_smoothing" );

IECore::InternedString g_dispMapAttributeName( "ai:disp_map" );
IECore::InternedString g_dispHeightAttributeName( "ai:disp_height" );
IECore::InternedString g_dispPaddingAttributeName( "ai:disp_padding" );
IECore::InternedString g_dispZeroValueAttributeName( "ai:disp_zero_value" );
Expand All @@ -1001,6 +1010,23 @@ IECore::InternedString g_lightFilterPrefix( "ai:lightFilter:" );

IECore::InternedString g_filteredLights( "filteredLights" );

const std::vector<IECore::InternedString> g_surfaceShaderAttributeNames = {
"ai:surface",
"osl:surface",
/// \todo Remove support for interpreting "osl:shader" as a surface shader assignment.
"osl:shader",
"surface"
};

const std::vector<IECore::InternedString> g_volumeShaderAttributeNames = {
"ai:volume",
"volume"
};

const std::vector<IECore::InternedString> g_filterMapAttributeNames = { "ai:filtermap" };
const std::vector<IECore::InternedString> g_uvRemapAttributeNames = { "ai:uv_remap" };
const std::vector<IECore::InternedString> g_dispMapAttributeNames = { "ai:disp_map" };

const char *customAttributeName( const std::string &attributeName, bool *hasPrecedence = nullptr )
{
if( boost::starts_with( attributeName, "user:" ) )
Expand Down Expand Up @@ -1051,31 +1077,10 @@ class ArnoldAttributes : public IECoreScenePreview::Renderer::AttributesInterfac
updateShadingFlag( g_arnoldOpaqueAttributeName, Opaque, attributes );
updateShadingFlag( g_arnoldMatteAttributeName, Matte, attributes );

const IECoreScene::ShaderNetwork *surfaceShaderAttribute = attribute<IECoreScene::ShaderNetwork>( g_arnoldSurfaceShaderAttributeName, attributes );
surfaceShaderAttribute = surfaceShaderAttribute ? surfaceShaderAttribute : attribute<IECoreScene::ShaderNetwork>( g_oslSurfaceShaderAttributeName, attributes );
/// \todo Remove support for interpreting "osl:shader" as a surface shader assignment.
surfaceShaderAttribute = surfaceShaderAttribute ? surfaceShaderAttribute : attribute<IECoreScene::ShaderNetwork>( g_oslShaderAttributeName, attributes );
surfaceShaderAttribute = surfaceShaderAttribute ? surfaceShaderAttribute : attribute<IECoreScene::ShaderNetwork>( g_surfaceShaderAttributeName, attributes );
if( surfaceShaderAttribute )
{
m_surfaceShader = shaderCache->get( surfaceShaderAttribute, attributes );
}

const IECoreScene::ShaderNetwork *volumeShaderAttribute = attribute<IECoreScene::ShaderNetwork>( g_arnoldVolumeShaderAttributeName, attributes );
volumeShaderAttribute = volumeShaderAttribute ? volumeShaderAttribute : attribute<IECoreScene::ShaderNetwork>( g_volumeShaderAttributeName, attributes );
if( volumeShaderAttribute )
{
m_volumeShader = shaderCache->get( volumeShaderAttribute, attributes );
}

if( auto filterMapAttribute = attribute<IECoreScene::ShaderNetwork>( g_arnoldFilterMapAttributeName, attributes ) )
{
m_filterMap = shaderCache->get( filterMapAttribute, attributes );
}
if( auto uvRemapAttribute = attribute<IECoreScene::ShaderNetwork>( g_arnoldUVRemapAttributeName, attributes ) )
{
m_uvRemap = shaderCache->get( uvRemapAttribute, attributes );
}
m_surfaceShader = shaderCache->get( g_surfaceShaderAttributeNames, attributes );
m_volumeShader = shaderCache->get( g_volumeShaderAttributeNames, attributes );
m_filterMap = shaderCache->get( g_filterMapAttributeNames, attributes );
m_uvRemap = shaderCache->get( g_uvRemapAttributeNames, attributes );

m_lightShader = attribute<IECoreScene::ShaderNetwork>( g_arnoldLightShaderAttributeName, attributes );
m_lightShader = m_lightShader ? m_lightShader : attribute<IECoreScene::ShaderNetwork>( g_lightShaderAttributeName, attributes );
Expand Down Expand Up @@ -1116,7 +1121,7 @@ class ArnoldAttributes : public IECoreScenePreview::Renderer::AttributesInterfac
}
else if( boost::starts_with( it->first.string(), g_lightFilterPrefix.string() ) )
{
ArnoldShaderPtr filter = shaderCache->get( IECore::runTimeCast<const IECoreScene::ShaderNetwork>( it->second.get() ), attributes );
ArnoldShaderPtr filter = shaderCache->get( IECore::runTimeCast<const IECoreScene::ShaderNetwork>( it->second.get() ), it->first, attributes );
m_lightFilterShaders.push_back( filter );
}
}
Expand Down Expand Up @@ -1590,10 +1595,7 @@ class ArnoldAttributes : public IECoreScenePreview::Renderer::AttributesInterfac

Displacement( const IECore::CompoundObject *attributes, ShaderCache *shaderCache )
{
if( const IECoreScene::ShaderNetwork *mapAttribute = attribute<IECoreScene::ShaderNetwork>( g_dispMapAttributeName, attributes ) )
{
map = shaderCache->get( mapAttribute, attributes );
}
map = shaderCache->get( g_dispMapAttributeNames, attributes );
height = attributeValue<float>( g_dispHeightAttributeName, attributes, 1.0f );
padding = attributeValue<float>( g_dispPaddingAttributeName, attributes, 0.0f );
zeroValue = attributeValue<float>( g_dispZeroValueAttributeName, attributes, 0.0f );
Expand Down Expand Up @@ -3559,7 +3561,7 @@ class ArnoldGlobals
{
if( const IECoreScene::ShaderNetwork *d = reportedCast<const IECoreScene::ShaderNetwork>( value, "option", name ) )
{
m_colorManager = m_shaderCache->get( d, nullptr );
m_colorManager = m_shaderCache->get( d, IECore::InternedString(), nullptr );
}
}
AiNodeSetPtr( options, g_colorManagerArnoldString, m_colorManager ? m_colorManager->root() : nullptr );
Expand All @@ -3572,7 +3574,7 @@ class ArnoldGlobals
{
if( const IECoreScene::ShaderNetwork *d = reportedCast<const IECoreScene::ShaderNetwork>( value, "option", name ) )
{
m_atmosphere = m_shaderCache->get( d, nullptr );
m_atmosphere = m_shaderCache->get( d, IECore::InternedString(), nullptr );
}
}
AiNodeSetPtr( options, g_atmosphereArnoldString, m_atmosphere ? m_atmosphere->root() : nullptr );
Expand All @@ -3585,7 +3587,7 @@ class ArnoldGlobals
{
if( const IECoreScene::ShaderNetwork *d = reportedCast<const IECoreScene::ShaderNetwork>( value, "option", name ) )
{
m_background = m_shaderCache->get( d, nullptr );
m_background = m_shaderCache->get( d, IECore::InternedString(), nullptr );
}
}
AiNodeSetPtr( options, g_backgroundArnoldString, m_background ? m_background->root() : nullptr );
Expand All @@ -3598,7 +3600,7 @@ class ArnoldGlobals
{
if( const IECoreScene::ShaderNetwork *d = reportedCast<const IECoreScene::ShaderNetwork>( value, "option", name ) )
{
m_imager = m_shaderCache->get( d, nullptr );
m_imager = m_shaderCache->get( d, IECore::InternedString(), nullptr );
}
}
for( const auto &output : m_outputs )
Expand All @@ -3614,7 +3616,7 @@ class ArnoldGlobals
{
if( const IECoreScene::ShaderNetwork *d = reportedCast<const IECoreScene::ShaderNetwork>( value, "option", name ) )
{
m_aovShaders[name] = m_shaderCache->get( d, nullptr );
m_aovShaders[name] = m_shaderCache->get( d, IECore::InternedString(), nullptr );
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/IECoreArnold/ShaderNetworkAlgo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1184,11 +1184,11 @@ bool g_textureSubstitutionsRegistration = [] () {
IECoreArnold::ShaderNetworkAlgo::registerSubstitution(
"stringSubstitution",
// Hash
[] ( const IECoreScene::ShaderNetwork *shaderNetwork, const IECore::CompoundObject *attributes, IECore::MurmurHash &hash ) {
[] ( const IECoreScene::ShaderNetwork *shaderNetwork, InternedString attributeName, const IECore::CompoundObject *attributes, IECore::MurmurHash &hash ) {
shaderNetwork->hashSubstitutions( attributes, hash );
},
// Apply
[] ( IECoreScene::ShaderNetwork *shaderNetwork, const IECore::CompoundObject *attributes ) {
[] ( IECoreScene::ShaderNetwork *shaderNetwork, InternedString attributeName, const IECore::CompoundObject *attributes ) {
shaderNetwork->applySubstitutions( attributes );
}
);
Expand Down Expand Up @@ -1233,19 +1233,19 @@ void deregisterSubstitution( const std::string &name )
);
}

void hashSubstitutions( const IECoreScene::ShaderNetwork *shaderNetwork, const IECore::CompoundObject *attributes, IECore::MurmurHash &hash )
void hashSubstitutions( const IECoreScene::ShaderNetwork *shaderNetwork, InternedString attributeName, const IECore::CompoundObject *attributes, IECore::MurmurHash &hash )
{
for( const auto &x : substitutions() )
{
x.hash( shaderNetwork, attributes, hash );
x.hash( shaderNetwork, attributeName, attributes, hash );
}
}

void applySubstitutions( IECoreScene::ShaderNetwork *shaderNetwork, const IECore::CompoundObject *attributes )
void applySubstitutions( IECoreScene::ShaderNetwork *shaderNetwork, InternedString attributeName, const IECore::CompoundObject *attributes )
{
for( const auto &x : substitutions() )
{
x.apply( shaderNetwork, attributes );
x.apply( shaderNetwork, attributeName, attributes );
}
}

Expand Down

0 comments on commit d8f3604

Please sign in to comment.