Copying an image onto another with JOCL/OpenCL

so my goal is to use the GPU for my brand new Java project which is to create a game and the game engine itself (I think it is a very good way to learn in deep how it works).

I was using multi-threading on the CPU with java.awt.Graphics2D to display my game, but i have observed on other PCs that the game was running below 40FPS so i have decided to learn how to use GPU (I will be still rendering all objects in a for loop then draw the image on screen).

For that reason, I started to code following the OpenCL documentation and the JOCL samples a small simple test which is to paint the texture onto the background image (let’s amdit that every entities has a texture).

This method is called in each render call and it is given the background, the texture, and the position of this entity as arguments.

This is the link of the stackoverflow post with sources code that I have made and which I will update : java - copying an image onto another with JOCL/OpenCL - Stack Overflow

The goal and context are not yet entirely clear for me.

If you have some „entities“, and each of them has a BufferedImage that you’d like to paint (maybe on top of a „background“ image), then the fastest way of doing this is graphics.drawImage. This will be hardware-accelerated, and faster than everything that one could write „manually“.

(There are some degrees of freedom, and some „tricks“ that can improve the performance in some cases, but that that depends on the general setup+goals)

Referring to the code that you posted on stack overflow: You’re calling getDataBuffer there. Note that this single call can make painting the image much slower. I posted an example showing that, at https://gamedev.stackexchange.com/a/75667/46384 (under „Implementation Details“, the ImagePaintingTestButNoBenchmark class).

Beyond that: There currently are not many samples that involve images, but JOCLSamples/JOCLSimpleImage.java at master · gpu/JOCLSamples · GitHub shows at least some basic image handling.

Depending on your goal, you could

  • provide your original project, without OpenCL, and maybe it’s possible to figure out why it is „slow“
  • provide the current project, with OpenCL, and maybe one can figure out what is wrong

But in any case: If your goal is just to efficiently paint images, then OpenCL will hardly help you. (You could do some trickery with OpenGL and replacing the images with real textures, and this could in turn be much faster than Java2D/Graphics-based painting, but with much more effort as well…)

I have a version of my code with awt.drawImage but it caps around 45 fps for ‚normal‘ cpu but I have like 100 textures so I thought of few optimisations that could be performed to accelerate the rendering but i really think gpu is the solution. I might change as well the way all my entities are represented and stored but for the moment, I focus on making my code with jocl working. Thx for the advices.

There are some aspects that may influence the performance. But I just hacked together this test:

package bytewelt;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;


public class FastImagePainting
{
    public static void main(String[] args)
    {
        List<BufferedImage> images = new ArrayList<BufferedImage>();
        
        Random random = new Random(0);
        int n = 500;
        int w = 500;
        int h = 500;
        for (int i=0; i<n; i++)
        {
            BufferedImage image = new BufferedImage(
                w, h, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g = image.createGraphics();
            int cr = random.nextInt(255);
            int cg = random.nextInt(255);
            int cb = random.nextInt(255);
            Color color = new Color(cr, cg, cb);
            System.out.println(color);
            g.setColor(color);
            g.fillRect(0, 0, w, h);
            g.dispose();
            images.add(image);
        }
        JPanel panel = new JPanel()
        {
            private int counter = 0;
            private long prevNs = -1;
            
            @Override
            protected void paintComponent(Graphics g)
            {
                super.paintComponent(g);
                for (int i=0; i<images.size(); i++)
                {
                    int x = random.nextInt(Math.max(1, getWidth() - w));
                    int y = random.nextInt(Math.max(1, getHeight() - h));
                    g.drawImage(images.get(i), x, y, null);
                }
                counter++;
                long ns = System.nanoTime();
                if (prevNs == -1)
                {
                    prevNs = ns;
                }
                else
                {
                    double s = (ns - prevNs) / 1e9;
                    if (s > 1.0)
                    {
                        System.out.println("FPS: "+counter);
                        counter = 0;
                        prevNs = ns;
                    }
                }
            }
        };
        Thread t = new Thread(() -> 
        {
            // DON'T DO THIS IN A REAL APPLICATION!
            while (true)
            {
                panel.repaint();
            }
        });
        t.setDaemon(true);
        t.start();
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(panel);
        f.setSize(1200, 800);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

It paints 500 images at random positions, in each frame, and uses a brutal while(true) loop to repaint as fast as possible, and it’s running at ~500 FPS for me. So whatever the limiting factor for your 45FPS is: It’s unlikely to be the image painting…