I am making a 2D platform in Java, and I'm using a Swing timer, it is set at 5 milliseconds interval. However, sometimes it is smooth movement at a good speed, but then randomly it will become super fast and sometimes super slow. What could be the reason for this?
Code:
public Board() {
addKeyListener(new KeyListener());
setFocusable(true);
setBackground(new Color(204,250,255));
//draws the object off the screen in memory, then brings it in
setDoubleBuffered(true);
...(other code not relevant)...
timer = new Timer(5, this);
timer.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
//draw platform
for(int i = 0; i < platform.length; i++) {
//g2d.setColor(new Color(0,0,0));
//g2d.drawRect(platform[i].getX(), platform[i].getY(), platform[i].getWidth(), platform[i].getHeight());
g2d.drawImage(platform[i].getImage(), platform[i].getX(), platform[i].getY(), this);
}
//draw guy
g2d.drawImage(guy.getImage(), guy.getX(), guy.getY(), this);
//destroy unneeded process
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
Basically, the cycle function decides whether the character needs to move or not, and moves it if it does.
It is hard to make Java work as a real-time systems…. because of unknown factors like when the OS will schedule your process to run and GC. Since you are trying to update the game at 200 fps (if my math is correct) you might want to try the following approach. To make the game smoother I would try to find the time between the current event fired and the last event fired, now theoretically that might be 5ms but sometimes it will be 7ms (going super slow) and sometimes it will be 3ms (going super fast). Ones you have the time between the two event I would calculate where the animation should be, so if the time is 3ms then the animation will be a bit before the 5ms time. Lets take a simple example where your are trying to update the X position of a image 5 px every 5ms.
//your current code
newX = X + 5px
//new code
//this wont always be 5ms
timeDiff = currentEventTime – lastEventTime;
newX = X + 5px (timeDiff/5ms)
Related
I'm working on some animation where I have a certain number of dots wandering around my JFrame and based on their distance they should be connected by lines of different strengths.
The base code for moving the dots works and actually I also had them displayed correctly in the beggining but I had some issues where the movement was stuttering (probably due to the repaint process). At that point the Window class handled the entire repaint procedure.
After reading some posts around here I adapted my code according to the github page linked in this post to use the individual Dots as JComponents and have them being repainted individually. However, now the problem is that although I still have 100 Dots as components on my JPanel, only one of them is being painted (however, the stuttering is gone at least). I also see that all components are being iterated and their repaint method is being called but they just don't display.
This is my paintComponent method in the Dot class:
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(new Color(0, 0, 0));
Ellipse2D.Double circle = new Ellipse2D.Double(x - 10 / 2, y - 10 / 2, 10, 10);
g2d.fill(circle);
}
And this is what my repaintTimer looks like:
final Timer repaintTimer = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for(Component component : window.getContentPane().getComponents()) {
component.repaint();
}
recalculateDots();
}
});
repaintTimer.start();
The result I get is something like this:
I tried some things of which I thought that it could solve the problem but nothing really helped and I'm really confused as to why this is happening. I'd appreciate any help very much because this issue doesn't make any sense for me.
I am working on a program utilizing Java2D, but performance was not as good as i hoped it to be. I made some measurements and found that it can take something like a millisecond to draw a single line, which seemed insane to me.
I tried to create a simple benchmark application, but performance was surprisingly great (2 orders of magnitude better). So I searched for the discrepancy between my real application and my benchmark application. While I could find and fix some issues (e.g. using strokes can slowdown everything by factor ~10 and using OpenGL hardware acceleration makes it even worse), a significant performance loss seems to be related to the setXORMode method and I don't get it:
Here is my benchmark code:
public class Main {
final static boolean USE_XOR_MODE = true;
static boolean firstFrame = true;
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(1900, 1000);
frame.setUndecorated(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent drawComponent = new JComponent() {
private AffineTransform originalTransform;
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
if(USE_XOR_MODE && firstFrame){
System.out.println("Doing some XOR stuff...");
g.setXORMode(Color.WHITE);
g.setColor(Color.BLACK);
g.fillRect(0, 0, 1, 1);
g.setPaintMode();
firstFrame=false;
}
g2d.setColor(Color.WHITE);
long timeStart = System.nanoTime();
g2d.fillRect(0, 0, 500, 500);
System.out.println("FillRect: "+(System.nanoTime()-timeStart)/1000+"us");
System.out.println();
}
}
;
drawComponent.setDoubleBuffered(true);
frame.add(drawComponent);
frame.setVisible(true);
}
}
If I run this application, I get the following output:
Doing some XOR stuff...
FillRect: 6124us
FillRect: 220us
FillRect: 187us
If I set the USE_XOR_MODE constant to false, it looks like this:
FillRect: 66us
FillRect: 12us
Now let's take a look and try to analyze:
let's ignore the first draw calls and focus on the last: 187us vs 12us
if there are more draw calls the measured performance doesn't really change all that much... using the xor mode is more than a magnitude slower than not using the xor mode
note that in both programs the same code is measured and from my understanding should be executed exactly the same way (since setPaintMode undos the XORMode)
I even tried using the Graphics2D.set/getComposite methods to restore the composite the the XORMode destroys and PaintMode is supposed to reset with no success
also note that the XORMode is only used on the first draw call! So using the XORMode even once messes up performance forever it seems!
if fillRect is not called after setting the XOR mode, i don't see any performance issues
My questions:
Why is there a decrease in performance after calling setXORMode? Is this a bug?
Is there a way to (re)set the Graphics object to a state, where you gain back the original performance?
I have been developing on my Mac a JAVA Applications . The logic is as follows:
A Server sends to a client application some orders to Draw basic shapes
The client applications draws the the basic shapes into a Jpanel
Every Time a Shape arrives the program calls repaint()
public void paintShape(Shape p)
{
//this.paintComponent(this.getGraphics());
arrayofShapes.add(p);
this.repaint();
//this.updateUI();
//this.update(this.getGraphics());
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g2d = (Graphics2D) g.create();
g2d.setStroke(new BasicStroke(2));
g2d.setColor(pickedColor);
for(final Shape p : arrayofShapes)
{
g2d.draw(p);
}
//g2d.dispose();
}
Everything works smoothly(on real time) , so I decided to test the same application on a Windows computer. The result is a laggy application. These are the conclusion that I have reached.
RepaintManager is accumulating repaint() calls. I see how the shapes arrive at destination but in some cases more than 5 repaint calls are accumulated into one, which make the application very lagged/not real Time.
I have tried instead of calling repaint every time a shape arrives to do it with a Timer every few milliseconds, the result is the same. Code :
ActionListener listener = new ActionListener(){
public void actionPerformed(ActionEvent event){
repaint();
}
};
Timer displayTimer = new Timer(5, listener);
displayTimer.start();
In addition i have tested some random code that allows you to paint with the mouse, same logic as mine with paintComponent. In this case it work smoothly without sense of lag.... Example: http://javagraphics.blogspot.com.es/2010/06/shapes-implementing-freehand-pencil.html
I do not understand why paintComponent is so slow on my Windows Computer(same Jar). What could be affecting the performance of my program?
I have read all the answers regarding paint Components but any of them has solved this issue.
Any advice on how could I solve the problem and actually archive Real-Time?
Thank you in advance
Update Videos:
Mac Video:https://youtu.be/OhNXdGXoQpk real Time no problem handling heavy load
Windows Video https://youtu.be/yol2miHudZc clearly laggy
I apologize for the low quality
Update BufferedImage:
After introducing the BufferedImage the result is still a slow painting Application. It creates another problem, since one of the orders is to delete all shapes, it adds some complexity since I have to do a :
g2d.clearRect(0, 0, screenSize.width, screenSize.height);
HW/OS/JavaVersion
Windows
Processor i5-4300u 2.5ghz
Ram 12gb
Java version 1.7.0_71
MAC
Processor i7 2.9ghz
Ram 8gb
Java version 1.7.0_67
Java VisualVM
Video of live VisualVM:https://youtu.be/cRNX4b3rlZk
I do not see anything strange that could explain why the lag occurs but I'm far from being an expert(Again sorry for low quality)
Thank you for all your responses
There's no need to create() a new graphics context each time; just cast g to Graphics2D. This is safe on all concrete implementations. This also obviates the need to dispose() of the created context. As noted here, preserve any context variables that may be critical for later painting.
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
Stroke oldStroke = g2d.getStroke();
g2d.setStroke(new BasicStroke(2));
g2d.setColor(pickedColor);
for(final Shape p : arrayofShapes) {
g2d.draw(p);
}
g2d.setStroke(oldStroke);
}
Also, compare the profiles on both platforms to look for disparities. For reference, the example cited here comfortably handles selections containing hundreds of shapes on either platform.
I would recommend that you do static drawing to a BufferedImage, and then draw the BufferedImage in your paintComponent method.
e.g.,
private BufferedImage bufferedImage = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_ARGB);
public void paintShape(Shape p) {
Graphics2D g2 = bufferedImage.createGraphics();
g2d.setStroke(MY_STROKE); // make this a constant
g2d.setColor(pickedColor);
g2d.draw(p);
g2d.dispose();
this.repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (bufferedImage != null) {
g2.drawImage(bufferedImage, 0, 0, null);
}
// do dynamic drawing such as drawing of moving sprites here
}
After more than two days of debugging I have found out that the problem has nothing to do with paintComponent()
With the same server generating random shapes
At the Windows app, some shapes are received at the same time which is impossible since I am sending shapes every 15 ms. That's why it accumulates shapes(result =lag).
On the other Hand at the Mac app, every shape has different time reception (result = real Time)
Thank you for the kind responses, and sorry for inconvenience I may have cause
My game engine is drawing a tiled map on a Canvas inside a JFrame. I am using a Bufferstrategy to get smoother rendering and tried to boost the performance at several points inside the program and end up with ca. 120 frames per second to fill a 1920 * 1080 Window with a black color and draw some isometric 512 * 256 tiles on top. When moveing the map around there are small black lines in between the tiles and . I assume that they occur because some tiles have already been moved to the new position, but the image isn´t done yet, when put on the sreen. But I don´t really have a solution to that and I´m also not sure if I´m right.
I should probably metnion that I´m using two threads in my program, one thread calls update and render methods, the other one is currently just getting user input to move the camera position. Also in some lauches everything works smoothly and in others it flickers very bad.
Here is some code of the window creation and rendering:
private final void createWindow(){
frame = new JFrame(title);
frame.setMinimumSize(size);
frame.setMaximumSize(size);
frame.setPreferredSize(size);
display = new Canvas();
display.setMinimumSize(size);
display.setMaximumSize(size);
display.setPreferredSize(size);
image = ImageLoader.boostPerformance(new BufferedImage((int)size.getWidth(), (int)size.getHeight(), BufferedImage.TYPE_INT_RGB));
graphics = (Graphics2D) image.getGraphics();
frame.add(display);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.requestFocusInWindow();
}
public final void renderToScreen(){
BufferStrategy bs = display.getBufferStrategy();
if(bs==null){
display.createBufferStrategy(bufferCount);
return;
}
graphicsToScreen = (Graphics2D) bs.getDrawGraphics();
graphicsToScreen.drawImage(image, 0, 0, display.getWidth(), display.getHeight(), null);
graphicsToScreen.dispose();
bs.show();
}
I use the Graphics2D graphics object in other classes to draw the tiles.
public void render(Graphics2D g) {
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
tilemap.render(g);
g.setColor(Color.red);
g.drawString(String.valueOf(Main.actualFps), 30, 30);
}
Above is the render method in another class using the graphics object I mentioned.
And here you can see the tile map render method:
private final void renderIsometric(Graphics2D g){
for(int x = 0; x < 4; x++){
for(int y = 3; y >= 0; y--){
if(x > tilesX - 1|| y > tilesY - 1)break;
if(x < 0 || y < 0)continue;
if(map[x][y] == null)continue;
g.drawImage(map[x][y].getImage(), (int)orthoToIso(x, y).x - cameraPosX, (int)orthoToIso(x, y).y - cameraPosY, null);
}
}
}
Finally here is the main loop to ensure that the fps counter is working and the value of 120 fps is correct:
while(running){
long now = System.nanoTime();
double ns = 1000000000.0 / preferredUps;
delta += (now - lastTime) / ns;
lastTime = now;
while(delta >= 1){
//call the update method
update();
updates++;
delta--;
}
//call the render method
render();
frames++;
//refresh the actualUps and actualFps once every second
if(System.currentTimeMillis() - timer > 1000){
timer+=1000;
actualUps = updates;
actualFps = frames;
updates = 0;
frames = 0;
}
}
How can I get rid of these tiny lags and flicker problems? And additionally I have been wondering if it would give better results to use OpenGL via LWJGL instead of Java2D?
Thanks for your help!
What you see there is most likely not flickering strickly speaking. Its tearing and weaving artifacts. Its a natural consequence of updating the screen without synchronizing to the vertical synchronization of screen refresh.
Windowing systems are in general not made to honor v-sync (they favor performance, as those effects are not grossly perceptible - or important - in a normal UI). As far as I know there is no way of synchronizing with a (J)Frame (you could probably hack something with JNI, asking the OS to tell you when the raster beam is at the top of the frame, but thats beside the point).
Also rendering at 120fps may actually make the problem worse, unless the display device also refreshes at 120hz - when you render more frames than the device actually displays you force the device to display graphics from more than one frame (e.g. frame 1 in the top half of screen, frame 2 in the bottom half). This will always result in perciptible artifacts with moving objects.
There is one way around it in pure java, and thats fullscreen mode (that will wait for v-sync, as a result your rendering rate is limited to and controlled by the refresh rate of the display device).
You can try openGL, but I have no idea if it will synchronize when rendering in windowed mode.
A first step to improve things would be to not render faster than the display devices displays (e.g. max. 60 fps for a 60hz screen).
In case your application is not time critical, you can resolve flickering by using a double-buffering strategy.
Essentially, the image you are compositing is copied to a secondary off-screen image buffer, which is not rendered this frame, but next frame. There are some tricks you can use to have Swing components double buffer automatically for you, see also:
https://gamedev.stackexchange.com/questions/30921/thread-safe-double-buffering
Double buffered image example in Jpanel
The downside of double buffering is that you introduce one frame of latency, which means your input is only going to result in visible changes one frame later than it would without double buffering. At 120hz this is probably not going to be a big issue, but just for completeness sake..
I have made a custom component (derived from JComponent) which represents
a draggable Bezier-curve.
(looks like a hanging cable, someone might know it
from Bender or Cubase)
My problem is: The curve may become really long,
let's say from top left to bottom right corners of the desktop.
This makes Swing's repaint functionality inefficient:
The area of the curve is perhaps few hundred pixels, but the area of
the component (being mostly 'transparent') is millions of pixels big.
My subjection impression is:
The longer the curve, the more flicker I get when dragging it.
I hope I made myself clear about the problem.
Perhaps it would help when I somehow could choose by myself, which regions
of the component needs repainting at all.
EDIT:
Such a mess! I'm profiling the application using Netbeans, which helps to
find inefficient code normally, but this Swing framework is making hundreds
of nested calls! I just can't figure out, what is slow and why.
By the way, disabling super.paint(...) or super.paintComponent(...) doesn't help.
Check out Filthy Rich Clients by Chet Haase and Romain Guy. They address these very optimizations among others along the way to producing responsive and graphically impressive UI.
Doing all of your bezier mathematics on the paint thread everytime the component is refreshed is (as you've gathered) a bad idea. Does your curve change often? If not then why not paint it to a BufferedImage as and when it changes, and change your paint() code to simply draw the buffered image to the component instead.
class CurveComponent extends JComponent {
private BufferedImage image;
#Override
public void paintComponent( Graphics g ) {
if ( image == null ) {
return;
}
g.drawImage( image, 0, 0, this );
}
private void updateCurve() {
image = new BufferedImage( getWidth(), getHeight(), BufferedImage.ARGB );
Graphics g = image.getGraphics();
// draw the curve onto image using g.
g.dispose();
}
}
Only call updateCurve() when you need to and all that expensive mathematics won't be needlessly repeated. Painting should be pretty responsive, even for a fullscreen window. drawImage() will be doing a straightforward memory copy and should be lightning fast.
Try writing a tiny test app, which consists of nothing except what you need to reproduce this problem. This will make profiling easier. Then post that app here, so we can take a look at possible solutions.
I found your question interesting so I wrote a test app myself. This draws a Bezier curve which is continually resized as you drag. I created a gradient background to ensure this works well with a nasty background. I get good performance and low flicker, although I use top-notch machine.
It pays to read "Filthy Rich Clients" to learn all the tricks of writing custom Swing components that perform really well.
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Point2D;
public class CustomComponent extends JComponent {
private Point2D start = new Point2D.Double(0, 0);
private Point2D end = new Point2D.Double(300, 200);
private CustomComponent() {
this.setOpaque(true);
final MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
setEnd(e.getPoint());
}
};
this.addMouseListener(mouseAdapter);
this.addMouseMotionListener(mouseAdapter);
}
public void setStart(Point2D start) {
this.start = start;
repaint();
}
public void setEnd(Point2D end) {
this.end = end;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
final Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// draw gradient background
final int width = getWidth();
final int height = getHeight();
g2.setPaint(new GradientPaint(0, 0, Color.WHITE, width, height, Color.YELLOW));
g2.fillRect(0, 0, width, height);
// draw Bezier curve
final Shape shape = new CubicCurve2D.Double(start.getX(), start.getY(), start.getX(), end.getY(), end.getX(), start.getY(), end.getX(), end.getY());
g2.setColor(Color.BLACK);
g2.draw(shape);
g2.drawString("Click and drag to test for flickering", 100, 20);
}
public static void main(String[] args) {
final CustomComponent component = new CustomComponent();
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
final Dimension size = new Dimension(screenSize.width - 20, screenSize.height - 100);
component.setPreferredSize(size);
final JFrame frame = new JFrame();
frame.add(component);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Some things to note:
only overwrite paintComponent(Graphics g), not the other paintXXX() methods
set custom component to opaque if possible
only use repaint() to request repainting. Never directly order a repaint directly in your code. This lets Swing handle it well.
There is no efficient way to create lots of small clip rectangles for a diagonal structure which leaves you with two strategies to avoid flickering:
Double buffering. This needs an enormous amount of memory but the memory copy is very fast (it usually happens in the time the "electron beam" goes back from lower right to upper left ... if there was still a beam in your LCD).
Don't call super.paint() (which draws or "erases" the background) and draw the curve a second time with the background color to erase it.
For more details, see this document.
[EDIT] If fillRect() wasn't abstract, you could set a break point :) Set a break point in paint(), check who calls it and whether the background got cleared at that time. It should be since rendering would be completely wrong. Then set break points further up in the call chain.
You can redraw a smaller portion of the screen using repaint(Rectangle r)
http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JComponent.html#repaint(java.awt.Rectangle)
Then you mention flicker. Since you are using swing, which uses double buffering your flickering must be coming from something else. Are you clearing the screen in paintComponent(...)? I.e. call to fillRect(...)? Don't do that, it's not needed (IIRC).
Which method do yo use to paint your curve? paint or paintComponent?
My solution was a partial re-design:
Now I don't represent each "cable"-element by a component.
Now, cables are just dummy objects (with no involved JComponent).
The repaint takes place "globally", on the content pane of the parent JFrame.
Now it's efficient, and flickers less.
just use getVisibleRect(); inside paintComponent(Graphics g) to get the area you actually need to redraw