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