Compare commits
No commits in common. "15b9cf47a77d9d7d95285ef5186c7e468c20a49e" and "4b7069b0762f8bec791223c11ed30099d7f89e10" have entirely different histories.
15b9cf47a7
...
4b7069b076
2 changed files with 3 additions and 148 deletions
|
|
@ -48,9 +48,10 @@ pub const Sdl2Backend = struct {
|
||||||
errdefer c.SDL_DestroyWindow(window);
|
errdefer c.SDL_DestroyWindow(window);
|
||||||
|
|
||||||
// Create renderer with hardware acceleration and VSync
|
// Create renderer with hardware acceleration and VSync
|
||||||
// VSync syncs with monitor refresh rate (typically 60Hz):
|
// VSync syncs with monitor refresh rate (typically 60Hz) which:
|
||||||
// - Eliminates screen tearing
|
// - Eliminates screen tearing
|
||||||
// - Reduces CPU/GPU usage
|
// - Reduces CPU usage (no busy-wait needed)
|
||||||
|
// - Provides consistent frame timing
|
||||||
// Falls back to software renderer if GPU not available
|
// Falls back to software renderer if GPU not available
|
||||||
const renderer = c.SDL_CreateRenderer(
|
const renderer = c.SDL_CreateRenderer(
|
||||||
window,
|
window,
|
||||||
|
|
|
||||||
|
|
@ -87,16 +87,6 @@ pub const Context = struct {
|
||||||
/// Whether the entire screen needs redraw
|
/// Whether the entire screen needs redraw
|
||||||
full_redraw: bool,
|
full_redraw: bool,
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// Dirty Panel System (granularity per named panel)
|
|
||||||
// =========================================================================
|
|
||||||
|
|
||||||
/// Registered panel areas by name (e.g., "who_list", "doc_detail")
|
|
||||||
panel_areas: std.StringHashMapUnmanaged(Layout.Rect),
|
|
||||||
|
|
||||||
/// Dirty flags per panel (true = needs redraw)
|
|
||||||
dirty_panels: std.StringHashMapUnmanaged(bool),
|
|
||||||
|
|
||||||
/// Frame statistics
|
/// Frame statistics
|
||||||
stats: FrameStats,
|
stats: FrameStats,
|
||||||
|
|
||||||
|
|
@ -167,8 +157,6 @@ pub const Context = struct {
|
||||||
.height = height,
|
.height = height,
|
||||||
.dirty_rects = .{},
|
.dirty_rects = .{},
|
||||||
.full_redraw = true,
|
.full_redraw = true,
|
||||||
.panel_areas = .{},
|
|
||||||
.dirty_panels = .{},
|
|
||||||
.stats = .{},
|
.stats = .{},
|
||||||
.focus = FocusSystem.init(),
|
.focus = FocusSystem.init(),
|
||||||
};
|
};
|
||||||
|
|
@ -189,8 +177,6 @@ pub const Context = struct {
|
||||||
.height = height,
|
.height = height,
|
||||||
.dirty_rects = .{},
|
.dirty_rects = .{},
|
||||||
.full_redraw = true,
|
.full_redraw = true,
|
||||||
.panel_areas = .{},
|
|
||||||
.dirty_panels = .{},
|
|
||||||
.stats = .{},
|
.stats = .{},
|
||||||
.focus = FocusSystem.init(),
|
.focus = FocusSystem.init(),
|
||||||
};
|
};
|
||||||
|
|
@ -202,8 +188,6 @@ pub const Context = struct {
|
||||||
self.overlay_commands.deinit(self.allocator);
|
self.overlay_commands.deinit(self.allocator);
|
||||||
self.id_stack.deinit(self.allocator);
|
self.id_stack.deinit(self.allocator);
|
||||||
self.dirty_rects.deinit(self.allocator);
|
self.dirty_rects.deinit(self.allocator);
|
||||||
self.panel_areas.deinit(self.allocator);
|
|
||||||
self.dirty_panels.deinit(self.allocator);
|
|
||||||
self.frame_arena.deinit();
|
self.frame_arena.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,10 +238,6 @@ pub const Context = struct {
|
||||||
|
|
||||||
// Reset full_redraw for next frame
|
// Reset full_redraw for next frame
|
||||||
self.full_redraw = false;
|
self.full_redraw = false;
|
||||||
|
|
||||||
// Clear dirty panel flags AFTER rendering
|
|
||||||
// (renderer uses getDirtyPanelRects during frame)
|
|
||||||
self.clearDirtyPanels();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
@ -763,132 +743,6 @@ pub const Context = struct {
|
||||||
self.width = width;
|
self.width = width;
|
||||||
self.height = height;
|
self.height = height;
|
||||||
self.invalidateAll();
|
self.invalidateAll();
|
||||||
self.markAllPanelsDirty(); // Resize requires all panels to redraw
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// Dirty Panel System (granularity per named panel)
|
|
||||||
//
|
|
||||||
// The application registers panel areas at startup:
|
|
||||||
// ctx.registerPanelArea("who_list", rect);
|
|
||||||
//
|
|
||||||
// When data changes, the application marks panels dirty:
|
|
||||||
// ctx.invalidatePanel("who_list");
|
|
||||||
//
|
|
||||||
// The renderer checks which panels are dirty:
|
|
||||||
// const dirty_rects = ctx.getDirtyPanelRects();
|
|
||||||
// renderer.clearDirtyRegions(dirty_rects);
|
|
||||||
//
|
|
||||||
// At endFrame(), dirty flags are cleared automatically.
|
|
||||||
// =========================================================================
|
|
||||||
|
|
||||||
/// Register a named panel area. Call at startup or when layout changes.
|
|
||||||
/// The ID should be descriptive (e.g., "who_list", "doc_detail").
|
|
||||||
pub fn registerPanelArea(self: *Self, id: []const u8, rect: Layout.Rect) void {
|
|
||||||
self.panel_areas.put(self.allocator, id, rect) catch {
|
|
||||||
// If we can't register, fall back to full redraw
|
|
||||||
self.full_redraw = true;
|
|
||||||
};
|
|
||||||
// New panels start dirty (need initial draw)
|
|
||||||
self.dirty_panels.put(self.allocator, id, true) catch {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update a panel's rect (e.g., after window resize).
|
|
||||||
/// Does NOT mark the panel dirty - call invalidatePanel separately if needed.
|
|
||||||
pub fn updatePanelArea(self: *Self, id: []const u8, rect: Layout.Rect) void {
|
|
||||||
if (self.panel_areas.getPtr(id)) |ptr| {
|
|
||||||
ptr.* = rect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark a panel as needing redraw.
|
|
||||||
/// Call this when data changes that affects the panel's content.
|
|
||||||
pub fn invalidatePanel(self: *Self, id: []const u8) void {
|
|
||||||
if (self.dirty_panels.getPtr(id)) |ptr| {
|
|
||||||
ptr.* = true;
|
|
||||||
} else {
|
|
||||||
// Panel not registered - this is probably a bug
|
|
||||||
// For safety, do full redraw
|
|
||||||
self.full_redraw = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if a panel is marked dirty.
|
|
||||||
pub fn isPanelDirty(self: *Self, id: []const u8) bool {
|
|
||||||
if (self.full_redraw) return true;
|
|
||||||
return self.dirty_panels.get(id) orelse false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark all registered panels as dirty.
|
|
||||||
/// Use for resize, tab change, or other global invalidation.
|
|
||||||
pub fn markAllPanelsDirty(self: *Self) void {
|
|
||||||
var iter = self.dirty_panels.iterator();
|
|
||||||
while (iter.next()) |entry| {
|
|
||||||
entry.value_ptr.* = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 {
|
|
||||||
if (self.full_redraw) {
|
|
||||||
// Return single rect covering entire screen
|
|
||||||
const full = Layout.Rect{
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
.w = self.width,
|
|
||||||
.h = self.height,
|
|
||||||
};
|
|
||||||
const result = self.frame_arena.alloc_slice(Layout.Rect, 1) orelse return &.{};
|
|
||||||
result[0] = full;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count dirty panels
|
|
||||||
var dirty_count: usize = 0;
|
|
||||||
var iter = self.dirty_panels.iterator();
|
|
||||||
while (iter.next()) |entry| {
|
|
||||||
if (entry.value_ptr.*) dirty_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dirty_count == 0) return &.{};
|
|
||||||
|
|
||||||
// Allocate result array from frame arena
|
|
||||||
const result = self.frame_arena.alloc_slice(Layout.Rect, dirty_count) orelse return &.{};
|
|
||||||
|
|
||||||
// Fill with dirty panel rects
|
|
||||||
var i: usize = 0;
|
|
||||||
var iter2 = self.dirty_panels.iterator();
|
|
||||||
while (iter2.next()) |entry| {
|
|
||||||
if (entry.value_ptr.*) {
|
|
||||||
if (self.panel_areas.get(entry.key_ptr.*)) |rect| {
|
|
||||||
result[i] = rect;
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result[0..i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if any panel is dirty (useful for skip-redraw optimization).
|
|
||||||
pub fn hasAnyDirtyPanel(self: *Self) bool {
|
|
||||||
if (self.full_redraw) return true;
|
|
||||||
|
|
||||||
var iter = self.dirty_panels.iterator();
|
|
||||||
while (iter.next()) |entry| {
|
|
||||||
if (entry.value_ptr.*) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clear all dirty panel flags.
|
|
||||||
/// Called automatically at endFrame(), but can be called manually if needed.
|
|
||||||
pub fn clearDirtyPanels(self: *Self) void {
|
|
||||||
var iter = self.dirty_panels.iterator();
|
|
||||||
while (iter.next()) |entry| {
|
|
||||||
entry.value_ptr.* = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue