SDL事件处理延迟

SDL Event Handling Delay

本文关键字:延迟 事件处理 SDL      更新时间:2023-10-16

按键之间有一个主要(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;
        }
    }
}

我还尝试了一个Input管理器类,但这是同样的问题:延迟。更新方法就是我在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两次吗?

是因为我不能运行SDL_PollEvents两次吗?

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

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

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

我认为这是一个设计问题。你应该问自己,为什么Display委托游戏结束?把这一事实连同其他一切事情一起通知展览不是更明智吗?

SDL保留一个事件堆栈,该堆栈在程序运行时添加。SDL_PollEvents从堆栈中弹出事件,直到它为空。

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

不过,Qualia的答案是正确的。

然而,我并不认为拥有多个事件循环是件坏事——它们可能非常有用。两个场景:

1)在主事件循环中捕获类似resize事件的东西。如果后续操作非常耗时,那么只要用户继续调整窗口大小,事件队列就会被更多的调整大小事件淹没。

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

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

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