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
15
16#ifdef THREADED_PIPELINE
17
18#include "config_pipeline.h"
19#include "pipeline.h"
20
21/**
22 *
23 */
24PipelineCyclerTrueImpl::
25PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) :
26 _pipeline(pipeline),
27 _dirty(0),
28 _lock(this)
29{
30 if (_pipeline == nullptr) {
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 */
46PipelineCyclerTrueImpl::
47PipelineCyclerTrueImpl(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 */
82void PipelineCyclerTrueImpl::
83operator = (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 */
107PipelineCyclerTrueImpl::
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 */
124CycleData *PipelineCyclerTrueImpl::
125write_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 */
172CycleData *PipelineCyclerTrueImpl::
173write_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 */
256PT(CycleData) PipelineCyclerTrueImpl::
257cycle() {
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 */
289void PipelineCyclerTrueImpl::
290set_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 */
326void PipelineCyclerTrueImpl::CyclerMutex::
327output(std::ostream &out) const {
328 out << "CyclerMutex ";
329 _cycler->cheat()->output(out);
330}
331#endif // DEBUG_THREADS
332
333#endif // THREADED_PIPELINE
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
This class manages a staged pipeline of data, for instance the render pipeline, so that each stage of...
Definition: pipeline.h:38
static Pipeline * get_render_pipeline()
Returns a pointer to the global render pipeline.
Definition: pipeline.I:18
Similar to MutexHolder, but for a reentrant mutex.
Definition: reMutexHolder.h:25
A thread; that is, a lightweight process.
Definition: thread.h:46
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.