Warp (Preview)

Overview

_images/warp_header.png

NVIDIA Warp is a Python framework for writing high-performance simulation and graphics code in Omniverse. Warp provides an easy way for Python coders to write kernel-based programs, and benefit from GPU-acceleration in Omniverse and OmniGraph.

NVIDIA’s Omniverse developers are using Warp to help create new GPU-accelerated 3D simulation workflows and dynamic content today. Some of the key benefits include simulation performance on-par with native code, and improved developer productivity thanks to fast iteration times.

In Warp, kernels are defined in Python syntax and JIT compiled to C++/CUDA at runtime. Users can hot-reload kernels directly from within Python, and leverage rich high-level data structures and algorithms to build real-time simulations.

User guide

Installation

To get started with Warp in Omniverse users should enable the bundled extension from the extension registry (note that version may differ):

_images/warp_extension.png

This will install the Warp extension into the Omniverse Python environment and allow users to import the module in their scripts and nodes. For example, once installed, users should be able to open the Script Editor window and execute the following code successfully:

import warp

print(warp.config.version)

Defining Kernels

Kernels in Warp are defined using a @wp.kernel decorator around user Python functions. For example, a simple function to deform a mesh according to a sine-wave could be defined as follows:

@wp.kernel
def deform(points_in: wp.array(dtype=wp.vec3),
           points_out: wp.array(dtype=wp.vec3),
           time: float):

    # get thread-id
    tid = wp.tid()

    # sine-wave deformer
    points_out[tid] = points_in[tid] + wp.vec3(0.0, wp.sin(time + points_in[tid][0])*10.0, 0.0)

Launching Kernels

One of the main uses for Warp is as a convenient and productive way to define GPU OmniGraph nodes from Python. Below, we show the source for a node that deforms a set of mesh points using the kernel above, and outputs the result. The node’s compute() method is responsible for wrapping input data arrays in a wp.array() which ensures data is uploaded to the GPU, then launches our kernel on the device:

class OgnDeform:

    @staticmethod
    def compute(db) -> bool:

        with wp.ScopedCudaGuard():

            # convert node inputs to a GPU array
            points_in = wp.array(db.inputs.points, dtype=wp.vec3, device="cuda")
            points_out = wp.zeros_like(points_in)

            # launch deformation kernel
            wp.launch(kernel=deform, dim=len(points_in), inputs=[points_in, points_out, db.inputs.time], device="cuda")

            # write node outputs
            db.outputs.points = points_out.numpy()

        return True

On first launch the deform function will be JIT compiled to a native CUDA kernel and executed on the GPU. After execution, the result can be converted back to numpy arrays using the array.numpy() method and assigned to the node outputs.

If we wire a USD mesh primitive to our node, we have a simple deformer that generates the following result:

_images/warp_deformer.png

The source code and USD for this example scene can be found in the Warp extension directory: omni.warp\data\example_deform.usd.

Further Reading

For more background and the full documentation of the Warp SDK please see the following external references: