Skip to content

Commit 17304f5

Browse files
committed
Use launcher runtime intent for clone decision
1 parent 7aefc99 commit 17304f5

3 files changed

Lines changed: 127 additions & 48 deletions

File tree

source/isaaclab/isaaclab/scene/interactive_scene.py

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
from isaaclab.sim.utils.stage import get_current_stage, get_current_stage_id
3737
from isaaclab.sim.views import FrameView
3838
from isaaclab.terrains import TerrainImporter, TerrainImporterCfg
39-
from isaaclab.utils.version import has_kit
4039

4140
# Note: This is a temporary import for the VisuoTactileSensorCfg class.
4241
# It will be removed once the VisuoTactileSensor class is added to the core Isaac Lab framework.
@@ -146,7 +145,6 @@ def __init__(self, cfg: InteractiveSceneCfg):
146145
self.stage_id = get_current_stage_id()
147146
self.physics_backend = self.sim.physics_manager.__name__.lower()
148147
requested_viz_types = set(self.sim.resolve_visualizer_types())
149-
requested_renderer_types = set(self._cfg_renderer_types())
150148
if self.physics_backend.startswith("ovphysx"):
151149
from isaaclab_ovphysx.cloner import ovphysx_replicate
152150

@@ -166,13 +164,14 @@ def __init__(self, cfg: InteractiveSceneCfg):
166164
# prepare cloner for environment replication
167165
self.env_prim_paths = [f"{self.env_ns}/env_{i}" for i in range(self.cfg.num_envs)]
168166
is_newton_replicated_scene = self.cfg.replicate_physics and self.physics_backend.startswith("newton")
167+
has_ovrtx_renderer = self.sim.has_requested_renderer_type("ovrtx")
169168

170169
self.cloner_cfg = cloner.CloneCfg(
171170
clone_regex=self.env_regex_ns,
172171
clone_in_fabric=self.cfg.clone_in_fabric,
173172
device=self.device,
174173
physics_clone_fn=physics_clone_fn,
175-
clone_usd=not is_newton_replicated_scene or has_kit() or "ovrtx" in requested_renderer_types,
174+
clone_usd=not is_newton_replicated_scene or self.sim.runtime_needs_kit() or has_ovrtx_renderer,
176175
)
177176

178177
# create source prim
@@ -354,17 +353,6 @@ def _aggregate_scene_data_requirements(self, visualizer_types=()) -> None:
354353
if requirements != current_req:
355354
self.sim.update_scene_data_requirements(requirements)
356355

357-
def _cfg_renderer_types(self) -> list[str]:
358-
"""Return renderer type names declared by scene cfg entries before sensors are constructed."""
359-
cfg_fields = InteractiveSceneCfg.__dataclass_fields__
360-
renderer_types: list[str] = []
361-
for asset_cfg in (v for k, v in self.cfg.__dict__.items() if k not in cfg_fields and v is not None):
362-
cfgs = asset_cfg.rigid_objects.values() if isinstance(asset_cfg, RigidObjectCollectionCfg) else [asset_cfg]
363-
for cfg in cfgs:
364-
if (rcfg := getattr(cfg, "renderer_cfg", None)) is not None:
365-
renderer_types.append(getattr(rcfg, "renderer_type", "default"))
366-
return renderer_types
367-
368356
def _sensor_renderer_types(self) -> list[str]:
369357
"""Return renderer type names used by scene sensors (skipping any without a renderer cfg)."""
370358
return [

source/isaaclab/isaaclab/sim/simulation_context.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,22 @@ def resolve_visualizer_types(self) -> list[str]:
529529
visualizer_cfgs = [visualizer_cfgs]
530530
return [cfg.visualizer_type for cfg in visualizer_cfgs if getattr(cfg, "visualizer_type", None)]
531531

532+
def resolve_renderer_types(self) -> list[str]:
533+
"""Return renderer type names predetermined by the simulation launcher."""
534+
requested = self.get_setting("/isaaclab/runtime/renderer_types")
535+
if not isinstance(requested, str) or not requested.strip():
536+
return []
537+
return [value for chunk in requested.split(",") for value in chunk.split() if value]
538+
539+
def has_requested_renderer_type(self, renderer_type: str) -> bool:
540+
"""Return whether the launcher detected a requested renderer type."""
541+
return renderer_type in self.resolve_renderer_types()
542+
543+
def runtime_needs_kit(self) -> bool:
544+
"""Return the launcher-predetermined Kit requirement, falling back to active Kit state."""
545+
needs_kit = self.get_setting("/isaaclab/runtime/needs_kit")
546+
return has_kit() if needs_kit is None else bool(needs_kit)
547+
532548
def _resolve_visualizer_cfgs(self) -> list[Any]:
533549
"""Resolve final visualizer configs from cfg and optional CLI override.
534550

source/isaaclab_tasks/isaaclab_tasks/utils/sim_launcher.py

Lines changed: 109 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99

1010
import argparse
1111
import logging
12-
from collections.abc import Callable, Generator
12+
from collections.abc import Callable, Generator, Iterable, Mapping
1313
from contextlib import contextmanager
1414
from typing import Any
1515

16+
from isaaclab.app.settings_manager import get_settings_manager
1617
from isaaclab.physics.physics_manager_cfg import PhysicsCfg
1718
from isaaclab.renderers.renderer_cfg import RendererCfg
1819
from isaaclab.sensors.camera.camera_cfg import CameraCfg
@@ -31,6 +32,17 @@ def add_launcher_args(parser: argparse.ArgumentParser) -> None:
3132
AppLauncher.add_app_launcher_args(parser)
3233

3334

35+
def _iter_config_children(node) -> Iterable[Any]:
36+
if isinstance(node, Mapping):
37+
return node.values()
38+
if isinstance(node, (list, tuple)):
39+
return node
40+
try:
41+
return vars(node).values()
42+
except TypeError:
43+
return ()
44+
45+
3446
def _scan_config(cfg, predicates: list[Callable[[Any], bool]]) -> list[bool]:
3547
"""Recursively walk *cfg* and evaluate each predicate on every node.
3648
@@ -53,11 +65,7 @@ def _visit(node):
5365
if not results[i] and pred(node):
5466
results[i] = True
5567

56-
try:
57-
children = vars(node)
58-
except TypeError:
59-
return
60-
for child in children.values():
68+
for child in _iter_config_children(node):
6169
if child is None or isinstance(child, (int, float, str, bool)):
6270
continue
6371
_visit(child)
@@ -66,6 +74,64 @@ def _visit(node):
6674
return results
6775

6876

77+
def _collect_renderer_types(cfg) -> set[str]:
78+
"""Return renderer type names declared in the resolved runtime config."""
79+
renderer_types: set[str] = set()
80+
visited: set[int] = set()
81+
82+
def _visit(node):
83+
node_id = id(node)
84+
if node_id in visited:
85+
return
86+
visited.add(node_id)
87+
88+
if isinstance(node, RendererCfg):
89+
renderer_types.add(getattr(node, "renderer_type", "default"))
90+
return
91+
92+
for child in _iter_config_children(node):
93+
if child is None or isinstance(child, (int, float, str, bool)):
94+
continue
95+
_visit(child)
96+
97+
_visit(cfg)
98+
return renderer_types
99+
100+
101+
def _compute_runtime_intent(env_cfg, launcher_args: argparse.Namespace | dict | None = None) -> dict[str, Any]:
102+
"""Compute backend, renderer, and visualizer intent from the resolved env config."""
103+
is_kitless, has_kit_cameras, has_kit_physics, has_ovrtx_renderer, has_ovphysx_physics = _scan_config(
104+
env_cfg, [_is_kitless_physics, _is_kit_camera, _is_kit_physics, _is_ovrtx_renderer, _is_ovphysx_physics]
105+
)
106+
renderer_types = _collect_renderer_types(env_cfg)
107+
visualizer_types = _get_visualizer_types(launcher_args)
108+
visualizer_intent = _compute_visualizer_intent(env_cfg)
109+
has_kit_visualizer = "kit" in visualizer_types or visualizer_intent.get("has_kit_visualizer", False)
110+
needs_kit = has_kit_cameras or not is_kitless or "kit" in visualizer_types
111+
return {
112+
"needs_kit": needs_kit,
113+
"has_kit_cameras": has_kit_cameras,
114+
"has_kit_physics": has_kit_physics,
115+
"has_kit_visualizer": has_kit_visualizer,
116+
"has_ovrtx_renderer": has_ovrtx_renderer or "ovrtx" in renderer_types,
117+
"has_ovphysx_physics": has_ovphysx_physics,
118+
"renderer_types": renderer_types,
119+
"visualizer_types": visualizer_types,
120+
"visualizer_intent": visualizer_intent,
121+
}
122+
123+
124+
def _sync_runtime_intent_settings(runtime_intent: dict[str, Any]) -> None:
125+
"""Persist launcher runtime intent for SimulationContext readers."""
126+
settings = get_settings_manager()
127+
renderer_types = sorted(str(v) for v in runtime_intent.get("renderer_types", set()) if str(v))
128+
settings.set_bool("/isaaclab/runtime/needs_kit", bool(runtime_intent.get("needs_kit", False)))
129+
settings.set_bool("/isaaclab/runtime/has_kit_cameras", bool(runtime_intent.get("has_kit_cameras", False)))
130+
settings.set_bool("/isaaclab/runtime/has_kit_visualizer", bool(runtime_intent.get("has_kit_visualizer", False)))
131+
settings.set_bool("/isaaclab/runtime/has_ovrtx_renderer", bool(runtime_intent.get("has_ovrtx_renderer", False)))
132+
settings.set_string("/isaaclab/runtime/renderer_types", " ".join(renderer_types))
133+
134+
69135
def _is_kitless_physics(node) -> bool:
70136
"""True when the node is a kitless physics config (Newton or OvPhysX)."""
71137
return isinstance(node, PhysicsCfg) and type(node).__name__ in ("NewtonCfg", "OvPhysxCfg")
@@ -165,12 +231,12 @@ def compute_kit_requirements(
165231
Returns:
166232
(needs_kit, has_kit_cameras, visualizer_types)
167233
"""
168-
is_kitless, has_kit_cameras = _scan_config(env_cfg, [_is_kitless_physics, _is_kit_camera])
169-
needs_kit = has_kit_cameras or not is_kitless
170-
visualizer_types = _get_visualizer_types(launcher_args)
171-
if "kit" in visualizer_types:
172-
needs_kit = True
173-
return needs_kit, has_kit_cameras, visualizer_types
234+
runtime_intent = _compute_runtime_intent(env_cfg, launcher_args)
235+
return (
236+
bool(runtime_intent["needs_kit"]),
237+
bool(runtime_intent["has_kit_cameras"]),
238+
set(runtime_intent["visualizer_types"]),
239+
)
174240

175241

176242
def validate_runtime_compatibility(
@@ -194,12 +260,14 @@ def validate_runtime_compatibility(
194260
ValueError: If the OVRTX renderer is combined with Kit-based physics or the
195261
Kit visualizer, or if OvPhysX physics is combined with the Kit visualizer.
196262
"""
197-
has_kit_physics, has_ovrtx_renderer, has_ovphysx_physics = _scan_config(
198-
env_cfg, [_is_kit_physics, _is_ovrtx_renderer, _is_ovphysx_physics]
199-
)
200-
visualizer_intent = _compute_visualizer_intent(env_cfg)
201-
visualizer_types = _get_visualizer_types(launcher_args)
202-
has_kit_visualizer = "kit" in visualizer_types or visualizer_intent.get("has_kit_visualizer", False)
263+
_validate_runtime_compatibility_from_intent(_compute_runtime_intent(env_cfg, launcher_args))
264+
265+
266+
def _validate_runtime_compatibility_from_intent(runtime_intent: dict[str, Any]) -> None:
267+
has_kit_physics = bool(runtime_intent["has_kit_physics"])
268+
has_ovrtx_renderer = bool(runtime_intent["has_ovrtx_renderer"])
269+
has_ovphysx_physics = bool(runtime_intent["has_ovphysx_physics"])
270+
has_kit_visualizer = bool(runtime_intent["has_kit_visualizer"])
203271

204272
if has_ovphysx_physics and has_kit_visualizer:
205273
raise ValueError(
@@ -319,23 +387,26 @@ def launch_simulation(
319387
# ovrtx and Kit ship the same RTX hydra libraries under conflicting USD namespaces;
320388
# loading both in the same process causes a dynamic-linker crash. Use
321389
# --visualizer newton instead, which is compatible with ovrtx presets.
322-
early_visualizer_types = _get_visualizer_types(launcher_args)
323-
if "kit" in early_visualizer_types:
324-
has_ovrtx = _scan_config(
325-
env_cfg, [lambda node: isinstance(node, RendererCfg) and getattr(node, "renderer_type", None) == "ovrtx"]
326-
)[0]
327-
if has_ovrtx:
328-
raise ValueError(
329-
"[launch_simulation] '--visualizer kit' is incompatible with 'ovrtx_renderer'. "
330-
"Both Kit (Isaac Sim) and ovrtx ship conflicting RTX hydra libraries "
331-
"(librtx.hydra.so, liblegacy.hydra.so) compiled against different USD namespaces, "
332-
"which causes a dynamic-linker crash when loaded into the same process. "
333-
"Use '--visualizer newton' instead, which is fully compatible with ovrtx presets."
334-
)
390+
runtime_intent = _compute_runtime_intent(env_cfg, launcher_args)
391+
early_visualizer_types = set(runtime_intent["visualizer_types"])
392+
if "kit" in early_visualizer_types and runtime_intent["has_ovrtx_renderer"]:
393+
raise ValueError(
394+
"[launch_simulation] '--visualizer kit' is incompatible with 'ovrtx_renderer'. "
395+
"Both Kit (Isaac Sim) and ovrtx ship conflicting RTX hydra libraries "
396+
"(librtx.hydra.so, liblegacy.hydra.so) compiled against different USD namespaces, "
397+
"which causes a dynamic-linker crash when loaded into the same process. "
398+
"Use '--visualizer newton' instead, which is fully compatible with ovrtx presets."
399+
)
335400

336-
validate_runtime_compatibility(env_cfg, launcher_args)
401+
_validate_runtime_compatibility_from_intent(runtime_intent)
337402
needs_kit, has_kit_cameras, visualizer_types = compute_kit_requirements(env_cfg, launcher_args)
338-
visualizer_intent = _compute_visualizer_intent(env_cfg)
403+
visualizer_intent = runtime_intent["visualizer_intent"]
404+
runtime_intent["needs_kit"] = needs_kit
405+
runtime_intent["has_kit_cameras"] = has_kit_cameras
406+
runtime_intent["has_kit_visualizer"] = "kit" in visualizer_types or visualizer_intent.get(
407+
"has_kit_visualizer", False
408+
)
409+
runtime_intent["visualizer_types"] = visualizer_types
339410
_set_visualizer_intent_on_launcher_args(launcher_args, visualizer_intent)
340411

341412
if needs_kit and has_kit_cameras:
@@ -421,7 +492,11 @@ def launch_simulation(
421492
if sim_cfg is not None and hasattr(app_launcher, "device"):
422493
sim_cfg.device = app_launcher.device
423494
close_fn = app_launcher.app.close
424-
elif visualizer_types or visualizer_explicit_none:
495+
_sync_runtime_intent_settings(runtime_intent)
496+
else:
497+
_sync_runtime_intent_settings(runtime_intent)
498+
499+
if not needs_kit and (visualizer_types or visualizer_explicit_none):
425500
# Newton path without Kit: AppLauncher is skipped, so manually store the visualizer
426501
# selection in SettingsManager (works in standalone mode via plain dict) so that
427502
# SimulationContext._get_cli_visualizer_types() can find it.

0 commit comments

Comments
 (0)