Hello @Turowicz! Accelerating the creation of USD references is something we’ve been actively working on and I’m happy to share a function that you can use now until the next version of Replicator which will integrate this optimization natively. This approach achieve a ~190x speedup
from typing import Optional
import omni.usd
import pxr
import omni.replicator.core as rep
def from_usd_fast(
usd_path: str = None,
prim_path: str = None,
parent: Optional[str] = None,
count: int = 1,
name: str = "Ref",
) -> rep.utils.ReplicatorItem:
"""Use Prim Specs to create references within an Sdf.ChangeBlock.
This function is faster than the default create function because it uses Prim Specs to create references within an
Sdf.ChangeBlock.
Args:
usd_path (str): _description_
prim_path (str): _description_
parent (Optional[str], optional): _description_. Defaults to None.
count (int, optional): _description_. Defaults to 1.
name (str, optional): _description_. Defaults to "Ref".
Returns:
rep.utils.ReplicatorItem: _description_
"""
usd_path = str(usd_path) if usd_path is not None else ""
prim_path = str(prim_path) if prim_path is not None else pxr.Sdf.Path()
stage = omni.usd.get_context().get_stage()
if parent is None:
if not stage.GetPrimAtPath(rep.create.REPLICATOR_SCOPE):
stage.DefinePrim(rep.create.REPLICATOR_SCOPE, "Scope")
parent = rep.create.REPLICATOR_SCOPE
xform_paths = []
prims_paths = []
suffix = 0
xform_name = f"{name}_Xform"
with pxr.Sdf.ChangeBlock():
for _ in range(count):
# First create a xform on top of it.
if suffix == 0:
xform_path = f"{parent}/{xform_name}"
suffix += 1
else:
xform_path = f"{parent}/{xform_name}_{suffix:02d}"
suffix += 1
# Find first available path
while stage.GetPrimAtPath(xform_path):
suffix += 1
xform_path = f"{parent}/{xform_name}_{suffix:02d}"
spec = pxr.Sdf.CreatePrimInLayer(stage.GetEditTarget().GetLayer(), xform_path)
spec.typeName = "Xform"
spec.specifier = pxr.Sdf.SpecifierDef
# Create the prim
name = pxr.Tf.MakeValidIdentifier(name)
ref_prim_path = f"{xform_path}/{name}"
prim_spec = pxr.Sdf.CreatePrimInLayer(stage.GetEditTarget().GetLayer(), ref_prim_path)
prim_spec.specifier = pxr.Sdf.SpecifierDef
xform_paths.append(xform_path)
prim_spec.referenceList.Prepend(pxr.Sdf.Reference(assetPath=usd_path, primPath=prim_path))
prims_paths.append(ref_prim_path)
return rep.create.group(xform_paths)
rep.create.register(from_usd_fast)
Test Script:
import time
N = 5000
ref = rep.example.ASSETS[0]
omni.usd.get_context().new_stage()
print("Starting Standard Test...")
start = time.time()
rep.create.from_usd(ref, count=N)
print("Standard", time.time() - start)
omni.usd.get_context().new_stage()
print("Starting Fast Test...")
start = time.time()
rep.create.from_usd_fast(usd_path=ref, count=N)
print("Fast", time.time() - start)
omni.usd.get_context().new_stage()
print("Starting Standard Test with positions...")
start = time.time()
refs = rep.create.from_usd(ref, count=N)
with refs:
rep.modify.pose(position=rep.distribution.uniform((-500, -500, -500), (500, 500, 500)))
print("Standard with positions", time.time() - start)
omni.usd.get_context().new_stage()
print("Starting Fast Test with positions...")
start = time.time()
refs = rep.create.from_usd_fast(usd_path=ref, count=N)
with refs:
rep.modify.pose(position=rep.distribution.uniform((-500, -500, -500), (500, 500, 500)))
print("Fast with positions", time.time() - start)
Output on test (N=5000, machine (Ryzen 1700X, Nvidia TitanRTX))
Starting Standard Test...
Standard 194.00616669654846
Starting Fast Test...
Fast 0.994647741317749
Starting Standard Test with positions...
Standard with positions 194.6883668899536
Starting Fast Test with positions...
Fast with positions 1.2436614036560059