2d rendering performance/ layer drawing

I’ve been at this for awhile and I know how to do most things. I had to take Java for my comp. sci. degree at Penn State where I will be a junior starting next semester. I used the code of a well known youtuber called the Cherno as a base. He handled the graphics with the BufferedImage class and pixel arrays. I tried that but with my bigger window size and bigger tiles I was getting only a few frames per second at maximum.

So I tried instead to just draw the sprites on the screen without converting them to pixels first and getting it all together in one big pixel array. That also gave terrible performance but because of drawing all the tiles - not because of all the copying of pixel arrays like for the Cherno’s method. Btw I have a hot gaming PC I’m doing all this on and I still get this really low fps issue.

I enabled java 2d in Eclipse (-Dsun.java2d.opengl=true) and I used the graphics 2D methods for drawing but it didn’t help performance. I am trying to do things this way because my understanding is that, unlike with the pixel arrays, I can make use of the graphics card or chip to speed up the performance of image drawing. So, my first question is how to get the opengl hardware acceleration involved so I can draw all this on the screen and still have a reasonable fps.

My second question is how to maintain layers when I draw to the screen and keep the screen from flickering. I understand how the pixel array method did this (the method I’d prefer to use but it lacks the potential for hardware acceleration), but I’m not sure how to do it when I am repeatedly calling the drawImage method in my rendering method. Is this what BufferStrategy is supposed to do? I don’t have flickering, I just anticipate it when I get my tiles to display properly behind the player character.

So I have two things I’m asking about, basically. How do I or can I enable hardware acceleration for drawing and do I use buffer strategy or something else to draw properly in the game loop.

[quote]So, my first question is how to get the opengl hardware acceleration involved so I can draw all this on the screen and still have a reasonable fps.
[/quote]
I can’t really help you with Java2D, what i can tell you is that you should look into VolatileImage: BufferedImage vs VolatileImage

Now i have one question for you: Do you want to continue needlessly pissing away your time with the Java2D dead-end trail or would you rather like to start learning OpenGL (=> LWJGL )?

The Java2D tutorial you’re following is from 2012, there are better ways now, even JavaFX would be better then what you are currently getting yourself into:

W3gAzLwfIP0

He uses C++ in his OpenGL series, but the OpenGL commands and constants are the same for C++ and Java (LWJGL). You might want to watch some tutorial on how to set up LWJGL first.

Or start with LibGDX and then slowly get into raw OpenGL from there.

Maybe this could help:
GLG2D, OpenGL accelerated Graphics2D

If you need some help to use JOGL directly or in a backend of another API, contact us on our official forum.

I use Java2D myself, but I will say that VaTTeRGeR and gouessej do have a point. Moving forward with new games and a new code base, it is worth considering other approaches.

I haven’t watched the videos in question. But I have done a few Java 2D games.

Your game loop could do something like the following:

  1. Update mouse position.

  2. Process and Dispatch events

  3. Update game state - that is, do one frame’s worth of game logic

  4. Do your rendering

     // Assuming that we have cached a reference to the BufferStrategy
     Graphics g = strategy.getDrawGraphics();
    
     // Here is where your game elements draw/paint/render themselves (choose your term) .
    

    offscreenDraw(g);

     g.dispose();
    
     strategy.show();
    

I don’t know about using a pixel array. If you use BufferedImage or VolatileImage objects, they’ll be hardware accelerated if they can be. Sprites can use individual BufferedImage objects. You only need one instance of a BufferedImage for each sprite that use a particular image, of course. As a pretty obvious example, cities on a map could be static sprites. Every “town” sprite uses the instance of the “town” graphic in the form of a BufferedImage to draw itself.

Depending on the size of the large image, have you considered tiling it yourself? I have found that this sometimes makes a big difference. I always thought that was probably due to bad drivers, and your new machine should not have that problem. But I just do it that way anyway. And by “tiles” I simply mean sub-images of a preset size. Maybe 256 x 256 or 512 x 512. You do have to write your own drawImage() methods in that case.

That said, there are a couple of other things that could be giving you bad performance. But you probably know about them: don’t write to accelerated images because that kicks them back out of VRAM; some 2D operations such as XOR drawing mode will kill your performance. Etc. Without seeing your code it’s hard to say.

Also, is this a exclusive mode (fullscreen) game? I don’t know if you’ll get what you want from BufferStrategy in a windowed-style game. I haven’t done a windowed game in a long time, so that is just my $0.02.

Sorry guys, I meant to reply earlier. In fact, I wrote a response but went somewhere else before hitting the post button and forgot it was there. So here’s my second attempt at responding!

First of all, thank you for laying out the options for me. I will check them out. My first intention is to try VolatileImage and see if I get a significant enough performance boost. That may be enough and then I can just move on with things.

I’ll update you guys on whether this works or not - it may take a day or two. If it works then it might benefit others who see this thread to know about it.

…or maybe JavaFX will meet your needs, as a happy medium in complexity between OpenGL and Java2D.

Cas :slight_smile:

@princec When you recommend using JavaFX, do you have a preference or recommendations as when to use the javafx.scene.canvas.Canvas (drawing images onto the canvas) versus managing images as nodes with javafx.scene.image.ImageView?

I’m also intrigued with trying out the layering capabilities of Canvas. For example, if some elements rarely change and others are more dynamic, can layering improve performance?

Depends on use case I suppose. It’s been 3-4 years since I did anything “in anger” in JavaFX though so I can’t really remember what was best.
I also, uh, ended up using LWJGL anyway for all the fast stuff and just used JavaFX to present it in a UI :slight_smile:

Cas :slight_smile:

