aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/games
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/java/games')
-rw-r--r--src/net/java/games/gluegen/FunctionEmitter.java9
-rw-r--r--src/net/java/games/gluegen/GlueGen.java9
-rw-r--r--src/net/java/games/gluegen/JavaEmitter.java24
-rw-r--r--src/net/java/games/gluegen/JavaMethodBindingEmitter.java25
-rw-r--r--src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java16
-rw-r--r--src/net/java/games/gluegen/JavaType.java4
-rw-r--r--src/net/java/games/gluegen/MethodBinding.java130
-rw-r--r--src/net/java/games/gluegen/StructLayout.java6
-rw-r--r--src/net/java/games/gluegen/opengl/GLEmitter.java55
-rw-r--r--src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java1
-rw-r--r--src/net/java/games/jogl/Animator.java3
-rw-r--r--src/net/java/games/jogl/DefaultGLCapabilitiesChooser.java16
-rw-r--r--src/net/java/games/jogl/GLCanvas.java41
-rw-r--r--src/net/java/games/jogl/GLCapabilities.java8
-rw-r--r--src/net/java/games/jogl/GLDrawable.java4
-rw-r--r--src/net/java/games/jogl/GLJPanel.java547
-rw-r--r--src/net/java/games/jogl/GLPbuffer.java5
-rw-r--r--src/net/java/games/jogl/Version.java2
-rw-r--r--src/net/java/games/jogl/impl/FunctionAvailabilityCache.java6
-rw-r--r--src/net/java/games/jogl/impl/GLContext.java20
-rw-r--r--src/net/java/games/jogl/impl/GLPbufferImpl.java59
-rw-r--r--src/net/java/games/jogl/impl/NativeLibLoader.java61
-rwxr-xr-xsrc/net/java/games/jogl/impl/SingleThreadedWorkaround.java38
-rw-r--r--src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java1
-rw-r--r--src/net/java/games/jogl/impl/macosx/MacOSXPbufferGLContext.java7
-rw-r--r--src/net/java/games/jogl/impl/windows/WindowsGLContext.java114
-rw-r--r--src/net/java/games/jogl/impl/windows/WindowsGLContextFactory.java51
-rw-r--r--src/net/java/games/jogl/impl/windows/WindowsOffscreenGLContext.java3
-rw-r--r--src/net/java/games/jogl/impl/windows/WindowsOnscreenGLContext.java21
-rw-r--r--src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java94
-rw-r--r--src/net/java/games/jogl/impl/x11/X11GLContext.java60
-rw-r--r--src/net/java/games/jogl/impl/x11/X11OffscreenGLContext.java2
-rw-r--r--src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java33
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();
- }
}