openusd 0.8 generates typed Rust code from any USD schema

1 hour ago

openusd is a Rust implementation of Pixar's Universal Scene Description, with no C++ dependencies. Version 0.8 adds a code generator that turns a schema file into typed Rust views, and the standard schemas are now built with it. Stages also open and traverse faster.

Ask about this presentation

Chapters

  1. 0:00openusd 0.8
  2. 0:20Schema codegen
  3. 0:43Generated views
  4. 1:02Schema families
  5. 1:22Primvars
  6. 1:41Faster traversal
  7. 2:05Memory mapping
  8. 2:26Also new
  9. 2:48Upgrading
Show transcript
  1. openusd is a Rust implementation of Pixar's Universal Scene Description, with no C++ dependencies. Version 0.8 adds a code generator that turns a schema file into typed Rust views, and the standard schemas are now built with it. Stages also open and traverse faster.

    Slide text

    openusd 0.8 generates typed Rust code from any USD schema

    Release v0.8.0 of mxpv/openusd, a Rust implementation of OpenUSD with no C++ dependencies

  2. The new crate, openusd-build, does the job that usdGenSchema does for C++, without C++ or Python. Add it as a build dependency. In the build script, give it a search path and a schema file for each library, then call generate. Each library becomes one Rust file, with tokens, a schemas constant, typed views and token enums.

    Slide text
    openusd v0.8.0
    • Schema codegen
    • Generated views
    • Schema families
    • Primvars
    • Faster traversal
    • Memory mapping
    • Also new
    • Upgrading

    Point a build script at schema.usda and get typed Rust views

    Cargo.toml
    [dependencies]
    openusd = "0.8"
    
    [build-dependencies]
    openusd-build = "0.8"
    build.rs
    fn main() -> Result<(), openusd_build::Error> {
        openusd_build::configure()
            .search_path("schemas")
            .schema("schemas/usdGeom/schema.usda")
            .generate()
    }
    One Rust file per library:
    tokens SCHEMAS typed views token enums
  3. The include schema macro brings the generated code in under the library name that the schema declares, here usdGeom. Its schemas constant registers the library, and the stage opens with that registry. From there, a mesh is a typed view, and its points read straight into a vector.

    Slide text
    openusd v0.8.0
    • Schema codegen
    • Generated views
    • Schema families
    • Primvars
    • Faster traversal
    • Memory mapping
    • Also new
    • Upgrading

    Include the views, register them, read a mesh

    use openusd::{gf, usd};
    
    pub mod geom {
        openusd::include_schema!("usdGeom");
    }
    
    let registry = usd::SchemaRegistry::builder()
        .register(geom::SCHEMAS)
        .build()?;
    let stage = usd::Stage::builder()
        .schema_registry(registry)
        .open("scene.usda")?;
    
    let mesh = geom::Mesh::get(&stage, "/World/Mesh")?
        .expect("a Mesh");
    let points = mesh.points_attr()
        .get::<Vec<gf::Vec3f>>()?;
  4. The schemas crate is now generated this way, and its views replace the hand-written ones. That covers eleven families, from geometry to volumes. UsdSemantics is new, and labels prims by taxonomy. UsdGeom gains primvars, the visibility and motion APIs, and a transform cache.

    Slide text
    openusd v0.8.0
    • Schema codegen
    • Generated views
    • Schema families
    • Primvars
    • Faster traversal
    • Memory mapping
    • Also new
    • Upgrading

    All eleven standard schema families now come out of the generator

    UsdGeom UsdShade UsdSkel UsdLux UsdPhysics UsdRender UsdVol UsdMedia UsdUI UsdProc UsdSemanticsnew
    UsdGeom gains
    • Primvars
    • VisibilityAPI
    • MotionAPI
    • XformCache
  5. The new PrimvarsAPI creates, lists, blocks and removes a prim's primvars. Here, a heat primvar on a mesh stores two values. Four vertices share them through indices. get_at returns the two values as they were authored, and compute_flattened applies the indices, so you get one value for every vertex.

    Slide text
    openusd v0.8.0
    • Schema codegen
    • Generated views
    • Schema families
    • Primvars
    • Faster traversal
    • Memory mapping
    • Also new
    • Upgrading

    Read a primvar as authored, or flattened to one value per vertex

    let heat = geom::PrimvarsAPI::from_prim_unchecked(
            mesh.prim().clone())
        .primvar_builder("heat", "float[]")
        .interpolation(geom::Interpolation::Vertex)
        .set(sdf::Value::FloatVec(vec![20.0, 35.0]))
        .indices(vec![0, 1, 1, 0])
        .build()?;
    get_at · as authored
    20.0 35.0
    indices
    0 1 1 0
    compute_flattened · per vertex
    20.0 35.0 35.0 20.0
  6. Grey is before these changes and blue is after, on a generated scene of about 66 thousand prims. A warm traversal fell from about 510 milliseconds to about 110. The biggest single step came from a prim taking its status from its parent. A cold traversal takes more than 40 percent less time, and opening the scene takes about half the time.

    Slide text
    openusd v0.8.0
    • Schema codegen
    • Generated views
    • Schema families
    • Primvars
    • Faster traversal
    • Memory mapping
    • Also new
    • Upgrading

    A warm traversal is almost five times faster

    before after the changes in #142
    Warm traversal
    507 ms
    107 ms
    Cold traversal
    1,026 ms
    577 ms
    Open the scene
    37.8 ms
    20.5 ms

    Generated scene of 65,556 prims, release build, median of 7 runs. From pull request #142 by @bresilla.

  7. Memory mapping is new, and off by default. With it, peak heap drops by 39 percent on Moana with instance proxies, and by 42 percent on Caldera. Turn on the mmap feature and call map_files on the default resolver. It is marked unsafe because a mapped file must not change while anything read from it is still alive.

    Slide text
    openusd v0.8.0
    • Schema codegen
    • Generated views
    • Schema families
    • Primvars
    • Faster traversal
    • Memory mapping
    • Also new
    • Upgrading

    Opt in to memory-mapped files and peak heap drops by up to 42 percent

    SceneBeforeMappedChange
    Moana, default 135k prims3,3992,752−19%
    Moana, instance proxies 417k prims7,4524,526−39%
    Caldera, default 54k prims636371−42%

    Heap peak in MiB

    openusd = { version = "0.8", features = ["mmap"] }
    let resolver = unsafe { DefaultResolver::new().map_files() };
  8. A new utilities module ports dependency discovery, usdz packaging and asset path rewriting. A usdz package nested inside another package now reads. resolve_info reports which spec a value came from, and flatten writes a composed stage into one layer. And a damaged binary file returns an error instead of aborting.

    Slide text
    openusd v0.8.0
    • Schema codegen
    • Generated views
    • Schema families
    • Primvars
    • Faster traversal
    • Memory mapping
    • Also new
    • Upgrading

    Package, flatten and trace where a value came from

    usd_utils
    Find dependencies, package .usdz, rewrite asset paths
    nested .usdz
    A package inside a package now reads
    Attribute::resolve_info
    Which spec a value came from
    Stage::flatten
    A composed stage written into one layer
    .usdc reader
    Damaged files return errors instead of aborting
  9. If you use the schema views, two lines of the README example changed. Accessors now live on a trait named after the class, so PointBased becomes PointBasedSchema. And the stage opens through the builder with the schema registry, which is what makes a prim a mesh. Bump both crates to 0.8. They are still before 1.0, so the API can keep changing. So openusd 0.8 brings typed Rust code from any USD schema, and a faster stage.

    Slide text
    openusd v0.8.0
    • Schema codegen
    • Generated views
    • Schema families
    • Primvars
    • Faster traversal
    • Memory mapping
    • Also new
    • Upgrading

    The README example changes in two places from 0.7 to 0.8

    0.7
    use openusd_schemas::geom::{self, PointBased};
    let stage = usd::Stage::open("scene.usda")?;
    0.8
    use openusd_schemas::geom::{self, PointBasedSchema};
    let stage = usd::Stage::builder()
        .schema_registry(openusd_schemas::schema_registry())
        .open("scene.usda")?;
    toml
    openusd = "0.8"
    openusd-schemas = { version = "0.8", features = ["geom", "shade"] }

    github.com/mxpv/openusd/releases/tag/v0.8.0

What happened in AI today, in your inbox

One short email each evening. Read it and you’re caught up.

Built in New York with ❤️