Sign in to follow this  
Frank Force

Expert question: How to get a better time delta?

Recommended Posts

ApochPiQ    23003

Using a "fixed" timestep is not about expecting your timer to fire ever N milliseconds on the nose. Even when running fixed-step simulation you need to measure the [b]actual[/b] wall clock time that elapses and use [i]that[/i] to advance your simulation. Otherwise you will get clock drift and indeterminacy over time.

 

So yeah, you're basically right. I'm not sure what the question is, though, unless you're just looking to make sure you understand the situation correctly (which it seems you do).

Share this post


Link to post
Share on other sites
Frank Force    198

How do you measure the actual wall clock time that elapses?  I think that's what I'm already doing that I found was wrong. So, there are two different deltas here, the wall clock delta and the fixed time step delta.  The problem I'm talking about is that the measured wall clock is not ever an exact even multiple of the vsync interval even though it should be in order for the rendered frames to be interpolated properly.  Can you see how that would be an issue?

Share this post


Link to post
Share on other sites
ApochPiQ    23003

I don't see why you need some theoretical magic time value for anything to work correctly. Run your simulation at [i]as close to[/i] fixed as you can, and you're done.

 

You will never get 100% precisely perfect timings on a consumer OS, they simply aren't designed to give you that guarantee. (Look at process timeslice quantization rules for example.) Design your code accordingly.

Share this post


Link to post
Share on other sites
Frank Force    198

You say it's not possible to get perfect timing, but you also say that doesn't really need that to work correctly.  I agree that a game engine will still work mostly correct without the stuff I'm talking about, every game engine I've ever seen has certainly worked fine without it.  But I don't understand why people are willing to settle for mostly correct, if we could get perfect timing that would be better right?  I mean, it's not theoretical or magical, monitors have a very specific vsync interval that images are displayed at.

 

The whole point of what I'm trying to talk about here is that you can get perfect timing that is 100% precise and this is how you do it. The only sacrifice is a small amount of unavoidable latency which is a small price to pay for perfect timing and smoothness of motion.  With triple buffering it doesn't really matter what the os does or how much fluctuation between deltas there is. What about my plan makes you think it won't yield perfect timing?

Share this post


Link to post
Share on other sites
Hodgman    51226
I've never thought of / tried this, but it does make sense in a vsync'ed system to only use deltas that are a multiple of the refresh rate...

When running at 60hz, what kind of deltas were you measuring befor implementing this system / what size correction deltas are you applying?
How is your timer itself implemented? Do you use 64bit absolute time as much as possible over 32bit deltas?

Last I checked in my game, when vsync'ed my deltas were 16.66667, which seems correct, but I'm still keen to check out your correction when I get a chance and see if it helps.

Share this post


Link to post
Share on other sites
ApochPiQ    23003

My point is that your corrected timing code [i]is fine[/i].

 

You will [b]never[/b] get guaranteed time deltas without a hard-realtime OS. The kernel is free to schedule some other process for a quantum, which means you don't get a chance to even run when your timestep "should" be firing. This is why you get timing variances like you describe, and in a multitasking OS, it's actually a very good thing.

 

This compensation is actually relatively common in my experience; basically you're just eating the time variance by amortizing it across the subsequent frame(s).

Share this post


Link to post
Share on other sites
Frank Force    198

Hodgman - 

 

My deltas don't vary much, generally between 16 and 17 or so but can be much less/greater. Also when triple buffering multiple updates must sometimes happen during the same vsync interval, otherwise you aren't really buffering anything, right? My engine uses a double for the timer, but that shouldn't affect this issue.  Changing to a fixed point solution or increasing the timer accuracy won't get rid of the fluctuation. I am curious about why your measured deltas don't vary, have you tried logging them to a file?

 

ApochPiQ - 

 

Thanks that helps a little.  I like to think of it as buffering the time delta.  I figured this was a common thing that I must have never came across or seen mentioned anywhere because it seems pretty damn important.  That's why I'm asking if I am over thinking things or maybe just using the wrong terminology.

Share this post


Link to post
Share on other sites
Hodgman    51226

I am curious about why your measured deltas don't vary, have you tried logging them to a file?

Yeah I was mistaken, when synced to display at 60Hz, the delta log looks like:
...
0.016761
0.016591
0.016652
0.016698
0.016710
0.016666
...
Which over time seems to average out to the correct value, but yes, there is jitter.
[edit]They actually don't average out to 1/60 - they're generally higher than 1/60[/edit]
 
