enumdisplaysettings 返回显示器标称刷新率而非实时v-sync频率;需传正确 dmsize 和设备名(或 null 用于单屏主显),xrandr/libdrm 在 linux 获取同样为标称值;实时vblank频率不可靠,应用应缓存标称值并用高精度计时器动态调整。

Windows平台用 EnumDisplaySettings 获取当前显示器标称刷新率
Windows下没有直接暴露“实时V-Sync频率”的API,操作系统和显卡驱动只提供当前显示模式下的**标称垂直刷新率**(即显卡设置里选的那个值),它由 EnumDisplaySettings 返回的 DEVMODE.dmDisplayFrequency 字段给出。这个值不是实时测量的,而是驱动根据EDID或用户手动设置确认的理论值。
常见错误是试图用 GetDeviceCaps(HORZRES/VERTRES) 或 GetSystemMetrics 获取刷新率——它们根本不返回频率信息。
- 必须传入正确的
dmSize = sizeof(DEVMODE),否则调用失败且不报错 - 设备名要用
EnumDisplayDevices枚举出的DeviceName,不能硬写"\\.\DISPLAY1"(不同系统编号可能变化) - 若想获取主显示器,可传
NULL作为设备名,但仅限单显示器环境;多屏时需枚举并匹配dwFlags & DISPLAY_DEVICE_PRIMARY
Linux上通过 xrandr 和 libdrm 查看当前模式刷新率
Linux桌面环境(X11)中,xrandr --verbose 输出里每种模式都带 h: width ... v: height ... +hsync +vsync 行,其后紧跟的 60.00*+ 这类标记就是当前激活模式的标称刷新率。星号(*)表示当前活动模式,加号(+)表示首选模式。
程序内获取需解析 xrandr 输出或使用 libdrm + libkms 直接读取KMS(Kernel Mode Setting)接口:
-
drmModeGetConnector返回的drmModeModeInfo结构中,vrefresh字段即为该模式标称垂直频率(单位Hz) - 注意:此值仍是EDID声明值,非实时测量;DRM层不提供运行时帧率采样接口
- Wayland环境下无全局xrandr,需走compositor协议(如wlr-output-management-unstable-v1),但多数compositor不暴露实时vblank计数
无法真正获取“实时垂直频率”,只能间接估算
所谓“实时V-Sync频率”在OS层面并不存在统一概念——GPU每帧等待vblank信号是硬件行为,驱动只保证帧提交与vblank对齐(启用V-Sync时),但不对外暴露vblank发生时间戳或计数器。
可行的近似方案是:在应用层连续记录两次垂直同步事件的时间间隔,再换算成Hz。这依赖底层支持:
- OpenGL:用
glXSwapIntervalEXT(1)启用V-Sync后,调用glXWaitVideoSyncSGI可阻塞到下一vblank,并用glXGetVideoSyncSGI获取vblank计数(仅NVIDIA驱动稳定支持,AMD/Intel常返回-1) - Vulkan:可通过
vkQueuePresentKHR的VkPresentInfoKHR中pResults判断是否被vsync延迟,但无vblank时间戳 - Windows DXGI:
IDXGISwapChain::Present返回DXGI_STATUS_OCCLUDED或DXGI_PRESENT_DISCARD不反映vblank,需配合QueryPerformanceCounter手动打点测间隔
误差来源明显:系统调度延迟、驱动队列深度、前台/后台切换都会导致两次vblank间隔波动,连续5次测量的标准差常达±1.5Hz以上。
跨平台游戏引擎通常只缓存标称值,不尝试实时跟踪
像SDL2、GLFW这类库在创建窗口时会调用平台API获取当前模式刷新率(如SDL的 SDL_GetDesktopDisplayMode 返回 refresh_rate 字段),但这个值在窗口生命周期内不会自动更新——即使用户中途在系统设置里改了刷新率,应用也不会感知。
实际开发中更可靠的做法是:
- 启动时读一次标称值,用于初始化帧时间预算(如
1000.0f / refresh_rate) - 运行时用高精度计时器(
std::chrono::steady_clock)统计真实帧间隔,动态调整逻辑步进或渲染负载,而非依赖“实时V-Sync频率” - 若需检测刷新率变更(如Win10多屏不同频),监听
WM_DISPLAYCHANGE(Windows)或ScreenChangeNotify(X11),然后重新查询
真正需要精确vblank时间戳的场景(如专业视频同步、VR姿态预测)基本绕过OS抽象层,直接对接GPU厂商提供的专用SDK(如NVIDIA VIO、AMD ADL),且仅限特定硬件型号。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











