JDK-6648018 : PIT: D3D: Copying VolatileImage to the window of small size causes an infinite validation loop
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 6u10
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2008-01-08
  • Updated: 2010-10-14
  • Resolved: 2008-01-22
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6
6u10 b11Fixed
Related Reports
Relates :  
Description
I am rendering a volatile image on to a frame and validating it within the paint method of the frame. When the frame appears, try doing any of the following -

1. Open display settings and change the color depth to 16 bit. 
2. resize the frame inward so that frame becomes as small as 20x20.

The above operations make the validation loop run infinitely. This is reproducible only on 6u10-b10 PIT build and not reproducible on b09. Not reproducible on earlier releases (ddraw, d3d on jdk6-fcs) and hence a regression in D3D. I reproduced it on WinXP, WinVista Home Premium with ATI as well as Nvidia video boards. 

Run the test -  VolatileFlushTest_new.java. When the frame appears, reduce the size of the frame to 20x20 or lesser. You will see the infinite loop on the console.

(The same issue can be reproduced with another test also - VolatileFlushTestCanvas).

Comments
SUGGESTED FIX http://sa.sfbay.sun.com/projects/java2d_data/6u10/6648018.0
15-01-2008

EVALUATION A note from the code review request: There is a bunch of smaller changes sprinkled around (like null, instanceof checks) - those are to fix smaller issues I found while testing the main fix. I'll explain one change in particular: D3DScreenUpdateManager.java:243 - synchronized is removed in createGraphics(). The reason is that I have ran into a deadlock while testing: ============================= "D3D Screen Updater": waiting to lock monitor 0x0aacd3d4 (object 0x02f1c548, a sun.java2d.d3d.D3DScreenUpdateManager), which is held by "AWT-EventQueue-0" "AWT-EventQueue-0": waiting for ownable synchronizer 0x02ec9b80, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "D3D Screen Updater" Java stack information for the threads listed above: =================================================== "D3D Screen Updater": at sun.java2d.d3d.D3DScreenUpdateManager.trackScreenSurface(D3DScreenUpdateManager.java:286) - waiting to lock <0x02f1c548> (a sun.java2d.d3d.D3DScreenUpdateManager) at sun.java2d.d3d.D3DScreenUpdateManager.getReplacementScreenSurface(D3DScreenUpdateManager.java:315) at sun.java2d.d3d.D3DSurfaceData$D3DWindowSurfaceData.getReplacement(D3DSurfaceData.java:773) at sun.java2d.SunGraphics2D.revalidateAll(SunGraphics2D.java:2284) at sun.java2d.SunGraphics2D.getCompClip(SunGraphics2D.java:468) at sun.java2d.d3d.D3DRenderer.validateContext(D3DRenderer.java:32) at sun.java2d.pipe.BufferedRenderPipe.fillRect(BufferedRenderPipe.java:100) at sun.java2d.d3d.D3DRenderer$Tracer.fillRect(D3DRenderer.java:95) at sun.java2d.pipe.ValidatePipe.fillRect(ValidatePipe.java:58) at sun.java2d.SunGraphics2D.fillRect(SunGraphics2D.java:2264) at sun.java2d.d3d.D3DScreenUpdateManager.validate(D3DScreenUpdateManager.java:482) at sun.java2d.d3d.D3DScreenUpdateManager.run(D3DScreenUpdateManager.java:452) at java.lang.Thread.run(Thread.java:619) "AWT-EventQueue-0": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x02ec9b80> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:747) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:778) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1114) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262) at sun.awt.SunToolkit.awtLock(SunToolkit.java:245) at sun.java2d.pipe.RenderQueue.lock(RenderQueue.java:94) at sun.java2d.d3d.D3DSurfaceData.initSurface(D3DSurfaceData.java:356) at sun.java2d.d3d.D3DSurfaceData.restoreSurface(D3DSurfaceData.java:746) at sun.java2d.d3d.D3DSurfaceData$D3DWindowSurfaceData.restoreSurface(D3DSurfaceData.java:791) at sun.java2d.d3d.D3DScreenUpdateManager.r.validate(D3DScreenUpdateManager.java:477) at sun.java2d.d3d.D3DScreenUpdateManager.createGraphics(D3DScreenUpdateManager.java:250) ... at java.awt.EventQueue.dispatchEvent(EventQueue.java:599) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:284) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161) at java.awt.EventDispatchThread.run(EventDispatchThread.java:122) It deadlocked because we first take the object lock (since the method is synchronized), then call validate() which may take RQ lock, and on other threads we have a reversed order. The validate() call was added recently there and synchronized should have been removed then. We should only synchronize on the d3dscreenupdate manager when dealing with its data, and make sure we never take any locks while holding its lock.
14-01-2008

