Skip to main content

box3d_rust/world/
validate.rs

1// b3ValidateSolverSets from physics_world.c, split out of world/mod.rs to
2// keep modules focused.
3//
4// SPDX-FileCopyrightText: 2025 Erin Catto
5// SPDX-License-Identifier: MIT
6
7use crate::world::World;
8
9impl World {
10    /// Debug validation of solver set bookkeeping. (b3ValidateSolverSets)
11    ///
12    /// C gates this behind B3_VALIDATE; here it always runs and asserts in
13    /// debug builds (release builds compile the checks away).
14    pub fn validate_solver_sets(&self) {
15        use crate::body::body_flags;
16        use crate::broad_phase::proxy_type;
17        use crate::constants::GRAPH_COLOR_COUNT;
18        use crate::constraint_graph::OVERFLOW_INDEX;
19        use crate::contact::contact_flags;
20        use crate::core::NULL_INDEX;
21        use crate::solver_set::{AWAKE_SET, DISABLED_SET, FIRST_SLEEPING_SET, STATIC_SET};
22        use crate::types::BodyType;
23
24        debug_assert!(self.body_id_pool.id_capacity() == self.bodies.len() as i32);
25        debug_assert!(self.contact_id_pool.id_capacity() == self.contacts.len() as i32);
26        debug_assert!(self.joint_id_pool.id_capacity() == self.joints.len() as i32);
27        debug_assert!(self.island_id_pool.id_capacity() == self.islands.len() as i32);
28        debug_assert!(self.solver_set_id_pool.id_capacity() == self.solver_sets.len() as i32);
29
30        let mut active_set_count = 0;
31        let mut total_body_count = 0;
32        let mut total_joint_count = 0;
33        let mut total_contact_count = 0;
34        let mut total_island_count = 0;
35
36        // Validate all solver sets
37        for (set_index, set) in self.solver_sets.iter().enumerate() {
38            if set.set_index != NULL_INDEX {
39                active_set_count += 1;
40
41                if set_index == STATIC_SET as usize {
42                    debug_assert!(set.contact_indices.is_empty());
43                    debug_assert!(set.island_sims.is_empty());
44                    debug_assert!(set.body_states.is_empty());
45                } else if set_index == DISABLED_SET as usize {
46                    debug_assert!(set.island_sims.is_empty());
47                    debug_assert!(set.body_states.is_empty());
48                } else if set_index == AWAKE_SET as usize {
49                    debug_assert!(set.body_sims.len() == set.body_states.len());
50                    debug_assert!(set.joint_sims.is_empty());
51                } else {
52                    debug_assert!(set.body_states.is_empty());
53                }
54
55                // Validate bodies
56                {
57                    total_body_count += set.body_sims.len() as i32;
58                    for (local_index, body_sim) in set.body_sims.iter().enumerate() {
59                        let body_id = body_sim.body_id;
60                        debug_assert!(0 <= body_id && (body_id as usize) < self.bodies.len());
61                        let body = &self.bodies[body_id as usize];
62                        debug_assert!(body.set_index == set_index as i32);
63                        debug_assert!(body.local_index == local_index as i32);
64                        debug_assert!(body.id == body_id);
65
66                        let synced_flags = body.flags & !body_flags::BODY_TRANSIENT_FLAGS;
67                        debug_assert!((body_sim.flags & synced_flags) == synced_flags);
68
69                        if set_index == AWAKE_SET as usize {
70                            let body_state = &set.body_states[local_index];
71                            debug_assert!((body_state.flags & synced_flags) == synced_flags);
72                            let _ = body_state;
73                        }
74
75                        if body.type_ == BodyType::Dynamic {
76                            debug_assert!((body.flags & body_flags::DYNAMIC_FLAG) != 0);
77                        }
78
79                        if set_index == DISABLED_SET as usize {
80                            debug_assert!(body.head_contact_key == NULL_INDEX);
81                        }
82
83                        // Validate body shapes
84                        let mut prev_shape_id = NULL_INDEX;
85                        let mut shape_id = body.head_shape_id;
86                        while shape_id != NULL_INDEX {
87                            let shape = &self.shapes[shape_id as usize];
88                            debug_assert!(shape.id == shape_id);
89                            debug_assert!(shape.prev_shape_id == prev_shape_id);
90
91                            if set_index == DISABLED_SET as usize {
92                                debug_assert!(shape.proxy_key == NULL_INDEX);
93                            } else if set_index == STATIC_SET as usize {
94                                debug_assert!(proxy_type(shape.proxy_key) == BodyType::Static);
95                            } else {
96                                let proxy_type_ = proxy_type(shape.proxy_key);
97                                debug_assert!(
98                                    proxy_type_ == BodyType::Kinematic
99                                        || proxy_type_ == BodyType::Dynamic
100                                );
101                                let _ = proxy_type_;
102                            }
103
104                            prev_shape_id = shape_id;
105                            shape_id = shape.next_shape_id;
106                        }
107
108                        // Validate body contacts
109                        let mut contact_key = body.head_contact_key;
110                        while contact_key != NULL_INDEX {
111                            let contact_id = contact_key >> 1;
112                            let edge_index = (contact_key & 1) as usize;
113
114                            let contact = &self.contacts[contact_id as usize];
115                            debug_assert!(contact.set_index != STATIC_SET);
116                            debug_assert!(
117                                contact.edges[0].body_id == body_id
118                                    || contact.edges[1].body_id == body_id
119                            );
120                            contact_key = contact.edges[edge_index].next_key;
121                        }
122
123                        // Validate body joints
124                        let mut joint_key = body.head_joint_key;
125                        while joint_key != NULL_INDEX {
126                            let joint_id = joint_key >> 1;
127                            let edge_index = (joint_key & 1) as usize;
128
129                            let joint = &self.joints[joint_id as usize];
130
131                            let other_edge_index = edge_index ^ 1;
132                            let other_body =
133                                &self.bodies[joint.edges[other_edge_index].body_id as usize];
134
135                            if set_index == DISABLED_SET as usize
136                                || other_body.set_index == DISABLED_SET
137                            {
138                                debug_assert!(joint.set_index == DISABLED_SET);
139                            } else if set_index == STATIC_SET as usize
140                                && other_body.set_index == STATIC_SET
141                            {
142                                debug_assert!(joint.set_index == STATIC_SET);
143                            } else if body.type_ != BodyType::Dynamic
144                                && other_body.type_ != BodyType::Dynamic
145                            {
146                                debug_assert!(joint.set_index == STATIC_SET);
147                            } else if set_index == AWAKE_SET as usize {
148                                debug_assert!(joint.set_index == AWAKE_SET);
149                            } else if set_index >= FIRST_SLEEPING_SET as usize {
150                                debug_assert!(joint.set_index == set_index as i32);
151                            }
152
153                            // (b3GetJointSim)
154                            let joint_sim = if joint.set_index == AWAKE_SET {
155                                debug_assert!(
156                                    0 <= joint.color_index && joint.color_index < GRAPH_COLOR_COUNT
157                                );
158                                &self.constraint_graph.colors[joint.color_index as usize].joint_sims
159                                    [joint.local_index as usize]
160                            } else {
161                                debug_assert!(joint.color_index == NULL_INDEX);
162                                &self.solver_sets[joint.set_index as usize].joint_sims
163                                    [joint.local_index as usize]
164                            };
165                            debug_assert!(joint_sim.joint_id == joint_id);
166                            debug_assert!(joint_sim.body_id_a == joint.edges[0].body_id);
167                            debug_assert!(joint_sim.body_id_b == joint.edges[1].body_id);
168                            let _ = joint_sim;
169
170                            joint_key = joint.edges[edge_index].next_key;
171                        }
172
173                        let _ = (body, local_index);
174                    }
175                }
176
177                // Validate contacts
178                {
179                    total_contact_count += set.contact_indices.len() as i32;
180                    for (local_index, &contact_index) in set.contact_indices.iter().enumerate() {
181                        let contact = &self.contacts[contact_index as usize];
182                        if set_index == AWAKE_SET as usize {
183                            // contact should be non-touching if awake
184                            // or it could be this contact hasn't been transferred yet
185                            debug_assert!(
186                                contact.manifold_count() == 0
187                                    || (contact.flags & contact_flags::SIM_STARTED_TOUCHING) != 0
188                            );
189                        }
190                        debug_assert!(contact.set_index == set_index as i32);
191                        debug_assert!(contact.color_index == NULL_INDEX);
192                        debug_assert!(contact.local_index == local_index as i32);
193                        let _ = (contact, local_index);
194                    }
195                }
196
197                // Validate joints
198                {
199                    total_joint_count += set.joint_sims.len() as i32;
200                    for (local_index, joint_sim) in set.joint_sims.iter().enumerate() {
201                        let joint = &self.joints[joint_sim.joint_id as usize];
202                        debug_assert!(joint.set_index == set_index as i32);
203                        debug_assert!(joint.color_index == NULL_INDEX);
204                        debug_assert!(joint.local_index == local_index as i32);
205                        let _ = (joint, local_index);
206                    }
207                }
208
209                // Validate islands
210                {
211                    total_island_count += set.island_sims.len() as i32;
212                    for (local_index, island_sim) in set.island_sims.iter().enumerate() {
213                        let island = &self.islands[island_sim.island_id as usize];
214                        debug_assert!(island.set_index == set_index as i32);
215                        debug_assert!(island.local_index == local_index as i32);
216                        let _ = (island, local_index);
217                    }
218                }
219            } else {
220                debug_assert!(set.body_sims.is_empty());
221                debug_assert!(set.contact_indices.is_empty());
222                debug_assert!(set.joint_sims.is_empty());
223                debug_assert!(set.island_sims.is_empty());
224                debug_assert!(set.body_states.is_empty());
225            }
226        }
227
228        debug_assert!(active_set_count == self.solver_set_id_pool.id_count());
229        debug_assert!(total_body_count == self.body_id_pool.id_count());
230        debug_assert!(total_island_count == self.island_id_pool.id_count());
231
232        // Validate constraint graph
233        for color_index in 0..GRAPH_COLOR_COUNT {
234            let color = &self.constraint_graph.colors[color_index as usize];
235            let mut bit_count = 0;
236
237            total_contact_count += color.convex_contacts.len() as i32;
238            for (local_index, &contact_id) in color.convex_contacts.iter().enumerate() {
239                let contact = &self.contacts[contact_id as usize];
240                // contact should be touching in the constraint graph or awaiting transfer to non-touching
241                debug_assert!(
242                    contact.manifold_count() > 0
243                        || (contact.flags
244                            & (contact_flags::SIM_STOPPED_TOUCHING | contact_flags::SIM_DISJOINT))
245                            != 0
246                );
247                debug_assert!(contact.set_index == AWAKE_SET);
248                debug_assert!(contact.color_index == color_index);
249                debug_assert!(contact.local_index == local_index as i32);
250
251                if color_index < OVERFLOW_INDEX {
252                    let body_a = &self.bodies[contact.edges[0].body_id as usize];
253                    let body_b = &self.bodies[contact.edges[1].body_id as usize];
254                    debug_assert!(
255                        color.body_set.get_bit(contact.edges[0].body_id as u32)
256                            == (body_a.type_ == BodyType::Dynamic)
257                    );
258                    debug_assert!(
259                        color.body_set.get_bit(contact.edges[1].body_id as u32)
260                            == (body_b.type_ == BodyType::Dynamic)
261                    );
262
263                    bit_count += if body_a.type_ == BodyType::Dynamic {
264                        1
265                    } else {
266                        0
267                    };
268                    bit_count += if body_b.type_ == BodyType::Dynamic {
269                        1
270                    } else {
271                        0
272                    };
273                }
274                let _ = (contact, local_index);
275            }
276
277            total_contact_count += color.contacts.len() as i32;
278            for (local_index, spec) in color.contacts.iter().enumerate() {
279                let contact = &self.contacts[spec.contact_id as usize];
280                // contact should be touching in the constraint graph or awaiting transfer to non-touching
281                debug_assert!(
282                    contact.manifold_count() > 0
283                        || (contact.flags
284                            & (contact_flags::SIM_STOPPED_TOUCHING | contact_flags::SIM_DISJOINT))
285                            != 0
286                );
287                debug_assert!(contact.set_index == AWAKE_SET);
288                debug_assert!(contact.color_index == color_index);
289                debug_assert!(contact.local_index == local_index as i32);
290
291                if color_index < OVERFLOW_INDEX {
292                    let body_a = &self.bodies[contact.edges[0].body_id as usize];
293                    let body_b = &self.bodies[contact.edges[1].body_id as usize];
294                    debug_assert!(
295                        color.body_set.get_bit(contact.edges[0].body_id as u32)
296                            == (body_a.type_ == BodyType::Dynamic)
297                    );
298                    debug_assert!(
299                        color.body_set.get_bit(contact.edges[1].body_id as u32)
300                            == (body_b.type_ == BodyType::Dynamic)
301                    );
302
303                    bit_count += if body_a.type_ == BodyType::Dynamic {
304                        1
305                    } else {
306                        0
307                    };
308                    bit_count += if body_b.type_ == BodyType::Dynamic {
309                        1
310                    } else {
311                        0
312                    };
313                }
314                let _ = (contact, local_index);
315            }
316
317            total_joint_count += color.joint_sims.len() as i32;
318            for (local_index, joint_sim) in color.joint_sims.iter().enumerate() {
319                let joint = &self.joints[joint_sim.joint_id as usize];
320                debug_assert!(joint.set_index == AWAKE_SET);
321                debug_assert!(joint.color_index == color_index);
322                debug_assert!(joint.local_index == local_index as i32);
323
324                if color_index < OVERFLOW_INDEX {
325                    let body_a = &self.bodies[joint.edges[0].body_id as usize];
326                    let body_b = &self.bodies[joint.edges[1].body_id as usize];
327                    debug_assert!(
328                        color.body_set.get_bit(joint.edges[0].body_id as u32)
329                            == (body_a.type_ == BodyType::Dynamic)
330                    );
331                    debug_assert!(
332                        color.body_set.get_bit(joint.edges[1].body_id as u32)
333                            == (body_b.type_ == BodyType::Dynamic)
334                    );
335
336                    bit_count += if body_a.type_ == BodyType::Dynamic {
337                        1
338                    } else {
339                        0
340                    };
341                    bit_count += if body_b.type_ == BodyType::Dynamic {
342                        1
343                    } else {
344                        0
345                    };
346                }
347                let _ = (joint, local_index);
348            }
349
350            // Validate the bit population for this graph color
351            debug_assert!(bit_count == color.body_set.count_set_bits());
352            let _ = bit_count;
353        }
354
355        debug_assert!(total_contact_count == self.contact_id_pool.id_count());
356        debug_assert!(total_contact_count == self.broad_phase.pair_set.count());
357
358        debug_assert!(total_joint_count == self.joint_id_pool.id_count());
359
360        let _ = (
361            active_set_count,
362            total_body_count,
363            total_joint_count,
364            total_contact_count,
365            total_island_count,
366        );
367    }
368}