Jagnat
|
|
« on: October 26, 2014, 02:51:50 PM » |
|
Greetings! I'm currently struggling a bit with setting up a robust game loop. I've based my current code off of this: http://gafferongames.com/game-physics/fix-your-timestep/, but although my physics seems to be solid, my rendering tends to be a bit stuttery. I added a second counter to make it so that rendering doesn't happen as fast as possible. My render function takes an interval between 0 and 1, which represents how far in between an update we currently are, which I multiply the velocities of my objects by and add to their position. This cuts out a lot of jerkiness, but there's still a little visual stutter every time the update function is called. Any help would be appreciated. There is hopefully a simple solution; I'm kinda bad at this! Here is the code: prevTime = SDL_GetTicks(); uint32_t updateElapsed = 0; uint32_t renderElapsed = 0; uint32_t currentTime; uint32_t elapsed;
running = true; while (running) { currentTime = SDL_GetTicks(); elapsed = currentTime - prevTime; if (elapsed > 500) { std::cout << "Running Slowly! Elapsed time between last cycle is " << elapsed << "!" << std::endl; elapsed = 500; } updateElapsed += elapsed; renderElapsed += elapsed;
HandleEvents();
// targetUpdatePeriod: 33, or roughly 30 cycles per sec while (updateElapsed >= targetUpdatePeriod) { Update(); updateElapsed -= targetUpdatePeriod; }
// targetrenderPeriod: 17, or roughly 60 frames per sec if (renderElapsed >= targetRenderPeriod) { // Where are we between updates Render((float)updateElapsed / (float)targetUpdatePeriod); renderElapsed = 0; }
// CPU break SDL_Delay(1);
prevTime = currentTime; }
|
|
|
Logged
|
|
|
|
Layl
|
|
« Reply #1 on: October 26, 2014, 03:26:32 PM » |
|
You're updating at half the speed you're rendering, there's no reason to render the frame again if you didn't update, nothing changed. Not sure if this is the kind of stutter you're talking about.
|
|
|
Logged
|
|
|
|
Boreal
Level 6
Reinventing the wheel
|
|
« Reply #2 on: October 26, 2014, 03:46:43 PM » |
|
Instead of making the renderer predict where things are going to be, make it lag one update interval behind and interpolate between the two most recent states.
|
|
|
Logged
|
|
|
|
Jagnat
|
|
« Reply #3 on: October 26, 2014, 04:00:19 PM » |
|
You're updating at half the speed you're rendering, there's no reason to render the frame again if you didn't update, nothing changed. Not sure if this is the kind of stutter you're talking about.
From how I understand it, the render extrapolation should fix this potential issue. The reason I have a slower update rate than render rate is so that on crappy hardware I'll (hopefully) still get a constant amount of updates per second. The render interval on the other hand is meant to be adjustable, and still look smooth by extrapolating using an object's velocity. Correct me if I'm wrong! Instead of making the renderer predict where things are going to be, make it lag one update interval behind and interpolate between the two most recent states.
I'm not sure if I understand this correctly.. Are you saying that I should keep two physics states, and instead of extrapolating the potential position using the velocity, I should instead interpolate between the past state and the current state and then render that? Will that cause a noticeable delay in between input changing an object, and the change actually showing up on the screen?
|
|
|
Logged
|
|
|
|
Thomas Hiatt
|
|
« Reply #4 on: October 26, 2014, 04:11:40 PM » |
|
You could use SDL_GetPerformanceFrequency() and SDL_GetPerformanceCounter() instead of SDL_GetTicks() to potentially have more precision.
|
|
|
Logged
|
|
|
|
tjcbs
|
|
« Reply #5 on: October 26, 2014, 04:12:59 PM » |
|
I would very much like to understand this idea as well. Are people really keeping two sets of state for everything, and interpolating between the two?? It seems horribly wasteful and difficult to work with.
|
|
|
Logged
|
|
|
|
Boreal
Level 6
Reinventing the wheel
|
|
« Reply #6 on: October 26, 2014, 04:36:06 PM » |
|
I would very much like to understand this idea as well. Are people really keeping two sets of state for everything, and interpolating between the two?? It seems horribly wasteful and difficult to work with.
If you want to have an unlocked render frequency and a fixed update frequency, then yeah that's what you should do. And it's not two sets of state for everything, just things like position, rotation, color, etc. In terms of latency I've implemented this type of game loop before and there's really no difference.
|
|
|
Logged
|
|
|
|
ThemsAllTook
|
|
« Reply #7 on: October 26, 2014, 06:07:35 PM » |
|
I'm not sure if I understand this correctly.. Are you saying that I should keep two physics states, and instead of extrapolating the potential position using the velocity, I should instead interpolate between the past state and the current state and then render that? Will that cause a noticeable delay in between input changing an object, and the change actually showing up on the screen?
Yes and yes. Extrapolation can't ever be accurate if something changes in Update(), so interpolation is your only real option. For less latency, you could always update more frequently - is there a reason you've chosen 30hz? I usually update at either 60hz or 120hz. I would very much like to understand this idea as well. Are people really keeping two sets of state for everything, and interpolating between the two?? It seems horribly wasteful and difficult to work with.
Last time I implemented a system like this, I kept just a small subset of the previous frame's state for interpolation. All I needed for most cases was for each visible game object to keep a lastPosition variable (and maybe lastOrientation too if it can rotate). Not a huge inefficiency.
|
|
|
Logged
|
|
|
|
Jagnat
|
|
« Reply #8 on: October 26, 2014, 06:28:55 PM » |
|
Yes and yes. Extrapolation can't ever be accurate if something changes in Update(), so interpolation is your only real option. For less latency, you could always update more frequently - is there a reason you've chosen 30hz? I usually update at either 60hz or 120hz.
Last time I implemented a system like this, I kept just a small subset of the previous frame's state for interpolation. All I needed for most cases was for each visible game object to keep a lastPosition variable (and maybe lastOrientation too if it can rotate). Not a huge inefficiency.
No, 30hz was completely arbitrary. My thought process behind keeping it that low was that it wouldn't cause any problems on very slow machines, but as is evident by this topic, I don't have much experience with this kind of system . I guess when I think about it more, updating is never going to be slower than rendering in most games.
|
|
|
Logged
|
|
|
|
Sik
|
|
« Reply #9 on: October 26, 2014, 07:12:57 PM » |
|
Bah, reason is in the above post, but: I recall SDL1 tutorials insisting that you should strive for 30FPS because of the accuracy of SDL_GetTicks() (yes, it's really inaccurate, it doesn't update in steps of 1ms despite the values it returns). In practice you're better off with the high accuracy timers instead, although those are somewhat harder to setup since the update frequency varies among systems.
|
|
|
Logged
|
|
|
|
Layl
|
|
« Reply #10 on: October 27, 2014, 08:45:27 AM » |
|
A tricky note to be aware of with high frequency timers: If your game thread is switched between CPU cores, the time goes wacky. For some reason different cores tend to give a slightly different time with that.
|
|
|
Logged
|
|
|
|
Sik
|
|
« Reply #11 on: October 27, 2014, 10:41:38 AM » |
|
Modern computers have a dedicated timer independent of all cores actually, so that's not the issue. The problem is that buggy implementations can cause the returned value to be incorrect (in really bad cases going as far as making time go backwards). Generally the OS knows to work around this, although in some cases it may fail. The problem is that the alternatives have their own downsides as well (lower resolution, higher power usage if you try to increase accuracy to 1ms, etc.).
The real issue is that PC hardware has some really serious problems with timers in general.
|
|
|
Logged
|
|
|
|
Boreal
Level 6
Reinventing the wheel
|
|
« Reply #12 on: October 27, 2014, 11:24:23 AM » |
|
Is there ever any reason to be putting calls to the high performance timer outside of your main thread? I wouldn't worry about it.
|
|
|
Logged
|
|
|
|
Sik
|
|
« Reply #13 on: October 27, 2014, 02:49:14 PM » |
|
No, but the kernel can decide to run the thread in a different CPU core the next time it gets scheduled.
|
|
|
Logged
|
|
|
|
Boreal
Level 6
Reinventing the wheel
|
|
« Reply #14 on: October 27, 2014, 03:25:37 PM » |
|
No, but the kernel can decide to run the thread in a different CPU core the next time it gets scheduled.
I suppose that's true. I guess I wasn't thinking about sleep(), since normally I either just use V-sync or don't limit the frame rate at all.
|
|
|
Logged
|
|
|
|
|