简体   繁体   中英

SDL Event Handling Delay

I am getting a major (1-2 second) delay between key presses.

Here is main.cpp (the lagging input handling):

#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;
}

Here is Display.cpp, which runs PERFECTLY without any delay when I run the same code (SDL_KEYDOWN, SDL_KEYUP) I just run SDL_QUIT down there.

#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;
        }
    }
}

I also tried experimenting with an Input manager class, but that was the same deal: delays. The update method is what I would call in main.cpp (I believe that it is commented out)

#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;
                }
            }
        }
    }
}

So InputManager and main.cpp have major delays, while Display.cpp runs perfectly. Is it because I cannot run SDL_PollEvents twice?

Is it because I cannot run SDL_PollEvents twice?

Your problem isn't what I'd expect, but, yes, it's a bad idea to run SDL_PollEvents twice. SDL keeps an event stack which is added to as your program runs. SDL_PollEvents pops events from the stack until it is empty. As a result, running two polling loops, one will remove events which the other will then not see. Blind luck (or execution bottlenecks) will determine which loop is more likely to see any particular event occur. (See http://wiki.libsdl.org/SDL_PollEvent ).

If you really want to run two polling loops, you can store unhandled events in your default case, and push the list of events back after each loop with SDL_PushEvent : http://wiki.libsdl.org/SDL_PushEvent

This said, I'm surprised that your events "get through" after a delay: I would expect them to vanish. Are you holding the keys down? Then, your OS key-repeat delay might be what you're seeing, after which the event queue is being flooded between each loop. You might want to check the repeat flag of the key event: http://wiki.libsdl.org/SDL_KeyboardEvent

I would say this points to a design problem. You should ask yourself, why does the Display delegate the Game ending? Would it not be more sensible to inform the Display, along with everything else, of this fact?

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

I am fairly sure that it is not a stack, but a queue. The SDL_PushEvent has a somewhat misleading name there; what it really does is shove the event back into the queue from the "wrong" end. (It might be implemented internally as a stack, but it's behaviour is that of a queue.)

Still, Qualia's answer is the right way to go.

However, I don't agree that it is necessarily bad to have multiple event loops - they can be very useful. 2 scenarios:

1) You catch something like a resize event in your main event loop. If the ensuing operations are very time-consuming the event queue will be flooded with more resize events as long as the user keeps resizing the window.

In this case, on can have a separate event loop after the time-consuming repaint, which simply loops until it finds the first non-resize event, then pushes the last 2 events it saw back into the queue, and returns control to the main loop. That way you can discard the accumulated resize events. (It can be done more elegantly using the SDL_PeepEvents function, especially if there is a really huge pile-up of events in the queue.)

2) The action your program takes after catching a specific event will trigger other events, like when using SDL_RaiseWindow , which may trigger a multitude of focus and window related subsequent events, especially if you have more than one SDL window. Here, having a separate event loop can be used to deal with these triggered events, especially if the response to these events should be suppressed.

On the issue of delays, I have also encountered all sorts of strange behaviour with the SDL_KEYDOWN event, usually the event being triggered multiple times, and definitely not related to OS key repetition. This seems to occur only when using SDL_PollEvent ; SDL_WaitEventTimeout , even with the timeout delay set to just '1', seems to inhibit this strange behaviour. Interestingly, the SDL_KEYUP event does not exhibit this behaviour.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM