Access Bullet Profile Timers in Ammo.js
This article explains how to access and utilize the internal Bullet physics profiling timers within Ammo.js to measure simulation performance. You will learn how to expose the C++ profile manager classes to JavaScript, retrieve timing data, and recursively traverse the physics step breakdown.
Expose Bullet Profiler in ammo.idl
Ammo.js is a direct port of the Bullet physics engine compiled via
Emscripten. Because the performance profiling classes are not always
exposed in default Ammo.js builds, you must ensure they are defined in
your ammo.idl file before compilation.
Add the following WebIDL definitions to your ammo.idl to
expose the necessary C++ profiling interfaces:
interface CProfileIterator {
void First();
void Next();
boolean Is_Done();
boolean Is_Root();
void Enter_Child(long index);
void Enter_Parent();
DOMString Get_Current_Name();
float Get_Current_Total_Time();
long Get_Current_Total_Calls();
};
interface CProfileManager {
static void Reset();
static void Increment_Frame_Counter();
static CProfileIterator Get_Iterator();
static void Release_Iterator(CProfileIterator iterator);
static float Get_Time_Since_Reset();
};
Enable Profiling in Your Code
To gather profiling data, the internal Bullet timers must be updated
each frame. Call Increment_Frame_Counter at the end of your
game loop, right after stepping the physics world.
// Step the physics world
physicsWorld.stepSimulation(deltaTime, 10);
// Increment the profiler frame counter
Ammo.CProfileManager.Increment_Frame_Counter();Retrieve and Traverse Profile Data
Once the frame counter is incremented, use the
CProfileIterator to navigate the hierarchical execution
tree of the physics engine (such as collision detection, constraint
solving, and integration).
Here is a recursive JavaScript function to extract and print the profiling hierarchy:
function dumpProfileData() {
let iterator = Ammo.CProfileManager.Get_Iterator();
console.log("--- Physics Profile Frame ---");
traverseNode(iterator, 0);
Ammo.CProfileManager.Release_Iterator(iterator);
}
function traverseNode(iterator, depth) {
iterator.First();
let indent = " ".repeat(depth);
while (!iterator.Is_Done()) {
let name = iterator.Get_Current_Name();
let totalTime = iterator.Get_Current_Total_Time();
let totalCalls = iterator.Get_Current_Total_Calls();
console.log(`${indent}${name}: ${totalTime.toFixed(3)} ms (${totalCalls} calls)`);
// Enter child nodes recursively
iterator.Enter_Child(0); // Index is typically ignored or managed internally
traverseNode(iterator, depth + 1);
iterator.Enter_Parent();
iterator.Next();
}
}Reset Timers
To clear accumulated timing statistics and start a fresh profiling session, invoke the static reset method:
Ammo.CProfileManager.Reset();