plot::renderer
cheatah-plot v0.1.0-alpha — Biome Standard 0.6.3-alpha
Classes
Image— An RGBA8 raster image — the CPU-side pixel form the rasterizer produces and both writers below serialize.Prim— One drawable primitive — a 64-byte POD laid out identically in C++ and Slang (16 four-byte lanes; no pointers, no padding surprises).RasterParams— The uniform parameter block the kernels read (bound as a trailing uint buffer, the proven emulated-Metal-compatible convention): framebuffer size, the tile grid, and the clear colour.TileBins— The flattened per-tile primitive index lists (CSR layout) the kernels bind.
Concepts
RasterContext— The context contract the raster step drives — what BOTH lane classes implement.
Functions
TileBins bin_prims(const std::vector< Prim > &prims, std::uint32_t width, std::uint32_t height)
#
Bucket prims into the tile grid covering a width×height framebuffer.
Two passes over the primitive list (count, then fill) build the CSR layout with exactly-sized vectors; inside each tile the indices ascend, preserving paint order.
prims | The primitive list, in paint order. |
width | The framebuffer width in pixels (>= 1). |
height | The framebuffer height in pixels (>= 1). |
The per-tile index lists.
O(prims · tiles-they-touch).
the two returned vectors.
plot:binningvoid push_seg(DrawList &dl, float x0, float y0, float x1, float y1, float half_width, std::uint32_t rgba, float dash_on=0.0f, float dash_off=0.0f)
#
Append a stroked segment.
dl | The list to append to. |
x0 | Start x (pixels). |
y0 | Start y. |
x1 | End x. |
y1 | End y. |
half_width | Half the stroke width in pixels (>= 0.5 for a hairline). |
rgba | The packed colour. |
dash_on | Dash drawn-length in pixels (0 = solid). |
dash_off | Dash gap-length in pixels. |
O(1) amortized.
amortized vector growth.
plot:drawlistAppend a filled disc (scatter/stem markers).
dl | The list to append to. |
cx | Center x (pixels). |
cy | Center y. |
radius | The disc radius in pixels. |
rgba | The packed colour. |
O(1) amortized.
amortized vector growth.
plot:drawlistvoid push_rect(DrawList &dl, float x0, float y0, float x1, float y1, std::uint32_t rgba, float outline_half_width=0.0f)
#
Append an axis-aligned rectangle — filled, or an outline when outline_half_width > 0 (bars, heatmap cells, legend swatches, square markers, gridline boxes).
dl | The list to append to. |
x0 | Left. |
y0 | Top. |
x1 | Right. |
y1 | Bottom (x0<x1, y0<y1). |
rgba | The packed colour. |
outline_half_width | 0 for a filled rect; else half the outline stroke width. |
O(1) amortized.
amortized vector growth.
plot:drawlistvoid push_tri(DrawList &dl, float x0, float y0, float x1, float y1, float x2, float y2, std::uint32_t rgba)
#
Append a filled triangle (area fills are fan-triangulated into these).
dl | The list to append to. |
x0 | Vertex 0 x. |
y0 | Vertex 0 y. |
x1 | Vertex 1 x. |
y1 | Vertex 1 y. |
x2 | Vertex 2 x. |
y2 | Vertex 2 y. |
rgba | The packed colour. |
O(1) amortized.
amortized vector growth.
plot:drawlistAppend one 8×16 glyph at a pixel position — the glyph's bitmap rides IN the prim's aux lanes (4 bits-rows per lane), so the kernel needs no font atlas.
dl | The list to append to. |
x | Left edge (pixels). |
y | Top edge. |
c | The character (the font substitutes '?' outside printable ASCII). |
rgba | The packed colour. |
O(1) amortized.
amortized vector growth.
plot:drawlistAppend a text run, left-aligned at (x, y) top-left, one glyph prim per character.
dl | The list to append to. |
x | Left edge of the first glyph (pixels). |
y | Top edge. |
text | The characters to draw. |
rgba | The packed colour. |
O(len).
amortized vector growth (len prims).
plot:drawlistThe 8×16 bitmap for character c — 16 rows top to bottom, one byte per row, bit 7 = leftmost pixel of the cell (matching the glyph prim's row packing in kernels.hpp).
Characters outside printable ASCII 32..126 (including negative char values) return the glyph for '?', so text drawing never branches on validity.
c | The character to look up. |
The glyph's 16 row bytes (a reference into the compile-time font table).
O(1).
none.
plot:fontThe pixel width of s drawn in this font — the font is strictly monospace, so this is pure arithmetic on the length: kGlyphWidth * len.
The layout passes (tick-label centring, legend sizing) call this instead of measuring glyphs.
s | The text to measure. |
The width in pixels of the rendered run (0 for the empty string).
O(1) — size() is constant time; no glyph is touched.
none.
plot:fontWhether the default GPU lane can come up on this machine — a cached one-shot probe that NEVER throws.
This is the runtime "is there a GPU?" question: render() gates its GPU try on it instead of hand-rolling a try/catch around a first dispatch.
true when the default lane's context is (or can be) live; false when bring-up failed.
O(1) after the first call (the probe result is cached).
none after the first call. @gpualloc the lane's bring-up, once, on success.
plot:gpu_rasterPack an RGBA colour (channels 0..1) into the kernel's byte order (r | g<<8 | b<<16 | a<<24), clamping each channel — the ONE quantization point between the float model layers and the integer raster.
r | Red in [0, 1]. |
g | Green in [0, 1]. |
b | Blue in [0, 1]. |
a | Alpha in [0, 1]. |
The packed RGBA8 value.
O(1).
none.
plot:rasterEncode img as a complete PNG byte stream: the 8-byte signature; IHDR declaring 8-bit RGBA (colour type 6, no interlace); one IDAT whose zlib stream (header 0x78 0x01) carries the filtered pixel data as STORED deflate blocks of at most 65535 bytes each, followed by the Adler-32 of that raw data; then IEND.
Every scanline is prefixed with filter byte 0 (None), so the raw stream is exactly height * (1 + width * 4) bytes and any conforming PNG reader reproduces the input pixels bit for bit.
img | The image to encode; |
The PNG file bytes, ready to write to disk or stream to a viewer.
std::runtime_error | when the pixel buffer size does not match the dimensions. |
O(width × height).
the returned vector plus one transient filtered-scanline buffer of the same order.
plot:pngEncode img and write the PNG to path in binary mode — the renderer's standard save.
img | The image to write; |
path | The destination file path; its parent directory must already exist. |
std::runtime_error | when the pixel buffer size is wrong, the file cannot be opened, or a write fails. |
O(width × height).
the transient encoded byte stream.
plot:pngWrite img to path as a binary P6 PPM (24-bit RGB; the alpha channel is dropped) — the fallback format for tooling that predates PNG support, and a handy raw-bytes debug tap.
img | The image to write; |
path | The destination file path; its parent directory must already exist. |
std::runtime_error | when the pixel buffer size is wrong, the file cannot be opened, or a write fails. |
O(width × height).
one transient RGB row buffer.
plot:pngstd::vector< std::uint32_t > raster_cpu(const std::vector< Prim > &prims, const TileBins &bins, const RasterParams ¶ms)
#
Rasterize a binned draw list into an RGBA8 framebuffer (one uint32 per pixel, row-major) — the reference implementation of plot_clear + plot_raster.
Every pixel starts at the clear colour, then blends its tile's primitives in paint order with the integer src-over — the same loop the kernels run, one thread per pixel.
prims | The draw list, in paint order. |
bins | The tile bins for |
params | The framebuffer size, tile stride, and clear colour. |
The framebuffer, params.width*params.height packed pixels.
O(pixels + Σ per-tile prim work).
the returned framebuffer.
plot:rasterReduce a Figure to the primitive list a rasterizer draws — layout, axes, ticks + labels, grid, every mark kind, and per-subplot legends, in paint order.
fig | The figure model (subplots, axes, series, palette). |
width | The framebuffer width in pixels. |
height | The framebuffer height in pixels. |
The draw list, ready for bin_prims + raster_cpu (or the GPU kernels).
O(total data points + ticks + glyphs).
the returned list (plus transient tick arrays).
plot:reduceRender a figure to RGBA8 pixels at the figure's own size — the readback form (the stream frame, the test surface, the encoder input).
A GPU-enabled build TRIES the default GPU lane first when gpu_available reports one and the environment does not say CHEATAH_PLOT_FORCE_CPU=1; any device failure falls back to raster_cpu with a one-time stderr notice, and further renders stay on the CPU. The two rasterizers share the reduce, the bins and the quantization, so the choice never changes what the figure MEANS — only which silicon fills the pixels.
fig | The figure model to draw. |
The rendered image (width/height from the figure, row-major RGBA).
O(pixels + data); single-threaded by design.
the returned image + the transient draw list, bins, and framebuffer. @gpualloc on the GPU path, the five transient buffers of raster_gpu.
plot:renderRender a figure and write it to path as a PNG — the one-call "give me my plot" form.
fig | The figure model to draw. |
path | The output file path (.png). |
O(pixels + data).
transient render buffers + the encoded byte stream.
plot:renderstd::vector< std::uint32_t > raster_gpu(Ctx &ctx, const std::vector< Prim > &prims, const TileBins &bins, const RasterParams ¶ms)
#
Rasterize a binned draw list through the plot kernels on context ctx — the device mirror of raster_cpu: upload prims/offsets/indices/params, dispatch plot_clear then plot_raster (one thread per pixel), download the framebuffer.
Ctx | The device context lane (see RasterContext). |
ctx | The live context to dispatch on (see detail::ctx_of). |
prims | The draw list, in paint order. |
bins | The tile bins for |
params | The framebuffer size, tile stride, and clear colour. |
The framebuffer, params.width*params.height packed pixels.
O(pixels + Σ per-tile prim work) device-side; O(prims + tiles) transfer.
the returned framebuffer. @gpualloc five transient device buffers (prims, offsets, indices, framebuffer, params), released before returning — also on the throw path.
plot:gpu_rasterRender a figure to RGBA8 pixels on the default GPU lane — the device mirror of render: same reduce, same bins, same packed-pixel unpack; only the rasterizer differs.
Unlike render this does NOT fall back: a dead lane or missing kernel binary throws, so a caller that asked for the GPU explicitly hears exactly why it could not have it.
fig | The figure model to draw. |
The rendered image (width/height from the figure, row-major RGBA).
O(pixels + data); single dispatch pair, blocking.
the returned image + the transient draw list, bins, and framebuffer. @gpualloc the five transient buffers of raster_gpu (plus one-time lane bring-up).
plot:gpu_rasterbool try_gpu(const std::vector< Prim > &prims, const TileBins &bins, const RasterParams ¶ms, std::vector< std::uint32_t > &out_fb)
#
Attempt the default GPU lane for one raster: honours CHEATAH_PLOT_FORCE_CPU=1, the cached gpu_available probe, and a process-wide failure latch — the FIRST device failure prints a one-time stderr notice and every later render goes straight to the CPU reference (retrying a broken lane per frame would re-fail and re-allocate forever).
prims | The draw list, in paint order. |
bins | The tile bins for |
params | The framebuffer size, tile stride, and clear colour. |
out_fb | Receives the device framebuffer on success; untouched on refusal/failure. |
true when the GPU produced out_fb; false to take the CPU path.
O(pixels + per-tile prim work) on the device.
the returned framebuffer vector on success. @gpualloc the five transient buffers of raster_gpu, released either way.
plot:gpuConstants & variables
Threads per workgroup axis of BOTH kernels — matches [numthreads(16, 16, 1)] in shaders/plot.slang.
Equal to kTile ON PURPOSE: one workgroup spans one raster tile, so every thread in a group walks the same tile bin.
The clear kernel's entry name — plot_clear in shaders/plot.slang (buffers: fb, params).
The raster kernel's entry name — plot_raster in shaders/plot.slang (buffers: prims, tile_offsets, tile_prims, fb, params).
The raster tile edge in pixels: binning buckets primitives into kTile×kTile screen tiles.
The glyph cell the kernel rasterizes: 8 pixels wide (a glyph prim's aux lanes carry the bitmap, one byte per row, bit 7 = leftmost pixel).
The glyph cell height in pixels (16 rows = the prim's four aux lanes, 4 rows per lane).
Types
The primitive kinds the raster kernel draws. Values are the on-device codes — append-only.
The primitive list, in paint order. Push through the helpers below.
