* Big refactoring of LWJGL display system, mainly to support updating the main loop without a render context or input devices being available.

* Added test that demonstrates above functionality, by starting Application without attaching the canvas, and then constantly attaching and detaching canvas from a frame.
 * Deleted deprecated methods in JmeContext 
 * Deleted deprecated class LwjglJoyInput
 * Audio renderer will not attempt to initialize OpenAL twice if already initialized

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7078 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
sha..rd 14 years ago
parent eaeb1de436
commit 61aea1e2c5
  1. 12
      engine/src/core/com/jme3/app/Application.java
  2. 5
      engine/src/core/com/jme3/app/SimpleApplication.java
  3. 1
      engine/src/core/com/jme3/renderer/RenderManager.java
  4. 30
      engine/src/core/com/jme3/system/JmeContext.java
  5. 5
      engine/src/core/com/jme3/system/NullContext.java
  6. 4
      engine/src/lwjgl-oal/com/jme3/audio/lwjgl/LwjglAudioRenderer.java
  7. 181
      engine/src/lwjgl-ogl/com/jme3/input/lwjgl/LwjglJoyInput.java
  8. 20
      engine/src/lwjgl-ogl/com/jme3/input/lwjgl/LwjglKeyInput.java
  9. 24
      engine/src/lwjgl-ogl/com/jme3/input/lwjgl/LwjglMouseInput.java
  10. 113
      engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglAbstractDisplay.java
  11. 233
      engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglCanvas.java
  12. 88
      engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglContext.java
  13. 92
      engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglDisplay.java
  14. 38
      engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java
  15. 14
      engine/src/test/jme3test/awt/TestSafeCanvas.java

