Skip to content
Blog

The math behind Brilliant's renderer: a budget of nanoseconds per pixel

7 min read

Every architectural decision in Brilliant's renderer comes down to one piece of arithmetic. A display frame at 60 Hz is 16.7 milliseconds. A modern laptop viewport is somewhere between 2 and 15 million device pixels. Divide, and you get the entire budget for everything the engine does: between one and eight nanoseconds per pixel, per frame. Model diffing, geometry, rasterization, effects, compositing, the works.

One nanosecond is about three clock cycles. That number is the renderer's whole worldview, and this post is a tour of the thinking that follows from it: what things cost, why some caches are provably a bad idea, and what happens on hardware where the arithmetic stops working.

The two currencies

GPU work is paid in two currencies, and they fail differently.

Bandwidth is bytes moved. A backdrop-reading effect like frosted glass reads the pixels behind it, blurs them, and writes them back, which is a lot of traffic per pixel. On a discrete GPU that is almost free. On an integrated GPU with a fifth of the bandwidth, it's the first thing that saturates.

Driver overhead is the sneaky one: fixed per-operation costs that don't care how many pixels you touch. We learned this the expensive way. A user's canvas with a few hundred glass elements produced frames where the GPU work measured fine but the frame took 170 milliseconds anyway. The fix wasn't faster shading; the per-pixel math was already cheap. It was restructuring the work so the driver had far less of it to coordinate, and the same content dropped from 170 ms of driver-side overhead to half a millisecond. Same pixels, same math per pixel, two orders of magnitude difference, because we had been paying in the wrong currency.

World space times zoom equals screen space

The most consequential equation in the codebase is embarrassingly small: screen distance = world distance × zoom. Anything represented in world space and evaluated against the live camera is automatically correct at every zoom level, forever, with zero per-zoom preparation.

That property is held across the pipeline, and it's why curves in Brilliant stay sharp from 2% zoom to 5300% without the engine ever pre-rendering anything per zoom level. When a user once photographed faint waviness on a wide shallow curve, the fix wasn't a special case: it was tightening the accuracy budget until the curve's worst-case deviation sat below what a screen can physically show, at every zoom in the range. The waviness went away everywhere at once, because the guarantee is a function of zoom, not a table of pre-rendered levels.

The same equation is the case against zoom-keyed caches, the ones that render something at one zoom and reuse it at another. Stretched reuse has error proportional to the zoom ratio (that's the blur), and re-baking on zoom change costs a full render at gesture time (that's the hitch). A world-space representation evaluated live has neither failure mode, and when we finally benchmarked our most sophisticated zoom-keyed cache against live evaluation on real documents, live won on speed too. That story, with its numbers, is in the rust engine post.

Never budget against a global count

Here is the lesson from this year's work that we find genuinely instructive. Our most demanding effect needs to know, for every pixel near a shape's rim, how far that pixel is from the shape's edge. A complex shape (a boolean of a whole word of text, say) can have tens of thousands of edge segments. Budget naively, cost per pixel against total segments, and the arithmetic collapses: we measured the worst constructible cases at hundreds of milliseconds per frame.

But the pathological number was global, and a pixel doesn't care about global. A pixel on the rim only needs the geometry near it, and when we measured real "monster" shapes, the local neighborhoods were an order of magnitude sparser than the global count implied. Restructuring the evaluation around that fact made per-pixel cost track local density instead of document complexity, and a document that used to route to a lower-quality fallback now evaluates exactly, live, in 0.2 to 0.3 milliseconds per frame across the entire zoom range.

The general law: never budget against a global count when each pixel only ever sees a neighborhood. Global counts are how you scare yourself into building caches you don't need.

What the measured anchors say

Numbers, from our reference workstation (an M4-class Mac driving up to 15 megapixels; treat these as one machine's measurements, not universal constants):

  • Full glass effect, all-in, at total-viewport rim coverage: about 1.2 ns per device pixel. Of that, only a small slice is the exact geometry evaluation; the bulk is the bandwidth-bound optics tail (backdrop grab plus blur) that any implementation pays.

  • 1.2 ns/px × 14.7 Mpx ≈ 17.6 ms: right at the frame budget, and that is the worst constructible case, a rim covering every pixel on a 5K display. The same scene at a more typical 7 Mpx viewport is ~8.5 ms, comfortable.

  • Real documents are nowhere near the worst case: a 4,919-element production document measured 0.10 ms of glass evaluation at the median across a 0.02-to-53 zoom sweep.

Now the extrapolation, clearly labeled as such. An integrated GPU has roughly a tenth of the compute and a fifth of the bandwidth of that workstation, so both halves of the cost get several times worse. Sounds fatal, except for the law that quietly rescues every budget in this post: weaker machines drive smaller viewports. An integrated-GPU laptop pushes 2 to 4 megapixels, not 15. At the small end, even worst-case saturation lands right at budget; partial glass coverage, the actual common case, fits with a wide margin.

And at the top of the iGPU range, full-viewport saturation honestly does not close: the arithmetic says tens of milliseconds no matter how clever the code, and the caches we deleted wouldn't have closed it either, since the bandwidth tail dominates. That case is why our rendering contract has an honest-degradation value: when the math says no, the answer is a deterministic, visible, reported reduction that settles back to exact one frame after rest, never a silent quality trade.

Measuring without fooling yourself

One more family of math matters here: the statistics of measurement, because per-pixel nanoseconds are exactly the kind of number a machine will lie to you about.

Thermals move everything. We measured the same workload 5.5x slower hot versus cold, uniformly. So no performance gate in our suite asserts absolute milliseconds; every gated measurement is a ratio against a reference workload measured under the same conditions, and the ratio stays put while the absolute numbers swing. GPU timing has its own failure modes on top (asynchronous readbacks, saturation, debug-build inflation), and every report is stamped with enough context that nobody judges a number captured under conditions that invalidate it. The full philosophy is in the jank post; the one-line version is that a measurement pipeline needs error analysis exactly like any other instrument.

The point

None of this math is exotic. It's division, a cost model, one change of variables, and some care with statistics. What makes it valuable is that it's written down and every renderer decision has to survive it: features get a cost model before they get an implementation, optimizations get an A/B against the thing they claim to beat, and when a beautiful idea loses to arithmetic, the arithmetic wins. The nanosecond budget isn't a limitation we resent. It's the sharpest design tool we have.