0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用USDRT优化NVIDIA Omniverse的动态数据更改功能

丽台科技 来源:丽台科技 2026-01-12 09:11 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

在 NVIDIA Omniverse 开发中,此前我们已探讨了常见的性能瓶颈、如何使用 Tracy 等工具进行问题定位,并初步介绍了 FSD(Fabric Scene Delegation)与 USDRT 等核心概念。本期文章将聚焦USDRT,通过具体的代码与示例,深入解读如何利用它来高效优化场景中的动态数据更改。

1. USDRT 基本概念

USD:Pixar 开发提供的原生 API 库,有 C++Python 两种接口,提供对 USD 场景的操作访问,其特点是写入速度慢。

Fabric:由 Omniverse 编写提供的库组件,只有 C++ 一种接口,为底层 USD 的写入提供支持,可理解为一个大的场景缓存。

- Fabric Stage(常称作 Fabric Flat Cache)是 Omniverse 中的一种高性能场景视图。它将 USD 合成后的场景数据“展平”并缓存至 Fabric 内部,旨在为物理、渲染、OmniGraph 等下游系统提供高性能的数据访问。与每次直接遍历 USD Stage 不同,该系统以列式数据、批处理等 GPU 友好的方式组织数据,从而大幅提升访问效率。

- Fabric 解决了传统 USD 开发中的常见性能瓶颈:以往,系统如需查找特定类型的 Prim(例如所有 Cube),必须每次遍历整个 USD Stage,并需手动维护数据缓存及监听 USD Notice 以实现同步更新,这种做法的实现复杂度高且效率有限。而 Fabric Stage 能够自动为多个扩展或系统集中维护这份场景缓存与对应的“脏标记”状态;其内部会按 Tag、属性、类型等标准将 Prim 自动分桶组织,并直接提供 findPrims 等查询接口,以及可将属性数组批量拉取至 CPU 或 GPU 内存的高性能 API(具体用法将在后文详述)。

USDRT(USD Runtime):Omniverse 提供的一套高性能运行时库。其 Python API 设计与原生 USD(pxr)基本同名,但底层通过 Fabric 组件对USD文件进行读写(代码中通过usdrt.前缀调用)。为解决 Fabric 仅有 C++ 接口的问题,USDRT 专门提供了兼容 USD 的 Python 封装层,便于开发者学习和使用。

0d47ebae-ec41-11f0-92de-92fbcf53809c.png

Persistent Data:会被永久写入 USD 文件的数据。

Transient Data:运行时生成的临时数据(如仿真结果、动画效果),不会影响底层 USD 文件。

Composition:按照 USD 规则(如继承、覆盖)将多个 Layer 合成的机制。

Pre-Composition:合成发生前,多个 Layer 可分别读取状态属性的状态。

After-Composition:合成完成后,所有 Layer 统一融合成一个总 Layer 的最终场景状态。

USDRT 诞生的根本动因,是解决原生 USD 数据写入速度慢的性能瓶颈。为此,Omniverse 团队开发了 USDRT / Fabric 组件,其核心机制是处理场景中 Prim 的属性变化数据,将这些更新暂存于内存或显存中,而非直接、即时地写入底层 USD 文件。当然,系统也提供了相应的 API,可在必要时将最终数据写回 USD。这一过程可通过下图直观理解,并且也提供了快速查询和访问的API接口。

0e68b590-ec41-11f0-92de-92fbcf53809c.png

正如在《Omniverse 性能优化系列(一):Tracy Profiler》中所讨论的,渲染器(Renderer)必须等待 USD 写入操作完成,这正是造成卡顿的根本原因。下文将结合具体实例,并再次使用 Tracy 来观察和验证此优化带来的实际效果。

2. USDRT 实践

2.1“Hello World” in USDRT

上文提到原生的 PXR USD 的 API 和 USDRT 大体相同,不同在于 USD / USDR 的 Stage 获取方式。

USDRT 通过以下方式获取当前已打开的 Stage:

stage= usdrt.Usd.Stage.Attach(omni.usd.get_context().get_stage_id())

原生 USD 则通过以下方式获取 Stage:

stage= omni.usd.get_context().get_stage()

下面为一个 USDRT 的 Hello World,可以用 Omniverse 自带的 Script Editor 打开并运行:

https://github.com/slayersong/OV_Perf_tutorial/blob/main/2.USDRT/usdrt_helloworld.py(复制链接至浏览器打开,下同)

importusdrt
stage = usdrt.Usd.Stage.Attach(omni.usd.get_context().get_stage_id())
print(f"USD RT Hello world, USDRT Stage{stage}")

也可通过其他打开 USD 文件的方式或参考 Stage 相关信息,具体可查看下面的代码和文档:

https://docs.omniverse.nvidia.com/kit/docs/usdrt/latest/docs/scenegraph_use.html#stages

Importusdrt
stage=usdrt.Usd.Stage.Open(DATA_DIR +"/cornell.usda")

2.2 USDRT 示例讲解

2.2.1 修改 Prim 的 attribute

首先通过一个简单的例子来说明:USDRT 只是影响了场景的 Runtime,并不实际修改 USD 文件本身。

下载并用 Omniverse 打开“cornell.usda”这个场景。附场景文件下载地址:

https://github.com/slayersong/OV_Perf_tutorial/tree/main/usd/tests

通过“/Cornell_Box/Root/Cornell_Box1_LP/White_Wall_Back" 查看这个 Prim Path 下面的primvars:displayColor,属性值是 [0.5,0.5,0.5]。

0ec8e2bc-ec41-11f0-92de-92fbcf53809c.png

打开 Script Editor 并运行下面的代码,作用在于将颜色修改成(1,0,0):

https://github.com/slayersong/OV_Perf_tutorial/blob/main/2.USDRT/usdrt_color.py

fromusdrtimportGf, Sdf, Usd, UsdGeom, Vt
importomni
stage = Usd.Stage.Attach(omni.usd.get_context().get_stage_id())
path ="/Cornell_Box/Root/Cornell_Box1_LP/White_Wall_Back"
prim = stage.GetPrimAtPath(path)
attr = prim.GetAttribute(UsdGeom.Tokens.primvarsDisplayColor)
# Get the value of displayColor on White_Wall_Back,
# which is mid-gray on this stage
result = attr.Get()
print(f"before set color is{result}")
attr.Set(Vt.Vec3fArray([Gf.Vec3f(1,0,0)]))
result = attr.Get()
print(f"after set color is{result}")

此时会发现墙体的颜色变红了,但查看 Omniverse 的 Stage 面板中primvars:displayColor这个 attribute 实际数值并未更改,左上角的场景文件也没有变动信息 (usd 文件如有改变会有“*”)。

0f2890ea-ec41-11f0-92de-92fbcf53809c.png0f8a41be-ec41-11f0-92de-92fbcf53809c.png

通过上图对比可知,USDRT 的修改仅影响运行时的场景表现。若需将这些更改存储至最终的 USD 文件,必须调用以下 API:

stage.WriteToStage()

*注:此 API 在某些特定 GPU 下可能导致进程无响应。

2.2.2 USDRT 快速查询场景 Prim

按照传统的 USD API 的做法,查询场景中某一类的 Prim(比如 Mesh)需要对整个 Stage 进行遍历,代码如下:

mesh_prims=[]
forprim in stage.Traverse():
 ifprim.IsA(UsdGeom.Mesh):
   mesh_prims.append(prim)

通过分析可知,原生遍历方法的时间复杂度为 O(n)。在大型场景中,若要搜索某一特定 Prim,其耗时将更为显著。针对此性能瓶颈,Fabric / USDRT 为 Prim 的快速查询提供了以下 3 个 API:

UsdStage::GetPrimsWithTypeName(TfToken typeName)

UsdStage::GetPrimsWithAppliedAPIName(TfToken apiName)

GetPrimsWithTypeAndAppliedAPIName(TfToken typeName, TfTokenVector apiNames)

