Skip to content

Commit

Permalink
initial cast benchmark
Browse files Browse the repository at this point in the history
update mass options instead of automatic mass
  • Loading branch information
erincatto committed Oct 5, 2024
1 parent b864f53 commit 6336023
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 18 deletions.
10 changes: 6 additions & 4 deletions include/box2d/box2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,10 @@ B2_API b2ShapeId b2CreateCapsuleShape( b2BodyId bodyId, const b2ShapeDef* def, c
/// @return the shape id for accessing the shape
B2_API b2ShapeId b2CreatePolygonShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Polygon* polygon );

/// Destroy a shape
B2_API void b2DestroyShape( b2ShapeId shapeId );
/// Destroy a shape. You may defer the body mass update which can improve performance if several shapes on a
/// body are destroyed at once.
/// @see b2Body_ApplyMassFromShapes
B2_API void b2DestroyShape( b2ShapeId shapeId, bool updateBodyMass );

/// Shape identifier validation. Provides validation for up to 64K allocations.
B2_API bool b2Shape_IsValid( b2ShapeId id );
Expand All @@ -492,9 +494,9 @@ B2_API void b2Shape_SetUserData( b2ShapeId shapeId, void* userData );
B2_API void* b2Shape_GetUserData( b2ShapeId shapeId );

/// Set the mass density of a shape, typically in kg/m^2.
/// This will not update the mass properties on the parent body.
/// This will optionally update the mass properties on the parent body.
/// @see b2ShapeDef::density, b2Body_ApplyMassFromShapes
B2_API void b2Shape_SetDensity( b2ShapeId shapeId, float density );
B2_API void b2Shape_SetDensity( b2ShapeId shapeId, float density, bool updateBodyMass );

/// Get the density of a shape, typically in kg/m^2
B2_API float b2Shape_GetDensity( b2ShapeId shapeId );
Expand Down
7 changes: 3 additions & 4 deletions include/box2d/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,6 @@ typedef struct b2BodyDef
/// Used to disable a body. A disabled body does not move or collide.
bool isEnabled;

/// Automatically compute mass and related properties on this body from shapes.
/// Triggers whenever a shape is add/removed/changed. Default is true.
bool automaticMass;

/// This allows this body to bypass rotational speed limits. Should only be used
/// for circular objects, like wheels.
bool allowFastRotation;
Expand Down Expand Up @@ -367,6 +363,9 @@ typedef struct b2ShapeDef
/// This is implicitly always true for sensors.
bool forceContactCreation;

/// Should the body update the mass properties when this shape is created. Default is true.
bool updateBodyMass;

/// Used internally to detect a valid definition. DO NOT SET.
int32_t internalValue;
} b2ShapeDef;
Expand Down
227 changes: 224 additions & 3 deletions samples/sample_benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,17 @@ class BenchmarkTumbler : public Sample
polygon = b2MakeOffsetBox( 10.0f, 0.5f, { 0.0f, -10.0f }, b2Rot_identity );
b2CreatePolygonShape( bodyId, &shapeDef, &polygon );

shapeDef.customColor = b2_colorBlueViolet;
b2Circle circle = { { 5.0f, 5.0f }, 1.0f };
b2CreateCircleShape( bodyId, &shapeDef, &circle );
circle = { { 5.0f, -5.0f }, 1.0f };
b2CreateCircleShape( bodyId, &shapeDef, &circle );
circle = { { -5.0f, -5.0f }, 1.0f };
b2CreateCircleShape( bodyId, &shapeDef, &circle );
circle = { { -5.0f, 5.0f }, 1.0f };
b2CreateCircleShape( bodyId, &shapeDef, &circle );


// m_motorSpeed = 9.0f;
m_motorSpeed = 25.0f;

Expand Down Expand Up @@ -1436,8 +1447,8 @@ class BenchmarkCompound : public Sample
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_dynamicBody;
// defer mass properties to avoid n-squared mass computations
bodyDef.automaticMass = false;
b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.updateBodyMass = false;

for ( int m = 0; m < count; ++m )
{
Expand Down Expand Up @@ -1498,13 +1509,14 @@ class BenchmarkKinematic : public Sample
b2BodyDef bodyDef = b2DefaultBodyDef();
bodyDef.type = b2_kinematicBody;
bodyDef.angularVelocity = 1.0f;
// defer mass properties to avoid n-squared mass computations
bodyDef.automaticMass = false;

b2ShapeDef shapeDef = b2DefaultShapeDef();
shapeDef.filter.categoryBits = 1;
shapeDef.filter.maskBits = 2;

// defer mass properties to avoid n-squared mass computations
shapeDef.updateBodyMass = false;

b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );

