LWJGL - Clearing one layer question

Hi again!

So I was continuing on my game made in LWJGL, and I am able to move a player around on a grass background. The only problem I am having is being able to update the player correctly by getting rid of the older player positions.
If i call:

GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

then the grass background disappears and the player updates fine. I can’t redraw the grass background every time I update, because the FPS will drop down to about 1 - 3. If there is a way that I can just clear the layer that the player is on, then I would greatly appreciate it.

Thanks,
Longarmx

Then you’re doing something very wrong in drawing the grass. You should be able to draw everything visible in every frame and still sustain high framerates. Maybe if you pasted the relevant rendering code?

Sorry, I should have given a bit more detail. I am drawing the grass tiles in a nested for loop, and I can’t run that 60 times per second.

Longarmx

You should be able to render thousands (if not tens of thousands) of small (e.g. 32x32) tiles easily to the screen every frame.

A) How many tiles are you rendering per frame?
B) How are you drawing the grass tiles?

Show us the code and we can help.

Some ways of speeding up rendering:

  • Batch your texture binds. If you are binding a new texture each tile, then you’re going to run into performance problems. To minimize texture binds, merge various tiles (grass, wood, dirt, water, etc) into the same “sprite sheet” or texture atlas.
  • If you are using immediate mode (glBegin/glEnd), you can speed up tile rendering by calling glBegin only once before your nested loop, and glEnd only once after your loop (assuming all tiles use the same texture and you aren’t doing any weird glTranslate/glRotate/etc. in between)
  • Learn to write your own vertex array renderer to further speed up sprite rendernig
  • If the tiles cover the entire screen, then you don’t need to call glClear (assuming you are rendering all tiles every frame). This will speed things up slightly.

Indeed, OP is doing something very wrong. Just take a look at what’s possible.

I bet that OP is drawing tiles using glBegin and glEnd. This is insanely slow since your CPU can’t feed data fast enough to your GPU. You really need to draw tiles in batches instead of one by one. Since you can’t switch textures while drawing tiles you need to have your tiles in a tile map, e.g. a single texture containing all tiles and then pick out tiles using texture coordinates. The easiest way of doing this is with display lists. They’re very easy to use. You simply wrap your glBegin/glEnd code in a display list, and then you can draw everything in the display list with a single draw call later without the CPU overhead of glBegin, glEnd, glVertex, e.t.c. Simply doing this should give you perfectly fine performance

I’m not so sure about not calling glClear each frame though. glClear isn’t simply a fullscreen quad with a certain color; it’s first of all much much faster and secondly might improve performance in other ways. With some pixel modes (mostly multisampled modes) the color/depth/stencil/e.t.c buffers are often compressed to reduce memory usage and more importantly memory bandwidth which can lead to a small performance win. When glClear is called, it resets the compression blocks which is both very and improves performance for subsequent rendering. On a much more advanced level, glClear also signals that frame data will not be reused in multi-GPU setups, which allows the driver to remove the copy between GPUs, but this is of course completely irrelevant in in this case. The point is that the performance cost of glClear is ridiculously low and literally zero if the game is CPU bound, which is OPs case and the case for pretty much every 2D game out there, while there are potential benefits of it. It’s better to just make a habit of clearing the screen each frame.

[quote]The point is that the performance cost of glClear is ridiculously low and literally zero if the game is CPU bound
[/quote]
On my system there is a pretty significant difference. Drawing a single textured quad fit to the display size:
FPS w/o glClear ~ 1000
FPS w/ glClear ~ 770

A more practical example – in my game (which is drawing hundreds of sprites per frame) it looks more like this:
FPS w/o glClear ~ 650
FPS w/ glClear ~ 490

I suppose the difference isn’t huge enough (at this point) to warrant getting rid of glClear, but since I’m not using multi-sampling or depth/stencil buffers I don’t see much of a benefit.

On my system there is a pretty significant difference. Drawing a single textured quad fit to the display size:
FPS w/o glClear ~ 1000
FPS w/ glClear ~ 770

