jogamp.opengl.GLDrawableImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
* Copyright (c) 2010 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:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution 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 Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
* ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
* DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for use
* in the design, construction, operation or maintenance of any nuclear
* facility.
*
* Sun gratefully acknowledges that this software was originally authored
* and developed by Kenneth Bradley Russell and Christopher John Kline.
*/
package jogamp.opengl;
import javax.media.nativewindow.AbstractGraphicsDevice;
import javax.media.nativewindow.NativeSurface;
import javax.media.nativewindow.ProxySurface;
import javax.media.opengl.GL;
import javax.media.opengl.GLCapabilitiesImmutable;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawable;
import javax.media.opengl.GLDrawableFactory;
import javax.media.opengl.GLException;
import javax.media.opengl.GLProfile;
public abstract class GLDrawableImpl implements GLDrawable {
protected static final boolean DEBUG = GLDrawableFactoryImpl.DEBUG;
protected GLDrawableImpl(GLDrawableFactory factory, NativeSurface comp, boolean realized) {
this(factory, comp, (GLCapabilitiesImmutable) comp.getGraphicsConfiguration().getRequestedCapabilities(), realized);
}
protected GLDrawableImpl(GLDrawableFactory factory, NativeSurface comp, GLCapabilitiesImmutable requestedCapabilities, boolean realized) {
this.factory = factory;
this.surface = comp;
this.realized = realized;
this.requestedCapabilities = requestedCapabilities;
}
/**
* Returns the DynamicLookupHelper
*/
public abstract GLDynamicLookupHelper getGLDynamicLookupHelper();
public final GLDrawableFactoryImpl getFactoryImpl() {
return (GLDrawableFactoryImpl) getFactory();
}
@Override
public final void swapBuffers() throws GLException {
if( !realized ) { // volatile OK (locked below)
return; // destroyed already
}
final int lockRes = lockSurface(); // it's recursive, so it's ok within [makeCurrent .. release]
if (NativeSurface.LOCK_SURFACE_NOT_READY == lockRes) {
return;
}
try {
if( realized ) { // volatile OK
final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable)surface.getGraphicsConfiguration().getChosenCapabilities();
if ( caps.getDoubleBuffered() ) {
if(!surface.surfaceSwap()) {
swapBuffersImpl(true);
}
} else {
final GLContext ctx = GLContext.getCurrent();
if(null!=ctx && ctx.getGLDrawable()==this) {
ctx.getGL().glFlush();
}
swapBuffersImpl(false);
}
}
} finally {
unlockSurface();
}
surface.surfaceUpdated(this, surface, System.currentTimeMillis());
}
/**
* Platform and implementation depending surface swap.
* The surface is locked.
*
* If doubleBuffered
is true
,
* an actual platform dependent surface swap shall be executed.
*
*
* If doubleBuffered
is false
,
* {@link GL#glFlush()} has been called already and
* the implementation may execute implementation specific code.
*
* @param doubleBuffered indicates whether double buffering is enabled, see above.
*/
protected abstract void swapBuffersImpl(boolean doubleBuffered);
public final static String toHexString(long hex) {
return "0x" + Long.toHexString(hex);
}
@Override
public final GLProfile getGLProfile() {
return requestedCapabilities.getGLProfile();
}
@Override
public GLCapabilitiesImmutable getChosenGLCapabilities() {
return (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getChosenCapabilities();
}
public final GLCapabilitiesImmutable getRequestedGLCapabilities() {
return requestedCapabilities;
}
@Override
public NativeSurface getNativeSurface() {
return surface;
}
/**
* called with locked surface @ setRealized(false) or @ lockSurface(..) when surface changed
*
* Must be paired w/ {@link #createHandle()}.
*
*/
protected void destroyHandle() {}
/**
* called with locked surface @ setRealized(true) or @ lockSurface(..) when surface changed
*
* Must be paired w/ {@link #destroyHandle()}.
*
*/
protected void createHandle() {}
@Override
public long getHandle() {
return surface.getSurfaceHandle();
}
@Override
public final GLDrawableFactory getFactory() {
return factory;
}
@Override
public final void setRealized(boolean realizedArg) {
if ( realized != realizedArg ) { // volatile: OK (locked below)
final boolean isProxySurface = surface instanceof ProxySurface;
if(DEBUG) {
System.err.println(getThreadName() + ": setRealized: drawable "+getClass().getSimpleName()+", surface "+surface.getClass().getSimpleName()+", isProxySurface "+isProxySurface+": "+realized+" -> "+realizedArg);
Thread.dumpStack();
}
AbstractGraphicsDevice aDevice = surface.getGraphicsConfiguration().getScreen().getDevice();
if(realizedArg) {
if(isProxySurface) {
((ProxySurface)surface).createNotify();
}
if(NativeSurface.LOCK_SURFACE_NOT_READY >= surface.lockSurface()) {
throw new GLException("GLDrawableImpl.setRealized(true): Surface not ready (lockSurface)");
}
} else {
aDevice.lock();
}
try {
if ( realized != realizedArg ) { // volatile: OK
realized = realizedArg;
if(realizedArg) {
setRealizedImpl();
createHandle();
} else {
destroyHandle();
setRealizedImpl();
}
}
} finally {
if(realizedArg) {
surface.unlockSurface();
} else {
aDevice.unlock();
if(isProxySurface) {
((ProxySurface)surface).destroyNotify();
}
}
}
} else if(DEBUG) {
System.err.println(getThreadName() + ": setRealized: "+getClass().getName()+" "+this.realized+" == "+realizedArg);
}
}
/**
* Platform specific realization of drawable
*/
protected abstract void setRealizedImpl();
/**
* Callback for special implementations, allowing
*
* - to associate bound context to this drawable (bound == true)
* or to remove such association (bound == false).
* - to trigger GLContext/GLDrawable related lifecycle:
construct
, destroy
.
*
*
* If bound
is true
, the context is current and being newly associated w/ this drawable.
*
*
* If bound
is false
, the context is still current and will be unbound (released and destroyed, or simply disassociated).
*
*
* Being called by {@link GLContextImpl#associateDrawable(boolean)}.
*
* @param ctx the just bounded or unbounded context
* @param bound if true
create an association, otherwise remove it
*/
protected void associateContext(GLContext ctx, boolean bound) { }
/**
* Callback for special implementations, allowing GLContext to trigger GL related lifecycle: makeCurrent
, release
.
*
* If current
is true
, the context has just been made current.
*
*
* If current
is false
, the context is still current and will be release after this method returns.
*
*
* Being called by {@link GLContextImpl#contextMadeCurrent(boolean)}.
*
* @see #associateContext(GLContext, boolean)
*/
protected void contextMadeCurrent(GLContext glc, boolean current) { }
/** Callback for special implementations, allowing GLContext to fetch a custom default render framebuffer. Defaults to zero.*/
protected int getDefaultDrawFramebuffer() { return 0; }
/** Callback for special implementations, allowing GLContext to fetch a custom default read framebuffer. Defaults to zero. */
protected int getDefaultReadFramebuffer() { return 0; }
/** Callback for special implementations, allowing GLContext to fetch a custom default read buffer of current framebuffer. */
protected int getDefaultReadBuffer(GL gl) {
if(gl.isGLES() || getChosenGLCapabilities().getDoubleBuffered()) {
// Note-1: Neither ES1 nor ES2 supports selecting the read buffer via glReadBuffer
// Note-2: ES3 only supports GL_BACK, GL_NONE or GL_COLOR_ATTACHMENT0+i
return GL.GL_BACK;
}
return GL.GL_FRONT ;
}
@Override
public final boolean isRealized() {
return realized;
}
@Override
public int getWidth() {
return surface.getWidth();
}
@Override
public int getHeight() {
return surface.getHeight();
}
@Override
public boolean isGLOriented() {
return true;
}
/**
* {@link NativeSurface#lockSurface() Locks} the underlying windowing toolkit's {@link NativeSurface surface}.
*
* If drawable is {@link #setRealized(boolean) realized},
* the {@link #getHandle() drawable handle} is valid after successfully {@link NativeSurface#lockSurface() locking}
* it's {@link NativeSurface surface} until being {@link #unlockSurface() unlocked}.
*
*
* In case the {@link NativeSurface surface} has changed as indicated by it's
* {@link NativeSurface#lockSurface() lock} result {@link NativeSurface#LOCK_SURFACE_CHANGED},
* the implementation is required to update this information as needed within it's implementation.
*
*
* @see NativeSurface#lockSurface()
* @see #getHandle()
*/
public final int lockSurface() throws GLException {
final int lockRes = surface.lockSurface();
if ( NativeSurface.LOCK_SURFACE_CHANGED == lockRes && realized ) {
// Update the drawable handle, in case the surface handle has changed.
final long _handle1 = getHandle();
destroyHandle();
createHandle();
final long _handle2 = getHandle();
if(DEBUG) {
if( _handle1 != _handle2) {
System.err.println(getThreadName() + ": Drawable handle changed: "+toHexString(_handle1)+" -> "+toHexString(_handle2));
}
}
}
return lockRes;
}
/**
* {@link NativeSurface#unlockSurface() Unlocks} the underlying windowing toolkit {@link NativeSurface surface},
* which may render the {@link #getHandle() drawable handle} invalid.
*
* @see NativeSurface#unlockSurface()
* @see #getHandle()
*/
public final void unlockSurface() {
surface.unlockSurface();
}
@Override
public String toString() {
return getClass().getSimpleName()+"[Realized "+isRealized()+
",\n\tFactory "+getFactory()+
",\n\tHandle "+toHexString(getHandle())+
",\n\tSurface "+getNativeSurface()+"]";
}
protected static String getThreadName() { return Thread.currentThread().getName(); }
protected GLDrawableFactory factory;
protected NativeSurface surface;
protected GLCapabilitiesImmutable requestedCapabilities;
// Indicates whether the surface (if an onscreen context) has been
// realized. Plausibly, before the surface is realized the JAWT
// should return an error or NULL object from some of its
// operations; this appears to be the case on Win32 but is not true
// at least with Sun's current X11 implementation (1.4.x), which
// crashes with no other error reported if the DrawingSurfaceInfo is
// fetched from a locked DrawingSurface during the validation as a
// result of calling show() on the main thread. To work around this
// we prevent any JAWT or OpenGL operations from being done until
// addNotify() is called on the surface.
protected volatile boolean realized;
}