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