Compare commits
No commits in common. "908815b5851a364ce187209a6397840a9f3eb9ae" and "84bf37cd89f8a0dc41e8efdc04e93bbd787a4dcd" have entirely different histories.
908815b585
...
84bf37cd89
6 changed files with 4 additions and 102 deletions
26
CLAUDE.md
26
CLAUDE.md
|
|
@ -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
|
## NOTAS ZIG 0.15.2
|
||||||
|
|
||||||
```zig
|
```zig
|
||||||
|
|
|
||||||
|
|
@ -102,10 +102,6 @@ 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,
|
||||||
|
|
||||||
|
|
@ -149,10 +145,8 @@ pub const Context = struct {
|
||||||
|
|
||||||
/// Frame statistics for performance monitoring
|
/// Frame statistics for performance monitoring
|
||||||
pub const FrameStats = struct {
|
pub const FrameStats = struct {
|
||||||
/// Number of commands generated this frame
|
/// Number of commands this frame
|
||||||
command_count: usize = 0,
|
command_count: usize = 0,
|
||||||
/// Number of commands actually executed (set by renderer)
|
|
||||||
executed_cmds: usize = 0,
|
|
||||||
/// Number of widgets drawn
|
/// Number of widgets drawn
|
||||||
widget_count: usize = 0,
|
widget_count: usize = 0,
|
||||||
/// Arena bytes used this frame
|
/// Arena bytes used this frame
|
||||||
|
|
@ -233,7 +227,6 @@ pub const Context = struct {
|
||||||
|
|
||||||
// Reset frame stats
|
// Reset frame stats
|
||||||
self.stats.command_count = 0;
|
self.stats.command_count = 0;
|
||||||
self.stats.executed_cmds = 0;
|
|
||||||
self.stats.widget_count = 0;
|
self.stats.widget_count = 0;
|
||||||
self.stats.arena_bytes = 0;
|
self.stats.arena_bytes = 0;
|
||||||
self.stats.dirty_rect_count = 0;
|
self.stats.dirty_rect_count = 0;
|
||||||
|
|
@ -672,24 +665,10 @@ 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 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:
|
/// Supports two modes:
|
||||||
/// - **Explicit**: Provide focus_bg, unfocus_bg, border_color directly
|
/// - **Explicit**: Provide focus_bg, unfocus_bg, border_color directly
|
||||||
|
|
@ -699,11 +678,7 @@ pub const Context = struct {
|
||||||
rect: Layout.Rect,
|
rect: Layout.Rect,
|
||||||
transition: *ColorTransition,
|
transition: *ColorTransition,
|
||||||
config: PanelFrameConfig,
|
config: PanelFrameConfig,
|
||||||
) PanelFrameResult {
|
) bool {
|
||||||
// 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| {
|
||||||
|
|
@ -789,8 +764,7 @@ pub const Context = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// should_draw = false indica que el panel debe saltar operaciones costosas (BD, widgets)
|
return animating;
|
||||||
return .{ .animating = animating, .should_draw = !burst_suppressed };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resize the context
|
/// 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).
|
/// 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 {
|
||||||
|
|
|
||||||
|
|
@ -739,19 +739,6 @@ pub const ColorTransition = struct {
|
||||||
.a = lerpU8(self.current.a, target.a, t),
|
.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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,6 @@ 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)));
|
||||||
|
|
|
||||||
|
|
@ -323,9 +323,6 @@ pub const SoftwareRenderer = struct {
|
||||||
/// Shadow cache for instant shadow blitting (optional, requires allocator)
|
/// Shadow cache for instant shadow blitting (optional, requires allocator)
|
||||||
shadow_cache: ?ShadowCache = null,
|
shadow_cache: ?ShadowCache = null,
|
||||||
|
|
||||||
/// Counter of actually executed commands (for telemetry)
|
|
||||||
executed_count: usize = 0,
|
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
/// Initialize the renderer (without shadow cache)
|
/// Initialize the renderer (without shadow cache)
|
||||||
|
|
@ -385,7 +382,6 @@ pub const SoftwareRenderer = struct {
|
||||||
|
|
||||||
/// Execute a single draw command
|
/// Execute a single draw command
|
||||||
pub fn execute(self: *Self, cmd: DrawCommand) void {
|
pub fn execute(self: *Self, cmd: DrawCommand) void {
|
||||||
self.executed_count += 1;
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
.rect => |r| self.drawRect(r),
|
.rect => |r| self.drawRect(r),
|
||||||
.rounded_rect => |r| self.drawRoundedRect(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
|
/// Execute all commands in a list
|
||||||
pub fn executeAll(self: *Self, commands: []const DrawCommand) void {
|
pub fn executeAll(self: *Self, commands: []const DrawCommand) void {
|
||||||
for (commands) |cmd| {
|
for (commands) |cmd| {
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,6 @@ 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;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue