Browse Source

* Fixed issue with default samples value causing "No support for WGL_ARB_multisample" error

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9021 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
Sha..rd 14 years ago
parent
commit
bd828d629c

+ 482 - 478
engine/src/lwjgl/com/jme3/system/lwjgl/LwjglCanvas.java

@@ -1,478 +1,482 @@
-/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.jme3.system.lwjgl;
-
-import com.jme3.system.AppSettings;
-import com.jme3.system.JmeCanvasContext;
-import com.jme3.system.JmeContext.Type;
-import com.jme3.system.JmeSystem;
-import com.jme3.system.Platform;
-import java.awt.Canvas;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.swing.SwingUtilities;
-import org.lwjgl.LWJGLException;
-import org.lwjgl.input.Keyboard;
-import org.lwjgl.input.Mouse;
-import org.lwjgl.opengl.Display;
-import org.lwjgl.opengl.Pbuffer;
-import org.lwjgl.opengl.PixelFormat;
-
-public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContext {
-
-    protected static final int TASK_NOTHING = 0,
-                               TASK_DESTROY_DISPLAY = 1,
-                               TASK_CREATE_DISPLAY = 2,
-                               TASK_COMPLETE = 3;
-    
-//    protected static final boolean USE_SHARED_CONTEXT =
-//                Boolean.parseBoolean(System.getProperty("jme3.canvas.sharedctx", "true"));
-    
-    protected static final boolean USE_SHARED_CONTEXT = false;
-    
-    private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName());
-    private Canvas canvas;
-    private int width;
-    private int height;
-
-    private final Object taskLock = new Object();
-    private int desiredTask = TASK_NOTHING;
-
-    private Thread renderThread;
-    private boolean runningFirstTime = true;
-    private boolean mouseWasGrabbed = false;
-    
-    private boolean mouseWasCreated = false;
-    private boolean keyboardWasCreated = false;
-
-    private Pbuffer pbuffer;
-    private PixelFormat pbufferFormat;
-    private PixelFormat canvasFormat;
-
-    private class GLCanvas extends Canvas {
-        @Override
-        public void addNotify(){
-            super.addNotify();
-
-            if (renderThread != null && renderThread.getState() == Thread.State.TERMINATED)
-                return; // already destroyed.
-
-            if (renderThread == null){
-                logger.log(Level.INFO, "EDT: Creating OGL thread.");
-
-                // Also set some settings on the canvas here.
-                // So we don't do it outside the AWT thread.
-                canvas.setFocusable(true);
-                canvas.setIgnoreRepaint(true);
-
-                renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread");
-                renderThread.start();
-            }else if (needClose.get()){
-                return;
-            }
-
-            logger.log(Level.INFO, "EDT: Telling OGL to create display ..");
-            synchronized (taskLock){
-                desiredTask = TASK_CREATE_DISPLAY;
-//                while (desiredTask != TASK_COMPLETE){
-//                    try {
-//                        taskLock.wait();
-//                    } catch (InterruptedException ex) {
-//                        return;
-//                    }
-//                }
-//                desiredTask = TASK_NOTHING;
-            }
-//            logger.log(Level.INFO, "EDT: OGL has created the display");
-        }
-
-        @Override
-        public void removeNotify(){
-            if (needClose.get()){
-                logger.log(Level.INFO, "EDT: Application is stopped. Not restoring canvas.");
-                super.removeNotify();
-                return;
-            }
-
-            // We must tell GL context to shutdown and wait for it to
-            // shutdown, otherwise, issues will occur.
-            logger.log(Level.INFO, "EDT: Telling OGL to destroy display ..");
-            synchronized (taskLock){
-                desiredTask = TASK_DESTROY_DISPLAY;
-                while (desiredTask != TASK_COMPLETE){
-                    try {
-                        taskLock.wait();
-                    } catch (InterruptedException ex){
-                        super.removeNotify();
-                        return;
-                    }
-                }
-                desiredTask = TASK_NOTHING;
-            }
-            
-            logger.log(Level.INFO, "EDT: Acknowledged receipt of canvas death");
-            // GL context is dead at this point
-
-            super.removeNotify();
-        }
-    }
-
-    public LwjglCanvas(){
-        super();
-        canvas = new GLCanvas();
-    }
-
-    @Override
-    public Type getType() {
-        return Type.Canvas;
-    }
-
-    public void create(boolean waitFor){
-        if (renderThread == null){
-            logger.log(Level.INFO, "MAIN: Creating OGL thread.");
-
-            renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread");
-            renderThread.start();
-        }
-        // do not do anything.
-        // superclass's create() will be called at initInThread()
-        if (waitFor)
-            waitFor(true);
-    }
-
-    @Override
-    public void setTitle(String title) {
-    }
-
-    @Override
-    public void restart() {
-        frameRate = settings.getFrameRate();
-        // TODO: Handle other cases, like change of pixel format, etc.
-    }
-
-    public Canvas getCanvas(){
-        return canvas;
-    }
-    
-    @Override
-    protected void runLoop(){
-        if (desiredTask != TASK_NOTHING){
-            synchronized (taskLock){
-                switch (desiredTask){
-                    case TASK_CREATE_DISPLAY:
-                        logger.log(Level.INFO, "OGL: Creating display ..");
-                        restoreCanvas();
-                        listener.gainFocus();
-                        desiredTask = TASK_NOTHING;
-                        break;
-                    case TASK_DESTROY_DISPLAY:
-                        logger.log(Level.INFO, "OGL: Destroying display ..");
-                        listener.loseFocus();
-                        pauseCanvas();
-                        break;
-                }
-                desiredTask = TASK_COMPLETE;
-                taskLock.notifyAll();
-            }
-        }
-        
-        if (renderable.get()){
-            int newWidth = Math.max(canvas.getWidth(), 1);
-            int newHeight = Math.max(canvas.getHeight(), 1);
-            if (width != newWidth || height != newHeight){
-                width = newWidth;
-                height = newHeight;
-                if (listener != null){
-                    listener.reshape(width, height);
-                }
-            }
-        }else{
-            if (frameRate <= 0){
-                // NOTE: MUST be done otherwise 
-                // Windows OS will freeze
-                Display.sync(30);
-            }
-        }
-        
-        super.runLoop();
-    }
-
-    private void pauseCanvas(){
-        if (Mouse.isCreated()){
-            if (Mouse.isGrabbed()){
-                Mouse.setGrabbed(false);
-                mouseWasGrabbed = true;
-            }
-            mouseWasCreated = true;
-            Mouse.destroy();
-        }
-        if (Keyboard.isCreated()){
-            keyboardWasCreated = true;
-            Keyboard.destroy();
-        }
-
-        renderable.set(false);
-        destroyContext();
-    }
-
-    /**
-     * Called to restore the canvas.
-     */
-    private void restoreCanvas(){
-        logger.log(Level.INFO, "OGL: Waiting for canvas to become displayable..");
-        while (!canvas.isDisplayable()){
-            try {
-                Thread.sleep(10);
-            } catch (InterruptedException ex) {
-                logger.log(Level.SEVERE, "OGL: Interrupted! ", ex);
-            }
-        }
-        
-        logger.log(Level.INFO, "OGL: Creating display context ..");
-
-        // Set renderable to true, since canvas is now displayable.
-        renderable.set(true);
-        createContext(settings);
-
-        logger.log(Level.INFO, "OGL: Display is active!");
-
-        try {
-            if (mouseWasCreated){
-                Mouse.create();
-                if (mouseWasGrabbed){
-                    Mouse.setGrabbed(true);
-                    mouseWasGrabbed = false;
-                }
-            }
-            if (keyboardWasCreated){
-                Keyboard.create();
-                keyboardWasCreated = false;
-            }
-        } catch (LWJGLException ex){
-            logger.log(Level.SEVERE, "Encountered exception when restoring input", ex);
-        }
-
-        SwingUtilities.invokeLater(new Runnable(){
-            public void run(){
-                canvas.requestFocus();
-            }
-        });
-    }
-    
-    /**
-     * It seems it is best to use one pixel format for all shared contexts.
-     * @see <a href="http://developer.apple.com/library/mac/#qa/qa1248/_index.html">http://developer.apple.com/library/mac/#qa/qa1248/_index.html</a>
-     */
-    protected PixelFormat acquirePixelFormat(boolean forPbuffer){
-        if (forPbuffer){
-            // Use 0 samples for pbuffer format, prevents
-            // crashes on bad drivers
-            if (pbufferFormat == null){
-                pbufferFormat = new PixelFormat(settings.getBitsPerPixel(),
-                                                0,
-                                                settings.getDepthBits(),
-                                                settings.getStencilBits(),
-                                                0);
-            }
-            return pbufferFormat;
-        }else{
-            if (canvasFormat == null){
-                canvasFormat = new PixelFormat(settings.getBitsPerPixel(),
-                                               0,
-                                               settings.getDepthBits(),
-                                               settings.getStencilBits(),
-                                               settings.getSamples());
-            }
-            return canvasFormat;
-        }
-    }
-
-    /**
-     * Makes sure the pbuffer is available and ready for use
-     */
-    protected void makePbufferAvailable() throws LWJGLException{
-        if (pbuffer != null && pbuffer.isBufferLost()){
-            logger.log(Level.WARNING, "PBuffer was lost!");
-            pbuffer.destroy();
-            pbuffer = null;
-        }
-        
-        if (pbuffer == null) {
-            pbuffer = new Pbuffer(1, 1, acquirePixelFormat(true), null);
-            pbuffer.makeCurrent();
-            logger.log(Level.INFO, "OGL: Pbuffer has been created");
-            
-            // Any created objects are no longer valid
-            if (!runningFirstTime){
-                renderer.resetGLObjects();
-            }
-        }
-        
-        pbuffer.makeCurrent();
-        if (!pbuffer.isCurrent()){
-            throw new LWJGLException("Pbuffer cannot be made current");
-        }
-    }
-    
-    protected void destroyPbuffer(){
-        if (pbuffer != null){
-            if (!pbuffer.isBufferLost()){
-                pbuffer.destroy();
-            }
-            pbuffer = null;
-        }
-    }
-    
-    /**
-     * This is called:
-     * 1) When the context thread ends
-     * 2) Any time the canvas becomes non-displayable
-     */
-    protected void destroyContext(){
-        try {
-            // invalidate the state so renderer can resume operation
-            if (!USE_SHARED_CONTEXT){
-                renderer.cleanup();
-            }
-            
-            if (Display.isCreated()){
-                /* FIXES:
-                 * org.lwjgl.LWJGLException: X Error
-                 * BadWindow (invalid Window parameter) request_code: 2 minor_code: 0
-                 * 
-                 * Destroying keyboard early prevents the error above, triggered
-                 * by destroying keyboard in by Display.destroy() or Display.setParent(null).
-                 * Therefore Keyboard.destroy() should precede any of these calls.
-                 */
-                if (Keyboard.isCreated()){
-                    // Should only happen if called in 
-                    // LwjglAbstractDisplay.deinitInThread().
-                    Keyboard.destroy();
-                }
-
-                //try {
-                    // NOTE: On Windows XP, not calling setParent(null)
-                    // freezes the application.
-                    // On Mac it freezes the application.
-                    // On Linux it fixes a crash with X Window System.
-                    if (JmeSystem.getPlatform() == Platform.Windows32
-                     || JmeSystem.getPlatform() == Platform.Windows64){
-                        //Display.setParent(null);
-                    }
-                //} catch (LWJGLException ex) {
-                //    logger.log(Level.SEVERE, "Encountered exception when setting parent to null", ex);
-                //}
-
-                Display.destroy();
-            }
-            
-            // The canvas is no longer visible,
-            // but the context thread is still running.
-            if (!needClose.get()){
-                // MUST make sure there's still a context current here ..
-                // Display is dead, make pbuffer available to the system
-                makePbufferAvailable();
-                
-                renderer.invalidateState();
-            }else{
-                // The context thread is no longer running.
-                // Destroy pbuffer.
-                destroyPbuffer();
-            }
-        } catch (LWJGLException ex) {
-            listener.handleError("Failed make pbuffer available", ex);
-        }
-    }
-
-    /**
-     * This is called:
-     * 1) When the context thread starts
-     * 2) Any time the canvas becomes displayable again.
-     */
-    @Override
-    protected void createContext(AppSettings settings) {
-        // In case canvas is not visible, we still take framerate
-        // from settings to prevent "100% CPU usage"
-        frameRate = settings.getFrameRate();
-        
-        try {
-            if (renderable.get()){
-                if (!runningFirstTime){
-                    // because the display is a different opengl context
-                    // must reset the context state.
-                    if (!USE_SHARED_CONTEXT){
-                        renderer.cleanup();
-                    }
-                }
-                
-                // if the pbuffer is currently active, 
-                // make sure to deactivate it
-                destroyPbuffer();
-                
-                if (Keyboard.isCreated()){
-                    Keyboard.destroy();
-                }
-                
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException ex) {
-                }
-                
-                Display.setVSyncEnabled(settings.isVSync());
-                Display.setParent(canvas);
-                
-                if (USE_SHARED_CONTEXT){
-                    Display.create(acquirePixelFormat(false), pbuffer);
-                }else{
-                    Display.create(acquirePixelFormat(false));
-                }
-                
-                renderer.invalidateState();
-            }else{
-                // First create the pbuffer, if it is needed.
-                makePbufferAvailable();
-            }
-
-            // At this point, the OpenGL context is active.
-            if (runningFirstTime){
-                // THIS is the part that creates the renderer.
-                // It must always be called, now that we have the pbuffer workaround.
-                initContextFirstTime();
-                runningFirstTime = false;
-            }
-        } catch (LWJGLException ex) {
-            listener.handleError("Failed to initialize OpenGL context", ex);
-            // TODO: Fix deadlock that happens after the error (throw runtime exception?)
-        }
-    }
-}
+F/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system.lwjgl;
+
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeCanvasContext;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.system.JmeSystem;
+import com.jme3.system.Platform;
+import java.awt.Canvas;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.SwingUtilities;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.Display;
+import org.lwjgl.opengl.Pbuffer;
+import org.lwjgl.opengl.PixelFormat;
+
+public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContext {
+
+    protected static final int TASK_NOTHING = 0,
+                               TASK_DESTROY_DISPLAY = 1,
+                               TASK_CREATE_DISPLAY = 2,
+                               TASK_COMPLETE = 3;
+    
+//    protected static final boolean USE_SHARED_CONTEXT =
+//                Boolean.parseBoolean(System.getProperty("jme3.canvas.sharedctx", "true"));
+    
+    protected static final boolean USE_SHARED_CONTEXT = false;
+    
+    private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName());
+    private Canvas canvas;
+    private int width;
+    private int height;
+
+    private final Object taskLock = new Object();
+    private int desiredTask = TASK_NOTHING;
+
+    private Thread renderThread;
+    private boolean runningFirstTime = true;
+    private boolean mouseWasGrabbed = false;
+    
+    private boolean mouseWasCreated = false;
+    private boolean keyboardWasCreated = false;
+
+    private Pbuffer pbuffer;
+    private PixelFormat pbufferFormat;
+    private PixelFormat canvasFormat;
+
+    private class GLCanvas extends Canvas {
+        @Override
+        public void addNotify(){
+            super.addNotify();
+
+            if (renderThread != null && renderThread.getState() == Thread.State.TERMINATED)
+                return; // already destroyed.
+
+            if (renderThread == null){
+                logger.log(Level.INFO, "EDT: Creating OGL thread.");
+
+                // Also set some settings on the canvas here.
+                // So we don't do it outside the AWT thread.
+                canvas.setFocusable(true);
+                canvas.setIgnoreRepaint(true);
+
+                renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread");
+                renderThread.start();
+            }else if (needClose.get()){
+                return;
+            }
+
+            logger.log(Level.INFO, "EDT: Telling OGL to create display ..");
+            synchronized (taskLock){
+                desiredTask = TASK_CREATE_DISPLAY;
+//                while (desiredTask != TASK_COMPLETE){
+//                    try {
+//                        taskLock.wait();
+//                    } catch (InterruptedException ex) {
+//                        return;
+//                    }
+//                }
+//                desiredTask = TASK_NOTHING;
+            }
+//            logger.log(Level.INFO, "EDT: OGL has created the display");
+        }
+
+        @Override
+        public void removeNotify(){
+            if (needClose.get()){
+                logger.log(Level.INFO, "EDT: Application is stopped. Not restoring canvas.");
+                super.removeNotify();
+                return;
+            }
+
+            // We must tell GL context to shutdown and wait for it to
+            // shutdown, otherwise, issues will occur.
+            logger.log(Level.INFO, "EDT: Telling OGL to destroy display ..");
+            synchronized (taskLock){
+                desiredTask = TASK_DESTROY_DISPLAY;
+                while (desiredTask != TASK_COMPLETE){
+                    try {
+                        taskLock.wait();
+                    } catch (InterruptedException ex){
+                        super.removeNotify();
+                        return;
+                    }
+                }
+                desiredTask = TASK_NOTHING;
+            }
+            
+            logger.log(Level.INFO, "EDT: Acknowledged receipt of canvas death");
+            // GL context is dead at this point
+
+            super.removeNotify();
+        }
+    }
+
+    public LwjglCanvas(){
+        super();
+        canvas = new GLCanvas();
+    }
+
+    @Override
+    public Type getType() {
+        return Type.Canvas;
+    }
+
+    public void create(boolean waitFor){
+        if (renderThread == null){
+            logger.log(Level.INFO, "MAIN: Creating OGL thread.");
+
+            renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread");
+            renderThread.start();
+        }
+        // do not do anything.
+        // superclass's create() will be called at initInThread()
+        if (waitFor)
+            waitFor(true);
+    }
+
+    @Override
+    public void setTitle(String title) {
+    }
+
+    @Override
+    public void restart() {
+        frameRate = settings.getFrameRate();
+        // TODO: Handle other cases, like change of pixel format, etc.
+    }
+
+    public Canvas getCanvas(){
+        return canvas;
+    }
+    
+    @Override
+    protected void runLoop(){
+        if (desiredTask != TASK_NOTHING){
+            synchronized (taskLock){
+                switch (desiredTask){
+                    case TASK_CREATE_DISPLAY:
+                        logger.log(Level.INFO, "OGL: Creating display ..");
+                        restoreCanvas();
+                        listener.gainFocus();
+                        desiredTask = TASK_NOTHING;
+                        break;
+                    case TASK_DESTROY_DISPLAY:
+                        logger.log(Level.INFO, "OGL: Destroying display ..");
+                        listener.loseFocus();
+                        pauseCanvas();
+                        break;
+                }
+                desiredTask = TASK_COMPLETE;
+                taskLock.notifyAll();
+            }
+        }
+        
+        if (renderable.get()){
+            int newWidth = Math.max(canvas.getWidth(), 1);
+            int newHeight = Math.max(canvas.getHeight(), 1);
+            if (width != newWidth || height != newHeight){
+                width = newWidth;
+                height = newHeight;
+                if (listener != null){
+                    listener.reshape(width, height);
+                }
+            }
+        }else{
+            if (frameRate <= 0){
+                // NOTE: MUST be done otherwise 
+                // Windows OS will freeze
+                Display.sync(30);
+            }
+        }
+        
+        super.runLoop();
+    }
+
+    private void pauseCanvas(){
+        if (Mouse.isCreated()){
+            if (Mouse.isGrabbed()){
+                Mouse.setGrabbed(false);
+                mouseWasGrabbed = true;
+            }
+            mouseWasCreated = true;
+            Mouse.destroy();
+        }
+        if (Keyboard.isCreated()){
+            keyboardWasCreated = true;
+            Keyboard.destroy();
+        }
+
+        renderable.set(false);
+        destroyContext();
+    }
+
+    /**
+     * Called to restore the canvas.
+     */
+    private void restoreCanvas(){
+        logger.log(Level.INFO, "OGL: Waiting for canvas to become displayable..");
+        while (!canvas.isDisplayable()){
+            try {
+                Thread.sleep(10);
+            } catch (InterruptedException ex) {
+                logger.log(Level.SEVERE, "OGL: Interrupted! ", ex);
+            }
+        }
+        
+        logger.log(Level.INFO, "OGL: Creating display context ..");
+
+        // Set renderable to true, since canvas is now displayable.
+        renderable.set(true);
+        createContext(settings);
+
+        logger.log(Level.INFO, "OGL: Display is active!");
+
+        try {
+            if (mouseWasCreated){
+                Mouse.create();
+                if (mouseWasGrabbed){
+                    Mouse.setGrabbed(true);
+                    mouseWasGrabbed = false;
+                }
+            }
+            if (keyboardWasCreated){
+                Keyboard.create();
+                keyboardWasCreated = false;
+            }
+        } catch (LWJGLException ex){
+            logger.log(Level.SEVERE, "Encountered exception when restoring input", ex);
+        }
+
+        SwingUtilities.invokeLater(new Runnable(){
+            public void run(){
+                canvas.requestFocus();
+            }
+        });
+    }
+    
+    /**
+     * It seems it is best to use one pixel format for all shared contexts.
+     * @see <a href="http://developer.apple.com/library/mac/#qa/qa1248/_index.html">http://developer.apple.com/library/mac/#qa/qa1248/_index.html</a>
+     */
+    protected PixelFormat acquirePixelFormat(boolean forPbuffer){
+        if (forPbuffer){
+            // Use 0 samples for pbuffer format, prevents
+            // crashes on bad drivers
+            if (pbufferFormat == null){
+                pbufferFormat = new PixelFormat(settings.getBitsPerPixel(),
+                                                0,
+                                                settings.getDepthBits(),
+                                                settings.getStencilBits(),
+                                                0);
+            }
+            return pbufferFormat;
+        }else{
+            if (canvasFormat == null){
+			int samples = 0;
+		      if (settings.getSamples() > 1){
+                    samples = settings.getSamples();
+                }
+                canvasFormat = new PixelFormat(settings.getBitsPerPixel(),
+                                               0,
+                                               settings.getDepthBits(),
+                                               settings.getStencilBits(),
+                                               samples);
+            }
+            return canvasFormat;
+        }
+    }
+
+    /**
+     * Makes sure the pbuffer is available and ready for use
+     */
+    protected void makePbufferAvailable() throws LWJGLException{
+        if (pbuffer != null && pbuffer.isBufferLost()){
+            logger.log(Level.WARNING, "PBuffer was lost!");
+            pbuffer.destroy();
+            pbuffer = null;
+        }
+        
+        if (pbuffer == null) {
+            pbuffer = new Pbuffer(1, 1, acquirePixelFormat(true), null);
+            pbuffer.makeCurrent();
+            logger.log(Level.INFO, "OGL: Pbuffer has been created");
+            
+            // Any created objects are no longer valid
+            if (!runningFirstTime){
+                renderer.resetGLObjects();
+            }
+        }
+        
+        pbuffer.makeCurrent();
+        if (!pbuffer.isCurrent()){
+            throw new LWJGLException("Pbuffer cannot be made current");
+        }
+    }
+    
+    protected void destroyPbuffer(){
+        if (pbuffer != null){
+            if (!pbuffer.isBufferLost()){
+                pbuffer.destroy();
+            }
+            pbuffer = null;
+        }
+    }
+    
+    /**
+     * This is called:
+     * 1) When the context thread ends
+     * 2) Any time the canvas becomes non-displayable
+     */
+    protected void destroyContext(){
+        try {
+            // invalidate the state so renderer can resume operation
+            if (!USE_SHARED_CONTEXT){
+                renderer.cleanup();
+            }
+            
+            if (Display.isCreated()){
+                /* FIXES:
+                 * org.lwjgl.LWJGLException: X Error
+                 * BadWindow (invalid Window parameter) request_code: 2 minor_code: 0
+                 * 
+                 * Destroying keyboard early prevents the error above, triggered
+                 * by destroying keyboard in by Display.destroy() or Display.setParent(null).
+                 * Therefore Keyboard.destroy() should precede any of these calls.
+                 */
+                if (Keyboard.isCreated()){
+                    // Should only happen if called in 
+                    // LwjglAbstractDisplay.deinitInThread().
+                    Keyboard.destroy();
+                }
+
+                //try {
+                    // NOTE: On Windows XP, not calling setParent(null)
+                    // freezes the application.
+                    // On Mac it freezes the application.
+                    // On Linux it fixes a crash with X Window System.
+                    if (JmeSystem.getPlatform() == Platform.Windows32
+                     || JmeSystem.getPlatform() == Platform.Windows64){
+                        //Display.setParent(null);
+                    }
+                //} catch (LWJGLException ex) {
+                //    logger.log(Level.SEVERE, "Encountered exception when setting parent to null", ex);
+                //}
+
+                Display.destroy();
+            }
+            
+            // The canvas is no longer visible,
+            // but the context thread is still running.
+            if (!needClose.get()){
+                // MUST make sure there's still a context current here ..
+                // Display is dead, make pbuffer available to the system
+                makePbufferAvailable();
+                
+                renderer.invalidateState();
+            }else{
+                // The context thread is no longer running.
+                // Destroy pbuffer.
+                destroyPbuffer();
+            }
+        } catch (LWJGLException ex) {
+            listener.handleError("Failed make pbuffer available", ex);
+        }
+    }
+
+    /**
+     * This is called:
+     * 1) When the context thread starts
+     * 2) Any time the canvas becomes displayable again.
+     */
+    @Override
+    protected void createContext(AppSettings settings) {
+        // In case canvas is not visible, we still take framerate
+        // from settings to prevent "100% CPU usage"
+        frameRate = settings.getFrameRate();
+        
+        try {
+            if (renderable.get()){
+                if (!runningFirstTime){
+                    // because the display is a different opengl context
+                    // must reset the context state.
+                    if (!USE_SHARED_CONTEXT){
+                        renderer.cleanup();
+                    }
+                }
+                
+                // if the pbuffer is currently active, 
+                // make sure to deactivate it
+                destroyPbuffer();
+                
+                if (Keyboard.isCreated()){
+                    Keyboard.destroy();
+                }
+                
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException ex) {
+                }
+                
+                Display.setVSyncEnabled(settings.isVSync());
+                Display.setParent(canvas);
+                
+                if (USE_SHARED_CONTEXT){
+                    Display.create(acquirePixelFormat(false), pbuffer);
+                }else{
+                    Display.create(acquirePixelFormat(false));
+                }
+                
+                renderer.invalidateState();
+            }else{
+                // First create the pbuffer, if it is needed.
+                makePbufferAvailable();
+            }
+
+            // At this point, the OpenGL context is active.
+            if (runningFirstTime){
+                // THIS is the part that creates the renderer.
+                // It must always be called, now that we have the pbuffer workaround.
+                initContextFirstTime();
+                runningFirstTime = false;
+            }
+        } catch (LWJGLException ex) {
+            listener.handleError("Failed to initialize OpenGL context", ex);
+            // TODO: Fix deadlock that happens after the error (throw runtime exception?)
+        }
+    }
+}

