Panda3D

geomMunger.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 geomMunger.cxx
10 * @author drose
11 * @date 2005-03-10
12 */
13
14 #include "geomMunger.h"
15 #include "geom.h"
16 #include "geomCacheManager.h"
17 #include "lightMutexHolder.h"
18 #include "lightReMutexHolder.h"
19 #include "pStatTimer.h"
20
21 GeomMunger::Registry *GeomMunger::_registry = nullptr;
22 TypeHandle GeomMunger::_type_handle;
23
24 PStatCollector GeomMunger::_munge_pcollector("*:Munge");
25
26 /**
27 *
28 */
29 GeomMunger::
30 GeomMunger(GraphicsStateGuardianBase *gsg) :
31 _gsg(gsg),
32 _is_registered(false)
33 {
34 #ifndef NDEBUG
35 Registry *registry = get_registry();
36 LightReMutexHolder holder(registry->_registry_lock);
37 _registered_key = registry->_mungers.end();
38 #endif
39 }
40
41 /**
42 *
43 */
44 GeomMunger::
45 GeomMunger(const GeomMunger &copy) :
46 _is_registered(false)
47 {
48 #ifndef NDEBUG
49 Registry *registry = get_registry();
50 LightReMutexHolder holder(registry->_registry_lock);
51 _registered_key = registry->_mungers.end();
52 #endif
53 }
54
55 /**
56 *
57 */
58 void GeomMunger::
59 operator = (const GeomMunger &copy) {
60 nassertv(!_is_registered);
61 }
62
63 /**
64 *
65 */
66 GeomMunger::
67 ~GeomMunger() {
68 unregister_myself();
69 nassertv(_formats_by_animation.empty());
70 }
71
72 /**
73 * Removes a prepared GeomVertexData from the cache.
74 */
75 void GeomMunger::
76 remove_data(const GeomVertexData *data) {
77 // If this assertion is triggered, maybe we accidentally deleted a
78 // GeomVertexData while we were in the process of unregistering, causing a
79 // recursive re-entry.
80 nassertv(_is_registered);
81 }
82
83 /**
84 * Applies the indicated munger to the geom and its data, and returns a
85 * (possibly different) geom and data, according to the munger's whim.
86 *
87 * The assumption is that for a particular geom and a particular munger, the
88 * result will always be the same; so this result may be cached.
89 *
90 * If force is false, this may do nothing and return false if the vertex data
91 * is nonresident. If force is true, this will always return true, but it may
92 * have to block while the vertex data is paged in.
93 */
94 bool GeomMunger::
95 munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
96 bool force, Thread *current_thread) {
97
98 // Look up the munger in the geom's cache--maybe we've recently applied it.
99 PT(Geom::CacheEntry) entry;
100
101 Geom::CacheKey key(data, this);
102
103 geom->_cache_lock.acquire();
104 Geom::Cache::const_iterator ci = geom->_cache.find(&key);
105 if (ci == geom->_cache.end()) {
106 geom->_cache_lock.release();
107 } else {
108 entry = (*ci).second;
109 geom->_cache_lock.release();
110 nassertr(entry->_source == geom, false);
111
112 // Here's an element in the cache for this computation. Record a cache
113 // hit, so this element will stay in the cache a while longer.
114 entry->refresh(current_thread);
115
116 // Now check that it's fresh.
117 Geom::CDCacheReader cdata(entry->_cycler, current_thread);
118 if (cdata->_source == geom &&
119 cdata->_geom_result != nullptr &&
120 geom->get_modified(current_thread) <= cdata->_geom_result->get_modified(current_thread) &&
121 data->get_modified(current_thread) <= cdata->_data_result->get_modified(current_thread)) {
122 // The cache entry is still good; use it.
123
124 geom = cdata->_geom_result;
125 data = cdata->_data_result;
126 return true;
127 }
128
129 // The cache entry is stale, but we'll recompute it below. Note that
130 // there's a small race condition here; another thread might recompute the
131 // cache at the same time. No big deal, since it'll compute the same
132 // result.
133 }
134
135 if (!force && (!geom->request_resident() || !data->request_resident())) {
136 // Oh dear, the data isn't resident. We can't munge it, so give up.
137 return false;
138 }
139
140 // Ok, invoke the munger.
141 PStatTimer timer(_munge_pcollector, current_thread);
142
143 PT(Geom) orig_geom = (Geom *)geom.p();
144 data = munge_data(data);
145 munge_geom_impl(geom, data, current_thread);
146
147 // Record the new result in the cache.
148 if (entry == nullptr) {
149 // Create a new entry for the result.
150 // We don't need the key anymore, move the pointers into the CacheEntry.
151 entry = new Geom::CacheEntry(orig_geom, std::move(key));
152
153 {
154 LightMutexHolder holder(orig_geom->_cache_lock);
155 bool inserted = orig_geom->_cache.insert(Geom::Cache::value_type(&entry->_key, entry)).second;
156 if (!inserted) {
157 // Some other thread must have beat us to the punch. Never mind.
158 return true;
159 }
160 }
161
162 // And tell the cache manager about the new entry. (It might immediately
163 // request a delete from the cache of the thing we just added.)
164 entry->record(current_thread);
165 }
166
167 // Finally, store the cached result on the entry.
168 Geom::CDCacheWriter cdata(entry->_cycler, true, current_thread);
169 cdata->_source = (Geom *)orig_geom.p();
170 cdata->set_result(geom, data);
171
172 return true;
173 }
174
175 /**
176 * The protected implementation of munge_format(). This exists just to cast
177 * away the const pointer.
178 */
179 CPT(GeomVertexFormat) GeomMunger::
180 do_munge_format(const GeomVertexFormat *format,
181 const GeomVertexAnimationSpec &animation) {
182 nassertr(_is_registered, nullptr);
183 nassertr(format->is_registered(), nullptr);
184
185 LightMutexHolder holder(_formats_lock);
186
187 Formats &formats = _formats_by_animation[animation];
188
189 Formats::iterator fi;
190 fi = formats.find(format);
191 if (fi != formats.end()) {
192 // This format was previously munged, so the answer will be the same.
193 return (*fi).second;
194 }
195
196 // We have to munge this format for the first time.
197 CPT(GeomVertexFormat) derived_format = munge_format_impl(format, animation);
198 nassertr(derived_format->is_registered(), nullptr);
199
200 // Store the answer in the map, so we can quickly get it next time.
201 bool inserted = formats.insert(Formats::value_type(format, derived_format)).second;
202 nassertr(inserted, nullptr);
203
204 return derived_format;
205 }
206
207 /**
208 * Given a source GeomVertexFormat, converts it if necessary to the
209 * appropriate format for rendering.
210 */
211 CPT(GeomVertexFormat) GeomMunger::
212 munge_format_impl(const GeomVertexFormat *orig, const GeomVertexAnimationSpec &) {
213 return orig;
214 }
215
216 /**
217 * Given a source GeomVertexData, converts it as necessary for rendering.
218 */
219 CPT(GeomVertexData) GeomMunger::
220 munge_data_impl(const GeomVertexData *data) {
221 nassertr(_is_registered, nullptr);
222
223 CPT(GeomVertexFormat) orig_format = data->get_format();
224 CPT(GeomVertexFormat) new_format =
225 munge_format(orig_format, orig_format->get_animation());
226
227 if (new_format == orig_format) {
228 // Trivial case.
229 return data;
230 }
231
232 return data->convert_to(new_format);
233 }
234
235 /**
236 * Converts a Geom and/or its data as necessary.
237 */
238 void GeomMunger::
239 munge_geom_impl(CPT(Geom) &, CPT(GeomVertexData) &, Thread *) {
240 // The default implementation does nothing (the work has already been done
241 // in munge_format_impl() and munge_data_impl()).
242 }
243
244 /**
245 * The protected implementation of premunge_format(). This exists just to
246 * cast away the const pointer.
247 */
248 CPT(GeomVertexFormat) GeomMunger::
249 do_premunge_format(const GeomVertexFormat *format) {
250 nassertr(_is_registered, nullptr);
251 nassertr(format->is_registered(), nullptr);
252
253 LightMutexHolder holder(_formats_lock);
254
255 Formats::iterator fi;
256 fi = _premunge_formats.find(format);
257 if (fi != _premunge_formats.end()) {
258 // This format was previously munged, so the answer will be the same.
259 return (*fi).second;
260 }
261
262 // We have to munge this format for the first time.
263 CPT(GeomVertexFormat) derived_format = premunge_format_impl(format);
264 nassertr(derived_format->is_registered(), nullptr);
265
266 // Store the answer in the map, so we can quickly get it next time.
267 bool inserted = _premunge_formats.insert(Formats::value_type(format, derived_format)).second;
268 nassertr(inserted, nullptr);
269
270 return derived_format;
271 }
272
273 /**
274 * Given a source GeomVertexFormat, converts it if necessary to the
275 * appropriate format for rendering.
276 */
277 CPT(GeomVertexFormat) GeomMunger::
278 premunge_format_impl(const GeomVertexFormat *orig) {
279 return orig;
280 }
281
282 /**
283 * Given a source GeomVertexData, converts it as necessary for rendering.
284 */
285 CPT(GeomVertexData) GeomMunger::
286 premunge_data_impl(const GeomVertexData *data) {
287 nassertr(_is_registered, nullptr);
288
289 CPT(GeomVertexFormat) orig_format = data->get_format();
290 CPT(GeomVertexFormat) new_format = premunge_format(orig_format);
291
292 if (new_format == orig_format) {
293 // Trivial case.
294 return data;
295 }
296
297 return data->convert_to(new_format);
298 }
299
300 /**
301 * Converts a Geom and/or its data as necessary.
302 */
303 void GeomMunger::
304 premunge_geom_impl(CPT(Geom) &, CPT(GeomVertexData) &) {
305 // The default implementation does nothing (the work has already been done
306 // in premunge_format_impl() and premunge_data_impl()).
307 }
308
309 /**
310 * Called to compare two GeomMungers who are known to be of the same type, for
311 * an apples-to-apples comparison. This will never be called on two pointers
312 * of a different type.
313 */
314 int GeomMunger::
315 compare_to_impl(const GeomMunger *other) const {
316 return 0;
317 }
318
319 /**
320 * Compares two GeomMungers, considering only whether they would produce a
321 * different answer to munge_format(), munge_data(), or munge_geom(). (They
322 * still might be different in other ways, but if they would produce the same
323 * answer, this function will consider them to be the same.)
324 */
325 int GeomMunger::
326 geom_compare_to_impl(const GeomMunger *other) const {
327 return 0;
328 }
329
330 /**
331 * Returns the global registry object.
332 */
333 void GeomMunger::
334 make_registry() {
335 if (_registry == nullptr) {
336 _registry = new Registry;
337 }
338 }
339
340 /**
341 * Called internally when the munger is registered.
342 */
343 void GeomMunger::
344 do_register(Thread *current_thread) {
345 if (gobj_cat.is_debug()) {
346 gobj_cat.debug()
347 << "GeomMunger::do_register(): " << (void *)this << "\n";
348 }
349 nassertv(!_is_registered);
350 nassertv(_formats_by_animation.empty());
351
352 // Tell the cache manager to hang on to this new GeomMunger, so we don't
353 // waste our time re-registering the same GeomMunger over and over again.
354 CacheEntry *entry = new CacheEntry;
355 entry->_munger = this;
356 entry->record(current_thread);
357
358 _is_registered = true;
359 }
360
361 /**
362 * Called internally when the munger is unregistered.
363 */
364 void GeomMunger::
365 do_unregister() {
366 if (gobj_cat.is_debug()) {
367 gobj_cat.debug()
368 << "GeomMunger::do_unregister(): " << (void *)this << "\n";
369 }
370 nassertv(_is_registered);
371 _is_registered = false;
372
373 // Unregistering means we should blow away the cache.
374 _formats_by_animation.clear();
375 }
376
377 /**
378 *
379 */
380 void GeomMunger::CacheEntry::
381 output(std::ostream &out) const {
382 out << "munger " << _munger;
383 }
384
385 /**
386 *
387 */
388 GeomMunger::Registry::
389 Registry() {
390 }
391
392 /**
393 * Adds the indicated munger to the registry, if there is not an equivalent
394 * munger already there; in either case, returns the pointer to the equivalent
395 * munger now in the registry.
396 *
397 * This must be called before a munger may be used in a Geom. After this
398 * call, you should discard the original pointer you passed in (which may or
399 * may not now be invalid) and let its reference count decrement normally; you
400 * should use only the returned value from this point on.
401 */
402 PT(GeomMunger) GeomMunger::Registry::
403 register_munger(GeomMunger *munger, Thread *current_thread) {
404 if (munger->is_registered()) {
405 return munger;
406 }
407
408 // Save the incoming pointer in a local PointerTo, so that if it has a zero
409 // reference count and is not added into the map below, it will be
410 // automatically deleted when this function returns.
411 PT(GeomMunger) pt_munger = munger;
412
413 LightReMutexHolder holder(_registry_lock);
414
415 Mungers::iterator mi = _mungers.insert(munger).first;
416 GeomMunger *new_munger = (*mi);
417 if (!new_munger->is_registered()) {
418 new_munger->_registered_key = mi;
419 new_munger->do_register(current_thread);
420 }
421
422 return new_munger;
423 }
424
425 /**
426 * Removes the indicated munger from the registry. Normally this should not
427 * be done until the munger is destructing.
428 */
429 void GeomMunger::Registry::
430 unregister_munger(GeomMunger *munger) {
431 LightReMutexHolder holder(_registry_lock);
432
433 nassertv(munger->is_registered());
434 nassertv(munger->_registered_key != _mungers.end());
435 _mungers.erase(munger->_registered_key);
436 munger->_registered_key = _mungers.end();
437 munger->do_unregister();
438 }
439
440 /**
441 * Removes all the mungers from the registry that are associated with the
442 * indicated GSG.
443 */
444 void GeomMunger::Registry::
445 unregister_mungers_for_gsg(GraphicsStateGuardianBase *gsg) {
446 LightReMutexHolder holder(_registry_lock);
447
448 Mungers::iterator mi = _mungers.begin();
449 while (mi != _mungers.end()) {
450 GeomMunger *munger = (*mi);
451 Mungers::iterator mnext = mi;
452 ++mnext;
453
454 if (munger->get_gsg() == gsg) {
455 nassertv(mi == munger->_registered_key);
456 _mungers.erase(mi);
457 munger->_registered_key = _mungers.end();
458 munger->do_unregister();
459 }
460
461 mi = mnext;
462 }
463 }
CycleDataReader
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
Definition: cycleDataReader.h:35
CycleDataWriter
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
Definition: cycleDataWriter.h:34
GeomMunger
Objects of this class are used to convert vertex data from a Geom into a format suitable for passing ...
Definition: geomMunger.h:50
GeomMunger::munge_geom
bool munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data, bool force, Thread *current_thread)
Applies the indicated munger to the geom and its data, and returns a (possibly different) geom and da...
Definition: geomMunger.cxx:95
GeomMunger::remove_data
void remove_data(const GeomVertexData *data)
Removes a prepared GeomVertexData from the cache.
Definition: geomMunger.cxx:76
GeomMunger::get_gsg
GraphicsStateGuardianBase * get_gsg() const
Returns a pointer to the GSG that created this munger.
Definition: geomMunger.I:18
GeomMunger::is_registered
bool is_registered() const
Returns true if this munger has been registered, false if it has not.
Definition: geomMunger.I:28
GeomVertexAnimationSpec
This object describes how the vertex animation, if any, represented in a GeomVertexData is encoded.
Definition: geomVertexAnimationSpec.h:38
GeomVertexData
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
Definition: geomVertexData.h:68
GeomVertexFormat
This class defines the physical layout of the vertex data stored within a Geom.
Definition: geomVertexFormat.h:55
GeomVertexFormat::is_registered
is_registered
Returns true if this format has been registered, false if it has not.
Definition: geomVertexFormat.h:68
Geom::CacheEntry
Definition: geom.h:269
Geom::CacheKey
Definition: geom.h:256
Geom
A container for geometry primitives.
Definition: geom.h:54
Geom::request_resident
bool request_resident() const
Returns true if all the primitive arrays are currently resident in memory.
Definition: geom.cxx:1003
GraphicsStateGuardianBase
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
Definition: graphicsStateGuardianBase.h:110
LightMutexDirect::release
void release() const
Releases the lightMutex.
Definition: lightMutexDirect.I:69
LightMutexDirect::acquire
void acquire() const
Grabs the lightMutex if it is available.
Definition: lightMutexDirect.I:56
LightMutexHolder
Similar to MutexHolder, but for a light mutex.
Definition: lightMutexHolder.h:25
LightReMutexHolder
Similar to MutexHolder, but for a light reentrant mutex.
Definition: lightReMutexHolder.h:25
PStatCollector
A lightweight class that represents a single element that may be timed and/or counted via stats.
Definition: pStatCollector.h:43
PStatTimer
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
Thread
A thread; that is, a lightweight process.
Definition: thread.h:46
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
geomCacheManager.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CPT
CPT(GeomVertexFormat) GeomMunger
The protected implementation of munge_format().
Definition: geomMunger.cxx:179
PT
PT(GeomMunger) GeomMunger
Adds the indicated munger to the registry, if there is not an equivalent munger already there; in eit...
Definition: geomMunger.cxx:402
geomMunger.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geom.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
lightMutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
lightReMutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pStatTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.