Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
54d6485
feat: add indexed fabric transform kernels for local/world matrix pro…
pv-nvidia May 18, 2026
f54776d
test: convert fabric kernel tests to plain functions (no classes)
pv-nvidia May 21, 2026
e4bced7
test: rewrite fabric kernel tests with wp.array (no Fabric runtime deps)
pv-nvidia May 21, 2026
3ff0100
refactor: extract world↔local math into @wp.func for testability
pv-nvidia May 21, 2026
4723bec
refactor: extract world↔local math into @wp.func for testability
pv-nvidia May 21, 2026
79e94e6
refactor: inline @wp.func helpers, add __all__, sync with full-stack
pv-nvidia May 22, 2026
bc196b7
Revert "refactor: inline @wp.func helpers, add __all__, sync with ful…
pv-nvidia May 22, 2026
32828af
docs: update changelog wording ('Will be used by' → 'Used by')
pv-nvidia May 22, 2026
d764352
docs: fix changelog wording (Used by → Will be used by)
pv-nvidia May 23, 2026
6fc7653
revert: remove wp.where refactor (unrelated to this PR)
pv-nvidia May 23, 2026
96dd33e
test: replace scipy with warp builtins in fabric kernel test helper
pv-nvidia May 23, 2026
cae710a
docs: remove dead ThreeDWorld link (domain squatted)
pv-nvidia May 23, 2026
c7ec169
feat: Fabric-accelerated get/set_local_poses via indexedfabricarray
pv-nvidia May 25, 2026
d31a0aa
refactor: remove kernel unit tests, consolidate changelogs for combin…
pv-nvidia May 25, 2026
91bc10a
refactor: inline @wp.func helpers into production kernels
pv-nvidia May 26, 2026
72f8c27
feat: deprecate set/get_scales, add explicit local/world scale API
pv-nvidia May 26, 2026
88517d3
fix: UsdFrameView.set/get_world_scales Gf.Vec3d type errors
pv-nvidia May 26, 2026
098dc5f
cleanup: remove redundant get/set_scales overrides from subclasses
pv-nvidia May 26, 2026
4da0317
fix: review improvements to scale API refactoring
May 30, 2026
5259467
feat: return ProxyArray from scale getters, add scale contract tests,…
May 30, 2026
a21e839
cleanup: remove derived-class details from BaseFrameView docstrings
Jun 1, 2026
0a4f79a
cleanup: rename _get/set_scales_default to _impl, warn only once
Jun 1, 2026
510588c
revert: restore docs/source/setup/ecosystem.rst to upstream state
Jun 1, 2026
bf132bb
cleanup: update stale set_scales/get_scales references in FabricFrame…
Jun 1, 2026
d6d26d3
fix: reset newton_site_frame_view.py to upstream + minimal scale addi…
Jun 2, 2026
9619f5c
feat: add tests for world and local scale conversions under scaled pa…
pv-nvidia Jun 2, 2026
958ff09
test: fix FrameView scale contracts
Jun 3, 2026
1808331
style: format FrameView scale contract test
Jun 4, 2026
721a235
bench: include FrameView scale operations
pv-nvidia Jun 4, 2026
ff467e6
fix: preserve ProxyArray get_scales contract
pv-nvidia Jun 4, 2026
2e946cc
docs: remove ProxyArray scale return attribution
pv-nvidia Jun 4, 2026
9c5e638
fix: preserve Newton legacy scale semantics
pv-nvidia Jun 4, 2026
688411b
style: format Newton scale type annotation
pv-nvidia Jun 4, 2026
29b3202
fix: flush Fabric world matrices after local writes
pv-nvidia Jun 4, 2026
342d9b2
style: apply pre-commit formatting
pv-nvidia Jun 4, 2026
54b78c6
feat: configure Fabric change-block matrix flushes
pv-nvidia Jun 5, 2026
44d0905
Restore three-selection RO/RW design, drop eager world<->local flushes
pv-nvidia Jun 5, 2026
38fb1d0
docs: clarify Fabric getter synchronization
pv-nvidia Jun 6, 2026
c6cbd3c
Introduce FrameViewSpaceWriter context API; drop dirty tracking
pv-nvidia Jun 9, 2026
6a42f2a
refactor: split xform_space_writer(space) into xform_world_space_writ…
pv-nvidia Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/source/api/lab/isaaclab.utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,16 @@ Warp operations
:members:
:imported-members:
:show-inheritance:

