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::
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::
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 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CPT(GeomVertexFormat) GeomMunger
The protected implementation of munge_format().
Definition: geomMunger.cxx:179
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:76
Objects of this class are used to convert vertex data from a Geom into a format suitable for passing ...
Definition: geomMunger.h:50
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_registered() const
Returns true if this munger has been registered, false if it has not.
Definition: geomMunger.I:28
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
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
is_registered
Returns true if this format has been registered, false if it has not.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A lightweight class that represents a single element that may be timed and/or counted via stats.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Similar to MutexHolder, but for a light mutex.
void acquire() const
Grabs the lightMutex if it is available.
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:54
void release() const
Releases the lightMutex.
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
Similar to MutexHolder, but for a light reentrant mutex.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class defines the physical layout of the vertex data stored within a Geom.
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:46
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
GraphicsStateGuardianBase * get_gsg() const
Returns a pointer to the GSG that created this munger.
Definition: geomMunger.I:18
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
bool request_resident() const
Returns true if all the primitive arrays are currently resident in memory.
Definition: geom.cxx:1003