Panda3D
 All Classes Functions Variables Enumerations
pipeline.cxx
00001 // Filename: pipeline.cxx
00002 // Created by:  drose (21Feb02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "pipeline.h"
00016 #include "pipelineCyclerTrueImpl.h"
00017 #include "reMutexHolder.h"
00018 #include "configVariableInt.h"
00019 #include "config_pipeline.h"
00020 
00021 Pipeline *Pipeline::_render_pipeline = (Pipeline *)NULL;
00022 
00023 ////////////////////////////////////////////////////////////////////
00024 //     Function: Pipeline::Constructor
00025 //       Access: Public
00026 //  Description: 
00027 ////////////////////////////////////////////////////////////////////
00028 Pipeline::
00029 Pipeline(const string &name, int num_stages) :
00030   Namable(name)
00031 #ifdef THREADED_PIPELINE
00032   , _lock("Pipeline")
00033 #endif
00034 {
00035 #ifdef THREADED_PIPELINE
00036 
00037   // We maintain all of the cyclers in the world on one of two linked
00038   // lists.  Cyclers that are "clean", which is to say, they have the
00039   // same value across all pipeline stages, are stored on the _clean
00040   // list.  Cyclers that are "dirty", which have different values
00041   // across some pipeline stages, are stored instead on the _dirty
00042   // list.  Cyclers can move themselves from clean to dirty by calling
00043   // add_dirty_cycler(), and cyclers get moved from dirty to clean
00044   // during cycle().
00045 
00046   // To visit each cycler once requires traversing both lists.
00047   _clean.make_head();
00048   _dirty.make_head();
00049 
00050   // We also store the total count of all cyclers, clean and dirty, in
00051   // _num_cyclers; and the count of only dirty cyclers in
00052   // _num_dirty_cyclers.
00053   _num_cyclers = 0;
00054   _num_dirty_cyclers = 0;
00055 
00056   // This flag is true only during the call to cycle().
00057   _cycling = false;
00058 
00059 #endif  // THREADED_PIPELINE
00060 
00061   set_num_stages(num_stages);
00062 }
00063 
00064 ////////////////////////////////////////////////////////////////////
00065 //     Function: Pipeline::Destructor
00066 //       Access: Public
00067 //  Description: 
00068 ////////////////////////////////////////////////////////////////////
00069 Pipeline::
00070 ~Pipeline() {
00071 #ifdef THREADED_PIPELINE
00072   nassertv(_num_cyclers == 0);
00073   nassertv(_num_dirty_cyclers == 0);
00074   _clean.clear_head();
00075   _dirty.clear_head();
00076   nassertv(!_cycling);
00077 #endif  // THREADED_PIPELINE
00078 }
00079 
00080 ////////////////////////////////////////////////////////////////////
00081 //     Function: Pipeline::cycle
00082 //       Access: Public
00083 //  Description: Flows all the pipeline data down to the next stage.
00084 ////////////////////////////////////////////////////////////////////
00085 void Pipeline::
00086 cycle() {
00087 #ifdef THREADED_PIPELINE
00088   if (pipeline_cat.is_debug()) {
00089     pipeline_cat.debug()
00090       << "Beginning the pipeline cycle\n";
00091   }
00092 
00093   pvector< PT(CycleData) > saved_cdatas;
00094   saved_cdatas.reserve(_num_dirty_cyclers);
00095   {
00096     ReMutexHolder holder(_lock);
00097     if (_num_stages == 1) {
00098       // No need to cycle if there's only one stage.
00099       nassertv(_dirty._next == &_dirty);
00100       return;
00101     }
00102     
00103     nassertv(!_cycling);
00104     _cycling = true;
00105     
00106     // Move the dirty list to prev_dirty, for processing.
00107     PipelineCyclerLinks prev_dirty;
00108     prev_dirty.make_head();
00109     prev_dirty.take_list(_dirty);
00110     _num_dirty_cyclers = 0;
00111 
00112     switch (_num_stages) {
00113     case 2:
00114       while (prev_dirty._next != &prev_dirty) {
00115         PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next;
00116         cycler->remove_from_list();
00117         ReMutexHolder holder2(cycler->_lock);
00118         
00119         // We save the result of cycle(), so that we can defer the
00120         // side-effects that might occur when CycleDatas destruct, at
00121         // least until the end of this loop.
00122         saved_cdatas.push_back(cycler->cycle_2());
00123         
00124         if (cycler->_dirty) {
00125           // The cycler is still dirty after cycling.  Keep it on the
00126           // dirty list for next time.
00127           cycler->insert_before(&_dirty);
00128           ++_num_dirty_cyclers;
00129         } else {
00130           // The cycler is now clean.  Add it back to the clean list.
00131           cycler->insert_before(&_clean);
00132 #ifdef DEBUG_THREADS
00133           inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
00134 #endif
00135         }
00136       }
00137       break;
00138 
00139     case 3:
00140       while (prev_dirty._next != &prev_dirty) {
00141         PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next;
00142         cycler->remove_from_list();
00143         ReMutexHolder holder2(cycler->_lock);
00144         
00145         saved_cdatas.push_back(cycler->cycle_3());
00146         
00147         if (cycler->_dirty) {
00148           cycler->insert_before(&_dirty);
00149           ++_num_dirty_cyclers;
00150         } else {
00151           cycler->insert_before(&_clean);
00152 #ifdef DEBUG_THREADS
00153           inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
00154 #endif
00155         }
00156       }
00157       break;
00158 
00159     default:
00160       while (prev_dirty._next != &prev_dirty) {
00161         PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next;
00162         cycler->remove_from_list();
00163         ReMutexHolder holder2(cycler->_lock);
00164         
00165         saved_cdatas.push_back(cycler->cycle());
00166         
00167         if (cycler->_dirty) {
00168           cycler->insert_before(&_dirty);
00169           ++_num_dirty_cyclers;
00170         } else {
00171           cycler->insert_before(&_clean);
00172 #ifdef DEBUG_THREADS
00173           inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
00174 #endif
00175         }
00176       }
00177       break;
00178     }
00179       
00180     // Now we're ready for the next frame.
00181     prev_dirty.clear_head();
00182     _cycling = false;
00183   }
00184 
00185   // And now it's safe to let the CycleData pointers in saved_cdatas
00186   // destruct, which may cause cascading deletes, and which will in
00187   // turn cause PipelineCyclers to remove themselves from (or add
00188   // themselves to) the _dirty list.
00189   saved_cdatas.clear();
00190 
00191   if (pipeline_cat.is_debug()) {
00192     pipeline_cat.debug()
00193       << "Finished the pipeline cycle\n";
00194   }
00195 
00196 #endif  // THREADED_PIPELINE
00197 }
00198 
00199 ////////////////////////////////////////////////////////////////////
00200 //     Function: Pipeline::set_num_stages
00201 //       Access: Public
00202 //  Description: Specifies the number of stages required for the
00203 //               pipeline.
00204 ////////////////////////////////////////////////////////////////////
00205 void Pipeline::
00206 set_num_stages(int num_stages) {
00207   nassertv(num_stages >= 1);
00208 #ifdef THREADED_PIPELINE
00209   ReMutexHolder holder(_lock);
00210   if (num_stages != _num_stages) {
00211 
00212     // We need to lock every PipelineCycler object attached to this
00213     // pipeline before we can adjust the number of stages.
00214     PipelineCyclerLinks *links;
00215     for (links = _clean._next; links != &_clean; links = links->_next) {
00216       PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
00217       cycler->_lock.acquire();
00218     }
00219     for (links = _dirty._next; links != &_dirty; links = links->_next) {
00220       PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
00221       cycler->_lock.acquire();
00222     }
00223 
00224     _num_stages = num_stages;
00225 
00226     for (links = _clean._next; links != &_clean; links = links->_next) {
00227       PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
00228       cycler->set_num_stages(num_stages);
00229     }
00230     for (links = _dirty._next; links != &_dirty; links = links->_next) {
00231       PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
00232       cycler->set_num_stages(num_stages);
00233     }
00234 
00235     // Now release them all.
00236     int count = 0;
00237     for (links = _clean._next; links != &_clean; links = links->_next) {
00238       PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
00239       cycler->_lock.release();
00240       ++count;
00241     }
00242     for (links = _dirty._next; links != &_dirty; links = links->_next) {
00243       PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
00244       cycler->_lock.release();
00245       ++count;
00246     }
00247     nassertv(count == _num_cyclers);
00248   }
00249 
00250 #else  // THREADED_PIPELINE
00251   if (num_stages != 1) {
00252     pipeline_cat.warning()
00253       << "Requested " << num_stages
00254       << " pipeline stages but multithreaded render pipelines not enabled in build.\n";
00255   }
00256   _num_stages = 1;
00257 #endif  // THREADED_PIPELINE
00258 }
00259 
00260 #ifdef THREADED_PIPELINE
00261 ////////////////////////////////////////////////////////////////////
00262 //     Function: Pipeline::add_cycler
00263 //       Access: Public
00264 //  Description: Adds the indicated cycler to the list of cyclers
00265 //               associated with the pipeline.  This method only
00266 //               exists when true pipelining is configured on.
00267 ////////////////////////////////////////////////////////////////////
00268 void Pipeline::
00269 add_cycler(PipelineCyclerTrueImpl *cycler) {
00270   ReMutexHolder holder(_lock);
00271   nassertv(!cycler->_dirty);
00272   nassertv(!_cycling);
00273 
00274   cycler->insert_before(&_clean);
00275   ++_num_cyclers;
00276   
00277 #ifdef DEBUG_THREADS
00278   inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), 1);
00279 #endif
00280 }
00281 #endif  // THREADED_PIPELINE
00282 
00283 #ifdef THREADED_PIPELINE
00284 ////////////////////////////////////////////////////////////////////
00285 //     Function: Pipeline::add_dirty_cycler
00286 //       Access: Public
00287 //  Description: Marks the indicated cycler as "dirty", meaning it
00288 //               will need to be cycled next frame.  This both adds it
00289 //               to the "dirty" set and also sets the "dirty" flag
00290 //               within the cycler.  This method only exists when true
00291 //               pipelining is configured on.
00292 ////////////////////////////////////////////////////////////////////
00293 void Pipeline::
00294 add_dirty_cycler(PipelineCyclerTrueImpl *cycler) {
00295   nassertv(cycler->_lock.debug_is_locked());
00296 
00297   ReMutexHolder holder(_lock);
00298   nassertv(_num_stages != 1);
00299   nassertv(!_cycling);
00300   nassertv(!cycler->_dirty);
00301 
00302   // Remove it from the "clean" list and add it to the "dirty" list.
00303   cycler->remove_from_list();
00304   cycler->insert_before(&_dirty);
00305   cycler->_dirty = true;
00306   ++_num_dirty_cyclers;
00307 
00308 #ifdef DEBUG_THREADS
00309   inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), 1);
00310 #endif
00311 }
00312 #endif  // THREADED_PIPELINE
00313 
00314 #ifdef THREADED_PIPELINE
00315 ////////////////////////////////////////////////////////////////////
00316 //     Function: Pipeline::remove_cycler
00317 //       Access: Public
00318 //  Description: Removes the indicated cycler from the list of cyclers
00319 //               associated with the pipeline.  This method only
00320 //               exists when true pipelining is configured on.
00321 ////////////////////////////////////////////////////////////////////
00322 void Pipeline::
00323 remove_cycler(PipelineCyclerTrueImpl *cycler) {
00324   nassertv(cycler->_lock.debug_is_locked());
00325 
00326   ReMutexHolder holder(_lock);
00327   nassertv(!_cycling);
00328 
00329   --_num_cyclers;
00330   cycler->remove_from_list();
00331 
00332 #ifdef DEBUG_THREADS
00333   inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), -1);
00334 #endif
00335 
00336   if (cycler->_dirty) {
00337     cycler->_dirty = false;
00338     --_num_dirty_cyclers;
00339 #ifdef DEBUG_THREADS
00340     inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
00341 #endif
00342   }
00343 }
00344 #endif  // THREADED_PIPELINE
00345 
00346 #if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS) 
00347 ////////////////////////////////////////////////////////////////////
00348 //     Function: Pipeline::iterate_all_cycler_types
00349 //       Access: Public
00350 //  Description: Walks through the list of all the different
00351 //               PipelineCycler types in the universe.  For each one,
00352 //               calls the indicated callback function with the
00353 //               TypeHandle of the respective type (actually, the
00354 //               result of cycler::get_parent_type()) and the count of
00355 //               pipeline cyclers of that type.  Mainly used for
00356 //               PStats reporting.
00357 ////////////////////////////////////////////////////////////////////
00358 void Pipeline::
00359 iterate_all_cycler_types(CallbackFunc *func, void *data) const {
00360   ReMutexHolder holder(_lock);
00361   TypeCount::const_iterator ci;
00362   for (ci = _all_cycler_types.begin(); ci != _all_cycler_types.end(); ++ci) {
00363     func((*ci).first, (*ci).second, data);
00364   }
00365 }
00366 #endif  // THREADED_PIPELINE && DEBUG_THREADS
00367 
00368 #if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS) 
00369 ////////////////////////////////////////////////////////////////////
00370 //     Function: Pipeline::iterate_dirty_cycler_types
00371 //       Access: Public
00372 //  Description: Walks through the list of all the different
00373 //               PipelineCycler types, for only the dirty
00374 //               PipelineCyclers.  See also
00375 //               iterate_all_cycler_types().
00376 ////////////////////////////////////////////////////////////////////
00377 void Pipeline::
00378 iterate_dirty_cycler_types(CallbackFunc *func, void *data) const {
00379   ReMutexHolder holder(_lock);
00380   TypeCount::const_iterator ci;
00381   for (ci = _dirty_cycler_types.begin(); ci != _dirty_cycler_types.end(); ++ci) {
00382     func((*ci).first, (*ci).second, data);
00383   }
00384 }
00385 #endif  // THREADED_PIPELINE && DEBUG_THREADS
00386 
00387 ////////////////////////////////////////////////////////////////////
00388 //     Function: Pipeline::make_render_pipeline
00389 //       Access: Private, Static
00390 //  Description: 
00391 ////////////////////////////////////////////////////////////////////
00392 void Pipeline::
00393 make_render_pipeline() {
00394   ConfigVariableInt pipeline_stages
00395     ("pipeline-stages", 1,
00396      PRC_DESC("The initial number of stages in the render pipeline.  This is "
00397               "only meaningful if threaded pipelining is compiled into "
00398               "Panda.  In most cases, you should not set this at all anyway, "
00399               "since the pipeline can automatically grow stages as needed, "
00400               "but it will not remove stages automatically, and having more "
00401               "pipeline stages than your application requires will incur "
00402               "additional runtime overhead."));
00403 
00404   nassertv(_render_pipeline == (Pipeline *)NULL);
00405   _render_pipeline = new Pipeline("render", pipeline_stages);
00406 }
00407 
00408 #if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS) 
00409 ////////////////////////////////////////////////////////////////////
00410 //     Function: Pipeline::inc_cycler_type
00411 //       Access: Private, Static
00412 //  Description: Increments (or decrements, according to added) the
00413 //               value for TypeHandle in the indicated TypeCount map.
00414 //               This is used in DEBUG_THREADS mode to track the types
00415 //               of PipelineCyclers that are coming and going, mainly
00416 //               for PStats reporting.
00417 //
00418 //               It is assumed the lock is held during this call.
00419 ////////////////////////////////////////////////////////////////////
00420 void Pipeline::
00421 inc_cycler_type(TypeCount &count, TypeHandle type, int addend) {
00422   TypeCount::iterator ci = count.find(type);
00423   if (ci == count.end()) {
00424     ci = count.insert(TypeCount::value_type(type, 0)).first;
00425   }
00426   (*ci).second += addend;
00427   nassertv((*ci).second >= 0);
00428 }
00429 #endif  // THREADED_PIPELINE && DEBUG_THREADS
 All Classes Functions Variables Enumerations