Omniverse Kit 109 Migration Guide#

This guide details the steps and considerations necessary for migrating extensions and applications to Omniverse Kit 109. It incorporates essential updates, dependency changes and known issues, ensuring a clear and structured approach to facilitate upgrading from previous releases.

The Omniverse Kit 109 release includes the following updated dependencies:

Component

Reference

NumPy 2.3.1

Release Notes

CUDA 12.4.1

Release Notes

Table of Contents#

  1. Preparing for Migration

  2. Fabric Token and Path Changes

  3. NumPy 2.x Update

  4. CUDA Update

  5. Carbonite String Safety

  6. Other Breaking Changes

  7. Previous Release Migration Guides

Preparing for Migration#

Before starting any update process, please ensure that you have the following prerequisites in order to minimize disruptions during migration:

  • Ensure you have access to the latest Omniverse Kit SDK.

  • You are familiar with your application’s architecture, and existing dependencies.

  • You have a backup of your current project, to prevent any potential data loss during migration.

  • A process in place to confirm all functionalities of your application and extensions behave as expected after upgrading.

    • This process can vary depending on your organization, but typically includes elements such as automated builds, unit or integration tests, documentation about your feature’s behavior, reviewing log information, etc.

Carbonite#

  • carb::thread::shared_lock has been removed. std::shared_lock can be used as a drop-in replacement.

  • IDictionary::MakeAtPathS(...) function has been renamed to IDictionary::MakeAtPath, and returns an Item* of the dictionary entry just created.

  • carb::extras::compareStringsNoCase(a, b) has been removed. Now you can use the function carb::extras::caseInsensitiveCompare(a, b) if either a or b is a safe string type (i.e. one of the following types: std::string_view, std::string, carb::cpp::string_view, carb::cpp::zstring_view, omni::string, char[N], or a string literal). If both parameters are unsafe string types (char*s pointing to a null-terminated string), it must be replaced with the overload carb::extras::caseInsensitiveCompare(carb::cpp::unsafe_length, a, b).

  • ILogger interface has changed, Logger class has been replaced with Logger2 and the older functions have been deprecated. handleMaessage is now a virtual function instead of a pointer.

  • Potential fmt namespace conflict. Carbonite introduces carb/Format.h that adds a namespace carb::fmt, which in some cases (e.g. when using namespace carb; is used) can conflict with the fmt namespace provided by the fmt library. To fix this, disambiguate the function calls with the full namespace (either ::fmt or ::carb::fmt).

Fabric Token and Path Changes#

Overview#

Kit 109 introduces breaking changes to Fabric’s token and path handling system. The legacy fabric::TokenC, fabric::PathC, fabric::TokenId, and fabric::PathId types have been removed in favor of modernized fabric::Token and fabric::Path types that are trivially copyable and use weak references.

These changes were made to make storage of pxr::TfToken and pxr::SdfPath data in Fabric safe. Typically these pxr types are “incrementally refcounted” meaning that operator=, destructors, etc bookkeep the reference counts automatically. Fabric treats all attribute memory as trivially copyable for performance reasons, and memcpy-ing pxr::TfToken and pxr::SdfPath into trivially copyable memory is tricky to handle correctly, particularly when the underlying memory is shared between multiple Fabrics. The new fabric::Token and fabric::Path handles become invalid safely, and remain valid so long as the Fabrics they are registered to still exist. Make sure to register any handles stored in attribute to the appropriate Fabrics to ensure they validity.

Key Changes:

  • fabric::TokenC and fabric::PathC have been removed

  • fabric::TokenId and fabric::PathId have been removed

  • fabric::Token and fabric::Path are now the standard types for all use cases

  • fabric::Token and fabric::Path are weak references and trivially copyable (no reference counting on assignment)

  • Token and Path cannot be reinterpret-casted to TfToken and SdfPath as they represent different handles to different backing storages

  • Fabric attribute buffers of type BaseDataType::eToken and ePath are now fabric::Token and fabric::Path

Required Code Updates#

Token and Path Validity Checking#

The legacy kUninitializedToken and kUninitializedPath constants have been removed.

Checking Validity:

Each Token and Path can be checked for validity using one of the following methods:

  • Compare to default-initialized value

  • Call isNull() to check if never initialized

  • Call isExpired() to check if backing storage has been released. Prefer calling isNull() for performance unless this function is required. Calling getString() on an expired Path will no longer crash, so knowledge of expiration is generally less important.

  • Call validate() to check both (not null and not expired)

Note: validate() is a non-trivial operation (hence the name), and the validity state can change immediately after the function returns if another thread releases the backing storage.

Migration Examples:

Before (Kit 108)

After (Kit 109)

if (token == kUninitializedToken)

if (token.isNull())

if (path == kUninitializedPath)

if (path.isNull())

-

if (!token.validate()) // Check both null and expired

Creating Fabric Tokens and Paths#

To obtain fabric::Token or fabric::Path instances, you must use one of the following construction methods:

Direct Construction with FabricId:

omni::fabric::Token token(fabricId, "tokenString");
omni::fabric::Path path(fabricId, "/path/string");

Using StageReaderWriter:

// Register single token
Token token = stageReaderWriter->registerToken("tokenString");

// Register multiple tokens (batched - preferred for performance)
const char* tokenStrings[] = {"token1", "token2", "token3"};
Token tokens[3];
stageReaderWriter->registerTokens(
    carb::cpp::Span<const char*>(tokenStrings, 3),
    carb::cpp::Span<Token>(tokens, 3)
);

Using StageReaderWriterUsd (for TfToken/SdfPath):

// Register single TfToken
Token token = stageReaderWriterUsd->registerToken(tfToken);

// Register multiple TfTokens (batched - preferred for performance)
TfToken usdTokens[] = {TfToken("token1"), TfToken("token2")};
Token fabricTokens[2];
stageReaderWriterUsd->registerTokens(
    carb::cpp::Span<const PXR_NS::TfToken>(usdTokens, 2),
    carb::cpp::Span<Token>(fabricTokens, 2)
);

// Path works the same way
Path path = stageReaderWriterUsd->registerPath(sdfPath);

Creating Immortal Tokens (Use with Caution):

// Only use for tokens that:
// 1. Will always have the same string across multiple runs
// 2. Will not cause leaks if a scene is reloaded multiple times
Token immortalToken = omni::fabric::Token::createImmortal("constantToken");

Important Notes:

  • Tokens and paths are reference-counted with the fabrics they are registered with

  • The lifetime is at least as long as the specified fabric

Storing TfToken and SdfPath in Fabric#

When you need to store USD tokens and paths in Fabric attributes:

  1. Use StageReaderWriterUsd::registerToken or registerPath to convert from USD types.

  2. Store the resulting fabric::Token or fabric::Path in the Fabric attribute.

  3. Each lineage of fabrics (fabrics copied from each other) shares a cluster of Token and Path handles.

  4. The internal registry is only released when the last fabric in the lineage is destroyed.

Cross-Lineage Considerations:

  • When copying a Token or Path between fabrics that are not from the same lineage, you must register them again.

  • Without re-registration, handles remain safe but become invalid when the old lineage is destroyed.

  • There is currently no API to split a fabric from its lineage (by design).

Example:

// Convert and store TfToken in fabric
TfToken usdToken("myAttribute");
Token fabricToken = stageReaderWriterUsd->registerToken(usdToken);

// Store in fabric attribute
// (attribute of type BaseDataType::eToken now uses fabric::Token)
fabricAttribute.set(fabricToken);

Conversion Between USD and Fabric Types#

Prefer Batched Conversion:

All individual conversion functions internally call batched versions, so prefer batched APIs for performance:

// ✅ Good - Batched conversion
std::vector<TfToken> usdTokens = {...};
std::vector<Token> fabricTokens(usdTokens.size());
stageReaderWriterUsd->registerTokens(
    carb::cpp::Span<const TfToken>(usdTokens.data(), usdTokens.size()),
    carb::cpp::Span<Token>(fabricTokens.data(), fabricTokens.size())
);

// ❌ Less efficient - Individual conversions in loop
for (const auto& usdToken : usdTokens) {
    fabricTokens.push_back(stageReaderWriterUsd->registerToken(usdToken));
}

Conversion Function Guidelines:

  • Prefer using getSdfPath and getTfToken methods on StageReaderWriterUsd when available

  • Free-floating conversion functions without a fabric ID parameter create “immortal” tokens/paths stored in a global cache

  • Use methods on StageReaderWriter(Usd) or functions with a fabric ID parameter to limit lifetime to specific fabrics

StageReaderWriterUSD Direct Access#

StageReaderWriterUSD can now access primitives using SdfPath/TfToken directly without explicit conversion:

