Repository navigation
v0.6.0
This release is about maturing the core: large parts of the internal machinery — scene-description data, change tracking, and the PCP composition cache — were reorganized around clear data ownership and a single source of truth, making the crate easier to extend and a lot cheaper to run. On top of that foundation it introduces a set of new public APIs (value clips, first-class time samples, change listeners, payload load/unload, complete edit targets, and a namespace editor), and it is the first release validated against production-scale scenes: Activision's Caldera and Disney's Moana Island Scene both open and traverse correctly.
Compliance
New AOUSD Core Spec coverage in this release:
10.3.3Arc permissions (permission = private) — parsed, stored, and round-tripped; deliberately not enforced by composition, matching C++'sUsd-mode caches
Highlights
Internal rework
- The machinery around
sdf::AbstractData,sdf::Data, and the spec views was cleaned up into a unified read/write backend with clear data ownership and a single source of truth, and a pluggablesdf::FileFormatseam was introduced on top. Custom formats can now feed data straight into the USD infrastructure (think reading FBX or glTF into a layer), and layers can be exported to aVec<u8>orString(mirroringExportToStringin C++ USD). - Layer change tracking now lives on
sdf::Layeritself: every edit runs throughLayer::editas an atomic copy-on-write transaction that derives ansdf::ChangeList— there is no way to bypass it, so the stage always sees a faithful change record. - PCP cache invalidation is granular: a stage edit drops only what it actually affects, driven by a reverse
layer/site → prim indexdependency map. A significant arc edit drops the affected subtree; an inertoveradd/remove refresheshas_specsin place instead of rebuilding; atargetPaths/connectionPathsedit clears one property's memoized targets; asubLayers/offset/timeCodesPerSecondedit drops only indices reading the affected layer stacks; and anexpressionVariablesedit resyncs exactly the prims that recorded reading the changed variables. Unaffected siblings stay cached. sdf::PathTableis a path-keyed table whose subtree and ancestor walks replace full scans. It backs the PCP index store and dependency maps, so subtree invalidation and namespace queries are structural walks instead of O(cache) sweeps — faster and cleaner.- The
ar::Resolverinterface was reworked: it now exposes a stableidentity, and layer-stack identity is keyed on it — two stages opened through equal resolvers are the same composition input (which is what lets edit targets transfer between stages). Identifier creation and anchoring go through the resolver, so relative asset paths resolve consistently everywhere. sdf::AssetPathcarries an asset value's authored, evaluated (after variable expressions), and resolved forms together, integrated through the PCP engine — consumers get the resolver-anchored path without re-deriving it.tf::Tokenis an interned identifier type distinguishing tokens from plain strings, used across token-typed metadata and list-ops — a correctness win in the many places where the two used to blur.
New APIs
-
usd::ClipsAPIcovers explicit and template value clips, read and write, toUsdClipsAPIparity, and value resolution is clip-aware:let clips = usd::ClipsAPI::new(&stage.prim("/Model")); for set in clips.clip_set_names()? { let assets = clips.clip_asset_paths(&set)?; let active = clips.clip_active(&set)?; println!("{set}: {} clips, {} active ranges", assets.len(), active.len()); }
-
Time samples are first-class:
usd::TimeCodedrives value resolution, attributes expose sample queries (time_sample_times,time_samples_in_interval,bracketing_time_samples), and the cachedusd::AttributeQueryresolves the property stack once and answers repeated value queries cheaply:let attr = stage.attribute("/Points.positions"); let query = attr.query(); // resolves the property stack once for t in attr.time_sample_times()? { let value = query.get_at::<sdf::Value>(usd::TimeCode::new(t))?; }
-
Change listeners attach at two tiers:
sdf::LayerSinkat a layer's commit seam (veto inbefore_commit, observe inafter_commit) andusd::StageSinkfor composed change events with resynced/info-only paths in stage namespace:stage.add_sink(|_stage: &usd::Stage, change: &usd::CommittedChange<'_>| { for path in change.resynced { println!("resynced: {path}"); } });
-
Payloads load and unload through a stage API — a stage can open with nothing loaded and pull payloads in on demand:
let stage = usd::Stage::builder() .load(usd::InitialLoadSet::LoadNone) .open("island.usda")?; stage.load("/island/isBayCedarA1", usd::LoadPolicy::WithDescendants); stage.unload("/island/osOcean");
-
Edit targets are complete: local, variant, and arc targets with full path remapping, so authoring a composed path lands at the right spec in the right layer (including inside a referenced layer or a variant):
let target = stage.edit_target_for_node( &sdf::path("/World/Model")?, usd::EditTargetArc::Reference, )?; let _ctx = stage.edit_context(target)?; // Authors into the referenced layer, at the referenced prim's path. stage.attribute("/World/Model.size").set(2.0)?;
-
usd::NamespaceEditorbatches atomic rename / reparent / delete edits with fixup of relationship targets, connections, and internal references, authoring relocates for objects that arrive across composition arcs:let mut editor = usd::NamespaceEditor::new(&stage); editor .rename_prim(&stage.prim("/Old"), "New")? .reparent_prim(&stage.prim("/A/Child"), &stage.prim("/B"))? .delete_prim("/Obsolete"); editor.apply()?; // all-or-nothing
-
sdf::LayerRegistryis the single owner of asset loading and format dispatch: composition opens reference/payload targets through it on demand, and it is where customsdf::FileFormats will register in the future. -
usd::UndoStage/usd::ReplayStagerecord edits as a transferableDiff, applied atomically withStage::apply_diff— the building block for undo stacks and edit mirroring. -
The variable-expression evaluator was rewritten to C++ parity, including
matches_regex()support.
Production-scale scenes
As the crate matures it is now tested against real scenes, which drove a series of bug fixes and performance work: Caldera (60+ layers) and Disney's Moana Island Scene (#127, #129) both open and traverse correctly. The stage now loads layers on demand as composition reaches them instead of recursively up front, and dependency tracking was rescoped to observed layers — traversing Caldera went from 80+ GB of memory to about 1.1 GB, and from 10+ minutes to roughly 4 seconds.
Changelog
Composition engine (pcp)
- Fine-grained expression-variable dependency tracking and invalidation (1bc87ba)
- C++-model identity variant maps; fixes relationship/connection targets through variants (d16e640)
- Materialize instance prototypes reached through a non-root reference (02cc988)
- Re-resolvable session-region membership (5f4b3c7)
- Demand-driven sublayer loading to a fixed point (8e28539)
- Source-aware layer-stack identity (
VarsSource) (afa88c8) - Evaluate variant selection expressions (35a8d7a)
- Store composed expression variables per layer stack (2ce0445, 57dd030)
- Session variables resolve root
${VAR}sublayers (fd868a3) - Thread expression variables through sublayer edges (edec353)
- Expression variables across sub-root arc frames (40efa05)
- Invalidate on
expressionVariablesedit (eb2dbdf) - Context-keyed layer-stack instances (7d98b89)
- Handle-based layer stacks on composition nodes (533596f)
- Carry authoring context on layer demands (efc7030)
- Finer incremental invalidation (937dc37)
- Suffix-precise target-memo clear (d0e652f)
- Cheaper spec-tier change refresh (f3c48fc)
- Spec-tier rescan for inert spec edits (26254a8)
- Scope layer-stack-edit invalidation (06d619d)
- Scope dependencies to observed layers (e157d8e)
- Memoize per-node prim spec stack (bcf7691)
- Incremental recompose on layer mute (6bd975e)
- Wire muted layers into composition (abebac6)
- Key muted layers by canonical id (ae6121e)
- Surface muted reference/payload targets (91873dc)
- Muted-layer collection diagnostics (673649e)
- Resolve sublayer diamonds per ancestry (3d52f28)
- Relocate conflicts over the effective set (bdd4bbd)
- Graft implied relocations to the grandparent (ea11980)
- Cull empty class/variant arc targets (6b69a10)
- Un-cull dependents when an arc target is authored (44dd0b7)
- Stop enforcing
permission = private(matches C++Usd-mode caches) (8933fcb) - Keep overlapping
clipSetslist-op items in order (61fc88c) - Align clip introspection with
value_at(0aad3b9) - Unify value-clip ownership check (81ace95)
- Type clip metadata via strict extraction (3d79b45)
- Anchored-exact layer lookups (1f5eae7)
- Extract
IndexStore,compose_site,ClipCache; fold cache maps intoPrimEntry(036e009, 41da34f, cb86a7c)
Scene description (sdf)
- Pluggable
FileFormatread/write seam (371471a) - Copy-on-write layer edit transactions (9bb2427)
- Two-tier change sinks +
LayerEdit(bb0b142) - Unify change tracking on
CowData(da00f4a) - Spec-view authoring + change recording (7a6e414)
- Surface structural fields in change records (677157a)
- Spec-copy primitive (
SdfCopySpec) (eadaf63) - C++-parity expression evaluator, including
matches_regex()(6599175) PathTable; used in the pcp cache and dependency maps (03b4ba6)- Typed reads via
Value::get/Spec::get(b4878ad) - Type token list-ops and add a
Value::casttier (45e3469) - Make
AbstractDataread+write; typed read errors; renamed accessors (bda779b, 57e75df, 5adf1a5) - Mint unique anonymous layer identifiers (97f65fa)
batch_edit+Provenance(a3e2b55)- Keep list-op items present in multiple buckets (422da4d)
Stage & authoring (usd)
- Complete edit targets; arc edit targets and path remapping (417c09c, a548af5)
- Namespace editing via
NamespaceEditor(7d1b1b7) - Variant/arc namespace edit targets (e37abaf)
- Plan relocates for namespace edits; relocate synthesis via mapped targets (2b08f25, 7cdee82)
- Change-notification listeners (
UsdNotice) (856f218) - Edit-target and layer-muting notices (d3f607e)
- Notice paths in stage namespace (d25082f)
- Payload load/unload API (7934d9f)
- Lazy on-demand layer loading (5c4055b)
- Undo and replay edit recorders (96bd4d0)
- Diff-based edit replication (e2d1caa)
- Removal authoring +
apply_diff(a5fdf46) - Reject wrong path kind in prim/property removal (d38c6d8)
Value resolution & time samples
usd::TimeCodefor value resolution (c9320b9)- Attribute time-sample queries (36a5ac1)
- Cached
AttributeQuery(6efdff0) - Clip-aware time-sample introspection (e0f978c)
- Time-code setters honor the edit target (26d7ca1)
- Interpolate samples without cloning the map; resolve sample times without cloning (17a27d5, 8dbb58a)
Value clips
ClipsAPIexplicit value-clip getters and template-clip getters (7bf91af, bd80753)ClipsAPIvalue-clip setters (8e4325a)- Extend
ClipsAPItoUsdClipsAPIparity (e129dea)
Formats (usdc, usdz)
- Decode integer-compressed float arrays (b0044a2)
- Decode crate paths iteratively (29fbac5)
- Store crate specs as a thin index (165ee32)
- Resolve references inside
.usdzpackages (589e4be) - Anchor packaged layers by real path (7255b2d)
- Sniff format of extension-agnostic
.usdlayers (f455881)
Foundations (tf, gf, ar)
- Add
tf::Token; make token-typed reads token-only (49fb20f) - Key layer stack on resolver identity (a924d2b)
- Ungate matrix nested-array conversions (b616d5e)
Tooling
Dependencies
- Update
lz4_flexfrom 0.13 to 0.14 (c0a28ef)