Compare commits

..

No commits in common. "908815b5851a364ce187209a6397840a9f3eb9ae" and "84bf37cd89f8a0dc41e8efdc04e93bbd787a4dcd" have entirely different histories.

6 changed files with 4 additions and 102 deletions

View file

@ -367,32 +367,6 @@ pub const MacroPlayer = struct {
---
## OPTIMIZACIONES DE RENDIMIENTO (2026-01-03)
### Optimizaciones Activas ✅
| Optimización | Archivo | Descripción |
|--------------|---------|-------------|
| **Turbo Píxeles** | `render/framebuffer.zig:117` | `@setRuntimeSafety(false)` en `fillRect` - elimina bounds checks en hot path |
| **Fast Path Texto** | `render/ttf.zig:323,375` | Escritura directa para píxeles opacos (alpha==255), evita blend innecesario |
### Optimización Probada y Revertida ❌
| Optimización | Problema | Alternativa |
|--------------|----------|-------------|
| **Burst Suppression** (auto-gestión de estrés) | Causaba paneles vacíos durante navegación rápida | El debounce en DataManager ya evita queries excesivas |
**Detalles del Burst Suppression:**
- Concepto: suprimir dibujo de paneles durante ráfagas de navegación (<100ms)
- Implementación: `if (!frame_result.should_draw) return;` en paneles
- Problema: el frame se dibujaba vacío (solo fondo), sin widgets
- Causa raíz: suprimía TODO el dibujo, no solo las queries BD
- Lección: el debounce a nivel DataManager es más elegante
**Pendiente verificar:** Mensajes continuos de redraw en idle (posible bug en animaciones).
---
## NOTAS ZIG 0.15.2
```zig

View file

@ -102,10 +102,6 @@ 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,
@ -149,10 +145,8 @@ pub const Context = struct {
/// Frame statistics for performance monitoring
pub const FrameStats = struct {
/// Number of commands generated this frame
/// Number of commands this frame
command_count: usize = 0,
/// Number of commands actually executed (set by renderer)
executed_cmds: usize = 0,
/// Number of widgets drawn
widget_count: usize = 0,
/// Arena bytes used this frame
@ -233,7 +227,6 @@ pub const Context = struct {
// Reset frame stats
self.stats.command_count = 0;
self.stats.executed_cmds = 0;
self.stats.widget_count = 0;
self.stats.arena_bytes = 0;
self.stats.dirty_rect_count = 0;
@ -672,24 +665,10 @@ 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 PanelFrameResult: check should_draw to know if panel content should be rendered.
/// Returns true if the transition is still animating (need more frames).
///
/// Supports two modes:
/// - **Explicit**: Provide focus_bg, unfocus_bg, border_color directly
@ -699,11 +678,7 @@ pub const Context = struct {
rect: Layout.Rect,
transition: *ColorTransition,
config: PanelFrameConfig,
) 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();
) bool {
// Determine colors: Z-Design derivation or explicit
const focus_bg: Style.Color = blk: {
if (config.base_color) |base| {
@ -789,8 +764,7 @@ pub const Context = struct {
}
}
// should_draw = false indica que el panel debe saltar operaciones costosas (BD, widgets)
return .{ .animating = animating, .should_draw = !burst_suppressed };
return animating;
}
/// Resize the context
@ -863,23 +837,6 @@ 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

@ -739,19 +739,6 @@ pub const ColorTransition = struct {
.a = lerpU8(self.current.a, target.a, t),
};
// EPSILON FIX: Forzar convergencia cuando estamos "suficientemente cerca"
// Sin esto, lerpU8 trunca incrementos <1 y la animación nunca termina.
// Umbral de 1 es imperceptible al ojo pero garantiza convergencia.
const dr = @abs(@as(i16, self.current.r) - @as(i16, target.r));
const dg = @abs(@as(i16, self.current.g) - @as(i16, target.g));
const db = @abs(@as(i16, self.current.b) - @as(i16, target.b));
const da = @abs(@as(i16, self.current.a) - @as(i16, target.a));
if (dr <= 1 and dg <= 1 and db <= 1 and da <= 1) {
self.current = target; // Snap al target
return false; // Animación terminada
}
return true;
}

View file

@ -114,7 +114,6 @@ 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

@ -323,9 +323,6 @@ pub const SoftwareRenderer = struct {
/// Shadow cache for instant shadow blitting (optional, requires allocator)
shadow_cache: ?ShadowCache = null,
/// Counter of actually executed commands (for telemetry)
executed_count: usize = 0,
const Self = @This();
/// Initialize the renderer (without shadow cache)
@ -385,7 +382,6 @@ pub const SoftwareRenderer = struct {
/// Execute a single draw command
pub fn execute(self: *Self, cmd: DrawCommand) void {
self.executed_count += 1;
switch (cmd) {
.rect => |r| self.drawRect(r),
.rounded_rect => |r| self.drawRoundedRect(r),
@ -403,16 +399,6 @@ pub const SoftwareRenderer = struct {
}
}
/// Reset executed command counter (call at start of frame)
pub fn resetExecutedCount(self: *Self) void {
self.executed_count = 0;
}
/// Get number of commands executed since last reset
pub fn getExecutedCount(self: Self) usize {
return self.executed_count;
}
/// Execute all commands in a list
pub fn executeAll(self: *Self, commands: []const DrawCommand) void {
for (commands) |cmd| {

View file

@ -320,7 +320,6 @@ 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;