-1

I've looked at a few threads related to this question, in particular here and here however The first one was asked many years ago (before java 7 even I think) and the second one is asking about a simplified case of drawing a single color which has a much better solution that was provided, namely using Graphics.fillRect.

I also did try out some of the suggestions made in the first thread, such as using a compatible image format in my case I was already using it (TYPE_INT_ARGB) and I also tried telling java to use openGL; this one had the cool result of rendering most of my application as a black window...

So here I am asking the question again in hopes of getting a better solution to this issue.

So here is the issue:

I am working on an application that needs to draw a new auto generated and constantly changing image based on user interaction. The user interaction tends to run at an abysmal 4-10fps and I narrowed the culprit down to a call to Graphics2D.drawImage this function starts out running in a reasonable time, less than 10 milliseconds but quickly, as in after the first 2-3 calls, increases to more than 250 milliseconds. The image itself is can vary in size but in my testing was consistently less than 1000x1000 pixels and is a BufferedImage using type TYPE_INT_ARGB which is the compatible type for my system. Any help on this would be greatly appreciated!

As requested here is a minimal reproducible example:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JComponent;
import javax.swing.JFrame;

public class LowFPS {
    public static final int SIZE = 700;
    public static final BufferedImage image = new BufferedImage(SIZE, SIZE, BufferedImage.TYPE_INT_RGB);

    public static void main(String args[]) {
        JFrame frame = new JFrame("Low FPS Example");
        JComponent view = new ImageComponent();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        frame.add(view, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }

    public static class ImageComponent extends JComponent {
        public ImageComponent() {
            setPreferredSize(new Dimension(SIZE, SIZE));
            setOpaque(true);
            addMouseMotionListener(
                    new MouseMotionListener() {
                        @Override
                        public void mouseMoved(MouseEvent e) {
                        }
                        @Override
                        public void mouseDragged(MouseEvent e) {
                            double start = System.nanoTime()/1e9;
                            int raster[] = new int[SIZE*SIZE];
                            int g = 0;
                            Random rand = new Random();
                            for (int i = 0; i < SIZE; i++) {
                                for (int j = 0; j < SIZE; j++) {
                                    g = rand.nextInt(156)+100;
                                    raster[SIZE*j+i] = (0xff << 24) | (g << 16) | (g << 8) | g;
                                }
                            }
                            synchronized(image) {
                                image.getRaster().setDataElements(
                                        0, 0, SIZE, SIZE, raster
                                );
                            }
                            double stop = System.nanoTime()/1e9;
                            System.out.printf("generate time: %f\n", stop-start);
                            ImageComponent.this.repaint();
                        }
                    }
            );
        }
        @Override
        public void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            synchronized(image) {
                double start = System.nanoTime()/1e9;
                g2.drawImage(image, null, 0, 0);
                double stop = System.nanoTime()/1e9;
                System.out.printf("draw time: %f\n", stop-start);
            }
        }
    }
}

and here is some output I got from running this:

