/**
* 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 java.util.List;
import javax.media.nativewindow.AbstractGraphicsDevice;
import javax.media.nativewindow.NativeSurface;
import javax.media.nativewindow.NativeWindowException;
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.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.common.util.locks.RecursiveLock;
import com.jogamp.opengl.GLAutoDrawableDelegate;
/**
* 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 = GLDrawableImpl.DEBUG;
protected final GLDrawableHelper helper = new GLDrawableHelper();
protected final FPSCounterImpl fpsCounter = new FPSCounterImpl();
protected volatile GLDrawableImpl drawable; // volatile: avoid locking for read-only access
protected GLContextImpl context;
protected final boolean ownsDevice;
protected int additionalCtxCreationFlags = 0;
protected volatile boolean sendReshape = false; // volatile: maybe written by WindowManager thread w/o locking
protected volatile boolean sendDestroy = false; // volatile: maybe written by WindowManager thread w/o locking
/**
* @param drawable upstream {@link GLDrawableImpl} instance, may be null for lazy initialization
* @param context upstream {@link GLContextImpl} instance, may be null for lazy initialization
* @param ownsDevice pass true
if {@link AbstractGraphicsDevice#close()} shall be issued,
* otherwise pass false
. Closing the device is required in case
* the drawable is created w/ it's own new instance, e.g. offscreen drawables,
* and no further lifecycle handling is applied.
*/
public GLAutoDrawableBase(GLDrawableImpl drawable, GLContextImpl context, boolean ownsDevice) {
this.drawable = drawable;
this.context = context;
this.ownsDevice = ownsDevice;
resetFPSCounter();
}
/** Returns the recursive lock object of the upstream implementation, which synchronizes multithreaded access. */
protected abstract RecursiveLock getLock();
/** Default implementation to handle repaint events from the windowing system */
protected final void defaultWindowRepaintOp() {
final GLDrawable _drawable = drawable;
if( null != _drawable && _drawable.isRealized() ) {
if( !_drawable.getNativeSurface().isSurfaceLockedByOtherThread() && !helper.isAnimatorAnimatingOnOtherThread() ) {
display();
}
}
}
/** Default implementation to handle resize events from the windowing system. All required locks are being claimed. */
protected final void defaultWindowResizedOp(int newWidth, int newHeight) throws NativeWindowException, GLException {
GLDrawableImpl _drawable = drawable;
if( null!=_drawable ) {
if(DEBUG) {
System.err.println("GLAutoDrawableBase.sizeChanged: ("+Thread.currentThread().getName()+"): "+newWidth+"x"+newHeight+" - surfaceHandle 0x"+Long.toHexString(getNativeSurface().getSurfaceHandle()));
}
if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) {
final RecursiveLock _lock = getLock();
_lock.lock();
try {
final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, newWidth, newHeight);
if(_drawable != _drawableNew) {
// write back
_drawable = _drawableNew;
drawable = _drawableNew;
}
} finally {
_lock.unlock();
}
}
sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock
if( _drawable.isRealized() ) {
if( !_drawable.getNativeSurface().isSurfaceLockedByOtherThread() && !helper.isAnimatorAnimatingOnOtherThread() ) {
display();
}
}
}
}
/**
* Default implementation to handle destroy notifications from the windowing system.
*
*
* If the {@link NativeSurface} does not implement {@link WindowClosingProtocol} * or {@link WindowClosingMode#DISPOSE_ON_CLOSE} is enabled (default), * a thread safe destruction is being induced. *
*/ protected final 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 ) { destroyAvoidAwareOfLocking(); } } /** * Calls {@link #destroy()} * directly if the following requirements are met: ** Otherwise destroy is being flagged to be called within the next * call of display(). *
** This method is being used to avoid deadlock if * destruction is desired by other threads, e.g. the window manager. *
* @see #defaultWindowDestroyNotifyOp() * @see #defaultDisplay() */ protected final void destroyAvoidAwareOfLocking() { final NativeSurface ns = getNativeSurface(); final GLAnimatorControl ctrl = helper.getAnimator(); // Is an animator thread perform rendering? if ( helper.isAnimatorStartedOnOtherThread() ) { // Pause animations before initiating safe destroy. 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; // async, but avoiding deadlock } else { // Without an external thread animating or locking the // surface, we are safe. destroy(); } } /** * Calls {@link #destroyImplInLock()} while claiming the lock. */ protected final void defaultDestroy() { final RecursiveLock lock = getLock(); lock.lock(); try { destroyImplInLock(); } finally { lock.unlock(); } } /** * Default implementation to destroys the drawable and context of this GLAutoDrawable: *Method assumes the lock is being hold.
*Override it to extend it to destroy your resources, i.e. the actual window.
* In such case call super.destroyImplInLock
first.