MPS scatter/index_put produces out-of-range indices on large meshes (RemeshMesh + DecimateMesh, Trellis2/Pixal3D workflow)
- Dominant language
- Python
- Stars
- 133k
- Forks
- 15.7k
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 155
Description
### Custom Node Testing
- [ ] I have tried disabling custom nodes and the issue persists (see [how to disable custom nodes](https://docs.comfy.org/troubleshooting/custom-node-issues#step-1%3A-test-with-all-custom-nodes-disabled) if you need help)
### Expected Behavior
RemeshMesh and DecimateMesh should complete successfully on MPS, the same way they do on CPU with identical input. Forcing compute to CPU produces correct output on the same mesh.
### Actual Behavior
Summary
Running the native Trellis2/Pixal3D image-to-3D template (added in v0.34.0) on Apple Silicon (MPS), two independent mesh post-processing nodes fail with the same error signature: torch.AcceleratorError: scatter: index N is out of bounds for dimension ... with size M, where N is negative. This happened in RemeshMesh (dual contouring, comfy_extras/mesh3d/postprocess/remesh.py) and separately in DecimateMesh (QEM simplification, comfy_extras/mesh3d/postprocess/qem_decimate.py), on meshes in the 3-8 million vertex/face range.
In both cases, forcing the node's compute device to CPU instead of MPS (comfy.model_management.get_torch_device()) avoids the error entirely, and produced correct output. On my hardware (Apple M5 Max, 128GB), CPU execution was not slower — for RemeshMesh it was faster than the MPS run had been getting through before it crashed (29/29 iterations in 17s on CPU vs. crashing at 10/29 after ~7s on MPS).
I believe this points to a PyTorch MPS backend bug (likely in scatter/index_put_ on large tensors) rather than a bug in either mesh algorithm, since the two functions are independent implementations (dual contouring vs. QEM edge collapse) hitting the identical symptom. Happy to open a corresponding issue against pytorch/pytorch if that's judged to be the better venue — filing here first since this is where I have full repro context.
### Steps to Reproduce
Load the native Trellis2/Pixal3D image-to-3D template workflow, run it on Apple Silicon (MPS) with a large output mesh (millions of vertices/faces), and either RemeshMesh or DecimateMesh will fail. Mention your specific settings if the template box asks for exact widget values: RemeshMesh with sign_mode=udf, qef=false, resolution=768.
### Debug Logs
```powershell
Reproduction 1: RemeshMesh
Node ID: 241, Node Type: RemeshMesh Exception: torch.AcceleratorError: scatter: index -56 is out of bounds for dimension with size 4746059
Mesh going in: 8,469,451 vertices / 16,774,830 faces (Trellis2 output, sign_mode=udf, qef=false, resolution=768).
Traceback:
File "/ComfyUI/execution.py", line 545, in execute
File "/ComfyUI/execution.py", line 344, in get_output_data
File "/ComfyUI/execution.py", line 318, in _async_map_node_over_list
File "/ComfyUI/execution.py", line 306, in process_inputs
File "/ComfyUI/comfy_api/internal/__init__.py", line 149, in wrapped_func
File "/ComfyUI/comfy_api/latest/_io.py", line 2025, in EXECUTE_NORMALIZED
File "/ComfyUI/comfy_extras/nodes_mesh_postprocess.py", line 2429, in execute
result = _process_mesh_batch(mesh, _fn)
File "/ComfyUI/comfy_extras/nodes_mesh_postprocess.py", line 2185, in _process_mesh_batch
v_i, f_i, c_i = process_single(v_i, f_i, c_i, bar)
File "/ComfyUI/comfy_extras/nodes_mesh_postprocess.py", line 2163, in process_single
v, f, c = per_item_fn(v, f, c)
File "/ComfyUI/comfy_extras/nodes_mesh_postprocess.py", line 2411, in _fn
rv, rf, rc = remesh_narrow_band_dc(
File "/ComfyUI/comfy_extras/mesh3d/postprocess/remesh.py", line 1140, in remesh_narrow_band_dc
dual_verts = dual_verts[used]
torch.AcceleratorError: scatter: index -56 is out of bounds for dimension with size 4746059
Note: line 1140 (dual_verts[used]) is boolean-mask indexing, not a scatter — the actual failing op is almost certainly one of the used[new_faces[:, N]] = True index_put_ assignments a few lines above, and MPS's async dispatch is surfacing the error at the next sync point rather than at the actual failing call.
Reproduction 2: DecimateMesh
Node ID: 186, Node Type: DecimateMesh Exception: torch.AcceleratorError: scatter: index -682 is out of bounds for dimension 0 with size 3144119
Same source mesh, after a successful (CPU-patched) RemeshMesh pass, going into QEM decimation.
Traceback:
File "/ComfyUI/execution.py", line 545, in execute
File "/ComfyUI/execution.py", line 344, in get_output_data
File "/ComfyUI/execution.py", line 318, in _async_map_node_over_list
File "/ComfyUI/execution.py", line 306, in process_inputs
File "/ComfyUI/comfy_api/internal/__init__.py", line 149, in wrapped_func
File "/ComfyUI/comfy_api/latest/_io.py", line 2025, in EXECUTE_NORMALIZED
File "/ComfyUI/comfy_extras/nodes_mesh_postprocess.py", line 2315, in execute
result = _process_mesh_batch(mesh, _fn)
File "/ComfyUI/comfy_extras/nodes_mesh_postprocess.py", line 2185, in _process_mesh_batch
v_i, f_i, c_i = process_single(v_i, f_i, c_i, bar)
File "/ComfyUI/comfy_extras/nodes_mesh_postprocess.py", line 2163, in process_single
v, f, c = per_item_fn(v, f, c)
File "/ComfyUI/comfy_extras/nodes_mesh_postprocess.py", line 2304, in _fn
rv, rf, rc, _rn, _rs = qem_decimate_simplify(
File "/ComfyUI/comfy_extras/mesh3d/postprocess/qem_decimate.py", line 1633, in qem_decimate_simplify
return qem_simplify(vertices, faces, target, colors, normals, max_edge_length, config)
File "/ComfyUI/comfy_extras/mesh3d/postprocess/qem_decimate.py", line 1278, in qem_simplify
thresh = float(cfg.threshold_start) * float(mesh_scale_sq) if cfg.threshold_driver else 0.0
torch.AcceleratorError: scatter: index -682 is out of bounds for dimension 0 with size 3144119
Same pattern here: line 1278 is a scalar float conversion, not a scatter — again consistent with MPS's error reporting lagging behind the actual failing async op.
Workaround used
Patched both RemeshMesh.execute and DecimateMesh.execute in comfy_extras/nodes_mesh_postprocess.py to force CPU when the detected device is MPS:
compute_device = torch.device('cpu') if comfy.model_management.get_torch_device().type == 'mps' else comfy.model_management.get_torch_device()
```
### Other
Both nodes ran successfully with no other changes once compute was forced to CPU. Happy to provide the full workflow JSON or further logs if useful — I removed some surrounding log noise (custom node import warnings unrelated to this issue) from the tracebacks above for readability, but have the complete ComfyUI error reports if needed.
Contributor guide
Research direction
Start with RemeshMesh.execute and DecimateMesh.execute in comfy_extras/nodes_mesh_postprocess.py, then inspect remesh.py and qem_decimate.py at the reported operations. Reproduce the Trellis2/Pixal3D workflow on MPS with the stated mesh sizes and compare it with CPU execution. Done means the failure is reproduced and its ownership or handling in ComfyUI versus PyTorch is established.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos, python, pytorch
- Domain
- backend, computer-graphics, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 44/100