diff options
Diffstat (limited to 'src/net/java/games/jogl/impl')
15 files changed, 436 insertions, 134 deletions
diff --git a/src/net/java/games/jogl/impl/FunctionAvailabilityCache.java b/src/net/java/games/jogl/impl/FunctionAvailabilityCache.java index bceb66f9b..a4b67b5e8 100644 --- a/src/net/java/games/jogl/impl/FunctionAvailabilityCache.java +++ b/src/net/java/games/jogl/impl/FunctionAvailabilityCache.java @@ -52,7 +52,7 @@ import java.lang.reflect.*; * and display. */ public final class FunctionAvailabilityCache { - private static final boolean DEBUG = false; + private static final boolean DEBUG = Debug.debug("FunctionAvailabilityCache"); FunctionAvailabilityCache(GLContext context) { @@ -114,6 +114,10 @@ public final class FunctionAvailabilityCache { } String allAvailableExtensions = gl.glGetString(GL.GL_EXTENSIONS) + " " + context.getPlatformExtensionsString(); + if (DEBUG) { + System.err.println("!!! Available extensions: " + allAvailableExtensions); + System.err.println("!!! GL vendor: " + gl.glGetString(GL.GL_VENDOR)); + } StringTokenizer tok = new StringTokenizer(allAvailableExtensions); while (tok.hasMoreTokens()) { String availableExt = tok.nextToken().trim(); diff --git a/src/net/java/games/jogl/impl/GLContext.java b/src/net/java/games/jogl/impl/GLContext.java index 2aa8e9640..17667fbce 100644 --- a/src/net/java/games/jogl/impl/GLContext.java +++ b/src/net/java/games/jogl/impl/GLContext.java @@ -44,7 +44,7 @@ import net.java.games.jogl.*; import net.java.games.gluegen.runtime.*; public abstract class GLContext { - protected static final boolean DEBUG = false; + protected static final boolean DEBUG = Debug.debug("GLContext"); static { NativeLibLoader.load(); @@ -145,11 +145,7 @@ public abstract class GLContext { GLCapabilitiesChooser chooser, GLContext shareWith) { this.component = component; - try { - this.capabilities = (GLCapabilities) capabilities.clone(); - } catch (CloneNotSupportedException e) { - throw new GLException(e); - } + this.capabilities = (GLCapabilities) capabilities.clone(); this.chooser = chooser; setGL(createGL()); functionAvailability = new FunctionAvailabilityCache(this); @@ -255,6 +251,15 @@ public abstract class GLContext { if (isReshape) { deferredReshapeAction = runnable; } + + // Clean up after ourselves on the way out. + // NOTE that this is an abbreviated version of the code below + // and should probably be refactored/cleaned up -- this bug + // fix was done without a lot of intense thought about the + // situation + if (curContext != null) { + curContext.makeCurrent(curInitAction); + } return; } if (DEBUG) { @@ -620,6 +625,9 @@ public abstract class GLContext { from within the destroy() implementation. */ protected synchronized void setRealized(boolean realized) { this.realized = realized; + if (DEBUG) { + System.err.println("GLContext.setRealized(" + realized + ") for context " + this); + } } /** Indicates whether the component associated with this context has diff --git a/src/net/java/games/jogl/impl/GLPbufferImpl.java b/src/net/java/games/jogl/impl/GLPbufferImpl.java index 2fb927c92..be0f23398 100644 --- a/src/net/java/games/jogl/impl/GLPbufferImpl.java +++ b/src/net/java/games/jogl/impl/GLPbufferImpl.java @@ -40,6 +40,7 @@ package net.java.games.jogl.impl; import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.event.*; import java.beans.PropertyChangeListener; @@ -61,7 +62,9 @@ public class GLPbufferImpl implements GLPbuffer { } public void display() { - context.invokeGL(displayAction, false, initAction); + maybeDoSingleThreadedWorkaround(displayOnEventDispatchThreadAction, + displayAction, + false); } public void setSize(int width, int height) { @@ -107,7 +110,7 @@ public class GLPbufferImpl implements GLPbuffer { } void willSetRenderingThread() { - context.willSetRenderingThread(); + // Not supported for pbuffers } public void setRenderingThread(Thread currentThreadOrNull) throws GLException { @@ -135,7 +138,7 @@ public class GLPbufferImpl implements GLPbuffer { } public void swapBuffers() { - context.invokeGL(swapBuffersAction, false, initAction); + maybeDoSingleThreadedWorkaround(swapBuffersOnEventDispatchThreadAction, swapBuffersAction, false); } public boolean canCreateOffscreenDrawable() { @@ -149,10 +152,14 @@ public class GLPbufferImpl implements GLPbuffer { } public void bindTexture() { + // Doesn't make much sense to try to do this on the event dispatch + // thread given that it has to be called while the context is current context.bindPbufferToTexture(); } public void releaseTexture() { + // Doesn't make much sense to try to do this on the event dispatch + // thread given that it has to be called while the context is current context.releasePbufferFromTexture(); } @@ -196,10 +203,38 @@ public class GLPbufferImpl implements GLPbuffer { return isInitialized; } + public void destroy() { + context.destroy(); + } + //---------------------------------------------------------------------- // Internals only below this point // + private void maybeDoSingleThreadedWorkaround(Runnable eventDispatchThreadAction, + Runnable invokeGLAction, + boolean isReshape) { + if (SingleThreadedWorkaround.doWorkaround() && !EventQueue.isDispatchThread()) { + try { + // Reshape events must not block on the event queue due to the + // possibility of deadlocks during initial component creation. + // This solution is not optimal, because it changes the + // semantics of reshape() to have some of the processing being + // done asynchronously, but at least it preserves the + // semantics of the single-threaded workaround. + if (!isReshape) { + EventQueue.invokeAndWait(eventDispatchThreadAction); + } else { + EventQueue.invokeLater(eventDispatchThreadAction); + } + } catch (Exception e) { + throw new GLException(e); + } + } else { + context.invokeGL(invokeGLAction, isReshape, initAction); + } + } + class InitAction implements Runnable { public void run() { drawableHelper.init(GLPbufferImpl.this); @@ -221,4 +256,22 @@ public class GLPbufferImpl implements GLPbuffer { } } 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(); + class SwapBuffersOnEventDispatchThreadAction implements Runnable { + public void run() { + context.invokeGL(swapBuffersAction, false, initAction); + } + } + private SwapBuffersOnEventDispatchThreadAction swapBuffersOnEventDispatchThreadAction = + new SwapBuffersOnEventDispatchThreadAction(); } diff --git a/src/net/java/games/jogl/impl/NativeLibLoader.java b/src/net/java/games/jogl/impl/NativeLibLoader.java index cf2b06939..0c2420955 100644 --- a/src/net/java/games/jogl/impl/NativeLibLoader.java +++ b/src/net/java/games/jogl/impl/NativeLibLoader.java @@ -42,34 +42,45 @@ package net.java.games.jogl.impl; import java.security.*; public class NativeLibLoader { - static { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - boolean isOSX = System.getProperty("os.name").equals("Mac OS X"); - if (!isOSX) { - try { - System.loadLibrary("jawt"); - } catch (UnsatisfiedLinkError e) { - // Accessibility technologies load JAWT themselves; safe to continue - // as long as JAWT is loaded by any loader - if (e.getMessage().indexOf("already loaded") == -1) { - throw e; - } - } - } - System.loadLibrary("jogl"); + private static volatile boolean doLoading = true; + private static volatile boolean doneLoading = false; - // Workaround for 4845371. - // Make sure the first reference to the JNI GetDirectBufferAddress is done - // from a privileged context so the VM's internal class lookups will succeed. - JAWT jawt = new JAWT(); - JAWTFactory.JAWT_GetAWT(jawt); + public static void disableLoading() { + doLoading = false; + } - return null; - } - }); + public static void enableLoading() { + doLoading = true; } - public static void load() { + public static synchronized void load() { + if (doLoading && !doneLoading) { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + boolean isOSX = System.getProperty("os.name").equals("Mac OS X"); + if (!isOSX) { + try { + System.loadLibrary("jawt"); + } catch (UnsatisfiedLinkError e) { + // Accessibility technologies load JAWT themselves; safe to continue + // as long as JAWT is loaded by any loader + if (e.getMessage().indexOf("already loaded") == -1) { + throw e; + } + } + } + System.loadLibrary("jogl"); + + // Workaround for 4845371. + // Make sure the first reference to the JNI GetDirectBufferAddress is done + // from a privileged context so the VM's internal class lookups will succeed. + JAWT jawt = new JAWT(); + JAWTFactory.JAWT_GetAWT(jawt); + + return null; + } + }); + doneLoading = true; + } } } diff --git a/src/net/java/games/jogl/impl/SingleThreadedWorkaround.java b/src/net/java/games/jogl/impl/SingleThreadedWorkaround.java index 9dbed9e43..7b9fa46c4 100755 --- a/src/net/java/games/jogl/impl/SingleThreadedWorkaround.java +++ b/src/net/java/games/jogl/impl/SingleThreadedWorkaround.java @@ -45,24 +45,40 @@ 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 */ + that is simultaneously being resized by the event queue thread. + <p> + + As of JOGL 1.1 b10, this property defaults to true. Problems have + been seen on Windows, Linux and Mac OS X platforms that are solved + by switching all OpenGL work to a single thread, which this + workaround provides. The forthcoming JSR-231 work will rethink how + such a mechanism is implemented, but the core result of needing to + perform all OpenGL work on a single thread for best compatibility + will remain. +*/ public class SingleThreadedWorkaround { - private static boolean ATI_WORKAROUND = false; + private static boolean singleThreadedWorkaround = true; // 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; - private static boolean verbose = false; static { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { - String workaround = System.getProperty("ATI_WORKAROUND"); - if (workaround != null) { + String workaround = System.getProperty("jogl.1thread"); + if (workaround == null) { + // Old system property (for compatibility) + workaround = System.getProperty("JOGL_SINGLE_THREADED_WORKAROUND"); + } + if (workaround == null) { + // Older system property (for compatibility) + workaround = System.getProperty("ATI_WORKAROUND"); + } + if (workaround != null && (!workaround.equals("auto"))) { systemPropertySpecified = true; - ATI_WORKAROUND = Boolean.valueOf(workaround).booleanValue(); + singleThreadedWorkaround = Boolean.valueOf(workaround).booleanValue(); } - verbose = (System.getProperty("jogl.verbose") != null); printWorkaroundNotice(); return null; } @@ -71,18 +87,18 @@ public class SingleThreadedWorkaround { public static void shouldDoWorkaround() { if (!systemPropertySpecified) { - ATI_WORKAROUND = true; + singleThreadedWorkaround = true; printWorkaroundNotice(); } } public static boolean doWorkaround() { - return ATI_WORKAROUND; + return singleThreadedWorkaround; } private static void printWorkaroundNotice() { - if (ATI_WORKAROUND && verbose) { - System.err.println("Using ATI workaround of dispatching display() on event thread"); + if (singleThreadedWorkaround && Debug.verbose()) { + System.err.println("Using single-threaded 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 f8af120da..3c8027e7c 100644 --- a/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java +++ b/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java @@ -132,6 +132,7 @@ public abstract class MacOSXGLContext extends GLContext } nsContext = CGL.createContext(share, nsView, + capabilities.getDoubleBuffered() ? 1 : 0, capabilities.getRedBits(), capabilities.getGreenBits(), capabilities.getBlueBits(), diff --git a/src/net/java/games/jogl/impl/macosx/MacOSXPbufferGLContext.java b/src/net/java/games/jogl/impl/macosx/MacOSXPbufferGLContext.java index 6ac854090..8e65ce1f3 100644 --- a/src/net/java/games/jogl/impl/macosx/MacOSXPbufferGLContext.java +++ b/src/net/java/games/jogl/impl/macosx/MacOSXPbufferGLContext.java @@ -4,8 +4,7 @@ import net.java.games.jogl.*; import net.java.games.jogl.impl.*; public class MacOSXPbufferGLContext extends MacOSXGLContext { - - private static final boolean DEBUG = false; + private static final boolean DEBUG = Debug.debug("MacOSXPbufferGLContext"); protected int initWidth; protected int initHeight; @@ -131,6 +130,10 @@ public class MacOSXPbufferGLContext extends MacOSXGLContext { return false; } + protected void destroyImpl() throws GLException { + destroyPBuffer(); + } + public void swapBuffers() throws GLException { // FIXME: do we need to do anything if the pbuffer is double-buffered? } diff --git a/src/net/java/games/jogl/impl/windows/WindowsGLContext.java b/src/net/java/games/jogl/impl/windows/WindowsGLContext.java index eed383794..37a3f5359 100644 --- a/src/net/java/games/jogl/impl/windows/WindowsGLContext.java +++ b/src/net/java/games/jogl/impl/windows/WindowsGLContext.java @@ -61,6 +61,7 @@ public abstract class WindowsGLContext extends GLContext { private GLProcAddressTable glProcAddressTable; // Handle to GLU32.dll private long hglu32; + private boolean haveWGLARBPbuffer = true; private static final int MAX_PFORMATS = 256; private static final int MAX_ATTRIBS = 256; @@ -141,10 +142,17 @@ public abstract class WindowsGLContext extends GLContext { if (!WGL.wglMakeCurrent(hdc, hglrc)) { throw new GLException("Error making context current: " + WGL.GetLastError()); + } else { + if (DEBUG) { + System.err.println("wglMakeCurrent(hdc " + hdcToString(hdc) + + ", hglrc " + hdcToString(hglrc) + ") succeeded"); + } } if (created) { resetGLFunctionAvailability(); + haveWGLARBPbuffer = (isExtensionAvailable("WGL_ARB_pbuffer") && + isExtensionAvailable("WGL_ARB_pixel_format")); // Windows can set up sharing of display lists after creation time WindowsGLContext other = (WindowsGLContext) GLContextShareSet.getShareContext(this); if (other != null) { @@ -154,7 +162,8 @@ public abstract class WindowsGLContext extends GLContext { } if (!WGL.wglShareLists(hglrc2, hglrc)) { throw new GLException("wglShareLists(0x" + Long.toHexString(hglrc2) + - ", 0x" + Long.toHexString(hglrc) + ") failed"); + ", 0x" + Long.toHexString(hglrc) + ") failed: error code " + + WGL.GetLastError()); } } GLContextShareSet.contextCreated(this); @@ -302,6 +311,8 @@ public abstract class WindowsGLContext extends GLContext { freeWGLC = true; } } + // Fallback path for older cards, in particular Intel Extreme motherboard graphics + boolean gotAvailableCaps = false; if (dummyGL != null && haveWGLChoosePixelFormatARB) { int[] iattributes = new int [2 * MAX_ATTRIBS]; int[] iresults = new int [2 * MAX_ATTRIBS]; @@ -408,46 +419,54 @@ public abstract class WindowsGLContext extends GLContext { // window, to a pbuffer, or to a pixmap) niattribs = 0; iattributes[0] = GL.WGL_NUMBER_PIXEL_FORMATS_ARB; - if (!dummyGL.wglGetPixelFormatAttribivARB(hdc, 0, 0, 1, iattributes, iresults)) { - throw new GLException("Unable to enumerate pixel formats of window using wglGetPixelFormatAttribivARB: " + WGL.GetLastError()); - } - numFormats = iresults[0]; - // Should we be filtering out the pixel formats which aren't - // applicable, as we are doing here? - // We don't have enough information in the GLCapabilities to - // represent those that aren't... - iattributes[niattribs++] = GL.WGL_DRAW_TO_WINDOW_ARB; - iattributes[niattribs++] = GL.WGL_ACCELERATION_ARB; - iattributes[niattribs++] = GL.WGL_SUPPORT_OPENGL_ARB; - iattributes[niattribs++] = GL.WGL_DEPTH_BITS_ARB; - iattributes[niattribs++] = GL.WGL_STENCIL_BITS_ARB; - iattributes[niattribs++] = GL.WGL_DOUBLE_BUFFER_ARB; - iattributes[niattribs++] = GL.WGL_STEREO_ARB; - iattributes[niattribs++] = GL.WGL_PIXEL_TYPE_ARB; - iattributes[niattribs++] = GL.WGL_RED_BITS_ARB; - iattributes[niattribs++] = GL.WGL_GREEN_BITS_ARB; - iattributes[niattribs++] = GL.WGL_BLUE_BITS_ARB; - iattributes[niattribs++] = GL.WGL_ALPHA_BITS_ARB; - iattributes[niattribs++] = GL.WGL_ACCUM_RED_BITS_ARB; - iattributes[niattribs++] = GL.WGL_ACCUM_GREEN_BITS_ARB; - iattributes[niattribs++] = GL.WGL_ACCUM_BLUE_BITS_ARB; - iattributes[niattribs++] = GL.WGL_ACCUM_ALPHA_BITS_ARB; - if (haveWGLARBMultisample) { - iattributes[niattribs++] = GL.WGL_SAMPLE_BUFFERS_ARB; - iattributes[niattribs++] = GL.WGL_SAMPLES_ARB; - } + if (dummyGL.wglGetPixelFormatAttribivARB(hdc, 0, 0, 1, iattributes, iresults)) { + numFormats = iresults[0]; + // Should we be filtering out the pixel formats which aren't + // applicable, as we are doing here? + // We don't have enough information in the GLCapabilities to + // represent those that aren't... + iattributes[niattribs++] = GL.WGL_DRAW_TO_WINDOW_ARB; + iattributes[niattribs++] = GL.WGL_ACCELERATION_ARB; + iattributes[niattribs++] = GL.WGL_SUPPORT_OPENGL_ARB; + iattributes[niattribs++] = GL.WGL_DEPTH_BITS_ARB; + iattributes[niattribs++] = GL.WGL_STENCIL_BITS_ARB; + iattributes[niattribs++] = GL.WGL_DOUBLE_BUFFER_ARB; + iattributes[niattribs++] = GL.WGL_STEREO_ARB; + iattributes[niattribs++] = GL.WGL_PIXEL_TYPE_ARB; + iattributes[niattribs++] = GL.WGL_RED_BITS_ARB; + iattributes[niattribs++] = GL.WGL_GREEN_BITS_ARB; + iattributes[niattribs++] = GL.WGL_BLUE_BITS_ARB; + iattributes[niattribs++] = GL.WGL_ALPHA_BITS_ARB; + iattributes[niattribs++] = GL.WGL_ACCUM_RED_BITS_ARB; + iattributes[niattribs++] = GL.WGL_ACCUM_GREEN_BITS_ARB; + iattributes[niattribs++] = GL.WGL_ACCUM_BLUE_BITS_ARB; + iattributes[niattribs++] = GL.WGL_ACCUM_ALPHA_BITS_ARB; + if (haveWGLARBMultisample) { + iattributes[niattribs++] = GL.WGL_SAMPLE_BUFFERS_ARB; + iattributes[niattribs++] = GL.WGL_SAMPLES_ARB; + } - availableCaps = new GLCapabilities[numFormats]; - for (int i = 0; i < numFormats; i++) { - if (!dummyGL.wglGetPixelFormatAttribivARB(hdc, i+1, 0, niattribs, iattributes, iresults)) { - throw new GLException("Error getting pixel format attributes for pixel format " + (i + 1) + " of device context"); + availableCaps = new GLCapabilities[numFormats]; + for (int i = 0; i < numFormats; i++) { + if (!dummyGL.wglGetPixelFormatAttribivARB(hdc, i+1, 0, niattribs, iattributes, iresults)) { + throw new GLException("Error getting pixel format attributes for pixel format " + (i + 1) + " of device context"); + } + availableCaps[i] = iattributes2GLCapabilities(iattributes, iresults, niattribs, true); + } + if( freeWGLC ) { + WGL.wglMakeCurrent( 0, 0 ); + } + gotAvailableCaps = true; + } else { + int lastErr = WGL.GetLastError(); + // Intel Extreme graphics fails with a zero error code + if (lastErr != 0) { + throw new GLException("Unable to enumerate pixel formats of window using wglGetPixelFormatAttribivARB: error code " + WGL.GetLastError()); } - availableCaps[i] = iattributes2GLCapabilities(iattributes, iresults, niattribs, true); - } - if( freeWGLC ) { - WGL.wglMakeCurrent( 0, 0 ); } - } else { + } + + if (!gotAvailableCaps) { if (DEBUG) { System.err.println("Using ChoosePixelFormat because no wglChoosePixelFormatARB: dummyGL = " + dummyGL); } @@ -467,6 +486,7 @@ public abstract class WindowsGLContext extends GLContext { availableCaps[i] = pfd2GLCapabilities(pfd); } } + // Supply information to chooser pixelFormat = chooser.chooseCapabilities(capabilities, availableCaps, recommendedPixelFormat); if ((pixelFormat < 0) || (pixelFormat >= numFormats)) { @@ -490,11 +510,17 @@ public abstract class WindowsGLContext extends GLContext { pixelFormat = WGL.ChoosePixelFormat(hdc, pfd); } if (!WGL.SetPixelFormat(hdc, pixelFormat, pfd)) { - throw new GLException("Unable to set pixel format"); + int lastError = WGL.GetLastError(); + if (DEBUG) { + System.err.println("SetPixelFormat failed: current context = " + WGL.wglGetCurrentContext() + + ", current DC = " + WGL.wglGetCurrentDC()); + System.err.println("GetPixelFormat(hdc " + hdcToString(hdc) + ") returns " + WGL.GetPixelFormat(hdc)); + } + throw new GLException("Unable to set pixel format " + pixelFormat + " for device context " + hdcToString(hdc) + ": error code " + lastError); } hglrc = WGL.wglCreateContext(hdc); if (DEBUG) { - System.err.println("!!! Created OpenGL context " + hglrc); + System.err.println("!!! Created OpenGL context " + hglrc + " for device context " + hdcToString(hdc) + " using pixel format " + pixelFormat); } if (hglrc == 0) { throw new GLException("Unable to create OpenGL context"); @@ -654,4 +680,12 @@ public abstract class WindowsGLContext extends GLContext { } return res; } + + protected static String hdcToString(long hdc) { + return "0x" + Long.toHexString(hdc); + } + + protected boolean haveWGLARBPbuffer() { + return haveWGLARBPbuffer; + } } diff --git a/src/net/java/games/jogl/impl/windows/WindowsGLContextFactory.java b/src/net/java/games/jogl/impl/windows/WindowsGLContextFactory.java index 726091bf1..1c976c355 100644 --- a/src/net/java/games/jogl/impl/windows/WindowsGLContextFactory.java +++ b/src/net/java/games/jogl/impl/windows/WindowsGLContextFactory.java @@ -43,6 +43,7 @@ import java.awt.Component; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.Rectangle; +import java.io.File; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.HashMap; @@ -74,6 +75,56 @@ public class WindowsGLContextFactory extends GLContextFactory { AccessController.doPrivileged( new PrivilegedAction() { public Object run() { Runtime.getRuntime().addShutdownHook( new ShutdownHook() ); + + // Test for whether we should enable the single-threaded + // workaround for ATI cards. It appears that if we make any + // OpenGL context current on more than one thread on ATI cards + // on Windows then we see random failures like the inability + // to create more OpenGL contexts, or having just the next + // OpenGL SetPixelFormat operation fail with a GetNextError() + // code of 0 (but subsequent ones on subsequently-created + // windows succeed). These kinds of failures are obviously due + // to bugs in ATI's OpenGL drivers. Through trial and error it + // was found that specifying + // -DJOGL_SINGLE_THREADED_WORKAROUND=true on the command line + // caused these problems to completely disappear. Therefore at + // least on Windows we try to enable the single-threaded + // workaround before creating any OpenGL contexts. In the + // future, if problems are encountered on other platforms and + // -DJOGL_SINGLE_THREADED_WORKAROUND=true works around them, + // we may want to implement a workaround like this on other + // platforms. + + // The algorithm here is to try to find the system directory + // (assuming it is on the same drive as TMPDIR, exposed + // through the system property java.io.tmpdir) and see whether + // a known file in the ATI drivers is present; if it is, we + // enable the single-threaded workaround. + + // If any path down this code fails, we simply bail out -- we + // don't go to great lengths to figure out if the ATI drivers + // are present. We could add more checks here in the future if + // these appear to be insufficient. + + String tmpDirProp = System.getProperty("java.io.tmpdir"); + if (tmpDirProp != null) { + File file = new File(tmpDirProp); + if (file.isAbsolute()) { + File parent = null; + do { + parent = file.getParentFile(); + if (parent != null) { + file = parent; + } + } while (parent != null); + // Now the file contains just the drive letter + file = new File(new File(new File(file, "windows"), "system32"), "atioglxx.dll"); + if (file.exists()) { + SingleThreadedWorkaround.shouldDoWorkaround(); + } + } + } + return( null ); } }); diff --git a/src/net/java/games/jogl/impl/windows/WindowsOffscreenGLContext.java b/src/net/java/games/jogl/impl/windows/WindowsOffscreenGLContext.java index 9e871caba..5743253a0 100644 --- a/src/net/java/games/jogl/impl/windows/WindowsOffscreenGLContext.java +++ b/src/net/java/games/jogl/impl/windows/WindowsOffscreenGLContext.java @@ -172,7 +172,8 @@ public class WindowsOffscreenGLContext extends WindowsGLContext { } hbitmap = WGL.CreateDIBSection(hdc, info, WGL.DIB_RGB_COLORS, 0, 0, 0); if (hbitmap == 0) { - throw new GLException("Error creating offscreen bitmap"); + throw new GLException("Error creating offscreen bitmap of width " + width + + ", height " + height); } if ((origbitmap = WGL.SelectObject(hdc, hbitmap)) == 0) { throw new GLException("Error selecting bitmap into new device context"); diff --git a/src/net/java/games/jogl/impl/windows/WindowsOnscreenGLContext.java b/src/net/java/games/jogl/impl/windows/WindowsOnscreenGLContext.java index 3bfdee5d1..de13be0a1 100644 --- a/src/net/java/games/jogl/impl/windows/WindowsOnscreenGLContext.java +++ b/src/net/java/games/jogl/impl/windows/WindowsOnscreenGLContext.java @@ -61,6 +61,25 @@ public class WindowsOnscreenGLContext extends WindowsGLContext { super(component, capabilities, chooser, shareWith); } + public void invokeGL(Runnable runnable, boolean isReshape, Runnable initAction) throws GLException { + // Unfortunately, invokeGL can be called with the AWT tree lock + // held, and the Windows onscreen implementation of + // choosePixelFormatAndCreateContext calls + // Component.getGraphicsConfiguration(), which grabs the tree + // lock. To avoid deadlock we have to lock the tree lock before + // grabbing the GLContext's lock if we're going to create an + // OpenGL context during this call. This code might not be + // completely correct, and we might need to uniformly grab the AWT + // tree lock, which might become a performance issue... + if (hglrc == 0) { + synchronized(component.getTreeLock()) { + super.invokeGL(runnable, isReshape, initAction); + } + } else { + super.invokeGL(runnable, isReshape, initAction); + } + } + protected GL createGL() { return new WindowsGLImpl(this); @@ -83,7 +102,7 @@ public class WindowsOnscreenGLContext extends WindowsGLContext { } public boolean canCreatePbufferContext() { - return true; + return haveWGLARBPbuffer(); } public synchronized GLContext createPbufferContext(GLCapabilities capabilities, diff --git a/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java b/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java index d3795905f..f0ce74d9d 100644 --- a/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java +++ b/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java @@ -43,7 +43,7 @@ import net.java.games.jogl.*; import net.java.games.jogl.impl.*; public class WindowsPbufferGLContext extends WindowsGLContext { - private static final boolean DEBUG = false; + private static final boolean DEBUG = Debug.debug("WindowsPbufferGLContext"); private int initWidth; private int initHeight; @@ -75,6 +75,13 @@ public class WindowsPbufferGLContext extends WindowsGLContext { throw new GLException("Initial width and height of pbuffer must be positive (were (" + initWidth + ", " + initHeight + "))"); } + + if (DEBUG) { + System.out.println("Pbuffer caps on init: " + capabilities + + (capabilities.getOffscreenRenderToTexture() ? " [rtt]" : "") + + (capabilities.getOffscreenRenderToTextureRectangle() ? " [rect]" : "") + + (capabilities.getOffscreenFloatingPointBuffers() ? " [float]" : "")); + } } public boolean canCreatePbufferContext() { @@ -128,6 +135,13 @@ public class WindowsPbufferGLContext extends WindowsGLContext { int nfattribs = 0; int niattribs = 0; + if (DEBUG) { + System.out.println("Pbuffer caps: " + capabilities + + (capabilities.getOffscreenRenderToTexture() ? " [rtt]" : "") + + (capabilities.getOffscreenRenderToTextureRectangle() ? " [rect]" : "") + + (capabilities.getOffscreenFloatingPointBuffers() ? " [float]" : "")); + } + rtt = capabilities.getOffscreenRenderToTexture(); rect = capabilities.getOffscreenRenderToTextureRectangle(); boolean useFloat = capabilities.getOffscreenFloatingPointBuffers(); @@ -228,9 +242,10 @@ public class WindowsPbufferGLContext extends WindowsGLContext { iattributes[5] = GL.WGL_FLOAT_COMPONENTS_NV; iattributes[6] = GL.WGL_SAMPLE_BUFFERS_EXT; iattributes[7] = GL.WGL_SAMPLES_EXT; - int[] ivalues = new int[8]; + iattributes[8] = GL.WGL_DRAW_TO_PBUFFER_ARB; + int[] ivalues = new int[9]; for (int i = 0; i < nformats; i++) { - if (!gl.wglGetPixelFormatAttribivARB(parentHdc, pformats[i], 0, 8, iattributes, ivalues)) { + if (!gl.wglGetPixelFormatAttribivARB(parentHdc, pformats[i], 0, 9, iattributes, ivalues)) { throw new GLException("Error while querying pixel format " + pformats[i] + "'s (index " + i + "'s) capabilities for debugging"); } @@ -245,38 +260,49 @@ public class WindowsPbufferGLContext extends WindowsGLContext { if (ivalues[5] != 0) { System.err.print(" [float]"); } + if (ivalues[8] != 0) { + System.err.print(" [pbuffer]"); + } System.err.println(); } } - int format = pformats[0]; + long tmpBuffer = 0; + int whichFormat = 0; + // Loop is a workaround for bugs in NVidia's recent drivers + do { + int format = pformats[whichFormat]; - // Create the p-buffer. - niattribs = 0; + // Create the p-buffer. + niattribs = 0; - if (rtt) { - iattributes[niattribs++] = GL.WGL_TEXTURE_FORMAT_ARB; - if (useFloat) { - iattributes[niattribs++] = GL.WGL_TEXTURE_FLOAT_RGB_NV; - } else { - iattributes[niattribs++] = GL.WGL_TEXTURE_RGBA_ARB; - } + if (rtt) { + iattributes[niattribs++] = GL.WGL_TEXTURE_FORMAT_ARB; + if (useFloat) { + iattributes[niattribs++] = GL.WGL_TEXTURE_FLOAT_RGB_NV; + } else { + iattributes[niattribs++] = GL.WGL_TEXTURE_RGBA_ARB; + } - iattributes[niattribs++] = GL.WGL_TEXTURE_TARGET_ARB; - iattributes[niattribs++] = rect ? GL.WGL_TEXTURE_RECTANGLE_NV : GL.WGL_TEXTURE_2D_ARB; + iattributes[niattribs++] = GL.WGL_TEXTURE_TARGET_ARB; + iattributes[niattribs++] = rect ? GL.WGL_TEXTURE_RECTANGLE_NV : GL.WGL_TEXTURE_2D_ARB; - iattributes[niattribs++] = GL.WGL_MIPMAP_TEXTURE_ARB; - iattributes[niattribs++] = GL.GL_FALSE; + iattributes[niattribs++] = GL.WGL_MIPMAP_TEXTURE_ARB; + iattributes[niattribs++] = GL.GL_FALSE; - iattributes[niattribs++] = GL.WGL_PBUFFER_LARGEST_ARB; - iattributes[niattribs++] = GL.GL_FALSE; - } + iattributes[niattribs++] = GL.WGL_PBUFFER_LARGEST_ARB; + iattributes[niattribs++] = GL.GL_FALSE; + } + + iattributes[niattribs++] = 0; - iattributes[niattribs++] = 0; + tmpBuffer = gl.wglCreatePbufferARB(parentHdc, format, initWidth, initHeight, iattributes); + ++whichFormat; + } while ((tmpBuffer == 0) && (whichFormat < nformats)); - long tmpBuffer = gl.wglCreatePbufferARB(parentHdc, format, initWidth, initHeight, iattributes); if (tmpBuffer == 0) { - throw new GLException("pbuffer creation error: wglCreatePbufferARB() failed: " + wglGetLastError()); + throw new GLException("pbuffer creation error: wglCreatePbufferARB() failed: tried " + nformats + + " pixel formats, last error was: " + wglGetLastError()); } // Get the device context. @@ -308,10 +334,16 @@ public class WindowsPbufferGLContext extends WindowsGLContext { if (buffer == 0) { // pbuffer not instantiated yet + if (DEBUG) { + System.err.println("pbuffer not instantiated yet"); + } return false; } boolean res = super.makeCurrent(initAction); + if (DEBUG) { + System.err.println("super.makeCurrent() = " + res + ", created = " + created); + } if (created) { // Initialize render-to-texture support if requested rtt = capabilities.getOffscreenRenderToTexture(); @@ -396,6 +428,22 @@ public class WindowsPbufferGLContext extends WindowsGLContext { } } + protected void destroyImpl() throws GLException { + if (hglrc != 0) { + super.destroyImpl(); + // Must release DC and pbuffer + GL gl = getGL(); + if (gl.wglReleasePbufferDCARB(buffer, hdc) == 0) { + throw new GLException("Error releasing pbuffer device context: error code " + WGL.GetLastError()); + } + hdc = 0; + if (!gl.wglDestroyPbufferARB(buffer)) { + throw new GLException("Error destroying pbuffer: error code " + WGL.GetLastError()); + } + buffer = 0; + } + } + public void swapBuffers() throws GLException { // FIXME: do we need to do anything if the pbuffer is double-buffered? // For now, just grab the pixels for the render-to-texture support. diff --git a/src/net/java/games/jogl/impl/x11/X11GLContext.java b/src/net/java/games/jogl/impl/x11/X11GLContext.java index 359d0c95c..4709691f8 100644 --- a/src/net/java/games/jogl/impl/x11/X11GLContext.java +++ b/src/net/java/games/jogl/impl/x11/X11GLContext.java @@ -40,6 +40,7 @@ package net.java.games.jogl.impl.x11; import java.awt.Component; +import java.security.*; import java.util.*; import net.java.games.gluegen.runtime.*; // for PROCADDRESS_VAR_PREFIX import net.java.games.jogl.*; @@ -64,11 +65,24 @@ public abstract class X11GLContext extends GLContext { // so that we can implement displayImpl() (which must be done when // the context is not current) protected long mostRecentDisplay; + // There is currently a bug on Linux/AMD64 distributions in glXGetProcAddressARB + protected static boolean isLinuxAMD64; static { functionNameMap = new HashMap(); functionNameMap.put("glAllocateMemoryNV", "glXAllocateMemoryNV"); functionNameMap.put("glFreeMemoryNV", "glXFreeMemoryNV"); + + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + String os = System.getProperty("os.name").toLowerCase(); + String arch = System.getProperty("os.arch").toLowerCase(); + if (os.startsWith("linux") && arch.equals("amd64")) { + isLinuxAMD64 = true; + } + return null; + } + }); } public X11GLContext(Component component, @@ -173,6 +187,7 @@ public abstract class X11GLContext extends GLContext { } protected void destroyImpl() throws GLException { + lockAWT(); if (context != 0) { GLX.glXDestroyContext(mostRecentDisplay, context); if (DEBUG) { @@ -180,12 +195,16 @@ public abstract class X11GLContext extends GLContext { } context = 0; } + unlockAWT(); } public abstract void swapBuffers() throws GLException; protected long dynamicLookupFunction(String glFuncName) { - long res = GLX.glXGetProcAddressARB(glFuncName); + long res = 0; + if (!isLinuxAMD64) { + res = GLX.glXGetProcAddressARB(glFuncName); + } if (res == 0) { // GLU routines aren't known to the OpenGL function lookup res = GLX.dlsym(glFuncName); @@ -218,6 +237,22 @@ public abstract class X11GLContext extends GLContext { if (!GLX.glXQueryVersion(display, major, minor)) { throw new GLException("glXQueryVersion failed"); } + if (DEBUG) { + System.err.println("!!! GLX version: major " + major[0] + + ", minor " + minor[0]); + } + + // Work around bugs in ATI's Linux drivers where they report they + // only implement GLX version 1.2 but actually do support pbuffers + if (major[0] == 1 && minor[0] == 2) { + GL gl = getGL(); + String str = gl.glGetString(GL.GL_VENDOR); + if (str != null && str.indexOf("ATI") >= 0) { + isGLX13 = true; + return; + } + } + isGLX13 = ((major[0] > 1) || (minor[0] > 2)); } @@ -235,11 +270,20 @@ public abstract class X11GLContext extends GLContext { throw new GLException("Context not current"); } if (!glXQueryExtensionsStringInitialized) { - glXQueryExtensionsStringAvailable = (GLX.glXGetProcAddressARB("glXQueryExtensionsString") != 0); + glXQueryExtensionsStringAvailable = (dynamicLookupFunction("glXQueryExtensionsString") != 0); glXQueryExtensionsStringInitialized = true; } if (glXQueryExtensionsStringAvailable) { - return GLX.glXQueryExtensionsString(display, GLX.DefaultScreen(display)); + lockAWT(); + try { + String ret = GLX.glXQueryExtensionsString(display, GLX.DefaultScreen(display)); + if (DEBUG) { + System.err.println("!!! GLX extensions: " + ret); + } + return ret; + } finally { + unlockAWT(); + } } else { return ""; } @@ -364,4 +408,14 @@ public abstract class X11GLContext extends GLContext { protected long getContext() { return context; } + + // These synchronization primitives prevent the AWT from making + // requests from the X server asynchronously to this code. + protected void lockAWT() { + getJAWT().Lock(); + } + + protected void unlockAWT() { + getJAWT().Unlock(); + } } diff --git a/src/net/java/games/jogl/impl/x11/X11OffscreenGLContext.java b/src/net/java/games/jogl/impl/x11/X11OffscreenGLContext.java index 2aa85e9f8..a22367091 100644 --- a/src/net/java/games/jogl/impl/x11/X11OffscreenGLContext.java +++ b/src/net/java/games/jogl/impl/x11/X11OffscreenGLContext.java @@ -44,7 +44,7 @@ import net.java.games.jogl.*; import net.java.games.jogl.impl.*; public class X11OffscreenGLContext extends X11GLContext { - private int pixmap; + private long pixmap; private boolean isDoubleBuffered; // Width and height of the underlying bitmap private int width; diff --git a/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java b/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java index 5ab976870..5b6df1843 100644 --- a/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java +++ b/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java @@ -43,7 +43,7 @@ import net.java.games.jogl.*; import net.java.games.jogl.impl.*; public class X11PbufferGLContext extends X11GLContext { - private static final boolean DEBUG = false; + private static final boolean DEBUG = Debug.debug("X11PbufferGLContext"); private int initWidth; private int initHeight; @@ -209,7 +209,7 @@ public class X11PbufferGLContext extends X11GLContext { iattributes[niattribs++] = 0; - int tmpBuffer = GLX.glXCreatePbuffer(display, fbConfig, iattributes); + long tmpBuffer = GLX.glXCreatePbuffer(display, fbConfig, iattributes); if (tmpBuffer == 0) { // FIXME: query X error code for detail error message throw new GLException("pbuffer creation error: glXCreatePbuffer() failed"); @@ -251,10 +251,7 @@ public class X11PbufferGLContext extends X11GLContext { created = true; } - // FIXME: this cast to int would be wrong on 64-bit platforms - // where the argument type to glXMakeCurrent would change (should - // probably make GLXDrawable, and maybe XID, Opaque as long) - if (!GLX.glXMakeContextCurrent(display, (int) buffer, (int) buffer, context)) { + if (!GLX.glXMakeContextCurrent(display, buffer, buffer, context)) { throw new GLException("Error making context current"); } @@ -318,6 +315,19 @@ public class X11PbufferGLContext extends X11GLContext { } } + protected void destroyImpl() throws GLException { + lockAWT(); + try { + if (context != 0) { + super.destroyImpl(); + GLX.glXDestroyPbuffer(display, buffer); + buffer = 0; + } + } finally { + unlockAWT(); + } + } + public void swapBuffers() throws GLException { // FIXME: do we need to do anything if the pbuffer is double-buffered? } @@ -329,15 +339,4 @@ public class X11PbufferGLContext extends X11GLContext { } return tmp[0]; } - - // These synchronization primitives, which prevent the AWT from - // making requests from the X server asynchronously to this code, - // are required for pbuffers to work properly on X11. - private void lockAWT() { - getJAWT().Lock(); - } - - private void unlockAWT() { - getJAWT().Unlock(); - } } |