Advertisement

Making Breakout - Code Structure help requested

Started by September 11, 2017 01:08 PM
77 comments, last by Kylotan 7 years, 5 months ago

It seems weird that the Vsync has to hold up the entire pipeline, the time it make the program "wait" and enter the update loop let's say 16 ms later (as shown in the images above), those could have been 16 ms we spent calculating, so I really see no reason for it.

Couldn't I provide it with a pointer to a rendered image and it "Presents" when it wants but by using the latest pointer address available? 

I bet that to avoid having  it working like this would require stuff that has to do with threads, which I do know nothing about yet :\

 

Ok, refined my measurement, refined my interpolation code, still getting some bogus resoults :D

This is the current Interpolation code:


float GameTimer<TickPerSec>::Interpolation()
{
	time_point<steady_clock> Now = steady_clock::now();
	nanoseconds InterpolationRange = NextUpdateTime - PrevUpdateTime;
	nanoseconds InterpolationPosition = Now - PrevUpdateTime;
	float Interpolation = (static_cast<float>(InterpolationPosition.count()) / static_cast<float>(InterpolationRange.count()));
	return  Interpolation;
}

In the image below (1000ms, 25 updates) all the numbers express milliseconds, the colors have the following meanings:

Red: next game state update (at steps of 40ms)

Orange: when we actually execute the game state update (can vary depending on Vsync or  rendering time I guess)

Blue: A Sample of Interpolation value calculated for that specific point between the last update(orange) and the next update(red)

Green: millisecond corresponding to when the blue interpolation value above it was recorded

YxW300s.png

Most value would look fine, but some are suspicious which tells me my math is wrong.

The suspicious one are for example (I refer to the green numbers) the sample at 492ms, 892ms, especially 609ms which is 3ms past the last update with an interpolation value of 0.000... that can't be :| 475ms looks extremely wrong as well, is almost touching the red update in front of it and has a value of 0.84...nonsense =_=

If anyone spot the error in my math, please let me know(well, can't totally exclude it's an error in my picture, but still, I think is the interpolation math) :D

Advertisement
7 hours ago, MarcusAseth said:

Ok, refined my measurement, refined my interpolation code, still getting some bogus resoults

This is the current Interpolation code:



float GameTimer<TickPerSec>::Interpolation()
{
	time_point<steady_clock> Now = steady_clock::now();
	nanoseconds InterpolationRange = NextUpdateTime - PrevUpdateTime;
	nanoseconds InterpolationPosition = Now - PrevUpdateTime;
	float Interpolation = (static_cast<float>(InterpolationPosition.count()) / static_cast<float>(InterpolationRange.count()));
	return  Interpolation;
}

In the image below (1000ms, 25 updates) all the numbers express milliseconds, the colors have the following meanings:

Red: next game state update (at steps of 40ms)

Orange: when we actually execute the game state update (can vary depending on Vsync or  rendering time I guess)

Blue: A Sample of Interpolation value calculated for that specific point between the last update(orange) and the next update(red)

Green: millisecond corresponding to when the blue interpolation value above it was recorded

YxW300s.png

Most value would look fine, but some are suspicious which tells me my math is wrong.

The suspicious one are for example (I refer to the green numbers) the sample at 492ms, 892ms, especially 609ms which is 3ms past the last update with an interpolation value of 0.000... that can't be  475ms looks extremely wrong as well, is almost touching the red update in front of it and has a value of 0.84...nonsense =_=

If anyone spot the error in my math, please let me know(well, can't totally exclude it's an error in my picture, but still, I think is the interpolation math) 

If you really want some help, create one simple cpp sample application (Win32, STL, moving a rectangle around + opengl 1.x rendering, compilable by everyone) without any classes whatsoever and post it here as one source file. So we can see all your math and stuff and help you better.

But posting some snippets from some arbitary functions/classes is useless - because they wont work, when the other stuff are broken.

 

This also helps when you have groundwork issues like this, so you can track down the actual problem - without thinking about any other parts. Simulating game logic is easy, just put a Sleep() in - this will produce enough random time work to simulate doing actual work.

@Finalspace That won't do, I can't use openGL yet, and also the Sleep() won't stop the timer, I can't even debug time because the time advance even if you are waiting at a break point.

Anyway I think I might have fixed it, the error was actually in the code snippet above in, either in the static_cast and/or the fact I was doing division using the .count() of the nanoseconds class which return crazy big numbers and/or the fact that nanosecond rapresentation is a long long therefore there will be some truncation involved.

