SchOS/include/gfx.h
Nexoniarz 60d06658f0 gfx: draw the console in RAM, and write it to the card write-combined
On the first real machine with a discrete card (an RTX 3060, 800x600 from
the UEFI GOP) the console was slow enough to photograph mid-scroll, and the
boot died on CPU 1's "cpu1: starting": CPU 0 spun 200M times on the console
lock while CPU 1 held it, scrolling, and called it a deadlock.

The framebuffer is device memory in the uncached 3-4 GB page of the direct
map. QEMU's stdvga is RAM underneath, so nothing showed there; on a card,
gfx_scroll_up's memmove read the whole screen back across the bus one byte
at a time, and every anti-aliased glyph read back each pixel it blended.

So the console now draws into a copy of the screen in RAM (vmap_alloc: kalloc
pages behind a page table of their own, since nothing needs it contiguous),
and gfx_flush() copies the changed rectangle onto the card through an
ioremap_wc() mapping -- PAT entry 4 set to write-combining on every CPU.
Flushes happen where drawing ends: a message, a terminal write, an echo, a
terminal switch. Scrolling moves eight bytes at a time instead of one.

/dev/fb0 reads and writes the card itself, as before. The early console's
fb_info now carries the physical address it always should have.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-22 23:46:58 +02:00

105 lines
4.7 KiB
C

#pragma once
// Drawing, and the text console under it, on whatever display is bound.
//
// This was kernel/dev/vga.c, and every consumer called it by that name:
// panic.c drew the crash screen with vga_fill_rect, the console printed with
// vga_putc, /dev/fb0 handed out vga_fb_phys(). None of that code has anything
// to do with a VGA -- it is bitmap drawing over a linear framebuffer, and the
// only VGA-specific part was where the framebuffer came from.
//
// So the driver pushes its scanout in here (gfx_attach_scanout) and everything
// above draws through these calls. A second display driver becomes a second
// caller of one function rather than an edit to the crash screen. This is the
// same arrangement as include/netdev.h, for the same reason.
//
// Not the same thing as the display class in include/display.h. That is the
// compositor's interface -- acquire a back buffer, present a rectangle, wait
// for vblank -- and it is about *frames*. This is the kernel's own drawing
// surface, used by the console and the crash screen, which do not double
// buffer and must keep working when nothing else in the system does.
// A linear framebuffer, exactly as the hardware presents it.
struct fb_info {
volatile uchar *ptr; // kernel-mapped base, or 0 if there is none
uint64 phys; // physical base, for mmap and for page tables
uint64 size; // bytes, stride * height rather than w * h * bpp
uint32 stride; // bytes per row, which may exceed width * bpp/8
uint32 bpp;
uint32 width, height;
};
// A driver publishing the framebuffer it owns. Called once, early: the console
// draws long before any bus is enumerated, so whoever owns the display the
// loader left running has to say so before then.
void gfx_attach_scanout(const struct fb_info *);
// The scanout as attached, for the few callers that need the memory itself --
// /dev/fb0 maps it, and fork copies the mapping. Returns 0 if none.
const struct fb_info *gfx_scanout(void);
int gfx_ready(void);
void gfx_dims(int *w, int *h);
uint32 gfx_width(void);
uint32 gfx_height(void);
uint64 gfx_fb_phys(void);
uint64 gfx_fb_size(void);
uint32 gfx_fb_stride(void);
uint32 gfx_fb_bpp(void);
volatile uchar *gfx_fb_ptr(void);
// The rows drawn since this was last called, for whoever is keeping a second
// screen in step with this one. Returns 0 when nothing has changed.
int gfx_damage_take(int *y0, int *y1);
// Pixels and shapes.
void gfx_draw_pixel(int x, int y, uint32 color);
void gfx_clear(uint32 color);
void gfx_fill_rect(int x, int y, int w, int h, uint32 color);
void gfx_fill_circle(int cx, int cy, int r, uint32 color);
void gfx_draw_logo(int x, int y, uint32 color, uint32 bg);
void gfx_logo_centered(int cy, uint32 color, uint32 bg);
// The built-in 8x16 font: glyphs at a position, and text that leaves the
// cursor alone. The crash screen lays itself out rather than printing, which
// is what needs the second one.
void gfx_draw_char(int x, int y, uchar c, uint32 color);
void gfx_text_at(int x, int y, const char *s, uint32 color);
int gfx_char_w(void);
int gfx_char_h(void);
// The text console: a cursor, scrolling, and the erase operations an ANSI
// escape parser needs. See kernel/core/console.c.
void gfx_putc(int c, uint32 color);
void gfx_puts(int x, int y, char *s, uint32 color);
void gfx_set_bg(uint32 color);
void gfx_screen_reset(void);
// See kernel/dev/gfx.c: drawing goes to a copy of the screen in RAM once
// gfx_accelerate() has run, and reaches the display at gfx_flush().
void gfx_accelerate(void);
void gfx_flush(void);
void gfx_flush_all(void); // the crash screen: unconditionally
volatile uchar *gfx_hw_ptr(void);
int gfx_cols(void);
int gfx_rows(void);
// For the text layer in kernel/dev/vt.c: one cell with its own colours, and
// a screen scroll that leaves the cursor alone.
// One terminal cell. `cp` is a Unicode codepoint, not a byte -- see the note on
// struct vtcell in vt.h for why that distinction is the whole feature.
void gfx_draw_cell(int col, int row, uint32 cp, uint32 fg, uint32 bg);
// Bring the console typeface up, once, early. Falls back to the built-in
// bitmap and says nothing if the font will not open -- see the note in gfx.c
// on why it cannot print for itself.
void gfx_font_init(void);
int gfx_font_ready(void);
void gfx_cell_size(int *w, int *h);
// A filled rectangle in pixels, with the per-pixel checks hoisted out.
void gfx_fill(int x, int y, int w, int h, uint32 color);
void gfx_scroll_up(uint32 bg);
void gfx_cursor_get(int *col, int *row);
void gfx_cursor_set(int col, int row);
void gfx_erase_display(int mode);
void gfx_erase_line(int mode);