Until now, I've done double buffering by creating and Image, drawing what I wanted to that Image using its associated Graphics object then draw that Image to the screen using the paint method's Graphics object. Recently, I learned about the BufferStrategy class and its uses. I was wondering what are the pros and cons of the two methods.
EDIT:
I dont't think I made my question very clear. I wanted to know the pros/cons of both the DIY method and the BufferStrategy and when, if ever, I should use one or the other.
I've always had good results using the default BufferStrategy by being careful to
Always construct GUI components on the EDT
Never draw from a thread except the EDT
This excellent example must double buffer because it draws continually on the initial thread rather than the EDT. In contrast, this fairly busy example relies on nothing more than repaint() called in response to a Swing Timer. I rarely need an offscreen buffer except for a composite. Finally, this tutorial article offers more guidelines.
I recommend reading Painting in AWT and Swing if you haven't.
I don't think you usually need Do-It-Yourself double buffering if you're using JFrame. Swing has built in double buffering which is turned on by default. Manually doing this yourself will just slow things down. You can check if double buffering is on by calling isDoubleBufferingEnabled() on any of your JComponents.
There are cases where you may want to do this yourself, but that should be the exception rather than the rule. Maybe you're doing something involved like writing a game, in which case maybe my advice doesn't apply. Anyway, hopefully the above is useful info.
Related
I am a complete novice when it comes to any kind of graphics. So, I want to create a method in a class Creature that would be able to draw lines on a screen (turtle graphics style). I have no idea what would be a good way of doing this. I mean I could store all lines drawn by user in a container or whatever and every time the repaint() method is called I would redraw all lines but it looks bothersome. Or perhaps it's the best way and I am just being silly? As I said, I don't have any experience with this and everything is starting to look like black magic to me. I would appreciate any help or suggestions. Thanks!
See Custom Painting Approaches for two common ways to do this:
Keep a List of objects to be painted and repaint them every time
Paint to a BufferedImage and just display the image
Updating what to draw and actually drawing it should be separate, because you can't control when repaint() is called. You usually want to control how often the updating is done so it's always a good idea to separate. This also reduces the time it takes to draw so it increases performance as well.
I'm planning to write a simple spaceshooter. I have read that the repaint() method is only a request, and it doesn't execute every time it's called. I believe I'm noticing the effects of this, as my spaceship tends to lag ever so slightly when I'm moving it. Currently I'm simply drawing my ship in a a JPanel's paintComponent() method, and keep calling repaint() on regular intervals (my panel's also Runnable). Seeing as repaint() may potentially screw me over, I'm trying to find a way to work arround it, however I've ran out of ideas. The code I have so far:
private void renderGraphics() {
if (MyImage == null) {
MyImage = new BufferedImage(getPreferredSize().width,
getPreferredSize().height, BufferedImage.TYPE_INT_RGB);
}
MyGraphics = MyImage.getGraphics();
MyGraphics.setColor(Color.BLACK);
MyGraphics.fillRect(0, 0, getPreferredSize().width, getPreferredSize().height);
MyGraphics.drawImage(ship.getImage(), ship.getCurrentX(), ship.getCurrentY(), null);
}
The idea was to create my own graphics, and then make the JPanel draw it, and keep calling this instead of repaint() in my run() method, however I have no idea how to do that. I'd appriciate any input on the matter.
There are multiple ways to approach this.
The best is probably to use BufferStrategy and draw to that, of which I have included a code snippet that should work for you.
You can take this one step further and abandon Swing altogether, just using Frame/BufferStrategy. There is a fully working example (from which the code snippet was taken and adapted) in my question here:
AWT custom rendering - capture smooth resizes and eliminate resize flicker
Anyway, here is an implementation BufferStrategy that you should be able to just drop in:
// you should be extending JFrame
public void addNotify() {
super.addNotify();
createBufferStrategy(2);
}
private synchronized void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy==null) return;
sizeChanged = false;
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
MyGraphics draw = strategy.getDrawGraphics();
draw.setColor(Color.BLACK);
draw.fillRect(0, 0, getPreferredSize().width, getPreferredSize().height);
draw.drawImage(ship.getImage(), ship.getCurrentX(), ship.getCurrentY(), null);
draw.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
any drawing will still be performed in the Swing Thread, so no matter what you try work around, it wont help.
Make sure you are not doing any lengthy calculations in the swing thread, this may be stopping repaint from being executed as soon as it needs to be executed
Separate all the logic into 2 parts. Static and Dynamic. (e.g. sea and moving ship. Ship changes shape/location on a static image of sea)
Draw static content in an image once and use the image in your paintComponent(). Call dynamic parts painting after the static image.
Use setClip() to restrict repainting areas.
Calling repaint without any arguments means that the whole panel is repainted.
If you need to repaint parts of the screen (the spaceship has moved to a different location) you should make shure that only those parts of the screen are repainted. The areas that stay the same should not be touched.
Repaint takes coordinates of a rectangle that should be repainted. When moving the ship you should know the old coordinates of the ship and the coordinates the ship should move to.
repaint( oldShipCoordinateX, oldShipCoordinateY, shipWidth, shipLength );
repaint( newShipCoordinateX, newShipCoordinateY, shipWidth, shipLength );
This is usually much faster than calling repaint() without arguments. However you have extra effort to remember the last position of the ship and must be able to calculate the new position of the ship.
See also: http://download.oracle.com/javase/tutorial/uiswing/painting/index.html - especially step 3
Just for code that you post here:
1/ if you want to display Image/ImageIcon, then the best and easiest way is to Use Labels
2/ as you mentioned Runnable{...}.start(); Swing is simple threaded and all output to GUI must be done on EDT; you have to look at Concurrency in Swing, result is that all output from BackGround Task(s) must be wrapped into invokeLater(), and if is there problem with perfomancie then into invokeAndWait()
3/ if you be switch (between JComponents)/add/delete/change Layout then you have to call revalidate() + repaint() as last lines in concrete code block
EDIT:
dirty hack would be paintImmediately()
I have read that the repaint() method is only a request, and it doesn't execute every time it's called
It consolidates multiple repaint() requests into one to be more efficient.
I believe I'm noticing the effects of this, as my spaceship tends to lag ever so slightly when I'm moving it.
Then post your SSCCE that demonstrates this problem. I suspect the problem is your code.
Regarding the solution you accepted, take a look at Charles last posting: Swing/JFrame vs AWT/Frame for rendering outside the EDT comparing Swing vs AWT solutions.
I'm porting a really old AWT game to a really naff new device.
The game has a whole bunch of things wrong with it, including a very lax approach to thread safety: the game engine is trying to draw directly onto the screen in its engine thread using a graphics context it got from the UI thread. This doesn't work on the device.
I've managed to hack it into working, by having the engine thread draw onto an off-screen buffer and then have the UI thread periodically call repaint() on the display component and the display component blitting the buffer onto the screen, but performance sucks --- not surprising given all the context switches and double buffering.
I'm not actually a particularly knowlegable AWT programmer; it's sufficiently hateful that I've avoided it up to now. But this problem --- having an engine thread want to draw onto the screen --- must be a common one. Does anyone know of any decent strategies (and preferably example code!) of how to do this in a safe manner that squeezes as much performance out of the system as possible?
(What I'd particularly like is a safe shortcut to allow the engine thread to directly render onto the screen graphics context when it feels ready to do so, and so avoid having to tell the UI thread to request a redraw. That will let me take out a whole layer of double-buffering. But I don't know whether such a thing is possible...)
This is all on PBP 1.1.2 --- yes, it's neither full Java nor honest MidP...
Using a game canvas might help. It allows the painting to be done in the game loop, so you wont need double buffering. Another more crude approach is to use paintImmediately(). It will force the gui to repaint.
//Gui
public void update(/*may want to pass the shapes to paint*/)
{
paintImmediately(this.getGrphics()); // assuming 'this' is a jpanel
}
//Game loop
public void gameLoop()
{
// collision detection etc
gui.update();
}
I have a custom UI drawn for my java application. Right now I draw the entire UI from scratch. I know for a fact some parts of the UI are static. I know I could save these static parts to an image, but will this yield a improvement in performance (Since even an image must be drawn every frame)?
Is it plausible to save a reference to the Graphics2D object after the UI has been drawn and assign that to the new graphics object every frame (starting from a point where all the static components are drawn)?
Thanks in advance,
Alan
You don't need to redraw everything in every frame. So if you have static parts of your UI (or even dynamic parts that you know haven't changed since last frame) then you simply don't need to repaint them.
In my code (Swing games and simulations mostly) I usually try to follow the following rules:
Pre-prepare static images (e.g. BufferedImage textures for UI elements)
Override the paintComponent() method for each UI element individually to do the painting
Only call the repaint() method of any given UI element if I know that something has changed
Call repaint() in a timer-based loop for animation, but only call it on the portion of the UI that is being animated (e.g. a nested JPanel)
This approach seems to work and perform pretty well (though I'd welcome comments if there are ways to improve it!!)
There are two main optimizations you can do here. The first is to make sure that when you cause your UI to be repainted, usually done by calling repaint, make sure you call the version of repaint where you specify a rectangle that has changed. Only make the rectangle big enough to encompass the parts that actually have changed, not the static parts. For this to be effective you also have to pay attention to the clipRect in the Graphics2D object you are passed in paint(). That is used by the system to tell you exactly what needs to be repainted, in the above case usually the rectangle that you passed to repaint. Don't paint anything that lies entirely outside that rectangle.
You can also get significant performance improvements by caching the static parts of your interface in an image. Writing an image is by far the fastest way of getting things onto a screen. My measurements indicate that small images are faster than even a few simple drawing primitives. However you need to make sure the image characteristics match the screen, by using createCompatibleImage().
Of course you may be using a lot of memory to get this speedup. I would recommend testing to see if you need to do image caching before implementing it.
if some parts of the screen is completely static, then never redraw that part. Don't do a full-screen/window clear, just clear the part of the screen/window that changes all the time.
This way, you don't unnecessarily redraw the static image.
I was programming a simple game and wanted to display it via the Java Graphics + Swing API. It felt a little slow, of course, so I measured how long it took to repaint, which was around 32ms. Then I read about accelerated Java graphics and used the method described here: Space Invaders
However, somehow this is even slower. It now takes around 98ms to redraw. Why is that?
Note that I am aware of libraries like LWGL and JOGL, but I don't want to use a full-blown OpenGL wrapper for such a graphically simple game.
A few hints to improve performance:
Clip and just draw the area that has changed
Don't scale anything when drawing
Use the correct buffer strategy
Use full screen exclusive mode if appropriate.
Swing isn't inherently slow. Most of the confusion about Swing comes from people who don't know or understand about the Event Dispatch Thread (and yes, it's a lot of effort to get it right).
Regarding your times, are you just calling g.drawImage between the time settings? If so, what size are you repainting, and are you doing any scaling on this call?
EDIT: Forgot to mention Pulp Core - a very nice gaming framework for Java.
Read this about timers
Use an always sleeping thread in the background to lower the system interrupt rate to get much more precise timers!
You can take a look at my code in here
A small game I've created can be found here
long renderStart = System.nanoTime()
// render stuff here
long renderTime = (System.nanoTime() - renderStart) / 1000000;
Swing will be slow for a game. Your best bet is probably an SDL wrapper, such as jsdl or sdljava. SDL is quite lightweight and supported on many platforms.
The nice thing is, well-implemented 2d graphics library wrappers implement the Graphics interface, so you should be able to try several and see which one performs best for your app without modifying your code.
I am not an expert, but Java2D supports different rendering pipelines. On my machine, switching to the OpenGL one by setting the system property
sun.java2d.opengl=true
yielded a substantial increase in rendering speed.
You may look at processing.
I agree with Nicolas, for graphics / interactivity processing is great
Try JGame. It's a simple engine for 2D games which uses GL when its available and can produce games that run on anything from a mobile phone to a desktop system.
First of all, you should check and see how much time is spent in your own code vs. how much time is spent actually drawing the frame. You may have some bug or glitch that makes it run more slowly.
You should be able to get much better performance then 98ms to draw your screen. Those game libraries will probably be using the same graphics calls as you would. But which containers you use can have a big impact on how fast things draw. I remember a couple months ago working on some double buffered graphics code, and how the back buffer was created made a huge difference in performance.
In particular, make sure you only create background image once or at least, only when your canvas resizes. In my draw code I do this:
//create a graphics backplane
if(backplane == null || !backplaneSize.equals(this.getSize())){
backplane = this.createImage((int)this.getSize().getWidth(), (int)this.getSize().getHeight());
backplaneSize = this.getSize();
}
So the back plane only gets created if it's new, or if the component is resized. This has a huge impact on speed.
I've been doing graphics programming in java since JDK 1.0. Actually I'd never heard of this BufferStrategy thing. Heh.
I've never had any trouble getting good frame rates with basic java graphics calls. Another thing to look out for is making sure that Swing isn't trying to clear the background of your component for you. That can be another source of slowdown.
Java2D performance is a bit of a dark art. It can vary between platforms, but it possible to get it to perform well.
Sometimes doing seemingly innocuous things can result in much slower performance. You want to make sure that you are using accelerated images as much as possible.
The type of the image can also be significant. I don't know the full details, but I do know that my programs were much faster using type 1 images (INT RGB) than other types such as type 5 (3BYTE BGR) because of conversion issues.
I can't tell exactly what you are doing. Your question is about Swing but you mention a Canvas and Bufferstrategy. Well in Swing you would use a JPanel which is automatically double buffered so you don't have to worry about that.
I tested a simple animation with 1000 moving balls. The Timer was set to fire every 100ms. When the Timer fired, the 1000 ball where randomly moved to a new position and a new BufferedImage was created and the panel displaying the image was repainted. The difference in time between the repainting was 110ms implying it only took 10ms to create an new BufferedImage and do the painting. The painting was done a a full screen so no clipping was done.
This posting show the example code I tested.