Skip to content

refactor: fuse set_world_poses/set_scales into single _compose_fabric_transform#5674

Draft
pv-nvidia wants to merge 3 commits into
isaac-sim:developfrom
pv-nvidia:pv/fabric-fused-compose
Draft

refactor: fuse set_world_poses/set_scales into single _compose_fabric_transform#5674
pv-nvidia wants to merge 3 commits into
isaac-sim:developfrom
pv-nvidia:pv/fabric-fused-compose

Conversation

@pv-nvidia
Copy link
Copy Markdown

@pv-nvidia pv-nvidia commented May 18, 2026

Problem

FabricFrameView had duplicated kernel-launch logic in set_world_poses and set_scales, and the initial USD→Fabric sync called both methods sequentially. This meant:

  1. Duplicated code — the same 20-line kernel launch pattern appeared twice with minor variations (positions/orientations vs scales).
  2. Double PrepareForReuse — the initial USD→Fabric sync in _sync_fabric_from_usd_once called set_world_poses then set_scales, each invoking PrepareForReuse. A second non-idempotent PrepareForReuse call could mask a topology-change signal that should have triggered a fabricarray rebuild.

Solution

Extract _compose_fabric_transform(positions=None, orientations=None, scales=None, indices=None) — a single method that composes any subset of transform components into one kernel launch. Components left as None are skipped via empty arrays.

  • set_world_poses → delegates to _compose_fabric_transform(positions=..., orientations=...)
  • set_scales → delegates to _compose_fabric_transform(scales=...)
  • _sync_fabric_from_usd_once → single fused call with all three components

Additional fix

The topology-change invariant guard in _rebuild_fabric_arrays used assert, which is stripped under python -O. Replaced with raise RuntimeError so it's always active.

Tests

All 36 existing Fabric tests pass (+ 2 xfail).


Merge Order

This PR is part of a stacked series for Fabric-accelerated local poses:

  1. feat: add typed service locator to SimulationContext #5672 — service locator ← this depends on
  2. feat: add FabricStageCache service for shared hierarchy handles #5676 — FabricStageCache
  3. feat: add indexed fabric transform kernels for local/world matrix propagation #5675 — indexed fabric kernels
  4. refactor: move Fabric/USD dispatch from FabricFrameView to FrameView factory #5673 — factory dispatch
  5. refactor: fuse set_world_poses/set_scales into single _compose_fabric_transform #5674 — fused compose ← this PR (depends on feat: add typed service locator to SimulationContext #5672)
  6. PR C+D — local poses (depends on all above)

Copy link
Copy Markdown

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Choose a reason for hiding this comment

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

🔍 Code Review Summary

This PR successfully refactors FabricFrameView to consolidate duplicate kernel-launch logic into a single _compose_fabric_transform method and addresses a critical assertion bug. The changes are well-structured and improve both correctness and maintainability.


✅ Correctness

No blocking issues found.

The refactoring maintains behavioral equivalence:

  • _compose_fabric_transform correctly handles None parameters via empty arrays, preserving the kernel's ability to skip unspecified components
  • The _fabric_usd_sync_done flag is set correctly after writes
  • The assertRuntimeError change in _rebuild_fabric_arrays is a valuable fix — assertions are stripped under python -O, which could silently produce wrong poses or out-of-bounds kernel indices in production

Minor observation:

  • The _compose_fabric_transform method always calls _fabric_hierarchy.update_world_xforms() even when only scales are updated. This matches the original set_scales behavior, so no regression, but worth noting for future optimization.

⚡ Performance

Net positive impact:

  1. Reduced PrepareForReuse calls — The critical fix: _sync_fabric_from_usd_once now invokes PrepareForReuse exactly once (via the fused compose) instead of twice. This prevents potential topology-change signal masking.

  2. Single kernel launch for initial sync — USD→Fabric sync now uses one wp.launch instead of two, reducing kernel launch overhead.

Micro-optimization opportunity (non-blocking):

# Current: creates empty arrays every call
empty3 = wp.zeros((0, 3), dtype=wp.float32, device=self._device)
empty4 = wp.zeros((0, 4), dtype=wp.float32, device=self._device)

These could be cached as class-level constants, but the overhead is negligible for zero-sized arrays.


📝 Maintainability

Excellent improvements:

  1. Code deduplication — ~40 lines of duplicate kernel-launch boilerplate consolidated into one reusable method
  2. Clearer namingempty3/empty4 communicates intent better than dummy3/dummy4
  3. Good documentation — The _compose_fabric_transform docstring clearly explains the single-PrepareForReuse guarantee and the _fabric_usd_sync_done side effect
  4. Test modernizationwp.to_torch(fab_pos)fab_pos.torch uses the current ProxyArray API

Changelogs are well-formatted and accurately describe both the behavioral changes and the assert fix.


🔗 Service Locator (Bundled Feature)

The typed service locator in SimulationContext is a clean pattern:

  • Type-safe via TypeVar
  • Automatic close() on clear_instance()
  • Good test coverage for edge cases (re-registration, missing close method, etc.)

One consideration: set_service does not auto-close the replaced service (caller responsibility per docstring). This is documented but could be a subtle footgun. Consider adding an optional auto_close=True parameter in the future if replacement becomes common.


Verdict: Approve

The refactoring achieves its goals cleanly:

  • Eliminates duplicate code
  • Fixes a real bug (assert under -O)
  • Reduces PrepareForReuse call count
  • Adds useful infrastructure (service locator)

All 36 existing Fabric tests pass per PR description. Ship it! 🚀

@pv-nvidia pv-nvidia force-pushed the pv/fabric-fused-compose branch from 2ca6310 to 1b12c6c Compare May 18, 2026 13:54
pv-nvidia added 3 commits May 18, 2026 17:06
Add get_service(cls) / set_service(cls, instance) — a lightweight typed
singleton registry on SimulationContext, keyed by service class.

This lets backend-specific caches (e.g. Fabric hierarchy handles) register
themselves without polluting SimulationContext with backend-specific fields
or imports.  Services with a close() method are automatically closed:
- On replacement via set_service()
- On teardown via clear_instance()

No existing behavior changes — this is purely additive.
- 7 unit tests covering get_service, set_service, replacement lifecycle,
  close-on-clear_instance, multiple service types, and idempotent re-registration
- Changelog entry for the new service locator API
Extract _compose_fabric_transform() to deduplicate the kernel-launch
logic shared by set_world_poses and set_scales. The initial USD->Fabric
sync now composes position, orientation, and scale in one call, so
PrepareForReuse is invoked exactly once per logical update.

Also replace assert with RuntimeError in _rebuild_fabric_arrays so the
topology-change guard survives python -O.
@pv-nvidia pv-nvidia force-pushed the pv/fabric-fused-compose branch from 1b12c6c to aad8a83 Compare May 18, 2026 17:10
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.

1 participant