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