So I casted it to a float rapresentation of milliseconds before doing the division, this below is the new Interpolation function, I watched all the returned value and I can't spot a single one that doesn't make sense now, so it must have been that. Though I'll repeat 2-3 more times to be sure no bogus value are present anymore, but I'm fairly optimist now :P


template <size_t TickPerSec>
float GameTimer<TickPerSec>::Interpolation()
{
	time_point<steady_clock> Now = steady_clock::now();
	nanoseconds InterpolationRange = NextUpdateTime - PrevUpdateTime;
	nanoseconds InterpolationPosition = Now - PrevUpdateTime;
	float Interpolation = duration_cast<duration<float,std::milli>>(InterpolationPosition) / 
			      duration_cast<duration<float, std::milli>>(InterpolationRange);
	return  Interpolation;
}

Value below seem pretty solid, I'll be moving on to input I gues :)

DW2RzLd.png

15 hours ago, MarcusAseth said:

It seems weird that the Vsync has to hold up the entire pipeline, the time it make the program "wait" and enter the update loop let's say 16 ms later (as shown in the images above), those could have been 16 ms we spent calculating, so I really see no reason for it.

Couldn't I provide it with a pointer to a rendered image and it "Presents" when it wants but by using the latest pointer address available? 

I bet that to avoid having  it working like this would require stuff that has to do with threads, which I do know nothing about yet :\

 

The whole point is that you're saying "here is the image I want you to show, and I want you to show it when the graphics system is ready to display it with no tearing or other potential visual artifacts". The graphics system has to wait. You're right in saying that the CPU doesn't have to stop doing anything at that point, and that it can start getting on with the next frame. And that's usually exactly what happens. The interesting part is when that second frame is finished - has the graphics system finished that first frame yet? If not, we either have to wait for that first frame to be displayed before we can send the second, so eventually, the CPU does have to wait on the GPU.

Most graphics APIs allow you to tweak this various ways - such as opting to discard and replace a queued frame instead of waiting for it to render, or using extra memory to queue up additional frames so that things are smoothed out if certain frames take much longer than others to be generated. But ultimately any synchronisation involves something waiting for something else. That's what you're asking for when you enable VSYNC.

 

Regarding your actual problem, the thing that I see being wrong is that you're thinking about "when we actually execute the game state update", which is completely irrelevant. All that matters is "how many updates we should have done by now".

Your interpolation function should not have the current time in it, because this interpolaton method is not designed to be a function you can call from arbitrary parts of the code. You should be storing the time at the start of the loop, running as many updates as you can to 'catch up' to that time without running any partial updates, and use that remainder - i.e. how much of a partial update you'd have to perform - to inform the rendering system to improve the smoothness of the rendering.

 

14 minutes ago, Kylotan said:

Your interpolation function should not have the current time in it, because this interpolaton method is not designed to be a function you can call from arbitrary parts of the code. You should be storing the time at the start of the loop, running as many updates as you can to 'catch up' to that time without running any partial updates, and use that remainder - i.e. how much of a partial update you'd have to perform - to inform the rendering system to improve the smoothness of the rendering.

Thanks for the explanation above :)

Regarding  the interpolation using the current clock time, I actually catched that and fixed after I had posted my last message and before yours (that's also why the image I added is even more accurate), now is using the TimeNow inside the timer which is a private variable set when the TimerTick(), and it ticks only once at the start of every loop. And also thanks for pointing that out :)

Now my game loop looks like this below, I think I only have to refactor the Timer update stuff into a method in the Timer class and should be in good shape to proceed with input :P


void App::GameLoop()
{
	Timer->Restart();

	while (Game->IsRunning())
	{
		time_point<steady_clock>& CurrentTime = Timer->Tick();

		//UPDATE
		while (CurrentTime >= Timer->NextUpdateTime)
		{
			Game->Update();
			Timer->NextUpdateTime += Timer->GameUpdateTPS;
			Timer->PrevUpdateTime = CurrentTime;
		}

		//DRAW
		Game->Draw(Timer->Interpolation());
	}
}

 

Advertisement

Actually, I have no clue about how to handle input :|

Any input is appreciated :P

 

Handle game specific input in Game->Update() if you like. You probably need to start a new thread if you have input-specific questions.

This topic is closed to new replies.

Advertisement