1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #include "pxr/pxr.h"
25 #include "pxr/usd/usd/stage.h"
26
27 #include "pxr/usd/usd/attribute.h"
28 #include "pxr/usd/usd/attributeQuery.h"
29 #include "pxr/usd/usd/clip.h"
30 #include "pxr/usd/usd/clipCache.h"
31 #include "pxr/usd/usd/clipSet.h"
32 #include "pxr/usd/usd/common.h"
33 #include "pxr/usd/usd/debugCodes.h"
34 #include "pxr/usd/usd/instanceCache.h"
35 #include "pxr/usd/usd/interpolators.h"
36 #include "pxr/usd/usd/notice.h"
37 #include "pxr/usd/usd/prim.h"
38 #include "pxr/usd/usd/primDefinition.h"
39 #include "pxr/usd/usd/primRange.h"
40 #include "pxr/usd/usd/primTypeInfoCache.h"
41 #include "pxr/usd/usd/relationship.h"
42 #include "pxr/usd/usd/resolver.h"
43 #include "pxr/usd/usd/resolveInfo.h"
44 #include "pxr/usd/usd/schemaBase.h"
45 #include "pxr/usd/usd/schemaRegistry.h"
46 #include "pxr/usd/usd/stageCache.h"
47 #include "pxr/usd/usd/stageCacheContext.h"
48 #include "pxr/usd/usd/tokens.h"
49 #include "pxr/usd/usd/usdFileFormat.h"
50 #include "pxr/usd/usd/valueUtils.h"
51
52 #include "pxr/usd/pcp/changes.h"
53 #include "pxr/usd/pcp/errors.h"
54 #include "pxr/usd/pcp/layerStack.h"
55 #include "pxr/usd/pcp/layerStackIdentifier.h"
56 #include "pxr/usd/pcp/site.h"
57
58 // used for creating prims
59 #include "pxr/usd/sdf/attributeSpec.h"
60 #include "pxr/usd/sdf/changeBlock.h"
61 #include "pxr/usd/sdf/layerUtils.h"
62 #include "pxr/usd/sdf/primSpec.h"
63 #include "pxr/usd/sdf/relationshipSpec.h"
64 #include "pxr/usd/sdf/fileFormat.h"
65 #include "pxr/usd/sdf/schema.h"
66 #include "pxr/usd/sdf/types.h"
67
68 #include "pxr/base/trace/trace.h"
69 #include "pxr/usd/ar/resolver.h"
70 #include "pxr/usd/ar/resolverContext.h"
71 #include "pxr/usd/ar/resolverContextBinder.h"
72 #include "pxr/usd/ar/resolverScopedCache.h"
73
74 #include "pxr/base/gf/interval.h"
75 #include "pxr/base/gf/multiInterval.h"
76
77 #include "pxr/base/arch/demangle.h"
78 #include "pxr/base/arch/pragmas.h"
79
80 #include "pxr/base/plug/plugin.h"
81 #include "pxr/base/plug/registry.h"
82 #include "pxr/base/tf/enum.h"
83 #include "pxr/base/tf/envSetting.h"
84 #include "pxr/base/tf/hashset.h"
85 #include "pxr/base/tf/mallocTag.h"
86 #include "pxr/base/tf/ostreamMethods.h"
87 #include "pxr/base/tf/pyLock.h"
88 #include "pxr/base/tf/registryManager.h"
89 #include "pxr/base/tf/scoped.h"
90 #include "pxr/base/tf/span.h"
91 #include "pxr/base/tf/stl.h"
92 #include "pxr/base/tf/stringUtils.h"
93 #include "pxr/base/work/dispatcher.h"
94 #include "pxr/base/work/loops.h"
95 #include "pxr/base/work/utils.h"
96 #include "pxr/base/work/withScopedParallelism.h"
97
98 #include <boost/optional.hpp>
99 #include <boost/iterator/transform_iterator.hpp>
100 #include <boost/utility/in_place_factory.hpp>
101
102 #include <tbb/spin_rw_mutex.h>
103 #include <tbb/spin_mutex.h>
104
105 #include <algorithm>
106 #include <functional>
107 #include <memory>
108 #include <mutex>
109 #include <string>
110 #include <utility>
111 #include <vector>
112
113 PXR_NAMESPACE_OPEN_SCOPE
114
115 using boost::make_transform_iterator;
116 using std::pair;
117 using std::make_pair;
118 using std::map;
119 using std::string;
120 using std::vector;
121
122 // ------------------------------------------------------------------------- //
123 // UsdStage Helpers
124 // ------------------------------------------------------------------------- //
125
126 using _ColorConfigurationFallbacks = pair<SdfAssetPath, TfToken>;
127
128 // Fetch the color configuration fallback values from the plugins.
TF_MAKE_STATIC_DATA(_ColorConfigurationFallbacks,_colorConfigurationFallbacks)129 TF_MAKE_STATIC_DATA(_ColorConfigurationFallbacks, _colorConfigurationFallbacks)
130 {
131 PlugPluginPtrVector plugs = PlugRegistry::GetInstance().GetAllPlugins();
132 for (const auto& plug : plugs) {
133 JsObject metadata = plug->GetMetadata();
134 JsValue dictVal;
135 if (TfMapLookup(metadata, "UsdColorConfigFallbacks", &dictVal)) {
136 if (!dictVal.Is<JsObject>()) {
137 TF_CODING_ERROR(
138 "%s[UsdColorConfigFallbacks] was not a dictionary.",
139 plug->GetName().c_str());
140 continue;
141 }
142
143 JsObject dict = dictVal.Get<JsObject>();
144 for (const auto& d : dict) {
145 const std::string &key = d.first;
146 if (key == SdfFieldKeys->ColorConfiguration) {
147 if (!d.second.IsString()) {
148 TF_CODING_ERROR("'colorConfiguration' value in "
149 "%s[UsdColorConfigFallbacks] must be a string.",
150 plug->GetName().c_str());
151 continue;
152 }
153 std::string colorConfig = d.second.GetString();
154 if (!colorConfig.empty()) {
155 _colorConfigurationFallbacks->first =
156 SdfAssetPath(colorConfig);
157 }
158 } else if (key == SdfFieldKeys->ColorManagementSystem) {
159 if (!d.second.IsString()) {
160 TF_CODING_ERROR("'colorManagementSystem' value in "
161 "%s[UsdColorConfigFallbacks] must be a string.",
162 plug->GetName().c_str());
163 continue;
164 }
165 std::string cms = d.second.GetString();
166 if (!cms.empty()) {
167 _colorConfigurationFallbacks->second = TfToken(cms);
168 }
169 } else {
170 TF_CODING_ERROR("Unknown key '%s' found in "
171 "%s[UsdColorConfigFallbacks].", key.c_str(),
172 plug->GetName().c_str());
173 }
174 }
175 // Once we file a plugInfo file with UsdColorConfigFallbacks and
176 // there were no errors in retrieving the fallbacks, skip the
177 // remaining plugins. There should only be one plugin site-wide
178 // that defines this.
179 continue;
180 }
181 }
182 }
183
184 //
185 // Usd lets you configure the fallback variants to use in plugInfo.json.
186 // This static data goes to discover that on first access.
187 //
TF_MAKE_STATIC_DATA(PcpVariantFallbackMap,_usdGlobalVariantFallbackMap)188 TF_MAKE_STATIC_DATA(PcpVariantFallbackMap, _usdGlobalVariantFallbackMap)
189 {
190 PcpVariantFallbackMap fallbacks;
191
192 PlugPluginPtrVector plugs = PlugRegistry::GetInstance().GetAllPlugins();
193 for (const auto& plug : plugs) {
194 JsObject metadata = plug->GetMetadata();
195 JsValue dictVal;
196 if (TfMapLookup(metadata, "UsdVariantFallbacks", &dictVal)) {
197 if (!dictVal.Is<JsObject>()) {
198 TF_CODING_ERROR(
199 "%s[UsdVariantFallbacks] was not a dictionary.",
200 plug->GetName().c_str());
201 continue;
202 }
203 JsObject dict = dictVal.Get<JsObject>();
204 for (const auto& d : dict) {
205 std::string vset = d.first;
206 if (!d.second.IsArray()) {
207 TF_CODING_ERROR(
208 "%s[UsdVariantFallbacks] value for %s must "
209 "be an arrays.",
210 plug->GetName().c_str(), vset.c_str());
211 continue;
212 }
213 std::vector<std::string> vsels =
214 d.second.GetArrayOf<std::string>();
215 if (!vsels.empty()) {
216 fallbacks[vset] = vsels;
217 }
218 }
219 }
220 }
221
222 *_usdGlobalVariantFallbackMap = fallbacks;
223 }
224 static tbb::spin_rw_mutex _usdGlobalVariantFallbackMapMutex;
225
226 PcpVariantFallbackMap
GetGlobalVariantFallbacks()227 UsdStage::GetGlobalVariantFallbacks()
228 {
229 tbb::spin_rw_mutex::scoped_lock
230 lock(_usdGlobalVariantFallbackMapMutex, /*write=*/false);
231 return *_usdGlobalVariantFallbackMap;
232 }
233
234 void
SetGlobalVariantFallbacks(const PcpVariantFallbackMap & fallbacks)235 UsdStage::SetGlobalVariantFallbacks(const PcpVariantFallbackMap &fallbacks)
236 {
237 tbb::spin_rw_mutex::scoped_lock
238 lock(_usdGlobalVariantFallbackMapMutex, /*write=*/true);
239 *_usdGlobalVariantFallbackMap = fallbacks;
240 }
241
242 // Returns the SdfLayerOffset that maps times in \a layer in the local layer
243 // stack of \a node up to the root of the pcp node tree. Use
244 // SdfLayerOffset::GetInverse() to go the other direction.
245 template <class LayerPtr>
246 static SdfLayerOffset
_GetLayerToStageOffset(const PcpNodeRef & pcpNode,const LayerPtr & layer)247 _GetLayerToStageOffset(const PcpNodeRef& pcpNode,
248 const LayerPtr& layer)
249 {
250 // PERFORMANCE: This is cached in the PcpNode and should be cheap.
251 // Get the node-local path and layer offset.
252 const SdfLayerOffset &nodeToRootNodeOffset =
253 pcpNode.GetMapToRoot().GetTimeOffset();
254
255 //
256 // Each sublayer may have a layer offset, so we must adjust the
257 // time accordingly here.
258 //
259 // This is done by first translating the current layer's time to
260 // the root layer's time (for this LayerStack) followed by a
261 // translation from the local PcpNode to the root PcpNode.
262 //
263 SdfLayerOffset localOffset = nodeToRootNodeOffset;
264
265 if (const SdfLayerOffset *layerToRootLayerOffset =
266 pcpNode.GetLayerStack()->GetLayerOffsetForLayer(layer)) {
267 localOffset = localOffset * (*layerToRootLayerOffset);
268 }
269
270 // NOTE: FPS is intentionally excluded here; in Usd FPS is treated as pure
271 // metadata, and does not factor into the layer offset scale. Additionally,
272 // it is a validation error to compose mixed frame rates. This was done as a
273 // performance optimization.
274
275 return localOffset;
276 }
277
278 char const *_dormantMallocTagID = "UsdStages in aggregate";
279
280 inline
281 std::string
_StageTag(const std::string & id)282 _StageTag(const std::string &id)
283 {
284 return "UsdStage: @" + id + "@";
285 }
286
287 class UsdStage::_PendingChanges
288 {
289 public:
290 // Set to true to force ObjectsChanged notice to indicate recomposition
291 // of the pseudo-root regardless of what was actually recomposed.
292 bool notifyPseudoRootResync = false;
293
294 PcpChanges pcpChanges;
295
296 using PathsToChangesMap = UsdNotice::ObjectsChanged::_PathsToChangesMap;
297 PathsToChangesMap recomposeChanges, otherResyncChanges, otherInfoChanges;
298 };
299
300 // ------------------------------------------------------------------------- //
301 // UsdStage implementation
302 // ------------------------------------------------------------------------- //
303
TF_REGISTRY_FUNCTION(TfEnum)304 TF_REGISTRY_FUNCTION(TfEnum)
305 {
306 TF_ADD_ENUM_NAME(UsdStage::LoadAll, "Load all loadable prims");
307 TF_ADD_ENUM_NAME(UsdStage::LoadNone, "Load no loadable prims");
308 }
309
310 static ArResolverContext
_CreatePathResolverContext(const SdfLayerHandle & layer)311 _CreatePathResolverContext(
312 const SdfLayerHandle& layer)
313 {
314 if (layer && !layer->IsAnonymous()) {
315 // Ask for a default context for the layer based on the repository
316 // path, or if that's empty (i.e. the asset system is not
317 // initialized), use the file path.
318 // XXX: This should ultimately not be based on repository path.
319 return ArGetResolver().CreateDefaultContextForAsset(
320 layer->GetRepositoryPath().empty() ?
321 layer->GetRealPath() : layer->GetRepositoryPath());
322 }
323
324 return ArGetResolver().CreateDefaultContext();
325 }
326
327 static std::string
_AnchorAssetPathRelativeToLayer(const SdfLayerHandle & anchor,const std::string & assetPath)328 _AnchorAssetPathRelativeToLayer(
329 const SdfLayerHandle& anchor,
330 const std::string& assetPath)
331 {
332 if (assetPath.empty() ||
333 SdfLayer::IsAnonymousLayerIdentifier(assetPath)) {
334 return assetPath;
335 }
336
337 return SdfComputeAssetPathRelativeToLayer(anchor, assetPath);
338 }
339
340 static std::string
_ResolveAssetPathRelativeToLayer(const SdfLayerHandle & anchor,const std::string & assetPath)341 _ResolveAssetPathRelativeToLayer(
342 const SdfLayerHandle& anchor,
343 const std::string& assetPath)
344 {
345 const std::string computedAssetPath =
346 _AnchorAssetPathRelativeToLayer(anchor, assetPath);
347 if (computedAssetPath.empty()) {
348 return computedAssetPath;
349 }
350
351 return ArGetResolver().Resolve(computedAssetPath);
352 }
353
354 // If anchorAssetPathsOnly is true, this function will only
355 // update the authored assetPaths by anchoring them to the
356 // anchor layer; it will not fill in the resolved path field.
357 static void
_MakeResolvedAssetPathsImpl(const SdfLayerRefPtr & anchor,const ArResolverContext & context,SdfAssetPath * assetPaths,size_t numAssetPaths,bool anchorAssetPathsOnly)358 _MakeResolvedAssetPathsImpl(const SdfLayerRefPtr &anchor,
359 const ArResolverContext &context,
360 SdfAssetPath *assetPaths,
361 size_t numAssetPaths,
362 bool anchorAssetPathsOnly)
363 {
364 ArResolverContextBinder binder(context);
365 for (size_t i = 0; i != numAssetPaths; ++i) {
366 if (anchorAssetPathsOnly) {
367 assetPaths[i] = SdfAssetPath(
368 _AnchorAssetPathRelativeToLayer(
369 anchor, assetPaths[i].GetAssetPath()));
370 }
371 else {
372 assetPaths[i] = SdfAssetPath(
373 assetPaths[i].GetAssetPath(),
374 _ResolveAssetPathRelativeToLayer(
375 anchor, assetPaths[i].GetAssetPath()));
376 }
377 }
378 }
379
380 void
_MakeResolvedAssetPaths(UsdTimeCode time,const UsdAttribute & attr,SdfAssetPath * assetPaths,size_t numAssetPaths,bool anchorAssetPathsOnly) const381 UsdStage::_MakeResolvedAssetPaths(UsdTimeCode time,
382 const UsdAttribute& attr,
383 SdfAssetPath *assetPaths,
384 size_t numAssetPaths,
385 bool anchorAssetPathsOnly) const
386 {
387 // Get the layer providing the strongest value and use that to anchor the
388 // resolve.
389 auto anchor = _GetLayerWithStrongestValue(time, attr);
390 if (anchor) {
391 _MakeResolvedAssetPathsImpl(
392 anchor, GetPathResolverContext(), assetPaths, numAssetPaths,
393 anchorAssetPathsOnly);
394 }
395 }
396
397 void
_MakeResolvedAssetPathsValue(UsdTimeCode time,const UsdAttribute & attr,VtValue * value,bool anchorAssetPathsOnly) const398 UsdStage::_MakeResolvedAssetPathsValue(UsdTimeCode time,
399 const UsdAttribute& attr,
400 VtValue* value,
401 bool anchorAssetPathsOnly) const
402 {
403 if (value->IsHolding<SdfAssetPath>()) {
404 SdfAssetPath assetPath;
405 value->UncheckedSwap(assetPath);
406 _MakeResolvedAssetPaths(
407 time, attr, &assetPath, 1, anchorAssetPathsOnly);
408 value->UncheckedSwap(assetPath);
409
410 }
411 else if (value->IsHolding<VtArray<SdfAssetPath>>()) {
412 VtArray<SdfAssetPath> assetPaths;
413 value->UncheckedSwap(assetPaths);
414 _MakeResolvedAssetPaths(
415 time, attr, assetPaths.data(), assetPaths.size(),
416 anchorAssetPathsOnly);
417 value->UncheckedSwap(assetPaths);
418 }
419 }
420
421 void
_MakeResolvedTimeCodes(UsdTimeCode time,const UsdAttribute & attr,SdfTimeCode * timeCodes,size_t numTimeCodes) const422 UsdStage::_MakeResolvedTimeCodes(UsdTimeCode time, const UsdAttribute &attr,
423 SdfTimeCode *timeCodes,
424 size_t numTimeCodes) const
425 {
426 UsdResolveInfo info;
427 _GetResolveInfo(attr, &info, &time);
428 if (!info._layerToStageOffset.IsIdentity()) {
429 for (size_t i = 0; i != numTimeCodes; ++i) {
430 Usd_ApplyLayerOffsetToValue(&timeCodes[i], info._layerToStageOffset);
431 }
432 }
433 }
434
435 void
_MakeResolvedAttributeValue(UsdTimeCode time,const UsdAttribute & attr,VtValue * value) const436 UsdStage::_MakeResolvedAttributeValue(
437 UsdTimeCode time, const UsdAttribute &attr, VtValue *value) const
438 {
439 if (value->IsHolding<SdfTimeCode>()) {
440 SdfTimeCode timeCode;
441 value->UncheckedSwap(timeCode);
442 _MakeResolvedTimeCodes(time, attr, &timeCode, 1);
443 value->UncheckedSwap(timeCode);
444
445 }
446 else if (value->IsHolding<VtArray<SdfTimeCode>>()) {
447 VtArray<SdfTimeCode> timeCodes;
448 value->UncheckedSwap(timeCodes);
449 _MakeResolvedTimeCodes(
450 time, attr, timeCodes.data(), timeCodes.size());
451 value->UncheckedSwap(timeCodes);
452 } else {
453 _MakeResolvedAssetPathsValue(time, attr, value);
454 }
455 }
456
457 static SdfLayerRefPtr
_CreateAnonymousSessionLayer(const SdfLayerHandle & rootLayer)458 _CreateAnonymousSessionLayer(const SdfLayerHandle &rootLayer)
459 {
460 return SdfLayer::CreateAnonymous(
461 TfStringGetBeforeSuffix(
462 SdfLayer::GetDisplayNameFromIdentifier(rootLayer->GetIdentifier())) +
463 "-session.usda");
464 }
465
UsdStage(const SdfLayerRefPtr & rootLayer,const SdfLayerRefPtr & sessionLayer,const ArResolverContext & pathResolverContext,const UsdStagePopulationMask & mask,InitialLoadSet load)466 UsdStage::UsdStage(const SdfLayerRefPtr& rootLayer,
467 const SdfLayerRefPtr& sessionLayer,
468 const ArResolverContext& pathResolverContext,
469 const UsdStagePopulationMask& mask,
470 InitialLoadSet load)
471 : _pseudoRoot(nullptr)
472 , _rootLayer(rootLayer)
473 , _sessionLayer(sessionLayer)
474 , _editTarget(_rootLayer)
475 , _cache(new PcpCache(PcpLayerStackIdentifier(
476 _rootLayer, _sessionLayer, pathResolverContext),
477 UsdUsdFileFormatTokens->Target,
478 /*usdMode=*/true))
479 , _clipCache(new Usd_ClipCache)
480 , _instanceCache(new Usd_InstanceCache)
481 , _usedLayersRevision(0)
482 , _interpolationType(UsdInterpolationTypeLinear)
483 , _lastChangeSerialNumber(0)
484 , _pendingChanges(nullptr)
485 , _initialLoadSet(load)
486 , _populationMask(mask)
487 , _isClosingStage(false)
488 , _isWritingFallbackPrimTypes(false)
489 {
490 if (!TF_VERIFY(_rootLayer))
491 return;
492
493 TF_DEBUG(USD_STAGE_LIFETIMES).Msg(
494 "UsdStage::UsdStage(rootLayer=@%s@, sessionLayer=@%s@)\n",
495 _rootLayer->GetIdentifier().c_str(),
496 _sessionLayer ? _sessionLayer->GetIdentifier().c_str() : "<null>");
497
498 ARCH_PRAGMA_PUSH
499 ARCH_PRAGMA_DEPRECATED_POSIX_NAME
500 _mallocTagID = TfMallocTag::IsInitialized() ?
501 strdup(_StageTag(rootLayer->GetIdentifier()).c_str()) :
502 _dormantMallocTagID;
503 ARCH_PRAGMA_POP
504
505 _cache->SetVariantFallbacks(GetGlobalVariantFallbacks());
506 }
507
~UsdStage()508 UsdStage::~UsdStage()
509 {
510 TF_DEBUG(USD_STAGE_LIFETIMES).Msg(
511 "UsdStage::~UsdStage(rootLayer=@%s@, sessionLayer=@%s@)\n",
512 _rootLayer ? _rootLayer->GetIdentifier().c_str() : "<null>",
513 _sessionLayer ? _sessionLayer->GetIdentifier().c_str() : "<null>");
514 _Close();
515 if (_mallocTagID != _dormantMallocTagID){
516 free(const_cast<char*>(_mallocTagID));
517 }
518 }
519
520 void
_Close()521 UsdStage::_Close()
522 {
523 TfScopedVar<bool> resetIsClosing(_isClosingStage, true);
524
525 TF_PY_ALLOW_THREADS_IN_SCOPE();
526
527 WorkWithScopedParallelism([this]() {
528
529 // Destroy prim structure.
530 vector<SdfPath> primsToDestroy;
531 {
532 // Scope the dispatcher so that its dtor Wait()s for work to
533 // complete before primsToDestroy is destroyed, since tasks we
534 // schedule in the dispatcher access it.
535 WorkDispatcher wd;
536
537 // Stop listening for notices.
538 wd.Run([this]() {
539 for (auto &p: _layersAndNoticeKeys)
540 TfNotice::Revoke(p.second);
541 TfNotice::Revoke(_resolverChangeKey);
542 });
543
544 if (_pseudoRoot) {
545 // Instancing prototypes are not children of the pseudo-root
546 // so we need to explicitly destroy those subtrees.
547 primsToDestroy = _instanceCache->GetAllPrototypes();
548 wd.Run([this, &primsToDestroy]() {
549 primsToDestroy.push_back(
550 SdfPath::AbsoluteRootPath());
551 _DestroyPrimsInParallel(primsToDestroy);
552 _pseudoRoot = nullptr;
553 WorkMoveDestroyAsync(primsToDestroy);
554 });
555 }
556
557 // Clear members.
558 wd.Run([this]() { _cache.reset(); });
559 wd.Run([this]() { _clipCache.reset(); });
560 wd.Run([this]() { _instanceCache.reset(); });
561 wd.Run([this]() { _sessionLayer.Reset(); });
562 wd.Run([this]() { _rootLayer.Reset(); });
563 _editTarget = UsdEditTarget();
564 }
565 });
566
567 WorkSwapDestroyAsync(_primMap);
568 // XXX: Do not do this async, since python might shut down concurrently with
569 // this vector's destruction, and if any of the layers within have been
570 // reflected to python, the identity management stuff can blow up (since it
571 // accesses python).
572 //WorkSwapDestroyAsync(_layersAndNoticeKeys);
573 }
574
575 namespace {
576
577 // A predicate we pass to PcpCache::ComputePrimIndexesInParallel() to avoid
578 // computing indexes for children of inactive prims or instance prims.
579 // We don't populate such prims in Usd.
580 struct _NameChildrenPred
581 {
_NameChildrenPred__anon7d3fbd5f0911::_NameChildrenPred582 explicit _NameChildrenPred(const UsdStagePopulationMask *mask,
583 const UsdStageLoadRules *loadRules,
584 Usd_InstanceCache* instanceCache)
585 : _mask(mask)
586 , _loadRules(loadRules)
587 , _instanceCache(instanceCache)
588 { }
589
operator ()__anon7d3fbd5f0911::_NameChildrenPred590 bool operator()(const PcpPrimIndex &index,
591 TfTokenVector* childNamesToCompose) const
592 {
593 // Use a resolver to walk the index and find the strongest active
594 // opinion.
595 Usd_Resolver res(&index);
596 for (; res.IsValid(); res.NextLayer()) {
597 bool active = true;
598 if (res.GetLayer()->HasField(
599 res.GetLocalPath(), SdfFieldKeys->Active, &active)) {
600 if (!active) {
601 return false;
602 }
603 break;
604 }
605 }
606
607 // UsdStage doesn't expose any prims beneath instances, so we don't need
608 // to compute indexes for children of instances unless the index will be
609 // used as a source for a prototype prim.
610 if (index.IsInstanceable()) {
611 return _instanceCache->RegisterInstancePrimIndex(
612 index, _mask, *_loadRules);
613 }
614
615 // Compose only the child prims that are included in the population
616 // mask, if any. Masks are included in instancing keys, so this works
617 // correctly with instancing.
618 return !_mask ||
619 _mask->GetIncludedChildNames(index.GetPath(), childNamesToCompose);
620 }
621
622 private:
623 const UsdStagePopulationMask *_mask;
624 const UsdStageLoadRules *_loadRules;
625 Usd_InstanceCache *_instanceCache;
626 };
627
628 } // anon
629
630 /* static */
631 UsdStageRefPtr
_InstantiateStage(const SdfLayerRefPtr & rootLayer,const SdfLayerRefPtr & sessionLayer,const ArResolverContext & pathResolverContext,const UsdStagePopulationMask & mask,InitialLoadSet load)632 UsdStage::_InstantiateStage(const SdfLayerRefPtr &rootLayer,
633 const SdfLayerRefPtr &sessionLayer,
634 const ArResolverContext &pathResolverContext,
635 const UsdStagePopulationMask &mask,
636 InitialLoadSet load)
637 {
638 TF_DEBUG(USD_STAGE_OPEN)
639 .Msg("UsdStage::_InstantiateStage: Creating new UsdStage\n");
640
641 // We don't want to pay for the tag-string construction unless
642 // we instrumentation is on, since some Stage ctors (InMemory) can be
643 // very lightweight.
644 boost::optional<TfAutoMallocTag2> tag;
645
646 if (TfMallocTag::IsInitialized()){
647 tag = boost::in_place("Usd", _StageTag(rootLayer->GetIdentifier()));
648 }
649
650 // Debug timing info
651 TfStopwatch stopwatch;
652 const bool usdInstantiationTimeDebugCodeActive =
653 TfDebug::IsEnabled(USD_STAGE_INSTANTIATION_TIME);
654
655 if (usdInstantiationTimeDebugCodeActive) {
656 stopwatch.Start();
657 }
658
659 if (!rootLayer)
660 return TfNullPtr;
661
662 UsdStageRefPtr stage = TfCreateRefPtr(
663 new UsdStage(rootLayer, sessionLayer, pathResolverContext, mask, load));
664
665 ArResolverScopedCache resolverCache;
666
667 // Set the stage's load rules.
668 stage->_loadRules = (load == LoadAll) ?
669 UsdStageLoadRules::LoadAll() : UsdStageLoadRules::LoadNone();
670
671 Usd_InstanceChanges instanceChanges;
672 const SdfPath& absoluteRootPath = SdfPath::AbsoluteRootPath();
673
674 // Populate the stage, request payloads according to InitialLoadSet load.
675 stage->_ComposePrimIndexesInParallel(
676 {absoluteRootPath}, "instantiating stage", &instanceChanges);
677 stage->_pseudoRoot = stage->_InstantiatePrim(absoluteRootPath);
678
679 const size_t subtreeCount = instanceChanges.newPrototypePrims.size() + 1;
680 std::vector<Usd_PrimDataPtr> subtreesToCompose;
681 SdfPathVector primIndexPathsForSubtrees;
682 subtreesToCompose.reserve(subtreeCount);
683 primIndexPathsForSubtrees.reserve(subtreeCount);
684 subtreesToCompose.push_back(stage->_pseudoRoot);
685 primIndexPathsForSubtrees.push_back(absoluteRootPath);
686
687 // We only need to add new prototypes since, during stage initialization
688 // there should not be any changed prototypes
689 for (size_t i = 0; i != instanceChanges.newPrototypePrims.size(); ++i) {
690 const SdfPath& protoPath = instanceChanges.newPrototypePrims[i];
691 const SdfPath& protoPrimIndexPath =
692 instanceChanges.newPrototypePrimIndexes[i];
693
694 Usd_PrimDataPtr protoPrim = stage->_InstantiatePrototypePrim(protoPath);
695 subtreesToCompose.push_back(protoPrim);
696 primIndexPathsForSubtrees.push_back(protoPrimIndexPath);
697 }
698
699 stage->_ComposeSubtreesInParallel(
700 subtreesToCompose, &primIndexPathsForSubtrees);
701
702 stage->_RegisterPerLayerNotices();
703 stage->_RegisterResolverChangeNotice();
704
705 // Publish this stage into all current writable caches.
706 for (const auto cache : UsdStageCacheContext::_GetWritableCaches()) {
707 cache->Insert(stage);
708 }
709
710 // Debug timing info
711 if (usdInstantiationTimeDebugCodeActive) {
712 stopwatch.Stop();
713 TF_DEBUG(USD_STAGE_INSTANTIATION_TIME)
714 .Msg("UsdStage::_InstantiateStage: Time elapsed (s): %f\n",
715 stopwatch.GetSeconds());
716 }
717
718 return stage;
719 }
720
721 // Attempt to create a new layer with \p identifier. Issue an error in case of
722 // failure.
723 static SdfLayerRefPtr
_CreateNewLayer(const std::string & identifier)724 _CreateNewLayer(const std::string &identifier)
725 {
726 TfErrorMark mark;
727 SdfLayerRefPtr rootLayer = SdfLayer::CreateNew(identifier);
728 if (!rootLayer) {
729 // If Sdf did not report an error message, we must.
730 if (mark.IsClean()) {
731 TF_RUNTIME_ERROR("Failed to CreateNew layer with identifier '%s'",
732 identifier.c_str());
733 }
734 }
735 return rootLayer;
736 }
737
738 /* static */
739 UsdStageRefPtr
CreateNew(const std::string & identifier,InitialLoadSet load)740 UsdStage::CreateNew(const std::string& identifier,
741 InitialLoadSet load)
742 {
743 TfAutoMallocTag2 tag("Usd", _StageTag(identifier));
744
745 if (SdfLayerRefPtr layer = _CreateNewLayer(identifier))
746 return Open(layer, _CreateAnonymousSessionLayer(layer), load);
747 return TfNullPtr;
748 }
749
750 /* static */
751 UsdStageRefPtr
CreateNew(const std::string & identifier,const SdfLayerHandle & sessionLayer,InitialLoadSet load)752 UsdStage::CreateNew(const std::string& identifier,
753 const SdfLayerHandle& sessionLayer,
754 InitialLoadSet load)
755 {
756 TfAutoMallocTag2 tag("Usd", _StageTag(identifier));
757
758 if (SdfLayerRefPtr layer = _CreateNewLayer(identifier))
759 return Open(layer, sessionLayer, load);
760 return TfNullPtr;
761 }
762
763 /* static */
764 UsdStageRefPtr
CreateNew(const std::string & identifier,const ArResolverContext & pathResolverContext,InitialLoadSet load)765 UsdStage::CreateNew(const std::string& identifier,
766 const ArResolverContext& pathResolverContext,
767 InitialLoadSet load)
768 {
769 TfAutoMallocTag2 tag("Usd", _StageTag(identifier));
770
771 if (SdfLayerRefPtr layer = _CreateNewLayer(identifier))
772 return Open(layer, pathResolverContext, load);
773 return TfNullPtr;
774 }
775
776 /* static */
777 UsdStageRefPtr
CreateNew(const std::string & identifier,const SdfLayerHandle & sessionLayer,const ArResolverContext & pathResolverContext,InitialLoadSet load)778 UsdStage::CreateNew(const std::string& identifier,
779 const SdfLayerHandle& sessionLayer,
780 const ArResolverContext& pathResolverContext,
781 InitialLoadSet load)
782 {
783 TfAutoMallocTag2 tag("Usd", _StageTag(identifier));
784
785 if (SdfLayerRefPtr layer = _CreateNewLayer(identifier))
786 return Open(layer, sessionLayer, pathResolverContext, load);
787 return TfNullPtr;
788 }
789
790 /* static */
791 UsdStageRefPtr
CreateInMemory(InitialLoadSet load)792 UsdStage::CreateInMemory(InitialLoadSet load)
793 {
794 // Use usda file format if an identifier was not provided.
795 //
796 // In regards to "tmp.usda" below, SdfLayer::CreateAnonymous always
797 // prefixes the identifier with the layer's address in memory, so using the
798 // same identifier multiple times still produces unique layers.
799 return CreateInMemory("tmp.usda", load);
800 }
801
802 /* static */
803 UsdStageRefPtr
CreateInMemory(const std::string & identifier,InitialLoadSet load)804 UsdStage::CreateInMemory(const std::string& identifier,
805 InitialLoadSet load)
806 {
807 return Open(SdfLayer::CreateAnonymous(identifier), load);
808 }
809
810 /* static */
811 UsdStageRefPtr
CreateInMemory(const std::string & identifier,const ArResolverContext & pathResolverContext,InitialLoadSet load)812 UsdStage::CreateInMemory(const std::string& identifier,
813 const ArResolverContext& pathResolverContext,
814 InitialLoadSet load)
815 {
816 // CreateAnonymous() will transform 'identifier', so don't bother
817 // using it as a tag
818 TfAutoMallocTag tag("Usd");
819
820 return Open(SdfLayer::CreateAnonymous(identifier),
821 pathResolverContext, load);
822 }
823
824 /* static */
825 UsdStageRefPtr
CreateInMemory(const std::string & identifier,const SdfLayerHandle & sessionLayer,InitialLoadSet load)826 UsdStage::CreateInMemory(const std::string& identifier,
827 const SdfLayerHandle &sessionLayer,
828 InitialLoadSet load)
829 {
830 // CreateAnonymous() will transform 'identifier', so don't bother
831 // using it as a tag
832 TfAutoMallocTag tag("Usd");
833
834 return Open(SdfLayer::CreateAnonymous(identifier),
835 sessionLayer, load);
836 }
837
838 /* static */
839 UsdStageRefPtr
CreateInMemory(const std::string & identifier,const SdfLayerHandle & sessionLayer,const ArResolverContext & pathResolverContext,InitialLoadSet load)840 UsdStage::CreateInMemory(const std::string& identifier,
841 const SdfLayerHandle &sessionLayer,
842 const ArResolverContext& pathResolverContext,
843 InitialLoadSet load)
844 {
845 // CreateAnonymous() will transform 'identifier', so don't bother
846 // using it as a tag
847 TfAutoMallocTag tag("Usd");
848
849 return Open(SdfLayer::CreateAnonymous(identifier),
850 sessionLayer, pathResolverContext, load);
851 }
852
853 static
854 SdfLayerRefPtr
_OpenLayer(const std::string & filePath,const ArResolverContext & resolverContext=ArResolverContext ())855 _OpenLayer(
856 const std::string &filePath,
857 const ArResolverContext &resolverContext = ArResolverContext())
858 {
859 boost::optional<ArResolverContextBinder> binder;
860 if (!resolverContext.IsEmpty())
861 binder = boost::in_place(resolverContext);
862
863 SdfLayer::FileFormatArguments args;
864 args[SdfFileFormatTokens->TargetArg] =
865 UsdUsdFileFormatTokens->Target.GetString();
866
867 return SdfLayer::FindOrOpen(filePath, args);
868 }
869
870 /* static */
871 UsdStageRefPtr
Open(const std::string & filePath,InitialLoadSet load)872 UsdStage::Open(const std::string& filePath, InitialLoadSet load)
873 {
874 TfAutoMallocTag2 tag("Usd", _StageTag(filePath));
875
876 SdfLayerRefPtr rootLayer = _OpenLayer(filePath);
877 if (!rootLayer) {
878 TF_RUNTIME_ERROR("Failed to open layer @%s@", filePath.c_str());
879 return TfNullPtr;
880 }
881 return Open(rootLayer, load);
882 }
883
884 /* static */
885 UsdStageRefPtr
Open(const std::string & filePath,const ArResolverContext & pathResolverContext,InitialLoadSet load)886 UsdStage::Open(const std::string& filePath,
887 const ArResolverContext& pathResolverContext,
888 InitialLoadSet load)
889 {
890 TfAutoMallocTag2 tag("Usd", _StageTag(filePath));
891
892 SdfLayerRefPtr rootLayer = _OpenLayer(filePath, pathResolverContext);
893 if (!rootLayer) {
894 TF_RUNTIME_ERROR("Failed to open layer @%s@", filePath.c_str());
895 return TfNullPtr;
896 }
897 return Open(rootLayer, pathResolverContext, load);
898 }
899
900 /* static */
901 UsdStageRefPtr
OpenMasked(const std::string & filePath,const UsdStagePopulationMask & mask,InitialLoadSet load)902 UsdStage::OpenMasked(const std::string& filePath,
903 const UsdStagePopulationMask &mask,
904 InitialLoadSet load)
905 {
906 TfAutoMallocTag2 tag("Usd", _StageTag(filePath));
907
908 SdfLayerRefPtr rootLayer = _OpenLayer(filePath);
909 if (!rootLayer) {
910 TF_RUNTIME_ERROR("Failed to open layer @%s@", filePath.c_str());
911 return TfNullPtr;
912 }
913 return OpenMasked(rootLayer, mask, load);
914 }
915
916 /* static */
917 UsdStageRefPtr
OpenMasked(const std::string & filePath,const ArResolverContext & pathResolverContext,const UsdStagePopulationMask & mask,InitialLoadSet load)918 UsdStage::OpenMasked(const std::string& filePath,
919 const ArResolverContext& pathResolverContext,
920 const UsdStagePopulationMask &mask,
921 InitialLoadSet load)
922 {
923 TfAutoMallocTag2 tag("Usd", _StageTag(filePath));
924
925 SdfLayerRefPtr rootLayer = _OpenLayer(filePath, pathResolverContext);
926 if (!rootLayer) {
927 TF_RUNTIME_ERROR("Failed to open layer @%s@", filePath.c_str());
928 return TfNullPtr;
929 }
930 return OpenMasked(rootLayer, pathResolverContext, mask, load);
931 }
932
933 class Usd_StageOpenRequest : public UsdStageCacheRequest
934 {
935 public:
Usd_StageOpenRequest(UsdStage::InitialLoadSet load,SdfLayerHandle const & rootLayer)936 Usd_StageOpenRequest(UsdStage::InitialLoadSet load,
937 SdfLayerHandle const &rootLayer)
938 : _rootLayer(rootLayer)
939 , _initialLoadSet(load) {}
Usd_StageOpenRequest(UsdStage::InitialLoadSet load,SdfLayerHandle const & rootLayer,SdfLayerHandle const & sessionLayer)940 Usd_StageOpenRequest(UsdStage::InitialLoadSet load,
941 SdfLayerHandle const &rootLayer,
942 SdfLayerHandle const &sessionLayer)
943 : _rootLayer(rootLayer)
944 , _sessionLayer(sessionLayer)
945 , _initialLoadSet(load) {}
Usd_StageOpenRequest(UsdStage::InitialLoadSet load,SdfLayerHandle const & rootLayer,ArResolverContext const & pathResolverContext)946 Usd_StageOpenRequest(UsdStage::InitialLoadSet load,
947 SdfLayerHandle const &rootLayer,
948 ArResolverContext const &pathResolverContext)
949 : _rootLayer(rootLayer)
950 , _pathResolverContext(pathResolverContext)
951 , _initialLoadSet(load) {}
Usd_StageOpenRequest(UsdStage::InitialLoadSet load,SdfLayerHandle const & rootLayer,SdfLayerHandle const & sessionLayer,ArResolverContext const & pathResolverContext)952 Usd_StageOpenRequest(UsdStage::InitialLoadSet load,
953 SdfLayerHandle const &rootLayer,
954 SdfLayerHandle const &sessionLayer,
955 ArResolverContext const &pathResolverContext)
956 : _rootLayer(rootLayer)
957 , _sessionLayer(sessionLayer)
958 , _pathResolverContext(pathResolverContext)
959 , _initialLoadSet(load) {}
960
~Usd_StageOpenRequest()961 virtual ~Usd_StageOpenRequest() {}
IsSatisfiedBy(UsdStageRefPtr const & stage) const962 virtual bool IsSatisfiedBy(UsdStageRefPtr const &stage) const {
963 // Works if other stage's root layer matches and we either don't care
964 // about the session layer or it matches, and we either don't care about
965 // the path resolverContext or it matches.
966 return _rootLayer == stage->GetRootLayer() &&
967 (!_sessionLayer || (*_sessionLayer == stage->GetSessionLayer())) &&
968 (!_pathResolverContext || (*_pathResolverContext ==
969 stage->GetPathResolverContext()));
970 }
IsSatisfiedBy(UsdStageCacheRequest const & other) const971 virtual bool IsSatisfiedBy(UsdStageCacheRequest const &other) const {
972 auto req = dynamic_cast<Usd_StageOpenRequest const *>(&other);
973 if (!req)
974 return false;
975
976 // Works if other's root layer matches and we either don't care about
977 // the session layer or it matches, and we either don't care about the
978 // path resolverContext or it matches.
979 return _rootLayer == req->_rootLayer &&
980 (!_sessionLayer || (_sessionLayer == req->_sessionLayer)) &&
981 (!_pathResolverContext || (_pathResolverContext ==
982 req->_pathResolverContext));
983 }
Manufacture()984 virtual UsdStageRefPtr Manufacture() {
985 return UsdStage::_InstantiateStage(
986 SdfLayerRefPtr(_rootLayer),
987 _sessionLayer ? SdfLayerRefPtr(*_sessionLayer) :
988 _CreateAnonymousSessionLayer(_rootLayer),
989 _pathResolverContext ? *_pathResolverContext :
990 _CreatePathResolverContext(_rootLayer),
991 UsdStagePopulationMask::All(),
992 _initialLoadSet);
993 }
994
995 private:
996 SdfLayerHandle _rootLayer;
997 boost::optional<SdfLayerHandle> _sessionLayer;
998 boost::optional<ArResolverContext> _pathResolverContext;
999 UsdStage::InitialLoadSet _initialLoadSet;
1000 };
1001
1002 /* static */
1003 template <class... Args>
1004 UsdStageRefPtr
_OpenImpl(InitialLoadSet load,Args const &...args)1005 UsdStage::_OpenImpl(InitialLoadSet load, Args const &... args)
1006 {
1007 // Try to find a matching stage in read-only caches.
1008 for (const UsdStageCache *cache:
1009 UsdStageCacheContext::_GetReadableCaches()) {
1010 if (UsdStageRefPtr stage = cache->FindOneMatching(args...))
1011 return stage;
1012 }
1013
1014 // If none found, request the stage in all the writable caches. If we
1015 // manufacture a stage, we'll publish it to all the writable caches, so
1016 // subsequent requests will get the same stage out.
1017 UsdStageRefPtr stage;
1018 auto writableCaches = UsdStageCacheContext::_GetWritableCaches();
1019 if (writableCaches.empty()) {
1020 stage = Usd_StageOpenRequest(load, args...).Manufacture();
1021 }
1022 else {
1023 for (UsdStageCache *cache: writableCaches) {
1024 auto r = cache->RequestStage(Usd_StageOpenRequest(load, args...));
1025 if (!stage)
1026 stage = r.first;
1027 if (r.second) {
1028 // We manufactured the stage -- we published it to all the other
1029 // caches too, so nothing left to do.
1030 break;
1031 }
1032 }
1033 }
1034 TF_VERIFY(stage);
1035 return stage;
1036 }
1037
1038 /* static */
1039 UsdStageRefPtr
Open(const SdfLayerHandle & rootLayer,InitialLoadSet load)1040 UsdStage::Open(const SdfLayerHandle& rootLayer, InitialLoadSet load)
1041 {
1042 if (!rootLayer) {
1043 TF_CODING_ERROR("Invalid root layer");
1044 return TfNullPtr;
1045 }
1046
1047 TF_DEBUG(USD_STAGE_OPEN)
1048 .Msg("UsdStage::Open(rootLayer=@%s@, load=%s)\n",
1049 rootLayer->GetIdentifier().c_str(),
1050 TfStringify(load).c_str());
1051
1052 return _OpenImpl(load, rootLayer);
1053 }
1054
1055 /* static */
1056 UsdStageRefPtr
Open(const SdfLayerHandle & rootLayer,const SdfLayerHandle & sessionLayer,InitialLoadSet load)1057 UsdStage::Open(const SdfLayerHandle& rootLayer,
1058 const SdfLayerHandle& sessionLayer,
1059 InitialLoadSet load)
1060 {
1061 if (!rootLayer) {
1062 TF_CODING_ERROR("Invalid root layer");
1063 return TfNullPtr;
1064 }
1065
1066 TF_DEBUG(USD_STAGE_OPEN)
1067 .Msg("UsdStage::Open(rootLayer=@%s@, sessionLayer=@%s@, load=%s)\n",
1068 rootLayer->GetIdentifier().c_str(),
1069 sessionLayer ? sessionLayer->GetIdentifier().c_str() : "<null>",
1070 TfStringify(load).c_str());
1071
1072 return _OpenImpl(load, rootLayer, sessionLayer);
1073 }
1074
1075 /* static */
1076 UsdStageRefPtr
Open(const SdfLayerHandle & rootLayer,const ArResolverContext & pathResolverContext,InitialLoadSet load)1077 UsdStage::Open(const SdfLayerHandle& rootLayer,
1078 const ArResolverContext& pathResolverContext,
1079 InitialLoadSet load)
1080 {
1081 if (!rootLayer) {
1082 TF_CODING_ERROR("Invalid root layer");
1083 return TfNullPtr;
1084 }
1085
1086 TF_DEBUG(USD_STAGE_OPEN)
1087 .Msg("UsdStage::Open(rootLayer=@%s@, pathResolverContext=%s, "
1088 "load=%s)\n",
1089 rootLayer->GetIdentifier().c_str(),
1090 pathResolverContext.GetDebugString().c_str(),
1091 TfStringify(load).c_str());
1092
1093 return _OpenImpl(load, rootLayer, pathResolverContext);
1094 }
1095
1096 /* static */
1097 UsdStageRefPtr
Open(const SdfLayerHandle & rootLayer,const SdfLayerHandle & sessionLayer,const ArResolverContext & pathResolverContext,InitialLoadSet load)1098 UsdStage::Open(const SdfLayerHandle& rootLayer,
1099 const SdfLayerHandle& sessionLayer,
1100 const ArResolverContext& pathResolverContext,
1101 InitialLoadSet load)
1102 {
1103 if (!rootLayer) {
1104 TF_CODING_ERROR("Invalid root layer");
1105 return TfNullPtr;
1106 }
1107
1108 TF_DEBUG(USD_STAGE_OPEN)
1109 .Msg("UsdStage::Open(rootLayer=@%s@, sessionLayer=@%s@, "
1110 "pathResolverContext=%s, load=%s)\n",
1111 rootLayer->GetIdentifier().c_str(),
1112 sessionLayer ? sessionLayer->GetIdentifier().c_str() : "<null>",
1113 pathResolverContext.GetDebugString().c_str(),
1114 TfStringify(load).c_str());
1115
1116 return _OpenImpl(load, rootLayer, sessionLayer, pathResolverContext);
1117 }
1118
1119 ////////////////////////////////////////////////////////////////////////
1120 // masked opens.
1121
1122 /* static */
1123 UsdStageRefPtr
OpenMasked(const SdfLayerHandle & rootLayer,const UsdStagePopulationMask & mask,InitialLoadSet load)1124 UsdStage::OpenMasked(const SdfLayerHandle& rootLayer,
1125 const UsdStagePopulationMask &mask,
1126 InitialLoadSet load)
1127 {
1128 if (!rootLayer) {
1129 TF_CODING_ERROR("Invalid root layer");
1130 return TfNullPtr;
1131 }
1132
1133 TF_DEBUG(USD_STAGE_OPEN)
1134 .Msg("UsdStage::OpenMasked(rootLayer=@%s@, mask=%s, load=%s)\n",
1135 rootLayer->GetIdentifier().c_str(),
1136 TfStringify(mask).c_str(),
1137 TfStringify(load).c_str());
1138
1139 return _InstantiateStage(SdfLayerRefPtr(rootLayer),
1140 _CreateAnonymousSessionLayer(rootLayer),
1141 _CreatePathResolverContext(rootLayer),
1142 mask,
1143 load);
1144 }
1145
1146 /* static */
1147 UsdStageRefPtr
OpenMasked(const SdfLayerHandle & rootLayer,const SdfLayerHandle & sessionLayer,const UsdStagePopulationMask & mask,InitialLoadSet load)1148 UsdStage::OpenMasked(const SdfLayerHandle& rootLayer,
1149 const SdfLayerHandle& sessionLayer,
1150 const UsdStagePopulationMask &mask,
1151 InitialLoadSet load)
1152 {
1153 if (!rootLayer) {
1154 TF_CODING_ERROR("Invalid root layer");
1155 return TfNullPtr;
1156 }
1157
1158 TF_DEBUG(USD_STAGE_OPEN)
1159 .Msg("UsdStage::OpenMasked(rootLayer=@%s@, sessionLayer=@%s@, "
1160 "mask=%s, load=%s)\n",
1161 rootLayer->GetIdentifier().c_str(),
1162 sessionLayer ? sessionLayer->GetIdentifier().c_str() : "<null>",
1163 TfStringify(mask).c_str(),
1164 TfStringify(load).c_str());
1165
1166 return _InstantiateStage(SdfLayerRefPtr(rootLayer),
1167 SdfLayerRefPtr(sessionLayer),
1168 _CreatePathResolverContext(rootLayer),
1169 mask,
1170 load);
1171 }
1172
1173 /* static */
1174 UsdStageRefPtr
OpenMasked(const SdfLayerHandle & rootLayer,const ArResolverContext & pathResolverContext,const UsdStagePopulationMask & mask,InitialLoadSet load)1175 UsdStage::OpenMasked(const SdfLayerHandle& rootLayer,
1176 const ArResolverContext& pathResolverContext,
1177 const UsdStagePopulationMask &mask,
1178 InitialLoadSet load)
1179 {
1180 if (!rootLayer) {
1181 TF_CODING_ERROR("Invalid root layer");
1182 return TfNullPtr;
1183 }
1184
1185 TF_DEBUG(USD_STAGE_OPEN)
1186 .Msg("UsdStage::OpenMasked(rootLayer=@%s@, pathResolverContext=%s, "
1187 "mask=%s, load=%s)\n",
1188 rootLayer->GetIdentifier().c_str(),
1189 pathResolverContext.GetDebugString().c_str(),
1190 TfStringify(mask).c_str(),
1191 TfStringify(load).c_str());
1192
1193 return _InstantiateStage(SdfLayerRefPtr(rootLayer),
1194 _CreateAnonymousSessionLayer(rootLayer),
1195 pathResolverContext,
1196 mask,
1197 load);
1198 }
1199
1200 /* static */
1201 UsdStageRefPtr
OpenMasked(const SdfLayerHandle & rootLayer,const SdfLayerHandle & sessionLayer,const ArResolverContext & pathResolverContext,const UsdStagePopulationMask & mask,InitialLoadSet load)1202 UsdStage::OpenMasked(const SdfLayerHandle& rootLayer,
1203 const SdfLayerHandle& sessionLayer,
1204 const ArResolverContext& pathResolverContext,
1205 const UsdStagePopulationMask &mask,
1206 InitialLoadSet load)
1207 {
1208 if (!rootLayer) {
1209 TF_CODING_ERROR("Invalid root layer");
1210 return TfNullPtr;
1211 }
1212
1213 TF_DEBUG(USD_STAGE_OPEN)
1214 .Msg("UsdStage::OpenMasked(rootLayer=@%s@, sessionLayer=@%s@, "
1215 "pathResolverContext=%s, mask=%s, load=%s)\n",
1216 rootLayer->GetIdentifier().c_str(),
1217 sessionLayer ? sessionLayer->GetIdentifier().c_str() : "<null>",
1218 pathResolverContext.GetDebugString().c_str(),
1219 TfStringify(mask).c_str(),
1220 TfStringify(load).c_str());
1221
1222 return _InstantiateStage(SdfLayerRefPtr(rootLayer),
1223 SdfLayerRefPtr(sessionLayer),
1224 pathResolverContext,
1225 mask,
1226 load);
1227 }
1228
1229 static inline SdfAttributeSpecHandle
_GetSchemaPropSpec(SdfAttributeSpec *,const UsdPrimDefinition & primDef,TfToken const & attrName)1230 _GetSchemaPropSpec(SdfAttributeSpec *,
1231 const UsdPrimDefinition &primDef,
1232 TfToken const &attrName)
1233 {
1234 return primDef.GetSchemaAttributeSpec(attrName);
1235 }
1236
1237 static inline SdfRelationshipSpecHandle
_GetSchemaPropSpec(SdfRelationshipSpec *,const UsdPrimDefinition & primDef,TfToken const & attrName)1238 _GetSchemaPropSpec(SdfRelationshipSpec *,
1239 const UsdPrimDefinition &primDef,
1240 TfToken const &attrName)
1241 {
1242 return primDef.GetSchemaRelationshipSpec(attrName);
1243 }
1244
1245 static inline SdfPropertySpecHandle
_GetSchemaPropSpec(SdfPropertySpec *,const UsdPrimDefinition & primDef,TfToken const & attrName)1246 _GetSchemaPropSpec(SdfPropertySpec *,
1247 const UsdPrimDefinition &primDef,
1248 TfToken const &attrName)
1249 {
1250 return primDef.GetSchemaPropertySpec(attrName);
1251 }
1252
1253 template <class PropType>
1254 SdfHandle<PropType>
_GetSchemaPropertySpec(const UsdProperty & prop) const1255 UsdStage::_GetSchemaPropertySpec(const UsdProperty &prop) const
1256 {
1257 Usd_PrimDataHandle const &primData = prop._Prim();
1258 if (!primData)
1259 return TfNullPtr;
1260
1261 // Consult the registry.
1262 return _GetSchemaPropSpec(static_cast<PropType *>(nullptr),
1263 primData->GetPrimDefinition(), prop.GetName());
1264 }
1265
1266 SdfPropertySpecHandle
_GetSchemaPropertySpec(const UsdProperty & prop) const1267 UsdStage::_GetSchemaPropertySpec(const UsdProperty &prop) const
1268 {
1269 return _GetSchemaPropertySpec<SdfPropertySpec>(prop);
1270 }
1271
1272 SdfAttributeSpecHandle
_GetSchemaAttributeSpec(const UsdAttribute & attr) const1273 UsdStage::_GetSchemaAttributeSpec(const UsdAttribute &attr) const
1274 {
1275 return _GetSchemaPropertySpec<SdfAttributeSpec>(attr);
1276 }
1277
1278 SdfRelationshipSpecHandle
_GetSchemaRelationshipSpec(const UsdRelationship & rel) const1279 UsdStage::_GetSchemaRelationshipSpec(const UsdRelationship &rel) const
1280 {
1281 return _GetSchemaPropertySpec<SdfRelationshipSpec>(rel);
1282 }
1283
1284 bool
_ValidateEditPrim(const UsdPrim & prim,const char * operation) const1285 UsdStage::_ValidateEditPrim(const UsdPrim &prim, const char* operation) const
1286 {
1287 if (ARCH_UNLIKELY(prim.IsInPrototype())) {
1288 TF_CODING_ERROR("Cannot %s at path <%s>; "
1289 "authoring to an instancing prototype is not allowed.",
1290 operation, prim.GetPath().GetText());
1291 return false;
1292 }
1293
1294 if (ARCH_UNLIKELY(prim.IsInstanceProxy())) {
1295 TF_CODING_ERROR("Cannot %s at path <%s>; "
1296 "authoring to an instance proxy is not allowed.",
1297 operation, prim.GetPath().GetText());
1298 return false;
1299 }
1300
1301 return true;
1302 }
1303
1304 bool
_ValidateEditPrimAtPath(const SdfPath & primPath,const char * operation) const1305 UsdStage::_ValidateEditPrimAtPath(const SdfPath &primPath,
1306 const char* operation) const
1307 {
1308 if (ARCH_UNLIKELY(Usd_InstanceCache::IsPathInPrototype(primPath))) {
1309 TF_CODING_ERROR("Cannot %s at path <%s>; "
1310 "authoring to an instancing prototype is not allowed.",
1311 operation, primPath.GetText());
1312 return false;
1313 }
1314
1315 if (ARCH_UNLIKELY(_IsObjectDescendantOfInstance(primPath))) {
1316 TF_CODING_ERROR("Cannot %s at path <%s>; "
1317 "authoring to an instance proxy is not allowed.",
1318 operation, primPath.GetText());
1319 return false;
1320 }
1321
1322 return true;
1323 }
1324
1325 namespace {
1326
1327 SdfPrimSpecHandle
_CreatePrimSpecAtEditTarget(const UsdEditTarget & editTarget,const SdfPath & path)1328 _CreatePrimSpecAtEditTarget(const UsdEditTarget &editTarget,
1329 const SdfPath &path)
1330 {
1331 const SdfPath &targetPath = editTarget.MapToSpecPath(path);
1332 return targetPath.IsEmpty() ? SdfPrimSpecHandle() :
1333 SdfCreatePrimInLayer(editTarget.GetLayer(), targetPath);
1334 }
1335
1336 }
1337
1338 SdfPrimSpecHandle
_CreatePrimSpecForEditing(const UsdPrim & prim)1339 UsdStage::_CreatePrimSpecForEditing(const UsdPrim& prim)
1340 {
1341 if (ARCH_UNLIKELY(!_ValidateEditPrim(prim, "create prim spec"))) {
1342 return TfNullPtr;
1343 }
1344
1345 return _CreatePrimSpecAtEditTarget(GetEditTarget(), prim.GetPath());
1346 }
1347
1348 static SdfAttributeSpecHandle
_StampNewPropertySpec(const SdfPrimSpecHandle & primSpec,const TfToken & propName,const SdfAttributeSpecHandle & toCopy)1349 _StampNewPropertySpec(const SdfPrimSpecHandle &primSpec,
1350 const TfToken &propName,
1351 const SdfAttributeSpecHandle &toCopy)
1352 {
1353 return SdfAttributeSpec::New(
1354 primSpec, propName, toCopy->GetTypeName(),
1355 toCopy->GetVariability(), toCopy->IsCustom());
1356 }
1357
1358 static SdfRelationshipSpecHandle
_StampNewPropertySpec(const SdfPrimSpecHandle & primSpec,const TfToken & propName,const SdfRelationshipSpecHandle & toCopy)1359 _StampNewPropertySpec(const SdfPrimSpecHandle &primSpec,
1360 const TfToken &propName,
1361 const SdfRelationshipSpecHandle &toCopy)
1362 {
1363 return SdfRelationshipSpec::New(
1364 primSpec, propName, toCopy->IsCustom(),
1365 toCopy->GetVariability());
1366 }
1367
1368 static SdfPropertySpecHandle
_StampNewPropertySpec(const SdfPrimSpecHandle & primSpec,const TfToken & propName,const SdfPropertySpecHandle & toCopy)1369 _StampNewPropertySpec(const SdfPrimSpecHandle &primSpec,
1370 const TfToken &propName,
1371 const SdfPropertySpecHandle &toCopy)
1372 {
1373 // Type dispatch to correct property type.
1374 if (SdfAttributeSpecHandle attrSpec =
1375 TfDynamic_cast<SdfAttributeSpecHandle>(toCopy)) {
1376 return _StampNewPropertySpec(primSpec, propName, attrSpec);
1377 } else {
1378 return _StampNewPropertySpec(
1379 primSpec, propName, TfStatic_cast<SdfRelationshipSpecHandle>(toCopy));
1380 }
1381 }
1382
1383 template <class PropType>
1384 SdfHandle<PropType>
_CreatePropertySpecForEditing(const UsdProperty & prop)1385 UsdStage::_CreatePropertySpecForEditing(const UsdProperty &prop)
1386 {
1387 UsdPrim prim = prop.GetPrim();
1388 if (ARCH_UNLIKELY(!_ValidateEditPrim(prim, "create property spec"))) {
1389 return TfNullPtr;
1390 }
1391
1392 typedef SdfHandle<PropType> TypedSpecHandle;
1393
1394 const UsdEditTarget &editTarget = GetEditTarget();
1395
1396 const SdfPath &propPath = prop.GetPath();
1397 const TfToken &propName = prop.GetName();
1398
1399 // Check to see if there already exists a property with this path at the
1400 // current EditTarget.
1401 if (SdfPropertySpecHandle propSpec =
1402 editTarget.GetPropertySpecForScenePath(propPath)) {
1403 // If it's of the correct type, we're done. Otherwise this is an error:
1404 // attribute/relationship type mismatch.
1405 if (TypedSpecHandle spec = TfDynamic_cast<TypedSpecHandle>(propSpec))
1406 return spec;
1407
1408 TF_RUNTIME_ERROR("Spec type mismatch. Failed to create %s for <%s> at "
1409 "<%s> in @%s@. %s already at that location.",
1410 ArchGetDemangled<PropType>().c_str(),
1411 propPath.GetText(),
1412 editTarget.MapToSpecPath(propPath).GetText(),
1413 editTarget.GetLayer()->GetIdentifier().c_str(),
1414 TfStringify(propSpec->GetSpecType()).c_str());
1415 return TfNullPtr;
1416 }
1417
1418 // There is no property spec at the current EditTarget. Look for a typed
1419 // spec whose metadata we can copy. First check to see if there is a
1420 // builtin we can use. Failing that, try to take the strongest authored
1421 // spec.
1422 TypedSpecHandle specToCopy;
1423
1424 // Get definition, if any.
1425 specToCopy = _GetSchemaPropertySpec<PropType>(prop);
1426
1427 if (!specToCopy) {
1428 // There is no definition available, either because the prim has no
1429 // known schema, or its schema has no definition for this property. In
1430 // this case, we look to see if there's a strongest property spec. If
1431 // so, we copy its required metadata.
1432 for (Usd_Resolver r(&prim.GetPrimIndex()); r.IsValid(); r.NextLayer()) {
1433 if (SdfPropertySpecHandle propSpec = r.GetLayer()->
1434 GetPropertyAtPath(r.GetLocalPath().AppendProperty(propName))) {
1435 if ((specToCopy = TfDynamic_cast<TypedSpecHandle>(propSpec)))
1436 break;
1437 // Type mismatch.
1438 TF_RUNTIME_ERROR("Spec type mismatch. Failed to create %s for "
1439 "<%s> at <%s> in @%s@. Strongest existing "
1440 "spec, %s at <%s> in @%s@",
1441 ArchGetDemangled<PropType>().c_str(),
1442 propPath.GetText(),
1443 editTarget.MapToSpecPath(propPath).GetText(),
1444 editTarget.GetLayer()->GetIdentifier().c_str(),
1445 TfStringify(propSpec->GetSpecType()).c_str(),
1446 propSpec->GetPath().GetText(),
1447 propSpec->GetLayer()->GetIdentifier().c_str());
1448 return TfNullPtr;
1449 }
1450 }
1451 }
1452
1453 // If we have a spec to copy from, then we author an opinion at the edit
1454 // target.
1455 if (specToCopy) {
1456 SdfChangeBlock block;
1457 SdfPrimSpecHandle primSpec = _CreatePrimSpecForEditing(prim);
1458 if (TF_VERIFY(primSpec))
1459 return _StampNewPropertySpec(primSpec, propName, specToCopy);
1460 }
1461
1462 // Otherwise, we fail to create a spec.
1463 return TfNullPtr;
1464 }
1465
1466 SdfAttributeSpecHandle
_CreateAttributeSpecForEditing(const UsdAttribute & attr)1467 UsdStage::_CreateAttributeSpecForEditing(const UsdAttribute &attr)
1468 {
1469 return _CreatePropertySpecForEditing<SdfAttributeSpec>(attr);
1470 }
1471
1472 SdfRelationshipSpecHandle
_CreateRelationshipSpecForEditing(const UsdRelationship & rel)1473 UsdStage::_CreateRelationshipSpecForEditing(const UsdRelationship &rel)
1474 {
1475 return _CreatePropertySpecForEditing<SdfRelationshipSpec>(rel);
1476 }
1477
1478 SdfPropertySpecHandle
_CreatePropertySpecForEditing(const UsdProperty & prop)1479 UsdStage::_CreatePropertySpecForEditing(const UsdProperty &prop)
1480 {
1481 return _CreatePropertySpecForEditing<SdfPropertySpec>(prop);
1482 }
1483
1484 bool
_SetMetadata(const UsdObject & object,const TfToken & key,const TfToken & keyPath,const VtValue & value)1485 UsdStage::_SetMetadata(const UsdObject &object,
1486 const TfToken &key,
1487 const TfToken &keyPath,
1488 const VtValue &value)
1489 {
1490 // The VtValue may be holding a type that needs to be mapped across edit
1491 // targets.
1492 if (value.IsHolding<SdfTimeCode>()) {
1493 return _SetMetadata(object, key, keyPath,
1494 value.UncheckedGet<SdfTimeCode>());
1495 } else if (value.IsHolding<VtArray<SdfTimeCode>>()) {
1496 return _SetMetadata(object, key, keyPath,
1497 value.UncheckedGet<VtArray<SdfTimeCode>>());
1498 } else if (value.IsHolding<VtDictionary>()) {
1499 return _SetMetadata(object, key, keyPath,
1500 value.UncheckedGet<VtDictionary>());
1501 } else if (value.IsHolding<SdfTimeSampleMap>()) {
1502 return _SetMetadata(object, key, keyPath,
1503 value.UncheckedGet<SdfTimeSampleMap>());
1504 }
1505
1506 return _SetMetadataImpl(object, key, keyPath, value);
1507 }
1508
1509 // This function handles the inverse mapping of values to an edit target's layer
1510 // for value types that get resolved by layer offsets. It's templated by a set
1511 // value implementation function in order to abstract out this value mapping for
1512 // both attribute values and metadata.
1513 // Fn type is equivalent to:
1514 // bool setValueImpl(const SdfAbstractDataConstValue &)
1515 template <typename T, typename Fn>
1516 static bool
_SetMappedValueForEditTarget(const T & newValue,const UsdEditTarget & editTarget,const Fn & setValueImpl)1517 _SetMappedValueForEditTarget(const T &newValue,
1518 const UsdEditTarget &editTarget,
1519 const Fn &setValueImpl)
1520 {
1521 const SdfLayerOffset &layerOffset =
1522 editTarget.GetMapFunction().GetTimeOffset();
1523 if (!layerOffset.IsIdentity()) {
1524 // Copy the value, apply the offset to the edit layer, and set it using
1525 // the provided set function.
1526 T targetValue = newValue;
1527 Usd_ApplyLayerOffsetToValue(&targetValue, layerOffset.GetInverse());
1528
1529 SdfAbstractDataConstTypedValue<T> in(&targetValue);
1530 return setValueImpl(in);
1531 }
1532
1533 SdfAbstractDataConstTypedValue<T> in(&newValue);
1534 return setValueImpl(in);
1535 }
1536
1537 template <class T>
_SetEditTargetMappedMetadata(const UsdObject & obj,const TfToken & fieldName,const TfToken & keyPath,const T & newValue)1538 bool UsdStage::_SetEditTargetMappedMetadata(
1539 const UsdObject &obj, const TfToken& fieldName,
1540 const TfToken &keyPath, const T &newValue)
1541 {
1542 static_assert(_IsEditTargetMappable<T>::value,
1543 "_SetEditTargetMappedMetadata can only be instantiated for "
1544 "types that are edit target mappable.");
1545 return _SetMappedValueForEditTarget(
1546 newValue, GetEditTarget(),
1547 [this, &obj, &fieldName, &keyPath](const SdfAbstractDataConstValue &in)
1548 {
1549 return this->_SetMetadataImpl(obj, fieldName, keyPath, in);
1550 });
1551 }
1552
1553 static const std::type_info &
_GetTypeInfo(const SdfAbstractDataConstValue & value)1554 _GetTypeInfo(const SdfAbstractDataConstValue &value)
1555 {
1556 return value.valueType;
1557 }
1558
1559 static const std::type_info &
_GetTypeInfo(const VtValue & value)1560 _GetTypeInfo(const VtValue &value)
1561 {
1562 return value.IsEmpty() ? typeid(void) : value.GetTypeid();
1563 }
1564
1565 template <class T>
1566 bool
_SetMetadataImpl(const UsdObject & obj,const TfToken & fieldName,const TfToken & keyPath,const T & newValue)1567 UsdStage::_SetMetadataImpl(const UsdObject &obj,
1568 const TfToken &fieldName,
1569 const TfToken &keyPath,
1570 const T &newValue)
1571 {
1572 if (!SdfSchema::GetInstance().IsRegistered(fieldName)) {
1573 TF_CODING_ERROR("Unregistered metadata field: %s", fieldName.GetText());
1574 return false;
1575 }
1576
1577 TfAutoMallocTag2 tag("Usd", _mallocTagID);
1578
1579 SdfSpecHandle spec;
1580
1581 if (obj.Is<UsdProperty>()) {
1582 spec = _CreatePropertySpecForEditing(obj.As<UsdProperty>());
1583 } else if (obj.Is<UsdPrim>()) {
1584 spec = _CreatePrimSpecForEditing(obj.As<UsdPrim>());
1585 } else {
1586 TF_CODING_ERROR("Cannot set metadata at path <%s> in layer @%s@; "
1587 "a prim or property is required",
1588 GetEditTarget().MapToSpecPath(obj.GetPath()).GetText(),
1589 GetEditTarget().GetLayer()->GetIdentifier().c_str());
1590 return false;
1591 }
1592
1593 if (!spec) {
1594 TF_CODING_ERROR("Cannot set metadata. Failed to create spec <%s> in "
1595 "layer @%s@",
1596 GetEditTarget().MapToSpecPath(obj.GetPath()).GetText(),
1597 GetEditTarget().GetLayer()->GetIdentifier().c_str());
1598 return false;
1599 }
1600
1601 const auto& schema = spec->GetSchema();
1602 const auto specType = spec->GetSpecType();
1603 if (!schema.IsValidFieldForSpec(fieldName, specType)) {
1604 TF_CODING_ERROR("Cannot set metadata. '%s' is not registered "
1605 "as valid metadata for spec type %s.",
1606 fieldName.GetText(),
1607 TfStringify(specType).c_str());
1608 return false;
1609 }
1610
1611 if (keyPath.IsEmpty()) {
1612 spec->GetLayer()->SetField(spec->GetPath(), fieldName, newValue);
1613 } else {
1614 spec->GetLayer()->SetFieldDictValueByKey(
1615 spec->GetPath(), fieldName, keyPath, newValue);
1616 }
1617 return true;
1618 }
1619
1620 template <class T>
1621 bool
_SetEditTargetMappedValue(UsdTimeCode time,const UsdAttribute & attr,const T & newValue)1622 UsdStage::_SetEditTargetMappedValue(
1623 UsdTimeCode time, const UsdAttribute &attr, const T &newValue)
1624 {
1625 static_assert(_IsEditTargetMappable<T>::value,
1626 "_SetEditTargetMappedValue can only be instantiated for "
1627 "types that are edit target mappable.");
1628 return _SetMappedValueForEditTarget(newValue, GetEditTarget(),
1629 [this, &time, &attr](const SdfAbstractDataConstValue &in)
1630 {
1631 return this->_SetValueImpl(time, attr, in);
1632 });
1633 }
1634
1635 // Default _SetValue implementation for most attribute value types that never
1636 // need to be mapped for an edit target.
1637 template <class T>
1638 typename std::enable_if<!UsdStage::_IsEditTargetMappable<T>::value, bool>::type
_SetValue(UsdTimeCode time,const UsdAttribute & attr,const T & newValue)1639 UsdStage::_SetValue(UsdTimeCode time, const UsdAttribute &attr,
1640 const T &newValue)
1641 {
1642 SdfAbstractDataConstTypedValue<T> in(&newValue);
1643 return _SetValueImpl<SdfAbstractDataConstValue>(time, attr, in);
1644 }
1645
1646 // Specializations for SdfTimeCode and its array type which may need to be
1647 // value mapped for edit targets.
1648 // Note that VtDictionary and SdfTimeSampleMap are value types that are time
1649 // mapped when setting metadata, but we don't include them for _SetValue as
1650 // they're not valid attribute value types.
1651 template <class T>
1652 typename std::enable_if<UsdStage::_IsEditTargetMappable<T>::value, bool>::type
_SetValue(UsdTimeCode time,const UsdAttribute & attr,const T & newValue)1653 UsdStage::_SetValue(UsdTimeCode time, const UsdAttribute &attr,
1654 const T &newValue)
1655 {
1656 return _SetEditTargetMappedValue(time, attr, newValue);
1657 }
1658
1659 bool
_SetValue(UsdTimeCode time,const UsdAttribute & attr,const VtValue & newValue)1660 UsdStage::_SetValue(
1661 UsdTimeCode time, const UsdAttribute &attr, const VtValue &newValue)
1662 {
1663 // May need to map the value if it's holding a time code type.
1664 if (newValue.IsHolding<SdfTimeCode>()) {
1665 return _SetValue(time, attr,
1666 newValue.UncheckedGet<SdfTimeCode>());
1667 } else if (newValue.IsHolding<VtArray<SdfTimeCode>>()) {
1668 return _SetValue(time, attr,
1669 newValue.UncheckedGet<VtArray<SdfTimeCode>>());
1670 }
1671 return _SetValueImpl(time, attr, newValue);
1672 }
1673
1674 bool
_ClearValue(UsdTimeCode time,const UsdAttribute & attr)1675 UsdStage::_ClearValue(UsdTimeCode time, const UsdAttribute &attr)
1676 {
1677 if (ARCH_UNLIKELY(!_ValidateEditPrim(attr.GetPrim(), "clear attribute value"))) {
1678 return false;
1679 }
1680
1681 if (time.IsDefault())
1682 return _ClearMetadata(attr, SdfFieldKeys->Default);
1683
1684 const UsdEditTarget &editTarget = GetEditTarget();
1685 if (!editTarget.IsValid()) {
1686 TF_CODING_ERROR("EditTarget does not contain a valid layer.");
1687 return false;
1688 }
1689
1690 const SdfLayerHandle &layer = editTarget.GetLayer();
1691 if (!layer->HasSpec(editTarget.MapToSpecPath(attr.GetPath()))) {
1692 return true;
1693 }
1694
1695 SdfAttributeSpecHandle attrSpec = _CreateAttributeSpecForEditing(attr);
1696
1697 if (!TF_VERIFY(attrSpec,
1698 "Failed to get attribute spec <%s> in layer @%s@",
1699 editTarget.MapToSpecPath(attr.GetPath()).GetText(),
1700 editTarget.GetLayer()->GetIdentifier().c_str())) {
1701 return false;
1702 }
1703
1704 const SdfLayerOffset stageToLayerOffset =
1705 editTarget.GetMapFunction().GetTimeOffset().GetInverse();
1706
1707 const double layerTime = stageToLayerOffset * time.GetValue();
1708
1709 attrSpec->GetLayer()->EraseTimeSample(attrSpec->GetPath(), layerTime);
1710
1711 return true;
1712 }
1713
1714 bool
_ClearMetadata(const UsdObject & obj,const TfToken & fieldName,const TfToken & keyPath)1715 UsdStage::_ClearMetadata(const UsdObject &obj, const TfToken& fieldName,
1716 const TfToken &keyPath)
1717 {
1718 if (ARCH_UNLIKELY(!_ValidateEditPrim(obj.GetPrim(), "clear metadata"))) {
1719 return false;
1720 }
1721
1722 const UsdEditTarget &editTarget = GetEditTarget();
1723 if (!editTarget.IsValid()) {
1724 TF_CODING_ERROR("EditTarget does not contain a valid layer.");
1725 return false;
1726 }
1727
1728 const SdfLayerHandle &layer = editTarget.GetLayer();
1729 if (!layer->HasSpec(editTarget.MapToSpecPath(obj.GetPath()))) {
1730 return true;
1731 }
1732
1733 SdfSpecHandle spec;
1734 if (obj.Is<UsdProperty>())
1735 spec = _CreatePropertySpecForEditing(obj.As<UsdProperty>());
1736 else
1737 spec = _CreatePrimSpecForEditing(obj.As<UsdPrim>());
1738
1739 if (!TF_VERIFY(spec,
1740 "No spec at <%s> in layer @%s@",
1741 editTarget.MapToSpecPath(obj.GetPath()).GetText(),
1742 editTarget.GetLayer()->GetIdentifier().c_str())) {
1743 return false;
1744 }
1745
1746 const auto& schema = spec->GetSchema();
1747 const auto specType = spec->GetSpecType();
1748 if (!schema.IsValidFieldForSpec(fieldName, specType)) {
1749 TF_CODING_ERROR("Cannot clear metadata. '%s' is not registered "
1750 "as valid metadata for spec type %s.",
1751 fieldName.GetText(),
1752 TfStringify(specType).c_str());
1753 return false;
1754 }
1755
1756 if (keyPath.IsEmpty()) {
1757 spec->GetLayer()->EraseField(spec->GetPath(), fieldName);
1758 } else {
1759 spec->GetLayer()->EraseFieldDictValueByKey(
1760 spec->GetPath(), fieldName, keyPath);
1761 }
1762 return true;
1763 }
1764
1765 static
1766 bool
_IsPrivateFieldKey(const TfToken & fieldKey)1767 _IsPrivateFieldKey(const TfToken& fieldKey)
1768 {
1769 static TfHashSet<TfToken, TfToken::HashFunctor> ignoredKeys;
1770
1771 // XXX -- Use this instead of an initializer list in case TfHashSet
1772 // doesn't support initializer lists. Should ensure that
1773 // TfHashSet does support them.
1774 static std::once_flag once;
1775 std::call_once(once, [](){
1776 // Composition keys.
1777 ignoredKeys.insert(SdfFieldKeys->InheritPaths);
1778 ignoredKeys.insert(SdfFieldKeys->Payload);
1779 ignoredKeys.insert(SdfFieldKeys->References);
1780 ignoredKeys.insert(SdfFieldKeys->Specializes);
1781 ignoredKeys.insert(SdfFieldKeys->SubLayers);
1782 ignoredKeys.insert(SdfFieldKeys->SubLayerOffsets);
1783 ignoredKeys.insert(SdfFieldKeys->VariantSelection);
1784 ignoredKeys.insert(SdfFieldKeys->VariantSetNames);
1785 // Clip keys.
1786 {
1787 auto clipFields = UsdGetClipRelatedFields();
1788 ignoredKeys.insert(clipFields.begin(), clipFields.end());
1789 }
1790 // Value keys.
1791 ignoredKeys.insert(SdfFieldKeys->Default);
1792 ignoredKeys.insert(SdfFieldKeys->TimeSamples);
1793 });
1794
1795 // First look-up the field in the exclude/ignore table.
1796 if (ignoredKeys.find(fieldKey) != ignoredKeys.end())
1797 return true;
1798
1799 // Implicitly excluded fields (child containers & readonly metadata).
1800 SdfSchema const & schema = SdfSchema::GetInstance();
1801 SdfSchema::FieldDefinition const* field =
1802 schema.GetFieldDefinition(fieldKey);
1803 if (field && (field->IsReadOnly() || field->HoldsChildren()))
1804 return true;
1805
1806 // The field is not private.
1807 return false;
1808 }
1809
1810 UsdPrim
GetPseudoRoot() const1811 UsdStage::GetPseudoRoot() const
1812 {
1813 return UsdPrim(_pseudoRoot, SdfPath());
1814 }
1815
1816 UsdPrim
GetDefaultPrim() const1817 UsdStage::GetDefaultPrim() const
1818 {
1819 TfToken name = GetRootLayer()->GetDefaultPrim();
1820 return SdfPath::IsValidIdentifier(name)
1821 ? GetPrimAtPath(SdfPath::AbsoluteRootPath().AppendChild(name))
1822 : UsdPrim();
1823 }
1824
1825 void
SetDefaultPrim(const UsdPrim & prim)1826 UsdStage::SetDefaultPrim(const UsdPrim &prim)
1827 {
1828 GetRootLayer()->SetDefaultPrim(prim.GetName());
1829 }
1830
1831 void
ClearDefaultPrim()1832 UsdStage::ClearDefaultPrim()
1833 {
1834 GetRootLayer()->ClearDefaultPrim();
1835 }
1836
1837 bool
HasDefaultPrim() const1838 UsdStage::HasDefaultPrim() const
1839 {
1840 return GetRootLayer()->HasDefaultPrim();
1841 }
1842
1843 UsdPrim
GetPrimAtPath(const SdfPath & path) const1844 UsdStage::GetPrimAtPath(const SdfPath &path) const
1845 {
1846 // Silently return an invalid UsdPrim if the given path is not an
1847 // absolute path to maintain existing behavior.
1848 if (!path.IsAbsolutePath()) {
1849 return UsdPrim();
1850 }
1851
1852 // If this path points to a prim beneath an instance, return
1853 // an instance proxy that uses the prim data from the corresponding
1854 // prim in the prototype but appears to be a prim at the given path.
1855 Usd_PrimDataConstPtr primData = _GetPrimDataAtPathOrInPrototype(path);
1856 const SdfPath& proxyPrimPath =
1857 primData && primData->GetPath() != path ? path : SdfPath::EmptyPath();
1858 return UsdPrim(primData, proxyPrimPath);
1859 }
1860
1861 UsdObject
GetObjectAtPath(const SdfPath & path) const1862 UsdStage::GetObjectAtPath(const SdfPath &path) const
1863 {
1864 // Maintain consistent behavior with GetPrimAtPath
1865 if (!path.IsAbsolutePath()) {
1866 return UsdObject();
1867 }
1868
1869 const bool isPrimPath = path.IsPrimPath();
1870 const bool isPropPath = !isPrimPath && path.IsPropertyPath();
1871 if (!isPrimPath && !isPropPath) {
1872 return UsdObject();
1873 }
1874
1875 // A valid prim must be found to return either a prim or prop
1876 if (isPrimPath) {
1877 return GetPrimAtPath(path);
1878 } else if (isPropPath) {
1879 if (auto prim = GetPrimAtPath(path.GetPrimPath())) {
1880 return prim.GetProperty(path.GetNameToken());
1881 }
1882 }
1883
1884 return UsdObject();
1885 }
1886
1887 UsdProperty
GetPropertyAtPath(const SdfPath & path) const1888 UsdStage::GetPropertyAtPath(const SdfPath &path) const
1889 {
1890 return GetObjectAtPath(path).As<UsdProperty>();
1891 }
1892
1893 UsdAttribute
GetAttributeAtPath(const SdfPath & path) const1894 UsdStage::GetAttributeAtPath(const SdfPath &path) const
1895 {
1896 return GetObjectAtPath(path).As<UsdAttribute>();
1897 }
1898
1899 UsdRelationship
GetRelationshipAtPath(const SdfPath & path) const1900 UsdStage::GetRelationshipAtPath(const SdfPath &path) const
1901 {
1902 return GetObjectAtPath(path).As<UsdRelationship>();
1903 }
1904
1905 Usd_PrimDataConstPtr
_GetPrimDataAtPath(const SdfPath & path) const1906 UsdStage::_GetPrimDataAtPath(const SdfPath &path) const
1907 {
1908 tbb::spin_rw_mutex::scoped_lock lock;
1909 if (_primMapMutex)
1910 lock.acquire(*_primMapMutex, /*write=*/false);
1911 PathToNodeMap::const_iterator entry = _primMap.find(path);
1912 return entry != _primMap.end() ? entry->second.get() : nullptr;
1913 }
1914
1915 Usd_PrimDataPtr
_GetPrimDataAtPath(const SdfPath & path)1916 UsdStage::_GetPrimDataAtPath(const SdfPath &path)
1917 {
1918 tbb::spin_rw_mutex::scoped_lock lock;
1919 if (_primMapMutex)
1920 lock.acquire(*_primMapMutex, /*write=*/false);
1921 PathToNodeMap::const_iterator entry = _primMap.find(path);
1922 return entry != _primMap.end() ? entry->second.get() : nullptr;
1923 }
1924
1925 Usd_PrimDataConstPtr
_GetPrimDataAtPathOrInPrototype(const SdfPath & path) const1926 UsdStage::_GetPrimDataAtPathOrInPrototype(const SdfPath &path) const
1927 {
1928 Usd_PrimDataConstPtr primData = _GetPrimDataAtPath(path);
1929
1930 // If no prim data exists at the given path, check if this
1931 // path is pointing to a prim beneath an instance. If so, we
1932 // need to return the prim data for the corresponding prim
1933 // in the prototype.
1934 if (!primData) {
1935 const SdfPath primInPrototypePath =
1936 _instanceCache->GetPathInPrototypeForInstancePath(path);
1937 if (!primInPrototypePath.IsEmpty()) {
1938 primData = _GetPrimDataAtPath(primInPrototypePath);
1939 }
1940 }
1941
1942 return primData;
1943 }
1944
1945 bool
_IsValidForUnload(const SdfPath & path) const1946 UsdStage::_IsValidForUnload(const SdfPath& path) const
1947 {
1948 if (!path.IsAbsolutePath()) {
1949 TF_CODING_ERROR("Attempted to load/unload a relative path <%s>",
1950 path.GetText());
1951 return false;
1952 }
1953 if (_instanceCache->IsPathInPrototype(path)) {
1954 TF_CODING_ERROR("Attempted to load/unload a prototype path <%s>",
1955 path.GetText());
1956 return false;
1957 }
1958 return true;
1959 }
1960
1961 bool
_IsValidForLoad(const SdfPath & path) const1962 UsdStage::_IsValidForLoad(const SdfPath& path) const
1963 {
1964 if (!_IsValidForUnload(path)) {
1965 return false;
1966 }
1967
1968 // XXX PERFORMANCE: could use HasPrimAtPath
1969 UsdPrim curPrim = GetPrimAtPath(path);
1970
1971 if (!curPrim) {
1972 // Lets see if any ancestor exists, if so it's safe to attempt to load.
1973 SdfPath parentPath = path;
1974 while (parentPath != SdfPath::AbsoluteRootPath()) {
1975 if ((curPrim = GetPrimAtPath(parentPath))) {
1976 break;
1977 }
1978 parentPath = parentPath.GetParentPath();
1979 }
1980
1981 // We walked up to the absolute root without finding anything
1982 // report error.
1983 if (parentPath == SdfPath::AbsoluteRootPath()) {
1984 TF_RUNTIME_ERROR("Attempt to load a path <%s> which is not "
1985 "present in the stage",
1986 path.GetString().c_str());
1987 return false;
1988 }
1989 }
1990
1991 if (!curPrim.IsActive()) {
1992 TF_CODING_ERROR("Attempt to load an inactive path <%s>",
1993 path.GetString().c_str());
1994 return false;
1995 }
1996
1997 if (curPrim.IsPrototype()) {
1998 TF_CODING_ERROR("Attempt to load instance prototype <%s>",
1999 path.GetString().c_str());
2000 return false;
2001 }
2002
2003 return true;
2004 }
2005
2006 void
_DiscoverPayloads(const SdfPath & rootPath,UsdLoadPolicy policy,SdfPathSet * primIndexPaths,bool unloadedOnly,SdfPathSet * usdPrimPaths) const2007 UsdStage::_DiscoverPayloads(const SdfPath& rootPath,
2008 UsdLoadPolicy policy,
2009 SdfPathSet* primIndexPaths,
2010 bool unloadedOnly,
2011 SdfPathSet* usdPrimPaths) const
2012 {
2013 tbb::concurrent_vector<SdfPath> primIndexPathsVec;
2014 tbb::concurrent_vector<SdfPath> usdPrimPathsVec;
2015
2016 auto addPrimPayload =
2017 [this, unloadedOnly, primIndexPaths, usdPrimPaths,
2018 &primIndexPathsVec, &usdPrimPathsVec]
2019 (UsdPrim const &prim) {
2020 // Inactive prims are never included in this query. Prototypes are
2021 // also never included, since they aren't independently loadable.
2022 if (!prim.IsActive() || prim.IsPrototype())
2023 return;
2024
2025 if (prim._GetSourcePrimIndex().HasAnyPayloads()) {
2026 SdfPath const &payloadIncludePath =
2027 prim._GetSourcePrimIndex().GetPath();
2028 if (!unloadedOnly ||
2029 !_cache->IsPayloadIncluded(payloadIncludePath)) {
2030 if (primIndexPaths)
2031 primIndexPathsVec.push_back(payloadIncludePath);
2032 if (usdPrimPaths)
2033 usdPrimPathsVec.push_back(prim.GetPath());
2034 }
2035 }
2036 };
2037
2038 if (policy == UsdLoadWithDescendants) {
2039 if (UsdPrim root = GetPrimAtPath(rootPath)) {
2040 UsdPrimRange children = UsdPrimRange(
2041 root, UsdTraverseInstanceProxies(UsdPrimAllPrimsPredicate));
2042 WorkParallelForEach(
2043 children.begin(), children.end(), addPrimPayload);
2044 }
2045 } else {
2046 addPrimPayload(GetPrimAtPath(rootPath));
2047 }
2048
2049 // Copy stuff out.
2050 if (primIndexPaths) {
2051 primIndexPaths->insert(
2052 primIndexPathsVec.begin(), primIndexPathsVec.end());
2053 }
2054 if (usdPrimPaths) {
2055 usdPrimPaths->insert(usdPrimPathsVec.begin(), usdPrimPathsVec.end());
2056 }
2057 }
2058
2059 UsdPrim
Load(const SdfPath & path,UsdLoadPolicy policy)2060 UsdStage::Load(const SdfPath& path, UsdLoadPolicy policy)
2061 {
2062 SdfPathSet exclude, include;
2063 include.insert(path);
2064
2065 // Update the load set; this will trigger recomposition and include any
2066 // recursive payloads needed.
2067 LoadAndUnload(include, exclude, policy);
2068
2069 return GetPrimAtPath(path);
2070 }
2071
2072 void
Unload(const SdfPath & path)2073 UsdStage::Unload(const SdfPath& path)
2074 {
2075 SdfPathSet include, exclude;
2076 exclude.insert(path);
2077
2078 // Update the load set; this will trigger recomposition and include any
2079 // recursive payloads needed.
2080 LoadAndUnload(include, exclude);
2081 }
2082
2083 void
LoadAndUnload(const SdfPathSet & loadSet,const SdfPathSet & unloadSet,UsdLoadPolicy policy)2084 UsdStage::LoadAndUnload(const SdfPathSet &loadSet,
2085 const SdfPathSet &unloadSet,
2086 UsdLoadPolicy policy)
2087 {
2088 TfAutoMallocTag2 tag("Usd", _mallocTagID);
2089
2090 // Optimization: If either or both of the sets is empty then check the other
2091 // set to see if the load rules already produce the desired state. If so
2092 // this is a noop and we can early-out.
2093 if (loadSet.empty() || unloadSet.empty()) {
2094 bool isNoOp = true;
2095 if (unloadSet.empty()) {
2096 // Check the loadSet to see if we're already in the desired state.
2097 for (SdfPath const &path: loadSet) {
2098 if ((policy == UsdLoadWithDescendants &&
2099 !_loadRules.IsLoadedWithAllDescendants(path)) ||
2100 (policy == UsdLoadWithoutDescendants &&
2101 !_loadRules.IsLoadedWithNoDescendants(path))) {
2102 isNoOp = false;
2103 break;
2104 }
2105 }
2106 }
2107 else {
2108 // Check the unloadSet to see if we're already in the desired state.
2109 for (SdfPath const &path: unloadSet) {
2110 if (_loadRules.GetEffectiveRuleForPath(path) !=
2111 UsdStageLoadRules::NoneRule) {
2112 isNoOp = false;
2113 break;
2114 }
2115 }
2116 }
2117 if (isNoOp) {
2118 // No changes in effective load state for given paths, early-out.
2119 return;
2120 }
2121 }
2122
2123 SdfPathSet finalLoadSet, finalUnloadSet;
2124
2125 for (auto const &path : loadSet) {
2126 if (!_IsValidForLoad(path)) {
2127 continue;
2128 }
2129 finalLoadSet.insert(path);
2130 }
2131
2132 for (auto const &path: unloadSet) {
2133 if (!_IsValidForUnload(path)) {
2134 continue;
2135 }
2136 finalUnloadSet.insert(path);
2137 }
2138
2139 _loadRules.LoadAndUnload(finalLoadSet, finalUnloadSet, policy);
2140
2141 // Go through the finalLoadSet, and check ancestors -- if any are loaded,
2142 // include the most ancestral which was loaded last in the finalLoadSet.
2143 for (SdfPath const &p: finalLoadSet) {
2144 SdfPath curPath = p;
2145 while (true) {
2146 SdfPath parentPath = curPath.GetParentPath();
2147 if (parentPath.IsEmpty())
2148 break;
2149 UsdPrim prim = GetPrimAtPath(parentPath);
2150 if (prim && prim.IsLoaded() && p != curPath) {
2151 finalLoadSet.insert(curPath);
2152 break;
2153 }
2154 curPath = parentPath;
2155 }
2156 }
2157
2158 // Go through the loadSet and unloadSet, and find the most ancestral
2159 // instance path for each (or the path itself if no such path exists) and
2160 // treat them as significant changes.
2161 SdfPathVector recomposePaths;
2162 for (SdfPath const &p: finalLoadSet) {
2163 SdfPath instancePath = _instanceCache->GetMostAncestralInstancePath(p);
2164 recomposePaths.push_back(instancePath.IsEmpty() ? p : instancePath);
2165 }
2166 for (SdfPath const &p: finalUnloadSet) {
2167 SdfPath instancePath = _instanceCache->GetMostAncestralInstancePath(p);
2168 recomposePaths.push_back(instancePath.IsEmpty() ? p : instancePath);
2169 }
2170
2171 // This leaves recomposePaths sorted.
2172 SdfPath::RemoveDescendentPaths(&recomposePaths);
2173
2174 PcpChanges changes;
2175 for (SdfPath const &p: recomposePaths) {
2176 changes.DidChangeSignificantly(_cache.get(), p);
2177 }
2178
2179 // Remove any included payloads that are descendant to recomposePaths.
2180 // We'll re-include everything we need during _Recompose via the inclusion
2181 // predicate.
2182 PcpCache::PayloadSet const ¤tIncludes = _cache->GetIncludedPayloads();
2183 SdfPathSet currentIncludesAsSet(currentIncludes.begin(),
2184 currentIncludes.end());
2185 SdfPathSet payloadsToExclude;
2186 for (SdfPath const &p: recomposePaths) {
2187 auto range = SdfPathFindPrefixedRange(currentIncludesAsSet.begin(),
2188 currentIncludesAsSet.end(), p);
2189 payloadsToExclude.insert(range.first, range.second);
2190 }
2191 _cache->RequestPayloads(SdfPathSet(), payloadsToExclude, &changes);
2192
2193 if (TfDebug::IsEnabled(USD_PAYLOADS)) {
2194 TF_DEBUG(USD_PAYLOADS).Msg(
2195 "UsdStage::LoadAndUnload()\n"
2196 " finalLoadSet: %s\n"
2197 " finalUnloadSet: %s\n"
2198 " _loadRules: %s\n"
2199 " payloadsToExclude: %s\n"
2200 " recomposePaths: %s\n",
2201 TfStringify(finalLoadSet).c_str(),
2202 TfStringify(finalUnloadSet).c_str(),
2203 TfStringify(_loadRules).c_str(),
2204 TfStringify(payloadsToExclude).c_str(),
2205 TfStringify(recomposePaths).c_str());
2206 }
2207
2208 // Recompose, given the resulting changes from Pcp.
2209 //
2210 // PERFORMANCE: Note that Pcp will always include the paths in
2211 // both sets as "significant changes" regardless of the actual changes
2212 // resulting from this request, this will trigger recomposition of UsdPrims
2213 // that potentially didn't change; it seems like we could do better.
2214 TF_DEBUG(USD_CHANGES).Msg("\nProcessing Load/Unload changes\n");
2215 _Recompose(changes);
2216
2217 UsdStageWeakPtr self(this);
2218
2219 UsdNotice::ObjectsChanged::_PathsToChangesMap resyncChanges, infoChanges;
2220 for (SdfPath const &p: recomposePaths) {
2221 resyncChanges[p];
2222 }
2223
2224 UsdNotice::ObjectsChanged(self, &resyncChanges, &infoChanges).Send(self);
2225
2226 UsdNotice::StageContentsChanged(self).Send(self);
2227 }
2228
2229 SdfPathSet
GetLoadSet()2230 UsdStage::GetLoadSet()
2231 {
2232 SdfPathSet loadSet;
2233 for (const auto& primIndexPath : _cache->GetIncludedPayloads()) {
2234 // Get the path of the Usd prim using this prim index path.
2235 // This ensures we return the appropriate path if this prim index
2236 // is being used by a prim within a prototype.
2237 //
2238 // If there is no Usd prim using this prim index, we return the
2239 // prim index path anyway. This could happen if the ancestor of
2240 // a previously-loaded prim is deactivated, for instance.
2241 // Including this path in the returned set reflects what's loaded
2242 // in the underlying PcpCache and ensures users can still unload
2243 // the payloads for those prims by calling
2244 // LoadAndUnload([], GetLoadSet()).
2245 const SdfPath primPath =
2246 _GetPrimPathUsingPrimIndexAtPath(primIndexPath);
2247 if (primPath.IsEmpty()) {
2248 loadSet.insert(primIndexPath);
2249 }
2250 else {
2251 loadSet.insert(primPath);
2252 }
2253 }
2254
2255 return loadSet;
2256 }
2257
2258 SdfPathSet
FindLoadable(const SdfPath & rootPath)2259 UsdStage::FindLoadable(const SdfPath& rootPath)
2260 {
2261 SdfPath path = rootPath;
2262
2263 SdfPathSet loadable;
2264 _DiscoverPayloads(path, UsdLoadWithDescendants, nullptr,
2265 /* unloadedOnly = */ false, &loadable);
2266 return loadable;
2267 }
2268
2269 void
SetLoadRules(UsdStageLoadRules const & rules)2270 UsdStage::SetLoadRules(UsdStageLoadRules const &rules)
2271 {
2272 // For now just set the rules and recompose everything.
2273 _loadRules = rules;
2274
2275 PcpChanges changes;
2276 changes.DidChangeSignificantly(_cache.get(), SdfPath::AbsoluteRootPath());
2277 _Recompose(changes);
2278
2279 // Notify.
2280 UsdStageWeakPtr self(this);
2281 UsdNotice::ObjectsChanged::_PathsToChangesMap resyncChanges, infoChanges;
2282 resyncChanges[SdfPath::AbsoluteRootPath()];
2283 UsdNotice::ObjectsChanged(self, &resyncChanges, &infoChanges).Send(self);
2284 UsdNotice::StageContentsChanged(self).Send(self);
2285 }
2286
2287 void
SetPopulationMask(UsdStagePopulationMask const & mask)2288 UsdStage::SetPopulationMask(UsdStagePopulationMask const &mask)
2289 {
2290 // For now just set the mask and recompose everything.
2291 _populationMask = mask;
2292
2293 PcpChanges changes;
2294 changes.DidChangeSignificantly(_cache.get(), SdfPath::AbsoluteRootPath());
2295 _Recompose(changes);
2296
2297 // Notify.
2298 UsdStageWeakPtr self(this);
2299 UsdNotice::ObjectsChanged::_PathsToChangesMap resyncChanges, infoChanges;
2300 resyncChanges[SdfPath::AbsoluteRootPath()];
2301 UsdNotice::ObjectsChanged(self, &resyncChanges, &infoChanges).Send(self);
2302 UsdNotice::StageContentsChanged(self).Send(self);
2303 }
2304
2305 void
ExpandPopulationMask(std::function<bool (UsdRelationship const &)> const & relPred,std::function<bool (UsdAttribute const &)> const & attrPred)2306 UsdStage::ExpandPopulationMask(
2307 std::function<bool (UsdRelationship const &)> const &relPred,
2308 std::function<bool (UsdAttribute const &)> const &attrPred)
2309 {
2310 if (GetPopulationMask().IncludesSubtree(SdfPath::AbsoluteRootPath()))
2311 return;
2312
2313 // Walk everything, calling UsdPrim::FindAllRelationshipTargetPaths() and
2314 // include them in the mask. If the mask changes, call SetPopulationMask()
2315 // and redo. Continue until the mask ceases expansion.
2316 while (true) {
2317 auto root = GetPseudoRoot();
2318 SdfPathVector
2319 tgtPaths = root.FindAllRelationshipTargetPaths(relPred, false),
2320 connPaths = root.FindAllAttributeConnectionPaths(attrPred, false);
2321
2322 tgtPaths.erase(remove_if(tgtPaths.begin(), tgtPaths.end(),
2323 [this](SdfPath const &path) {
2324 return _populationMask.Includes(path);
2325 }),
2326 tgtPaths.end());
2327 connPaths.erase(remove_if(connPaths.begin(), connPaths.end(),
2328 [this](SdfPath const &path) {
2329 return _populationMask.Includes(path);
2330 }),
2331 connPaths.end());
2332
2333 if (tgtPaths.empty() && connPaths.empty())
2334 break;
2335
2336 auto popMask = GetPopulationMask();
2337 for (auto const &path: tgtPaths) {
2338 popMask.Add(path.GetPrimPath());
2339 }
2340 for (auto const &path: connPaths) {
2341 popMask.Add(path.GetPrimPath());
2342 }
2343 SetPopulationMask(popMask);
2344 }
2345 }
2346
2347 // ------------------------------------------------------------------------- //
2348 // Instancing
2349 // ------------------------------------------------------------------------- //
2350
2351 vector<UsdPrim>
GetPrototypes() const2352 UsdStage::GetPrototypes() const
2353 {
2354 // Sort the instance prototype paths to provide a stable ordering for
2355 // this function.
2356 SdfPathVector prototypePaths = _instanceCache->GetAllPrototypes();
2357 std::sort(prototypePaths.begin(), prototypePaths.end());
2358
2359 vector<UsdPrim> prototypePrims;
2360 for (const auto& path : prototypePaths) {
2361 UsdPrim p = GetPrimAtPath(path);
2362 if (TF_VERIFY(p, "Failed to find prim at prototype path <%s>.\n",
2363 path.GetText())) {
2364 prototypePrims.push_back(p);
2365 }
2366 }
2367 return prototypePrims;
2368 }
2369
2370 vector<UsdPrim>
_GetInstancesForPrototype(const UsdPrim & prototypePrim) const2371 UsdStage::_GetInstancesForPrototype(const UsdPrim& prototypePrim) const
2372 {
2373 if (!prototypePrim.IsPrototype()) {
2374 return {};
2375 }
2376
2377 vector<UsdPrim> instances;
2378 SdfPathVector instancePaths =
2379 _instanceCache->GetInstancePrimIndexesForPrototype(
2380 prototypePrim.GetPath());
2381 instances.reserve(instancePaths.size());
2382 for (const SdfPath& instancePath : instancePaths) {
2383 Usd_PrimDataConstPtr primData =
2384 _GetPrimDataAtPathOrInPrototype(instancePath);
2385 instances.push_back(UsdPrim(primData, SdfPath::EmptyPath()));
2386 }
2387 return instances;
2388 }
2389
2390 Usd_PrimDataConstPtr
_GetPrototypeForInstance(Usd_PrimDataConstPtr prim) const2391 UsdStage::_GetPrototypeForInstance(Usd_PrimDataConstPtr prim) const
2392 {
2393 if (!prim->IsInstance()) {
2394 return nullptr;
2395 }
2396
2397 const SdfPath protoPath =
2398 _instanceCache->GetPrototypeForInstanceablePrimIndexPath(
2399 prim->GetPrimIndex().GetPath());
2400 return protoPath.IsEmpty() ? nullptr : _GetPrimDataAtPath(protoPath);
2401 }
2402
2403 bool
_IsObjectDescendantOfInstance(const SdfPath & path) const2404 UsdStage::_IsObjectDescendantOfInstance(const SdfPath& path) const
2405 {
2406 // If the given path is a descendant of an instanceable
2407 // prim index, it would not be computed during composition unless
2408 // it is also serving as the source prim index for a prototype prim
2409 // on this stage.
2410 //
2411 // Check if we have any instancing in this stage to avoid unnecessary
2412 // path operations for performance.
2413 return (_instanceCache->GetNumPrototypes() > 0 &&
2414 _instanceCache->IsPathDescendantToAnInstance(
2415 path.GetAbsoluteRootOrPrimPath()));
2416 }
2417
2418 SdfPath
_GetPrimPathUsingPrimIndexAtPath(const SdfPath & primIndexPath) const2419 UsdStage::_GetPrimPathUsingPrimIndexAtPath(const SdfPath& primIndexPath) const
2420 {
2421 SdfPath primPath;
2422
2423 // In general, the path of a UsdPrim on a stage is the same as the
2424 // path of its prim index. However, this is not the case when
2425 // prims in prototypes are involved. In these cases, we need to use
2426 // the instance cache to map the prim index path to the prototype
2427 // prim on the stage.
2428 if (GetPrimAtPath(primIndexPath)) {
2429 primPath = primIndexPath;
2430 }
2431 else if (_instanceCache->GetNumPrototypes() != 0) {
2432 const vector<SdfPath> prototypesUsingPrimIndex =
2433 _instanceCache->GetPrimsInPrototypesUsingPrimIndexPath(
2434 primIndexPath);
2435
2436 for (const auto& pathInPrototype : prototypesUsingPrimIndex) {
2437 // If this path is a root prim path, it must be the path of a
2438 // prototype prim. This function wants to ignore prototype prims,
2439 // since they appear to have no prim index to the outside
2440 // consumer.
2441 //
2442 // However, if this is not a root prim path, it must be the
2443 // path of an prim nested inside a prototype, which we do want
2444 // to return. There will only ever be one of these, so we
2445 // can get this prim and break immediately.
2446 if (!pathInPrototype.IsRootPrimPath()) {
2447 primPath = pathInPrototype;
2448 break;
2449 }
2450 }
2451 }
2452
2453 return primPath;
2454 }
2455
2456 Usd_PrimDataPtr
_InstantiatePrim(const SdfPath & primPath)2457 UsdStage::_InstantiatePrim(const SdfPath &primPath)
2458 {
2459 TfAutoMallocTag tag("Usd_PrimData");
2460
2461 // Instantiate new prim data instance.
2462 Usd_PrimDataPtr p = new Usd_PrimData(this, primPath);
2463 pair<PathToNodeMap::iterator, bool> result;
2464 std::pair<SdfPath, Usd_PrimDataPtr> payload(primPath, p);
2465 {
2466 tbb::spin_rw_mutex::scoped_lock lock;
2467 if (_primMapMutex)
2468 lock.acquire(*_primMapMutex);
2469 result = _primMap.insert(payload);
2470 }
2471
2472 // Insert entry into the map -- should always succeed.
2473 TF_VERIFY(result.second, "Newly instantiated prim <%s> already present in "
2474 "_primMap", primPath.GetText());
2475 return p;
2476 }
2477
2478 Usd_PrimDataPtr
_InstantiatePrototypePrim(const SdfPath & primPath)2479 UsdStage::_InstantiatePrototypePrim(const SdfPath &primPath)
2480 {
2481 // Prototype prims are parented beneath the pseudo-root,
2482 // but are *not* children of the pseudo-root. This ensures
2483 // that consumers never see prototype prims unless they are
2484 // explicitly asked for. So, we don't need to set the child
2485 // link here.
2486 Usd_PrimDataPtr prototypePrim = _InstantiatePrim(primPath);
2487 prototypePrim->_SetParentLink(_pseudoRoot);
2488 return prototypePrim;
2489 }
2490
2491 namespace {
2492 // Less-than comparison for iterators that compares what they point to.
2493 struct _DerefIterLess {
2494 template <class Iter>
operator ()__anon7d3fbd5f1111::_DerefIterLess2495 bool operator()(const Iter &lhs, const Iter &rhs) const {
2496 return *lhs < *rhs;
2497 }
2498 };
2499 // Less-than comparison by prim name.
2500 struct _PrimNameLess {
2501 template <class PrimPtr>
operator ()__anon7d3fbd5f1111::_PrimNameLess2502 bool operator()(const PrimPtr &lhs, const PrimPtr &rhs) const {
2503 return lhs->GetName() < rhs->GetName();
2504 }
2505 };
2506 // Less-than comparison of second element in a pair.
2507 struct _SecondLess {
2508 template <class Pair>
operator ()__anon7d3fbd5f1111::_SecondLess2509 bool operator()(const Pair &lhs, const Pair &rhs) const {
2510 return lhs.second < rhs.second;
2511 }
2512 };
2513 }
2514
2515 // This method has some subtle behavior to support minimal repopulation and
2516 // ideal allocation order. See documentation for this method in the .h file for
2517 // important details regarding this method's behavior.
2518 void
_ComposeChildren(Usd_PrimDataPtr prim,UsdStagePopulationMask const * mask,bool recurse)2519 UsdStage::_ComposeChildren(Usd_PrimDataPtr prim,
2520 UsdStagePopulationMask const *mask, bool recurse)
2521 {
2522 // If prim is deactivated, discard any existing children and return.
2523 if (!prim->IsActive()) {
2524 TF_DEBUG(USD_COMPOSITION).Msg("Inactive prim <%s>\n",
2525 prim->GetPath().GetText());
2526 _DestroyDescendents(prim);
2527 return;
2528 }
2529
2530 // Instance prims do not directly expose any of their name children.
2531 // Discard any pre-existing children and add a task for composing
2532 // the instance's prototype's subtree if it's root uses this instance's
2533 // prim index as a source.
2534 if (prim->IsInstance()) {
2535 TF_DEBUG(USD_COMPOSITION).Msg("Instance prim <%s>\n",
2536 prim->GetPath().GetText());
2537 _DestroyDescendents(prim);
2538 return;
2539 }
2540
2541 // Compose child names for this prim.
2542 TfTokenVector nameOrder;
2543 if (!TF_VERIFY(prim->_ComposePrimChildNames(&nameOrder)))
2544 return;
2545
2546 // Filter nameOrder by the mask, if necessary. If this subtree is
2547 // completely included, stop looking at the mask from here forward.
2548 if (mask) {
2549 // We always operate on the source prim index path here, not the prim
2550 // path since that would be something like /__Prototype_X/.. for prims
2551 // in prototypes. Masks and load rules operate on the "uninstanced"
2552 // view of the world, and are included in instancing keys, so whichever
2553 // index we choose to be the source for a prototype must be included in
2554 // the stage-wide pop mask & load rules, and identically for all
2555 // instances that share a prototype.
2556 const SdfPath& sourceIndexPath = prim->GetSourcePrimIndex().GetPath();
2557 if (mask->IncludesSubtree(sourceIndexPath)) {
2558 mask = nullptr;
2559 } else {
2560 // Remove all names from nameOrder that aren't included in the mask.
2561 nameOrder.erase(
2562 remove_if(nameOrder.begin(), nameOrder.end(),
2563 [&sourceIndexPath, mask](TfToken const &nameTok) {
2564 return !mask->Includes(
2565 sourceIndexPath.AppendChild(nameTok));
2566 }), nameOrder.end());
2567 }
2568 }
2569
2570 // If the prim has no children, simply destroy any existing child prims.
2571 if (nameOrder.empty()) {
2572 TF_DEBUG(USD_COMPOSITION).Msg("Children empty <%s>\n",
2573 prim->GetPath().GetText());
2574 _DestroyDescendents(prim);
2575 return;
2576 }
2577
2578 // Find the first mismatch between the prim's current child prims and
2579 // the new list of child prims specified in nameOrder.
2580 Usd_PrimDataSiblingIterator
2581 begin = prim->_ChildrenBegin(),
2582 end = prim->_ChildrenEnd(),
2583 cur = begin;
2584 TfTokenVector::const_iterator
2585 curName = nameOrder.begin(),
2586 nameEnd = nameOrder.end();
2587 for (; cur != end && curName != nameEnd; ++cur, ++curName) {
2588 if ((*cur)->GetName() != *curName)
2589 break;
2590 }
2591
2592 // The prims in [begin, cur) match the children specified in
2593 // [nameOrder.begin(), curName); recompose these child subtrees if needed.
2594 if (recurse) {
2595 for (Usd_PrimDataSiblingIterator it = begin; it != cur; ++it) {
2596 _ComposeChildSubtree(*it, prim, mask);
2597 }
2598 }
2599
2600 // The prims in [cur, end) do not match the children specified in
2601 // [curName, nameEnd), so we need to process these trailing elements.
2602
2603 // No trailing elements means children are unchanged.
2604 if (cur == end && curName == nameEnd) {
2605 TF_DEBUG(USD_COMPOSITION).Msg("Children same in same order <%s>\n",
2606 prim->GetPath().GetText());
2607 return;
2608 }
2609
2610 // Trailing names only mean that children have been added to the end
2611 // of the prim's existing children. Note this includes the case where
2612 // the prim had no children previously.
2613 if (cur == end && curName != nameEnd) {
2614 const SdfPath& parentPath = prim->GetPath();
2615 Usd_PrimDataPtr head = nullptr, prev = nullptr, tail = nullptr;
2616 for (; curName != nameEnd; ++curName) {
2617 tail = _InstantiatePrim(parentPath.AppendChild(*curName));
2618 if (recurse) {
2619 _ComposeChildSubtree(tail, prim, mask);
2620 }
2621 if (!prev) {
2622 head = tail;
2623 }
2624 else {
2625 prev->_SetSiblingLink(tail);
2626 }
2627 prev = tail;
2628 }
2629
2630 if (cur == begin) {
2631 TF_DEBUG(USD_COMPOSITION).Msg("Children all new <%s>\n",
2632 prim->GetPath().GetText());
2633 TF_VERIFY(!prim->_firstChild);
2634 prim->_firstChild = head;
2635 tail->_SetParentLink(prim);
2636 }
2637 else {
2638 TF_DEBUG(USD_COMPOSITION).Msg("Children appended <%s>\n",
2639 prim->GetPath().GetText());
2640 Usd_PrimDataSiblingIterator lastChild = begin, next = begin;
2641 for (++next; next != cur; lastChild = next, ++next) { }
2642
2643 (*lastChild)->_SetSiblingLink(head);
2644 tail->_SetParentLink(prim);
2645 }
2646 return;
2647 }
2648
2649 // Trailing children only mean that children have been removed from
2650 // the end of the prim's existing children.
2651 if (cur != end && curName == nameEnd) {
2652 TF_DEBUG(USD_COMPOSITION).Msg("Children removed from end <%s>\n",
2653 prim->GetPath().GetText());
2654 for (Usd_PrimDataSiblingIterator it = cur; it != end; ) {
2655 // Make sure we advance to the next sibling before we destroy
2656 // the current child so we don't read from a deleted prim.
2657 _DestroyPrim(*it++);
2658 }
2659
2660 if (cur == begin) {
2661 prim->_firstChild = nullptr;
2662 }
2663 else {
2664 Usd_PrimDataSiblingIterator lastChild = begin, next = begin;
2665 for (++next; next != cur; lastChild = next, ++next) { }
2666 (*lastChild)->_SetParentLink(prim);
2667 }
2668 return;
2669 }
2670
2671 // Otherwise, both trailing children and names mean there was some
2672 // other change to the prim's list of children. Do the general form
2673 // of preserving preexisting children and ordering them according
2674 // to nameOrder.
2675 TF_DEBUG(USD_COMPOSITION).Msg(
2676 "Require general children recomposition <%s>\n",
2677 prim->GetPath().GetText());
2678
2679 // Make a vector of iterators into nameOrder from [curName, nameEnd).
2680 typedef vector<TfTokenVector::const_iterator> TokenVectorIterVec;
2681 TokenVectorIterVec nameOrderIters(std::distance(curName, nameEnd));
2682 for (size_t i = 0, sz = nameOrderIters.size(); i != sz; ++i) {
2683 nameOrderIters[i] = curName + i;
2684 }
2685
2686 // Sort the name order iterators *by name*.
2687 sort(nameOrderIters.begin(), nameOrderIters.end(), _DerefIterLess());
2688
2689 // Make a vector of the existing prim children and sort them by name.
2690 vector<Usd_PrimDataPtr> oldChildren(cur, end);
2691 sort(oldChildren.begin(), oldChildren.end(), _PrimNameLess());
2692
2693 vector<Usd_PrimDataPtr>::const_iterator
2694 oldChildIt = oldChildren.begin(),
2695 oldChildEnd = oldChildren.end();
2696
2697 TokenVectorIterVec::const_iterator
2698 newNameItersIt = nameOrderIters.begin(),
2699 newNameItersEnd = nameOrderIters.end();
2700
2701 // We build a vector of pairs of prims and the original name order
2702 // iterators. This lets us re-sort by original order once we're finished.
2703 vector<pair<Usd_PrimDataPtr, TfTokenVector::const_iterator> >
2704 tempChildren;
2705 tempChildren.reserve(nameOrderIters.size());
2706
2707 const SdfPath &parentPath = prim->GetPath();
2708
2709 while (newNameItersIt != newNameItersEnd || oldChildIt != oldChildEnd) {
2710 // Walk through old children that no longer exist up to the current
2711 // potentially new name, removing them.
2712 while (oldChildIt != oldChildEnd &&
2713 (newNameItersIt == newNameItersEnd ||
2714 (*oldChildIt)->GetName() < **newNameItersIt)) {
2715 TF_DEBUG(USD_COMPOSITION).Msg("Removing <%s>\n",
2716 (*oldChildIt)->GetPath().GetText());
2717 _DestroyPrim(*oldChildIt++);
2718 }
2719
2720 // Walk through any matching children and preserve them.
2721 for (; newNameItersIt != newNameItersEnd &&
2722 oldChildIt != oldChildEnd &&
2723 **newNameItersIt == (*oldChildIt)->GetName();
2724 ++newNameItersIt, ++oldChildIt) {
2725 TF_DEBUG(USD_COMPOSITION).Msg("Preserving <%s>\n",
2726 (*oldChildIt)->GetPath().GetText());
2727 tempChildren.push_back(make_pair(*oldChildIt, *newNameItersIt));
2728 if (recurse) {
2729 Usd_PrimDataPtr child = tempChildren.back().first;
2730 _ComposeChildSubtree(child, prim, mask);
2731 }
2732 }
2733
2734 // Walk newly-added names up to the next old name, adding them.
2735 for (; newNameItersIt != newNameItersEnd &&
2736 (oldChildIt == oldChildEnd ||
2737 **newNameItersIt < (*oldChildIt)->GetName());
2738 ++newNameItersIt) {
2739 SdfPath newChildPath = parentPath.AppendChild(**newNameItersIt);
2740 TF_DEBUG(USD_COMPOSITION).Msg("Creating new <%s>\n",
2741 newChildPath.GetText());
2742 tempChildren.push_back(
2743 make_pair(_InstantiatePrim(newChildPath), *newNameItersIt));
2744 if (recurse) {
2745 Usd_PrimDataPtr child = tempChildren.back().first;
2746 _ComposeChildSubtree(child, prim, mask);
2747 }
2748 }
2749 }
2750
2751 // tempChildren should never be empty at this point. If it were, it means
2752 // that the above loop would have only deleted existing children, but that
2753 // case is covered by optimization 4 above.
2754 if (!TF_VERIFY(!tempChildren.empty())) {
2755 return;
2756 }
2757
2758 // Now all the new children are in lexicographical order by name, paired
2759 // with their name's iterator in the original name order. Recover the
2760 // original order by sorting by the iterators natural order.
2761 sort(tempChildren.begin(), tempChildren.end(), _SecondLess());
2762
2763 // Now all the new children are correctly ordered. Set the
2764 // sibling and parent links to add them to the prim's children.
2765 for (size_t i = 0, e = tempChildren.size() - 1; i < e; ++i) {
2766 tempChildren[i].first->_SetSiblingLink(tempChildren[i+1].first);
2767 }
2768 tempChildren.back().first->_SetParentLink(prim);
2769
2770 if (cur == begin) {
2771 prim->_firstChild = tempChildren.front().first;
2772 }
2773 else {
2774 Usd_PrimDataSiblingIterator lastChild = begin, next = begin;
2775 for (++next; next != cur; lastChild = next, ++next) { }
2776 (*lastChild)->_SetSiblingLink(tempChildren.front().first);
2777 }
2778 }
2779
2780 void
_ComposeChildSubtree(Usd_PrimDataPtr prim,Usd_PrimDataConstPtr parent,UsdStagePopulationMask const * mask)2781 UsdStage::_ComposeChildSubtree(Usd_PrimDataPtr prim,
2782 Usd_PrimDataConstPtr parent,
2783 UsdStagePopulationMask const *mask)
2784 {
2785 if (parent->IsInPrototype()) {
2786 // If this UsdPrim is a child of an instance prototype, its
2787 // source prim index won't be at the same path as its stage path.
2788 // We need to construct the path from the parent's source index.
2789 const SdfPath sourcePrimIndexPath =
2790 parent->GetSourcePrimIndex().GetPath().AppendChild(prim->GetName());
2791 _ComposeSubtree(prim, parent, mask, sourcePrimIndexPath);
2792 }
2793 else {
2794 _ComposeSubtree(prim, parent, mask);
2795 }
2796 }
2797
2798 void
_ReportPcpErrors(const PcpErrorVector & errors,const std::string & context) const2799 UsdStage::_ReportPcpErrors(const PcpErrorVector &errors,
2800 const std::string &context) const
2801 {
2802 _ReportErrors(errors, std::vector<std::string>(), context);
2803 }
2804
2805 // Report any errors. It's important for error filtering that each
2806 // error be a single line. It's equally important that we provide
2807 // some clue to associating the errors to the originating stage
2808 // (it is caller's responsibility to ensure that any further required
2809 // context (e.g. prim path) be present in 'context' already). We choose
2810 // a balance between total specificity (which would require identifying
2811 // both the session layer and ArResolverContext and be very long)
2812 // and brevity. We can modulate this behavior with TfDebug if needed.
2813 // Finally, we use a mutex to ensure there is no interleaving of errors
2814 // from multiple threads.
2815 void
_ReportErrors(const PcpErrorVector & errors,const std::vector<std::string> & otherErrors,const std::string & context) const2816 UsdStage::_ReportErrors(const PcpErrorVector &errors,
2817 const std::vector<std::string> &otherErrors,
2818 const std::string &context) const
2819 {
2820 static std::mutex errMutex;
2821
2822 if (!errors.empty() || !otherErrors.empty()) {
2823 std::string fullContext = TfStringPrintf("(%s on stage @%s@ <%p>)",
2824 context.c_str(),
2825 GetRootLayer()->GetIdentifier().c_str(),
2826 this);
2827 std::vector<std::string> allErrors;
2828 allErrors.reserve(errors.size() + otherErrors.size());
2829
2830 for (const auto& err : errors) {
2831 allErrors.push_back(TfStringPrintf("%s %s",
2832 err->ToString().c_str(),
2833 fullContext.c_str()));
2834 }
2835 for (const auto& err : otherErrors) {
2836 allErrors.push_back(TfStringPrintf("%s %s",
2837 err.c_str(),
2838 fullContext.c_str()));
2839 }
2840
2841 {
2842 std::lock_guard<std::mutex> lock(errMutex);
2843
2844 for (const auto &err : allErrors){
2845 TF_WARN(err);
2846 }
2847 }
2848 }
2849 }
2850
2851 // Static prim type info cache
2852 static Usd_PrimTypeInfoCache &
_GetPrimTypeInfoCache()2853 _GetPrimTypeInfoCache()
2854 {
2855 static Usd_PrimTypeInfoCache cache;
2856 return cache;
2857 }
2858
2859 // Iterate over a prim's specs until we get a non-empty, non-any-type typeName.
2860 static TfToken
_ComposeTypeName(const PcpPrimIndex * primIndex)2861 _ComposeTypeName(const PcpPrimIndex *primIndex)
2862 {
2863 for (Usd_Resolver res(primIndex); res.IsValid(); res.NextLayer()) {
2864 TfToken tok;
2865 if (res.GetLayer()->HasField(
2866 res.GetLocalPath(), SdfFieldKeys->TypeName, &tok)) {
2867 if (!tok.IsEmpty() && tok != SdfTokens->AnyTypeToken)
2868 return tok;
2869 }
2870 }
2871 return TfToken();
2872 }
2873
2874 static void
_ComposeAuthoredAppliedSchemas(const PcpPrimIndex * primIndex,TfTokenVector * schemas)2875 _ComposeAuthoredAppliedSchemas(
2876 const PcpPrimIndex *primIndex, TfTokenVector *schemas)
2877 {
2878 // Collect all list op opinions for the API schemas field from strongest to
2879 // weakest. Then we apply them from weakest to strongest.
2880 std::vector<SdfTokenListOp> listOps;
2881
2882 SdfTokenListOp listOp;
2883 for (Usd_Resolver res(primIndex); res.IsValid(); res.NextLayer()) {
2884 if (res.GetLayer()->HasField(
2885 res.GetLocalPath(), UsdTokens->apiSchemas, &listOp)) {
2886 // Add the populated list op to the end of the list.
2887 listOps.emplace_back();
2888 listOps.back().Swap(listOp);
2889 // An explicit list op overwrites anything weaker so we can just
2890 // stop here if it's explicit.
2891 if (listOps.back().IsExplicit()) {
2892 break;
2893 }
2894 }
2895 }
2896
2897 // Apply the listops to our output in reverse order (weakest to strongest).
2898 std::for_each(listOps.crbegin(), listOps.crend(),
2899 [&schemas](const SdfTokenListOp& op) { op.ApplyOperations(schemas); });
2900 }
2901
2902 void
_ComposeSubtreeInParallel(Usd_PrimDataPtr prim)2903 UsdStage::_ComposeSubtreeInParallel(Usd_PrimDataPtr prim)
2904 {
2905 _ComposeSubtreesInParallel(vector<Usd_PrimDataPtr>(1, prim));
2906 }
2907
2908 void
_ComposeSubtreesInParallel(const vector<Usd_PrimDataPtr> & prims,const vector<SdfPath> * primIndexPaths)2909 UsdStage::_ComposeSubtreesInParallel(
2910 const vector<Usd_PrimDataPtr> &prims,
2911 const vector<SdfPath> *primIndexPaths)
2912 {
2913 TF_PY_ALLOW_THREADS_IN_SCOPE();
2914
2915 // TF_DEBUG(USD_COMPOSITION).Msg("Composing Subtrees at: %s\n",
2916 // TfStringify(
2917 // [&prims]() {
2918 // vector<SdfPath> paths;
2919 // for (auto p : prims) { paths.push_back(p->GetPath()); }
2920 // return paths;
2921 // }()).c_str());
2922
2923 TRACE_FUNCTION();
2924
2925 // Begin a subtree composition in parallel.
2926 WorkWithScopedParallelism([this, &prims, &primIndexPaths]() {
2927 _primMapMutex = boost::in_place();
2928 _dispatcher = boost::in_place();
2929 // We populate the clip cache concurrently during composition, so we
2930 // need to enable concurrent population here.
2931 Usd_ClipCache::ConcurrentPopulationContext
2932 clipConcurrentPopContext(*_clipCache);
2933 try {
2934 for (size_t i = 0; i != prims.size(); ++i) {
2935 Usd_PrimDataPtr p = prims[i];
2936 _dispatcher->Run(
2937 &UsdStage::_ComposeSubtreeImpl, this, p, p->GetParent(),
2938 &_populationMask,
2939 primIndexPaths ? (*primIndexPaths)[i] : p->GetPath());
2940 }
2941 }
2942 catch (...) {
2943 _dispatcher = boost::none;
2944 _primMapMutex = boost::none;
2945 throw;
2946 }
2947
2948 _dispatcher = boost::none;
2949 _primMapMutex = boost::none;
2950 });
2951 }
2952
2953 void
_ComposeSubtree(Usd_PrimDataPtr prim,Usd_PrimDataConstPtr parent,UsdStagePopulationMask const * mask,const SdfPath & primIndexPath)2954 UsdStage::_ComposeSubtree(
2955 Usd_PrimDataPtr prim, Usd_PrimDataConstPtr parent,
2956 UsdStagePopulationMask const *mask,
2957 const SdfPath& primIndexPath)
2958 {
2959 if (_dispatcher) {
2960 _dispatcher->Run(
2961 &UsdStage::_ComposeSubtreeImpl, this,
2962 prim, parent, mask, primIndexPath);
2963 } else {
2964 // TF_DEBUG(USD_COMPOSITION).Msg("Composing Subtree at <%s>\n",
2965 // prim->GetPath().GetText());
2966 // TRACE_FUNCTION();
2967 _ComposeSubtreeImpl(prim, parent, mask, primIndexPath);
2968 }
2969 }
2970
2971 void
_ComposeSubtreeImpl(Usd_PrimDataPtr prim,Usd_PrimDataConstPtr parent,UsdStagePopulationMask const * mask,const SdfPath & inPrimIndexPath)2972 UsdStage::_ComposeSubtreeImpl(
2973 Usd_PrimDataPtr prim, Usd_PrimDataConstPtr parent,
2974 UsdStagePopulationMask const *mask,
2975 const SdfPath& inPrimIndexPath)
2976 {
2977 TfAutoMallocTag2 tag("Usd", _mallocTagID);
2978
2979 const SdfPath primIndexPath =
2980 (inPrimIndexPath.IsEmpty() ? prim->GetPath() : inPrimIndexPath);
2981
2982 // Find the prim's PcpPrimIndex. This should have already been
2983 // computed in a prior call to _ComposePrimIndexesInParallel.
2984 // Note that it's unsafe to call PcpCache::ComputePrimIndex here,
2985 // that method is not thread-safe unless the prim index happens
2986 // to have been computed already.
2987 prim->_primIndex = _GetPcpCache()->FindPrimIndex(primIndexPath);
2988 if (!TF_VERIFY(
2989 prim->_primIndex,
2990 "Prim index at <%s> not found in PcpCache for UsdStage %s",
2991 primIndexPath.GetText(), UsdDescribe(this).c_str())) {
2992 return;
2993 }
2994
2995 parent = parent ? parent : prim->GetParent();
2996
2997 // If this prim's parent is the pseudo-root and it has a different
2998 // path from its source prim index, it must represent a prototype prim.
2999 const bool isPrototypePrim =
3000 (parent == _pseudoRoot
3001 && prim->_primIndex->GetPath() != prim->GetPath());
3002
3003 if (parent && !isPrototypePrim) {
3004 // Compose the type info full type ID for the prim which includes
3005 // the type name, applied schemas, and a possible mapped fallback type
3006 // if the stage specifies it.
3007 Usd_PrimTypeInfoCache::TypeId typeId(
3008 _ComposeTypeName(prim->_primIndex));
3009 _ComposeAuthoredAppliedSchemas(
3010 prim->_primIndex, &typeId.appliedAPISchemas);
3011 if (const TfToken *fallbackType = TfMapLookupPtr(
3012 _invalidPrimTypeToFallbackMap, typeId.primTypeName)) {
3013 typeId.mappedTypeName = *fallbackType;
3014 }
3015
3016 // Ask the type info cache for the type info for our type.
3017 prim->_primTypeInfo =
3018 _GetPrimTypeInfoCache().FindOrCreatePrimTypeInfo(std::move(typeId));
3019 } else {
3020 prim->_primTypeInfo = _GetPrimTypeInfoCache().GetEmptyPrimTypeInfo();
3021 }
3022
3023 // Compose type info and flags for prim.
3024 prim->_ComposeAndCacheFlags(parent, isPrototypePrim);
3025
3026 // Pre-compute clip information for this prim to avoid doing so
3027 // at value resolution time.
3028 if (prim->GetPath() != SdfPath::AbsoluteRootPath()) {
3029 bool primHasAuthoredClips = _clipCache->PopulateClipsForPrim(
3030 prim->GetPath(), prim->GetPrimIndex());
3031 prim->_SetMayHaveOpinionsInClips(
3032 primHasAuthoredClips || parent->MayHaveOpinionsInClips());
3033 } else {
3034 // When composing the pseudoroot we also determine any fallback type
3035 // mappings that the stage defines for type names that don't have a
3036 // valid schema. The possible mappings are defined in the root layer
3037 // metadata and are needed to compose type info for all the other prims,
3038 // thus why we do this here.
3039 _invalidPrimTypeToFallbackMap.clear();
3040 VtDictionary fallbackPrimTypes;
3041 if (GetMetadata(UsdTokens->fallbackPrimTypes, &fallbackPrimTypes)) {
3042 _GetPrimTypeInfoCache().ComputeInvalidPrimTypeToFallbackMap(
3043 fallbackPrimTypes, &_invalidPrimTypeToFallbackMap);
3044 }
3045 }
3046
3047 // Compose the set of children on this prim.
3048 _ComposeChildren(prim, mask, /*recurse=*/true);
3049 }
3050
3051 void
_DestroyDescendents(Usd_PrimDataPtr prim)3052 UsdStage::_DestroyDescendents(Usd_PrimDataPtr prim)
3053 {
3054 // Recurse to children first.
3055 Usd_PrimDataSiblingIterator
3056 childIt = prim->_ChildrenBegin(), childEnd = prim->_ChildrenEnd();
3057 prim->_firstChild = nullptr;
3058 while (childIt != childEnd) {
3059 if (_dispatcher) {
3060 _dispatcher->Run(&UsdStage::_DestroyPrim, this, *childIt++);
3061 } else {
3062 _DestroyPrim(*childIt++);
3063 }
3064 }
3065 }
3066
3067 void
_DestroyPrimsInParallel(const vector<SdfPath> & paths)3068 UsdStage::_DestroyPrimsInParallel(const vector<SdfPath>& paths)
3069 {
3070 TF_PY_ALLOW_THREADS_IN_SCOPE();
3071
3072 TRACE_FUNCTION();
3073
3074 TF_AXIOM(!_dispatcher && !_primMapMutex);
3075
3076 WorkWithScopedParallelism([&]() {
3077 _primMapMutex = boost::in_place();
3078 _dispatcher = boost::in_place();
3079 for (const auto& path : paths) {
3080 Usd_PrimDataPtr prim = _GetPrimDataAtPath(path);
3081 // We *expect* every prim in paths to be valid as we iterate,
3082 // but at one time had issues with deactivated prototype prims,
3083 // so we preserve a guard for resiliency. See
3084 // testUsdBug141491.py
3085 if (TF_VERIFY(prim)) {
3086 _dispatcher->Run(&UsdStage::_DestroyPrim, this, prim);
3087 }
3088 }
3089 _dispatcher = boost::none;
3090 _primMapMutex = boost::none;
3091 });
3092 }
3093
3094 void
_DestroyPrim(Usd_PrimDataPtr prim)3095 UsdStage::_DestroyPrim(Usd_PrimDataPtr prim)
3096 {
3097 TF_DEBUG(USD_COMPOSITION).Msg(
3098 "Destroying <%s>\n", prim->GetPath().GetText());
3099
3100 // Destroy descendents first.
3101 _DestroyDescendents(prim);
3102
3103 // Set the prim's dead bit.
3104 prim->_MarkDead();
3105
3106 // Remove from the map -- this prim should always be present.
3107
3108 // XXX: We intentionally copy the prim's path to the local variable primPath
3109 // here. If we don't, the erase() call ends up reading freed memory. This
3110 // is because libstdc++'s hash_map's backing implementation implements
3111 // erase() as: find the first element with a matching key, erase it, then
3112 // walk subsequent bucket elements with matching keys and erase them. This
3113 // might seem odd for hash_map since only one element can have the given
3114 // key, but it works this way since the backing implementation is shared
3115 // between hash_map and hash_multimap.
3116 //
3117 // This is a problem since prim->GetPath() returns a const reference to a
3118 // member variable, so once the first element (and only element) is erased
3119 // the reference is invalidated, but erase() may look at the path reference
3120 // again to do the key comparison for subsequent elements. Copying the path
3121 // out to a local variable ensures it stays alive for the duration of
3122 // erase().
3123 //
3124 // NOTE: The above was true in gcc 4.4 but not in gcc 4.8, nor is it
3125 // true in boost::unordered_map or std::unordered_map.
3126 if (!_isClosingStage) {
3127 SdfPath primPath = prim->GetPath();
3128 tbb::spin_rw_mutex::scoped_lock lock;
3129 const bool hasMutex = static_cast<bool>(_primMapMutex);
3130 if (hasMutex)
3131 lock.acquire(*_primMapMutex);
3132 bool erased = _primMap.erase(primPath);
3133 if (hasMutex)
3134 lock.release();
3135 TF_VERIFY(erased,
3136 "Destroyed prim <%s> not present in stage's data structures",
3137 prim->GetPath().GetString().c_str());
3138 }
3139 }
3140
3141 void
Reload()3142 UsdStage::Reload()
3143 {
3144 TfAutoMallocTag2 tag("Usd", _mallocTagID);
3145
3146 // This UsdStage may receive layer change notices due to layers being
3147 // reloaded below. However, we won't receive that notice for any layers
3148 // that we failed to load previously but are now loadable. For example,
3149 // if a prim had a reference to a non-existent layer, but then that
3150 // layer was created, the only indication of that would be a prim resync
3151 // in the PcpChanges object returned by Reload.
3152 //
3153 // We want to combine the stage changes from processing the layer changes
3154 // with the stage changes indicated in the PcpChanges returned by Reload
3155 // so that this stage only goes through one round of change processing
3156 // and notification. So, we create a _PendingChanges object that will
3157 // be filled in by _HandleLayersDidChange and the call to Reload, then
3158 // process all of that information in _ProcessPendingChanges().
3159 _PendingChanges localPendingChanges;
3160 _pendingChanges = &localPendingChanges;
3161
3162 ArResolverScopedCache resolverCache;
3163
3164 #if AR_VERSION > 1
3165 // Refresh the resolver to pick up changes that might have
3166 // affected asset resolution.
3167 ArGetResolver().RefreshContext(GetPathResolverContext());
3168 #endif
3169
3170 // Reload layers in a change block to batch together change notices.
3171 {
3172 SdfChangeBlock block;
3173
3174 // Reload layers that are reached via composition.
3175 PcpChanges& changes = _pendingChanges->pcpChanges;
3176 _cache->Reload(&changes);
3177
3178 // Reload all clip layers that are opened.
3179 _clipCache->Reload();
3180 }
3181
3182 // Process changes if they haven't already been processed in response
3183 // to layer change notices above. If they have already been processed,
3184 // _pendingChanges would have been reset to NULL.
3185 if (_pendingChanges == &localPendingChanges) {
3186 _ProcessPendingChanges();
3187 }
3188 }
3189
3190 /*static*/
3191 bool
IsSupportedFile(const std::string & filePath)3192 UsdStage::IsSupportedFile(const std::string& filePath)
3193 {
3194 if (filePath.empty()) {
3195 TF_CODING_ERROR("Empty file path given");
3196 return false;
3197 }
3198
3199 // grab the file's extension, and assert it to be valid
3200 std::string fileExtension = SdfFileFormat::GetFileExtension(filePath);
3201 if (fileExtension.empty()) {
3202 return false;
3203 }
3204
3205 // if the extension is valid we'll get a non null FileFormatPtr
3206 return SdfFileFormat::FindByExtension(fileExtension,
3207 UsdUsdFileFormatTokens->Target);
3208 }
3209
3210 namespace {
3211
_SaveLayers(const SdfLayerHandleVector & layers)3212 void _SaveLayers(const SdfLayerHandleVector& layers)
3213 {
3214 for (const SdfLayerHandle& layer : layers) {
3215 if (!layer->IsDirty()) {
3216 continue;
3217 }
3218
3219 if (layer->IsAnonymous()) {
3220 TF_WARN("Not saving @%s@ because it is an anonymous layer",
3221 layer->GetIdentifier().c_str());
3222 continue;
3223 }
3224
3225 // Sdf will emit errors if there are any problems with
3226 // saving the layer.
3227 layer->Save();
3228 }
3229 }
3230
3231 }
3232
3233 void
Save()3234 UsdStage::Save()
3235 {
3236 SdfLayerHandleVector layers = GetUsedLayers();
3237
3238 const PcpLayerStackPtr localLayerStack = _GetPcpCache()->GetLayerStack();
3239 if (TF_VERIFY(localLayerStack)) {
3240 const SdfLayerHandleVector sessionLayers =
3241 localLayerStack->GetSessionLayers();
3242 const auto isSessionLayer =
3243 [&sessionLayers](const SdfLayerHandle& l) {
3244 return std::find(
3245 sessionLayers.begin(), sessionLayers.end(), l)
3246 != sessionLayers.end();
3247 };
3248
3249 layers.erase(std::remove_if(layers.begin(), layers.end(),
3250 isSessionLayer),
3251 layers.end());
3252 }
3253
3254 _SaveLayers(layers);
3255 }
3256
3257 void
SaveSessionLayers()3258 UsdStage::SaveSessionLayers()
3259 {
3260 const PcpLayerStackPtr localLayerStack = _GetPcpCache()->GetLayerStack();
3261 if (TF_VERIFY(localLayerStack)) {
3262 _SaveLayers(localLayerStack->GetSessionLayers());
3263 }
3264 }
3265
3266 void
WriteFallbackPrimTypes()3267 UsdStage::WriteFallbackPrimTypes()
3268 {
3269 // Mark that we're writing the fallback prim types from the schema registry
3270 // so that we can ignore changes to the fallbackPrimTypes metadata if we
3271 // end up writing it below. Otherwise we could end up rebuilding the entire
3272 // stage unnecessarily when this particular data shouldn't change any of
3273 // the prims' composition.
3274 TfScopedVar<bool> resetIsWriting(_isWritingFallbackPrimTypes, true);
3275
3276 // Any fallback types for schema prim types will be defined in the schemas
3277 // themselves. The schema registry provides the fallback prim type
3278 // dictionary for us to write in the metadata
3279 const VtDictionary &schemaFallbackTypes =
3280 UsdSchemaRegistry::GetInstance().GetFallbackPrimTypes();
3281 if (!schemaFallbackTypes.empty()) {
3282 // The stage may already have metadata for fallback prim types, written
3283 // from this version of Usd, a different version of Usd, or possibly
3284 // direct user authoring of the metadata. We don't overwrite any
3285 // existing fallbacks; we only add entries for the types that don't have
3286 // fallbacks defined in the metadata yet.
3287 VtDictionary existingFallbackTypes;
3288 if (GetMetadata(UsdTokens->fallbackPrimTypes, &existingFallbackTypes)) {
3289 VtDictionaryOver(&existingFallbackTypes, schemaFallbackTypes);
3290 SetMetadata(UsdTokens->fallbackPrimTypes, existingFallbackTypes);
3291 } else {
3292 SetMetadata(UsdTokens->fallbackPrimTypes, schemaFallbackTypes);
3293 }
3294 }
3295 }
3296
3297 std::pair<bool, UsdPrim>
_IsValidPathForCreatingPrim(const SdfPath & path) const3298 UsdStage::_IsValidPathForCreatingPrim(const SdfPath &path) const
3299 {
3300 std::pair<bool, UsdPrim> status = { false, UsdPrim() };
3301
3302 // Path must be absolute.
3303 if (ARCH_UNLIKELY(!path.IsAbsolutePath())) {
3304 TF_CODING_ERROR("Path must be an absolute path: <%s>", path.GetText());
3305 return status;
3306 }
3307
3308 // Path must be a prim path (or the absolute root path).
3309 if (ARCH_UNLIKELY(!path.IsAbsoluteRootOrPrimPath())) {
3310 TF_CODING_ERROR("Path must be a prim path: <%s>", path.GetText());
3311 return status;
3312 }
3313
3314 // Path must not contain variant selections.
3315 if (ARCH_UNLIKELY(path.ContainsPrimVariantSelection())) {
3316 TF_CODING_ERROR("Path must not contain variant selections: <%s>",
3317 path.GetText());
3318 return status;
3319 }
3320
3321 const UsdPrim prim = GetPrimAtPath(path);
3322 if (ARCH_UNLIKELY(prim ? !_ValidateEditPrim(prim, "create prim")
3323 : !_ValidateEditPrimAtPath(path, "create prim"))) {
3324 return status;
3325 }
3326
3327 status = { true, prim };
3328 return status;
3329 }
3330
3331 UsdPrim
OverridePrim(const SdfPath & path)3332 UsdStage::OverridePrim(const SdfPath &path)
3333 {
3334 // Special-case requests for the root. It always succeeds and never does
3335 // authoring since the root cannot have PrimSpecs.
3336 if (path == SdfPath::AbsoluteRootPath())
3337 return GetPseudoRoot();
3338
3339 // Validate path input.
3340 std::pair<bool, UsdPrim> status = _IsValidPathForCreatingPrim(path);
3341 if (!status.first) {
3342 return UsdPrim();
3343 }
3344
3345 // Do the authoring, if any to do.
3346 if (!status.second) {
3347 {
3348 SdfChangeBlock block;
3349 TfErrorMark m;
3350 SdfPrimSpecHandle primSpec =
3351 _CreatePrimSpecAtEditTarget(GetEditTarget(), path);
3352 // If spec creation failed, return. Issue an error if a more
3353 // specific error wasn't already issued.
3354 if (!primSpec) {
3355 if (m.IsClean())
3356 TF_RUNTIME_ERROR("Failed to create PrimSpec for <%s>",
3357 path.GetText());
3358 return UsdPrim();
3359 }
3360 }
3361
3362 // Attempt to fetch the prim we tried to create.
3363 status.second = GetPrimAtPath(path);
3364 }
3365
3366 return status.second;
3367 }
3368
3369 UsdPrim
DefinePrim(const SdfPath & path,const TfToken & typeName)3370 UsdStage::DefinePrim(const SdfPath &path,
3371 const TfToken &typeName)
3372 {
3373 // Validate path input.
3374 if (!_IsValidPathForCreatingPrim(path).first)
3375 return UsdPrim();
3376
3377 return _DefinePrim(path, typeName);
3378 }
3379
3380 UsdPrim
_DefinePrim(const SdfPath & path,const TfToken & typeName)3381 UsdStage::_DefinePrim(const SdfPath &path, const TfToken &typeName)
3382 {
3383 // Special-case requests for the root. It always succeeds and never does
3384 // authoring since the root cannot have PrimSpecs.
3385 if (path == SdfPath::AbsoluteRootPath())
3386 return GetPseudoRoot();
3387
3388 // Define all ancestors.
3389 if (!_DefinePrim(path.GetParentPath(), TfToken()))
3390 return UsdPrim();
3391
3392 // Now author scene description for this prim.
3393 TfErrorMark m;
3394 UsdPrim prim = GetPrimAtPath(path);
3395 if (!prim || !prim.IsDefined() ||
3396 (!typeName.IsEmpty() && prim.GetTypeName() != typeName)) {
3397 {
3398 SdfChangeBlock block;
3399 SdfPrimSpecHandle primSpec =
3400 _CreatePrimSpecAtEditTarget(GetEditTarget(), path);
3401 // If spec creation failed, return. Issue an error if a more
3402 // specific error wasn't already issued.
3403 if (!primSpec) {
3404 if (m.IsClean())
3405 TF_RUNTIME_ERROR(
3406 "Failed to create primSpec for <%s>", path.GetText());
3407 return UsdPrim();
3408 }
3409
3410 // Set specifier and typeName, if not empty.
3411 primSpec->SetSpecifier(SdfSpecifierDef);
3412 if (!typeName.IsEmpty())
3413 primSpec->SetTypeName(typeName);
3414 }
3415 // Fetch prim if newly created.
3416 prim = prim ? prim : GetPrimAtPath(path);
3417 }
3418
3419 // Issue an error if we were unable to define this prim and an error isn't
3420 // already issued.
3421 if ((!prim || !prim.IsDefined()) && m.IsClean())
3422 TF_RUNTIME_ERROR("Failed to define UsdPrim <%s>", path.GetText());
3423
3424 return prim;
3425 }
3426
3427 UsdPrim
CreateClassPrim(const SdfPath & path)3428 UsdStage::CreateClassPrim(const SdfPath &path)
3429 {
3430 // Classes must be created in local layers.
3431 if (_editTarget.GetMapFunction().IsIdentity() &&
3432 !HasLocalLayer(_editTarget.GetLayer())) {
3433 TF_CODING_ERROR("Must create classes in local LayerStack");
3434 return UsdPrim();
3435 }
3436
3437 // Validate path input.
3438 const std::pair<bool, UsdPrim> status = _IsValidPathForCreatingPrim(path);
3439 if (!status.first) {
3440 return UsdPrim();
3441 }
3442
3443 UsdPrim prim = status.second;
3444
3445 // It's an error to try to transform a defined non-class into a class.
3446 if (prim && prim.IsDefined() &&
3447 prim.GetSpecifier() != SdfSpecifierClass) {
3448 TF_RUNTIME_ERROR("Non-class prim already exists at <%s>",
3449 path.GetText());
3450 return UsdPrim();
3451 }
3452
3453 // Stamp a class PrimSpec if need-be.
3454 if (!prim || !prim.IsAbstract()) {
3455 prim = _DefinePrim(path, TfToken());
3456 if (prim)
3457 prim.SetMetadata(SdfFieldKeys->Specifier, SdfSpecifierClass);
3458 }
3459 return prim;
3460 }
3461
3462 bool
RemovePrim(const SdfPath & path)3463 UsdStage::RemovePrim(const SdfPath& path)
3464 {
3465 return _RemovePrim(path);
3466 }
3467
3468 const UsdEditTarget &
GetEditTarget() const3469 UsdStage::GetEditTarget() const
3470 {
3471 return _editTarget;
3472 }
3473
3474 UsdEditTarget
GetEditTargetForLocalLayer(size_t i)3475 UsdStage::GetEditTargetForLocalLayer(size_t i)
3476 {
3477 const SdfLayerRefPtrVector & layers = _cache->GetLayerStack()->GetLayers();
3478 if (i >= layers.size()) {
3479 TF_CODING_ERROR("Layer index %zu is out of range: only %zu entries in "
3480 "layer stack", i, layers.size());
3481 return UsdEditTarget();
3482 }
3483 const SdfLayerOffset *layerOffset =
3484 _cache->GetLayerStack()->GetLayerOffsetForLayer(i);
3485 return UsdEditTarget(layers[i],
3486 layerOffset ? *layerOffset : SdfLayerOffset() );
3487 }
3488
3489 UsdEditTarget
GetEditTargetForLocalLayer(const SdfLayerHandle & layer)3490 UsdStage::GetEditTargetForLocalLayer(const SdfLayerHandle &layer)
3491 {
3492 const SdfLayerOffset *layerOffset =
3493 _cache->GetLayerStack()->GetLayerOffsetForLayer(layer);
3494 return UsdEditTarget(layer, layerOffset ? *layerOffset : SdfLayerOffset() );
3495 }
3496
3497 bool
HasLocalLayer(const SdfLayerHandle & layer) const3498 UsdStage::HasLocalLayer(const SdfLayerHandle &layer) const
3499 {
3500 return _cache->GetLayerStack()->HasLayer(layer);
3501 }
3502
3503 void
SetEditTarget(const UsdEditTarget & editTarget)3504 UsdStage::SetEditTarget(const UsdEditTarget &editTarget)
3505 {
3506 if (!editTarget.IsValid()){
3507 TF_CODING_ERROR("Attempt to set an invalid UsdEditTarget as current");
3508 return;
3509 }
3510 // Do some extra error checking if the EditTarget specifies a local layer.
3511 if (editTarget.GetMapFunction().IsIdentity() &&
3512 !HasLocalLayer(editTarget.GetLayer())) {
3513 TF_CODING_ERROR("Layer @%s@ is not in the local LayerStack rooted "
3514 "at @%s@",
3515 editTarget.GetLayer()->GetIdentifier().c_str(),
3516 GetRootLayer()->GetIdentifier().c_str());
3517 return;
3518 }
3519
3520 // If different from current, set EditTarget and notify.
3521 if (editTarget != _editTarget) {
3522 _editTarget = editTarget;
3523 UsdStageWeakPtr self(this);
3524 UsdNotice::StageEditTargetChanged(self).Send(self);
3525 }
3526 }
3527
3528 SdfLayerHandle
GetRootLayer() const3529 UsdStage::GetRootLayer() const
3530 {
3531 return _rootLayer;
3532 }
3533
3534 ArResolverContext
GetPathResolverContext() const3535 UsdStage::GetPathResolverContext() const
3536 {
3537 if (!TF_VERIFY(_GetPcpCache())) {
3538 static ArResolverContext empty;
3539 return empty;
3540 }
3541 return _GetPcpCache()->GetLayerStackIdentifier().pathResolverContext;
3542 }
3543
3544 SdfLayerHandleVector
GetLayerStack(bool includeSessionLayers) const3545 UsdStage::GetLayerStack(bool includeSessionLayers) const
3546 {
3547 SdfLayerHandleVector result;
3548
3549 // Pcp's API lets us get either the whole stack or just the session layer
3550 // stack. We get the whole stack and either copy the whole thing to Handles
3551 // or only the portion starting at the root layer to the end.
3552
3553 if (PcpLayerStackPtr layerStack = _cache->GetLayerStack()) {
3554 const SdfLayerRefPtrVector &layers = layerStack->GetLayers();
3555
3556 // Copy everything if sublayers requested, otherwise copy from the root
3557 // layer to the end.
3558 SdfLayerRefPtrVector::const_iterator copyBegin =
3559 includeSessionLayers ? layers.begin() :
3560 find(layers.begin(), layers.end(), GetRootLayer());
3561
3562 TF_VERIFY(copyBegin != layers.end(),
3563 "Root layer @%s@ not in LayerStack",
3564 GetRootLayer()->GetIdentifier().c_str());
3565
3566 result.assign(copyBegin, layers.end());
3567 }
3568
3569 return result;
3570 }
3571
3572 SdfLayerHandleVector
GetUsedLayers(bool includeClipLayers) const3573 UsdStage::GetUsedLayers(bool includeClipLayers) const
3574 {
3575 if (!_cache)
3576 return SdfLayerHandleVector();
3577
3578 SdfLayerHandleSet usedLayers = _cache->GetUsedLayers();
3579
3580 if (includeClipLayers && _clipCache){
3581 SdfLayerHandleSet clipLayers = _clipCache->GetUsedLayers();
3582 if (!clipLayers.empty()){
3583 usedLayers.insert(clipLayers.begin(), clipLayers.end());
3584 }
3585 }
3586
3587 return SdfLayerHandleVector(usedLayers.begin(), usedLayers.end());
3588 }
3589
3590
3591 SdfLayerHandle
GetSessionLayer() const3592 UsdStage::GetSessionLayer() const
3593 {
3594 return _sessionLayer;
3595 }
3596
3597 void
MuteLayer(const std::string & layerIdentifier)3598 UsdStage::MuteLayer(const std::string &layerIdentifier)
3599 {
3600 MuteAndUnmuteLayers({layerIdentifier}, {});
3601 }
3602
3603 void
UnmuteLayer(const std::string & layerIdentifier)3604 UsdStage::UnmuteLayer(const std::string &layerIdentifier)
3605 {
3606 MuteAndUnmuteLayers({}, {layerIdentifier});
3607 }
3608
3609 void
MuteAndUnmuteLayers(const std::vector<std::string> & muteLayers,const std::vector<std::string> & unmuteLayers)3610 UsdStage::MuteAndUnmuteLayers(const std::vector<std::string> &muteLayers,
3611 const std::vector<std::string> &unmuteLayers)
3612 {
3613 TfAutoMallocTag2 tag("Usd", _mallocTagID);
3614
3615 PcpChanges changes;
3616 std::vector<std::string> newMutedLayers, newUnMutedLayers;
3617 _cache->RequestLayerMuting(muteLayers, unmuteLayers, &changes,
3618 &newMutedLayers, &newUnMutedLayers);
3619
3620 UsdStageWeakPtr self(this);
3621
3622 // Notify for layer muting/unmuting
3623 if (!newMutedLayers.empty() || !newUnMutedLayers.empty()) {
3624 UsdNotice::LayerMutingChanged(self, newMutedLayers, newUnMutedLayers)
3625 .Send(self);
3626 }
3627
3628 if (changes.IsEmpty()) {
3629 return;
3630 }
3631
3632 using _PathsToChangesMap = UsdNotice::ObjectsChanged::_PathsToChangesMap;
3633 _PathsToChangesMap resyncChanges, infoChanges;
3634 _Recompose(changes, &resyncChanges);
3635
3636 UsdNotice::ObjectsChanged(self, &resyncChanges, &infoChanges)
3637 .Send(self);
3638 UsdNotice::StageContentsChanged(self).Send(self);
3639 }
3640
3641 const std::vector<std::string>&
GetMutedLayers() const3642 UsdStage::GetMutedLayers() const
3643 {
3644 return _cache->GetMutedLayers();
3645 }
3646
3647 bool
IsLayerMuted(const std::string & layerIdentifier) const3648 UsdStage::IsLayerMuted(const std::string& layerIdentifier) const
3649 {
3650 return _cache->IsLayerMuted(layerIdentifier);
3651 }
3652
3653 UsdPrimRange
Traverse()3654 UsdStage::Traverse()
3655 {
3656 return UsdPrimRange::Stage(UsdStagePtr(this));
3657 }
3658
3659 UsdPrimRange
Traverse(const Usd_PrimFlagsPredicate & predicate)3660 UsdStage::Traverse(const Usd_PrimFlagsPredicate &predicate)
3661 {
3662 return UsdPrimRange::Stage(UsdStagePtr(this), predicate);
3663 }
3664
3665 UsdPrimRange
TraverseAll()3666 UsdStage::TraverseAll()
3667 {
3668 return UsdPrimRange::Stage(UsdStagePtr(this), UsdPrimAllPrimsPredicate);
3669 }
3670
3671 bool
_RemovePrim(const SdfPath & path)3672 UsdStage::_RemovePrim(const SdfPath& path)
3673 {
3674 SdfPrimSpecHandle spec = _GetPrimSpec(path);
3675 if (!spec) {
3676 return false;
3677 }
3678
3679 SdfPrimSpecHandle parent = spec->GetRealNameParent();
3680 if (!parent) {
3681 return false;
3682 }
3683
3684 return parent->RemoveNameChild(spec);
3685 }
3686
3687 bool
_RemoveProperty(const SdfPath & path)3688 UsdStage::_RemoveProperty(const SdfPath &path)
3689 {
3690 SdfPropertySpecHandle propHandle =
3691 GetEditTarget().GetPropertySpecForScenePath(path);
3692
3693 if (!propHandle) {
3694 return false;
3695 }
3696
3697 // dynamic cast needed because of protected copyctor
3698 // safe to assume a prim owner because we are in UsdPrim
3699 SdfPrimSpecHandle parent
3700 = TfDynamic_cast<SdfPrimSpecHandle>(propHandle->GetOwner());
3701
3702 if (!TF_VERIFY(parent, "Prop has no parent")) {
3703 return false;
3704 }
3705
3706 parent->RemoveProperty(propHandle);
3707 return true;
3708 }
3709
3710 template <class... Values>
3711 static void
_AddToChangedPaths(SdfPathVector * paths,const SdfPath & p,const Values &...data)3712 _AddToChangedPaths(SdfPathVector *paths, const SdfPath& p,
3713 const Values&... data)
3714 {
3715 paths->push_back(p);
3716 }
3717
3718 template <class ChangedPaths, class... Values>
3719 static void
_AddToChangedPaths(ChangedPaths * paths,const SdfPath & p,const Values &...data)3720 _AddToChangedPaths(ChangedPaths *paths, const SdfPath& p, const Values&... data)
3721 {
3722 (*paths)[p].emplace_back(data...);
3723 }
3724
3725 static std::string
_Stringify(const SdfPathVector & paths)3726 _Stringify(const SdfPathVector& paths)
3727 {
3728 return TfStringify(paths);
3729 }
3730
3731 template <class ChangedPaths>
3732 static std::string
_Stringify(const ChangedPaths & paths)3733 _Stringify(const ChangedPaths& paths)
3734 {
3735 return _Stringify(SdfPathVector(
3736 make_transform_iterator(paths.begin(), TfGet<0>()),
3737 make_transform_iterator(paths.end(), TfGet<0>())));
3738 }
3739
3740 // Add paths in the given cache that depend on the given path in the given
3741 // layer to changedPaths. If ChangedPaths is a map of paths to list of
3742 // objects, will construct an object using the given extraData
3743 // and append to the back of the list for each dependent path. If
3744 // ChangedPaths is a vector, each dependent path will be appended to
3745 // the vector and extraData is ignored.
3746 template <class ChangedPaths, class... ExtraData>
3747 static void
_AddAffectedStagePaths(const SdfLayerHandle & layer,const SdfPath & path,const PcpCache & cache,ChangedPaths * changedPaths,const ExtraData &...extraData)3748 _AddAffectedStagePaths(const SdfLayerHandle &layer, const SdfPath &path,
3749 const PcpCache &cache,
3750 ChangedPaths *changedPaths,
3751 const ExtraData&... extraData)
3752 {
3753 // We include virtual dependencies so that we can process
3754 // changes like adding missing defaultPrim metadata.
3755 const PcpDependencyFlags depTypes =
3756 PcpDependencyTypeDirect
3757 | PcpDependencyTypeAncestral
3758 | PcpDependencyTypeNonVirtual
3759 | PcpDependencyTypeVirtual;
3760
3761 // Do not filter dependencies against the indexes cached in PcpCache,
3762 // because Usd does not cache PcpPropertyIndex entries.
3763 const bool filterForExistingCachesOnly = false;
3764
3765 // If this site is in the cache's layerStack, we always add it here.
3766 // We do this instead of including PcpDependencyTypeRoot in depTypes
3767 // because we do not want to include root deps on those sites, just
3768 // the other kinds of inbound deps.
3769 if (cache.GetLayerStack()->HasLayer(layer)) {
3770 const SdfPath depPath = path.StripAllVariantSelections();
3771 _AddToChangedPaths(changedPaths, depPath, extraData...);
3772 }
3773
3774 for (const PcpDependency& dep:
3775 cache.FindSiteDependencies(layer, path, depTypes,
3776 /* recurseOnSite */ true,
3777 /* recurseOnIndex */ false,
3778 filterForExistingCachesOnly)) {
3779 _AddToChangedPaths(changedPaths, dep.indexPath, extraData...);
3780 }
3781
3782 TF_DEBUG(USD_CHANGES).Msg(
3783 "Adding paths that use <%s> in layer @%s@: %s\n",
3784 path.GetText(),
3785 layer->GetIdentifier().c_str(),
3786 _Stringify(*changedPaths).c_str());
3787 }
3788
3789 // Removes all elements from changedPaths whose paths are prefixed
3790 // by other elements.
3791 template <class ChangedPaths>
3792 static void
_RemoveDescendentEntries(ChangedPaths * changedPaths)3793 _RemoveDescendentEntries(ChangedPaths *changedPaths)
3794 {
3795 for (auto it = changedPaths->begin(); it != changedPaths->end(); ) {
3796 auto prefixedIt = it;
3797 ++prefixedIt;
3798
3799 auto prefixedEndIt = prefixedIt;
3800 for (; prefixedEndIt != changedPaths->end()
3801 && prefixedEndIt->first.HasPrefix(it->first); ++prefixedEndIt)
3802 { }
3803
3804 changedPaths->erase(prefixedIt, prefixedEndIt);
3805 ++it;
3806 }
3807 }
3808
3809 // Removes all elements from weaker whose paths are prefixed by other
3810 // elements in stronger. If elements with the same path exist in both
3811 // weaker and stronger, merges those elements into stronger and removes
3812 // the element from weaker. Assumes that stronger has no elements
3813 // whose paths are prefixed by other elements in stronger.
3814 template <class ChangedPaths>
3815 static void
_MergeAndRemoveDescendentEntries(ChangedPaths * stronger,ChangedPaths * weaker)3816 _MergeAndRemoveDescendentEntries(ChangedPaths *stronger, ChangedPaths *weaker)
3817 {
3818 // We may be removing entries from weaker, and depending on the
3819 // concrete type of ChangedPaths that may invalidate iterators. So don't
3820 // cache the end iterator here.
3821 auto weakIt = weaker->begin();
3822
3823 auto strongIt = stronger->begin();
3824 const auto strongEndIt = stronger->end();
3825
3826 while (strongIt != strongEndIt && weakIt != weaker->end()) {
3827 if (weakIt->first < strongIt->first) {
3828 // If the current element in weaker is less than the current element
3829 // in stronger, it cannot be prefixed, so retain it.
3830 ++weakIt;
3831 } else if (weakIt->first == strongIt->first) {
3832 // If the same path exists in both weaker and stronger, merge the
3833 // weaker entry into stronger, then remove it from weaker.
3834 strongIt->second.insert(strongIt->second.end(),
3835 weakIt->second.begin(), weakIt->second.end());
3836 weakIt = weaker->erase(weakIt);
3837 } else if (weakIt->first.HasPrefix(strongIt->first)) {
3838 // Otherwise if this element in weaker is prefixed by the current
3839 // element in stronger, discard it.
3840 //
3841 // Note that if stronger was allowed to have elements that were
3842 // prefixed by other elements in stronger, this would not be
3843 // correct, since stronger could have an exact match for this
3844 // path, which we'd need to merge.
3845 weakIt = weaker->erase(weakIt);
3846 } else {
3847 // Otherwise advance to the next element in stronger.
3848 ++strongIt;
3849 }
3850 }
3851 }
3852
3853 void
_HandleLayersDidChange(const SdfNotice::LayersDidChangeSentPerLayer & n)3854 UsdStage::_HandleLayersDidChange(
3855 const SdfNotice::LayersDidChangeSentPerLayer &n)
3856 {
3857 TfAutoMallocTag2 tag("Usd", _mallocTagID);
3858
3859 // Ignore if this is not the round of changes we're looking for.
3860 size_t serial = n.GetSerialNumber();
3861 if (serial == _lastChangeSerialNumber)
3862 return;
3863
3864 if (ARCH_UNLIKELY(serial < _lastChangeSerialNumber)) {
3865 // If we receive a change from an earlier round of change processing
3866 // than one we've already seen, there must be a violation of the Usd
3867 // threading model -- concurrent edits to layers that apply to a single
3868 // stage are disallowed.
3869 TF_CODING_ERROR("Detected usd threading violation. Concurrent changes "
3870 "to layer(s) composed in stage %p rooted at @%s@. "
3871 "(serial=%zu, lastSerial=%zu).",
3872 this, GetRootLayer()->GetIdentifier().c_str(),
3873 serial, _lastChangeSerialNumber);
3874 return;
3875 }
3876
3877 _lastChangeSerialNumber = serial;
3878
3879 TF_DEBUG(USD_CHANGES).Msg(
3880 "\nHandleLayersDidChange received (%s)\n", UsdDescribe(this).c_str());
3881
3882 // If a function up the call stack has set up _PendingChanges, merge in
3883 // all of the information from layer changes so it can be processed later.
3884 // Otherwise, fill in our own _PendingChanges and process it at the end
3885 // of this function.
3886 _PendingChanges localPendingChanges;
3887 if (!_pendingChanges) {
3888 _pendingChanges = &localPendingChanges;
3889 }
3890
3891 // Keep track of paths to USD objects that need to be recomposed or
3892 // have otherwise changed.
3893 using _PathsToChangesMap = UsdNotice::ObjectsChanged::_PathsToChangesMap;
3894 _PathsToChangesMap& recomposeChanges = _pendingChanges->recomposeChanges;
3895 _PathsToChangesMap& otherResyncChanges = _pendingChanges->otherResyncChanges;
3896 _PathsToChangesMap& otherInfoChanges = _pendingChanges->otherInfoChanges;
3897
3898 SdfPathVector changedActivePaths;
3899
3900 // A fallback prim types change occurs when the fallbackPrimTypes metadata
3901 // changes on the root or session layer.
3902 // Note that we never process these changes while writing the schema
3903 // defined prim type fallbacks to the stage metadata via
3904 // WriteFallbackPrimTypes. Since the function can only write fallbacks for
3905 // recognized schema types and does not overwrite existing fallback entries,
3906 // it creates no effective changes to the composed prims. So, we have to
3907 // ignore this layer metadata change to avoid unnecessarily recomposing
3908 // the whole stage.
3909 auto _IsFallbackPrimTypesChange =
3910 [this](const SdfLayerHandle &layer, const SdfPath &sdfPath,
3911 const TfToken &infoKey)
3912 {
3913 return infoKey == UsdTokens->fallbackPrimTypes &&
3914 !this->_isWritingFallbackPrimTypes &&
3915 sdfPath == SdfPath::AbsoluteRootPath() &&
3916 (layer == this->GetRootLayer() ||
3917 layer == this->GetSessionLayer());
3918 };
3919
3920 // Add dependent paths for any PrimSpecs whose fields have changed that may
3921 // affect cached prim information.
3922 for(const auto& layerAndChangelist : n.GetChangeListVec()) {
3923 // If this layer does not pertain to us, skip.
3924 const SdfLayerHandle &layer = layerAndChangelist.first;
3925 if (_cache->FindAllLayerStacksUsingLayer(layer).empty()) {
3926 continue;
3927 }
3928
3929 // Loop over the changes in this layer, and determine what parts of the
3930 // usd stage are affected by them.
3931 for (const auto& entryList : layerAndChangelist.second.GetEntryList()) {
3932
3933 // This path is the path in the layer that was modified -- in
3934 // general it's not the same as a path to an object on a usd stage.
3935 // Instead, it's the path to the changed part of a layer, which may
3936 // affect zero or more objects on the usd stage, depending on
3937 // reference structures, active state, etc. We have to map these
3938 // paths to those objects on the stage that are affected.
3939 const SdfPath &sdfPath = entryList.first;
3940 const SdfChangeList::Entry &entry = entryList.second;
3941
3942 // Skip target paths entirely -- we do not create target objects in
3943 // USD.
3944 if (sdfPath.IsTargetPath())
3945 continue;
3946
3947 TF_DEBUG(USD_CHANGES).Msg(
3948 "<%s> in @%s@ changed.\n",
3949 sdfPath.GetText(),
3950 layer->GetIdentifier().c_str());
3951
3952 bool willRecompose = false;
3953 if (sdfPath == SdfPath::AbsoluteRootPath() ||
3954 sdfPath.IsPrimOrPrimVariantSelectionPath()) {
3955
3956 bool didChangeActive = false;
3957 for (const auto& info : entry.infoChanged) {
3958 if (info.first == SdfFieldKeys->Active) {
3959 TF_DEBUG(USD_CHANGES).Msg(
3960 "Changed field: %s\n", info.first.GetText());
3961 didChangeActive = true;
3962 break;
3963 }
3964 }
3965
3966 if (didChangeActive || entry.flags.didReorderChildren) {
3967 willRecompose = true;
3968 } else {
3969 for (const auto& info : entry.infoChanged) {
3970 const auto& infoKey = info.first;
3971 if (infoKey == SdfFieldKeys->Kind ||
3972 infoKey == SdfFieldKeys->TypeName ||
3973 infoKey == SdfFieldKeys->Specifier ||
3974 infoKey == UsdTokens->apiSchemas ||
3975
3976 // XXX: Could be more specific when recomposing due
3977 // to clip changes. E.g., only update the clip
3978 // resolver and bits on each prim.
3979 UsdIsClipRelatedField(infoKey) ||
3980 // Fallback prim type changes may potentially only
3981 // affect a small number or prims, but this type of
3982 // change should be so rare that it's not really
3983 // worth parsing the minimal set of prims to
3984 // recompose.
3985 _IsFallbackPrimTypesChange(layer, sdfPath, infoKey)) {
3986
3987 TF_DEBUG(USD_CHANGES).Msg(
3988 "Changed field: %s\n", infoKey.GetText());
3989
3990 willRecompose = true;
3991 break;
3992 }
3993 }
3994 }
3995
3996 if (willRecompose) {
3997 _AddAffectedStagePaths(layer, sdfPath,
3998 *_cache, &recomposeChanges, &entry);
3999 }
4000 if (didChangeActive) {
4001 _AddAffectedStagePaths(layer, sdfPath,
4002 *_cache, &changedActivePaths);
4003 }
4004 }
4005 else {
4006 willRecompose = sdfPath.IsPropertyPath() &&
4007 (entry.flags.didAddPropertyWithOnlyRequiredFields ||
4008 entry.flags.didAddProperty ||
4009 entry.flags.didRemovePropertyWithOnlyRequiredFields ||
4010 entry.flags.didRemoveProperty);
4011
4012 if (willRecompose) {
4013 _AddAffectedStagePaths(
4014 layer, sdfPath, *_cache, &otherResyncChanges, &entry);
4015 }
4016 }
4017
4018 // If we're not going to recompose this path, record the dependent
4019 // scene paths separately so we can notify clients about the
4020 // changes.
4021 if (!willRecompose) {
4022 _AddAffectedStagePaths(layer, sdfPath,
4023 *_cache, &otherInfoChanges, &entry);
4024 }
4025 }
4026 }
4027
4028 // Now we have collected the affected paths in UsdStage namespace in
4029 // recomposeChanges, otherResyncChanges, otherInfoChanges and
4030 // changedActivePaths. Push changes through Pcp to determine further
4031 // invalidation based on composition metadata (reference, inherits, variant
4032 // selections, etc).
4033
4034 PcpChanges& changes = _pendingChanges->pcpChanges;
4035 const PcpCache *cache = _cache.get();
4036 changes.DidChange(
4037 TfSpan<const PcpCache*>(&cache, 1), n.GetChangeListVec());
4038
4039 // Pcp does not consider activation changes to be significant since
4040 // it doesn't look at activation during composition. However, UsdStage
4041 // needs to do so, since it elides children of deactivated prims.
4042 // This ensures that prim indexes for these prims are ejected from
4043 // the PcpCache.
4044 for (const SdfPath& p : changedActivePaths) {
4045 changes.DidChangeSignificantly(_cache.get(), p);
4046 }
4047
4048 // Normally we'd call _ProcessPendingChanges only if _pendingChanges
4049 // pointed to localPendingChanges. If it didn't, it would mean that an
4050 // upstream caller initialized _pendingChanges and that caller would be
4051 // expected to call _ProcessPendingChanges itself.
4052 //
4053 // However, the _PathsToChangesMap objects in _pendingChanges may hold
4054 // raw pointers to entries stored in the notice, so we must process these
4055 // changes immediately while the notice is still alive.
4056 _ProcessPendingChanges();
4057 }
4058
4059 void
_ProcessPendingChanges()4060 UsdStage::_ProcessPendingChanges()
4061 {
4062 if (!TF_VERIFY(_pendingChanges)) {
4063 return;
4064 }
4065
4066 TF_DEBUG(USD_CHANGES).Msg(
4067 "\nProcessPendingChanges (%s)\n", UsdDescribe(this).c_str());
4068
4069 PcpChanges& changes = _pendingChanges->pcpChanges;
4070
4071 using _PathsToChangesMap = UsdNotice::ObjectsChanged::_PathsToChangesMap;
4072 _PathsToChangesMap& recomposeChanges = _pendingChanges->recomposeChanges;
4073 _PathsToChangesMap& otherResyncChanges=_pendingChanges->otherResyncChanges;
4074 _PathsToChangesMap& otherInfoChanges = _pendingChanges->otherInfoChanges;
4075
4076 _Recompose(changes, &recomposeChanges);
4077
4078 if (_pendingChanges->notifyPseudoRootResync) {
4079 recomposeChanges.clear();
4080 recomposeChanges[SdfPath::AbsoluteRootPath()];
4081
4082 otherResyncChanges.clear();
4083 otherInfoChanges.clear();
4084 }
4085 else {
4086 // Filter out all changes to objects beneath instances and remap
4087 // them to the corresponding object in the instance's prototype. Do this
4088 // after _Recompose so that the instancing cache is up-to-date.
4089 auto remapChangesToPrototypes = [this](_PathsToChangesMap* changes) {
4090 std::vector<_PathsToChangesMap::value_type> prototypeChanges;
4091 for (auto it = changes->begin(); it != changes->end(); ) {
4092 if (_IsObjectDescendantOfInstance(it->first)) {
4093 const SdfPath primIndexPath =
4094 it->first.GetAbsoluteRootOrPrimPath();
4095 for (const SdfPath& pathInPrototype :
4096 _instanceCache->GetPrimsInPrototypesUsingPrimIndexPath(
4097 primIndexPath)) {
4098 prototypeChanges.emplace_back(
4099 it->first.ReplacePrefix(
4100 primIndexPath, pathInPrototype),
4101 it->second);
4102 }
4103 it = changes->erase(it);
4104 continue;
4105 }
4106 ++it;
4107 }
4108
4109 for (const auto& entry : prototypeChanges) {
4110 auto& value = (*changes)[entry.first];
4111 value.insert(
4112 value.end(), entry.second.begin(), entry.second.end());
4113 }
4114 };
4115
4116 remapChangesToPrototypes(&recomposeChanges);
4117 remapChangesToPrototypes(&otherResyncChanges);
4118 remapChangesToPrototypes(&otherInfoChanges);
4119
4120 // Add in all other paths that are marked as resynced.
4121 if (recomposeChanges.empty()) {
4122 recomposeChanges.swap(otherResyncChanges);
4123 }
4124 else {
4125 _RemoveDescendentEntries(&recomposeChanges);
4126 _MergeAndRemoveDescendentEntries(
4127 &recomposeChanges, &otherResyncChanges);
4128 for (auto& entry : otherResyncChanges) {
4129 recomposeChanges[entry.first] = std::move(entry.second);
4130 }
4131 }
4132
4133 // Collect the paths in otherChangedPaths that aren't under paths that
4134 // were recomposed. If the pseudo-root had been recomposed, we can
4135 // just clear out otherChangedPaths since everything was recomposed.
4136 if (!recomposeChanges.empty() &&
4137 recomposeChanges.begin()->first == SdfPath::AbsoluteRootPath()) {
4138 // If the pseudo-root is present, it should be the only path in the
4139 // changes.
4140 TF_VERIFY(recomposeChanges.size() == 1);
4141 otherInfoChanges.clear();
4142 }
4143
4144 // Now we want to remove all elements of otherInfoChanges that are
4145 // prefixed by elements in recomposeChanges or beneath instances.
4146 _MergeAndRemoveDescendentEntries(&recomposeChanges, &otherInfoChanges);
4147 }
4148
4149 // Reset _pendingChanges before sending notices so that any changes to
4150 // this stage that happen in response to the notices are handled
4151 // properly. The object that _pendingChanges referred to should remain
4152 // alive, so the references we took above are still valid.
4153 _pendingChanges = nullptr;
4154
4155 if (!recomposeChanges.empty() || !otherInfoChanges.empty()) {
4156 UsdStageWeakPtr self(this);
4157
4158 // Notify about changed objects.
4159 UsdNotice::ObjectsChanged(
4160 self, &recomposeChanges, &otherInfoChanges).Send(self);
4161
4162 // Receivers can now refresh their caches... or just dirty them
4163 UsdNotice::StageContentsChanged(self).Send(self);
4164 }
4165 }
4166
4167 void
_HandleResolverDidChange(const ArNotice::ResolverChanged & n)4168 UsdStage::_HandleResolverDidChange(
4169 const ArNotice::ResolverChanged& n)
4170 {
4171 #if AR_VERSION == 1
4172 return;
4173 #endif
4174
4175 // A ResolverChanged notice that affects our resolver context means that
4176 // any asset paths that have been resolved on this stage may now resolve
4177 // to a different resolved path. This includes asset paths that were
4178 // resolved during composition and asset path-valued attributes.
4179 //
4180 // Handling this notice correctly must be done downstream of Sdf, since
4181 // asset paths have to be re-resolved under the contexts they were
4182 // originally resolved with. Sdf does not have the information needed to do
4183 // this, since it only tracks the context a layer was originally opened
4184 // with and not any other contexts.
4185 //
4186 // For example: let's say we have stage A that opens a layer with asset path
4187 // L, then we create stage B with a different context that also references
4188 // L. If L happens to resolve to the same file under B's context, then A and
4189 // B will share that layer. However, at the Sdf level that layer only knows
4190 // about A's context since that's what it was opened under. If we get a
4191 // ResolverChanged notice that affects stage B, we need to re-resolve L
4192 // under stage B's context to determine if anything needs to change.
4193 if (!n.AffectsContext(GetPathResolverContext())) {
4194 return;
4195 }
4196
4197 TF_DEBUG(USD_CHANGES).Msg(
4198 "\nHandleResolverDidChange received (%s)\n", UsdDescribe(this).c_str());
4199
4200 // Merge stage changes computed in this function with other pending changes
4201 // or start up our own pending changes batch so we can process them at the
4202 // end of the function.
4203 _PendingChanges localPendingChanges;
4204 if (!_pendingChanges) {
4205 _pendingChanges = &localPendingChanges;
4206 }
4207
4208 // Inform Pcp of the change to the resolver to determine prims that
4209 // may need to be resynced. Pcp will re-resolve asset paths for all prim
4210 // indexes to see if any now refer to a different resolved path and
4211 // indicate that resyncs are necessary for those prims.
4212 PcpChanges& changes = _pendingChanges->pcpChanges;
4213 changes.DidChangeAssetResolver(_GetPcpCache());
4214
4215 // Asset-path valued attributes on this stage may be invalidated.
4216 // We don't want to incur the expense of scanning the entire stage
4217 // to see if any such attributes exist so we conservatively notify
4218 // clients that the pseudo-root has resynced, even though we may
4219 // only be recomposing a subset of the stage.
4220 _pendingChanges->notifyPseudoRootResync = true;
4221
4222 // Process pending changes if we are the originators of the batch.
4223 if (_pendingChanges == &localPendingChanges) {
4224 _ProcessPendingChanges();
4225 }
4226 }
4227
4228 void
_Recompose(const PcpChanges & changes)4229 UsdStage::_Recompose(const PcpChanges &changes)
4230 {
4231 using _PathsToChangesMap = UsdNotice::ObjectsChanged::_PathsToChangesMap;
4232 _Recompose(changes, (_PathsToChangesMap*)nullptr);
4233 }
4234
4235 template <class T>
4236 void
_Recompose(const PcpChanges & changes,T * initialPathsToRecompose)4237 UsdStage::_Recompose(const PcpChanges &changes,
4238 T *initialPathsToRecompose)
4239 {
4240 T newPathsToRecompose;
4241 T *pathsToRecompose = initialPathsToRecompose ?
4242 initialPathsToRecompose : &newPathsToRecompose;
4243
4244 // Note: Calling changes.Apply() will result in recomputation of
4245 // pcpPrimIndexes for changed prims, these get updated on the respective
4246 // prims during _ComposeSubtreeImpl call. Using these outdated primIndexes
4247 // can result in undefined behavior
4248 changes.Apply();
4249
4250 // Process layer stack changes.
4251 //
4252 // Pcp recomputes layer stacks immediately upon the call to
4253 // PcpChanges::Apply, which causes composition errors that occur
4254 // during this process to not be reported in _ComposePrimIndexesInParallel.
4255 // Walk through all modified layer stacks and report their errors here.
4256 const PcpChanges::LayerStackChanges &layerStackChanges =
4257 changes.GetLayerStackChanges();
4258
4259 for (const auto& layerStackChange : layerStackChanges) {
4260 const PcpLayerStackPtr& layerStack = layerStackChange.first;
4261 const PcpErrorVector& errors = layerStack->GetLocalErrors();
4262 if (!errors.empty()) {
4263 _ReportPcpErrors(errors, "Recomposing stage");
4264 }
4265 }
4266
4267 // Process composed prim changes.
4268 const PcpChanges::CacheChanges &cacheChanges = changes.GetCacheChanges();
4269 if (!cacheChanges.empty()) {
4270 const PcpCacheChanges &ourChanges = cacheChanges.begin()->second;
4271
4272 for (const auto& path : ourChanges.didChangeSignificantly) {
4273 (*pathsToRecompose)[path];
4274 TF_DEBUG(USD_CHANGES).Msg("Did Change Significantly: %s\n",
4275 path.GetText());
4276 }
4277
4278 for (const auto& path : ourChanges.didChangePrims) {
4279 (*pathsToRecompose)[path];
4280 TF_DEBUG(USD_CHANGES).Msg("Did Change Prim: %s\n", path.GetText());
4281 }
4282
4283 } else {
4284 TF_DEBUG(USD_CHANGES).Msg("No cache changes\n");
4285 }
4286
4287 _RecomposePrims(pathsToRecompose);
4288
4289 // Update layer change notice listeners if changes may affect
4290 // the set of used layers. This is potentially expensive which is why we
4291 // try to make sure the changes require it.
4292 _RegisterPerLayerNotices();
4293 }
4294
4295 template <class T>
4296 void
_RecomposePrims(T * pathsToRecompose)4297 UsdStage::_RecomposePrims(T *pathsToRecompose)
4298 {
4299 if (pathsToRecompose->empty()) {
4300 TF_DEBUG(USD_CHANGES).Msg("Nothing to recompose in cache changes\n");
4301 return;
4302 }
4303
4304 // Prune descendant paths.
4305 _RemoveDescendentEntries(pathsToRecompose);
4306
4307 // Invalidate the clip cache, but keep the clips alive for the duration
4308 // of recomposition in the (likely) case that clip data hasn't changed
4309 // and the underlying clip layer can be reused.
4310 Usd_ClipCache::Lifeboat clipLifeboat(*_clipCache);
4311 for (const auto& entry : *pathsToRecompose) {
4312 _clipCache->InvalidateClipsForPrim(entry.first);
4313 }
4314
4315 // Ask Pcp to compute all the prim indexes in parallel, stopping at
4316 // stuff that's not active.
4317 SdfPathVector primPathsToRecompose;
4318 primPathsToRecompose.reserve(pathsToRecompose->size());
4319 for (const auto& entry : *pathsToRecompose) {
4320 const SdfPath& path = entry.first;
4321 if (!path.IsAbsoluteRootOrPrimPath() ||
4322 path.ContainsPrimVariantSelection()) {
4323 continue;
4324 }
4325
4326 // Instance prims don't expose any name children, so we don't
4327 // need to recompose any prim index beneath instance prim
4328 // indexes *unless* they are being used as the source index
4329 // for a prototype.
4330 if (_instanceCache->IsPathDescendantToAnInstance(path)) {
4331 const bool primIndexUsedByPrototype =
4332 _instanceCache->PrototypeUsesPrimIndexPath(path);
4333 if (!primIndexUsedByPrototype) {
4334 TF_DEBUG(USD_CHANGES).Msg(
4335 "Ignoring elided prim <%s>\n", path.GetText());
4336 continue;
4337 }
4338 }
4339
4340 // Unregister all instances beneath the given path. This
4341 // allows us to determine which instance prim indexes are
4342 // no longer present and make the appropriate instance
4343 // changes during prim index composition below.
4344 _instanceCache->UnregisterInstancePrimIndexesUnder(path);
4345
4346 primPathsToRecompose.push_back(path);
4347 }
4348
4349 ArResolverScopedCache resolverCache;
4350 Usd_InstanceChanges instanceChanges;
4351 _ComposePrimIndexesInParallel(
4352 primPathsToRecompose, "recomposing stage", &instanceChanges);
4353
4354 // Determine what instance prototype prims on this stage need to
4355 // be recomposed due to instance prim index changes.
4356 typedef TfHashMap<SdfPath, SdfPath, SdfPath::Hash> _PrototypeToPrimIndexMap;
4357 _PrototypeToPrimIndexMap prototypeToPrimIndexMap;
4358
4359 const bool pathsContainsAbsRoot =
4360 pathsToRecompose->begin()->first == SdfPath::AbsoluteRootPath();
4361
4362 //If AbsoluteRootPath is present then that should be the only entry!
4363 TF_VERIFY(!pathsContainsAbsRoot || pathsToRecompose->size() == 1);
4364
4365 const size_t origNumPathsToRecompose = pathsToRecompose->size();
4366 for (const auto& entry : *pathsToRecompose) {
4367 const SdfPath& path = entry.first;
4368 // Add corresponding inPrototypePaths for any instance or proxy paths in
4369 // pathsToRecompose
4370 for (const SdfPath& inPrototypePath :
4371 _instanceCache->GetPrimsInPrototypesUsingPrimIndexPath(path)) {
4372 prototypeToPrimIndexMap[inPrototypePath] = path;
4373 (*pathsToRecompose)[inPrototypePath];
4374 }
4375 // Add any unchanged prototypes whose instances are descendents of paths
4376 // in pathsToRecompose
4377 for (const std::pair<SdfPath, SdfPath>& prototypeSourceIndexPair:
4378 _instanceCache->GetPrototypesUsingPrimIndexPathOrDescendents(
4379 path))
4380 {
4381 const SdfPath& prototypePath = prototypeSourceIndexPair.first;
4382 const SdfPath& sourceIndexPath = prototypeSourceIndexPair.second;
4383 prototypeToPrimIndexMap[prototypePath] = sourceIndexPath;
4384 (*pathsToRecompose)[prototypePath];
4385 }
4386 }
4387
4388 // Add new prototypes paths to pathsToRecompose
4389 for (size_t i = 0; i != instanceChanges.newPrototypePrims.size(); ++i) {
4390 prototypeToPrimIndexMap[instanceChanges.newPrototypePrims[i]] =
4391 instanceChanges.newPrototypePrimIndexes[i];
4392 (*pathsToRecompose)[instanceChanges.newPrototypePrims[i]];
4393 }
4394
4395 // Add changed prototypes paths to pathsToRecompose
4396 for (size_t i = 0; i != instanceChanges.changedPrototypePrims.size(); ++i) {
4397 prototypeToPrimIndexMap[instanceChanges.changedPrototypePrims[i]] =
4398 instanceChanges.changedPrototypePrimIndexes[i];
4399 (*pathsToRecompose)[instanceChanges.changedPrototypePrims[i]];
4400 }
4401
4402 // If pseudoRoot is present in pathsToRecompose, then the only other prims
4403 // in pathsToRecompose can be prototype prims (added above), in which case
4404 // we do not want to remove these prototypes. If not we need to make sure
4405 // any descendents of prototypes are removed if corresponding prototype is
4406 // present
4407 if (!pathsContainsAbsRoot &&
4408 pathsToRecompose->size() != origNumPathsToRecompose) {
4409 _RemoveDescendentEntries(pathsToRecompose);
4410 }
4411
4412 // XXX: If the call chain here ever starts composing prims in parallel,
4413 // we'll have to add a Usd_ClipCache::ConcurrentPopulationContext object
4414 // around this.
4415 std::vector<Usd_PrimDataPtr> subtreesToRecompose;
4416 _ComputeSubtreesToRecompose(
4417 make_transform_iterator(pathsToRecompose->begin(), TfGet<0>()),
4418 make_transform_iterator(pathsToRecompose->end(), TfGet<0>()),
4419 &subtreesToRecompose);
4420
4421 // Recompose subtrees.
4422 if (prototypeToPrimIndexMap.empty()) {
4423 _ComposeSubtreesInParallel(subtreesToRecompose);
4424 }
4425 else {
4426
4427 SdfPathVector primIndexPathsForSubtrees;
4428 primIndexPathsForSubtrees.reserve(subtreesToRecompose.size());
4429 for (const auto& prim : subtreesToRecompose) {
4430 primIndexPathsForSubtrees.push_back(TfMapLookupByValue(
4431 prototypeToPrimIndexMap, prim->GetPath(), prim->GetPath()));
4432 }
4433 _ComposeSubtreesInParallel(
4434 subtreesToRecompose, &primIndexPathsForSubtrees);
4435 }
4436
4437 // Destroy dead prototype subtrees, making sure to record them in
4438 // paths to recompose for notifications.
4439 for (const SdfPath& p : instanceChanges.deadPrototypePrims) {
4440 (*pathsToRecompose)[p];
4441 }
4442 _DestroyPrimsInParallel(instanceChanges.deadPrototypePrims);
4443 }
4444
4445
4446 template <class Iter>
4447 void
_ComputeSubtreesToRecompose(Iter i,Iter end,std::vector<Usd_PrimDataPtr> * subtreesToRecompose)4448 UsdStage::_ComputeSubtreesToRecompose(
4449 Iter i, Iter end,
4450 std::vector<Usd_PrimDataPtr>* subtreesToRecompose)
4451 {
4452 // XXX: If this function ever winds up composing prims in parallel, callers
4453 // will have to ensure that a Usd_ClipCache::ConcurrentPopulationContext
4454 // object is alive during the call.
4455
4456 subtreesToRecompose->reserve(
4457 subtreesToRecompose->size() + std::distance(i, end));
4458
4459 while (i != end) {
4460 TF_DEBUG(USD_CHANGES).Msg("Recomposing: %s\n", i->GetText());
4461 // TODO: refactor into shared method
4462 // We only care about recomposing prim-like things
4463 // so avoid recomposing anything else.
4464 if (!i->IsAbsoluteRootOrPrimPath() ||
4465 i->ContainsPrimVariantSelection()) {
4466 TF_DEBUG(USD_CHANGES).Msg("Skipping non-prim: %s\n",
4467 i->GetText());
4468 ++i;
4469 continue;
4470 }
4471
4472 // Add prototypes to list of subtrees to recompose and instantiate any
4473 // new prototype not present in the primMap from before
4474 if (_instanceCache->IsPrototypePath(*i)) {
4475 PathToNodeMap::const_iterator itr = _primMap.find(*i);
4476 Usd_PrimDataPtr prototypePrim;
4477 if (itr != _primMap.end()) {
4478 // should be a changed prototype if already in the primMap
4479 prototypePrim = itr->second.get();
4480 } else {
4481 // newPrototype should be absent from the primMap, instantiate
4482 // these now to be added to subtreesToRecompose
4483 prototypePrim = _InstantiatePrototypePrim(*i);
4484 }
4485 subtreesToRecompose->push_back(prototypePrim);
4486 ++i;
4487 continue;
4488 }
4489
4490 // Collect all non-prototype prims (including descendants of prototypes)
4491 // to be added to subtreesToRecompute
4492 SdfPath const &parentPath = i->GetParentPath();
4493 PathToNodeMap::const_iterator parentIt = _primMap.find(parentPath);
4494 if (parentIt != _primMap.end()) {
4495
4496 // Since our input range contains no descendant paths, siblings
4497 // must appear consecutively. We want to process all siblings that
4498 // have changed together in order to only recompose the parent's
4499 // list of children once. We scan forward while the paths share a
4500 // parent to find the range of siblings.
4501
4502 // Recompose parent's list of children.
4503 auto parent = parentIt->second.get();
4504 _ComposeChildren(
4505 parent, parent->IsInPrototype() ? nullptr : &_populationMask,
4506 /*recurse=*/false);
4507
4508 // Recompose the subtree for each affected sibling.
4509 do {
4510 PathToNodeMap::const_iterator primIt = _primMap.find(*i);
4511 if (primIt != _primMap.end()) {
4512 subtreesToRecompose->push_back(primIt->second.get());
4513 } else if (_instanceCache->IsPrototypePath(*i)) {
4514 // If this path is a prototype path and is not present in
4515 // the primMap, then this must be a new prototype added
4516 // during this processing, instantiate and add it.
4517 Usd_PrimDataPtr protoPrim = _InstantiatePrototypePrim(*i);
4518 subtreesToRecompose->push_back(protoPrim);
4519 }
4520 ++i;
4521 } while (i != end && i->GetParentPath() == parentPath);
4522 } else if (parentPath.IsEmpty()) {
4523 // This is the pseudo root, so we need to blow and rebuild
4524 // everything.
4525 subtreesToRecompose->push_back(_pseudoRoot);
4526 ++i;
4527 } else {
4528 ++i;
4529 }
4530 }
4531 }
4532
4533 struct UsdStage::_IncludePayloadsPredicate
4534 {
_IncludePayloadsPredicateUsdStage::_IncludePayloadsPredicate4535 explicit _IncludePayloadsPredicate(UsdStage const *stage)
4536 : _stage(stage) {}
4537
operator ()UsdStage::_IncludePayloadsPredicate4538 bool operator()(SdfPath const &primIndexPath) const {
4539 // Apply the stage's load rules to this primIndexPath. This works
4540 // correctly with instancing, because load rules are included in
4541 // instancing keys.
4542 return _stage->_loadRules.IsLoaded(primIndexPath);
4543 }
4544
4545 UsdStage const *_stage;
4546 };
4547
4548 void
_ComposePrimIndexesInParallel(const std::vector<SdfPath> & primIndexPaths,const std::string & context,Usd_InstanceChanges * instanceChanges)4549 UsdStage::_ComposePrimIndexesInParallel(
4550 const std::vector<SdfPath>& primIndexPaths,
4551 const std::string& context,
4552 Usd_InstanceChanges* instanceChanges)
4553 {
4554 if (TfDebug::IsEnabled(USD_COMPOSITION)) {
4555 // Ensure not too much spew if primIndexPaths is big.
4556 constexpr size_t maxPaths = 16;
4557 std::vector<SdfPath> dbgPaths(
4558 primIndexPaths.begin(),
4559 primIndexPaths.begin() + std::min(maxPaths, primIndexPaths.size()));
4560 string msg = TfStringPrintf(
4561 "Composing prim indexes: %s%s\n",
4562 TfStringify(dbgPaths).c_str(), primIndexPaths.size() > maxPaths ?
4563 TfStringPrintf(
4564 " (and %zu more)", primIndexPaths.size() - maxPaths).c_str() :
4565 "");
4566 TF_DEBUG(USD_COMPOSITION).Msg("%s", msg.c_str());
4567 }
4568
4569 // We only want to compute prim indexes included by the stage's
4570 // population mask. As an optimization, if all prims are included the
4571 // name children predicate doesn't need to consider the mask at all.
4572 static auto allMask = UsdStagePopulationMask::All();
4573 const UsdStagePopulationMask* mask =
4574 _populationMask == allMask ? nullptr : &_populationMask;
4575
4576 // Ask Pcp to compute all the prim indexes in parallel, stopping at
4577 // prim indexes that won't be used by the stage.
4578 PcpErrorVector errs;
4579
4580 _cache->ComputePrimIndexesInParallel(
4581 primIndexPaths, &errs,
4582 _NameChildrenPred(mask, &_loadRules, _instanceCache.get()),
4583 _IncludePayloadsPredicate(this),
4584 "Usd", _mallocTagID);
4585
4586 if (!errs.empty()) {
4587 _ReportPcpErrors(errs, context);
4588 }
4589
4590 // Process instancing changes due to new or changed instanceable
4591 // prim indexes discovered during composition.
4592 Usd_InstanceChanges changes;
4593 _instanceCache->ProcessChanges(&changes);
4594
4595 if (instanceChanges) {
4596 instanceChanges->AppendChanges(changes);
4597 }
4598
4599 // After processing changes, we may discover that some prototype prims
4600 // need to change their source prim index. This may be because their
4601 // previous source prim index was destroyed or was no longer an
4602 // instance. Compose the new source prim indexes.
4603 if (!changes.changedPrototypePrims.empty()) {
4604 _ComposePrimIndexesInParallel(
4605 changes.changedPrototypePrimIndexes, context, instanceChanges);
4606 }
4607 }
4608
4609 void
_RegisterPerLayerNotices()4610 UsdStage::_RegisterPerLayerNotices()
4611 {
4612 // The goal is to update _layersAndNoticeKeys so it reflects the current
4613 // cache's set of used layers (from GetUsedLayers()). We want to avoid
4614 // thrashing the TfNotice registrations since we expect that usually only a
4615 // relatively small subset of used layers will change, if any.
4616 //
4617 // We walk both the current _layersAndNoticeKeys and the cache's
4618 // GetUsedLayers, and incrementally update, TfNotice::Revoke()ing any layers
4619 // we no longer use, TfNotice::Register()ing for new layers we didn't use
4620 // previously, and leaving alone those layers that remain. The linear walk
4621 // works because the PcpCache::GetUsedLayers() returns a std::set, so we
4622 // always retain things in a stable order.
4623
4624 // Check to see if the set of used layers hasn't changed, and skip all this
4625 // if so.
4626 size_t currentUsedLayersRevision = _cache->GetUsedLayersRevision();
4627 if (_usedLayersRevision &&
4628 _usedLayersRevision == currentUsedLayersRevision) {
4629 return;
4630 }
4631
4632 SdfLayerHandleSet usedLayers = _cache->GetUsedLayers();
4633 _usedLayersRevision = currentUsedLayersRevision;
4634
4635 SdfLayerHandleSet::const_iterator
4636 usedLayersIter = usedLayers.begin(),
4637 usedLayersEnd = usedLayers.end();
4638
4639 _LayerAndNoticeKeyVec::iterator
4640 layerAndKeyIter = _layersAndNoticeKeys.begin(),
4641 layerAndKeyEnd = _layersAndNoticeKeys.end();
4642
4643 // We'll build a new vector and swap it into place at the end. We can
4644 // preallocate space upfront since we know the resulting size will be
4645 // exactly the size of usedLayers.
4646 _LayerAndNoticeKeyVec newLayersAndNoticeKeys;
4647 newLayersAndNoticeKeys.reserve(usedLayers.size());
4648
4649 UsdStagePtr self(this);
4650
4651 while (usedLayersIter != usedLayersEnd ||
4652 layerAndKeyIter != layerAndKeyEnd) {
4653
4654 // There are three cases to consider: a newly added layer, a layer no
4655 // longer used, or a layer that we used before and continue to use.
4656 if (layerAndKeyIter == layerAndKeyEnd ||
4657 (usedLayersIter != usedLayersEnd &&
4658 *usedLayersIter < layerAndKeyIter->first)) {
4659 // This is a newly added layer. Register for the notice and add it.
4660 newLayersAndNoticeKeys.push_back(
4661 make_pair(*usedLayersIter,
4662 TfNotice::Register(
4663 self, &UsdStage::_HandleLayersDidChange,
4664 *usedLayersIter)));
4665 ++usedLayersIter;
4666 } else if (usedLayersIter == usedLayersEnd ||
4667 (layerAndKeyIter != layerAndKeyEnd &&
4668 layerAndKeyIter->first < *usedLayersIter)) {
4669 // This is a layer we no longer use, unregister and skip over.
4670 TfNotice::Revoke(layerAndKeyIter->second);
4671 ++layerAndKeyIter;
4672 } else {
4673 // This is a layer we had before and still have, just copy it over.
4674 newLayersAndNoticeKeys.push_back(*layerAndKeyIter);
4675 ++layerAndKeyIter, ++usedLayersIter;
4676 }
4677 }
4678
4679 // Swap new set into place.
4680 _layersAndNoticeKeys.swap(newLayersAndNoticeKeys);
4681 }
4682
4683 void
_RegisterResolverChangeNotice()4684 UsdStage::_RegisterResolverChangeNotice()
4685 {
4686 _resolverChangeKey = TfNotice::Register(
4687 TfCreateWeakPtr(this), &UsdStage::_HandleResolverDidChange);
4688 }
4689
4690 SdfPrimSpecHandle
_GetPrimSpec(const SdfPath & path)4691 UsdStage::_GetPrimSpec(const SdfPath& path)
4692 {
4693 return GetEditTarget().GetPrimSpecForScenePath(path);
4694 }
4695
4696 SdfSpecType
_GetDefiningSpecType(Usd_PrimDataConstPtr primData,const TfToken & propName) const4697 UsdStage::_GetDefiningSpecType(Usd_PrimDataConstPtr primData,
4698 const TfToken& propName) const
4699 {
4700 if (!TF_VERIFY(primData) || !TF_VERIFY(!propName.IsEmpty()))
4701 return SdfSpecTypeUnknown;
4702
4703 // Check for a spec type in the definition registry, in case this is a
4704 // builtin property.
4705 const UsdPrimDefinition &primDef = primData->GetPrimDefinition();
4706 SdfSpecType specType = primDef.GetSpecType(propName);
4707 if (specType != SdfSpecTypeUnknown)
4708 return specType;
4709
4710 // Otherwise look for the strongest authored property spec.
4711 Usd_Resolver res(&primData->GetPrimIndex(), /*skipEmptyNodes=*/true);
4712 SdfPath curPath;
4713 bool curPathValid = false;
4714 while (res.IsValid()) {
4715 const SdfLayerRefPtr& layer = res.GetLayer();
4716 if (layer->HasSpec(res.GetLocalPath())) {
4717 if (!curPathValid) {
4718 curPath = res.GetLocalPath().AppendProperty(propName);
4719 curPathValid = true;
4720 }
4721 specType = layer->GetSpecType(curPath);
4722 if (specType != SdfSpecTypeUnknown)
4723 return specType;
4724 }
4725 if (res.NextLayer())
4726 curPathValid = false;
4727 }
4728
4729 // Unknown.
4730 return SdfSpecTypeUnknown;
4731 }
4732
4733 // ------------------------------------------------------------------------- //
4734 // Flatten & Export Utilities
4735 // ------------------------------------------------------------------------- //
4736
4737 class Usd_FlattenAccess
4738 {
4739 public:
4740
GetAllMetadataForFlatten(const UsdObject & obj,UsdMetadataValueMap * resultMap)4741 static void GetAllMetadataForFlatten(
4742 const UsdObject &obj, UsdMetadataValueMap* resultMap)
4743 {
4744 // Get the resolved metadata with any asset paths anchored.
4745 obj.GetStage()->_GetAllMetadata(
4746 obj, /* useFallbacks = */ false, resultMap,
4747 /* anchorAssetPathsOnly = */ true);
4748 }
4749
ResolveValueForFlatten(UsdTimeCode time,const UsdAttribute & attr,const SdfLayerOffset & timeOffset,VtValue * value)4750 static void ResolveValueForFlatten(
4751 UsdTimeCode time, const UsdAttribute& attr,
4752 const SdfLayerOffset &timeOffset, VtValue* value)
4753 {
4754 // Asset path values are anchored for flatten operations
4755 attr.GetStage()->_MakeResolvedAssetPathsValue(
4756 time, attr, value, /* anchorAssetPathsOnly = */ true);
4757 // Time based values are adjusted by layer offset when flattened to a
4758 // layer affected by an offset.
4759 if (!timeOffset.IsIdentity()) {
4760 Usd_ApplyLayerOffsetToValue(value, timeOffset);
4761 }
4762
4763 }
4764
MakeTimeSampleMapForFlatten(const UsdAttribute & attr,const SdfLayerOffset & offset,SdfTimeSampleMap * out)4765 static bool MakeTimeSampleMapForFlatten(
4766 const UsdAttribute &attr, const SdfLayerOffset& offset,
4767 SdfTimeSampleMap *out)
4768 {
4769 UsdAttributeQuery attrQuery(attr);
4770
4771 std::vector<double> timeSamples;
4772 if (attrQuery.GetTimeSamples(&timeSamples)) {
4773 for (const auto& timeSample : timeSamples) {
4774 VtValue value;
4775 if (attrQuery.Get(&value, timeSample)) {
4776 Usd_FlattenAccess::ResolveValueForFlatten(
4777 timeSample, attr, offset, &value);
4778 (*out)[offset * timeSample].Swap(value);
4779 }
4780 else {
4781 (*out)[offset * timeSample] = VtValue(SdfValueBlock());
4782 }
4783 }
4784 return true;
4785 }
4786 return false;
4787 }
4788
4789 };
4790
4791 namespace {
4792
4793 // Map from path to replacement for remapping target paths during flattening.
4794 using _PathRemapping = std::map<SdfPath, SdfPath>;
4795
4796 // Apply path remappings to a list of target paths.
4797 void
_RemapTargetPaths(SdfPathVector * targetPaths,const _PathRemapping & pathRemapping)4798 _RemapTargetPaths(SdfPathVector* targetPaths,
4799 const _PathRemapping& pathRemapping)
4800 {
4801 if (pathRemapping.empty()) {
4802 return;
4803 }
4804
4805 for (SdfPath& p : *targetPaths) {
4806 auto it = SdfPathFindLongestPrefix(pathRemapping, p);
4807 if (it != pathRemapping.end()) {
4808 p = p.ReplacePrefix(it->first, it->second);
4809 }
4810 }
4811 }
4812
4813 // Remove any paths to prototype prims or descendants from given target paths
4814 // for srcProp. Issues a warning if any paths were removed.
4815 void
_RemovePrototypeTargetPaths(const UsdProperty & srcProp,SdfPathVector * targetPaths)4816 _RemovePrototypeTargetPaths(const UsdProperty& srcProp,
4817 SdfPathVector* targetPaths)
4818 {
4819 auto removeIt = std::remove_if(
4820 targetPaths->begin(), targetPaths->end(),
4821 Usd_InstanceCache::IsPathInPrototype);
4822 if (removeIt == targetPaths->end()) {
4823 return;
4824 }
4825
4826 TF_WARN(
4827 "Some %s paths from <%s> could not be flattened because "
4828 "they targeted objects within an instancing prototype.",
4829 srcProp.Is<UsdAttribute>() ?
4830 "attribute connection" : "relationship target",
4831 srcProp.GetPath().GetText());
4832
4833 targetPaths->erase(removeIt, targetPaths->end());
4834 }
4835
4836 // We want to give generated prototypes in the flattened stage
4837 // reserved(using '__' as a prefix), unclashing paths, however,
4838 // we don't want to use the '__Prototype' paths which have special
4839 // meaning to UsdStage. So we create a mapping between our generated
4840 // 'Flattened_Prototype'-style paths and the '__Prototype' paths.
4841 _PathRemapping
_GenerateFlattenedPrototypePath(const std::vector<UsdPrim> & prototypes)4842 _GenerateFlattenedPrototypePath(const std::vector<UsdPrim>& prototypes)
4843 {
4844 size_t primPrototypeId = 1;
4845
4846 const auto generatePathName = [&primPrototypeId]() {
4847 return SdfPath(TfStringPrintf("/Flattened_Prototype_%lu",
4848 primPrototypeId++));
4849 };
4850
4851 _PathRemapping prototypeToFlattened;
4852
4853 for (auto const& prototypePrim : prototypes) {
4854 SdfPath flattenedPrototypePath;
4855 const auto prototypePrimPath = prototypePrim.GetPath();
4856
4857 auto prototypePathLookup = prototypeToFlattened.find(prototypePrimPath);
4858 if (prototypePathLookup == prototypeToFlattened.end()) {
4859 // We want to ensure that we don't clash with user
4860 // prims in the unlikely even they named it Flatten_xxx
4861 flattenedPrototypePath = generatePathName();
4862 const auto stage = prototypePrim.GetStage();
4863 while (stage->GetPrimAtPath(flattenedPrototypePath)) {
4864 flattenedPrototypePath = generatePathName();
4865 }
4866 prototypeToFlattened.emplace(
4867 prototypePrimPath, flattenedPrototypePath);
4868 } else {
4869 flattenedPrototypePath = prototypePathLookup->second;
4870 }
4871 }
4872
4873 return prototypeToFlattened;
4874 }
4875
4876 void
_CopyMetadata(const SdfSpecHandle & dest,const UsdMetadataValueMap & metadata)4877 _CopyMetadata(const SdfSpecHandle& dest, const UsdMetadataValueMap& metadata)
4878 {
4879 // Copy each key/value into the Sdf spec.
4880 TfErrorMark m;
4881 vector<string> msgs;
4882 for (auto const& tokVal : metadata) {
4883 dest->SetInfo(tokVal.first, tokVal.second);
4884 if (!m.IsClean()) {
4885 msgs.clear();
4886 for (auto i = m.GetBegin(); i != m.GetEnd(); ++i) {
4887 msgs.push_back(i->GetCommentary());
4888 }
4889 m.Clear();
4890 TF_WARN("Failed copying metadata: %s", TfStringJoin(msgs).c_str());
4891 }
4892 }
4893 }
4894
4895 void
_CopyAuthoredMetadata(const UsdObject & source,const SdfSpecHandle & dest)4896 _CopyAuthoredMetadata(const UsdObject &source, const SdfSpecHandle& dest)
4897 {
4898 // GetAllMetadata returns all non-private metadata fields (it excludes
4899 // composition arcs and values), which is exactly what we want here.
4900 UsdMetadataValueMap metadata;
4901 Usd_FlattenAccess::GetAllMetadataForFlatten(source, &metadata);
4902
4903 _CopyMetadata(dest, metadata);
4904 }
4905
4906 void
_CopyProperty(const UsdProperty & prop,const SdfPrimSpecHandle & dest,const TfToken & destName,const _PathRemapping & pathRemapping,const SdfLayerOffset & timeOffset)4907 _CopyProperty(const UsdProperty &prop,
4908 const SdfPrimSpecHandle &dest, const TfToken &destName,
4909 const _PathRemapping &pathRemapping,
4910 const SdfLayerOffset &timeOffset)
4911 {
4912 if (prop.Is<UsdAttribute>()) {
4913 UsdAttribute attr = prop.As<UsdAttribute>();
4914
4915 if (!attr.GetTypeName()){
4916 TF_WARN("Attribute <%s> has unknown value type. "
4917 "It will be omitted from the flattened result.",
4918 attr.GetPath().GetText());
4919 return;
4920 }
4921
4922 SdfAttributeSpecHandle sdfAttr = dest->GetAttributes()[destName];
4923 if (!sdfAttr) {
4924 sdfAttr = SdfAttributeSpec::New(dest, destName, attr.GetTypeName());
4925 }
4926
4927 _CopyAuthoredMetadata(attr, sdfAttr);
4928
4929 // Copy the default & time samples, if present. We get the
4930 // correct timeSamples/default value resolution here because
4931 // GetBracketingTimeSamples sets hasSamples=false when the
4932 // default value is stronger.
4933
4934 double lower = 0.0, upper = 0.0;
4935 bool hasSamples = false;
4936 if (attr.GetBracketingTimeSamples(
4937 0.0, &lower, &upper, &hasSamples) && hasSamples) {
4938 SdfTimeSampleMap ts;
4939 if (Usd_FlattenAccess::MakeTimeSampleMapForFlatten(
4940 attr, timeOffset, &ts)) {
4941 sdfAttr->SetInfo(SdfFieldKeys->TimeSamples, VtValue::Take(ts));
4942 }
4943 }
4944 if (attr.HasAuthoredMetadata(SdfFieldKeys->Default)) {
4945 VtValue defaultValue;
4946 if (attr.Get(&defaultValue)) {
4947 Usd_FlattenAccess::ResolveValueForFlatten(
4948 UsdTimeCode::Default(), attr, timeOffset, &defaultValue);
4949 }
4950 else {
4951 defaultValue = SdfValueBlock();
4952 }
4953 sdfAttr->SetInfo(SdfFieldKeys->Default, defaultValue);
4954 }
4955 SdfPathVector sources;
4956 attr.GetConnections(&sources);
4957 if (!sources.empty()) {
4958 _RemapTargetPaths(&sources, pathRemapping);
4959 _RemovePrototypeTargetPaths(prop, &sources);
4960 sdfAttr->GetConnectionPathList().GetExplicitItems() = sources;
4961 }
4962 }
4963 else if (prop.Is<UsdRelationship>()) {
4964 UsdRelationship rel = prop.As<UsdRelationship>();
4965 // NOTE: custom = true by default for relationship, but the
4966 // SdfSchema fallback is false, so we must set it explicitly
4967 // here. The situation is similar for variability.
4968 SdfRelationshipSpecHandle sdfRel = dest->GetRelationships()[destName];
4969 if (!sdfRel){
4970 sdfRel = SdfRelationshipSpec::New(
4971 dest, destName, /*custom*/ false, SdfVariabilityVarying);
4972 }
4973
4974 _CopyAuthoredMetadata(rel, sdfRel);
4975
4976 SdfPathVector targets;
4977 rel.GetTargets(&targets);
4978 if (!targets.empty()) {
4979 _RemapTargetPaths(&targets, pathRemapping);
4980 _RemovePrototypeTargetPaths(prop, &targets);
4981 sdfRel->GetTargetPathList().GetExplicitItems() = targets;
4982 }
4983 }
4984 }
4985
4986 void
_CopyPrim(const UsdPrim & usdPrim,const SdfLayerHandle & layer,const SdfPath & path,const _PathRemapping & prototypeToFlattened)4987 _CopyPrim(const UsdPrim &usdPrim,
4988 const SdfLayerHandle &layer, const SdfPath &path,
4989 const _PathRemapping &prototypeToFlattened)
4990 {
4991 SdfPrimSpecHandle newPrim;
4992
4993 if (!usdPrim.IsActive()) {
4994 return;
4995 }
4996
4997 if (usdPrim.GetPath() == SdfPath::AbsoluteRootPath()) {
4998 newPrim = layer->GetPseudoRoot();
4999 } else {
5000 // Note that the true value for spec will be populated in _CopyMetadata
5001 newPrim = SdfPrimSpec::New(layer->GetPrimAtPath(path.GetParentPath()),
5002 path.GetName(), SdfSpecifierOver,
5003 usdPrim.GetTypeName());
5004 }
5005
5006 if (usdPrim.IsInstance()) {
5007 const auto flattenedPrototypePath =
5008 prototypeToFlattened.at(usdPrim.GetPrototype().GetPath());
5009
5010 // Author an internal reference to our flattened prototype prim
5011 newPrim->GetReferenceList().Add(SdfReference(std::string(),
5012 flattenedPrototypePath));
5013 }
5014
5015 _CopyAuthoredMetadata(usdPrim, newPrim);
5016
5017 // In the case of flattening clips, we may have builtin attributes which
5018 // aren't declared in the static scene topology, but may have a value
5019 // in some clips that we want to relay into the flattened result.
5020 // XXX: This should be removed if we fix GetProperties()
5021 // and GetAuthoredProperties to consider clips.
5022 auto hasValue = [](const UsdProperty& prop){
5023 return prop.Is<UsdAttribute>()
5024 && prop.As<UsdAttribute>().HasAuthoredValue();
5025 };
5026
5027 for (auto const &prop : usdPrim.GetProperties()) {
5028 if (prop.IsAuthored() || hasValue(prop)) {
5029 _CopyProperty(prop, newPrim, prop.GetName(), prototypeToFlattened,
5030 SdfLayerOffset());
5031 }
5032 }
5033 }
5034
5035 void
_CopyPrototypePrim(const UsdPrim & prototypePrim,const SdfLayerHandle & destinationLayer,const _PathRemapping & prototypeToFlattened)5036 _CopyPrototypePrim(const UsdPrim &prototypePrim,
5037 const SdfLayerHandle &destinationLayer,
5038 const _PathRemapping &prototypeToFlattened)
5039 {
5040 const auto& flattenedPrototypePath
5041 = prototypeToFlattened.at(prototypePrim.GetPath());
5042
5043 for (UsdPrim child: UsdPrimRange::AllPrims(prototypePrim)) {
5044 // We need to update the child path to use the Flatten name.
5045 const auto flattenedChildPath = child.GetPath().ReplacePrefix(
5046 prototypePrim.GetPath(), flattenedPrototypePath);
5047
5048 _CopyPrim(child, destinationLayer, flattenedChildPath,
5049 prototypeToFlattened);
5050 }
5051 }
5052
5053 bool
_IsPrivateFallbackFieldKey(const TfToken & fieldKey)5054 _IsPrivateFallbackFieldKey(const TfToken& fieldKey)
5055 {
5056 // Consider documentation and comment fallbacks as private; these are
5057 // primarily for schema authors and are not expected to be authored
5058 // in flattened results.
5059 if (fieldKey == SdfFieldKeys->Documentation ||
5060 fieldKey == SdfFieldKeys->Comment) {
5061 return true;
5062 }
5063
5064 // Consider default value fallback as non-private, since we do write out
5065 // default values during flattening.
5066 if (fieldKey == SdfFieldKeys->Default) {
5067 return false;
5068 }
5069
5070 return _IsPrivateFieldKey(fieldKey);
5071 }
5072
5073 bool
_HasAuthoredValue(const TfToken & fieldKey,const SdfPropertySpecHandleVector & propStack)5074 _HasAuthoredValue(const TfToken& fieldKey,
5075 const SdfPropertySpecHandleVector& propStack)
5076 {
5077 return std::any_of(
5078 propStack.begin(), propStack.end(),
5079 [&fieldKey](const SdfPropertySpecHandle& spec) {
5080 return spec->HasInfo(fieldKey);
5081 });
5082 }
5083
5084 void
_CopyFallbacks(const SdfPropertySpecHandle & srcPropDef,const SdfPropertySpecHandle & dstPropDef,const SdfPropertySpecHandle & dstPropSpec,const SdfPropertySpecHandleVector & dstPropStack)5085 _CopyFallbacks(const SdfPropertySpecHandle &srcPropDef,
5086 const SdfPropertySpecHandle &dstPropDef,
5087 const SdfPropertySpecHandle &dstPropSpec,
5088 const SdfPropertySpecHandleVector &dstPropStack)
5089 {
5090 if (!srcPropDef) {
5091 return;
5092 }
5093
5094 std::vector<TfToken> fallbackFields = srcPropDef->ListFields();
5095 fallbackFields.erase(
5096 std::remove_if(fallbackFields.begin(), fallbackFields.end(),
5097 _IsPrivateFallbackFieldKey),
5098 fallbackFields.end());
5099
5100 UsdMetadataValueMap fallbacks;
5101 for (const auto& fieldName : fallbackFields) {
5102 // If the property spec already has a value for this field,
5103 // don't overwrite it with the fallback.
5104 if (dstPropSpec->HasField(fieldName)) {
5105 continue;
5106 }
5107
5108 // If we're flattening over a builtin property and the
5109 // fallback for that property matches the source fallback
5110 // and there isn't an authored value that's overriding that
5111 // fallback, we don't need to write the fallback.
5112 VtValue fallbackVal = srcPropDef->GetField(fieldName);
5113 if (dstPropDef && dstPropDef->GetField(fieldName) == fallbackVal &&
5114 !_HasAuthoredValue(fieldName, dstPropStack)) {
5115 continue;
5116 }
5117
5118 fallbacks[fieldName].Swap(fallbackVal);
5119 }
5120
5121 _CopyMetadata(dstPropSpec, fallbacks);
5122 }
5123
5124 } // end anonymous namespace
5125
5126 bool
ExportToString(std::string * result,bool addSourceFileComment) const5127 UsdStage::ExportToString(std::string *result, bool addSourceFileComment) const
5128 {
5129 SdfLayerRefPtr flatLayer = Flatten(addSourceFileComment);
5130 return flatLayer->ExportToString(result);
5131 }
5132
5133 bool
Export(const std::string & newFileName,bool addSourceFileComment,const SdfLayer::FileFormatArguments & args) const5134 UsdStage::Export(const std::string & newFileName, bool addSourceFileComment,
5135 const SdfLayer::FileFormatArguments &args) const
5136 {
5137 SdfLayerRefPtr flatLayer = Flatten(addSourceFileComment);
5138 return flatLayer->Export(newFileName, /* comment = */ std::string(), args);
5139 }
5140
5141 SdfLayerRefPtr
Flatten(bool addSourceFileComment) const5142 UsdStage::Flatten(bool addSourceFileComment) const
5143 {
5144 TRACE_FUNCTION();
5145
5146 SdfLayerHandle rootLayer = GetRootLayer();
5147 SdfLayerRefPtr flatLayer = SdfLayer::CreateAnonymous(".usda");
5148
5149 if (!TF_VERIFY(rootLayer)) {
5150 return TfNullPtr;
5151 }
5152
5153 if (!TF_VERIFY(flatLayer)) {
5154 return TfNullPtr;
5155 }
5156
5157 // Preemptively populate our mapping. This allows us to populate
5158 // nested instances in the destination layer much more simply.
5159 const auto prototypeToFlattened =
5160 _GenerateFlattenedPrototypePath(GetPrototypes());
5161
5162 // We author the prototype overs first to produce simpler
5163 // assets which have them grouped at the top of the file.
5164 for (auto const& prototype : GetPrototypes()) {
5165 _CopyPrototypePrim(prototype, flatLayer, prototypeToFlattened);
5166 }
5167
5168 for (UsdPrim prim: UsdPrimRange::AllPrims(GetPseudoRoot())) {
5169 _CopyPrim(prim, flatLayer, prim.GetPath(), prototypeToFlattened);
5170 }
5171
5172 if (addSourceFileComment) {
5173 std::string doc = flatLayer->GetDocumentation();
5174
5175 if (!doc.empty()) {
5176 doc.append("\n\n");
5177 }
5178
5179 doc.append(TfStringPrintf("Generated from Composed Stage "
5180 "of root layer %s\n",
5181 GetRootLayer()->GetRealPath().c_str()));
5182
5183 flatLayer->SetDocumentation(doc);
5184 }
5185
5186 return flatLayer;
5187 }
5188
5189 UsdProperty
_FlattenProperty(const UsdProperty & srcProp,const UsdPrim & dstParent,const TfToken & dstName)5190 UsdStage::_FlattenProperty(const UsdProperty &srcProp,
5191 const UsdPrim &dstParent, const TfToken &dstName)
5192 {
5193 if (!srcProp) {
5194 TF_CODING_ERROR("Cannot flatten invalid property <%s>",
5195 UsdDescribe(srcProp).c_str());
5196 return UsdProperty();
5197 }
5198
5199 if (!dstParent) {
5200 TF_CODING_ERROR("Cannot flatten property <%s> to invalid %s",
5201 UsdDescribe(srcProp).c_str(),
5202 UsdDescribe(dstParent).c_str());
5203 return UsdProperty();
5204 }
5205
5206 // Keep track of the pre-existing property stack for the destination
5207 // property if any -- we use this later to determine if we need to
5208 // stamp out the fallback values from the source property.
5209 SdfPropertySpecHandleVector dstPropStack;
5210 if (UsdProperty dstProp = dstParent.GetProperty(dstName)) {
5211 if ((srcProp.Is<UsdAttribute>() && !dstProp.Is<UsdAttribute>()) ||
5212 (srcProp.Is<UsdRelationship>() && !dstProp.Is<UsdRelationship>())) {
5213 TF_CODING_ERROR("Cannot flatten %s to %s because they are "
5214 "different property types",
5215 UsdDescribe(srcProp).c_str(),
5216 UsdDescribe(dstProp).c_str());
5217 return UsdProperty();
5218 }
5219
5220 dstPropStack = dstProp.GetPropertyStack();
5221 }
5222
5223 UsdProperty dstProp;
5224 {
5225 SdfChangeBlock block;
5226
5227 // Use the edit target from the destination prim's stage, since it may
5228 // be different from this stage
5229 SdfPrimSpecHandle primSpec =
5230 dstParent.GetStage()->_CreatePrimSpecForEditing(dstParent);
5231 if (!primSpec) {
5232 // _CreatePrimSpecForEditing will have already issued any
5233 // coding errors, so just bail out.
5234 return UsdProperty();
5235 }
5236
5237 if (SdfPropertySpecHandle dstPropSpec =
5238 primSpec->GetProperties()[dstName]) {
5239 // Ignore the pre-existing property spec when determining
5240 // whether to stamp out fallback values.
5241 dstPropStack.erase(
5242 std::remove(dstPropStack.begin(), dstPropStack.end(),
5243 dstPropSpec),
5244 dstPropStack.end());
5245
5246 // Clear out the existing property spec unless we're flattening
5247 // over the source property. In that case, we don't want to
5248 // remove the property spec because its authored opinions should
5249 // be considered when flattening. This won't leave behind any
5250 // unwanted opinions since we'll be overwriting all of the
5251 // destination property spec's fields anyway in this case.
5252 const bool flatteningToSelf =
5253 srcProp.GetPrim() == dstParent && srcProp.GetName() == dstName;
5254 if (!flatteningToSelf) {
5255 primSpec->RemoveProperty(dstPropSpec);
5256 }
5257 }
5258
5259 // Set up a path remapping so that attribute connections or
5260 // relationships targeting an object beneath the old parent prim
5261 // now target objects beneath the new parent prim.
5262 _PathRemapping remapping;
5263 if (srcProp.GetPrim() != dstParent) {
5264 remapping[srcProp.GetPrimPath()] = dstParent.GetPath();
5265 }
5266
5267 // Apply offsets that affect the edit target to flattened time
5268 // samples to ensure they resolve to the expected value.
5269 // Use the edit target from the destination prim's stage, since it may
5270 // be different from this stage.
5271 const SdfLayerOffset stageToLayerOffset =
5272 dstParent.GetStage()->GetEditTarget().GetMapFunction().
5273 GetTimeOffset().GetInverse();
5274
5275 // Copy authored property values and metadata.
5276 _CopyProperty(srcProp, primSpec, dstName, remapping, stageToLayerOffset);
5277 SdfPropertySpecHandle dstPropSpec =
5278 primSpec->GetProperties().get(dstName);
5279 if (!dstPropSpec) {
5280 return UsdProperty();
5281 }
5282
5283 dstProp = dstParent.GetProperty(dstName);
5284
5285 // Copy fallback property values and metadata if needed.
5286 _CopyFallbacks(
5287 _GetSchemaPropertySpec(srcProp),
5288 _GetSchemaPropertySpec(dstProp),
5289 dstPropSpec, dstPropStack);
5290 }
5291
5292 return dstProp;
5293 }
5294
5295 const PcpPrimIndex*
_GetPcpPrimIndex(const SdfPath & primPath) const5296 UsdStage::_GetPcpPrimIndex(const SdfPath& primPath) const
5297 {
5298 return _cache->FindPrimIndex(primPath);
5299 }
5300
5301 // ========================================================================== //
5302 // VALUE RESOLUTION //
5303 // ========================================================================== //
5304
5305 //
5306 // Helper template function for determining type names from arbitrary pointer
5307 // types, which may include SdfAbstractDataValue and VtValue.
5308 //
5309 static const std::type_info &
_GetTypeid(const SdfAbstractDataValue * val)5310 _GetTypeid(const SdfAbstractDataValue *val) { return val->valueType; }
5311
5312 static const std::type_info &
_GetTypeid(const VtValue * val)5313 _GetTypeid(const VtValue *val) { return val->GetTypeid(); }
5314
5315 template <class T, class Holder>
_IsHolding(const Holder & holder)5316 static bool _IsHolding(const Holder &holder) {
5317 return TfSafeTypeCompare(typeid(T), _GetTypeid(holder));
5318 }
5319
5320 template <class T>
_UncheckedGet(const SdfAbstractDataValue * val)5321 static const T &_UncheckedGet(const SdfAbstractDataValue *val) {
5322 return *static_cast<T const *>(val->value);
5323 }
5324 template <class T>
_UncheckedGet(const VtValue * val)5325 static const T &_UncheckedGet(const VtValue *val) {
5326 return val->UncheckedGet<T>();
5327 }
5328
5329 template <class T>
_UncheckedSwap(SdfAbstractDataValue * dv,T & val)5330 void _UncheckedSwap(SdfAbstractDataValue *dv, T& val) {
5331 using namespace std;
5332 swap(*static_cast<T*>(dv->value), val);
5333 }
5334 template <class T>
_UncheckedSwap(VtValue * value,T & val)5335 void _UncheckedSwap(VtValue *value, T& val) {
5336 value->UncheckedSwap(val);
5337 }
5338
5339 namespace {
5340
5341 // Helper for lazily computing and caching the layer to stage offset for the
5342 // value resolution functions below. This allows to only resolve the layer
5343 // offset once we've determined that a value is holding a type that can be
5344 // resolved layer offsets while caching this computation for types that may
5345 // use it multiple times (e.g. SdfTimeCodeMap and VtDictionary)
5346 struct LayerOffsetAccess
5347 {
5348 public:
LayerOffsetAccess__anon7d3fbd5f1e11::LayerOffsetAccess5349 LayerOffsetAccess(const PcpNodeRef &node, const SdfLayerHandle &layer)
5350 : _node(node), _layer(layer), _hasLayerOffset(false) {}
5351
Get__anon7d3fbd5f1e11::LayerOffsetAccess5352 const SdfLayerOffset & Get() const {
5353 // Compute once and cache.
5354 if (!_hasLayerOffset){
5355 _hasLayerOffset = true;
5356 _layerOffset = _GetLayerToStageOffset(_node, _layer);
5357 }
5358 return _layerOffset;
5359 }
5360
5361 private:
5362 // Private helper meant to be transient so store references to inputs.
5363 const PcpNodeRef _node;
5364 const SdfLayerHandle _layer;
5365
5366 mutable SdfLayerOffset _layerOffset;
5367 mutable bool _hasLayerOffset;
5368 };
5369 }; // end anonymous namespace
5370
5371 static void
_ResolveAssetPath(SdfAssetPath * v,const ArResolverContext & context,const SdfLayerRefPtr & layer,bool anchorAssetPathsOnly)5372 _ResolveAssetPath(SdfAssetPath *v,
5373 const ArResolverContext &context,
5374 const SdfLayerRefPtr &layer,
5375 bool anchorAssetPathsOnly)
5376 {
5377 _MakeResolvedAssetPathsImpl(
5378 layer, context, v, 1, anchorAssetPathsOnly);
5379 }
5380
5381 static void
_ResolveAssetPath(VtArray<SdfAssetPath> * v,const ArResolverContext & context,const SdfLayerRefPtr & layer,bool anchorAssetPathsOnly)5382 _ResolveAssetPath(VtArray<SdfAssetPath> *v,
5383 const ArResolverContext &context,
5384 const SdfLayerRefPtr &layer,
5385 bool anchorAssetPathsOnly)
5386 {
5387 _MakeResolvedAssetPathsImpl(
5388 layer, context, v->data(), v->size(), anchorAssetPathsOnly);
5389 }
5390
5391 template <class T, class Storage>
5392 static void
_UncheckedResolveAssetPath(Storage storage,const ArResolverContext & context,const SdfLayerRefPtr & layer,bool anchorAssetPathsOnly)5393 _UncheckedResolveAssetPath(Storage storage,
5394 const ArResolverContext &context,
5395 const SdfLayerRefPtr &layer,
5396 bool anchorAssetPathsOnly)
5397 {
5398 T v;
5399 _UncheckedSwap(storage, v);
5400 _ResolveAssetPath(&v, context, layer, anchorAssetPathsOnly);
5401 _UncheckedSwap(storage, v);
5402 }
5403
5404 template <class T, class Storage>
5405 static bool
_TryResolveAssetPath(Storage storage,const ArResolverContext & context,const SdfLayerRefPtr & layer,bool anchorAssetPathsOnly)5406 _TryResolveAssetPath(Storage storage,
5407 const ArResolverContext &context,
5408 const SdfLayerRefPtr &layer,
5409 bool anchorAssetPathsOnly)
5410 {
5411 if (_IsHolding<T>(storage)) {
5412 _UncheckedResolveAssetPath<T>(
5413 storage, context, layer, anchorAssetPathsOnly);
5414 return true;
5415 }
5416 return false;
5417 }
5418
5419 // Tries to resolve the asset path in storage if it's holding an asset path
5420 // type. Returns true if the value is holding an asset path type.
5421 template <class Storage>
5422 static bool
_TryResolveAssetPaths(Storage storage,const ArResolverContext & context,const SdfLayerRefPtr & layer,bool anchorAssetPathsOnly)5423 _TryResolveAssetPaths(Storage storage,
5424 const ArResolverContext &context,
5425 const SdfLayerRefPtr &layer,
5426 bool anchorAssetPathsOnly)
5427 {
5428 return
5429 _TryResolveAssetPath<SdfAssetPath>(
5430 storage, context, layer, anchorAssetPathsOnly) ||
5431 _TryResolveAssetPath<VtArray<SdfAssetPath>>(
5432 storage, context, layer, anchorAssetPathsOnly);
5433 }
5434
5435 template <class T, class Storage>
5436 static void
_UncheckedApplyLayerOffsetToValue(Storage storage,const SdfLayerOffset & offset)5437 _UncheckedApplyLayerOffsetToValue(Storage storage,
5438 const SdfLayerOffset &offset)
5439 {
5440 if (!offset.IsIdentity()) {
5441 T v;
5442 _UncheckedSwap(storage, v);
5443 Usd_ApplyLayerOffsetToValue(&v, offset);
5444 _UncheckedSwap(storage, v);
5445 }
5446 }
5447
5448 // Tries to apply the layer offset to the value in storage if its holding the
5449 // templated class type. Returns true if the value is holding the specified
5450 // type.
5451 template <class T, class Storage>
5452 static bool
_TryApplyLayerOffsetToValue(Storage storage,const LayerOffsetAccess & offsetAccess)5453 _TryApplyLayerOffsetToValue(Storage storage,
5454 const LayerOffsetAccess &offsetAccess)
5455 {
5456 if (_IsHolding<T>(storage)) {
5457 const SdfLayerOffset &offset = offsetAccess.Get();
5458 _UncheckedApplyLayerOffsetToValue<T>(storage, offset);
5459 return true;
5460 }
5461 return false;
5462 }
5463
5464 // Tries to resolve the time code(s) in storage with the layer offset if it's
5465 // holding an time code type. Returns true if the value is holding a time code
5466 // type.
5467 template <class Storage>
5468 static bool
_TryResolveTimeCodes(Storage storage,const LayerOffsetAccess & offsetAccess)5469 _TryResolveTimeCodes(Storage storage, const LayerOffsetAccess &offsetAccess)
5470 {
5471 return
5472 _TryApplyLayerOffsetToValue<SdfTimeCode>(storage, offsetAccess) ||
5473 _TryApplyLayerOffsetToValue<VtArray<SdfTimeCode>>(storage, offsetAccess);
5474 }
5475
5476 // If the given dictionary contains any resolvable values, fills in those values
5477 // with their resolved paths.
5478 static void
_ResolveValuesInDictionary(const SdfLayerRefPtr & anchor,const ArResolverContext & context,const LayerOffsetAccess * offsetAccess,VtDictionary * dict,bool anchorAssetPathsOnly)5479 _ResolveValuesInDictionary(const SdfLayerRefPtr &anchor,
5480 const ArResolverContext &context,
5481 const LayerOffsetAccess *offsetAccess,
5482 VtDictionary *dict,
5483 bool anchorAssetPathsOnly)
5484 {
5485 // If there is no layer offset, don't bother with resolving time codes and
5486 // just resolve asset paths.
5487 if (offsetAccess) {
5488 Usd_ResolveValuesInDictionary(dict,
5489 [&anchor, &context, &offsetAccess, &anchorAssetPathsOnly]
5490 (VtValue *value)
5491 {
5492 _TryResolveAssetPaths(
5493 value, context, anchor, anchorAssetPathsOnly) ||
5494 _TryResolveTimeCodes(value, *offsetAccess);
5495 });
5496 } else {
5497 Usd_ResolveValuesInDictionary(dict,
5498 [&anchor, &context, &anchorAssetPathsOnly](VtValue *value)
5499 {
5500 _TryResolveAssetPaths(
5501 value, context, anchor, anchorAssetPathsOnly);
5502 });
5503 }
5504 }
5505
5506 // Tries to resolve all the resolvable values contained within a VtDictionary in
5507 // storage. Returns true if the value is holding a VtDictionary.
5508 template <class Storage>
5509 static bool
_TryResolveValuesInDictionary(Storage storage,const SdfLayerRefPtr & anchor,const ArResolverContext & context,const LayerOffsetAccess * offsetAccess,bool anchorAssetPathsOnly)5510 _TryResolveValuesInDictionary(Storage storage,
5511 const SdfLayerRefPtr &anchor,
5512 const ArResolverContext &context,
5513 const LayerOffsetAccess *offsetAccess,
5514 bool anchorAssetPathsOnly)
5515 {
5516 if (_IsHolding<VtDictionary>(storage)) {
5517 VtDictionary resolvedDict;
5518 _UncheckedSwap(storage, resolvedDict);
5519 _ResolveValuesInDictionary(
5520 anchor, context, offsetAccess, &resolvedDict, anchorAssetPathsOnly);
5521 _UncheckedSwap(storage, resolvedDict);
5522 return true;
5523 }
5524 return false;
5525 }
5526
5527
5528 namespace {
5529
5530 // Non-virtual value composer base class. Helps provide shared functionality
5531 // amongst the different derived value composer classed. The derived classes
5532 // must all implement a ConsumeAuthored and ConsumeUsdFallback function.
5533 template <class Storage>
5534 struct ValueComposerBase
5535 {
5536 static const bool ProducesValue = true;
5537
GetHeldTypeid__anon7d3fbd5f2111::ValueComposerBase5538 const std::type_info& GetHeldTypeid() const { return _GetTypeid(_value); }
IsDone__anon7d3fbd5f2111::ValueComposerBase5539 bool IsDone() const { return _done; }
5540
5541 template <class ValueType>
ConsumeExplicitValue__anon7d3fbd5f2111::ValueComposerBase5542 void ConsumeExplicitValue(ValueType type)
5543 {
5544 Usd_SetValue(_value, type);
5545 _done = true;
5546 }
5547
5548 protected:
5549 // Protected constructor.
ValueComposerBase__anon7d3fbd5f2111::ValueComposerBase5550 explicit ValueComposerBase(Storage s, bool anchorAssetPathsOnly = false)
5551 : _value(s), _done(false), _anchorAssetPathsOnly(anchorAssetPathsOnly)
5552 {}
5553
5554 // Gets the value from the layer spec.
_GetValue__anon7d3fbd5f2111::ValueComposerBase5555 bool _GetValue(const SdfLayerRefPtr &layer,
5556 const SdfPath &specPath,
5557 const TfToken &fieldName,
5558 const TfToken &keyPath)
5559 {
5560 return keyPath.IsEmpty() ?
5561 layer->HasField(specPath, fieldName, _value) :
5562 layer->HasFieldDictKey(specPath, fieldName, keyPath, _value);
5563 }
5564
5565 // Gets the fallback value for the property
_GetFallbackValue__anon7d3fbd5f2111::ValueComposerBase5566 bool _GetFallbackValue(const UsdPrimDefinition &primDef,
5567 const TfToken &propName,
5568 const TfToken &fieldName,
5569 const TfToken &keyPath)
5570 {
5571 // Try to read fallback value.
5572 return Usd_GetFallbackValue(
5573 primDef, propName, fieldName, keyPath, _value);
5574 }
5575
5576 // Consumes an authored dictionary value and merges it into the current
5577 // strongest dictionary value.
_ConsumeAndMergeAuthoredDictionary__anon7d3fbd5f2111::ValueComposerBase5578 bool _ConsumeAndMergeAuthoredDictionary(const PcpNodeRef &node,
5579 const SdfLayerRefPtr &layer,
5580 const SdfPath &specPath,
5581 const TfToken &fieldName,
5582 const TfToken &keyPath)
5583 {
5584 // Copy to the side since we'll have to merge if the next opinion
5585 // is also a dictionary.
5586 VtDictionary tmpDict = _UncheckedGet<VtDictionary>(_value);
5587
5588 // Try to read value from scene description.
5589 if (_GetValue(layer, specPath, fieldName, keyPath)) {
5590 const ArResolverContext &context =
5591 node.GetLayerStack()->GetIdentifier().pathResolverContext;
5592 // Create a layer offset accessor so we don't compute the layer
5593 // offset unless one of the resolve functions actually needs it.
5594 LayerOffsetAccess layerOffsetAccess(node, layer);
5595
5596 // Try resolving the values in the dictionary.
5597 if (_TryResolveValuesInDictionary(
5598 _value, layer, context, &layerOffsetAccess,
5599 _anchorAssetPathsOnly)) {
5600 // Merge the resolved dictionary.
5601 VtDictionaryOverRecursive(
5602 &tmpDict, _UncheckedGet<VtDictionary>(_value));
5603 _UncheckedSwap(_value, tmpDict);
5604 }
5605 return true;
5606 }
5607 return false;
5608 }
5609
5610 // Consumes the fallback dictionary value and merges it into the current
5611 // dictionary value.
_ConsumeAndMergeFallbackDictionary__anon7d3fbd5f2111::ValueComposerBase5612 void _ConsumeAndMergeFallbackDictionary(
5613 const UsdPrimDefinition &primDef,
5614 const TfToken &propName,
5615 const TfToken &fieldName,
5616 const TfToken &keyPath)
5617 {
5618 // Copy to the side since we'll have to merge if the next opinion is
5619 // also a dictionary.
5620 VtDictionary tmpDict = _UncheckedGet<VtDictionary>(_value);
5621
5622 // Try to read fallback value.
5623 if(_GetFallbackValue(primDef, propName, fieldName, keyPath)) {
5624 // Always done after reading the fallback value.
5625 _done = true;
5626 if (_IsHolding<VtDictionary>(_value)) {
5627 // Merge dictionaries: _value is weaker, tmpDict stronger.
5628 VtDictionaryOverRecursive(&tmpDict,
5629 _UncheckedGet<VtDictionary>(_value));
5630 _UncheckedSwap(_value, tmpDict);
5631 }
5632 }
5633 }
5634
5635 Storage _value;
5636 bool _done;
5637 bool _anchorAssetPathsOnly;
5638 };
5639
5640 // Value composer for a type erased VtValue. This will check the type
5641 // of the stored value and do the appropriate value composition for the type.
5642 struct UntypedValueComposer : public ValueComposerBase<VtValue *>
5643 {
5644 using Base = ValueComposerBase<VtValue *>;
5645
UntypedValueComposer__anon7d3fbd5f2111::UntypedValueComposer5646 explicit UntypedValueComposer(
5647 VtValue *s, bool anchorAssetPathsOnly = false)
5648 : Base(s, anchorAssetPathsOnly) {}
5649
ConsumeAuthored__anon7d3fbd5f2111::UntypedValueComposer5650 bool ConsumeAuthored(const PcpNodeRef &node,
5651 const SdfLayerRefPtr &layer,
5652 const SdfPath &specPath,
5653 const TfToken &fieldName,
5654 const TfToken &keyPath)
5655 {
5656 if (_IsHoldingDictionary()) {
5657 // Handle special value-type composition: dictionaries merge atop
5658 // each other.
5659 return this->_ConsumeAndMergeAuthoredDictionary(
5660 node, layer, specPath, fieldName, keyPath);
5661 } else {
5662 // Try to read value from scene description and resolve it if needed
5663 // if the value is found.
5664 if (this->_GetValue(layer, specPath, fieldName, keyPath)) {
5665 // We're done if we got value and it's not a dictionary. For
5666 // dictionaries we'll continue to merge in weaker dictionaries.
5667 if (!_IsHoldingDictionary()) {
5668 this->_done = true;
5669 }
5670 _ResolveValue(node, layer);
5671 return true;
5672 }
5673 return false;
5674 }
5675 }
5676
ConsumeUsdFallback__anon7d3fbd5f2111::UntypedValueComposer5677 void ConsumeUsdFallback(const UsdPrimDefinition &primDef,
5678 const TfToken &propName,
5679 const TfToken &fieldName,
5680 const TfToken &keyPath)
5681 {
5682 if (_IsHoldingDictionary()) {
5683 // Handle special value-type composition: fallback dictionaries
5684 // are merged into the current dictionary value..
5685 this->_ConsumeAndMergeFallbackDictionary(
5686 primDef, propName, fieldName, keyPath);
5687 } else {
5688 // Try to read fallback value. Fallbacks are not resolved.
5689 this->_done = this->_GetFallbackValue(
5690 primDef, propName, fieldName, keyPath);
5691 }
5692 }
5693
5694 protected:
_IsHoldingDictionary__anon7d3fbd5f2111::UntypedValueComposer5695 bool _IsHoldingDictionary() const {
5696 return _IsHolding<VtDictionary>(this->_value);
5697 }
5698
_ResolveValue__anon7d3fbd5f2111::UntypedValueComposer5699 void _ResolveValue(const PcpNodeRef &node, const SdfLayerRefPtr &layer)
5700 {
5701 const ArResolverContext &context =
5702 node.GetLayerStack()->GetIdentifier().pathResolverContext;
5703 // Create a layer offset accessor so we don't compute the layer
5704 // offset unless one of the resolve functions actually needs it.
5705 LayerOffsetAccess layerOffsetAccess(node, layer);
5706
5707 // Since we don't know the type, we have to try to resolve the
5708 // consumed value for all the types that require additional
5709 // value resolution.
5710
5711 // Try resolving the value as a dictionary first. Note that even though
5712 // we have a special case in ConsumeAuthored for when the value is
5713 // holding a dictionary, we still have to check for dictionary values
5714 // here to the cover the case when the storage container starts as an
5715 // empty VtValue.
5716 if (_TryResolveValuesInDictionary(
5717 this->_value, layer, context, &layerOffsetAccess,
5718 this->_anchorAssetPathsOnly)) {
5719 } else {
5720 // Otherwise try resolving each of the the other resolvable
5721 // types.
5722 _TryApplyLayerOffsetToValue<SdfTimeSampleMap>(
5723 this->_value, layerOffsetAccess) ||
5724 _TryResolveAssetPaths(
5725 this->_value, context, layer, this->_anchorAssetPathsOnly) ||
5726 _TryResolveTimeCodes(this->_value, layerOffsetAccess);
5727 }
5728 }
5729 };
5730
5731 // Strongest value composer for a SdfAbstractDataValue holding a type we know
5732 // does not need type specific value resolution. The data value is type erased
5733 // since this composer only needs to get the strongest value. For value types
5734 // with, type specific value resolution, the TypeSpecficValueComposer must be
5735 // used instead to get a correctly resolved value.
5736 struct StrongestValueComposer : public ValueComposerBase<SdfAbstractDataValue *>
5737 {
5738 using Base = ValueComposerBase<SdfAbstractDataValue *>;
5739
StrongestValueComposer__anon7d3fbd5f2111::StrongestValueComposer5740 explicit StrongestValueComposer(SdfAbstractDataValue *s)
5741 : Base(s, /* anchorAssetPathsOnly = */ false) {}
5742
5743
ConsumeAuthored__anon7d3fbd5f2111::StrongestValueComposer5744 bool ConsumeAuthored(const PcpNodeRef &node,
5745 const SdfLayerRefPtr &layer,
5746 const SdfPath &specPath,
5747 const TfToken &fieldName,
5748 const TfToken &keyPath)
5749 {
5750 // Try to read value from scene description, we're done if the value
5751 // is found.
5752 if (this->_GetValue(layer, specPath, fieldName, keyPath)) {
5753 this->_done = true;
5754 return true;
5755 }
5756 return false;
5757 }
5758
ConsumeUsdFallback__anon7d3fbd5f2111::StrongestValueComposer5759 void ConsumeUsdFallback(const UsdPrimDefinition &primDef,
5760 const TfToken &propName,
5761 const TfToken &fieldName,
5762 const TfToken &keyPath)
5763 {
5764 this->_done = this->_GetFallbackValue(
5765 primDef, propName, fieldName, keyPath);
5766 }
5767 };
5768
5769 // Value composer for a storage container whose type requires type specific
5770 // value resolution. If this composer is used for a type that does not have
5771 // type specific value resolution then this is equivalent to using a
5772 // StrongestValueComposer. For types that have type specific value resolution,
5773 // this is specialized to perform the appropriate resolution.
5774 template <class T>
5775 struct TypeSpecificValueComposer :
5776 public ValueComposerBase<SdfAbstractDataValue *>
5777 {
5778 using Base = ValueComposerBase<SdfAbstractDataValue *>;
5779 friend Base;
5780
TypeSpecificValueComposer__anon7d3fbd5f2111::TypeSpecificValueComposer5781 explicit TypeSpecificValueComposer(SdfAbstractDataTypedValue<T> *s)
5782 : Base(s, /*anchorAssetPathsOnly = */ false) {}
5783
ConsumeAuthored__anon7d3fbd5f2111::TypeSpecificValueComposer5784 bool ConsumeAuthored(const PcpNodeRef &node,
5785 const SdfLayerRefPtr &layer,
5786 const SdfPath &specPath,
5787 const TfToken &fieldName,
5788 const TfToken &keyPath)
5789 {
5790 // Try to read value from scene description and resolve it if needed
5791 // if the value is found.
5792 if (this->_GetValue(layer, specPath, fieldName, keyPath)) {
5793 // We're done if we got value and it's not a dictionary. For
5794 // dictionaries we'll continue to merge in weaker dictionaries.
5795 this->_done = true;
5796 _ResolveValue(node, layer);
5797 return true;
5798 }
5799 return false;
5800 }
5801
ConsumeUsdFallback__anon7d3fbd5f2111::TypeSpecificValueComposer5802 void ConsumeUsdFallback(const UsdPrimDefinition &primDef,
5803 const TfToken &propName,
5804 const TfToken &fieldName,
5805 const TfToken &keyPath)
5806 {
5807 this->_done = this->_GetFallbackValue(
5808 primDef, propName, fieldName, keyPath);
5809 }
5810
5811 protected:
5812 // Implementation for the base class.
_ResolveValue__anon7d3fbd5f2111::TypeSpecificValueComposer5813 void _ResolveValue(const PcpNodeRef &node,
5814 const SdfLayerRefPtr &layer)
5815 {
5816 // The default for almost all types is to do no extra value resolution.
5817 // The few types that require resolution must either specialize this
5818 // method or reimplement ConsumeAuthored and delete this method.
5819 static_assert(!UsdStage::_HasTypeSpecificResolution<T>::value,
5820 "Value types that have type specific value resolution "
5821 "must either specialize _ResolveValue or delete it and "
5822 "reimplement ConsumeAuthored");
5823 }
5824 };
5825
5826 // Specializations of _ResolveValue for type specific value resolution types.
5827 // Note that we can assume that _value always holds the template value type so
5828 // there is no value type checking.
5829 // We may, however, want to skip these resolves when _value.isValueBlock is true
5830 template <>
5831 void
_ResolveValue(const PcpNodeRef & node,const SdfLayerRefPtr & layer)5832 TypeSpecificValueComposer<SdfAssetPath>::_ResolveValue(
5833 const PcpNodeRef &node,
5834 const SdfLayerRefPtr &layer)
5835 {
5836 const ArResolverContext &context =
5837 node.GetLayerStack()->GetIdentifier().pathResolverContext;
5838 _UncheckedResolveAssetPath<SdfAssetPath>(
5839 _value, context, layer, /*anchorAssetPathsOnly = */ false);
5840 }
5841
5842 template <>
5843 void
_ResolveValue(const PcpNodeRef & node,const SdfLayerRefPtr & layer)5844 TypeSpecificValueComposer<VtArray<SdfAssetPath>>::_ResolveValue(
5845 const PcpNodeRef &node,
5846 const SdfLayerRefPtr &layer)
5847 {
5848 const ArResolverContext &context =
5849 node.GetLayerStack()->GetIdentifier().pathResolverContext;
5850 _UncheckedResolveAssetPath<VtArray<SdfAssetPath>>(
5851 _value, context, layer, /*anchorAssetPathsOnly = */ false);
5852 }
5853
5854 template <>
5855 void
_ResolveValue(const PcpNodeRef & node,const SdfLayerRefPtr & layer)5856 TypeSpecificValueComposer<SdfTimeCode>::_ResolveValue(
5857 const PcpNodeRef &node,
5858 const SdfLayerRefPtr &layer)
5859 {
5860 SdfLayerOffset offset = _GetLayerToStageOffset(node, layer);
5861 _UncheckedApplyLayerOffsetToValue<SdfTimeCode>(_value, offset);
5862 }
5863
5864 template <>
5865 void
_ResolveValue(const PcpNodeRef & node,const SdfLayerRefPtr & layer)5866 TypeSpecificValueComposer<VtArray<SdfTimeCode>>::_ResolveValue(
5867 const PcpNodeRef &node,
5868 const SdfLayerRefPtr &layer)
5869 {
5870 SdfLayerOffset offset = _GetLayerToStageOffset(node, layer);
5871 _UncheckedApplyLayerOffsetToValue<VtArray<SdfTimeCode>>(_value, offset);
5872 }
5873
5874 template <>
5875 void
_ResolveValue(const PcpNodeRef & node,const SdfLayerRefPtr & layer)5876 TypeSpecificValueComposer<SdfTimeSampleMap>::_ResolveValue(
5877 const PcpNodeRef &node,
5878 const SdfLayerRefPtr &layer)
5879 {
5880 SdfLayerOffset offset = _GetLayerToStageOffset(node, layer);
5881 _UncheckedApplyLayerOffsetToValue<SdfTimeSampleMap>(_value, offset);
5882 }
5883
5884 // The TypeSpecificValueComposer for VtDictionary has additional specialization
5885 // for consuming values as it merges in weaker values unlike most types that
5886 // only consume the strongest value.
5887 template <>
5888 bool
ConsumeAuthored(const PcpNodeRef & node,const SdfLayerRefPtr & layer,const SdfPath & specPath,const TfToken & fieldName,const TfToken & keyPath)5889 TypeSpecificValueComposer<VtDictionary>::ConsumeAuthored(
5890 const PcpNodeRef &node,
5891 const SdfLayerRefPtr &layer,
5892 const SdfPath &specPath,
5893 const TfToken &fieldName,
5894 const TfToken &keyPath)
5895 {
5896 // Handle special value-type composition: dictionaries merge atop
5897 // each other.
5898 return this->_ConsumeAndMergeAuthoredDictionary(
5899 node, layer, specPath, fieldName, keyPath);
5900 }
5901
5902 template <>
5903 void
ConsumeUsdFallback(const UsdPrimDefinition & primDef,const TfToken & propName,const TfToken & fieldName,const TfToken & keyPath)5904 TypeSpecificValueComposer<VtDictionary>::ConsumeUsdFallback(
5905 const UsdPrimDefinition &primDef,
5906 const TfToken &propName,
5907 const TfToken &fieldName,
5908 const TfToken &keyPath)
5909 {
5910 // Handle special value-type composition: fallback dictionaries
5911 // are merged into the current dictionary value..
5912 _ConsumeAndMergeFallbackDictionary(
5913 primDef, propName, fieldName, keyPath);
5914 }
5915
5916 template <>
5917 void
5918 TypeSpecificValueComposer<VtDictionary>::_ResolveValue(
5919 const PcpNodeRef &, const SdfLayerRefPtr &) = delete;
5920
5921
5922 struct ExistenceComposer
5923 {
5924 static const bool ProducesValue = false;
5925
ExistenceComposer__anon7d3fbd5f2111::ExistenceComposer5926 ExistenceComposer() : _done(false), _strongestLayer(nullptr) {}
ExistenceComposer__anon7d3fbd5f2111::ExistenceComposer5927 explicit ExistenceComposer(SdfLayerRefPtr *strongestLayer)
5928 : _done(false), _strongestLayer(strongestLayer) {}
5929
GetHeldTypeid__anon7d3fbd5f2111::ExistenceComposer5930 const std::type_info& GetHeldTypeid() const { return typeid(void); }
IsDone__anon7d3fbd5f2111::ExistenceComposer5931 bool IsDone() const { return _done; }
ConsumeAuthored__anon7d3fbd5f2111::ExistenceComposer5932 bool ConsumeAuthored(const PcpNodeRef &node,
5933 const SdfLayerRefPtr &layer,
5934 const SdfPath &specPath,
5935 const TfToken &fieldName,
5936 const TfToken &keyPath,
5937 const SdfLayerOffset & = SdfLayerOffset()) {
5938 _done = keyPath.IsEmpty() ?
5939 layer->HasField(specPath, fieldName,
5940 static_cast<VtValue *>(nullptr)) :
5941 layer->HasFieldDictKey(specPath, fieldName, keyPath,
5942 static_cast<VtValue*>(nullptr));
5943 if (_done && _strongestLayer)
5944 *_strongestLayer = layer;
5945 return _done;
5946 }
ConsumeUsdFallback__anon7d3fbd5f2111::ExistenceComposer5947 void ConsumeUsdFallback(const UsdPrimDefinition &primDef,
5948 const TfToken &propName,
5949 const TfToken &fieldName,
5950 const TfToken &keyPath) {
5951 _done = Usd_GetFallbackValue(primDef, propName, fieldName, keyPath,
5952 static_cast<VtValue *>(nullptr));
5953 if (_strongestLayer)
5954 *_strongestLayer = TfNullPtr;
5955 }
5956 template <class ValueType>
ConsumeExplicitValue__anon7d3fbd5f2111::ExistenceComposer5957 void ConsumeExplicitValue(ValueType type) {
5958 _done = true;
5959 }
5960
5961 protected:
5962 bool _done;
5963 SdfLayerRefPtr *_strongestLayer;
5964 };
5965
5966 }
5967
5968 template <class T>
5969 bool
_SetValueImpl(UsdTimeCode time,const UsdAttribute & attr,const T & newValue)5970 UsdStage::_SetValueImpl(
5971 UsdTimeCode time, const UsdAttribute &attr, const T& newValue)
5972 {
5973 // if we are setting a value block, we don't want type checking
5974 if (!Usd_ValueContainsBlock(&newValue)) {
5975 // Do a type check. Obtain typeName.
5976 TfToken typeName;
5977 SdfAbstractDataTypedValue<TfToken> abstrToken(&typeName);
5978 TypeSpecificValueComposer<TfToken> composer(&abstrToken);
5979 _GetMetadataImpl(attr, SdfFieldKeys->TypeName, TfToken(),
5980 /*useFallbacks=*/true, &composer);
5981
5982 if (typeName.IsEmpty()) {
5983 TF_RUNTIME_ERROR("Empty typeName for <%s>",
5984 attr.GetPath().GetText());
5985 return false;
5986 }
5987 // Ensure this typeName is known to our schema.
5988 TfType valType = SdfSchema::GetInstance().FindType(typeName).GetType();
5989 if (valType.IsUnknown()) {
5990 TF_RUNTIME_ERROR("Unknown typename for <%s>: '%s'",
5991 typeName.GetText(), attr.GetPath().GetText());
5992 return false;
5993 }
5994 // Check that the passed value is the expected type.
5995 if (!TfSafeTypeCompare(_GetTypeInfo(newValue), valType.GetTypeid())) {
5996 TF_CODING_ERROR("Type mismatch for <%s>: expected '%s', got '%s'",
5997 attr.GetPath().GetText(),
5998 ArchGetDemangled(valType.GetTypeid()).c_str(),
5999 ArchGetDemangled(_GetTypeInfo(newValue)).c_str());
6000 return false;
6001 }
6002
6003 // Check variability, but only if the appropriate debug flag is
6004 // enabled. Variability is a statement of intent but doesn't control
6005 // behavior, so we only want to perform this validation when it is
6006 // requested.
6007 if (TfDebug::IsEnabled(USD_VALIDATE_VARIABILITY) &&
6008 time != UsdTimeCode::Default() &&
6009 _GetVariability(attr) == SdfVariabilityUniform) {
6010 TF_DEBUG(USD_VALIDATE_VARIABILITY)
6011 .Msg("Warning: authoring time sample value on "
6012 "uniform attribute <%s> at time %.3f\n",
6013 UsdDescribe(attr).c_str(), time.GetValue());
6014 }
6015 }
6016
6017 SdfAttributeSpecHandle attrSpec = _CreateAttributeSpecForEditing(attr);
6018
6019 if (!attrSpec) {
6020 TF_RUNTIME_ERROR(
6021 "Cannot set attribute value. Failed to create "
6022 "attribute spec <%s> in layer @%s@",
6023 GetEditTarget().MapToSpecPath(attr.GetPath()).GetText(),
6024 GetEditTarget().GetLayer()->GetIdentifier().c_str());
6025 return false;
6026 }
6027
6028 if (time.IsDefault()) {
6029 attrSpec->GetLayer()->SetField(attrSpec->GetPath(),
6030 SdfFieldKeys->Default,
6031 newValue);
6032 } else {
6033 // XXX: should this loft the underlying values up when
6034 // authoring over a weaker layer?
6035
6036 // XXX: this won't be correct if we are trying to edit
6037 // across two different reference arcs -- which may have
6038 // different time offsets. perhaps we need the map function
6039 // to track a time offset for each path?
6040 const SdfLayerOffset stageToLayerOffset =
6041 GetEditTarget().GetMapFunction().GetTimeOffset().GetInverse();
6042
6043 double localTime = stageToLayerOffset * time.GetValue();
6044
6045 attrSpec->GetLayer()->SetTimeSample(
6046 attrSpec->GetPath(),
6047 localTime,
6048 newValue);
6049 }
6050
6051 return true;
6052 }
6053
6054 // --------------------------------------------------------------------- //
6055 // Helpers for Metadata Resolution
6056 // --------------------------------------------------------------------- //
6057
6058 template <class Composer>
6059 static bool
_GetFallbackMetadataImpl(Usd_PrimDataConstPtr primData,const TfToken & propName,const TfToken & fieldName,const TfToken & keyPath,Composer * composer)6060 _GetFallbackMetadataImpl(Usd_PrimDataConstPtr primData,
6061 const TfToken& propName,
6062 const TfToken &fieldName,
6063 const TfToken &keyPath,
6064 Composer *composer)
6065 {
6066 // Look for a fallback value in the definition.
6067 // NOTE: This code is performance critical.
6068 composer->ConsumeUsdFallback(
6069 primData->GetPrimDefinition(), propName, fieldName, keyPath);
6070 return composer->IsDone();
6071 }
6072
6073 template <class Composer>
6074 static bool
_ComposeGeneralMetadataImpl(Usd_PrimDataConstPtr primData,const TfToken & propName,const TfToken & fieldName,const TfToken & keyPath,bool useFallbacks,Usd_Resolver * res,Composer * composer)6075 _ComposeGeneralMetadataImpl(Usd_PrimDataConstPtr primData,
6076 const TfToken& propName,
6077 const TfToken& fieldName,
6078 const TfToken& keyPath,
6079 bool useFallbacks,
6080 Usd_Resolver* res,
6081 Composer *composer)
6082 {
6083 // Main resolution loop.
6084 SdfPath specPath = res->GetLocalPath(propName);
6085 bool gotOpinion = false;
6086
6087 for (bool isNewNode = false; res->IsValid(); isNewNode = res->NextLayer()) {
6088 if (isNewNode) {
6089 specPath = res->GetLocalPath(propName);
6090 }
6091
6092 // Consume an authored opinion here, if one exists.
6093 gotOpinion |= composer->ConsumeAuthored(
6094 res->GetNode(), res->GetLayer(), specPath, fieldName, keyPath);
6095
6096 if (composer->IsDone()) {
6097 return true;
6098 }
6099 }
6100
6101 if (useFallbacks) {
6102 _GetFallbackMetadataImpl(
6103 primData, propName, fieldName, keyPath, composer);
6104 }
6105
6106 return gotOpinion || composer->IsDone();
6107 }
6108
6109 // Special composing for just the pseudoroot. The pseudoroot only composes
6110 // metadata opinions on the absolute root path from the session and root layers.
6111 // Note that the pseudoroot itself doesn't provide fallbacks.
6112 // Returns true if an opinion was found.
6113 template <class Composer>
6114 static bool
_ComposePseudoRootMetadataImpl(Usd_PrimDataConstPtr primData,const TfToken & fieldName,const TfToken & keyPath,const SdfLayerRefPtr & rootLayer,const SdfLayerRefPtr & sessionLayer,Composer * composer)6115 _ComposePseudoRootMetadataImpl(Usd_PrimDataConstPtr primData,
6116 const TfToken& fieldName,
6117 const TfToken& keyPath,
6118 const SdfLayerRefPtr &rootLayer,
6119 const SdfLayerRefPtr &sessionLayer,
6120 Composer *composer)
6121 {
6122 const SdfPath &specPath = SdfPath::AbsoluteRootPath();
6123 bool gotOpinion = false;
6124
6125 PcpNodeRef node = primData->GetPrimIndex().GetRootNode();
6126
6127 // If we a have a session layer and it isn't muted, we try to consume its
6128 // opinion first. The session layer will be the first layer in the
6129 // layer stack unless it is muted.
6130 if (sessionLayer &&
6131 node.GetLayerStack()->GetLayers().front() == sessionLayer) {
6132 // Consume an authored opinion here, if one exists.
6133 gotOpinion = composer->ConsumeAuthored(
6134 node, sessionLayer, specPath, fieldName, keyPath);
6135 if (composer->IsDone()) {
6136 return true;
6137 }
6138 }
6139
6140 // Consume an authored opinion from the root layer (which cannot be muted).
6141 gotOpinion |= composer->ConsumeAuthored(
6142 node, rootLayer, specPath, fieldName, keyPath);
6143
6144 // Return whether we got an opinion from either layer.
6145 return gotOpinion;
6146 }
6147
6148 // --------------------------------------------------------------------- //
6149 // Specialized Metadata Resolution
6150 // --------------------------------------------------------------------- //
6151
6152 template <class Composer>
6153 static bool
6154 _GetPrimSpecifierImpl(Usd_PrimDataConstPtr primData,
6155 bool useFallbacks, Composer *composer);
6156
6157 SdfSpecifier
_GetSpecifier(Usd_PrimDataConstPtr primData)6158 UsdStage::_GetSpecifier(Usd_PrimDataConstPtr primData)
6159 {
6160 SdfSpecifier result = SdfSpecifierOver;
6161 SdfAbstractDataTypedValue<SdfSpecifier> resultVal(&result);
6162 TypeSpecificValueComposer<SdfSpecifier> composer(&resultVal);
6163 _GetPrimSpecifierImpl(primData, /* useFallbacks = */ true, &composer);
6164 return result;
6165 }
6166
6167 template <class Composer>
6168 static bool
_GetPrimKindImpl(Usd_PrimDataConstPtr primData,bool useFallbacks,Composer * composer)6169 _GetPrimKindImpl(Usd_PrimDataConstPtr primData,
6170 bool useFallbacks, Composer *composer)
6171 {
6172 Usd_Resolver resolver(&primData->GetPrimIndex());
6173 return _ComposeGeneralMetadataImpl(
6174 primData, TfToken(), SdfFieldKeys->Kind, TfToken(), useFallbacks,
6175 &resolver, composer);
6176 }
6177
6178 TfToken
_GetKind(Usd_PrimDataConstPtr primData)6179 UsdStage::_GetKind(Usd_PrimDataConstPtr primData)
6180 {
6181 TfToken kind;
6182 SdfAbstractDataTypedValue<TfToken> resultValue(&kind);
6183 TypeSpecificValueComposer<TfToken> composer(&resultValue);
6184
6185 // We don't allow fallbacks for kind.
6186 _GetPrimKindImpl(primData, /* useFallbacks = */ false, &composer);
6187 return kind;
6188 }
6189
6190 template <class Composer>
6191 static bool
_GetPrimActiveImpl(Usd_PrimDataConstPtr primData,bool useFallbacks,Composer * composer)6192 _GetPrimActiveImpl(Usd_PrimDataConstPtr primData,
6193 bool useFallbacks, Composer *composer)
6194 {
6195 Usd_Resolver resolver(&primData->GetPrimIndex());
6196 return _ComposeGeneralMetadataImpl(
6197 primData, TfToken(), SdfFieldKeys->Active, TfToken(), useFallbacks,
6198 &resolver, composer);
6199 }
6200
6201 bool
_IsActive(Usd_PrimDataConstPtr primData)6202 UsdStage::_IsActive(Usd_PrimDataConstPtr primData)
6203 {
6204 bool active = true;
6205 SdfAbstractDataTypedValue<bool> resultValue(&active);
6206 TypeSpecificValueComposer<bool> composer(&resultValue);
6207
6208 // We don't allow fallbacks for active.
6209 _GetPrimActiveImpl(primData, /* useFallbacks = */ false, &composer);
6210 return active;
6211 }
6212
6213 bool
_IsCustom(const UsdProperty & prop) const6214 UsdStage::_IsCustom(const UsdProperty &prop) const
6215 {
6216 // Custom is composed as true if there is no property definition and it is
6217 // true anywhere in the stack of opinions.
6218
6219 if (_GetSchemaPropertySpec(prop))
6220 return false;
6221
6222 const TfToken &propName = prop.GetName();
6223
6224 TF_REVERSE_FOR_ALL(itr, prop.GetPrim().GetPrimIndex().GetNodeRange()) {
6225
6226 if (itr->IsInert() || !itr->HasSpecs()) {
6227 continue;
6228 }
6229
6230 SdfPath specPath = itr->GetPath().AppendProperty(propName);
6231 TF_REVERSE_FOR_ALL(layerIt, itr->GetLayerStack()->GetLayers()) {
6232 bool result = false;
6233 if ((*layerIt)->HasField(specPath, SdfFieldKeys->Custom, &result)
6234 && result) {
6235 return true;
6236 }
6237 }
6238 }
6239
6240 return SdfSchema::GetInstance().GetFieldDefinition(
6241 SdfFieldKeys->Custom)->GetFallbackValue().Get<bool>();
6242 }
6243
6244 SdfVariability
6245 UsdStage
_GetVariability(const UsdProperty & prop) const6246 ::_GetVariability(const UsdProperty &prop) const
6247 {
6248 // The composed variability is the taken from the weakest opinion in the
6249 // stack, unless this is a built-in attribute, in which case the definition
6250 // wins.
6251
6252 if (prop.Is<UsdAttribute>()) {
6253 UsdAttribute attr = prop.As<UsdAttribute>();
6254 // Check definition.
6255 if (SdfAttributeSpecHandle attrDef = _GetSchemaAttributeSpec(attr)) {
6256 return attrDef->GetVariability();
6257 }
6258
6259 // Check authored scene description.
6260 const TfToken &attrName = attr.GetName();
6261 TF_REVERSE_FOR_ALL(itr, attr.GetPrim().GetPrimIndex().GetNodeRange()) {
6262 if (itr->IsInert() || !itr->HasSpecs())
6263 continue;
6264
6265 SdfPath specPath = itr->GetPath().AppendProperty(attrName);
6266 TF_REVERSE_FOR_ALL(layerIt, itr->GetLayerStack()->GetLayers()) {
6267 SdfVariability result;
6268 if ((*layerIt)->HasField(
6269 specPath, SdfFieldKeys->Variability, &result)) {
6270 return result;
6271 }
6272 }
6273 }
6274 }
6275
6276 // Fall back to schema.
6277 return SdfSchema::GetInstance().GetFieldDefinition(
6278 SdfFieldKeys->Variability)->GetFallbackValue().Get<SdfVariability>();
6279 }
6280
6281 // --------------------------------------------------------------------- //
6282 // Metadata Resolution
6283 // --------------------------------------------------------------------- //
6284
6285 // Populates the time sample map with the resolved values for the given
6286 // attribute and returns true if time samples exist, false otherwise.
6287 static bool
_GetTimeSampleMap(const UsdAttribute & attr,SdfTimeSampleMap * out)6288 _GetTimeSampleMap(const UsdAttribute &attr, SdfTimeSampleMap *out)
6289 {
6290 UsdAttributeQuery attrQuery(attr);
6291
6292 std::vector<double> timeSamples;
6293 if (attrQuery.GetTimeSamples(&timeSamples)) {
6294 for (const auto& timeSample : timeSamples) {
6295 VtValue value;
6296 if (attrQuery.Get(&value, timeSample)) {
6297 (*out)[timeSample].Swap(value);
6298 } else {
6299 (*out)[timeSample] = VtValue(SdfValueBlock());
6300 }
6301 }
6302 return true;
6303 }
6304 return false;
6305 }
6306
6307 bool
_GetMetadata(const UsdObject & obj,const TfToken & fieldName,const TfToken & keyPath,bool useFallbacks,VtValue * result) const6308 UsdStage::_GetMetadata(const UsdObject &obj, const TfToken &fieldName,
6309 const TfToken &keyPath, bool useFallbacks,
6310 VtValue* result) const
6311 {
6312 TRACE_FUNCTION();
6313
6314 // XXX: HORRIBLE HACK. Special-case timeSamples for now, since its
6315 // resulting value is a complicated function influenced by "model clips",
6316 // not a single value from scene description or fallbacks. We special-case
6317 // it upfront here, since the Composer mechanism cannot deal with it. We'd
6318 // like to consider remove "attribute value" fields from the set of stuff
6319 // that Usd considers to be "metadata", in which case we can remove this.
6320 if (obj.Is<UsdAttribute>()) {
6321 if (fieldName == SdfFieldKeys->TimeSamples) {
6322 SdfTimeSampleMap timeSamples;
6323 if (_GetTimeSampleMap(obj.As<UsdAttribute>(), &timeSamples)) {
6324 *result = timeSamples;
6325 return true;
6326 }
6327 return false;
6328 }
6329 }
6330
6331 UntypedValueComposer composer(result);
6332 return _GetMetadataImpl(obj, fieldName, keyPath, useFallbacks, &composer);
6333 }
6334
6335 bool
_GetStrongestResolvedMetadata(const UsdObject & obj,const TfToken & fieldName,const TfToken & keyPath,bool useFallbacks,SdfAbstractDataValue * result) const6336 UsdStage::_GetStrongestResolvedMetadata(const UsdObject &obj,
6337 const TfToken& fieldName,
6338 const TfToken &keyPath,
6339 bool useFallbacks,
6340 SdfAbstractDataValue* result) const
6341 {
6342 StrongestValueComposer composer(result);
6343 return _GetMetadataImpl(obj, fieldName, keyPath, useFallbacks, &composer);
6344 }
6345
6346 template <class T>
6347 bool
_GetTypeSpecificResolvedMetadata(const UsdObject & obj,const TfToken & fieldName,const TfToken & keyPath,bool useFallbacks,T * result) const6348 UsdStage::_GetTypeSpecificResolvedMetadata(const UsdObject &obj,
6349 const TfToken& fieldName,
6350 const TfToken &keyPath,
6351 bool useFallbacks,
6352 T* result) const
6353 {
6354 SdfAbstractDataTypedValue<T> out(result);
6355 TypeSpecificValueComposer<T> composer(&out);
6356 return _GetMetadataImpl(obj, fieldName, keyPath, useFallbacks, &composer);
6357 }
6358
6359 // This specialization for SdfTimeSampleMap is still required because of the
6360 // attribute time samples hack.
6361 template <>
6362 bool
_GetTypeSpecificResolvedMetadata(const UsdObject & obj,const TfToken & fieldName,const TfToken & keyPath,bool useFallbacks,SdfTimeSampleMap * result) const6363 UsdStage::_GetTypeSpecificResolvedMetadata(const UsdObject &obj,
6364 const TfToken& fieldName,
6365 const TfToken &keyPath,
6366 bool useFallbacks,
6367 SdfTimeSampleMap* result) const
6368 {
6369 TRACE_FUNCTION();
6370
6371 // XXX: HORRIBLE HACK. Special-case timeSamples for now, since its
6372 // resulting value is a complicated function influenced by "model clips",
6373 // not a single value from scene description or fallbacks. We special-case
6374 // it upfront here, since the Composer mechanism cannot deal with it. We'd
6375 // like to consider remove "attribute value" fields from the set of stuff
6376 // that Usd considers to be "metadata", in which case we can remove this.
6377 if (obj.Is<UsdAttribute>()) {
6378 if (fieldName == SdfFieldKeys->TimeSamples) {
6379 return _GetTimeSampleMap(obj.As<UsdAttribute>(), result);
6380 }
6381 }
6382
6383 SdfAbstractDataTypedValue<SdfTimeSampleMap> out(result);
6384 TypeSpecificValueComposer<SdfTimeSampleMap> composer(&out);
6385 return _GetMetadataImpl(obj, fieldName, keyPath, useFallbacks, &composer);
6386 }
6387
6388 template <class Composer>
6389 void
_GetAttrTypeImpl(const UsdAttribute & attr,const TfToken & fieldName,bool useFallbacks,Composer * composer) const6390 UsdStage::_GetAttrTypeImpl(const UsdAttribute &attr,
6391 const TfToken &fieldName,
6392 bool useFallbacks,
6393 Composer *composer) const
6394 {
6395 TRACE_FUNCTION();
6396 composer->ConsumeUsdFallback(
6397 attr._Prim()->GetPrimDefinition(),
6398 attr.GetName(), fieldName, TfToken());
6399 if (composer->IsDone()) {
6400 return;
6401 }
6402
6403 // Fall back to general metadata composition.
6404 _GetGeneralMetadataImpl(attr, fieldName, TfToken(), useFallbacks, composer);
6405 }
6406
6407 template <class Composer>
6408 void
_GetAttrVariabilityImpl(const UsdAttribute & attr,bool useFallbacks,Composer * composer) const6409 UsdStage::_GetAttrVariabilityImpl(const UsdAttribute &attr, bool useFallbacks,
6410 Composer *composer) const
6411 {
6412 TRACE_FUNCTION();
6413 composer->ConsumeUsdFallback(
6414 attr._Prim()->GetPrimDefinition(),
6415 attr.GetName(), SdfFieldKeys->Variability, TfToken());
6416 if (composer->IsDone()) {
6417 return;
6418 }
6419
6420 // Otherwise variability is determined by the *weakest* authored opinion.
6421 // Walk authored scene description in reverse order.
6422 const TfToken &attrName = attr.GetName();
6423 TF_REVERSE_FOR_ALL(itr, attr.GetPrim().GetPrimIndex().GetNodeRange()) {
6424 if (itr->IsInert() || !itr->HasSpecs())
6425 continue;
6426 SdfPath specPath = itr->GetPath().AppendProperty(attrName);
6427 TF_REVERSE_FOR_ALL(layerIt, itr->GetLayerStack()->GetLayers()) {
6428 composer->ConsumeAuthored(
6429 *itr, *layerIt, specPath, SdfFieldKeys->Variability, TfToken());
6430 if (composer->IsDone())
6431 return;
6432 }
6433 }
6434 }
6435
6436 template <class Composer>
6437 void
_GetPropCustomImpl(const UsdProperty & prop,bool useFallbacks,Composer * composer) const6438 UsdStage::_GetPropCustomImpl(const UsdProperty &prop, bool useFallbacks,
6439 Composer *composer) const
6440 {
6441 TRACE_FUNCTION();
6442 // Custom is composed as true if there is no property definition and it is
6443 // true anywhere in the stack of opinions.
6444 if (_GetSchemaPropertySpec(prop)) {
6445 composer->ConsumeUsdFallback(
6446 prop._Prim()->GetPrimDefinition(),
6447 prop.GetName(),
6448 SdfFieldKeys->Custom, TfToken());
6449 return;
6450 }
6451
6452 const TfToken &propName = prop.GetName();
6453
6454 TF_REVERSE_FOR_ALL(itr, prop.GetPrim().GetPrimIndex().GetNodeRange()) {
6455 if (itr->IsInert() || !itr->HasSpecs())
6456 continue;
6457
6458 SdfPath specPath = itr->GetPath().AppendProperty(propName);
6459 TF_REVERSE_FOR_ALL(layerIt, itr->GetLayerStack()->GetLayers()) {
6460 composer->ConsumeAuthored(
6461 *itr, *layerIt, specPath, SdfFieldKeys->Custom, TfToken());
6462 if (composer->IsDone())
6463 return;
6464 }
6465 }
6466 }
6467
6468 template <class Composer>
6469 static void
_GetPrimTypeNameImpl(Usd_PrimDataConstPtr primData,bool useFallbacks,Composer * composer)6470 _GetPrimTypeNameImpl(Usd_PrimDataConstPtr primData,
6471 bool useFallbacks, Composer *composer)
6472 {
6473 TRACE_FUNCTION();
6474 for (Usd_Resolver res(&primData->GetPrimIndex());
6475 res.IsValid(); res.NextLayer()) {
6476 TfToken tok;
6477 if (res.GetLayer()->HasField(
6478 res.GetLocalPath(), SdfFieldKeys->TypeName, &tok)) {
6479 if (!tok.IsEmpty() && tok != SdfTokens->AnyTypeToken) {
6480 composer->ConsumeAuthored(
6481 res.GetNode(), res.GetLayer(), res.GetLocalPath(),
6482 SdfFieldKeys->TypeName, TfToken());
6483 if (composer->IsDone())
6484 return;
6485 }
6486 }
6487 }
6488 }
6489
6490 template <class Composer>
6491 static bool
_GetPrimSpecifierImpl(Usd_PrimDataConstPtr primData,bool useFallbacks,Composer * composer)6492 _GetPrimSpecifierImpl(Usd_PrimDataConstPtr primData,
6493 bool useFallbacks, Composer *composer)
6494 {
6495 // The pseudo-root and instance prototype prims are always defined -- see
6496 // Usd_PrimData for details. Since the fallback for specifier is 'over', we
6497 // have to handle these prims specially here.
6498 if (primData->GetPath().IsAbsoluteRootPath() || primData->IsPrototype()) {
6499 composer->ConsumeExplicitValue(SdfSpecifierDef);
6500 return true;
6501 }
6502
6503 TRACE_FUNCTION();
6504 // Compose specifier. The result is not given by simple strength order. A
6505 // defining specifier is always stronger than a non-defining specifier.
6506 // Also, perhaps surprisingly, a class specifier due to a direct inherit is
6507 // weaker than any other defining specifier. This handles cases like the
6508 // following:
6509 //
6510 // -- root.file -----------------------------------------------------------
6511 // class "C" {}
6512 // over "A" (references = @other.file@</B>) {}
6513 //
6514 // -- other.file ----------------------------------------------------------
6515 // class "C" {}
6516 // def "B" (inherits = </C>) {}
6517 //
6518 // Here /A references /B in other.file, and /B inherits class /C.
6519 // The strength order of specifiers for /A from strong-to-weak is:
6520 //
6521 // 1. 'over' (from /A)
6522 // 2. 'class' (from /C in root)
6523 // 3. 'def' (from /B)
6524 // 4. 'class' (from /C in other)
6525 //
6526 // If we were to pick the strongest defining specifier, /A would be a class.
6527 // But that's wrong: /A should be a 'def'. Inheriting a class should not
6528 // make the instance a class. Classness should not be inherited. Treating
6529 // 'class' specifiers due to direct inherits as weaker than all other
6530 // defining specifiers avoids this problem.
6531
6532 // These are ordered so stronger strengths are numerically larger.
6533 enum _SpecifierStrength {
6534 _SpecifierStrengthNonDefining,
6535 _SpecifierStrengthDirectlyInheritedClass,
6536 _SpecifierStrengthDefining
6537 };
6538
6539 boost::optional<SdfSpecifier> specifier;
6540 _SpecifierStrength strength = _SpecifierStrengthNonDefining;
6541
6542 // Iterate over all prims, strongest to weakest.
6543 SdfSpecifier curSpecifier = SdfSpecifierOver;
6544
6545 Usd_Resolver::Position specPos;
6546
6547 const PcpPrimIndex &primIndex = primData->GetPrimIndex();
6548 for (Usd_Resolver res(&primIndex); res.IsValid(); res.NextLayer()) {
6549 // Get specifier and its strength from this prim.
6550 _SpecifierStrength curStrength = _SpecifierStrengthDefining;
6551 if (res.GetLayer()->HasField(
6552 res.GetLocalPath(), SdfFieldKeys->Specifier, &curSpecifier)) {
6553 specPos = res.GetPosition();
6554
6555 if (SdfIsDefiningSpecifier(curSpecifier)) {
6556 // Compute strength.
6557 if (curSpecifier == SdfSpecifierClass) {
6558 // See if this excerpt is due to direct inherits. Walk up
6559 // the excerpt tree looking for a direct inherit. If we
6560 // find one set the strength and stop.
6561 for (PcpNodeRef node = res.GetNode();
6562 node; node = node.GetParentNode()) {
6563
6564 if (PcpIsInheritArc(node.GetArcType()) &&
6565 !node.IsDueToAncestor()) {
6566 curStrength =
6567 _SpecifierStrengthDirectlyInheritedClass;
6568 break;
6569 }
6570 }
6571
6572 }
6573 }
6574 else {
6575 // Strength is _SpecifierStrengthNonDefining and can't be
6576 // stronger than the current strength so there's no need to do
6577 // the check below.
6578 continue;
6579 }
6580 }
6581 else {
6582 // Variant PrimSpecs don't have a specifier field, continue looking
6583 // for a specifier.
6584 continue;
6585 }
6586
6587 // Use the specifier if it's stronger.
6588 if (curStrength > strength) {
6589 specifier = curSpecifier;
6590 strength = curStrength;
6591
6592 // We can stop as soon as we find a specifier with the strongest
6593 // strength.
6594 if (strength == _SpecifierStrengthDefining)
6595 break;
6596 }
6597 }
6598
6599 // Verify we found *something*. We should never have PrimData without at
6600 // least one PrimSpec, and 'specifier' is required, so it must be present.
6601 if (TF_VERIFY(specPos.GetLayer(), "No PrimSpecs for '%s'",
6602 primData->GetPath().GetText())) {
6603 // Let the composer see the deciding opinion.
6604 composer->ConsumeAuthored(
6605 specPos.GetNode(), specPos.GetLayer(),
6606 specPos.GetLocalPath(),
6607 SdfFieldKeys->Specifier, TfToken());
6608 }
6609 return true;
6610 }
6611
6612 template <class ListOpType, class Composer>
6613 static bool
_GetListOpMetadataImpl(Usd_PrimDataConstPtr primData,const TfToken & propName,const TfToken & fieldName,bool useFallbacks,Usd_Resolver * res,Composer * composer)6614 _GetListOpMetadataImpl(Usd_PrimDataConstPtr primData,
6615 const TfToken &propName,
6616 const TfToken &fieldName,
6617 bool useFallbacks,
6618 Usd_Resolver *res,
6619 Composer *composer)
6620 {
6621 // Collect all list op opinions for this field.
6622 std::vector<ListOpType> listOps;
6623
6624 SdfPath specPath = res->GetLocalPath(propName);
6625
6626 for (bool isNewNode = false; res->IsValid(); isNewNode = res->NextLayer()) {
6627 if (isNewNode)
6628 specPath = res->GetLocalPath(propName);
6629
6630 // Consume an authored opinion here, if one exists.
6631 ListOpType op;
6632 if (res->GetLayer()->HasField(specPath, fieldName, &op)) {
6633 listOps.emplace_back(op);
6634 }
6635 }
6636
6637 if (useFallbacks) {
6638 ListOpType fallbackListOp;
6639 SdfAbstractDataTypedValue<ListOpType> out(&fallbackListOp);
6640 TypeSpecificValueComposer<ListOpType> composer(&out);
6641 if (_GetFallbackMetadataImpl(
6642 primData, propName, fieldName, TfToken(), &composer)) {
6643 listOps.emplace_back(fallbackListOp);
6644 }
6645 }
6646
6647 // Bake the result of applying the list ops into a single explicit
6648 // list op.
6649 if (!listOps.empty()) {
6650 typename ListOpType::ItemVector items;
6651 std::for_each(
6652 listOps.crbegin(), listOps.crend(),
6653 [&items](const ListOpType& op) { op.ApplyOperations(&items); });
6654
6655 ListOpType bakedListOp;
6656 bakedListOp.SetExplicitItems(std::move(items));
6657 composer->ConsumeExplicitValue(bakedListOp);
6658 return true;
6659 }
6660
6661 return false;
6662 }
6663
6664 template <class Composer>
6665 bool
_GetSpecialPropMetadataImpl(const UsdObject & obj,const TfToken & fieldName,const TfToken & keyPath,bool useFallbacks,Composer * composer) const6666 UsdStage::_GetSpecialPropMetadataImpl(const UsdObject &obj,
6667 const TfToken &fieldName,
6668 const TfToken &keyPath,
6669 bool useFallbacks,
6670 Composer *composer) const
6671 {
6672 // Dispatch to special-case composition rules based on type and field.
6673 // Return true if the given field was handled, false otherwise.
6674 if (obj.Is<UsdAttribute>()) {
6675 if (fieldName == SdfFieldKeys->TypeName) {
6676 _GetAttrTypeImpl(
6677 obj.As<UsdAttribute>(), fieldName, useFallbacks, composer);
6678 return true;
6679 } else if (fieldName == SdfFieldKeys->Variability) {
6680 _GetAttrVariabilityImpl(
6681 obj.As<UsdAttribute>(), useFallbacks, composer);
6682 return true;
6683 }
6684 }
6685 if (fieldName == SdfFieldKeys->Custom) {
6686 _GetPropCustomImpl(obj.As<UsdProperty>(), useFallbacks, composer);
6687 return true;
6688 }
6689 return false;
6690 }
6691
6692 template <class Composer>
6693 static bool
_GetSpecialPrimMetadataImpl(Usd_PrimDataConstPtr primData,const TfToken & fieldName,const TfToken & keyPath,bool useFallbacks,Composer * composer)6694 _GetSpecialPrimMetadataImpl(Usd_PrimDataConstPtr primData,
6695 const TfToken &fieldName,
6696 const TfToken &keyPath,
6697 bool useFallbacks,
6698 Composer *composer)
6699 {
6700 // Dispatch to special-case composition rules based on type and field.
6701 // Return true if the given field was handled, false otherwise.
6702 if (fieldName == SdfFieldKeys->TypeName) {
6703 _GetPrimTypeNameImpl(primData, useFallbacks, composer);
6704 return true;
6705 } else if (fieldName == SdfFieldKeys->Specifier) {
6706 _GetPrimSpecifierImpl(primData, useFallbacks, composer);
6707 return true;
6708 } else if (fieldName == SdfFieldKeys->Kind) {
6709 // XXX: We do not not respect fallback kind values during
6710 // Usd_PrimData composition (see _GetKind), but we do allow
6711 // fallback values here to maintain existing behavior. However,
6712 // we may want to force the useFallbacks flag to false here for
6713 // consistency.
6714 _GetPrimKindImpl(primData, useFallbacks, composer);
6715 return true;
6716 } else if (fieldName == SdfFieldKeys->Active) {
6717 // XXX: See comment in the handling of 'kind' re: fallback values.
6718 _GetPrimActiveImpl(primData, useFallbacks, composer);
6719 return true;
6720 }
6721
6722 return false;
6723 }
6724
6725 template <class Composer>
6726 bool
_GetMetadataImpl(const UsdObject & obj,const TfToken & fieldName,const TfToken & keyPath,bool useFallbacks,Composer * composer) const6727 UsdStage::_GetMetadataImpl(
6728 const UsdObject &obj,
6729 const TfToken& fieldName,
6730 const TfToken& keyPath,
6731 bool useFallbacks,
6732 Composer *composer) const
6733 {
6734 // XXX: references, inherit paths, variant selection currently unhandled.
6735 TfErrorMark m;
6736
6737 // Handle special cases.
6738 if (obj.Is<UsdProperty>()) {
6739 if (_GetSpecialPropMetadataImpl(
6740 obj, fieldName, keyPath, useFallbacks, composer)) {
6741 return composer->IsDone() && m.IsClean();
6742 }
6743 } else if (obj.Is<UsdPrim>()) {
6744 // If the prim is the pseudo root, we have a special metadata
6745 // composition to perform as the pseudoroot only composes metadata
6746 // opinions from the session layer and root layer.
6747 if (obj._Prim()->IsPseudoRoot()) {
6748 // Note that this function returns true if an opinion was found so
6749 // we don't need to check composer->IsDone(). IsDone will always
6750 // return false for dictionary metadata on the pseudo root since
6751 // we don't have fallbacks.
6752 return _ComposePseudoRootMetadataImpl(
6753 get_pointer(obj._Prim()), fieldName, keyPath,
6754 _rootLayer, _sessionLayer, composer) && m.IsClean();
6755 } else if (_GetSpecialPrimMetadataImpl(
6756 get_pointer(obj._Prim()), fieldName, keyPath, useFallbacks,
6757 composer)) {
6758 return composer->IsDone() && m.IsClean();
6759 }
6760 }
6761
6762 return _GetGeneralMetadataImpl(
6763 obj, fieldName, keyPath, useFallbacks, composer) && m.IsClean();
6764 }
6765
6766 template <class Composer>
6767 bool
_GetGeneralMetadataImpl(const UsdObject & obj,const TfToken & fieldName,const TfToken & keyPath,bool useFallbacks,Composer * composer) const6768 UsdStage::_GetGeneralMetadataImpl(const UsdObject &obj,
6769 const TfToken& fieldName,
6770 const TfToken& keyPath,
6771 bool useFallbacks,
6772 Composer *composer) const
6773 {
6774 const Usd_PrimDataConstPtr primData = get_pointer(obj._Prim());
6775
6776 static TfToken empty;
6777 const TfToken &propName = obj.Is<UsdProperty>() ? obj.GetName() : empty;
6778
6779 Usd_Resolver resolver(&primData->GetPrimIndex());
6780 if (!_ComposeGeneralMetadataImpl(
6781 primData, propName, fieldName, keyPath, useFallbacks, &resolver,
6782 composer)) {
6783 return false;
6784 }
6785
6786 if (Composer::ProducesValue) {
6787 // If the metadata value produced by the composer is a type that
6788 // requires specific composition behavior, dispatch to the appropriate
6789 // helper. Pass along the same resolver so that the helper can start
6790 // from where _ComposeGeneralMetadataImpl found the first metadata
6791 // value.
6792 const std::type_info& valueTypeId(composer->GetHeldTypeid());
6793 if (valueTypeId == typeid(SdfIntListOp)) {
6794 return _GetListOpMetadataImpl<SdfIntListOp>(
6795 primData, propName, fieldName, useFallbacks, &resolver,
6796 composer);
6797 }
6798 else if (valueTypeId == typeid(SdfInt64ListOp)) {
6799 return _GetListOpMetadataImpl<SdfInt64ListOp>(
6800 primData, propName, fieldName, useFallbacks, &resolver,
6801 composer);
6802 }
6803 else if (valueTypeId == typeid(SdfUIntListOp)) {
6804 return _GetListOpMetadataImpl<SdfUIntListOp>(
6805 primData, propName, fieldName, useFallbacks, &resolver,
6806 composer);
6807 }
6808 else if (valueTypeId == typeid(SdfUInt64ListOp)) {
6809 return _GetListOpMetadataImpl<SdfUInt64ListOp>(
6810 primData, propName, fieldName, useFallbacks, &resolver,
6811 composer);
6812 }
6813 else if (valueTypeId == typeid(SdfStringListOp)) {
6814 return _GetListOpMetadataImpl<SdfStringListOp>(
6815 primData, propName, fieldName, useFallbacks, &resolver,
6816 composer);
6817 }
6818 else if (valueTypeId == typeid(SdfTokenListOp)) {
6819 return _GetListOpMetadataImpl<SdfTokenListOp>(
6820 primData, propName, fieldName, useFallbacks, &resolver,
6821 composer);
6822 }
6823 }
6824
6825 return true;
6826 }
6827
6828 bool
_HasMetadata(const UsdObject & obj,const TfToken & fieldName,const TfToken & keyPath,bool useFallbacks) const6829 UsdStage::_HasMetadata(const UsdObject &obj, const TfToken& fieldName,
6830 const TfToken &keyPath, bool useFallbacks) const
6831 {
6832 ExistenceComposer composer;
6833 _GetMetadataImpl(obj, fieldName, keyPath, useFallbacks, &composer);
6834 return composer.IsDone();
6835 }
6836
6837 static
6838 SdfSpecType
_ListMetadataFieldsImpl(Usd_PrimDataConstPtr primData,const TfToken & propName,bool useFallbacks,TfTokenVector * result)6839 _ListMetadataFieldsImpl(Usd_PrimDataConstPtr primData,
6840 const TfToken &propName,
6841 bool useFallbacks,
6842 TfTokenVector *result)
6843 {
6844 TRACE_FUNCTION();
6845
6846 Usd_Resolver res(&primData->GetPrimIndex());
6847 SdfPath specPath = res.GetLocalPath(propName);
6848 PcpNodeRef lastNode = res.GetNode();
6849 SdfSpecType specType = SdfSpecTypeUnknown;
6850
6851 const UsdPrimDefinition &primDef = primData->GetPrimDefinition();
6852
6853 // If this is a builtin property, determine specType from the definition.
6854 if (!propName.IsEmpty()) {
6855 specType = primDef.GetSpecType(propName);
6856 }
6857
6858 // Insert authored fields, discovering spec type along the way.
6859 for (; res.IsValid(); res.NextLayer()) {
6860 if (res.GetNode() != lastNode) {
6861 lastNode = res.GetNode();
6862 specPath = res.GetLocalPath(propName);
6863 }
6864 const SdfLayerRefPtr& layer = res.GetLayer();
6865 if (specType == SdfSpecTypeUnknown)
6866 specType = layer->GetSpecType(specPath);
6867
6868 for (const auto& fieldName : layer->ListFields(specPath)) {
6869 if (!_IsPrivateFieldKey(fieldName))
6870 result->push_back(fieldName);
6871 }
6872 }
6873
6874 // If including fallbacks, add any defined metadata fields from the prim
6875 // definition for the property (or the prim if the prop name is empty).
6876 if (useFallbacks) {
6877 const TfTokenVector fallbackFields = propName.IsEmpty() ?
6878 primDef.ListMetadataFields() :
6879 primDef.ListPropertyMetadataFields(propName);
6880 result->insert(result->end(),
6881 fallbackFields.begin(), fallbackFields.end());
6882 }
6883
6884 return specType;
6885 }
6886
6887 static
6888 SdfSpecType
_ListPseudoRootMetadataFieldsImpl(Usd_PrimDataConstPtr primData,const SdfLayerRefPtr & rootLayer,const SdfLayerRefPtr & sessionLayer,TfTokenVector * result)6889 _ListPseudoRootMetadataFieldsImpl(Usd_PrimDataConstPtr primData,
6890 const SdfLayerRefPtr &rootLayer,
6891 const SdfLayerRefPtr &sessionLayer,
6892 TfTokenVector *result)
6893 {
6894 TRACE_FUNCTION();
6895
6896 const SdfPath &specPath = SdfPath::AbsoluteRootPath();
6897 PcpNodeRef node = primData->GetPrimIndex().GetRootNode();
6898
6899 // If we a have a session layer and it isn't muted, get its authored layer
6900 // metadata fields. The session layer will be the first layer in the
6901 // layer stack unless it is muted.
6902 if (sessionLayer &&
6903 node.GetLayerStack()->GetLayers().front() == sessionLayer) {
6904 for (const auto& fieldName : sessionLayer->ListFields(specPath)) {
6905 if (!_IsPrivateFieldKey(fieldName)) {
6906 result->push_back(fieldName);
6907 }
6908 }
6909 }
6910
6911 // Get all authored layer metadata fields from the root layer (which can't
6912 // be muted).
6913 for (const auto& fieldName : rootLayer->ListFields(specPath)) {
6914 if (!_IsPrivateFieldKey(fieldName)) {
6915 result->push_back(fieldName);
6916 }
6917 }
6918
6919 return SdfSpecTypePseudoRoot;
6920 }
6921
6922 TfTokenVector
_ListMetadataFields(const UsdObject & obj,bool useFallbacks) const6923 UsdStage::_ListMetadataFields(const UsdObject &obj, bool useFallbacks) const
6924 {
6925 TRACE_FUNCTION();
6926
6927 TfTokenVector result;
6928
6929 SdfSpecType specType = SdfSpecTypeUnknown;
6930 Usd_PrimDataConstPtr primData = get_pointer(obj._Prim());
6931 if (obj.Is<UsdProperty>()) {
6932 // List metadata fields for property
6933 specType = _ListMetadataFieldsImpl(
6934 primData, obj.GetName(), useFallbacks, &result);
6935 } else if (obj._Prim()->IsPseudoRoot()) {
6936 // Custom implementation for listing metadata for the pseudo root.
6937 specType = _ListPseudoRootMetadataFieldsImpl(
6938 primData, _rootLayer, _sessionLayer, &result);
6939 } else {
6940 // List metadata fields for non pseudo root prims.
6941 specType = _ListMetadataFieldsImpl(
6942 primData, TfToken(), useFallbacks, &result);
6943 }
6944
6945 // Insert required fields for spec type.
6946 const SdfSchema::SpecDefinition* specDef = nullptr;
6947 specDef = SdfSchema::GetInstance().GetSpecDefinition(specType);
6948 if (specDef) {
6949 for (const auto& fieldName : specDef->GetRequiredFields()) {
6950 if (!_IsPrivateFieldKey(fieldName))
6951 result.push_back(fieldName);
6952 }
6953 }
6954
6955 // Sort & remove duplicate fields.
6956 std::sort(result.begin(), result.end(), TfDictionaryLessThan());
6957 result.erase(std::unique(result.begin(), result.end()), result.end());
6958
6959 return result;
6960 }
6961
6962 void
_GetAllMetadata(const UsdObject & obj,bool useFallbacks,UsdMetadataValueMap * resultMap,bool anchorAssetPathsOnly) const6963 UsdStage::_GetAllMetadata(const UsdObject &obj,
6964 bool useFallbacks,
6965 UsdMetadataValueMap* resultMap,
6966 bool anchorAssetPathsOnly) const
6967 {
6968 TRACE_FUNCTION();
6969
6970 UsdMetadataValueMap &result = *resultMap;
6971
6972 TfTokenVector fieldNames = _ListMetadataFields(obj, useFallbacks);
6973 for (const auto& fieldName : fieldNames) {
6974 VtValue val;
6975 UntypedValueComposer composer(&val, anchorAssetPathsOnly);
6976 _GetMetadataImpl(obj, fieldName, TfToken(), useFallbacks, &composer);
6977 result[fieldName] = val;
6978 }
6979 }
6980
6981 // --------------------------------------------------------------------- //
6982 // Default & TimeSample Resolution
6983 // --------------------------------------------------------------------- //
6984
6985 static bool
_ClipsApplyToLayerStackSite(const Usd_ClipSetRefPtr & clips,const PcpLayerStackPtr & layerStack,const SdfPath & primPathInLayerStack)6986 _ClipsApplyToLayerStackSite(
6987 const Usd_ClipSetRefPtr& clips,
6988 const PcpLayerStackPtr& layerStack, const SdfPath& primPathInLayerStack)
6989 {
6990 return (layerStack == clips->sourceLayerStack
6991 && primPathInLayerStack.HasPrefix(clips->sourcePrimPath));
6992 }
6993
6994 static bool
_ClipsApplyToNode(const Usd_ClipSetRefPtr & clips,const PcpNodeRef & node)6995 _ClipsApplyToNode(
6996 const Usd_ClipSetRefPtr& clips,
6997 const PcpNodeRef& node)
6998 {
6999 return (node.GetLayerStack() == clips->sourceLayerStack
7000 && node.GetPath().HasPrefix(clips->sourcePrimPath));
7001 }
7002
7003 static bool
_ClipsContainValueForAttribute(const Usd_ClipSetRefPtr & clips,const SdfPath & attrSpecPath)7004 _ClipsContainValueForAttribute(
7005 const Usd_ClipSetRefPtr& clips,
7006 const SdfPath& attrSpecPath)
7007 {
7008 // Only look for samples in clips for attributes that are
7009 // marked as varying in the clip manifest (if one is present).
7010 // This gives users a way to indicate that an attribute will
7011 // never have samples in a clip, which can help performance.
7012 //
7013 // We normally do not consider variability during value
7014 // resolution to avoid the cost of composing variability on
7015 // each value fetch. We can use it here because we're only
7016 // fetching it from a single layer, which should be cheap.
7017 // This is also convenient for users, since it allows them
7018 // to reuse assets that may have both uniform and varying
7019 // attributes as manifests.
7020 if (clips->manifestClip) {
7021 SdfVariability attrVariability = SdfVariabilityUniform;
7022 if (clips->manifestClip->HasField(
7023 attrSpecPath, SdfFieldKeys->Variability, &attrVariability)
7024 && attrVariability == SdfVariabilityVarying) {
7025 return true;
7026 }
7027 }
7028 return false;
7029 }
7030
7031 static
7032 const std::vector<Usd_ClipSetRefPtr>
_GetClipsThatApplyToNode(const std::vector<Usd_ClipSetRefPtr> & clipsAffectingPrim,const PcpNodeRef & node,const SdfPath & specPath)7033 _GetClipsThatApplyToNode(
7034 const std::vector<Usd_ClipSetRefPtr>& clipsAffectingPrim,
7035 const PcpNodeRef& node,
7036 const SdfPath& specPath)
7037 {
7038 std::vector<Usd_ClipSetRefPtr> relevantClips;
7039
7040 for (const auto& localClips : clipsAffectingPrim) {
7041 if (_ClipsApplyToNode(localClips, node)
7042 && _ClipsContainValueForAttribute(localClips, specPath)) {
7043 relevantClips.push_back(localClips);
7044 }
7045 }
7046
7047 return relevantClips;
7048 }
7049
7050 static bool
_HasTimeSamples(const SdfLayerRefPtr & source,const SdfPath & specPath,const double * time=nullptr,double * lower=nullptr,double * upper=nullptr)7051 _HasTimeSamples(const SdfLayerRefPtr& source,
7052 const SdfPath& specPath,
7053 const double* time = nullptr,
7054 double* lower = nullptr, double* upper = nullptr)
7055 {
7056 if (time) {
7057 // If caller wants bracketing time samples as well, we can just use
7058 // GetBracketingTimeSamplesForPath. If no samples exist, this should
7059 // return false.
7060 return source->GetBracketingTimeSamplesForPath(
7061 specPath, *time, lower, upper);
7062 }
7063
7064 return source->GetNumTimeSamplesForPath(specPath) > 0;
7065 }
7066
7067 static bool
_HasTimeSamples(const Usd_ClipSetRefPtr & sourceClips,const SdfPath & specPath,const double * time=nullptr,double * lower=nullptr,double * upper=nullptr)7068 _HasTimeSamples(const Usd_ClipSetRefPtr& sourceClips,
7069 const SdfPath& specPath,
7070 const double* time = nullptr,
7071 double* lower = nullptr, double* upper = nullptr)
7072 {
7073 // Bail out immediately if this clip set does not contain values
7074 // for this attribute.
7075 if (!_ClipsContainValueForAttribute(sourceClips, specPath)) {
7076 return false;
7077 }
7078
7079 if (time) {
7080 return sourceClips->GetBracketingTimeSamplesForPath(
7081 specPath, *time, lower, upper);
7082 }
7083
7084 // Since this clip set has declared it contains values for this
7085 // attribute, we always return true.
7086 return true;
7087 }
7088
7089 // Helper for getting the fully resolved value from an attribute generically
7090 // for all value types for use by _GetValue and _GetValueForResolveInfo.
7091 template <class T>
7092 struct Usd_AttrGetValueHelper {
7093
7094 public:
7095 // Get the value at time for the attribute. The getValueImpl function is
7096 // templated for sharing of this functionality between _GetValue and
7097 // _GetValueForResolveInfo.
7098 template <class Fn>
GetValueUsd_AttrGetValueHelper7099 static bool GetValue(const UsdStage &stage, UsdTimeCode time,
7100 const UsdAttribute &attr, T* result,
7101 const Fn &getValueImpl)
7102 {
7103 // Special case if time is default: we can grab the value from the
7104 // metadata. This value will be fully resolved already.
7105 if (time.IsDefault()) {
7106 SdfAbstractDataTypedValue<T> out(result);
7107 TypeSpecificValueComposer<T> composer(&out);
7108 bool valueFound = stage._GetMetadataImpl(
7109 attr, SdfFieldKeys->Default, TfToken(),
7110 /*useFallbacks=*/true, &composer);
7111
7112 return valueFound &&
7113 (!Usd_ClearValueIfBlocked<SdfAbstractDataValue>(&out));
7114 }
7115
7116 return _GetResolvedValue(stage, time, attr, result, getValueImpl);
7117 }
7118
7119 private:
7120 // Metafunction for selecting the appropriate interpolation object if the
7121 // given value type supports linear interpolation.
7122 struct _SelectInterpolator
7123 : public boost::mpl::if_c<
7124 UsdLinearInterpolationTraits<T>::isSupported,
7125 Usd_LinearInterpolator<T>,
7126 Usd_HeldInterpolator<T> > { };
7127
7128 // Gets the attribute value from the implementation with appropriate
7129 // interpolation. In the case of value types that have type specific value
7130 // resolution (like SdfAssetPath and SdfTimeCode), the value returned from
7131 // from this is NOT fully resolved yet.
7132 template <class Fn>
_GetValueFromImplUsd_AttrGetValueHelper7133 static bool _GetValueFromImpl(const UsdStage &stage,
7134 UsdTimeCode time, const UsdAttribute &attr,
7135 T* result, const Fn &getValueImpl)
7136 {
7137 SdfAbstractDataTypedValue<T> out(result);
7138
7139 if (stage._interpolationType == UsdInterpolationTypeLinear) {
7140 typedef typename _SelectInterpolator::type _Interpolator;
7141 _Interpolator interpolator(result);
7142 return getValueImpl(stage, time, attr, &interpolator, &out);
7143 };
7144
7145 Usd_HeldInterpolator<T> interpolator(result);
7146 return getValueImpl(stage, time, attr, &interpolator, &out);
7147 }
7148
7149 // Gets the fully resolved value for the attribute.
7150 template <class Fn>
_GetResolvedValueUsd_AttrGetValueHelper7151 static bool _GetResolvedValue(const UsdStage &stage,
7152 UsdTimeCode time, const UsdAttribute &attr,
7153 T* result, const Fn &getValueImpl)
7154 {
7155 if (_GetValueFromImpl(stage, time, attr, result, getValueImpl)) {
7156 // Do the the type specific value resolution on the result. For
7157 // most types _ResolveValue does nothing.
7158 _ResolveValue(stage, time, attr, result);
7159 return true;
7160 }
7161 return false;
7162 }
7163
7164 // Performs type specific value resolution.
_ResolveValueUsd_AttrGetValueHelper7165 static void _ResolveValue(
7166 const UsdStage &stage, UsdTimeCode time, const UsdAttribute &attr,
7167 T* result)
7168 {
7169 // Do nothing for types without type specific value resolution.
7170 static_assert(!UsdStage::_HasTypeSpecificResolution<T>::value,
7171 "Value types with type specific value resolution must "
7172 "specialize Usd_AttrGetValueHelper::_ResolveValue");
7173 }
7174 };
7175
7176 // Specializations implementing _ResolveValue for types with type specific
7177 // value resolution.
7178 template <>
_ResolveValue(const UsdStage & stage,UsdTimeCode time,const UsdAttribute & attr,SdfAssetPath * result)7179 void Usd_AttrGetValueHelper<SdfAssetPath>::_ResolveValue(
7180 const UsdStage &stage, UsdTimeCode time, const UsdAttribute &attr,
7181 SdfAssetPath* result)
7182 {
7183 stage._MakeResolvedAssetPaths(time, attr, result, 1);
7184 }
7185
7186 template <>
_ResolveValue(const UsdStage & stage,UsdTimeCode time,const UsdAttribute & attr,VtArray<SdfAssetPath> * result)7187 void Usd_AttrGetValueHelper<VtArray<SdfAssetPath>>::_ResolveValue(
7188 const UsdStage &stage, UsdTimeCode time, const UsdAttribute &attr,
7189 VtArray<SdfAssetPath>* result)
7190 {
7191 stage._MakeResolvedAssetPaths(time, attr, result->data(), result->size());
7192 }
7193
7194 template <>
_ResolveValue(const UsdStage & stage,UsdTimeCode time,const UsdAttribute & attr,SdfTimeCode * result)7195 void Usd_AttrGetValueHelper<SdfTimeCode>::_ResolveValue(
7196 const UsdStage &stage, UsdTimeCode time, const UsdAttribute &attr,
7197 SdfTimeCode* result)
7198 {
7199 stage._MakeResolvedTimeCodes(time, attr, result, 1);
7200 }
7201
7202 template <>
_ResolveValue(const UsdStage & stage,UsdTimeCode time,const UsdAttribute & attr,VtArray<SdfTimeCode> * result)7203 void Usd_AttrGetValueHelper<VtArray<SdfTimeCode>>::_ResolveValue(
7204 const UsdStage &stage, UsdTimeCode time, const UsdAttribute &attr,
7205 VtArray<SdfTimeCode>* result)
7206 {
7207 stage._MakeResolvedTimeCodes(time, attr, result->data(), result->size());
7208 }
7209
7210 // Attribute value getter for type erased VtValue.
7211 struct Usd_AttrGetUntypedValueHelper {
7212 template <class Fn>
GetValueUsd_AttrGetUntypedValueHelper7213 static bool GetValue(const UsdStage &stage, UsdTimeCode time,
7214 const UsdAttribute &attr, VtValue* result,
7215 const Fn &getValueImpl)
7216 {
7217 // Special case if time is default: we can grab the value from the
7218 // metadata. This value will be fully resolved already because
7219 // _GetMetadata returns fully resolved values.
7220 if (time.IsDefault()) {
7221 bool valueFound = stage._GetMetadata(
7222 attr, SdfFieldKeys->Default, TfToken(),
7223 /*useFallbacks=*/true, result);
7224 return valueFound && (!Usd_ClearValueIfBlocked(result));
7225 }
7226
7227 Usd_UntypedInterpolator interpolator(attr, result);
7228 if (getValueImpl(stage, time, attr, &interpolator, result)) {
7229 if (result) {
7230 // Always run the resolve functions for value types that need
7231 // it.
7232 stage._MakeResolvedAttributeValue(time, attr, result);
7233 }
7234 return true;
7235 }
7236 return false;
7237 }
7238 };
7239
7240 bool
_GetValue(UsdTimeCode time,const UsdAttribute & attr,VtValue * result) const7241 UsdStage::_GetValue(UsdTimeCode time, const UsdAttribute &attr,
7242 VtValue* result) const
7243 {
7244 auto getValueImpl = [](const UsdStage &stage,
7245 UsdTimeCode time, const UsdAttribute &attr,
7246 Usd_InterpolatorBase* interpolator,
7247 VtValue* value)
7248 {
7249 return stage._GetValueImpl(time, attr, interpolator, value);
7250 };
7251
7252 return Usd_AttrGetUntypedValueHelper::GetValue(
7253 *this, time, attr, result, getValueImpl);
7254 }
7255
7256 template <class T>
7257 bool
_GetValue(UsdTimeCode time,const UsdAttribute & attr,T * result) const7258 UsdStage::_GetValue(UsdTimeCode time, const UsdAttribute &attr,
7259 T* result) const
7260 {
7261 auto getValueImpl = [](const UsdStage &stage,
7262 UsdTimeCode time, const UsdAttribute &attr,
7263 Usd_InterpolatorBase* interpolator,
7264 SdfAbstractDataValue* value)
7265 {
7266 return stage._GetValueImpl(time, attr, interpolator, value);
7267 };
7268
7269 return Usd_AttrGetValueHelper<T>::GetValue(
7270 *this, time, attr, result, getValueImpl);
7271 }
7272
7273 class UsdStage_ResolveInfoAccess
7274 {
7275 public:
7276 template <class T>
_GetTimeSampleValue(UsdTimeCode time,const UsdAttribute & attr,const UsdResolveInfo & info,const double * lowerHint,const double * upperHint,Usd_InterpolatorBase * interpolator,T * result)7277 static bool _GetTimeSampleValue(
7278 UsdTimeCode time, const UsdAttribute& attr,
7279 const UsdResolveInfo &info,
7280 const double *lowerHint, const double *upperHint,
7281 Usd_InterpolatorBase *interpolator,
7282 T *result)
7283 {
7284 const SdfPath specPath =
7285 info._primPathInLayerStack.AppendProperty(attr.GetName());
7286 const SdfLayerHandle& layer = info._layer;
7287 const double localTime =
7288 info._layerToStageOffset.GetInverse() * time.GetValue();
7289
7290 double upper = 0.0;
7291 double lower = 0.0;
7292
7293 if (lowerHint && upperHint) {
7294 lower = *lowerHint;
7295 upper = *upperHint;
7296 }
7297 else {
7298 if (!TF_VERIFY(layer->GetBracketingTimeSamplesForPath(
7299 specPath, localTime, &lower, &upper),
7300 "No bracketing time samples for "
7301 "%s on <%s> for time %g between %g and %g",
7302 layer->GetIdentifier().c_str(),
7303 specPath.GetText(),
7304 localTime, lower, upper)) {
7305 return false;
7306 }
7307 }
7308
7309 TF_DEBUG(USD_VALUE_RESOLUTION).Msg(
7310 "RESOLVE: reading field %s:%s from @%s@, "
7311 "with requested time = %.3f (local time = %.3f) "
7312 "reading from sample %.3f \n",
7313 specPath.GetText(),
7314 SdfFieldKeys->TimeSamples.GetText(),
7315 layer->GetIdentifier().c_str(),
7316 time.GetValue(),
7317 localTime,
7318 lower);
7319
7320 return Usd_GetOrInterpolateValue(
7321 layer, specPath, localTime, lower, upper, interpolator, result);
7322 }
7323
7324 template <class T>
_GetClipValue(UsdTimeCode time,const UsdAttribute & attr,const UsdResolveInfo & info,const Usd_ClipSetRefPtr & clipSet,const double * lowerHint,const double * upperHint,Usd_InterpolatorBase * interpolator,T * result)7325 static bool _GetClipValue(
7326 UsdTimeCode time, const UsdAttribute& attr,
7327 const UsdResolveInfo &info,
7328 const Usd_ClipSetRefPtr &clipSet,
7329 const double *lowerHint, const double *upperHint,
7330 Usd_InterpolatorBase *interpolator,
7331 T *result)
7332 {
7333 const SdfPath specPath =
7334 info._primPathInLayerStack.AppendProperty(attr.GetName());
7335
7336 // Note that we do not apply layer offsets to the time.
7337 // Because clip metadata may be authored in different
7338 // layers in the LayerStack, each with their own
7339 // layer offsets, it is simpler to bake the effects of
7340 // those offsets into Usd_Clip.
7341 const double localTime = time.GetValue();
7342 double upper = 0.0;
7343 double lower = 0.0;
7344
7345 if (lowerHint && upperHint) {
7346 lower = *lowerHint;
7347 upper = *upperHint;
7348 }
7349 else {
7350 _HasTimeSamples(clipSet, specPath, &localTime, &lower, &upper);
7351 }
7352
7353 TF_DEBUG(USD_VALUE_RESOLUTION).Msg(
7354 "RESOLVE: reading field %s:%s from clip set %s, "
7355 "with requested time = %.3f "
7356 "reading from sample %.3f \n",
7357 specPath.GetText(),
7358 SdfFieldKeys->TimeSamples.GetText(),
7359 clipSet->name.c_str(),
7360 localTime,
7361 lower);
7362
7363 return Usd_GetOrInterpolateValue(
7364 clipSet, specPath, localTime, lower, upper, interpolator, result);
7365 }
7366 };
7367
7368 // Helper structure populated by _GetResolveInfo and _ResolveInfoResolver
7369 // with extra information accumulated in the process. This allows clients to
7370 // avoid redoing work.
7371 template <class T>
7372 struct UsdStage::_ExtraResolveInfo
7373 {
7374 // If the resolve info source is UsdResolveInfoSourceTimeSamples or
7375 // UsdResolveInfoSourceValueClips and an explicit time is given to
7376 // _GetResolveInfo, this will be the lower and upper bracketing time
7377 // samples for that time.
7378 double lowerSample = 0;
7379 double upperSample = 0;
7380
7381 // If the resolve info source is UsdResolveInfoSourceDefault or
7382 // UsdResolveInfoSourceFallback and this is non-null, the default
7383 // or fallback value will be copied to the object this pointer refers to.
7384 T* defaultOrFallbackValue = nullptr;
7385
7386 // If the resolve info source is UsdResolveInfoSourceValueClips this will
7387 // be the Usd_ClipSet containing values for the attribute.
7388 Usd_ClipSetRefPtr clipSet;
7389 };
7390
7391 SdfLayerRefPtr
_GetLayerWithStrongestValue(UsdTimeCode time,const UsdAttribute & attr) const7392 UsdStage::_GetLayerWithStrongestValue(
7393 UsdTimeCode time, const UsdAttribute &attr) const
7394 {
7395 SdfLayerRefPtr resultLayer;
7396 if (time.IsDefault()) {
7397 ExistenceComposer getLayerComposer(&resultLayer);
7398 _GetMetadataImpl(attr, SdfFieldKeys->Default,
7399 TfToken(), /*useFallbacks=*/false, &getLayerComposer);
7400 } else {
7401 UsdResolveInfo resolveInfo;
7402 _ExtraResolveInfo<SdfAbstractDataValue> extraResolveInfo;
7403
7404 _GetResolveInfo(attr, &resolveInfo, &time, &extraResolveInfo);
7405
7406 if (resolveInfo._source == UsdResolveInfoSourceTimeSamples ||
7407 resolveInfo._source == UsdResolveInfoSourceDefault) {
7408 resultLayer = resolveInfo._layer;
7409 }
7410 else if (resolveInfo._source == UsdResolveInfoSourceValueClips) {
7411 const Usd_ClipSetRefPtr& clipSet = extraResolveInfo.clipSet;
7412 const Usd_ClipRefPtr& activeClip =
7413 clipSet->GetActiveClip(time.GetValue());
7414 const SdfPath specPath =
7415 resolveInfo._primPathInLayerStack.AppendProperty(attr.GetName());
7416
7417 // If the active clip has authored time samples, the value will
7418 // come from it (or at least be interpolated from it) so use that
7419 // clip's layer. Otherwise the value will come from the manifest.
7420 resultLayer = activeClip->HasAuthoredTimeSamples(specPath) ?
7421 activeClip->GetLayer() : clipSet->manifestClip->GetLayer();
7422 }
7423 }
7424 return resultLayer;
7425 }
7426
7427 template <class T>
7428 bool
_GetValueImpl(UsdTimeCode time,const UsdAttribute & attr,Usd_InterpolatorBase * interpolator,T * result) const7429 UsdStage::_GetValueImpl(UsdTimeCode time, const UsdAttribute &attr,
7430 Usd_InterpolatorBase* interpolator,
7431 T *result) const
7432 {
7433 UsdResolveInfo resolveInfo;
7434 _ExtraResolveInfo<T> extraResolveInfo;
7435 extraResolveInfo.defaultOrFallbackValue = result;
7436
7437 TfErrorMark m;
7438 _GetResolveInfo(attr, &resolveInfo, &time, &extraResolveInfo);
7439
7440 if (resolveInfo._source == UsdResolveInfoSourceTimeSamples) {
7441 return UsdStage_ResolveInfoAccess::_GetTimeSampleValue(
7442 time, attr, resolveInfo,
7443 &extraResolveInfo.lowerSample, &extraResolveInfo.upperSample,
7444 interpolator, result);
7445 }
7446 else if (resolveInfo._source == UsdResolveInfoSourceValueClips) {
7447 return UsdStage_ResolveInfoAccess::_GetClipValue(
7448 time, attr, resolveInfo,
7449 extraResolveInfo.clipSet,
7450 &extraResolveInfo.lowerSample, &extraResolveInfo.upperSample,
7451 interpolator, result);
7452 }
7453 else if (resolveInfo._source == UsdResolveInfoSourceDefault ||
7454 resolveInfo._source == UsdResolveInfoSourceFallback) {
7455 // Nothing to do here -- the call to _GetResolveInfo will have
7456 // filled in the result with the default value.
7457 return m.IsClean();
7458 }
7459
7460 return false;
7461 }
7462
7463 // Our property stack resolver never indicates for resolution to stop
7464 // as we need to gather all relevant property specs in the LayerStack
7465 struct UsdStage::_PropertyStackResolver {
7466 SdfPropertySpecHandleVector propertyStack;
7467
ProcessFallbackUsdStage::_PropertyStackResolver7468 bool ProcessFallback() { return false; }
7469
7470 bool
ProcessLayerUsdStage::_PropertyStackResolver7471 ProcessLayer(const size_t layerStackPosition,
7472 const SdfPath& specPath,
7473 const PcpNodeRef& node,
7474 const double *time)
7475 {
7476 const auto layer
7477 = node.GetLayerStack()->GetLayers()[layerStackPosition];
7478 const auto propertySpec = layer->GetPropertyAtPath(specPath);
7479 if (propertySpec) {
7480 propertyStack.push_back(propertySpec);
7481 }
7482
7483 return false;
7484 }
7485
7486 bool
ProcessClipsUsdStage::_PropertyStackResolver7487 ProcessClips(const Usd_ClipSetRefPtr& clipSet,
7488 const SdfPath& specPath,
7489 const PcpNodeRef& node,
7490 const double* time)
7491 {
7492 // Look through clips to see if they have a time sample for
7493 // this attribute. If a time is given, examine just the clips
7494 // that are active at that time.
7495 double lowerSample = 0.0, upperSample = 0.0;
7496
7497 if (_HasTimeSamples(
7498 clipSet, specPath, time, &lowerSample, &upperSample)) {
7499
7500 const Usd_ClipRefPtr& activeClip = clipSet->GetActiveClip(*time);
7501
7502 // If the active clip has authored time samples, the value will
7503 // come from it (or at least be interpolated from it) so use the
7504 // property spec from that clip. Otherwise the value will come
7505 // from the manifest.
7506 const Usd_ClipRefPtr& sourceClip =
7507 activeClip->HasAuthoredTimeSamples(specPath) ?
7508 activeClip : clipSet->manifestClip;
7509
7510 if (!TF_VERIFY(sourceClip)) {
7511 return false;
7512 }
7513
7514 if (const auto propertySpec =
7515 sourceClip->GetPropertyAtPath(specPath)) {
7516 propertyStack.push_back(propertySpec);
7517 }
7518 }
7519
7520 return false;
7521 }
7522 };
7523
7524 SdfPropertySpecHandleVector
_GetPropertyStack(const UsdProperty & prop,UsdTimeCode time) const7525 UsdStage::_GetPropertyStack(const UsdProperty &prop,
7526 UsdTimeCode time) const
7527 {
7528 _PropertyStackResolver resolver;
7529 _GetResolvedValueImpl(prop, &resolver, &time);
7530 return resolver.propertyStack;
7531 }
7532
7533 // A 'Resolver' for filling UsdResolveInfo.
7534 template <typename T>
7535 struct UsdStage::_ResolveInfoResolver
7536 {
_ResolveInfoResolverUsdStage::_ResolveInfoResolver7537 explicit _ResolveInfoResolver(const UsdAttribute& attr,
7538 UsdResolveInfo* resolveInfo,
7539 UsdStage::_ExtraResolveInfo<T>* extraInfo)
7540 : _attr(attr),
7541 _resolveInfo(resolveInfo),
7542 _extraInfo(extraInfo)
7543 {
7544 }
7545
7546 bool
ProcessFallbackUsdStage::_ResolveInfoResolver7547 ProcessFallback()
7548 {
7549 if (const bool hasFallback =
7550 _attr._Prim()->GetPrimDefinition().GetAttributeFallbackValue(
7551 _attr.GetName(), _extraInfo->defaultOrFallbackValue)) {
7552 _resolveInfo->_source = UsdResolveInfoSourceFallback;
7553 return true;
7554 }
7555
7556 // No values at all.
7557 _resolveInfo->_source = UsdResolveInfoSourceNone;
7558 return true;
7559 }
7560
7561 bool
ProcessLayerUsdStage::_ResolveInfoResolver7562 ProcessLayer(const size_t layerStackPosition,
7563 const SdfPath& specPath,
7564 const PcpNodeRef& node,
7565 const double *time)
7566 {
7567 const PcpLayerStackRefPtr& nodeLayers = node.GetLayerStack();
7568 const SdfLayerRefPtrVector& layerStack = nodeLayers->GetLayers();
7569 const SdfLayerOffset layerToStageOffset =
7570 _GetLayerToStageOffset(node, layerStack[layerStackPosition]);
7571 const SdfLayerRefPtr& layer = layerStack[layerStackPosition];
7572 boost::optional<double> localTime;
7573 if (time) {
7574 localTime = layerToStageOffset.GetInverse() * (*time);
7575 }
7576
7577 if (_HasTimeSamples(layer, specPath, localTime.get_ptr(),
7578 &_extraInfo->lowerSample,
7579 &_extraInfo->upperSample)) {
7580 _resolveInfo->_source = UsdResolveInfoSourceTimeSamples;
7581 }
7582 else {
7583 Usd_DefaultValueResult defValue = Usd_HasDefault(
7584 layer, specPath, _extraInfo->defaultOrFallbackValue);
7585 if (defValue == Usd_DefaultValueResult::Found) {
7586 _resolveInfo->_source = UsdResolveInfoSourceDefault;
7587 }
7588 else if (defValue == Usd_DefaultValueResult::Blocked) {
7589 _resolveInfo->_valueIsBlocked = true;
7590 return ProcessFallback();
7591 }
7592 }
7593
7594 if (_resolveInfo->_source != UsdResolveInfoSourceNone) {
7595 _resolveInfo->_layerStack = nodeLayers;
7596 _resolveInfo->_layer = layer;
7597 _resolveInfo->_primPathInLayerStack = node.GetPath();
7598 _resolveInfo->_layerToStageOffset = layerToStageOffset;
7599 _resolveInfo->_node = node;
7600 return true;
7601 }
7602
7603 return false;
7604 }
7605
7606 bool
ProcessClipsUsdStage::_ResolveInfoResolver7607 ProcessClips(const Usd_ClipSetRefPtr& clipSet,
7608 const SdfPath& specPath,
7609 const PcpNodeRef& node,
7610 const double* time)
7611 {
7612 if (!_HasTimeSamples(
7613 clipSet, specPath, time,
7614 &_extraInfo->lowerSample, &_extraInfo->upperSample)) {
7615 return false;
7616 }
7617
7618 _extraInfo->clipSet = clipSet;
7619
7620 _resolveInfo->_source = UsdResolveInfoSourceValueClips;
7621 _resolveInfo->_layerStack = node.GetLayerStack();
7622 _resolveInfo->_primPathInLayerStack = node.GetPath();
7623 _resolveInfo->_node = node;
7624
7625 return true;
7626 }
7627
7628 private:
7629 const UsdAttribute& _attr;
7630 UsdResolveInfo* _resolveInfo;
7631 UsdStage::_ExtraResolveInfo<T>* _extraInfo;
7632 };
7633
7634 template <class T>
7635 void
_GetResolveInfo(const UsdAttribute & attr,UsdResolveInfo * resolveInfo,const UsdTimeCode * time,_ExtraResolveInfo<T> * extraInfo) const7636 UsdStage::_GetResolveInfo(const UsdAttribute &attr,
7637 UsdResolveInfo *resolveInfo,
7638 const UsdTimeCode *time,
7639 _ExtraResolveInfo<T> *extraInfo) const
7640 {
7641 _ExtraResolveInfo<T> localExtraInfo;
7642 if (!extraInfo) {
7643 extraInfo = &localExtraInfo;
7644 }
7645
7646 _ResolveInfoResolver<T> resolver(attr, resolveInfo, extraInfo);
7647 _GetResolvedValueImpl(attr, &resolver, time);
7648
7649 if (TfDebug::IsEnabled(USD_VALIDATE_VARIABILITY) &&
7650 (resolveInfo->_source == UsdResolveInfoSourceTimeSamples ||
7651 resolveInfo->_source == UsdResolveInfoSourceValueClips) &&
7652 _GetVariability(attr) == SdfVariabilityUniform) {
7653
7654 TF_DEBUG(USD_VALIDATE_VARIABILITY)
7655 .Msg("Warning: detected time sample value on "
7656 "uniform attribute <%s>\n",
7657 UsdDescribe(attr).c_str());
7658 }
7659 }
7660
7661 // This function takes a Resolver object, which is used to process opinions
7662 // in strength order. Resolvers must implement three functions:
7663 //
7664 // ProcessLayer()
7665 // ProcessClips()
7666 // ProcessFallback()
7667 //
7668 // Each of these functions is required to return true, to indicate that
7669 // iteration of opinions should stop, and false otherwise.
7670 template <class Resolver>
7671 void
_GetResolvedValueImpl(const UsdProperty & prop,Resolver * resolver,const UsdTimeCode * time) const7672 UsdStage::_GetResolvedValueImpl(const UsdProperty &prop,
7673 Resolver *resolver,
7674 const UsdTimeCode *time) const
7675 {
7676 auto primHandle = prop._Prim();
7677 boost::optional<double> localTime;
7678 if (time && !time->IsDefault()) {
7679 localTime = time->GetValue();
7680 }
7681
7682 // Retrieve all clips that may contribute time samples for this
7683 // attribute at the given time. Clips never contribute default
7684 // values.
7685 const std::vector<Usd_ClipSetRefPtr>* clipsAffectingPrim = nullptr;
7686 if (primHandle->MayHaveOpinionsInClips()
7687 && (!time || !time->IsDefault())) {
7688 clipsAffectingPrim =
7689 &(_clipCache->GetClipsForPrim(primHandle->GetPath()));
7690 }
7691
7692 // Clips may contribute opinions at nodes where no specs for the attribute
7693 // exist in the node's LayerStack. So, if we have any clips, tell
7694 // Usd_Resolver that we want to iterate over 'empty' nodes as well.
7695 const bool skipEmptyNodes = (bool)(!clipsAffectingPrim);
7696
7697 for (Usd_Resolver res(&primHandle->GetPrimIndex(), skipEmptyNodes);
7698 res.IsValid(); res.NextNode()) {
7699
7700 const PcpNodeRef& node = res.GetNode();
7701 const bool nodeHasSpecs = node.HasSpecs();
7702 if (!nodeHasSpecs && !clipsAffectingPrim) {
7703 continue;
7704 }
7705
7706 const SdfPath specPath = node.GetPath().AppendProperty(prop.GetName());
7707 const SdfLayerRefPtrVector& layerStack
7708 = node.GetLayerStack()->GetLayers();
7709 boost::optional<std::vector<Usd_ClipSetRefPtr>> clips;
7710 for (size_t i = 0, e = layerStack.size(); i < e; ++i) {
7711 if (nodeHasSpecs) {
7712 if (resolver->ProcessLayer(i, specPath, node,
7713 localTime.get_ptr())) {
7714 return;
7715 }
7716 }
7717
7718 if (clipsAffectingPrim){
7719 if (!clips) {
7720 clips = _GetClipsThatApplyToNode(*clipsAffectingPrim,
7721 node, specPath);
7722 // If we don't have specs on this node and clips don't
7723 // apply we can mode onto the next node.
7724 if (!nodeHasSpecs && clips->empty()) {
7725 break;
7726 }
7727 }
7728
7729 // gcc 4.8 incorrectly detects boost::optional as uninitialized.
7730 // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47679
7731 ARCH_PRAGMA_PUSH
7732 ARCH_PRAGMA_MAYBE_UNINITIALIZED
7733
7734 for (const Usd_ClipSetRefPtr& clipSet : *clips) {
7735 // We only care about clips that were introduced at this
7736 // position within the LayerStack.
7737 if (clipSet->sourceLayerIndex != i) {
7738 continue;
7739 }
7740
7741 // Look through clips to see if they have a time sample for
7742 // this attribute. If a time is given, examine just the clips
7743 // that are active at that time.
7744 if (resolver->ProcessClips(
7745 clipSet, specPath, node, localTime.get_ptr())) {
7746 return;
7747 }
7748 }
7749
7750 ARCH_PRAGMA_POP
7751 }
7752 }
7753 }
7754
7755 resolver->ProcessFallback();
7756 }
7757
7758 void
_GetResolveInfo(const UsdAttribute & attr,UsdResolveInfo * resolveInfo,const UsdTimeCode * time) const7759 UsdStage::_GetResolveInfo(const UsdAttribute &attr,
7760 UsdResolveInfo *resolveInfo,
7761 const UsdTimeCode *time) const
7762 {
7763 _GetResolveInfo<SdfAbstractDataValue>(attr, resolveInfo, time);
7764 }
7765
7766 template <class T>
7767 bool
_GetValueFromResolveInfoImpl(const UsdResolveInfo & info,UsdTimeCode time,const UsdAttribute & attr,Usd_InterpolatorBase * interpolator,T * result) const7768 UsdStage::_GetValueFromResolveInfoImpl(const UsdResolveInfo &info,
7769 UsdTimeCode time, const UsdAttribute &attr,
7770 Usd_InterpolatorBase* interpolator,
7771 T* result) const
7772 {
7773 if (info._source == UsdResolveInfoSourceTimeSamples) {
7774 return UsdStage_ResolveInfoAccess::_GetTimeSampleValue(
7775 time, attr, info, nullptr, nullptr, interpolator, result);
7776 }
7777 else if (info._source == UsdResolveInfoSourceDefault) {
7778 const SdfPath specPath =
7779 info._primPathInLayerStack.AppendProperty(attr.GetName());
7780 const SdfLayerHandle& layer = info._layer;
7781
7782 TF_DEBUG(USD_VALUE_RESOLUTION).Msg(
7783 "RESOLVE: reading field %s:%s from @%s@, with t = %.3f"
7784 " as default\n",
7785 specPath.GetText(),
7786 SdfFieldKeys->TimeSamples.GetText(),
7787 layer->GetIdentifier().c_str(),
7788 time.GetValue());
7789
7790 return TF_VERIFY(
7791 layer->HasField(specPath, SdfFieldKeys->Default, result));
7792 }
7793 else if (info._source == UsdResolveInfoSourceValueClips) {
7794 const SdfPath specPath =
7795 info._primPathInLayerStack.AppendProperty(attr.GetName());
7796
7797 const UsdPrim prim = attr.GetPrim();
7798 const std::vector<Usd_ClipSetRefPtr>& clipsAffectingPrim =
7799 _clipCache->GetClipsForPrim(prim.GetPath());
7800
7801 for (const auto& clipSet : clipsAffectingPrim) {
7802 if (!_ClipsApplyToLayerStackSite(
7803 clipSet, info._layerStack, info._primPathInLayerStack)
7804 || !_ClipsContainValueForAttribute(clipSet, specPath)) {
7805 continue;
7806 }
7807
7808 return UsdStage_ResolveInfoAccess::_GetClipValue(
7809 time, attr, info, clipSet, nullptr, nullptr,
7810 interpolator, result);
7811 }
7812 }
7813 else if (info._source == UsdResolveInfoSourceFallback) {
7814 // Get the fallback value.
7815 return attr._Prim()->GetPrimDefinition().GetAttributeFallbackValue(
7816 attr.GetName(), result);
7817 }
7818
7819 return false;
7820 }
7821
7822 bool
_GetValueFromResolveInfo(const UsdResolveInfo & info,UsdTimeCode time,const UsdAttribute & attr,VtValue * result) const7823 UsdStage::_GetValueFromResolveInfo(const UsdResolveInfo &info,
7824 UsdTimeCode time, const UsdAttribute &attr,
7825 VtValue* result) const
7826 {
7827 auto getValueImpl = [&info](const UsdStage &stage,
7828 UsdTimeCode time, const UsdAttribute &attr,
7829 Usd_InterpolatorBase* interpolator,
7830 VtValue* value)
7831 {
7832 return stage._GetValueFromResolveInfoImpl(
7833 info, time, attr, interpolator, value);
7834 };
7835
7836 return Usd_AttrGetUntypedValueHelper::GetValue(
7837 *this, time, attr, result, getValueImpl);
7838 }
7839
7840 template <class T>
7841 bool
_GetValueFromResolveInfo(const UsdResolveInfo & info,UsdTimeCode time,const UsdAttribute & attr,T * result) const7842 UsdStage::_GetValueFromResolveInfo(const UsdResolveInfo &info,
7843 UsdTimeCode time, const UsdAttribute &attr,
7844 T* result) const
7845 {
7846 auto getValueImpl = [&info](const UsdStage &stage,
7847 UsdTimeCode time, const UsdAttribute &attr,
7848 Usd_InterpolatorBase* interpolator,
7849 SdfAbstractDataValue* value)
7850 {
7851 return stage._GetValueFromResolveInfoImpl(
7852 info, time, attr, interpolator, value);
7853 };
7854
7855 return Usd_AttrGetValueHelper<T>::GetValue(
7856 *this, time, attr, result, getValueImpl);
7857 }
7858
7859 // --------------------------------------------------------------------- //
7860 // Specialized Time Sample I/O
7861 // --------------------------------------------------------------------- //
7862
7863 bool
_GetTimeSamplesInInterval(const UsdAttribute & attr,const GfInterval & interval,std::vector<double> * times) const7864 UsdStage::_GetTimeSamplesInInterval(const UsdAttribute& attr,
7865 const GfInterval& interval,
7866 std::vector<double>* times) const
7867 {
7868 UsdResolveInfo info;
7869 _GetResolveInfo(attr, &info);
7870 return _GetTimeSamplesInIntervalFromResolveInfo(info, attr, interval, times);
7871 }
7872
7873 bool
_GetTimeSamplesInIntervalFromResolveInfo(const UsdResolveInfo & info,const UsdAttribute & attr,const GfInterval & interval,std::vector<double> * times) const7874 UsdStage::_GetTimeSamplesInIntervalFromResolveInfo(
7875 const UsdResolveInfo &info,
7876 const UsdAttribute &attr,
7877 const GfInterval& interval,
7878 std::vector<double>* times) const
7879 {
7880 // An empty requested interval would result in in empty times
7881 // vector so avoid computing any of the contained samples
7882 if (interval.IsEmpty()) {
7883 return true;
7884 }
7885
7886 // This is the lowest-level site for guaranteeing that all GetTimeSample
7887 // queries clear out the return vector
7888 times->clear();
7889 const auto copySamplesInInterval = [](const std::set<double>& samples,
7890 vector<double>* target,
7891 const GfInterval& interval)
7892 {
7893 std::set<double>::iterator samplesBegin, samplesEnd;
7894
7895 if (interval.IsMinOpen()) {
7896 samplesBegin = std::upper_bound(samples.begin(),
7897 samples.end(),
7898 interval.GetMin());
7899 } else {
7900 samplesBegin = std::lower_bound(samples.begin(),
7901 samples.end(),
7902 interval.GetMin());
7903 }
7904
7905 if (interval.IsMaxOpen()) {
7906 samplesEnd = std::lower_bound(samplesBegin,
7907 samples.end(),
7908 interval.GetMax());
7909 } else {
7910 samplesEnd = std::upper_bound(samplesBegin,
7911 samples.end(),
7912 interval.GetMax());
7913 }
7914
7915 target->insert(target->end(), samplesBegin, samplesEnd);
7916 };
7917
7918 if (info._source == UsdResolveInfoSourceTimeSamples) {
7919 const SdfPath specPath =
7920 info._primPathInLayerStack.AppendProperty(attr.GetName());
7921 const SdfLayerHandle& layer = info._layer;
7922
7923 const std::set<double> samples =
7924 layer->ListTimeSamplesForPath(specPath);
7925 if (!samples.empty()) {
7926 if (info._layerToStageOffset.IsIdentity()) {
7927 // The layer offset is identity, so we can use the interval
7928 // directly, and do not need to remap the sample times.
7929 copySamplesInInterval(samples, times, interval);
7930 } else {
7931 // Map the interval (expressed in stage time) to layer time.
7932 const SdfLayerOffset stageToLayer =
7933 info._layerToStageOffset.GetInverse();
7934 const GfInterval layerInterval =
7935 interval * stageToLayer.GetScale()
7936 + stageToLayer.GetOffset();
7937 copySamplesInInterval(samples, times, layerInterval);
7938 // Map the layer sample times to stage times.
7939 for (auto &time : *times) {
7940 time = info._layerToStageOffset * time;
7941 }
7942 }
7943 }
7944
7945 return true;
7946 }
7947 else if (info._source == UsdResolveInfoSourceValueClips) {
7948 const UsdPrim prim = attr.GetPrim();
7949
7950 // See comments in _GetValueImpl regarding clips.
7951 const std::vector<Usd_ClipSetRefPtr>& clipsAffectingPrim =
7952 _clipCache->GetClipsForPrim(prim.GetPath());
7953
7954 const SdfPath specPath =
7955 info._primPathInLayerStack.AppendProperty(attr.GetName());
7956
7957 // Loop through all the clips that apply to this node and
7958 // combine all the time samples that are provided.
7959 for (const auto& clipSet : clipsAffectingPrim) {
7960 if (!_ClipsApplyToLayerStackSite(
7961 clipSet, info._layerStack, info._primPathInLayerStack)
7962 || !_ClipsContainValueForAttribute(clipSet, specPath)) {
7963 continue;
7964 }
7965
7966 // See comments in _GetValueImpl regarding layer
7967 // offsets and why they're not applied here.
7968 const std::set<double> samples =
7969 clipSet->ListTimeSamplesForPath(specPath);
7970 copySamplesInInterval(samples, times, interval);;
7971 return true;
7972 }
7973 }
7974
7975 return true;
7976 }
7977
7978 size_t
_GetNumTimeSamples(const UsdAttribute & attr) const7979 UsdStage::_GetNumTimeSamples(const UsdAttribute &attr) const
7980 {
7981 UsdResolveInfo info;
7982 _GetResolveInfo(attr, &info);
7983 return _GetNumTimeSamplesFromResolveInfo(info, attr);
7984
7985 }
7986
7987 size_t
_GetNumTimeSamplesFromResolveInfo(const UsdResolveInfo & info,const UsdAttribute & attr) const7988 UsdStage::_GetNumTimeSamplesFromResolveInfo(const UsdResolveInfo &info,
7989 const UsdAttribute &attr) const
7990 {
7991 if (info._source == UsdResolveInfoSourceTimeSamples) {
7992 const SdfPath specPath =
7993 info._primPathInLayerStack.AppendProperty(attr.GetName());
7994 const SdfLayerHandle& layer = info._layer;
7995
7996 return layer->GetNumTimeSamplesForPath(specPath);
7997 }
7998 else if (info._source == UsdResolveInfoSourceValueClips) {
7999 // XXX: optimization
8000 //
8001 // We don't have an efficient way of getting the number of time
8002 // samples from all the clips involved. To avoid code duplication,
8003 // simply get all the time samples and return the size here.
8004 //
8005 // This is good motivation for why we really need the ability to
8006 // ask the question of whether there is more than one sample directly.
8007 //
8008 std::vector<double> timesFromAllClips;
8009 _GetTimeSamplesInIntervalFromResolveInfo(info, attr,
8010 GfInterval::GetFullInterval(), ×FromAllClips);
8011 return timesFromAllClips.size();
8012 }
8013
8014 return 0;
8015 }
8016
8017 bool
_GetBracketingTimeSamples(const UsdAttribute & attr,double desiredTime,bool requireAuthored,double * lower,double * upper,bool * hasSamples) const8018 UsdStage::_GetBracketingTimeSamples(const UsdAttribute &attr,
8019 double desiredTime,
8020 bool requireAuthored,
8021 double* lower,
8022 double* upper,
8023 bool* hasSamples) const
8024 {
8025 const UsdTimeCode time(desiredTime);
8026
8027 UsdResolveInfo resolveInfo;
8028 _ExtraResolveInfo<SdfAbstractDataValue> extraInfo;
8029
8030 _GetResolveInfo<SdfAbstractDataValue>(
8031 attr, &resolveInfo, &time, &extraInfo);
8032
8033 if (resolveInfo._source == UsdResolveInfoSourceTimeSamples) {
8034 // In the time samples case, we bail out early to avoid another
8035 // call to SdfLayer::GetBracketingTimeSamples. _GetResolveInfo will
8036 // already have filled in the lower and upper samples with the
8037 // results of that function at the desired time.
8038 *lower = extraInfo.lowerSample;
8039 *upper = extraInfo.upperSample;
8040
8041 const SdfLayerOffset offset = resolveInfo._layerToStageOffset;
8042 if (!offset.IsIdentity()) {
8043 *lower = offset * (*lower);
8044 *upper = offset * (*upper);
8045 }
8046
8047 *hasSamples = true;
8048 return true;
8049 }
8050 else if (resolveInfo._source == UsdResolveInfoSourceValueClips) {
8051 *lower = extraInfo.lowerSample;
8052 *upper = extraInfo.upperSample;
8053 *hasSamples = true;
8054 return true;
8055 }
8056
8057 return _GetBracketingTimeSamplesFromResolveInfo(
8058 resolveInfo, attr, desiredTime, requireAuthored, lower, upper,
8059 hasSamples);
8060 }
8061
8062 bool
_GetBracketingTimeSamplesFromResolveInfo(const UsdResolveInfo & info,const UsdAttribute & attr,double desiredTime,bool requireAuthored,double * lower,double * upper,bool * hasSamples) const8063 UsdStage::_GetBracketingTimeSamplesFromResolveInfo(const UsdResolveInfo &info,
8064 const UsdAttribute &attr,
8065 double desiredTime,
8066 bool requireAuthored,
8067 double* lower,
8068 double* upper,
8069 bool* hasSamples) const
8070 {
8071 if (info._source == UsdResolveInfoSourceTimeSamples) {
8072 const SdfPath specPath =
8073 info._primPathInLayerStack.AppendProperty(attr.GetName());
8074 const SdfLayerHandle& layer = info._layer;
8075 const double layerTime =
8076 info._layerToStageOffset.GetInverse() * desiredTime;
8077
8078 if (layer->GetBracketingTimeSamplesForPath(
8079 specPath, layerTime, lower, upper)) {
8080
8081 if (!info._layerToStageOffset.IsIdentity()) {
8082 *lower = info._layerToStageOffset * (*lower);
8083 *upper = info._layerToStageOffset * (*upper);
8084 }
8085
8086 *hasSamples = true;
8087 return true;
8088 }
8089 }
8090 else if (info._source == UsdResolveInfoSourceDefault) {
8091 *hasSamples = false;
8092 return true;
8093 }
8094 else if (info._source == UsdResolveInfoSourceValueClips) {
8095 const SdfPath specPath =
8096 info._primPathInLayerStack.AppendProperty(attr.GetName());
8097
8098 const UsdPrim prim = attr.GetPrim();
8099
8100 // See comments in _GetValueImpl regarding clips.
8101 const std::vector<Usd_ClipSetRefPtr>& clipsAffectingPrim =
8102 _clipCache->GetClipsForPrim(prim.GetPath());
8103
8104 for (const auto& clipSet : clipsAffectingPrim) {
8105 if (!_ClipsApplyToLayerStackSite(
8106 clipSet, info._layerStack, info._primPathInLayerStack)
8107 || !_ClipsContainValueForAttribute(clipSet, specPath)) {
8108 continue;
8109 }
8110
8111 if (clipSet->GetBracketingTimeSamplesForPath(
8112 specPath, desiredTime, lower, upper)) {
8113 *hasSamples = true;
8114 return true;
8115 }
8116 }
8117 }
8118 else if (info._source == UsdResolveInfoSourceFallback) {
8119 // At this point, no authored value was found, so if the client only
8120 // wants authored values, we can exit.
8121 *hasSamples = false;
8122 if (requireAuthored)
8123 return false;
8124
8125 // Check for a registered fallback.
8126 if (SdfAttributeSpecHandle attrDef = _GetSchemaAttributeSpec(attr)) {
8127 if (attrDef->HasDefaultValue()) {
8128 *hasSamples = false;
8129 return true;
8130 }
8131 }
8132 }
8133
8134 // No authored value, no fallback.
8135 return false;
8136 }
8137
8138 static bool
_ValueFromClipsMightBeTimeVarying(const Usd_ClipSetRefPtr & clipSet,const SdfPath & attrSpecPath)8139 _ValueFromClipsMightBeTimeVarying(const Usd_ClipSetRefPtr &clipSet,
8140 const SdfPath &attrSpecPath)
8141 {
8142 // If there is only one clip active over all time and it has more than one
8143 // time sample for the attribute, it might be time varying. Otherwise the
8144 // attribute's value must be constant over all time.
8145 if (clipSet->valueClips.size() == 1) {
8146 const size_t numTimeSamples =
8147 clipSet->valueClips.front()->GetNumTimeSamplesForPath(attrSpecPath);
8148 return numTimeSamples > 1;
8149 }
8150
8151 // Since there are multiple clips active across all time, we can't say
8152 // for certain whether there are multiple time samples without
8153 // potentially opening every clip. So, we have to report that the value
8154 // might be time varying.
8155 return true;
8156 }
8157
8158 bool
_ValueMightBeTimeVarying(const UsdAttribute & attr) const8159 UsdStage::_ValueMightBeTimeVarying(const UsdAttribute &attr) const
8160 {
8161 UsdResolveInfo info;
8162 _ExtraResolveInfo<SdfAbstractDataValue> extraInfo;
8163 _GetResolveInfo(attr, &info, nullptr, &extraInfo);
8164
8165 if (info._source == UsdResolveInfoSourceValueClips) {
8166 // See comment in _ValueMightBeTimeVaryingFromResolveInfo.
8167 const SdfPath specPath =
8168 info._primPathInLayerStack.AppendProperty(attr.GetName());
8169 return _ValueFromClipsMightBeTimeVarying(extraInfo.clipSet, specPath);
8170 }
8171
8172 return _ValueMightBeTimeVaryingFromResolveInfo(info, attr);
8173 }
8174
8175 bool
_ValueMightBeTimeVaryingFromResolveInfo(const UsdResolveInfo & info,const UsdAttribute & attr) const8176 UsdStage::_ValueMightBeTimeVaryingFromResolveInfo(const UsdResolveInfo &info,
8177 const UsdAttribute &attr) const
8178 {
8179 if (info._source == UsdResolveInfoSourceValueClips) {
8180 // Do a specialized check for value clips instead of falling through
8181 // to calling _GetNumTimeSamplesFromResolveInfo, which requires opening
8182 // every clip to get the total time sample count.
8183 const SdfPath specPath =
8184 info._primPathInLayerStack.AppendProperty(attr.GetName());
8185
8186 const std::vector<Usd_ClipSetRefPtr>& clipsAffectingPrim =
8187 _clipCache->GetClipsForPrim(attr.GetPrim().GetPath());
8188 for (const auto& clipSet : clipsAffectingPrim) {
8189 if (!_ClipsApplyToLayerStackSite(
8190 clipSet, info._layerStack, info._primPathInLayerStack)) {
8191 continue;
8192 }
8193
8194 if (_HasTimeSamples(clipSet, specPath)) {
8195 return _ValueFromClipsMightBeTimeVarying(clipSet, specPath);
8196 }
8197 }
8198
8199 return false;
8200 }
8201
8202 return _GetNumTimeSamplesFromResolveInfo(info, attr) > 1;
8203 }
8204
8205 bool
GetMetadata(const TfToken & key,VtValue * value) const8206 UsdStage::GetMetadata(const TfToken &key, VtValue *value) const
8207 {
8208 if (!value){
8209 TF_CODING_ERROR(
8210 "Null out-param 'value' for UsdStage::GetMetadata(\"%s\")",
8211 key.GetText());
8212 return false;
8213
8214 }
8215 const SdfSchema &schema = SdfSchema::GetInstance();
8216
8217 if (!schema.IsValidFieldForSpec(key, SdfSpecTypePseudoRoot)){
8218 return false;
8219 }
8220
8221 if (!GetPseudoRoot().GetMetadata(key, value)) {
8222 *value = SdfSchema::GetInstance().GetFallback(key);
8223 } else if (value->IsHolding<VtDictionary>()){
8224 const VtDictionary &fallback = SdfSchema::GetInstance().GetFallback(key).Get<VtDictionary>();
8225
8226 VtDictionary dict;
8227 value->UncheckedSwap<VtDictionary>(dict);
8228 VtDictionaryOverRecursive(&dict, fallback);
8229 value->UncheckedSwap<VtDictionary>(dict);
8230 }
8231 return true;
8232 }
8233
8234 bool
HasMetadata(const TfToken & key) const8235 UsdStage::HasMetadata(const TfToken &key) const
8236 {
8237 const SdfSchema &schema = SdfSchema::GetInstance();
8238
8239 if (!schema.IsValidFieldForSpec(key, SdfSpecTypePseudoRoot))
8240 return false;
8241
8242 return (GetPseudoRoot().HasAuthoredMetadata(key) ||
8243 !schema.GetFallback(key).IsEmpty());
8244 }
8245
8246 bool
HasAuthoredMetadata(const TfToken & key) const8247 UsdStage::HasAuthoredMetadata(const TfToken& key) const
8248 {
8249 const SdfSchema &schema = SdfSchema::GetInstance();
8250
8251 if (!schema.IsValidFieldForSpec(key, SdfSpecTypePseudoRoot))
8252 return false;
8253
8254 return GetPseudoRoot().HasAuthoredMetadata(key);
8255 }
8256
8257 static
8258 void
_SetLayerFieldOrDictKey(const SdfLayerHandle & layer,const TfToken & key,const TfToken & keyPath,const VtValue & val)8259 _SetLayerFieldOrDictKey(const SdfLayerHandle &layer, const TfToken &key,
8260 const TfToken &keyPath, const VtValue &val)
8261 {
8262 if (keyPath.IsEmpty()) {
8263 layer->SetField(SdfPath::AbsoluteRootPath(), key, val);
8264 } else {
8265 layer->SetFieldDictValueByKey(SdfPath::AbsoluteRootPath(),
8266 key, keyPath, val);
8267 }
8268 }
8269
8270 static
8271 void
_ClearLayerFieldOrDictKey(const SdfLayerHandle & layer,const TfToken & key,const TfToken & keyPath)8272 _ClearLayerFieldOrDictKey(const SdfLayerHandle &layer, const TfToken &key,
8273 const TfToken &keyPath)
8274 {
8275 if (keyPath.IsEmpty()) {
8276 layer->EraseField(SdfPath::AbsoluteRootPath(), key);
8277 } else {
8278 layer->EraseFieldDictValueByKey(SdfPath::AbsoluteRootPath(),
8279 key, keyPath);
8280 }
8281 }
8282
8283 static
8284 bool
_SetStageMetadataOrDictKey(const UsdStage & stage,const TfToken & key,const TfToken & keyPath,const VtValue & val)8285 _SetStageMetadataOrDictKey(const UsdStage &stage, const TfToken &key,
8286 const TfToken &keyPath, const VtValue &val)
8287 {
8288 SdfLayerHandle rootLayer = stage.GetRootLayer();
8289 SdfLayerHandle sessionLayer = stage.GetSessionLayer();
8290 const SdfSchema &schema = SdfSchema::GetInstance();
8291
8292 if (!schema.IsValidFieldForSpec(key, SdfSpecTypePseudoRoot)) {
8293 TF_CODING_ERROR("Metadata '%s' is not registered as valid Layer "
8294 "metadata, and cannot be set on UsdStage %s.",
8295 key.GetText(),
8296 rootLayer->GetIdentifier().c_str());
8297 return false;
8298 }
8299
8300 const SdfLayerHandle &editTargetLayer = stage.GetEditTarget().GetLayer();
8301 if (editTargetLayer == rootLayer || editTargetLayer == sessionLayer) {
8302 _SetLayerFieldOrDictKey(editTargetLayer, key, keyPath, val);
8303 } else {
8304 TF_CODING_ERROR("Cannot set layer metadata '%s' in current edit "
8305 "target \"%s\", as it is not the root layer or "
8306 "session layer of stage \"%s\".",
8307 key.GetText(),
8308 editTargetLayer->GetIdentifier().c_str(),
8309 rootLayer->GetIdentifier().c_str());
8310 return false;
8311 }
8312
8313 return true;
8314 }
8315
8316 bool
SetMetadata(const TfToken & key,const VtValue & value) const8317 UsdStage::SetMetadata(const TfToken &key, const VtValue &value) const
8318 {
8319 return _SetStageMetadataOrDictKey(*this, key, TfToken(), value);
8320 }
8321
8322
8323 static
8324 bool
_ClearStageMetadataOrDictKey(const UsdStage & stage,const TfToken & key,const TfToken & keyPath)8325 _ClearStageMetadataOrDictKey(const UsdStage &stage, const TfToken &key,
8326 const TfToken &keyPath)
8327 {
8328 SdfLayerHandle rootLayer = stage.GetRootLayer();
8329 SdfLayerHandle sessionLayer = stage.GetSessionLayer();
8330 const SdfSchema &schema = SdfSchema::GetInstance();
8331
8332 if (!schema.IsValidFieldForSpec(key, SdfSpecTypePseudoRoot)) {
8333 TF_CODING_ERROR("Metadata '%s' is not registered as valid Layer "
8334 "metadata, and cannot be cleared on UsdStage %s.",
8335 key.GetText(),
8336 rootLayer->GetIdentifier().c_str());
8337 return false;
8338 }
8339
8340 const SdfLayerHandle &editTargetLayer = stage.GetEditTarget().GetLayer();
8341 if (editTargetLayer == rootLayer || editTargetLayer == sessionLayer) {
8342 _ClearLayerFieldOrDictKey(editTargetLayer, key, keyPath);
8343 } else {
8344 TF_CODING_ERROR("Cannot clear layer metadata '%s' in current edit "
8345 "target \"%s\", as it is not the root layer or "
8346 "session layer of stage \"%s\".",
8347 key.GetText(),
8348 editTargetLayer->GetIdentifier().c_str(),
8349 rootLayer->GetIdentifier().c_str());
8350 return false;
8351 }
8352
8353 return true;
8354 }
8355
8356 bool
ClearMetadata(const TfToken & key) const8357 UsdStage::ClearMetadata(const TfToken &key) const
8358 {
8359 return _ClearStageMetadataOrDictKey(*this, key, TfToken());
8360 }
8361
8362 bool
GetMetadataByDictKey(const TfToken & key,const TfToken & keyPath,VtValue * value) const8363 UsdStage::GetMetadataByDictKey(const TfToken& key, const TfToken &keyPath,
8364 VtValue *value) const
8365 {
8366 if (keyPath.IsEmpty())
8367 return false;
8368
8369 if (!value){
8370 TF_CODING_ERROR(
8371 "Null out-param 'value' for UsdStage::GetMetadataByDictKey"
8372 "(\"%s\", \"%s\")",
8373 key.GetText(), keyPath.GetText());
8374 return false;
8375
8376 }
8377 const SdfSchema &schema = SdfSchema::GetInstance();
8378
8379 if (!schema.IsValidFieldForSpec(key, SdfSpecTypePseudoRoot))
8380 return false;
8381
8382 if (!GetPseudoRoot().GetMetadataByDictKey(key, keyPath, value)) {
8383 const VtValue &fallback = SdfSchema::GetInstance().GetFallback(key);
8384 if (!fallback.IsEmpty()){
8385 const VtValue *elt = fallback.Get<VtDictionary>().
8386 GetValueAtPath(keyPath);
8387 if (elt){
8388 *value = *elt;
8389 return true;
8390 }
8391 }
8392 return false;
8393 }
8394 else if (value->IsHolding<VtDictionary>()){
8395 const VtDictionary &fallback = SdfSchema::GetInstance().GetFallback(key).Get<VtDictionary>();
8396 const VtValue *elt = fallback.GetValueAtPath(keyPath);
8397 if (elt && elt->IsHolding<VtDictionary>()){
8398 VtDictionary dict;
8399 value->UncheckedSwap<VtDictionary>(dict);
8400 VtDictionaryOverRecursive(&dict, elt->UncheckedGet<VtDictionary>());
8401 value->UncheckedSwap<VtDictionary>(dict);
8402 }
8403 }
8404
8405 return true;
8406 }
8407
8408 bool
HasMetadataDictKey(const TfToken & key,const TfToken & keyPath) const8409 UsdStage::HasMetadataDictKey(const TfToken& key, const TfToken &keyPath) const
8410 {
8411 const SdfSchema &schema = SdfSchema::GetInstance();
8412
8413 if (keyPath.IsEmpty() ||
8414 !schema.IsValidFieldForSpec(key, SdfSpecTypePseudoRoot))
8415 return false;
8416
8417 if (GetPseudoRoot().HasAuthoredMetadataDictKey(key, keyPath)) {
8418 return true;
8419 }
8420
8421 const VtValue &fallback = schema.GetFallback(key);
8422
8423 return ((!fallback.IsEmpty()) &&
8424 (fallback.Get<VtDictionary>().GetValueAtPath(keyPath) != nullptr));
8425 }
8426
8427 bool
HasAuthoredMetadataDictKey(const TfToken & key,const TfToken & keyPath) const8428 UsdStage::HasAuthoredMetadataDictKey(
8429 const TfToken& key, const TfToken &keyPath) const
8430 {
8431 if (keyPath.IsEmpty())
8432 return false;
8433
8434 return GetPseudoRoot().HasAuthoredMetadataDictKey(key, keyPath);
8435 }
8436
8437 bool
SetMetadataByDictKey(const TfToken & key,const TfToken & keyPath,const VtValue & value) const8438 UsdStage::SetMetadataByDictKey(
8439 const TfToken& key, const TfToken &keyPath, const VtValue& value) const
8440 {
8441 if (keyPath.IsEmpty())
8442 return false;
8443
8444 return _SetStageMetadataOrDictKey(*this, key, keyPath, value);
8445 }
8446
8447 bool
ClearMetadataByDictKey(const TfToken & key,const TfToken & keyPath) const8448 UsdStage::ClearMetadataByDictKey(
8449 const TfToken& key, const TfToken& keyPath) const
8450 {
8451 if (keyPath.IsEmpty())
8452 return false;
8453
8454 return _ClearStageMetadataOrDictKey(*this, key, keyPath);
8455 }
8456
8457 ///////////////////////////////////////////////////////////////////////////////
8458 // XXX(Frame->Time): backwards compatibility
8459 // Temporary helper functions to support backwards compatibility.
8460 ///////////////////////////////////////////////////////////////////////////////
8461
8462 static
8463 bool
_HasStartFrame(const SdfLayerConstHandle & layer)8464 _HasStartFrame(const SdfLayerConstHandle &layer)
8465 {
8466 return layer->GetPseudoRoot()->HasInfo(SdfFieldKeys->StartFrame);
8467 }
8468
8469 static
8470 bool
_HasEndFrame(const SdfLayerConstHandle & layer)8471 _HasEndFrame(const SdfLayerConstHandle &layer)
8472 {
8473 return layer->GetPseudoRoot()->HasInfo(SdfFieldKeys->EndFrame);
8474 }
8475
8476 static
8477 double
_GetStartFrame(const SdfLayerConstHandle & layer)8478 _GetStartFrame(const SdfLayerConstHandle &layer)
8479 {
8480 VtValue startFrame = layer->GetPseudoRoot()->GetInfo(SdfFieldKeys->StartFrame);
8481 if (startFrame.IsHolding<double>())
8482 return startFrame.UncheckedGet<double>();
8483 return 0.0;
8484 }
8485
8486 static
8487 double
_GetEndFrame(const SdfLayerConstHandle & layer)8488 _GetEndFrame(const SdfLayerConstHandle &layer)
8489 {
8490 VtValue endFrame = layer->GetPseudoRoot()->GetInfo(SdfFieldKeys->EndFrame);
8491 if (endFrame.IsHolding<double>())
8492 return endFrame.UncheckedGet<double>();
8493 return 0.0;
8494 }
8495
8496 //////////////////////////////////////////////////////////////////////////////
8497
8498 // XXX bug/123508 - Once we can remove backwards compatibility with
8499 // startFrame/endFrame, these methods can become as simple as those for
8500 // TimeCodesPerSecond and FramesPerSecond
8501 double
GetStartTimeCode() const8502 UsdStage::GetStartTimeCode() const
8503 {
8504 // Look for 'startTimeCode' first. If it is not available, then look for
8505 // the deprecated field 'startFrame'.
8506 const SdfLayerConstHandle sessionLayer = GetSessionLayer();
8507 if (sessionLayer) {
8508 if (sessionLayer->HasStartTimeCode())
8509 return sessionLayer->GetStartTimeCode();
8510 else if (_HasStartFrame(sessionLayer))
8511 return _GetStartFrame(sessionLayer);
8512 }
8513
8514 if (GetRootLayer()->HasStartTimeCode())
8515 return GetRootLayer()->GetStartTimeCode();
8516 return _GetStartFrame(GetRootLayer());
8517 }
8518
8519 void
SetStartTimeCode(double startTime)8520 UsdStage::SetStartTimeCode(double startTime)
8521 {
8522 SetMetadata(SdfFieldKeys->StartTimeCode, startTime);
8523 }
8524
8525 double
GetEndTimeCode() const8526 UsdStage::GetEndTimeCode() const
8527 {
8528 // Look for 'endTimeCode' first. If it is not available, then look for
8529 // the deprecated field 'startFrame'.
8530 const SdfLayerConstHandle sessionLayer = GetSessionLayer();
8531 if (sessionLayer) {
8532 if (sessionLayer->HasEndTimeCode())
8533 return sessionLayer->GetEndTimeCode();
8534 else if (_HasEndFrame(sessionLayer))
8535 return _GetEndFrame(sessionLayer);
8536 }
8537
8538 if (GetRootLayer()->HasEndTimeCode())
8539 return GetRootLayer()->GetEndTimeCode();
8540 return _GetEndFrame(GetRootLayer());
8541 }
8542
8543 void
SetEndTimeCode(double endTime)8544 UsdStage::SetEndTimeCode(double endTime)
8545 {
8546 SetMetadata(SdfFieldKeys->EndTimeCode, endTime);
8547 }
8548
8549 bool
HasAuthoredTimeCodeRange() const8550 UsdStage::HasAuthoredTimeCodeRange() const
8551 {
8552 SdfLayerHandle rootLayer = GetRootLayer();
8553 SdfLayerHandle sessionLayer = GetSessionLayer();
8554
8555 return (sessionLayer &&
8556 ((sessionLayer->HasStartTimeCode() && sessionLayer->HasEndTimeCode()) ||
8557 (_HasStartFrame(sessionLayer) && _HasEndFrame(sessionLayer)))) ||
8558 (rootLayer &&
8559 ((rootLayer->HasStartTimeCode() && rootLayer->HasEndTimeCode()) ||
8560 (_HasStartFrame(rootLayer) && _HasEndFrame(rootLayer))));
8561 }
8562
8563 double
GetTimeCodesPerSecond() const8564 UsdStage::GetTimeCodesPerSecond() const
8565 {
8566 // PcpLayerStack computes timeCodesPerSecond for its map function layer
8567 // offsets. The root layer stack will always have the stage's fully
8568 // computed timeCodesPerSecond value accounting for the unique interaction
8569 // between the root and session layer.
8570 const PcpLayerStackPtr localLayerStack = _GetPcpCache()->GetLayerStack();
8571 return localLayerStack->GetTimeCodesPerSecond();
8572 }
8573
8574 void
SetTimeCodesPerSecond(double timeCodesPerSecond) const8575 UsdStage::SetTimeCodesPerSecond(double timeCodesPerSecond) const
8576 {
8577 SetMetadata(SdfFieldKeys->TimeCodesPerSecond, timeCodesPerSecond);
8578 }
8579
8580 double
GetFramesPerSecond() const8581 UsdStage::GetFramesPerSecond() const
8582 {
8583 // We expect the SdfSchema to provide a fallback, so simply:
8584 double result = 0;
8585 GetMetadata(SdfFieldKeys->FramesPerSecond, &result);
8586 return result;
8587 }
8588
8589 void
SetFramesPerSecond(double framesPerSecond) const8590 UsdStage::SetFramesPerSecond(double framesPerSecond) const
8591 {
8592 SetMetadata(SdfFieldKeys->FramesPerSecond, framesPerSecond);
8593 }
8594
8595 void
SetColorConfiguration(const SdfAssetPath & colorConfig) const8596 UsdStage::SetColorConfiguration(const SdfAssetPath &colorConfig) const
8597 {
8598 SetMetadata(SdfFieldKeys->ColorConfiguration, colorConfig);
8599 }
8600
8601 SdfAssetPath
GetColorConfiguration() const8602 UsdStage::GetColorConfiguration() const
8603 {
8604 SdfAssetPath colorConfig;
8605 GetMetadata(SdfFieldKeys->ColorConfiguration, &colorConfig);
8606
8607 return colorConfig.GetAssetPath().empty() ?
8608 _colorConfigurationFallbacks->first : colorConfig;
8609 }
8610
8611 void
SetColorManagementSystem(const TfToken & cms) const8612 UsdStage::SetColorManagementSystem(const TfToken &cms) const
8613 {
8614 SetMetadata(SdfFieldKeys->ColorManagementSystem, cms);
8615 }
8616
8617 TfToken
GetColorManagementSystem() const8618 UsdStage::GetColorManagementSystem() const
8619 {
8620 TfToken cms;
8621 GetMetadata(SdfFieldKeys->ColorManagementSystem, &cms);
8622
8623 return cms.IsEmpty() ? _colorConfigurationFallbacks->second : cms;
8624 }
8625
8626 /* static */
8627 void
GetColorConfigFallbacks(SdfAssetPath * colorConfiguration,TfToken * colorManagementSystem)8628 UsdStage::GetColorConfigFallbacks(
8629 SdfAssetPath *colorConfiguration,
8630 TfToken *colorManagementSystem)
8631 {
8632 if (colorConfiguration) {
8633 *colorConfiguration = _colorConfigurationFallbacks->first;
8634 }
8635 if (colorManagementSystem) {
8636 *colorManagementSystem = _colorConfigurationFallbacks->second;
8637 }
8638 }
8639
8640 /* static */
8641 void
SetColorConfigFallbacks(const SdfAssetPath & colorConfiguration,const TfToken & colorManagementSystem)8642 UsdStage::SetColorConfigFallbacks(
8643 const SdfAssetPath &colorConfiguration,
8644 const TfToken &colorManagementSystem)
8645 {
8646 if (!colorConfiguration.GetAssetPath().empty())
8647 _colorConfigurationFallbacks->first = colorConfiguration;
8648 if (!colorManagementSystem.IsEmpty())
8649 _colorConfigurationFallbacks->second = colorManagementSystem;
8650 }
8651
8652 std::string
ResolveIdentifierToEditTarget(std::string const & identifier) const8653 UsdStage::ResolveIdentifierToEditTarget(std::string const &identifier) const
8654 {
8655 const SdfLayerHandle &anchor = _editTarget.GetLayer();
8656
8657 // This check finds anonymous layers, which we consider to always resolve
8658 if (SdfLayer::IsAnonymousLayerIdentifier(identifier)) {
8659 if (SdfLayerHandle lyr = SdfLayer::Find(identifier)){
8660 TF_DEBUG(USD_PATH_RESOLUTION).Msg(
8661 "Resolved identifier %s because it was anonymous\n",
8662 identifier.c_str());
8663 return identifier;
8664 }
8665 else {
8666 TF_DEBUG(USD_PATH_RESOLUTION).Msg(
8667 "Resolved identifier %s to \"\" because it was anonymous but "
8668 "no layer is open with that identifier\n",
8669 identifier.c_str());
8670 return std::string();
8671 }
8672 }
8673
8674 ArResolverContextBinder binder(GetPathResolverContext());
8675
8676 // Handles non-relative paths also
8677 const std::string resolved =
8678 _ResolveAssetPathRelativeToLayer(anchor, identifier);
8679 TF_DEBUG(USD_PATH_RESOLUTION).Msg("Resolved identifier \"%s\" against layer "
8680 "@%s@ to: \"%s\"\n",
8681 identifier.c_str(),
8682 anchor->GetIdentifier().c_str(),
8683 resolved.c_str());
8684 return resolved;
8685 }
8686
8687 void
SetInterpolationType(UsdInterpolationType interpolationType)8688 UsdStage::SetInterpolationType(UsdInterpolationType interpolationType)
8689 {
8690 if (_interpolationType != interpolationType) {
8691 _interpolationType = interpolationType;
8692
8693 // Notify, as interpolated attributes values have likely changed.
8694 UsdStageWeakPtr self(this);
8695 UsdNotice::ObjectsChanged::_PathsToChangesMap resyncChanges, infoChanges;
8696 resyncChanges[SdfPath::AbsoluteRootPath()];
8697 UsdNotice::ObjectsChanged(self, &resyncChanges, &infoChanges).Send(self);
8698 UsdNotice::StageContentsChanged(self).Send(self);
8699 }
8700 }
8701
8702 UsdInterpolationType
GetInterpolationType() const8703 UsdStage::GetInterpolationType() const
8704 {
8705 return _interpolationType;
8706 }
8707
UsdDescribe(const UsdStage * stage)8708 std::string UsdDescribe(const UsdStage *stage) {
8709 if (!stage) {
8710 return "null stage";
8711 } else {
8712 return TfStringPrintf(
8713 "stage with rootLayer @%s@%s",
8714 stage->GetRootLayer()->GetIdentifier().c_str(),
8715 (stage->GetSessionLayer() ? TfStringPrintf(
8716 ", sessionLayer @%s@", stage->GetSessionLayer()->
8717 GetIdentifier().c_str()).c_str() : ""));
8718 }
8719 }
8720
UsdDescribe(const UsdStage & stage)8721 std::string UsdDescribe(const UsdStage &stage) {
8722 return UsdDescribe(&stage);
8723 }
8724
UsdDescribe(const UsdStagePtr & stage)8725 std::string UsdDescribe(const UsdStagePtr &stage) {
8726 return UsdDescribe(get_pointer(stage));
8727 }
8728
UsdDescribe(const UsdStageRefPtr & stage)8729 std::string UsdDescribe(const UsdStageRefPtr &stage) {
8730 return UsdDescribe(get_pointer(stage));
8731 }
8732
8733 // Explicitly instantiate templated getters and setters for all Sdf value
8734 // types.
8735 #define _INSTANTIATE_GET(r, unused, elem) \
8736 template bool UsdStage::_GetValue( \
8737 UsdTimeCode, const UsdAttribute&, \
8738 SDF_VALUE_CPP_TYPE(elem)*) const; \
8739 template bool UsdStage::_GetValue( \
8740 UsdTimeCode, const UsdAttribute&, \
8741 SDF_VALUE_CPP_ARRAY_TYPE(elem)*) const; \
8742 \
8743 template bool UsdStage::_GetValueFromResolveInfo( \
8744 const UsdResolveInfo&, UsdTimeCode, const UsdAttribute&, \
8745 SDF_VALUE_CPP_TYPE(elem)*) const; \
8746 template bool UsdStage::_GetValueFromResolveInfo( \
8747 const UsdResolveInfo&, UsdTimeCode, const UsdAttribute&, \
8748 SDF_VALUE_CPP_ARRAY_TYPE(elem)*) const; \
8749 \
8750 template bool UsdStage::_SetValue( \
8751 UsdTimeCode, const UsdAttribute&, \
8752 const SDF_VALUE_CPP_TYPE(elem)&); \
8753 template bool UsdStage::_SetValue( \
8754 UsdTimeCode, const UsdAttribute&, \
8755 const SDF_VALUE_CPP_ARRAY_TYPE(elem)&);
8756
8757 BOOST_PP_SEQ_FOR_EACH(_INSTANTIATE_GET, ~, SDF_VALUE_TYPES)
8758 #undef _INSTANTIATE_GET
8759
8760 // In addition to the Sdf value types, _SetValue can also be called with an
8761 // SdfValueBlock.
8762 template bool UsdStage::_SetValue(
8763 UsdTimeCode, const UsdAttribute&, const SdfValueBlock &);
8764
8765 // Explicitly instantiate the templated _SetEditTargetMappedMetadata and
8766 // _GetTypeSpecificResolvedMetadata functions for the types that support each.
8767 // The types instantiated here must match the types whose value is true for
8768 // _HasTypeSpecificResolution<T> and _IsEditTargetMappable<T>.
8769 #define INSTANTIATE_SET_MAPPED_METADATA(elem) \
8770 template USD_API bool UsdStage::_SetEditTargetMappedMetadata( \
8771 const UsdObject &, const TfToken&, const TfToken &, const elem &);
8772
8773 #define INSTANTIATE_GET_TYPE_RESOLVED_METADATA(elem) \
8774 template USD_API bool UsdStage::_GetTypeSpecificResolvedMetadata( \
8775 const UsdObject &, const TfToken&, const TfToken &, bool, elem *) const;
8776
8777 #define INSTANTIATE_GET_TYPE_RESOLVED_AND_SET_MAPPED_METADATA(elem) \
8778 INSTANTIATE_GET_TYPE_RESOLVED_METADATA(elem); \
8779 INSTANTIATE_SET_MAPPED_METADATA(elem);
8780
8781 INSTANTIATE_GET_TYPE_RESOLVED_METADATA(SdfAssetPath);
8782 INSTANTIATE_GET_TYPE_RESOLVED_METADATA(VtArray<SdfAssetPath>);
8783 INSTANTIATE_GET_TYPE_RESOLVED_AND_SET_MAPPED_METADATA(SdfTimeCode);
8784 INSTANTIATE_GET_TYPE_RESOLVED_AND_SET_MAPPED_METADATA(VtArray<SdfTimeCode>);
8785 // Do not explicitly instantiate _GetTypeSpecificResolvedMetadata for
8786 // SdfTimeSampleMap because we provide a specialization instead.
8787 INSTANTIATE_SET_MAPPED_METADATA(SdfTimeSampleMap);
8788 INSTANTIATE_GET_TYPE_RESOLVED_AND_SET_MAPPED_METADATA(VtDictionary);
8789
8790 #undef INSTANTIATE_GET_TYPE_RESOLVED_AND_SET_MAPPED_METADATA
8791 #undef INSTANTIATE_GET_TYPE_RESOLVED_METADATA
8792 #undef INSTANTIATE_SET_MAPPED_METADATA
8793
8794 // Make sure both versions of _SetMetadataImpl are instantiated as they are
8795 // directly called from UsdObject.
8796 template USD_API bool UsdStage::_SetMetadataImpl(
8797 const UsdObject &, const TfToken &, const TfToken &,
8798 const VtValue &);
8799 template USD_API bool UsdStage::_SetMetadataImpl(
8800 const UsdObject &, const TfToken &, const TfToken &,
8801 const SdfAbstractDataConstValue &);
8802
8803 PXR_NAMESPACE_CLOSE_SCOPE
8804
8805