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"
16#include "configVariableInt.h"
17#include "config_pipeline.h"
18
19Pipeline *Pipeline::_render_pipeline = nullptr;
20
21/**
22 *
23 */
24Pipeline::
25Pipeline(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 */
72Pipeline::
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 */
87cycle() {
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 */
283set_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 */
344void Pipeline::
345add_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 */
367void Pipeline::
368add_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 */
394void Pipeline::
395remove_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 */
459void Pipeline::
460iterate_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 */
476void Pipeline::
477iterate_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 */
491void Pipeline::
492make_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 */
516void Pipeline::
517inc_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.