c++ - 带互斥量的多线程输入不平滑(如预期)

标签 c++ multithreading c++11 input

我目前正在(再次)从头开发 3D 引擎,因为我想使用更现代的技术(坦率地说,我以前的设计很糟糕)。现在我正在实现我的输入线程。

现在我更有经验了,我知道如果我从我的输入线程和我的渲染/主线程写入相同的变量,那么我将得到数据竞争,所以我决定使用互斥体(mutices?)来锁定可能会发生的数据在不同的线程中写入,但这导致了一个 Not Acceptable 错误:鼠标输入不再流畅:/

虽然我确实有点期待,但我只是觉得我的想法可能不对。

现在我被困在十字路口,因为我不知道如何解决这个问题!

我从两个线程写入的变量是 x_rely_rel,这是我收到事件时鼠标相对于最后位置的位置。

输入线程设置变量,渲染/主线程在完成时将它们重置为 0.0。这工作正常,但正如我所说,这使我的鼠标运动非常僵硬。

我的问题是,我可以做些什么来获得流畅的输入,同时仍然保持跨线程的安全竞争?


这是我的互斥量定义(它是全局的):

std::mutex mouse_mutex;

这是我用来获取鼠标事件的代码:

void input_thread_func(application &app, const bool &running, double &x_rel, double &y_rel){
    while(running){
        application::event ev = app.get_input();
        switch(ev.type){
            case events::mouse_motion :{
                if(mouse_mutex.try_lock()){
                    x_rel = ev.xrel;
                    y_rel = ev.yrel;
                    mouse_mutex.unlock();
                }
                break;
            }

            default:
                break;
        }
    }
}

这是我的主要功能:

int main(int argc, char *argv[]){
    /* all my init stuff */

    application app;
    bool running = true;
    double x_rel = 0.0, y_rel = 0.0;
    std::thread input_thread(
        input_thread_func,
        std::ref(app), std::cref(running)
        std::ref(x_rel), std::ref(y_rel)
    );

    double multiplier = /* whatever I like */;        

    while(running){
        /* check input data */

        if(mouse_mutex.try_lock()){
            update_camera(x_rel * multiplier, y_rel * multiplier);            
            app.set_mouse_pos(0, 0);
            x_rel = 0.0; y_rel = 0.0;
            mouse_mutex.unlock();
        }

        /* do rendering stuff */
    }
}

最佳答案

这是我对你的问题的理解:

基本上,您有 2 个线程,一个处理来自系统的鼠标事件,另一个负责渲染任务。据我了解,后者需要最新的鼠标位置才能计算出一组准确的相机矩阵。然而,由于输入设备的性质,事件线程充斥着鼠标位置事件:系统以足够快的速度轮询设备以在每个渲染帧获得许多更新,并且这些更新被推回到事件线程。该线程只有那个任务要做,它会在处理这些事件时不断地锁定/解锁鼠标互斥锁,并且当渲染线程实际上想要获取它时,事件线程持有该锁的可能性很高。

您当前的设置可能会出现两个问题:

  • 渲染线程需要所有鼠标更新,因此您需要在该线程和事件线程之间实现一个事件队列以跟踪所有这些更新,然后您实际上是在过滤鼠标事件以将它们分派(dispatch)给渲染:

    1. 在这些线程之间设置鼠标事件队列,然后将鼠标事件从输入队列推送到新队列。
    2. 让渲染在每一帧检查鼠标队列,并根据需要进行尽可能多的相机更新,或者更好 - 如评论中所建议的那样,如果可能,将所有这些事件组合在一个相机更新中。您应该尝试将该计算放在事件线程中,以减少渲染负载。
  • 或者渲染只需要最新的事件(就像它看起来正在做的那样),在这种情况下,您需要只用最新的事件更新互斥数据结构(从而减少对该互斥体的争用)。

回顾一下,这取决于相机更新功能的工作方式。鼠标事件,您可能必须将其更改为仅使用一组从所有相关事件累积构建的绝对坐标(也许您可以直接从鼠标事件结构中获取它们?),并且仅更新互斥数据 每个事件流一次。

<子> 注意:
您还可以检查其他引擎是如何做到的:IdTech2 (Quake I/II) 系列引擎是单线程的,但它们仍然是一个很好的灵感来源。每一帧,这些 一次处理所有鼠标输入。在帧渲染期间,调用第一个例程(In_MoveHandleEvents 或其他函数,具体取决于后端,请参阅 sys_* 文件)以检查所有事件并更新所有相关结构。当调用渲染代码 (R_RenderFrame) 时,这些结构不再存在争用。您可能希望通过确保渲染不会被一个或多个互斥体阻碍来“模拟”相同的行为。上面已经为鼠标输入描述了一种可能的解决方案,并且当然可以扩展以处理其他类型的输入设备。

关于c++ - 带互斥量的多线程输入不平滑(如预期),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23772325/

相关文章:

c++ - 将 `-std=c++11` 传递给 CMakeLists?

c++ - {c++} 程序在没有 visual studio 的计算机上运行时发送错误 0xc00007b 适当的 dll 到位

c++ - 在 Visual Studio Code 中运行代码时遇到问题

java - 保存对象时出错 : No Hibernate Session bound

android - 为 Android NDK 编译 C++11 源代码

c++ - 错误 : no type named 'Unary' in namespace 'ast'

C++ 使用 lambda 函数作为模板函数特化

c++ - 通过 Qt Creator C++ 向导创建 QLabel 的子类?

java - 当其他线程中数据会发生变化时实现多线程

java - 为什么虚拟线程(仅在短时间内 hibernate )会提高另一个线程的性能?