Fast Stage Queries with USDRT Scenegraph API
About
usdrt-6.0.6 adds new APIs to the usdrt::UsdStage
implementation that
leverage the capability of Fabric to do constant-time queries of
data in Fabric. This document reviews those APIs, their behavior, and
how you can leverage them to increase performance in your own application or
extension.
Query the stage for prims by type or applied API schema
There are three new APIs to do fast queries of the stage for prims of a given type, prims with a specific applied API schema, or both. These are specific to USDRT’s UsdStage implementation, and leverage Fabric’s unique data layout to return information about the stage without requiring a complete stage traversal with per-prim inquiries.
std::vector<SdfPath> UsdStage::GetPrimsWithTypeName(TfToken typeName)
std::vector<SdfPath> UsdStage::GetPrimsWithAppliedAPIName(TfToken apiName)
std::vector<SdfPath> UsdStage::GetPrimsWithTypeAndAppliedAPIName(TfToken typeName, TfTokenVector apiNames)
The first call to any of these APIs will populate Fabric with a minimal representation of the USD stage if necessary. Subsequent calls will leverage the existing data in Fabric and query Fabric directly.
Query by prim type
Both of these examples discover all Mesh prims, and set the
primvars:displayColor attribute if it exists on the prim. Note that
these APIs return a vector of SdfPaths, so an additional call
to UsdStage::GetPrimAtPath
is necessary to access the related
UsdPrim.
C++
using namespace usdrt;
const TfToken mesh("Mesh");
const TfToken attrName = UsdGeomTokens->primvarsDisplayColor;
const VtArray<GfVec3f> red = { GfVec3f(1, 0, 0) };
const SdfPathVector meshPaths = stage->GetPrimsWithTypeName(mesh);
for (SdfPath meshPath : meshPaths)
{
UsdPrim prim = stage->GetPrimAtPath(meshPath);
if (prim.HasAttribute(attrName))
{
prim.GetAttribute(attrName).Set(red);
}
}
Python
from usdrt import Gf, Sdf, Usd, UsdGeom, Vt
red = Vt.Vec3fArray([Gf.Vec3f(1, 0, 0)])
meshPaths = stage.GetPrimsWithTypeName("Mesh")
for meshPath in meshPaths:
prim = stage.GetPrimAtPath(meshPath)
if prim.HasAttribute(UsdGeom.Tokens.primvarsDisplayColor):
prim.GetAttribute(UsdGeom.Tokens.primvarsDisplayColor).Set(red)
It is also possible to search by inherited types. This example discovers all prims where “doubleSided” is true for the stage.
from usdrt import Gf, Sdf, Usd, UsdGeom
gPrimPaths = stage.GetPrimsWithTypeName("Gprim")
attr = UsdGeom.Tokens.doubleSided
doubleSided = []
for gPrimPath in gPrimPaths:
prim = stage.GetPrimAtPath(gPrimPath)
if prim.HasAttribute(attr) and prim.GetAttribute(attr).Get():
doubleSided.append(prim)
Query by applied API schema
It may be useful in some cases to discover every prim where a particular API schema has been applied. It is possible to query by individual API schema, or by a list of API schemas for a specific prim type. The following examples from the USDRT Scenegraph unit tests demonstrate the concept:
from usdrt import Gf, Sdf, Usd
stage = Usd.Stage.Open(TEST_DIR + "/data/usd/tests/cornell.usda")
self.assertTrue(stage)
paths = stage.GetPrimsWithAppliedAPIName("ShapingAPI")
self.assertEqual(len(paths), 2)
paths = stage.GetPrimsWithAppliedAPIName("CollectionAPI:lightLink")
self.assertEqual(len(paths), 5)
from usdrt import Gf, Sdf, Usd
stage = Usd.Stage.Open(TEST_DIR + "/data/usd/tests/cornell.usda")
self.assertTrue(stage)
paths = stage.GetPrimsWithTypeAndAppliedAPIName("Mesh", ["CollectionAPI:test"])
self.assertEqual(len(paths), 1)
Get the world bound for the stage
An additional API exists that leverages world extent data in Fabric set by Fabric Scene Delegate or the RtBoundable schema to compute the bounding extent for the stage:
GfRange3d UsdStage::GetStageExtent()
If no world extent data exists in Fabric, this API will populate it from USD using a multi-threaded compute and Fabric queries to target only Boundable prims directly.
from usdrt import Gf, Sdf, Usd
stage = Usd.Stage.Open(TEST_DIR + "/data/usd/tests/cornell.usda")
self.assertTrue(stage)
bound = stage.GetStageExtent()
self.assertTrue(Gf.IsClose(bound.GetMin(), Gf.Vec3d(-333.8142, -249.9100, -5.444), 0.01))
self.assertTrue(Gf.IsClose(bound.GetMax(), Gf.Vec3d(247.1132, 249.9178, 500.0), 0.01))
Using stage queries for optimal performance
The key benefit of these query APIs is that they only populate Fabric with the minimal set of data from the underlying USD stage required for each query, and once populated, querying Fabric is extremely fast. Fabric queries are performed in constant time, and unlike stage traversal, do not increase relative to prim count on the USD stage.
This performance characteristic is beneficial for modular software architectures where each module may require independent inquiries into the USD stage. For example, consider a hypothetical suite of Kit extensions where each extension needs to do one of the following inquiries into the USD stage in response to a stage-opened event:
A BackupLight extension checks if there are any lights on the USD stage, and if not, creates a default light
A CharacterAnimator extension finds all of the UsdSkel prims on the USD stage and populates a menu with their names
A FastRender extension checks if any Mesh prim names end with the string “_glass” to optimize render settings
A ForkliftSimulator extension finds all prims with the PhysicsDriveAPI schema applied to prepare a simulation context
Accomplishing these tasks with the conventional USD API requires each extension to independently perform a complete traversal of the USD stage. However, with the USDRT stage query APIs and underlying Fabric data, at most one USD stage traversal is required for lightweight Fabric population, and subsequent queries after the first leverage the data that has already been populated into Fabric. This makes stage inspection fast and lightweight, and modularity straightforward.