From aa3fbd5e659fd0e37202589df8f3b22477506929 Mon Sep 17 00:00:00 2001 From: John Haddon Date: Mon, 15 Jun 2026 16:32:09 +0100 Subject: [PATCH 1/4] CI : Update to Cortex 10.7.0.0a11 This requires some tweaks to our handling of instancers now that IECoreUSD unconditionally loads prototypes with relative paths where possible, and doesn't use a `./` prefix any more. --- .github/workflows/main.yml | 2 +- .github/workflows/main/installDependencies.py | 2 +- Changes.md | 11 +++++++++++ bin/__private/_gaffer.py | 6 ------ python/GafferSceneTest/InstancerTest.py | 6 +++--- src/GafferScene/Instancer.cpp | 2 +- 6 files changed, 17 insertions(+), 12 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 36db524b660..6b0b560ba35 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -77,7 +77,7 @@ jobs: testRunner: sudo -E -u testUser sconsCacheMegabytes: 400 jobs: 4 - dependenciesURL: https://github.com/ImageEngine/cortex/releases/download/10.7.0.0a10/cortex-10.7.0.0a10-linux-platform24.tar.gz + dependenciesURL: https://github.com/ImageEngine/cortex/releases/download/10.7.0.0a11/cortex-10.7.0.0a11-linux-platform24.tar.gz extraBuildArguments: CYCLES_ROOT="" - name: windows diff --git a/.github/workflows/main/installDependencies.py b/.github/workflows/main/installDependencies.py index ef169b585a8..85ef03d044b 100755 --- a/.github/workflows/main/installDependencies.py +++ b/.github/workflows/main/installDependencies.py @@ -49,7 +49,7 @@ # Determine default archive URL. -defaultURL = "https://github.com/GafferHQ/dependencies/releases/download/11.0.0a8/gafferDependencies-11.0.0a8-{platform}-{vfxPlatform}.{extension}" +defaultURL = "https://github.com/ImageEngine/cortex/releases/download/10.7.0.0a11/cortex-10.7.0.0a11-{platform}-{vfxPlatform}.{extension}" # Parse command line arguments. diff --git a/Changes.md b/Changes.md index 69710350756..f6aea7afa5f 100644 --- a/Changes.md +++ b/Changes.md @@ -17,6 +17,17 @@ API - Image : Added `updateImage()` method. +Build +----- + +- Cortex : Updated to version 10.7.0.0a11. + +Breaking Changes +---------------- + +- SceneReader : Removed `./` prefix from relative prototype paths loaded from USD files. +- Instancer : Defaulted `GAFFERSCENE_INSTANCER_EXPLICIT_ABSOLUTE_PATHS` to `1`, as required by SceneReader's updated handling of relative USD prototypes. The environment variable may be removed in future. + 1.7.0.0a2 (relative to 1.7.0.0a1) ========= diff --git a/bin/__private/_gaffer.py b/bin/__private/_gaffer.py index 04e41a7f01b..ce3ebf92712 100644 --- a/bin/__private/_gaffer.py +++ b/bin/__private/_gaffer.py @@ -105,12 +105,6 @@ def prependToPath( pathToPrepend, envVar ) : # Stop Cortex from making all Python modules load with RTLD_GLOBAL. os.environ["IECORE_RTLD_GLOBAL"] = "0" -# Load USD PointInstancer prototypes as relative paths by default. This allows -# the _PointInstancerAdaptor to function even when the instancers are reparented -# in the Gaffer hierarchy. -if "IECOREUSD_POINTINSTANCER_RELATIVE_PROTOTYPES" not in os.environ : - os.environ["IECOREUSD_POINTINSTANCER_RELATIVE_PROTOTYPES"] = "1" - # Work around https://github.com/ImageEngine/cortex/issues/1338, which causes # bad serialisations in certain locales. os.environ["LC_NUMERIC"] = "C" diff --git a/python/GafferSceneTest/InstancerTest.py b/python/GafferSceneTest/InstancerTest.py index a2094b26320..6cc69d0ea0e 100644 --- a/python/GafferSceneTest/InstancerTest.py +++ b/python/GafferSceneTest/InstancerTest.py @@ -3690,17 +3690,17 @@ def testRelativePrototypePaths( self ): self.assertEqual( instancer["out"].object( "/groupB/object/instances/sphere/0" ), rootSphere["out"].object( "/sphere" ) ) self.assertEqual( instancer["out"].object( "/groupB/object/instances/sphere1/1" ), sphereB["out"].object( "/sphere" ) ) - if os.environ.get( "GAFFERSCENE_INSTANCER_EXPLICIT_ABSOLUTE_PATHS", "0" ) != "0": + if os.environ.get( "GAFFERSCENE_INSTANCER_EXPLICIT_ABSOLUTE_PATHS", "1" ) != "0": self.assertEqual( instancer["out"].object( "/groupA/object/instances/sphere2/2" ), sphereA["out"].object( "/sphere" ) ) self.assertEqual( instancer["out"].object( "/groupB/object/instances/sphere2/2" ), sphereB["out"].object( "/sphere" ) ) else: self.assertEqual( instancer["out"].object( "/groupA/object/instances/sphere2/2" ), rootSphere["out"].object( "/sphere" ) ) self.assertEqual( instancer["out"].object( "/groupB/object/instances/sphere2/2" ), rootSphere["out"].object( "/sphere" ) ) - def testRelativePrototypePathsWithExplicitAbsolute( self ): + def testRelativePrototypePathsWithoutExplicitAbsolute( self ): try : env = Gaffer.environment() - env["GAFFERSCENE_INSTANCER_EXPLICIT_ABSOLUTE_PATHS"] = "1" + env["GAFFERSCENE_INSTANCER_EXPLICIT_ABSOLUTE_PATHS"] = "0" subprocess.check_output( [ str( Gaffer.executablePath() ), "test", "GafferSceneTest.InstancerTest.testRelativePrototypePaths" ], stderr = subprocess.STDOUT, diff --git a/src/GafferScene/Instancer.cpp b/src/GafferScene/Instancer.cpp index 264d1388db7..57833f3fdef 100644 --- a/src/GafferScene/Instancer.cpp +++ b/src/GafferScene/Instancer.cpp @@ -1067,7 +1067,7 @@ class Instancer::EngineData : public Data m_prototypeIndexRemap.reserve( rootStrings->size() ); - const static bool g_explicitAbsolutePaths = checkEnvFlag( "GAFFERSCENE_INSTANCER_EXPLICIT_ABSOLUTE_PATHS", false ); + const static bool g_explicitAbsolutePaths = checkEnvFlag( "GAFFERSCENE_INSTANCER_EXPLICIT_ABSOLUTE_PATHS", true ); size_t i = 0; ScenePlug::ScenePath path; From d0a7080060aefa9ba7a00781a45d5663820ebbc7 Mon Sep 17 00:00:00 2001 From: John Haddon Date: Fri, 29 May 2026 11:37:56 +0100 Subject: [PATCH 2/4] NodeBinding, PlugBinding : Remove `isInstanceOf()` hacks These optimisations are now provided universally by Cortex, in https://github.com/ImageEngine/cortex/pull/1535. We have test coverage verifying this is the case already in `BoxTest.testComputeNodeCastDoesntRequirePython`. --- include/GafferBindings/NodeBinding.h | 41 ---------------------------- include/GafferBindings/PlugBinding.h | 21 -------------- 2 files changed, 62 deletions(-) diff --git a/include/GafferBindings/NodeBinding.h b/include/GafferBindings/NodeBinding.h index fdd0b2f10b5..b3174dee0a4 100644 --- a/include/GafferBindings/NodeBinding.h +++ b/include/GafferBindings/NodeBinding.h @@ -77,47 +77,6 @@ class NodeWrapper : public GraphComponentWrapper { } - bool isInstanceOf( IECore::TypeId typeId ) const override - { - // Optimise for common queries for types we know about. The standard - // wrapper implementation of `isInstanceOf()` would have to enter - // Python just in case the type was implemented there. Entering - // Python is incredibly costly for such a simple operation, and we - // perform these operations often, so these optimisations are well - // worth it. - - if( - // We're a Node, so we cannot be a plug. - typeId == (IECore::TypeId)Gaffer::PlugTypeId || - typeId == (IECore::TypeId)Gaffer::ValuePlugTypeId - ) - { - return false; - } - - if( - // It's important to optimise for ContextProcessor and - // Switch specifically, because they are queried heavily during - // the `Dispatcher::dispatch()` process. - typeId == (IECore::TypeId)Gaffer::ContextProcessorTypeId || - typeId == (IECore::TypeId)Gaffer::SwitchTypeId || - // ScriptNode, DependencyNode, ComputeNode and EditScope also - // appear on performance critical code paths. - typeId == (IECore::TypeId)Gaffer::ScriptNodeTypeId || - typeId == (IECore::TypeId)Gaffer::ComputeNodeTypeId || - typeId == (IECore::TypeId)Gaffer::DependencyNodeTypeId || - typeId == (IECore::TypeId)Gaffer::EditScopeTypeId - ) - { - // The types above are implemented in C++, so there is no need - // to consider Python overrides for `isInstanceOf()`. The base - // class implementation is sufficient. - return WrappedType::isInstanceOf( typeId ); - } - - return GraphComponentWrapper::isInstanceOf( typeId ); - } - bool acceptsInput( const Gaffer::Plug *plug, const Gaffer::Plug *inputPlug ) const override { if( this->isSubclassed() ) diff --git a/include/GafferBindings/PlugBinding.h b/include/GafferBindings/PlugBinding.h index 0e0f5ab12d4..148536841af 100644 --- a/include/GafferBindings/PlugBinding.h +++ b/include/GafferBindings/PlugBinding.h @@ -69,27 +69,6 @@ class PlugWrapper : public GraphComponentWrapper { } - bool isInstanceOf( IECore::TypeId typeId ) const override - { - // Optimise for common queries we know should fail. - // The standard wrapper implementation of isInstanceOf() - // would have to enter Python only to discover this inevitable - // failure as it doesn't have knowledge of the relationships - // among types. Entering Python is incredibly costly for such - // a simple operation, and we perform these operations often, - // so this optimisation is well worth it. - if( - typeId == (IECore::TypeId)Gaffer::ScriptNodeTypeId || - typeId == (IECore::TypeId)Gaffer::NodeTypeId || - typeId == (IECore::TypeId)Gaffer::DependencyNodeTypeId || - typeId == (IECore::TypeId)Gaffer::ComputeNodeTypeId - ) - { - return false; - } - return GraphComponentWrapper::isInstanceOf( typeId ); - } - bool acceptsInput( const Gaffer::Plug *input ) const override { if( this->isSubclassed() ) From 46f8c0a5b0747c7d3bac80451490aee349d0a416 Mon Sep 17 00:00:00 2001 From: John Haddon Date: Mon, 15 Jun 2026 16:34:05 +0100 Subject: [PATCH 3/4] Changes.md : Fix typo --- Changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changes.md b/Changes.md index f6aea7afa5f..4809b97816e 100644 --- a/Changes.md +++ b/Changes.md @@ -4,7 +4,7 @@ Features -------- -- FlamencoDispatcher : Added a new node for sending tasks to Blender's [Flamenco]((https://flamenco.blender.org) render farm manager. +- FlamencoDispatcher : Added a new node for sending tasks to Blender's [Flamenco](https://flamenco.blender.org) render farm manager. - PrimitiveQuery : Added a new node for querying a primitive's type and variable sizes. Fixes From 91787e2c93de83a40c4ee54201c5ed0a683b328a Mon Sep 17 00:00:00 2001 From: John Haddon Date: Tue, 16 Jun 2026 18:04:33 +0100 Subject: [PATCH 4/4] SceneViewUI : Account for PointInstancers Without this, the icon is always "stuck on". At some point we might want separate masks for Points and Point Instancers, but this is an easy fix that restores things to the way folks are already used to them. --- python/GafferSceneUI/SceneViewUI.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/python/GafferSceneUI/SceneViewUI.py b/python/GafferSceneUI/SceneViewUI.py index 7ceaa1ce3a2..d88a06ae4a4 100644 --- a/python/GafferSceneUI/SceneViewUI.py +++ b/python/GafferSceneUI/SceneViewUI.py @@ -750,12 +750,16 @@ def _leafTypes( typeId ) : derivedTypes = IECore.RunTimeTyped.derivedTypeIds( typeId ) # By "leaf" we really mean "derived enough to appear in the Selection Mask - # menu". So we must pretend that the private InstancerCapsule subclass of - # Capsule doesn't exist. + # menu". So we ignore a couple of derived types. ## \todo No doubt this could be expressed more naturally somehow, perhaps - # just with a set union of `derivedTypes` and `typesWeUseInTheMenu`. - instancerCapsuleTypeId = IECore.RunTimeTyped.typeIdFromTypeName( "InstancerCapsule" ) - derivedTypes = [ t for t in derivedTypes if t != instancerCapsuleTypeId ] + # just with a set union of `derivedTypes` and `typesWeUseInTheMenu`. We + # might like to have separate masks for PointsPrimitives and PointInstancers + # too. + ignoredDerivedTypes = { + IECore.RunTimeTyped.typeIdFromTypeName( "InstancerCapsule" ), + IECoreScene.PointInstancer.staticTypeId() + } + derivedTypes = [ t for t in derivedTypes if t not in ignoredDerivedTypes ] if derivedTypes : return set().union( *[ _leafTypes( t ) for t in derivedTypes ] )