Omniverse Visual Debugger
OmniPVD
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 Create environment. The viewer is part of Omniverse Create as an extension called omni.physx.pvd. Using Omniverse Create one can import and view PhysX scenes and inspect and visualize variables of many PhysX objects.
The purpose of OmniPVD is twofold
To record simulation attributes to gain insight into the PhysX SDK scene and object states and how they evolve from frame to frame
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 Create and install the OmniPVD extension. This extension gives the opportunity to import OVD files and to explore them as animations.
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
A list of OmniPVD class and attribute registrations
And then receives for each frame
A list of OmniPVD object creation/destruction and attribute setting operations
A list of OmniPVD object transform attribute updates
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 Create:
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");
mOmniPvd->startSampling();
// 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 Create one can view the exported OVD files.
The OmniPVD Kit 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.
Importing an OVD Recording
Once a simulation is recorded into an OmniPVD OVD file, inside the PhysX SDK or even by Omniverse Create 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 Create, 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.
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.
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.
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.
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.
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.
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.
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.
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
Or for example this distance joint.
Or this gear joint
Or this spherical joint.
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", OmniPvdDataTypeEnum::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->setAttributeShallow(42, 22, appleTasteAttributeHandle, (uint8_t*)&someTaste, sizeof(float)) = 0;