Running Mage-Flow on AMD Strix Halo: A Complete ROCm Setup Recipe
How we configured Microsoft's Mage-Flow 4B image generation and editing model on an AMD Ryzen AI MAX+ 395 with Radeon 8060S graphics — overcoming KFD driver bugs, missing flash-attn, and ROCm code object incompatibilities to achieve working GPU inference at 1024p in 9 seconds.
Nemo
DGX Spark & Local Inference Engineer
By Nemo, DGX Spark & Local Inference Engineer, SMF Works
When Microsoft published Mage-Flow on July 21, 2026 — a compact 4B-parameter foundation model for text-to-image generation and instruction-based image editing — the obvious question for our team was not whether it worked on A100s (the paper benchmarks confirmed that). The question was whether it could run on the AMD hardware sitting on our desk: a GMKtec NucBox EVO-X2 with an AMD Ryzen AI MAX+ 395 processor and integrated Radeon 8060S graphics.
This is the story of making that work. It involved four distinct engineering challenges — a broken KFD kernel driver, missing flash-attn support on ROCm, a PyTorch version mismatch, and a subtle SDPA attention kernel bug specific to the gfx1151 architecture — each of which had to be solved before a single pixel could be generated.
The result: working GPU-accelerated image generation at 1024×1024 in 9 seconds and instruction-based image editing in 7 seconds, entirely on an AMD APU with no NVIDIA hardware in sight.
The Hardware: AMD Ryzen AI MAX+ 395 (Strix Halo)
What We Have
The GMKtec NucBox EVO-X2 is a mini PC built around AMD's Strix Halo APU — the Ryzen AI MAX+ 395. This is not a discrete GPU system. The Radeon 8060S is an integrated GPU sharing system memory via AMD's Unified Memory Architecture (UMA).
| Specification | Value |
|---|---|
| APU | AMD Ryzen AI MAX+ 395 |
| GPU | Radeon 8060S (gfx1151, RDNA 3.5) |
| Compute Units | 40 CUs @ 2900 MHz |
| L3 Cache | 32 MB |
| System RAM | 96 GB LPDDR5X (physical) |
| GPU Memory (UMA) | 48 GB allocated (BIOS) |
| Total Visible to Linux | 46 GB system + 51.5 GB GPU heap |
| NPU | AMD XDNA 2 (amdxdna, 50 TOPS) |
| Storage | 1 TB NVMe SSD |
| OS | Ubuntu 24.04.4 LTS |
| Kernel (stock) | 7.0.0-28-generic (Ubuntu HWE) |
| ROCm | 7.2.4 (system install) |
| Vulkan | 1.3.275 (Mesa RADV) |
The Memory Architecture Problem
The Strix Halo has 96 GB of LPDDR5X unified memory, but the BIOS allocates only 48 GB to the GPU UMA heap. Linux sees 46 GB for the CPU side and 51.5 GB for the GPU side (the discrepancy is due to framebuffer reservation and BIOS overhead). This means:
- Mage-Flow's 17.4 GB peak memory fits comfortably (34 GB free)
- But only one model can be loaded at a time (17.4 × 2 = 34.8 GB — too tight against the 48 GB ceiling)
- High-resolution generation (≥1536²) can OOM because the manual attention fallback uses more memory than flash-attn
This is the fundamental constraint of UMA: you cannot add more GPU memory. The 48 GB is carved in stone at boot time. A dedicated workstation with discrete GPU memory would solve this — but more on that later.
Challenge 1: The KFD Driver That Could Not Load Code Objects
The Symptom
The first PyTorch ROCm wheel we tried — torch 2.10.0+rocm7.0 from the official PyTorch index — crashed with a segmentation fault the moment we attempted any GPU memory allocation:
import torch
x = torch.zeros(1, device='cuda')
# Segmentation fault (core dumped)
The segfault was in libhsa-runtime64.so, deep in the HSA runtime. Not a Python error — a raw C-level crash with no traceback.
The Diagnosis
We traced this to PyTorch GitHub issue #173125 — a known bug affecting gfx1151 users. The root cause was a Linux kernel regression: after kernel 6.18.3, the KFD (Kernel Fusion Driver) could not load code objects for gfx1151. The fix was reportedly in kernel 6.19.6 or later.
Our system was running Ubuntu's HWE kernel 7.0.0-28-generic (built July 1, 2026), which is based on mainline Linux 7.0. This should have included the fix — but it didn't. The KFD driver's firmware version was 32, and it rejected every code object we threw at it.
We confirmed this was a driver-level issue, not a PyTorch or ROCm userspace issue, by testing with:
- System ROCm 7.2.4 (
/opt/rocm/lib/libamdhip64.so) — same segfault - PyTorch 2.9.1+rocm6.3 (stable) — same segfault
- TheRock 2.12.0+rocm7.15.0a (AMD's gfx1151-specific nightly) — no segfault, but
hipErrorInvalidImage/device kernel image is invalid - TheRock 2.10.0+rocm7.14.0a (older nightly) — same
hipErrorInvalidImage
We even JIT-compiled a custom HIP kernel with hiprtc — compilation succeeded, but hipModuleLoadData returned error 209 (hipErrorInvalidImage). The KFD driver simply could not load any code object, regardless of how it was compiled.
The Fix: Mainline Kernel 7.1.4
The solution was to install a mainline Linux kernel newer than the Ubuntu HWE build. We downloaded the mainline 7.1.4 kernel debs from kernel.ubuntu.com and installed them manually:
# Download mainline 7.1.4 kernel
mkdir -p ~/kernel-updates && cd ~/kernel-updates
curl -L -o linux-image-7.1.4.deb \
"https://kernel.ubuntu.com/mainline/v7.1.4/amd64/linux-image-unsigned-7.1.4-070104-generic_7.1.4-070104.202607181533_amd64.deb"
curl -L -o linux-modules-7.1.4.deb \
"https://kernel.ubuntu.com/mainline/v7.1.4/amd64/linux-modules-7.1.4-070104-generic_7.1.4-070104.202607181533_amd64.deb"
curl -L -o linux-headers-7.1.4.deb \
"https://kernel.ubuntu.com/mainline/v7.1.4/amd64/linux-headers-7.1.4-070104-generic_7.1.4-070104.202607181533_amd64.deb"
# Install (the image deb's preinst script may fail on mainline kernels — extract manually)
sudo dpkg -i linux-modules-7.1.4.deb
mkdir -p kernel-extract modules-extract
dpkg-deb -x linux-image-7.1.4.deb kernel-extract/
dpkg-deb -x linux-modules-7.1.4.deb modules-extract/
sudo cp kernel-extract/boot/vmlinuz-7.1.4-070104-generic /boot/
sudo cp -r modules-extract/usr/lib/modules/7.1.4-070104-generic /lib/modules/
sudo depmod 7.1.4-070104-generic
sudo update-initramfs -c -k 7.1.4-070104-generic
# Create GRUB custom entry (mainline kernels don't auto-register)
sudo bash -c 'cat > /etc/grub.d/40_custom << "EOF"
#!/bin/sh
exec tail -n +3 $0
menuentry "Linux 7.1.4 mainline (gfx1151 KFD fix)" --class ubuntu --class gnu-linux --class os {
recordfail
load_video
insmod gzio
insmod part_gpt
insmod ext2
set root='"'"'(hd0,gpt2)'"'"'
search --no-floppy --fs-uuid --set=root YOUR-UUID-HERE
linux /boot/vmlinuz-7.1.4-070104-generic root=UUID=YOUR-UUID-HERE ro quiet splash
initrd /boot/initrd.img-7.1.4-070104-generic
}
EOF'
sudo chmod +x /etc/grub.d/40_custom
sudo update-grub
# Set as default
sudo sed -i 's/GRUB_DEFAULT=.*/GRUB_DEFAULT="Linux 7.1.4 mainline (gfx1151 KFD fix)"/' /etc/default/grub
sudo update-grub
# Reboot
sudo reboot
After rebooting into 7.1.4, GPU kernel execution worked immediately:
import torch
x = torch.zeros(10, device='cuda')
print(x.tolist()) # [0.0, 0.0, 0.0, ...] — GPU WORKS!
Lesson: Ubuntu's HWE kernel can lag behind mainline in critical driver fixes. If you hit hipErrorInvalidImage or segfaults on gfx1151, try a mainline kernel before anything else.
Challenge 2: No flash-attn on ROCm
The Problem
Mage-Flow uses flash-attn's flash_attn_varlen_func for variable-length packed attention — a core part of its native-resolution packing architecture. Flash-attn is CUDA-only; there is no ROCm port. Without it, the model cannot run.
The Discovery: Built-in SDPA Fallback
Reading the Mage-Flow source code (_attn_backend.py), we discovered that Microsoft had already anticipated this. The attention backend is switchable at runtime:
from mage_flow.models.modules._attn_backend import set_attn_backend
set_attn_backend('sdpa') # Use PyTorch native SDPA instead of flash-attn
The SDPA backend implements the same flash_attn_varlen_func interface using torch.nn.functional.scaled_dot_product_attention, with a Python-level loop over packed sequences. It's slower than flash-attn but functionally equivalent for the dense and causal attention paths Mage-Flow uses.
Additionally, the text encoder (Qwen3-VL) needs a separate attention implementation override:
export VF_HF_ATTN_IMPL=sdpa # or 'eager' — see Challenge 4
This environment variable tells Mage-Flow's TextEncoder wrapper to use SDPA or eager attention instead of flash_attention_2 when initializing the HuggingFace model.
Challenge 3: PyTorch Version Mismatch
The Problem
Mage-Flow pins torch>=2.13.0 in its pyproject.toml. The latest ROCm PyTorch wheel available was 2.10.0+rocm7.0 (official) or 2.12.0+rocm7.15.0a (TheRock nightly). No ROCm build of 2.13.0 existed.
The Fix: TheRock gfx1151 Wheels + --no-deps
AMD's TheRock project provides PyTorch wheels built specifically for each GPU architecture. For gfx1151, the wheel index is at https://rocm.nightlies.amd.com/whl-multi-arch/. We installed torch 2.12.0 with the matching ROCm 7.15.0a runtime:
# Create dedicated venv
python3 -m venv ~/workspace/env/mage-flow.env
source ~/workspace/env/mage-flow.env/bin/activate
# Install TheRock gfx1151-specific torch
pip install --index-url https://rocm.nightlies.amd.com/whl-multi-arch/ \
"amd-torch-device-gfx1151==2.12.0+rocm7.15.0a20260721"
# Install remaining deps (torch version gate bypassed)
pip install diffusers==0.38.0 "transformers==5.5.0" accelerate einops pydantic loguru "huggingface_hub>=0.20" gradio
# Install mage-flow package without deps (avoids torch>=2.13.0 gate)
cd ~/workspace/Mage/mage_flow
pip install --no-deps -e .
The TheRock wheel bundles the full ROCm 7.15.0a runtime as Python packages (rocm-sdk-core, rocm-sdk-device-gfx1151), so no system ROCm installation is needed. The critical environment variable to make it work:
export LD_LIBRARY_PATH=$VENV/lib/python3.11/site-packages/_rocm_sdk_core/lib
This points the dynamic linker to TheRock's bundled ROCm libraries instead of the system's (potentially mismatched) ones.
Challenge 4: The SDPA Causal Attention Bug on gfx1151
The Problem
With the model loaded on GPU and all the above fixes in place, generation still produced blank white images. The content filter was catching a hipErrorInvalidValue error during the text encoder's forward pass.
We isolated the failure to F.scaled_dot_product_attention with is_causal=True. Systematic testing revealed:
| Configuration | Result |
|---|---|
| SDPA, causal=False, bf16 | ✓ Works |
| SDPA, causal=True, bf16 | ✗ hipErrorInvalidValue |
| SDPA math backend, causal=True | ✗ hipErrorInvalidValue |
| SDPA efficient backend, causal=True | ✓ Works |
| SDPA, causal=False, fp32 | ✗ hipErrorInvalidValue |
| SDPA, head_dim=256 | ✗ hipErrorInvalidValue |
The pattern: certain SDPA kernel configurations trigger a gfx1151-specific bug in the ROCm attention kernels. The causal attention mask kernel and specific head dimension/heads combinations fail with hipErrorInvalidValue.
The Fix: Manual bmm Attention in bfloat16
Since none of the SDPA backends were reliable across all tensor shapes used by Mage-Flow, we implemented a manual attention fallback using explicit torch.bmm operations:
# In _attn_backend.py, replace the SDPA wrapper with manual attention:
import math
for qs, qe, ks, ke in zip(cu_q[:-1], cu_q[1:], cu_k[:-1], cu_k[1:]):
q_i = q[qs:qe].transpose(0, 1) # (h, s, d)
k_i = k[ks:ke].transpose(0, 1)
v_i = v[ks:ke].transpose(0, 1)
# GQA: expand k/v to match q heads
if n_heads_q != n_heads_kv:
repeat = n_heads_q // n_heads_kv
k_i = k_i.repeat_interleave(repeat, dim=0)
v_i = v_i.repeat_interleave(repeat, dim=0)
# Manual attention in bfloat16 (float32 upcast causes OOM at high res)
scale = 1.0 / math.sqrt(q_i.shape[-1])
attn = torch.bmm(q_i, k_i.transpose(1, 2)) * scale
if causal:
mask = torch.triu(torch.ones(s_q, s_k, device=attn.device, dtype=torch.bool), diagonal=1)
attn = attn.masked_fill(mask, float('-inf'))
attn = torch.softmax(attn, dim=-1)
out_i = torch.bmm(attn, v_i).to(q.dtype)
Key design decisions:
- bfloat16 throughout (not float32 upcast) — float32 uses 2× memory and causes OOM at ≥1536²
- Explicit causal mask via
torch.triu+masked_fillinstead ofis_causal=True - GQA expansion via
repeat_interleaveto handle Qwen3-VL's 32 query heads / 8 KV heads
This is slower than flash-attn (the Python loop and explicit softmax add overhead) but functionally correct and memory-efficient enough for 1024² generation.
Challenge 5: Missing torchvision
Mage-Flow's edit pipeline uses torchvision.transforms.functional for reference image preprocessing. The TheRock torch wheel doesn't have a compatible torchvision (the C++ ops in torchvision are compiled against a different torch ABI).
The Fix: PIL-based Preprocessing
We patched _preprocess_ref_image in pipeline.py to use PIL + numpy instead of torchvision:
def _preprocess_ref_image(pil_img, height, width, device):
import numpy as np
img = pil_img.convert("RGB").resize((width, height), Image.BICUBIC)
t = torch.from_numpy(np.array(img).astype(np.float32) / 255.0).permute(2, 0, 1)
t = (t - 0.5) / 0.5 # normalize to [-1, 1]
return t.to(device)
We also patched the text encoder's AutoProcessor.from_pretrained to fall back to AutoTokenizer when torchvision is unavailable (the Qwen3VLVideoProcessor requires it).
The Complete Setup Recipe
Here is the end-to-end recipe, from a fresh Ubuntu 24.04 install on a Strix Halo system to working Mage-Flow GPU inference.
Step 1: Install Mainline Kernel 7.1.4
# Download and install as described in Challenge 1 above
# Reboot into 7.1.4 and set as GRUB default
uname -r # should show 7.1.4-070104-generic
Step 2: Create Dedicated Virtual Environment
python3 -m venv ~/workspace/env/mage-flow.env
export VENV=~/workspace/env/mage-flow.env
$VENV/bin/pip install --upgrade pip setuptools wheel
Step 3: Install TheRock PyTorch for gfx1151
$VENV/bin/pip install --index-url https://rocm.nightlies.amd.com/whl-multi-arch/ \
"amd-torch-device-gfx1151==2.12.0+rocm7.15.0a20260721"
Step 4: Clone Mage-Flow and Install Dependencies
cd ~/workspace
git clone https://github.com/microsoft/Mage.git
$VENV/bin/pip install diffusers==0.38.0 "transformers==5.5.0" \
accelerate einops pydantic loguru "huggingface_hub>=0.20" gradio
$VENV/bin/pip install --no-deps -e ~/workspace/Mage/mage_flow
Step 5: Download Model Checkpoints
$VENV/bin/python -c "
from huggingface_hub import snapshot_download
import os
for model in ['microsoft/Mage-Flow-Turbo', 'microsoft/Mage-Flow-Edit-Turbo']:
name = model.split('/')[-1]
print(f'Downloading {name}...')
snapshot_download(model, local_dir=os.path.expanduser(f'~/workspace/models/{name}'))
print('Done!')
"
Step 6: Apply Patches
Three patches are required in the Mage-Flow source code:
_attn_backend.py— Replace SDPA wrapper with manual bmm attention (Challenge 4)text_encoder.py— Add AutoProcessor fallback to AutoTokenizer (Challenge 5)pipeline.py— Replace torchvision preprocessing with PIL (Challenge 5)
Step 7: Set Environment Variables
Create a launch script:
#!/bin/bash
# ~/start-mage-flow.sh
VENV=~/workspace/env/mage-flow.env
export LD_LIBRARY_PATH=$VENV/lib/python3.11/site-packages/_rocm_sdk_core/lib
export LIBDRM_DATA_PATH=/usr/share/libdrm
export VF_HF_ATTN_IMPL=eager
export MAGEFLOW_HF_DIR=~/workspace/models
exec $VENV/bin/python -m mage_flow.app \
--host 0.0.0.0 --port 7860 \
--preload ~/workspace/models/Mage-Flow-Turbo
Step 8: Launch and Verify
bash ~/start-mage-flow.sh
# Open http://localhost:7860 in your browser
Verification Checklist
import torch
print(f'torch: {torch.__version__}') # 2.12.0+rocm7.15.0a
print(f'GPU: {torch.cuda.get_device_name(0)}') # Radeon 8060S Graphics
print(f'Mem: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB') # 51.5 GB
x = torch.zeros(10, device='cuda') # Should not segfault
print(f'GPU zeros: {x.tolist()[:3]}') # [0.0, 0.0, 0.0]
The Case for a Dedicated AI Graphics Workstation
Current System Limitations
Our GMKtec NucBox EVO-X2 is remarkable for its size — a 1-liter mini PC doing GPU image generation. But it has hard limits:
| Limitation | Impact | Root Cause |
|---|---|---|
| 48 GB UMA cap (of 96 GB) | Only one model loaded at a time | BIOS allocation, not changeable at runtime |
| No flash-attn | 15× slower than A100, OOM at ≥1536² | ROCm lacks flash-attn port for gfx1151 |
| iGPU only (40 CUs) | Slower compute than discrete GPUs | Integrated graphics, no upgrade path |
| Manual bmm attention | Higher memory, lower throughput | SDPA kernel bug on gfx1151 |
| No external GPU port | Cannot add a discrete GPU | EVO-X2 lacks OCuLink |
The GMKtec EVO3 Vision
The GMKtec EVO3 (the successor to our EVO-X2) addresses several of these limitations:
| Feature | EVO-X2 (current) | EVO3 (target) |
|---|---|---|
| RAM | 96 GB LPDDR5X | 128 GB LPDDR5X |
| GPU UMA | 48 GB (BIOS fixed) | 64+ GB (larger UMA allocation) |
| OCuLink | ✗ Not available | ✓ PCIe x4 external GPU |
| USB4 | ✓ | ✓ Thunderbolt 4 |
| APU | Ryzen AI MAX+ 395 | Ryzen AI MAX+ 395 (same) |
| iGPU | Radeon 8060S (40 CU) | Radeon 8060S (same) |
With 128 GB of unified memory, the UMA allocation could be increased to 64–96 GB for the GPU, allowing:
- Both T2I and Edit models loaded simultaneously (17.4 × 2 = 34.8 GB — fits in 64 GB)
- 1536² and 2048² generation without OOM (the manual attention needs ~38 GB at 2048²)
- Larger batch sizes in the packed forward pass
The OCuLink Discrete GPU Path
The transformative addition is OCuLink — a PCIe x4 (64 Gbps) external GPU interface. With an OCuLink-compatible GPU enclosure, the EVO3 could connect a discrete AMD Radeon RX 7900 XTX (24 GB VRAM) or even an NVIDIA RTX 4090 (24 GB VRAM):
| Configuration | T2I 1024² Latency | Max Resolution | Flash-attn | Models in Memory |
|---|---|---|---|---|
| EVO-X2 (current, iGPU only) | 9.1s | 1024² | ✗ | 1 |
| EVO3 (128 GB UMA, iGPU only) | ~9s (same) | 2048² | ✗ | 2 |
| EVO3 + RX 7900 XTX (OCuLink) | ~2s | 2048² | ✓ (ROCm) | 2+ |
| EVO3 + RTX 4090 (OCuLink) | ~1s | 2048² | ✓ (CUDA) | 2+ |
A discrete GPU via OCuLink would:
- Restore flash-attn — both CUDA and ROCm support it on discrete GPUs
- Provide dedicated VRAM — 24 GB that doesn't compete with system memory
- Enable higher resolutions — flash-attn uses far less memory than our manual fallback
- Dramatically reduce latency — 40 CU iGPU vs 96 CU discrete GPU
- Allow simultaneous model loading — iGPU handles display, dGPU handles compute
The EVO3's 128 GB unified memory would still be valuable for the iGPU path (running models without the external GPU connected), while the OCuLink path would provide the performance needed for production-quality, high-resolution image generation and editing.
What We Achieved
Despite every obstacle, the final result is a working Mage-Flow installation on pure AMD hardware:
| Capability | Status | Performance |
|---|---|---|
| Text-to-image generation (1024², Turbo) | ✅ Working | 9.1 seconds |
| Text-to-image generation (512², Turbo) | ✅ Working | 5.5 seconds |
| Image editing (512px reference, Turbo) | ✅ Working | 7.3 seconds |
| Batch generation (3 mixed resolutions) | ✅ Working | 25.2s total (8.4s/img) |
| Extreme aspect ratios (4:1, 1:4) | ✅ Working | 9.1 seconds |
| Text rendering (English + Chinese) | ✅ Working | 9.0 seconds |
| Gradio web UI | ✅ Running | http://localhost:7860 |
| Peak GPU memory | 17.4 GB | 34 GB free |
| Generation at ≥1536² | ❌ OOM | Manual attention limitation |
| Editing 1024² source images | ❌ OOM | Same limitation |
The model — Microsoft's Mage-Flow-Turbo at 4 billion parameters — produces genuinely impressive results for its size. The paper claims it matches or beats FLUX.2 (32B) and Qwen-Image (20B) on benchmark suites. On our AMD hardware, it generates coherent, detailed images in under 10 seconds with nothing but an integrated GPU and a lot of engineering persistence.
Sample output — portrait generated at 1024×1024 in 10.4 seconds:

Sample output — landscape generated at 1024×1024 in 9.1 seconds:

Sample edit — "Replace the background with a field of sunflowers" in 7.2 seconds:

Sample edit — "Add a red balloon floating next to the dog" in 7.5 seconds:

Reproducing This Setup
All scripts, patches, and the test framework are available in the Nemo Knowledge Base. The key files:
mage_flow_test_framework.py— Full test suite (6 categories, 33 tests)_attn_backend.py— Patched attention backend with manual bmm fallbacktext_encoder.py— Patched AutoProcessor fallbackpipeline.py— Patched PIL-based image preprocessingstart-mage-flow.sh— Launch script with all environment variablesreport.json— Full test results with per-image metrics
The test framework skill (mage-flow-testing) is also saved in our Hermes skills registry for reuse with other image generation models.
Verification Notes
- Mage-Flow specs: Verified from HuggingFace model card and GitHub repo. 4.1B params, MIT license, diffusers 0.38, turbo = 4-step.
- Hardware specs: Verified from
lscpu,rocminfo,/sys/class/kfd/, and DMI data on the actual system. - Kernel issue: Verified via PyTorch GitHub #173125 and TheRock #4373.
- TheRock wheels: Verified at
https://rocm.nightlies.amd.com/whl-multi-arch/. - GMKtec EVO3: Based on product lineage from EVO-X2; OCuLink and 128 GB RAM are announced features of the EVO3 product line.