While debugging a severe performance problem with one app, I noticed that our RepaintManager validates far too much. I observed how the RI behaves and found that: - only components with a validateRoot are validated - only components that are displayable and visible and that have visible parents are validated - Components inside CellRendererPane are never validated (makes sense somehow, as CellRendererPane have no layout anyway and need ultra-fast handling of layout and repaint)
I fixed the RepaintManager, which gives all layout-related operations a
nice boost. Especially component creation is much faster now (Swing
components get revalidated quite often during initialization, because
many properties get set by the UI).
While I didn't notice any regressions from this, I think it's possible
that layout is broken in some corner cases, because we previously
layouted things in a brute force -like manner, and now perform much less
layouting. Please drop me a line if you observe problems.
2006-06-19 Roman Kennke <[EMAIL PROTECTED]>
* javax/swing/RepaintManager.java
(addInvalidComponent): Only add component that are displayable,
that have displayable parents and that have a validateRoot.
Also, don't validate components that have a CellRendererPane
ancestor.
/Roman
--
“Improvement makes straight roads, but the crooked roads, without
Improvement, are roads of Genius.” - William Blake
Index: javax/swing/RepaintManager.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/RepaintManager.java,v
retrieving revision 1.40
diff -u -1 -0 -r1.40 RepaintManager.java
--- javax/swing/RepaintManager.java 15 Jun 2006 13:42:31 -0000 1.40
+++ javax/swing/RepaintManager.java 19 Jun 2006 09:43:28 -0000
@@ -31,20 +31,21 @@
independent module, the terms and conditions of the license of that
module. An independent module is a module which is not derived from
or based on this library. If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
package javax.swing;
+import java.applet.Applet;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.image.VolatileImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -70,26 +71,20 @@
* @author Audrius Meskauskas ([EMAIL PROTECTED])
*/
public class RepaintManager
{
/**
* The current repaint managers, indexed by their ThreadGroups.
*/
static WeakHashMap currentRepaintManagers;
/**
- * Used to disable merging of regions in commitBuffer(). This has caused
- * problems and may either need to be reworked or removed.
- */
- private static final boolean MERGE_REGIONS = false;
-
- /**
* A rectangle object to be reused in damaged regions calculation.
*/
private static Rectangle rectCache = new Rectangle();
/**
* <p>A helper class which is placed into the system event queue at
* various times in order to facilitate repainting and layout. There is
* typically only one of these objects active at any time. When the
* [EMAIL PROTECTED] RepaintManager} is told to queue a repaint, it checks to see if
* a [EMAIL PROTECTED] RepaintWorker} is "live" in the system event queue, and if
@@ -312,40 +307,59 @@
* Add a component to the [EMAIL PROTECTED] #invalidComponents} vector. If the
* [EMAIL PROTECTED] #repaintWorker} class is not active, insert it in the system
* event queue.
*
* @param component The component to add
*
* @see #removeInvalidComponent
*/
public void addInvalidComponent(JComponent component)
{
- Component ancestor = component;
+ Component validateRoot = null;
+ Component c = component;
+ while (c != null)
+ {
+ // Special cases we don't bother validating are when the invalidated
+ // component (or any of it's ancestors) is inside a CellRendererPane
+ // or if it doesn't have a peer yet (== not displayable).
+ if (c instanceof CellRendererPane || c.getPeer() == null)
+ return;
+ if (c instanceof JComponent && ((JComponent) c).isValidateRoot())
+ {
+ validateRoot = c;
+ break;
+ }
- while (ancestor != null
- && (! (ancestor instanceof JComponent)
- || ! ((JComponent) ancestor).isValidateRoot() ))
- ancestor = ancestor.getParent();
-
- if (ancestor != null
- && ancestor instanceof JComponent
- && ((JComponent) ancestor).isValidateRoot())
- component = (JComponent) ancestor;
+ c = c.getParent();
+ }
- if (invalidComponents.contains(component))
+ // If we didn't find a validate root, then we don't validate.
+ if (validateRoot == null)
return;
- synchronized (invalidComponents)
+ // Make sure the validate root and all of it's ancestors are visible.
+ c = validateRoot;
+ while (c != null)
{
- invalidComponents.add(component);
+ if (! c.isVisible() || c.getPeer() == null)
+ return;
+ c = c.getParent();
}
+ if (invalidComponents.contains(validateRoot))
+ return;
+
+ //synchronized (invalidComponents)
+ // {
+ invalidComponents.add(validateRoot);
+ // }
+
if (! repaintWorker.isLive())
{
repaintWorker.setLive(true);
SwingUtilities.invokeLater(repaintWorker);
}
}
/**
* Remove a component from the [EMAIL PROTECTED] #invalidComponents} vector.
*
@@ -377,21 +391,21 @@
* @see #getDirtyRegion
* @see #isCompletelyDirty
* @see #markCompletelyClean
* @see #markCompletelyDirty
*/
public void addDirtyRegion(JComponent component, int x, int y,
int w, int h)
{
if (w <= 0 || h <= 0 || !component.isShowing())
return;
-
+
Component parent = component.getParent();
component.computeVisibleRect(rectCache);
SwingUtilities.computeIntersection(x, y, w, h, rectCache);
if (! rectCache.isEmpty())
{
if (dirtyComponents.containsKey(component))
{
SwingUtilities.computeUnion(rectCache.x, rectCache.y,
@@ -631,21 +645,21 @@
{
int width = Math.max(proposedWidth, root.getWidth());
width = Math.min(doubleBufferMaximumSize.width, width);
int height = Math.max(proposedHeight, root.getHeight());
height = Math.min(doubleBufferMaximumSize.height, height);
buffer = component.createImage(width, height);
offscreenBuffers.put(root, buffer);
}
return buffer;
}
-
+
/**
* Blits the back buffer of the specified root component to the screen. If
* the RepaintManager is currently working on a paint request, the commit
* requests are queued up and committed at once when the paint request is
* done (by [EMAIL PROTECTED] #commitRemainingBuffers}). This is package private because
* it must get called by JComponent.
*
* @param comp the component to be painted
* @param area the area to paint on screen, in comp coordinates
*/
@@ -659,21 +673,21 @@
Component root = getHeavyweightParent(comp);
// FIXME: Optimize this.
Rectangle rootRect = SwingUtilities.convertRectangle(comp, area, root);
// We synchronize on dirtyComponents here because that is what
// paintDirtyRegions also synchronizes on while painting.
synchronized (dirtyComponents)
{
// If the RepaintManager is not currently painting, then directly
// blit the requested buffer on the screen.
- if (! MERGE_REGIONS || ! repaintUnderway)
+ if (true || ! repaintUnderway)
{
blitBuffer(root, rootRect);
}
// Otherwise queue this request up, until all the RepaintManager work
// is done.
else
{
if (commitRequests.containsKey(root))
SwingUtilities.computeUnion(rootRect.x, rootRect.y,
@@ -690,35 +704,38 @@
* not necessarily the component with which the offscreen buffer is
* associated. The offscreen buffers are always allocated for the toplevel
* windows. However, painted is performed on lower-level heavyweight
* components too, if they contain Swing components.
*
* @param root the heavyweight component to blit upon
* @param rootRect the rectangle in the root component's coordinate space
*/
private void blitBuffer(Component root, Rectangle rootRect)
{
+ if (! root.isShowing())
+ return;
+
// Find the Window from which we use the backbuffer.
Component bufferRoot = root;
Rectangle bufferRect = rootRect.getBounds();
if (!(bufferRoot instanceof Window))
{
bufferRoot = SwingUtilities.getWindowAncestor(bufferRoot);
SwingUtilities.convertRectangleToAncestor(root, rootRect, bufferRoot);
}
Graphics g = root.getGraphics();
Image buffer = (Image) offscreenBuffers.get(bufferRoot);
// Make sure we have a sane clip at this point.
g.clipRect(rootRect.x, rootRect.y, rootRect.width, rootRect.height);
- g.drawImage(buffer, bufferRect.x - rootRect.x, bufferRect.y - rootRect.y,
+ g.drawImage(buffer, rootRect.x - bufferRect.x, rootRect.y - bufferRect.y,
root);
g.dispose();
}
/**
* Finds and returns the nearest heavyweight parent for the specified
* component. If the component isn't contained inside a heavyweight parent,
* this returns null.
*
signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil
