Skip to content

Honor replicate_physics in replication sessions#6541

Open
nblauch wants to merge 1 commit into
isaac-sim:developfrom
nblauch:codex/replicate-physics-upstream
Open

Honor replicate_physics in replication sessions#6541
nblauch wants to merge 1 commit into
isaac-sim:developfrom
nblauch:codex/replicate-physics-upstream

Conversation

@nblauch

@nblauch nblauch commented Jul 15, 2026

Copy link
Copy Markdown

Summary

  • propagate InteractiveSceneCfg.replicate_physics into ReplicateSession
  • skip physics-backend replication when replicate_physics=False
  • continue USD replication so physics can discover the completed stage normally
  • add coverage for policy forwarding, queue draining, and backend dispatch

Problem

The replication-session refactor in c7cbe661d07 stopped honoring InteractiveSceneCfg.replicate_physics.

Before that refactor, replicate_physics=False performed USD cloning without invoking backend physics replication. After the refactor, InteractiveScene entered a ReplicateSession that unconditionally dispatched every queued replication context.

This caused scenes explicitly configured with replicate_physics=False to invoke native PhysX replication unexpectedly.

Implementation

replicate() and ReplicateSession now accept a replicate_physics argument, defaulting to True for compatibility with existing callers.

Physics replication contexts declare replicates_physics = True. When physics replication is disabled, those contexts are drained from the queue but not dispatched. USD replication still runs, preserving the complete stage needed for normal physics discovery.

PhysX, OVPhysX, and Newton replication contexts are marked consistently.

Validation

  • focused tests cover both enabled and disabled physics replication
  • Ruff passes on all changed Python files
  • downstream CPU suite: 347 passed, 41 skipped
  • 4/4 independent four-GPU PhysX + RTX runs using heterogeneous object mappings completed 10/10 training epochs, saved final checkpoints, and uploaded outputs with replicate_physics=False

The runtime-tested patch was applied to the IsaacLab revision pinned by the downstream image. The commit submitted here is an equivalent clean port onto the current develop branch.

Important follow-up

This change restores the intended replicate_physics=False behavior, but it does not resolve a second issue observed during debugging:

With replicate_physics=True, native PhysX replication of a heterogeneous ClonePlan, where different environment rows receive different rigid-object prototypes/configurations, intermittently corrupts native memory under a multi-GPU PhysX + RTX workload. Observed failures include:

  • malloc(): unaligned tcache chunk detected
  • double free or corruption
  • worker termination by SIGABRT

Disabling only object physics replication eliminated the failure in 4/4 runs, and restoring the scene-level replicate_physics=False policy passed another 4/4 runs.

This suggests a separate issue in the heterogeneous mGPU physics-replication path. It should be investigated independently; this PR restores the supported opt-out path but does not claim to fix that underlying failure.

@github-actions github-actions Bot added the isaac-lab Related to Isaac Lab team label Jul 15, 2026
@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR restores the InteractiveSceneCfg.replicate_physics=False opt-out that was broken by the replication-session refactor (c7cbe661). Before the refactor, setting this flag skipped physics-backend replication entirely; after the refactor, ReplicateSession unconditionally dispatched every queued backend context regardless of that flag.

  • replicate() and ReplicateSession gain a replicate_physics: bool = True keyword argument (defaulting to True for backward compatibility); when False, any backend context class carrying replicates_physics = True is drained from the queue but never instantiated or dispatched.
  • PhysxReplicateContext, OvPhysxReplicateContext, and NewtonReplicateContext each gain a replicates_physics = True class attribute so the core session can identify them without importing backend-specific types.
  • InteractiveScene forwards self.cfg.replicate_physics into ReplicateSession, closing the loop. Tests cover end-to-end forwarding and queue-drain behavior.

Confidence Score: 5/5

Safe to merge. The change is minimal and narrowly scoped: it adds a flag that defaults to the current behavior so no existing caller is affected, and the fix is well-covered by unit tests.

The logic correctly uses getattr(BackendCtxCls, 'replicates_physics', False) before instantiation, so physics contexts are never constructed when the flag is off. The queue is always drained upfront, preventing stale entries from bleeding into the next session. All three physics backends are consistently marked, and the True default means existing callers need no changes.

No files require special attention. Authors of future physics-backend replication contexts should remember to add replicates_physics = True, or their backend will be dispatched even when replicate_physics=False.

Important Files Changed

