aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
blob: d52dbebcaa0bf06824aea34a3a31c6fc0ddde58e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# imggen

Local SDXL image generation on an Intel Arc B580 (12GB) under Slackware, via a
container that carries the Intel Level Zero runtime the host distro does not
package. Prompt in, PNG on the host out.

## Why this exists (and its limits)

The Arc B580 runs SDXL's *compute* fast (~31 UNet it/s warm), but its *memory*
is tight. Stock SDXL fp16 OOMs at VAE decode on 12GB because diffusers upcasts
the VAE to fp32. Swapping in the fp16-fix VAE removes that upcast and the whole
pipeline fits, at roughly 6.3s per warm 1024x1024 image, 25 steps.

Measured peak VRAM for the working config is 9.07 GB on a 12GB card, so there
is roughly 2.5-3 GB of real headroom, not zero.

Known to work:

- SDXL base, single image, 1024x1024, ~25 steps. ~6.3s warm.

Plausibly fits in the remaining headroom (UNTESTED, try before assuming):

- A second batched image, or modest resolution bumps above 1024.
- Light img2img.

Probably does NOT fit (a second resident model exceeds ~3GB):

- SDXL refiner co-resident with base.

Does NOT fit:

- Flux (any variant). Far heavier than the available margin.

If you want the refiner or Flux, that is the signal to move that tier of work
to a RunPod serverless endpoint rather than fighting 12GB. Local covers the
light case comfortably and may stretch a little past it; it does not reach the
heavy tier.

## Build

The Dockerfile is PINNED to the exact versions captured from the known-good
test container (torch 2.12.1+xpu, diffusers 0.39.0, transformers 5.13.0, etc.,
on oneAPI 2025.3.2 / Ubuntu 24.04.4). These produced ~6.3s warm SDXL-base
images with 9.07 GB peak VRAM on an Arc B580.

Build:

```
docker build -t imggen:local .
```

If the base image tag `intel/oneapi-basekit:2025.3.2-0-devel-ubuntu24.04` is
not available on Docker Hub, substitute the closest 2025.3.x tag and record
what you used. Do not fall back to `:latest`.

## Install the wrapper

```
cp imggen ~/bin/imggen      # or anywhere on your PATH
chmod +x ~/bin/imggen
```

## Usage

```
imggen "a red bicycle against a stone wall"
imggen "a foggy harbour at dawn" --steps 30 --seed 42
imggen "portrait of a fox" -o fox.png -n "blurry, low quality"
```

Output lands in `~/imggen-out` by default. Models cache in
`~/.cache/imggen-models` so the ~7.5GB download happens once.

Override paths with env vars:

```
IMGGEN_OUT=/srv/blog/img IMGGEN_CACHE=/mnt/models imggen "a lighthouse"
```

## First run

The first invocation downloads SDXL base + the fp16-fix VAE (~7.5GB) into the
cache volume. That run is network-bound and slow. Subsequent runs skip the
download. The first *generation* in any fresh container is also slower (~7.3s)
due to one-time kernel compilation; warm runs settle to ~6.3s.

## Upgrading

Treat upgrades as deliberate, tested events. Do not `pip install -U` casually.
This working state depends on a specific torch-XPU build, a specific diffusers
version, and the fp16-fix VAE. If you rebuild with newer versions, keep the old
pinned Dockerfile in git so you can revert when something breaks.

## Trial

This is a time-boxed experiment. Run it against real blog work for a few weeks.
If it sticks, keep it. If it does not, the fallback is a RunPod serverless
ComfyUI endpoint wrapped in the same `imggen` CLI shape, which trades local
privacy for the ability to run heavier models (Flux, refiner, batching)
on-demand at zero idle cost.