在 NVIDIA Omniverse 開發(fā)中,此前我們已探討了常見的性能瓶頸、如何使用 Tracy 等工具進行問題定位,并初步介紹了 FSD(Fabric Scene Delegation)與 USDRT 等核心概念。本期文章將聚焦USDRT,通過具體的代碼與示例,深入解讀如何利用它來高效優(yōu)化場景中的動態(tài)數(shù)據(jù)更改。
1. USDRT 基本概念
USD:由 Pixar 開發(fā)提供的原生 API 庫,有 C++ 和 Python 兩種接口,提供對 USD 場景的操作訪問,其特點是寫入速度慢。
Fabric:由 Omniverse 編寫提供的庫組件,只有 C++ 一種接口,為底層 USD 的寫入提供支持,可理解為一個大的場景緩存。
- Fabric Stage(常稱作 Fabric Flat Cache)是 Omniverse 中的一種高性能場景視圖。它將 USD 合成后的場景數(shù)據(jù)“展平”并緩存至 Fabric 內(nèi)部,旨在為物理、渲染、OmniGraph 等下游系統(tǒng)提供高性能的數(shù)據(jù)訪問。與每次直接遍歷 USD Stage 不同,該系統(tǒng)以列式數(shù)據(jù)、批處理等 GPU 友好的方式組織數(shù)據(jù),從而大幅提升訪問效率。
- Fabric 解決了傳統(tǒng) USD 開發(fā)中的常見性能瓶頸:以往,系統(tǒng)如需查找特定類型的 Prim(例如所有 Cube),必須每次遍歷整個 USD Stage,并需手動維護數(shù)據(jù)緩存及監(jiān)聽 USD Notice 以實現(xiàn)同步更新,這種做法的實現(xiàn)復雜度高且效率有限。而 Fabric Stage 能夠自動為多個擴展或系統(tǒng)集中維護這份場景緩存與對應的“臟標記”狀態(tài);其內(nèi)部會按 Tag、屬性、類型等標準將 Prim 自動分桶組織,并直接提供 findPrims 等查詢接口,以及可將屬性數(shù)組批量拉取至 CPU 或 GPU 內(nèi)存的高性能 API(具體用法將在后文詳述)。
USDRT(USD Runtime):Omniverse 提供的一套高性能運行時庫。其 Python API 設計與原生 USD(pxr)基本同名,但底層通過 Fabric 組件對USD文件進行讀寫(代碼中通過usdrt.前綴調(diào)用)。為解決 Fabric 僅有 C++ 接口的問題,USDRT 專門提供了兼容 USD 的 Python 封裝層,便于開發(fā)者學習和使用。

Persistent Data:會被永久寫入 USD 文件的數(shù)據(jù)。
Transient Data:運行時生成的臨時數(shù)據(jù)(如仿真結(jié)果、動畫效果),不會影響底層 USD 文件。
Composition:按照 USD 規(guī)則(如繼承、覆蓋)將多個 Layer 合成的機制。
Pre-Composition:合成發(fā)生前,多個 Layer 可分別讀取狀態(tài)屬性的狀態(tài)。
After-Composition:合成完成后,所有 Layer 統(tǒng)一融合成一個總 Layer 的最終場景狀態(tài)。
USDRT 誕生的根本動因,是解決原生 USD 數(shù)據(jù)寫入速度慢的性能瓶頸。為此,Omniverse 團隊開發(fā)了 USDRT / Fabric 組件,其核心機制是處理場景中 Prim 的屬性變化數(shù)據(jù),將這些更新暫存于內(nèi)存或顯存中,而非直接、即時地寫入底層 USD 文件。當然,系統(tǒng)也提供了相應的 API,可在必要時將最終數(shù)據(jù)寫回 USD。這一過程可通過下圖直觀理解,并且也提供了快速查詢和訪問的API接口。

