opt: lua API optimizations, buffer-based drawing, LuaJIT

This commit is contained in:
RadsammyT 2026-05-19 10:32:52 -04:00
commit e4a945f925
No known key found for this signature in database
3 changed files with 112 additions and 55 deletions

View file

@ -74,6 +74,7 @@ pub fn build(b: *std.Build) !void {
const zlua = b.dependency("zlua", .{
.target = target,
.optimize = optimize,
.lang = .luajit
});
exe.root_module.addImport("zlua", zlua.module("zlua"));

View file

@ -17,6 +17,8 @@ instance: ?Widget = null,
lua: *zlua.Lua,
log: *LogFile,
terminal_buffer: *TerminalBuffer,
width: usize,
height: usize,
io: std.Io,
lua_error: bool = false,
@ -33,53 +35,110 @@ pub fn init(
.terminal_buffer = buf,
.instance = null,
.log = log,
.width = 0,
.height = 0,
.io = io,
};
self.lua.openLibs();
// exclude IO and debug libraries
self.lua.openBase();
self.lua.openBit();
self.lua.openMath();
self.lua.openOS();
self.lua.openString();
self.lua.openTable();
self.lua.openString();
if (file) |f| {
const zf = std.mem.concatWithSentinel(alloc, u8, &[1][]const u8{f}, 0) catch unreachable;
defer alloc.free(zf);
self.lua.doString(LyLua) catch {
const errorStr = self.lua.toString(-1) catch unreachable;
self.log.err(self.io, "Lua", "Lua Error: {s}", .{errorStr}) catch unreachable;
self.lua.setGlobal("_ERROR");
self.lua_error = true;
};
self.lua.doFile(zf) catch {
const errorStr = self.lua.toString(-1) catch unreachable;
self.log.err(self.io, "Lua", "Lua Error: {s}", .{errorStr}) catch unreachable;
self.lua.setGlobal("_ERROR");
self.lua_error = true;
};
}
_ = self.lua.getGlobal("ly");
addFuncToTable(self.lua, "putCell", luaPutCell);
self.lua.setGlobal("ly");
return self;
}
fn draw(self: *Lua) void {
_ = self.lua.getGlobal("ly");
_ =self.lua.pushString("width");
self.lua.pushInteger(@intCast(self.terminal_buffer.width));
self.lua.setTable(-3);
_ =self.lua.pushString("height");
self.lua.pushInteger(@intCast(self.terminal_buffer.height));
self.lua.setTable(-3);
self.lua.setGlobal("ly");
if (self.terminal_buffer.height != self.height or
self.terminal_buffer.width != self.width) {
self.width = self.terminal_buffer.width;
self.height = self.terminal_buffer.height;
_ = self.lua.getGlobal("ly");
_ =self.lua.pushString("width");
self.lua.pushInteger(@intCast(self.terminal_buffer.width));
self.lua.setTable(-3);
_ =self.lua.pushString("height");
self.lua.pushInteger(@intCast(self.terminal_buffer.height));
self.lua.setTable(-3);
self.lua.setGlobal("ly");
}
if (!self.lua_error) {
self.lua.doString(
"draw()"
) catch {
_ = self.lua.getGlobal("draw");
self.lua.protectedCall(.{}) catch {
const errorStr = self.lua.toString(-1) catch unreachable;
self.log.err(self.io, "Lua", "Lua Error: {s}", .{errorStr}) catch unreachable;
self.lua.setGlobal("_ERROR");
self.log.err(self.io, "Lua", "Error (Cannot call draw()): {s}", .{errorStr}) catch unreachable;
self.lua_error = true;
};
const bufferType = self.lua.getGlobal("_BUFFER");
if (bufferType != .table) {
self.log.err(self.io, "Lua", "global '_BUFFER' must be a table: got {}", .{bufferType})
catch {};
self.lua_error = true;
return;
}
self.lua.pushNil();
while (self.lua.next(-2)) {
// expect the key to be an int
// expect the value to be a table
const pos = self.lua.toInteger(-2) catch {
self.log.err(self.io, "Lua", "pos: cannot convert to integer", .{})
catch {};
self.lua_error = true;
return;
};
_ = self.lua.pushString("char");
_ = self.lua.getTable(-2);
const char = self.lua.toInteger(-1) catch {
self.log.err(self.io, "Lua", "char: cannot convert to integer", .{})
catch {};
self.lua_error = true;
return;
};
self.lua.pop(1);
_ = self.lua.pushString("fg");
_ = self.lua.getTable(-2);
const fg = self.lua.toInteger(-1) catch {
self.log.err(self.io, "Lua", "fg: cannot convert to integer", .{})
catch {};
self.lua_error = true;
return;
};
self.lua.pop(1);
_ = self.lua.pushString("bg");
_ = self.lua.getTable(-2);
const bg = self.lua.toInteger(-1) catch {
self.log.err(self.io, "Lua", "bg: cannot convert to integer", .{})
catch {};
self.lua_error = true;
return;
};
self.lua.pop(1);
self.lua.pop(1);
if (pos < 0) continue; // do not draw cells that underflow
Cell.init(@intCast(char), @intCast(fg), @intCast(bg))
.put(@rem(@as(usize, @intCast(pos)), self.width), @divFloor(@as(usize, @bitCast(pos)), self.width)) catch {};
}
} else {
// set the entire background to red
// Ly's Red Screen of Omega-Death:tm:
const cell = Cell.init(0x2588, 0x00ff0000, 0x00ff0000);
for (0..self.terminal_buffer.height) |y|
for (0..self.terminal_buffer.width) |x|
@ -88,13 +147,24 @@ fn draw(self: *Lua) void {
}
fn calculateTimeout(self: *Lua, _: *anyopaque) !?usize {
if (self.lua_error) return null;
const lyType = self.lua.getGlobal("ly"); // +1
_ = self.lua.pushString("frame_delay"); // + 1
if (lyType != .table)
std.debug.panic("Lua: global 'ly' must be a table: got {}", .{lyType});
if (lyType != .table) {
try self.log.err(self.io, "Lua", "global 'ly' must be a table: got {}", .{lyType});
self.lua_error = true;
self.lua.pop(-1);
self.lua.pop(-1);
return null;
}
const frameDelayType = self.lua.getTable(-2); // neut
if (frameDelayType != .number)
std.debug.panic("Lua: 'ly.frame_delay' must be a number: got {}", .{frameDelayType});
if (frameDelayType != .number) {
try self.log.err(self.io, "Lua", "'ly.frame_delay' must be a number: got {}", .{lyType});
self.lua_error = true;
self.lua.pop(-1);
self.lua.pop(-1);
return null;
}
const timeout = try self.lua.toNumeric(usize, -1);
self.lua.pop(-1); // pop number from stack
self.lua.pop(-1); // pop table 'ly' from stack
@ -120,32 +190,3 @@ pub fn widget(self: *Lua) *Widget {
);
return &self.instance.?;
}
fn luaPutCell(lua: ?*zlua.LuaState) callconv(.c) c_int {
var vm = @as(*zlua.Lua, @ptrCast(lua));
const cell = vm.checkNumeric(u32, 1);
const fg = vm.checkNumeric(u32, 2);
const bg = vm.checkNumeric(u32, 3);
const x = vm.checkNumeric(usize, 4);
const y = vm.checkNumeric(usize, 5);
Cell.init(cell, fg, bg).put(x, y) catch {};
return 0; // This function doesnt return anything
}
/// Adds member function `name` to table at the top of the stack
///
/// Pushes: 0
/// Pops: 0, this function can be called continuously
/// Errors: unknown
fn addFuncToTable(
vm: *zlua.Lua,
comptime name: []const u8,
func: zlua.CFn,
) void
{
_ = vm.pushString(name);
vm.pushFunction(func);
vm.setTable(-3);
}

View file

@ -1,3 +1,18 @@
ly = {
frame_delay = 0,
width = 0,
height = 0,
}
_BUFFER = {}
function ly.putCell(byte, fg, bg, x, y)
_BUFFER[x + (y * ly.width)] = {
char = math.floor(byte),
fg = math.floor(fg),
bg = math.floor(bg),
}
end
function ly.clear()
_BUFFER = {}
end