Skip to content

v0.6.0

Choose a tag to compare

@mxpv mxpv released this 02 Aug 01:02
· 454 commits to main since this release
8d605e3

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.3 Arc permissions (permission = private) — parsed, stored, and round-tripped; deliberately not enforced by composition, matching C++'s Usd-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 pluggable sdf::FileFormat seam 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 a Vec<u8> or String (mirroring ExportToString in C++ USD).
  • Layer change tracking now lives on sdf::Layer itself: every edit runs through Layer::edit as an atomic copy-on-write transaction that derives an sdf::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 index dependency map. A significant arc edit drops the affected subtree; an inert over add/remove refreshes has_specs in place instead of rebuilding; a targetPaths/connectionPaths edit clears one property's memoized targets; a subLayers/offset/timeCodesPerSecond edit drops only indices reading the affected layer stacks; and an expressionVariables edit resyncs exactly the prims that recorded reading the changed variables. Unaffected siblings stay cached.
  • sdf::PathTable is 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::Resolver interface was reworked: it now exposes a stable identity, 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::AssetPath carries 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::Token is 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::ClipsAPI covers explicit and template value clips, read and write, to UsdClipsAPI parity, 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::TimeCode drives value resolution, attributes expose sample queries (time_sample_times, time_samples_in_interval, bracketing_time_samples), and the cached usd::AttributeQuery resolves 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::LayerSink at a layer's commit seam (veto in before_commit, observe in after_commit) and usd::StageSink for 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::NamespaceEditor batches 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::LayerRegistry is the single owner of asset loading and format dispatch: composition opens reference/payload targets through it on demand, and it is where custom sdf::FileFormats will register in the future.

  • usd::UndoStage / usd::ReplayStage record edits as a transferable Diff, applied atomically with Stage::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 expressionVariables edit (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 clipSets list-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 into PrimEntry (036e009, 41da34f, cb86a7c)

Scene description (sdf)

  • Pluggable FileFormat read/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::cast tier (45e3469)
  • Make AbstractData read+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::TimeCode for 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

  • ClipsAPI explicit value-clip getters and template-clip getters (7bf91af, bd80753)
  • ClipsAPI value-clip setters (8e4325a)
  • Extend ClipsAPI to UsdClipsAPI parity (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 .usdz packages (589e4be)
  • Anchor packaged layers by real path (7255b2d)
  • Sniff format of extension-agnostic .usd layers (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

  • usdtree-style stage tree printer example (a2f3111)
  • Track peak heap in usdtree (2cb15da)

Dependencies

  • Update lz4_flex from 0.13 to 0.14 (c0a28ef)