draw time: 0.005215
draw time: 0.003509
generate time: 0.025820
generate time: 0.009886
draw time: 0.001034
generate time: 0.008111
generate time: 0.007449
draw time: 0.000753
generate time: 0.005950
generate time: 0.005828
draw time: 0.000670
generate time: 0.005807
generate time: 0.005737
draw time: 0.000619
generate time: 0.005666
draw time: 0.000657
generate time: 0.005559
draw time: 0.000530
generate time: 0.005521
draw time: 0.000508
generate time: 0.005455
draw time: 0.000487
generate time: 0.005410
draw time: 0.002473
generate time: 0.005392
generate time: 0.005406
draw time: 0.011764
generate time: 0.006291
generate time: 0.006608
draw time: 0.042349
generate time: 0.016228
generate time: 0.010290
draw time: 0.111547
generate time: 0.016587
generate time: 0.010646
draw time: 0.240194
generate time: 0.016043
generate time: 0.010762
draw time: 0.125226
generate time: 0.016076
generate time: 0.010350
draw time: 0.104254
generate time: 0.015372
generate time: 0.009877
draw time: 0.234022
generate time: 0.016878
generate time: 0.010765
draw time: 0.103371
generate time: 0.015868
generate time: 0.010262
draw time: 0.232200
generate time: 0.015839
generate time: 0.009695
draw time: 0.118478
generate time: 0.015144
generate time: 0.009925
draw time: 0.100979
generate time: 0.015897
generate time: 0.010610
draw time: 0.245413
generate time: 0.015212
generate time: 0.009755
draw time: 0.123311
generate time: 0.014929
generate time: 0.009887
draw time: 0.115697
generate time: 0.016013
generate time: 0.010928
draw time: 0.251448
generate time: 0.015607
generate time: 0.010624
draw time: 0.117246
generate time: 0.015960
generate time: 0.010239
draw time: 0.117721
generate time: 0.016382
generate time: 0.010506
draw time: 0.122491
generate time: 0.015746
generate time: 0.010153
draw time: 0.117414
generate time: 0.015598
generate time: 0.010362
draw time: 0.117872
generate time: 0.015859
generate time: 0.010637
draw time: 0.113869
generate time: 0.016953
generate time: 0.010780
draw time: 0.111810
generate time: 0.016389
generate time: 0.010655
draw time: 0.246486
generate time: 0.016682
generate time: 0.010737
draw time: 0.121975
generate time: 0.015779
generate time: 0.010437
draw time: 0.125625
generate time: 0.016359
generate time: 0.010636
draw time: 0.221910
generate time: 0.016156
generate time: 0.010369
draw time: 0.122495
generate time: 0.016397
generate time: 0.010612
draw time: 0.238445
generate time: 0.015114
generate time: 0.009899
draw time: 0.113739
generate time: 0.015389
generate time: 0.010127
draw time: 0.126756
generate time: 0.018503
generate time: 0.011559
draw time: 0.243606
generate time: 0.016405
generate time: 0.010426
draw time: 0.116460
generate time: 0.015355
generate time: 0.010080
draw time: 0.119474
generate time: 0.016397
generate time: 0.010536
draw time: 0.258496
generate time: 0.015853
generate time: 0.010504
draw time: 0.102392
generate time: 0.021260
generate time: 0.009640
draw time: 0.121522
generate time: 0.015351
generate time: 0.009437
draw time: 0.119224
generate time: 0.015698
generate time: 0.010410
draw time: 0.113848
generate time: 0.014471
generate time: 0.009880
draw time: 0.241783
generate time: 0.016351
generate time: 0.010552
draw time: 0.127653
generate time: 0.016064
generate time: 0.010969
draw time: 0.127003
generate time: 0.014897
generate time: 0.009697
draw time: 0.251630
generate time: 0.015527
generate time: 0.010259
draw time: 0.111767
generate time: 0.015485
generate time: 0.010329
draw time: 0.123999
generate time: 0.014251
generate time: 0.009680
draw time: 0.239466
generate time: 0.015720
generate time: 0.011239
draw time: 0.112809
generate time: 0.015376
generate time: 0.010115
draw time: 0.113489
generate time: 0.015563
generate time: 0.010294
draw time: 0.117295
generate time: 0.013725
generate time: 0.009450
draw time: 0.123859
generate time: 0.014134
generate time: 0.009614
draw time: 0.237366
generate time: 0.014752
generate time: 0.010723
draw time: 0.120068
generate time: 0.014044
draw time: 0.129768

As you can see the generation time is capable of supporting at a minimum 50fps however the time it takes to draw the image is reducing the frame rate to about 8fps.

jdodle
  • 141
  • 1
  • 3
  • 11

1 Answers1

0

Doing any extra processing on delaying on the EDT will slow things down. And synchronization is no exception. I suspect processing tasks on the EDT are getting backed up (queued for processing). I recommend you change this:

 synchronized(image) {
          image.getRaster().setDataElements(
                 0, 0, SIZE, SIZE, raster
          );
}

to this

image.getRaster().setDataElements(
       0, 0, SIZE, SIZE, raster);

And get rid of the sychronization in paintComponent too. Unless you are modifying the raster in another thread, this is unnecessary. There is only one Event Dispatch Thread All events on the EDT happen serially.

WJS
  • 36,363
  • 4
  • 24
  • 39
  • I agree the synchronization isn't really necessary here but in my application there are some things that can update the raster on separate threads. However I left it out because I was able to reproduce the results without it. The data I included shows that the `drawImage` function itself can take up to 250ms to complete, since the data processing is happening "serially" in this case that shouldn't have any affect on the runtime of `drawImage` should it? – jdodle Feb 09 '21 at 16:50
  • testing without synchronization also didn't change the results. I also tried limiting the processing to just the first 20x20 pixels and while it did, as expected, significantly reduce the "generate time" it did not affect the "draw time" at all. – jdodle Feb 09 '21 at 16:56
  • Interesting. It changed the results significantly for me. – WJS Feb 09 '21 at 17:08