diff --git a/Changes.md b/Changes.md index 927854660c1..d14dd290031 100644 --- a/Changes.md +++ b/Changes.md @@ -25,6 +25,7 @@ Fixes - Reference : Fixed rare reloading error. - PlugLayout : Fixed lack of update when `layout:customWidget:*` metadata changes. - Dispatch app : Removed unnecessary and misleading "Execute" button. +- SceneAlgo : Fixed computation of `ScenePlug.object` in networks with nodes derived from `ObjectProcessor`. These include : `CameraTweaks`, `ClosestPointSampler`, `CollectPrimitiveVariables`, `CopyPrimitiveVariables`, `CurveSampler`, `DeleteCurves`, `DeleteFaces`, `DeletePoints`, `MapOffset`, `MapProjection`, `MeshDistortion`, `MeshNormals`, `MeshSegments`, `MeshTangents`, `MeshToPoints`, `MeshType`, `Orientation`, `PointsType`, `PrimitiveSampler`, `PrimitiveVariables`, `ReverseWinding`, `ShufflePrimitiveVariables` and `UVSampler` (#5406). API --- diff --git a/python/GafferSceneTest/SceneAlgoTest.py b/python/GafferSceneTest/SceneAlgoTest.py index 68b938d79ca..1d95b28dc7e 100644 --- a/python/GafferSceneTest/SceneAlgoTest.py +++ b/python/GafferSceneTest/SceneAlgoTest.py @@ -404,6 +404,38 @@ def testHistoryPerformanceAlreadyCached( self ) : h = h.predecessors[-1] self.assertEqual( h.context["seed"], 5 ) + def testObjectProcessorHistory( self ) : + + plane = GafferScene.Plane() + + meshType = GafferScene.MeshType() + meshType["in"].setInput( plane["out"] ) + + def runTest() : + + history = GafferScene.SceneAlgo.history( meshType["out"]["object"], "/plane" ) + + for plug, scenePath in [ + ( meshType["out"], "/plane" ), + ( meshType["in"], "/plane" ), + ( plane["out"], "/plane" ), + ] : + self.assertEqual( history.scene, plug ) + self.assertEqual( GafferScene.ScenePlug.pathToString( history.context["scene:path"] ), scenePath ) + history = history.predecessors[0] if history.predecessors else None + + self.assertIsNone( history ) + + runTest() + + # Test running the test while everything is already cached still works, and doesn't add any + # new entries to the cache + Gaffer.ValuePlug.clearHashCache() + runTest() + before = Gaffer.ValuePlug.hashCacheTotalUsage() + runTest() + self.assertEqual( Gaffer.ValuePlug.hashCacheTotalUsage(), before ) + def testHistoryWithNoComputes( self ) : switch = Gaffer.Switch() diff --git a/src/GafferScene/SceneAlgo.cpp b/src/GafferScene/SceneAlgo.cpp index 9a65e4f47d2..9e74f317ec5 100644 --- a/src/GafferScene/SceneAlgo.cpp +++ b/src/GafferScene/SceneAlgo.cpp @@ -400,6 +400,8 @@ struct CapturedProcess }; +const InternedString g_processedObjectPlugName( "__processedObject" ); + /// \todo Perhaps add this to the Gaffer module as a /// public class, and expose it within the stats app? /// Give a bit more thought to the CapturedProcess @@ -432,7 +434,7 @@ class CapturingMonitor : public Monitor CapturedProcess::Ptr capturedProcess; ProcessOrScope entry; - if( !p->parent() || p->getName() != m_scenePlugChildName ) + if( !shouldCapture( p ) ) { // Parents may spawn other processes in support of the requested plug. This is one // of these other plugs that isn't directly the requested plug. Instead of creating @@ -491,10 +493,7 @@ class CapturingMonitor : public Monitor bool forceMonitoring( const Plug *plug, const IECore::InternedString &processType ) override { - if( - processType == g_hashProcessType && - plug->parent() && plug->getName() == m_scenePlugChildName - ) + if( processType == g_hashProcessType && shouldCapture( plug ) ) { return true; } @@ -504,6 +503,14 @@ class CapturingMonitor : public Monitor private : + const bool shouldCapture( const Plug *plug ) const + { + return + ( plug->parent() && plug->getName() == m_scenePlugChildName ) || + ( (Gaffer::TypeId)plug->typeId() == Gaffer::TypeId::ObjectPlugTypeId && plug->getName() == g_processedObjectPlugName ) + ; + } + using Mutex = tbb::spin_mutex; Mutex m_mutex;