PDA

View Full Version : Can anyone tell me why my frames drops...*solved*



FusionWolf
22-02-2005, 11:04 AM
Hiya all,

I have been programming a simple 2D game with the DirectX API and created a "skeleton" for the rendering engine. Then I created a sprite manager system to handle my sprites and when I started to render a simple 32x32 sized sprite to the screen (with ID3DXSprite interface) I noticed that game slows down from time to time.

Then I put up a test program where is nothing more than mainloop, a timer and DirectX initialized. Mainloop uses Windows idle time to render. Timer uses QueryPerformanceCounter() to calculate time which has been passed between calls (and FPS of course) also IDirect3D9 and IDirect3DDevice9 interfaces are initialized.

First I sat display parameters so that rendering was locked to vsync. Then I rendered a blank scene (only backbuffer was cleared) and time was measured during rendering with QueryPerformanceCounter. I Got around 50 frames per second which drops to 30 frames from time to time. Test computer was a 1.6GHz laptop which had screen set to 60Hz.

Then I sat display parameters so that rendering occurs immediatel. Then I got approx. 1200 frames per second in windowed mode which drops to around 300 from time to time ("from time to time" I mean about every 3rd second)!?!?

What the hell happens there? I have been testing my program with various different kind of computers in the place where I'm working coz there's hundred of different kind of computers, but in every computer it does the same. Only number of the frames per second changes?!?

Why the frame drops so dramatically about every 3rd second?

Here's the code....


unit uTest;

interface

uses
uGameDX, uGameTimer, SysUtils, Forms;

type
TGame = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FExitLoop: Boolean;
FTimer: TGameTimer;
protected
procedure DoLoop(Sender: TObject; var Done: Boolean);
procedure DoActivate(Sender: TObject);
procedure DoDeactivate(Sender: TObject);
public
property Inactive: Boolean read FExitLoop write FExitLoop;
property Timer: TGameTimer read Ftimer write FTimer;
end;

var
Game: TGame;

implementation
{$R *.dfm}

// Game initialization.
procedure TGame.FormCreate(Sender: TObject);
begin
// Initialize high quality timer to measure time and frames per second.
Timer := TGameTimer.Create;

// Initialize IDirect3D and IDirect3DDevice interfaces to windowed mode.
GameDX := TGameDX.Create(Self, False);

// Set notify events to seek when application is active or not.
Application.OnDeactivate := DoDeactivate;
Application.OnActivate := DoActivate;
end;

// Release objects from memory.
procedure TGame.FormDestroy(Sender: TObject);
begin
GameDX.Free;
Timer.Free;
end;

// Mainloop.
procedure TGame.DoLoop(Sender: TObject; var Done: Boolean);
begin
// Calculate time which has been passed since last call.
Timer.Update;

// Render empty scene.
GameDX.Device.BeginScene($000000);
GameDX.Device.EndScene;

// Check the rendering time and FPS
Game.Caption := Timer.FrameTime + ' - ' + Timer.FPS;

// If application is active keep looping. Otherwise halt execution.
Done := FExitLoop;
end;

// Executed when application gains focus.
procedure TGame.DoActivate(Sender: TObject);
begin
// Game is active and main loop should be executed whenever Windows has a time.
Inactive := False;

// If this is first time when this method is executed, then timer is not paused and doesn't need to resume.
if Timer.Paused then
Timer.Resume;

// Start looping.
Application.OnIdle := DoLoop;
end;

// Executed when application loses focus.
procedure TGame.DoDeactivate(Sender: TObject);
begin
// Game is inactive so don't loop.
Inactive := True;
Application.OnIdle := nil;

// Do not try to measure time if idle loop is executed anyway.
Timer.Pause;
end;
end.


Here's the code of the test application (without the timer code, but that's not the reason for the drop downs). If I remove DirectX part of this code then I get over 30 000 frames per second. With DirectX begin - end calls frames drops down to ~1200 FPS and from there around 300 FPS from time to time?!?!

These frame amounts doesn't matter now, but if I render lot's of sprites then it does. If frames is somewhere around 60 per second and then it's drops down to 30 or 20 at the moment then game slows down dramatically? What is wrong and where? I'm using Clooties DirectX headers.

edit: Ouh, I must add here that DirectX interfaces are initialized excatly the same way as DX SDK teaches.

FusionWolf
23-02-2005, 06:59 PM
Because no one hasn't been answered I put up a test application which you can download from link below. There's a full source code. Application is very simple. It just renders blue scene, measures a rendering time and displays it on caption of the form. If you keep watching the caption you would notice that frames drops down from time to time. How much the frames are just depends from your computer.

http://personal.inet.fi/koti/fusionwolf/misc/test.zip

And this is just for test. If I render some sprites and try this on some slower computer frames would drop down to 90 per second and from there to even 10 fps randomly. You might imagine that, it's very hard to play game which rendering variables so much.

Help?


edit: I must add here if someone is wondering why I'm using idle time for main loop. It's just for testing. In my game I have "full scale" rendering "skeleton" which has own message handler (peekmessage), window and etc. but it slows down too at the same way.

czar
23-02-2005, 07:17 PM
I see what you mean it varies quite a bit. Unfortunately I could not compile your code, I am not sure what I am missing. No matter...

Instead of an idle loop have you tried using a high performance timer? Say using the time from Omega or even DelphiX?

Clootie
23-02-2005, 08:58 PM
Basically you are calculating your FPC incorrectly. You should average FPC at least over some set of frames (say 10 of them), or over period of time.

