rumpk/hal/surface.zig

127 lines
3.4 KiB
Zig

// SPDX-License-Identifier: LCL-1.0
// Copyright (c) 2026 Markus Maiwald
// Stewardship: Self Sovereign Society Foundation
//
// This file is part of the Nexus Commonwealth.
// See legal/LICENSE_COMMONWEALTH.md for license terms.
//! Rumpk Layer 0: Surface Allocator
//!
//! Manages contiguous memory chunks for window buffers (compositor).
//! Provides a simple pool-based allocation for up to 16 surfaces.
//!
//! DECISION(Graphics): Fixed pool size of 32MB for surfaces.
//! Bump-style allocation; reclamation requires system reset.
const std = @import("std");
const uart = @import("uart.zig");
pub const MAX_SURFACES = 16;
pub const SURFACE_POOL_SIZE = 1 * 1024 * 1024; // 1MB for surfaces (temporary reduction)
// Surface Descriptor
pub const Surface = struct {
id: i32,
ptr: [*]u32,
width: u32,
height: u32,
active: bool,
};
// Global Surface Pool
// SAFETY(Surfaces): Array is initialized by hal_surface_init before use.
var surfaces: [MAX_SURFACES]Surface = undefined;
var next_surface_id: i32 = 1;
// Backing memory for surfaces (in BSS)
// SAFETY(SurfaceHeap): Memory written by alloc before any read.
// Initialized to `undefined` to avoid zeroing 32MB at boot.
var surface_heap: [SURFACE_POOL_SIZE]u8 align(4096) = undefined;
var heap_offset: usize = 0;
export fn hal_surface_init() void {
for (&surfaces) |*s| {
s.id = -1;
s.active = false;
// SAFETY(Surfaces): Pointer reset to `undefined`. Populated during allocation.
s.ptr = undefined;
s.width = 0;
s.height = 0;
}
heap_offset = 0;
uart.print("[Surface] Allocator Initialized. Pool: 32MB\n");
}
pub fn alloc(width: u32, height: u32) ?i32 {
const size = width * height * 4;
// Alignment to 4096
const aligned_size = (size + 4095) & ~@as(u32, 4095);
if (heap_offset + aligned_size > SURFACE_POOL_SIZE) {
uart.print("[Surface] ERROR: Out of Memory in Surface Pool!\n");
return null;
}
// Find free slot
var slot: ?*Surface = null;
for (&surfaces) |*s| {
if (!s.active) {
slot = s;
break;
}
}
if (slot) |s| {
s.id = next_surface_id;
next_surface_id += 1;
s.width = width;
s.height = height;
s.ptr = @ptrCast(@alignCast(&surface_heap[heap_offset]));
s.active = true;
heap_offset += aligned_size;
uart.print("[Surface] Allocated ID=");
uart.print_hex(@intCast(s.id));
uart.print(" Size=");
uart.print_hex(size);
uart.print("\n");
return s.id;
}
uart.print("[Surface] ERROR: Max Surfaces Reached!\n");
return null;
}
pub fn get_surface(id: i32) ?*Surface {
for (&surfaces) |*s| {
if (s.active and s.id == id) return s;
}
return null;
}
pub fn free(id: i32) bool {
for (&surfaces) |*s| {
if (s.active and s.id == id) {
s.active = false;
// Note: In our simple bump-style allocator, we don't reclaim heap space
// unless we implement a real allocator. For now, we assume surfaces are
// mostly permanent or the system reboots.
return true;
}
}
return false;
}
// Exported for Nim
export fn hal_surface_alloc(w: u32, h: u32) i32 {
return alloc(w, h) orelse -1;
}
export fn hal_surface_get_ptr(id: i32) ?[*]u32 {
if (get_surface(id)) |s| return s.ptr;
return null;
}