What is the fastest way to Render to a JPanel? - java

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

Related

Graphics2D performance issue

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.

Java Paint Component Refresh Rate?

I am developing a small Game in Java, and I'm rewriting the Player Movement system to not be Grid-Based. It is a 2D side-scroller, and what I'm trying to achieve is basic Player Movement, so that when the user presses, and holds, the right Key the Player moves right, and the same for the Left Key. The problem I am having is that the Paint Component in another Class draws the image of the Player on the screen, with positions X and Y stored in a Settings Class. Then a KeyListener Class gets when the user is pressing Right, and adds to the X value (And the same for Left, but minuses 1 every time). This creates a slowly moving Character on the screen, and what I want to do is have him move faster without adding more than 1px each time as it would seem like he was skipping pixels (I've already tried).
I was wondering if there was a better way to go about this, as the code I'm using is bare-minimum, and my outcome would be a smoothly moving Player.
KeyListener Snippet:
public void keyPressed(KeyEvent arg0) {
int key = arg0.getKeyCode();
if(key == 39) { // Right Key
Settings.player_pos_x++;
}else if(key == 37) { // Left Key
Settings.player_pos_x--;
}
main.Game.redo();
}
Drawing User on-screen:
g.drawImage(player_image, Settings.player_pos_x, Settings.player_pos_y, this);
Any help is appreciated, if you need any more information or code please feel free to ask.
Let's try again :)
Double buffering
Quote: http://msdn.microsoft.com/en-us/library/b367a457.aspx
Flicker is a common problem when programming graphics. Graphics operations that require
multiple complex painting operations can cause the rendered images to appear to flicker
or have an otherwise unacceptable appearance.
When double buffering is enabled, all paint operations are first rendered to a memory
buffer instead of the drawing surface on the screen. After all paint operations are
completed, the memory buffer is copied directly to the drawing surface associated with
it. Because only one graphics operation is performed on the screen, the image
flickering associated with complex painting operations is eliminated.
Quote: http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html
Suppose you had to draw an entire picture on the screen, pixel by pixel or line by
line. If you were to draw such a thing directly to the screen (using, say,
Graphics.drawLine), you would probably notice with much disappointment that it takes
a bit of time. You will probably even notice visible artifacts of how your picture is
drawn. Rather than watching things being drawn in this fashion and at this pace, most
programmers use a technique called double-buffering.
Perhaps you could write the app to iterate multiple redraws to the right, each only 1 or 2 pixels per keyboard input received. This way, you're kind of artificially setting how fast you want it to move. Otherwise, you'd be limited to how the user has their keyboard iteration speed set up.
Also, java's jPanel is not exactly the place to look for video game efficiency. Just saying; you might want to look to something like openGL for that.
At best, you could optimize it to have a transition buffer outside of the jpanel drawing logic, with the same draw functionality as a jPanel, this way you write to the buffer, then copy the whole buffer during a writeImage call... I don't know if you're already doing that, but I think it avoids flicker or something... read about it a long time ago, it's called double buffering.
While double buffering will probably help you should first address how you are calculating the distance you move each time.
Change in distance (dx) can be defined as the velocity(v) times the change in time(dt), dx = v * dt. From what I can see you are omitting dt (change in time). You should calculate the time difference from the last time the character was moved and multiply that by your velocity. This way if your distance processing code is executed 10 time or 100 times in 10 seconds the character will always move the same distance.
The code will look something like this:
int currentTime = System.nanoTime();
int deltaTime = currentTime - previousTime;
Settings.player_pos_x += velX * deltaTime;
previousTime = currentTime;
With this code you will probably need to significantly increase velX. Typically I would have a constant that I would multiply by the velocity so they are smaller numbers.
Additionally if you want better movement and physics look into JBox2D although it's probably over kill for you right now. Their documentation might also help to understand basic physics.
In order to answer your question about the speed, most games i've seen store a global int for the velocity in each plane (x and y planes), then apply the velocity to the current position rather than simply incrementing it as you have done above.
public int velX = 2;
public int velY = 2;
public void keyPressed(KeyEvent arg0) {
int key = arg0.getKeyCode();
if(key == 39) { // Right Key
Settings.player_pos_x += velX;
}else if(key == 37) { // Left Key
Settings.player_pos_x -= velX;
}
main.Game.redo();
}
Once you have that set up, try adding operations which may increase the velocity (i.e. holding the key down for a long time, or something else);
Also, try to implement a jump operation, where the players Y position goes up, and try to add a gravity field to make it come back down.

Efficient Methods of Rapidly Redrawing a Map (Java / JPanel)

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.

Dealing with memory making Swing animations

It's my first time writting there but after reading and searching a lot about the subject I'm going to explain, I didn't found anything useful for me (I've checked a post of a guy explaining something similar about an iphone app, but without a final solution). Sorry for my english I'll try to do my best hehe :).
Ok, I'm starting: I have java application that shows a set of animations at the same time on a JPanel. These animations are java objects from Animation (inheritates from JLabel), a class of my own that stores an array of ImageIcon (representing the frames of animation) and an integer pointer, showing actual frame to paint (index).
That class has overriden the method setPaint() in that way:
#Override
public void paintComponent(Graphics g) {
g.drawImage(getImageAt(getCurrentIndex()).getImage(), getX2(), getY2(), getW(), getH(), null);
//getImage at retrieves the frame at a "index" position from frame array.
if (getCurrentIndex() < getTotal() - 1) {
incrementIndex();//increments frame by 1 (next frame)
} else {
resetIndex();//set index to 0 (frame 0)
}
}
On the other hand, we have Display, which inheritates from JPanel and is responsible for repainting the panel (it uses I javax.swing.Timer object and repaints every 50 ms).
All the images from every Animation object have the same size 768x576 and png format with transparencies (thus I can paint many animations at a time).
The problem is that when I load an animation it occupies too much memory because it creates I don't know why either how 200 MB of "int" objects apart of the 2MB that really occupies. When I load more animations the heap's space of the JVM rockets (up to 1.7 GB... I didn't want to try anymore xD). For example, 6 animations may be 405 frames (90 in average).
I've collected this data with JProfiler, but I can't post the capture so this what I get:
Memory usage of my app by JProfiler:
java.awtRectangle 3351 KB
sun.java2d.SunGraphics2D 16.510 KB (I think this is the size of all the animations and sounds logical).
java.lang.String 669KB
int[] 1337 MB
...
In display, the loading images process is in this way:
add(new Animation (loadImageSet(Constants.DIR_CINTA_EJE,"all"),
Constants.A_CINTA_EJE, 75,-200,768,576));//1
Where DIR_CINTA_EJE is the directory where all the frames of that animation are and the value "all" means that I want to load all the frames.
There is a way to avoid that memory used grows linearly with the number of animations? Why those ​​"int" values occupies so much? Are they the pixels of each image?
And yes, I have Increased my heap space up to 2GB and it's working, but at a high cost.
Thank you very much for all the help you can provide. I love this forum, really.
Regards.
I've been working hard looking for a satisfactory solution and finally I've got it!
That's what I have done:
Class Animation does no more inheritates from JLabel it only implements ActionListener, in which actionPerformed() method I increment the frame to show for each one (I use Timer class). Therefore, Animation does no more override paintComponent() method (it's work for DisplayPanel).
In DisplayPanel, I use thre global variables:
private BufferedImage _auxiliary_image;
private ImageIcon _image2display;
private ArrayList<Animation>;//I store there all the running animations there
And now, I override there the DisplayPanel paintComponent(), thus:
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
for (int i = 0; i < _animations.size(); i++) {
if(i==0){
_auxiliary_image.createGraphics().drawImage(_animations.get(i).getActual().getImage(), 0, 0, Color.white, null);
}else{
_auxiliary_image.createGraphics().drawImage(_animations.get(i).getActual().getImage(), 0, 0, null);
}
}
_image2display.setImage(_auxiliary_image);
_image2display.paintIcon(this, g, 50, -200);
}
The reason why I difference two cases, one for i==0 and another for the rest of frames is that on painting I saw a black background and the whole positions of the animation, so is this way that I solve the issue.
Now, with 8 animations with 50 frames each one occuppies only 360 MB of memory instead 1.3 GB I get last time =) (I only paint one image, which results from merging all the frames to be printed at the same time for each animation).
Thank you and I hope this could help!!!

Java awt performance anxiety

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

Categories

Resources