Skip to main content
gamedev.net
🔒 Locked

[SDL] Very strange event polling and vsync issue

Started by dc443 34 replies 15.3k views
Original Post
dc443
dc443
I am using SDL and OpenGL along with Box2D in my project. I would prefer to enable vsync, because it tends to reduce CPU load by at least 50%, while at the same time ensuring that there is never any graphical "tearing". What's not to like, right? Plus anyone can easily override the setting in their video drivers should they be bothered by the mouse lag that results (and thats an entirely different issue altogether). Well, there's this particularly strange issue that I'm getting. Whenever you move the mouse in the window WITHOUT ANY BUTTONS HELD DOWN, the SDL_PollEvent(SDL_Event*) and SDL_PumpEvents() functions often take over 13 milliseconds to execute. This is about the same time as an entire frame, so the animation becomes stuttery and in my case even the physics simulation is forced to slow down as a result. Okay, so I thought maybe this is just what it takes to figure out how far my mouse moved. This COULD be understandable since my mouse does refresh at 500Hz. But what's screwed up is, if I hold down a mouse button and THEN move it, there is now slowdown. Holding down of the four buttons (left click, middle click, right click, side button) works. I also know that SDL does in fact receieve all of the mouse motion data because if I am DRAGGING something along in my sim by holding down a button, it will run perfectly smoothly, then once I let go of the button, I have to stop moving the mouse or move it out of the screen to make the stuttering stop. Note that when I say stuttering, it's more like instead of each frame being displayed for exactly 1/60 second, it becomes something like one frame for 1/32, and another for 1/480... sure, average fps is 60, but visually you get 30fps. Fraps will display 60fps because it averages the value over several frames. You can try it for yourself and see if you can reproduce this issue: http://sourceforge.net/projects/box2dsandbox/ Get the 0.15 download. The stuttering effect is easiest to notice when you pick up the concave random-looking shape, and toss it somewhere. while its moving and spinning, move your mouse. If you look closely, you will also notice the entire simulation slow down as well (less so if you do not run 60Hz refresh, in fact all of the effects are reduced at higher refresh rates, but I can very easily notice it at 75Hz on my screen). This issue is not reproducible when vsync is off. Anyone got any ideas? EDIT: This appears to only affect Nvidia hardware on certain driver versions and I originally thought it was purely an SDL bug. You may be unable to reproduce it with my link above. SOLUTION:
Quote:
Original post by bobmitch SOLVED!!! It's a quirk of nvidia's slightly experimental drivers I think. After puzzling over my code and how opengl works, I narrowed it down to either an SDL bug or a driver bug. The more I fiddled with various delays in my main loop, the more it seemed like a threading issue... but my code is running in a single thread right now. Solution: Disable threaded optimization in the advanced settings in the Nvidia control panel, it looks like it's set to Auto by default.
[Edited by - dc443 on September 17, 2008 7:06:40 PM]
Kylotan
Kylotan
I'd like to see the event loop.
dc443
dc443
		while (SDL_PollEvent(&event))		{			if( event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE ))                        	quit = true;			else if (event.type == SDL_MOUSEBUTTONDOWN)			{				...			}			else if (event.type == SDL_MOUSEBUTTONUP)			{				...			}						else if (event.type == SDL_MOUSEMOTION)			{				...			}			else if (event.type == SDL_KEYDOWN)			{				...			}		}


When I did timings I started the timer right before the while loop, and put the timer.stop() right in the beginning of the loop, like this:

		timer.startTimer();		while (SDL_PollEvent(&event))		{			printf("%f ms\n",timer.stopTimer()*1000.);			...



Also, if I comment out my entire event handling loop and use SDL_PumpEvents() instead, and use SDL_GetKeyState() and SDL_GetMouseState(), the exact same thing happens.
dc443
dc443
Okay, did some more testing and it definitely looks like this has something to do with VSync.

That's because when I have my refresh at 75Hz, polling events causes it to lag for 13.3 ms on average. 13.3333ms is exactly 1/75 second.

Similarly when I set my refresh to 60Hz, the lag increases to 16ms, and 1/60 s == 16.6667ms.

I still have no idea what might be causing this though. I mean, it works totally fine when a mouse button is pressed down. What is up with that!?!
Kylotan
Kylotan
Where do you do your rendering? Also, which OS is this? And which rendering driver are you using?
dc443
dc443
I'm on windows. XP 32 bit.

while (!quit)	{		//clear		glClear(GL_COLOR_BUFFER_BIT);			//drawing 		b2Body* body = world->GetBodyList();		while(body)		{			debugDrawBody(body, d);			body = body->GetNext();		}		//end drawing 				//swap buffers		SDL_GL_SwapBuffers();		//SDL Event Handling		while (SDL_PollEvent(&event))		{			...		}				...		// advance physics	}


Inside debugDrawBody() are standard gl draw functions. I'm pretty sure there are no problems in there.


I've moved the drawing and swapping of buffers to different spots in the loop (before or after the event polling) to no avail.

Also what do you mean by rendering driver?
rnodal
rnodal
I registered in this forums just for this question. I am having the same issue. I have a spinning pyramid and it will slow down when I move the mouse but when I move the mouse with some of the buttons pressed it will not happen. In my case I have a function that returns the time elapsed between frames and some times it gives me zero. I don't know how that's possible.

float getDeltaTime(void){    // return delta time    return (this->deltaTime / 1000.0f);}   // getDeltaTime



deltaTime some times is zero. I hope some one has a clue for this issue.
================================My project:http://www.egengine.com
rnodal
rnodal
I think I solved the problem. At least in my case.
All I had to do was to get the time at the beginning of the frame then process all events then at the end of the loop get the time again and everything runs smooth again. I hope this helps you some how.

    //  main loop    for(;;)    {        // get time before frame        this->beforeFrameTime = (float)SDL_GetTicks();        // handle events        this->handleEvents();                // render stuff        // .............        // get time after frame        this->afterFrameTime = (float)SDL_GetTicks();        // time since last frame        this->deltaTime = this->afterFrameTime - this->beforeFrameTime;}



-r
================================My project:http://www.egengine.com
dc443
dc443
As I understand it, what will happen is, normally the game loop finishes and calls Swap Buffers, which waits 16 or so miliseconds until swapping the buffers and allowing the loop to continue. With the SDL bug, sometimes the events functions will hog up 16 seconds, which means that once it reaches swap_buffers it will instantly swap the buffers and return. The result? One frame displayed for 1/30 second and one frame displayed for almost no time at all. I guess your timer isnt precise enough to catch the super short frame, which is probably going past in under 1 milisecond.
dc443
dc443
Wait, you fixed it by changing your timing functions? My thing does it even if I remove those entirely.
rnodal
rnodal
Could you please elaborate on how is that my timer is nor precise enough. I would love to improve it. :)
================================My project:http://www.egengine.com
rnodal
rnodal
Do yo mind showing where you get your time? Maybe I can understand you better. All I did was to get the time before rendering a new frame at the very beginning as opposed to after processing events.
================================My project:http://www.egengine.com
dc443
dc443
Well I use this method to get high precision time:

http://cplus.about.com/od/howtodothingsi2/a/timing.htm

I think it's precise to less than one microsecond or something like that.
rnodal
rnodal
Well, I did more testing and I still get some frames where the delta time is zero but the animation looks very smooth. I had the same problem that you have. Specially when I had multi sampling on the animation would really slow down big time when you could really notice it. In my last run I go delta to be equal zero at frame, 0, 1, 2, 3704 and I was moving the mouse without any other button or key pressed. So maybe I have not solved the problem but at least things are better. :)

================================My project:http://www.egengine.com
nullsquared
nullsquared
Move SDL_GL_SwapBuffers() to the end of the loop. You're basically waiting for the rendering to finish (due to VSync), *then* polling for events. Instead, you want to issue rendering commands, poll for events, do physics, and *then* swap the back buffers.
rnodal
rnodal
I actually have SDL_GL_SwapBuffers() as the last thing and still I get the same problem. They way that I went about it was to prevent deltaTime from being zero. Also having an internal fps limiter helps. If you have one then you then you can make all frames take the same time to render even if they were render quicker.

// regulate fpsif((this->maxFPS > 0) && ((SDL_GetTicks() - this->beforeFrameTime) < (1000.0 / this->maxFPS))){     // wait until the desired time has passed     SDL_Delay((Uint32)((1000.0 / this->maxFPS) - (SDL_GetTicks() -    this->beforeFrameTime)));}
================================My project:http://www.egengine.com
dc443
dc443
Yes but I'm timing the actual function call that handles events, and it for some reason gets delayed about the same amount as the vsync delay. I know for a fact that I am not timing the vsync delay as well.
rnodal
rnodal
Then only reason that I can think of that would make your animation slow down is when the delta time is zero so if you have something like:

dy -= speed * dt;


Just because you request vsync it does not mean you are going to get it. So I'm kind of confused now. I thought the problem was that when you moved the mouse things would slow down but when you pressed the mouse buttons things would be normal. I had the same issue and I fixed by doing what I have described so far.
================================My project:http://www.egengine.com
Slather
Slather
Hi, I'm using SDL, opengl, and box2d as well and have never had this problem, but I can tell you how I do things and what I do differently from what you've told us.

I've turned v-sync off because if not, the screen updates sync with the monitor refresh rate, meaning that the rest of your game loop will never run faster than that limit. If you're concerned about CPU usage with v-sync off, try puting an SDL_Delay(1) call after you swap buffers. This yields the cpu to the OS, which mostly idles, and you'll see your task manager cpu percentage lower.
This is good for windowed mode, and saving battery on laptops.

As for the slowdown, are you doing anything that affects box2d in you mouse-motion event handling?

This is the order of calls I make:
time update
Update/Logic
Render
swap buffers
input task

Also, if you do turn v-sync off, you'll want to decouple the physics step from your framerate so the game doesn't slow down or speed up with the framerate.
I use Glenn Fiedler's timestep fix with the final touch and it works beautifully.

I hope that helps in some way.

dc443
dc443
My physics is already decoupled, and I have interpolation working great and everything. It's beautiful.. but only when i'm not moving the mouse.

Oh well, I guess I'll just leave vsync off...

Topic Locked

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

Sign in to reply to this topic.