A more practical example – in my game (which is drawing hundreds of sprites per frame) it looks more like this:
FPS w/o glClear ~ 650
FPS w/ glClear ~ 490

I suppose the difference isn’t huge enough (at this point) to warrant getting rid of glClear, but since I’m not using multi-sampling or depth/stencil buffers I don’t see much of a benefit.
[/quote]
You can’t afford 0.3 ms? =S

The difference becomes more pronounced at larger resolutions. At 1920x1080:

[quote]w/o glClear average FPS 99, 12.658228 ms
w/ glClear average FPS 79, 10.10101 ms
[/quote]
Like I said – I don’t see a benefit if I’m not targeting multi-GPU machines or embedded systems.

yep pls always use milliseconds instead of FPS to compare performance results d:

Like I said – I don’t see a benefit if I’m not targeting multi-GPU machines or embedded systems.
[/quote]
Whatever. Do what you want. I’ve said all I’ve wanted to say. =S

I’m fairly sure the OP loads the grass image from disk every frame (or every quad). Nothing else will kill your framerate that quickly.

I guess that depends on how much grass he’s drawing, but now that you mention it it seems very possible.

That’d be my guess too. And that’s why we ask for source. :stuck_out_tongue:

Ok, here is the source. But before you jump all over me for bad coding, let me just tell you that this is before taking any of your advice.

package com.src.raingame;

import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_QUADS;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glEnd;

import java.io.IOException;

import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

public class Grid{
	
	private float grassWidth;
	private float grassHeight;
	private float gTexWidth, gTexHeight;

	static Texture grass;
	static Texture player;
	
	
       // * * * * * *
       // All of this is called during initialization
       // * * * * * *

	public Grid(){
		
		// Clearing the screen and the depth
				glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
				
				try{
				grassWidth = grass.getImageWidth();
				grassHeight = grass.getImageHeight();
				}catch(NullPointerException e){
					System.err.println("Getting grass image size failed. Shutting Down...");
					System.exit(1);
				}
				
				
				try{
				gTexWidth = grass.getWidth();
				gTexHeight = grass.getHeight();
				}catch(NullPointerException e){
					System.err.println("Getting grass texture size failed. Shutting Down...");
					System.exit(1);
				}
				
				grass.bind();

				// Drawing the background (grass)
				glBegin(GL_QUADS); 
				
                                // So should I just make it into one image and then draw it every time in the main loop?

				for (int i = 0; i < Display.getWidth() / grassWidth; i++) {
					for (int b = 0; b < Display.getHeight() / grassHeight; b++) {
						
						GL11.glTexCoord2f(0, 0);
						GL11.glVertex2f(i * grassWidth,b * grassHeight);
						GL11.glTexCoord2f(gTexWidth, 0);
						GL11.glVertex2f(i * grassWidth + grassWidth, b* grassHeight);
						GL11.glTexCoord2f(gTexWidth, gTexHeight);
						GL11.glVertex2f(i * grassWidth + grassWidth,b * grassHeight + grassHeight);
						GL11.glTexCoord2f(0, gTexHeight);
						GL11.glVertex2f(i * grassWidth,b * grassHeight + grassHeight);

					}

				}
				glEnd();
			
	}
	
	public static void loadGrass(){
		try {
			grass = TextureLoader.getTexture("PNG",
					ResourceLoader.getResourceAsStream("res/grass.png"));
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println("Grass file not found, Shutting down...");
			System.exit(1);
		}
		
		
	}
}

What I want is some way not to clear the loop drawing the grass, and still being able to clear the player images and then draw it in a new position.
Some command that would be nice is: glClear(oldPlayerDrawing);
And then draw the new player.

Thanks,
Longarmx

You don’t do it like that with OpenGL, you clear your entire screen and re-draw it from scratch. Unless you do something horrifically wrong, this will still be incredibly quick.

Where do you call loadGrass() from? Possibly you could show your whole game loop and what it calls?

Well, I tried rendering the grass every frame and this time it worked without a big fps drop. I don’t know why it worked this time but I’m happy. Perhaps I forgot to remove another piece of code when I was testing it before. This is the code I’ve ended up with:

