Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

working on threaded pose support

  • Loading branch information...
commit 65dcf8c707df27762a8654a3db61c496e2e247ef 1 parent da27f40
@rtv authored
View
0  docsrc/Markdown.pl 100644 → 100755
File mode changed
View
0  docsrc/sourcedocs.sh 100644 → 100755
File mode changed
View
328 libstage/#blockgroup.cc#
@@ -1,328 +0,0 @@
-
-#include "stage.hh"
-#include "worldfile.hh"
-
-#include <libgen.h> // for dirname(3)
-#include <limits.h> // for _POSIX_PATH_MAX
-
-#undef DEBUG
-
-using namespace Stg;
-using namespace std;
-
-BlockGroup::BlockGroup()
- : displaylist(0),
- blocks(),
- minx(0),
- maxx(0),
- miny(0),
- maxy(0)
-{ /* empty */ }
-
-BlockGroup::~BlockGroup()
-{
- Clear();
-}
-
-void BlockGroup::AppendBlock( Block* block )
-{
- blocks.insert( block );
-}
-
-void BlockGroup::Clear()
-{
- FOR_EACH( it, blocks )
- delete *it;
-
- blocks.clear();
-}
-
-void BlockGroup::SwitchToTestedCells()
-{
- // confirm the tentative pose for all blocks
- FOR_EACH( it, blocks )
- (*it)->SwitchToTestedCells();
-}
-
-void BlockGroup::AppendTouchingModels( ModelPtrSet &v )
-{
- FOR_EACH( it, blocks )
- (*it)->AppendTouchingModels( v );
-}
-
-Model* BlockGroup::TestCollision()
-{
- Model* hitmod = NULL;
-
- FOR_EACH( it, blocks )
- if( (hitmod = (*it)->TestCollision()))
- break; // bail on the earliest collision
-
- return hitmod; // NULL if no collision
-}
-
-
-// establish the min and max of all the blocks, so we can scale this
-// group later
-void BlockGroup::CalcSize()
-{
- // assuming the blocks currently fit in a square +/- one billion units
- //double minx, miny, maxx, maxy, minz, maxz;
- minx = miny = billion;
- maxx = maxy = -billion;
-
-x size.z = 0.0; // grow to largest z we see
-
- FOR_EACH( it, blocks )
- {
- // examine all the points in the polygon
- Block* block = *it;
-
- FOR_EACH( it, block->pts )
- {
- if( it->x < minx ) minx = it->x;
- if( it->y < miny ) miny = it->y;
- if( it->x > maxx ) maxx = it->x;
- if( it->y > maxy ) maxy = it->y;
- }
-
- size.z = std::max( block->local_z.max, size.z );
- }
-
- // store these bounds for normalization purposes
- size.x = maxx-minx;
- size.y = maxy-miny;
-
- offset.x = minx + size.x/2.0;
- offset.y = miny + size.y/2.0;
- offset.z = 0; // todo?
-
- InvalidateModelPointCache();
-}
-
-
-void BlockGroup::Map()
-{
- FOR_EACH( it, blocks )
- (*it)->Map();
-}
-
-void BlockGroup::UnMap()
-{
- FOR_EACH( it, blocks )
- (*it)->UnMap();
-}
-
-void BlockGroup::DrawSolid( const Geom & geom )
-{
- glPushMatrix();
-
- Gl::pose_shift( geom.pose );
-
- glScalef( geom.size.x / size.x,
- geom.size.y / size.y,
- geom.size.z / size.z );
-
- glTranslatef( -offset.x, -offset.y, -offset.z );
-
- FOR_EACH( it, blocks )
- (*it)->DrawSolid();
-
- glPopMatrix();
-}
-
-void BlockGroup::DrawFootPrint( const Geom & geom )
-{
- glPushMatrix();
-
- glScalef( geom.size.x / size.x,
- geom.size.y / size.y,
- geom.size.z / size.z );
-
- glTranslatef( -offset.x, -offset.y, -offset.z );
-
- FOR_EACH( it, blocks )
- (*it)->DrawFootPrint();
-
- glPopMatrix();
-}
-
-void BlockGroup::BuildDisplayList( Model* mod )
-{
- //puts( "build" );
-
- if( ! mod->world->IsGUI() )
- return;
-
- //printf( "display list for model %s\n", mod->token );
- if( displaylist == 0 )
- {
- displaylist = glGenLists(1);
- CalcSize();
- }
-
- glNewList( displaylist, GL_COMPILE );
-
- Geom geom = mod->GetGeom();
-
- Gl::pose_shift( geom.pose );
-
- glScalef( geom.size.x / size.x,
- geom.size.y / size.y,
- geom.size.z / size.z );
-
- glTranslatef( -offset.x, -offset.y, -offset.z );
-
- glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
- glEnable(GL_POLYGON_OFFSET_FILL);
- glPolygonOffset(1.0, 1.0);
-
- mod->PushColor( mod->color );
-
- FOR_EACH( it, blocks )
- {
- Block* blk = (*it);
-
- if( (!blk->inherit_color) && (blk->color != mod->color) )
- {
- mod->PushColor( blk->color );
- blk->DrawSolid();
- mod->PopColor();
- }
- else
- blk->DrawSolid();
- }
-
- mod->PopColor();
-
- glDisable(GL_POLYGON_OFFSET_FILL);
-
- glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
- glDepthMask(GL_FALSE);
-
- Color c = mod->color;
- c.r /= 2.0;
- c.g /= 2.0;
- c.b /= 2.0;
- mod->PushColor( c );
-
- //c.Print( "color" );
-
-
- FOR_EACH( it, blocks )
- {
- Block* blk = *it;
-
- if( (!blk->inherit_color) && (blk->color != mod->color) )
- {
- Color c = blk->color;
- c.r /= 2.0;
- c.g /= 2.0;
- c.b /= 2.0;
- mod->PushColor( c );
- //c.Print( "bar" );
- blk->DrawSolid();
- mod->PopColor();
- }
- else
- blk->DrawSolid();
- }
-
- glDepthMask(GL_TRUE);
- glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
-
- mod->PopColor();
-
- glEndList();
-}
-
-void BlockGroup::CallDisplayList( Model* mod )
-{
- if( displaylist == 0 || mod->rebuild_displaylist )
- {
- BuildDisplayList( mod );
- mod->rebuild_displaylist = 0;
- }
-
- glCallList( displaylist );
-}
-
-void BlockGroup::LoadBlock( Model* mod, Worldfile* wf, int entity )
-{
- AppendBlock( new Block( mod, wf, entity ));
-}
-
-void BlockGroup::LoadBitmap( Model* mod, const std::string& bitmapfile, Worldfile* wf )
-{
- PRINT_DEBUG1( "attempting to load bitmap \"%s\n", bitmapfile );
-
- std::string full;
-
- if( bitmapfile[0] == '/' )
- full = bitmapfile;
- else
- {
- char* workaround_const = strdup(wf->filename.c_str());
- full = std::string(dirname(workaround_const)) + "/" + bitmapfile;
- free( workaround_const );
- }
-
- PRINT_DEBUG1( "attempting to load image %s", full );
-
- std::vector<rotrect_t> rects;
- unsigned int width, height;
- if( rotrects_from_image_file( full,
- rects,
- width,
- height ) )
- {
- PRINT_ERR1( "failed to load rects from image file \"%s\"",
- full.c_str() );
- return;
- }
-
- //printf( "found %d rects in \"%s\" at %p\n",
- // rect_count, full, rects );
-
- // TODO fix this
- Color col( 1.0, 0.0, 1.0, 1.0 );
-
- FOR_EACH( rect, rects )
- {
- std::vector<point_t> pts(4);
-
- double x = rect->pose.x;
- double y = rect->pose.y;
- double w = rect->size.x;
- double h = rect->size.y;
-
- pts[0].x = x;
- pts[0].y = y;
- pts[1].x = x + w;
- pts[1].y = y;
- pts[2].x = x + w;
- pts[2].y = y + h;
- pts[3].x = x;
- pts[3].y = y + h;
-
- AppendBlock( new Block( mod,
- pts,
- 0,1,
- col,
- true,
- false) );
- }
-
- CalcSize();
-}
-
-
-void BlockGroup::Rasterize( uint8_t* data,
- unsigned int width,
- unsigned int height,
- meters_t cellwidth,
- meters_t cellheight )
-{
- FOR_EACH( it, blocks )
- (*it)->Rasterize( data, width, height, cellwidth, cellheight );
-}
View
108 libstage/block.cc
@@ -190,12 +190,9 @@ Model* Block::TestCollision()
void Block::Map()
{
- // clear out of the old cells
- RemoveFromCellArray( rendered_cells );
-
- // now calculate the local coords of the block vertices
+ // calculate the local coords of the block vertices
const size_t pt_count(pts.size());
-
+
if( mpts.size() == 0 )
{
// no valid cache of model coord points, so generate them
@@ -223,30 +220,17 @@ void Block::Map()
mapped = true;
}
-void Block::UnMap()
-{
- RemoveFromCellArray( rendered_cells );
- rendered_cells.clear();
- mapped = false;
-}
-
#include <algorithm>
#include <functional>
-inline void Block::RemoveFromCellArray( CellPtrVec& cells )
+void Block::UnMap()
{
- // FOR_EACH( it, *cells )
- // (*it)->RemoveBlock(this);
-
- // this is equivalent to the above commented code - experimenting
- // with optimizations
- std::for_each( cells.begin(),
- cells.end(),
+ std::for_each( rendered_cells.begin(),
+ rendered_cells.end(),
std::bind2nd( std::mem_fun(&Cell::RemoveBlock), this));
-}
-
-void Block::SwitchToTestedCells()
-{
+
+ rendered_cells.clear();
+ mapped = false;
}
inline point_t Block::BlockPointToModelMeters( const point_t& bpt )
@@ -410,7 +394,7 @@ void Block::Load( Worldfile* wf, int entity )
char key[128];
for( size_t p=0; p<pt_count; ++p )
{
- snprintf(key, sizeof(key), "point[%d]", p );
+ snprintf(key, sizeof(key), "point[%d]", (int)p );
pts.push_back( point_t( wf->ReadTupleLength(entity, key, 0, 0),
wf->ReadTupleLength(entity, key, 1, 0) ));
@@ -432,74 +416,6 @@ void Block::Load( Worldfile* wf, int entity )
wheel = wf->ReadInt( entity, "wheel", wheel );
}
-
-// void Block::MapLine( const point_int_t& start,
-// const point_int_t& end )
-// {
-// // line rasterization adapted from Cohen's 3D version in
-// // Graphics Gems II. Should be very fast.
-// const int32_t dx( end.x - start.x );
-// const int32_t dy( end.y - start.y );
-// const int32_t sx(sgn(dx));
-// const int32_t sy(sgn(dy));
-// const int32_t ax(abs(dx));
-// const int32_t ay(abs(dy));
-// const int32_t bx(2*ax);
-// const int32_t by(2*ay);
-// int32_t exy(ay-ax);
-// int32_t n(ax+ay);
-
-// int32_t globx(start.x);
-// int32_t globy(start.y);
-
-// while( n )
-// {
-// Region* reg( mod->GetWorld()
-// ->GetSuperRegionCreate( point_int_t(GETSREG(globx),
-// GETSREG(globy)))
-// ->GetRegion( GETREG(globx),
-// GETREG(globy)));
-
-// //printf( "REGION %p\n", reg );
-
-// // add all the required cells in this region before looking up
-// // another region
-// int32_t cx( GETCELL(globx) );
-// int32_t cy( GETCELL(globy) );
-
-// // need to call Region::GetCell() before using a Cell pointer
-// // directly, because the region allocates cells lazily, waiting
-// // for a call of this method
-// Cell* c( reg->GetCell( cx, cy ) );
-
-// // while inside the region, manipulate the Cell pointer directly
-// while( (cx>=0) && (cx<REGIONWIDTH) &&
-// (cy>=0) && (cy<REGIONWIDTH) &&
-// n > 0 )
-// {
-// c->AddBlock(this);
-
-// // cleverly skip to the next cell (now it's safe to
-// // manipulate the cell pointer)
-// if( exy < 0 )
-// {
-// globx += sx;
-// exy += by;
-// c += sx;
-// cx += sx;
-// }
-// else
-// {
-// globy += sy;
-// exy -= bx;
-// c += sy * REGIONWIDTH;
-// cy += sy;
-// }
-// --n;
-// }
-// }
-// }
-
/////////////////////////////////////////////////////////////////////////////////////////
// utility functions to ensure block winding is consistent and matches OpenGL's default
@@ -518,11 +434,9 @@ void pi_ize(radians_t& angle)
while (M_PI < angle) angle -= 2 * M_PI;
}
-typedef point_t V2;
-
static
/// util; How much was v1 rotated to get to v2?
-radians_t angle_change(V2 v1, V2 v2)
+radians_t angle_change(point_t v1, point_t v2)
{
radians_t a1 = atan2(v1.y, v1.x);
positivize(a1);
@@ -545,7 +459,7 @@ vector<point_t> find_vectors(vector<point_t> const& pts)
for (unsigned i = 0, n = pts.size(); i < n; ++i)
{
unsigned j = (i + 1) % n;
- vs.push_back(V2(pts[j].x - pts[i].x, pts[j].y - pts[i].y));
+ vs.push_back(point_t(pts[j].x - pts[i].x, pts[j].y - pts[i].y));
}
assert(vs.size() == pts.size());
return vs;
View
21 libstage/blockgroup.cc
@@ -37,13 +37,6 @@ void BlockGroup::Clear()
blocks.clear();
}
-void BlockGroup::SwitchToTestedCells()
-{
- // confirm the tentative pose for all blocks
- FOR_EACH( it, blocks )
- (*it)->SwitchToTestedCells();
-}
-
void BlockGroup::AppendTouchingModels( ModelPtrSet &v )
{
FOR_EACH( it, blocks )
@@ -107,10 +100,20 @@ void BlockGroup::Map()
(*it)->Map();
}
+// defined in world.cc
+void SwitchSuperRegionLock( SuperRegion* current, SuperRegion* next );
+
void BlockGroup::UnMap()
{
- FOR_EACH( it, blocks )
- (*it)->UnMap();
+ SuperRegion* sr = NULL;
+
+ FOR_EACH( it, blocks )
+ {
+ // SwitchSuperRegionLock( sr, (*it)->GetSuperRegions() );
+ (*it)->UnMap();
+ }
+
+// if(sr) sr->Unlock();
}
void BlockGroup::DrawSolid( const Geom & geom )
View
31 libstage/model.cc
@@ -777,15 +777,20 @@ void Model::Startup( void )
//printf( "model %s using queue %d\n", token, event_queue_num );
// if we're thread safe, we can use an event queue >0
- if( thread_safe )
- event_queue_num = world->GetEventQueue( this );
+ event_queue_num = world->GetEventQueue( this );
// put my first update request in the world's queue
- world->Enqueue( event_queue_num, interval, this );
-
+ if( thread_safe )
+ world->Enqueue( event_queue_num, interval, this );
+ else
+ world->Enqueue( 0, interval, this );
+
if( velocity_enable )
- world->active_velocity.insert( this );
-
+ {
+ world->active_velocity.insert( this );
+ world->active_velocity_threaded[event_queue_num].insert( this );
+ }
+
if( FindPowerPack() )
world->active_energy.insert( this );
@@ -800,6 +805,8 @@ void Model::Shutdown( void )
world->active_energy.erase( this );
world->active_velocity.erase( this );
+ world->active_velocity_threaded[event_queue_num].erase( this );
+
// allows data visualizations to be cleared.
NeedRedraw();
}
@@ -807,12 +814,16 @@ void Model::Shutdown( void )
void Model::Update( void )
{
- CallCallbacks( CB_UPDATE );
-
+ // CallCallbacks( CB_UPDATE );
+
last_update = world->sim_time;
world->Enqueue( event_queue_num, interval, this );
}
+void Model::CallUpdateCallbacks( void )
+{
+ CallCallbacks( CB_UPDATE );
+}
meters_t Model::ModelHeight() const
{
@@ -930,7 +941,7 @@ void Model::UpdatePose( void )
return;
// convert usec to sec
- double interval( (double)world->sim_interval / 1e6 );
+ const double interval( (double)world->sim_interval / 1e6 );
// find the change of pose due to our velocity vector
const Pose p( velocity.x * interval,
@@ -949,7 +960,7 @@ void Model::UpdatePose( void )
UnMapWithChildren(); // remove from all blocks
MapWithChildren(); // render into new blocks
- Model* hitmod = TestCollision();
+ const Model* hitmod = TestCollision();
if( hitmod ) // crunch!
{
View
16 libstage/model_callbacks.cc
@@ -2,12 +2,20 @@
using namespace Stg;
using namespace std;
+
void Model::AddCallback( callback_type_t type,
model_callback_t cb,
void* user )
{
//callbacks[address].insert( cb_t( cb, user ));
callbacks[type].insert( cb_t( cb, user ));
+
+ // debug info - record the global number of registered callbacks
+ if( type == CB_UPDATE )
+ {
+ assert( world->update_cb_count >= 0 );
+ world->update_cb_count++;
+ }
}
@@ -16,6 +24,13 @@ int Model::RemoveCallback( callback_type_t type,
{
set<cb_t>& callset = callbacks[type];
callset.erase( cb_t( callback, NULL) );
+
+
+ if( type == CB_UPDATE )
+ {
+ world->update_cb_count--;
+ assert( world->update_cb_count >= 0 );
+ }
// return the number of callbacks remaining for this address. Useful
// for detecting when there are none.
@@ -33,6 +48,7 @@ int Model::CallCallbacks( callback_type_t type )
FOR_EACH( it, callset )
{
const cb_t& cba = *it;
+ // callbacks return true if they should be cancelled
if( (cba.callback)( this, cba.arg ) )
doomed.push_back( cba );
}
View
2  libstage/model_ranger.cc
@@ -430,7 +430,7 @@ void ModelRanger::Vis::Visualize( Model* mod, Camera* cam )
glEnd();
char buf[8];
- snprintf( buf, 8, "%d", s );
+ snprintf( buf, 8, "%d", (int)s );
Gl::draw_string( rngr.pose.x, rngr.pose.y, rngr.pose.z, buf );
}
View
21 libstage/region.cc
@@ -4,6 +4,7 @@
Copyright Richard Vaughan 2008
*/
+#include <pthread.h>
#include "region.hh"
using namespace Stg;
@@ -53,11 +54,14 @@ void Region::RemoveBlock()
}
SuperRegion::SuperRegion( World* world, point_int_t origin )
- : regions(),
- origin(origin),
- world(world),
- count(0)
+ : count(0),
+ mutex(),
+ origin(origin),
+ regions(),
+ world(world)
{
+ pthread_mutex_init(&mutex,NULL);
+
for( int32_t c=0; c<SUPERREGIONSIZE;++c)
regions[c].superregion = this;
}
@@ -220,7 +224,7 @@ void SuperRegion::DrawVoxels() const
glEnable( GL_DEPTH_TEST );
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
- const Region* r = &regions[0];//GetRegion( 0, 0);
+ const Region* r = &regions[0];
for( int y=0; y<SUPERREGIONWIDTH; ++y )
for( int x=0; x<SUPERREGIONWIDTH; ++x )
@@ -272,13 +276,16 @@ void Cell::AddBlock( Block* b )
blocks.push_back( b );
b->rendered_cells.push_back(this);
region->AddBlock();
-
//mapped_blocks.insert(b);
}
void Cell::RemoveBlock( Block* b )
{
+ //static unsigned int callcount(0);
+ //printf( "RemoveBlock calls %d\n", ++callcount );
+
+ //region->superregion->Lock();
//if( mapped_blocks.find(b) == mapped_blocks.end() )
//printf( "REMOVE BLOCK %p that was never mapped\n", b );
@@ -319,4 +326,6 @@ void Cell::RemoveBlock( Block* b )
}
region->RemoveBlock();
+
+ //region->superregion->Unlock();
}
View
10 libstage/region.hh
@@ -103,13 +103,12 @@ namespace Stg
class SuperRegion
{
private:
- Region regions[SUPERREGIONSIZE];
-
+ unsigned long count; // number of blocks rendered into this superregion
+ pthread_mutex_t mutex;
point_int_t origin;
+ Region regions[SUPERREGIONSIZE];
World* world;
- unsigned long count; // number of blocks rendered into this superregion
-
public:
SuperRegion( World* world, point_int_t origin );
~SuperRegion();
@@ -121,6 +120,9 @@ namespace Stg
void DrawOccupancy() const;
void DrawVoxels() const;
+
+ inline void Lock(){ pthread_mutex_lock( &mutex); }
+ inline void Unlock(){ pthread_mutex_unlock( &mutex); }
inline void AddBlock();
inline void RemoveBlock();
View
52 libstage/stage.hh
@@ -1005,7 +1005,7 @@ namespace Stg
/** Enlarge the bounding volume to include this point */
- void Extend( point3_t pt );
+ inline void Extend( point3_t pt );
virtual void AddModel( Model* mod );
virtual void RemoveModel( Model* mod );
@@ -1044,6 +1044,9 @@ namespace Stg
/** Queue of pending simulation events for the main thread to handle. */
std::vector<std::priority_queue<Event> > event_queues;
+ /** Queue of pending simulation events for the main thread to handle. */
+ std::vector<std::queue<Model*> > pending_update_callbacks;
+
/** Create a new simulation event to be handled in the future.
@param queue_num Specify which queue the event should be on. The main
@@ -1055,17 +1058,27 @@ namespace Stg
@param mod The model that should have its Update() method
called at the specified time.
*/
- void Enqueue( unsigned int queue_num, usec_t delay, Model* mod );
-
+ void Enqueue( unsigned int queue_num, usec_t delay, Model* mod )
+ { event_queues[queue_num].push( Event( sim_time + delay, mod ) ); }
+
/** Set of models that require energy calculations at each World::Update(). */
std::set<Model*> active_energy;
/** Set of models that require their positions to be recalculated at each World::Update(). */
std::set<Model*> active_velocity;
-
+
+ std::vector<std::set<Model*> > active_velocity_threaded;
+
+ unsigned int thread_task;
+
/** The amount of simulated time to run for each call to Update() */
usec_t sim_interval;
-
+
+ // debug instrumentation - making sure the number of update callbacks
+ // in each thread is consistent with the number that have been
+ // registered globally
+ int update_cb_count;
+
/** consume events from the queue up to and including the current sim_time */
void ConsumeQueue( unsigned int queue_num );
@@ -1220,15 +1233,11 @@ namespace Stg
/** Set the extent in Z of the block */
void SetZ( double min, double max );
- inline void RemoveFromCellArray( CellPtrVec& blocks );
- //inline void GenerateCandidateCells();
-
void AppendTouchingModels( ModelPtrSet& touchers );
/** Returns the first model that shares a bitmap cell with this model */
Model* TestCollision();
- void SwitchToTestedCells();
void Load( Worldfile* wf, int entity );
Model* GetModel(){ return mod; };
const Color& GetColor();
@@ -1260,14 +1269,8 @@ namespace Stg
/** record the cells into which this block has been rendered to
UnMapping them very quickly. */
CellPtrVec rendered_cells;
-
- /** When moving a model, we test for collisions by generating, for
- each block, a list of the cells in which it would be rendered if the
- move were to be successful. If no collision occurs, the move is
- allowed - the rendered cells are cleared, the potential cells are
- written, and the pointers to the rendered and potential cells are
- switched for next time (avoiding a memory copy).*/
- //CellPtrVec * candidate_cells;
+
+ //std::set<SuperRegion*> rendered_superregions;
PointIntVec gpts;
@@ -1318,8 +1321,6 @@ namespace Stg
with a block in this group, or NULL, if none are detected. */
Model* TestCollision();
- void SwitchToTestedCells();
-
void Map();
void UnMap();
@@ -2102,23 +2103,14 @@ namespace Stg
const uint32_t sample_count,
const bool ztest = true );
-
- /** Causes this model and its children to recompute their global
- position instead of using a cached pose in
- Model::GetGlobalPose()..*/
- //void GPoseDirtyTree();
-
virtual void Startup();
virtual void Shutdown();
virtual void Update();
virtual void UpdatePose();
virtual void UpdateCharge();
- //Model* Move( const Pose& newpose );
-
- // EXP
- //void ConditionalMove_calc( const Pose& newpose );
- //void ConditionalMove_commit( void );
+ /** Calls CallCallback( CB_UPDATE ) */
+ void CallUpdateCallbacks( void );
meters_t ModelHeight() const;
View
213 libstage/world.cc
@@ -118,6 +118,9 @@ bool World::lty::operator()(const Model* a, const Model* b) const
return ay < by;
}
+const unsigned int THREAD_TASK_MOVE = 0;
+const unsigned int THREAD_TASK_UPDATE = 1;
+const unsigned int THREAD_TASK_COUNT = 2;
// static data members
unsigned int World::next_id = 0;
@@ -163,7 +166,13 @@ World::World( const std::string& name,
wf( NULL ),
paused( false ),
event_queues(1), // use 1 thread by default
- sim_interval( 1e5 ) // 100 msec has proved a good default
+ pending_update_callbacks(),
+ active_energy(),
+ active_velocity(),
+ active_velocity_threaded(),
+ thread_task(0),
+ sim_interval( 1e5 ), // 100 msec has proved a good default
+ update_cb_count(0)
{
if( ! Stg::InitDone() )
{
@@ -220,6 +229,8 @@ bool World::UpdateAll()
return quit;
}
+
+
void* World::update_thread_entry( std::pair<World*,int> *thread_info )
{
World* world = thread_info->first;
@@ -227,8 +238,8 @@ void* World::update_thread_entry( std::pair<World*,int> *thread_info )
//printf( "thread ID %d waiting for mutex\n", thread_instance );
- pthread_mutex_lock( &world->thread_mutex );
-
+ pthread_mutex_lock( &world->thread_mutex );
+
while( 1 )
{
//printf( "thread ID %d waiting for start\n", thread_instance );
@@ -237,13 +248,37 @@ void* World::update_thread_entry( std::pair<World*,int> *thread_info )
//puts( "worker waiting for start signal" );
pthread_cond_wait( &world->threads_start_cond, &world->thread_mutex );
+
+ unsigned int task = world->thread_task;
+
pthread_mutex_unlock( &world->thread_mutex );
- //puts( "worker thread awakes" );
+ //printf( "worker %u thread awakes for task %u\n", thread_instance, task );
+
+ switch(task)
+ {
+ case THREAD_TASK_UPDATE:
+ // consume events on the queue up to the current sim time
+ world->ConsumeQueue( thread_instance );
+ break;
+
+ case THREAD_TASK_MOVE:
+ {
+ std::set<Model*>& tomove =
+ world->active_velocity_threaded[thread_instance];
+
+ for( std::set<Model*>::iterator it = tomove.begin();
+ it != tomove.end();
+ ++it )
+ (*it)->UpdatePose();
+ }
+ break;
+
+ default:
+ PRINT_ERR1( "unrecognized worker thread task type %d\n", task );
+ break;
+ }
- // consume events on the queue up to the current sim time
- world->ConsumeQueue( thread_instance );
-
//printf( "thread %d done\n", thread_instance );
// done working, so increment the counter. If this was the last
@@ -401,6 +436,10 @@ void World::Load( const std::string& worldfile_path )
1e3 * wf->ReadFloat( entity, "interval_sim", this->sim_interval / 1e3 );
this->worker_threads = wf->ReadInt( entity, "threads", this->worker_threads );
+
+ active_velocity_threaded.resize( worker_threads + 1 );
+ pending_update_callbacks.resize( worker_threads + 1 );
+
if( worker_threads > 0 )
{
PRINT_WARN( "\nmulti-thread support is experimental and may not work properly, if at all." );
@@ -519,6 +558,7 @@ void World::AddUpdateCallback( world_callback_t cb,
{
// add the callback & argument to the list
cb_list.push_back( std::pair<world_callback_t,void*>(cb, user) );
+
}
int World::RemoveUpdateCallback( world_callback_t cb,
@@ -542,6 +582,31 @@ int World::RemoveUpdateCallback( world_callback_t cb,
void World::CallUpdateCallbacks()
{
+ // call model CB_UPDATE callbacks queued up by worker threads
+ size_t threads( pending_update_callbacks.size() );
+ int cbcount( 0 );
+
+ for( size_t t(0); t<threads; ++t )
+ {
+ std::queue<Model*>& q( pending_update_callbacks[t] );
+
+// printf( "pending callbacks for thread %u: %u\n",
+// (unsigned int)t,
+// (unsigned int)q.size() );
+
+ cbcount += q.size();
+
+ while( ! q.empty() )
+ {
+ q.front()->CallUpdateCallbacks();
+ q.pop();
+ }
+ }
+ // printf( "cb total %u (global %d)\n\n", (unsigned int)cbcount,update_cb_count );
+
+ assert( update_cb_count >= cbcount );
+
+ // world callbacks
FOR_EACH( it, cb_list )
{
if( ((*it).first )( this, (*it).second ) )
@@ -570,7 +635,19 @@ void World::ConsumeQueue( unsigned int queue_num )
//printf( "@ %llu next event <%s %llu %s>\n", sim_time, modelType.c_str(), ev.time, ev.mod->Token() );
if( ev.mod->subs > 0 ) // no subscriptions means the event is discarded
- ev.mod->Update(); // update the model
+ {
+ ev.mod->Update(); // update the model
+
+ // we updated the model, so it needs to have its update
+ // callback called in series back in the main thread. It's
+ // not safe to run user callbacks in a worker thread, as
+ // they may make OpenGL calls or unsafe Stage API calls,
+ // etc. We queue up the callback into a queue specific to
+ // this thread.
+ if( !ev.mod->callbacks[Model::CB_UPDATE].empty() )
+ pending_update_callbacks[queue_num].push(ev.mod);
+ }
+
}
while( !queue.empty() );
}
@@ -592,12 +669,11 @@ bool World::Update()
sim_time += sim_interval;
- FOR_EACH( it, active_velocity )
- (*it)->UpdatePose();
+// FOR_EACH( it, active_velocity )
+// (*it)->UpdatePose();
// rebuild the sets sorted by position on x,y axis
-#if( 1 )
models_with_fiducials_byx.clear();
models_with_fiducials_byy.clear();
@@ -606,41 +682,45 @@ bool World::Update()
models_with_fiducials_byx.insert( *it );
models_with_fiducials_byy.insert( *it );
}
-#endif
//printf( "x %lu y %lu\n", models_with_fiducials_byy.size(),
// models_with_fiducials_byx.size() );
// handle the zeroth queue synchronously in the main thread
ConsumeQueue( 0 );
-
- // handle all the remaining queues asynchronously in worker threads
- if( worker_threads > 0 )
- {
- pthread_mutex_lock( &thread_mutex );
- threads_working = worker_threads;
- // unblock the workers - they are waiting on this condition var
- //puts( "main thread signalling workers" );
- pthread_cond_broadcast( &threads_start_cond );
-
- // todo - take the 1th thread work here?
- // wait for all the last update job to complete - it will
- // signal the worker_threads_done condition var
- while( threads_working > 0 )
- {
- //puts( "main thread waiting for workers to finish" );
- pthread_cond_wait( &threads_done_cond, &thread_mutex );
- }
- pthread_mutex_unlock( &thread_mutex );
- //puts( "main thread awakes" );
-
- // TODO: allow threadsafe callbacks to be called in worker
- // threads
- }
-
+ for( thread_task=0; thread_task < THREAD_TASK_COUNT; ++thread_task )
+ {
+ // handle all the remaining queues asynchronously in worker threads
+ if( worker_threads > 0 )
+ {
+ pthread_mutex_lock( &thread_mutex );
+ threads_working = worker_threads;
+ // unblock the workers - they are waiting on this condition var
+ //puts( "main thread signalling workers" );
+ pthread_cond_broadcast( &threads_start_cond );
+
+ // todo - take the 1th thread work here?
+
+ // wait for all the last update job to complete - it will
+ // signal the worker_threads_done condition var
+ while( threads_working > 0 )
+ {
+ //puts( "main thread waiting for workers to finish" );
+ pthread_cond_wait( &threads_done_cond, &thread_mutex );
+ }
+ pthread_mutex_unlock( &thread_mutex );
+ //puts( "main thread awakes" );
+
+ // TODO: allow threadsafe callbacks to be called in worker
+ // threads
+ }
+ }
+
dirty = true; // need redraw
-
+
+ // this stuff must be done in series here
+
// world callbacks
CallUpdateCallbacks();
@@ -970,6 +1050,17 @@ void World::Reload( void )
ForEachDescendant( _reload_cb, NULL );
}
+void SwitchSuperRegionLock( SuperRegion* current, SuperRegion* next )
+{
+ if( current != next )
+ {
+ //printf( "SR %p next %p\n", sr, nextsr );
+ if(current) current->Unlock();
+
+ current = next;
+ current->Lock();
+ }
+}
void World::MapPoly( const PointIntVec& pts, Block* block )
{
@@ -977,12 +1068,12 @@ void World::MapPoly( const PointIntVec& pts, Block* block )
for( size_t i(0); i<pt_count; ++i )
{
- const point_int_t& start = pts[i];
- const point_int_t& end = pts[(i+1)%pt_count];
-
- // line rasterization adapted from Cohen's 3D version in
- // Graphics Gems II. Should be very fast.
- const int32_t dx( end.x - start.x );
+ const point_int_t& start(pts[i] );
+ const point_int_t& end(pts[(i+1)%pt_count]);
+
+ // line rasterization adapted from Cohen's 3D version in
+ // Graphics Gems II. Should be very fast.
+ const int32_t dx( end.x - start.x );
const int32_t dy( end.y - start.y );
const int32_t sx(sgn(dx));
const int32_t sy(sgn(dy));
@@ -996,13 +1087,18 @@ void World::MapPoly( const PointIntVec& pts, Block* block )
int32_t globx(start.x);
int32_t globy(start.y);
+ SuperRegion* sr(NULL);
+
while( n )
{
- Region* reg( GetSuperRegionCreate( point_int_t(GETSREG(globx),
- GETSREG(globy)))
- ->GetRegion( GETREG(globx),
- GETREG(globy)));
+ SuperRegion* nextsr =
+ GetSuperRegionCreate( point_int_t(GETSREG(globx),
+ GETSREG(globy)));
+
+ SwitchSuperRegionLock( sr, nextsr );
+ Region* reg(sr->GetRegion( GETREG(globx),
+ GETREG(globy)));
//printf( "REGION %p\n", reg );
// add all the required cells in this region before looking up
@@ -1041,6 +1137,7 @@ void World::MapPoly( const PointIntVec& pts, Block* block )
--n;
}
}
+ if(sr) sr->Unlock();
}
}
@@ -1056,19 +1153,15 @@ SuperRegion* World::AddSuperRegion( const point_int_t& sup )
// set the lower left corner of the new superregion
Extend( point3_t( (sup.x << SRBITS) / ppm,
- (sup.y << SRBITS) / ppm,
- 0 ));
+ (sup.y << SRBITS) / ppm,
+ 0 ));
// top right corner of the new superregion
Extend( point3_t( ((sup.x+1) << SRBITS) / ppm,
- ((sup.y+1) << SRBITS) / ppm,
- 0 ));
+ ((sup.y+1) << SRBITS) / ppm,
+ 0 ));
//printf( "top right (%.2f,%.2f,%.2f)\n", pt.x, pt.y, pt.z );
-// // map all jit models
-// FOR_EACH( it, jit_render )
-// (*it)->Map();
-
return sr;
}
@@ -1140,18 +1233,10 @@ void World:: RegisterOption( Option* opt )
void World::Log( Model* mod )
{
LogEntry( sim_time, mod);
-
printf( "log entry count %u\n", (unsigned int)LogEntry::Count() );
//LogEntry::Print();
}
-void World::Enqueue( unsigned int queue_num, usec_t delay, Model* mod )
-{
- //printf( "enqueue at %llu %p %s\n", sim_time + delay, mod, mod->Token() );
-
- event_queues[queue_num].push( Event( sim_time + delay, mod ) );
-}
-
bool World::Event::operator<( const Event& other ) const
{
return( time > other.time );
View
2  worlds/fasr2.world
@@ -16,7 +16,7 @@ quit_time 200
resolution 0.02
-threads 2
+threads 8
# configure the GUI window
window
Please sign in to comment.
Something went wrong with that request. Please try again.