// Direct primitive lookup using SdfPath
auto prim = stageReaderWriterUsd->getPrimAtPath(sdfPath);

// Separate prim lookup table using SdfPath is maintained

This provides more convenient access patterns when working primarily with USD types.

Migration Checklist#

  • [ ] Replace kUninitializedToken and kUninitializedPath with .isNull() or .validate() checks

  • [ ] Update code that creates tokens/paths to use registration methods

  • [ ] Replace TokenC and PathC types with Token and Path

  • [ ] Replace TokenId and PathId types with Token and Path

  • [ ] Remove any reinterpret_cast between Token/TfToken or Path/SdfPath

  • [ ] Update attribute buffer code expecting old token/path types

  • [ ] Prefer batched conversion functions over individual conversions

  • [ ] Review cross-fabric token/path usage and add re-registration where needed

  • [ ] Test fabric attribute operations involving tokens and paths

  • [ ] Rebuild all C++ extensions (ABI breaking change)

NumPy 2.x Update#

Kit 109 upgrades from NumPy 1.x to NumPy 2.3.1. This update enables seamless integration with Isaac-Newton and modern robotics frameworks, and brings improved performance and new capabilities from the NumPy 2.x series.

Breaking Changes#

NumPy 2.0 introduced several breaking changes that may affect your extensions:

1. API Changes

  • Some deprecated NumPy 1.x APIs have been removed

  • Type promotion rules have changed

  • C API functions may have different signatures

2. Data Type Changes

  • Default integer size on Windows changed from int32 to int64

  • String dtype handling has changed

  • Datetime64 behavior updates

3. C API Updates

Migration Steps#

  1. Review NumPy Usage: Audit your code for deprecated NumPy 1.x APIs

  2. Update Type Assumptions: Check code that assumes specific integer sizes or types

  3. Test Thoroughly: NumPy type changes can cause subtle bugs

  4. Update Dependencies: Ensure other Python packages are NumPy 2.x compatible

For detailed migration guidance, see the NumPy 2.0 Migration Guide.

CUDA Update#

Kit 109 updates CUDA SDK to version 12.4.1, providing the latest GPU compute capabilities and optimizations.

Driver Requirements#

This update requires minimum driver versions:

  • Linux: >= 550.54.15

  • Windows: >= 551.78

Action Required: Verify your driver versions before upgrading to Kit 109. Applications will fail to start with older drivers.

Compatibility Notes#

  • Extensions using CUDA APIs should be rebuilt against CUDA 12.4.1

  • Review CUDA 12.4 release notes for API changes if you use CUDA directly

  • RTX rendering improvements leverage new CUDA 12.4 features

Carbonite String Safety#

Kit 109 introduces comprehensive string safety upgrades throughout Carbonite and Kit, replacing unsafe string functions with secure alternatives.

Interface Changes#

New safe-only interfaces have been introduced for core Carbonite systems:

  • ISettings - Settings dictionary interface

  • IError - Error handling interface

  • IDictionary - Dictionary interface

Migration Required: Code using legacy string-based methods on these interfaces may need updates to use string_view-based alternatives.

String View Migration#

Many Carbonite APIs now accept carb::cpp::string_view instead of const char*:

// ✅ New - String view based (preferred)
settings->setString("/my/setting", carb::cpp::string_view("value"));

// Still supported but prefer string_view where available
settings->setString("/my/setting", "value");

Benefits:

  • Eliminates buffer overflow vulnerabilities

  • Improves performance by avoiding unnecessary string copies

  • Enables safer string handling across API boundaries

Action Items#

  1. Review Compiler Warnings: New string safety checks may produce warnings

  2. Update String Handling: Migrate to string_view where recommended

  3. Test String Operations: Ensure string handling behaves correctly with new interfaces

Other Breaking Changes#

Fabric Scene Delegate Default Enabled#

Fabric Scene Delegate (FSD) is now enabled by default in Kit 109. Applications no longer need to explicitly enable FSD in .kit configuration files.

Action Required: If your application explicitly disables FSD, test compatibility and performance with FSD enabled.

Memory Allocator Change (Windows)#

Kit on Windows now uses mimalloc as the default memory allocator. For compatibility scenarios, the system allocator remains available via kit-sysalloc.exe.

Impact: Most applications will see improved memory performance. If you experience memory-related issues, the system allocator fallback is available for troubleshooting.

Previous Release Migration Guides#

For migration from earlier Kit versions: