Advertisement

Strange SDL_PollEvent behavior causing jittery graphics

Started by April 24, 2011 05:00 AM
9 comments, last by avocados 13 years, 6 months ago
Hokay, so I'm using SDL with opengl piggybacked and am having some problems with mouse events and pollEvent acting unusual. I'll post the code, but I'm going to truncate it down to the essential parts so I don't paste 1000 lines of code :D
To be specific, (I think) the problem is that SDL_PollEvent likes to provide event information in 'bursts', when I want it in a stream. For clarification, see the cout's in the code and the bit of console output I paste at the end of it all.
There is also a nice video I took that illustrates the issue.




int Infinita::mainGameLoop(){
cout<<"general\n"; //for debugging purposes

if(isKeyPressed(SDLK_k)){ //this strange snippet is used to emulate that there is a mouse event every single time mainGameLoop loops
SDL_WarpMouse(320,lolx+=1);
if(lolx>100){
lolx=0;
}
}

while(SDL_PollEvent(&Event)){
if(Event.type == SDL_MOUSEMOTION){

mouseMovementEvent(Event); //this function adds a constant value to a rotation variable used to rotate camera

cout<<"there is mouse movement\n"; //for debugging purposes
}
}

movementHandler(1,1); //this function handles wasd movement, I highly doubt it is source of the problem


drawGLScene(); //renders everything and swaps buffers

return 0;
}


Now when this is run and I shake the mouse around, the console does this:




general
general
there is mouse movement
there is mouse movement
general
general
there is mouse movement
there is mouse movement
general
general
there is mouse movement
there is mouse movement


Now what this means is that rotation is going up pretty fast, and each individual increment in rotation isn't being rendered. This is my best guess at the cause of the jitteryness.

Here is video example. In first half of video, I am moving the mouse around and getting console activity as seen in above snippet. In second half, I am holding down the 'k' button, which makes it so there is (fake) mouse activity every time the code loops.

[media]
[/media]

However this is my best guess at what is wrong with the program after an hour or so of debugging, and it may be wrong and there is something catastrophically wrong somewhere else \o/. I want to "smooth" out the lack of mouseevents somehow, but there is no way to distinguish which 'lackings' of mouseevent are legitimate because the mouse stopped moving, and which are this strange behavior.


Here is a 7zip of the entire project: http://dl.dropbox.co...520/Infinita.7z

Thanks for your time.
have you tried it without outputting to console?
I've seen removing/adding console output change behavior before.
Advertisement

have you tried it without outputting to console?
I've seen removing/adding console output change behavior before.


I just removed the console window and there was no noticeable improvement.


I have a feeling this problem relates to SDL not getting the mouse input very often, but I don't have a shot in the dark as to figuring out why the mouse event goes off twice when it does.
I disagree. The way that SDL_PollEvents grabs events is the only proper way on Windows.

The issue here lies with you not calling SDL_Delay once per frame, I would bet. Calling SDL_Delay tells the operating system that your thread doesn't need to run for a supplied number of milliseconds, so it allows other threads to run. Otherwise your thread effectively "starves" the other thread of the CPU, which is no good. This is concurrency programming madness that doesn't really apply to the typical game, so all you need to know is that calling SDL_Delay once per frame will do the following:
1) Provide more consistency in receiving input.
2) Provide more consistency in music playback (read: http://forums.libsdl...opic.php?t=7178 )
3) Lower the CPU usage of your game.
4) Which in turn lowers the power usage of your game.
5) Which in turn lowers the heat generated by the processor.
6) Which will extend the life of the processor, and hardware in general, slightly.
7) And save money on electric bills.
8) And drain less battery on mobile devices and laptops.
Are you actually sure that this is an SDL problem? I'm also pretty unconvinced that adding a call to SDL_Delay will do anything useful.

Instead of adding a fixed amount to your scene rotation every time you get an input, you should scale it based on the amount of mouse movement. i.e.


float scaleFactor = 0.5.f; // rotation in degrees relating to one pixel of mouse movement.

heading += xChange * scaleFactor;


However, do note that you should also strive for frame-rate independence, so that rotation actually occurs at a set speed. On my computer, the camera movements in your .exe are all far too fast to be controllable.
So what's the magic number here before mouseevents are caught smoothly? I've set sdl_delay to 4, which is about 25 frames per second, but haven't noticed any difference compared to not using it.

If that isn't the solution, I think the problem may lie with how I actually handle the rotation of the camera. I've noticed the problem only occurs when I'm moving AND the camera is rotating, but does not happen when it's either one or the other.

Other than that, the only option is to use a smoother of some sort, I think. Right now the camera angle changes simply by using adding SDL_GetRelativeMouseState() times a speed multiplier, which comes off as sorta rudimentary considering.
Advertisement

