Direct3D 11缺少GetRasterStatus,如何检测垂直空白期?

Rob*_*Rob 11 c++ directx graphics vsync

我正在更新一个应用程序,在该应用程序中,在屏幕上显示刺激时间的测量需要最大的准确度.它目前用DirectDraw编写,很久以前就被放到了牧场,需要更新我们的图形库.

我们测量呈现时间的方式利用检测垂直空白周期的结束.具体而言,当需要翻转到主表面(或在交换链中呈现)的任何内容实际上被屏幕绘制时,我需要知道最大可能的准确度.检测扫描线可以增加该测量的确定性,但是我能够仅在检测到在翻转或存在之后立即结束垂直空白时段的情况下工作.

Direct 3D 9具有IDirect3DDevice9 :: GetRasterStatus方法,该方法返回包含InVBlank布尔值的D3DRASTER_STATUS结构,该结构描述设备是否处于垂直空白以及当前扫描线.DirectDraw具有类似的功能(IDirectDraw :: GetVerticalBlankStatus,IDirectDraw :: GetScanLine,它在Vertical Blank期间返回DDERR_VERTICALBLANKINPROGRESS,可用于检测VB).

但是我在Direct3D11中找不到任何类似的功能.有没有人知道Direct3D9和Direct3D11之间是否移动或删除了这个功能,如果是后者,为什么?

Gle*_*den 8

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

因此,DWM 始终会控制所有各种IDXGISurface( n )对象和IDXGIOutput( n )监视器的帧速率、渲染队列管理和最终合成,并且在跟踪屏幕外渲染目标的VSync 方面没有多大用处,除非我遗漏了什么(无意讽刺)。至于你的问题,我不确定你的目标是否是:

  1. 获取极其精确的计时信息,但仅用于诊断、分析或信息用途,或
  2. 然后应用程序是否会(尝试)使用这些结果来(尝试)安排自己当前的周期。

如果是后者,我相信只有当 D3D 应用程序以全屏独占模式运行时,您才能有效地做到这一点。这也正是DWM-在为幌子的唯一情况DXGI -将会真正值得信任的客户来处理自己的Present时间。

这里(勉强)好消息是,如果您对VSync的兴趣只是提供信息——也就是说,您属于上面的项目符号类别 (1.)——那么您确实可以获得您想要的所有计时数据,并且在QueryPerformanceFrequency分辨率下,通常约为320 ns。¹

这是获取高分辨率视频计时信息的方法。但同样,要清楚的是,尽管在获得如下所示的信息方面取得了明显的成功,但任何尝试使用这些有趣的结果,例如,对某些确定性的——因此可能有用——的结果进行调节,您获得的读数将注定要失败,也就是完全被DWM中介阻挠:

DWM_TIMING_INFO

指定桌面窗口管理器 (DWM) 合成计时信息。由DwmGetCompositionTimingInfo函数使用。

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;
Run Code Online (Sandbox Code Playgroud)

(注:为了横向压缩以上源代码以在本网站上显示,假设前面有以下缩写:)

typedef UNSIGNED_RATIO URATIO;
typedef DWM_FRAME_COUNT CFRAMES;
Run Code Online (Sandbox Code Playgroud)

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

与现代来讲,因为在现代化暗示的问题,你要考虑使用IDXGISwapChain1从获得IDXGIFactory2 :: CreateSwapChainForComposition以便能够使用新的DirectComposition组件。

DirectComposition通过实现高帧率、使用图形硬件和独立于 UI 线程运行来实现丰富而流畅的过渡。DirectComposition 可以接受由不同渲染库绘制的位图内容,包括 Microsoft DirectX 位图和渲染到窗口的位图(HWND 位图)。此外,DirectComposition 支持各种变换,例如 2D 仿射变换和 3D 透视变换,以及基本效果,例如剪裁和不透明度。

无论如何,详细的时间信息似乎不太可能有用地告知应用程序的运行时行为;也许它会帮助你预测你的下一个 VSync,但人们确实想知道“对空白期的敏锐意识”对于某些特定的 DWM 压制的屏幕外交换链可能有什么意义。

因为您的应用程序的表面只是 DWM 正在处理的众多表面之一,所以 DWM 将在每个客户端行为一致的假设下自行进行各种动态调整。在这样的制度中,不可预测的适应是不合作的,最终很可能会混淆双方。




注:
1. QPC 的分辨率比DateTime刻度线的分辨率高许多数量级,尽管后者暗示使用 100 ns。单位名称。将其DateTime.Now.Ticks视为 (毫秒表示) 的重新打包Environment.TickCount,但转换为 100 ns 单位。要获得尽可能高的分辨率,请使用静态方法Stopwatch.GetTimestamp()而不是DateTime.Now.Ticks.

  • *我发现这是非常有用的信息* OP 的语言提出了一个应用程序,就像我自己的一样,它提供刺激并测量与刺激时间锁定的生理反应。例如,解码脑干视觉诱发电位取决于知道光子何时以微秒精度撞击视网膜。在这种情况下预测 vsync 没有意义,所以这个期待已久的答案是正确的! (2认同)