I am running my physics on a fixed time step of 60Hz (with some internal parts of the physics taking 10 sub-steps for each 1 physics tick), so it seems that occasionally I should actually be doing no physics updates for a frame, followed to two the next frame. Using a minimum delta of 1/60th (and the correction buffer) might smooth this out. Thanks. Edited by Hodgman

Share this post


Link to post
Share on other sites
Cornstalks    7030

Interesting idea. I can see how this would help make things extra smooth. I'll have to try it out some time and see what the difference is like (I'm on OS X currently so I can't try your demo).

 

Question: is there a way to support monitors with different refresh rates? Most monitors these days are 60Hz, but there are some displays and systems that aren't running at 60Hz, but something "odd" (for example, some projectors may be 48 or 72Hz (since most movies are 24Hz)). If I'm understanding it right, you can't use this method and "properly" support monitors with different refresh rates (then again, the traditional fixed time step doesn't either).

 

I'm going to have to think about this one some more...

Share this post


Link to post
Share on other sites
Frank Force    198

Hodgeman - Mine seems to fluctuate a bit more, but that seems about right.  I think I know why it averages higher then 1/60.  Any frames that get skipped will raise the average above the refresh rate.  Are you doing any kind of interpolation?  The correction buffer thing will ensure the delta is greater than 1 / refreshRate.  You don't want to cap it the minimum delta directly because even though the measured delta can be less then 1/60 it still needs to be accounted for.

 

Cornstalks - With a fixed time step you need interpolation to correct for the difference between your monitor's refresh rate and the fixed time step, that is a different issue.  This method should work fine regardless of the refresh rate of the monitor.

Share this post


Link to post
Share on other sites
Hodgman    51226

I added a bunch of logging to my timing code, and tried adding Frank's fixup.
Originally, I had an accumulator for a fixed time-step, and no interpolation (which simplifies things), very simple:

static float accumulator = 0;
accumulator += deltaTime;
const float stepSize = 1/60.0f;
while( accumulator >= stepSize )
{
	accumulator -= stepSize;
	DoPhysics( stepSize );
}

And then I added this 'snap to vsync' code above it:

static float buffer = 0;
float actualDelta = deltaTime + buffer;
float frameCount = floorf(actualDelta * 60.0f + 0.5);//I did this a bit differently - rounding to nearest number of frames
frameCount = max(1.0f, frameCount);
deltaTime = frameCount / 60.0f;
buffer = actualDelta - deltaTime;

Without the fixup, the accumulator would gradually increase it's remainder, until over about 1000 frames, a 'spare' 1.66ms of time builds up in the accumulator, and the physics loop runs twice in a single frame. Occasionally when this occurs, the next frame will only measure a delta of slightly less than 1/60th, e.g. 0.016654s, which means that no physics update occurs this frame. Then occasionally the next frame will be slightly over 1/60th, which when added to the accumulator, results in another two physics steps.
So typically, I'm getting 1 physics step a frame. Then once every few thousand frames, I take 2 steps in one frame, then 0, then 2, then back to normal. I hadn't noticed this small quirk, and only found it now due to this thread!
 
With the fixup, things are much more stable around this edge case. When the 'buffer' and/or accumulator build up enough extra time, then I still do two physics updates in a frame. However, the case where this is followed by zero the next frame is gone (and also the case where the 'zero' frame is followed by a 'two' frame is also gone).
 
So, from that, it seems to be a pretty good idea in general, and I think if I was using interpolation/extrapolation in my fixed-time-step code, then this fix would be even more important! As is, my step size matches my refresh rate, but in general I can't rely on this being true, so I need to add interpolation at some point. Without the fix, I'm guessing the original jittery timings would have a large impact on the perceived quality of the interpolation. Thanks frank biggrin.png

 

[edit]I also did some testing on what my actual average frame time was, but it was a bit inconsistant run-to-run. One run it averaged to 0.01667, another 0.01668, and another 0.01666... Also, at different times, the average would either be slowly creeping upwards or downwards.

Edited by Hodgman

Share this post


Link to post
Share on other sites
Frank Force    198

Hodgeman - Very interesting results!  Did you notice an actual visual pop when it did the 2-0-2 thing?  If you are triple buffering (and possibly double buffering i think) it is normal to get 2 updates or 0 in one frame,

Share this post


Link to post
Share on other sites
Hodgman    51226

Did you notice an actual visual pop when it did the 2-0-2 thing?

No, to me, my demo (which is a car racing game) felt silky smooth both before and after the fix... but this is a jitter the size of just one or two frames, occuring at most once every ~15s, and sometimes as low as once every few minutes.

 

I have a frame-counter variable which I used to add a hack where once every N frames, there would be a frame with 0 physics updates (followed by a frame with 2):

if( time.frame % N != 0) /* do fixed timestep update loop */

With a value like N=10, the game actually still felt quite smooth, and if I didn't know there was a problem, I don't know if I would've noticed that something was wrong.

With a value of N=3, it felt like a 30Hz game instead of a 60Hz game, or a bit like the experience of watching a 24Hz movie on a 60Hz TV.

 

But even at these extremes, it was still hard to tell what was wrong -- it just didn't feel like a 60Hz game (although Fraps will tell you it's rendering at 60Hz). It's only when I record a video of the game at 60Hz and then step through the video frame by frame that it's obvious that every 3rd frame is a duplicate of the one before it.

 

If you are triple buffering (and possibly double buffering i think) it is normal to get 2 updates or 0 in one frame

Well, I delimit my frames by calling Present -- to me that's the end/start of each frame. To the monitor, the start/end of a frame is the vblank.

Vsync should make the two match up, but in order for the GPU to end up with 3 frame buffers (2 with valid data, and one being written), then AFAIK the driver will not perform a vsync initially until the CPU is far enough ahead of the GPU. Once it has those 2 valid frames and a 3rd being drawn to, it will begin to actually vsync.

So ideally, I always have one physics update per "game frame", but there may be multiple of my "game frames" per vblank to begin with, until the necessary latency is achieved.

Edited by Hodgman

Share this post


Link to post
Share on other sites
Icebone1000    1958

One way I use to find the smoothness of my rendering, more intuitive then inspecting the variation of frame times, is to render a sprite model moving at constant speed in a loop, rendering it against the empty render target WITHOUT clearing it. That way the sprite will create a pattern on the screen, that pattern will be constant almost times, but when it jitters it will leave a gap on the pattern...can you understand it?

Heres my example:

https://lh3.googleusercontent.com/-pjanW27XNKY/UVrw6Nt0uyI/AAAAAAAAAgg/hundTIgOGaI/s1076/Untitled-1.png

I think its an osome way to check for jittering/stuttering or wathever its called..

Im current using Glenns method, my test loop current looks like that:

 

 

        //========================================================================
        // game loop vars
        //========================================================================
        double frameDeltaAccumulated = 0.0;
        const double fixedStep = 1.0/24.0;
        //========================================================================
        bool bGoingRight = true;
        while( Msg.message != WM_QUIT ){

            win::UniqueHigPerfTimer().Update();
            win::UniqueFileLogger()<<win::UniqueHigPerfTimer().GetDeltaSeconds()<<SZ_NEWLINE;

            while( PeekMessage( &Msg, NULL, NULL, NULL, PM_REMOVE )    ){

                TranslateMessage( &Msg );
                DispatchMessage( &Msg );

                win::UniqueFileLogger()<<"WM"<<SZ_NEWLINE;
            }

            //========================================================================
            // GLENN loop
            double currentFrameDeltaSec = win::UniqueHigPerfTimer().GetDeltaSeconds();
            if( currentFrameDeltaSec > 0.25 )//fixedStep )//0.25
                currentFrameDeltaSec = 0.25;

            frameDeltaAccumulated += currentFrameDeltaSec;

            while( frameDeltaAccumulated > fixedStep ){

                frameDeltaAccumulated -= fixedStep;
            //========================================================================
            
                mySprite.m_trafo.Step(); //saves previous trafo for interpolation

                if( bGoingRight ){

                    const DirectX::FXMVECTOR vVel = DirectX::XMVectorSet( (float)(100.0*fixedStep), 0.0f, 0.0f, 0.0f );
                    mySprite.m_trafo.m_vCurrentPosition = DirectX::XMVectorAdd( mySprite.m_trafo.m_vCurrentPosition, vVel);

                    if( DirectX::XMVectorGetX( mySprite.m_trafo.m_vCurrentPosition ) > 500.0f ) bGoingRight = false;
                }
                else{
                    const DirectX::FXMVECTOR vVel = DirectX::XMVectorSet( -(float)(100.0*fixedStep), 0.0f, 0.0f, 0.0f );
                    mySprite.m_trafo.m_vCurrentPosition = DirectX::XMVectorAdd( mySprite.m_trafo.m_vCurrentPosition, vVel);

                    if( DirectX::XMVectorGetX( mySprite.m_trafo.m_vCurrentPosition ) < -500.0f ) bGoingRight = true;
                }
            }

            //------------------------------------------------------------------------
            // interpolate:

            const double alpha = frameDeltaAccumulated / fixedStep;

            mySprite.Update( alpha, myWindow.m_device.GetContext() );

            //------------------------------------------------------------------------
            myWindow.m_spriteRenderer.Render( &mySprite ); // queue sprite commands
            myWindow.m_spriteRenderer.Raster( myWindow.m_device.GetContext()); // execute commands

            myWindow.m_swapChain.m_pSwapChain->Present(1, 0);        
        }
 

Im totally new to fixed time steps, previously Id just gather the delta with the hig perf counters and update with it.

 

Heres how my deltas look like(im rendering a single sprite, and my deltas are far from constant):

 

0.016912
0.016572
0.016887
0.016759
0.016487
0.015790
0.016446
0.016867
0.016665
0.016744
0.016644
0.016964
0.016686
0.016564
0.016661
0.017432
0.016770
0.016777
0.016366
0.017165
0.017188
0.016933
0.016495
0.016738
0.016733
 

 

I will try your method later.

Edited by Icebone1000

Share this post


Link to post
Share on other sites
Frank Force    198

Hodgeman - I'm not surprised your game already felt smooth because using a fixed time step that is equal to the vsync is the ideal case to not have jitters.  Its interesting that tried that method to force it to produce duplicate frames and still couldn't really detect it visually.  It does depend on the type of game, for some games it is less obvious.  The most obvious case is a 2d scrolling game where pops like this are very apparent.  In 3d fps type games it seems less obvious.  I also think that many of even the very best games have this jittery issue so we are trained to not notice it.

 

I think the whole point of triple buffering is to allow the CPU to fall behind a bit to smooth out pops by sacrificing latency.  So even if you have just 1 new backbuffer to display and the vsync happens then it will flip it, why wouldn't it?  The next vsync you will just need to do 2 frames to catch up.  This is how pops are smoothed out even if you have a very long frame.  Triple buffering + vsync is a way of actually decoupling the vsync from the game update.  Even though you get 1 update per vsync on average, it's really more about keeping the back buffers updated then waiting for the vsync.

 

Icebone - Wow, that's a really cool idea!  Keep in mind though that due to rounding that gaps in the pattern may actually be correct.  So for example if it's moving at 1.1 pixels per vsync, you would expect it to jump an extra pixel every 10 vsyncs. The math to make things move an integer number of pixels when there's a fixed time step with interpolations is a bit complicated.  What I do to visualize jitter is have a wrapping image scroll continuously across the screen.  When running at 60 fps it should move extremely smooth.  I will need to think more about your idea though, I like how it is visualized over time.

Share this post


Link to post
Share on other sites
Norman Barrows    7179

But even at these extremes, it was still hard to tell what was wrong -- it just didn't feel like a 60Hz game (although Fraps will tell you it's rendering at 60Hz). It's only when I record a video of the game at 60Hz and then step through the video frame by frame that it's obvious that every 3rd frame is a duplicate of the one before it.

 

30 fps was the original "industry standard" (or target) for how fast a game should run for smooth animation. but that was a minimum speed, not the fastest rate at which humans can perceive a change.

 

this is what you were experiencing when it didn't "feel" like 60 fps.  sure you have to do screen dumps to explicitly see it. but on a lower, automatic, subconscious level your brain already perceives it, although you may not be able to quite articulate what you're brain is recognizing.

 

human brains appear to be quite good at picking up on subtle things like this, especially in graphics, since we're visually oriented creatures.

Share this post


Link to post
Share on other sites
Norman Barrows    7179

Keep in mind though that due to rounding that gaps in the pattern may actually be correct.

 

 

