[ArtStudio M1] Device abstraction in art_pipe core — cuda/mps/cpu seam (upstream to ProArt) #356

Closed
opened 2026-07-04 05:27:27 +00:00 by spikerj · 2 comments
Owner

Sub-ticket of epic #346, Mac workstream. Foundation for #355 and the M2–M4 tickets. Based on the MPS portability audit (comment on #355).

What: art_pipe has no device abstraction — every backend independently does cuda-if-available-else-cpu and installs CUDA wheels unconditionally. Add a cuda | mps | cpu seam so macOS (Apple Silicon, PyTorch MPS) is a first-class target. This is generic, upstreamable work — plan to PR it to Eric's ProArt repo rather than fork.

Scope (exact touchpoints from the audit):

  • src/artpipe/hardware.py — add torch.backends.mps.is_available() branch to detect() (:183-207); report unified memory as VRAM; no nvidia-smi path on Darwin (:104-166)
  • src/artpipe/main.py::_check_gpu_compat (:22-70) — treat MPS as GPU-available; skip sm/TORCH_CUDA_ARCH_LIST checks on Darwin
  • src/artpipe/torch_compat.py — Darwin branch: default PyPI torch (ships MPS), no cu-index mapping
  • src/artpipe/venv_setup.py (:257-320, defaults :8-9) + bootstrap.py (:23,78) — on Darwin ignore torch_index_url, install stock torch; per-OS manifest override convention (e.g. torch_index_url_darwin: "")
  • Shared device helper (e.g. artpipe.device.resolve() returning device str + preferred dtype) and adopt it in the 13 backends that hardcode "cuda": flux_schnell.py:67, sdxl_lightning.py:58, sdxl_turbo.py:41, sd_turbo_tex.py:60, sf3d.py:44, text3d.py:62, triposr.py:46, triposg.py:46, instantmesh.py:68, shap_e.py:123, mdm.py:87 (int 0 — needs mapping), hymotion.py:101, rignet_infer.py:325; fix instantmesh.py:87 unconditional fp16
  • cv/checks.py:79-83 + cv/analysis.py:203-217 — mps branch for SigLIP/CLIP
  • Blender: src/artpipe/blender/headless/_helpers.py:137-165 — set cprefs.compute_device_type = "METAL" on macOS before refresh_devices() (today Cycles silently falls back to CPU)
  • Guard torch.cuda.empty_cache() call sites (pattern from trellis-mac patches/mps_compat.py)

Note: no Metal kernels in this ticket — CUDA-extension backends stay CUDA-only and must fail with a clear "requires CUDA" message on Mac (stub pattern from trellis-mac backends/stubs.py), not ImportError crashes.

Acceptance: on an Apple-Silicon Mac, artpipe setup installs stock torch, artpipe launch passes the GPU check reporting MPS + unified memory, a Tier-A backend (e.g. sdxl_lightning) runs on mps, a blocked backend (e.g. triposg) fails with the clear message, and Blender Cycles renders on Metal. CUDA behavior on Linux unchanged.

Sub-ticket of epic #346, Mac workstream. Foundation for #355 and the M2–M4 tickets. Based on the MPS portability audit (comment on #355). **What:** art_pipe has no device abstraction — every backend independently does `cuda-if-available-else-cpu` and installs CUDA wheels unconditionally. Add a `cuda | mps | cpu` seam so macOS (Apple Silicon, PyTorch MPS) is a first-class target. This is generic, upstreamable work — plan to PR it to Eric's ProArt repo rather than fork. **Scope (exact touchpoints from the audit):** - [ ] `src/artpipe/hardware.py` — add `torch.backends.mps.is_available()` branch to `detect()` (:183-207); report unified memory as VRAM; no `nvidia-smi` path on Darwin (:104-166) - [ ] `src/artpipe/main.py::_check_gpu_compat` (:22-70) — treat MPS as GPU-available; skip sm/`TORCH_CUDA_ARCH_LIST` checks on Darwin - [ ] `src/artpipe/torch_compat.py` — Darwin branch: default PyPI torch (ships MPS), no cu-index mapping - [ ] `src/artpipe/venv_setup.py` (:257-320, defaults :8-9) + `bootstrap.py` (:23,78) — on Darwin ignore `torch_index_url`, install stock torch; per-OS manifest override convention (e.g. `torch_index_url_darwin: ""`) - [ ] Shared device helper (e.g. `artpipe.device.resolve()` returning device str + preferred dtype) and adopt it in the 13 backends that hardcode `"cuda"`: `flux_schnell.py:67`, `sdxl_lightning.py:58`, `sdxl_turbo.py:41`, `sd_turbo_tex.py:60`, `sf3d.py:44`, `text3d.py:62`, `triposr.py:46`, `triposg.py:46`, `instantmesh.py:68`, `shap_e.py:123`, `mdm.py:87` (int `0` — needs mapping), `hymotion.py:101`, `rignet_infer.py:325`; fix `instantmesh.py:87` unconditional fp16 - [ ] `cv/checks.py:79-83` + `cv/analysis.py:203-217` — mps branch for SigLIP/CLIP - [ ] Blender: `src/artpipe/blender/headless/_helpers.py:137-165` — set `cprefs.compute_device_type = "METAL"` on macOS before `refresh_devices()` (today Cycles silently falls back to CPU) - [ ] Guard `torch.cuda.empty_cache()` call sites (pattern from trellis-mac `patches/mps_compat.py`) **Note:** no Metal kernels in this ticket — CUDA-extension backends stay CUDA-only and must fail with a clear "requires CUDA" message on Mac (stub pattern from trellis-mac `backends/stubs.py`), not ImportError crashes. **Acceptance:** on an Apple-Silicon Mac, `artpipe setup` installs stock torch, `artpipe launch` passes the GPU check reporting MPS + unified memory, a Tier-A backend (e.g. sdxl_lightning) runs on `mps`, a blocked backend (e.g. triposg) fails with the clear message, and Blender Cycles renders on Metal. CUDA behavior on Linux unchanged.
spikerj added the enhancement label 2026-07-04 05:27:27 +00:00
Author
Owner

