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

+57
View File
@@ -10,6 +10,9 @@
#include <drivers/keyboard.h>
#include <lib/string.h>
#include <mm/kmalloc.h>
#include <kernel/ktime.h>
#define WM_OPEN_ANIM_MS 160
#define BTN_SIZE 14
#define BTN_GAP 4
@@ -85,9 +88,63 @@ gui_window_t* gui_wm_create_window(const char* title, int x, int y, int w, int h
g_window_order[g_num_windows++] = win->id;
gui_wm_focus_window(win->id);
// Kick off the grow-open animation: remember the final geometry and centre,
// then start small. gui_wm_update() eases it back to full over ~160 ms.
win->target_w = win->width;
win->target_h = win->height;
win->center_x = win->x + win->width / 2;
win->center_y = win->y + win->height / 2;
win->anim_open_ns = ktime_ns();
win->width = win->target_w * 6 / 10;
win->height = win->target_h * 6 / 10;
win->x = win->center_x - win->width / 2;
win->y = win->center_y - win->height / 2;
if (win->x < 0) win->x = 0;
if (win->y < 0) win->y = 0;
return win;
}
// Advance open animations. Returns true while any window is still animating.
bool gui_wm_update(void) {
bool animating = false;
uint64_t now = ktime_ns();
for (int i = 0; i < g_num_windows; i++) {
gui_window_t* win = gui_wm_get_window(g_window_order[i]);
if (!win || win->anim_open_ns == 0) continue;
uint64_t elapsed_ms = (now - win->anim_open_ns) / 1000000ULL;
if (elapsed_ms >= WM_OPEN_ANIM_MS) {
// Snap to the final geometry and stop animating.
win->width = win->target_w;
win->height = win->target_h;
win->x = win->center_x - win->target_w / 2;
win->y = win->center_y - win->target_h / 2;
win->anim_open_ns = 0;
} else {
animating = true;
int p = (int)(elapsed_ms * 1000 / WM_OPEN_ANIM_MS); // 0..1000
int inv = 1000 - p;
int ease = 1000 - (inv * inv / 1000) * inv / 1000; // cubic ease-out
int scale = 600 + 400 * ease / 1000; // 600..1000 permille
win->width = win->target_w * scale / 1000;
win->height = win->target_h * scale / 1000;
win->x = win->center_x - win->width / 2;
win->y = win->center_y - win->height / 2;
}
// Keep the animating window on screen.
if (win->x < 0) win->x = 0;
if (win->y < 0) win->y = 0;
if (win->x + win->width > g_work_w) win->x = g_work_w - win->width;
if (win->y + win->height > g_work_h) win->y = g_work_h - win->height;
if (win->x < 0) win->x = 0;
if (win->y < 0) win->y = 0;
}
return animating;
}
static int order_index_of(int win_id) {
for (int i = 0; i < g_num_windows; i++) {
if (g_window_order[i] == win_id) return i;