I think I’m going to try lwjgl. A quick test with VolatileImage did give me a performance boost, but it wasn’t good enough. Still getting only 1 or 2 fps though my ups was up by about +50 to +100 of what it was before.

I still find this surprising. It is drawing tiles in the background that caps my fps so low, but the tiles are just 90 x 90 pixel colored squares at this point. I don’t know how it is my pc can run Skyrim with ease and yet it struggles to draw painted squares in my Java game.

How many tiles?
I’m surprised to hear 1 or 2 fps.

[quote]Still getting only 1 or 2 fps though my ups was up by about +50 to +100 of what it was before.
[/quote]
You’re doing something wrong, that’s crazy bad. Here’s an example i found online changed up so it uses BufferedImage and it runs at above 1200fps on my 12yo rig:

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;

public class Java2D extends Canvas implements Runnable {

	private static final long serialVersionUID = 1L;

	public static final int WIDTH = 1920;
	public static final int HEIGHT = WIDTH * 9 / 16;
	public static final String TITLE = "YOUR GAMES NAME";
	public static final int TICKSPERS = 120;
	public static final boolean ISFRAMECAPPED = false;

	public BufferedImage texture;
	public int rectWidth = 50;
	public int rectHeight = 50;

	public static JFrame frame;

	private Thread thread;
	private boolean running = false;

	public int frames;
	public int lastFrames;
	public int ticks;

	public Java2D () {
		Dimension size = new Dimension(WIDTH, HEIGHT);
		setPreferredSize(size);
		setMaximumSize(size);
		setMinimumSize(size);

		// texture = ImageIO.read(new File(""));
		texture = new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB);
		texture.setAccelerationPriority(1f);

		for (int x = 0; x < rectWidth; x ++) {
			for (int y = 0; y < rectHeight; y ++) {
				texture.setRGB(x, y, (int)(Math.random() * Integer.MAX_VALUE));
			}
		}
	}

	public void render () {
		frames++;
		BufferStrategy bs = getBufferStrategy();
		if (bs == null) {
			createBufferStrategy(2);
			return;
		}
		Graphics g = bs.getDrawGraphics();
		g.setColor(new Color(79, 194, 232));
		g.fillRect(0, 0, getWidth(), getHeight());
		// Call your render funtions from here

		for (int x = 0; x < 1920; x += rectWidth) {
			for (int y = 0; y < 1080; y += rectHeight) {
				//g.setColor(new Color((int)(Math.random() * (double)Integer.MAX_VALUE)));
				//g.fillRect(x, y, rectWidth, rectHeight);
				g.drawImage(texture, x + (int)(Math.random()*10), y + (int)(Math.random()*10), null);
			}
		}

		g.dispose();
		bs.show();
	}

	public void tick () {
	}

	public synchronized void start () {
		if (running) return;
		running = true;
		thread = new Thread(this, "Thread");
		thread.start();
	}

	public synchronized void stop () {
		if (!running) return;
		running = false;
		try {
			System.exit(1);
			frame.dispose();
			thread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public void init () {

	}

	public void run () {
		init();
		// Tick counter variable
		long lastTime = System.nanoTime();
		// Nanoseconds per Tick
		double nsPerTick = 1000000000D / TICKSPERS;
		frames = 0;
		ticks = 0;
		long fpsTimer = System.currentTimeMillis();
		double delta = 0;
		boolean shouldRender;
		while (running) {
			shouldRender = !ISFRAMECAPPED;
			long now = System.nanoTime();
			delta += (now - lastTime) / nsPerTick;
			lastTime = now;
			// if it should tick it does this
			while (delta >= 1) {
				ticks++;
				tick();
				delta -= 1;
				shouldRender = true;
			}
			if (shouldRender) {
				render();
			}
			if (fpsTimer < System.currentTimeMillis() - 1000) {
				System.out.println(ticks + " ticks, " + frames + " frames");
				ticks = 0;
				lastFrames = frames;
				frames = 0;
				fpsTimer = System.currentTimeMillis();
			}
		}
	}

	public static void main (String[] args) {
		Java2D game = new Java2D();
		frame = new JFrame(TITLE);
		frame.add(game);
		frame.pack();
		frame.setResizable(false);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
		game.start();
	}

}

Are you by any chance calling g.dispose after every draw call or something like that?

Or modifying the image every frame or even worse, at every drawing operation?

@VaTTeRGeR

I was getting between 150 and 200 fps if I got rid of the scrolling tiled background code, which at this point just displays colored tiles:


for (int i = firsttile_y - 90; i < getHeight(); i+=(90))  {
	for (int j = firsttile_x - 90; j < getWidth(); j+=(90))  {
		g2d.drawImage(getVImage(level.getTile(x, y).getSprite().getBImage()), i, j, this);
		x++;
	}
	y++;
}

I call .dispose every time before I show the bufferstrategy (bs.show in my code).

My tiles are 90 pixels by 90 pixels. firsttile_y - 90 and firsttile_x - 90 refer to the locations of the first sets of tiles at the top and left sides of the screen, even if they are partly off-screen. Level is the level; getTile(x, y) accesses a text file that contains integers representing the tile map; and you can figure out the rest of it I’m sure.

I tried adding sprite-image.setAccelerationPriority(1f); to my code (with getBImage and not getVImage) after I load the buffered image from the png file into the sprite object’s memory but it didn’t help.

EDIT: Maybe I’m copying from the sprite sheet every time instead of just once for things. I will investigate!
EDIT2: Yeah I was reading from the sprite sheet every time. Now I call that code when I generate the level (just once) so the level object has the memory loaded into it already. Getting 60 fps now and it looks good. I learned a lot from this mistake though - how I can take advantage of hardware acceleration and what else is out there for gaming in Java. Thanks guys.