Skip to content

Commit 6932085

Browse files
committed
Expose Luau built-in float vector support
The Luau VM supports native f32 3- or 4-vectors so that typical linear algebra operations are fast in game code. Both the 3- and 4-vector flavors are supported. Use -Dluau_vector_size=N to choose which. This must be configured at build time, as the native Luau VM must be re-compiled for this setting.
1 parent 848190f commit 6932085

File tree

3 files changed

+113
-3
lines changed

3 files changed

+113
-3
lines changed

build.zig

+10-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub fn build(b: *Build) void {
2020

2121
const lang = b.option(Language, "lang", "Lua language version to build") orelse .lua54;
2222
const shared = b.option(bool, "shared", "Build shared library instead of static") orelse false;
23-
23+
const luau_use_4_vector = b.option(bool, "luau_use_4_vector", "Build Luau to use 4-vectors instead of the default 3-vector.") orelse false;
2424
const upstream = b.dependency(@tagName(lang), .{});
2525

2626
// Zig module
@@ -37,10 +37,16 @@ pub fn build(b: *Build) void {
3737
// Expose build configuration to the ziglua module
3838
const config = b.addOptions();
3939
config.addOption(Language, "lang", lang);
40+
config.addOption(bool, "luau_use_4_vector", luau_use_4_vector);
4041
ziglua.addOptions("config", config);
4142

43+
if (lang == .luau) {
44+
const vector_size: usize = if (luau_use_4_vector) 4 else 3;
45+
ziglua.addCMacro("LUA_VECTOR_SIZE", b.fmt("{}", .{vector_size}));
46+
}
47+
4248
const lib = switch (lang) {
43-
.luau => buildLuau(b, target, optimize, upstream, shared),
49+
.luau => buildLuau(b, target, optimize, upstream, shared, luau_use_4_vector),
4450
else => buildLua(b, target, optimize, upstream, lang, shared),
4551
};
4652

@@ -152,7 +158,7 @@ fn buildLua(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.Optim
152158
}
153159

154160
/// Luau has diverged enough from Lua (C++, project structure, ...) that it is easier to separate the build logic
155-
fn buildLuau(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, upstream: *Build.Dependency, shared: bool) *Step.Compile {
161+
fn buildLuau(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, upstream: *Build.Dependency, shared: bool, luau_use_4_vector: bool) *Step.Compile {
156162
const lib_opts = .{
157163
.name = "luau",
158164
.target = target,
@@ -174,6 +180,7 @@ fn buildLuau(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.Opti
174180
"-DLUA_API=extern\"C\"",
175181
"-DLUACODE_API=extern\"C\"",
176182
"-DLUACODEGEN_API=extern\"C\"",
183+
if (luau_use_4_vector) "-DLUA_VECTOR_SIZE=4" else "",
177184
};
178185

179186
for (luau_source_files) |file| {

src/libluau.zig

+34
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ const c = @cImport({
1212
const config = @import("config");
1313
pub const lang = config.lang;
1414

15+
/// The length of Luau vector values, either 3 or 4.
16+
pub const luau_vector_size = if (config.luau_use_4_vector) 4 else 3;
17+
1518
/// This function is defined in luau.cpp and must be called to define the assertion printer
1619
extern "c" fn zig_registerAssertionHandler() void;
1720

@@ -139,6 +142,7 @@ pub const LuaType = enum(i5) {
139142
boolean = c.LUA_TBOOLEAN,
140143
light_userdata = c.LUA_TLIGHTUSERDATA,
141144
number = c.LUA_TNUMBER,
145+
vector = c.LUA_TVECTOR,
142146
string = c.LUA_TSTRING,
143147
table = c.LUA_TTABLE,
144148
function = c.LUA_TFUNCTION,
@@ -520,6 +524,11 @@ pub const Lua = struct {
520524
return c.lua_isuserdata(lua.state, index) != 0;
521525
}
522526

527+
/// Returns true if the value at the given index is a vector
528+
pub fn isVector(lua: *Lua, index: i32) bool {
529+
return c.lua_isvector(lua.state, index);
530+
}
531+
523532
/// Returns true if the value at index1 is smaller than the value at index2, following the
524533
/// semantics of the Lua < operator.
525534
/// See https://www.lua.org/manual/5.4/manual.html#lua_lessthan
@@ -713,6 +722,17 @@ pub const Lua = struct {
713722
c.lua_pushvalue(lua.state, index);
714723
}
715724

725+
fn pushVector3(lua: *Lua, x: f32, y: f32, z: f32) void {
726+
c.lua_pushvector(lua.state, x, y, z);
727+
}
728+
729+
fn pushVector4(lua: *Lua, x: f32, y: f32, z: f32, w: f32) void {
730+
c.lua_pushvector(lua.state, x, y, z, w);
731+
}
732+
733+
/// Pushes a floating point 3-vector (or 4-vector if configured) `v` onto the stack
734+
pub const pushVector = if (luau_vector_size == 3) pushVector3 else pushVector4;
735+
716736
/// Returns true if the two values in indices `index1` and `index2` are primitively equal
717737
/// Bypasses __eq metamethods
718738
/// Returns false if not equal, or if any index is invalid
@@ -927,6 +947,20 @@ pub const Lua = struct {
927947
return error.Fail;
928948
}
929949

950+
/// Converts the Lua value at the given `index` to a 3- or 4-vector.
951+
/// The Lua value must be a vector.
952+
pub fn toVector(lua: *Lua, index: i32) ![luau_vector_size]f32 {
953+
const res = c.lua_tovector(lua.state, index);
954+
if (res) |r| {
955+
switch (luau_vector_size) {
956+
3 => return [_]f32{ r[0], r[1], r[2] },
957+
4 => return [_]f32{ r[0], r[1], r[2], r[3] },
958+
else => @compileError("invalid luau_vector_size - should not happen"),
959+
}
960+
}
961+
return error.Fail;
962+
}
963+
930964
/// Returns the `LuaType` of the value at the given index
931965
/// Note that this is equivalent to lua_type but because type is a Zig primitive it is renamed to `typeOf`
932966
/// See https://www.lua.org/manual/5.1/manual.html#lua_type

src/tests.zig

+69
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,10 @@ test "typenames" {
381381
try expectEqualStrings("function", lua.typeName(.function));
382382
try expectEqualStrings("userdata", lua.typeName(.userdata));
383383
try expectEqualStrings("thread", lua.typeName(.thread));
384+
385+
if (ziglua.lang == .luau) {
386+
try expectEqualStrings("vector", lua.typeName(.vector));
387+
}
384388
}
385389

386390
test "unsigned" {
@@ -2249,3 +2253,68 @@ test "tagged userdata" {
22492253
lua.pushInteger(13);
22502254
try expectError(error.Fail, lua.userdataTag(-1));
22512255
}
2256+
2257+
fn vectorCtor(l: *Lua) i32 {
2258+
const x = l.toNumber(1) catch unreachable;
2259+
const y = l.toNumber(2) catch unreachable;
2260+
const z = l.toNumber(3) catch unreachable;
2261+
if (ziglua.luau_vector_size == 4) {
2262+
const w = l.optNumber(4, 0);
2263+
l.pushVector(@floatCast(x), @floatCast(y), @floatCast(z), @floatCast(w));
2264+
} else {
2265+
l.pushVector(@floatCast(x), @floatCast(y), @floatCast(z));
2266+
}
2267+
return 1;
2268+
}
2269+
2270+
test "luau vectors" {
2271+
if (ziglua.lang != .luau) return;
2272+
2273+
var lua = try Lua.init(testing.allocator);
2274+
defer lua.deinit();
2275+
lua.openLibs();
2276+
lua.register("vector", ziglua.wrap(vectorCtor));
2277+
2278+
try lua.doString(
2279+
\\function test()
2280+
\\ local a = vector(1, 2, 3)
2281+
\\ local b = vector(4, 5, 6)
2282+
\\ local c = (a + b) * vector(2, 2, 2)
2283+
\\ return c
2284+
\\end
2285+
);
2286+
_ = try lua.getGlobal("test");
2287+
try lua.protectedCall(0, 1, 0);
2288+
var v = try lua.toVector(-1);
2289+
try testing.expectEqualSlices(f32, &[3]f32{ 10, 14, 18 }, v[0..3]);
2290+
2291+
if (ziglua.luau_vector_size == 3) lua.pushVector(1, 2, 3) else lua.pushVector(1, 2, 3, 4);
2292+
try expect(lua.isVector(-1));
2293+
v = try lua.toVector(-1);
2294+
const expected = if (ziglua.luau_vector_size == 3) [3]f32{ 1, 2, 3 } else [4]f32{ 1, 2, 3, 4 };
2295+
try expectEqual(expected, v);
2296+
try expectEqualStrings("vector", lua.typeNameIndex(-1));
2297+
2298+
lua.pushInteger(5);
2299+
try expect(!lua.isVector(-1));
2300+
}
2301+
2302+
test "luau 4-vectors" {
2303+
if (ziglua.lang != .luau) return;
2304+
2305+
var lua = try Lua.init(testing.allocator);
2306+
defer lua.deinit();
2307+
lua.openLibs();
2308+
lua.register("vector", ziglua.wrap(vectorCtor));
2309+
2310+
// More specific 4-vector tests
2311+
if (ziglua.luau_vector_size == 4) {
2312+
try lua.doString(
2313+
\\local a = vector(1, 2, 3, 4)
2314+
\\local b = vector(5, 6, 7, 8)
2315+
\\return a + b
2316+
);
2317+
const vec4 = try lua.toVector(-1);
2318+
try expectEqual([4]f32{ 6, 8, 10, 12 }, vec4);
2319+
}
2320+
}

0 commit comments

Comments
 (0)