diff --git a/.codespellrc b/.codespellrc index 0864665338..ef3a145cfd 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,3 +1,3 @@ [codespell] -skip = ./testing/**/*,./.git/**/*,./external/cxxopts/cxxopts.hpp,./external/nlohmann_json/nlohmann/json.hpp +skip = ./testing/**/*,./.git/**/*,./**/*.patch,./external/cxxopts/cxxopts.hpp,./external/nlohmann_json/nlohmann/json.hpp ignore-words-list=nnumber,unknwn,dota,modle diff --git a/.github/actions/boost-install-dep/action.yml b/.github/actions/boost-install-dep/action.yml new file mode 100644 index 0000000000..511d905321 --- /dev/null +++ b/.github/actions/boost-install-dep/action.yml @@ -0,0 +1,60 @@ +name: 'Install Boost Dependency' +description: 'Install Boost Dependency using cache when possible' + +runs: + using: "composite" + steps: + + - name: Cache Boost + id: cache-boost + uses: actions/cache@v3 + with: + path: dependencies/boost_install + key: boost-1.82.0-${{runner.os}}-0 + + - name: Checkout Boost + if: steps.cache-boost.outputs.cache-hit != 'true' + uses: actions/checkout@v3 + with: + repository: boostorg/boost + submodules: true + fetch-depth: 0 + path: './dependencies/boost' + ref: boost-1.82.0 + + - name: Setup Boost + if: steps.cache-boost.outputs.cache-hit != 'true' + working-directory: ${{github.workspace}}/dependencies + shell: bash + run: | + mkdir boost_build + mkdir boost_install + + - name: Configure Boost + if: steps.cache-boost.outputs.cache-hit != 'true' + working-directory: ${{github.workspace}}/dependencies/boost + shell: bash + run: ./bootstrap.sh + + - name: Build Boost + if: steps.cache-boost.outputs.cache-hit != 'true' + working-directory: ${{github.workspace}}/dependencies/boost + shell: bash + run: > + ./b2 + --prefix='../boost_install' + --build-dir='../boost_build' + variant='release' + link=static + runtime-link=shared + --with-date_time + --with-filesystem + --with-program_options + --with-regex + --with-system + install + + - name: Copy to install + working-directory: ${{github.workspace}}/dependencies/boost_install + shell: bash + run: cp -r ./* ../install/ diff --git a/.github/actions/coverage-ci/action.yml b/.github/actions/coverage-ci/action.yml index 52056c7b2a..27b5926317 100644 --- a/.github/actions/coverage-ci/action.yml +++ b/.github/actions/coverage-ci/action.yml @@ -65,6 +65,7 @@ runs: -DF3D_PLUGIN_BUILD_ASSIMP=ON -DF3D_PLUGIN_BUILD_DRACO=ON -DF3D_PLUGIN_BUILD_OCCT=ON + -DF3D_PLUGIN_BUILD_USD=ON -DF3D_STRICT_BUILD=ON -DF3D_TESTING_ENABLE_LONG_TIMEOUT_TESTS=ON diff --git a/.github/actions/f3d-dependencies/action.yml b/.github/actions/f3d-dependencies/action.yml index a51ba43d77..be973b8e5b 100644 --- a/.github/actions/f3d-dependencies/action.yml +++ b/.github/actions/f3d-dependencies/action.yml @@ -48,3 +48,11 @@ runs: - name: Install pybind11 dependency uses: ./source/.github/actions/pybind11-install-dep + + - name: Install Boost dependency + uses: ./source/.github/actions/boost-install-dep + + - name: Install USD dependency + uses: ./source/.github/actions/usd-install-dep + with: + cpu: ${{inputs.cpu}} diff --git a/.github/actions/generic-ci/action.yml b/.github/actions/generic-ci/action.yml index 1d02741a35..cc6a827ebc 100644 --- a/.github/actions/generic-ci/action.yml +++ b/.github/actions/generic-ci/action.yml @@ -122,7 +122,7 @@ runs: if: runner.os == 'Windows' shell: powershell working-directory: ${{github.workspace}} - run: echo "PATH=$env:PATH;$(pwd)\dependencies\install\bin\;$(pwd)\install\bin\;"| Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + run: echo "PATH=$env:PATH;$(pwd)\dependencies\install\bin\;$(pwd)\dependencies\install\lib\;$(pwd)\install\bin\;"| Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: Determine if testing should be enabled id: vars @@ -160,6 +160,7 @@ runs: -DF3D_PLUGIN_BUILD_DRACO=${{ inputs.optional_deps_label == 'optional-deps' && 'ON' || 'OFF' }} -DF3D_PLUGIN_BUILD_EXODUS=${{ inputs.cpu == 'x86_64' && inputs.optional_deps_label == 'optional-deps' && 'ON' || 'OFF' }} -DF3D_PLUGIN_BUILD_OCCT=${{ inputs.optional_deps_label == 'optional-deps' && 'ON' || 'OFF' }} + -DF3D_PLUGIN_BUILD_USD=${{ inputs.optional_deps_label == 'optional-deps' && 'ON' || 'OFF' }} -DF3D_PLUGIN_OCCT_COLORING_SUPPORT=${{ runner.os == 'macOS' && 'OFF' || 'ON' }} -DF3D_STRICT_BUILD=ON -DF3D_WINDOWS_GUI=ON diff --git a/.github/actions/lfs-copy/action.yml b/.github/actions/lfs-copy/action.yml index 3653d8f5fd..745c69ce34 100644 --- a/.github/actions/lfs-copy/action.yml +++ b/.github/actions/lfs-copy/action.yml @@ -22,7 +22,7 @@ runs: uses: actions/cache@v3 with: path: lfs_data - key: lfs-data-${{inputs.lfs_sha}}-1 + key: lfs-data-${{inputs.lfs_sha}}-0 - name: Checkout LFS data if: | diff --git a/.github/actions/sanitizer-ci/action.yml b/.github/actions/sanitizer-ci/action.yml index 3d210d0f43..ad84f1bfea 100644 --- a/.github/actions/sanitizer-ci/action.yml +++ b/.github/actions/sanitizer-ci/action.yml @@ -53,6 +53,8 @@ runs: working-directory: ${{github.workspace}} run: mkdir build + # USD gives a lot of leaks and race conditions + # Not sure if it's false positives but the plugin is disabled for now - name: Configure shell: bash working-directory: ${{github.workspace}}/build @@ -71,6 +73,7 @@ runs: -DF3D_PLUGIN_BUILD_ASSIMP=ON -DF3D_PLUGIN_BUILD_DRACO=ON -DF3D_PLUGIN_BUILD_OCCT=ON + -DF3D_PLUGIN_BUILD_USD=OFF -DF3D_SANITIZER=${{inputs.sanitizer_type}} -DF3D_STRICT_BUILD=ON -DF3D_TESTING_ENABLE_LONG_TIMEOUT_TESTS=ON diff --git a/.github/actions/static-analysis-ci/action.yml b/.github/actions/static-analysis-ci/action.yml index 4c0300662d..2ecce678fd 100644 --- a/.github/actions/static-analysis-ci/action.yml +++ b/.github/actions/static-analysis-ci/action.yml @@ -62,6 +62,7 @@ runs: -DF3D_PLUGIN_BUILD_ASSIMP=ON -DF3D_PLUGIN_BUILD_DRACO=ON -DF3D_PLUGIN_BUILD_OCCT=ON + -DF3D_PLUGIN_BUILD_USD=ON -DF3D_STRICT_BUILD=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON diff --git a/.github/actions/usd-install-dep/0001-usd-onetbb.patch b/.github/actions/usd-install-dep/0001-usd-onetbb.patch new file mode 100644 index 0000000000..128244d128 --- /dev/null +++ b/.github/actions/usd-install-dep/0001-usd-onetbb.patch @@ -0,0 +1,2064 @@ +From 26594a04da545ce7368a4abebe4c6a3012721ef6 Mon Sep 17 00:00:00 2001 +From: gitamohr +Date: Thu, 18 May 2023 14:05:35 -0700 +Subject: [PATCH 01/14] sdf: Path node cleanups. Embed the "has token" flag in + the node's refcount so we don't read/write it non-atomically. + +(Internal change: 2276996) +--- + pxr/usd/sdf/path.h | 8 ++-- + pxr/usd/sdf/pathNode.cpp | 14 +++--- + pxr/usd/sdf/pathNode.h | 93 ++++++++++++++++++++++------------------ + 3 files changed, 62 insertions(+), 53 deletions(-) + +diff --git a/pxr/usd/sdf/path.h b/pxr/usd/sdf/path.h +index a0986d26d..c49c37bf4 100644 +--- a/pxr/usd/sdf/path.h ++++ b/pxr/usd/sdf/path.h +@@ -145,7 +145,7 @@ public: + _poolHandle = Handle { nullptr }; + } + +- Sdf_PathNode const * ++ inline Sdf_PathNode const * + get() const noexcept { + return reinterpret_cast(_poolHandle.GetPtr()); + } +@@ -179,17 +179,17 @@ public: + } + private: + +- void _AddRef(Sdf_PathNode const *p) const { ++ inline void _AddRef(Sdf_PathNode const *p) const { + if (Counted) { + intrusive_ptr_add_ref(p); + } + } + +- void _AddRef() const { ++ inline void _AddRef() const { + _AddRef(get()); + } + +- void _DecRef() const { ++ inline void _DecRef() const { + if (Counted) { + intrusive_ptr_release(get()); + } +diff --git a/pxr/usd/sdf/pathNode.cpp b/pxr/usd/sdf/pathNode.cpp +index 49ab851b0..defaf35f3 100644 +--- a/pxr/usd/sdf/pathNode.cpp ++++ b/pxr/usd/sdf/pathNode.cpp +@@ -61,7 +61,7 @@ static_assert(sizeof(Sdf_PrimPropertyPathNode) == 3 * sizeof(void *), ""); + struct Sdf_PathNodePrivateAccess + { + template +- static inline tbb::atomic & ++ static inline std::atomic & + GetRefCount(Handle h) { + Sdf_PathNode const *p = + reinterpret_cast(h.GetPtr()); +@@ -263,8 +263,9 @@ _FindOrCreate(Table &table, + } + if (iresult.second || + (Table::NodeHandle::IsCounted && +- Access::GetRefCount( +- iresult.first->second).fetch_and_increment() == 0)) { ++ (Access::GetRefCount( ++ iresult.first->second).fetch_add(1) & ++ Sdf_PathNode::RefCountMask) == 0)) { + // There was either no entry, or there was one but it had begun dying + // (another client dropped its refcount to 0). We have to create a new + // entry in the table. When the client that is deleting the other node +@@ -437,10 +438,7 @@ Sdf_PathNode::Sdf_PathNode(bool isAbsolute) : + _refCount(1), + _elementCount(0), + _nodeType(RootNode), +- _isAbsolute(isAbsolute), +- _containsPrimVariantSelection(false), +- _containsTargetPath(false), +- _hasToken(false) ++ _nodeFlags(isAbsolute ? IsAbsoluteFlag : 0) + { + } + +@@ -506,7 +504,7 @@ Sdf_PathNode::GetPathToken(Sdf_PathNode const *primPart, + { + // Set the cache bit. We only ever read this during the dtor, and that has + // to be exclusive to all other execution. +- primPart->_hasToken = true; ++ primPart->_refCount.fetch_or(HasTokenBit, std::memory_order_relaxed); + + // Attempt to insert. + TfAutoMallocTag2 tag("Sdf", "SdfPath"); +diff --git a/pxr/usd/sdf/pathNode.h b/pxr/usd/sdf/pathNode.h +index 1fdd1ba0d..ae09d5ccf 100644 +--- a/pxr/usd/sdf/pathNode.h ++++ b/pxr/usd/sdf/pathNode.h +@@ -33,8 +33,6 @@ + #include + #include + +-#include +- + PXR_NAMESPACE_OPEN_SCOPE + + // Sdf_PathNode +@@ -63,10 +61,18 @@ class Sdf_PathNode { + Sdf_PathNode(Sdf_PathNode const &) = delete; + Sdf_PathNode &operator=(Sdf_PathNode const &) = delete; + public: ++ ++ static constexpr uint8_t IsAbsoluteFlag = 1 << 0; ++ static constexpr uint8_t ContainsPrimVarSelFlag = 1 << 1; ++ static constexpr uint8_t ContainsTargetPathFlag = 1 << 2; ++ ++ static constexpr uint32_t HasTokenBit = 1u << 31; ++ static constexpr uint32_t RefCountMask = ~HasTokenBit; ++ + // Node types identify what kind of path node a given instance is. + // There are restrictions on what type of children each node type + // can have, +- enum NodeType { ++ enum NodeType : uint8_t { + + /********************************************************/ + /******************************* Prim portion nodes *****/ +@@ -172,19 +178,23 @@ public: + bool stopAtRootPrim); + + // This method returns a node pointer +- Sdf_PathNode const *GetParentNode() const { return _parent.get(); } ++ inline Sdf_PathNode const *GetParentNode() const { return _parent.get(); } + +- size_t GetElementCount() const { return size_t(_elementCount); } +- bool IsAbsolutePath() const { return _isAbsolute; } +- bool IsAbsoluteRoot() const { return (_isAbsolute) & (!_elementCount); } +- bool ContainsTargetPath() const { return _containsTargetPath; } ++ size_t GetElementCount() const { return _elementCount; } ++ bool IsAbsolutePath() const { return _nodeFlags & IsAbsoluteFlag; } ++ bool IsAbsoluteRoot() const { return IsAbsolutePath() & (!_elementCount); } ++ bool ContainsTargetPath() const { ++ return _nodeFlags & ContainsTargetPathFlag; ++ } + bool IsNamespaced() const { +- return (_nodeType == PrimPropertyNode || +- _nodeType == RelationalAttributeNode) && _IsNamespacedImpl(); ++ // Bitwise-or to avoid branching in the node type comparisons, but ++ // logical and to avoid calling _IsNamespacedImpl() unless necessary. ++ return ((_nodeType == PrimPropertyNode) | ++ (_nodeType == RelationalAttributeNode)) && _IsNamespacedImpl(); + } + + bool ContainsPrimVariantSelection() const { +- return _containsPrimVariantSelection; ++ return _nodeFlags & ContainsPrimVarSelFlag; + } + + // For PrimNode, PrimPropertyNode, RelationalAttributeNode, and +@@ -230,7 +240,9 @@ public: + + // Return the current ref-count. + // Meant for diagnostic use. +- unsigned int GetCurrentRefCount() const { return _refCount; } ++ uint32_t GetCurrentRefCount() const { ++ return _refCount.load(std::memory_order_relaxed) & RefCountMask; ++ } + + protected: + Sdf_PathNode(Sdf_PathNode const *parent, NodeType nodeType) +@@ -238,22 +250,18 @@ protected: + , _refCount(1) + , _elementCount(parent ? parent->_elementCount + 1 : 1) + , _nodeType(nodeType) +- , _isAbsolute(parent && parent->IsAbsolutePath()) +- , _containsPrimVariantSelection( +- nodeType == PrimVariantSelectionNode || +- (parent && parent->_containsPrimVariantSelection)) +- , _containsTargetPath(nodeType == TargetNode || +- nodeType == MapperNode || +- (parent && parent->_containsTargetPath)) +- , _hasToken(false) +- {} ++ , _nodeFlags( ++ (parent ? parent->_nodeFlags : 0) | _NodeTypeToFlags(nodeType)) ++ { ++ } + + // This constructor is used only to create the two special root nodes. + explicit Sdf_PathNode(bool isAbsolute); + + ~Sdf_PathNode() { +- if (_hasToken) ++ if (_refCount.load(std::memory_order_relaxed) & HasTokenBit) { + _RemovePathTokenFromTable(); ++ } + } + + // Helper to downcast and destroy the dynamic type of this object -- this is +@@ -296,6 +304,16 @@ protected: + friend void intrusive_ptr_release(const Sdf_PathNode*); + + private: ++ static constexpr uint8_t _NodeTypeToFlags(NodeType nt) { ++ if (nt == PrimVariantSelectionNode) { ++ return ContainsPrimVarSelFlag; ++ } ++ if (nt == TargetNode || nt == MapperNode) { ++ return ContainsTargetPathFlag; ++ } ++ return 0; ++ } ++ + // Downcast helper, just sugar to static_cast this to Derived const *. + template + Derived const *_Downcast() const { +@@ -311,23 +329,15 @@ private: + // Instance variables. PathNode's size is important to keep small. Please + // be mindful of that when making any changes here. + const Sdf_PathNodeConstRefPtr _parent; +- mutable tbb::atomic _refCount; +- +- const short _elementCount; +- const unsigned char _nodeType; +- const bool _isAbsolute:1; +- const bool _containsPrimVariantSelection:1; +- const bool _containsTargetPath:1; +- +- // This is racy -- we ensure that the token creation code carefully +- // synchronizes so that if we read 'true' from this flag, it guarantees that +- // there's a token for this path node in the token table. If we read +- // 'false' it means there may or may not be, unless we're in the destructor, +- // which must run exclusively, then reading 'false' guarantees there is no +- // token in the table. We use this flag to do that optimization in the +- // destructor so we can avoid looking in the table in the case where we +- // haven't created a token. +- mutable bool _hasToken:1; ++ ++ // The high-order bit of _refCount (HasTokenBit) indicates whether or not ++ // we've created a token for this path node. ++ mutable std::atomic _refCount; ++ ++ const uint16_t _elementCount; ++ const NodeType _nodeType; ++ const uint8_t _nodeFlags; ++ + }; + + class Sdf_PrimPartPathNode : public Sdf_PathNode { +@@ -748,11 +758,12 @@ Sdf_PathNode::GetElement() const + SDF_API void Sdf_DumpPathStats(); + + inline void intrusive_ptr_add_ref(const PXR_NS::Sdf_PathNode* p) { +- ++p->_refCount; ++ p->_refCount.fetch_add(1, std::memory_order_relaxed); + } + inline void intrusive_ptr_release(const PXR_NS::Sdf_PathNode* p) { +- if (p->_refCount.fetch_and_decrement() == 1) ++ if ((p->_refCount.fetch_sub(1) & PXR_NS::Sdf_PathNode::RefCountMask) == 1) { + p->_Destroy(); ++ } + } + + PXR_NAMESPACE_CLOSE_SCOPE +-- +2.40.1 + + +From 957207c7a2024d3402ba2e5700b726e6d82ec12e Mon Sep 17 00:00:00 2001 +From: Alex Fuller +Date: Wed, 17 May 2023 16:27:47 +0200 +Subject: [PATCH 02/14] oneTBB: tbb::atomic to std::atomic in pcp + +--- + pxr/usd/pcp/mapExpression.cpp | 4 ++-- + pxr/usd/pcp/mapExpression.h | 3 +-- + pxr/usd/pcp/pch.h | 1 - + 3 files changed, 3 insertions(+), 5 deletions(-) + +diff --git a/pxr/usd/pcp/mapExpression.cpp b/pxr/usd/pcp/mapExpression.cpp +index 70e83df2f..b6bffda12 100644 +--- a/pxr/usd/pcp/mapExpression.cpp ++++ b/pxr/usd/pcp/mapExpression.cpp +@@ -238,7 +238,7 @@ PcpMapExpression::_Node::New( _Op op_, + // Check for existing instance to re-use + _NodeMap::accessor accessor; + if (_nodeRegistry->map.insert(accessor, key) || +- accessor->second->_refCount.fetch_and_increment() == 0) { ++ accessor->second->_refCount.fetch_add(1) == 0) { + // Either there was no node in the table, or there was but it had + // begun dying (another client dropped its refcount to 0). We have + // to create a new node in the table. When the client that is +@@ -388,7 +388,7 @@ intrusive_ptr_add_ref(PcpMapExpression::_Node* p) + void + intrusive_ptr_release(PcpMapExpression::_Node* p) + { +- if (p->_refCount.fetch_and_decrement() == 1) ++ if (p->_refCount.fetch_sub(1) == 1) + delete p; + } + +diff --git a/pxr/usd/pcp/mapExpression.h b/pxr/usd/pcp/mapExpression.h +index c91250a65..e61475ea5 100644 +--- a/pxr/usd/pcp/mapExpression.h ++++ b/pxr/usd/pcp/mapExpression.h +@@ -30,7 +30,6 @@ + + #include + +-#include + #include + + #include +@@ -265,7 +264,7 @@ private: // data + struct _NodeMap; + static TfStaticData<_NodeMap> _nodeRegistry; + +- mutable tbb::atomic _refCount; ++ mutable std::atomic _refCount; + mutable Value _cachedValue; + mutable std::set<_Node*> _dependentExpressions; + Value _valueForVariable; +diff --git a/pxr/usd/pcp/pch.h b/pxr/usd/pcp/pch.h +index a7180637d..220c3ef38 100644 +--- a/pxr/usd/pcp/pch.h ++++ b/pxr/usd/pcp/pch.h +@@ -194,7 +194,6 @@ + #include + #include + #include +-#include + #include + #include + #include +-- +2.40.1 + + +From 824456523ad4628e6806f74dfa2141aa08fac87b Mon Sep 17 00:00:00 2001 +From: Alex Fuller +Date: Wed, 17 May 2023 16:27:47 +0200 +Subject: [PATCH 03/14] oneTBB: tbb::atomic to std::atomic in sdf + +--- + pxr/usd/sdf/changeManager.cpp | 10 +++++----- + pxr/usd/sdf/layer.cpp | 2 +- + pxr/usd/sdf/layer.h | 2 +- + pxr/usd/sdf/pch.h | 1 - + 4 files changed, 7 insertions(+), 8 deletions(-) + +diff --git a/pxr/usd/sdf/changeManager.cpp b/pxr/usd/sdf/changeManager.cpp +index 9d55d29a3..00b909562 100644 +--- a/pxr/usd/sdf/changeManager.cpp ++++ b/pxr/usd/sdf/changeManager.cpp +@@ -34,7 +34,7 @@ + #include "pxr/base/tf/instantiateSingleton.h" + #include "pxr/base/tf/stackTrace.h" + +-#include ++#include + + using std::string; + using std::vector; +@@ -150,9 +150,9 @@ Sdf_ChangeManager::_ProcessRemoveIfInert(_Data *data) + TF_VERIFY(data->outermostBlock); + } + +-static tbb::atomic & ++static std::atomic & + _InitChangeSerialNumber() { +- static tbb::atomic value; ++ static std::atomic value; + value = 1; + return value; + } +@@ -191,8 +191,8 @@ Sdf_ChangeManager::_SendNotices(_Data *data) + } + + // Obtain a serial number for this round of change processing. +- static tbb::atomic &changeSerialNumber = _InitChangeSerialNumber(); +- size_t serialNumber = changeSerialNumber.fetch_and_increment(); ++ static std::atomic &changeSerialNumber = _InitChangeSerialNumber(); ++ size_t serialNumber = changeSerialNumber.fetch_add(1); + + // Send global notice. + SdfNotice::LayersDidChange(changes, serialNumber).Send(); +diff --git a/pxr/usd/sdf/layer.cpp b/pxr/usd/sdf/layer.cpp +index cc82b8ded..5b70a4e53 100644 +--- a/pxr/usd/sdf/layer.cpp ++++ b/pxr/usd/sdf/layer.cpp +@@ -214,7 +214,7 @@ SdfLayer::SdfLayer( + _MarkCurrentStateAsClean(); + } + +-SdfLayer::~SdfLayer() ++SdfLayer::~SdfLayer() noexcept + { + TF_PY_ALLOW_THREADS_IN_SCOPE(); + +diff --git a/pxr/usd/sdf/layer.h b/pxr/usd/sdf/layer.h +index f5afeaa55..3aea74bf7 100644 +--- a/pxr/usd/sdf/layer.h ++++ b/pxr/usd/sdf/layer.h +@@ -98,7 +98,7 @@ class SdfLayer + public: + /// Destructor + SDF_API +- virtual ~SdfLayer(); ++ virtual ~SdfLayer() noexcept; // noexcept needed for std::atomic member + + /// Noncopyable + SdfLayer(const SdfLayer&) = delete; +diff --git a/pxr/usd/sdf/pch.h b/pxr/usd/sdf/pch.h +index 0728ebe68..6af480e7e 100644 +--- a/pxr/usd/sdf/pch.h ++++ b/pxr/usd/sdf/pch.h +@@ -225,7 +225,6 @@ + #include + #include + #include +-#include + #include + #include + #include +-- +2.40.1 + + +From e4dbc9de9348c96270bee60046815522b2c02db2 Mon Sep 17 00:00:00 2001 +From: Alex Fuller +Date: Wed, 17 May 2023 16:27:47 +0200 +Subject: [PATCH 04/14] oneTBB: tbb::atomic to std::atomic in usdGeom + +--- + pxr/usd/usdGeom/bboxCache.cpp | 21 +++++++++++++++++---- + pxr/usd/usdGeom/pch.h | 1 - + 2 files changed, 17 insertions(+), 5 deletions(-) + +diff --git a/pxr/usd/usdGeom/bboxCache.cpp b/pxr/usd/usdGeom/bboxCache.cpp +index 447fe6790..21cf7aa2f 100644 +--- a/pxr/usd/usdGeom/bboxCache.cpp ++++ b/pxr/usd/usdGeom/bboxCache.cpp +@@ -46,6 +46,7 @@ + + #include + #include ++#include + + PXR_NAMESPACE_OPEN_SCOPE + +@@ -124,11 +125,24 @@ private: + + struct _PrototypeTask + { +- _PrototypeTask() : numDependencies(0) { } ++ _PrototypeTask() noexcept ++ : numDependencies(0) { } ++ ++ _PrototypeTask(const _PrototypeTask &other) noexcept ++ : dependentPrototypes(other.dependentPrototypes) ++ { ++ numDependencies.store(other.numDependencies.load()); ++ } ++ ++ _PrototypeTask(_PrototypeTask &&other) noexcept ++ : dependentPrototypes(std::move(other.dependentPrototypes)) ++ { ++ numDependencies.store(other.numDependencies.load()); ++ } + + // Number of dependencies -- prototype prims that must be resolved + // before this prototype can be resolved. +- tbb::atomic numDependencies; ++ std::atomic numDependencies; + + // List of prototype prims that depend on this prototype. + std::vector<_PrimContext> dependentPrototypes; +@@ -220,7 +234,7 @@ private: + _PrototypeTask& dependentPrototypeData = + prototypeTasks->find(dependentPrototype)->second; + if (dependentPrototypeData.numDependencies +- .fetch_and_decrement() == 1){ ++ .fetch_sub(1) == 1){ + dispatcher->Run( + &_PrototypeBBoxResolver::_ExecuteTaskForPrototype, + this, dependentPrototype, prototypeTasks, xfCaches, +@@ -1522,4 +1536,3 @@ UsdGeomBBoxCache::_PrimContext::ToString() const { + } + + PXR_NAMESPACE_CLOSE_SCOPE +- +diff --git a/pxr/usd/usdGeom/pch.h b/pxr/usd/usdGeom/pch.h +index 824c5b0f9..1a5fd6507 100644 +--- a/pxr/usd/usdGeom/pch.h ++++ b/pxr/usd/usdGeom/pch.h +@@ -181,7 +181,6 @@ + #include + #include + #include +-#include + #include + #include + #include +-- +2.40.1 + + +From 53991d66ed81c734825d507c4d4a44c6c36f5ed9 Mon Sep 17 00:00:00 2001 +From: Alex Fuller +Date: Wed, 17 May 2023 16:27:47 +0200 +Subject: [PATCH 05/14] oneTBB: tbb::atomic to std::atomic in usdImagining + +--- + pxr/usdImaging/plugin/usdShaders/pch.h | 1 - + pxr/usdImaging/usdAppUtils/pch.h | 1 - + pxr/usdImaging/usdImaging/pch.h | 1 - + .../usdImaging/resolvedAttributeCache.h | 24 +++++++++++++++---- + pxr/usdImaging/usdImagingGL/pch.h | 1 - + pxr/usdImaging/usdProcImaging/pch.h | 1 - + pxr/usdImaging/usdRiImaging/pch.h | 1 - + pxr/usdImaging/usdSkelImaging/pch.h | 1 - + pxr/usdImaging/usdVolImaging/pch.h | 1 - + pxr/usdImaging/usdviewq/pch.h | 1 - + 10 files changed, 19 insertions(+), 14 deletions(-) + +diff --git a/pxr/usdImaging/plugin/usdShaders/pch.h b/pxr/usdImaging/plugin/usdShaders/pch.h +index 2037e25b8..c5836624f 100644 +--- a/pxr/usdImaging/plugin/usdShaders/pch.h ++++ b/pxr/usdImaging/plugin/usdShaders/pch.h +@@ -160,7 +160,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usdImaging/usdAppUtils/pch.h b/pxr/usdImaging/usdAppUtils/pch.h +index 70b9602d5..f403f4500 100644 +--- a/pxr/usdImaging/usdAppUtils/pch.h ++++ b/pxr/usdImaging/usdAppUtils/pch.h +@@ -173,7 +173,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usdImaging/usdImaging/pch.h b/pxr/usdImaging/usdImaging/pch.h +index 35ea5620e..0419c53ef 100644 +--- a/pxr/usdImaging/usdImaging/pch.h ++++ b/pxr/usdImaging/usdImaging/pch.h +@@ -173,7 +173,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usdImaging/usdImaging/resolvedAttributeCache.h b/pxr/usdImaging/usdImaging/resolvedAttributeCache.h +index 8a91fde14..df8555f73 100644 +--- a/pxr/usdImaging/usdImaging/resolvedAttributeCache.h ++++ b/pxr/usdImaging/usdImaging/resolvedAttributeCache.h +@@ -285,22 +285,36 @@ private: + // non-time varying data, entries may exist in the cache with invalid + // values. The version is used to determine validity. + struct _Entry { +- _Entry() ++ _Entry() noexcept + : value(Strategy::MakeDefault()) + , version(_GetInitialEntryVersion()) + { } + + _Entry(const query_type & query_, + const value_type& value_, +- unsigned version_) ++ unsigned version_) noexcept + : query(query_) + , value(value_) + , version(version_) + { } + ++ _Entry(const _Entry &other) noexcept ++ : query(other.query) ++ , value(other.value) ++ { ++ version.store(other.version.load()); ++ } ++ ++ _Entry(_Entry &&other) noexcept ++ : query(std::move(other.query)) ++ , value(std::move(other.value)) ++ { ++ version.store(other.version.load()); ++ } ++ + query_type query; + value_type value; +- tbb::atomic version; ++ std::atomic version; + }; + + // Returns the version number for a valid cache entry +@@ -340,7 +354,7 @@ private: + + // A serial number indicating the valid state of entries in the cache. When + // an entry has an equal or greater value, the entry is valid. +- tbb::atomic _cacheVersion; ++ std::atomic _cacheVersion; + + // Value overrides for a set of descendents. + ValueOverridesMap _valueOverrides; +@@ -359,7 +373,7 @@ UsdImaging_ResolvedAttributeCache::_SetCacheEntryForPrim( + // Note: _cacheVersion is not allowed to change during cache access. + unsigned v = entry->version; + if (v < _cacheVersion +- && entry->version.compare_and_swap(_cacheVersion, v) == v) ++ && entry->version.compare_exchange_strong(v, _cacheVersion.load())) + { + entry->value = value; + entry->version = _GetValidVersion(); +diff --git a/pxr/usdImaging/usdImagingGL/pch.h b/pxr/usdImaging/usdImagingGL/pch.h +index b9dfa7e41..78b92909e 100644 +--- a/pxr/usdImaging/usdImagingGL/pch.h ++++ b/pxr/usdImaging/usdImagingGL/pch.h +@@ -186,7 +186,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usdImaging/usdProcImaging/pch.h b/pxr/usdImaging/usdProcImaging/pch.h +index 32358e284..95900985f 100644 +--- a/pxr/usdImaging/usdProcImaging/pch.h ++++ b/pxr/usdImaging/usdProcImaging/pch.h +@@ -161,7 +161,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usdImaging/usdRiImaging/pch.h b/pxr/usdImaging/usdRiImaging/pch.h +index 6ad2d403b..09a7a06cc 100644 +--- a/pxr/usdImaging/usdRiImaging/pch.h ++++ b/pxr/usdImaging/usdRiImaging/pch.h +@@ -169,7 +169,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usdImaging/usdSkelImaging/pch.h b/pxr/usdImaging/usdSkelImaging/pch.h +index 69caaac23..7086fc412 100644 +--- a/pxr/usdImaging/usdSkelImaging/pch.h ++++ b/pxr/usdImaging/usdSkelImaging/pch.h +@@ -169,7 +169,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usdImaging/usdVolImaging/pch.h b/pxr/usdImaging/usdVolImaging/pch.h +index 0fd54d571..b286bc759 100644 +--- a/pxr/usdImaging/usdVolImaging/pch.h ++++ b/pxr/usdImaging/usdVolImaging/pch.h +@@ -167,7 +167,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usdImaging/usdviewq/pch.h b/pxr/usdImaging/usdviewq/pch.h +index 2b6f4d782..d14b76bf7 100644 +--- a/pxr/usdImaging/usdviewq/pch.h ++++ b/pxr/usdImaging/usdviewq/pch.h +@@ -164,7 +164,6 @@ + #include + #include + #include +-#include + #include + #include + #include +-- +2.40.1 + + +From 9d7fb437c3f898030249a61ce5003a4d93bf5325 Mon Sep 17 00:00:00 2001 +From: Alex Fuller +Date: Wed, 17 May 2023 16:27:47 +0200 +Subject: [PATCH 06/14] oneTBB: remove tbb atomic includes in precompiled + headers + +--- + extras/usd/examples/usdObj/pch.h | 1 - + extras/usd/examples/usdSchemaExamples/pch.h | 1 - + pxr/base/plug/pch.h | 1 - + pxr/base/tf/pch.h | 1 - + pxr/base/trace/pch.h | 1 - + pxr/base/vt/pch.h | 1 - + pxr/base/work/pch.h | 1 - + pxr/imaging/garch/pch.h | 1 - + pxr/imaging/glf/pch.h | 1 - + pxr/imaging/hd/pch.h | 1 - + pxr/imaging/hdGp/pch.h | 1 - + pxr/imaging/hdMtlx/pch.h | 1 - + pxr/imaging/hdSt/pch.h | 1 - + pxr/imaging/hdar/pch.h | 1 - + pxr/imaging/hdsi/pch.h | 1 - + pxr/imaging/hdx/pch.h | 1 - + pxr/imaging/hgiMetal/pch.h | 1 - + pxr/imaging/plugin/hdEmbree/pch.h | 1 - + pxr/imaging/plugin/hdStorm/pch.h | 1 - + pxr/imaging/plugin/hioOiio/pch.h | 1 - + pxr/usd/ar/pch.h | 1 - + pxr/usd/ndr/pch.h | 1 - + pxr/usd/plugin/usdAbc/pch.h | 1 - + pxr/usd/plugin/usdDraco/pch.h | 1 - + pxr/usd/usd/pch.h | 1 - + pxr/usd/usdHydra/pch.h | 1 - + pxr/usd/usdLux/pch.h | 1 - + pxr/usd/usdMedia/pch.h | 1 - + pxr/usd/usdMtlx/pch.h | 1 - + pxr/usd/usdPhysics/pch.h | 1 - + pxr/usd/usdProc/pch.h | 1 - + pxr/usd/usdRender/pch.h | 1 - + pxr/usd/usdRi/pch.h | 1 - + pxr/usd/usdShade/pch.h | 1 - + pxr/usd/usdSkel/pch.h | 1 - + pxr/usd/usdUI/pch.h | 1 - + pxr/usd/usdUtils/pch.h | 1 - + pxr/usd/usdVol/pch.h | 1 - + 38 files changed, 38 deletions(-) + +diff --git a/extras/usd/examples/usdObj/pch.h b/extras/usd/examples/usdObj/pch.h +index 6a8744cbc..5e9527305 100644 +--- a/extras/usd/examples/usdObj/pch.h ++++ b/extras/usd/examples/usdObj/pch.h +@@ -166,7 +166,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/extras/usd/examples/usdSchemaExamples/pch.h b/extras/usd/examples/usdSchemaExamples/pch.h +index 47666439a..ef31dc083 100644 +--- a/extras/usd/examples/usdSchemaExamples/pch.h ++++ b/extras/usd/examples/usdSchemaExamples/pch.h +@@ -168,7 +168,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/base/plug/pch.h b/pxr/base/plug/pch.h +index 98b6fc782..64ffff9b9 100644 +--- a/pxr/base/plug/pch.h ++++ b/pxr/base/plug/pch.h +@@ -183,7 +183,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/base/tf/pch.h b/pxr/base/tf/pch.h +index 64e232c84..01781a6a0 100644 +--- a/pxr/base/tf/pch.h ++++ b/pxr/base/tf/pch.h +@@ -242,7 +242,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/base/trace/pch.h b/pxr/base/trace/pch.h +index 25f4b5d6c..e98e18308 100644 +--- a/pxr/base/trace/pch.h ++++ b/pxr/base/trace/pch.h +@@ -178,7 +178,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/base/vt/pch.h b/pxr/base/vt/pch.h +index 75f03bee2..3924c8f02 100644 +--- a/pxr/base/vt/pch.h ++++ b/pxr/base/vt/pch.h +@@ -171,7 +171,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/base/work/pch.h b/pxr/base/work/pch.h +index 228b18d0d..79030be79 100644 +--- a/pxr/base/work/pch.h ++++ b/pxr/base/work/pch.h +@@ -110,7 +110,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/imaging/garch/pch.h b/pxr/imaging/garch/pch.h +index 7ef3bd8af..7d13f5556 100644 +--- a/pxr/imaging/garch/pch.h ++++ b/pxr/imaging/garch/pch.h +@@ -145,7 +145,6 @@ + #include + #include + #include +-#include + #include + #include + #ifdef PXR_PYTHON_SUPPORT_ENABLED +diff --git a/pxr/imaging/glf/pch.h b/pxr/imaging/glf/pch.h +index 9a7e85a6c..9e396e34b 100644 +--- a/pxr/imaging/glf/pch.h ++++ b/pxr/imaging/glf/pch.h +@@ -199,7 +199,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/imaging/hd/pch.h b/pxr/imaging/hd/pch.h +index 604861b7b..fe94789a2 100644 +--- a/pxr/imaging/hd/pch.h ++++ b/pxr/imaging/hd/pch.h +@@ -152,7 +152,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/imaging/hdGp/pch.h b/pxr/imaging/hdGp/pch.h +index bf09080c2..ad94cf86e 100644 +--- a/pxr/imaging/hdGp/pch.h ++++ b/pxr/imaging/hdGp/pch.h +@@ -120,7 +120,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/imaging/hdMtlx/pch.h b/pxr/imaging/hdMtlx/pch.h +index bf8ff6a36..dd22c8c14 100644 +--- a/pxr/imaging/hdMtlx/pch.h ++++ b/pxr/imaging/hdMtlx/pch.h +@@ -139,7 +139,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/imaging/hdSt/pch.h b/pxr/imaging/hdSt/pch.h +index 196960f7b..d4377da1c 100644 +--- a/pxr/imaging/hdSt/pch.h ++++ b/pxr/imaging/hdSt/pch.h +@@ -170,7 +170,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/imaging/hdar/pch.h b/pxr/imaging/hdar/pch.h +index 56d867c1c..f34080f5c 100644 +--- a/pxr/imaging/hdar/pch.h ++++ b/pxr/imaging/hdar/pch.h +@@ -131,7 +131,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/imaging/hdsi/pch.h b/pxr/imaging/hdsi/pch.h +index 1662d7e8b..1f7c8223b 100644 +--- a/pxr/imaging/hdsi/pch.h ++++ b/pxr/imaging/hdsi/pch.h +@@ -119,7 +119,6 @@ + #include + #include + #include +-#include + #include + #ifdef PXR_PYTHON_SUPPORT_ENABLED + #include "pxr/base/tf/pySafePython.h" +diff --git a/pxr/imaging/hdx/pch.h b/pxr/imaging/hdx/pch.h +index 33ddb1b5d..8fcf02aac 100644 +--- a/pxr/imaging/hdx/pch.h ++++ b/pxr/imaging/hdx/pch.h +@@ -152,7 +152,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/imaging/hgiMetal/pch.h b/pxr/imaging/hgiMetal/pch.h +index 877bc45ee..dd9f067dd 100644 +--- a/pxr/imaging/hgiMetal/pch.h ++++ b/pxr/imaging/hgiMetal/pch.h +@@ -141,7 +141,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/imaging/plugin/hdEmbree/pch.h b/pxr/imaging/plugin/hdEmbree/pch.h +index 771022980..733d83b21 100644 +--- a/pxr/imaging/plugin/hdEmbree/pch.h ++++ b/pxr/imaging/plugin/hdEmbree/pch.h +@@ -154,7 +154,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/imaging/plugin/hdStorm/pch.h b/pxr/imaging/plugin/hdStorm/pch.h +index 33c5124a9..081338138 100644 +--- a/pxr/imaging/plugin/hdStorm/pch.h ++++ b/pxr/imaging/plugin/hdStorm/pch.h +@@ -141,7 +141,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/imaging/plugin/hioOiio/pch.h b/pxr/imaging/plugin/hioOiio/pch.h +index 9a7e85a6c..9e396e34b 100644 +--- a/pxr/imaging/plugin/hioOiio/pch.h ++++ b/pxr/imaging/plugin/hioOiio/pch.h +@@ -199,7 +199,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/ar/pch.h b/pxr/usd/ar/pch.h +index b3333376f..4f60d1b76 100644 +--- a/pxr/usd/ar/pch.h ++++ b/pxr/usd/ar/pch.h +@@ -166,7 +166,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/ndr/pch.h b/pxr/usd/ndr/pch.h +index 897ad796b..8aefd88ee 100644 +--- a/pxr/usd/ndr/pch.h ++++ b/pxr/usd/ndr/pch.h +@@ -198,7 +198,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/plugin/usdAbc/pch.h b/pxr/usd/plugin/usdAbc/pch.h +index f5c7e6fb6..33cc2cacb 100644 +--- a/pxr/usd/plugin/usdAbc/pch.h ++++ b/pxr/usd/plugin/usdAbc/pch.h +@@ -206,7 +206,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/plugin/usdDraco/pch.h b/pxr/usd/plugin/usdDraco/pch.h +index aff99924d..ce6d0d114 100644 +--- a/pxr/usd/plugin/usdDraco/pch.h ++++ b/pxr/usd/plugin/usdDraco/pch.h +@@ -169,7 +169,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usd/pch.h b/pxr/usd/usd/pch.h +index bd515aa43..96a656e80 100644 +--- a/pxr/usd/usd/pch.h ++++ b/pxr/usd/usd/pch.h +@@ -228,7 +228,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdHydra/pch.h b/pxr/usd/usdHydra/pch.h +index 5ba9df4c2..61ffecdf0 100644 +--- a/pxr/usd/usdHydra/pch.h ++++ b/pxr/usd/usdHydra/pch.h +@@ -162,7 +162,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdLux/pch.h b/pxr/usd/usdLux/pch.h +index 8fe34cb95..e503ef104 100644 +--- a/pxr/usd/usdLux/pch.h ++++ b/pxr/usd/usdLux/pch.h +@@ -177,7 +177,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdMedia/pch.h b/pxr/usd/usdMedia/pch.h +index 7802ec3e2..8388208cd 100644 +--- a/pxr/usd/usdMedia/pch.h ++++ b/pxr/usd/usdMedia/pch.h +@@ -170,7 +170,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdMtlx/pch.h b/pxr/usd/usdMtlx/pch.h +index 5eba44e7e..88f787238 100644 +--- a/pxr/usd/usdMtlx/pch.h ++++ b/pxr/usd/usdMtlx/pch.h +@@ -193,7 +193,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdPhysics/pch.h b/pxr/usd/usdPhysics/pch.h +index 824c5b0f9..1a5fd6507 100644 +--- a/pxr/usd/usdPhysics/pch.h ++++ b/pxr/usd/usdPhysics/pch.h +@@ -181,7 +181,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdProc/pch.h b/pxr/usd/usdProc/pch.h +index f40455a1c..fc95220bd 100644 +--- a/pxr/usd/usdProc/pch.h ++++ b/pxr/usd/usdProc/pch.h +@@ -165,7 +165,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdRender/pch.h b/pxr/usd/usdRender/pch.h +index 7802ec3e2..8388208cd 100644 +--- a/pxr/usd/usdRender/pch.h ++++ b/pxr/usd/usdRender/pch.h +@@ -170,7 +170,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdRi/pch.h b/pxr/usd/usdRi/pch.h +index 905d2a123..ba6c60988 100644 +--- a/pxr/usd/usdRi/pch.h ++++ b/pxr/usd/usdRi/pch.h +@@ -173,7 +173,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdShade/pch.h b/pxr/usd/usdShade/pch.h +index 698ab0ca6..8e0433d34 100644 +--- a/pxr/usd/usdShade/pch.h ++++ b/pxr/usd/usdShade/pch.h +@@ -179,7 +179,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdSkel/pch.h b/pxr/usd/usdSkel/pch.h +index a4205ac46..63975be9d 100644 +--- a/pxr/usd/usdSkel/pch.h ++++ b/pxr/usd/usdSkel/pch.h +@@ -180,7 +180,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdUI/pch.h b/pxr/usd/usdUI/pch.h +index 47666439a..ef31dc083 100644 +--- a/pxr/usd/usdUI/pch.h ++++ b/pxr/usd/usdUI/pch.h +@@ -168,7 +168,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdUtils/pch.h b/pxr/usd/usdUtils/pch.h +index 3a108ee5e..e76753154 100644 +--- a/pxr/usd/usdUtils/pch.h ++++ b/pxr/usd/usdUtils/pch.h +@@ -215,7 +215,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/pxr/usd/usdVol/pch.h b/pxr/usd/usdVol/pch.h +index 7802ec3e2..8388208cd 100644 +--- a/pxr/usd/usdVol/pch.h ++++ b/pxr/usd/usdVol/pch.h +@@ -170,7 +170,6 @@ + #include + #include + #include +-#include + #include + #include + #include +-- +2.40.1 + + +From 4f9601d4e969ac1e454b429507fc5f32cea36b0b Mon Sep 17 00:00:00 2001 +From: Brecht Van Lommel +Date: Thu, 18 May 2023 00:51:24 +0200 +Subject: [PATCH 07/14] oneTBB: change tbb::mutex to std::mutex + +--- + pxr/usd/usd/clipCache.cpp | 22 ++++++++++------------ + pxr/usd/usd/clipCache.h | 4 ++-- + pxr/usd/usd/instanceCache.h | 2 +- + 3 files changed, 13 insertions(+), 15 deletions(-) + +diff --git a/pxr/usd/usd/clipCache.cpp b/pxr/usd/usd/clipCache.cpp +index c582e1d8b..b8215378e 100644 +--- a/pxr/usd/usd/clipCache.cpp ++++ b/pxr/usd/usd/clipCache.cpp +@@ -218,10 +218,9 @@ Usd_ClipCache::PopulateClipsForPrim( + const bool primHasClips = !allClips.empty(); + if (primHasClips) { + TRACE_SCOPE("Usd_ClipCache::PopulateClipsForPrim (primHasClips)"); +- tbb::mutex::scoped_lock lock; +- if (_concurrentPopulationContext) { +- lock.acquire(_concurrentPopulationContext->_mutex); +- } ++ std::unique_lock lock = (_concurrentPopulationContext) ? ++ std::unique_lock(_concurrentPopulationContext->_mutex) : ++ std::unique_lock(); + + // Find nearest ancestor with clips specified. + const std::vector* ancestralClips = nullptr; +@@ -260,10 +259,10 @@ Usd_ClipCache::PopulateClipsForPrim( + SdfLayerHandleSet + Usd_ClipCache::GetUsedLayers() const + { +- tbb::mutex::scoped_lock lock; +- if (_concurrentPopulationContext) { +- lock.acquire(_concurrentPopulationContext->_mutex); +- } ++ std::unique_lock lock = (_concurrentPopulationContext) ? ++ std::unique_lock(_concurrentPopulationContext->_mutex) : ++ std::unique_lock(); ++ + SdfLayerHandleSet layers; + for (_ClipTable::iterator::value_type const &clipsListIter : _table){ + for (Usd_ClipSetRefPtr const &clipSet : clipsListIter.second){ +@@ -342,10 +341,9 @@ const std::vector& + Usd_ClipCache::GetClipsForPrim(const SdfPath& path) const + { + TRACE_FUNCTION(); +- tbb::mutex::scoped_lock lock; +- if (_concurrentPopulationContext) { +- lock.acquire(_concurrentPopulationContext->_mutex); +- } ++ std::unique_lock lock = (_concurrentPopulationContext) ? ++ std::unique_lock(_concurrentPopulationContext->_mutex) : ++ std::unique_lock(); + return _GetClipsForPrim_NoLock(path); + } + +diff --git a/pxr/usd/usd/clipCache.h b/pxr/usd/usd/clipCache.h +index 2bff0833a..fbe2ea72d 100644 +--- a/pxr/usd/usd/clipCache.h ++++ b/pxr/usd/usd/clipCache.h +@@ -30,7 +30,7 @@ + #include "pxr/usd/usd/clipSet.h" + #include "pxr/usd/sdf/pathTable.h" + +-#include ++#include + #include + + PXR_NAMESPACE_OPEN_SCOPE +@@ -61,7 +61,7 @@ public: + explicit ConcurrentPopulationContext(Usd_ClipCache &cache); + ~ConcurrentPopulationContext(); + Usd_ClipCache &_cache; +- tbb::mutex _mutex; ++ std::mutex _mutex; + }; + + /// Populate the cache with clips for \p prim. Returns true if clips +diff --git a/pxr/usd/usd/instanceCache.h b/pxr/usd/usd/instanceCache.h +index 0a25a7e30..4b7c948de 100644 +--- a/pxr/usd/usd/instanceCache.h ++++ b/pxr/usd/usd/instanceCache.h +@@ -30,7 +30,7 @@ + #include "pxr/usd/sdf/path.h" + #include "pxr/base/tf/hashmap.h" + +-#include ++#include + #include + #include + #include +-- +2.40.1 + + +From da2d2fe4db8eca9f92d40a93dcd95a9bb6d2128b Mon Sep 17 00:00:00 2001 +From: Brecht Van Lommel +Date: Thu, 18 May 2023 00:51:26 +0200 +Subject: [PATCH 08/14] oneTBB: change tbb::tbb_thread to std::thread + +--- + pxr/base/tf/testenv/error.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/pxr/base/tf/testenv/error.cpp b/pxr/base/tf/testenv/error.cpp +index e8eed44a4..606b2a943 100644 +--- a/pxr/base/tf/testenv/error.cpp ++++ b/pxr/base/tf/testenv/error.cpp +@@ -29,7 +29,7 @@ + + #include "pxr/base/arch/functionLite.h" + +-#include ++#include + + #define FILENAME "error.cpp" + +@@ -195,7 +195,7 @@ Test_TfErrorThreadTransport() + printf("Creating TfErrorMark\n"); + TfErrorMark m; + printf("Launching thread\n"); +- tbb::tbb_thread t([&transport]() { _ThreadTask(&transport); }); ++ std::thread t([&transport]() { _ThreadTask(&transport); }); + TF_AXIOM(m.IsClean()); + t.join(); + printf("Thread completed, posting error.\n"); +-- +2.40.1 + + +From 519c8308f2d4edcd65309cd51afa5c56f3752c27 Mon Sep 17 00:00:00 2001 +From: Brecht Van Lommel +Date: Thu, 18 May 2023 00:51:27 +0200 +Subject: [PATCH 09/14] oneTBB: explicitly specify hasher + +--- + pxr/usd/usd/crateFile.h | 17 ++++++++++------- + 1 file changed, 10 insertions(+), 7 deletions(-) + +diff --git a/pxr/usd/usd/crateFile.h b/pxr/usd/usd/crateFile.h +index 399367e6e..344f35722 100644 +--- a/pxr/usd/usd/crateFile.h ++++ b/pxr/usd/usd/crateFile.h +@@ -349,12 +349,15 @@ private: + bool operator!=(ZeroCopySource const &other) const { + return !(*this == other); + } +- friend size_t tbb_hasher(ZeroCopySource const &z) { +- return TfHash::Combine( +- reinterpret_cast(z._addr), +- z._numBytes +- ); +- } ++ ++ struct Hash { ++ inline size_t operator()(const ZeroCopySource& z) const { ++ return TfHash::Combine( ++ reinterpret_cast(z._addr), ++ z._numBytes ++ ); ++ } ++ }; + + // Return true if the refcount is nonzero. + bool IsInUse() const { return _refCount; } +@@ -422,7 +425,7 @@ private: + ArchConstFileMapping _mapping; + char const *_start; + int64_t _length; +- tbb::concurrent_unordered_set _outstandingRanges; ++ tbb::concurrent_unordered_set _outstandingRanges; + }; + + public: +-- +2.40.1 + + +From ad766aad56cc3304741c6377dbaafb044954298d Mon Sep 17 00:00:00 2001 +From: Brecht Van Lommel +Date: Thu, 18 May 2023 00:51:28 +0200 +Subject: [PATCH 10/14] oneTBB: replace TBB utility functions for placement new + +--- + pxr/base/trace/concurrentList.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/pxr/base/trace/concurrentList.h b/pxr/base/trace/concurrentList.h +index 0337933a3..01886a1b9 100644 +--- a/pxr/base/trace/concurrentList.h ++++ b/pxr/base/trace/concurrentList.h +@@ -111,7 +111,7 @@ public: + while (curNode) { + Node* nodeToDelete = curNode; + curNode = curNode->next; +- _alloc.destroy(nodeToDelete); ++ nodeToDelete->~Node(); + _alloc.deallocate(nodeToDelete, 1); + } + } +@@ -130,7 +130,7 @@ public: + /// the newly created item. + iterator Insert() { + Node* newNode = _alloc.allocate(1); +- _alloc.construct(newNode); ++ new(newNode) Node(); + + // Add the node to the linked list in an atomic manner. + do { +-- +2.40.1 + + +From 00be6ed878819cb552bd99118492f8ff45ebc677 Mon Sep 17 00:00:00 2001 +From: Brecht Van Lommel +Date: Thu, 18 May 2023 00:51:30 +0200 +Subject: [PATCH 11/14] oneTBB: support thread limits + +This now modifies global_control::max_allowed_parallelism instead of +task_scheduler_init, which leads to changes in API behavior. + +* Increasing number of threads beyond the number of cores now additionally + requires creating a task_arena with higher max concurrency. +* All application threads are affected when setting the concurrency limit, + not just the current thread. +* The winning call to set the number of threads may now be different due to + global_control always changing the number, while with task_scheduler_init + the first created instance determines the number of threads. + +Also, in the existing implementation task_scheduler_init is never freed, not on +shutdown or on changing the concurrently limit back to the default. This seems +unideal, but the new code does the same to keep the same behavior. +--- + .../work/testenv/testWorkThreadLimits.cpp | 59 ++++++++++++++----- + pxr/base/work/threadLimits.cpp | 43 ++++++++++++-- + 2 files changed, 83 insertions(+), 19 deletions(-) + +diff --git a/pxr/base/work/testenv/testWorkThreadLimits.cpp b/pxr/base/work/testenv/testWorkThreadLimits.cpp +index 414bba2c0..4c833199a 100644 +--- a/pxr/base/work/testenv/testWorkThreadLimits.cpp ++++ b/pxr/base/work/testenv/testWorkThreadLimits.cpp +@@ -38,6 +38,10 @@ + #include + #include + ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++#include ++#endif ++ + using namespace std::placeholders; + + PXR_NAMESPACE_USING_DIRECTIVE +@@ -56,16 +60,41 @@ _CountThreads(size_t begin, size_t end) + _uniqueThreads->insert(std::this_thread::get_id()); + } + ++static unsigned ++_GetConcurrencyLimit() ++{ ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++ // For oneTBB, get limit in an arena with max concurrency as ++ // WorkSetConcurrencyLimit by itself no longer increases the concurrency ++ // beyond the number of cores by itself. ++ unsigned limit; ++ tbb::task_arena arena(tbb::global_control::active_value(tbb::global_control::max_allowed_parallelism)); ++ arena.execute([&]() { ++ limit = WorkGetConcurrencyLimit(); ++ }); ++ return limit; ++#else ++ return WorkGetConcurrencyLimit(); ++#endif ++} ++ + static size_t + _ExpectedLimit(const int envVal, const size_t n) + { + // If envVal is non-zero, it wins over n! + // envVal may also be a negative number, which means all but that many + // cores. +- return envVal ? ++ const size_t val = envVal ? + (envVal < 0 ? + std::max(1, envVal+WorkGetPhysicalConcurrencyLimit()) : envVal) + : n; ++ ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++ // oneTBB has an internal limit of 256 + 1 threads. ++ return std::min(val, 257); ++#else ++ return val; ++#endif + } + + static void +@@ -101,41 +130,41 @@ _TestArguments(const int envVal) + // Set to maximum concurrency, which should remain within envVal. + const int numCores = WorkGetPhysicalConcurrencyLimit(); + WorkSetConcurrencyLimitArgument(numCores); +- TF_AXIOM(WorkGetConcurrencyLimit() == _ExpectedLimit(envVal, numCores)); ++ TF_AXIOM(_GetConcurrencyLimit() == _ExpectedLimit(envVal, numCores)); + + // n = 0, means "no change" + WorkSetConcurrencyLimitArgument(0); +- TF_AXIOM(WorkGetConcurrencyLimit() == _ExpectedLimit(envVal, numCores)); ++ TF_AXIOM(_GetConcurrencyLimit() == _ExpectedLimit(envVal, numCores)); + + // n = 1 means no threading + WorkSetConcurrencyLimitArgument(1); +- TF_AXIOM(WorkGetConcurrencyLimit() == _ExpectedLimit(envVal, 1)); ++ TF_AXIOM(_GetConcurrencyLimit() == _ExpectedLimit(envVal, 1)); + + // n = 3 means 3 + WorkSetConcurrencyLimitArgument(3); +- TF_AXIOM(WorkGetConcurrencyLimit() == _ExpectedLimit(envVal, 3)); ++ TF_AXIOM(_GetConcurrencyLimit() == _ExpectedLimit(envVal, 3)); + + // n = 1000 means 1000 + WorkSetConcurrencyLimitArgument(1000); +- TF_AXIOM(WorkGetConcurrencyLimit() == _ExpectedLimit(envVal, 1000)); ++ TF_AXIOM(_GetConcurrencyLimit() == _ExpectedLimit(envVal, 1000)); + + // n = -1 means numCores - 1, with a minimum of 1 + WorkSetConcurrencyLimitArgument(-1); +- TF_AXIOM(WorkGetConcurrencyLimit() == ++ TF_AXIOM(_GetConcurrencyLimit() == + _ExpectedLimit(envVal, std::max(1, numCores-1))); + + // n = -3 means numCores - 3, with a minimum of 1 + WorkSetConcurrencyLimitArgument(-3); +- TF_AXIOM(WorkGetConcurrencyLimit() == ++ TF_AXIOM(_GetConcurrencyLimit() == + _ExpectedLimit(envVal, std::max(1, numCores-3))); + + // n = -numCores means 1 (no threading) + WorkSetConcurrencyLimitArgument(-numCores); +- TF_AXIOM(WorkGetConcurrencyLimit() == _ExpectedLimit(envVal, 1)); ++ TF_AXIOM(_GetConcurrencyLimit() == _ExpectedLimit(envVal, 1)); + + // n = -numCores*10 means 1 (no threading) + WorkSetConcurrencyLimitArgument(-numCores*10); +- TF_AXIOM(WorkGetConcurrencyLimit() == _ExpectedLimit(envVal, 1)); ++ TF_AXIOM(_GetConcurrencyLimit() == _ExpectedLimit(envVal, 1)); + } + + struct _RawTBBCounter +@@ -218,35 +247,35 @@ main(int argc, char **argv) + // Test with full concurrency. + std::cout << "Testing full concurrency...\n"; + WorkSetMaximumConcurrencyLimit(); +- TF_AXIOM(WorkGetConcurrencyLimit() == ++ TF_AXIOM(_GetConcurrencyLimit() == + _ExpectedLimit(envVal, WorkGetPhysicalConcurrencyLimit())); + _TestThreadLimit(envVal, WorkGetPhysicalConcurrencyLimit()); + + // Test with no concurrency. + std::cout << "Testing turning off concurrency...\n"; + WorkSetConcurrencyLimit(1); +- TF_AXIOM(WorkGetConcurrencyLimit() == ++ TF_AXIOM(_GetConcurrencyLimit() == + _ExpectedLimit(envVal, 1)); + _TestThreadLimit(envVal, 1); + + // Test with 2 threads. + std::cout << "Testing with 2 threads...\n"; + WorkSetConcurrencyLimit(2); +- TF_AXIOM(WorkGetConcurrencyLimit() == ++ TF_AXIOM(_GetConcurrencyLimit() == + _ExpectedLimit(envVal, 2)); + _TestThreadLimit(envVal, 2); + + // Test with 4 threads. + std::cout << "Testing with 4 threads...\n"; + WorkSetConcurrencyLimit(4); +- TF_AXIOM(WorkGetConcurrencyLimit() == ++ TF_AXIOM(_GetConcurrencyLimit() == + _ExpectedLimit(envVal, 4)); + _TestThreadLimit(envVal, 4); + + // Test with 1000 threads. + std::cout << "Testing with 1000 threads...\n"; + WorkSetConcurrencyLimit(1000); +- TF_AXIOM(WorkGetConcurrencyLimit() == ++ TF_AXIOM(_GetConcurrencyLimit() == + _ExpectedLimit(envVal, 1000)); + _TestThreadLimit(envVal, 1000); + +diff --git a/pxr/base/work/threadLimits.cpp b/pxr/base/work/threadLimits.cpp +index bc629b812..ad6bae8ae 100644 +--- a/pxr/base/work/threadLimits.cpp ++++ b/pxr/base/work/threadLimits.cpp +@@ -29,9 +29,18 @@ + + #include "pxr/base/tf/envSetting.h" + +-#include ++// Blocked range is not used in this file, but this header happens to pull in ++// the TBB version header in a way that works in all TBB versions. ++#include + #include + ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++#include ++#include ++#else ++#include ++#endif ++ + #include + #include + +@@ -58,16 +67,25 @@ TF_DEFINE_ENV_SETTING( + + PXR_NAMESPACE_OPEN_SCOPE + +-// We create a task_scheduler_init instance at static initialization time if +-// PXR_WORK_THREAD_LIMIT is set to a nonzero value. Otherwise this stays NULL. +-static tbb::task_scheduler_init *_tbbTaskSchedInit; ++// We create a global_control or task_scheduler_init instance at static ++// initialization time if PXR_WORK_THREAD_LIMIT is set to a nonzero value. ++// Otherwise this stays NULL. ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++static tbb::global_control *_tbbGlobalControl = nullptr; ++#else ++static tbb::task_scheduler_init *_tbbTaskSchedInit = nullptr; ++#endif + + unsigned + WorkGetPhysicalConcurrencyLimit() + { + // Use TBB here, since it pays attention to the affinity mask on Linux and + // Windows. ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++ return tbb::info::default_concurrency(); ++#else + return tbb::task_scheduler_init::default_num_threads(); ++#endif + } + + // This function always returns an actual thread count >= 1. +@@ -123,7 +141,11 @@ Work_InitializeThreading() + // previously initialized by the hosting environment (e.g. if we are running + // as a plugin to another application.) + if (settingVal) { ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++ _tbbGlobalControl = new tbb::global_control(tbb::global_control::max_allowed_parallelism, threadLimit); ++#else + _tbbTaskSchedInit = new tbb::task_scheduler_init(threadLimit); ++#endif + } + } + static int _forceInitialization = (Work_InitializeThreading(), 0); +@@ -153,6 +175,11 @@ WorkSetConcurrencyLimit(unsigned n) + threadLimit = WorkGetConcurrencyLimit(); + } + ++ ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++ delete _tbbGlobalControl; ++ _tbbGlobalControl = new tbb::global_control(tbb::global_control::max_allowed_parallelism, threadLimit); ++#else + // Note that we need to do some performance testing and decide if it's + // better here to simply delete the task_scheduler_init object instead + // of re-initializing it. If we decide that it's better to re-initialize +@@ -168,6 +195,7 @@ WorkSetConcurrencyLimit(unsigned n) + } else { + _tbbTaskSchedInit = new tbb::task_scheduler_init(threadLimit); + } ++#endif + } + + void +@@ -185,7 +213,14 @@ WorkSetConcurrencyLimitArgument(int n) + unsigned + WorkGetConcurrencyLimit() + { ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++ // The effective concurrency requires taking into account both the ++ // task_arena and internal thread pool size set by global_control. ++ // https://github.com/oneapi-src/oneTBB/issues/405 ++ return std::min(tbb::global_control::active_value(tbb::global_control::max_allowed_parallelism), tbb::this_task_arena::max_concurrency()); ++#else + return tbb::this_task_arena::max_concurrency(); ++#endif + } + + bool +-- +2.40.1 + + +From 162e5ed55151f739615144622647e3ce28655efb Mon Sep 17 00:00:00 2001 +From: Brecht Van Lommel +Date: Thu, 18 May 2023 00:51:31 +0200 +Subject: [PATCH 12/14] oneTBB: support work dispatcher + +To make concurrent wait thread safe this is accessing the internals of TBB, +since the TBB wait implementation has a comment saying it is not thread safe. +--- + pxr/base/work/dispatcher.cpp | 23 ++++++++++++-- + pxr/base/work/dispatcher.h | 61 +++++++++++++++++++++++++++++++++--- + 2 files changed, 77 insertions(+), 7 deletions(-) + +diff --git a/pxr/base/work/dispatcher.cpp b/pxr/base/work/dispatcher.cpp +index adba7dff3..66ca5181a 100644 +--- a/pxr/base/work/dispatcher.cpp ++++ b/pxr/base/work/dispatcher.cpp +@@ -32,27 +32,42 @@ WorkDispatcher::WorkDispatcher() + tbb::task_group_context::isolated, + tbb::task_group_context::concurrent_wait | + tbb::task_group_context::default_traits) ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++ , _taskGroup(_context) ++#endif + { + _waitCleanupFlag.clear(); +- ++ ++#if TBB_INTERFACE_VERSION_MAJOR < 12 + // The concurrent_wait flag used with the task_group_context ensures + // the ref count will remain at 1 after all predecessor tasks are + // completed, so we don't need to keep resetting it in Wait(). + _rootTask = new(tbb::task::allocate_root(_context)) tbb::empty_task; + _rootTask->set_ref_count(1); ++#endif + } + +-WorkDispatcher::~WorkDispatcher() ++WorkDispatcher::~WorkDispatcher() noexcept + { + Wait(); ++ ++#if TBB_INTERFACE_VERSION_MAJOR < 12 + tbb::task::destroy(*_rootTask); ++#endif + } + + void + WorkDispatcher::Wait() + { + // Wait for tasks to complete. ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++ // The native task_group::wait() has a comment saying its call to the ++ // context reset method is not thread safe. So we bypass that implementation ++ // and do our own synchronization to ensure it is called once. ++ tbb::detail::d1::wait(_taskGroup.get_internal_wait_context(), _context); ++#else + _rootTask->wait_for_all(); ++#endif + + // If we take the flag from false -> true, we do the cleanup. + if (_waitCleanupFlag.test_and_set() == false) { +@@ -73,7 +88,11 @@ WorkDispatcher::Wait() + void + WorkDispatcher::Cancel() + { ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++ _taskGroup.cancel(); ++#else + _context.cancel_group_execution(); ++#endif + } + + /* static */ +diff --git a/pxr/base/work/dispatcher.h b/pxr/base/work/dispatcher.h +index 2c499d6ab..62eb7132d 100644 +--- a/pxr/base/work/dispatcher.h ++++ b/pxr/base/work/dispatcher.h +@@ -33,8 +33,15 @@ + #include "pxr/base/tf/errorMark.h" + #include "pxr/base/tf/errorTransport.h" + ++// Blocked range is not used in this file, but this header happens to pull in ++// the TBB version header in a way that works in all TBB versions. ++#include + #include ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++#include ++#else + #include ++#endif + + #include + #include +@@ -79,7 +86,7 @@ public: + WORK_API WorkDispatcher(); + + /// Wait() for any pending tasks to complete, then destroy the dispatcher. +- WORK_API ~WorkDispatcher(); ++ WORK_API ~WorkDispatcher() noexcept; // noexcept needed for tbb::task_group + + WorkDispatcher(WorkDispatcher const &) = delete; + WorkDispatcher &operator=(WorkDispatcher const &) = delete; +@@ -103,7 +110,11 @@ public: + + template + inline void Run(Callable &&c) { ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++ _taskGroup.run(std::move(_InvokerTask::type>(std::move(c), &_errors))); ++#else + _rootTask->spawn(_MakeInvokerTask(std::forward(c))); ++#endif + } + + template +@@ -136,12 +147,38 @@ private: + // Function invoker helper that wraps the invocation with an ErrorMark so we + // can transmit errors that occur back to the thread that Wait() s for tasks + // to complete. ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 + template +- struct _InvokerTask : public tbb::task { ++ struct _InvokerTask { + explicit _InvokerTask(Fn &&fn, _ErrorTransports *err) +- : _fn(std::move(fn)), _errors(err) {} ++ : _fn(std::make_unique(std::move(fn))), _errors(err) {} + + explicit _InvokerTask(Fn const &fn, _ErrorTransports *err) ++ : _fn(std::make_unique(std::move(fn))), _errors(err) {} ++ ++ // Ensure only moves happen, no copies or assignments. ++ _InvokerTask(_InvokerTask &&other) = default; ++ _InvokerTask(const _InvokerTask &other) = delete; ++ _InvokerTask &operator=(const _InvokerTask &other) = delete; ++ _InvokerTask &operator=(_InvokerTask &&other) = delete; ++ ++ void operator()() const { ++ TfErrorMark m; ++ (*_fn)(); ++ if (!m.IsClean()) ++ WorkDispatcher::_TransportErrors(m, _errors); ++ } ++ private: ++ std::unique_ptr _fn; ++ _ErrorTransports *_errors; ++ }; ++#else ++ template ++ struct _InvokerTask : public tbb::task { ++ explicit _InvokerTask(Fn &&fn, _ErrorTransports *err) ++ : _fn(std::move(fn)), _errors(err) {} ++ ++ explicit _InvokerTask(Fn const &fn, _ErrorTransports *err) + : _fn(fn), _errors(err) {} + + virtual tbb::task* execute() { +@@ -164,16 +201,30 @@ private: + _InvokerTask::type>( + std::forward(fn), &_errors); + } ++#endif + + // Helper function that removes errors from \p m and stores them in a new + // entry in \p errors. + WORK_API static void + _TransportErrors(const TfErrorMark &m, _ErrorTransports *errors); + +- // Task group context and associated root task that allows us to cancel +- // tasks invoked directly by this dispatcher. ++ // Task group context to run tasks in. + tbb::task_group_context _context; ++#if TBB_INTERFACE_VERSION_MAJOR >= 12 ++ // Custom task group that lets us implement thread safe concurrent wait. ++ class _TaskGroup : public tbb::task_group { ++ public: ++ _TaskGroup(tbb::task_group_context& ctx) : tbb::task_group(ctx) {} ++ tbb::detail::d1::wait_context& get_internal_wait_context() { ++ return m_wait_ctx; ++ } ++ }; ++ ++ _TaskGroup _taskGroup; ++#else ++ // Root task that allows us to cancel tasks invoked directly by this dispatcher. + tbb::empty_task* _rootTask; ++#endif + + // The error transports we use to transmit errors in other threads back to + // this thread. +-- +2.40.1 + + +From 54227d077b8e2bb7c9290fa7887950a30e8a02b1 Mon Sep 17 00:00:00 2001 +From: Brecht Van Lommel +Date: Thu, 18 May 2023 00:51:32 +0200 +Subject: [PATCH 13/14] Add --onetbb option to build_usd.py, to build with TBB + 2021 + +And update FindTBB to support it. This also changes OpenVDB 7 to 10 when +using oneTBB since earlier versions do not support it. +--- + build_scripts/build_usd.py | 35 ++++++++++++++++++++++++++++------- + cmake/modules/FindTBB.cmake | 7 ++++++- + 2 files changed, 34 insertions(+), 8 deletions(-) + +diff --git a/build_scripts/build_usd.py b/build_scripts/build_usd.py +index 80acf4aa3..33dca5af3 100644 +--- a/build_scripts/build_usd.py ++++ b/build_scripts/build_usd.py +@@ -934,13 +934,19 @@ elif MacOS(): + else: + TBB_URL = "https://github.com/oneapi-src/oneTBB/archive/refs/tags/2019_U6.zip" + ++TBB_2021_URL = "https://github.com/oneapi-src/oneTBB/archive/refs/tags/v2021.9.0.zip" ++ + def InstallTBB(context, force, buildArgs): +- if Windows(): +- InstallTBB_Windows(context, force, buildArgs) +- elif MacOS(): +- InstallTBB_MacOS(context, force, buildArgs) ++ if context.tbbVersion == "2021": ++ with CurrentWorkingDirectory(DownloadURL(TBB_2021_URL, context, force)): ++ RunCMake(context, force, buildArgs) + else: +- InstallTBB_Linux(context, force, buildArgs) ++ if Windows(): ++ InstallTBB_Windows(context, force, buildArgs) ++ elif MacOS(): ++ InstallTBB_MacOS(context, force, buildArgs) ++ else: ++ InstallTBB_Linux(context, force, buildArgs) + + def InstallTBB_Windows(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(TBB_URL, context, force, +@@ -1240,10 +1246,13 @@ BLOSC = Dependency("Blosc", InstallBLOSC, "include/blosc.h") + ############################################################ + # OpenVDB + +-OPENVDB_URL = "https://github.com/AcademySoftwareFoundation/openvdb/archive/refs/tags/v7.1.0.zip" ++OPENVDB_7_URL = "https://github.com/AcademySoftwareFoundation/openvdb/archive/refs/tags/v7.1.0.zip" ++OPENVDB_10_URL = "https://github.com/AcademySoftwareFoundation/openvdb/archive/refs/tags/v10.0.1.zip" + + def InstallOpenVDB(context, force, buildArgs): +- with CurrentWorkingDirectory(DownloadURL(OPENVDB_URL, context, force)): ++ # oneTBB requires new OpenVDB ++ openvdb_url = OPENVDB_10_URL if context.tbbVersion == "2021" else OPENVDB_7_URL ++ with CurrentWorkingDirectory(DownloadURL(openvdb_url, context, force)): + extraArgs = [ + '-DOPENVDB_BUILD_PYTHON_MODULE=OFF', + '-DOPENVDB_BUILD_BINARIES=OFF', +@@ -2036,6 +2045,13 @@ subgroup.add_argument("--no-openvdb", dest="enable_openvdb", + action="store_false", + help="Disable OpenVDB support in imaging (default)") + subgroup = group.add_mutually_exclusive_group() ++subgroup.add_argument("--onetbb", dest="enable_onetbb", action="store_true", ++ default=False, ++ help="Use new oneAPI TBB version") ++subgroup.add_argument("--no-onetbb", dest="enable_onetbb", ++ action="store_false", ++ help="Use old TBB version (default)") ++subgroup = group.add_mutually_exclusive_group() + subgroup.add_argument("--usdview", dest="build_usdview", + action="store_true", default=True, + help="Build usdview (default)") +@@ -2207,6 +2223,9 @@ class InstallContext: + self.buildTutorials = args.build_tutorials + self.buildTools = args.build_tools + ++ # - TBB ++ self.tbbVersion = "2021" if args.enable_onetbb else "2019" ++ + # - Imaging + self.buildImaging = (args.build_imaging == IMAGING or + args.build_imaging == USD_IMAGING) +@@ -2481,6 +2500,7 @@ summaryMsg += """\ + Python Debug: {debugPython} + Python 3: {enablePython3} + Python docs: {buildPythonDocs} ++ TBB version: {tbbVersion} + Documentation {buildDocs} + Tests {buildTests} + Examples {buildExamples} +@@ -2543,6 +2563,7 @@ summaryMsg = summaryMsg.format( + debugPython=("On" if context.debugPython else "Off"), + enablePython3=("On" if Python3() else "Off"), + buildPythonDocs=("On" if context.buildPythonDocs else "Off"), ++ tbbVersion=context.tbbVersion, + buildDocs=("On" if context.buildDocs else "Off"), + buildTests=("On" if context.buildTests else "Off"), + buildExamples=("On" if context.buildExamples else "Off"), +diff --git a/cmake/modules/FindTBB.cmake b/cmake/modules/FindTBB.cmake +index 9bf69a022..49c09d290 100644 +--- a/cmake/modules/FindTBB.cmake ++++ b/cmake/modules/FindTBB.cmake +@@ -197,7 +197,12 @@ if(NOT TBB_FOUND) + ################################## + + if(TBB_INCLUDE_DIRS) +- file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file) ++ # Use new oneTBB version header if it exists. ++ if(EXISTS "${TBB_INCLUDE_DIRS}/tbb/version.h") ++ file(READ "${TBB_INCLUDE_DIRS}/tbb/version.h" _tbb_version_file) ++ else() ++ file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file) ++ endif() + string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" + TBB_VERSION_MAJOR "${_tbb_version_file}") + string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" +-- +2.40.1 + + +From 09f55987a3773c8e3cfa0bdbc7f4ba8097ef56af Mon Sep 17 00:00:00 2001 +From: Michael Migliore +Date: Wed, 14 Jun 2023 23:38:15 +0200 +Subject: [PATCH 14/14] oneAPI: fix remaining issues + +--- + cmake/modules/FindTBB.cmake | 10 ++++++---- + pxr/base/work/pch.h | 1 - + 2 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/cmake/modules/FindTBB.cmake b/cmake/modules/FindTBB.cmake +index 49c09d290..686ff7d1a 100644 +--- a/cmake/modules/FindTBB.cmake ++++ b/cmake/modules/FindTBB.cmake +@@ -198,8 +198,8 @@ if(NOT TBB_FOUND) + + if(TBB_INCLUDE_DIRS) + # Use new oneTBB version header if it exists. +- if(EXISTS "${TBB_INCLUDE_DIRS}/tbb/version.h") +- file(READ "${TBB_INCLUDE_DIRS}/tbb/version.h" _tbb_version_file) ++ if(EXISTS "${TBB_INCLUDE_DIRS}/oneapi/tbb/version.h") ++ file(READ "${TBB_INCLUDE_DIRS}/oneapi/tbb/version.h" _tbb_version_file) + else() + file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file) + endif() +@@ -227,12 +227,14 @@ if(NOT TBB_FOUND) + if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};") + + # Search for the libraries +- find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp} ++ find_library(TBB_${_comp}_LIBRARY_RELEASE ++ NAMES ${_comp}12 ${_comp} + HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} + PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH + PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) + +- find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}_debug ++ find_library(TBB_${_comp}_LIBRARY_DEBUG ++ NAMES ${_comp}12_debug ${_comp}_debug + HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} + PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH + PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) +diff --git a/pxr/base/work/pch.h b/pxr/base/work/pch.h +index 79030be79..2f2e069a5 100644 +--- a/pxr/base/work/pch.h ++++ b/pxr/base/work/pch.h +@@ -120,4 +120,3 @@ + #include + #include + #include +-#include +-- +2.40.1 + diff --git a/.github/actions/usd-install-dep/action.yml b/.github/actions/usd-install-dep/action.yml new file mode 100644 index 0000000000..1b87e413fb --- /dev/null +++ b/.github/actions/usd-install-dep/action.yml @@ -0,0 +1,74 @@ +name: 'Install USD Dependency' +description: 'Install USD Dependency using cache when possible' +inputs: + cpu: + description: 'CPU architecture to build for' + required: false + default: 'x86_64' + +runs: + using: "composite" + steps: + + - name: Cache USD + id: cache-usd + uses: actions/cache@v3 + with: + path: dependencies/usd_install + key: usd-v23.05-${{runner.os}}-${{inputs.cpu}}-0 + + - name: Checkout USD + if: steps.cache-usd.outputs.cache-hit != 'true' + uses: actions/checkout@v3 + with: + repository: PixarAnimationStudios/USD + path: './dependencies/usd' + ref: v23.05 + + # This patch is coming from https://gist.github.com/Meakk/c1d5e25c0e4d3dfffb5f776b2f93663b + # Related issue: https://github.com/PixarAnimationStudios/OpenUSD/issues/1471#issuecomment-1587463831 + - name: Patch USD oneTBB + if: steps.cache-usd.outputs.cache-hit != 'true' + working-directory: ${{github.workspace}}/dependencies/usd + shell: bash + run: patch -p1 < $GITHUB_ACTION_PATH/0001-usd-onetbb.patch + + - name: Setup USD + if: steps.cache-usd.outputs.cache-hit != 'true' + working-directory: ${{github.workspace}}/dependencies + shell: bash + run: | + mkdir usd_build + mkdir usd_install + + - name: Configure USD + if: steps.cache-usd.outputs.cache-hit != 'true' + working-directory: ${{github.workspace}}/dependencies/usd_build + shell: bash + run: > + cmake ../usd + -DPXR_BUILD_ALEMBIC_PLUGIN:BOOL=OFF + -DPXR_BUILD_EMBREE_PLUGIN:BOOL=OFF + -DPXR_BUILD_IMAGING:BOOL=OFF + -DPXR_BUILD_MONOLITHIC:BOOL=OFF + -DPXR_BUILD_TESTS:BOOL=OFF + -DPXR_BUILD_USD_IMAGING:BOOL=OFF + -DPXR_ENABLE_PYTHON_SUPPORT:BOOL=OFF + -DPXR_BUILD_EXAMPLES:BOOL=OFF + -DPXR_BUILD_TUTORIALS:BOOL=OFF + -DPXR_BUILD_USD_TOOLS:BOOL=OFF + -DCMAKE_BUILD_TYPE=Release + -DCMAKE_INSTALL_PREFIX:PATH=$(pwd)/../usd_install + -DCMAKE_OSX_ARCHITECTURES=${{ inputs.cpu }} + -DCMAKE_PREFIX_PATH:PATH=../install/ + + - name: Build USD + if: steps.cache-usd.outputs.cache-hit != 'true' + working-directory: ${{github.workspace}}/dependencies/usd_build + shell: bash + run: cmake --build . --parallel 2 --target install --config Release + + - name: Copy to install + working-directory: ${{github.workspace}}/dependencies/usd_install + shell: bash + run: cp -r ./* ../install/ diff --git a/application/testing/CMakeLists.txt b/application/testing/CMakeLists.txt index 0d98765ead..d20353737d 100644 --- a/application/testing/CMakeLists.txt +++ b/application/testing/CMakeLists.txt @@ -553,6 +553,25 @@ if(F3D_PLUGIN_BUILD_OCCT) endif() endif() +if(F3D_PLUGIN_BUILD_USD) + f3d_test(NAME TestUSD DATA suzanne.usd ARGS --load-plugins=usd --up=+Z DEFAULT_LIGHTS) + f3d_test(NAME TestUSDAPrimitives DATA primitives.usda ARGS --load-plugins=usd DEFAULT_LIGHTS) + f3d_test(NAME TestUSDAPrimitivesZAxis DATA primitivesZ.usda ARGS --load-plugins=usd DEFAULT_LIGHTS) + f3d_test(NAME TestUSDAInstancing DATA instancing.usda ARGS --load-plugins=usd DEFAULT_LIGHTS) + f3d_test(NAME TestUSDAGlyphs DATA glyphs.usda ARGS --load-plugins=usd DEFAULT_LIGHTS) + f3d_test(NAME TestUSDInvalid DATA invalid.usd REGEXP "Failed to open layer" ARGS --load-plugins=usd NO_BASELINE) + + # https://gitlab.kitware.com/vtk/vtk/-/merge_requests/8224 + if(VTK_VERSION VERSION_GREATER_EQUAL 9.0.20210805) # for embedded material support + f3d_test(NAME TestUSDZAnimated DATA AnimatedCube.usdz ARGS --load-plugins=usd --animation-time=0.3) + f3d_test(NAME TestUSDZAnimatedStop DATA AnimatedCube.usdz ARGS --load-plugins=usd --animation-speed-factor=0.01 INTERACTION)#Space;Wait;Space; + + # The threshold is quite high because of macos + # However, the PNSR is >30db which is acceptable + f3d_test(NAME TestUSDZMaterials DATA McUsd.usdz ARGS --load-plugins=usd --camera-position=1055,912,-247 --camera-focal-point=69,173,63 THRESHOLD 280) + endif() +endif() + if(F3D_PLUGIN_BUILD_ALEMBIC AND F3D_PLUGIN_BUILD_ASSIMP) f3d_test(NAME TestMultiplePluginsLoad DATA cow.vtp ARGS --load-plugins=assimp,alembic NO_BASELINE REGEXP_FAIL "Plugin failed to load") endif() diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index c175d34be8..cfe9168d3d 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -3,6 +3,7 @@ option(F3D_PLUGIN_BUILD_ASSIMP "Assimp plugin (FBX, OFF, DAE anf DXF files)" OFF option(F3D_PLUGIN_BUILD_DRACO "Draco plugin (DRC files)" OFF) option(F3D_PLUGIN_BUILD_EXODUS "ExodusII plugin (EX2 files)" ON) option(F3D_PLUGIN_BUILD_OCCT "OpenCASCADE plugin (STEP and IGES files)" OFF) +option(F3D_PLUGIN_BUILD_USD "Universal Scene Description plugin (USD files)" OFF) add_subdirectory(native) @@ -25,3 +26,7 @@ endif() if (F3D_PLUGIN_BUILD_OCCT) add_subdirectory(occt) endif() + +if (F3D_PLUGIN_BUILD_USD) + add_subdirectory(usd) +endif() diff --git a/plugins/usd/CMakeLists.txt b/plugins/usd/CMakeLists.txt new file mode 100644 index 0000000000..8853d88793 --- /dev/null +++ b/plugins/usd/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.19) + +project(f3d-plugin-usd) + +include(GNUInstallDirs) + +# Check if the plugin is built externally +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + find_package(f3d REQUIRED) +else() + include(${CMAKE_SOURCE_DIR}/cmake/f3dPlugin.cmake) +endif() + +find_package(pxr REQUIRED) + +message(STATUS "Plugin: USD found") + +f3d_plugin_init() + +f3d_plugin_declare_reader( + NAME USD + EXTENSIONS usd usdc usda usdz + MIMETYPES application/vnd.usd application/vnd.usdc model/vnd.usda model/vnd.usdz+zip + VTK_IMPORTER vtkF3DUSDImporter + FORMAT_DESCRIPTION "Universal Scene Descriptor" +) + +set(rpaths "") +get_target_property(target_type usdGeom TYPE) +if (target_type STREQUAL SHARED_LIBRARY) + list(APPEND rpaths "$") +endif () + +f3d_plugin_build( + NAME usd + VERSION 1.0 + DESCRIPTION "USD support" + VTK_MODULES IOImage + ADDITIONAL_RPATHS ${rpaths} + MIMETYPE_XML_FILES "${CMAKE_CURRENT_SOURCE_DIR}/f3d-usd-formats.xml" + CONFIGURATION_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/configs/config.d" "${CMAKE_CURRENT_SOURCE_DIR}/configs/thumbnail.d" + FREEDESKTOP +) diff --git a/plugins/usd/configs/config.d/10_usd.json b/plugins/usd/configs/config.d/10_usd.json new file mode 100644 index 0000000000..794a2c0337 --- /dev/null +++ b/plugins/usd/configs/config.d/10_usd.json @@ -0,0 +1,6 @@ +{ + ".*(usd)[acz]?": + { + "load-plugins": "usd" + } +} diff --git a/plugins/usd/configs/thumbnail.d/10_usd.json b/plugins/usd/configs/thumbnail.d/10_usd.json new file mode 100644 index 0000000000..794a2c0337 --- /dev/null +++ b/plugins/usd/configs/thumbnail.d/10_usd.json @@ -0,0 +1,6 @@ +{ + ".*(usd)[acz]?": + { + "load-plugins": "usd" + } +} diff --git a/plugins/usd/f3d-usd-formats.xml b/plugins/usd/f3d-usd-formats.xml new file mode 100644 index 0000000000..60d1fd8e69 --- /dev/null +++ b/plugins/usd/f3d-usd-formats.xml @@ -0,0 +1,19 @@ + + + + USD + + + + USD Binary + + + + USD ASCII + + + + Compressed USD + + + diff --git a/plugins/usd/module/CMakeLists.txt b/plugins/usd/module/CMakeLists.txt new file mode 100644 index 0000000000..9d684401fc --- /dev/null +++ b/plugins/usd/module/CMakeLists.txt @@ -0,0 +1,11 @@ +set(classes + vtkF3DUSDImporter + ) + +vtk_module_add_module(f3d::VTKExtensionsUSDReader + FORCE_STATIC + CLASSES ${classes}) + +vtk_module_link(f3d::VTKExtensionsUSDReader PRIVATE ${PXR_LIBRARIES}) + +vtk_module_set_properties(f3d::VTKExtensionsUSDReader CXX_STANDARD 17) diff --git a/plugins/usd/module/Testing/CMakeLists.txt b/plugins/usd/module/Testing/CMakeLists.txt new file mode 100644 index 0000000000..f7e6b7519d --- /dev/null +++ b/plugins/usd/module/Testing/CMakeLists.txt @@ -0,0 +1,13 @@ +list(APPEND VTKExtensionsPluginUSD_list + TestF3DUSDImporter.cxx + ) + +if(VTK_VERSION VERSION_LESS_EQUAL 9.1.0) + cmake_policy(SET CMP0115 OLD) +endif() + +vtk_add_test_cxx(VTKExtensionsPluginUSD tests + NO_DATA NO_VALID NO_OUTPUT + ${VTKExtensionsPluginUSD_list} + ${CMAKE_SOURCE_DIR}/testing/ ${CMAKE_BINARY_DIR}/Testing/Temporary/) +vtk_test_cxx_executable(VTKExtensionsPluginUSD tests) diff --git a/plugins/usd/module/Testing/TestF3DUSDImporter.cxx b/plugins/usd/module/Testing/TestF3DUSDImporter.cxx new file mode 100644 index 0000000000..365386dfdf --- /dev/null +++ b/plugins/usd/module/Testing/TestF3DUSDImporter.cxx @@ -0,0 +1,16 @@ +#include +#include + +#include "vtkF3DUSDImporter.h" + +#include + +int TestF3DUSDImporter(int vtkNotUsed(argc), char* argv[]) +{ + std::string filename = std::string(argv[1]) + "data/suzanne.usd"; + vtkNew importer; + importer->SetFileName(filename); + importer->Update(); + importer->Print(cout); + return importer->GetRenderer() ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/plugins/usd/module/vtk.module b/plugins/usd/module/vtk.module new file mode 100644 index 0000000000..42fca7a441 --- /dev/null +++ b/plugins/usd/module/vtk.module @@ -0,0 +1,12 @@ +NAME + f3d::VTKExtensionsUSDReader +DESCRIPTION + A VTK module for the USD plugin +DEPENDS + VTK::CommonCore + VTK::FiltersGeneral + VTK::FiltersSources + VTK::IOImport + VTK::IOImage +TEST_DEPENDS + VTK::TestingCore diff --git a/plugins/usd/module/vtkF3DUSDImporter.cxx b/plugins/usd/module/vtkF3DUSDImporter.cxx new file mode 100644 index 0000000000..224ba27ba5 --- /dev/null +++ b/plugins/usd/module/vtkF3DUSDImporter.cxx @@ -0,0 +1,966 @@ +#include "vtkF3DUSDImporter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push, 0) +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +class vtkF3DUSDImporter::vtkInternals +{ +public: + void ReadScene(const std::string& filePath) + { + // in case of failure, you may want to set PXR_PLUGINPATH_NAME to the lib/usd path + this->Stage = pxr::UsdStage::Open(filePath); + } + + template + std::pair GetConnectedShaderPrim(const T& port) + { + if (port) + { + pxr::UsdShadeConnectableAPI api; + pxr::UsdShadeAttributeType type; + pxr::TfToken token; + port.GetConnectedSource(&api, &token, &type); + + if (api && api.GetPrim().IsA()) + { + return { pxr::UsdShadeShader(api.GetPrim()), token }; + } + } + return { pxr::UsdShadeShader(), pxr::TfToken() }; + } + + vtkSmartPointer ConvertMatrix(const pxr::GfMatrix4d& uMatrix) + { + vtkNew mat; + + std::copy(uMatrix.data(), uMatrix.data() + 16, mat->GetData()); + mat->Transpose(); + + return mat; + } + + vtkSmartPointer GetLocalTransform( + const pxr::UsdGeomImageable& node, pxr::UsdTimeCode timeCode) + { + // get xform + return this->ConvertMatrix(node.ComputeLocalToWorldTransform(timeCode)); + } + + void ImportNode(vtkRenderer* renderer, const pxr::UsdPrim& node, const pxr::SdfPath& path, + vtkMatrix4x4* currentMatrix) + { + pxr::UsdTimeCode timeCode = this->CurrentTime * this->Stage->GetTimeCodesPerSecond(); + + // simple range-for iteration + for (pxr::UsdPrim prim : pxr::UsdPrimSiblingRange(node.GetAllChildren())) + { + if (prim.IsInstance()) + { + pxr::UsdGeomXform xform = pxr::UsdGeomXform(prim); + + auto mat = this->GetLocalTransform(xform, timeCode); + + this->ImportNode(renderer, prim.GetPrototype(), path.AppendChild(prim.GetName()), mat); + } + else if (prim.IsA()) + { + pxr::UsdGeomPointInstancer glyphs = pxr::UsdGeomPointInstancer(prim); + + // Ideally, we should use the 3D glyph mapper, but it's left for future work + + pxr::VtMatrix4dArray xforms; + + if (glyphs.ComputeInstanceTransformsAtTime(&xforms, timeCode, timeCode)) + { + int i = 0; + for (const pxr::GfMatrix4d& currInstMatrix : xforms) + { + auto mat = this->ConvertMatrix(currInstMatrix); + vtkMatrix4x4::Multiply4x4(currentMatrix, mat, mat); + + pxr::TfToken tok(std::string("instance_") + std::to_string(i++)); + + this->ImportNode( + renderer, prim, path.AppendChild(prim.GetName()).AppendChild(tok), mat); + } + } + } + else if (prim.IsA()) + { + pxr::UsdGeomGprim geomPrim = pxr::UsdGeomGprim(prim); + + vtkSmartPointer polydata; + + if (prim.IsA()) + { + pxr::UsdGeomMesh meshPrim = pxr::UsdGeomMesh(prim); + + polydata = this->MeshMap[meshPrim.GetPath().GetAsString()]; + bool meshAlreadyExists = (polydata != nullptr); + + if (!meshAlreadyExists) + { + polydata = vtkSmartPointer::New(); + } + + // read points + pxr::UsdAttribute pointsAttr = meshPrim.GetPointsAttr(); + + if (!meshAlreadyExists || pointsAttr.ValueMightBeTimeVarying()) + { + pxr::VtArray positions; + pointsAttr.Get(&positions, timeCode); + + vtkNew points; + points->Allocate(positions.size()); + for (const pxr::GfVec3f& p : positions) + { + points->InsertNextPoint(p[0], p[1], p[2]); + } + + polydata->SetPoints(points); + } + + // read normals + pxr::UsdAttribute normalsAttr = meshPrim.GetNormalsAttr(); + + if (meshPrim.GetNormalsInterpolation() == pxr::TfToken("vertex")) + { + // only vertex interpolation is supported for now + // there are many datasets using faceVarying to represent sharp edges + // but it is not supported by VTK so points have to be duplicated manually + if (!meshAlreadyExists || normalsAttr.ValueMightBeTimeVarying()) + { + pxr::VtArray normals; + normalsAttr.Get(&normals, timeCode); + + vtkNew vNormals; + vNormals->SetName("Normals"); + vNormals->SetNumberOfComponents(3); + vNormals->Allocate(normals.size()); + + for (const pxr::GfVec3f& n : normals) + { + vNormals->InsertNextTuple3(n[0], n[1], n[2]); + } + + polydata->GetPointData()->SetNormals(vNormals); + } + } + + // read uvs + pxr::UsdGeomPrimvar uvAttr = + pxr::UsdGeomPrimvarsAPI(meshPrim).GetPrimvar(pxr::TfToken("st")); + + if (!meshAlreadyExists || uvAttr.ValueMightBeTimeVarying()) + { + pxr::VtArray uvs; + uvAttr.Get(&uvs, timeCode); + + if (uvs.size() > 0) + { + vtkNew texCoords; + texCoords->SetName("TCoords"); + texCoords->SetNumberOfComponents(2); + texCoords->Allocate(uvs.size()); + + for (const pxr::GfVec2f& uv : uvs) + { + texCoords->InsertNextTuple2(uv[0], uv[1]); + } + + polydata->GetPointData()->SetTCoords(texCoords); + } + } + + // read polys + pxr::UsdAttribute facesCountAttr = meshPrim.GetFaceVertexCountsAttr(); + pxr::UsdAttribute facesIndicesAttr = meshPrim.GetFaceVertexIndicesAttr(); + + if (!meshAlreadyExists || facesCountAttr.ValueMightBeTimeVarying() || + facesIndicesAttr.ValueMightBeTimeVarying()) + { + pxr::VtArray counts; + facesCountAttr.Get(&counts, timeCode); + + pxr::VtArray indices; + facesIndicesAttr.Get(&indices, timeCode); + + // add polygons + vtkNew cells; + auto currentCellIt = indices.cbegin(); + std::vector indexArr; + for (int c : counts) + { + indexArr.clear(); + indexArr.insert(indexArr.begin(), currentCellIt, std::next(currentCellIt, c)); + cells->InsertNextCell(c, indexArr.data()); + std::advance(currentCellIt, c); + } + + polydata->SetPolys(cells); + } + } + else if (prim.IsA()) + { + pxr::UsdGeomSphere spherePrim = pxr::UsdGeomSphere(prim); + + vtkNew sphere; + sphere->SetThetaResolution(20); + sphere->SetPhiResolution(20); + + double radius; + if (spherePrim.GetRadiusAttr().Get(&radius)) + { + sphere->SetRadius(radius); + } + + sphere->Update(); + polydata = sphere->GetOutput(); + } + else if (prim.IsA()) + { + pxr::UsdGeomCube cubePrim = pxr::UsdGeomCube(prim); + + vtkNew cube; + + double length; + if (cubePrim.GetSizeAttr().Get(&length)) + { + cube->SetXLength(length); + cube->SetYLength(length); + cube->SetZLength(length); + } + + cube->Update(); + polydata = cube->GetOutput(); + } + else if (prim.IsA()) + { + pxr::UsdGeomCapsule capsulePrim = pxr::UsdGeomCapsule(prim); + +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20230921) + vtkNew capsule; + capsule->CapsuleCapOn(); + + double height; + if (capsulePrim.GetHeightAttr().Get(&height)) + { + capsule->SetHeight(height); + } +#else + vtkNew capsule; + + double height; + if (capsulePrim.GetHeightAttr().Get(&height)) + { + capsule->SetCylinderLength(height); + } +#endif + + double radius; + if (capsulePrim.GetRadiusAttr().Get(&radius)) + { + capsule->SetRadius(radius); + } + + // In VTK, the capsule is aligned with the Y axis + // In USD, the default is aligned with Z, but can be modified + // Let's rotate it if needed + vtkNew transform; + vtkNew t; + transform->SetTransform(t); + + pxr::TfToken axisToken("Z"); + capsulePrim.GetAxisAttr().Get(&axisToken); + + if (axisToken == pxr::TfToken("X")) + { + t->RotateZ(90.0); + } + else if (axisToken == pxr::TfToken("Z")) + { + t->RotateX(90.0); + } + + transform->SetInputConnection(capsule->GetOutputPort()); + transform->Update(); + polydata = vtkPolyData::SafeDownCast(transform->GetOutput()); + } + else if (prim.IsA()) + { + pxr::UsdGeomCylinder cylinderPrim = pxr::UsdGeomCylinder(prim); + vtkNew cylinder; + cylinder->SetResolution(20); + + double height; + if (cylinderPrim.GetHeightAttr().Get(&height)) + { + cylinder->SetHeight(height); + } + + double radius; + if (cylinderPrim.GetRadiusAttr().Get(&radius)) + { + cylinder->SetRadius(radius); + } + + // In VTK, the cylinder is aligned with the Y axis + // In USD, the default is aligned with Z, but can be modified + // Let's rotate it if needed + vtkNew transform; + vtkNew t; + transform->SetTransform(t); + + pxr::TfToken axisToken("Z"); + cylinderPrim.GetAxisAttr().Get(&axisToken); + + if (axisToken == pxr::TfToken("X")) + { + t->RotateZ(90.0); + } + else if (axisToken == pxr::TfToken("Z")) + { + t->RotateX(90.0); + } + + transform->SetInputConnection(cylinder->GetOutputPort()); + transform->Update(); + polydata = vtkPolyData::SafeDownCast(transform->GetOutput()); + } + else if (prim.IsA()) + { + pxr::UsdGeomCone conePrim = pxr::UsdGeomCone(prim); + vtkNew cone; + cone->SetResolution(20); + + double height; + if (conePrim.GetHeightAttr().Get(&height)) + { + cone->SetHeight(height); + } + + double radius; + if (conePrim.GetRadiusAttr().Get(&radius)) + { + cone->SetRadius(radius); + } + + // In VTK, the cylinder is aligned with the X axis + // In USD, the default is aligned with Z, but can be modified + // Let's rotate it if needed + vtkNew transform; + vtkNew t; + transform->SetTransform(t); + + pxr::TfToken axisToken("Z"); + conePrim.GetAxisAttr().Get(&axisToken); + + if (axisToken == pxr::TfToken("Y")) + { + t->RotateZ(90.0); + } + else if (axisToken == pxr::TfToken("Z")) + { + t->RotateY(90.0); + } + + transform->SetInputConnection(cone->GetOutputPort()); + transform->Update(); + polydata = vtkPolyData::SafeDownCast(transform->GetOutput()); + } + + // create actor + pxr::SdfPath actorPath = path.AppendChild(prim.GetName()); + + auto& actor = this->ActorMap[actorPath.GetAsString()]; + bool actorAlreadyExists = (actor != nullptr); + + if (!actorAlreadyExists) + { + actor = vtkSmartPointer::New(); + + // get associated material/shader + pxr::UsdShadeMaterial material = + pxr::UsdShadeMaterialBindingAPI(geomPrim).ComputeBoundMaterial(); + + if (material) + { + auto [shaderPrim, token] = this->GetConnectedShaderPrim(material.GetSurfaceOutput()); + + auto [prop, isTranslucent] = this->GetVTKProperty(shaderPrim); + actor->SetProperty(prop); + if (isTranslucent) + { + actor->ForceTranslucentOn(); + } + } + else + { + // if there is no material, fallback on display color + pxr::UsdAttribute displayColorAttr = geomPrim.GetDisplayColorAttr(); + + vtkNew prop; + prop->SetInterpolationToPBR(); + + pxr::VtArray color; + if (displayColorAttr.Get(&color) && color.size() == 1) + { + prop->SetColor(color[0][0], color[0][1], color[0][2]); + } + + actor->SetProperty(prop); + } + + // set mapper + vtkNew mapper; + + if (actor->GetProperty()->GetTexture("normalTex")) + { + vtkNew triangulate; + triangulate->SetInputData(polydata); + + vtkNew tangents; + tangents->SetInputConnection(triangulate->GetOutputPort()); + tangents->Update(); + mapper->SetInputData(tangents->GetOutput()); + } + else + { + mapper->SetInputData(polydata); + } + + if (!this->HasTimeCode()) + { + mapper->StaticOn(); + } + + actor->SetMapper(mapper); + + renderer->AddActor(actor); + } + + // get xform + auto mat = this->GetLocalTransform(geomPrim, timeCode); + vtkMatrix4x4::Multiply4x4(currentMatrix, mat, mat); + actor->SetUserMatrix(mat); + } + else + { + // just traverse the node + this->ImportNode(renderer, prim, path.AppendChild(prim.GetName()), currentMatrix); + } + } + } + + void ImportRoot(vtkRenderer* renderer) + { + if (!this->Stage) + { + vtkErrorWithObjectMacro(renderer, << "Stage failed to open"); + return; + } + + // TODO: USD bake skinning is not performant + // We need to read joints and do the skinning in the shader + pxr::UsdSkelBakeSkinning(this->Stage->Traverse()); + this->Stage->Save(); + + vtkNew identity; + this->ImportNode(renderer, this->Stage->GetPseudoRoot(), pxr::SdfPath("/"), identity); + } + + vtkSmartPointer CombineORMImage( + vtkImageData* occlusionImage, vtkImageData* roughnessImage, vtkImageData* metallicImage) + { + if (!occlusionImage && !roughnessImage && !metallicImage) + { + return nullptr; + } + + int maxWidth = 0; + int maxHeight = 0; + + if (occlusionImage) + { + int* size = occlusionImage->GetDimensions(); + + maxWidth = std::max(maxWidth, size[0]); + maxHeight = std::max(maxHeight, size[1]); + } + + if (roughnessImage) + { + int* size = roughnessImage->GetDimensions(); + + maxWidth = std::max(maxWidth, size[0]); + maxHeight = std::max(maxHeight, size[1]); + } + + if (metallicImage) + { + int* size = metallicImage->GetDimensions(); + + maxWidth = std::max(maxWidth, size[0]); + maxHeight = std::max(maxHeight, size[1]); + } + + auto ResizeAndExtractChannel = [&](vtkImageData* img) -> vtkSmartPointer { + if (!img) + { + vtkNew emptyImage; + emptyImage->SetDimensions(maxWidth, maxHeight, 1); + emptyImage->AllocateScalars(VTK_UNSIGNED_CHAR, 1); + unsigned char* data = static_cast(emptyImage->GetScalarPointer()); + std::fill(data, data + maxWidth * maxHeight, 0xff); + return emptyImage; + } + + // The image should already be a single channel image, but just in case + vtkNew extract; + extract->SetInputData(img); + extract->SetComponents(0); + + vtkNew resize; + resize->SetInputConnection(extract->GetOutputPort()); + resize->SetOutputDimensions(maxWidth, maxHeight, 1); + resize->Update(); + return resize->GetOutput(); + }; + + vtkNew appendChannels; + appendChannels->SetInputData(ResizeAndExtractChannel(occlusionImage)); + appendChannels->AddInputData(ResizeAndExtractChannel(roughnessImage)); + appendChannels->AddInputData(ResizeAndExtractChannel(metallicImage)); + appendChannels->Update(); + + return appendChannels->GetOutput(); + } + + vtkSmartPointer CombineColorOpacityImage( + vtkImageData* colorImage, vtkImageData* opacityImage) + { + if (!opacityImage) + { + return colorImage; + } + + vtkNew appendChannels; + appendChannels->SetInputData(colorImage); + appendChannels->AddInputData(opacityImage); + appendChannels->Update(); + + return appendChannels->GetOutput(); + } + + vtkSmartPointer GetVTKTexture( + const pxr::UsdShadeShader& samplerPrim, const pxr::TfToken& token) + { + if (!samplerPrim) + { + return nullptr; + } + + auto& tex = this->TextureMap[samplerPrim.GetPath().GetAsString()]; + + if (tex == nullptr) + { + pxr::TfToken idToken; + bool defined = samplerPrim.GetIdAttr().Get(&idToken); + if (!defined || idToken != pxr::TfToken("UsdUVTexture")) + { + // only pxr::UsdUVTexture supported for now + return nullptr; + } + + pxr::SdfAssetPath path; + if (samplerPrim.GetInput(pxr::TfToken("file")).Get(&path)) + { + vtkSmartPointer reader; + +// CreateImageReader2FromExtension needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/8211 +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 0, 20210729) + const std::string& assetPath = path.GetAssetPath(); + std::string ext = assetPath.substr(assetPath.find_last_of('.')); + reader.TakeReference(vtkImageReader2Factory::CreateImageReader2FromExtension(ext.c_str())); +#endif + + if (!reader) + { + // cannot read the image file + return nullptr; + } + + const std::string& resolvedPath = path.GetResolvedPath(); + auto asset = pxr::ArGetResolver().OpenAsset(pxr::ArResolvedPath(resolvedPath)); + + if (!asset) + { + // cannot get USD asset + return nullptr; + } + + auto buffer = asset->GetBuffer(); + + if (!buffer) + { + // buffer invalid + return nullptr; + } + + reader->SetMemoryBuffer(buffer.get()); + reader->SetMemoryBufferLength(asset->GetSize()); + reader->Update(); + + tex = reader->GetOutput(); + } + } + + // extract component based on token + vtkNew extract; + extract->SetInputData(tex); + + const std::string& channels = token.GetString(); + + if (channels == "rgb") + { + extract->SetComponents(0, 1, 2); + } + else if (channels == "r") + { + extract->SetComponents(0); + } + else if (channels == "g") + { + extract->SetComponents(1); + } + else if (channels == "b") + { + extract->SetComponents(2); + } + else if (channels == "a") + { + extract->SetComponents(3); + } + else + { + // just return the image as is + return tex; + } + + extract->Update(); + return extract->GetOutput(); + } + + std::pair, bool> GetVTKProperty( + const pxr::UsdShadeShader& shaderPrim) + { + auto& prop = this->ShaderMap[shaderPrim.GetPath().GetAsString()]; + + bool isTranslucent = false; + + if (prop == nullptr) + { + pxr::UsdAttribute attr = shaderPrim.GetIdAttr(); + + if (attr) + { + pxr::TfToken materialToken; + + bool defined = attr.Get(&materialToken); + + if (!defined || materialToken != pxr::TfToken("UsdPreviewSurface")) + { + // only pxr::UsdPreviewSurface supported for now + return { nullptr, isTranslucent }; + } + + prop = vtkSmartPointer::New(); + prop->SetInterpolationToPBR(); + + // diffuseColor + pxr::GfVec3f diffuseColorValue; + pxr::UsdShadeInput diffuseColor = shaderPrim.GetInput(pxr::TfToken("diffuseColor")); + if (diffuseColor && diffuseColor.Get(&diffuseColorValue)) + { + prop->SetColor(diffuseColorValue[0], diffuseColorValue[1], diffuseColorValue[2]); + } + + auto [diffuseColorSampler, colorToken] = this->GetConnectedShaderPrim(diffuseColor); + vtkSmartPointer diffuseColorImage; + if (diffuseColorSampler) + { + diffuseColorImage = this->GetVTKTexture(diffuseColorSampler, colorToken); + } + + // opacity + float opacityValue; + pxr::UsdShadeInput opacity = shaderPrim.GetInput(pxr::TfToken("opacity")); + if (opacity && opacity.Get(&opacityValue)) + { + prop->SetOpacity(opacityValue); + } + + auto [opacitySampler, opacityToken] = this->GetConnectedShaderPrim(opacity); + vtkSmartPointer opacityImage; + if (opacitySampler) + { + opacityImage = this->GetVTKTexture(opacitySampler, opacityToken); + isTranslucent = true; + } + + auto baseColor = this->CombineColorOpacityImage(diffuseColorImage, opacityImage); + if (baseColor) + { + vtkNew texture; + texture->SetInputData(baseColor); + + texture->MipmapOn(); + texture->InterpolateOn(); + texture->SetColorModeToDirectScalars(); + + texture->UseSRGBColorSpaceOn(); + + prop->SetBaseColorTexture(texture); + } + + // emissive + pxr::UsdShadeInput emissive = shaderPrim.GetInput(pxr::TfToken("emissiveColor")); + auto [emissiveSampler, emissiveToken] = this->GetConnectedShaderPrim(emissive); + vtkSmartPointer emissiveImage; + if (emissiveSampler) + { + emissiveImage = this->GetVTKTexture(emissiveSampler, emissiveToken); + if (emissiveImage) + { + vtkNew texture; + texture->SetInputData(emissiveImage); + + texture->MipmapOn(); + texture->InterpolateOn(); + texture->SetColorModeToDirectScalars(); + + texture->UseSRGBColorSpaceOn(); + + prop->SetEmissiveTexture(texture); + } + } + + // ORM texture + float roughnessValue; + pxr::UsdShadeInput roughness = shaderPrim.GetInput(pxr::TfToken("roughness")); + if (roughness && roughness.Get(&roughnessValue)) + { + prop->SetRoughness(roughnessValue); + } + + auto [roughnessSampler, roughnessToken] = this->GetConnectedShaderPrim(roughness); + vtkSmartPointer roughnessImage; + if (roughnessSampler) + { + prop->SetRoughness(1.0); + roughnessImage = this->GetVTKTexture(roughnessSampler, roughnessToken); + } + + float metallicValue; + pxr::UsdShadeInput metallic = shaderPrim.GetInput(pxr::TfToken("metallic")); + if (metallic && metallic.Get(&metallicValue)) + { + prop->SetMetallic(metallicValue); + } + + auto [metallicSampler, metallicToken] = this->GetConnectedShaderPrim(metallic); + vtkSmartPointer metallicImage; + if (metallicSampler) + { + prop->SetMetallic(1.0); + metallicImage = this->GetVTKTexture(metallicSampler, metallicToken); + } + + pxr::UsdShadeInput occlusion = shaderPrim.GetInput(pxr::TfToken("occlusion")); + auto [occlusionSampler, occlusionToken] = this->GetConnectedShaderPrim(occlusion); + vtkSmartPointer occlusionImage; + if (occlusionSampler) + { + occlusionImage = this->GetVTKTexture(occlusionSampler, occlusionToken); + } + + auto orm = this->CombineORMImage(occlusionImage, roughnessImage, metallicImage); + + if (orm) + { + vtkNew texture; + texture->SetInputData(orm); + + texture->MipmapOn(); + texture->InterpolateOn(); + texture->SetColorModeToDirectScalars(); + + prop->SetORMTexture(texture); + } + + // normal + pxr::UsdShadeInput normal = shaderPrim.GetInput(pxr::TfToken("normal")); + auto [normalSampler, normalToken] = this->GetConnectedShaderPrim(normal); + if (normalSampler) + { + auto img = this->GetVTKTexture(normalSampler, normalToken); + + if (img) + { + vtkNew texture; + texture->SetInputData(img); + + texture->MipmapOn(); + texture->InterpolateOn(); + texture->SetColorModeToDirectScalars(); + + prop->SetNormalTexture(texture); + } + } + } + } + + return { prop, isTranslucent }; + } + + bool HasTimeCode() { return this->Stage ? this->Stage->HasAuthoredTimeCodeRange() : false; } + + void SetCurrentTime(double currentTime) { this->CurrentTime = currentTime; } + + void GetTimeRange(double timeRange[2]) + { + timeRange[0] = this->Stage->GetStartTimeCode() / this->Stage->GetTimeCodesPerSecond(); + timeRange[1] = this->Stage->GetEndTimeCode() / this->Stage->GetTimeCodesPerSecond(); + } + + pxr::UsdStageRefPtr Stage; + +private: + std::unordered_map > ActorMap; + std::unordered_map > MeshMap; + std::unordered_map > ShaderMap; + std::unordered_map > TextureMap; + double CurrentTime = 0.0; +}; + +vtkStandardNewMacro(vtkF3DUSDImporter); + +//---------------------------------------------------------------------------- +vtkF3DUSDImporter::vtkF3DUSDImporter() + : Internals(new vtkF3DUSDImporter::vtkInternals()) +{ +} + +//---------------------------------------------------------------------------- +vtkF3DUSDImporter::~vtkF3DUSDImporter() = default; + +//---------------------------------------------------------------------------- +int vtkF3DUSDImporter::ImportBegin() +{ + this->Internals->ReadScene(this->FileName); + + return 1; +} + +//---------------------------------------------------------------------------- +void vtkF3DUSDImporter::ImportActors(vtkRenderer* renderer) +{ + this->Internals->ImportRoot(renderer); +} + +//---------------------------------------------------------------------------- +vtkIdType vtkF3DUSDImporter::GetNumberOfAnimations() +{ + return this->Internals->HasTimeCode() ? 1 : 0; +} + +// Complete GetTemporalInformation needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/7246 +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 0, 20200912) +//---------------------------------------------------------------------------- +bool vtkF3DUSDImporter::GetTemporalInformation(vtkIdType vtkNotUsed(animationIndex), + double frameRate, int& nbTimeSteps, double timeRange[2], vtkDoubleArray* timeSteps) +{ + this->Internals->GetTimeRange(timeRange); + + nbTimeSteps = static_cast((timeRange[1] - timeRange[0]) * frameRate); + + for (int i = 0; i < nbTimeSteps; i++) + { + double timestep = timeRange[0] + static_cast(i) / frameRate; + timeSteps->InsertNextTypedTuple(×tep); + } + + return true; +} +#endif + +//---------------------------------------------------------------------------- +void vtkF3DUSDImporter::UpdateTimeStep(double timeStep) +{ + this->Internals->SetCurrentTime(timeStep); + this->Update(); +} + +//---------------------------------------------------------------------------- +void vtkF3DUSDImporter::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + os << indent << "FileName: " << this->FileName << "\n"; + os << indent << "AnimationEnabled: " << std::boolalpha << this->AnimationEnabled << "\n"; +} diff --git a/plugins/usd/module/vtkF3DUSDImporter.h b/plugins/usd/module/vtkF3DUSDImporter.h new file mode 100644 index 0000000000..09a95d7b75 --- /dev/null +++ b/plugins/usd/module/vtkF3DUSDImporter.h @@ -0,0 +1,83 @@ +/** + * @class vtkF3DUSDImporter + * @brief Importer using USD library + * + * Import used to load OpenUSD files. + * It supports .usd, .usda, .usdc and .usdz formats. + * + * This importer should cover a large part of the features including + * instancing and materials but there are known limitations including: + * - Only supports preview materials + * - Does not support UV transforms + * - Do not support lights and cameras + * - Skinning is done on the CPU + * - Ignore volumes and NURBS + * + * @sa https://openusd.org/release/index.html + */ + +#ifndef vtkF3DUSDImporter_h +#define vtkF3DUSDImporter_h + +#include +#include +#include + +#include + +class vtkF3DUSDImporter : public vtkImporter +{ +public: + static vtkF3DUSDImporter* New(); + vtkTypeMacro(vtkF3DUSDImporter, vtkImporter); + void PrintSelf(ostream& os, vtkIndent indent) override; + + /** + * Set the file name. + */ + vtkSetMacro(FileName, std::string); + +protected: + vtkF3DUSDImporter(); + ~vtkF3DUSDImporter() override; + + int ImportBegin() override; + void ImportActors(vtkRenderer*) override; + + void EnableAnimation(vtkIdType vtkNotUsed(animationIndex)) override + { + this->AnimationEnabled = true; + } + + void DisableAnimation(vtkIdType vtkNotUsed(animationIndex)) override + { + this->AnimationEnabled = false; + } + + bool IsAnimationEnabled(vtkIdType vtkNotUsed(animationIndex)) override + { + return this->AnimationEnabled; + } + + vtkIdType GetNumberOfAnimations() override; + +// Complete GetTemporalInformation needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/7246 +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 0, 20201016) + bool GetTemporalInformation(vtkIdType animationIndex, double frameRate, int& nbTimeSteps, + double timeRange[2], vtkDoubleArray* timeSteps) override; +#endif + + void UpdateTimeStep(double timeStep) override; + + std::string FileName; + bool AnimationEnabled = false; + +private: + vtkF3DUSDImporter(const vtkF3DUSDImporter&) = delete; + void operator=(const vtkF3DUSDImporter&) = delete; + + class vtkInternals; + std::unique_ptr Internals; +}; + +#endif diff --git a/testing/baselines/TestUSD.png b/testing/baselines/TestUSD.png new file mode 100644 index 0000000000..2bbd390025 --- /dev/null +++ b/testing/baselines/TestUSD.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5618562f094693564e2576126af1380f2fd46b919a0111c37648f31060a76408 +size 12707 diff --git a/testing/baselines/TestUSDAGlyphs.png b/testing/baselines/TestUSDAGlyphs.png new file mode 100644 index 0000000000..c474d8722b --- /dev/null +++ b/testing/baselines/TestUSDAGlyphs.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a5558184974fd377ecff811c84779c46f3a1e828516bd499c342c2050fca55b9 +size 3934 diff --git a/testing/baselines/TestUSDAInstancing.png b/testing/baselines/TestUSDAInstancing.png new file mode 100644 index 0000000000..3cb029919d --- /dev/null +++ b/testing/baselines/TestUSDAInstancing.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:28526675aa9359f42a05ce745e3921cb9f4ea1d28c903336da9649649134766e +size 8354 diff --git a/testing/baselines/TestUSDAPrimitives.png b/testing/baselines/TestUSDAPrimitives.png new file mode 100644 index 0000000000..e7e8336c76 --- /dev/null +++ b/testing/baselines/TestUSDAPrimitives.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a790a06ec6a43db100189b481fea34546cd11d4b8d60388602f82352a2f1d32d +size 9724 diff --git a/testing/baselines/TestUSDAPrimitivesZAxis.png b/testing/baselines/TestUSDAPrimitivesZAxis.png new file mode 100644 index 0000000000..07286d7613 --- /dev/null +++ b/testing/baselines/TestUSDAPrimitivesZAxis.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0fd740c7f8bb5247a6c39713ec35f5a4f52fb2ced45756e304cffd3e9cba7105 +size 9449 diff --git a/testing/baselines/TestUSDZAnimated.png b/testing/baselines/TestUSDZAnimated.png new file mode 100644 index 0000000000..0c5b068236 --- /dev/null +++ b/testing/baselines/TestUSDZAnimated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f504c8a8b80a2357b726423812f1071ff1f4c1c3572881832cea4ce64dc8210e +size 60595 diff --git a/testing/baselines/TestUSDZAnimatedStop.png b/testing/baselines/TestUSDZAnimatedStop.png new file mode 100644 index 0000000000..f2def7201c --- /dev/null +++ b/testing/baselines/TestUSDZAnimatedStop.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:483377375c53647e8a50ae7ef1b1db5113a6feb5798e5e569c7a6f20e69f8a0c +size 108509 diff --git a/testing/baselines/TestUSDZMaterials.png b/testing/baselines/TestUSDZMaterials.png new file mode 100644 index 0000000000..821df420fd --- /dev/null +++ b/testing/baselines/TestUSDZMaterials.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2374c31fdc091c8394040d3f7f636f4e052c263faac49169d1ea0b656e20a71 +size 102887 diff --git a/testing/data/AnimatedCube.usdz b/testing/data/AnimatedCube.usdz new file mode 100644 index 0000000000..6211b56d33 --- /dev/null +++ b/testing/data/AnimatedCube.usdz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b33a297f13efe1a73bb3b0ec1313d746e27487ca3023dd745e5ddaa320fbfe2 +size 897044 diff --git a/testing/data/DATA_LICENSES.md b/testing/data/DATA_LICENSES.md index 011b41f552..d68a64078c 100644 --- a/testing/data/DATA_LICENSES.md +++ b/testing/data/DATA_LICENSES.md @@ -3,6 +3,8 @@ # Copyrights and Licenses - albedo.png: glTF-Sample-Models: Public Domain +- AnimatedCube.usdz: [USD Working Group](https://github.com/usd-wg/assets): Apache 2.0 +- McUSD.usdz: [USD Working Group](https://github.com/usd-wg/assets): CC-NC-BY-SA, textures by [jasonjgardner](https://github.com/jasonjgardner) - animatedWorld.fbx: VTK Data: BSD-3-Clause - animation_with_skeleton.fbx: assimp test models: BSD-3-Clause - beach.nrrd: VTK Data: BSD-3-Clause diff --git a/testing/data/McUsd.usdz b/testing/data/McUsd.usdz new file mode 100644 index 0000000000..1d21d21e21 --- /dev/null +++ b/testing/data/McUsd.usdz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0fef9dbb8733372c297902830d2edfc91634976d5310bb5980597b6dcafbe211 +size 4608991 diff --git a/testing/data/glyphs.usda b/testing/data/glyphs.usda new file mode 100644 index 0000000000..d341b2601f --- /dev/null +++ b/testing/data/glyphs.usda @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:984a6eb550d205d513526c1f0c01b5b0d42f1c31ded261c97d9e1c82f371d831 +size 1018 diff --git a/testing/data/instancing.usda b/testing/data/instancing.usda new file mode 100644 index 0000000000..36c16dbc92 --- /dev/null +++ b/testing/data/instancing.usda @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02c95925caae4233eb62906fbbc8186f1d26f1b7c074e0fcf04be3fabe3cf088 +size 334 diff --git a/testing/data/invalid.usd b/testing/data/invalid.usd new file mode 100644 index 0000000000..10fc0715da --- /dev/null +++ b/testing/data/invalid.usd @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:907b7dd9189c720c9318ded901a7adcfcee7e873e428a23aa0a7d2469c494fc4 +size 29 diff --git a/testing/data/primitives.usda b/testing/data/primitives.usda new file mode 100644 index 0000000000..1500fbe5a6 --- /dev/null +++ b/testing/data/primitives.usda @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2a09db8ca5e7d9a76d18d9b060827b4a08961ae5683334d223a4faaa8232229 +size 2814 diff --git a/testing/data/primitivesZ.usda b/testing/data/primitivesZ.usda new file mode 100644 index 0000000000..09334a3d77 --- /dev/null +++ b/testing/data/primitivesZ.usda @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53d9b53975ecb3a7ee4963d2eb785682b9747dbf02b51381edcc53eda3420869 +size 2814 diff --git a/testing/data/suzanne.usd b/testing/data/suzanne.usd new file mode 100644 index 0000000000..442813d62f --- /dev/null +++ b/testing/data/suzanne.usd @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61e40d0f3f66a84a8e865e04164b595e95289cff93fe230a8abc4b61542c2510 +size 49571 diff --git a/testing/recordings/TestUSDZAnimatedStop.log b/testing/recordings/TestUSDZAnimatedStop.log new file mode 100644 index 0000000000..babe9704da --- /dev/null +++ b/testing/recordings/TestUSDZAnimatedStop.log @@ -0,0 +1,40 @@ +# StreamVersion 1.2 +ConfigureEvent 2 1409 0 0 0 0 0 +ExposeEvent 0 1409 0 0 0 0 0 +RenderEvent 0 1409 0 0 0 0 0 +KeyPressEvent 1698 319 0 32 1 space 0 +CharEvent 1698 319 0 32 1 space 0 +KeyReleaseEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +TimerEvent 1698 319 0 32 1 space 0 +KeyPressEvent 1698 319 0 32 1 space 0 +CharEvent 1698 319 0 32 1 space 0 +KeyReleaseEvent 1698 319 0 32 1 space 0 diff --git a/vcpkg.json b/vcpkg.json index d2a249f805..1b4c076a03 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -7,11 +7,21 @@ "opencascade", "openexr", "pybind11", + "tbb", + "usd", { "name": "vtk", "default-features": false, "features": [ "opengl" ] } ], - "builtin-baseline": "60e2c07d20aeb4bf534a8deabf92a70cbcb07617" + "overrides": [ + { + "$comment": "USD is not compatible with newer versions of TBB", + "name": "tbb", + "version-string": "2020_U3", + "port-version": 8 + } + ], + "builtin-baseline": "112b42c680c82c60fb4d37ebf7e959af7fc4e944" }