JDK-6670586 : D3D: accelerate embedded applets on Vista (DWM)
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 6u10,7u3
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: windows_vista,windows_7
  • CPU: x86
  • Submitted: 2008-03-03
  • Updated: 2014-03-11
  • Resolved: 2014-03-11
Related Reports
Duplicate :  
Duplicate :  
Relates :  
ImageMap applet fails to repaint, display properly on Vista using jre 6u10-b12.  The same scenario works fine using jre 6u5-fcs (b13) on the same machines. 

Tested OS:  32-bit Windows Vista- Business
       Browser: FF IE 7
       jre:   6u5-fcs/ 6u10-b12

The problem occurs with using 6u10-b12 classic Plug-in on FF IE 7 and the new Plug-in on IE 7

Steps to reproduce:
1) Install promoted jre 6u10-b12 on 32-bit Vista
2) Browse http://java.sun.com/applets/jdk/1.4/demo/applets/ImageMap/example1.html
3) The ImageMap applet should be loaded. Using the browser vertical scrollbar to scroll up and down.

The image fails to repaint and display properly. At some points during the scrolling period, the image isn't displayed at all. This problem doesn't occur in 6u5.

P4 and 7-pool as vista only

EVALUATION I have changed the synopsis to clarify the situation. This bug no longer tracks the flickering issue in 6u10 (this one went away) but the fact that embedded applets aren't hw accelerated on Vista with DWM enabled. The resolution of this bug is pending on when Microsoft comes up with the fix for DWM.

EVALUATION As mentioned above, this bug is no longer reproducible in 6u10 but only because we put in a work around which disables hw acceleration for embedded frames (note that off-screen acceleration is still enabled, but there would be no way of copying that accelerated offscreen surface to the screen).

EVALUATION An update. The issue had been filed with Microsoft. It is apparently a known bug - if GDI operations are performed on the parent window (like ScrollDC) the children (even clipped) which do D3D will not show up correctly. At this point we'll have to stick with the workaround which is to disable hw acceleration for applets inside the browser on Vista with Aero enabled (covered by this bug fix: 6675297). Once Microsoft fixes the problem we'll re-evaluate - providing there's a way to detect the fix and turn off the work around. Alternatively we'll have to require certain OS patch level for the end users.

EVALUATION Things got even more interesting once I have started testing in the browsers. IE (I tested 7) and FF (v3 b5) behave differently. Even with -Dsun.awt.noerasebackground=true we get horrible flickering while scrolling, and on IE the window doesn't even get painted unless obscured by the edge of the frame or screen. It looks like this: once the Applet window cleares windows' edge, it gets painted once, and then it just gets cleared to the standard window color (verified by setting some arbitrary color in Window Appearance dialog) not to ever repaint again unless the Applet itself causes a repaint - then it repaints fine. So it appears that somehow DWM clears our window after we render to it. It looks like somebody somewhere does some GDI activity on our window which causes DWM to freak out since the rest of the rendering to this window goes through GDI. None of this is reproducible on XP or on Vista if DWM is disabled. It is not reproducible if Java2D's Direct3D pipeline is disabled - there's no flickering at all. I have tried to locate the responsible party - whoever does something GDI-ish to our window. With -Dsun.awt.noerasebackground=true I have guaranteed that AWT doesn't clear the background using GDI, and Java2D only does D3D, so it must be something else. The new plugin's AxControl window does call BeginPaint/EndPaint but shouldn't matter since it's a parent window of ours, and it does have the WS_CLIPCHILDREN style so in theory it could not have affected our stuff. Just to make sure I have created another child window inside our toplevel (a Canvas inside the Applet) - and still got the same exact effect. Just to be sure, I have replaced BeginPaint/EndPaint with ValidateRgn in WM_PAINT handler - doesn't help. The Window classes for AWT windows do not set the background brush with registering the class (hbrBackground is NULL when calling ::RegisterClass) so it is not that. The window hierarchy in the browser (at least, with the new plugin) looks something like this: .. - Shell Doc Object View (IE's window) (0) - Internet Explorer_Server (1) - Java Plug-in Control Window (2) - SunAwtFrame (3) - SunAwtCanvas (4) All of these windows have WS_CLIPCHILDREN style set, so even if one of the parent windows did use gdi to clear their backgrounds (for example: windows 0, 1, 2 set the their background brushes in their window class) clearing in theory should not have produced the clearing I'm seeing. I have verified that if I do do ::FillRect with some odd color in the WM_PAINT handler of window 2, whatever clears widnows 3,4 is rendered on top of 2 - as expected. Since in theory rendering GDI and D3D to different windows within the same hierarchy should be supported: http://blogs.msdn.com/greg_schechter/archive/2006/05/02/588934.aspx I think this is some kind of DWM bug, and we'll need to take this issue to Microsoft. Note that while the behavior is slightly different in FF3 - the window does get repainted after it's cleared, it is only so because FF sends a WM_PAINT every time the browser window is scrolled. I'm not sure if this is intentional or accidental.

