How can I copy large chunks of data from Lua to C++ extremely efficiently?
How can I copy large chunks of data from Lua to C++ extremely efficiently?
Without LuaJIT/FFI.
Context: I’m building a game engine, and one of its features allows generating bitmaps on the fly using Lua. For example, I can run a mini raytracing engine in Lua and blit the resulting texture to SDL.
This works well for small resolutions. However, I’ve reached a point where copying 3 million pixels (an array of uint32_t values) causes a major FPS drop.
I’ve optimized the data transfer as much as I could and ended up with a version that copies one uint32_t at a time. It works, but it’s inefficient.
What I need is a way to copy large chunks of pixel data from Lua to C++ at once — ideally as a single block — to improve performance significantly, especially at higher resolutions.
lua.new_usertype<graphics::canvas>(
"Canvas",
sol::no_constructor,
"pixels", sol::property(
[](const graphics::canvas&) {
return sol::lua_nil;
},
[](graphics::canvas& canvas, sol::table table) {
const auto n = table.size();
static std::vector<uint32_t> buffer;
if (buffer.size() != n) [[unlikely]] {
buffer.resize(n);
}
const auto L = table.lua_state();
sol::stack::push(L, table);
for (std::size_t i = 0; i < n; ++i) {
lua_rawgeti(L, -1, static_cast<int>(i + 1));
buffer[i] = static_cast<uint32_t>(lua_tointeger(L, -1)); // one uint32_t per time, in other words, millions of calls per frame
lua_pop(L, 1);
}
lua_pop(L, 1);
canvas.set_pixels(buffer);
}
)
);
Usage
-- stylua: ignore
_G.engine = EngineFactory.new()
:with_title("Tela Vermelha")
:with_width(1920)
:with_height(4080)
:with_scale(1.0)
:with_fullscreen(false)
:create()
local canvas = engine:canvas()
local width, height = 1920, 4080
local pixels = {}
function setup()
local red = 0xFF0000FF
for i = 1, width * height do
pixels[i] = red
end
end
function loop()
canvas.pixels = pixels
end
Pardon the late reply but I'll weigh in.
From what I understand, calling into C++ code from Lua is costly enough that calling some C++ function for each pixel will probably erase any performance gains you were hoping to get by manipulating a high-performance type (such as a pixel buffer). Lua's quickest at manipulating its own types, so I think you're on the right track converting a big Lua value
But there's one other option: strings. Lua strings can contain arbitrary binary and lua has built-in functionality for serializing (with string.pack) large amount of data into strings. If you can find some way to navigate the immutability (and thus, copying) of strings, it would almost certainly trivialize the copying of pixel data.
Another option would be to expose a usertype for your canvas with high-level operations like blitting. Then you can manipulate a lot of pixels with optimized C++ code without crossing the Lua/C++ barrier too much.
Beyond that... You may just need to consider a faster runtime like Luau or LuaJIT.
Yes, std::string_view was the best choice.
See here https://github.com/willtobyte/carimbo/blob/15d3d70656c104644b8dd2e56b8cb21d71def7cc/src/scriptengine.cpp#L974
And here https://github.com/willtobyte/carimbo/blob/15d3d70656c104644b8dd2e56b8cb21d71def7cc/src/canvas.cpp#L36-L44
Usage
canvas.pixels = "huge RGBA pixelbuffer"