我正在更新一个应用程序,其中在屏幕上显示刺激信号的时间需要最大的准确性。目前,它是使用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
。