Panda3D

renderState.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 renderState.cxx
10 * @author drose
11 * @date 2002-02-21
12 */
13
14 #include "renderState.h"
15 #include "transparencyAttrib.h"
16 #include "cullBinAttrib.h"
17 #include "cullBinManager.h"
18 #include "fogAttrib.h"
19 #include "clipPlaneAttrib.h"
20 #include "scissorAttrib.h"
21 #include "transparencyAttrib.h"
22 #include "colorAttrib.h"
23 #include "colorScaleAttrib.h"
24 #include "textureAttrib.h"
25 #include "texGenAttrib.h"
26 #include "shaderAttrib.h"
27 #include "pStatTimer.h"
28 #include "config_pgraph.h"
29 #include "bamReader.h"
30 #include "bamWriter.h"
31 #include "datagramIterator.h"
32 #include "indent.h"
33 #include "compareTo.h"
34 #include "lightReMutexHolder.h"
35 #include "lightMutexHolder.h"
36 #include "thread.h"
37 #include "renderAttribRegistry.h"
38
39 using std::ostream;
40
41 LightReMutex *RenderState::_states_lock = nullptr;
42 RenderState::States *RenderState::_states = nullptr;
43 const RenderState *RenderState::_empty_state = nullptr;
44 UpdateSeq RenderState::_last_cycle_detect;
45 size_t RenderState::_garbage_index = 0;
46
47 PStatCollector RenderState::_cache_update_pcollector("*:State Cache:Update");
48 PStatCollector RenderState::_garbage_collect_pcollector("*:State Cache:Garbage Collect");
49 PStatCollector RenderState::_state_compose_pcollector("*:State Cache:Compose State");
50 PStatCollector RenderState::_state_invert_pcollector("*:State Cache:Invert State");
51 PStatCollector RenderState::_node_counter("RenderStates:On nodes");
52 PStatCollector RenderState::_cache_counter("RenderStates:Cached");
53 PStatCollector RenderState::_state_break_cycles_pcollector("*:State Cache:Break Cycles");
54 PStatCollector RenderState::_state_validate_pcollector("*:State Cache:Validate");
55
56 CacheStats RenderState::_cache_stats;
57
58 TypeHandle RenderState::_type_handle;
59
60
61 /**
62 * Actually, this could be a private constructor, since no one inherits from
63 * RenderState, but gcc gives us a spurious warning if all constructors are
64 * private.
65 */
66 RenderState::
67 RenderState() :
68 _flags(0),
69 _lock("RenderState")
70 {
71 if (_states == nullptr) {
72 init_states();
73 }
74 _saved_entry = -1;
75 _last_mi = -1;
76 _cache_stats.add_num_states(1);
77 _read_overrides = nullptr;
78 _generated_shader = nullptr;
79
80 #ifdef DO_MEMORY_USAGE
81 MemoryUsage::update_type(this, this);
82 #endif
83 }
84
85 /**
86 * RenderStates are only meant to be copied internally.
87 */
88 RenderState::
89 RenderState(const RenderState &copy) :
90 _filled_slots(copy._filled_slots),
91 _flags(0),
92 _lock("RenderState")
93 {
94 // Copy over the attributes.
95 for (int i = 0; i < RenderAttribRegistry::_max_slots; ++i) {
96 _attributes[i] = copy._attributes[i];
97 }
98
99 _saved_entry = -1;
100 _last_mi = -1;
101 _cache_stats.add_num_states(1);
102 _read_overrides = nullptr;
103 _generated_shader = nullptr;
104
105 #ifdef DO_MEMORY_USAGE
106 MemoryUsage::update_type(this, this);
107 #endif
108 }
109
110 /**
111 * The destructor is responsible for removing the RenderState from the global
112 * set if it is there.
113 */
114 RenderState::
115 ~RenderState() {
116 // We'd better not call the destructor twice on a particular object.
117 nassertv(!is_destructing());
118 set_destructing();
119
120 LightReMutexHolder holder(*_states_lock);
121
122 // unref() should have cleared these.
123 nassertv(_saved_entry == -1);
124 nassertv(_composition_cache.is_empty() && _invert_composition_cache.is_empty());
125
126 // If this was true at the beginning of the destructor, but is no longer
127 // true now, probably we've been double-deleted.
128 nassertv(get_ref_count() == 0);
129 _cache_stats.add_num_states(-1);
130 }
131
132 /**
133 * Provides an arbitrary ordering among all unique RenderStates, so we can
134 * store the essentially different ones in a big set and throw away the rest.
135 *
136 * This method is not needed outside of the RenderState class because all
137 * equivalent RenderState objects are guaranteed to share the same pointer;
138 * thus, a pointer comparison is always sufficient.
139 */
140 int RenderState::
141 compare_to(const RenderState &other) const {
142 SlotMask mask = _filled_slots | other._filled_slots;
143 int slot = mask.get_lowest_on_bit();
144 while (slot >= 0) {
145 int result = _attributes[slot].compare_to(other._attributes[slot]);
146 if (result != 0) {
147 return result;
148 }
149 mask.clear_bit(slot);
150 slot = mask.get_lowest_on_bit();
151 }
152
153 return 0;
154 }
155
156 /**
157 * Returns -1, 0, or 1 according to the relative sorting of these two
158 * RenderStates, with regards to rendering performance, so that "heavier"
159 * RenderAttribs (as defined by RenderAttribRegistry::get_slot_sort()) are
160 * more likely to be grouped together. This is not related to the sorting
161 * order defined by compare_to.
162 */
163 int RenderState::
164 compare_sort(const RenderState &other) const {
165 if (this == &other) {
166 // Trivial case.
167 return 0;
168 }
169
170 RenderAttribRegistry *reg = RenderAttribRegistry::quick_get_global_ptr();
171 int num_sorted_slots = reg->get_num_sorted_slots();
172 for (int n = 0; n < num_sorted_slots; ++n) {
173 int slot = reg->get_sorted_slot(n);
174 nassertr((_attributes[slot]._attrib != nullptr) == _filled_slots.get_bit(slot), 0);
175
176 const RenderAttrib *a = _attributes[slot]._attrib;
177 const RenderAttrib *b = other._attributes[slot]._attrib;
178 if (a != b) {
179 return a < b ? -1 : 1;
180 }
181 }
182
183 return 0;
184 }
185
186 /**
187 * This version of compare_to takes a slot mask that indicates which
188 * attributes to include in the comparison. Unlike compare_to, this method
189 * compares the attributes by pointer.
190 */
191 int RenderState::
192 compare_mask(const RenderState &other, SlotMask compare_mask) const {
193 SlotMask mask = (_filled_slots | other._filled_slots) & compare_mask;
194 int slot = mask.get_lowest_on_bit();
195 while (slot >= 0) {
196 const RenderAttrib *a = _attributes[slot]._attrib;
197 const RenderAttrib *b = other._attributes[slot]._attrib;
198 if (a != b) {
199 return a < b ? -1 : 1;
200 }
201 mask.clear_bit(slot);
202 slot = mask.get_lowest_on_bit();
203 }
204
205 return 0;
206 }
207
208 /**
209 * Calls cull_callback() on each attrib. If any attrib returns false,
210 * interrupts the list and returns false immediately; otherwise, completes the
211 * list and returns true.
212 */
213 bool RenderState::
214 cull_callback(CullTraverser *trav, const CullTraverserData &data) const {
215 SlotMask mask = _filled_slots;
216 int slot = mask.get_lowest_on_bit();
217 while (slot >= 0) {
218 const Attribute &attrib = _attributes[slot];
219 nassertr(attrib._attrib != nullptr, false);
220 if (!attrib._attrib->cull_callback(trav, data)) {
221 return false;
222 }
223
224 mask.clear_bit(slot);
225 slot = mask.get_lowest_on_bit();
226 }
227
228 return true;
229 }
230
231 /**
232 * Returns a RenderState with one attribute set.
233 */
234 CPT(RenderState) RenderState::
235 make(const RenderAttrib *attrib, int override) {
236 RenderState *state = new RenderState;
237 int slot = attrib->get_slot();
238 state->_attributes[slot].set(attrib, override);
239 state->_filled_slots.set_bit(slot);
240 return return_new(state);
241 }
242
243 /**
244 * Returns a RenderState with two attributes set.
245 */
246 CPT(RenderState) RenderState::
247 make(const RenderAttrib *attrib1,
248 const RenderAttrib *attrib2, int override) {
249 RenderState *state = new RenderState;
250 state->_attributes[attrib1->get_slot()].set(attrib1, override);
251 state->_attributes[attrib2->get_slot()].set(attrib2, override);
252 state->_filled_slots.set_bit(attrib1->get_slot());
253 state->_filled_slots.set_bit(attrib2->get_slot());
254 return return_new(state);
255 }
256
257 /**
258 * Returns a RenderState with three attributes set.
259 */
260 CPT(RenderState) RenderState::
261 make(const RenderAttrib *attrib1,
262 const RenderAttrib *attrib2,
263 const RenderAttrib *attrib3, int override) {
264 RenderState *state = new RenderState;
265 state->_attributes[attrib1->get_slot()].set(attrib1, override);
266 state->_attributes[attrib2->get_slot()].set(attrib2, override);
267 state->_attributes[attrib3->get_slot()].set(attrib3, override);
268 state->_filled_slots.set_bit(attrib1->get_slot());
269 state->_filled_slots.set_bit(attrib2->get_slot());
270 state->_filled_slots.set_bit(attrib3->get_slot());
271 return return_new(state);
272 }
273
274 /**
275 * Returns a RenderState with four attributes set.
276 */
277 CPT(RenderState) RenderState::
278 make(const RenderAttrib *attrib1,
279 const RenderAttrib *attrib2,
280 const RenderAttrib *attrib3,
281 const RenderAttrib *attrib4, int override) {
282 RenderState *state = new RenderState;
283 state->_attributes[attrib1->get_slot()].set(attrib1, override);
284 state->_attributes[attrib2->get_slot()].set(attrib2, override);
285 state->_attributes[attrib3->get_slot()].set(attrib3, override);
286 state->_attributes[attrib4->get_slot()].set(attrib4, override);
287 state->_filled_slots.set_bit(attrib1->get_slot());
288 state->_filled_slots.set_bit(attrib2->get_slot());
289 state->_filled_slots.set_bit(attrib3->get_slot());
290 state->_filled_slots.set_bit(attrib4->get_slot());
291 return return_new(state);
292 }
293
294 /**
295 * Returns a RenderState with five attributes set.
296 */
297 CPT(RenderState) RenderState::
298 make(const RenderAttrib *attrib1,
299 const RenderAttrib *attrib2,
300 const RenderAttrib *attrib3,
301 const RenderAttrib *attrib4,
302 const RenderAttrib *attrib5, int override) {
303 RenderState *state = new RenderState;
304 state->_attributes[attrib1->get_slot()].set(attrib1, override);
305 state->_attributes[attrib2->get_slot()].set(attrib2, override);
306 state->_attributes[attrib3->get_slot()].set(attrib3, override);
307 state->_attributes[attrib4->get_slot()].set(attrib4, override);
308 state->_attributes[attrib5->get_slot()].set(attrib5, override);
309 state->_filled_slots.set_bit(attrib1->get_slot());
310 state->_filled_slots.set_bit(attrib2->get_slot());
311 state->_filled_slots.set_bit(attrib3->get_slot());
312 state->_filled_slots.set_bit(attrib4->get_slot());
313 state->_filled_slots.set_bit(attrib5->get_slot());
314 return return_new(state);
315 }
316
317 /**
318 * Returns a RenderState with n attributes set.
319 */
320 CPT(RenderState) RenderState::
321 make(const RenderAttrib * const *attrib, int num_attribs, int override) {
322 if (num_attribs == 0) {
323 return _empty_state;
324 }
325 RenderState *state = new RenderState;
326 for (int i = 0; i < num_attribs; i++) {
327 int slot = attrib[i]->get_slot();
328 state->_attributes[slot].set(attrib[i], override);
329 state->_filled_slots.set_bit(slot);
330 }
331 return return_new(state);
332 }
333
334 /**
335 * Returns a new RenderState object that represents the composition of this
336 * state with the other state.
337 *
338 * The result of this operation is cached, and will be retained as long as
339 * both this RenderState object and the other RenderState object continue to
340 * exist. Should one of them destruct, the cached entry will be removed, and
341 * its pointer will be allowed to destruct as well.
342 */
343 CPT(RenderState) RenderState::
344 compose(const RenderState *other) const {
345 // This method isn't strictly const, because it updates the cache, but we
346 // pretend that it is because it's only a cache which is transparent to the
347 // rest of the interface.
348
349 // We handle empty state (identity) as a trivial special case.
350 if (is_empty()) {
351 return other;
352 }
353 if (other->is_empty()) {
354 return this;
355 }
356
357 if (!state_cache) {
358 return do_compose(other);
359 }
360
361 LightReMutexHolder holder(*_states_lock);
362
363 // Is this composition already cached?
364 int index = _composition_cache.find(other);
365 if (index != -1) {
366 Composition &comp = ((RenderState *)this)->_composition_cache.modify_data(index);
367 if (comp._result == nullptr) {
368 // Well, it wasn't cached already, but we already had an entry (probably
369 // created for the reverse direction), so use the same entry to store
370 // the new result.
371 CPT(RenderState) result = do_compose(other);
372 comp._result = result;
373
374 if (result != (const RenderState *)this) {
375 // See the comments below about the need to up the reference count
376 // only when the result is not the same as this.
377 result->cache_ref();
378 }
379 }
380 // Here's the cache!
381 _cache_stats.inc_hits();
382 return comp._result;
383 }
384 _cache_stats.inc_misses();
385
386 // We need to make a new cache entry, both in this object and in the other
387 // object. We make both records so the other RenderState object will know
388 // to delete the entry from this object when it destructs, and vice-versa.
389
390 // The cache entry in this object is the only one that indicates the result;
391 // the other will be NULL for now.
392 CPT(RenderState) result = do_compose(other);
393
394 _cache_stats.add_total_size(1);
395 _cache_stats.inc_adds(_composition_cache.is_empty());
396
397 ((RenderState *)this)->_composition_cache[other]._result = result;
398
399 if (other != this) {
400 _cache_stats.add_total_size(1);
401 _cache_stats.inc_adds(other->_composition_cache.is_empty());
402 ((RenderState *)other)->_composition_cache[this]._result = nullptr;
403 }
404
405 if (result != (const RenderState *)this) {
406 // If the result of compose() is something other than this, explicitly
407 // increment the reference count. We have to be sure to decrement it
408 // again later, when the composition entry is removed from the cache.
409 result->cache_ref();
410
411 // (If the result was just this again, we still store the result, but we
412 // don't increment the reference count, since that would be a self-
413 // referential leak.)
414 }
415
416 _cache_stats.maybe_report("RenderState");
417
418 return result;
419 }
420
421 /**
422 * Returns a new RenderState object that represents the composition of this
423 * state's inverse with the other state.
424 *
425 * This is similar to compose(), but is particularly useful for computing the
426 * relative state of a node as viewed from some other node.
427 */
428 CPT(RenderState) RenderState::
429 invert_compose(const RenderState *other) const {
430 // This method isn't strictly const, because it updates the cache, but we
431 // pretend that it is because it's only a cache which is transparent to the
432 // rest of the interface.
433
434 // We handle empty state (identity) as a trivial special case.
435 if (is_empty()) {
436 return other;
437 }
438 // Unlike compose(), the case of other->is_empty() is not quite as trivial
439 // for invert_compose().
440
441 if (other == this) {
442 // a->invert_compose(a) always produces identity.
443 return _empty_state;
444 }
445
446 if (!state_cache) {
447 return do_invert_compose(other);
448 }
449
450 LightReMutexHolder holder(*_states_lock);
451
452 // Is this composition already cached?
453 int index = _invert_composition_cache.find(other);
454 if (index != -1) {
455 Composition &comp = ((RenderState *)this)->_invert_composition_cache.modify_data(index);
456 if (comp._result == nullptr) {
457 // Well, it wasn't cached already, but we already had an entry (probably
458 // created for the reverse direction), so use the same entry to store
459 // the new result.
460 CPT(RenderState) result = do_invert_compose(other);
461 comp._result = result;
462
463 if (result != (const RenderState *)this) {
464 // See the comments below about the need to up the reference count
465 // only when the result is not the same as this.
466 result->cache_ref();
467 }
468 }
469 // Here's the cache!
470 _cache_stats.inc_hits();
471 return comp._result;
472 }
473 _cache_stats.inc_misses();
474
475 // We need to make a new cache entry, both in this object and in the other
476 // object. We make both records so the other RenderState object will know
477 // to delete the entry from this object when it destructs, and vice-versa.
478
479 // The cache entry in this object is the only one that indicates the result;
480 // the other will be NULL for now.
481 CPT(RenderState) result = do_invert_compose(other);
482
483 _cache_stats.add_total_size(1);
484 _cache_stats.inc_adds(_invert_composition_cache.is_empty());
485 ((RenderState *)this)->_invert_composition_cache[other]._result = result;
486
487 if (other != this) {
488 _cache_stats.add_total_size(1);
489 _cache_stats.inc_adds(other->_invert_composition_cache.is_empty());
490 ((RenderState *)other)->_invert_composition_cache[this]._result = nullptr;
491 }
492
493 if (result != (const RenderState *)this) {
494 // If the result of compose() is something other than this, explicitly
495 // increment the reference count. We have to be sure to decrement it
496 // again later, when the composition entry is removed from the cache.
497 result->cache_ref();
498
499 // (If the result was just this again, we still store the result, but we
500 // don't increment the reference count, since that would be a self-
501 // referential leak.)
502 }
503
504 return result;
505 }
506
507 /**
508 * Returns a new RenderState object that represents the same as the source
509 * state, with the new RenderAttrib added. If there is already a RenderAttrib
510 * with the same type, it is replaced (unless the override is lower).
511 */
512 CPT(RenderState) RenderState::
513 add_attrib(const RenderAttrib *attrib, int override) const {
514 int slot = attrib->get_slot();
515 if (_filled_slots.get_bit(slot) &&
516 _attributes[slot]._override > override) {
517 // The existing attribute overrides.
518 return this;
519 }
520
521 // The new attribute replaces.
522 RenderState *new_state = new RenderState(*this);
523 new_state->_attributes[slot].set(attrib, override);
524 new_state->_filled_slots.set_bit(slot);
525 return return_new(new_state);
526 }
527
528 /**
529 * Returns a new RenderState object that represents the same as the source
530 * state, with the new RenderAttrib added. If there is already a RenderAttrib
531 * with the same type, it is replaced unconditionally. The override is not
532 * changed.
533 */
534 CPT(RenderState) RenderState::
535 set_attrib(const RenderAttrib *attrib) const {
536 RenderState *new_state = new RenderState(*this);
537 int slot = attrib->get_slot();
538 new_state->_attributes[slot]._attrib = attrib;
539 new_state->_filled_slots.set_bit(slot);
540 return return_new(new_state);
541 }
542
543 /**
544 * Returns a new RenderState object that represents the same as the source
545 * state, with the new RenderAttrib added. If there is already a RenderAttrib
546 * with the same type, it is replaced unconditionally. The override is also
547 * replaced unconditionally.
548 */
549 CPT(RenderState) RenderState::
550 set_attrib(const RenderAttrib *attrib, int override) const {
551 RenderState *new_state = new RenderState(*this);
552 int slot = attrib->get_slot();
553 new_state->_attributes[slot].set(attrib, override);
554 new_state->_filled_slots.set_bit(slot);
555 return return_new(new_state);
556 }
557
558 /**
559 * Returns a new RenderState object that represents the same as the source
560 * state, with the indicated RenderAttrib removed.
561 */
562 CPT(RenderState) RenderState::
563 remove_attrib(int slot) const {
564 if (_attributes[slot]._attrib == nullptr) {
565 // Already removed.
566 return this;
567 }
568
569 // Will this bring us down to the empty state?
570 if (_filled_slots.get_num_on_bits() == 1) {
571 return _empty_state;
572 }
573
574 RenderState *new_state = new RenderState(*this);
575 new_state->_attributes[slot].set(nullptr, 0);
576 new_state->_filled_slots.clear_bit(slot);
577 return return_new(new_state);
578 }
579
580 /**
581 * Returns a new RenderState object that represents the same as the source
582 * state, with all attributes' override values incremented (or decremented, if
583 * negative) by the indicated amount. If the override would drop below zero,
584 * it is set to zero.
585 */
586 CPT(RenderState) RenderState::
587 adjust_all_priorities(int adjustment) const {
588 RenderState *new_state = new RenderState(*this);
589
590 SlotMask mask = _filled_slots;
591 int slot = mask.get_lowest_on_bit();
592 while (slot >= 0) {
593 Attribute &attrib = new_state->_attributes[slot];
594 nassertr(attrib._attrib != nullptr, this);
595 attrib._override = std::max(attrib._override + adjustment, 0);
596
597 mask.clear_bit(slot);
598 slot = mask.get_lowest_on_bit();
599 }
600
601 return return_new(new_state);
602 }
603
604 /**
605 * This method overrides ReferenceCount::unref() to check whether the
606 * remaining reference count is entirely in the cache, and if so, it checks
607 * for and breaks a cycle in the cache involving this object. This is
608 * designed to prevent leaks from cyclical references within the cache.
609 */
610 bool RenderState::
611 unref() const {
612 if (garbage_collect_states || !state_cache) {
613 // If we're not using the cache at all, or if we're relying on garbage
614 // collection, just allow the pointer to unref normally.
615 return ReferenceCount::unref();
616 }
617
618 // Here is the normal refcounting case, with a normal cache, and without
619 // garbage collection in effect. In this case we will pull the object out
620 // of the cache when its reference count goes to 0.
621
622 // We always have to grab the lock, since we will definitely need to be
623 // holding it if we happen to drop the reference count to 0. Having to grab
624 // the lock at every call to unref() is a big limiting factor on
625 // parallelization.
626 LightReMutexHolder holder(*_states_lock);
627
628 if (auto_break_cycles && uniquify_states) {
629 if (get_cache_ref_count() > 0 &&
630 get_ref_count() == get_cache_ref_count() + 1) {
631 // If we are about to remove the one reference that is not in the cache,
632 // leaving only references in the cache, then we need to check for a
633 // cycle involving this RenderState and break it if it exists.
634 ((RenderState *)this)->detect_and_break_cycles();
635 }
636 }
637
638 if (ReferenceCount::unref()) {
639 // The reference count is still nonzero.
640 return true;
641 }
642
643 // The reference count has just reached zero. Make sure the object is
644 // removed from the global object pool, before anyone else finds it and
645 // tries to ref it.
646 ((RenderState *)this)->release_new();
647 ((RenderState *)this)->remove_cache_pointers();
648
649 return false;
650 }
651
652 /**
653 *
654 */
655 void RenderState::
656 output(ostream &out) const {
657 out << "S:";
658 if (is_empty()) {
659 out << "(empty)";
660
661 } else {
662 out << "(";
663 const char *sep = "";
664
665 SlotMask mask = _filled_slots;
666 int slot = mask.get_lowest_on_bit();
667 while (slot >= 0) {
668 const Attribute &attrib = _attributes[slot];
669 nassertv(attrib._attrib != nullptr);
670 out << sep << attrib._attrib->get_type();
671 sep = " ";
672
673 mask.clear_bit(slot);
674 slot = mask.get_lowest_on_bit();
675 }
676 out << ")";
677 }
678 }
679
680 /**
681 *
682 */
683 void RenderState::
684 write(ostream &out, int indent_level) const {
685 if (is_empty()) {
686 indent(out, indent_level)
687 << "(empty)\n";
688 }
689
690 SlotMask mask = _filled_slots;
691 int slot = mask.get_lowest_on_bit();
692 while (slot >= 0) {
693 const Attribute &attrib = _attributes[slot];
694 nassertv(attrib._attrib != nullptr);
695 attrib._attrib->write(out, indent_level);
696
697 mask.clear_bit(slot);
698 slot = mask.get_lowest_on_bit();
699 }
700 }
701
702 /**
703 * Returns the maximum priority number (sometimes called override) that may be
704 * set on any node. This may or may not be enforced, but the scene graph code
705 * assumes that no priority numbers will be larger than this, and some effects
706 * may not work properly if you use a larger number.
707 */
708 int RenderState::
709 get_max_priority() {
710 return 1000000000;
711 }
712
713 /**
714 * Returns the total number of unique RenderState objects allocated in the
715 * world. This will go up and down during normal operations.
716 */
717 int RenderState::
718 get_num_states() {
719 if (_states == nullptr) {
720 return 0;
721 }
722 LightReMutexHolder holder(*_states_lock);
723 return _states->get_num_entries();
724 }
725
726 /**
727 * Returns the total number of RenderState objects that have been allocated
728 * but have no references outside of the internal RenderState cache.
729 *
730 * A nonzero return value is not necessarily indicative of leaked references;
731 * it is normal for two RenderState objects, both of which have references
732 * held outside the cache, to have to result of their composition stored
733 * within the cache. This result will be retained within the cache until one
734 * of the base RenderStates is released.
735 *
736 * Use list_cycles() to get an idea of the number of actual "leaked"
737 * RenderState objects.
738 */
739 int RenderState::
740 get_num_unused_states() {
741 if (_states == nullptr) {
742 return 0;
743 }
744 LightReMutexHolder holder(*_states_lock);
745
746 // First, we need to count the number of times each RenderState object is
747 // recorded in the cache.
748 typedef pmap<const RenderState *, int> StateCount;
749 StateCount state_count;
750
751 size_t size = _states->get_num_entries();
752 for (size_t si = 0; si < size; ++si) {
753 const RenderState *state = _states->get_key(si);
754
755 size_t i;
756 size_t cache_size = state->_composition_cache.get_num_entries();
757 for (i = 0; i < cache_size; ++i) {
758 const RenderState *result = state->_composition_cache.get_data(i)._result;
759 if (result != nullptr && result != state) {
760 // Here's a RenderState that's recorded in the cache. Count it.
761 std::pair<StateCount::iterator, bool> ir =
762 state_count.insert(StateCount::value_type(result, 1));
763 if (!ir.second) {
764 // If the above insert operation fails, then it's already in the
765 // cache; increment its value.
766 (*(ir.first)).second++;
767 }
768 }
769 }
770 cache_size = state->_invert_composition_cache.get_num_entries();
771 for (i = 0; i < cache_size; ++i) {
772 const RenderState *result = state->_invert_composition_cache.get_data(i)._result;
773 if (result != nullptr && result != state) {
774 std::pair<StateCount::iterator, bool> ir =
775 state_count.insert(StateCount::value_type(result, 1));
776 if (!ir.second) {
777 (*(ir.first)).second++;
778 }
779 }
780 }
781 }
782
783 // Now that we have the appearance count of each RenderState object, we can
784 // tell which ones are unreferenced outside of the RenderState cache, by
785 // comparing these to the reference counts.
786 int num_unused = 0;
787
788 StateCount::iterator sci;
789 for (sci = state_count.begin(); sci != state_count.end(); ++sci) {
790 const RenderState *state = (*sci).first;
791 int count = (*sci).second;
792 nassertr(count == state->get_cache_ref_count(), num_unused);
793 nassertr(count <= state->get_ref_count(), num_unused);
794 if (count == state->get_ref_count()) {
795 num_unused++;
796
797 if (pgraph_cat.is_debug()) {
798 pgraph_cat.debug()
799 << "Unused state: " << (void *)state << ":"
800 << state->get_ref_count() << " =\n";
801 state->write(pgraph_cat.debug(false), 2);
802 }
803 }
804 }
805
806 return num_unused;
807 }
808
809 /**
810 * Empties the cache of composed RenderStates. This makes every RenderState
811 * forget what results when it is composed with other RenderStates.
812 *
813 * This will eliminate any RenderState objects that have been allocated but
814 * have no references outside of the internal RenderState map. It will not
815 * eliminate RenderState objects that are still in use.
816 *
817 * Nowadays, this method should not be necessary, as reference-count cycles in
818 * the composition cache should be automatically detected and broken.
819 *
820 * The return value is the number of RenderStates freed by this operation.
821 */
822 int RenderState::
823 clear_cache() {
824 if (_states == nullptr) {
825 return 0;
826 }
827 LightReMutexHolder holder(*_states_lock);
828
829 PStatTimer timer(_cache_update_pcollector);
830 int orig_size = _states->get_num_entries();
831
832 // First, we need to copy the entire set of states to a temporary vector,
833 // reference-counting each object. That way we can walk through the copy,
834 // without fear of dereferencing (and deleting) the objects in the map as we
835 // go.
836 {
837 typedef pvector< CPT(RenderState) > TempStates;
838 TempStates temp_states;
839 temp_states.reserve(orig_size);
840
841 size_t size = _states->get_num_entries();
842 for (size_t si = 0; si < size; ++si) {
843 const RenderState *state = _states->get_key(si);
844 temp_states.push_back(state);
845 }
846
847 // Now it's safe to walk through the list, destroying the cache within
848 // each object as we go. Nothing will be destructed till we're done.
849 TempStates::iterator ti;
850 for (ti = temp_states.begin(); ti != temp_states.end(); ++ti) {
851 RenderState *state = (RenderState *)(*ti).p();
852
853 size_t i;
854 size_t cache_size = (int)state->_composition_cache.get_num_entries();
855 for (i = 0; i < cache_size; ++i) {
856 const RenderState *result = state->_composition_cache.get_data(i)._result;
857 if (result != nullptr && result != state) {
858 result->cache_unref();
859 nassertr(result->get_ref_count() > 0, 0);
860 }
861 }
862 _cache_stats.add_total_size(-(int)state->_composition_cache.get_num_entries());
863 state->_composition_cache.clear();
864
865 cache_size = (int)state->_invert_composition_cache.get_num_entries();
866 for (i = 0; i < cache_size; ++i) {
867 const RenderState *result = state->_invert_composition_cache.get_data(i)._result;
868 if (result != nullptr && result != state) {
869 result->cache_unref();
870 nassertr(result->get_ref_count() > 0, 0);
871 }
872 }
873 _cache_stats.add_total_size(-(int)state->_invert_composition_cache.get_num_entries());
874 state->_invert_composition_cache.clear();
875 }
876
877 // Once this block closes and the temp_states object goes away, all the
878 // destruction will begin. Anything whose reference was held only within
879 // the various objects' caches will go away.
880 }
881
882 int new_size = _states->get_num_entries();
883 return orig_size - new_size;
884 }
885
886 /**
887 * Performs a garbage-collection cycle. This must be called periodically if
888 * garbage-collect-states is true to ensure that RenderStates get cleaned up
889 * appropriately. It does no harm to call it even if this variable is not
890 * true, but there is probably no advantage in that case.
891 *
892 * This automatically calls RenderAttrib::garbage_collect() as well.
893 */
894 int RenderState::
895 garbage_collect() {
896 int num_attribs = RenderAttrib::garbage_collect();
897
898 if (_states == nullptr || !garbage_collect_states) {
899 return num_attribs;
900 }
901
902 LightReMutexHolder holder(*_states_lock);
903
904 PStatTimer timer(_garbage_collect_pcollector);
905 size_t orig_size = _states->get_num_entries();
906
907 // How many elements to process this pass?
908 size_t size = orig_size;
909 size_t num_this_pass = std::max(0, int(size * garbage_collect_states_rate));
910 if (num_this_pass <= 0) {
911 return num_attribs;
912 }
913
914 bool break_and_uniquify = (auto_break_cycles && uniquify_transforms);
915
916 size_t si = _garbage_index;
917 if (si >= size) {
918 si = 0;
919 }
920
921 num_this_pass = std::min(num_this_pass, size);
922 size_t stop_at_element = (si + num_this_pass) % size;
923
924 do {
925 RenderState *state = (RenderState *)_states->get_key(si);
926 if (break_and_uniquify) {
927 if (state->get_cache_ref_count() > 0 &&
928 state->get_ref_count() == state->get_cache_ref_count()) {
929 // If we have removed all the references to this state not in the
930 // cache, leaving only references in the cache, then we need to
931 // check for a cycle involving this RenderState and break it if it
932 // exists.
933 state->detect_and_break_cycles();
934 }
935 }
936
937 if (!state->unref_if_one()) {
938 // This state has recently been unreffed to 1 (the one we added when
939 // we stored it in the cache). Now it's time to delete it. This is
940 // safe, because we're holding the _states_lock, so it's not possible
941 // for some other thread to find the state in the cache and ref it
942 // while we're doing this. Also, we've just made sure to unref it to 0,
943 // to ensure that another thread can't get it via a weak pointer.
944
945 state->release_new();
946 state->remove_cache_pointers();
947 state->cache_unref_only();
948 delete state;
949
950 // When we removed it from the hash map, it swapped the last element
951 // with the one we just removed. So the current index contains one we
952 // still need to visit.
953 --size;
954 --si;
955 if (stop_at_element > 0) {
956 --stop_at_element;
957 }
958 }
959
960 si = (si + 1) % size;
961 } while (si != stop_at_element);
962 _garbage_index = si;
963
964 nassertr(_states->get_num_entries() == size, 0);
965
966 #ifdef _DEBUG
967 nassertr(_states->validate(), 0);
968 #endif
969
970 // If we just cleaned up a lot of states, see if we can reduce the table in
971 // size. This will help reduce iteration overhead in the future.
972 _states->consider_shrink_table();
973
974 return (int)orig_size - (int)size + num_attribs;
975 }
976
977 /**
978 * Completely empties the cache of state + gsg -> munger, for all states and
979 * all gsg's. Normally there is no need to empty this cache.
980 */
981 void RenderState::
982 clear_munger_cache() {
983 LightReMutexHolder holder(*_states_lock);
984
985 size_t size = _states->get_num_entries();
986 for (size_t si = 0; si < size; ++si) {
987 RenderState *state = (RenderState *)(_states->get_key(si));
988 state->_mungers.clear();
989 state->_munged_states.clear();
990 state->_last_mi = -1;
991 }
992 }
993
994 /**
995 * Detects all of the reference-count cycles in the cache and reports them to
996 * standard output.
997 *
998 * These cycles may be inadvertently created when state compositions cycle
999 * back to a starting point. Nowadays, these cycles should be automatically
1000 * detected and broken, so this method should never list any cycles unless
1001 * there is a bug in that detection logic.
1002 *
1003 * The cycles listed here are not leaks in the strictest sense of the word,
1004 * since they can be reclaimed by a call to clear_cache(); but they will not
1005 * be reclaimed automatically.
1006 */
1007 void RenderState::
1008 list_cycles(ostream &out) {
1009 if (_states == nullptr) {
1010 return;
1011 }
1012 LightReMutexHolder holder(*_states_lock);
1013
1014 typedef pset<const RenderState *> VisitedStates;
1015 VisitedStates visited;
1016 CompositionCycleDesc cycle_desc;
1017
1018 size_t size = _states->get_num_entries();
1019 for (size_t si = 0; si < size; ++si) {
1020 const RenderState *state = _states->get_key(si);
1021
1022 bool inserted = visited.insert(state).second;
1023 if (inserted) {
1024 ++_last_cycle_detect;
1025 if (r_detect_cycles(state, state, 1, _last_cycle_detect, &cycle_desc)) {
1026 // This state begins a cycle.
1027 CompositionCycleDesc::reverse_iterator csi;
1028
1029 out << "\nCycle detected of length " << cycle_desc.size() + 1 << ":\n"
1030 << "state " << (void *)state << ":" << state->get_ref_count()
1031 << " =\n";
1032 state->write(out, 2);
1033 for (csi = cycle_desc.rbegin(); csi != cycle_desc.rend(); ++csi) {
1034 const CompositionCycleDescEntry &entry = (*csi);
1035 if (entry._inverted) {
1036 out << "invert composed with ";
1037 } else {
1038 out << "composed with ";
1039 }
1040 out << (const void *)entry._obj << ":" << entry._obj->get_ref_count()
1041 << " " << *entry._obj << "\n"
1042 << "produces " << (const void *)entry._result << ":"
1043 << entry._result->get_ref_count() << " =\n";
1044 entry._result->write(out, 2);
1045 visited.insert(entry._result);
1046 }
1047
1048 cycle_desc.clear();
1049 } else {
1050 ++_last_cycle_detect;
1051 if (r_detect_reverse_cycles(state, state, 1, _last_cycle_detect, &cycle_desc)) {
1052 // This state begins a cycle.
1053 CompositionCycleDesc::iterator csi;
1054
1055 out << "\nReverse cycle detected of length " << cycle_desc.size() + 1 << ":\n"
1056 << "state ";
1057 for (csi = cycle_desc.begin(); csi != cycle_desc.end(); ++csi) {
1058 const CompositionCycleDescEntry &entry = (*csi);
1059 out << (const void *)entry._result << ":"
1060 << entry._result->get_ref_count() << " =\n";
1061 entry._result->write(out, 2);
1062 out << (const void *)entry._obj << ":"
1063 << entry._obj->get_ref_count() << " =\n";
1064 entry._obj->write(out, 2);
1065 visited.insert(entry._result);
1066 }
1067 out << (void *)state << ":"
1068 << state->get_ref_count() << " =\n";
1069 state->write(out, 2);
1070
1071 cycle_desc.clear();
1072 }
1073 }
1074 }
1075 }
1076 }
1077
1078
1079 /**
1080 * Lists all of the RenderStates in the cache to the output stream, one per
1081 * line. This can be quite a lot of output if the cache is large, so be
1082 * prepared.
1083 */
1084 void RenderState::
1085 list_states(ostream &out) {
1086 if (_states == nullptr) {
1087 out << "0 states:\n";
1088 return;
1089 }
1090 LightReMutexHolder holder(*_states_lock);
1091
1092 size_t size = _states->get_num_entries();
1093 out << size << " states:\n";
1094 for (size_t si = 0; si < size; ++si) {
1095 const RenderState *state = _states->get_key(si);
1096 state->write(out, 2);
1097 }
1098 }
1099
1100 /**
1101 * Ensures that the cache is still stored in sorted order, and that none of
1102 * the cache elements have been inadvertently deleted. Returns true if so,
1103 * false if there is a problem (which implies someone has modified one of the
1104 * supposedly-const RenderState objects).
1105 */
1106 bool RenderState::
1107 validate_states() {
1108 if (_states == nullptr) {
1109 return true;
1110 }
1111
1112 PStatTimer timer(_state_validate_pcollector);
1113
1114 LightReMutexHolder holder(*_states_lock);
1115 if (_states->is_empty()) {
1116 return true;
1117 }
1118
1119 if (!_states->validate()) {
1120 pgraph_cat.error()
1121 << "RenderState::_states cache is invalid!\n";
1122 return false;
1123 }
1124
1125 size_t size = _states->get_num_entries();
1126 size_t si = 0;
1127 nassertr(si < size, false);
1128 nassertr(_states->get_key(si)->get_ref_count() >= 0, false);
1129 size_t snext = si;
1130 ++snext;
1131 while (snext < size) {
1132 nassertr(_states->get_key(snext)->get_ref_count() >= 0, false);
1133 const RenderState *ssi = _states->get_key(si);
1134 const RenderState *ssnext = _states->get_key(snext);
1135 int c = ssi->compare_to(*ssnext);
1136 int ci = ssnext->compare_to(*ssi);
1137 if ((ci < 0) != (c > 0) ||
1138 (ci > 0) != (c < 0) ||
1139 (ci == 0) != (c == 0)) {
1140 pgraph_cat.error()
1141 << "RenderState::compare_to() not defined properly!\n";
1142 pgraph_cat.error(false)
1143 << "(a, b): " << c << "\n";
1144 pgraph_cat.error(false)
1145 << "(b, a): " << ci << "\n";
1146 ssi->write(pgraph_cat.error(false), 2);
1147 ssnext->write(pgraph_cat.error(false), 2);
1148 return false;
1149 }
1150 si = snext;
1151 ++snext;
1152 }
1153
1154 return true;
1155 }
1156
1157 /**
1158 * Returns the union of the Geom::GeomRendering bits that will be required
1159 * once this RenderState is applied to a geom which includes the indicated
1160 * geom_rendering bits.
1161 */
1162 int RenderState::
1163 get_geom_rendering(int geom_rendering) const {
1164 const RenderModeAttrib *render_mode;
1165 const TexGenAttrib *tex_gen;
1166 const TexMatrixAttrib *tex_matrix;
1167
1168 if (get_attrib(render_mode)) {
1169 geom_rendering = render_mode->get_geom_rendering(geom_rendering);
1170 }
1171 if (get_attrib(tex_gen)) {
1172 geom_rendering = tex_gen->get_geom_rendering(geom_rendering);
1173 }
1174 if (get_attrib(tex_matrix)) {
1175 geom_rendering = tex_matrix->get_geom_rendering(geom_rendering);
1176 }
1177
1178 return geom_rendering;
1179 }
1180
1181 /**
1182 * Intended to be called by CullBinManager::remove_bin(), this informs all the
1183 * RenderStates in the world to remove the indicated bin_index from their
1184 * cache if it has been cached.
1185 */
1186 void RenderState::
1187 bin_removed(int bin_index) {
1188 // Do something here.
1189 nassertv(false);
1190 }
1191
1192 /**
1193 * Returns true if the _filled_slots bitmask is consistent with the table of
1194 * RenderAttrib pointers, false otherwise.
1195 */
1196 bool RenderState::
1197 validate_filled_slots() const {
1198 SlotMask mask;
1199
1200 RenderAttribRegistry *reg = RenderAttribRegistry::quick_get_global_ptr();
1201 int max_slots = reg->get_max_slots();
1202 for (int slot = 1; slot < max_slots; ++slot) {
1203 const Attribute &attribute = _attributes[slot];
1204 if (attribute._attrib != nullptr) {
1205 mask.set_bit(slot);
1206 }
1207 }
1208
1209 return (mask == _filled_slots);
1210 }
1211
1212 /**
1213 * Computes a suitable hash value for phash_map.
1214 */
1215 void RenderState::
1216 do_calc_hash() {
1217 _hash = 0;
1218
1219 SlotMask mask = _filled_slots;
1220 int slot = mask.get_lowest_on_bit();
1221 while (slot >= 0) {
1222 const Attribute &attrib = _attributes[slot];
1223 nassertv(attrib._attrib != nullptr);
1224 _hash = pointer_hash::add_hash(_hash, attrib._attrib);
1225 _hash = int_hash::add_hash(_hash, attrib._override);
1226
1227 mask.clear_bit(slot);
1228 slot = mask.get_lowest_on_bit();
1229 }
1230
1231 _flags |= F_hash_known;
1232 }
1233
1234 /**
1235 * This function is used to share a common RenderState pointer for all
1236 * equivalent RenderState objects.
1237 *
1238 * This is different from return_unique() in that it does not actually
1239 * guarantee a unique pointer, unless uniquify-states is set.
1240 */
1241 CPT(RenderState) RenderState::
1242 return_new(RenderState *state) {
1243 nassertr(state != nullptr, state);
1244
1245 // Make sure we don't have anything in the 0 slot. If we did, that would
1246 // indicate an uninitialized slot number.
1247 #ifndef NDEBUG
1248 if (state->_attributes[0]._attrib != nullptr) {
1249 const RenderAttrib *attrib = state->_attributes[0]._attrib;
1250 if (attrib->get_type() == TypeHandle::none()) {
1251 ((RenderAttrib *)attrib)->force_init_type();
1252 pgraph_cat->error()
1253 << "Uninitialized RenderAttrib type: " << attrib->get_type()
1254 << "\n";
1255
1256 } else {
1257 static pset<TypeHandle> already_reported;
1258 if (already_reported.insert(attrib->get_type()).second) {
1259 pgraph_cat->error()
1260 << attrib->get_type() << " did not initialize its slot number.\n";
1261 }
1262 }
1263 }
1264 #endif
1265 state->_attributes[0]._attrib = nullptr;
1266 state->_filled_slots.clear_bit(0);
1267
1268 #ifndef NDEBUG
1269 nassertr(state->validate_filled_slots(), state);
1270 #endif
1271
1272 if (!uniquify_states && !state->is_empty()) {
1273 return state;
1274 }
1275
1276 return return_unique(state);
1277 }
1278
1279 /**
1280 * This function is used to share a common RenderState pointer for all
1281 * equivalent RenderState objects.
1282 *
1283 * See the similar logic in RenderAttrib. The idea is to create a new
1284 * RenderState object and pass it through this function, which will share the
1285 * pointer with a previously-created RenderState object if it is equivalent.
1286 */
1287 CPT(RenderState) RenderState::
1288 return_unique(RenderState *state) {
1289 nassertr(state != nullptr, nullptr);
1290
1291 if (!state_cache) {
1292 return state;
1293 }
1294
1295 #ifndef NDEBUG
1296 if (paranoid_const) {
1297 nassertr(validate_states(), state);
1298 }
1299 #endif
1300
1301 LightReMutexHolder holder(*_states_lock);
1302
1303 if (state->_saved_entry != -1) {
1304 // This state is already in the cache. nassertr(_states->find(state) ==
1305 // state->_saved_entry, pt_state);
1306 return state;
1307 }
1308
1309 // Ensure each of the individual attrib pointers has been uniquified before
1310 // we add the state to the cache.
1311 if (!uniquify_attribs && !state->is_empty()) {
1312 SlotMask mask = state->_filled_slots;
1313 int slot = mask.get_lowest_on_bit();
1314 while (slot >= 0) {
1315 Attribute &attrib = state->_attributes[slot];
1316 nassertd(attrib._attrib != nullptr) continue;
1317 attrib._attrib = attrib._attrib->get_unique();
1318 mask.clear_bit(slot);
1319 slot = mask.get_lowest_on_bit();
1320 }
1321 }
1322
1323 int si = _states->find(state);
1324 if (si != -1) {
1325 // There's an equivalent state already in the set. Return it. The state
1326 // that was passed may be newly created and therefore may not be
1327 // automatically deleted. Do that if necessary.
1328 if (state->get_ref_count() == 0) {
1329 delete state;
1330 }
1331 return _states->get_key(si);
1332 }
1333
1334 // Not already in the set; add it.
1335 if (garbage_collect_states) {
1336 // If we'll be garbage collecting states explicitly, we'll increment the
1337 // reference count when we store it in the cache, so that it won't be
1338 // deleted while it's in it.
1339 state->cache_ref();
1340 }
1341 si = _states->store(state, nullptr);
1342
1343 // Save the index and return the input state.
1344 state->_saved_entry = si;
1345 return state;
1346 }
1347
1348 /**
1349 * The private implemention of compose(); this actually composes two
1350 * RenderStates, without bothering with the cache.
1351 */
1352 CPT(RenderState) RenderState::
1353 do_compose(const RenderState *other) const {
1354 PStatTimer timer(_state_compose_pcollector);
1355
1356 RenderState *new_state = new RenderState;
1357
1358 SlotMask mask = _filled_slots | other->_filled_slots;
1359 new_state->_filled_slots = mask;
1360
1361 int slot = mask.get_lowest_on_bit();
1362 while (slot >= 0) {
1363 const Attribute &a = _attributes[slot];
1364 const Attribute &b = other->_attributes[slot];
1365 Attribute &result = new_state->_attributes[slot];
1366
1367 if (a._attrib == nullptr) {
1368 nassertr(b._attrib != nullptr, this);
1369 // B wins.
1370 result = b;
1371
1372 } else if (b._attrib == nullptr) {
1373 // A wins.
1374 result = a;
1375
1376 } else if (b._override < a._override) {
1377 // A, the higher RenderAttrib, overrides.
1378 result = a;
1379
1380 } else if (a._override < b._override &&
1381 a._attrib->lower_attrib_can_override()) {
1382 // B, the higher RenderAttrib, overrides. This is a special case;
1383 // normally, a lower RenderAttrib does not override a higher one, even
1384 // if it has a higher override value. But certain kinds of
1385 // RenderAttribs redefine lower_attrib_can_override() to return true,
1386 // allowing this override.
1387 result = b;
1388
1389 } else {
1390 // Either they have the same override value, or B is higher. In either
1391 // case, the result is the composition of the two, with B's override
1392 // value.
1393 result.set(a._attrib->compose(b._attrib), b._override);
1394 }
1395
1396 mask.clear_bit(slot);
1397 slot = mask.get_lowest_on_bit();
1398 }
1399
1400 return return_new(new_state);
1401 }
1402
1403 /**
1404 * The private implemention of invert_compose().
1405 */
1406 CPT(RenderState) RenderState::
1407 do_invert_compose(const RenderState *other) const {
1408 PStatTimer timer(_state_invert_pcollector);
1409
1410 RenderState *new_state = new RenderState;
1411
1412 SlotMask mask = _filled_slots | other->_filled_slots;
1413 new_state->_filled_slots = mask;
1414
1415 int slot = mask.get_lowest_on_bit();
1416 while (slot >= 0) {
1417 const Attribute &a = _attributes[slot];
1418 const Attribute &b = other->_attributes[slot];
1419 Attribute &result = new_state->_attributes[slot];
1420
1421 if (a._attrib == nullptr) {
1422 nassertr(b._attrib != nullptr, this);
1423 // B wins.
1424 result = b;
1425
1426 } else if (b._attrib == nullptr) {
1427 // A wins. Invert it.
1428 RenderAttribRegistry *reg = RenderAttribRegistry::quick_get_global_ptr();
1429 result.set(a._attrib->invert_compose(reg->get_slot_default(slot)), 0);
1430
1431 } else {
1432 // Both are good. (Overrides are not used in invert_compose.) Compose.
1433 result.set(a._attrib->invert_compose(b._attrib), 0);
1434 }
1435
1436 mask.clear_bit(slot);
1437 slot = mask.get_lowest_on_bit();
1438 }
1439 return return_new(new_state);
1440 }
1441
1442 /**
1443 * Detects whether there is a cycle in the cache that begins with this state.
1444 * If any are detected, breaks them by removing this state from the cache.
1445 */
1446 void RenderState::
1447 detect_and_break_cycles() {
1448 PStatTimer timer(_state_break_cycles_pcollector);
1449
1450 ++_last_cycle_detect;
1451 if (r_detect_cycles(this, this, 1, _last_cycle_detect, nullptr)) {
1452 // Ok, we have a cycle. This will be a leak unless we break the cycle by
1453 // freeing the cache on this object.
1454 if (pgraph_cat.is_debug()) {
1455 pgraph_cat.debug()
1456 << "Breaking cycle involving " << (*this) << "\n";
1457 }
1458
1459 ((RenderState *)this)->remove_cache_pointers();
1460 } else {
1461 ++_last_cycle_detect;
1462 if (r_detect_reverse_cycles(this, this, 1, _last_cycle_detect, nullptr)) {
1463 if (pgraph_cat.is_debug()) {
1464 pgraph_cat.debug()
1465 << "Breaking cycle involving " << (*this) << "\n";
1466 }
1467
1468 ((RenderState *)this)->remove_cache_pointers();
1469 }
1470 }
1471 }
1472
1473 /**
1474 * Detects whether there is a cycle in the cache that begins with the
1475 * indicated state. Returns true if at least one cycle is found, false if
1476 * this state is not part of any cycles. If a cycle is found and cycle_desc
1477 * is not NULL, then cycle_desc is filled in with the list of the steps of the
1478 * cycle, in reverse order.
1479 */
1480 bool RenderState::
1481 r_detect_cycles(const RenderState *start_state,
1482 const RenderState *current_state,
1483 int length, UpdateSeq this_seq,
1484 RenderState::CompositionCycleDesc *cycle_desc) {
1485 if (current_state->_cycle_detect == this_seq) {
1486 // We've already seen this state; therefore, we've found a cycle.
1487
1488 // However, we only care about cycles that return to the starting state
1489 // and involve more than two steps. If only one or two nodes are
1490 // involved, it doesn't represent a memory leak, so no problem there.
1491 return (current_state == start_state && length > 2);
1492 }
1493 ((RenderState *)current_state)->_cycle_detect = this_seq;
1494
1495 size_t i;
1496 size_t cache_size = current_state->_composition_cache.get_num_entries();
1497 for (i = 0; i < cache_size; ++i) {
1498 const RenderState *result = current_state->_composition_cache.get_data(i)._result;
1499 if (result != nullptr) {
1500 if (r_detect_cycles(start_state, result, length + 1,
1501 this_seq, cycle_desc)) {
1502 // Cycle detected.
1503 if (cycle_desc != nullptr) {
1504 const RenderState *other = current_state->_composition_cache.get_key(i);
1505 CompositionCycleDescEntry entry(other, result, false);
1506 cycle_desc->push_back(entry);
1507 }
1508 return true;
1509 }
1510 }
1511 }
1512
1513 cache_size = current_state->_invert_composition_cache.get_num_entries();
1514 for (i = 0; i < cache_size; ++i) {
1515 const RenderState *result = current_state->_invert_composition_cache.get_data(i)._result;
1516 if (result != nullptr) {
1517 if (r_detect_cycles(start_state, result, length + 1,
1518 this_seq, cycle_desc)) {
1519 // Cycle detected.
1520 if (cycle_desc != nullptr) {
1521 const RenderState *other = current_state->_invert_composition_cache.get_key(i);
1522 CompositionCycleDescEntry entry(other, result, true);
1523 cycle_desc->push_back(entry);
1524 }
1525 return true;
1526 }
1527 }
1528 }
1529
1530 // No cycle detected.
1531 return false;
1532 }
1533
1534 /**
1535 * Works the same as r_detect_cycles, but checks for cycles in the reverse
1536 * direction along the cache chain. (A cycle may appear in either direction,
1537 * and we must check both.)
1538 */
1539 bool RenderState::
1540 r_detect_reverse_cycles(const RenderState *start_state,
1541 const RenderState *current_state,
1542 int length, UpdateSeq this_seq,
1543 RenderState::CompositionCycleDesc *cycle_desc) {
1544 if (current_state->_cycle_detect == this_seq) {
1545 // We've already seen this state; therefore, we've found a cycle.
1546
1547 // However, we only care about cycles that return to the starting state
1548 // and involve more than two steps. If only one or two nodes are
1549 // involved, it doesn't represent a memory leak, so no problem there.
1550 return (current_state == start_state && length > 2);
1551 }
1552 ((RenderState *)current_state)->_cycle_detect = this_seq;
1553
1554 size_t i;
1555 size_t cache_size = current_state->_composition_cache.get_num_entries();
1556 for (i = 0; i < cache_size; ++i) {
1557 const RenderState *other = current_state->_composition_cache.get_key(i);
1558 if (other != current_state) {
1559 int oi = other->_composition_cache.find(current_state);
1560 nassertr(oi != -1, false);
1561
1562 const RenderState *result = other->_composition_cache.get_data(oi)._result;
1563 if (result != nullptr) {
1564 if (r_detect_reverse_cycles(start_state, result, length + 1,
1565 this_seq, cycle_desc)) {
1566 // Cycle detected.
1567 if (cycle_desc != nullptr) {
1568 const RenderState *other = current_state->_composition_cache.get_key(i);
1569 CompositionCycleDescEntry entry(other, result, false);
1570 cycle_desc->push_back(entry);
1571 }
1572 return true;
1573 }
1574 }
1575 }
1576 }
1577
1578 cache_size = current_state->_invert_composition_cache.get_num_entries();
1579 for (i = 0; i < cache_size; ++i) {
1580 const RenderState *other = current_state->_invert_composition_cache.get_key(i);
1581 if (other != current_state) {
1582 int oi = other->_invert_composition_cache.find(current_state);
1583 nassertr(oi != -1, false);
1584
1585 const RenderState *result = other->_invert_composition_cache.get_data(oi)._result;
1586 if (result != nullptr) {
1587 if (r_detect_reverse_cycles(start_state, result, length + 1,
1588 this_seq, cycle_desc)) {
1589 // Cycle detected.
1590 if (cycle_desc != nullptr) {
1591 const RenderState *other = current_state->_invert_composition_cache.get_key(i);
1592 CompositionCycleDescEntry entry(other, result, false);
1593 cycle_desc->push_back(entry);
1594 }
1595 return true;
1596 }
1597 }
1598 }
1599 }
1600
1601 // No cycle detected.
1602 return false;
1603 }
1604
1605 /**
1606 * This inverse of return_new, this releases this object from the global
1607 * RenderState table.
1608 *
1609 * You must already be holding _states_lock before you call this method.
1610 */
1611 void RenderState::
1612 release_new() {
1613 nassertv(_states_lock->debug_is_locked());
1614
1615 if (_saved_entry != -1) {
1616 _saved_entry = -1;
1617 nassertv_always(_states->remove(this));
1618 }
1619 }
1620
1621 /**
1622 * Remove all pointers within the cache from and to this particular
1623 * RenderState. The pointers to this object may be scattered around in the
1624 * various CompositionCaches from other RenderState objects.
1625 *
1626 * You must already be holding _states_lock before you call this method.
1627 */
1628 void RenderState::
1629 remove_cache_pointers() {
1630 nassertv(_states_lock->debug_is_locked());
1631
1632 // Fortunately, since we added CompositionCache records in pairs, we know
1633 // exactly the set of RenderState objects that have us in their cache: it's
1634 // the same set of RenderState objects that we have in our own cache.
1635
1636 /*
1637 * We do need to put considerable thought into this loop, because as we clear
1638 * out cache entries we'll cause other RenderState objects to destruct, which
1639 * could cause things to get pulled out of our own _composition_cache map. We
1640 * want to allow this (so that we don't encounter any just-destructed pointers
1641 * in our cache), but we don't want to get bitten by this cascading effect.
1642 * Instead of walking through the map from beginning to end, therefore, we
1643 * just pull out the first one each time, and erase it.
1644 */
1645
1646 #ifdef DO_PSTATS
1647 if (_composition_cache.is_empty() && _invert_composition_cache.is_empty()) {
1648 return;
1649 }
1650 PStatTimer timer(_cache_update_pcollector);
1651 #endif // DO_PSTATS
1652
1653 // There are lots of ways to do this loop wrong. Be very careful if you
1654 // need to modify it for any reason.
1655 size_t i = 0;
1656 while (!_composition_cache.is_empty()) {
1657 // It is possible that the "other" RenderState object is currently within
1658 // its own destructor. We therefore can't use a PT() to hold its pointer;
1659 // that could end up calling its destructor twice. Fortunately, we don't
1660 // need to hold its reference count to ensure it doesn't destruct while we
1661 // process this loop; as long as we ensure that no *other* RenderState
1662 // objects destruct, there will be no reason for that one to.
1663 RenderState *other = (RenderState *)_composition_cache.get_key(i);
1664
1665 // We hold a copy of the composition result so we can dereference it
1666 // later.
1667 Composition comp = _composition_cache.get_data(i);
1668
1669 // Now we can remove the element from our cache. We do this now, rather
1670 // than later, before any other RenderState objects have had a chance to
1671 // destruct, so we are confident that our iterator is still valid.
1672 _composition_cache.remove_element(i);
1673 _cache_stats.add_total_size(-1);
1674 _cache_stats.inc_dels();
1675
1676 if (other != this) {
1677 int oi = other->_composition_cache.find(this);
1678
1679 // We may or may not still be listed in the other's cache (it might be
1680 // halfway through pulling entries out, from within its own destructor).
1681 if (oi != -1) {
1682 // Hold a copy of the other composition result, too.
1683 Composition ocomp = other->_composition_cache.get_data(oi);
1684
1685 other->_composition_cache.remove_element(oi);
1686 _cache_stats.add_total_size(-1);
1687 _cache_stats.inc_dels();
1688
1689 // It's finally safe to let our held pointers go away. This may have
1690 // cascading effects as other RenderState objects are destructed, but
1691 // there will be no harm done if they destruct now.
1692 if (ocomp._result != nullptr && ocomp._result != other) {
1693 cache_unref_delete(ocomp._result);
1694 }
1695 }
1696 }
1697
1698 // It's finally safe to let our held pointers go away. (See comment
1699 // above.)
1700 if (comp._result != nullptr && comp._result != this) {
1701 cache_unref_delete(comp._result);
1702 }
1703 }
1704
1705 // A similar bit of code for the invert cache.
1706 i = 0;
1707 while (!_invert_composition_cache.is_empty()) {
1708 RenderState *other = (RenderState *)_invert_composition_cache.get_key(i);
1709 nassertv(other != this);
1710 Composition comp = _invert_composition_cache.get_data(i);
1711 _invert_composition_cache.remove_element(i);
1712 _cache_stats.add_total_size(-1);
1713 _cache_stats.inc_dels();
1714 if (other != this) {
1715 int oi = other->_invert_composition_cache.find(this);
1716 if (oi != -1) {
1717 Composition ocomp = other->_invert_composition_cache.get_data(oi);
1718 other->_invert_composition_cache.remove_element(oi);
1719 _cache_stats.add_total_size(-1);
1720 _cache_stats.inc_dels();
1721 if (ocomp._result != nullptr && ocomp._result != other) {
1722 cache_unref_delete(ocomp._result);
1723 }
1724 }
1725 }
1726 if (comp._result != nullptr && comp._result != this) {
1727 cache_unref_delete(comp._result);
1728 }
1729 }
1730 }
1731
1732 /**
1733 * This is the private implementation of get_bin_index() and get_draw_order().
1734 */
1735 void RenderState::
1736 determine_bin_index() {
1737 LightMutexHolder holder(_lock);
1738 if ((_flags & F_checked_bin_index) != 0) {
1739 // Someone else checked it first.
1740 return;
1741 }
1742
1743 std::string bin_name;
1744 _draw_order = 0;
1745
1746 const CullBinAttrib *bin;
1747 if (get_attrib(bin)) {
1748 bin_name = bin->get_bin_name();
1749 _draw_order = bin->get_draw_order();
1750 }
1751
1752 if (bin_name.empty()) {
1753 // No explicit bin is specified; put in the in the default bin, either
1754 // opaque or transparent, based on the transparency setting.
1755 bin_name = "opaque";
1756
1757 const TransparencyAttrib *transparency;
1758 if (get_attrib(transparency)) {
1759 switch (transparency->get_mode()) {
1760 case TransparencyAttrib::M_alpha:
1761 case TransparencyAttrib::M_premultiplied_alpha:
1762 case TransparencyAttrib::M_dual:
1763 // These transparency modes require special back-to-front sorting.
1764 bin_name = "transparent";
1765 break;
1766
1767 default:
1768 break;
1769 }
1770 }
1771 }
1772
1773 CullBinManager *bin_manager = CullBinManager::get_global_ptr();
1774 _bin_index = bin_manager->find_bin(bin_name);
1775 if (_bin_index == -1) {
1776 pgraph_cat.warning()
1777 << "No bin named " << bin_name << "; creating default bin.\n";
1778 _bin_index = bin_manager->add_bin(bin_name, CullBinManager::BT_unsorted, 0);
1779 }
1780 _flags |= F_checked_bin_index;
1781 }
1782
1783 /**
1784 * This is the private implementation of has_cull_callback().
1785 */
1786 void RenderState::
1787 determine_cull_callback() {
1788 LightMutexHolder holder(_lock);
1789 if ((_flags & F_checked_cull_callback) != 0) {
1790 // Someone else checked it first.
1791 return;
1792 }
1793
1794 SlotMask mask = _filled_slots;
1795 int slot = mask.get_lowest_on_bit();
1796 while (slot >= 0) {
1797 const Attribute &attrib = _attributes[slot];
1798 nassertv(attrib._attrib != nullptr);
1799 if (attrib._attrib->has_cull_callback()) {
1800 _flags |= F_has_cull_callback;
1801 break;
1802 }
1803
1804 mask.clear_bit(slot);
1805 slot = mask.get_lowest_on_bit();
1806 }
1807
1808 _flags |= F_checked_cull_callback;
1809 }
1810
1811 /**
1812 * Fills up the state with all of the default attribs.
1813 */
1814 void RenderState::
1815 fill_default() {
1816 RenderAttribRegistry *reg = RenderAttribRegistry::quick_get_global_ptr();
1817 int num_slots = reg->get_num_slots();
1818 for (int slot = 1; slot < num_slots; ++slot) {
1819 _attributes[slot].set(reg->get_slot_default(slot), 0);
1820 _filled_slots.set_bit(slot);
1821 }
1822 }
1823
1824 /**
1825 * Moves the RenderState object from one PStats category to another, so that
1826 * we can track in PStats how many pointers are held by nodes, and how many
1827 * are held in the cache only.
1828 */
1829 void RenderState::
1830 update_pstats(int old_referenced_bits, int new_referenced_bits) {
1831 #ifdef DO_PSTATS
1832 if ((old_referenced_bits & R_node) != 0) {
1833 _node_counter.sub_level(1);
1834 } else if ((old_referenced_bits & R_cache) != 0) {
1835 _cache_counter.sub_level(1);
1836 }
1837 if ((new_referenced_bits & R_node) != 0) {
1838 _node_counter.add_level(1);
1839 } else if ((new_referenced_bits & R_cache) != 0) {
1840 _cache_counter.add_level(1);
1841 }
1842 #endif // DO_PSTATS
1843 }
1844
1845 /**
1846 * Make sure the global _states map is allocated. This only has to be done
1847 * once. We could make this map static, but then we run into problems if
1848 * anyone creates a RenderState object at static init time; it also seems to
1849 * cause problems when the Panda shared library is unloaded at application
1850 * exit time.
1851 */
1852 void RenderState::
1853 init_states() {
1854 _states = new States;
1855
1856 // TODO: we should have a global Panda mutex to allow us to safely create
1857 // _states_lock without a startup race condition. For the meantime, this is
1858 // OK because we guarantee that this method is called at static init time,
1859 // presumably when there is still only one thread in the world.
1860 _states_lock = new LightReMutex("RenderState::_states_lock");
1861 _cache_stats.init();
1862 nassertv(Thread::get_current_thread() == Thread::get_main_thread());
1863
1864 // Initialize the empty state object as well. It is used so often that it
1865 // is declared globally, and lives forever.
1866 RenderState *state = new RenderState;
1867 state->local_object();
1868 state->_saved_entry = _states->store(state, nullptr);
1869 _empty_state = state;
1870 }
1871
1872 /**
1873 * Tells the BamReader how to create objects of type RenderState.
1874 */
1875 void RenderState::
1876 register_with_read_factory() {
1877 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
1878 }
1879
1880 /**
1881 * Writes the contents of this object to the datagram for shipping out to a
1882 * Bam file.
1883 */
1884 void RenderState::
1885 write_datagram(BamWriter *manager, Datagram &dg) {
1886 TypedWritable::write_datagram(manager, dg);
1887
1888 int num_attribs = _filled_slots.get_num_on_bits();
1889 nassertv(num_attribs == (int)(uint16_t)num_attribs);
1890 dg.add_uint16(num_attribs);
1891
1892 // **** We should smarten up the writing of the override number--most of the
1893 // time these will all be zero.
1894 SlotMask mask = _filled_slots;
1895 int slot = mask.get_lowest_on_bit();
1896 while (slot >= 0) {
1897 const Attribute &attrib = _attributes[slot];
1898 nassertv(attrib._attrib != nullptr);
1899 manager->write_pointer(dg, attrib._attrib);
1900 dg.add_int32(attrib._override);
1901
1902 mask.clear_bit(slot);
1903 slot = mask.get_lowest_on_bit();
1904 }
1905 }
1906
1907 /**
1908 * Receives an array of pointers, one for each time manager->read_pointer()
1909 * was called in fillin(). Returns the number of pointers processed.
1910 */
1911 int RenderState::
1912 complete_pointers(TypedWritable **p_list, BamReader *manager) {
1913 int pi = TypedWritable::complete_pointers(p_list, manager);
1914
1915 int num_attribs = 0;
1916
1917 RenderAttribRegistry *reg = RenderAttribRegistry::quick_get_global_ptr();
1918 for (size_t i = 0; i < (*_read_overrides).size(); ++i) {
1919 int override = (*_read_overrides)[i];
1920
1921 RenderAttrib *attrib = DCAST(RenderAttrib, p_list[pi++]);
1922 if (attrib != nullptr) {
1923 int slot = attrib->get_slot();
1924 if (slot > 0 && slot < reg->get_max_slots()) {
1925 _attributes[slot].set(attrib, override);
1926 _filled_slots.set_bit(slot);
1927 ++num_attribs;
1928 }
1929 }
1930 }
1931
1932 delete _read_overrides;
1933 _read_overrides = nullptr;
1934
1935 return pi;
1936 }
1937
1938 /**
1939 * Called immediately after complete_pointers(), this gives the object a
1940 * chance to adjust its own pointer if desired. Most objects don't change
1941 * pointers after completion, but some need to.
1942 *
1943 * Once this function has been called, the old pointer will no longer be
1944 * accessed.
1945 */
1946 TypedWritable *RenderState::
1947 change_this(TypedWritable *old_ptr, BamReader *manager) {
1948 // First, uniquify the pointer.
1949 RenderState *state = DCAST(RenderState, old_ptr);
1950 CPT(RenderState) pointer = return_unique(state);
1951
1952 // But now we have a problem, since we have to hold the reference count and
1953 // there's no way to return a TypedWritable while still holding the
1954 // reference count! We work around this by explicitly upping the count, and
1955 // also setting a finalize() callback to down it later.
1956 if (pointer == state) {
1957 pointer->ref();
1958 manager->register_finalize(state);
1959 }
1960
1961 // We have to cast the pointer back to non-const, because the bam reader
1962 // expects that.
1963 return (RenderState *)pointer.p();
1964 }
1965
1966 /**
1967 * Called by the BamReader to perform any final actions needed for setting up
1968 * the object after all objects have been read and all pointers have been
1969 * completed.
1970 */
1971 void RenderState::
1972 finalize(BamReader *) {
1973 // Unref the pointer that we explicitly reffed in change_this().
1974 unref();
1975
1976 // We should never get back to zero after unreffing our own count, because
1977 // we expect to have been stored in a pointer somewhere. If we do get to
1978 // zero, it's a memory leak; the way to avoid this is to call unref_delete()
1979 // above instead of unref(), but this is dangerous to do from within a
1980 // virtual function.
1981 nassertv(get_ref_count() != 0);
1982 }
1983
1984 /**
1985 * This function is called by the BamReader's factory when a new object of
1986 * type RenderState is encountered in the Bam file. It should create the
1987 * RenderState and extract its information from the file.
1988 */
1989 TypedWritable *RenderState::
1990 make_from_bam(const FactoryParams &params) {
1991 RenderState *state = new RenderState;
1992 DatagramIterator scan;
1993 BamReader *manager;
1994
1995 parse_params(params, scan, manager);
1996 state->fillin(scan, manager);
1997 manager->register_change_this(change_this, state);
1998
1999 return state;
2000 }
2001
2002 /**
2003 * This internal function is called by make_from_bam to read in all of the
2004 * relevant data from the BamFile for the new RenderState.
2005 */
2006 void RenderState::
2007 fillin(DatagramIterator &scan, BamReader *manager) {
2008 TypedWritable::fillin(scan, manager);
2009
2010 int num_attribs = scan.get_uint16();
2011 _read_overrides = new vector_int;
2012 (*_read_overrides).reserve(num_attribs);
2013
2014 for (int i = 0; i < num_attribs; ++i) {
2015 manager->read_pointer(scan);
2016 int override = scan.get_int32();
2017 (*_read_overrides).push_back(override);
2018 }
2019 }
bamReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
parse_params
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
bamWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
cache_unref_delete
void cache_unref_delete(RefCountType *ptr)
This global helper function will unref the given ReferenceCount object, and if the reference count re...
Definition: cachedTypedWritableReferenceCount.I:204
BamReader
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
BamReader::register_finalize
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
BamReader::register_change_this
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
BamReader::read_pointer
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
BamReader::get_factory
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
BamWriter
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
BamWriter::write_pointer
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317
BitMask< uint32_t, 32 >
BitMask::get_lowest_on_bit
int get_lowest_on_bit() const
Returns the index of the lowest 1 bit in the mask.
Definition: bitMask.I:283
BitMask::set_bit
void set_bit(int index)
Sets the nth bit on.
Definition: bitMask.I:119
BitMask::get_bit
bool get_bit(int index) const
Returns true if the nth bit is set, false if it is cleared.
Definition: bitMask.I:109
BitMask::clear_bit
void clear_bit(int index)
Sets the nth bit off.
Definition: bitMask.I:129
BitMask::get_num_on_bits
int get_num_on_bits() const
Returns the number of bits that are set to 1 in the mask.
Definition: bitMask.I:264
CacheStats
This is used to track the utilization of the TransformState and RenderState caches,...
Definition: cacheStats.h:25
CacheStats::inc_adds
void inc_adds(bool is_new)
Increments by 1 the count of elements added to the cache.
Definition: cacheStats.I:56
CacheStats::add_total_size
void add_total_size(int count)
Adds the indicated count (positive or negative) to the total number of entries for the cache (net occ...
Definition: cacheStats.I:80
CacheStats::add_num_states
void add_num_states(int count)
Adds the indicated count (positive or negative) to the total count of individual RenderState or Trans...
Definition: cacheStats.I:91
CacheStats::maybe_report
void maybe_report(const char *name)
Outputs a report if enough time has elapsed.
Definition: cacheStats.I:18
CacheStats::inc_dels
void inc_dels()
Increments by 1 the count of elements removed from the cache.
Definition: cacheStats.I:69
CacheStats::init
void init()
Initializes the CacheStats for the first time.
Definition: cacheStats.cxx:22
CacheStats::inc_hits
void inc_hits()
Increments by 1 the count of cache hits.
Definition: cacheStats.I:35
CacheStats::inc_misses
void inc_misses()
Increments by 1 the count of cache misses.
Definition: cacheStats.I:45
CachedTypedWritableReferenceCount::get_cache_ref_count
get_cache_ref_count
Returns the current reference count.
Definition: cachedTypedWritableReferenceCount.h:47
CullBinAttrib
Assigns geometry to a particular bin by name.
Definition: cullBinAttrib.h:27
CullBinAttrib::get_draw_order
get_draw_order
Returns the draw order this attribute specifies.
Definition: cullBinAttrib.h:40
CullBinAttrib::get_bin_name
get_bin_name
Returns the name of the bin this attribute specifies.
Definition: cullBinAttrib.h:39
CullBinManager
This is a global object that maintains the collection of named CullBins in the world.
Definition: cullBinManager.h:34
CullBinManager::get_global_ptr
static CullBinManager * get_global_ptr()
Returns the pointer to the global CullBinManager object.
Definition: cullBinManager.I:281
CullBinManager::add_bin
int add_bin(const std::string &name, BinType type, int sort)
Defines a new bin with the indicated name, and returns the new bin_index.
Definition: cullBinManager.cxx:52
CullBinManager::find_bin
int find_bin(const std::string &name) const
Returns the bin_index associated with the bin of the given name, or -1 if no bin has that name.
Definition: cullBinManager.cxx:157
CullTraverserData
This collects together the pieces of data that are accumulated for each node while walking the scene ...
Definition: cullTraverserData.h:40
CullTraverser
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
DatagramIterator
A class to retrieve the individual data elements previously stored in a Datagram.
Definition: datagramIterator.h:27
DatagramIterator::get_uint16
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
Definition: datagramIterator.I:145
DatagramIterator::get_int32
int32_t get_int32()
Extracts a signed 32-bit integer.
Definition: datagramIterator.I:107
Datagram
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
Datagram::add_int32
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
Definition: datagram.I:67
Datagram::add_uint16
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
FactoryParams
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
Factory::register_factory
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
LightMutexHolder
Similar to MutexHolder, but for a light mutex.
Definition: lightMutexHolder.h:25
LightReMutexDirect::debug_is_locked
bool debug_is_locked() const
Returns true if the current thread has locked the LightReMutex, false otherwise.
Definition: lightReMutexDirect.I:123
LightReMutexHolder
Similar to MutexHolder, but for a light reentrant mutex.
Definition: lightReMutexHolder.h:25
LightReMutex
A lightweight reentrant mutex.
Definition: lightReMutex.h:32
MemoryUsage::update_type
static void update_type(ReferenceCount *ptr, TypeHandle type)
Associates the indicated type with the given pointer.
Definition: memoryUsage.I:55
PStatCollector
A lightweight class that represents a single element that may be timed and/or counted via stats.
Definition: pStatCollector.h:43
PStatTimer
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
ReferenceCount::local_object
void local_object()
This function should be called, once, immediately after creating a new instance of some ReferenceCoun...
Definition: referenceCount.I:229
ReferenceCount::unref_if_one
bool unref_if_one() const
Atomically decreases the reference count of this object if it is one.
Definition: referenceCount.I:327
ReferenceCount::get_ref_count
get_ref_count
Returns the current reference count.
Definition: referenceCount.h:53
ReferenceCount::unref
virtual bool unref() const
Explicitly decrements the reference count.
Definition: referenceCount.I:179
RenderAttribRegistry
This class is used to associate each RenderAttrib with a different slot index at runtime,...
Definition: renderAttribRegistry.h:31
RenderAttribRegistry::get_slot_default
const RenderAttrib * get_slot_default(int slot) const
Returns the default RenderAttrib object associated with slot n.
Definition: renderAttribRegistry.I:60
RenderAttribRegistry::quick_get_global_ptr
static RenderAttribRegistry * quick_get_global_ptr()
Returns the global_ptr without first ensuring it has been initialized.
Definition: renderAttribRegistry.I:100
RenderAttribRegistry::get_sorted_slot
int get_sorted_slot(int n) const
Returns the nth slot in sorted order.
Definition: renderAttribRegistry.I:79
RenderAttribRegistry::get_num_slots
int get_num_slots() const
Returns the number of RenderAttrib slots that have been allocated.
Definition: renderAttribRegistry.I:32
RenderAttribRegistry::get_num_sorted_slots
int get_num_sorted_slots() const
Returns the number of entries in the sorted_slots list.
Definition: renderAttribRegistry.I:69
RenderAttrib
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:51
RenderAttrib::garbage_collect
static int garbage_collect()
Performs a garbage-collection cycle.
Definition: renderAttrib.cxx:188
RenderModeAttrib
Specifies how polygons are to be drawn.
Definition: renderModeAttrib.h:27
RenderModeAttrib::get_geom_rendering
int get_geom_rendering(int geom_rendering) const
Returns the union of the Geom::GeomRendering bits that will be required once this RenderModeAttrib is...
Definition: renderModeAttrib.I:72
RenderState
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
RenderState::compare_to
int compare_to(const RenderState &other) const
Provides an arbitrary ordering among all unique RenderStates, so we can store the essentially differe...
Definition: renderState.cxx:141
RenderState::compare_mask
int compare_mask(const RenderState &other, SlotMask compare_mask) const
This version of compare_to takes a slot mask that indicates which attributes to include in the compar...
Definition: renderState.cxx:192
RenderState::register_with_read_factory
static void register_with_read_factory()
Tells the BamReader how to create objects of type RenderState.
Definition: renderState.cxx:1876
RenderState::get_num_states
static int get_num_states()
Returns the total number of unique RenderState objects allocated in the world.
Definition: renderState.cxx:718
RenderState::list_states
static void list_states(std::ostream &out)
Lists all of the RenderStates in the cache to the output stream, one per line.
Definition: renderState.cxx:1085
RenderState::is_empty
bool is_empty() const
Returns true if the state is empty, false otherwise.
Definition: renderState.I:27
RenderState::write_datagram
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: renderState.cxx:1885
RenderState::complete_pointers
virtual int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
Definition: renderState.cxx:1912
RenderState::garbage_collect
static int garbage_collect()
Performs a garbage-collection cycle.
Definition: renderState.cxx:895
RenderState::cull_callback
bool cull_callback(CullTraverser *trav, const CullTraverserData &data) const
Calls cull_callback() on each attrib.
Definition: renderState.cxx:214
RenderState::bin_removed
static void bin_removed(int bin_index)
Intended to be called by CullBinManager::remove_bin(), this informs all the RenderStates in the world...
Definition: renderState.cxx:1187
RenderState::compare_sort
int compare_sort(const RenderState &other) const
Returns -1, 0, or 1 according to the relative sorting of these two RenderStates, with regards to rend...
Definition: renderState.cxx:164
RenderState::unref
virtual bool unref() const
Explicitly decrements the reference count.
RenderState::init_states
static void init_states()
Make sure the global _states map is allocated.
Definition: renderState.cxx:1853
RenderState::clear_munger_cache
static void clear_munger_cache()
Completely empties the cache of state + gsg -> munger, for all states and all gsg's.
Definition: renderState.cxx:982
RenderState::validate_states
static bool validate_states()
Ensures that the cache is still stored in sorted order, and that none of the cache elements have been...
Definition: renderState.cxx:1107
RenderState::change_this
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...
Definition: renderState.cxx:1947
RenderState::list_cycles
static void list_cycles(std::ostream &out)
Detects all of the reference-count cycles in the cache and reports them to standard output.
Definition: renderState.cxx:1008
RenderState::clear_cache
static int clear_cache()
Empties the cache of composed RenderStates.
Definition: renderState.cxx:823
RenderState::finalize
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
Definition: renderState.cxx:1972
RenderState::~RenderState
virtual ~RenderState()
The destructor is responsible for removing the RenderState from the global set if it is there.
Definition: renderState.cxx:115
RenderState::get_geom_rendering
int get_geom_rendering(int geom_rendering) const
Returns the union of the Geom::GeomRendering bits that will be required once this RenderState is appl...
Definition: renderState.cxx:1163
RenderState::get_num_unused_states
static int get_num_unused_states()
Returns the total number of RenderState objects that have been allocated but have no references outsi...
Definition: renderState.cxx:740
SimpleHashMap
This template class implements an unordered map of keys to data, implemented as a hashtable.
Definition: simpleHashMap.h:81
SimpleHashMap::get_key
const Key & get_key(size_t n) const
Returns the key in the nth entry of the table.
Definition: simpleHashMap.I:396
SimpleHashMap::store
int store(const Key &key, const Value &data)
Records the indicated key/data pair in the map.
Definition: simpleHashMap.I:198
SimpleHashMap::validate
bool validate() const
Returns true if the internal table appears to be consistent, false if there are some internal errors.
Definition: simpleHashMap.I:525
SimpleHashMap::clear
void clear()
Completely empties the table.
Definition: simpleHashMap.I:352
SimpleHashMap::get_data
const Value & get_data(size_t n) const
Returns the data in the nth entry of the table.
Definition: simpleHashMap.I:408
SimpleHashMap::find
int find(const Key &key) const
Searches for the indicated key in the table.
Definition: simpleHashMap.I:177
SimpleHashMap::remove
bool remove(const Key &key)
Removes the indicated key and its associated data from the table.
Definition: simpleHashMap.I:275
SimpleHashMap::consider_shrink_table
bool consider_shrink_table()
Shrinks the table if the allocated storage is significantly larger than the number of elements in it.
Definition: simpleHashMap.I:714
SimpleHashMap::remove_element
void remove_element(size_t n)
Removes the nth entry from the table.
Definition: simpleHashMap.I:456
SimpleHashMap::is_empty
bool is_empty() const
Returns true if the table is empty; i.e.
Definition: simpleHashMap.I:475
SimpleHashMap::get_num_entries
size_t get_num_entries() const
Returns the number of active entries in the table.
Definition: simpleHashMap.I:466
TexGenAttrib
Computes texture coordinates for geometry automatically based on vertex position and/or normal.
Definition: texGenAttrib.h:32
TexGenAttrib::get_geom_rendering
int get_geom_rendering(int geom_rendering) const
Returns the union of the Geom::GeomRendering bits that will be required once this TexGenAttrib is app...
Definition: texGenAttrib.I:44
TexMatrixAttrib
Applies a transform matrix to UV's before they are rendered.
Definition: texMatrixAttrib.h:30
TexMatrixAttrib::get_geom_rendering
int get_geom_rendering(int geom_rendering) const
Returns the union of the Geom::GeomRendering bits that will be required once this TexMatrixAttrib is ...
Definition: texMatrixAttrib.I:50
Thread::get_main_thread
get_main_thread
Returns a pointer to the "main" Thread object–this is the Thread that started the whole process.
Definition: thread.h:107
Thread::get_current_thread
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
TransparencyAttrib
This controls the enabling of transparency.
Definition: transparencyAttrib.h:31
TransparencyAttrib::get_mode
get_mode
Returns the transparency mode.
Definition: transparencyAttrib.h:54
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
TypedWritable
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
TypedWritable::fillin
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...
Definition: typedWritable.cxx:103
TypedWritable::write_datagram
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: typedWritable.cxx:54
TypedWritable::complete_pointers
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
Definition: typedWritable.cxx:81
UpdateSeq
This is a sequence number that increments monotonically.
Definition: updateSeq.h:37
integer_hash::add_hash
static size_t add_hash(size_t start, const Key &key)
Adds the indicated key into a running hash.
Definition: stl_compares.I:101
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
pointer_hash::add_hash
static size_t add_hash(size_t start, const void *key)
Adds the indicated key into a running hash.
Definition: stl_compares.I:110
pset
This is our own Panda specialization on the default STL set.
Definition: pset.h:49
pvector
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
clipPlaneAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
colorAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
colorScaleAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
compareTo.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_pgraph.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
cullBinAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
cullBinManager.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
datagramIterator.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
fogAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
indent
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
indent.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
lightMutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
lightReMutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pStatTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_lowest_on_bit
int get_lowest_on_bit(unsigned short x)
Returns the index of the lowest 1 bit in the word.
Definition: pbitops.I:175
renderAttribRegistry.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CPT
CPT(RenderState) RenderState
Returns a RenderState with one attribute set.
Definition: renderState.cxx:234
renderState.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
scissorAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
shaderAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
texGenAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
textureAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
thread.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
transparencyAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.