Skip to main content

tensor-bindings-gpu

Exchange GPU simulation state as caller-owned CUDA tensors (DLPack, GPU-to-GPU with no CPU staging) through tensor bindings. The tensor-binding CODE API is deprecated in ovphysx 0.6 in favor of the session read/write API (Python PhysX.read / PhysX.write, C ovphysx_read / ovphysx_write); use ovphysx-session-write for control inputs and state, and the ovphysx-output-read skill (ovstage-native identity-preserving reads and write-back) to read output back. Tensor bindings are kept only for maintaining existing caller-owned bulk-CUDA binding code during the deprecation period.

소스 정보

저장소
NVIDIA-Omniverse/PhysX
최근 소스 활동
2026년 10월 15일 11:12
감지된 SKILL.md 언어
영어
스타
4,812
포크
677

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
2 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
tensor-bindings-gpu
description
Exchange GPU simulation state as caller-owned CUDA tensors (DLPack, GPU-to-GPU with no CPU staging) through tensor bindings. The tensor-binding CODE API is deprecated in ovphysx 0.6 in favor of the session read/write API (Python PhysX.read / PhysX.write, C ovphysx_read / ovphysx_write); use ovphysx-session-write for control inputs and state, and the ovphysx-output-read skill (ovstage-native identity-preserving reads and write-back) to read output back. Tensor bindings are kept only for maintaining existing caller-owned bulk-CUDA binding code during the deprecation period.
license
Apache-2.0
compatibility
ovphysx >=0.6.0 GPU mode with CUDA device memory; C examples require the SDK headers, libraries, CUDA runtime, and an NVIDIA GPU; Python examples require PyTorch with CUDA.
allowed-tools
Read Shell
metadata
{"version":"0.1.8","author":"NVIDIA Omniverse Physics","tags":"ovphysx, physics, tensor-bindings, gpu, cuda"}
# Tensor Bindings: GPU Read and Write > **Tensor-binding code API deprecated (ovphysx 0.6).** The tensor-binding CODE API is deprecated in > favor of the session read/write API — Python `PhysX.read` / `PhysX.write`, C `ovphysx_read` / > `ovphysx_write`. Prefer the session API where it fits (`ovphysx-output-read` covers ovstage-native > identity-preserving reads and write-back). For writing control inputs and state, use the > `ovphysx-session-write` skill; tensor bindings are kept only for maintaining existing > caller-owned bulk-CUDA binding code. GPU tensor bindings use GPU-mode PhysX with DLPack CUDA tensors. DirectGPU (`/physics/suppressReadback`) is a separate setting: keep it enabled for fastest tensor-pipeline workloads and disabled for workflows that need contact modification. ovstage populates the authored scene, while binding path resolution also includes runtime-only clone paths. The tensor data path does not go through ovstage. ## When to Use Use this skill when a caller needs GPU-to-GPU tensor exchange through CUDA device pointers, PyTorch CUDA tensors, or DLPack without CPU staging. ## Instructions 1. Read the full C or Python sample before adapting this pattern because CUDA memory lifetime, DLPack shape storage, and device ordinal handling matter. 2. Create the binding, query its native device with `binding.native_device` or `ovphysx_get_tensor_binding_native_device()`, require `kDLCUDA` for a no-staging GPU path, allocate on the returned process-visible ordinal, and keep shape storage valid until the synchronous read or write returns. 3. Use Shell to compile and run the full sample or a local integration test after adapting the scene path and tensor type. ## C sample (CUDA) The GPU tensor bindings sample uses `cuda_runtime.h` for device memory allocation and wraps those device pointers in DLPack `DLTensor` structs for: - `ovphysx_read_tensor_binding()` - `ovphysx_write_tensor_binding()` Full sample: - `samples/c_samples/tensor_bindings_gpu_c/main.c` (SDK) - Source checkout: `tests/c_samples/tensor_bindings_gpu_c/main.c` Minimal DLPack wrapper pattern: ```c #include <ovphysx/ovphysx.h> #include <ovphysx/dlpack/dlpack.h> #include <stddef.h> #include <stdint.h> static DLTensor make_cuda_tensor_f32_2d( // CUDA device memory, for example from cudaMalloc(); caller frees it. void* device_buffer, int64_t rows, int64_t columns, // Caller-owned storage; tensor.shape points here until the tensor is consumed. int64_t shape_storage[2], // Native CUDA ordinal returned for this binding. int32_t cuda_device_id) { shape_storage[0] = rows; shape_storage[1] = columns; DLTensor tensor = { .data = device_buffer, .device = { kDLCUDA, cuda_device_id }, .ndim = 2, .dtype = { kDLFloat, 32, 1 }, .shape = shape_storage, .strides = NULL, .byte_offset = 0 }; return tensor; } static int read_and_write_gpu_tensor( ovphysx_instance_handle_t handle, ovphysx_tensor_binding_handle_t binding, // CUDA device memory, for example from cudaMalloc(); caller frees it. void* device_buffer, int64_t count, int64_t components, // Native CUDA ordinal returned for this binding. int32_t cuda_device_id) { int64_t shape_storage[2]; DLTensor tensor = make_cuda_tensor_f32_2d( device_buffer, count, components, shape_storage, cuda_device_id); // Check every result; GPU I/O failures surface through the return status. if (ovphysx_read_tensor_binding(handle, binding, &tensor).status != OVPHYSX_API_SUCCESS) { return 0; } // NULL means no index tensor: apply a full, unindexed write. if (ovphysx_write_tensor_binding(handle, binding, &tensor, NULL).status != OVPHYSX_API_SUCCESS) { return 0; } return 1; } ``` After creating each binding, call `ovphysx_get_tensor_binding_native_device()` and require `kDLCUDA` before using this GPU-only pattern. Allocate and wrap the buffer on the returned `device_id`; the binding query, not configured `active_cuda_gpus`, is authoritative for empty or multi-scene device selection. The full C sample shows the query before allocation. This helper wraps an existing CUDA allocation; it does not allocate memory. `device_buffer`, `shape_storage`, and optional `strides` storage must stay valid until the synchronous read or write call returns. The snippet uses `strides = NULL` for C-contiguous tensors, matching the ovphysx C samples. For Python, install `physx-usd-schemas` and `newton-usd-schemas` alongside `ovphysx`. Register both PhysX and Newton schemas before the first population call so authored `newton:*` values are preserved. Import the PhysX schema recipe from `ovphysx.samples.python_samples.schema_support`; for native samples, set `PHYSX_USD_SCHEMAS_ROOT` to the extracted wheel. See [Physics Schemas](../../docs/physics_schemas.md) for setup. ## Python with PyTorch ```python from pathlib import Path import ovphysx from ovphysx.samples.python_samples.schema_support import physx_schema_paths import torch from ovphysx import ConfigKey, DLDeviceType, PhysX, PhysXConfig from ovphysx.types import TensorType import ovstage cuda_device = 0 # DirectGPU (`/physics/suppressReadback`) still uses the legacy PhysXConfig # carbonite path; GPU ordinal is per-instance after create. physx = PhysX( config=PhysXConfig( carbonite_overrides={"/physics/suppressReadback": True}, ), ) physx.set_config(ConfigKey.ACTIVE_CUDA_GPUS, str(cuda_device)) # Register external PhysX and Newton schemas before the first population. ovstage.population.register_usd_schemas( [*[str(p) for p in physx_schema_paths()], str(ovphysx.newton_schema_root())] ) stage = ovstage.Stage("ovphysx-gpu-tensors") usd_path = Path(ovphysx.__file__).resolve().parent / "samples" / "data" / "basic_simulation.usda" ovstage.population.open_usd(stage, str(usd_path), ordinal=1, domains=ovstage.PopulationDomain.ALL) # attach_ovstage() reads at a sealed ordinal. stage.advance_write_floor(ordinal=1).wait() physx.wait_task(physx.attach_ovstage(stage, read_ordinal=1)) # /World/envs/env* is a clone pattern: env1..env3 exist only after clone + wait. # clone-environments owns the full clone contract (wait_all, CPU isolation). targets = ["/World/envs/env1", "/World/envs/env2", "/World/envs/env3"] anchor_transforms = [ (4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0), (8.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0), (12.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0), ] physx.clone("/World/envs/env0", targets, anchor_transforms=anchor_transforms) physx.wait_all() binding = physx.create_tensor_binding( pattern="/World/envs/env*/table", tensor_type=TensorType.RIGID_BODY_POSE, raise_if_empty=True, ) native_device = binding.native_device if native_device.device_type.value != DLDeviceType.kDLCUDA: raise RuntimeError(f"Expected a native CUDA binding, got {native_device}") cuda_device = native_device.device_id # Write poses from a PyTorch CUDA tensor via DLPack (e.g. an RL env reset). # The pose layout is [px, py, pz, qx, qy, qz, qw]; qw = 1 is identity rotation. reset_poses = torch.zeros( binding.shape, dtype=torch.float32, device=f"cuda:{cuda_device}" ) reset_poses[..., 6] = 1.0 binding.write(reset_poses) # step_sync steps and waits in one call physx.step_sync(1.0 / 60.0) # Read simulated poses back into a CUDA tensor (no CPU staging) output = torch.zeros( binding.shape, dtype=torch.float32, device=f"cuda:{cuda_device}" ) binding.read(output) binding.destroy() physx.wait_task(physx.detach_ovstage()) stage.destroy() physx.destroy() ``` Use `ALL` when populating USD so nested world transforms are resolved by the rendering domain. `PHYSICS` alone seeds local world matrices in OVStage 0.3; see [Population domains](../../docs/ovstage_integration.md#population-domains). ## Important notes - GPU dynamics are enabled by default (`physxScene:enableGPUDynamics` defaults to `true`); set it to `false` to opt into CPU dynamics (PhysX reads this). To prevent any GPU usage in ovphysx, call `PhysX.set_cpu_mode(True)` before creating any `PhysX` instance. Process-wide CPU-only mode rejects CUDA DLPack tensors before accessing CUDA. - DLPack interop works with PyTorch and other objects implementing the `__dlpack__` protocol. - `binding.native_device` is the authoritative no-staging placement. A CPU result means this GPU-only pattern does not apply to that binding. - Masked writes are supported on GPU via `ovphysx_write_tensor_binding_masked()` / `binding.write(tensor, mask=mask)` for bindings whose storage follows the simulation device. CPU-only property bindings require host-resident source and mask tensors. - Create bindings once outside simulation loops and reuse them; binding creation allocates native TensorAPI resources. - Pass `raise_if_empty=True` when a zero-count binding is a configuration error (wrong pattern, missing clone). The default `False` accepts empty bindings; check `binding.count` before reading. See `docs/tutorials/tensor_bindings.md`. ## References - Docs: `docs/tutorials/tensor_bindings.md` (tensor bindings tutorial, shared with the CPU skill); `docs/developer_guide.md` for GPU/DirectGPU specifics - C sample: `samples/c_samples/tensor_bindings_gpu_c/main.c` (SDK; source: `tests/c_samples/tensor_bindings_gpu_c/main.c`)
GitHub에서 보기