Page 2 of 3 FirstFirst 123 LastLast
Results 11 to 20 of 26

Thread: QueryPerformanceCounter problem and questions

  1. #11

    QueryPerformanceCounter problem and questions

    The division with the Frequency (or multiplication with InvFrequency,
    respective) always produces a floatingpoint number. So at least if you want
    to know how much seconds or milliseconds (*1000) passed you need single
    or double [which is better for such big numbers].

    "what style of hardware dont have this avaliable?"
    I don't know. Just know that this feature is abundant nowadays.
    Wiki says its even integreated in CPU, so forget my Mainboard statement
    (English Wiki shows a table how much counters are integrated in which
    CPU; it mentions AMD Athlon and Pentium III).

  2. #12

    QueryPerformanceCounter problem and questions

    Try this:
    [pascal]
    unit PerfCounter;

    interface

    uses
    Windows;

    type
    PComp = ^Comp;

    { .: TPerformanceCounter :. }
    TPerformanceCounter = class(TObject)
    private
    { Private declarations }
    StartTime, Freq: Comp;
    public
    { Public declarations }
    constructor Create();

    procedure StartTiming();
    function GetTime(): Double;
    end;

    function QueryPerformanceCounter(lpPerformanceCount: PComp): Boolean; stdcall; external 'KERNEL32.DLL';
    function QueryPerformanceFrequency(lpFrequency: PComp): Boolean; stdcall; external 'KERNEL32.DLL';

    implementation

    { TPerformanceCounter }

    constructor TPerformanceCounter.Create;
    begin
    inherited Create();

    QueryPerformanceFrequency(@Freq);
    end;

    function TPerformanceCounter.GetTime: Double;
    var
    X: Comp;
    begin
    QueryPerformanceCounter(@X);
    Result := (X - StartTime) * 1000.0 / Freq;
    end;

    procedure TPerformanceCounter.StartTiming;
    begin
    QueryPerformanceCounter(@StartTime);
    end;

    end.
    [/pascal]

    Let us know if it works as you want.

  3. #13

    QueryPerformanceCounter problem and questions

    Quote Originally Posted by waran
    The division with the Frequency (or multiplication with InvFrequency,
    respective) always produces a floatingpoint number. So at least if you want
    to know how much seconds or milliseconds (*1000) passed you need single
    or double [which is better for such big numbers].

    "what style of hardware dont have this avaliable?"
    I don't know. Just know that this feature is abundant nowadays.
    Wiki says its even integreated in CPU, so forget my Mainboard statement
    (English Wiki shows a table how much counters are integrated in which
    CPU; it mentions AMD Athlon and Pentium III).
    it aways return something.0

    (at least here)
    From brazil (:

    Pascal pownz!

  4. #14

    QueryPerformanceCounter problem and questions

    ORLY?
    Code:
    uses windows;
    var
      t, fq: int64;
      t_sec: double;
    begin
      QueryPerformanceFrequency(fq);
      QueryPerformanceCounter(t);
      t_sec := t/fq;
      
      writeln(fq);          // 3579545
      writeln(t);           // 9629603054
      writeln(t_sec:12:12); // 2690.175163044465
      
      readln;
    end.
    However: If GetTime (didn't test it) returns something.0 something is
    foul with the code

  5. #15

    QueryPerformanceCounter problem and questions

    Hello Waran

    You think that calculating DeltaTime without interval is ok? inside Application.OnIdle
    Knowledge is power.

  6. #16

    QueryPerformanceCounter problem and questions

    Quote Originally Posted by waran
    ORLY?
    Code:
    uses windows;
    var
      t, fq: int64;
      t_sec: double;
    begin
      QueryPerformanceFrequency(fq);
      QueryPerformanceCounter(t);
      t_sec := t/fq;
      
      writeln(fq);          // 3579545
      writeln(t);           // 9629603054
      writeln(t_sec:12:12); // 2690.175163044465
      
      readln;
    end.
    However: If GetTime (didn't test it) returns something.0 something is
    foul with the code
    uhm, thats true, my mistake
    From brazil (:

    Pascal pownz!

  7. #17

    QueryPerformanceCounter problem and questions

    Ok, so the idea here is to decouple updating from rendering. You want your game loop to render graphics as fast and as smoothly as possible while your simulation continues to update at a known and predictable rate and do this across different machine configurations. A frame-base timing system is way to achieve this goal. One problem with FBT is that if there a large delta from the last frame your objects will have a big speed change for the next frame. The speed of the object will be same, but the visual impact of this will be disconcerting. Your code should be able to handle this situation and keep it to a minimum.

    Maybe this code can be of use to someone. It's the frame-base timing code that I use (for the past 7 years) in PyroGine SDK. It will try to use the performance counter by default and if not found will drop down to the Windows multimedia timer. With it you can set your desired simulation rate and the maximum number of frames to try and keep up your desired simulation rate. It can also return the your game loop frame rate in addition to performing some other useful timing functions.

    [pascal]type

    { TPyroTime }

    TPyroTimer = class(TPyroObject)
    private
    function GetTickCountPriv: Int64;
    protected
    FPCAvail: Boolean;
    FPCFreq: Int64;
    FCurTime: Int64;
    FLastTime: Int64;
    FDesiredFPS: Single;
    FMinElapsedTime: Single;
    FMaxElapsedTime: Single;
    FElapsedTimeScale: Single;
    FFPSTimeScale: Single;
    FFPSElapsedTime: Single;
    FElapsedTime: Single;
    FFrameRate: Cardinal;
    FFrameCount: Cardinal;
    FTimer: Single;
    public
    constructor Create;
    destructor Destroy; override;

    procedure Init(aDesiredFPS, aMaxElapsedTime: Single);
    procedure Clear;
    procedure Update;

    function GetElapsedTime: Single;
    function GetFrameRate: Cardinal;
    function GetDesiredFPS: Single;

    function FrameElapsed(aFrames: Single): Boolean; overload;
    function FrameElapsed(var aTimer: Single; aFrames: Single): Boolean; overload;

    procedure ResetFrameElapsed(aFrame: Single); overload;
    procedure ResetFrameElapsed(var aTimer: Single; aFrame: Single); overload;

    function FrameSpeed(aSpeedFPS: Single): Boolean; overload;
    function FrameSpeed(var aTimer: Single; aSpeedFPS: Single): Boolean; overload;

    function ResetFrameSpeed(aSpeedFPS: Single): Single; overload;
    function ResetFrameSpeed(var aTimer: Single; aSpeedFPS: Single): Single; overload;

    function GetTickCount: Cardinal;

    property ElapsedTime: Single read GetElapsedTime;
    property FrameRate: Cardinal read GetFrameRate;
    property DesiredFPS: Single read GetDesiredFPS;
    end;

    { TPyroTimer }
    function TPyroTimer.GetTickCountPriv: Int64;
    var
    Ticks: Int64;
    begin
    if FPCAvail then
    begin
    QueryPerformanceCounter(Ticks);
    end
    else
    begin
    timeBeginPeriod(1);
    Ticks := timeGetTime();
    timeEndPeriod(1);
    end;

    Result := Ticks;
    end;

    constructor TPyroTimer.Create;
    begin
    inherited Create;
    Init(35.0, 2.0);
    end;

    destructor TPyroTimer.Destroy;
    begin
    inherited Destroy;
    end;

    procedure TPyroTimer.Clear;
    begin
    FTimer := 0;
    FFPSElapsedTime := 1;
    FLastTime := 1000;
    Update;
    end;

    function TPyroTimer.GetTickCount: Cardinal;
    var
    Ticks: Int64;
    Ms : Cardinal;
    begin
    if FPCAvail then
    begin
    QueryPerformanceCounter(Ticks);
    Ms := (Ticks * 1000) div FPCFreq;
    end
    else
    begin
    timeBeginPeriod(1);
    Ms := timeGetTime();
    timeEndPeriod(1);
    end;

    Result := Ms;
    end;

    procedure TPyroTimer.Init(aDesiredFPS, aMaxElapsedTime: Single);
    begin
    // check for performace counter
    FPCAvail := QueryPerformanceFrequency(FPCFreq);
    if FPCAvail then
    begin
    QueryPerformanceFrequency(FLastTime);
    end
    else
    begin
    FLastTime := 1000;
    end;
    FDesiredFPS := aDesiredFPS;
    FMinElapsedTime := 0.0;
    FMaxElapsedTime := aMaxElapsedTime;
    if FMaxElapsedTime < 1 then
    FMaxElapsedTime := 1
    else if FMaxElapsedTime > 1000 then
    FMaxElapsedTime := 1000;

    FElapsedTimeScale := FDesiredFPS / FLastTime;
    FFPSTimeScale := 1.0 / FLastTime;
    FTimer := 0;
    end;

    procedure TPyroTimer.Update;
    begin
    // calc elapsed time
    FCurTime := GetTickCountPriv;
    FElapsedTime := (FCurTime - FLastTime) * FElapsedTimeScale;
    if FElapsedTime < FMinElapsedTime then
    FElapsedTime := FMinElapsedTime
    else if FElapsedTime > FMaxElapsedTime
    then FElapsedTime := FMaxElapsedTime;

    // calc frame rate
    Inc(FFrameCount);
    FFPSElapsedTime := FFPSElapsedTime + ((FCurTime - FLastTime) * FFPSTimeScale);

    if FFPSElapsedTime >= 1 then
    begin
    FFPSElapsedTime := 0;
    FFrameRate := FFrameCount;
    FFrameCount := 0;
    end;

    FLastTime := FCurTime;
    end;

    function TPyroTimer.GetElapsedTime: Single;
    begin
    Result := FElapsedTime;
    end;

    function TPyroTimer.GetFrameRate: Cardinal;
    begin
    Result := FFrameRate;
    end;

    function TPyroTimer.GetDesiredFPS: Single;
    begin
    Result := FDesiredFPS;
    end;

    function TPyroTimer.FrameElapsed(var aTimer: Single; aFrames: Single): Boolean;
    begin
    Result := False;
    aTimer := aTimer + (1.0*FElapsedTime);
    if aTimer > aFrames then
    begin
    aTimer := 0;
    Result := True;
    end;
    end;

    function TPyroTimer.FrameElapsed(aFrames: Single): Boolean;
    begin
    Result := FrameElapsed(FTimer, aFrames);
    end;

    procedure TPyroTimer.ResetFrameElapsed(var aTimer: Single; aFrame: Single);
    begin
    aTimer := aFrame + 1;
    end;

    procedure TPyroTimer.ResetFrameElapsed(aFrame: Single);
    begin
    ResetFrameElapsed(FTimer, aFrame);
    end;

    function TPyroTimer.FrameSpeed(var aTimer: Single; aSpeedFPS: Single): Boolean;
    var
    ScaleTime: Single;
    begin
    Result := False;
    aTimer := aTimer + FElapsedTime;
    ScaleTime := (FDesiredFPS / aSpeedFPS);
    if aTimer > ScaleTime then
    begin
    aTimer := aTimer - ScaleTime;
    Result := True;
    end;
    end;

    function TPyroTimer.FrameSpeed(aSpeedFPS: Single): Boolean;
    begin
    Result := FrameSpeed(FTimer, aSpeedFPS);
    end;

    function TPyroTimer.ResetFrameSpeed(var aTimer: Single; aSpeedFPS: Single): Single;
    begin
    Result := (FDesiredFPS / aSpeedFPS);
    aTimer := Result;
    end;

    function TPyroTimer.ResetFrameSpeed(aSpeedFPS: Single): Single;
    begin
    Result := ResetFrameSpeed(FTimer, aSpeedFPS);
    end;
    [/pascal]

    Example usage:

    [pascal]// init simulation to 35 fps and try to keep this up to two elapsed frames
    Timer.Init(35,0);

    // in your game loop call Update once per frame
    Timer.Update;

    // get current framerate
    FrameRate := Timer.FrameRate;

    // get elapsed time value which is based on desired framerate
    // multiply by speed value to keep objects moving at 35 fps
    ElapsedTime := Timer.ElapsedTime;

    // update object speed
    myobj.speed := myobj.speed * ElapsedTime
    [/pascal]

    Now as your game loop speed may vary wildly from frame to frame but your simulation rate will continue to update in a predictable manner and this is what we are after here. We need to be able to have control over the simulation as much as possible.

    Also if VSync is off then you will experience "tearing." The result will be a "popping" effect. Note that this is purely visual but it does detract from the overall experience. You have to assume that you will have no control over VSync so as the developer you must be prepared to minimize this problem. I've found that using a simulation rate that is a value slightly above a multiple of the monitor's refresh rate will yield the best results. 60hz is common to all monitors so traditionally I've used 35 fps as a simulation rate in all of my game projects.

    Note: The code tags is messing with some of the code for some reason. The line in TPyroTimer.Init should be the following:

    if FMaxElapsedTime [LessThan] 1 then
    FMaxElapsedTime := 1
    else if FMaxElapsedTime [GreaterThan] 1000 then
    FMaxElapsedTime := 1000;

    and in TPyroTimer.Update:

    if FElapsedTime [LessThan] FMinElapsedTime then
    FElapsedTime := FMinElapsedTime
    else if FElapsedTime [GreaterThan] FMaxElapsedTime
    then FElapsedTime := FMaxElapsedTime;

    It seems the forum does not like the GreaterThan/LessThan constructs.
    Jarrod Davis
    Technical Director @ Piradyne Games

  8. #18

    QueryPerformanceCounter problem and questions

    i cant compile your code with delphi 7

    error:

    [Error] Unit2.pas(136): Incompatible types
    [Error] Unit2.pas(150): Incompatible types
    [Fatal Error] Project1.dpr(6): Could not compile used unit 'Unit2.pas'

    procedure TPyroTimer.Init(aDesiredFPS, aMaxElapsedTime: Single);
    begin
    // check for performace counter
    FPCAvail := QueryPerformanceFrequency(FPCFreq);
    if FPCAvail then
    begin
    QueryPerformanceFrequency(FLastTime);
    end
    else
    begin
    FLastTime := 1000;
    end;
    FDesiredFPS := aDesiredFPS;
    FMinElapsedTime := 0.0;
    FMaxElapsedTime := aMaxElapsedTime;
    if FMaxElapsedTime <1> 1000 then
    FMaxElapsedTime := 1000;

    FElapsedTimeScale := FDesiredFPS / FLastTime;
    FFPSTimeScale := 1.0 / FLastTime;
    FTimer := 0;
    end;


    procedure TPyroTimer.Update;
    begin
    // calc elapsed time
    FCurTime := GetTickCountPriv;
    FElapsedTime := (FCurTime - FLastTime) * FElapsedTimeScale;
    if FElapsedTime <FMinElapsedTime> FMaxElapsedTime
    then FElapsedTime := FMaxElapsedTime;

    // calc frame rate
    Inc(FFrameCount);
    FFPSElapsedTime := FFPSElapsedTime + ((FCurTime - FLastTime) * FFPSTimeScale);

    if FFPSElapsedTime >= 1 then
    begin
    FFPSElapsedTime := 0;
    FFrameRate := FFrameCount;
    FFrameCount := 0;
    end;

    FLastTime := FCurTime;
    end;
    Knowledge is power.

  9. #19

    QueryPerformanceCounter problem and questions

    Because the forum does not seem to allow you to have GreaterThan/LessThan code symbols in your post for some reason. It will not even allow this without the code tags.

    So... here is a unit that you can download and add to the uses section:

    PyroTimer - Frame-base timing system.
    Jarrod Davis
    Technical Director @ Piradyne Games

  10. #20

    QueryPerformanceCounter problem and questions

    Quote Originally Posted by Pyrogine
    Because the forum does not seem to allow you to have GreaterThan/LessThan code symbols in your post for some reason. It will not even allow this without the code tags.

    So... here is a unit that you can download and add to the uses section:

    PyroTimer - Frame-base timing system.
    than you

    please, can you see my private message?
    Knowledge is power.

Page 2 of 3 FirstFirst 123 LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •