Skip to content

Commit

Permalink
The UsdSchemaRegistry no longer combines all the plugin generatedSchema
Browse files Browse the repository at this point in the history
files into a single large schematics layer and instead just allows the
UsdPrimDefinitions it builds to map its properties directly to the
generateSchema layer that holds its prim spec. This is a performance
improvement in the initialization of the schema registry and opens up
potential further improvement to the generatedSchemas themselves.

Refactored some of the prim definition building implementation, moving
it from UsdSchemaRegistry into UsdPrimDefinition with the goal of having
all the code that handles the _LayerAndPath structure live in the
UsdPrimDefinition code itself.

Upgraded _LayerAndPath from a std::pair to a struct with helper accessor
members for the now common uses of accessing data from the layer and
path.

(Internal change: 2268201)
  • Loading branch information
pixar-oss committed Mar 21, 2023
1 parent 8dba2ed commit a328724
Show file tree
Hide file tree
Showing 6 changed files with 414 additions and 466 deletions.
247 changes: 177 additions & 70 deletions pxr/usd/usd/primDefinition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,43 @@

PXR_NAMESPACE_OPEN_SCOPE

UsdPrimDefinition::UsdPrimDefinition(
void
UsdPrimDefinition::_IntializeForTypedSchema(
const SdfLayerHandle &schematicsLayer,
const SdfPath &schematicsPrimPath, bool isAPISchema)
: _primLayerAndPath{get_pointer(schematicsLayer), schematicsPrimPath}
const SdfPath &schematicsPrimPath,
const VtTokenArray &propertiesToIgnore)
{
// If this prim definition is not for an API schema, the primary prim spec
// will provide the prim metadata. map the empty property name to the prim
// path in the schematics for the field accessor functions.
// Note that this mapping aids the efficiency of value resolution by
// allowing UsdStage to access fallback metadata from both prims and
// properties through the same code path without extra conditionals.
if (!isAPISchema) {
_primLayerAndPath = {get_pointer(schematicsLayer), schematicsPrimPath};

if (_MapSchematicsPropertyPaths(propertiesToIgnore)) {
// Prim definition for Typed schemas use the prim spec to provide prim
// level metadata, so we map the empty property name to the prim path
// in the schematics for the field accessor functions. This mapping aids
// the efficiency of value resolution by allowing UsdStage to access
// fallback metadata from both prims and properties through the same
// code path without extra conditionals.
// Note that API schema prim definitions do not provide prim level
// metadata so they exclude this mapping
_propLayerAndPathMap.emplace(TfToken(), _primLayerAndPath);
}
}

void
UsdPrimDefinition::_IntializeForAPISchema(
const TfToken &apiSchemaName,
const SdfLayerHandle &schematicsLayer,
const SdfPath &schematicsPrimPath,
const VtTokenArray &propertiesToIgnore)
{
// We always include the API schema itself as the first applied API
// schema in its prim definition.
_appliedAPISchemas = {apiSchemaName};

_primLayerAndPath = {get_pointer(schematicsLayer), schematicsPrimPath};

_MapSchematicsPropertyPaths(propertiesToIgnore);
}

SdfPropertySpecHandle
UsdPrimDefinition::GetSchemaPropertySpec(const TfToken& propName) const
{
Expand All @@ -54,8 +75,8 @@ UsdPrimDefinition::GetSchemaPropertySpec(const TfToken& propName) const
// shouldn't be editable via the prim definitions. But these methods
// already exist and return an editable property spec. These methods
// should one day be deprecated and replaced.
return const_cast<SdfLayer *>(layerAndPath->first)->GetPropertyAtPath(
layerAndPath->second);
return const_cast<SdfLayer *>(layerAndPath->layer)->GetPropertyAtPath(
layerAndPath->path);
}
return TfNullPtr;
}
Expand All @@ -68,8 +89,8 @@ UsdPrimDefinition::GetSchemaAttributeSpec(const TfToken& attrName) const
// shouldn't be editable via the prim definitions. But these methods
// already exist and return an editable property spec. These methods
// should one day be deprecated and replaced.
return const_cast<SdfLayer *>(layerAndPath->first)->GetAttributeAtPath(
layerAndPath->second);
return const_cast<SdfLayer *>(layerAndPath->layer)->GetAttributeAtPath(
layerAndPath->path);
}
return TfNullPtr;
}
Expand All @@ -82,8 +103,8 @@ UsdPrimDefinition::GetSchemaRelationshipSpec(const TfToken& relName) const
// shouldn't be editable via the prim definitions. But these methods
// already exist and return an editable property spec. These methods
// should one day be deprecated and replaced.
return const_cast<SdfLayer *>(layerAndPath->first)->GetRelationshipAtPath(
layerAndPath->second);
return const_cast<SdfLayer *>(layerAndPath->layer)->GetRelationshipAtPath(
layerAndPath->path);
}
return TfNullPtr;
}
Expand All @@ -98,10 +119,7 @@ UsdPrimDefinition::GetDocumentation() const
// field from the schematics for the prim path (which we store for all
// definitions specifically to access the documentation).
std::string docString;
if (_primLayerAndPath.first) {
_primLayerAndPath.first->HasField(
_primLayerAndPath.second, SdfFieldKeys->Documentation, &docString);
}
_primLayerAndPath.HasField(SdfFieldKeys->Documentation, &docString);
return docString;
}

