Skip to content
NVIDIA-OmniversePublic

About

A C and Python library providing a shared, high-performance, vectorized, GPU-capable scene data substrate for OpenUSD data across Omniverse Libraries (physics, rendering, sensors, animation).

Topics

Resources

Security policy

Stars

19 stars

Watchers

6 watching

Forks

Repository files navigation

NVIDIA ovstage

Documentation License GitHub last commit Docs build

ovstage is a C and Python library providing a shared, high-performance, vectorized, GPU-capable scene data substrate for USD scene data for use in Omniverse Libraries spanning physics, rendering, sensors, animation, and more.

It provides C and Python APIs for reading, writing, querying, and managing simulation data such as transforms, velocities, materials, hierarchy, and metadata across CPU and GPU memory, with zero-copy data paths and DLPack tensor interchange. Currently, zero-copy applies to CUDA source-tensor writes; payload reads and map/unmap buffers are CPU-resident.

ovstage is suited for developers looking to build simulation and visualization applications, tools or workflows based on Omniverse Libraries, such as ovphysx and ovrtx.

No Omniverse background is needed to use ovstage. It is a standalone library that gives your application a fast, vectorized, in-memory representation of a scene to read and write from C or Python. You can author scene data directly, or load it from OpenUSD. Omniverse Libraries such as ovphysx and ovrtx are consumers that build on the same substrate, not prerequisites.

Note

ovstage is currently pre-release software. The API, runtime behavior, and packaging may change. Full API documentation is published at https://nvidia-omniverse.github.io/ovstage (see Documentation).

To get started with ovstage follow the instructions below for the included Python and C/C++ examples.

Sources live under examples/ and are the source of truth for the code snippets referenced by the ovstage skills — see the examples index.

High-level Feature Set

  • Population from USD:
    • Support loading of USD scene description to a vectorized runtime representation suitable for high performance simulation on CPU and GPU. Compatibility with OpenUSD allows interchange with a vast ecosystem of content creation, CAD and simulation tools.
    • Load from file or from USDA text description in memory
    • Add references to other USD files or to USDA text description in memory
    • Apply changes from timesampled data in USD to the entire stage
  • Export selected runtime state to USD:
    • Select candidates with the same predicate graphs as generic population, then author sparse OVER layers or durable DEF prims through explicit projection rules
    • Target one-shot saved files or reusable destinations that ovstage opens and owns, without exposing OpenUSD C++ types in the C ABI
    • Use synchronous export directly or the optional per-instance asynchronous scheduler
  • Efficient cloning of stage subtrees
  • Stage hierarchy lookups
  • Explicit queries for instanced data (prototype and instance roots)
  • Easy integration with Python simulation and AI ecosystem.

Packaging and Dependencies

ovstage is distributed as a C package (.zip file) available in the Releases page in this repo, and as Python wheels available via pypi.org. These packages include a number of pre-packaged dependencies.

In addition to these pre-packaged dependencies, on Windows, ovstage depends on Microsoft's VC runtime redistributable libraries, with a minimum version of 14.38 (as included in Visual Studio 2022 17.8). These libraries can be installed by an end user (using the linked Microsoft resources) or can be included by an application. The use of this version of the MSVC runtime libraries makes our binaries compatible with the vcruntime140.dll pre-packaged in Python distributions for Windows as old as Python 3.11. Older Python versions include older vcruntime140.dll, and are therefore not guaranteed to work.

On Linux, the packages are built against the manylinux_2_35 baseline: they require glibc 2.35 or newer (Ubuntu 22.04+, or an equivalent distribution). A few host libraries are deliberately not bundled and must be resolvable on the system:

  • Required to link a C/C++ application against the SDK, and to load it: libX11.so.6, libGL.so.1, and libgomp.so.1 (Debian/Ubuntu packages libx11-6, libgl1, libgomp1). These are transitive dependencies of the pre-packaged runtime, so the linker must be able to find them even though your code never references them directly.
  • Additionally used at runtime by optional components: libXt.so.6, libGLX.so.0, libOpenGL.so.0 (libxt6, libglx0, libopengl0), and the NVIDIA driver's libcuda.so.1 for the GPU-resident data paths.

Building must use the distribution's binutils (as, ld). On x86_64 hosts with Homebrew/Linuxbrew installed, note that Homebrew has linked its own binutils into PATH by default since June 2026: GCC locates as and ld through PATH, so it silently uses Homebrew's tools even when CMake selects the system compiler. Depending on the Homebrew and host glibc versions, the build then fails at compile time (as: ... version 'GLIBC_2.xx' not found) or at link time with unresolved X11/GL symbols or GLIBC_2.xx version errors reported against the pre-packaged runtime. If you hit this, either run brew unlink binutils, or remove the Homebrew bin directory from PATH for the build, or add -B/usr/bin to your compiler flags. The SDK's CMake package config detects a Homebrew linker and warns at configure time (pass -DOVSTAGE_SKIP_LINKER_CHECK=ON to CMake, or set that environment variable, to silence it).

