PDA

View Full Version : Constant frame rate aka limit FPS



gintasdx
05-02-2011, 06:38 PM
I wonder how to limit FPS into simple 60 since now the game takes to much processor usage on Windows. ???

Stoney
05-02-2011, 06:58 PM
This depends on the graphics library you are using.
If you are using SDL, you might want to take a look at SDL_gfx which has some nice frame regulation functions or you may try to implement them yourself like described here: http://lazyfoo.net/SDL_tutorials/lesson14/index.php (It's C++-Code but it's relatively easy to port this to Object Pascal.)

Edit: Sorry, I just noticed this was posted in the ZenGL forums. So my answer won't help you. :(

User137
05-02-2011, 09:37 PM
Nah, not dependent on graphics library. I've found easiest way to insert TTimer on form with interval 17. It will be smooth, save your cpu and gpu and be very close to 60 fps.

dazappa
05-02-2011, 11:35 PM
You might be interested in this post: http://www.pascalgamedevelopment.com/showthread.php?6703-Font-Issues&p=52894&viewfull=1#post52894

and perhaps a few above it ;)

gintasdx
06-02-2011, 12:15 PM
Well actually you don't need to convert any C++ code. There is good examples of limiting FPS using SDL_Delay(); in JEDI-SDL package,SDLSpriteEngine demos folder. :)

gintasdx
06-02-2011, 12:42 PM
You might be interested in this post: http://www.pascalgamedevelopment.com/showthread.php?6703-Font-Issues&p=52894&viewfull=1#post52894

and perhaps a few above it ;)

Yeah I can say that VSync may work with not all video drivers. I don't see any effect when I turn on .

With VSync: [00010966ms] Average FPS: 459
Without VSync : [00014209ms] Average FPS: 458

BTW, ZGL IDE is really neat idea. ;)

Andru
06-02-2011, 12:57 PM
Yeah I can say that VSync may work with not all video drivers. I don't see any effect when I turn on
Only if this drivers configured to do not use VSync, or if drivers just a cr*p :)


There is good examples of limiting FPS using SDL_Delay()
SDL_Delay is not good idea because it's just a Sleep function under Windows, and it far from "constant value". But yeah, you can limit your fps to ~61 with this function.

gintasdx
06-02-2011, 07:45 PM
Only if this drivers configured to do not use VSync, or if drivers just a cr*p :)



Well I found the solution. Seems DirectX9 version of this engine works just fine with VSync. It gives around
55 FPS :) DirectX8 only about 28FPS with VSync. With OpenGL I only could get stable 60FPS with VSync in fullscreen mode,but on Linux everything is fine with the latest Intel drivers.

vgo
07-02-2011, 09:55 AM
Some old code that I've used in my projects, not sure where it originally came from...



unit glThread;

interface

uses
Forms, Windows, Classes, MMSystem;

type
TThreadProcedure = procedure of object;

TGLDrawEvent = procedure(Sender: TObject) of object;

TGLRunThread = class(TThread)
private
FIsTerminated: Boolean;
fAverageDrawList : TList;
fAverageDrawTime : single;
fFrameRate : single;
fIdleTime : Single;
fAverageIdleTime : single;
fMaxFrameRate : integer;
fThreadProc : TThreadProcedure;
fGLPriority : TThreadPriority;
fUnlimited : boolean;
FrameCount : integer;
FrameTicks : Single;
procedure SetfGLPriority (Value : TThreadPriority);
procedure DoIt;
protected
QPFrequency: Int64;
QPCurrentTime: Int64;
QPLastTime: Int64;
QPEnabled: Boolean;
procedure Execute; override;
function GetTime: Single;
public
constructor Create (CreateSuspended : boolean; ThreadProc : TThreadProcedure);
destructor Destroy; override;
property GLPriority : TThreadPriority read fGLPriority write SetfGLPriority;
property ThreadProc : TThreadProcedure write fThreadProc;
property IdleTime : Single read fIdleTime;
property AverageDrawTime : single read fAverageDrawTime;
property AverageIdleTime : single read fAverageIdleTime;
property FrameRate : single read fFrameRate;
property MaxFrameRate : integer read fMaxFrameRate write fMaxFrameRate;
property Unlimited : boolean read fUnlimited write fUnlimited;
property IsTerminated: Boolean read FIsTerminated;
end;

TGLThread = class(TComponent)
private
procedure ThreadProc;
protected
RunThread: TGLRunThread;
FOnDraw: TGLDrawEvent;
function GetAverageDrawTime: Single;
function GetFrameRate: Single;
function GetMaxFrameRate: Integer;
procedure SetMaxFrameRate(Value: Integer);
function GetRunning: Boolean;
function GetUnlimited: Boolean;
procedure SetUnlimited(Value: Boolean);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Start;
procedure Stop;

property FrameRate: Single read GetFrameRate;
property AverageDrawTime: Single read GetAverageDrawTime;
property Running: Boolean read GetRunning;
published
property OnDraw: TGLDrawEvent read FOnDraw write FOnDraw;
property MaxFrameRate: Integer read GetMaxFrameRate write SetMaxFrameRate;
property Unlimited: Boolean read GetUnlimited write SetUnlimited;
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents('OpenGL', [TGLThread]);
end;

{---------------------------------------------------------------------------
-----------------------}

function TGLRunThread.GetTime: Single;
var
Time: Int64;
begin
QueryPerformanceCounter(Time);
Result := (Time / QPFrequency);
end;

//TThreadPriority = (tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical);

