Skip to content

HIP: fix corrupted flash-attention output at fp32 precision - #24984

Closed
RapidMark wants to merge 1 commit into
ggml-org:masterfrom
RapidMark:cloudhands/fattn-f32
Closed

HIP: fix corrupted flash-attention output at fp32 precision#24984
RapidMark wants to merge 1 commit into
ggml-org:masterfrom
RapidMark:cloudhands/fattn-f32

Conversation

@RapidMark

@RapidMark RapidMark commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Why this exists

On AMD, image generation came out visibly wrong at 1024×1024 and larger: a ghosted, doubled subject that got worse as the resolution grew. Smaller resolutions were clean, and the same job ran clean on CPU and on NVIDIA, so it was specific to AMD and only showed above a certain resolution.

The flash-attention kernel was quietly ignoring the precision the model asked for and running in half precision instead. That is usually harmless, but at large resolutions the rounding piles up until it shows in the image. Worse, it fails silently: no error, just a wrong, ghosted picture.

What it does

When the model asks for fp32 precision, the kernel now uses it, instead of quietly running in half precision. Where fp32 will not fit, it falls back to the existing path, so those cases stay unchanged.

This only affects AMD/HIP. NVIDIA never uses this kernel, so CUDA builds are untouched.

Why it's the right fix

Before this, there was no way to get a correct image out of an affected AMD GPU. The kernel did not fall back to anything; it just silently produced a garbled result. The only way to a correct image was to give up on the GPU and run on the CPU, which is far slower. So this is not a slower-path tradeoff: it makes the AMD GPU usable for these images, where before it was quietly broken. Builds with rocWMMA enabled already had a correct GPU path; this fix is for the AMD setups that do not.

How I found it

I reproduced it with Chroma (a non-GQA model, head size 128) in stable-diffusion.cpp on an RDNA4 GPU (a Radeon AI PRO R9700), built without rocWMMA flash-attention. Comparing the same generation on the CPU and on the GPU step by step, everything matched until the attention step (the FLASH_ATTN_EXT op), which was the one place the GPU result diverged. It also does not show up in test-backend-ops, because that test's synthetic inputs are too uniform to trigger the rounding.

It surfaced alongside a separate bug on the same path, an incorrect mask shape in masked flash attention, which I fixed upstream in leejet/stable-diffusion.cpp#1625 (merged). Those two changes are independent; together they make Chroma's masked flash attention correct on HIP.

Validation

The clearest way to see the bug is to render the same prompt and seed on several backends and compare. The correct backends agree on one image; the unpatched AMD tile kernel is the lone outlier, and the fix brings it back into agreement.

Chroma1-HD, 1280², 20 steps, euler, flash attention on, fixed seed:

  • NVIDIA (A100), AMD with matrix cores (rocWMMA), and AMD with the fix (fp32 tile) all produce the same image.
  • AMD without the fix (fp16 tile) produces a different, ghosted, wrong image.

That is the point: the unpatched AMD result is not "just a different valid render." Three correct paths converge on one image: a different vendor (NVIDIA) and two different AMD kernels, the matrix-core rocWMMA path and the fp32 tile fix. Only the unpatched fp16 tile diverges. With the fix, that path rejoins them. At higher resolution the unpatched path degrades further, to a fully black image at 2048, while the others stay correct.

composite

Environment:

  • NVIDIA A100-SXM4-40GB: driver 580.159.04, CUDA 13.0
  • AMD Radeon AI PRO R9700 (gfx1201): ROCm 7.2.4, amdgpu 6.16.13

test-backend-ops is unchanged; head sizes 512 and 576 fall back to fp16.

Reproduction

Reproduced via stable-diffusion.cpp (commit 4e85e07), which vendors this ggml kernel.

Build sd-cli for AMD (HIP):

cmake -B build -DSD_HIPBLAS=ON -DCMAKE_HIP_ARCHITECTURES=gfx1201 -DCMAKE_BUILD_TYPE=Release
cmake --build build -j --target sd-cli

The wrong result is this build as-is (the unpatched fp16 tile path); the correct result is the same build with this PR's ggml patch applied. For the NVIDIA reference, configure with -DSD_CUDA=ON instead.

Render (identical command on every backend, only the build differs):

./sd-cli -M img_gen \
  --diffusion-model Chroma1-HD.safetensors \
  --t5xxl t5xxl_fp16.safetensors \
  --vae ae.safetensors \
  -p "a photograph of a dog on the beach, photorealistic, hyperrealistic, highly detailed, sharp focus, natural lighting, DSLR" \
  --cfg-scale 4.0 --guidance 3.5 --sampling-method euler --steps 20 \
  -W 1280 -H 1280 --seed 42 --rng cuda --chroma-enable-t5-mask --diffusion-fa \
  -o out.png

Performance note

This is the tile flash-attention kernel, the path used whenever the matrix-core path (rocWMMA flash-attention) is not in use. That path is opt-in, a build flag that is off by default, so a default HIP build runs the tile kernel on any AMD GPU, with or without matrix cores. The tile kernel has no hardware-accelerated fp32. On RDNA the fast fp16 path uses a packed dot instruction that fp32 cannot use, and fp32 storage roughly doubles the shared memory the kernel needs, which lowers occupancy. So running this kernel in fp32 is inherently slower than fp16. It is still far faster than the only correct option this path had before, which was to fall back to the CPU.

That slowdown is not a reason to leave it broken. The kernel is an active code path, and this is current hardware, not a legacy quirk: it was caught on an RDNA4 Radeon AI PRO R9700. Any build without rocWMMA flash-attention lands here, and the hardware affected worst is anything with no matrix cores at all, such as the RDNA2 generation, which cannot use rocWMMA and so has the tile kernel as its only attention path, with no faster option to switch to. All of them silently produce a ghosted image. GPUs that do have the matrix-core path keep using it and are unaffected. For everyone on the fallback, a small and correct slowdown is far better than a fast and wrong result.

The tile kernel picked fp16 vs fp32 storage from the arch capability alone and ignored the op's requested precision, so an fp32 request was silently down-cast to fp16.
On long key lengths that rounding compounds into a visibly corrupted (ghosted) result for non-GQA attention — the path AMD uses without rocWMMA flash-attention.

Honor GGML_PREC_F32: store Q/K/KQ in fp32 when it's requested and fits shared memory; keep the existing fp16 path for shapes where it doesn't (byte-for-byte unchanged). Gated to HIP.
@github-actions github-actions Bot added ggml changes relating to the ggml tensor library for machine learning CUDA Related to the CUDA backend labels Jun 24, 2026
@ggml-gh-bot

ggml-gh-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown

Hi @RapidMark, thanks for your contribution!

Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:

  • Multiple open PRs from a new contributor: We limit new contributors (those without a previously merged PR) to 1 open PR at a time. You currently have 2 open PRs.

  • AI-generated content: This project does not accept PRs, descriptions or commit messages that are fully or predominantly AI-generated. If you have used AI to assist you in writing code, please make sure to disclose that explicitly.


Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below.

@RapidMark
RapidMark marked this pull request as ready for review June 24, 2026 20:09
@RapidMark
RapidMark requested a review from a team as a code owner June 24, 2026 20:09
@RapidMark

Copy link
Copy Markdown
Contributor Author

Sorry again about the multiple PRs... but I'm finding some important issues and trying to fix them...

As for AI... I did use AI to run the multiple tests to verify everything is working and to create the image... but that was about it.

@RapidMark RapidMark closed this Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CUDA Related to the CUDA backend ggml changes relating to the ggml tensor library for machine learning

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant