Repository navigation
[Backport release/3.0.0-beta2] FrameView local poses (#5677) + view-scoped Fabric selections (#6805) - #7342
Conversation
…r API (isaac-sim#5677) Two things: 1. **Local-space pose and scale ops are now GPU-fast on the Fabric backend.** They go through the same Warp-kernel path that world-space ops already used. The old USD fallback for local-space is ~100× slower (see the table below). 2. **All transform writes go through a small context manager.** You open a scope, do your writes inside it, and the scope handles the cleanup: ```python with view.xform_world_space_writer() as writer: writer.set_poses(positions=p, orientations=o) writer.set_scales(scales=s) ``` Use `view.xform_local_space_writer()` for local-space writes. Builds on Piotr's prototype at [`bareya/pbarejko/camera-update`](https://github.com/bareya/IsaacLab/tree/pbarejko/camera-update). A FrameView keeps two copies of each prim's transform in Fabric storage: the `omni:fabric:worldMatrix` and `omni:fabric:localMatrix` attributes. When you write one, the other has to be recomputed so they stay consistent. Without a scope, that recompute has to run after every single write. With a scope, it runs **once**, when the scope closes. You can call `set_poses` and `set_scales` as many times as you want inside the scope; on exit, one Warp kernel derives the other space and one `wp.synchronize()` runs. Empty scopes cost nothing. The scope also pauses **Fabric Hierarchy**'s transform-change tracking while you're writing, then restores it on exit. (Fabric itself is just the data store; Fabric Hierarchy, exposed through USDRT's `IFabricHierarchy`, is the plugin that watches writes to `omni:fabric:localMatrix` / `omni:fabric:worldMatrix` and keeps them mutually consistent across the hierarchy.) Its tracking is pull-based: a per-attribute listener records writes into a changelog, and the plugin drains and processes that changelog on the next call to `IFabricHierarchy::update_world_xforms()`. Pausing the listener does not block the writes themselves — they still land in Fabric storage — it just keeps them out of the changelog. With the changelog empty, the next tick has nothing to "catch up on" for these prims, and can't decide one of the two spaces is canonical and derive the other from it. See the next section for why that matters. Rules: - Only one writer can be open on a view at a time. Opening a second one raises `RuntimeError`. - While a writer is open, the view's own getters (`get_world_poses`, etc.) raise. Read through the writer (`writer.get_poses()`) inside the scope, or close the scope first. - **Do not advance the simulation or render from inside the scope.** No `sim.step()`, no `world.render()`, no `SimulationApp.update()`. The matrices are mid-write until exit, and rendering against that state would read torn data. Keep scopes short and close them before stepping. - If something raises inside the scope (a user-code bug, a notebook cell interrupt, etc.), the scope still runs the opposite-space recompute on exit so the world and local matrices stay consistent prim-by-prim. The partial write itself is not rolled back — if you need all-or-nothing, snapshot the matrices yourself before entering. correctness) A writer scope is synchronous Python code, so the renderer can't see anything mid-scope — no tick runs until we exit. The risk is on the **next** render/sim tick after the scope closes. On that next tick, Fabric Hierarchy runs its `IFabricHierarchy::update_world_xforms()` step, which can recompute `omni:fabric:worldMatrix` from `omni:fabric:localMatrix` (or vice versa). If it thinks one of the two was the user's most recent edit, it derives the other from it; the derived value — not what we wrote — is then what the **Fabric Scene Delegate (FSD)** hands to RTX on the render path. Three things in this PR keep that next-tick recompute from clobbering our work: 1. **Opposite-space derive at scope exit.** By the time the scope closes, `omni:fabric:worldMatrix` and `omni:fabric:localMatrix` are mutually consistent prim-by-prim, so any recompute Fabric Hierarchy does is a no-op. 2. **Fabric-Hierarchy change tracking is paused for the whole scope.** The writer calls `track_local_xform_changes(False)` and `track_world_xform_changes(False)` on enter (saving the prior state) and restores them on exit. While tracking is paused, the listeners don't record our writes in their changelog, so when the next tick runs there's nothing for `update_world_xforms()` to "catch up on". 3. **Two persistent selections with explicit read-only / read-write flags.** Outside the scope, the view's selection is fully read-only — both `omni:fabric:worldMatrix` and `omni:fabric:localMatrix` are flagged RO. Inside the scope, the writer flips to a fully read-write selection. ``` _sel_ro : worldMatrix=RO, localMatrix=RO (steady state, between writes) _sel_rw : worldMatrix=RW, localMatrix=RW (active during a writer scope) ``` Both selections are built once when the view is initialized and kept alive for its lifetime. The writer scope flips a single flag (`_is_rw`) on enter/exit; nothing is rebuilt. The RO steady state tells Fabric Hierarchy's next-tick `update_world_xforms()` that no attribute is user-authored, so it leaves both alone. - **Fabric (PhysX):** the fast GPU path described above. - **USD, Newton, OVPhysX:** the writer is a thin wrapper. Writes go straight to the backend's existing storage. No batching, no extra recompute (there is no second matrix to keep in sync). Same API across all four backends. Newton has two different "scale" ideas, and the API keeps them apart: - `NewtonSiteFrameView.set_scales(...)` (deprecated) writes the **Newton collision-shape geometry** size. - The writer's `set_scales(...)` writes per-site **transform scale**, matching what USD FrameView does. They operate on different state and are not merged. **New (recommended):** - `view.xform_world_space_writer()` / `view.xform_local_space_writer()` — a context manager with `set_poses`, `set_scales`, `get_poses`, `get_scales`. **Deprecated (still works, warns once per class on first use):** - `set_world_poses` / `set_local_poses` — use the writer scope. - `get_scales` / `set_scales` — use `get_local_scales` / `get_world_scales`, or the writer's `set_scales`. Per-iteration timings (lower is better): | Operation | USD (ms) | Fabric (ms) | Newton Site (ms) | |----------------------------|---------:|------------:|-----------------:| | Get World Poses | 8.6148 | 0.0375 | 0.0307 | | Set World Poses | 19.1357 | 0.1090 | 0.0368 | | Get Local Poses | 5.9556 | 0.0374 | 0.0497 | | Set Local Poses | 7.9794 | 0.0979 | 0.0790 | | Get World Scales | 13.1193 | 0.0371 | 0.0015 | | Set World Scales | 21.4571 | 0.1014 | 0.0612 | | Get Local Scales | 3.2544 | 0.0375 | 0.0022 | | Set Local Scales | 3.8350 | 0.0937 | 0.0587 | | Get Both (World+Local) | 14.7506 | 0.0738 | 0.1302 | | Interleaved World Set→Get | 28.1046 | 0.1432 | 0.1588 | | **Per-iter total** | **126.2** | **0.77** | **0.61** | Speedup vs USD (per-iter ops; one-time view construction excluded): | Operation | Fabric × | Newton Site × | |----------------------------|---------:|--------------:| | Get World Poses | 229.6× | 280.2× | | Set World Poses | 175.6× | 519.8× | | Get Local Poses | 159.0× | 119.9× | | Set Local Poses | 81.5× | 101.0× | | Get World Scales | 353.9× | 8911.5× | | Set World Scales | 211.6× | 350.4× | | Get Local Scales | 86.8× | 1489.9× | | Set Local Scales | 40.9× | 65.4× | | Get Both (World+Local) | 200.0× | 113.3× | | Interleaved World Set→Get | 196.2× | 177.0× | | **Overall (per-iter)** | **164×** | **207×** | One-time view construction (reported separately, not part of the per-iter total): USD 4.6 ms, Fabric 4.4 ms, Newton Site 1013 ms — the Newton number is dominated by stage population on the first call. Steady-state per-iteration cost is what the speedup row reflects. - Getters call `wp.synchronize()` before returning, so the returned `ProxyArray` is always immediately readable from both GPU and host code — no caller-side sync needed. (Both the cached and the per-indices paths sync; this used to be asymmetric.) --------- Co-authored-by: pv-nvidia <peter.verswyvelen@gmail.com> Co-authored-by: pv-nvidia <{"message":"Not Found","documentation_url":"https://docs.github.com/rest/users/emails#list-email-addresses-for-the-authenticated-user","status":"404"}>
…aac-sim#6805) Camera world-pose resolution stalled at high environment counts on the PhysX backend, badly enough to time out benchmarks. There are two independent bottlenecks; the PR also adds a lifecycle hook that came out of review. whole stage Cameras read their poses from Fabric. To do that the frame view needs to know where each camera's data sits in Fabric memory, so it builds a lookup table from "camera number" to "Fabric slot". Building that table was very slow. The view selected prims by requiring the Fabric world and local matrix attributes — but every xformable in the stage carries those, so the selection matched the **entire stage** (~1.1M prims at 8192 environments), not the view's handful of cameras. Finding its own prims in that list then meant building a Python dictionary over all of it, on the host: ```python path_to_idx = {str(p): i for i, p in enumerate(selection.GetPaths())} ``` That ran twice per rebuild (once for the children, once for their parents), and rebuilt whenever `PrepareForReuse()` reported a bucket change — in practice on every environment reset. The cost of that dictionary scales with the size of the whole scene, not with the number of cameras: it converts every path in the stage to a Python string and inserts it. Measured directly, one full rebuild takes 10 ms on a bare stage but **556 ms once 100k unrelated prims are present** (table below), and it grows linearly from there — seconds of host work per reset at the ~1.1M prims of an 8192-environment scene. **Fix:** author a private per-view `uint` index attribute on each managed prim (and on each unique parent), holding that prim's view index. Every selection requires the matching index attribute, so **selections resolve to exactly the view's prims**. The view-to-Fabric slot mapping is then rebuilt by inverting that attribute in a single Warp kernel launch: ```python fabric_slots[view_indices[fabric_slot]] = fabric_slot ``` That is `O(view)` device work with no host-side path resolution and no Python objects created per frame. There is also **no cache**, so there is nothing to invalidate. The table is rebuilt from live Fabric data on every access, which means a bucket reorder can never leave a stale mapping behind. Selections are still checked with `GetCount()`, which is exact here: the index attribute belongs to one view, so the count can only change if one of that view's prims actually disappeared — and then the view raises a clear error instead of silently reading the wrong prim. Attribute names embed a process-wide monotonic uid, so a dead view's leftover attributes can never satisfy a live view's selection — and since review, they are removed outright when the view is released (section 3). Parent reads get their own read-only selection, keeping the child RO/RW flip semantics from isaac-sim#5677 intact. ```python for i, path in enumerate(input_paths): mapping[i] = paths.index(path) # O(N) scan, per path ``` **Fix:** build the reverse dict once (first occurrence wins, matching `list.index` semantics), then resolve each path in `O(1)`. Measured over 200k paths, preserving first-occurrence semantics costs nothing versus an unguarded dict comprehension, so the fix carries no behaviour change. review) The per-view index attributes from fix 1 were initially never removed, so views recreated over the same prims on a long-lived stage accumulated attribute pairs, widening those prims' Fabric buckets. Measured over 8 successive views on one stage (512 prims), attributes on a single prim went 1 → 8 and first-access rose monotonically from 120 ms to 173 ms. **Fix:** `FabricFrameView.close()` removes the view's tags (measured at 4.7 µs per prim, ~77 ms for an 8192-env view) and is safe to call more than once. `BaseFrameView` gains a no-op `close()` so callers can close any backend uniformly. Every in-repo callsite now closes its view: `Camera` when invalidated **and when dropped** (`__del__`), `prim_world_positions` after each per-env view it creates in a loop, and `SceneAsset` before replacing its cached view. Views dropped without `close()` are cleaned up best-effort from `__del__`, following the shutdown-safe idiom the env classes already use (`sys` bound as a default argument; nothing runs during interpreter finalization, where Fabric dies with the process anyway), and a warning names the view so the missing `close()` call can be fixed. The stall was invisible in Tracy. Comparing captures before and after the commit that introduced it, Kit's own per-frame work is unchanged — `App Update` totals 65.1 s before and 62.3 s after, over ~120 frames. All the lost time sits in the gaps *between* frames, where the main thread is blocked in Python and Kit records nothing. A `py-spy` capture found the cause: `_compute_fabric_indices_for` accounted for **11.86% of samples (23.2 s of a 195 s run)**, reached through `camera.reset()` → `get_world_poses()` → the selection rebuild. Every frame on that chain carries essentially the same share, so the whole cost is that one function. Fixes nvbug 6535498. - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) — `FrameView.close()` Task `Isaac-Lift-KukaAllegro-Camera`, 8192 environments, `presets=physx,isaacsim_rtx_renderer,duo_camera`. Commit `69888c34e471` (which introduced the problem) against its parent `e5f99d320338`: | | Parent | With the problem | | --- | ---: | ---: | | Mean environment step | 699 ms | 7193 ms | | Slow steps (over 3 s) | 1 of 100 | 28 of 100, averaging 18.9 s | | GPU utilization | 23.4% | 4.7% | GPU and CPU utilization both drop during the slow steps, which is what a stall looks like — the pipeline is waiting, not doing extra work. With this PR, the same task at 8192 environments steps at **mean 1.554 s, peak 1.663 s** — peak within 7% of mean, i.e. the stalls are gone. (Measured on an RTX A6000, so the absolute step time is not comparable to the table above, which was captured on different hardware; the stall signature is the comparison that matters.) L40, `cuda:0`, 1024-prim view (1024 children + 1024 parents). "Filler" is xformable prims **not** in the view but carrying Fabric matrices — i.e. the rest of a real scene. | filler prims | `develop` selection | this PR | | ---: | ---: | ---: | | 0 | 2,048 | 1,024 | | 10,000 | 12,048 | 1,024 | | 100,000 | 102,048 | **1,024** | `develop` scales with the stage; this PR is pinned to the view. | filler prims | `develop` child | `develop` parent | `develop` full RO rebuild | PR child | PR parent | | ---: | ---: | ---: | ---: | ---: | ---: | | 0 | 4.67 | 5.40 | 10.24 | 0.137 | 0.191 | | 10,000 | 24.05 | 24.69 | 62.47 | 0.139 | 0.193 | | 100,000 | 274.21 | 270.47 | **556.41** | **0.137** | **0.190** | `develop` grows linearly with stage size — **556 ms per rebuild** at 100k filler prims. This PR is flat at **~0.14 ms**, a **~2000×** reduction, and unchanged from 0 to 100k filler prims. First access at 100k filler: 1824 ms → 383 ms. | N | `develop` | this PR | speedup | | ---: | ---: | ---: | ---: | | 1,000 | 0.0038 s | 0.0001 s | 27× | | 5,000 | 0.0862 s | 0.0007 s | 120× | | 20,000 | 1.3241 s | 0.0030 s | 434× | | 50,000 | 8.5507 s | 0.0092 s | **929×** | | 200,000 | (not run) | 0.0467 s | — | Outputs verified identical at every N. At the ~200k rigid bodies of an 8192-environment scene the old path takes minutes. | Suite | Result | | --- | --- | | `isaaclab_physx/test/sim/test_views_xform_prim_fabric.py` | 79 passed, 4 skipped (`cpu` + `cuda:0` on an L40) | | `isaaclab/test/sim/test_views_xform_prim.py` (USD contract) | 63 passed | | `isaaclab_newton/test/sim/test_views_xform_prim_newton.py` | 58 passed | | `isaaclab/test/sensors/test_camera.py` (main consumer, incl. the new `close()` call) | 35 passed, 0 missing-`close()` warnings | | `isaaclab/test/utils/warp/test_proxy_array.py` | 81 passed | New tests: - `test_selections_match_only_the_view_prims` asserts each selection matches exactly the prims the view manages. Verified to fail without the fix — with the selections unscoped it reports `matched 8 prims, expected 4`, because the child selections pick up the parents too. - `test_close_removes_index_attributes` asserts the tag-scoped selection drops to zero after `close()` and that a second `close()` is a no-op. - `test_garbage_collection_removes_index_attributes_and_warns` asserts a view dropped without `close()` still removes its tags on collection and logs the warning. `test_fabric_rebuild_after_topology_change` was updated to drive the new refresh paths instead of the removed `_rebuild_{ro,rw}_arrays`. The 4 skips are environmental: empty device parameter sets, and Fabric hierarchy bindings that are unavailable in a headless experience. **Coverage gap:** `test_physx_scene_data_backend.py` and `test_ovphysx_scene_data_backend.py` both skip at collection on the machine used here, so the `create_mapping` change is exercised only by the standalone benchmark above, not by the test suite. Worth a look in CI, where those backends are available. Not applicable — no visual change. The benchmarks above cover the behaviour change. **Review feedback addressed so far:** the index-attribute accumulation is fixed by `close()` (section 3); the opposite-space recompute no longer refreshes the same selection twice on writer-scope exit; `create_mapping` keeps `list.index`'s first-occurrence semantics for duplicate paths (measured free at 200k paths); and docstrings/changelogs were trimmed to the observable contract. **The `isaaclab`/`isaaclab_physx` changelog fragments are still named `fix-physx-newton-camera-pose-scaling.rst`**, inherited from isaac-sim#6554 — nothing here touches Newton or scaling. The new `isaaclab_mimic` fragment uses the branch slug. Happy to rename if reviewers prefer. **A handful of tests still drop views without `close()`** and now log the new warning during runs; harmless, cleanup to follow. EDIT: fixed — the only remaining warning is the one `test_garbage_collection_removes_index_attributes_and_warns` asserts on. `Camera` previously closed its view only on invalidation, so a dropped camera still went through `__del__`; it now closes in `Camera.__del__` too. `test_camera.py` went from 3 warnings to 0. `SceneDataProvider` fix unchanged (credited with a `Co-authored-by` line) and replaces its Fabric frame view fix. cached the result, reusing it while the number of selected prims stayed the same. Two problems with that. The cache key is unsafe: an equal prim count does not mean the prims or their order stayed the same, so after a bucket reorder or a same-count membership change the cached indices point at the wrong prims and cameras silently read another prim's transform. And it treats the symptom — the underlying operation is still proportional to the size of the whole scene, just performed less often. Making the selection small removes the need for a cache at all, so both problems go away. - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package (do **not** edit `CHANGELOG.rst` or bump `extension.toml` — CI handles that) - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Co-authored-by: pv-nvidia <197907000+pv-nvidia@users.noreply.github.com> Co-authored-by: yts-nv <yts-nv@users.noreply.github.com> Co-authored-by: Kelly Guo <kellyg@nvidia.com> (cherry picked from commit 480370c)
The isaac-sim#5677 cherry-pick brought in OvPhysxFrameView code calling OvPhysxView (try_binding_for / read_into). That class arrives with isaac-sim#6224, which is not on this release branch, so the module import raised ModuleNotFoundError as soon as an OVPhysX frame view was initialized. Restore the direct physx.create_tensor_binding / binding.read calls this branch provides.
689325b to
2f6b179
Compare
Greptile SummaryThis backport adds writer-scoped FrameView transform updates, local/world pose and scale support across USD, Fabric, Newton, and OVPhysX, and view-scoped Fabric selections with deterministic cleanup.
Confidence Score: 3/5The PR should not merge until signed-scale handling is corrected because reflected transforms can be changed silently by the new world/local writer paths. Fabric partial writes reconstruct existing transforms with unsigned scales, while USD world-scale writes divide by unsigned parent scales; both produce incorrect transforms for valid negatively scaled hierarchies. Files Needing Attention: source/isaaclab/isaaclab/utils/warp/fabric.py; source/isaaclab/isaaclab/sim/views/usd_frame_view.py Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart LR
Caller[FrameView caller] --> Scope{Writer space}
Scope -->|World| World[Write world pose or scale]
Scope -->|Local| Local[Write local pose or scale]
World --> DeriveLocal[Derive local matrix]
Local --> DeriveWorld[Derive world matrix]
DeriveLocal --> Sync[Synchronize and close scope]
DeriveWorld --> Sync
Sync --> Read[Guarded world/local getters]
|
| parent_scale = Gf.Vec3d( | ||
| Gf.Vec3d(parent_world[0][0], parent_world[0][1], parent_world[0][2]).GetLength(), | ||
| Gf.Vec3d(parent_world[1][0], parent_world[1][1], parent_world[1][2]).GetLength(), | ||
| Gf.Vec3d(parent_world[2][0], parent_world[2][1], parent_world[2][2]).GetLength(), | ||
| ) |
There was a problem hiding this comment.
When a prim has a negatively scaled parent, the world-space scale writer derives parent scale with unsigned basis-vector lengths, producing a local scale with the wrong sign and a composed world transform that does not match the requested scale.
Knowledge Base Used: Physics backend extensions
There was a problem hiding this comment.
Isaac Lab Review Bot
The backport consistently introduces the FrameView writer-scope architecture and view-scoped Fabric selections, but it also introduces compatibility regressions in the legacy scale API, a writer-lock failure path, shared world/local scale storage, and public documentation that does not match the implemented API or warning behavior.
- Design and architecture: The
_get_*_impl,_apply_*, and_make_*_writersplit is applied consistently across USD, Fabric, Newton, and OvPhysX. However,FrameViewSpaceWriterBase.__enter__claims the per-view writer lock before backend initialization without releasing it if initialization raises, leaving the view unusable after an entry failure. - API: The legacy
get_scales()contract changes fromwp.arraytoProxyArraywithout prior deprecation, and FabricFrameView's Fabric-disabled fallback changes legacy scale semantics from local to world space. The local-writer example incorrectly usestranslations=although writerset_poses()acceptspositions=, and several changelog fragments promise deprecation warnings that the implementation never emits. - Implementation: The Fabric selection scoping and world/local matrix derivation paths are well covered, including rotated and scaled parents. The remaining implementation defect is that full world-scale and local-scale reads reuse the same cached buffer and ProxyArray, so a later read silently mutates an earlier result. Writer entry also needs exception-safe lock and backend-state restoration.
Significant concerns. Posted 6 actionable findings inline.
Automated review; human maintainers own approval decisions.
| # Scale getter/setter convenience helpers. | ||
| # ------------------------------------------------------------------ | ||
|
|
||
| def get_scales(self, indices: wp.array | None = None) -> ProxyArray: |
There was a problem hiding this comment.
🟡 Warning · Api — get_scales return type changed without deprecation
get_scales() was declared and implemented as returning wp.array; it now returns ProxyArray, and every backend _get_scales_impl wraps its result. Callers doing wp.clone(view.get_scales()) or wp.to_torch(...) break, while the changelog states this helper "remains supported ... (not deprecated)". Keep the legacy getter returning wp.array and reserve ProxyArray for the new explicit scale getters, or deprecate the change first.
| f"({type(self._view._active_writer).__name__}). Exit the existing scope before " | ||
| "opening a new one." | ||
| ) | ||
| self._view._active_writer = self |
There was a problem hiding this comment.
🟡 Warning · Implementation — Writer lock leaks when entry fails
__enter__ sets _view._active_writer = self before calling _enter_impl(). Python does not invoke __exit__ when __enter__ raises, so a failure in backend setup (e.g. _initialize_fabric() raising on a missing prim) leaves the view permanently locked: every getter and every future writer then raises. Clear _active_writer (and undo any partial backend state) if _enter_impl() raises.
| scales_wp = _to_float32_2d(scales) | ||
| use_cached = indices is None or indices == slice(None) | ||
| if use_cached: | ||
| scales_wp = self._fabric_scales_buf |
There was a problem hiding this comment.
🟡 Warning · Implementation — World and local scale getters share one buffer
_decompose_scales writes into the single cached _fabric_scales_buf and returns _fabric_scales_ta for both _get_world_scales_impl and _get_local_scales_impl on the indices=None path. Calling get_world_scales() then get_local_scales() silently overwrites the first result, which then reports local values. The pose getters correctly use separate world/local buffers; add a distinct local-scale buffer and ProxyArray here.
| return self._usd_view.get_scales(indices) | ||
| def _get_scales_impl(self, indices=None) -> ProxyArray: | ||
| """Fabric: get_scales returns world-space scales (legacy behavior).""" | ||
| return self._get_world_scales_impl(indices) |
There was a problem hiding this comment.
🟡 Warning · Api — Fallback get_scales/set_scales switch to world space
Previously, when /physics/fabricEnabled was false, FabricFrameView.get_scales/set_scales delegated to UsdFrameView and operated on local xformOp:scale. _get_scales_impl/_set_scales_impl now always route through the world-space hooks, which compose with (and divide by) the parent world scale. Under scaled parents this silently changes both reads and writes on the USD-fallback path; route the fallback legacy path to the local hooks.
| .. code-block:: python | ||
|
|
||
| with view.xform_local_space_writer() as w: | ||
| w.set_poses(translations=t, orientations=o) |
There was a problem hiding this comment.
🔵 Suggestion · Api — Docstring example uses nonexistent set_poses keyword
The xform_local_space_writer example calls w.set_poses(translations=t, orientations=o), but FrameViewSpaceWriterBase.set_poses and every backend implementation declare the parameter as positions. Copying this documented snippet raises TypeError. Fix the example, and note the rename for callers migrating from set_local_poses(translations=...).
| implementations so writes follow the new | ||
| :meth:`~isaaclab.sim.views.BaseFrameView.xform_world_space_writer` / | ||
| :meth:`~isaaclab.sim.views.BaseFrameView.xform_local_space_writer` context API. | ||
| ``set_world_poses`` / ``set_local_poses`` shims still work (one-time |
There was a problem hiding this comment.
🔵 Suggestion · Api — Changelog promises unimplemented deprecation warnings
This fragment (and the newton/ovphysx/physx fabric-local-poses fragments) states that the pose shims and get_scales/set_scales emit a DeprecationWarning. No warnings.warn exists in any added backend or in BaseFrameView, and source/isaaclab/changelog.d/xform-space-writer.rst says these helpers are explicitly not deprecated. Either emit the warnings or drop the deprecation claims so release notes match shipped behavior.
2f6b179 to
89adc46
Compare
AntoineRichard
left a comment
There was a problem hiding this comment.
I don't think we should be doing this, but if they need it.
|
run-ci |
bffdce9
into
isaac-sim:release/3.0.0-beta2
Description
Backports two already-merged FrameView PRs to
release/3.0.0-beta2, cherry-picked in merge order:xform_space_writerAPI (upstream69888c34e471)FabricFrameViewselections to the view, not the stage (upstream480370cdc5b0, nvbug 6535498)Two support commits, because this branch predates some prerequisites:
Shim needed for Arena compatibility.(@amillane) addssplit_clone_template, backported from Fixedusd_replicate()authoring environment grid positions on nested prims (e.g. cameras), overwriting their local transforms. #6071. Ordered first so no commit imports it before it exists.fix: use the OVPhysX tensor binding API…restores this branch'sphysx.create_tensor_binding()/binding.read(). The feat: GPU-accelerated FrameView local poses + a new xform_space_writer API #5677 pick referencedOvPhysxView, which arrives with Add OvPhysxView: a string-keyed binding manager for OVPhysX tensor bindings (view series, part 1) #6224 and is absent here, soOvPhysxFrameViewinit raisedModuleNotFoundError.Conflict resolutions converge each touched file on its post-#6805 state from
develop, so the backported code is byte-identical to what has been running and tested there. Two consequences of that choice:get_scales()now returnsProxyArrayin all backends, as ondevelop. On this branch it previously returnedwp.array, so callers that pass the result straight to Warp APIs need.warp(Torch users:.torch). This is the one API change the backport carries.The only deviation from
develop's state is test-infra: device parametrization uses literal["cpu", "cuda:0"]becausetest_devices()does not exist on this branch.Type of change
FrameView.close(), writer-scope APIget_scales()returnsProxyArrayinstead ofwp.array, matchingdevelopTests
test_views_xform_prim_ovphysx.py— 49 passed (26cpu+ 24cuda:0; needs one process per device,ovphysx<=0.3.7binds device mode in C++ on firstPhysX(...))test_views_xform_prim_newton.py— 58 passed