Omniverse Visual Debugger

OmniPVD

../_images/omnipvd_selection.png

The PhysX Omniverse Visual Debugger (OmniPVD), is the next iteration of PhysX Visual Debugger (PVD), and it consists of a library that is used to both record PhysX simulations as well as to read them back.

The reason for the Omniverse moniker is that OmniPVD runs its viewer inside Nvidia’s Omniverse Composer environment. The viewer is part of Omniverse Composer as an extension called omni.physx.pvd. Using Omniverse Composer one can import and view PhysX scenes and inspect and visualize variables of many PhysX objects.

The purpose of OmniPVD is twofold

  1. To record simulation attributes to gain insight into the PhysX SDK scene and object states and how they evolve from frame to frame

  2. To visualize the recorded state to allow for an interactive exploration of the frame-by-frame simulation data

One can record for example to a file stream, into what we have dubbed OVD files. How to set up the PhysX SDK to record into OVD files is described in the section “File Recording Setup”.

Once the data is recorded one can download Omniverse Composer and install the OmniPVD extension. This extension gives the opportunity to import OVD files and to explore them as animations.

OmniPVD and PVD Feature Set Comparison

OmniPVD takes inspiration from PVD, but is currently constrained to a subset of its features. This sections sheds some light on which features are covered and which features are not.

  • Data Stream
    • PVD supports streaming of data from a debug session into a viewer application that can view the result as it arrives over TCP/IP

    • OmniPVD supports currently a file stream output where the data is saved into OVD files on disk. These files can be opened and viewed in USD Composer

  • Debug Object Serialization
    • Both PVD and OmniPVD support the serialization of PhysX objects with inital state and per simulation step updates

    • OmniPVD currently supports the PhysX objects listed below

  • OmniPVD Supported PhysX Classes
    • PxPhysics

    • PxGpuDynamicsMemoryConfig

    • PxScene

    • PxBaseMaterial

    • PxMaterial

    • PxFEMSoftBodyMaterial

    • PxFEMClothMaterial

    • PxPBDMaterial

    • PxAggregate

    • PxActor

    • PxRigidActor

    • PxRigidStatic

    • PxRigidBody

    • PxRigidDynamic

    • PxPBDParticleSystem

    • PxArticulationLink

    • PxArticulationReducedCoordinate

    • PxArticulationJointReducedCoordinate

    • PxArticulationMimicJoint

    • PxShape

    • PxGeometry

    • PxSphereGeometry

    • PxCapsuleGeometry

    • PxBoxGeometry

    • PxPlaneGeometry

    • PxConvexMeshGeometry

    • PxConvexMesh

    • PxHeightFieldGeometry

    • PxHeightField

    • PxTriangleMeshGeometry

    • PxTriangleMesh

    • PxTetrahedronMeshGeometry

    • PxTetrahedronMesh

    • PxCustomGeometry

    • PxSoftBodyMesh

    • PxParticleBuffer

    • PxDiffuseParticleParams

    • PxParticleAndDiffuseBuffer

    • PxParticleClothBuffer

    • PxParticleRigidBuffer

    • PxJoint

    • PxFixedJoint

    • PxPrismaticJoint

    • PxRevoluteJoint

    • PxSphericalJoint

    • PxDistanceJoint

    • PxContactJoint

    • PxGearJoint

    • PxRackAndPinionJoint

    • PxD6Joint

  • OmniPVD Supported PhysX Bit Fields
    • PxSceneFlag

    • PxMaterialFlag

    • PxActorFlag

    • PxRigidBodyFlag

    • PxArticulationFlag

    • PxRigidDynamicLockFlag

    • PxShapeFlag

    • PxParticleFlag

    • PxParticleLockFlag

  • OmniPVD Supported PhysX Enums
    • PxFrictionType

    • PxBroadPhaseType

    • PxSolverType

    • PxPairFilteringMode

    • PxCombineMode

    • PxActorType

    • PxArticulationJointType

    • PxArticulationMotion

    • PxArticulationDriveType

    • PxArticulationAxis

    • PxConstraintFlag

    • PxRevoluteJointFlag

    • PxPrismaticJointFlag

    • PxDistanceJointFlag

    • PxSphericalJointFlag

    • PxD6JointDriveFlag

    • PxJointConcreteType

    • PxD6Motion

  • OmniPVD Specific Export Classes
    • PxOmniPvdMetaData

    • PxCustomGeometryExtBaseConvexCallbacks

    • PxCustomGeometryExtCylinderCallbacks

    • PxCustomGeometryExtConeCallbacks

  • Data Recording Flags
    • PVD allows to define if debug, memory or profiling data is to be recorded or not

    • PVD further allows on a per scene level, to disable or enable the recording of contacts, scene queries and constraints

    • OmniPVD currently streams debug data by default and also all contact data for all scenes

  • Memory and Profiling
    • PVD allows for the recording of profiling and memory statistics

    • OmniPVD currently neither streams profile zone nor memory statistics

  • Simulation Frame Export and Import
    • PVD can export a recorded simulation frame into the RepX format, which can be used to initialize a simulation in the PhysX SDK

    • OmniPVD can export a recorded simulation frame into the Physics USD format (only with transforms at the moment) and the Physics USD format can be loaded into USD Composer to partially initialize a PhysX simulation

  • Debug Draw Visualization
    • PVD allows to write points, lines, triangles and text (per secene) in a way that does not require the creation of a certain PhysX object

    • OmniPVD currently does not support custom rendering commands issued by the user. The data in the OVD stream must contain objects that the OmniPVD extension can turn into USD primitives.

  • PhysX SDK Error Stream
    • PVD allows the streaming of PhysX SDK error codes through the error callbacks

    • OmniPVD does not support an error stream output except for the OmniPVD API calls

  • Direct GPU API Recording
    • PVD does not have the ability to record Direct GPU API data changes nor simulation updates

    • OVD allows for recording of data after each simulation step while using the Direct GPU API