正如在《Omniverse 性能優(yōu)化系列(一):Tracy Profiler》中所討論的,渲染器(Renderer)必須等待 USD 寫入操作完成,這正是造成卡頓的根本原因。下文將結(jié)合具體實例,并再次使用 Tracy 來觀察和驗證此優(yōu)化帶來的實際效果。
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]。

打開 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}")
此時會發(fā)現(xiàn)墻體的顏色變紅了,但查看 Omniverse 的 Stage 面板中primvars:displayColor這個 attribute 實際數(shù)值并未更改,左上角的場景文件也沒有變動信息 (usd 文件如有改變會有“*”)。


通過上圖對比可知,USDRT 的修改僅影響運行時的場景表現(xiàn)。若需將這些更改存儲至最終的 USD 文件,必須調(diào)用以下 API:
stage.WriteToStage()
*注:此 API 在某些特定 GPU 下可能導致進程無響應。
2.2.2 USDRT 快速查詢場景 Prim
按照傳統(tǒng)的 USD API 的做法,查詢場景中某一類的 Prim(比如 Mesh)需要對整個 Stage 進行遍歷,代碼如下:
mesh_prims=[] forprim in stage.Traverse(): ifprim.IsA(UsdGeom.Mesh): mesh_prims.append(prim)
通過分析可知,原生遍歷方法的時間復雜度為 O(n)。在大型場景中,若要搜索某一特定 Prim,其耗時將更為顯著。針對此性能瓶頸,F(xiàn)abric / 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 等)。創(chuàng)建后在 Stage 的面板中也可看到基本的 Type 類型。


