Panda3D
Loading...
Searching...
No Matches
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
21GeomMunger::Registry *GeomMunger::_registry = nullptr;
22TypeHandle GeomMunger::_type_handle;
23
24PStatCollector GeomMunger::_munge_pcollector("*:Munge");
25
26/**
27 *
28 */
29GeomMunger::
30GeomMunger(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 */
44GeomMunger::
45GeomMunger(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 */
58void GeomMunger::
59operator = (const GeomMunger &copy) {
60 nassertv(!_is_registered);
61}
62
63/**
64 *
65 */
66GeomMunger::
67~GeomMunger() {
68 unregister_myself();
69 nassertv(_formats_by_animation.empty());
70}
71
72/**
73 * Removes a prepared GeomVertexData from the cache.
74 */
76remove_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 */
95munge_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 */
179CPT(GeomVertexFormat) GeomMunger::
180do_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 */
211CPT(GeomVertexFormat) GeomMunger::
212munge_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 */
219CPT(GeomVertexData) GeomMunger::
220munge_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 */
238void GeomMunger::
239munge_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 */
248CPT(GeomVertexFormat) GeomMunger::
249do_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 */
277CPT(GeomVertexFormat) GeomMunger::
278premunge_format_impl(const GeomVertexFormat *orig) {
279 return orig;
280}
281
282/**
283 * Given a source GeomVertexData, converts it as necessary for rendering.
284 */
285CPT(GeomVertexData) GeomMunger::
286premunge_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 */
303void GeomMunger::
304premunge_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 */
314int GeomMunger::
315compare_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 */
325int GeomMunger::
326geom_compare_to_impl(const GeomMunger *other) const {
327 return 0;
328}
329
330/**
331 * Returns the global registry object.
332 */
333void GeomMunger::
334make_registry() {
335 if (_registry == nullptr) {
336 _registry = new Registry;
337 }
338}
339
340/**
341 * Called internally when the munger is registered.
342 */
343void GeomMunger::
344do_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 */
364void GeomMunger::
365do_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 */
380void GeomMunger::CacheEntry::
381output(std::ostream &out) const {
382 out << "munger " << _munger;
383}
384
385/**
386 *
387 */
388GeomMunger::Registry::
389Registry() {
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 */
402PT(GeomMunger) GeomMunger::Registry::
403register_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 */
429void GeomMunger::Registry::
430unregister_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 */
444void GeomMunger::Registry::
445unregister_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...
void remove_data(const GeomVertexData *data)
Removes a prepared GeomVertexData from the cache.
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.
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.