If someone is still interested, here is my full code for a dynamic vsync demo.
should be cross-platform, uses lib GLFW, compiled on Dev-C++
//========================================================================
// === playing with dynamic vsync ===
//
// keys :
// v - sets vsync on
// b - (default) sets vsync in dynamic mode :
// when frame rate goes (a bit) above DISPLAY_REFRESH_RATE, vsync ON to eliminate tearing
// when frame rate goes (a bit) under, vsync OFF to get more FPS
// n - sets vsync off
//
// try to follow the moving white bar with your eyes, I am not responsible for epilepsy, seizures, etc
//
// edit DISPLAY_REFRESH_RATE so that it matches your desktop refresh rate
// then edit OVERDRAW so that, with vsync off, the FPS can go down to around 45 and up to 200+
//
// author ZbuffeR
// from a nice code snippet published by Mikkel Gjoel,
// from what I though was a good idea
// Original discussion here :
// http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=196712#Post196712
// ************************************************************************/
#include <stdio.h>
#include <GL/glfw.h>
/////////////////////////////////////////////
// display native refresh rate
const double DISPLAY_REFRESH_RATE = 60.0;
////////////////////////////////////////////
// max number of black fullwindow quads
// this value (30) is good for 1280*1024 with a geforce 6800LE
// feel free to increase :-)
const int OVERDRAW = 30;
const int VSYNC_OFF = 0;
const int VSYNC_ON = 1;
const int VSYNC_DYNAMIC = 2;
//========================================================================
// main()
//========================================================================
int main( void )
{
int width, height, running, vsync;
double t, t0, fps,offset;
char titlestr[ 200 ];
long int frames =0;
double tlast;
int vsync_mode = VSYNC_DYNAMIC;
char* vsync_mode_string = "VSYNC_DYNAMIC";
// Initialise GLFW
glfwInit();
// Open OpenGL window
if( !glfwOpenWindow( 1280, 1024, 0,0,0,0, 0,0, GLFW_WINDOW ) )
{
glfwTerminate();
return 0;
}
// Enable sticky keys
glfwEnable( GLFW_STICKY_KEYS );
// vertical sync (on cards that support it)
glfwSwapInterval( 1 );
vsync = 1;
glDisable( GL_TEXTURE_2D );
glEnable( GL_BLEND);
// Main loop
running = GL_TRUE;
frames = 0;
t0 = glfwGetTime();
tlast=glfwGetTime();
// glfwSetMousePosCallback( (GLFWmouseposfun)(checkmouse) );
while( running )
{
// Get time and mouse position
t = glfwGetTime();
// Calculate and display FPS (frames per second)
if( (t-t0) > 0.2 || frames == 0 )
{
fps = (double)frames / (t-t0);
sprintf( titlestr, "[keys: v,b,n] mode:%s (%4.1f FPS)", vsync_mode_string, fps );
glfwSetWindowTitle( titlestr );
t0 = t;
frames = 0;
}
frames ++;
if (glfwGetKey( 'V')) {
vsync =1;
vsync_mode = VSYNC_ON;
vsync_mode_string = "VSYNC_ON";
glfwSwapInterval( 1);
vsync=1;
}
if (glfwGetKey( 'B')) {
vsync_mode = VSYNC_DYNAMIC;
vsync_mode_string = "VSYNC_DYNAMIC";
}
if (glfwGetKey( 'N')) {
vsync_mode = VSYNC_OFF;
vsync_mode_string = "VSYNC_OFF";
glfwSwapInterval( 0);
vsync=0;
}
if (vsync_mode == VSYNC_DYNAMIC) {
if( t-tlast>1.0/(DISPLAY_REFRESH_RATE*0.60)) {
//Mikkel Gjoel: if( t-tlast>0.024) {
//too slow
if(vsync) {
vsync=0;
glfwSwapInterval( 0);
}
} else if( t-tlast<1.0/DISPLAY_REFRESH_RATE*1.15){
//Mikkel Gjoel: } else if( t-tlast<0.012){
//too fast
if(!vsync) {
vsync=1;
glfwSwapInterval( 1 );
}
}
}
tlast=t;
// Get window size (may be different than the requested size)
glfwGetWindowSize( &width, &height );
height = height > 0 ? height : 1;
// Set viewport
glViewport( 0, 0, width, height );
// Clear color buffer
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f);
glClear( GL_COLOR_BUFFER_BIT );
// Select and setup the projection matrix
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
// Select and setup the modelview matrix
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
//make it slow...
int i = 0;
glColor3f( vsync/8.0f, (1-vsync)/8.0f, 0.0f);
// from 1 to n fullscreen black quads
for (i=0;i<=OVERDRAW*(1+sin(t));i++) {
glBegin( GL_QUADS );
glVertex2f( -1, -1.0f );
glVertex2f( 1, -1.0f);
glVertex2f( 1, 1.0f);
glVertex2f( -1, 1.0f);
glEnd();
}
glColor3f( 1.0f, 1.0f, 1.0f);
// the fast moving white quad
offset = sin(6*t);
glBegin( GL_QUADS );
glVertex2f( offset, -1.0f );
glVertex2f( offset+0.5, -1.0f);
glVertex2f( offset+0.5, 1.0f);
glVertex2f( offset, 1.0f);
glEnd();
// Check if the ESC key was pressed or the window was closed
running = !glfwGetKey( GLFW_KEY_ESC ) &&
glfwGetWindowParam( GLFW_OPENED );
glfwSwapBuffers();
}
// Close OpenGL window and terminate GLFW
glfwTerminate();
return 0;
}
I added the subtle color change in the background to track whether the automatic mode switch was messy or not (redish means vsync active, greenish means vsync inactive).
… and you are right, it seems quite messy
Taking further apart the thresholds seem to cure the mess, but the reaction becomes too slow, so it’s not ideal anyway.
Putting the low threshold to 0.5 * DISPLAY_REFRESH_RATE and high treshold to DISPLAY_REFRESH_RATE is not too bad, but it does not prevent the “framerate halving when just below refresh rate” of classic vsync, so it is not very interesting.
I will settle for a 200Hz display