问题
Situation
I created a particle system with JavaFX using the following technique:
Each particle is an ImageView which contains an Image with a radial gradient:
The particle handling loop is an AnimationTimer in which the list of particles is handled via the list's stream().parallel() method, it actually gives the whole system a boost. Something like this:
loop = new AnimationTimer() {
@Override
public void handle(long now) {
addParticle();
// apply force: gravity
allParticles.stream().parallel().forEach(Particle::applyForceGravity);
// move particle
allParticles.stream().parallel().forEach(Particle::move);
// update position in fx scene
allParticles.forEach(Particle::display);
// remove all particles that aren't visible anymore
removeDeadParticles();
}
};
The particle's color changes via ColorAdjust during its lifecycle. I used fire colors for testing, something like this which contains 1700 particles:
What I've learned:
- not using parallel() is slower
- using Circle with transparency is way slower than using an ImageView
- using a Blend Mode is very slow
- using a PixelWriter to change the image color is unbearable slow. Question arises: how does ColorAdjust change the color (via d3d & hardware)? I haven't found the mechanism in the JavaFX source code.
Question
Is there a better way to implement a particle system in JavaFX (other Node types, Thread, etc) in regards to performance?
I can post some code if anyone wants to toy around.
Thank you very much for the expertise!
Edit: Since it was asked, you can get the full code which uses nodes as particles with coloradjust from this gist. Btw, even if you pre-render the images and don't use coloradjust, the performance is low.
However, the question is more of a theoretical kind, so digging through the code isn't really necessary.
回答1:
I think I can add an answer to my own question. But I hope someone else with more experience can share their knowledge, because what I came up with was just the result of toying around:
Painting on a Canvas instead of using JavaFX ImageView nodes surprisingly resulted in a factor of at least 10x speed increase. This is basically what I did:
- pre-calculate the images with the gradients, i. e. the color and the size of them depending on the particle's lifespan
- in the animation timer get the pre-calculated image and paint it on the canvas
If anyone is interested, you can get the full code from this gist. Just click "Download Zip" and put the code of the zip in an "application" package of a JavaFX project and start the Main class. The difference to the code in the question is in the Particle.java class. In the question the particles are used as nodes and moved in the animation timer, but in this answer only the data is used for drawing an image on a canvas in the animation timer, the node itself isn't put on the scene.
You can use the Settings class in order to specify resolution, number of new particles per frame, etc.
This screenshot shows 3 repellers and 25400 particles on the screen in Full-HD resolution, running at 60fps:
回答2:
I think it might also help to combine these two lines:
// apply force: gravity
allParticles.stream().parallel().forEach(Particle::applyForceGravity);
// move particle
allParticles.stream().parallel().forEach(Particle::move);
kind of like this:
allParticles.stream().parallel().forEach(particle -> {
particle.applyForceGravity();
particle.move();
});
This way you don't get twice the overhead.
来源:https://stackoverflow.com/questions/31613669/performance-for-particle-system