Skip to content

Waits for textures to be loaded for Isaac RTX renderer#4956

Open
bareya wants to merge 11 commits intoisaac-sim:developfrom
bareya:pbarejko/rtx-renderer-load-textures
Open

Waits for textures to be loaded for Isaac RTX renderer#4956
bareya wants to merge 11 commits intoisaac-sim:developfrom
bareya:pbarejko/rtx-renderer-load-textures

Conversation

@bareya
Copy link
Collaborator

@bareya bareya commented Mar 11, 2026

Wait for all textures to load in IsaacRTX Renderer for Camera and Tiled Camera

Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
List any dependencies that are required for this change.

Fixes # (issue)

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (existing functionality will not work without user modification)
  • Documentation update

Screenshots

Please attach before and after screenshots of the change if applicable.

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks with ./isaaclab.sh --format
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the changelog and the corresponding version in the extension's config/extension.toml file
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

@bareya bareya changed the title Feature: Wait for textures to be loaded. Feature: Wait for textures to be loaded Mar 11, 2026
@github-actions github-actions bot added the isaac-lab Related to Isaac Lab team label Mar 11, 2026
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 11, 2026

Greptile Summary

This PR replaces a long-standing workaround (running 5 fixed simulation steps to allow textures to load) with a proper event-driven mechanism that subscribes to the Carb omni.streamingstatus:streaming_status event and pumps app.update() until the RTX renderer reports idle. The change touches isaac_rtx_renderer_utils.py for the core implementation and removes the warm-up loops from ~40 test functions across 5 test files.

Key changes:

  • New module-level streaming subscription in isaac_rtx_renderer_utils.py using carb.eventdispatcher; subscription is established lazily on the first ensure_isaac_rtx_render_update() call
  • _wait_for_streaming_complete() pumps app.update() in a loop until _streaming_is_busy is False, with a 30 s hard timeout and a warning log on expiry
  • All 5 test files cleaned up by removing the now-unnecessary 5-step (or 12-step in one case) warm-up loops

Issues found:

  • _streaming_is_busy and _streaming_subscribed are module-level globals that survive SimulationContext teardown; a timeout-caused stale _streaming_is_busy = True from one test will trigger a second full-timeout wait at the start of the next test
  • The _streaming_subscribed = True guard is set before verifying the subscription actually succeeded, permanently preventing retry if get_eventdispatcher() returns None
  • The busy-wait loop has no sleep between app.update() calls, which can spin the CPU unnecessarily under headless/fast-return conditions

Confidence Score: 3/5

  • The PR is safe to merge for normal usage but has stale global state across SimulationContext instances that could cause test flakiness or unexpected multi-second delays in test suites.
  • The core mechanism is sound — subscribing to streaming-status events is far better than the fixed 5-step workaround. However, the module-level _streaming_is_busy flag is never reset when a new SimulationContext is created, meaning a timed-out streaming run in one test can cascade into an unnecessary 30 s wait in the next. Additionally, the subscription guard being set before verifying dispatcher availability can silently disable the feature with no diagnostics. These issues don't affect production single-simulation usage but are a real concern for multi-test CI runs.
  • source/isaaclab_physx/isaaclab_physx/renderers/isaac_rtx_renderer_utils.py — the stale global streaming state and silent subscription failure path need attention before this lands in test-heavy CI pipelines.

Important Files Changed

Filename Overview
source/isaaclab_physx/isaaclab_physx/renderers/isaac_rtx_renderer_utils.py Core implementation: adds event-driven RTX streaming wait via Carb event dispatcher. Has stale module-level state issues across SimulationContext instances and a silently-swallowed subscription failure path.
source/isaaclab/test/sensors/test_camera.py Removes the 5-step warm-up workaround from ~14 test functions; no new logic introduced.
source/isaaclab/test/sensors/test_tiled_camera.py Removes the 5-step warm-up workaround from ~18 test functions; no new logic introduced.
source/isaaclab/test/sensors/test_multi_tiled_camera.py Removes the 5-step warm-up workaround from 4 test functions; no new logic introduced.
source/isaaclab/test/sensors/test_multi_mesh_ray_caster_camera.py Removes the 5-step warm-up workaround from 5 test functions; no new logic introduced.
source/isaaclab/test/deps/isaacsim/check_camera.py Removes the 5-step warm-up loop workaround; leaves a stray blank line but otherwise clean.

Sequence Diagram