for ( int i = -span; i < span; ++i )
Expand All @@ -1529,3 +1541,212 @@ class BenchmarkKinematic : public Sample
};

static int sampleKinematic = RegisterSample( "Benchmark", "Kinematic", BenchmarkKinematic::Create );

#if 1

enum QueryType
{
e_rayCast,
e_shapeCast,
e_overlap,
};

class BenchmarkCast : public Sample
{
public:
explicit BenchmarkCast( Settings& settings )
: Sample( settings )
{
if ( settings.restart == false )
{
g_camera.m_center = { 500.0f, 500.0f };
g_camera.m_zoom = 25.0f * 21.0f;
}

m_queryType = e_rayCast;
m_ratio = 5.0f;
m_grid = 1.0f;
m_fill = 0.1f;
m_rowCount = g_sampleDebug ? 100 : 1000;
m_columnCount = g_sampleDebug ? 100 : 1000;
m_categoryBits = true;

BuildScene();
}

void BuildScene()
{
g_seed = 1234;
b2DestroyWorld( m_worldId );
b2WorldDef worldDef = b2DefaultWorldDef();
m_worldId = b2CreateWorld( &worldDef );

b2BodyDef bodyDef = b2DefaultBodyDef();
b2ShapeDef shapeDef = b2DefaultShapeDef();

float y = 0.0f;

for ( int i = 0; i < m_rowCount; ++i )
{
float x = 0.0f;

for ( int j = 0; j < m_columnCount; ++j )
{
float fillTest = RandomFloat( 0.0f, 1.0f );
if ( fillTest <= m_fill )
{
bodyDef.position = { x, y };
b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef );

float ratio = RandomFloat( 1.0f, m_ratio );
float halfWidth = RandomFloat( 0.05f, 0.25f );

b2Polygon box;
if ( RandomFloat() > 0.0f )
{
box = b2MakeBox( ratio * halfWidth, halfWidth );
}
else
{
box = b2MakeBox( halfWidth, ratio * halfWidth );
}

int category = RandomInt( 1, 3 );
shapeDef.filter.categoryBits = category;
if ( category == 1 )
{
shapeDef.customColor = b2_colorBox2DBlue;
}
else if ( category == 2 )
{
shapeDef.customColor = b2_colorBox2DYellow;
}
else
{
shapeDef.customColor = b2_colorBox2DGreen;
}

b2CreatePolygonShape( bodyId, &shapeDef, &box );
}

x += m_grid;
}

y += m_grid;
}
}

void UpdateUI() override
{
float height = 320.0f;
ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once );
ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) );

ImGui::Begin( "Cast", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize );

ImGui::PushItemWidth( 100.0f );

bool changed = false;
if ( ImGui::SliderInt( "rows", &m_rowCount, 0, 1000, "%d" ) )
{
changed = true;
}

if ( ImGui::SliderInt( "columns", &m_columnCount, 0, 1000, "%d" ) )
{
changed = true;
}

if ( ImGui::SliderFloat( "fill", &m_fill, 0.0f, 1.0f, "%.2f" ) )
{
changed = true;
}

if ( ImGui::SliderFloat( "grid", &m_grid, 0.5f, 2.0f, "%.2f" ) )
{
changed = true;
}

if ( ImGui::SliderFloat( "ratio", &m_ratio, 1.0f, 10.0f, "%.2f" ) )
{
changed = true;
}

if ( ImGui::Checkbox( "categories", &m_categoryBits) )
{
changed = true;
}

const char* queryTypes[] = { "Ray Cast", "Circle Cast", "Overlap" };
int queryType = int( m_queryType );
changed = changed || ImGui::Combo( "Query", &queryType, queryTypes, IM_ARRAYSIZE( queryTypes ) );
m_queryType = QueryType( queryType );

ImGui::PopItemWidth();
ImGui::End();

if ( changed )
{
BuildScene();
}
}

void Step( Settings& settings) override
{
Sample::Step( settings );

int sampleCount = g_sampleDebug ? 10 : 1000;

float extent = m_rowCount * m_grid;
b2QueryFilter filter = b2DefaultQueryFilter();
filter.maskBits = 1;
int hitCount = 0;
float ms = 0.0f;

if (m_queryType == e_rayCast)
{
b2Timer timer = b2CreateTimer();

b2Vec2 rayStart = b2Vec2_zero;
b2Vec2 rayEnd = b2Vec2_zero;
for (int i = 0; i < sampleCount; ++i)
{
rayStart = RandomVec2( 0.0f, extent );
rayEnd = RandomVec2( 0.0f, extent );

b2RayResult result = b2World_CastRayClosest( m_worldId, rayStart, b2Sub( rayEnd, rayStart ), filter );
hitCount += result.hit ? 1 : 0;
}

ms = b2GetMilliseconds( &timer );

g_draw.DrawSegment( rayStart, rayEnd, b2_colorBeige );
}

g_draw.DrawString( 5, m_textLine, "hit count = %03d", hitCount );
m_textLine += m_textIncrement;

g_draw.DrawString( 5, m_textLine, "ms = %.3f",ms );
m_textLine += m_textIncrement;
}

