Skip to content

sycl: images on the SYCL port (CPU encoder, --vision cpu) - #2495

Closed
skipzx-lab wants to merge 1 commit into
Niko1221:mainfrom
skipzx-lab:sycl-vision
Closed

skipzx-lab wants to merge 1 commit into
Niko1221:mainfrom
skipzx-lab:sycl-vision

Conversation

@skipzx-lab

@skipzx-lab skipzx-lab commented Oct 14, 2026 •

Copy link
Copy Markdown

Title

Issue: none. docs/INTEL.md listed images as "Not done" on the SYCL port.

Summary

This turns on images for the Intel SYCL port. The engine already had --vision / GENI / M-RoPE from the port, and that code works unchanged. This PR adds what was missing around it: a way to build the CPU encoder, setup support, and getting the embeddings file into the engine's container. Images stay opt-in (--vision cpu). Without the flag the config is byte-identical to before.

What changed

  • sycl/tools/build-vision.sh (new): builds strata-vision (tools/vision, CPU, the same cmake flags as setup's build_vision_cpu) in the strata-sycl-dev image.
    • It copies the image's libgomp.so.1 next to the binary and writes a launcher, build-vision/strata-vision.sh, so a host without an OpenMP runtime can run it.
    • It prints the image's glibc version. Without third_party/llama.cpp it ends with BUILD EXIT 1 and says what to run.
  • sycl/setup_intel.py:
    • --vision cpu turns images on. yes and gpu also select the CPU encoder and print a warning, because the port has no GPU encoder. Images stay off by default.
    • It stops early, with the three steps in order, when images are asked for and the encoder is not built.
    • to_sycl() keeps the vision section with host paths (the encoder runs on the host) and the launcher as exe, and adds --vision exactly once.
    • Setup's 700 MiB GPU-encoder reserve is dropped unless the user typed it, because the CPU encoder takes no VRAM.
    • Side fix: the --vram-reserve-mib / --vision checks in install() now see the --flag=value form too. Before, --vision=cpu or --vram-reserve-mib=N got a second copy of the flag added.
  • sycl/serve/server_intel.py: install_vision_paths() runs only with a vision section. It puts the embeddings file under /dev/shm/strata-sycl-vision, or <data root>/vision-tmp when /dev/shm is short, both mode 0700, and exports STRATA_SYCL_VISION_DIRS.
  • sycl/serve/strata-sycl.sh: with --vision in the args, it mounts those directories read-only at the same paths. Without --vision nothing changes.
  • tools/test_setup_sycl_vision.py (new, 33 tests, no GPU or network) covers:
    • the mapping and the early stop;
    • to_sycl with and without images, the latter checked against the output of the code before this change;
    • the reserve rule;
    • the path redirection, run in temp directories;
    • the wrapper's mounts, using a fake docker;
    • bash -n, and build-vision.sh without llama.cpp.
  • docs/INTEL.md:
    • the feature table entry;
    • a new "Images (SYCL port)" section covering the build order, how it works, RAM, the image-token cap and measurements;
    • "Not done" now covers only the llama.cpp path.

Extra Notes

  • Measured on an Arc Pro B60 24 GB (xe, PCIe Gen3 x8) with a Xeon W-2155, Coder IQ1_M, --resident-experts, and the encoder on 4 threads.
    • A 1920x1080 dashboard screenshot read correctly in 4.0 s with the cap at 1024 (1,071 tokens). Uncapped it was 2,103 tokens and 5.2 s.
    • Several images in one message, follow-up turns, PNG/JPEG/WebP, OpenAI and Anthropic image blocks, streaming, and a broken image (400, then the next request reads) all work.
    • Text-only decode is unchanged.
  • The IQ1_M model misreads some repeated-digit strings in pictures, for example "2222" read as "22". The pinned llama.cpp's llama-mtmd-cli (SYCL, the same GGUF and mmproj) gives exactly the same four wrong answers, so this comes from the model at this quant, not from the port.
  • Not tested:
    • an Alchemist card;
    • a layer split, although the mrope table upload already covers every stage;
    • setup's default cap of 300 image tokens. I measured at 1024 and uncapped; the docs say so.
  • Builds: the SYCL engine itself is unchanged, and the encoder was built with build-vision.sh in the dev image. No CUDA or HIP files are touched.
  • Tests: python -m unittest tools.test_setup_sycl_vision tools.test_setup_sycl tools.test_setup_small_card gives 67 OK.

The SYCL engine already carried the --vision / GENI / M-RoPE path from the
port; this wires it up. strata-vision (tools/vision, CPU) is built in the
strata-sycl-dev image by sycl/tools/build-vision.sh and runs on the host;
server_intel.py writes the embeddings file under /dev/shm/strata-sycl-vision
(else <data root>/vision-tmp) and strata-sycl.sh mounts those read-only into
the engine's container when --vision is in the args. setup_intel.py maps
--vision cpu|yes|gpu to the CPU encoder (off by default) and writes the
config's vision section with host paths.

Opt-in: without --vision the config is byte-identical to before
(tools/test_setup_sycl_vision.py checks it against the pre-change output).
@deriknel

Copy link
Copy Markdown

Tested on two Arc Pro B70s with a layer split (26) and --pipeline-windows 2, Swift 1.5 Flash-Next IQ3_S with every expert resident, engine 0.1.44.1 (296b59d) plus our two pipeline fixes (#2433, #2436). The encoder is built from tools/vision against the pinned llama.cpp (3cf03257), native with OpenMP, 8 threads, with Swift's BF16 mmproj. Our server and engine run in one container, so we used only the encoder and a "vision" config section, not the wrapper changes.

  • Text with images on: greedy text identical on 5 prompts, decode 83.7 / 122.8 / 93.5 tok/s, the same as without.
  • Reading, all correct at max_tokens 1024 and 300: a text card; two cards in one message, in order; a 1920x1080 dashboard screenshot (banner, three buttons with their colours, two values); a code screenshot (a constant, the function name, a default argument, the caught exception); a follow-up turn; and a broken image (400, then the next request works).
  • Pipeline windows after an image: the greedy description of the screenshot is the same with the pipeline on and off, two rounds each.
  • The 1920x1080 screenshot: 1,041 prompt tokens and an 11.3 s request at 1024 (4.3 s when the same picture comes again); 309 tokens and 4.5 s at 300.
  • Encoder RAM: 0.93 GB after start, 1.14 GB after the pictures at 1024, 1.02 GB at 300.
  • Stability: a ~148K-token prompt then 20 prompts of ~13K, twice on a fresh engine with the pictures sent first: no lost device, no xe resets.

So the layer split works here, with pipeline windows too.

We investigated (myself and Claude Opus 5.5 in Claude Code) and I reviewed the findings and ran every test on my two Arc Pro B70s on my CachyOS machine.

@qiuzan2001

Copy link
Copy Markdown

Overlap note: two other open PRs add the same feature (images on the SYCL port with the CPU encoder): #2229 (sycl/serve/server_intel.py, sycl/setup_intel.py) and #2483 (sycl/setup_intel.py, a SYCL vision build script, tools/vision/CMakeLists.txt). git merge-tree shows this branch conflicting with #2229 in sycl/serve/server_intel.py and sycl/setup_intel.py, and with #2483 in docs/INTEL.md and sycl/setup_intel.py, so only one of the three can land as is. On Linux (no Intel GPU), this branch's tools/test_setup_sycl_vision.py and tools/test_setup_sycl.py pass.

@skipzx-lab

Copy link
Copy Markdown
Author

Thanks for the overlap note. I've read #2229: it covers the core wiring this PR does (the --vision cpu mapping, keeping the config's vision section, --vision on the engine args, the embeddings path through the /work mount), and it applies cleanly to current main, so I'm happy for it to go first.

What this PR adds on top, which I'd rebase onto #2229 once it lands:

  • sycl/tools/build-vision.sh: builds the encoder inside the strata-sycl-dev image (with its libgomp), for hosts with no C++ compiler. sycl: enable vision (--vision cpu) on intel #2229 uses setup.py's host build, which needs one.
  • Drops setup's 700 MiB --vram-reserve-mib for a CPU-only encoder (that reserve is sized for the GPU one), unless the user typed it.
  • tools/test_setup_sycl_vision.py (33 tests) and the docs/INTEL.md "Images" section: measured times, RAM, and the --vision-tokens cap for screenshots, plus deriknel's two-B70 layer-split results above.

The --flag=value handling fix will go in its own PR. #2483's GPU encoder is separate and could sit on top of either.

@beyondcr

Copy link
Copy Markdown

Happy to rebase #2483 whenever either lands.

@Niko1221

Copy link
Copy Markdown
Owner

Thanks for the pull request.

We're resetting the tracker for 0.2.0-beta. There are more than 1,300 open issues and pull requests, too many for us to review properly, so we're closing everything that isn't being worked on right now. Closed here does not mean rejected.

To reopen it takes about two minutes:

  1. Rebase on main if it no longer applies, and re-run your checks on 0.2.0-beta.
  2. Start the title with its area: [Engine], [GUI], [TUI], [Benchmark], [Docs] or [Other].
  3. Fill in the pull request template: the problem and result, and the validation (what you ran, on which revision, what you saw).
  4. Comment /reopen. A check runs and either reopens the pull request or lists what is still missing.

The rules are in CONTRIBUTING.md.

@Niko1221 Niko1221 closed this Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants