I'm trying to program a game in java, which basically constitutes mostly drawing a lot of image assets onto the screen.
That works great in basis, I have 70 frames per second on my main screen. The problem is that when I drag my gamewindow onto the other screen, suddenly it experiences screen tearing and severe framerate issues, only displaying 15-25 frames per second.
I've narrowed it down to 1 screen (Samsung TV) using properly configurated sun.java2d.d3d.D3DGraphicsConfig, while the other screen (laptop screen https://www.acer.com/ac/de/DE/content/series/nitro5) defaults to sun.awt.Win32GraphicsConfig.
I tried solutions from Java2D Performance Issues and FPS drops after loading images in java and changed all my images to properly configurated images with this code:
final BufferedImage image = ImageIO.read(file);
if(usedConfig != null) {
final BufferedImage compatibleImage = usedConfig.createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency());
final Graphics2D g = compatibleImage.createGraphics();
g.drawImage(image, 0, 0, null);
}
currentImages.put(pathname+file.getName(), image);
I'm adjusting my config every frame with:
GraphicsConfiguration currentConfig = null;
if(Game.jpanel != null) {
currentConfig = Game.board.getGraphicsConfiguration();
}
if(usedConfig != currentConfig) {
System.out.println("config mismatch");
if(usedConfig != null) {
System.out.println(usedConfig.getClass().getName());
}
System.out.println(currentConfig.getClass().getName());
usedConfig = currentConfig;
firstCall = true;
}
if(firstCall != true) {
return;
}
firstCall = false;
...//load and transform each image
The problem is that no matter what I do, it seems that on the slow screen nothing really helps, I'm stuck with at most 25 Frames per second.
I'm still working on this problem as you're reading this, so I will post an answer if I find one.
To simulate the problems you can try the VM option -Dsun.java2d.d3d=false with the following code:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SmallestReproducableExample {
public static int frames = 0;
public static int fps = 0;
public static void main(final String[] args) throws IOException {
final String pathname = "./image.png";
final File file = new File(pathname);
final BufferedImage image = ImageIO.read(file);
final JFrame frame = new JFrame("SmallestReproducableExample");
frame.setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE);
final TimerTask fpsCounterTask = new TimerTask() {
@Override
public void run() {
fps = frames;
frames = 0;
}
};
final Timer fpsCounter = new Timer();
final Timer paintThread = new Timer();
fpsCounter.schedule(fpsCounterTask, 1000, 1000);
final JPanel board = new JPanel() {
@Override
public void paint(final Graphics g) {
g.clearRect(-45, -45, 2041, 1041);
for(int x = -40; x <= 2000; x+=40) {
for(int y = -40; y <= 1000; y+=40) {
g.drawImage(image, x, y, 40, 40, (ImageObserver)null);
}
}
for(int x = -41; x <= 2000; x+=40) {
for(int y = -41; y <= 1000; y+=40) {
g.drawImage(image, x, y, 40, 40, (ImageObserver)null);
}
}
for(int x = -42; x <= 2000; x+=40) {
for(int y = -42; y <= 1000; y+=40) {
g.drawImage(image, x, y, 40, 40, (ImageObserver)null);
}
}
frame.setTitle("SmallestReproducableExample fps: "+fps);
final TimerTask repaintTask = new TimerTask() { //Task must be new otherwise it throws java.lang.IllegalStateException: Task already scheduled or cancelled
@Override
public void run() {
repaint();
}
};
++frames;
paintThread.schedule(repaintTask, 1);
}
};
board.setSize((1900),(1070));
board.setDoubleBuffered(true);
frame.add(board);
frame.setSize((1900),(1070));
frame.setVisible(true);
frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
}
}
you obviously need your own test image with something transparent, as I can't provide one because of licenses.