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_PushEvent
:http://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
事件没有表现出这种行为。
我在两次按键之间出现了严重的(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_PushEvent
:http://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
事件没有表现出这种行为。