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/schemaRegistry.h"
26 #include "pxr/usd/usd/primDefinition.h"
27
28 #include "pxr/usd/usd/debugCodes.h"
29 #include "pxr/usd/usd/clip.h"
30 #include "pxr/usd/usd/typed.h"
31 #include "pxr/usd/usd/schemaBase.h"
32 #include "pxr/usd/usd/apiSchemaBase.h"
33 #include "pxr/usd/usd/tokens.h"
34
35 #include "pxr/base/plug/plugin.h"
36 #include "pxr/base/plug/registry.h"
37 #include "pxr/usd/sdf/attributeSpec.h"
38 #include "pxr/usd/sdf/changeBlock.h"
39 #include "pxr/usd/sdf/primSpec.h"
40 #include "pxr/usd/sdf/propertySpec.h"
41 #include "pxr/usd/sdf/relationshipSpec.h"
42 #include "pxr/usd/sdf/schema.h"
43
44 #include "pxr/base/tf/envSetting.h"
45 #include "pxr/base/tf/fileUtils.h"
46 #include "pxr/base/tf/instantiateSingleton.h"
47 #include "pxr/base/tf/registryManager.h"
48 #include "pxr/base/tf/token.h"
49 #include "pxr/base/tf/type.h"
50 #include "pxr/base/work/loops.h"
51 #include "pxr/base/work/withScopedParallelism.h"
52
53 #include <set>
54 #include <utility>
55 #include <vector>
56
57 PXR_NAMESPACE_OPEN_SCOPE
58
59 using std::set;
60 using std::string;
61 using std::vector;
62
63 TF_DEFINE_ENV_SETTING(
64 USD_DISABLE_PRIM_DEFINITIONS_FOR_USDGENSCHEMA, false,
65 "Set to true to disable the generation of prim definitions for schema "
66 "types in the schema registry. This is used is to prevent the processing "
67 "of generatedSchema.usda files during schema generation as it's the "
68 "process used to create, update, or fix generatedSchema.usda files. "
69 "This should only be used by usdGenSchema.py as this can cause crashes in "
70 "most contexts which expect prim definitions for schema types.");
71
72 TF_DEFINE_ENV_SETTING(
73 USD_DISABLE_AUTO_APPLY_API_SCHEMAS, false,
74 "Set to true to disable the application of all auto-apply API schemas.");
75
76 TF_INSTANTIATE_SINGLETON(UsdSchemaRegistry);
77
78 TF_DEFINE_PRIVATE_TOKENS(
79 _tokens,
80 (appliedAPISchemas)
81 (multipleApplyAPISchemas)
82 (multipleApplyAPISchemaPrefixes)
83 (autoApplyAPISchemas)
84
85 (apiSchemaAutoApplyTo)
86 (apiSchemaCanOnlyApplyTo)
87 (apiSchemaAllowedInstanceNames)
88 (apiSchemaInstances)
89 (schemaKind)
90 (nonAppliedAPI)
91 (singleApplyAPI)
92 (multipleApplyAPI)
93 (concreteTyped)
94 (abstractTyped)
95 (abstractBase)
96
97 ((PluginAutoApplyAPISchemasKey, "AutoApplyAPISchemas"))
98 );
99
100 using _TypeToTokenVecMap =
101 TfHashMap<TfType, TfTokenVector, TfHash>;
102 using _TokenToTokenMap =
103 TfHashMap<TfToken, TfToken, TfToken::HashFunctor>;
104
105 namespace {
106 // Helper struct for caching a bidirecional mapping between schema TfType and
107 // USD type name token. This cache is used as a static local instance providing
108 // this type mapping without having to build the entire schema registry
109 struct _TypeMapCache {
_TypeMapCache__anond69f1a350111::_TypeMapCache110 _TypeMapCache() {
111 const TfType schemaBaseType = TfType::Find<UsdSchemaBase>();
112
113 auto _MapDerivedTypes = [this, &schemaBaseType](
114 const TfType &baseType, bool isTyped)
115 {
116 set<TfType> types;
117 PlugRegistry::GetAllDerivedTypes(baseType, &types);
118 for (const TfType &type : types) {
119 // The USD type name is the type's alias under UsdSchemaBase.
120 // All schemas should have a type name alias.
121 const vector<string> aliases = schemaBaseType.GetAliases(type);
122 if (aliases.size() == 1) {
123 TfToken typeName(aliases.front(), TfToken::Immortal);
124 nameToType.insert(std::make_pair(
125 typeName, TypeInfo(type, isTyped)));
126 typeToName.insert(std::make_pair(
127 type, TypeNameInfo(typeName, isTyped)));
128 }
129 }
130 };
131
132 _MapDerivedTypes(TfType::Find<UsdTyped>(), /*isTyped=*/true);
133 _MapDerivedTypes(TfType::Find<UsdAPISchemaBase>(), /*isTyped=*/false);
134 }
135
136 // For each type and type name mapping we also want to store if it's a
137 // concrete prim type vs an API schema type.
138 struct TypeInfo {
139 TfType type;
140 bool isTyped;
TypeInfo__anond69f1a350111::_TypeMapCache::TypeInfo141 TypeInfo(const TfType &type_, bool isTyped_)
142 : type(type_), isTyped(isTyped_) {}
143 };
144
145 struct TypeNameInfo {
146 TfToken name;
147 bool isTyped;
TypeNameInfo__anond69f1a350111::_TypeMapCache::TypeNameInfo148 TypeNameInfo(const TfToken &name_, bool isTyped_)
149 : name(name_), isTyped(isTyped_) {}
150 };
151
152 TfHashMap<TfToken, TypeInfo, TfHash> nameToType;
153 TfHashMap<TfType, TypeNameInfo, TfHash> typeToName;
154 };
155
156 // Static singleton accessor
157 static const _TypeMapCache &
_GetTypeMapCache()158 _GetTypeMapCache() {
159 static _TypeMapCache typeCache;
160 return typeCache;
161 }
162
163 // Helper struct for caching the information extracted from plugin metadata
164 // about how API schema types are applied.
165 struct _APISchemaApplyToInfoCache {
_APISchemaApplyToInfoCache__anond69f1a350111::_APISchemaApplyToInfoCache166 _APISchemaApplyToInfoCache()
167 {
168 TRACE_FUNCTION();
169
170 // Get all types that derive UsdSchemaBase by getting the type map
171 // cache.
172 const _TypeMapCache &typeCache = _GetTypeMapCache();
173
174 // For each schema type, extract the can apply and auto apply plugin
175 // info into the cache.
176 for (const auto &valuePair : typeCache.typeToName) {
177 const TfType &type = valuePair.first;
178 const TfToken &typeName = valuePair.second.name;
179
180 Usd_GetAPISchemaPluginApplyToInfoForType(
181 type,
182 typeName,
183 &autoApplyAPISchemasMap,
184 &canOnlyApplyAPISchemasMap,
185 &allowedInstanceNamesMap);
186 }
187
188 // Collect any plugin auto apply API schema mappings. These can be
189 // defined in any plugin to auto apply schemas in a particular
190 // application context instead of the type itself being defined to
191 // always auto apply whenever it is present.
192 UsdSchemaRegistry::CollectAddtionalAutoApplyAPISchemasFromPlugins(
193 &autoApplyAPISchemasMap);
194 }
195
196 // Mapping of API schema type name to a list of type names it should auto
197 // applied to.
198 std::map<TfToken, TfTokenVector> autoApplyAPISchemasMap;
199
200 // Mapping of API schema type name to a list of prim type names that it
201 // is ONLY allowed to be applied to.
202 TfHashMap<TfToken, TfTokenVector, TfHash> canOnlyApplyAPISchemasMap;
203
204 // Mapping of multiple apply API schema type name to a set of instance names
205 // that are the only allowed instance names for that type.
206 TfHashMap<TfToken, TfToken::Set, TfHash> allowedInstanceNamesMap;
207 };
208
209 // Static singleton accessor
210 static const _APISchemaApplyToInfoCache &
_GetAPISchemaApplyToInfoCache()211 _GetAPISchemaApplyToInfoCache() {
212 static _APISchemaApplyToInfoCache applyToInfo;
213 return applyToInfo;
214 }
215
216 }; // end anonymous namespace
217
218 static bool
_IsConcreteSchemaKind(const UsdSchemaKind schemaKind)219 _IsConcreteSchemaKind(const UsdSchemaKind schemaKind)
220 {
221 return schemaKind == UsdSchemaKind::ConcreteTyped;
222 }
223
224 static bool
_IsAppliedAPISchemaKind(const UsdSchemaKind schemaKind)225 _IsAppliedAPISchemaKind(const UsdSchemaKind schemaKind)
226 {
227 return schemaKind == UsdSchemaKind::SingleApplyAPI ||
228 schemaKind == UsdSchemaKind::MultipleApplyAPI;
229 }
230
231 static bool
_IsMultipleApplySchemaKind(const UsdSchemaKind schemaKind)232 _IsMultipleApplySchemaKind(const UsdSchemaKind schemaKind)
233 {
234 return schemaKind == UsdSchemaKind::MultipleApplyAPI;
235 }
236
237 static UsdSchemaKind
_GetSchemaKindFromMetadata(const JsObject & dict)238 _GetSchemaKindFromMetadata(const JsObject &dict)
239 {
240 const JsValue *kindValue = TfMapLookupPtr(dict, _tokens->schemaKind);
241 if (!kindValue) {
242 return UsdSchemaKind::Invalid;
243 }
244
245 const TfToken schemaTypeToken(kindValue->GetString());
246 if (schemaTypeToken == _tokens->nonAppliedAPI) {
247 return UsdSchemaKind::NonAppliedAPI;
248 } else if (schemaTypeToken == _tokens->singleApplyAPI) {
249 return UsdSchemaKind::SingleApplyAPI;
250 } else if (schemaTypeToken == _tokens->multipleApplyAPI) {
251 return UsdSchemaKind::MultipleApplyAPI;
252 } else if (schemaTypeToken == _tokens->concreteTyped) {
253 return UsdSchemaKind::ConcreteTyped;
254 } else if (schemaTypeToken == _tokens->abstractTyped) {
255 return UsdSchemaKind::AbstractTyped;
256 } else if (schemaTypeToken == _tokens->abstractBase) {
257 return UsdSchemaKind::AbstractBase;
258 }
259
260 TF_CODING_ERROR("Invalid schema kind name '%s' found for plugin metadata "
261 "key '%s'.",
262 schemaTypeToken.GetText(), _tokens->schemaKind.GetText());
263 return UsdSchemaKind::Invalid;
264 }
265
266 static UsdSchemaKind
_GetSchemaKindFromPlugin(const TfType & schemaType)267 _GetSchemaKindFromPlugin(const TfType &schemaType)
268 {
269 PlugPluginPtr plugin =
270 PlugRegistry::GetInstance().GetPluginForType(schemaType);
271 if (!plugin) {
272 TF_CODING_ERROR("Failed to find plugin for schema type '%s'",
273 schemaType.GetTypeName().c_str());
274 return UsdSchemaKind::Invalid;
275 }
276
277 return _GetSchemaKindFromMetadata(plugin->GetMetadataForType(schemaType));
278 }
279
280 /*static*/
281 TfToken
GetSchemaTypeName(const TfType & schemaType)282 UsdSchemaRegistry::GetSchemaTypeName(const TfType &schemaType)
283 {
284 const _TypeMapCache & typeMapCache = _GetTypeMapCache();
285 auto it = typeMapCache.typeToName.find(schemaType);
286 return it != typeMapCache.typeToName.end() ? it->second.name : TfToken();
287 }
288
289 /*static*/
290 TfToken
GetConcreteSchemaTypeName(const TfType & schemaType)291 UsdSchemaRegistry::GetConcreteSchemaTypeName(const TfType &schemaType)
292 {
293 const _TypeMapCache & typeMapCache = _GetTypeMapCache();
294 auto it = typeMapCache.typeToName.find(schemaType);
295 if (it != typeMapCache.typeToName.end() &&
296 it->second.isTyped &&
297 _IsConcreteSchemaKind(_GetSchemaKindFromPlugin(schemaType))) {
298 return it->second.name;
299 }
300 return TfToken();
301 }
302
303 /*static*/
304 TfToken
GetAPISchemaTypeName(const TfType & schemaType)305 UsdSchemaRegistry::GetAPISchemaTypeName(const TfType &schemaType)
306 {
307 const _TypeMapCache & typeMapCache = _GetTypeMapCache();
308 auto it = typeMapCache.typeToName.find(schemaType);
309 return it != typeMapCache.typeToName.end() && !it->second.isTyped ?
310 it->second.name : TfToken();
311 }
312
313 /*static*/
314 TfType
GetTypeFromSchemaTypeName(const TfToken & typeName)315 UsdSchemaRegistry::GetTypeFromSchemaTypeName(const TfToken &typeName)
316 {
317 const _TypeMapCache & typeMapCache = _GetTypeMapCache();
318 auto it = typeMapCache.nameToType.find(typeName);
319 return it != typeMapCache.nameToType.end() ? it->second.type : TfType();
320 }
321
322 /*static*/
323 TfType
GetConcreteTypeFromSchemaTypeName(const TfToken & typeName)324 UsdSchemaRegistry::GetConcreteTypeFromSchemaTypeName(const TfToken &typeName)
325 {
326 const _TypeMapCache & typeMapCache = _GetTypeMapCache();
327 auto it = typeMapCache.nameToType.find(typeName);
328 if (it != typeMapCache.nameToType.end() &&
329 it->second.isTyped &&
330 _IsConcreteSchemaKind(_GetSchemaKindFromPlugin(it->second.type))) {
331 return it->second.type;
332 }
333 return TfType();
334 }
335
336 /*static*/
337 TfType
GetAPITypeFromSchemaTypeName(const TfToken & typeName)338 UsdSchemaRegistry::GetAPITypeFromSchemaTypeName(const TfToken &typeName)
339 {
340 const _TypeMapCache & typeMapCache = _GetTypeMapCache();
341 auto it = typeMapCache.nameToType.find(typeName);
342 return it != typeMapCache.nameToType.end() && !it->second.isTyped ?
343 it->second.type : TfType();
344 }
345
346 /*static*/
347 UsdSchemaKind
GetSchemaKind(const TfType & schemaType)348 UsdSchemaRegistry::GetSchemaKind(const TfType &schemaType)
349 {
350 const _TypeMapCache & typeMapCache = _GetTypeMapCache();
351 auto it = typeMapCache.typeToName.find(schemaType);
352 if (it == typeMapCache.typeToName.end()) {
353 // No schema kind because it is not a schema type.
354 return UsdSchemaKind::Invalid;
355 }
356 // Is a valid schema type.
357 return _GetSchemaKindFromPlugin(schemaType);
358 }
359
360 /*static*/
361 UsdSchemaKind
GetSchemaKind(const TfToken & typeName)362 UsdSchemaRegistry::GetSchemaKind(const TfToken &typeName)
363 {
364 const _TypeMapCache & typeMapCache = _GetTypeMapCache();
365 auto it = typeMapCache.nameToType.find(typeName);
366 if (it == typeMapCache.nameToType.end()) {
367 // No schema kind because it is not a schema type.
368 return UsdSchemaKind::Invalid;
369 }
370 // Is a valid schema type.
371 return _GetSchemaKindFromPlugin(it->second.type);
372 }
373
374 /*static*/
375 bool
IsConcrete(const TfType & primType)376 UsdSchemaRegistry::IsConcrete(const TfType& primType)
377 {
378 return _IsConcreteSchemaKind(GetSchemaKind(primType));
379 }
380
381 /*static*/
382 bool
IsConcrete(const TfToken & primType)383 UsdSchemaRegistry::IsConcrete(const TfToken& primType)
384 {
385 return _IsConcreteSchemaKind(GetSchemaKind(primType));
386 }
387
388 /*static*/
389 bool
IsMultipleApplyAPISchema(const TfType & apiSchemaType)390 UsdSchemaRegistry::IsMultipleApplyAPISchema(const TfType& apiSchemaType)
391 {
392 return _IsMultipleApplySchemaKind(GetSchemaKind(apiSchemaType));
393 }
394
395 /*static*/
396 bool
IsMultipleApplyAPISchema(const TfToken & apiSchemaType)397 UsdSchemaRegistry::IsMultipleApplyAPISchema(const TfToken& apiSchemaType)
398 {
399 return _IsMultipleApplySchemaKind(GetSchemaKind(apiSchemaType));
400 }
401
402 /*static*/
403 bool
IsAppliedAPISchema(const TfType & apiSchemaType)404 UsdSchemaRegistry::IsAppliedAPISchema(const TfType& apiSchemaType)
405 {
406 return _IsAppliedAPISchemaKind(GetSchemaKind(apiSchemaType));
407 }
408
409 /*static*/
410 bool
IsAppliedAPISchema(const TfToken & apiSchemaType)411 UsdSchemaRegistry::IsAppliedAPISchema(const TfToken& apiSchemaType)
412 {
413 return _IsAppliedAPISchemaKind(GetSchemaKind(apiSchemaType));
414 }
415
416 template <class T>
417 static void
_CopySpec(const T & srcSpec,const T & dstSpec)418 _CopySpec(const T &srcSpec, const T &dstSpec)
419 {
420 for (const TfToken& key : srcSpec->ListInfoKeys()) {
421 if (!UsdSchemaRegistry::IsDisallowedField(key)) {
422 dstSpec->SetInfo(key, srcSpec->GetInfo(key));
423 }
424 }
425 }
426
427 static void
_AddSchema(SdfLayerRefPtr const & source,SdfLayerRefPtr const & target)428 _AddSchema(SdfLayerRefPtr const &source, SdfLayerRefPtr const &target)
429 {
430 for (SdfPrimSpecHandle const &prim: source->GetRootPrims()) {
431 if (!target->GetPrimAtPath(prim->GetPath())) {
432
433 SdfPrimSpecHandle newPrim =
434 SdfPrimSpec::New(target, prim->GetName(), prim->GetSpecifier(),
435 prim->GetTypeName());
436 _CopySpec(prim, newPrim);
437
438 for (SdfAttributeSpecHandle const &attr: prim->GetAttributes()) {
439 SdfAttributeSpecHandle newAttr =
440 SdfAttributeSpec::New(
441 newPrim, attr->GetName(), attr->GetTypeName(),
442 attr->GetVariability(), attr->IsCustom());
443 _CopySpec(attr, newAttr);
444 }
445
446 for (SdfRelationshipSpecHandle const &rel:
447 prim->GetRelationships()) {
448 SdfRelationshipSpecHandle newRel =
449 SdfRelationshipSpec::New(
450 newPrim, rel->GetName(), rel->IsCustom());
451 _CopySpec(rel, newRel);
452 }
453 }
454 }
455 }
456
457 static SdfLayerRefPtr
_GetGeneratedSchema(const PlugPluginPtr & plugin)458 _GetGeneratedSchema(const PlugPluginPtr &plugin)
459 {
460 // Look for generatedSchema in Resources.
461 const string fname = TfStringCatPaths(plugin->GetResourcePath(),
462 "generatedSchema.usda");
463 SdfLayerRefPtr layer = SdfLayer::OpenAsAnonymous(fname);
464
465 TF_DEBUG(USD_SCHEMA_REGISTRATION).Msg(
466 "Looking up generated schema for plugin %s at path %s. "
467 "Generated schema %s.\n",
468 plugin->GetName().c_str(),
469 fname.c_str(),
470 (layer ? "valid" : "invalid")
471 );
472 return layer;
473 }
474
475 static TfTokenVector
_GetNameListFromMetadata(const JsObject & dict,const TfToken & key)476 _GetNameListFromMetadata(const JsObject &dict, const TfToken &key)
477 {
478 const JsValue *value = TfMapLookupPtr(dict, key);
479 if (!value) {
480 return TfTokenVector();
481 }
482
483 if (!value->IsArrayOf<std::string>()) {
484 TF_CODING_ERROR("Plugin metadata value for key '%s' does not hold a "
485 "string array", key.GetText());
486 return TfTokenVector();
487 }
488 return TfToTokenVector(value->GetArrayOf<std::string>());
489 }
490
491 /*static*/
492 void
CollectAddtionalAutoApplyAPISchemasFromPlugins(std::map<TfToken,TfTokenVector> * autoApplyAPISchemas)493 UsdSchemaRegistry::CollectAddtionalAutoApplyAPISchemasFromPlugins(
494 std::map<TfToken, TfTokenVector> *autoApplyAPISchemas)
495 {
496 TRACE_FUNCTION();
497
498 // Skip if auto apply API schemas have been disabled.
499 if (TfGetEnvSetting(USD_DISABLE_AUTO_APPLY_API_SCHEMAS)) {
500 return;
501 }
502
503 // Check all registered plugins for metadata that may supply additional
504 // auto apply API schem mappings.
505 const PlugPluginPtrVector& plugins =
506 PlugRegistry::GetInstance().GetAllPlugins();
507 for (const PlugPluginPtr &plug : plugins) {
508
509 // The metadata will contain a dictionary with entries of the form:
510 // "AutoApplyAPISchemas": {
511 // "<APISchemaName1>": {
512 // "apiSchemaAutoApplyTo": [
513 // "<TypedSchema1>", "<TypedSchema2>"
514 // ]
515 // },
516 // "<APISchemaName2>": {
517 // "apiSchemaAutoApplyTo": [
518 // "<TypedSchema1>", "<TypedSchema2>"
519 // ]
520 // }
521 // }
522 const JsObject &metadata = plug->GetMetadata();
523 const JsValue *autoApplyMetadataValue = TfMapLookupPtr(
524 metadata, _tokens->PluginAutoApplyAPISchemasKey);
525 if (!autoApplyMetadataValue) {
526 continue;
527 }
528
529 TF_DEBUG(USD_AUTO_APPLY_API_SCHEMAS).Msg(
530 "Collecting additional auto apply API schemas from "
531 "'AutoApplyAPISchemas' metadata in plugin '%s' at path '%s'.",
532 plug->GetName().c_str(), plug->GetPath().c_str());
533
534 const JsObject &autoApplyMetadata =
535 autoApplyMetadataValue->GetJsObject();
536 for(const auto &entry : autoApplyMetadata) {
537 if (!entry.second.IsObject()) {
538 continue;
539 }
540
541 TfToken apiSchemaName(entry.first);
542
543 // The metadata for the apiSchemaAutoApplyTo list is the same as
544 // for the auto apply built in to the schema type info.
545 TfTokenVector apiSchemaAutoApplyToNames =
546 _GetNameListFromMetadata(
547 entry.second.GetJsObject(), _tokens->apiSchemaAutoApplyTo);
548
549 if (!apiSchemaAutoApplyToNames.empty()) {
550
551 TF_DEBUG(USD_AUTO_APPLY_API_SCHEMAS).Msg(
552 "Plugin '%s' is adding automatic application of API schema "
553 "'%s' to the following schema types: [%s].\n",
554 plug->GetName().c_str(), apiSchemaName.GetText(),
555 TfStringJoin(apiSchemaAutoApplyToNames.begin(),
556 apiSchemaAutoApplyToNames.end(), ", ").c_str());
557
558 // The API schema may already have an entry in the map, in which
559 // case we have to append to the existing entry.
560 auto it = autoApplyAPISchemas->find(apiSchemaName);
561 if (it == autoApplyAPISchemas->end()) {
562 autoApplyAPISchemas->emplace(
563 apiSchemaName, std::move(apiSchemaAutoApplyToNames));
564 } else {
565 it->second.insert(it->second.end(),
566 apiSchemaAutoApplyToNames.begin(),
567 apiSchemaAutoApplyToNames.end());
568 }
569 }
570 }
571 }
572 }
573
574 static _TypeToTokenVecMap
_GetTypeToAutoAppliedAPISchemaNames()575 _GetTypeToAutoAppliedAPISchemaNames()
576 {
577 _TypeToTokenVecMap result;
578
579 const _TypeMapCache & typeMapCache = _GetTypeMapCache();
580
581 for (const auto &valuePair : UsdSchemaRegistry::GetAutoApplyAPISchemas()) {
582 const TfToken &apiSchemaName = valuePair.first;
583 const TfTokenVector &autoApplyToSchemas = valuePair.second;
584
585 // Collect all the types to apply the API schema to which includes any
586 // derived types of each of the listed types.
587 std::set<TfType> applyToTypes;
588 for (const TfToken &schemaName : autoApplyToSchemas) {
589 // The names listed are the USD type names (not the full TfType
590 // name) for abstract and concrete Typed schemas, so we need to get
591 // the actual TfType of the schema and its derived types.
592 const auto it = typeMapCache.nameToType.find(schemaName);
593 if (it != typeMapCache.nameToType.end()) {
594 const TfType &schemaType = it->second.type;
595 if (applyToTypes.insert(schemaType).second) {
596 schemaType.GetAllDerivedTypes(&applyToTypes);
597 }
598 }
599 }
600
601 // With all the apply to types collected we can add the API schema to
602 // the list of applied schemas for each Typed schema type.
603 //
604 // Note that the auto apply API schemas map is sorted alphabetically by
605 // API schema name so this list for each prim type will also be sorted
606 // alphabetically which is intentional. This ordering is arbitrary but
607 // necessary to ensure we get a consistent strength ordering for auto
608 // applied schemas every time. In practice, schema writers should be
609 // careful to make sure that auto applied API schemas have unique
610 // property names so that application order doesn't matter, but this at
611 // least gives us consistent behavior if property name collisions occur.
612 for (const TfType &applyToType : applyToTypes) {
613 result[applyToType].push_back(apiSchemaName);
614 }
615 }
616
617 return result;
618 }
619
620 // Helper class for initializing the schema registry by finding all generated
621 // schema types in plugin libraries and creating the static prim definitions
622 // for all concrete and applied API schema types.
623 class UsdSchemaRegistry::_SchemaDefInitHelper
624 {
625 public:
_SchemaDefInitHelper(UsdSchemaRegistry * registry)626 _SchemaDefInitHelper(UsdSchemaRegistry *registry)
627 : _registry(registry)
628 {};
629
FindAndBuildAllSchemaDefinitions()630 void FindAndBuildAllSchemaDefinitions()
631 {
632 // Find and load all the generated schema in plugin libraries. We find
633 // these files adjacent to pluginfo files in libraries that provide
634 // subclasses of UsdSchemaBase.
635 _InitializePrimDefsAndSchematicsForPluginSchemas();
636
637 // Populate multiple apply API schema definitions first. These can't
638 // include other API schemas so they're populated directly from the
639 // their prim spec in the schematics.
640 _PopulateMultipleApplyAPIPrimDefinitions();
641
642 // Populate single apply API schema definitions second. These can
643 // include other API schemas including instances of multiple apply
644 // API schemas.
645 _PopulateSingleApplyAPIPrimDefinitions();
646
647 // Populate all concrete API schema definitions after all API schemas
648 // they may depend on have been populated.
649 _PopulateConcretePrimDefinitions();
650 }
651
652 private:
653 // Single apply API schemas require some processing to determine all
654 // the included API schemas and all the prim specs that need to be composed
655 // into its prim definition. This structure is used to hold this info.
656 struct _SchemaDefCompositionInfo {
657
658 // The prim definition to compose.
659 UsdPrimDefinition *primDef;
660
661 // The ordered list of prim specs from the schematics to compose into
662 // the prim definition. The list is actually a vector of pairs, where
663 // the pair is the prim spec and a possible instance name (for included
664 // instances of multiple apply API schemas).
665 using _SchemaPrimSpecToCompose =
666 std::pair<const SdfPrimSpecHandle, TfToken>;
667 using _SchemaPrimSpecsToCompose = std::vector<_SchemaPrimSpecToCompose>;
668 _SchemaPrimSpecsToCompose schemaPrimSpecsToCompose;
669
670 // The expanded list of names of all API schemas that will be present
671 // in the prim definition.
672 TfTokenVector allAPISchemaNames;
673
_SchemaDefCompositionInfoUsdSchemaRegistry::_SchemaDefInitHelper::_SchemaDefCompositionInfo674 _SchemaDefCompositionInfo(
675 UsdPrimDefinition *primDef_,
676 const TfToken &apiSchemaName,
677 const SdfPrimSpecHandle &apiSchemaPrimSpec)
678 : primDef(primDef_)
679 {
680 // We'll always compose the schema type's own prim spec.
681 schemaPrimSpecsToCompose.emplace_back(apiSchemaPrimSpec, TfToken());
682 // The schema's own name will always be the first entry in its own
683 // list of applied API schemas.
684 allAPISchemaNames.push_back(apiSchemaName);
685 }
686 };
687
688 void _InitializePrimDefsAndSchematicsForPluginSchemas();
689
690 bool _CollectMultipleApplyAPISchemaNamespaces(
691 const VtDictionary &customDataDict);
692
693 void _PrependAPISchemasFromSchemaPrim(
694 const SdfPath &schematicsPrimPath,
695 TfTokenVector *appliedAPISchemas);
696
697 void _GatherAllAPISchemaPrimSpecsToCompose(
698 _SchemaDefCompositionInfo *apiDefInfo,
699 const TfTokenVector &appliedAPISchemas) const;
700
701 void _PopulateMultipleApplyAPIPrimDefinitions();
702 void _PopulateSingleApplyAPIPrimDefinitions();
703 void _PopulateConcretePrimDefinitions();
704
705 UsdSchemaRegistry *_registry;
706 };
707
708 bool
709 UsdSchemaRegistry::_SchemaDefInitHelper::
_CollectMultipleApplyAPISchemaNamespaces(const VtDictionary & customDataDict)710 _CollectMultipleApplyAPISchemaNamespaces(
711 const VtDictionary &customDataDict)
712 {
713 // Names of multiple apply API schemas are stored in their schemas
714 // in a dictionary mapping them to their property namespace prefixes.
715 // These will be useful in mapping schema instance property names
716 // to the schema property specs.
717
718 auto it = customDataDict.find(_tokens->multipleApplyAPISchemas);
719 if (it == customDataDict.end()) {
720 return true;
721 }
722
723 if (!it->second.IsHolding<VtDictionary>()) {
724 TF_CODING_ERROR("Found an unexpected value type for layer "
725 "customData key '%s'; expected a dictionary. Multiple apply API "
726 "schemas may be incorrect.",
727 _tokens->multipleApplyAPISchemas.GetText());
728 return false;
729 }
730
731 bool success = true;
732 const VtDictionary &multipleApplyAPISchemas =
733 it->second.UncheckedGet<VtDictionary>();
734 for (const auto &it : multipleApplyAPISchemas) {
735 const TfToken apiSchemaName(it.first);
736
737 if (!it.second.IsHolding<std::string>()) {
738 TF_CODING_ERROR("Found an unexpected value type for key '%s' in "
739 "the dictionary for layer customData field '%s'; expected a "
740 "string. Multiple apply API schema of type '%s' will not be "
741 "correctly registered.",
742 apiSchemaName.GetText(),
743 _tokens->multipleApplyAPISchemas.GetText(),
744 apiSchemaName.GetText());
745 success = false;
746 continue;
747 }
748
749 // The property namespace is stored along side where the prim definition
750 // defining the schema's properties will be.
751 _registry->_multiApplyAPIPrimDefinitions[apiSchemaName]
752 .propertyNamespace = TfToken(it.second.UncheckedGet<std::string>());
753 }
754 return success;
755 }
756
757 void
758 UsdSchemaRegistry::_SchemaDefInitHelper::
_InitializePrimDefsAndSchematicsForPluginSchemas()759 _InitializePrimDefsAndSchematicsForPluginSchemas()
760 {
761 // Get all types that derive from UsdSchemaBase by getting the type map
762 // cache.
763 const _TypeMapCache &typeCache = _GetTypeMapCache();
764
765 // Gather the mapping of TfTypes to the schemas that are auto applied to
766 // those types. We need this before initializing our prim definitions. Note
767 // the prim definitions will take the API schema lists from this map in the
768 // following loop.
769 _TypeToTokenVecMap typeToAutoAppliedAPISchemaNames =
770 _GetTypeToAutoAppliedAPISchemaNames();
771
772 // Get all the plugins that provide the types and initialize prim defintions
773 // for the found schema types.
774 std::vector<PlugPluginPtr> plugins;
775 for (const auto &valuePair : typeCache.typeToName) {
776 const TfType &type = valuePair.first;
777 const TfToken &typeName = valuePair.second.name;
778
779 if (PlugPluginPtr plugin =
780 PlugRegistry::GetInstance().GetPluginForType(type)) {
781
782 auto insertIt =
783 std::lower_bound(plugins.begin(), plugins.end(), plugin);
784 if (insertIt == plugins.end() || *insertIt != plugin) {
785 plugins.insert(insertIt, plugin);
786 }
787 }
788
789 // We register prim definitions by the schema type name which we already
790 // grabbed from the TfType alias, and is also the name of the defining
791 // prim in the schema layer. The actual TfType's typename
792 // (i.e. C++ type name) is not a valid typename for a prim.
793 SdfPath schematicsPrimPath =
794 SdfPath::AbsoluteRootPath().AppendChild(typeName);
795
796 // Create a new UsdPrimDefinition for this type in the appropriate map
797 // based on the type's schema kind. The prim definitions are not fully
798 // populated by the schematics here; we'll populate all these prim
799 // definitions in the rest FindAndBuildAllSchemaDefinitions.
800 const UsdSchemaKind schemaKind = _GetSchemaKindFromPlugin(type);
801 if (_IsConcreteSchemaKind(schemaKind)) {
802 UsdPrimDefinition *newPrimDef = new UsdPrimDefinition(
803 schematicsPrimPath, /* isAPISchema = */ false);
804 _registry->_concreteTypedPrimDefinitions.emplace(
805 typeName, newPrimDef);
806 // Check if there are any API schemas that have been setup to apply
807 // to this type. We'll set these in the prim definition's applied
808 // API schemas list so they can be processed when building out this
809 // prim definition in _PopulateConcretePrimDefinitions.
810 if (TfTokenVector *autoAppliedAPIs =
811 TfMapLookupPtr(typeToAutoAppliedAPISchemaNames, type)) {
812 TF_DEBUG(USD_AUTO_APPLY_API_SCHEMAS).Msg(
813 "The prim definition for schema type '%s' has "
814 "these additional built-in auto applied API "
815 "schemas: [%s].\n",
816 typeName.GetText(),
817 TfStringJoin(autoAppliedAPIs->begin(),
818 autoAppliedAPIs->end(), ", ").c_str());
819
820 // Note that we take the API schema list from the map as the
821 // map was only created help populate these prim definitions.
822 newPrimDef->_appliedAPISchemas = std::move(*autoAppliedAPIs);
823 }
824 } else if (_IsAppliedAPISchemaKind(schemaKind)) {
825 UsdPrimDefinition *newPrimDef = new UsdPrimDefinition(
826 schematicsPrimPath, /* isAPISchema = */ true);
827 if (_IsMultipleApplySchemaKind(schemaKind)) {
828 // The multiple apply schema map stores both the prim definition
829 // and the property prefix for the schema, but we won't know
830 // the prefix until we read the generated schemas.
831 _registry->_multiApplyAPIPrimDefinitions[typeName].primDef =
832 newPrimDef;
833 } else {
834 _registry->_singleApplyAPIPrimDefinitions.emplace(
835 typeName, newPrimDef);
836
837 // Check if there are any API schemas that have been setup to
838 // apply to this API schema type. We'll set these in the prim
839 // definition's applied API schemas list so they can be
840 // processed when building out this prim definition in
841 // _PopulateSingleApplyAPIPrimDefinitions.
842 if (TfTokenVector *autoAppliedAPIs =
843 TfMapLookupPtr(typeToAutoAppliedAPISchemaNames, type)) {
844 TF_DEBUG(USD_AUTO_APPLY_API_SCHEMAS).Msg(
845 "The prim definition for API schema type '%s' has "
846 "these additional built-in auto applied API "
847 "schemas: [%s].\n",
848 typeName.GetText(),
849 TfStringJoin(autoAppliedAPIs->begin(),
850 autoAppliedAPIs->end(), ", ").c_str());
851
852 // Note that we take the API schema list from the map as the
853 // map was only created help populate these prim definitions.
854 newPrimDef->_appliedAPISchemas = std::move(*autoAppliedAPIs);
855 }
856 }
857 }
858 }
859
860 // For each plugin, if it has generated schema, add it to the schematics.
861 std::vector<SdfLayerRefPtr> generatedSchemas(plugins.size());
862 WorkWithScopedParallelism([&plugins, &generatedSchemas]() {
863 WorkParallelForN(
864 plugins.size(),
865 [&plugins, &generatedSchemas](size_t begin, size_t end) {
866 for (; begin != end; ++begin) {
867 generatedSchemas[begin] =
868 _GetGeneratedSchema(plugins[begin]);
869 }
870 });
871 });
872
873 SdfChangeBlock block;
874
875 for (const SdfLayerRefPtr& generatedSchema : generatedSchemas) {
876 if (generatedSchema) {
877 VtDictionary customDataDict = generatedSchema->GetCustomLayerData();
878
879 bool hasErrors = false;
880
881 // This collects all the multiple apply API schema namespaces
882 // prefixes that are defined in the generated schema and stores
883 // them in the prim definition map entry that will contain the
884 // prim definition for the corresponding multiple apply API schema
885 // name.
886 if (!_CollectMultipleApplyAPISchemaNamespaces(customDataDict)) {
887 hasErrors = true;
888 }
889
890 _AddSchema(generatedSchema, _registry->_schematics);
891
892 // Schema generation will have added any defined fallback prim
893 // types as a dictionary in layer metadata which will be composed
894 // into the single fallback types dictionary.
895 VtDictionary generatedFallbackPrimTypes;
896 if (generatedSchema->HasField(SdfPath::AbsoluteRootPath(),
897 UsdTokens->fallbackPrimTypes,
898 &generatedFallbackPrimTypes)) {
899 for (const auto &it: generatedFallbackPrimTypes) {
900 if (it.second.IsHolding<VtTokenArray>()) {
901 _registry->_fallbackPrimTypes.insert(it);
902 } else {
903 TF_CODING_ERROR("Found a VtTokenArray value for type "
904 "name key '%s' in fallbackPrimTypes layer metadata "
905 "dictionary in generated schema file '%s'.",
906 it.first.c_str(),
907 generatedSchema->GetRealPath().c_str());
908 }
909 }
910 }
911
912 if (hasErrors) {
913 TF_CODING_ERROR(
914 "Encountered errors in layer metadata from generated "
915 "schema file '%s'. This schema must be regenerated.",
916 generatedSchema->GetRealPath().c_str());
917 }
918 }
919 }
920 }
921
922 // Helper that gets the authored API schemas from the schema prim path in the
923 // schematics layer and prepends them to the given applied API schemas list.
924 void
_PrependAPISchemasFromSchemaPrim(const SdfPath & schematicsPrimPath,TfTokenVector * appliedAPISchemas)925 UsdSchemaRegistry::_SchemaDefInitHelper::_PrependAPISchemasFromSchemaPrim(
926 const SdfPath &schematicsPrimPath,
927 TfTokenVector *appliedAPISchemas)
928 {
929 // Get the API schemas from the list op field in the schematics.
930 SdfTokenListOp apiSchemasListOp;
931 if (!_registry->_schematics->HasField(
932 schematicsPrimPath, UsdTokens->apiSchemas, &apiSchemasListOp)) {
933 return;
934 }
935 TfTokenVector apiSchemas;
936 apiSchemasListOp.ApplyOperations(&apiSchemas);
937 if (apiSchemas.empty()) {
938 return;
939 }
940
941 // If the existing list has API schemas, append them to list we just pulled
942 // from the schematics before replacing the existing list with the new list.
943 if (!appliedAPISchemas->empty()) {
944 apiSchemas.insert(
945 apiSchemas.end(),
946 appliedAPISchemas->begin(),
947 appliedAPISchemas->end());
948 }
949 *appliedAPISchemas = std::move(apiSchemas);
950 }
951
952 // For the single API schema prim definition in depCompInfo, that is in the
953 // process of being built, this takes the list of appliedAPISchemas and
954 // recursively gathers all the API schemas that the prim definition needs to
955 // include as well as the prim specs that will provide the properties. This
956 // is all stored back in the depCompInfo in preparation for the final population
957 // of the prim definition. This step is expected to be run after all prim
958 // definitions representing API schemas have been established with their
959 // directly included API schemas. but before any of the API schema prim
960 // definitions have been updated with their fully expanded API schemas list.
961 void
_GatherAllAPISchemaPrimSpecsToCompose(_SchemaDefCompositionInfo * defCompInfo,const TfTokenVector & appliedAPISchemas) const962 UsdSchemaRegistry::_SchemaDefInitHelper::_GatherAllAPISchemaPrimSpecsToCompose(
963 _SchemaDefCompositionInfo *defCompInfo,
964 const TfTokenVector &appliedAPISchemas) const
965 {
966 for (const TfToken &apiSchemaName : appliedAPISchemas) {
967 // This mainly to avoid API schema inclusion cycles but also has the
968 // added benefit of avoiding duplicates if included API schemas
969 // themselves include the same other API schema.
970 //
971 // Note that we linear search the vector of API schema names. This
972 // vector is expected to be small and shouldn't be a performance
973 // concern. But it's something to be aware of in case it does cause
974 // performance problems in the future, especially since by far the most
975 // common case will be that we don't find the name in the list.
976 if (std::find(defCompInfo->allAPISchemaNames.begin(),
977 defCompInfo->allAPISchemaNames.end(),
978 apiSchemaName)
979 != defCompInfo->allAPISchemaNames.end()) {
980 continue;
981 }
982
983 // Find the registered prim definition (and property prefix if it's a
984 // multiple apply instance). Skip this API if we can't find a def.
985 std::string propPrefix;
986 const UsdPrimDefinition *apiSchemaTypeDef =
987 _registry->_FindAPIPrimDefinitionByFullName(
988 apiSchemaName, &propPrefix);
989 if (!apiSchemaTypeDef) {
990 continue;
991 }
992
993 // The found API schema def may not be fully populated at this point,
994 // but it will always have its prim spec path stored. Get the prim spec
995 // for this API def.
996 SdfPrimSpecHandle primSpec =
997 _registry->_schematics->GetPrimAtPath(
998 apiSchemaTypeDef->_schematicsPrimPath);
999 if (!primSpec) {
1000 continue;
1001 }
1002
1003 // Add this API schema and its prim spec to the composition
1004 defCompInfo->schemaPrimSpecsToCompose.emplace_back(
1005 primSpec, propPrefix);
1006 defCompInfo->allAPISchemaNames.push_back(apiSchemaName);
1007
1008 // At this point in initialization, all API schemas prim defs will
1009 // have their directly included API schemas set in the definition, but
1010 // will not have had them expanded to include APIs included from other
1011 // APIs. Thus, we can do a depth first recursion on the current applied
1012 // API schemas of the API prim definition to continue expanding the
1013 // full list of API schemas and prim specs to compose in strength order.
1014 _GatherAllAPISchemaPrimSpecsToCompose(
1015 defCompInfo,
1016 apiSchemaTypeDef->GetAppliedAPISchemas());
1017 }
1018 }
1019
1020 void
1021 UsdSchemaRegistry::_SchemaDefInitHelper::
_PopulateMultipleApplyAPIPrimDefinitions()1022 _PopulateMultipleApplyAPIPrimDefinitions()
1023 {
1024 // Populate all multiple apply API schema definitions. These can't
1025 // include other API schemas so they're populated directly from the
1026 // their prim spec in the schematics.
1027 for (auto &nameAndDefPtr : _registry->_multiApplyAPIPrimDefinitions) {
1028 UsdPrimDefinition *&primDef = nameAndDefPtr.second.primDef;
1029 if (!TF_VERIFY(primDef)) {
1030 continue;
1031 }
1032
1033 SdfPrimSpecHandle primSpec =
1034 _registry->_schematics->GetPrimAtPath(primDef->_schematicsPrimPath);
1035 if (!primSpec) {
1036 // XXX: This, and the warnings below, should probably be coding
1037 // errors. However a coding error here causes usdGenSchema to
1038 // fail when there are plugin schema types that are missing a
1039 // generated schema prim spec. Since running usdGenSchema is how
1040 // you'd fix this error, that's not helpful.
1041 TF_WARN("Could not find a prim spec at path '%s' in the "
1042 "schematics layer for registered multiple apply "
1043 "API schema '%s'. Schemas need to be regenerated.",
1044 primDef->_schematicsPrimPath.GetText(),
1045 nameAndDefPtr.first.GetText());
1046 continue;
1047 }
1048
1049 // Compose the properties from the prim spec to the prim definition.
1050 primDef->_ComposePropertiesFromPrimSpec(primSpec);
1051 }
1052 }
1053
1054 void
1055 UsdSchemaRegistry::_SchemaDefInitHelper::
_PopulateSingleApplyAPIPrimDefinitions()1056 _PopulateSingleApplyAPIPrimDefinitions()
1057 {
1058 // Single apply schemas are more complicated. These may contain other
1059 // applied API schemas which may also include other API schemas. To populate
1060 // their properties correctly, we must do this in multiple passes.
1061 std::vector<_SchemaDefCompositionInfo> apiSchemasWithAppliedSchemas;
1062
1063 // Step 1. For each single apply API schema, we determine what (if any)
1064 // applied API schemas it has. If it has none, we can just populate its
1065 // prim definition from the schematics prim spec and be done. Otherwise we
1066 // need to store the directly included built-in API schemas now and process
1067 // them in the next pass once we know ALL the API schemas that every single
1068 // apply API includes.
1069 for (auto &nameAndDefPtr : _registry->_singleApplyAPIPrimDefinitions) {
1070 const TfToken &usdTypeNameToken = nameAndDefPtr.first;
1071 UsdPrimDefinition *&primDef = nameAndDefPtr.second;
1072 if (!TF_VERIFY(primDef)) {
1073 continue;
1074 }
1075
1076 SdfPrimSpecHandle primSpec =
1077 _registry->_schematics->GetPrimAtPath(primDef->_schematicsPrimPath);
1078 if (!primSpec) {
1079 TF_WARN("Could not find a prim spec at path '%s' in the "
1080 "schematics layer for registered single apply "
1081 "API schema '%s'. Schemas need to be regenerated.",
1082 primDef->_schematicsPrimPath.GetText(),
1083 nameAndDefPtr.first.GetText());
1084 continue;
1085 }
1086
1087 // During initialization, any auto applied API schema names relevant
1088 // to this API schema type were put in prim definition's applied API
1089 // schema list. There may be applied API schemas defined in the metadata
1090 // for the type in the schematics. If so, these must be prepended to the
1091 // collected auto applied schema names (auto apply API schemas are
1092 // weaker) to get full list of API schemas that need to be composed into
1093 // this prim definition.
1094 _PrependAPISchemasFromSchemaPrim(
1095 primDef->_schematicsPrimPath,
1096 &primDef->_appliedAPISchemas);
1097
1098 if (primDef->_appliedAPISchemas.empty()) {
1099 // If there are no API schemas to apply to this schema, we can just
1100 // apply the prim specs properties and be done.
1101 primDef->_ComposePropertiesFromPrimSpec(primSpec);
1102 // We always include the API schema itself as an applied API schema
1103 // in its prim definition. Note that we don't do this for multiple
1104 // apply schema definitions which cannot be applied without an
1105 // instance name.
1106 primDef->_appliedAPISchemas = {usdTypeNameToken};
1107 } else {
1108 apiSchemasWithAppliedSchemas.emplace_back(
1109 primDef, usdTypeNameToken, primSpec);
1110 }
1111 }
1112
1113 // Step 2. For each single apply API that has other applied API schemas,
1114 // recursively gather the fully expanded list of API schemas and their
1115 // corresponding prim specs that will be used to populate the prim
1116 // definition's properties.
1117 //
1118 // We specifically do this step here because each API schema prim
1119 // definition will have only its direct built-in API schemas in its list
1120 // allowing us to recurse without cycling. Only once we've gathered what
1121 // will be the fully expanded list of API schemas for all of them can we
1122 // start to populate the API schemas with all their properties.
1123 for (_SchemaDefCompositionInfo &defCompInfo : apiSchemasWithAppliedSchemas) {
1124 _GatherAllAPISchemaPrimSpecsToCompose(
1125 &defCompInfo,
1126 defCompInfo.primDef->_appliedAPISchemas);
1127 }
1128
1129 // Step 3. For each single apply API schema from step 2, we can update the
1130 // prim definition by applying the properties from all the gathered prim
1131 // specs and setting the fully expanded list of API schemas (which will
1132 // include itself).
1133 for (_SchemaDefCompositionInfo &defCompInfo : apiSchemasWithAppliedSchemas) {
1134 for (const auto &primSpecAndPropPrefix :
1135 defCompInfo.schemaPrimSpecsToCompose) {
1136 defCompInfo.primDef->_ComposePropertiesFromPrimSpec(
1137 primSpecAndPropPrefix.first,
1138 primSpecAndPropPrefix.second);
1139 }
1140 defCompInfo.primDef->_appliedAPISchemas =
1141 std::move(defCompInfo.allAPISchemaNames);
1142 }
1143 }
1144
1145 void
1146 UsdSchemaRegistry::_SchemaDefInitHelper::
_PopulateConcretePrimDefinitions()1147 _PopulateConcretePrimDefinitions()
1148 {
1149 // Populate all concrete API schema definitions; it is expected that all
1150 // API schemas, which these may depend on, have already been populated.
1151 for (auto &nameAndDefPtr : _registry->_concreteTypedPrimDefinitions) {
1152 UsdPrimDefinition *&primDef = nameAndDefPtr.second;
1153 if (!TF_VERIFY(primDef)) {
1154 continue;
1155 }
1156
1157 SdfPrimSpecHandle primSpec =
1158 _registry->_schematics->GetPrimAtPath(primDef->_schematicsPrimPath);
1159 if (!primSpec) {
1160 TF_WARN("Could not find a prim spec at path '%s' in the "
1161 "schematics layer for registered concrete typed "
1162 "schema '%s'. Schemas need to be regenerated.",
1163 primDef->_schematicsPrimPath.GetText(),
1164 nameAndDefPtr.first.GetText());
1165 continue;
1166 }
1167
1168 // During initialization, any auto applied API schema names relevant
1169 // to this prim type were put in prim definition's applied API schema
1170 // list. There may be applied API schemas defined in the metadata for
1171 // the type in the schematics. If so, these must be prepended to the
1172 // collected auto applied schema names (auto apply API schemas are
1173 // weaker) to get full list of API schemas that need to be composed into
1174 // this prim definition.
1175 _PrependAPISchemasFromSchemaPrim(
1176 primDef->_schematicsPrimPath,
1177 &primDef->_appliedAPISchemas);
1178
1179 // Compose the properties from the prim spec to the prim definition
1180 // first as these are stronger than the built-in API schema properties.
1181 primDef->_ComposePropertiesFromPrimSpec(primSpec);
1182
1183 // If there are any applied API schemas in the list, compose them
1184 // in now
1185 if (!primDef->_appliedAPISchemas.empty()) {
1186 // We've stored the names of all the API schemas we're going to
1187 // compose in the primDef's _appliedAPISchemas even though we
1188 // haven't composed in any of their properties yet. In addition to
1189 // composing in properties, _ComposeAPISchemasIntoPrimDefinition
1190 // will also append the API schemas it receives to the primDef's
1191 // _appliedAPISchemas. Thus, we copy _appliedAPISchemas to a
1192 // temporary and clear it first so that we don't end up with the
1193 // entire contents of list appended to itself again.
1194 TfTokenVector apiSchemasToCompose =
1195 std::move(primDef->_appliedAPISchemas);
1196 primDef->_appliedAPISchemas.clear();
1197 _registry->_ComposeAPISchemasIntoPrimDefinition(
1198 primDef, apiSchemasToCompose);
1199 }
1200 }
1201 }
1202
UsdSchemaRegistry()1203 UsdSchemaRegistry::UsdSchemaRegistry()
1204 {
1205 _schematics = SdfLayer::CreateAnonymous("registry.usda");
1206 _emptyPrimDefinition = new UsdPrimDefinition();
1207
1208 // Find and load all the generated schema in plugin libraries and build all
1209 // the schema prim definitions.
1210 if (!TfGetEnvSetting(USD_DISABLE_PRIM_DEFINITIONS_FOR_USDGENSCHEMA)) {
1211 _SchemaDefInitHelper schemaDefHelper(this);
1212 schemaDefHelper.FindAndBuildAllSchemaDefinitions();
1213 }
1214
1215 TfSingleton<UsdSchemaRegistry>::SetInstanceConstructed(*this);
1216 TfRegistryManager::GetInstance().SubscribeTo<UsdSchemaRegistry>();
1217 }
1218
1219 /*static*/
1220 bool
IsDisallowedField(const TfToken & fieldName)1221 UsdSchemaRegistry::IsDisallowedField(const TfToken &fieldName)
1222 {
1223 static TfHashSet<TfToken, TfToken::HashFunctor> disallowedFields;
1224
1225 // XXX -- Use this instead of an initializer list in case TfHashSet
1226 // doesn't support initializer lists. Should ensure that
1227 // TfHashSet does support them.
1228 static std::once_flag once;
1229 std::call_once(once, [](){
1230 // Disallow fallback values for composition arc fields, since they
1231 // won't be used during composition.
1232 disallowedFields.insert(SdfFieldKeys->InheritPaths);
1233 disallowedFields.insert(SdfFieldKeys->Payload);
1234 disallowedFields.insert(SdfFieldKeys->References);
1235 disallowedFields.insert(SdfFieldKeys->Specializes);
1236 disallowedFields.insert(SdfFieldKeys->VariantSelection);
1237 disallowedFields.insert(SdfFieldKeys->VariantSetNames);
1238
1239 // Disallow customData, since it contains information used by
1240 // usdGenSchema that isn't relevant to other consumers.
1241 disallowedFields.insert(SdfFieldKeys->CustomData);
1242
1243 // Disallow fallback values for these fields, since they won't be
1244 // used during scenegraph population or value resolution.
1245 disallowedFields.insert(SdfFieldKeys->Active);
1246 disallowedFields.insert(SdfFieldKeys->Instanceable);
1247 disallowedFields.insert(SdfFieldKeys->TimeSamples);
1248 disallowedFields.insert(SdfFieldKeys->ConnectionPaths);
1249 disallowedFields.insert(SdfFieldKeys->TargetPaths);
1250
1251 // Disallow fallback values for specifier. Even though it will always
1252 // be present, it has no meaning as a fallback value.
1253 disallowedFields.insert(SdfFieldKeys->Specifier);
1254
1255 // Disallow fallback values for children fields.
1256 disallowedFields.insert(SdfChildrenKeys->allTokens.begin(),
1257 SdfChildrenKeys->allTokens.end());
1258
1259 // Disallow fallback values for clip-related fields, since they won't
1260 // be used during value resolution.
1261 const std::vector<TfToken> clipFields = UsdGetClipRelatedFields();
1262 disallowedFields.insert(clipFields.begin(), clipFields.end());
1263 });
1264
1265 return (disallowedFields.find(fieldName) != disallowedFields.end());
1266 }
1267
1268 /*static*/
1269 bool
IsTyped(const TfType & primType)1270 UsdSchemaRegistry::IsTyped(const TfType& primType)
1271 {
1272 return primType.IsA<UsdTyped>();
1273 }
1274
1275 TfType
GetTypeFromName(const TfToken & typeName)1276 UsdSchemaRegistry::GetTypeFromName(const TfToken& typeName){
1277 static const TfType schemaBaseType = TfType::Find<UsdSchemaBase>();
1278 return PlugRegistry::GetInstance().FindDerivedTypeByName(
1279 schemaBaseType, typeName.GetString());
1280 }
1281
1282 std::pair<TfToken, TfToken>
GetTypeNameAndInstance(const TfToken & apiSchemaName)1283 UsdSchemaRegistry::GetTypeNameAndInstance(const TfToken &apiSchemaName)
1284 {
1285 // Try to split the string at the first namespace delimiter. We always use
1286 // the first as type names can not have embedded namespaces but instances
1287 // names can.
1288 const char namespaceDelimiter =
1289 SdfPathTokens->namespaceDelimiter.GetText()[0];
1290 const std::string &typeString = apiSchemaName.GetString();
1291 size_t delim = typeString.find(namespaceDelimiter);
1292 // If the delimiter is not found, we have a single apply API schema and
1293 // no instance name.
1294 if (delim == std::string::npos) {
1295 return std::make_pair(apiSchemaName, TfToken());
1296 } else {
1297 return std::make_pair(TfToken(typeString.substr(0, delim)),
1298 TfToken(typeString.c_str() + delim + 1));
1299 }
1300 }
1301
1302 /*static*/
1303 const std::map<TfToken, TfTokenVector> &
GetAutoApplyAPISchemas()1304 UsdSchemaRegistry::GetAutoApplyAPISchemas()
1305 {
1306 return _GetAPISchemaApplyToInfoCache().autoApplyAPISchemasMap;
1307 }
1308
1309 /*static*/
1310 bool
IsAllowedAPISchemaInstanceName(const TfToken & apiSchemaName,const TfToken & instanceName)1311 UsdSchemaRegistry::IsAllowedAPISchemaInstanceName(
1312 const TfToken &apiSchemaName, const TfToken &instanceName)
1313 {
1314 // Verify we have a multiple apply API schema and a non-empty instance name.
1315 if (instanceName.IsEmpty() || !IsMultipleApplyAPISchema(apiSchemaName)) {
1316 return false;
1317 }
1318
1319 // A multiple apply schema may specify a list of instance names that
1320 // are allowed for it. If so we check for that here. If no list of
1321 // instance names exist or it is empty, then any valid instance name is
1322 // allowed.
1323 const TfHashMap<TfToken, TfToken::Set, TfHash> &allowedInstanceNamesMap =
1324 _GetAPISchemaApplyToInfoCache().allowedInstanceNamesMap;
1325 if (const TfToken::Set *allowedInstanceNames =
1326 TfMapLookupPtr(allowedInstanceNamesMap, apiSchemaName)) {
1327 if (!allowedInstanceNames->empty() &&
1328 !allowedInstanceNames->count(instanceName)) {
1329 return false;
1330 }
1331 }
1332
1333 // In all cases, we don't allow instance names whose base name matches the
1334 // name of a property of the API schema. We check the prim definition for
1335 // this.
1336 const UsdPrimDefinition *apiSchemaDef =
1337 GetInstance().FindAppliedAPIPrimDefinition(apiSchemaName);
1338 if (!apiSchemaDef) {
1339 TF_CODING_ERROR("Could not find UsdPrimDefinition for multiple apply "
1340 "API schema '%s'", apiSchemaName.GetText());
1341 return false;
1342 }
1343
1344 const TfTokenVector tokens =
1345 SdfPath::TokenizeIdentifierAsTokens(instanceName);
1346 if (tokens.empty()) {
1347 return false;
1348 }
1349
1350 const TfToken &baseName = tokens.back();
1351 if (apiSchemaDef->_propPathMap.count(baseName)) {
1352 return false;
1353 }
1354
1355 return true;
1356 }
1357
1358 const TfTokenVector &
GetAPISchemaCanOnlyApplyToTypeNames(const TfToken & apiSchemaName,const TfToken & instanceName)1359 UsdSchemaRegistry::GetAPISchemaCanOnlyApplyToTypeNames(
1360 const TfToken &apiSchemaName, const TfToken &instanceName)
1361 {
1362 const TfHashMap<TfToken, TfTokenVector, TfHash> &canOnlyApplyToMap =
1363 _GetAPISchemaApplyToInfoCache().canOnlyApplyAPISchemasMap;
1364
1365 if (!instanceName.IsEmpty()) {
1366 // It's possible that specific instance names of the schema can only be
1367 // applied to the certain types. If a list of "can only apply to" types
1368 // is exists for the given instance, we use it.
1369 TfToken fullApiSchemaName(
1370 SdfPath::JoinIdentifier(apiSchemaName, instanceName));
1371 if (const TfTokenVector *canOnlyApplyToTypeNames =
1372 TfMapLookupPtr(canOnlyApplyToMap, fullApiSchemaName)) {
1373 return *canOnlyApplyToTypeNames;
1374 }
1375 }
1376
1377 // Otherwise, no there's no instance specific list, so try to find one just
1378 // from the API schema type name.
1379 if (const TfTokenVector *canOnlyApplyToTypeNames =
1380 TfMapLookupPtr(canOnlyApplyToMap, apiSchemaName)) {
1381 return *canOnlyApplyToTypeNames;
1382 }
1383
1384 static const TfTokenVector empty;
1385 return empty;
1386 }
1387
1388 TfToken
GetPropertyNamespacePrefix(const TfToken & multiApplyAPISchemaName) const1389 UsdSchemaRegistry::GetPropertyNamespacePrefix(
1390 const TfToken &multiApplyAPISchemaName) const
1391 {
1392 if (const _MultipleApplyAPIDefinition *def = TfMapLookupPtr(
1393 _multiApplyAPIPrimDefinitions, multiApplyAPISchemaName)) {
1394 return def->propertyNamespace;
1395 }
1396 return TfToken();
1397 }
1398
1399 std::unique_ptr<UsdPrimDefinition>
BuildComposedPrimDefinition(const TfToken & primType,const TfTokenVector & appliedAPISchemas) const1400 UsdSchemaRegistry::BuildComposedPrimDefinition(
1401 const TfToken &primType, const TfTokenVector &appliedAPISchemas) const
1402 {
1403 if (appliedAPISchemas.empty()) {
1404 TF_CODING_ERROR("BuildComposedPrimDefinition without applied API "
1405 "schemas is not allowed. If you want a prim definition "
1406 "for a single prim type with no appied schemas, use "
1407 "FindConcretePrimDefinition instead.");
1408 return std::unique_ptr<UsdPrimDefinition>();
1409 }
1410
1411 // Start with a new prim definition mapped to the same prim spec path as the
1412 // prim type's definition. This does not yet add the prim type's properties.
1413 // Note that its perfectly valid for there to be no prim definition for the
1414 // given prim type, in which case we compose API schemas over an empty prim
1415 // definition.
1416 const UsdPrimDefinition *primDef = FindConcretePrimDefinition(primType);
1417 std::unique_ptr<UsdPrimDefinition> composedPrimDef(primDef ?
1418 new UsdPrimDefinition(
1419 primDef->_schematicsPrimPath, /* isAPISchema = */ false) :
1420 new UsdPrimDefinition());
1421
1422 // We compose in the new API schemas first as these API schema property
1423 // opinions need to be stronger than the prim type's prim definition's
1424 // opinions.
1425 _ComposeAPISchemasIntoPrimDefinition(
1426 composedPrimDef.get(), appliedAPISchemas);
1427
1428 // Now compose in the prim type's properties if we can.
1429 if (primDef) {
1430 composedPrimDef->_ComposePropertiesFromPrimDef(*primDef);
1431 // The prim type's prim definition may have its own built-in API
1432 // schemas (which were already composed into its definition). We need
1433 // to append these to applied API schemas list for our composed prim
1434 // definition.
1435 composedPrimDef->_appliedAPISchemas.insert(
1436 composedPrimDef->_appliedAPISchemas.end(),
1437 primDef->_appliedAPISchemas.begin(),
1438 primDef->_appliedAPISchemas.end());
1439 }
1440
1441 return composedPrimDef;
1442 }
1443
1444 const UsdPrimDefinition *
_FindAPIPrimDefinitionByFullName(const TfToken & apiSchemaName,std::string * propertyPrefix) const1445 UsdSchemaRegistry::_FindAPIPrimDefinitionByFullName(
1446 const TfToken &apiSchemaName,
1447 std::string *propertyPrefix) const
1448 {
1449 // Applied schemas may be single or multiple apply so we have to parse
1450 // the full schema name into a type and possibly an instance name.
1451 auto typeNameAndInstance = GetTypeNameAndInstance(apiSchemaName);
1452 const TfToken &typeName = typeNameAndInstance.first;
1453 const TfToken &instanceName = typeNameAndInstance.second;
1454
1455 // If the instance name is empty we expect a single apply API schema
1456 // otherwise it should be a multiple apply API.
1457 if (instanceName.IsEmpty()) {
1458 if (const UsdPrimDefinition * const *apiSchemaTypeDef =
1459 TfMapLookupPtr(_singleApplyAPIPrimDefinitions, typeName)) {
1460 return *apiSchemaTypeDef;
1461 }
1462 } else {
1463 const _MultipleApplyAPIDefinition *multiApplyDef =
1464 TfMapLookupPtr(_multiApplyAPIPrimDefinitions, typeName);
1465 if (multiApplyDef) {
1466 // We also provide the full property namespace prefix for this
1467 // particular instance of the multiple apply API.
1468 *propertyPrefix = SdfPath::JoinIdentifier(
1469 multiApplyDef->propertyNamespace, instanceName);
1470 return multiApplyDef->primDef;
1471 }
1472 }
1473
1474 return nullptr;
1475 }
1476
_ComposeAPISchemasIntoPrimDefinition(UsdPrimDefinition * primDef,const TfTokenVector & appliedAPISchemas) const1477 void UsdSchemaRegistry::_ComposeAPISchemasIntoPrimDefinition(
1478 UsdPrimDefinition *primDef, const TfTokenVector &appliedAPISchemas) const
1479 {
1480 // Add in properties from each new applied API schema. Applied API schemas
1481 // are ordered strongest to weakest so we compose, in order, each weaker
1482 // schema's properties.
1483 for (const TfToken &apiSchemaName : appliedAPISchemas) {
1484
1485 std::string propPrefix;
1486 const UsdPrimDefinition *apiSchemaTypeDef =
1487 _FindAPIPrimDefinitionByFullName(apiSchemaName, &propPrefix);
1488
1489 if (apiSchemaTypeDef) {
1490 // Compose in the properties from the API schema def.
1491 primDef->_ComposePropertiesFromPrimDef(
1492 *apiSchemaTypeDef, propPrefix);
1493
1494 // Append all the API schemas included in the schema def to the
1495 // prim def's API schemas list.
1496 const TfTokenVector &apiSchemasToAppend =
1497 apiSchemaTypeDef->GetAppliedAPISchemas();
1498
1499 if (apiSchemasToAppend.empty()) {
1500 // The API def's applied API schemas list will be empty if it's
1501 // multiple apply API so in that case we append the schema name.
1502 primDef->_appliedAPISchemas.push_back(apiSchemaName);
1503 } else {
1504 // Otherwise, it's a single apply API and its definition's API
1505 // schemas list will hold itself followed by all other API
1506 // schemas that were composed into its definition.
1507 primDef->_appliedAPISchemas.insert(
1508 primDef->_appliedAPISchemas.end(),
1509 apiSchemasToAppend.begin(), apiSchemasToAppend.end());
1510 }
1511 }
1512 }
1513 }
1514
1515 void
Usd_GetAPISchemaPluginApplyToInfoForType(const TfType & apiSchemaType,const TfToken & apiSchemaName,std::map<TfToken,TfTokenVector> * autoApplyAPISchemasMap,TfHashMap<TfToken,TfTokenVector,TfHash> * canOnlyApplyAPISchemasMap,TfHashMap<TfToken,TfToken::Set,TfHash> * allowedInstanceNamesMap)1516 Usd_GetAPISchemaPluginApplyToInfoForType(
1517 const TfType &apiSchemaType,
1518 const TfToken &apiSchemaName,
1519 std::map<TfToken, TfTokenVector> *autoApplyAPISchemasMap,
1520 TfHashMap<TfToken, TfTokenVector, TfHash> *canOnlyApplyAPISchemasMap,
1521 TfHashMap<TfToken, TfToken::Set, TfHash> *allowedInstanceNamesMap)
1522 {
1523 PlugPluginPtr plugin =
1524 PlugRegistry::GetInstance().GetPluginForType(apiSchemaType);
1525 if (!plugin) {
1526 TF_CODING_ERROR("Failed to find plugin for schema type '%s'",
1527 apiSchemaType.GetTypeName().c_str());
1528 return;
1529 }
1530
1531 // We don't load the plugin, we just use its metadata.
1532 const JsObject dict = plugin->GetMetadataForType(apiSchemaType);
1533
1534 // Skip types that aren't applied API schemas
1535 const UsdSchemaKind schemaKind = _GetSchemaKindFromMetadata(dict);
1536 if (!_IsAppliedAPISchemaKind(schemaKind)) {
1537 return;
1538 }
1539
1540 // Both single and multiple apply API schema types can have metadata
1541 // specifying the list that the type can only be applied to.
1542 TfTokenVector canOnlyApplyToTypeNames =
1543 _GetNameListFromMetadata(dict, _tokens->apiSchemaCanOnlyApplyTo);
1544 if (!canOnlyApplyToTypeNames.empty()) {
1545 (*canOnlyApplyAPISchemasMap)[apiSchemaName] =
1546 std::move(canOnlyApplyToTypeNames);
1547 }
1548
1549 if (schemaKind == UsdSchemaKind::SingleApplyAPI) {
1550 // Skip if auto apply API schemas have been disabled.
1551 if (TfGetEnvSetting(USD_DISABLE_AUTO_APPLY_API_SCHEMAS)) {
1552 return;
1553 }
1554
1555 // For single apply API schemas, we can get the types it should auto
1556 // apply to.
1557 TfTokenVector autoApplyToTypeNames =
1558 _GetNameListFromMetadata(dict, _tokens->apiSchemaAutoApplyTo);
1559 if (!autoApplyToTypeNames.empty()) {
1560 TF_DEBUG(USD_AUTO_APPLY_API_SCHEMAS).Msg(
1561 "API schema '%s' is defined to auto apply to the following "
1562 "schema types: [%s].\n",
1563 apiSchemaName.GetText(),
1564 TfStringJoin(autoApplyToTypeNames.begin(),
1565 autoApplyToTypeNames.end(), ", ").c_str());
1566 (*autoApplyAPISchemasMap)[apiSchemaName] =
1567 std::move(autoApplyToTypeNames);
1568 }
1569 } else {
1570 // For multiple apply schemas, the metadata may specify a list of
1571 // allowed instance names.
1572 TfTokenVector allowedInstanceNames =
1573 _GetNameListFromMetadata(dict, _tokens->apiSchemaAllowedInstanceNames);
1574 if (!allowedInstanceNames.empty()) {
1575 (*allowedInstanceNamesMap)[apiSchemaName].insert(
1576 allowedInstanceNames.begin(), allowedInstanceNames.end());
1577 }
1578
1579 // Multiple apply API schema metadata may specify a dictionary of
1580 // additional apply info for individual instance names. Right now this
1581 // will only contain additional "can only apply to" types for individual
1582 // instances names, but in the future we can add auto-apply metadata
1583 // here as well.
1584 const JsValue *apiSchemaInstancesValue =
1585 TfMapLookupPtr(dict, _tokens->apiSchemaInstances);
1586 if (!apiSchemaInstancesValue) {
1587 return;
1588 }
1589
1590 if (!apiSchemaInstancesValue->IsObject()) {
1591 TF_CODING_ERROR("Metadata value for key '%s' for API schema type "
1592 "'%s' is not holding a dictionary. PlugInfo may "
1593 "need to be regenerated.",
1594 _tokens->apiSchemaInstances.GetText(),
1595 apiSchemaName.GetText());
1596 return;
1597 }
1598
1599 // For each instance name in the metadata dictionary we grab any
1600 // "can only apply to" types specified for and add it to
1601 // "can only apply to" types map under the fully joined API schema name
1602 for (const auto &entry : apiSchemaInstancesValue->GetJsObject()) {
1603 const std::string &instanceName = entry.first;
1604
1605 if (!entry.second.IsObject()) {
1606 TF_CODING_ERROR("%s value for instance name '%s' for API "
1607 "schema type '%s' is not holding a dictionary. "
1608 "PlugInfo may need to be regenerated.",
1609 _tokens->apiSchemaInstances.GetText(),
1610 instanceName.c_str(),
1611 apiSchemaName.GetText());
1612 continue;
1613 }
1614 const JsObject &instanceDict = entry.second.GetJsObject();
1615
1616 const TfToken schemaInstanceName(
1617 SdfPath::JoinIdentifier(apiSchemaName, instanceName));
1618
1619 TfTokenVector instanceCanOnlyApplyToTypeNames =
1620 _GetNameListFromMetadata(instanceDict,
1621 _tokens->apiSchemaCanOnlyApplyTo);
1622 if (!instanceCanOnlyApplyToTypeNames.empty()) {
1623 (*canOnlyApplyAPISchemasMap)[schemaInstanceName] =
1624 std::move(instanceCanOnlyApplyToTypeNames);
1625 }
1626 }
1627 }
1628 }
1629
1630 PXR_NAMESPACE_CLOSE_SCOPE
1631
1632