constructor TGLRunThread.Create(CreateSuspended : boolean; ThreadProc : TThreadProcedure);
begin
inherited Create(CreateSuspended);
fAverageDrawList := TList.Create;
fFrameRate := 0;
fMaxFrameRate := 60; // Need a frame rate of 60 frames / second.
fThreadProc := ThreadProc;
//FreeOnTerminate := true;
FreeOnTerminate := FALSE;
//GLPriority := tpIdle;
GLPriority := tpNormal;
fUnlimited := false;
FIsTerminated := FALSE;
QueryPerformanceFrequency(QPFrequency);
end; {constructor}

{---------------------------------------------------------------------------
-----------------------}

destructor TGLRunThread.Destroy;
begin
fAverageDrawList.Free;
inherited;
end; {destructor}

{---------------------------------------------------------------------------
-----------------------}

procedure TGLRunThread.SetfGLPriority(Value : TThreadPriority);
begin
if (fGLPriority <> Value) then begin
fGLPriority := Value;
Priority := fGLPriority;
end; {if}
end; {SetfGLPriority}

{---------------------------------------------------------------------------
-----------------------}

procedure TGLRunThread.Execute;
begin
FIsTerminated := FALSE;
FrameCount := 0; // Used to get the frame rate.
FrameTicks := GetTime;
while not Terminated do begin
//Synchronize(DoIt);
DoIt;
end; {while}
FIsTerminated := TRUE;
end; {Execute}

procedure TGLRunThread.DoIt;
{................................................. ..........................
.....................}

function AverageTime(TimeList : TList; TimeValue : Single; Samples : integer) : single;
var
lp1 : integer;
begin
TimeList.Add(Pointer(TimeValue));
if (TimeList.Count > Samples) then
TimeList.Delete(0);
Result := 0;
for lp1 := 0 to pred(TimeList.Count) do
Result := Result + Single(TimeList[lp1]);
Result := Result / TimeList.Count;
end; {AverageTime}

{................................................. ..........................
.....................}

var
StartTicks : Single;
DrawTime : Single;
begin
//StartTicks := GetTickCount; // Get the start time.
StartTicks := GetTime; // Get the start time.
if Assigned(fThreadProc) then // Do the drawing ...
Synchronize(fThreadProc); // <------- The main draw routine
//DrawTime := Integer(GetTickCount-StartTicks); // Get the draw time.
DrawTime := (GetTime-StartTicks); // Get the draw time.

fAverageDrawTime := AverageTime(fAverageDrawList, DrawTime, 50); // Work out the average draw time.

if fUnlimited then begin // Frame rate limited ?
fIdleTime := 0; // Set these to zero ... then ignore.
fAverageIdleTime := 0;
end else begin
fIdleTime := (1/fMaxFrameRate) - DrawTime; // Idle time is the difference between the
fAverageIdleTime := 1/fMaxFrameRate - fAverageDrawTime; // wanted frame duration and the draw time.
end; {if}

if (fIdleTime > 0) then // If there is some idle time then delay
Sleep(Round(fIdleTime * 1000)); // for the idle time by sleeping.

inc(FrameCount); // Work out the frame rate ... effectively
if (StartTicks - FrameTicks > 1) then begin // sample every second and count the number
fFrameRate := (1*FrameCount) / (StartTicks-FrameTicks); // of frames.
FrameCount := 0;
//FrameTicks := GetTickCount;
FrameTicks := GetTime;
end; {if}


end;

{---------------------------------------------------------------------------
-----------------------}
{ajGLThread}

constructor TGLThread.Create(AOwner: TComponent);
begin
inherited;
RunThread := TGLRunThread.Create(TRUE, ThreadProc);
end;

destructor TGLThread.Destroy;
begin
Stop;
RunThread.Free;
inherited;
end;

function TGLThread.GetAverageDrawTime: Single;
begin
Result := RunThread.AverageDrawTime;
end;

function TGLThread.GetFrameRate: Single;
begin
Result := RunThread.FrameRate;
end;

function TGLThread.GetMaxFrameRate: Integer;
begin
Result := RunThread.MaxFrameRate;
end;

procedure TGLThread.SetMaxFrameRate(Value: Integer);
begin
RunThread.MaxFrameRate := Value;
end;

function TGLThread.GetRunning: Boolean;
begin
Result := not RunThread.IsTerminated;
end;

function TGLThread.GetUnlimited: Boolean;
begin
Result := RunThread.Unlimited;
end;

procedure TGLThread.SetUnlimited(Value: Boolean);
begin
RunThread.Unlimited := Value;
end;

procedure TGLThread.Start;
begin
RunThread.Resume;
end;

procedure TGLThread.Stop;
begin
if not RunThread.IsTerminated then
begin
RunThread.Terminate;
RunThread.WaitFor;
end;
end;

procedure TGLThread.ThreadProc;
begin
if Assigned(FOnDraw) then
FOnDraw(Self);
end;

end.

gintasdx
07-02-2011, 04:07 PM
The Asphyre Sphinx seems to have a nice way to limit FPS without using a sleep function. Anyways VSync helps to avoid tearing,but I rarely see a games that has it turned on by default. And I wonder does anyone tried the way from GameDev.net (http://www.gamedev.net/topic/457590-fps-limiting/)

Andru
07-02-2011, 04:34 PM
The Asphyre Sphinx seems to have a nice way to limit FPS without using a sleep function.
ORLY? It use SleepEx :)

Ñuño Martínez
28-04-2011, 03:43 PM
I know this is old (long time I don't walk here...) but I wrote an article about how to control time using my Allegro.pas library (http://allegro-pas.sourceforge.net/wiki/doku.php?id=timers). It uses "interruption-like". May be you can translate it to your favourite library.