Skip to content

Commit 9a40feb

Browse files
committed
shadows: fix light buffer not being destructed after light removal
Fixes LP 1672089
1 parent 1fd8af5 commit 9a40feb

File tree

7 files changed

+124
-23
lines changed

7 files changed

+124
-23
lines changed

panda/src/pgraph/light.cxx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,20 @@ get_attenuation() const {
157157
return no_atten;
158158
}
159159

160+
/**
161+
* This is called when the light is added to a LightAttrib.
162+
*/
163+
void Light::
164+
attrib_ref() {
165+
}
166+
167+
/**
168+
* This is called when the light is removed from a LightAttrib.
169+
*/
170+
void Light::
171+
attrib_unref() {
172+
}
173+
160174
/**
161175
* Computes the vector from a particular vertex to this light. The exact
162176
* vector depends on the type of light (e.g. point lights return a different

panda/src/pgraph/light.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class EXPCL_PANDA_PGRAPH Light {
6464
MAKE_PROPERTY(priority, get_priority, set_priority);
6565

6666
public:
67+
virtual void attrib_ref();
68+
virtual void attrib_unref();
69+
6770
virtual void output(ostream &out) const=0;
6871
virtual void write(ostream &out, int indent_level) const=0;
6972
virtual void bind(GraphicsStateGuardianBase *gsg, const NodePath &light,

panda/src/pgraph/lightAttrib.I

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,6 @@ INLINE LightAttrib::
1818
LightAttrib() : _off_all_lights(false), _num_non_ambient_lights(0) {
1919
}
2020

21-
/**
22-
* Use LightAttrib::make() to construct a new LightAttrib object. The copy
23-
* constructor is only defined to facilitate methods like add_on_light().
24-
*/
25-
INLINE LightAttrib::
26-
LightAttrib(const LightAttrib &copy) :
27-
_on_lights(copy._on_lights),
28-
_off_lights(copy._off_lights),
29-
_off_all_lights(copy._off_all_lights),
30-
_sort_seq(UpdateSeq::old())
31-
{
32-
}
33-
3421
/**
3522
* Returns the number of lights that are turned on by the attribute.
3623
*/

panda/src/pgraph/lightAttrib.cxx

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,44 @@ class CompareLightPriorities {
4747
}
4848
};
4949