EVALUATION The problem is much harder to fix than it looked. First, the background erasing happens on the Toolkit thread, which is the Rendering thread for the D3D pipeline which means that we can not take the render queue (or any other) locks. This means that we can't just create a graphics object to use for clearing- it may and will take the RQ lock (when creating or restoring the on-screen surface). This could be worked around by creating a graphics object when the canvas is first created and caching it. The D3DScreenUpdateManager will update the surfaceData in the graphics to be replaced when needed. Another problem is that the on-screen surface flipping happens on another thread (the screen updater thread) and we can not synchroniusly flush it from the toolkit thread, unless we do a crude hack and flip the surface ourselves - which may lead to issues (what if the surface was just restored and the update manager didn't have time to clear it - we'll flip it with some garbage). So if we can not do synch flipping then the bg clearing may happen some time (up to 100ms) later, which may cause artifacts. Yet another issue is that the area to be cleared may not be a rectangle, but a complex region. So if we were to use the 2D rendering code for clearing we'll need to get the clip region and convert it to a shape which can be set to the graphics object. That's not too bad, even if I had to trip over a few strange (to me) issues (why would ::GetClipBox(dc) return REGION_COMPLEX, indicating a complex clip region, but ::GetClipRgn(dc) return an empty region? I had to use ::GetUpdateRegion instead). Even when this is done, there's another obstacle. When the window is resized, the newly exposed regions need to be cleared. But the 'on-screen' surface will not be of the right size to cover the exposed area - the surface update happens some time later. So we still see garbage in the newly exposed area during resize. One would think that the expose events don't happen often on Vista with DWM compositing enabled. This is not necessarily so. For example, if the window is moved partly out of the screen and then brought back, the area which was outside of the screen is receives expose events. Why, why??? Supposedly the window is cached in a DWM's texture, the window location shouldn't matter - it still has all the pixels! Oh well. Another case is scrolling. If a hw component is scrolled (like a Canvas in a ScrollPane), it receives expose events and the newly exposed area must be cleared, which is fine. Suppose we're scrolling a canvas 300x300 and scroll it through a viewport of 50x50. The d3d surface for onscreen rendering to the canvas will be 300x300, and when it is flipped it sometimes appears to render outside of the window. The area is quickly repaired by the DWM manager, but it is still easy to see. This is clearly a DWM bug - this should have been clipped. (BTW, some of this happens even w/o the d3d - the scrollbars sometimes become black when scrolling) Anyway, at this point it appears that it will be very hard to come up with a robust solution. We could certainly restrict the fix to Vista with DWM enabled, but so far I haven't been able to find a satisfactory solution (especially for resizing). We could have some kind of hybrid - use GDI clearing for resizing and d3d for normal expose events, in hope that resizing and further creation of the d3d swap chain for this window resets DWM's machinery so that when the resizing is complete and we start rendering with d3d it doesn't cause issues.

EVALUATION The problem is reproducible only on Vista with DWM (Aero) enabled. I have also seen complete failure to repaint the whole IE browser window - there was some garbage from the background (outside of the applet window) - that must have been IE bug. Note that even with the D3D pipeline disabled I see a lot of flickering in the ImageMap applet (when resizing the page, or causing the repaint by any other means), and occasional scrolling artifacts. The most likely cause for the repainting issues is the use of D3D and GDI on the same window, which is broken^H^H^H^H^H^H prohibited on Vista. There's at least one place in the AWT code where GDI's ::FillRect is used on the canvas - when the canvas' background is cleared before repaint. There's a flag which disables this behavior: -Dsun.awtnoerasebackground=true . With this flag set the repainting issues pretty much disappear - at least to the same level as with the d3d pipeline disabled. There's a couple of ways of fixing this: either disable erasing the background for windows to which we render with d3d - this may cause some compatibility issues, or make awt go through Java2D painting code instead of calling ::FillRect directly. The latter will be trickier since the FillRect happens on the toolkit thread in response to the "erase background" event, and with the D3D pipeline the Toolkit thread is the rendering thread, so we must take care not to take the RQ lock - in the same way it is done in D3DSurfaceData.swapBuffers - by using trylock. Even with this fix in place there may be some issues. In particular, if an application uses BufferStrategy for active rendering we do not support rendering to the screen with d3d (we have a mechanism for redirecting on-screen rendering - which is impossible with d3d9 - to an off-screen d3d surface, but it is disabled in cases like this), so the fillRect call will end up a GDI operation directly to the window. Note that for Swing applications we already disable background erase both on resize and "erase bg" events.

WORK AROUND Use the following java2d runtime property: -Dsun.java2d.d3d=false

EVALUATION This problem can be workaround by setting the following java2d property to false: -Dsun.java2d.d3d=false Much improvements can be seen with the above setting with the old java plugin with either IE7 or FF2 on Vista. With the new java plugin with IE7, the repaint occurs with the above property set but flickering can be seen sometimes. *** (#1 of 1): [ UNSAVED ] ###@###.###