SDL 事件处理延迟

SDL Event Handling Delay

我在两次按键之间出现了严重的(1-2 秒)延迟。

这里是main.cpp(滞后的输入处理):

#include <iostream>

#include "src/Input/InputManager.h"
#include "src/Graphics/Display.h"

#define LOG(x) std::cout << x << std::endl;

using namespace Rambug;

int main(int arc, char** argv)
{
    Graphics::Display display(900, 600, "Rambug Engine Tester", true);
    display.createDisplay();

    SDL_Event event;
    Input::InputManager inputManager;

    // "Game" Loop
    while (!display.isClosed())
    {
        display.update();

        glClearColor(0.0f, 0.02f, 0.5f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        while (SDL_PollEvent(&event))
        {
            if (event.type == SDL_KEYDOWN)
            {
                std::cout << "Keydowner" << std::endl;
            }
            if (event.type == SDL_KEYUP)
            {
                std::cout << "Keyupper" << std::endl;
            }
        }

    //  inputManager.update();
    }

    display.destroyDisplay();

    system("PAUSE");
    return 0;
}

这是 Display.cpp,当我 运行 相同的代码(SDL_KEYDOWN、SDL_KEYUP)时,运行 完全没有任何延迟,我只是 运行 SDL_QUIT 在下面。

#include "Display.h"

namespace Rambug
{
    namespace Graphics
    {
        Display::Display(int width, int height, std::string title, bool log)
        {
            m_displayWidth = width;
            m_displayHeight = height;
            m_displayTitle = title;
            m_log = log;
            m_window = nullptr;
        }

        Display::Display()
        {

        }

        Display::~Display()
        {

        }

        void Display::createDisplay()
        {
            // Initialize SDL
            SDL_Init(SDL_INIT_EVERYTHING);

            // Setting attributes to our window
            SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
            SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
            SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
            SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
            SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
            SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

            // Create window
            m_window = SDL_CreateWindow((m_displayTitle.c_str()), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, m_displayWidth, m_displayHeight, SDL_WINDOW_OPENGL);

            // Error Check Window
            if (m_window == nullptr)
            {
                if (m_log)
                    std::cerr << "Window could not be created!" << std::endl;
            }
            else
            {
                if (m_log)
                    std::cout << "Window Created Successfully With SDL!" << std::endl;
            }

            // Create OpenGL Context
            m_glContext = SDL_GL_CreateContext(m_window);

            // Initialize GLEW
            glewExperimental = GL_TRUE;
            GLenum status = glewInit();

            if (glewExperimental)
            {
                if (m_log)
                    std::cout << "Glew Experimental: On" << std::endl;
            }

            // Error Check GLEW
            if (status != GLEW_OK)
            {
                if (m_log)
                {
                    std::cerr << "GLEW could not be initialized!" << std::endl;
                }
            }
            else
            {
                if (m_log)
                {
                    std::cout << "GLEW Was Initilized Successfully!" << std::endl;
                }
            }

            // Log OpenGL Version Number
            if (m_log)
            {
                std::cout << "Using OpenGL Version: " << glGetString(GL_VERSION) << std::endl;
            }

            m_closed = false;
        }

        void Display::destroyDisplay()
        {
            SDL_GL_DeleteContext(m_glContext);
            SDL_DestroyWindow(m_window);
            SDL_Quit();
        }

        void Display::update()
        {
            SDL_GL_SwapWindow(m_window);

            // Check for Input
            while (SDL_PollEvent(&m_sdlEvent))
            {
                if (m_sdlEvent.type == SDL_QUIT)
                {
                    m_closed = true;
                }
            }
        }

        bool Display::isClosed()
        {
            return m_closed;
        }
    }
}

我也尝试过使用输入管理器 class,但都是一样的:延迟。更新方法是我在 main.cpp 中调用的方法(我相信它被注释掉了)

#include "InputManager.h"
#include <iostream>

#define LOG(x) std::cout << x << std::endl;

namespace Rambug
{
    namespace Input
    {
        InputManager::InputManager()
        {

        }

        InputManager::~InputManager()
        {

        }

        void InputManager::keyPressed(unsigned int keyCode)
        {
            m_keyMap[keyCode] = true;
        }

        void InputManager::keyReleased(unsigned int keyCode)
        {
            m_keyMap[keyCode] = false;
        }

        bool InputManager::isKeyDown(unsigned int keyCode)
        {
            auto it = m_keyMap.find(keyCode);

            if (it != m_keyMap.end())
            {
                 return it->second;
            } 
            else
            {
                return false;
            } 
        }

        void InputManager::update()
        {
            while (SDL_PollEvent(&m_event))
            {
                switch (m_event.type)
                {
                case SDL_KEYDOWN:
                    LOG("SDL_KEYDOWN");
                    keyPressed(m_event.key.keysym.sym);
                    break;
                case SDL_KEYUP:
                    LOG("SDL_KEYUP");
                    keyReleased(m_event.key.keysym.sym);
                    break;
                }
            }
        }
    }
}

因此 InputManager 和 main.cpp 有重大延迟,而 Display.cpp 运行 完美。是因为我不能运行SDL_PollEvents两次吗?

Is it because I cannot run SDL_PollEvents twice?

您的问题不是我所期望的,但是,是的,运行 SDL_PollEvents 两次是个坏主意。 SDL 保留一个事件堆栈,该堆栈作为您的程序添加到 运行s。 SDL_PollEvents 从堆栈中弹出事件,直到它为空。结果,运行宁两个轮询循环,一个将删除另一个将看不到的事件。盲目运气(或执行瓶颈)将决定哪个循环更有可能看到任何特定事件发生。 (参见 http://wiki.libsdl.org/SDL_PollEvent)。

如果你真的想要 运行 两个轮询循环,你可以在默认情况下存储未处理的事件,并在每个循环后将事件列表推回SDL_PushEventhttp://wiki.libsdl.org/SDL_PushEvent

这就是说,我很惊讶你的事件 "get through" 在延迟之后:我希望它们消失。你在按住键吗?然后,您的 OS 键重复延迟可能就是您所看到的,之后事件队列在每个循环之间被淹没。您可能需要检查按键事件的重复标志:http://wiki.libsdl.org/SDL_KeyboardEvent

我会说这指向一个设计问题。你应该问问自己,为什么 Display 会委托 Game 结束?将这一事实与其他所有内容一起告知显示器不是更明智吗?

SDL keeps an event stack which is added to as your program runs. SDL_PollEvents pops events from the stack until it is empty.

我很确定它不是栈,而是队列。 SDL_PushEvent 的名称有点误导;它真正做的是将事件从 "wrong" 端推回队列。 (它可能在内部实现为堆栈,但它的行为是队列的行为。)

不过,Qualia 的回答是正确的方法。

但是,我不同意拥有多个事件循环就一定不好——它们可能非常有用。 2 个场景:

1) 您在主事件循环中捕获了类似调整大小事件的事件。如果随后的操作非常耗时,只要用户不断调整 window.

的大小,事件队列就会充满更多的调整大小事件。

在这种情况下,on 可以有一个单独的事件循环在耗时的重绘之后,它简单地循环直到找到第一个非调整大小的事件,然后推送最后一个它看到的 2 个事件返回队列,returns 控制主循环。这样您就可以丢弃累积的调整大小事件。 (使用 SDL_PeepEvents 函数可以更优雅地完成,尤其是当队列中有大量事件堆积时。)

2) 您的程序在捕捉到特定事件后采取的操作将触发其他事件,例如使用 SDL_RaiseWindow 时,这可能会触发大量焦点和 window 相关的后续事件,尤其是如果你有不止一个 SDL window。这里,有一个单独的事件循环可以用来处理这些触发的事件,特别是如果应该抑制对这些事件的响应。

关于延迟的问题,我也遇到过SDL_KEYDOWN事件的各种奇怪行为,通常事件被多次触发,绝对与OS键重复无关。这似乎只有在使用 SDL_PollEvent 时才会发生; SDL_WaitEventTimeout,即使超时延迟设置为“1”,似乎也抑制了这种奇怪的行为。有趣的是,SDL_KEYUP 事件没有表现出这种行为。