sol2 icon indicating copy to clipboard operation
sol2 copied to clipboard

How can I copy large chunks of data from Lua to C++ extremely efficiently?

Open skhaz opened this issue 6 months ago • 3 comments

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

skhaz avatar Jul 08 '25 20:07 skhaz

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.

EvanBalster avatar Dec 09 '25 00:12 EvanBalster

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

skhaz avatar Dec 09 '25 18:12 skhaz

Usage

canvas.pixels = "huge RGBA pixelbuffer"

skhaz avatar Dec 09 '25 18:12 skhaz