summaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/javax/media/opengl/awt
diff options
context:
space:
mode:
Diffstat (limited to 'src/jogl/classes/javax/media/opengl/awt')
-rw-r--r--src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java16
-rw-r--r--src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java14
-rw-r--r--src/jogl/classes/javax/media/opengl/awt/GLCanvas.java1093
-rw-r--r--src/jogl/classes/javax/media/opengl/awt/GLJPanel.java2031
4 files changed, 2151 insertions, 1003 deletions
diff --git a/src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java b/src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java
index d1e725b00..2698678af 100644
--- a/src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java
+++ b/src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java
@@ -1,21 +1,21 @@
/*
* Copyright (c) 2003 Sun Microsystems, Inc. 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
@@ -28,18 +28,18 @@
* 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 javax.media.opengl.awt;
-import javax.media.opengl.*;
+import javax.media.opengl.GLAutoDrawable;
public interface AWTGLAutoDrawable extends GLAutoDrawable, ComponentEvents {
/** Requests a new width and height for this AWTGLAutoDrawable. */
diff --git a/src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java b/src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java
index 0c4f63c2d..5feaa5760 100644
--- a/src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java
+++ b/src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java
@@ -1,21 +1,21 @@
/*
* Copyright (c) 2003 Sun Microsystems, Inc. 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
@@ -28,11 +28,11 @@
* 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.
*/
diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
index 0a75865e2..abf670c95 100644
--- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
+++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
@@ -1,22 +1,22 @@
/*
* 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
@@ -29,11 +29,11 @@
* 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.
*/
@@ -44,29 +44,32 @@ import java.beans.Beans;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
-
import java.awt.Canvas;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
+import java.awt.event.HierarchyEvent;
+import java.awt.event.HierarchyListener;
+import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Rectangle2D;
-
import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
+import java.util.List;
import javax.media.nativewindow.AbstractGraphicsConfiguration;
import javax.media.nativewindow.OffscreenLayerOption;
+import javax.media.nativewindow.VisualIDHolder;
import javax.media.nativewindow.WindowClosingProtocol;
import javax.media.nativewindow.AbstractGraphicsDevice;
import javax.media.nativewindow.AbstractGraphicsScreen;
import javax.media.nativewindow.GraphicsConfigurationFactory;
import javax.media.nativewindow.NativeSurface;
import javax.media.nativewindow.NativeWindowFactory;
-
import javax.media.opengl.GL;
import javax.media.opengl.GLAnimatorControl;
import javax.media.opengl.GLAutoDrawable;
@@ -80,21 +83,29 @@ import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLException;
import javax.media.opengl.GLProfile;
import javax.media.opengl.GLRunnable;
+import javax.media.opengl.GLSharedContextSetter;
import javax.media.opengl.Threading;
import com.jogamp.common.GlueGenVersion;
import com.jogamp.common.util.VersionUtil;
+import com.jogamp.common.util.awt.AWTEDTExecutor;
+import com.jogamp.common.util.locks.LockFactory;
+import com.jogamp.common.util.locks.RecursiveLock;
import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration;
import com.jogamp.nativewindow.awt.AWTGraphicsDevice;
import com.jogamp.nativewindow.awt.AWTGraphicsScreen;
+import com.jogamp.nativewindow.awt.AWTPrintLifecycle;
import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol;
import com.jogamp.nativewindow.awt.JAWTWindow;
import com.jogamp.opengl.JoglVersion;
+import com.jogamp.opengl.util.GLDrawableUtil;
+import com.jogamp.opengl.util.TileRenderer;
-import jogamp.common.awt.AWTEDTExecutor;
import jogamp.opengl.Debug;
import jogamp.opengl.GLContextImpl;
import jogamp.opengl.GLDrawableHelper;
+import jogamp.opengl.GLDrawableImpl;
+import jogamp.opengl.awt.AWTTilePainter;
// FIXME: Subclasses need to call resetGLFunctionAvailability() on their
// context whenever the displayChanged() function is called on our
@@ -106,7 +117,17 @@ import jogamp.opengl.GLDrawableHelper;
interfaces when adding a heavyweight doesn't work either because
of Z-ordering or LayoutManager problems.
*
- * <h5><A NAME="java2dgl">Java2D OpenGL Remarks</A></h5>
+ * <h5><a name="offscreenlayer">Offscreen Layer Remarks</a></h5>
+ *
+ * {@link OffscreenLayerOption#setShallUseOffscreenLayer(boolean) setShallUseOffscreenLayer(true)}
+ * maybe called to use an offscreen drawable (FBO or PBuffer) allowing
+ * the underlying JAWT mechanism to composite the image, if supported.
+ * <p>
+ * {@link OffscreenLayerOption#setShallUseOffscreenLayer(boolean) setShallUseOffscreenLayer(true)}
+ * is being called if {@link GLCapabilitiesImmutable#isOnscreen()} is <code>false</code>.
+ * </p>
+ *
+ * <h5><a name="java2dgl">Java2D OpenGL Remarks</a></h5>
*
* To avoid any conflicts with a potential Java2D OpenGL context,<br>
* you shall consider setting the following JVM properties:<br>
@@ -123,7 +144,7 @@ import jogamp.opengl.GLDrawableHelper;
* <li><pre>sun.java2d.noddraw=true</pre></li>
* </ul>
*
- * <h5><A NAME="backgrounderase">Disable Background Erase</A></h5>
+ * <h5><a name="backgrounderase">Disable Background Erase</a></h5>
*
* GLCanvas tries to disable background erase for the AWT Canvas
* before native peer creation (X11) and after it (Windows), <br>
@@ -135,34 +156,44 @@ import jogamp.opengl.GLDrawableHelper;
*/
@SuppressWarnings("serial")
-public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol, OffscreenLayerOption {
+public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol, OffscreenLayerOption, AWTPrintLifecycle, GLSharedContextSetter {
private static final boolean DEBUG = Debug.debug("GLCanvas");
- private GLDrawableHelper drawableHelper = new GLDrawableHelper();
+ private final RecursiveLock lock = LockFactory.createRecursiveLock();
+ private final GLDrawableHelper helper = new GLDrawableHelper();
private AWTGraphicsConfiguration awtConfig;
- private volatile GLDrawable drawable;
- private GLContextImpl context;
- private boolean sendReshape = false;
-
+ private volatile GLDrawableImpl drawable; // volatile: avoid locking for read-only access
+ private volatile JAWTWindow jawtWindow; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle
+ private volatile GLContextImpl context; // volatile: avoid locking for read-only access
+ private volatile boolean sendReshape = false; // volatile: maybe written by EDT w/o locking
+
// copy of the cstr args, mainly for recreation
- private GLCapabilitiesImmutable capsReqUser;
- private GLCapabilitiesChooser chooser;
- private GLContext shareWith;
- private int additionalCtxCreationFlags = 0;
- private GraphicsDevice device;
+ private final GLCapabilitiesImmutable capsReqUser;
+ private final GLCapabilitiesChooser chooser;
+ private int additionalCtxCreationFlags = 0;
+ private final GraphicsDevice device;
private boolean shallUseOffscreenLayer = false;
- private AWTWindowClosingProtocol awtWindowClosingProtocol =
+ private volatile boolean isShowing;
+ private final HierarchyListener hierarchyListener = new HierarchyListener() {
+ @Override
+ public void hierarchyChanged(HierarchyEvent e) {
+ isShowing = GLCanvas.this.isShowing();
+ }
+ };
+
+ private final AWTWindowClosingProtocol awtWindowClosingProtocol =
new AWTWindowClosingProtocol(this, new Runnable() {
+ @Override
public void run() {
- GLCanvas.this.destroy();
+ GLCanvas.this.destroyImpl( true );
}
- });
+ }, null);
/** Creates a new GLCanvas component with a default set of OpenGL
capabilities, using the default OpenGL capabilities selection
- mechanism, on the default screen device.
+ mechanism, on the default screen device.
* @throws GLException if no default profile is available for the default desktop device.
*/
public GLCanvas() throws GLException {
@@ -171,7 +202,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
/** Creates a new GLCanvas component with the requested set of
OpenGL capabilities, using the default OpenGL capabilities
- selection mechanism, on the default screen device.
+ selection mechanism, on the default screen device.
* @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
* @see GLCanvas#GLCanvas(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, javax.media.opengl.GLContext, java.awt.GraphicsDevice)
*/
@@ -186,9 +217,11 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
*
* @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
* @see GLCanvas#GLCanvas(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, javax.media.opengl.GLContext, java.awt.GraphicsDevice)
+ * @deprecated Use {@link #GLCanvas(GLCapabilitiesImmutable)}
+ * and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}.
*/
- public GLCanvas(GLCapabilitiesImmutable capsReqUser, GLContext shareWith)
- throws GLException
+ public GLCanvas(GLCapabilitiesImmutable capsReqUser, GLContext shareWith)
+ throws GLException
{
this(capsReqUser, null, shareWith, null);
}
@@ -198,23 +231,45 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
default set of capabilities is used. The GLCapabilitiesChooser
specifies the algorithm for selecting one of the available
GLCapabilities for the component; a DefaultGLCapabilitesChooser
+ is used if null is passed for this argument.
+ The passed GraphicsDevice indicates the screen on
+ which to create the GLCanvas; the GLDrawableFactory uses the
+ default screen device of the local GraphicsEnvironment if null
+ is passed for this argument.
+ * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
+ */
+ public GLCanvas(GLCapabilitiesImmutable capsReqUser,
+ GLCapabilitiesChooser chooser,
+ GraphicsDevice device)
+ throws GLException
+ {
+ this(capsReqUser, chooser, null, device);
+ }
+
+ /** Creates a new GLCanvas component. The passed GLCapabilities
+ specifies the OpenGL capabilities for the component; if null, a
+ default set of capabilities is used. The GLCapabilitiesChooser
+ specifies the algorithm for selecting one of the available
+ GLCapabilities for the component; a DefaultGLCapabilitesChooser
is used if null is passed for this argument. The passed
GLContext specifies an OpenGL context with which to share
textures, display lists and other OpenGL state, and may be null
if sharing is not desired. See the note in the overview
documentation on <a
- href="../../../overview-summary.html#SHARING">context
+ href="../../../spec-overview.html#SHARING">context
sharing</a>. The passed GraphicsDevice indicates the screen on
which to create the GLCanvas; the GLDrawableFactory uses the
default screen device of the local GraphicsEnvironment if null
- is passed for this argument.
+ is passed for this argument.
* @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
+ * @deprecated Use {@link #GLCanvas(GLCapabilitiesImmutable, GLCapabilitiesChooser, GraphicsDevice)}
+ * and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}.
*/
public GLCanvas(GLCapabilitiesImmutable capsReqUser,
GLCapabilitiesChooser chooser,
GLContext shareWith,
- GraphicsDevice device)
- throws GLException
+ GraphicsDevice device)
+ throws GLException
{
/*
* Determination of the native window is made in 'super.addNotify()',
@@ -231,6 +286,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
// don't allow the user to change data
capsReqUser = (GLCapabilitiesImmutable) capsReqUser.cloneMutable();
}
+ if(!capsReqUser.isOnscreen()) {
+ setShallUseOffscreenLayer(true); // trigger offscreen layer - if supported
+ }
if(null==device) {
GraphicsConfiguration gc = super.getGraphicsConfiguration();
@@ -242,37 +300,61 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
// instantiation will be issued in addNotify()
this.capsReqUser = capsReqUser;
this.chooser = chooser;
- this.shareWith = shareWith;
+ if( null != shareWith ) {
+ helper.setSharedContext(null, shareWith);
+ }
this.device = device;
+
+ this.addHierarchyListener(hierarchyListener);
+ this.isShowing = isShowing();
+ }
+
+ @Override
+ public final void setSharedContext(GLContext sharedContext) throws IllegalStateException {
+ helper.setSharedContext(this.context, sharedContext);
+ }
+
+ @Override
+ public final void setSharedAutoDrawable(GLAutoDrawable sharedAutoDrawable) throws IllegalStateException {
+ helper.setSharedAutoDrawable(this, sharedAutoDrawable);
+ }
+
+ @Override
+ public final Object getUpstreamWidget() {
+ return this;
}
+ @Override
public void setShallUseOffscreenLayer(boolean v) {
shallUseOffscreenLayer = v;
}
+ @Override
public final boolean getShallUseOffscreenLayer() {
- return shallUseOffscreenLayer;
+ return shallUseOffscreenLayer;
}
+ @Override
public final boolean isOffscreenLayerSurfaceEnabled() {
- if(null != drawable) {
- return ((JAWTWindow)drawable.getNativeSurface()).isOffscreenLayerSurfaceEnabled();
+ final JAWTWindow _jawtWindow = jawtWindow;
+ if(null != _jawtWindow) {
+ return _jawtWindow.isOffscreenLayerSurfaceEnabled();
}
return false;
}
-
+
/**
* Overridden to choose a GraphicsConfiguration on a parent container's
* GraphicsDevice because both devices
*/
- @Override
+ @Override
public GraphicsConfiguration getGraphicsConfiguration() {
/*
* Workaround for problems with Xinerama and java.awt.Component.checkGD
* when adding to a container on a different graphics device than the
* one that this Canvas is associated with.
- *
+ *
* GC will be null unless:
* - A native peer has assigned it. This means we have a native
* peer, and are already comitted to a graphics configuration.
@@ -282,11 +364,16 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
* all platforms since the peer hasn't been created.
*/
final GraphicsConfiguration gc = super.getGraphicsConfiguration();
+
+ if( Beans.isDesignTime() ) {
+ return gc;
+ }
+
/*
* chosen is only non-null on platforms where the GLDrawableFactory
* returns a non-null GraphicsConfiguration (in the GLCanvas
* constructor).
- *
+ *
* if gc is from this Canvas' native peer then it should equal chosen,
* otherwise it is from an ancestor component that this Canvas is being
* added to, and we go into this block.
@@ -298,21 +385,21 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
* Check for compatibility with gc. If they differ by only the
* device then return a new GCconfig with the super-class' GDevice
* (and presumably the same visual ID in Xinerama).
- *
+ *
*/
if (!chosen.getDevice().getIDstring().equals(gc.getDevice().getIDstring())) {
/*
* Here we select a GraphicsConfiguration on the alternate
* device that is presumably identical to the chosen
* configuration, but on the other device.
- *
+ *
* Should really check to ensure that we select a configuration
* with the same X visual ID for Xinerama screens, otherwise the
* GLDrawable may have the wrong visual ID (I don't think this
* ever gets updated). May need to add a method to
* X11GLDrawableFactory to do this in a platform specific
* manner.
- *
+ *
* However, on platforms where we can actually get into this
* block, both devices should have the same visual list, and the
* same configuration should be selected here.
@@ -330,7 +417,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
System.err.println("Created Config (n): HAVE CF "+awtConfig);
System.err.println("Created Config (n): Choosen CF "+config);
System.err.println("Created Config (n): EQUALS CAPS "+equalCaps);
- Thread.dumpStack();
+ // Thread.dumpStack();
}
if (compatible != null) {
@@ -340,10 +427,15 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
*/
chosen = compatible;
- awtConfig = config;
-
if( !equalCaps && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) {
- dispose(true);
+ // complete destruction!
+ destroyImpl( true );
+ // recreation!
+ awtConfig = config;
+ createJAWTDrawableAndContext();
+ validateGLDrawable();
+ } else {
+ awtConfig = config;
}
}
}
@@ -353,7 +445,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
* return the GC that was selected in the constructor (and might
* cause an exception in Component.checkGD when adding to a
* container, but in this case that would be the desired behavior).
- *
+ *
*/
return chosen;
} else if (gc == null) {
@@ -372,29 +464,72 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
*/
return gc;
}
-
- public GLContext createContext(GLContext shareWith) {
- return (null != drawable) ? drawable.createContext(shareWith) : null;
+
+ @Override
+ public GLContext createContext(final GLContext shareWith) {
+ final RecursiveLock _lock = lock;
+ _lock.lock();
+ try {
+ if(drawable != null) {
+ final GLContext _ctx = drawable.createContext(shareWith);
+ _ctx.setContextCreationFlags(additionalCtxCreationFlags);
+ return _ctx;
+ }
+ return null;
+ } finally {
+ _lock.unlock();
+ }
+ }
+
+ private final void setRealizedImpl(boolean realized) {
+ final RecursiveLock _lock = lock;
+ _lock.lock();
+ try {
+ final GLDrawable _drawable = drawable;
+ if( null == _drawable || realized == _drawable.isRealized() ||
+ realized && ( 0 >= _drawable.getWidth() || 0 >= _drawable.getHeight() ) ) {
+ return;
+ }
+ _drawable.setRealized(realized);
+ if( realized && _drawable.isRealized() ) {
+ sendReshape=true; // ensure a reshape is being send ..
+ }
+ } finally {
+ _lock.unlock();
+ }
}
+ private final Runnable realizeOnEDTAction = new Runnable() {
+ @Override
+ public void run() { setRealizedImpl(true); }
+ };
+ private final Runnable unrealizeOnEDTAction = new Runnable() {
+ @Override
+ public void run() { setRealizedImpl(false); }
+ };
- public void setRealized(boolean realized) {
+ @Override
+ public final void setRealized(boolean realized) {
+ // Make sure drawable realization happens on AWT-EDT and only there. Consider the AWTTree lock!
+ AWTEDTExecutor.singleton.invoke(getTreeLock(), false /* allowOnNonEDT */, true /* wait */, realized ? realizeOnEDTAction : unrealizeOnEDTAction);
}
+ @Override
public boolean isRealized() {
- return ( null != drawable ) ? drawable.isRealized() : false;
- }
- protected final boolean isRealizedImpl() {
- return ( null != drawable ) ? drawable.isRealized() : false;
+ final GLDrawable _drawable = drawable;
+ return ( null != _drawable ) ? _drawable.isRealized() : false;
}
+ @Override
public WindowClosingMode getDefaultCloseOperation() {
return awtWindowClosingProtocol.getDefaultCloseOperation();
}
+ @Override
public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) {
return awtWindowClosingProtocol.setDefaultCloseOperation(op);
}
+ @Override
public void display() {
if( !validateGLDrawable() ) {
if(DEBUG) {
@@ -402,63 +537,43 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
}
return; // not yet available ..
}
- Threading.invoke(true, displayOnEventDispatchThreadAction, getTreeLock());
-
- awtWindowClosingProtocol.addClosingListenerOneShot();
- }
-
- private void dispose(boolean regenerate) {
- final GLAnimatorControl animator = getAnimator();
- if(DEBUG) {
- System.err.println(getThreadName()+": Info: dispose("+regenerate+") - START, hasContext " +
- (null!=context) + ", hasDrawable " + (null!=drawable)+", "+animator);
- Thread.dumpStack();
- }
-
- if(null!=context) {
- boolean animatorPaused = false;
- if(null!=animator) {
- // can't remove us from animator for recreational addNotify()
- animatorPaused = animator.pause();
- }
-
- disposeRegenerate=regenerate;
-
- if(context.isCreated()) {
- Threading.invoke(true, disposeOnEventDispatchThreadAction, getTreeLock());
- }
-
- if(animatorPaused) {
- animator.resume();
- }
- }
-
- if(!regenerate) {
- disposeAbstractGraphicsDevice();
- }
-
- if(DEBUG) {
- System.err.println(getThreadName()+": dispose("+regenerate+") - END, "+animator);
+ if( isShowing && !printActive ) {
+ Threading.invoke(true, displayOnEDTAction, getTreeLock());
}
}
/**
- * Just an alias for removeNotify
+ * {@inheritDoc}
+ *
+ * <p>
+ * This impl. only destroys all GL related resources.
+ * </p>
+ * <p>
+ * This impl. does not remove the GLCanvas from it's parent AWT container
+ * so this class's {@link #removeNotify()} AWT override won't get called.
+ * To do so, remove this component from it's parent AWT container.
+ * </p>
*/
+ @Override
public void destroy() {
- removeNotify();
+ destroyImpl( false );
+ }
+
+ protected void destroyImpl(boolean destroyJAWTWindowAndAWTDevice) {
+ Threading.invoke(true, destroyOnEDTAction, getTreeLock());
+ if( destroyJAWTWindowAndAWTDevice ) {
+ AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, disposeJAWTWindowAndAWTDeviceOnEDT);
+ }
}
/** Overridden to cause OpenGL rendering to be performed during
repaint cycles. Subclasses which override this method must call
super.paint() in their paint() method in order to function
- properly. <P>
-
- <B>Overrides:</B>
- <DL><DD><CODE>paint</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
- @Override
+ properly.
+ */
+ @Override
public void paint(Graphics g) {
- if (Beans.isDesignTime()) {
+ if( Beans.isDesignTime() ) {
// Make GLCanvas behave better in NetBeans GUI builder
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
@@ -476,9 +591,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
g.drawString(name,
(int) ((getWidth() - bounds.getWidth()) / 2),
(int) ((getHeight() + bounds.getHeight()) / 2));
- return;
- }
- if( ! this.drawableHelper.isAnimatorAnimating() ) {
+ } else if( !this.helper.isAnimatorAnimatingOnOtherThread() ) {
display();
}
}
@@ -493,83 +606,114 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
@SuppressWarnings("deprecation")
@Override
public void addNotify() {
- if(DEBUG) {
- System.err.println(getThreadName()+": Info: addNotify - start, bounds: "+this.getBounds());
- Thread.dumpStack();
- }
+ final RecursiveLock _lock = lock;
+ _lock.lock();
+ try {
+ final boolean isBeansDesignTime = Beans.isDesignTime();
- /**
- * 'super.addNotify()' determines the GraphicsConfiguration,
- * while calling this class's overriden 'getGraphicsConfiguration()' method
- * after which it creates the native peer.
- * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration
- * is being used in getGraphicsConfiguration().
- * This code order also allows recreation, ie re-adding the GLCanvas.
- */
- awtConfig = chooseGraphicsConfiguration(capsReqUser, capsReqUser, chooser, device);
- if(null==awtConfig) {
- throw new GLException("Error: NULL AWTGraphicsConfiguration");
- }
+ if(DEBUG) {
+ System.err.println(getThreadName()+": Info: addNotify - start, bounds: "+this.getBounds()+", isBeansDesignTime "+isBeansDesignTime);
+ // Thread.dumpStack();
+ }
- // before native peer is valid: X11
- disableBackgroundErase();
+ if( isBeansDesignTime ) {
+ super.addNotify();
+ } else {
+ /**
+ * 'super.addNotify()' determines the GraphicsConfiguration,
+ * while calling this class's overriden 'getGraphicsConfiguration()' method
+ * after which it creates the native peer.
+ * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration
+ * is being used in getGraphicsConfiguration().
+ * This code order also allows recreation, ie re-adding the GLCanvas.
+ */
+ awtConfig = chooseGraphicsConfiguration(capsReqUser, capsReqUser, chooser, device);
+ if(null==awtConfig) {
+ throw new GLException("Error: NULL AWTGraphicsConfiguration");
+ }
- // issues getGraphicsConfiguration() and creates the native peer
- super.addNotify();
+ // before native peer is valid: X11
+ disableBackgroundErase();
- // after native peer is valid: Windows
- disableBackgroundErase();
+ // issues getGraphicsConfiguration() and creates the native peer
+ super.addNotify();
- if (!Beans.isDesignTime()) {
- createDrawableAndContext();
- }
+ // after native peer is valid: Windows
+ disableBackgroundErase();
- // init drawable by paint/display makes the init sequence more equal
- // for all launch flavors (applet/javaws/..)
- // validateGLDrawable();
+ createJAWTDrawableAndContext();
- if(DEBUG) {
- System.err.println(getThreadName()+": Info: addNotify - end: peer: "+getPeer());
+ // init drawable by paint/display makes the init sequence more equal
+ // for all launch flavors (applet/javaws/..)
+ // validateGLDrawable();
+ }
+ awtWindowClosingProtocol.addClosingListener();
+
+ if(DEBUG) {
+ System.err.println(getThreadName()+": Info: addNotify - end: peer: "+getPeer());
+ }
+ } finally {
+ _lock.unlock();
}
}
- private void createDrawableAndContext() {
- // no lock required, since this resource ain't available yet
- final JAWTWindow jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig);
- jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer);
- jawtWindow.lockSurface();
- try {
- drawable = GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow);
- context = (GLContextImpl) drawable.createContext(shareWith);
- context.setContextCreationFlags(additionalCtxCreationFlags);
- } finally {
- jawtWindow.unlockSurface();
+ private void createJAWTDrawableAndContext() {
+ if ( !Beans.isDesignTime() ) {
+ jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig);
+ jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer);
+ jawtWindow.lockSurface();
+ try {
+ drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow);
+ createContextImpl(drawable);
+ } finally {
+ jawtWindow.unlockSurface();
+ }
}
}
-
- private boolean validateGLDrawable() {
- boolean realized = false;
- if (!Beans.isDesignTime()) {
- if ( null != drawable ) { // OK: drawable is volatile
- realized = drawable.isRealized();
- if ( !realized && 0 < drawable.getWidth() * drawable.getHeight() ) {
- // make sure drawable realization happens on AWT EDT, due to AWTTree lock
- AWTEDTExecutor.singleton.invoke(true, setRealizedOnEventDispatchThreadAction);
- realized = true;
- sendReshape=true; // ensure a reshape is being send ..
- if(DEBUG) {
- System.err.println(getThreadName()+": Realized Drawable: "+drawable.toString());
- Thread.dumpStack();
- }
- }
+ private boolean createContextImpl(final GLDrawable drawable) {
+ final GLContext[] shareWith = { null };
+ if( !helper.isSharedGLContextPending(shareWith) ) {
+ context = (GLContextImpl) drawable.createContext(shareWith[0]);
+ context.setContextCreationFlags(additionalCtxCreationFlags);
+ if(DEBUG) {
+ System.err.println(getThreadName()+": Context created: has shared "+(null != shareWith[0]));
}
+ return true;
+ } else {
+ if(DEBUG) {
+ System.err.println(getThreadName()+": Context !created: pending share");
+ }
+ return false;
}
- return realized;
}
- private Runnable setRealizedOnEventDispatchThreadAction = new Runnable() {
- public void run() {
- drawable.setRealized(true);
- } };
+
+ private boolean validateGLDrawable() {
+ if( Beans.isDesignTime() || !isDisplayable() ) {
+ return false; // early out!
+ }
+ final GLDrawable _drawable = drawable;
+ if ( null != _drawable ) {
+ boolean res = _drawable.isRealized();
+ if( !res ) {
+ // re-try drawable creation
+ if( 0 >= _drawable.getWidth() || 0 >= _drawable.getHeight() ) {
+ return false; // early out!
+ }
+ setRealized(true);
+ res = _drawable.isRealized();
+ if(DEBUG) {
+ System.err.println(getThreadName()+": Realized Drawable: isRealized "+res+", "+_drawable.toString());
+ // Thread.dumpStack();
+ }
+ }
+ if( res && null == context ) {
+ // re-try context creation
+ res = createContextImpl(_drawable); // pending creation.
+ }
+ return res;
+ }
+ return false;
+ }
/** <p>Overridden to track when this component is removed from a
container. Subclasses which override this method must call
@@ -584,20 +728,17 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
public void removeNotify() {
if(DEBUG) {
System.err.println(getThreadName()+": Info: removeNotify - start");
- Thread.dumpStack();
+ // Thread.dumpStack();
}
awtWindowClosingProtocol.removeClosingListener();
- if (Beans.isDesignTime()) {
+ if( Beans.isDesignTime() ) {
super.removeNotify();
} else {
try {
- dispose(false);
+ destroyImpl( true );
} finally {
- context=null;
- drawable=null;
- awtConfig=null;
super.removeNotify();
}
}
@@ -616,229 +757,524 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
@SuppressWarnings("deprecation")
@Override
public void reshape(int x, int y, int width, int height) {
- super.reshape(x, y, width, height);
- if(null != drawable && drawable.isRealized() && !drawable.getChosenGLCapabilities().isOnscreen()) {
- dispose(true);
- } else {
- sendReshape = true;
+ synchronized (getTreeLock()) { // super.reshape(..) claims tree lock, so we do extend it's lock over reshape
+ super.reshape(x, y, width, height);
+
+ if(DEBUG) {
+ final NativeSurface ns = getNativeSurface();
+ final long nsH = null != ns ? ns.getSurfaceHandle() : 0;
+ System.err.println("GLCanvas.sizeChanged: ("+getThreadName()+"): "+width+"x"+height+" - surfaceHandle 0x"+Long.toHexString(nsH));
+ // Thread.dumpStack();
+ }
+ if( validateGLDrawable() && !printActive ) {
+ final GLDrawableImpl _drawable = drawable;
+ if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) {
+ final RecursiveLock _lock = lock;
+ _lock.lock();
+ try {
+ final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, width, height);
+ if(_drawable != _drawableNew) {
+ // write back
+ drawable = _drawableNew;
+ }
+ } finally {
+ _lock.unlock();
+ }
+ }
+ sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock
+ }
}
}
- /** <B>Overrides:</B>
- <DL><DD><CODE>update</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
- /**
+ /**
* Overridden from Canvas to prevent the AWT's clearing of the
* canvas from interfering with the OpenGL rendering.
*/
- @Override
+ @Override
public void update(Graphics g) {
paint(g);
}
-
+
+ private volatile boolean printActive = false;
+ private GLAnimatorControl printAnimator = null;
+ private GLAutoDrawable printGLAD = null;
+ private AWTTilePainter printAWTTiles = null;
+
+ @Override
+ public void setupPrint(double scaleMatX, double scaleMatY, int numSamples, int tileWidth, int tileHeight) {
+ printActive = true;
+ final int componentCount = isOpaque() ? 3 : 4;
+ final TileRenderer printRenderer = new TileRenderer();
+ printAWTTiles = new AWTTilePainter(printRenderer, componentCount, scaleMatX, scaleMatY, numSamples, tileWidth, tileHeight, DEBUG);
+ AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, setupPrintOnEDT);
+ }
+ private final Runnable setupPrintOnEDT = new Runnable() {
+ @Override
+ public void run() {
+ if( !validateGLDrawable() ) {
+ if(DEBUG) {
+ System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, drawable not valid yet");
+ }
+ printActive = false;
+ return; // not yet available ..
+ }
+ if( !isShowing ) {
+ if(DEBUG) {
+ System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, drawable valid, canvas not showing");
+ }
+ printActive = false;
+ return; // not yet available ..
+ }
+ sendReshape = false; // clear reshape flag
+ printAnimator = helper.getAnimator();
+ if( null != printAnimator ) {
+ printAnimator.remove(GLCanvas.this);
+ }
+ printGLAD = GLCanvas.this; // _not_ default, shall be replaced by offscreen GLAD
+ final GLCapabilities caps = (GLCapabilities)getChosenGLCapabilities().cloneMutable();
+ final int printNumSamples = printAWTTiles.getNumSamples(caps);
+ GLDrawable printDrawable = printGLAD.getDelegatedDrawable();
+ final boolean reqNewGLADSamples = printNumSamples != caps.getNumSamples();
+ final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getWidth() ||
+ printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getHeight();
+ final boolean reqNewGLADOnscrn = caps.isOnscreen();
+ // It is desired to use a new offscreen GLAD, however Bug 830 forbids this for AA onscreen context.
+ // Bug 830: swapGLContextAndAllGLEventListener and onscreen MSAA w/ NV/GLX
+ final boolean reqNewGLAD = !caps.getSampleBuffers() && ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize );
+ if( DEBUG ) {
+ System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+"], "+
+ ", drawableSize "+printDrawable.getWidth()+"x"+printDrawable.getHeight()+
+ ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+
+ ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+
+ ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator);
+ }
+ if( reqNewGLAD ) {
+ caps.setDoubleBuffered(false);
+ caps.setOnscreen(false);
+ if( printNumSamples != caps.getNumSamples() ) {
+ caps.setSampleBuffers(0 < printNumSamples);
+ caps.setNumSamples(printNumSamples);
+ }
+ final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile());
+ printGLAD = factory.createOffscreenAutoDrawable(null, caps, null,
+ printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE,
+ printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE);
+ GLDrawableUtil.swapGLContextAndAllGLEventListener(GLCanvas.this, printGLAD);
+ printDrawable = printGLAD.getDelegatedDrawable();
+ }
+ printAWTTiles.setGLOrientation(printGLAD.isGLOriented(), printGLAD.isGLOriented());
+ printAWTTiles.renderer.setTileSize(printDrawable.getWidth(), printDrawable.getHeight(), 0);
+ printAWTTiles.renderer.attachAutoDrawable(printGLAD);
+ if( DEBUG ) {
+ System.err.println("AWT print.setup "+printAWTTiles);
+ System.err.println("AWT print.setup AA "+printNumSamples+", "+caps);
+ System.err.println("AWT print.setup printGLAD: "+printGLAD.getWidth()+"x"+printGLAD.getHeight()+", "+printGLAD);
+ System.err.println("AWT print.setup printDraw: "+printDrawable.getWidth()+"x"+printDrawable.getHeight()+", "+printDrawable);
+ }
+ }
+ };
+
+ @Override
+ public void releasePrint() {
+ if( !printActive || null == printGLAD ) {
+ throw new IllegalStateException("setupPrint() not called");
+ }
+ sendReshape = false; // clear reshape flag
+ AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, releasePrintOnEDT);
+ }
+ private final Runnable releasePrintOnEDT = new Runnable() {
+ @Override
+ public void run() {
+ if( DEBUG ) {
+ System.err.println("AWT print.release "+printAWTTiles);
+ }
+ printAWTTiles.dispose();
+ printAWTTiles= null;
+ if( printGLAD != GLCanvas.this ) {
+ GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLCanvas.this);
+ printGLAD.destroy();
+ }
+ printGLAD = null;
+ if( null != printAnimator ) {
+ printAnimator.add(GLCanvas.this);
+ printAnimator = null;
+ }
+ sendReshape = true; // trigger reshape, i.e. gl-viewport and -listener - this component might got resized!
+ printActive = false;
+ display();
+ }
+ };
+
+ @Override
+ public void print(Graphics graphics) {
+ if( !printActive || null == printGLAD ) {
+ throw new IllegalStateException("setupPrint() not called");
+ }
+ if(DEBUG && !EventQueue.isDispatchThread()) {
+ System.err.println(getThreadName()+": Warning: GLCanvas print - not called from AWT-EDT");
+ // we cannot dispatch print on AWT-EDT due to printing internal locking ..
+ }
+ sendReshape = false; // clear reshape flag
+
+ final Graphics2D g2d = (Graphics2D)graphics;
+ try {
+ printAWTTiles.setupGraphics2DAndClipBounds(g2d, getWidth(), getHeight());
+ final TileRenderer tileRenderer = printAWTTiles.renderer;
+ if( DEBUG ) {
+ System.err.println("AWT print.0: "+tileRenderer);
+ }
+ if( !tileRenderer.eot() ) {
+ try {
+ do {
+ if( printGLAD != GLCanvas.this ) {
+ tileRenderer.display();
+ } else {
+ Threading.invoke(true, displayOnEDTAction, getTreeLock());
+ }
+ } while ( !tileRenderer.eot() );
+ if( DEBUG ) {
+ System.err.println("AWT print.1: "+printAWTTiles);
+ }
+ } finally {
+ tileRenderer.reset();
+ printAWTTiles.resetGraphics2D();
+ }
+ }
+ } catch (NoninvertibleTransformException nte) {
+ System.err.println("Catched: Inversion failed of: "+g2d.getTransform());
+ nte.printStackTrace();
+ }
+ if( DEBUG ) {
+ System.err.println("AWT print.X: "+printAWTTiles);
+ }
+ }
+
+ @Override
public void addGLEventListener(GLEventListener listener) {
- drawableHelper.addGLEventListener(listener);
+ helper.addGLEventListener(listener);
+ }
+
+ @Override
+ public void addGLEventListener(int index, GLEventListener listener) throws IndexOutOfBoundsException {
+ helper.addGLEventListener(index, listener);
+ }
+
+ @Override
+ public int getGLEventListenerCount() {
+ return helper.getGLEventListenerCount();
+ }
+
+ @Override
+ public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException {
+ return helper.getGLEventListener(index);
+ }
+
+ @Override
+ public boolean areAllGLEventListenerInitialized() {
+ return helper.areAllGLEventListenerInitialized();
+ }
+
+ @Override
+ public boolean getGLEventListenerInitState(GLEventListener listener) {
+ return helper.getGLEventListenerInitState(listener);
}
- public void addGLEventListener(int index, GLEventListener listener) {
- drawableHelper.addGLEventListener(index, listener);
+ @Override
+ public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) {
+ helper.setGLEventListenerInitState(listener, initialized);
}
- public void removeGLEventListener(GLEventListener listener) {
- drawableHelper.removeGLEventListener(listener);
+ @Override
+ public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) {
+ final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove);
+ Threading.invoke(true, r, getTreeLock());
+ return r.listener;
}
+ @Override
+ public GLEventListener removeGLEventListener(GLEventListener listener) {
+ return helper.removeGLEventListener(listener);
+ }
+
+ @Override
public void setAnimator(GLAnimatorControl animatorControl) {
- drawableHelper.setAnimator(animatorControl);
+ helper.setAnimator(animatorControl);
}
+ @Override
public GLAnimatorControl getAnimator() {
- return drawableHelper.getAnimator();
+ return helper.getAnimator();
}
- public void invoke(boolean wait, GLRunnable glRunnable) {
- drawableHelper.invoke(this, wait, glRunnable);
+ @Override
+ public final Thread setExclusiveContextThread(Thread t) throws GLException {
+ return helper.setExclusiveContextThread(t, context);
}
- public void setContext(GLContext ctx) {
- context=(GLContextImpl)ctx;
- if(null != context) {
- context.setContextCreationFlags(additionalCtxCreationFlags);
- }
+ @Override
+ public final Thread getExclusiveContextThread() {
+ return helper.getExclusiveContextThread();
+ }
+
+ @Override
+ public boolean invoke(boolean wait, GLRunnable glRunnable) {
+ return helper.invoke(this, wait, glRunnable);
+ }
+
+ @Override
+ public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) {
+ return helper.invoke(this, wait, glRunnables);
}
+ @Override
+ public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) {
+ final RecursiveLock _lock = lock;
+ _lock.lock();
+ try {
+ final GLContext oldCtx = context;
+ GLDrawableHelper.switchContext(drawable, oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags);
+ context=(GLContextImpl)newCtx;
+ return oldCtx;
+ } finally {
+ _lock.unlock();
+ }
+ }
+
+ @Override
+ public final GLDrawable getDelegatedDrawable() {
+ return drawable;
+ }
+
+ @Override
public GLContext getContext() {
return context;
}
+ @Override
public GL getGL() {
- if (Beans.isDesignTime()) {
+ if( Beans.isDesignTime() ) {
return null;
}
- GLContext ctx = getContext();
- return (ctx == null) ? null : ctx.getGL();
+ final GLContext _context = context;
+ return (_context == null) ? null : _context.getGL();
}
+ @Override
public GL setGL(GL gl) {
- GLContext ctx = getContext();
- if (ctx != null) {
- ctx.setGL(gl);
+ final GLContext _context = context;
+ if (_context != null) {
+ _context.setGL(gl);
return gl;
}
return null;
}
+ @Override
public void setAutoSwapBufferMode(boolean onOrOff) {
- drawableHelper.setAutoSwapBufferMode(onOrOff);
+ helper.setAutoSwapBufferMode(onOrOff);
}
+ @Override
public boolean getAutoSwapBufferMode() {
- return drawableHelper.getAutoSwapBufferMode();
+ return helper.getAutoSwapBufferMode();
}
+ @Override
public void swapBuffers() {
- Threading.invoke(true, swapBuffersOnEventDispatchThreadAction, getTreeLock());
+ Threading.invoke(true, swapBuffersOnEDTAction, getTreeLock());
}
+ @Override
public void setContextCreationFlags(int flags) {
additionalCtxCreationFlags = flags;
+ final GLContext _context = context;
+ if(null != _context) {
+ _context.setContextCreationFlags(additionalCtxCreationFlags);
+ }
}
-
+
+ @Override
public int getContextCreationFlags() {
- return additionalCtxCreationFlags;
+ return additionalCtxCreationFlags;
}
-
+
+ @Override
public GLProfile getGLProfile() {
return capsReqUser.getGLProfile();
}
+ @Override
public GLCapabilitiesImmutable getChosenGLCapabilities() {
- if (awtConfig == null) {
+ if( Beans.isDesignTime() ) {
+ return capsReqUser;
+ } else if( null == awtConfig ) {
throw new GLException("No AWTGraphicsConfiguration: "+this);
}
-
return (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities();
}
public GLCapabilitiesImmutable getRequestedGLCapabilities() {
- if (awtConfig == null) {
+ if( null == awtConfig ) {
return capsReqUser;
}
-
return (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities();
}
+ @Override
+ public boolean isGLOriented() {
+ final GLDrawable _drawable = drawable;
+ return null != _drawable ? _drawable.isGLOriented() : true;
+ }
+
+ @Override
public NativeSurface getNativeSurface() {
- return (null != drawable) ? drawable.getNativeSurface() : null;
+ final GLDrawable _drawable = drawable;
+ return (null != _drawable) ? _drawable.getNativeSurface() : null;
}
+ @Override
public long getHandle() {
- return (null != drawable) ? drawable.getHandle() : 0;
+ final GLDrawable _drawable = drawable;
+ return (null != _drawable) ? _drawable.getHandle() : 0;
}
+ @Override
public GLDrawableFactory getFactory() {
- return (null != drawable) ? drawable.getFactory() : null;
+ final GLDrawable _drawable = drawable;
+ return (null != _drawable) ? _drawable.getFactory() : null;
}
@Override
public String toString() {
- final int dw = (null!=drawable) ? drawable.getWidth() : -1;
- final int dh = (null!=drawable) ? drawable.getHeight() : -1;
-
+ final GLDrawable _drawable = drawable;
+ final int dw = (null!=_drawable) ? _drawable.getWidth() : -1;
+ final int dh = (null!=_drawable) ? _drawable.getHeight() : -1;
+
return "AWT-GLCanvas[Realized "+isRealized()+
- ",\n\t"+((null!=drawable)?drawable.getClass().getName():"null-drawable")+
+ ",\n\t"+((null!=_drawable)?_drawable.getClass().getName():"null-drawable")+
",\n\tFactory "+getFactory()+
",\n\thandle 0x"+Long.toHexString(getHandle())+
",\n\tDrawable size "+dw+"x"+dh+
",\n\tAWT pos "+getX()+"/"+getY()+", size "+getWidth()+"x"+getHeight()+
- ",\n\tvisible "+isVisible()+
+ ",\n\tvisible "+isVisible()+", displayable "+isDisplayable()+", showing "+isShowing+
",\n\t"+awtConfig+"]";
}
-
+
//----------------------------------------------------------------------
// Internals only below this point
//
- private boolean disposeRegenerate;
- private final Runnable postDisposeAction = new Runnable() {
+ private final Runnable destroyOnEDTAction = new Runnable() {
+ @Override
public void run() {
- context=null;
- if(null!=drawable) {
- final JAWTWindow jawtWindow = (JAWTWindow)drawable.getNativeSurface();
- drawable.setRealized(false);
- drawable=null;
- if(null!=jawtWindow) {
- jawtWindow.destroy();
- }
- }
-
- if(disposeRegenerate) {
- // Similar process as in addNotify()!
-
- // Recreate GLDrawable/GLContext to reflect it's new graphics configuration
- createDrawableAndContext();
-
- if(DEBUG) {
- System.err.println(getThreadName()+": GLCanvas.dispose(true): new drawable: "+drawable);
- }
- validateGLDrawable(); // immediate attempt to recreate the drawable
- }
- }
- };
+ final RecursiveLock _lock = lock;
+ _lock.lock();
+ try {
+ final GLAnimatorControl animator = getAnimator();
- private final Runnable disposeOnEventDispatchThreadAction = new Runnable() {
- public void run() {
- drawableHelper.disposeGL(GLCanvas.this, drawable, context, postDisposeAction);
- }
- };
+ if(DEBUG) {
+ System.err.println(getThreadName()+": Info: destroyOnEDTAction() - START, hasContext " +
+ (null!=context) + ", hasDrawable " + (null!=drawable)+", "+animator);
+ // Thread.dumpStack();
+ }
- private final Runnable disposeAbstractGraphicsDeviceAction = new Runnable() {
- public void run() {
- if(null != awtConfig) {
- final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration();
- final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice();
- final String adeviceMsg;
- if(DEBUG) {
- adeviceMsg = adevice.toString();
- } else {
- adeviceMsg = null;
- }
- boolean closed = adevice.close();
- if(DEBUG) {
- System.err.println(getThreadName()+": GLCanvas.dispose(false): closed GraphicsDevice: "+adeviceMsg+", result: "+closed);
- }
- awtConfig=null;
- }
+ final boolean animatorPaused;
+ if(null!=animator) {
+ // can't remove us from animator for recreational addNotify()
+ animatorPaused = animator.pause();
+ } else {
+ animatorPaused = false;
+ }
+
+ // OLS will be detached by disposeGL's context destruction below
+ if( null != context ) {
+ if( context.isCreated() ) {
+ // Catch dispose GLExceptions by GLEventListener, just 'print' them
+ // so we can continue with the destruction.
+ try {
+ helper.disposeGL(GLCanvas.this, context, true);
+ if(DEBUG) {
+ System.err.println(getThreadName()+": destroyOnEDTAction() - post ctx: "+context);
+ }
+ } catch (GLException gle) {
+ gle.printStackTrace();
+ }
+ }
+ context = null;
+ }
+ if( null != drawable ) {
+ drawable.setRealized(false);
+ if(DEBUG) {
+ System.err.println(getThreadName()+": destroyOnEDTAction() - post drawable: "+drawable);
+ }
+ drawable = null;
+ }
+
+ if(animatorPaused) {
+ animator.resume();
+ }
+
+ if(DEBUG) {
+ System.err.println(getThreadName()+": dispose() - END, animator "+animator);
+ }
+
+ } finally {
+ _lock.unlock();
+ }
}
};
/**
- * Disposes the AbstractGraphicsDevice within EDT,
+ * Disposes the JAWTWindow and AbstractGraphicsDevice within EDT,
* since resources created (X11: Display), must be destroyed in the same thread, where they have been created.
+ * <p>
+ * The drawable and context handle are null'ed as well, assuming {@link #destroy()} has been called already.
+ * </p>
*
* @see #chooseGraphicsConfiguration(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, java.awt.GraphicsDevice)
*/
- void disposeAbstractGraphicsDevice() {
- if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) {
- disposeAbstractGraphicsDeviceAction.run();
- } else {
- try {
- EventQueue.invokeAndWait(disposeAbstractGraphicsDeviceAction);
- } catch (InvocationTargetException e) {
- throw new GLException(e.getTargetException());
- } catch (InterruptedException e) {
- throw new GLException(e);
+ private final Runnable disposeJAWTWindowAndAWTDeviceOnEDT = new Runnable() {
+ @Override
+ public void run() {
+ context=null;
+ drawable=null;
+
+ if( null != jawtWindow ) {
+ jawtWindow.destroy();
+ if(DEBUG) {
+ System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post JAWTWindow: "+jawtWindow);
+ }
+ jawtWindow=null;
+ }
+
+ if(null != awtConfig) {
+ final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration();
+ final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice();
+ final String adeviceMsg;
+ if(DEBUG) {
+ adeviceMsg = adevice.toString();
+ } else {
+ adeviceMsg = null;
+ }
+ boolean closed = adevice.close();
+ if(DEBUG) {
+ System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post GraphicsDevice: "+adeviceMsg+", result: "+closed);
+ }
}
+ awtConfig=null;
}
- }
+ };
private final Runnable initAction = new Runnable() {
+ @Override
public void run() {
- drawableHelper.init(GLCanvas.this);
+ helper.init(GLCanvas.this, !sendReshape);
}
};
-
+
private final Runnable displayAction = new Runnable() {
+ @Override
public void run() {
if (sendReshape) {
if(DEBUG) {
@@ -846,33 +1282,62 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
}
// Note: we ignore the given x and y within the parent component
// since we are drawing directly into this heavyweight component.
- drawableHelper.reshape(GLCanvas.this, 0, 0, getWidth(), getHeight());
+ helper.reshape(GLCanvas.this, 0, 0, getWidth(), getHeight());
sendReshape = false;
}
- drawableHelper.display(GLCanvas.this);
+ helper.display(GLCanvas.this);
}
};
- private final Runnable swapBuffersAction = new Runnable() {
+ private final Runnable displayOnEDTAction = new Runnable() {
+ @Override
public void run() {
- drawable.swapBuffers();
+ final RecursiveLock _lock = lock;
+ _lock.lock();
+ try {
+ if( null != drawable && drawable.isRealized() ) {
+ helper.invokeGL(drawable, context, displayAction, initAction);
+ }
+ } finally {
+ _lock.unlock();
+ }
}
};
- // Workaround for ATI driver bugs related to multithreading issues
- // like simultaneous rendering via Animators to canvases that are
- // being resized on the AWT event dispatch thread
- private final Runnable displayOnEventDispatchThreadAction = new Runnable() {
+ private final Runnable swapBuffersOnEDTAction = new Runnable() {
+ @Override
public void run() {
- drawableHelper.invokeGL(drawable, context, displayAction, initAction);
+ final RecursiveLock _lock = lock;
+ _lock.lock();
+ try {
+ if( null != drawable && drawable.isRealized() ) {
+ drawable.swapBuffers();
+ }
+ } finally {
+ _lock.unlock();
+ }
}
};
-
- private final Runnable swapBuffersOnEventDispatchThreadAction = new Runnable() {
+
+ private class DisposeGLEventListenerAction implements Runnable {
+ GLEventListener listener;
+ private final boolean remove;
+ private DisposeGLEventListenerAction(GLEventListener listener, boolean remove) {
+ this.listener = listener;
+ this.remove = remove;
+ }
+
+ @Override
public void run() {
- drawableHelper.invokeGL(drawable, context, swapBuffersAction, initAction);
- }
+ final RecursiveLock _lock = lock;
+ _lock.lock();
+ try {
+ listener = helper.disposeGLEventListener(GLCanvas.this, drawable, context, listener, remove);
+ } finally {
+ _lock.unlock();
+ }
+ }
};
// Disables the AWT's erasing of this Canvas's background on Windows
@@ -886,6 +1351,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
if (!disableBackgroundEraseInitialized) {
try {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
public Object run() {
try {
Class<?> clazz = getToolkit().getClass();
@@ -935,36 +1401,37 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
* @param device
* @return the chosen AWTGraphicsConfiguration
*
- * @see #disposeAbstractGraphicsDevice()
+ * @see #disposeJAWTWindowAndAWTDeviceOnEDT
*/
private AWTGraphicsConfiguration chooseGraphicsConfiguration(final GLCapabilitiesImmutable capsChosen,
final GLCapabilitiesImmutable capsRequested,
final GLCapabilitiesChooser chooser,
final GraphicsDevice device) {
// Make GLCanvas behave better in NetBeans GUI builder
- if (Beans.isDesignTime()) {
+ if( Beans.isDesignTime() ) {
return null;
}
- final AbstractGraphicsScreen aScreen = null != device ?
+ final AbstractGraphicsScreen aScreen = null != device ?
AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT):
AWTGraphicsScreen.createDefault();
AWTGraphicsConfiguration config = null;
if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) {
config = (AWTGraphicsConfiguration)
- GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class).chooseGraphicsConfiguration(capsChosen,
+ GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen,
capsRequested,
- chooser, aScreen);
+ chooser, aScreen, VisualIDHolder.VID_UNDEFINED);
} else {
try {
final ArrayList<AWTGraphicsConfiguration> bucket = new ArrayList<AWTGraphicsConfiguration>(1);
EventQueue.invokeAndWait(new Runnable() {
+ @Override
public void run() {
AWTGraphicsConfiguration c = (AWTGraphicsConfiguration)
- GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class).chooseGraphicsConfiguration(capsChosen,
+ GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen,
capsRequested,
- chooser, aScreen);
+ chooser, aScreen, VisualIDHolder.VID_UNDEFINED);
bucket.add(c);
}
});
@@ -982,11 +1449,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
return config;
}
-
- protected static String getThreadName() {
- return Thread.currentThread().getName();
- }
-
+
+ protected static String getThreadName() { return Thread.currentThread().getName(); }
+
/**
* A most simple JOGL AWT test entry
*/
@@ -996,7 +1461,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
// System.err.println(NativeWindowVersion.getInstance());
System.err.println(JoglVersion.getInstance());
- System.err.println(JoglVersion.getDefaultOpenGLInfo(null).toString());
+ System.err.println(JoglVersion.getDefaultOpenGLInfo(null, null, true).toString());
final GLCapabilitiesImmutable caps = new GLCapabilities( GLProfile.getDefault(GLProfile.getDefaultDevice()) );
final Frame frame = new Frame("JOGL AWT Test");
@@ -1006,17 +1471,22 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
frame.setSize(128, 128);
glCanvas.addGLEventListener(new GLEventListener() {
+ @Override
public void init(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
System.err.println(JoglVersion.getGLInfo(gl, null));
}
+ @Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { }
+ @Override
public void display(GLAutoDrawable drawable) { }
+ @Override
public void dispose(GLAutoDrawable drawable) { }
});
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
public void run() {
frame.setVisible(true);
}});
@@ -1026,6 +1496,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
glCanvas.display();
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
public void run() {
frame.dispose();
}});
diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
index 167b99374..adc0a0d23 100644
--- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
+++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
@@ -1,22 +1,22 @@
/*
* 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
@@ -29,42 +29,42 @@
* 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 javax.media.opengl.awt;
-import java.nio.ByteBuffer;
-import java.nio.IntBuffer;
-import java.beans.Beans;
-
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
+import java.awt.event.HierarchyEvent;
+import java.awt.event.HierarchyListener;
+import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
-import javax.swing.JPanel;
+import java.beans.Beans;
+import java.nio.IntBuffer;
+import java.util.List;
-import javax.media.nativewindow.WindowClosingProtocol;
import javax.media.nativewindow.AbstractGraphicsDevice;
import javax.media.nativewindow.NativeSurface;
-import javax.media.nativewindow.WindowClosingProtocol.WindowClosingMode;
-
-import javax.media.opengl.DefaultGLCapabilitiesChooser;
+import javax.media.nativewindow.SurfaceUpdatedListener;
+import javax.media.nativewindow.WindowClosingProtocol;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
+import javax.media.opengl.GL2ES3;
import javax.media.opengl.GL2GL3;
import javax.media.opengl.GLAnimatorControl;
import javax.media.opengl.GLAutoDrawable;
@@ -76,129 +76,219 @@ import javax.media.opengl.GLDrawable;
import javax.media.opengl.GLDrawableFactory;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLException;
-import javax.media.opengl.GLPbuffer;
+import javax.media.opengl.GLFBODrawable;
import javax.media.opengl.GLProfile;
import javax.media.opengl.GLRunnable;
+import javax.media.opengl.GLSharedContextSetter;
import javax.media.opengl.Threading;
-
-import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol;
-import com.jogamp.opengl.util.FBObject;
-import com.jogamp.opengl.util.GLBuffers;
+import javax.swing.JPanel;
import jogamp.opengl.Debug;
import jogamp.opengl.GLContextImpl;
import jogamp.opengl.GLDrawableFactoryImpl;
import jogamp.opengl.GLDrawableHelper;
import jogamp.opengl.GLDrawableImpl;
+import jogamp.opengl.awt.AWTTilePainter;
import jogamp.opengl.awt.Java2D;
-import jogamp.opengl.awt.Java2DGLContext;
+import jogamp.opengl.util.glsl.GLSLTextureRaster;
-// FIXME: Subclasses need to call resetGLFunctionAvailability() on their
-// context whenever the displayChanged() function is called on their
-// GLEventListeners
+import com.jogamp.common.util.awt.AWTEDTExecutor;
+import com.jogamp.nativewindow.awt.AWTPrintLifecycle;
+import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol;
+import com.jogamp.opengl.FBObject;
+import com.jogamp.opengl.GLRendererQuirks;
+import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes;
+import com.jogamp.opengl.util.GLPixelBuffer.SingletonGLPixelBufferProvider;
+import com.jogamp.opengl.util.GLDrawableUtil;
+import com.jogamp.opengl.util.GLPixelStorageModes;
+import com.jogamp.opengl.util.TileRenderer;
+import com.jogamp.opengl.util.awt.AWTGLPixelBuffer;
+import com.jogamp.opengl.util.awt.AWTGLPixelBuffer.AWTGLPixelBufferProvider;
+import com.jogamp.opengl.util.awt.AWTGLPixelBuffer.SingleAWTGLPixelBufferProvider;
+import com.jogamp.opengl.util.texture.TextureState;
/** A lightweight Swing component which provides OpenGL rendering
support. Provided for compatibility with Swing user interfaces
when adding a heavyweight doesn't work either because of
Z-ordering or LayoutManager problems.
- <P>
+ <p>
The GLJPanel can be made transparent by creating it with a
GLCapabilities object with alpha bits specified and calling {@link
#setOpaque}(false). Pixels with resulting OpenGL alpha values less
- than 1.0 will be overlaid on any underlying Swing rendering. </P>
- <P>
- Notes specific to the Reference Implementation: This component
- attempts to use hardware-accelerated rendering via pbuffers and
- falls back on to software rendering if problems occur.
- Note that because this component attempts to use pbuffers for
- rendering, and because pbuffers can not be resized, somewhat
- surprising behavior may occur during resize operations; the {@link
- GLEventListener#init} method may be called multiple times as the
- pbuffer is resized to be able to cover the size of the GLJPanel.
- This behavior is correct, as the textures and display lists for
- the GLJPanel will have been lost during the resize operation. The
- application should attempt to make its GLEventListener.init()
- methods as side-effect-free as possible. </P>
+ than 1.0 will be overlaid on any underlying Swing rendering.
+ </p>
+ <p>
+ This component attempts to use hardware-accelerated rendering via FBO or pbuffers and
+ falls back on to software rendering if none of the former are available
+ using {@link GLDrawableFactory#createOffscreenDrawable(AbstractGraphicsDevice, GLCapabilitiesImmutable, GLCapabilitiesChooser, int, int) GLDrawableFactory.createOffscreenDrawable(..)}.<br/>
+ </p>
+ <p>
+ <a name="verticalFlip">
+ In case</a> the drawable {@link #isGLOriented()} and {@link #setSkipGLOrientationVerticalFlip(boolean) vertical flip is not skipped},
+ this component performs the required vertical flip to bring the content from OpenGL's orientation into AWT's orientation.
+ See details about <a href="#fboGLSLVerticalFlip">FBO and GLSL vertical flipping</a>.
+ </p>
+ <p>
+ The OpenGL path is concluded by copying the rendered pixels an {@link BufferedImage} via {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer) glReadPixels(..)}
+ for later Java2D composition.
+ </p>
+ <p>
+ In case {@link #setSkipGLOrientationVerticalFlip(boolean) vertical-flip is not skipped} and <a href="#fboGLSLVerticalFlip">GLSL based vertical-flip</a> is not performed,
+ {@link System#arraycopy(Object, int, Object, int, int) System.arraycopy(..)} is used line by line.
+ This step causes more CPU load per frame and is not hardware-accelerated.
+ </p>
+ <p>
+ Finally the Java2D compositioning takes place via via {@link Graphics#drawImage(java.awt.Image, int, int, int, int, java.awt.image.ImageObserver) Graphics.drawImage(...)}
+ on the prepared {@link BufferedImage} as described above.
+ </p>
<P>
- * Please read <A HREF="GLCanvas.html#java2dgl">Java2D OpenGL Remarks</A>.
+ * Please read <a href="GLCanvas.html#java2dgl">Java2D OpenGL Remarks</a>.
* </P>
+ *
+ <a name="fboGLSLVerticalFlip"><h5>FBO / GLSL Vertical Flip</h5></a>
+ In case FBO is used and GLSL is available and {@link #setSkipGLOrientationVerticalFlip(boolean) vertical flip is not skipped}, a fragment shader is utilized
+ to flip the FBO texture vertically. This hardware-accelerated step can be disabled via system property <code>jogl.gljpanel.noglsl</code>.
+ <p>
+ The FBO / GLSL code path uses one texture-unit and binds the FBO texture to it's active texture-target,
+ see {@link #setTextureUnit(int)} and {@link #getTextureUnit()}.
+ </p>
+ <p>
+ The active and dedicated texture-unit's {@link GL#GL_TEXTURE_2D} state is preserved via {@link TextureState}.
+ See also {@link Texture#textureCallOrder Order of Texture Commands}.
+ </p>
+ <p>
+ The current gl-viewport is preserved.
+ </p>
+ <p>
+ <i>Warning (Bug 842)</i>: Certain GL states other than viewport and texture (see above)
+ influencing rendering, will also influence the GLSL vertical flip, e.g. {@link GL#glFrontFace(int) glFrontFace}({@link GL#GL_CCW}).
+ It is recommended to reset those states to default when leaving the {@link GLEventListener#display(GLAutoDrawable)} method!
+ We may change this behavior in the future, i.e. preserve all influencing states.
+ </p>
*/
@SuppressWarnings("serial")
-public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosingProtocol {
- private static final boolean DEBUG = Debug.debug("GLJPanel");
+public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosingProtocol, AWTPrintLifecycle, GLSharedContextSetter {
+ private static final boolean DEBUG;
+ private static final boolean DEBUG_VIEWPORT;
+ private static final boolean USE_GLSL_TEXTURE_RASTERIZER;
+ private static final boolean SKIP_VERTICAL_FLIP_DEFAULT;
+
+ /** Indicates whether the Java 2D OpenGL pipeline is requested by user. */
+ private static final boolean java2dOGLEnabledByProp;
+
+ /** Indicates whether the Java 2D OpenGL pipeline is enabled, resource-compatible and requested by user. */
+ private static final boolean useJava2DGLPipeline;
+
+ /** Indicates whether the Java 2D OpenGL pipeline's usage is error free. */
+ private static boolean java2DGLPipelineOK;
+
+ static {
+ Debug.initSingleton();
+ DEBUG = Debug.debug("GLJPanel");
+ DEBUG_VIEWPORT = Debug.isPropertyDefined("jogl.debug.GLJPanel.Viewport", true);
+ USE_GLSL_TEXTURE_RASTERIZER = !Debug.isPropertyDefined("jogl.gljpanel.noglsl", true);
+ SKIP_VERTICAL_FLIP_DEFAULT = Debug.isPropertyDefined("jogl.gljpanel.noverticalflip", true);
+ boolean enabled = Debug.getBooleanProperty("sun.java2d.opengl", false);
+ java2dOGLEnabledByProp = enabled && !Debug.isPropertyDefined("jogl.gljpanel.noogl", true);
+
+ enabled = false;
+ if( java2dOGLEnabledByProp ) {
+ // Force eager initialization of part of the Java2D class since
+ // otherwise it's likely it will try to be initialized while on
+ // the Queue Flusher Thread, which is not allowed
+ if (Java2D.isOGLPipelineResourceCompatible() && Java2D.isFBOEnabled()) {
+ if( null != Java2D.getShareContext(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()) ) {
+ enabled = true;
+ }
+ }
+ }
+ useJava2DGLPipeline = enabled;
+ java2DGLPipelineOK = enabled;
+ if( DEBUG ) {
+ System.err.println("GLJPanel: DEBUG_VIEWPORT "+DEBUG_VIEWPORT);
+ System.err.println("GLJPanel: USE_GLSL_TEXTURE_RASTERIZER "+USE_GLSL_TEXTURE_RASTERIZER);
+ System.err.println("GLJPanel: SKIP_VERTICAL_FLIP_DEFAULT "+SKIP_VERTICAL_FLIP_DEFAULT);
+ System.err.println("GLJPanel: java2dOGLEnabledByProp "+java2dOGLEnabledByProp);
+ System.err.println("GLJPanel: useJava2DGLPipeline "+useJava2DGLPipeline);
+ System.err.println("GLJPanel: java2DGLPipelineOK "+java2DGLPipelineOK);
+ }
+ }
+
+ private static SingleAWTGLPixelBufferProvider singleAWTGLPixelBufferProvider = null;
+ private static synchronized SingleAWTGLPixelBufferProvider getSingleAWTGLPixelBufferProvider() {
+ if( null == singleAWTGLPixelBufferProvider ) {
+ singleAWTGLPixelBufferProvider = new SingleAWTGLPixelBufferProvider( true /* allowRowStride */ );
+ }
+ return singleAWTGLPixelBufferProvider;
+ }
+
+ private final GLDrawableHelper helper;
+ private boolean autoSwapBufferMode;
- private GLDrawableHelper drawableHelper = new GLDrawableHelper();
private volatile boolean isInitialized;
+ //
// Data used for either pbuffers or pixmap-based offscreen surfaces
+ //
+ private AWTGLPixelBufferProvider customPixelBufferProvider = null;
+ /** Single buffered offscreen caps */
private GLCapabilitiesImmutable offscreenCaps;
- private GLProfile glProfile;
- private GLDrawableFactoryImpl factory;
- private GLCapabilitiesChooser chooser;
- private GLContext shareWith;
+ private final GLProfile glProfile;
+ private final GLDrawableFactoryImpl factory;
+ private final GLCapabilitiesChooser chooser;
private int additionalCtxCreationFlags = 0;
-
- // Width of the actual GLJPanel
- private int panelWidth = 0;
- private int panelHeight = 0;
- // Lazy reshape notification
+
+ // Lazy reshape notification: reshapeWidth -> panelWidth -> backend.width
private boolean handleReshape = false;
private boolean sendReshape = true;
- // The backend in use
- private Backend backend;
-
- // Used by all backends either directly or indirectly to hook up callbacks
- private Updater updater = new Updater();
-
- // Turns off the pbuffer-based backend (used by default, unless the
- // Java 2D / OpenGL pipeline is in use)
- private static boolean hardwareAccelerationDisabled =
- Debug.isPropertyDefined("jogl.gljpanel.nohw", true);
-
- // Turns off the fallback to software-based rendering from
- // pbuffer-based rendering
- private static boolean softwareRenderingDisabled =
- Debug.isPropertyDefined("jogl.gljpanel.nosw", true);
-
- // Indicates whether the Java 2D OpenGL pipeline is enabled
- private boolean oglPipelineEnabled =
- Java2D.isOGLPipelineActive() &&
- !Debug.isPropertyDefined("jogl.gljpanel.noogl", true);
-
- // For handling reshape events lazily
- // private int reshapeX;
- // private int reshapeY;
+ // For handling reshape events lazily: reshapeWidth -> panelWidth -> backend.width
private int reshapeWidth;
private int reshapeHeight;
+ // Width of the actual GLJPanel: reshapeWidth -> panelWidth -> backend.width
+ private int panelWidth = 0;
+ private int panelHeight = 0;
+
// These are always set to (0, 0) except when the Java2D / OpenGL
// pipeline is active
private int viewportX;
private int viewportY;
- private AWTWindowClosingProtocol awtWindowClosingProtocol =
+ private int requestedTextureUnit = 0; // default
+
+ // The backend in use
+ private volatile Backend backend;
+
+ private boolean skipGLOrientationVerticalFlip = SKIP_VERTICAL_FLIP_DEFAULT;
+
+ // Used by all backends either directly or indirectly to hook up callbacks
+ private final Updater updater = new Updater();
+
+ private boolean oglPipelineUsable() {
+ return null == customPixelBufferProvider && useJava2DGLPipeline && java2DGLPipelineOK;
+ }
+
+ private volatile boolean isShowing;
+ private final HierarchyListener hierarchyListener = new HierarchyListener() {
+ @Override
+ public void hierarchyChanged(HierarchyEvent e) {
+ isShowing = GLJPanel.this.isShowing();
+ }
+ };
+
+ private final AWTWindowClosingProtocol awtWindowClosingProtocol =
new AWTWindowClosingProtocol(this, new Runnable() {
+ @Override
public void run() {
GLJPanel.this.destroy();
}
- });
-
- static {
- // Force eager initialization of part of the Java2D class since
- // otherwise it's likely it will try to be initialized while on
- // the Queue Flusher Thread, which is not allowed
- if (Java2D.isOGLPipelineActive() && Java2D.isFBOEnabled()) {
- Java2D.getShareContext(GraphicsEnvironment.
- getLocalGraphicsEnvironment().
- getDefaultScreenDevice());
- }
- }
+ }, null);
/** Creates a new GLJPanel component with a default set of OpenGL
capabilities and using the default OpenGL capabilities selection
- mechanism.
+ mechanism.
* @throws GLException if no default profile is available for the default desktop device.
*/
public GLJPanel() throws GLException {
@@ -207,7 +297,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
/** Creates a new GLJPanel component with the requested set of
OpenGL capabilities, using the default OpenGL capabilities
- selection mechanism.
+ selection mechanism.
* @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
*/
public GLJPanel(GLCapabilitiesImmutable userCapsRequest) throws GLException {
@@ -219,18 +309,34 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
default set of capabilities is used. The GLCapabilitiesChooser
specifies the algorithm for selecting one of the available
GLCapabilities for the component; a DefaultGLCapabilitesChooser
+ is used if null is passed for this argument.
+ * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
+ */
+ public GLJPanel(GLCapabilitiesImmutable userCapsRequest, GLCapabilitiesChooser chooser)
+ throws GLException
+ {
+ this(userCapsRequest, chooser, null);
+ }
+
+ /** Creates a new GLJPanel component. The passed GLCapabilities
+ specifies the OpenGL capabilities for the component; if null, a
+ default set of capabilities is used. The GLCapabilitiesChooser
+ specifies the algorithm for selecting one of the available
+ GLCapabilities for the component; a DefaultGLCapabilitesChooser
is used if null is passed for this argument. The passed
GLContext specifies an OpenGL context with which to share
textures, display lists and other OpenGL state, and may be null
if sharing is not desired. See the note in the overview documentation on
- <a href="../../../overview-summary.html#SHARING">context sharing</a>.
+ <a href="../../../spec-overview.html#SHARING">context sharing</a>.
<P>
Note: Sharing cannot be enabled using J2D OpenGL FBO sharing,
since J2D GL Context must be shared and we can only share one context.
* @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
- */
- public GLJPanel(GLCapabilitiesImmutable userCapsRequest, GLCapabilitiesChooser chooser, GLContext shareWith)
- throws GLException
+ * @deprecated Use {@link #GLJPanel(GLCapabilitiesImmutable, GLCapabilitiesChooser)}
+ * and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}.
+ */
+ public GLJPanel(GLCapabilitiesImmutable userCapsRequest, GLCapabilitiesChooser chooser, GLContext shareWith)
+ throws GLException
{
super();
@@ -248,22 +354,102 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
}
this.glProfile = offscreenCaps.getGLProfile();
this.factory = GLDrawableFactoryImpl.getFactoryImpl(glProfile);
- this.chooser = ((chooser != null) ? chooser : new DefaultGLCapabilitiesChooser());
- this.shareWith = shareWith;
+ this.chooser = chooser;
+
+ helper = new GLDrawableHelper();
+ if( null != shareWith ) {
+ helper.setSharedContext(null, shareWith);
+ }
+ autoSwapBufferMode = helper.getAutoSwapBufferMode();
+
+ this.setFocusable(true); // allow keyboard input!
+ this.addHierarchyListener(hierarchyListener);
+ this.isShowing = isShowing();
}
- public void display() {
- if (EventQueue.isDispatchThread()) {
- // Want display() to be synchronous, so call paintImmediately()
- paintImmediately(0, 0, getWidth(), getHeight());
+ /**
+ * Attempts to initialize the backend, if not initialized yet.
+ * <p>
+ * If backend is already initialized method returns <code>true</code>.
+ * </p>
+ * <p>
+ * If <code>offthread</code> is <code>true</code>, initialization will kicked off
+ * on a <i>short lived</i> arbitrary thread and method returns immediately.<br/>
+ * If platform supports such <i>arbitrary thread</i> initialization method returns
+ * <code>true</code>, otherwise <code>false</code>.
+ * </p>
+ * <p>
+ * If <code>offthread</code> is <code>false</code>, initialization be performed
+ * on the current thread and method returns after initialization.<br/>
+ * Method returns <code>true</code> if initialization was successful, otherwise <code>false</code>.
+ * <p>
+ * @param offthread
+ */
+ public final boolean initializeBackend(boolean offthread) {
+ if( offthread ) {
+ new Thread(getThreadName()+"-GLJPanel_Init") {
+ public void run() {
+ if( !isInitialized ) {
+ initializeBackendImpl();
+ }
+ } }.start();
+ return true;
} else {
- // Multithreaded redrawing of Swing components is not allowed,
- // so do everything on the event dispatch thread
- try {
- EventQueue.invokeAndWait(paintImmediatelyAction);
- } catch (Exception e) {
- throw new GLException(e);
+ if( !isInitialized ) {
+ return initializeBackendImpl();
+ } else {
+ return true;
+ }
+ }
+ }
+
+ @Override
+ public final void setSharedContext(GLContext sharedContext) throws IllegalStateException {
+ helper.setSharedContext(this.getContext(), sharedContext);
+ }
+
+ @Override
+ public final void setSharedAutoDrawable(GLAutoDrawable sharedAutoDrawable) throws IllegalStateException {
+ helper.setSharedAutoDrawable(this, sharedAutoDrawable);
+ }
+
+ public AWTGLPixelBufferProvider getCustomPixelBufferProvider() { return customPixelBufferProvider; }
+
+ /**
+ * @param custom custom {@link AWTGLPixelBufferProvider}
+ * @throws IllegalArgumentException if <code>custom</code> is <code>null</code>
+ * @throws IllegalStateException if backend is already realized, i.e. this instanced already painted once.
+ */
+ public void setPixelBufferProvider(AWTGLPixelBufferProvider custom) throws IllegalArgumentException, IllegalStateException {
+ if( null == custom ) {
+ throw new IllegalArgumentException("Null PixelBufferProvider");
+ }
+ if( null != backend ) {
+ throw new IllegalStateException("Backend already realized.");
}
+ customPixelBufferProvider = custom;
+ }
+
+ @Override
+ public final Object getUpstreamWidget() {
+ return this;
+ }
+
+ @Override
+ public void display() {
+ if( isShowing ) {
+ if (EventQueue.isDispatchThread()) {
+ // Want display() to be synchronous, so call paintImmediately()
+ paintImmediately(0, 0, getWidth(), getHeight());
+ } else {
+ // Multithreaded redrawing of Swing components is not allowed,
+ // so do everything on the event dispatch thread
+ try {
+ EventQueue.invokeAndWait(paintImmediatelyAction);
+ } catch (Exception e) {
+ throw new GLException(e);
+ }
+ }
}
}
@@ -280,7 +466,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
animatorPaused = animator.pause();
}
- if(backend.getContext().isCreated()) {
+ if(backend.getContext().isCreated()) {
Threading.invoke(true, disposeAction, getTreeLock());
}
if(null != backend) {
@@ -291,9 +477,9 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
if(animatorPaused) {
animator.resume();
- }
+ }
}
-
+
if(DEBUG) {
System.err.println(getThreadName()+": GLJPanel.dispose() - stop");
}
@@ -302,6 +488,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
/**
* Just an alias for removeNotify
*/
+ @Override
public void destroy() {
removeNotify();
}
@@ -335,11 +522,11 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
return;
}
- if (backend == null || !isInitialized) {
- createAndInitializeBackend();
+ if( !isInitialized ) {
+ initializeBackendImpl();
}
- if (!isInitialized) {
+ if (!isInitialized || printActive) {
return;
}
@@ -347,16 +534,19 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
// involve destroying the pbuffer (current context) and
// re-creating it -- tricky to do properly while the context is
// current
- if (handleReshape) {
- handleReshape();
- }
+ if( !printActive ) {
+ if (handleReshape) {
+ handleReshape = false;
+ sendReshape = handleReshape();
+ }
- updater.setGraphics(g);
-
- backend.doPaintComponent(g);
+ if( isShowing ) {
+ updater.setGraphics(g);
+ backend.doPaintComponent(g);
+ }
+ }
}
-
/** Overridden to track when this component is added to a container.
Subclasses which override this method must call
super.addNotify() in their addNotify() method in order to
@@ -366,6 +556,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
@Override
public void addNotify() {
super.addNotify();
+ awtWindowClosingProtocol.addClosingListener();
if (DEBUG) {
System.err.println(getThreadName()+": GLJPanel.addNotify()");
}
@@ -389,18 +580,207 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
called on all registered {@link GLEventListener}s. Subclasses
which override this method must call super.reshape() in
their reshape() method in order to function properly. <P>
-
- <DL><DD><CODE>reshape</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
+ *
+ * {@inheritDoc}
+ */
@SuppressWarnings("deprecation")
@Override
-public void reshape(int x, int y, int width, int height) {
+ public void reshape(int x, int y, int width, int height) {
super.reshape(x, y, width, height);
- // reshapeX = x;
- // reshapeY = y;
- reshapeWidth = width;
- reshapeHeight = height;
- handleReshape = true;
+ if( DEBUG ) {
+ System.err.println(getThreadName()+": GLJPanel.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+" [ this "+getWidth()+"x"+getHeight()+", panel "+
+ panelWidth+"x"+panelHeight +
+ ", reshape: " +reshapeWidth+"x"+reshapeHeight +
+ "] -> "+(printActive?"skipped":"") + width+"x"+height);
+ }
+ if( !printActive ) {
+ reshapeWidth = width;
+ reshapeHeight = height;
+ handleReshape = true;
+ }
+ }
+
+ private volatile boolean printActive = false;
+ private GLAnimatorControl printAnimator = null;
+ private GLAutoDrawable printGLAD = null;
+ private AWTTilePainter printAWTTiles = null;
+
+ @Override
+ public void setupPrint(double scaleMatX, double scaleMatY, int numSamples, int tileWidth, int tileHeight) {
+ printActive = true;
+ final int componentCount = isOpaque() ? 3 : 4;
+ final TileRenderer printRenderer = new TileRenderer();
+ printAWTTiles = new AWTTilePainter(printRenderer, componentCount, scaleMatX, scaleMatY, numSamples, tileWidth, tileHeight, DEBUG);
+ AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, setupPrintOnEDT);
+ }
+ private final Runnable setupPrintOnEDT = new Runnable() {
+ @Override
+ public void run() {
+ if( !isInitialized ) {
+ initializeBackendImpl();
+ }
+ if (!isInitialized) {
+ if(DEBUG) {
+ System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, drawable not valid yet");
+ }
+ printActive = false;
+ return; // not yet available ..
+ }
+ if( !isShowing ) {
+ if(DEBUG) {
+ System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, drawable valid, panel not showing");
+ }
+ printActive = false;
+ return; // not yet available ..
+ }
+ sendReshape = false; // clear reshape flag
+ handleReshape = false; // ditto
+ printAnimator = helper.getAnimator();
+ if( null != printAnimator ) {
+ printAnimator.remove(GLJPanel.this);
+ }
+
+ printGLAD = GLJPanel.this; // default: re-use
+ final GLCapabilities caps = (GLCapabilities)getChosenGLCapabilities().cloneMutable();
+ final int printNumSamples = printAWTTiles.getNumSamples(caps);
+ GLDrawable printDrawable = printGLAD.getDelegatedDrawable();
+ final boolean reqNewGLADSamples = printNumSamples != caps.getNumSamples();
+ final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getWidth() ||
+ printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getHeight();
+ final boolean reqNewGLAD = reqNewGLADSamples || reqNewGLADSize ;
+ if( DEBUG ) {
+ System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ samples "+reqNewGLADSamples+", size "+reqNewGLADSize+"], "+
+ ", drawableSize "+printDrawable.getWidth()+"x"+printDrawable.getHeight()+
+ ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+
+ ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+
+ ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator);
+ }
+ if( reqNewGLAD ) {
+ caps.setDoubleBuffered(false);
+ caps.setOnscreen(false);
+ caps.setSampleBuffers(0 < printNumSamples);
+ caps.setNumSamples(printNumSamples);
+ final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile());
+ printGLAD = factory.createOffscreenAutoDrawable(null, caps, null,
+ printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE,
+ printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE);
+ GLDrawableUtil.swapGLContextAndAllGLEventListener(GLJPanel.this, printGLAD);
+ printDrawable = printGLAD.getDelegatedDrawable();
+ }
+ printAWTTiles.setGLOrientation( !GLJPanel.this.skipGLOrientationVerticalFlip && printGLAD.isGLOriented(), printGLAD.isGLOriented() );
+ printAWTTiles.renderer.setTileSize(printDrawable.getWidth(), printDrawable.getHeight(), 0);
+ printAWTTiles.renderer.attachAutoDrawable(printGLAD);
+ if( DEBUG ) {
+ System.err.println("AWT print.setup "+printAWTTiles);
+ System.err.println("AWT print.setup AA "+printNumSamples+", "+caps);
+ System.err.println("AWT print.setup printGLAD: "+printGLAD.getWidth()+"x"+printGLAD.getHeight()+", "+printGLAD);
+ System.err.println("AWT print.setup printDraw: "+printDrawable.getWidth()+"x"+printDrawable.getHeight()+", "+printDrawable);
+ }
+ }
+ };
+
+ @Override
+ public void releasePrint() {
+ if( !printActive ) {
+ throw new IllegalStateException("setupPrint() not called");
+ }
+ sendReshape = false; // clear reshape flag
+ handleReshape = false; // ditto
+ AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, releasePrintOnEDT);
+ }
+
+ private final Runnable releasePrintOnEDT = new Runnable() {
+ @Override
+ public void run() {
+ if( DEBUG ) {
+ System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0 "+printAWTTiles);
+ }
+ printAWTTiles.dispose();
+ printAWTTiles= null;
+ if( printGLAD != GLJPanel.this ) {
+ GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLJPanel.this);
+ printGLAD.destroy();
+ }
+ printGLAD = null;
+ if( null != printAnimator ) {
+ printAnimator.add(GLJPanel.this);
+ printAnimator = null;
+ }
+
+ // trigger reshape, i.e. gl-viewport and -listener - this component might got resized!
+ final int awtWidth = GLJPanel.this.getWidth();
+ final int awtHeight= GLJPanel.this.getHeight();
+ final GLDrawable drawable = GLJPanel.this.getDelegatedDrawable();
+ if( awtWidth != panelWidth || awtHeight != panelHeight ||
+ drawable.getWidth() != panelWidth || drawable.getHeight() != panelHeight ) {
+ // -> !( awtSize == panelSize == drawableSize )
+ if ( DEBUG ) {
+ System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0: resizeWithinPrint panel " +panelWidth+"x"+panelHeight +
+ ", draw "+drawable.getWidth()+"x"+drawable.getHeight()+
+ " -> " + awtWidth+"x"+awtHeight);
+ }
+ reshapeWidth = awtWidth;
+ reshapeHeight = awtHeight;
+ sendReshape = handleReshape(); // reshapeSize -> panelSize, backend reshape w/ GL reshape
+ } else {
+ sendReshape = true; // only GL reshape
+ }
+ printActive = false;
+ display();
+ }
+ };
+
+ @Override
+ public void print(Graphics graphics) {
+ if( !printActive ) {
+ throw new IllegalStateException("setupPrint() not called");
+ }
+ if(DEBUG && !EventQueue.isDispatchThread()) {
+ System.err.println(getThreadName()+": Warning: GLCanvas print - not called from AWT-EDT");
+ // we cannot dispatch print on AWT-EDT due to printing internal locking ..
+ }
+ sendReshape = false; // clear reshape flag
+ handleReshape = false; // ditto
+
+ final Graphics2D g2d = (Graphics2D)graphics;
+ try {
+ printAWTTiles.setupGraphics2DAndClipBounds(g2d, getWidth(), getHeight());
+ final TileRenderer tileRenderer = printAWTTiles.renderer;
+ if( DEBUG ) {
+ System.err.println("AWT print.0: "+tileRenderer);
+ }
+ if( !tileRenderer.eot() ) {
+ try {
+ do {
+ if( printGLAD != GLJPanel.this ) {
+ tileRenderer.display();
+ } else {
+ backend.doPlainPaint();
+ }
+ } while ( !tileRenderer.eot() );
+ if( DEBUG ) {
+ System.err.println("AWT print.1: "+printAWTTiles);
+ }
+ } finally {
+ tileRenderer.reset();
+ printAWTTiles.resetGraphics2D();
+ }
+ }
+ } catch (NoninvertibleTransformException nte) {
+ System.err.println("Catched: Inversion failed of: "+g2d.getTransform());
+ nte.printStackTrace();
+ }
+ if( DEBUG ) {
+ System.err.println("AWT print.X: "+printAWTTiles);
+ }
+ }
+ @Override
+ protected void printComponent(Graphics g) {
+ if( DEBUG ) {
+ System.err.println("AWT printComponent.X: "+printAWTTiles);
+ }
+ print(g);
}
@Override
@@ -411,58 +791,143 @@ public void reshape(int x, int y, int width, int height) {
super.setOpaque(opaque);
}
+ @Override
public void addGLEventListener(GLEventListener listener) {
- drawableHelper.addGLEventListener(listener);
+ helper.addGLEventListener(listener);
}
+ @Override
public void addGLEventListener(int index, GLEventListener listener) {
- drawableHelper.addGLEventListener(index, listener);
+ helper.addGLEventListener(index, listener);
+ }
+
+ @Override
+ public int getGLEventListenerCount() {
+ return helper.getGLEventListenerCount();
+ }
+
+ @Override
+ public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException {
+ return helper.getGLEventListener(index);
+ }
+
+ @Override
+ public boolean areAllGLEventListenerInitialized() {
+ return helper.areAllGLEventListenerInitialized();
+ }
+
+ @Override
+ public boolean getGLEventListenerInitState(GLEventListener listener) {
+ return helper.getGLEventListenerInitState(listener);
+ }
+
+ @Override
+ public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) {
+ helper.setGLEventListenerInitState(listener, initialized);
+ }
+
+ @Override
+ public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) {
+ final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove);
+ if (EventQueue.isDispatchThread()) {
+ r.run();
+ } else {
+ // Multithreaded redrawing of Swing components is not allowed,
+ // so do everything on the event dispatch thread
+ try {
+ EventQueue.invokeAndWait(r);
+ } catch (Exception e) {
+ throw new GLException(e);
+ }
+ }
+ return r.listener;
}
- public void removeGLEventListener(GLEventListener listener) {
- drawableHelper.removeGLEventListener(listener);
+ @Override
+ public GLEventListener removeGLEventListener(GLEventListener listener) {
+ return helper.removeGLEventListener(listener);
}
+ @Override
public void setAnimator(GLAnimatorControl animatorControl) {
- drawableHelper.setAnimator(animatorControl);
+ helper.setAnimator(animatorControl);
}
+ @Override
public GLAnimatorControl getAnimator() {
- return drawableHelper.getAnimator();
+ return helper.getAnimator();
}
- public void invoke(boolean wait, GLRunnable glRunnable) {
- drawableHelper.invoke(this, wait, glRunnable);
+ @Override
+ public final Thread setExclusiveContextThread(Thread t) throws GLException {
+ return helper.setExclusiveContextThread(t, getContext());
}
+ @Override
+ public final Thread getExclusiveContextThread() {
+ return helper.getExclusiveContextThread();
+ }
+
+ @Override
+ public boolean invoke(boolean wait, GLRunnable glRunnable) {
+ return helper.invoke(this, wait, glRunnable);
+ }
+
+ @Override
+ public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) {
+ return helper.invoke(this, wait, glRunnables);
+ }
+
+ @Override
public GLContext createContext(GLContext shareWith) {
- return (null != backend) ? backend.createContext(shareWith) : null;
+ final Backend b = backend;
+ if ( null == b ) {
+ return null;
+ }
+ return b.createContext(shareWith);
}
+ @Override
public void setRealized(boolean realized) {
}
+ @Override
public boolean isRealized() {
return isInitialized;
}
- public void setContext(GLContext ctx) {
- if (backend == null) {
- return;
+ @Override
+ public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) {
+ final Backend b = backend;
+ if ( null == b ) {
+ return null;
}
- if(null != ctx) {
- ctx.setContextCreationFlags(additionalCtxCreationFlags);
+ final GLContext oldCtx = b.getContext();
+ GLDrawableHelper.switchContext(b.getDrawable(), oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags);
+ b.setContext(newCtx);
+ return oldCtx;
+ }
+
+
+ @Override
+ public final GLDrawable getDelegatedDrawable() {
+ final Backend b = backend;
+ if ( null == b ) {
+ return null;
}
- backend.setContext(ctx);
+ return b.getDrawable();
}
+ @Override
public GLContext getContext() {
- if (backend == null) {
- return null;
+ final Backend b = backend;
+ if ( null == b ) {
+ return null;
}
- return backend.getContext();
+ return b.getContext();
}
+ @Override
public GL getGL() {
if (Beans.isDesignTime()) {
return null;
@@ -471,6 +936,7 @@ public void reshape(int x, int y, int width, int height) {
return (context == null) ? null : context.getGL();
}
+ @Override
public GL setGL(GL gl) {
GLContext context = getContext();
if (context != null) {
@@ -480,39 +946,46 @@ public void reshape(int x, int y, int width, int height) {
return null;
}
- public void setAutoSwapBufferMode(boolean onOrOff) {
- // In the current implementation this is a no-op. Both the pbuffer
- // and pixmap based rendering paths use a single-buffered surface
- // so swapping the buffers doesn't do anything. We also don't
- // currently have the provision to skip copying the data to the
- // Swing portion of the GLJPanel in any of the rendering paths.
+ @Override
+ public void setAutoSwapBufferMode(boolean enable) {
+ this.autoSwapBufferMode = enable;
+ boolean backendHandlesSwapBuffer = false;
+ if( isInitialized ) {
+ final Backend b = backend;
+ if ( null != b ) {
+ backendHandlesSwapBuffer= b.handlesSwapBuffer();
+ }
+ }
+ if( !backendHandlesSwapBuffer ) {
+ helper.setAutoSwapBufferMode(enable);
+ }
}
+ @Override
public boolean getAutoSwapBufferMode() {
- // In the current implementation this is a no-op. Both the pbuffer
- // and pixmap based rendering paths use a single-buffered surface
- // so swapping the buffers doesn't do anything. We also don't
- // currently have the provision to skip copying the data to the
- // Swing portion of the GLJPanel in any of the rendering paths.
- return true;
- }
-
+ return autoSwapBufferMode;
+ }
+
+ @Override
public void swapBuffers() {
- // In the current implementation this is a no-op. Both the pbuffer
- // and pixmap based rendering paths use a single-buffered surface
- // so swapping the buffers doesn't do anything. We also don't
- // currently have the provision to skip copying the data to the
- // Swing portion of the GLJPanel in any of the rendering paths.
+ if( isInitialized ) {
+ final Backend b = backend;
+ if ( null != b ) {
+ b.swapBuffers();
+ }
+ }
}
+ @Override
public void setContextCreationFlags(int flags) {
additionalCtxCreationFlags = flags;
}
-
+
+ @Override
public int getContextCreationFlags() {
- return additionalCtxCreationFlags;
+ return additionalCtxCreationFlags;
}
-
+
/** For a translucent GLJPanel (one for which {@link #setOpaque
setOpaque}(false) has been called), indicates whether the
application should preserve the OpenGL color buffer
@@ -526,99 +999,181 @@ public void reshape(int x, int y, int width, int height) {
to perform OpenGL rendering using the GLJPanel into the same
OpenGL drawable as the Swing implementation uses. */
public boolean shouldPreserveColorBufferIfTranslucent() {
- return oglPipelineEnabled;
+ return oglPipelineUsable();
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Method returns a valid value only <i>after</i>
+ * the backend has been initialized, either {@link #initializeBackend(boolean) eagerly}
+ * or manually via the first display call.<br/>
+ * Method always returns a valid value when called from within a {@link GLEventListener}.
+ * </p>
+ */
+ @Override
+ public boolean isGLOriented() {
+ final Backend b = backend;
+ if ( null == b ) {
+ return true;
+ }
+ return b.getDrawable().isGLOriented();
+ }
+
+ /**
+ * Set skipping {@link #isGLOriented()} based vertical flip,
+ * which usually is required by the offscreen backend,
+ * see details about <a href="#verticalFlip">vertical flip</a>
+ * and <a href="#fboGLSLVerticalFlip">FBO / GLSL vertical flip</a>.
+ * <p>
+ * If set to <code>true</code>, user needs to flip the OpenGL rendered scene
+ * <i>if {@link #isGLOriented()} == true</i>, e.g. via the PMV matrix.<br/>
+ * See constraints of {@link #isGLOriented()}.
+ * </p>
+ */
+ public final void setSkipGLOrientationVerticalFlip(boolean v) {
+ skipGLOrientationVerticalFlip = v;
+ }
+ /** See {@link #setSkipGLOrientationVerticalFlip(boolean)}. */
+ public final boolean getSkipGLOrientationVerticalFlip() {
+ return skipGLOrientationVerticalFlip;
}
+ @Override
public GLCapabilitiesImmutable getChosenGLCapabilities() {
- return backend.getChosenGLCapabilities();
+ final Backend b = backend;
+ if ( null == b ) {
+ return null;
+ }
+ return b.getChosenGLCapabilities();
}
+ @Override
public final GLProfile getGLProfile() {
return glProfile;
}
+ @Override
public NativeSurface getNativeSurface() {
- throw new GLException("FIXME");
+ final Backend b = backend;
+ if ( null == b ) {
+ return null;
+ }
+ return b.getDrawable().getNativeSurface();
}
+ @Override
public long getHandle() {
- throw new GLException("FIXME");
+ final Backend b = backend;
+ if ( null == b ) {
+ return 0;
+ }
+ return b.getDrawable().getNativeSurface().getSurfaceHandle();
}
+ @Override
public final GLDrawableFactory getFactory() {
return factory;
}
+ /**
+ * Returns the used texture unit, i.e. a value of [0..n], or -1 if non used.
+ * <p>
+ * If implementation uses a texture-unit, it will be known only after the first initialization, i.e. display call.
+ * </p>
+ * <p>
+ * See <a href="#fboGLSLVerticalFlip">FBO / GLSL Vertical Flip</a>.
+ * </p>
+ */
+ public final int getTextureUnit() {
+ final Backend b = backend;
+ if ( null == b ) {
+ return -1;
+ }
+ return b.getTextureUnit();
+ }
+
+ /**
+ * Allows user to request a texture unit to be used,
+ * must be called before the first initialization, i.e. {@link #display()} call.
+ * <p>
+ * Defaults to <code>0</code>.
+ * </p>
+ * <p>
+ * See <a href="#fboGLSLVerticalFlip">FBO / GLSL Vertical Flip</a>.
+ * </p>
+ *
+ * @param v requested texture unit
+ * @see #getTextureUnit()
+ */
+ public final void setTextureUnit(int v) {
+ requestedTextureUnit = v;
+ }
+
//----------------------------------------------------------------------
// Internals only below this point
//
- private void createAndInitializeBackend() {
- if (panelWidth == 0 ||
- panelHeight == 0) {
- // See whether we have a non-zero size yet and can go ahead with
- // initialization
- if (reshapeWidth == 0 ||
- reshapeHeight == 0) {
- return;
- }
-
- // Pull down reshapeWidth and reshapeHeight into panelWidth and
- // panelHeight eagerly in order to complete initialization, and
- // force a reshape later
- panelWidth = reshapeWidth;
- panelHeight = reshapeHeight;
- }
+ private final Object initSync = new Object();
+ private boolean initializeBackendImpl() {
+ synchronized(initSync) {
+ if( !isInitialized ) {
+ if ( 0 >= panelWidth || 0 >= panelHeight ) {
+ // See whether we have a non-zero size yet and can go ahead with
+ // initialization
+ if (0 >= reshapeWidth || 0 >= reshapeHeight ) {
+ return false;
+ }
- do {
- if (backend == null) {
- if (oglPipelineEnabled) {
- backend = new J2DOGLBackend();
- } else {
- if (!hardwareAccelerationDisabled &&
- factory.canCreateGLPbuffer(null)) {
- backend = new PbufferBackend();
- } else {
- if (softwareRenderingDisabled) {
- throw new GLException("Fallback to software rendering disabled by user");
+ if (DEBUG) {
+ System.err.println(getThreadName()+": GLJPanel.createAndInitializeBackend: " +panelWidth+"x"+panelHeight + " -> " + reshapeWidth+"x"+reshapeHeight);
+ }
+ // Pull down reshapeWidth and reshapeHeight into panelWidth and
+ // panelHeight eagerly in order to complete initialization, and
+ // force a reshape later
+ panelWidth = reshapeWidth;
+ panelHeight = reshapeHeight;
}
- backend = new SoftwareBackend();
- }
- }
- }
- if (!isInitialized) {
- backend.initialize();
- }
- // The backend might set itself to null, indicating it punted to
- // a different implementation -- try again
- } while (backend == null);
+ if ( null == backend ) {
+ if ( oglPipelineUsable() ) {
+ backend = new J2DOGLBackend();
+ } else {
+ backend = new OffscreenBackend(glProfile, customPixelBufferProvider);
+ }
+ isInitialized = false;
+ }
- awtWindowClosingProtocol.addClosingListenerOneShot();
+ if (!isInitialized) {
+ backend.initialize();
+ }
+ return isInitialized;
+ } else {
+ return true;
+ }
+ }
}
+ @Override
public WindowClosingMode getDefaultCloseOperation() {
return awtWindowClosingProtocol.getDefaultCloseOperation();
}
+ @Override
public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) {
return awtWindowClosingProtocol.setDefaultCloseOperation(op);
}
- private void handleReshape() {
- panelWidth = reshapeWidth;
- panelHeight = reshapeHeight;
-
+ private boolean handleReshape() {
if (DEBUG) {
- System.err.println(getThreadName()+": GLJPanel.handleReshape: (w,h) = (" +
- panelWidth + "," + panelHeight + ")");
+ System.err.println(getThreadName()+": GLJPanel.handleReshape: " +panelWidth+"x"+panelHeight + " -> " + reshapeWidth+"x"+reshapeHeight);
}
+ panelWidth = reshapeWidth;
+ panelHeight = reshapeHeight;
- sendReshape = true;
- backend.handleReshape();
- handleReshape = false;
+ return backend.handleReshape();
}
-
+
// This is used as the GLEventListener for the pbuffer-based backend
// as well as the callback mechanism for the other backends
class Updater implements GLEventListener {
@@ -628,18 +1183,21 @@ public void reshape(int x, int y, int width, int height) {
this.g = g;
}
+ @Override
public void init(GLAutoDrawable drawable) {
if (!backend.preGL(g)) {
return;
}
- drawableHelper.init(GLJPanel.this);
+ helper.init(GLJPanel.this, !sendReshape);
backend.postGL(g, false);
}
+ @Override
public void dispose(GLAutoDrawable drawable) {
- drawableHelper.dispose(GLJPanel.this);
+ helper.disposeAllGLEventListener(GLJPanel.this, false);
}
+ @Override
public void display(GLAutoDrawable drawable) {
if (!backend.preGL(g)) {
return;
@@ -648,68 +1206,100 @@ public void reshape(int x, int y, int width, int height) {
if (DEBUG) {
System.err.println(getThreadName()+": GLJPanel.display: reshape(" + viewportX + "," + viewportY + " " + panelWidth + "x" + panelHeight + ")");
}
- drawableHelper.reshape(GLJPanel.this, viewportX, viewportY, panelWidth, panelHeight);
+ helper.reshape(GLJPanel.this, viewportX, viewportY, panelWidth, panelHeight);
sendReshape = false;
}
- drawableHelper.display(GLJPanel.this);
+ helper.display(GLJPanel.this);
backend.postGL(g, true);
}
- public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
- // This is handled above and dispatched directly to the appropriate context
+ public void plainPaint(GLAutoDrawable drawable) {
+ helper.display(GLJPanel.this);
}
- public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
+ @Override
+ public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
+ // This is handled above and dispatched directly to the appropriate context
}
}
+ @Override
public String toString() {
- return "AWT-GLJPanel[ "+((null!=backend)?backend.getDrawable().getClass().getName():"null-drawable")+"]";
+ final GLDrawable d = ( null != backend ) ? backend.getDrawable() : null;
+ return "AWT-GLJPanel[ drawableType "+ ( ( null != d ) ? d.getClass().getName() : "null" ) +
+ ", chosenCaps " + getChosenGLCapabilities() +
+ "]";
}
- private final Runnable postDisposeAction = new Runnable() {
- public void run() {
- if (backend != null && !backend.isUsingOwnThreadManagment()) {
- backend.destroy();
- backend = null;
- isInitialized = false;
- }
- }
- };
-
private final Runnable disposeAction = new Runnable() {
+ @Override
public void run() {
- drawableHelper.disposeGL(GLJPanel.this, backend.getDrawable(), backend.getContext(), postDisposeAction);
+ if ( null != backend ) {
+ final GLContext _context = backend.getContext();
+ final boolean backendDestroy = !backend.isUsingOwnLifecycle();
+ if( null != _context && _context.isCreated() ) {
+ // Catch dispose GLExceptions by GLEventListener, just 'print' them
+ // so we can continue with the destruction.
+ try {
+ helper.disposeGL(GLJPanel.this, _context, !backendDestroy);
+ } catch (GLException gle) {
+ gle.printStackTrace();
+ }
+ }
+ if ( backendDestroy ) {
+ backend.destroy();
+ backend = null;
+ isInitialized = false;
+ }
+ }
}
- };
+ };
private final Runnable updaterInitAction = new Runnable() {
+ @Override
public void run() {
updater.init(GLJPanel.this);
}
};
private final Runnable updaterDisplayAction = new Runnable() {
+ @Override
public void run() {
updater.display(GLJPanel.this);
}
};
+ private final Runnable updaterPlainDisplayAction = new Runnable() {
+ @Override
+ public void run() {
+ updater.plainPaint(GLJPanel.this);
+ }
+ };
+
private final Runnable paintImmediatelyAction = new Runnable() {
+ @Override
public void run() {
paintImmediately(0, 0, getWidth(), getHeight());
}
- };
+ };
- private int getNextPowerOf2(int number) {
- // Workaround for problems where 0 width or height are transiently
- // seen during layout
- if (number == 0) {
- return 2;
- }
- return GLBuffers.getNextPowerOf2(number);
- }
+ private class DisposeGLEventListenerAction implements Runnable {
+ GLEventListener listener;
+ private final boolean remove;
+ private DisposeGLEventListenerAction(GLEventListener listener, boolean remove) {
+ this.listener = listener;
+ this.remove = remove;
+ }
+
+ @Override
+ public void run() {
+ final Backend b = backend;
+ if ( null != b ) {
+ listener = helper.disposeGLEventListener(GLJPanel.this, b.getDrawable(), b.getContext(), listener, remove);
+ }
+ }
+ };
private int getGLInteger(GL gl, int which) {
int[] tmp = new int[1];
@@ -717,492 +1307,555 @@ public void reshape(int x, int y, int width, int height) {
return tmp[0];
}
- protected static String getThreadName() {
- return Thread.currentThread().getName();
- }
-
+ protected static String getThreadName() { return Thread.currentThread().getName(); }
+
//----------------------------------------------------------------------
// Implementations of the various backends
//
- // Abstraction of the different rendering backends: i.e., pure
- // software / pixmap rendering, pbuffer-based acceleration, Java 2D
- // / JOGL bridge
+ /**
+ * Abstraction of the different rendering backends: i.e., pure
+ * software / pixmap rendering, pbuffer-based acceleration, Java 2D
+ * JOGL bridge
+ */
static interface Backend {
- // Create, Destroy, ..
- public boolean isUsingOwnThreadManagment();
-
- // Called each time the backend needs to initialize itself
+ /** Create, Destroy, .. */
+ public boolean isUsingOwnLifecycle();
+
+ /** Called each time the backend needs to initialize itself */
public void initialize();
- // Called when the backend should clean up its resources
+ /** Called when the backend should clean up its resources */
public void destroy();
- // Called when the opacity of the GLJPanel is changed
+ /** Called when the opacity of the GLJPanel is changed */
public void setOpaque(boolean opaque);
- // Called to manually create an additional OpenGL context against
- // this GLJPanel
+ /**
+ * Called to manually create an additional OpenGL context against
+ * this GLJPanel
+ */
public GLContext createContext(GLContext shareWith);
- // Called to set the current backend's GLContext
+ /** Called to set the current backend's GLContext */
public void setContext(GLContext ctx);
- // Called to get the current backend's GLContext
+ /** Called to get the current backend's GLContext */
public GLContext getContext();
- // Called to get the current backend's GLDrawable
+ /** Called to get the current backend's GLDrawable */
public GLDrawable getDrawable();
- // Called to fetch the "real" GLCapabilities for the backend
+ /** Returns the used texture unit, i.e. a value of [0..n], or -1 if non used. */
+ public int getTextureUnit();
+
+ /** Called to fetch the "real" GLCapabilities for the backend */
public GLCapabilitiesImmutable getChosenGLCapabilities();
- // Called to fetch the "real" GLProfile for the backend
+ /** Called to fetch the "real" GLProfile for the backend */
public GLProfile getGLProfile();
- // Called to handle a reshape event. When this is called, the
- // OpenGL context associated with the backend is not current, to
- // make it easier to destroy and re-create pbuffers if necessary.
- public void handleReshape();
-
- // Called before the OpenGL work is done in init() and display().
- // If false is returned, this render is aborted.
+ /**
+ * Called to handle a reshape event. When this is called, the
+ * OpenGL context associated with the backend is not current, to
+ * make it easier to destroy and re-create pbuffers if necessary.
+ */
+ public boolean handleReshape();
+
+ /**
+ * Called before the OpenGL work is done in init() and display().
+ * If false is returned, this render is aborted.
+ */
public boolean preGL(Graphics g);
- // Called after the OpenGL work is done in init() and display().
- // The isDisplay argument indicates whether this was called on
- // behalf of a call to display() rather than init().
+ /**
+ * Return true if backend handles 'swap buffer' itself
+ * and hence the helper's setAutoSwapBuffer(enable) shall not be called.
+ * In this case {@link GLJPanel#autoSwapBufferMode} is being used
+ * in the backend to determine whether to swap buffers or not.
+ */
+ public boolean handlesSwapBuffer();
+
+ /**
+ * Shall invoke underlying drawable's swapBuffer.
+ */
+ public void swapBuffers();
+
+ /**
+ * Called after the OpenGL work is done in init() and display().
+ * The isDisplay argument indicates whether this was called on
+ * behalf of a call to display() rather than init().
+ */
public void postGL(Graphics g, boolean isDisplay);
- // Called from within paintComponent() to initiate the render
+ /** Called from within paintComponent() to initiate the render */
public void doPaintComponent(Graphics g);
+
+ /** Called from print .. no backend update desired onscreen */
+ public void doPlainPaint();
}
// Base class used by both the software (pixmap) and pbuffer
// backends, both of which rely on reading back the OpenGL frame
// buffer and drawing it with a BufferedImage
- abstract class AbstractReadbackBackend implements Backend {
- // This image is exactly the correct size to render into the panel
- protected BufferedImage offscreenImage;
+ class OffscreenBackend implements Backend {
+ private final AWTGLPixelBufferProvider pixelBufferProvider;
+ private final boolean useSingletonBuffer;
+ private AWTGLPixelBuffer pixelBuffer;
+ private BufferedImage alignedImage;
+
// One of these is used to store the read back pixels before storing
// in the BufferedImage
- protected ByteBuffer readBackBytes;
- protected IntBuffer readBackInts;
- protected int readBackWidthInPixels;
- protected int readBackHeightInPixels;
+ protected IntBuffer readBackIntsForCPUVFlip;
+
+ // Implementation using software rendering
+ private volatile GLDrawableImpl offscreenDrawable; // volatile: avoid locking for read-only access
+ private boolean offscreenIsFBO;
+ private FBObject fboFlipped;
+ private GLSLTextureRaster glslTextureRaster;
- private int glFormat;
- private int glType;
+ private volatile GLContextImpl offscreenContext; // volatile: avoid locking for read-only access
+ private boolean flipVertical;
+ private int frameCount = 0;
// For saving/restoring of OpenGL state during ReadPixels
- private int[] swapbytes = new int[1];
- private int[] rowlength = new int[1];
- private int[] skiprows = new int[1];
- private int[] skippixels = new int[1];
- private int[] alignment = new int[1];
-
- public void setOpaque(boolean opaque) {
- if (opaque != isOpaque()) {
- if (offscreenImage != null) {
- offscreenImage.flush();
- offscreenImage = null;
- }
- }
- }
+ private final GLPixelStorageModes psm = new GLPixelStorageModes();
- public boolean preGL(Graphics g) {
- // Empty in this implementation
- return true;
+ OffscreenBackend(GLProfile glp, AWTGLPixelBufferProvider custom) {
+ if(null == custom) {
+ pixelBufferProvider = getSingleAWTGLPixelBufferProvider();
+ } else {
+ pixelBufferProvider = custom;
+ }
+ if( pixelBufferProvider instanceof SingletonGLPixelBufferProvider ) {
+ useSingletonBuffer = true;
+ } else {
+ useSingletonBuffer = false;
+ }
}
- public void postGL(Graphics g, boolean isDisplay) {
- if (isDisplay) {
- // Must now copy pixels from offscreen context into surface
- if (offscreenImage == null) {
- if (panelWidth > 0 && panelHeight > 0) {
- // It looks like NVidia's drivers (at least the ones on my
- // notebook) are buggy and don't allow a sub-rectangle to be
- // read from a pbuffer...this doesn't really matter because
- // it's the Graphics.drawImage() calls that are the
- // bottleneck
-
- int awtFormat = 0;
-
- // Should be more flexible in these BufferedImage formats;
- // perhaps see what the preferred image types are on the
- // given platform
- if (isOpaque()) {
- awtFormat = BufferedImage.TYPE_INT_RGB;
- } else {
- awtFormat = BufferedImage.TYPE_INT_ARGB;
- }
+ @Override
+ public final boolean isUsingOwnLifecycle() { return false; }
- offscreenImage = new BufferedImage(panelWidth,
- panelHeight,
- awtFormat);
- switch (awtFormat) {
- case BufferedImage.TYPE_3BYTE_BGR:
- glFormat = GL2.GL_BGR;
- glType = GL.GL_UNSIGNED_BYTE;
- readBackBytes = ByteBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels * 3);
- break;
-
- case BufferedImage.TYPE_INT_RGB:
- case BufferedImage.TYPE_INT_ARGB:
- glFormat = GL.GL_BGRA;
- glType = getGLPixelType();
- readBackInts = IntBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels);
- break;
-
- default:
- // FIXME: Support more off-screen image types (current
- // offscreen context implementations don't use others, and
- // some of the OpenGL formats aren't supported in the 1.1
- // headers, which we're currently using)
- throw new GLException("Unsupported offscreen image type " + awtFormat);
- }
+ @Override
+ public final void initialize() {
+ if(DEBUG) {
+ System.err.println(getThreadName()+": OffscreenBackend: initialize() - frameCount "+frameCount);
+ }
+ try {
+ final GLContext[] shareWith = { null };
+ if( helper.isSharedGLContextPending(shareWith) ) {
+ return; // pending ..
}
- }
-
- if (offscreenImage != null) {
- GL2 gl = getGL().getGL2();
- // Save current modes
- gl.glGetIntegerv(GL2.GL_PACK_SWAP_BYTES, swapbytes, 0);
- gl.glGetIntegerv(GL2.GL_PACK_ROW_LENGTH, rowlength, 0);
- gl.glGetIntegerv(GL2.GL_PACK_SKIP_ROWS, skiprows, 0);
- gl.glGetIntegerv(GL2.GL_PACK_SKIP_PIXELS, skippixels, 0);
- gl.glGetIntegerv(GL2.GL_PACK_ALIGNMENT, alignment, 0);
-
- gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, GL.GL_FALSE);
- gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, readBackWidthInPixels);
- gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, 0);
- gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, 0);
- gl.glPixelStorei(GL2.GL_PACK_ALIGNMENT, 1);
-
- // Actually read the pixels.
- gl.glReadBuffer(GL2.GL_FRONT);
- if (readBackBytes != null) {
- gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackBytes);
- } else if (readBackInts != null) {
- gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackInts);
+ offscreenDrawable = (GLDrawableImpl) factory.createOffscreenDrawable(
+ null /* default platform device */,
+ offscreenCaps,
+ chooser,
+ panelWidth, panelHeight);
+ offscreenDrawable.setRealized(true);
+ if( DEBUG ) {
+ offscreenDrawable.getNativeSurface().addSurfaceUpdatedListener(new SurfaceUpdatedListener() {
+ @Override
+ public final void surfaceUpdated(Object updater, NativeSurface ns, long when) {
+ System.err.println(getThreadName()+": OffscreenBackend.swapBuffers - frameCount "+frameCount);
+ } } );
}
- // Restore saved modes.
- gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, swapbytes[0]);
- gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, rowlength[0]);
- gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, skiprows[0]);
- gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, skippixels[0]);
- gl.glPixelStorei(GL2.GL_PACK_ALIGNMENT, alignment[0]);
-
- if (readBackBytes != null || readBackInts != null) {
- // Copy temporary data into raster of BufferedImage for faster
- // blitting Note that we could avoid this copy in the cases
- // where !offscreenContext.offscreenImageNeedsVerticalFlip(),
- // but that's the software rendering path which is very slow
- // anyway
- Object src = null;
- Object dest = null;
- int srcIncr = 0;
- int destIncr = 0;
-
- if (readBackBytes != null) {
- src = readBackBytes.array();
- dest = ((DataBufferByte) offscreenImage.getRaster().getDataBuffer()).getData();
- srcIncr = readBackWidthInPixels * 3;
- destIncr = offscreenImage.getWidth() * 3;
- } else {
- src = readBackInts.array();
- dest = ((DataBufferInt) offscreenImage.getRaster().getDataBuffer()).getData();
- srcIncr = readBackWidthInPixels;
- destIncr = offscreenImage.getWidth();
- }
-
- if (flipVertically()) {
- int srcPos = 0;
- int destPos = (offscreenImage.getHeight() - 1) * destIncr;
- for (; destPos >= 0; srcPos += srcIncr, destPos -= destIncr) {
- System.arraycopy(src, srcPos, dest, destPos, destIncr);
+ offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith[0]);
+ offscreenContext.setContextCreationFlags(additionalCtxCreationFlags);
+ if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) {
+ isInitialized = true;
+ helper.setAutoSwapBufferMode(false); // we handle swap-buffers, see handlesSwapBuffer()
+
+ final GL gl = offscreenContext.getGL();
+ flipVertical = !GLJPanel.this.skipGLOrientationVerticalFlip && offscreenDrawable.isGLOriented();
+ final GLCapabilitiesImmutable chosenCaps = offscreenDrawable.getChosenGLCapabilities();
+ offscreenIsFBO = chosenCaps.isFBO();
+ final boolean glslCompliant = !offscreenContext.hasRendererQuirk(GLRendererQuirks.GLSLNonCompliant);
+ final boolean useGLSLFlip = flipVertical && offscreenIsFBO && gl.isGL2ES2() && USE_GLSL_TEXTURE_RASTERIZER && glslCompliant;
+ if( DEBUG ) {
+ System.err.println(getThreadName()+": OffscreenBackend.initialize: useGLSLFlip "+useGLSLFlip+
+ " [flip "+flipVertical+", isFBO "+offscreenIsFBO+", isGL2ES2 "+gl.isGL2ES2()+
+ ", noglsl "+!USE_GLSL_TEXTURE_RASTERIZER+", glslNonCompliant "+!glslCompliant+
+ ", isGL2ES2 " + gl.isGL2ES2()+"]");
}
- } else {
- int srcPos = 0;
- int destEnd = destIncr * offscreenImage.getHeight();
- for (int destPos = 0; destPos < destEnd; srcPos += srcIncr, destPos += destIncr) {
- System.arraycopy(src, srcPos, dest, destPos, destIncr);
+ if( useGLSLFlip ) {
+ final GLFBODrawable fboDrawable = (GLFBODrawable) offscreenDrawable;
+ fboDrawable.setTextureUnit( GLJPanel.this.requestedTextureUnit );
+ try {
+ fboFlipped = new FBObject();
+ fboFlipped.reset(gl, fboDrawable.getWidth(), fboDrawable.getHeight(), 0, false);
+ fboFlipped.attachTexture2D(gl, 0, chosenCaps.getAlphaBits()>0);
+ // fboFlipped.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24);
+ glslTextureRaster = new GLSLTextureRaster(fboDrawable.getTextureUnit(), true);
+ glslTextureRaster.init(gl.getGL2ES2());
+ glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, fboDrawable.getWidth(), fboDrawable.getHeight());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ if(null != glslTextureRaster) {
+ glslTextureRaster.dispose(gl.getGL2ES2());
+ glslTextureRaster = null;
+ }
+ if(null != fboFlipped) {
+ fboFlipped.destroy(gl);
+ fboFlipped = null;
+ }
+ }
+ } else {
+ fboFlipped = null;
+ glslTextureRaster = null;
+ }
+ offscreenContext.release();
+ } else {
+ isInitialized = false;
+ }
+ } finally {
+ if( !isInitialized ) {
+ if(null != offscreenContext) {
+ offscreenContext.destroy();
+ offscreenContext = null;
+ }
+ if(null != offscreenDrawable) {
+ offscreenDrawable.setRealized(false);
+ offscreenDrawable = null;
}
- }
-
- // Note: image will be drawn back in paintComponent() for
- // correctness on all platforms
}
- }
- }
- }
-
- public void doPaintComponent(Graphics g) {
- doPaintComponentImpl();
- if (offscreenImage != null) {
- // Draw resulting image in one shot
- g.drawImage(offscreenImage, 0, 0,
- offscreenImage.getWidth(),
- offscreenImage.getHeight(),
- GLJPanel.this);
- }
- }
-
- protected abstract void doPaintComponentImpl();
- protected abstract int getGLPixelType();
- protected abstract boolean flipVertically();
- }
-
- class SoftwareBackend extends AbstractReadbackBackend {
- // Implementation using software rendering
- private GLDrawableImpl offscreenDrawable;
- private GLContextImpl offscreenContext;
-
- public boolean isUsingOwnThreadManagment() { return false; }
-
- public void initialize() {
- if(DEBUG) {
- System.err.println(getThreadName()+": SoftwareBackend: initialize()");
}
- // Fall-through path: create an offscreen context instead
- offscreenDrawable = (GLDrawableImpl) factory.createOffscreenDrawable(
- null /* default platform device */,
- offscreenCaps,
- chooser,
- Math.max(1, panelWidth),
- Math.max(1, panelHeight));
- offscreenDrawable.setRealized(true);
- offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith);
- offscreenContext.setContextCreationFlags(additionalCtxCreationFlags);
-
- isInitialized = true;
}
- public void destroy() {
+ @Override
+ public final void destroy() {
if(DEBUG) {
- System.err.println(getThreadName()+": SoftwareBackend: destroy() - offscreenContext: "+(null!=offscreenContext)+" - offscreenDrawable: "+(null!=offscreenDrawable));
+ System.err.println(getThreadName()+": OffscreenBackend: destroy() - offscreenContext: "+(null!=offscreenContext)+" - offscreenDrawable: "+(null!=offscreenDrawable)+" - frameCount "+frameCount);
}
- if (offscreenContext != null) {
- offscreenContext.destroy();
- offscreenContext = null;
+ if ( null != offscreenContext && offscreenContext.isCreated() ) {
+ if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) {
+ try {
+ final GL gl = offscreenContext.getGL();
+ if(null != glslTextureRaster) {
+ glslTextureRaster.dispose(gl.getGL2ES2());
+ }
+ if(null != fboFlipped) {
+ fboFlipped.destroy(gl);
+ }
+ } finally {
+ offscreenContext.destroy();
+ }
+ }
}
+ offscreenContext = null;
+ glslTextureRaster = null;
+ fboFlipped = null;
+ offscreenContext = null;
+
if (offscreenDrawable != null) {
final AbstractGraphicsDevice adevice = offscreenDrawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice();
- offscreenDrawable.destroy();
+ offscreenDrawable.setRealized(false);
offscreenDrawable = null;
if(null != adevice) {
adevice.close();
}
}
- }
+ offscreenIsFBO = false;
- public GLContext createContext(GLContext shareWith) {
- return (null != offscreenDrawable) ? offscreenDrawable.createContext(shareWith) : null;
+ if( null != readBackIntsForCPUVFlip ) {
+ readBackIntsForCPUVFlip.clear();
+ readBackIntsForCPUVFlip = null;
+ }
+ if( null != pixelBuffer ) {
+ if( !useSingletonBuffer ) {
+ pixelBuffer.dispose();
+ }
+ pixelBuffer = null;
+ }
+ alignedImage = null;
}
- public void setContext(GLContext ctx) {
- offscreenContext=(GLContextImpl)ctx;
+ @Override
+ public final void setOpaque(boolean opaque) {
+ if ( opaque != isOpaque() && !useSingletonBuffer ) {
+ pixelBuffer.dispose();
+ pixelBuffer = null;
+ alignedImage = null;
+ }
}
- public GLContext getContext() {
- return offscreenContext;
+ @Override
+ public final boolean preGL(Graphics g) {
+ // Empty in this implementation
+ return true;
}
- public GLDrawable getDrawable() {
- return offscreenDrawable;
+ @Override
+ public final boolean handlesSwapBuffer() {
+ return true;
}
- public GLCapabilitiesImmutable getChosenGLCapabilities() {
- if (offscreenDrawable == null) {
- return null;
- }
- return offscreenDrawable.getChosenGLCapabilities();
+ @Override
+ public final void swapBuffers() {
+ final GLDrawable d = offscreenDrawable;
+ if( null != d ) {
+ d.swapBuffers();
+ }
}
- public GLProfile getGLProfile() {
- if (offscreenDrawable == null) {
- return null;
- }
- return offscreenDrawable.getGLProfile();
- }
+ @Override
+ public final void postGL(Graphics g, boolean isDisplay) {
+ if (isDisplay) {
+ if(DEBUG) {
+ System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: - frameCount "+frameCount);
+ }
+ if( autoSwapBufferMode ) {
+ // Since we only use a single-buffer non-MSAA or double-buffered MSAA offscreenDrawable,
+ // we can always swap!
+ offscreenDrawable.swapBuffers();
+ }
+ final GL gl = offscreenContext.getGL();
+
+ final int componentCount;
+ final int alignment;
+ if( isOpaque() ) {
+ // w/o alpha
+ componentCount = 3;
+ alignment = 1;
+ } else {
+ // with alpha
+ componentCount = 4;
+ alignment = 4;
+ }
- public void handleReshape() {
- destroy();
- initialize();
- readBackWidthInPixels = Math.max(1, panelWidth);
- readBackHeightInPixels = Math.max(1, panelHeight);
+ final GLPixelAttributes pixelAttribs = pixelBufferProvider.getAttributes(gl, componentCount);
- if (offscreenImage != null) {
- offscreenImage.flush();
- offscreenImage = null;
- }
- }
+ if( useSingletonBuffer ) { // attempt to fetch the latest AWTGLPixelBuffer
+ pixelBuffer = (AWTGLPixelBuffer) ((SingletonGLPixelBufferProvider)pixelBufferProvider).getSingleBuffer(pixelAttribs);
+ }
+ if( null != pixelBuffer && pixelBuffer.requiresNewBuffer(gl, panelWidth, panelHeight, 0) ) {
+ pixelBuffer.dispose();
+ pixelBuffer = null;
+ alignedImage = null;
+ }
+ if ( null == pixelBuffer ) {
+ if (0 >= panelWidth || 0 >= panelHeight ) {
+ return;
+ }
+ pixelBuffer = pixelBufferProvider.allocate(gl, pixelAttribs, panelWidth, panelHeight, 1, true, 0);
+ if(DEBUG) {
+ System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelBufferProvider isSingletonBufferProvider "+useSingletonBuffer+", 0x"+Integer.toHexString(pixelBufferProvider.hashCode())+", "+pixelBufferProvider.getClass().getSimpleName());
+ System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelBuffer 0x"+Integer.toHexString(pixelBuffer.hashCode())+", "+pixelBuffer+", alignment "+alignment);
+ System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" flippedVertical "+flipVertical+", glslTextureRaster "+(null!=glslTextureRaster));
+ System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" panelSize "+panelWidth+"x"+panelHeight);
+ }
+ }
+ if( offscreenDrawable.getWidth() != panelWidth || offscreenDrawable.getHeight() != panelHeight ) {
+ throw new InternalError("OffscreenDrawable panelSize mismatch (reshape missed): panelSize "+panelWidth+"x"+panelHeight+" != drawable "+offscreenDrawable.getWidth()+"x"+offscreenDrawable.getHeight()+", on thread "+getThreadName());
+ }
+ if( null == alignedImage ||
+ panelWidth != alignedImage.getWidth() || panelHeight != alignedImage.getHeight() ||
+ !pixelBuffer.isDataBufferSource(alignedImage) ) {
+ alignedImage = pixelBuffer.getAlignedImage(panelWidth, panelHeight);
+ if(DEBUG) {
+ System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" new alignedImage "+alignedImage.getWidth()+"x"+alignedImage.getHeight()+", "+alignedImage+", pixelBuffer "+pixelBuffer.width+"x"+pixelBuffer.height+", "+pixelBuffer);
+ }
+ }
+ final IntBuffer readBackInts;
- protected void doPaintComponentImpl() {
- drawableHelper.invokeGL(offscreenDrawable, offscreenContext, updaterDisplayAction, updaterInitAction);
- }
+ if( !flipVertical || null != glslTextureRaster ) {
+ readBackInts = (IntBuffer) pixelBuffer.buffer;
+ } else {
+ if( null == readBackIntsForCPUVFlip || pixelBuffer.width * pixelBuffer.height > readBackIntsForCPUVFlip.remaining() ) {
+ readBackIntsForCPUVFlip = IntBuffer.allocate(pixelBuffer.width * pixelBuffer.height);
+ }
+ readBackInts = readBackIntsForCPUVFlip;
+ }
- protected int getGLPixelType() {
- return offscreenContext.getOffscreenContextPixelDataType();
- }
+ final TextureState usrTexState, fboTexState;
+ final int fboTexUnit = GL.GL_TEXTURE0 + ( offscreenIsFBO ? ((GLFBODrawable)offscreenDrawable).getTextureUnit() : 0 );
+
+ if( offscreenIsFBO ) {
+ usrTexState = new TextureState(gl, GL.GL_TEXTURE_2D);
+ if( fboTexUnit != usrTexState.getUnit() ) {
+ // glActiveTexture(..) + glBindTexture(..) are implicit performed in GLFBODrawableImpl's
+ // swapBuffers/contextMadeCurent -> swapFBOImpl.
+ // We need to cache the texture unit's bound texture-id before it's overwritten.
+ gl.glActiveTexture(fboTexUnit);
+ fboTexState = new TextureState(gl, fboTexUnit, GL.GL_TEXTURE_2D);
+ } else {
+ fboTexState = usrTexState;
+ }
+ } else {
+ usrTexState = null;
+ fboTexState = null;
+ }
- protected boolean flipVertically() {
- return offscreenContext.offscreenImageNeedsVerticalFlip();
- }
- }
+ // Must now copy pixels from offscreen context into surface
+ if(DEBUG) {
+ System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.readPixels: - frameCount "+frameCount);
+ }
- class PbufferBackend extends AbstractReadbackBackend {
- private GLPbuffer pbuffer;
- private int pbufferWidth = 256;
- private int pbufferHeight = 256;
+ // Save current modes
+ psm.setAlignment(gl, alignment, alignment);
+ if(gl.isGL2ES3()) {
+ final GL2ES3 gl2es3 = gl.getGL2ES3();
+ gl2es3.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, panelWidth);
+ gl2es3.glReadBuffer(gl2es3.getDefaultReadBuffer());
+ }
- public boolean isUsingOwnThreadManagment() { return false; }
-
- public void initialize() {
- if (pbuffer != null) {
- throw new InternalError("Creating pbuffer twice without destroying it (memory leak / correctness bug)");
- }
- if(DEBUG) {
- System.err.println(getThreadName()+": PbufferBackend: initialize()");
- }
- try {
- pbuffer = factory.createGLPbuffer(null /* default platform device */,
- offscreenCaps,
- null,
- pbufferWidth,
- pbufferHeight,
- shareWith);
- pbuffer.setContextCreationFlags(additionalCtxCreationFlags);
- pbuffer.addGLEventListener(updater);
- isInitialized = true;
- } catch (GLException e) {
- if (DEBUG) {
- e.printStackTrace();
- System.err.println(getThreadName()+": GLJPanel: Falling back on software rendering because of problems creating pbuffer");
+ if(null != glslTextureRaster) { // implies flippedVertical
+ final boolean viewportChange;
+ final int[] usrViewport = new int[] { 0, 0, 0, 0 };
+ gl.glGetIntegerv(GL.GL_VIEWPORT, usrViewport, 0);
+ viewportChange = 0 != usrViewport[0] || 0 != usrViewport[1] ||
+ offscreenDrawable.getWidth() != usrViewport[2] || offscreenDrawable.getHeight() != usrViewport[3];
+ if( DEBUG_VIEWPORT ) {
+ System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL: "+GLJPanel.this.getName()+" Viewport: change "+viewportChange+
+ ", "+usrViewport[0]+"/"+usrViewport[1]+" "+usrViewport[2]+"x"+usrViewport[3]+
+ " -> 0/0 "+offscreenDrawable.getWidth()+"x"+offscreenDrawable.getHeight());
+ }
+ if( viewportChange ) {
+ gl.glViewport(0, 0, offscreenDrawable.getWidth(), offscreenDrawable.getHeight());
+ }
+
+ // perform vert-flipping via OpenGL/FBO
+ final GLFBODrawable fboDrawable = (GLFBODrawable)offscreenDrawable;
+ final FBObject.TextureAttachment fboTex = fboDrawable.getTextureBuffer(GL.GL_FRONT);
+
+ fboFlipped.bind(gl);
+
+ // gl.glActiveTexture(GL.GL_TEXTURE0 + fboDrawable.getTextureUnit()); // implicit by GLFBODrawableImpl: swapBuffers/contextMadeCurent -> swapFBOImpl
+ gl.glBindTexture(GL.GL_TEXTURE_2D, fboTex.getName());
+ // gl.glClear(GL.GL_DEPTH_BUFFER_BIT); // fboFlipped runs w/o DEPTH!
+
+ glslTextureRaster.display(gl.getGL2ES2());
+ gl.glReadPixels(0, 0, panelWidth, panelHeight, pixelAttribs.format, pixelAttribs.type, readBackInts);
+
+ fboFlipped.unbind(gl);
+ if( viewportChange ) {
+ gl.glViewport(usrViewport[0], usrViewport[1], usrViewport[2], usrViewport[3]);
+ }
+ fboTexState.restore(gl);
+ } else {
+ gl.glReadPixels(0, 0, panelWidth, panelHeight, pixelAttribs.format, pixelAttribs.type, readBackInts);
+
+ if ( flipVertical ) {
+ // Copy temporary data into raster of BufferedImage for faster
+ // blitting Note that we could avoid this copy in the cases
+ // where !offscreenDrawable.isGLOriented(),
+ // but that's the software rendering path which is very slow anyway.
+ final BufferedImage image = alignedImage;
+ final int[] src = readBackInts.array();
+ final int[] dest = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
+ final int incr = panelWidth;
+ int srcPos = 0;
+ int destPos = (panelHeight - 1) * panelWidth;
+ for (; destPos >= 0; srcPos += incr, destPos -= incr) {
+ System.arraycopy(src, srcPos, dest, destPos, incr);
+ }
+ }
+ }
+ if( offscreenIsFBO && fboTexUnit != usrTexState.getUnit() ) {
+ usrTexState.restore(gl);
}
- hardwareAccelerationDisabled = true;
- backend = null;
- isInitialized = false;
- createAndInitializeBackend();
- }
- }
- public void destroy() {
- if(DEBUG) {
- System.err.println(getThreadName()+": PbufferBackend: destroy() - pbuffer: "+(null!=pbuffer));
- }
- if (pbuffer != null) {
- pbuffer.destroy();
- pbuffer = null;
+ // Restore saved modes.
+ psm.restore(gl);
+
+ // Note: image will be drawn back in paintComponent() for
+ // correctness on all platforms
}
}
- public GLContext createContext(GLContext shareWith) {
- return (null != pbuffer) ? pbuffer.createContext(shareWith) : null;
+ @Override
+ public final int getTextureUnit() {
+ if(null != glslTextureRaster && null != offscreenDrawable) { // implies flippedVertical
+ return ((GLFBODrawable)offscreenDrawable).getTextureUnit();
+ }
+ return -1;
}
- public void setContext(GLContext ctx) {
- if (pbuffer == null && Beans.isDesignTime()) {
- return;
- }
- pbuffer.setContext(ctx);
- }
+ @Override
+ public final void doPaintComponent(Graphics g) {
+ helper.invokeGL(offscreenDrawable, offscreenContext, updaterDisplayAction, updaterInitAction);
- public GLContext getContext() {
- // Workaround for crashes in NetBeans GUI builder
- if (pbuffer == null && Beans.isDesignTime()) {
- return null;
+ if ( null != alignedImage ) {
+ if( DEBUG ) {
+ System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.doPaintComponent.drawImage: - frameCount "+frameCount);
+ }
+ // Draw resulting image in one shot
+ g.drawImage(alignedImage, 0, 0, alignedImage.getWidth(), alignedImage.getHeight(), null); // Null ImageObserver since image data is ready.
}
- return pbuffer.getContext();
+ frameCount++;
}
- public GLDrawable getDrawable() {
- return pbuffer;
+ @Override
+ public final void doPlainPaint() {
+ helper.invokeGL(offscreenDrawable, offscreenContext, updaterPlainDisplayAction, updaterInitAction);
}
- public GLCapabilitiesImmutable getChosenGLCapabilities() {
- if (pbuffer == null) {
- return null;
- }
- return pbuffer.getChosenGLCapabilities();
- }
-
- public GLProfile getGLProfile() {
- if (pbuffer == null) {
- return null;
- }
- return pbuffer.getGLProfile();
- }
-
- public void handleReshape() {
- // Use factor larger than 2 during shrinks for some hysteresis
- float shrinkFactor = 2.5f;
- if ((panelWidth > pbufferWidth) || (panelHeight > pbufferHeight) ||
- (panelWidth < (pbufferWidth / shrinkFactor)) || (panelHeight < (pbufferHeight / shrinkFactor))) {
+ @Override
+ public final boolean handleReshape() {
+ GLDrawableImpl _drawable = offscreenDrawable;
+ {
+ final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, offscreenContext, panelWidth, panelHeight);
+ if(_drawable != _drawableNew) {
+ // write back
+ _drawable = _drawableNew;
+ offscreenDrawable = _drawableNew;
+ }
+ }
if (DEBUG) {
- System.err.println(getThreadName()+": Resizing pbuffer from (" + pbufferWidth + ", " + pbufferHeight + ") " +
- " to fit (" + panelWidth + ", " + panelHeight + ")");
+ System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.handleReshape: " +panelWidth+"x"+panelHeight + " -> " + _drawable.getWidth()+"x"+_drawable.getHeight());
}
- // Must destroy and recreate pbuffer to fit
- if (pbuffer != null) {
- // Watch for errors during pbuffer destruction (due to
- // buggy / bad OpenGL drivers, in particular SiS) and fall
- // back to software rendering
- try {
- pbuffer.destroy();
- } catch (GLException e) {
- hardwareAccelerationDisabled = true;
- backend = null;
- isInitialized = false;
- // Just disabled hardware acceleration during this resize operation; do a fixup
- readBackWidthInPixels = Math.max(1, panelWidth);
- readBackHeightInPixels = Math.max(1, panelHeight);
- if (DEBUG) {
- System.err.println(getThreadName()+": Warning: falling back to software rendering due to bugs in OpenGL drivers");
- e.printStackTrace();
+ panelWidth = _drawable.getWidth();
+ panelHeight = _drawable.getHeight();
+
+ if( null != glslTextureRaster ) {
+ if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) {
+ try {
+ final GL gl = offscreenContext.getGL();
+ fboFlipped.reset(gl, _drawable.getWidth(), _drawable.getHeight(), 0, false);
+ glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, _drawable.getWidth(), _drawable.getHeight());
+ } finally {
+ offscreenContext.release();
+ }
}
- createAndInitializeBackend();
- return;
- }
}
- pbuffer = null;
- isInitialized = false;
- pbufferWidth = getNextPowerOf2(panelWidth);
- pbufferHeight = getNextPowerOf2(panelHeight);
- if (DEBUG && !hardwareAccelerationDisabled) {
- System.err.println(getThreadName()+": New pbuffer size is (" + pbufferWidth + ", " + pbufferHeight + ")");
- }
- initialize();
- }
+ return _drawable.isRealized();
+ }
- // It looks like NVidia's drivers (at least the ones on my
- // notebook) are buggy and don't allow a rectangle of less than
- // the pbuffer's width to be read...this doesn't really matter
- // because it's the Graphics.drawImage() calls that are the
- // bottleneck. Should probably make the size of the offscreen
- // image be the exact size of the pbuffer to save some work on
- // resize operations...
- readBackWidthInPixels = pbufferWidth;
- readBackHeightInPixels = panelHeight;
-
- if (offscreenImage != null) {
- offscreenImage.flush();
- offscreenImage = null;
- }
+ @Override
+ public final GLContext createContext(GLContext shareWith) {
+ return (null != offscreenDrawable) ? offscreenDrawable.createContext(shareWith) : null;
}
- protected void doPaintComponentImpl() {
- pbuffer.display();
+ @Override
+ public final void setContext(GLContext ctx) {
+ offscreenContext=(GLContextImpl)ctx;
}
- protected int getGLPixelType() {
- // This seems to be a good choice on all platforms
- return GL2.GL_UNSIGNED_INT_8_8_8_8_REV;
+ @Override
+ public final GLContext getContext() {
+ return offscreenContext;
}
- protected boolean flipVertically() {
- return true;
+ @Override
+ public final GLDrawable getDrawable() {
+ return offscreenDrawable;
+ }
+
+ @Override
+ public final GLCapabilitiesImmutable getChosenGLCapabilities() {
+ if (offscreenDrawable == null) {
+ return null;
+ }
+ return offscreenDrawable.getChosenGLCapabilities();
+ }
+
+ @Override
+ public final GLProfile getGLProfile() {
+ if (offscreenDrawable == null) {
+ return null;
+ }
+ return offscreenDrawable.getGLProfile();
}
}
@@ -1222,11 +1875,11 @@ public void reshape(int x, int y, int width, int height) {
private GLContext joglContext;
// State captured from Java2D OpenGL context necessary in order to
// properly render into Java2D back buffer
- private int[] drawBuffer = new int[1];
- private int[] readBuffer = new int[1];
+ private final int[] drawBuffer = new int[1];
+ private final int[] readBuffer = new int[1];
// This is required when the FBO option of the Java2D / OpenGL
// pipeline is active
- private int[] frameBuffer = new int[1];
+ private final int[] frameBuffer = new int[1];
// Current (as of this writing) NVidia drivers have a couple of bugs
// relating to the sharing of framebuffer and renderbuffer objects
// between contexts. It appears we have to (a) reattach the color
@@ -1255,9 +1908,11 @@ public void reshape(int x, int y, int width, int height) {
// comment related to Issue 274 below
private GraphicsConfiguration workaroundConfig;
- public boolean isUsingOwnThreadManagment() { return true; }
-
- public void initialize() {
+ @Override
+ public final boolean isUsingOwnLifecycle() { return true; }
+
+ @Override
+ public final void initialize() {
if(DEBUG) {
System.err.println(getThreadName()+": J2DOGL: initialize()");
}
@@ -1265,8 +1920,10 @@ public void reshape(int x, int y, int width, int height) {
isInitialized = true;
}
- public void destroy() {
+ @Override
+ public final void destroy() {
Java2D.invokeWithOGLContextCurrent(null, new Runnable() {
+ @Override
public void run() {
if(DEBUG) {
System.err.println(getThreadName()+": J2DOGL: destroy() - joglContext: "+(null!=joglContext)+" - joglDrawable: "+(null!=joglDrawable));
@@ -1284,45 +1941,58 @@ public void reshape(int x, int y, int width, int height) {
});
}
- public void setOpaque(boolean opaque) {
+ @Override
+ public final void setOpaque(boolean opaque) {
// Empty in this implementation
}
- public GLContext createContext(GLContext shareWith) {
+ @Override
+ public final GLContext createContext(GLContext shareWith) {
if(null != shareWith) {
throw new GLException("J2DOGLBackend cannot create context w/ additional shared context, since it already needs to share the context w/ J2D.");
}
return (null != joglDrawable && null != j2dContext) ? joglDrawable.createContext(j2dContext) : null;
}
- public void setContext(GLContext ctx) {
+ @Override
+ public final void setContext(GLContext ctx) {
joglContext=ctx;
}
- public GLContext getContext() {
+ @Override
+ public final GLContext getContext() {
return joglContext;
}
- public GLDrawable getDrawable() {
+ @Override
+ public final GLDrawable getDrawable() {
return joglDrawable;
}
- public GLCapabilitiesImmutable getChosenGLCapabilities() {
- // FIXME: should do better than this; is it possible to using only platform-independent code?
+ @Override
+ public final int getTextureUnit() { return -1; }
+
+ @Override
+ public final GLCapabilitiesImmutable getChosenGLCapabilities() {
+ // FIXME: should do better than this; is it possible to query J2D Caps ?
return new GLCapabilities(null);
}
- public GLProfile getGLProfile() {
- // FIXME: should do better than this; is it possible to using only platform-independent code?
+ @Override
+ public final GLProfile getGLProfile() {
+ // FIXME: should do better than this; is it possible to query J2D's Profile ?
return GLProfile.getDefault(GLProfile.getDefaultDevice());
}
- public void handleReshape() {
+ @Override
+ public final boolean handleReshape() {
// Empty in this implementation
+ return true;
}
- public boolean preGL(Graphics g) {
- GL2 gl = joglContext.getGL().getGL2();
+ @Override
+ public final boolean preGL(Graphics g) {
+ final GL2 gl = joglContext.getGL().getGL2();
// Set up needed state in JOGL context from Java2D context
gl.glEnable(GL2.GL_SCISSOR_TEST);
Rectangle r = Java2D.getOGLScissorBox(g);
@@ -1403,13 +2073,13 @@ public void reshape(int x, int y, int width, int height) {
int[] height = new int[1];
gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2.GL_TEXTURE_WIDTH, width, 0);
gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2.GL_TEXTURE_HEIGHT, height, 0);
-
+
gl.glGenRenderbuffers(1, frameBufferDepthBuffer, 0);
if (DEBUG) {
System.err.println(getThreadName()+": GLJPanel: Generated frameBufferDepthBuffer " + frameBufferDepthBuffer[0] +
" with width " + width[0] + ", height " + height[0]);
}
-
+
gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, frameBufferDepthBuffer[0]);
// FIXME: may need a loop here like in Java2D
gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, GL2GL3.GL_DEPTH_COMPONENT24, width[0], height[0]);
@@ -1457,11 +2127,25 @@ public void reshape(int x, int y, int width, int height) {
return true;
}
- public void postGL(Graphics g, boolean isDisplay) {
+ @Override
+ public final boolean handlesSwapBuffer() {
+ return false;
+ }
+
+ @Override
+ public final void swapBuffers() {
+ final GLDrawable d = joglDrawable;
+ if( null != d ) {
+ d.swapBuffers();
+ }
+ }
+
+ @Override
+ public final void postGL(Graphics g, boolean isDisplay) {
// Cause OpenGL pipeline to flush its results because
// otherwise it's possible we will buffer up multiple frames'
// rendering results, resulting in apparent mouse lag
- GL gl = getGL();
+ GL gl = joglContext.getGL();
gl.glFinish();
if (Java2D.isFBOEnabled() &&
@@ -1473,7 +2157,8 @@ public void reshape(int x, int y, int width, int height) {
}
}
- public void doPaintComponent(final Graphics g) {
+ @Override
+ public final void doPaintComponent(final Graphics g) {
// This is a workaround for an issue in the Java 2D / JOGL
// bridge (reported by an end user as JOGL Issue 274) where Java
// 2D can occasionally leave its internal OpenGL context current
@@ -1498,10 +2183,12 @@ public void reshape(int x, int y, int width, int height) {
getDefaultScreenDevice().
getDefaultConfiguration();
}
- Java2D.invokeWithOGLSharedContextCurrent(workaroundConfig, new Runnable() { public void run() {}});
+ Java2D.invokeWithOGLSharedContextCurrent(workaroundConfig, new Runnable() { @Override
+ public void run() {}});
}
Java2D.invokeWithOGLContextCurrent(g, new Runnable() {
+ @Override
public void run() {
if (DEBUG) {
System.err.println(getThreadName()+": GLJPanel.invokeWithOGLContextCurrent");
@@ -1549,7 +2236,7 @@ public void reshape(int x, int y, int width, int height) {
}
isInitialized = false;
backend = null;
- oglPipelineEnabled = false;
+ java2DGLPipelineOK = false;
handleReshape = true;
j2dContext.destroy();
j2dContext = null;
@@ -1603,29 +2290,14 @@ public void reshape(int x, int y, int width, int height) {
System.err.println("-- Created External Drawable: "+joglDrawable);
System.err.println("-- Created Context: "+joglContext);
}
- } else if (factory.canCreateContextOnJava2DSurface(device)) {
- // Mac OS X code path
- joglContext = factory.createContextOnJava2DSurface(g, j2dContext);
- if (DEBUG) {
- System.err.println("-- Created Context: "+joglContext);
- }
}
- /*if (DEBUG) {
- joglContext.setGL(new DebugGL2(joglContext.getGL().getGL2()));
- }*/
-
if (Java2D.isFBOEnabled() &&
Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT &&
fbObjectWorkarounds) {
createNewDepthBuffer = true;
}
}
- if (joglContext instanceof Java2DGLContext) {
- // Mac OS X code path
- ((Java2DGLContext) joglContext).setGraphics(g);
- }
-
- drawableHelper.invokeGL(joglDrawable, joglContext, updaterDisplayAction, updaterInitAction);
+ helper.invokeGL(joglDrawable, joglContext, updaterDisplayAction, updaterInitAction);
}
} finally {
j2dContext.release();
@@ -1634,7 +2306,12 @@ public void reshape(int x, int y, int width, int height) {
});
}
- private void captureJ2DState(GL gl, Graphics g) {
+ @Override
+ public final void doPlainPaint() {
+ helper.invokeGL(joglDrawable, joglContext, updaterPlainDisplayAction, updaterInitAction);
+ }
+
+ private final void captureJ2DState(GL gl, Graphics g) {
gl.glGetIntegerv(GL2.GL_DRAW_BUFFER, drawBuffer, 0);
gl.glGetIntegerv(GL2.GL_READ_BUFFER, readBuffer, 0);
if (Java2D.isFBOEnabled() &&