16

我正在更新一个应用程序,在该应用程序中,测量屏幕上刺激呈现的时间需要最高精度。它目前是用 DirectDraw 编写的,很久以前就被淘汰了,需要更新我们的图形库。

我们测量呈现时间的方法是利用检测垂直空白期的结束。具体来说,我需要尽可能准确地知道,当翻转到主表面(或显示在交换链中)的任何内容实际上被屏幕绘制时。检测扫描线可以增加该测量的确定性,但我只能检测在调用 Flip 或 Present 后垂直空白周期何时结束。

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

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

4

4 回答 4

9

很抱歉回复晚了,但我注意到仍然没有接受的答案,所以也许你从来没有找到一个有效的答案。如今在 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;

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

typedef UNSIGNED_RATIO URATIO;
typedef DWM_FRAME_COUNT CFRAMES;

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

说到现代,由于问题暗示了现代化,您需要考虑使用从IDXGIFactory2::CreateSwapChainForComposition获得的 IDXGISwapChain1来启用新的DirectComposition组件。

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

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

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




注释:
1. QPC 的分辨率比DateTimetick 的分辨率高出许多数量级,尽管后者建议使用 100 ns。单位面额。将DateTime.Now.Ticks其视为 (millisecond-denoted) 的重新包装Environment.TickCount,但转换为 100-ns 单位。要获得尽可能高的分辨率,请使用静态方法Stopwatch.GetTimestamp()而不是DateTime.Now.Ticks.

于 2017-02-26T10:55:44.687 回答
4

另一种选择:

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

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

它还告诉您 VBlank 状态和光栅扫描线。

它对于“延迟至关重要”应用程序中的光束竞赛应用程序很有用。一些虚拟现实渲染使用光束竞赛,即使只有 20 毫秒的延迟也可能意味着令人愉悦的 VR 和令人眼花缭乱/令人眼花缭乱的 VR 之间的差异。

在扫描显示之后,Beam Racing 正在运行中渲染。在专门的延迟关键应用程序中,您可以将 Direct3D Present() 到像素击中眼球的延迟减少到绝对最小值(低至 3 毫秒)。

要了解什么是光束竞赛,https ://www.wired.com/2009/03/racing-the-beam/——在图形芯片没有帧缓冲区的时代很常见——使得光束竞赛成为必要的改进了 Atari 2600、Nintendo、Commodore 64 等的图形...

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

于 2018-04-03T23:45:05.187 回答
0

“具体来说,我需要尽可能准确地知道,什么时候翻转到主表面(或显示在交换链中)的东西实际上是由屏幕绘制的。”

祝你好运。

实际上并不能保证您放入当前队列的任何内容都会显示在屏幕上(!!);您可以手动丢弃带有缓冲区排序存在标志的帧,或者 NVIDIA 可以为您完成(...谢谢?)

DXGI 中的缓冲区测序

DXGI Swapchain 的翻转队列通常是 FIFO,但是关注延迟的用户肯定会启用流行的新驱动程序覆盖(即 FastSync),支持 CPU 端吞吐量而不是显示您绘制的任何帧这样的琐碎事情:)

通常你可以指望 IDXGISwapChain::Present (...) 当交换链充满未显示的图像并且驱动程序在 GPU 之前暂存命令 n 帧时开始阻塞,但是强制使用 FastSync,Present 永远不会阻塞并且render-ahead-queue 通过覆盖 Swapchain 中等待 VBLANK 的任何已完成帧来刷新其工作。

比屏幕刷新更快完成的背靠背呈现没有义务(也不会)扫描出来,因此它们与 VBLANK 相关的状态是没有意义的。

除非您自己实施速率限制以防止 CPU 在对 Present 的任何调用后立即暂存下一帧,否则您需要一个不同的范例来完全测量帧状态。

D3D9Ex / DXGI 支持翻转/全屏显示统计独家:

框架实际上不会呈现给用户,除非以下 API 声明它们这样做:

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

您可以使用帧统计数据来实时计算渲染队列的长度/当前延迟,并且您的时间目标可能可以通过跟踪当前 # 与成功同步帧的记帐信息来满足。

于 2020-10-21T12:53:10.543 回答
-2

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

首先,你想达到什么目的?通过在 D3D 或 OpenGL 上设置交换间隔来完成无撕裂渲染。试图比那里的操作系统做得更好是有害的。想想像多台显示器这样的情况,或者如果多个应用程序尝试同步会发生什么。

如果您是某个其他进程的客户端并希望在 VSync 上运行您的计时,那么不幸的是,据我所知,Windows 没有提供任何等待的对象。您最好的选择是仍然依赖当前调用并估计正在发生的事情。

有两种情况:您的渲染(呈现)比 vsync 更快或更慢。如果你更快,Present 应该已经为你阻止了。如果 present 从不等待,并且您的通话间隔时间超过 1/60 秒,您可能希望减少渲染频率。

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

于 2012-05-22T08:16:54.590 回答