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