瀏覽代碼

* Canvas is now using pbuffer workaround, allowing renderer to acquire renderer capabilities even if the canvas is not visible yet.
* Handling of context destruction is now handled individually for displays and canvases.
For canvas, this allows it to destroy the pbuffer in addition to the display.
* VertexBuffer now has better detection for data size changes, might prevent GL errors in certain cases. NOTE: VertexBuffer.updateData() is generally more stable than VertexBuffer.setUpdateNeeded(). Refrain from using setUpdateNeeded() .. its an internal call anyway. Using it directly could cause GL errors.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7374 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

sha..rd 14 年之前
父節點
當前提交
0d0454f248

+ 7 - 10
engine/src/core/com/jme3/scene/VertexBuffer.java

@@ -225,6 +225,7 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable {
     }
     }
 
 
     protected int offset = 0;
     protected int offset = 0;
+    protected int lastLimit = 0;
     protected int stride = 0;
     protected int stride = 0;
     protected int components = 0;
     protected int components = 0;
 
 
@@ -233,7 +234,6 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable {
      */
      */
     protected transient int componentsLength = 0;
     protected transient int componentsLength = 0;
     protected Buffer data = null;
     protected Buffer data = null;
-    protected transient ByteBuffer mappedData;
     protected Usage usage;
     protected Usage usage;
     protected Type bufType;
     protected Type bufType;
     protected Format format;
     protected Format format;
@@ -303,14 +303,6 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable {
         return data;
         return data;
     }
     }
 
 
-    public ByteBuffer getMappedData() {
-        return mappedData;
-    }
-
-    public void setMappedData(ByteBuffer mappedData) {
-        this.mappedData = mappedData;
-    }
-
     /**
     /**
      * @return The usage of this buffer. See {@link Usage} for more
      * @return The usage of this buffer. See {@link Usage} for more
      * information.
      * information.
@@ -403,6 +395,7 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable {
         this.usage = usage;
         this.usage = usage;
         this.format = format;
         this.format = format;
         this.componentsLength = components * format.getComponentSize();
         this.componentsLength = components * format.getComponentSize();
+        this.lastLimit = data.limit();
         setUpdateNeeded();
         setUpdateNeeded();
     }
     }
 
 
@@ -424,8 +417,9 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable {
         }
         }
 
 
         // will force renderer to call glBufferData again
         // will force renderer to call glBufferData again
-        if (this.data.capacity() != data.capacity()){
+        if (this.data.getClass() != data.getClass() || data.limit() != lastLimit){
             dataSizeChanged = true;
             dataSizeChanged = true;
+            lastLimit = data.limit();
         }
         }
         this.data = data;
         this.data = data;
         setUpdateNeeded();
         setUpdateNeeded();
@@ -696,10 +690,13 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable {
         }
         }
     }
     }
 
 
+    @Override
     public VertexBuffer clone(){
     public VertexBuffer clone(){
         // NOTE: Superclass GLObject automatically creates shallow clone
         // NOTE: Superclass GLObject automatically creates shallow clone
         // e.g re-use ID.
         // e.g re-use ID.
         VertexBuffer vb = (VertexBuffer) super.clone();
         VertexBuffer vb = (VertexBuffer) super.clone();
+        vb.handleRef = new Object();
+        vb.id = -1;
         if (data != null)
         if (data != null)
             vb.updateData(BufferUtils.clone(data));
             vb.updateData(BufferUtils.clone(data));
         
         

+ 1 - 2
engine/src/core/com/jme3/scene/debug/WireFrustum.java

@@ -73,7 +73,6 @@ public class WireFrustum extends Mesh {
             return;
             return;
         }
         }
 
 
-
         FloatBuffer b = BufferUtils.createFloatBuffer(points);
         FloatBuffer b = BufferUtils.createFloatBuffer(points);
         FloatBuffer a = (FloatBuffer) vb.getData();
         FloatBuffer a = (FloatBuffer) vb.getData();
         b.rewind();
         b.rewind();
@@ -81,7 +80,7 @@ public class WireFrustum extends Mesh {
         a.put(b);
         a.put(b);
         a.rewind();
         a.rewind();
 
 
-        vb.setUpdateNeeded();
+        vb.updateData(a);
         
         
         updateBound();
         updateBound();
     }
     }

+ 1 - 1
engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java

@@ -417,9 +417,9 @@ public class LwjglRenderer implements Renderer {
     public void resetGLObjects() {
     public void resetGLObjects() {
         objManager.resetObjects();
         objManager.resetObjects();
         statistics.clearMemory();
         statistics.clearMemory();
+        context.reset();
         boundShader = null;
         boundShader = null;
         lastFb = null;
         lastFb = null;
-        context.reset();
     }
     }
 
 
     public void cleanup() {
     public void cleanup() {

+ 16 - 14
engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglAbstractDisplay.java

@@ -81,7 +81,10 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
      */
      */
     protected abstract void createContext(AppSettings settings) throws LWJGLException;
     protected abstract void createContext(AppSettings settings) throws LWJGLException;
 
 