Warp Fabric kernels
^^^^^^^^^^^^^^^^^^^

Warp kernels for reading and writing Fabric ``Matrix4d`` attributes
(``omni:fabric:worldMatrix`` / ``omni:fabric:localMatrix``) via
:class:`wp.fabricarray` and :class:`wp.indexedfabricarray`. Will be used by
:class:`~isaaclab_physx.sim.views.FabricFrameView` to keep child world and
local matrices consistent without round-tripping through USD.

.. automodule:: isaaclab.utils.warp.fabric
:members:
:show-inheritance:
3 changes: 2 additions & 1 deletion scripts/benchmarks/benchmark_view_comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@ def _run_pose_benchmarks(

start_time = time.perf_counter()
for _ in range(num_iterations):
view.set_world_poses(new_positions, orientations)
with view.xform_world_space_writer() as w:
w.set_poses(new_positions, orientations)
timing_results["set_world_poses"] = (time.perf_counter() - start_time) / num_iterations

ret_pos, ret_quat = view.get_world_poses()
Expand Down
90 changes: 84 additions & 6 deletions scripts/benchmarks/benchmark_xform_prim_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,16 @@ def benchmark_frame_view( # noqa: C901
is_newton = api == "isaaclab-newton-site"

def to_torch(a):
return wp.to_torch(a) if isinstance(a, wp.array) else a
if isinstance(a, wp.array):
return wp.to_torch(a)
if hasattr(a, "torch"):
return a.torch
return a

try:
# -- Warmup --------------------------------------------------------
xform_view.get_world_poses()
xform_view.get_world_scales()

# -- get_world_poses -----------------------------------------------
if is_newton:
Expand All @@ -162,7 +167,7 @@ def to_torch(a):

# -- set_world_poses -----------------------------------------------
if is_newton:
new_positions = wp.clone(positions)
new_positions = wp.clone(positions.warp)
wp.to_torch(new_positions)[:, 2] += 0.1
else:
new_positions = positions_t.clone()
Expand All @@ -172,7 +177,8 @@ def to_torch(a):
torch.cuda.synchronize()
start_time = time.perf_counter()
for _ in range(num_iterations):
xform_view.set_world_poses(new_positions, orientations)
with xform_view.xform_world_space_writer() as w:
w.set_poses(new_positions, orientations)
if is_newton:
torch.cuda.synchronize()
timing_results["set_world_poses"] = (time.perf_counter() - start_time) / num_iterations
Expand All @@ -198,7 +204,7 @@ def to_torch(a):

# -- set_local_poses -----------------------------------------------
if is_newton:
new_translations = wp.clone(translations)
new_translations = wp.clone(translations.warp)
wp.to_torch(new_translations)[:, 2] += 0.1
else:
new_translations = translations_t.clone()
Expand All @@ -208,7 +214,8 @@ def to_torch(a):
torch.cuda.synchronize()
start_time = time.perf_counter()
for _ in range(num_iterations):
xform_view.set_local_poses(new_translations, orientations_local)
with xform_view.xform_local_space_writer() as w:
w.set_poses(new_translations, orientations_local)
if is_newton:
torch.cuda.synchronize()
timing_results["set_local_poses"] = (time.perf_counter() - start_time) / num_iterations
Expand All @@ -217,6 +224,72 @@ def to_torch(a):
computed_results["local_translations_after_set"] = to_torch(ta).clone()
computed_results["local_orientations_after_set"] = to_torch(ola).clone()

# -- get_world_scales ----------------------------------------------
if is_newton:
torch.cuda.synchronize()
start_time = time.perf_counter()
for _ in range(num_iterations):
world_scales = xform_view.get_world_scales()
if is_newton:
torch.cuda.synchronize()
timing_results["get_world_scales"] = (time.perf_counter() - start_time) / num_iterations

world_scales_t = to_torch(world_scales)
computed_results["initial_world_scales"] = world_scales_t.clone()

# -- set_world_scales ----------------------------------------------
if is_newton:
new_world_scales = wp.clone(world_scales.warp)
wp.to_torch(new_world_scales)[:] = 1.1
else:
new_world_scales = world_scales_t.clone()
new_world_scales[:] = 1.1

if is_newton:
torch.cuda.synchronize()
start_time = time.perf_counter()
for _ in range(num_iterations):
with xform_view.xform_world_space_writer() as w:
w.set_scales(new_world_scales)
if is_newton:
torch.cuda.synchronize()
timing_results["set_world_scales"] = (time.perf_counter() - start_time) / num_iterations

computed_results["world_scales_after_set"] = to_torch(xform_view.get_world_scales()).clone()

# -- get_local_scales ----------------------------------------------
if is_newton:
torch.cuda.synchronize()
start_time = time.perf_counter()
for _ in range(num_iterations):
local_scales = xform_view.get_local_scales()
if is_newton:
torch.cuda.synchronize()
timing_results["get_local_scales"] = (time.perf_counter() - start_time) / num_iterations

local_scales_t = to_torch(local_scales)
computed_results["initial_local_scales"] = local_scales_t.clone()

# -- set_local_scales ----------------------------------------------
if is_newton:
new_local_scales = wp.clone(local_scales.warp)
wp.to_torch(new_local_scales)[:] = 0.9
else:
new_local_scales = local_scales_t.clone()
new_local_scales[:] = 0.9

if is_newton:
torch.cuda.synchronize()
start_time = time.perf_counter()
for _ in range(num_iterations):
with xform_view.xform_local_space_writer() as w:
w.set_scales(new_local_scales)
if is_newton:
torch.cuda.synchronize()
timing_results["set_local_scales"] = (time.perf_counter() - start_time) / num_iterations

computed_results["local_scales_after_set"] = to_torch(xform_view.get_local_scales()).clone()

# -- get_both (world + local) --------------------------------------
if is_newton:
torch.cuda.synchronize()
Expand All @@ -233,7 +306,8 @@ def to_torch(a):
torch.cuda.synchronize()
start_time = time.perf_counter()
for _ in range(num_iterations):
xform_view.set_world_poses(new_positions, orientations)
with xform_view.xform_world_space_writer() as w:
w.set_poses(new_positions, orientations)
xform_view.get_world_poses()
if is_newton:
torch.cuda.synchronize()
Expand Down Expand Up @@ -273,6 +347,10 @@ def print_results(results_dict: dict[str, dict[str, float]], num_prims: int, num
("Set World Poses", "set_world_poses"),
("Get Local Poses", "get_local_poses"),
("Set Local Poses", "set_local_poses"),
("Get World Scales", "get_world_scales"),
("Set World Scales", "set_world_scales"),
("Get Local Scales", "get_local_scales"),
("Set Local Scales", "set_local_scales"),
("Get Both (World+Local)", "get_both"),
("Interleaved World Set->Get", "interleaved_world_set_get"),
]
Expand Down
28 changes: 28 additions & 0 deletions source/isaaclab/changelog.d/fabric-local-poses.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
Added
^^^^^

* Added explicit local/world scale methods
:meth:`~isaaclab.sim.views.BaseFrameView.get_local_scales`,
:meth:`~isaaclab.sim.views.BaseFrameView.set_local_scales`,
:meth:`~isaaclab.sim.views.BaseFrameView.get_world_scales`, and
:meth:`~isaaclab.sim.views.BaseFrameView.set_world_scales` to the FrameView
API, implemented for :class:`~isaaclab.sim.views.UsdFrameView`.

* Added :func:`~isaaclab.utils.warp.fabric.decompose_indexed_fabric_transforms`,
:func:`~isaaclab.utils.warp.fabric.compose_indexed_fabric_transforms`,
:func:`~isaaclab.utils.warp.fabric.update_indexed_local_matrix_from_world`, and
:func:`~isaaclab.utils.warp.fabric.update_indexed_world_matrix_from_local`
Warp kernels operating on :class:`wp.indexedfabricarray` for reading and
writing Fabric ``Matrix4d`` attributes (``omni:fabric:worldMatrix`` /
``omni:fabric:localMatrix``).

Deprecated
^^^^^^^^^^

* Deprecated :meth:`~isaaclab.sim.views.BaseFrameView.get_scales` and
:meth:`~isaaclab.sim.views.BaseFrameView.set_scales` in favor of the explicit
``get_local_scales`` / ``set_local_scales`` (operates on ``xformOp:scale``) or
``get_world_scales`` / ``set_world_scales`` (operates on composed world-space
scale). The deprecated methods still work but emit a ``DeprecationWarning``;
:class:`~isaaclab.sim.views.UsdFrameView` preserves prior behavior by
defaulting to local scales.
41 changes: 41 additions & 0 deletions source/isaaclab/changelog.d/xform-space-writer.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Added
^^^^^

* Added :class:`~isaaclab.sim.views.FrameViewSpaceWriterBase`, the new context-managed
write API for ``FrameView``-managed prim transforms. Open with
``view.xform_world_space_writer()`` or ``view.xform_local_space_writer()`` and call
:meth:`~isaaclab.sim.views.FrameViewSpaceWriterBase.set_poses` /
:meth:`~isaaclab.sim.views.FrameViewSpaceWriterBase.set_scales` inside the scope;
the writer's ``__exit__`` derives the opposite-space matrices once and
synchronizes once. Only one writer scope may be active per view at a
time. View-level getters
(:meth:`~isaaclab.sim.views.BaseFrameView.get_world_poses` etc.) raise
:class:`RuntimeError` while a writer scope is active.

* Added the two concrete tag classes
:class:`~isaaclab.sim.views.FrameViewWorldSpaceWriter` and
:class:`~isaaclab.sim.views.FrameViewLocalSpaceWriter` returned by
:meth:`~isaaclab.sim.views.BaseFrameView.xform_world_space_writer` /
:meth:`~isaaclab.sim.views.BaseFrameView.xform_local_space_writer`.

Deprecated
^^^^^^^^^^

* Deprecated :meth:`~isaaclab.sim.views.BaseFrameView.set_world_poses` and
:meth:`~isaaclab.sim.views.BaseFrameView.set_local_poses`. Use
``with view.xform_world_space_writer() as w: w.set_poses(...)`` (or
:meth:`~isaaclab.sim.views.BaseFrameView.xform_local_space_writer`)
instead. The deprecated methods still work but emit a one-time
``DeprecationWarning`` per class and open a single-statement writer scope
internally.

Removed
^^^^^^^

* **Breaking:** Removed ``set_world_scales`` and ``set_local_scales``
from :class:`~isaaclab.sim.views.BaseFrameView` (and all subclasses).
These were introduced in this release cycle without a stable downstream
user, so they are removed outright (no deprecation cycle). Use
``with view.xform_world_space_writer() as w: w.set_scales(...)`` (or
:meth:`~isaaclab.sim.views.BaseFrameView.xform_local_space_writer`)
instead.
14 changes: 8 additions & 6 deletions source/isaaclab/isaaclab/sensors/camera/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,8 @@ def set_world_poses(
orientations = convert_camera_frame_orientation_convention(orientations, origin=convention, target="opengl")
ori_wp = wp.from_torch(orientations.contiguous(), dtype=wp.vec4f)
idx_wp = self._resolve_env_ids_wp(env_ids)
self._view.set_world_poses(pos_wp, ori_wp, idx_wp)
with self._view.xform_world_space_writer() as writer:
writer.set_poses(pos_wp, ori_wp, idx_wp)

def set_world_poses_from_view(
self, eyes: torch.Tensor, targets: torch.Tensor, env_ids: Sequence[int] | None = None
Expand Down Expand Up @@ -434,11 +435,12 @@ def set_world_poses_from_view(
env_ids_torch = env_ids_torch.index_select(0, valid_indices)
orientations = quat_from_matrix(rotation_matrix)
idx_wp = wp.from_torch(env_ids_torch.contiguous(), dtype=wp.int32)
self._view.set_world_poses(
wp.from_torch(eyes.contiguous(), dtype=wp.vec3f),
wp.from_torch(orientations.contiguous(), dtype=wp.vec4f),
idx_wp,
)
with self._view.xform_world_space_writer() as writer:
writer.set_poses(
wp.from_torch(eyes.contiguous(), dtype=wp.vec3f),
wp.from_torch(orientations.contiguous(), dtype=wp.vec4f),
idx_wp,
)

"""
Operations
Expand Down
4 changes: 4 additions & 0 deletions source/isaaclab/isaaclab/sim/views/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ __all__ = [
"BaseFrameView",
"UsdFrameView",
"FrameView",
"FrameViewSpaceWriterBase",
"FrameViewWorldSpaceWriter",
"FrameViewLocalSpaceWriter",
# Deprecated alias
"XformPrimView",
]

from .base_frame_view import BaseFrameView
from .usd_frame_view import UsdFrameView
from .frame_view import FrameView
from .xform_space_writer import FrameViewSpaceWriterBase, FrameViewWorldSpaceWriter, FrameViewLocalSpaceWriter
# Deprecated alias
from .xform_prim_view import XformPrimView
Loading
Loading