Search code examples
c++sfmlmesavsync

SFML vsync always on?


I have been playing around with game loops and physics. The other day, I added some debug statements to see how much time each frame of my game loop was taking. As expected, the results were in the 16ms range. However, I tried disabling vsync, and these results did not change. Clearly vsync was still occuring. I commented out the SFML display call, and sure enough the frames sped up.

Okay, so why then is vsync stuck on? At first I thought it must be a bug in DSFML (SFML bindings for the D language). I created a simple test case in C++ which uses SFML directly, and the performance characteristics are exactly the same!

My system is as follows:

$ inxi -SMG
System: Host: c7 Kernel: 3.16.4-1-ARCH x86_64 (64 bit) Desktop: i3 4.8 Distro: Arch Linux Machine: System: Google product: Parrot v: 1.0
Mobo: N/A model: N/A Bios: coreboot v: 4.0-4744-gac16405-dirty date: 10/23/2013
Graphics: Card: Intel 2nd Generation Core Processor Family Integrated Graphics Controller
Display Server: X.Org 1.16.1 driver: intel Resolution: [email protected]
GLX Renderer: Mesa DRI Intel Sandybridge Mobile GLX Version: 3.0 Mesa 10.3.1

The SFML vsync test case is given below, with vsync turned on:

#include <chrono>
#include <iostream>
#include "SFML/Graphics.hpp"

int main()
{
    auto* window = new sf::RenderWindow(sf::VideoMode(640, 480), "test",
        sf::Style::Titlebar | sf::Style::Close);
    window->setVerticalSyncEnabled(true);
    auto firstTime = std::chrono::high_resolution_clock::now();
    while(window->isOpen())
    {
        //print frame timing
        {
            auto secondTime = std::chrono::high_resolution_clock::now();
            using dMsecs = std::chrono::duration<double, std::chrono::milliseconds::period>;
            auto elapsed = dMsecs(secondTime - firstTime);
            firstTime = secondTime;
            std::cout << elapsed.count() << '\n';
        }
        //event handler
        {
            sf::Event e;
            while(window->pollEvent(e))
            {
                if(e.type == sf::Event::EventType::Closed)
                    window->close();
            }
        }
        //render
        {
            window->clear();
            window->display();
        }
    }
}

Googling this issue comes up with results suggesting that the graphics driver is forcing vsync on. But then I wondered why vsync works for other programs on my system?

I wrote another test case, this time using SDL2:

#include <chrono>
#include <iostream>
#include "SDL2/SDL.h"

int main()
{
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
    auto firstTime = std::chrono::high_resolution_clock::now();
    auto quit = false;
    while(!quit)
    {
        //print frame timing
        {
            auto secondTime = std::chrono::high_resolution_clock::now();
            using dMsecs = std::chrono::duration<double, std::chrono::milliseconds::period>;
            auto elapsed = dMsecs(secondTime - firstTime);
            firstTime = secondTime;
            std::cout << elapsed.count() << '\n';
        }
        //event handler
        {
            SDL_Event e;
            while(SDL_PollEvent(&e))
            {
                if(e.type == SDL_QUIT) quit = true;
            }
        }
        //render
        {
            SDL_RenderClear(renderer);
            SDL_RenderPresent(renderer);
        }
    }
}

Now I disable vsync is this test case, and see frame times in the 0ms range, as expected! So then the way SFML implements vsync is buggy on my system, whereas SDL seems to handle it properly.

What is the difference in implementation between the two libraries causing this differing behaviour, and can it be resolved? How would I go about getting the correct behaviour with SFML?


Solution

  • It's a buggy driver. glXSwapIntervalMESA works. The pointer returned for glXSwapIntervalSGI is valid, so SFML can't detect this issue without taking an approach similiar to SDL's.