aboutsummaryrefslogtreecommitdiff
path: root/src/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java')
-rw-r--r--src/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java552
1 files changed, 552 insertions, 0 deletions
diff --git a/src/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java b/src/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java
new file mode 100644
index 000000000..b3b0fa8d5
--- /dev/null
+++ b/src/windows/classes/sun/java2d/d3d/D3DScreenUpdateManager.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.java2d.d3d;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.Window;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashMap;
+import sun.awt.SunToolkit;
+import sun.awt.Win32GraphicsConfig;
+import sun.awt.windows.WComponentPeer;
+import sun.java2d.InvalidPipeException;
+import sun.java2d.ScreenUpdateManager;
+import sun.java2d.SunGraphics2D;
+import sun.java2d.SurfaceData;
+import sun.java2d.windows.GDIWindowSurfaceData;
+import sun.java2d.d3d.D3DSurfaceData.D3DWindowSurfaceData;
+import sun.java2d.windows.WindowsFlags;
+
+/**
+ * This class handles rendering to the screen with the D3D pipeline.
+ *
+ * Since it is not possible to render directly to the front buffer
+ * with D3D9, we create a swap chain surface (with COPY effect) in place of the
+ * GDIWindowSurfaceData. A background thread handles the swap chain flips.
+ *
+ * There are some restrictions to which windows we would use this for.
+ * @see #createScreenSurface()
+ */
+public class D3DScreenUpdateManager extends ScreenUpdateManager
+ implements Runnable
+{
+ /**
+ * A window must be at least MIN_WIN_SIZE in one or both dimensions
+ * to be considered for the update manager.
+ */
+ private static final int MIN_WIN_SIZE = 150;
+
+ private volatile boolean done;
+ private volatile Thread screenUpdater;
+ private boolean needsUpdateNow;
+
+ /**
+ * Object used by the screen updater thread for waiting
+ */
+ private Object runLock = new Object();
+ /**
+ * List of D3DWindowSurfaceData surfaces. Surfaces are added to the
+ * list when a graphics object is created, and removed when the surface
+ * is invalidated.
+ */
+ private ArrayList<D3DWindowSurfaceData> d3dwSurfaces;
+ /**
+ * Cache of GDIWindowSurfaceData surfaces corresponding to the
+ * D3DWindowSurfaceData surfaces. Surfaces are added to the list when
+ * a d3dw surface is lost and could not be restored (due to lack of vram,
+ * for example), and removed then the d3dw surface is invalidated.
+ */
+ private HashMap<D3DWindowSurfaceData, GDIWindowSurfaceData> gdiSurfaces;
+
+ public D3DScreenUpdateManager() {
+ done = false;
+ AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public Object run() {
+ ThreadGroup currentTG =
+ Thread.currentThread().getThreadGroup();
+ ThreadGroup parentTG = currentTG.getParent();
+ while (parentTG != null) {
+ currentTG = parentTG;
+ parentTG = currentTG.getParent();
+ }
+ try {
+ Runtime.getRuntime().addShutdownHook(
+ new Thread(currentTG,
+ new Runnable() {
+ public void run() {
+ done = true;
+ wakeUpUpdateThread();
+ }
+ }
+ )
+ );
+ } catch (Exception e) {
+ done = true;
+ }
+ return null;
+ }
+ }
+ );
+ }
+
+ /**
+ * If possible, creates a D3DWindowSurfaceData (which is actually
+ * a back-buffer surface). If the creation fails, returns GDI
+ * onscreen surface instead.
+ *
+ * Note that the created D3D surface does not initialize the native
+ * resources (and is marked lost) to avoid wasting video memory. It is
+ * restored when a graphics object is requested from the peer.
+ *
+ * Note that this method is called from a synchronized block in
+ * WComponentPeer, so we don't need to synchronize
+ *
+ * Note that we only create a substibute d3dw surface if certain conditions
+ * are met
+ * <ul>
+ * <li>the fake d3d rendering on screen is not disabled via flag
+ * <li>d3d on the device is enabled
+ * <li>surface is larger than MIN_WIN_SIZE (don't bother for smaller ones)
+ * <li>it doesn't have a backBuffer for a BufferStrategy already
+ * <li>the peer is either Canvas, Panel, Window, Frame,
+ * Dialog or EmbeddedFrame
+ * </ul>
+ *
+ * @param gc GraphicsConfiguration on associated with the surface
+ * @param peer peer for which the surface is to be created
+ * @param bbNum number of back-buffers requested. if this number is >0,
+ * method returns GDI surface (we don't want to have two swap chains)
+ * @param isResize whether this surface is being created in response to
+ * a component resize event. This determines whether a repaint event will
+ * be issued after a surface is created: it will be if <code>isResize</code>
+ * is <code>true</code>.
+ * @return surface data to be use for onscreen rendering
+ */
+ @Override
+ public SurfaceData createScreenSurface(Win32GraphicsConfig gc,
+ WComponentPeer peer,
+ int bbNum, boolean isResize)
+ {
+ if (done || !(gc instanceof D3DGraphicsConfig)) {
+ return super.createScreenSurface(gc, peer, bbNum, isResize);
+ }
+
+ SurfaceData sd = null;
+
+ if (canUseD3DOnScreen(peer, gc, bbNum)) {
+ try {
+ // note that the created surface will be in the "lost"
+ // state, it will be restored prior to rendering to it
+ // for the first time. This is done so that vram is not
+ // wasted for surfaces never rendered to
+ sd = D3DSurfaceData.createData(peer);
+ } catch (InvalidPipeException ipe) {
+ sd = null;
+ }
+ }
+ if (sd == null) {
+ sd = GDIWindowSurfaceData.createData(peer);
+ // note that we do not add this surface to the list of cached gdi
+ // surfaces as there's no d3dw surface to associate it with;
+ // this peer will have a gdi surface until next time a surface
+ // will need to be replaced
+ }
+
+ if (isResize) {
+ // since we'd potentially replaced the back-buffer surface
+ // (either with another bb, or a gdi one), the
+ // component will need to be completely repainted;
+ // this only need to be done when the surface is created in
+ // response to a resize event since when a component is created it
+ // will be repainted anyway
+ repaintPeerTarget(peer);
+ }
+
+ return sd;
+ }
+
+ /**
+ * Determines if we can use a d3d surface for onscreen rendering for this
+ * peer.
+ * We only create onscreen d3d surfaces if the following conditions are met:
+ * - d3d is enabled on this device and onscreen emulation is enabled
+ * - window is big enough to bother (either dimension > MIN_WIN_SIZE)
+ * - this heavyweight doesn't have a BufferStrategy
+ * - if we are in full-screen mode then it must be the peer of the
+ * full-screen window (since there could be only one SwapChain in fs)
+ * and it must not have any heavyweight children
+ * (as Present() doesn't respect component clipping in fullscreen mode)
+ * - it's one of the classes likely to have custom rendering worth
+ * accelerating
+ *
+ * @returns true if we can use a d3d surface for this peer's onscreen
+ * rendering
+ */
+ public static boolean canUseD3DOnScreen(final WComponentPeer peer,
+ final Win32GraphicsConfig gc,
+ final int bbNum)
+ {
+ if (!(gc instanceof D3DGraphicsConfig)) {
+ return false;
+ }
+ D3DGraphicsConfig d3dgc = (D3DGraphicsConfig)gc;
+ D3DGraphicsDevice d3dgd = d3dgc.getD3DDevice();
+ String peerName = peer.getClass().getName();
+ Rectangle r = peer.getBounds();
+ Component target = (Component)peer.getTarget();
+ Window fsw = d3dgd.getFullScreenWindow();
+
+ return
+ WindowsFlags.isD3DOnScreenEnabled() &&
+ d3dgd.isD3DEnabledOnDevice() &&
+ peer.isAccelCapable() &&
+ (r.width > MIN_WIN_SIZE || r.height > MIN_WIN_SIZE) &&
+ bbNum == 0 &&
+ (fsw == null || (fsw == target && !hasHWChildren(target))) &&
+ (peerName.equals("sun.awt.windows.WCanvasPeer") ||
+ peerName.equals("sun.awt.windows.WDialogPeer") ||
+ peerName.equals("sun.awt.windows.WPanelPeer") ||
+ peerName.equals("sun.awt.windows.WWindowPeer") ||
+ peerName.equals("sun.awt.windows.WFramePeer") ||
+ peerName.equals("sun.awt.windows.WEmbeddedFramePeer"));
+ }
+
+ /**
+ * Creates a graphics object for the passed in surface data. If
+ * the surface is lost, it is restored.
+ * If the surface wasn't lost or the restoration was successful
+ * the surface is added to the list of maintained surfaces
+ * (if it hasn't been already).
+ *
+ * If the updater thread hasn't been created yet , it will be created and
+ * started.
+ *
+ * @param sd surface data for which to create SunGraphics2D
+ * @param peer peer associated with the surface data
+ * @param fgColor fg color to be used in graphics
+ * @param bgColor bg color to be used in graphics
+ * @param font font to be used in graphics
+ * @return a SunGraphics2D object for the surface (or for temp GDI
+ * surface data)
+ */
+ @Override
+ public Graphics2D createGraphics(SurfaceData sd,
+ WComponentPeer peer, Color fgColor, Color bgColor, Font font)
+ {
+ if (!done && sd instanceof D3DWindowSurfaceData) {
+ D3DWindowSurfaceData d3dw = (D3DWindowSurfaceData)sd;
+ if (!d3dw.isSurfaceLost() || validate(d3dw)) {
+ trackScreenSurface(d3dw);
+ return new SunGraphics2D(sd, fgColor, bgColor, font);
+ }
+ // could not restore the d3dw surface, use the cached gdi surface
+ // instead for this graphics object; note that we do not track
+ // this new gdi surface, it is only used for this graphics
+ // object
+ sd = getGdiSurface(d3dw);
+ }
+ return super.createGraphics(sd, peer, fgColor, bgColor, font);
+ }
+
+ /**
+ * Posts a repaint event for the peer's target to the EDT
+ * @param peer for which target's the repaint should be issued
+ */
+ private void repaintPeerTarget(WComponentPeer peer) {
+ // we don't want to call user code on our priveleged
+ // thread, delegate to EDT
+ final Component target = (Component)peer.getTarget();
+ SunToolkit.executeOnEventHandlerThread(target, new Runnable() {
+ public void run() {
+ target.repaint();
+ }
+ });
+ }
+
+ /**
+ * Adds a surface to the list of tracked surfaces.
+ *
+ * @param d3dw the surface to be added
+ */
+ private void trackScreenSurface(SurfaceData sd) {
+ if (!done && sd instanceof D3DWindowSurfaceData) {
+ synchronized (this) {
+ if (d3dwSurfaces == null) {
+ d3dwSurfaces = new ArrayList<D3DWindowSurfaceData>();
+ }
+ D3DWindowSurfaceData d3dw = (D3DWindowSurfaceData)sd;
+ if (!d3dwSurfaces.contains(d3dw)) {
+ d3dwSurfaces.add(d3dw);
+ }
+ }
+ startUpdateThread();
+ }
+ }
+
+ @Override
+ public synchronized void dropScreenSurface(SurfaceData sd) {
+ if (d3dwSurfaces != null && sd instanceof D3DWindowSurfaceData) {
+ D3DWindowSurfaceData d3dw = (D3DWindowSurfaceData)sd;
+ removeGdiSurface(d3dw);
+ d3dwSurfaces.remove(d3dw);
+ }
+ }
+
+ @Override
+ public SurfaceData getReplacementScreenSurface(WComponentPeer peer,
+ SurfaceData sd)
+ {
+ SurfaceData newSurface = super.getReplacementScreenSurface(peer, sd);
+ // if some outstanding graphics context wants to get a replacement we
+ // need to make sure that the new surface (if it is accelerated) is
+ // being tracked
+ trackScreenSurface(newSurface);
+ return newSurface;
+ }
+
+ /**
+ * Remove the gdi surface corresponding to the passed d3dw surface
+ * from list of the cached gdi surfaces.
+ *
+ * @param d3dw surface for which associated gdi surface is to be removed
+ */
+ private void removeGdiSurface(final D3DWindowSurfaceData d3dw) {
+ if (gdiSurfaces != null) {
+ GDIWindowSurfaceData gdisd = gdiSurfaces.get(d3dw);
+ if (gdisd != null) {
+ gdisd.invalidate();
+ gdiSurfaces.remove(d3dw);
+ }
+ }
+ }
+
+ /**
+ * If the update thread hasn't yet been created, it will be;
+ * otherwise it is awaken
+ */
+ private synchronized void startUpdateThread() {
+ if (screenUpdater == null) {
+ screenUpdater = (Thread)java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Object run() {
+ ThreadGroup tg =
+ Thread.currentThread().getThreadGroup();
+ for (ThreadGroup tgn = tg;
+ tgn != null; tg = tgn, tgn = tg.getParent());
+ Thread t = new Thread(tg, D3DScreenUpdateManager.this,
+ "D3D Screen Updater");
+ // REMIND: should it be higher?
+ t.setPriority(Thread.NORM_PRIORITY + 2);
+ t.setDaemon(true);
+ return t;
+ }
+ });
+ screenUpdater.start();
+ } else {
+ wakeUpUpdateThread();
+ }
+ }
+
+ /**
+ * Wakes up the screen updater thread.
+ *
+ * This method is not synchronous, it doesn't wait
+ * for the updater thread to complete the updates.
+ *
+ * It should be used when it is not necessary to wait for the
+ * completion, for example, when a new surface had been added
+ * to the list of tracked surfaces (which means that it's about
+ * to be rendered to).
+ */
+ public void wakeUpUpdateThread() {
+ synchronized (runLock) {
+ runLock.notifyAll();
+ }
+ }
+
+ /**
+ * Wakes up the screen updater thread and waits for the completion
+ * of the update.
+ *
+ * This method is called from Toolkit.sync() or
+ * when there was a copy from a VI to the screen
+ * so that swing applications would not appear to be
+ * sluggish.
+ */
+ public void runUpdateNow() {
+ synchronized (this) {
+ // nothing to do if the updater thread hadn't been started or if
+ // there are no tracked surfaces
+ if (done || screenUpdater == null ||
+ d3dwSurfaces == null || d3dwSurfaces.size() == 0)
+ {
+ return;
+ }
+ }
+ synchronized (runLock) {
+ needsUpdateNow = true;
+ runLock.notifyAll();
+ while (needsUpdateNow) {
+ try {
+ runLock.wait();
+ } catch (InterruptedException e) {}
+ }
+ }
+ }
+
+ public void run() {
+ while (!done) {
+ synchronized (runLock) {
+ // If the list is empty, suspend the thread until a
+ // new surface is added. Note that we have to check before
+ // wait() (and inside the runLock), otherwise we could miss a
+ // notify() when a new surface is added and sleep forever.
+ long timeout = d3dwSurfaces.size() > 0 ? 100 : 0;
+
+ // don't go to sleep if there's a thread waiting for an update
+ if (!needsUpdateNow) {
+ try { runLock.wait(timeout); }
+ catch (InterruptedException e) {}
+ }
+ // if we were woken up, there are probably surfaces in the list,
+ // no need to check if the list is empty
+ }
+
+ // make a copy to avoid synchronization during the loop
+ D3DWindowSurfaceData surfaces[] = new D3DWindowSurfaceData[] {};
+ synchronized (this) {
+ surfaces = d3dwSurfaces.toArray(surfaces);
+ }
+ for (D3DWindowSurfaceData sd : surfaces) {
+ // skip invalid surfaces (they could have become invalid
+ // after we made a copy of the list) - just a precaution
+ if (sd.isValid() && (sd.isDirty() || sd.isSurfaceLost())) {
+ if (!sd.isSurfaceLost()) {
+ // the flip and the clearing of the dirty state
+ // must be done under the lock, otherwise it's
+ // possible to miss an update to the surface
+ D3DRenderQueue rq = D3DRenderQueue.getInstance();
+ rq.lock();
+ try {
+ Rectangle r = sd.getBounds();
+ D3DSurfaceData.swapBuffers(sd, 0, 0,
+ r.width, r.height);
+ sd.markClean();
+ } finally {
+ rq.unlock();
+ }
+ } else if (!validate(sd)) {
+ // it is possible that the validation may never
+ // succeed, we need to detect this and replace
+ // the d3dw surface with gdi; the replacement of
+ // the surface will also trigger a repaint
+ sd.getPeer().replaceSurfaceDataLater();
+ }
+ }
+ }
+ synchronized (runLock) {
+ needsUpdateNow = false;
+ runLock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Restores the passed surface if it was lost, resets the lost status.
+ * @param sd surface to be validated
+ * @return true if surface wasn't lost or if restoration was successful,
+ * false otherwise
+ */
+ private boolean validate(D3DWindowSurfaceData sd) {
+ if (sd.isSurfaceLost()) {
+ try {
+ sd.restoreSurface();
+ // if succeeded, first fill the surface with bg color
+ // note: use the non-synch method to avoid incorrect lock order
+ Color bg = sd.getPeer().getBackgroundNoSync();
+ SunGraphics2D sg2d = new SunGraphics2D(sd, bg, bg, null);
+ sg2d.fillRect(0, 0, sd.getBounds().width, sd.getBounds().height);
+ sg2d.dispose();
+ // now clean the dirty status so that we don't flip it
+ // next time before it gets repainted; it is safe
+ // to do without the lock because we will issue a
+ // repaint anyway so we will not lose any rendering
+ sd.markClean();
+ // since the surface was successfully restored we need to
+ // repaint whole window to repopulate the back-buffer
+ repaintPeerTarget(sd.getPeer());
+ } catch (InvalidPipeException ipe) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Creates (or returns a cached one) gdi surface for the same peer as
+ * the passed d3dw surface has.
+ *
+ * @param d3dw surface used as key into the cache
+ * @return gdi window surface associated with the d3d window surfaces' peer
+ */
+ private synchronized SurfaceData getGdiSurface(D3DWindowSurfaceData d3dw) {
+ if (gdiSurfaces == null) {
+ gdiSurfaces =
+ new HashMap<D3DWindowSurfaceData, GDIWindowSurfaceData>();
+ }
+ GDIWindowSurfaceData gdisd = gdiSurfaces.get(d3dw);
+ if (gdisd == null) {
+ gdisd = GDIWindowSurfaceData.createData(d3dw.getPeer());
+ gdiSurfaces.put(d3dw, gdisd);
+ }
+ return gdisd;
+ }
+
+ /**
+ * Returns true if the component has heavyweight children.
+ *
+ * @param comp component to check for hw children
+ * @return true if Component has heavyweight children
+ */
+ private static boolean hasHWChildren(Component comp) {
+ if (comp instanceof Container) {
+ for (Component c : ((Container)comp).getComponents()) {
+ if (c.getPeer() instanceof WComponentPeer || hasHWChildren(c)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}