Panda3D
pipelineCyclerTrueImpl.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 pipelineCyclerTrueImpl.cxx
10  * @author drose
11  * @date 2006-01-31
12  */
13 
14 #include "pipelineCyclerTrueImpl.h"
15 
16 #ifdef THREADED_PIPELINE
17 
18 #include "config_pipeline.h"
19 #include "pipeline.h"
20 
21 /**
22  *
23  */
24 PipelineCyclerTrueImpl::
25 PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) :
26  _pipeline(pipeline),
27  _dirty(0),
28  _lock(this)
29 {
30  if (_pipeline == nullptr) {
31  _pipeline = Pipeline::get_render_pipeline();
32  }
33 
34  _num_stages = _pipeline->get_num_stages();
35  _data = new CycleDataNode[_num_stages];
36  for (int i = 0; i < _num_stages; ++i) {
37  _data[i]._cdata = initial_data;
38  }
39 
40  _pipeline->add_cycler(this);
41 }
42 
43 /**
44  *
45  */
46 PipelineCyclerTrueImpl::
47 PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl &copy) :
48  _pipeline(copy._pipeline),
49  _dirty(0),
50  _lock(this)
51 {
52  ReMutexHolder holder(_lock);
53  ReMutexHolder holder2(copy._lock);
54 
55  _num_stages = _pipeline->get_num_stages();
56  nassertv(_num_stages == copy._num_stages);
57  _data = new CycleDataNode[_num_stages];
58 
59  // It's no longer critically important that we preserve pointerwise
60  // equivalence between different stages in the copy, but it doesn't cost
61  // much and might be a little more efficient, so we do it anyway.
62  typedef pmap<CycleData *, PT(CycleData) > Pointers;
63  Pointers pointers;
64 
65  for (int i = 0; i < _num_stages; ++i) {
66  PT(CycleData) &new_pt = pointers[copy._data[i]._cdata];
67  if (new_pt == nullptr) {
68  new_pt = copy._data[i]._cdata->make_copy();
69  }
70  _data[i]._cdata = new_pt.p();
71  }
72 
73  _pipeline->add_cycler(this);
74  if (copy._dirty) {
75  _pipeline->add_dirty_cycler(this);
76  }
77 }
78 
79 /**
80  *
81  */
82 void PipelineCyclerTrueImpl::
83 operator = (const PipelineCyclerTrueImpl &copy) {
84  ReMutexHolder holder1(_lock);
85  ReMutexHolder holder2(copy._lock);
86  nassertv(get_parent_type() == copy.get_parent_type());
87 
88  typedef pmap<CycleData *, PT(CycleData) > Pointers;
89  Pointers pointers;
90 
91  for (int i = 0; i < _num_stages; ++i) {
92  PT(CycleData) &new_pt = pointers[copy._data[i]._cdata];
93  if (new_pt == nullptr) {
94  new_pt = copy._data[i]._cdata->make_copy();
95  }
96  _data[i]._cdata = new_pt.p();
97  }
98 
99  if (copy._dirty && !_dirty) {
100  _pipeline->add_dirty_cycler(this);
101  }
102 }
103 
104 /**
105  *
106  */
107 PipelineCyclerTrueImpl::
108 ~PipelineCyclerTrueImpl() {
109  ReMutexHolder holder(_lock);
110 
111  _pipeline->remove_cycler(this);
112 
113  delete[] _data;
114  _data = nullptr;
115  _num_stages = 0;
116 }
117 
118 /**
119  * Returns a pointer suitable for writing to the nth stage of the pipeline.
120  * This is for special applications that need to update the entire pipeline at
121  * once (for instance, to remove an invalid pointer). This pointer should
122  * later be released with release_write_stage().
123  */
124 CycleData *PipelineCyclerTrueImpl::
125 write_stage(int pipeline_stage, Thread *current_thread) {
126  _lock.acquire(current_thread);
127 
128 #ifndef NDEBUG
129  nassertd(pipeline_stage >= 0 && pipeline_stage < _num_stages) {
130  _lock.release();
131  return nullptr;
132  }
133 #endif // NDEBUG
134 
135  CycleData *old_data = _data[pipeline_stage]._cdata;
136 
137  // We only perform copy-on-write if this is the first CycleData requested
138  // for write mode from this thread. (We will never have outstanding writes
139  // for multiple threads, because we hold the CyclerMutex during the entire
140  // lifetime of write() .. release()).
141  if (_data[pipeline_stage]._writes_outstanding == 0) {
142  // Only the node reference count is considered an important count for
143  // copy-on-write purposes. A standard reference of other than 1 just
144  // means that some code (other that the PipelineCycler) has a pointer,
145  // which is safe to modify.
146  if (old_data->get_node_ref_count() != 1) {
147  // Copy-on-write.
148  _data[pipeline_stage]._cdata = old_data->make_copy();
149  if (pipeline_cat.is_debug()) {
150  pipeline_cat.debug()
151  << "Copy-on-write a: " << old_data << " becomes "
152  << _data[pipeline_stage]._cdata << "\n";
153  // nassertr(false, NULL);
154  }
155 
156  // Now we have differences between some of the data pointers, so we're
157  // "dirty". Mark it so.
158  if (!_dirty && _num_stages != 1) {
159  _pipeline->add_dirty_cycler(this);
160  }
161  }
162  }
163 
164  ++(_data[pipeline_stage]._writes_outstanding);
165  return _data[pipeline_stage]._cdata;
166 }
167 
168 /**
169  * This special variant on write_stage() will automatically propagate changes
170  * back to upstream pipeline stages. See write_upstream().
171  */
172 CycleData *PipelineCyclerTrueImpl::
173 write_stage_upstream(int pipeline_stage, bool force_to_0, Thread *current_thread) {
174  _lock.acquire(current_thread);
175 
176 #ifndef NDEBUG
177  nassertd(pipeline_stage >= 0 && pipeline_stage < _num_stages) {
178  _lock.release();
179  return nullptr;
180  }
181 #endif // NDEBUG
182 
183  CycleData *old_data = _data[pipeline_stage]._cdata;
184 
185  if (old_data->get_ref_count() != 1 || force_to_0) {
186  // Count the number of references before the current stage, and the number
187  // of references remaining other than those.
188  int external_count = old_data->get_ref_count() - 1;
189  int k = pipeline_stage - 1;
190  while (k >= 0 && _data[k]._cdata == old_data) {
191  --k;
192  --external_count;
193  }
194 
195  // We only perform copy-on-write if this is the first CycleData requested
196  // for write mode from this thread. (We will never have outstanding
197  // writes for multiple threads, because we hold the CyclerMutex during the
198  // entire lifetime of write() .. release()).
199  if (external_count > 0 && _data[pipeline_stage]._writes_outstanding == 0) {
200  // There are references other than the ones before this stage in the
201  // pipeline; perform a copy-on-write.
202  PT(CycleData) new_data = old_data->make_copy();
203  if (pipeline_cat.is_debug()) {
204  pipeline_cat.debug()
205  << "Copy-on-write b: " << old_data << " becomes "
206  << new_data << "\n";
207  // nassertr(false, NULL);
208  }
209 
210  k = pipeline_stage - 1;
211  while (k >= 0 && (_data[k]._cdata == old_data || force_to_0)) {
212  nassertr(_data[k]._writes_outstanding == 0, nullptr);
213  _data[k]._cdata = new_data.p();
214  --k;
215  }
216 
217  _data[pipeline_stage]._cdata = new_data;
218 
219  if (k >= 0 || pipeline_stage + 1 < _num_stages) {
220  // Now we have differences between some of the data pointers, which
221  // makes us "dirty".
222  if (!_dirty) {
223  _pipeline->add_dirty_cycler(this);
224  }
225  }
226 
227  } else if (k >= 0 && force_to_0) {
228  // There are no external pointers, so no need to copy-on-write, but the
229  // current pointer doesn't go all the way back. Make it do so.
230  while (k >= 0) {
231  nassertr(_data[k]._writes_outstanding == 0, nullptr);
232  _data[k]._cdata = old_data;
233  --k;
234  }
235  }
236  }
237 
238  ++(_data[pipeline_stage]._writes_outstanding);
239  return _data[pipeline_stage]._cdata;
240 }
241 
242 /**
243  * Cycles the data between frames. This is only called from
244  * Pipeline::cycle(), and presumably it is only called if the cycler is
245  * "dirty".
246  *
247  * At the conclusion of this method, the cycler should clear its dirty flag if
248  * it is no longer "dirty"--that is, if all of the pipeline pointers are the
249  * same.
250  *
251  * The return value is the CycleData pointer which fell off the end of the
252  * cycle. If this is allowed to destruct immediately, there may be side-
253  * effects that cascade through the system, so the caller may choose to hold
254  * the pointer until it can safely be released later.
255  */
256 PT(CycleData) PipelineCyclerTrueImpl::
257 cycle() {
258  // This trick moves an NPT into a PT without unnecessarily incrementing and
259  // subsequently decrementing the regular reference count.
260  PT(CycleData) last_val;
261  last_val.swap(_data[_num_stages - 1]._cdata);
262  last_val->node_unref_only();
263 
264  nassertr(_lock.debug_is_locked(), last_val);
265  nassertr(_dirty, last_val);
266 
267  int i;
268  for (i = _num_stages - 1; i > 0; --i) {
269  nassertr(_data[i]._writes_outstanding == 0, last_val);
270  _data[i]._cdata = _data[i - 1]._cdata;
271  }
272 
273  for (i = 1; i < _num_stages; ++i) {
274  if (_data[i]._cdata != _data[i - 1]._cdata) {
275  // Still dirty.
276  return last_val;
277  }
278  }
279 
280  // No longer dirty.
281  _dirty = 0;
282  return last_val;
283 }
284 
285 /**
286  * Changes the number of stages in the cycler. This is only called from
287  * Pipeline::set_num_stages();
288  */
289 void PipelineCyclerTrueImpl::
290 set_num_stages(int num_stages) {
291  nassertv(_lock.debug_is_locked());
292 
293  if (num_stages <= _num_stages) {
294  // Don't bother to reallocate the array smaller; we just won't use the
295  // rest of the array.
296  for (int i = _num_stages; i < num_stages; ++i) {
297  nassertv(_data[i]._writes_outstanding == 0);
298  _data[i]._cdata.clear();
299  }
300 
301  _num_stages = num_stages;
302 
303 
304  } else {
305  // To increase the array, we must reallocate it larger.
306  CycleDataNode *new_data = new CycleDataNode[num_stages];
307  int i;
308  for (i = 0; i < _num_stages; ++i) {
309  nassertv(_data[i]._writes_outstanding == 0);
310  new_data[i]._cdata = _data[i]._cdata;
311  }
312  for (i = _num_stages; i < num_stages; ++i) {
313  new_data[i]._cdata = _data[_num_stages - 1]._cdata;
314  }
315  delete[] _data;
316 
317  _num_stages = num_stages;
318  _data = new_data;
319  }
320 }
321 
322 #ifdef DEBUG_THREADS
323 /**
324  *
325  */
326 void PipelineCyclerTrueImpl::CyclerMutex::
327 output(std::ostream &out) const {
328  out << "CyclerMutex ";
329  _cycler->cheat()->output(out);
330 }
331 #endif // DEBUG_THREADS
332 
333 #endif // THREADED_PIPELINE
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:47
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Similar to MutexHolder, but for a reentrant mutex.
Definition: reMutexHolder.h:25
static Pipeline * get_render_pipeline()
Returns a pointer to the global render pipeline.
Definition: pipeline.I:18
This class manages a staged pipeline of data, for instance the render pipeline, so that each stage of...
Definition: pipeline.h:38
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A thread; that is, a lightweight process.
Definition: thread.h:46