Skip to content

Commit 016a28a

Browse files
committed
Move hibernation to store persistent island by daisy-chaining entity indices
using ContactIsland.entity_idx_to_next_entity_idx_in_hibernated_island
1 parent 87372f6 commit 016a28a

File tree

5 files changed

+126
-87
lines changed

5 files changed

+126
-87
lines changed

genesis/engine/solvers/rigid/constraint_solver_decomp_island.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
import genesis.utils.array_class as array_class
1010

1111
from .rigid_solver_decomp_util import func_wakeup_entity_and_its_temp_island
12+
from .rigid_validate import (
13+
validate_entity_hibernation_state_for_all_entities_in_temp_island,
14+
validate_next_hibernated_entity_indices_in_entire_scene,
15+
)
1216
from .contact_island import ContactIsland
1317
from genesis.engine.solvers.rigid.rigid_debug import Debug
1418

@@ -247,6 +251,12 @@ def add_collision_constraints__and_wakeup_entities(self, i_island: int, i_b: int
247251
self.contact_island,
248252
)
249253

254+
# after all collisions added for the island
255+
if ti.static(Debug.validate):
256+
validate_entity_hibernation_state_for_all_entities_in_temp_island( \
257+
i_island, i_b, self._solver.entities_state, self.contact_island, expected_hibernation_state=False)
258+
259+
250260
@ti.func
251261
def add_joint_limit_constraints(self, i_island: int, i_b: int):
252262
for i_island_entity in range(self.contact_island.island_entity[i_island, i_b].n):

genesis/engine/solvers/rigid/contact_island.py

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55

66
import genesis as gs
77
import genesis.utils.geom as gu
8+
from genesis.engine.solvers.rigid.rigid_debug import Debug
9+
810

911
if TYPE_CHECKING:
1012
from genesis.engine.solvers.rigid.collider_decomp import Collider
1113
from genesis.engine.solvers.rigid.rigid_solver_decomp import RigidSolver
1214

1315
INVALID_NEXT_HIBERNATED_ENTITY_IDX = -1
14-
INVALID_HIBERNATED_ISLAND_ID = -1 # -1 is reserved for "no island", i.e. active or fixed entities
15-
FIRST_HIBERNATED_ISLAND_ID = 1 # start at 1, leave 0 unused, use -1 for "no island"
16+
17+
18+
from genesis.engine.solvers.rigid.rigid_validate import validate_next_hibernated_entity_indices_in_entire_scene
19+
1620

1721
@ti.data_oriented
1822
class ContactIsland:
@@ -87,14 +91,8 @@ def __init__(self, collider: "Collider"):
8791
#
8892

8993
# Used to make islands persist through hibernation:
90-
self.unused__entity_idx_to_next_entity_idx_in_hibernated_island = ti.field(dtype=gs.ti_int, shape=self.solver._batch_shape(self.solver.n_entities))
91-
self.unused__entity_idx_to_next_entity_idx_in_hibernated_island.fill(INVALID_NEXT_HIBERNATED_ENTITY_IDX)
92-
93-
# Warning: do not mistake island_id for island_idx
94-
self.next_hibernated_island_id = ti.field(dtype=gs.ti_int, shape=self.solver._B)
95-
self.next_hibernated_island_id.fill(FIRST_HIBERNATED_ISLAND_ID)
96-
self.hibernated_entity_idx_to_hibernated_island_id = ti.field(dtype=gs.ti_int, shape=self.solver._batch_shape(self.solver.n_entities))
97-
self.hibernated_entity_idx_to_hibernated_island_id.fill(INVALID_HIBERNATED_ISLAND_ID)
94+
self.entity_idx_to_next_entity_idx_in_hibernated_island = ti.field(dtype=gs.ti_int, shape=self.solver._batch_shape(self.solver.n_entities))
95+
self.entity_idx_to_next_entity_idx_in_hibernated_island.fill(INVALID_NEXT_HIBERNATED_ENTITY_IDX)
9896

9997
@ti.kernel
10098
def clear_island_mapping(self):
@@ -139,23 +137,27 @@ def add_all_contact_edges(self):
139137
self.add_edge(link_a, link_b, i_b)
140138

