I'm going to mark this as solved since I already have a workaround. Although at the bottom of this post, I have a possible feature request. But first, here's the problem I encountered...
This is on Windows Vista with SFML-2.3.1.
There seems to be lag or "hiccups" in a smooth framerate when calling the window's pollEvent() without a Joystick/controller plugged in. I especially noticed this since I limit the framerate in my prototype game to 60fps - every second or so, there was a glitch or hiccup and by recording and printing out the frame periods I was able to verify this with most of the times being around 15 to 17 milliseconds but every so often a 30 or 40 ms period showing up.
Just through trial and error I noticed that this DOESN'T happen when I have an Xbox360 clone USB controller plugged in. Another way to "fix" the glitches was to remove the pollEvent() loop.
Anyway, after searching the forum a bit and consulting with a twitter pal, I found this thread:
http://en.sfml-dev.org/forums/index.php?topic=6079.0That's obviously an old thread, but I guess some of it still applies.
TEST PROGRAM//
// Simple example to demonstrate lag/hiccups in SFML when framerate is set to 60fps
// and window.pollEvent() is called.
//
// On my machine (Windows Vista 64bit) compiling with mingw 4.8.1 & SFML 2.3.1
// the lag or "hiccups" go away when I plug in a controller/joystick. Unplugging the
// controller again, the "hiccups" stay gone.
//
// Also commenting out the pollEvent() call "fixes" the problem. So maybe it's an
// interaction with a framerate limited to 60fps and polling for events when no
// controller is plugged in. Could just be my machine, too.
// --> Core2 Duo T6500 CPU at 2.1 GHz
// with Intel Graphics (but decent-ish "Entertainment" Notebook PC GPU)
// surely I should be able to draw one quad at 60fps without lag.
// Seems more like a bug somewhere with polling for controller Events???
//
// The hiccups can be seen in the flickering of the quad (that represents the frametime).
// This rectangle should be about 2*16 or 2*17 pixels long, where the 16 or 17 represent
// milliseconds between frames in the test program. However, the drawing is occasionally
// missed to to a lag introduced that takes the frametime up to around 30ms.
//
// Frame times can be verified by tapping the Return/Enter key to dump the last 100 frame
// times and whereas most are ~16ms, the occasional ~30ms time can be seen in the dump.
//
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
int Window_Width;
int Window_Height;
Window_Width = sf::VideoMode::getDesktopMode().width;
Window_Height = sf::VideoMode::getDesktopMode().height;
// Create a fullscreen window
sf::RenderWindow renderWindow(sf::VideoMode(Window_Width, Window_Height),"FrameRate Test", sf::Style::None);
renderWindow.setVerticalSyncEnabled(true);
renderWindow.setFramerateLimit(60);
// clock to measure time
sf::Clock clock;
sf::Int32 lastTime = 0;
sf::Int32 thisTime = 0;
sf::Int32 deltaTime;
// check updates in ms
int numChecks = 0;
int i = 0;
sf::Int32 check[100];
// prime the timer/clock
lastTime = clock.getElapsedTime().asMilliseconds();
// draw a frame time quad
sf::VertexArray ftquad(sf::Quads, 4);
ftquad[0].position = sf::Vector2f(100,50);
ftquad[1].position = sf::Vector2f(200,50);
ftquad[2].position = sf::Vector2f(200,100);
ftquad[3].position = sf::Vector2f(100,100);
ftquad[0].color = sf::Color::Red;
ftquad[1].color = sf::Color::Red;
ftquad[2].color = sf::Color::Red;
ftquad[3].color = sf::Color::Red;
bool frametimes_dumped = false;
// ********************************************************************************************************************
// Start the game loop
// ********************************************************************************************************************
while (renderWindow.isOpen())
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) {
renderWindow.close();
}
// commenting out the pollEvent() call "fixes" the problem with lag/hiccups
///*
sf::Event event;
while (renderWindow.pollEvent(event)) {
// DO NOTHING
}
//*/
// -----------------------------------------------------------------
// UPDATING
// -----------------------------------------------------------------
thisTime = clock.getElapsedTime().asMilliseconds();
deltaTime = thisTime - lastTime;
// record "frame time" from last frame to now
check[numChecks] = deltaTime;
numChecks++;
if (numChecks >= 100) numChecks = 0;
lastTime = thisTime;
// draw a rectangle with length of twice the frame time
ftquad[0].position = sf::Vector2f(100, 50);
ftquad[1].position = sf::Vector2f(100 + deltaTime*2, 50);
ftquad[2].position = sf::Vector2f(100 + deltaTime*2, 100);
ftquad[3].position = sf::Vector2f(100, 100);
// dump past 100 frame times when Return key is hit
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Return) && !frametimes_dumped) {
frametimes_dumped = true;
for (i=numChecks; i < 100; i++) // dump older recorded frame times
std::cout << "check" << std::dec << i << " : " << check[i] << "ms\n" ;
for (i=0; i < numChecks; i++) // dump most recent recorded frame times
std::cout << "check" << std::dec << i << " : " << check[i] << "ms\n" ;
}
// -----------------------------------------------------------------
// DRAWING
// -----------------------------------------------------------------
renderWindow.clear(sf::Color::Black);
renderWindow.draw(ftquad);
renderWindow.display();
}
return EXIT_SUCCESS;
}
On my PC at least (and perhaps others), if you run the program without a controller plugged in, you'll see a red bar that shows the frame period, but occasionally it'll miss a frame and will just draw black. Commenting out the pollEvent() call/loop or plugging in a Controller alleviates the problem and it draws every frame.
It's possible you won't see this on a higher powered CPU, but I'm guessing it'll show up even there if you limit your framerate to 60fps and add the sort of complexity & number of entities you're drawing in an actual game.
WORKAROUNDI hacked together a somewhat similar workaround as the one mentioned in the older forum thread, but in this case, by adding a bool member to Win32/JoystickImpl.hpp
// hacky new methods
bool isfixConnected();
void setfixConnected(bool flag);
// hacky new private member
bool m_fix_connected;
and Win32/JoystickImpl.cpp
bool JoystickImpl::isfixConnected() {return m_fix_connected;}
void JoystickImpl::setfixConnected(bool flag) {m_fix_connected = flag;}
and then changing Window/JoystickManager.cpp JoystickManager::update() to this
////////////////////////////////////////////////////////////
void JoystickManager::update()
{
for (int i = 0; i < Joystick::Count; ++i)
{
Item& item = m_joysticks[i];
if (item.state.connected)
{
// Get the current state of the joystick
item.state = item.joystick.update();
// Check if it's still connected
if (!item.state.connected)
{
item.joystick.close();
item.capabilities = JoystickCaps();
item.state = JoystickState();
item.identification = Joystick::Identification();
}
}
else
{
// Check if the joystick was connected since last update
// Hack - fix glitch when joystick is not connected
if (!item.joystick.isfixConnected()) {
// HACK - have it only check once...
item.joystick.setfixConnected(true);
if (JoystickImpl::isConnected(i))
{
if (item.joystick.open(i))
{
item.capabilities = item.joystick.getCapabilities();
item.state = item.joystick.update();
item.identification = item.joystick.getIdentification();
}
}
}
}
}
}
So it will only check once (when the game launches) whether a controller is plugged in. This fixes the "hiccups" in the frame rate.
FEATURE REQUEST?I guess it might be a useful addition to add a flag to the Window & Joystick implementations to workaround this, so that the default is to check every time JoystickManager::update() is called, but allow the game dev to set a flag after creating the window so that it only checks once when the game starts.
That is until a definite solution is found.
Just putting this out there in case someone else runs into the same problem.