By Davide Ciffa

MOSS-Image on the R9700: a 1024x1024 image in 2.7 s at 8 steps, 1.8x faster than ComfyUI on a DGX Spark

Our engine now makes images: it runs MOSS-Image, a 5B text-to-image model, natively on the Radeon AI PRO R9700. A 1024x1024 image takes 2.7 s at 8 steps, 1.8x faster than ComfyUI on a DGX Spark at the same settings.

A Radeon AI PRO R9700 graphics card floating above its shadow, beside a large block model of the MOSS-Image logo
MOSS-Image runs natively in our engine on the R9700.
2.7 sfor a 1024x1024 image at 8 steps and CFG 1, with a new prompt each time
1.8xfaster than ComfyUI on a DGX Spark at the same settings, and 1.5x a step
4.4xfaster than stock stable-diffusion.cpp at its default settings, on the same R9700

TL;DR

Below, both machines make the same eight images, one request at a time, replayed from the timings we recorded. Both run the same int8 weights with an 8-bit text encoder and the full decoder.

Eight new prompts in real time: 21.7 s on the R9700, 40.9 s on the DGX Spark. Each side shows the images it returned. The two engines draw their noise differently, so the same seed makes a different image on each.

Seconds per image

Each request is a new prompt, timed from the request to the finished PNG. On a DGX Spark, ComfyUI runs the same model at the same size, steps, sampler and CFG.

Seconds per 1024x1024 MOSS-Image image with a new prompt Our engine on the R9700: 2.74 s. ComfyUI on a DGX Spark: 5.02 s with our int8 file, the full decoder and its 8-bit text encoder, 5.34 s with its shipped files. Stock stable-diffusion.cpp on the R9700 at its default settings: 12.19 s. seconds per image, a new prompt each time R9700, our engine 2.74 s Spark ComfyUI, our file 5.02 s 1.8x Spark ComfyUI, shipped 5.34 s 1.9x R9700, stock sd.cpp 12.19 s 4.4x
Lower is better. Our file: our int8 weights and the full decoder, with ComfyUI's 8-bit text encoder and 16-bit attention. Shipped: ComfyUI's template files, with int8 attention, the small decoder and a 4-bit text encoder. Stock stable-diffusion.cpp runs our int8 weights at its default settings.

ComfyUI takes 5.02 s with our int8 weights, the full decoder and its 8-bit text encoder, and 5.34 s with its shipped files, whose 4-bit text encoder is the slower one on the Spark. Stock stable-diffusion.cpp takes 12.19 s on the same R9700 at its default settings.

ComfyUI keeps the last prompt's text encoding, so a repeated prompt skips that step. It then takes 4.22 to 4.54 s, against our 2.74 s with a new prompt: 1.5 to 1.7x.

Where the time goes, for one image with a new prompt
R9700, our engineDGX Spark, ComfyUI with our int8 file
Text encoding0.03 s0.55 s
Sampling, 8 steps2.53 s (0.32 s a step)3.87 s (0.48 s a step)
Decoding the image0.16 s0.47 s
PNG and HTTP, or saving0.03 s0.11 to 0.30 s

DGX Spark: ComfyUI 0.42.0 with PyTorch 2.14.1 and CUDA 13.0, default launch flags, its native MOSS-Image nodes and its default sampler for the model. Its times are ComfyUI's own "Prompt executed in", averages of five to eight new prompts. They include saving a level-4 PNG to disk, but not the HTTP round trip.

R9700: medians of 9 images over three server starts, timed by the client, so our side counts more work, not less. Our server sends a level-1 PNG over HTTP.

We timed 8 steps at CFG 1, the engine's default. ComfyUI's MOSS-Image template uses 25 steps at CFG 2.5, and the model card 50 steps at CFG 3.

Per step at CFG 1, the R9700 runs the model 1.5x faster than the Spark: 0.32 s against 0.48 s. The text encoder, the decoder and the PNG make up the rest of the 1.8x, so the gap narrows as steps are added.

The same model, faster kernels

Nothing is distilled and no step is skipped. The weights are ComfyUI's int8 convrot format, written from the bf16 release, and the speed comes from kernels written for the R9700's RDNA4 chip.

The engine started as kernel patches to stable-diffusion.cpp. It follows that project's samplers and schedules and reproduces its noise bit for bit, so a seed starts from the same noise in both. With the text encoder on the R9700, the same request makes the same image after every restart.

How close to the full model

We compared each build's images with the bf16 weights run on stock stable-diffusion.cpp with flash attention off, its most exact mode. PSNR measures how close two images are: higher is closer.

PSNR against the bf16 reference, mean of three prompts, Euler sampler; stock runs use its default flash attention
BuildWeightsAfter 1 stepAfter 8 steps
Stock stable-diffusion.cppint839.8 dB27.6 dB
Stock stable-diffusion.cppbf1626.8 dB
Our engineint850.7 dB35.0 dB

Our int8 images land closer to the reference than stock's, even when stock runs the bf16 weights. On this card, stock's default flash attention moves its images more than int8 does. Three prompts is a small sample, and these numbers measure closeness to the reference, not which image looks better.

