我正在更新一个应用程序,其中在屏幕上显示刺激信号的时间需要最大的准确性。目前,它是使用DirectDraw编写的,已经在牧场上放了很久,并且需要更新我们的图形库。

我们测量显示时间的方法是利用检测垂直空白时间的结束。特别是,我需要以最大可能的精度知道屏幕上实际绘制了什么内容(翻转到主表面(或显示在交换链中))。检测扫描线可以增加该测量的确定性,但是我将只能检测在调用Flip或Present之后垂直空白时间何时结束。

Direct 3D 9具有IDirect3DDevice9::GetRasterStatus Method,该IDirectDraw::GetVerticalBlankStatus返回D3DRASTER_STATUS结构,该结构包括InVBlank bool 值,该 bool 值描述设备是否处于垂直空白以及当前扫描行。 DirectDraw具有类似的功能(ojit_a,也可以使用IDirectDraw::GetScanLine在垂直空白期间返回DDERR_VERTICALBLANKINPROGRESS来检测VB)。

但是我无法在Direct3D11中找到任何类似的功能。有谁知道在Direct3D9和Direct3D11之间是否移动或删除了此功能,如果是后者,为什么?

最佳答案

抱歉,回复晚了,但我注意到仍然没有被接受的答案,所以也许您从未找到有效的答案。如今,在Windows上, DesktopWindowManager 服务(dwm.exe)可以协调所有内容,无法真正被绕开。从Windows 8开始,就不能禁用此服务。

因此,DWM始终将控制所有各种IDXGISurface(n)对象和IDXGIOutput(n)监视器的帧速率,渲染队列管理和最终组成,并且除非有必要,否则对于跟踪屏幕外渲染目标的 VSync 跟踪并没有多大用处缺少某些东西(无讽刺意味)。至于您的问题,我不确定您的目标是:

  • 获得极为精确的时序信息,但仅用于诊断,配置文件或信息性使用,或
  • 应用程序是否要(尝试)使用这些结果来计划(尝试)自己的当前周期。

  • 如果是后者,我相信您只有在D3D应用程序以全屏独家模式运行时才能有效地做到这一点。唯一的情况是DWM(以的形式伪装DXGI )将真正信任客户端来处理自己的Present时序。

    这里的(很少)好消息是,如果您对 VSync感兴趣仅是引用信息,也就是说,您从上方进入了子弹类别(1.),那么您确实可以获取所有的计时数据所需,且分辨率为QueryPerformanceFrequency,通常约为320 ns.¹

    这是获取高分辨率视频时序信息的方法。但是,再次要说明的是,尽管在如下所示的信息获取中取得了明显的成功,但是任何尝试使用这些有趣的结果(例如,对所获得的读数进行确定性的并因此可能有用的结果)的尝试注定要失败,也就是说,完全被DWM中介挫败了:


    typedef struct _DWM_TIMING_INFO
    {
        UINT32    cbSize;                 // size of this DWM_TIMING_INFO structure
        URATIO    rateRefresh;            // monitor refresh rate
        QPC_TIME  qpcRefreshPeriod;       // monitor refresh period
        URATIO    rateCompose;            // composition rate
        QPC_TIME  qpcVBlank;              // query performance counter value before the vertical blank
        CFRAMES   cRefresh;               // DWM refresh counter
        UINT      cDXRefresh;             // DirectX refresh counter
        QPC_TIME  qpcCompose;             // query performance counter value for a frame composition
        CFRAMES   cFrame;                 // frame number that was composed at qpcCompose
        UINT      cDXPresent;             // DirectX present number used to identify rendering frames
        CFRAMES   cRefreshFrame;          // refresh count of the frame that was composed at qpcCompose
        CFRAMES   cFrameSubmitted;        // DWM frame number that was last submitted
        UINT      cDXPresentSubmitted;    // DirectX present number that was last submitted
        CFRAMES   cFrameConfirmed;        // DWM frame number that was last confirmed as presented
        UINT      cDXPresentConfirmed;    // DirectX present number that was last confirmed as presented
        CFRAMES   cRefreshConfirmed;      // target refresh count of the last frame confirmed as completed by the GPU
        UINT      cDXRefreshConfirmed;    // DirectX refresh count when the frame was confirmed as presented
        CFRAMES   cFramesLate;            // number of frames the DWM presented late
        UINT      cFramesOutstanding;     // number of composition frames that have been issued but have not been confirmed as completed
        CFRAMES   cFrameDisplayed;        // last frame displayed
        QPC_TIME  qpcFrameDisplayed;      // QPC time of the composition pass when the frame was displayed
        CFRAMES   cRefreshFrameDisplayed; // vertical refresh count when the frame should have become visible
        CFRAMES   cFrameComplete;         // ID of the last frame marked as completed
        QPC_TIME  qpcFrameComplete;       // QPC time when the last frame was marked as completed
        CFRAMES   cFramePending;          // ID of the last frame marked as pending
        QPC_TIME  qpcFramePending;        // QPC time when the last frame was marked as pending
        CFRAMES   cFramesDisplayed;       // number of unique frames displayed
        CFRAMES   cFramesComplete;        // number of new completed frames that have been received
        CFRAMES   cFramesPending;         // number of new frames submitted to DirectX but not yet completed
        CFRAMES   cFramesAvailable;       // number of frames available but not displayed, used, or dropped
        CFRAMES   cFramesDropped;         // number of rendered frames that were never displayed because composition occurred too late
        CFRAMES   cFramesMissed;          // number of times an old frame was composed when a new frame should have been used but was not available
        CFRAMES   cRefreshNextDisplayed;  // frame count at which the next frame is scheduled to be displayed
        CFRAMES   cRefreshNextPresented;  // frame count at which the next DirectX present is scheduled to be displayed
        CFRAMES   cRefreshesDisplayed;    // total number of refreshes that have been displayed for the application since the DwmSetPresentParameters function was last called
        CFRAMES   cRefreshesPresented;    // total number of refreshes that have been presented by the application since DwmSetPresentParameters was last called
        CFRAMES   cRefreshStarted;        // refresh number when content for this window started to be displayed
        ULONGLONG cPixelsReceived;        // total number of pixels DirectX redirected to the DWM
        ULONGLONG cPixelsDrawn;           // number of pixels drawn
        CFRAMES   cBuffersEmpty;          // number of empty buffers in the flip chain
    }
    DWM_TIMING_INFO;
    

    (注意:要水平压缩以上源代码以在本网站上显示,请假定以下缩写是:)
    typedef UNSIGNED_RATIO URATIO;
    typedef DWM_FRAME_COUNT CFRAMES;
    

    现在,对于以窗口模式运行的应用程序,您当然可以根据需要随时获取这些详细信息。如果只需要它进行被动配置,那么从DwmGetCompositionTimingInfo获取数据是现代的方法。

    谈到现代,既然这个问题暗示要进行现代化,那么您将考虑使用从IDXGISwapChain1获得的IDXGIFactory2::CreateSwapChainForComposition来启用新的DirectComposition组件。



    无论如何,详细的时序信息似乎不太可能有用地告知应用程序的运行时行为。 也许它将帮助您预测下一个VSync,但确实想知道“敏锐地了解消隐期”对于某些特定的DWM屏外交换链有何意义。

    因为您的应用程序表面只是DWM所处理的众多事情之一,所以DWM将在每个客户端行为一致的假设下自行进行各种动态调整。在这种情况下,不可预测的适应是不合作的,并且可能最终会使双方困惑。

    注意: 1. QPC的分辨率比DateTime滴答声的分辨率高很多个数量级,尽管后者建议使用100 ns。单位面额。将DateTime.Now.Ticks视为对(以毫秒为单位)Environment.TickCount的重新打包,但是转换为100 ns单位。为了获得尽可能高的分辨率,请使用静态方法Stopwatch.GetTimestamp()而不是DateTime.Now.Ticks

    10-01 18:59