From 5e734ed4c713e862b8abb91ffd1483010f79c453 Mon Sep 17 00:00:00 2001 From: shylie Date: Thu, 7 May 2026 14:01:30 -0400 Subject: [PATCH] Add lua wrapper --- CMakeLists.txt | 21 ++- include/sand/rule.h | 3 +- include/sand/rule_builder.h | 1 - include/sand/sand.h | 1 + include/sand/type.h | 6 +- include/sand/type_builder.h | 2 +- src/lualib.cpp | 298 +++++++++++++++++++++++++++++++++++- src/rule.cpp | 4 +- src/rule_builder.cpp | 12 -- src/sand.hip | 16 +- src/type.cpp | 6 +- src/type_builder.cpp | 8 +- src/type_range.cpp | 2 +- 13 files changed, 331 insertions(+), 49 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d020ac..a046273 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 3.21) project(sand LANGUAGES CXX HIP) -find_package(OpenMP) - find_package(PkgConfig REQUIRED) pkg_check_modules(LUAJIT REQUIRED luajit) @@ -31,20 +29,27 @@ set_target_properties(sand PROPERTIES POSITION_INDEPENDENT_CODE ON ) -if(OpenMP_FOUND) - target_link_libraries(sand PRIVATE OpenMP::OpenMP_CXX) -endif() - if(EXISTS third/tracy/CMakeLists.txt) add_subdirectory(third/tracy) - target_link_libraries(sand PUBLIC Tracy::TracyClient) - if(NOT TRACY_ENABLE) + if(TRACY_ENABLE) + target_link_libraries(sand PUBLIC Tracy::TracyClient) + else() target_compile_definitions(sand PUBLIC FrameMark= ZoneScoped=) endif() else() target_compile_definitions(sand PUBLIC FrameMark= ZoneScoped=) endif() +add_library(lsand SHARED + src/lualib.cpp +) +target_include_directories(lsand PRIVATE ${LUAJIT_INCLUDE_DIRS}) +target_link_libraries(lsand PRIVATE sand ${LUAJIT_LIBRARIES}) +set_target_properties(lsand PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON +) + find_package(SDL3 CONFIG REQUIRED) add_executable(sandtest diff --git a/include/sand/rule.h b/include/sand/rule.h index e3a62f4..929b573 100644 --- a/include/sand/rule.h +++ b/include/sand/rule.h @@ -24,6 +24,7 @@ public: rule(rule&&) = default; rule& operator=(const rule&) = delete; + rule& operator=(rule&&) = default; ~rule(); @@ -39,7 +40,7 @@ public: private: rule(builder& rb, type from, type to); - builder& rb; + builder* rb; type from; type to; match_function matches[8]; diff --git a/include/sand/rule_builder.h b/include/sand/rule_builder.h index 7c720b0..9efdd2e 100644 --- a/include/sand/rule_builder.h +++ b/include/sand/rule_builder.h @@ -19,7 +19,6 @@ private: builder(type::range types, const std::vector& default_conversions); void finish_rule(const rule& rule); - unsigned int get_mask_index(type from, type to); unsigned int insert_conversion(type from, type to); type::range types; diff --git a/include/sand/sand.h b/include/sand/sand.h index 5778ad2..1d062e5 100644 --- a/include/sand/sand.h +++ b/include/sand/sand.h @@ -19,6 +19,7 @@ public: sand(sand&&) = default; sand& operator=(const sand&) = delete; + sand& operator=(sand&&) = default; ~sand(); diff --git a/include/sand/type.h b/include/sand/type.h index f42e14a..472847d 100644 --- a/include/sand/type.h +++ b/include/sand/type.h @@ -15,16 +15,16 @@ public: class range; using id_ty = uint16_t; + explicit type(id_ty id); + bool operator==(const type&) const; + operator id_ty() const; static const type OFF_GRID; static constexpr unsigned int MAX_TYPES = UINT16_MAX; private: - type(id_ty id); - operator id_ty() const; - id_ty id; friend builder; diff --git a/include/sand/type_builder.h b/include/sand/type_builder.h index e61a8bf..f5b7905 100644 --- a/include/sand/type_builder.h +++ b/include/sand/type_builder.h @@ -17,7 +17,7 @@ public: type assign(); range assign_range(unsigned int count); - void set_default_conversion(const type& to, const type& from); + void set_default_conversion(const type& from, const type& to); rule::builder build(); diff --git a/src/lualib.cpp b/src/lualib.cpp index 71193b7..bef7755 100644 --- a/src/lualib.cpp +++ b/src/lualib.cpp @@ -1,12 +1,296 @@ -#ifdef __cplusplus -extern "C" +#include +#include +#include + +namespace { -#endif // __cplusplus -#include +constexpr char TYPE_BUILDER_MT[] = "lsand.type_builder"; +constexpr char RULE_BUILDER_MT[] = "lsand.rule_builder"; +constexpr char RULE_MT[] = "lsand.rule"; +constexpr char SAND_MT[] = "lsand.sand"; -#ifdef __cplusplus +sand::type::builder& check_type_builder(lua_State* L, int index) +{ + return *(sand::type::builder*)luaL_checkudata(L, index, TYPE_BUILDER_MT); } -#endif // __cplusplus -extern "C" int luaopen_sand(lua_State* state) { return 1; } +sand::rule::builder& check_rule_builder(lua_State* L, int index) +{ + return *(sand::rule::builder*)luaL_checkudata(L, index, RULE_BUILDER_MT); +} + +sand::rule& check_rule(lua_State* L, int index) +{ + return *(sand::rule*)luaL_checkudata(L, index, RULE_MT); +} + +sand::sand& check_sand(lua_State* L, int index) +{ + return *(sand::sand*)luaL_checkudata(L, index, SAND_MT); +} + +int l_type_builder_gc(lua_State* L) +{ + auto& tb = check_type_builder(L, 1); + tb.~builder(); + + return 0; +} + +int l_type_builder_assign(lua_State* L) +{ + auto& tb = check_type_builder(L, 1); + sand::type t = tb.assign(); + + lua_pushinteger(L, t); + + return 1; +} + +int l_type_builder_set_default_conversion(lua_State* L) +{ + auto& tb = check_type_builder(L, 1); + auto from = sand::type(luaL_checkinteger(L, 2)); + auto to = sand::type(luaL_checkinteger(L, 3)); + + tb.set_default_conversion(to, from); + + return 0; +} + +int l_type_builder_build(lua_State* L) +{ + auto& tb = check_type_builder(L, 1); + auto* rb + = (sand::rule::builder*)lua_newuserdata(L, sizeof(sand::rule::builder)); + luaL_getmetatable(L, RULE_BUILDER_MT); + lua_setmetatable(L, -2); + + new (rb) sand::rule::builder(tb.build()); + + return 1; +} + +int l_rule_builder_gc(lua_State* L) +{ + auto& rb = check_rule_builder(L, 1); + rb.~builder(); + + return 0; +} + +int l_rule_builder_add_rule(lua_State* L) +{ + auto& rb = check_rule_builder(L, 1); + auto from = sand::type(luaL_checkinteger(L, 2)); + auto to = sand::type(luaL_checkinteger(L, 3)); + + auto* rule = (sand::rule*)lua_newuserdata(L, sizeof(sand::rule)); + luaL_getmetatable(L, RULE_MT); + lua_setmetatable(L, -2); + + new (rule) sand::rule(rb.add_rule(from, to)); + + return 1; +} + +int l_rule_builder_build(lua_State* L) +{ + auto& rb = check_rule_builder(L, 1); + int width = luaL_checkinteger(L, 2); + int height = luaL_checkinteger(L, 3); + + auto* sand_object = (sand::sand*)lua_newuserdata(L, sizeof(sand::sand)); + luaL_getmetatable(L, SAND_MT); + lua_setmetatable(L, -2); + + new (sand_object) sand::sand(rb.build(width, height, sand::type(1))); + + return 1; +} + +int l_rule_match(lua_State* L) +{ + auto& r = check_rule(L, 1); + std::string match_position = luaL_checkstring(L, 2); + luaL_checktype(L, 3, LUA_TFUNCTION); + + int fn_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + const auto& match_fn = [L, fn_ref](auto t) -> bool + { + lua_rawgeti(L, LUA_REGISTRYINDEX, fn_ref); + lua_pushinteger(L, t); + if (lua_pcall(L, 1, 1, 0) != LUA_OK) + { + const char* msg = lua_tostring(L, -1); + lua_pop(L, 1); + luaL_error(L, "Error during match callback: %s", msg); + } + if (!lua_isboolean(L, -1)) + { + luaL_error(L, "match callback did not return a boolean"); + } + bool value = lua_toboolean(L, -1); + lua_pop(L, 1); + return value; + }; + + if (match_position == "top_left") + { + r.top_left(match_fn); + } + else if (match_position == "top") + { + r.top(match_fn); + } + else if (match_position == "top_right") + { + r.top_right(match_fn); + } + else if (match_position == "left") + { + r.left(match_fn); + } + else if (match_position == "right") + { + r.right(match_fn); + } + else if (match_position == "bottom_left") + { + r.bottom_left(match_fn); + } + else if (match_position == "bottom") + { + r.bottom(match_fn); + } + else if (match_position == "bottom_right") + { + r.bottom_right(match_fn); + } + else + { + luaL_argerror(L, 2, "Invalid match position"); + } + + lua_pushvalue(L, 1); + return 1; +} + +int l_rule_finish(lua_State* L) +{ + auto& r = check_rule(L, 1); + r.~rule(); + + // mark rule as an invalid object + // will error if any other methods + // are called on this object + lua_pushnil(L); + lua_setmetatable(L, -1); + + return 0; +} + +int l_sand_gc(lua_State* L) +{ + auto& sand = check_sand(L, 1); + sand.~sand(); + + return 0; +} + +int l_sand_tick(lua_State* L) +{ + auto& sand = check_sand(L, 1); + sand.tick(); + + return 0; +} + +int l_sand_get(lua_State* L) +{ + auto& sand = check_sand(L, 1); + int x = luaL_checkinteger(L, 2); + int y = luaL_checkinteger(L, 3); + + lua_pushinteger(L, sand.get(x, y)); + return 1; +} + +int l_sand_set(lua_State* L) +{ + auto& sand = check_sand(L, 1); + int x = luaL_checkinteger(L, 2); + int y = luaL_checkinteger(L, 3); + sand::type t = sand::type(luaL_checkinteger(L, 4)); + + sand.set(x, y, t); + return 0; +} + +int l_lsand_new(lua_State* L) +{ + auto* tb + = (sand::type::builder*)lua_newuserdata(L, sizeof(sand::type::builder)); + luaL_getmetatable(L, TYPE_BUILDER_MT); + lua_setmetatable(L, -2); + + new (tb) sand::type::builder(); + + return 1; +} + +constexpr luaL_Reg TYPE_BUILDER_FUNCS[] + = { "__gc", + l_type_builder_gc, + "assign", + l_type_builder_assign, + "set_default_conversion", + l_type_builder_set_default_conversion, + "build", + l_type_builder_build, + nullptr, + nullptr }; + +constexpr luaL_Reg RULE_BUILDER_FUNCS[] + = { "__gc", l_rule_builder_gc, "add_rule", l_rule_builder_add_rule, + "build", l_rule_builder_build, nullptr, nullptr }; + +constexpr luaL_Reg RULE_FUNCS[] + = { "match", l_rule_match, "finish", l_rule_finish, nullptr, nullptr }; + +constexpr luaL_Reg SAND_FUNCS[] + = { "__gc", l_sand_gc, "tick", l_sand_tick, "get", + l_sand_get, "set", l_sand_set, nullptr, nullptr }; + +constexpr luaL_Reg LSAND_FUNCS[] = { "new", l_lsand_new, nullptr, nullptr }; + +} // namespace + +extern "C" int luaopen_lsand(lua_State* L) +{ + luaL_newmetatable(L, TYPE_BUILDER_MT); + luaL_register(L, nullptr, TYPE_BUILDER_FUNCS); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + + luaL_newmetatable(L, RULE_BUILDER_MT); + luaL_register(L, nullptr, RULE_BUILDER_FUNCS); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + + luaL_newmetatable(L, RULE_MT); + luaL_register(L, nullptr, RULE_FUNCS); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + + luaL_newmetatable(L, SAND_MT); + luaL_register(L, nullptr, SAND_FUNCS); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + + luaL_openlib(L, "lsand", LSAND_FUNCS, 0); + + return 1; +} diff --git a/src/rule.cpp b/src/rule.cpp index 382b63f..b535b3d 100644 --- a/src/rule.cpp +++ b/src/rule.cpp @@ -3,7 +3,7 @@ #include "sand/rule_builder.h" using namespace sand; -rule::~rule() { rb.finish_rule(*this); } +rule::~rule() { rb->finish_rule(*this); } rule& rule::top_left(rule::match_function match) { @@ -54,7 +54,7 @@ rule& rule::bottom_right(rule::match_function match) } rule::rule(rule::builder& rb, type from, type to) : - rb(rb), + rb(&rb), from(from), to(to), matches{ nullptr, nullptr, nullptr, nullptr, diff --git a/src/rule_builder.cpp b/src/rule_builder.cpp index ad0534c..de33895 100644 --- a/src/rule_builder.cpp +++ b/src/rule_builder.cpp @@ -65,18 +65,6 @@ void rule::builder::finish_rule(const rule& rule) } } -unsigned int rule::builder::get_mask_index(type from, type to) -{ - for (int i = metas[from].begin; i < metas[from].end; i++) - { - if (conversions[i] == to) - { - return i; - } - } - return insert_conversion(from, to); -} - unsigned int rule::builder::insert_conversion(type from, type to) { uint32_t position = metas[from].end; diff --git a/src/sand.hip b/src/sand.hip index 5e1f672..906f7db 100644 --- a/src/sand.hip +++ b/src/sand.hip @@ -35,10 +35,10 @@ tick_kernel(sand::type::id_ty* data, unsigned int width, unsigned int height, sand::rule::mask* masks, unsigned masks_size, bool current, uint16_t types_size) { - const unsigned int tile_x = blockIdx.x * blockDim.x + threadIdx.x; - const unsigned int tile_y = blockIdx.y * blockDim.y + threadIdx.y; + const int tile_x = blockIdx.x * blockDim.x + threadIdx.x; + const int tile_y = blockIdx.y * blockDim.y + threadIdx.y; - if (tile_x >= width || tile_y >= height) + if (tile_x < 0 || tile_x >= width || tile_y < 0 || tile_y >= height) { return; } @@ -60,7 +60,7 @@ tick_kernel(sand::type::id_ty* data, unsigned int width, unsigned int height, } auto neighbor_type = 0; - if (tile_x + dx > 0 && tile_x + dx < width && tile_y + dy > 0 + if (tile_x + dx >= 0 && tile_x + dx < width && tile_y + dy >= 0 && tile_y + dy < height) { neighbor_type = data[(tile_x + dx) + (tile_y + dy) * width @@ -106,16 +106,20 @@ sand::sand::~sand() sand::type sand::sand::get(int x, int y) { - sync_device_read_data(); if (x < 0 || x >= width || y < 0 || y >= height) { return type::OFF_GRID; } - return data[x + y * width + current * width * height]; + sync_device_read_data(); + return type(data[x + y * width + current * width * height]); } void sand::sand::set(int x, int y, type type) { + if (x < 0 || x >= width || y < 0 || y >= height) + { + return; + } data[x + y * width + current * width * height] = type; write_dirty = true; } diff --git a/src/type.cpp b/src/type.cpp index 5d8835d..f8417fe 100644 --- a/src/type.cpp +++ b/src/type.cpp @@ -1,13 +1,13 @@ #include "sand/type.h" using namespace sand; -const type type::OFF_GRID = 0; - -bool type::operator==(const type& other) const { return id == other.id; } +const type type::OFF_GRID = type(0); type::type(id_ty id) : id(id) { } +bool type::operator==(const type& other) const { return id == other.id; } + type::operator id_ty() const { return id; } diff --git a/src/type_builder.cpp b/src/type_builder.cpp index 46300fe..3fef276 100644 --- a/src/type_builder.cpp +++ b/src/type_builder.cpp @@ -15,8 +15,8 @@ type::builder::builder() : type type::builder::assign() { - default_conversions.push_back(next_id); - return next_id++; + default_conversions.push_back(type(next_id)); + return type(next_id++); } type::range type::builder::assign_range(unsigned int count) @@ -29,9 +29,9 @@ type::range type::builder::assign_range(unsigned int count) return r; } -void type::builder::set_default_conversion(const type& to, const type& from) +void type::builder::set_default_conversion(const type& from, const type& to) { - default_conversions[to] = from; + default_conversions[from] = to; } rule::builder type::builder::build() diff --git a/src/type_range.cpp b/src/type_range.cpp index fad2954..cce6a49 100644 --- a/src/type_range.cpp +++ b/src/type_range.cpp @@ -17,7 +17,7 @@ type::range::range(type::id_ty begin, type::id_ty end) : { } -type type::range::iter::operator*() const { return value; } +type type::range::iter::operator*() const { return type(value); } type::range::iter& type::range::iter::operator++() {