Overview
On a TTY, only one frame can be submitted to an output at a time, and the compositor must wait until the output repaints (indicated by a VBlank) to be able to submit the next frame.In niri we keep track of this via the
RedrawState enum that you can find in an OutputState.RedrawState State Machine
Here’s a diagram of state transitions for theRedrawState state machine:

State Descriptions
Idle State
Idle
The default state, when the output does not need to be repainted.Transition: Any operation that may cause the screen to update calls
queue_redraw(), which moves the output to a Queued state.Queued State
Queued
The output is scheduled for redraw.What happens: At the end of an event loop dispatch, niri calls
redraw() for every Queued output.After Redraw
After callingredraw(), there are two possible paths:
- With damage
- Without damage
If the redraw causes damage (i.e. something on the output changed):
Move to WaitingForVBlank
We move into the
WaitingForVBlank state, since we cannot redraw until we receive a VBlank event.Wait for hardware VBlank
The compositor waits for the actual VBlank event from the display hardware.
Why Estimated VBlank?
Without this throttling, applications can start continuously redrawing without damage and eating a lot of CPU in the process.Example Scenario
Off-screen content changes
If the application window is partially off-screen, and it is only the off-screen part that changes, the compositor sees no damage on the visible area.Without estimated VBlank throttling, the application could:
- Render a frame
- Immediately receive a frame callback
- Render another frame
- Repeat indefinitely
State Flow Summary
Key Takeaways
Frame submission is sequential
Frame submission is sequential
Only one frame can be submitted to an output at a time on TTY.
VBlank synchronization
VBlank synchronization
The compositor must wait for VBlank before submitting the next frame with damage.
Frame callback throttling
Frame callback throttling
Estimated VBlank is used to throttle frame callbacks even when there’s no damage, preventing excessive CPU usage.
State tracking via RedrawState
State tracking via RedrawState
The
RedrawState enum in OutputState manages the entire redraw cycle.