Loading USD (population)
When to Use
Use this skill when the program ingests existing USD into an ovstage instead of
writing attribute columns directly. That includes: loading a .usd/.usda/.usdc
file or an inline USDA string into the stage, choosing which data domains to
populate (rendering vs. physics), adding/removing USD references, propagating live
USD edits per tick, or confirming what landed by querying populated prims.
For the direct write → seal → read data-plane lifecycle (no USD), use
application-flow and path-dictionary instead.
For the reverse path — exporting this populated runtime state back to a USD
file or a reusable destination — use exporting-to-usd.
Inputs
Resolve inputs in this order: existing repository files and referenced snippets, explicit user request, then broader agent context.
- USD source: a file/URL path, or an inline USDA string.
- Data domains to populate: RENDERING, PHYSICS, or ALL (bitmask).
0(DOMAIN_NONE) authors nothing. - The caller-owned
ordinalfor the tick (population never opens/commits its own). - Whether this is a one-shot open or a running loop with live edits
(
apply_usd_changes) / time playback (apply_usd_time). - The shipped
ovstage_population.hheader and the referenced test snippets are the authoritative contract.
Prerequisites
- A created ovstage instance (
ovstage_create_instance/ovstage.Stage). Per-stage population state is created lazily on first populate call and released with the instance. - Understand the async submit/observe model (
cpu-ahead-gpu-async): population enqueues return an op id; nothing is materialized until you wait on it. - Ordinal ownership: the caller (frame coordinator) owns the ordinal and the write floor; population is invoked against a caller-provided ordinal.
Instructions
- Open the USD into the stage. Call
ovstage_population_open_usd_from_string(inline USDA) or_open_usd_from_file(path/URL), passing the tickordinal, atimein seconds, and adomainsbitmask. This both loads the USD and populates the ovstage in one op, replacing any previously loaded content. - Await the op. Population is asynchronous — wait via
ovstage_population_wait_op(C) or the blockingpopulation.open_usd_from_string/Operation.wait()(Python). - Read back / consume. Populated prims are queryable immediately (queries
resolve against the latest committed state). Confirm with a
usd-pathfilter query, or hand the stage to a consumer (e.g.ovrtx_attach_ovstage). - Live edits (optional). For a running loop:
add_usd_reference_*/remove_usd_reference/reset_usdedit the USD source only — follow withapply_usd_changes(ordinal)to propagate structural edits into the ovstage; useapply_usd_time(ordinal, time)once per tick for time-sampled playback. - Diagnose failures via
ovstage_population_get_last_error/_get_last_op_error(C) or theOvstageErrorraised by the blocking Python wrappers.
Output Format
- For explanations, return the populate → wait → query/consume sequence with the chosen domains and where live edits fit.
- For code changes, summarize the population calls touched, snippets affected, and the validation run.
Overview
ovstage itself has no USD dependency; the population API is the bridge that reads
USD (files or inline USDA) and mirrors it into an ovstage instance so consumers like
ovrtx can render it. The initial open_usd_* is one-shot (it replaces prior USD
content); subsequent live edits are picked up by apply_usd_changes (structural) and
apply_usd_time (time-sampled).
Domains
ovstage_population_domain_t is a bitmask — OR values together:
RENDERING— meshes, lights, materials, and cameras.PHYSICS— colliders, rigid bodies, joints, articulations, physics schema attrs.ALL— both.NONE(0) authors nothing.
Stage metadata is populated onto the root prim, under the usd-metadata:<path> column
prefix, when something asks for it: a selected domain may bring its own, and a
description names whatever it wants through stage_metadata_paths.
Selectors
A domain is a prebuilt policy; ovstage_population_selector_t is how a caller writes their
own. Each selector pairs a prim predicate (which prims) with a property predicate (which of
their properties), and selectors compose: a prim is in scope if any selector matches it, and
its properties are the union over the selectors that did. Selectors go in a desc alongside
domains, so a caller's own selection unions with a built-in domain in one traversal.
Predicate kinds accepted today — prim: NONE, ALL, AND, OR, NOT, HAS_SCHEMA,
HAS_TYPE, IS_A_TYPE, HAS_APPLIED_SCHEMA, HAS_APPLIED_SCHEMA_IN_NAMESPACE, HAS_PATH,
IS_UNDER_PATH, HAS_KIND, HAS_PURPOSE, HAS_METADATA, HAS_PARENT, HAS_ANCESTOR,
HAS_PROPERTY;
property: NONE, ALL, AND, OR, NOT, DECLARED_BY_SCHEMA, HAS_NAME, IN_NAMESPACE,
HAS_METADATA, IS_ATTRIBUTE, IS_RELATIONSHIP, IS_CUSTOM, IS_AUTHORED — the whole declared
vocabulary. A kind outside either enum is rejected at enqueue with NOT_SUPPORTED, as is a
malformed graph, so a description cannot silently populate nothing through a kind the build
cannot honour. Values are not checked against any registry: a schema or type name nothing
registers is accepted and matches nothing, so spell those from the stage you are populating.
HAS_PROPERTY nests a property predicate inside a prim one, so a prim can be gated on what it
carries. It sees every property the prim has — those it authors, including custom ones no schema
declares, and those its schemas declare that resolve to a fallback. So an unauthored schema
property still counts; pair it with IS_AUTHORED for "carries an authored value".
The common shape is "populate these schemas": HAS_SCHEMA over the schema names, paired
with DECLARED_BY_SCHEMA over the same names to publish just what those schemas declare.
The same list serves both halves — HAS_SCHEMA matches a name whether it is the prim's type
or one of its applied API schemas, so a schema list needs no partitioning. Narrow it to a
branch by AND-ing IS_UNDER_PATH.
Source:
tests/python/test_population.pysnippetspopulate-by-schema,populate-narrowed,populate-stage-metadata,populate-several-descs;tests/c/test_population.cppsnippetspopulate-by-schema-c,populate-narrowed-c
In C a nested predicate and its values are referenced, not copied, so every node and string array must outlive the call — hence the named locals in the C snippets. The Python builder owns them instead, so a tree can be built inline.
Registering your own USD schemas
ovstage ships only what its USD build registers — the core Usd* schemas. If your
stages use anything else (a physics extension such as PhysxSchema, a sensor
family, your studio's own schemas), register it first:
Source:
tests/c/test_population.cppsnippetregister-usd-schemas-c
Each path is a plugInfo.json or a directory containing one; a descriptor's
Includes are followed, so one entry can pull in a whole tree of families.
No schema code is loaded: USD loads a schema's C++ library only when something
asks for that schema's C++ type, and population never does. A code-full drop is
consumed for its definitions alone, library untouched.
Without this, prims carrying those schemas are still populated, but only their authored attributes are visible: unauthored attributes do not resolve their schema fallbacks and do not appear in the prim's property set.
Order matters. USD assembles its schema definitions once, the first time anything reads them, and never revisits that set. Register before the first ovstage call that reads them — populating a stage and exporting one both do — as a family registered afterwards registers cleanly and still contributes nothing. ovstage reports a registration that arrives after its own first such call; one that arrives after some other USD consumer in the process got there first cannot be detected.
Registering does not change which prims a domain claims. It makes a family's definitions resolvable.
Python
The verified test populates from an inline USDA string and confirms the prim landed
via a usd-path filter query:
Source:
tests/python/test_population.pysnippetpopulate-and-query
Live edits are asserted too — add a USD reference then propagate it, reset the USD source, and confirm a missing file fails the populate op:
Source:
tests/python/test_population.pysnippetsusd-reference,reset-usd,open-missing-file
population.open_usd_from_string(stage, usda, ordinal=, time_code=, domains=) blocks;
open_usd_from_string_async returns an Operation for the CPU-ahead pattern
(cpu-ahead-gpu-async). open_usd / open_usd_from_file load from a path/URL. Live
edits: add_usd_reference[_from_string], remove_usd, reset_usd,
apply_usd_changes, update_from_usd_time (each has an *_async variant).
C
The C sibling drives the async op explicitly and reads back with a usd-path filter
query:
Source:
tests/c/test_population.cppsnippetpopulate-and-query-c
Live edits (add/apply/remove a reference, reset) and the missing-file failure are asserted in the C sibling too:
Source:
tests/c/test_population.cppsnippetsusd-reference-c,reset-usd-c,open-missing-file-c
ovstage_population_open_usd_from_string / _open_usd_from_file return an enqueue
result (status + op_index); await with ovstage_population_wait_op. Reference
edits (ovstage_population_add_usd_reference_from_*, _remove_usd_reference,
_reset_usd) touch USD source only — follow with ovstage_population_apply_usd_changes.
Key Functions
| Purpose | C | Python |
|---|---|---|
| Open inline USDA | ovstage_population_open_usd_from_string |
population.open_usd_from_string |
| Open file/URL | ovstage_population_open_usd_from_file |
population.open_usd / open_usd_from_file |
| Open with a description | ovstage_population_open_usd_from_{file,string}_with_desc |
population.open_usd_with_desc / open_usd_from_string_with_desc |
| Await op | ovstage_population_wait_op |
Operation.wait() (blocking wrappers auto-wait) |
| Add reference | ovstage_population_add_usd_reference_from_{file,string} |
population.add_usd_reference[_from_string] |
| Remove reference | ovstage_population_remove_usd_reference |
population.remove_usd |
| Reset USD source | ovstage_population_reset_usd |
population.reset_usd |
| Propagate structural edits | ovstage_population_apply_usd_changes |
population.apply_usd_changes |
| Time-sampled tick | ovstage_population_apply_usd_time |
population.update_from_usd_time |
Scripts
This skill has no scripts.
Limitations
- The referenced snippets are the source of truth; this skill composes them and describes the surrounding population API rather than introducing new code.
- One-shot open.
open_usd_*replaces previously loaded USD content; additive changes go throughadd_usd_reference_*+apply_usd_changes. - Surviving-anchor removal (current-build implementation gap). Removing a reference from an existing prim or a root sublayer may leave stale descendants; removal works when the resync root disappears.
- Ordinal is caller-owned. Population never opens/commits an ordinal; pass the tick ordinal in and advance the write floor yourself.
- Latest-snapshot payloads. Do not design a flow around historical payloads; bounded older change membership is available only at or above the reported retention frontier.
- ⚠️ Draft — API in flux. Treat exact symbols/ordering as provisional against the shipped headers.
Troubleshooting
- Populate enqueue rejected — a
NULLstage or (foradd_usd_reference/apply_usd_changes/reset_usd) no population state yet: call anopen_usd_*first. The rejection is in the enqueuestatus. - Op accepted but nothing materialized — enqueue success ≠ completion. Wait on
the op (
ovstage_population_wait_op/Operation.wait()); checkovstage_population_get_last_op_errorfor the failing op. - Query returns nothing after populate — verify the
usd-pathvalue matches the prim exactly (FILTER_OP_IN) or use a prefix; confirm the prim's domain was populated (a physics-only prim won't appear underRENDERING). - USDA fails to parse — surfaced by the wait, with detail in
ovstage_population_get_last_error; validate the USDA independently. - Reference/added subtree not visible —
add_usd_reference_*edits USD source only; callapply_usd_changes(ordinal)afterwards to propagate it into the ovstage.
References
- Use the
> **Source:**directives in this skill to locate tested snippets before reusing API patterns. application-flow— the direct write/read lifecycle (no USD).path-dictionary— tokens, path lists, and queries used to read populated prims back.cpu-ahead-gpu-async— the async submit/observe model shared by the*_asyncpopulation calls.error-handling— status checks and per-op error reporting.- The shipped
include/ovstage/ovstage_population.hheader is the authoritative contract.