From 1a38cbd3d1c4331f87befc410a215e9889a129f4 Mon Sep 17 00:00:00 2001 From: John Haddon Date: Fri, 16 Jan 2026 11:27:04 +0000 Subject: [PATCH 1/2] ShaderNetworkAlgo : Add render adaptor API This has been lifted from IECoreArnold, because we now want to provide it in all renderer backends. In IECoreArnold it was described as a "substitutions" API, but I've characterised it as a "render adaptor" API here as it isn't limited to substitutions and we want to make it clear that it applies at render time. There are no Python bindings because doing anything with Python would be a performance issue when translating scenes at scale. Test coverage will be provided by IECoreArnold, IECoreRenderMan etc. --- Changes | 6 ++ include/IECoreScene/ShaderNetworkAlgo.h | 32 +++++++++ src/IECoreScene/ShaderNetworkAlgo.cpp | 89 +++++++++++++++++++++++++ 3 files changed, 127 insertions(+) diff --git a/Changes b/Changes index b85ef0da23..08a55d1ed1 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,12 @@ 10.6.x.x (relative to 10.6.2.2) ======== +Improvements +------------ + +- ShaderNetworkAlgo : Added render adaptor API, allowing just-in-time modifications to + ShaderNetworks during translation for rendering. + 10.6.2.2 ======== diff --git a/include/IECoreScene/ShaderNetworkAlgo.h b/include/IECoreScene/ShaderNetworkAlgo.h index 0e8998ad5a..42e9c1156c 100644 --- a/include/IECoreScene/ShaderNetworkAlgo.h +++ b/include/IECoreScene/ShaderNetworkAlgo.h @@ -156,6 +156,38 @@ IECORESCENE_API IECore::ConstCompoundDataPtr collapseSplineParameters( const IEC /// different spline conventions for different renderers' shader libraries IECORESCENE_API IECore::ConstCompoundDataPtr expandSplineParameters( const IECore::ConstCompoundDataPtr& parametersData ); +/// Render Adaptors +/// =============== +/// +/// Render adaptors are functions called to make "just in time" modifications to +/// shader networks prior to rendering. They have access to a fully resolved +/// attribute state, which can be used to drive the modifications. There is currently +/// a single built-in adaptor, which uses `ShaderNetworkAlgo::applySubstitutions()` +/// to allow strings such as texture paths to be substituted in from attributes that +/// define them. +/// +/// Adaptors are performance critical, so are deliberately not available via +/// the Python API. They must be implemented in C++ only. + +/// A function that adapts the shader network for rendering, given the full +/// inherited `attributes` for an object. Must be threadsafe. +using RenderAdaptorFunction = void (*)( 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 an AdaptorFunction. Particular attention must be paid to +/// the performance of any such function, as it will be called frequently. If an +/// adaptor will be a no-op, then nothing should be appended to the hash. +/// Must be threadsafe. +using RenderAdaptorHashFunction = void (*)( const ShaderNetwork *shaderNetwork, IECore::InternedString attributeName, const IECore::CompoundObject *attributes, IECore::MurmurHash &hash ); + +/// Registers an adaptor. +IECORESCENE_API void registerRenderAdaptor( const std::string &name, RenderAdaptorHashFunction hashFunction, RenderAdaptorFunction adaptorFunction ); +/// Removes a previously registered adaptor with the specified name. +IECORESCENE_API void deregisterRenderAdaptor( const std::string &name ); + +/// Hashes all the currently registered adaptors for `shaderNetwork`. +IECORESCENE_API void hashRenderAdaptors( const ShaderNetwork *shaderNetwork, IECore::InternedString attributeName, const IECore::CompoundObject *attributes, IECore::MurmurHash &hash ); +/// Applies all the currently registered adaptors to `shaderNetwork`. +IECORESCENE_API void applyRenderAdaptors( ShaderNetwork *shaderNetwork, IECore::InternedString attributeName, const IECore::CompoundObject *attributes ); } // namespace ShaderNetworkAlgo diff --git a/src/IECoreScene/ShaderNetworkAlgo.cpp b/src/IECoreScene/ShaderNetworkAlgo.cpp index bab0cecf0e..16e0daad60 100644 --- a/src/IECoreScene/ShaderNetworkAlgo.cpp +++ b/src/IECoreScene/ShaderNetworkAlgo.cpp @@ -1472,3 +1472,92 @@ IECore::ConstCompoundDataPtr ShaderNetworkAlgo::expandSplineParameters( const IE return parametersData; } } + + +////////////////////////////////////////////////////////////////////////// +// Render Adaptors +////////////////////////////////////////////////////////////////////////// + +namespace +{ + +struct RenderAdaptor +{ + std::string name; + IECoreScene::ShaderNetworkAlgo::RenderAdaptorHashFunction hash; + IECoreScene::ShaderNetworkAlgo::RenderAdaptorFunction apply; +}; + +using RenderAdaptors = std::vector; +RenderAdaptors &renderAdaptors() +{ + static RenderAdaptors g_renderAdaptors; + return g_renderAdaptors; +} + +bool g_stringSubstitutionsRegistration = [] () { + + IECoreScene::ShaderNetworkAlgo::registerRenderAdaptor( + "stringSubstitution", + // Hash + [] ( const IECoreScene::ShaderNetwork *shaderNetwork, InternedString attributeName, const IECore::CompoundObject *attributes, IECore::MurmurHash &hash ) { + shaderNetwork->hashSubstitutions( attributes, hash ); + }, + // Apply + [] ( IECoreScene::ShaderNetwork *shaderNetwork, InternedString attributeName, const IECore::CompoundObject *attributes ) { + shaderNetwork->applySubstitutions( attributes ); + } + ); + + return true; +} (); + +} // namespace + +void ShaderNetworkAlgo::registerRenderAdaptor( const std::string &name, RenderAdaptorHashFunction hashFunction, RenderAdaptorFunction adaptorFunction ) +{ + // Replace existing adaptor if it exists. + RenderAdaptors &a = renderAdaptors(); + for( auto &x : a ) + { + if( x.name == name ) + { + x.hash = hashFunction; + x.apply = adaptorFunction; + return; + } + } + // Otherwise add new adaptor. + a.push_back( { name, hashFunction, adaptorFunction } ); +} + +void ShaderNetworkAlgo::deregisterRenderAdaptor( const std::string &name ) +{ + RenderAdaptors &a = renderAdaptors(); + a.erase( + std::remove_if( + a.begin(), + a.end(), + [&] ( const RenderAdaptor &x ) { + return x.name == name; + } + ), + a.end() + ); +} + +void ShaderNetworkAlgo::hashRenderAdaptors( const IECoreScene::ShaderNetwork *shaderNetwork, InternedString attributeName, const IECore::CompoundObject *attributes, IECore::MurmurHash &hash ) +{ + for( const auto &x : renderAdaptors() ) + { + x.hash( shaderNetwork, attributeName, attributes, hash ); + } +} + +void ShaderNetworkAlgo::applyRenderAdaptors( IECoreScene::ShaderNetwork *shaderNetwork, InternedString attributeName, const IECore::CompoundObject *attributes ) +{ + for( const auto &x : renderAdaptors() ) + { + x.apply( shaderNetwork, attributeName, attributes ); + } +} From 20dc1462c155f4908f58178789f58de273b260b1 Mon Sep 17 00:00:00 2001 From: John Haddon Date: Fri, 16 Jan 2026 12:56:20 +0000 Subject: [PATCH 2/2] SConstruct : Remove unused imports This is critical on Windows CI, because GitHub has just updated the default Python version to 3.12, which no longer includes `distutils`. See https://github.com/actions/runner-images/issues/13468. --- SConstruct | 3 --- 1 file changed, 3 deletions(-) diff --git a/SConstruct b/SConstruct index 6e2f498b6b..1cc8b22a00 100644 --- a/SConstruct +++ b/SConstruct @@ -41,15 +41,12 @@ # ########################################################################## -import SCons -import shutil import glob import sys import os import re import subprocess import platform -import distutils EnsureSConsVersion( 3, 0, 2 ) # Substfile is a default builder as of 3.0.2 SConsignFile()