View Single Post
Old 10-19-11, 03:25 PM   #11
lexa2
Registered User
 
Join Date: Jul 2011
Location: Moscow, Russian Federation
Posts: 58
Send a message via ICQ to lexa2 Send a message via Skype™ to lexa2
Exclamation Re: Wine + VSync forced in nvidia-settings doesn't work

I can confirm that for this demo there's no offect in ticking "Sync to VBlank" checkbox in nVIDIA X Server settings in case the demo runs in windowed mode. You can control VSync behavior through app configuration menu (accessed using F2 key) by changing D3D presentation interval setting. Switching app into using fullscreen mode makes vsync behave as expected (i.e. there's no tearing and FPS almost matched screen refresh rate).

Tested on GeForce 550 Ti + 280.13 drivers + Wine 1.3.30.

Inspecting Wine sources shows that currently Wine uses GLX_SGI_swap_control OpenGL extension from 1995. wined3d sets swap interval using this code:
Code:
    switch (swapchain->presentParms.PresentationInterval)
    {
        case WINED3DPRESENT_INTERVAL_IMMEDIATE:
            swap_interval = 0;
            break;
        case WINED3DPRESENT_INTERVAL_DEFAULT:
        case WINED3DPRESENT_INTERVAL_ONE:
            swap_interval = 1;
            break;
        case WINED3DPRESENT_INTERVAL_TWO:
            swap_interval = 2;
            break;
        case WINED3DPRESENT_INTERVAL_THREE:
            swap_interval = 3;
            break;
        case WINED3DPRESENT_INTERVAL_FOUR:
            swap_interval = 4;
            break;
        default:
            FIXME("Unknown presentation interval %08x\n", swapchain->presentParms.PresentationInterval);
            swap_interval = 1;
    }

    if (gl_info->supported[WGL_EXT_SWAP_CONTROL])
    {
        if (!GL_EXTCALL(wglSwapIntervalEXT(swap_interval)))
            ERR("wglSwapIntervalEXT failed to set swap interval %d for context %p, last error %#x\n",
                swap_interval, ret, GetLastError());
    }
wglSwapIntervalEXT() it uses actually translates to this:
Code:
...
...
static BOOL has_swap_control;
static int swap_interval = 1;
...
...
static int WINAPI X11DRV_wglGetSwapIntervalEXT(VOID) {
    /* GLX_SGI_swap_control doesn't have any provisions for getting the swap
     * interval, so the swap interval has to be tracked. */
    TRACE("()\n");
    return swap_interval;
}
...
...
static BOOL WINAPI X11DRV_wglSwapIntervalEXT(int interval) {
    BOOL ret = TRUE;

    TRACE("(%d)\n", interval);

    if (interval < 0)
    {
        SetLastError(ERROR_INVALID_DATA);
        return FALSE;
    }
    else if (!has_swap_control && interval == 0)
    {
        /* wglSwapIntervalEXT considers an interval value of zero to mean that
         * vsync should be disabled, but glXSwapIntervalSGI considers such a
         * value to be an error. Just silently ignore the request for now. */
        WARN("Request to disable vertical sync is not handled\n");
        swap_interval = 0;
    }
    else
    {
        if (pglXSwapIntervalSGI)
        {
            wine_tsx11_lock();
            ret = !pglXSwapIntervalSGI(interval);
            wine_tsx11_unlock();
        }
        else
            WARN("GLX_SGI_swap_control extension is not available\n");

        if (ret)
            swap_interval = interval;
        else
            SetLastError(ERROR_DC_NOT_FOUND);
    }

    return ret;
}
The code is pretty simple. It is clear that Wine's d3d9 implementation defines it's own "default swap interval" to be D3DPRESENT_INTERVAL_ONE in case app specify is as D3DPRESENT_INTERVAL_DEFAULT. The default value for swap_interval when using wine OpenGL is assumed to be 1 as this is what GLX_SGI_swap_control specs explicitly define.

Now, back to the problem with controling vsync using nVIDIA driver CP. "Sync to VBlank" setting at "OpenGL Settings" page doesn't explicitly mention that it would force vsync to be always on. I suspect that the only real effect it has it setting the default internal value the driver uses as swap_interval. As a result we get that Wine app that uses d3d to do rendering would always set swap_interval value basing on the app supplied D3DPRESENT_INTERVAL and/or on built-in wined3d defaults.

What should be done here by nVIDIA devteam:

a) nVIDIA should document if setting "SyncToVBlank" nvcontrol attribute to be 1 really forces swap_interval to be at least 1.

b) nVIDIA should fix __GL_SYNC_TO_VBLANK so it sets default OpenGL swap_interval to be 1 and any attempts to reset it back to 0 using GLX_EXT_swap_control extension should fail with an error (or maybe should be silently ignored). GLX_SGI_swap_control should be OK in its current form as the specs state that it is impossible to disable vsync once it had been enabled usign this extension (glXSwapIntervalSGI should do nothing and fail with an error if receives 0 as input). This behavior should be consistent no matter is the target drawable attached to the fullscreen X11 window or to an ordinary X11 window.

As a side note, Wine should be extended to use GLX_EXT_swap_control if it's available instead of much older and less capable GLX_SGI_swap_control extension. I would implement a patch and send it to Wine devteam as soon as would have a spare time for it.
Upd. Actually Wine currently relies on an assumption that calls to glXSwapIntervalSGI() are mapped to the glXSwapIntervalEXT() internally in the driver if latter advertises support for "GLX_EXT_swap_control". Thus there's no urgent need in patching Wine to directly use GLX_EXT_swap_control - it would be of a much benefit.
lexa2 is offline   Reply With Quote