cx11framebufferepollglx

GLX Vsync event


I'm wondering if I could catch the screen vsync event by any file descriptor and [select | poll | epoll]ing it.

Normally, if I'm right, glXSwapBuffers() doesn't block the process so I could do something like :

int init() {
    create epollfd;
    add Xconnection number to it;
    add some other fd like socket timer tty etc...
    possibly add a vsync fd like dri/card0 or fb0 or other???
    return epollfd;
}

main() {
    int run = 1;
    int epollfd = init();

    while(run) {
        epoll_wait(epollfd, ...) {

        if(trigedfd = socket) {
            do network computing;
        }

        if(trigedfd = timer) {
            do physics computing;
        }

        if(trigedfd = tty) {
            do electronic communications;
        }

        if(trigedfd = Xconnection number) {
            switch(Xevent) {
                case key event:
                    do key computing;
                case mouse event:
                    do mouse computing;
                case vsync???:
                    do GL computings;
                    glXSwapBuffers();
            }
        }

        if(trigedfd = dri/card0 or fb0 or other???) {
            do GL computings;
            glXSwapBuffers();
        }
    }
}

So I could then I could trig any event regardless when vsync event happen and avoid by the same time tearing effect in the case where I use only X drawing function and possibly GL for vsync.

Could libdrm help me? the more general question is :

So what fd do I have to use to catch vsync event and how to make shure on this fd that the event that happened is a vsync event?


Solution

  • It looks like you can use the libdrm API to see vsync events. See this blog entry, and in particular this example code. A comment from the code explains how it works:

    /* (...)
     * The DRM_MODE_PAGE_FLIP_EVENT flag tells drmModePageFlip() to send us a
     * page-flip event on the DRM-fd when the page-flip happened. The last argument
     * is a data-pointer that is returned with this event.
     * (...)
     */
    

    You need to set up a page-flip event handler to be notified when a vsync occurs, which will be called by the drmHandleEvent method (from libdrm) which you can call when there is activity on the drm file descriptor.

    However, mapping all this into an X client might prove difficult or impossible. It may be that you can open the drm device yourself and just listen for vsync events (without attempting to set the mode etc), but this might also prove impossible. Pertinent code:

    drmEventContext ev;
    memset(&ev, 0, sizeof(ev));
    ev.version = DRM_EVENT_CONTEXT_VERSION;
    ev.page_flip_handler = modeset_page_flip_event;
    
    // When file descriptor input is available:
    drmHandleEvent(fd, &ev);
    // If above works, "modeset_page_flip_event" will be called on vertical refresh.
    

    The problem is that a page flip event only seems to be generated if you have actually issued a page flip (buffer swap) request. Presumably it would be the X server that issued such requests, but it doesn't even necessarily flag that it wants to be notified when the vsync actually occurs (i.e. uses the DRM_MODE_PAGE_FLIP_EVENT flag).

    There's also the difficulty of opening the correct dri device (/dev/dri/card0 or /dev/dri/card1 or ...?)

    Given the difficulty/unreliability/general unworkability of all the above, the easiest solution is probably to:

    1. Use a separate thread to wait for vsync using standard GL calls. According to this page on the OpenGL wiki you should use glXSwapIntervalEXT(1) to enable vsync, then glXSwapBuffers and glFinish to ensure that the vertical retrace actually occurs.
    2. Then, notify the main thread that vertical retrace occurred. You can do this by sending data to a pipe or, on Linux, use an eventfd.

    Update:

    You may be able to use the GLX_INTEL_swap_event extension:

    Accepted by the parameter of glXSelectEvent and returned in the parameter of glXGetSelectedEvent:

    GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK 0x04000000

    Returned in the <event_type> field of a "swap complete" event:

    GLX_EXCHANGE_COMPLETE_INTEL 0x8180
    GLX_COPY_COMPLETE_INTEL 0x8181
    GLX_FLIP_COMPLETE_INTEL 0x8182

    ...

    A client can ask to receive swap complete GLX events on a window. When an event is received, the caller can determine what kind of swap occurred by checking the event_type field.

    This means that, if the extension is supported, you can receive swap notifications (which correspond to vertical retrace if vsync is enabled) via regular X events, which you'll see on the X connection file descriptor.