* 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. 30
      engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglAbstractDisplay.java
  5. 211
      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 lastLimit = 0;
protected int stride = 0;
protected int components = 0;
@ -233,7 +234,6 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable {
*/
protected transient int componentsLength = 0;
protected Buffer data = null;
protected transient ByteBuffer mappedData;
protected Usage usage;
protected Type bufType;
protected Format format;
@ -303,14 +303,6 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable {
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
* information.
@ -403,6 +395,7 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable {
this.usage = usage;
this.format = format;
this.componentsLength = components * format.getComponentSize();
this.lastLimit = data.limit();
setUpdateNeeded();
}
@ -424,8 +417,9 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable {
}
// will force renderer to call glBufferData again
if (this.data.capacity() != data.capacity()){
if (this.data.getClass() != data.getClass() || data.limit() != lastLimit){
dataSizeChanged = true;
lastLimit = data.limit();
}
this.data = data;
setUpdateNeeded();
@ -696,10 +690,13 @@ public class VertexBuffer extends GLObject implements Savable, Cloneable {
}
}
@Override
public VertexBuffer clone(){
// NOTE: Superclass GLObject automatically creates shallow clone
// e.g re-use ID.
VertexBuffer vb = (VertexBuffer) super.clone();
vb.handleRef = new Object();
vb.id = -1;
if (data != null)
vb.updateData(BufferUtils.clone(data));

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

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

@ -81,7 +81,10 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
*/
protected abstract void createContext(AppSettings settings) throws LWJGLException;
/**
* Destroy the context.
*/
protected abstract void destroyContext();
/**
* 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);
if (renderable.get()) // assumes createContext will set this flag
printContextInitInfo();
printContextInitInfo();
created.set(true);
} catch (Exception ex){
@ -137,6 +142,9 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
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()){
assert checkGLError();
@ -158,22 +166,16 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
if (frameRate > 0)
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.
*/
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();
logger.info("Display destroyed.");

@ -43,10 +43,10 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Controllers;
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 {
@ -63,67 +63,72 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
private Thread renderThread;
private boolean runningFirstTime = true;
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();
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 && 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;
}
if (renderThread == null){
logger.log(Level.INFO, "EDT: Creating OGL thread.");
logger.log(Level.INFO, "EDT: Notifying OGL that canvas is visible..");
needRestoreCanvas.set(true);
// Also set some settings on the canvas here.
// So we don't do it outside the AWT thread.
canvas.setFocusable(true);
canvas.setIgnoreRepaint(true);
// NOTE: no need to wait for OGL to initialize the canvas,
// it can happen at any time.
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;
}
logger.log(Level.INFO, "EDT: Notifying OGL that canvas is visible..");
needRestoreCanvas.set(true);
// 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
// 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();
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
@ -150,6 +155,8 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
@Override
public void restart() {
frameRate = settings.getFrameRate();
// TODO: Handle other cases, like change of pixel format, etc.
}
public Canvas getCanvas(){
@ -194,7 +201,6 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
private void pauseCanvas(){
mouseActive = Mouse.isCreated();
keyboardActive = Keyboard.isCreated();
joyActive = Controllers.isCreated();
if (mouseActive && Mouse.isGrabbed()){
Mouse.setGrabbed(false);
@ -205,13 +211,11 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
Mouse.destroy();
if (keyboardActive)
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);
destroyContext();
}
/**
@ -234,9 +238,10 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
createContext(settings);
// must call after createContext, as renderer might be null
renderer.resetGLObjects();
// renderer.resetGLObjects();
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()){
try {
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
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();
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){
// 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 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?)
}
}

@ -115,8 +115,6 @@ public abstract class LwjglContext implements JmeContext {
}
protected void initContextFirstTime(){
assert renderable.get();
if (GLContext.getCapabilities().OpenGL20){
renderer = new LwjglRenderer();
}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){
if (created.get()){
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.system.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.Sys;
@ -81,7 +82,7 @@ public class LwjglTimer extends Timer {
reset();
//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() {

@ -1,11 +1,7 @@
package com.jme3.math;
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;
public class TrigonometryTest {

Loading…
Cancel
Save