Skip to content

drm-gbm: present via vsynced page flips instead of per-frame SetCrtc#25

Open
lhoward wants to merge 1 commit into
masterfrom
lhoward/vsync-drm
Open

drm-gbm: present via vsynced page flips instead of per-frame SetCrtc#25
lhoward wants to merge 1 commit into
masterfrom
lhoward/vsync-drm

Conversation

@lhoward
Copy link
Copy Markdown

@lhoward lhoward commented May 5, 2026

Disclaimer: AI-generated

NativeWindowDrmGbm::SwapBuffers() called drmModeSetCrtc every frame, which is not vsynced -- the CRTC re-latches its scanout buffer immediately, not at vblank. On slower panels with high-contrast transitions the seam is plainly visible as tearing. Refs sony#136.

Use drmModePageFlip with DRM_MODE_PAGE_FLIP_EVENT after the initial modeset. WaitForPageFlip() drains the previous flip event before the next is queued, and a release-on-flip handshake (bo_release_after_flip_ -> bo_pending_release_) ensures a displaced BO is freed only after the kernel has stopped scanning it out.

No explicit GPU wait is needed: eglSwapBuffers attaches an implicit dma-buf exclusive fence to the rendered BO, which the legacy page-flip path already honours -- the kernel will not latch a framebuffer whose fence has not signalled.

Resize() drains the in-flight flip and re-arms modeset for the fresh GBM surface; the destructor drains before restoring the original CRTC. enable_vsync_ remains a gate -- passing enableVSync: false in the Swift wrapper restores the legacy synchronous SetCrtc path.

NativeWindowDrmGbm::SwapBuffers() called drmModeSetCrtc every frame,
which is not vsynced -- the CRTC re-latches its scanout buffer
immediately, not at vblank. On slower panels with high-contrast
transitions the seam is plainly visible as tearing. Refs sony#136.

Use drmModePageFlip with DRM_MODE_PAGE_FLIP_EVENT after the initial
modeset. WaitForPageFlip() drains the previous flip event before the
next is queued, and a release-on-flip handshake (bo_release_after_flip_
-> bo_pending_release_) ensures a displaced BO is freed only after the
kernel has stopped scanning it out.

No explicit GPU wait is needed: eglSwapBuffers attaches an implicit
dma-buf exclusive fence to the rendered BO, which the legacy page-flip
path already honours -- the kernel will not latch a framebuffer whose
fence has not signalled.

Resize() drains the in-flight flip and re-arms modeset for the fresh
GBM surface; the destructor drains before restoring the original CRTC.
enable_vsync_ remains a gate -- passing enableVSync: false in the
Swift wrapper restores the legacy synchronous SetCrtc path.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019df5d6-7d1c-755a-9be1-e10d54295a9f
@lhoward lhoward force-pushed the lhoward/vsync-drm branch from 0886004 to 12b5024 Compare May 5, 2026 04:36
@lhoward lhoward requested a review from martinetd May 5, 2026 20:38
@lhoward lhoward self-assigned this May 15, 2026
@lhoward lhoward added the enhancement New feature or request label May 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant