Last active
November 9, 2024 07:48
-
-
Save Pierre-Terdiman/9388e5c29a8f678d7278275c73bce29b to your computer and use it in GitHub Desktop.
Adding PhysX to tinyBVH benchmark
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
See https://github.com/jbikker/tinybvh | |
PhysX is a general purpose library not focused on raycasts and the best setup for raycast performance may not be obvious. | |
Here is the code I used in the tiny BVH benchmark. Results: https://x.com/PierreTerdiman/status/1854629430246748455 | |
Adapted from https://github.com/NVIDIA-Omniverse/PhysX/tree/main/physx/snippets/snippetpathtracing | |
For building: | |
#ifdef PHYSX_BUILD | |
const bool quantized = false; // quantized trees use less memory but are usually slower for raycasts | |
const bool useSAH = false; // SAH build is much slower but can give slightly better perf. Not the PhysX default. | |
PxTriangleMesh* triangleMesh; | |
{ | |
printf( "- physx builder: " ); | |
const PxTolerancesScale scale; | |
PxCookingParams params(scale); | |
params.midphaseDesc.setToDefault(PxMeshMidPhase::eBVH34); | |
params.midphaseDesc.mBVH34Desc.quantized = quantized; | |
//params.midphaseDesc.mBVH34Desc.numPrimsPerLeaf = 2; // something else to play with | |
if(useSAH) | |
params.midphaseDesc.mBVH34Desc.buildStrategy = PxBVH34BuildStrategy::eSAH; | |
//params.midphaseDesc.setToDefault(PxMeshMidPhase::eBVH33); // legacy version | |
// We don't need the extra data structures for just doing raycasts vs the mesh | |
params.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE; | |
params.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH; | |
{ | |
PxVec3* physx_verts = new PxVec3[verts]; | |
PxU32* physx_indices = new unsigned int[verts]; | |
for (int i = 0; i < verts; i++) | |
{ | |
physx_verts[i].x = triangles[i].x, | |
physx_verts[i].y = triangles[i].y, | |
physx_verts[i].z = triangles[i].z, | |
physx_indices[i] = i; // Note: not using shared vertices. | |
} | |
t.reset(); | |
PxTriangleMeshDesc meshDesc; | |
meshDesc.points.count = verts; | |
meshDesc.points.stride = sizeof(PxVec3); | |
meshDesc.points.data = physx_verts; | |
meshDesc.triangles.count = verts/3; | |
meshDesc.triangles.stride = sizeof(int)*3; | |
meshDesc.triangles.data = physx_indices; | |
triangleMesh = PxCreateTriangleMesh(params, meshDesc); | |
float physxBuildTime = t.elapsed(); | |
printf( "%.2fms for %i triangles\n ", physxBuildTime * 1000.0f, verts / 3 ); | |
delete [] physx_indices; | |
delete [] physx_verts; | |
} | |
} | |
#endif | |
For queries: | |
#ifdef PHYSX_BUILD | |
// trace all rays three times to estimate average performance | |
{ | |
PX_SIMD_GUARD; // Put this once in the calling code, see comment below | |
PxGeomRaycastHit* hits = new PxGeomRaycastHit[N]; | |
const PxTriangleMeshGeometry meshGeom(triangleMesh); | |
const PxTransform idt(PxIdentity); | |
printf( "- PhysX (quantized: %d) (SAH: %d): ", quantized, useSAH); | |
t.reset(); | |
for (int pass = 0; pass < 3; pass++) | |
{ | |
for (int i = 0; i < N; i++) | |
{ | |
// Pass PxGeometryQueryFlag::Enum(0) to avoid the PX_SIMD_GUARD cost for each raycast. The call has some more overhead that we could | |
// get rid of for ultimate performance (e.g. internal tests to select between the legacy & current implementations, the code that deals | |
// with that extra geom transform we don't need here, etc) | |
// PhysX does backface culling by default, TinyBVH does not. So pass eMESH_BOTH_SIDES to disable culling. | |
// PhysX also computes impact position & normal but we only need the UVs, so pass eUV. Distance is always returned. | |
PxGeometryQuery::raycast((const PxVec3&)rays[i].O, (const PxVec3&)rays[i].D, meshGeom, idt, 10000.0f, PxHitFlag::eUV|PxHitFlag::eMESH_BOTH_SIDES, 1, &hits[i], sizeof(PxGeomRaycastHit), PxGeometryQueryFlag::Enum(0)); | |
} | |
} | |
float traceTimeAlt = t.elapsed() / 3.0f; | |
mrays = (float)N / traceTimeAlt; | |
printf( "%.2fms for %.2fM rays (%.2fMRays/s)\n", traceTimeAlt * 1000, (float)N * 1e-6f, mrays * 1e-6f ); | |
delete [] hits; | |
} | |
PX_RELEASE(triangleMesh); | |
#endif | |
For the Fenster test (to draw the test image) you might need some additional bits to retrieve the proper touched primitives. PhysX internally changes the order of triangles and returns an index into that internal array. You need to use a remap table to get the intial user-side index. This code worked and drew the proper test image: | |
PX_SIMD_GUARD; | |
const PxTriangleMeshGeometry meshGeom(triangleMesh); | |
const PxTransform idt(PxIdentity); | |
const PxU32* remap = triangleMesh->getTrianglesRemap(); | |
PxGeomRaycastHit hit; | |
for (int i = 0; i < N; i++) | |
{ | |
if(PxGeometryQuery::raycast((const PxVec3&)rays[i].O, (const PxVec3&)rays[i].D, meshGeom, idt, 10000.0f, PxHitFlag::eUV|PxHitFlag::eMESH_BOTH_SIDES, 1, &hit, sizeof(PxGeomRaycastHit), PxGeometryQueryFlag::Enum(0))) | |
{ | |
rays[i].hit.t = hit.distance; | |
rays[i].hit.u = hit.u; | |
rays[i].hit.v = hit.v; | |
rays[i].hit.prim = remap[hit.faceIndex]; | |
} | |
else | |
{ | |
rays[i].hit.t = -1.0f; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment