Panda3D
pipeline.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file pipeline.cxx
10  * @author drose
11  * @date 2002-02-21
12  */
13 
14 #include "pipeline.h"
15 #include "pipelineCyclerTrueImpl.h"
16 #include "configVariableInt.h"
17 #include "config_pipeline.h"
18 
19 Pipeline *Pipeline::_render_pipeline = nullptr;
20 
21 /**
22  *
23  */
24 Pipeline::
25 Pipeline(const std::string &name, int num_stages) :
26  Namable(name),
27 #ifdef THREADED_PIPELINE
28  _num_stages(num_stages),
29  _cycle_lock("Pipeline cycle"),
30  _lock("Pipeline"),
31  _next_cycle_seq(1)
32 #else
33  _num_stages(1)
34 #endif
35 {
36 #ifdef THREADED_PIPELINE
37  // We maintain all of the cyclers in the world on one of two linked
38  // lists. Cyclers that are "clean", which is to say, they have the
39  // same value across all pipeline stages, are stored on the _clean
40  // list. Cyclers that are "dirty", which have different values
41  // across some pipeline stages, are stored instead on the _dirty
42  // list. Cyclers can move themselves from clean to dirty by calling
43  // add_dirty_cycler(), and cyclers get moved from dirty to clean
44  // during cycle().
45 
46  // To visit each cycler once requires traversing both lists.
47  _clean.make_head();
48  _dirty.make_head();
49 
50  // We also store the total count of all cyclers, clean and dirty, in
51  // _num_cyclers; and the count of only dirty cyclers in _num_dirty_cyclers.
52  _num_cyclers = 0;
53  _num_dirty_cyclers = 0;
54 
55  // This flag is true only during the call to cycle().
56  _cycling = false;
57 
58 #else
59  if (num_stages != 1) {
60  pipeline_cat.warning()
61  << "Requested " << num_stages
62  << " pipeline stages but multithreaded render pipelines not enabled in build.\n";
63  }
64 #endif // THREADED_PIPELINE
65 
66  nassertv(num_stages >= 1);
67 }
68 
69 /**
70  *
71  */
72 Pipeline::
73 ~Pipeline() {
74 #ifdef THREADED_PIPELINE
75  nassertv(_num_cyclers == 0);
76  nassertv(_num_dirty_cyclers == 0);
77  _clean.clear_head();
78  _dirty.clear_head();
79  nassertv(!_cycling);
80 #endif // THREADED_PIPELINE
81 }
82 
83 /**
84  * Flows all the pipeline data down to the next stage.
85  */
86 void Pipeline::
87 cycle() {
88 #ifdef THREADED_PIPELINE
89  if (pipeline_cat.is_debug()) {
90  pipeline_cat.debug()
91  << "Beginning the pipeline cycle\n";
92  }
93 
94  pvector< PT(CycleData) > saved_cdatas;
95  {
96  ReMutexHolder cycle_holder(_cycle_lock);
97  unsigned int prev_seq, next_seq;
98  PipelineCyclerLinks prev_dirty;
99  {
100  // We can't hold the lock protecting the linked lists during the cycling
101  // itself, since it could cause a deadlock.
102  MutexHolder holder(_lock);
103  if (_num_stages == 1) {
104  // No need to cycle if there's only one stage.
105  nassertv(_dirty._next == &_dirty);
106  return;
107  }
108 
109  nassertv(!_cycling);
110  _cycling = true;
111 
112  // Increment the cycle sequence number, which is used by this method to
113  // communicate with remove_cycler() about the status of dirty cyclers.
114  prev_seq = next_seq = _next_cycle_seq;
115  if (++next_seq == 0) {
116  // Skip 0, which is a reserved number used to indicate a clean cycler.
117  ++next_seq;
118  }
119  _next_cycle_seq = next_seq;
120 
121  // Move the dirty list to prev_dirty, for processing.
122  prev_dirty.make_head();
123  prev_dirty.take_list(_dirty);
124 
125  saved_cdatas.reserve(_num_dirty_cyclers);
126  _num_dirty_cyclers = 0;
127  }
128 
129  // This is duplicated for different number of stages, as an optimization.
130  switch (_num_stages) {
131  case 2:
132  while (prev_dirty._next != &prev_dirty) {
133  PipelineCyclerLinks *link = prev_dirty._next;
134  while (link != &prev_dirty) {
135  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)link;
136 
137  if (!cycler->_lock.try_lock()) {
138  // No big deal, just move on to the next one for now, and we'll
139  // come back around to it. It's important not to block here in
140  // order to prevent one cycler from deadlocking another.
141  if (link->_prev != &prev_dirty || link->_next != &prev_dirty) {
142  link = cycler->_next;
143  continue;
144  } else {
145  // Well, we are the last cycler left, so we might as well wait.
146  // This is necessary to trigger the deadlock detection code.
147  cycler->_lock.lock();
148  }
149  }
150 
151  MutexHolder holder(_lock);
152  cycler->remove_from_list();
153 
154  // We save the result of cycle(), so that we can defer the side-
155  // effects that might occur when CycleDatas destruct, at least until
156  // the end of this loop.
157  saved_cdatas.push_back(cycler->cycle_2());
158 
159  // cycle_2() won't leave a cycler dirty. Add it to the clean list.
160  nassertd(!cycler->_dirty) break;
161  cycler->insert_before(&_clean);
162 #ifdef DEBUG_THREADS
163  inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
164 #endif
165  cycler->_lock.unlock();
166  break;
167  }
168  }
169  break;
170 
171  case 3:
172  while (prev_dirty._next != &prev_dirty) {
173  PipelineCyclerLinks *link = prev_dirty._next;
174  while (link != &prev_dirty) {
175  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)link;
176 
177  if (!cycler->_lock.try_lock()) {
178  // No big deal, just move on to the next one for now, and we'll
179  // come back around to it. It's important not to block here in
180  // order to prevent one cycler from deadlocking another.
181  if (link->_prev != &prev_dirty || link->_next != &prev_dirty) {
182  link = cycler->_next;
183  continue;
184  } else {
185  // Well, we are the last cycler left, so we might as well wait.
186  // This is necessary to trigger the deadlock detection code.
187  cycler->_lock.lock();
188  }
189  }
190 
191  MutexHolder holder(_lock);
192  cycler->remove_from_list();
193 
194  saved_cdatas.push_back(cycler->cycle_3());
195 
196  if (cycler->_dirty) {
197  // The cycler is still dirty. Add it back to the dirty list.
198  nassertd(cycler->_dirty == prev_seq) break;
199  cycler->insert_before(&_dirty);
200  cycler->_dirty = next_seq;
201  ++_num_dirty_cyclers;
202  } else {
203  // The cycler is now clean. Add it back to the clean list.
204  cycler->insert_before(&_clean);
205 #ifdef DEBUG_THREADS
206  inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
207 #endif
208  }
209  cycler->_lock.unlock();
210  break;
211  }
212  }
213  break;
214 
215  default:
216  while (prev_dirty._next != &prev_dirty) {
217  PipelineCyclerLinks *link = prev_dirty._next;
218  while (link != &prev_dirty) {
219  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)link;
220 
221  if (!cycler->_lock.try_lock()) {
222  // No big deal, just move on to the next one for now, and we'll
223  // come back around to it. It's important not to block here in
224  // order to prevent one cycler from deadlocking another.
225  if (link->_prev != &prev_dirty || link->_next != &prev_dirty) {
226  link = cycler->_next;
227  continue;
228  } else {
229  // Well, we are the last cycler left, so we might as well wait.
230  // This is necessary to trigger the deadlock detection code.
231  cycler->_lock.lock();
232  }
233  }
234 
235  MutexHolder holder(_lock);
236  cycler->remove_from_list();
237 
238  saved_cdatas.push_back(cycler->cycle());
239 
240  if (cycler->_dirty) {
241  // The cycler is still dirty. Add it back to the dirty list.
242  nassertd(cycler->_dirty == prev_seq) break;
243  cycler->insert_before(&_dirty);
244  cycler->_dirty = next_seq;
245  ++_num_dirty_cyclers;
246  } else {
247  // The cycler is now clean. Add it back to the clean list.
248  cycler->insert_before(&_clean);
249 #ifdef DEBUG_THREADS
250  inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
251 #endif
252  }
253  cycler->_lock.unlock();
254  break;
255  }
256  }
257  break;
258  }
259 
260  // Now we're ready for the next frame.
261  prev_dirty.clear_head();
262  _cycling = false;
263  }
264 
265  // And now it's safe to let the CycleData pointers in saved_cdatas destruct,
266  // which may cause cascading deletes, and which will in turn cause
267  // PipelineCyclers to remove themselves from (or add themselves to) the
268  // _dirty list.
269  saved_cdatas.clear();
270 
271  if (pipeline_cat.is_debug()) {
272  pipeline_cat.debug()
273  << "Finished the pipeline cycle\n";
274  }
275 
276 #endif // THREADED_PIPELINE
277 }
278 
279 /**
280  * Specifies the number of stages required for the pipeline.
281  */
282 void Pipeline::
283 set_num_stages(int num_stages) {
284  nassertv(num_stages >= 1);
285 #ifdef THREADED_PIPELINE
286  // Make sure it's not currently cycling.
287  ReMutexHolder cycle_holder(_cycle_lock);
288  MutexHolder holder(_lock);
289  if (num_stages != _num_stages) {
290 
291  // We need to lock every PipelineCycler object attached to this pipeline
292  // before we can adjust the number of stages.
293  PipelineCyclerLinks *links;
294  for (links = _clean._next; links != &_clean; links = links->_next) {
295  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
296  cycler->_lock.lock();
297  }
298  for (links = _dirty._next; links != &_dirty; links = links->_next) {
299  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
300  cycler->_lock.lock();
301  }
302 
303  _num_stages = num_stages;
304 
305  for (links = _clean._next; links != &_clean; links = links->_next) {
306  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
307  cycler->set_num_stages(num_stages);
308  }
309  for (links = _dirty._next; links != &_dirty; links = links->_next) {
310  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
311  cycler->set_num_stages(num_stages);
312  }
313 
314  // Now release them all.
315  int count = 0;
316  for (links = _clean._next; links != &_clean; links = links->_next) {
317  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
318  cycler->_lock.unlock();
319  ++count;
320  }
321  for (links = _dirty._next; links != &_dirty; links = links->_next) {
322  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
323  cycler->_lock.unlock();
324  ++count;
325  }
326  nassertv(count == _num_cyclers);
327  }
328 
329 #else // THREADED_PIPELINE
330  if (num_stages != 1) {
331  pipeline_cat.warning()
332  << "Requested " << num_stages
333  << " pipeline stages but multithreaded render pipelines not enabled in build.\n";
334  }
335  _num_stages = 1;
336 #endif // THREADED_PIPELINE
337 }
338 
339 #ifdef THREADED_PIPELINE
340 /**
341  * Adds the indicated cycler to the list of cyclers associated with the
342  * pipeline. This method only exists when true pipelining is configured on.
343  */
344 void Pipeline::
345 add_cycler(PipelineCyclerTrueImpl *cycler) {
346  // It's safe to add it to the list while cycling, since the _clean list is
347  // not touched during the cycle loop.
348  MutexHolder holder(_lock);
349  nassertv(!cycler->_dirty);
350 
351  cycler->insert_before(&_clean);
352  ++_num_cyclers;
353 
354 #ifdef DEBUG_THREADS
355  inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), 1);
356 #endif
357 }
358 #endif // THREADED_PIPELINE
359 
360 #ifdef THREADED_PIPELINE
361 /**
362  * Marks the indicated cycler as "dirty", meaning it will need to be cycled
363  * next frame. This both adds it to the "dirty" set and also sets the "dirty"
364  * flag within the cycler. This method only exists when true pipelining is
365  * configured on.
366  */
367 void Pipeline::
368 add_dirty_cycler(PipelineCyclerTrueImpl *cycler) {
369  nassertv(cycler->_lock.debug_is_locked());
370 
371  // It's safe to add it to the list while cycling, since it's not currently
372  // on the dirty list.
373  MutexHolder holder(_lock);
374  nassertv(!cycler->_dirty);
375  nassertv(_num_stages != 1);
376 
377  // Remove it from the "clean" list and add it to the "dirty" list.
378  cycler->remove_from_list();
379  cycler->insert_before(&_dirty);
380  cycler->_dirty = _next_cycle_seq;
381  ++_num_dirty_cyclers;
382 
383 #ifdef DEBUG_THREADS
384  inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), 1);
385 #endif
386 }
387 #endif // THREADED_PIPELINE
388 
389 #ifdef THREADED_PIPELINE
390 /**
391  * Removes the indicated cycler from the list of cyclers associated with the
392  * pipeline. This method only exists when true pipelining is configured on.
393  */
394 void Pipeline::
395 remove_cycler(PipelineCyclerTrueImpl *cycler) {
396  nassertv(cycler->_lock.debug_is_locked());
397 
398  MutexHolder holder(_lock);
399 
400  // If it's dirty, it may currently be processed by cycle(), so we need to be
401  // careful not to cause a race condition. It's safe for us to remove it
402  // during cycle only if it's 0 (clean) or _next_cycle_seq (scheduled for the
403  // next cycle, so not owned by the current one).
404  while (cycler->_dirty != 0 && cycler->_dirty != _next_cycle_seq) {
405  if (_cycle_lock.try_lock()) {
406  // OK, great, we got the lock, so it finished cycling already.
407  nassertv(!_cycling);
408 
409  --_num_cyclers;
410  cycler->remove_from_list();
411 
412  cycler->_dirty = false;
413  --_num_dirty_cyclers;
414 
415  #ifdef DEBUG_THREADS
416  inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), -1);
417  inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
418  #endif
419 
420  _cycle_lock.unlock();
421  return;
422  } else {
423  // It's possibly currently being cycled. We will wait for the cycler
424  // to be done with it, so that we can safely remove it.
425  _lock.unlock();
426  cycler->_lock.unlock();
428  cycler->_lock.lock();
429  _lock.lock();
430  }
431  }
432 
433  // It's not being owned by a cycle operation, so it's fair game.
434  --_num_cyclers;
435  cycler->remove_from_list();
436 
437 #ifdef DEBUG_THREADS
438  inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), -1);
439 #endif
440 
441  if (cycler->_dirty) {
442  cycler->_dirty = 0;
443  --_num_dirty_cyclers;
444 #ifdef DEBUG_THREADS
445  inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
446 #endif
447  }
448 }
449 #endif // THREADED_PIPELINE
450 
451 #if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS)
452 /**
453  * Walks through the list of all the different PipelineCycler types in the
454  * universe. For each one, calls the indicated callback function with the
455  * TypeHandle of the respective type (actually, the result of
456  * cycler::get_parent_type()) and the count of pipeline cyclers of that type.
457  * Mainly used for PStats reporting.
458  */
459 void Pipeline::
460 iterate_all_cycler_types(CallbackFunc *func, void *data) const {
461  // Make sure it's not currently cycling.
462  ReMutexHolder cycle_holder(_cycle_lock);
463  MutexHolder holder(_lock);
464  TypeCount::const_iterator ci;
465  for (ci = _all_cycler_types.begin(); ci != _all_cycler_types.end(); ++ci) {
466  func((*ci).first, (*ci).second, data);
467  }
468 }
469 #endif // THREADED_PIPELINE && DEBUG_THREADS
470 
471 #if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS)
472 /**
473  * Walks through the list of all the different PipelineCycler types, for only
474  * the dirty PipelineCyclers. See also iterate_all_cycler_types().
475  */
476 void Pipeline::
477 iterate_dirty_cycler_types(CallbackFunc *func, void *data) const {
478  // Make sure it's not currently cycling.
479  ReMutexHolder cycle_holder(_cycle_lock);
480  MutexHolder holder(_lock);
481  TypeCount::const_iterator ci;
482  for (ci = _dirty_cycler_types.begin(); ci != _dirty_cycler_types.end(); ++ci) {
483  func((*ci).first, (*ci).second, data);
484  }
485 }
486 #endif // THREADED_PIPELINE && DEBUG_THREADS
487 
488 /**
489  *
490  */
491 void Pipeline::
492 make_render_pipeline() {
493  ConfigVariableInt pipeline_stages
494  ("pipeline-stages", 1,
495  PRC_DESC("The initial number of stages in the render pipeline. This is "
496  "only meaningful if threaded pipelining is compiled into "
497  "Panda. In most cases, you should not set this at all anyway, "
498  "since the pipeline can automatically grow stages as needed, "
499  "but it will not remove stages automatically, and having more "
500  "pipeline stages than your application requires will incur "
501  "additional runtime overhead."));
502 
503  nassertv(_render_pipeline == nullptr);
504  _render_pipeline = new Pipeline("render", pipeline_stages);
505 }
506 
507 #if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS)
508 /**
509  * Increments (or decrements, according to added) the value for TypeHandle in
510  * the indicated TypeCount map. This is used in DEBUG_THREADS mode to track
511  * the types of PipelineCyclers that are coming and going, mainly for PStats
512  * reporting.
513  *
514  * It is assumed the lock is held during this call.
515  */
516 void Pipeline::
517 inc_cycler_type(TypeCount &count, TypeHandle type, int addend) {
518  TypeCount::iterator ci = count.find(type);
519  if (ci == count.end()) {
520  ci = count.insert(TypeCount::value_type(type, 0)).first;
521  }
522  (*ci).second += addend;
523  nassertv((*ci).second >= 0);
524 }
525 #endif // THREADED_PIPELINE && DEBUG_THREADS
void set_num_stages(int num_stages)
Specifies the number of stages required for the pipeline.
Definition: pipeline.cxx:283
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:47
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:25
static void force_yield()
Suspends the current thread for the rest of the current epoch.
Definition: thread.I:201
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
void cycle()
Flows all the pipeline data down to the next stage.
Definition: pipeline.cxx:87
A base class for all things which can have a name.
Definition: namable.h:26
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Similar to MutexHolder, but for a reentrant mutex.
Definition: reMutexHolder.h:25
This class manages a staged pipeline of data, for instance the render pipeline, so that each stage of...
Definition: pipeline.h:38
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a convenience class to specialize ConfigVariable as an integer type.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81