In the Main Class:

package com.src.raingame;

import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.GL_QUADS;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glEnd;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glOrtho;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.opengl.Texture;

public class Boot {

	long lastFrame;
	int fps;
	long lastFPS;
	private int playerX, playerY;
	private int playerSpeed;
	
	Player p;
	Grid g;
	
	Texture grass;
	Texture player;

	public static void main(String[] args) {
		new Boot();
	}

	public Boot() {

		boolean running = false;
		
		p = new Player();

		// Creating a new display
		try {
			Display.setDisplayMode(new DisplayMode(800, 600));
			Display.create();
		} catch (LWJGLException e) {
			e.printStackTrace();
		}

		// All the initializations
		Grid.loadGrass();
		p.loadPlayer();
		playerSpeed = 100;
		playerX = p.getPlayerX();
		playerY = p.getPlayerY();
		initGL();
		init();
		grass = g.getGrass();
		player = p.getPlayer();
		running = true;

		// Main Loop
		while (running) {
			if (Display.isCloseRequested()) {
				running = false;
			}

			 render();

			updateFPS();

			Display.update();
			Display.sync(60);
		}

		// Getting rid of the display
		Display.destroy();
		System.exit(0);
	}


	// Returns the time
	public long getTime() {
		return System.nanoTime() / 1000000;
	}

	// Calculates the delta and then returns it
	public int getDelta() {
		long time = getTime();
		int delta = (int) (time - lastFrame);
		lastFrame = time;
		return delta;
	}

	// Updating the FPS once per second and setting the title
	public void updateFPS() {
		if (getTime() - lastFPS > 1000) {
			Display.setTitle("Some name pre alpha :: FPS: " + fps);
			fps = 0;
			lastFPS += 1000;
		}
		fps++;
	}

	public void init() {
		
		getDelta();
		lastFPS = getTime();
		
		g = new Grid();
		
		p.drawPlayer();
		
	}

	// Initializing the openGL
	public void initGL() {

		GL11.glEnable(GL11.GL_TEXTURE_2D);

		GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

		// Enabling alpha blending
		GL11.glEnable(GL11.GL_BLEND);
		GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

		GL11.glViewport(0, 0, Display.getWidth(), Display.getHeight());

		glMatrixMode(GL_MODELVIEW);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, Display.getWidth(), Display.getHeight(), 0, 1, -1);
		glMatrixMode(GL_MODELVIEW);

	}

	public void render() {
		glClear(GL_DEPTH_BUFFER_BIT);
		
		grass.bind();
		glBegin(GL_QUADS);
		g.drawGrid();
		glEnd();
		
		checkInput();
		
		player.bind();
		p.updatePlayer();
	}
	
	public void checkInput(){
		
		playerX = p.getPlayerX();
		playerY = p.getPlayerY();
		
		if(Keyboard.next()){
			if(Keyboard.isKeyDown(Keyboard.KEY_A)){
				p.setPlayerX(playerX - playerSpeed);
				System.out.println("A pressed.");
			}
			if(Keyboard.isKeyDown(Keyboard.KEY_W)){
				p.setPlayerY(playerY - playerSpeed);
				System.out.println("W pressed.");
			}
			if(Keyboard.isKeyDown(Keyboard.KEY_S)){
				p.setPlayerY(playerY + playerSpeed);
				System.out.println("S pressed.");
			}
			if(Keyboard.isKeyDown(Keyboard.KEY_D)){
				p.setPlayerX(playerX + playerSpeed);
				System.out.println("D pressed.");
			}
			if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)){
				System.out.println("Escape key pressed. Shutting Down...");
				System.exit(0);
			}
		}
	}

}

In the Grid Class:

package com.src.raingame;

import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.glClear;

import java.io.IOException;

import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

public class Grid{
	
	private float grassWidth;
	private float grassHeight;
	private float gTexWidth, gTexHeight;

