Panda3D
pipeline.cxx
1 // Filename: pipeline.cxx
2 // Created by: drose (21Feb02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pipeline.h"
16 #include "pipelineCyclerTrueImpl.h"
17 #include "reMutexHolder.h"
18 #include "configVariableInt.h"
19 #include "config_pipeline.h"
20 
21 Pipeline *Pipeline::_render_pipeline = (Pipeline *)NULL;
22 
23 ////////////////////////////////////////////////////////////////////
24 // Function: Pipeline::Constructor
25 // Access: Public
26 // Description:
27 ////////////////////////////////////////////////////////////////////
28 Pipeline::
29 Pipeline(const string &name, int num_stages) :
30  Namable(name),
31 #ifdef THREADED_PIPELINE
32  _num_stages(num_stages),
33  _lock("Pipeline")
34 #else
35  _num_stages(1)
36 #endif
37 {
38 #ifdef THREADED_PIPELINE
39  // We maintain all of the cyclers in the world on one of two linked
40  // lists. Cyclers that are "clean", which is to say, they have the
41  // same value across all pipeline stages, are stored on the _clean
42  // list. Cyclers that are "dirty", which have different values
43  // across some pipeline stages, are stored instead on the _dirty
44  // list. Cyclers can move themselves from clean to dirty by calling
45  // add_dirty_cycler(), and cyclers get moved from dirty to clean
46  // during cycle().
47 
48  // To visit each cycler once requires traversing both lists.
49  _clean.make_head();
50  _dirty.make_head();
51 
52  // We also store the total count of all cyclers, clean and dirty, in
53  // _num_cyclers; and the count of only dirty cyclers in
54  // _num_dirty_cyclers.
55  _num_cyclers = 0;
56  _num_dirty_cyclers = 0;
57 
58  // This flag is true only during the call to cycle().
59  _cycling = false;
60 
61 #else
62  if (num_stages != 1) {
63  pipeline_cat.warning()
64  << "Requested " << num_stages
65  << " pipeline stages but multithreaded render pipelines not enabled in build.\n";
66  }
67 #endif // THREADED_PIPELINE
68 
69  nassertv(num_stages >= 1);
70 }
71 
72 ////////////////////////////////////////////////////////////////////
73 // Function: Pipeline::Destructor
74 // Access: Public
75 // Description:
76 ////////////////////////////////////////////////////////////////////
77 Pipeline::
78 ~Pipeline() {
79 #ifdef THREADED_PIPELINE
80  nassertv(_num_cyclers == 0);
81  nassertv(_num_dirty_cyclers == 0);
82  _clean.clear_head();
83  _dirty.clear_head();
84  nassertv(!_cycling);
85 #endif // THREADED_PIPELINE
86 }
87 
88 ////////////////////////////////////////////////////////////////////
89 // Function: Pipeline::cycle
90 // Access: Public
91 // Description: Flows all the pipeline data down to the next stage.
92 ////////////////////////////////////////////////////////////////////
93 void Pipeline::
94 cycle() {
95 #ifdef THREADED_PIPELINE
96  if (pipeline_cat.is_debug()) {
97  pipeline_cat.debug()
98  << "Beginning the pipeline cycle\n";
99  }
100 
101  pvector< PT(CycleData) > saved_cdatas;
102  saved_cdatas.reserve(_num_dirty_cyclers);
103  {
104  ReMutexHolder holder(_lock);
105  if (_num_stages == 1) {
106  // No need to cycle if there's only one stage.
107  nassertv(_dirty._next == &_dirty);
108  return;
109  }
110 
111  nassertv(!_cycling);
112  _cycling = true;
113 
114  // Move the dirty list to prev_dirty, for processing.
115  PipelineCyclerLinks prev_dirty;
116  prev_dirty.make_head();
117  prev_dirty.take_list(_dirty);
118  _num_dirty_cyclers = 0;
119 
120  switch (_num_stages) {
121  case 2:
122  while (prev_dirty._next != &prev_dirty) {
123  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next;
124  cycler->remove_from_list();
125  ReMutexHolder holder2(cycler->_lock);
126 
127  // We save the result of cycle(), so that we can defer the
128  // side-effects that might occur when CycleDatas destruct, at
129  // least until the end of this loop.
130  saved_cdatas.push_back(cycler->cycle_2());
131 
132  if (cycler->_dirty) {
133  // The cycler is still dirty after cycling. Keep it on the
134  // dirty list for next time.
135  cycler->insert_before(&_dirty);
136  ++_num_dirty_cyclers;
137  } else {
138  // The cycler is now clean. Add it back to the clean list.
139  cycler->insert_before(&_clean);
140 #ifdef DEBUG_THREADS
141  inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
142 #endif
143  }
144  }
145  break;
146 
147  case 3:
148  while (prev_dirty._next != &prev_dirty) {
149  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next;
150  cycler->remove_from_list();
151  ReMutexHolder holder2(cycler->_lock);
152 
153  saved_cdatas.push_back(cycler->cycle_3());
154 
155  if (cycler->_dirty) {
156  cycler->insert_before(&_dirty);
157  ++_num_dirty_cyclers;
158  } else {
159  cycler->insert_before(&_clean);
160 #ifdef DEBUG_THREADS
161  inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
162 #endif
163  }
164  }
165  break;
166 
167  default:
168  while (prev_dirty._next != &prev_dirty) {
169  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next;
170  cycler->remove_from_list();
171  ReMutexHolder holder2(cycler->_lock);
172 
173  saved_cdatas.push_back(cycler->cycle());
174 
175  if (cycler->_dirty) {
176  cycler->insert_before(&_dirty);
177  ++_num_dirty_cyclers;
178  } else {
179  cycler->insert_before(&_clean);
180 #ifdef DEBUG_THREADS
181  inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
182 #endif
183  }
184  }
185  break;
186  }
187 
188  // Now we're ready for the next frame.
189  prev_dirty.clear_head();
190  _cycling = false;
191  }
192 
193  // And now it's safe to let the CycleData pointers in saved_cdatas
194  // destruct, which may cause cascading deletes, and which will in
195  // turn cause PipelineCyclers to remove themselves from (or add
196  // themselves to) the _dirty list.
197  saved_cdatas.clear();
198 
199  if (pipeline_cat.is_debug()) {
200  pipeline_cat.debug()
201  << "Finished the pipeline cycle\n";
202  }
203 
204 #endif // THREADED_PIPELINE
205 }
206 
207 ////////////////////////////////////////////////////////////////////
208 // Function: Pipeline::set_num_stages
209 // Access: Public
210 // Description: Specifies the number of stages required for the
211 // pipeline.
212 ////////////////////////////////////////////////////////////////////
213 void Pipeline::
214 set_num_stages(int num_stages) {
215  nassertv(num_stages >= 1);
216 #ifdef THREADED_PIPELINE
217  ReMutexHolder holder(_lock);
218  if (num_stages != _num_stages) {
219 
220  // We need to lock every PipelineCycler object attached to this
221  // pipeline before we can adjust the number of stages.
222  PipelineCyclerLinks *links;
223  for (links = _clean._next; links != &_clean; links = links->_next) {
224  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
225  cycler->_lock.acquire();
226  }
227  for (links = _dirty._next; links != &_dirty; links = links->_next) {
228  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
229  cycler->_lock.acquire();
230  }
231 
232  _num_stages = num_stages;
233 
234  for (links = _clean._next; links != &_clean; links = links->_next) {
235  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
236  cycler->set_num_stages(num_stages);
237  }
238  for (links = _dirty._next; links != &_dirty; links = links->_next) {
239  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
240  cycler->set_num_stages(num_stages);
241  }
242 
243  // Now release them all.
244  int count = 0;
245  for (links = _clean._next; links != &_clean; links = links->_next) {
246  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
247  cycler->_lock.release();
248  ++count;
249  }
250  for (links = _dirty._next; links != &_dirty; links = links->_next) {
251  PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
252  cycler->_lock.release();
253  ++count;
254  }
255  nassertv(count == _num_cyclers);
256  }
257 
258 #else // THREADED_PIPELINE
259  if (num_stages != 1) {
260  pipeline_cat.warning()
261  << "Requested " << num_stages
262  << " pipeline stages but multithreaded render pipelines not enabled in build.\n";
263  }
264  _num_stages = 1;
265 #endif // THREADED_PIPELINE
266 }
267 
268 #ifdef THREADED_PIPELINE
269 ////////////////////////////////////////////////////////////////////
270 // Function: Pipeline::add_cycler
271 // Access: Public
272 // Description: Adds the indicated cycler to the list of cyclers
273 // associated with the pipeline. This method only
274 // exists when true pipelining is configured on.
275 ////////////////////////////////////////////////////////////////////
276 void Pipeline::
277 add_cycler(PipelineCyclerTrueImpl *cycler) {
278  ReMutexHolder holder(_lock);
279  nassertv(!cycler->_dirty);
280  nassertv(!_cycling);
281 
282  cycler->insert_before(&_clean);
283  ++_num_cyclers;
284 
285 #ifdef DEBUG_THREADS
286  inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), 1);
287 #endif
288 }
289 #endif // THREADED_PIPELINE
290 
291 #ifdef THREADED_PIPELINE
292 ////////////////////////////////////////////////////////////////////
293 // Function: Pipeline::add_dirty_cycler
294 // Access: Public
295 // Description: Marks the indicated cycler as "dirty", meaning it
296 // will need to be cycled next frame. This both adds it
297 // to the "dirty" set and also sets the "dirty" flag
298 // within the cycler. This method only exists when true
299 // pipelining is configured on.
300 ////////////////////////////////////////////////////////////////////
301 void Pipeline::
302 add_dirty_cycler(PipelineCyclerTrueImpl *cycler) {
303  nassertv(cycler->_lock.debug_is_locked());
304 
305  ReMutexHolder holder(_lock);
306  nassertv(_num_stages != 1);
307  nassertv(!_cycling);
308  nassertv(!cycler->_dirty);
309 
310  // Remove it from the "clean" list and add it to the "dirty" list.
311  cycler->remove_from_list();
312  cycler->insert_before(&_dirty);
313  cycler->_dirty = true;
314  ++_num_dirty_cyclers;
315 
316 #ifdef DEBUG_THREADS
317  inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), 1);
318 #endif
319 }
320 #endif // THREADED_PIPELINE
321 
322 #ifdef THREADED_PIPELINE
323 ////////////////////////////////////////////////////////////////////
324 // Function: Pipeline::remove_cycler
325 // Access: Public
326 // Description: Removes the indicated cycler from the list of cyclers
327 // associated with the pipeline. This method only
328 // exists when true pipelining is configured on.
329 ////////////////////////////////////////////////////////////////////
330 void Pipeline::
331 remove_cycler(PipelineCyclerTrueImpl *cycler) {
332  nassertv(cycler->_lock.debug_is_locked());
333 
334  ReMutexHolder holder(_lock);
335  nassertv(!_cycling);
336 
337  --_num_cyclers;
338  cycler->remove_from_list();
339 
340 #ifdef DEBUG_THREADS
341  inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), -1);
342 #endif
343 
344  if (cycler->_dirty) {
345  cycler->_dirty = false;
346  --_num_dirty_cyclers;
347 #ifdef DEBUG_THREADS
348  inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
349 #endif
350  }
351 }
352 #endif // THREADED_PIPELINE
353 
354 #if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS)
355 ////////////////////////////////////////////////////////////////////
356 // Function: Pipeline::iterate_all_cycler_types
357 // Access: Public
358 // Description: Walks through the list of all the different
359 // PipelineCycler types in the universe. For each one,
360 // calls the indicated callback function with the
361 // TypeHandle of the respective type (actually, the
362 // result of cycler::get_parent_type()) and the count of
363 // pipeline cyclers of that type. Mainly used for
364 // PStats reporting.
365 ////////////////////////////////////////////////////////////////////
366 void Pipeline::
367 iterate_all_cycler_types(CallbackFunc *func, void *data) const {
368  ReMutexHolder holder(_lock);
369  TypeCount::const_iterator ci;
370  for (ci = _all_cycler_types.begin(); ci != _all_cycler_types.end(); ++ci) {
371  func((*ci).first, (*ci).second, data);
372  }
373 }
374 #endif // THREADED_PIPELINE && DEBUG_THREADS
375 
376 #if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS)
377 ////////////////////////////////////////////////////////////////////
378 // Function: Pipeline::iterate_dirty_cycler_types
379 // Access: Public
380 // Description: Walks through the list of all the different
381 // PipelineCycler types, for only the dirty
382 // PipelineCyclers. See also
383 // iterate_all_cycler_types().
384 ////////////////////////////////////////////////////////////////////
385 void Pipeline::
386 iterate_dirty_cycler_types(CallbackFunc *func, void *data) const {
387  ReMutexHolder holder(_lock);
388  TypeCount::const_iterator ci;
389  for (ci = _dirty_cycler_types.begin(); ci != _dirty_cycler_types.end(); ++ci) {
390  func((*ci).first, (*ci).second, data);
391  }
392 }
393 #endif // THREADED_PIPELINE && DEBUG_THREADS
394 
395 ////////////////////////////////////////////////////////////////////
396 // Function: Pipeline::make_render_pipeline
397 // Access: Private, Static
398 // Description:
399 ////////////////////////////////////////////////////////////////////
400 void Pipeline::
401 make_render_pipeline() {
402  ConfigVariableInt pipeline_stages
403  ("pipeline-stages", 1,
404  PRC_DESC("The initial number of stages in the render pipeline. This is "
405  "only meaningful if threaded pipelining is compiled into "
406  "Panda. In most cases, you should not set this at all anyway, "
407  "since the pipeline can automatically grow stages as needed, "
408  "but it will not remove stages automatically, and having more "
409  "pipeline stages than your application requires will incur "
410  "additional runtime overhead."));
411 
412  nassertv(_render_pipeline == (Pipeline *)NULL);
413  _render_pipeline = new Pipeline("render", pipeline_stages);
414 }
415 
416 #if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS)
417 ////////////////////////////////////////////////////////////////////
418 // Function: Pipeline::inc_cycler_type
419 // Access: Private, Static
420 // Description: Increments (or decrements, according to added) the
421 // value for TypeHandle in the indicated TypeCount map.
422 // This is used in DEBUG_THREADS mode to track the types
423 // of PipelineCyclers that are coming and going, mainly
424 // for PStats reporting.
425 //
426 // It is assumed the lock is held during this call.
427 ////////////////////////////////////////////////////////////////////
428 void Pipeline::
429 inc_cycler_type(TypeCount &count, TypeHandle type, int addend) {
430  TypeCount::iterator ci = count.find(type);
431  if (ci == count.end()) {
432  ci = count.insert(TypeCount::value_type(type, 0)).first;
433  }
434  (*ci).second += addend;
435  nassertv((*ci).second >= 0);
436 }
437 #endif // THREADED_PIPELINE && DEBUG_THREADS
void set_num_stages(int num_stages)
Specifies the number of stages required for the pipeline.
Definition: pipeline.cxx:214
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
void cycle()
Flows all the pipeline data down to the next stage.
Definition: pipeline.cxx:94
A base class for all things which can have a name.
Definition: namable.h:29
Similar to MutexHolder, but for a reentrant mutex.
Definition: reMutexHolder.h:27
This class manages a staged pipeline of data, for instance the render pipeline, so that each stage of...
Definition: pipeline.h:41
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:85