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 #include "pxr/pxr.h"
25 #include "pxr/usd/usd/instanceCache.h"
26 #include "pxr/usd/usd/debugCodes.h"
27 
28 #include "pxr/usd/pcp/primIndex.h"
29 
30 #include "pxr/base/tf/envSetting.h"
31 #include "pxr/base/trace/trace.h"
32 
33 #include <utility>
34 
35 PXR_NAMESPACE_OPEN_SCOPE
36 
37 using std::make_pair;
38 using std::pair;
39 using std::vector;
40 
41 TF_DEFINE_ENV_SETTING(
42     USD_ASSIGN_PROTOTYPES_DETERMINISTICALLY, false,
43     "Set to true to cause instances to be assigned to prototypes in a "
44     "deterministic way, ensuring consistency across runs.  This incurs "
45     "some additional overhead.");
46 
Usd_InstanceCache()47 Usd_InstanceCache::Usd_InstanceCache()
48     : _lastPrototypeIndex(0)
49 {
50 }
51 
52 bool
RegisterInstancePrimIndex(const PcpPrimIndex & index,UsdStagePopulationMask const * mask,UsdStageLoadRules const & loadRules)53 Usd_InstanceCache::RegisterInstancePrimIndex(
54     const PcpPrimIndex& index,
55     UsdStagePopulationMask const *mask,
56     UsdStageLoadRules const &loadRules)
57 {
58     TfAutoMallocTag tag("InstanceCache::RegisterIndex");
59 
60     if (!TF_VERIFY(index.IsInstanceable())) {
61         return false;
62     }
63 
64     // Make sure we compute the key for this index before we grab
65     // the mutex to minimize the time we hold the lock.
66     const Usd_InstanceKey key(index, mask, loadRules);
67 
68     // Check whether a prototype for this prim index already exists
69     // or if this prim index is already being used as the source for
70     // a prototype.
71     _InstanceKeyToPrototypeMap::const_iterator keyToPrototypeIt =
72         _instanceKeyToPrototypeMap.find(key);
73     const bool prototypeAlreadyExists =
74         (keyToPrototypeIt != _instanceKeyToPrototypeMap.end());
75 
76     {
77         tbb::spin_mutex::scoped_lock lock(_mutex);
78 
79         _PrimIndexPaths& pendingIndexes = _pendingAddedPrimIndexes[key];
80         pendingIndexes.push_back(index.GetPath());
81 
82         // A new prototype must be created for this instance if one doesn't
83         // already exist and this instance is the first one registered for
84         // this key.
85         const bool needsNewPrototype =
86             (!prototypeAlreadyExists && pendingIndexes.size() == 1);
87         if (needsNewPrototype) {
88             return true;
89         }
90     }
91 
92     if (prototypeAlreadyExists) {
93         _PrototypeToSourcePrimIndexMap::const_iterator prototypeToSourceIndexIt=
94             _prototypeToSourcePrimIndexMap.find(keyToPrototypeIt->second);
95         const bool existingPrototypeUsesIndexAsSource =
96             (prototypeToSourceIndexIt != _prototypeToSourcePrimIndexMap.end() &&
97              prototypeToSourceIndexIt->second == index.GetPath());
98         return existingPrototypeUsesIndexAsSource;
99     }
100 
101     return false;
102 }
103 
104 void
UnregisterInstancePrimIndexesUnder(const SdfPath & primIndexPath)105 Usd_InstanceCache::UnregisterInstancePrimIndexesUnder(
106     const SdfPath& primIndexPath)
107 {
108     TfAutoMallocTag tag("InstanceCache::UnregisterIndex");
109 
110     for (_PrimIndexToPrototypeMap::const_iterator
111              it = _primIndexToPrototypeMap.lower_bound(primIndexPath),
112              end = _primIndexToPrototypeMap.end();
113          it != end && it->first.HasPrefix(primIndexPath); ++it) {
114 
115         const SdfPath& prototypePath = it->second;
116         _PrototypeToInstanceKeyMap::const_iterator prototypeToKeyIt =
117             _prototypeToInstanceKeyMap.find(prototypePath);
118         if (!TF_VERIFY(prototypeToKeyIt != _prototypeToInstanceKeyMap.end())) {
119             continue;
120         }
121 
122         const Usd_InstanceKey& key = prototypeToKeyIt->second;
123         _PrimIndexPaths& pendingIndexes = _pendingRemovedPrimIndexes[key];
124         pendingIndexes.push_back(it->first);
125     }
126 }
127 
128 void
ProcessChanges(Usd_InstanceChanges * changes)129 Usd_InstanceCache::ProcessChanges(Usd_InstanceChanges* changes)
130 {
131     TRACE_FUNCTION();
132     TfAutoMallocTag tag("InstanceCache::ProcessChanges");
133 
134 
135     // Remove unregistered prim indexes from the cache.
136     std::unordered_map<SdfPath, SdfPath, SdfPath::Hash>
137         prototypeToOldSourceIndexPath;
138     for (_InstanceKeyToPrimIndexesMap::value_type &v:
139              _pendingRemovedPrimIndexes) {
140         const Usd_InstanceKey& key = v.first;
141         _PrimIndexPaths& primIndexes = v.second;
142 
143         // Ignore any unregistered prim index that was subsequently
144         // re-registered.
145         _InstanceKeyToPrimIndexesMap::const_iterator registeredIt =
146             _pendingAddedPrimIndexes.find(key);
147         if (registeredIt != _pendingAddedPrimIndexes.end()) {
148             _PrimIndexPaths registered = registeredIt->second;
149             _PrimIndexPaths unregistered;
150             unregistered.swap(primIndexes);
151 
152             std::sort(registered.begin(), registered.end());
153             std::sort(unregistered.begin(), unregistered.end());
154             std::set_difference(unregistered.begin(), unregistered.end(),
155                                 registered.begin(), registered.end(),
156                                 std::back_inserter(primIndexes));
157         }
158 
159         _RemoveInstances(key, primIndexes, changes,
160                          &prototypeToOldSourceIndexPath);
161     }
162 
163     // Add newly-registered prim indexes to the cache.
164     if (TfGetEnvSetting(USD_ASSIGN_PROTOTYPES_DETERMINISTICALLY)) {
165         // The order in which we process newly-registered prim indexes
166         // determines the name of the prototype prims assigned to instances.
167         // We need to iterate over the hash map in a fixed ordering to
168         // ensure we have a consistent assignment of instances to prototypes.
169         typedef std::map<SdfPath, Usd_InstanceKey> _PrimIndexPathToKey;
170         std::map<SdfPath, Usd_InstanceKey> keysToProcess;
171         for (_InstanceKeyToPrimIndexesMap::value_type& v:
172                  _pendingAddedPrimIndexes) {
173             const Usd_InstanceKey& key = v.first;
174             const _PrimIndexPaths& primIndexes = v.second;
175             if (TF_VERIFY(!primIndexes.empty())) {
176                 TF_VERIFY(
177                     keysToProcess.emplace(primIndexes.front(), key).second);
178             }
179         }
180 
181         for (const _PrimIndexPathToKey::value_type& v: keysToProcess) {
182             const Usd_InstanceKey& key = v.second;
183             _PrimIndexPaths& primIndexes = _pendingAddedPrimIndexes[key];
184             _CreateOrUpdatePrototypeForInstances(
185                 key, &primIndexes, changes, prototypeToOldSourceIndexPath);
186         }
187     }
188     else {
189         for(_InstanceKeyToPrimIndexesMap::value_type& v:
190                 _pendingAddedPrimIndexes) {
191             _CreateOrUpdatePrototypeForInstances(
192                 v.first, &v.second, changes, prototypeToOldSourceIndexPath);
193         }
194     }
195 
196     // Now that we've processed all additions and removals, we can find and
197     // drop any prototypes that have no instances associated with them.
198     for (const auto& v : _pendingRemovedPrimIndexes) {
199         _RemovePrototypeIfNoInstances(v.first, changes);
200     }
201 
202     _pendingAddedPrimIndexes.clear();
203     _pendingRemovedPrimIndexes.clear();
204 }
205 
206 void
_CreateOrUpdatePrototypeForInstances(const Usd_InstanceKey & key,_PrimIndexPaths * primIndexPaths,Usd_InstanceChanges * changes,std::unordered_map<SdfPath,SdfPath,SdfPath::Hash> const & prototypeToOldSourceIndexPath)207 Usd_InstanceCache::_CreateOrUpdatePrototypeForInstances(
208     const Usd_InstanceKey& key,
209     _PrimIndexPaths* primIndexPaths,
210     Usd_InstanceChanges* changes,
211     std::unordered_map<SdfPath, SdfPath, SdfPath::Hash> const &
212     prototypeToOldSourceIndexPath)
213 {
214     pair<_InstanceKeyToPrototypeMap::iterator, bool> result =
215         _instanceKeyToPrototypeMap.insert(make_pair(key, SdfPath()));
216 
217     const bool createdNewPrototype = result.second;
218     if (createdNewPrototype) {
219         // If this is a new prototype prim, the first instanceable prim
220         // index that was registered must be selected as the source
221         // index because the consumer was told that index required
222         // a new prototype via RegisterInstancePrimIndex.
223         //
224         // Note that this means the source prim index for a prototype may
225         // change from run to run. This should be fine, because all
226         // prim indexes with the same instancing key should have the
227         // same composed values.
228         const SdfPath newPrototypePath = _GetNextPrototypePath(key);
229         result.first->second = newPrototypePath;
230         _prototypeToInstanceKeyMap[newPrototypePath] = key;
231 
232         const SdfPath sourcePrimIndexPath = primIndexPaths->front();
233         _sourcePrimIndexToPrototypeMap[sourcePrimIndexPath] = newPrototypePath;
234         _prototypeToSourcePrimIndexMap[newPrototypePath] = sourcePrimIndexPath;
235 
236         changes->newPrototypePrims.push_back(newPrototypePath);
237         changes->newPrototypePrimIndexes.push_back(sourcePrimIndexPath);
238 
239         TF_DEBUG(USD_INSTANCING).Msg(
240             "Instancing: Creating prototype <%s> with source prim index <%s> "
241             "for instancing key: %s\n",
242             newPrototypePath.GetString().c_str(),
243             sourcePrimIndexPath.GetString().c_str(),
244             TfStringify(key).c_str());
245     }
246     else {
247         // Otherwise, if a prototype prim for this instance already exists
248         // but no source prim index has been assigned, do so here. This
249         // is exactly what happens in _RemoveInstances when a new source
250         // is assigned to a prototype; however, this handles the case where
251         // the last instance of a prototype has been removed and a new instance
252         // of the prototype has been added in the same round of changes.
253         const SdfPath& prototypePath = result.first->second;
254         const bool assignNewPrimIndexForPrototype =
255             (_prototypeToSourcePrimIndexMap.count(prototypePath) == 0);
256         if (assignNewPrimIndexForPrototype) {
257             const SdfPath sourcePrimIndexPath = primIndexPaths->front();
258             _sourcePrimIndexToPrototypeMap[sourcePrimIndexPath] = prototypePath;
259             _prototypeToSourcePrimIndexMap[prototypePath] = sourcePrimIndexPath;
260 
261             changes->changedPrototypePrims.push_back(prototypePath);
262             changes->changedPrototypePrimIndexes.push_back(sourcePrimIndexPath);
263             SdfPath const &oldSourcePath =
264                 prototypeToOldSourceIndexPath.find(prototypePath)->second;
265 
266             TF_DEBUG(USD_INSTANCING).Msg(
267                 "Instancing: Changing source <%s> -> <%s> for <%s>\n",
268                 oldSourcePath.GetText(), sourcePrimIndexPath.GetText(),
269                 prototypePath.GetText());
270         }
271     }
272 
273     // Assign the newly-registered prim indexes to their prototype.
274     const SdfPath& prototypePath = result.first->second;
275     for (const SdfPath& primIndexPath: *primIndexPaths) {
276         TF_DEBUG(USD_INSTANCING).Msg(
277             "Instancing: Added instance prim index <%s> for prototype "
278             "<%s>\n", primIndexPath.GetText(), prototypePath.GetText());
279 
280         _primIndexToPrototypeMap[primIndexPath] = prototypePath;
281     }
282 
283     _PrimIndexPaths& primIndexesForPrototype =
284         _prototypeToPrimIndexesMap[prototypePath];
285     std::sort(primIndexPaths->begin(), primIndexPaths->end());
286 
287     if (primIndexesForPrototype.empty()) {
288         primIndexesForPrototype.swap(*primIndexPaths);
289     }
290     else {
291         const size_t oldNumPrimIndexes = primIndexesForPrototype.size();
292         primIndexesForPrototype.insert(
293             primIndexesForPrototype.end(),
294             primIndexPaths->begin(), primIndexPaths->end());
295 
296         _PrimIndexPaths::iterator newlyAddedIt =
297             primIndexesForPrototype.begin() + oldNumPrimIndexes;
298         std::inplace_merge(primIndexesForPrototype.begin(), newlyAddedIt,
299                            primIndexesForPrototype.end());
300 
301         primIndexesForPrototype.erase(
302             std::unique(primIndexesForPrototype.begin(),
303                         primIndexesForPrototype.end()),
304             primIndexesForPrototype.end());
305     }
306 }
307 
308 void
_RemoveInstances(const Usd_InstanceKey & instanceKey,const _PrimIndexPaths & primIndexPaths,Usd_InstanceChanges * changes,std::unordered_map<SdfPath,SdfPath,SdfPath::Hash> * prototypeToOldSourceIndexPath)309 Usd_InstanceCache::_RemoveInstances(
310     const Usd_InstanceKey& instanceKey,
311     const _PrimIndexPaths& primIndexPaths,
312     Usd_InstanceChanges* changes,
313     std::unordered_map<SdfPath, SdfPath, SdfPath::Hash> *
314     prototypeToOldSourceIndexPath)
315 {
316     if (primIndexPaths.empty()) {
317         // if all unregistered primIndexes are also in the registered set, then
318         // vector of primIndexPaths to remove can be empty.
319         return;
320     }
321     _InstanceKeyToPrototypeMap::iterator keyToPrototypeIt =
322         _instanceKeyToPrototypeMap.find(instanceKey);
323     if (keyToPrototypeIt == _instanceKeyToPrototypeMap.end()) {
324         return;
325     }
326 
327     const SdfPath& prototypePath = keyToPrototypeIt->second;
328     // This will be set to the prim index path that the prototype was formerly
329     // using if we wind up removing it.  In this case, we'll need to select a
330     // new prim index path for the prototype.
331     SdfPath removedPrototypePrimIndexPath;
332 
333     // Remove the prim indexes from the prim index <-> prototype bidirectional
334     // mapping.
335     _PrimIndexPaths& primIndexesForPrototype =
336         _prototypeToPrimIndexesMap[prototypePath];
337     for (const SdfPath& path: primIndexPaths) {
338         _PrimIndexPaths::iterator it = std::find(
339             primIndexesForPrototype.begin(), primIndexesForPrototype.end(),
340             path);
341         if (it != primIndexesForPrototype.end()) {
342             TF_DEBUG(USD_INSTANCING).Msg(
343                 "Instancing: Removed instance prim index <%s> for prototype "
344                 "<%s>\n", path.GetText(), prototypePath.GetText());
345 
346             primIndexesForPrototype.erase(it);
347             _primIndexToPrototypeMap.erase(path);
348         }
349 
350         // This path is no longer instanced under this prototype, so record the
351         // old source index path and the prim's index path. Note that we may
352         // have removed the entry from _prototypeToSourcePrimIndexMap in an
353         // earlier iteration of this loop; if we have, then we will have saved
354         // the old path away in removedPrototypePrimIndexPath.
355         const SdfPath* oldSourcePrimIndexPath =
356             TfMapLookupPtr(_prototypeToSourcePrimIndexMap, prototypePath);
357         if (!oldSourcePrimIndexPath) {
358             oldSourcePrimIndexPath = &removedPrototypePrimIndexPath;
359         }
360 
361         if (_sourcePrimIndexToPrototypeMap.erase(path)) {
362             TF_VERIFY(_prototypeToSourcePrimIndexMap.erase(prototypePath));
363             removedPrototypePrimIndexPath = path;
364         }
365     }
366 
367     // If the source prim index for this prototype is no longer available
368     // but we have other instance prim indexes we can use instead, select
369     // one of those to serve as the new source.
370     //
371     // Otherwise, do nothing; we defer removal of this prototype until the end
372     // of instance change processing (see _RemovePrototypeIfNoInstances)
373     // in case a new instance for this prototype was registered.
374     if (!removedPrototypePrimIndexPath.IsEmpty()) {
375         if (!primIndexesForPrototype.empty()) {
376             const SdfPath& newSourceIndexPath = primIndexesForPrototype.front();
377 
378             TF_DEBUG(USD_INSTANCING).Msg(
379                 "Instancing: Changing source <%s> -> <%s> for <%s>\n",
380                 removedPrototypePrimIndexPath.GetText(),
381                 newSourceIndexPath.GetText(), prototypePath.GetText());
382 
383             _sourcePrimIndexToPrototypeMap[newSourceIndexPath] = prototypePath;
384             _prototypeToSourcePrimIndexMap[prototypePath] = newSourceIndexPath;
385 
386             changes->changedPrototypePrims.push_back(prototypePath);
387             changes->changedPrototypePrimIndexes.push_back(newSourceIndexPath);
388 
389         } else {
390             // Fill a data structure with the removedPrototypePrimIndexPath
391             // for the prototype so that we can fill in the right "before" path
392             // in changedPrototypePrimIndexes in
393             // _CreateOrUpdatePrototypeForInstances().
394             (*prototypeToOldSourceIndexPath)[prototypePath] =
395                 removedPrototypePrimIndexPath;
396         }
397     }
398 }
399 
400 void
_RemovePrototypeIfNoInstances(const Usd_InstanceKey & instanceKey,Usd_InstanceChanges * changes)401 Usd_InstanceCache::_RemovePrototypeIfNoInstances(
402     const Usd_InstanceKey& instanceKey,
403     Usd_InstanceChanges* changes)
404 {
405     auto keyToPrototypeIt = _instanceKeyToPrototypeMap.find(instanceKey);
406     if (keyToPrototypeIt == _instanceKeyToPrototypeMap.end()) {
407         return;
408     }
409 
410     const SdfPath& prototypePath = keyToPrototypeIt->second;
411     auto prototypeToPrimIndexesIt =
412         _prototypeToPrimIndexesMap.find(prototypePath);
413     if (!TF_VERIFY(
414             prototypeToPrimIndexesIt != _prototypeToPrimIndexesMap.end())) {
415         return;
416     }
417 
418     const _PrimIndexPaths& primIndexesForPrototype =
419         prototypeToPrimIndexesIt->second;
420     if (primIndexesForPrototype.empty()) {
421         // This prototype has no more instances associated with it, so it can
422         // be released.
423         TF_DEBUG(USD_INSTANCING).Msg(
424             "Instancing: Removing prototype <%s>\n", prototypePath.GetText());
425 
426         // Do this first, since prototypePath will be a stale reference after
427         // removing the map entries.
428         changes->deadPrototypePrims.push_back(prototypePath);
429 
430         _prototypeToInstanceKeyMap.erase(keyToPrototypeIt->second);
431         _instanceKeyToPrototypeMap.erase(keyToPrototypeIt);
432 
433         _prototypeToPrimIndexesMap.erase(prototypeToPrimIndexesIt);
434     }
435 }
436 
437 bool
IsPathInPrototype(const SdfPath & path)438 Usd_InstanceCache::IsPathInPrototype(const SdfPath& path)
439 {
440     if (path.IsEmpty() || path == SdfPath::AbsoluteRootPath()) {
441         return false;
442     }
443     if (!path.IsAbsolutePath()) {
444         // We require an absolute path because there is no way for us
445         // to walk to the root prim level from a relative path.
446         TF_CODING_ERROR("IsPathInPrototype() requires an absolute path "
447                         "but was given <%s>", path.GetText());
448         return false;
449     }
450 
451     SdfPath rootPath = path;
452     while (!rootPath.IsRootPrimPath()) {
453         rootPath = rootPath.GetParentPath();
454     }
455 
456     return TfStringStartsWith(rootPath.GetName(), "__Prototype_");
457 }
458 
459 bool
IsPrototypePath(const SdfPath & path)460 Usd_InstanceCache::IsPrototypePath(const SdfPath& path)
461 {
462     return path.IsRootPrimPath() &&
463         TfStringStartsWith(path.GetName(), "__Prototype_");
464 }
465 
466 vector<SdfPath>
GetInstancePrimIndexesForPrototype(const SdfPath & prototypePath) const467 Usd_InstanceCache::GetInstancePrimIndexesForPrototype(
468     const SdfPath& prototypePath) const
469 {
470     _PrototypeToPrimIndexesMap::const_iterator it =
471         _prototypeToPrimIndexesMap.find(prototypePath);
472 
473     return (it == _prototypeToPrimIndexesMap.end()) ?
474         vector<SdfPath>() : it->second;
475 }
476 
477 SdfPath
_GetNextPrototypePath(const Usd_InstanceKey & key)478 Usd_InstanceCache::_GetNextPrototypePath(const Usd_InstanceKey& key)
479 {
480     return SdfPath::AbsoluteRootPath().AppendChild(
481         TfToken(TfStringPrintf("__Prototype_%zu", ++_lastPrototypeIndex)));
482 }
483 
484 vector<SdfPath>
GetAllPrototypes() const485 Usd_InstanceCache::GetAllPrototypes() const
486 {
487     vector<SdfPath> paths;
488     paths.reserve(_instanceKeyToPrototypeMap.size());
489     for (const _InstanceKeyToPrototypeMap::value_type& v:
490              _instanceKeyToPrototypeMap) {
491         paths.push_back(v.second);
492     }
493     return paths;
494 }
495 
496 size_t
GetNumPrototypes() const497 Usd_InstanceCache::GetNumPrototypes() const
498 {
499     return _prototypeToInstanceKeyMap.size();
500 }
501 
502 SdfPath
GetPrototypeUsingPrimIndexPath(const SdfPath & primIndexPath) const503 Usd_InstanceCache::GetPrototypeUsingPrimIndexPath(
504     const SdfPath& primIndexPath) const
505 {
506     _SourcePrimIndexToPrototypeMap::const_iterator it =
507         _sourcePrimIndexToPrototypeMap.find(primIndexPath);
508     return it == _sourcePrimIndexToPrototypeMap.end() ? SdfPath() : it->second;
509 }
510 
511 template <class PathMap>
512 static
513 typename PathMap::const_iterator
_FindEntryForPathOrAncestor(const PathMap & map,SdfPath path)514 _FindEntryForPathOrAncestor(const PathMap& map, SdfPath path)
515 {
516     return SdfPathFindLongestPrefix(map, path);
517 }
518 
519 template <class PathMap>
520 static
521 typename PathMap::const_iterator
_FindEntryForAncestor(const PathMap & map,const SdfPath & path)522 _FindEntryForAncestor(const PathMap& map, const SdfPath& path)
523 {
524     if (path == SdfPath::AbsoluteRootPath()) {
525         return map.end();
526     }
527     return SdfPathFindLongestStrictPrefix(map, path);
528 }
529 
530 bool
PrototypeUsesPrimIndexPath(const SdfPath & primIndexPath) const531 Usd_InstanceCache::PrototypeUsesPrimIndexPath(
532     const SdfPath& primIndexPath) const
533 {
534     return _PrototypeUsesPrimIndexPath(primIndexPath);
535 }
536 
537 vector<SdfPath>
GetPrimsInPrototypesUsingPrimIndexPath(const SdfPath & primIndexPath) const538 Usd_InstanceCache::GetPrimsInPrototypesUsingPrimIndexPath(
539     const SdfPath& primIndexPath) const
540 {
541     vector<SdfPath> prototypePaths;
542     _PrototypeUsesPrimIndexPath(primIndexPath, &prototypePaths);
543     return prototypePaths;
544 }
545 
546 vector<std::pair<SdfPath, SdfPath>>
GetPrototypesUsingPrimIndexPathOrDescendents(const SdfPath & primIndexPath) const547 Usd_InstanceCache::GetPrototypesUsingPrimIndexPathOrDescendents(
548         const SdfPath& primIndexPath) const
549 {
550     vector<std::pair<SdfPath, SdfPath>> prototypeSourceIndexPairs;
551     for (_SourcePrimIndexToPrototypeMap::const_iterator
552              it = _sourcePrimIndexToPrototypeMap.lower_bound(primIndexPath),
553              end = _sourcePrimIndexToPrototypeMap.end();
554          it != end && it->first.HasPrefix(primIndexPath); ++it) {
555 
556         const SdfPath& prototypePath = it->second;
557         _PrototypeToSourcePrimIndexMap::const_iterator prototypeToSourceIt =
558             _prototypeToSourcePrimIndexMap.find(prototypePath);
559 
560         if (!TF_VERIFY(
561                 prototypeToSourceIt != _prototypeToSourcePrimIndexMap.end(),
562                 "prototypePath <%s> missing in prototypesToSourceIndexPath map",
563                 prototypePath.GetText())) {
564             prototypeSourceIndexPairs.emplace_back(prototypePath, SdfPath());
565             continue;
566         }
567 
568         const SdfPath& sourceIndexPath = prototypeToSourceIt->second;
569         prototypeSourceIndexPairs.emplace_back(prototypePath, sourceIndexPath);
570     }
571 
572     return prototypeSourceIndexPairs;
573 }
574 
575 bool
_PrototypeUsesPrimIndexPath(const SdfPath & primIndexPath,vector<SdfPath> * prototypePaths) const576 Usd_InstanceCache::_PrototypeUsesPrimIndexPath(
577     const SdfPath& primIndexPath,
578     vector<SdfPath>* prototypePaths) const
579 {
580     // This function is trickier than you might expect because it has
581     // to deal with nested instances. Consider this case:
582     //
583     // /World
584     //   Set_1     [prototype: </__Prototype_1>]
585     // /__Prototype_1 [index: </World/Set_1>]
586     //   Prop_1    [prototype: </__Prototype_2>, index: </World/Set_1/Prop_1> ]
587     //   Prop_2    [prototype: </__Prototype_2>, index: </World/Set_1/Prop_2> ]
588     // /__Prototype_2 [index: </World/Set_1/Prop_1>]
589     //   Scope     [index: </World/Set_1/Prop_1/Scope>]
590     //
591     // Asking if the prim index /World/Set_1/Prop_1/Scope is used by a
592     // prototype should return true, because it is used by /__Prototype_2/Scope.
593     // But this function should return false for /World/Set_1/Prop_2/Scope.
594     // The naive implementation that looks through
595     // _sourcePrimIndexToPrototypeMap would wind up returning true for both
596     // of these.
597 
598     bool prototypeUsesPrimIndex = false;
599 
600     SdfPath curIndexPath = primIndexPath;
601     while (curIndexPath != SdfPath::AbsoluteRootPath()) {
602         // Find the instance prim index that is closest to the current prim
603         // index path. If there isn't one, this prim index isn't a descendent
604         // of an instance, which means it can't possibly be used by a prototype.
605         _PrimIndexToPrototypeMap::const_iterator it =
606             _FindEntryForPathOrAncestor(_primIndexToPrototypeMap, curIndexPath);
607         if (it == _primIndexToPrototypeMap.end()) {
608             break;
609         }
610 
611         // Figure out what prototype is associated with the prim index
612         // we found, and see if the given prim index is a descendent of its
613         // source prim index. If it is, then this prim index must be used
614         // by a descendent of that prototype.
615         _PrototypeToSourcePrimIndexMap::const_iterator prototypeToSourceIt =
616             _prototypeToSourcePrimIndexMap.find(it->second);
617         if (!TF_VERIFY(
618                 prototypeToSourceIt != _prototypeToSourcePrimIndexMap.end())) {
619             break;
620         }
621 
622         const SdfPath& prototypePath = prototypeToSourceIt->first;
623         const SdfPath& sourcePrimIndexPath = prototypeToSourceIt->second;
624         if (curIndexPath.HasPrefix(sourcePrimIndexPath)) {
625             // If we don't need to collect all the prototype paths using this
626             // prim index, we can bail out immediately.
627             prototypeUsesPrimIndex = true;
628             if (prototypePaths) {
629                 prototypePaths->push_back(primIndexPath.ReplacePrefix(
630                     sourcePrimIndexPath, prototypePath));
631             }
632             else {
633                 break;
634             }
635         }
636 
637         // If we found an entry for an ancestor of curIndexPath in
638         // _primIndexToPrototypeMap, the index must be a descendant of an
639         // instanceable prim index. These indexes can only ever be used by
640         // a single prototype prim, so we can stop here.
641         //
642         // Otherwise, this index is an instanceable prim index. In the case of
643         // nested instancing, there may be another prototype prim using this
644         // index,
645         // so we have to keep looking.
646         const bool indexIsDescendentOfInstance = (it->first != curIndexPath);
647         if (indexIsDescendentOfInstance) {
648             break;
649         }
650 
651         curIndexPath = it->first.GetParentPath();
652     }
653 
654     return prototypeUsesPrimIndex;
655 }
656 
657 bool
IsPathDescendantToAnInstance(const SdfPath & usdPrimPath) const658 Usd_InstanceCache::IsPathDescendantToAnInstance(
659     const SdfPath& usdPrimPath) const
660 {
661     // If any ancestor of usdPrimPath is in _primIndexToPrototypeMap, it's
662     // a descendent of an instance.
663     return _FindEntryForAncestor(_primIndexToPrototypeMap, usdPrimPath) !=
664         _primIndexToPrototypeMap.end();
665 }
666 
667 SdfPath
GetMostAncestralInstancePath(const SdfPath & usdPrimPath) const668 Usd_InstanceCache::GetMostAncestralInstancePath(
669     const SdfPath &usdPrimPath) const
670 {
671     SdfPath path = usdPrimPath;
672     SdfPath result;
673     SdfPath const &absRoot = SdfPath::AbsoluteRootPath();
674     while (path != absRoot) {
675         auto it = _FindEntryForAncestor(_primIndexToPrototypeMap, path);
676         if (it == _primIndexToPrototypeMap.end())
677             break;
678         result = it->first;
679         path = it->first.GetParentPath();
680     }
681     return result;
682 }
683 
684 SdfPath
GetPrototypeForInstanceablePrimIndexPath(const SdfPath & primIndexPath) const685 Usd_InstanceCache::GetPrototypeForInstanceablePrimIndexPath(
686     const SdfPath& primIndexPath) const
687 {
688     // Search the mapping from instance prim index to prototype prim
689     // to find the associated prototype.
690     _PrimIndexToPrototypeMap::const_iterator it =
691         _primIndexToPrototypeMap.find(primIndexPath);
692     return (it == _primIndexToPrototypeMap.end() ? SdfPath() : it->second);
693 }
694 
695 SdfPath
GetPathInPrototypeForInstancePath(const SdfPath & primPath) const696 Usd_InstanceCache::GetPathInPrototypeForInstancePath(
697     const SdfPath& primPath) const
698 {
699     SdfPath primIndexPath;
700 
701     // Without instancing, the path of a prim on a stage will be the same
702     // as the path for its prim index. However, this is not the case for
703     // prims in prototypes (e.g., /__Prototype_1/Instance/Child). In this case,
704     // we need to figure out what the source prim index path would be.
705     if (IsPathInPrototype(primPath)) {
706         // If primPath is prefixed by a prototype prim path, replace it
707         // with that prototype's source index path to produce a prim index
708         // path.
709         _PrototypeToSourcePrimIndexMap::const_iterator it =
710             _prototypeToSourcePrimIndexMap.upper_bound(primPath);
711         if (it != _prototypeToSourcePrimIndexMap.begin()) {
712             --it;
713             const SdfPath& prototypePath = it->first;
714             const SdfPath& sourcePrimIndexPath = it->second;
715 
716             // Just try the prefix replacement instead of doing a separate
717             // HasPrefix check. If it does nothing, we know primPath wasn't
718             // a prim in a prototype that this cache knows about.
719             const SdfPath p =
720                 primPath.ReplacePrefix(prototypePath, sourcePrimIndexPath);
721             if (p != primPath) {
722                 primIndexPath = p;
723             }
724         }
725     }
726     else {
727         primIndexPath = primPath;
728     }
729 
730     if (primIndexPath.IsEmpty())
731         return primIndexPath;
732 
733     // This function is trickier than you might expect because it has
734     // to deal with nested instances. Consider this case:
735     //
736     // /World
737     //   Set_1     [prototype: </__Prototype_1>, index: </World/Set_1>]
738     //   Set_2     [prototype: </__Prototype_1>, index: </World/Set_2>]
739     // /__Prototype_1 [index: </World/Set_1>]
740     //   Prop_1    [prototype: </__Prototype_2>, index: </World/Set_1/Prop_1> ]
741     //   Prop_2    [prototype: </__Prototype_2>, index: </World/Set_1/Prop_2> ]
742     // /__Prototype_2 [index: </World/Set_1/Prop_1>]
743     //   Scope     [index: </World/Set_1/Prop_1/Scope>]
744     //
745     // Asking for the prim in prototype for the prim index
746     // /World/Set_2/Prop_1/Scope should return /__Prototype_2/Scope, since
747     // /World/Set_2 is an instance of /__Prototype_1, and /__Prototype_1/Prop_1
748     // is an instance of /__Prototype_2.
749     //
750     // The naive implementation would look through _primIndexToPrototypeMap
751     // and do a prefix replacement, but that gives /__Prototype_1/Prop_1/Scope.
752     // This is because the prim index /World/Set_2/Prop_1/Scope has never been
753     // computed in this example!
754 
755     SdfPath primInPrototypePath;
756     SdfPath curPrimIndexPath = primIndexPath;
757     while (!curPrimIndexPath.IsEmpty()) {
758         // Find the instance prim index that is closest to the current
759         // prim index path. If there isn't one, this prim index isn't a
760         // descendent of an instance.
761         _PrimIndexToPrototypeMap::const_iterator it =
762             _FindEntryForAncestor(_primIndexToPrototypeMap, curPrimIndexPath);
763         if (it == _primIndexToPrototypeMap.end()) {
764             break;
765         }
766 
767         // Find the source prim index corresponding to this prototype.
768         // If curPrimIndexPath is already relative to this prim index,
769         // we can do a prefix replacement to determine the final prototype
770         // prim path.
771         //
772         // If curPrimIndexPath is *not* relative to this prim index,
773         // do a prefix replacement to make it so, then loop and try again.
774         // This helps us compute the correct prim in prototype in the case
775         // above because we know the source prim index *must* have been
776         // computed -- otherwise, it wouldn't be a prototype's source index.
777         // The next time around we'll find a match for curPrimIndexPath
778         // in _primIndexToPrototypeMap that gets us closer to the nested
779         // instance's prototype (if one exists).
780         _PrototypeToSourcePrimIndexMap::const_iterator prototypeToSourceIt =
781             _prototypeToSourcePrimIndexMap.find(it->second);
782         if (!TF_VERIFY(
783                 prototypeToSourceIt != _prototypeToSourcePrimIndexMap.end())) {
784             break;
785         }
786 
787         const SdfPath& sourcePrimIndexPath = prototypeToSourceIt->second;
788         if (it->first == sourcePrimIndexPath) {
789             primInPrototypePath =
790                 curPrimIndexPath.ReplacePrefix(it->first, it->second);
791             break;
792         }
793 
794         curPrimIndexPath =
795             curPrimIndexPath.ReplacePrefix(it->first, sourcePrimIndexPath);
796     }
797 
798     return primInPrototypePath;
799 }
800 
801 PXR_NAMESPACE_CLOSE_SCOPE
802 
803