System requirements

  • A CUDA-capable GPU is required to enable the use of GPU-resident data paths. CPU payload paths are also part of the API surface and do not require a GPU.
  • C/C++:
    • The ovstage library has a C11-compatible interface. It can be loaded dynamically or by statically linking to the ovstage-static loader library, which requires linking to the C++ stdlib (on Linux, the system libstdc++ — the loader's link interface names it by -lstdc++, so it is resolved by whichever linker runs the consumer link).
    • The example code requires a C++17 compiler and CMake 3.18+. The examples use cmake to fetch the prebuilt ovstage package from the GitHub.com release page.
    • On Linux, linking against the SDK additionally requires glibc 2.35+, a small set of system libraries, and the distribution's binutils linker — a Homebrew/Linuxbrew ld on PATH is not supported; see Packaging and Dependencies.
  • Python:
    • Python 3.10–3.13 versions are supported. On Windows, Python 3.11+ is recommended (Python 3.10 is not guaranteed to work because older distributions bundle an older vcruntime140.dll; see Packaging and Dependencies).
    • The examples use uv to resolve the ovstage wheel.

Getting Started in Python

ovstage Python wheels are distributed on PyPI.

The fastest way to get running with ovstage is the minimal Python example. It runs against the released ovstage wheel via uv:

cd examples/python/minimal
uv run main.py

Getting Started in C/C++

The minimal C/C++ example is the fastest way to get started with ovstage in a C/C++ environment. The example builds standalone with CMake and fetches the released ovstage package on first configure:

cd examples/c/minimal

# Linux
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
./build/minimal

On Windows, configure and build the same way (cmake -B build, then cmake --build build --config Release) and put the ovstage package bin/ directory on PATH before running — see examples/c/minimal/README.md.

Expected output:

attribute token <N> = 'temperature'
read back ordinal 1: 1.0 2.0 3.0

What is ovstage?

ovstage is an in-process runtime data store for post-composition USD scene data. It stores transforms, materials, hierarchy, metadata, and other attributes in a vectorized runtime format so producer and consumer libraries can exchange state through one shared substrate.

ovstage is the shared stage for the Omniverse library stack. Other Omniverse libraries using ovstage are ovphysx and ovrtx. Both libraries rely on ovstage to load USD data to the ovstage vectorized runtime representation. ovphysx then uses ovstage to write transforms or joint state updates into ovstage, while ovrtx can read the changed state from ovstage. Multiple libraries can share prim identities through the path dictionary. The application owns orchestration, supplies ordinals, advances the write floor, and decides when each consumer reads.

Shared Stage Deployment Models

The simplest model for integrating ovstage into an application is one where the application owns one ovstage instance and orchestrates the simulation loop. Physics, rendering, sensors, and application code exchange data through that shared stage.

graph TD
    subgraph "Single Process / Container"
        APP["Application<br/><i>owns ovstage + path dictionary<br/>+ simulation loop</i>"]

        APP --> |"steps ovphysx simulation"| OVPHYSX
        APP --> |"steps ovrtx sensor simulation"| OVRTX

        OVPHYSX["ovphysx<br/><i>Reads collision meshes, transforms<br/>Writes simulation results back</i>"]
        OVRTX["ovrtx<br/><i>Reads changed meshes, lights, cameras<br/>Shares path-dictionary handles. Renders sensor outputs</i>"]

        APP --> |"request loading of USD data"| OVSTAGE
        OVSTAGE -->|"reads scene data "| OVPHYSX
        OVPHYSX -->|"writes at simulation rate"| OVSTAGE
        OVSTAGE -->|"reads scene data + gets scene changes at render rate"| OVRTX

        OVSTAGE["ovstage<br/><i>Single instance · GPU-native<br/>Zero-copy · Change membership</i>"]
    end
Loading

This model is appropriate when producers and consumers are tightly coupled and benefit from shared memory, such as robotics simulation, reinforcement learning, or interactive visualization.

What functionality is available?

What you can do with it:

  • Share one runtime stage across Omniverse libraries - populate composed USD once, then let physics, rendering, sensors, and application code read or write through ovstage.
  • Use asynchronous, ordinal-keyed execution - mutating and data-producing calls enqueue work, return an op_index, and can expose typed handles that feed later enqueues.
  • Advance visibility explicitly - writes carry an ordinal, and callers advance the write floor so consumers read the latest committed stage state.
  • Read latest committed payloads - this release retains the current committed snapshot for payload reads; older ordinal payloads are not read back.
  • Track change membership - consumers can ask what changed since an ordinal and receive exact changed-prim membership within the reported retention frontier plus latest committed data, avoiding full-scene diffs (expressed as an ordinal-range read; see ovstage_ordinal_range_t). Callers query the frontier rather than assuming a fixed retention depth; older markers may be coalesced per attribute and prim, so this is not a historical-payload event log. Because only the latest payload is retained, a fixed range whose selected (attribute, path) changed again after the range end returns OVSTAGE_ERROR_OUT_OF_RANGE instead of membership and data — end the range at the newest change, or use a snapshot read, when that matters.
  • Keep tensor data CPU- or GPU-native - exchange payloads as DLPack DLTensor values for zero-copy CPU/GPU interop.
  • Map attributes for direct producer writes - use map/unmap flows when a producer wants to fill ovstage-owned storage directly.
  • Reuse prim identity across libraries - exchange tokens and prim-path lists through the shared path dictionary instead of string lookups at every boundary.
  • Use built-in USD metadata - usd-path, usd-schemas, usd-prim-type, usd-parent, and usd-children are auto-maintained and usable in filter predicates. (usd-active appears in the header contract but is not supported — it returns NOT_SUPPORTED — and is subject to removal in a future release.)
  • Export selected runtime state to USD - select candidates with shared population predicates, project them through explicit authoring rules, then use a one-shot saved file or an explicitly saved reusable destination that ovstage opens and owns.

Single Parse, Shared Runtime State

With ovstage, a USD scene can be populated once and shared by multiple runtime libraries. Each consumer reads only the data it needs, while producers can write results back into the same stage.

graph LR
    USD[USD File] --> OVPOP["ovstage population API<br/><i>one parser, one traversal</i>"]
    OVPOP --> OVSTAGE["ovstage<br/><i>Single GPU-resident copy<br/>Latest committed snapshot<br/>Change membership<br/>Shared path dictionary</i>"]
    OVSTAGE --> OVPHYSX["ovphysx<br/><i>reads scene state<br/>writes simulation results</i>"]
    OVPHYSX --> OVSTAGE
    OVSTAGE --> OVRTX["ovrtx<br/><i>reads changed render state</i>"]
Loading

Benefits:

  • Parse once - the population layer traverses the composed USD stage once, and runtime libraries consume the same ovstage data.
  • Reduce duplicate memory - scene attributes live in one shared runtime stage instead of N private library representations.
  • Avoid full-scene diffs - consumers ask for change membership since their last ordinal instead of diffing the full scene.
  • Run producers and consumers at different rates - a lagging consumer gets a larger change window on its next read rather than blocking the producer.
  • Keep data GPU-native - simulation output and render input can stay in DLPack/CUDA-compatible tensor form.
  • Share prim identity - libraries exchange tokens and prim-path lists through the path dictionary instead of resolving strings at every boundary.

Consumer Update Model

ovstage stores data and exposes changes; it does not push updates into consumers. Each runtime library owns when and how it reads:

  • Pull-based - consumers call read operations when they are ready for new data.
  • Delta-aware - consumers can ask "what changed since ordinal N?" and receive changed prim membership plus latest committed data, not an unconditional full scene snapshot.
  • Independent rates - producers can write at simulation cadence while renderers, sensors, or tools consume at their own cadence.
  • No global sync point - a write by one producer does not immediately force every consumer to update; consumers advance their own cursors.

Documentation

The full documentation site is available at https://nvidia-omniverse.github.io/ovstage:

Reference links in this source tree:

Building the Documentation

Prerequisites: uv and Doxygen (Linux only for Doxygen; see docs/README.md for Windows and further build details).

cd docs
make html

Then serve the output locally and open http://localhost:8000/:

uv run python -m http.server 8000 -d _build/html

Contributing

At this time this project is not open to external contributions.

Authors and acknowledgment

NVIDIA Corporation

License and security

The software and materials are governed by the NVIDIA Software License Agreement and the Product Specific Terms for NVIDIA AI Products.

This project will download and install additional third-party open source software projects. Review the license terms of these open source projects before use.

To report a security issue, see SECURITY.md. Do not report security vulnerabilities through GitHub/GitLab.

Support

Report documentation issues, installation problems, and runtime issues through the NVIDIA Omniverse developer forum.

Roadmap

ovstage is pre-release software: the API, runtime behavior, and packaging may change between releases. See Packaging and Dependencies for how ovstage is distributed today; the minimal C (CMake) and Python (uv) examples are the validated bring-up path (see Getting Started in Python and Getting Started in C/C++). A public roadmap will be shared as ovstage approaches general availability.

AI Coding Agents

If you are an AI coding agent, read AGENTS.md first.


Copyright (c) 2026 NVIDIA Corporation.

About

A C and Python library providing a shared, high-performance, vectorized, GPU-capable scene data substrate for OpenUSD data across Omniverse Libraries (physics, rendering, sensors, animation).

Topics

Resources

Security policy

Stars

19 stars

Watchers

6 watching

Forks

Releases

Packages

Contributors

Languages