Skip to main content
gamedev.net
🔒 Locked

Event Queue Unresponsive (SDL)

Started by GenuineXP Jan 23, 2007 at 7:45 PM 5 replies 2.5k views
Original Post
GenuineXP
GenuineXP
When getting events from SDL's event queue, things get pretty unresponsive when processing gets intense. I'm not sure what's going on, but key pressed and mouse movements aren't always picked up. For instance, I may press the space bar repeatedly but only three quarters of those presses are actually picked up. I'm not sure if this is a common problem or not. I'm building an event-driven system atop SDL (and other libraries; everything's abstracted). I think the way I'm pushing events back onto the queue may be part of the problem (it looks like a slow way to handle things to begin with. Take this function for example:
void SDLKeyboard::onCycleBegin(CycleEvent<Application>* e)
{
	static SDL_Event events;
	static vector<SDL_Event> ignored;
	while (SDL_PollEvent(&events))
	{
		switch (events.type)
		{
		case SDL_KEYDOWN:
			notifyKeyPressed(events.key.keysym.sym);
			break;
		case SDL_KEYUP:
			notifyKeyReleased(events.key.keysym.sym);
			break;
		default:
			ignored.push_back(events);
			break;
		}
	}
	for (vector<SDL_Event>::iterator i = ignored.begin(); i != ignored.end(); ++i)
		SDL_PushEvent(&(*i)); // IMPORTANT: Keep unhandled events in the queue.
	ignored.clear();
}
onCycleBegin() is implemented by CycleListener objects that listen for game cycles from Application objects. Objects that need to process events from SDL (like SDLKeyboard) poll the event queue and place any unrelated events back so that other objects can handle them if and when they poll for events. In this case SDLKeyboard checks for key events during the beginning of every game cycle. notifyKeyPressed() is a private utility function that handles the call to broadcastKeyPressed() which broadcasts a KeyEvent to registerd listeners (and likewise for notifyKeyReleased()). Could this be what's slowing things down? Is this even doing what I think it does, or am I losing/ignoring events in the queue this way? I have no problems when there isn't much being done, but when I stress things (i.e., create a bunch of objects and do a lot of number crunching) events seem to slip right through the system unnoticed. Any help is appreciated. Thanks.
common
common
I've never used SDL, but I assume the concepts you're dealing with are pretty much universal and not necessarily SDL-specific. Now, with that (possibly dangerous!) assumption:


Quote:
I may press the space bar repeatedly but only three quarters of those presses are actually picked up.

I know in the Windows message pipeline windows gives you two different messages for keyboard input: WM_KEYUP and WM_KEYDOWN. Now if there is something equivalent to those messages in SDL you may have a source of slowdown here if you are doing something like:
switch(messageFromWindow) {	case keyIsDown:		// register a key down event		notifyKeyPressed(messageFromWindow.keyParamter);		break;	case keyIsUp:		// register a key up event		notifyKeyReleased(messageFromWindow.keyParamter);		break;}

This may cause slow down because your event queue will be bombarded with KeyDown events, especially if you are holding down a key. (and even if you aren't, because even tapping a key will be registered for multiple cycles). So, to combat this, one solution is to implement a key array of 256 values and do something like:
//...initbool keys[256]; // array of 256 ascii key codesmemset(keys, false, sizeof(keys)); // set all keys to false//....switch(messageFromWindow) {	case keyIsDown:		// if key is not already down		if (!keys[messageFromWindow.keyParamter]) {			// set to down in array, and send off a notification			keys[messageFromWindow.keyParamter] = true;			notifyKeyPressed(messageFromWindow.keyParamter);		}		break;	case keyIsUp:		// you don't really need the if(keyAlreadyUp) check here, cuz		// you'll only get this message if a key has been released		// and there's no way you can "hold" a released key		// set to up in array, and send off a notification		keys[messageFromWindow.keyParamter] = false;		notifyKeyReleased(messageFromWindow.keyParamter);		break;}

This way you won't be flooding your queue with unneeded keyDown events, but instead only one on a keyDown and the key's corresponding keyUp. Now, I don't know how SDL handles this, but also know that there may not be a corresponding keyUp guaranteed if the user is holding down a key and then clicks out of the window's focus. You'll have to handle for that however you'd like to, I just clear the key array when the window is out of focus and send out keyUp events for every key that was down when the window went out of focus.

Hopefully you'll notice a boost in responsiveness as a result of that, if something like it hasn't already been implemented by you or already handled for by SDL.

Quote:
Could this be what's slowing things down? Is this even doing what I think it does, or am I losing/ignoring events in the queue this way?

The only way to know for sure what your application is doing is to step through it with your debugger. Just place a break point in SDLKeyboard::onCycleBegin in the default handler within the switch statement and also a conditional break point at the beginning of SDLKeyboard::onCycleBegin (for when event count > 3 or something) so you can see exactly what happens when there is more than one event, and exactly what happens when an event needs to get pushed onto the "ignore" list and exactly how it's handled from there.



Also, in my event system (which is actually quite a new development, and as such should not be considered the exact "right" way to do things) I've done everything from the console, so all of my events for debugging purposes output a message to the console specifying where they're getting handled, what type of event they are, and what parameters were sent with them. You should consider doing something like this as it can save you alot of stepping through the debugger and rummaging through a complicated Events structure every time you're checking if an event is going to the right place.

But really, you should track an event through the pipeline and really be sure you know exactly what every scenario will bring about, so you have a good idea of how your system works, including any sort of little possible bottlenecks on the way.

Such as this:
for (vector<SDL_Event>::iterator i = ignored.begin(); i != ignored.end(); ++i)		SDL_PushEvent(&(*i)); // IMPORTANT: Keep unhandled events in the queue.	ignored.clear();


Instead try checking the "ignored" size, it might save some time (or maybe not, since i will be equal to the ignored.end() already, right? but regardless I think it's probably a good idea to skip over all of it if it's just not necessary):

if (ignored.size() > 0) {	for (vector<SDL_Event>::iterator i = ignored.begin(); i != ignored.end(); ++i)		SDL_PushEvent(&(*i)); // IMPORTANT: Keep unhandled events in the queue.	ignored.clear();}


Keep me posted if anything I said worked (or didn't!) I'm just getting into all this event-driven stuff myself, I hope I could help with this post, and I'll certainly try to help with any future issues that arise. [smile]

Oh, and I know I didn't go so in depth in to all of the possible causes for your lack of responsiveness, I just kinda wanted to offer these initial suggestions to get them out of the way so I could further delve into the problem if it persists. Basically attacking it from the surface downward I suppose you could say. Again, I really hope I haven't totally missed the ball or anything, as I have never worked with SDL in the past, but hopefully you'll get something useful out of this freakishly long post!

[Edited by - common on January 23, 2007 10:27:41 PM]
GenuineXP
GenuineXP
Thanks for the great reply, common. I can't work on this until later today, but I'll look into your suggestions and let you know if they helped. For once thing, I think I'll log input events as you suggested to see just what's happening.

Also, unless key repeating is enabled, SDL generates only one keydown or keyup event when a key is pressed (and held), so I don't have to throw any extra events away. (I think you hinted at this.)

Thanks again! :-)
Kylotan
Kylotan
I would think that forever pushing things back onto the queue is going to be slow. I'd prefer to have a dedicated event dispatcher that pulls every event from the SDL queue and sends it to the appropriate event-handling object. Otherwise you're pulling and pushing each event X times, once for every object that handles them, and maybe even leaving it in the queue at the end when you were supposed to discard it?
GenuineXP
GenuineXP
The Application class handles events left dangling in the queue. Still, I agree that having a more central event processing system would be better rather than having individual objects examine the queue and place unrelated events back. That may be where I take things next.
GenuineXP
GenuineXP
I seemed to solve the problem. I was using SDL incorrectly. Rather than using SDL_PollEvent(), I'm manually pumping the event queue (via SDL_PumpEvents()) and checking for events I'm interested in with SDL_PeepEvents(). With this method, there's not need to push unrelated events back on the queue. Having to do that should have been a sign of bad design considering that the SDL documentation says that SDL_PushEvent() is usually used to put user events on the queue or to simulate input events. :-)

So, SDLKeyboard::onCycleBegin() now looks like this:

void SDLKeyboard::onCycleBegin(CycleEvent<Application>* e){	static SDL_Event events;	memset(&events, 0, sizeof(events));	while (SDL_PeepEvents(&events, 1, SDL_GETEVENT, SDL_KEYDOWNMASK | SDL_KEYUPMASK) > 0)	{		switch (events.type)		{		case SDL_KEYDOWN:			std::cout << "Caught SDL_KEYDOWN\n";			notifyKeyPressed(events.key.keysym.sym);			break;		case SDL_KEYUP:			std::cout << "Caught SDL_KEYUP\n";			notifyKeyReleased(events.key.keysym.sym);			break;		default:			break;		}	}}

Much cleaner. SDLApplication fires off CycleEvents and pumps the event queue before it does. Objects can check for events they need, and when control passed back to SDLApplication it checks for any exit events, does a few more cycle operations under the hood, and starts the loop again, pumping the queue.

Missing key events don't seem to be an issue anymore. :-) Thanks for the help.
Aldacron
Aldacron
Quote:
Original post by GenuineXPObjects can check for events they need


This was the root of your problem. SDL_PollEvents would have given you no trouble had you set up a central event dispatcher that sent events out to interested objects, rather than having each object poll for specific events. Plus, you wouldn't have needed to push events back into the queue. This, IMO, would actually be a cleaner design than what you are using now. It separates the details of event processing from the game code, keeping it in a central location. It may seem logical to delegate event processing to device-specific objects, but it's more practical to keep it all in one place.

But, whatever works for you :)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.