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  */
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  */
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
This is a convenience class to specialize ConfigVariable as an integer type.
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:25
A base class for all things which can have a name.
Definition: namable.h:26
This class manages a staged pipeline of data, for instance the render pipeline, so that each stage of...
Definition: pipeline.h:38
void cycle()
Flows all the pipeline data down to the next stage.
Definition: pipeline.cxx:87
void set_num_stages(int num_stages)
Specifies the number of stages required for the pipeline.
Definition: pipeline.cxx:283
Similar to MutexHolder, but for a reentrant mutex.
Definition: reMutexHolder.h:25
static void force_yield()
Suspends the current thread for the rest of the current epoch.
Definition: thread.I:201
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.