diff --git a/source/isaaclab/changelog.d/pbarejko-fabric-hierarchy-gpu-update.skip b/source/isaaclab/changelog.d/pbarejko-fabric-hierarchy-gpu-update.skip new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/source/isaaclab/isaaclab/cloner/_fabric_notices.py b/source/isaaclab/isaaclab/cloner/_fabric_notices.py index 513b70d4ccd3..d4a582bb4452 100644 --- a/source/isaaclab/isaaclab/cloner/_fabric_notices.py +++ b/source/isaaclab/isaaclab/cloner/_fabric_notices.py @@ -9,10 +9,9 @@ framework so cloning can suspend Fabric's USD notice listener without depending on ``isaacsim.core.simulation_manager``. -Mirrors the in-tree pattern in :mod:`isaaclab_newton.physics._cubric` for -``omni::cubric::IAdapter`` — same problem (base-Kit Carbonite interface with no -Python binding), same solution. When Kit exposes this from Python, replace this -module with a one-line import. +This is a temporary shim for a base-Kit Carbonite interface that has no Python +binding yet. When Kit exposes this from Python, replace this module with a +one-line import. """ from __future__ import annotations diff --git a/source/isaaclab_newton/changelog.d/pbarejko-fabric-hierarchy-gpu-update.rst b/source/isaaclab_newton/changelog.d/pbarejko-fabric-hierarchy-gpu-update.rst new file mode 100644 index 000000000000..ed18bc2710b5 --- /dev/null +++ b/source/isaaclab_newton/changelog.d/pbarejko-fabric-hierarchy-gpu-update.rst @@ -0,0 +1,15 @@ +Changed +^^^^^^^ + +* Changed Newton Kit viewport transform sync to call + ``IFabricHierarchy.update_world_xforms_gpu_with_options`` with + ``FabricHierarchyGpuUpdateOptions.RIGID_BODY | FORCE_UPDATE`` instead of the + private ctypes ``omni::cubric::IAdapter`` shim. Older Kit builds without the + new API continue to fall back to ``IFabricHierarchy.update_world_xforms``. + +Removed +^^^^^^^ + +* Removed :mod:`isaaclab_newton.physics._cubric` ctypes bindings for + ``omni::cubric::IAdapter``. Use + ``IFabricHierarchy.update_world_xforms_gpu_with_options`` instead. diff --git a/source/isaaclab_newton/isaaclab_newton/physics/_cubric.py b/source/isaaclab_newton/isaaclab_newton/physics/_cubric.py deleted file mode 100644 index 3bc889f36e49..000000000000 --- a/source/isaaclab_newton/isaaclab_newton/physics/_cubric.py +++ /dev/null @@ -1,357 +0,0 @@ -# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). -# All rights reserved. -# -# SPDX-License-Identifier: BSD-3-Clause - -"""Pure-Python ctypes bindings for the cubric GPU transform-hierarchy API. - -Acquires the ``omni::cubric::IAdapter`` carb interface directly from the -Carbonite framework and wraps its function-pointer methods so that Newton -can call cubric's GPU transform propagation without C++ pybind11 changes. - -The flow mirrors PhysX's ``DirectGpuHelper::updateXForms_GPU()``: - -1. ``IAdapter::create`` → allocate a cubric adapter ID -2. ``IAdapter::bindToStage`` → bind to the current Fabric stage -3. ``IAdapter::compute`` → GPU kernel: propagate world transforms -4. ``IAdapter::release`` → free the adapter - -When cubric is unavailable (e.g. CPU-only machine, plugin not loaded), the -caller falls back to the CPU ``update_world_xforms()`` path. -""" - -from __future__ import annotations - -import ctypes -import logging - -logger = logging.getLogger(__name__) - -# --------------------------------------------------------------------------- -# Carb Framework struct layout (CARB_ABI function-pointer offsets, x86_64) -# --------------------------------------------------------------------------- -# Counting only CARB_ABI fields from the top of ``struct Framework``: -# 0: loadPluginsEx -# 8: unloadAllPlugins -# 16: acquireInterfaceWithClient -# 24: tryAcquireInterfaceWithClient ← we use this one -# 32: acquireInterfaceFromInterfaceWithClient -# 40: tryAcquireInterfaceFromInterfaceWithClient -# 48: acquireInterfaceFromLibraryWithClient -# 56: tryAcquireInterfaceFromLibraryWithClient -# 64: getInterfacesCountEx -# 72: acquireInterfacesWithClient -# 80: releaseInterfaceWithClient -# 88: getPluginDesc -# 96: getInterfacePluginDesc ← we use this one -_FW_OFF_TRY_ACQUIRE = 24 -_FW_OFF_GET_INTERFACE_PLUGIN_DESC = 96 - -# --------------------------------------------------------------------------- -# IAdapter struct layout (from omni/cubric/IAdapter.h) -# --------------------------------------------------------------------------- -# v0.1 layout: -# 0: getAttribute -# 8: create(AdapterId*) -# 16: refcount -# 24: retain -# 32: release(AdapterId) -# 40: bindToStage(AdapterId, const FabricId&) -# 48: unbind -# 56: compute(AdapterId, options, dirtyMode, outFlags*) -_IA_OFF_CREATE = 8 -_IA_OFF_RELEASE = 32 -_IA_OFF_BIND = 40 -_IA_OFF_COMPUTE = 56 - -# Expected IAdapter version. -_IA_EXPECTED_MAJOR = 0 -_IA_EXPECTED_MINOR = 1 - -# AdapterId sentinel -_INVALID_ADAPTER_ID = ctypes.c_uint64(~0).value - -# AdapterComputeOptions flags (from IAdapter.h) -_OPT_FORCE_UPDATE = 1 << 0 # Force update, ignoring invalidation status -_OPT_FORCE_STATE_RECONSTRUCTION = 1 << 1 # Force full rebuild of internal accel structures -_OPT_SKIP_ISOLATED = 1 << 2 # Skip prims with connectivity degree 0 -_OPT_RIGID_BODY = 1 << 3 # Use PhysicsRigidBodyAPI tag for inverse propagation - -# Newton prims get tagged with PhysicsRigidBodyAPI at init time so -# cubric's eRigidBody mode can distinguish rigid-body buckets -# (Inverse: preserve world matrix written by Newton, derive local) -# from non-rigid-body buckets (Forward: propagate to children). -# eForceUpdate is ORed in to bypass the change-listener check. -_OPT_DEFAULT = _OPT_RIGID_BODY | _OPT_FORCE_UPDATE - -# AdapterDirtyMode -_DIRTY_ALL = 0 # eAll — dirty all prims in the stage -_DIRTY_COARSE = 1 # eCoarse — dirty all prims in visited buckets - - -# --------------------------------------------------------------------------- -# ctypes struct mirrors -# --------------------------------------------------------------------------- -class _Version(ctypes.Structure): - _fields_ = [("major", ctypes.c_uint32), ("minor", ctypes.c_uint32)] - - -class _InterfaceDesc(ctypes.Structure): - """``carb::InterfaceDesc`` — {const char* name, Version version}.""" - - _fields_ = [ - ("name", ctypes.c_char_p), - ("version", _Version), - ] - - -# carb::PluginDesc offsets. PluginImplDesc occupies the first 40 bytes -# (3 char* + 4-byte hotReload + 4-byte pad + char*). -_PD_OFF_INTERFACES = 40 -_PD_OFF_INTERFACE_COUNT = 48 -_INTERFACE_DESC_STRIDE = 16 # char* + Version - - -def _read_u64(addr: int) -> int: - return ctypes.c_uint64.from_address(addr).value - - -# --------------------------------------------------------------------------- -# Public API -# --------------------------------------------------------------------------- -class CubricBindings: - """Typed wrappers around the cubric ``IAdapter`` API. - - Call :meth:`initialize` once; if it returns ``True``, the four adapter - methods are available. - """ - - def __init__(self) -> None: - self._ia_ptr: int = 0 - self._create_fn = None - self._release_fn = None - self._bind_fn = None - self._compute_fn = None - - # -- lifecycle ----------------------------------------------------------- - - def initialize(self) -> bool: - """Acquire the cubric ``IAdapter`` from the carb framework.""" - # Ensure the omni.cubric extension (native carb plugin) is loaded. - try: - import omni.kit.app - - ext_mgr = omni.kit.app.get_app().get_extension_manager() - if not ext_mgr.is_extension_enabled("omni.cubric"): - ext_mgr.set_extension_enabled_immediate("omni.cubric", True) - if not ext_mgr.is_extension_enabled("omni.cubric"): - logger.warning("Failed to enable omni.cubric extension") - return False - except Exception as exc: - logger.warning("Cannot enable omni.cubric: %s", exc) - return False - - # Get Framework* via libcarb.so acquireFramework (singleton). - try: - libcarb = ctypes.CDLL("libcarb.so") - except OSError: - logger.warning("Could not load libcarb.so") - return False - - libcarb.acquireFramework.restype = ctypes.c_void_p - libcarb.acquireFramework.argtypes = [ctypes.c_char_p, _Version] - fw_ptr = libcarb.acquireFramework(b"isaaclab.cubric", _Version(0, 0)) - if not fw_ptr: - logger.warning("acquireFramework returned null") - return False - - # Read tryAcquireInterfaceWithClient fn-ptr from Framework vtable. - try_acquire_addr = _read_u64(fw_ptr + _FW_OFF_TRY_ACQUIRE) - if try_acquire_addr == 0: - logger.warning("tryAcquireInterfaceWithClient is null in Framework") - return False - - try_acquire_fn = ctypes.CFUNCTYPE( - ctypes.c_void_p, # return: void* (IAdapter*) - ctypes.c_char_p, # clientName - _InterfaceDesc, # desc (by value) - ctypes.c_char_p, # pluginName - )(try_acquire_addr) - - desc = _InterfaceDesc( - name=b"omni::cubric::IAdapter", - version=_Version(_IA_EXPECTED_MAJOR, _IA_EXPECTED_MINOR), - ) - - # Try tryAcquire first (non-loading); fall back to acquire (will load the plugin if registered). - ia_ptr = try_acquire_fn(b"isaaclab.cubric", desc, None) - if not ia_ptr: - acquire_addr = _read_u64(fw_ptr + 16) # acquireInterfaceWithClient - if acquire_addr: - acquire_fn = ctypes.CFUNCTYPE( - ctypes.c_void_p, - ctypes.c_char_p, - _InterfaceDesc, - ctypes.c_char_p, - )(acquire_addr) - ia_ptr = acquire_fn(b"isaaclab.cubric", desc, None) - if not ia_ptr: - logger.warning( - "Could not acquire omni::cubric::IAdapter v%d.%d — plugin may not be " - "registered or its version is older. Falling back to update_world_xforms().", - _IA_EXPECTED_MAJOR, - _IA_EXPECTED_MINOR, - ) - return False - - if not self._verify_iadapter_version(fw_ptr, ia_ptr): - return False - self._ia_ptr = ia_ptr - - # Wrap the four IAdapter function pointers we need. - create_addr = _read_u64(ia_ptr + _IA_OFF_CREATE) - release_addr = _read_u64(ia_ptr + _IA_OFF_RELEASE) - bind_addr = _read_u64(ia_ptr + _IA_OFF_BIND) - compute_addr = _read_u64(ia_ptr + _IA_OFF_COMPUTE) - - if not all([create_addr, release_addr, bind_addr, compute_addr]): - logger.warning("One or more IAdapter function pointers are null") - return False - - self._create_fn = ctypes.CFUNCTYPE( - ctypes.c_bool, - ctypes.POINTER(ctypes.c_uint64), - )(create_addr) - - self._release_fn = ctypes.CFUNCTYPE( - ctypes.c_bool, - ctypes.c_uint64, - )(release_addr) - - # FabricId is uint64, passed by const-ref -> pointer on x86_64 - self._bind_fn = ctypes.CFUNCTYPE( - ctypes.c_bool, - ctypes.c_uint64, - ctypes.POINTER(ctypes.c_uint64), - )(bind_addr) - - self._compute_fn = ctypes.CFUNCTYPE( - ctypes.c_bool, - ctypes.c_uint64, # adapterId - ctypes.c_uint32, # options (AdapterComputeOptions) - ctypes.c_int32, # dirtyMode (AdapterDirtyMode) - ctypes.c_void_p, # outAccountFlags* (nullable) - )(compute_addr) - - logger.info("cubric IAdapter bindings ready") - return True - - @staticmethod - def _verify_iadapter_version(fw_ptr: int, ia_ptr: int) -> bool: - """Verify the acquired IAdapter is compatible with this shim's vtable offsets. - - Only the exact version whose vtable and transform behavior have been - validated is accepted. Any mismatch returns False so the caller uses - the safe CPU hierarchy fallback. - """ - get_desc_addr = _read_u64(fw_ptr + _FW_OFF_GET_INTERFACE_PLUGIN_DESC) - if get_desc_addr == 0: - logger.warning("getInterfacePluginDesc is null in Framework") - return False - - get_desc_fn = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)(get_desc_addr) - plugin_desc_ptr = get_desc_fn(ia_ptr) - if not plugin_desc_ptr: - logger.warning("getInterfacePluginDesc returned null for IAdapter") - return False - - interfaces_ptr = _read_u64(plugin_desc_ptr + _PD_OFF_INTERFACES) - interface_count = _read_u64(plugin_desc_ptr + _PD_OFF_INTERFACE_COUNT) - if interfaces_ptr == 0 or interface_count == 0: - logger.warning("PluginDesc reports zero interfaces for cubric plugin") - return False - if interface_count > 64: - logger.warning( - "PluginDesc interfaceCount suspiciously large (%d); struct layout mismatch?", - interface_count, - ) - return False - - for i in range(interface_count): - entry_addr = interfaces_ptr + i * _INTERFACE_DESC_STRIDE - name_addr = _read_u64(entry_addr) - if name_addr == 0: - continue - target_name = b"omni::cubric::IAdapter\x00" - if ctypes.string_at(name_addr, len(target_name)) != target_name: - continue - major = ctypes.c_uint32.from_address(entry_addr + 8).value - minor = ctypes.c_uint32.from_address(entry_addr + 12).value - if not (major == _IA_EXPECTED_MAJOR and minor == _IA_EXPECTED_MINOR): - logger.warning( - "cubric IAdapter version incompatible with this shim: plugin " - "reports v%d.%d, shim is pinned to v%d.%d. Falling back to " - "update_world_xforms().", - major, - minor, - _IA_EXPECTED_MAJOR, - _IA_EXPECTED_MINOR, - ) - return False - return True - - logger.warning( - "cubric plugin does not advertise omni::cubric::IAdapter — unexpected. " - "Falling back to update_world_xforms()." - ) - return False - - @property - def available(self) -> bool: - return self._ia_ptr != 0 - - # -- cubric adapter methods ---------------------------------------------- - - def create_adapter(self) -> int | None: - """Create a cubric adapter. Returns an adapter ID or ``None``.""" - if not self._create_fn: - return None - adapter_id = ctypes.c_uint64(_INVALID_ADAPTER_ID) - ok = self._create_fn(ctypes.byref(adapter_id)) - if not ok or adapter_id.value == _INVALID_ADAPTER_ID: - logger.warning("IAdapter::create failed") - return None - return adapter_id.value - - def bind_to_stage(self, adapter_id: int, fabric_id: int) -> bool: - """Bind the adapter to a Fabric stage.""" - if not self._bind_fn: - return False - fid = ctypes.c_uint64(fabric_id) - ok = self._bind_fn(adapter_id, ctypes.byref(fid)) - if not ok: - logger.warning("IAdapter::bindToStage failed (adapter=%d, fabricId=%d)", adapter_id, fabric_id) - return ok - - def compute(self, adapter_id: int) -> bool: - """Run the GPU transform-hierarchy compute pass. - - Uses ``eRigidBody | eForceUpdate`` with ``eAll`` dirty mode. - ``eRigidBody`` makes cubric apply Inverse propagation on buckets - tagged with ``PhysicsRigidBodyAPI`` (keeps Newton's world transforms, - derives local) and Forward on everything else (propagates to children). - ``eForceUpdate`` bypasses the change-listener dirty check. - """ - if not self._compute_fn: - return False - flags = ctypes.c_uint32(0) - ok = self._compute_fn(adapter_id, _OPT_DEFAULT, _DIRTY_ALL, ctypes.byref(flags)) - if not ok: - logger.warning("IAdapter::compute returned false (flags=0x%x)", flags.value) - return ok - - def release_adapter(self, adapter_id: int) -> None: - """Release an adapter.""" - if not adapter_id or not self._release_fn: - return - self._release_fn(adapter_id) diff --git a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py index 7527f95fdc95..3a2264ed9f79 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py @@ -389,10 +389,8 @@ def provides_implicit_damping(cls) -> bool: _newton_particle_count_attr = "newton:particleCount" _particle_visual_prims: dict[str, _ParticleVisualPrim] = {} - # cubric GPU transform hierarchy (replaces CPU update_world_xforms) - _cubric = None - _cubric_adapter: int | None = None - _cubric_bound_fabric_id: int | None = None + # Cached after the first fabric sync that probes IFabricHierarchy GPU APIs. + _use_fabric_gpu_hierarchy: bool | None = None # Set to True after sync_transforms_to_usd() successfully writes body positions for # the first time in each simulation session. Reset to False in clear(). Polled by @@ -570,11 +568,12 @@ def sync_transforms_to_usd(cls) -> None: The Warp kernel reads ``state_0.body_q[newton_index[i]]`` and writes the corresponding ``mat44d`` to ``omni:fabric:worldMatrix`` for each prim. - When cubric is available the method mirrors PhysX's ``DirectGpuHelper`` - pattern: pause Fabric change tracking, write transforms, resume tracking, - then call ``IAdapter::compute`` on the GPU to propagate the hierarchy and - notify the Fabric Scene Delegate. Otherwise it falls back to the CPU - ``update_world_xforms()`` path. + When ``IFabricHierarchy.update_world_xforms_gpu_with_options`` is + available the method mirrors PhysX's ``DirectGpuHelper`` pattern: pause + Fabric change tracking, write transforms, resume tracking, then run the + GPU hierarchy update with ``RIGID_BODY | FORCE_UPDATE`` so Newton-authored + world matrices stay authoritative on rigid-body prims. Otherwise it + falls back to the CPU ``update_world_xforms()`` path. """ if cls._usdrt_stage is None or cls._model is None or cls._state_0 is None: return @@ -583,23 +582,28 @@ def sync_transforms_to_usd(cls) -> None: try: import usdrt - # Lazy adapter creation: deferred from initialize_solver() to avoid - # startup-ordering issues with the cubric plugin. - if cls._cubric is not None and cls._cubric.available and cls._cubric_adapter is None: - NewtonManager._cubric_adapter = cls._cubric.create_adapter() - if cls._cubric_adapter is not None: - logger.info("cubric GPU transform hierarchy enabled") - else: - logger.warning("cubric adapter creation failed; falling back to update_world_xforms()") - NewtonManager._cubric = None - - use_cubric = cls._cubric is not None and cls._cubric_adapter is not None - fabric_hierarchy = None + gpu_opts_cls = None if hasattr(usdrt, "hierarchy"): fabric_hierarchy = usdrt.hierarchy.IFabricHierarchy().get_fabric_hierarchy( cls._usdrt_stage.GetFabricId(), cls._usdrt_stage.GetStageIdAsStageId() ) + gpu_opts_cls = getattr(usdrt.hierarchy, "FabricHierarchyGpuUpdateOptions", None) + + if cls._use_fabric_gpu_hierarchy is None and hasattr(usdrt, "hierarchy"): + # Probe the pybind class once so a transient null hierarchy handle does + # not permanently disable the GPU path for the session. + NewtonManager._use_fabric_gpu_hierarchy = gpu_opts_cls is not None and hasattr( + usdrt.hierarchy.IFabricHierarchy, "update_world_xforms_gpu_with_options" + ) + if cls._use_fabric_gpu_hierarchy: + logger.info("Fabric GPU transform hierarchy enabled via IFabricHierarchy") + else: + logger.info("Fabric GPU transform hierarchy unavailable; falling back to update_world_xforms()") + + use_gpu_hierarchy = bool( + cls._use_fabric_gpu_hierarchy and fabric_hierarchy is not None and gpu_opts_cls is not None + ) # Pause hierarchy change tracking BEFORE SelectPrims. # SelectPrims with ReadWrite access calls getAttributeArrayGpu @@ -608,7 +612,7 @@ def sync_transforms_to_usd(cls) -> None: # Kit's updateWorldXforms will do an expensive connectivity # rebuild every frame. PhysX avoids this via ScopedUSDRT which # pauses tracking before any Fabric writes. - if use_cubric and fabric_hierarchy is not None: + if use_gpu_hierarchy: fabric_hierarchy.track_world_xform_changes(False) fabric_hierarchy.track_local_xform_changes(False) @@ -642,16 +646,17 @@ def sync_transforms_to_usd(cls) -> None: NewtonManager._newton_fabric_ready = True NewtonManager._transforms_dirty = False - if use_cubric and fabric_hierarchy is not None: - fabric_id = cls._usdrt_stage.GetFabricId().id - if fabric_id != cls._cubric_bound_fabric_id: - cls._cubric.bind_to_stage(cls._cubric_adapter, fabric_id) - NewtonManager._cubric_bound_fabric_id = fabric_id - cls._cubric.compute(cls._cubric_adapter) + if use_gpu_hierarchy: + # RIGID_BODY: inverse-propagate on PhysicsRigidBodyAPI buckets + # (keep Newton world matrices, derive local). FORCE_UPDATE: + # bypass the change-listener dirty check after tracking pause. + fabric_hierarchy.update_world_xforms_gpu_with_options( + gpu_opts_cls.RIGID_BODY | gpu_opts_cls.FORCE_UPDATE + ) elif fabric_hierarchy is not None: fabric_hierarchy.update_world_xforms() finally: - if use_cubric and fabric_hierarchy is not None: + if use_gpu_hierarchy: fabric_hierarchy.track_world_xform_changes(True) fabric_hierarchy.track_local_xform_changes(True) except Exception: @@ -927,11 +932,7 @@ def is_fabric_enabled(cls) -> bool: @classmethod def clear(cls): """Clear all Newton-specific state (callbacks cleared by super().close()).""" - if cls._cubric is not None and cls._cubric_adapter is not None: - cls._cubric.release_adapter(cls._cubric_adapter) - NewtonManager._cubric = None - NewtonManager._cubric_adapter = None - NewtonManager._cubric_bound_fabric_id = None + NewtonManager._use_fabric_gpu_hierarchy = None NewtonManager._newton_fabric_ready = False NewtonManager._builder = None NewtonManager._model = None @@ -1451,8 +1452,8 @@ def _initialize_fabric_body_prims(stage, fabric_hierarchy, usdrt, body_bindings: prim.CreateAttribute(NewtonManager._newton_index_attr, usdrt.Sdf.ValueTypeNames.UInt, custom=True) prim.GetAttribute(NewtonManager._newton_index_attr).Set(body_index) - # Tag with PhysicsRigidBodyAPI so cubric's eRigidBody mode applies - # Inverse propagation (preserves Newton's world transforms and derives + # Tag with PhysicsRigidBodyAPI so FabricHierarchyGpuUpdateOptions.RIGID_BODY + # applies Inverse propagation (preserves Newton's world transforms and derives # local) instead of Forward. prim.AddAppliedSchema("PhysicsRigidBodyAPI") @@ -1764,9 +1765,9 @@ def initialize_solver(cls) -> None: Thin orchestrator: delegates solver construction to :meth:`_build_solver` (overridden by each solver subclass), allocates the collision pipeline (when applicable) via - :meth:`_initialize_contacts`, then sets up cubric bindings and either - captures the CUDA graph immediately or defers capture until the - first :meth:`step` call (RTX-active path). + :meth:`_initialize_contacts`, then either captures the CUDA graph + immediately or defers capture until the first :meth:`step` call + (RTX-active path). .. warning:: When using a CUDA-enabled device, the simulation is graphed. @@ -1805,9 +1806,6 @@ def initialize_solver(cls) -> None: # Runs before graph capture below so the capture warmup sees a valid body_q. cls._eval_fk(None, None) - if cls._usdrt_stage is not None: - cls._setup_cubric_bindings() - # Skip the initial graph capture when the Newton actuator fast path is # active. Capturing here would use ``cls._decimation`` (still its default # of 1, because the env's ``set_decimation`` hasn't run yet); a second @@ -1821,24 +1819,6 @@ def initialize_solver(cls) -> None: if not cls._use_newton_actuators_active: cls._capture_or_defer_graph() - @classmethod - def _setup_cubric_bindings(cls) -> None: - """Initialize cubric ctypes bindings when the Kit viewport is active. - - Adapter creation itself is deferred to the first - :meth:`sync_transforms_to_usd` call to avoid startup-ordering issues - with the cubric plugin. - """ - from isaaclab_newton.physics._cubric import CubricBindings - - bindings = CubricBindings() - if bindings.initialize(): - NewtonManager._cubric = bindings - logger.info("cubric bindings ready (adapter deferred to first render)") - else: - NewtonManager._cubric = None - logger.warning("cubric bindings init failed; falling back to update_world_xforms()") - @classmethod def _capture_or_defer_graph(cls) -> None: """Capture (or schedule deferred capture of) the CUDA graph. diff --git a/source/isaaclab_newton/test/physics/test_cubric.py b/source/isaaclab_newton/test/physics/test_cubric.py deleted file mode 100644 index 9dc5adb985ec..000000000000 --- a/source/isaaclab_newton/test/physics/test_cubric.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). -# All rights reserved. -# -# SPDX-License-Identifier: BSD-3-Clause - -import ctypes - -from isaaclab_newton.physics._cubric import CubricBindings - - -def _verify_version(major: int, minor: int) -> bool: - """Run the cubric ABI verifier against an in-memory plugin descriptor.""" - interface_name = ctypes.create_string_buffer(b"omni::cubric::IAdapter\0") - interface = (ctypes.c_uint64 * 2)() - interface[0] = ctypes.addressof(interface_name) - ctypes.c_uint32.from_address(ctypes.addressof(interface) + 8).value = major - ctypes.c_uint32.from_address(ctypes.addressof(interface) + 12).value = minor - - plugin_descriptor = (ctypes.c_ubyte * 56)() - ctypes.c_uint64.from_address(ctypes.addressof(plugin_descriptor) + 40).value = ctypes.addressof(interface) - ctypes.c_uint64.from_address(ctypes.addressof(plugin_descriptor) + 48).value = 1 - - get_descriptor_type = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p) - get_descriptor = get_descriptor_type(lambda _: ctypes.addressof(plugin_descriptor)) - framework = (ctypes.c_uint64 * 13)() - framework[12] = ctypes.cast(get_descriptor, ctypes.c_void_p).value - - return CubricBindings._verify_iadapter_version(ctypes.addressof(framework), 1) - - -def test_cubric_adapter_rejects_unvalidated_minor_version(): - """A newer minor ABI must use the safe CPU transform-hierarchy fallback.""" - assert _verify_version(0, 1) - assert not _verify_version(0, 2)