sequenceDiagram
    participant Cam as Camera/TiledCamera
    participant Utils as isaac_rtx_renderer_utils
    participant Carb as carb.eventdispatcher
    participant App as omni.kit.app

    Cam->>Utils: ensure_isaac_rtx_render_update()
    Utils->>Utils: check dedup key (id(sim), step_count)
    Utils->>Carb: _ensure_streaming_subscription() [idempotent]
    Carb-->>Utils: subscription handle
    Utils->>App: app.update()  [trigger RTX render]
    App-->>Carb: fires streaming_status(isBusy=True)
    Carb-->>Utils: _on_streaming_status_event → _streaming_is_busy=True
    Utils->>Utils: if _streaming_is_busy → _wait_for_streaming_complete()
    loop Until idle or 30s timeout
        Utils->>App: app.update()
        App-->>Carb: fires streaming_status(isBusy=False)
        Carb-->>Utils: _on_streaming_status_event → _streaming_is_busy=False
    end
    Utils->>Utils: update _last_render_update_key
    Utils-->>Cam: return (textures ready)
Loading

Last reviewed commit: fc40398

Comment on lines +28 to +30
_streaming_is_busy: bool = False
_streaming_subscription = None
_streaming_subscribed: bool = False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale streaming state across SimulationContext instances

_streaming_is_busy and _streaming_subscribed are module-level globals that are never reset when a new SimulationContext is created. The render-update dedup key (_last_render_update_key) correctly invalidates itself per-context via id(sim), but the streaming state has no equivalent invalidation.

The problematic scenario: if a previous simulation ends while _streaming_is_busy = True (i.e., the 30 s timeout was hit), the next test's first call to ensure_isaac_rtx_render_update() will skip _ensure_streaming_subscription() (already subscribed) then immediately enter _wait_for_streaming_complete() because the stale True flag is never cleared between contexts. This could add an unnecessary second full timeout wait at the start of every subsequent test in a test suite.

Consider resetting the streaming state alongside the dedup key when a new SimulationContext is detected:

# at the top of ensure_isaac_rtx_render_update(), after computing `key`
if key[0] != _last_render_update_key[0]:   # new SimulationContext
    global _streaming_is_busy, _streaming_subscribed, _streaming_subscription
    _streaming_is_busy = False
    _streaming_subscribed = False
    _streaming_subscription = None

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Thanks AI!

Comment on lines +46 to +64
def _ensure_streaming_subscription() -> None:
"""Subscribe to RTX streaming status events (idempotent)."""
global _streaming_subscription, _streaming_subscribed
if _streaming_subscribed:
return

_streaming_subscribed = True
try:
from carb.eventdispatcher import get_eventdispatcher

dispatcher = get_eventdispatcher()
if dispatcher is not None:
_streaming_subscription = dispatcher.observe_event(
observer_name="isaaclab_rtx_streaming_wait",
event_name=_RTX_STREAMING_STATUS_EVENT,
on_event=_on_streaming_status_event,
)
except (ImportError, AttributeError):
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_streaming_subscribed guard set before subscription succeeds

_streaming_subscribed = True is written on line 52 before the try block verifies that a dispatcher is available and the subscription actually succeeds. If get_eventdispatcher() returns None (or raises and is silently caught), _streaming_subscribed is permanently True, meaning no retry will ever be attempted — even though _streaming_subscription remains None and no events will ever arrive.

The result is that the texture-wait feature silently degrades to a no-op without any log message, making it hard to diagnose why textures are still black in environments where the event dispatcher is slow to initialise.

Consider either:

  • Moving _streaming_subscribed = True to after a successful observe_event() call, or
  • Logging a warning when the dispatcher is unavailable.

Comment on lines +67 to +73
def _wait_for_streaming_complete() -> None:
"""Pump ``app.update()`` until RTX streaming reports idle or timeout."""
import omni.kit.app

start = time.monotonic()
while _streaming_is_busy and (time.monotonic() - start) < _STREAMING_WAIT_TIMEOUT_S:
omni.kit.app.get_app().update()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No rate-limiting in the busy-wait loop

_wait_for_streaming_complete() calls omni.kit.app.get_app().update() in a tight loop with no time.sleep() between iterations. While each app.update() call does real work, it can return quickly in headless/test configurations, turning this into a CPU-intensive spin for up to 30 seconds in the timeout scenario.

Consider adding a small sleep to yield the CPU between pumps:

while _streaming_is_busy and (time.monotonic() - start) < _STREAMING_WAIT_TIMEOUT_S:
    omni.kit.app.get_app().update()
    time.sleep(0.005)  # ~200 Hz max pump rate

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think we need to add overhead of sleep here, even small. if something is timing out no need to optimize the exit IMO

@nvsekkin nvsekkin force-pushed the pbarejko/rtx-renderer-load-textures branch from 5b51550 to 8704770 Compare March 12, 2026 02:15
@bareya bareya requested a review from kellyguo11 March 12, 2026 20:50
@kellyguo11 kellyguo11 changed the title Feature: Wait for textures to be loaded Waits for textures to be loaded for Isaac RTX renderer Mar 13, 2026
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.

3 participants