Panda3D
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 
21 using std::ostream;
22 
23 LightReMutex *RenderAttrib::_attribs_lock = nullptr;
24 RenderAttrib::Attribs *RenderAttrib::_attribs = nullptr;
25 TypeHandle RenderAttrib::_type_handle;
26 
27 size_t RenderAttrib::_garbage_index = 0;
28 
29 PStatCollector RenderAttrib::_garbage_collect_pcollector("*:State Cache:Garbage Collect");
30 
31 /**
32  *
33  */
34 RenderAttrib::
35 RenderAttrib() {
36  if (_attribs == nullptr) {
37  init_attribs();
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  */
47 ~RenderAttrib() {
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  */
83 has_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  */
106 unref() 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  */
139 void RenderAttrib::
140 output(ostream &out) const {
141  out << get_type();
142 }
143 
144 /**
145  *
146  */
147 void RenderAttrib::
148 write(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  */
157 get_num_attribs() {
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  */
172 list_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  */
188 garbage_collect() {
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  */
320 CPT(RenderAttrib) RenderAttrib::
321 return_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  */
341 CPT(RenderAttrib) RenderAttrib::
342 return_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  */
400 int RenderAttrib::
401 compare_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  */
411 size_t RenderAttrib::
412 get_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  */
427 CPT(RenderAttrib) RenderAttrib::
428 compose_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  */
438 CPT(RenderAttrib) RenderAttrib::
439 invert_compose_impl(const RenderAttrib *other) const {
440  return other;
441 }
442 
443 /**
444  * Outputs a string representation of the given PandaCompareFunc object.
445  */
446 void RenderAttrib::
447 output_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  */
493 void RenderAttrib::
494 release_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  */
511 init_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  */
527 write_datagram(BamWriter *manager, Datagram &dg) {
528  TypedWritable::write_datagram(manager, 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  */
540 change_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  */
565 finalize(BamReader *) {
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  */
581 void RenderAttrib::
582 fillin(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...
Definition: bamReader.cxx:808
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...
Definition: bamReader.cxx:835
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,...
Definition: cullTraverser.h:45
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.
Definition: lightReMutex.h:32
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...
Definition: renderAttrib.h:51
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.
Definition: simpleHashMap.h:81
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.
Definition: typedWritable.h:35
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.
CPT(RenderAttrib) RenderAttrib
This function is used by derived RenderAttrib types to share a common RenderAttrib pointer for all eq...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.