From 8245d4456849ad3fb406a7248d9c1319b14563b1 Mon Sep 17 00:00:00 2001 From: Lucien Fostier Date: Wed, 3 Sep 2025 18:45:32 -0700 Subject: [PATCH 1/6] SceneCacheFileFormat: Handle invalid file path. Handle the exception to avoid crashing the application. --- .../IECoreUSD/src/IECoreUSD/SceneCacheFileFormat.cpp | 5 +++++ .../src/IECoreUSD/SdfFileFormatSharedSceneWriters.cpp | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/contrib/IECoreUSD/src/IECoreUSD/SceneCacheFileFormat.cpp b/contrib/IECoreUSD/src/IECoreUSD/SceneCacheFileFormat.cpp index e811ea4446..cf75f70ddd 100644 --- a/contrib/IECoreUSD/src/IECoreUSD/SceneCacheFileFormat.cpp +++ b/contrib/IECoreUSD/src/IECoreUSD/SceneCacheFileFormat.cpp @@ -175,6 +175,11 @@ bool UsdSceneCacheFileFormat::WriteToFile( const SdfLayer& layer, const std::str SceneInterfacePtr outScene; outScene = SdfFileFormatSharedSceneWriters::get( filePath ); + if ( !outScene ) + { + IECore::msg( IECore::Msg::Error, "UsdSceneCacheFileFormat::WriteToFile", boost::format( "Invalid file path \"%s\" for layer \"%s\"." ) % filePath % layer.GetIdentifier() ); + return false; + } SceneInterface::NameList childNames; usdScene->childNames( childNames ); diff --git a/contrib/IECoreUSD/src/IECoreUSD/SdfFileFormatSharedSceneWriters.cpp b/contrib/IECoreUSD/src/IECoreUSD/SdfFileFormatSharedSceneWriters.cpp index 6cfc3f700a..8ced3db1f3 100644 --- a/contrib/IECoreUSD/src/IECoreUSD/SdfFileFormatSharedSceneWriters.cpp +++ b/contrib/IECoreUSD/src/IECoreUSD/SdfFileFormatSharedSceneWriters.cpp @@ -35,6 +35,7 @@ #include "SdfFileFormatSharedSceneWriters.h" #include "IECore/LRUCache.h" +#include "IECore/MessageHandler.h" using namespace IECore; using namespace IECoreScene; @@ -61,7 +62,15 @@ class Cache : public SceneLRUCache static SceneInterfacePtr fileCacheGetter( const std::string &fileName, size_t &cost ) { - SceneInterfacePtr result = SceneInterface::create( fileName, IECore::IndexedIO::Write ); + SceneInterfacePtr result = nullptr; + try + { + result = SceneInterface::create( fileName, IECore::IndexedIO::Write ); + } + catch ( ... ) + { + IECore::msg( IECore::Msg::Error, "SdfFileFormatSharedSceneWriters::SceneLRUCache", boost::format( "Unable to open file path \"%s\" for writing IndexedIo data." ) % fileName ); + } cost = 1; return result; } From 565a8f94052285d30a560eb4faa871541373df92 Mon Sep 17 00:00:00 2001 From: Lucien Fostier Date: Thu, 4 Sep 2025 17:25:27 -0700 Subject: [PATCH 2/6] SceneCacheFileFormatTest: Test invalid path. --- .../test/IECoreUSD/SceneCacheFileFormatTest.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py b/contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py index f943a2706d..20a4748f8b 100644 --- a/contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py +++ b/contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py @@ -939,6 +939,17 @@ def testSceneWrite( self ): stage.Export( exportPath ) self.assertTrue( os.path.exists( exportPath ) ) + # invalid path + invalidExportPath = os.path.join( self.temporaryDirectory(), "invalid", "invalid.scc" ) + with IECore.CapturingMessageHandler() as mh : + stage.Export( invalidExportPath ) + + self.assertEqual( len( mh.messages ), 2 ) + self.assertEqual( mh.messages[0].level, IECore.Msg.Level.Error ) + self.assertEqual( mh.messages[0].context, "SdfFileFormatSharedSceneWriters::SceneLRUCache" ) + self.assertEqual( mh.messages[1].level, IECore.Msg.Level.Error ) + self.assertEqual( mh.messages[1].context, "UsdSceneCacheFileFormat::WriteToFile" ) + # root layer = pxr.Sdf.Layer.FindOrOpen( linkFileName ) From 3c4aacc728321398afcb2c8e955eb9175f8159b3 Mon Sep 17 00:00:00 2001 From: Lucien Fostier Date: Thu, 4 Sep 2025 15:44:01 -0700 Subject: [PATCH 3/6] SceneCacheData: Add support for root location tags added to the internal root. In USD, we can't add collections to the absolute root `/` so we leverage the internal root (used to work around the limitation with referencing layer in USD that get read of the file's pseudo root) to store the tags/collections. --- .../src/IECoreUSD/SceneCacheData.cpp | 24 +++++++++++++++++-- .../IECoreUSD/src/IECoreUSD/SceneCacheData.h | 2 +- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/contrib/IECoreUSD/src/IECoreUSD/SceneCacheData.cpp b/contrib/IECoreUSD/src/IECoreUSD/SceneCacheData.cpp index 3376906812..bc03f94f86 100644 --- a/contrib/IECoreUSD/src/IECoreUSD/SceneCacheData.cpp +++ b/contrib/IECoreUSD/src/IECoreUSD/SceneCacheData.cpp @@ -217,7 +217,7 @@ void SceneCacheData::addReference( ConstSceneInterfacePtr scene, SpecData& spec, addValueClip( spec, times, actives, linkFileName, linkRootPath.GetText() ); } -void SceneCacheData::addInternalRoot( TfTokenVector children ) +void SceneCacheData::addInternalRoot( TfTokenVector children, IECoreScene::ConstSceneInterfacePtr scene ) { // add transform for internal root. SdfPath internalRootPath = SdfPath::AbsoluteRootPath().AppendChild( SceneCacheDataAlgo::internalRootNameToken() ); @@ -237,6 +237,26 @@ void SceneCacheData::addInternalRoot( TfTokenVector children ) // steal root children internalRootSpec.fields.push_back( FieldValuePair( SdfChildrenKeys->PrimChildren, children ) ); + // add collection + FieldValuePair propertyChildren; + propertyChildren.first = SdfChildrenKeys->PropertyChildren; + TfTokenVector properties; + + // we don't want to keep children tags in this case. + m_collections.clear(); + + SceneInterface::NameList tags; + scene->readTags( tags ); + for ( auto& tag : tags ) + { + m_collections[tag].push_back( internalRootPath ); + } + + addCollections( internalRootSpec, properties, internalRootPath ); + + propertyChildren.second = properties; + internalRootSpec.fields.push_back( propertyChildren ); + m_data[internalRootPath] = internalRootSpec; } @@ -344,7 +364,7 @@ void SceneCacheData::loadSceneIntoCache( ConstSceneInterfacePtr scene ) // end timecode spec.fields.push_back( FieldValuePair( SdfFieldKeys->EndTimeCode, lastFrame ) ); - addInternalRoot( children ); + addInternalRoot( children, scene ); // add internal root as single child children.clear(); diff --git a/contrib/IECoreUSD/src/IECoreUSD/SceneCacheData.h b/contrib/IECoreUSD/src/IECoreUSD/SceneCacheData.h index 003e5878a9..632d5d0d98 100644 --- a/contrib/IECoreUSD/src/IECoreUSD/SceneCacheData.h +++ b/contrib/IECoreUSD/src/IECoreUSD/SceneCacheData.h @@ -179,7 +179,7 @@ class SceneCacheData : public SdfAbstractData void addCollections( SpecData& spec, TfTokenVector& properties, const SdfPath& primPath ); void addReference( IECoreScene::ConstSceneInterfacePtr scene, SpecData& spec, TfTokenVector& children ); void addValueClip( SpecData& spec, const VtVec2dArray times, const VtVec2dArray actives, const std::string& assetPath, const std::string& primPath); - void addInternalRoot( TfTokenVector children ); + void addInternalRoot( TfTokenVector children, IECoreScene::ConstSceneInterfacePtr scene ); VtValue getTimeSampleMap( const SdfPath& path, const TfToken& field, const VtValue& value ) const; From 2d86a9a1e96ce3891270547b9e494ebd27a82b01 Mon Sep 17 00:00:00 2001 From: Lucien Fostier Date: Wed, 3 Sep 2025 16:01:54 -0700 Subject: [PATCH 4/6] SceneCacheFileFormat: Add support for collection stored on the internalRoot. USD doesnt support writing collections on the absolute root prim (`/`) but IECoreScene does. We have some tags we want to be able to write out of Houdini that should be stored on root loacation (`/`), therefore we leverage the internal root to author the collections and map that to IECoreScene root loaction. Note that there is still a limitation for read tags from the root location and converting them to collections stored on the internal root but the current priority is to support writing out. --- .../IECoreUSD/src/IECoreUSD/SceneCacheFileFormat.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/contrib/IECoreUSD/src/IECoreUSD/SceneCacheFileFormat.cpp b/contrib/IECoreUSD/src/IECoreUSD/SceneCacheFileFormat.cpp index cf75f70ddd..b01e0425b7 100644 --- a/contrib/IECoreUSD/src/IECoreUSD/SceneCacheFileFormat.cpp +++ b/contrib/IECoreUSD/src/IECoreUSD/SceneCacheFileFormat.cpp @@ -375,6 +375,18 @@ void UsdSceneCacheFileFormat::writeLocation( } } } + // internal root is mapped to the SceneInterface root '/' + else + { + SceneInterface::NameList tags; + inChild->readTags( tags ); + // round trip internal tag name + for ( auto& tag : tags ) + { + tag = SceneCacheDataAlgo::fromInternalName( tag ); + } + outChild->writeTags( tags ); + } // recursion SceneInterface::NameList grandChildNames; From 1820df9c7b5538989f46593d6b347a44942ec1e8 Mon Sep 17 00:00:00 2001 From: Lucien Fostier Date: Thu, 4 Sep 2025 15:50:57 -0700 Subject: [PATCH 5/6] SceneCacheFileFormatTest: Test support for root location tags. --- .../test/IECoreUSD/SceneCacheFileFormatTest.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py b/contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py index 20a4748f8b..3ce3692f85 100644 --- a/contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py +++ b/contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py @@ -320,6 +320,7 @@ def testTagsLoadedAsCollections( self ): # includes fileName = os.path.join( self.temporaryDirectory(), "testUSDTags.scc" ) m = IECoreScene.SceneCache( fileName, IECore.IndexedIO.OpenMode.Write ) + m.writeTags( ["geoId:chessy"] ) t = m.createChild( "t" ) s = t.createChild( "s" ) t.writeTags( ["t1", "all", "asset-(12)"] ) @@ -330,6 +331,16 @@ def testTagsLoadedAsCollections( self ): stage = pxr.Usd.Stage.Open( fileName ) root = stage.GetPseudoRoot() + tagInternalRootPrim = root.GetPrimAtPath( f"/{IECoreUSD.SceneCacheDataAlgo.internalRootName()}" ) + self.assertEqual( + tagInternalRootPrim.GetRelationship( + "collection:{}:includes".format( + IECoreUSD.SceneCacheDataAlgo.toInternalName( "geoId:chessy" ) + ) + ).GetTargets(), + [ pxr.Sdf.Path( f"/{IECoreUSD.SceneCacheDataAlgo.internalRootName()}" ) ] + ) + tagPrim = root.GetPrimAtPath( "/{}/t".format( IECoreUSD.SceneCacheDataAlgo.internalRootName() ) ) self.assertTrue( tagPrim ) @@ -346,6 +357,10 @@ def testTagsLoadedAsCollections( self ): stage.Export( exportPath ) scene = IECoreScene.SharedSceneInterfaces.get( exportPath ) + # check root tags + self.assertTrue( "geoId:chessy" in scene.readTags() ) + + # check children tags for tag, paths in tags.items(): for path in paths: child = scene.scene( IECoreScene.SceneInterface.stringToPath( path ) ) From 8062e44b83d26d7ca26c9622dc76c4aa3db37c8e Mon Sep 17 00:00:00 2001 From: Lucien Fostier Date: Fri, 5 Sep 2025 11:10:03 -0700 Subject: [PATCH 6/6] Changes: Added changes for root tag improvements in IECoreUSD --- Changes | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index ea551fb21d..80b9caf689 100644 --- a/Changes +++ b/Changes @@ -48,10 +48,23 @@ Breaking Changes - Removed support for `IECORE_RTLD_GLOBAL` environment variable. - SmoothSkinningData : Removed, along with all associated Ops and Parameters. -10.5.x.x (relative to 10.5.15.3) +10.5.x.x (relative to 10.5.15.4) +======= + + + +10.5.15.4 (relative to 10.5.15.3) ======== +Improvements +----- + +- IECoreUSD: Added support for root level tags (reading/writing) to our IECoreUSD::SceneCacheData plugin. + +Fixes +----- +- IECoreUSD: Fixed crash when using invalid file path with IECoreUSD::SceneCacheFileFormat. 10.5.15.3 (relative to 10.5.15.2) =========