There is a program that generates hundreds or thousands of moving particles onscreen at once. After several hundred are displayed, slowdown occurs. Performance was analyzed using netbeans profiler and about 80% of the time was spend in the jpanel's paint method...so algorithm optimization seems unlikely to make a noticeable difference. Is there anything that can be done about this or is it time to think about a new platform? The paint method looks something like this:
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.WHITE);
for (int i = 0; i < gameLogic.getParticleArrSize(); i++) {
g.setColor(gameLogic.getParticleColor(i));
g.fillOval(gameLogic.getParticleXCoor(i),
gameLogic.getParticleYCoor(i),
gameLogic.getParticleSize(i),
gameLogic.getParticleSize(i));
}
g.setColor(gameLogic.getCurrPartColor());
g.fillOval(mouseX - mouseOvalRadius, mouseY - mouseOvalRadius,
mouseOvalSize, mouseOvalSize);
g.setColor(gameLogic.getCursorColor());
g.fillOval(mouseX - 19, mouseY - 19, 38, 38);
for (int i = 0; i < gameLogic.getBombArrSize(); i++) {
g.setColor(Color.RED);
g.fillOval(gameLogic.getBombXCoor(i) - 6,
gameLogic.getBombYCoor(i) - 6,
gameLogic.getBombSize(i),
gameLogic.getBombSize(i));
}
for (int i = 0; i < gameLogic.getPowerUpParticleArrSize(); i++) {
g.setColor(gameLogic.getPowerUpParticleColor(i));
g.fillOval(gameLogic.getPowerUpParticleXCoor(i),
gameLogic.getPowerUpParticleYCoor(i),
gameLogic.getPowerUpParticleSize(i),
gameLogic.getPowerUpParticleSize(i));
}
for (int i = 0; i < gameLogic.getBlackArrSize(); i++) {
g.setColor(Color.BLACK);
g.fillOval(gameLogic.getBlackParticleXCoor(i),
gameLogic.getBlackParticleYCoor(i),
gameLogic.getBlackParticleSize(i),
gameLogic.getBlackParticleSize(i));
}
}
When are you triggering repaint()? Is it set on a timer on some time interval? I created an animation lib and I was able to animate 40 items smoothly. You probably have many more objects but I would look at the time interval first.
Edit:
Ok so here is my suggestion. First you need to figure out which function in paint is taking the most amount of time. It seems like you are calling fillOval a lot. My suggestion would be to create the shape once using something like: new RoundRectangle2D.Double(). And then use AffineTransformation to move around the shapes instead. I am curious to know what other suggest, so I will check back.
Try exploring the Graphics2D library. The fill algorithm for any shape, even something as simple as an oval, is expensive. It is far faster to create a shape, fill it then copy it to other locations. The 2D library also supports various transforms for scaling etc.
This is very similar to Daniel Viona's sprite suggestion. Sprites exist for a reason - they are a fast way to draw many small simple objects!
If I get some time I will try to draw 1000's of small objects - care to give an idea of the size range these objects will have? Just a few pixels? I am guessing the power ups and bombs are relative few, it is only the particles that are hurting you right...
about 80% of the time was spend in the jpanel's paint method
Since the vast majority of your processing is single-threaded, that means at least one CPU core is being almost entirely wasted on modern machines. Most computers have at least dual-cores these days, if not more. There are a couple ways for you to take advantage of this:
If 80% of your time is spent in one method, make sure that the remaining 20% doesn't have to wait for the painting to complete. Compute the next frame in advance while waiting for the last one to finish drawing.
Your paint method can also be split up into multiple threads. It's not safe to share the same Graphics object between multiple threads, but you can have different threads rendering to different images then composite them together at the end.
For example, have one thread take the first half of the particles and render directly to the backbuffer. A second thread takes the remaining particles and renders them to an image. Once they both complete, blit the image to your backbuffer, combining the two images.
As a bonus note... If you really want performance, consider JOGL. Look here for an example:
http://download.java.net/media/jogl/jsr-231-2.x-demos-webstart/ParticleEngine.jnlp
Related
I am making a copy of Terraria and obviously There is a beneath zone where thousands of "dirt" objects will be loaded. However this causes a heavy drop in my FPS. I have searched the web but I can't find any solutions due to my specific problem. I have tried rendering only 100 blocks and to only render what is in screen however my FPS drops to 20 even when 300 or so blocks are rendered. My game looks like this
(only what is seen is being rendered)
There are exactly 4 different images and the code to run this looks like this
public void render() {
fps.log();
collisionHandler.run();
// collisionHandler.update();
player.update();
camera.update();
mapGrid.update();
Gdx.gl.glClear(GL30.GL_COLOR_BUFFER_BIT);
batch.setShader(null);
batch.begin();
batch.draw(background, 0, 0, Gdx.graphics.getWidth(),
Gdx.graphics.getHeight());
batch.end();
mapGrid.drawGrid(renderer);
batch.setShader(null);
player.draw(batch, renderer);
ground.update(batch);
ground1.update(batch);
for (int i = 0; i < groundList.size(); i++){
groundList.get(i).update(batch);
shader.begin();
shader.setUniformf("u_resolution", groundList.get(i).getPosition());
shader.end();
}
//batch.setShader(shader);
for (int i = 0; i < dirtList.size(); i++)
// update means render*** dirtList consists of dirt objects!
dirtList.get(i).update(batch);
}
I have tried storing these objects in a pool or cache and yet nothing seems to boost the FPS back to 60. Please help any help or direction at all would be greatly appreciated!
It might be better to use a repeating texture or something for big clusters that can be drawn at once. For example, if there is a rectangular cluster made of 10*15 blocks, instead of drawing 150 blocks one by one, just fill the are with the same repeated texture. Break it down to smaller clusters when the player interacts with them.
In the screenshot you posted, all of the dirt blocks can be drawn as a single block. If any of them break, just recalculate big rectangular areas, and draw new smaller clusters.
Here's an example for repeated textures
How many dirt objects are you trying to render per frame? If its in the thousands as you say, I would say this is your problem.
Pooling/caching wont fix your framerate here. It'll only help you with memory management when creating/destroying all those dirt objects.
If you know your way around GLSL, you could actually render all those dirt blocks with one pass, instead of thousands. Thats one solution.
I'm new to Java's graphics library and I'm still coming to grips with its limitations. Below I have a draw function for a Grid class, which draws a 2D array of tiles as filled rectangles.
Not that it's pertinent to the question but the the scale and offset arguments are there to adjust the Grid's tiles and tileSize variables such that it's drawn in the correct position and scale on the screen.
My question is it normal for this to lag considerably when the tiles variable is quite big? I normally get around 500 fps without any Grids on screen, and no noticeable reduction with a Grid of tiles[10][10] to [50][50]. But at tiles[1000][1000], or 1,000,000 total rectangles to draw, fps drops to 7.
I know a million is a lot, but they are just rectangles after all, and my pc can play a game like Skyrim on full settings no problem. I'd imagine there's more than a million polygons on display in Skyrim, and they comes with all sorts of highres textures and lighting and all so, why should a million gray squares be such a problem? Is Java's graphics library really poor? Am I expecting too much? Or, as I suspect, is there a much better way of drawing something like this?
I can provide the main class's paintComponent if that's important, but it's just a call to _Grid.draw() so I don't think the problem's there..
public Graphics draw(Graphics g, double scale, Point offset) {
Graphics2D g2 = (Graphics2D) g;
for(int i = 0; i != this.tiles.length; i++) {
for(int j = 0; j != this.tiles[0].length; j++) {
boolean draw = true;
if(this.tiles[i][j].type.equals("EMPTY")) {
draw = false;
} else if(this.tiles[i][j].type.equals("PATH")) {
g2.setColor(Color.LIGHT_GRAY);
} else if(this.tiles[i][j].type.equals("WALL")) {
g2.setColor(Color.DARK_GRAY);
}
if(draw) {
g2.fillRect((int)((this.xPos+i*this.tileSize)*scale + offset.x),
(int)((this.yPos+j*this.tileSize)*scale + offset.y),
(int)(this.tileSize*scale),
(int)(this.tileSize*scale));
}
}
}
return g2;
}
Java's BufferedImage class is slow. It's a known fact. If you want to do fast image manipulation then it's the wrong tool to use.
Additionally games like Skyrim are using the graphics card to do most of the work, using the Java Image stuff it is all being done in the CPU.
You should really look into using a game framework - there are 2d ones like Slick2d and 3d ones like jMonkeyEngine3.
A few things you might consider trying:
If your original Graphics object is being created by a BufferedImage, consider using VolatileImage instead. As others have said, everything in this draw method is happening on the CPU via the Swing Event Dispatch Thread, so you are limited to a single CPU for drawing (unless you spawn more Threads somewhere). VolatileImage, when used for double-buffering, takes advantage of the graphics hardware and can be MUCH faster for this kind of thing.
You're doing a TON of String comparisons, which can be very slow. I'd consider refactoring your type field to be a custom enum instead, then have that enum define its own draw method that takes a Graphics object and draws itself. That way, you could call this.tyles[i][j].type.draw(g2) and rely on late binding to draw the right colored rectangle, thereby eliminating the String comparisons.
Make sure you're only drawing the stuff that's on screen. I don't know anything about your tiles array, but if it's significantly larger than what's actually being rendered to screen, you could be wasting a lot of CPU cycles there.
This might sound silly, but you're actually doing twice as many memory accesses as you really need. I'm going to assume you have Tile[][] tiles ... defined somewhere in your source code. In the outer loop, grab the row first by writing something like Tile[] row = tiles[i];, then in your inner loop, get the type by calling row[j].type. This will cut your memory access count in half when iterating over the tile array.
So I am making some kind of small 2D - Game-Engine.
So far it is working quite okay, however the Rendering is a bit jittery, and has a lag every 1-2 Seconds (frame freezes for half a second). Even though this is hardly a deal breaker, it is stil an annoyance that should be sorted out, and I am obviously kind of curious about why this is.
So my current Method to Render the frame is by manipulating the g2d Object of a certain JPanel:
(img is the Map that is drawn. This method is part of a Class that has all Information like width and height of the Screen and Position of the Camera. (so PosX,PosY,width,height are taken from the Instace of the Object this is called on)
public void DrawByManipulatedMapSubimage(BufferedImage img, Graphics2D g2d)
{
if (isActive)
{
BufferedImage img2 = img.getSubimage(PosX, PosY, width, height);
g2d.drawImage(img2,0,0,null);
List<MapObject> MapObjects = Map.getObjectInformation();
List<UiComponent> UC = this.UI.getUiComponents();
int l = this.Map.getObjectInformation().size();
for (int i = 0; i < l; i++)
{
MapObject MO = MapObjects.get(i);
int MOX = MO.getPosX();
int MOY = MO.getPosY();
BufferedImage MOB = MO.getCurrentAnimation().getCurrentlyActiveFrameAsBufferedImage();
g2d.drawImage(MOB, MOX - PosX, MOY - PosY, null);
}
for (int i = 0; i < UC.size(); i++)
{
UiComponent CC = UC.get(i);
if (CC.isVisible())
{
Point P = CC.getPosition();
int x = P.x;
int y = P.y;
g2d.drawImage(CC.getImg(),x,y,null);
}
}
try
{
Thread.sleep(0);
}
catch (InterruptedException ex)
{
Logger.getLogger(Viewport.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
What this Basically does is, that it
Paints a Subimage of the currently active Map, based on where the User is viewing from (the Camera-View in a Way)
Draws the BufferedImage of the Current Animation of all attached MabObjects (basically Everything that is due to change; like the Player, Npcs, moving trees, spaceships, whatever) on top of that.
Draws all attached UserInterface-Components on top of the last two things.
(just things like character portraits for example; no buttons or anything interactable)
The thing that is making me curious is, that this can't be a case where a Program is using so much cpu that it just can't keep up with Rendering, as the Program has the same lags running with x attached extras as without any.
Also this program uses about 5% of my CPU (at 30fps)
So the Problem has to be somewhere else.
Do you maybe have an idea to optimize it?
The thing that is making me curious is, that this can't be a case
where a Program is using so much cpu that it just can't keep up with
Rendering, as the Program has the same lags running with x attached
extras as without any. Also this program uses about 5% of my CPU (at
30fps)
caused by Thread.sleep(int), you shouldn't to use Thread.sleep(int), only in the cases that you want to simulating some long and expensive sleep, otherwise to use Timer
Thread.sleep(0) == zero miliseconds freeze current JVM instance untill loop ended, nothing happens untill this loop freezed by Thread.sleep() ended,
latency in Native OS isn't under 16milisecond, 25 could be a limit, but to use Timer instead with this value
g2d.drawImage
for todays Java6/7
should be invoked by override paintComponent in JPanel,
1st. code line inside paintComponent should be super.paintComponent() == reset all previous painting, otherwise painting cumulated
delayed or repainted by Swing Timer on reasonable frequency 33-50 milisecond
it about good practicies in painting in Swing to store current snapshot in BufferedImage, and all volatile variables or Objects could be stored in List<Whatever>, inside paintComponent to g2d.drawImage and for rest of painting to loop inside prepared array of Objects (List<Whatever>)
for better help sooner post an SSCCE, short, runnable, compilable, just about a.m. issue
You are probably suffering from Garbage Collection pauses, due to excessive allocation. Some methods look very suspicious regarding allocation (e.g. "getCurrentlyActiveFrameAsBufferedImage" ).
Use VisualGC to check allocation rate, then improve your program or try to get a big eden. Large binary objects such as bitmaps/byte[] are allocated directly on OldSpace, so if you create too much of them, you'll trigger a lot of oldGC pauses. If this is the case, tweaking Eden size will not help.
Edit: check wether the 'pauses' correlate with GC by running your program with the -verbose:GC option
I have an array that holds the names and then displays the appropriate graphics using:
public void paintComponent(Graphics g){
for(int i = 0; i<20; i++)
{
for(int j = 0; j<20; j++)
{
if(testMap.getData(i+(rowX-7), j+(columnY-7)).indexOf("rock") >= 0)
{
g.drawImage(image3, j*50 + 5*add, i*50 + 5*add2, 50, 50, this);
}
}
}
This works well displaying the actual graphics of my game. However, for my minimap, I do the same thing, with different values and increased numbers in the 'i' and 'j' in the for loop. This creates a smaller version of the map with a bigger scope, which leaves me with this. (Note, I only included one of the drawImage() methods, I removed the rest here to make the code more readable):
This is pretty much my desired effect (aside from the positioning, which I can change easily), however, this only shows a smaller version of what I already see on the screen. Any larger than about 20 x20, though, and the game begins to lag heavily -- probably something to do with the terrible way that I coded it.
I have tried replacing the images with squares using fillRect, but it does not help the issue.
Here is the code of my main class if anybody would like to take a look: http://pastebin.com/eEQWs2DS
The for loop within the paintComponent method begins at around line 3160, the loop for the main display is around 2678. I set up the Jframe at around 1260.
So, with all that said, basically my question is this:
Is there a better, more efficient way to create my minimap? My idea was to generate an image at the beginning of the program so that the game wouldn't have to recalculate every time the frame refreshes. I could create the map in advance, but I would have to manually update that every time I changed the map, which is definitely a hassle. I am having trouble researching how to do the former. My other idea is to slow the refresh rate of the minimap only, but I also do not know how to do that.
Other than that, if there are any simple fixes to the lag issue, please enlighten me. I apologize for the very, very messy code -- This is my first time programming anything with a display so I sort of just...hacked at it until it worked.
I don't know how easy this would be with your implementation, but instead of drawing an image, perhaps you could draw a square of a certain color based on what type of tile it should be?
For instance if you're looping through your list of tiles and you find a grass tile, you would first draw the grass tile at its proper location, then draw a smaller green square on the minimap.
The downside to this is that first you'd have to define what colors to use for the tiles, or perhaps when you load the tiles you can compute an average color for each one, and then just use that. Another issue is that the houses may not translate well on to the minimap, since the tiles have much more detail. You could draw some kind of house icon on the minimap instead of actually drawing any of the house tiles, though.
Basically, you want to use simpler representations of the objects in your map on the minimap, since it's smaller and less detail can be drawn anyway.
Have a look at how I do it in Tyrant (a Java roguelike):
https://github.com/mikera/tyrant/blob/master/src/main/java/mikera/tyrant/LevelMapPanel.java
Key tricks:
Use an offscreen BufferedImage. Cache this to avoid recreating it each time you paint.
Use an int[] array to setup up a single colour for each tile
Use setRGB with the array to draw all the map at once
Draw the mini-map at x2 zoom to get 2x2 pixel blocks for each tile.
If you want to be even more efficient, keep a changed flag and only update the mini-map BufferedImage when something visible on the mini-map changes. I didn't think it was worth it as the above is already very efficient, but YMMV.
I'm currently working on a simple 2D top-down shooter in which you maneuver your character around a map. The graphics are still very much in the "testing" phase (solid colors and rectangles). The drawing process is as follows:
I have the map (this is basically just walls or free space) stored as an integer array which is then looped through and painted onto my JPanel.
Once the map is drawn, I go through each "entity" on the map (powerups, other characters) and place them.
Finally, I draw the user's character
I experimented with different methods, and finally decided that the user's character would stay in the center of the screen with the map moving around it.
Currently, it works but it seems slow. I only have 4 walls, 2 entities, and the user's character being drawn and it's at 90FPS, spiking around 60-70 quite often. This would be acceptable if the game were finished, however I still have fancier graphics, many more entities, walls, bullets, etc. to be rendered onto the map. I've tried increasing the speed of my gameLoop, which can get it as high as 230 FPS, but even then when I move into areas with entities or walls it spikes down to 50-70.
I've tried limiting what is drawn to only what is visible but the results were overall negligible and even negative.
The current paintComponent() method looks something like this:
public void painComponent(Graphics g) {
for(int x = 0; x < Map.size.getWidth() ; x++) {
for(int y = 0; y < Map.size.getHeight(); y++) {
if(mapArray[y][x] == WALL) {
g.setColor(Color.BLACK);
g.fillRect(x, y, wallSize, wallSize);
}
}
}
for(int i = 0; i < map.countEntities(); i++) {
Entity obj = map.getEntity(i);
g.setColor(Color.WHITE);
g.fillRect(obj.getCoords().x, obj.getCoords().y, obj.getSize().width, obj.getSize().height);
}
Robot bot = map.getPlayerBot();
g.setColor(Color.BLACK);
g.fillRect(bot.getCoords().x, bot.getCoords().y, bot.getSize().width, bot.getSize().height);
}
Do any of you gurus out there have any tips to speed this up a bit?
Beyond the answers mentioned in the following links:
Java 2D Drawing Optimal Performance,
Java2D Performance Issues,
Java 2D game graphics
and How to display many SVGs in Java with high performance
A common and simple trick to improve 2D performance is to draw all the static objects ('background') into a buffered image, then when drawing the next image you just offset the pixels of your background image from the previous screen accordingly, then you need only draw the pixels around at most two edges of the screen rather than the entire screen, this only really matters when you're drawing a lot of image layers to the screen.
Beyond all of that make sure you have double buffering enabled so that the drawing doesn't try and draw to the actual screen while you're still drawing the next image.
Seph's answer was taken into consideration and implemented, but it did not increase the speed. Apparently, the image being drawn was not the problem at all. The problem was the timer that I was using to call the update methods. I was using an instance of the Timer class which seems to be very unreliable, especially at small intervals such as these. Upon removing the timer and calling my update methods based on the system clock, I was able to reach and maintain incredibly high FPS.