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