"don't clear the screen, draw trails, and measure the gaps" should tell you if you're accurate enough for a given resolution. sounds super simple to me, just turn off one line of code (clearscreen).

Share this post


Link to post
Share on other sites
Hodgman    51226

full physics? PC platform? available when?
(pardon the interruption, but i'm curious)

Yes, it's a fully realistic car simulation engine, but we're using futuristic/concept car parameters instead of current realistic car values. Windows PC to begin with. Aiming for an alpha/prototype around September.
Placeholder site is 22series.com, or the company sites in my sig ;-)
[/off topic]

Share this post


Link to post
Share on other sites
alh420    5995

human brains appear to be quite good at picking up on subtle things like this, especially in graphics, since we're visually oriented creatures.

 

And with practice, you get even better at it.

I think its also important to remember that we who stare at pixels all day get pretty darn good at it, a lot more so then the average person.

Also, it bugs us a lot more, because we know how smooth it _can_ look smile.png

 

Same thing with color depths, that whole "True color, the human eye can not see more then 24 bit color" is just marketing, the RGB space cover only a part of our color vision, and someone trained in looking at computer graphics and  finding faults easily notice all the banding and dithering going on to try to compensate.

 

Not saying you shouldn't try to make it as smooth as possible, of course that is a priority (and usually your job as a low level graphics guy) smile.png

 

Really interesting thread!

Getting your delta time right is more work then one would think

Edited by Olof Hedman

Share this post


Link to post
Share on other sites
Icebone1000    1958

Can someone give some input about the difference in handling gameloop in PC and in consoles?

 

ppl always say that pc is too generic and cant guarantee a thing, so, how that works in a console?

Share this post


Link to post
Share on other sites
Hodgman    51226

Can someone give some input about the difference in handling gameloop in PC and in consoles?

ppl always say that pc is too generic and cant guarantee a thing, so, how that works in a console?

There's no real difference in your game loop these days, but the level of abstraction between you and the hardware is thinner -- there's a fixed hardware spec (with some exceptions, like storage size) and the OS is simpler.
On older consoles, there isn't even really an OS at all, just the game and the hardware, so all the "device driver" code is in the game. On a console, you can do dangerous things that you really don't want to be available to general purpose PC applications, like the ability to take mutually exclusive control over a device, or generate hardware interrupts which call their own kernel-mode functions, communicate directly to devices via MMIO without going through a driver, implement CPU/GPU/refresh-rate synchronization yourself from scratch...

With such a simple machine you can be sure that no background process is going to steal any of your CPU time.
On windows (with default settings), if a thread sleeps (which might happen inside Present), then the thread might not wake for 15ms+, and in a worst case, windows can leave a thread sleeping for over 5 seconds if you've got enough processes running ;/

Share this post


Link to post
Share on other sites
_the_phantom_    11250

But even at these extremes, it was still hard to tell what was wrong -- it just didn't feel like a 60Hz game (although Fraps will tell you it's rendering at 60Hz). It's only when I record a video of the game at 60Hz and then step through the video frame by frame that it's obvious that every 3rd frame is a duplicate of the one before it.

For various reasons I wouldn't really trust fraps to be telling you the whole truth of what is going on anyway; http://anandtech.com/show/6857/amd-stuttering-issues-driver-roadmap-fraps

Share this post


Link to post
Share on other sites
Frank Force    198

Interesting article, completely wrong about some of the conclusions they make though...

 

" If the GPU takes longer to render a frame than expected – keeping in mind it’s impossible to accurately predict rendering times ahead of time – then that would result in stuttering."

 

First of all that's what triple buffering is for.  You can have a long frame and even miss the vsync but it will be ok as long as the buffer is full.  Secondly you absolutely can predict the rendering times ahead of time with almost perfect accurately.  True that it is impossible to know the exact phase shift of the vsync but that doesn't matter.  Also, obviously if you fall too far behind or the OS decides to give something else priority there can be stutter but in my experience with my game engine its not unrealistic to expect your game to be perfectly smooth without any dropped frames or stutter on a decent PC.

 

"Variance will always exist and so some degree of stuttering will always be present. The only point we can really make is the same point AMD made to us, which is that stuttering is only going to matter when it impacts the user."

 

Actually no, we can completely eliminate the variance and it's not even hard to do.  It is very surprising that they don't mention anything like my idea because it seems like it would fix most of the issues they talk about in the article.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this