git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9021 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
9c6ca69b34
commit
bd828d629c
@ -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?)
|
||||
} |
||||
} |
||||
} |
||||
|
@ -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) { |
||||
} |
||||
|
||||
} |
||||
|
Loading…
Reference in new issue