I'm writing an editor and I have some performance issues. The thing is when there is a lot of text (10K Lines) in the editor Swing blocks (gets really slow) the UI because there is a lot of words to highlight (re-paint/re-render).
I'm also using EDT (Event Dispatch Thread).
Does swing block UI when painting/rendering? Is there any way to optimize rendering while I type some words to the editor (like async painting etc.)?
As you already mentioned, take care to always call Swing painting operations on the event dispatching thread by using SwingUtilities.invokeLater(Runnable) or SwingUtilities.invokeAndWait(Runnable). Otherwise you'll get into trouble and have responsiveness issues that can ultimately lead to the so called 'Grey-Rect-Problem' where your frame is rendered as a grey rect and the UI does not respond anymore (keyboard, mouse events and so on).
Difference between invokeLater and invokeAndWait is that invokeLater causes the java.lang.Runnable you pass to it to be executed asynchronously on the AWT event dispatching thread. I don't know how you ensure that your painting operations are done on the EDT - so if your're not already using invokeLater try this out first.
Other than that, as a general rule for optimisation of UI performance: Always try to minimize the area that has to be repainted! E.g. by using java.awt.Component.repaint(long tm, int x, int y, int width, int height), which repaints a specific area of a UI component in between a specified time.
Maybe these links also helps:
JTextArea setText(veryLongString) is taking too much time
https://pavelfatin.com/low-latency-painting-in-awt-and-swing/
Related
I'm developing a plugin for an old application that uses the Swing UI toolkit. As far as I know, Swing's philosophy is that everything executes on the Event Dispatch Thread. I need to integrate a JPanel where it repaint()s itself every 16ms.
I think this will cause the EDT to be flooded with redrawing that JPanel and making other events wait longer, (mentioned in the Javadocs) which could slow down the app. I'm already seeing slowdowns in a test instance of the application.
The way I see it now there are 2 solutions:
Render everything to an image, then render that to the screen, this way, long computations can be rendered by a background thread and the EDT takes care of drawing a simple image, reducing latency.
Make repaint calls only when the user is actually doing something.
What is a performant way to draw dynamic, frequently-updated content inside a Swing UI without blocking the EDT and the rest of the UI?
The animation, processing changes painting complex modelling objects on a Graphics/Graphics2D can indeed be separated. Whether SwingWorker, SwingTimer, event driven.
You can use a BuffferedImage with getGraphics and at the end of Graphics2D drawing do a disposeGraphics. Take a compatible image type as the Graphics of the screen.
Then the actual painting will just do a drawImage in the paintComponent update event handling.
Log processing times, and repaints - for optimization. This profiling is crucial to knowing performance bottlenecks.
Often costly things can be improved. Like using affine transformations, preloaded images of course, and so on. Like doing light-weight drawing instead of tiny JComponent hierarchies. Like pruning invisible parts.
i' m little bit confused about few things:
Example code,that shows my problem,this isn't compilable
// image
private BufferedImage image;
private Graphics2D graphic;
private changeImage;
. . .
//thread loop
while (running) {
. . .
render();
Graphics showGraphic = getGraphics();
showGraphic.drawImage(image, 0, 0, null);
showGraphic.dispose();
}
public void render(){
if(changeImage == 1)
graphic.drawImage(ImageLoader.TREE, 0, 0, null);
else if(changeImage == 2){
graphic.drawImage(ImageLoader.HOUSE, 0, 0, null);
. . .
graphic.fillRect(50,60,30,40);
}
}
I create an global object Graphic2D and i draw things in render(), I do not call repaint() inside the game loop, is it good practice to do this?
Should I use repaint() inside my loop , and the paintComponent() function ?
one other thing, how graphic.dispose() works correctly? , because trying to remove this line of code, nothing happens.
I understand how works dispose() according to java docs, but I have not noticed any differences with dispose() or without.
my program runs very well, but i have this dubts.
is it good practice to do this
No, this is actually incredibly horrible and error prone. This assumes that this is the Component#getGraphics method. The problem is Swing uses a passive rendering algorithm, that is, Swing decides when and what should be repainted and does this for optimisation reasons.
This means updates are not regular, which really helps when doing animation, and can happen at any time for any number reasons, many of which you simply don't and can't control.
For these reasons, painting should be done within one of the paint methods, preferably, paintComponent of a JComponent based class. This way, when Swing decides to do a repaint, one, you know about it and can update the output accordingly, and two, it won't clear what you have previously painted to it using getGraphics which could cause flickering...
You are also running into potential threading issues, as you thread is trying to paint to the Graphics context, so might the Event Dispatching Thread and that isn't going to end pretty...All painting should be done from within the context of the EDT (for component based painting)
You could try using a BufferedStrategy, which would give you direct control over the painting process, but this limits you to the AWT library. Don't know if this is good or bad in your case.
one other thing, how graphic.dispose() works correctly? , because
trying to remove this line of code, nothing happens.
dispose basically releases any native resources that the Graphics context might be holding. General rule of thumb, if you didn't create, you don't dispose of it.
So if you use Graphics#create or BufferedImage#createGraphics to obtain a Graphics context, then you should call dispose when your done with it.
On some systems calling dispose on the Grpahics context used to perform painting of components (such as that passed to paintComponent or obtained from getGraphcis) can prevent further content from been painted.
Equally, not disposing of Graphics contexts that you have created can lead to memory leaks as they won't get garbage collected...
There are many questions about "good" or "correct" painting in Swing, so this question could be considered as a duplicate, and I won't try to repeat here what should be read in context, for example, in the Lesson about Performing Custom Painting.
However, to summarize the most important information short and concisely:
You should never call getGraphics on a Component. It may return null or a Graphics object that is in any other way "invalid". It will fail in the one form or the other, sooner or later
You should do all your painting operations in the paintComponent method (or in methods that are called from paintComponent), using the Graphics object that you receive there as an argument
Whether or not you call the Graphics#dispose method may not make a difference that is "immediately" visible. You should not call it on the one that you received in the paintComponent. You should only call it on a Graphics object that you either obtained from a BufferedImage, or one that you created by calling Graphics#create(). When you do not call it, this might cause a memory leak that will only be noticed after the application has been running for a while.
I'm making a game. Each object in the game is registered in a registry. I have an update and render method being called by a game loop. The render method calls repaint() on my JPanel and I have overridden paintComponent(Graphics g) for graphics. When it renders and updates, it goes through each object and updates it and renders it from the registry. Sometimes I get a concurrent modification error, and i believe it is because something is calling the paintComponent() other than from my game loop. If a JPanel or even JFrame calls repaint on its own, is there any way to disable it?
No, repaint() doesn't get called automatically, but paint(Graphics g) does at times, and you have no control over this. Also, your repaint() calls may be ignored if they get "stacked". For more on this, please check out this article: Painting in AWT and Swing.
Hopefully you have no program logic inside of your paintComponent method. If you do, get it out of there.
"paintComponent() other than from my game loop"
First, you should never be calling any paint method yourself (even printing should be done via the print methods).
Second, if you call repaint, a paint event will be scheduled by the repaint manager and (eventually) processed by the Event Dispatching Thread. When and how this is done comes down to how the repaint manager schedules these events. This means that all painting is done within the context of the EDT (unless you are doing something you shouldn't be). This means painting is never done from within the context of your game loop
This means, if your changing the state of the game that the paint routines rely on, you will have concurrent issues.
You could synchronize the shared data structures so that they can't be accessed while some other thread is accessing the data.
You could render the state of the to a backing buffer within the game thread, then assign it to a active buffer. Thus is kind of like a double buffering approach
EDIT: solved, look below for my solution.
first of all, this is my very first question here, so if I make any mistakes, please tell me.
I am trying to write a Mandelbrot Fractal Program in Java, for training purposes. The ideal for all the functionality I want to have is the Fractalizer (http://www.fractalizer.de/en/), but for now, I will be happy with a program that draws the Mandelbrot Set on the screen (instead of, for example, writing it to an image file). Of course, I want the program to be fast, so I thought that I could split the calculation into multiple threads to utilize my multi-core processor; for example, on a quad-core system, the image would be divided into 2x2=4 images, each to be calculated by a separate thread. All these threads get a Graphics object passed on which they draw the pixels as they are calculated.
My first attempt to get this working was to have the threads draw on a BufferedImage.getGraphics() and to have the paint() method constantly call repaint() as long as the image isn't finished:
g.drawImage(tempImg, 0, 0, null);
if (waiterThread.isAlive())
{
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
// do nothing
}
repaint(10);
}
(waiterThread joins all calculating threads one after another, so as long as the waiterThread is alive, at least one calculating thread is not yet finished.)
This works, but causes ugly flickering on the canvas because of the frequent repainting.
Then, by means of a small test program, I found out that Graphics.draw*anything* draws on the screen instantly, before the paint method returns, so my current approach is the following:
One Panel with a GridLayout that contains 2x2 (on a <4-core system, 1x1) MandelbrotCanvas objects
Each MandelbrotCanvas object will, on the first paint() call, initialize a calculating Thread, pass its own Graphics object to it (actually, I'm using a custom GroupGraphics class that passes one Graphics call to several graphics, to "backup" the image into a BufferedImage.getGraphics(), but that's not important), and start the calculating thread.
The panel will in its paint() method fetch the calculating threads from each of the MandelbrotCanvases and join() them.
Unfortunately, this creates only a black screen. Only when calculation is finished, the image is displayed.
What is the right way to have several threads paint onto one component?
EDIT:
What I didn't know: Only the Event Dispatch Thread is allowed to paint on AWT components (roughly spoken), which means that the last approach above can't possibly work - apparently, it's supposed to throw an exception, but I didn't get one. My solution is to use the first approach - draw the image onto a BufferedImage and draw that onto the Canvas - with the only modification that I overload the update() method to call the paint() method without clearing the painting area:
public void update(Graphics g)
{
paint(g);
}
So I guess my answer to the general question ("How do I let multiple Threads paint onto an AWT component?") would be: You can't, it's not allowed. Let the Threads draw onto a BufferedImage.getGraphics(), and draw that image repeatedly. Overload the update() method like above to avoid flickering. (It looks really great now.) Another tip that I can't use in my case, but is still good, is that there is a variant of repaint() that takes rectangle arguments to specify the area that has to be redrawn, and a variant that takes a time argument (in milliseconds), so the repaint doesn't have to happen immediately.
EDIT2: This link provides very helpful information: http://java.sun.com/products/jfc/tsc/articles/painting/index.html
Only the GUI thread can paint directly on your component.
So you must call the repaint method.
When you have background computation, to force a fast drawing, you should use the version taking a time as parameter.
Some details from here :
NOTE: If multiple calls to repaint() occur on a component before the
initial repaint request is processed, the multiple requests may be
collapsed into a single call to update(). The algorithm for
determining when multiple requests should be collapsed is
implementation-dependent. If multiple requests are collapsed, the
resulting update rectangle will be equal to the union of the
rectangles contained in the collapsed requests.
You have to send requests to the EDT.
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Rectangle r = myCurrentWorkingThread.getFinishedRectangle();
myPainter.repaint(r);
}
});
The idea is that you won't repaint pixel by pixel, rather giving larger chunks to the worker-threads. As soon as they're finished with a unit of work, they notify the main object (myPainter) that would do the actual work. This construct (EventQueue.invokeLater) will guarantee that it will be on the Event Dispatcher Thread.
How should I run animation in a Swing applet?
I have an animation thread performing active rendering and it initially animates fine. Sometimes (anywhere from 1 minute to 2 hours later) it begins to fail to update the screen and only the sounds occur. I believe this is due to the fact that the paint is not performed from the EDT causing some kind of concurrency problem.
So, should the active rendering (ie getGraphics() and painting) be called only from the EDT? A problem with this is the Swing timer lacks precision.
Or has anyone had success with active rendering without using the EDT, and completely disabling any EDT updates to the page (maybe using Canvas / or ignore repaint on a JPanel)?
You can paint graphics into your own off-screen image in another thread and copy to the screen in the EDT. But for single threaded stuff, I would hope your frame rate is high enough to be able to do it in the EDT.
A few things to look at would be to make sure you are only repainting what needs to be repainted and not the whole graphics context each time unless necessary. Also there is a timing framework that you can use to handle some animations. I don't think it is actively being developed but last time I looked at it it had some nice api's to use for animation.
Without knowing your specific use case this is all I could come up w/ off the top of my head.