Implemented on local branch feature/device-abstraction-mps in art_pipe (commit a186626, +384/−82 across 25 files). All scope items done: artpipe/device.py (resolve() cuda>mps>cpu + guarded empty_cache(), lazy-torch so the stdlib-only server still imports), hardware detect/live_stats MPS branch (Apple GPU + unified-memory-as-VRAM), _check_gpu_compat MPS path, Darwin torch-index handling in torch_compat/venv_setup/bootstrap (+ optional torch_index_url_darwin manifest key), all 13 hardcoded-cuda backends moved to the shared helper (incl. mdm int-device fix and instantmesh unconditional-fp16 fix), clear requires-CUDA guards on trellis/unirig/triposg/hunyuan3d_paint, SigLIP/CLIP mps branch, Blender Cycles METAL enable on macOS.

Verified on the M5 Max (128 GB): pytest 574 passed / 10 skipped / 0 failed; resolve()('mps', float16); hardware.detect() → "Apple M5 Max GPU", 131072 MB, mps_available: true; python -m artpipe serve /hardware endpoint returns the Apple GPU. Audit line-drift corrections and a real pre-existing bug fixed en route (detect() force-set cuda_available=True for any GPU entry) are noted in the commit.

Blocked on a decision: art_pipe's origin is Eric's server (clam.local:3000/EricSpiker/art_pipe) — not pushing to someone else's Gitea without Joey's go-ahead. Options: (a) push branch + open PR on Eric's Gitea if Joey has an account/fork there, (b) mirror art_pipe into git.spikersoft.com and PR there, then upstream later. Runtime model validation on MPS is #357 scope.

Working-tree note: the checkout has repo-wide CRLF noise (Windows-origin) plus an uncommitted README author-handle edit — left untouched; the commit was kept EOL-clean for upstreamability.

Implemented on local branch `feature/device-abstraction-mps` in art_pipe (commit `a186626`, +384/−82 across 25 files). All scope items done: `artpipe/device.py` (`resolve()` cuda>mps>cpu + guarded `empty_cache()`, lazy-torch so the stdlib-only server still imports), hardware detect/live_stats MPS branch (Apple GPU + unified-memory-as-VRAM), `_check_gpu_compat` MPS path, Darwin torch-index handling in torch_compat/venv_setup/bootstrap (+ optional `torch_index_url_darwin` manifest key), all 13 hardcoded-cuda backends moved to the shared helper (incl. mdm int-device fix and instantmesh unconditional-fp16 fix), clear requires-CUDA guards on trellis/unirig/triposg/hunyuan3d_paint, SigLIP/CLIP mps branch, Blender Cycles METAL enable on macOS. **Verified on the M5 Max (128 GB):** pytest 574 passed / 10 skipped / 0 failed; `resolve()` → `('mps', float16)`; `hardware.detect()` → "Apple M5 Max GPU", 131072 MB, `mps_available: true`; `python -m artpipe serve` `/hardware` endpoint returns the Apple GPU. Audit line-drift corrections and a real pre-existing bug fixed en route (`detect()` force-set `cuda_available=True` for any GPU entry) are noted in the commit. **Blocked on a decision:** art_pipe's `origin` is Eric's server (`clam.local:3000/EricSpiker/art_pipe`) — not pushing to someone else's Gitea without Joey's go-ahead. Options: (a) push branch + open PR on Eric's Gitea if Joey has an account/fork there, (b) mirror art_pipe into git.spikersoft.com and PR there, then upstream later. Runtime model validation on MPS is #357 scope. Working-tree note: the checkout has repo-wide CRLF noise (Windows-origin) plus an uncommitted README author-handle edit — left untouched; the commit was kept EOL-clean for upstreamability.
Author
Owner

Resolved in spikersoft-artpipe PR #1 (merged to main — ProArt now lives at spikerj/spikersoft-artpipe per the mirror decision). The cuda/mps/cpu device seam: artpipe.device.resolve() adopted across all 13 hardcoded-cuda backends, MPS-aware hardware detect/live_stats/_check_gpu_compat, Darwin torch installs (stock PyPI, manifest override key), clear requires-CUDA guards on trellis/unirig/triposg/hunyuan3d_paint, SigLIP/CLIP mps, Blender Cycles METAL enable. Validated across six doc slices on the M5; 682 pytest green at merge; CUDA behavior unchanged. Upstreaming to Eric's original repo is now optional/at-leisure since ProArt is consumed as SpikerSoft work. Closing.

Resolved in spikersoft-artpipe PR #1 (merged to `main` — ProArt now lives at `spikerj/spikersoft-artpipe` per the mirror decision). The cuda/mps/cpu device seam: `artpipe.device.resolve()` adopted across all 13 hardcoded-cuda backends, MPS-aware hardware detect/live_stats/_check_gpu_compat, Darwin torch installs (stock PyPI, manifest override key), clear requires-CUDA guards on trellis/unirig/triposg/hunyuan3d_paint, SigLIP/CLIP mps, Blender Cycles METAL enable. Validated across six doc slices on the M5; 682 pytest green at merge; CUDA behavior unchanged. Upstreaming to Eric's original repo is now optional/at-leisure since ProArt is consumed as SpikerSoft work. Closing.
Sign in to join this conversation.