diff options
Diffstat (limited to 'src/net/java/games')
33 files changed, 1110 insertions, 365 deletions
diff --git a/src/net/java/games/gluegen/FunctionEmitter.java b/src/net/java/games/gluegen/FunctionEmitter.java index 097d8d0be..9acde1211 100644 --- a/src/net/java/games/gluegen/FunctionEmitter.java +++ b/src/net/java/games/gluegen/FunctionEmitter.java @@ -59,6 +59,15 @@ public abstract class FunctionEmitter this.defaultOutput = defaultOutput; } + /** + * Makes this FunctionEmitter a copy of the passed one. + */ + public FunctionEmitter(FunctionEmitter arg) { + modifiers = (HashSet) arg.modifiers.clone(); + commentEmitter = arg.commentEmitter; + defaultOutput = arg.defaultOutput; + } + public PrintWriter getDefaultOutput() { return defaultOutput; } public void addModifiers(Iterator/*<EmissionModifier>*/ mi) diff --git a/src/net/java/games/gluegen/GlueGen.java b/src/net/java/games/gluegen/GlueGen.java index 2e5f3421a..6c343a5ee 100644 --- a/src/net/java/games/gluegen/GlueGen.java +++ b/src/net/java/games/gluegen/GlueGen.java @@ -141,7 +141,14 @@ public class GlueGen implements GlueEmitterControls { } HeaderParser headerParser = new HeaderParser(); - MachineDescription machDesc = new MachineDescription32Bit(); + MachineDescription machDesc; + String os = System.getProperty("os.name").toLowerCase(); + String cpu = System.getProperty("os.arch").toLowerCase(); + if (os.startsWith("linux") && cpu.equals("amd64")) { + machDesc = new MachineDescription64Bit(); + } else { + machDesc = new MachineDescription32Bit(); + } headerParser.setMachineDescription(machDesc); TypeDictionary td = new TypeDictionary(); headerParser.setTypedefDictionary(td); diff --git a/src/net/java/games/gluegen/JavaEmitter.java b/src/net/java/games/gluegen/JavaEmitter.java index 00887c333..318c2ad73 100644 --- a/src/net/java/games/gluegen/JavaEmitter.java +++ b/src/net/java/games/gluegen/JavaEmitter.java @@ -264,10 +264,8 @@ public class JavaEmitter implements GlueEmitter { continue; // don't generate bindings for this symbol } - Iterator allBindings = generateMethodBindingEmitters(cFunc); - while (allBindings.hasNext()) { - methodBindingEmitters.add(allBindings.next()); - } + List allBindings = generateMethodBindingEmitters(cFunc); + methodBindingEmitters.addAll(allBindings); } // Emit all the methods @@ -305,7 +303,7 @@ public class JavaEmitter implements GlueEmitter { * Generate all appropriate Java bindings for the specified C function * symbols. */ - protected Iterator generateMethodBindingEmitters(FunctionSymbol sym) throws Exception { + protected List generateMethodBindingEmitters(FunctionSymbol sym) throws Exception { ArrayList/*<FunctionEmitter>*/ allEmitters = new ArrayList(1); @@ -375,7 +373,7 @@ public class JavaEmitter implements GlueEmitter { if (cfg.allStatic()) { entryPoint.addModifier(JavaMethodBindingEmitter.STATIC); } - if (!isUnimplemented && !binding.needsBody()) { + if (!isUnimplemented && !bindingNeedsBody(binding)) { entryPoint.addModifier(JavaMethodBindingEmitter.NATIVE); } entryPoint.setReturnedArrayLengthExpression(cfg.returnedArrayLength(binding.getName())); @@ -395,8 +393,8 @@ public class JavaEmitter implements GlueEmitter { // If the user has stated that the function will be // manually implemented, then don't auto-generate a function body. if (!cfg.manuallyImplement(sym.getName()) && !isUnimplemented) { - if (binding.needsBody()) { - // Generate the method which calls the underlying function + if (bindingNeedsBody(binding)) { + // Generate the method which calls the underlying C function // after unboxing has occurred PrintWriter output = cfg.allStatic() ? javaWriter() : javaImplWriter(); JavaMethodBindingEmitter wrappedEntryPoint = @@ -422,7 +420,7 @@ public class JavaEmitter implements GlueEmitter { "Error while generating bindings for \"" + sym + "\"", e); } - return allEmitters.iterator(); + return allEmitters; } @@ -571,7 +569,7 @@ public class JavaEmitter implements GlueEmitter { JavaMethodBindingEmitter entryPoint = new JavaMethodBindingImplEmitter(binding, writer, cfg.runtimeExceptionType()); entryPoint.addModifier(JavaMethodBindingEmitter.PUBLIC); - if (!binding.needsBody() && !binding.hasContainingType()) { + if (!bindingNeedsBody(binding) && !binding.hasContainingType()) { entryPoint.addModifier(JavaMethodBindingEmitter.NATIVE); } entryPoint.emit(); @@ -676,6 +674,12 @@ public class JavaEmitter implements GlueEmitter { // Internals only below this point // + protected boolean bindingNeedsBody(MethodBinding binding) { + // We need to perform NIO checks and conversions and array length + // checks + return binding.signatureUsesNIO() || binding.signatureUsesCArrays(); + } + private CMethodBindingEmitter makeCEmitter(MethodBinding binding, boolean overloaded, boolean doingImplRoutine, diff --git a/src/net/java/games/gluegen/JavaMethodBindingEmitter.java b/src/net/java/games/gluegen/JavaMethodBindingEmitter.java index 131faae0d..e5b83a26c 100644 --- a/src/net/java/games/gluegen/JavaMethodBindingEmitter.java +++ b/src/net/java/games/gluegen/JavaMethodBindingEmitter.java @@ -66,7 +66,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter private String runtimeExceptionType; private MethodBinding binding; - private boolean forNIOBufferBaseRoutine; + private boolean forImplementingMethodCall; // A non-null value indicates that rather than returning a compound // type accessor we are returning an array of such accessors; this @@ -80,18 +80,26 @@ public class JavaMethodBindingEmitter extends FunctionEmitter this(binding, output, runtimeExceptionType, false); } - public JavaMethodBindingEmitter(MethodBinding binding, PrintWriter output, String runtimeExceptionType, boolean forNIOBufferBaseRoutine) + public JavaMethodBindingEmitter(MethodBinding binding, PrintWriter output, String runtimeExceptionType, boolean forImplementingMethodCall) { super(output); this.binding = binding; - this.forNIOBufferBaseRoutine = forNIOBufferBaseRoutine; + this.forImplementingMethodCall = forImplementingMethodCall; this.runtimeExceptionType = runtimeExceptionType; setCommentEmitter(defaultInterfaceCommentEmitter); } + public JavaMethodBindingEmitter(JavaMethodBindingEmitter arg) { + super(arg); + runtimeExceptionType = arg.runtimeExceptionType; + binding = arg.binding; + forImplementingMethodCall = arg.forImplementingMethodCall; + returnedArrayLengthExpression = arg.returnedArrayLengthExpression; + } + public final MethodBinding getBinding() { return binding; } - public final boolean isForNIOBufferBaseRoutine() { return forNIOBufferBaseRoutine; } + public boolean isForImplementingMethodCall() { return forImplementingMethodCall; } public String getName() { return binding.getName(); @@ -130,9 +138,10 @@ public class JavaMethodBindingEmitter extends FunctionEmitter protected void emitName(PrintWriter writer) { - writer.print(binding.getName()); - if (forNIOBufferBaseRoutine) { - writer.print("0"); + if (forImplementingMethodCall) { + writer.print(getImplMethodName()); + } else { + writer.print(getName()); } } @@ -141,7 +150,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter boolean needComma = false; int numEmitted = 0; - if (forNIOBufferBaseRoutine && binding.hasContainingType()) { + if (forImplementingMethodCall && binding.hasContainingType()) { // Always emit outgoing "this" argument writer.print("java.nio.Buffer "); writer.print(javaThisArgumentName()); diff --git a/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java b/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java index 2701bc2ec..dc3dfa629 100644 --- a/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java +++ b/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java @@ -65,6 +65,13 @@ public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter this.isUnimplemented = isUnimplemented; } + public JavaMethodBindingImplEmitter(JavaMethodBindingEmitter arg) { + super(arg); + if (arg instanceof JavaMethodBindingImplEmitter) { + this.isUnimplemented = ((JavaMethodBindingImplEmitter) arg).isUnimplemented; + } + } + protected void emitBody(PrintWriter writer) { MethodBinding binding = getBinding(); @@ -84,8 +91,15 @@ public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter } } + protected boolean isUnimplemented() { + return isUnimplemented; + } + protected boolean needsBody() { - return isUnimplemented || getBinding().needsBody() || getBinding().hasContainingType(); + return (isUnimplemented || + getBinding().signatureUsesNIO() || + getBinding().signatureUsesCArrays() || + getBinding().hasContainingType()); } protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { diff --git a/src/net/java/games/gluegen/JavaType.java b/src/net/java/games/gluegen/JavaType.java index 2d41a543d..b879187ef 100644 --- a/src/net/java/games/gluegen/JavaType.java +++ b/src/net/java/games/gluegen/JavaType.java @@ -325,6 +325,10 @@ public class JavaType { return ((clazz != null) && !isArray() && clazz.isPrimitive() && (clazz != Void.TYPE)); } + public boolean isPrimitiveArray() { + return (isArray() && (clazz.getComponentType().isPrimitive())); + } + public boolean isVoid() { return (clazz == Void.TYPE); } diff --git a/src/net/java/games/gluegen/MethodBinding.java b/src/net/java/games/gluegen/MethodBinding.java index fd2b7b7bc..b7117185d 100644 --- a/src/net/java/games/gluegen/MethodBinding.java +++ b/src/net/java/games/gluegen/MethodBinding.java @@ -52,8 +52,10 @@ public class MethodBinding { private FunctionSymbol sym; private JavaType javaReturnType; private List javaArgumentTypes; - private boolean computedNeedsBody; - private boolean needsBody; + private boolean computedSignatureProperties; + private boolean signatureUsesNIO; + private boolean signatureUsesCArrays; + private boolean signatureUsesPrimitiveArrays; private JavaType containingType; private Type containingCType; private int thisPointerIndex = -1; @@ -66,13 +68,15 @@ public class MethodBinding { public MethodBinding(MethodBinding bindingToCopy) { this.sym = bindingToCopy.sym; - this.containingType = bindingToCopy.containingType; - this.containingCType = bindingToCopy.containingCType; - this.javaReturnType = bindingToCopy.javaReturnType; - this.javaArgumentTypes = (List)((ArrayList)bindingToCopy.javaArgumentTypes).clone(); - this.computedNeedsBody = bindingToCopy.computedNeedsBody; - this.needsBody = bindingToCopy.needsBody; - this.thisPointerIndex = bindingToCopy.thisPointerIndex; + this.containingType = bindingToCopy.containingType; + this.containingCType = bindingToCopy.containingCType; + this.javaReturnType = bindingToCopy.javaReturnType; + this.javaArgumentTypes = (List)((ArrayList)bindingToCopy.javaArgumentTypes).clone(); + this.computedSignatureProperties = bindingToCopy.computedSignatureProperties; + this.signatureUsesNIO = bindingToCopy.signatureUsesNIO; + this.signatureUsesCArrays = bindingToCopy.signatureUsesCArrays; + this.signatureUsesPrimitiveArrays = bindingToCopy.signatureUsesPrimitiveArrays; + this.thisPointerIndex = bindingToCopy.thisPointerIndex; } /** Constructor for calling a C function. */ @@ -90,7 +94,7 @@ public class MethodBinding { public void setJavaReturnType(JavaType type) { javaReturnType = type; - computedNeedsBody = false; + computedSignatureProperties = false; } public void addJavaArgumentType(JavaType type) { @@ -98,7 +102,7 @@ public class MethodBinding { javaArgumentTypes = new ArrayList(); } javaArgumentTypes.add(type); - computedNeedsBody = false; + computedSignatureProperties = false; } public JavaType getJavaReturnType() { @@ -162,46 +166,86 @@ public class MethodBinding { } return binding; } + + /** + * Returns true if the return type or any of the outgoing arguments + * in the method's signature require conversion or checking due to + * the use of New I/O. + */ + public boolean signatureUsesNIO() { + computeSignatureProperties(); + return signatureUsesNIO; + } + + /** + * Returns true if any of the outgoing arguments in the method's + * signature represent fixed-length C arrays which require length + * checking during the call. + */ + public boolean signatureUsesCArrays() { + computeSignatureProperties(); + return signatureUsesCArrays; + } + + /** + * Returns true if any of the outgoing arguments in the method's + * signature represent primitive arrays which require a + * GetPrimitiveArrayCritical or similar operation during the call. + */ + public boolean signatureUsesPrimitiveArrays() { + computeSignatureProperties(); + return signatureUsesPrimitiveArrays; + } + /** - * Returns true if this method needs a special implementation to wrap and/or - * set the byte order of its arguments or return type (i.e., needs special - * pre-processing of the data passed to the native function, or - * post-processing of the data returned from the native function). <P> - * - * Returns false if this binding can be implemented via a one-to-one - * correspondence between a Java method and its native implementation. + * Computes summary information about the method's C and Java + * signatures. */ - public boolean needsBody() { - if (!computedNeedsBody) { - if (javaReturnType.isCompoundTypeWrapper() || - javaReturnType.isNIOByteBuffer() || - javaReturnType.isArrayOfCompoundTypeWrappers()) { - // Needs wrapping and/or setting of byte order (neither of - // which can be done easily from native code) - needsBody = true; - } else { - for (int i = 0; i < getNumArguments(); i++) { - JavaType javaArgType = getJavaArgumentType(i); - Type cArgType = getCArgumentType(i); - if (javaArgType.isCompoundTypeWrapper() || - javaArgType.isNIOBuffer() || - cArgType.isArray() || - javaArgType.isNIOBufferArray()) { - // Needs unwrapping of accessors, checking of array - // lengths, or checking of direct buffer property - needsBody = true; - break; - } - } + protected void computeSignatureProperties() { + if (computedSignatureProperties) + return; + + signatureUsesNIO = false; + signatureUsesCArrays = false; + signatureUsesPrimitiveArrays = false; + + if (javaReturnType.isCompoundTypeWrapper() || + javaReturnType.isNIOByteBuffer() || + javaReturnType.isArrayOfCompoundTypeWrappers()) { + // Needs wrapping and/or setting of byte order (neither of + // which can be done easily from native code) + signatureUsesNIO = true; + } + + for (int i = 0; i < getNumArguments(); i++) { + JavaType javaArgType = getJavaArgumentType(i); + Type cArgType = getCArgumentType(i); + if (javaArgType.isCompoundTypeWrapper() || + javaArgType.isNIOBuffer() || + javaArgType.isNIOBufferArray()) { + // Needs unwrapping of accessors or checking of direct + // buffer property + signatureUsesNIO = true; + } + + if (cArgType.isArray()) { + // Needs checking of array lengths + signatureUsesCArrays = true; + } + + if (javaArgType.isPrimitiveArray()) { + // Needs getPrimitiveArrayCritical or similar construct + // depending on native code calling convention + signatureUsesPrimitiveArrays = true; } - computedNeedsBody = true; } - return needsBody; + computedSignatureProperties = true; } + public MethodBinding createNIOBufferVariant() { - if (!needsBody()) { + if (!signatureUsesNIO()) { return this; } MethodBinding binding = new MethodBinding(sym, containingType, containingCType); diff --git a/src/net/java/games/gluegen/StructLayout.java b/src/net/java/games/gluegen/StructLayout.java index 4f5b76f1c..ebbace6b5 100644 --- a/src/net/java/games/gluegen/StructLayout.java +++ b/src/net/java/games/gluegen/StructLayout.java @@ -123,9 +123,11 @@ public class StructLayout { String cpu = System.getProperty("os.arch").toLowerCase(); if ((os.startsWith("windows") && cpu.equals("x86")) || (os.startsWith("linux") && cpu.equals("i386")) || + (os.startsWith("linux") && cpu.equals("amd64")) || (os.startsWith("sunos") && cpu.equals("sparc")) || - (os.startsWith("sunos") && cpu.equals("x86"))|| - (os.startsWith("mac os") && cpu.equals("ppc")) + (os.startsWith("sunos") && cpu.equals("x86")) || + (os.startsWith("mac os") && cpu.equals("ppc")) || + (os.startsWith("freebsd") && cpu.equals("i386")) ) { // FIXME: make struct alignment configurable? May need to change // packing rules on a per-type basis? diff --git a/src/net/java/games/gluegen/opengl/GLEmitter.java b/src/net/java/games/gluegen/opengl/GLEmitter.java index 861e85bb7..d30f145ab 100644 --- a/src/net/java/games/gluegen/opengl/GLEmitter.java +++ b/src/net/java/games/gluegen/opengl/GLEmitter.java @@ -93,14 +93,27 @@ public class GLEmitter extends JavaEmitter return new GLConfiguration(); } - protected Iterator generateMethodBindingEmitters(FunctionSymbol sym) throws Exception + protected List generateMethodBindingEmitters(FunctionSymbol sym) throws Exception { - Iterator defaultEmitters = super.generateMethodBindingEmitters(sym); + return generateMethodBindingEmittersImpl(sym); + } + + protected List generateMethodBindingEmitters(FunctionSymbol sym, boolean skipProcessing) throws Exception { + if (skipProcessing) { + return super.generateMethodBindingEmitters(sym); + } else { + return generateMethodBindingEmittersImpl(sym); + } + } + + private List generateMethodBindingEmittersImpl(FunctionSymbol sym) throws Exception + { + List defaultEmitters = super.generateMethodBindingEmitters(sym); // if the superclass didn't generate any bindings for the symbol, let's // honor that (for example, the superclass might have caught an Ignore // direction that matched the symbol's name). - if (!defaultEmitters.hasNext()) + if (defaultEmitters.isEmpty()) { return defaultEmitters; } @@ -121,9 +134,9 @@ public class GLEmitter extends JavaEmitter emitGLProcAddressTableEntryForSymbol(sym); } - while (defaultEmitters.hasNext()) + for (Iterator iter = defaultEmitters.iterator(); iter.hasNext(); ) { - FunctionEmitter emitter = (FunctionEmitter)defaultEmitters.next(); + FunctionEmitter emitter = (FunctionEmitter) iter.next(); if (emitter instanceof JavaMethodBindingEmitter) { JavaMethodBindingEmitter newEmitter = @@ -144,7 +157,7 @@ public class GLEmitter extends JavaEmitter } } - return modifiedEmitters.iterator(); + return modifiedEmitters; } /** @@ -168,7 +181,7 @@ public class GLEmitter extends JavaEmitter // Internals only below this point // - private JavaMethodBindingEmitter generateModifiedEmitter(JavaMethodBindingEmitter baseJavaEmitter) + protected JavaMethodBindingEmitter generateModifiedEmitter(JavaMethodBindingEmitter baseJavaEmitter) { if (!(baseJavaEmitter instanceof JavaMethodBindingImplEmitter)) { // We only want to wrap the native entry point in the implementation @@ -178,7 +191,7 @@ public class GLEmitter extends JavaEmitter // it needs argument conversion or similar, filter that out since we will // be providing such an emitter ourselves. Otherwise return the emitter // unmodified. - if (baseJavaEmitter.isForNIOBufferBaseRoutine()) + if (baseJavaEmitter.isForImplementingMethodCall()) return null; return baseJavaEmitter; } @@ -189,7 +202,7 @@ public class GLEmitter extends JavaEmitter return new JavaGLPAWrapperEmitter(baseJavaEmitter, getGLConfig().getProcAddressTableExpr()); } - private CMethodBindingEmitter generateModifiedEmitter(CMethodBindingEmitter baseCEmitter) + protected CMethodBindingEmitter generateModifiedEmitter(CMethodBindingEmitter baseCEmitter) { // The C-side JNI binding for this particular function will have an // extra final argument, which is the address (the OpenGL procedure @@ -202,7 +215,7 @@ public class GLEmitter extends JavaEmitter return res; } - private boolean needsProcAddressWrapper(FunctionSymbol sym) + protected boolean needsProcAddressWrapper(FunctionSymbol sym) { String symName = sym.getName(); @@ -275,6 +288,10 @@ public class GLEmitter extends JavaEmitter tableWriter.println("public class " + tableClassName); tableWriter.println("{"); numProcAddressEntries = 0; + + for (Iterator iter = getGLConfig().getForceProcAddressGen().iterator(); iter.hasNext(); ) { + emitGLProcAddressTableEntryForString((String) iter.next()); + } } private void endGLProcAddressTable() throws Exception @@ -316,16 +333,21 @@ public class GLEmitter extends JavaEmitter w.close(); } - private void emitGLProcAddressTableEntryForSymbol(FunctionSymbol cFunc) + protected void emitGLProcAddressTableEntryForSymbol(FunctionSymbol cFunc) + { + emitGLProcAddressTableEntryForString(cFunc.getName()); + } + + protected void emitGLProcAddressTableEntryForString(String str) { tableWriter.print(" public long "); tableWriter.print(PROCADDRESS_VAR_PREFIX); - tableWriter.print(cFunc.getName()); + tableWriter.print(str); tableWriter.println(";"); ++numProcAddressEntries; } - private GLConfiguration getGLConfig() { + protected GLConfiguration getGLConfig() { return (GLConfiguration) getConfig(); } @@ -335,6 +357,7 @@ public class GLEmitter extends JavaEmitter private String tableClassPackage; private String tableClassName = "ProcAddressTable"; private Set/*<String>*/ skipProcAddressGen = new HashSet(); + private List/*<String>*/ forceProcAddressGen = new ArrayList(); private String contextVariableName = "context"; private String defaultGetProcAddressTableExpr = ".getGLProcAddressTable()"; private String getProcAddressTableExpr; @@ -358,6 +381,11 @@ public class GLEmitter extends JavaEmitter String sym = readString("SkipProcAddressGen", tok, filename, lineNo); skipProcAddressGen.add(sym); } + else if (cmd.equalsIgnoreCase("ForceProcAddressGen")) + { + String sym = readString("ForceProcAddressGen", tok, filename, lineNo); + forceProcAddressGen.add(sym); + } else if (cmd.equalsIgnoreCase("ContextVariableName")) { contextVariableName = readString("ContextVariableName", tok, filename, lineNo); @@ -386,6 +414,7 @@ public class GLEmitter extends JavaEmitter public String tableClassPackage() { return tableClassPackage; } public String tableClassName() { return tableClassName; } public boolean skipProcAddressGen (String name) { return skipProcAddressGen.contains(name); } + public List getForceProcAddressGen() { return forceProcAddressGen; } public String contextVariableName() { return contextVariableName; } public String getProcAddressTableExpr() { if (getProcAddressTableExpr == null) { diff --git a/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java b/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java index 4fb50ea21..1bb06b791 100644 --- a/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java +++ b/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java @@ -151,7 +151,6 @@ public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter protected void emitPreCallSetup(MethodBinding binding, PrintWriter writer) { super.emitPreCallSetup(binding, writer); - JavaType returnType = binding.getJavaReturnType(); MethodBinding wrappedBinding = emitterBeingWrapped.getBinding(); String procAddressVariable = diff --git a/src/net/java/games/jogl/Animator.java b/src/net/java/games/jogl/Animator.java index 3f5bfdeb2..4c50b9362 100644 --- a/src/net/java/games/jogl/Animator.java +++ b/src/net/java/games/jogl/Animator.java @@ -145,8 +145,7 @@ public class Animator { // 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())) { + if ((Thread.currentThread() == thread) || EventQueue.isDispatchThread()) { return; } while (shouldStop && thread != null) { diff --git a/src/net/java/games/jogl/DefaultGLCapabilitiesChooser.java b/src/net/java/games/jogl/DefaultGLCapabilitiesChooser.java index 8d5ecabc9..9cac4f83a 100644 --- a/src/net/java/games/jogl/DefaultGLCapabilitiesChooser.java +++ b/src/net/java/games/jogl/DefaultGLCapabilitiesChooser.java @@ -39,6 +39,8 @@ package net.java.games.jogl; +import net.java.games.jogl.impl.Debug; + /** <P> The default implementation of the {@link GLCapabilitiesChooser} interface, which provides consistent visual selection behavior across platforms. The precise algorithm is @@ -78,11 +80,17 @@ package net.java.games.jogl; */ public class DefaultGLCapabilitiesChooser implements GLCapabilitiesChooser { - private static final boolean DEBUG = false; + private static final boolean DEBUG = Debug.debug("DefaultGLCapabilitiesChooser"); public int chooseCapabilities(GLCapabilities desired, GLCapabilities[] available, int windowSystemRecommendedChoice) { + if (DEBUG) { + for (int i = 0; i < available.length; i++) { + System.err.println("Available " + i + ": " + available[i]); + } + } + if (windowSystemRecommendedChoice >= 0 && windowSystemRecommendedChoice < available.length && available[windowSystemRecommendedChoice] != null) { @@ -93,12 +101,6 @@ public class DefaultGLCapabilitiesChooser implements GLCapabilitiesChooser { return windowSystemRecommendedChoice; } - if (DEBUG) { - for (int i = 0; i < available.length; i++) { - System.err.println("Available " + i + ": " + available[i]); - } - } - // Create score array int[] scores = new int[available.length]; int NO_SCORE = -9999999; diff --git a/src/net/java/games/jogl/GLCanvas.java b/src/net/java/games/jogl/GLCanvas.java index 1deac7f32..17aed5bea 100644 --- a/src/net/java/games/jogl/GLCanvas.java +++ b/src/net/java/games/jogl/GLCanvas.java @@ -72,7 +72,9 @@ public final class GLCanvas extends Canvas implements GLDrawable { } public void display() { - displayImpl(); + maybeDoSingleThreadedWorkaround(displayOnEventDispatchThreadAction, + displayAction, + false); } /** Overridden from Canvas; calls {@link #display}. Should not be @@ -109,12 +111,18 @@ public final class GLCanvas extends Canvas implements GLDrawable { final int fy = 0; final int fwidth = width; final int fheight = height; - context.invokeGL(new Runnable() { + final Runnable reshapeRunnable = new Runnable() { public void run() { getGL().glViewport(fx, fy, fwidth, fheight); drawableHelper.reshape(GLCanvas.this, fx, fy, fwidth, fheight); } - }, true, initAction); + }; + final Runnable reshapeOnEDTRunnable = new Runnable() { + public void run() { + context.invokeGL(reshapeRunnable, true, initAction); + } + }; + maybeDoSingleThreadedWorkaround(reshapeOnEDTRunnable, reshapeRunnable, true); } /** Overridden from Canvas to prevent Java2D's clearing of the @@ -176,7 +184,7 @@ public final class GLCanvas extends Canvas implements GLDrawable { } public void swapBuffers() { - context.invokeGL(swapBuffersAction, false, initAction); + maybeDoSingleThreadedWorkaround(swapBuffersOnEventDispatchThreadAction, swapBuffersAction, false); } public boolean canCreateOffscreenDrawable() { @@ -197,15 +205,27 @@ public final class GLCanvas extends Canvas implements GLDrawable { // Internals only below this point // - private void displayImpl() { + private void maybeDoSingleThreadedWorkaround(Runnable eventDispatchThreadAction, + Runnable invokeGLAction, + boolean isReshape) { if (SingleThreadedWorkaround.doWorkaround() && !EventQueue.isDispatchThread()) { try { - EventQueue.invokeAndWait(displayOnEventDispatchThreadAction); + // 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(displayAction, false, initAction); + context.invokeGL(invokeGLAction, isReshape, initAction); } } @@ -240,4 +260,11 @@ public final class GLCanvas extends Canvas implements GLDrawable { } 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/GLCapabilities.java b/src/net/java/games/jogl/GLCapabilities.java index 5095c802a..794f152f6 100644 --- a/src/net/java/games/jogl/GLCapabilities.java +++ b/src/net/java/games/jogl/GLCapabilities.java @@ -75,8 +75,12 @@ public class GLCapabilities implements Cloneable { */ public GLCapabilities() {} - public Object clone() throws CloneNotSupportedException { - return super.clone(); + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new GLException(e); + } } /** Indicates whether double-buffering is enabled. */ diff --git a/src/net/java/games/jogl/GLDrawable.java b/src/net/java/games/jogl/GLDrawable.java index aadc2b4fc..7c671c0b2 100644 --- a/src/net/java/games/jogl/GLDrawable.java +++ b/src/net/java/games/jogl/GLDrawable.java @@ -190,7 +190,9 @@ public interface GLDrawable extends ComponentEvents { /** Indicates whether this drawable is capable of fabricating a subordinate offscreen drawable for advanced rendering techniques - which require offscreen hardware-accelerated surfaces. */ + which require offscreen hardware-accelerated surfaces. Note that + this method is only guaranteed to return a correct result once + your GLEventListener's init() method has been called. */ public boolean canCreateOffscreenDrawable(); /** Creates a subordinate offscreen drawable (pbuffer) for this diff --git a/src/net/java/games/jogl/GLJPanel.java b/src/net/java/games/jogl/GLJPanel.java index 3ac5c984c..425160494 100644 --- a/src/net/java/games/jogl/GLJPanel.java +++ b/src/net/java/games/jogl/GLJPanel.java @@ -40,12 +40,15 @@ package net.java.games.jogl; import java.awt.Component; +import java.awt.EventQueue; +import java.awt.Frame; import java.awt.Graphics; import java.awt.GraphicsConfiguration; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; +import java.security.*; import javax.swing.JComponent; import javax.swing.JPanel; import net.java.games.jogl.impl.*; @@ -57,28 +60,47 @@ import net.java.games.jogl.impl.*; /** 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. Currently implemented using - offscreen (i.e., non-hardware accelerated) rendering, so - performance will likely be poor. This class can not be + Z-ordering or LayoutManager problems. This component attempts to + use hardware-accelerated rendering via pbuffers and falls back on + to software rendering if problems occur. This class can not be instantiated directly; use {@link GLDrawableFactory} to construct them. */ public final class GLJPanel extends JPanel implements GLDrawable { private GLDrawableHelper drawableHelper = new GLDrawableHelper(); - private GLContext context; - private BufferedImage offscreenImage; - private int awtFormat; - private int glFormat; - private int glType; - private int glComps; + + // Data used for either pbuffers or pixmap-based offscreen surfaces + private GLCapabilities offscreenCaps; + private GLCapabilitiesChooser chooser; + private GLDrawable shareWith; + private BufferedImage offscreenImage; + private int neededOffscreenImageWidth; + private int neededOffscreenImageHeight; private DataBufferByte dbByte; private DataBufferInt dbInt; private Object semaphore = new Object(); - private boolean repaintDone; + private int panelWidth = 0; + private int panelHeight = 0; + private Updater updater; + private int awtFormat; + private int glFormat; + private int glType; + + // Implementation using pbuffers + private static boolean hardwareAccelerationDisabled = + Debug.isPropertyDefined("jogl.gljpanel.nohw"); + private boolean pbufferInitializationCompleted; + private GLPbuffer pbuffer; + private int pbufferWidth = 256; + private int pbufferHeight = 256; + private GLCanvas heavyweight; + private Frame toplevel; + + // Implementation using software rendering + private GLContext offscreenContext; // For saving/restoring of OpenGL state during ReadPixels private int[] swapbytes = new int[1]; - private int[] lsbfirst = new int[1]; private int[] rowlength = new int[1]; private int[] skiprows = new int[1]; private int[] skippixels = new int[1]; @@ -86,31 +108,76 @@ public final class GLJPanel extends JPanel implements GLDrawable { GLJPanel(GLCapabilities capabilities, GLCapabilitiesChooser chooser, GLDrawable shareWith) { super(); - context = GLContextFactory.getFactory().createGLContext(null, capabilities, chooser, - GLContextHelper.getContext(shareWith)); + + // Works around problems on many vendors' cards; we don't need a + // back buffer for the offscreen surface anyway + offscreenCaps = (GLCapabilities) capabilities.clone(); + offscreenCaps.setDoubleBuffered(false); + this.chooser = chooser; + this.shareWith = shareWith; + + initialize(); } public void display() { - // Multithreaded redrawing of Swing components is not allowed - try { - synchronized(semaphore) { - repaintDone = false; - repaint(); - while (!repaintDone) { - semaphore.wait(); + 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 { + // Wait a reasonable period of time for the repaint to + // complete, so that we don't swamp the AWT event queue thread + // with repaint requests. We used to have an explicit flag to + // detect when the repaint completed; unfortunately, under + // some circumstances, the top-level window can be torn down + // while we're waiting for the repaint to complete, which will + // never happen. It doesn't look like there's enough + // information in the EventQueue to figure out whether there + // are pending events without posting to the queue, which we + // don't want to do during shutdown, and adding a + // HierarchyListener and watching for displayability events + // might be fragile since we don't know exactly how this + // component will be used in users' applications. For these + // reasons we simply wait up to a brief period of time for the + // repaint to complete. + synchronized(semaphore) { + repaint(); + semaphore.wait(100); } + } catch (InterruptedException e) { } - } catch (InterruptedException e) { } } - /** Overridden from JComponent; calls {@link #display}. Should not - be invoked by applications directly. */ + /** Overridden from JComponent; calls {@link + GLEventListener#display}. Should not be invoked by applications + directly. */ public void paintComponent(Graphics g) { - displayAction.setGraphics(g); - context.invokeGL(displayAction, false, initAction); + updater.setGraphics(g); + if (!hardwareAccelerationDisabled) { + if (!pbufferInitializationCompleted) { + try { + heavyweight.display(); + pbuffer.display(); + } catch (GLException e) { + // We consider any exception thrown during updating of the + // heavyweight or pbuffer during the initialization phases + // to be an indication that there was a problem + // instantiating the pbuffer, regardless of whether the + // exception originated in the user's GLEventListener. In + // these cases we immediately back off and use software + // rendering. + disableHardwareRendering(); + } + } else { + pbuffer.display(); + } + } else { + offscreenContext.invokeGL(displayAction, false, initAction); + } synchronized(semaphore) { - repaintDone = true; semaphore.notifyAll(); } } @@ -121,25 +188,74 @@ public final class GLJPanel extends JPanel implements GLDrawable { directly. */ public void reshape(int x, int y, int width, int height) { super.reshape(x, y, width, height); - // NOTE: we don't pay attention to the x and y provided since we - // are blitting into this component directly - final int fx = 0; - final int fy = 0; - final int fwidth = width; + + // Move all reshape requests onto AWT EventQueue thread + final int fx = x; + final int fy = y; + final int fwidth = width; final int fheight = height; - context.resizeOffscreenContext(width, height); - context.invokeGL(new Runnable() { + + Runnable r = new Runnable() { public void run() { - getGL().glViewport(fx, fy, fwidth, fheight); - drawableHelper.reshape(GLJPanel.this, fx, fy, fwidth, fheight); + GLContext context = null; + neededOffscreenImageWidth = 0; + neededOffscreenImageHeight = 0; + + if (!hardwareAccelerationDisabled) { + if (fwidth > pbufferWidth || fheight > pbufferHeight) { + // Must destroy and recreate pbuffer to fit + pbuffer.destroy(); + if (fwidth > pbufferWidth) { + pbufferWidth = getNextPowerOf2(fwidth); + } + if (fheight > pbufferHeight) { + pbufferHeight = getNextPowerOf2(fheight); + } + initialize(); + } + GLPbufferImpl pbufferImpl = (GLPbufferImpl) pbuffer; + context = pbufferImpl.getContext(); + // 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... + neededOffscreenImageWidth = pbufferWidth; + neededOffscreenImageHeight = fheight; + } else { + offscreenContext.resizeOffscreenContext(fwidth, fheight); + context = offscreenContext; + neededOffscreenImageWidth = fwidth; + neededOffscreenImageHeight = fheight; + } + if (offscreenImage != null && - (offscreenImage.getWidth() != context.getOffscreenContextWidth() || - offscreenImage.getHeight() != context.getOffscreenContextHeight())) { + (offscreenImage.getWidth() != neededOffscreenImageWidth || + offscreenImage.getHeight() != neededOffscreenImageHeight)) { offscreenImage.flush(); offscreenImage = null; } + + panelWidth = fwidth; + panelHeight = fheight; + + context.invokeGL(new Runnable() { + public void run() { + getGL().glViewport(0, 0, panelWidth, panelHeight); + drawableHelper.reshape(GLJPanel.this, 0, 0, panelWidth, panelHeight); + } + }, true, initAction); } - }, true, initAction); + }; + if (EventQueue.isDispatchThread()) { + r.run(); + } else { + // Avoid blocking EventQueue thread due to possible deadlocks + // during component creation + EventQueue.invokeLater(r); + } } public void addGLEventListener(GLEventListener listener) { @@ -151,19 +267,35 @@ public final class GLJPanel extends JPanel implements GLDrawable { } public GL getGL() { - return context.getGL(); + if (!hardwareAccelerationDisabled) { + return pbuffer.getGL(); + } else { + return offscreenContext.getGL(); + } } public void setGL(GL gl) { - context.setGL(gl); + if (!hardwareAccelerationDisabled) { + pbuffer.setGL(gl); + } else { + offscreenContext.setGL(gl); + } } public GLU getGLU() { - return context.getGLU(); + if (!hardwareAccelerationDisabled) { + return pbuffer.getGLU(); + } else { + return offscreenContext.getGLU(); + } } public void setGLU(GLU glu) { - context.setGLU(glu); + if (!hardwareAccelerationDisabled) { + pbuffer.setGLU(glu); + } else { + offscreenContext.setGLU(glu); + } } public void setRenderingThread(Thread currentThreadOrNull) throws GLException { @@ -172,7 +304,7 @@ public final class GLJPanel extends JPanel implements GLDrawable { } public Thread getRenderingThread() { - return context.getRenderingThread(); + return null; } public void setNoAutoRedrawMode(boolean noAutoRedraws) { @@ -183,21 +315,32 @@ public final class GLJPanel extends JPanel implements GLDrawable { } public void setAutoSwapBufferMode(boolean onOrOff) { - context.setAutoSwapBufferMode(onOrOff); + if (!hardwareAccelerationDisabled) { + pbuffer.setAutoSwapBufferMode(onOrOff); + } else { + offscreenContext.setAutoSwapBufferMode(onOrOff); + } } public boolean getAutoSwapBufferMode() { - return context.getAutoSwapBufferMode(); + if (!hardwareAccelerationDisabled) { + return pbuffer.getAutoSwapBufferMode(); + } else { + return offscreenContext.getAutoSwapBufferMode(); + } } public void swapBuffers() { - context.invokeGL(swapBuffersAction, false, initAction); + if (!hardwareAccelerationDisabled) { + pbuffer.swapBuffers(); + } else { + offscreenContext.invokeGL(swapBuffersAction, false, initAction); + } } public boolean canCreateOffscreenDrawable() { - // For now let's say no; maybe we can reimplement this class in - // terms of pbuffers (though not all vendors support them, and - // they seem to require an onscreen context) + // For now let's say no, although we could using the heavyweight + // if hardware acceleration is still enabled return false; } @@ -208,119 +351,259 @@ public final class GLJPanel extends JPanel implements GLDrawable { } GLContext getContext() { - return context; + if (!hardwareAccelerationDisabled) { + return ((GLPbufferImpl) pbuffer).getContext(); + } else { + return offscreenContext; + } } //---------------------------------------------------------------------- // Internals only below this point // - class InitAction implements Runnable { - public void run() { - drawableHelper.init(GLJPanel.this); + private void disableHardwareRendering() { + if (Debug.verbose()) { + System.err.println("GLJPanel: Falling back on software rendering due to pbuffer problems"); } + hardwareAccelerationDisabled = true; + pbufferInitializationCompleted = false; + EventQueue.invokeLater(new Runnable() { + public void run() { + toplevel.setVisible(false); + // Should dispose of this -- not sure about stability on + // various cards -- should test (FIXME) + // toplevel.dispose(); + } + }); + initialize(); } - private InitAction initAction = new InitAction(); - - class DisplayAction implements Runnable { + + private void initialize() { + // Initialize either the hardware-accelerated rendering path or + // the lightweight rendering path + if (!hardwareAccelerationDisabled) { + boolean firstTime = false; + if (heavyweight == null) { + // Make the heavyweight share with the "shareWith" parameter. + // The pbuffer shares textures and display lists with the + // heavyweight, so by transitivity the pbuffer will share with + // it as well. + heavyweight = GLDrawableFactory.getFactory().createGLCanvas(new GLCapabilities(), shareWith); + firstTime = true; + } + if (heavyweight.canCreateOffscreenDrawable()) { + if (firstTime) { + toplevel = new Frame(); + toplevel.setUndecorated(true); + } + pbuffer = heavyweight.createOffscreenDrawable(offscreenCaps, pbufferWidth, pbufferHeight); + updater = new Updater(); + pbuffer.addGLEventListener(updater); + pbufferInitializationCompleted = false; + if (firstTime) { + toplevel.add(heavyweight); + toplevel.setSize(0, 0); + } + EventQueue.invokeLater(new Runnable() { + public void run() { + try { + toplevel.setVisible(true); + } catch (GLException e) { + disableHardwareRendering(); + } + } + }); + return; + } else { + // If the heavyweight reports that it can't create an + // offscreen drawable (pbuffer), don't try again the next + // time, and fall through to the software rendering path + hardwareAccelerationDisabled = true; + } + } + + // Create an offscreen context instead + offscreenContext = GLContextFactory.getFactory().createGLContext(null, offscreenCaps, chooser, + GLContextHelper.getContext(shareWith)); + offscreenContext.resizeOffscreenContext(panelWidth, panelHeight); + updater = new Updater(); + if (panelWidth > 0 && panelHeight > 0) { + offscreenContext.invokeGL(new Runnable() { + public void run() { + getGL().glViewport(0, 0, panelWidth, panelHeight); + drawableHelper.reshape(GLJPanel.this, 0, 0, panelWidth, panelHeight); + } + }, true, initAction); + } + } + + class Updater implements GLEventListener { private Graphics g; public void setGraphics(Graphics g) { this.g = g; } - public void run() { + public void init(GLDrawable drawable) { + if (!hardwareAccelerationDisabled) { + pbufferInitializationCompleted = true; + EventQueue.invokeLater(new Runnable() { + public void run() { + toplevel.setVisible(false); + } + }); + } + drawableHelper.init(GLJPanel.this); + } + + public void display(GLDrawable drawable) { drawableHelper.display(GLJPanel.this); + // Must now copy pixels from offscreen context into surface if (offscreenImage == null) { - int awtFormat = context.getOffscreenContextBufferedImageType(); - offscreenImage = new BufferedImage(context.getOffscreenContextWidth(), context.getOffscreenContextHeight(), awtFormat); - switch (awtFormat) { - case BufferedImage.TYPE_3BYTE_BGR: - glFormat = GL.GL_BGR; - glType = GL.GL_UNSIGNED_BYTE; - glComps = 3; - dbByte = (DataBufferByte) offscreenImage.getRaster().getDataBuffer(); - break; - - case BufferedImage.TYPE_INT_RGB: - glFormat = GL.GL_BGRA; - glType = GL.GL_UNSIGNED_BYTE; - glComps = 4; - dbInt = (DataBufferInt) offscreenImage.getRaster().getDataBuffer(); - break; - - case BufferedImage.TYPE_INT_ARGB: - glFormat = GL.GL_BGRA; - glType = context.getOffscreenContextPixelDataType(); - glComps = 4; - dbInt = (DataBufferInt) offscreenImage.getRaster().getDataBuffer(); - 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); + 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; + int hwGLFormat = 0; + if (!hardwareAccelerationDisabled) { + // Should be more flexible in these BufferedImage formats; + // perhaps see what the preferred image types are on the + // given platform + if (offscreenCaps.getAlphaBits() > 0) { + awtFormat = BufferedImage.TYPE_INT_ARGB; + } else { + awtFormat = BufferedImage.TYPE_INT_RGB; + } + + // This seems to be a good choice on all platforms + hwGLFormat = GL.GL_UNSIGNED_INT_8_8_8_8_REV; + } else { + awtFormat = offscreenContext.getOffscreenContextBufferedImageType(); + } + + offscreenImage = new BufferedImage(neededOffscreenImageWidth, + neededOffscreenImageHeight, + awtFormat); + switch (awtFormat) { + case BufferedImage.TYPE_3BYTE_BGR: + glFormat = GL.GL_BGR; + glType = GL.GL_UNSIGNED_BYTE; + dbByte = (DataBufferByte) offscreenImage.getRaster().getDataBuffer(); + break; + + case BufferedImage.TYPE_INT_RGB: + case BufferedImage.TYPE_INT_ARGB: + glFormat = GL.GL_BGRA; + glType = (hardwareAccelerationDisabled + ? offscreenContext.getOffscreenContextPixelDataType() + : hwGLFormat); + dbInt = (DataBufferInt) offscreenImage.getRaster().getDataBuffer(); + 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); + } } } - GL gl = getGL(); - // Save current modes - gl.glGetIntegerv(GL.GL_PACK_SWAP_BYTES, swapbytes); - gl.glGetIntegerv(GL.GL_PACK_LSB_FIRST, lsbfirst); - gl.glGetIntegerv(GL.GL_PACK_ROW_LENGTH, rowlength); - gl.glGetIntegerv(GL.GL_PACK_SKIP_ROWS, skiprows); - gl.glGetIntegerv(GL.GL_PACK_SKIP_PIXELS, skippixels); - gl.glGetIntegerv(GL.GL_PACK_ALIGNMENT, alignment); - - // Little endian machines (DEC Alpha, Intel X86, PPC (in LSB - // mode)... for example) could benefit from setting - // GL_PACK_LSB_FIRST to GL_TRUE instead of GL_FALSE, but this - // would require changing the generated bitmaps too. - gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, GL.GL_FALSE); - gl.glPixelStorei(GL.GL_PACK_LSB_FIRST, GL.GL_TRUE); - gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, offscreenImage.getWidth()); - gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, 0); - gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, 0); - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1); - - // Actually read the pixels. - gl.glReadBuffer(context.getOffscreenContextReadBuffer()); - if (dbByte != null) { - gl.glReadPixels(0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), glFormat, glType, dbByte.getData()); - } else if (dbInt != null) { - gl.glReadPixels(0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), glFormat, glType, dbInt.getData()); - } + if (offscreenImage != null) { + GL gl = getGL(); + // Save current modes + gl.glGetIntegerv(GL.GL_PACK_SWAP_BYTES, swapbytes); + gl.glGetIntegerv(GL.GL_PACK_ROW_LENGTH, rowlength); + gl.glGetIntegerv(GL.GL_PACK_SKIP_ROWS, skiprows); + gl.glGetIntegerv(GL.GL_PACK_SKIP_PIXELS, skippixels); + gl.glGetIntegerv(GL.GL_PACK_ALIGNMENT, alignment); + + gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, GL.GL_FALSE); + gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, offscreenImage.getWidth()); + gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, 0); + gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, 0); + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1); + + // Actually read the pixels. + gl.glReadBuffer(GL.GL_FRONT); + if (dbByte != null) { + gl.glReadPixels(0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), glFormat, glType, dbByte.getData()); + } else if (dbInt != null) { + gl.glReadPixels(0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), glFormat, glType, dbInt.getData()); + } - // Restore saved modes. - gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, swapbytes[0]); - gl.glPixelStorei(GL.GL_PACK_LSB_FIRST, lsbfirst[0]); - gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, rowlength[0]); - gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, skiprows[0]); - gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, skippixels[0]); - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, alignment[0]); + // Restore saved modes. + gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, swapbytes[0]); + gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, rowlength[0]); + gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, skiprows[0]); + gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, skippixels[0]); + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, alignment[0]); - gl.glFlush(); - gl.glFinish(); - - if (context.offscreenImageNeedsVerticalFlip()) { - g.drawImage(offscreenImage, - 0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), - 0, offscreenImage.getHeight(), offscreenImage.getWidth(), 0, - GLJPanel.this); - } else { - g.drawImage(offscreenImage, 0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), GLJPanel.this); + if (!hardwareAccelerationDisabled || + offscreenContext.offscreenImageNeedsVerticalFlip()) { + // This performs reasonably well; the snippet below does not. + // Should figure out if we need to set the image scaling + // preference to FAST since it doesn't require subsampling + // of pixels -- FIXME + for (int i = 0; i < panelHeight - 1; i++) { + g.drawImage(offscreenImage, + 0, i, panelWidth, i+1, + 0, panelHeight - i - 2, panelWidth, panelHeight - i - 1, + GLJPanel.this); + } + } else { + g.drawImage(offscreenImage, 0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), GLJPanel.this); + } } } + + public void reshape(GLDrawable drawable, int x, int y, int width, int height) { + // This is handled above and dispatched directly to the appropriate context + } + + public void displayChanged(GLDrawable drawable, boolean modeChanged, boolean deviceChanged) { + } + } + + class InitAction implements Runnable { + public void run() { + updater.init(GLJPanel.this); + } + } + private InitAction initAction = new InitAction(); + + class DisplayAction implements Runnable { + public void run() { + updater.display(GLJPanel.this); + } } private DisplayAction displayAction = new DisplayAction(); + // This one is used exclusively in the non-hardware-accelerated case class SwapBuffersAction implements Runnable { public void run() { - context.swapBuffers(); + offscreenContext.swapBuffers(); } } private SwapBuffersAction swapBuffersAction = new SwapBuffersAction(); + + private int getNextPowerOf2(int number) { + if (((number-1) & number) == 0) { + //ex: 8 -> 0b1000; 8-1=7 -> 0b0111; 0b1000&0b0111 == 0 + return number; + } + int power = 0; + while (number > 0) { + number = number>>1; + power++; + } + return (1<<power); + } } diff --git a/src/net/java/games/jogl/GLPbuffer.java b/src/net/java/games/jogl/GLPbuffer.java index d41ca1bd6..fe2c19974 100644 --- a/src/net/java/games/jogl/GLPbuffer.java +++ b/src/net/java/games/jogl/GLPbuffer.java @@ -63,4 +63,9 @@ public interface GLPbuffer extends GLDrawable { /** Queries initialization status of this pBuffer. */ public boolean isInitialized(); + + /** Destroys the native resources associated with this pbuffer. It + is not valid to call display() or any other routines on this + pbuffer after it has been destroyed. */ + public void destroy(); } diff --git a/src/net/java/games/jogl/Version.java b/src/net/java/games/jogl/Version.java index 3a6e36e8d..a5e7f43eb 100644 --- a/src/net/java/games/jogl/Version.java +++ b/src/net/java/games/jogl/Version.java @@ -89,7 +89,7 @@ public final class Version { /** * Version string of this build. */ - private static final String version = "1.1.0-b08"; + private static final String version = "1.1.0-b10"; /** * Returns the verison string and build number of 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(); - } } |