Hello people!
So, I've come back to work on my game engine a little bit more. And this is only possible by Casey providing the best content to learn from I ever came across. Without Handmade Hero this hobby wouldn't be anything I could really spent time on because of the lack of good documentation. Especially for game engine related stuff as mostly anybody uses Unity or whatever.
However there is one part, I can't really figure out how to get right and its -- TIMING.
From my understanding and from anybody elses too a basic game loop seems to look like the following pseudocode:
| int main()
{
while(GameIsRunning)
{
getInput();
updateGameAndRender();
waitForFrameRate();
}
}
|
And this is basically how I am doing it too. For the future I would like to play around with some basic physics simulation. And to be able to I have to get timing right. As I've seen in more complex Systems and also in Handmade Hero the estimated frame time is passed to the game itself so that the game can advance it's physics and world simulation based on the time spent until the next frame is displayed.
I really want to understand this and get this right since it bothers me a lot!
As I am using MacOS X I use mach for timing and OpenGL to "blit" the RenderBuffer to the screen. Back when I was starting with this project I didn't quite understand that using mach_wait_until(NStoSleep) makes no sense when vSync is enabled. And I was always wondering why my animations where always kind of stuttery but I couldn't really tell if they actually were.
Knowing that I commented mach_wait_until(NStoSleep) and suddenly I got super smooth animation movement. Even though my FPS were between 58-62 but this could be caused by calculation and rounding problems I guess.
So how would I ever be able to create an !!halfway!! percice physics simulation when my frame timing code results in quite jittery animations?
I get that vSync is used to prevent tearing as it swaps the Buffers at the exact same time when the Screen refreshes. But as I understood when vSync is enabled the program waits at [GlobalGLContext flushBuffer] until the swap happened. So I gess that the OpenGL timing is more percice than mine.
So my Questions are:
1. Am I doing something wrong with my timing code or is this behaviour expected when vSync is disabled?
Moreover if this behaviour is expected, how are smooth animations with 30fps achieved since vSync syncs to 60fps?
2. Or can I vSync to 30fps somehow?
3. Is there a way to get a more percice timing, as my timing seems to vary a lot even when run without XCode. There has to be a way to get even smoother animations without vSync enabled right?
4. And how is timing handled right now in Handmade Hero since BEGIN_BLOCK(FramerateWait) until END_BLOCK(FramerateWait) is completely "commented out" through if 0? Only through vSync?
5. I am not multiplying character movement with the expected frame time as I was not sure if my timing was correct anyways. Could this produce smooth animations without vSync?
Here is all the timing code thats present in my current build:
This code produces smooth animations as vSync is enabled and mach_wait_until(now + (SleepNS)); is not used. But when I disable vSync and use mach_wait_until animations get jittery which makes me think my timing is corrupted. It wouldn't make sense to base physics on corrupted timing...
I deleted everything that has nothing to do with timing to increase readability.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 | global_variable uint64 NanosecondsElapsed;
global_variable mach_timebase_info_data_t sTimebaseInfo;
int main(int argc, const char * argv[])
{
running = true;
@autoreleasepool
{
#define MonitorRefreshHz 60;
#define GameUpdateHz (MonitorRefreshHz / 60);
real32 TargetSecondsPerFrame = (1.0f / 60);
uint64 LastCounter = mach_absolute_time();
GameData.LastCounter = LastCounter;
float SecondsElapsed = 0;
if ( sTimebaseInfo.denom == 0 )
{
(void) mach_timebase_info(&sTimebaseInfo);
}
while (running)
{
OSXProcessPendingMessages(&GameData);
[GlobalGLContext makeCurrentContext];
OSXProcessFrameAndRunGameLogic(&GameData, ContentViewFrame,
MouseInWindowFlag, MouseLocationInView,
MouseButtonMask);
uint64 WorkCounter = mach_absolute_time();
double WorkSecondsElapsed = ((WorkCounter - LastCounter) * (sTimebaseInfo.numer / sTimebaseInfo.denom)) / 1e9 ;
double SecondsElapsedForFrame = WorkSecondsElapsed;
if(SecondsElapsedForFrame < TargetSecondsPerFrame)
{
double SleepS = (TargetSecondsPerFrame - SecondsElapsedForFrame);
double SleepNS = SleepS * 1e9;
if(SleepNS > 0)
{
uint64 now = mach_absolute_time();
//mach_wait_until(now + (SleepNS));
}
}
[GlobalGLContext flushBuffer];
uint64 EndCounter = mach_absolute_time();
uint64 CyclesElapsed = EndCounter - LastCounter;
NanosecondsElapsed = CyclesElapsed * sTimebaseInfo.numer / sTimebaseInfo.denom;
SecondsElapsed = NanosecondsElapsed / 1e9;
float FPS = ( float )( 1e9 / (float)NanosecondsElapsed );
printf("CyclesElapsed: %llu, TimeElapsed: %f, FPS: %f\n", CyclesElapsed, SecondsElapsed, FPS);
LastCounter = EndCounter;
GameData.LastCounter = EndCounter;
}
} // @autoreleasepool
return (0);
}
|
If I got something wrong please correct me. Also feel free to point my to any resources.
I would really appreciate your help!
Thank you very much, and have a nice day!