Sign in to follow this  
Endar

Time based rendering placement problems (updated)

Recommended Posts

Endar    668
Skip to the last post for the newer stuff.
Yeah I know that the title is kind of confusing, but I didn't really know what else to have it as. Anyway, the problem is that the code that I have to do time based rendering so I restrict the fps of the engine is currently in my SceneManager class. When I need to render, I simply call SceneManager::render() and that traverses the scene graph tree and calls 'render' for everything. So, the code to restrict the engine fps needs to be in SceneManager::render(). I recently realised that if I call SceneManager::render() more than once every frame, I get problems. For instance if I need to render twice for shadows, or to draw reflections, or something like that. It's a little bit of a story, so here goes: This is the code that restricts the fps of the engine:
/**
 * Render all objects in the scene graph.
 */
void CSceneGraph::render()
{

	while( (m_time += m_hiResTimer.getElapsedSeconds()) < m_minTimeBetweenFrames );

	//if( (m_time += m_hiResTimer.getElapsedSeconds()) < m_minTimeBetweenFrames )
	//	return;


	util::list<ISceneGraphNode*>::iterator i;

	// pre render
	for(i = m_nodeList.begin() ; i != m_nodeList.end(); i++)
		(*i)->preRender( m_time );

	// render
	for(i = m_nodeList.begin() ; i != m_nodeList.end(); i++)
		(*i)->render();

	// post render
	for(i = m_nodeList.begin() ; i != m_nodeList.end(); i++)
		(*i)->postRender( m_time );

	// reset time to 0.0f because we have just drawn a frame
	m_time = 0.0f;

}




I currently have to use the while statement, otherwise I get serious flickering and no restriction of the fps. I think I know why this is. Whenever VideoDriver::endScene is called, it swaps the buffers for the window, and obviously if no rendering is done then nothing gets drawn to the back buffer and a blank buffer gets swapped to the screen, causing the flickering. I think. So, for both these reasons, (blank buffer swap, and calling SceneManager::render more than once a frame) I need to move the fps code to the video driver, so I can prevent the buffer being swapped when nothing has been drawn to it. Should I just have a pointer to the scene graph inside the video driver, move the fps code to the video driver and instead of calling 'render' on the scene graph object, do it on the video driver object? How have other people done this bit? Or did you all do the smart thing and have a better design than I did? [Edited by - Endar on May 23, 2006 4:17:16 AM]

Share this post


Link to post
Share on other sites
hplus0603    11356
Time restriction does not below in a scene graph.

When you call "render" on a scene graph, it should traverse the graph, and render all the items to the indicated target, using the current state of the objects. It should not change the state of the objects, or check the time, or anything like that. When it comes to object state, the scene graph should be absolutely dumb (only the user changes the objects).

If calling Render() twice in a row in your scene graph causes an empty frame to be rendered, you have a bug in your scene graph.

If you need to limit the frame rate, you should first try turning on vSync in your code -- D3DPRESENTINTERVAL_xxx has some options for you. This will remove all tearing. Only after that should you start throttling in the application -- and, in that case, in the high-level code. I e, your code might look something like:


forever() {
toSleep = nextTime - time();
Sleep(toSleep);
nextTime = time() + desiredFrameTime;
ReadInput();
ProcessPhysics(desiredFrameTime);
RenderScene();
}


Note that Sleep() may be a little bit jittery on some systems; you may have better results with something like MsgWaitForMultipleObjectsEx() with a timeout.

Share this post


Link to post
Share on other sites
Endar    668
Quote:
Original post by hplus0603
If you need to limit the frame rate, you should first try turning on vSync in your code -- D3DPRESENTINTERVAL_xxx has some options for you.


Do you know the equivalent for OGL? Oh, well I don't think it matters as I'm not currently using full screen, I'm just doing it windowed.

Share this post


Link to post
Share on other sites
Endar    668
Okay, I thought this would work, but I'm having problems with the code and it's better to post here than start up a new thread.


void IVideoDriver::render()
{
// If the total time since the last frame is less than the time between frames for the desired fps
// ie. If to restrict to the desired fps, we don't have to draw yet
if( (m_time += m_hiResTimer.getElapsedSeconds()) < m_minTimeBetweenFrames )
return;

// if we get here, then we should be rendering a frame

// if we have a scene manager
if( m_sceneManager )
m_sceneManager->render( m_time ); // render the scene graph

// reset the time
m_time = 0.0f;

// indicate we should be rendering
m_renderFrame = true;
}

/**
* Does housekeeping for the end of rendering a frame.
*/

virtual void IVideoDriver::endScene()
{
// if we should be rendering a frame
if( m_renderFrame ){
m_fpsCounter.endFrame(); // register the end of a frame, so, calculate the FPS
m_renderFrame = false; // it's the beginning of a new frame
}
}

/**
* Does OGL housekeeping for the end of rendering a frame.
* Flushes the pipeline, swap buffers, counts fps.
*/

void COpenGLDriver::endScene()
{
// If the time since the last frame is greater than the min time between frames
// ie. If we should be rendering
if( m_renderFrame ){
glFlush(); // flush the pipeline

SwapBuffers(m_hdc); // render the scene to the window

// do the FPS calculating, etc
IVideoDriver::endScene();
}
}




