aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/java')
-rw-r--r--src/net/java/games/jogl/Animator.java18
-rw-r--r--src/net/java/games/jogl/GLCanvas.java38
-rw-r--r--src/net/java/games/jogl/impl/GLContext.java122
-rwxr-xr-xsrc/net/java/games/jogl/impl/SingleThreadedWorkaround.java86
-rw-r--r--src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java12
-rw-r--r--src/net/java/games/jogl/impl/windows/WindowsGLContext.java15
-rw-r--r--src/net/java/games/jogl/impl/windows/WindowsGLContextFactory.java35
-rw-r--r--src/net/java/games/jogl/impl/windows/WindowsOnscreenGLContext.java9
-rw-r--r--src/net/java/games/jogl/impl/x11/X11GLContext.java17
-rw-r--r--src/net/java/games/jogl/impl/x11/X11OffscreenGLContext.java1
-rw-r--r--src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java1
-rw-r--r--src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java1
12 files changed, 323 insertions, 32 deletions
diff --git a/src/net/java/games/jogl/Animator.java b/src/net/java/games/jogl/Animator.java
index 5ab2aad75..e844c1501 100644
--- a/src/net/java/games/jogl/Animator.java
+++ b/src/net/java/games/jogl/Animator.java
@@ -39,6 +39,9 @@
package net.java.games.jogl;
+import java.awt.EventQueue;
+import net.java.games.jogl.impl.SingleThreadedWorkaround;
+
/** <P> An Animator can be attached to a GLDrawable to drive its
display() method in a loop. For efficiency, it sets up the
rendering thread for the drawable to be its own internal thread,
@@ -113,7 +116,12 @@ public class Animator {
// during display(), so don't disable the rendering
// thread again.
if (noException) {
- drawable.setRenderingThread(null);
+ // Destruction of the underlying GLContext may have
+ // disabled the setRenderingThread optimization out
+ // from under us
+ if (drawable.getRenderingThread() != null) {
+ drawable.setRenderingThread(null);
+ }
}
} finally {
synchronized (Animator.this) {
@@ -133,6 +141,14 @@ public class Animator {
finished. */
public synchronized void stop() {
shouldStop = true;
+ // It's hard to tell whether the thread which calls stop() has
+ // dependencies on the Animator's internal thread. Currently we
+ // use a couple of heuristics to determine whether we should do
+ // the blocking wait().
+ if ((Thread.currentThread() == thread) ||
+ (SingleThreadedWorkaround.doWorkaround() && EventQueue.isDispatchThread())) {
+ return;
+ }
while (shouldStop && thread != null) {
try {
wait();
diff --git a/src/net/java/games/jogl/GLCanvas.java b/src/net/java/games/jogl/GLCanvas.java
index ee33a14c9..1deac7f32 100644
--- a/src/net/java/games/jogl/GLCanvas.java
+++ b/src/net/java/games/jogl/GLCanvas.java
@@ -40,6 +40,7 @@
package net.java.games.jogl;
import java.awt.Canvas;
+import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import net.java.games.jogl.impl.*;
@@ -60,7 +61,7 @@ public final class GLCanvas extends Canvas implements GLDrawable {
private GLDrawableHelper drawableHelper = new GLDrawableHelper();
private GLContext context;
-
+
GLCanvas(GraphicsConfiguration config,
GLCapabilities capabilities,
GLCapabilitiesChooser chooser,
@@ -82,6 +83,20 @@ public final class GLCanvas extends Canvas implements GLDrawable {
}
}
+ /** Overridden from Canvas; used to indicate when it's safe to
+ create an OpenGL context for the component. */
+ public void addNotify() {
+ super.addNotify();
+ context.setRealized();
+ }
+
+ /** Overridden from Canvas; used to indicate that it's no longer
+ safe to have an OpenGL context for the component. */
+ public void removeNotify() {
+ context.destroy();
+ super.removeNotify();
+ }
+
/** Overridden from Canvas; causes {@link GLDrawableHelper#reshape}
to be called on all registered {@link GLEventListener}s. Called
automatically by the AWT; should not be invoked by applications
@@ -183,7 +198,15 @@ public final class GLCanvas extends Canvas implements GLDrawable {
//
private void displayImpl() {
- context.invokeGL(displayAction, false, initAction);
+ if (SingleThreadedWorkaround.doWorkaround() && !EventQueue.isDispatchThread()) {
+ try {
+ EventQueue.invokeAndWait(displayOnEventDispatchThreadAction);
+ } catch (Exception e) {
+ throw new GLException(e);
+ }
+ } else {
+ context.invokeGL(displayAction, false, initAction);
+ }
}
class InitAction implements Runnable {
@@ -206,4 +229,15 @@ public final class GLCanvas extends Canvas implements GLDrawable {
}
}
private SwapBuffersAction swapBuffersAction = new SwapBuffersAction();
+
+ // 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
+ class DisplayOnEventDispatchThreadAction implements Runnable {
+ public void run() {
+ context.invokeGL(displayAction, false, initAction);
+ }
+ }
+ private DisplayOnEventDispatchThreadAction displayOnEventDispatchThreadAction =
+ new DisplayOnEventDispatchThreadAction();
}
diff --git a/src/net/java/games/jogl/impl/GLContext.java b/src/net/java/games/jogl/impl/GLContext.java
index 9d6036aaf..5260bda6a 100644
--- a/src/net/java/games/jogl/impl/GLContext.java
+++ b/src/net/java/games/jogl/impl/GLContext.java
@@ -40,7 +40,6 @@
package net.java.games.jogl.impl;
import java.awt.Component;
-import java.awt.EventQueue;
import net.java.games.jogl.*;
import net.java.games.gluegen.runtime.*;
@@ -62,8 +61,8 @@ public abstract class GLContext {
// fetched from a locked DrawingSurface during the validation as a
// result of calling show() on the main thread. To work around this
// we prevent any JAWT or OpenGL operations from being done until
- // the first event is received from the AWT event dispatch thread.
- private boolean realized;
+ // addNotify() is called on the component.
+ protected boolean realized;
protected GLCapabilities capabilities;
protected GLCapabilitiesChooser chooser;
@@ -77,6 +76,14 @@ public abstract class GLContext {
protected GLU glu = gluRoot; // this is the context's GLU interface
protected Thread renderingThread;
protected Runnable deferredReshapeAction;
+ // Support for OpenGL context destruction and recreation in the face
+ // of the setRenderingThread optimization, which makes the context
+ // permanently current on the animation thread. FIXME: should make
+ // this more uniform and general, possibly by implementing in terms
+ // of Runnables; however, necessary sequence of operations in
+ // invokeGL makes this tricky.
+ protected boolean deferredDestroy;
+ protected boolean deferredSetRealized;
// Error checking for setRenderingThread to ensure that one thread
// doesn't attempt to call setRenderingThread on more than one
@@ -163,26 +170,41 @@ public abstract class GLContext {
// Defer JAWT and OpenGL operations until onscreen components are
// realized
- if (!realized()) {
- realized = EventQueue.isDispatchThread();
- }
-
- if (!realized() ||
+ if (!isRealized() ||
willSetRenderingThread ||
(renderingThread != null &&
renderingThread != currentThread)) {
- if (isReshape) {
- deferredReshapeAction = runnable;
+ // Support for removeNotify()/addNotify() when the
+ // setRenderingThread optimization is in effect and before the
+ // animation thread gets a chance to handle either request
+ if (!isRealized() && deferredSetRealized) {
+ setRealized();
+ deferredSetRealized = false;
+ } else {
+ if (isReshape) {
+ deferredReshapeAction = runnable;
+ }
+ return;
}
- return;
}
- if (isReshape && noAutoRedraw) {
+ if (isReshape && noAutoRedraw && !SingleThreadedWorkaround.doWorkaround()) {
// Don't process reshape requests on the AWT thread
deferredReshapeAction = runnable;
return;
}
+ if (deferredDestroy) {
+ deferredDestroy = false;
+ if (renderingThread != null) {
+ // Need to disable the setRenderingThread optimization to free
+ // up the context
+ setRenderingThread(null, initAction);
+ }
+ destroy();
+ return;
+ }
+
// The goal of this code is to optimize OpenGL context handling as
// much as possible. In particular:
//
@@ -345,6 +367,11 @@ public abstract class GLContext {
}
public synchronized void setRenderingThread(Thread currentThreadOrNull, Runnable initAction) {
+ if (SingleThreadedWorkaround.doWorkaround()) {
+ willSetRenderingThread = false;
+ return;
+ }
+
Thread currentThread = Thread.currentThread();
if (currentThreadOrNull != null && currentThreadOrNull != currentThread) {
throw new GLException("Argument must be either the current thread or null");
@@ -436,6 +463,7 @@ public abstract class GLContext {
resetProcAddressTable(gluProcAddressTable);
haveResetGLUProcAddressTable = true; // Only need to do this once globally
}
+ recomputeSingleThreadedWorkaround();
}
/**
@@ -550,6 +578,53 @@ public abstract class GLContext {
GLException to be thrown. */
protected abstract void free() throws GLException;
+ /** Inform the system that the associated heavyweight widget has
+ been realized and that it is safe to create an associated OpenGL
+ context. If the widget is later destroyed then destroy() should
+ be called, which will cause the underlying OpenGL context to be
+ destroyed as well as the realized bit to be set to false. */
+ public void setRealized() {
+ if (getRenderingThread() != null &&
+ Thread.currentThread() != getRenderingThread()) {
+ deferredSetRealized = true;
+ return;
+ }
+ setRealized(true);
+ }
+
+ /** Sets only the "realized" bit. Should be called by subclasses
+ from within the destroy() implementation. */
+ protected synchronized void setRealized(boolean realized) {
+ this.realized = realized;
+ }
+
+ /** Indicates whether the component associated with this context has
+ been realized. */
+ public synchronized boolean getRealized() {
+ return realized;
+ }
+
+ /** Destroys the underlying OpenGL context and changes the realized
+ state to false. This should be called when the widget is being
+ destroyed. */
+ public synchronized void destroy() throws GLException {
+ if (getRenderingThread() != null &&
+ Thread.currentThread() != getRenderingThread()) {
+ deferredDestroy = true;
+ return;
+ }
+ setRealized(false);
+ GLContextShareSet.contextDestroyed(this);
+ destroyImpl();
+ }
+
+ /** Destroys the underlying OpenGL context. */
+ protected abstract void destroyImpl() throws GLException;
+
+ public synchronized boolean isRealized() {
+ return (component == null || getRealized());
+ }
+
/** Helper routine which resets a ProcAddressTable generated by the
GLEmitter by looking up anew all of its function pointers. */
protected void resetProcAddressTable(Object table) {
@@ -610,10 +685,23 @@ public abstract class GLContext {
perThreadSavedCurrentContext.set(new GLContextInitActionPair(context, initAction));
}
- //----------------------------------------------------------------------
- // Internals only below this point
- //
- private boolean realized() {
- return ((component == null) || realized || component.isDisplayable());
+ /** Support for automatic detection of whether we need to enable the
+ single-threaded workaround for ATI and other vendors' cards.
+ Should be called by subclasses for onscreen rendering inside
+ their makeCurrent() implementation once the context is
+ current. */
+ private void recomputeSingleThreadedWorkaround() {
+ if (!SingleThreadedWorkaround.doWorkaround()) {
+ GL gl = getGL();
+ String str = gl.glGetString(GL.GL_VENDOR);
+ if (str != null && str.indexOf("ATI") >= 0) {
+ // Doing this instead of calling setRenderingThread(null) should
+ // be OK since we are doing this very early in the maintenance
+ // of the per-thread context stack, before we are actually
+ // pushing any GLContext objects on it
+ renderingThread = null;
+ SingleThreadedWorkaround.shouldDoWorkaround();
+ }
+ }
}
}
diff --git a/src/net/java/games/jogl/impl/SingleThreadedWorkaround.java b/src/net/java/games/jogl/impl/SingleThreadedWorkaround.java
new file mode 100755
index 000000000..ca6485ea2
--- /dev/null
+++ b/src/net/java/games/jogl/impl/SingleThreadedWorkaround.java
@@ -0,0 +1,86 @@
+/*
+ * 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
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
+ * MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
+ * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
+ * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
+ * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
+ * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
+ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
+ * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
+ * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package net.java.games.jogl.impl;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/** Encapsulates the workaround of running all display operations on
+ the AWT event queue thread for the purposes of working around
+ problems seen primarily on ATI cards when rendering into a surface
+ that is simultaneously being resized by the event queue thread */
+
+public class SingleThreadedWorkaround {
+ private static boolean ATI_WORKAROUND = false;
+ // If the user specified the workaround's system property (either
+ // true or false), don't let the automatic detection have any effect
+ private static boolean systemPropertySpecified = false;
+
+ static {
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ String workaround = System.getProperty("ATI_WORKAROUND");
+ if (workaround != null) {
+ systemPropertySpecified = true;
+ ATI_WORKAROUND = Boolean.valueOf(workaround).booleanValue();
+ }
+ printWorkaroundNotice();
+ return null;
+ }
+ });
+ }
+
+ public static void shouldDoWorkaround() {
+ if (!systemPropertySpecified) {
+ ATI_WORKAROUND = true;
+ printWorkaroundNotice();
+ }
+ }
+
+ public static boolean doWorkaround() {
+ return ATI_WORKAROUND;
+ }
+
+ private static void printWorkaroundNotice() {
+ if (ATI_WORKAROUND) {
+ System.err.println("Using ATI workaround of dispatching display() on event thread");
+ }
+ }
+}
diff --git a/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java b/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java
index 2a34709f2..22d12b575 100644
--- a/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java
+++ b/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java
@@ -172,6 +172,18 @@ public abstract class MacOSXGLContext extends GLContext
}
}
+ protected void destroyImpl() throws GLException {
+ if (nsContext != 0) {
+ if (!CGL.deleteContext(nsContext, null)) {
+ throw new GLException("Unable to delete OpenGL context");
+ }
+ if (DEBUG) {
+ System.err.println("!!! Destroyed OpenGL context " + nsContext);
+ }
+ nsContext = 0;
+ }
+ }
+
public abstract void swapBuffers() throws GLException;
protected long dynamicLookupFunction(String glFuncName) {
diff --git a/src/net/java/games/jogl/impl/windows/WindowsGLContext.java b/src/net/java/games/jogl/impl/windows/WindowsGLContext.java
index 56a7188eb..d58a781a8 100644
--- a/src/net/java/games/jogl/impl/windows/WindowsGLContext.java
+++ b/src/net/java/games/jogl/impl/windows/WindowsGLContext.java
@@ -168,6 +168,18 @@ public abstract class WindowsGLContext extends GLContext {
}
}
+ protected void destroyImpl() throws GLException {
+ if (hglrc != 0) {
+ if (!WGL.wglDeleteContext(hglrc)) {
+ throw new GLException("Unable to delete OpenGL context");
+ }
+ if (DEBUG) {
+ System.err.println("!!! Destroyed OpenGL context " + hglrc);
+ }
+ hglrc = 0;
+ }
+ }
+
public abstract void swapBuffers() throws GLException;
protected long dynamicLookupFunction(String glFuncName) {
@@ -455,6 +467,9 @@ public abstract class WindowsGLContext extends GLContext {
throw new GLException("Unable to set pixel format");
}
hglrc = WGL.wglCreateContext(hdc);
+ if (DEBUG) {
+ System.err.println("!!! Created OpenGL context " + hglrc);
+ }
if (hglrc == 0) {
throw new GLException("Unable to create OpenGL context");
}
diff --git a/src/net/java/games/jogl/impl/windows/WindowsGLContextFactory.java b/src/net/java/games/jogl/impl/windows/WindowsGLContextFactory.java
index 961a116d8..74d72645d 100644
--- a/src/net/java/games/jogl/impl/windows/WindowsGLContextFactory.java
+++ b/src/net/java/games/jogl/impl/windows/WindowsGLContextFactory.java
@@ -101,10 +101,10 @@ public class WindowsGLContextFactory extends GLContextFactory {
GraphicsConfiguration config = device.getDefaultConfiguration();
final Dialog frame = new Dialog(new Frame(config), "", false, config);
frame.setUndecorated(true);
- GLCanvas canvas = GLDrawableFactory.getFactory().createGLCanvas(new GLCapabilities(),
- null,
- null,
- device);
+ final GLCanvas canvas = GLDrawableFactory.getFactory().createGLCanvas(new GLCapabilities(),
+ null,
+ null,
+ device);
canvas.addGLEventListener(new GLEventListener() {
public void init(GLDrawable drawable) {
pendingContextSet.remove(device);
@@ -133,16 +133,29 @@ public class WindowsGLContextFactory extends GLContextFactory {
public void reshape(GLDrawable drawable, int x, int y, int width, int height) {
}
+ public void destroy(GLDrawable drawable) {
+ }
+
public void displayChanged(GLDrawable drawable, boolean modeChanged, boolean deviceChanged) {
}
});
- canvas.setSize(0, 0);
- canvas.setNoAutoRedrawMode(true);
- canvas.setAutoSwapBufferMode(false);
- frame.add(canvas);
- frame.pack();
- frame.show();
- canvas.display();
+ // Attempt to work around deadlock issues with SingleThreadedWorkaround,
+ // which causes some of the methods below to block doing work on the AWT thread
+ try {
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ canvas.setSize(0, 0);
+ canvas.setNoAutoRedrawMode(true);
+ canvas.setAutoSwapBufferMode(false);
+ frame.add(canvas);
+ frame.pack();
+ frame.show();
+ canvas.display();
+ }
+ });
+ } catch (Exception e) {
+ throw new GLException(e);
+ }
}
return (GL) dummyContextMap.get(device);
diff --git a/src/net/java/games/jogl/impl/windows/WindowsOnscreenGLContext.java b/src/net/java/games/jogl/impl/windows/WindowsOnscreenGLContext.java
index 868846079..5bcdb4559 100644
--- a/src/net/java/games/jogl/impl/windows/WindowsOnscreenGLContext.java
+++ b/src/net/java/games/jogl/impl/windows/WindowsOnscreenGLContext.java
@@ -155,12 +155,19 @@ public class WindowsOnscreenGLContext extends WindowsGLContext {
throw new GLException("Unable to lock surface");
}
// See whether the surface changed and if so destroy the old
- // OpenGL context so it will be recreated
+ // OpenGL context so it will be recreated (NOTE: removeNotify
+ // should handle this case, but it may be possible that race
+ // conditions can cause this code to be triggered -- should test
+ // more)
if ((res & JAWTFactory.JAWT_LOCK_SURFACE_CHANGED) != 0) {
if (hglrc != 0) {
if (!WGL.wglDeleteContext(hglrc)) {
throw new GLException("Unable to delete old GL context after surface changed");
}
+ GLContextShareSet.contextDestroyed(this);
+ if (DEBUG) {
+ System.err.println("!!! Destroyed OpenGL context " + hglrc + " due to JAWT_LOCK_SURFACE_CHANGED");
+ }
hglrc = 0;
}
}
diff --git a/src/net/java/games/jogl/impl/x11/X11GLContext.java b/src/net/java/games/jogl/impl/x11/X11GLContext.java
index e3bb4ce72..40e462fb3 100644
--- a/src/net/java/games/jogl/impl/x11/X11GLContext.java
+++ b/src/net/java/games/jogl/impl/x11/X11GLContext.java
@@ -59,6 +59,11 @@ public abstract class X11GLContext extends GLContext {
// OpenGL functions.
private GLProcAddressTable glProcAddressTable;
private static boolean haveResetGLXProcAddressTable;
+ // Cache the most recent value of the "display" variable (which we
+ // only guarantee to be valid in between makeCurrent / free pairs)
+ // so that we can implement displayImpl() (which must be done when
+ // the context is not current)
+ protected long mostRecentDisplay;
static {
functionNameMap = new HashMap();
@@ -167,6 +172,18 @@ public abstract class X11GLContext extends GLContext {
}
}
+ protected void destroyImpl() throws GLException {
+ if (context != 0) {
+ if (!GLX.glXDestroyContext(mostRecentDisplay, context)) {
+ throw new GLException("Unable to delete OpenGL context");
+ }
+ if (DEBUG) {
+ System.err.println("!!! Destroyed OpenGL context " + context);
+ }
+ context = 0;
+ }
+ }
+
public abstract void swapBuffers() throws GLException;
protected long dynamicLookupFunction(String glFuncName) {
diff --git a/src/net/java/games/jogl/impl/x11/X11OffscreenGLContext.java b/src/net/java/games/jogl/impl/x11/X11OffscreenGLContext.java
index 8c88b0225..0290f8d44 100644
--- a/src/net/java/games/jogl/impl/x11/X11OffscreenGLContext.java
+++ b/src/net/java/games/jogl/impl/x11/X11OffscreenGLContext.java
@@ -129,6 +129,7 @@ public class X11OffscreenGLContext extends X11GLContext {
pendingOffscreenResize = false;
}
}
+ mostRecentDisplay = display;
return super.makeCurrent(initAction);
}
diff --git a/src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java b/src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java
index 911fc3740..1b5f54979 100644
--- a/src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java
+++ b/src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java
@@ -189,6 +189,7 @@ public class X11OnscreenGLContext extends X11GLContext {
visualID = 0;
return false;
}
+ mostRecentDisplay = display;
return true;
}
diff --git a/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java b/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java
index ccbba8b92..5ab976870 100644
--- a/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java
+++ b/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java
@@ -217,6 +217,7 @@ public class X11PbufferGLContext extends X11GLContext {
// Set up instance variables
this.display = display;
+ mostRecentDisplay = display;
this.parentContext = parentContext;
buffer = tmpBuffer;
this.fbConfig = fbConfig;