Two 1024x1024 MOSS-Image images from our engine at 8 steps of er_sde and CFG 2.5: a vintage travel poster with the word LUCEBOX over a mountain lake, and a red bicycle against a stone wall in a narrow street
Two images from our engine on the R9700, at 8 steps and CFG 2.5.

Our default is CFG 1, which runs the model once a step, and at 8 steps some images come out misshapen. In a check by eye, 4 of 8 were, against 1 of 8 at CFG 2.5. CFG 2.5 runs the model twice a step and takes 5.2 s.

On a Lucebox: images beside chat

A Lucebox serves chat from the same R9700, so there the image model and the chat model take turns on the card. An image waits for the chat replies in progress, and new chat requests wait behind it.

The image model keeps the card for 10 s after each image, so a batch of images is not cut in two. The next chat request takes the card back, or the chat model returns by itself two minutes after the last image.

With standby, the text encoder runs on the Strix Halo's integrated GPU and the image weights wait in system memory. Two seconds after the last chat request, they go onto the R9700 beside the chat model in 1.6 s.

Seconds to an image while the R9700 also serves chat An image takes 2.74 s with the image server alone on the R9700, 2.98 s with standby when its weights are already on the card beside the chat model, and 4.07 s when a long chat reply took them off and the image copies them during its first step. seconds to an image, end to end, a new prompt each time image server alone 2.74 s weights on the card 2.98 s weights copied first 4.07 s
With standby, beside Qwen 3.8 27B on the same R9700. A long chat reply takes the image weights off the card, and the next image copies them during its first step.

A chat request takes the weights off again in under 10 ms. Every image is the same, bit for bit, whether its weights were on the card or copied during the first step.

A Lucebox also runs ComfyUI as an app. Its Lucebox image node asks the box's image API for up to 8 images a run, so ComfyUI never runs a model itself.

Run it

The engine is open source. Build it for the R9700, fetch the model and start the server:

cmake -S server -B server/build -DCMAKE_BUILD_TYPE=Release \
  -DLUCE_GPU_BACKEND=hip -DLUCE_HIP_ARCHITECTURES=gfx1201
cmake --build server/build --target luce_server -j
cd optimizations/image_gen && ./fetch_models.sh
../../server/build/luce_server models/moss-image.gguf \
  --text-encoder models/Qwen3.5-9B-Q8_0.gguf --port 7860

It answers OpenAI's images API and AUTOMATIC1111's txt2img:

curl -s http://127.0.0.1:7860/v1/images/generations \
  -H 'Content-Type: application/json' \
  -d '{"prompt": "a red bicycle in a narrow street", "size": "1024x1024"}'

The engine's README has the full API and the method behind every number here.

A Lucebox needs none of this: the image model installs from Lucebox Manage, the dashboard that comes with the box, 15 GB in all.

What is next

With the text encoder on the Strix Halo, a prompt over 128 tokens can encode slightly differently from run to run, and its image with it. We have not found the cause yet. On the R9700 the same request always makes the same image.

Almost all of a step's 0.32 s is spent in the transformer's layers, so that is where the next gains have to come from.

Hardware and setup

GPUAMD Radeon AI PRO R9700 32 GB at 330 W, ROCm 7.2.4, Ubuntu. At its stock 300 W an image is about 1% slower. The race video ran on a second R9700.
Standby runsThe same R9700 beside a Ryzen AI MAX+ 395, whose Radeon 8060S runs the text encoder, with Qwen 3.8 27B as the chat model
ModelMOSS-Image SFT in ComfyUI's int8 convrot format, written from the bf16 release, with the full FLUX.2 decoder in one file. The release and ComfyUI's template use the small decoder. Text encoder: Qwen3.5 9B at Q8_0.
Workload1024x1024, 8 steps of er_sde on the simple schedule, CFG 1, a new prompt for each request. ComfyUI's template uses 25 steps at CFG 2.5, the model card 50 at CFG 3.
DGX SparkNVIDIA GB10, ComfyUI 0.42.0 (main at b1a247c), PyTorch 2.14.1, CUDA 13.0, default launch flags. Text encoder: ComfyUI's Qwen3.5 9B, 8-bit with our file and in the race video, 4-bit with the shipped files.
stable-diffusion.cppStock commit b13a482, built with ROCm for the R9700, default settings, the same int8 weights and full decoder

Bottom line

MOSS-Image makes a 1024x1024 image in 2.7 s at 8 steps on one R9700, from the full model in int8. That is 1.8x ComfyUI on a DGX Spark at the same settings, and 1.5x a step. The engine is open source, and on a Lucebox the image model shares the R9700 with chat.


Measured by us in October 2026. MOSS-Image is an open model by the OpenMOSS team; the logo in the illustration is theirs. Our engine builds on stable-diffusion.cpp's samplers and noise and on ComfyUI's int8 convrot format and files: thanks to both projects.

Related

Make images on your R9700

The engine is open source: build it, fetch the model and start the server.

GitHub Image engine Discord