Skip to content

Newton raycaster + bvh refactor#6502

Open
StafaH wants to merge 8 commits into
isaac-sim:developfrom
StafaH:mh/newton_bvh_update
Open

Newton raycaster + bvh refactor#6502
StafaH wants to merge 8 commits into
isaac-sim:developfrom
StafaH:mh/newton_bvh_update

Conversation

@StafaH

@StafaH StafaH commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Description

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

@github-actions github-actions Bot added documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team labels Jul 14, 2026
@greptile-apps

greptile-apps Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR refactors the Newton ray-cast sensor stack, replacing the single monolithic ray_caster.py with three new files: NewtonRaycastSensor (BVH-backed, no mesh config), NewtonRaycastSensorCfg/Data, and legacy_ray_caster.py (mesh-target adapters with deprecation aliases). Newton manager gains a shared sensor scheduling graph — conditional CUDA graph capture with wp.capture_if per task — so BVH refit and per-sensor ray queries are batched into one graph per step.

  • NewtonRaycastSensor queries every collision shape in the scene BVH via newton.intersect_ray, registered as a named task with NewtonManager which handles cooperative BVH refit and CUDA-graph capture/invalidation.
  • NewtonWarpRenderer.render is refactored to register its launch as a sensor task, and RenderData.camera_transforms is made persistent (allocated once) to be safe inside a captured graph.
  • VelocityEnvHeightScannerCfg is added as a backend-dispatching preset for height scanning, using NewtonRaycastSensorCfg with global_world_only=True for Newton backends.

Confidence Score: 4/5

The change is safe to merge; the new sensor graph scheduling and BVH refit batching are well-tested and the refactor correctly handles graph invalidation on task registration/removal.

The core Newton manager sensor scheduling logic is sound — invalidation, fallback to eager, and the conditional CUDA graph via wp.capture_if are all handled consistently. The main concerns are non-blocking: a shared mutable config object in VelocityEnvHeightScannerCfg, a broad except RuntimeError in site registration that could mask real configuration mistakes, and PPISP running immediately after an async graph launch with an implicit same-stream ordering dependency. Tests cover eager and CUDA-graph paths as well as moving-geometry BVH refit.

newton_raycast_sensor.py (_register_sites_for_expr error handling), newton_warp_renderer.py (PPISP ordering after graph launch), and velocity_env_cfg.py (shared config object aliases)

Important Files Changed

Filename Overview
source/isaaclab_newton/isaaclab_newton/sensors/ray_caster/newton_raycast_sensor.py New BVH-backed ray-cast sensor; broad RuntimeError catch in _register_sites_for_expr could silently mis-configure sensor pose, and _update_newton_site_transforms lacks the None guards present in the old implementation
source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py Adds sensor task scheduling with conditional CUDA-graph capture; logic is well-structured with proper invalidation and fallback to eager execution
source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py Render launch moved into sensor task graph; PPISP is now applied after async graph launch with an implicit same-stream ordering assumption that should be documented
source/isaaclab_tasks/isaaclab_tasks/core/velocity/velocity_env_cfg.py Adds VelocityEnvHeightScannerCfg preset; newton_kamino, physx, and ovphysx alias the same config instances as newton_mjwarp/default respectively, creating shared mutable object references
source/isaaclab_newton/isaaclab_newton/sensors/ray_caster/legacy_ray_caster.py Clean extraction of mesh-target tracking into _LegacyNewtonRayCasterMixin with deprecation aliases; straightforward refactor with no new logic risks
source/isaaclab_newton/test/sensors/test_newton_raycast_sensor.py Good coverage: ground-plane hits, moving geometry BVH refit, backend dispatch, shared manager graph with renderer; both cuda_graph=True/False paths exercised via parametrize

Comments Outside Diff (1)

  1. source/isaaclab_newton/isaaclab_newton/sensors/ray_caster/newton_raycast_sensor.py, line 419-427 (link)

    P2 Missing None guard for model and state compared to previous implementation

    The previous _update_newton_site_transforms in ray_caster.py explicitly checked if model is None or state is None: raise RuntimeError(...) before launching the kernel. The new version calls NewtonManager.get_model() and NewtonManager.get_state_0() without checking the returned values. While in the normal lifecycle both are guaranteed to be non-None by the time any sensor task fires, a defensive guard preserves the informative error message and prevents a cryptic warp NullPointerException if the guard is ever violated by a reordering of initialization steps.

Reviews (1): Last reviewed commit: "Remove sensor manager" | Re-trigger Greptile

Comment on lines +104 to +107
ray_alignment="yaw",
pattern_cfg=patterns.GridPatternCfg(resolution=0.1, size=[1.6, 1.0]),
debug_vis=False,
global_world_only=True,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Shared mutable config object across multiple fields