Expand All @@ -120,86 +138,134 @@ TfTokenVector
UsdPrimDefinition::_ListMetadataFields(const TfToken &propName) const
{
if (const _LayerAndPath *layerAndPath = _GetPropertyLayerAndPath(propName)) {
// Get the list of fields from the schematics for the property (or prim)
// path and remove the fields that we don't allow fallbacks for.
TfTokenVector fields =
layerAndPath->first->ListFields(layerAndPath->second);
fields.erase(std::remove_if(fields.begin(), fields.end(),
&UsdSchemaRegistry::IsDisallowedField),
fields.end());
return fields;
return layerAndPath->ListMetadataFields();
}
return TfTokenVector();
}

void
UsdPrimDefinition::_AddProperties(
std::vector<std::pair<TfToken, SdfPath>> &&propNameToPathVec)
TfTokenVector
UsdPrimDefinition::_LayerAndPath::ListMetadataFields() const
{
_properties.reserve(_properties.size() + propNameToPathVec.size());
// Get the list of fields from the schematics for the property (or prim)
// path and remove the fields that we don't allow fallbacks for.
TfTokenVector fields = layer->ListFields(path);
fields.erase(
std::remove_if(fields.begin(), fields.end(),
&UsdSchemaRegistry::IsDisallowedField),
fields.end());
return fields;
}

for (auto &propNameAndPath : propNameToPathVec) {
bool
UsdPrimDefinition::_MapSchematicsPropertyPaths(
const VtTokenArray &propertiesToIgnore)
{
// Get the names of all the properties defined in the prim spec.
TfTokenVector specPropertyNames;
if (!_primLayerAndPath.HasField(
SdfChildrenKeys->PropertyChildren, &specPropertyNames)) {
if (!_primLayerAndPath.layer->HasSpec(_primLayerAndPath.path)) {
// While its possible for the spec to have no properties, we expect
// the prim spec itself to exist.
TF_WARN("No prim spec exists at path '%s' in schematics layer %s.",
_primLayerAndPath.path.GetText(),
_primLayerAndPath.layer->GetIdentifier().c_str());
return false;
}
return true;
}

auto addPropFn = [&](TfToken &propName) {
_LayerAndPath propLayerAndPath {
_primLayerAndPath.layer,
_primLayerAndPath.path.AppendProperty(propName)};
auto insertIt = _propLayerAndPathMap.emplace(
std::move(propNameAndPath.first),
std::make_pair(_primLayerAndPath.first,
std::move(propNameAndPath.second)));
std::move(propName), std::move(propLayerAndPath));
if (insertIt.second) {
_properties.push_back(insertIt.first->first);
}
};

_properties.reserve(specPropertyNames.size());
// If there are no properties to ignore just simply add all the properties
// found in the spec. Otherwise, we need to do the check to skip adding
// any ignored properties.
if (propertiesToIgnore.empty()) {
for (TfToken &propName : specPropertyNames) {
addPropFn(propName);
}
} else {
for (TfToken &propName : specPropertyNames) {
// Note the propertiesToIgnore list is expected to be extremely
// small (like a few entries at most) so linear search should be
// efficient enough.
if (std::find(propertiesToIgnore.begin(),
propertiesToIgnore.end(),
propName) == propertiesToIgnore.end()) {
addPropFn(propName);
}
}
}

return true;
}

