SDL_PollEvent()在空闲时会出现卡顿现象?

16

我使用SDL2在C++中拼凑了一个非常基本的游戏循环,但我注意到每隔几秒钟,即使什么也没有发生,SDL_PollEvent似乎异常缓慢。

我在每次循环时将我的deltaTime发送到控制台,当SDL_PollEvent滞后时,其差异约为100毫秒。我已经确认这是该函数的问题,通过移动我的计时器来确认,但我不确定在哪里进一步诊断问题。

我的循环:

while (!quit) {

    uint32_t startTime = SDL_GetTicks();

    while (SDL_PollEvent(&e) != 0) {
    std::cout << "Event: "<< e.type << std::endl; // Added later, read update
        if (e.type == SDL_QUIT) {
            quit = true;
        }
    }

    if (engine.AllowUpdate()) { // Restricts updates to every 20ms
        GameState::Update(); 
    }


    engine.rMan.BeginRender();
    //^v Literally just SDL_RenderClear and SDL_RenderPresent
    engine.rMan.FinishRender();

    engine.deltaTime = SDL_GetTicks() - startTime;
    std::cout << std::setw(10) << engine.deltaTime;
}

没有Vsync的控制台输出,请注意106。这是我的延迟:No Vsync console output

开启Vsync。请注意延迟后面的时间略短。不确定为什么:Vsync console output

我也发现即使我不在调试,这个问题还是会出现,并且至少有另一台机器没有此问题。希望能提出任何建议。

编辑1:尝试将通过队列进行的所有事件打印到控制台,以查看是否存在导致该问题的事件。已将打印行添加到上面的代码中。似乎在出现延迟的时间内没有事件触发,而我也处于空闲状态。

编辑2:根据要求,提供了一些可运行的代码,在VS2017上使用C++14和SDL2-2.0.9构建:

#include <iostream>
#include <SDL.h>

void InitSDL();
void BuildWindow();
void BuildRenderer();

SDL_Window* window;
SDL_Renderer* renderer;

int main(int argc, char* args[]) {
    InitSDL();
    BuildWindow();
    BuildRenderer();
    bool quit = false;

    uint32_t deltaTime = 0;

    while (!quit) {
        uint32_t startTime = SDL_GetTicks();

        SDL_Event e;
        while (SDL_PollEvent(&e) != 0) {
            if (e.type == SDL_QUIT) {
                quit = true;
            }
        }
        deltaTime = SDL_GetTicks() - startTime;
        std::cout << deltaTime << std::endl;

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);
        SDL_RenderPresent(renderer);
    }

    return 0;
}

void InitSDL() {
    Uint32 flags = SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS;
    SDL_Init(flags);
}