@ -300,12 +300,16 @@ public class Application implements SystemListener {
return cam; return cam;
} }
/**
* Starts the application as a display.
*/
public void start(){ public void start(){
start(JmeContext.Type.Display); start(JmeContext.Type.Display);
} }
/** /**
* Starts the application. Creating a display and running the main loop. * Starts the application. Creating a rendering context and executing
* the main loop in a separate thread.
*/ */
public void start(JmeContext.Type contextType){ public void start(JmeContext.Type contextType){
if (context != null && context.isCreated()){ if (context != null && context.isCreated()){
@ -423,6 +427,10 @@ public class Application implements SystemListener {
context.destroy(false); context.destroy(false);
} }
/**
* Enqueues a task/callable object to execute in the jME3
* rendering thread.
*/
public <V> Future<V> enqueue(Callable<V> callable) { public <V> Future<V> enqueue(Callable<V> callable) {
AppTask<V> task = new AppTask<V>(callable); AppTask<V> task = new AppTask<V>(callable);
taskQueue.add(task); taskQueue.add(task);
@ -495,6 +503,4 @@ public class Application implements SystemListener {
return viewPort; return viewPort;
} }
} }

@ -249,7 +249,9 @@ public abstract class SimpleApplication extends Application {
// render states // render states
stateManager.render(renderManager); stateManager.render(renderManager);
renderManager.render(tpf); if (context.isRenderable()){
renderManager.render(tpf);
}
simpleRender(renderManager); simpleRender(renderManager);
stateManager.postRender(); stateManager.postRender();
} }
@ -257,7 +259,6 @@ public abstract class SimpleApplication extends Application {
public void setDisplayFps(boolean show) { public void setDisplayFps(boolean show) {
showFps = show; showFps = show;
fpsText.setCullHint(show ? CullHint.Never : CullHint.Always); fpsText.setCullHint(show ? CullHint.Never : CullHint.Always);
} }
public void setDisplayStatView(boolean show) { public void setDisplayStatView(boolean show) {

@ -108,7 +108,6 @@ public class RenderManager {
public RenderManager(Renderer renderer) { public RenderManager(Renderer renderer) {
this.renderer = renderer; this.renderer = renderer;
this.shader = renderer.getCaps().contains(Caps.GLSL100); this.shader = renderer.getCaps().contains(Caps.GLSL100);
} }
public ViewPort getPreView(String viewName) { public ViewPort getPreView(String viewName) {

@ -48,16 +48,19 @@ public interface JmeContext {
public enum Type { public enum Type {
/** /**
* A display can represent a windowed or a fullscreen-exclusive display. * A display can represent a windowed or a fullscreen-exclusive display.
* If windowed, the graphics are rendered to a new onscreen surface * If windowed, the graphics are rendered to a new on-screen surface
* enclosed in a system defined by the operating system. Implementations * enclosed in a window defined by the operating system. Implementations
* are encourged to not use AWT or Swing to create the OpenGL display * are encouraged to not use AWT or Swing to create the OpenGL display
* but rather use native operating system functions to set up a native * but rather use native operating system functions to set up a native
* display with the windowing system. * display with the windowing system.
*/ */
Display, Display,
/** /**
* * A canvas type context makes a rendering surface available as an
* AWT {@link java.awt.Canvas} object that can be embedded in a Swing/AWT
* frame. To retrieve the Canvas object, you should cast the context
* to {@link JmeCanvasContext}.
*/ */
Canvas, Canvas,
@ -140,16 +143,17 @@ public interface JmeContext {
public boolean isCreated(); public boolean isCreated();
/** /**
* @param enabled If enabled, the context will automatically flush * @return True if the context contains a valid render surface,
* frames to the video card (swap buffers) after an update cycle. * if any of the rendering methods in {@link Renderer} are called
* while this is <code>false</code>, then the result is undefined.
*/ */
public void setAutoFlushFrames(boolean enabled); public boolean isRenderable();
/** /**
* Creates the context and makes it active. * @param enabled If enabled, the context will automatically flush
* frames to the video card (swap buffers) after an update cycle.
*/ */
@Deprecated public void setAutoFlushFrames(boolean enabled);
public void create();
/** /**
* Creates the context and makes it active. * Creates the context and makes it active.
@ -164,12 +168,6 @@ public interface JmeContext {
*/ */
public void restart(); public void restart();
/**
* Destroys the context completely, making it inactive.
*/
@Deprecated
public void destroy();
/** /**
* Destroys the context completely, making it inactive. * Destroys the context completely, making it inactive.
* *

@ -218,4 +218,9 @@ public class NullContext implements JmeContext, Runnable {
return timer; return timer;
} }
public boolean isRenderable() {
return true; // Doesn't really matter if true or false. Either way
// RenderManager won't render anything.
}
} }

@ -154,7 +154,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
public void initInThread(){ public void initInThread(){
try{ try{
AL.create(); if (!AL.isCreated()){
AL.create();
}
}catch (OpenALException ex){ }catch (OpenALException ex){
logger.log(Level.SEVERE, "Failed to load audio library", ex); logger.log(Level.SEVERE, "Failed to load audio library", ex);
audioDisabled = true; audioDisabled = true;

@ -1,181 +0,0 @@
/*
* 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.input.lwjgl;
import com.jme3.input.InputManager;
import com.jme3.input.JoyInput;
import com.jme3.input.Joystick;
import com.jme3.input.RawInputListener;
import com.jme3.input.event.JoyAxisEvent;
import com.jme3.input.event.JoyButtonEvent;
import com.jme3.system.lwjgl.LwjglTimer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Controller;
import org.lwjgl.input.Controllers;
@Deprecated
class LwjglJoyInput implements JoyInput {
private static final Logger logger = Logger.getLogger(LwjglKeyInput.class.getName());
private RawInputListener listener;
private boolean enabled = false;
public void initialize() {
try {
Controllers.create();
if (Controllers.getControllerCount() == 0 || !Controllers.isCreated()){
logger.warning("Joysticks disabled.");
return;
}
logger.info("Joysticks created.");
enabled = true;
} catch (LWJGLException ex) {
logger.log(Level.SEVERE, "Failed to create joysticks", ex);
}
}
public int getJoyCount() {
return Controllers.getControllerCount();
}
public String getJoyName(int joyIndex) {
return Controllers.getController(joyIndex).getName();
}
public int getAxesCount(int joyIndex) {
return Controllers.getController(joyIndex).getAxisCount();
}
public int getButtonCount(int joyIndex) {
return Controllers.getController(joyIndex).getButtonCount();
}
private void printController(Controller c){
System.out.println("Name: "+c.getName());
System.out.println("Index: "+c.getIndex());
System.out.println("Button Count: "+c.getButtonCount());
System.out.println("Axis Count: "+c.getAxisCount());
int buttons = c.getButtonCount();
for (int b = 0; b < buttons; b++) {
System.out.println("Button " + b + " = " + c.getButtonName(b));
}
int axis = c.getAxisCount();
for (int b = 0; b < axis; b++) {
System.out.println("Axis " + b + " = " + c.getAxisName(b));
}
}
public void update() {
if (!enabled)
return;
Controllers.poll();
while (Controllers.next()){
Controller c = Controllers.getEventSource();
if (Controllers.isEventAxis()){
int realAxis = Controllers.getEventControlIndex();
JoyAxisEvent evt = new JoyAxisEvent(c.getIndex(),
realAxis,
c.getAxisValue(realAxis));
listener.onJoyAxisEvent(evt);
}else if (Controllers.isEventPovX()){
JoyAxisEvent evt = new JoyAxisEvent(c.getIndex(),
JoyInput.AXIS_POV_X,
c.getPovX());
listener.onJoyAxisEvent(evt);
}else if (Controllers.isEventPovY()){
JoyAxisEvent evt = new JoyAxisEvent(c.getIndex(),
JoyInput.AXIS_POV_Y,
c.getPovY());
listener.onJoyAxisEvent(evt);
}else if (Controllers.isEventButton()){
int btn = Controllers.getEventControlIndex();
JoyButtonEvent evt = new JoyButtonEvent(c.getIndex(),
btn,
c.isButtonPressed(btn));
listener.onJoyButtonEvent(evt);
}
}
Controllers.clearEvents();
}
public void destroy() {
if (!enabled)
return;
Controllers.destroy();
logger.info("Joysticks destroyed.");
}
public boolean isInitialized() {
if (!enabled)
return false;
return Controllers.isCreated();
}
public void setInputListener(RawInputListener listener) {
this.listener = listener;
}
public long getInputTimeNanos() {
return Sys.getTime() * LwjglTimer.LWJGL_TIME_TO_NANOS;
}
public void setJoyRumble(int joyId, float amount){
}
public Joystick[] loadJoysticks(InputManager inputManager) {
int count = Controllers.getControllerCount();
Joystick[] joysticks = new Joystick[count];
for (int i = 0; i < count; i++){
Controller c = Controllers.getController(i);
Joystick j = new Joystick(inputManager,
this,
i,
c.getName(),
c.getButtonCount(),
c.getAxisCount(),
-1,-1);
joysticks[i] = j;
}
return joysticks;
}
}

@ -35,6 +35,7 @@ package com.jme3.input.lwjgl;
import com.jme3.input.KeyInput; import com.jme3.input.KeyInput;
import com.jme3.input.event.KeyInputEvent; import com.jme3.input.event.KeyInputEvent;
import com.jme3.input.RawInputListener; import com.jme3.input.RawInputListener;
import com.jme3.system.lwjgl.LwjglAbstractDisplay;
import com.jme3.system.lwjgl.LwjglTimer; import com.jme3.system.lwjgl.LwjglTimer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -46,9 +47,18 @@ public class LwjglKeyInput implements KeyInput {
private static final Logger logger = Logger.getLogger(LwjglKeyInput.class.getName()); private static final Logger logger = Logger.getLogger(LwjglKeyInput.class.getName());
private LwjglAbstractDisplay context;
private RawInputListener listener; private RawInputListener listener;
public LwjglKeyInput(LwjglAbstractDisplay context){
this.context = context;
}
public void initialize() { public void initialize() {
if (!context.isRenderable())
return;
try { try {
Keyboard.create(); Keyboard.create();
Keyboard.enableRepeatEvents(true); Keyboard.enableRepeatEvents(true);
@ -58,15 +68,14 @@ public class LwjglKeyInput implements KeyInput {
} }
} }
public boolean isKeyDown(int key){
return Keyboard.isKeyDown(key);
}
public int getKeyCount(){ public int getKeyCount(){
return Keyboard.KEYBOARD_SIZE; return Keyboard.KEYBOARD_SIZE;
} }
public void update() { public void update() {
if (!context.isRenderable())
return;
Keyboard.poll(); Keyboard.poll();
while (Keyboard.next()){ while (Keyboard.next()){
int keyCode = Keyboard.getEventKey(); int keyCode = Keyboard.getEventKey();
@ -81,6 +90,9 @@ public class LwjglKeyInput implements KeyInput {
} }
public void destroy() { public void destroy() {
if (!context.isRenderable())
return;
Keyboard.destroy(); Keyboard.destroy();
logger.info("Keyboard destroyed."); logger.info("Keyboard destroyed.");
} }

@ -36,6 +36,7 @@ import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent; import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.MouseInput; import com.jme3.input.MouseInput;
import com.jme3.input.RawInputListener; import com.jme3.input.RawInputListener;
import com.jme3.system.lwjgl.LwjglAbstractDisplay;
import com.jme3.system.lwjgl.LwjglTimer; import com.jme3.system.lwjgl.LwjglTimer;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -48,6 +49,8 @@ public class LwjglMouseInput implements MouseInput {
private static final Logger logger = Logger.getLogger(LwjglMouseInput.class.getName()); private static final Logger logger = Logger.getLogger(LwjglMouseInput.class.getName());
private LwjglAbstractDisplay context;
private RawInputListener listener; private RawInputListener listener;
private boolean supportHardwareCursor = false; private boolean supportHardwareCursor = false;
@ -55,11 +58,21 @@ public class LwjglMouseInput implements MouseInput {
private int curX, curY, curWheel; private int curX, curY, curWheel;
public LwjglMouseInput(LwjglAbstractDisplay context){
this.context = context;
}
public void initialize() { public void initialize() {
if (!context.isRenderable())
return;
try { try {
Mouse.create(); Mouse.create();
logger.info("Mouse created."); logger.info("Mouse created.");
supportHardwareCursor = (Cursor.getCapabilities() & Cursor.CURSOR_ONE_BIT_TRANSPARENCY) != 0; supportHardwareCursor = (Cursor.getCapabilities() & Cursor.CURSOR_ONE_BIT_TRANSPARENCY) != 0;
// Recall state that was set before initialization
Mouse.setGrabbed(!cursorVisible);
} catch (LWJGLException ex) { } catch (LWJGLException ex) {
logger.log(Level.SEVERE, "Error while creating mouse", ex); logger.log(Level.SEVERE, "Error while creating mouse", ex);
} }
@ -74,6 +87,9 @@ public class LwjglMouseInput implements MouseInput {
} }
public void update() { public void update() {
if (!context.isRenderable())
return;
while (Mouse.next()){ while (Mouse.next()){
int btn = Mouse.getEventButton(); int btn = Mouse.getEventButton();
@ -109,13 +125,19 @@ public class LwjglMouseInput implements MouseInput {
} }
public void destroy() { public void destroy() {
if (!context.isRenderable())
return;
Mouse.destroy(); Mouse.destroy();
logger.info("Mouse destroyed."); logger.info("Mouse destroyed.");
} }
public void setCursorVisible(boolean visible){ public void setCursorVisible(boolean visible){
Mouse.setGrabbed(!visible);
cursorVisible = visible; cursorVisible = visible;
if (!context.isRenderable())
return;
Mouse.setGrabbed(!visible);
} }
public void setInputListener(RawInputListener listener) { public void setInputListener(RawInputListener listener) {

@ -47,16 +47,13 @@ import java.util.logging.Logger;
import org.lwjgl.LWJGLException; import org.lwjgl.LWJGLException;
import org.lwjgl.Sys; import org.lwjgl.Sys;
import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.opengl.OpenGLException; import org.lwjgl.opengl.OpenGLException;
import org.lwjgl.opengl.Util; import org.lwjgl.opengl.Util;
public abstract class LwjglAbstractDisplay extends LwjglContext implements Runnable { public abstract class LwjglAbstractDisplay extends LwjglContext implements Runnable {
private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName()); private static final Logger logger = Logger.getLogger(LwjglAbstractDisplay.class.getName());
protected AtomicBoolean needClose = new AtomicBoolean(false); protected AtomicBoolean needClose = new AtomicBoolean(false);
protected boolean wasActive = false; protected boolean wasActive = false;
protected int frameRate = 0; protected int frameRate = 0;
@ -84,6 +81,8 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
*/ */
protected abstract void createContext(AppSettings settings) throws LWJGLException; protected abstract void createContext(AppSettings settings) throws LWJGLException;
/** /**
* Does LWJGL display initialization in the OpenGL thread * Does LWJGL display initialization in the OpenGL thread
*/ */
@ -97,42 +96,22 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
}); });
} }
// For canvas, this wont happen until its initialized.
createContext(settings); createContext(settings);
// String rendererStr = settings.getString("Renderer"); if (renderable.get()) // assumes createContext will set this flag
printContextInitInfo();
logger.info("Display created.");
logger.log(Level.FINE, "Running on thread: {0}", Thread.currentThread().getName());
logger.log(Level.INFO, "Adapter: {0}", Display.getAdapter());
logger.log(Level.INFO, "Driver Version: {0}", Display.getVersion());
String vendor = GL11.glGetString(GL11.GL_VENDOR);
logger.log(Level.INFO, "Vendor: {0}", vendor);
String version = GL11.glGetString(GL11.GL_VERSION);
logger.log(Level.INFO, "OpenGL Version: {0}", version);
String renderer = GL11.glGetString(GL11.GL_RENDERER);
logger.log(Level.INFO, "Renderer: {0}", renderer);
if (GLContext.getCapabilities().OpenGL20){
String shadingLang = GL11.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION);
logger.log(Level.INFO, "GLSL Ver: {0}", shadingLang);
}
created.set(true); created.set(true);
} catch (Exception ex){ } catch (Exception ex){
listener.handleError("Failed to create display", ex); try {
} finally {
// TODO: It is possible to avoid "Failed to find pixel format"
// error here by creating a default display.
if (!created.get()){
if (Display.isCreated()) if (Display.isCreated())
Display.destroy(); Display.destroy();
} catch (Exception ex2){
return; // if we failed to create display, do not continue logger.log(Level.WARNING, null, ex2);
} }
listener.handleError("Failed to create display", ex);
return; // if we failed to create display, do not continue
} }
super.internalCreate(); super.internalCreate();
listener.initialize(); listener.initialize();
@ -156,26 +135,29 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
throw new IllegalStateException(); throw new IllegalStateException();
listener.update(); listener.update();
assert checkGLError();
// calls swap buffers, etc. if (renderable.get()){
try { assert checkGLError();
if (autoFlush){
Display.update(); // calls swap buffers, etc.
}else{ try {
Display.processMessages(); if (autoFlush){
Thread.sleep(50); Display.update();
// add a small wait }else{
// to reduce CPU usage Display.processMessages();
Thread.sleep(50);
// add a small wait
// to reduce CPU usage
}
} catch (Throwable ex){
listener.handleError("Error while swapping buffers", ex);
} }
} catch (Throwable ex){
listener.handleError("Error while swapping buffers", ex);
} }
if (frameRate > 0) if (frameRate > 0)
Display.sync(frameRate); Display.sync(frameRate);
if (autoFlush) if (renderable.get() && autoFlush)
renderer.onFrame(); renderer.onFrame();
} }
@ -205,16 +187,18 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
logger.log(Level.INFO, "Using LWJGL {0}", Sys.getVersion()); logger.log(Level.INFO, "Using LWJGL {0}", Sys.getVersion());
initInThread(); initInThread();
while (true){ while (true){
if (Display.isCloseRequested()) if (renderable.get()){
listener.requestClose(false); if (Display.isCloseRequested())
listener.requestClose(false);
if (wasActive != Display.isActive()){
if (!wasActive){ if (wasActive != Display.isActive()) {
listener.gainFocus(); if (!wasActive) {
wasActive = true; listener.gainFocus();
}else{ wasActive = true;
listener.loseFocus(); } else {
wasActive = false; listener.loseFocus();
wasActive = false;
}
} }
} }
@ -227,15 +211,24 @@ public abstract class LwjglAbstractDisplay extends LwjglContext implements Runna
} }
public JoyInput getJoyInput() { public JoyInput getJoyInput() {
return new JInputJoyInput(); if (joyInput == null){
joyInput = new JInputJoyInput();
}
return joyInput;
} }
public MouseInput getMouseInput() { public MouseInput getMouseInput() {
return new LwjglMouseInput(); if (mouseInput == null){
mouseInput = new LwjglMouseInput(this);
}
return mouseInput;
} }
public KeyInput getKeyInput() { public KeyInput getKeyInput() {
return new LwjglKeyInput(); if (keyInput == null){
keyInput = new LwjglKeyInput(this);
}
return keyInput;
} }
public void setAutoFlushFrames(boolean enabled){ public void setAutoFlushFrames(boolean enabled){

@ -36,6 +36,8 @@ import com.jme3.system.AppSettings;
import com.jme3.system.JmeCanvasContext; import com.jme3.system.JmeCanvasContext;
import com.jme3.system.JmeContext.Type; import com.jme3.system.JmeContext.Type;
import java.awt.Canvas; import java.awt.Canvas;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -54,68 +56,67 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
private int width; private int width;
private int height; private int height;
private AtomicBoolean reinitReq = new AtomicBoolean(false); private final AtomicBoolean needRestoreCanvas = new AtomicBoolean(false);
private final Object reinitReqLock = new Object(); private final AtomicBoolean needDestroyCanvas = new AtomicBoolean(false);
private final CyclicBarrier actionRequiredBarrier = new CyclicBarrier(2);
private AtomicBoolean reinitAuth = new AtomicBoolean(false);
private final Object reinitAuthLock = new Object();
private Thread renderThread; private Thread renderThread;
private boolean runningFirstTime = true;
private boolean mouseWasGrabbed = false; private boolean mouseWasGrabbed = false;
// private Pbuffer dummyCtx; private boolean mouseActive, keyboardActive, joyActive;
public LwjglCanvas(){ public LwjglCanvas(){
super(); super();
canvas = new Canvas(){ canvas = new Canvas(){
@Override @Override
public void addNotify(){ public void addNotify(){
super.addNotify(); super.addNotify();
if (renderThread == null || renderThread.getState() == Thread.State.TERMINATED){
if (renderThread != null && renderThread.getState() == Thread.State.TERMINATED){ if (renderThread != null && renderThread.getState() == Thread.State.TERMINATED)
logger.log(Level.INFO, "EDT: Creating OGL thread. Was terminated."); return; // already destroyed.
}else{
logger.log(Level.INFO, "EDT: Creating OGL thread."); if (renderThread == null){
} logger.log(Level.INFO, "EDT: Creating OGL thread.");
renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread"); renderThread = new Thread(LwjglCanvas.this, "LWJGL Renderer Thread");
renderThread.start(); renderThread.start();
}else{ }else if (needClose.get()){
if (needClose.get()) return;
return; }
logger.log(Level.INFO, "EDT: Sending re-init authorization.."); logger.log(Level.INFO, "EDT: Notifying OGL that canvas is visible..");
needRestoreCanvas.set(true);
// reinitializing canvas // NOTE: no need to wait for OGL to initialize the canvas,
synchronized (reinitAuthLock){ // it can happen at any time.
reinitAuth.set(true);
reinitAuthLock.notifyAll();
}
}
} }
@Override @Override
public void removeNotify(){ public void removeNotify(){
if (needClose.get()){ if (needClose.get()){
logger.log(Level.INFO, "EDT: Close requested. Not re-initing."); logger.log(Level.INFO, "EDT: Application is stopped. Not restoring canvas.");
super.removeNotify();
return; return;
} }
// request to put context into reinit mode // We must tell GL context to shutdown and wait for it to
// this waits until reinit is authorized // shutdown, otherwise, issues will occur.
logger.log(Level.INFO, "EDT: Sending re-init request.."); logger.log(Level.INFO, "EDT: Sending destroy request..");
synchronized (reinitReqLock){ needDestroyCanvas.set(true);
reinitReq.set(true); try {
while (reinitReq.get()){ actionRequiredBarrier.await();
try { } catch (InterruptedException ex) {
reinitReqLock.wait(); logger.log(Level.SEVERE, "EDT: Interrupted! ", ex);
} catch (InterruptedException ex) { } catch (BrokenBarrierException ex){
logger.log(Level.SEVERE, "EDT: Interrupted! ", ex); logger.log(Level.SEVERE, "EDT: Broken barrier! ", ex);
}
}
// NOTE: reinitReq is now false.
} }
logger.log(Level.INFO, "EDT: Acknowledged receipt of re-init request!");
logger.log(Level.INFO, "EDT: Acknowledged receipt of destroy request!");
// GL context is dead at this point
// Reset barrier for future use
actionRequiredBarrier.reset();
super.removeNotify(); super.removeNotify();
} }
@ -131,6 +132,12 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
} }
public void create(boolean waitFor){ 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. // do not do anything.
// superclass's create() will be called at initInThread() // superclass's create() will be called at initInThread()
if (waitFor) if (waitFor)
@ -151,106 +158,64 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
@Override @Override
protected void runLoop(){ protected void runLoop(){
boolean reinitNeeded; if (needDestroyCanvas.getAndSet(false)){
synchronized (reinitReqLock){ // Destroy canvas
reinitNeeded = reinitReq.get(); logger.log(Level.INFO, "OGL: Received destroy request! Complying..");
}
if (reinitNeeded){
logger.log(Level.INFO, "OGL: Re-init request received!");
listener.loseFocus();
boolean mouseActive = Mouse.isCreated();
boolean keyboardActive = Keyboard.isCreated();
boolean joyActive = Controllers.isCreated();
if (mouseActive)
Mouse.destroy();
if (keyboardActive)
Keyboard.destroy();
if (joyActive)
Controllers.destroy();
pauseCanvas();
synchronized (reinitReqLock){
reinitReq.set(false);
reinitReqLock.notifyAll();
}
// we got the reinit request, now we wait for reinit to happen..
logger.log(Level.INFO, "OGL: Waiting for re-init authorization..");
synchronized (reinitAuthLock){
while (!reinitAuth.get()){
try {
reinitAuthLock.wait();
if (Thread.interrupted())
throw new InterruptedException();
} catch (InterruptedException ex) {
if (needClose.get()){
logger.log(Level.INFO, "OGL: Re-init aborted. Closing display..");
return;
}
logger.log(Level.SEVERE, "OGL: Interrupted! ", ex);
}
}
// NOTE: reinitAuth becamse true, now set it to false.
reinitAuth.set(false);
}
logger.log(Level.INFO, "OGL: Re-init authorization received. Re-initializing..");
restoreCanvas();
try { try {
if (mouseActive){ listener.loseFocus();
Mouse.create(); pauseCanvas();
} finally {
try {
// Required to avoid deadlock if an exception occurs
actionRequiredBarrier.await();
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, "OGL: Interrupted! ", ex);
} catch (BrokenBarrierException ex) {
logger.log(Level.SEVERE, "OGL: Broken barrier! ", ex);
} }
if (keyboardActive){
Keyboard.create();
}
if (joyActive){
Controllers.create();
}
} catch (LWJGLException ex){
listener.handleError("Failed to re-init input", ex);
} }
}else if (needRestoreCanvas.getAndSet(false)){
// Put canvas back online
logger.log(Level.INFO, "OGL: Canvas is now visible! Re-initializing..");
restoreCanvas();
listener.gainFocus();
} }
if (width != canvas.getWidth() || height != canvas.getHeight()){ if (width != canvas.getWidth() || height != canvas.getHeight()){
width = canvas.getWidth(); width = canvas.getWidth();
height = canvas.getHeight(); height = canvas.getHeight();
if (listener != null) if (listener != null)
listener.reshape(width, height); listener.reshape(width, height);
} }
super.runLoop();
}
@Override super.runLoop();
public void destroy(boolean waitFor){
needClose.set(true);
if (renderThread != null && renderThread.isAlive()){
renderThread.interrupt();
// make sure it really does get interrupted
synchronized(reinitAuthLock){
reinitAuthLock.notifyAll();
}
}
if (waitFor)
waitFor(false);
} }
private void pauseCanvas(){ private void pauseCanvas(){
if (Mouse.isCreated() && Mouse.isGrabbed()){ mouseActive = Mouse.isCreated();
keyboardActive = Keyboard.isCreated();
joyActive = Controllers.isCreated();
if (mouseActive && Mouse.isGrabbed()){
Mouse.setGrabbed(false); Mouse.setGrabbed(false);
mouseWasGrabbed = true; mouseWasGrabbed = true;
} }
if (mouseActive)
Mouse.destroy();
if (keyboardActive)
Keyboard.destroy();
if (joyActive)
Controllers.destroy();
logger.log(Level.INFO, "OGL: Destroying display (temporarily)"); logger.log(Level.INFO, "OGL: Destroying display (temporarily)");
Display.destroy(); Display.destroy();
renderable.set(false);
} }
/** /**
* Called if canvas was removed and then restored unexpectedly * Called to restore the canvas.
*/ */
private void restoreCanvas(){ private void restoreCanvas(){
logger.log(Level.INFO, "OGL: Waiting for canvas to become displayable.."); logger.log(Level.INFO, "OGL: Waiting for canvas to become displayable..");
@ -261,8 +226,12 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
logger.log(Level.SEVERE, "OGL: Interrupted! ", ex); logger.log(Level.SEVERE, "OGL: Interrupted! ", ex);
} }
} }
renderer.resetGLObjects(); renderer.resetGLObjects();
logger.log(Level.INFO, "OGL: Creating display.."); logger.log(Level.INFO, "OGL: Creating display..");
// Set renderable to true, since canvas is now displayable.
renderable.set(true);
createContext(settings); createContext(settings);
logger.log(Level.INFO, "OGL: Waiting for display to become active.."); logger.log(Level.INFO, "OGL: Waiting for display to become active..");
@ -276,26 +245,33 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
logger.log(Level.INFO, "OGL: Display is active!"); logger.log(Level.INFO, "OGL: Display is active!");
try { try {
if (mouseWasGrabbed){ if (mouseActive){
Mouse.create(); Mouse.create();
Mouse.setGrabbed(true); if (mouseWasGrabbed){
mouseWasGrabbed = false; Mouse.setGrabbed(true);
} mouseWasGrabbed = false;
SwingUtilities.invokeLater(new Runnable(){
public void run(){
canvas.requestFocus();
} }
}); }
if (keyboardActive){
Keyboard.create();
}
logger.log(Level.INFO, "OGL: Input has been reinitialized");
} catch (LWJGLException ex) { } catch (LWJGLException ex) {
logger.log(Level.SEVERE, "restoreCanvas()", ex); logger.log(Level.SEVERE, "Failed to re-init input", ex);
} }
listener.gainFocus(); SwingUtilities.invokeLater(new Runnable(){
public void run(){
canvas.requestFocus();
}
});
} }
@Override @Override
protected void createContext(AppSettings settings) { protected void createContext(AppSettings settings) {
if (!renderable.get())
return;
frameRate = settings.getFrameRate(); frameRate = settings.getFrameRate();
Display.setVSyncEnabled(settings.isVSync()); Display.setVSyncEnabled(settings.isVSync());
@ -308,6 +284,11 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
settings.getSamples()); settings.getSamples());
Display.create(pf); Display.create(pf);
Display.makeCurrent(); Display.makeCurrent();
if (runningFirstTime){
initContextFirstTime();
runningFirstTime = false;
}
}catch (LWJGLException ex){ }catch (LWJGLException ex){
listener.handleError("Failed to parent canvas to display", ex); listener.handleError("Failed to parent canvas to display", ex);
} }

