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で見る