review pt 2 + BIG performance improvement

This commit is contained in:
RadsammyT 2026-06-16 22:20:45 -04:00
commit 3c56a883c6
No known key found for this signature in database
3 changed files with 79 additions and 133 deletions

View file

@ -1,17 +1,19 @@
-- [[
-- This is an example of using LuaJIT to create a custom animation in Ly, in this case a
-- bouncing square that changes colors.
-- This is an example of using LuaJIT to create a custom animation in Ly, in this case
-- bouncing squares that change colors.
--
-- You are given the following `ly` table:
-- {
-- height: number -- The height of the terminal
-- width: number -- The width of the terminal
-- putCell(byte, fg, bg, x, y) -- Put the cell a buffer. This buffer will be drawn
-- on screen after draw() is called. It will not clear
-- itself automatically, see `ly.clear()`.
-- All arguments to this function are numbers, and
-- must be in the unsigned 32-bit integer range: 0 to 2^32-1.
-- clear() -- Clear the buffer.
-- putCell(byte, fg, bg, x, y) -- Draw a cell.
-- All arguments to this function are integers, and
-- must be in the unsigned 32-bit integer range: 0 to 2^32-1.
--
-- For arguments fg and bg: they are colors in the format
-- 0xSSRRGGBB, where SS is for styling. See your
-- config.ini for more details.
--
-- clock() -- The time, in microseconds.
-- }
--
@ -19,16 +21,21 @@
--
-- ]]
-- You should probably copy FPS and FPS_COUNT into any future LuaJIT animations you create.
local FPS_COUNT = 40
local function FPS()
return (1/FPS_COUNT)*1000000
end
local SQUARE_WIDTH = 10
local SQUARE_HEIGHT = 5
local SQUARE_COUNT = 25
local FPS = 60
local squares = {}
for i = 0, SQUARE_COUNT-1 do
for i = 1, SQUARE_COUNT do
local vx = 1
local vy = 1
if math.random(1, 2) == 2 then vx = -vx end
@ -46,17 +53,13 @@ local timer = ly.clock()
function draw()
-- Rather than progressing the animation by frame, do it based on
-- seconds, like from ly.clock().
if timer + ((1/FPS)*1000000)< ly.clock() then
ly.clear()
-- seconds, via ly.clock(). In this timeframe, you can update the animation state.
-- DO NOT DRAW CELLS IN THIS TIMEFRAME. You will get flickering.
if timer + FPS() < ly.clock() then -- if this check passes, we can update the animation
for i, v in ipairs(squares) do
v.x = v.x + v.vx
v.y = v.y + v.vy
for x = v.x, v.x + SQUARE_WIDTH do
for y = v.y, v.y + SQUARE_HEIGHT do
ly.putCell(string.byte(' '), 0, v.color, x, y)
end
end
if v.x <= 0 then v.vx = 1; v.color = math.random(0xFFFFFF) end
if v.x + SQUARE_WIDTH >= ly.width-1 then v.vx = -1; v.color = math.random(0xFFFFFF) end
if v.y <= 0 then v.vy = 1; v.color = math.random(0xFFFFFF) end
@ -64,4 +67,13 @@ function draw()
end
timer = ly.clock()
end
for i, v in ipairs(squares) do
for x = v.x, v.x + SQUARE_WIDTH do
for y = v.y, v.y + SQUARE_HEIGHT do
ly.putCell(string.byte(' '), 0, v.color, x, y)
end
end
end
end

View file

@ -74,31 +74,33 @@ pub fn init(
file_loading: {
const zf = std.mem.concatWithSentinel(alloc, u8, &[1][]const u8{file}, 0) catch |e| {
self.log.err(self.io, "lua", "failed to allocate file path: {}", .{e}) catch unreachable;
try self.log.err(self.io, "lua", "failed to allocate file path: {}", .{e});
self.lua_str = "failed to allocate file path!";
self.info_line.addMessage(lang.err_alloc, self.bg, self.fg) catch {};
self.lua_error = true;
break :file_loading;
return e;
};
defer alloc.free(zf);
self.lua.doString(ly_lua) catch {
const errorStr = self.lua.toString(-1) catch unreachable;
self.lua_str = try self.allocator.dupeSentinel(u8, errorStr, 0);
self.log.err(self.io, "lua", "lua error: {s}", .{errorStr}) catch unreachable;
self.lua_error = true;
break :file_loading;
};
self.propogateTerminalBounds();
// create the ly table
self.lua.newTable();
self.lua.setGlobal("ly");
// create ly.width and ly.height from TerminalBuffer width/height
self.propagateTerminalBounds();
_ = self.lua.getGlobal("ly");
_ = self.lua.pushString("clock");
self.lua.pushFunction(luaLyClock);
self.lua.setTable(-3);
_ = self.lua.pushString("putCell");
self.lua.pushFunction(luaPutCell);
self.lua.setTable(-3);
self.lua.setGlobal("ly");
self.lua.doFile(zf) catch {
const errorStr = self.lua.toString(-1) catch unreachable;
self.lua_str = try self.allocator.dupeSentinel(u8, errorStr, 0);
self.log.err(self.io, "lua", "lua error: {s}", .{errorStr}) catch unreachable;
try self.log.err(self.io, "lua", "lua error: {s}", .{errorStr});
self.lua_error = true;
break :file_loading;
};
@ -108,7 +110,7 @@ pub fn init(
}
fn draw(self: *Lua) void {
self.propogateTerminalBounds();
self.propagateTerminalBounds();
if (self.lua_error) {
// Ly's Red Screen of Omega-Death:tm:
const cell = Cell.init(0x2588, 0x00ff0000, 0x00ff0000);
@ -135,94 +137,12 @@ fn draw(self: *Lua) void {
self.lua_str = std.mem.concatWithSentinel(
self.allocator,
u8,
&[_][]const u8{ "Cannot call draw(): ", errorStr },
&.{ "Cannot call draw(): ", errorStr },
0,
) catch unreachable;
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_str = std.fmt.allocPrintSentinel(
self.allocator,
"global '_BUFFER' must be a table: got {}",
.{bufferType},
0,
) catch unreachable;
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_str = std.fmt.allocPrintSentinel(
self.allocator,
"pos: cannot convert to integer",
.{},
0,
) catch unreachable;
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_str = std.fmt.allocPrintSentinel(
self.allocator,
"char: cannot convert to integer",
.{},
0,
) catch unreachable;
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_str = std.fmt.allocPrintSentinel(
self.allocator,
"fg: cannot convert to integer",
.{},
0,
) catch unreachable;
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_str = std.fmt.allocPrintSentinel(
self.allocator,
"bg: cannot convert to integer",
.{},
0,
) catch unreachable;
self.lua_error = true;
return;
};
self.lua.pop(1);
self.lua.pop(1);
if (pos < 0) continue; // do not draw cells that underflow
TerminalBuffer.setCell(
@rem(@as(usize, @intCast(pos)), self.width),
@divFloor(@as(usize, @bitCast(pos)), self.width),
.init(
@intCast(char),
@intCast(@as(i32, @truncate(fg))),
@intCast(@as(i32, @truncate(bg))),
),
) catch {};
}
}
fn calculateTimeout(self: *Lua, _: *anyopaque) !?usize {
@ -250,7 +170,7 @@ pub fn widget(self: *Lua) *Widget {
return &self.instance.?;
}
fn propogateTerminalBounds(self: *Lua) void {
fn propagateTerminalBounds(self: *Lua) void {
if (self.terminal_buffer.height != self.height or
self.terminal_buffer.width != self.width)
{
@ -273,3 +193,34 @@ fn luaLyClock(state: ?*zlua.LuaState) callconv(.c) c_int {
lua.pushInteger(std.Io.Timestamp.now(threaded.io(), .real).toMicroseconds());
return 1;
}
fn luaPutCell(state: ?*zlua.LuaState) callconv(.c) c_int {
const lua: *zlua.Lua = @ptrCast(@alignCast(state orelse unreachable));
const MSG = "ly.putCell: Cannot convert %s-typed ";
const byte = lua.toNumeric(u32, 1) catch {
const t = lua.typeName(lua.typeOf(1));
lua.raiseErrorStr(MSG ++ "byte to u32", .{t.ptr});
};
const fg = lua.toNumeric(u32, 2) catch {
const t = lua.typeName(lua.typeOf(2));
lua.raiseErrorStr(MSG ++ "fg to u32", .{t.ptr});
};
const bg = lua.toNumeric(u32, 3) catch {
const t = lua.typeName(lua.typeOf(3));
lua.raiseErrorStr(MSG ++ "bg to u32", .{t.ptr});
};
const x = lua.toNumeric(usize, 4) catch {
const t = lua.typeName(lua.typeOf(4));
lua.raiseErrorStr(MSG ++ "x to usize", .{t.ptr});
};
const y = lua.toNumeric(usize, 5) catch {
const t = lua.typeName(lua.typeOf(5));
lua.raiseErrorStr(MSG ++ "y to usize", .{t.ptr});
};
TerminalBuffer.setCell(x, y, .{
.fg = fg,
.bg = bg,
.ch = byte,
}) catch {};
return 0;
}

View file

@ -1,17 +0,0 @@
ly = {
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