United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-6345743 : Vista DWM disabled when Toolkit.sync() is called

Submit Date:
Updated Date:
Project Name:
Resolved Date:
Affected Versions:
Fixed Versions:

Related Reports

Sub Tasks

The next version of Windows, Vista, uses a "Desktop Window Manager" (DWM) to combine the windows on the screen in such a way that effects like tranclucent title bars exist automatically.  This is done through using Direct3D textures as the surfaces that applications render to (instead of actual windows on the screen).

Operations that talk directly to the screen defeat this mechanism because the DWM cannot guarantee that the offscreen version of the desktop and windows will coincide exactly with the pixels on the screen.  Examples of such operations include using DirectDraw to lock the primary surface (which returns a pointer to the onscreen pixel buffer) and calling GDI's GetDC(NULL) (which returns an HDC which could be used to draw directly onto the screen).

When the DWM detects that such an operation is occurring, it disables itself and Vista renders in a more XP-like way.  The effect to the user is a momentary flash of the desktop and all windows are repainted with opaque title bars.  Thereafter (for the life of the offending process), all windows lack the translucency and warping effects that the DWM would typically use.  Once the process exits, there is another flash/repaint and the DWM is back in action.

In pre-Mustang releases, we performed operations such as these at startup time (for example, our initialization of DirectX would Lock/Unlock the primary surface just to test its validity), which causes the DWM to punt as soon as a Java app is started.

As of Mustang, we no longer perform such operations at startup.  This means that you can run a desktop Java app that looks good on Vista; title bars are still translucent and the DWM is still operational.  However, we still may execute such operations upon demand internally.

One example is Toolkit.sync().  In 1.4.*, we added the capability to sync() to flush the DirectX pipeline, which is necessary to avoid buffering up DirectX requests faster than they can be handled by the graphics card.  One effective way to flush these commands is to Lock/Unlock the primary surface, so that's the way we made it work.  In a later tweak, we limited the lock to a small region of the screen (to avoid flashing artifacts with various GDI rendering operations), but we're still taking a lock on the onscreen pixels.
This causes the DWM to punt for any Java application that calls sync().

In Windows Vista (I used build 5231, but presumably any version with DWM will behave the same) run the attached application, SyncTester.  There are two buttons in the window.  Ignore the "AWT Frame" button (I added that just to make sure that other functionality did not force a DWM punt).  Click on the "sync" button and watch the flash/repaint artifact occur.  We've cause the DWM to punt because we've called Lock/Unlock on the desktop.



I modified the test case only render to the offscreen
surface - meaning no blit from offscreen surface (back-buffer) to the screen.

So instead of a typical loop
  blt offscreen surface (sprite) to back-buffer
  blt back-buffer to the screen
I did  
  blt offscreen surface (sprite) to back-buffer
(see the changes to the test - a new "-noblt" parameter)

And example of where we could run into this is J2DBench: 
we sync and take the timing when rendering to offscreen surface w/o
copying them to the screen.

This proved to be an interesting experiment. It showed that
we do need to touch the sync surface prior to locking to get
more consistent results on Vista.

Here's a table I came up with after running a bunch of 
tests on my Vista system:
         |         with WDM         |       without WDM        |
         |  onscr | offscr |off only|  onscr | offscr |off only|
ddraw    |        |        |        |        |        |        |
 touch   | 212/30 | 172/21 | 262/40 | 205/25 | 328/55 | 522/104|
 no touch| 230/35 | 189/20 | Inf/Nan| 210/27 | 374/58 | 645/215|
d3d      |        |        |        |        |        |        |
 touch   | ---/-- | 299/53 | 780/800| ---/-- | 505/106|1028/791|
 no touch| ---/-- | 358/69 | Inf/Nan| ---/-- | 650/152| Inf/NaN|

With/Without WDM - with WDM enabled or disabled
touch/no touch - run version with/without touching the offscreen sync surface
onscr:  offscreen -> screen + sync
offscr: offscreen -> offscreen surface(backbuffer) -> screen + sync
off only: offscreen -> offscreen + sync

The numbers:
XXX/YYY : average FPS was XXX with Standard Deviation YYY

As you can see the numbers are all screwed up if we don't touch
the sync surface. Also, it appears that the Display manager has some
weird effects on performance.

Interestingly, I get similar effect on Windows XP: the "offscreen only"
experiment's numbers are all over, often with Infinity/Nan as FPS/StD .

So we have a similar issues there even now when we lock the primary surface.
Unfortunately, trying to use the same code path we have for Vista on
Windows XP didn't yield the same results: the numbers were still out of
whack mostly. So it looks that just taking this new code and using it on
XP won't work. More investigation required for this part.

But the fix we'll use for Vista is to create a sync surface, and render to it
prior to locking for each sync.

It appears that creating an offscreen surface which we lock instead
of primary works well. We don't even need to Blt to it, just lock/unlock.

For example, running (a slightly modified) SyncTest.java on Vista and XP
produces the following results. This is a standard deviation 
for the frames per second with different locking techniques. The lower
the deviation the better.
                No Sync  | GDI sync |Lock Primary  |Lock Offscreen  |
Vista                43  |       42 |            61|              39|
ATI Mobility             |          |              |                |
Radeon 9600              |          |              |                |
                         |          |              |                |
XP                   47  |       39 |             4|               5|
Nvidia                   |          |              |                |
GF FX5200                |          |              |                |
                         |          |              |                |
XP                   19  |       22 |            24|              20|
Intel i852               |          |              |                |

The intel and ati boards were on notebooks may be that could 
explain weird results.

I'll run some more tests before committing to the fix.

Disabling ddraw would fix this - if we are not using ddraw then we do not call DDraw's Lock function on the primary during Toolkit.sync().

There is still no clear way to "flush" all DirectX commands.  However, according to Microsoft, it may be good enough to either:
1) flush the surface we really care about
2) flush any surface that we've just rendered to

(1) seems like the better approach.  If we can figure out whether there is a main offscreen surface that we're dealing with (such as the Swing back buffer), then we could call Lock/Unlock on that surface which should call all operations to that surface (and potentiall to all DX surfaces) to flush first.

Failing (1), we could allocate a small surface that we use just for this purpose.
Whenever we want to sync(), we call Blit (say, of a 1x1 colorfill) to that surface then
call Lock/Unlock on that surface.  At a minimum, the GPU would have to flush that Blit operation prior to Locking the surface.  But hopefully (depending on how clever the drivers and hardware are) it would actually flush all of DirectX prior to the Lock call.

Hardware and Software, Engineered to Work Together