Сохранять состояние Lua в среде C ++ для ограничения переключений контекста - PullRequest
1 голос
/ 06 мая 2020

Мне нравится кодировать простые демонстрации OpenGL, и я недавно решил использовать Lua с моим движком C ++, чтобы динамически изменять рендеринг без перекомпиляции в моем проекте. Таким образом я могу легче настроить алгоритм рендеринга. Но я знаю, что мои текущие функции обновления рендеринга, вероятно, далеки от эффективности.

На данный момент я передаю матрицу из C ++ в Lua, изменяю ее в сценарии Lua и отправляю обратно в свой механизм рендеринга C ++. Но я перезагружаю сценарий Lua каждый раз, когда получаю вызов обновления от механизма C ++, и теряю весь контекст переменной. Это означает, что я всегда начинаю с нуля и мой рендеринг далеко не плавный. Ниже я привожу пример кода, чтобы объяснить, что я делаю. В настоящее время я изучаю Lua со встраиванием C ++, поэтому знаю, что у меня до сих пор нет лучших практик.

обновление. 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

матрица. 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");
}

Думаю of передача некоторых аргументов при вызове функции update (), но мне интересно, является ли подход C ++ к Lua, а затем обратно к C ++ правильным и эффективным. Особенно с учетом того, что я могу перенести и модифицировать огромную матрицу в Lua. Мне, вероятно, не хватает некоторых встроенных знаний Lua, чтобы сохранять контекст при загрузке скрипта. У вас есть общий совет, как я могу улучшить свой код? Я знаю, что мой нынешний подход слишком сложен.

Ответы [ 2 ]

0 голосов
/ 06 мая 2020

Хорошо, загрузка фрагмента функции update () в глобальную переменную и наличие таблицы глобальных параметров в сценарии Lua - это путь к go. Я достиг этого , используя следующие рекомендации , и я опубликую подробные шаги ниже. По сути, загрузка скрипта полностью сначала гарантирует, что все глобальные переменные сохранятся в контексте 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 - это простая структура Dynami c, определенная как таковая:

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

Я использую только float, поэтому эта простая структура достаточно удобна для моих нужд , и позволяет мне добавлять множество переменных в мой сценарий Lua, не беспокоясь об определении структуры на C ++. Таким образом, я могу добавить столько параметров в свою таблицу Lua и производить вычисления при обновлении.

Шаг 6

Настройте скрипт Lua навсегда! Et voila:

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

Это решение соответствует моим потребностям, но кажется очень сложным и неэффективным. Но в любом случае это был отличный хакерский сеанс.

0 голосов
/ 06 мая 2020

Быстрое исправление - загрузить файл только в том случае, если он был изменен с момента последнего кадра:

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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...