From e48216da4016532b096794548f49ab97ef337a99 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 8 Mar 2025 17:33:56 +0300 Subject: [PATCH] experimental optimize canvas:set_data using ffi --- res/scripts/stdmin.lua | 28 +++++++++++++++++++ src/logic/scripting/lua/lua_engine.cpp | 19 +++++++------ src/logic/scripting/lua/lua_util.cpp | 8 ++++-- src/logic/scripting/lua/lua_wrapper.hpp | 6 +++- .../lua/usertypes/lua_type_canvas.cpp | 11 +++++++- src/logic/scripting/scripting.cpp | 2 +- 6 files changed, 61 insertions(+), 13 deletions(-) diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua index ce592277..e2e4d3c7 100644 --- a/res/scripts/stdmin.lua +++ b/res/scripts/stdmin.lua @@ -1,3 +1,31 @@ +local _ffi = ffi +ffi = nil + +-- Lua has no parallelizm, also _set_data does not call any lua functions so +-- may be reused one global ffi buffer per lua_State +local canvas_ffi_buffer +local canvas_ffi_buffer_size = 0 + +function __vc_Canvas_set_data(self, data) + if type(data) == "cdata" then + self:_set_data(tostring(_ffi.cast("uintptr_t", data))) + end + local width = self.width + local height = self.height + + local size = width * height * 4 + if size > canvas_ffi_buffer_size then + canvas_ffi_buffer = _ffi.new( + string.format("unsigned char[%s]", size) + ) + canvas_ffi_buffer_size = size + end + for i=0, size - 1 do + canvas_ffi_buffer[i] = data[i + 1] + end + self:_set_data(tostring(_ffi.cast("uintptr_t", canvas_ffi_buffer))) +end + -- Check if given table is an array function is_array(x) if #x > 0 then diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index 251f789b..09c0f424 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -88,14 +88,17 @@ static void create_libs(State* L, StateType stateType) { void lua::init_state(State* L, StateType stateType) { // Allowed standard libraries - pop(L, luaopen_base(L)); - pop(L, luaopen_math(L)); - pop(L, luaopen_string(L)); - pop(L, luaopen_table(L)); - pop(L, luaopen_debug(L)); - pop(L, luaopen_jit(L)); - pop(L, luaopen_bit(L)); - pop(L, luaopen_os(L)); + luaL_openlibs(L); + + if (getglobal(L, "require")) { + pushstring(L, "ffi"); + if (call_nothrow(L, 1, 1)) { + setglobal(L, "ffi"); + } + } + pushnil(L); + setglobal(L, "io"); + const char* removed_os[] { "execute", "exit", "remove", "rename", "setlocale", "tmpname", nullptr}; remove_lib_funcs(L, "os", removed_os); diff --git a/src/logic/scripting/lua/lua_util.cpp b/src/logic/scripting/lua/lua_util.cpp index d740165b..8cd77fb1 100644 --- a/src/logic/scripting/lua/lua_util.cpp +++ b/src/logic/scripting/lua/lua_util.cpp @@ -163,20 +163,23 @@ int lua::call(State* L, int argc, int nresults) { int handler_pos = gettop(L) - argc; pushcfunction(L, l_error_handler); insert(L, handler_pos); + int top = gettop(L); if (lua_pcall(L, argc, nresults, handler_pos)) { std::string log = tostring(L, -1); pop(L); remove(L, handler_pos); throw luaerror(log); } + int added = gettop(L) - (top - argc - 1); remove(L, handler_pos); - return nresults == -1 ? 1 : nresults; + return added; } int lua::call_nothrow(State* L, int argc, int nresults) { int handler_pos = gettop(L) - argc; pushcfunction(L, l_error_handler); insert(L, handler_pos); + int top = gettop(L); if (lua_pcall(L, argc, -1, handler_pos)) { auto errorstr = tostring(L, -1); if (errorstr) { @@ -188,8 +191,9 @@ int lua::call_nothrow(State* L, int argc, int nresults) { remove(L, handler_pos); return 0; } + int added = gettop(L) - (top - argc - 1); remove(L, handler_pos); - return 1; + return added; } void lua::dump_stack(State* L) { diff --git a/src/logic/scripting/lua/lua_wrapper.hpp b/src/logic/scripting/lua/lua_wrapper.hpp index 417314cd..f82f5bd4 100644 --- a/src/logic/scripting/lua/lua_wrapper.hpp +++ b/src/logic/scripting/lua/lua_wrapper.hpp @@ -25,7 +25,8 @@ namespace lua { if (n < 0) { abort(); } - if (lua_gettop(L) < n) { + int top = lua_gettop(L); + if (top < n) { abort(); } #endif @@ -63,6 +64,9 @@ namespace lua { inline void rawseti(lua::State* L, int n, int idx = -2) { lua_rawseti(L, idx, n); } + inline void rawset(lua::State* L, int idx = -3) { + lua_rawset(L, idx); + } inline int createtable(lua::State* L, int narr, int nrec) { lua_createtable(L, narr, nrec); diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp index 944f62a2..a4292f56 100644 --- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp +++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp @@ -136,6 +136,12 @@ static int l_set_data(State* L) { auto& canvas = require_canvas(L, 1); auto& image = canvas.data(); auto data = image.getData(); + + if (lua::isstring(L, 2)) { + auto ptr = reinterpret_cast(std::stoull(lua::tostring(L, 2))); + std::memcpy(data, ptr, image.getDataSize()); + return 0; + } int len = objlen(L, 2); if (len < image.getDataSize()) { throw std::runtime_error( @@ -167,7 +173,7 @@ static std::unordered_map methods { {"blit", lua::wrap}, {"clear", lua::wrap}, {"update", lua::wrap}, - {"set_data", lua::wrap}, + {"_set_data", lua::wrap}, }; static int l_meta_index(State* L) { @@ -189,6 +195,9 @@ static int l_meta_index(State* L) { if (!strcmp(name, "height")) { return pushinteger(L, data.getHeight()); } + if (!strcmp(name, "set_data")) { + return getglobal(L, "__vc_Canvas_set_data"); + } if (auto func = methods.find(tostring(L, 2)); func != methods.end()) { return pushcfunction(L, func->second); } diff --git a/src/logic/scripting/scripting.cpp b/src/logic/scripting/scripting.cpp index 92ed66a2..66606776 100644 --- a/src/logic/scripting/scripting.cpp +++ b/src/logic/scripting/scripting.cpp @@ -120,7 +120,7 @@ std::unique_ptr scripting::start_coroutine( lua::loadbuffer(L, 0, source, script.name()); if (lua::call(L, 1)) { int id = lua::tointeger(L, -1); - lua::pop(L, 2); + lua::pop(L, 1); return std::make_unique(L, id); } lua::pop(L);