newton_kamino = newton_mjwarp and physx = default / ovphysx = default assign the same object reference to multiple class attributes. Any downstream code that mutates one variant (e.g. scene_cfg.height_scanner.newton_kamino.debug_vis = True) will silently modify the other variant pointing to the same instance, since Python does not copy the object. The same applies to physx and ovphysx sharing the default RayCasterCfg. Consider constructing a separate instance for each alias that is intended to be independently configurable (or clearly document that these variants are intentionally frozen/immutable).

Comment on lines +333 to +348
ray_directions=self._ray_directions_w_flat,
ray_worlds=self._ray_worlds,
enable_global_world=not bool(getattr(self.cfg, "global_world_only", False)),
out_dist=self._hit_dist,
out_normal=self._hit_normal,
)
wp.launch(
_resolve_bvh_hits_kernel,
dim=(self._num_envs, self.num_rays),
inputs=[
self._is_outdated,
self._ray_starts_w,
self._ray_directions_w,
self._hit_dist,
self._hit_normal,
float(self.cfg.max_distance),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Broad except RuntimeError may swallow real configuration errors

The except RuntimeError block is meant to handle the case where _resolve_rigid_body_ancestor_expr() fails because the USD prim doesn't exist yet. However, it catches every RuntimeError from that call, including errors that indicate genuine misconfiguration (e.g., a body lookup failure caused by a malformed prim path or an unsupported prim type). In those cases the fallback silently uses prim_expr as the body expression, which can produce a Newton site attached to the wrong ancestor, leading to incorrect sensor poses at runtime with no warning. Consider catching a narrower exception type, or at minimum logging the swallowed error so it is visible during debugging.

Comment on lines 341 to +360
def render(self, render_data: RenderData):
"""Render and write to output buffers. See :meth:`~isaaclab.renderers.base_renderer.BaseRenderer.render`."""

newton_state: newton.State = NewtonManager.get_state()
# Refresh the shadow state under PhysX before the manager refits the BVH.
NewtonManager.get_state()
if render_data.sensor_task_name is None:
render_data.sensor_task_name = f"newton_warp_render:{id(render_data)}"
NewtonManager._register_sensor_task(render_data.sensor_task_name, lambda: self._launch_render(render_data))
NewtonManager._update_sensor_tasks(render_data.sensor_task_name)

# Refit the shape BVH against the current state since env body poses move every frame.
# ``build_bvh_shape`` ran once in ``__init__``; ``refit_bvh_shape`` reuses that topology.
if self.newton_sensor.model.shape_count > 0:
newton.geometry.refit_bvh_shape(self.newton_sensor.model, newton_state)
# Post-render PPISP: HDR scene-linear → LDR RGBA. Source/destination
# tensors were bound once in ``set_outputs``.
if render_data.ppisp_pipeline is not None:
render_data.ppisp_pipeline.apply(
render_data._ppisp_hdr_source,
render_data._ppisp_rgba_dest,
)

def _launch_render(self, render_data: RenderData) -> None:
"""Launch the tiled-camera render kernels for sensor graph capture."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 PPISP applied immediately after async graph launch — stream ordering dependency

NewtonManager._update_sensor_tasks in CUDA-graph mode calls wp.capture_launch, which is asynchronous on the host: the render graph is queued on the warp stream but the host continues immediately. The PPISP apply() call on the lines that follow consumes the rendered HDR buffer. If ppisp_pipeline.apply() is backed exclusively by warp/CUDA operations on the same stream, ordering is guaranteed by the stream. If it dispatches to a different stream or to synchronous CPU code that then reads GPU memory without an explicit wp.synchronize_stream, the apply may begin before the render graph has produced its output. The old code called newton_sensor.update() eagerly before PPISP, so this ordering was implicit. Consider adding a comment that makes the stream dependency explicit, or inserting a stream synchronization point before PPISP when the graph path is active.

debug_vis=False,
global_world_only=True,
)
newton_kamino = newton_mjwarp

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I see, I agree it looks a bit ugly, I guess its fine for now we can think of better solution

@StafaH StafaH changed the title Mh/newton bvh update Newton raycaster + bvh refactor Jul 20, 2026
StafaH and others added 3 commits July 19, 2026 22:09
The Newton BVH ray caster PR repoints the Newton backend of
RayCasterCamera, MultiMeshRayCaster, and MultiMeshRayCasterCamera to
their Legacy warp-mesh classes, so the isaaclab package needs its own
changelog fragment. Add a patch-level Changed entry; the change is
transparent to users of the backend-dispatching sensors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants