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  */
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  */
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 }
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
Objects of this class are used to convert vertex data from a Geom into a format suitable for passing ...
Definition: geomMunger.h:50
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
void remove_data(const GeomVertexData *data)
Removes a prepared GeomVertexData from the cache.
Definition: geomMunger.cxx:76
GraphicsStateGuardianBase * get_gsg() const
Returns a pointer to the GSG that created this munger.
Definition: geomMunger.I:18
bool is_registered() const
Returns true if this munger has been registered, false if it has not.
Definition: geomMunger.I:28
This object describes how the vertex animation, if any, represented in a GeomVertexData is encoded.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This class defines the physical layout of the vertex data stored within a Geom.
is_registered
Returns true if this format has been registered, false if it has not.
A container for geometry primitives.
Definition: geom.h:54
bool request_resident() const
Returns true if all the primitive arrays are currently resident in memory.
Definition: geom.cxx:1016
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
void release() const
Releases the lightMutex.
void acquire() const
Grabs the lightMutex if it is available.
Similar to MutexHolder, but for a light mutex.
Similar to MutexHolder, but for a light reentrant mutex.
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CPT(GeomVertexFormat) GeomMunger
The protected implementation of munge_format().
Definition: geomMunger.cxx:179
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.