Lag when scrolling backgrounds/moving

Started by Izman, October 16, 2012, 03:06:00

Previous topic - Next topic

Izman

Hey all, this is my first post on the LWJGL forums.

So a few months ago I started a little 2D platforming project with a couple friends.  I've taken a course in Java and I have a small background with C++, but generally I'm a "beginner"

Here's my setup so far:

I'm using SlickUtil + LWJGL 2.8.4, drawing a 800x600 window with a 1920x1080 background and equivalently long floor and ceiling sprites with a couple platforms.  When the player moves past a certain point in the centre of the screen, movement is taken from the player and instead I move the 1920x1080 background (of course, there are specific cases when the player reaches the end of the map).

However, occasionally I'm noticing lag while moving the background.  When the player starts to trigger the background-moving logic, the screen starts to stutter (70% of the time).  Even when the screen isn't moving, occasionally the player starts to lag (~10% of the time). This is all while setting the Display.sync(60).

However, I figured out a solution: setting Display.sync() to really large values (~700) makes the game run completely smooth.  Now, obviously having a 700-fps requirement for a 2D game is nothing short of ridiculous, which brings me here.

Am I going about scrolling completely wrong?  Is this a problem with my movement algorithms? Display.sync()? Sprite data type?  Some direction would be nice before I start ripping my code apart :)

kappa

It could be a number of thing, ranging from simple screen tearing to garbage collection, try  Display.setVSyncEnabled(boolean) to enable vertical synchronization instead of Display.sync().

Izman

Thanks for the reply :)

