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