针对上述 API,下面对 Omniverse USD 中的基本概念进行梳理:

Type:在 Omniverse 的 Viewport 中单击鼠标右键,点击“Create”所列出来的选项就是 Type,例如 Shape 中的 Capsule、Cone、Cube 等(Shape 中都是单独的一个 Type 类型,Camera、Curves 等)。创建后在 Stage 的面板中也可看到基本的 Type 类型。

0fe9375a-ec41-11f0-92de-92fbcf53809c.png10476a82-ec41-11f0-92de-92fbcf53809c.png

AppliedAPI:要理解 Applied API,需先了解 USD 中的API Schema(https://openusd.org/release/glossary.html#api-schema)。它指的是一组可通过特定接口动态应用到 Prim 上、为其增添功能的属性集合。例如,可以为一个 Mesh Prim 增加刚体碰撞的属性,或绑定材质的属性。

bindingAPI = UsdShade.MaterialBindingAPI.Apply(prim)
bindingAPI.Bind(materialPrim)

USD 中有多少种 API Schema 类型?我们可以在 Omniverse 的 Script Editor 中运行以下代码,查询所有已注册的 API Schema。其中主要包含物理、灯光、UsdGeomModelAPI 等相关类型:

https://github.com/slayersong/OV_Perf_tutorial/blob/main/2.USDRT/APISchema_query.py

frompxrimportUsd, Tf
schema_reg = Usd.SchemaRegistry()
single_apply_list = []
multiple_apply_list = []
fortinTf.Type.FindByName("UsdAPISchemaBase").GetAllDerivedTypes():
  ifnot (schema_reg.IsAppliedAPISchema(t)orschema_reg.IsMultipleApplyAPISchema(t)):    
   continue
 
 if(schema_reg.IsAppliedAPISchema(t) andnot schema_reg.IsMultipleApplyAPISchema(t)):
    single_apply_list.append(str(t).split("'")[1::2][0])
   
 if(schema_reg.IsMultipleApplyAPISchema(t)):
    multiple_apply_list.append(str(t).split("'")[1::2][0])
# Sort and print
print("Single-apply API Schemas:")
forx insorted(single_apply_list):
 print(x)
print("
Multi-apply API Schemas:")
forx insorted(multiple_apply_list):
 print(x)

回到最初的问题:在场景中查找具有特定类型(Type)的 Prim。使用原生 USD API 只能通过遍历整个 Stage 实现,其时间复杂度为O(n)。而通过 USDRT 提供的 API,时间复杂度可降至O(1)

可以使用以下三个 API 进行快速查询:

meshPaths = stage.GetPrimsWithTypeName("Mesh")

shapingPaths = stage.GetPrimsWithAppliedAPIName("ShapingAPI")

paths = stage.GetPrimsWithAppliedAPIName("CollectionAPI:lightLink")

完整的代码链接:

https://github.com/slayersong/OV_Perf_tutorial/blob/main/2.USDRT/APISchema_query.py

2.3 USDRT vs USD 性能测试

下面通过一个实际案例,来对比动态修改 USD Prim 属性时的性能差异。我们将遍历场景中所有 Mesh 并修改其颜色属性,分别使用原生 USD API 与 USDRT 实现,并记录运行时间。

2.3.1 准备测试场景

首先下载一个复杂度较高的场景资产用于测试:

访问网站下载 Kitchen Set 资产:

https://developer.nvidia.com/usd

解压后,在 Omniverse 中打开Kitchen_set.usd文件。

10a9e4e6-ec41-11f0-92de-92fbcf53809c.png

2.3.2 使用原生 USD API 修改颜色

在 Script Editor 当中运行以下代码:

https://github.com/slayersong/OV_Perf_tutorial/blob/main/2.USDRT/usd.py

frompxrimportGf, Sdf, Usd, UsdGeom, Vt
importomni
importtime
importcarb
stage = omni.usd.get_context().get_stage()
color = Vt.Vec3fArray([Gf.Vec3f(0.8,0.2,0)])
# 遍历所有 prim,筛选出 Mesh
mesh_prims = []
forpriminstage.Traverse():
 ifprim.IsA(UsdGeom.Mesh):
    mesh_prims.append(prim)
t0 = time.perf_counter()
forpriminmesh_prims:
 ifprim.HasAttribute(UsdGeom.Tokens.primvarsDisplayColor):
   #carb.log_info(f"The prim is {prim}")
    prim.GetAttribute(UsdGeom.Tokens.primvarsDisplayColor).Set(color)
t1 = time.perf_counter()
elapsed_ms = (t1 - t0) *1000.0
carb.log_warn(f"[Native USD] Painted{len(mesh_prims)}meshes in{elapsed_ms:.2f}ms")

测试结果:

在配置了 NVIDIA RTX 5880 Ada 的测试机上,首次运行耗时约1337 ms(具体时间因配置而异)。

此时可观察到原生 USD 文件已被修改。

注意:如果不更改颜色值再次运行同一段代码,耗时将降至约14 ms。如果改变颜色值,时间又会如何?建议动手尝试并思考背后的原因。

11073c22-ec41-11f0-92de-92fbcf53809c.png

回顾此前在《Omniverse 性能优化系列(一):Tracy Profiler》中的分析:抓取最大渲染运行时间,通过函数的 callstack 可以发现,渲染线程在等待 USD 文件写入完成。这意味着 USD 的写入操作很可能是单线程串行的。USD 数据变更后,会通过 Observer 设计模式逐一通知相关模块,整个过程是同步的。

1186c816-ec41-11f0-92de-92fbcf53809c.png

2.3.3 使用 USDRT 修改颜色

接下来,使用 USDRT 实现相同的颜色修改操作:

https://github.com/slayersong/OV_Perf_tutorial/blob/main/2.USDRT/usdrt.py

fromusdrtimportGf, Sdf, Usd, UsdGeom, Vt
importomni
importtime
importusdrt
importcarb


stage = usdrt.Usd.Stage.Attach(omni.usd.get_context().get_stage_id())


color = Vt.Vec3fArray([Gf.Vec3f(0.2,0.2,0)])
meshPaths = stage.GetPrimsWithTypeName("Mesh")
t0 = time.perf_counter()
formeshPathinmeshPaths:
  prim = stage.GetPrimAtPath(meshPath)
 ifprim.HasAttribute(UsdGeom.Tokens.primvarsDisplayColor):
   #carb.log_info(f"The prim is {prim}")
    prim.GetAttribute(UsdGeom.Tokens.primvarsDisplayColor).Set(color)
t1 = time.perf_counter()
elapsed_ms = (t1 - t0) *1000.0


carb.log_warn(f"[USDRT] Painted{len(meshPaths)}meshes in{elapsed_ms:.2f}ms")

测试结果:

相同操作耗时仅约148 ms,相比原生 USD 提升了近一个数量级。

观察左上角可发现,原生 USD 文件本身并未被修改。

*延伸尝试:根据上文提到的 API 把 traverse stage 变成 USDRT 提供的快速查询 API 并对比时间差异。

11fa027c-ec41-11f0-92de-92fbcf53809c.png130b85be-ec41-11f0-92de-92fbcf53809c.jpg

2.4 USDRT 中的 Change Tracking 机制

在 USD 体系中,TfNotice(Tool Foundation Notice)是核心的消息处理机制,它实现了 “场景 / 资源变化 → 自动推送事件” 的监听模式,可应用于监视 Stage 编辑、属性更新、层加载、实时协作以及视口刷新等场景。

USD 原生的 TfNotice 为监听器(Listener)提供了一种回调机制。然而,USDRT 提供了一套完全不同的变化跟踪范式。它提供了一系列主动查询函数,让开发者能够精确跟踪所关心的属性变化。用户需要在合适的频率主动查询并处理这些更改(相当于需要自行封装类似回调的逻辑)。

这一设计变革源于一个关键的性能考量:USD 原生的通知 (Notify) 机制是完全同步的。当发送者 (Sender) 的线程触发通知时,它会阻塞(Block)并等待所有已注册的监听器按顺序处理完毕。这意味着发送者线程的性能严重依赖于最慢的那个监听器。

为了避免这种阻塞,USDRT 采用了非阻塞的主动查询机制。下表概述了其核心 API:

1366c35c-ec41-11f0-92de-92fbcf53809c.jpg

实践示例:

结合前文提及的注册 Omniverse 的消息函数“Subscribe to Update Event”去跟踪更改:

https://docs.omniverse.nvidia.com/dev-guide/latest/programmer_ref/events.html#subscribe-to-update-events

还是打开之前的“cornell.usda”这个场景,然后跟踪某些 Prim 的颜色属性,利用 AI 辅助,我们编写了一个 RTTrackingTester,它会在异步更新循环中追踪更改并定期清空记录。完整代码如下:

https://github.com/slayersong/OV_Perf_tutorial/blob/main/2.USDRT/RTTrackingTester.py

3. 总结

总的来说,USDRT / Fabric 的本质是对 USD 文件写入的缓存层。其中,Fabric 提供底层 C++ 接口,USDRT 则负责提供与原生 USD API 一致的友好封装,旨在实现代码风格与调用的统一,让开发者以熟悉的方式获得显著的性能提升。

13c67a5e-ec41-11f0-92de-92fbcf53809c.png

文案提供 & 技术支持:

宋毅明 NVIDIA Omniverse & OpenUSD 开发者关系经理

*与 NVIDIA 产品相关的图片或视频(完整或部分)的版权均归 NVIDIA Corporation 所有。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • NVIDIA
    +关注

    关注

    14

    文章

    5783

    浏览量

    110524
  • 代码
    +关注

    关注

    30

    文章

    4986

    浏览量

    74655
  • python
    +关注

    关注

    60

    文章

    4896

    浏览量

    90594
  • 开发者
    +关注

    关注

    1

    文章

    811

    浏览量

    18132

原文标题:Omniverse 性能优化系列(二):USDRT 的使用

文章出处:【微信号:Leadtek,微信公众号:丽台科技】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    NVIDIA Omniverse Extension开发秘籍

    NVIDIA Omniverse 是一个模块化平台,使用高级 API 和微服务来构建由 OpenUSD 和 NVIDIA RTX 提供支持的 3D 应用。OpenUSD 功能强大的 3
    的头像 发表于 08-22 15:52 4004次阅读
    <b class='flag-5'>NVIDIA</b> <b class='flag-5'>Omniverse</b> Extension开发秘籍

    NVIDIA Omniverse Create最新版功能介绍

      NVIDIA Omniverse Create 2021.3 现已在中提供公开测试版,为 Omniverse 艺术家、设计师、开发人员和工程师提供一组新功能,以增强图形和内容创建工
    的头像 发表于 04-07 15:34 2566次阅读

    NVIDIA Omniverse的特性及应用

    NVIDIA Omniverse 基于 Pixar 的 Universal Scene Description 和 NVIDIA RTX技术打造,是一款可扩展的多 GPU 实时推理开发平台,用于实现 3D 模拟和设计协作。
    的头像 发表于 06-15 09:53 2638次阅读

    借助NVIDIA Omniverse Replicator功能加快AI培训

    Omniverse Replicator 是一个构建在 NVIDIA Omniverse 平台上的高度可扩展框架,它支持物理精确的 3D 合成数据生成 ,以加快感知网络的训练和准确性。
    的头像 发表于 10-11 14:54 1893次阅读
    借助<b class='flag-5'>NVIDIA</b> <b class='flag-5'>Omniverse</b> Replicator<b class='flag-5'>功能</b>加快AI培训

    Omniverse 中文课程系列 1: 开发 Extensions 来自定义 Omniverse 功能与 UI

    Omniverse 中 定制化开发和发布 Extensions 学习目标 想要根据自己的喜好更改 Omniverse功能和用户界面(UI)吗?使用 Python 代码通过扩展
    的头像 发表于 05-27 17:45 1817次阅读
    <b class='flag-5'>Omniverse</b> 中文课程系列 1: 开发 Extensions 来自定义 <b class='flag-5'>Omniverse</b> <b class='flag-5'>功能</b>与 UI

    Omniverse 资讯速递 | 行业动态、应用案例、创作者故事、教程与资源等你来解锁!

    Omniverse 行业动态 U 设计周 × NVIDIA Omniverse 元宇宙设计大赛获奖名单 U 设计周 × NVIDIA
    的头像 发表于 06-07 08:50 1685次阅读
    <b class='flag-5'>Omniverse</b> 资讯速递 | 行业<b class='flag-5'>动态</b>、应用案例、创作者故事、教程与资源等你来解锁!

    探索NVIDIA AI和Omniverse加速设计创作

    面对这个时代的发展机遇,NVIDIA 带来的是 Omniverse 平台。NVIDIA Omniverse 是一个启用多 GPU 的开放式云原生平台,由
    的头像 发表于 06-13 15:40 1441次阅读

    Omniverse 资讯速递 | 行业动态、近期发布在线听、最新更新、中文课程系列等你来解锁!

    Omniverse  中文课程系列 Omniverse 行业动态 NVIDIA Omniverse 闪耀 U 设计周:为创作者插上“AI
    的头像 发表于 06-19 18:55 1610次阅读

    Omniverse 资讯速递 | 行业动态、应用案例、创作者故事等你来解锁!

    在本期 「Omniverse 资讯速递」 中,Omniverse 用户和开发者将了解到: Omniverse 行业动态 Omniverse
    的头像 发表于 07-17 19:55 1428次阅读

    Omniverse 资讯速递 | 行业动态、应用案例、合成数据生成系列视频等你来解锁!

    在本期 「Omniverse 资讯速递」 中,Omniverse 用户和开发者将了解到: Omniverse 行业动态 Omniverse
    的头像 发表于 08-07 19:15 1940次阅读

    Omniverse Connectors功能及区别简析

    NVIDIA Omniverse™ Connect 允许您使用常用的应用程序作为 NVIDIA Omniverse™ 平台的内容交付工具。
    的头像 发表于 01-05 09:23 1803次阅读
    <b class='flag-5'>Omniverse</b> Connectors<b class='flag-5'>功能</b>及区别简析

    NVIDIA Omniverse中的物理模拟功能

    NVIDIA Omniverse™ Simulation 作为 NVIDIA Omniverse™ 平台的关键组件之一,由 NVIDIA
    的头像 发表于 03-08 11:30 2935次阅读
    <b class='flag-5'>NVIDIA</b> <b class='flag-5'>Omniverse</b>中的物理模拟<b class='flag-5'>功能</b>

    NVIDIA Omniverse USD Composer能用来做什么?如何获取呢?

    NVIDIA Omniverse™ USD Composer(以前称为 Create)是 NVIDIA Omniverse™ 中用于构建虚拟世界的参考应用程序,允许用户进行组装、模拟和
    的头像 发表于 05-20 10:07 2482次阅读
    <b class='flag-5'>NVIDIA</b> <b class='flag-5'>Omniverse</b> USD Composer能用来做什么?如何获取呢?

    Omniverse教程(12):NVIDIA Omniverse USD Presenter的基础应用

    如前所述,NVIDIA Omniverse™ 是一个参考开发平台,通过模块化的开发框架能够轻松扩展和自定义。
    的头像 发表于 05-20 10:09 1994次阅读
    <b class='flag-5'>Omniverse</b>教程(12):<b class='flag-5'>NVIDIA</b> <b class='flag-5'>Omniverse</b> USD Presenter的基础应用

    NVIDIA Omniverse基于Container的部署推流方案

    为了让客户能够高效安装和部署 NVIDIA OmniverseNVIDIA Isaac 平台,NVIDIA 现已推出简单便捷的容器化部署方案,以支持在
    的头像 发表于 12-17 10:17 1111次阅读
    <b class='flag-5'>NVIDIA</b> <b class='flag-5'>Omniverse</b>基于Container的部署推流方案