0

What i'm trying to create is high resolution metaballs. Here is my algorithm:

public constructor(){
        this.image = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_ARGB);
        this.rgbRaster = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
        this.width = getWidth();
        this.height = getHeight();
}

private void updateRaster() {
        int red = c.getRed(), green = c.getGreen(), blue = c.getBlue();

        for (int y = 0; y < height; y++)
            for (int x = 0; x < width; x++) {
                var sum = 0f;
                for (Blob b : blobs)
                    sum += getSum(x, y, b);

                sum = Math.min(sum, 255) / 255f;

                rgbRaster[(width * y) + x] = ((0xFF) << 24) | (((int) (sum * red) & 0xFF) << 16) |
                        (((int) (sum * green) & 0xFF) << 8) | (((int) (sum * blue)) & 0xFF);
            }
    }
private float getSum(int x, int y, Blob b) {
        var d = Point.distance(x, y, b.pos.x, b.pos.y);
        return (float) map(1000f * b.r / d, 0, this.maxDist, 0, 255);
    }

private double map(double n, double start1, double stop1, double start2, double stop2) {
        return (n - start1) / (stop1 - start1) * (stop2 - start2) + start2;
    }

Blob class is pretty simple it's just a ball that bounces off the screen:

public class Blob {
    public Vector2D pos;
    public Vector2D vel;
    public float r;

    public Blob(Vector2D pos, Vector2D vel, float r) {
        this.pos = pos;
        this.vel = vel;
        this.r = r;
    }

    public void update(float w, float h) {
        this.pos.add(vel);
        this.bounds(w, h);
    }

    public void draw(Graphics2D g) {
        g.fill(new Ellipse2D.Double(this.pos.x - r, this.pos.y - r, r * 2, r * 2));
    }

    private void bounds(float w, float h) {
        if (this.pos.x + r > w || this.pos.x - r < 0) this.vel.mult(new Vector2D(-1, 1));
        if (this.pos.y + r > h || this.pos.y - r < 0) this.vel.mult(new Vector2D(1, -1));
    }
}

The problem is that there are too many iterations in nasted loops and it causes major freezes. I cannot figure out other way of cheking distanse from every single pixel of the screen to the center point of all blobs, so with high resolution picture and lots of blobs it makes it a slideshow. Also I know that converting everything into a BufferedImage is not a perfect idea but i don't know other ways of converting raster into a graphics object. Thanks in advance for your help) (Edit: getSum() finds a sum of all distanses to all blobs, maps it to color range, and limits it to 255 this function is certainly not an issue but a little bit complicated to understand that's why i didn't include it)(Edit 2: Thanks to @ThatOtherGuy now this way more optimized but probably there a some other mistakes)

David
  • 125
  • 8
  • What is `getSum`? Please edit your question to include a [mcve]. – kaya3 Oct 07 '20 at 23:43
  • 1
    There's a lot of detail missing, but a good start would be to A. only set each pixel once, instead of once for each blob, B. reuse the same float[] instead of allocating hundreds of MB per frame, C. Using `getRaster` instead of `getData` to avoid copying the entire screen multiple times per frame. – that other guy Oct 07 '20 at 23:44
  • @thatotherguy thanks a lot it really helped. Did i get you right about B. here is edt `var color = new float[3]; color[0] = sum / 255f * c.getRed(); color[1] = sum / 255f * c.getGreen(); color[2] = sum / 255f * c.getBlue(); `? – David Oct 08 '20 at 00:01
  • `var color = new float[3];` should be next to `var w = getHeight();` so you only do it once per frame. – that other guy Oct 08 '20 at 00:03
  • @thatotherguy Yep, got you, thank's a lot. Do you have some more comments?)) – David Oct 08 '20 at 00:11
  • 1
    You shouldn't need `img.setData(raster);` anymore since you're updating it directly. You're still calculating `var maxDist = Math.sqrt(w * w + h * h);` millions of times on every frame even though it'll always give the same result. I'm guessing the JIT is unable to cache the value of `c.getRed()` and friends so you could store them in local variables. There's plenty more you could be doing, but with increasingly diminishing returns. – that other guy Oct 08 '20 at 00:29
  • OK, @thatotherguy it seems like A. and B. helped most of all. But there's still giant loss in fps so is it a problem in java and is there are globally better ways of doing such things? (e.g K-D trees instead nested loop for checking collisions) – David Oct 09 '20 at 07:30
  • 1
    Stop doing `this.createImage(w, h)` on every frame, and instead reuse the same one (until the window changes size). Calculate int pixels yourself and [write them directly into the backing array](https://stackoverflow.com/a/29280741/1899640). If after that you want to squeeze more raw performance out, get rid of all the unnecessary indirections by replacing the `ArrayList` with an `int[]` (and avoid `this.anything` in loops), get rid of the floating point math/conversion and integer division. I have no doubt you can speed it up significantly still, and that's before multithreading – that other guy Oct 09 '20 at 18:11
  • I've done what was possible, I don't really understand how can I replace `ArrayList` with an `int[]` but creating an image outside the update function gave me about 5-6 fps. Also I can't get rid of floating point math because it makes a wrong image – David Oct 10 '20 at 21:28

0 Answers0