Swap Buffers and Gameloop, vsync

Hello!

I cannot for the love of god make a gameloop that works well with/without V-sync enabled.

This is what I have:


		long start = System.nanoTime();
		long timeElapsed;
		float ds;
		boolean dropframe = false;
		
		while(running){
							
				timeElapsed = System.nanoTime() - start;
				if (timeElapsed <= updateTime){
					try {
						Thread.sleep((updateTime - timeElapsed)/1000000);
						continue;
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}else{
					ds = timeElapsed/1000000000f;
					start = System.nanoTime();
					if (!dropframe){
						display.clearScreen();
						current.render(ds);
						Renderer.flushLayer();
						poll(ds);
						current.update(ds);
						running = running && display.update();
						if (System.nanoTime() - start > updateTime)
							dropframe = true;
					}else{
						System.out.println("FRAME DROPPED");
						Renderer.clear();
						poll(ds);
						current.update(ds);
						dropframe = false;
					}
			}
		}



The display.update() is when glSwapBuffers is called.

This loop works fine without V-sync. I’ve timed the glSwapBuffers and it takes arround 3% of my update time when in an intense scene.

However, when I enable V-sync I get out of sync and frames drop to half, if not 1/4th.

I’ve tried doing a seperate function for when v-sync is enabled:



if (vSync){
	timeElapsed = System.nanoTime() - start;
	start = System.nanoTime();
	ds = timeElapsed/1000000000f;
	display.clearScreen();
	current.render(ds);
	Renderer.flushLayer();
	poll(ds);
	current.update(ds);
	running = running && display.update();
}


To let the vsync functionality do the waiting for me, but it is unpredictable and makes my GPU fans spin out of control.

I enable w-sync through GLFW (setSwapInterval(1)). I assume it’s an openGl operation under the hood, that’s why I’m here.

My goal is to use my first loop and have it work with/without v-sych, no matter what my screen-frequency or chosen FPS is. (FPS <= screen-frequency of course).

Anyone have any ideas?

This is very strong evidence that V-Sync on your system isn’t configured properly.

It sounds like you’re V-Sync isn’t waiting on vertical retrace in the GPU scan-out at all. It’s just immediately returning, causing you to beat your GPU silly (thus the fan speed increase due to increased GPU heat dissipation.

I enable w-sync through GLFW (setSwapInterval(1)). I assume it’s an openGl operation under the hood, that’s why I’m here.

Sounds right. However, something’s not being set up properly. Check your GPU driver control panel. Often times they provide an override that allows the driver to basically ignore what the application wants and just hard-wire V-Sync on or off. Sounds like this might be what’s going on in your case.

My goal is to use my first loop and have it work with/without v-sych, no matter what my screen-frequency or chosen FPS is. (FPS <= screen-frequency of course).

If so, you miss half the point of V-Sync. It’s not just about saving power. It’s also about preventing tearing artifacts on-screen, and presenting the user a new video frame for every frame scanned out of the GPU video output.

[QUOTE=Dark Photon;1278983]This is very strong evidence that V-Sync on your system isn’t configured properly.

It sounds like you’re V-Sync isn’t waiting on vertical retrace in the GPU scan-out at all. It’s just immediately returning, causing you to beat your GPU silly (thus the fan speed increase due to increased GPU heat dissipation.

Sounds right. However, something’s not being set up properly. Check your GPU driver control panel. Often times they provide an override that allows the driver to basically ignore what the application wants and just hard-wire V-Sync on or off. Sounds like this might be what’s going on in your case.

If so, you miss half the point of V-Sync. It’s not just about saving power. It’s also about preventing tearing artifacts on-screen, and presenting the user a new video frame for every frame scanned out of the GPU video output.[/QUOTE]

I will check!

Regarding your last statement I might have been a bit vague. I want non-tearing, absolutely, but I also want a loop that lets me set FPS that aren’t affected by my monitors refresh-rate. If I have a 60Hz monitor, I might still want to set a targeted FPS of 30. All of this while having no tearing and no synchronization problem.

While I was writing this, I think I came up with a solution. I don’t think it’s an V-sync configuration problem, I just had to fix something. I thought that if I ascertained that if I had a refresh-rate of 60hz and V-sync enabled, then I wouldn’t have synchronization problems as long as there’s at least 1/60th second in between calling glSwapBuffers. However, this is false in my case. I must make sure that there’s at least 1/60th second from when glSwapBuffers returns and when I call it again. This solved my problem for now. Will experiment a bit more.

This is what I mean:

swapBuffers()

make sure 1/60 seconds pass here.

swapBuffers()

Update: This seemed to work fine. However, it proved to be unpredictable and sometimes f***s up. Must be something with my hardware.

Never ever use sleep in a game loop. Sleep guarantees a minimum time to sleep for, nu a strict limit. See this.
Also, variable dt based game loops will always cause issues, especially with OpenGL that has a tendency of hitching now and again.
I strongly recommend you read this.

BTW, swapInterval takes the minimum number of screen updates to wait, so , for example, using setSwapInterval(2) on a 60Hz display will refresh at 30FPS.

[QUOTE=jaketehsnake;1278987]This is what I mean:

swapBuffers()

make sure 1/60 seconds pass here.

swapBuffers()

Update: This seemed to work fine. However, it proved to be unpredictable and sometimes f***s up. Must be something with my hardware.[/QUOTE]

No, this is wrong. Don’t do this. It will be unpredictable, as you’ve found out, and not just on your hardware.

Once you fix your VSync, you can do this:

render()
swapBuffers()
glFinish() // If you’re on a desktop GPU

and this will keep your frame loop synchronized with your VSync SwapInterval.

By default SwapInternal = 1, so for instance at 60Hz VSync, your frame time will be 16.66ms per frame. But if you set it to numbers > 1, you can synchronize with with multiples of that frame rate. For example, 2 would be 60/2 = 30Hz which is 16.66*2 = 33.33ms per frame.

Indeed, there must be something wrong with my drivers… I get anti-tear, but erratic frameRates and big heat production.

Thanks to all your advice I’ve improved my gameloop and it gives me the behaviour I was looking for. I can manually set a framerate while my updating and rendering is smooth, regardless of V-sync.

I didn’t skip the sleep thing, but might do so in the future. As of now I think it’s a nice trade-off - sparing the CPU with the slight possibility of a spike or lag. But time will tell.

This is my result:


		long nanoMax = 1000000000/32;
		long nanoMin = 1000000000/128;
		float secondsMax = 1f/32f;
		float secondsMin = 1f/128f;

		float secondsRender;
		
		long maxAmount;
		long minAmount;
		
		long start = System.nanoTime();
		long nanoAccumilator = 0;
		
		long nanoTimeElapsed;
		
		while(running){
			
			nanoTimeElapsed = System.nanoTime() - start;
			
			nanoAccumilator += nanoTimeElapsed;
			
			maxAmount = nanoAccumilator/nanoMax;
			nanoAccumilator -= maxAmount*nanoMax;
			minAmount = nanoAccumilator/nanoMin;
			nanoAccumilator -= minAmount*nanoMin;
			
			secondsRender = secondsMax*maxAmount + minAmount*secondsMin;
			
			start = System.nanoTime();
			
			while (maxAmount > 0){
				maxAmount -= 1;
				poll(secondsMax);
				current.update(secondsMax);
			}

			if (minAmount > 0){
				poll(secondsMin*minAmount);
				current.update(secondsMin*minAmount);
			}
				
			display.clearScreen();
			current.render(secondsRender);
			Renderer.flushLayer();
			running = running && display.update();
			
			sleep(System.nanoTime() - start);
			
		}