Files
picom/src/vsync.c
Yuxuan Shui cebe1b18d6 vsync: choose vsync method automatically
Choose the best vsync method for the user, instead of asking them to
frustratingly try every one of the options to see what works.

With this commit, the `vsync` option will take only a boolean value.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2019-03-10 15:00:56 +00:00

205 lines
4.5 KiB
C

// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
/// Function pointers to init VSync modes.
#include "common.h"
#include "log.h"
#ifdef CONFIG_OPENGL
#include "backend/gl/glx.h"
#include "opengl.h"
#endif
#ifdef CONFIG_VSYNC_DRM
#include <drm.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif
#include "config.h"
#include "vsync.h"
#ifdef CONFIG_VSYNC_DRM
/**
* Wait for next VSync, DRM method.
*
* Stolen from:
* https://github.com/MythTV/mythtv/blob/master/mythtv/libs/libmythtv/vsync.cpp
*/
static int vsync_drm_wait(session_t *ps) {
int ret = -1;
drm_wait_vblank_t vbl;
vbl.request.type = _DRM_VBLANK_RELATIVE, vbl.request.sequence = 1;
do {
ret = ioctl(ps->drm_fd, DRM_IOCTL_WAIT_VBLANK, &vbl);
vbl.request.type &= ~_DRM_VBLANK_RELATIVE;
} while (ret && errno == EINTR);
if (ret)
log_error("VBlank ioctl did not work, unimplemented in this drmver?");
return ret;
}
/**
* Initialize DRM VSync.
*
* @return true for success, false otherwise
*/
static bool vsync_drm_init(session_t *ps) {
// Should we always open card0?
if (ps->drm_fd < 0 && (ps->drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) {
log_error("Failed to open device.");
return false;
}
if (vsync_drm_wait(ps))
return false;
return true;
}
#endif
#ifdef CONFIG_OPENGL
/**
* Initialize OpenGL VSync.
*
* Stolen from:
* http://git.tuxfamily.org/?p=ccm/cairocompmgr.git;a=commitdiff;h=efa4ceb97da501e8630ca7f12c99b1dce853c73e
* Possible original source: http://www.inb.uni-luebeck.de/~boehme/xvideo_sync.html
*
* @return true for success, false otherwise
*/
static bool vsync_opengl_init(session_t *ps) {
if (!ensure_glx_context(ps))
return false;
return glxext.has_GLX_SGI_video_sync;
}
static bool vsync_opengl_oml_init(session_t *ps) {
if (!ensure_glx_context(ps))
return false;
return glxext.has_GLX_OML_sync_control;
}
static inline bool vsync_opengl_swc_swap_interval(session_t *ps, unsigned int interval) {
if (glxext.has_GLX_MESA_swap_control)
return glXSwapIntervalMESA(interval) == 0;
else if (glxext.has_GLX_SGI_swap_control)
return glXSwapIntervalSGI(interval) == 0;
else if (glxext.has_GLX_EXT_swap_control) {
GLXDrawable d = glXGetCurrentDrawable();
if (d == None) {
// We don't have a context??
return false;
}
glXSwapIntervalEXT(ps->dpy, glXGetCurrentDrawable(), interval);
return true;
}
return false;
}
static bool vsync_opengl_swc_init(session_t *ps) {
if (!bkend_use_glx(ps)) {
log_error("OpenGL swap control requires the GLX backend.");
return false;
}
if (!vsync_opengl_swc_swap_interval(ps, 1)) {
log_error("Failed to load a swap control extension.");
return false;
}
return true;
}
/**
* Wait for next VSync, OpenGL method.
*/
static int vsync_opengl_wait(session_t *ps) {
unsigned vblank_count = 0;
glXGetVideoSyncSGI(&vblank_count);
glXWaitVideoSyncSGI(2, (vblank_count + 1) % 2, &vblank_count);
return 0;
}
/**
* Wait for next VSync, OpenGL OML method.
*
* https://mail.gnome.org/archives/clutter-list/2012-November/msg00031.html
*/
static int vsync_opengl_oml_wait(session_t *ps) {
int64_t ust = 0, msc = 0, sbc = 0;
glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc);
glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2, &ust, &msc, &sbc);
return 0;
}
#endif
/**
* Initialize current VSync method.
*/
bool vsync_init(session_t *ps) {
#ifdef CONFIG_OPENGL
if (bkend_use_glx(ps)) {
// Mesa turns on swap control by default, undo that
vsync_opengl_swc_swap_interval(ps, 0);
}
#endif
#ifdef CONFIG_VSYNC_DRM
log_warn("The DRM vsync method is deprecated, please don't enable it.");
#endif
if (!ps->o.vsync) {
return true;
}
#ifdef CONFIG_OPENGL
if (bkend_use_glx(ps)) {
if (!vsync_opengl_swc_init(ps)) {
return false;
}
ps->vsync_wait = NULL; // glXSwapBuffers will automatically wait
// for vsync, we don't need to do anything.
return true;
}
#endif
// Oh no, we are not using glx backend.
// Throwing things at wall.
#ifdef CONFIG_OPENGL
if (vsync_opengl_oml_init(ps)) {
log_info("Using the opengl-oml vsync method");
ps->vsync_wait = vsync_opengl_oml_wait;
return true;
}
if (vsync_opengl_init(ps)) {
log_info("Using the opengl vsync method");
ps->vsync_wait = vsync_opengl_wait;
return true;
}
#endif
#ifdef CONFIG_VSYNC_DRM
if (vsync_drm_init(ps)) {
log_info("Using the drm vsync method");
ps->vsync_wait = vsync_drm_wait;
return true;
}
#endif
log_error("No supported vsync method found for this backend");
return false;
}