	static Texture grass;
	static Texture player;
	
	
	public Grid(){
		
		// Clearing the screen and the depth
				glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
				
				try{
				grassWidth = grass.getImageWidth();
				grassHeight = grass.getImageHeight();
				}catch(NullPointerException e){
					System.err.println("Getting grass image size failed. Shutting Down...");
					System.exit(1);
				}
				
				
				try{
				gTexWidth = grass.getWidth();
				gTexHeight = grass.getHeight();
				}catch(NullPointerException e){
					System.err.println("Getting grass texture size failed. Shutting Down...");
					System.exit(1);
				}
				
				grass.bind();

	}
	
	public static void loadGrass(){
		try {
			grass = TextureLoader.getTexture("PNG",
					ResourceLoader.getResourceAsStream("res/grass.png"));
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println("Grass file not found, Shutting down...");
			System.exit(1);
		}
		
		
	}
	
	public void drawGrid(){
		for (int i = 0; i < Display.getWidth() / grassWidth; i++) {
			for (int b = 0; b < Display.getHeight() / grassHeight; b++) {
				
				GL11.glTexCoord2f(0, 0);
				GL11.glVertex2f(i * grassWidth,b * grassHeight);
				GL11.glTexCoord2f(gTexWidth, 0);
				GL11.glVertex2f(i * grassWidth + grassWidth, b* grassHeight);
				GL11.glTexCoord2f(gTexWidth, gTexHeight);
				GL11.glVertex2f(i * grassWidth + grassWidth,b * grassHeight + grassHeight);
				GL11.glTexCoord2f(0, gTexHeight);
				GL11.glVertex2f(i * grassWidth,b * grassHeight + grassHeight);

			}

		}
	}
	
	public Texture getGrass(){
		return grass;
	}
}

And in the Player Class:

package com.src.raingame;

import static org.lwjgl.opengl.GL11.GL_QUADS;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glEnd;

import java.io.IOException;

import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

public class Player {
	
	Texture player;
	
	private int playerX;
	private int playerY;
	private float playerWidth, playerHeight, pTexWidth, pTexHeight;
	
	public Player(){
		playerX = 25;
		playerY = 0;
	}
	
	public void loadPlayer(){
		try {
			player = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/player.png"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void drawPlayer(){
		playerWidth = player.getImageWidth();
		playerHeight = player.getImageHeight();
		
		pTexWidth = player.getWidth();
		pTexHeight = player.getHeight();
		
		player.bind();
		
	}
	
	// Updating player position
	
	public void updatePlayer(){
		glBegin(GL_QUADS);
		
		GL11.glTexCoord2f(0, 0);
		GL11.glVertex2f(playerX,playerY);
		GL11.glTexCoord2f(pTexWidth, 0);
		GL11.glVertex2f(playerX + playerWidth, playerY);
		GL11.glTexCoord2f(pTexWidth, pTexHeight);
		GL11.glVertex2f(playerX + playerWidth, playerY + playerHeight);
		GL11.glTexCoord2f(0, pTexHeight);
		GL11.glVertex2f( playerX, playerY + playerHeight);
		
		glEnd();
	}
	
	// Player position
	
	public int getPlayerX() {
		return playerX;
	}

	public void setPlayerX(int playerX) {
		if(playerX <= Display.getWidth() && playerX >= 0){
		this.playerX = playerX;
		}
	}

	public int getPlayerY() {
		return playerY;
	}

	public void setPlayerY(int playerY) {
		if(playerY <= Display.getHeight() - playerHeight && playerY >= 0){
			this.playerY = playerY;
			}
	}
	
	// Player image sizes and texture sizes

	public float getPlayerWidth() {
		return playerWidth;
	}

	public float getPlayerHeight() {
		return playerHeight;
	}

	public float getpTexWidth() {
		return pTexWidth;
	}

	public float getpTexHeight() {
		return pTexHeight;
	}
	
	public Texture getPlayer(){
		return player;
	}

}

I hope I did everything as efficiently as possible. Also, sorry if this is too much code.

Thanks for everything you guys,
Longarmx