141139
@ti.kernel
142-
def temp_hack__add_all_hibernated_island_edges(self):
140+
def add_all_hibernated_island_edges(self):
143141
_B = self.solver._B
144142
n_entities = self.solver.n_entities
145143
ti.loop_config(serialize=self.solver._para_level < gs.PARA_LEVEL.ALL)
146144
for i_b in range(_B):
147145
for i_e in range(n_entities):
148-
for i_e2 in range(i_e + 1, n_entities):
149-
if self.hibernated_entity_idx_to_hibernated_island_id[i_e, i_b] != INVALID_HIBERNATED_ISLAND_ID \
150-
and self.hibernated_entity_idx_to_hibernated_island_id[i_e, i_b] == self.hibernated_entity_idx_to_hibernated_island_id[i_e2, i_b]:
151-
any_link_a = self.solver.entities_info.link_start[i_e]
152-
any_link_b = self.solver.entities_info.link_start[i_e2]
153-
self.add_edge(any_link_a, any_link_b, i_b)
146+
next_entity_idx = self.entity_idx_to_next_entity_idx_in_hibernated_island[i_e, i_b]
147+
if next_entity_idx != INVALID_NEXT_HIBERNATED_ENTITY_IDX and next_entity_idx != i_e:
148+
if ti.static(Debug.validate):
149+
island_idx_a = self.entity_island[i_e, i_b]
150+
island_idx_b = self.entity_island[next_entity_idx, i_b]
151+
Debug.assertf(0x7ad00012, island_idx_a == island_idx_b)
152+
153+
any_link_a = self.solver.entities_info.link_start[i_e]
154+
any_link_b = self.solver.entities_info.link_start[next_entity_idx]
155+
self.add_edge(any_link_a, any_link_b, i_b)
154156

155157
def construct(self):
156158
self.clear_island_mapping()
157159
self.add_all_contact_edges()
158-
self.temp_hack__add_all_hibernated_island_edges()
160+
self.add_all_hibernated_island_edges()
159161
self.preprocess_island__map_entities_to_edges()
160162
self.construct_islands()
161163
self.postprocess_island__assign_contact_data_to_temp_islands()
@@ -257,6 +259,8 @@ def construct_islands(self):
257259
self.stack[self.n_stack[i_b], i_b] = i_v
258260
self.n_stack[i_b] = self.n_stack[i_b] + 1
259261
self.entity_island[i_v, i_b] = self.n_islands[i_b]
262+
if ti.static(Debug.validate) and self.n_stack[i_b] > self.stack.shape[0]:
263+
print(f"danger: stack overflow: capacity and size: {self.stack.shape[0]} {self.n_stack[i_b]}")
260264

261265
while self.n_stack[i_b] > 0:
262266
self.n_stack[i_b] = self.n_stack[i_b] - 1
@@ -273,6 +277,8 @@ def construct_islands(self):
273277
self.stack[self.n_stack[i_b], i_b] = next_v
274278
self.n_stack[i_b] = self.n_stack[i_b] + 1
275279
self.entity_island[next_v, i_b] = self.n_islands[i_b]
280+
if ti.static(Debug.validate) and self.n_stack[i_b] > self.stack.shape[0]:
281+
print(f"danger: stack overflow: capacity and size: {self.stack.shape[0]} {self.n_stack[i_b]}")
276282

277283
self.n_islands[i_b] = self.n_islands[i_b] + 1
278284

genesis/engine/solvers/rigid/rigid_solver_decomp.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,18 @@
1818
import genesis.utils.array_class as array_class
1919
from genesis.engine.solvers.rigid.contact_island import ContactIsland
2020
from genesis.engine.solvers.rigid.rigid_debug import Debug
21-
from genesis.engine.solvers.rigid.rigid_validate import validate_entity_hibernation_state_for_all_entities_in_temp_island
21+
from genesis.engine.solvers.rigid.rigid_validate import (
22+
validate_entity_hibernation_state_for_all_entities_in_temp_island,
23+
validate_next_hibernated_entity_indices_in_entire_scene,
24+
)
2225

2326
from ..base_solver import Solver
2427
from .collider_decomp import Collider
2528
from .constraint_solver_decomp import ConstraintSolver
2629
from .constraint_solver_decomp_island import ConstraintSolverIsland
2730
from .rigid_solver_decomp_util import func_wakeup_entity_and_its_temp_island
2831
from ....utils.sdf_decomp import SDF
32+
from .contact_island import INVALID_NEXT_HIBERNATED_ENTITY_IDX
2933

3034
if TYPE_CHECKING:
3135
from genesis.engine.scene import Scene
@@ -4039,7 +4043,7 @@ def kernel_step_2(
40394043
)
40404044