// Returns true if the property with the given name in these two separate prim
// definitions have the same type. "Same type" here means that they are both
// the same kind of property (attribute or relationship) and if they are
// attributes, that their attributes type names are the same.
static bool _PropertyTypesMatch(
const UsdPrimDefinition &strongerPrimDef,
const UsdPrimDefinition &weakerPrimDef,
const TfToken &propName)
/*static*/
bool UsdPrimDefinition::_PropertyTypesMatch(
const _LayerAndPath &strongLayerAndPath,
const _LayerAndPath &weakLayerAndPath)
{
// Empty prop name represents the schema's prim level metadata. This
// doesn't have a "property type" so it always matches.
if (propName.IsEmpty()) {
return true;
}

const SdfSpecType specType = strongerPrimDef.GetSpecType(propName);
const SdfSpecType specType = strongLayerAndPath.GetSpecType();
const bool specIsAttribute = (specType == SdfSpecTypeAttribute);

// Compare spec types (relationship vs attribute)
if (specType != weakerPrimDef.GetSpecType(propName)) {
TF_WARN("%s '%s' from stronger schema failed to override %s '%s' "
"from weaker schema during schema prim definition composition "
"because of the property spec types do not match.",
if (specType != weakLayerAndPath.GetSpecType()) {
TF_WARN("%s at path '%s' from stronger schema failed to override %s at "
"'%s' from weaker schema during schema prim definition "
"composition because of the property spec types do not match.",
specIsAttribute ? "Attribute" : "Relationsip",
propName.GetText(),
strongLayerAndPath.path.GetText(),
specIsAttribute ? "relationsip" : "attribute",
propName.GetText());
weakLayerAndPath.path.GetText());
return false;
}

// Compare variability
SdfVariability strongVariability, weakVariability;
strongLayerAndPath.HasField(SdfFieldKeys->Variability, &strongVariability);
weakLayerAndPath.HasField(SdfFieldKeys->Variability, &weakVariability);
if (weakVariability != strongVariability) {
TF_WARN("Property at path '%s' from stronger schema failed to override "
"property at path '%s' from weaker schema during schema prim "
"definition composition because their variability does not "
"match.",
strongLayerAndPath.path.GetText(),
weakLayerAndPath.path.GetText());
return false;
}

// Done comparing if its not an attribute.
// Done comparing if it's not an attribute.
if (!specIsAttribute) {
return true;
}

// Compare the type name field of the attributes.
TfToken strongerTypeName;
strongerPrimDef.GetPropertyMetadata(
propName, SdfFieldKeys->TypeName, &strongerTypeName);
TfToken weakerTypeName;
weakerPrimDef.GetPropertyMetadata(
propName, SdfFieldKeys->TypeName, &weakerTypeName);
if (weakerTypeName != strongerTypeName) {
TF_WARN("Attribute '%s' with type name '%s' from stronger schema "
"failed to override attribute '%s' with type name '%s' from "
"weaker schema during schema prim definition composition "
"because of the attribute type names do not match.",
propName.GetText(),
strongerTypeName.GetText(),
propName.GetText(),
weakerTypeName.GetText());
TfToken strongTypeName, weakTypeName;
strongLayerAndPath.HasField(SdfFieldKeys->TypeName, &strongTypeName);
weakLayerAndPath.HasField(SdfFieldKeys->TypeName, &weakTypeName);
if (weakTypeName != strongTypeName) {
TF_WARN("Attribute at path '%s' with type name '%s' from stronger "
"schema failed to override attribute at path '%s' with type "
"name '%s' from weaker schema during schema prim definition "
"composition because of the attribute type names do not match.",
strongLayerAndPath.path.GetText(),
strongTypeName.GetText(),
weakLayerAndPath.path.GetText(),
weakTypeName.GetText());
return false;
}
return true;
Expand Down Expand Up @@ -231,7 +297,7 @@ UsdPrimDefinition::_ComposePropertiesFromPrimDef(
// replace the existing with the weaker property if the types
// do not match.
if (useWeakerPropertyForTypeConflict &&
!_PropertyTypesMatch(*this, weakerPrimDef, it.first)) {
!_PropertyTypesMatch(insertResult.first->second, it.second)) {
insertResult.first->second = it.second;
}
}
Expand Down Expand Up @@ -260,6 +326,47 @@ UsdPrimDefinition::_ComposePropertiesFromPrimDefInstance(
}
}

void
UsdPrimDefinition::_ComposeOverAndReplaceExistingProperty(
const TfToken &propName,
const SdfLayerRefPtr &overLayer,
const SdfPath &overPrimPath)
{
// Get the path to the property in the prim definition that the
// override property applies to. If no such property exists, we ignore
// the override.
UsdPrimDefinition::_LayerAndPath *defLayerAndPath =
_GetPropertyLayerAndPath(propName);
if (!defLayerAndPath) {
return;
}

// Property overrides are not allowed to change the type of a property
// from its defining spec.
_LayerAndPath overLayerAndPath{
get_pointer(overLayer), overPrimPath.AppendProperty(propName)};
if (!_PropertyTypesMatch(overLayerAndPath, *defLayerAndPath)) {
return;
}

// Compose the defined property into the override property spec to
// get the property spec with the overrides applied. Any fields that
// are defined in the override spec are stronger so we copy the defined
// spec fields that aren't already in the override spec.
for (const TfToken srcField : defLayerAndPath->ListMetadataFields()) {
if (!overLayerAndPath.HasField<VtValue>(srcField, nullptr)) {
VtValue value;
if (defLayerAndPath->HasField(srcField, &value)) {
overLayer->SetField(overLayerAndPath.path, srcField, value);
}
}
}

// With the override spec composed, set the definition's path for the
// property to the composed override spec path.
*defLayerAndPath = std::move(overLayerAndPath);
}

bool
UsdPrimDefinition::_ComposeWeakerAPIPrimDefinition(
const UsdPrimDefinition &apiPrimDef,
Expand Down
Loading

0 comments on commit a328724

Please sign in to comment.