Skip to main content

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 the RedrawState state machine:
RedrawState state transition diagram

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 calling redraw(), there are two possible paths:
If the redraw causes damage (i.e. something on the output changed):
1

Move to WaitingForVBlank

We move into the WaitingForVBlank state, since we cannot redraw until we receive a VBlank event.
2

Wait for hardware VBlank

The compositor waits for the actual VBlank event from the display hardware.
3

Return to Idle or Queued

When VBlank arrives:
  • If nothing needs redrawing: return to Idle
  • If redraw was requested: move to Queued

Why Estimated VBlank?

This is necessary in order to throttle frame callbacks sent to applications to at most once per output refresh cycle.
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:
  1. Render a frame
  2. Immediately receive a frame callback
  3. Render another frame
  4. Repeat indefinitely
This would consume excessive CPU for no visible benefit.

State Flow Summary

Key Takeaways

Only one frame can be submitted to an output at a time on TTY.
The compositor must wait for VBlank before submitting the next frame with damage.
Estimated VBlank is used to throttle frame callbacks even when there’s no damage, preventing excessive CPU usage.
The RedrawState enum in OutputState manages the entire redraw cycle.

Build docs developers (and LLMs) love