40414045
if ti.static(static_rigid_sim_config.use_hibernation):
4042-
func_check_all_awake_islands_for_hibernation_or_update_aabb_sort_buffer(
4046+
func_for_all_awake_islands_either_hiberanate_or_update_aabb_sort_buffer(
40434047
dofs_state=dofs_state,
40444048
entities_state=entities_state,
40454049
entities_info=entities_info,
@@ -4050,7 +4054,7 @@ def kernel_step_2(
40504054
static_rigid_sim_config=static_rigid_sim_config,
40514055
contact_island=contact_island,
40524056
)
4053-
nondeterministic__func_aggregate_awake_entities(
4057+
func_aggregate_awake_entities(
40544058
entities_state=entities_state,
40554059
entities_info=entities_info,
40564060
rigid_global_info=rigid_global_info,
@@ -4920,7 +4924,7 @@ def kernel_update_vgeoms(
49204924

49214925

49224926
@ti.func
4923-
def func_check_all_awake_islands_for_hibernation_or_update_aabb_sort_buffer(
4927+
def func_for_all_awake_islands_either_hiberanate_or_update_aabb_sort_buffer(
49244928
dofs_state: array_class.DofsState,
49254929
entities_state: array_class.EntitiesState,
49264930
entities_info: array_class.EntitiesInfo,
@@ -4987,14 +4991,12 @@ def func_check_all_awake_islands_for_hibernation_or_update_aabb_sort_buffer(
49874991
# perform hibernation
49884992
prev_entity_ref = entity_ref_range.start + entity_ref_range.n - 1
49894993
prev_entity_idx = ci.entity_id[prev_entity_ref, i_b]
4990-
hibernated_island_id = ci.next_hibernated_island_id[i_b]
4991-
ci.next_hibernated_island_id[i_b] = hibernated_island_id + 1
4992-
# print(f"Hibernated island id: {hibernated_island_id} of {entity_ref_range.n} entities")
4994+
49934995
for i in range(entity_ref_range.n):
49944996
entity_ref = entity_ref_range.start + i
49954997
entity_idx = ci.entity_id[entity_ref, i_b]
49964998

4997-
func_mark_entity_for_hibernation_and_zero_dof_velocities(
4999+
func_hibernate_entity_and_zero_dof_velocities(
49985000
entity_idx,
49995001
i_b,
50005002
entities_state=entities_state,
@@ -5005,14 +5007,13 @@ def func_check_all_awake_islands_for_hibernation_or_update_aabb_sort_buffer(
50055007
)
50065008

50075009
# store entities in the hibernated islands by daisy chaining them
5008-
ci.unused__entity_idx_to_next_entity_idx_in_hibernated_island[prev_entity_idx, i_b] = entity_idx
5010+
Debug.assertf(0x7ad00014, ci.entity_idx_to_next_entity_idx_in_hibernated_island[prev_entity_idx, i_b] == INVALID_NEXT_HIBERNATED_ENTITY_IDX)
5011+
ci.entity_idx_to_next_entity_idx_in_hibernated_island[prev_entity_idx, i_b] = entity_idx
50095012
prev_entity_idx = entity_idx
50105013

5011-
ci.hibernated_entity_idx_to_hibernated_island_id[entity_idx, i_b] = hibernated_island_id
5012-
50135014

50145015
@ti.func
5015-
def nondeterministic__func_aggregate_awake_entities(
5016+
def func_aggregate_awake_entities(
50165017
entities_state: array_class.EntitiesState,
50175018
entities_info: array_class.EntitiesInfo,
50185019
rigid_global_info: array_class.RigidGlobalInfo,
@@ -5032,21 +5033,21 @@ def nondeterministic__func_aggregate_awake_entities(
50325033
next_awake_entity_idx = ti.atomic_add(rigid_global_info.n_awake_entities[i_b], 1)
50335034
rigid_global_info.awake_entities[next_awake_entity_idx, i_b] = i_e
50345035

5035-
dof_start: ti.int32 = entities_info.dof_start[i_e]
50365036
n_dofs = entities_info.n_dofs[i_e]
5037+
entity_dofs_base_idx: ti.int32 = entities_info.dof_start[i_e]
50375038
awake_dofs_base_idx = ti.atomic_add(rigid_global_info.n_awake_dofs[i_b], n_dofs)
50385039
for i in range(n_dofs):
5039-
rigid_global_info.awake_dofs[awake_dofs_base_idx + i, i_b] = dof_start + i
5040+
rigid_global_info.awake_dofs[awake_dofs_base_idx + i, i_b] = entity_dofs_base_idx + i
50405041

5041-
link_start: ti.int32 = entities_info.link_start[i_e]
50425042
n_links = entities_info.n_links[i_e]
5043+
entity_links_base_idx: ti.int32 = entities_info.link_start[i_e]
50435044
awake_links_base_idx = ti.atomic_add(rigid_global_info.n_awake_links[i_b], n_links)
50445045
for i in range(n_links):
5045-
rigid_global_info.awake_links[awake_links_base_idx + i, i_b] = link_start + i
5046+
rigid_global_info.awake_links[awake_links_base_idx + i, i_b] = entity_links_base_idx + i
50465047

50475048

50485049
@ti.func
5049-
def func_mark_entity_for_hibernation_and_zero_dof_velocities(
5050+
def func_hibernate_entity_and_zero_dof_velocities(
50505051
i_e: int,
50515052
i_b: int,
50525053
entities_state: array_class.EntitiesState,
Lines changed: 53 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import taichi as ti
22

33
import genesis.utils.array_class as array_class
4-
from genesis.engine.solvers.rigid.contact_island import INVALID_HIBERNATED_ISLAND_ID
5-
from genesis.engine.solvers.rigid.rigid_debug import Debug
6-
from genesis.engine.solvers.rigid.rigid_validate import validate_temp_island_contains_both_hibernated_and_awake_entities
74

5+
from genesis.engine.solvers.rigid.contact_island import INVALID_NEXT_HIBERNATED_ENTITY_IDX
6+
from genesis.engine.solvers.rigid.rigid_debug import Debug
7+
from genesis.engine.solvers.rigid.rigid_validate import (
8+
validate_temp_island_contains_both_hibernated_and_awake_entities,
9+
validate_entity_hibernation_state_for_all_entities_in_temp_island,
10+
validate_next_hibernated_entity_indices_in_entire_scene,
11+
)
812

913

1014

@@ -23,51 +27,54 @@ def func_wakeup_entity_and_its_temp_island(
2327
# Note: Original function handled non-hibernated & fixed entities.
2428
# Now, we require a properly hibernated entity to be passed in.
2529
island_idx = contact_island.entity_island[i_e, i_b]
26-
is_entity_fixed = island_idx == -1
27-
Debug.assertf(0x0ad00007, not is_entity_fixed) # Fixed entities are excluded from hibernation logic
28-
Debug.assertf(0x0ad0000b, entities_state.hibernated[i_e, i_b])
30+
Debug.assertf(0x7ad00008, not contact_island.island_hibernated[island_idx, i_b]) # Island already expected to be marked as not hibernated
2931

30-
# Note: temporarily, we have duplicated logic for hibernation: the entity_state.hibernated,
31-
# and the new contact_island's arrays that are used to store persistent_hibernated_island_id.
32-
hibernated_island_id = contact_island.hibernated_entity_idx_to_hibernated_island_id[i_e, i_b]
33-
is_entity_hibernated = hibernated_island_id != INVALID_HIBERNATED_ISLAND_ID
34-
Debug.assertf(0x0ad0000c, is_entity_hibernated) # Entityt must belong to a persistent hibernated island
32+
is_entity_fixed = island_idx == -1
33+
Debug.assertf(0x7ad00007, not is_entity_fixed) # Fixed entities are excluded from hibernation logic
34+
Debug.assertf(0x7ad0000b, entities_state.hibernated[i_e, i_b])
3535

3636
if ti.static(Debug.validate):
3737
validate_temp_island_contains_both_hibernated_and_awake_entities(island_idx, i_b, entities_state, contact_island)
3838

39-
n_entities = entities_info.n_links.shape[0]
40-
for entity_idx in range(n_entities):
41-
42-
43-
if contact_island.hibernated_entity_idx_to_hibernated_island_id[entity_idx, i_b] == hibernated_island_id:
44-
Debug.assertf(0x0ad0000b, entities_state.hibernated[entity_idx, i_b]) # Entity expected to be hibernated
45-
46-
contact_island.hibernated_entity_idx_to_hibernated_island_id[entity_idx, i_b] = INVALID_HIBERNATED_ISLAND_ID
47-
48-
if entities_state.hibernated[entity_idx, i_b]:
49-
entities_state.hibernated[entity_idx, i_b] = False
50-
n_awake_entities = ti.atomic_add(rigid_global_info.n_awake_entities[i_b], 1)
51-
rigid_global_info.awake_entities[n_awake_entities, i_b] = entity_idx
52-
53-
# todo: do single atomic add
54-
for i_d in range(entities_info.dof_start[entity_idx], entities_info.dof_end[entity_idx]):
55-
dofs_state.hibernated[i_d, i_b] = False
56-
n_awake_dofs = ti.atomic_add(rigid_global_info.n_awake_dofs[i_b], 1)
57-
rigid_global_info.awake_dofs[n_awake_dofs, i_b] = i_d
58-
59-
# todo: do single atomic add
60-
for i_l in range(entities_info.link_start[entity_idx], entities_info.link_end[entity_idx]):
61-
links_state.hibernated[i_l, i_b] = False
62-
n_awake_links = ti.atomic_add(rigid_global_info.n_awake_links[i_b], 1)
63-
rigid_global_info.awake_links[n_awake_links, i_b] = i_l
64-
65-
for i_g in range(entities_info.geom_start[entity_idx], entities_info.geom_end[entity_idx]):
66-
geoms_state.hibernated[i_g, i_b] = False
67-
68-
# validation only: un-hibernate the island
69-
non_persistent_hibernated_island_idx = contact_island.entity_island[entity_idx, i_b]
70-
Debug.assertf(0x0ad00008, non_persistent_hibernated_island_idx != -1) # Entity being hibernated has invalid temp island index
71-
Debug.assertf(0x0ad00009, non_persistent_hibernated_island_idx == island_idx) # Not matching island indices
72-
73-
contact_island.island_hibernated[non_persistent_hibernated_island_idx, i_b] = False
39+
entity_ref_range = contact_island.island_entity[island_idx, i_b]
40+
for ei in range(entity_ref_range.n):
41+
entity_ref = entity_ref_range.start + ei
42+
entity_idx = contact_island.entity_id[entity_ref, i_b]
43+
Debug.assertf(0x7ad0000d, contact_island.entity_island[entity_idx, i_b] == island_idx) # Unexpected entity from outside of island
44+
45+
is_entity_hibernated = entities_state.hibernated[entity_idx, i_b]
46+
next_hibernated_entity_idx = contact_island.entity_idx_to_next_entity_idx_in_hibernated_island[entity_idx, i_b]
47+
Debug.assertf(0x7ad0000e, is_entity_hibernated == (next_hibernated_entity_idx != INVALID_NEXT_HIBERNATED_ENTITY_IDX)) # Inconsistent entity state
48+
49+
if is_entity_hibernated:
50+
contact_island.entity_idx_to_next_entity_idx_in_hibernated_island[entity_idx, i_b] = INVALID_NEXT_HIBERNATED_ENTITY_IDX
51+
52+
entities_state.hibernated[entity_idx, i_b] = False
53+
n_awake_entities = ti.atomic_add(rigid_global_info.n_awake_entities[i_b], 1)
54+
rigid_global_info.awake_entities[n_awake_entities, i_b] = entity_idx
55+
56+
n_dofs = entities_info.n_dofs[entity_idx]
57+
base_entity_dof_idx = entities_info.dof_start[entity_idx]
58+
base_awake_dof_idx = ti.atomic_add(rigid_global_info.n_awake_dofs[i_b], n_dofs)
59+
for i in range(n_dofs):
60+
i_d = base_entity_dof_idx + i
61+
dofs_state.hibernated[i_d, i_b] = False
62+
rigid_global_info.awake_dofs[base_awake_dof_idx + i, i_b] = i_d
63+
64+
n_links = entities_info.n_links[entity_idx]
65+
base_entity_link_idx = entities_info.link_start[entity_idx]
66+
base_awake_link_idx = ti.atomic_add(rigid_global_info.n_awake_links[i_b], n_links)
67+
for i in range(n_links):
68+
i_l = base_entity_link_idx + i
69+
links_state.hibernated[i_l, i_b] = False
70+
rigid_global_info.awake_links[base_awake_link_idx + i, i_b] = i_l
71+
72+
for i_g in range(entities_info.geom_start[entity_idx], entities_info.geom_end[entity_idx]):
73+
geoms_state.hibernated[i_g, i_b] = False
74+
75+
# contact_island.island_hibernated[island_idx, i_b] = False
76+
if ti.static(Debug.validate):
77+
validate_entity_hibernation_state_for_all_entities_in_temp_island( \
78+
island_idx, i_b, entities_state, contact_island, expected_hibernation_state=False)
79+
validate_next_hibernated_entity_indices_in_entire_scene(i_b, entities_state, contact_island)
80+

0 commit comments

Comments
 (0)