static Sample* Create( Settings& settings )
{
return new BenchmarkCast( settings );
}

QueryType m_queryType;

std::vector<b2Vec2> m_origins;
std::vector<b2Vec2> m_translations;

int m_rowCount, m_columnCount;
int m_updateType;
float m_fill;
float m_ratio;
float m_grid;
bool m_categoryBits;
};

static int sampleCast = RegisterSample( "Benchmark", "Cast", BenchmarkCast::Create );
#endif
10 changes: 9 additions & 1 deletion samples/sample_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,9 +672,17 @@ class ContactEvent : public Sample
m_debrisIds[index] = b2_nullBodyId;
}


for ( int i = 0; i < destroyCount; ++i )
{
b2DestroyShape( shapesToDestroy[i] );
bool updateMass = false;
b2DestroyShape( shapesToDestroy[i], updateMass );
}

if (destroyCount > 0)
{
// Update mass just once
b2Body_ApplyMassFromShapes( m_playerId );
}

if ( settings.hertz > 0.0f && settings.pause == false )
Expand Down
1 change: 0 additions & 1 deletion src/body.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,6 @@ b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def )
body->fixedRotation = def->fixedRotation;
body->isSpeedCapped = false;
body->isMarked = false;
body->automaticMass = def->automaticMass;

// dynamic and kinematic bodies that are enabled need a island
if ( setId >= b2_awakeSet )
Expand Down
14 changes: 10 additions & 4 deletions src/shape.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ b2ShapeId b2CreateShape( b2BodyId bodyId, const b2ShapeDef* def, const void* geo

b2Shape* shape = b2CreateShapeInternal( world, body, transform, def, geometry, shapeType );

if ( body->automaticMass == true )
if ( def->updateBodyMass == true )
{
b2UpdateBodyMassData( world, body );
}
Expand Down Expand Up @@ -262,7 +262,7 @@ void b2DestroyShapeInternal( b2World* world, b2Shape* shape, b2Body* body, bool
b2ValidateSolverSets( world );
}

void b2DestroyShape( b2ShapeId shapeId )
void b2DestroyShape( b2ShapeId shapeId, bool updateBodyMass )
{
b2World* world = b2GetWorldLocked( shapeId.world0 );

Expand All @@ -274,7 +274,7 @@ void b2DestroyShape( b2ShapeId shapeId )
b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
b2DestroyShapeInternal( world, shape, body, wakeBodies );

if ( body->automaticMass == true )
if ( updateBodyMass == true )
{
b2UpdateBodyMassData( world, body );
}
Expand Down Expand Up @@ -911,7 +911,7 @@ b2CastOutput b2Shape_RayCast( b2ShapeId shapeId, const b2RayCastInput* input )
return output;
}

void b2Shape_SetDensity( b2ShapeId shapeId, float density )
void b2Shape_SetDensity( b2ShapeId shapeId, float density, bool updateBodyMass )
{
B2_ASSERT( b2IsValid( density ) && density >= 0.0f );

Expand All @@ -929,6 +929,12 @@ void b2Shape_SetDensity( b2ShapeId shapeId, float density )
}

shape->density = density;

if (updateBodyMass == true)
{
b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId );
b2UpdateBodyMassData( world, body );
}
}

float b2Shape_GetDensity( b2ShapeId shapeId )
Expand Down
2 changes: 1 addition & 1 deletion src/types.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ b2BodyDef b2DefaultBodyDef( void )
def.enableSleep = true;
def.isAwake = true;
def.isEnabled = true;
def.automaticMass = true;
def.internalValue = B2_SECRET_COOKIE;
return def;
}
Expand All @@ -62,6 +61,7 @@ b2ShapeDef b2DefaultShapeDef( void )
def.filter = b2DefaultFilter();
def.enableSensorEvents = true;
def.enableContactEvents = true;
def.updateBodyMass = true;
def.internalValue = B2_SECRET_COOKIE;
return def;
}
Expand Down

0 comments on commit 6336023

Please sign in to comment.