/**
* Copyright 2012 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of JogAmp Community.
*/
package jogamp.opengl;
import java.io.PrintStream;
import javax.media.nativewindow.NativeSurface;
import javax.media.nativewindow.WindowClosingProtocol;
import javax.media.nativewindow.WindowClosingProtocol.WindowClosingMode;
import javax.media.opengl.FPSCounter;
import javax.media.opengl.GL;
import javax.media.opengl.GLAnimatorControl;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLAutoDrawableDelegate;
import javax.media.opengl.GLCapabilitiesImmutable;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLException;
import javax.media.opengl.GLProfile;
import javax.media.opengl.GLRunnable;
import com.jogamp.opengl.util.Animator;
/**
* Abstract common code for GLAutoDrawable implementations.
*
* @see GLAutoDrawable
* @see GLAutoDrawableDelegate
* @see GLPBufferImpl
* @see GLWindow
*/
public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter {
public static final boolean DEBUG = Debug.debug("GLAutoDrawable");
protected final GLDrawableHelper helper = new GLDrawableHelper();
protected final FPSCounterImpl fpsCounter = new FPSCounterImpl();
protected GLDrawableImpl drawable;
protected GLContextImpl context;
protected int additionalCtxCreationFlags = 0;
protected boolean sendReshape = false;
protected boolean sendDestroy = false;
public GLAutoDrawableBase(GLDrawableImpl drawable, GLContextImpl context) {
this.drawable = drawable;
this.context = context;
resetFPSCounter();
}
/** Returns the delegated GLDrawable */
public final GLDrawable getDelegatedDrawable() { return drawable; }
/** Default implementation to handle repaint events from the windowing system */
protected void defaultWindowRepaintOp() {
if( null != drawable && drawable.isRealized() ) {
if( !drawable.getNativeSurface().isSurfaceLockedByOtherThread() && !helper.isAnimatorAnimating() ) {
display();
}
}
}
/** Default implementation to handle resize events from the windowing system */
protected void defaultWindowResizedOp() {
if( null!=drawable ) {
if(DEBUG) {
System.err.println("GLAutoDrawableBase.sizeChanged: ("+Thread.currentThread().getName()+"): "+getWidth()+"x"+getHeight()+" - surfaceHandle 0x"+Long.toHexString(getNativeSurface().getSurfaceHandle()));
}
sendReshape = true;
defaultWindowRepaintOp();
}
}
/** Default implementation to handle destroy notifications from the windowing system */
protected void defaultWindowDestroyNotifyOp() {
final NativeSurface ns = getNativeSurface();
final boolean shallClose;
if(ns instanceof WindowClosingProtocol) {
shallClose = WindowClosingMode.DISPOSE_ON_CLOSE == ((WindowClosingProtocol)ns).getDefaultCloseOperation();
} else {
shallClose = true;
}
if( shallClose ) {
// Is an animator thread perform rendering?
if (helper.isExternalAnimatorRunning()) {
// Pause animations before initiating safe destroy.
final GLAnimatorControl ctrl = helper.getAnimator();
final boolean isPaused = ctrl.pause();
destroy();
if(isPaused) {
ctrl.resume();
}
} else if (null != ns && ns.isSurfaceLockedByOtherThread()) {
// surface is locked by another thread
// Flag that destroy should be performed on the next
// attempt to display.
sendDestroy = true;
} else {
// Without an external thread animating or locking the
// surface, we are safe.
destroy ();
}
}
}
/**
* Default implementation to destroys the drawable and context of this GLAutoDrawable:
*
* - issues the GLEventListener dispose call, if drawable and context are valid
* - destroys the GLContext, if valid
* - destroys the GLDrawable, if valid
*
*/
protected void defaultDestroyOp() {
if( null != drawable && drawable.isRealized() ) {
if( null != context && context.isCreated() ) {
// Catch dispose GLExceptions by GLEventListener, just 'print' them
// so we can continue with the destruction.
try {
helper.disposeGL(this, drawable, context, null);
} catch (GLException gle) {
gle.printStackTrace();
}
}
drawable.setRealized(false);
}
context = null;
drawable = null;
}
//
// GLAutoDrawable
//
protected final Runnable defaultInitAction = new Runnable() {
@Override
public final void run() {
// Lock: Locked Surface/Window by MakeCurrent/Release
helper.init(GLAutoDrawableBase.this);
resetFPSCounter();
} };
protected final Runnable defaultDisplayAction = new Runnable() {
@Override
public final void run() {
// Lock: Locked Surface/Window by display _and_ MakeCurrent/Release
if (sendReshape) {
helper.reshape(GLAutoDrawableBase.this, 0, 0, getWidth(), getHeight());
sendReshape = false;
}
helper.display(GLAutoDrawableBase.this);
fpsCounter.tickFPS();
} };
@Override
public final GLContext getContext() {
return context;
}
@Override
public final GLContext setContext(GLContext newCtx) {
final GLContext oldCtx = context;
final boolean newCtxCurrent = helper.switchContext(drawable, oldCtx, newCtx, additionalCtxCreationFlags);
context=(GLContextImpl)newCtx;
if(newCtxCurrent) {
context.makeCurrent();
}
return oldCtx;
}
@Override
public final GL getGL() {
if (context == null) {
return null;
}
return context.getGL();
}
@Override
public final GL setGL(GL gl) {
if (context != null) {
context.setGL(gl);
return gl;
}
return null;
}
@Override
public final void addGLEventListener(GLEventListener listener) {
helper.addGLEventListener(listener);
}
@Override
public final void addGLEventListener(int index, GLEventListener listener) throws IndexOutOfBoundsException {
helper.addGLEventListener(index, listener);
}
@Override
public final void removeGLEventListener(GLEventListener listener) {
helper.removeGLEventListener(listener);
}
@Override
public GLEventListener removeGLEventListener(int index) throws IndexOutOfBoundsException {
return helper.removeGLEventListener(index);
}
@Override
public final void setAnimator(GLAnimatorControl animatorControl)
throws GLException {
helper.setAnimator(animatorControl);
}
@Override
public final GLAnimatorControl getAnimator() {
return helper.getAnimator();
}
@Override
public final boolean invoke(boolean wait, GLRunnable glRunnable) {
return helper.invoke(this, wait, glRunnable);
}
@Override
public final void setAutoSwapBufferMode(boolean enable) {
helper.setAutoSwapBufferMode(enable);
}
@Override
public final boolean getAutoSwapBufferMode() {
return helper.getAutoSwapBufferMode();
}
@Override
public final void setContextCreationFlags(int flags) {
additionalCtxCreationFlags = flags;
if(null != context) {
context.setContextCreationFlags(additionalCtxCreationFlags);
}
}
@Override
public final int getContextCreationFlags() {
return additionalCtxCreationFlags;
}
//
// FPSCounter
//
@Override
public final void setUpdateFPSFrames(int frames, PrintStream out) {
fpsCounter.setUpdateFPSFrames(frames, out);
}
@Override
public final void resetFPSCounter() {
fpsCounter.resetFPSCounter();
}
@Override
public final int getUpdateFPSFrames() {
return fpsCounter.getUpdateFPSFrames();
}
@Override
public final long getFPSStartTime() {
return fpsCounter.getFPSStartTime();
}
@Override
public final long getLastFPSUpdateTime() {
return fpsCounter.getLastFPSUpdateTime();
}
@Override
public final long getLastFPSPeriod() {
return fpsCounter.getLastFPSPeriod();
}
@Override
public final float getLastFPS() {
return fpsCounter.getLastFPS();
}
@Override
public final int getTotalFPSFrames() {
return fpsCounter.getTotalFPSFrames();
}
@Override
public final long getTotalFPSDuration() {
return fpsCounter.getTotalFPSDuration();
}
@Override
public final float getTotalFPS() {
return fpsCounter.getTotalFPS();
}
//
// GLDrawable delegation
//
@Override
public final GLContext createContext(final GLContext shareWith) {
if(drawable != null) {
final GLContext _ctx = drawable.createContext(shareWith);
_ctx.setContextCreationFlags(additionalCtxCreationFlags);
return _ctx;
}
return null;
}
@Override
public final boolean isRealized() {
return null != drawable ? drawable.isRealized() : false;
}
@Override
public int getWidth() {
return null != drawable ? drawable.getWidth() : 0;
}
@Override
public int getHeight() {
return null != drawable ? drawable.getHeight() : 0;
}
/**
* @param t the thread for which context release shall be skipped, usually the animation thread,
* ie. {@link Animator#getThread()}.
* @deprecated this is an experimental feature,
* intended for measuring performance in regards to GL context switch
*/
@Deprecated
public void setSkipContextReleaseThread(Thread t) {
helper.setSkipContextReleaseThread(t);
}
/**
* @deprecated see {@link #setSkipContextReleaseThread(Thread)}
*/
@Deprecated
public Thread getSkipContextReleaseThread() {
return helper.getSkipContextReleaseThread();
}
@Override
public final void swapBuffers() throws GLException {
if(drawable!=null && context != null) {
drawable.swapBuffers();
}
}
@Override
public final GLCapabilitiesImmutable getChosenGLCapabilities() {
return null != drawable ? drawable.getChosenGLCapabilities() : null;
}
@Override
public final GLProfile getGLProfile() {
return null != drawable ? drawable.getGLProfile() : null;
}
@Override
public final NativeSurface getNativeSurface() {
return null != drawable ? drawable.getNativeSurface() : null;
}
@Override
public final long getHandle() {
return null != drawable ? drawable.getHandle() : 0;
}
}