Fix crashes with CollisionObject debug shapes

MeshInstance added as child nodes for CollisionObject debug shapes can
be invalidated while deleting the collision object (child nodes are
deleted first), which caused accesses to invalid memory in
shape_owner_remove_shape that lead to random crashes.

Also optimized accesses to shapes to avoid copy-on-write on each
iteration.
This commit is contained in:
PouleyKetchoupp 2021-04-12 20:08:30 -07:00
parent 075f358fcd
commit d7353c5d41
2 changed files with 28 additions and 1 deletions

View File

@ -75,6 +75,11 @@ void CollisionObject3D::_notification(int p_what) {
}
} break;
case NOTIFICATION_PREDELETE: {
if (debug_shape_count > 0) {
_clear_debug_shapes();
}
} break;
}
}
@ -116,11 +121,13 @@ void CollisionObject3D::_update_debug_shapes() {
for (Set<uint32_t>::Element *shapedata_idx = debug_shapes_to_update.front(); shapedata_idx; shapedata_idx = shapedata_idx->next()) {
if (shapes.has(shapedata_idx->get())) {
ShapeData &shapedata = shapes[shapedata_idx->get()];
ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw();
for (int i = 0; i < shapedata.shapes.size(); i++) {
ShapeData::ShapeBase &s = shapedata.shapes.write[i];
ShapeData::ShapeBase &s = shapes[i];
if (s.debug_shape) {
s.debug_shape->queue_delete();
s.debug_shape = nullptr;
--debug_shape_count;
}
if (s.shape.is_null() || shapedata.disabled) {
continue;
@ -133,12 +140,30 @@ void CollisionObject3D::_update_debug_shapes() {
add_child(mi);
mi->force_update_transform();
s.debug_shape = mi;
++debug_shape_count;
}
}
}
debug_shapes_to_update.clear();
}
void CollisionObject3D::_clear_debug_shapes() {
for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
ShapeData &shapedata = E->get();
ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw();
for (int i = 0; i < shapedata.shapes.size(); i++) {
ShapeData::ShapeBase &s = shapes[i];
if (s.debug_shape) {
s.debug_shape->queue_delete();
s.debug_shape = nullptr;
--debug_shape_count;
}
}
}
debug_shape_count = 0;
}
void CollisionObject3D::_update_shape_data(uint32_t p_owner) {
if (is_inside_tree() && get_tree()->is_debugging_collisions_hint() && !Engine::get_singleton()->is_editor_hint()) {
if (debug_shapes_to_update.is_empty()) {

View File

@ -62,6 +62,7 @@ class CollisionObject3D : public Node3D {
bool ray_pickable = true;
Set<uint32_t> debug_shapes_to_update;
int debug_shape_count = 0;
void _update_pickable();
@ -78,6 +79,7 @@ protected:
virtual void _mouse_exit();
void _update_debug_shapes();
void _clear_debug_shapes();
public:
uint32_t create_shape_owner(Object *p_owner);