* 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
3.0
sha..rd 14 years ago
parent 4b52dba7f8
commit 0d0454f248
  1. 17
      engine/src/core/com/jme3/scene/VertexBuffer.java
  2. 3
      engine/src/core/com/jme3/scene/debug/WireFrustum.java
  3. 2
      engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java
  4. 26
      engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglAbstractDisplay.java
  5. 119
      engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglCanvas.java
  6. 2
      engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglContext.java
  7. 10
      engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglDisplay.java
  8. 3
      engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglTimer.java
  9. 4
      engine/test/com/jme3/math/TrigonometryTest.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));

@ -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();
} }

@ -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() {

@ -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,9 +100,11 @@ 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);
@ -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,7 +166,8 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
if (frameRate > 0) if (frameRate > 0)
Display.sync(frameRate); Display.sync(frameRate);
if (renderable.get() && autoFlush) // Subclasses just call GLObjectManager clean up objects here
// it is safe .. for now.
renderer.onFrame(); renderer.onFrame();
} }
@ -166,14 +175,7 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
* De-initialize in the OpenGL thread. * De-initialize in the OpenGL thread.
*/ */
protected void deinitInThread(){ protected void deinitInThread(){
if (Display.isCreated()){ destroyContext();
renderer.cleanup();
Display.destroy();
}else{
// If using canvas temporary closing, the display would
// be closed at this point
renderer.resetGLObjects();
}
listener.destroy(); listener.destroy();
logger.info("Display destroyed."); logger.info("Display destroyed.");

@ -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,12 +63,11 @@ 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(){ private Pbuffer pbuffer;
super();
canvas = new Canvas(){ private class GLCanvas extends Canvas {
@Override @Override
public void addNotify(){ public void addNotify(){
super.addNotify(); super.addNotify();
@ -79,6 +78,11 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
if (renderThread == null){ if (renderThread == null){
logger.log(Level.INFO, "EDT: Creating OGL thread."); 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 = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread");
renderThread.start(); renderThread.start();
}else if (needClose.get()){ }else if (needClose.get()){
@ -102,7 +106,7 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
// We must tell GL context to shutdown and wait for it to // We must tell GL context to shutdown and wait for it to
// shutdown, otherwise, issues will occur. // shutdown, otherwise, issues will occur.
logger.log(Level.INFO, "EDT: Sending destroy request.."); logger.log(Level.INFO, "EDT: Notifying OGL that canvas is about to become invisible..");
needDestroyCanvas.set(true); needDestroyCanvas.set(true);
try { try {
actionRequiredBarrier.await(); actionRequiredBarrier.await();
@ -112,7 +116,7 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
logger.log(Level.SEVERE, "EDT: Broken barrier! ", ex); logger.log(Level.SEVERE, "EDT: Broken barrier! ", ex);
} }
logger.log(Level.INFO, "EDT: Acknowledged receipt of destroy request!"); logger.log(Level.INFO, "EDT: Acknowledged receipt of canvas death");
// GL context is dead at this point // GL context is dead at this point
// Reset barrier for future use // Reset barrier for future use
@ -120,10 +124,11 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
super.removeNotify(); super.removeNotify();
} }
}; }
canvas.setFocusable(true); public LwjglCanvas(){
canvas.setIgnoreRepaint(true); 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)"); logger.log(Level.INFO, "OGL: Canvas will become invisible! Destroying ..");
Display.destroy();
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()) try {
return; // First create the pbuffer, if it is needed.
makePbufferAvailable();
Display.setVSyncEnabled(settings.isVSync()); if (renderable.get()){
if (pbuffer.isCurrent()){
pbuffer.releaseContext();
}
try{ Display.setVSyncEnabled(settings.isVSync());
Display.setParent(canvas); Display.setParent(canvas);
PixelFormat pf = new PixelFormat(settings.getBitsPerPixel(), PixelFormat pf = new PixelFormat(settings.getBitsPerPixel(),
0, 0,
settings.getDepthBits(), settings.getDepthBits(),
settings.getStencilBits(), settings.getStencilBits(),
settings.getSamples()); settings.getSamples());
Display.create(pf); Display.create(pf, pbuffer);
Display.makeCurrent(); 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){ } catch (LWJGLException ex) {
listener.handleError("Failed to parent canvas to display", ex); listener.handleError("Failed to initialize OpenGL context", ex);
// TODO: Fix deadlock that happens after the error (throw runtime exception?)
} }
} }

@ -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{

@ -134,6 +134,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()){
logger.warning("create() called when display is already created!"); logger.warning("create() called when display is already created!");

@ -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() {

@ -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 {

Loading…
Cancel
Save