Note:: COpenGLDriver inherits from IVideoDriver.

If I use the code was is, the FPS counter says the fps is ~50. But when I do some rotation on the cube, which is the only thing is the scene, it rotates way too fast for 50 fps.

If I comment out the first if statement, and don't add to 'm_time' at all, I get about ~87.

This is really confusing.

Share this post


Link to post
Share on other sites
Bob Janova    769
You are only restricting the rendering frame rate by putting checks in a render() function, not the physics frame rate. Your high-level message loop is the place to restrict speed, not a rendering function.

And you don't need to bump after 7 hours.

Share this post


Link to post
Share on other sites
Endar    668
Quote:
Original post by Bob Janova
You are only restricting the rendering frame rate by putting checks in a render() function, not the physics frame rate. Your high-level message loop is the place to restrict speed, not a rendering function.


At the moment, I'm only trying to restrict the rendering speed (I currently have no physics).

Why is the high-level message loop the place to restrict speed? Can't I have each system restricting it's own speed to whatever it needs or wants?

Quote:
Original post by Bob Janova
And you don't need to bump after 7 hours.


Uh, 3:45am I wrote the post, and bumped it at 10:55pm.

Share this post


Link to post
Share on other sites
Bob Janova    769
Okay, you don't need to bump after X hours, where X < 24 and your page is still on the first screen :P.

Quote:
At the moment, I'm only trying to restrict the rendering speed (I currently have no physics).

Uh, where does rotating this model happen, then? The rate at which that occurs will be related to your input framerate, which I presume is not called from within render(). You don't restrict the framerate of your message loop at all by returning from render() (to do that you need to wait).

Quote:
Why is the high-level message loop the place to restrict speed? Can't I have each system restricting it's own speed to whatever it needs or wants?

Well, yes, it's just more complicated. By restricting frame rate at the highest level you ensure all your game modules run at the same speed. If you want to restrict each component separately then that's fine, but don't expect your input and rendering to happen at the same frame rate!

Share this post


Link to post
Share on other sites
Endar    668
Quote:
Original post by Bob Janova
Uh, where does rotating this model happen, then? The rate at which that occurs will be related to your input framerate, which I presume is not called from within render(). You don't restrict the framerate of your message loop at all by returning from render() (to do that you need to wait).


I thought that eventually I'd be able to do something like this:

while( running ){

processInput();
render();
physicsUpdate();
sleep();

}


And for the rendering and physics, or at least the rendering calc the seconds (fractions obviously) between each render to get a specific fps, and if we've not hit it yet, then just don't render and go on to the physics. Kind of the same with the physics (kind of, I know nothing about physics yet).

Or was this what you were telling me and it's just not entering my thick, thick head?

Share this post


Link to post
Share on other sites
Bob Janova    769
Okay, so in that case what is the problem with the cube rotating faster than 50 fps :P ? If you're just not rendering then you'd expect the other parts of the game (input, physics) to go faster. Sounds like your solution is working just like you wanted it to 8).

Share this post


Link to post
Share on other sites
Endar    668
Hmmmm. It's working as it should, but not as I want it too. So, it's not technically broken. :D

Anyway, I'm putting it aside for now to re-visit it tomorrow.

Share this post


Link to post
Share on other sites
Endar    668
It's been a while, but here I am again.

I've made some modifications which absolutely did not work, so I scrapped it all.

Here's what I've got:

while( engine->run() ){
toSleep = nextTime - hiResTimer.getElapsedSeconds(); // set the time to sleep
sleepTime = (int) (toSleep * 1000);
Sleep( sleepTime ); // turn the float into milliseconds
nextTime = hiResTimer.getElapsedSeconds() + desiredFrameTime;

/* render the scene (no physics yet) */

}




This was supplied by hplus0603 earlier in the thread. But, with this code, the fps starts at 10, and just halves every couple of seconds. So, I thought that I'd post it here in case anyone can give me a hand while I'm figuring it out.

Also, a question: does it matter whether vsync is on or not if the application is not being run fullscreen? Because I'm don't even have support for fullscreen at the moment, I'm just running it windowed.

[Edited by - Endar on May 23, 2006 4:21:45 AM]

Share this post


Link to post
Share on other sites
Endar    668
I had put this aside for a while, but I'm attempting to make it work again, and I'll probably need help.

Share this post


Link to post
Share on other sites
Demus79    362

I made actually a class for this purpose. I call it the "sequencer". Every object in the program uses it to update itself at certain interval. When I say object, it includes sound systems, environmental controlling, physics, internal clocks plus of course the objects in the gaming environment (characters, vehicles). To solve certain dependancies there are several sequencers, (working possibly at different frame rates).

So, the none of the top level loops have any timing code in them. This sequencer is used with a certain event object. The event object calls a call back within the objects at a requested frame rate. Plus, for interpolation purposes, this event object can be request for alpha value between the frames.

With this system, the main loop becomes quite clean:

while(having_fun)
{
Sequencer->Poll();
Render();
}

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