void BuildWindow() {
    window = SDL_CreateWindow
    ("SDL Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        800, 600, NULL);
}

void BuildRenderer() {
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
}

在组装这个内容的过程中,我注意到了一些东西:

1. 没有 SDL_RenderPresent 就不会出现卡顿。 经过再次检查,这似乎并不是事实,但是 SDL_RenderPresent 受到了卡顿的影响。

  1. 与卡顿同时发生的 deltaTime 增加似乎发生在 SDL_PollEvent 的某个地方,正如 deltaTime 被赋值的位置所示。

  2. 第一个 deltaTime 总是更长,尽管我怀疑这与某些默认事件在启动时触发有关。

编辑3:深入挖掘了一下。试图将我的 delta 赋值移到 SDL_RenderPresent 之外。

示例片段:

    SDL_Event e;
    while (SDL_PollEvent(&e) != 0) {
        std::cout << "Event: "<< e.type << std::endl;
        if (e.type == SDL_QUIT) {
            quit = true;
        }
    }

    uint32_t startTime = SDL_GetTicks();
    //SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    //SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);
    deltaTime = SDL_GetTicks() - startTime;
    std::cout << deltaTime << std::endl;

打开垂直同步后,控制台输出如下:Console output vsync delta on render

编辑4: 更多数据。看起来卡顿几乎每3000毫秒发生一次。我让控制台只输出大于50毫秒的增量。图像格式为:游戏循环周期数| deltaTime | SDL_GetTicks() just bad deltas

我也考虑过这可能是硬件问题,因为在另一台电脑上没有这个问题,而且我也下载了一些其他的开源SDL游戏,体验到了相同的卡顿,间隔为3000毫秒。我也在Windows 10和Windows 7上的相同硬件上看到了同样的问题。除非有人认为有必要,否则我不会发布我的规格,但我已经通过从我的GPU中删除该游戏运行时看到完全相同的问题来排除了我的专用GPU有问题的可能性。

编辑5: 看起来延迟与USB设备有关。SDL每3000ms左右进行所有设备的查找吗?

把GPU重新放回机器后,我注意到延迟显著下降,而且我发现之前和之后唯一的区别是我的USB耳机不再插入。

出于一时的冲动,我再次运行了循环,这次观察任何大于3ms的deltaTime。当我移除设备时,我观察控制台以寻找变化: USB device impact

真可喜!有点意外。没有插入USB设备,deltaTime一直保持在低于3ms的范围内。我测试的第二台机器是笔记本电脑,因此没有插入USB设备。我回去用同样的USB鼠标进行测试,如预期的那样,我看到每3000毫秒有明显的卡顿。

所以当前的问题是:USB设备如何导致这种卡顿?SDL每3000毫秒做什么与(a) USB设备和(b) SDL_RenderPresent()有关?


1
你认为是PollEvent导致了卡顿,而不是RenderPresent等其他因素吗?你能否提供一个最小完整示例,让其他人可以在他们的机器上验证(并将其编辑到问题中)?你确定不是printf/cout导致了减速? - keltar
我将deltaTime计时器移动到仅包围SDL_PollEvent循环,并获得了类似于控制台的结果。当我将计时器移动以排除SDL_PollEvent循环时,控制台显示稳定的delta,但我仍然遇到了卡顿。我会尝试整理一下我的代码并进行更新。 - Aaron Schultheis
@keltar 问题已经更新,附上了所需的代码。 - Aaron Schultheis
如果您全屏(例如适当的SDL_WINDOW_FULLSCREEN模式更改全屏,而不是桌面大小的无边框窗口),而不是窗口,是否会有任何变化? - genpfault
没有不同窗口类型的更改,但我即将在帖子中加入一些曲线球。看起来似乎与USB设备有关。 - Aaron Schultheis
1个回答

13

我仍不确定导致问题的确切原因,但它肯定与USB设备有关。插入的设备越多,延迟峰值就越长,几乎每隔3000毫秒就会出现。

我从SDL2-2.0.9回滚到了SDL2-2.0.8,问题已经解决。

编辑:在 https://hg.libsdl.org/SDL/rev/9091b20040cf 找到的更改集似乎可以解决这个问题,在SDL2-2.0.9中。


2
我建议提交一个回归错误报告。 - HolyBlackCat
2
对于那些在家跟踪的人,请访问以下网址:https://bugzilla.libsdl.org/show_bug.cgi?id=4417。 - genpfault
1
@genpfault,这个修复似乎解决了问题。我会将其添加到答案中。 - Aaron Schultheis
1
这与插入的USB设备数量有关?!我被这个问题困扰了很长时间。已经更改了语言和游戏库,但问题仍然存在。非常感谢您缩小了这个问题的范围! - Stephen
今晚我花了几个小时调试奇怪的卡顿,感觉自己快疯了。我把版本从 v2.0.9 回退到了 v2.0.7,问题也停止了。他们真的需要发布 v2.0.10,里面包含你的游戏手柄轮询修复程序。 - Sean Werkema
天哪,谁在理智的头脑下批准了2.0.9版本。要找到更荒谬的答案/解决方案,你必须追踪大量荒谬的错误。感谢OP的提示,这让我整夜都没睡好! - LilaQ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接