AppliedAPI:要理解 Applied API,需先了解 USD 中的API Schema(https://openusd.org/release/glossary.html#api-schema)。它指的是一組可通過特定接口動態(tài)應用到 Prim 上、為其增添功能的屬性集合。例如,可以為一個 Mesh Prim 增加剛體碰撞的屬性,或綁定材質(zhì)的屬性。
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 實現(xiàn),其時間復雜度為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 性能測試
下面通過一個實際案例,來對比動態(tài)修改 USD Prim 屬性時的性能差異。我們將遍歷場景中所有 Mesh 并修改其顏色屬性,分別使用原生 USD API 與 USDRT 實現(xiàn),并記錄運行時間。
2.3.1 準備測試場景
首先下載一個復雜度較高的場景資產(chǎn)用于測試:
訪問網(wǎng)站下載 Kitchen Set 資產(chǎn):
https://developer.nvidia.com/usd
解壓后,在 Omniverse 中打開Kitchen_set.usd文件。

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")
測試結(jié)果:
在配置了 NVIDIA RTX 5880 Ada 的測試機上,首次運行耗時約1337 ms(具體時間因配置而異)。
此時可觀察到原生 USD 文件已被修改。
注意:如果不更改顏色值再次運行同一段代碼,耗時將降至約14 ms。如果改變顏色值,時間又會如何?建議動手嘗試并思考背后的原因。

回顧此前在《Omniverse 性能優(yōu)化系列(一):Tracy Profiler》中的分析:抓取最大渲染運行時間,通過函數(shù)的 callstack 可以發(fā)現(xiàn),渲染線程在等待 USD 文件寫入完成。這意味著 USD 的寫入操作很可能是單線程串行的。USD 數(shù)據(jù)變更后,會通過 Observer 設計模式逐一通知相關模塊,整個過程是同步的。

2.3.3 使用 USDRT 修改顏色
接下來,使用 USDRT 實現(xiàn)相同的顏色修改操作:
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")
測試結(jié)果:
相同操作耗時僅約148 ms,相比原生 USD 提升了近一個數(shù)量級。
觀察左上角可發(fā)現(xiàn),原生 USD 文件本身并未被修改。
*延伸嘗試:根據(jù)上文提到的 API 把 traverse stage 變成 USDRT 提供的快速查詢 API 并對比時間差異。


2.4 USDRT 中的 Change Tracking 機制
在 USD 體系中,TfNotice(Tool Foundation Notice)是核心的消息處理機制,它實現(xiàn)了 “場景 / 資源變化 → 自動推送事件” 的監(jiān)聽模式,可應用于監(jiān)視 Stage 編輯、屬性更新、層加載、實時協(xié)作以及視口刷新等場景。
USD 原生的 TfNotice 為監(jiān)聽器(Listener)提供了一種回調(diào)機制。然而,USDRT 提供了一套完全不同的變化跟蹤范式。它提供了一系列主動查詢函數(shù),讓開發(fā)者能夠精確跟蹤所關心的屬性變化。用戶需要在合適的頻率主動查詢并處理這些更改(相當于需要自行封裝類似回調(diào)的邏輯)。
這一設計變革源于一個關鍵的性能考量:USD 原生的通知 (Notify) 機制是完全同步的。當發(fā)送者 (Sender) 的線程觸發(fā)通知時,它會阻塞(Block)并等待所有已注冊的監(jiān)聽器按順序處理完畢。這意味著發(fā)送者線程的性能嚴重依賴于最慢的那個監(jiān)聽器。
為了避免這種阻塞,USDRT 采用了非阻塞的主動查詢機制。下表概述了其核心 API:

實踐示例:
結(jié)合前文提及的注冊 Omniverse 的消息函數(shù)“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,它會在異步更新循環(huán)中追蹤更改并定期清空記錄。完整代碼如下:
https://github.com/slayersong/OV_Perf_tutorial/blob/main/2.USDRT/RTTrackingTester.py
3. 總結(jié)
總的來說,USDRT / Fabric 的本質(zhì)是對 USD 文件寫入的緩存層。其中,F(xiàn)abric 提供底層 C++ 接口,USDRT 則負責提供與原生 USD API 一致的友好封裝,旨在實現(xiàn)代碼風格與調(diào)用的統(tǒng)一,讓開發(fā)者以熟悉的方式獲得顯著的性能提升。

文案提供 & 技術(shù)支持:
宋毅明 NVIDIA Omniverse & OpenUSD 開發(fā)者關系經(jīng)理
*與 NVIDIA 產(chǎn)品相關的圖片或視頻(完整或部分)的版權(quán)均歸 NVIDIA Corporation 所有。
-
NVIDIA
+關注
關注
14文章
5665瀏覽量
109977 -
代碼
+關注
關注
30文章
4973瀏覽量
74203 -
python
+關注
關注
58文章
4880瀏覽量
90203 -
開發(fā)者
+關注
關注
1文章
777瀏覽量
18047
原文標題:Omniverse 性能優(yōu)化系列(二):USDRT 的使用
文章出處:【微信號:Leadtek,微信公眾號:麗臺科技】歡迎添加關注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
NVIDIA Omniverse Extension開發(fā)秘籍
NVIDIA Omniverse Create最新版功能介紹
NVIDIA Omniverse的特性及應用
借助NVIDIA Omniverse Replicator功能加快AI培訓
Omniverse 中文課程系列 1: 開發(fā) Extensions 來自定義 Omniverse 功能與 UI
Omniverse 資訊速遞 | 行業(yè)動態(tài)、應用案例、創(chuàng)作者故事、教程與資源等你來解鎖!
探索NVIDIA AI和Omniverse加速設計創(chuàng)作
Omniverse 資訊速遞 | 行業(yè)動態(tài)、近期發(fā)布在線聽、最新更新、中文課程系列等你來解鎖!
Omniverse 資訊速遞 | 行業(yè)動態(tài)、應用案例、創(chuàng)作者故事等你來解鎖!
Omniverse 資訊速遞 | 行業(yè)動態(tài)、應用案例、合成數(shù)據(jù)生成系列視頻等你來解鎖!
Omniverse Connectors功能及區(qū)別簡析
NVIDIA Omniverse USD Composer能用來做什么?如何獲取呢?
Omniverse教程(12):NVIDIA Omniverse USD Presenter的基礎應用
NVIDIA Omniverse基于Container的部署推流方案
使用USDRT優(yōu)化NVIDIA Omniverse的動態(tài)數(shù)據(jù)更改功能
評論