GLFW 按键回调同步

GLFW key callback synchronization

总的来说,我对 GLFW 和 OpenGL 还很陌生,我正在研究一个小型模型渲染器。 我目前正在处理输入,但我正面临 GLFW 如何处理输入的问题,让我解释一下: 每个教程都告诉我们使用 glfwGetKey 和 "if forest" 来查看是否按下了某个键。我遇到的问题是,如果我映射很多键,它可能会变慢,而且它很难看。 所以我使用函数指针表和 glfwSetKeyCallback 来加快速度并获得更清晰的代码。 我遇到的问题是我面临着看起来像是竞争条件的问题,相机似乎停滞不前。 我使用在每一帧上计算的增量时间来保持恒定速度。 据我所知,似乎每隔一段时间就会调用一次键回调函数,而不是在重复键时在每一帧上调用一次...... 我使用的是来自他们 github 的最新版本的 glfw3,我在每个循环开始时交换缓冲区并在结束时使用 glfwPollEvents()。

我的问题如下:有没有办法同步 glfwPollEvents 调用和渲染,以避免渲染循环和回调函数之间的卡顿和时间差? 预先感谢您的帮助!

一般来说,处理输入的方式应该是保留一个键列表,并记录它们最后的输入状态。

struct key_event {
    int key, code, action, modifiers;
    std::chrono::steady_clock::time_point time_of_event;
}

std::map<int, bool> keys;
std::queue<key_event> unhandled_keys;
void handle_key(GLFWwindow* window, int key, int code, int action, int modifiers) {
    unhandled_keys.emplace_back(key, code, action, modifiers, std::chrono::steady_clock::now());
}

然后,在渲染循环中(或者如果你对你的多线程+同步能力有信心,你可以将它分成不同的循环)你可以这样写代码:

float now = glfwGetTime();
static float last_update = now;
float delta_time = now - last_update;
last_update = now;
handle_input(delta_time);

其中 handle_input 看起来像这样:

float external_position[2];
std::map<int, std::function<void(/*args*/)>> key_functions;
void handle_input(float delta_time) {
    //Anything that should happen "when the users presses the key" should happen here
    while(!unhandled_keys.is_empty()) {
        key_event event = unhandled_keys.front();
        unhandled_keys.pop();
        key_functions[event.key](/*args*/);
        bool pressed = event.action == GLFW_PRESS || event.action == GLFW_REPEAT;
        keys[event.key] = pressed;
    }
    //Anything that should happen "while the key is held down" should happen here.
    float movement[2] = {0,0};
    if(keys[GLFW_KEY_W]) movement[0] += delta_time;
    if(keys[GLFW_KEY_S]) movement[0] -= delta_time;
    if(keys[GLFW_KEY_A]) movement[1] -= delta_time;
    if(keys[GLFW_KEY_D]) movement[1] += delta_time;
    external_position[0] += movement[0];
    external_position[1] += movement[1];
}

编辑:我添加了处理 "on press" / "on release" 类型函数的逻辑。因此,例如,如果这段代码在渲染器中:

key_functions[GLFW_KEY_SPACE] = [&renderer] {renderer.pause();};

然后按 [Space] 键将暂停渲染器。

好吧,我会回答我自己的问题(并保留@Xirema 的回答,因为他是对的)

我想我刚刚明白为什么它在帧率和重复回调之间不同步,这是因为 OS 处理按键重复的方式,它不会每秒发送 4000+ 按键重复信号(当我获得 4000+ fps 时)并将自身限制为 60 calls/s 左右! 为了纠正这个问题,我确实会简单地在回调函数中注册最后按下的键 60 times/s 并在 GLFWkeyfun 之外(在主循环中)每帧执行一次该函数,或者找到一种方法来规范化运动避免口吃!

无论如何,再次感谢您花时间回答@Xirema 我希望我的问题对某人有用,现在,开始编写很棒的代码吧! (不是真的 ;-) )

P.S :知道按键重复信号发送了多少次,在 key_repeat 回调函数中使用固定的增量时间值也可以做到这一点!

P.P.S :好吧,考虑到键盘重复可以根据 OS 或用户设置甚至 CPU 功率而变化,最好的解决方案是有一个单独的 delta- GLFWkeyfun 的时间,这样你就可以将它发送到你的回调函数并始终获得正确的按键重复时间增量并避免所有口吃!