50+
/**
51+
* Use LightAttrib::make() to construct a new LightAttrib object. The copy
52+
* constructor is only defined to facilitate methods like add_on_light().
53+
*/
54+
LightAttrib::
55+
LightAttrib(const LightAttrib &copy) :
56+
_on_lights(copy._on_lights),
57+
_off_lights(copy._off_lights),
58+
_off_all_lights(copy._off_all_lights),
59+
_sort_seq(UpdateSeq::old())
60+
{
61+
// Increase the attrib_ref of all the lights in this attribute.
62+
Lights::const_iterator it;
63+
for (it = _on_lights.begin(); it != _on_lights.end(); ++it) {
64+
Light *lobj = (*it).node()->as_light();
65+
nassertd(lobj != nullptr) continue;
66+
lobj->attrib_ref();
67+
}
68+
}
69+
70+
/**
71+
* Destructor.
72+
*/
73+
LightAttrib::
74+
~LightAttrib() {
75+
// Call attrib_unref() on all on lights.
76+
Lights::const_iterator it;
77+
for (it = _on_lights.begin(); it != _on_lights.end(); ++it) {
78+
const NodePath &np = *it;
79+
if (!np.is_empty()) {
80+
Light *lobj = np.node()->as_light();
81+
if (lobj != nullptr) {
82+
lobj->attrib_unref();
83+
}
84+
}
85+
}
86+
}
87+
5088
/**
5189
* Constructs a new LightAttrib object that turns on (or off, according to op)
5290
* the indicated light(s).
@@ -376,14 +414,17 @@ make_all_off() {
376414
*/
377415
CPT(RenderAttrib) LightAttrib::
378416
add_on_light(const NodePath &light) const {
379-
nassertr(!light.is_empty() && light.node()->as_light() != (Light *)NULL, this);
417+
nassertr(!light.is_empty(), this);
418+
Light *lobj = light.node()->as_light();
419+
nassertr(lobj != nullptr, this);
420+
380421
LightAttrib *attrib = new LightAttrib(*this);
381-
attrib->_on_lights.insert(light);
382-
attrib->_off_lights.erase(light);
383422

384423
pair<Lights::iterator, bool> insert_result =
385424
attrib->_on_lights.insert(Lights::value_type(light));
386425
if (insert_result.second) {
426+
lobj->attrib_ref();
427+
387428
// Also ensure it is removed from the off_lights list.
388429
attrib->_off_lights.erase(light);
389430
}
@@ -397,9 +438,14 @@ add_on_light(const NodePath &light) const {
397438
*/
398439
CPT(RenderAttrib) LightAttrib::
399440
remove_on_light(const NodePath &light) const {
400-
nassertr(!light.is_empty() && light.node()->as_light() != (Light *)NULL, this);
441+
nassertr(!light.is_empty(), this);
442+
Light *lobj = light.node()->as_light();
443+
nassertr(lobj != nullptr, this);
444+
401445
LightAttrib *attrib = new LightAttrib(*this);
402-
attrib->_on_lights.erase(light);
446+
if (attrib->_on_lights.erase(light)) {
447+
lobj->attrib_unref();
448+
}
403449
return return_new(attrib);
404450
}
405451

@@ -409,12 +455,17 @@ remove_on_light(const NodePath &light) const {
409455
*/
410456
CPT(RenderAttrib) LightAttrib::
411457
add_off_light(const NodePath &light) const {
412-
nassertr(!light.is_empty() && light.node()->as_light() != (Light *)NULL, this);
458+
nassertr(!light.is_empty(), this);
459+
Light *lobj = light.node()->as_light();
460+
nassertr(lobj != nullptr, this);
461+
413462
LightAttrib *attrib = new LightAttrib(*this);
414463
if (!_off_all_lights) {
415464
attrib->_off_lights.insert(light);
416465
}
417-
attrib->_on_lights.erase(light);
466+
if (attrib->_on_lights.erase(light)) {
467+
lobj->attrib_unref();
468+
}
418469
return return_new(attrib);
419470
}
420471

@@ -960,6 +1011,10 @@ finalize(BamReader *manager) {
9601011
// If it's in the registry, replace it.
9611012
_on_lights[i] = areg->get_node(n);
9621013
}
1014+
1015+
Light *lobj = _on_lights[i].node()->as_light();
1016+
nassertd(lobj != nullptr) continue;
1017+
lobj->attrib_ref();
9631018
}
9641019

9651020
} else {
@@ -992,10 +1047,15 @@ finalize(BamReader *manager) {
9921047
if (n != -1) {
9931048
// If it's in the registry, add that NodePath.
9941049
_on_lights.push_back(areg->get_node(n));
1050+
node = _on_lights.back().node();
9951051
} else {
9961052
// Otherwise, add any arbitrary NodePath. Complain if it's ambiguous.
9971053
_on_lights.push_back(NodePath(node));
9981054
}
1055+
1056+
Light *lobj = node->as_light();
1057+
nassertd(lobj != nullptr) continue;
1058+
lobj->attrib_ref();
9991059
}
10001060
}
10011061

panda/src/pgraph/lightAttrib.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@
3030
class EXPCL_PANDA_PGRAPH LightAttrib : public RenderAttrib {
3131
protected:
3232
INLINE LightAttrib();
33-
INLINE LightAttrib(const LightAttrib &copy);
33+
LightAttrib(const LightAttrib &copy);
3434

3535
PUBLISHED:
36+
virtual ~LightAttrib();
3637

3738
// This is the old, deprecated interface to LightAttrib. Do not use any of
3839
// these methods for new code; these methods will be removed soon.

panda/src/pgraphnodes/lightLensNode.cxx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ TypeHandle LightLensNode::_type_handle;
2727
*/
2828
LightLensNode::
2929
LightLensNode(const string &name, Lens *lens) :
30-
Camera(name, lens)
30+
Camera(name, lens),
31+
_attrib_count(0)
3132
{
3233
set_active(false);
3334
_shadow_caster = false;
@@ -46,6 +47,10 @@ LightLensNode::
4647
~LightLensNode() {
4748
set_active(false);
4849
clear_shadow_buffers();
50+
51+
// If this triggers, the number of attrib_ref() didn't match the number of
52+
// attrib_unref() calls, probably indicating a bug in LightAttrib.
53+
nassertv(AtomicAdjust::get(_attrib_count) == 0);
4954
}
5055

5156
/**
@@ -57,7 +62,8 @@ LightLensNode(const LightLensNode &copy) :
5762
Camera(copy),
5863
_shadow_caster(copy._shadow_caster),
5964
_sb_size(copy._sb_size),
60-
_sb_sort(-10)
65+
_sb_sort(-10),
66+
_attrib_count(0)
6167
{
6268
}
6369

@@ -81,6 +87,28 @@ clear_shadow_buffers() {
8187
_sbuffers.clear();
8288
}
8389

90+
91+
/**
92+
* This is called when the light is added to a LightAttrib.
93+
*/
94+
void LightLensNode::
95+
attrib_ref() {
96+
AtomicAdjust::inc(_attrib_count);
97+
}
98+
99+
/**
100+
* This is called when the light is removed from a LightAttrib.
101+
*/
102+
void LightLensNode::
103+
attrib_unref() {
104+
// When it is removed from the last LightAttrib, destroy the shadow buffers.
105+
// This is necessary to break the circular reference that the buffer holds
106+
// on this node, via the display region's camera.
107+
if (!AtomicAdjust::dec(_attrib_count)) {
108+
clear_shadow_buffers();
109+
}
110+
}
111+
84112
/**
85113
* Returns the Light object upcast to a PandaNode.
86114
*/

panda/src/pgraphnodes/lightLensNode.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "camera.h"
2121
#include "graphicsStateGuardianBase.h"
2222
#include "graphicsOutputBase.h"
23+
#include "atomicAdjust.h"
2324

2425
class ShaderGenerator;
2526
class GraphicsStateGuardian;
@@ -59,7 +60,14 @@ class EXPCL_PANDA_PGRAPHNODES LightLensNode : public Light, public Camera {
5960
typedef pmap<PT(GraphicsStateGuardianBase), PT(GraphicsOutputBase) > ShadowBuffers;
6061
ShadowBuffers _sbuffers;
6162

63+
// This counts how many LightAttribs in the world are referencing this
64+
// LightLensNode object.
65+
AtomicAdjust::Integer _attrib_count;
66+
6267
public:
68+
virtual void attrib_ref();
69+
virtual void attrib_unref();
70+
6371
virtual PandaNode *as_node();
6472
virtual Light *as_light();
6573

0 commit comments

Comments
 (0)