ComfyUIのUpscaleがMac MPSで壊れる問題をcontiguousで直した
AntiGravityで出した640px画像をComfyUIで一括拡大していたら、出力が一部ガチで崩壊した。
最初はアップスケーラモデルの相性だと思っていたけど、実際はもっと手前のメモリレイアウトが原因だった。
何が起きていたか
再現条件は以下だった。
- 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() を挟むと再発防止しやすい。