The Source for Java Technology Collaboration
User: Password:



Scott Violet

Scott Violet's Blog

Swing Painting Improvements: No More Gray Rect!

Posted by zixle on April 18, 2005 at 06:37 AM | Comments (63)

In scoping out various performance related projects for mustang we wanted to tackle one of Swing's long standing problem areas that has contributed to bad perceived performance. That is, when a Swing based app is exposed after being hidden by another application there is a noticeable delay between when the background of the window is erased and when the actual contents are painted. We've come to call this the 'gray rect' problem. I'm happy to report that with the promotion of mustang build 32 this bug (4967886) has been fixed! Download it now and give us feedback!

The fix involved adding true double buffering support to Swing. That is, each window will now have an off-screen image that is kept in sync with the on-screen image. When a window is exposed we copy directly from the off-screen image, on the toolkit thread, to the screen. That's it! Your application will no longer see paint events in this scenario!

I've been running various Netbeans 4.1 builds for a while now with this fix and while subtle, it makes a big difference! I no longer see the Netbeans window flash when alt-tab'ing back and forth between Netbeans and other apps. Painting of exposed windows is nearly instantaneous.

An added bonus of this fix is that if your application is blocking the event dispatch thread, say you're contacting a server and a user hides your window and exposes it the app will still paint. Nice!

This fix is the most substantial change to Swing's painting architecture since pre 1.0. I'll have a follow on article that discusses the changes in more details. Until then, download the beta, kick the tires and give us feedback! We're not only interested in feedback on this change, but other areas that effect runtime performance you feel we should be focusing on.

For the hackers that want to see the Swing side of the code changes grab the latest source from the JDK source snapshot and look in javax.swing.BufferStrategyPaintManager, javax.swing.SwingPaintEventDispatcher and javax.swing.RepaintManager.

The fine print. For compatibility sake there are a handful of situations that will trigger turning off true double buffering. Most notably if you install your own RepaintManager. Additionally, true double buffering is currently only enabled on Windows. The code works on all platforms, but we have as yet to enable it everywhere. Yes, we're evaluating that now!


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • If you fixed this; then it will be easier to fix the other problem with blank backgrounds; a JFrame.setBackground(Color c) not actually changing the background color after the frame has been shown the first time. I can imagine the double buffer to be cleared to the right background color now, fixing this long standing bug.

    ps. will you add a method to the repaint Manager indicating it supports double buffering? So extending repaint managers will not disable this by default?

    Posted by: zander on April 18, 2005 at 07:01 AM

  • Not that I've ever really noticed this bug, but why can't we have the fix in the next minor release of 1.5? Or are we allowed to fix it ourselves in 1.5 and distribute the fixed VM?

    Posted by: ricky_clarkson on April 18, 2005 at 08:04 AM

  • say you're contacting a server and a user hides your window and exposes it the app will still paint.

    That's HUGE. This is the largest cause of perceived slowness as lots of apps incorrectly do long-running tasks inside the event dispatch thread.

    For non-Swing folks here's an example: userA selects a menu to perform an operation in the event dispatch thread. The program is written badly (all too common) and doesn't use SwingWorker (or equivalent) to do the operation in the background. Swing (until this latest beta release) will not repaint exposed regions until the operation is finished. This means Swing will appear hung; it won't respond to your mouse and if you move other windows on top of your Swing app and then move them away Swing won't redraw your app (blank grey rectangles are left).

    This is going to do a lot for perceived slowness.

    Cheers.

    Posted by: markswanson on April 18, 2005 at 08:19 AM

  • Thanks *a lot* !

    Posted by: gfx on April 18, 2005 at 09:04 AM

  • Thanks for fixing this one. GOing to d/l the new build now.

    Posted by: ronaldyang on April 18, 2005 at 10:01 AM

  • So is it a very very bad news for SWT? I would think so... Swing is the Java way to go!

    Posted by: ludo on April 18, 2005 at 10:32 AM

  • This sounds great, I'm downlolading the build right now to see it. ricky, yes, you could merge these changes into Java 5.0 and redistribute it with the new Sun licenses.

    Posted by: keithkml on April 18, 2005 at 10:35 AM

  • Believe me, I would love to see this backported. But I can't see this happening for two reasons:
    • It contains new public API, something we don't backport.
    • The fix was trully enormous! It touched close to 60 files!
    In other words it's too much and too risky to go back to an update release.

    Posted by: zixle on April 18, 2005 at 01:01 PM

  • In response to markswanson and to this part of Scott's article:

    "if your application is blocking the event dispatch thread, say you're contacting a server ..."
    This is the wrong thing to do!

    I'm sure you guys know this, but it has to be pointed out to other readers who may not be aware:
    You should not be doing any long-running work on the Event Despatch Thread.
    By "long-running" here we mean more than about 250 millis.
    Anything longer than that should be shifted off onto another thread to allow the Event Despatch Thread to continue receiving and dispatching events and repainting the screen.

    IMO, if you do put long-running tasks on the Event Despatch Thread, this fix will not make your application be more responsive.
    The only difference would be that, instead of seeing a grey rect when the user ALT-Tabs, they will instead see a picture of your GUI that doesn't respond at all when clicked on.
    This won't make people think your app is responsive.
    It'll make them think it's crashed.

    This is a great enhancement for situations like the one Scott pointed out where ALT-Tabbing back to NetBeans (or IntelliJ or <insert any Swing-based app with a large memory footprint>) now won't show the grey rect for a split second before repainting.
    But it is not a fix for, nor an excuse to start having long-running tasks on the Event Despatch Thread.

    Graham.

    Posted by: grlea on April 18, 2005 at 06:13 PM

  • This is excellent! It also appears to have made netbeans menu painting delays smaller resulting in smoother menu repaints.

    Posted by: jdolphin on April 18, 2005 at 11:16 PM

  • Just downloaded and tested a bit, and it seems to work really well. I guess we can call this the first flying car in Mustang ;-)

    Posted by: jansan on April 19, 2005 at 03:48 AM

    • It is an excellent bugfix...! You can drag windows over an other without an glitch!
    • The second "flying car" that we would like to have is the "Cleartype" antialiasing. Think about to give this capability in Win9x, Win2000! It would be a punch for Microsoft. If Sun doesn't have enough human resources for implementing this, it can buy it from other companies!

    Posted by: dtrehas on April 19, 2005 at 04:46 AM

  • Subpixel rendering, or LCD text, or Cleartype (pick your term) is currently under way for 1.6. The RFE for it is 4726365.

    Posted by: zixle on April 19, 2005 at 05:36 AM

  • If subpixel anti-aliasing makes it into Mustang, I'll invite all the Swing team to have a beer this summer :)

    Posted by: gfx on April 19, 2005 at 06:52 AM

  • While the Swing team would certainly love a beer, we have very little to do with the actual implementation. 99.9% of the work is on the 2D side of things.

    Posted by: zixle on April 19, 2005 at 06:57 AM

  • Er ok, it seems this internship will cost me a lot :)

    Posted by: gfx on April 19, 2005 at 08:26 AM

  • You wouldn't believe what a difference this makes in the overall perception of application performance! On a side note, the tool bar buttons in Netbeans are oversized and overspaced when I run with 1.6.0.

    Posted by: rabbe on April 19, 2005 at 09:02 AM

  • Graham, you completely misunderstood my post. I said that it is bad to do long running tasks in the event thread, then you say "NO! that is wrong! You should not do long running tasks in the event thread" - which is exactly what I said in the first place. I believe you are being disingenuous.

    I also believe it is disingenuous of you to imply I was suggesting it was a fix or an excuse when I was implying no such thing.

    I stand by my post. It looks better to see the app redrawn properly than to see grey rectangles, and I feel a lot of people who don't know any better will perceive this as improved performance.

    Posted by: markswanson on April 19, 2005 at 09:18 AM

  • The toolbar button problem is a known issue in Netbeans and I believe it's been fixed in the weekly builds.

    Posted by: zixle on April 19, 2005 at 09:58 AM

  • Will this bugfix be patched to JDK 1.5 or even 1.4?

    Posted by: visualparadigm on April 19, 2005 at 10:28 AM

  • This fix doesn't seem to make any difference on Linux. I'm running fedora3 and I see the same 'gray rect' problem with jdk1.5 and jdk1.6. Should this also work on Linux? Kees.

    Posted by: keeskuip on April 19, 2005 at 10:50 AM

  • Read the entry more carefully :
    Additionally, true double buffering is currently only enabled on Windows. The code works on all platforms, but we have as yet to enable it everywhere. Yes, we're evaluating that now!
    :))

    Posted by: gfx on April 19, 2005 at 05:50 PM

  • Mark,

    Sorry about referencing your post from mine.
    I may have read your post quicker than I should and, though I did note you pointing out that doing long-running tasks on the event thread is "incorrect", after your conclusion that "This is going to do a lot for perceived slowness", I had the impression that you had the impression that this change would give a good impression even when people do things "incorrectly".

    The bulk of my post, though, was concerned with the quoted phrase from the original blog, not your post, and I was writing with a mind to inform the general public rather than to criticise yourself or Scott.
    In retrospect, it was incorrect to address my post the way I did, and had I left out the first line:
    "In response to markswanson and to this part of Scott's article: "
    there would have been no confusion at all about my purpose.

    Lastly, I wasn't trying to infer that you, or anyone, had suggested this was a fix or an excuse for long-running tasks on the event thread.
    I was simply saying "this is not a fix or an excuse" in case the people you referred to who "don't know any better" didn't realise that.

    Sorry for any confusion or offence.

    Graham.

    Posted by: grlea on April 19, 2005 at 06:02 PM

  • Graham,

    Peace. It's big of you to explain. I'll try harder to take more time and be clearer in the future.

    It's ironic because ultimately I agree 100% about the bulk of your post. :-)

    Cheers.

    Posted by: markswanson on April 19, 2005 at 06:33 PM

  • Will this big refactoring affect Swing testing tools like Foxtrot or Spin?

    Posted by: shorn on April 19, 2005 at 08:13 PM

  • Alejuja!!!

    Posted by: tiom on April 19, 2005 at 11:20 PM

  • I know it's currently only available on Windows, but is the plan to automatically disable this on systems that do window compositing, like OSX and Longhorn? Mind you, by the time Longhorn gets here.... :-)

    Posted by: dgriffiths on April 20, 2005 at 01:01 AM

  • Another *very* nice thing - JTabbedPane's tabs can now be any (J)Component. Finally option to add small (X) button for closing a tab :)

    Posted by: kirillcool on April 20, 2005 at 01:45 AM

  • It would be great to see this fix in older releases too ! Not everybody can use Mustang right now......

    Posted by: lianril on April 20, 2005 at 01:54 AM

  • The other flying car we need is: Please fix the performance and the L&F of JFileChooser on Windows

    Posted by: jdolphin on April 20, 2005 at 02:22 AM

  • > Will this big refactoring affect Swing testing tools like Foxtrot or Spin?

    I'm not aware of any dependancies these two libraries have on painting, so to my knowledge they should not be effected. That said, I haven't verified this claim.

    > but is the plan to automatically disable this on systems that do window compositing, like OSX and Longhorn?

    I don't know enough about the OSX implementation to definitively say. I don't believe this change will effect them in anyway, meaning they most likely won't do the same thing we're doing on Windows. The change may make their actual implementation easier in that swing painting code now has clear start/end entry points.

    As to Longhorn, we're evaluating what the appropriate rendering pipeline should be and it's too early to say.

    > Please fix the performance and the L&F of JFileChooser on Windows

    Do you have any specifics we should be looking at?

    -Scott

    Posted by: zixle on April 20, 2005 at 08:22 AM

  • > Please fix the performance and the L&F of JFileChooser on Windows Do you have any specifics we should be looking at? -Scott
    Hi Scott Opening folders is much slower with the WinXP L&F than either the Metal L&F or with the native windows file chooser. This is *really* obvious when I open folders with a large number of files. I have a test you can do for yourself to be sure... Create about 9000 files (with a mixture of file extensions .png, .txt, .java, .html) in a test folder on your local disk. Open this folder using the Metal L&F JFileChooser (with "all files" as the filter). On my pc (3.4GHz 1Gig RAM) this takes 2 seconds. If I open SwingSet again, switch to the WinXP L&F and open the same folder with JFileChooser (with "all files" as the filter) it takes about 22 seconds! The same test on the native Windows XP file chooser takes about 2 seconds. The difference it smaller but still very noticeable for larger smaller folders. Could you please have a look at this for Mustang.

    Posted by: jdolphin on April 20, 2005 at 11:21 PM

  • > Please fix the performance and the L&F of JFileChooser on Windows Do you have any specifics we should be looking at? -Scott
    As for the windows L&F the following issues are the most obvious: Right click menus in JFileChooser are completely different to the right click menus in the native windows file chooser. You cannot select which columns should be visible in detail view or sort. The highlighting of selected items both in the file list and the 'look in' combo is much wider than the native impl. and the icons do not change color as you highlight an item. My recent documents appears in the native file chooser, but not in Swing. I cannot browse to e.g. \\123.456.7.8 which is a computer with shares on our Windows network (the filechooser thinks I have entered the name of a file and exits). Another minor issue is the resize marker missing in the bottom right of the Swing impl. I would really appreciate it if these could also be fixed for Mustang. thanks

    Posted by: jdolphin on April 20, 2005 at 11:31 PM

  • Or if you don't have the resources/time to fix the above JFileChooser bugs why not simply add multiple file selection and file filtering to the AWT FileDialog? I don't need to imbed or customize the file chooser anyway.

    Posted by: jdolphin on April 21, 2005 at 03:07 AM

  • THIS IS AWESOME GUYS/GALS. WAY TO GO !!!!! Very Nicely done. I tested this on Windows 2K with SwingSet and I also set: java.awt.Toolkit.getDefaultToolkit().setDynamicLayout(true); So now it appears that dynamically repainting of windows when they resize performs much better also. Is there any chance in making the .setDynamicLayout(true); now that the appearance of repainting of Windows is so much Better. Thanks Deane Richan Xito Project

    Posted by: drichan on April 21, 2005 at 10:26 AM

  • There are a number of filechooser performance related bugs, and we're hoping to make some headway there soon!

    As to turning on dynamic layout by default, we're evaluating that too. Surprisingly it's not as straigthforward as you would think.

    -Scott

    Posted by: zixle on April 22, 2005 at 04:51 PM

  • I would love to see the AWT Filedialog include filtering capabilites because the JFileChooser is still way behind the native Windows filechooser. It's impossible to do "Arrange icons by" which is very useful. Including this will drive Java more and more in the desktop.

    Posted by: carcour on April 22, 2005 at 07:48 PM

  • Thanks for the update. There is one other problem. I am wondering if it is (will be/was) fixed. When a dialog is displayed, the dialog frame is diplayed empty first, and contents are displayed later. This results in flickering. In a prior post you mentioned that the team was working on this issues. What is the status of this issue?

    Posted by: sermetyucel on April 22, 2005 at 10:04 PM

  • I'm delighted that this problem has been solved. I need it already. It tool some doing to get Java 1.4.2_8 and 1.5.0_2 installed for production use by two users. Unfortunately, the first user spent a good long while staring at a LARGE blank gray rectangle. It's D-day, and we're on the beach... This needs to be in 1.5.0_3 Please find a way Mike Rainville

    Posted by: mike__rainville on April 23, 2005 at 10:08 AM

  • I AM using SwingWorker wherever I do anything... Perhaps a compile time warning could be devised, e.g. assert @NOT_EVENT_RELATED // add SwingWorker to this, if necessary assert @EVENT_RELATED javac -showEventCodeTransitions

    Posted by: mike__rainville on April 23, 2005 at 10:13 AM

  • Please do NOT turn on dynamic layout by default in Mustang. If you do, there must be a simple way to disable it for certain windows, otherwise you will inevitable create new performance problems with some applications, adding a lot of fuel to the "Swing is slow" fire. I would prefer to have dynamic layout disabled by default, and a simple way to switch it on for individual windows. This way you stay consistent with old JRE version, and people who enjoy dynamic layout can enable it in their applications if they want to. Please, no such blunders for Mustang.

    Posted by: jansan on April 25, 2005 at 08:27 AM

  • great news :-D

    Posted by: zcrar70 on April 25, 2005 at 09:31 AM

  • I've been using development version of NetBeans with latest Mustang... and I second your comment about the difference. I've found out the grey rect issue was actually how I've found I've switched to a Java application. I think this fix has huge psychological effect. I'm also happy about the subpixel rendering, although it has sense in my opinion on bigger fonts (at least 15 pt), smaller sizes are just to small for AA. But I know lots of people who use AA on smaller fonts as well.

    Posted by: romanstrobl on June 11, 2005 at 01:26 AM

  • Hi In our application, we would like to set the default grey background to black. Is this possible in Mustang? Regards Keith

    Posted by: keithcsl on June 22, 2005 at 03:37 AM

  • Certainly. If you set the background color of your opaque components to Color.BLACK, you should get what you want?
    -Scott

    Posted by: zixle on June 22, 2005 at 08:14 AM

  • Thanks for that Scott. I tried setting the opaque components to Color.black but on an intensive operation, it still shows the gray color (slight flicker). After more debugging, I realised that it was the component's parent's background that was painted first. So, I have set all parent components to also have a black color. Everything is now working great! Thanks.

    Posted by: keithcsl on June 23, 2005 at 05:40 AM

  • I have components that do a lot of optimized drawing using getGraphics().copyArea() or getTopLevelAncestor().getGraphics().copyArea() where possible (for cut, paste, etc. in addition to scrolling). With the new double buffering, that doesn't work, since the area won't be copied in the buffer (covering and exposing such a window will show old buffer contents). Is there any way I can get that to work again, without using a lot of reflection to call non-public methods? Can we get something public like JComponent.copyArea() for this purpose?

    Posted by: lbarowski on August 03, 2005 at 03:40 AM

  • lbarowski,
    The best option for now is to disable true double buffering. That can be done by installing your own RepaintManager. That is:
    RepaintManager.setCurrentManager(new RepaintManager());
    
    Is enough to turn off true double buffering.
    I'll give though to the copyArea issue, that's a bit trickier as to whether or not we want to expose that.
    Thanks for the feedback!
    -Scott

    Posted by: zixle on August 03, 2005 at 08:34 AM

  • I'll give though to the copyArea issue, that's a bit trickier as to whether or not we want to expose that.

    Yes, I see that the mechanisms there have become quite complex. Without it though, it will be difficult for custom components to do really efficient repainting while taking advantage of the new double buffering. Maybe it would be easier to provide this through JViewport, since that's where the guts of the scrolling code are now. That would be good enough for me.

    Posted by: lbarowski on August 03, 2005 at 11:42 AM

  • I'm also facing the same issue with copyArea. OS repaints events are not propogated to the paint methods. Is there anyway that the RepaintManager could accept repaint event listeners? This would allow the application to more control over when to invalidate the component and update the buffer.

    Posted by: 876 on October 19, 2005 at 09:31 AM

  • Great work guys..!
    There is hope though regarding the gray rect. problem, I ran across a work around in Java developers journal. You still get flicker, but it is better than the gray rect. problem
    FrameResizer by Phil Herold
    - Carl

    Posted by: carldea on October 23, 2005 at 07:54 PM

  • Putting aside the way things get painted, it would be better to solve the gray rect problem with repainting the new contents directly over what was there before, when another window was in foreground (as it can be done in native Windows apps). This way "new" app can read other app graphics - downside, and "garbage" can be left if not cleared manually. Is the off-screen image updated then the app sits all the time in background, and is periodically updated (eg. by setText() or repainted)? If not, a flicker is still likely.

    Posted by: joansmithdev on January 10, 2006 at 10:36 PM

  • All 3 strategies would optimally be available in Swing for heavyweight components: gray rect, double-buffering (memory expensive!) and no-cleanup. I hope NO gray rect is drawn in jdk

    Posted by: joansmithdev on January 10, 2006 at 11:12 PM

  • 'm also facing the same issue with copyArea.
    What do you need beyond what JViewport gives you? Do you have an idea as to what the API you would like consists of?

    Thanks,
       -Scott

    Posted by: zixle on January 17, 2006 at 10:46 AM

  • joansmithdev,
    it would be better to solve the gray rect problem with repainting the new contents directly over what was there before
    This is actually what we do now. When the host OS notifies us we need to repaint we do not fill in the background, instead copy the updated buffer into the location.

    Is the off-screen image updated then the app sits all the time in background, and is periodically updated (eg. by setText() or repainted)?
    Any time you paint, you paint to the offscreen image. As such, the offscreen image is always in sync with the contents on the screen. To service a native paint event it's then just a matter of copying from the offscreen image to the screen.

       -Scott

    Posted by: zixle on January 17, 2006 at 10:50 AM

  • All 3 strategies would optimally be available in Swing for heavyweight components: gray rect, double-buffering (memory expensive!) and no-cleanup.
    Good idea, I've filed bug 6373358 on this. It'll be visible on the JDC shortly.

        -Scott

    Posted by: zixle on January 17, 2006 at 10:53 AM

  • What do you need beyond what JViewport gives you? Do you have an idea as to what the API you would like consists of?

    Since it will be a replacement for Graphics.copyArea(), it should have the same parameters (int x, int y, int width, int height, int dx, int dy). It should copy the screen area, as Graphics.copyArea() does now, and do the same for the off-screen image (if present). I suppose a parameter could be added for copying only the screen area, off-screen image, or both. I can't think of a use for that offhand, but I suppose there may be one.

    Posted by: lbarowski on January 23, 2006 at 10:24 PM

  • ... Of course, I suppose that if an off-screen image is present, copying the area within the off-screen image then copying the target rectangle to the screen may be more efficient.

    Posted by: lbarowski on January 23, 2006 at 10:32 PM

  • What do you need beyond what JViewport gives you? Do you have an idea as to what the API you would like consists of?

    The biggest problem related to this issue is that:
    1) copyArea does not behave the same way as in 1.5. copyArea could be used reliably with JComponent default settings.
    2) copyArea in can no longer be reliably used since obstructed copyAreas (eg window on top) do not propagate screen invalidation to the application.

    As for JViewport... I have been having the following issues with it:
    1) It is very difficult to extend JViewport. You can override the computeBlit but you can not control what & how the component is painted. To me both these operations go hand in hand. If you override the paint operation Jviewport is a complete rewrite.
    2) JViewport has poor diagonal scrolling performance
    3) JViewport has poor scrolling performance when anything is overlayed. Example if a translucent overlay occurs at the boundary of a JViewport, copyArea can still be used for the areas not covered by the translucent overlay. This results in using a smaller area for copyArea and a larger repaint area. However copyArea can still used for a significant part of the screen update.

    As for an API... I would nice if the new DB rendering algorithm could keep count on how many copyAreas has been executed since the last paint. If copyArea ops have been executed since the last paint then the DB support is by-passed and the paint event is propagated to the application like it was in 1.5.

    An event API to receive the blocked paint event could be a work around if the copyArea problem can not be automatically handled by J2SE.

    Posted by: ftsiang on March 12, 2006 at 06:34 PM

  • We have an application running on JRE1.4.2. 1. How can we incorporate this fix to existing application like this? 2. Is it patched to JDK 1.4?

    Posted by: jobinesh on May 23, 2006 at 07:30 AM

  • jobinesh,

    We have an application running on JRE1.4.2. 1. How can we incorporate this fix to existing application like this? 2. Is it patched to JDK 1.4?

    The fix is only available in mustang, 1.6. It is too large a fix, and one that requires API changes, to backport.

        -Scott

    Posted by: zixle on May 31, 2006 at 04:30 PM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds