Trouble with SDL 1.3 / OpenGL and VSync on Linux

I’ve created a simple test application to evaluate SDL 1.3 with OpenGL renderer and testing vertical synchronization on platforms Win32 (Windows XP) and Linux using the same computer. Graphics card is a NVIDIA Quadro FX 4600. Target is to set up a full screen video mode and show with every vertical retrace a new frame. So in my test environment using a default LCD panel with a resolution of 1600x1200 I’ve tried to switch between two images every vertical retrace.

I’ve list all available display modes using the new multi monitor functions of SDL 1.3 and selected the highest one (1600x1200x24bpp). Then I’ve created a SDL_Window with that width and height set using flags SHOWN, FULLSCREEN and OPENGL. I’ve set the display mode using SDL_SetWindowDisplayMode and created a renderer with flags PRESENTFLIP2, PRESENTVSYNC and ACCELERATED. After selecting that renderer (I’ve seen by debugging it’s an OpenGL renderer) two textures set to display size using the format of the display mode and streaming access. The first texture is filled with blue pixels using SDL_UpdateTexture with a pixel array containing only blue pixels and the other texture is filled in the same way with red pixels. Then in a loop I copied the first texture to the renderer using SDL_RenderCopy and called SDL_RenderPresent followed by copying the other texture and calling SDL_RenderPresent again.

First I’ve tried all this on windows resulting switching between both images every vertical retrace and it seems to be perfectly in sync. Now I’ve tried all this using the same source on linux but there is trouble. After both images were displayed the second image keeps on screen and it is not switched anymore and I don’t know why. So I’ve tried to play a bit with the flags given to SDL_CreateRenderer. When not using PRESENTVSYNC it works! So I’ve tried to not use PRESENTVSYNC and wanted to sync directly using glXGetVideoSyncSGI and glXWaitVideoSyncSGI functions of the OpenGL library. So it worked but it was not sync to the vertical retrace as on windows. I measured the timings using SDL_GetTicks() resulting in 3000 ticks for 180 frames on windows and 3020 to 3045 ticks on linux. When looking to the display there is also a difference to the result when testing on windows. It looks like the texture is shown while a new retrace started, looking like having frames on my panel wh
ere about 10% of the pixels on top are those blue pixels from texture1 and the other 90% of the pixels are those red pixels from texture2. I could remove this effect nearly perfect when placing an usleep after calling glXWaitVideoSyncSGI and before calling SDL_RenderPresent. On my used 60Hz mode I’ve waited about 14ms. Smaller values seems to reduce the effect but does not clear it.

Has anybody an idea what’s wrong in there? Thank you!

Here is the code:

Code:

#include <SDL.h>
#include

#ifdef _MSC_VER
#include <tchar.h>
#include <Windows.h>
#else
#define GLX_GLXEXT_PROTOTYPES 1
#include <GL/glx.h>
#include <GL/glxext.h>
#include <unistd.h>
#endif

#ifndef DWORD
#define DWORD unsigned long
#endif

#ifndef RGB
#define RGB(r, g, b) ((r << 16) | (g << 8) | b)
#endif

bool InitDisplays(std::vector<SDL_DisplayMode>& FullScreenModes)
{
int numdisplays = SDL_GetNumVideoDisplays();
int defaultdisplay = SDL_GetCurrentVideoDisplay();
bool success = true;

for (int display = 0; display < numdisplays; display++)
{
    int nummodes;
    SDL_DisplayMode modeinfo, fullscreenmode;

    SDL_SelectVideoDisplay(display);
    nummodes = SDL_GetNumDisplayModes();
    if (nummodes < 1)
    {
        success = false;
        break;
    }

    memset(&fullscreenmode, 0, sizeof(fullscreenmode));
    for (int mode = 0; mode < nummodes; mode++)
    {
        SDL_GetDisplayMode(mode, &modeinfo);

#ifndef _MSC_VER
if (modeinfo.refresh_rate == 0)
{
//Assume 60Hz when nothing is given on non-windows
modeinfo.refresh_rate = 60;
}
#endif

        if ((mode == 0) ||
            ((fullscreenmode.h * fullscreenmode.w) < (modeinfo.h * modeinfo.w)) ||
            (((fullscreenmode.h * fullscreenmode.w) == (modeinfo.h * modeinfo.w)) && (fullscreenmode.refresh_rate < modeinfo.refresh_rate))
            )
        {
            memcpy(&fullscreenmode, &modeinfo, sizeof(fullscreenmode));
        }
    }

    FullScreenModes.push_back(fullscreenmode);
}

SDL_SelectVideoDisplay(defaultdisplay);

return success;

}

