[ Architecture ]
Rendering Pipeline Architecture
Overview
Section titled “Overview”The rendering pipeline in koto::Frame (include/koto/frame.hpp, src/frame.cpp) and koto::Cell (include/koto/cell.hpp) transforms immediate-mode widget draw calls into a single synchronized ANSI byte stream per dirty frame.
Mermaid diagram
Section titled “Mermaid diagram”flowchart TD
DrawFrame["App::draw_frame"] --> ClearBack["Frame::clear_back_buffer"]
ClearBack --> RenderCb["m_render_handler(m_frame)"]
RenderCb --> SetCell["Frame::set_cell(row, col, cell, tag_id)"]
SetCell --> BackBuf["Frame::m_back_buffer & Frame::m_hit_buffer"]
DrawFrame --> SyncGeom["FocusManager::sync_geometry(m_frame)"]
SyncGeom --> FrameRender["Frame::render(flush)"]
FrameRender --> DirtyScan{"front != back or cursor_changed?"}
DirtyScan -->|false| EarlyReturn["return (0 bytes emitted)"]
DirtyScan -->|true| BeginSync["m_output.append('\033[?2026h')"]
BeginSync --> MoveCursor["Frame::move_cursor(r, c)"]
MoveCursor --> PaintCell["Frame::paint_cell(back)"]
PaintCell --> EndSync["m_output.append('\033[?2026l')"]
EndSync --> WriteAll["koto::write_all(STDOUT_FILENO, m_output)"]
Walkthrough
Section titled “Walkthrough”- Back-buffer reset (
Frame::clear_back_buffer,src/frame.cpp:179-189):- Resets all elements of
m_back_bufferto defaultCell{}(ch = U' ',fg = COLOR_DEFAULT,bg = COLOR_DEFAULT,width = 1,continuation = false,attrs = 0), fillsm_hit_bufferwithTAG_NONE(-1), clearsm_tag_bounds, and resetsm_cursor_visible = false,m_cursor_row = -1,m_cursor_col = -1.
- Resets all elements of
- Cell staging and wide-character repair (
Frame::set_cell,src/frame.cpp:67-141):- Rejects out-of-bounds
(row, col). - Repair rule 1: If
col > 0 && m_back_buffer[idx].continuation, overwrites the left-hand owner cellm_back_buffer[idx - 1]withCell{U' '}. - Repair rule 2: If
m_back_buffer[idx].width == 2 && col + 1 < m_cols, resetsm_back_buffer[idx + 1]toCell{}. - Resolves effective width
w(1or2). Ifw == 2 && col + 1 >= m_cols, replacescwithCell{U' '}of width1to prevent right-margin terminal wrap. - Repair rule 3: If
w == 2 && col + 1 < m_cols, clearsm_back_buffer[idx + 2]ifm_back_buffer[idx + 1]was previously a width-2 head, then writesCell::make_continuation()tom_back_buffer[idx + 1](and stampstag_idintom_hit_buffer[idx + 1]). - Writes
ctom_back_buffer[idx]and updatesm_hit_buffer[idx]andm_tag_bounds[tag_id.value()].
- Rejects out-of-bounds
- Differential scan and SGR state compression (
Frame::render&Frame::paint_cell,src/frame.cpp:200-440):render()first checks whether any cell with!back.continuationsatisfies!(m_front_buffer[idx] == m_back_buffer[idx])or whether cursor visibility/coordinates changed since the last frame. If neither changed,render()returns immediately.- Wraps output in DEC Mode 2026 (
[?2026h..[?2026l) and resets tracked SGR state (m_cur_fg = ~0u,m_cur_bg = ~0u,m_cur_attrs = 0xFF). - For each dirty non-continuation cell at
(r, c), callsmove_cursor(r, c)([%d;%dHusing 1-based coordinates) unlessr == cursor_row && c == cursor_col && last_cell_was_ascii(back.width == 1 && back.ch >= 0x20 && back.ch < 0x7F && back.grapheme.size == 1). paint_cell(back)emits[22;23;24;27;29mwhen any previously active attribute bit is cleared, emits[1m/2m/3m/4m/7m/9mfor newly enabledTextAttrbits, emits foreground and background SGR codes according tom_term->color_mode()(TrueColor,Ansi256,Ansi16, orNoColor) only whenc.fg != m_cur_fgorc.bg != m_cur_bg, and appendsc.grapheme.view().- Copies
backintofront(andm_back_buffer[idx + 1]intom_front_buffer[idx + 1]whenback.width == 2). - Appends cursor show/hide (
[?25hor[?25l),[?2026l, and flushes viawrite_all(STDOUT_FILENO, m_output)andfflush(stdout).
Invariants
Section titled “Invariants”- Cache-line
Cellsize:static_assert(sizeof(Cell) == 64)(include/koto/cell.hpp:74) guarantees that everyCelloccupies exactly one 64-byte cache line (Grapheme44B +ch4B +fg4B +bg4B +width1B +continuation1B +attrs1B +reserved5B). - Post-Unicode cursor resynchronization:
last_cell_was_asciiis set tofalseafter painting any non-ASCII or width-2 cell (src/frame.cpp:395-401), forcing an explicit[r;cHcursor positioning sequence before the next dirty cell so font/emulator width discrepancies can never drift subsequent cells. - No orphaned continuation cells: The three repair steps in
Frame::set_cell(src/frame.cpp:73-120) guarantee that every cell withcontinuation == trueis preceded atcol - 1by a cell withwidth == 2, and every cell withwidth == 2is followed atcol + 1 < m_colsby a cell withcontinuation == true.