GLFWSwapInterval(0) giving inconsistent behavior. GLFW 3.2.1, Windows 10, NVidia


#1

I’m trying to implement a solution where I render video to two windows. (Ultimately, these would be operating on different displays but for now they are on the same display). But I’m having issues controlling the frame rate. When trying to draw and swapbuffers to both windows, the frame rate sticks at about 60-65 frames per second. In an effort to debug this, I commented out the code related to rendering to my 2nd window. I still create both windows, but now I only render to my primary window. I start by exploring the maximum frame rate that I can achieve by implementing:

glfwSwapInterval(0);

for both window contexts.

Then in the rendering loop, I implement the code:

glfwMakeContextCurrent(window);
drawScene(0); /* These are my custom draw commands */
glfwSwapBuffers(window);
glfwPollEvents();

This is where the strange behavior occurs. I’ll compile my code and run it and it will render video at about 215 frames per second. I’ll then stop the code. Then, without changing anything, re-run the code and then it starts to run at 60 to 65 frames per second. It’s almost as if the first time I run the newly compiled code something gets set on my system that forces the code to lock to the vsync for any subsequent runs.

I tried to go into my Nvidia control panel and specifically turn off ‘Vertical Sync’ but that didn’t help.

I’ve been reading the discussion boards about setting the GLFW_USE_DWM_SWAP_INTERVAL parameter - but it seems that that is related to an older version of GLFW (I’m using version 3.2.1)

Any idea on what’s going on here?


#2

GLFW_USE_DWM_SWAP_INTERVAL is no longer needed, this issue was fixed.

Have you checked any of the examples, for instance try boing.c with glfwSwapInterval(0)? You’ll need to use an external framerate profiler like Fraps or add your own.


#3

Thanks for the reply. boing.c uses Glad. Is there any chance you could point me toward a sample that uses GLEW? (I forgot to mention that I am also using that library…)


#4

Using GLEW or GLAD shouldn’t change the result since GLFW itself doesn’t use them, just the sample.

You should be able to compile from the source using cmake and test from the glfw github source, as the glad requirements are included.


#5

@dougbinks Thanks for the tips. I found boing in my GLFW build and confirmed with FRAPS that the glfwSwapInterval(0); functionality is working. I then went back to my original app and started to benchmark with FRAPS and I’m seeing a strange result:

With glfwSwapInterval(0); set, the FRAPS benchmark is indicating frame rates on the order of 1000 FPS. However, the video is clearly playing back at about 60 FPS (and my internal frame rate calculation is confirming that speed). (The other confounding thing is that the frame rate of the primary monitor in my system (on which the two windows are rendering) is currently operating with a frame rate of 50 Hz). Why would FRAPS report a frame rate that is clearly not reflective of what I’m seeing on screen? Does FRAPS only measure the rendering time to the back buffer or something like that?


#6

The frame rate you render at with glfwSwapInterval(0) is unrelated to your monitors refresh rate because it will not wait for the vertical blank to swap buffers. This will result in you not seeing all of the frames rendered, and you will likely see tearing.

I don’t have enough information on your application to know why your internal frame rate counter does not measure the same as FRAPS, but you could easily output the times at which you complete glfwSwapBuffers(window) to verify.

As an extra note I’d advocate thinking in terms of frame times in ms rather than frame rates - this is the approach graphics developers take as it makes it easier to compose total frame times from times for rendering parts of the frame.


#7

I’ve actually done more work on this and I now have a relatively simple example that I hope will help clarify this discussion. My sample code is as follows:

	#include <GLFW/glfw3.h>
	#include <stdlib.h>
	#include <stdio.h>

	#define INCLUDE_SWAPINTERVAL_LINE 0
	bool lock_to_vsync = false;

	static void error_callback(int error, const char* description)
	{
		fputs(description, stderr);
	}
	static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
	{
		if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
			glfwSetWindowShouldClose(window, GL_TRUE);

		if (key == GLFW_KEY_V && action == GLFW_PRESS)
		{
			lock_to_vsync = !lock_to_vsync;
		}
	}


	int main(void)
	{
		GLFWwindow* window;
		if (!glfwInit())
			exit(EXIT_FAILURE);
		window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);
		if (!window)
		{
			glfwTerminate();
			exit(EXIT_FAILURE);
		}
		glfwSetWindowPos(window, -1000, 1000);

		glfwSetErrorCallback(error_callback);
		glfwSetKeyCallback(window, key_callback);
		while (!glfwWindowShouldClose(window))
		{
			glfwMakeContextCurrent(window);
			#if INCLUDE_SWAPINTERVAL_LINE
				glfwSwapInterval(lock_to_vsync);
			#endif	
			float ratio;
			int width, height;
			glfwGetFramebufferSize(window, &width, &height);
			ratio = width / (float) height;
			glViewport(0, 0, width, height);
			glClear(GL_COLOR_BUFFER_BIT);
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			glOrtho(-ratio, ratio, -1.f, 1.f, 1.f, -1.f);
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			glRotatef((float) glfwGetTime() * 50.f, 0.f, 0.f, 1.f);
			glBegin(GL_TRIANGLES);
			glColor3f(1.f, 0.f, 0.f);
			glVertex3f(-0.6f, -0.4f, 0.f);
			glColor3f(0.f, 1.f, 0.f);
			glVertex3f(0.6f, -0.4f, 0.f);
			glColor3f(0.f, 0.f, 1.f);
			glVertex3f(0.f, 0.6f, 0.f);
			glEnd();
			glfwSwapBuffers(window);
			glfwPollEvents();
		}
		glfwDestroyWindow(window);
		glfwTerminate();
		exit(EXIT_SUCCESS);
	}

I’m running on Windows 10 with an Nvidia GTX 1080 GPU. I have two monitors: A main monitor with a refresh rate of 50 Hz and a secondary monitor with a refresh rate of 60 Hz. I’m using Fraps to diagnose the frame rate of my rendered window. My goal is to create a ‘primary window’ on a faster, secondary monitor and render to it at that monitor’s refresh rate. Here’s what I’m running into:

'1. I would think, that if I wanted to achieve my objective, I would, in the above code, include the glfwSwapInterval(lock_to_vsync) line (by setting the #define to 1) while also setting lock_to_vsync = true;
Result: If I do this, my primary window (even though it is located squarely in my secondary monitor) updates at 50 Hz (i.e. the refresh rate of my main monitor).

'1a. And, btw, if I toggle the lock_to_vsync boolean by hitting the ‘v’ key, the frame rate will jump to some ridiculously high value (like 5900 frames a second).

'2. However, if I completely eliminate the glfwSwapInterval line from the compilation (by setting the INCLUDE_SWAPINTERVAL_LINE #define to 0), the primary window does what I expect/want it to do: If I locate the primary window on the secondary monitor, it updates at 60 Hz and if I move it back to the main monitor it updates at 50 Hz.

So the success of case #2 argues for not using the glfwSwapInterval() command at all when the primary window’s context is ‘current’. This gives me the desired behavior (if I am only rendering to one window (see my post-script for what to do w/ two windows)). I think this is the ‘solution’ <= it’s just a little (actually more than a little) disconcerting that by using the glfwSwapInterval(1); command it locks only onto the vsync of the main monitor and not the vsync of the monitor on which it is located. Is there any command that allows you to control which vsync a particular rendered window locks to?


#8

And, as a post-script, I can now also create a second ‘auxiliary’ window that I will be displaying on the main (slower) monitor. It turns out that when that auxiliary window’s context is current, I have to specifically set glfwSwapInterval(0); when swapping that window’s buffer so that it’s placement on the slower monitor does not throttle the primary window. <= I include this post-script in case anyone else is trying to achieve a similar two-window rendering solution…


#9

This isn’t an issue with GLFW. It’s an issue with your OpenGL driver and Windows Desktop Window Manager.

Check your results in a fullscreen mode, where you should find glfwSwapInterval works as expected unless the GPU driver setting has a forced vsync setting.

Note that:

  1. glfwSwapInterval takes an int. bool to int promotion is 0 or 1 so you’re fine with your code here.
  2. You only need to call glfwSwapInterval once to set the option.
  3. You also only need to call glfwMakeContextCurrent once.

#10

FYI from the GLFW docs for glfwSwapInterval:

[…] some swap interval extensions used by GLFW do not allow the swap interval to be reset to zero once it has been set to a non-zero value.
Some GPU drivers do not honor the requested swap interval, either because of a user setting that overrides the application’s request or due to bugs in the driver.

It looks as though it works on your PC, but be warned that you cannot reliably toggle vsync on and off as in your example code.


#11

@tombsar, thanks for the observations. I only included that lock_to_vsync boolean as a way to simply demonstrate the disparity that I was seeing. I will keep your comment in mind…


#12

@doubbinks, thanks for youre reply. I’ve worked some more on this and it does seem that if I render to a full screen window that the commands are a lot more responsive. In full screen mode, I can change the frame rate as needed.

It turns out that I’m now having a number of other, apparently unrelated, problems related to this full screen mode, but I’ll ask a separate question related to those issues.

Thanks again