@ -32,6 +32,9 @@
package com.jme3.system.lwjgl; package com.jme3.system.lwjgl;
import com.jme3.input.lwjgl.JInputJoyInput;
import com.jme3.input.lwjgl.LwjglKeyInput;
import com.jme3.input.lwjgl.LwjglMouseInput;
import com.jme3.renderer.Renderer; import com.jme3.renderer.Renderer;
import com.jme3.renderer.lwjgl.LwjglGL1Renderer; import com.jme3.renderer.lwjgl.LwjglGL1Renderer;
import com.jme3.renderer.lwjgl.LwjglRenderer; import com.jme3.renderer.lwjgl.LwjglRenderer;
@ -40,6 +43,12 @@ import com.jme3.system.SystemListener;
import com.jme3.system.JmeContext; import com.jme3.system.JmeContext;
import com.jme3.system.Timer; import com.jme3.system.Timer;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GLContext; import org.lwjgl.opengl.GLContext;
/** /**
@ -47,11 +56,17 @@ import org.lwjgl.opengl.GLContext;
*/ */
public abstract class LwjglContext implements JmeContext { public abstract class LwjglContext implements JmeContext {
private static final Logger logger = Logger.getLogger(LwjglContext.class.getName());
protected AtomicBoolean created = new AtomicBoolean(false); protected AtomicBoolean created = new AtomicBoolean(false);
protected AtomicBoolean renderable = new AtomicBoolean(false);
protected final Object createdLock = new Object(); protected final Object createdLock = new Object();
protected AppSettings settings = new AppSettings(true); protected AppSettings settings = new AppSettings(true);
protected Renderer renderer; protected Renderer renderer;
protected LwjglKeyInput keyInput;
protected LwjglMouseInput mouseInput;
protected JInputJoyInput joyInput;
protected Timer timer; protected Timer timer;
protected SystemListener listener; protected SystemListener listener;
@ -59,9 +74,72 @@ public abstract class LwjglContext implements JmeContext {
this.listener = listener; this.listener = listener;
} }
protected void printContextInitInfo(){
logger.log(Level.FINE, "Running on thread: {0}", Thread.currentThread().getName());
logger.log(Level.INFO, "Adapter: {0}", Display.getAdapter());
logger.log(Level.INFO, "Driver Version: {0}", Display.getVersion());
String vendor = GL11.glGetString(GL11.GL_VENDOR);
logger.log(Level.INFO, "Vendor: {0}", vendor);
String version = GL11.glGetString(GL11.GL_VERSION);
logger.log(Level.INFO, "OpenGL Version: {0}", version);
String renderGl = GL11.glGetString(GL11.GL_RENDERER);
logger.log(Level.INFO, "Renderer: {0}", renderGl);
if (GLContext.getCapabilities().OpenGL20){
String shadingLang = GL11.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION);
logger.log(Level.INFO, "GLSL Ver: {0}", shadingLang);
}
}
protected ContextAttribs createContextAttribs(){
if (settings.getBoolean("GraphicsDebug") || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)){
ContextAttribs attr;
if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)){
attr = new ContextAttribs(3, 3);
attr = attr.withProfileCore(true).withForwardCompatible(true).withProfileCompatibility(false);
}else{
attr = new ContextAttribs();
}
if (settings.getBoolean("GraphicsDebug")){
attr = attr.withDebug(true);
}
return attr;
}else{
return null;
}
}
protected void initContextFirstTime(){
assert renderable.get();
// Init renderer
if (renderer instanceof LwjglRenderer){
((LwjglRenderer)renderer).initialize();
}else if (renderer instanceof LwjglGL1Renderer){
((LwjglGL1Renderer)renderer).initialize();
}else{
assert false;
}
// Init input
if (keyInput != null)
keyInput.initialize();
if (mouseInput != null)
mouseInput.initialize();
if (joyInput != null)
joyInput.initialize();
}
public void internalDestroy(){ public void internalDestroy(){
renderer = null; renderer = null;
timer = null; timer = null;
renderable.set(false);
synchronized (createdLock){ synchronized (createdLock){
created.set(false); created.set(false);
createdLock.notifyAll(); createdLock.notifyAll();
@ -73,10 +151,8 @@ public abstract class LwjglContext implements JmeContext {
if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL2) if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL2)
|| settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)){ || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)){
renderer = new LwjglRenderer(); renderer = new LwjglRenderer();
((LwjglRenderer)renderer).initialize();
}else if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL1)){ }else if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL1)){
renderer = new LwjglGL1Renderer(); renderer = new LwjglGL1Renderer();
((LwjglGL1Renderer)renderer).initialize();
}else{ }else{
throw new UnsupportedOperationException("Unsupported renderer: " + settings.getRenderer()); throw new UnsupportedOperationException("Unsupported renderer: " + settings.getRenderer());
} }
@ -84,6 +160,10 @@ public abstract class LwjglContext implements JmeContext {
created.set(true); created.set(true);
createdLock.notifyAll(); createdLock.notifyAll();
} }
if (renderable.get())
initContextFirstTime();
else
assert getType() == Type.Canvas;
} }
public void create(){ public void create(){
@ -109,6 +189,10 @@ public abstract class LwjglContext implements JmeContext {
return created.get(); return created.get();
} }
public boolean isRenderable(){
return renderable.get();
}
public void setSettings(AppSettings settings) { public void setSettings(AppSettings settings) {
this.settings.copyFrom(settings); this.settings.copyFrom(settings);
} }

@ -47,6 +47,7 @@ import java.util.logging.Logger;
import org.lwjgl.opengl.ARBMultisample; import org.lwjgl.opengl.ARBMultisample;
import org.lwjgl.opengl.ContextAttribs; import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.opengl.PixelFormat; import org.lwjgl.opengl.PixelFormat;
public class LwjglDisplay extends LwjglAbstractDisplay { public class LwjglDisplay extends LwjglAbstractDisplay {
@ -118,65 +119,21 @@ public class LwjglDisplay extends LwjglAbstractDisplay {
Display.setVSyncEnabled(settings.isVSync()); Display.setVSyncEnabled(settings.isVSync());
if (!created.get() || pixelFormatChanged){ if (!created.get() || pixelFormatChanged){
if (settings.getBoolean("GraphicsDebug") || settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)){ ContextAttribs attr = createContextAttribs();
ContextAttribs attr; if (attr != null){
if (settings.getRenderer().equals(AppSettings.LWJGL_OPENGL3)){
attr = new ContextAttribs(3, 3);
attr = attr.withProfileCore(true).withForwardCompatible(true).withProfileCompatibility(false);
}else{
attr = new ContextAttribs();
}
if (settings.getBoolean("GraphicsDebug")){
attr = attr.withDebug(true);
}
Display.create(pixelFormat, attr); Display.create(pixelFormat, attr);
}else{ }else{
Display.create(pixelFormat); Display.create(pixelFormat);
} }
renderable.set(true);
if (pixelFormatChanged && pixelFormat.getSamples() > 1){ if (pixelFormatChanged && pixelFormat.getSamples() > 1
&& GLContext.getCapabilities().GL_ARB_multisample){
GL11.glEnable(ARBMultisample.GL_MULTISAMPLE_ARB); GL11.glEnable(ARBMultisample.GL_MULTISAMPLE_ARB);
} }
} }
} }
private ByteBuffer[] imagesToByteBuffers(BufferedImage[] images) {
ByteBuffer[] out = new ByteBuffer[images.length];
for (int i = 0; i < images.length; i++) {
out[i] = imageToByteBuffer(images[i]);
}
return out;
}
private ByteBuffer imageToByteBuffer(BufferedImage image) {
if (image.getType() != BufferedImage.TYPE_INT_ARGB_PRE) {
BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
Graphics2D g = convertedImage.createGraphics();
double width = image.getWidth() * (double) 1;
double height = image.getHeight() * (double) 1;
g.drawImage(image, (int) ((convertedImage.getWidth() - width) / 2),
(int) ((convertedImage.getHeight() - height) / 2),
(int) (width), (int) (height), null);
g.dispose();
image = convertedImage;
}
byte[] imageBuffer = new byte[image.getWidth() * image.getHeight() * 4];
int counter = 0;
for (int i = 0; i < image.getHeight(); i++) {
for (int j = 0; j < image.getWidth(); j++) {
int colorSpace = image.getRGB(j, i);
imageBuffer[counter + 0] = (byte) ((colorSpace << 8) >> 24);
imageBuffer[counter + 1] = (byte) ((colorSpace << 16) >> 24);
imageBuffer[counter + 2] = (byte) ((colorSpace << 24) >> 24);
imageBuffer[counter + 3] = (byte) (colorSpace >> 24);
counter += 4;
}
}
return ByteBuffer.wrap(imageBuffer);
}
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!");
@ -190,6 +147,7 @@ public class LwjglDisplay extends LwjglAbstractDisplay {
@Override @Override
public void runLoop(){ public void runLoop(){
// This method is overriden to do restart
if (needRestart.getAndSet(false)){ if (needRestart.getAndSet(false)){
try{ try{
createContext(settings); createContext(settings);
@ -221,4 +179,40 @@ public class LwjglDisplay extends LwjglAbstractDisplay {
Display.setTitle(title); Display.setTitle(title);
} }
private ByteBuffer[] imagesToByteBuffers(BufferedImage[] images) {
ByteBuffer[] out = new ByteBuffer[images.length];
for (int i = 0; i < images.length; i++) {
out[i] = imageToByteBuffer(images[i]);
}
return out;
}
private ByteBuffer imageToByteBuffer(BufferedImage image) {
if (image.getType() != BufferedImage.TYPE_INT_ARGB_PRE) {
BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
Graphics2D g = convertedImage.createGraphics();
double width = image.getWidth() * (double) 1;
double height = image.getHeight() * (double) 1;
g.drawImage(image, (int) ((convertedImage.getWidth() - width) / 2),
(int) ((convertedImage.getHeight() - height) / 2),
(int) (width), (int) (height), null);
g.dispose();
image = convertedImage;
}
byte[] imageBuffer = new byte[image.getWidth() * image.getHeight() * 4];
int counter = 0;
for (int i = 0; i < image.getHeight(); i++) {
for (int j = 0; j < image.getWidth(); j++) {
int colorSpace = image.getRGB(j, i);
imageBuffer[counter + 0] = (byte) ((colorSpace << 8) >> 24);
imageBuffer[counter + 1] = (byte) ((colorSpace << 16) >> 24);
imageBuffer[counter + 2] = (byte) ((colorSpace << 24) >> 24);
imageBuffer[counter + 3] = (byte) (colorSpace >> 24);
counter += 4;
}
}
return ByteBuffer.wrap(imageBuffer);
}
} }

@ -42,15 +42,11 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.lwjgl.LWJGLException; import org.lwjgl.LWJGLException;
import org.lwjgl.Sys; import org.lwjgl.Sys;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.opengl.OpenGLException; import org.lwjgl.opengl.OpenGLException;
import org.lwjgl.opengl.Pbuffer; import org.lwjgl.opengl.Pbuffer;
import org.lwjgl.opengl.PixelFormat; import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.opengl.Util; import org.lwjgl.opengl.Util;
public class LwjglOffscreenBuffer extends LwjglContext implements Runnable { public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
private static final Logger logger = Logger.getLogger(LwjglOffscreenBuffer.class.getName()); private static final Logger logger = Logger.getLogger(LwjglOffscreenBuffer.class.getName());
@ -74,6 +70,12 @@ public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
width = settings.getWidth(); width = settings.getWidth();
height = settings.getHeight(); height = settings.getHeight();
try{ try{
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread thread, Throwable thrown) {
listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
}
});
//String rendererStr = settings.getString("Renderer"); //String rendererStr = settings.getString("Renderer");
// if (rendererStr.startsWith("LWJGL-OpenGL3")){ // if (rendererStr.startsWith("LWJGL-OpenGL3")){
// ContextAttribs attribs; // ContextAttribs attribs;
@ -86,35 +88,15 @@ public class LwjglOffscreenBuffer extends LwjglContext implements Runnable {
// attribs.withDebug(false); // attribs.withDebug(false);
// Display.create(pf, attribs); // Display.create(pf, attribs);
// }else{ // }else{
pbuffer = new Pbuffer(width, height, pixelFormat, null); pbuffer = new Pbuffer(width, height, pixelFormat, null, null, createContextAttribs());
// } // }
pbuffer.makeCurrent(); pbuffer.makeCurrent();
logger.info("Offscreen buffer created."); renderable.set(true);
logger.log(Level.FINE, "Running on thread: {0}", Thread.currentThread().getName());
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread thread, Throwable thrown) {
listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
}
});
String vendor = GL11.glGetString(GL11.GL_VENDOR);
logger.log(Level.INFO, "Vendor: {0}", vendor);
String version = GL11.glGetString(GL11.GL_VERSION); logger.info("Offscreen buffer created.");
logger.log(Level.INFO, "OpenGL Version: {0}", version); printContextInitInfo();
String renderer = GL11.glGetString(GL11.GL_RENDERER);
logger.log(Level.INFO, "Renderer: {0}", renderer);
if (GLContext.getCapabilities().OpenGL20){
String shadingLang = GL11.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION);
logger.log(Level.INFO, "GLSL Ver: {0}", shadingLang);
}
created.set(true);
} catch (LWJGLException ex){ } catch (LWJGLException ex){
listener.handleError("Failed to create display", ex); listener.handleError("Failed to create display", ex);
} finally { } finally {

@ -14,7 +14,7 @@ import javax.swing.JFrame;
public class TestSafeCanvas extends SimpleApplication { public class TestSafeCanvas extends SimpleApplication {
public static void main(String[] args){ public static void main(String[] args) throws InterruptedException{
AppSettings settings = new AppSettings(true); AppSettings settings = new AppSettings(true);
settings.setWidth(640); settings.setWidth(640);
settings.setHeight(480); settings.setHeight(480);
@ -28,6 +28,10 @@ public class TestSafeCanvas extends SimpleApplication {
Canvas canvas = context.getCanvas(); Canvas canvas = context.getCanvas();
canvas.setSize(settings.getWidth(), settings.getHeight()); canvas.setSize(settings.getWidth(), settings.getHeight());
app.startCanvas(true);
Thread.sleep(3000);
JFrame frame = new JFrame("Test"); JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() { frame.addWindowListener(new WindowAdapter() {
@ -40,6 +44,14 @@ public class TestSafeCanvas extends SimpleApplication {
frame.pack(); frame.pack();
frame.setLocationRelativeTo(null); frame.setLocationRelativeTo(null);
frame.setVisible(true); frame.setVisible(true);
Thread.sleep(3000);
frame.getContentPane().remove(canvas);
Thread.sleep(3000);
frame.getContentPane().add(canvas);
} }
@Override @Override

Loading…
Cancel
Save