BGE performance: second round of scenegraph improvement.

Use dynamic linked list to handle scenegraph rather than dumb scan
of the whole tree. The performance improvement depends on the fraction
of moving objects. If most objects are static, the speed up is 
considerable. The following table compares the time spent on 
scenegraph before and after this commit on a scene with 10000 objects
in various configuratons:

Scenegraph time (ms)              Before         After
(includes culling)

All objects static,               8.8            1.7  
all visible but small fraction          
in the view frustrum

All objects static,               7,5            0.01
all invisible.

All objects moving,               14.1           8.4
all visible but small fraction
in the view frustrum

This tables shows that static and invisible objects take no CPU at all
for scenegraph and culling. In the general case, this commit will 
speed up the scenegraph between 2x and 5x. Compared to 2.48a, it should
be between 4x and 10x faster. Further speed up is possible by making
the scenegraph cache-friendly.

Next round of performance improvement will be on the rasterizer: use
the same dynamic linked list technique for the mesh slots.
This commit is contained in:
2009-05-03 22:29:00 +00:00
parent 2aa3c932d0
commit 3abb8e8e68
14 changed files with 649 additions and 420 deletions

View File

@@ -29,6 +29,7 @@
#ifndef __SG_IOBJECT
#define __SG_IOBJECT
#include "SG_QList.h"
#include <vector>
// used for debugging: stage of the game engine main loop at which a Scenegraph modification is done
@@ -84,6 +85,18 @@ typedef void (*SG_UpdateTransformCallback)(
void* clientinfo
);
typedef bool (*SG_ScheduleUpdateCallback)(
SG_IObject* sgobject,
void* clientobj,
void* clientinfo
);
typedef bool (*SG_RescheduleUpdateCallback)(
SG_IObject* sgobject,
void* clientobj,
void* clientinfo
);
/**
* SG_Callbacks hold 2 call backs to the outside world.
@@ -106,30 +119,38 @@ struct SG_Callbacks
):
m_replicafunc(NULL),
m_destructionfunc(NULL),
m_updatefunc(NULL)
m_updatefunc(NULL),
m_schedulefunc(NULL),
m_reschedulefunc(NULL)
{
};
SG_Callbacks(
SG_ReplicationNewCallback repfunc,
SG_DestructionNewCallback destructfunc,
SG_UpdateTransformCallback updatefunc
SG_UpdateTransformCallback updatefunc,
SG_ScheduleUpdateCallback schedulefunc,
SG_RescheduleUpdateCallback reschedulefunc
):
m_replicafunc(repfunc),
m_destructionfunc(destructfunc),
m_updatefunc(updatefunc)
m_updatefunc(updatefunc),
m_schedulefunc(schedulefunc),
m_reschedulefunc(reschedulefunc)
{
};
SG_ReplicationNewCallback m_replicafunc;
SG_DestructionNewCallback m_destructionfunc;
SG_UpdateTransformCallback m_updatefunc;
SG_ScheduleUpdateCallback m_schedulefunc;
SG_RescheduleUpdateCallback m_reschedulefunc;
};
/**
base object that can be part of the scenegraph.
*/
class SG_IObject
class SG_IObject : public SG_QList
{
private :
@@ -177,9 +198,10 @@ public:
* using STL?
*/
SGControllerList&
GetSGControllerList(
);
SGControllerList& GetSGControllerList()
{
return m_SGcontrollers;
}
/**
@@ -192,16 +214,16 @@ public:
* This may be NULL.
*/
void*
GetSGClientObject(
);
inline const void* GetSGClientObject() const
{
return m_SGclientObject;
}
const
void*
GetSGClientObject(
) const ;
inline void* GetSGClientObject()
{
return m_SGclientObject;
}
/**
* Set the client object for this node. This is just a
* pointer to an object allocated that should exist for
@@ -209,10 +231,10 @@ public:
* this function is called again.
*/
void
SetSGClientObject(
void* clientObject
);
void SetSGClientObject(void* clientObject)
{
m_SGclientObject = clientObject;
}
/**
* Set the current simulation time for this node.
@@ -220,10 +242,7 @@ public:
* the nodes list of controllers and calls their SetSimulatedTime methods
*/
void
SetControllerTime(
double time
);
void SetControllerTime(double time);
virtual
void
@@ -235,20 +254,76 @@ protected :
bool
ActivateReplicationCallback(
SG_IObject *replica
);
)
{
if (m_callbacks.m_replicafunc)
{
// Call client provided replication func
if (m_callbacks.m_replicafunc(replica,m_SGclientObject,m_SGclientInfo) == NULL)
return false;
}
return true;
}
void
ActivateDestructionCallback(
);
)
{
if (m_callbacks.m_destructionfunc)
{
// Call client provided destruction function on this!
m_callbacks.m_destructionfunc(this,m_SGclientObject,m_SGclientInfo);
}
else
{
// no callback but must still destroy the node to avoid memory leak
delete this;
}
}
void
ActivateUpdateTransformCallback(
);
)
{
if (m_callbacks.m_updatefunc)
{
// Call client provided update func.
m_callbacks.m_updatefunc(this, m_SGclientObject, m_SGclientInfo);
}
}
bool
ActivateScheduleUpdateCallback(
)
{
// HACK, this check assumes that the scheduled nodes are put on a DList (see SG_Node.h)
// The early check on Empty() allows up to avoid calling the callback function
// when the node is already scheduled for update.
if (Empty() && m_callbacks.m_schedulefunc)
{
// Call client provided update func.
return m_callbacks.m_schedulefunc(this, m_SGclientObject, m_SGclientInfo);
}
return false;
}
void
ActivateRecheduleUpdateCallback(
)
{
if (m_callbacks.m_reschedulefunc)
{
// Call client provided update func.
m_callbacks.m_reschedulefunc(this, m_SGclientObject, m_SGclientInfo);
}
}
SG_IObject(
void* clientobj,
void* clientinfo,
SG_Callbacks callbacks
SG_Callbacks& callbacks
);
SG_IObject(