I'm also pretty unconvinced that adding a call to SDL_Delay will do anything useful.


Perhaps not in this case. But it has fixed many issues before, and is generally good practice.


So what's the magic number here before mouseevents are caught smoothly? I've set sdl_delay to 4, which is about 25 frames per second, but haven't noticed any difference compared to not using it.

You shouldn't need more than one millisecond. I'm not sure what the default quantum is on Windows, but 1 millisecond should be more than enough time for other threads to get their chance. Do note that the end result may actually be more than 1 millisecond, though.

If that isn't the solution, I think the problem may lie with how I actually handle the rotation of the camera. I've noticed the problem only occurs when I'm moving AND the camera is rotating, but does not happen when it's either one or the other. [/quote]
You should have said so before! Unfortunately, I can't help you with this if that's the issue.
Still having this problem, I think the issue lies with how I'm drawing the scene.

Here's the pseudocode of the draw function, this is called every time maingameloop runs.




glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

glRotatef(zheading,1.0f,0,0); // Rotate Up And Down To Look Up And Down
glRotatef(sceneroty,0,1.0f,0); // Rotate Depending On Direction Player Is Facing
glTranslatef(xtrans, ytrans, ztrans); //move the camera to new position


glBindTexture(GL_TEXTURE_2D, stuff); //this chunk of code is simplified without the for loop in actual implementation
glBegin(GL_QUADS);
glNormal3f( stuff );
glTexCoord2f( stuff );
glNormal3f( stuff );
glTexCoord2f( stuff );
glNormal3f( stuff );
glTexCoord2f( stuff );
glNormal3f( stuff );
glTexCoord2f( stuff );
glEnd();

SDL_GL_SwapBuffers();


Do you think there's anything in there that may be causing this problem to occur?
I've used the following program and SDL generates a single mouse event per frame for me:


#include <iostream>
#include <cstdlib>

#include "SDL.h"

int process(int position, int &velocity, int min, int max)
{
position += velocity;
if(position < min)
{
velocity = -velocity;
return min;
}
else if(position > max)
{
velocity = -velocity;
return max;
}
return position;
}

int main(int argc, char **argv)
{
if(SDL_Init(SDL_INIT_VIDEO) < 0)
{
std::cerr << SDL_GetError() << '\n';
return 1;
}
std::atexit(&SDL_Quit);

SDL_Surface *screen = SDL_SetVideoMode(800, 600, 0, 0);
if(!screen)
{
std::cerr << SDL_GetError() << '\n';
return 2;
}

int x = 0, y = 0;
int vx = 1, vy = 1;
const int SIZE = 40;
bool running = true;
bool warping = false;
while(running)
{
if(warping)
{
SDL_WarpMouse(x, y);
}

std::cout << "Loop\n";
SDL_Event event;
while(SDL_PollEvent(&event))
{
if(event.type == SDL_MOUSEMOTION)
{
int mx = event.motion.x;
int my = event.motion.y;
x = process(mx, vx, SIZE, screen->w - SIZE);
y = process(my, vy, SIZE, screen->h - SIZE);
std::cout << "Mouse motion: " << mx << ", " << my << std::endl;
}
else if(event.type == SDL_KEYDOWN)
{
switch(event.key.keysym.sym)
{
case SDLK_ESCAPE:
running = !running;
break;
case SDLK_k:
warping = !warping;
break;
}
}
else if(event.type == SDL_QUIT)
{
running = false;
}
}

SDL_FillRect(screen, 0, 0);
SDL_Rect dest = {x, y, SIZE, SIZE};
Uint32 colour = SDL_MapRGB(screen->format, 0xff, 0, 0);
SDL_FillRect(screen, &dest, colour);
SDL_Flip(screen);
}
}

I don't see why you are getting anything other than a single mouse motion event per frame when you are using SDL_WarpMouse().

In any case, with real input events you must deal with no or multiple mouse motions in a single frame, even during "smooth" mouse motion.

To be specific, (I think) the problem is that SDL_PollEvent likes to provide event information in 'bursts', when I want it in a stream.
[/quote]
SDL_PollEvent() doesn't do any special batching of events - it just gives you all the events it knows about. This is normally what you want: SDL is reporting all available data since you last polled.

You don't really want a stream of events, what you want is to smooth the user's mouse motion somehow. If your game loop is polling faster than I move the mouse, you shouldn't interpret the motion gaps as the mouse being stationary. With the code I just posted, I get 3 or 4 frames between discrete mouse events if I wave my mouse around consistently.
thanks, I compared my mouse code to yours, and they were basically similar. I just took a video of the glitch in slow motion, and at this point I'm 90% sure the problem lies with my opengl initialization settings or something not directly associated to sdl's mouse movement stuff.

This topic is closed to new replies.

Advertisement