我正在更新一个应用程序,其中在屏幕上呈现刺激的时间的测量需要最大的准确度。它目前用DirectDraw编写,很久以前就被放到了牧场,需要更新我们的图形库。
我们测量演示时间的方式是利用检测垂直空白时段的结束。具体而言,当需要翻转到主表面(或在交换链中呈现)的任何内容实际上被屏幕绘制时,我需要知道最大可能的准确度。检测扫描线可以增加该测量的确定性,但是我只能检测在翻转或存在之后立即结束垂直空白周期的时间。
Direct 3D 9具有IDirect3DDevice9::GetRasterStatus Method,它返回一个D3DRASTER_STATUS结构,其中包含一个InVBlank布尔值,用于描述设备是否处于垂直空白状态,以及当前扫描线。 DirectDraw具有类似的功能(IDirectDraw::GetVerticalBlankStatus,IDirectDraw :: GetScanLine在垂直空白期间返回DDERR_VERTICALBLANKINPROGRESS可用于检测VB)。
但是我在Direct3D11中找不到任何类似的功能。有没有人知道Direct3D9和Direct3D11之间是否移动或移除了这个功能,如果是后者,为什么?
答案 0 :(得分:3)
很抱歉迟到的回复,但我注意到仍然没有接受答案,所以也许你从来没有找到一个有效的。如今,在Windows上, DesktopWindowManager 服务(dwm.exe)可以协调所有内容,并且无法绕过。自从Windows 8以来,这项服务无法被禁用。
因此,DWM始终会控制所有各种IDXGISurface(n)对象和IDXGIOutput(n)监视器的帧速率,渲染队列管理和最终合成,并且在跟踪中没有多大用处屏幕渲染目标的 VSync ,除非我遗漏了某些内容(没有讽刺意图)。至于你的问题,我不确定你的目标是:
如果是后者,我相信如果D3D应用程序以全屏独占模式运行,您只能有效地执行此操作。这是唯一一种情况,DWM以 DXGI - 的名义真正信任客户端处理自己的Present
定时。
这里的(好消息)好消息是,如果您对 VSync 的兴趣只是信息性 - 也就是说您从上面属于子弹类别(1.)那么您确实可以获得你想要的所有时间数据,QueryPerformanceFrequency分辨率,通常在320 ns.¹左右
以下是如何获得高分辨率视频时序信息。但同样要明确的是,尽管取得了明显的成功(根据传统的程序观察),任何试图调整如下获得的读数的确定性(因而可能有用)结果的尝试实际上将被DWM中介挫败:
指定桌面窗口管理器(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将在每个客户端表现一致的假设下进行自己的各种动态调整。在这种制度下,不可预测的适应性是不合作的,并且可能最终会使双方混淆。
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状态&光栅扫描线。
它对于极限“延迟至关重要”应用中的波束竞赛应用非常有用。一些虚拟现实呈现使用光束竞赛,即使仅仅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 Swapchain的翻转队列通常是FIFO,但是与延迟相关的用户肯定可以启用流行的新驱动程序替代(即FastSync),它比显示诸如绘制任何帧之类的琐碎事情更倾向于CPU端吞吐量: )
通常,当交换链中充满未显示的图像并且驱动程序为staging commands n-many frames ahead of the GPU时,通常您可以依靠IDXGISwapChain :: Present(...)开始阻止,但是在强制使用FastSync的情况下,Present从不阻止,并且呈现-预先队列通过覆盖Swapchain中等待VBLANK的所有已完成帧来刷新其工作。
背对背呈现比屏幕刷新更快的呈现没有义务(也不会)进行扫描,因此它们相对于VBLANK的状态是没有意义的。
除非您实施速率限制以防止CPU在对Present进行任何调用后立即登台下一帧,否则您将需要一个不同的模式来完全测量帧状态。
除非以下API明确指出,否则框架实际上不会呈现给用户:
IDXGISwapChain::GetFrameStatistics (...)和IDXGISwapChain::GetLastPresentCount (...)
您可以使用帧统计信息实时计算渲染队列的长度/当前等待时间,并且可以通过针对成功同步帧的计费信息跟踪当前#来满足您的计时目标。
答案 3 :(得分:-2)
这里的问题是为什么?看起来你想解决问题的症状;也许这会分散你真正的问题。在Amiga或DOS上等待vsync是一种有用的技术。在任何合成或多线程操作系统上都是完全错误的。
首先,你想要实现什么?通过在D3D或OpenGL上设置交换间隔来完成无撕裂渲染。尝试做得比那里的操作系统更好是有害的。只需考虑多个监视器等情况,或者如果多个应用程序尝试同步会发生什么。
如果您是某个其他进程的客户端并希望在VSync上运行您的计时,那么据我所知,Windows无法提供等待的对象。你最好的选择仍然是依靠现在的电话并估计正在发生的事情。
有两种情况:您要么比vsync渲染(呈现)更快或更慢。如果你更快,现在应该阻止你。如果存在,则永远不会等待,并且您的通话间隔时间超过1/60秒,您可能希望不经常渲染。
人们关心VSync的最常见情况是视频。你可以比vsync更快地渲染,但是想要等待合适的时间来呈现。唯一要做的就是尽可能快地运行几帧,并根据估计帧时间。使用一些抖动和反馈...或者使用内置的硬件视频,这些视频很高兴成为视频驱动程序的内核朋友。