#ifdef _MSC_VER
int _tmain(int argc, _TCHAR* argv[])
#else
int main(int argc, char **argv)
#endif
{
int errCode = SDL_Init(SDL_INIT_VIDEO);
if (errCode < 0)
{
printf(“SDLInit() failed.\n”);
}
else
{
std::vector<SDL_DisplayMode> modes;

    // Linux cannot detect refresh rate
    if (InitDisplays(modes))
    {
        int display = 0;
        int maxdisplay = modes.size() - 1;

        printf("Found displays:\n");
        for (std::vector<SDL_DisplayMode>::iterator mode = modes.begin(); mode != modes.end(); mode++)
        {
            display++;
            printf("#%d: %dx%dx%dbpp @ %dHz\n", display, mode->w, mode->h, SDL_BITSPERPIXEL(mode->format), mode->refresh_rate);
        }

        SDL_SelectVideoDisplay(maxdisplay);
        SDL_WindowID window = SDL_CreateWindow("SDLTest", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, modes[maxdisplay].w, modes[maxdisplay].h, SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
        SDL_SetWindowDisplayMode(window, &modes[maxdisplay]);

#ifdef GLX_GLXEXT_PROTOTYPES
// SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTDISCARD | SDL_RENDERER_ACCELERATED);
// SDL_CreateRenderer(window, -1, SDL_RENDERER_SINGLEBUFFER | SDL_RENDERER_ACCELERATED);
SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTCOPY | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
#else
SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTFLIP2 | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
#endif
SDL_SelectRenderer(window);

        SDL_Texture* text1 = SDL_CreateTexture(modes[maxdisplay].format, SDL_TEXTUREACCESS_STREAMING, modes[maxdisplay].w, modes[maxdisplay].h);
        SDL_Texture* text2 = SDL_CreateTexture(modes[maxdisplay].format, SDL_TEXTUREACCESS_STREAMING, modes[maxdisplay].w, modes[maxdisplay].h);

        int w = modes[maxdisplay].w;
        int h = modes[maxdisplay].h;
        DWORD* pixels = new DWORD[w * h];

        for (int i = 0; i < w * h; i++)
        {
            pixels[i] = RGB(0xFF, 0x00, 0x00);
        }
        SDL_UpdateTexture(text1, NULL, pixels, 4);

        for (int i = 0; i < w * h; i++)
        {
            pixels[i] = RGB(0x00, 0x00, 0xFF);
        }
        SDL_UpdateTexture(text2, NULL, pixels, 4);

        delete pixels;

        SDL_ShowCursor(SDL_DISABLE);

        bool flipper = true;
        int frames = 0;
        int maxframes = modes[maxdisplay].refresh_rate * 3;

#ifdef GLX_GLXEXT_PROTOTYPES
unsigned int retraceCount;
// Try 80% of frame interval
unsigned int delay = 800000 / modes[maxdisplay].refresh_rate;
#endif

        unsigned int dwStartTime = SDL_GetTicks();

        while (frames < maxframes)
        {
            SDL_RenderCopy(flipper ? text1 : text2, NULL, NULL);
            SDL_RenderPresent();

#ifdef GLX_GLXEXT_PROTOTYPES
// glXGetVideoSyncSGI(&retraceCount);
// glXWaitVideoSyncSGI(2, (retraceCount+1)%2, &retraceCount);
// usleep(delay);
#endif

            flipper = !flipper;
            frames++;
        }

        unsigned int dwEndTime = SDL_GetTicks() - dwStartTime;

        SDL_DestroyTexture(text1);
        SDL_DestroyTexture(text2);
        SDL_DestroyWindow(window);
        SDL_SelectVideoDisplay(0);

#ifdef GLX_GLXEXT_PROTOTYPES
printf(“ticks = %d, retrace count = %d, delay after sync = %d?s\n”, dwEndTime, retraceCount, delay);
#else
printf(“ticks = %d\r\n”, dwEndTime);
#endif
}

    SDL_Quit();
}

return 0;

}

[/code]