EVALUATION The bug is caused by the fix for 6596702: D3D: intermittent repainting issues in SwingSet after restoring from fs mode In particular, the part in D3DBlitLoop where we mark the source surface lost if VI is copied to a screen surface. If the screen surface can not be replaced by a onscreen d3d surface we'll spin in the validation loop forever. Which does happen if the window is too small for the d3d onscreen rendering mechanism to work, or say there's a buffer strategy created for this window. The reason the original change was made was to avoid disabling hw acceleration for the source VI during transient stages of surface restoration (say when a display change occurs, or any other event which caused the onscreen d3d surface to become lost and not restored). We would still like to have this implemented, so the following changes have been made: - during a D3D->GDI blit, we check if the destination peer can ever have an accelerated on-screen surface - if that's not the case (the window is too small or whatever) the hw acceleration in the source image is disabled - if indeed the destination surface can become accelerated then we assume the condition is temporary and mark the source as lost with the intention that a backup surface from the VolatileSurfaceManager is used while the onscreen surface is sorted out - in order to make that happen we need to delay restoration of the source surface (because otherwise it will be restored on the next vi.validate() and if the destination is still lost we'll spin in a loop again). This is achieved by introducing a 'restore countdown' - the restoration of the source surface is delayed by a few iterations, thus allowing the backup surface to be used in the meantime. Once the countdown expires the source surface will be restored and we'll attempt to copy it to the screen again. - another wrinkle to this is that D3DVolatileSurfaceManager used to override contentsLost() to return the status of the accelerated surface instead of the internal flag set by the validate() call as it is the case in the superclass. This again forced us into a possibly infinite loop without a chance to use the backup surface. The override was done in an attempt to make sure that contentsLost() always returns up to date information (since the surface can be lost between validate() and contentsLost). This need to be changed back to the way it was done in the old pipeline: when the surface is lost it should notify the surface manager by calling SurfaceManager.acceleratedSurfaceLost(), which sets the appropriate 'contents lost' flag. Then contentsLost() will return true, we'll loop around for validate(), which will attempt to restore the accelerated surface, but since we have a restore countdown in place, restoration will fail, and the backup surface will be used. - another issue discovered during testing was with the D3DScreenUpdateManager which keeps track of surfaces for which there are Graphics contexts. When a graphics context is created by the peer we add the surface to the tracking list and start the update thread responsible for flipping it (and restoring if it's lost). However, if the such surface was replaced because of display change event or resize it is no longer tracked by the manager (as it is dropped from the list before the new surface is set for the peer). This is a problem because if there's an outstanding graphics object which has the old (now invalidated) tracked surface, attempts to use the surface will lead to InvalidPipeException and Graphics revalidation. The revalidation will install a replacement surface obtained from the old surface via SurfaceData.getReplacement() - in this case the surface will be replaced by the new surface in the peer. So now we'll have a Graphics object which has an accelerated onscreen surface, but this surface is not being tracked becaus when this graphics object was created it had a different surface. This means that the surface won't be flipped/restored by the manager. In order to fix this the getReplacement() will need to add the new surface to the tracked list. Moreover, even the GDI surface will need to do the same: it could be that originally we couldn't create an accelerated surface so we handled out a GDI one. But then it got invalidated and replaced with the accelerated one, so getReplacement() will put an accelerated surface into the Graphics - again, it will be untracked unless we add it to the tracking list at the time of replacement. The way this is handled now is that ScreenUpdateManager now has a new getReplacement(peer) method which both D3D and GDI SurfaceDatas will call. The default ScreenUpdateManager will just return the current peer's surface. The D3DScreenUpdateManager override�� this method to add the new surface to the tracked list. - there were also a few other minor issues I ran into that needed to be addressed
10-01-2008