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