perf: @setRuntimeSafety(false) + burst detection infrastructure

- fillRect: disable runtime safety for hot path (bounds pre-validated)
- drawGlyphBitmap: disable runtime safety for hot path
- Context: add burst detection (isSelectionBurstActive, markNavigationEvent)
- PanelFrameConfig: add burst_sensitive field
- PanelFrameResult: new struct for frame drawing results

Note: burst_sensitive mechanism available but not actively used
(causes visual flickering when suppressing panel content)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
R.Eugenio 2026-01-02 18:27:10 +01:00
parent 84bf37cd89
commit 4ce39c4fc8
3 changed files with 45 additions and 3 deletions

View file

@ -102,6 +102,10 @@ pub const Context = struct {
/// Set by application for non-dirty panels to save CPU while keeping input working.
suppress_commands: bool = false,
/// Burst detection: timestamp of last navigation event (selection change)
/// Used by drawPanelFrame to auto-suppress burst_sensitive panels during rapid navigation
last_navigation_time: i64 = 0,
/// Frame statistics
stats: FrameStats,
@ -665,10 +669,24 @@ pub const Context = struct {
draw_shadow: bool = true,
/// Draw bevel effect (default true)
draw_bevel: bool = true,
// === Performance ===
/// Si true, el panel se auto-suprime durante ráfagas de navegación.
/// Paneles principales (listas, fichas) deben ser false.
/// Paneles secundarios (documentos) pueden ser true para mejor rendimiento.
burst_sensitive: bool = true,
};
/// Result of drawPanelFrame for LEGO-style control flow
pub const PanelFrameResult = struct {
/// True if color transition is still animating
animating: bool,
/// False if panel was suppressed - caller should return immediately
should_draw: bool,
};
/// Draw a complete panel frame with focus transition and 3D effects.
/// Returns true if the transition is still animating (need more frames).
/// Returns PanelFrameResult: check should_draw to know if panel content should be rendered.
///
/// Supports two modes:
/// - **Explicit**: Provide focus_bg, unfocus_bg, border_color directly
@ -678,7 +696,11 @@ pub const Context = struct {
rect: Layout.Rect,
transition: *ColorTransition,
config: PanelFrameConfig,
) bool {
) PanelFrameResult {
// Auto-supresión LEGO: detectar si el panel debe suprimir operaciones costosas
// El frame SIEMPRE se dibuja, pero should_draw indica si continuar con widgets/BD
const burst_suppressed = config.burst_sensitive and self.isSelectionBurstActive();
// Determine colors: Z-Design derivation or explicit
const focus_bg: Style.Color = blk: {
if (config.base_color) |base| {
@ -764,7 +786,8 @@ pub const Context = struct {
}
}
return animating;
// should_draw = false indica que el panel debe saltar operaciones costosas (BD, widgets)
return .{ .animating = animating, .should_draw = !burst_suppressed };
}
/// Resize the context
@ -837,6 +860,23 @@ pub const Context = struct {
}
}
// =========================================================================
// BURST DETECTION: Para auto-supresión de paneles durante navegación rápida
// =========================================================================
/// Marca que ocurrió un evento de navegación (cambio de selección).
/// Llamar desde DataManager cuando notifica cambios de selección.
pub fn markNavigationEvent(self: *Self) void {
self.last_navigation_time = std.time.milliTimestamp();
}
/// Devuelve true si estamos en medio de una ráfaga de navegación.
/// Se considera ráfaga si pasaron menos de 100ms desde el último evento.
pub fn isSelectionBurstActive(self: *Self) bool {
const now = std.time.milliTimestamp();
return (now - self.last_navigation_time) < 100;
}
/// Get rectangles of all dirty panels (for renderer).
/// Returns slice allocated from frame arena (valid until next beginFrame).
pub fn getDirtyPanelRects(self: *Self) []const Layout.Rect {

View file

@ -114,6 +114,7 @@ pub const Framebuffer = struct {
/// Draw a filled rectangle
/// Optimized with SIMD-friendly @memset for solid colors (alpha=255)
pub fn fillRect(self: *Self, x: i32, y: i32, w: u32, h: u32, color: Color) void {
@setRuntimeSafety(false); // Hot path: bounds already validated below
const x_start = @max(0, x);
const y_start = @max(0, y);
const x_end = @min(@as(i32, @intCast(self.width)), x + @as(i32, @intCast(w)));

View file

@ -320,6 +320,7 @@ pub const TtfFont = struct {
color: Color,
clip: Rect,
) void {
@setRuntimeSafety(false); // Hot path: bounds validated in visible region calculation
_ = self;
const width: u32 = glyph.metrics.width;