What it is
BLT-faster is a C patch against the BLT 2.4 graph / stripchart / barchart widget that reduces the per-frame cost of live, continuously-redrawn charts, with no change to rendered output. It targets the BLT 2.4 tree as bundled in AndroWish / undroidwish and most other BLT 2.4 distributions.
It ships as a GPL-3 patch project: it distributes patches/blt-faster.patch plus documentation, not the BLT source. Apply the patch to a pristine BLT 2.4 tree and rebuild; the changes apply to every graph.
The performance problem
Profiling case: three live espresso charts (pressure, flow, temperature) redrawing at 10 Hz on a tablet. Findings:
- The per-frame cost is dominated by the axes, not the data. Roughly 55–70 % of every frame goes into drawing the axis tick labels and tick marks — re-rasterized through FreeType/AGG on every redraw, even though the label text ("0", "5", "10"…) rarely changes.
- The data curves are a minority of the cost.
- Line width, point count,
-smooth,-reduce, and dashes each move the frame time by less than 10 %. - Batching the tick-mark segments into one draw call saved ~6 %: the cost is pixel-coverage rasterization, not per-segment setup.
The approach is to avoid repeating the axis work every frame.
The optimizations
-cachelabels — the label cache (default ON)
Each unique axis tick-label string is rendered once into a small pixmap (on the axis-margin background, where there is no grid underneath) and then blitted at its new position each frame instead of re-rasterizing the text.
- Pixel-identical — verified by snapshot comparison with zero differing pixels. Falls back to normal drawing for rotated / active / shadowed labels.
- Applies when the axis is auto-scaling, because only the label position moves — the text is reused.
- Measured ~1.3× on live charts.
- On by default; no application change required: patch, rebuild, and every graph's labels use the cache.
- Implemented in
bltGrAxis.c(DrawAxis, a per-axis label cache,GetLabelSprite,Blt_ClearAllLabelCaches).
-bufferchrome — chrome buffering (default OFF, opt-in)
The entire static "chrome" (background, grid, tick labels, axes, border) is rendered once into a cache pixmap and reused, drawing only the data elements on top.
- The chrome is re-rendered only when a signature changes (
Blt_ComputeChromeSignature, an FNV hash of the plot rectangle, focus, and each axis's hidden / min / max / tick-count state), and is invalidated on graph or axis reconfigure. - Measured ~1.9× on graphs whose axes are static or change rarely — history views, zoomed plots, dashboards, finished charts.
- Off by default because a graph whose x-axis auto-rescales on every frame (a live stripchart) cannot cache anything, so it is roughly neutral-to-slightly-slower there. Enable it per graph with
-bufferchrome 1. - Implemented in
bltGraph.c(DrawChrome,DrawCachedChrome,DisplayGraph).
-redrawduty / -minredrawms — redraw throttling (designed)
A separately-designed redraw-pacing pair of options that limits how often expensive redraws run on slow hardware:
-redrawduty <k>— adaptive. After a redraw that took D ms, refuse to redraw again for D·k ms (k=1 gives roughly a 50 % duty cycle). Self-tuning to the hardware.-minredrawms <ms>— a fixed minimum interval between redraws.
This is a responsiveness / CPU-budget change (it can free ~40 % CPU on a slow tablet where stock BLT leaves only ~8 % headroom), not a chart-freshness fix — keeping the chart up to date instead relies on cheaper redraws (-bufferchrome plus a stepped rather than grow-every-frame x-axis). It composes with the label cache and chrome buffering. These options are designed and documented but not part of the applied patch yet.
Results
Measured on an Apple M4, using a reproduction of the 3-chart live espresso page (pressure / flow / temperature), full element set, 10 Hz over a 45 s "shot":
| chart kind | stock BLT | BLT-faster | speedup |
|---|---|---|---|
| live charts, auto-scaling x-axis (1280×800) | 5.3 ms/frame | 3.9 ms/frame | 1.34× (-cachelabels) |
| fixed-axis graph (history / zoomed / dashboard) | 3.8 ms/frame | 2.0 ms/frame | 1.9× (-bufferchrome) |
slow tablet, -redrawduty |
— | — | frees ~40 % CPU |
The relative speedup typically grows on slower hardware (tablets, single-board computers), where text rasterization is comparatively more expensive than on a fast desktop.
Measurement note: benchmark the on-screen redraw path (update idletasks), not $graph snap. Snap forces a full reset-world redraw plus a photo read-back and bypasses -bufferchrome, so it does not reflect real live-redraw cost.
How to apply
From the root of a BLT 2.4 source tree (the directory containing src/):
patch -p1 < /path/to/BLT-faster/patches/blt-faster.patch
# ...then rebuild BLT the way you normally do.
Or use the helper:
./apply-patch.sh /path/to/blt
The patch touches four files (src/bltGraph.{c,h}, src/bltGrAxis.{c,h}), adds roughly 375 lines, and is additive: with the options at their defaults the rendering is identical to stock BLT. After rebuilding, every graph has -cachelabels on; enable -bufferchrome per graph:
.g configure -bufferchrome 1 ;# opt IN to the chrome cache (default off)
.g configure -cachelabels 0 ;# opt OUT of the label cache (default on)
BLT loads as a runtime library, so a test script can load the patched build to override an embedded one without relinking the host wish.
Repository
Source, patch, and documentation (docs/HOW-IT-WORKS.md, docs/BENCHMARKS.md) are on GitHub: github.com/johnbuckman/BLT-faster. GPL-3.0. The patch and docs are original work; they modify BLT, which carries its own license — this repo distributes the changes, not the BLT source.