+ 5 - 1
engine/src/lwjgl/com/jme3/system/lwjgl/LwjglDisplay.java

@@ -81,11 +81,15 @@ public class LwjglDisplay extends LwjglAbstractDisplay {
             displayMode = new DisplayMode(settings.getWidth(), settings.getHeight());
         }
 
+	   int samples = 0;
+        if (settings.getSamples() > 1){
+            samples = settings.getSamples();
+        }
         PixelFormat pf = new PixelFormat(settings.getBitsPerPixel(),
                                          0,
                                          settings.getDepthBits(),
                                          settings.getStencilBits(),
-                                         settings.getSamples());
+                                         samples);
 
         frameRate = settings.getFrameRate();
         logger.log(Level.INFO, "Selected display mode: {0}", displayMode);

+ 198 - 194
engine/src/lwjgl/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java

@@ -1,194 +1,198 @@
-/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.jme3.system.lwjgl;
-
-import com.jme3.input.JoyInput;
-import com.jme3.input.KeyInput;
-import com.jme3.input.MouseInput;
-import com.jme3.input.TouchInput;
-import com.jme3.input.dummy.DummyKeyInput;
-import com.jme3.input.dummy.DummyMouseInput;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import org.lwjgl.LWJGLException;
-import org.lwjgl.Sys;
-import org.lwjgl.opengl.*;
-
-public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
-
-    private static final Logger logger = Logger.getLogger(LwjglOffscreenBuffer.class.getName());
-    private Pbuffer pbuffer;
-    protected AtomicBoolean needClose = new AtomicBoolean(false);
-    private int width;
-    private int height;
-    private PixelFormat pixelFormat;
-
-    protected void initInThread(){
-        if ((Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) == 0){
-            logger.severe("Offscreen surfaces are not supported.");
-            return;
-        }
-
-        pixelFormat = new PixelFormat(settings.getBitsPerPixel(),
-                                      0,
-                                      settings.getDepthBits(),
-                                      settings.getStencilBits(),
-                                      settings.getSamples());
-        
-        width = settings.getWidth();
-        height = settings.getHeight();
-        try{
-            Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
-                public void uncaughtException(Thread thread, Throwable thrown) {
-                    listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
-                }
-            });
-
-            pbuffer = new Pbuffer(width, height, pixelFormat, null, null, createContextAttribs());
-            pbuffer.makeCurrent();
-
-            renderable.set(true);
-
-            logger.info("Offscreen buffer created.");
-            printContextInitInfo();
-        } catch (LWJGLException ex){
-            listener.handleError("Failed to create display", ex);
-        } finally {
-            // TODO: It is possible to avoid "Failed to find pixel format"
-            // error here by creating a default display.
-        }
-        super.internalCreate();
-        listener.initialize();
-    }
-
-    protected boolean checkGLError(){
-        try {
-            Util.checkGLError();
-        } catch (OpenGLException ex){
-            listener.handleError("An OpenGL error has occured!", ex);
-        }
-        // NOTE: Always return true since this is used in an "assert" statement
-        return true;
-    }
-
-    protected void runLoop(){
-        if (!created.get())
-            throw new IllegalStateException();
-
-        if (pbuffer.isBufferLost()){
-            pbuffer.destroy();
-            try{
-                pbuffer = new Pbuffer(width, height, pixelFormat, null);
-                pbuffer.makeCurrent();
-            }catch (LWJGLException ex){
-                listener.handleError("Failed to restore pbuffer content", ex);
-            }
-        }
-
-        listener.update();
-        assert checkGLError();
-        
-        renderer.onFrame();
-        
-        int frameRate = settings.getFrameRate();
-        if (frameRate >= 1){
-            Display.sync(frameRate);
-        }
-    }
-
-    protected void deinitInThread(){
-        renderable.set(false);
-        
-        listener.destroy();
-        renderer.cleanup();
-        pbuffer.destroy();
-        logger.info("Offscreen buffer destroyed.");
-    }
-
-    public void run(){
-        logger.log(Level.INFO, "Using LWJGL {0}", Sys.getVersion());
-        initInThread();
-        while (!needClose.get()){
-            runLoop();
-        }
-        deinitInThread();
-    }
-
-    public void destroy(boolean waitFor){
-        needClose.set(true);
-        if (waitFor)
-            waitFor(false);
-    }
-
-    public void create(boolean waitFor){
-        if (created.get()){
-            logger.warning("create() called when pbuffer is already created!");
-            return;
-        }
-
-        new Thread(this, "LWJGL Renderer Thread").start();
-        if (waitFor)
-            waitFor(true);
-    }
-
-    public void restart() {
-    }
-
-    public void setAutoFlushFrames(boolean enabled){
-    }
-
-    public Type getType() {
-        return Type.OffscreenSurface;
-    }
-
-    public MouseInput getMouseInput() {
-        return new DummyMouseInput();
-    }
-
-    public KeyInput getKeyInput() {
-        return new DummyKeyInput();
-    }
-
-    public JoyInput getJoyInput() {
-        return null;
-    }
-    
-    public TouchInput getTouchInput() {
-        return null;
-    }
-
-    public void setTitle(String title) {
-    }
-
-}
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.system.lwjgl;
+
+import com.jme3.input.JoyInput;
+import com.jme3.input.KeyInput;
+import com.jme3.input.MouseInput;
+import com.jme3.input.TouchInput;
+import com.jme3.input.dummy.DummyKeyInput;
+import com.jme3.input.dummy.DummyMouseInput;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.Sys;
+import org.lwjgl.opengl.*;
+
+public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
+
+    private static final Logger logger = Logger.getLogger(LwjglOffscreenBuffer.class.getName());
+    private Pbuffer pbuffer;
+    protected AtomicBoolean needClose = new AtomicBoolean(false);
+    private int width;
+    private int height;
+    private PixelFormat pixelFormat;
+
+    protected void initInThread(){
+        if ((Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) == 0){
+            logger.severe("Offscreen surfaces are not supported.");
+            return;
+        }
+
+	   int samples = 0;
+         if (settings.getSamples() > 1){
+              samples = settings.getSamples();
+        }
+        pixelFormat = new PixelFormat(settings.getBitsPerPixel(),
+                                      0,
+                                      settings.getDepthBits(),
+                                      settings.getStencilBits(),
+                                      settings.getSamples());
+        
+        width = settings.getWidth();
+        height = settings.getHeight();
+        try{
+            Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+                public void uncaughtException(Thread thread, Throwable thrown) {
+                    listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
+                }
+            });
+
+            pbuffer = new Pbuffer(width, height, pixelFormat, null, null, createContextAttribs());
+            pbuffer.makeCurrent();
+
+            renderable.set(true);
+
+            logger.info("Offscreen buffer created.");
+            printContextInitInfo();
+        } catch (LWJGLException ex){
+            listener.handleError("Failed to create display", ex);
+        } finally {
+            // TODO: It is possible to avoid "Failed to find pixel format"
+            // error here by creating a default display.
+        }
+        super.internalCreate();
+        listener.initialize();
+    }
+
+    protected boolean checkGLError(){
+        try {
+            Util.checkGLError();
+        } catch (OpenGLException ex){
+            listener.handleError("An OpenGL error has occured!", ex);
+        }
+        // NOTE: Always return true since this is used in an "assert" statement
+        return true;
+    }
+
+    protected void runLoop(){
+        if (!created.get())
+            throw new IllegalStateException();
+
+        if (pbuffer.isBufferLost()){
+            pbuffer.destroy();
+            try{
+                pbuffer = new Pbuffer(width, height, pixelFormat, null);
+                pbuffer.makeCurrent();
+            }catch (LWJGLException ex){
+                listener.handleError("Failed to restore pbuffer content", ex);
+            }
+        }
+
+        listener.update();
+        assert checkGLError();
+        
+        renderer.onFrame();
+        
+        int frameRate = settings.getFrameRate();
+        if (frameRate >= 1){
+            Display.sync(frameRate);
+        }
+    }
+
+    protected void deinitInThread(){
+        renderable.set(false);
+        
+        listener.destroy();
+        renderer.cleanup();
+        pbuffer.destroy();
+        logger.info("Offscreen buffer destroyed.");
+    }
+
+    public void run(){
+        logger.log(Level.INFO, "Using LWJGL {0}", Sys.getVersion());
+        initInThread();
+        while (!needClose.get()){
+            runLoop();
+        }
+        deinitInThread();
+    }
+
+    public void destroy(boolean waitFor){
+        needClose.set(true);
+        if (waitFor)
+            waitFor(false);
+    }
+
+    public void create(boolean waitFor){
+        if (created.get()){
+            logger.warning("create() called when pbuffer is already created!");
+            return;
+        }
+
+        new Thread(this, "LWJGL Renderer Thread").start();
+        if (waitFor)
+            waitFor(true);
+    }
+
+    public void restart() {
+    }
+
+    public void setAutoFlushFrames(boolean enabled){
+    }
+
+    public Type getType() {
+        return Type.OffscreenSurface;
+    }
+
+    public MouseInput getMouseInput() {
+        return new DummyMouseInput();
+    }
+
+    public KeyInput getKeyInput() {
+        return new DummyKeyInput();
+    }
+
+    public JoyInput getJoyInput() {
+        return null;
+    }
+    
+    public TouchInput getTouchInput() {
+        return null;
+    }
+
+    public void setTitle(String title) {
+    }
+
+}