Uploaded sanitized BC250/ROCm Repository.

This commit is contained in:
Fabian
2026-08-20 00:45:43 +02:00
parent 7d2184f1e8
commit d7d22e93b3
678 changed files with 65963 additions and 1 deletions
@@ -0,0 +1,637 @@
# ComfyUI + Z-Image Turbo on AMD BC-250 — Complete Setup Guide
> **Hardware**: AMD BC-250 (Cyan Skillfish, gfx1013→gfx1010, 24 CUs, shared RAM)
> **Backend**: ROCm 7.2.0 / PyTorch 2.5.1+rocm6.2
> **OS**: CachyOS, kernel 6.18.8-3-cachyos
> **ComfyUI Version**: 0.15.1
> **Date**: 2026-03-02
---
## Table of Contents
1. [Overview](#1-overview)
2. [Architecture](#2-architecture)
3. [Prerequisites](#3-prerequisites)
4. [Installation — Step by Step](#4-installation--step-by-step)
5. [Model Setup](#5-model-setup)
6. [Launch Script](#6-launch-script)
7. [ComfyUI Workflow — Z-Image Turbo](#7-comfyui-workflow--z-image-turbo)
8. [BC-250 Specific Tuning](#8-bc-250-specific-tuning)
9. [Troubleshooting](#9-troubleshooting)
10. [File Inventory](#10-file-inventory)
11. [Performance Notes](#11-performance-notes)
---
## 1. Overview
ComfyUI is a node-based Stable Diffusion GUI that runs Z-Image Turbo (a Lumina2-architecture model) via PyTorch with ROCm/HIP on the AMD BC-250 GPU. The model uses a GGUF-quantized diffusion model (Q5_K_S) loaded via the ComfyUI-GGUF custom node, with a Gemma 2 2B text encoder and a Flux-compatible VAE.
### What's Running
| Component | File | Size | Format |
|-----------|------|------|--------|
| Diffusion Model | `z_image_turbo-Q5_K_S.gguf` | 5.2 GB | GGUF Q5_K_S |
| Text Encoder | `gemma2_2b_lumina2.safetensors` | 9.8 GB | Safetensors (f32) |
| VAE | `ae.safetensors` | 335 MB | Safetensors (f32) |
### Pipeline
```
[ComfyUI WebUI :8188] → [PyTorch] → [ROCm/HIP] → [AMD BC-250 GPU]
[Gemma 2 2B Text Encoder] → CLIP Encode → [Z-Image Turbo Diffusion] → [VAE Decode] → Image
```
---
## 2. Architecture
### Z-Image Turbo Details
- **Architecture**: Lumina2 (Lumina-Image 2.0 family)
- **Base**: Z-Image by Freepik, turbo-distilled variant
- **Text Encoder**: Gemma 2 2B (Google, 2304-dim embeddings)
- **VAE**: Flux-compatible autoencoder (`ae.safetensors`)
- **Sampler**: Euler with SGM Uniform scheduler, 8 steps (turbo)
- **CFG Scale**: 3.0 (turbo models use low CFG)
- **Latent Format**: Flux-style latent space
### Why GGUF?
The BC-250 has ~14.7 GB shared system RAM. The full FP16 diffusion model would be too large. GGUF Q5_K_S quantization reduces the model from ~12+ GB to 5.2 GB, making it feasible alongside the text encoder and VAE.
---
## 3. Prerequisites
Before starting, you need ROCm working on the BC-250. See `ROCm_BC250_Documentation.md` for the full ROCm setup.
### Required
- ROCm 7.2.0 installed and working (`rocminfo` detects BC-250)
- Python 3.11 (`/usr/bin/python3.11`)
- Git
- ~30 GB free disk space
### Verify ROCm
```bash
rocminfo | grep "Name:"
# Should show: gfx1010 and AMD BC-250
```
---
## 4. Installation — Step by Step
### 4.1 Clone ComfyUI
```bash
cd ~
git clone https://github.com/comfyanonymous/ComfyUI.git
cd ComfyUI
```
### 4.2 Create Python 3.11 Virtual Environment
Python 3.11 is required — Python 3.14 (system default) is too new for PyTorch ROCm wheels.
```bash
python3.11 -m venv venv
source venv/bin/activate
```
### 4.3 Install PyTorch with ROCm Support
```bash
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.2
```
This downloads ~4 GB. The ROCm 6.2 PyTorch wheel is compatible with the ROCm 7.2 runtime.
**Verify installation:**
```bash
python -c "import torch; print(torch.version.cuda); print(torch.cuda.is_available())"
# Should print: 6.2 and True
```
### 4.4 Install ComfyUI Dependencies
```bash
pip install -r requirements.txt
```
### 4.5 Install ComfyUI-GGUF Custom Node
This enables loading GGUF-quantized models in ComfyUI.
```bash
cd ~/ComfyUI/custom_nodes
git clone https://github.com/city96/ComfyUI-GGUF.git
source ~/ComfyUI/venv/bin/activate
pip install gguf
```
### 4.6 Install huggingface-hub (for model downloads)
```bash
pip install huggingface-hub
```
---
## 5. Model Setup
### 5.1 Directory Structure
ComfyUI looks for models in `~/ComfyUI/models/`. Our models live in `~/sd-models/` and are symlinked.
```
~/ComfyUI/models/
├── unet/
│ └── z_image_turbo-Q5_K_S.gguf → ~/sd-models/diffusion_models/z_image_turbo-Q5_K_S.gguf
├── text_encoders/
│ └── gemma2_2b_lumina2.safetensors (merged from 3 shards, 9.8 GB)
├── vae/
│ └── ae.safetensors → ~/sd-models/vae/ae.safetensors
└── ...
```
### 5.2 Symlink Diffusion Model (GGUF)
The Z-Image Turbo GGUF model must go in `models/unet/` (ComfyUI-GGUF's `UnetLoaderGGUF` node reads from there):
```bash
ln -sf /home/dars/sd-models/diffusion_models/z_image_turbo-Q5_K_S.gguf \
~/ComfyUI/models/unet/z_image_turbo-Q5_K_S.gguf
```
### 5.3 Text Encoder — Gemma 2 2B
Z-Image Turbo uses the Gemma 2 2B text encoder from the Lumina-Image-2.0 family. The original model is sharded into 3 safetensors files. We merge them into a single file for ComfyUI.
**Download from Alpha-VLLM (not gated, no login required):**
```bash
source ~/ComfyUI/venv/bin/activate
python3 -c "
from huggingface_hub import hf_hub_download
import os
repo = 'Alpha-VLLM/Lumina-Image-2.0'
dest = os.path.expanduser('~/sd-models/text_encoders/lumina2_gemma2_2b')
os.makedirs(dest, exist_ok=True)
files = [
'text_encoder/config.json',
'text_encoder/model.safetensors.index.json',
'text_encoder/model-00001-of-00003.safetensors',
'text_encoder/model-00002-of-00003.safetensors',
'text_encoder/model-00003-of-00003.safetensors',
]
for f in files:
print(f'Downloading {f}...')
hf_hub_download(repo, f, local_dir=dest)
print('Done!')
"
```
**Merge shards into single file:**
```bash
source ~/ComfyUI/venv/bin/activate
python3 << 'EOF'
import safetensors.torch
import torch
import os, json
base_dir = os.path.expanduser("~/sd-models/text_encoders/lumina2_gemma2_2b/text_encoder")
output = os.path.expanduser("~/ComfyUI/models/text_encoders/gemma2_2b_lumina2.safetensors")
os.makedirs(os.path.dirname(output), exist_ok=True)
with open(os.path.join(base_dir, "model.safetensors.index.json")) as f:
index = json.load(f)
all_tensors = {}
shards = set(index["weight_map"].values())
print(f"Loading {len(shards)} shards with {len(index['weight_map'])} tensors...")
for shard in sorted(shards):
path = os.path.join(base_dir, shard)
print(f" Loading {shard}...")
tensors = safetensors.torch.load_file(path, device="cpu")
all_tensors.update(tensors)
print(f"Total tensors: {len(all_tensors)}")
print(f"Saving merged file...")
safetensors.torch.save_file(all_tensors, output)
print(f"Done! Size: {os.path.getsize(output)/1e9:.2f} GB")
EOF
```
**Clean up shards (optional):**
```bash
rm -rf ~/sd-models/text_encoders/lumina2_gemma2_2b/
```
### 5.4 VAE
```bash
ln -sf /home/dars/sd-models/vae/ae.safetensors \
~/ComfyUI/models/vae/ae.safetensors
```
### 5.5 Verify All Models in Place
```bash
ls -lh ~/ComfyUI/models/unet/*.gguf \
~/ComfyUI/models/text_encoders/*.safetensors \
~/ComfyUI/models/vae/*.safetensors
```
Expected output:
```
9.8G ~/ComfyUI/models/text_encoders/gemma2_2b_lumina2.safetensors
5.2G ~/ComfyUI/models/unet/z_image_turbo-Q5_K_S.gguf (symlink)
335M ~/ComfyUI/models/vae/ae.safetensors (symlink)
```
---
## 6. Launch Script
### Location: `~/start-comfyui.sh`
```bash
#!/bin/bash
# =============================================================
# ComfyUI Launch Script for AMD BC-250 (ROCm / gfx1013)
# =============================================================
set -euo pipefail
echo "=========================================="
echo " ComfyUI — BC-250 ROCm Launcher"
echo "=========================================="
# --- GPU Health Check ---
if dmesg 2>/dev/null | tail -50 | grep -qi "KIQ fence timeout"; then
echo "[ABORT] KIQ fence timeout detected in dmesg — reboot required!"
exit 1
fi
echo "[OK] GPU health check passed"
# --- ROCm Environment for BC-250 (gfx1013 → gfx1010 spoof) ---
export HSA_OVERRIDE_GFX_VERSION=10.1.0
export HSA_ENABLE_SDMA=0
export HIP_VISIBLE_DEVICES=0
export ROCM_PATH=/opt/rocm
export HSA_TOOLS_LIB=""
export HSA_TOOLS_REPORT_LOAD_FAILURE=0
export PATH="/opt/rocm/bin:$PATH"
export LD_LIBRARY_PATH="/opt/rocm/lib"
# --- Unset old workaround variables that destroy performance ---
unset GPU_MAX_HW_QUEUES 2>/dev/null || true
unset HIP_LAUNCH_BLOCKING 2>/dev/null || true
unset GGML_CUDA_ENABLE_UNIFIED_MEMORY 2>/dev/null || true
unset GGML_HIP_HOST_ALLOC 2>/dev/null || true
unset GGML_CUDA_NO_PINNED 2>/dev/null || true
unset GGML_HIP_NO_COARSE_GRAIN 2>/dev/null || true
unset HSA_DISABLE_FRAGMENT_ALLOCATOR 2>/dev/null || true
# --- PyTorch ROCm tuning ---
export PYTORCH_HIP_ALLOC_CONF="expandable_segments:False"
echo "[OK] ROCm environment configured"
# --- Activate venv ---
cd ~/ComfyUI
source venv/bin/activate
# --- Launch ComfyUI ---
echo "[START] Launching ComfyUI on http://0.0.0.0:8188"
echo "=========================================="
python main.py \
--listen 0.0.0.0 \
--port 8188 \
--force-fp32 \
--lowvram \
"$@"
```
### Usage
```bash
# Foreground (see logs):
bash ~/start-comfyui.sh
# Background with logging:
nohup bash ~/start-comfyui.sh > /tmp/comfyui.log 2>&1 &
# Check if running:
curl -s http://localhost:8188/system_stats | python3 -m json.tool
```
### CLI Flags Explained
| Flag | Why |
|------|-----|
| `--listen 0.0.0.0` | Accept connections from any interface (access from other machines) |
| `--port 8188` | Default ComfyUI port |
| `--force-fp32` | BC-250 gfx1010 has limited FP16 support in PyTorch ROCm; FP32 prevents crashes |
| `--lowvram` | Enables aggressive model offloading — essential for 14.7 GB shared RAM |
---
## 7. ComfyUI Workflow — Z-Image Turbo
### Access the WebUI
Open in browser: **http://localhost:8188** (or `http://<machine-ip>:8188` from another machine)
### Pre-made Workflow
A ready-to-use workflow is saved at:
```
~/ComfyUI/workflows/z_image_turbo_bc250.json
```
Load it via: **Menu → Load → select `z_image_turbo_bc250.json`**
### Manual Node Setup
If building the workflow from scratch, create these nodes:
#### Node 1: UnetLoaderGGUF
- **Type**: `UnetLoaderGGUF` (from ComfyUI-GGUF custom node, category: bootleg)
- **unet_name**: `z_image_turbo-Q5_K_S.gguf`
- **Output**: MODEL → connect to KSampler's "model" input
#### Node 2: CLIPLoader
- **Type**: `CLIPLoader` (built-in, category: advanced/loaders)
- **clip_name**: `gemma2_2b_lumina2.safetensors`
- **type**: `lumina2`**CRITICAL: must be set to lumina2**
- **Output**: CLIP → connect to both CLIP Text Encode nodes
#### Node 3: CLIP Text Encode (Positive)
- **Type**: `CLIPTextEncode`
- **text**: Your prompt (e.g., "a beautiful sunset over the ocean")
- **Input**: clip ← from CLIPLoader
- **Output**: CONDITIONING → connect to KSampler's "positive" input
#### Node 4: CLIP Text Encode (Negative)
- **Type**: `CLIPTextEncode`
- **text**: Empty string `""` (turbo models work best with empty negative)
- **Input**: clip ← from CLIPLoader
- **Output**: CONDITIONING → connect to KSampler's "negative" input
#### Node 5: Empty Latent Image
- **Type**: `EmptyLatentImage`
- **width**: `512`
- **height**: `512`
- **batch_size**: `1`
- **Output**: LATENT → connect to KSampler's "latent_image" input
#### Node 6: KSampler
- **Type**: `KSampler`
- **seed**: Any number (42)
- **control_after_generate**: `fixed` (or `randomize` for variety)
- **steps**: `8` (turbo — more steps won't improve quality)
- **cfg**: `3.0` (turbo models use low CFG guidance)
- **sampler_name**: `euler`
- **scheduler**: `sgm_uniform`
- **denoise**: `1.0`
- **Inputs**: model, positive, negative, latent_image
- **Output**: LATENT → connect to VAEDecode
#### Node 7: VAELoader
- **Type**: `VAELoader`
- **vae_name**: `ae.safetensors`
- **Output**: VAE → connect to VAEDecode's "vae" input
#### Node 8: VAE Decode
- **Type**: `VAEDecode`
- **Inputs**: samples (from KSampler), vae (from VAELoader)
- **Output**: IMAGE → connect to SaveImage
#### Node 9: Save Image
- **Type**: `SaveImage`
- **filename_prefix**: `ComfyUI`
- **Input**: images ← from VAEDecode
- Output images saved to: `~/ComfyUI/output/`
### Wiring Summary
```
UnetLoaderGGUF ───MODEL──→ KSampler
CLIPLoader ───CLIP──→ CLIPTextEncode (positive) ──CONDITIONING──→ KSampler
CLIPLoader ───CLIP──→ CLIPTextEncode (negative) ──CONDITIONING──→ KSampler
EmptyLatentImage ──LATENT──→ KSampler
KSampler ──LATENT──→ VAEDecode
VAELoader ──VAE──→ VAEDecode
VAEDecode ──IMAGE──→ SaveImage
```
---
## 8. BC-250 Specific Tuning
### Environment Variables (set in launch script)
| Variable | Value | Why |
|----------|-------|-----|
| `HSA_OVERRIDE_GFX_VERSION` | `10.1.0` | BC-250 (gfx1013) needs gfx1010 spoof for ROCm |
| `HSA_ENABLE_SDMA` | `0` | SDMA engine has hardware bugs on gfx1013 |
| `HIP_VISIBLE_DEVICES` | `0` | Select the BC-250 GPU |
| `ROCM_PATH` | `/opt/rocm` | ROCm installation path |
| `HSA_TOOLS_LIB` | `""` | Disable profiling tools (stability) |
| `HSA_TOOLS_REPORT_LOAD_FAILURE` | `0` | Suppress tool warnings |
| `PYTORCH_HIP_ALLOC_CONF` | `expandable_segments:False` | Prevent memory fragmentation |
### Variables to NEVER Set
These old workarounds **destroy performance** and must NOT be set:
| Variable | Why it's bad |
|----------|-------------|
| `GPU_MAX_HW_QUEUES=1` | Serializes all GPU ops to 1 queue |
| `HIP_LAUNCH_BLOCKING=1` | Forces synchronous kernel launches |
| `GGML_CUDA_ENABLE_UNIFIED_MEMORY=1` | Page fault overhead |
| `GGML_HIP_HOST_ALLOC=1` | Zero-copy over PCIe is slow |
### Memory Considerations
- Total available: ~14.7 GB shared system RAM
- Diffusion model (GGUF Q5_K_S): ~5.2 GB
- Text encoder (Gemma 2 2B f32): ~9.8 GB
- VAE: ~335 MB
- Total model footprint: ~15.3 GB — exceeds available RAM
- **`--lowvram` is essential**: it offloads models to CPU when not in active use
- Only one component is on GPU at a time during inference
### Resolution Recommendations
| Resolution | Latent Size | Notes |
|-----------|-------------|-------|
| 512×512 | 64×64 | Fastest, recommended for testing |
| 768×768 | 96×96 | Good quality, slower |
| 1024×1024 | 128×128 | May OOM on BC-250 |
---
## 9. Troubleshooting
### "KIQ fence timeout" in dmesg → Reboot
```bash
sudo dmesg | grep -i "KIQ fence timeout"
```
If this appears, the GPU is in a bad state. **Reboot the machine.**
### ComfyUI won't start — "No module named torch"
Make sure you activated the venv:
```bash
source ~/ComfyUI/venv/bin/activate
python -c "import torch; print(torch.__version__)"
```
### "CLIP type not found" or wrong model type
Make sure the CLIPLoader node type is set to **`lumina2`** — NOT `stable_diffusion`.
### OOM (Out of Memory) during generation
1. Reduce resolution to 512×512
2. Ensure `--lowvram` is set
3. Close other programs using RAM
4. Try `--use-split-cross-attention` flag
### Model not showing in dropdown
Verify symlinks are not broken:
```bash
ls -la ~/ComfyUI/models/unet/z_image_turbo-Q5_K_S.gguf
ls -la ~/ComfyUI/models/vae/ae.safetensors
ls -la ~/ComfyUI/models/text_encoders/gemma2_2b_lumina2.safetensors
```
### "UnetLoaderGGUF" node not found
Ensure ComfyUI-GGUF is installed:
```bash
ls ~/ComfyUI/custom_nodes/ComfyUI-GGUF/
pip list | grep gguf
```
### PyTorch ROCm version mismatch
```bash
python -c "import torch; print(torch.version.cuda)"
# Should print: 6.2
```
---
## 10. File Inventory
### Installation Files
| File | Purpose |
|------|---------|
| `~/ComfyUI/` | ComfyUI installation directory |
| `~/ComfyUI/venv/` | Python 3.11 virtual environment |
| `~/ComfyUI/custom_nodes/ComfyUI-GGUF/` | GGUF model loader custom node |
| `~/ComfyUI/workflows/z_image_turbo_bc250.json` | Pre-made Z-Image Turbo workflow |
| `~/start-comfyui.sh` | Launch script with ROCm env vars |
### Model Files
| File | Size | Format |
|------|------|--------|
| `~/ComfyUI/models/unet/z_image_turbo-Q5_K_S.gguf` | 5.2 GB | Symlink → `~/sd-models/diffusion_models/` |
| `~/ComfyUI/models/text_encoders/gemma2_2b_lumina2.safetensors` | 9.8 GB | Merged from Alpha-VLLM/Lumina-Image-2.0 |
| `~/ComfyUI/models/vae/ae.safetensors` | 335 MB | Symlink → `~/sd-models/vae/` |
### Output
| File | Purpose |
|------|---------|
| `~/ComfyUI/output/` | Generated images saved here |
### Python Packages (key ones)
| Package | Version |
|---------|---------|
| torch | 2.5.1+rocm6.2 |
| torchvision | 0.20.1+rocm6.2 |
| torchaudio | 2.5.1+rocm6.2 |
| pytorch-triton-rocm | 3.1.0 |
| transformers | 5.2.0 |
| safetensors | 0.7.0 |
| gguf | 0.18.0 |
| comfyui-frontend-package | 1.39.19 |
---
## 11. Performance Notes
### Startup Output (successful launch)
```
Total VRAM 14750 MB, total RAM 15205 MB
pytorch version: 2.5.1+rocm6.2
AMD arch: gfx1010
ROCm version: (6, 2)
Forcing FP32
Set vram state to: LOW_VRAM
Device: cuda:0 AMD Radeon Graphics : native
ComfyUI version: 0.15.1
ComfyUI-GGUF: Partial torch compile only, consider updating pytorch
```
### Expected Timing (BC-250, 512×512, 8 steps)
| Phase | Estimated Time |
|-------|---------------|
| Model Loading (first run) | 30-60s |
| Text Encoding (Gemma 2 2B) | ~2-5s |
| Sampling (8 steps, Euler) | ~60-90s |
| VAE Decode | ~10-15s |
| **Total (first image)** | **~2-3 min** |
| **Total (subsequent)** | **~1-2 min** |
### Comparison with sdcpp-restapi
| | ComfyUI + PyTorch | sdcpp-restapi |
|---|---|---|
| Frontend | Full node-based GUI | REST API + simple WebUI |
| Model format | GGUF + safetensors | GGUF only |
| Memory management | PyTorch (--lowvram) | ggml manual |
| Flexibility | Full workflow customization | Fixed pipeline |
| Turbo steps | Configurable per-run | Config-based |
---
## Appendix: Quick Start Cheatsheet
```bash
# 1. Launch ComfyUI
bash ~/start-comfyui.sh
# 2. Open browser
# http://localhost:8188
# 3. Load workflow
# Menu → Load → z_image_turbo_bc250.json
# 4. Click "Queue Prompt" to generate
# 5. Images saved in ~/ComfyUI/output/
```
---
*Document generated: 2026-03-02 | System: CachyOS + AMD BC-250 + ROCm 7.2.0*
@@ -0,0 +1,443 @@
# Qwen3-4B on llama.cpp with ROCm (AMD BC-250)
> Running **Qwen3-4B-Q8_0** via **llama.cpp** with **ROCm HIP** GPU acceleration on the **AMD BC-250 (gfx1010)**.
---
## Table of Contents
- [System Overview](#system-overview)
- [Prerequisites](#prerequisites)
- [Step 1: Clone llama.cpp](#step-1-clone-llamacpp)
- [Step 2: Build llama.cpp with ROCm HIP](#step-2-build-llamacpp-with-rocm-hip)
- [Step 3: Download the Model](#step-3-download-the-model)
- [Step 4: Run Inference (Interactive Chat)](#step-4-run-inference-interactive-chat)
- [Step 5: Run Inference (One-Shot / Batch)](#step-5-run-inference-one-shot--batch)
- [Step 6: Run as API Server](#step-6-run-as-api-server)
- [Performance Results](#performance-results)
- [VRAM / Memory Breakdown](#vram--memory-breakdown)
- [Useful Parameters Reference](#useful-parameters-reference)
- [Troubleshooting](#troubleshooting)
- [Notes & Tips](#notes--tips)
---
## System Overview
| Component | Value |
|------------------|-----------------------------------------------|
| **GPU** | AMD BC-250 (Navi 10, gfx1010) |
| **VRAM** | ~14.4 GiB (14750 MiB) |
| **Wave Size** | 32 |
| **ROCm Version** | 7.2.0 |
| **HIP Version** | 7.2.26043-9999 |
| **HIP Compiler** | AMD clang 22.0.0git (ROCm LLVM) |
| **OS** | CachyOS (Arch-based), Kernel 6.18.8-3-cachyos |
| **CPU** | 12 threads |
| **RAM** | 14 GiB system + 14 GiB swap |
| **llama.cpp** | Build b8184 (commit `3191462`) |
| **CMake** | 4.2.3 |
---
## Prerequisites
Before starting, ensure you have:
1. **ROCm installed and working** — verify with:
```bash
rocm-smi
rocminfo | grep -E "Name:|gfx"
```
2. **Required packages**:
```bash
# Arch/CachyOS
sudo pacman -S git cmake base-devel aria2
# Ubuntu/Debian
sudo apt install git cmake build-essential aria2
```
3. **ROCm development libraries** (hipblas, rocblas):
```bash
# Verify they exist
ls /opt/rocm/lib/libhipblas.so
ls /opt/rocm/lib/librocblas.so
ls /opt/rocm/lib/llvm/bin/clang++
```
4. **Know your GPU architecture**:
```bash
rocminfo | grep "Name:" | grep gfx
# Output: gfx1010 (for BC-250)
```
---
## Step 1: Clone llama.cpp
```bash
cd ~
git clone https://github.com/ggml-org/llama.cpp.git
cd llama.cpp
```
If already cloned, update:
```bash
cd ~/llama.cpp
git stash # if you have local changes
git pull
```
---
## Step 2: Build llama.cpp with ROCm HIP
### Configure
```bash
cd ~/llama.cpp
rm -rf build
mkdir build && cd build
cmake .. \
-DGGML_HIP=ON \
-DAMDGPU_TARGETS="gfx1010" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_HIP_COMPILER=/opt/rocm/lib/llvm/bin/clang++ \
-G "Unix Makefiles"
```
**Key flags explained:**
| Flag | Purpose |
|------|---------|
| `-DGGML_HIP=ON` | Enable HIP/ROCm GPU backend |
| `-DAMDGPU_TARGETS="gfx1010"` | Target GPU architecture (BC-250 = gfx1010) |
| `-DCMAKE_HIP_COMPILER=/opt/rocm/lib/llvm/bin/clang++` | Use ROCm's clang directly (required for CMake ≥ 4.x, `hipcc` wrapper is rejected) |
| `-G "Unix Makefiles"` | Use Make instead of Ninja |
> **Important (CMake 4.x):** Do NOT use `-DCMAKE_HIP_COMPILER=/opt/rocm/bin/hipcc` — CMake 4.x explicitly rejects the hipcc wrapper. You must point to the clang++ binary inside ROCm's LLVM directory.
### Verify Configuration
```bash
grep "GGML_HIP" CMakeCache.txt
# Should show: GGML_HIP:BOOL=ON
```
### Build
```bash
make -j$(nproc)
```
> **Build time:** HIP compilation is slow (~15-25 minutes on 12 threads). Each `.cu` template gets compiled to AMDGPU ISA for gfx1010. Be patient.
### Verify Build Output
```bash
ls -lh build/bin/llama-cli build/bin/llama-server
# Verify HIP linkage
ldd build/bin/llama-cli | grep -i "hip\|rocm"
```
Expected output:
```
libggml-hip.so.0 => .../libggml-hip.so.0
libhipblas.so.3 => /opt/rocm/lib/libhipblas.so.3
librocblas.so.5 => /opt/rocm/lib/librocblas.so.5
libamdhip64.so.7 => /opt/rocm/lib/libamdhip64.so.7
librocsolver.so.0 => /opt/rocm/lib/librocsolver.so.0
libhsa-runtime64.so.1 => /opt/rocm/lib/libhsa-runtime64.so.1
```
---
## Step 3: Download the Model
### Using aria2 (Recommended — Maximum Speed)
```bash
mkdir -p ~/models
aria2c \
-x 16 \
-s 16 \
-k 1M \
-d ~/models \
-o Qwen3-4B-Q8_0.gguf \
"https://huggingface.co/Qwen/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q8_0.gguf"
```
| aria2 Flag | Purpose |
|------------|---------|
| `-x 16` | 16 connections per server |
| `-s 16` | Split into 16 segments |
| `-k 1M` | Minimum split size 1MB |
### Using wget (Fallback)
```bash
wget -c \
"https://huggingface.co/Qwen/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q8_0.gguf" \
-O ~/models/Qwen3-4B-Q8_0.gguf
```
### Verify Download
```bash
ls -lh ~/models/Qwen3-4B-Q8_0.gguf
# Expected: ~4.0 GiB (4,280,404,704 bytes)
```
> **Model Source:** [Qwen/Qwen3-4B-GGUF](https://huggingface.co/Qwen/Qwen3-4B-GGUF) on Hugging Face (official Qwen repo).
---
## Step 4: Run Inference (Interactive Chat)
```bash
cd ~/llama.cpp/build/bin
./llama-cli \
-m ~/models/Qwen3-4B-Q8_0.gguf \
-ngl 99 \
-c 4096 \
--temp 0.6 \
--top-k 20 \
--top-p 0.95
```
**Expected startup output:**
```
ggml_cuda_init: found 1 ROCm devices:
Device 0: AMD BC-250, gfx1010:xnack- (0x1010), VMM: no, Wave Size: 32
build : b213-3191462
model : Qwen3-4B-Q8_0.gguf
modalities : text
```
You'll get an interactive `>` prompt. Type your question and press Enter.
**In-chat commands:**
| Command | Action |
|-------------|---------------------------------|
| `/exit` | Exit the chat |
| `/clear` | Clear chat history |
| `/regen` | Regenerate last response |
| `/read` | Load a text file into context |
| `Ctrl+C` | Force exit |
### Disable Thinking Mode
Qwen3-4B has a "thinking" mode enabled by default (responses start with `[Start thinking]`). To disable it and get direct answers:
```bash
./llama-cli \
-m ~/models/Qwen3-4B-Q8_0.gguf \
-ngl 99 \
-c 4096 \
--temp 0.7 \
--top-k 20 \
--top-p 0.8 \
--jinja \
--chat-template-file ~/llama.cpp/models/templates/qwen3.jinja \
-e
```
Or append `/no_think` to your prompt for per-message control.
---
## Step 5: Run Inference (One-Shot / Batch)
For scripting or single-prompt usage without interactive mode:
```bash
cd ~/llama.cpp/build/bin
./llama-cli \
-m ~/models/Qwen3-4B-Q8_0.gguf \
-ngl 99 \
-p "Explain what ROCm is in 2 sentences." \
-n 200 \
--no-display-prompt \
--no-conversation
```
---
## Step 6: Run as API Server
llama.cpp includes an OpenAI-compatible HTTP API server:
```bash
cd ~/llama.cpp/build/bin
./llama-server \
-m ~/models/Qwen3-4B-Q8_0.gguf \
-ngl 99 \
-c 4096 \
--host 0.0.0.0 \
--port 8080
```
### Test with curl
```bash
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3-4b",
"messages": [
{"role": "user", "content": "What is ROCm?"}
],
"max_tokens": 200,
"temperature": 0.7
}'
```
### Web UI
Open `http://localhost:8080` in a browser for the built-in chat UI.
---
## Performance Results
Benchmarked on AMD BC-250 with full GPU offload (`-ngl 99`):
| Metric | Value |
|----------------------|----------------|
| **Prompt Processing** | ~84267 t/s |
| **Generation Speed** | ~5557 t/s |
| **Context Size** | 20484096 |
| **GPU Offload** | All 36 layers |
> Prompt processing speed varies by prompt length (shorter prompts = higher t/s due to overhead ratio). Generation speed is consistently **~56-57 tokens/second**.
---
## VRAM / Memory Breakdown
From `llama_memory_breakdown_print` at exit (context size 2048):
| Location | Total | Free | Model | Context | Compute |
|------------------|---------|--------|--------|---------|---------|
| **ROCm0 (BC-250)** | 14750 MiB | 8382 MiB | 4076 MiB | 288 MiB | 301 MiB |
| **Host (CPU)** | — | — | 394 MiB | 0 MiB | 14 MiB |
- **Model weights**: ~4.0 GiB VRAM (matches the Q8_0 file size)
- **Remaining free VRAM**: ~8.4 GiB (plenty of room for larger context windows)
- **Host RAM**: ~394 MiB for metadata
With context size 4096, VRAM usage for context doubles to ~576 MiB, still well within the 14.4 GiB available.
---
## Useful Parameters Reference
| Parameter | Default | Description |
|-----------|---------|-------------|
| `-m` | — | Path to GGUF model file |
| `-ngl 99` | 0 | Number of layers to offload to GPU (99 = all) |
| `-c` | 4096 | Context window size (in tokens) |
| `-n` | -1 | Max tokens to generate (-1 = unlimited) |
| `-p` | — | Initial prompt text |
| `--temp` | 0.6 | Sampling temperature (lower = more deterministic) |
| `--top-k` | 20 | Top-K sampling |
| `--top-p` | 0.95 | Top-P (nucleus) sampling |
| `--no-display-prompt` | off | Don't echo the prompt in output |
| `--no-conversation` | off | Exit after first response (no interactive loop) |
| `-t` | auto | Number of CPU threads |
| `--host` | 127.0.0.1 | Server bind address |
| `--port` | 8080 | Server port |
---
## Troubleshooting
### CMake Error: "CMAKE_HIP_COMPILER is set to the hipcc wrapper"
**Cause:** CMake ≥ 4.x rejects the `hipcc` wrapper script.
**Fix:** Point to the ROCm clang directly:
```bash
-DCMAKE_HIP_COMPILER=/opt/rocm/lib/llvm/bin/clang++
```
### Build Error: "No rule to make target 'libserver-context.a'"
**Cause:** Race condition from running multiple `make` processes in the same build directory simultaneously.
**Fix:** Kill all builds, `rm -rf build`, and rebuild from scratch with a single `make -j$(nproc)`.
### "GGML_HIP:BOOL=OFF" in CMakeCache
**Cause:** ROCm dev libraries not found during cmake configuration.
**Fix:** Ensure `/opt/rocm/lib/libhipblas.so` and `/opt/rocm/lib/llvm/bin/clang++` exist. Re-run cmake.
### Model file is 0 bytes after download
**Cause:** Incorrect URL (case-sensitive) — Hugging Face returns 404.
**Fix:** The correct filename is `Qwen3-4B-Q8_0.gguf` (capital Q, capital B). Full URL:
```
https://huggingface.co/Qwen/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q8_0.gguf
```
### Slow GPU performance / "low-power state" warning
```
WARNING: AMD GPU device(s) is/are in a low-power state
```
The BC-250 may throttle. Force performance mode:
```bash
sudo sh -c 'echo high > /sys/class/drm/card1/device/power_dpm_force_performance_level'
```
### "Exception caught: map::at" in rocm-smi
Known BC-250 issue with rocm-smi power monitoring. Does not affect inference. Ignore safely.
---
## Notes & Tips
- **Qwen3 Thinking Mode**: By default, Qwen3 wraps responses in `[Start thinking]...[End thinking]` blocks showing its reasoning chain. This is a feature, not a bug. Use `--jinja` with the official template or `/no_think` to disable it.
- **Q8_0 Quantization**: This is the highest quality GGUF quantization (8-bit). The 4B parameter model at Q8_0 uses ~4 GiB VRAM, leaving plenty of headroom on the BC-250's ~14.4 GiB.
- **Full GPU Offload**: With `-ngl 99`, all 36 transformer layers are offloaded to the GPU. No CPU fallback needed for this model size.
- **Other Quant Options**: Qwen also provides Q4_K_M (~2.5 GiB) and Q4_0 (~2.3 GiB) variants on the same Hugging Face repo if you want to save VRAM for larger context windows.
- **Multiple Models**: The BC-250 has enough VRAM to potentially run larger models like Qwen3-8B at Q4_K_M quantization (~5 GiB).
---
## Quick Reference
```bash
# Build (one-time)
cd ~/llama.cpp && rm -rf build && mkdir build && cd build
cmake .. -DGGML_HIP=ON -DAMDGPU_TARGETS="gfx1010" -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_HIP_COMPILER=/opt/rocm/lib/llvm/bin/clang++ -G "Unix Makefiles"
make -j$(nproc)
# Download model (one-time)
mkdir -p ~/models
aria2c -x 16 -s 16 -k 1M -d ~/models -o Qwen3-4B-Q8_0.gguf \
"https://huggingface.co/Qwen/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q8_0.gguf"
# Run interactive chat
~/llama.cpp/build/bin/llama-cli -m ~/models/Qwen3-4B-Q8_0.gguf -ngl 99 -c 4096
# Run API server
~/llama.cpp/build/bin/llama-server -m ~/models/Qwen3-4B-Q8_0.gguf -ngl 99 -c 4096 --host 0.0.0.0 --port 8080
```
---
*Documentation generated on March 1, 2026. Based on llama.cpp build b8184, ROCm 7.2.0, AMD BC-250 (gfx1010).*
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,572 @@
# Z-Image-Turbo on AMD BC-250 — Complete Guide
> **Hardware**: AMD BC-250 (Cyan Skillfish, gfx1013→gfx1010, 24 CUs, shared RAM)
> **Backend**: ROCm 7.2.0 / HIP (hipBLAS)
> **OS**: CachyOS, kernel 6.18.8-3-cachyos with v3 kernel patches
> **Server**: [stable-diffusion.cpp-restapi](https://github.com/leejet/stable-diffusion.cpp) (sdcpp-restapi)
---
## 1. Overview
Z-Image-Turbo is a Stable Diffusion model optimized for fast image generation. On the BC-250, it runs via the ROCm/HIP backend using `sdcpp-restapi` as an HTTP server. The server provides a REST API and a WebUI for image generation.
### Architecture
```
[User / WebUI] → HTTP :8080 → [sdcpp-restapi] → [stable-diffusion.cpp] → [HIP/hipBLAS] → [AMD BC-250 GPU]
```
### Pipeline Phases
Each image generation goes through three phases:
| Phase | What it does | Time (BC-250) |
|-------|-------------|---------------|
| **Text-Encoding** (CLIP) | Encodes the text prompt into embeddings | ~0.50.6s |
| **Sampling** (Diffusion) | Iterative denoising (8 steps with Euler sampler) | ~7378s |
| **VAE Decode** | Decodes latent space back to pixel image | ~14s |
| **Total** | End-to-end generation | **~8292s** |
---
## 2. Directory Layout
```
~/stable-diffusion.cpp-restapi/
├── build/
│ ├── bin/sdcpp-restapi # Server binary
│ └── config.json # Server configuration
├── src/ # Server source code
└── build/_deps/stable-diffusion-src/ # SD library (fetched via CMake)
~/sd-models/
├── diffusion_models/
│ └── z_image_turbo-Q5_K_S.gguf # Main diffusion model (5.2 GB, Q5_K_S quantized)
├── vae/
│ └── ae.safetensors # VAE decoder (335 MB, f32)
├── llm/
│ ├── Qwen3-4B-Instruct-2507-Q5_K_S.gguf # LLM for prompt enhancement (2.8 GB)
│ ├── Qwen3-8B-Q4_K_M.gguf # Alternative LLM (5.0 GB)
│ └── gpt-oss-20b-Q4_K_M.gguf # Large LLM (11.6 GB) — too big for BC-250
├── lora/
├── clip/
├── controlnet/
├── esrgan/
└── taesd/
~/sd-outputs/ # Generated images output directory
~/start-zimage.sh # Launch script
~/VibeROCm/ZImage_Documentation.md # This document
```
---
## 3. Building from Source
```bash
cd ~/stable-diffusion.cpp-restapi
mkdir -p build && cd build
cmake .. -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DSD_HIP=ON \
-DSDCPP_WEBUI=ON \
-DCMAKE_PREFIX_PATH=/opt/rocm
ninja -j$(nproc --all)
```
**Requirements**: ROCm 7.2.0, CMake, Ninja, hipBLAS/rocBLAS
---
## 4. Configuration
### Server Config (`~/stable-diffusion.cpp-restapi/build/config.json`)
```json
{
"server": {
"host": "0.0.0.0",
"port": 8080,
"threads": 8
},
"paths": {
"diffusion_models": "/home/dars/sd-models/diffusion_models",
"vae": "/home/dars/sd-models/vae",
"llm": "/home/dars/sd-models/llm",
"lora": "/home/dars/sd-models/lora",
"clip": "/home/dars/sd-models/clip",
"controlnet": "/home/dars/sd-models/controlnet",
"esrgan": "/home/dars/sd-models/esrgan",
"taesd": "/home/dars/sd-models/taesd",
"output": "/home/dars/sd-outputs"
},
"sd_defaults": {
"n_threads": 10,
"keep_clip_on_cpu": true,
"keep_vae_on_cpu": true,
"flash_attn": false,
"offload_to_cpu": true,
"free_params_immediately": true
}
}
```
### Environment Variables (set in `start-zimage.sh`)
#### Required
| Variable | Value | Why |
|----------|-------|-----|
| `HSA_OVERRIDE_GFX_VERSION` | `10.1.0` | BC-250 (gfx1013) needs gfx1010 spoof for ROCm |
| `HSA_ENABLE_SDMA` | `0` | SDMA engine has hardware bugs on gfx1013 |
| `HIP_VISIBLE_DEVICES` | `0` | Select the BC-250 GPU |
| `ROCM_PATH` | `/opt/rocm` | ROCm installation path |
| `HSA_TOOLS_LIB` | `""` | Disable profiling tools (stability) |
| `HSA_TOOLS_REPORT_LOAD_FAILURE` | `0` | Suppress tool warnings |
#### Explicitly Unset (performance-critical!)
These old workaround variables were used during development and **destroy performance** if set. The start script explicitly unsets them:
| Variable | Effect if set | Impact |
|----------|--------------|--------|
| `GPU_MAX_HW_QUEUES=1` | Serializes all GPU operations to 1 queue | Severe slowdown |
| `HIP_LAUNCH_BLOCKING=1` | Forces synchronous kernel launches | Prevents pipelining |
| `GGML_CUDA_ENABLE_UNIFIED_MEMORY=1` | Uses hipMallocManaged (page fault overhead) | +20% slower |
| `GGML_HIP_HOST_ALLOC=1` | Uses hipHostMalloc (zero-copy over PCIe) | +40% slower |
| `GGML_CUDA_NO_PINNED=1` | Disables pinned memory pools | Not needed with v3 patches |
| `GGML_HIP_NO_COARSE_GRAIN=1` | Disables coarse-grain memory | Not needed with v3 patches |
| `HSA_DISABLE_FRAGMENT_ALLOCATOR=1` | Disables memory fragment allocator | Not needed with v3 patches |
---
## 5. Starting the Server
### Using the Launch Script (recommended)
```bash
bash ~/start-zimage.sh
```
Or in background with logging:
```bash
nohup bash ~/start-zimage.sh > /tmp/zimage.log 2>&1 &
```
The script handles:
- GPU health check (aborts if KIQ fence timeout detected — reboot needed)
- Unsetting old workaround variables
- Setting correct ROCm environment
- Starting the server
### Manual Start (if needed)
```bash
export HSA_OVERRIDE_GFX_VERSION=10.1.0
export HSA_ENABLE_SDMA=0
export HSA_TOOLS_LIB=""
export HIP_VISIBLE_DEVICES=0
export ROCM_PATH=/opt/rocm
export PATH="/opt/rocm/bin:$PATH"
export LD_LIBRARY_PATH="/opt/rocm/lib"
# CRITICAL: unset old workarounds
unset GPU_MAX_HW_QUEUES HIP_LAUNCH_BLOCKING
unset GGML_CUDA_ENABLE_UNIFIED_MEMORY GGML_HIP_HOST_ALLOC
unset GGML_CUDA_NO_PINNED GGML_HIP_NO_COARSE_GRAIN
unset HSA_DISABLE_FRAGMENT_ALLOCATOR
cd ~/stable-diffusion.cpp-restapi/build
./bin/sdcpp-restapi --config config.json
```
### Endpoints
| Endpoint | Description |
|----------|-------------|
| `http://localhost:8080/ui` | Web UI |
| `http://localhost:8080` | API root |
| `ws://localhost:8081` | WebSocket (live progress) |
---
## 6. Loading Models
After the server starts, you must load a model before generating images.
### Load Z-Image-Turbo (recommended command)
```bash
curl -s -X POST http://localhost:8080/models/load \
-H "Content-Type: application/json" \
-d '{
"model_name": "z_image_turbo-Q5_K_S.gguf",
"model_type": "diffusion",
"vae": "ae.safetensors",
"llm": "Qwen3-4B-Instruct-2507-Q5_K_S.gguf",
"options": {
"flash_attn": false
}
}'
```
**Expected response** (takes ~25 seconds):
```json
{
"success": true,
"message": "Model loaded successfully",
"model_name": "z_image_turbo-Q5_K_S.gguf",
"model_type": "diffusion",
"loaded_components": {
"vae": "ae.safetensors",
"llm": "Qwen3-4B-Instruct-2507-Q5_K_S.gguf"
}
}
```
### Model Load API Reference
**POST** `/models/load`
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `model_name` | string | **Yes** | Filename of the diffusion model (must be in `diffusion_models/` dir) |
| `model_type` | string | **Yes** | `"diffusion"` for SD models |
| `vae` | string | Yes | VAE filename (from `vae/` dir) |
| `llm` | string | Optional | LLM for prompt enhancement (from `llm/` dir) |
| `options` | object | Optional | Model options (see below) |
### Model Load Options
| Option | Type | Default | Recommended | Description |
|--------|------|---------|-------------|-------------|
| `flash_attn` | bool | `false` | **`false`** | Flash attention — gfx1010 does NOT support it |
| `keep_clip_on_cpu` | bool | `true` | `true` | CLIP runs fine on CPU, saves GPU memory |
| `keep_vae_on_cpu` | bool | `false` | `false` | VAE on GPU is faster |
| `offload_to_cpu` | bool | `false` | `false` | CPU offload hurts performance |
| `vae_decode_only` | bool | `true` | `true` | Only need decode for txt2img |
| `free_params_immediately` | bool | `false` | `false` | Frees memory faster but slower load |
| `vae_conv_direct` | bool | `false` | **`false`** | Direct convolutions — model load never finishes! |
| `diffusion_conv_direct` | bool | `false` | **`false`** | Direct convolutions — model load never finishes! |
| `n_threads` | int | `-1` (auto) | `-1` | CPU threads for compute |
| `enable_mmap` | bool | `true` | `true` | Memory-mapped file loading |
### Unload Model
```bash
curl -s -X POST http://localhost:8080/models/unload
```
---
## 7. Generating Images
### Via API (curl)
**Step 1: Submit a generation job**
```bash
curl -s -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "blonde woman",
"width": 512,
"height": 1024,
"steps": 8,
"cfg_scale": 1,
"sampler": "euler",
"scheduler": "smoothstep",
"seed": 42
}'
```
Response:
```json
{
"job_id": "e95872f8-15cb-48ce-a10f-19f2de430d9e",
"position": 1,
"status": "pending"
}
```
**Step 2: Poll for completion**
```bash
curl -s http://localhost:8080/queue/<job_id>
```
Response when done:
```json
{
"status": "completed",
"duration": "92.2s",
"outputs": ["image_filename.png"]
}
```
### Via WebUI
Open `http://localhost:8080/ui` in a browser.
### Generation Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `prompt` | string | Required | Text description of the image |
| `negative_prompt` | string | `""` | What to avoid |
| `width` | int | 512 | Image width (pixels) |
| `height` | int | 512 | Image height (pixels) |
| `steps` | int | 20 | Sampling steps (8 is optimal for Z-Image-Turbo) |
| `cfg_scale` | float | 7.0 | Classifier-free guidance scale (1.0 for turbo models) |
| `sampler` | string | `"euler"` | Sampling method |
| `scheduler` | string | `"smoothstep"` | Noise schedule |
| `seed` | int | random | Seed for reproducibility (-1 = random) |
### Recommended Settings for Z-Image-Turbo
```json
{
"steps": 8,
"cfg_scale": 1,
"sampler": "euler",
"scheduler": "smoothstep"
}
```
Z-Image-Turbo is a distilled model — 8 steps is the sweet spot. More steps don't improve quality.
---
## 8. Performance Benchmarks
All benchmarks: 512×1024 image, 8 steps, Euler sampler, smoothstep scheduler, seed 42, prompt "blonde woman".
### Best Result: ~82s (measured warm run, 3rd consecutive generation)
### Systematic Benchmark Matrix
| Config | Text-Enc | Sampling | VAE Decode | Total | vs Baseline |
|--------|----------|----------|------------|-------|-------------|
| **Clean (cold, 1st gen)** | 0.54s | 77.4s | 13.9s | **92s** | Baseline |
| **Clean (warm, 2nd gen)** | — | — | — | **85s** | -8% |
| **Clean (warm, 3rd gen)** | — | — | — | **82s** | -11% |
| `GGML_CUDA_ENABLE_UNIFIED_MEMORY=1` | 0.52s | 82.7s | 26.1s | **109s** | +18% slower |
| `GGML_HIP_HOST_ALLOC=1` | 0.55s | 98.3s | 29.8s | **129s** | +40% slower |
| `conv_direct=true` (both) | — | — | — | **N/A** | Model load stalls (>13 min, aborted) |
| Vulkan backend (RADV) | — | — | — | **~79s** | Reference |
### Key Findings
1. **Clean config is best**: No GGML environment variables. The v3 kernel patches make all old workarounds unnecessary.
2. **Warm vs cold run**: First generation after model load takes ~92s. Subsequent runs improve to ~85s (2nd) → ~82s (3rd+) as GPU caches warm up.
3. **GGML_CUDA_ENABLE_UNIFIED_MEMORY=1 hurts**: `hipMallocManaged` causes page fault overhead → +18% slower sampling, +88% slower VAE.
4. **GGML_HIP_HOST_ALLOC=1 worst**: Zero-copy over PCIe is terrible → +27% slower sampling, +114% slower VAE.
5. **conv_direct is broken**: Setting `vae_conv_direct=true` and/or `diffusion_conv_direct=true` causes the model load to hang indefinitely (13+ minutes, never completes). These bypass rocBLAS im2col+GEMM path for direct convolutions, but the compute graph build is too expensive for this model size (244 VAE f32 tensors + 180 diffusion q5_K tensors).
6. **ROCm vs Vulkan gap**: ROCm (8292s) vs Vulkan (79s). ROCm warm runs are very close to Vulkan (~82s vs ~79s, only ~4% gap). The remaining overhead is ggml compute graph scheduling, not GPU compute itself (~10s actual GPU time vs ~67s framework overhead in sampling).
### Timing Breakdown
The 92s total breaks down as:
```
Text-Encoding (CLIP on CPU): 0.5s ( 0.5%) — NOT a bottleneck
Sampling (8 steps diffusion): 77.4s ( 84.1%) — main bottleneck
├── Actual GPU compute: ~10s ( 10.9%)
└── ggml framework overhead: ~67s ( 73.2%) — graph scheduling, kernel launches
VAE Decode: 13.9s ( 15.1%)
```
The 67s ggml overhead is an upstream limitation of the stable-diffusion.cpp framework, not something fixable via configuration.
---
## 9. Tested Configurations (Full History)
All the environment variable combinations tested during optimization:
### Config 1: "All Workarounds" (pre-v3 era)
```bash
GPU_MAX_HW_QUEUES=1
HIP_LAUNCH_BLOCKING=1
GGML_CUDA_ENABLE_UNIFIED_MEMORY=1
GGML_HIP_HOST_ALLOC=1
GGML_CUDA_NO_PINNED=1
GGML_HIP_NO_COARSE_GRAIN=1
HSA_DISABLE_FRAGMENT_ALLOCATOR=1
```
**Result**: ~155s initially, ~82s with warm cache. These variables were needed before v3 kernel patches but are harmful now.
### Config 2: Clean (current optimal)
```bash
# NO GGML variables set at all
# Only ROCm basics:
HSA_OVERRIDE_GFX_VERSION=10.1.0
HSA_ENABLE_SDMA=0
HIP_VISIBLE_DEVICES=0
```
**Result**: 92s cold / ~82s warm (3rd gen). **This is the recommended config.**
### Config 3: UNIFIED_MEMORY only
```bash
GGML_CUDA_ENABLE_UNIFIED_MEMORY=1
```
**Result**: 109s (+18%). hipMallocManaged page faults add overhead.
### Config 4: HIP_HOST_ALLOC only
```bash
GGML_HIP_HOST_ALLOC=1
```
**Result**: 129s (+40%). Zero-copy over PCIe kills performance.
### Config 5: Conv Direct
```bash
# Model load options:
vae_conv_direct=true
diffusion_conv_direct=true
```
**Result**: Model load never completes (>13 minutes at 100% CPU, aborted). Not viable.
---
## 10. Troubleshooting
### Server won't start
```
[ERROR] GPU is in a broken state (KIQ fence timeout detected)
```
**Fix**: Reboot the system. The BC-250 GPU cannot recover from KIQ timeouts.
```bash
sudo reboot
```
### Model load fails
```json
{"error": "Main model not found: ''"}
```
**Fix**: Use `model_name` field (not `model` or `model_path`). Value must be the **filename only** (not full path). The server looks in the configured `diffusion_models/` directory.
### Generation hangs or crashes
- **Do NOT run** `rocm-smi`, `clinfo`, or any other GPU query tool while the server is running
- **Do NOT start** multiple GPU processes simultaneously
- If the server crashes, **reboot before restarting** — the GPU state may be corrupted
### Old environment variables leaking from .bashrc
If generation is unexpectedly slow (>100s), check for leftover variables:
```bash
env | grep -E "GGML_|GPU_MAX|HIP_LAUNCH"
```
If any of the old workaround vars are set, unset them:
```bash
unset GPU_MAX_HW_QUEUES HIP_LAUNCH_BLOCKING
unset GGML_CUDA_ENABLE_UNIFIED_MEMORY GGML_HIP_HOST_ALLOC
unset GGML_CUDA_NO_PINNED GGML_HIP_NO_COARSE_GRAIN
unset HSA_DISABLE_FRAGMENT_ALLOCATOR
```
### flash_attn errors
```
gfx1010 does not support flash attention
```
**Fix**: Always load with `"flash_attn": false`. The gfx1010 architecture does not have the required hardware. This is already the default in the patched code.
---
## 11. Quick Reference
### Start Server + Load Model + Generate Image (one-liner workflow)
```bash
# 1. Start server
nohup bash ~/start-zimage.sh > /tmp/zimage.log 2>&1 &
sleep 5
# 2. Load model
curl -s -X POST http://localhost:8080/models/load \
-H "Content-Type: application/json" \
-d '{
"model_name": "z_image_turbo-Q5_K_S.gguf",
"model_type": "diffusion",
"vae": "ae.safetensors",
"llm": "Qwen3-4B-Instruct-2507-Q5_K_S.gguf",
"options": {"flash_attn": false}
}'
# 3. Generate image
JOB=$(curl -s -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "blonde woman",
"width": 512, "height": 1024,
"steps": 8, "cfg_scale": 1,
"sampler": "euler", "scheduler": "smoothstep"
}' | python3 -c "import sys,json; print(json.load(sys.stdin)['job_id'])")
echo "Job: $JOB"
# 4. Poll until done
while true; do
STATUS=$(curl -s http://localhost:8080/queue/$JOB | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('status','?'))")
echo "Status: $STATUS"
[ "$STATUS" = "completed" ] && break
sleep 10
done
# 5. Check result
curl -s http://localhost:8080/queue/$JOB | python3 -m json.tool
```
### Check Server Logs
```bash
tail -f /tmp/zimage.log
```
### Check Timing Breakdown
```bash
grep -a "get_learned\|sampling completed\|decode_first\|generate_image completed" /tmp/zimage.log | tail -4
```
---
## 12. Important Warnings
1. **Do NOT run `rocm-smi`** or any GPU monitoring tool while the server is running — it can crash the GPU
2. **Reboot after any crash** — the BC-250 GPU state cannot be recovered without a full reboot
3. **Do NOT set old GGML environment variables** — they are not needed with v3 kernel patches and severely hurt performance
4. **Do NOT use `conv_direct`** — model load hangs indefinitely
5. **Do NOT use `flash_attn: true`** — gfx1010 does not support flash attention
6. **One GPU process at a time** — the BC-250 cannot handle concurrent GPU workloads
@@ -0,0 +1,12 @@
# AMD BC-250 (Cyan Skillfish / gfx1013) — ROCm Stability Parameters
# This GPU is APU-like with shared system memory (no dedicated VRAM).
#
# noretry=0 — Allow page fault retry (critical for shared memory / APU)
# gpu_recovery=1 — Enable GPU recovery on timeout
# sched_hw_submission=2 — Limit concurrent HW submissions (prevent queue overload)
# ppfeaturemask=0xfff73ef7 — Disable GFXOFF (bit 15), SCLK_DEEP_SLEEP (bit 3),
# and ULV (bit 8) to prevent GPU from entering
# unrecoverable power-saving states.
# Clock management is handled by cyan-skillfish-governor.
# Default is 0xfff7bfff.
options amdgpu noretry=0 gpu_recovery=1 sched_hw_submission=2 ppfeaturemask=0xfff73ef7
@@ -0,0 +1,106 @@
#!/bin/bash
# =============================================================================
# GPU Watchdog for AMD BC-250 (Cyan Skillfish)
# =============================================================================
# Monitors kernel logs for KIQ fence timeouts and immediately kills the
# offending GPU process before the timeout cascade crashes the system.
#
# BC-250 Crash Pattern:
# 1st KIQ timeout → 12s → 2nd KIQ timeout → 12s → cascade → hard crash
# Window to act: ~10 seconds after first timeout
#
# Usage:
# ./gpu_watchdog.sh # Monitor and auto-kill
# ./gpu_watchdog.sh --dry-run # Monitor only, don't kill
# ./gpu_watchdog.sh --max-kiq 2 # Kill after 2 KIQ timeouts
# =============================================================================
set -euo pipefail
MAX_KIQ_TIMEOUTS=1
CHECK_INTERVAL=2
DRY_RUN=false
LOG_FILE="/tmp/gpu_watchdog.log"
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run) DRY_RUN=true; shift ;;
--max-kiq) MAX_KIQ_TIMEOUTS="$2"; shift 2 ;;
--interval) CHECK_INTERVAL="$2"; shift 2 ;;
*) echo "Unknown: $1"; exit 1 ;;
esac
done
log() {
local msg="[$(date '+%H:%M:%S')] $1"
echo "$msg"
echo "$msg" >> "$LOG_FILE"
}
get_kiq_count() {
local count
count=$(journalctl -k -b --no-pager 2>/dev/null | grep -c "timeout waiting for kiq fence" | head -1 | tr -d '[:space:]')
echo "${count:-0}"
}
kill_gpu_processes() {
local pids
pids=$(fuser /dev/kfd 2>/dev/null || pgrep -f "sdcpp-restapi" 2>/dev/null || true)
pids=$(echo "$pids" | xargs)
if [[ -z "$pids" ]]; then
log "WARN: No GPU processes found"
return 1
fi
for pid in $pids; do
local name
name=$(ps -p "$pid" -o comm= 2>/dev/null || echo "?")
if [[ "$DRY_RUN" == "true" ]]; then
log "DRY-RUN: Would SIGKILL PID $pid ($name)"
else
log "SIGKILL PID $pid ($name)"
kill -9 "$pid" 2>/dev/null || true
fi
done
if [[ "$DRY_RUN" == "false" ]]; then
systemctl --user stop zimage 2>/dev/null || true
log "Stopped zimage service"
fi
}
echo "=========================================="
echo " GPU Watchdog — AMD BC-250"
echo " Kill after: $MAX_KIQ_TIMEOUTS KIQ timeout(s)"
echo " Interval: ${CHECK_INTERVAL}s"
echo " Dry run: $DRY_RUN"
echo "=========================================="
log "Watchdog started"
INITIAL_KIQ=$(get_kiq_count)
PREV_KIQ=$INITIAL_KIQ
log "Baseline KIQ count: $INITIAL_KIQ"
[[ "$INITIAL_KIQ" -gt 0 ]] && log "WARNING: GPU already has $INITIAL_KIQ pre-existing KIQ timeouts"
while true; do
CUR=$(get_kiq_count)
NEW=$((CUR - INITIAL_KIQ))
if [[ "$CUR" -ne "$PREV_KIQ" ]]; then
log "!! KIQ timeout #$CUR (new=$NEW)"
if [[ "$NEW" -ge "$MAX_KIQ_TIMEOUTS" ]]; then
log "!!! THRESHOLD HIT — EMERGENCY KILL !!!"
kill_gpu_processes
log "GPU processes killed. Reboot needed for clean GPU."
INITIAL_KIQ=$CUR
log "Watchdog reset, continuing..."
fi
fi
PREV_KIQ=$CUR
sleep "$CHECK_INTERVAL"
done
@@ -0,0 +1,180 @@
# Z-Image-Turbo on AMD BC-250 — Setup Documentation
## Overview
**Z-Image-Turbo** image generation server running on **AMD BC-250** (GFX1013 RDNA2, 24 CUs) via **Vulkan** (RADV/Mesa) on **CachyOS**.
| Component | Detail |
|-----------|--------|
| **GPU** | AMD BC-250 — 24 CUs, GFX1013 RDNA2, 16 GB shared GDDR6 |
| **Driver** | RADV (Mesa 25.3.4) via Vulkan 1.4.335 |
| **Backend** | Vulkan only (no ROCm) |
| **Server** | [stable-diffusion.cpp-restapi](https://github.com/fszontagh/stable-diffusion.cpp-restapi) |
| **OS** | CachyOS (Arch-based) |
## Models
| Model | File | Size | Location |
|-------|------|------|----------|
| Z-Image-Turbo | `z_image_turbo-Q5_K_S.gguf` | 4.9 GB | `~/sd-models/diffusion_models/` |
| Qwen3-4B Instruct | `Qwen3-4B-Instruct-2507-Q5_K_S.gguf` | 2.8 GB | `~/sd-models/llm/` |
| FLUX VAE | `ae.safetensors` | 320 MB | `~/sd-models/vae/` |
## Directory Structure
```
~/
├── stable-diffusion.cpp-restapi/
│ └── build/
│ ├── bin/sdcpp-restapi # Server binary (80 MB)
│ ├── config.json # Server configuration
│ └── webui/ # Vue.js WebUI
├── sd-models/
│ ├── diffusion_models/ # Main diffusion model GGUFs (Z-Image)
│ ├── checkpoints/ # Legacy checkpoint models
│ ├── vae/ # VAE models
│ ├── llm/ # LLM models (prompt enhancement)
│ ├── lora/ # LoRA adapters
│ ├── clip/ # CLIP models
│ ├── t5/ # T5 text encoders
│ ├── embeddings/ # Textual inversions
│ ├── controlnet/ # ControlNet models
│ ├── esrgan/ # Upscaler models
│ └── taesd/ # Tiny AutoEncoder models
├── sd-outputs/ # Generated images
├── start-zimage.sh # Quick start script
└── .config/systemd/user/
└── zimage.service # Systemd user service
```
## Quick Start
```bash
# Option 1: Manual start
~/start-zimage.sh
# Option 2: Systemd service
systemctl --user start zimage
```
Then open: **http://localhost:8080/ui**
## Usage
### WebUI
1. Open http://localhost:8080/ui
2. Go to **Models** → select `z_image_turbo-Q5_K_S.gguf`
3. Set VAE to `ae.safetensors`, LLM to `Qwen3-4B-Instruct-2507-Q5_K_S.gguf`
4. First load takes 2040 seconds
5. Settings: **Steps 8**, **CFG 1.0**, **Euler** sampler
6. Start with 512×512 (~37 s), then try 1024×1024 (~80 s)
### API — Load Model
```bash
curl -X POST http://localhost:8080/models/load \
-H "Content-Type: application/json" \
-d '{
"model_name": "z_image_turbo-Q5_K_S.gguf",
"model_type": "diffusion",
"vae": "ae.safetensors",
"llm": "Qwen3-4B-Instruct-2507-Q5_K_S.gguf"
}'
```
### API — Generate Image (512×512)
```bash
curl -X POST http://localhost:8080/txt2img \
-H "Content-Type: application/json" \
-d '{
"prompt": "astronaut on mars, cinematic",
"width": 512,
"height": 512,
"steps": 8,
"cfg_scale": 1.0,
"sampler_name": "euler",
"scheduler": "smoothstep",
"seed": -1
}' --output image.png
```
## Systemd Service
```bash
# Enable auto-start on login
systemctl --user enable zimage
# Start / stop / restart
systemctl --user start zimage
systemctl --user stop zimage
systemctl --user restart zimage
# View logs
journalctl --user -u zimage -f
```
## Vulkan Environment Variables
| Variable | Value | Purpose |
|----------|-------|---------|
| `AMD_VULKAN_ICD` | `RADV` | Use Mesa RADV driver |
| `GGML_VK_FORCE_MAX_ALLOCATION_SIZE` | `536870912` | 512 MB max alloc (OOM prevention) |
| `RADV_PERFTEST` | `nggc` | NGG culling compute boost |
## Troubleshooting
### Slow generation (>20s at 1024×1024)
```bash
# Check logs for allocation failures
journalctl --user -u zimage -f
# Try reducing max allocation to 256 MB
export GGML_VK_FORCE_MAX_ALLOCATION_SIZE=268435456
# Try AMDVLK instead of RADV
sudo pacman -S amdvlk
export AMD_VULKAN_ICD=AMDVLK
```
### Vulkan not detecting GPU
```bash
# Verify Vulkan
vulkaninfo --summary | grep BC-250
# Force ICD file path
VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/radeon_icd.x86_64.json vulkaninfo
```
### Out of Memory (OOM)
- Use Q3_K_S quantization (smaller model)
- Set `batch_count: 1` in generation requests
- Reduce resolution to 512×512
### Monitor GPU
```bash
# GPU utilization
watch radeontop
# VRAM usage
cat /sys/class/drm/card0/device/mem_info_vram_used
```
## Build from Source (Reference)
```bash
cd ~/stable-diffusion.cpp-restapi
mkdir -p build && cd build
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DSD_VULKAN=ON -DSDCPP_WEBUI=ON
ninja -j$(nproc --all)
```
## Performance Expectations
| Resolution | Expected Time | Notes |
|------------|--------------|-------|
| 512×512 | ~37 seconds | Q5_K_S, 8 steps, Euler, Vulkan |
| 512×1024 | ~80 seconds | Q5_K_S, 8 steps, Euler, Vulkan |
| 1024×1024 | ~150 seconds | Q5_K_S, 8 steps, Euler, Vulkan |
| First load | 2040 seconds | One-time on startup |
@@ -0,0 +1,80 @@
/**
* Minimal HIP diagnostic — step-by-step GPU compute validation
* Tests each operation individually with timeout awareness
*/
#include <hip/hip_runtime.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define HIP_CHECK(call) do { \
hipError_t err = call; \
if (err != hipSuccess) { \
fprintf(stderr, "HIP ERROR [%d]: %s at %s:%d\n", \
(int)err, hipGetErrorString(err), __FILE__, __LINE__); \
exit(1); \
} \
} while(0)
__global__ void simpleKernel(int* out) {
out[threadIdx.x] = threadIdx.x * 2;
}
int main() {
setbuf(stdout, NULL); // Force unbuffered output
setbuf(stderr, NULL);
printf("STEP 0: HIP init\n");
int count;
HIP_CHECK(hipGetDeviceCount(&count));
printf(" Devices: %d\n", count);
hipDeviceProp_t p;
HIP_CHECK(hipGetDeviceProperties(&p, 0));
printf(" Device: %s, CU=%d, Mem=%zuMB, isIntegrated=%d\n",
p.name, p.multiProcessorCount, p.totalGlobalMem/(1024*1024), p.integrated);
printf("STEP 1: hipMalloc (small: 256 bytes)\n");
int *d_buf = nullptr;
HIP_CHECK(hipMalloc(&d_buf, 256));
printf(" OK: d_buf=%p\n", (void*)d_buf);
printf("STEP 2: hipMemset\n");
HIP_CHECK(hipMemset(d_buf, 0, 256));
printf(" OK\n");
printf("STEP 3: hipMemcpy H2D (64 ints)\n");
int h_buf[64];
for (int i = 0; i < 64; i++) h_buf[i] = i + 100;
HIP_CHECK(hipMemcpy(d_buf, h_buf, 256, hipMemcpyHostToDevice));
printf(" OK\n");
printf("STEP 4: Launch kernel (1 block, 32 threads)\n");
simpleKernel<<<1, 32>>>(d_buf);
HIP_CHECK(hipGetLastError());
printf(" Launched\n");
printf("STEP 5: hipDeviceSynchronize\n");
HIP_CHECK(hipDeviceSynchronize());
printf(" OK\n");
printf("STEP 6: hipMemcpy D2H\n");
int h_out[64];
memset(h_out, 0, 256);
HIP_CHECK(hipMemcpy(h_out, d_buf, 128, hipMemcpyDeviceToHost));
printf(" OK\n");
printf("STEP 7: Verify results\n");
int pass = 1;
for (int i = 0; i < 32; i++) {
if (h_out[i] != i * 2) {
printf(" FAIL: h_out[%d]=%d expected %d\n", i, h_out[i], i*2);
pass = 0;
}
}
if (pass) printf(" ALL 32 VALUES CORRECT\n");
HIP_CHECK(hipFree(d_buf));
printf("\nRESULT: %s\n", pass ? "PASS — ROCm HIP COMPUTE WORKS" : "FAIL");
return pass ? 0 : 1;
}
@@ -0,0 +1,108 @@
/**
* HIP Minimal Probe — Safe diagnostic for AMD BC-250
* Step-by-step: each step prints BEFORE attempting, so we know where it hangs/crashes
*/
#include <hip/hip_runtime.h>
#include <cstdio>
#include <unistd.h> // _exit() — bypasses C++ destructors
int main() {
printf("PROBE: Starting HIP minimal probe...\n");
fflush(stdout);
// Step 1: hipGetDeviceCount
printf("PROBE [1/6]: hipGetDeviceCount... ");
fflush(stdout);
int count = 0;
hipError_t err = hipGetDeviceCount(&count);
if (err != hipSuccess) {
printf("FAILED: %s\n", hipGetErrorString(err));
return 1;
}
printf("OK (%d devices)\n", count);
fflush(stdout);
if (count == 0) {
printf("PROBE: No devices. Exiting.\n");
return 1;
}
// Step 2: hipGetDeviceProperties
printf("PROBE [2/6]: hipGetDeviceProperties... ");
fflush(stdout);
hipDeviceProp_t props;
err = hipGetDeviceProperties(&props, 0);
if (err != hipSuccess) {
printf("FAILED: %s\n", hipGetErrorString(err));
return 1;
}
printf("OK\n");
printf(" Name: %s\n", props.name);
printf(" GCN Arch: %s\n", props.gcnArchName);
printf(" CUs: %d\n", props.multiProcessorCount);
printf(" Total Mem: %zu MB\n", props.totalGlobalMem / (1024*1024));
printf(" Managed Memory: %s\n", props.managedMemory ? "YES" : "NO");
printf(" Concurrent Managed: %s\n", props.concurrentManagedAccess ? "YES" : "NO");
printf(" Integrated (APU): %s\n", props.integrated ? "YES" : "NO");
printf(" pageableMemoryAccess: %s\n", props.pageableMemoryAccess ? "YES" : "NO");
fflush(stdout);
// Step 3: hipSetDevice
printf("PROBE [3/6]: hipSetDevice(0)... ");
fflush(stdout);
err = hipSetDevice(0);
if (err != hipSuccess) {
printf("FAILED: %s\n", hipGetErrorString(err));
return 1;
}
printf("OK\n");
fflush(stdout);
// Step 4: Try hipHostMalloc (pinned host memory — safest for APU)
printf("PROBE [4/6]: hipHostMalloc (64 KB, coherent)... ");
fflush(stdout);
float* hostPtr = nullptr;
err = hipHostMalloc(&hostPtr, 64 * 1024, hipHostMallocCoherent);
if (err != hipSuccess) {
printf("FAILED: %s\n", hipGetErrorString(err));
printf(" Trying hipHostMallocDefault...\n");
err = hipHostMalloc(&hostPtr, 64 * 1024, hipHostMallocDefault);
if (err != hipSuccess) {
printf(" Also FAILED: %s\n", hipGetErrorString(err));
return 1;
}
}
printf("OK (ptr=%p)\n", (void*)hostPtr);
fflush(stdout);
// Step 5: Try hipMallocManaged (unified memory)
printf("PROBE [5/6]: hipMallocManaged (64 KB)... ");
fflush(stdout);
float* managedPtr = nullptr;
err = hipMallocManaged(&managedPtr, 64 * 1024);
if (err != hipSuccess) {
printf("FAILED: %s (this may be expected without XNACK)\n", hipGetErrorString(err));
fflush(stdout);
} else {
printf("OK (ptr=%p)\n", (void*)managedPtr);
fflush(stdout);
// Write test
managedPtr[0] = 42.0f;
printf(" Write test: managedPtr[0] = %f\n", managedPtr[0]);
fflush(stdout);
hipFree(managedPtr);
}
// Step 6: Free host memory — do NOT call hipDeviceReset()!
// hipDeviceReset() causes KIQ fence timeout → system hang on BC-250
printf("PROBE [6/6]: Cleanup (no device reset)... ");
fflush(stdout);
hipHostFree(hostPtr);
// hipDeviceReset() intentionally omitted — crashes BC-250
printf("OK\n");
fflush(stdout);
printf("\nPROBE: ALL STEPS COMPLETED SUCCESSFULLY\n");
fflush(stdout);
_exit(0); // HARD EXIT — bypasses HIP runtime destructors that crash BC-250
}
@@ -0,0 +1,151 @@
/**
* HIP Vector Addition Test — AMD BC-250 ROCm Validation
*
* Uses hipMallocManaged (unified memory) — REQUIRED for BC-250 which is an
* APU-like device with shared system memory (no dedicated VRAM).
* Standard hipMalloc + hipMemcpy will crash the system!
*
* Verifies GPU compute works end-to-end:
* 1. HIP runtime initializes & device query
* 2. Managed memory allocation (unified address space)
* 3. GPU kernel execution
* 4. Results are numerically correct
*/
#include <hip/hip_runtime.h>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <unistd.h> // _exit() — bypasses C++ destructors
#define HIP_CHECK(call) do { \
hipError_t err = call; \
if (err != hipSuccess) { \
fprintf(stderr, "HIP Error: %s at %s:%d\n", \
hipGetErrorString(err), __FILE__, __LINE__); \
fflush(stderr); \
exit(1); \
} \
} while(0)
__global__ void vectorAdd(const float* A, const float* B, float* C, int N) {
int i = blockDim.x * blockIdx.x + threadIdx.x;
if (i < N) {
C[i] = A[i] + B[i];
}
}
int main() {
printf("=== HIP Vector Addition Test (Managed Memory) ===\n");
printf(" Target: AMD BC-250 (gfx1013, APU shared memory)\n\n");
fflush(stdout);
// Step 1: Query device
printf("[1/4] Querying HIP device... ");
fflush(stdout);
int deviceCount = 0;
HIP_CHECK(hipGetDeviceCount(&deviceCount));
if (deviceCount == 0) {
printf("FAIL: No HIP devices found!\n");
return 1;
}
hipDeviceProp_t props;
HIP_CHECK(hipGetDeviceProperties(&props, 0));
printf("OK\n");
printf(" Device: %s\n", props.name);
printf(" GCN Arch: %s\n", props.gcnArchName);
printf(" Compute Units: %d\n", props.multiProcessorCount);
printf(" Total Memory: %zu MB (shared system RAM)\n", props.totalGlobalMem / (1024*1024));
printf(" Integrated: %s\n", props.integrated ? "YES (APU)" : "NO");
printf(" Managed Memory: %s\n", props.managedMemory ? "YES" : "NO");
fflush(stdout);
// Step 2: Allocate MANAGED memory (unified — safe for APU/shared memory)
const int N = 1 << 16; // 65536 elements
size_t bytes = N * sizeof(float);
printf("[2/4] Allocating managed memory (%.1f KB x3)... ", bytes/1024.0);
fflush(stdout);
float *A = nullptr, *B = nullptr, *C = nullptr;
HIP_CHECK(hipMallocManaged(&A, bytes));
HIP_CHECK(hipMallocManaged(&B, bytes));
HIP_CHECK(hipMallocManaged(&C, bytes));
printf("OK\n");
fflush(stdout);
// Initialize on host (managed memory is accessible from both CPU and GPU)
for (int i = 0; i < N; i++) {
A[i] = sinf(i) * sinf(i);
B[i] = cosf(i) * cosf(i);
C[i] = 0.0f;
}
// Step 3: Launch kernel
int threadsPerBlock = 256;
int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;
printf("[3/4] Launching kernel (%d blocks x %d threads)... ", blocksPerGrid, threadsPerBlock);
fflush(stdout);
hipEvent_t start, stop;
HIP_CHECK(hipEventCreate(&start));
HIP_CHECK(hipEventCreate(&stop));
HIP_CHECK(hipEventRecord(start));
hipLaunchKernelGGL(vectorAdd, dim3(blocksPerGrid), dim3(threadsPerBlock), 0, 0, A, B, C, N);
HIP_CHECK(hipGetLastError());
HIP_CHECK(hipEventRecord(stop));
HIP_CHECK(hipEventSynchronize(stop));
float ms = 0;
HIP_CHECK(hipEventElapsedTime(&ms, start, stop));
printf("OK (%.3f ms)\n", ms);
fflush(stdout);
// Step 4: Verify on host (managed memory — no memcpy needed!)
printf("[4/4] Verifying results... ");
fflush(stdout);
// Ensure GPU is done
HIP_CHECK(hipDeviceSynchronize());
int errors = 0;
for (int i = 0; i < N; i++) {
float expected = A[i] + B[i]; // sin²(x) + cos²(x) = 1.0
if (fabsf(C[i] - expected) > 1e-5) {
if (errors < 5) {
fprintf(stderr, "\n Mismatch at [%d]: got %f, expected %f",
i, C[i], expected);
}
errors++;
}
}
if (errors == 0) {
printf("PASSED — all %d elements correct (sin²+cos²=1.0)\n", N);
} else {
printf("FAILED — %d/%d mismatches\n", errors, N);
}
fflush(stdout);
// CRITICAL BC-250 WORKAROUND:
// The HIP runtime's static destructors trigger KIQ queue teardown on the
// BC-250's shared-memory architecture, causing "timeout waiting for kiq fence"
// and a full system hang (no GPU reset possible on shared RAM APU).
//
// We MUST use _exit() to terminate immediately, bypassing:
// - C++ static destructors (HIP runtime cleanup)
// - atexit() handlers
// - HIP's internal queue teardown via KIQ
//
// Memory is reclaimed by the OS. The GPU queues are released by KFD
// when the process file descriptors are closed, which is safer than
// the HIP runtime's explicit teardown path.
printf("\n=== ROCm HIP Compute: %s ===\n", errors == 0 ? "FULLY OPERATIONAL" : "FAILED");
fflush(stdout);
fflush(stderr);
_exit(errors > 0 ? 1 : 0); // HARD EXIT — bypasses HIP destructors
}
@@ -0,0 +1,130 @@
> The system will freeze and I have to force reboot it.
You need an old kernel (~5.10.0) and proprietary drivers (It won't freeze completely and the amount of VRAM will be correctly determined in pytorch).
Try:
hiveos-0.6-217-stable (5.10.0-hiveos #110.hiveos.220411) or old ubuntu (focal or maybe jammy)
22.20.5 AMDGPU-PRO driver
https://repo.radeon.com/rocm/manylinux/
https://repo.radeon.com/rocm/apt/
https://www.reddit.com/r/Hiveon_Official/comments/tzttyu/hiveon_os_v06215220409_whats_new/
the closest version I could find hiveos-0.6-217-stable@220423
https://web.archive.org/web/20220511060106/https://download.hiveos.farm/history/
https://web.archive.org/web/20220514095728/https://download.hiveos.farm/history/hiveos-0.6-217-stable@220423.img.xz
https://github.com/minershive/hiveos-pxe-diskless/issues/26 ->
https://github.com/panaceya/hiveos-pxe-diskless/compare/master...TheJames5:hiveos-pxe-diskless:master#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R7
also https://github.com/Gddrig/Qubic-AMD/releases see old releases
23
You can try running it under the 1010 architecture:
export HSA_OVERRIDE_GFX_VERSION=10.1.0
export AMDGPU_TARGETS=gfx1010
export HCC_AMDGPU_TARGET=gfx1010
export PYTORCH_ROCM_ARCH=gfx1010
export HSA_ENABLE_SDMA=0
export HSA_ENABLE_PEER_SDMA=0
ROCM 5.2, which afaik is the "last known good" version for RDNA1 cards
Something else to maybe try: IIRC from ROCM 5.3-6.0 they broke support for gfx101* (and fixed it again in 6.1). I remember reading that post-6.1+ there were some pretty significant performance regressions in 6.x for this GPU target, which weren't present in 5.2
Yeah when I was trying this, I wasn't able to get ROCm to build compute kernels/tensor libraries specifically for the gfx1013... had to build everything targetting gfx1010 and then run programs with HSA_OVERRIDE_GFX_VERSION=10.1.0 to force it to detect the gfx1013 as a gfx1010 (since its ISA is a superset of gfx1010, it should work in theory)
afaik none of the rocm libraries have build configs for gfx1013 in any of the versions I checked, but they have gfx1010-1012
Also be careful not to try to run gfx1030 code on it, since that's a different isa I believe (in some cases I had it default to this)
But you will probably have to compile rocm and pytorch kernels for the bc250 architecture (gfx1013).
Examples of projects where they built for unsupported architectures:
https://github.com/ulyssesrr/docker-rocm-xtra
https://github.com/xuhuisheng/rocm-build/tree/master/navi10
https://github.com/woodrex83/ROCm-For-RX580
https://github.com/robertrosenbusch/gfx803_rocm
more about regression https://github.com/ROCm/ROCm/discussions/4030
GitHub
Regression in rocm 5.3 and newer for gfx1010 · ROCm ROCm · Discus...
Since when pytorch 2 was officially released, i wasn&#39;t able to run it on my 5700XT, while i was previously able to use it just fine on pytorch 1.13.1 by setting &quot;export HSA_OVERRIDE_GFX_VE...
Regression in rocm 5.3 and newer for gfx1010 · ROCm ROCm · Discus...
Starting glibc 2.41, the precompiled PyTorch wheels with ROCm 5.2 support no longer work due to changes in the stack execution policy for shared libraries.
If you run a rolling release Linux distribution and have this version of glibc or later, compiling ROCm from source is the only way to stay current on RDNA1 hardware.
NightFox
24.02.26, 22:37
Weitergeleitet
Item Version Description
Operating System Ubuntu Ubuntu20.04.3
Ubuntu18.04.6
Ubuntu GFX amdgpu-pro-21.50-1347991-ubuntu-20.04.tar.xz \scbufs01\SCBUSW\SWQA\SCBU_SW_Programs\Robin\Drivers\EXT_release\3Dec
amdgpu-pro-21.50-1347991-ubuntu-18.04.tar.xz
TRM teamredminer-v0.8.6.6-linux.tgz \scbufs01\SCBUSW\SWQA\SCBU_SW_Programs\Robin\Tool\Offline_TRM\offline_benchmark
BC250 Community • Dienstag
Weitergeleitet
Was what the p3.00 bios was certified on
BC250 Community • Dienstag
n00bos
24.02.26, 23:22
i remember this is very true , had the same thing on my rx470
probably we should use the same ubuntu version that works with the mi50 "hack"
NightFox
25.02.26, 00:44
AMD removed old versions from the repositories 🙁
The only thing we have left is: https://download.hiveos.farm/repo/binary/amd-ocl/
There is still 23.80 but it does not have Ubuntu package for ROCm
n00bos
25.02.26, 00:58
yes! i remember this when i was trying to get rocm working on my rx470 i was running in to amd removing old versions from repositories and i found it very sus
maybe some data hoarder has them ?
n00bos
25.02.26, 00:59
can be compiled from source
NightFox
25.02.26, 09:24
Weitergeleitet
oh hell yeah. 22.20 installs on ubuntu 20.04, BUT you have to edit the apt sources that the .deb file installs. since AMD has archived the repos now, and invoking the amdgpu-install script will fail for the missing repo.
cd /etc/apt/sources.list.d
sudo nano amdgpu.list
in there you will see:
deb https://repo.radeon.com/amdgpu/22.20/ubuntu focal main
change this to (add '.' before the 22.20):
deb https://repo.radeon.com/amdgpu/.22.20/ubuntu focal main
make the same kind of change inside amdgpu-proprietary.list
then you can properly invoke the amdgpu-install script (i have a sever CLI install, no GUI so i did not install graphics)
amdgpu-install --usecase=opencl --opencl=rocr --accept-eula
BC250 Community • Mittwoch
🅳🅳🅻
26.02.26, 04:06
https://web.archive.org/ ???
NightFox
26.02.26, 10:15
There are no driver archives, but you can use a script to try archiving hidden dirs while they are still accessible.
The installation packages are located here:
https://repo.radeon.com/amdgpu/.22.20/ubuntu/pool/proprietary/
https://repo.radeon.com/amdgpu/.21.15/ubuntu/pool/proprietary/
The full list of versions is still available here:
https://repo.radeon.com/amdgpu-install/
I was thinking of downloading everything there and posting a mirror on GitHub, but I don't have time yet.
@@ -0,0 +1,167 @@
#!/bin/bash
# BC-250 KIQ Fix - Post-Reboot Validation Script
# Run this after rebooting with the patched amdgpu module
#
# Tests:
# 1. Module loaded check
# 2. dmesg for KIQ fence timeouts (should be ZERO)
# 3. rocminfo
# 4. hip_probe (with normal exit, no _exit hack)
# 5. hip_vector_add
# 6. Multiple sequential GPU runs (stress test)
# 7. Process exit cleanup (the main fix target)
set +e # Don't exit on error - we want to see all results
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
pass() { echo -e "${GREEN}[PASS]${NC} $1"; }
fail() { echo -e "${RED}[FAIL]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
info() { echo -e " $1"; }
echo "=========================================="
echo "BC-250 KIQ Fix - Post-Reboot Validation"
echo "=========================================="
echo "Date: $(date)"
echo "Kernel: $(uname -r)"
echo ""
# Test 1: Module loaded
echo "--- Test 1: Module Status ---"
if lsmod | grep -q amdgpu; then
pass "amdgpu module is loaded"
MODULE_SIZE=$(lsmod | grep "^amdgpu " | awk '{print $2}')
info "Module size: ${MODULE_SIZE}K"
else
fail "amdgpu module not loaded!"
exit 1
fi
# Test 2: Check for KIQ fence timeouts at boot
echo ""
echo "--- Test 2: KIQ Fence Timeouts (boot) ---"
KIQ_ERRORS=$(dmesg | grep -ci "kiq.*fence\|fence.*kiq\|KIQ.*timeout" 2>/dev/null || echo "0")
if [ "$KIQ_ERRORS" = "0" ]; then
pass "No KIQ fence timeout errors in dmesg"
else
warn "Found $KIQ_ERRORS KIQ-related messages (checking if they're errors...)"
dmesg | grep -i "kiq.*fence\|fence.*kiq\|KIQ.*timeout" | head -5
fi
# Test 3: SDMA check (known issue, cosmetic)
echo ""
echo "--- Test 3: SDMA Status ---"
SDMA_ERRORS=$(dmesg | grep -ci "sdma.*fence\|sdma.*timeout" 2>/dev/null || echo "0")
if [ "$SDMA_ERRORS" = "0" ]; then
pass "No SDMA fence errors"
else
warn "SDMA errors present (cosmetic, mitigated by HSA_ENABLE_SDMA=0)"
fi
# Test 4: rocminfo
echo ""
echo "--- Test 4: rocminfo ---"
if timeout 30 rocminfo 2>/dev/null | grep -q "gfx10"; then
pass "rocminfo detects GPU"
rocminfo 2>/dev/null | grep "Name:\|Marketing Name:\|Compute Unit:" | head -5
else
fail "rocminfo failed or timed out"
fi
# Test 5: hip_probe
echo ""
echo "--- Test 5: hip_probe ---"
HIP_PROBE="/home/dars/VibeROCm/hip_probe"
if [ -x "$HIP_PROBE" ]; then
if timeout 30 "$HIP_PROBE" 2>&1; then
pass "hip_probe completed successfully"
else
fail "hip_probe failed (exit code: $?)"
fi
# CRITICAL: Check for KIQ errors AFTER process exit
sleep 3
POST_KIQ=$(dmesg | tail -20 | grep -ci "kiq.*fence\|fence.*kiq\|KIQ.*timeout" 2>/dev/null || echo "0")
if [ "$POST_KIQ" = "0" ]; then
pass "No KIQ errors after hip_probe exit!"
else
fail "KIQ errors appeared after hip_probe exit"
dmesg | tail -10
fi
else
warn "hip_probe not found at $HIP_PROBE"
fi
# Test 6: hip_vector_add
echo ""
echo "--- Test 6: hip_vector_add ---"
HIP_VECTOR="/home/dars/VibeROCm/hip_vector_add"
if [ -x "$HIP_VECTOR" ]; then
if timeout 30 "$HIP_VECTOR" 2>&1; then
pass "hip_vector_add completed successfully"
else
fail "hip_vector_add failed (exit code: $?)"
fi
sleep 3
POST_KIQ=$(dmesg | tail -20 | grep -ci "kiq.*fence\|fence.*kiq\|KIQ.*timeout" 2>/dev/null || echo "0")
if [ "$POST_KIQ" = "0" ]; then
pass "No KIQ errors after hip_vector_add exit!"
else
fail "KIQ errors appeared after hip_vector_add exit"
fi
else
warn "hip_vector_add not found at $HIP_VECTOR"
fi
# Test 7: Multiple sequential runs (stress test for process lifecycle)
echo ""
echo "--- Test 7: Sequential GPU Stress (5 rounds) ---"
if [ -x "$HIP_VECTOR" ]; then
STRESS_PASS=0
for i in 1 2 3 4 5; do
if timeout 30 "$HIP_VECTOR" >/dev/null 2>&1; then
STRESS_PASS=$((STRESS_PASS + 1))
else
fail "Round $i failed"
break
fi
sleep 1
done
if [ "$STRESS_PASS" -eq 5 ]; then
pass "All 5 sequential GPU runs completed successfully!"
else
fail "Only $STRESS_PASS/5 rounds passed"
fi
# Final KIQ check
sleep 5
FINAL_KIQ=$(dmesg | tail -50 | grep -ci "kiq.*fence\|fence.*kiq\|KIQ.*timeout" 2>/dev/null || echo "0")
if [ "$FINAL_KIQ" = "0" ]; then
pass "No KIQ errors after stress test!"
else
fail "KIQ errors after stress test: $FINAL_KIQ"
dmesg | tail -20 | grep -i "kiq\|fence"
fi
else
warn "Skipping stress test"
fi
# Test 8: rocminfo AFTER all GPU tests (this is the killer - previously hung)
echo ""
echo "--- Test 8: rocminfo After GPU Tests ---"
if timeout 30 rocminfo 2>/dev/null | grep -q "gfx10"; then
pass "rocminfo still works after GPU tests! (Previously this HUNG)"
else
fail "rocminfo hung or failed after GPU tests"
fi
echo ""
echo "=========================================="
echo "Validation complete."
echo "=========================================="
@@ -0,0 +1,186 @@
#!/bin/bash
# BC-250 v3 Post-Reboot Verification Script
# Run this IMMEDIATELY after booting with v3 module
#
# Tests in order of escalation:
# 1. Module verification (no GPU access)
# 2. Boot parameter verification
# 3. sysfs GPU state check
# 4. rocminfo (light GPU access)
# 5. Single HIP test (compute)
# 6. Sequential HIP test (the crash scenario from v2)
#
# Usage: bash post_reboot_v3_test.sh [--full]
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
pass() { echo -e "${GREEN}[PASS]${NC} $1"; }
fail() { echo -e "${RED}[FAIL]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
info() { echo -e " $1"; }
FULL_TEST=false
[[ "${1:-}" == "--full" ]] && FULL_TEST=true
echo "============================================"
echo " BC-250 v3 Post-Reboot Verification"
echo " $(date)"
echo "============================================"
echo ""
# Test 1: Kernel version
echo "--- Test 1: Kernel & Module ---"
KVER=$(uname -r)
if [[ "$KVER" == "6.18.8-3-cachyos" ]]; then
pass "Kernel: $KVER"
else
warn "Unexpected kernel: $KVER"
fi
# Test 2: v3 strings in loaded module
V3_STRINGS=$(sudo dmesg | grep -c "BC-250" 2>/dev/null || echo "0")
if [[ "$V3_STRINGS" -ge 1 ]]; then
pass "v3 BC-250 messages in dmesg ($V3_STRINGS occurrences)"
else
warn "No BC-250 messages in dmesg yet (may appear on first GPU use)"
fi
# Check GFXOFF disable message
if sudo dmesg | grep -q "GFXOFF disabled"; then
pass "GFXOFF disabled message confirmed in dmesg"
else
fail "GFXOFF disable message NOT found — v3 patch may not be loaded"
fi
# Test 3: ppfeaturemask
echo ""
echo "--- Test 2: Boot Parameters ---"
CMDLINE=$(cat /proc/cmdline)
if echo "$CMDLINE" | grep -q "ppfeaturemask=0xfff73ef7"; then
pass "ppfeaturemask=0xfff73ef7 in boot cmdline"
else
warn "ppfeaturemask not in boot cmdline (may be set via modprobe)"
fi
# Check actual ppfeaturemask from module
ACTUAL_PP=$(cat /sys/module/amdgpu/parameters/ppfeaturemask 2>/dev/null || echo "unknown")
if [[ "$ACTUAL_PP" == "0xfff73ef7" ]] || [[ "$ACTUAL_PP" == "4293869303" ]]; then
pass "Active ppfeaturemask: $ACTUAL_PP (GFXOFF+DeepSleep+ULV disabled)"
else
warn "Active ppfeaturemask: $ACTUAL_PP (expected 0xfff73ef7 / 4293869303)"
fi
# Test 4: KIQ errors check
echo ""
echo "--- Test 3: KIQ Error Check ---"
KIQ_ERRORS=$(sudo dmesg | grep -c "timeout waiting for kiq fence" 2>/dev/null || echo "0")
if [[ "$KIQ_ERRORS" -eq 0 ]]; then
pass "Zero KIQ timeout errors"
else
fail "Found $KIQ_ERRORS KIQ timeout errors!"
fi
# Check for GPU unreachable messages
GPU_DEAD=$(sudo dmesg | grep -c "GPU unreachable\|GPU died" 2>/dev/null || echo "0")
if [[ "$GPU_DEAD" -eq 0 ]]; then
pass "Zero GPU-unreachable events"
else
fail "Found $GPU_DEAD GPU health-check failures!"
fi
# Test 5: sysfs GPU state
echo ""
echo "--- Test 4: GPU State ---"
if [[ -f /sys/class/drm/card0/device/pp_dpm_sclk ]]; then
SCLK=$(cat /sys/class/drm/card0/device/pp_dpm_sclk)
pass "GPU clock levels accessible:"
echo "$SCLK" | sed 's/^/ /'
else
fail "Cannot read GPU clock levels"
fi
if [[ -f /sys/class/drm/card0/device/pp_od_clk_voltage ]]; then
OD=$(cat /sys/class/drm/card0/device/pp_od_clk_voltage)
pass "OD voltage table accessible:"
echo "$OD" | sed 's/^/ /'
else
warn "Cannot read OD voltage table"
fi
# Test 6: cyan-skillfish-governor
echo ""
echo "--- Test 5: Governor Status ---"
if systemctl is-active --quiet cyan-skillfish-governor.service 2>/dev/null; then
pass "cyan-skillfish-governor is running"
else
warn "cyan-skillfish-governor is NOT running"
fi
# Test 7: rocminfo
echo ""
echo "--- Test 6: ROCm Runtime ---"
if command -v rocminfo &>/dev/null; then
ROCM_OUT=$(timeout 30 rocminfo 2>&1)
if echo "$ROCM_OUT" | grep -q "gfx1013\|gfx10"; then
pass "rocminfo detects GPU (gfx1013)"
elif echo "$ROCM_OUT" | grep -q "Agent"; then
pass "rocminfo detects agents"
info "$(echo "$ROCM_OUT" | grep -i "name" | head -3)"
else
warn "rocminfo ran but output unexpected"
fi
else
warn "rocminfo not found"
fi
# Test 8: HIP test (only with --full)
echo ""
if $FULL_TEST; then
echo "--- Test 7: HIP Compute (FULL MODE) ---"
HIP_TEST="/home/dars/VibeROCm/hip_vector_add/hip_vector_add"
if [[ -x "$HIP_TEST" ]]; then
echo " Running first HIP test..."
if timeout 60 "$HIP_TEST" 2>&1; then
pass "First HIP vector_add completed"
sleep 3
echo ""
echo " Running SECOND HIP test (this is the crash scenario)..."
echo " Monitoring dmesg for GPU errors during test..."
sudo dmesg -C # Clear dmesg
if timeout 60 "$HIP_TEST" 2>&1; then
pass "Second HIP vector_add completed — CRASH BUG RESOLVED!"
# Check if any GPU errors occurred during the test
POST_ERRORS=$(sudo dmesg | grep -c "BC-250.*GPU\|timeout\|error" 2>/dev/null || echo "0")
if [[ "$POST_ERRORS" -eq 0 ]]; then
pass "No GPU errors during sequential HIP tests"
else
warn "GPU events during test ($POST_ERRORS), check dmesg"
fi
else
fail "Second HIP test failed or timed out"
sudo dmesg | grep -i "BC-250\|error\|timeout\|GPU" | tail -10
fi
else
fail "First HIP test failed"
sudo dmesg | grep -i "BC-250\|error\|timeout\|GPU" | tail -10
fi
else
warn "HIP test binary not found: $HIP_TEST"
info "Build with: cd /home/dars/VibeROCm/hip_vector_add && hipcc hip_vector_add.cpp -o hip_vector_add"
fi
else
echo "--- Test 7: HIP Compute (SKIPPED — use --full to enable) ---"
info "Run: bash post_reboot_v3_test.sh --full"
fi
echo ""
echo "============================================"
echo " Verification Complete"
echo "============================================"
@@ -0,0 +1,173 @@
#!/bin/bash
# BC-250 Safe Post-Reboot Test Script (v2)
# Tests GPU functionality after KIQ bypass patch installation
# CRITICAL: This script performs INCREMENTAL testing with safety checks
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
pass() { echo -e "${GREEN}[PASS]${NC} $1"; }
fail() { echo -e "${RED}[FAIL]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
info() { echo -e "[INFO] $1"; }
ERRORS=0
TESTS=0
echo "==========================================="
echo " BC-250 KIQ Bypass Patch - Verification"
echo " Date: $(date)"
echo "==========================================="
echo ""
# === STEP 0: Verify patch markers in dmesg ===
((TESTS++))
info "Step 0: Checking dmesg for patch markers..."
if dmesg 2>/dev/null | grep -q "BC-250 KIQ bypass active"; then
pass "BC-250 KIQ bypass markers found in dmesg"
dmesg | grep "BC-250" | while read line; do echo "$line"; done
else
warn "No BC-250 bypass markers in dmesg yet (may appear after first GPU use)"
fi
# === STEP 1: Check for KIQ errors at boot ===
((TESTS++))
info "Step 1: Checking for KIQ errors in boot log..."
if dmesg 2>/dev/null | grep -q "timeout waiting for kiq fence"; then
fail "KIQ fence timeout detected at boot!"
((ERRORS++))
echo " → GPU is likely in a bad state. Do NOT proceed."
exit 1
else
pass "No KIQ fence timeouts at boot"
fi
# === STEP 2: Check GPU is alive ===
((TESTS++))
info "Step 2: Checking GPU hardware..."
if ls /sys/class/drm/card0/device/ &>/dev/null; then
pass "GPU DRM device present"
else
fail "No GPU DRM device"
((ERRORS++))
exit 1
fi
# === STEP 3: Check Z-Image-Turbo is disabled ===
((TESTS++))
info "Step 3: Checking Z-Image-Turbo service..."
if systemctl --user is-active zimage.service 2>/dev/null | grep -q "^active"; then
fail "Z-Image-Turbo is RUNNING! Stop it first: systemctl --user stop zimage"
((ERRORS++))
exit 1
else
pass "Z-Image-Turbo is not running"
fi
# === STEP 4: rocminfo (safe, no TLB flush) ===
((TESTS++))
info "Step 4: Running rocminfo..."
if rocminfo 2>&1 | grep -q "gfx10"; then
pass "rocminfo detects GPU"
else
fail "rocminfo failed"
((ERRORS++))
fi
# Check dmesg AGAIN after rocminfo
if dmesg 2>/dev/null | grep -q "timeout waiting for kiq fence"; then
fail "KIQ error appeared after rocminfo!"
((ERRORS++))
exit 1
fi
echo ""
info "Step 4 complete. Checking dmesg for any BC-250 markers..."
dmesg 2>/dev/null | grep "BC-250" 2>/dev/null || info "(no BC-250 markers yet)"
echo ""
# === STEP 5: hip_probe (uses _exit(0), minimal cleanup) ===
((TESTS++))
info "Step 5: Running hip_probe (minimal GPU test)..."
cd /home/dars/VibeROCm
if [ -f hip_probe ]; then
timeout 30 ./hip_probe 2>&1
RESULT=$?
if [ $RESULT -eq 0 ]; then
pass "hip_probe passed"
else
fail "hip_probe failed (exit code: $RESULT)"
((ERRORS++))
fi
else
warn "hip_probe binary not found, skipping"
fi
# Check dmesg after hip_probe
sleep 2
if dmesg 2>/dev/null | grep -q "timeout waiting for kiq fence"; then
fail "KIQ error appeared after hip_probe!"
((ERRORS++))
echo " → The KIQ bypass patch may not be working. STOP HERE."
exit 1
fi
info "Checking BC-250 bypass markers after hip_probe..."
dmesg 2>/dev/null | grep "BC-250" 2>/dev/null || info "(no markers)"
echo ""
# === STEP 6: Wait and verify GPU is still alive ===
info "Step 6: Waiting 10 seconds to check GPU stability..."
sleep 10
if dmesg 2>/dev/null | grep -q "timeout waiting for kiq fence\|GPU fault\|GPU hang"; then
fail "GPU error detected during wait period!"
((ERRORS++))
exit 1
else
pass "GPU stable after 10-second wait"
fi
# === STEP 7: hip_vector_add (full GPU compute test) ===
((TESTS++))
info "Step 7: Running hip_vector_add (full compute test)..."
if [ -f hip_vector_add ]; then
timeout 60 ./hip_vector_add 2>&1
RESULT=$?
if [ $RESULT -eq 0 ]; then
pass "hip_vector_add passed"
else
fail "hip_vector_add failed (exit code: $RESULT)"
((ERRORS++))
fi
else
warn "hip_vector_add binary not found, skipping"
fi
# Final dmesg check
sleep 5
if dmesg 2>/dev/null | grep -q "timeout waiting for kiq fence"; then
fail "KIQ error appeared after hip_vector_add!"
((ERRORS++))
fi
echo ""
echo "==========================================="
info "Final dmesg BC-250 markers:"
dmesg 2>/dev/null | grep "BC-250" 2>/dev/null || info "(none)"
echo ""
if [ $ERRORS -eq 0 ]; then
echo -e "${GREEN}ALL TESTS PASSED${NC} ($TESTS tests)"
echo ""
echo "The KIQ bypass patch is working. The GPU survived compute workloads"
echo "without hanging. You can now safely re-enable Z-Image-Turbo:"
echo " systemctl --user enable --now zimage.service"
else
echo -e "${RED}$ERRORS ERRORS${NC} out of $TESTS tests"
echo ""
echo "The patch may need additional work. Check dmesg for details."
fi