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. /// Set by application for non-dirty panels to save CPU while keeping input working.
suppress_commands: bool = false, 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 /// Frame statistics
stats: FrameStats, stats: FrameStats,
@ -665,10 +669,24 @@ pub const Context = struct {
draw_shadow: bool = true, draw_shadow: bool = true,
/// Draw bevel effect (default true) /// Draw bevel effect (default true)
draw_bevel: bool = 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. /// 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: /// Supports two modes:
/// - **Explicit**: Provide focus_bg, unfocus_bg, border_color directly /// - **Explicit**: Provide focus_bg, unfocus_bg, border_color directly
@ -678,7 +696,11 @@ pub const Context = struct {
rect: Layout.Rect, rect: Layout.Rect,
transition: *ColorTransition, transition: *ColorTransition,
config: PanelFrameConfig, 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 // Determine colors: Z-Design derivation or explicit
const focus_bg: Style.Color = blk: { const focus_bg: Style.Color = blk: {
if (config.base_color) |base| { 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 /// 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). /// Get rectangles of all dirty panels (for renderer).
/// Returns slice allocated from frame arena (valid until next beginFrame). /// Returns slice allocated from frame arena (valid until next beginFrame).
pub fn getDirtyPanelRects(self: *Self) []const Layout.Rect { pub fn getDirtyPanelRects(self: *Self) []const Layout.Rect {

View file

@ -114,6 +114,7 @@ pub const Framebuffer = struct {
/// Draw a filled rectangle /// Draw a filled rectangle
/// Optimized with SIMD-friendly @memset for solid colors (alpha=255) /// 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 { 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 x_start = @max(0, x);
const y_start = @max(0, y); const y_start = @max(0, y);
const x_end = @min(@as(i32, @intCast(self.width)), x + @as(i32, @intCast(w))); 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, color: Color,
clip: Rect, clip: Rect,
) void { ) void {
@setRuntimeSafety(false); // Hot path: bounds validated in visible region calculation
_ = self; _ = self;
const width: u32 = glyph.metrics.width; const width: u32 = glyph.metrics.width;