Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi Now() function returns a wrong value

I have the DirectX-based application. And recently I found that Now() function returns a wrong value when being called from within the main loop of my graphics engine. It gives one value being called before engine initialized and different one (usually differs 2-3 minutes back or forward) when being called in my application when graphics is started.

I found that Now() function is a wrapper for Windows API GetLocalTime() function. Anyone can point out what can affect the return value of this very function? I heavily use timeGetTime() function in the main loop of my app, can it be the source of problem? Also I need to use CheckSyncronize() function in the main loop...

Any ideas? I'm out of clues... :(

code of the main loop:

    procedure Td2dCore.System_Run;
    var
        l_Msg: TMsg;
        l_Point: TPoint;
        l_Rect : TRect;
        l_Finish: Boolean;
    begin
        if f_WHandle = 0 then
        begin
            System_Log('Engine was not started!');
            Exit;
        end;

        if not Assigned(f_OnFrame) then
        begin
            System_Log('Frame function is not assigned!');
            Exit;
        end;

        // MAIN LOOP
        l_Finish := False;
        while not l_Finish do
        begin
            // dispatch messages
            if PeekMessage(l_Msg, 0, 0, 0, PM_REMOVE) then
            begin
                if l_Msg.message = WM_QUIT then
                    l_Finish := True;
                DispatchMessage(l_Msg);
                Continue;
            end;

            GetCursorPos(l_Point);
            GetClientRect(f_WHandle, l_Rect);
            MapWindowPoints(f_WHandle, 0, l_Rect, 2);
            f_MouseOver := f_MouseCaptured or (PtInRect(l_Rect, l_Point) and (WindowFromPoint(l_Point) = f_WHandle));
            if f_Active or f_DontSuspend then
            begin
                repeat
                    f_DeltaTicks := timeGetTime - f_Time0;
                    if f_DeltaTicks <= f_FixedDelta then
                        Sleep(1);
                until f_DeltaTicks > f_FixedDelta;
                //if f_DeltaTicks >= f_FixedDelta then
                begin
                    f_DeltaTime := f_DeltaTicks / 1000.0;

                    // if delay was too big, count it as if where was no delay
                    // (return from suspended state for instance)
                    if f_DeltaTime > 0.2 then
                        if f_FixedDelta > 0 then
                            f_DeltaTime := f_FixedDelta / 1000.0
                        else
                            f_DeltaTime := 0.01;

                    f_Time := f_Time + f_DeltaTime;

                    f_Time0 := timeGetTime;

                    if(f_Time0 - f_Time0FPS < 1000) then
                        Inc(f_FPSCount)
                    else
                    begin
                        f_FPS := f_FPSCount;
                        f_FPSCount := 0;
                        f_Time0FPS := f_Time0;
                    end;

                    f_OnFrame(f_DeltaTime, l_Finish);
                    if Assigned(f_OnRender) then
                        f_OnRender();
                    ClearQueue;
                    {
                    if (not f_Windowed) and (f_FixedFPS = D2D_FPS_VSYNC) then
                        Sleep(1);
                    }
                end;
                {
                else
                    if (f_FixedDelta > 0) and (f_DeltaTicks+3 < f_FixedDelta) then
                        Sleep(1);
                }
            end
            else
                Sleep(1);
            CheckSynchronize;
        end;
    end;

the Now() is called somewhere in f_OnFrame() function.

like image 249
Anton Zhuchkov Avatar asked Aug 27 '12 19:08

Anton Zhuchkov


3 Answers

Finally I've found the solution. I needed to specify th D3DCREATE_FPU_PRESERVE flag when creating a D3D device by D3D.CreateDevice.

Otherwise, without that flag, all floating point operations are performed with single precision. As the TDateTime is a simple Double, and Now() functions is consist of simple addition of date value to time value, it all get messed up by DirectX "smart" override.

Problem solved. It was a tricky one indeed. :)

like image 61
Anton Zhuchkov Avatar answered Nov 13 '22 09:11

Anton Zhuchkov


I use to run into these issues with OpenGL and the simulator and MapObjects. The timing sequence was found to be the differences between the equipment. Here is two links that might shed some light onto the subject:

http://delphi.about.com/od/windowsshellapi/a/delphi-high-performance-timer-tstopwatch.htm Zarko Gajic's article helped with the timer that I had to build for a serial interface. I used a variation of this code example to pull data for a tank sensor.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms644900%28v=vs.85%29.aspx This is the microsoft information on using high end timers in your software. Generally timers will cycle out and be off sync. This article goes into two timers, the High-Resolution Timer and the Waitable Timer Objects. The High-Resolution is the timer variant that I used for the simulator.

like image 34
Rush Dar Avatar answered Nov 13 '22 09:11

Rush Dar


As far as I've understood from the code, you are making decisions on

              f_DeltaTicks := timeGetTime - f_Time0;

f_Time0 variable is initialized at a later stage in the loop.

                f_Time0 := timeGetTime;

there might be a simple bug, that the f_Time0 is not initialized the way you want/require on the first entrance into the loop.

like image 39
Ali Avcı Avatar answered Nov 13 '22 08:11

Ali Avcı