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

时间:2011-07-12 21:30:40

标签: c++ graphics directx vsync

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

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

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

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

4 个答案:

答案 0 :(得分:3)

很抱歉迟到的回复,但我注意到仍然没有接受答案,所以也许你从来没有找到一个有效的。如今,在Windows上, DesktopWindowManager 服务(dwm.exe)可以协调所有内容,并且无法绕过。自从Windows 8以来,这项服务无法被禁用。

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

  1. 获取极其精确的时序信息,但仅用于诊断,分析或信息使用,或
  2. 该应用程序是否会(尝试)使用这些结果(尝试)安排自己的当前周期。
  3. 如果是后者,我相信如果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;
    

    (注意:要横向压缩上述源代码以便在本网站上显示,请假设以下缩写:)

    typedef UNSIGNED_RATIO URATIO;
    typedef DWM_FRAME_COUNT DWMFC;
    typedef QPC_TIME QPCT;
    

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

    说到现代,因为问题暗示现代化,你要考虑使用从IDXGISwapChain1获得的IDXGIFactory2::CreateSwapChainForComposition来使用新的DirectComposition组件

      

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

    无论如何,详细的时序信息似乎不太可能有用地告知应用程序的运行时行为; 也许 它可以帮助您预测下一个VSync,但是一个确实想知道什么意义"敏锐的意识到消隐期"可能有一些特定的DWM subjugated屏幕外交换链。

    因为你的应用程序的表面只是DWM玩杂耍的众多表面之一,所以DWM将在每个客户端表现一致的假设下进行自己的各种动态调整。在这种制度下,不可预测的适应性是不合作的,并且可能最终会使双方混淆。


    <小时/>
    备注:
    1。 QPC的分辨率比DateTime刻度高出许多个数量级,尽管后者建议使用100 ns。单位面额。将DateTime.Now.Ticks视为重新包装(毫秒表示)Environment.TickCount,但转换为100 ns单位。要获得尽可能高的分辨率,请使用静态方法Stopwatch.GetTimestamp()代替DateTime.Now.Ticks

答案 1 :(得分:1)

另一种选择:

D3DKMTGetScanLine()适用于D3D9,D3D10,D3D11,D3D12甚至OpenGL。

它实际上是一个GDI32函数,所以你可以捎带Window的现有图形hAdaptor来轮询VBlank / Scanline - 无需创建Direct3D帧缓冲区。这就是为什么这个API也适用于OpenGL,Mantle和非Direct3D渲染器,尽管这个API调用的D3D前缀。

它还告诉你VBlank状态&amp;光栅扫描线。

它对于极限“延迟至关重要”应用中的波束竞赛应用非常有用。一些虚拟现实呈现使用光束竞赛,即使仅仅20ms的延迟也可能意味着愉快的VR和令人眼花缭乱/可怜的VR之间的区别。

在扫描输出显示后,光束比赛即时渲染。在特殊的延迟关键应用程序中,您可以减少从Direct3D Present()到击中眼球的像素的延迟,到绝对最小值(最短3ms)。

要了解什么样的光束比赛,https://www.wired.com/2009/03/racing-the-beam/ - 在图形芯片没有帧缓冲的那一天很常见 - 在Atari 2600,Nintendo,Commodore 64等上改进图形所需的光束比赛...

有关更加现代化的光束比赛实现,请参阅Lagless VSYNC ON Algorithm for Emulators

答案 2 :(得分:0)

“特别是,当屏幕上实际绘制出任何被翻转到主表面(或出现在交换链中)的东西时,我特别需要以最大的准确性来知道。”

祝你好运。

实际上不能保证您放入当前队列的任何内容都会在屏幕上显示 (!!) ;您可以通过带缓冲序列的当前标志手动删除帧,或者NVIDIA可以为您做到(... 谢谢?)

DXGI中的缓冲区排序

DXGI Swapchain的翻转队列通常是FIFO,但是与延迟相关的用户肯定可以启用流行的新驱动程序替代(即FastSync),它比显示诸如绘制任何帧之类的琐碎事情更倾向于CPU端吞吐量: )

通常,当交换链中充满未显示的图像并且驱动程序为staging commands n-many frames ahead of the GPU时,通常您可以依靠IDXGISwapChain :: Present(...)开始阻止,但是在强制使用FastSync的情况下,Present从不阻止,并且呈现-预先队列通过覆盖Swapchain中等待VBLANK的所有已完成帧来刷新其工作。

背对背呈现比屏幕刷新更快的呈现没有义务(也不会)进行扫描,因此它们相对于VBLANK的状态是没有意义的。

除非您实施速率限制以防止CPU在对Present进行任何调用后立即登台下一帧,否则您将需要一个不同的模式来完全测量帧状态。

D3D9Ex / DXGI支持翻转/全屏独占的演示统计:

除非以下API明确指出,否则框架实际上不会呈现给用户:

IDXGISwapChain::GetFrameStatistics (...)IDXGISwapChain::GetLastPresentCount (...)

您可以使用帧统计信息实时计算渲染队列的长度/当前等待时间,并且可以通过针对成功同步帧的计费信息跟踪当前#来满足您的计时目标。

答案 3 :(得分:-2)

这里的问题是为什么?看起来你想解决问题的症状;也许这会分散你真正的问题。在Amiga或DOS上等待vsync是一种有用的技术。在任何合成或多线程操作系统上都是完全错误的。

首先,你想要实现什么?通过在D3D或OpenGL上设置交换间隔来完成无撕裂渲染。尝试做得比那里的操作系统更好是有害的。只需考虑多个监视器等情况,或者如果多个应用程序尝试同步会发生什么。

如果您是某个其他进程的客户端并希望在VSync上运行您的计时,那么据我所知,Windows无法提供等待的对象。你最好的选择仍然是依靠现在的电话并估计正在发生的事情。

有两种情况:您要么比vsync渲染(呈现)更快或更慢。如果你更快,现在应该阻止你。如果存在,则永远不会等待,并且您的通话间隔时间超过1/60秒,您可能希望不经常渲染。

人们关心VSync的最常见情况是视频。你可以比vsync更快地渲染,但是想要等待合适的时间来呈现。唯一要做的就是尽可能快地运行几帧,并根据估计帧时间。使用一些抖动和反馈...或者使用内置的硬件视频,这些视频很高兴成为视频驱动程序的内核朋友。