【问题标题】:Keep Lua state in a C++ environment to limit context switches在 C++ 环境中保持 Lua 状态以限制上下文切换
【发布时间】:2020-05-06 07:59:55
【问题描述】:

我在编写简单的 OpenGL 演示时玩得很开心,最近我决定将 Lua 与我的 C++ 引擎一起使用,以便动态更改渲染,而不必在我的项目上重新编译。因此,我可以更轻松地调整渲染算法。但我知道我目前的渲染更新功能可能远非高效。

目前,我正在将矩阵从 C++ 传输到 Lua,在 Lua 脚本中对其进行修改并将其发送回我的 C++ 渲染引擎。但是每次我从 C++ 引擎收到更新调用时,我都会重新加载 Lua 脚本,并且我会丢失所有变量上下文。这意味着我总是从头开始,我的渲染远非顺利。我在下面包含一些代码示例来解释我在做什么。我目前正在学习带有 C++ 嵌入的 Lua,所以我知道我仍然没有最佳实践。

update.lua

function transform(m)
    amplitude = 1.5
    frequency = 500
    phase = 0.0
    r = {}

    for i = 1, #m do
        r[i] = {}
        for j = 1, #m[i] do
            if (i % 2) then
                r[i][j] = amplitude * math.sin(m[i][j] + phase)
            else
                r[i][j] = -amplitude * math.sin(m[i][j] + phase)
            end
            phase = phase + 0.001
        end
    end
    return r
end

-- called by c++
function update()
    m = pull()
    r  = transform(m)
    push(r)
end

ma​​trix.cpp

// pull matrix from lua point of view
static int pull(lua_State * _L)
{
    _push(_L, &_m);

    return 1;
}

// push matrix from lua point of view
static int push(lua_State * _L)
{
    // get number of arguments
    int n = lua_gettop(_L);

    if(1 == n) {
        _pull(_L, 1, &_m);
    }

    return 1;
}

void matrix::load_file(char * file, char * function)
{
    int status;

    // load the file containing the script we are going to run
    status = luaL_loadfile(_L, file);
    switch (status) {
    case LUA_OK:
        break;
    case LUA_ERRFILE:
        std::cout << "LUA_ERRFILE: " << lua_error(_L) << std::endl;
        break;
    case LUA_ERRSYNTAX:
        std::cout << "LUA_ERRSYNTAX: " << lua_error(_L) << std::endl;
        break;
    default:
        std::cout << lua_error(_L) << std::endl;
    }

    lua_getglobal(_L, function);
    status = lua_pcall(_L, 1, 1, 0);
    if (status != LUA_OK) {
        std::cout << "error running file" << lua_error(_L) << std::endl;
    }
}

void matrix::update()
{
    load_file("lua/update.lua", "update");
}

我在调用 update() 函数时想到了passing some arguments,但我想知道 C++ 到 Lua 然后再回到 C++ 的方法是否正确且有效。特别是考虑到我可能会在 Lua 中传输和修改巨大的矩阵。我可能缺乏一些嵌入式 Lua 知识来在加载脚本时保持上下文。你对我将如何改进我的代码有一些一般性的建议吗?我知道我目前的方法过于复杂。

【问题讨论】:

  • load_file 长什么样子?
  • 我编辑了我的帖子以包含 load_file 函数
  • luaL_loadfile 之后,为什么不直接将加载的块保存到全局变量中?
  • 感谢您的建议。如果我保存块,它是否也会保留全局变量并能够在以后使用它们?我想我也可以在需要 update() 函数时重新运行?我正在检查如何保存该块。

标签: c++ lua


【解决方案1】:

一个快速的解决方法是只加载自上一帧以来修改过的文件:

static time_t last_modified = 0;
struct stat sbuf;
stat(file, &sbuf);
if (sbuf.st_mtime > last_modified) {
    last_modified = sbuf.st_mtime;
    status = luaL_loadfile(_L, file);
    // etc
}

// Now call the function
lua_getglobal(_L, function);
status = lua_pcall(_L, 1, 1, 0);

