ComfyUIのUpscaleがMac MPSで壊れる問題をcontiguousで直した
目次
AntiGravityで出した640px画像をComfyUIで一括拡大していたら、出力が一部ガチで崩壊した。
最初はアップスケーラモデルの相性だと思っていたけど、実際はもっと手前のメモリレイアウトが原因だった。
2026-04-29追記。ComfyUI を更新したら案の定この修正が上書きされて再発した。
同じ1行を再適用したら直ったが、ついでに上流の PyTorch issue(#169342)が出ていたこと、再発時の再現手順、再発を検知する小さな運用メモを末尾に足した。
何が起きていたか
再現条件は以下だった。
- Mac(Apple Silicon)
- ComfyUIの
Upscale Image (using Model) Load Imageから入った画像をそのままUpscale
同じUpscaleでも、生成フロー末尾の画像は壊れにくいのに、Load Image 起点だけ壊れる。
この差を追ったら、原因箇所がはっきりした。
原因
ComfyUIの画像テンソルは基本 BHWC。Upscaleノード内部ではESRGAN系モデルに渡すため BCHW に並べ替える。
この並べ替え後テンソルが non-contiguous のまま conv2d 側に渡るケースがあり、MPS環境で破綻する。
ポイントは tiled_scale_multidim 側でタイルを narrow() した s_in を、そのまま function(s_in) に渡していたこと。
実際に当てた修正
comfy/utils.py の該当行を次のように変更した。
# before
ps = function(s_in).to(output_device)
# after
ps = function(s_in.contiguous()).to(output_device)
この1行で、壊れていた出力が正常化した。
関連Issueはこれ。この記事を書いている時点でまだオープン。
https://github.com/comfyanonymous/ComfyUI/issues/11851
パッチ手順
cd ~/ComfyUI
source venv/bin/activate
# 念のためバックアップ
cp comfy/utils.py comfy/utils.py.bak
# 手修正(エディタで以下を置換)
# ps = function(s_in).to(output_device)
# ->
# ps = function(s_in.contiguous()).to(output_device)
# ComfyUI再起動
python main.py
補足
--force-fp32 や PYTORCH_ENABLE_MPS_FALLBACK=0 は切り分けとしては有効だった。
ただ、今回の本命は精度設定よりメモリレイアウトだった。
なので「MPSだから壊れる」というより、「non-contiguous入力を踏む経路だとMPSで壊れやすい」のほうが実態に近い。
運用メモ
ComfyUIを更新すると comfy/utils.py の手修正は上書きされる。
更新後に再発したら、同じ1行を再適用する。
同様の壊れ方が Sharpen や他の BHWC -> BCHW 変換ノードで出るなら、permute/movedim 直後に contiguous() を挟むと再発防止しやすい。
再発した(2026-04-29追記)
漫画調モノクロのワークフロー(カラー画像→背景除去(任意)→グレースケール+ハーフトーン+ラインアート抽出→4x-UltraSharpで拡大)を回したら、拡大結果が水平ノイズに化けた。直前の画像処理は正常で、ImageUpscaleWithModel 直後の出力だけが死んでいた。
| ノード [42](4x-UltraSharp直後) | 最終出力 [54] |
|---|---|
![]() | ![]() |
ワークフロー全体(FireShotで撮ったもの)はこんな感じ。

上流のPyTorch issueが立っていた
2月の時点では ComfyUI 側の issue #11851 しか見つけていなかったが、PyTorch 側にも本丸のbug reportが上がっていた。
pytorch/pytorch #169342 — MPS chunk view + conv produces incorrect results(2025-12提出、2026-04時点でopen)
tensor views created by
chunk()produce incorrect results when passed through convolution operations
chunk() だけでなく narrow() で作った非連続viewも同じ経路で踏む。ComfyUIの tiled_scale_multidim がタイル切り出しに narrow() を使っているのでドンピシャで該当する。
影響バージョン: PyTorch 2.9.0 / 2.9.1 / 2.10.0。CUDA / CPU では出ない。MPS固有。
関連するPRは #169935 として上がっているが、レビュー中かつ M5 チップ向けの限定対症療法(M5 デバイス検知時のみ contiguous を強制)なので、M1〜M4 ユーザーには上流fixが降ってこない可能性が高い。当面は手元の contiguous() パッチで凌ぐ前提でよい。
再発時の確認手順
入力依存じゃないと確信したいときの最短路。
- ワークフローの
ImageUpscaleWithModelの出力にPreviewImageを挿す - プレビューが「壊れたテレビの砂嵐」状態なら、上流ではなく拡大ノード自体が壊れている
- その状態で
comfy/utils.pyに.contiguous()が残っているか確認
PreviewImage 直挿しはGUIをいじらなくても、PNGのworkflow JSONに直接ノードを足してAPI(POST /prompt)に投げる形でも検証できる。
再発を検知する運用メモ
ComfyUI を更新するたびに毎回踏むのは無駄なので、起動前に grep する一発を ~/.zshrc あたりに置いておくと安全。
comfy_check_patch() {
if grep -q 'function(s_in.contiguous())' ~/ComfyUI/comfy/utils.py; then
echo "[ok] contiguous patch is present"
else
echo "[warn] contiguous patch is MISSING — re-apply before running on MPS"
return 1
fi
}
ComfyUI 起動ラッパーを書いている人は、その先頭で comfy_check_patch || return 1 を呼ぶようにしておくと、踏んでから気付くより早い。
パッチ適用後の出力
念のため再パッチ後のビフォーアフター。raw([42]直後)と最終([54])の両方で、画像内容が完全に復元される。
| ノード [42](4x-UltraSharp直後) | 最終出力 [54] |
|---|---|
![]() | ![]() |



