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 
25 #include "pxr/pxr.h"
26 #include "pxr/usd/pcp/primIndex.h"
27 #include "pxr/usd/pcp/arc.h"
28 #include "pxr/usd/pcp/cache.h"
29 #include "pxr/usd/pcp/dynamicFileFormatContext.h"
30 #include "pxr/usd/pcp/composeSite.h"
31 #include "pxr/usd/pcp/debugCodes.h"
32 #include "pxr/usd/pcp/diagnostic.h"
33 #include "pxr/usd/pcp/dynamicFileFormatInterface.h"
34 #include "pxr/usd/pcp/instancing.h"
35 #include "pxr/usd/pcp/layerStack.h"
36 #include "pxr/usd/pcp/layerStackRegistry.h"
37 #include "pxr/usd/pcp/node_Iterator.h"
38 #include "pxr/usd/pcp/primIndex_Graph.h"
39 #include "pxr/usd/pcp/primIndex_StackFrame.h"
40 #include "pxr/usd/pcp/statistics.h"
41 #include "pxr/usd/pcp/strengthOrdering.h"
42 #include "pxr/usd/pcp/types.h"
43 #include "pxr/usd/pcp/utils.h"
44 #include "pxr/usd/ar/resolver.h"
45 #include "pxr/usd/ar/resolverContextBinder.h"
46 #include "pxr/usd/sdf/fileFormat.h"
47 #include "pxr/usd/sdf/layer.h"
48 #include "pxr/base/trace/trace.h"
49 #include "pxr/base/tf/debug.h"
50 #include "pxr/base/tf/enum.h"
51 #include "pxr/base/tf/diagnostic.h"
52 #include "pxr/base/tf/envSetting.h"
53 #include "pxr/base/tf/mallocTag.h"
54 
55 #include <algorithm>
56 #include <functional>
57 #include <iostream>
58 #include <vector>
59 
60 // Un-comment for extra runtime validation.
61 // #define PCP_DIAGNOSTIC_VALIDATION 1
62 
63 using std::string;
64 using std::vector;
65 
66 PXR_NAMESPACE_OPEN_SCOPE
67 
68 TF_DEFINE_ENV_SETTING(
69     MENV30_ENABLE_NEW_DEFAULT_STANDIN_BEHAVIOR, true,
70     "If enabled then standin preference is weakest opinion.");
71 
72 static inline PcpPrimIndex const *
_GetOriginatingIndex(PcpPrimIndex_StackFrame * previousFrame,PcpPrimIndexOutputs * outputs)73 _GetOriginatingIndex(PcpPrimIndex_StackFrame *previousFrame,
74                      PcpPrimIndexOutputs *outputs) {
75     return ARCH_UNLIKELY(previousFrame) ?
76         previousFrame->originatingIndex : &outputs->primIndex;
77 }
78 
79 bool
PcpIsNewDefaultStandinBehaviorEnabled()80 PcpIsNewDefaultStandinBehaviorEnabled()
81 {
82     return TfGetEnvSetting(MENV30_ENABLE_NEW_DEFAULT_STANDIN_BEHAVIOR);
83 }
84 
85 ////////////////////////////////////////////////////////////////////////
86 
PcpPrimIndex()87 PcpPrimIndex::PcpPrimIndex()
88 {
89 }
90 
91 void
SetGraph(const PcpPrimIndex_GraphRefPtr & graph)92 PcpPrimIndex::SetGraph(const PcpPrimIndex_GraphRefPtr& graph)
93 {
94     _graph = graph;
95 }
96 
97 PcpPrimIndex_GraphPtr
GetGraph() const98 PcpPrimIndex::GetGraph() const
99 {
100     return _graph;
101 }
102 
103 PcpNodeRef
GetRootNode() const104 PcpPrimIndex::GetRootNode() const
105 {
106     return _graph ? _graph->GetRootNode() : PcpNodeRef();
107 }
108 
109 const SdfPath&
GetPath() const110 PcpPrimIndex::GetPath() const
111 {
112     return _graph ? _graph->GetRootNode().GetPath() : SdfPath::EmptyPath();
113 }
114 
115 bool
HasSpecs() const116 PcpPrimIndex::HasSpecs() const
117 {
118     // Prim stacks are not cached in Usd mode
119     if (!IsUsd()) {
120         return !_primStack.empty();
121     }
122 
123     for (const auto& node : GetNodeRange()) {
124         if (node.HasSpecs()) {
125             return true;
126         }
127     }
128 
129     return false;
130 }
131 
132 bool
HasAnyPayloads() const133 PcpPrimIndex::HasAnyPayloads() const
134 {
135     return _graph && _graph->HasPayloads();
136 }
137 
138 bool
IsUsd() const139 PcpPrimIndex::IsUsd() const
140 {
141     return _graph && _graph->IsUsd();
142 }
143 
144 bool
IsInstanceable() const145 PcpPrimIndex::IsInstanceable() const
146 {
147     return _graph && _graph->IsInstanceable();
148 }
149 
PcpPrimIndex(const PcpPrimIndex & rhs)150 PcpPrimIndex::PcpPrimIndex(const PcpPrimIndex &rhs)
151 {
152     _graph = rhs._graph;
153     _primStack = rhs._primStack;
154 
155     if (rhs._localErrors) {
156         _localErrors.reset(new PcpErrorVector(*rhs._localErrors.get()));
157     }
158 }
159 
160 void
Swap(PcpPrimIndex & rhs)161 PcpPrimIndex::Swap(PcpPrimIndex& rhs)
162 {
163     _graph.swap(rhs._graph);
164     _primStack.swap(rhs._primStack);
165     _localErrors.swap(rhs._localErrors);
166 }
167 
168 void
PrintStatistics() const169 PcpPrimIndex::PrintStatistics() const
170 {
171     Pcp_PrintPrimIndexStatistics(*this, std::cout);
172 }
173 
DumpToString(bool includeInheritOriginInfo,bool includeMaps) const174 std::string PcpPrimIndex::DumpToString(
175     bool includeInheritOriginInfo,
176     bool includeMaps) const
177 {
178     return PcpDump(
179         *this, includeInheritOriginInfo, includeMaps);
180 }
181 
DumpToDotGraph(const std::string & filename,bool includeInheritOriginInfo,bool includeMaps) const182 void PcpPrimIndex::DumpToDotGraph(
183     const std::string& filename,
184     bool includeInheritOriginInfo,
185     bool includeMaps) const
186 {
187     PcpDumpDotGraph(
188         *this, filename.c_str(), includeInheritOriginInfo, includeMaps);
189 }
190 
191 PcpNodeRange
GetNodeRange(PcpRangeType rangeType) const192 PcpPrimIndex::GetNodeRange(PcpRangeType rangeType) const
193 {
194     if (!_graph) {
195         return PcpNodeRange();
196     }
197 
198     const std::pair<size_t, size_t> range =
199         _graph->GetNodeIndexesForRange(rangeType);
200     return PcpNodeRange(
201         PcpNodeIterator(get_pointer(_graph), range.first),
202         PcpNodeIterator(get_pointer(_graph), range.second));
203 }
204 
205 PcpPrimRange
GetPrimRange(PcpRangeType rangeType) const206 PcpPrimIndex::GetPrimRange(PcpRangeType rangeType) const
207 {
208     if (!_graph) {
209         return PcpPrimRange();
210     }
211 
212     // Early out for common case of retrieving entire prim range.
213     if (rangeType == PcpRangeTypeAll) {
214         return PcpPrimRange(
215             PcpPrimIterator(this, 0),
216             PcpPrimIterator(this, _primStack.size()));
217     }
218 
219     const std::pair<size_t, size_t> range =
220         _graph->GetNodeIndexesForRange(rangeType);
221     const size_t startNodeIdx = range.first;
222     const size_t endNodeIdx = range.second;
223 
224     for (size_t startPrimIdx = 0;
225          startPrimIdx < _primStack.size(); ++startPrimIdx) {
226 
227         const Pcp_CompressedSdSite& startPrim = _primStack[startPrimIdx];
228         if (startPrim.nodeIndex >= startNodeIdx &&
229             startPrim.nodeIndex < endNodeIdx) {
230 
231             size_t endPrimIdx = startPrimIdx + 1;
232             for (; endPrimIdx < _primStack.size(); ++endPrimIdx) {
233                 const Pcp_CompressedSdSite& endPrim = _primStack[endPrimIdx];
234                 if (endPrim.nodeIndex >= endNodeIdx) {
235                     break;
236                 }
237             }
238 
239             return PcpPrimRange(
240                 PcpPrimIterator(this, startPrimIdx),
241                 PcpPrimIterator(this, endPrimIdx));
242         }
243     }
244 
245     return PcpPrimRange(PcpPrimIterator(this, _primStack.size()),
246                         PcpPrimIterator(this, _primStack.size()));
247 }
248 
249 PcpPrimRange
GetPrimRangeForNode(const PcpNodeRef & node) const250 PcpPrimIndex::GetPrimRangeForNode(const PcpNodeRef& node) const
251 {
252     PcpPrimIterator firstIt(this, 0);
253     PcpPrimIterator endIt(this, _primStack.size());
254 
255     // XXX: optimization
256     // This is slow, but the prim index doesn't provide us any faster
257     // way to associate a node with prims in the prim stack. We may need
258     // to store indices into the prim stack with each node, similar to
259     // Csd_NamespaceExcerpt and Csd_PrimCache.
260     while (firstIt != endIt && firstIt.GetNode() != node) {
261         ++firstIt;
262     }
263 
264     if (firstIt == endIt) {
265         return PcpPrimRange();
266     }
267 
268     PcpPrimIterator lastIt = firstIt;
269     while (++lastIt != endIt && lastIt.GetNode() == node) {
270         // Do nothing
271     }
272 
273     return PcpPrimRange(firstIt, lastIt);
274 }
275 
276 PcpNodeRef
GetNodeProvidingSpec(const SdfPrimSpecHandle & primSpec) const277 PcpPrimIndex::GetNodeProvidingSpec(const SdfPrimSpecHandle& primSpec) const
278 {
279     return GetNodeProvidingSpec(primSpec->GetLayer(), primSpec->GetPath());
280 }
281 
282 PcpNodeRef
GetNodeProvidingSpec(const SdfLayerHandle & layer,const SdfPath & path) const283 PcpPrimIndex::GetNodeProvidingSpec(
284     const SdfLayerHandle& layer, const SdfPath& path) const
285 {
286     for (const PcpNodeRef &node: GetNodeRange()) {
287         // If the site has the given path and contributes specs then
288         // search for the layer.
289         if (node.CanContributeSpecs() &&
290             node.GetPath() == path    &&
291             node.GetLayerStack()->HasLayer(layer)) {
292             return node;
293         }
294     }
295 
296     return PcpNodeRef();
297 }
298 
299 SdfVariantSelectionMap
ComposeAuthoredVariantSelections() const300 PcpPrimIndex::ComposeAuthoredVariantSelections() const
301 {
302     TRACE_FUNCTION();
303 
304     // Collect the selections according to the prim stack.
305     SdfVariantSelectionMap result;
306     const TfToken field = SdfFieldKeys->VariantSelection;
307     TF_FOR_ALL(i, GetPrimRange()) {
308         Pcp_SdSiteRef site = i.base()._GetSiteRef();
309         const VtValue& value = site.layer->GetField(site.path, field);
310         if (value.IsHolding<SdfVariantSelectionMap>()) {
311             const SdfVariantSelectionMap & vselMap =
312                 value.UncheckedGet<SdfVariantSelectionMap>();
313             result.insert(vselMap.begin(), vselMap.end());
314         }
315     }
316     return result;
317 }
318 
319 std::string
GetSelectionAppliedForVariantSet(const std::string & variantSet) const320 PcpPrimIndex::GetSelectionAppliedForVariantSet(
321     const std::string &variantSet) const
322 {
323     for (const PcpNodeRef &node: GetNodeRange()) {
324         if (node.GetPath().IsPrimVariantSelectionPath()) {
325             std::pair<std::string, std::string> vsel =
326                 node.GetPath().GetVariantSelection();
327             if (vsel.first == variantSet)
328                 return vsel.second;
329         }
330     }
331     return std::string();
332 }
333 
334 ////////////////////////////////////////////////////////////////////////
335 
336 template <class T>
337 static bool
_CheckIfEquivalent(const T * lhsPtr,const T * rhsPtr)338 _CheckIfEquivalent(const T* lhsPtr, const T* rhsPtr)
339 {
340     if (lhsPtr == rhsPtr) {
341         return true;
342     }
343 
344     static const T empty;
345     const T& lhs = (lhsPtr ? *lhsPtr : empty);
346     const T& rhs = (rhsPtr ? *rhsPtr : empty);
347     return lhs == rhs;
348 }
349 
350 bool
IsEquivalentTo(const PcpPrimIndexInputs & inputs) const351 PcpPrimIndexInputs::IsEquivalentTo(const PcpPrimIndexInputs& inputs) const
352 {
353     // Don't consider the PcpCache when determining equivalence, as
354     // prim index computation is independent of the cache.
355     return
356         _CheckIfEquivalent(variantFallbacks, inputs.variantFallbacks) &&
357         _CheckIfEquivalent(includedPayloads, inputs.includedPayloads) &&
358         cull == inputs.cull;
359 }
360 
361 ////////////////////////////////////////////////////////////////////////
362 
363 PcpNodeRef
Append(PcpPrimIndexOutputs && childOutputs,const PcpArc & arcToParent,PcpErrorBasePtr * error)364 PcpPrimIndexOutputs::Append(PcpPrimIndexOutputs&& childOutputs,
365                             const PcpArc& arcToParent,
366                             PcpErrorBasePtr *error)
367 {
368     PcpNodeRef parent = arcToParent.parent;
369     PcpNodeRef newNode = parent.InsertChildSubgraph(
370         childOutputs.primIndex.GetGraph(), arcToParent, error);
371     if (!newNode) {
372         return newNode;
373     }
374 
375     if (childOutputs.primIndex.GetGraph()->HasPayloads()) {
376         parent.GetOwningGraph()->SetHasPayloads(true);
377     }
378     // Append the contents of the child's file format dependency object to
379     // ours.
380     dynamicFileFormatDependency.AppendDependencyData(
381         std::move(childOutputs.dynamicFileFormatDependency));
382 
383     allErrors.insert(
384         allErrors.end(),
385         childOutputs.allErrors.begin(), childOutputs.allErrors.end());
386 
387     if (childOutputs.payloadState == NoPayload) {
388         // Do nothing, keep our payloadState.
389     }
390     else if (payloadState == NoPayload) {
391         // Take the child's payloadState.
392         payloadState = childOutputs.payloadState;
393     }
394     else if (childOutputs.payloadState != payloadState) {
395         // Inconsistent payload state -- issue a warning.
396         TF_WARN("Inconsistent payload states for primIndex <%s> -- "
397                 "parent=%d vs child=%d; taking parent=%d\n",
398                 primIndex.GetPath().GetText(),
399                 payloadState, childOutputs.payloadState, payloadState);
400     }
401 
402     return newNode;
403 }
404 
405 ////////////////////////////////////////////////////////////////////////
406 
407 static void
408 Pcp_BuildPrimIndex(
409     const PcpLayerStackSite & site,
410     const PcpLayerStackSite & rootSite,
411     int ancestorRecursionDepth,
412     bool evaluateImpliedSpecializes,
413     bool evaluateVariants,
414     bool rootNodeShouldContributeSpecs,
415     PcpPrimIndex_StackFrame *previousFrame,
416     const PcpPrimIndexInputs& inputs,
417     PcpPrimIndexOutputs* outputs);
418 
419 static inline bool
420 _NodeCanBeCulled(const PcpNodeRef& node,
421                  const PcpLayerStackSite& rootLayerStack);
422 
423 static void
424 _GatherNodesRecursively(const PcpNodeRef& node,
425                         std::vector<PcpNodeRef> *result);
426 
427 static bool
_HasSpecializesChild(const PcpNodeRef & parent)428 _HasSpecializesChild(const PcpNodeRef & parent)
429 {
430     TF_FOR_ALL(child, Pcp_GetChildrenRange(parent)) {
431         if (PcpIsSpecializeArc((*child).GetArcType()))
432             return true;
433     }
434     return false;
435 }
436 
437 // The implied specializes algorithm wants to start at the
438 // most ancestral parent of the given node that is a specializes
439 // arc, if such a node exists.
440 static PcpNodeRef
_FindStartingNodeForImpliedSpecializes(const PcpNodeRef & node)441 _FindStartingNodeForImpliedSpecializes(const PcpNodeRef& node)
442 {
443     PcpNodeRef specializesNode;
444     for (PcpNodeRef n = node, e = n.GetRootNode(); n != e;
445          n = n.GetParentNode()) {
446         if (PcpIsSpecializeArc(n.GetArcType())) {
447             specializesNode = n;
448         }
449     }
450     return specializesNode;
451 }
452 
453 static bool
_HasClassBasedChild(const PcpNodeRef & parent)454 _HasClassBasedChild(const PcpNodeRef & parent)
455 {
456     TF_FOR_ALL(child, Pcp_GetChildrenRange(parent)) {
457         if (PcpIsClassBasedArc((*child).GetArcType()))
458             return true;
459     }
460     return false;
461 }
462 
463 // Find the starting node of the class hierarchy of which node n is a part.
464 // This is the prim that starts the class chain, aka the 'instance' of the
465 // class hierarchy. Also returns the node for the first class in the
466 // chain that the instance inherits opinions from.
467 //
468 // For example, consider an inherits chain like this: I --> C1 --> C2 --> C3.
469 // When given either C1, C2, or C3, this method will return (I, C1).
470 // What will it do when given I?  Keep reading.
471 //
472 // One tricky aspect is that we need to distinguish nested class
473 // hierarchies at different levels of namespace, aka ancestral classes.
474 // Returning to the example above, consider if I -> ... -> C3 were all
475 // nested as sibling children under a class, G, with instance M:
476 //
477 //          inherits
478 // M ------------------------> G (depth=1)
479 // |                           |
480 // +- I  (depth=1)             +- I  (depth=1)
481 // |  :                        |  :
482 // |  : inherits               |  : inherits
483 // |  v                        |  v
484 // +- C1 (depth=2)             +- C1 (depth=2)
485 // |  :                        |  :
486 // |  : inherits               |  : inherits
487 // |  v                        |  v
488 // +- C2 (depth=2)             +- C2 (depth=2)
489 // |  :                        |  :
490 // |  : inherits               |  : inherits
491 // |  v                        |  v
492 // +- C3 (depth=2)             +- C3 (depth=2)
493 //
494 // Asking for the starting node of M/C1 .. M/C3 should all return (M/I, M/C1).
495 // Asking for the starting node of G/C1 .. G/C3 should all return (G/I, G/C1).
496 //
497 // However, asking for the starting node of G/I should return (M/I, G/I),
498 // because it is walking up the ancestral classes (M->G) instead.
499 //
500 // We distinguish ancestral class chains by considering, for the
501 // nodes being examined, how far they are below the point in namespace
502 // where they were introduced, using GetDepthBelowIntroduction().
503 // This lets us distinguish the hierarchy connecting the children
504 // G/C1, G/C2, and G/C3 (all at depth=2) from the ancestral hierarchy
505 // connecting G/I to M/I, which was introduced at depth=1 and thus up
506 // one level of ancestry.
507 //
508 // Note that this approach also handles a chain of classes that
509 // happen to live at different levels of namespace but which are not
510 // ancestrally connected to one another.  For example, consider if C2
511 // was tucked under a parent scope D:
512 //
513 //          inherits
514 // M ------------------------> G
515 // |                           |
516 // +- I  (depth=1)             +- I  (depth=1)
517 // |  :                        |  :
518 // |  : inherits               |  : inherits
519 // |  v                        |  v
520 // +- C1 (depth=2)             +- C1 (depth=2)
521 // |    :                      |    :
522 // +- D  : inherits            +- D  : inherits
523 // |  |  v                     |  |  v
524 // |  +- C2 (depth=3)          |  +- C2 (depth=3)
525 // |    :                      |    :
526 // |   : inherits              |   : inherits
527 // |  v                        |  v
528 // +- C3 (depth=2)             +- C3 (depth=2)
529 //
530 // Here, G/C1, G/D/C2, and G/C3 are all still identified as part of
531 // the same hierarchy.  C1 and C3 are at depth=2 and have 2 path
532 // components; C2 is at depth=3 and has 3 path components.  Thus,
533 // they all have the same GetDepthBelowIntroduction().
534 //
535 static
536 std::pair<PcpNodeRef, PcpNodeRef>
_FindStartingNodeOfClassHierarchy(const PcpNodeRef & n)537 _FindStartingNodeOfClassHierarchy(const PcpNodeRef& n)
538 {
539     TF_VERIFY(PcpIsClassBasedArc(n.GetArcType()));
540 
541     const int depth = n.GetDepthBelowIntroduction();
542     PcpNodeRef instanceNode = n;
543     PcpNodeRef classNode;
544 
545     while (PcpIsClassBasedArc(instanceNode.GetArcType())
546            && instanceNode.GetDepthBelowIntroduction() == depth) {
547         TF_VERIFY(instanceNode.GetParentNode());
548         classNode = instanceNode;
549         instanceNode = instanceNode.GetParentNode();
550     }
551 
552     return std::make_pair(instanceNode, classNode);
553 }
554 
555 // Given class-based node n, returns the 'starting' node where implied class
556 // processing should begin in order to correctly propagate n through the
557 // graph.
558 //
559 // The starting node will generally be the starting node of the class hierarchy
560 // that n is a part of. For instance, in the simple case:
561 //
562 //    inh     inh     inh
563 //  I ---> C1 ---> C2 ---> C3 ...
564 //
565 // Given any of { C1, C2, C3, ... }, the starting node would be I
566 // (See _FindStartingNodeOfClassHierarchy). This causes the entire class
567 // hierarchy to be propagated as a unit. If we were to propagate each class
568 // individually, it would be as if I inherited directly from C1, C2, and C3,
569 // which is incorrect.
570 //
571 // This gets more complicated when ancestral classes are involved. Basically,
572 // when a class-based node is added, we have to take into account the location
573 // of that node's site relative to the ancestral class to determine where to
574 // start from.
575 //
576 // Consider the prim /M/I/A in the following example:
577 //
578 //          reference
579 // M --------------------------> R
580 // |                             |
581 // +- CA <----+ implied inh.     +- CA <----+ inherit
582 // |          |                  |          |
583 // +- C1 <----|--+ implied inh.  +- C1 <----|--+ inherit
584 // |  |       |  |               |  |       |  |
585 // |  +- A ---+  |               |  +- A ---+  |
586 // |             |               |             |
587 // +- I ---------+               +- I ---------+
588 //    |                             |
589 //    +- A                          +- A
590 //
591 // /M/I/A inherits opinions from /M/C1/A due to the ancestral inherit arc
592 // between /M/I and /M/C1. Then, /M/C1/A inherits opinions from /M/CA.
593 // However, /M/I/A does NOT explicitly inherit opinions from /M/CA. If it did,
594 // opinions from /M/CA would show up twice.
595 //
596 // To ensure /M/I/A does not explicitly inherit from /M/CA, when /R/CA is added
597 // the chain of inherit nodes:        inh          inh
598 //                             /R/I/A ---> /R/C1/A ---> /R/CA
599 //
600 // Must be propagated as a single unit, even though it does not form a single
601 // class hierarchy. So, the starting node would be /R/I/A.
602 //
603 // Contrast that with this case:
604 //
605 //          reference
606 // M --------------------------> R
607 // |                             |
608 // +- C1 <------------+ implied  +- C1 <------------+ inherit
609 // |  |               | inh.     |  |               |
610 // |  +- CA <-+ impl. |          |  +- CA <-+ inh.  |
611 // |  |       | inh.  |          |  |       |       |
612 // |  +- A ---+       |          |  +- A ---+       |
613 // |                  |          |                  |
614 // +- I --------------+          +- I --------------+
615 //    |                             |
616 //    +- CA <-+                     +- CA <-+
617 //    |       | implied inh.        |       | implied inh.
618 //    +- A ---+                     +- A ---+
619 //
620 // In this case, we do expect /M/I/A to explicitly inherit from /M/I/CA.
621 // When /R/C1/CA is added, the chain:         inh          inh
622 //                                     /R/I/A ---> /R/C1/A ---> /R/C1/CA
623 //
624 // Must be propagated as a single unit (Note that this *is* a class hierarchy).
625 // So, the starting node would be /R/I/A.
626 //
627 // This (deceivingly simple) function accounts for all this.
628 // These variations are captured in the TrickyNestedClasses museum cases.
629 static PcpNodeRef
_FindStartingNodeForImpliedClasses(const PcpNodeRef & n)630 _FindStartingNodeForImpliedClasses(const PcpNodeRef& n)
631 {
632     TF_VERIFY(PcpIsClassBasedArc(n.GetArcType()));
633 
634     PcpNodeRef startNode = n;
635 
636     while (PcpIsClassBasedArc(startNode.GetArcType())) {
637         const std::pair<PcpNodeRef, PcpNodeRef> instanceAndClass =
638             _FindStartingNodeOfClassHierarchy(startNode);
639 
640         const PcpNodeRef& instanceNode = instanceAndClass.first;
641         const PcpNodeRef& classNode = instanceAndClass.second;
642 
643         startNode = instanceNode;
644 
645         // If the instance that inherits the class hierarchy is itself
646         // a class-based node, there must be an ancestral inherit arc which
647         // we need to consider. If the class being inherited from is a
648         // namespace child of the ancestral class (the second case shown
649         // above), we're done. Otherwise, we'll iterate again to find the
650         // start of the ancestral class hierarchy.
651         if (PcpIsClassBasedArc(instanceNode.GetArcType())) {
652             const SdfPath ancestralClassPath =
653                 instanceNode.GetPathAtIntroduction();
654             const bool classHierarchyIsChildOfAncestralHierarchy =
655                 classNode.GetPath().HasPrefix(ancestralClassPath);
656 
657             if (classHierarchyIsChildOfAncestralHierarchy) {
658                 break;
659             }
660         }
661     }
662 
663     return startNode;
664 }
665 
666 // This is a convenience function to create a map expression
667 // that maps a given source path to a target node, composing in
668 // relocations and layer offsets if any exist.
669 static PcpMapExpression
_CreateMapExpressionForArc(const SdfPath & sourcePath,const PcpNodeRef & targetNode,const PcpPrimIndexInputs & inputs,const SdfLayerOffset & offset=SdfLayerOffset ())670 _CreateMapExpressionForArc(const SdfPath &sourcePath,
671                            const PcpNodeRef &targetNode,
672                            const PcpPrimIndexInputs &inputs,
673                            const SdfLayerOffset &offset = SdfLayerOffset())
674 {
675     const SdfPath targetPath = targetNode.GetPath().StripAllVariantSelections();
676 
677     PcpMapFunction::PathMap sourceToTargetMap;
678     sourceToTargetMap[sourcePath] = targetPath;
679     PcpMapExpression arcExpr = PcpMapExpression::Constant(
680         PcpMapFunction::Create( sourceToTargetMap, offset ) );
681 
682     // Apply relocations that affect namespace at and below this site.
683     if (!inputs.usd) {
684         arcExpr = targetNode.GetLayerStack()
685             ->GetExpressionForRelocatesAtPath(targetPath)
686             .Compose(arcExpr);
687     }
688 
689     return arcExpr;
690 }
691 
692 // Bitfield of composition arc types
693 enum _ArcFlags {
694     _ArcFlagInherits    = 1<<0,
695     _ArcFlagVariants    = 1<<1,
696     _ArcFlagReferences  = 1<<2,
697     _ArcFlagPayloads    = 1<<3,
698     _ArcFlagSpecializes = 1<<4
699 };
700 
701 // Scan a node's specs for presence of fields describing composition arcs.
702 // This is used as a preflight check to confirm presence of these arcs
703 // before performing additional work to evaluate them.
704 // Return a bitmask of the arc types found.
705 inline static size_t
_ScanArcs(PcpNodeRef const & node)706 _ScanArcs(PcpNodeRef const& node)
707 {
708     size_t arcs = 0;
709     SdfPath const& path = node.GetPath();
710     for (SdfLayerRefPtr const& layer: node.GetLayerStack()->GetLayers()) {
711         if (!layer->HasSpec(path)) {
712             continue;
713         }
714         if (layer->HasField(path, SdfFieldKeys->InheritPaths)) {
715             arcs |= _ArcFlagInherits;
716         }
717         if (layer->HasField(path, SdfFieldKeys->VariantSetNames)) {
718             arcs |= _ArcFlagVariants;
719         }
720         if (layer->HasField(path, SdfFieldKeys->References)) {
721             arcs |= _ArcFlagReferences;
722         }
723         if (layer->HasField(path, SdfFieldKeys->Payload)) {
724             arcs |= _ArcFlagPayloads;
725         }
726         if (layer->HasField(path, SdfFieldKeys->Specializes)) {
727             arcs |= _ArcFlagSpecializes;
728         }
729     }
730     return arcs;
731 }
732 
733 ////////////////////////////////////////////////////////////////////////
734 
735 namespace {
736 
737 /// A task to perform on a particular node.
738 struct Task {
739     /// This enum must be in evaluation priority order.
740     enum Type {
741         EvalNodeRelocations,
742         EvalImpliedRelocations,
743         EvalNodeReferences,
744         EvalNodePayload,
745         EvalNodeInherits,
746         EvalImpliedClasses,
747         EvalNodeSpecializes,
748         EvalImpliedSpecializes,
749         EvalNodeVariantSets,
750         EvalNodeVariantAuthored,
751         EvalNodeVariantFallback,
752         EvalNodeVariantNoneFound,
753         None
754     };
755 
756     // This sorts tasks in priority order from lowest priority to highest
757     // priority, so highest priority tasks come last.
758     struct PriorityOrder {
operator ()__anone749f6d20111::Task::PriorityOrder759         inline bool operator()(const Task& a, const Task& b) const {
760             if (a.type != b.type) {
761                 return a.type > b.type;
762             }
763             // Node strength order is costly to compute, so avoid it for
764             // arcs with order-independent results.
765             switch (a.type) {
766             case EvalNodePayload:
767                 // Payloads can have dynamic file format arguments that depend
768                 // on non-local information, so we must process these in
769                 // strength order.
770                 return PcpCompareNodeStrength(a.node, b.node) == 1;
771             case EvalNodeVariantAuthored:
772             case EvalNodeVariantFallback:
773                 // Variant selections can depend on non-local information
774                 // so we must visit them in strength order.
775                 if (a.node != b.node) {
776                     return PcpCompareNodeStrength(a.node, b.node) == 1;
777                 } else {
778                     // Lower-number vsets have strength priority.
779                     return a.vsetNum > b.vsetNum;
780                 }
781             case EvalNodeVariantNoneFound:
782                 // In the none-found case, we only need to ensure a consistent
783                 // and distinct order for distinct tasks, the specific order can
784                 // be arbitrary.
785                 if (a.node != b.node) {
786                     return a.node > b.node;
787                 } else {
788                     return a.vsetNum > b.vsetNum;
789                 }
790             case EvalImpliedClasses:
791                 // When multiple implied classes tasks are queued for different
792                 // nodes, ordering matters in that ancestor nodes must be
793                 // processed after their descendants. This minimally guarantees
794                 // that by relying on an undocumented implementation detail
795                 // of the less than operator, which we use for performance
796                 // rather than doing a more expensive graph traversal.
797                 //
798                 // The less than operator compares the nodes' index in
799                 // the node graph. Each node's index is assigned incrementally
800                 // as its added to its parent in the graph so b.node having a
801                 // greater index than a.node guarantees that b.node is not an
802                 // ancestor of a.node.
803                 //
804                 // Note that while the composition cases where this order
805                 // matters are extremely rare, they do come up. The museum case
806                 // ImpliedAndAncestralInherits_ComplexEvaluation details the
807                 // minimal (though still complex) case that requires this
808                 // ordering be correct and should be referred to if a detailed
809                 // explanation is desired.
810                 return b.node > a.node;
811             default:
812                 // Arbitrary order
813                 return a.node > b.node;
814             }
815         }
816     };
817 
Task__anone749f6d20111::Task818     explicit Task(Type type, const PcpNodeRef& node = PcpNodeRef())
819         : type(type)
820         , vsetNum(0)
821         , node(node)
822     { }
823 
Task__anone749f6d20111::Task824     Task(Type type, const PcpNodeRef& node,
825          std::string &&vsetName, int vsetNum)
826         : type(type)
827         , vsetNum(vsetNum)
828         , node(node)
829         , vsetName(std::move(vsetName))
830     { }
831 
Task__anone749f6d20111::Task832     Task(Type type, const PcpNodeRef& node,
833          std::string const &vsetName, int vsetNum)
834         : type(type)
835         , vsetNum(vsetNum)
836         , node(node)
837         , vsetName(vsetName)
838     { }
839 
operator ==__anone749f6d20111::Task840     inline bool operator==(Task const &rhs) const {
841         return type == rhs.type && node == rhs.node &&
842             vsetName == rhs.vsetName && vsetNum == rhs.vsetNum;
843     }
844 
operator !=__anone749f6d20111::Task845     inline bool operator!=(Task const &rhs) const { return !(*this == rhs); }
846 
swap(Task & lhs,Task & rhs)847     friend void swap(Task &lhs, Task &rhs) {
848         std::swap(lhs.type, rhs.type);
849         std::swap(lhs.node, rhs.node);
850         lhs.vsetName.swap(rhs.vsetName);
851         std::swap(lhs.vsetNum, rhs.vsetNum);
852     }
853 
854     // Stream insertion operator for debugging.
operator <<(std::ostream & os,Task const & task)855     friend std::ostream &operator<<(std::ostream &os, Task const &task) {
856         os << TfStringPrintf(
857             "Task(type=%s, nodePath=<%s>, nodeSite=<%s>",
858             TfEnum::GetName(task.type).c_str(),
859             task.node.GetPath().GetText(),
860             TfStringify(task.node.GetSite()).c_str());
861         if (!task.vsetName.empty()) {
862             os << TfStringPrintf(", vsetName=%s, vsetNum=%d",
863                                  task.vsetName.c_str(), task.vsetNum);
864         }
865         return os << ")";
866     }
867 
868     Type type;
869     int vsetNum; // << only for variant tasks.
870     PcpNodeRef node;
871     std::string vsetName; // << only for variant tasks.
872 };
873 
874 }
875 
TF_REGISTRY_FUNCTION(TfEnum)876 TF_REGISTRY_FUNCTION(TfEnum) {
877     TF_ADD_ENUM_NAME(Task::EvalNodeRelocations);
878     TF_ADD_ENUM_NAME(Task::EvalImpliedRelocations);
879     TF_ADD_ENUM_NAME(Task::EvalNodeReferences);
880     TF_ADD_ENUM_NAME(Task::EvalNodePayload);
881     TF_ADD_ENUM_NAME(Task::EvalNodeInherits);
882     TF_ADD_ENUM_NAME(Task::EvalImpliedClasses);
883     TF_ADD_ENUM_NAME(Task::EvalNodeSpecializes);
884     TF_ADD_ENUM_NAME(Task::EvalImpliedSpecializes);
885     TF_ADD_ENUM_NAME(Task::EvalNodeVariantSets);
886     TF_ADD_ENUM_NAME(Task::EvalNodeVariantAuthored);
887     TF_ADD_ENUM_NAME(Task::EvalNodeVariantFallback);
888     TF_ADD_ENUM_NAME(Task::EvalNodeVariantNoneFound);
889     TF_ADD_ENUM_NAME(Task::None);
890 }
891 
892 // Pcp_PrimIndexer is used during prim cache population to track which
893 // tasks remain to finish building the graph.  As new nodes are added,
894 // we add task entries to this structure, which ensures that we
895 // process them in an appropriate order.
896 //
897 // This is the high-level control logic for the population algorithm.
898 // At each step, it determines what will happen next.
899 //
900 // Notes on the algorithm:
901 //
902 // - We can process inherits, and implied inherits in any order
903 //   any order, as long as we finish them before moving on to
904 //   deciding references and variants.  This is because evaluating any
905 //   arcs of the former group does not affect how we evaluate other arcs
906 //   of that group -- but they do affect how we evaluate references,
907 //   variants and payloads.  Specifically, they may introduce information
908 //   needed to evaluate references, opinions with variants selections,
909 //   or overrides to the payload target path.
910 //
911 //   It is important to complete evaluation of the former group
912 //   before proceeding to references/variants/payloads so that we gather
913 //   as much information as available before deciding those arcs.
914 //
915 // - We only want to process a payload when there is nothing else
916 //   left to do.  Again, this is to ensure that we have discovered
917 //   any opinions which may affect the payload arc, including
918 //   those inside variants.
919 //
920 // - At each step, we may introduce a new node that returns us
921 //   to an earlier stage of the algorithm.  For example, a payload
922 //   may introduce nodes that contain references, inherits, etc.
923 //   We need to process them to completion before we return to
924 //   check variants, and so on.
925 //
926 struct Pcp_PrimIndexer
927 {
928     // The root site for the prim indexing process.
929     const PcpLayerStackSite rootSite;
930 
931     // Total depth of ancestral recursion.
932     const int ancestorRecursionDepth;
933 
934     // Context for the prim index we are building.
935     const PcpPrimIndexInputs &inputs;
936     PcpPrimIndexOutputs* const outputs;
937 
938     // The previousFrame tracks information across recursive invocations
939     // of Pcp_BuildPrimIndex() so that recursive indexes can query
940     // outer indexes.  This is used for cycle detection as well as
941     // composing the variant selection.
942     PcpPrimIndex_StackFrame* const previousFrame;
943 
944     // Open tasks, in priority order
945     using _TaskQueue = std::vector<Task>;
946     _TaskQueue tasks;
947     bool tasksSorted;
948 
949     const bool evaluateImpliedSpecializes;
950     const bool evaluateVariants;
951 
952 #ifdef PCP_DIAGNOSTIC_VALIDATION
953     /// Diagnostic helper to make sure we don't revisit sites.
954     PcpNodeRefHashSet seen;
955 #endif // PCP_DIAGNOSTIC_VALIDATION
956 
Pcp_PrimIndexerPcp_PrimIndexer957     Pcp_PrimIndexer(PcpPrimIndexInputs const &inputs_,
958                     PcpPrimIndexOutputs *outputs_,
959                     PcpLayerStackSite rootSite_,
960                     int ancestorRecursionDepth_,
961                     PcpPrimIndex_StackFrame *previousFrame_=nullptr,
962                     bool evaluateImpliedSpecializes_=true,
963                     bool evaluateVariants_=true)
964         : rootSite(rootSite_)
965         , ancestorRecursionDepth(ancestorRecursionDepth_)
966         , inputs(inputs_)
967         , outputs(outputs_)
968         , previousFrame(previousFrame_)
969         , tasksSorted(true)
970         , evaluateImpliedSpecializes(evaluateImpliedSpecializes_)
971         , evaluateVariants(evaluateVariants_)
972     {
973     }
974 
GetOriginatingIndexPcp_PrimIndexer975     inline PcpPrimIndex const *GetOriginatingIndex() const {
976         return _GetOriginatingIndex(previousFrame, outputs);
977     }
978 
AddTaskPcp_PrimIndexer979     void AddTask(Task &&task) {
980         if (tasks.empty()) {
981             tasks.reserve(8); // Typically we have about this many tasks, and
982                               // this results in a single 256 byte allocation.
983             tasks.push_back(std::move(task));
984         }
985         else {
986             if (tasksSorted) {
987                 // If same task, skip.
988                 if (tasks.back() != task) {
989                     tasks.push_back(std::move(task));
990                     // Check if we've violated the order.  We've violated it if
991                     // the comparator says the new task is less than the
992                     // previously last task.
993                     Task::PriorityOrder comp;
994                     tasksSorted =
995                         !comp(tasks[tasks.size()-1], tasks[tasks.size()-2]);
996                 }
997             }
998             else {
999                 tasks.push_back(std::move(task));
1000             }
1001         }
1002     }
1003 
1004     // Select the next task to perform.
PopTaskPcp_PrimIndexer1005     Task PopTask() {
1006         Task task(Task::Type::None);
1007         if (!tasks.empty()) {
1008             if (!tasksSorted) {
1009                 Task::PriorityOrder comp;
1010                 std::sort(tasks.begin(), tasks.end(), comp);
1011                 tasks.erase(
1012                     std::unique(tasks.begin(), tasks.end()), tasks.end());
1013                 tasksSorted = true;
1014             }
1015             task = std::move(tasks.back());
1016             tasks.pop_back();
1017         }
1018         return task;
1019     }
1020 
1021     // Add this node and its children to the task queues.
_AddTasksForNodeRecursivelyPcp_PrimIndexer1022     void _AddTasksForNodeRecursively(
1023         const PcpNodeRef& n,
1024         bool skipCompletedNodesForAncestralOpinions,
1025         bool skipCompletedNodesForImpliedSpecializes,
1026         bool isUsd)
1027     {
1028 #ifdef PCP_DIAGNOSTIC_VALIDATION
1029         TF_VERIFY(seen.count(n) == 0, "Already processed <%s>",
1030                   n.GetPath().GetText());
1031         seen.insert(n);
1032 #endif // PCP_DIAGNOSTIC_VALIDATION
1033 
1034         TF_FOR_ALL(child, Pcp_GetChildrenRange(n)) {
1035             _AddTasksForNodeRecursively(
1036                 *child,
1037                 skipCompletedNodesForAncestralOpinions,
1038                 skipCompletedNodesForImpliedSpecializes, isUsd);
1039         }
1040 
1041         // If the node does not have specs or cannot contribute specs,
1042         // we can avoid even enqueueing certain kinds of tasks that will
1043         // end up being no-ops.
1044         const bool contributesSpecs = n.HasSpecs() && n.CanContributeSpecs();
1045 
1046         // Preflight scan for arc types that are present in specs.
1047         // This reduces pressure on the task queue, and enables more
1048         // data access locality, since we avoid interleaving tasks that
1049         // re-visit sites later only to determine there is no work to do.
1050         const size_t arcMask = contributesSpecs ? _ScanArcs(n) : 0;
1051 
1052         // If the caller tells us the new node and its children were already
1053         // indexed, we do not need to re-scan them for certain arcs based on
1054         // what was already completed.
1055         if (skipCompletedNodesForImpliedSpecializes) {
1056             // In this case, we only need to add tasks that come after
1057             // implied specializes.
1058             if (evaluateVariants && (arcMask & _ArcFlagVariants)) {
1059                 AddTask(Task(Task::Type::EvalNodeVariantSets, n));
1060             }
1061         } else {
1062             // Payloads and variants have expensive
1063             // sorting semantics, so do a preflight check
1064             // to see if there is any work to do.
1065             if (evaluateVariants && (arcMask & _ArcFlagVariants)) {
1066                 AddTask(Task(Task::Type::EvalNodeVariantSets, n));
1067             }
1068             if (!skipCompletedNodesForAncestralOpinions) {
1069                 // In this case, we only need to add tasks that weren't
1070                 // evaluated during the recursive prim indexing for
1071                 // ancestral opinions.
1072                 if (arcMask & _ArcFlagSpecializes) {
1073                     AddTask(Task(Task::Type::EvalNodeSpecializes, n));
1074                 }
1075                 if (arcMask & _ArcFlagInherits) {
1076                     AddTask(Task(Task::Type::EvalNodeInherits, n));
1077                 }
1078                 if (arcMask & _ArcFlagPayloads) {
1079                     AddTask(Task(Task::Type::EvalNodePayload, n));
1080                 }
1081                 if (arcMask & _ArcFlagReferences) {
1082                     AddTask(Task(Task::Type::EvalNodeReferences, n));
1083                 }
1084                 if (!isUsd) {
1085                     AddTask(Task(Task::Type::EvalNodeRelocations, n));
1086                 }
1087             }
1088             if (!isUsd && n.GetArcType() == PcpArcTypeRelocate) {
1089                 AddTask(Task(Task::Type::EvalImpliedRelocations, n));
1090             }
1091         }
1092     }
1093 
AddTasksForNodePcp_PrimIndexer1094     void AddTasksForNode(
1095         const PcpNodeRef& n,
1096         bool skipCompletedNodesForAncestralOpinions = false,
1097         bool skipCompletedNodesForImpliedSpecializes = false) {
1098 
1099         // Any time we add an edge to the graph, we may need to update
1100         // implied class edges.
1101         if (!skipCompletedNodesForImpliedSpecializes) {
1102             if (PcpIsClassBasedArc(n.GetArcType())) {
1103                 // The new node is itself class-based.  Find the starting
1104                 // prim of the chain of classes the node is a part of, and
1105                 // propagate the entire chain as a single unit.
1106                 if (PcpNodeRef base = _FindStartingNodeForImpliedClasses(n)) {
1107                     AddTask(Task(Task::Type::EvalImpliedClasses, base));
1108                 }
1109             } else if (_HasClassBasedChild(n)) {
1110                 // The new node is not class-based -- but it has class-based
1111                 // children.  Such children represent inherits found during the
1112                 // recursive computation of the node's subgraph.  We need to
1113                 // pick them up and continue propagating them now that we are
1114                 // merging the subgraph into the parent graph.
1115                 AddTask(Task(Task::Type::EvalImpliedClasses, n));
1116             }
1117             if (evaluateImpliedSpecializes) {
1118                 if (PcpNodeRef base =
1119                     _FindStartingNodeForImpliedSpecializes(n)) {
1120                     // We're adding a new specializes node or a node beneath
1121                     // a specializes node.  Add a task to propagate the subgraph
1122                     // beneath this node to the appropriate location.
1123                     AddTask(Task(Task::Type::EvalImpliedSpecializes, base));
1124                 }
1125                 else if (_HasSpecializesChild(n)) {
1126                     // The new node is not a specializes node or beneath a
1127                     // specializes node, but has specializes children.
1128                     // Such children represent arcs found during the recursive
1129                     // computation of the node's subgraph.  We need to pick them
1130                     // up and continue propagating them now that we are
1131                     // merging the subgraph into the parent graph.
1132                     AddTask(Task(Task::Type::EvalImpliedSpecializes, n));
1133                 }
1134             }
1135         }
1136 
1137         // Recurse over all of the rest of the nodes.  (We assume that any
1138         // embedded class hierarchies have already been propagated to
1139         // the top node n, letting us avoid redundant work.)
1140         _AddTasksForNodeRecursively(
1141             n, skipCompletedNodesForAncestralOpinions,
1142             skipCompletedNodesForImpliedSpecializes, inputs.usd);
1143 
1144         _DebugPrintTasks("After AddTasksForNode");
1145     }
1146 
_DebugPrintTasksPcp_PrimIndexer1147     inline void _DebugPrintTasks(char const *label) const {
1148 #if 0
1149         printf("-- %s ----------------\n", label);
1150         for (auto iter = tasks.rbegin(); iter != tasks.rend(); ++iter) {
1151             printf("%s\n", TfStringify(*iter).c_str());
1152         }
1153         printf("----------------\n");
1154 #endif
1155     }
1156 
1157     // Retry any variant sets that previously failed to find an authored
1158     // selection to take into account newly-discovered opinions.
1159     // EvalNodeVariantNoneFound is a placeholder representing variants
1160     // that were previously visited and yielded no variant; it exists
1161     // solely for this function to be able to find and retry them.
RetryVariantTasksPcp_PrimIndexer1162     void RetryVariantTasks() {
1163         // Optimization: We know variant tasks are the lowest priority, and
1164         // therefore sorted to the front of this container.  We promote the
1165         // leading non-authored variant tasks to authored tasks, then merge them
1166         // with any existing authored tasks.
1167         auto nonAuthVariantsEnd = std::find_if_not(
1168             tasks.begin(), tasks.end(),
1169             [](Task const &t) {
1170                 return t.type == Task::Type::EvalNodeVariantFallback ||
1171                        t.type == Task::Type::EvalNodeVariantNoneFound;
1172             });
1173 
1174         if (nonAuthVariantsEnd == tasks.begin()) {
1175             // No variant tasks present.
1176             return;
1177         }
1178 
1179         auto authVariantsEnd = std::find_if_not(
1180             nonAuthVariantsEnd, tasks.end(),
1181             [](Task const &t) {
1182                 return t.type == Task::Type::EvalNodeVariantAuthored;
1183             });
1184 
1185         // Now we've split tasks into three ranges:
1186         // non-authored variant tasks : [begin, nonAuthVariantsEnd)
1187         // authored variant tasks     : [nonAuthVariantsEnd, authVariantsEnd)
1188         // other tasks                : [authVariantsEnd, end)
1189         //
1190         // We want to change the non-authored variant tasks' types to be
1191         // authored instead, and then sort them in with the othered authored
1192         // tasks.
1193 
1194         // Change types.
1195         std::for_each(tasks.begin(), nonAuthVariantsEnd,
1196                       [](Task &t) {
1197                           t.type = Task::Type::EvalNodeVariantAuthored;
1198                       });
1199 
1200         // Sort and merge.
1201         Task::PriorityOrder comp;
1202         std::sort(tasks.begin(), nonAuthVariantsEnd, comp);
1203         std::inplace_merge(
1204             tasks.begin(), nonAuthVariantsEnd, authVariantsEnd, comp);
1205 
1206         // XXX Is it possible to have dupes here?  blevin?
1207         tasks.erase(
1208             std::unique(tasks.begin(), authVariantsEnd), authVariantsEnd);
1209 
1210 #ifdef PCP_DIAGNOSTIC_VALIDATION
1211         TF_VERIFY(std::is_sorted(tasks.begin(), tasks.end(), comp));
1212 #endif // PCP_DIAGNOSTIC_VALIDATION
1213 
1214         _DebugPrintTasks("After RetryVariantTasks");
1215     }
1216 
1217     // Convenience function to record an error both in this primIndex's
1218     // local errors vector and the allErrors vector.
RecordErrorPcp_PrimIndexer1219     void RecordError(const PcpErrorBasePtr &err) {
1220         RecordError(err, &outputs->primIndex, &outputs->allErrors);
1221     }
1222 
1223     // Convenience function to record an error both in this primIndex's
1224     // local errors vector and the allErrors vector.
RecordErrorPcp_PrimIndexer1225     static void RecordError(const PcpErrorBasePtr &err,
1226                             PcpPrimIndex *primIndex,
1227                             PcpErrorVector *allErrors) {
1228         // Capacity errors are reported at most once.
1229         if (err->ShouldReportAtMostOnce()) {
1230             for (PcpErrorBasePtr const& e: *allErrors) {
1231                 if (e->errorType == err->errorType) {
1232                     // Already reported.
1233                     return;
1234                 }
1235             }
1236         }
1237         allErrors->push_back(err);
1238         if (!primIndex->_localErrors) {
1239             primIndex->_localErrors.reset(new PcpErrorVector);
1240         }
1241         primIndex->_localErrors->push_back(err);
1242     }
1243 };
1244 
1245 // Returns true if there is a prim spec associated with the specified node
1246 // or any of its descendants.
1247 static bool
_PrimSpecExistsUnderNode(const PcpNodeRef & node,Pcp_PrimIndexer * indexer)1248 _PrimSpecExistsUnderNode(
1249     const PcpNodeRef &node,
1250     Pcp_PrimIndexer *indexer)
1251 {
1252     // Check for prim specs at this node's site.
1253     if (node.HasSpecs())
1254         return true;
1255 
1256     // Recursively check this node's children.
1257     TF_FOR_ALL(child, Pcp_GetChildrenRange(node)) {
1258         if (_PrimSpecExistsUnderNode(*child, indexer))
1259             return true;
1260     }
1261     return false;
1262 }
1263 
1264 // Mark an entire subtree of nodes as inert.
1265 static void
_InertSubtree(PcpNodeRef node)1266 _InertSubtree(
1267     PcpNodeRef node)
1268 {
1269     node.SetInert(true);
1270     TF_FOR_ALL(child, Pcp_GetChildrenRange(node)) {
1271         _InertSubtree(*child);
1272     }
1273 }
1274 
1275 inline static bool
_HasAncestorCycle(const PcpLayerStackSite & parentNodeSite,const PcpLayerStackSite & childNodeSite)1276 _HasAncestorCycle(
1277     const PcpLayerStackSite& parentNodeSite,
1278     const PcpLayerStackSite& childNodeSite )
1279 {
1280     // For example, a cycle exists if in the same layer stack
1281     // the prim at /A/B adds a child arc to /A or the prim at
1282     // /A adds a child arc to /A/B.
1283     return parentNodeSite.layerStack == childNodeSite.layerStack
1284         && (parentNodeSite.path.HasPrefix(childNodeSite.path)
1285             || childNodeSite.path.HasPrefix(parentNodeSite.path));
1286 }
1287 
1288 inline static bool
_FindAncestorCycleInParentGraph(const PcpNodeRef & parentNode,const PcpLayerStackSite & childNodeSite)1289 _FindAncestorCycleInParentGraph(const PcpNodeRef &parentNode,
1290                                 const PcpLayerStackSite& childNodeSite)
1291 {
1292     // We compare the targeted site to each previously-visited site:
1293     for (PcpNodeRef node = parentNode; node; node = node.GetParentNode()) {
1294         if (_HasAncestorCycle(node.GetSite(), childNodeSite)) {
1295             return true;
1296         }
1297     }
1298     return false;
1299 }
1300 
1301 static bool
_IsImpliedClassBasedArc(PcpArcType arcType,const PcpNodeRef & parent,const PcpNodeRef & origin)1302 _IsImpliedClassBasedArc(
1303     PcpArcType arcType,
1304     const PcpNodeRef &parent,
1305     const PcpNodeRef &origin)
1306 {
1307     return PcpIsClassBasedArc(arcType) && parent != origin;
1308 }
1309 
1310 static bool
_IsImpliedClassBasedArc(const PcpNodeRef & node)1311 _IsImpliedClassBasedArc(const PcpNodeRef& node)
1312 {
1313     return _IsImpliedClassBasedArc(
1314         node.GetArcType(), node.GetParentNode(), node.GetOriginNode());
1315 }
1316 
1317 // Check that no cycles are being introduced by adding this arc.
1318 static PcpErrorArcCyclePtr
_CheckForCycle(const PcpNodeRef & parent,const PcpNodeRef & origin,PcpArcType arcType,const PcpLayerStackSite & childSite,PcpPrimIndex_StackFrame * previousFrame)1319 _CheckForCycle(
1320     const PcpNodeRef &parent,
1321     const PcpNodeRef &origin,
1322     PcpArcType arcType,
1323     const PcpLayerStackSite &childSite,
1324     PcpPrimIndex_StackFrame *previousFrame )
1325 {
1326     // XXX:RelocatesSourceNodes: Don't check for cycles in placeholder
1327     // implied class nodes under relocates. These children of Relocates
1328     // nodes can yield invalid sites, because the arc will include
1329     // the effect of relocations but the Relocates node is the source
1330     // path. In this case, we won't be adding opinions anyway, so we
1331     // don't need to check for cycles.
1332     if (_IsImpliedClassBasedArc(arcType, parent, origin)) {
1333         // Skip across parent class arcs.
1334         PcpPrimIndex_StackFrameIterator j(parent, previousFrame);
1335         while (j.node
1336                && _IsImpliedClassBasedArc(j.GetArcType(), parent, origin)) {
1337             j.Next();
1338         }
1339         if (j.node && j.GetArcType() == PcpArcTypeRelocate) {
1340             // This is a class arc under a relocate.
1341             // Do not count this as a cycle.
1342             return PcpErrorArcCyclePtr();
1343         }
1344     }
1345 
1346     // Don't check for cycles for variant arcs, since these just
1347     // represent the selection of a particular branch of scene
1348     // description. For example, adding a variant selection child
1349     // /A{v=sel} to parent /A is not a cycle, even though the child
1350     // path is prefixed by the parent.
1351     if (arcType == PcpArcTypeVariant) {
1352         return PcpErrorArcCyclePtr();
1353     }
1354 
1355     bool foundCycle = false;
1356 
1357     // If the the current graph is a subgraph that is being recursively built
1358     // for another node, we have to crawl up the parent graph as well to check
1359     // for cycles.
1360     PcpLayerStackSite childSiteInStackFrame = childSite;
1361     for (PcpPrimIndex_StackFrameIterator it(parent, previousFrame);
1362          it.node; it.NextFrame()) {
1363 
1364         // Check for a cycle in the parent's current graph.
1365         if (_FindAncestorCycleInParentGraph(it.node, childSiteInStackFrame)) {
1366             foundCycle = true;
1367             break;
1368         }
1369 
1370         // In some cases we need to convert the child site's path into the
1371         // path it will have when its owning subgraph is added to the parent
1372         // graph in order to correctly check for cycles. This is best
1373         // explained with a simple example:
1374         //
1375         //    /A
1376         //    /A/B
1377         //    /A/C (ref = /D/B)
1378         //
1379         //    /D (ref = /A)
1380         //
1381         // If you compute the prim index /D/C it will have a reference arc
1382         // to /A/C because /D references /A. When the index then goes to
1383         // to add the reference arc to /D/B from /A/C it initiates a
1384         // recursive subgraph computation of /D/B.
1385         //
1386         // When we build the subgraph prim index for /D/B, the first step
1387         // is to compute its namespace ancestor which builds an index for
1388         // /D. When the index for /D tries to add its reference arc to /A,
1389         // we end up here in this function to check for cycles.
1390         //
1391         // If we just checked for cycles using the child site's current
1392         // path, /A, we'd find an ancestor cycle when we go up to the parent
1393         // graph for the node /A/C. However, the requested subgraph is for
1394         // /D/B not /D, so the child site will actually be /A/B instead of
1395         // /A when the subgraph reference arc is actually added for node
1396         // /A/C. Adding a node /A/B does not introduce any cycles.
1397         if (it.previousFrame) {
1398             const SdfPath& requestedPathForCurrentGraph =
1399                 it.previousFrame->requestedSite.path;
1400             const SdfPath& currentPathForCurrentGraph =
1401                 it.node.GetRootNode().GetPath();
1402 
1403             childSiteInStackFrame.path =
1404                 requestedPathForCurrentGraph.ReplacePrefix(
1405                     currentPathForCurrentGraph,
1406                     childSiteInStackFrame.path);
1407         }
1408     }
1409 
1410     if (foundCycle) {
1411         PcpErrorArcCyclePtr err = PcpErrorArcCycle::New();
1412         // Traverse the parent chain to build a list of participating arcs.
1413         PcpSiteTrackerSegment seg;
1414         for (PcpPrimIndex_StackFrameIterator i(parent, previousFrame);
1415              i.node; i.Next()) {
1416             seg.site = i.node.GetSite();
1417             seg.arcType = i.GetArcType();
1418             err->cycle.push_back(seg);
1419         }
1420         // Reverse the list to order arcs from root to leaf.
1421         std::reverse(err->cycle.begin(), err->cycle.end());
1422         // Retain the root site.
1423         err->rootSite = err->cycle.front().site;
1424         // There is no node for the last site in the chain, so report it
1425         // directly.
1426         seg.site = childSite;
1427         seg.arcType = arcType;
1428         err->cycle.push_back(seg);
1429         return err;
1430     }
1431 
1432     return PcpErrorArcCyclePtr();
1433 }
1434 
1435 // Add an arc of the given type from the parent node to the child site,
1436 // and track any new tasks that result.  Return the new node.
1437 //
1438 // If includeAncestralOpinions is specified, recursively build and
1439 // include the ancestral opinions that would affect the new site.
1440 //
1441 static PcpNodeRef
_AddArc(const PcpArcType arcType,PcpNodeRef parent,PcpNodeRef origin,const PcpLayerStackSite & site,PcpMapExpression mapExpr,int arcSiblingNum,int namespaceDepth,bool directNodeShouldContributeSpecs,bool includeAncestralOpinions,bool requirePrimAtTarget,bool skipDuplicateNodes,bool skipImpliedSpecializesCompletedNodes,Pcp_PrimIndexer * indexer)1442 _AddArc(
1443     const PcpArcType arcType,
1444     PcpNodeRef parent,
1445     PcpNodeRef origin,
1446     const PcpLayerStackSite & site,
1447     PcpMapExpression mapExpr,
1448     int arcSiblingNum,
1449     int namespaceDepth,
1450     bool directNodeShouldContributeSpecs,
1451     bool includeAncestralOpinions,
1452     bool requirePrimAtTarget,
1453     bool skipDuplicateNodes,
1454     bool skipImpliedSpecializesCompletedNodes,
1455     Pcp_PrimIndexer *indexer )
1456 {
1457     PCP_INDEXING_PHASE(
1458         indexer,
1459         parent,
1460         "Adding new %s arc to %s to %s",
1461         TfEnum::GetDisplayName(arcType).c_str(),
1462         Pcp_FormatSite(site).c_str(),
1463         Pcp_FormatSite(parent.GetSite()).c_str());
1464 
1465     PCP_INDEXING_MSG(
1466         indexer,
1467         parent,
1468         "origin: %s\n"
1469         "arcSiblingNum: %d\n"
1470         "namespaceDepth: %d\n"
1471         "directNodeShouldContributeSpecs: %s\n"
1472         "includeAncestralOpinions: %s\n"
1473         "requirePrimAtTarget: %s\n"
1474         "skipDuplicateNodes: %s\n"
1475         "skipImpliedSpecializesCompletedNodes: %s\n\n",
1476         origin ? Pcp_FormatSite(origin.GetSite()).c_str() : "<None>",
1477         arcSiblingNum,
1478         namespaceDepth,
1479         directNodeShouldContributeSpecs ? "true" : "false",
1480         includeAncestralOpinions ? "true" : "false",
1481         requirePrimAtTarget ? "true" : "false",
1482         skipDuplicateNodes ? "true" : "false",
1483         skipImpliedSpecializesCompletedNodes ? "true" : "false");
1484 
1485     if (!TF_VERIFY(!mapExpr.IsNull())) {
1486         return PcpNodeRef();
1487     }
1488 
1489     // Check for cycles.  If found, report an error and bail.
1490     if (PcpErrorArcCyclePtr err =
1491         _CheckForCycle(parent, origin, arcType, site, indexer->previousFrame)) {
1492         indexer->RecordError(err);
1493         return PcpNodeRef();
1494     }
1495 
1496     // We (may) want to determine whether adding this arc would cause the
1497     // final prim index to have nodes with the same site. If so, we need to
1498     // skip over it, as adding the arc would cause duplicate opinions in the
1499     // final prim index.
1500     //
1501     // This is tricky -- we need to search the current graph being built as
1502     // well as those in the previous recursive calls to Pcp_BuildPrimIndex.
1503     if (indexer->previousFrame) {
1504         skipDuplicateNodes |= indexer->previousFrame->skipDuplicateNodes;
1505     }
1506 
1507     if (skipDuplicateNodes) {
1508         PcpLayerStackSite siteToAddInCurrentGraph = site;
1509 
1510         bool foundDuplicateNode = false;
1511         for (PcpPrimIndex_StackFrameIterator it(parent, indexer->previousFrame);
1512              it.node; it.NextFrame()) {
1513 
1514             PcpPrimIndex_GraphPtr currentGraph = it.node.GetOwningGraph();
1515             if (currentGraph->GetNodeUsingSite(siteToAddInCurrentGraph)) {
1516                 foundDuplicateNode = true;
1517                 break;
1518             }
1519 
1520             // The graph in the previous stack frame may be at a different
1521             // level of namespace than the current graph. In order to search
1522             // it for this new node's site, we have to figure out what this
1523             // node's site would be once it was added to the previous graph.
1524             // Let's say we're in a recursive call to Pcp_BuildPrimIndex for
1525             // prim /A/B, and that we're processing ancestral opinions for /A.
1526             // In doing so, we're adding an arc to site /C. That would be:
1527             //
1528             //   - requestedPathForCurrentGraph = /A/B
1529             //     currentPathForCurrentGraph = /A
1530             //     siteToAddInCurrentGraph.path = /C
1531             //
1532             // When the recursive call to Pcp_BuildPrimIndex is all done,
1533             // the arc to site /C will have become /C/B. This is the path
1534             // we need to use to search the graph in the previous frame. We
1535             // compute this path using a simple prefix replacement.
1536             if (it.previousFrame) {
1537                 const SdfPath& requestedPathForCurrentGraph =
1538                     it.previousFrame->requestedSite.path;
1539                 const SdfPath& currentPathForCurrentGraph =
1540                     currentGraph->GetRootNode().GetPath();
1541 
1542                 siteToAddInCurrentGraph.path =
1543                     requestedPathForCurrentGraph.ReplacePrefix(
1544                         currentPathForCurrentGraph,
1545                         siteToAddInCurrentGraph.path);
1546             }
1547         }
1548 
1549         if (foundDuplicateNode) {
1550             return PcpNodeRef();
1551         }
1552     }
1553 
1554     // Local opinions are not allowed at the source of a relocation (or below).
1555     // This is colloquially known as the "salted earth" policy. We enforce
1556     // this policy here to ensure we examine all arcs as they're being added.
1557     // Optimizations:
1558     // - We only need to do this for non-root prims because root prims can't
1559     //   be relocated. This is indicated by the includeAncestralOpinions flag.
1560     if (directNodeShouldContributeSpecs && includeAncestralOpinions) {
1561         const SdfRelocatesMap & layerStackRelocates =
1562             site.layerStack->GetRelocatesSourceToTarget();
1563         SdfRelocatesMap::const_iterator
1564             i = layerStackRelocates.lower_bound( site.path );
1565         if (i != layerStackRelocates.end() && i->first.HasPrefix(site.path)) {
1566             directNodeShouldContributeSpecs = false;
1567         }
1568     }
1569 
1570     // Set up the arc.
1571     PcpArc newArc;
1572     newArc.type = arcType;
1573     newArc.mapToParent = mapExpr;
1574     newArc.parent = parent;
1575     newArc.origin = origin;
1576     newArc.namespaceDepth = namespaceDepth;
1577     newArc.siblingNumAtOrigin = arcSiblingNum;
1578 
1579     // Create the new node.
1580     PcpNodeRef newNode;
1581     PcpErrorBasePtr newNodeError;
1582     if (!includeAncestralOpinions) {
1583         // No ancestral opinions.  Just add the single new site.
1584         newNode = parent.InsertChild(site, newArc, &newNodeError);
1585         if (newNode) {
1586             newNode.SetInert(!directNodeShouldContributeSpecs);
1587 
1588             // Compose the existence of primSpecs and update the HasSpecs field
1589             // accordingly.
1590             newNode.SetHasSpecs(PcpComposeSiteHasPrimSpecs(newNode));
1591 
1592             if (!newNode.IsInert() && newNode.HasSpecs()) {
1593                 if (!indexer->inputs.usd) {
1594                     // Determine whether opinions from this site can be accessed
1595                     // from other sites in the graph.
1596                     newNode.SetPermission(
1597                         PcpComposeSitePermission(site.layerStack, site.path));
1598 
1599                     // Determine whether this node has any symmetry information.
1600                     newNode.SetHasSymmetry(
1601                         PcpComposeSiteHasSymmetry(site.layerStack, site.path));
1602                 }
1603             }
1604 
1605             PCP_INDEXING_UPDATE(
1606                 indexer, newNode,
1607                 "Added new node for site %s to graph",
1608                 TfStringify(site).c_str());
1609         }
1610 
1611     } else {
1612         // Ancestral opinions are those above the source site in namespace.
1613         // We only need to account for them if the site is not a root prim
1614         // (since root prims have no ancestors with scene description, only
1615         // the pseudo-root).
1616         //
1617         // Account for ancestral opinions by building out the graph for
1618         // that site and incorporating its root node as the new child.
1619         PCP_INDEXING_MSG(
1620             indexer, parent,
1621             "Need to build index for %s source at %s to "
1622             "pick up ancestral opinions",
1623             TfEnum::GetDisplayName(arcType).c_str(),
1624             Pcp_FormatSite(site).c_str());
1625 
1626         // We don't want to evaluate implied specializes immediately when
1627         // building the index for this source site. Instead, we'll add
1628         // tasks to do this after we have merged the source index into
1629         // the final index. This allows any specializes arcs in the source
1630         // index to be propagated to the root of the graph for the correct
1631         // strength ordering.
1632         const bool evaluateImpliedSpecializes = false;
1633 
1634         // We don't want to evaluate variants immediately when building
1635         // the index for the source site. This is because Pcp_BuildPrimIndex,
1636         // won't know anything about opinions outside of the source site,
1637         // which could cause stronger variant selections to be ignored.
1638         // (For instance, if a referencing layer stack had a stronger
1639         // opinion for the selection than what was authored at the source.
1640         //
1641         // So, tell Pcp_BuildPrimIndex to skip variants; we'll add tasks
1642         // for that after inserting the source index into our index. That
1643         // way, the variant evaluation process will have enough context
1644         // to decide what the strongest variant selection is.
1645         const bool evaluateVariants = false;
1646 
1647         // Provide a linkage across recursive calls to the indexer.
1648         PcpPrimIndex_StackFrame
1649             frame(site, parent, &newArc, indexer->previousFrame,
1650                   indexer->GetOriginatingIndex(), skipDuplicateNodes);
1651 
1652         PcpPrimIndexOutputs childOutputs;
1653         Pcp_BuildPrimIndex( site,
1654                             indexer->rootSite,
1655                             indexer->ancestorRecursionDepth,
1656                             evaluateImpliedSpecializes,
1657                             evaluateVariants,
1658                             directNodeShouldContributeSpecs,
1659                             &frame,
1660                             indexer->inputs,
1661                             &childOutputs );
1662 
1663         // Combine the child output with our current output.
1664         newNode = indexer->outputs->Append(std::move(childOutputs), newArc,
1665                                            &newNodeError);
1666         if (newNode) {
1667             PCP_INDEXING_UPDATE(
1668                 indexer, newNode,
1669                 "Added subtree for site %s to graph",
1670                 TfStringify(site).c_str());
1671         }
1672     }
1673 
1674     // Handle errors.
1675     if (newNodeError) {
1676         // Provide rootSite as context.
1677         newNodeError->rootSite = indexer->rootSite;
1678         indexer->RecordError(newNodeError);
1679     }
1680     if (!newNode) {
1681         TF_VERIFY(newNodeError, "Failed to create a node, but did not "
1682                   "specify the error.");
1683         return PcpNodeRef();
1684     }
1685 
1686     // If culling is enabled, check whether the entire subtree rooted
1687     // at the new node can be culled. This doesn't have to recurse down
1688     // the new subtree; instead, it just needs to check the new node only.
1689     // This is because computing the source prim index above will have culled
1690     // everything it can *except* for the subtree's root node.
1691     if (indexer->inputs.cull) {
1692         if (_NodeCanBeCulled(newNode, indexer->rootSite)) {
1693             newNode.SetCulled(true);
1694         }
1695         else {
1696             // Ancestor nodes that were previously marked as culled must
1697             // be updated because they now have a subtree that isn't culled.
1698             // This can happen during the propagation of implied inherits from
1699             // a class hierarchy. For instance, consider the graph:
1700             //
1701             //   root.menva       ref.menva
1702             //   Model_1 (ref)--> Model (inh)--> ModelClass (inh)--> CharClass.
1703             //
1704             // Let's say there were specs for /CharClass but NOT for /ModelClass
1705             // in the root layer stack. In that case, propagating ModelClass to
1706             // the root layer stack would result in a culled node. However, when
1707             // we then propagate CharClass, we wind up with an unculled node
1708             // beneath a culled node, which violates the culling invariant. So,
1709             // we would need to fix up /ModelClass to indicate that it can no
1710             // longer be culled.
1711             for (PcpNodeRef p = parent;
1712                  p && p.IsCulled(); p = p.GetParentNode()) {
1713                 p.SetCulled(false);
1714             }
1715         }
1716     }
1717 
1718     // Enqueue tasks to evaluate the new nodes.
1719     //
1720     // If we evaluated ancestral opinions, it it means the nested
1721     // call to Pcp_BuildPrimIndex() has already evaluated refs, payloads,
1722     // and inherits on this subgraph, so we can skip those tasks.
1723     const bool skipAncestralCompletedNodes = includeAncestralOpinions;
1724     indexer->AddTasksForNode(
1725         newNode, skipAncestralCompletedNodes,
1726         skipImpliedSpecializesCompletedNodes);
1727 
1728     // If requested, recursively check if there is a prim spec at the
1729     // targeted site or at any of its descendants. If there isn't,
1730     // we report an error. Note that we still return the new node in this
1731     // case because we want to propagate implied inherits, etc. in the graph.
1732     if (requirePrimAtTarget &&
1733         !_PrimSpecExistsUnderNode(newNode, indexer)) {
1734         PcpErrorUnresolvedPrimPathPtr err = PcpErrorUnresolvedPrimPath::New();
1735         err->rootSite = PcpSite(parent.GetRootNode().GetSite());
1736         err->site = PcpSite(parent.GetSite());
1737         err->unresolvedPath = newNode.GetPath();
1738         err->arcType = arcType;
1739         indexer->RecordError(err);
1740     }
1741 
1742     // If the arc targets a site that is itself private, issue an error.
1743     if (newNode.GetPermission() == SdfPermissionPrivate) {
1744         PcpErrorArcPermissionDeniedPtr err = PcpErrorArcPermissionDenied::New();
1745         err->rootSite = PcpSite(parent.GetRootNode().GetSite());
1746         err->site = PcpSite(parent.GetSite());
1747         err->privateSite = PcpSite(newNode.GetSite());
1748         err->arcType = arcType;
1749         indexer->RecordError(err);
1750 
1751         // Mark the new child subtree as inert so that it does not
1752         // contribute specs, but keep the node(s) to track the
1753         // dependencies in order to support processing later changes
1754         // that relax the permissions.
1755         //
1756         // Note, this is a complementary form of permissions enforcement
1757         // to that done by _EnforcePermissions().  That function enforces
1758         // the constraint that once something is made private via an
1759         // ancestral arc, overrides are prohibited.  This enforces the
1760         // equivalent constraint on direct arcs: you cannot employ an
1761         // arc directly to a private site.
1762         _InertSubtree(newNode);
1763     }
1764 
1765     // If the new node's path is the pseudo root, this is a special dependency
1766     // placeholder for unresolved default-target references/payloads.
1767     // Mark the node inert to node contribute opinions, but retain the
1768     // nodes to represent the dependency.
1769     if (newNode.GetPath() == SdfPath::AbsoluteRootPath()) {
1770         _InertSubtree(newNode);
1771     }
1772 
1773     return newNode;
1774 }
1775 
1776 static PcpNodeRef
_AddArc(const PcpArcType arcType,PcpNodeRef parent,PcpNodeRef origin,const PcpLayerStackSite & site,PcpMapExpression mapExpr,int arcSiblingNum,bool directNodeShouldContributeSpecs,bool includeAncestralOpinions,bool requirePrimAtTarget,bool skipDuplicateNodes,Pcp_PrimIndexer * indexer)1777 _AddArc(
1778     const PcpArcType arcType,
1779     PcpNodeRef parent,
1780     PcpNodeRef origin,
1781     const PcpLayerStackSite & site,
1782     PcpMapExpression mapExpr,
1783     int arcSiblingNum,
1784     bool directNodeShouldContributeSpecs,
1785     bool includeAncestralOpinions,
1786     bool requirePrimAtTarget,
1787     bool skipDuplicateNodes,
1788     Pcp_PrimIndexer *indexer )
1789 {
1790     // Strip variant selections when determining namespace depth.
1791     // Variant selections are (unfortunately) represented as path
1792     // components, but do not represent additional levels of namespace,
1793     // just alternate storage locations for data.
1794     const int namespaceDepth =
1795         PcpNode_GetNonVariantPathElementCount( parent.GetPath() );
1796 
1797     return _AddArc(
1798         arcType, parent, origin, site, mapExpr,
1799         arcSiblingNum, namespaceDepth,
1800         directNodeShouldContributeSpecs,
1801         includeAncestralOpinions,
1802         requirePrimAtTarget,
1803         skipDuplicateNodes,
1804         /* skipImpliedSpecializes = */ false,
1805         indexer);
1806 }
1807 
1808 ////////////////////////////////////////////////////////////////////////
1809 // References
1810 
1811 static SdfPath
_GetDefaultPrimPath(SdfLayerHandle const & layer)1812 _GetDefaultPrimPath(SdfLayerHandle const &layer)
1813 {
1814     TfToken target = layer->GetDefaultPrim();
1815     return SdfPath::IsValidIdentifier(target) ?
1816         SdfPath::AbsoluteRootPath().AppendChild(target) : SdfPath();
1817 }
1818 
1819 // Declare helper function for creating PcpDynamicFileFormatContext,
1820 // implemented in dynamicFileFormatContext.cpp
1821 PcpDynamicFileFormatContext
1822 Pcp_CreateDynamicFileFormatContext(
1823     const PcpNodeRef &, PcpPrimIndex_StackFrame *, TfToken::Set *);
1824 
1825 // Generates dynamic file format arguments for a payload's asset path if the
1826 // asset's file format supports it.
1827 static void
_ComposeFieldsForFileFormatArguments(const PcpNodeRef & node,const Pcp_PrimIndexer & indexer,const SdfPayload & payload,SdfLayer::FileFormatArguments * args)1828 _ComposeFieldsForFileFormatArguments(const PcpNodeRef &node,
1829                                      const Pcp_PrimIndexer &indexer,
1830                                      const SdfPayload &payload,
1831                                      SdfLayer::FileFormatArguments *args)
1832 {
1833     SdfFileFormatConstPtr fileFormat = SdfFileFormat::FindByExtension(
1834         SdfFileFormat::GetFileExtension(payload.GetAssetPath()),
1835         indexer.inputs.fileFormatTarget);
1836     if (!fileFormat) {
1837         return;
1838     }
1839     if (const PcpDynamicFileFormatInterface *dynamicFileFormat =
1840             dynamic_cast<const PcpDynamicFileFormatInterface *>(
1841                 get_pointer(fileFormat))) {
1842         // Create the context for composing the prim fields from the current
1843         // state of the index. This context will also populate a list of the
1844         // fields that it composed for dependency tracking
1845         TfToken::Set composedFieldNames;
1846         PcpDynamicFileFormatContext context = Pcp_CreateDynamicFileFormatContext(
1847             node, indexer.previousFrame, &composedFieldNames);
1848         // Ask the file format to generate dynamic file format arguments for
1849         // the asset in this context.
1850         VtValue dependencyContextData;
1851         dynamicFileFormat->ComposeFieldsForFileFormatArguments(
1852             payload.GetAssetPath(), context, args, &dependencyContextData);
1853 
1854         // Add this dependency context to dynamic file format dependency object.
1855         indexer.outputs->dynamicFileFormatDependency.AddDependencyContext(
1856             dynamicFileFormat, std::move(dependencyContextData),
1857             std::move(composedFieldNames));
1858     }
1859 }
1860 
1861 static bool
_ComposeFieldsForFileFormatArguments(const PcpNodeRef & node,const Pcp_PrimIndexer & indexer,const SdfReference & ref,SdfLayer::FileFormatArguments * args)1862 _ComposeFieldsForFileFormatArguments(const PcpNodeRef &node,
1863                                      const Pcp_PrimIndexer &indexer,
1864                                      const SdfReference &ref,
1865                                      SdfLayer::FileFormatArguments *args)
1866 {
1867     // References don't support dynamic file format arguments.
1868     return false;
1869 }
1870 
1871 // Reference and payload arcs are composed in essentially the same way.
1872 template <class RefOrPayloadType, PcpArcType ARC_TYPE>
1873 static void
_EvalRefOrPayloadArcs(PcpNodeRef node,Pcp_PrimIndexer * indexer,const std::vector<RefOrPayloadType> & arcs,const PcpSourceArcInfoVector & infoVec)1874 _EvalRefOrPayloadArcs(PcpNodeRef node,
1875                       Pcp_PrimIndexer *indexer,
1876                       const std::vector<RefOrPayloadType> &arcs,
1877                       const PcpSourceArcInfoVector &infoVec)
1878 {
1879     // This loop will be adding arcs and therefore can grow the node
1880     // storage vector, so we need to avoid holding any references
1881     // into that storage outside the loop.
1882     for (size_t arcNum=0; arcNum < arcs.size(); ++arcNum) {
1883         const RefOrPayloadType & refOrPayload = arcs[arcNum];
1884         const PcpSourceArcInfo& info = infoVec[arcNum];
1885         const SdfLayerHandle & srcLayer = info.layer;
1886         const SdfLayerOffset & srcLayerOffset = info.layerOffset;
1887         SdfLayerOffset layerOffset = refOrPayload.GetLayerOffset();
1888 
1889         PCP_INDEXING_MSG(
1890             indexer, node, "Found %s to @%s@<%s>",
1891             ARC_TYPE == PcpArcTypePayload ? "payload" : "reference",
1892             info.authoredAssetPath.c_str(),
1893             refOrPayload.GetPrimPath().GetText());
1894 
1895         bool fail = false;
1896 
1897         // Verify that the reference or payload targets the default
1898         // reference/payload target or a root prim.
1899         if (!refOrPayload.GetPrimPath().IsEmpty() &&
1900             !(refOrPayload.GetPrimPath().IsAbsolutePath() &&
1901               refOrPayload.GetPrimPath().IsPrimPath())) {
1902             PcpErrorInvalidPrimPathPtr err = PcpErrorInvalidPrimPath::New();
1903             err->rootSite = PcpSite(node.GetRootNode().GetSite());
1904             err->site = PcpSite(node.GetSite());
1905             err->primPath = refOrPayload.GetPrimPath();
1906             err->arcType = ARC_TYPE;
1907             indexer->RecordError(err);
1908             fail = true;
1909         }
1910 
1911         // Validate layer offset in original reference or payload (not the
1912         // composed layer offset stored in refOrPayload).
1913         if (!srcLayerOffset.IsValid() ||
1914             !srcLayerOffset.GetInverse().IsValid()) {
1915             PcpErrorInvalidReferenceOffsetPtr err =
1916                 PcpErrorInvalidReferenceOffset::New();
1917             err->rootSite = PcpSite(node.GetRootNode().GetSite());
1918             err->layer      = srcLayer;
1919             err->sourcePath = node.GetPath();
1920             err->assetPath  = info.authoredAssetPath;
1921             err->targetPath = refOrPayload.GetPrimPath();
1922             err->offset     = srcLayerOffset;
1923             indexer->RecordError(err);
1924 
1925             // Don't set fail, just reset the offset.
1926             layerOffset = SdfLayerOffset();
1927         }
1928 
1929         // Go no further if we've found any problems.
1930         if (fail) {
1931             continue;
1932         }
1933 
1934         // Compute the reference or payload layer stack
1935         // See Pcp_NeedToRecomputeDueToAssetPathChange
1936         SdfLayerRefPtr layer;
1937         PcpLayerStackRefPtr layerStack;
1938 
1939         const bool isInternal = refOrPayload.GetAssetPath().empty();
1940         if (isInternal) {
1941             layer = node.GetLayerStack()->GetIdentifier().rootLayer;
1942             layerStack = node.GetLayerStack();
1943         }
1944         else {
1945             std::string canonicalMutedLayerId;
1946             if (indexer->inputs.cache->IsLayerMuted(
1947                     srcLayer, info.authoredAssetPath, &canonicalMutedLayerId)) {
1948                 PcpErrorMutedAssetPathPtr err = PcpErrorMutedAssetPath::New();
1949                 err->rootSite = PcpSite(node.GetRootNode().GetSite());
1950                 err->site = PcpSite(node.GetSite());
1951                 err->targetPath = refOrPayload.GetPrimPath();
1952                 err->assetPath = info.authoredAssetPath;
1953                 err->resolvedAssetPath = canonicalMutedLayerId;
1954                 err->arcType = ARC_TYPE;
1955                 err->layer = srcLayer;
1956                 indexer->RecordError(err);
1957                 continue;
1958             }
1959 
1960             SdfLayer::FileFormatArguments args;
1961             // Compose any file format arguments that may come from the asset
1962             // file format if it's dynamic.
1963             _ComposeFieldsForFileFormatArguments(
1964                 node, *indexer, refOrPayload, &args);
1965             Pcp_GetArgumentsForFileFormatTarget(
1966                 refOrPayload.GetAssetPath(),
1967                 indexer->inputs.fileFormatTarget, &args);
1968 
1969             TfErrorMark m;
1970 
1971             // Relative asset paths will already have been anchored to their
1972             // source layers in PcpComposeSiteReferences, so we can just call
1973             // SdfLayer::FindOrOpen instead of FindOrOpenRelativeToLayer.
1974             layer = SdfLayer::FindOrOpen(refOrPayload.GetAssetPath(), args);
1975 
1976             if (!layer) {
1977                 PcpErrorInvalidAssetPathPtr err =
1978                     PcpErrorInvalidAssetPath::New();
1979                 err->rootSite = PcpSite(node.GetRootNode().GetSite());
1980                 err->site = PcpSite(node.GetSite());
1981                 err->targetPath = refOrPayload.GetPrimPath();
1982                 err->assetPath = info.authoredAssetPath;
1983                 err->resolvedAssetPath = refOrPayload.GetAssetPath();
1984                 err->arcType = ARC_TYPE;
1985                 err->layer = srcLayer;
1986                 if (!m.IsClean()) {
1987                     vector<string> commentary;
1988                     for (auto const &err: m) {
1989                         commentary.push_back(err.GetCommentary());
1990                     }
1991                     m.Clear();
1992                     err->messages = TfStringJoin(commentary.begin(),
1993                                                  commentary.end(), "; ");
1994                 }
1995                 indexer->RecordError(err);
1996                 continue;
1997             }
1998 
1999             const ArResolverContext& pathResolverContext =
2000                 node.GetLayerStack()->GetIdentifier().pathResolverContext;
2001             PcpLayerStackIdentifier layerStackIdentifier(
2002                 layer, SdfLayerHandle(), pathResolverContext );
2003             layerStack = indexer->inputs.cache->ComputeLayerStack(
2004                 layerStackIdentifier, &indexer->outputs->allErrors);
2005 
2006             if (!PcpIsTimeScalingForLayerTimeCodesPerSecondDisabled()) {
2007                 // If the referenced or payloaded layer has a different TCPS
2008                 // than the source layer that introduces it, we apply the time
2009                 // scale between these TCPS values to the layer offset.
2010                 // Note that if the introducing layer is a layer stack sublayer,
2011                 // any TCPS scaling from the layer stack will already have been
2012                 // applied to the layer offset for the reference/payload.
2013                 const double srcTimeCodesPerSecond =
2014                     srcLayer->GetTimeCodesPerSecond();
2015                 const double destTimeCodesPerSecond =
2016                     layerStack->GetTimeCodesPerSecond();
2017                 if (srcTimeCodesPerSecond != destTimeCodesPerSecond) {
2018                     layerOffset.SetScale(layerOffset.GetScale() *
2019                         srcTimeCodesPerSecond / destTimeCodesPerSecond);
2020                 }
2021             }
2022         }
2023 
2024         bool directNodeShouldContributeSpecs = true;
2025 
2026         // Determine the prim path.  This is either the one explicitly
2027         // specified in the SdfReference or SdfPayload, or if that's empty, then
2028         // the one specified by DefaultPrim in the referenced layer.
2029         SdfPath defaultPrimPath;
2030         if (refOrPayload.GetPrimPath().IsEmpty()) {
2031             // Check the layer for a defaultPrim, and use
2032             // that if present.
2033             defaultPrimPath = _GetDefaultPrimPath(layer);
2034             if (defaultPrimPath.IsEmpty()) {
2035                 PcpErrorUnresolvedPrimPathPtr err =
2036                     PcpErrorUnresolvedPrimPath::New();
2037                 err->rootSite = PcpSite(node.GetRootNode().GetSite());
2038                 err->site = PcpSite(node.GetSite());
2039                 // Use a relative path with the field key for a hint.
2040                 err->unresolvedPath = SdfPath::ReflexiveRelativePath().
2041                     AppendChild(SdfFieldKeys->DefaultPrim);
2042                 err->arcType = ARC_TYPE;
2043                 indexer->RecordError(err);
2044 
2045                 // Set the prim path to the pseudo-root path.  We'll still add
2046                 // an arc to it as a special dependency placeholder, so we
2047                 // correctly invalidate if/when the default target metadata gets
2048                 // authored in the target layer.
2049                 defaultPrimPath = SdfPath::AbsoluteRootPath();
2050                 directNodeShouldContributeSpecs = false;
2051             }
2052         }
2053 
2054         // Final prim path to use.
2055         SdfPath const &primPath = defaultPrimPath.IsEmpty() ?
2056             refOrPayload.GetPrimPath() : defaultPrimPath;
2057 
2058         // References and payloads only map values under the source path, aka
2059         // the reference root.  Any paths outside the reference root do
2060         // not map across.
2061         PcpMapExpression mapExpr =
2062             _CreateMapExpressionForArc(
2063                 /* source */ primPath, /* targetNode */ node,
2064                 indexer->inputs, layerOffset);
2065 
2066         // Only need to include ancestral opinions if the prim path is
2067         // not a root prim.
2068         const bool includeAncestralOpinions = !primPath.IsRootPrimPath();
2069 
2070         _AddArc( ARC_TYPE,
2071                  /* parent = */ node,
2072                  /* origin = */ node,
2073                  PcpLayerStackSite( layerStack, primPath ),
2074                  mapExpr,
2075                  /* arcSiblingNum = */ arcNum,
2076                  directNodeShouldContributeSpecs,
2077                  includeAncestralOpinions,
2078                  /* requirePrimAtTarget = */ true,
2079                  /* skipDuplicateNodes = */ false,
2080                  indexer );
2081     }
2082 }
2083 
2084 static void
_EvalNodeReferences(PcpPrimIndex * index,PcpNodeRef node,Pcp_PrimIndexer * indexer)2085 _EvalNodeReferences(
2086     PcpPrimIndex *index,
2087     PcpNodeRef node,
2088     Pcp_PrimIndexer *indexer)
2089 {
2090     PCP_INDEXING_PHASE(
2091         indexer, node,
2092         "Evaluating references at %s",
2093         Pcp_FormatSite(node.GetSite()).c_str());
2094 
2095     if (!node.CanContributeSpecs())
2096         return;
2097 
2098     // Compose value for local references.
2099     SdfReferenceVector refArcs;
2100     PcpSourceArcInfoVector refInfo;
2101     PcpComposeSiteReferences(node, &refArcs, &refInfo);
2102 
2103     // Add each reference arc.
2104     _EvalRefOrPayloadArcs<SdfReference, PcpArcTypeReference>(
2105         node, indexer, refArcs, refInfo);
2106 }
2107 
2108 ////////////////////////////////////////////////////////////////////////
2109 // Payload
2110 
2111 static void
_EvalNodePayloads(PcpPrimIndex * index,const PcpNodeRef & node,Pcp_PrimIndexer * indexer)2112 _EvalNodePayloads(
2113     PcpPrimIndex *index,
2114     const PcpNodeRef& node,
2115     Pcp_PrimIndexer *indexer)
2116 {
2117     PCP_INDEXING_PHASE(
2118         indexer, node, "Evaluating payload for %s",
2119         Pcp_FormatSite(node.GetSite()).c_str());
2120 
2121     if (!node.CanContributeSpecs()) {
2122         return;
2123     }
2124 
2125     // Compose value for local payloads.
2126     SdfPayloadVector payloadArcs;
2127     PcpSourceArcInfoVector payloadInfo;
2128     PcpComposeSitePayloads(node, &payloadArcs, &payloadInfo);
2129 
2130     if (payloadArcs.empty()) {
2131         return;
2132     }
2133 
2134     PCP_INDEXING_MSG(
2135         indexer, node, "Found payload for node %s", node.GetPath().GetText());
2136 
2137     // Mark that this prim index contains a payload.
2138     // However, only process the payload if it's been requested.
2139     index->GetGraph()->SetHasPayloads(true);
2140 
2141     // First thing we check is if this payload arc is being composed because it
2142     // will be an ancestral payload arc for a subgraph being being built for a
2143     // subroot reference or payload.
2144     // The prim index stack frame tells us whether we're building a subgraph
2145     // for a reference or payload and we can compare the stack frame
2146     // arc's requested site against the site we're building to check if this
2147     // we're building an ancestor of the actual target site.
2148     const bool isAncestralPayloadOfSubrootReference =
2149         indexer->previousFrame &&
2150         (indexer->previousFrame->arcToParent->type == PcpArcTypePayload ||
2151          indexer->previousFrame->arcToParent->type == PcpArcTypeReference) &&
2152         index->GetRootNode().GetSite() != indexer->previousFrame->requestedSite;
2153 
2154     // If this payload arc is an ancestral arc of the target of a subroot
2155     // reference/payload, then we always compose this payload. This is because
2156     // this ancestral prim index is not necessarily a one that would be
2157     // present on its own in the PcpCache and there may be no explicit way to
2158     // include it. So our policy is to always include the payload in this
2159     // context.
2160     //
2161     // Example:
2162     // Prim </A> in layer1 has a payload to another prim </B> in layer2
2163     // Prim </B> has a child prim </B/C>
2164     // Prim </B/C> has a payload to another prim </D> in layer3
2165     // Prim </E> on the root layer has a subroot reference to </A/C> in layer1
2166     //
2167     // When composing the reference arc for prim </E> we build a prim index for
2168     // </A/C> which builds the ancestral prim index for </A> first. In order for
2169     // </A/C> to exist, the ancestral payload for </A> to </B> must be included.
2170     // Because it will be an ancestral arc of a subroot reference subgraph, the
2171     // payload will always be included.
2172     //
2173     // However when we continue to compose </A/C> -> </B/C> and we encounter the
2174     // payload to </D>, this payload is NOT automatically included as it is a
2175     // direct arc from the subroot reference arc and can be included or excluded
2176     // via including/excluding </E>
2177     if (!isAncestralPayloadOfSubrootReference) {
2178         const PcpPrimIndexInputs::PayloadSet* includedPayloads =
2179             indexer->inputs.includedPayloads;
2180 
2181         // If includedPayloads is nullptr, we never include payloads.  Otherwise if
2182         // it does not have this path, we invoke the predicate.  If the predicate
2183         // returns true we set the output bit includedDiscoveredPayload and we
2184         // compose it.
2185         if (!includedPayloads) {
2186             PCP_INDEXING_MSG(indexer, node, "Payload was not included, skipping");
2187             return;
2188         }
2189         SdfPath const &path = indexer->rootSite.path;
2190 
2191         // If there's a payload predicate, we invoke that to decide whether or not
2192         // this payload should be included.
2193         bool composePayload = false;
2194         if (auto const &pred = indexer->inputs.includePayloadPredicate) {
2195             composePayload = pred(path);
2196             indexer->outputs->payloadState = composePayload ?
2197                 PcpPrimIndexOutputs::IncludedByPredicate :
2198                 PcpPrimIndexOutputs::ExcludedByPredicate;
2199         }
2200         else {
2201             tbb::spin_rw_mutex::scoped_lock lock;
2202             auto *mutex = indexer->inputs.includedPayloadsMutex;
2203             if (mutex) { lock.acquire(*mutex, /*write=*/false); }
2204             composePayload = includedPayloads->count(path);
2205             indexer->outputs->payloadState = composePayload ?
2206                 PcpPrimIndexOutputs::IncludedByIncludeSet :
2207                 PcpPrimIndexOutputs::ExcludedByIncludeSet;
2208         }
2209 
2210         if (!composePayload) {
2211             PCP_INDEXING_MSG(indexer, node,
2212                              "Payload <%s> was not included, skipping",
2213                              path.GetText());
2214             return;
2215         }
2216     }
2217 
2218     _EvalRefOrPayloadArcs<SdfPayload, PcpArcTypePayload>(
2219         node, indexer, payloadArcs, payloadInfo);
2220 }
2221 
2222 ////////////////////////////////////////////////////////////////////////
2223 // Relocations
2224 
2225 static void
_ElideSubtree(const Pcp_PrimIndexer & indexer,PcpNodeRef node)2226 _ElideSubtree(
2227     const Pcp_PrimIndexer& indexer,
2228     PcpNodeRef node)
2229 {
2230     if (indexer.inputs.cull) {
2231         node.SetCulled(true);
2232     }
2233     else {
2234         node.SetInert(true);
2235     }
2236 
2237     TF_FOR_ALL(child, Pcp_GetChildrenRange(node)) {
2238         _ElideSubtree(indexer, *child);
2239     }
2240 }
2241 
2242 static void
_ElideRelocatedSubtrees(const Pcp_PrimIndexer & indexer,PcpNodeRef node)2243 _ElideRelocatedSubtrees(
2244     const Pcp_PrimIndexer& indexer,
2245     PcpNodeRef node)
2246 {
2247     TF_FOR_ALL(it, Pcp_GetChildrenRange(node)) {
2248         const PcpNodeRef& childNode = *it;
2249 
2250         // We can cut off the traversal if this is a relocate node, since we
2251         // would have done this work when the node was originally added to
2252         // the graph.
2253         if (childNode.GetArcType() == PcpArcTypeRelocate) {
2254             continue;
2255         }
2256 
2257         // Elide the subtree rooted at this node if there's a relocate
2258         // statement that would move its opinions to a different prim.
2259         if (childNode.CanContributeSpecs()) {
2260             const PcpLayerStackRefPtr& layerStack = childNode.GetLayerStack();
2261             const SdfRelocatesMap& relocatesSrcToTarget =
2262                 layerStack->GetIncrementalRelocatesSourceToTarget();
2263             if (relocatesSrcToTarget.find(childNode.GetPath()) !=
2264                 relocatesSrcToTarget.end()) {
2265                 _ElideSubtree(indexer, childNode);
2266                 continue;
2267             }
2268         }
2269 
2270         _ElideRelocatedSubtrees(indexer, childNode);
2271     }
2272 }
2273 
2274 // Account for relocations that affect existing nodes in the graph.
2275 // This method is how we handle the effects of relocations, as we walk
2276 // down namespace.  For each prim, we start by using the parent's graph,
2277 // then applying relocations here.  For every relocation, we introduce a
2278 // new graph node for the relocation source, and recursively populate that
2279 // source via _AddArc().
2280 static void
_EvalNodeRelocations(PcpPrimIndex * index,const PcpNodeRef & node,Pcp_PrimIndexer * indexer)2281 _EvalNodeRelocations(
2282     PcpPrimIndex *index,
2283     const PcpNodeRef &node,
2284     Pcp_PrimIndexer *indexer )
2285 {
2286     PCP_INDEXING_PHASE(
2287         indexer, node,
2288         "Evaluating relocations under %s",
2289         Pcp_FormatSite(node.GetSite()).c_str());
2290 
2291     // Unlike other tasks, we skip processing if this node can't contribute
2292     // specs, but only if this node was introduced at this level at namespace.
2293     // This additional check is needed because a descendant node might not
2294     // have any specs and thus be marked as culled, but still have relocates
2295     // that affect that node.
2296     if (!node.CanContributeSpecs() && node.GetDepthBelowIntroduction() == 0) {
2297         return;
2298     }
2299 
2300     // Determine if this node was relocated, and from what source path.
2301     //
2302     // We need to use the incremental relocates map instead of the
2303     // fully-combined map to ensure we examine all sources of opinions
2304     // in the case where there are multiple relocations nested in different
2305     // levels of namespace that affect the same prim. The fully-combined
2306     // map collapses these relocations into a single entry, which would
2307     // cause us to skip looking at any intermediate sites.
2308     const SdfRelocatesMap & relocatesTargetToSource =
2309         node.GetLayerStack()->GetIncrementalRelocatesTargetToSource();
2310     SdfRelocatesMap::const_iterator i =
2311         relocatesTargetToSource.find(node.GetPath());
2312     if (i == relocatesTargetToSource.end()) {
2313         // This node was not relocated.
2314         return;
2315     }
2316 
2317     // This node was relocated.  Add a relocation arc back to the source.
2318     const SdfPath & relocSource = i->second;
2319     const SdfPath & relocTarget = i->first;
2320 
2321     PCP_INDEXING_MSG(
2322         indexer, node, "<%s> was relocated from source <%s>",
2323         relocTarget.GetText(), relocSource.GetText());
2324 
2325     // Determine how the opinions from the relocation source will compose
2326     // with opinions from ancestral arcs on the relocation target.
2327     // For certain nodes, we recursively mark their contributes as
2328     // shouldContributeSpecs=false to indicate that they should not
2329     // contribute opinions.
2330     //
2331     // TODO: We do not remove them entirely, because the
2332     // nodes there may be used as the 'origin' of an implied inherit
2333     // for purposes of determining relative strength. Perhaps we can
2334     // remove all nodes that aren't used as an origin?
2335     //
2336     // TODO: We may also want to use these nodes as a basis
2337     // to check for an issue errors about opinions at relocation
2338     // sources across references. Today, Csd silently ignores these,
2339     // but it seems like we should check for opinion collisions,
2340     // and either report the current relocation arc as invalid, or
2341     // choose between the opinions somehow.
2342     //
2343     TF_FOR_ALL(childIt, Pcp_GetChildrenRange(node)) {
2344         const PcpNodeRef& child = *childIt;
2345         switch (child.GetArcType()) {
2346             // Ancestral arcs of these types should contribute opinions.
2347         case PcpArcTypeVariant:
2348             // Variants are allowed to provide overrides of relocated prims.
2349             continue;
2350         case PcpArcTypeRoot:
2351         case PcpNumArcTypes:
2352             // Cases we should never encounter.
2353             TF_VERIFY(false, "Unexpected child node encountered");
2354             continue;
2355 
2356             // Nodes of these types should NOT contribute opinions.
2357         case PcpArcTypeRelocate:
2358             // Ancestral relocation arcs are superceded by this relocation,
2359             // which is 'closer' to the actual prim we're trying to index.
2360             // So, contributions from the ancestral subtree should be ignored
2361             // in favor of the ones from the relocation arc we're about to
2362             // add. See TrickyMultipleRelocations for an example.
2363         case PcpArcTypeReference:
2364         case PcpArcTypePayload:
2365         case PcpArcTypeInherit:
2366         case PcpArcTypeSpecialize:
2367             // Ancestral opinions at a relocation target across a reference
2368             // or inherit are silently ignored. See TrickyRelocationSquatter
2369             // for an example.
2370             //
2371             // XXX: Since inherits are stronger than relocations, I wonder
2372             //      if you could make the argument that classes should be
2373             //      able to override relocated prims, just like variants.
2374             break;
2375         };
2376 
2377         _ElideSubtree(*indexer, child);
2378 
2379         PCP_INDEXING_UPDATE(
2380             indexer, child,
2381             "Elided subtree that will be superceded by relocation source <%s>",
2382             relocSource.GetText());
2383     }
2384 
2385     // The mapping for a relocation source node is identity.
2386     //
2387     // The reason is that relocation mappings are applied across the
2388     // specific arcs whose target path is affected by relocations.
2389     // In this approach, relocates source nodes do not need to apply
2390     // relocation mappings since they would be redundant.
2391     //
2392     // Instead of representing the namespace mappings for relocations,
2393     // Relocation source nodes are primarily placeholders used to
2394     // incorporate the ancestral arcs from the relocation sources (spooky
2395     // ancestors).  Using actual nodes for this lets us easily
2396     // incorporate spooky ancestral opinions, spooky implied inherits
2397     // etc. without needed special accommodation.  However, it does
2398     // have some other ramifications; see XXX:RelocatesSourceNodes.
2399     //
2400     // XXX: It could be that a better design would be to only use
2401     // Relocates Source nodes during the temporary recursive indexing
2402     // of relocation sources, and then immediately transfer all of its
2403     // children to the relocates parent directly. To do this we would
2404     // need to decide how to resolve the relative arc strength of the
2405     // relocation target vs. source child nodes.
2406     const PcpMapExpression identityMapExpr = PcpMapExpression::Identity();
2407 
2408     // A prim can only be relocated from a single place -- our
2409     // expression of relocates as a map only allows for a single
2410     // entry -- so the arc number is always zero.
2411     const int arcSiblingNum = 0;
2412 
2413     PcpNodeRef newNode =
2414         _AddArc( PcpArcTypeRelocate,
2415                  /* parent = */ node,
2416                  /* origin = */ node,
2417                  PcpLayerStackSite( node.GetLayerStack(), relocSource ),
2418                  identityMapExpr,
2419                  arcSiblingNum,
2420                  /* The direct site of a relocation source is not allowed to
2421                     contribute opinions.  However, note that it usually
2422                     has node-children that do contribute opinions via
2423                     ancestral arcs. */
2424                  /* directNodeShouldContributeSpecs = */ false,
2425                  /* includeAncestralOpinions = */ true,
2426                  /* requirePrimAtTarget = */ false,
2427                  /* skipDuplicateNodes = */ false,
2428                  indexer );
2429 
2430     if (newNode) {
2431         // Check for the existence of opinions at the relocation
2432         // source, and issue errors for any that are found.
2433         //
2434         // XXX: It's a little misleading to do this only here, as this won't
2435         //      report relocation source errors for namespace children beneath
2436         //      this site. (See the error message for /Group/Model_Renamed/B
2437         //      in ErrorArcCycle for example; it cites invalid opinions at
2438         //      /Group/Model, but doesn't cite invalid opinions at
2439         //      /Group/Model/B.
2440         SdfSiteVector sites;
2441         PcpComposeSitePrimSites(newNode, &sites);
2442         TF_FOR_ALL(site, sites) {
2443             PcpErrorOpinionAtRelocationSourcePtr err =
2444                 PcpErrorOpinionAtRelocationSource::New();
2445             err->rootSite = PcpSite(node.GetRootNode().GetSite());
2446             err->layer = site->layer;
2447             err->path  = site->path;
2448             indexer->RecordError(err);
2449         }
2450 
2451         // Scan the added subtree to see it contains any opinions that would
2452         // be moved to a different prim by other relocate statements. If so,
2453         // we need to elide those opinions, or else we'll wind up with multiple
2454         // prims with opinions from the same site.
2455         //
2456         // See RelocatePrimsWithSameName test case for an example of this.
2457         _ElideRelocatedSubtrees(*indexer, newNode);
2458     }
2459 }
2460 
2461 static void
_EvalImpliedRelocations(PcpPrimIndex * index,const PcpNodeRef & node,Pcp_PrimIndexer * indexer)2462 _EvalImpliedRelocations(
2463     PcpPrimIndex *index,
2464     const PcpNodeRef &node,
2465     Pcp_PrimIndexer *indexer )
2466 {
2467     if (node.GetArcType() != PcpArcTypeRelocate || node.IsDueToAncestor()) {
2468         return;
2469     }
2470 
2471     PCP_INDEXING_PHASE(
2472         indexer, node,
2473         "Evaluating relocations implied by %s",
2474         Pcp_FormatSite(node.GetSite()).c_str());
2475 
2476     if (PcpNodeRef parent = node.GetParentNode()) {
2477         if (PcpNodeRef gp = parent.GetParentNode()) {
2478             SdfPath gpRelocSource =
2479                 parent.GetMapToParent().MapSourceToTarget(node.GetPath());
2480             if (!TF_VERIFY(!gpRelocSource.IsEmpty())) {
2481                 return;
2482             }
2483 
2484             PCP_INDEXING_PHASE(
2485                 indexer, node,
2486                 "Propagating relocate from %s to %s",
2487                 Pcp_FormatSite(node.GetSite()).c_str(),
2488                 gpRelocSource.GetText());
2489 
2490             // Check if this has already been propagated.
2491             TF_FOR_ALL(gpChildIt, Pcp_GetChildrenRange(gp)) {
2492                 const PcpNodeRef& gpChild = *gpChildIt;
2493                 if (gpChild.GetPath() == gpRelocSource &&
2494                     gpChild.GetArcType() == PcpArcTypeRelocate) {
2495                     PCP_INDEXING_PHASE(
2496                         indexer, node,
2497                         "Relocate already exists -- skipping");
2498                     return;
2499                 }
2500             }
2501 
2502             _AddArc( PcpArcTypeRelocate,
2503                      /* parent = */ gp,
2504                      /* origin = */ node,
2505                      PcpLayerStackSite( gp.GetLayerStack(),
2506                                         gpRelocSource ),
2507                      PcpMapExpression::Identity(),
2508                      /* arcSiblingNum = */ 0,
2509                      /* directNodeShouldContributeSpecs = */ false,
2510                      /* includeAncestralOpinions = */ false,
2511                      /* requirePrimAtTarget = */ false,
2512                      /* skipDuplicateNodes = */ false,
2513                      indexer );
2514         }
2515     }
2516 }
2517 
2518 ////////////////////////////////////////////////////////////////////////
2519 // Class-based Arcs
2520 
2521 // Walk over the child nodes of parent, looking for an existing inherit
2522 // node.
2523 static PcpNodeRef
_FindMatchingChild(const PcpNodeRef & parent,const PcpArcType parentArcType,const PcpLayerStackSite & site,const PcpArcType arcType,const PcpMapExpression & mapToParent,int depthBelowIntroduction)2524 _FindMatchingChild(const PcpNodeRef& parent,
2525                    const PcpArcType parentArcType,
2526                    const PcpLayerStackSite& site,
2527                    const PcpArcType arcType,
2528                    const PcpMapExpression & mapToParent,
2529                    int depthBelowIntroduction)
2530 {
2531     // Arbitrary-order traversal.
2532     TF_FOR_ALL(childIt, Pcp_GetChildrenRange(parent)) {
2533         const PcpNodeRef& child = *childIt;
2534 
2535         // XXX:RelocatesSourceNodes: This somewhat arcane way of comparing
2536         // inherits arc "identity" is necessary to handle the way implied
2537         // inherits map across relocation source nodes.  In particular,
2538         // comparing only the sites there would give us a collision, because
2539         // the sites for implied inherits under relocates sources are
2540         // not necessarily meaningful.
2541         if (parentArcType == PcpArcTypeRelocate) {
2542             if (child.GetArcType() == arcType &&
2543                 child.GetMapToParent().Evaluate() == mapToParent.Evaluate() &&
2544                 child.GetOriginNode().GetDepthBelowIntroduction()
2545                 == depthBelowIntroduction) {
2546                 return child;
2547             }
2548         }
2549         else {
2550             if (child.GetSite() == site) {
2551                 return child;
2552             }
2553         }
2554     }
2555     return PcpNodeRef();
2556 }
2557 
2558 static SdfPath
_FindContainingVariantSelection(SdfPath p)2559 _FindContainingVariantSelection(SdfPath p)
2560 {
2561     while (!p.IsEmpty() && !p.IsPrimVariantSelectionPath()) {
2562         p = p.GetParentPath();
2563     }
2564     return p;
2565 }
2566 
2567 // Use the mapping function to figure out the path of the site to
2568 // inherit, by mapping the parent's site back to the source.
2569 static SdfPath
_DetermineInheritPath(const SdfPath & parentPath,const PcpMapExpression & inheritMap)2570 _DetermineInheritPath(
2571     const SdfPath & parentPath,
2572     const PcpMapExpression & inheritMap )
2573 {
2574     // For example, given an inherit map like this:
2575     //    source: /Class
2576     //    target: /Model
2577     //
2578     // Say we are adding this inherit arc to </Model>; we'll map
2579     // the target path back to </Class>.
2580     //
2581     // Why don't we just use the source path directly?
2582     // The reason we use a mapping function to represent the arc,
2583     // rather than simply passing around the path of the class itself,
2584     // is to let us account for relocations that happened along the
2585     // way.  See TrickySpookyInheritsInSymmetricRig for an example
2586     // where we reparent a rig's LArm/Anim scope out to the anim
2587     // interface, and we need to account for the "spooky inherit"
2588     // back to SymArm/Anim from the new location.  The PcpMapFunction
2589     // lets us account for any relocations needed.
2590     //
2591     // We also have to handle variants here.  PcpLayerStackSites for variant
2592     // arcs may contain variant selections.  These variant selections
2593     // are purely to address appropriate section of opinion storage
2594     // in the layer, however; variant selections are *not* an aspect
2595     // of composed scene namespace, and must never appear in the paths
2596     // used in mapping functions.  Therefore, to add a class arc to a
2597     // variant-selection site, we take additional measures to strip out
2598     // the variant selections before mapping the path and then re-add
2599     // them afterwards.
2600     //
2601     if (!parentPath.ContainsPrimVariantSelection()) {
2602         // Easy case: Just map the site back across the inherit.
2603         return inheritMap.MapTargetToSource(parentPath);
2604     } else {
2605         // Harder case: The site path has variant selections.
2606         // We want to map the site's namespace back across the
2607         // inherit, but retain the embedded variant selections.
2608 
2609         // Find the nearest containing variant selection.
2610         SdfPath varPath = _FindContainingVariantSelection(parentPath);
2611         TF_VERIFY(!varPath.IsEmpty());
2612 
2613         // Strip the variant selections from the site path, apply the
2614         // inherit mapping, then re-add the variant selections.
2615         return inheritMap.MapTargetToSource(
2616                 parentPath.StripAllVariantSelections() )
2617                 .ReplacePrefix( varPath.StripAllVariantSelections(), varPath );
2618     }
2619 }
2620 
2621 // A helper that adds a single class-based arc below the given parent,
2622 // returning the new node.  If the arc already exists, this
2623 // returns the existing node.
2624 static PcpNodeRef
_AddClassBasedArc(PcpArcType arcType,PcpNodeRef parent,PcpNodeRef origin,const PcpMapExpression & inheritMap,const int inheritArcNum,const PcpLayerStackSite & ignoreIfSameAsSite,Pcp_PrimIndexer * indexer)2625 _AddClassBasedArc(
2626     PcpArcType arcType,
2627     PcpNodeRef parent,
2628     PcpNodeRef origin,
2629     const PcpMapExpression & inheritMap,
2630     const int inheritArcNum,
2631     const PcpLayerStackSite & ignoreIfSameAsSite,
2632     Pcp_PrimIndexer *indexer )
2633 {
2634     PCP_INDEXING_PHASE(
2635         indexer, parent, "Preparing to add %s arc to %s",
2636         TfEnum::GetDisplayName(arcType).c_str(),
2637         Pcp_FormatSite(parent.GetSite()).c_str());
2638 
2639     PCP_INDEXING_MSG(
2640         indexer, parent,
2641         "origin: %s\n"
2642         "inheritArcNum: %d\n"
2643         "ignoreIfSameAsSite: %s\n",
2644         Pcp_FormatSite(origin.GetSite()).c_str(),
2645         inheritArcNum,
2646         ignoreIfSameAsSite == PcpLayerStackSite() ?
2647             "<none>" : Pcp_FormatSite(ignoreIfSameAsSite).c_str());
2648 
2649     // Use the inherit map to figure out the site path to inherit.
2650     SdfPath inheritPath =
2651         _DetermineInheritPath( parent.GetPath(), inheritMap );
2652 
2653     // We need to check the parent node's arc type in a few places
2654     // below. PcpNode::GetArcType is insufficient because we could be in a
2655     // recursive prim indexing call. In that case, we need to know what
2656     // the arc type will be once this node is incorporated into the parent
2657     // prim index. We can use the PcpPrimIndex_StackFrameIterator to
2658     // determine that.
2659     const PcpArcType parentArcType =
2660         PcpPrimIndex_StackFrameIterator(parent, indexer->previousFrame)
2661         .GetArcType();
2662 
2663     if (!inheritPath.IsEmpty()) {
2664         PCP_INDEXING_MSG(indexer, parent,
2665                          "Inheriting from path <%s>", inheritPath.GetText());
2666     }
2667     else {
2668         // The parentNode site is outside the co-domain of the inherit.
2669         // This means there is no appropriate site for the parent
2670         // to inherit opinions along this inherit arc.
2671         //
2672         // For example, this could be an inherit that reaches outside
2673         // a referenced root to another subroot class, which cannot
2674         // be mapped across that reference.  Or it could be a root class
2675         // inherit in the context of a variant: variants cannot contain
2676         // opinions about root classes.
2677         //
2678         // This is not an error; it just means the class arc is not
2679         // meaningful from this site.
2680         PCP_INDEXING_MSG(indexer, parent,
2681                          "No appropriate site for inheriting opinions");
2682         return PcpNodeRef();
2683     }
2684 
2685     PcpLayerStackSite inheritSite( parent.GetLayerStack(), inheritPath );
2686 
2687     // Check if there are multiple inherits with the same site.
2688     // For example, this might be an implied inherit that was also
2689     // broken down explicitly.
2690     if (PcpNodeRef child = _FindMatchingChild(
2691             parent, parentArcType, inheritSite, arcType, inheritMap,
2692             origin.GetDepthBelowIntroduction())) {
2693 
2694         PCP_INDEXING_MSG(
2695             indexer, parent, child,
2696             TfEnum::GetDisplayName(arcType).c_str(),
2697             "A %s arc to <%s> already exists. Skipping.",
2698             inheritPath.GetText());
2699 
2700         // TODO Need some policy to resolve multiple arcs.  Existing Csd
2701         //      prefers the weaker of the two.  Currently, this just
2702         //      leaves the one that happened to get populated first
2703         //      in place, which is too loosey-goosey.
2704         return child;
2705     }
2706 
2707     // The class-based arc may map this path un-changed. For example,
2708     // consider an implied inherit being propagated from under a
2709     // reference node, that is in turn a child of a relocation node:
2710     //
2711     //   root -> relocation -> reference -> inherit
2712     //                    :
2713     //                    +--> implied inherit
2714     //
2715     // The reference node's mapToParent will apply the effect of the
2716     // relocations, because it is bringing opinions into a namespace
2717     // where relocations have been applied.  As a result, as soon as
2718     // the inherit is transferred to become the implied inherit, the
2719     // implied inherit map function also also includes the relocations.
2720     //
2721     // When we use it to _DetermineInheritPath() from the relocation node,
2722     // the relocation source site will end up hitting the identity
2723     // mapping (/ -> /) that every inherit has, and yield the same
2724     // path unchanged.
2725     //
2726     // We need to add these nodes to the graph to represent the logical
2727     // presence of the class arc, and to ensure that it continues to
2728     // be propagated further up the graph.  However, we do not want to
2729     // contribute redundant opinions, so we mark the newly added node
2730     // with shouldContributeSpecs=false.
2731     //
2732     // XXX: This situation is a pretty subtle implication of the way
2733     // we use PcpNodes to represent (and propagate) inherits. Overall,
2734     // it seems like an opportunity to find a cleaner representation.
2735     //
2736     const bool shouldContributeSpecs =
2737         (inheritPath != parent.GetPath()) &&
2738         (inheritSite != ignoreIfSameAsSite);
2739 
2740     // If we hit the cases described above, we need to ensure the placeholder
2741     // duplicate nodes are added to the graph to ensure the continued
2742     // propagation of implied classes. Otherwise, duplicate nodes should
2743     // be skipped over to ensure we don't introduce different paths
2744     // to the same site.
2745     const bool skipDuplicateNodes = shouldContributeSpecs;
2746 
2747     // Only subroot prim classes need to compute ancestral opinions.
2748     const bool includeAncestralOpinions =
2749         shouldContributeSpecs && !inheritPath.IsRootPrimPath();
2750 
2751     PcpNodeRef newNode =
2752         _AddArc( arcType, parent, origin,
2753                  inheritSite, inheritMap, inheritArcNum,
2754                  /* directNodeShouldContributeSpecs = */ shouldContributeSpecs,
2755                  includeAncestralOpinions,
2756                  /* requirePrimAtTarget = */ false,
2757                  skipDuplicateNodes,
2758                  indexer );
2759 
2760     return newNode;
2761 }
2762 
2763 // Helper function for adding a list of class-based arcs under the given
2764 // node in the given prim index.
2765 static void
_AddClassBasedArcs(PcpPrimIndex * index,const PcpNodeRef & node,const SdfPathVector & classArcs,PcpArcType arcType,Pcp_PrimIndexer * indexer)2766 _AddClassBasedArcs(
2767     PcpPrimIndex* index,
2768     const PcpNodeRef& node,
2769     const SdfPathVector& classArcs,
2770     PcpArcType arcType,
2771     Pcp_PrimIndexer* indexer)
2772 {
2773     for (size_t arcNum=0; arcNum < classArcs.size(); ++arcNum) {
2774         PCP_INDEXING_MSG(indexer, node, "Found %s to <%s>",
2775             TfEnum::GetDisplayName(arcType).c_str(),
2776             classArcs[arcNum].GetText());
2777 
2778         // The mapping for a class arc maps the class to the instance.
2779         // Every other path maps to itself.
2780         PcpMapExpression mapExpr =
2781             _CreateMapExpressionForArc(
2782                 /* source */ classArcs[arcNum], /* targetNode */ node,
2783                 indexer->inputs)
2784             .AddRootIdentity();
2785 
2786         _AddClassBasedArc(arcType,
2787             /* parent = */ node,
2788             /* origin = */ node,
2789             mapExpr,
2790             arcNum,
2791             /* ignoreIfSameAsSite = */ PcpLayerStackSite(),
2792             indexer);
2793     }
2794 }
2795 
2796 /// Build the effective map function for an implied class arc.
2797 ///
2798 /// \p classArc is the original class arc
2799 /// \p transfer is the function that maps the parent of the arc
2800 ///    to the destination parent
2801 ///
2802 /// Here is an example:
2803 ///
2804 /// Say Sullivan_1 references Sullivan, and has a child rig scope Rig
2805 /// that inherits a child class _class_Rig:
2806 ///
2807 ///   Sullivan_1 -----reference----->  Sullivan
2808 ///       |                                |
2809 ///       +---Rig                          +---Rig
2810 ///       |     :                          |     |
2811 ///       |     implicit inherit           |     inherits
2812 ///       |     :                          |     |
2813 ///       |     V                          |     V
2814 ///       +---_class_Rig                   +---_class_Rig
2815 ///
2816 /// The mapping for the inherit in Sullivan is
2817 ///
2818 ///    source: /Sullivan/_class_Rig
2819 ///    target: /Sullivan/Rig
2820 ///
2821 /// The mapping for the reference is:
2822 ///
2823 ///    source: /Sullivan
2824 ///    target: /Sullivan_1
2825 ///
2826 /// The implied classes are determined by applying \p transfer to
2827 /// \p classArc. In the same way we apply MapFunctions to individual
2828 /// paths to move them between namespaces, we apply functions to other
2829 /// functions to move them as well, via PcpMapFunction::Compose(). In
2830 /// this example, we use the reference mapping as the function to
2831 /// figure out the equivalent implicit class mapping on the left side.
2832 /// This ends up giving us the implicit class result:
2833 ///
2834 ///    source: /Sullivan_1/_class_Rig
2835 ///    target: /Sullivan_1/Rig
2836 ///
2837 /// In more elaborate cases where relocations are at play, transferFunc
2838 /// accounts for the effect of the relocations, and the implied class
2839 /// function we return here will also reflect those relocations.
2840 ///
2841 static PcpMapExpression
_GetImpliedClass(const PcpMapExpression & transfer,const PcpMapExpression & classArc)2842 _GetImpliedClass( const PcpMapExpression & transfer,
2843                   const PcpMapExpression & classArc )
2844 {
2845     if (transfer.IsConstantIdentity()) {
2846         return classArc;
2847     }
2848 
2849     return transfer.Compose( classArc.Compose( transfer.Inverse() ))
2850         .AddRootIdentity();
2851 }
2852 
2853 // Check the given node for class-based children, and add corresponding
2854 // implied classes to the parent node.
2855 static void
_EvalImpliedClassTree(PcpPrimIndex * index,PcpNodeRef destNode,PcpNodeRef srcNode,const PcpMapExpression & transferFunc,bool srcNodeIsStartOfTree,Pcp_PrimIndexer * indexer)2856 _EvalImpliedClassTree(
2857     PcpPrimIndex *index,
2858     PcpNodeRef destNode,
2859     PcpNodeRef srcNode,
2860     const PcpMapExpression & transferFunc,
2861     bool srcNodeIsStartOfTree,
2862     Pcp_PrimIndexer *indexer)
2863 {
2864     // XXX:RelocatesSourceNodes: Avoid propagating implied classes to
2865     // relocates nodes here. Classes on relocate nodes only exist as
2866     // placeholders so that they can continue to be propagated after
2867     // the relocation source tree is added to the prim index in _AddArc.
2868     // We don't need to propagate classes to relocate nodes here because
2869     // we don't need them to serve as placeholders; instead, we can just
2870     // propagate them directly to the relocate node's parent.
2871     //
2872     // Doing this avoids having to work around path translation subtleties
2873     // in _AddClassBasedArc.
2874     if (destNode.GetArcType() == PcpArcTypeRelocate) {
2875         // Create a transfer function for the relocate node's parent by
2876         // composing the relocate node's mapToParent with the given transfer
2877         // function. See _EvalImpliedClasses for more details.
2878         const PcpMapExpression newTransferFunc =
2879             destNode.GetMapToParent().AddRootIdentity().Compose(transferFunc);
2880         _EvalImpliedClassTree(
2881             index, destNode.GetParentNode(), srcNode, newTransferFunc,
2882             srcNodeIsStartOfTree, indexer);
2883 
2884         // Ensure that any ancestral class hierarchies beginning under
2885         // destNode are propagated. This normally occurs naturally when
2886         // a new implied class arc is added under destNode. However,
2887         // since we're adding implied class arcs to destNode's parent
2888         // instead, we have to explicitly add a task to ensure this occurs.
2889         // See TrickyInheritsAndRelocates5 for a test case where this is
2890         // important.
2891         indexer->AddTask(Task(Task::Type::EvalImpliedClasses, destNode));
2892         return;
2893     }
2894 
2895     // Visit all class arcs under srcNode, in arbitrary order.
2896     // Walk over the tree below srcNode, pushing to the parent.
2897     //
2898     // NOTE: We need to grab a copy of the child list and not just
2899     //       a reference. The recursive call may cause more nodes to
2900     //       be added to the graph's node pool, which would invalidate
2901     //       the reference.
2902     for (const PcpNodeRef& srcChild : Pcp_GetChildren(srcNode)) {
2903         // Skip everything that isn't a class-based arc.
2904         if (!PcpIsClassBasedArc(srcChild.GetArcType()))
2905             continue;
2906 
2907         PCP_INDEXING_MSG(
2908             indexer, srcChild, destNode,
2909             "Attempting to propagate %s of %s to %s.",
2910             TfEnum::GetDisplayName(srcChild.GetArcType()).c_str(),
2911             Pcp_FormatSite(srcChild.GetSite()).c_str(),
2912             Pcp_FormatSite(destNode.GetSite()).c_str());
2913 
2914         // Now, the purpose of this entire function is to propagate an
2915         // entire class hierarchy below one node, to its parent:
2916         //
2917         //    destNode ---> srcNode
2918         //                   : :
2919         //                  :   :
2920         //                 :     :
2921         //                :       :
2922         //             (...classes...)
2923         //
2924         // However, consider what happens when destNode inherits
2925         // srcNode, which also inherits some otherNode:
2926         //
2927         //              i            i
2928         //    destNode ---> srcNode ---> otherNode
2929         //
2930         // As we are processing the class-based children of srcNode,
2931         // we need to somehow distinguish the true children (i.e.
2932         // namespace descendants) from the arc that continues
2933         // the destNode --> srcNode --> otherNode chain.
2934         // We do NOT want to add an implied class arc directly
2935         // from otherNode to destNode.
2936         //
2937         if (srcNodeIsStartOfTree
2938             && PcpIsClassBasedArc(srcNode.GetArcType())
2939             && srcNode .GetDepthBelowIntroduction() ==
2940                srcChild.GetDepthBelowIntroduction()) {
2941 
2942             PCP_INDEXING_MSG(indexer, srcChild, destNode,
2943                              "Skipping ancestral class");
2944             continue;
2945         }
2946 
2947         // Determine the equivalent class mapping under destNode.
2948         PcpMapExpression destClassFunc =
2949             _GetImpliedClass(transferFunc, srcChild.GetMapToParent());
2950 
2951         PCP_INDEXING_MSG(
2952             indexer, srcChild, destNode,
2953             "Transfer function:\n%s", transferFunc.GetString().c_str());
2954         PCP_INDEXING_MSG(
2955             indexer, srcChild, destNode,
2956             "Implied class:\n%s", destClassFunc.GetString().c_str());
2957 
2958         PcpNodeRef destChild;
2959 
2960         // Check to see if an implied class for srcChild has already been
2961         // propagated to destNode by examining origin nodes. If we find a
2962         // a child node whose origin matches srcChild, that node must be
2963         // the implied class for srcChild, so we don't don't need to redo
2964         // the work to process it.
2965         TF_FOR_ALL(destChildIt, Pcp_GetChildrenRange(destNode)) {
2966             if (destChildIt->GetOriginNode() == srcChild &&
2967                 destChildIt->GetMapToParent().Evaluate()
2968                     == destClassFunc.Evaluate()) {
2969                 destChild = *destChildIt;
2970 
2971                 PCP_INDEXING_MSG(
2972                     indexer, srcChild, destChild,
2973                     "Found previously added implied inherit node");
2974                 break;
2975             }
2976         }
2977 
2978         // Try to add this implied class.
2979         //
2980         // This may fail if there's no equivalent site to inherit, due to
2981         // the namespace domains of the mappings involved.  Or it may
2982         // return an existing node if destNode already inherits the site.
2983         //
2984         // We use the same origin and sibling number information
2985         // as the srcChild in order to properly account for the
2986         // effective strength of this implied class.  For example,
2987         // there may be multiple class arcs from srcNode that
2988         // we are pushing to destNode, and we need to preserve
2989         // their relative strength.  destNode may also end up
2990         // receiving implied classes from multiple different
2991         // sources; we rely on their distinct origins to reconcile
2992         // their strength.
2993         //
2994         // It is also possible that the newly added class arc would
2995         // represent a redundant arc in the scene, due to relocations
2996         // or variants.  For example, this might be an inherit of
2997         // a class outside the scope of the relocation or variant.
2998         // We do not want to contribute redundant opinions to the
2999         // scene, but we still want to continue propagating the
3000         // inherit arc up the graph.  To handle this, we provide
3001         // the ignoreIfSameAsSite (the inherit site we are propagating)
3002         // so that _AddClassBasedArc() can determine if this would be
3003         // a redundant inherit.
3004         //
3005         if (!destChild) {
3006             destChild = _AddClassBasedArc(
3007                 srcChild.GetArcType(),
3008                 /* parent = */ destNode,
3009                 /* origin = */ srcChild,
3010                 destClassFunc,
3011                 srcChild.GetSiblingNumAtOrigin(),
3012                 /* ignoreIfSameAsSite = */ srcChild.GetSite(),
3013                 indexer);
3014         }
3015 
3016         // If we successfully added the arc (or found it already existed)
3017         // recurse on nested classes.  This will build up the full
3018         // class hierarchy that we are inheriting.
3019         // Optimization: Recursion requires some cost to set up
3020         // childTransferFunc, below.  Before we do that work,
3021         // check if there are any nested inherits.
3022         if (destChild && _HasClassBasedChild(srcChild)) {
3023             // Determine the transferFunc to use for the nested child,
3024             // by composing the functions to walk up from the srcChild,
3025             // across the transferFunc, and down to the destChild.
3026             // (Since we are walking down to destChild, we use the
3027             // inverse of its mapToParent.)
3028             //
3029             // This gives us a childTransferFunc that will map the
3030             // srcChild namespace to the destChild namespace, so
3031             // that can continue propagating implied classes from there.
3032             //
3033             PcpMapExpression childTransferFunc =
3034                 destClassFunc.Inverse()
3035                 .Compose(transferFunc.Compose(srcChild.GetMapToParent()));
3036 
3037             _EvalImpliedClassTree(index, destChild, srcChild,
3038                                   childTransferFunc,
3039                                   /* srcNodeIsStartOfTree = */ false,
3040                                   indexer);
3041         }
3042     }
3043 }
3044 
3045 static bool
3046 _IsPropagatedSpecializesNode(
3047     const PcpNodeRef& node);
3048 
3049 static void
_EvalImpliedClasses(PcpPrimIndex * index,PcpNodeRef node,Pcp_PrimIndexer * indexer)3050 _EvalImpliedClasses(
3051     PcpPrimIndex *index,
3052     PcpNodeRef node,
3053     Pcp_PrimIndexer *indexer)
3054 {
3055     PCP_INDEXING_PHASE(
3056         indexer, node,
3057         "Evaluating implied classes at %s",
3058         Pcp_FormatSite(node.GetSite()).c_str());
3059 
3060     // If this is the root node, there is no need to propagate classes.
3061     if (!node.GetParentNode())
3062         return;
3063 
3064     // Do not allow inherits to propagate from beneath propagated
3065     // specializes arcs.  These inherits need to be propagated from
3066     // the origin of these specializes arcs -- this ensures the origin
3067     // nodes of the propagated inherits have a consistent strength
3068     // ordering.  This is handled with the implied specializes task.
3069     if (_IsPropagatedSpecializesNode(node)) {
3070         return;
3071     }
3072 
3073     // Optimization: early-out if there are no class arcs to propagate.
3074     if (!_HasClassBasedChild(node)) {
3075         return;
3076     }
3077 
3078     // Grab the mapping to the parent node.
3079     // We will use it to map ("transfer") the class to the parent.
3080     // The mapping to the parent may have a restricted domain, such as
3081     // for a reference arc, which only maps the reference root prim.
3082     // To map root classes across such a mapping, we need to add
3083     // an identity (/->/) entry.  This is not a violation of reference
3084     // namespace encapsulation: classes deliberately work this way.
3085     PcpMapExpression transferFunc = node.GetMapToParent().AddRootIdentity();
3086 
3087     _EvalImpliedClassTree( index, node.GetParentNode(), node,
3088                            transferFunc,
3089                            /* srcNodeIsStartOfTree = */ true,
3090                            indexer );
3091 }
3092 
3093 ////////////////////////////////////////////////////////////////////////
3094 // Inherits
3095 
3096 // Evaluate any inherit arcs expressed directly at node.
3097 static void
_EvalNodeInherits(PcpPrimIndex * index,PcpNodeRef node,Pcp_PrimIndexer * indexer)3098 _EvalNodeInherits(
3099     PcpPrimIndex *index,
3100     PcpNodeRef node,
3101     Pcp_PrimIndexer *indexer)
3102 {
3103     PCP_INDEXING_PHASE(
3104         indexer, node,
3105         "Evaluating inherits at %s",
3106         Pcp_FormatSite(node.GetSite()).c_str());
3107 
3108     if (!node.CanContributeSpecs())
3109         return;
3110 
3111     // Compose value for local inherits.
3112     SdfPathVector inhArcs;
3113     PcpComposeSiteInherits(node, &inhArcs);
3114 
3115     // Add inherits arcs.
3116     _AddClassBasedArcs(
3117         index, node, inhArcs,
3118         PcpArcTypeInherit,
3119         indexer);
3120 }
3121 
3122 ////////////////////////////////////////////////////////////////////////
3123 // Specializes
3124 
3125 // Evaluate any specializes arcs expressed directly at node.
3126 static void
_EvalNodeSpecializes(PcpPrimIndex * index,const PcpNodeRef & node,Pcp_PrimIndexer * indexer)3127 _EvalNodeSpecializes(
3128     PcpPrimIndex* index,
3129     const PcpNodeRef& node,
3130     Pcp_PrimIndexer* indexer)
3131 {
3132     PCP_INDEXING_PHASE(
3133         indexer, node,
3134         "Evaluating specializes at %s",
3135         Pcp_FormatSite(node.GetSite()).c_str());
3136 
3137     if (!node.CanContributeSpecs())
3138         return;
3139 
3140     // Compose value for local specializes.
3141     SdfPathVector specArcs;
3142     PcpComposeSiteSpecializes(node, &specArcs);
3143 
3144     // Add specializes arcs.
3145     _AddClassBasedArcs(
3146         index, node, specArcs,
3147         PcpArcTypeSpecialize,
3148         indexer);
3149 }
3150 
3151 // Returns true if the given node is a specializes node that
3152 // has been propagated to the root of the graph for strength
3153 // ordering purposes in _EvalImpliedSpecializes.
3154 static bool
_IsPropagatedSpecializesNode(const PcpNodeRef & node)3155 _IsPropagatedSpecializesNode(
3156     const PcpNodeRef& node)
3157 {
3158     return (PcpIsSpecializeArc(node.GetArcType()) &&
3159             node.GetParentNode() == node.GetRootNode() &&
3160             node.GetSite() == node.GetOriginNode().GetSite());
3161 }
3162 
3163 static bool
_IsNodeInSubtree(const PcpNodeRef & node,const PcpNodeRef & subtreeRoot)3164 _IsNodeInSubtree(
3165     const PcpNodeRef& node,
3166     const PcpNodeRef& subtreeRoot)
3167 {
3168     for (PcpNodeRef n = node; n; n = n.GetParentNode()) {
3169         if (n == subtreeRoot) {
3170             return true;
3171         }
3172     }
3173     return false;
3174 }
3175 
3176 static std::pair<PcpNodeRef, bool>
_PropagateNodeToParent(PcpNodeRef parentNode,PcpNodeRef srcNode,bool skipImpliedSpecializes,const PcpMapExpression & mapToParent,const PcpNodeRef & srcTreeRoot,Pcp_PrimIndexer * indexer)3177 _PropagateNodeToParent(
3178     PcpNodeRef parentNode,
3179     PcpNodeRef srcNode,
3180     bool skipImpliedSpecializes,
3181     const PcpMapExpression& mapToParent,
3182     const PcpNodeRef& srcTreeRoot,
3183     Pcp_PrimIndexer* indexer)
3184 {
3185     bool createdNewNode = false;
3186 
3187     PcpNodeRef newNode;
3188     if (srcNode.GetParentNode() == parentNode) {
3189         newNode = srcNode;
3190     }
3191     else {
3192         newNode = _FindMatchingChild(
3193             parentNode, parentNode.GetArcType(),
3194             srcNode.GetSite(), srcNode.GetArcType(),
3195             mapToParent, srcNode.GetDepthBelowIntroduction());
3196 
3197         if (!newNode) {
3198             // Only propagate a node if it's a non-implied arc or if it's an
3199             // implied arc whose origin is outside the subgraph we're
3200             // propagating. If this is an implied arc whose origin is
3201             // within the subgraph, it will be handled when we evaluate
3202             // implied class arcs on the subgraph being propagated.
3203             if (!_IsImpliedClassBasedArc(srcNode) ||
3204                 !_IsNodeInSubtree(srcNode.GetOriginNode(), srcTreeRoot)) {
3205 
3206                 const int namespaceDepth =
3207                     (srcNode == srcTreeRoot ?
3208                         PcpNode_GetNonVariantPathElementCount(
3209                             parentNode.GetPath()) :
3210                         srcNode.GetNamespaceDepth());
3211 
3212                 const PcpNodeRef originNode =
3213                     (srcNode == srcTreeRoot || _IsImpliedClassBasedArc(srcNode) ?
3214                         srcNode : parentNode);
3215 
3216                 newNode = _AddArc(srcNode.GetArcType(),
3217                     /* parent = */ parentNode,
3218                     /* origin = */ originNode,
3219                     srcNode.GetSite(),
3220                     mapToParent,
3221                     srcNode.GetSiblingNumAtOrigin(),
3222                     namespaceDepth,
3223                     /* directNodeShouldContributeSpecs = */ !srcNode.IsInert(),
3224                     /* includeAncestralOpinions = */ false,
3225                     /* requirePrimAtTarget = */ false,
3226                     /* skipDuplicateNodes = */ false,
3227                     skipImpliedSpecializes,
3228                     indexer);
3229 
3230                 createdNewNode = static_cast<bool>(newNode);
3231             }
3232         }
3233 
3234         if (newNode) {
3235             newNode.SetInert(srcNode.IsInert());
3236             newNode.SetHasSymmetry(srcNode.HasSymmetry());
3237             newNode.SetPermission(srcNode.GetPermission());
3238             newNode.SetRestricted(srcNode.IsRestricted());
3239 
3240             srcNode.SetInert(true);
3241         }
3242         else {
3243             _InertSubtree(srcNode);
3244         }
3245     }
3246 
3247     return {newNode, createdNewNode};
3248 }
3249 
3250 static void
_PropagateSpecializesTreeToRoot(PcpPrimIndex * index,PcpNodeRef parentNode,PcpNodeRef srcNode,PcpNodeRef originNode,const PcpMapExpression & mapToParent,const PcpNodeRef & srcTreeRoot,Pcp_PrimIndexer * indexer)3251 _PropagateSpecializesTreeToRoot(
3252     PcpPrimIndex* index,
3253     PcpNodeRef parentNode,
3254     PcpNodeRef srcNode,
3255     PcpNodeRef originNode,
3256     const PcpMapExpression& mapToParent,
3257     const PcpNodeRef& srcTreeRoot,
3258     Pcp_PrimIndexer* indexer)
3259 {
3260     // Make sure to skip implied specializes tasks for the propagated
3261     // node. Otherwise, we'll wind up propagating this node back to
3262     // its originating subtree, which will leave it inert.
3263     const bool skipImpliedSpecializes = true;
3264 
3265     std::pair<PcpNodeRef, bool> newNode = _PropagateNodeToParent(
3266         parentNode, srcNode,
3267         skipImpliedSpecializes,
3268         mapToParent, srcTreeRoot, indexer);
3269     if (!newNode.first) {
3270         return;
3271     }
3272 
3273     for (PcpNodeRef childNode : Pcp_GetChildren(srcNode)) {
3274         if (!PcpIsSpecializeArc(childNode.GetArcType())) {
3275             _PropagateSpecializesTreeToRoot(
3276                 index, newNode.first, childNode, newNode.first,
3277                 childNode.GetMapToParent(), srcTreeRoot, indexer);
3278         }
3279     }
3280 }
3281 
3282 static void
_FindSpecializesToPropagateToRoot(PcpPrimIndex * index,PcpNodeRef node,Pcp_PrimIndexer * indexer)3283 _FindSpecializesToPropagateToRoot(
3284     PcpPrimIndex* index,
3285     PcpNodeRef node,
3286     Pcp_PrimIndexer* indexer)
3287 {
3288     // XXX:RelocatesSourceNodes: This node may be a placeholder
3289     // implied arc under a relocation node that is only present
3290     // to allow class-based arcs to be implied up the prim index.
3291     // These placeholders are not valid sources of opinions, so
3292     // we can cut off our search for specializes to propagate.
3293     const PcpNodeRef parentNode = node.GetParentNode();
3294     const bool nodeIsRelocatesPlaceholder =
3295         parentNode != node.GetOriginNode() &&
3296         parentNode.GetArcType() == PcpArcTypeRelocate &&
3297         parentNode.GetSite() == node.GetSite();
3298     if (nodeIsRelocatesPlaceholder) {
3299         return;
3300     }
3301 
3302     if (PcpIsSpecializeArc(node.GetArcType())) {
3303         PCP_INDEXING_MSG(
3304             indexer, node, node.GetRootNode(),
3305             "Propagating specializes arc %s to root",
3306             Pcp_FormatSite(node.GetSite()).c_str());
3307 
3308         // HACK: When we propagate specializes arcs from the root
3309         // to their origin in _PropagateArcsToOrigin, we will mark
3310         // them as inert=false. However, we will *not* do the same
3311         // for any of the implied specializes that originate from
3312         // that arc -- they will be left with inert=true.
3313         //
3314         // If we wind up having to propagate these implied specializes
3315         // back to the root, we will wind up copying the inert=true
3316         // flag, which isn't what we want. Instead of trying to fix
3317         // up the implied specializes in _PropagateArcsToOrigin,
3318         // it's much simpler if we just deal with that here by forcing
3319         // the specializes node to inert=false.
3320         node.SetInert(false);
3321 
3322         _PropagateSpecializesTreeToRoot(
3323             index, index->GetRootNode(), node, node,
3324             node.GetMapToRoot(), node, indexer);
3325     }
3326 
3327     for (PcpNodeRef childNode : Pcp_GetChildren(node)) {
3328         _FindSpecializesToPropagateToRoot(index, childNode, indexer);
3329     }
3330 }
3331 
3332 static void
_PropagateArcsToOrigin(PcpPrimIndex * index,PcpNodeRef parentNode,PcpNodeRef srcNode,const PcpMapExpression & mapToParent,const PcpNodeRef & srcTreeRoot,Pcp_PrimIndexer * indexer)3333 _PropagateArcsToOrigin(
3334     PcpPrimIndex* index,
3335     PcpNodeRef parentNode,
3336     PcpNodeRef srcNode,
3337     const PcpMapExpression& mapToParent,
3338     const PcpNodeRef& srcTreeRoot,
3339     Pcp_PrimIndexer* indexer)
3340 {
3341     // Don't skip implied specializes tasks as we propagate arcs back
3342     // to the origin.  If one of the arcs we propagate back is another
3343     // specializes arc, we need to ensure that arc is propagated back
3344     // to the root later on.
3345     const bool skipImpliedSpecializes = false;
3346 
3347     std::pair<PcpNodeRef, bool> newNode = _PropagateNodeToParent(
3348         parentNode, srcNode, skipImpliedSpecializes,
3349         mapToParent, srcTreeRoot, indexer);
3350     if (!newNode.first) {
3351         return;
3352     }
3353 
3354     // If we've propagated the given srcNode back to a new node
3355     // beneath the origin parentNode, it means srcNode represents a
3356     // newly-discovered composition arc at this level of namespace.
3357     // Instead of propagating the rest of the subtree beneath srcNode
3358     // we mark them as inert and allow composition to recompose the
3359     // subtree beneath the newly-created node. This avoids issues with
3360     // duplicate tasks being queued up for the propagated subtree,
3361     // which would lead to failed verifies later on.
3362     // See SpecializesAndAncestralArcs museum case.
3363     //
3364     // XXX:
3365     // This approach keeps the code simple but does cause us to redo
3366     // composition work unnecessarily, since recomposing the subgraph
3367     // beneath the newly-created node should yield the same result as
3368     // the subgraph beneath srcNode. Ideally, we would remove this
3369     // code, propagate the entire subgraph beneath srcNode, but find
3370     // some way to avoid enqueing tasks for the propagated nodes.
3371     if (newNode.second) {
3372         _InertSubtree(srcNode);
3373         return;
3374     }
3375 
3376     for (PcpNodeRef childNode : Pcp_GetChildren(srcNode)) {
3377         _PropagateArcsToOrigin(
3378             index, newNode.first, childNode, childNode.GetMapToParent(),
3379             srcTreeRoot, indexer);
3380     }
3381 }
3382 
3383 static void
_FindArcsToPropagateToOrigin(PcpPrimIndex * index,const PcpNodeRef & node,Pcp_PrimIndexer * indexer)3384 _FindArcsToPropagateToOrigin(
3385     PcpPrimIndex* index,
3386     const PcpNodeRef& node,
3387     Pcp_PrimIndexer* indexer)
3388 {
3389     TF_VERIFY(PcpIsSpecializeArc(node.GetArcType()));
3390 
3391     for (PcpNodeRef childNode : Pcp_GetChildren(node)) {
3392         PCP_INDEXING_MSG(
3393             indexer, childNode, node.GetOriginNode(),
3394             "Propagating arcs under %s to specializes origin %s",
3395             Pcp_FormatSite(childNode.GetSite()).c_str(),
3396             Pcp_FormatSite(node.GetOriginNode().GetSite()).c_str());
3397 
3398         _PropagateArcsToOrigin(
3399             index, node.GetOriginNode(), childNode, childNode.GetMapToParent(),
3400             node, indexer);
3401     }
3402 }
3403 
3404 // Opinions from specializes arcs, including those that are implied across
3405 // other arcs, are always weaker than the target of those arcs.  Conceptually,
3406 // this means that opinions from all specializes arcs (and any encapsulated
3407 // arcs) come after all other opinions.
3408 //
3409 //                                 ref
3410 // For instance,          Model ---------> Ref
3411 // given this example:    |                |
3412 //                        +- Instance      +- Instance
3413 //                        |   :            |   :
3414 //                        |   : implied    |   : specializes
3415 //                        |   v            |   v
3416 //                        +- Class         +- Class
3417 //
3418 // The intended strength ordering is for /Model/Instance is:
3419 //   [/Model/Instance, /Ref/Instance, /Model/Class, /Ref/Class].
3420 //
3421 // To achieve this, we propagate specializes subgraphs in the prim index
3422 // to the root of the graph.  Strength ordering will then place the
3423 // specializes arcs at the end of the graph, after all other arcs.
3424 //
3425 // We need to reverse this process when we discover additional arcs
3426 // beneath the specializes subgraphs that have been propagated to the
3427 // root.  This can happen if there are namespace children beneath the
3428 // source of a specializes arc with their own arcs.  This can also
3429 // happen if we discover variants after processing implied specializes.
3430 //
3431 // When we encounter this situation, the specializes subgraph is
3432 // propagated back to its origin.  The primary purpose of this is to
3433 // allow any implied arcs to be propagated to the necessary locations
3434 // using the already-existing mechanisms.  Once that's done,
3435 // the subgraph will be propagated back to the root.
3436 //
3437 static void
_EvalImpliedSpecializes(PcpPrimIndex * index,const PcpNodeRef & node,Pcp_PrimIndexer * indexer)3438 _EvalImpliedSpecializes(
3439     PcpPrimIndex* index,
3440     const PcpNodeRef& node,
3441     Pcp_PrimIndexer* indexer)
3442 {
3443     PCP_INDEXING_PHASE(
3444         indexer, node,
3445         "Evaluating implied specializes at %s",
3446         Pcp_FormatSite(node.GetSite()).c_str());
3447 
3448     // If this is the root node, there is no need to propagate specializes.
3449     if (!node.GetParentNode())
3450         return;
3451 
3452     if (_IsPropagatedSpecializesNode(node)) {
3453         _FindArcsToPropagateToOrigin(index, node, indexer);
3454     }
3455     else {
3456         _FindSpecializesToPropagateToRoot(index, node, indexer);
3457     }
3458 }
3459 
3460 ////////////////////////////////////////////////////////////////////////
3461 // Variants
3462 
3463 static bool
_ComposeVariantSelectionForNode(const PcpNodeRef & node,const SdfPath & pathInNode,const std::string & vset,std::string * vsel,PcpNodeRef * nodeWithVsel,PcpPrimIndexOutputs * outputs)3464 _ComposeVariantSelectionForNode(
3465     const PcpNodeRef& node,
3466     const SdfPath& pathInNode,
3467     const std::string & vset,
3468     std::string *vsel,
3469     PcpNodeRef *nodeWithVsel,
3470     PcpPrimIndexOutputs *outputs)
3471 {
3472     TF_VERIFY(!pathInNode.IsEmpty());
3473 
3474     // We are using path-translation to walk between nodes, so we
3475     // are working exclusively in namespace paths, which must have
3476     // no variant selection.
3477     TF_VERIFY(!pathInNode.ContainsPrimVariantSelection(),
3478               "Unexpected variant selection in namespace path <%s>",
3479               pathInNode.GetText());
3480 
3481     // If this node has an authored selection, use that.
3482     // Note that we use this even if the authored selection is
3483     // the empty string, which explicitly selects no variant.
3484     if (node.CanContributeSpecs()) {
3485         PcpLayerStackSite site(node.GetLayerStack(), pathInNode);
3486         // pathInNode is a namespace path, not a storage path,
3487         // so it will contain no variant selection (as verified above).
3488         // To find the storage site, we need to insert any variant
3489         // selection for this node.
3490         if (node.GetArcType() == PcpArcTypeVariant) {
3491             site.path = pathInNode.ReplacePrefix(
3492                 node.GetPath().StripAllVariantSelections(),
3493                 node.GetPath());
3494         }
3495 
3496         if (PcpComposeSiteVariantSelection(
3497                 site.layerStack, site.path, vset, vsel)) {
3498             *nodeWithVsel = node;
3499             return true;
3500         }
3501     }
3502 
3503     return false;
3504 }
3505 
3506 // Check the tree of nodes rooted at the given node for any node
3507 // representing a prior selection for the given variant set for the path.
3508 static bool
_FindPriorVariantSelection(const PcpNodeRef & node,const SdfPath & pathInRoot,int ancestorRecursionDepth,const std::string & vset,std::string * vsel,PcpNodeRef * nodeWithVsel)3509 _FindPriorVariantSelection(
3510     const PcpNodeRef& node,
3511     const SdfPath &pathInRoot,
3512     int ancestorRecursionDepth,
3513     const std::string & vset,
3514     std::string *vsel,
3515     PcpNodeRef *nodeWithVsel)
3516 {
3517     // If this node represents a variant selection at the same
3518     // effective depth of namespace, then check its selection.
3519     if (node.GetArcType() == PcpArcTypeVariant &&
3520         node.GetDepthBelowIntroduction() == ancestorRecursionDepth) {
3521         const SdfPath nodePathAtIntroduction = node.GetPathAtIntroduction();
3522         const std::pair<std::string, std::string> nodeVsel =
3523             nodePathAtIntroduction.GetVariantSelection();
3524         if (nodeVsel.first == vset) {
3525             // The node has a variant selection for the variant set we're
3526             // looking for, but we still have to check that the node actually
3527             // represents the prim path we're choosing a variant selection for
3528             // (as opposed to a different prim path that just happens to have
3529             // a variant set with the same name.
3530             //
3531             // Note that we have to map search prim path back down this node
3532             // to compare it as it was mapped up to the root of this node's
3533             // graph before being passed to this function.
3534             const SdfPath pathInNode =
3535                 node.GetMapToRoot().MapTargetToSource(pathInRoot);
3536             // If the path didn't translate to this node, it won't translate
3537             // to any of the node's children, so we might as well early out
3538             // here.
3539             if (pathInNode.IsEmpty()) {
3540                 return false;
3541             }
3542             if (nodePathAtIntroduction.GetPrimPath() == pathInNode) {
3543                 *vsel = nodeVsel.second;
3544                 *nodeWithVsel = node;
3545                 return true;
3546             }
3547         }
3548     }
3549     TF_FOR_ALL(child, Pcp_GetChildrenRange(node)) {
3550         if (_FindPriorVariantSelection(
3551                 *child, pathInRoot, ancestorRecursionDepth,
3552                 vset, vsel, nodeWithVsel)) {
3553             return true;
3554         }
3555     }
3556     return false;
3557 }
3558 
3559 typedef std::pair<PcpPrimIndex_StackFrame*, PcpNodeRef> _StackFrameAndChildNode;
3560 typedef std::vector<_StackFrameAndChildNode> _StackFrameAndChildNodeVector;
3561 
3562 static bool
_ComposeVariantSelectionAcrossStackFrames(const PcpNodeRef & node,const SdfPath & pathInNode,const std::string & vset,std::string * vsel,_StackFrameAndChildNodeVector * stackFrames,PcpNodeRef * nodeWithVsel,PcpPrimIndexOutputs * outputs)3563 _ComposeVariantSelectionAcrossStackFrames(
3564     const PcpNodeRef& node,
3565     const SdfPath& pathInNode,
3566     const std::string & vset,
3567     std::string *vsel,
3568     _StackFrameAndChildNodeVector *stackFrames,
3569     PcpNodeRef *nodeWithVsel,
3570     PcpPrimIndexOutputs *outputs)
3571 {
3572     // Compose variant selection in strong-to-weak order.
3573     if (_ComposeVariantSelectionForNode(
3574             node, pathInNode, vset, vsel, nodeWithVsel, outputs)) {
3575         return true;
3576     }
3577 
3578     // If we're in recursive prim index construction and hit the end
3579     // of a graph produced by the current stack frame, we need to look
3580     // at the next stack frame to continue the traversal to the next
3581     // part of the graph.
3582     //
3583     // XXX: See XXX comment in _ComposeVariantSelection. This probably has
3584     //      the same bug. The real fix would be to figure out where the
3585     //      graph for the next stack frame would be inserted into the
3586     //      current node's children in the below for loop and deal with it
3587     //      there.
3588     const bool atEndOfStack =
3589         (!stackFrames->empty() &&
3590          node == stackFrames->back().first->parentNode);
3591     if (atEndOfStack) {
3592         const _StackFrameAndChildNode nextFrame = stackFrames->back();
3593         stackFrames->pop_back();
3594 
3595         const PcpNodeRef& childNode = nextFrame.second;
3596         const SdfPath pathInChildNode =
3597             nextFrame.first->arcToParent->mapToParent
3598             .MapTargetToSource(pathInNode);
3599 
3600         if (!pathInChildNode.IsEmpty()) {
3601             return _ComposeVariantSelectionAcrossStackFrames(
3602                 childNode, pathInChildNode, vset, vsel, stackFrames,
3603                 nodeWithVsel, outputs);
3604         }
3605 
3606         return false;
3607     }
3608 
3609     TF_FOR_ALL(child, Pcp_GetChildrenRange(node)) {
3610         const PcpNodeRef& childNode = *child;
3611         const SdfPath pathInChildNode =
3612             childNode.GetMapToParent().MapTargetToSource(pathInNode);
3613 
3614         if (!pathInChildNode.IsEmpty() &&
3615             _ComposeVariantSelectionAcrossStackFrames(
3616                 *child, pathInChildNode, vset, vsel, stackFrames,
3617                 nodeWithVsel, outputs)) {
3618             return true;
3619         }
3620     }
3621 
3622     return false;
3623 }
3624 
3625 // Convert from the given node and the given path at the node to the
3626 // root node and the path mapped to the root node by traversing up the
3627 // parent nodes.
3628 static bool
_ConvertToRootNodeAndPath(PcpNodeRef * node,SdfPath * path)3629 _ConvertToRootNodeAndPath(PcpNodeRef *node, SdfPath *path)
3630 {
3631     // This function assumes the given path is not empty to begin with so
3632     // return true if this is already the root node.
3633     if (!node->GetParentNode()) {
3634         return true;
3635     }
3636     *path = node->GetMapToRoot().MapSourceToTarget(*path);
3637     *node = node->GetRootNode();
3638     // Return whether the path translates fully up to the root node.
3639     return !path->IsEmpty();
3640 }
3641 
3642 static void
_ComposeVariantSelection(int ancestorRecursionDepth,PcpPrimIndex_StackFrame * previousFrame,PcpNodeRef node,const SdfPath & pathInNode,const std::string & vset,std::string * vsel,PcpNodeRef * nodeWithVsel,PcpPrimIndexOutputs * outputs)3643 _ComposeVariantSelection(
3644     int ancestorRecursionDepth,
3645     PcpPrimIndex_StackFrame *previousFrame,
3646     PcpNodeRef node,
3647     const SdfPath &pathInNode,
3648     const std::string &vset,
3649     std::string *vsel,
3650     PcpNodeRef *nodeWithVsel,
3651     PcpPrimIndexOutputs *outputs)
3652 {
3653     TRACE_FUNCTION();
3654     TF_VERIFY(!pathInNode.IsEmpty());
3655     TF_VERIFY(!pathInNode.ContainsPrimVariantSelection(),
3656               "%s", pathInNode.GetText());
3657 
3658     // We want to look for variant selections in all nodes that have been
3659     // added up to this point.  Note that Pcp may pick up variant
3660     // selections from weaker locations than the node for which
3661     // we are evaluating variants.
3662     //
3663     // See bug 106950 and TrickyVariantWeakerSelection for more details.
3664     //
3665     // This is really a simple strength-order traversal of the
3666     // current prim index. It is complicated by the fact that we
3667     // may be in the middle of recursive calls to Pcp_BuildPrimIndex
3668     // that are building up subgraphs that will eventually be joined
3669     // together. To deal with this, we need to keep track of the
3670     // stack frames for these recursive calls so that we can traverse
3671     // the prim index as if it were fully constructed.
3672     //
3673     // Translate the given path up to the root node of the *entire*
3674     // prim index under construction, keeping track of when we need
3675     // to hop across a stack frame.
3676     _StackFrameAndChildNodeVector previousStackFrames;
3677     PcpNodeRef rootNode = node;
3678     SdfPath pathInRoot = pathInNode;
3679     _ConvertToRootNodeAndPath(&rootNode, &pathInRoot);
3680 
3681     // First check if we have already resolved this variant set in the current
3682     // stack frame. Try all nodes in all parent frames; ancestorRecursionDepth
3683     // accounts for any ancestral recursion.
3684     if (_FindPriorVariantSelection(rootNode, pathInRoot,
3685                                    ancestorRecursionDepth,
3686                                    vset, vsel, nodeWithVsel)) {
3687         return;
3688     }
3689 
3690     while (previousFrame) {
3691         // There may not be a valid mapping for the current path across
3692         // the previous stack frame. For example, this may happen when
3693         // trying to compose ancestral variant selections on a sub-root
3694         // reference (see SubrootReferenceAndVariants for an example).
3695         // This failure means there are no further sites with relevant
3696         // variant selection opinions across this stack frame. In this case,
3697         // we break out of the loop and only search the portion of the prim
3698         // index we've traversed.
3699         SdfPath pathInPreviousFrame =
3700             previousFrame->arcToParent->mapToParent.MapSourceToTarget(
3701                 pathInRoot);
3702         PcpNodeRef rootNodeInPreviousFrame = previousFrame->parentNode;
3703         // Note that even if the path can be mapped across the stack frame it
3704         // may not map all the way up to the root of the previous stack frame.
3705         // This can happen when composing an ancestor with a variant set for a
3706         // subroot inherit. Inherit arcs always have an identity mapping so an
3707         // ancestral prim path can still map across the inherit's stack frame,
3708         // but it may not map across other arcs, like references, on the way up
3709         // to the root. In this case we break out of the loop and only search
3710         // the the portion of the index before the stack frame jump.
3711         if (pathInPreviousFrame.IsEmpty() ||
3712             !_ConvertToRootNodeAndPath(&rootNodeInPreviousFrame,
3713                                        &pathInPreviousFrame)) {
3714             break;
3715         }
3716 
3717         // Check if we have already resolved this variant set in this previous
3718         // stack as well.
3719         if (_FindPriorVariantSelection(rootNodeInPreviousFrame,
3720                                        pathInPreviousFrame,
3721                                        ancestorRecursionDepth,
3722                                        vset, vsel, nodeWithVsel)) {
3723             return;
3724         }
3725 
3726         // rootNode is still set to be child of the previous frame's arc which
3727         // is why do this first.
3728         previousStackFrames.push_back(
3729             _StackFrameAndChildNode(previousFrame, rootNode));
3730 
3731         // Update the root node and path to be the root of this previous stack
3732         // frame.
3733         rootNode = rootNodeInPreviousFrame;
3734         pathInRoot = pathInPreviousFrame;
3735 
3736         previousFrame = previousFrame->previousFrame;
3737     }
3738 
3739     // Now recursively walk the prim index in strong-to-weak order
3740     // looking for a variant selection.
3741     _ComposeVariantSelectionAcrossStackFrames(
3742         rootNode, pathInRoot, vset, vsel, &previousStackFrames,
3743         nodeWithVsel, outputs);
3744 }
3745 
3746 static bool
_ShouldUseVariantFallback(const Pcp_PrimIndexer * indexer,const std::string & vset,const std::string & vsel,const std::string & vselFallback,const PcpNodeRef & nodeWithVsel)3747 _ShouldUseVariantFallback(
3748     const Pcp_PrimIndexer *indexer,
3749     const std::string& vset,
3750     const std::string& vsel,
3751     const std::string& vselFallback,
3752     const PcpNodeRef &nodeWithVsel)
3753 {
3754     // Can't use fallback if we don't have one.
3755     if (vselFallback.empty()) {
3756         return false;
3757     }
3758 
3759     // If there's no variant selected then use the default.
3760     if (vsel.empty()) {
3761         return true;
3762     }
3763 
3764     // The "standin" variant set has special behavior, below.
3765     // All other variant sets default when there is no selection.
3766     //
3767     // XXX This logic can be simpler when we remove the old standin stuff
3768     if (vset != "standin") {
3769         return false;
3770     }
3771 
3772     // If we're using the new behavior then the preferences can't win over
3773     // the opinion in vsel.
3774     if (PcpIsNewDefaultStandinBehaviorEnabled()) {
3775         return false;
3776     }
3777 
3778     // From here down we're trying to match the Csd policy, which can
3779     // be rather peculiar.  See bugs 29039 and 32264 for history that
3780     // lead to some of these policies.
3781 
3782     // If nodeWithVsel is a variant node that makes a selection for vset,
3783     // it structurally represents the fact that we have already decided
3784     // which variant selection to use for vset in this primIndex.  In
3785     // this case, we do not want to apply standin preferences, because
3786     // we will have already applied them.
3787     //
3788     // (Applying the policy again here could give us an incorrect result,
3789     // because this might be a different nodeWithVsel than was used
3790     // originally to apply the policy.)
3791     if (nodeWithVsel.GetArcType() == PcpArcTypeVariant      &&
3792         nodeWithVsel.GetPath().IsPrimVariantSelectionPath() &&
3793         nodeWithVsel.GetPath().GetVariantSelection().first == vset) {
3794         return false;
3795     }
3796 
3797     // Use the standin preference if the authored selection came from
3798     // inside the payload.
3799     for (PcpNodeRef n = nodeWithVsel; n; n = n.GetParentNode()) {
3800         if (n.GetArcType() == PcpArcTypePayload) {
3801             return true;
3802         }
3803     }
3804 
3805     // Use vsel if it came from a session layer, otherwise check the
3806     // standin preferences. For efficiency, we iterate over the full
3807     // layer stack instead of using PcpLayerStack::GetSessionLayerStack.
3808     const SdfLayerHandle rootLayer =
3809         indexer->rootSite.layerStack->GetIdentifier().rootLayer;
3810     TF_FOR_ALL(layer, indexer->rootSite.layerStack->GetLayers()) {
3811         if (*layer == rootLayer) {
3812             break;
3813         }
3814 
3815         static const TfToken field = SdfFieldKeys->VariantSelection;
3816 
3817         const VtValue& value =
3818             (*layer)->GetField(indexer->rootSite.path, field);
3819         if (value.IsHolding<SdfVariantSelectionMap>()) {
3820             const SdfVariantSelectionMap & vselMap =
3821                 value.UncheckedGet<SdfVariantSelectionMap>();
3822             SdfVariantSelectionMap::const_iterator i = vselMap.find(vset);
3823             if (i != vselMap.end() && i->second == vsel) {
3824                 // Standin selection came from the session layer.
3825                 return false;
3826             }
3827         }
3828     }
3829 
3830     // If we don't have a standin selection in the root node then check
3831     // the standin preferences.
3832     if (nodeWithVsel.GetArcType() != PcpArcTypeRoot) {
3833         return true;
3834     }
3835 
3836     return false;
3837 }
3838 
3839 static std::string
_ChooseBestFallbackAmongOptions(const std::string & vset,const std::set<std::string> & vsetOptions,const PcpVariantFallbackMap & variantFallbacks)3840 _ChooseBestFallbackAmongOptions(
3841     const std::string &vset,
3842     const std::set<std::string> &vsetOptions,
3843     const PcpVariantFallbackMap& variantFallbacks)
3844 {
3845     PcpVariantFallbackMap::const_iterator vsetIt = variantFallbacks.find(vset);
3846     if (vsetIt != variantFallbacks.end()) {
3847         for (const auto &vselIt: vsetIt->second) {
3848             if (vsetOptions.find(vselIt) != vsetOptions.end()) {
3849                 return vselIt;
3850             }
3851         }
3852     }
3853     return std::string();
3854 }
3855 
3856 static void
_AddVariantArc(Pcp_PrimIndexer * indexer,const PcpNodeRef & node,const std::string & vset,int vsetNum,const std::string & vsel)3857 _AddVariantArc(Pcp_PrimIndexer *indexer,
3858                const PcpNodeRef &node,
3859                const std::string &vset,
3860                int vsetNum,
3861                const std::string &vsel)
3862 {
3863     // Variants do not remap the scenegraph's namespace, they simply
3864     // represent a branch off into a different section of the layer
3865     // storage.  For this reason, the source site includes the
3866     // variant selection but the mapping function is identity.
3867     SdfPath varPath = node.GetSite().path.AppendVariantSelection(vset, vsel);
3868     if (_AddArc(PcpArcTypeVariant,
3869                 /* parent = */ node,
3870                 /* origin = */ node,
3871                 PcpLayerStackSite( node.GetLayerStack(), varPath ),
3872                 /* mapExpression = */ PcpMapExpression::Identity(),
3873                 /* arcSiblingNum = */ vsetNum,
3874                 /* directNodeShouldContributeSpecs = */ true,
3875                 /* includeAncestralOpinions = */ false,
3876                 /* requirePrimAtTarget = */ false,
3877                 /* skipDuplicateNodes = */ false,
3878                 indexer )) {
3879         // If we expanded a variant set, it may have introduced new
3880         // authored variant selections, so we must retry any pending
3881         // variant tasks as authored tasks.
3882         indexer->RetryVariantTasks();
3883     }
3884 }
3885 
3886 static void
_EvalNodeVariantSets(PcpPrimIndex * index,const PcpNodeRef & node,Pcp_PrimIndexer * indexer)3887 _EvalNodeVariantSets(
3888     PcpPrimIndex *index,
3889     const PcpNodeRef& node,
3890     Pcp_PrimIndexer *indexer)
3891 {
3892     PCP_INDEXING_PHASE(
3893         indexer, node,
3894         "Evaluating variant sets at %s",
3895         Pcp_FormatSite(node.GetSite()).c_str());
3896 
3897     if (!node.CanContributeSpecs())
3898         return;
3899 
3900     std::vector<std::string> vsetNames;
3901     PcpComposeSiteVariantSets(node, &vsetNames);
3902 
3903     for (int vsetNum=0, numVsets=vsetNames.size();
3904          vsetNum < numVsets; ++vsetNum) {
3905         indexer->AddTask(Task(Task::Type::EvalNodeVariantAuthored,
3906                               node, std::move(vsetNames[vsetNum]), vsetNum));
3907     }
3908 }
3909 
3910 static void
_EvalNodeAuthoredVariant(PcpPrimIndex * index,const PcpNodeRef & node,Pcp_PrimIndexer * indexer,const std::string & vset,int vsetNum)3911 _EvalNodeAuthoredVariant(
3912     PcpPrimIndex *index,
3913     const PcpNodeRef& node,
3914     Pcp_PrimIndexer *indexer,
3915     const std::string &vset,
3916     int vsetNum)
3917 {
3918     PCP_INDEXING_PHASE(
3919         indexer, node,
3920         "Evaluating authored selections for variant set %s at %s",
3921         vset.c_str(),
3922         Pcp_FormatSite(node.GetSite()).c_str());
3923 
3924     if (!node.CanContributeSpecs())
3925         return;
3926 
3927     // Compose options.
3928     std::set<std::string> vsetOptions;
3929     PcpComposeSiteVariantSetOptions(node, vset, &vsetOptions);
3930 
3931     // Determine what the fallback selection would be.
3932     // Generally speaking, authoring opinions win over fallbacks, however if
3933     // MENV30_ENABLE_NEW_DEFAULT_STANDIN_BEHAVIOR==false then that is not
3934     // always the case, and we must check the fallback here first.
3935     // TODO Remove this once we phase out the old behavior!
3936     const std::string vselFallback =
3937         _ChooseBestFallbackAmongOptions( vset, vsetOptions,
3938                                          *indexer->inputs.variantFallbacks );
3939     if (!vselFallback.empty()) {
3940         PCP_INDEXING_MSG(
3941             indexer, node, "Found fallback {%s=%s}",
3942             vset.c_str(),
3943             vselFallback.c_str());
3944     }
3945 
3946     // Determine the authored variant selection for this set, if any.
3947     std::string vsel;
3948     PcpNodeRef nodeWithVsel;
3949     _ComposeVariantSelection(indexer->ancestorRecursionDepth,
3950                              indexer->previousFrame, node,
3951                              node.GetPath().StripAllVariantSelections(),
3952                              vset, &vsel, &nodeWithVsel,
3953                              indexer->outputs);
3954     if (!vsel.empty()) {
3955         PCP_INDEXING_MSG(
3956             indexer, node, "Found variant selection {%s=%s} at %s",
3957             vset.c_str(),
3958             vsel.c_str(),
3959             Pcp_FormatSite(nodeWithVsel.GetSite()).c_str());
3960     }
3961     // Check if we should use the fallback
3962     if (_ShouldUseVariantFallback(indexer, vset, vsel, vselFallback,
3963                                   nodeWithVsel)) {
3964         PCP_INDEXING_MSG(indexer, node, "Deferring to variant fallback");
3965         indexer->AddTask(Task(Task::Type::EvalNodeVariantFallback,
3966                               node, vset, vsetNum));
3967         return;
3968     }
3969     // If no variant was chosen, do not expand this variant set.
3970     if (vsel.empty()) {
3971         PCP_INDEXING_MSG(indexer, node,
3972                          "No variant selection found for set '%s'",
3973                          vset.c_str());
3974         indexer->AddTask(Task(Task::Type::EvalNodeVariantNoneFound,
3975                               node, vset, vsetNum));
3976         return;
3977     }
3978 
3979     _AddVariantArc(indexer, node, vset, vsetNum, vsel);
3980 }
3981 
3982 static void
_EvalNodeFallbackVariant(PcpPrimIndex * index,const PcpNodeRef & node,Pcp_PrimIndexer * indexer,const std::string & vset,int vsetNum)3983 _EvalNodeFallbackVariant(
3984     PcpPrimIndex *index,
3985     const PcpNodeRef& node,
3986     Pcp_PrimIndexer *indexer,
3987     const std::string &vset,
3988     int vsetNum)
3989 {
3990     PCP_INDEXING_PHASE(
3991         indexer, node,
3992         "Evaluating fallback selections for variant set %s s at %s",
3993         vset.c_str(),
3994         Pcp_FormatSite(node.GetSite()).c_str());
3995 
3996     if (!node.CanContributeSpecs())
3997         return;
3998 
3999     // Compose options.
4000     std::set<std::string> vsetOptions;
4001     PcpComposeSiteVariantSetOptions(node, vset, &vsetOptions);
4002 
4003     // Determine what the fallback selection would be.
4004     const std::string vsel =
4005         _ChooseBestFallbackAmongOptions( vset, vsetOptions,
4006                                          *indexer->inputs.variantFallbacks );
4007     // If no variant was chosen, do not expand this variant set.
4008     if (vsel.empty()) {
4009         PCP_INDEXING_MSG(indexer, node,
4010                       "No variant fallback found for set '%s'", vset.c_str());
4011         indexer->AddTask(Task(Task::Type::EvalNodeVariantNoneFound,
4012                               node, vset, vsetNum));
4013         return;
4014     }
4015 
4016     _AddVariantArc(indexer, node, vset, vsetNum, vsel);
4017 }
4018 
4019 ////////////////////////////////////////////////////////////////////////
4020 // Prim Specs
4021 
4022 void
_GatherNodesRecursively(const PcpNodeRef & node,std::vector<PcpNodeRef> * result)4023 _GatherNodesRecursively(
4024     const PcpNodeRef& node,
4025     std::vector<PcpNodeRef> *result)
4026 {
4027     result->push_back(node);
4028 
4029     // Strength-order (strong-to-weak) traversal.
4030     TF_FOR_ALL(child, Pcp_GetChildrenRange(node)) {
4031         _GatherNodesRecursively(*child, result);
4032     }
4033 }
4034 
4035 static void
_EnforcePermissions(PcpPrimIndex * primIndex,PcpErrorVector * allErrors)4036 _EnforcePermissions(
4037     PcpPrimIndex *primIndex,
4038     PcpErrorVector *allErrors)
4039 {
4040     TRACE_FUNCTION();
4041 
4042     PcpNodeRef rootNode = primIndex->GetRootNode();
4043     TF_VERIFY(rootNode);
4044 
4045     // Gather all the nodes that may contribute prim specs.
4046     std::vector<PcpNodeRef> allNodes;
4047     _GatherNodesRecursively(rootNode, &allNodes);
4048 
4049     // Go backwards through the list of nodes, looking for prim specs.
4050     // If we find a node that isn't public, we stash it away, and then
4051     // issue an error for any stronger nodes, which violate permissions.
4052     PcpNodeRef privateNode;
4053     TF_REVERSE_FOR_ALL(nodeIter, allNodes) {
4054         PcpNodeRef curNode = *nodeIter;
4055         if (!curNode.CanContributeSpecs()) {
4056             // XXX: Should we be setting permissionDenied?
4057             continue;
4058         }
4059 
4060         // If we previously found a private node, the current node is
4061         // not allowed to contribute specs.
4062         if (privateNode) {
4063             curNode.SetRestricted(true);
4064 
4065             // Check for prim specs in reverse strength order (weak-to-strong).
4066             // XXX: We should avoid collecting the prim specs here
4067             //      and then again later when building the prim stack.
4068             //      If we built the prim stack first we'd have to
4069             //      discard stuff we discover to be private;  that's
4070             //      going to be rare so it's okay.
4071             if (curNode.HasSpecs()) {
4072                 TF_REVERSE_FOR_ALL(layer,
4073                                    curNode.GetLayerStack()->GetLayers()) {
4074                     if ((*layer)->HasSpec(curNode.GetPath())) {
4075                         // The current node has a prim spec. Since this violates
4076                         // permissions, we ignore this node's specs and report
4077                         // an error.
4078                         PcpErrorPrimPermissionDeniedPtr err =
4079                             PcpErrorPrimPermissionDenied::New();
4080                         err->rootSite =
4081                             PcpSite(curNode.GetRootNode().GetSite());
4082                         err->site = PcpSite(curNode.GetSite());
4083                         err->privateSite = PcpSite(privateNode.GetSite());
4084                         Pcp_PrimIndexer::RecordError(err, primIndex, allErrors);
4085                         break;
4086                     }
4087                 }
4088             }
4089         }
4090         // If this node is private, any subsequent nodes will generate
4091         // errors (see above).
4092         if (!privateNode &&
4093             curNode.GetPermission() != SdfPermissionPublic) {
4094             privateNode = curNode;
4095         }
4096     }
4097 }
4098 
4099 void
Pcp_RescanForSpecs(PcpPrimIndex * index,bool usd,bool updateHasSpecs)4100 Pcp_RescanForSpecs(PcpPrimIndex *index, bool usd, bool updateHasSpecs)
4101 {
4102     TfAutoMallocTag2 tag("Pcp", "Pcp_RescanForSpecs");
4103 
4104     if (usd) {
4105         // USD does not retain prim stacks.
4106         // We do need to update the HasSpecs flag on nodes, however.
4107         if (updateHasSpecs) {
4108             TF_FOR_ALL(nodeIt, index->GetNodeRange()) {
4109                 nodeIt->SetHasSpecs(PcpComposeSiteHasPrimSpecs(*nodeIt));
4110             }
4111         }
4112     } else {
4113         Pcp_CompressedSdSiteVector primSites;
4114         TF_FOR_ALL(nodeIt, index->GetNodeRange()) {
4115             PcpNodeRef node = *nodeIt;
4116             bool nodeHasSpecs = false;
4117             if (!node.IsCulled() && node.CanContributeSpecs()) {
4118                 // Add prim specs in strength order (strong-to-weak).
4119                 const SdfLayerRefPtrVector& layers =
4120                     node.GetLayerStack()->GetLayers();
4121                 const SdfPath& path = node.GetPath();
4122                 for (size_t i = 0, n = layers.size(); i != n; ++i) {
4123                     if (layers[i]->HasSpec(path)) {
4124                         nodeHasSpecs = true;
4125                         primSites.push_back(node.GetCompressedSdSite(i));
4126                     }
4127                 }
4128             }
4129             if (updateHasSpecs) {
4130                 node.SetHasSpecs(nodeHasSpecs);
4131             }
4132         }
4133         index->_primStack.swap(primSites);
4134     }
4135 }
4136 
4137 ////////////////////////////////////////////////////////////////////////
4138 
4139 static std::pair<
4140     PcpNodeRef_PrivateChildrenConstIterator,
4141     PcpNodeRef_PrivateChildrenConstIterator>
_GetDirectChildRange(const PcpNodeRef & node,PcpArcType arcType)4142 _GetDirectChildRange(const PcpNodeRef& node, PcpArcType arcType)
4143 {
4144     auto range = std::make_pair(
4145         PcpNodeRef_PrivateChildrenConstIterator(node),
4146         PcpNodeRef_PrivateChildrenConstIterator(node, /* end = */ true));
4147     for (; range.first != range.second; ++range.first) {
4148         const PcpNodeRef& childNode = *range.first;
4149         if (childNode.GetArcType() == arcType && !childNode.IsDueToAncestor()) {
4150             break;
4151         }
4152     }
4153 
4154     auto end = range.second;
4155     for (range.second = range.first; range.second != end; ++range.second) {
4156         const PcpNodeRef& childNode = *range.second;
4157         if (childNode.GetArcType() != arcType || childNode.IsDueToAncestor()) {
4158             break;
4159         }
4160     }
4161 
4162     return range;
4163 }
4164 
4165 static bool
_ComputedAssetPathWouldCreateDifferentNode(const PcpNodeRef & node,const std::string & newAssetPath)4166 _ComputedAssetPathWouldCreateDifferentNode(
4167     const PcpNodeRef& node, const std::string& newAssetPath)
4168 {
4169     // Get any file format arguments that were originally used to open the
4170     // layer so we can apply them to the new asset path.
4171     const SdfLayerRefPtr& nodeRootLayer =
4172         node.GetLayerStack()->GetIdentifier().rootLayer;
4173 
4174     std::string oldAssetPath;
4175     SdfLayer::FileFormatArguments oldArgs;
4176     if (!TF_VERIFY(SdfLayer::SplitIdentifier(
4177             nodeRootLayer->GetIdentifier(), &oldAssetPath, &oldArgs))) {
4178         return true;
4179     }
4180 
4181     // If no such layer is already open, this asset path must indicate a
4182     // layer that differs from the given node's root layer.
4183     const SdfLayerHandle newLayer = SdfLayer::Find(newAssetPath, oldArgs);
4184     if (!newLayer) {
4185         return true;
4186     }
4187 
4188     // Otherwise, if this layer differs from the given node's root layer,
4189     // this asset path would result in a different node during composition.
4190     return nodeRootLayer != newLayer;
4191 }
4192 
4193 bool
Pcp_NeedToRecomputeDueToAssetPathChange(const PcpPrimIndex & index)4194 Pcp_NeedToRecomputeDueToAssetPathChange(const PcpPrimIndex& index)
4195 {
4196     // Scan the index for any direct composition arcs that target another
4197     // layer. If any exist, try to determine if the asset paths that were
4198     // computed to load those layers would now target a different layer.
4199     // If so, this prim index needs to be recomputed to include that
4200     // new layer.
4201     for (const PcpNodeRef& node : index.GetNodeRange()) {
4202         if (!node.CanContributeSpecs()) {
4203             continue;
4204         }
4205 
4206         // Handle reference arcs. See _EvalNodeReferences.
4207         auto refNodeRange = _GetDirectChildRange(node, PcpArcTypeReference);
4208         if (refNodeRange.first != refNodeRange.second) {
4209             SdfReferenceVector refs;
4210             PcpSourceArcInfoVector sourceInfo;
4211             PcpComposeSiteReferences(node, &refs, &sourceInfo);
4212             TF_VERIFY(refs.size() == sourceInfo.size());
4213 
4214             const size_t numReferenceArcs =
4215                 std::distance(refNodeRange.first, refNodeRange.second) ;
4216             if (numReferenceArcs != refs.size()) {
4217                 // This could happen if there was some scene description
4218                 // change that added/removed references, but also if a
4219                 // layer couldn't be opened when this index was computed.
4220                 // We conservatively mark this index as needing recomputation
4221                 // in the latter case to simplify things.
4222                 return true;
4223             }
4224 
4225             for (size_t i = 0; i < refs.size(); ++i, ++refNodeRange.first) {
4226                 // Skip internal references since there's no asset path
4227                 // computation that occurs when processing them.
4228                 if (refs[i].GetAssetPath().empty()) {
4229                     continue;
4230                 }
4231 
4232                 // PcpComposeSiteReferences will have filled in each
4233                 // SdfReference with the same asset path that would be used
4234                 // during composition to open layers.
4235                 const std::string& anchoredAssetPath = refs[i].GetAssetPath();
4236 
4237                 if (_ComputedAssetPathWouldCreateDifferentNode(
4238                         *refNodeRange.first, anchoredAssetPath)) {
4239                     return true;
4240                 }
4241             }
4242         }
4243 
4244         // Handle payload arcs. See _EvalNodePayloads.
4245         // XXX: This is identical to the loop for references except for the
4246         // type and PcpComposeSite* function. When payloads are fully conformed
4247         // to references, it would be worth refactoring this.
4248         auto payloadNodeRange = _GetDirectChildRange(node, PcpArcTypePayload);
4249         if (payloadNodeRange.first != payloadNodeRange.second) {
4250             SdfPayloadVector payloads;
4251             PcpSourceArcInfoVector sourceInfo;
4252             PcpComposeSitePayloads(node, &payloads, &sourceInfo);
4253 
4254             const size_t numPayloadArcs =
4255                 std::distance(payloadNodeRange.first, payloadNodeRange.second) ;
4256             if (numPayloadArcs != payloads.size()) {
4257                 // This could happen if there was some scene description
4258                 // change that added/removed payloads, but also if a
4259                 // layer couldn't be opened when this index was computed.
4260                 // We conservatively mark this index as needing recomputation
4261                 // in the latter case to simplify things.
4262                 return true;
4263             }
4264 
4265             for (size_t i = 0; i < payloads.size(); ++i, ++payloadNodeRange.first) {
4266                 // Skip internal payloads since there's no asset path
4267                 // computation that occurs when processing them.
4268                 if (payloads[i].GetAssetPath().empty()) {
4269                     continue;
4270                 }
4271 
4272                 // PcpComposeSitePayloads will have filled in each
4273                 // SdfPayload with the same asset path that would be used
4274                 // during composition to open layers.
4275                 const std::string& anchoredAssetPath = payloads[i].GetAssetPath();
4276 
4277                 if (_ComputedAssetPathWouldCreateDifferentNode(
4278                         *payloadNodeRange.first, anchoredAssetPath)) {
4279                     return true;
4280                 }
4281             }
4282         }
4283     }
4284 
4285     return false;
4286 }
4287 
4288 ////////////////////////////////////////////////////////////////////////
4289 // Index Construction
4290 
4291 static void
_ConvertNodeForChild(PcpNodeRef node,const PcpPrimIndexInputs & inputs)4292 _ConvertNodeForChild(
4293     PcpNodeRef node,
4294     const PcpPrimIndexInputs& inputs)
4295 {
4296     // Because the child site is at a deeper level of namespace than
4297     // the parent, there may no longer be any specs.
4298     if (node.HasSpecs()) {
4299         node.SetHasSpecs(PcpComposeSiteHasPrimSpecs(node));
4300     }
4301 
4302     // Inert nodes are just placeholders, so we can skip computing these
4303     // bits of information since these nodes shouldn't have any opinions to
4304     // contribute.
4305     if (!node.IsInert() && node.HasSpecs()) {
4306         if (!inputs.usd) {
4307             // If the parent's permission is private, it will be inherited by
4308             // the child. Otherwise, we recompute it here.
4309             if (node.GetPermission() == SdfPermissionPublic) {
4310                 node.SetPermission(PcpComposeSitePermission(node));
4311             }
4312 
4313             // If the parent had symmetry, it will be inherited by the child.
4314             // Otherwise, we recompute it here.
4315             if (!node.HasSymmetry()) {
4316                 node.SetHasSymmetry(PcpComposeSiteHasSymmetry(node));
4317             }
4318         }
4319     }
4320 
4321     // Arbitrary-order traversal.
4322     TF_FOR_ALL(child, Pcp_GetChildrenRange(node)) {
4323         _ConvertNodeForChild(*child, inputs);
4324     }
4325 }
4326 
4327 // Returns true if the given node can be culled, false otherwise.
4328 //
4329 // In general, a node can be culled if no descendant nodes contribute
4330 // opinions, i.e., no specs are found in that subtree. There are some
4331 // exceptions that are documented in the function.
4332 static inline bool
_NodeCanBeCulled(const PcpNodeRef & node,const PcpLayerStackSite & rootSite)4333 _NodeCanBeCulled(
4334     const PcpNodeRef& node,
4335     const PcpLayerStackSite& rootSite)
4336 {
4337     // Trivial case if this node has already been culled.
4338     // This could happen if this node was culled ancestrally.
4339     if (node.IsCulled()) {
4340 #ifdef PCP_DIAGNOSTIC_VALIDATION
4341         TF_VERIFY(!node.IsRootNode());
4342 #endif // PCP_DIAGNOSTIC_VALIDATION
4343         return true;
4344     }
4345 
4346     // The root node of a prim index is never culled. If needed, this
4347     // node will be culled when attached to another prim index in _AddArc.
4348     if (node.IsRootNode()) {
4349         return false;
4350     }
4351 
4352     // We cannot cull any nodes that denote the addition of a new arc.
4353     // These nodes introduce dependencies and must be discoverable.
4354     // This usually isn't an issue -- arcs are generally added to sites
4355     // where prim specs exist, so even without this check these nodes
4356     // wouldn't be culled anyway. However, if an arc to a site with no prims
4357     // is added (e.g., a reference to a prim that doesn't exist), we need
4358     // to explicitly keep that around.
4359     if (node.GetDepthBelowIntroduction() == 0) {
4360         return false;
4361     }
4362 
4363     // XXX: The following are unfortunate cases where Pcp needs to keep
4364     //      around nodes it would otherwise cull solely for consumers in Csd.
4365     //      In theory, Csd would be able to generate this info by computing
4366     //      unculled prim indices as needed, but in these cases, that
4367     //      performance cost is too great.
4368 
4369     // Because of how Csd composes symmetry across namespace ancestors in a
4370     // layer stack before composing across arcs, Pcp needs to keep around
4371     // any node that directly OR ancestrally provides symmetry info.
4372     if (node.HasSymmetry()) {
4373         return false;
4374     }
4375 
4376     // CsdPrim::GetBases wants to return the path of all prims in the
4377     // composed scene from which this prim inherits opinions. To ensure
4378     // Csd has all the info it needs for this, Pcp has to avoid culling any
4379     // subroot prim inherit nodes in the root layer stack. To see why, consider:
4380     //
4381     // root layer stack      ref layer stack
4382     //                       /GlobalClass <--+
4383     //                                       | (root prim inh)
4384     // /Model_1  (ref) ----> /Model    ------+
4385     //                        + SymArm <-+
4386     //                                   | (subroot prim inh)
4387     //                        + LArm   --+
4388     //
4389     // The prim index for /Model_1/LArm would normally have the inherit nodes
4390     // for /GlobalClass/LArm and /Model_1/SymArm culled, as there are no specs
4391     // for either in the root layer stack. The nature of root classes implies
4392     // that, if no specs for /GlobalClass exist in the root layer, there is
4393     // no /GlobalClass in the composed scene. So, we don't have to protect
4394     // root prim inherits from being culled. However, because of referencing,
4395     // the subroot inherit /Model_1/SymArm *does* exist in the composed scene.
4396     // So, we can't cull that node -- GetBases needs it.
4397     if (node.GetArcType() == PcpArcTypeInherit &&
4398         node.GetLayerStack() == rootSite.layerStack) {
4399         // We check the intro path of the origin node as there are cases where
4400         // a new implied inherit arc is created from an ancestral inherit
4401         // which means it will be introduced from a subroot path even if the
4402         // original inherit node is a root prim path.
4403         const PcpNodeRef &originNode =
4404             node.GetOriginNode() == node.GetParentNode() ?
4405             node : node.GetOriginRootNode();
4406         if (!originNode.GetPathAtIntroduction().IsRootPrimPath()) {
4407             return false;
4408         }
4409     }
4410 
4411     // If any subtree beneath this node wasn't culled, we can't cull
4412     // this node either.
4413     TF_FOR_ALL(it, Pcp_GetChildrenRange(node)) {
4414         const PcpNodeRef& child = *it;
4415         if (!child.IsCulled()) {
4416             return false;
4417         }
4418     }
4419 
4420     // If this node contributes any opinions, we can't cull it.
4421     if (node.HasSpecs() && node.CanContributeSpecs())
4422         return false;
4423 
4424     return true;
4425 }
4426 
4427 // Helper that recursively culls subtrees at and under the given node.
4428 static void
_CullSubtreesWithNoOpinions(PcpNodeRef node,const PcpLayerStackSite & rootSite)4429 _CullSubtreesWithNoOpinions(
4430     PcpNodeRef node,
4431     const PcpLayerStackSite& rootSite)
4432 {
4433     // Recurse and attempt to cull all children first. Order doesn't matter.
4434     TF_FOR_ALL(child, Pcp_GetChildrenRange(node)) {
4435         // XXX:
4436         // We propagate and maintain duplicate node structure in the graph
4437         // for specializes arcs, so when we cull we need to ensure we do so
4438         // in both places consistently. For simplicity, we're going to skip
4439         // this for now and not cull beneath any specializes arcs.
4440         if (PcpIsSpecializeArc(child->GetArcType())) {
4441             continue;
4442         }
4443 
4444         _CullSubtreesWithNoOpinions(*child, rootSite);
4445     }
4446 
4447     // Now, mark this node as culled if we can. These nodes will be
4448     // removed from the prim index at the end of prim indexing.
4449     if (_NodeCanBeCulled(node, rootSite)) {
4450         node.SetCulled(true);
4451     }
4452 }
4453 
4454 // Helper that sets any nodes that cannot have overrides on name children
4455 // as inert.
4456 struct Pcp_DisableNonInstanceableNodesVisitor
4457 {
VisitPcp_DisableNonInstanceableNodesVisitor4458     bool Visit(PcpNodeRef node, bool nodeIsInstanceable)
4459     {
4460         if (!nodeIsInstanceable) {
4461             node.SetInert(true);
4462             return true;
4463         }
4464         return false;
4465     }
4466 };
4467 
4468 const PcpPrimIndex &
Pcp_ComputePrimIndexWithCompatibleInputs(PcpCache & cache,const SdfPath & path,const PcpPrimIndexInputs & inputs,PcpErrorVector * allErrors)4469 Pcp_ComputePrimIndexWithCompatibleInputs(
4470     PcpCache &cache,
4471     const SdfPath & path, const PcpPrimIndexInputs &inputs,
4472     PcpErrorVector *allErrors) {
4473     return cache._ComputePrimIndexWithCompatibleInputs(path, inputs, allErrors);
4474 }
4475 
4476 static void
_BuildInitialPrimIndexFromAncestor(const PcpLayerStackSite & site,const PcpLayerStackSite & rootSite,int ancestorRecursionDepth,PcpPrimIndex_StackFrame * previousFrame,bool evaluateImpliedSpecializes,bool rootNodeShouldContributeSpecs,const PcpPrimIndexInputs & inputs,PcpPrimIndexOutputs * outputs)4477 _BuildInitialPrimIndexFromAncestor(
4478     const PcpLayerStackSite &site,
4479     const PcpLayerStackSite &rootSite,
4480     int ancestorRecursionDepth,
4481     PcpPrimIndex_StackFrame *previousFrame,
4482     bool evaluateImpliedSpecializes,
4483     bool rootNodeShouldContributeSpecs,
4484     const PcpPrimIndexInputs& inputs,
4485     PcpPrimIndexOutputs* outputs)
4486 {
4487     bool ancestorIsInstanceable = false;
4488 
4489     // If we're asking for a prim index in the cache's layer stack and
4490     // we're not excluding anything from the prim index then ask the
4491     // cache for the prim index.  This will get it from the cache if
4492     // it's already there, and cache it and record dependencies if not.
4493     if (!previousFrame &&
4494         evaluateImpliedSpecializes &&
4495         inputs.cache->GetLayerStack() == site.layerStack &&
4496         inputs.cache->GetPrimIndexInputs().IsEquivalentTo(inputs)) {
4497         // Get prim index through our cache.  This ensures the lifetime
4498         // of layer stacks brought in by ancestors.
4499         const PcpPrimIndex& parentIndex =
4500             inputs.parentIndex ? *inputs.parentIndex :
4501             Pcp_ComputePrimIndexWithCompatibleInputs(
4502                 *inputs.cache, site.path.GetParentPath(), inputs,
4503                 &outputs->allErrors);
4504 
4505         // Clone the parent's graph..
4506         outputs->primIndex.SetGraph(
4507             PcpPrimIndex_Graph::New(parentIndex.GetGraph()));
4508 
4509         ancestorIsInstanceable = parentIndex.IsInstanceable();
4510 
4511         PCP_INDEXING_UPDATE(
4512             _GetOriginatingIndex(previousFrame, outputs),
4513             outputs->primIndex.GetRootNode(),
4514             "Retrieved index for <%s> from cache",
4515             site.path.GetParentPath().GetText());
4516     }
4517     else {
4518         // First build the prim index for the given site's parent.
4519         // Note that variants and payloads are always evaluated to ensure
4520         // ancestral opinions are picked up.
4521         const PcpLayerStackSite parentSite(site.layerStack,
4522                                            site.path.GetParentPath());
4523 
4524         Pcp_BuildPrimIndex(parentSite, parentSite,
4525                            ancestorRecursionDepth+1,
4526                            evaluateImpliedSpecializes,
4527                            /* Always pick up ancestral opinions from variants
4528                               evaluateVariants = */ true,
4529                            /* rootNodeShouldContributeSpecs = */ true,
4530                            previousFrame, inputs, outputs);
4531 
4532         ancestorIsInstanceable =
4533             Pcp_PrimIndexIsInstanceable(outputs->primIndex);
4534     }
4535 
4536     // If the ancestor graph is an instance, mark every node that cannot
4537     // have opinions about name children as inert. This will cause any
4538     // opinions in restricted locations to be ignored.
4539     if (ancestorIsInstanceable) {
4540         Pcp_DisableNonInstanceableNodesVisitor visitor;
4541         Pcp_TraverseInstanceableStrongToWeak(outputs->primIndex, &visitor);
4542     }
4543 
4544     // Adjust the parent graph for this child.
4545     PcpPrimIndex_GraphPtr graph = outputs->primIndex.GetGraph();
4546     graph->AppendChildNameToAllSites(site.path);
4547 
4548     // Reset the 'has payload' flag on this prim index.
4549     // This flag should only be set when a prim introduces a payload,
4550     // not when any of its parents introduced a payload.
4551     // Also reset the payload state in the outputs for the same reason.
4552     //
4553     // XXX:
4554     // Updating the graph's payload flag may cause a new copy of the prim
4555     // index graph to be created, which is wasteful if this graph will
4556     // later set the flag back to its original value. It would be
4557     // better to defer setting this bit until we have the final
4558     // answer.
4559     graph->SetHasPayloads(false);
4560     outputs->payloadState = PcpPrimIndexOutputs::NoPayload;
4561 
4562     PcpNodeRef rootNode = outputs->primIndex.GetRootNode();
4563     _ConvertNodeForChild(rootNode, inputs);
4564 
4565     if (inputs.cull) {
4566         _CullSubtreesWithNoOpinions(rootNode, rootSite);
4567     }
4568 
4569     // Force the root node to inert if the caller has specified that the
4570     // root node should not contribute specs. Note that the node
4571     // may already be set to inert when applying instancing restrictions
4572     // above.
4573     if (!rootNodeShouldContributeSpecs) {
4574         rootNode.SetInert(true);
4575     }
4576 
4577     PCP_INDEXING_UPDATE(
4578         _GetOriginatingIndex(previousFrame, outputs),
4579         rootNode,
4580         "Adjusted ancestral index for %s", site.path.GetName().c_str());
4581 }
4582 
4583 static void
Pcp_BuildPrimIndex(const PcpLayerStackSite & site,const PcpLayerStackSite & rootSite,int ancestorRecursionDepth,bool evaluateImpliedSpecializes,bool evaluateVariants,bool rootNodeShouldContributeSpecs,PcpPrimIndex_StackFrame * previousFrame,const PcpPrimIndexInputs & inputs,PcpPrimIndexOutputs * outputs)4584 Pcp_BuildPrimIndex(
4585     const PcpLayerStackSite & site,
4586     const PcpLayerStackSite& rootSite,
4587     int ancestorRecursionDepth,
4588     bool evaluateImpliedSpecializes,
4589     bool evaluateVariants,
4590     bool rootNodeShouldContributeSpecs,
4591     PcpPrimIndex_StackFrame *previousFrame,
4592     const PcpPrimIndexInputs& inputs,
4593     PcpPrimIndexOutputs* outputs )
4594 {
4595     Pcp_PrimIndexingDebug debug(&outputs->primIndex,
4596                                 _GetOriginatingIndex(previousFrame, outputs),
4597                                 site);
4598 
4599     // We only index prims (including the pseudo-root) or variant-selection
4600     // paths, and only with absolute paths.
4601     if (!TF_VERIFY(site.path.IsAbsolutePath() &&
4602                    (site.path.IsAbsoluteRootOrPrimPath() ||
4603                     site.path.IsPrimVariantSelectionPath()),
4604                    "%s", site.path.GetText())) {
4605         return;
4606     }
4607 
4608     // Establish initial PrimIndex contents.
4609     if (site.path.GetPathElementCount() == 0) {
4610         // Base case for the pseudo-root: just use the single site.
4611         outputs->primIndex.SetGraph(PcpPrimIndex_Graph::New(site, inputs.usd));
4612         // Even though the pseudo root spec exists implicitly, don't
4613         // assume that here.
4614         PcpNodeRef node = outputs->primIndex.GetGraph()->GetRootNode();
4615         node.SetHasSpecs(PcpComposeSiteHasPrimSpecs(node));
4616         // Optimization: Since no composition arcs can live on the
4617         // pseudo-root, we can return early.
4618         return;
4619     } else if (site.path.IsPrimVariantSelectionPath()) {
4620         // For variant selection paths, unlike regular prim paths, we do not
4621         // recurse on the parent to obtain ancestral opinions. This is
4622         // because variant arcs are evaluated in the process of evaluating
4623         // the parent path site, which will already account for ancestral
4624         // opinions about the variant itself.
4625         outputs->primIndex.SetGraph(PcpPrimIndex_Graph::New(site, inputs.usd));
4626 
4627         PcpNodeRef node = outputs->primIndex.GetGraph()->GetRootNode();
4628         node.SetHasSpecs(PcpComposeSiteHasPrimSpecs(node));
4629         node.SetInert(!rootNodeShouldContributeSpecs);
4630     } else {
4631         // Start by building and cloning the namespace parent's index.
4632         // This is to account for ancestral opinions: references and
4633         // other arcs introduced by namespace ancestors that might
4634         // contribute opinions to this child.
4635         _BuildInitialPrimIndexFromAncestor(
4636             site, rootSite, ancestorRecursionDepth, previousFrame,
4637             evaluateImpliedSpecializes,
4638             rootNodeShouldContributeSpecs,
4639             inputs, outputs);
4640     }
4641 
4642     // Initialize the task list.
4643     Pcp_PrimIndexer indexer(inputs, outputs, rootSite, ancestorRecursionDepth,
4644                             previousFrame, evaluateImpliedSpecializes,
4645                             evaluateVariants);
4646     indexer.AddTasksForNode( outputs->primIndex.GetRootNode() );
4647 
4648     // Process task list.
4649     bool tasksAreLeft = true;
4650     while (tasksAreLeft) {
4651         Task task = indexer.PopTask();
4652         switch (task.type) {
4653         case Task::Type::EvalNodeRelocations:
4654             _EvalNodeRelocations(&outputs->primIndex, task.node, &indexer);
4655             break;
4656         case Task::Type::EvalImpliedRelocations:
4657             _EvalImpliedRelocations(&outputs->primIndex, task.node, &indexer);
4658             break;
4659         case Task::Type::EvalNodeReferences:
4660             _EvalNodeReferences(&outputs->primIndex, task.node, &indexer);
4661             break;
4662         case Task::Type::EvalNodePayload:
4663             _EvalNodePayloads(&outputs->primIndex, task.node, &indexer);
4664             break;
4665         case Task::Type::EvalNodeInherits:
4666             _EvalNodeInherits(&outputs->primIndex, task.node, &indexer);
4667             break;
4668         case Task::Type::EvalImpliedClasses:
4669             _EvalImpliedClasses(&outputs->primIndex, task.node, &indexer);
4670             break;
4671         case Task::Type::EvalNodeSpecializes:
4672             _EvalNodeSpecializes(&outputs->primIndex, task.node, &indexer);
4673             break;
4674         case Task::Type::EvalImpliedSpecializes:
4675             _EvalImpliedSpecializes(&outputs->primIndex, task.node, &indexer);
4676             break;
4677         case Task::Type::EvalNodeVariantSets:
4678             _EvalNodeVariantSets(&outputs->primIndex, task.node, &indexer);
4679             break;
4680         case Task::Type::EvalNodeVariantAuthored:
4681             _EvalNodeAuthoredVariant(&outputs->primIndex, task.node, &indexer,
4682                                      task.vsetName, task.vsetNum);
4683             break;
4684         case Task::Type::EvalNodeVariantFallback:
4685             _EvalNodeFallbackVariant(&outputs->primIndex, task.node, &indexer,
4686                                      task.vsetName, task.vsetNum);
4687             break;
4688         case Task::Type::EvalNodeVariantNoneFound:
4689             // No-op.  These tasks are just markers for RetryVariantTasks().
4690             break;
4691         case Task::Type::None:
4692             tasksAreLeft = false;
4693             break;
4694         }
4695     }
4696 }
4697 
4698 void
PcpComputePrimIndex(const SdfPath & primPath,const PcpLayerStackPtr & layerStack,const PcpPrimIndexInputs & inputs,PcpPrimIndexOutputs * outputs,ArResolver * resolver)4699 PcpComputePrimIndex(
4700     const SdfPath& primPath,
4701     const PcpLayerStackPtr& layerStack,
4702     const PcpPrimIndexInputs& inputs,
4703     PcpPrimIndexOutputs* outputs,
4704     ArResolver* resolver)
4705 {
4706     TfAutoMallocTag2 tag("Pcp", "PcpComputePrimIndex");
4707 
4708     TRACE_FUNCTION();
4709 
4710     if (!(primPath.IsAbsolutePath()                 &&
4711              (primPath.IsAbsoluteRootOrPrimPath()   ||
4712               primPath.IsPrimVariantSelectionPath()))) {
4713         TF_CODING_ERROR("Path <%s> must be an absolute path to a prim, "
4714                         "a prim variant-selection, or the pseudo-root.",
4715                         primPath.GetText());
4716         return;
4717     }
4718 
4719     ArResolverContextBinder binder(
4720         resolver ? resolver : &ArGetResolver(),
4721         layerStack->GetIdentifier().pathResolverContext);
4722 
4723     const PcpLayerStackSite site(layerStack, primPath);
4724     Pcp_BuildPrimIndex(site, site,
4725                        /* ancestorRecursionDepth = */ 0,
4726                        /* evaluateImpliedSpecializes = */ true,
4727                        /* evaluateVariants = */ true,
4728                        /* rootNodeShouldContributeSpecs = */ true,
4729                        /* previousFrame = */ NULL,
4730                        inputs, outputs);
4731 
4732     // Tag each node that's not allowed to contribute prim specs due to
4733     // permissions. Note that we do this as a post-processing pass here,
4734     // but not in Pcp_BuildPrimIndex(), which gets called recursively above.
4735     // We don't actually need to *enforce* permissions until after the node
4736     // graph has been built. While it's being built, we only need to make
4737     // sure each node's permission is set correctly, which is done in
4738     // _AddArc() and _ConvertNodeForChild(). So we can defer calling
4739     // _EnforcePermissions() until the very end, which saves us from
4740     // doing some redundant work.
4741     if (!inputs.usd) {
4742         _EnforcePermissions(&outputs->primIndex, &outputs->allErrors);
4743     }
4744 
4745     // Determine whether this prim index is instanceable and store that
4746     // information in the prim index. This requires composed metadata
4747     // values, so we do this here after the prim index is fully composed
4748     // instead of in Pcp_BuildPrimIndex.
4749     outputs->primIndex.GetGraph()->SetIsInstanceable(
4750         Pcp_PrimIndexIsInstanceable(outputs->primIndex));
4751 
4752     // We're done modifying the graph, so finalize it.
4753     outputs->primIndex.GetGraph()->Finalize();
4754 
4755     // Collect the prim stack and the node for each prim in the stack.
4756     // Also collect all prim specs found in any node -- this is different
4757     // from the prim stack when nodes don't contribute prim specs.
4758     //
4759     // Note that we *must* do this after the graph is finalized, as
4760     // finalization will cause outstanding PcpNodeRefs to be invalidated.
4761     Pcp_RescanForSpecs(&outputs->primIndex, inputs.usd,
4762                        /* updateHasSpecs */false );
4763 }
4764 
4765 ////////////////////////////////////////////////////////////////////////
4766 // Name children / property names
4767 
4768 // Walk the graph, strong-to-weak, composing prim child names.
4769 // Account for spec children in each layer, list-editing statements,
4770 // and relocations.
4771 static void
_ComposePrimChildNamesAtNode(const PcpPrimIndex & primIndex,const PcpNodeRef & node,bool usd,TfTokenVector * nameOrder,PcpTokenSet * nameSet,PcpTokenSet * prohibitedNameSet)4772 _ComposePrimChildNamesAtNode(
4773     const PcpPrimIndex& primIndex,
4774     const PcpNodeRef& node,
4775     bool usd,
4776     TfTokenVector *nameOrder,
4777     PcpTokenSet *nameSet,
4778     PcpTokenSet *prohibitedNameSet)
4779 {
4780     if (!usd) {
4781         // Apply relocations from just this layer stack.
4782         // Classify them into three groups:  names to add, remove, or replace.
4783         std::set<TfToken> namesToAdd, namesToRemove;
4784         std::map<TfToken, TfToken> namesToReplace;
4785 
4786         // Check for relocations with a child as source.
4787         // See _EvalNodeRelocations for why we use the incremental relocates.
4788         const SdfRelocatesMap & relocatesSourceToTarget =
4789             node.GetLayerStack()->GetIncrementalRelocatesSourceToTarget();
4790         for (SdfRelocatesMap::const_iterator i =
4791                  relocatesSourceToTarget.lower_bound(node.GetPath());
4792              i != relocatesSourceToTarget.end() &&
4793                  i->first.HasPrefix(node.GetPath()); ++i) {
4794             const SdfPath & oldPath = i->first;
4795             const SdfPath & newPath = i->second;
4796 
4797             if (oldPath.GetParentPath() == node.GetPath()) {
4798                 if (newPath.GetParentPath() == node.GetPath()) {
4799                     // Target is the same parent, so this is a rename.
4800                     namesToReplace[oldPath.GetNameToken()] =
4801                         newPath.GetNameToken();
4802                 } else {
4803                     // Target is not the same parent, so this is remove.
4804                     namesToRemove.insert(oldPath.GetNameToken());
4805                 }
4806                 // The source name is now prohibited.
4807                 prohibitedNameSet->insert(oldPath.GetNameToken());
4808             }
4809         }
4810 
4811         // Check for relocations with a child as target.
4812         // See _EvalNodeRelocations for why we use the incremental relocates.
4813         const SdfRelocatesMap & relocatesTargetToSource =
4814             node.GetLayerStack()->GetIncrementalRelocatesTargetToSource();
4815         for (SdfRelocatesMap::const_iterator i =
4816                  relocatesTargetToSource.lower_bound(node.GetPath());
4817              i != relocatesTargetToSource.end() &&
4818                  i->first.HasPrefix(node.GetPath()); ++i) {
4819             const SdfPath & newPath = i->first;
4820             const SdfPath & oldPath = i->second;
4821 
4822             if (newPath.GetParentPath() == node.GetPath()) {
4823                 if (oldPath.GetParentPath() == node.GetPath()) {
4824                     // Source is the same parent, so this is a rename.
4825                     // We will have already handled this above.
4826                 } else {
4827                     // Source is not the same parent, so this is an add.
4828                     if (nameSet->find(newPath.GetNameToken()) ==
4829                         nameSet->end()) {
4830                         namesToAdd.insert(newPath.GetNameToken());
4831                     }
4832                 }
4833             }
4834         }
4835 
4836         // Apply the names to replace or remove.
4837         if (!namesToReplace.empty() || !namesToRemove.empty()) {
4838             // Do one pass, building a list of names to retain.
4839             TfTokenVector namesToRetain;
4840             namesToRetain.reserve( nameOrder->size() );
4841             TF_FOR_ALL(name, *nameOrder) {
4842                 std::map<TfToken, TfToken>::const_iterator i =
4843                     namesToReplace.find(*name);
4844                 if (i != namesToReplace.end()) {
4845                     // This name was replaced.
4846                     const TfToken & newName = i->second;
4847                     nameSet->erase(*name);
4848 
4849                     // Check if newName is already in the nameSet before adding
4850                     // it to the new name order.  newName may already be in
4851                     // the nameSet (and nameOrder) if it was contributed by
4852                     // a child spec from a weaker node.
4853                     //
4854                     // This can happen when a relocation renames X to Y and
4855                     // there is also a child spec for Y across a reference.
4856                     // The intended behavior of the relocation arc is that
4857                     // that "shadow" child Y is silently ignored.  PcpPrimIndex
4858                     // already ignores it when composing Y, but we also need
4859                     // to check for it here, when composing the child names
4860                     // for Y's parent.  See TrickyMultipleRelocations for a
4861                     // test that exercises this.
4862                     //
4863                     // TODO: Although silently ignoring the duplicate
4864                     // name is consistent with Csd's behavior, which we want
4865                     // to preserve for the initial Pcp work, we think this
4866                     // should perhaps be reported as a composition error,
4867                     // since the relocation arc is introducing a name collision.
4868                     //
4869                     if (nameSet->insert(newName).second) {
4870                         // Retain the new name in the same position as the
4871                         // old name.
4872                         namesToRetain.push_back(newName);
4873                     }
4874                 } else if (namesToRemove.find(*name) == namesToRemove.end()) {
4875                     // Retain this name as-is.
4876                     namesToRetain.push_back(*name);
4877                 } else {
4878                     // Do not retain this name.
4879                     nameSet->erase(*name);
4880                 }
4881             }
4882             nameOrder->swap(namesToRetain);
4883         }
4884 
4885         // Append children relocated to under this prim in lexicographic order.
4886         //
4887         // Semantics note: We use alphabetical order as a default ordering
4888         // because there is no required statement of ordering among prims
4889         // relocated here.  (We will, however, subsequently apply
4890         // re-ordering restatements in this site's layer stack.)
4891         //
4892         nameOrder->insert(nameOrder->end(), namesToAdd.begin(),
4893                           namesToAdd.end());
4894         nameSet->insert(namesToAdd.begin(), namesToAdd.end());
4895     }
4896 
4897     // Compose the site's local names over the current result.
4898     if (node.CanContributeSpecs()) {
4899         PcpComposeSiteChildNames(
4900             node.GetLayerStack()->GetLayers(), node.GetPath(),
4901             SdfChildrenKeys->PrimChildren, nameOrder, nameSet,
4902             &SdfFieldKeys->PrimOrder);
4903     }
4904 
4905     // Post-conditions, for debugging.
4906     // Disabled by default to avoid extra overhead.
4907 #ifdef PCP_DIAGNOSTIC_VALIDATION
4908     TF_VERIFY(nameSet->size() == nameOrder->size());
4909     TF_VERIFY(*nameSet == PcpTokenSet(nameOrder->begin(), nameOrder->end()));
4910 #endif // PCP_DIAGNOSTIC_VALIDATION
4911 }
4912 
4913 static void
_ComposePrimChildNames(const PcpPrimIndex & primIndex,const PcpNodeRef & node,bool usd,TfTokenVector * nameOrder,PcpTokenSet * nameSet,PcpTokenSet * prohibitedNameSet)4914 _ComposePrimChildNames( const PcpPrimIndex& primIndex,
4915                         const PcpNodeRef& node,
4916                         bool usd,
4917                         TfTokenVector *nameOrder,
4918                         PcpTokenSet *nameSet,
4919                         PcpTokenSet *prohibitedNameSet )
4920 {
4921     if (node.IsCulled()) {
4922         return;
4923     }
4924 
4925     // Reverse strength-order traversal (weak-to-strong).
4926     TF_REVERSE_FOR_ALL(child, Pcp_GetChildrenRange(node)) {
4927         _ComposePrimChildNames(primIndex, *child, usd,
4928                                nameOrder, nameSet, prohibitedNameSet);
4929     }
4930 
4931     _ComposePrimChildNamesAtNode(
4932         primIndex, node, usd, nameOrder, nameSet, prohibitedNameSet);
4933 }
4934 
4935 // Helper struct for _ComposePrimChildNamesForInstance, see comments
4936 // below.
4937 struct Pcp_PrimChildNameVisitor
4938 {
Pcp_PrimChildNameVisitorPcp_PrimChildNameVisitor4939     Pcp_PrimChildNameVisitor( const PcpPrimIndex& primIndex,
4940                               bool usd,
4941                               TfTokenVector *nameOrder,
4942                               PcpTokenSet *nameSet,
4943                               PcpTokenSet *prohibitedNameSet )
4944         : _primIndex(primIndex)
4945         , _usd(usd)
4946         , _nameOrder(nameOrder)
4947         , _nameSet(nameSet)
4948         , _prohibitedNameSet(prohibitedNameSet)
4949     {
4950     }
4951 
VisitPcp_PrimChildNameVisitor4952     void Visit(PcpNodeRef node, bool nodeIsInstanceable)
4953     {
4954         if (nodeIsInstanceable) {
4955             _ComposePrimChildNamesAtNode(
4956                 _primIndex, node, _usd,
4957                 _nameOrder, _nameSet, _prohibitedNameSet);
4958         }
4959     }
4960 
4961 private:
4962     const PcpPrimIndex& _primIndex;
4963     bool _usd;
4964     TfTokenVector* _nameOrder;
4965     PcpTokenSet* _nameSet;
4966     PcpTokenSet* _prohibitedNameSet;
4967 };
4968 
4969 static void
_ComposePrimChildNamesForInstance(const PcpPrimIndex & primIndex,bool usd,TfTokenVector * nameOrder,PcpTokenSet * nameSet,PcpTokenSet * prohibitedNameSet)4970 _ComposePrimChildNamesForInstance( const PcpPrimIndex& primIndex,
4971                                    bool usd,
4972                                    TfTokenVector *nameOrder,
4973                                    PcpTokenSet *nameSet,
4974                                    PcpTokenSet *prohibitedNameSet )
4975 {
4976     Pcp_PrimChildNameVisitor visitor(
4977         primIndex, usd, nameOrder, nameSet, prohibitedNameSet);
4978     Pcp_TraverseInstanceableWeakToStrong(primIndex, &visitor);
4979 }
4980 
4981 static void
_ComposePrimPropertyNames(const PcpPrimIndex & primIndex,const PcpNodeRef & node,bool isUsd,TfTokenVector * nameOrder,PcpTokenSet * nameSet)4982 _ComposePrimPropertyNames( const PcpPrimIndex& primIndex,
4983                            const PcpNodeRef& node,
4984                            bool isUsd,
4985                            TfTokenVector *nameOrder,
4986                            PcpTokenSet *nameSet )
4987 {
4988     if (node.IsCulled()) {
4989         return;
4990     }
4991 
4992     // Reverse strength-order traversal (weak-to-strong).
4993     TF_REVERSE_FOR_ALL(child, Pcp_GetChildrenRange(node)) {
4994         _ComposePrimPropertyNames(
4995             primIndex, *child, isUsd, nameOrder, nameSet );
4996     }
4997 
4998     // Compose the site's local names over the current result.
4999     if (node.CanContributeSpecs()) {
5000         PcpComposeSiteChildNames(
5001             node.GetLayerStack()->GetLayers(), node.GetPath(),
5002             SdfChildrenKeys->PropertyChildren, nameOrder, nameSet,
5003             isUsd ? nullptr : &SdfFieldKeys->PropertyOrder);
5004     }
5005 }
5006 
5007 void
ComputePrimChildNames(TfTokenVector * nameOrder,PcpTokenSet * prohibitedNameSet) const5008 PcpPrimIndex::ComputePrimChildNames( TfTokenVector *nameOrder,
5009                                      PcpTokenSet *prohibitedNameSet ) const
5010 {
5011     if (!_graph) {
5012         return;
5013     }
5014 
5015     TRACE_FUNCTION();
5016 
5017     // Provide a set with any existing nameOrder contents.
5018     PcpTokenSet nameSet(nameOrder->begin(), nameOrder->end());
5019 
5020     // Walk the graph to compose prim child names.
5021     if (IsInstanceable()) {
5022         _ComposePrimChildNamesForInstance(
5023             *this, IsUsd(),
5024             nameOrder, &nameSet, prohibitedNameSet);
5025     }
5026     else {
5027         _ComposePrimChildNames(
5028             *this, GetRootNode(), IsUsd(),
5029             nameOrder, &nameSet, prohibitedNameSet);
5030     }
5031 
5032     // Remove prohibited names from the composed prim child names.
5033     if (!prohibitedNameSet->empty()) {
5034         nameOrder->erase(
5035             std::remove_if(nameOrder->begin(), nameOrder->end(),
5036                 [prohibitedNameSet](const TfToken& name) {
5037                     return prohibitedNameSet->find(name)
5038                         != prohibitedNameSet->end();
5039                 }),
5040             nameOrder->end());
5041     }
5042 }
5043 
5044 void
ComputePrimPropertyNames(TfTokenVector * nameOrder) const5045 PcpPrimIndex::ComputePrimPropertyNames( TfTokenVector *nameOrder ) const
5046 {
5047     if (!_graph) {
5048         return;
5049     }
5050 
5051     TRACE_FUNCTION();
5052 
5053     // Provide a set with any existing nameOrder contents.
5054     PcpTokenSet nameSet;
5055     nameSet.insert_unique(nameOrder->begin(), nameOrder->end());
5056 
5057     // Walk the graph to compose prim child names.
5058     _ComposePrimPropertyNames(
5059         *this, GetRootNode(), IsUsd(), nameOrder, &nameSet);
5060 }
5061 
5062 PXR_NAMESPACE_CLOSE_SCOPE
5063