2

I'm using Swing and JavaFX to render images to the screen, but getting unexpected timings: the aim is simply to render 1,000,000 images at random positions on a component. Why is JavaFX taking so long?

Results: Swing : 2.5 secs. JavaFX 8.5 secs. Code below.

In JavaFX.

public class JFXTest extends Application
{
public static void main(String[] args)
{
    launch(args);
}

@Override
public void start(Stage theStage)
{
    Group root = new Group();
    Scene theScene = new Scene( root );
    theStage.setScene( theScene );

    Canvas canvas = new Canvas( 1000, 1000);
    root.getChildren().add( canvas );

    GraphicsContext gc = canvas.getGraphicsContext2D();

    new ResourceLoaderJFX();
    System.out.println("Running test");

    Random ran = new Random();

    ClassLoader classLoader = getClass().getClassLoader();
    URL url = classLoader.getResource("sky.png");
    Image image = new Image(url.toString());
    long t1 = System.nanoTime();
    for (int j=0; j<1000000; j++ ) {
        int x = ran.nextInt(1000);
        int y = ran.nextInt(1000);
        gc.drawImage(image, x, y);
    }
    System.out.println("\n");
    long t2 = System.nanoTime()-t1;
    System.out.println("Took " + (t2/1000000000.0) + " secs");
    System.out.println("Done");

    theStage.show();
}
}

Prism pipeline init order: d3d sw 
Using native-based Pisces rasterizer
Using dirty region optimizations
Not using texture mask for primitives
Not forcing power of 2 sizes for textures
Using hardware CLAMP_TO_ZERO mode
Opting in for HiDPI pixel scaling
Prism pipeline name = com.sun.prism.d3d.D3DPipeline
Loading D3D native library ...
D3DPipelineManager: Created D3D9Ex device
    succeeded.
Direct3D initialization succeeded
(X) Got class = class com.sun.prism.d3d.D3DPipeline
Initialized prism pipeline: com.sun.prism.d3d.D3DPipeline
OS Information:
Maximum supported texture size: 8192
    Windows version 10.0 build 14393
Maximum texture size clamped to 4096
D3D Driver Information:
    Intel(R) Iris(TM) Graphics 540
    \\.\DISPLAY2
    Driver igdumdim64.dll, version 20.19.15.4463
    Pixel Shader version 3.0
    Device : ven_8086, dev_1926, subsys_00151414
    Max Multisamples supported: 4
 vsync: true vpipe: true
Running test

Took 8.230974466 secs

In Swing:

public class SwingTest extends JPanel {

public void init() {
    setVisible(true);
}

public void runTest() {
    System.out.println("Running test");
    BufferedImage bufferedImage=null;
    try {
        bufferedImage = ImageIO.read(new File("C:\\Users\\resources\\png\\sky.png"));
    } catch (IOException e) {
        e.printStackTrace();
    }
    long t1 = System.nanoTime();
    Random ran = new Random();
    for (int j=0; j<(1000000); j++ ) {
        int x = ran.nextInt(1000);
        int y = ran.nextInt(1000);
        this.getGraphics().drawImage(bufferedImage, x, y, null);
    }
    long t2 = System.nanoTime()-t1;
    System.out.println("Took " + (t2/1000000000.0) + " secs");
}

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            JFrame f = new JFrame();
            SwingTest view= new SwingTest();
            view.init();
            f.add(worldViewPanel);
            f.pack();
            f.setSize(new Dimension(1000,1000));
            f.setVisible(true);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            view.runTest();
        }
    });
}
}

Connected to the target VM, address: '127.0.0.1:53764', transport: 'socket'

Took 2.586923483 secs

Interestingly, for lower numbers

JAVAFX Took 0.02173174 secs @ 10,000 images, second run took 0.018200605 secs

SWING Took 0.138639497 secs @ 10,000 images, second run took 0.13744251 secs

Michael IV
  • 11,016
  • 12
  • 92
  • 223
wax_lyrical
  • 922
  • 1
  • 10
  • 22
  • In your code JavaFX renders 1000K images, but Swing renders only 10K images. – Sergiy Medvynskyy Mar 24 '17 at 10:24
  • 2
    And please provide a [SSCCE](http://sscce.org) for both examples, so we can understand whether your test are correct. – Sergiy Medvynskyy Mar 24 '17 at 10:33
  • Thanks for pointing out the typo: I've edited code down for readability. I've checked numbers in both examples and results hold true. – wax_lyrical Mar 24 '17 at 10:34
  • 1
    Please provide the complete test code (in form of SSCCE), so we can understand whether your tests are correct – Sergiy Medvynskyy Mar 24 '17 at 10:34
  • JavaFX: You seem to be timing `ResourceLoaderJFX`; focus on its methods when you [profile](http://stackoverflow.com/q/2064427/230513). Swing: Instead of `getGraphics()`, override `paintComponent()`. – trashgod Mar 24 '17 at 11:01
  • I've edited to remove this. It was just a bufferedimage loader. See new edit please. In the original example, I was overriding paintComponent. Timings were same. – wax_lyrical Mar 24 '17 at 11:02
  • I get the opposite result: JavaFX is over twice as fast as Swing. – trashgod Mar 24 '17 at 12:31

2 Answers2

3

I think what you are experiencing is the difference between the retained mode of JavaFX and the immediate mode of Swing. Swing is literally taking those images and blitting them to the screen then moving on to the next blitting position. When it needs to draw them again, it starts from scratch. As it happens, this is very fast.

JavaFX is creating a distinct Object every time you invoke drawImage (see GraphicsContext.writeImage() ) and then retaining those Objects in an internal buffer it grabs from Canvas. On top of that it's creating six doubles and putting them into exactly the same buffer (see GraphicsContext.updateTransform() ).

The sell of JavaFX is its retained mode. It will allow you to manipulate its Nodes on screen as if they were in a 2-D (in fact 3-D) coordinate system and it will do this "for free". This is very powerful if you want to position objects in a 2-D scene and move them around, as game programmers are well aware.

The price you pay for this is the scene is much heavier than the corresponding scene in Swing and the memory cost of Images is cumulative in the JavaFX application. In your JavaFX app, you have a Scene to which you're adding a Canvas and it's creating a scene graph. Swing is not doing this.

If you run your program in a profiler, you can see exactly where the time is being spent and if you run your program in a debugger you can see how large the Canvas buffer is becoming.

0

You are comparing apples with beens here. In Swing the image is actually rendered when you call drawImage. In JavaFX this command to draw an image is just added to a command buffer which will be executed later.

mipa
  • 10,369
  • 2
  • 16
  • 35