04/Graphics engine/2026
Prism
GPU image processing for Swift
A Swift and Metal image-processing engine for Apple platforms. A compact API hides an explicit render graph, graph optimization, pooled textures and instrumentation. Core Image is not used to implement effects.
00/Input
Source photograph

01/Exposure
+0.3 EV

- Exposure0.125 ms
- GPU total0.23 ms
02/Contrast
1.30

- Exposure+Contrast0.177 ms
- GPU total0.25 ms
03/Saturation
1.35

- Exposure+Contrast+Saturation0.136 ms
- GPU total0.18 ms
04/Sharpen
amount 2.0, radius 2.5

- Exposure+Contrast+Saturation0.104 ms
- Sharpen blur horizontal0.104 ms
- Sharpen blur vertical0.098 ms
- Sharpen combine0.031 ms
- GPU total0.37 ms
A render graph
behind a small API.
Pipelines are reusable values. A Renderer owns the caches and pooled resources and is safe to share across concurrent tasks. Existing Metal textures can enter and leave without a CPU round trip.
Underneath, each pipeline is lowered to a validated graph, optimized, planned for resource lifetimes, and executed asynchronously on the GPU. The package depends only on Apple frameworks.
let pipeline = ImagePipeline {
Exposure(0.25)
Contrast(1.1)
GaussianBlur(radius: 6)
}
let output = try await pipeline.render(inputCGImage)01/Five decisions
- 01
A validated graph
A pipeline is lowered to a DAG that is checked for dependencies, cycles, dimensions and formats before any resource is allocated.
- 02
Optimization
Identity parameters, dead nodes and compatible pointwise operations are removed or fused before execution.
- 03
Lifetimes, not allocations
Planning computes each resource’s final use, so a ten-pass chain needs an input and two alternating intermediates, not one texture per pass.
- 04
Custom Metal kernels
Color, separable blur, sharpen, resize, vignette and LUT grading are hand-written. The tiled blur caches rows and columns in threadgroup memory.
- 05
Instrumentation
Three levels, Instruments signposts and per-pass GPU timestamps report texture reuse, cache behavior and eliminated passes.
02/Evidence
- 3.30 ms4K Gaussian blur, GPU, after optimization. 7.70 ms before
- −57%Blur GPU time after the tiled, register-blocked kernel
- 4.6 ms4K exposure, end to end, optimized image path. 16.8 ms before
- 182Tests in 22 suites
Medians from the committed result files on one Apple M5, macOS 27.0.1, release build, deterministic synthetic inputs. Numbers vary with GPU state, and performance data covers one GPU generation.
| Case | Resolution | Result |
|---|---|---|
| Exposure, original image path | 4K | 16.8 ms end to end |
| Exposure, optimized image path | 4K | 4.6 ms end to end |
| 5-stage pipeline, texture path | 4K | 2.8–3.1 ms |
| Gaussian blur σ=8, before tiled kernel | 4K | 7.70 ms GPU |
| Gaussian blur σ=8, optimized | 4K | 3.30 ms GPU |
| LUT encode, before cache | — | 0.42–0.47 ms |
| LUT encode, after cache | — | 0.02–0.04 ms |
Measure, change one thing, measure again
The baseline showed image conversion, not GPU work, dominating end-to-end time: at 4K, exporting a result took 10.8 ms of a 16.7 ms total against 1.3 ms on the GPU. The image path lost its redundant copies.
Blur turned out to be bound by texture fetches, roughly 120 billion per second regardless of radius, so the kernel now fetches each texel once per tile. LUT textures were being rebuilt on every render and are now cached.
One early estimate, that fusing per-pixel passes would cut time in proportion to pass count, was wrong. The optimization log keeps the original claim and records the correction beside it.
Every operation has a Double-precision CPU reference. Random graph tests cover optimizer semantics and resource aliasing. Concurrency, cancellation and precision mixing are tested, Thread and Address Sanitizer ran clean, and important tests were mutation-checked.
03/Limits
CGImage and MTLTexture are the only supported interop types.
Bilinear resize has no downsampling prefilter, and there is no public composite operation yet.
Peak memory scales with renders in flight, and Prism does not limit them.
High precision does not imply HDR or professional wide-color correctness.
SourceView on GitHub