Skip to content

Commit 1f2d535

Browse files
committed
Merge pull request #100983 from mihe/jolt/only-iterate-active-bodies
Improve performance with many static/sleeping bodies when using Jolt Physics
2 parents d28c416 + 7beaddc commit 1f2d535

11 files changed

+193
-154
lines changed

modules/jolt_physics/objects/jolt_area_3d.cpp

+43-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ JPH::ObjectLayer JoltArea3D::_get_object_layer() const {
5959
return space->map_to_object_layer(_get_broad_phase_layer(), collision_layer, collision_mask);
6060
}
6161

62+
bool JoltArea3D::_has_pending_events() const {
63+
if (body_monitor_callback.is_valid()) {
64+
for (const KeyValue<JPH::BodyID, Overlap> &E : bodies_by_id) {
65+
if (!E.value.pending_added.is_empty() || !E.value.pending_removed.is_empty()) {
66+
return true;
67+
}
68+
}
69+
}
70+
71+
if (area_monitor_callback.is_valid()) {
72+
for (const KeyValue<JPH::BodyID, Overlap> &E : areas_by_id) {
73+
if (!E.value.pending_added.is_empty() || !E.value.pending_removed.is_empty()) {
74+
return true;
75+
}
76+
}
77+
}
78+
79+
return false;
80+
}
81+
6282
void JoltArea3D::_add_to_space() {
6383
jolt_shape = build_shape();
6484

@@ -90,6 +110,18 @@ void JoltArea3D::_add_to_space() {
90110
jolt_settings = nullptr;
91111
}
92112

113+
void JoltArea3D::_enqueue_call_queries() {
114+
if (space != nullptr) {
115+
space->enqueue_call_queries(&call_queries_element);
116+
}
117+
}
118+
119+
void JoltArea3D::_dequeue_call_queries() {
120+
if (space != nullptr) {
121+
space->dequeue_call_queries(&call_queries_element);
122+
}
123+
}
124+
93125
void JoltArea3D::_add_shape_pair(Overlap &p_overlap, const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id) {
94126
const JoltReadableBody3D other_jolt_body = space->read_body(p_body_id);
95127
const JoltShapedObject3D *other_object = other_jolt_body.as_shaped();
@@ -270,6 +302,8 @@ void JoltArea3D::_space_changing() {
270302
_force_bodies_exited(true);
271303
_force_areas_exited(true);
272304
}
305+
306+
_dequeue_call_queries();
273307
}
274308

275309
void JoltArea3D::_space_changed() {
@@ -304,7 +338,8 @@ void JoltArea3D::_gravity_changed() {
304338
}
305339

306340
JoltArea3D::JoltArea3D() :
307-
JoltShapedObject3D(OBJECT_TYPE_AREA) {
341+
JoltShapedObject3D(OBJECT_TYPE_AREA),
342+
call_queries_element(this) {
308343
}
309344

310345
bool JoltArea3D::is_default_area() const {
@@ -659,7 +694,13 @@ void JoltArea3D::area_exited(const JPH::BodyID &p_body_id) {
659694
overlap->shape_pairs.clear();
660695
}
661696

662-
void JoltArea3D::call_queries(JPH::Body &p_jolt_body) {
697+
void JoltArea3D::call_queries() {
663698
_flush_events(bodies_by_id, body_monitor_callback);
664699
_flush_events(areas_by_id, area_monitor_callback);
665700
}
701+
702+
void JoltArea3D::post_step(float p_step, JPH::Body &p_jolt_body) {
703+
if (_has_pending_events()) {
704+
_enqueue_call_queries();
705+
}
706+
}

modules/jolt_physics/objects/jolt_area_3d.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ class JoltArea3D final : public JoltShapedObject3D {
8989

9090
typedef HashMap<JPH::BodyID, Overlap, BodyIDHasher> OverlapsById;
9191

92+
SelfList<JoltArea3D> call_queries_element;
93+
9294
OverlapsById bodies_by_id;
9395
OverlapsById areas_by_id;
9496

@@ -115,8 +117,13 @@ class JoltArea3D final : public JoltShapedObject3D {
115117

116118
virtual JPH::EMotionType _get_motion_type() const override { return JPH::EMotionType::Kinematic; }
117119

120+
bool _has_pending_events() const;
121+
118122
virtual void _add_to_space() override;
119123

124+
void _enqueue_call_queries();
125+
void _dequeue_call_queries();
126+
120127
void _add_shape_pair(Overlap &p_overlap, const JPH::BodyID &p_body_id, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id);
121128
bool _remove_shape_pair(Overlap &p_overlap, const JPH::SubShapeID &p_other_shape_id, const JPH::SubShapeID &p_self_shape_id);
122129

@@ -217,10 +224,12 @@ class JoltArea3D final : public JoltShapedObject3D {
217224
void body_exited(const JPH::BodyID &p_body_id, bool p_notify = true);
218225
void area_exited(const JPH::BodyID &p_body_id);
219226

220-
void call_queries(JPH::Body &p_jolt_body);
227+
void call_queries();
221228

222229
virtual bool has_custom_center_of_mass() const override { return false; }
223230
virtual Vector3 get_center_of_mass_custom() const override { return Vector3(); }
231+
232+
virtual void post_step(float p_step, JPH::Body &p_jolt_body) override;
224233
};
225234

226235
#endif // JOLT_AREA_3D_H

modules/jolt_physics/objects/jolt_body_3d.cpp

+51-45
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ void JoltBody3D::_add_to_space() {
128128
jolt_settings->mAllowDynamicOrKinematic = true;
129129
jolt_settings->mCollideKinematicVsNonDynamic = reports_all_kinematic_contacts();
130130
jolt_settings->mUseManifoldReduction = !reports_contacts();
131+
jolt_settings->mAllowSleeping = is_sleep_actually_allowed();
131132
jolt_settings->mLinearDamping = 0.0f;
132133
jolt_settings->mAngularDamping = 0.0f;
133134
jolt_settings->mMaxLinearVelocity = JoltProjectSettings::get_max_linear_velocity();
@@ -153,11 +154,19 @@ void JoltBody3D::_add_to_space() {
153154
jolt_settings = nullptr;
154155
}
155156

156-
void JoltBody3D::_integrate_forces(float p_step, JPH::Body &p_jolt_body) {
157-
if (!p_jolt_body.IsActive()) {
158-
return;
157+
void JoltBody3D::_enqueue_call_queries() {
158+
if (space != nullptr) {
159+
space->enqueue_call_queries(&call_queries_element);
160+
}
161+
}
162+
163+
void JoltBody3D::_dequeue_call_queries() {
164+
if (space != nullptr) {
165+
space->dequeue_call_queries(&call_queries_element);
159166
}
167+
}
160168

169+
void JoltBody3D::_integrate_forces(float p_step, JPH::Body &p_jolt_body) {
161170
_update_gravity(p_jolt_body);
162171

163172
if (!custom_integrator) {
@@ -182,8 +191,6 @@ void JoltBody3D::_integrate_forces(float p_step, JPH::Body &p_jolt_body) {
182191
p_jolt_body.AddForce(to_jolt(constant_force));
183192
p_jolt_body.AddTorque(to_jolt(constant_torque));
184193
}
185-
186-
sync_state = true;
187194
}
188195

189196
void JoltBody3D::_move_kinematic(float p_step, JPH::Body &p_jolt_body) {
@@ -201,27 +208,19 @@ void JoltBody3D::_move_kinematic(float p_step, JPH::Body &p_jolt_body) {
201208
}
202209

203210
p_jolt_body.MoveKinematic(new_position, new_rotation, p_step);
204-
205-
sync_state = true;
206-
}
207-
208-
void JoltBody3D::_pre_step_static(float p_step, JPH::Body &p_jolt_body) {
209-
// Nothing to do.
210211
}
211212

212213
void JoltBody3D::_pre_step_rigid(float p_step, JPH::Body &p_jolt_body) {
213214
_integrate_forces(p_step, p_jolt_body);
215+
_enqueue_call_queries();
214216
}
215217

216218
void JoltBody3D::_pre_step_kinematic(float p_step, JPH::Body &p_jolt_body) {
217219
_update_gravity(p_jolt_body);
218-
219220
_move_kinematic(p_step, p_jolt_body);
220221

221222
if (reports_contacts()) {
222-
// This seems to emulate the behavior of Godot Physics, where kinematic bodies are set as active (and thereby
223-
// have their state synchronized on every step) only if its max reported contacts is non-zero.
224-
sync_state = true;
223+
_enqueue_call_queries();
225224
}
226225
}
227226

@@ -428,6 +427,20 @@ void JoltBody3D::_update_possible_kinematic_contacts() {
428427
}
429428
}
430429

430+
void JoltBody3D::_update_sleep_allowed() {
431+
const bool value = is_sleep_actually_allowed();
432+
433+
if (!in_space()) {
434+
jolt_settings->mAllowSleeping = value;
435+
return;
436+
}
437+
438+
const JoltWritableBody3D body = space->write_body(jolt_id);
439+
ERR_FAIL_COND(body.is_invalid());
440+
441+
body->SetAllowSleeping(value);
442+
}
443+
431444
void JoltBody3D::_destroy_joint_constraints() {
432445
for (JoltJoint3D *joint : joints) {
433446
joint->destroy();
@@ -460,6 +473,7 @@ void JoltBody3D::_mode_changed() {
460473
_update_object_layer();
461474
_update_kinematic_transform();
462475
_update_mass_properties();
476+
_update_sleep_allowed();
463477
wake_up();
464478
}
465479

@@ -478,6 +492,7 @@ void JoltBody3D::_space_changing() {
478492

479493
_destroy_joint_constraints();
480494
_exit_all_areas();
495+
_dequeue_call_queries();
481496
}
482497

483498
void JoltBody3D::_space_changed() {
@@ -486,9 +501,8 @@ void JoltBody3D::_space_changed() {
486501
_update_kinematic_transform();
487502
_update_group_filter();
488503
_update_joint_constraints();
504+
_update_sleep_allowed();
489505
_areas_changed();
490-
491-
sync_state = false;
492506
}
493507

494508
void JoltBody3D::_areas_changed() {
@@ -519,11 +533,18 @@ void JoltBody3D::_axis_lock_changed() {
519533

520534
void JoltBody3D::_contact_reporting_changed() {
521535
_update_possible_kinematic_contacts();
536+
_update_sleep_allowed();
537+
wake_up();
538+
}
539+
540+
void JoltBody3D::_sleep_allowed_changed() {
541+
_update_sleep_allowed();
522542
wake_up();
523543
}
524544

525545
JoltBody3D::JoltBody3D() :
526-
JoltShapedObject3D(OBJECT_TYPE_BODY) {
546+
JoltShapedObject3D(OBJECT_TYPE_BODY),
547+
call_queries_element(this) {
527548
}
528549

529550
JoltBody3D::~JoltBody3D() {
@@ -573,7 +594,7 @@ Variant JoltBody3D::get_state(PhysicsServer3D::BodyState p_state) const {
573594
return is_sleeping();
574595
}
575596
case PhysicsServer3D::BODY_STATE_CAN_SLEEP: {
576-
return can_sleep();
597+
return is_sleep_allowed();
577598
}
578599
default: {
579600
ERR_FAIL_V_MSG(Variant(), vformat("Unhandled body state: '%d'. This should not happen. Please report this.", p_state));
@@ -596,7 +617,7 @@ void JoltBody3D::set_state(PhysicsServer3D::BodyState p_state, const Variant &p_
596617
set_is_sleeping(p_value);
597618
} break;
598619
case PhysicsServer3D::BODY_STATE_CAN_SLEEP: {
599-
set_can_sleep(p_value);
620+
set_is_sleep_allowed(p_value);
600621
} break;
601622
default: {
602623
ERR_FAIL_MSG(vformat("Unhandled body state: '%d'. This should not happen. Please report this.", p_state));
@@ -712,6 +733,10 @@ bool JoltBody3D::is_sleeping() const {
712733
return !body->IsActive();
713734
}
714735

736+
bool JoltBody3D::is_sleep_actually_allowed() const {
737+
return sleep_allowed && !(is_kinematic() && reports_contacts());
738+
}
739+
715740
void JoltBody3D::set_is_sleeping(bool p_enabled) {
716741
if (!in_space()) {
717742
sleep_initially = p_enabled;
@@ -727,27 +752,14 @@ void JoltBody3D::set_is_sleeping(bool p_enabled) {
727752
}
728753
}
729754

730-
bool JoltBody3D::can_sleep() const {
731-
if (!in_space()) {
732-
return jolt_settings->mAllowSleeping;
733-
}
734-
735-
const JoltReadableBody3D body = space->read_body(jolt_id);
736-
ERR_FAIL_COND_V(body.is_invalid(), false);
737-
738-
return body->GetAllowSleeping();
739-
}
740-
741-
void JoltBody3D::set_can_sleep(bool p_enabled) {
742-
if (!in_space()) {
743-
jolt_settings->mAllowSleeping = p_enabled;
755+
void JoltBody3D::set_is_sleep_allowed(bool p_enabled) {
756+
if (sleep_allowed == p_enabled) {
744757
return;
745758
}
746759

747-
const JoltWritableBody3D body = space->write_body(jolt_id);
748-
ERR_FAIL_COND(body.is_invalid());
760+
sleep_allowed = p_enabled;
749761

750-
body->SetAllowSleeping(p_enabled);
762+
_sleep_allowed_changed();
751763
}
752764

753765
Basis JoltBody3D::get_principal_inertia_axes() const {
@@ -1187,11 +1199,7 @@ void JoltBody3D::remove_joint(JoltJoint3D *p_joint) {
11871199
_joints_changed();
11881200
}
11891201

1190-
void JoltBody3D::call_queries(JPH::Body &p_jolt_body) {
1191-
if (!sync_state) {
1192-
return;
1193-
}
1194-
1202+
void JoltBody3D::call_queries() {
11951203
if (custom_integration_callback.is_valid()) {
11961204
const Variant direct_state_variant = get_direct_state();
11971205
const Variant *args[2] = { &direct_state_variant, &custom_integration_userdata };
@@ -1218,16 +1226,14 @@ void JoltBody3D::call_queries(JPH::Body &p_jolt_body) {
12181226
ERR_PRINT_ONCE(vformat("Failed to call state synchronization callback for '%s'. It returned the following error: '%s'.", to_string(), Variant::get_callable_error_text(state_sync_callback, args, 1, ce)));
12191227
}
12201228
}
1221-
1222-
sync_state = false;
12231229
}
12241230

12251231
void JoltBody3D::pre_step(float p_step, JPH::Body &p_jolt_body) {
12261232
JoltObject3D::pre_step(p_step, p_jolt_body);
12271233

12281234
switch (mode) {
12291235
case PhysicsServer3D::BODY_MODE_STATIC: {
1230-
_pre_step_static(p_step, p_jolt_body);
1236+
// Will never happen.
12311237
} break;
12321238
case PhysicsServer3D::BODY_MODE_RIGID:
12331239
case PhysicsServer3D::BODY_MODE_RIGID_LINEAR: {

0 commit comments

Comments
 (0)