Skip to content

Commit a74efed

Browse files
authored
Reapply "[FEATURE] Avoid manually sync between torch and quadrants on Metal. (#2760)" (#2794)
1 parent 7002aab commit a74efed

6 files changed

Lines changed: 17 additions & 44 deletions

File tree

genesis/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,21 @@ def init(
255255
random_seed=seed,
256256
)
257257

258+
# On Metal, share PyTorch MPS's command queue with Quadrants so that GPU-side ordering is automatic and the
259+
# per-interop-point sync overhead (qd.sync / torch.mps.synchronize) is eliminated.
260+
if backend == _gs_backend.metal and device.type == "mps":
261+
mps_queue = qd.interop.get_mps_command_queue()
262+
if not mps_queue:
263+
raise_exception(
264+
"Failed to extract PyTorch MPS's Metal command queue. This is required on Apple Metal for correct "
265+
"GPU synchronisation between Genesis and PyTorch. Please ensure you are using a supported PyTorch "
266+
"version (>= 2.0)."
267+
)
268+
qd_init_kwargs.update(
269+
external_metal_command_queue=mps_queue,
270+
external_metal_command_queue_is_torch_queue=True,
271+
)
272+
258273
# init quadrants
259274
qd_debug = debug and (os.environ.get("QD_DEBUG") != "0")
260275
with redirect_stdout(_qd_outputs):

genesis/engine/solvers/kinematic_solver.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -838,8 +838,6 @@ def set_qpos(self, qpos, qs_idx=None, envs_idx=None, *, skip_forward=False):
838838
assign_indexed_tensor(data, mask, qpos)
839839
if mask and isinstance(mask[0], torch.Tensor):
840840
envs_idx = mask[0].reshape((-1,))
841-
if gs.backend == gs.metal:
842-
torch.mps.synchronize()
843841
else:
844842
qpos, qs_idx, envs_idx = self._sanitize_io_variables(
845843
qpos, qs_idx, self.n_qs, "qs_idx", envs_idx, skip_allocation=True
@@ -906,8 +904,6 @@ def set_dofs_velocity(self, velocity, dofs_idx=None, envs_idx=None, *, skip_forw
906904
assign_indexed_tensor(vel, mask, velocity)
907905
if mask and isinstance(mask[0], torch.Tensor):
908906
envs_idx = mask[0].reshape((-1,))
909-
if gs.backend == gs.metal:
910-
torch.mps.synchronize()
911907
if not skip_forward and not isinstance(envs_idx, torch.Tensor):
912908
envs_idx = self._scene._sanitize_envs_idx(envs_idx)
913909
else:

genesis/engine/solvers/rigid/collider/collider.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -585,8 +585,6 @@ def reset(self, envs_idx=None, *, cache_only: bool = True) -> None:
585585
normal.zero_()
586586
else:
587587
normal[:, envs_idx] = 0.0
588-
if gs.backend == gs.metal:
589-
torch.mps.synchronize()
590588
return
591589

592590
envs_idx = self._solver._scene._sanitize_envs_idx(envs_idx)
@@ -640,8 +638,6 @@ def clear(self, envs_idx=None):
640638
pos[:, envs_idx] = 0.0
641639
normal[:, envs_idx] = 0.0
642640
force[:, envs_idx] = 0.0
643-
if gs.backend == gs.metal:
644-
torch.mps.synchronize()
645641
return
646642

647643
if not isinstance(envs_idx, torch.Tensor):

genesis/engine/solvers/rigid/constraint/solver.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,6 @@ def reset(self, envs_idx=None):
152152
else:
153153
is_warmstart[envs_idx] = False
154154
qacc_ws[:, envs_idx] = 0.0
155-
if gs.backend == gs.metal:
156-
torch.mps.synchronize()
157155
return
158156

159157
envs_idx = self._solver._scene._sanitize_envs_idx(envs_idx)
@@ -186,8 +184,6 @@ def clear(self, envs_idx=None):
186184
assign_indexed_tensor(n_constraints_equality, env_mask, 0)
187185
assign_indexed_tensor(n_constraints_frictionloss, env_mask, 0)
188186
assign_indexed_tensor(qd_n_equalities, env_mask, n_eq)
189-
if gs.backend == gs.metal:
190-
torch.mps.synchronize()
191187
return
192188

193189
if not isinstance(envs_idx, torch.Tensor):

genesis/engine/solvers/rigid/rigid_solver.py

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,8 +1592,6 @@ def set_state(self, f, state, envs_idx=None, *, partial: bool = False) -> None:
15921592
mass_dst[envs_idx] = state.mass_shift[envs_idx]
15931593
if self.n_geoms:
15941594
fric_dst[envs_idx] = state.friction_ratio[envs_idx]
1595-
if gs.backend == gs.metal:
1596-
torch.mps.synchronize()
15971595
else:
15981596
envs_idx = self._scene._sanitize_envs_idx(envs_idx)
15991597
kernel_set_zero(envs_idx, self._errno)
@@ -1720,8 +1718,6 @@ def set_base_links_pos(self, pos, links_idx=None, envs_idx=None, *, relative=Fal
17201718
target = data[:, link.q_start : link.q_start + 3]
17211719
pos = broadcast_tensor(pos, gs.tc_float, target.shape)
17221720
torch.where(envs_idx[:, None], pos, target, out=target)
1723-
if gs.backend == gs.metal:
1724-
torch.mps.synchronize()
17251721
else:
17261722
pos, links_idx, envs_idx = self._sanitize_io_variables(
17271723
pos, links_idx, self.n_links, "links_idx", envs_idx, (3,), skip_allocation=True
@@ -1819,8 +1815,6 @@ def set_base_links_quat(self, quat, links_idx=None, envs_idx=None, *, relative=F
18191815
target = data[:, link.q_start + 3 : link.q_start + 7]
18201816
quat = broadcast_tensor(quat, gs.tc_float, target.shape)
18211817
torch.where(envs_idx[:, None], quat, target, out=target)
1822-
if gs.backend == gs.metal:
1823-
torch.mps.synchronize()
18241818
else:
18251819
quat, links_idx, envs_idx = self._sanitize_io_variables(
18261820
quat, links_idx, self.n_links, "links_idx", envs_idx, (4,), skip_allocation=True
@@ -1939,8 +1933,6 @@ def set_links_inertia(self, ratio, links_idx=None, envs_idx=None):
19391933
assign_indexed_tensor(mass_data, mask, mass_data[mask] * ratio_t)
19401934
assign_indexed_tensor(inertial_i_data, mask, inertial_i_data[mask] * ratio_t[..., None, None])
19411935
assign_indexed_tensor(invweight_data, mask, invweight_data[mask] / ratio_t[..., None])
1942-
if gs.backend == gs.metal:
1943-
torch.mps.synchronize()
19441936
return
19451937

19461938
ratio, links_idx, envs_idx = self._sanitize_io_variables(
@@ -1995,8 +1987,6 @@ def set_qpos(self, qpos, qs_idx=None, envs_idx=None, *, skip_forward=False):
19951987
errno[envs_idx] = 0
19961988
if mask and isinstance(mask[0], torch.Tensor):
19971989
envs_idx = mask[0].reshape((-1,))
1998-
if gs.backend == gs.metal:
1999-
torch.mps.synchronize()
20001990
else:
20011991
qpos, qs_idx, envs_idx = self._sanitize_io_variables(
20021992
qpos, qs_idx, self.n_qs, "qs_idx", envs_idx, skip_allocation=True
@@ -2140,8 +2130,6 @@ def _set_dofs_info(self, tensor_list, dofs_idx, name, envs_idx=None):
21402130
num_values = len(tensor_list)
21412131
for j, mask_j in enumerate(((*mask, ..., j) for j in range(num_values)) if num_values > 1 else (mask,)):
21422132
assign_indexed_tensor(data, mask_j, tensor_list[j])
2143-
if gs.backend == gs.metal:
2144-
torch.mps.synchronize()
21452133
return
21462134

21472135
tensor_list = list(tensor_list)
@@ -2243,8 +2231,6 @@ def set_dofs_position(self, position, dofs_idx=None, envs_idx=None):
22432231
if gs.use_zerocopy:
22442232
errno = qd_to_torch(self._errno, copy=False)
22452233
errno[envs_idx] = 0
2246-
if gs.backend == gs.metal:
2247-
torch.mps.synchronize()
22482234
else:
22492235
kernel_set_zero(envs_idx, self._errno)
22502236

@@ -2272,8 +2258,6 @@ def control_dofs_force(self, force, dofs_idx=None, envs_idx=None):
22722258
ctrl_mode[mask] = gs.CTRL_MODE.FORCE
22732259
ctrl_force = qd_to_torch(self.dofs_state.ctrl_force, transpose=True, copy=False)
22742260
assign_indexed_tensor(ctrl_force, mask, force)
2275-
if gs.backend == gs.metal:
2276-
torch.mps.synchronize()
22772261
return
22782262

22792263
force, dofs_idx, envs_idx = self._sanitize_io_variables(
@@ -2293,8 +2277,6 @@ def control_dofs_velocity(self, velocity, dofs_idx=None, envs_idx=None):
22932277
ctrl_pos[mask] = 0.0
22942278
ctrl_vel = qd_to_torch(self.dofs_state.ctrl_vel, transpose=True, copy=False)
22952279
assign_indexed_tensor(ctrl_vel, mask, velocity)
2296-
if gs.backend == gs.metal:
2297-
torch.mps.synchronize()
22982280
return
22992281

23002282
velocity, dofs_idx, envs_idx = self._sanitize_io_variables(
@@ -2314,8 +2296,6 @@ def control_dofs_position(self, position, dofs_idx=None, envs_idx=None):
23142296
assign_indexed_tensor(ctrl_pos, mask, position)
23152297
ctrl_vel = qd_to_torch(self.dofs_state.ctrl_vel, transpose=True, copy=False)
23162298
ctrl_vel[mask] = 0.0
2317-
if gs.backend == gs.metal:
2318-
torch.mps.synchronize()
23192299
return
23202300

23212301
position, dofs_idx, envs_idx = self._sanitize_io_variables(
@@ -2335,8 +2315,6 @@ def control_dofs_position_velocity(self, position, velocity, dofs_idx=None, envs
23352315
assign_indexed_tensor(ctrl_pos, mask, position)
23362316
ctrl_vel = qd_to_torch(self.dofs_state.ctrl_vel, transpose=True, copy=False)
23372317
assign_indexed_tensor(ctrl_vel, mask, velocity)
2338-
if gs.backend == gs.metal:
2339-
torch.mps.synchronize()
23402318
return
23412319

23422320
position, dofs_idx, _ = self._sanitize_io_variables(
@@ -2672,8 +2650,6 @@ def clear_external_force(self):
26722650
for tensor in (self.links_state.cfrc_applied_ang, self.links_state.cfrc_applied_vel):
26732651
out = qd_to_torch(tensor, copy=False)
26742652
out.zero_()
2675-
if gs.backend == gs.metal:
2676-
torch.mps.synchronize()
26772653
return
26782654

26792655
kernel_clear_external_force(self.links_state, self._rigid_global_info, self._static_rigid_sim_config)

genesis/utils/misc.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -568,14 +568,8 @@ def qd_to_torch(
568568
tensor = value._T_tc if transpose else value._tc
569569
is_copy = False
570570

571-
if not is_copy:
572-
# FIXME: DLPack may return old values on Apple Metal if sync is not systematically called manually
573-
if gs.backend == gs.metal:
574-
qd.sync()
575-
if copy:
576-
tensor = tensor.clone()
577-
if gs.backend == gs.metal:
578-
torch.mps.synchronize()
571+
if copy and not is_copy:
572+
tensor = tensor.clone()
579573

580574
return _apply_masks(tensor, value, row_mask, col_mask, keepdim, copy, to_torch=True)
581575

0 commit comments

Comments
 (0)