1 // 2 // Copyright 2016 Pixar 3 // 4 // Licensed under the Apache License, Version 2.0 (the "Apache License") 5 // with the following modification; you may not use this file except in 6 // compliance with the Apache License and the following modification to it: 7 // Section 6. Trademarks. is deleted and replaced with: 8 // 9 // 6. Trademarks. This License does not grant permission to use the trade 10 // names, trademarks, service marks, or product names of the Licensor 11 // and its affiliates, except as required to comply with Section 4(c) of 12 // the License and to reproduce the content of the NOTICE file. 13 // 14 // You may obtain a copy of the Apache License at 15 // 16 // http://www.apache.org/licenses/LICENSE-2.0 17 // 18 // Unless required by applicable law or agreed to in writing, software 19 // distributed under the Apache License with the above modification is 20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 21 // KIND, either express or implied. See the Apache License for the specific 22 // language governing permissions and limitations under the Apache License. 23 // 24 #ifndef PXR_USD_USD_INSTANCE_CACHE_H 25 #define PXR_USD_USD_INSTANCE_CACHE_H 26 27 #include "pxr/pxr.h" 28 #include "pxr/usd/usd/instanceKey.h" 29 30 #include "pxr/usd/sdf/path.h" 31 #include "pxr/base/tf/hashmap.h" 32 33 #include <tbb/mutex.h> 34 #include <map> 35 #include <unordered_map> 36 #include <vector> 37 38 PXR_NAMESPACE_OPEN_SCOPE 39 40 /// \class Usd_InstanceChanges 41 /// 42 /// List of changes to prototype prims due to the discovery of new 43 /// or destroyed instanceable prim indexes. 44 /// 45 class Usd_InstanceChanges 46 { 47 public: AppendChanges(const Usd_InstanceChanges & c)48 void AppendChanges(const Usd_InstanceChanges& c) 49 { 50 newPrototypePrims.insert( 51 newPrototypePrims.end(), 52 c.newPrototypePrims.begin(), 53 c.newPrototypePrims.end()); 54 55 newPrototypePrimIndexes.insert( 56 newPrototypePrimIndexes.end(), 57 c.newPrototypePrimIndexes.begin(), 58 c.newPrototypePrimIndexes.end()); 59 60 changedPrototypePrims.insert( 61 changedPrototypePrims.end(), 62 c.changedPrototypePrims.begin(), 63 c.changedPrototypePrims.end()); 64 65 changedPrototypePrimIndexes.insert( 66 changedPrototypePrimIndexes.end(), 67 c.changedPrototypePrimIndexes.begin(), 68 c.changedPrototypePrimIndexes.end()); 69 70 deadPrototypePrims.insert( 71 deadPrototypePrims.end(), 72 c.deadPrototypePrims.begin(), 73 c.deadPrototypePrims.end()); 74 } 75 76 /// List of new prototype prims and their corresponding source 77 /// prim indexes. 78 std::vector<SdfPath> newPrototypePrims; 79 std::vector<SdfPath> newPrototypePrimIndexes; 80 81 /// List of prototype prims that have been changed to use a new 82 /// source prim index. 83 std::vector<SdfPath> changedPrototypePrims; 84 std::vector<SdfPath> changedPrototypePrimIndexes; 85 86 /// List of prototype prims that no longer have any instances. 87 std::vector<SdfPath> deadPrototypePrims; 88 }; 89 90 /// \class Usd_InstanceCache 91 /// 92 /// Private helper object for computing and caching instance information 93 /// on a UsdStage. This object is responsible for keeping track of the 94 /// instanceable prim indexes and their corresponding prototypes. 95 /// This includes: 96 /// 97 /// - Tracking all instanceable prim indexes and prototype prims 98 /// on the stage. 99 /// - Determining when a new prototype must be created or an 100 /// old prototype can be reused for a newly-discovered instanceable 101 /// prim index. 102 /// - Determining when a prototype can be removed due to it no longer 103 /// having any instanceable prim indexes. 104 /// 105 /// During composition, UsdStage will discover instanceable prim indexes 106 /// which will be registered with this cache. These prim indexes will 107 /// then be assigned to the appropriate prototype prim. One of these prim 108 /// indexes will be used as the "source" prim index for the prototype. 109 /// This object keeps track of the dependencies formed between 110 /// prototypes and prim indexes by this process. 111 /// 112 /// API note: It can be confusing to reason about prototypes and instances, 113 /// especially with arbitrarily nested instancing. To help clarify, the API 114 /// below uses two idioms to describe the two main kinds of relationships 115 /// involved in instancing: 1) instances to their prototype usd prims, and 2) 116 /// prototype usd prims to the prim indexes they use. For #1, we use phrasing 117 /// like, "prototype for instance". For example 118 /// GetPathInPrototypeForInstancePath() finds the corresponding prototype prim 119 /// for a given instance prim path. For #2, we use phrasing like, "prototype 120 /// using prim index". For example GetPrototypeUsingPrimIndexPath() finds the 121 /// prototype using the given prim index path as its source, if there is one. 122 /// 123 class Usd_InstanceCache 124 { 125 Usd_InstanceCache(Usd_InstanceCache const &) = delete; 126 Usd_InstanceCache &operator=(Usd_InstanceCache const &) = delete; 127 public: 128 Usd_InstanceCache(); 129 130 /// Registers the given instance prim index \p index with the cache. 131 /// The index will be added to a list of pending changes and will 132 /// not take effect until a subsequent call to ProcessChanges. 133 /// 134 /// It is safe to call this function concurrently from multiple 135 /// threads. 136 /// 137 /// Returns true if the given instance prim index requires a new 138 /// prototype prim or is the source for an existing prototype prim, false 139 /// otherwise. 140 bool RegisterInstancePrimIndex(const PcpPrimIndex& index, 141 const UsdStagePopulationMask *mask, 142 const UsdStageLoadRules &loadRules); 143 144 /// Unregisters all instance prim indexes at or under \p primIndexPath. 145 /// The indexes will be added to a list of pending changes and will 146 /// not take effect until a subsequent call to ProcessChanges. 147 void UnregisterInstancePrimIndexesUnder(const SdfPath& primIndexPath); 148 149 /// Process all instance prim indexes that have been registered or 150 /// unregistered since the last call to this function and return the 151 /// resulting list of prototype prim changes via \p changes. 152 void ProcessChanges(Usd_InstanceChanges* changes); 153 154 /// Return true if \p path identifies a prototype or a prototype descendant. 155 /// The \p path must be either an absolute path or empty. 156 static bool IsPathInPrototype(const SdfPath& path); 157 158 /// Return true if \p path identifies a prototype. 159 static bool IsPrototypePath(const SdfPath& path); 160 161 /// Return instance prim indexes registered for \p prototypePath, an empty 162 /// vector otherwise 163 std::vector<SdfPath> GetInstancePrimIndexesForPrototype( 164 const SdfPath& prototype) const; 165 166 /// Returns the paths of all prototype prims for instance prim 167 /// indexes registered with this cache. 168 std::vector<SdfPath> GetAllPrototypes() const; 169 170 /// Returns the number of prototype prims assigned to instance 171 /// prim indexes registered with this cache. 172 size_t GetNumPrototypes() const; 173 174 /// Return the path of the prototype root prim using the prim index at 175 /// \p primIndexPath as its source prim index, or the empty path if no such 176 /// prototype exists. 177 /// 178 /// Unlike GetPrototypeForPrimIndexPath, this function will return a 179 /// prototype prim path only if the prototype prim is using the specified 180 /// prim index as its source. 181 SdfPath GetPrototypeUsingPrimIndexPath(const SdfPath& primIndexPath) const; 182 183 /// Return the paths of all prims in prototypes using the prim index at 184 /// \p primIndexPath. 185 /// 186 /// There are at most two such paths. Without nested instancing, there is 187 /// at most one: the prim in the prototype corresponding to the instance 188 /// identified by \p primIndexPath. With nested instancing there will be 189 /// two if the \p primIndexPath identifies an instanceable prim index 190 /// descendant to another instancable prim index, and this \p primIndexPath 191 /// was selected for use by that nested instance's prototype. In that case 192 /// this function will return the path of the nested instance under the 193 /// outer prototype, and also the prototype path corresponding to that 194 /// nested instance. 195 std::vector<SdfPath> 196 GetPrimsInPrototypesUsingPrimIndexPath(const SdfPath& primIndexPath) const; 197 198 /// Return a vector of pair of prototype and respective source prim index 199 /// path for all prototypes using the prim index at \p primIndexPath or as 200 /// descendent of \p primIndexPath. 201 std::vector<std::pair<SdfPath, SdfPath>> 202 GetPrototypesUsingPrimIndexPathOrDescendents( 203 const SdfPath& primIndexPath) const; 204 205 /// Return true if a prim in a prototype uses the prim index at 206 /// \p primIndexPath. 207 bool PrototypeUsesPrimIndexPath(const SdfPath& primIndexPath) const; 208 209 /// Return the path of the prototype prim associated with the instanceable 210 /// \p primIndexPath. If \p primIndexPath is not instanceable, or if it 211 /// has no associated prototype because it lacks composition arcs, return 212 /// the empty path. 213 SdfPath 214 GetPrototypeForInstanceablePrimIndexPath( 215 const SdfPath& primIndexPath) const; 216 217 /// Returns true if \p primPath is descendent to an instance. That is, 218 /// return true if a strict ancestor path of \p usdPrimPath identifies an 219 /// instanceable prim index. 220 bool IsPathDescendantToAnInstance(const SdfPath& primPath) const; 221 222 /// Returns the shortest ancestor of \p primPath that identifies an 223 /// instanceable prim. If there is no such ancestor, return the empty path. 224 SdfPath GetMostAncestralInstancePath(const SdfPath &primPath) const; 225 226 /// Return the corresponding prototype prim path if \p primPath is 227 /// descendant to an instance (see IsPathDescendantToAnInstance()), 228 /// otherwise the empty path. 229 SdfPath GetPathInPrototypeForInstancePath(const SdfPath& primPath) const; 230 231 private: 232 typedef std::vector<SdfPath> _PrimIndexPaths; 233 234 void _CreateOrUpdatePrototypeForInstances( 235 const Usd_InstanceKey& instanceKey, 236 _PrimIndexPaths* primIndexPaths, 237 Usd_InstanceChanges* changes, 238 std::unordered_map<SdfPath, SdfPath, SdfPath::Hash> const & 239 prototypeToOldSourceIndexPath); 240 241 void _RemoveInstances( 242 const Usd_InstanceKey& instanceKey, 243 const _PrimIndexPaths& primIndexPaths, 244 Usd_InstanceChanges* changes, 245 std::unordered_map<SdfPath, SdfPath, SdfPath::Hash> * 246 prototypeToOldSourceIndexPath); 247 248 void _RemovePrototypeIfNoInstances( 249 const Usd_InstanceKey& instanceKey, 250 Usd_InstanceChanges* changes); 251 252 bool _PrototypeUsesPrimIndexPath( 253 const SdfPath& primIndexPath, 254 std::vector<SdfPath>* prototypePaths = nullptr) const; 255 256 SdfPath _GetNextPrototypePath(const Usd_InstanceKey& key); 257 258 private: 259 tbb::spin_mutex _mutex; 260 261 // Mapping from instance key <-> prototype prim path. 262 // This stores the path of the prototype prim that should be used 263 // for all instanceable prim indexes with the given instance key. 264 typedef TfHashMap<Usd_InstanceKey, SdfPath, boost::hash<Usd_InstanceKey> > 265 _InstanceKeyToPrototypeMap; 266 typedef TfHashMap<SdfPath, Usd_InstanceKey, SdfPath::Hash> 267 _PrototypeToInstanceKeyMap; 268 _InstanceKeyToPrototypeMap _instanceKeyToPrototypeMap; 269 _PrototypeToInstanceKeyMap _prototypeToInstanceKeyMap; 270 271 // Mapping from instance prim index path <-> prototype prim path. 272 // This map stores which prim index serves as the source index 273 // for a given prototype prim. 274 typedef std::map<SdfPath, SdfPath> _SourcePrimIndexToPrototypeMap; 275 typedef std::map<SdfPath, SdfPath> _PrototypeToSourcePrimIndexMap; 276 _SourcePrimIndexToPrototypeMap _sourcePrimIndexToPrototypeMap; 277 _PrototypeToSourcePrimIndexMap _prototypeToSourcePrimIndexMap; 278 279 // Mapping from prototype prim path <-> list of instanceable prim indexes 280 // This map stores which instanceable prim indexes have been assigned to 281 // a prototype prim. 282 typedef std::map<SdfPath, _PrimIndexPaths> _PrototypeToPrimIndexesMap; 283 typedef std::map<SdfPath, SdfPath> _PrimIndexToPrototypeMap; 284 _PrototypeToPrimIndexesMap _prototypeToPrimIndexesMap; 285 _PrimIndexToPrototypeMap _primIndexToPrototypeMap; 286 287 // Map from instance key -> list of prim index paths 288 // These maps contain lists of pending changes and are the only containers 289 // that should be modified during registration and unregistration. 290 typedef TfHashMap< 291 Usd_InstanceKey, _PrimIndexPaths, boost::hash<Usd_InstanceKey> > 292 _InstanceKeyToPrimIndexesMap; 293 _InstanceKeyToPrimIndexesMap _pendingAddedPrimIndexes; 294 _InstanceKeyToPrimIndexesMap _pendingRemovedPrimIndexes; 295 296 // Index of last prototype prim created. Used to create 297 // prototype prim names. 298 size_t _lastPrototypeIndex; 299 }; 300 301 PXR_NAMESPACE_CLOSE_SCOPE 302 303 #endif // PXR_USD_USD_INSTANCE_CACHE_H 304