【问题标题】:Delphi Now() function returns a wrong valueDelphi Now() 函数返回错误值
【发布时间】:2012-08-27 19:27:24
【问题描述】:

我有基于 DirectX 的应用程序。最近我发现Now() 函数在从图形引擎的主循环中调用时返回错误值。它会在引擎初始化之前调用一个值,而在我的应用程序中启动图形时调用另一个值(通常前后相差 2-3 分钟)。

我发现 Now() 函数是 Windows API GetLocalTime() 函数的包装器。任何人都可以指出什么会影响这个函数的返回值?我在我的应用程序的主循环中大量使用timeGetTime() 函数,这可能是问题的根源吗?另外我需要在主循环中使用CheckSyncronize() 函数...

有什么想法吗?我没有线索...... :(

主循环代码:

    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;

Now() 在 f_OnFrame() 函数的某处被调用。

【问题讨论】:

  • 它是否在所有机器上都出现异常?你能在一个小的示例程序中重现这个吗?
  • 是的。实际上我收到了这个错误作为错误报告。我想我可以做一个小程序,但它会涉及到我的图形引擎的链接......
  • 我已经在顶部消息中发布了一个主循环代码。
  • 你能重现这种行为吗?它是否在所有机器上都出现异常?
  • 是的。在我可以检查它的几台机器上它是不正常的......

标签: delphi datetime delphi-2007


【解决方案1】:

我终于找到了解决方案。在D3D.CreateDevice 创建 D3D 设备时,我需要指定 D3DCREATE_FPU_PRESERVE 标志。

否则,没有该标志,所有浮点运算都以单精度执行。由于TDateTime 是一个简单的Double,而Now() 函数是简单地将日期值添加到时间值,这一切都被DirectX“智能”覆盖搞砸了。

问题解决了。这确实是一个棘手的问题。 :)

【讨论】:

    【解决方案2】:

    我过去常常在 OpenGL 和模拟器和 MapObjects 中遇到这些问题。时间顺序被发现是设备之间的差异。这里有两个链接可能会对这个主题有所启发:

    http://delphi.about.com/od/windowsshellapi/a/delphi-high-performance-timer-tstopwatch.htm Zarko Gajic 的文章对我必须为串行接口构建的计时器提供了帮助。我使用此代码示例的变体来提取储罐传感器的数据。

    http://msdn.microsoft.com/en-us/library/windows/desktop/ms644900%28v=vs.85%29.aspx 这是有关在您的软件中使用高端计时器的微软信息。通常,计时器将循环退出并不同步。本文介绍了两个计时器,高分辨率计时器和可等待计时器对象。高分辨率是我用于模拟器的计时器变体。

    【讨论】:

    • 虽然这可能需要进一步微调,但似乎并不相关。该问题抱怨“SYSTEMTIME”在不同的调用之间滑动。它不会通过连续调用使用不同的筹码。或者可能我没有理解这个问题..
    • 我看不出这些链接如何与对 Now 的相邻调用相关联,返回值相隔几分钟。
    【解决方案3】:

    据我从代码中了解到,您正在做出决定

                  f_DeltaTicks := timeGetTime - f_Time0;
    

    f_Time0 变量在循环的稍后阶段被初始化。

                    f_Time0 := timeGetTime;
    

    可能存在一个简单的错误,即 f_Time0 在第一次进入循环时未按照您想要/要求的方式进行初始化。

    【讨论】:

    • 事实上,没有。没有错误。下面我发布了我找到的解决方案。但是感谢您的帮助! :)
    【解决方案4】:

    如果您使用的是 DirectX,最好在需要时打开数学准确性,并在不需要时将其关闭。

    uses Math;
    
    
    var pm: TFPUPrecisionMode;
    begin
       pm: = GetPrecisionMode;
       try
         SetPrecisionMode (pmExtended)
    .
    .
    .
       finally
         SetPrecisionMode (pm);
       end
    end;
    

    Direct X 和一些显卡驱动需要这个并且可能会抛出错误:Invalid floating point operation

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-07-16
      • 2015-12-28
      • 2011-03-15
      • 2020-11-22
      • 2019-05-02
      • 2019-11-07
      • 2019-04-13
      相关资源
      最近更新 更多