PS. displaying FPS on Windows caption can influence results you are getting too (as it's hard to predict when it will be really updated).

FusionWolf
24-02-2005, 07:25 AM
I see what you mean it varies quite a bit. Unfortunately I could not compile your code, I am not sure what I am missing. No matter...

Instead of an idle loop have you tried using a high performance timer? Say using the time from Omega or even DelphiX?

Yes I have and then all goes fine, but I don't like rendering a scene once in n- milliseconds. There should be nothing wrong with this style I'm using but I don't know why the frames slows down :roll: .

edit: And rendering a scene once in a n- milliseconds leads to jittering graphics.

FusionWolf
24-02-2005, 07:38 AM
Basically you are calculating your FPC incorrectly. You should average FPC at least over some set of frames (say 10 of them), or over period of time.

PS. displaying FPS on Windows caption can influence results you are getting too (as it's hard to predict when it will be really updated).

Okey I understand but it doesn't explain that if I render even a one simple sprite with ID3DXSprite interface frames drops to half immediately and then from there even a quarter of that randomly. Also rendering time increases to double or more.

And I tried to move a sprite from a keypress. First it accelerates and goes fine and when the top speed is achieved then it slows down dramaticly randomly and then goes fast again and slows down again and so on.

In a car game it's hard to go into the curve for example because car would go fast at first and then suddenly slows down even a quarter of a top speed for a second or so and then suddenly goes back to top speed and you are out of the curve because you don't have time to react.

And I guessed that someone says something about that I'm rendering the frames to the caption of the form. It's just for testing. I tried to render with ID3DXFont interface to the backbuffer, but it doesn't change the results.

I might try to change this test so it calculates average FPS and rendering time, but I think it still doesn't change the fact that why the mainloop is so ineffective?!?

And like I said I have tried main loop like "c++ style" meaning a without main form and by using the PeekMessage() and so on. The usual stuff. But it does same thing. Frames drops.

Clootie
24-02-2005, 06:47 PM
You should render based on current TIME and AVERAGE FPC not frame NUMBER. You can't control how fast your videocard renders single frame. There are a lot of side effects as your CPU and GPU are executing in parallel. For example, to maximize throughtput you videocard driver caches around 3 frames you've already drawn and later can occasionally burnst them all at once to screen (if VSync disabled).

FusionWolf
24-02-2005, 07:16 PM
I have been making some tests again. I developed that test application which I shared earlier (above) to the way that Clootie mentioned. Now it calculates FPS and rendering time from average of 15 frames. Clootie was right on some parts. FPS doesn't varies now, so it was wrong earlier. You can download new version with full source from link below.

Mainloop test application. (http://personal.inet.fi/koti/fusionwolf/misc/mainloop_test.zip) ~1.3MB

Then I noticed that the way I calculated sprite movement was badly wrong and I changed that too to the way mentioned in here -> http://www.gamedev.net/reference/articles/article753.asp. This gives the smooth animation but there's still one problem which should be corrected.

Now all sprite movement is based on the rendering time of the frame. And if computer is slow then the rendering time is high (slow) and also game speed slows down. And if computer is fast also rendering time is low (fast) so game goes way too fast.

My question is. How to render sprites so they goes same speed on every computer? Check the source code to see how I do it now and you notice it changes from computer to another.


edit: Clootie I really appreciate your answers. You have been very helpfull - thanks. And I appreciate if you would tell me something about that sprite movement question above.


edit2: I enabled a VSYNC and compiled this test application again and noticed what happens to the game if it's ran in computer which FPS is dramaticly different than mine has. You can download new executable from link below. D3DX92ab.dll is needed.

http://personal.inet.fi/koti/fusionwolf/misc/mainlooptest.zip ~200KB

czar
24-02-2005, 09:37 PM
Keep track on how long it takes for your frames to be rendered (e.g. timegettime at begin and end of render loop), then divide that number by 1000 (milliseconds) to give a fraction. Multiply this fraction by the movement, so you effectively get number of pixels per second movement.

That way slow computers move same number of pixels as slow computersm they just move more pixels per frame.

FusionWolf
24-02-2005, 09:53 PM
Keep track on how long it takes for your frames to be rendered (e.g. timegettime at begin and end of render loop), then divide that number by 1000 (milliseconds) to give a fraction. Multiply this fraction by the movement, so you effectively get number of pixels per second movement.

That way slow computers move same number of pixels as slow computersm they just move more pixels per frame.

Didn't you check the source? That is what I'm doing right now. Because the rendering time varies from computer to another also speed of the game varies.

edit: Ah, I was doing some calculation and realised that you are right. But I dont get it. Whats wrong with this test application? Why the movement of the sprites varies depending on rendering speed? What I'm doing wrong in this lates example? Why it changes the speed if I enable or disable vsync? Please, HEEEEEeellppp!! I'm getting nuts :?.

edit2: Hey, I just noticed that the shuttle in these two lates examples rotates at the speed it's meant to be and FPS is quite a different. So the calculation works as it should be but I'm calculating velocity of the shuttle wrong. How it should be done?

FusionWolf
25-02-2005, 07:26 PM
Problem solved. As I finally doubt the reason was in the formula how I accelerated the sprite. Now I have correct formula and all goes smooth.

edit: here you can download the lates and final version of it with fullsource code of course. -> Mainloop test final (http://personal.inet.fi/koti/fusionwolf/misc/mainlooptest_final.zip) ~1.4MB