|
Description
|
As discussed in this thread:
http://forums.java.net/jive/forum.jspa?forumID=69&start=0
On Windows Vista (multiple machines, video boards),
only with Aero (DWM) enabled, some swing applications
produce rendering artifacts when scrolling or
dragging internal frames. Doesn't happen if Aero
is disabled (even while the application is running)
by selecting "Vista Basic" color theme in the
Appearance dialog.
For example, run this this Substance L&F demo
(note that the look and feel doesn't matter, it's
reproducible with all L&Fs):
javaws https://substance.dev.java.net/webstart/test.jnlp
Play around with the pane on the left side -
expand/collapse the "tasks". The click at
"List" tab on the right pane, and scroll
the list. The scrolled area then becomes
black or white (depending on laf).
Similar thing happens in the "Desktop" tab - create
a new Internal frame and drag it around - it
produces similar artifacts.
This indicates that the problem is caused
by the copyArea() call.
Interestingly, if the app loses focus and then gains
it back (alt+tab x2) the repainting problems stop until
the task is collapsed/expanded at which point
they appear again.
Posted Date : 2007-11-16 01:49:14.0
|
|
Evaluation
|
This looks like a problem in Vista's DWM (since it's video board-agnostic),
but I'm not sure why is it triggered by the JXTaskPane.
Posted Date : 2007-11-16 01:51:03.0
OK, I've investigated this a bit more.
This is an issue with DWM, but it is caused by our code.
Essentially it is caused by doing D3D and GDI rendering to the
same window which is a bad idea on Vista - it's pretty
much broken.
Unfortunately in some situations Swing allows rendering directly
to the screen - which is what happens in this case.
The JXCollapsiblePane widget does some animation on collasing/expanding.
As part of that animation it apparently first paints the component to
an off-screen image. This triggers a special code path in
BufferStrategyPaintManager:
at javax.swing.BufferStrategyPaintManager$BufferInfo.setInSync(BufferStrategyPaintManager.java:719)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:293)
at javax.swing.RepaintManager.paint(RepaintManager.java:1172)
at javax.swing.JComponent.paint(JComponent.java:1014)
at org.jdesktop.swingx.JXCollapsiblePane$WrapperContainer.makeImage(JXCollapsiblePane.java:826)
at org.jdesktop.swingx.JXCollapsiblePane$WrapperContainer.showImage(JXCollapsiblePane.java:795)
at org.jdesktop.swingx.JXCollapsiblePane$AnimationListener.reinit(JXCollapsiblePane.java:770)
at org.jdesktop.swingx.JXCollapsiblePane.setCollapsed(JXCollapsiblePane.java:475)
at org.jdesktop.swingx.JXTaskPane.setExpanded(JXTaskPane.java:386)
at org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI$ToggleListener.mouseReleased(BasicTaskPaneUI.java:367)
at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:272)
The setInSync() indicates to the paint manager that the back-buffer is
no longer in sync with what's on the screen. After this all copyAreas
get redirected to the screen instead of the back-buffer (why only copyAreas?).
It's not clear why doing painting to another offscreen surface
necessarily indicates an out of sync back-buffer though.
Now, the D3D pipeline does special handling for rendering to
the screen - it redirects all rendering to a dedicated back-buffer,
a sort of "fake window", which is presented to the screen when
needed. That way we can still accelerated on-screen rendering using
Direct3D (direct on-screen rendering is not possible with Direct3D 9).
However, this special handling is disabled for windows which have their
own BufferStrategy - which is the case here, since Swing uses BufferStrategy
as a back-buffer when it is in "bufferPerWindow" mode - aka the "gray rect fix",
or "true double-buffering".
Because of that the rendering goes directly to the screen, so the
copy area is performed using GDICopyArea. It works fine XP, but
produces artifacts on Vista with Aero.
Looking at the latest code for JXCollapsiblePane it looks like it
no longer uses this approach for doing their animation, so it could
be that this particular case will no longer trigger the issue, but the
underlying problem is still there of course.
Since all circumstances under which Swing may switch from
buffer per window mode to the "regular" one, or allow on-screen
rendering, are not clear it doesn't seem likely that we could
fix swing's "buffer out of sync" cases.
One solution would be to use the old painting mode - where
Swing's back-buffer is a VolatileImage which is copied to the
screen - in this case the D3D pipeline's on-screen rendering
support will ensure that one never renders directly to the
screen.
Since Aero is already double-buffered (which in particular
means that applications don't get 'expose' events when another
window is moved over their window) the 'true double-buffering'
doesn't really give any advantages.
More investigation is needed.
Note: it is possible that this is the cause of other
issues on Vista like the one described here:
http://forums.java.net/jive/thread.jspa?threadID=32535&tstart=0
It looks especially plausible since disabling buffer per window
also helped there.
Posted Date : 2007-12-12 21:59:15.0
The solution we're working on is disabling Swing's
"true double-buffering" mode on Vista with DWM(Aero) enabled.
From the code review:
With buffer per window disabled Swing will use VolatileImage
as back-buffer and that will allow the D3D pipeline
to guarantee that no on-screen rendering will occur since
our mechanism will be working.
Another reason is that since Aero already does double-buffering
there's no point in wasting resources, the application
doesn't get expose events anyway.
This solution is also less risky than
changing the way Swing paints on all platforms.
Notes about the implementation: while the state of DWM
compositing can change during the lifetime of the
application it is an extremely rare event, and is
in most cases temporary (some app turns it off, then
re-enables it). So if it was turned on when we started
we'll detect it and not use buffer per window.
If they disable it during the runtime - no big deal,
everything will look sucky (flashing) anyway and we'll
be no exception.
If we find that we do need to handle runtime changes -
they're easy to handle, just add a listener mechanism
to Win32GraphicsEnv (espose through some shared interface),
and have BufferStrategyPaintManager listen to those
events and disable bpw if needed.
Posted Date : 2007-12-14 23:18:58.0
|