Basic Setup (SDK Side)

OmniPVD integration is enabled in the debug, checked and profiling configurations of the PhysX SDK. In order to reduce memory footprint, execution overhead (due to the sampling) and code size, it is not enabled in the release configuration.

The SDK outputs the OmniPVD debugging data in form of a stream. OmniPVD currently supports reading the stream from an OVD file.

A frame recording in OmniPVD is considered as the full list of actors and scene manipulations leading up to the simulation of the first frame, including the state post simulation of the actors at the end of the first frame. This frame is considered frame 0.

Subsequent frames contain first the list of user operations that might or might not occur leading up to the simulation of a frame and the subsequent transforms of the objects involved for that frame.

One can view the stream of incoming operations as commands or operations on a PhysX state which makes the receiving end of the OmniPVD stream a virtual machine of sorts.

The reader of the OmniPVD stream gets first

  1. A list of OmniPVD class and attribute registrations

And then receives for each frame

  1. A list of OmniPVD object creation/destruction and attribute setting operations

  2. A list of OmniPVD object transform attribute updates

  3. A startFrame command, signalling the start of another frame

File Recording Setup

Streaming debug data to a file is the currently well supported stream. It will also continue to be the recommended fall-back in case your platform or system setup does not support a network connection.

What is important is to make sure to pass the OmniPVD object to the PhysX instance creation function as a parameter and to immediately set the file name.

OmniPVD streams stored as files can be loaded in the OmniPVD extension in Omniverse Composer:

PxFoundation* foundation;
foundation = PxCreateFoundation(PX_PHYSICS_VERSION, mAllocator, *mErrorCallback);

// Create the OmniPVD instance
PxOmniPvd* omniPvd = PxCreateOmniPvd(*foundation);
if (omniPvd)
{
    OmniPvdWriter* omniWriter = omniPvd->getWriter();
    // Uncomment for debugging the OmniPvd write stream
    //omniWriter->setLogFunction(logFunc);
    OmniPvdFileWriteStream* omniFileWriteStream = omniPvd->getFileWriteStream();
    if (omniWriter && omniFileWriteStream)
    {
        omniWriter->setWriteStream(*omniFileWriteStream);
    }
}

// The OmniPVD object has to be passed to the PhysX instance creation function, otherwise no debug data
// can be stored in the OVD format, it is the last parameter in the CreatePhysics function
PxPhysics* physics = PxCreatePhysics(PX_PHYSICS_VERSION, *foundation, PxTolerancesScale(), true, 0, omniPvd);

// Once the PhysX instance is created with a non-zero OmniPVD object, one can now proced to set the
omniFileWriteStream->setFileName("myoutpufile.ovd");

// If startSampling returns true, the recording was started successfully
if (!mOmniPvd->startSampling())
{
  printf("Error! OVD recording could not start writing into %s", "myoutpufile.ovd");
}

// Now simulation can be recorded
// Do your simulation...

physics->release();
//After releasing PxPhysics, release OmniPVD
mOmniPvd->release();

OmniPVD Log Stream

One can debug the OmniPVD stream directly by attaching a log function to OmniPVD. This is useful to see when for example the PhysX SDK is registring the classes and attributes.:

OmniPvdWriter* omniWriter = omniPvd->getWriter();
omniWriter->setLogFunction(logFunc);

OmniPVD Viewer

If one downloads Omniverse Composer one can view the exported OVD files.

The OmniPVD Composer extension (omni.physx.pvd) makes it possible to import a physics recording in OVD format, transforming it seamlessly into a cached USDA format. Once imported one can do time scrubbing and inspection of an OVD file as a USD Stage, which is good for debugging, inspecting and visualizing a recorded physics scene, but it stays a pure animation.

The OmniPVD gizmos are also then possible to apply to the imported OVD file, for the sake of different visualizations such as collision contacts, bounding box representations, velocities as well as seeing mass and transform reference frames.

The OmniPVD extension has the option to export of a single specific frame of an OmniPVD USD animated Stage recording into the Physics USD format. This allows one to use the exported frame as an initial state when simulating the scene.

Lastly OmniPVD has a PhysX baking feature, for the reading of a physics recording in OVD format and the baking of the transforms of objects, onto Omniverse Physics objects in a separate Edit Layer. This makes for the replacement of the simulated objects with a set kinematic path, still allowing other physics simulated objects (added afterwards) to interact with them, but not affecting them.

Loading the OmniPVD Extension

The OmniPVD extension is called omni.physx.pvd and can be found in the extension browser by searching for “pvd”. Just make sure to click on the slider next to “AUTOLOAD” to enable it.

../_images/omnipvd_load_extension.png

Importing an OVD Recording

Once a simulation is recorded into an OmniPVD OVD file, inside the PhysX SDK or even by Omniverse Composer itself, one can import it which will automatically look for a cached version of a corresponding USD Stage (in the Cache directory) and load that if it’s already created. One can now explore the USD Stage of the imported OVD file in Composer, inspecting the recorded motion and data. By pressing the “Import” button in the file browser, the chosen file goes through the import process. By default the latest OVD recording is chosen and one can then simply click on “Import” to import it quickly.

../_images/omnipvd_import_ovd.png

Exploring the OVD Object Tree and Property Widget

A recent addition to OmniPVD extension is the OmniPVD Object Tree, while similar to the USD Stage view has some distinct features, namely

  • Objects are not required to be displayed with the USD Prim names (which have to follow SDF path rules), instead they can derive the name from the OmniPVD object name (a string set at the object creation call), a PhysX actor name or a combination of the OmniPVD class name and a unique identifier

  • Objects have the OmniPVD type displayed in the type column, not the USD Prim type

The Object Tree also has a search bar, which differs from the USD Stage search bar, in some ways

  • The search bar is not displaying the search results in a flat list, instead if expands the matching objects (based on their OVDTree name and UID) and allows for iterating through the results using the Prev/Next buttons

  • It is possible to collapse the view and thus make sure only the root objects in the tree are visible, this is a convenience not to have to collapse all the nodes/objects manually, as every search result accumulates the expansions to the existing ones, for any objects/Prims that match the search criteria

The USD hierarchy in the resulting cached Stage file (that is generated when an OVD file is imported) starts of with a scene, then lists the dynamic rigid bodies, static rigid bodies and then articulations of an OVD recording. For each body, one has an actor or articulation link with accompanying shapes and geometries. Each USD Prim has a set of custom USD attributes, which can be explored in detail and if they change over time, can be scrubbed through the animation and one can see how they are updated.

../_images/omnipvd_stage.png

Another view of the Stage is in the OmniPVD Object Tree, which allows for using of a non-SDF compliant name display (as opposed to USD), it mirrors the USD Stage, but displays the Prims with the PhysX derived name, or OmniPVD object name (given at creation time) otherwise with the OmniPVD type. A Unique Identifier (UID) is prepended to all the object, which is connected to the moment an object was created. It follows the creation/removal of objects, so only the objects that are active in the PhysX engine at that specific frame (defined by the timeline interface scrubber) are displayed in the tree. This is to have a more precise visualisation of what was actually simulated in the frame.

Selection in the viewport will select the Prim geometries but will make sure that selection affects the PxActors that are above the geometries in the OVD Tree. This is to make sure that selection is a smooth and intuitive operation and does not break the existing interaction paradigm of the USD Stage.

../_images/omnipvd_ovd_tree_hierarchy.png

One can also search in the names of the OmniPVD objects or the UID, as opposed to the search bar in the USD Stage (which only allows for search in the Prim path and Prim name), and one also gets the resulting searches expanded and coloured, with the possibility to walk over the results using the Next and Prev buttons. The “Collapse All” button, does what one would expect and collapses all expanded nodes of the tree, which is useful to do if one has done many searches as each search continues to expand nodes each time, resulting ultimately to the expansion of all nodes if one searches for a string that is part of all object names.

../_images/omnipvd_ovd_tree_search.png

Below the Object Tree, is the Property window and inside the Physics widget lives the OmniPVD Property widget, which displays the OmniPVD attributes in a pretty printed form. The attributes in the widget get updated with the timeline changes in the USD Stage.

../_images/omnipvd_ovd_properties.png

OmniPVD Visualization Gizmos

There are several ways to augment the imported OVD file with OmniPVD gizmos, they are mainly set to None, All or Selected from the menu below. The drop down menus with the scaling values attached allows to scaled each type of gizmo either independently or together globally. As here below the velocity gizmos are set to Selected and their individual scale to 0.2 while the global scale is 3.8, leading to a combined scaled of 3.8 x 0.2 for the velocities.

../_images/omnipvd_gizmos_menu.png

Selection can either done from the viewport, which also ends up selecting them in the Stage and OmniPVD Object Tree windows both. Selection is multi-directional between the viewport, the Stage and the OmniPVD Object Tree. It’s for example possible to see bounding boxes which are the same axis aligned bounding boxes that PhysX uses internally to delineate the actors.

../_images/omnipvd_gizmos_selection.png

The contacts with the reference frame along with the mass reference frame are shown for the selected actors below. What is interesting is that the cone has a differently aligned mass reference frame from its transform frame.

../_images/omnipvd_gizmos_contacts.png

One can also view joints (such as D6, distance, gear, prismatic, rack and pinion and others) as for example this rope made out of D6 joints

../_images/omnipvd_gizmos_joints_d6_ropes.png

Or for example this distance joint.

../_images/omnipvd_gizmos_joints_distance.png

Or this gear joint

../_images/omnipvd_gizmos_joints_gear.png

Or this spherical joint.

../_images/omnipvd_gizmos_joints_spherical.png

Using these gizmos one increases the ability to drill down into debug data.

Registering OmniPVD Classes and Attributes At Run-Time

A way to register new classes and attributes is possible by registering them at run-time, by using the OmniPVD API and directly do the registry calls to the write stream.

By extending the code in the “File Recording Setup” after the startSampling call with ones own:

// Once the PhysX instance is created with a non-zero OmniPVD object, one can now proced to set the
omniFileWriteStream->setFileName("myoutpufile.ovd");
mOmniPvd->startSampling();

// Get the writer and issue OmniPVD registration calls
mniPvdWriter* omniWriter = mOmniPvd->getWriter();

// Let's register an "apple" class
OmniPvdClassHandle appleClassHandle = omniWriter->registerClass("apple");

// And add an attribute "tasty" which is a single float value
OmniPvdAttributeHandle appleTasteAttributeHandle = omniWriter->registerAttribute(appleClassHandle, "tasty", OmniPvdDataType::eFLOAT32, 1);

// We can now piggyback on the PhysX SDK simulator to do the frame update for us and in between each simulation loop we can set the tasty attribute for example

// For that we need to create an instance of the apple class, in this case we don't care about object grouping so the context can be
// set to any value (here 42) and the object handle as long as it's unique (here 22), the object name is not required to be set.
omniWriter->createObject(42, appleClassHandle, 22, "best_apple_ever_01");

// One can set the context value to any suiting one and the objectHandle to any unique object of the apple class
float someTaste = 9000.0f;
omniWriter->setAttribute(42, 22, appleTasteAttributeHandle, (uint8_t*)&someTaste, sizeof(float)) = 0;