perf(gui): scene-dirty compositor + cursor overlay + 60 FPS pacer + animations

Rework the compositor so it stops repainting the whole screen every frame,
tripling the frame rate (17 -> 54 FPS at HD) and adding smooth animation.

- Scene-dirty skipping (gui/gui_desktop.c): the full scene is recomposited only
  when something other than the bare pointer changed -- input, an open animation,
  or a ~10 Hz heartbeat for live content (clock, graphs). Idle and pointer-only
  frames skip the recomposite entirely.
- Cursor save-under (gui/gui_cursor.c): the pointer is an overlay that stashes
  and restores the pixels beneath it, so moving the mouse never forces a full
  repaint -- pointer motion runs at the frame cap.
- 60 FPS frame pacer: each iteration is timed against the monotonic clock and
  padded to a 16.666 ms budget with the new ktime_delay_us() (HPET-precise
  busy-wait), giving consistent pacing instead of a free-running spin.
- Window open animation (gui/gui_wm.c): new windows grow from 60% to full size
  with a cubic ease-out over ~160 ms; gui_wm_update() drives it and reports
  while any window is still animating so the compositor keeps redrawing.
- Default display mode set to 1280x720 (HD) -- the sweet spot where the
  software compositor sustains a near-60 FPS cursor with headroom for effects.

Kernel: kernel/time/ktime.c gains ktime_delay_us(), a precise HPET-based delay
(bounded PIT fallback) used by the pacer.

Verified live (screenshotted): boots to 1280x720, logs in, tray reads ~54 FPS
(green) while the pointer moves, no cursor ghosting, windows render cleanly.
Clean build on x86_64/aarch64/armv8i; 0 panics / 0 OOM.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fh5z6V7nGPbHvqVG5p4CXX
This commit is contained in:
subhobhai943andClaude Opus 4.8 committed 2026-08-25 23:05:44 +05:30
1 parent 4ecf119377
commit 2c2e2d536e
8 files changed
+200 -13

No files matched your search

+48
View File
@@ -81,3 +81,51 @@ void gui_cursor_draw(void) {
}
}
}
// ---------------------------------------------------------------------------
// Save-under overlay: lets the compositor move the cursor without repainting
// the whole scene. The block under the cursor (body + drop shadow) is stashed
// before drawing and restored on the next frame.
// ---------------------------------------------------------------------------
#define CUR_SAVE_W (CURSOR_W + 3)
#define CUR_SAVE_H (CURSOR_H + 3)
static uint32_t g_save[CUR_SAVE_H * CUR_SAVE_W];
static int g_save_x = -1, g_save_y = -1, g_save_w = 0, g_save_h = 0;
// Put back whatever was under the cursor last frame (erasing it).
void gui_cursor_restore_under(void) {
if (g_save_x < 0) return;
uint32_t* bb = gui_gfx_get_backbuffer();
if (!bb) { g_save_x = -1; return; }
int W = gui_gfx_get_width();
for (int j = 0; j < g_save_h; j++) {
uint32_t* dst = &bb[(g_save_y + j) * W + g_save_x];
const uint32_t* src = &g_save[j * g_save_w];
for (int i = 0; i < g_save_w; i++) dst[i] = src[i];
}
g_save_x = -1;
}
// Stash the block under the current cursor position, then draw the cursor.
void gui_cursor_composite(void) {
uint32_t* bb = gui_gfx_get_backbuffer();
if (!bb) return;
int W = gui_gfx_get_width();
int H = gui_gfx_get_height();
int x = g_cur_x, y = g_cur_y;
int w = CUR_SAVE_W, h = CUR_SAVE_H;
if (x + w > W) w = W - x;
if (y + h > H) h = H - y;
if (w <= 0 || h <= 0) { g_save_x = -1; gui_cursor_draw(); return; }
for (int j = 0; j < h; j++) {
const uint32_t* srow = &bb[(y + j) * W + x];
uint32_t* d = &g_save[j * w];
for (int i = 0; i < w; i++) d[i] = srow[i];
}
g_save_x = x; g_save_y = y; g_save_w = w; g_save_h = h;
gui_cursor_draw();
}