Panda3D

Loading...
Searching...
No Matches
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 */
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 */
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::
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 * Adds the indicated cycler to the list of cyclers associated with the
363 * pipeline. This method only exists when true pipelining is configured on.
364 *
365 * If the dirty flag is true, it will be marked as dirty in addition, as though
366 * add_dirty_cycler() were called immediately afterward.
367 */
368 void Pipeline::
369add_cycler(PipelineCyclerTrueImpl *cycler, bool dirty) {
370 // It's safe to add it to the list while cycling, since the _clean list is
371 // not touched during the cycle loop.
372 MutexHolder holder(_lock);
373 nassertv(!cycler->_dirty);
374
375 if (!dirty) {
376 cycler->insert_before(&_clean);
377 }
378 else {
379 nassertv(_num_stages != 1);
380 cycler->insert_before(&_dirty);
381 cycler->_dirty = _next_cycle_seq;
382 ++_num_dirty_cyclers;
383
384#ifdef DEBUG_THREADS
385 inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), 1);
386#endif
387 }
388 ++_num_cyclers;
389
390#ifdef DEBUG_THREADS
391 inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), 1);
392#endif
393}
394#endif // THREADED_PIPELINE
395
396#ifdef THREADED_PIPELINE
397/**
398 * Marks the indicated cycler as "dirty", meaning it will need to be cycled
399 * next frame. This both adds it to the "dirty" set and also sets the "dirty"
400 * flag within the cycler. This method only exists when true pipelining is
401 * configured on.
402 */
403 void Pipeline::
404add_dirty_cycler(PipelineCyclerTrueImpl *cycler) {
405 nassertv(cycler->_lock.debug_is_locked());
406
407 // It's safe to add it to the list while cycling, since it's not currently
408 // on the dirty list.
409 MutexHolder holder(_lock);
410 nassertv(!cycler->_dirty);
411 nassertv(_num_stages != 1);
412
413 // Remove it from the "clean" list and add it to the "dirty" list.
414 cycler->remove_from_list();
415 cycler->insert_before(&_dirty);
416 cycler->_dirty = _next_cycle_seq;
417 ++_num_dirty_cyclers;
418
419#ifdef DEBUG_THREADS
420 inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), 1);
421#endif
422}
423#endif // THREADED_PIPELINE
424
425#ifdef THREADED_PIPELINE
426/**
427 * Removes the indicated cycler from the list of cyclers associated with the
428 * pipeline. This method only exists when true pipelining is configured on.
429 */
430 void Pipeline::
431remove_cycler(PipelineCyclerTrueImpl *cycler) {
432 nassertv(cycler->_lock.debug_is_locked());
433
434 MutexHolder holder(_lock);
435
436 // If it's dirty, it may currently be processed by cycle(), so we need to be
437 // careful not to cause a race condition. It's safe for us to remove it
438 // during cycle only if it's 0 (clean) or _next_cycle_seq (scheduled for the
439 // next cycle, so not owned by the current one).
440 while (cycler->_dirty != 0 && cycler->_dirty != _next_cycle_seq) {
441 if (_cycle_lock.try_lock()) {
442 // OK, great, we got the lock, so it finished cycling already.
443 nassertv(!_cycling);
444
445 --_num_cyclers;
446 cycler->remove_from_list();
447
448 cycler->_dirty = false;
449 --_num_dirty_cyclers;
450
451 #ifdef DEBUG_THREADS
452 inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), -1);
453 inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
454 #endif
455
456 _cycle_lock.unlock();
457 return;
458 } else {
459 // It's possibly currently being cycled. We will wait for the cycler
460 // to be done with it, so that we can safely remove it.
461 _lock.unlock();
462 cycler->_lock.unlock();
463 Thread::force_yield();
464 cycler->_lock.lock();
465 _lock.lock();
466 }
467 }
468
469 // It's not being owned by a cycle operation, so it's fair game.
470 --_num_cyclers;
471 cycler->remove_from_list();
472
473#ifdef DEBUG_THREADS
474 inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), -1);
475#endif
476
477 if (cycler->_dirty) {
478 cycler->_dirty = 0;
479 --_num_dirty_cyclers;
480#ifdef DEBUG_THREADS
481 inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
482#endif
483 }
484}
485#endif // THREADED_PIPELINE
486
487#if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS)
488/**
489 * Walks through the list of all the different PipelineCycler types in the
490 * universe. For each one, calls the indicated callback function with the
491 * TypeHandle of the respective type (actually, the result of
492 * cycler::get_parent_type()) and the count of pipeline cyclers of that type.
493 * Mainly used for PStats reporting.
494 */
495 void Pipeline::
496iterate_all_cycler_types(CallbackFunc *func, void *data) const {
497 // Make sure it's not currently cycling.
498 ReMutexHolder cycle_holder(_cycle_lock);
499 MutexHolder holder(_lock);
500 TypeCount::const_iterator ci;
501 for (ci = _all_cycler_types.begin(); ci != _all_cycler_types.end(); ++ci) {
502 func((*ci).first, (*ci).second, data);
503 }
504}
505#endif // THREADED_PIPELINE && DEBUG_THREADS
506
507#if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS)
508/**
509 * Walks through the list of all the different PipelineCycler types, for only
510 * the dirty PipelineCyclers. See also iterate_all_cycler_types().
511 */
512 void Pipeline::
513iterate_dirty_cycler_types(CallbackFunc *func, void *data) const {
514 // Make sure it's not currently cycling.
515 ReMutexHolder cycle_holder(_cycle_lock);
516 MutexHolder holder(_lock);
517 TypeCount::const_iterator ci;
518 for (ci = _dirty_cycler_types.begin(); ci != _dirty_cycler_types.end(); ++ci) {
519 func((*ci).first, (*ci).second, data);
520 }
521}
522#endif // THREADED_PIPELINE && DEBUG_THREADS
523
524/**
525 *
526 */
527 void Pipeline::
528make_render_pipeline() {
529 ConfigVariableInt pipeline_stages
530 ("pipeline-stages", 1,
531 PRC_DESC("The initial number of stages in the render pipeline. This is "
532 "only meaningful if threaded pipelining is compiled into "
533 "Panda. In most cases, you should not set this at all anyway, "
534 "since the pipeline can automatically grow stages as needed, "
535 "but it will not remove stages automatically, and having more "
536 "pipeline stages than your application requires will incur "
537 "additional runtime overhead."));
538
539 nassertv(_render_pipeline == nullptr);
540 _render_pipeline = new Pipeline("render", pipeline_stages);
541}
542
543#if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS)
544/**
545 * Increments (or decrements, according to added) the value for TypeHandle in
546 * the indicated TypeCount map. This is used in DEBUG_THREADS mode to track
547 * the types of PipelineCyclers that are coming and going, mainly for PStats
548 * reporting.
549 *
550 * It is assumed the lock is held during this call.
551 */
552 void Pipeline::
553inc_cycler_type(TypeCount &count, TypeHandle type, int addend) {
554 TypeCount::iterator ci = count.find(type);
555 if (ci == count.end()) {
556 ci = count.insert(TypeCount::value_type(type, 0)).first;
557 }
558 (*ci).second += addend;
559 nassertv((*ci).second >= 0);
560}
561#endif // THREADED_PIPELINE && DEBUG_THREADS
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
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.