Writing an OS Kernel from Scratch | 347: Display — From Framebuffer to GPU

Abstract: The display is the most intuitive output device of a computer, but the timing, resolution, framebuffer, and GPU rendering pipeline behind it are rarely studied in depth. This article starts from CRT timing principles, explains the DRM/KMS architecture, modetest/xrandr commands, taking you through the complete chain from pixel writing to photon emission.


1. CRT Timing: Why These Strange Resolutions?

1.1 Physics of Scanning

CRT (Cathode Ray Tube) displays scan the phosphor screen with an electron beam from left to right, top to bottom. After scanning one line, the beam needs a horizontal retrace; after the full screen, a vertical retrace. These retrace times result in a bunch of “edge” pixels being invisible.

Bash
←────────────── Visible ──────────────→←─ Front ──→←─ Sync ──→←─ Back ──→
                                  HFP        HS      HBP
 ─────────────────────────────────────────────────────────────────────────────→
 │                               │         │        │                        │
 │   Pixel 0    Pixel 1   ... Pixel N-1    |        |                        │
 │   ←───────── One scanline ──────────→ | ← HSync → |                        │
 │                                                              (Next line)   │
 ──────────────────────────────────────────────────────────────────────────────→↓
                              ↑ Horizontal Retrace

Core parameters (using 1920×1080@60Hz as example):

Parameter Value Description
Pixel Clock 148.5 MHz 148.5M pixels transmitted per second
Horizontal Total 2200 visible 1920 + front 88 + sync 44 + back 148
Vertical Total 1125 visible 1080 + front 4 + sync 5 + back 36
Refresh Rate 60 Hz 148.5M / (2200 × 1125) ≈ 60Hz

1.2 Sync and Polarity

HSync and VSync have polarity (positive/negative). Common resolution timings follow VESA standards:

C
// VESA DMT (Display Monitor Timing) standard 1920x1080@60
static const struct display_timing1920x1080_60 = {
    .pixel_clock_khz = 148500,
    .h_active = 1920,
    .h_blanking = 440,      // front_porch + sync + back_porch
    .v_active = 1080,
    .v_blanking = 45,       // front_porch + sync + back_porch
    .h_sync_offset = 88,    // front_porch
    .h_sync_width = 44,     // sync pulse width
    .v_sync_offset = 4,     // front_porch
    .v_sync_width = 5,      // sync pulse width
    .h_sync_polarity = 1,   // positive
    .v_sync_polarity = 1,   // positive
};

2. Modern Display Pipeline

Bash
Modern display rendering pipeline:

  CPU (Application)
    │
    │ draw GPU commands
    ▼
  GPU (Command Processor)
    │
    │ Vertex → Fragment → Rasterization
    ▼
  Framebuffer (GPU memory)
    │
    │ Scanout (DMA)
    ▼
  Display Controller (CRTC)
    │
    │ Generate HSync/VSync/DE signals
    │ Read pixel data from FB at pixel clock rate
    ▼
  Encoder (HDMI/DP/LVDS)
    │
    │ Encode pixel data + sync signals
    ▼
  Physical Connector → Monitor

2.1 Double/Triple Buffering

Bash
Single buffering:
  [FB] → Display
  Problem: Screen tearing (partial frame displayed during update)

Double buffering:
  [Front FB] → Display    [Back FB] ← CPU/GPU writes
  Swap: atomically switch front/back on VSync
  No tearing, but one frame of latency

Triple buffering:
  [Front FB] → Display    [Mid FB] ready    [Back FB] ← GPU writes
  Higher throughput, slightly more latency
  Reduces frame drops under load

2.2 Linux DRM/KMS Code Example

C
// Simplified DRM mode-setting
#include <xf86drm.h>
#include <xf86drmMode.h>

int main() {
    int fd = open("/dev/dri/card0", O_RDWR);
    drmModeRes *res = drmModeGetResources(fd);

    // Find a connector
    for (int i = 0; i < res->count_connectors; i++) {
        drmModeConnector *conn = drmModeGetConnector(fd, res->connectors[i]);
        if (conn->connection == DRM_MODE_CONNECTED) {
            // Use the first mode
            drmModeModeInfo *mode = &conn->modes[0];

            // Create dumb buffer
            uint32_t fb_id;
            uint32_t handle;
            drmModeCreateDumbBuffer(fd, mode->hdisplay, mode->vdisplay, 32, NULL, &handle, &fb_id);

            // Set CRTC
            drmModeSetCrtc(fd, res->crtcs[0], fb_id, 0, 0,
                          &conn->connector_id, 1, mode);
            break;
        }
    }
    close(fd);
    return 0;
}
Last modified: 2024年7月15日

Author

Comments

Write a Reply or Comment

Your email address will not be published.