jdk/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java
changeset 25859 3317bb8137f4
parent 23010 6dadb192ad81
child 31661 a5cb86f2253b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2003, 2012, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.awt.image;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.ImageCapabilities;
+import java.awt.image.BufferedImage;
+import java.awt.image.VolatileImage;
+import sun.awt.DisplayChangedListener;
+import sun.awt.image.SunVolatileImage;
+import sun.java2d.SunGraphicsEnvironment;
+import sun.java2d.SurfaceData;
+import sun.java2d.loops.CompositeType;
+import static sun.java2d.pipe.hw.AccelSurface.*;
+
+/**
+ * This SurfaceManager variant manages an accelerated volatile surface, if it
+ * is possible to create that surface.  If there is limited accelerated
+ * memory, or if the volatile surface disappears due to an operating system
+ * event, the VolatileSurfaceManager will attempt to restore the
+ * accelerated surface.  If that fails, a system memory surface will be
+ * created in its place.
+ */
+public abstract class VolatileSurfaceManager
+    extends SurfaceManager
+    implements DisplayChangedListener
+{
+    /**
+     * A reference to the VolatileImage whose contents are being managed.
+     */
+    protected SunVolatileImage vImg;
+
+    /**
+     * The accelerated SurfaceData object.
+     */
+    protected SurfaceData sdAccel;
+
+    /**
+     * The software-based SurfaceData object.  Only create when first asked
+     * to (otherwise it is a waste of memory as it will only be used in
+     * situations of surface loss).
+     */
+    protected SurfaceData sdBackup;
+
+    /**
+     * The current SurfaceData object.
+     */
+    protected SurfaceData sdCurrent;
+
+    /**
+     * A record-keeping object.  This keeps track of which SurfaceData was
+     * in use during the last call to validate().  This lets us see whether
+     * the SurfaceData object has changed since then and allows us to return
+     * the correct returnCode to the user in the validate() call.
+     */
+    protected SurfaceData sdPrevious;
+
+    /**
+     * Tracks loss of surface contents; queriable by user to see whether
+     * contents need to be restored.
+     */
+    protected boolean lostSurface;
+
+    /**
+     * Context for extra initialization parameters.
+     */
+    protected Object context;
+
+    protected VolatileSurfaceManager(SunVolatileImage vImg, Object context) {
+        this.vImg = vImg;
+        this.context = context;
+
+        GraphicsEnvironment ge =
+            GraphicsEnvironment.getLocalGraphicsEnvironment();
+        // We could have a HeadlessGE at this point, so double-check before
+        // assuming anything.
+        if (ge instanceof SunGraphicsEnvironment) {
+            ((SunGraphicsEnvironment)ge).addDisplayChangedListener(this);
+        }
+    }
+
+    /**
+     * This init function is separate from the constructor because the
+     * things we are doing here necessitate the object's existence.
+     * Otherwise, we end up calling into a subclass' overridden method
+     * during construction, before that subclass is completely constructed.
+     */
+    public void initialize() {
+        if (isAccelerationEnabled()) {
+            sdAccel = initAcceleratedSurface();
+            if (sdAccel != null) {
+                sdCurrent = sdAccel;
+            }
+        }
+        // only initialize the backup surface for images with unforced
+        // acceleration type
+        if (sdCurrent == null &&
+            vImg.getForcedAccelSurfaceType() == UNDEFINED)
+        {
+            sdCurrent = getBackupSurface();
+        }
+    }
+
+    public SurfaceData getPrimarySurfaceData() {
+        return sdCurrent;
+    }
+
+    /**
+     * Returns true if acceleration is enabled.  If not, we simply use the
+     * backup SurfaceData object and return quickly from most methods
+     * in this class.
+     */
+    protected abstract boolean isAccelerationEnabled();
+
+    /**
+     * Get the image ready for rendering.  This method is called to make
+     * sure that the accelerated SurfaceData exists and is
+     * ready to be used.  Users call this method prior to any set of
+     * rendering to or from the image, to make sure the image is ready
+     * and compatible with the given GraphicsConfiguration.
+     *
+     * The image may not be "ready" if either we had problems creating
+     * it in the first place (e.g., there was no space in vram) or if
+     * the surface became lost (e.g., some other app or the OS caused
+     * vram surfaces to be removed).
+     *
+     * Note that we want to return RESTORED in any situation where the
+     * SurfaceData is different than it was last time.  So whether it's
+     * software or hardware, if we have a different SurfaceData object,
+     * then the contents have been altered and we must reflect that
+     * change to the user.
+     */
+    public int validate(GraphicsConfiguration gc) {
+        int returnCode = VolatileImage.IMAGE_OK;
+        boolean lostSurfaceTmp = lostSurface;
+        lostSurface = false;
+
+        if (isAccelerationEnabled()) {
+            if (!isConfigValid(gc)) {
+                // If we're asked to render to a different device than the
+                // one we were created under, return INCOMPATIBLE error code.
+                // Note that a null gc simply ignores the incompatibility
+                // issue
+                returnCode = VolatileImage.IMAGE_INCOMPATIBLE;
+            } else if (sdAccel == null) {
+                // We either had problems creating the surface or the display
+                // mode changed and we nullified the old one.  Try it again.
+                sdAccel = initAcceleratedSurface();
+                if (sdAccel != null) {
+                    // set the current SurfaceData to accelerated version
+                    sdCurrent = sdAccel;
+                    // we don't need the system memory surface anymore, so
+                    // let's release it now (it can always be restored later)
+                    sdBackup = null;
+                    returnCode = VolatileImage.IMAGE_RESTORED;
+                } else {
+                    sdCurrent = getBackupSurface();
+                }
+            } else if (sdAccel.isSurfaceLost()) {
+                try {
+                    restoreAcceleratedSurface();
+                    // set the current SurfaceData to accelerated version
+                    sdCurrent = sdAccel;
+                    // restoration successful: accel surface no longer lost
+                    sdAccel.setSurfaceLost(false);
+                    // we don't need the system memory surface anymore, so
+                    // let's release it now (it can always be restored later)
+                    sdBackup = null;
+                    returnCode = VolatileImage.IMAGE_RESTORED;
+                } catch (sun.java2d.InvalidPipeException e) {
+                    // Set the current SurfaceData to software version so that
+                    // drawing can continue.  Note that we still have
+                    // the lostAccelSurface flag set so that we will continue
+                    // to attempt to restore the accelerated surface.
+                    sdCurrent = getBackupSurface();
+                }
+            } else if (lostSurfaceTmp) {
+                // Something else triggered this loss/restoration.  Could
+                // be a palette change that didn't require a SurfaceData
+                // recreation but merely a re-rendering of the pixels.
+                returnCode = VolatileImage.IMAGE_RESTORED;
+            }
+        } else if (sdAccel != null) {
+            // if the "acceleration enabled" state changed to disabled,
+            // switch to software surface
+            sdCurrent = getBackupSurface();
+            sdAccel = null;
+            returnCode = VolatileImage.IMAGE_RESTORED;
+        }
+
+        if ((returnCode != VolatileImage.IMAGE_INCOMPATIBLE) &&
+            (sdCurrent != sdPrevious))
+        {
+            // contents have changed - return RESTORED to user
+            sdPrevious = sdCurrent;
+            returnCode = VolatileImage.IMAGE_RESTORED;
+        }
+
+        if (returnCode == VolatileImage.IMAGE_RESTORED) {
+            // clear the current surface with the background color,
+            // only if the surface has been restored
+            initContents();
+        }
+
+        return returnCode;
+    }
+
+    /**
+     * Returns true if rendering data was lost since the last validate call.
+     *
+     * @see java.awt.image.VolatileImage#contentsLost
+     */
+    public boolean contentsLost() {
+        return lostSurface;
+    }
+
+    /**
+     * Creates a new accelerated surface that is compatible with the
+     * current GraphicsConfiguration.  Returns the new accelerated
+     * SurfaceData object, or null if the surface creation was not successful.
+     *
+     * Platform-specific subclasses should initialize an accelerated
+     * surface (e.g. a DirectDraw surface on Windows, an OpenGL pbuffer,
+     * or an X11 pixmap).
+     */
+    protected abstract SurfaceData initAcceleratedSurface();
+
+    /**
+     * Creates a software-based surface (of type BufImgSurfaceData).
+     * The software representation is only created when needed, which
+     * is only during some situation in which the hardware surface
+     * cannot be allocated.  This allows apps to at least run,
+     * albeit more slowly than they would otherwise.
+     */
+    protected SurfaceData getBackupSurface() {
+        if (sdBackup == null) {
+            BufferedImage bImg = vImg.getBackupImage();
+            // Sabotage the acceleration capabilities of the BufImg surface
+            SunWritableRaster.stealTrackable(bImg
+                                             .getRaster()
+                                             .getDataBuffer()).setUntrackable();
+            sdBackup = BufImgSurfaceData.createData(bImg);
+        }
+        return sdBackup;
+    }
+
+    /**
+     * Set contents of the current SurfaceData to default state (i.e. clear
+     * the background).
+     */
+    public void initContents() {
+        // images with forced acceleration type may have a null sdCurrent
+        // because we do not create a backup surface for them
+        if (sdCurrent != null) {
+            Graphics g = vImg.createGraphics();
+            g.clearRect(0, 0, vImg.getWidth(), vImg.getHeight());
+            g.dispose();
+        }
+    }
+
+    /**
+     * Called from a SurfaceData object, indicating that our
+     * accelerated surface has been lost and should be restored (perhaps
+     * using a backup system memory surface).  Returns the newly restored
+     * primary SurfaceData object.
+     */
+    public SurfaceData restoreContents() {
+        return getBackupSurface();
+    }
+
+    /**
+     * If the accelerated surface is the current SurfaceData for this manager,
+     * sets the variable lostSurface to true, which indicates that something
+     * happened to the image under management.  This variable is used in the
+     * validate method to tell the caller that the surface contents need to
+     * be restored.
+     */
+    public void acceleratedSurfaceLost() {
+        if (isAccelerationEnabled() && (sdCurrent == sdAccel)) {
+            lostSurface = true;
+        }
+    }
+
+    /**
+     * Restore sdAccel in case it was lost.  Do nothing in this
+     * default case; platform-specific implementations may do more in
+     * this situation as appropriate.
+     */
+    protected void restoreAcceleratedSurface() {
+    }
+
+    /**
+     * Called from SunGraphicsEnv when there has been a display mode change.
+     * Note that we simply invalidate hardware surfaces here; we do not
+     * attempt to recreate or re-render them.  This is to avoid threading
+     * conflicts with the native toolkit and associated threads.  Instead,
+     * we just nullify the old surface data object and wait for a future
+     * method in the rendering process to recreate the surface.
+     */
+    public void displayChanged() {
+        if (!isAccelerationEnabled()) {
+            return;
+        }
+        lostSurface = true;
+        if (sdAccel != null) {
+            // First, nullify the software surface.  This guards against
+            // using a SurfaceData that was created in a different
+            // display mode.
+            sdBackup = null;
+            // Now, invalidate the old hardware-based SurfaceData
+            // Note that getBackupSurface may set sdAccel to null so we have to invalidate it before
+            SurfaceData oldData = sdAccel;
+            sdAccel = null;
+            oldData.invalidate();
+            sdCurrent = getBackupSurface();
+        }
+        // Update graphicsConfig for the vImg in case it changed due to
+        // this display change event
+        vImg.updateGraphicsConfig();
+    }
+
+    /**
+     * When device palette changes, need to force a new copy
+     * of the image into our hardware cache to update the
+     * color indices of the pixels (indexed mode only).
+     */
+    public void paletteChanged() {
+        lostSurface = true;
+    }
+
+    /**
+     * Called by validate() to see whether the GC passed in is ok for
+     * rendering to.  This generic implementation checks to see
+     * whether the GC is either null or is from the same
+     * device as the one that this image was created on.  Platform-
+     * specific implementations may perform other checks as
+     * appropriate.
+     */
+    protected boolean isConfigValid(GraphicsConfiguration gc) {
+        return ((gc == null) ||
+                (gc.getDevice() == vImg.getGraphicsConfig().getDevice()));
+    }
+
+    @Override
+    public ImageCapabilities getCapabilities(GraphicsConfiguration gc) {
+        if (isConfigValid(gc)) {
+            return isAccelerationEnabled() ?
+                new AcceleratedImageCapabilities() :
+                new ImageCapabilities(false);
+        }
+        return super.getCapabilities(gc);
+    }
+
+    private class AcceleratedImageCapabilities
+        extends ImageCapabilities
+    {
+        AcceleratedImageCapabilities() {
+            super(false);
+        }
+        @Override
+        public boolean isAccelerated() {
+            return (sdCurrent == sdAccel);
+        }
+        @Override
+        public boolean isTrueVolatile() {
+            return isAccelerated();
+        }
+    }
+
+    /**
+     * Releases any associated hardware memory for this image by
+     * calling flush on sdAccel.  This method forces a lostSurface
+     * situation so any future operations on the image will need to
+     * revalidate the image first.
+     */
+    public void flush() {
+        lostSurface = true;
+        SurfaceData oldSD = sdAccel;
+        sdAccel = null;
+        if (oldSD != null) {
+            oldSD.flush();
+        }
+    }
+}