Panda3D
Loading...
Searching...
No Matches
renderAttrib.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 renderAttrib.cxx
10 * @author drose
11 * @date 2002-02-21
12 */
13
14#include "renderAttrib.h"
15#include "bamReader.h"
16#include "indent.h"
17#include "config_pgraph.h"
18#include "lightReMutexHolder.h"
19#include "pStatTimer.h"
20
21using std::ostream;
22
23LightReMutex *RenderAttrib::_attribs_lock = nullptr;
24RenderAttrib::Attribs *RenderAttrib::_attribs = nullptr;
25TypeHandle RenderAttrib::_type_handle;
26
27size_t RenderAttrib::_garbage_index = 0;
28
29PStatCollector RenderAttrib::_garbage_collect_pcollector("*:State Cache:Garbage Collect");
30
31/**
32 *
33 */
34RenderAttrib::
35RenderAttrib() {
36 if (_attribs == nullptr) {
38 }
39 _saved_entry = -1;
40}
41
42/**
43 * The destructor is responsible for removing the RenderAttrib from the global
44 * set if it is there.
45 */
48 LightReMutexHolder holder(*_attribs_lock);
49
50 // unref() should have cleared this.
51 nassertv(_saved_entry == -1);
52}
53
54/**
55 * Intended to be overridden by derived RenderAttrib types to specify how two
56 * consecutive RenderAttrib objects of the same type interact.
57 *
58 * This should return false if a RenderAttrib on a higher node will compose
59 * into a RenderAttrib on a lower node that has a higher override value, or
60 * true if the lower RenderAttrib will completely replace the state.
61 *
62 * The default behavior is false: normally, a RenderAttrib in the graph cannot
63 * completely override a RenderAttrib above it, regardless of its override
64 * value--instead, the two attribs are composed. But for some kinds of
65 * RenderAttribs, it is useful to allow this kind of override.
66 *
67 * This method only handles the one special case of a lower RenderAttrib with
68 * a higher override value. If the higher RenderAttrib has a higher override
69 * value, it always completely overrides. And if both RenderAttribs have the
70 * same override value, they are always composed.
71 */
74 return false;
75}
76
77/**
78 * Should be overridden by derived classes to return true if cull_callback()
79 * has been defined. Otherwise, returns false to indicate cull_callback()
80 * does not need to be called for this node during the cull traversal.
81 */
83has_cull_callback() const {
84 return false;
85}
86
87/**
88 * If has_cull_callback() returns true, this function will be called during
89 * the cull traversal to perform any additional operations that should be
90 * performed at cull time.
91 *
92 * This is called each time the RenderAttrib is discovered applied to a Geom
93 * in the traversal. It should return true if the Geom is visible, false if
94 * it should be omitted.
95 */
98 return true;
99}
100
101/**
102 * This method overrides ReferenceCount::unref() to clear the pointer from the
103 * global object pool when its reference count goes to zero.
104 */
106unref() const {
107 if (!state_cache || garbage_collect_states) {
108 // If we're not using the cache at all, or if we're relying on garbage
109 // collection, just allow the pointer to unref normally.
110 return ReferenceCount::unref();
111 }
112
113 // Here is the normal refcounting case, with a normal cache, and without
114 // garbage collection in effect. In this case we will pull the object out
115 // of the cache when its reference count goes to 0.
116
117 // We always have to grab the lock, since we will definitely need to be
118 // holding it if we happen to drop the reference count to 0. Having to grab
119 // the lock at every call to unref() is a big limiting factor on
120 // parallelization.
121 LightReMutexHolder holder(*_attribs_lock);
122
123 if (ReferenceCount::unref()) {
124 // The reference count is still nonzero.
125 return true;
126 }
127
128 // The reference count has just reached zero. Make sure the object is
129 // removed from the global object pool, before anyone else finds it and
130 // tries to ref it.
131 ((RenderAttrib *)this)->release_new();
132
133 return false;
134}
135
136/**
137 *
138 */
139void RenderAttrib::
140output(ostream &out) const {
141 out << get_type();
142}
143
144/**
145 *
146 */
147void RenderAttrib::
148write(ostream &out, int indent_level) const {
149 indent(out, indent_level) << *this << "\n";
150}
151
152/**
153 * Returns the total number of unique RenderAttrib objects allocated in the
154 * world. This will go up and down during normal operations.
155 */
158 LightReMutexHolder holder(*_attribs_lock);
159
160 if (_attribs == nullptr) {
161 return 0;
162 }
163 return _attribs->get_num_entries();
164}
165
166/**
167 * Lists all of the RenderAttribs in the cache to the output stream, one per
168 * line. This can be quite a lot of output if the cache is large, so be
169 * prepared.
170 */
172list_attribs(ostream &out) {
173 LightReMutexHolder holder(*_attribs_lock);
174
175 size_t size = _attribs->get_num_entries();
176 out << size << " attribs:\n";
177 for (size_t si = 0; si < size; ++si) {
178 const RenderAttrib *attrib = _attribs->get_key(si);
179 attrib->write(out, 2);
180 }
181}
182
183/**
184 * Performs a garbage-collection cycle. This is called automatically from
185 * RenderState::garbage_collect(); see that method for more information.
186 */
189 if (_attribs == nullptr || !garbage_collect_states) {
190 return 0;
191 }
192 LightReMutexHolder holder(*_attribs_lock);
193
194 PStatTimer timer(_garbage_collect_pcollector);
195 size_t orig_size = _attribs->get_num_entries();
196
197#ifdef _DEBUG
198 nassertr(_attribs->validate(), 0);
199#endif
200
201 // How many elements to process this pass?
202 size_t size = orig_size;
203 size_t num_this_pass = std::max(0, int(size * garbage_collect_states_rate));
204 if (num_this_pass <= 0) {
205 return 0;
206 }
207
208 size_t si = _garbage_index;
209 if (si >= size) {
210 si = 0;
211 }
212
213 num_this_pass = std::min(num_this_pass, size);
214 size_t stop_at_element = (si + num_this_pass) % size;
215
216 do {
217 RenderAttrib *attrib = (RenderAttrib *)_attribs->get_key(si);
218 if (!attrib->unref_if_one()) {
219 // This attrib has recently been unreffed to 1 (the one we added when
220 // we stored it in the cache). Now it's time to delete it. This is
221 // safe, because we're holding the _attribs_lock, so it's not possible
222 // for some other thread to find the attrib in the cache and ref it
223 // while we're doing this. Also, we've just made sure to unref it to 0,
224 // to ensure that another thread can't get it via a weak pointer.
225 attrib->release_new();
226 delete attrib;
227
228 // When we removed it from the hash map, it swapped the last element
229 // with the one we just removed. So the current index contains one we
230 // still need to visit.
231 --size;
232 --si;
233 if (stop_at_element > 0) {
234 --stop_at_element;
235 }
236 }
237
238 si = (si + 1) % size;
239 } while (si != stop_at_element);
240 _garbage_index = si;
241
242 nassertr(_attribs->get_num_entries() == size, 0);
243
244#ifdef _DEBUG
245 nassertr(_attribs->validate(), 0);
246#endif
247
248 // If we just cleaned up a lot of attribs, see if we can reduce the table in
249 // size. This will help reduce iteration overhead in the future.
250 _attribs->consider_shrink_table();
251
252 return (int)orig_size - (int)size;
253}
254
255/**
256 * Ensures that the cache is still stored in sorted order. Returns true if
257 * so, false if there is a problem (which implies someone has modified one of
258 * the supposedly-const RenderAttrib objects).
259 */
262 LightReMutexHolder holder(*_attribs_lock);
263 if (_attribs->is_empty()) {
264 return true;
265 }
266
267 if (!_attribs->validate()) {
268 pgraph_cat.error()
269 << "RenderAttrib::_attribs cache is invalid!\n";
270
271 size_t size = _attribs->get_num_entries();
272 for (size_t si = 0; si < size; ++si) {
273 const RenderAttrib *attrib = _attribs->get_key(si);
274 //cerr << si << ": " << attrib << "\n";
275 attrib->write(std::cerr, 2);
276 }
277
278 return false;
279 }
280
281 size_t size = _attribs->get_num_entries();
282 size_t si = 0;
283 nassertr(si < size, false);
284 nassertr(_attribs->get_key(si)->get_ref_count() >= 0, false);
285 size_t snext = si;
286 ++snext;
287 while (snext < size) {
288 nassertr(_attribs->get_key(snext)->get_ref_count() >= 0, false);
289 const RenderAttrib *ssi = _attribs->get_key(si);
290 const RenderAttrib *ssnext = _attribs->get_key(snext);
291 int c = ssi->compare_to(*ssnext);
292 int ci = ssnext->compare_to(*ssi);
293 if ((ci < 0) != (c > 0) ||
294 (ci > 0) != (c < 0) ||
295 (ci == 0) != (c == 0)) {
296 pgraph_cat.error()
297 << "RenderAttrib::compare_to() not defined properly!\n";
298 pgraph_cat.error(false)
299 << "(a, b): " << c << "\n";
300 pgraph_cat.error(false)
301 << "(b, a): " << ci << "\n";
302 ssi->write(pgraph_cat.error(false), 2);
303 ssnext->write(pgraph_cat.error(false), 2);
304 return false;
305 }
306 si = snext;
307 ++snext;
308 }
309
310 return true;
311}
312
313/**
314 * This function is used by derived RenderAttrib types to share a common
315 * RenderAttrib pointer for all equivalent RenderAttrib objects.
316 *
317 * This is different from return_unique() in that it does not actually
318 * guarantee a unique pointer, unless uniquify-attribs is set.
319 */
320CPT(RenderAttrib) RenderAttrib::
321return_new(RenderAttrib *attrib) {
322 nassertr(attrib != nullptr, attrib);
323 if (!uniquify_attribs) {
324 attrib->calc_hash();
325 return attrib;
326 }
327
328 return return_unique(attrib);
329}
330
331/**
332 * This function is used by derived RenderAttrib types to share a common
333 * RenderAttrib pointer for all equivalent RenderAttrib objects.
334 *
335 * The make() function of the derived type should create a new RenderAttrib
336 * and pass it through return_new(), which will either save the pointer and
337 * return it unchanged (if this is the first similar such object) or delete it
338 * and return an equivalent pointer (if there was already a similar object
339 * saved).
340 */
341CPT(RenderAttrib) RenderAttrib::
342return_unique(RenderAttrib *attrib) {
343 nassertr(attrib != nullptr, attrib);
344
345 attrib->calc_hash();
346
347 if (!state_cache) {
348 return attrib;
349 }
350
351#ifndef NDEBUG
352 if (paranoid_const) {
353 nassertr(validate_attribs(), attrib);
354 }
355#endif
356
357 LightReMutexHolder holder(*_attribs_lock);
358
359 if (attrib->_saved_entry != -1) {
360 // This attrib is already in the cache. nassertr(_attribs->find(attrib)
361 // == attrib->_saved_entry, attrib);
362 return attrib;
363 }
364
365 int si = _attribs->find(attrib);
366 if (si != -1) {
367 // There's an equivalent attrib already in the set. Return it. If this
368 // is a newly created RenderAttrib, though, be sure to delete it.
369 if (attrib->get_ref_count() == 0) {
370 delete attrib;
371 }
372 return _attribs->get_key(si);
373 }
374
375 // Not already in the set; add it.
376 if (garbage_collect_states) {
377 // If we'll be garbage collecting attribs explicitly, we'll increment the
378 // reference count when we store it in the cache, so that it won't be
379 // deleted while it's in it.
380 attrib->ref();
381 }
382 si = _attribs->store(attrib, nullptr);
383
384 // Save the index and return the input attrib.
385 attrib->_saved_entry = si;
386 return attrib;
387}
388
389/**
390 * Intended to be overridden by derived RenderAttrib types to return a unique
391 * number indicating whether this RenderAttrib is equivalent to the other one.
392 *
393 * This should return 0 if the two RenderAttrib objects are equivalent, a
394 * number less than zero if this one should be sorted before the other one,
395 * and a number greater than zero otherwise.
396 *
397 * This will only be called with two RenderAttrib objects whose get_type()
398 * functions return the same.
399 */
400int RenderAttrib::
401compare_to_impl(const RenderAttrib *other) const {
402 return 0;
403}
404
405/**
406 * Intended to be overridden by derived RenderAttrib types to return a unique
407 * hash for these particular properties. RenderAttribs that compare the same
408 * with compare_to_impl(), above, should return the same hash; RenderAttribs
409 * that compare differently should return a different hash.
410 */
411size_t RenderAttrib::
412get_hash_impl() const {
413 return 0;
414}
415
416/**
417 * Intended to be overridden by derived RenderAttrib types to specify how two
418 * consecutive RenderAttrib objects of the same type interact.
419 *
420 * This should return the result of applying the other RenderAttrib to a node
421 * in the scene graph below this RenderAttrib, which was already applied. In
422 * most cases, the result is the same as the other RenderAttrib (that is, a
423 * subsequent RenderAttrib completely replaces the preceding one). On the
424 * other hand, some kinds of RenderAttrib (for instance, ColorTransformAttrib)
425 * might combine in meaningful ways.
426 */
427CPT(RenderAttrib) RenderAttrib::
428compose_impl(const RenderAttrib *other) const {
429 return other;
430}
431
432/**
433 * Intended to be overridden by derived RenderAttrib types to specify how two
434 * consecutive RenderAttrib objects of the same type interact.
435 *
436 * See invert_compose() and compose_impl().
437 */
438CPT(RenderAttrib) RenderAttrib::
439invert_compose_impl(const RenderAttrib *other) const {
440 return other;
441}
442
443/**
444 * Outputs a string representation of the given PandaCompareFunc object.
445 */
446void RenderAttrib::
447output_comparefunc(ostream &out, PandaCompareFunc fn) const {
448 switch (fn) {
449 case M_none:
450 out << "none";
451 break;
452
453 case M_never:
454 out << "never";
455 break;
456
457 case M_less:
458 out << "less";
459 break;
460
461 case M_equal:
462 out << "equal";
463 break;
464
465 case M_less_equal:
466 out << "less_equal";
467 break;
468
469 case M_greater:
470 out << "greater";
471 break;
472
473 case M_not_equal:
474 out << "not_equal";
475 break;
476
477 case M_greater_equal:
478 out << "greater_equal";
479 break;
480
481 case M_always:
482 out << "always";
483 break;
484 }
485}
486
487/**
488 * This inverse of return_new, this releases this object from the global
489 * RenderAttrib table.
490 *
491 * You must already be holding _attribs_lock before you call this method.
492 */
493void RenderAttrib::
494release_new() {
495 nassertv(_attribs_lock->debug_is_locked());
496
497 if (_saved_entry != -1) {
498 _saved_entry = -1;
499 nassertv_always(_attribs->remove(this));
500 }
501}
502
503/**
504 * Make sure the global _attribs map is allocated. This only has to be done
505 * once. We could make this map static, but then we run into problems if
506 * anyone creates a RenderAttrib object at static init time; it also seems to
507 * cause problems when the Panda shared library is unloaded at application
508 * exit time.
509 */
511init_attribs() {
512 _attribs = new Attribs;
513
514 // TODO: we should have a global Panda mutex to allow us to safely create
515 // _attribs_lock without a startup race condition. For the meantime, this
516 // is OK because we guarantee that this method is called at static init
517 // time, presumably when there is still only one thread in the world.
518 _attribs_lock = new LightReMutex("RenderAttrib::_attribs_lock");
520}
521
522/**
523 * Writes the contents of this object to the datagram for shipping out to a
524 * Bam file.
525 */
527write_datagram(BamWriter *manager, Datagram &dg) {
529}
530
531/**
532 * Called immediately after complete_pointers(), this gives the object a
533 * chance to adjust its own pointer if desired. Most objects don't change
534 * pointers after completion, but some need to.
535 *
536 * Once this function has been called, the old pointer will no longer be
537 * accessed.
538 */
540change_this(TypedWritable *old_ptr, BamReader *manager) {
541 // First, uniquify the pointer.
542 RenderAttrib *attrib = DCAST(RenderAttrib, old_ptr);
543 CPT(RenderAttrib) pointer = return_unique(attrib);
544
545 // But now we have a problem, since we have to hold the reference count and
546 // there's no way to return a TypedWritable while still holding the
547 // reference count! We work around this by explicitly upping the count, and
548 // also setting a finalize() callback to down it later.
549 if (pointer == attrib) {
550 pointer->ref();
551 manager->register_finalize(attrib);
552 }
553
554 // We have to cast the pointer back to non-const, because the bam reader
555 // expects that.
556 return (RenderAttrib *)pointer.p();
557}
558
559/**
560 * Called by the BamReader to perform any final actions needed for setting up
561 * the object after all objects have been read and all pointers have been
562 * completed.
563 */
566 // Unref the pointer that we explicitly reffed in change_this().
567 unref();
568
569 // We should never get back to zero after unreffing our own count, because
570 // we expect to have been stored in a pointer somewhere. If we do get to
571 // zero, it's a memory leak; the way to avoid this is to call unref_delete()
572 // above instead of unref(), but this is dangerous to do from within a
573 // virtual function.
574 nassertv(get_ref_count() != 0);
575}
576
577/**
578 * This internal function is called by make_from_bam to read in all of the
579 * relevant data from the BamFile for the new RenderAttrib.
580 */
581void RenderAttrib::
582fillin(DatagramIterator &scan, BamReader *manager) {
583 TypedWritable::fillin(scan, manager);
584 manager->register_change_this(change_this, this);
585}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition bamReader.h:110
void register_finalize(TypedWritable *whom)
Should be called by an object reading itself from the Bam file to indicate that this particular objec...
void register_change_this(ChangeThisFunc func, TypedWritable *whom)
Called by an object reading itself from the bam file to indicate that the object pointer that will be...
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition bamWriter.h:63
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
A class to retrieve the individual data elements previously stored in a Datagram.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition datagram.h:38
bool debug_is_locked() const
Returns true if the current thread has locked the LightReMutex, false otherwise.
Similar to MutexHolder, but for a light reentrant mutex.
A lightweight 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
void ref() const
Explicitly increments the reference count.
bool unref_if_one() const
Atomically decreases the reference count of this object if it is one.
get_ref_count
Returns the current reference count.
virtual bool unref() const
Explicitly decrements the reference count.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
static void init_attribs()
Make sure the global _attribs map is allocated.
static TypedWritable * change_this(TypedWritable *old_ptr, BamReader *manager)
Called immediately after complete_pointers(), this gives the object a chance to adjust its own pointe...
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
virtual bool cull_callback(CullTraverser *trav, const CullTraverserData &data) const
If has_cull_callback() returns true, this function will be called during the cull traversal to perfor...
virtual ~RenderAttrib()
The destructor is responsible for removing the RenderAttrib from the global set if it is there.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
virtual bool unref() const final
This method overrides ReferenceCount::unref() to clear the pointer from the global object pool when i...
virtual bool has_cull_callback() const
Should be overridden by derived classes to return true if cull_callback() has been defined.
static int garbage_collect()
Performs a garbage-collection cycle.
static void list_attribs(std::ostream &out)
Lists all of the RenderAttribs in the cache to the output stream, one per line.
virtual bool lower_attrib_can_override() const
Intended to be overridden by derived RenderAttrib types to specify how two consecutive RenderAttrib o...
static bool validate_attribs()
Ensures that the cache is still stored in sorted order.
static int get_num_attribs()
Returns the total number of unique RenderAttrib objects allocated in the world.
This template class implements an unordered map of keys to data, implemented as a hashtable.
const Key & get_key(size_t n) const
Returns the key in the nth entry of the table.
int store(const Key &key, const Value &data)
Records the indicated key/data pair in the map.
bool validate() const
Returns true if the internal table appears to be consistent, false if there are some internal errors.
int find(const Key &key) const
Searches for the indicated key in the table.
bool remove(const Key &key)
Removes the indicated key and its associated data from the table.
bool consider_shrink_table()
Shrinks the table if the allocated storage is significantly larger than the number of elements in it.
bool is_empty() const
Returns true if the table is empty; i.e.
size_t get_num_entries() const
Returns the number of active entries in the table.
get_main_thread
Returns a pointer to the "main" Thread object–this is the Thread that started the whole process.
Definition thread.h:107
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition thread.h:109
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
virtual void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is intended to be called by each class's make_from_bam() method to read in all...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition indent.cxx:20
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.