Limiting game updates but not framerate in old D3D game

Started by
6 comments, last by frob 8 years, 9 months ago

Hi,

I have a game codebase from the late 90's that I updated to work with Direct3D9 and modern Windows operating systems.

What I did originally when updating the renderer was set D3D9's presentation parameters PresentationInterval to D3DPRESENT_INTERVAL_ONE, and this works great. I need to do this to limit the framerate as the game starts to act oddly at high framerate (bouncing items that should remain static, player movement becomes janky etc)

When I did this, I had 60hz refresh rates in mind, and this all worked fine. But of course, some users like to overwrite this value in their graphics card display adapters and/or we're now seeing 144hz refresh rates, so this isn't working so well anymore.

So what Ideally I'd like to do is cap the game updates to 60hz and let the game render as much as it needs. Should be ok?

I've seen the few "fix your timestamps" articles but unfortunately I didn't write this game, and tend not to go near the gameplay code, so modifying the underlying methods for how entities and whatnot update their velocities etc etc is beyond me.

Here's what I've been trying, to only update the game every 16ms and allow rendering as fast as possible - only clear the backbuffer and render a new frame every time the game updates, otherwise just redraw the previous frame:



uint32_t startTime = timeGetTime();

while (1)
{
	CheckWindowsMessages();
	
	bool doupdate = false;
	
	if (currentTime - startTime >= 16)
	{
		D3D9_BeginFrame();
		
		UpdateGame();
		
		startTime = timeGetTime(); //currentTime;
		doupdate = true;
	}
	
	if (doupdate)
	{
		D3D_EndFrame();
	
		FrameCounterHandler();
		/* FrameCounterHandler() is the games method for handling timer updates, 
		calculting how much time has passed since it was last called, and updating game
		variables with this value. Lots of fixed point math stuff lurking in here...
		*/
		
		doupdate = false;
	}
					
	D3D9_FlipBuffers();
}

This doesn't actually work well in reality, as my 4 year old computer can run a frame of this game at about 3000fps when vsync is off. What happens is sorta like vsync tearing, but instead of a single shear in the middle, there are multiple shears all down the screen (evenly spaced apart)

I guess what I'm asking is there a better way to do what I want, without having to pass any sort of delta time changes to the underlying game code? I might have my logic wrong somewhere here..

I'm really clueless on this timer stuff... Thanks!

Advertisement

hmm, I'm probably flipping an old frame with a new frame when I call present, without updating the back buffer?

http://gafferongames.com/game-physics/fix-your-timestep/

See the last and second to last approach. What you need to do is basically fully decouple updating and rendering. You render each frame (D3D9_BeginFrame() and D3D9_EndFrame() as well as all rendering code in between), and only when the 16.66666 ms have passed, you update. This means that you cannot just check for "currentTime - startTime >= 16" and then update (which I assume also does the rendering). Notice also that your code makes no real sense anyway - you set "doupdate" and check it right afterwards, which means you might as well put the if(doupdate) code in the other condition, which defies the purpose.

So yeah, I doubt this is going to be extremely easy if actual DirectX draw calls right now happen inside UpdateGame(). You might leave out the last step in the link with interpolation, because this sure makes things another inch harder (though without it you might experience small noticable artifacts). Let me know if there is something more you don't understand.

Thanks, I'll play around with that :) Yep my D3D render list is being generated in those update calls - I have a render list struct array that basically allows me to group draw calls and things like that, so can try move things around... maybe preserve my lists and vertex buffers to only clear them when I need to actually update them?

I shouldn't have any issues just redrawing the previous frame if nothing game-wise has updated, right?

Yeah that's a very simplified version of the loop - I have some code running in between (checks to see if streaming music has to be updated etc) so that's why the odd redundant looking boolean is there.

Redrawing the previous frame won't actually do anything though.. so you might as well not do it at all, or use D3DPRESENT_INTERVAL_TWO. I would just limit frame-rate if VSync is disabled, or if it's higher than 120 use two for present interval when possible.

If you want to present multiple times then that will of course work too, if you for example change the present parameters to use D3DSWAPEFFECT_COPY instead of discard (may have to change some more settings, check the docs for D3DPRESENT_PARAMETERS and D3DSWAPEFFECT).

https://msdn.microsoft.com/en-us/library/windows/desktop/bb172588%28v=vs.85%29.aspx

https://msdn.microsoft.com/en-us/library/windows/desktop/bb172612%28v=vs.85%29.aspx

What method would you recommend for limiting frame rate? Sleeping?

Just spin-loop.. though if you really need only a few ms or less to update you could sleep while the time to wait is larger than X ms or something.

Read the docs here for the time resolution in that case: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686298%28v=vs.85%29.aspx

If you do that, set the time-period to 1 and sleep 1 ms while time left until update > 3 ms or something like that, and otherwise just spin.


Just spin-loop

While in the short term that feels like a good solution, the more time passes and faster machines become, the worse the solution is.

Far better to wait on the actual thing. Since in this case he cannot rely on vsync, it is probably better done through calling WaitForSingleObject() coupled with a multimedia timer.

This topic is closed to new replies.

Advertisement