【讨论】:

  • 感谢您的反馈。我想我没有说清楚,因为我不是从 C++ 引擎重写脚本。只需定期调用 Lua 脚本中的 update() 函数
  • 这项技术将满足您的要求:停止每帧重新加载文件,从而保留上下文信息(只需将其存储在全局变量中)。另外,您可以在程序运行时修改lua文件,并在下一帧查看结果。
【解决方案2】:

好的,将 update() 函数的块加载到全局变量中并在 Lua 脚本中具有全局参数表是要走的路。我实现了这个using the following guidelines,我将在下面发布详细步骤。基本上,首先完全加载脚本可确保所有全局变量都存储在 C++ 上下文中。然后将想要的函数存储为索引允许我们再次运行它,同时保持脚本中的全局变量自行演变。

第 1 步

在初始化时第一次调用luaL_loadfile

第 2 步

使用lua_pcall(_L, 0, 0, 0); 运行一次脚本

这确保了在 Lua 脚本中用作参数的全局变量在内存中。

第 3 步

存储 Lua 函数。我设法用以下 C++ 代码做到了:

void matrix::store(char * function)
{
    lua_newtable(_L);  // create table for functions
    _idx = luaL_ref(_L, LUA_REGISTRYINDEX); // store said table in pseudo-registry
    lua_rawgeti(_L, LUA_REGISTRYINDEX, _idx); // retrieve table for functions

    lua_getglobal(_L, function); // retrieve function to store

    if (lua_isfunction(_L, -1)) {
        _f = luaL_ref(_L, -2); // store a function in the function table
    }
    else {
        lua_pop(_L, 1);
        std::cout << "can't find " << function << std::endl;
    }

    // table is two places up the current stack counter
    lua_pop(_L, 1); // we are done with the function table, so pop it

    std::cout << "idx: " << _idx << ", function: " << _f << std::endl;
}

第 4 步

使用以下 C++ 函数进行渲染时再次调用存储的函数:

void matrix::run()
{
    int status;

    if (_f == -1) {
        std::cout << "invalid function index " << _f << std::endl;
    }
    else {
        lua_rawgeti(_L, LUA_REGISTRYINDEX, _idx); // retrieve function table
        lua_rawgeti(_L, -1, _f); // retrieve function
        //use function
        status = lua_pcall(_L, 0, 0, 0);  // 0 arguments, 0 results
        if (status != LUA_OK) {
            std::cout << "error running function" << lua_error(_L) << std::endl;
        }
        //don't forget to pop the function table from the stack
        lua_pop(_L, 1);
    }
}

第 5 步(可选)

如果我们在全局表中设置所有 Lua 参数,我们可以使用以下代码在 C++ 中动态检索它们:

void matrix::get_params(char * p)
{
    lua_getglobal(_L, p);
    lua_pushnil(_L);

    int i = 0;
    while(lua_next(_L,-2))
    {
        const char * key = lua_tostring(_L,-2);
        double value = lua_tonumber(_L,-1);
        lua_pop(_L,1);
        std::cout << key << " = " << value << std::endl;
        _h[i].key.assign(key);
        _h[i].value = value;
        i++;
    }
    lua_pop(_L, 1);
}

其中_h是一个简单的动态结构,定义如下:

typedef struct {
    std::string key;
    float value;
} hash;

我只使用浮点数,所以这个简单的结构很方便满足我的需要,并且允许我在我的 Lua 脚本中添加大量变量,而无需担心 C++ 中的结构定义。这样我可以在我的 Lua 表中添加尽可能多的参数,并在更新时进行数学计算。

第 6 步

永远调整 Lua 脚本!瞧:

p = {
    amplitude = 1.5,
    frequency = 500,
    phase = 0.0
}

function transform(m)
    r = {}

    for i = 1, #m do
        r[i] = {}
        for j = 1, #m[i] do
            if (i % 2) then
                r[i][j] = p.amplitude * math.sin(m[i][j] + p.phase)
            else
                r[i][j] = -p.amplitude * math.sin(m[i][j] + p.phase)
            end
            p.phase = p.phase + 0.001
        end
    end
    return r
end

-- called by c++
function update()
    m = pull()
    r  = transform(m)
    push(r)
end

此解决方案符合我的需求,但似乎非常复杂且效率低下。但无论如何,这是一个很好的黑客会议。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-23
    • 1970-01-01
    相关资源
    最近更新 更多