Skip to content

Commit 4aa4cfb

Browse files
committed
fix: improve KMS vblank frame pacing for vsync games
Use display's actual vblank period from CRTC mode timing to determine when to skip a vblank. This prevents spurious frame drops when display refresh rate matches or is close to the stream FPS, and provides tighter frame selection for mismatched rates (e.g. 144Hz -> 60fps).
1 parent 9d78659 commit 4aa4cfb

1 file changed

Lines changed: 11 additions & 13 deletions

File tree

src/platform/linux/kmsgrab.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,12 @@ namespace platf {
679679

680680
kms::print(plane.get(), fb.get(), crtc.get());
681681

682+
if (crtc->mode.clock && crtc->mode.htotal && crtc->mode.vtotal) {
683+
vblank_period = std::chrono::nanoseconds(
684+
(int64_t)(1e9 * crtc->mode.htotal * crtc->mode.vtotal / ((double) crtc->mode.clock * 1000.0)));
685+
} else {
686+
vblank_period = delay;
687+
}
682688
img_width = fb->width;
683689
img_height = fb->height;
684690
img_offset_x = crtc->x;
@@ -1104,6 +1110,7 @@ namespace platf {
11041110
mem_type_e mem_type;
11051111

11061112
std::chrono::nanoseconds delay;
1113+
std::chrono::nanoseconds vblank_period; // display refresh period from CRTC mode
11071114

11081115
int img_width, img_height;
11091116
int img_offset_x, img_offset_y;
@@ -1172,18 +1179,13 @@ namespace platf {
11721179
vbl.request.type = (drmVBlankSeqType)(DRM_VBLANK_RELATIVE | (crtc_index << DRM_VBLANK_HIGH_CRTC_SHIFT));
11731180
vbl.request.sequence = 1;
11741181
if (drmWaitVBlank(card.fd.el, &vbl) == 0) {
1175-
// DRM vblank time is CLOCK_MONOTONIC, same as steady_clock on Linux
11761182
auto vblank_time = std::chrono::seconds(vbl.reply.tval_sec) + std::chrono::microseconds(vbl.reply.tval_usec);
11771183
vblank_timestamp = std::chrono::steady_clock::time_point(std::chrono::duration_cast<std::chrono::steady_clock::duration>(vblank_time));
11781184

1179-
// Skip frame if display Hz > client FPS (e.g., 120Hz display, 60fps stream)
1180-
if (*vblank_timestamp < next_frame) {
1185+
if (*vblank_timestamp < next_frame && next_frame - *vblank_timestamp > vblank_period / 2) {
11811186
continue;
11821187
}
1183-
next_frame += delay;
1184-
if (next_frame < *vblank_timestamp) {
1185-
next_frame = *vblank_timestamp + delay;
1186-
}
1188+
next_frame = *vblank_timestamp + delay;
11871189
} else {
11881190
// Vblank wait failed, fall back to timer-based delay
11891191
auto now = std::chrono::steady_clock::now();
@@ -1435,14 +1437,10 @@ namespace platf {
14351437
auto vblank_time = std::chrono::seconds(vbl.reply.tval_sec) + std::chrono::microseconds(vbl.reply.tval_usec);
14361438
vblank_timestamp = std::chrono::steady_clock::time_point(std::chrono::duration_cast<std::chrono::steady_clock::duration>(vblank_time));
14371439

1438-
// Skip frame if display Hz > client FPS (e.g., 120Hz display, 60fps stream)
1439-
if (*vblank_timestamp < next_frame) {
1440+
if (*vblank_timestamp < next_frame && next_frame - *vblank_timestamp > vblank_period / 2) {
14401441
continue;
14411442
}
1442-
next_frame += delay;
1443-
if (next_frame < *vblank_timestamp) {
1444-
next_frame = *vblank_timestamp + delay;
1445-
}
1443+
next_frame = *vblank_timestamp + delay;
14461444
} else {
14471445
// Vblank wait failed, fall back to timer-based delay
14481446
auto now = std::chrono::steady_clock::now();

0 commit comments

Comments
 (0)