docs v1.0.0

Render routes

bibui separates recording a frame from drawing it. The runtime turns your build into a DrawList. The list holds fills, borders, shadows, paths, textures and glyphs. A window provides a target, and a renderer draws the list onto it. There are three renderers and two windows. You choose the combination in code. Nothing is discovered at run time.

1 · BuildYour codeA tree of boxes, texts and icons, every frame
→
2 · LayoutSolverA rectangle for every node, all at once
→
3 · PaintDrawListDraw commands, with theme tokens resolved
→
4 · DrawRendererSkia GL, Skia CPU, or managed
→
5 · PresentWindowGL swap, or a Wayland commit

Steps 1 to 3 are identical on every route. That is why a frame is a pure function of tree, theme, surface size and scale factor. Changing route changes only steps 4 and 5.

The renderers

RendererPackageDraws onTextNotes
SkiaGlRendererBibui.Raster.SkiaThe GPU, through OpenGLSkiaThe fastest on whole-window redraws. Needs a GlTarget, so it runs on the Silk window only.
SkiaCpuRendererBibui.Raster.SkiaThe CPUSkiaDraws into a CpuTarget, an SKSurface or RasterPixels. The verifier draws its reference pictures with it.
ManagedRendererBibui.Raster.Managed.BackendThe CPU, in C#FreeType + HarfBuzzUses no Skia and no native code of its own. Clean under Native AOT, and has damage tracking.

Every renderer has the same calls: Draw, CreateTexture, UpdateTexture and ReleaseTexture. Build code never sees the renderer. An app names its renderer in one place:

C#
global using Renderer = Bibui.Raster.Skia.SkiaGlRenderer;   // or Bibui.Raster.Managed.Backend.ManagedRenderer

Window × renderer

Which renderer can draw into which window, and how the pixels get to the screen.

Skia, GPU
SkiaGlRenderer
Skia, CPU
SkiaCpuRenderer
Managed, CPU
ManagedRenderer
Silk windowGLFW + OpenGL DefaultSilkSkiaGl Yesvia GlCpuFramebuffer · SilkSkiaCpu Yesvia GlCpuFramebuffer · SilkManaged
Wayland windowwl_shm buffers Plannedno GL on this host yet YesWaylandSkiaCpu · AOT beside libSkiaSharp DefaultWaylandManaged · one AOT file
Headlessmemory you own — YesRasterPixels, SKSurface YesRasterPixels

The two complete routes

Silk + Skia

Bibui.Platform.Silk · Bibui.Raster.Skia
SilkWindow→window.Gl→SkiaGlRenderer
Dependencies
SkiaSharp, Silk.NET (GLFW), an OpenGL driver, fontconfig
Native AOT
No. Silk.NET finds its platform by reflection

Wayland + managed

Bibui.Platform.Wayland · Bibui.Raster.Managed.Backend
WaylandWindow→window.Cpu→ManagedRenderer
Dependencies
No NuGet packages. libwayland-client, libxkbcommon, FreeType, HarfBuzz
Native AOT
Yes, one file of about 5 MB

Choosing a route

The easy way is a Bibui.App package. It names the window and the renderer for you. Swap the package to swap the route, with no change to your code. To choose in code, open the window. Then construct the renderer, bound to one of the window's targets:

C#
using var window = SilkWindow.Open(runtime, new("My app", 960, 640));
using var skia = new SkiaGlRenderer(window.Gl);
// in the loop, between BeginDraw and EndDraw:
skia.Draw(runtime.DrawList);

Falling back from the GPU

Nothing falls back on its own. SkiaGlRenderer.ForGl returns null where Skia cannot drive the window's GL context. The app then decides what to do instead. Usually that means the CPU through GlCpuFramebuffer, as in the second tab above. Textures belong to the renderer that made them. If the app swaps renderer while it runs, the textures are made again.

Text

bibui holds, measures and draws text as UTF-16. Skia shapes and draws text on the Skia routes. On the managed route, FreeType rasterises the glyphs and HarfBuzz shapes them. Text from FreeType and HarfBuzz measures exactly as Skia's does. Its pixels match Skia's too, at SSIM 1.0. So a layout does not move when you change route.

Damage tracking

The managed rasterizer can repaint only what changed. Turn it on with new ManagedRenderer { DamageTracking = true } or BIBUI_DAMAGE=1. It then compares each DrawList with the previous one, draw by draw. It rasterises only the rectangles that differ. It draws them onto a surface that still holds an earlier frame.

  • The result is byte-identical to a full repaint.
  • If the changes cover more than half the surface, the whole frame is drawn.
  • Nothing above the rasterizer changes. Every frame is still built, laid out and recorded in full.
  • A typical frame changes only a hover colour or a caret. That is how a CPU-only stack holds 60 Hz.
  • BIBUI_DAMAGE_DEBUG=1 tints what each frame repainted, so you can see it.

Skia always draws whole frames.

Pixel parity

Each component has one reference picture, rendered by Skia. Every route is compared with it by SSIM. The managed rasterizer matches it except in gradients and wide strokes. Within one build of bibui, the same tree, theme, surface size and scale factor give the same image, byte for byte, every time.

CheckSkia CPUSkia GPUManaged
Component fixtures (87)ReferenceNot publishedMatches, except gradients and wide strokes
Text, shaped and drawnReferenceNot publishedSSIM 1.0
Damage-tracked frame against a full repaint——Byte-identical

Planned. A parity table per component for every route, giving each route's SSIM against the reference. The verifier will generate it on each release.

Environment variables

These are diagnostics. They are not part of the API promise (see Versions).

VariableEffect
BIBUI_DAMAGE=1Turns on damage tracking in ManagedRenderer.
BIBUI_DAMAGE_DEBUG=1Tints the region each frame repainted.
BIBUI_DAMAGE_TRACE=1Logs what damage tracking decided, frame by frame.
BIBUI_ALWAYS_RENDER=1Draws a frame every refresh, even when nothing asked for one. Useful for timing.
BIBUI_PROFILE=1Prints, for every frame, the time EndBuild spent building, laying out and recording.
BIBUI_REDUCED_MOTION=1Every eased value arrives in one step, hover included.
BIBUI_TEST_SERVER=1Starts the test server in a BibuiApp.

Custom drawing and textures

A box can paint itself. Set Box.Draw to a callback. Whatever it draws is recorded into the same DrawList as the built-in nodes. So every renderer draws it, and damage tracking sees it. Images and video frames are textures, made on the renderer the app chose. The formats are RGBA8, BGRA8, R8, RG8 and A8. R8 suits a camera's NIR frame, for example. A Stream texture is updated in place each frame. Concepts covers both.