I tried disabling Display.sync() and put Display.setVSyncEnabled() in my initializer, but it made the game dramatically choppy (both Display.sync() and VSync() capped the frames at 60) :(

I was also looking up a lot on Garbage collection, and even used the JVM arguments to change the garbage collection method, but it changed nothing.  AFAIK, I shouldn't be creating much garbage aside from maybe loading the areas, and I explicitly call System.gc() after.

This is an insert of my Sprite class:

public Sprite(String filename, float x, float y)
	{	
		try
		{
			texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("game/" + filename)); 
																														    
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		
		this.x = x;
		this.y = y;
		flip = false;

                textureX = (float)texture.getImageWidth()/texture.getTextureWidth();
		textureY = (float)texture.getImageHeight()/texture.getTextureHeight();
	}
	public void draw() 
	{
		GL11.glPushMatrix();
		texture.bind();
		
		if(flip)
		{
			GL11.glTranslatef(x, y, 0);
			GL11.glRotatef(180,0,1,0);
		}
		else
		{
			GL11.glTranslatef(x, y, 0);
		}
		
		GL11.glBegin(GL11.GL_QUADS);
		{	
			GL11.glTexCoord2f(0,0);
			GL11.glVertex2f(0,0);
			GL11.glTexCoord2f(textureX,0);
			GL11.glVertex2f(texture.getImageWidth(),0);
			GL11.glTexCoord2f(textureX,textureY);
			GL11.glVertex2f(texture.getImageWidth(),texture.getImageHeight());
			GL11.glTexCoord2f(0,textureY);
			GL11.glVertex2f(0,texture.getImageHeight());
		}
		GL11.glEnd();
		
		GL11.glPopMatrix();
	}


This is the correct method for drawing 2D sprites, right?

Izman

Okay so with a bit of playing around:

Exporting the game as a runnable Jar and running it eliminated 90% of the lag.

There still exists a bit of lag, but it's muuucchh better.

As well,  I set up my project on a Mac OSX Lion machine and, with using Java 6 (Apple) and Vertical Sync, there is absolutely 0 lag.

Here are the 2 machines:

Mac Mini:  OSX Mountain Lion, Intel Core 2 Duo 2.2GHz, NVIDIA 9400M, 4GB 1033(?)MHz DDR3:  Vertical Sync on, Java 6 Update 37
PC Tower:  Windows 7 Home Premium 64 bit, AMD Phenom II x4 955, AMD HD 6850, 8GB 1333MHz DDR3:  Display.sync(60), Java 7 Update 7

* Note: I tried JRE 6 Update 35 & Vertical Sync on the PC machine

Any ideas why my less powerful Mac runs this flawlessly?

EDIT:  Summary of my tests:

Windows 7:

Java 6 or 7 is pretty much the same
Vertical Sync makes the game look laggy as hell
Display.sync() to my monitors refresh rate has small lags, but is way better than vertical sync. Increasing value to insanely large values fixes all lag.

Mac:

Java 6
Display.sync() to monitors refresh rate has screen tearing
Vertical Sync yields flawless gameplay

Both are on exported jars, rendering 8 sprites in Immediate mode, 4 of which are the map (800x600, 2000x600, 2000x150, 2000x150), A player (40x80), and 3 platforms (150x50, 150x50, 150x50).

broumbroum

hi izman,
Do you print on the system.out while running the executable ?
There may be sync issue if multiple threads print out to the jvm. however if, e.g. with windows, you run with the JavaW.exe , then the jvm does not log the system.out console, so it should explain why it's much faster.

Izman

Quote from: broumbroum on November 01, 2012, 12:38:37
hi izman,
Do you print on the system.out while running the executable ?
There may be sync issue if multiple threads print out to the jvm. however if, e.g. with windows, you run with the JavaW.exe , then the jvm does not log the system.out console, so it should explain why it's much faster.


Hi broum,

Aside from Slick-Util creating some output at the beginning of the program, I don't output anything.

If it makes a difference, I'm compiling in Eclipse on a version I downloaded a couple months ago.

Izman

Yet another update...

All Windows machines run perfect in Full Screen Mode + Vertical sync (same performance as MacOSX Windowed/Full Screen Mode Vertical Sync).

However, in windowed mode, the Windows machines run like crap (vertical sync) and less crap (Display.sync(125)) and perfect (Display.sync(999)).

Here's a summary of my tests:

Machine 1: Windows 7 Home Premium, Intel i7 3.4GHz, AMD HD Radeon 6870, 8GB RAM
Machine 2: Windows 7 Home Premium, AMD Phenom II x4 3.2GHz, AMD HD Radeon 6850, 8GB RAM
Machine 3: Mac OSX Lion, Intel Duo Core 2.2GHz, NVIDIA 9400M, 4GB RAM
Laptop 1: Windows 7 Home Premium, Intel Processor, AMD Card (Friend's Laptop, mid-low range)

Windowed Mode & Vertical Sync:  M1 and M2 lag, M3 and L1 look playable/perfect
Windowed Mode & 125 FPS Cap: M1, M2, L1 look playable (some lag), M3 Screen tearing

Fullscreen Mode & Vertical Sync: M1, M2, M3, L1 perfect
Fullscreen Mode & 125 FPS Cap: M1, M3 Screen Tearing, M2 & L1 untested

Something to note is that high-end cards perform horribly with Vertical Sync windowed mode.  Most notably, they are from the 6800 series.
Mid-low range cards seem to handle it fine.

Could this be because Immediate Mode is not supported in newer versions of OpenGL?

broumbroum

This may be related to buffer unsync'ed updates. Theres an option in the catalyst driver windows panel : right click on the tray icon in windows task bar and select your video card, opengl , triple buffering. Set it on, if necessary.

Izman

Quote from: broumbroum on November 08, 2012, 07:37:00
This may be related to buffer unsync'ed updates. Theres an option in the catalyst driver windows panel : right click on the tray icon in windows task bar and select your video card, opengl , triple buffering. Set it on, if necessary.


Thanks for the suggestion :)

Sadly, Triple Buffering has no effect on the issue.

Something to note is that the lag feel "jerky", like all the updating is happening in bursts (imagine a delta going from 16/17 to 40 every second), but delta is staying uniform...

broumbroum

Ok, I experience the same issue in my test project while I use a secondary thread to load stuff. It has no sync with the rendering gl thread, which causes this pause-then-burst effect on screen. Whenever the loading task finishes everything is fluent.
We should have a look at your rendering loop.

Izman

Quote from: broumbroum on November 08, 2012, 17:28:41
Ok, I experience the same issue in my test project while I use a secondary thread to load stuff. It has no sync with the rendering gl thread, which causes this pause-then-burst effect on screen. Whenever the loading task finishes everything is fluent.
We should have a look at your rendering loop.

Sure thing,

        private void gameLoop()
	{
		while(gameState != 0 && !Display.isCloseRequested())
		{
			GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
			GL11.glMatrixMode(GL11.GL_MODELVIEW);
			GL11.glLoadIdentity();
			
			delta = getTime() - lastLoopTime;
			lastLoopTime = getTime();
			lastFpsTime += delta;
			fps++;
			
			if(lastFpsTime >= 1000)
			{
				Display.setTitle(WINDOW_TITLE + " (FPS: " + fps + ")");
				lastFpsTime = 0;
				fps = 0;
			}
			
			applyControls();		//Take input from keyboard
			SoundStore.get().poll(0); 	//Sound storage
			
			if(gameState == 3)				//gameState is equal to 3 when the game is out of the main menu
			{
				capDelta();			//if Delta is over 18 (aka, sub 60FPS), set to 17
				moveEntities();			//Move the entities
				logic();				//Displace entities based on collision and logic
			}
			
			render();					//Draw entities
			
			Display.update();
			Display.sync(125);
		}
		Display.destroy();
		AL.destroy();
		System.exit(0);
	}
	private void render()
	{
		if(gameState == 1)
		{
			menu.draw();
		}
		else if(gameState == 3)
		{
			map.draw();
			player[0].draw();
			player[1].draw();
		}
	}


The actual draw methods are inefficient (take a look at my Sprite class above to see), but for ~6 textures it shouldn't make a huge deal.

broumbroum

What the applyControis() looks like? Does it poll last events ?

Izman

Quote from: broumbroum on November 08, 2012, 17:41:58
What the applyControis() looks like? Does it poll last events ?

It just grabs input from the keyboard (pretty much straight out of the example on the LWJGL Wiki)

        private void applyControls()
	{
		if(gameState == 1)
		{
			applyMenuControls();
		}
		else if(gameState == 3)
		{
			applyGameControls();
		}
	}
        while (Keyboard.next())
		{
			if(Keyboard.getEventKeyState()) //KEY PRESSED
			{
				//PLAYER 1	
				if (Keyboard.getEventKey() == control[0][0])
				{
					player[0].setMove(1);
				}
				if (Keyboard.getEventKey() == control[1][0])
				{
					player[0].setMove(2);
				}
				if (Keyboard.getEventKey() == control[2][0])
				{
					player[0].setMove(3);
				}
				if (Keyboard.getEventKey() == control[3][0])
				{
					player[0].setMove(4);
				}
				//PLAYER 2	
				if (Keyboard.getEventKey() == control[0][1])
				{
					player[1].setMove(1);
				}
				if (Keyboard.getEventKey() == control[1][1])
				{
					player[1].setMove(2);
				}
				if (Keyboard.getEventKey() == control[2][1])
				{
					player[1].setMove(3);
				}
				if (Keyboard.getEventKey() == control[3][1])
				{
					player[1].setMove(4);
				}
                                ....


the .setMove(int) method flags a boolean in Player, which then moves the entities in the gameloop.

broumbroum

Hi
By watching at your code, it turns out that the window status is updated every frame by Display.setTitle() which might update and wait for the Window ToolKit sync. Try to not access any of the awt member functions within the gameloop thread.
As you mentioned, applyControls() does flag boolean's'which is the _correct manner.

Izman

Quote from: broumbroum on November 09, 2012, 07:48:22
Hi
By watching at your code, it turns out that the window status is updated every frame by Display.setTitle() which might update and wait for the Window ToolKit sync. Try to not access any of the awt member functions within the gameloop thread.
As you mentioned, applyControls() does flag boolean's'which is the _correct manner.

Okay, thanks a ton :)

The only awt class I'm importing (and using) is

import java.awt.Rectangle;


and I make calls every gameloop to
.intersects(Rectangle)
I guess it's worth a try to disable it.