Filename Overview
source/isaaclab/isaaclab/cloner/replicate_session.py Core change: adds replicate_physics: bool = True to both replicate() and ReplicateSession.__init__; physics-marked contexts are drained but not dispatched when False.
source/isaaclab/isaaclab/scene/interactive_scene.py One-liner change forwarding self.cfg.replicate_physics into ReplicateSession, restoring the pre-refactor behavior.
source/isaaclab_physx/isaaclab_physx/cloner/replicate.py Adds replicates_physics = True class attribute to PhysxReplicateContext to allow core session to identify and filter it when physics replication is disabled.
source/isaaclab_ovphysx/isaaclab_ovphysx/cloner/replicate.py Adds replicates_physics = True class attribute to OvPhysxReplicateContext for consistent physics-backend identification.
source/isaaclab_newton/isaaclab_newton/cloner/replicate.py Adds replicates_physics = True class attribute to NewtonReplicateContext for consistent physics-backend identification.
source/isaaclab/test/sim/test_cloner.py Adds test_replicate_can_skip_physics_backends covering that physics-marked contexts are drained but not dispatched when replicate_physics=False, while non-physics contexts still execute.
source/isaaclab/test/scene/test_interactive_scene.py Parametrizes existing test_scene_publishes_plan_via_replicate over replicate_physics=[True, False] to verify InteractiveScene correctly forwards the cfg field.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant IS as InteractiveScene
    participant RS as ReplicateSession
    participant Q as REPLICATION_QUEUE
    participant R as replicate()
    participant PB as PhysicsBackendCtx
    participant UB as UsdBackendCtx

    IS->>RS: __init__(replicate_physics)
    IS->>RS: __enter__()
    Note over RS: make_clone_plan()
    Note over IS,Q: Asset constructors queue physics backends
    IS->>Q: append((cfg, PhysxReplicateContext))
    IS->>Q: append((cfg, NewtonReplicateContext))

    IS->>RS: __exit__()
    RS->>R: replicate(plan, stage, replicate_physics)
    Note over R,Q: drain queue upfront

    alt replicate_physics is True
        R->>PB: PB(stage).queue_mapping() then .replicate()
        R->>UB: UB(stage).queue_mapping() then .replicate()
    else replicate_physics is False
        Note over R,PB: replicates_physics=True contexts skipped
        R->>UB: UB(stage).queue_mapping() then .replicate()
    end

    R->>IS: SimulationContext.set_clone_plan(plan)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant IS as InteractiveScene
    participant RS as ReplicateSession
    participant Q as REPLICATION_QUEUE
    participant R as replicate()
    participant PB as PhysicsBackendCtx
    participant UB as UsdBackendCtx

    IS->>RS: __init__(replicate_physics)
    IS->>RS: __enter__()
    Note over RS: make_clone_plan()
    Note over IS,Q: Asset constructors queue physics backends
    IS->>Q: append((cfg, PhysxReplicateContext))
    IS->>Q: append((cfg, NewtonReplicateContext))

    IS->>RS: __exit__()
    RS->>R: replicate(plan, stage, replicate_physics)
    Note over R,Q: drain queue upfront

    alt replicate_physics is True
        R->>PB: PB(stage).queue_mapping() then .replicate()
        R->>UB: UB(stage).queue_mapping() then .replicate()
    else replicate_physics is False
        Note over R,PB: replicates_physics=True contexts skipped
        R->>UB: UB(stage).queue_mapping() then .replicate()
    end

    R->>IS: SimulationContext.set_clone_plan(plan)
Loading

Reviews (1): Last reviewed commit: "fix: honor replicate_physics in replicat..." | Re-trigger Greptile

@ooctipus

Copy link
Copy Markdown
Collaborator

Thanks again for identifying this regression and the repro, @nblauch! I've opened #6550 as an alternative mechanism for the same fix, following the design discussion on our side: assets register their replication contexts as a per-cfg list (capability as data), and the scene — the owner of the replicate_physics argument — filters physics contexts out of the queue, keeping replicate()/ReplicateSession policy-free. It also guards the two backends where skipping physics replication cannot work (OvPhysX assets have no USD-only cloning path; Newton's stage parser cannot rebuild /World/envs layouts), which would otherwise break silently under either mechanism.

The replicate_physics=True + heterogeneous mGPU double-free is a separate bug in the PhysX replication path (self-replication of single-variant rows creating duplicate PhysX objects at the same prim path) — we'll follow up on that one separately as you suggested.

ooctipus added a commit to ooctipus/IsaacLab that referenced this pull request Jul 16, 2026
Since the replication-session refactor, InteractiveSceneCfg.replicate_physics
was ignored: scenes configured with replicate_physics=False invoked native
physics replication anyway, silently discarding per-environment USD
differences (identified in isaac-sim#6541).

The flag now lives where the policy executes:

- CloneCfg.replicate_physics is the policy's home; InteractiveScene pipes its
  cfg flag into it and forwards it through ReplicateSession to replicate().
- REPLICATION_QUEUE holds bare asset cfgs: construction only registers which
  cfgs participate (required by ClonePlan.from_env_0 in direct workflows and
  to distinguish clonable assets from lights/terrain).
- replicate() resolves how each cfg clones at dispatch: the cfg's
  cloning_contexts field when set, otherwise the active backend's default
  stack exported as isaaclab_<backend>.cloner.REPLICATION. PhysX pairs
  physics replication with USD clones (collision groups are authored on the
  per-env prims; PhysX has no kitless mode), Newton includes USD only under
  Kit, and OvPhysX replicates alone since its clone replay authors USD.
- With replicate_physics=False, cloning is USD-only: the physics engine
  parses the per-env USD prims directly; an asset whose contexts are all
  physics-based is simply not cloned.

Asset implementations know nothing about cloning beyond registering their
cfg; per-asset overrides are a cfg assignment. The per-backend
queue_*_replication helpers are removed in favor of this single path.
ooctipus added a commit to ooctipus/IsaacLab that referenced this pull request Jul 16, 2026
Since the replication-session refactor, InteractiveSceneCfg.replicate_physics
was ignored: scenes configured with replicate_physics=False invoked native
physics replication anyway, silently discarding per-environment USD
differences (identified in isaac-sim#6541).

The flag now lives where the policy executes:

- CloneCfg.replicate_physics is the policy's home; InteractiveScene pipes its
  cfg flag into it and forwards it through ReplicateSession to replicate().
- REPLICATION_QUEUE holds bare asset cfgs: construction only registers which
  cfgs participate (required by ClonePlan.from_env_0 in direct workflows and
  to distinguish clonable assets from lights/terrain).
- replicate() resolves how each cfg clones at dispatch: the cfg's
  cloning_contexts field when set, otherwise the active backend's default
  stack exported as isaaclab_<backend>.cloner.REPLICATION. PhysX pairs
  physics replication with USD clones (collision groups are authored on the
  per-env prims; PhysX has no kitless mode), Newton includes USD only under
  Kit, and OvPhysX replicates alone since its clone replay authors USD.
- With replicate_physics=False, cloning is USD-only: the physics engine
  parses the per-env USD prims directly; an asset whose contexts are all
  physics-based is simply not cloned.

Asset implementations know nothing about cloning beyond registering their
cfg; per-asset overrides are a cfg assignment. The per-backend
queue_*_replication helpers are removed in favor of this single path.
ooctipus added a commit to ooctipus/IsaacLab that referenced this pull request Jul 17, 2026
Since the replication-session refactor, InteractiveSceneCfg.replicate_physics
was ignored: scenes configured with replicate_physics=False invoked native
physics replication anyway, silently discarding per-environment USD
differences (identified in isaac-sim#6541).

The flag now lives where the policy executes:

- CloneCfg.replicate_physics is the policy's home; InteractiveScene pipes its
  cfg flag into it and forwards it through ReplicateSession to replicate().
- REPLICATION_QUEUE holds bare asset cfgs: construction only registers which
  cfgs participate (required by ClonePlan.from_env_0 in direct workflows and
  to distinguish clonable assets from lights/terrain).
- replicate() resolves how each cfg clones at dispatch: the cfg's
  cloning_contexts field when set, otherwise the active backend's default
  stack exported as isaaclab_<backend>.cloner.REPLICATION. PhysX pairs
  physics replication with USD clones (collision groups are authored on the
  per-env prims; PhysX has no kitless mode), Newton includes USD only under
  Kit, and OvPhysX replicates alone since its clone replay authors USD.
- With replicate_physics=False, cloning is USD-only: the physics engine
  parses the per-env USD prims directly; an asset whose contexts are all
  physics-based is simply not cloned.

Asset implementations know nothing about cloning beyond registering their
cfg; per-asset overrides are a cfg assignment. The per-backend
queue_*_replication helpers are removed in favor of this single path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants