The PhysX Visual Debugger (PVD) provides a graphical view of the PhysX scene and includes various tools to inspect and visualize variables of every PhysX object. Additionally it can also record and visualize memory and timing data.
PVD can be downloaded from: https://developer.nvidia.com/physx-visual-debugger
Questions regarding the usage of the GUI should all be answered by its detailed built-in help.
PVD integration is enabled in the debug, checked and profiling configurations of the SDK. In order to reduce memory footprint and code size, it is not enabled in the release configuration.
The SDK outputs the PVD debugging data in form of a stream. PVD supports reading the stream either from a TCP/IP network socket or from a file.
Streaming to TCP/IP is supported on almost all platforms, and is usually the most convenient way to collect PVD data. In this mode the stream can be watched in real-time, depending on network speed and scene complexity. In network mode PVD acts as a TCP/IP server and must therefore be launched before the SDK tries to connect to it. The default listening port is 5425:
use namespace physx;
PxPvd* pvd = PxCreatePvd(*foundation);
PxPvdTransport* transport = PxDefaultPvdSocketTransportCreate(PVD_HOST, 5425, 10);
pvd->connect(*transport,PxPvdInstrumentationFlag::eALL);
PxPhysics* physics = PxCreatePhysics(PX_PHYSICS_VERSION, *gFoundation, PxTolerancesScale(), true, pvd);
//After releasing PxPhysics, release the PVD
physics->release();
pvd->release();
transport->release();
Streaming to file is an alternative to network streams. This is the recommended fall-back in case your platform or system setup does not support a network connection to PVD. File streams are often faster than network sockets and therefore a good alternative if performance is more important than real-time viewing. Streams stored as files can be loaded by drag&drop or over the File->Load menu in PVD:
use namespace physx;
PxPvd* pvd = PxCreatePvd(*foundation);
PxPvdTransport* transport = PxDefaultPvdFileTransportCreate(filename);
pvd->connect(*transport,PxPvdInstrumentationFlag::eALL);
PxPhysics* physics = PxCreatePhysics(PX_PHYSICS_VERSION, *gFoundation, PxTolerancesScale(),true, pvd);
//After releasing PxPhysics, release the PVD
physics->release();
pvd->release();
transport->release();
To optimize the stream size we provide flags to enable specific features. This has both influence on PVD's and the SDK's performance:
Setup to transfer only profiling data over network:
pvd->connect(*transport, PxPvdInstrumentationFlag::ePROFILE);
Joints are implemented as an extension to the SDK constraints and therefore need special handling to get transmitted to PVD. Both joint and contact data can increase the stream size significantly. Visualizing it in PVD is therefore disabled by default. To enable them use following API calls:
mScene->getScenePvdClient()->setScenePvdFlags(PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS | PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES | PxPvdSceneFlag::eTRANSMIT_CONTACTS);
or set the flags separately:
mScene->getScenePvdClient()->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS, true);
Visualizing SceneQuery in PVD is disabled by default since queries and hits data can increase the stream size significantly. To enable it use following API calls:
mScene->getScenePvdClient()->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES, true);
Implement the PvdClient interface if your application needs to react upon connection or disconnection from PVD, or if you plan to send custom PVD events from your application. It is recommended to toggle the contact and constraint visualization in the onPvdConnected/onPvdDisconnected callbacks to avoid potential memory and compute overhead in the SDK:
// derive from PvdClient
struct MyPvdClient : public physx::pvdsdk::PvdClient
{
virtual void onPvdConnected()
{
// 1. create a PvdDataStream
// 2. send your custom PVD class descriptions from here
// this then allows PVD to correctly identify and represent
// custom data that is sent from your application to a PxVisualDebuggerConnection.
// example in JointConnectionHandler
// 3. do something when successfully connected
// e.g. enable contact and constraint visualization
}
virtual void onPvdDisconnected()
{
// handle disconnection, release PvdDataStream
// e.g. disable contact and constraint visualization
}
//impleament other methods
...
};
// register custom handler
MyPvdClient myPvdClient;
pvd->addClient(myPvdClient);
PhysX SDK sends all its own error messages to PVD if PVD is connected. In addition, you can call Ps::Foundation::error() or Ps::Foundation::getErrorHandler()::reportError() to report your error message. These functions will send error messages to PVD automatically.
The messages will be listed in ErrorStream view of PVD.
When using PxPvdInstrumentationFlag::ePROFILE, PVD internally calls PxSetProfilerCallback() to set itself up as the current profiler. This happens during the PxPvd::connect() call, and it overrides the potentially already existing profiler callback. That is, if users call PxSetProfilerCallback() with their own user profiler callback, and then initialize PVD with PxPvdInstrumentationFlag::ePROFILE, then the user profiler callback is lost. Similarly, initializing PVD first then calling PxSetProfilerCallback() will make PVD's profiling results vanish.
In case both PVD's internal profiling and a user's custom profiling are needed at the same time, it is recommended to initialize PVD first, then call PxSetProfilerCallback() with your own profiler. In your implementation, call the PVD profiling functions manually, before or after performing your own profiling operations:
struct UserProfilerCallback : public PxProfilerCallback
{
PxPvd* mPvd;
virtual void* zoneStart(const char* eventName, bool detached, uint64_t contextId)
{
// Do custom profiling here
// Then re-route to PVD implementation
return mPvd->zoneStart(eventName, detached, contextId);
}
virtual void zoneEnd(void* profilerData, const char* eventName, bool detached, uint64_t contextId)
{
// Do custom profiling here
// Then re-route to PVD implementation
mPvd->zoneEnd(profilerData, eventName, detached, contextId);
}
};
This is illustrated in SnippetCustomProfiler.