-    
+    /**
+     * Destroy the context.
+     */
+    protected abstract void destroyContext();
 
 
     /**
     /**
      * Does LWJGL display initialization in the OpenGL thread
      * Does LWJGL display initialization in the OpenGL thread
@@ -97,10 +100,12 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
                 });
                 });
             }
             }
 
 
-            // For canvas, this wont happen until its initialized.
+            // For canvas, this will create a pbuffer,
+            // allowing us to query information.
+            // When the canvas context becomes available, it will
+            // be replaced seamlessly.
             createContext(settings);
             createContext(settings);
-            if (renderable.get()) // assumes createContext will set this flag
-                printContextInitInfo();
+            printContextInitInfo();
 
 
             created.set(true);
             created.set(true);
         } catch (Exception ex){
         } catch (Exception ex){
@@ -137,6 +142,9 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
 
 
         listener.update();
         listener.update();
 
 
+        // All this does is call swap buffers
+        // If the canvas is not active, there's no need to waste time
+        // doing that ..
         if (renderable.get()){
         if (renderable.get()){
             assert checkGLError();
             assert checkGLError();
 
 
@@ -158,22 +166,16 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
         if (frameRate > 0)
         if (frameRate > 0)
             Display.sync(frameRate);
             Display.sync(frameRate);
 
 
-        if (renderable.get() && autoFlush)
-            renderer.onFrame();
+        // Subclasses just call GLObjectManager clean up objects here
+        // it is safe .. for now.
+        renderer.onFrame();
     }
     }
 
 
     /**
     /**
      * De-initialize in the OpenGL thread.
      * De-initialize in the OpenGL thread.
      */
      */
     protected void deinitInThread(){
     protected void deinitInThread(){
-        if (Display.isCreated()){
-            renderer.cleanup();
-            Display.destroy();
-        }else{
-            // If using canvas temporary closing, the display would
-            // be closed at this point
-            renderer.resetGLObjects();
-        }
+        destroyContext();
 
 
         listener.destroy();
         listener.destroy();
         logger.info("Display destroyed.");
         logger.info("Display destroyed.");

+ 146 - 73
engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglCanvas.java

@@ -43,10 +43,10 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.logging.Logger;
 import javax.swing.SwingUtilities;
 import javax.swing.SwingUtilities;
 import org.lwjgl.LWJGLException;
 import org.lwjgl.LWJGLException;
-import org.lwjgl.input.Controllers;
 import org.lwjgl.input.Keyboard;
 import org.lwjgl.input.Keyboard;
 import org.lwjgl.input.Mouse;
 import org.lwjgl.input.Mouse;
 import org.lwjgl.opengl.Display;
 import org.lwjgl.opengl.Display;
+import org.lwjgl.opengl.Pbuffer;
 import org.lwjgl.opengl.PixelFormat;
 import org.lwjgl.opengl.PixelFormat;
 
 
 public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContext {
 public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContext {
@@ -63,67 +63,72 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
     private Thread renderThread;
     private Thread renderThread;
     private boolean runningFirstTime = true;
     private boolean runningFirstTime = true;
     private boolean mouseWasGrabbed = false;
     private boolean mouseWasGrabbed = false;
-    private boolean mouseActive, keyboardActive, joyActive;
+    private boolean mouseActive, keyboardActive;
 
 
-    public LwjglCanvas(){
-        super();
+    private Pbuffer pbuffer;
 
 
-        canvas = new 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.");
-                    
-                    renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread");
-                    renderThread.start();
-                }else if (needClose.get()){
-                    return;
-                }
+    private class GLCanvas extends Canvas {
+        @Override
+        public void addNotify(){
+            super.addNotify();
+
+            if (renderThread != null && renderThread.getState() == Thread.State.TERMINATED)
+                return; // already destroyed.
 
 
-                logger.log(Level.INFO, "EDT: Notifying OGL that canvas is visible..");
-                needRestoreCanvas.set(true);
+            if (renderThread == null){
+                logger.log(Level.INFO, "EDT: Creating OGL thread.");
 
 
-                // NOTE: no need to wait for OGL to initialize the canvas,
-                // it can happen at any time.
+                // 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;
             }
             }
 
 
-            @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: Sending destroy request..");
-                needDestroyCanvas.set(true);
-                try {
-                    actionRequiredBarrier.await();
-                } catch (InterruptedException ex) {
-                    logger.log(Level.SEVERE, "EDT: Interrupted! ", ex);
-                } catch (BrokenBarrierException ex){
-                    logger.log(Level.SEVERE, "EDT: Broken barrier! ", ex);
-                }
-                
-                logger.log(Level.INFO, "EDT: Acknowledged receipt of destroy request!");
-                // GL context is dead at this point
+            logger.log(Level.INFO, "EDT: Notifying OGL that canvas is visible..");
+            needRestoreCanvas.set(true);
 
 
-                // Reset barrier for future use
-                actionRequiredBarrier.reset();
+            // NOTE: no need to wait for OGL to initialize the canvas,
+            // it can happen at any time.
+        }
 
 
+        @Override
+        public void removeNotify(){
+            if (needClose.get()){
+                logger.log(Level.INFO, "EDT: Application is stopped. Not restoring canvas.");
                 super.removeNotify();
                 super.removeNotify();
+                return;
             }
             }
-        };
-        
-        canvas.setFocusable(true);
-        canvas.setIgnoreRepaint(true);
+
+            // We must tell GL context to shutdown and wait for it to
+            // shutdown, otherwise, issues will occur.
+            logger.log(Level.INFO, "EDT: Notifying OGL that canvas is about to become invisible..");
+            needDestroyCanvas.set(true);
+            try {
+                actionRequiredBarrier.await();
+            } catch (InterruptedException ex) {
+                logger.log(Level.SEVERE, "EDT: Interrupted! ", ex);
+            } catch (BrokenBarrierException ex){
+                logger.log(Level.SEVERE, "EDT: Broken barrier! ", ex);
+            }
+
+            logger.log(Level.INFO, "EDT: Acknowledged receipt of canvas death");
+            // GL context is dead at this point
+
+            // Reset barrier for future use
+            actionRequiredBarrier.reset();
+
+            super.removeNotify();
+        }
+    }
+
+    public LwjglCanvas(){
+        super();
+        canvas = new GLCanvas();
     }
     }
 
 
     @Override
     @Override
@@ -150,6 +155,8 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
 
 
     @Override
     @Override
     public void restart() {
     public void restart() {
+        frameRate = settings.getFrameRate();
+        // TODO: Handle other cases, like change of pixel format, etc.
     }
     }
 
 
     public Canvas getCanvas(){
     public Canvas getCanvas(){
@@ -194,7 +201,6 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
     private void pauseCanvas(){
     private void pauseCanvas(){
         mouseActive = Mouse.isCreated();
         mouseActive = Mouse.isCreated();
         keyboardActive = Keyboard.isCreated();
         keyboardActive = Keyboard.isCreated();
-        joyActive = Controllers.isCreated();
 
 
         if (mouseActive && Mouse.isGrabbed()){
         if (mouseActive && Mouse.isGrabbed()){
             Mouse.setGrabbed(false);
             Mouse.setGrabbed(false);
@@ -205,13 +211,11 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
             Mouse.destroy();
             Mouse.destroy();
         if (keyboardActive)
         if (keyboardActive)
             Keyboard.destroy();
             Keyboard.destroy();
-        if (joyActive)
-            Controllers.destroy();
-
-        logger.log(Level.INFO, "OGL: Destroying display (temporarily)");
-        Display.destroy();
 
 
+        logger.log(Level.INFO, "OGL: Canvas will become invisible! Destroying ..");
+        
         renderable.set(false);
         renderable.set(false);
+        destroyContext();
     }
     }
 
 
     /**
     /**
@@ -234,9 +238,10 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
         createContext(settings);
         createContext(settings);
         
         
         // must call after createContext, as renderer might be null
         // must call after createContext, as renderer might be null
-        renderer.resetGLObjects();
+//        renderer.resetGLObjects();
 
 
         logger.log(Level.INFO, "OGL: Waiting for display to become active..");
         logger.log(Level.INFO, "OGL: Waiting for display to become active..");
+        // NOTE: A deadlock will happen here if createContext had an exception
         while (!Display.isCreated()){
         while (!Display.isCreated()){
             try {
             try {
                 Thread.sleep(10);
                 Thread.sleep(10);
@@ -269,33 +274,101 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
         });
         });
     }
     }
 
 
+    /**
+     * Makes sure the pbuffer is available and ready for use
+     */
+    protected void makePbufferAvailable() throws LWJGLException{
+        if (pbuffer == null || pbuffer.isBufferLost()){
+            if (pbuffer != null && pbuffer.isBufferLost()){
+                pbuffer.releaseContext();
+                pbuffer.destroy();
+            }
+            pbuffer = new Pbuffer(1, 1, new PixelFormat(0, 0, 0), null);
+            logger.log(Level.INFO, "OGL: Pbuffer has been created");
+        }
+    }
+
+    /**
+     * This is called:
+     * 1) When the context thread ends
+     * 2) Any time the canvas becomes non-displayable
+     */
+    protected void destroyContext(){
+        try {
+            renderer.resetGLObjects();
+            if (Display.isCreated()){
+                Display.releaseContext();
+                Display.destroy();
+            }
+        } catch (LWJGLException ex) {
+            listener.handleError("Failed to destroy context", ex);
+        }
+
+        try {
+            // 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();
+
+                // pbuffer is now available, make it current
+                pbuffer.makeCurrent();
+            }else{
+                // The context thread is no longer running.
+                // Destroy pbuffer.
+                if (pbuffer != null){
+                    pbuffer.destroy();
+                }
+            }
+        } 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
     @Override
     protected void createContext(AppSettings settings) {
     protected void createContext(AppSettings settings) {
         // In case canvas is not visible, we still take framerate
         // In case canvas is not visible, we still take framerate
         // from settings to prevent "100% CPU usage"
         // from settings to prevent "100% CPU usage"
         frameRate = settings.getFrameRate();
         frameRate = settings.getFrameRate();
         
         
-        if (!renderable.get())
-            return;
+        try {
+            // First create the pbuffer, if it is needed.
+            makePbufferAvailable();
 
 
-        Display.setVSyncEnabled(settings.isVSync());
+            if (renderable.get()){
+                if (pbuffer.isCurrent()){
+                    pbuffer.releaseContext();
+                }
 
 
-        try{
-            Display.setParent(canvas);
-            PixelFormat pf = new PixelFormat(settings.getBitsPerPixel(),
-                                             0,
-                                             settings.getDepthBits(),
-                                             settings.getStencilBits(),
-                                             settings.getSamples());
-            Display.create(pf);
-            Display.makeCurrent();
+                Display.setVSyncEnabled(settings.isVSync());
+                Display.setParent(canvas);
+                PixelFormat pf = new PixelFormat(settings.getBitsPerPixel(),
+                        0,
+                        settings.getDepthBits(),
+                        settings.getStencilBits(),
+                        settings.getSamples());
+                Display.create(pf, pbuffer);
+                Display.makeCurrent();
+            }else{
+                pbuffer.makeCurrent();
+            }
+            // At this point, the OpenGL context is active.
 
 
             if (runningFirstTime){
             if (runningFirstTime){
+                // THIS is the part that creates the renderer.
+                // It must always be called, now that we have the pbuffer workaround.
                 initContextFirstTime();
                 initContextFirstTime();
                 runningFirstTime = false;
                 runningFirstTime = false;
             }
             }
-        }catch (LWJGLException ex){
-            listener.handleError("Failed to parent canvas to display", ex);
+        } catch (LWJGLException ex) {
+            listener.handleError("Failed to initialize OpenGL context", ex);
+            // TODO: Fix deadlock that happens after the error (throw runtime exception?)
         }
         }
     }
     }
 
 

+ 0 - 2
engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglContext.java

@@ -115,8 +115,6 @@ public abstract class LwjglContext implements JmeContext {
     }
     }
 
 
     protected void initContextFirstTime(){
     protected void initContextFirstTime(){
-        assert renderable.get();
-
         if (GLContext.getCapabilities().OpenGL20){
         if (GLContext.getCapabilities().OpenGL20){
             renderer = new LwjglRenderer();
             renderer = new LwjglRenderer();
         }else{
         }else{

+ 10 - 0
engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglDisplay.java

@@ -133,6 +133,16 @@ public class LwjglDisplay extends LwjglAbstractDisplay {
             }
             }
         }
         }
     }
     }
+    
+    protected void destroyContext(){
+        try {
+            renderer.cleanup();
+            Display.releaseContext();
+            Display.destroy();
+        } catch (LWJGLException ex) {
+            listener.handleError("Failed to destroy context", ex);
+        }
+    }
 
 
     public void create(boolean waitFor){
     public void create(boolean waitFor){
         if (created.get()){
         if (created.get()){

+ 2 - 1
engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglTimer.java

@@ -34,6 +34,7 @@ package com.jme3.system.lwjgl;
 
 
 import com.jme3.math.FastMath;
 import com.jme3.math.FastMath;
 import com.jme3.system.Timer;
 import com.jme3.system.Timer;
+import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.logging.Logger;
 import org.lwjgl.Sys;
 import org.lwjgl.Sys;
 
 
@@ -81,7 +82,7 @@ public class LwjglTimer extends Timer {
         reset();
         reset();
 
 
         //print timer resolution info
         //print timer resolution info
-        logger.info("Timer resolution: " + LWJGL_TIMER_RES + " ticks per second");
+        logger.log(Level.INFO, "Timer resolution: {0} ticks per second", LWJGL_TIMER_RES);
     }
     }
 
 
     public void reset() {
     public void reset() {

+ 0 - 4
engine/test/com/jme3/math/TrigonometryTest.java

@@ -1,11 +1,7 @@
 package com.jme3.math;
 package com.jme3.math;
 
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 
-import com.jme3.math.FastMath;
-import com.jme3.math.Vector2f;
 import org.junit.Test;
 import org.junit.Test;
 
 
 public class TrigonometryTest {
 public class TrigonometryTest {