One-liner dispatch
Variadic launch(kernel, grid, args…) with compile-time static_asserts and runtime errors that name the exact mismatched argument.
Rendered live by your GPU, naturally
Write a kernel. Run it on the GPU in your pocket, and on every desktop GPU too. Gemma 3 1B at 60 tokens/sec on a phone — held flat across the whole conversation, from a 1.7 MB runtime.
THE MANIFESTO
01 · The pitch
Your phone's GPU can do trillions of operations per second. Android gives you almost no way to use it: no CUDA, no guaranteed OpenCL, RenderScript deprecated.
Xpose closes the gap: real OpenCL C kernels, zero Vulkan ceremony, one kernel on Adreno, Mali, NVIDIA and AMD.
#include <xpose/xpose.hpp>
xpose::Context ctx; // finds the GPU
xpose::Buffer<float> a(ctx, n), b(ctx, n);
xpose::Program prog(ctx, "kernels.spv");
auto add = prog.kernel("vector_add");
xpose::launch(add, {n}, a, b, n).wait(); // ← the whole API
02 · Positioning
| Xpose | clvk | Kompute | Raw Vulkan | |
|---|---|---|---|---|
| Kernel language | OpenCL C · pointers & structs | OpenCL C | GLSL | GLSL / HLSL |
| Dispatch | launch(k, grid, …) | Full OpenCL API | Manager / Tensor | ~300 lines each |
| Kernel compile | Build time | Runtime (ships LLVM) | Runtime (glslang) | Offline |
| Arg binding | Automatic ✓ | Automatic | Manual tensors | Manual |
| Non-coherent memory | Tested on-device ✓ | ✓ | △ | DIY |
| Vulkan SDK needed | No, vendored | Yes | Yes | Yes |
Looking for LiteRT or ONNX Runtime here? Different lane: they run model graphs; Xpose lets you write the kernels. Honest details in the FAQ.
03 · Built for the real world
Every desktop driver hides the bugs that kill you on phones. Xpose is engineered, and regression-tested on real hardware, around the traps that make mobile compute fail.
Variadic launch(kernel, grid, args…) with compile-time static_asserts and runtime errors that name the exact mismatched argument.
Transfer strategy keyed on mappability, not coherency. Zero-copy on UMA phones with precise cache flush/invalidate, verified on Mali-G57.
Headers vendored, volk dlopens libvulkan.so at runtime. Clone recursive, cmake, done. On any machine.
Grid{x,y,z} is global thread counts. wait_idle() is your cudaDeviceSynchronize. Muscle memory intact.
A ~1.3k-LOC static library. clvk ships LLVM; Kompute ships a framework. Xpose ships a fence.
91/91 tests across five drivers, 38/38 on-device on Qualcomm's proprietary driver. Every number measured on real hardware, and every measurement we got wrong is documented in the repo.
04 · The dispatch path
clspv's NonSemantic reflection is read once at Program::from_file, so every argument's binding, size and kind known before you launch.
Buffers become descriptor set 0. PODs pack into one push-constant block. A mismatch names the exact offending argument.
upload → launch → download and launch → launch chains are fenced with pipeline barriers on the compute queue, automatically.
wait() fires a completion hook that recycles the command buffer and descriptor sets. Nothing leaks, nothing stalls.
05 · Proof, not promises
Measured on a OnePlus 15 — Snapdragon 8 Elite Gen 5, Adreno 840, under Qualcomm’s own driver. Same phone, same model file, same int4 weights for every runtime in the chart.
ON-DEVICE LLM
A full transformer — 26 layers, 838 dispatches per token, all of it Vulkan compute we wrote — running at 60 tokens/sec in airplane mode. No llama.cpp. No TFLite. No NNAPI. The binary contains nothing but Xpose.
FLAT UNDER LOAD
Most on-device LLMs decay as context grows. Ours holds ~60 tok/s from token 128 to token 4,095 — flat to within 6% — because Gemma’s sliding-window attention is applied properly and the softmax that dominates it runs parallel.
06 · THE ECONOMICS
Compute on your users' devices costs you nothing per call. No datacenter bill, no scaling curve. The silicon is already paid for.
Right-sized workloads don't need a datacenter GPU. A phone delivers real TFLOP/s. Xpose is how you actually use them.
Vulkan-capable Android devices already shipped: fragmented and hostile to program. The fragmentation is the part we solve.
07 · Open core
Priced per active device, not per inference. The compute already runs free on your users' phones; metering it would mean phoning home.
Replacing a cloud inference bill? We price as a share of what you stop spending. Device makers & embedded: flat annual license.
08 · Straight answers
Yes, and Core ML has had updatable models since 2019. They fine-tune a graph prepared ahead of time. None of them let you write an arbitrary kernel, own the training loop, or run non-ML compute at all. Xpose is the programmable layer, not another model runner.
Any GPU with a Vulkan 1.1 driver, the floor Android has required on modern 64-bit devices for years. Tested green on Adreno 840 (proprietary driver), Adreno X1-85, Mali-G57, NVIDIA, RADV and llvmpipe. Not "every phone ever made", any modern Vulkan one.
It's OpenCL C, a C dialect with pointers, structs and loops, compiled by clspv. A supported subset of OpenCL C 1.2: no function pointers, no malloc, no recursion. Real enough to port real kernels; honest enough to say what's missing.
Measured, not modelled, on a OnePlus 15 (Adreno 840). Gemma 3 1B decodes at 60.6 tok/s and holds it from token 128 to 4,095 — flat to within 6%. On the same phone, same model, same int4 weights, llama.cpp’s OpenCL backend does 29.6 and its CPU backend 29.1. Model load is 1.9 s. Every number comes from a benchmark you can run from the repo, and every number we previously got wrong is documented there too.
09 · Quick start
Everything is vendored as submodules. No system Vulkan SDK, no environment
setup, no drivers to hunt. And when you're ready for real hardware,
run-android.sh pushes the whole suite to your phone over adb.
$ git clone --recursive https://github.com/mansiverma897993/Xpose.git
$ cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
$ cmake --build build
$ ./build/tests/xpose_tests
[==========] 82 tests passed.
# run the whole suite on a real phone:
$ ./scripts/run-android.sh --build