diff options
author | Kenneth Russel <[email protected]> | 2003-07-03 00:09:30 +0000 |
---|---|---|
committer | Kenneth Russel <[email protected]> | 2003-07-03 00:09:30 +0000 |
commit | 93d272a0525ec57aa3cd584f15cde6cf2a637e0c (patch) | |
tree | 3d6c9f2a305539d26c7449566bbb0d72bf74b12c /src | |
parent | 37d90a0da397a1958fb101051170a60f871fb8cc (diff) |
Added pbuffer support to JOGL on X11; HWShadowmapsSimple and
ProceduralTexturePhysics demos are now working on Linux with the
appropriate hardware. Moved core GLX routines out of the public GLX
interface; this allowed XVisualInfo (and the new GLXFBConfig) to be
removed from the public API. Added support to GlueGen for returning
arrays of pointers as arrays of StructAccessors in Java and for
choosing the typedef name for a pointer-to-struct if the struct itself
does not have a typedef name. Added support to GLEmitter to emit
ProcAddressTables under arbitrary names and to support arbitrary
mechanisms for fetching those tables. Made GLU (on all platforms) and
GLX (on X11) be dynamically linked. Refactored ProcAddressTable
filling to be mostly shared code. Tested changes on Linux, Mac OS X
and Windows.
git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@17 232f8b59-042b-4e1e-8c03-345bb8c30851
Diffstat (limited to 'src')
24 files changed, 970 insertions, 299 deletions
diff --git a/src/net/java/games/gluegen/CMethodBindingEmitter.java b/src/net/java/games/gluegen/CMethodBindingEmitter.java index 45035f640..763fe6695 100644 --- a/src/net/java/games/gluegen/CMethodBindingEmitter.java +++ b/src/net/java/games/gluegen/CMethodBindingEmitter.java @@ -50,6 +50,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { protected static final CommentEmitter defaultCommentEmitter = new DefaultCommentEmitter(); + + protected static final String arrayResLength = "_array_res_length"; + protected static final String arrayRes = "_array_res"; + protected static final String arrayIdx = "_array_idx"; private MethodBinding binding; @@ -89,6 +93,12 @@ public class CMethodBindingEmitter extends FunctionEmitter */ private MessageFormat returnValueCapacityExpression = null; + /** + * Length of the returned array. Is ignored if + * binding.getJavaReturnType().isArray() is false. + */ + private MessageFormat returnValueLengthExpression = null; + // Note: the VC++ 6.0 compiler emits hundreds of warnings when the // (necessary) null-checking code is enabled. This appears to just // be a compiler bug, but would be good to track down exactly why it @@ -168,6 +178,49 @@ public class CMethodBindingEmitter extends FunctionEmitter } /** + * Get the expression for the length of the returned array + */ + public final MessageFormat getReturnValueLengthExpression() + { + return returnValueLengthExpression; + } + + /** + * If this function returns an array, sets the expression for the + * length of the returned array. + * + * @param expression a MessageFormat which, when applied to an array + * of type String[] that contains each of the arguments names of the + * Java-side binding, returns an expression that will (when compiled + * by a C compiler) evaluate to an integer-valued expression. The + * value of this expression is the length of the array returned from + * this method. + * + * @throws IllegalArgumentException if the <code> + * binding.getJavaReturnType().isNIOBuffer() == false + * </code> + */ + public final void setReturnValueLengthExpression(MessageFormat expression) + { + returnValueLengthExpression = expression; + + if (!binding.getJavaReturnType().isArray()) + { + throw new IllegalArgumentException( + "Cannot specify return value length for a method that does not " + + "return an array: \"" + binding + "\""); + } + } + + /** + * Returns the List of Strings containing declarations for temporary + * C variables to be assigned to after the underlying function call. + */ + public final List/*<String>*/ getTemporaryCVariableDeclarations() { + return temporaryCVariableDeclarations; + } + + /** * Sets up a List of Strings containing declarations for temporary C * variables to be assigned to after the underlying function call. A * null argument indicates that no manual declarations are to be made. @@ -177,6 +230,16 @@ public class CMethodBindingEmitter extends FunctionEmitter } /** + * Returns the List of Strings containing assignments for temporary + * C variables which are made after the underlying function call. A + * null argument indicates that no manual assignments are to be + * made. + */ + public final List/*<String>*/ getTemporaryCVariableAssignments() { + return temporaryCVariableAssignments; + } + + /** * Sets up a List of Strings containing assignments for temporary C * variables which are made after the underlying function call. A * null argument indicates that no manual assignments are to be made. @@ -340,8 +403,6 @@ public class CMethodBindingEmitter extends FunctionEmitter Type cReturnType = binding.getCReturnType(); JavaType javaReturnType = binding.getJavaReturnType(); - String arrayResLength = "_array_res_length"; - String arrayRes = "_array_res"; String capitalizedComponentType = null; if (!cReturnType.isVoid()) { writer.print(" "); @@ -349,24 +410,36 @@ public class CMethodBindingEmitter extends FunctionEmitter writer.print(binding.getCSymbol().getReturnType().getName(true)); writer.println(" _res;"); if (javaReturnType.isArray()) { - writer.print(" int "); - writer.print(arrayResLength); - writer.println(";"); + if (javaReturnType.isNIOByteBufferArray()) { + writer.print(" int "); + writer.print(arrayResLength); + writer.println(";"); + writer.print(" int "); + writer.print(arrayIdx); + writer.println(";"); + writer.print(" jobjectArray "); + writer.print(arrayRes); + writer.println(";"); + } else { + writer.print(" int "); + writer.print(arrayResLength); + writer.println(";"); - Class componentType = javaReturnType.getJavaClass().getComponentType(); - if (componentType.isArray()) { - throw new RuntimeException("Multi-dimensional arrays not supported yet"); - } + Class componentType = javaReturnType.getJavaClass().getComponentType(); + if (componentType.isArray()) { + throw new RuntimeException("Multi-dimensional arrays not supported yet"); + } - String javaTypeName = componentType.getName(); - capitalizedComponentType = - "" + Character.toUpperCase(javaTypeName.charAt(0)) + javaTypeName.substring(1); - String javaArrayTypeName = "j" + javaTypeName + "Array"; - writer.print(" "); - writer.print(javaArrayTypeName); - writer.print(" "); - writer.print(arrayRes); - writer.println(";"); + String javaTypeName = componentType.getName(); + capitalizedComponentType = + "" + Character.toUpperCase(javaTypeName.charAt(0)) + javaTypeName.substring(1); + String javaArrayTypeName = "j" + javaTypeName + "Array"; + writer.print(" "); + writer.print(javaArrayTypeName); + writer.print(" "); + writer.print(arrayRes); + writer.println(";"); + } } } } @@ -568,6 +641,11 @@ public class CMethodBindingEmitter extends FunctionEmitter if (EMIT_NULL_CHECKS) { writer.println(" }"); } + } else if (javaArgType.isArrayOfCompoundTypeWrappers()) { + + // FIXME + throw new RuntimeException("Outgoing arrays of StructAccessors not yet implemented"); + } } } @@ -696,6 +774,11 @@ public class CMethodBindingEmitter extends FunctionEmitter if (EMIT_NULL_CHECKS) { writer.println(" }"); } + } else if (javaArgType.isArrayOfCompoundTypeWrappers()) { + + // FIXME + throw new RuntimeException("Outgoing arrays of StructAccessors not yet implemented"); + } } } @@ -826,35 +909,63 @@ public class CMethodBindingEmitter extends FunctionEmitter writer.print(" if (_res == NULL) return NULL;"); writer.println(" return (*env)->NewStringUTF(env, _res);"); } else if (javaReturnType.isArray()) { - // FIXME: must have user provide length of array in .cfg file - // by providing a constant value, input parameter, or - // expression which computes the array size (already present - // as ReturnValueCapacity, not yet implemented / tested here) - - throw new RuntimeException( - "Could not emit native code for function \"" + binding + - "\": array return values for non-char types not implemented yet"); - - // FIXME: This is approximately what will be required here - // - //writer.print(" "); - //writer.print(arrayRes); - //writer.print(" = (*env)->New"); - //writer.print(capitalizedComponentType); - //writer.print("Array(env, "); - //writer.print(arrayResLength); - //writer.println(");"); - //writer.print(" (*env)->Set"); - //writer.print(capitalizedComponentType); - //writer.print("ArrayRegion(env, "); - //writer.print(arrayRes); - //writer.print(", 0, "); - //writer.print(arrayResLength); - //writer.println(", _res);"); - //writer.print(" return "); - //writer.print(arrayRes); - //writer.println(";"); - + if (javaReturnType.isNIOByteBufferArray()) { + writer.println(" if (_res == NULL) return NULL;"); + if (returnValueLengthExpression == null) { + throw new RuntimeException("Error while generating C code: no length specified for array returned from function " + + binding); + } + String[] argumentNames = new String[binding.getNumArguments()]; + for (int i = 0; i < binding.getNumArguments(); i++) { + argumentNames[i] = binding.getArgumentName(i); + } + writer.println(" " + arrayResLength + " = " + returnValueLengthExpression.format(argumentNames) + ";"); + writer.println(" " + arrayRes + " = (*env)->NewObjectArray(env, " + arrayResLength + ", (*env)->FindClass(env, \"java/nio/ByteBuffer\"), NULL);"); + writer.println(" for (" + arrayIdx + " = 0; " + arrayIdx + " < " + arrayResLength + "; " + arrayIdx + "++) {"); + Type retType = binding.getCSymbol().getReturnType(); + Type baseType; + if (retType.isPointer()) { + baseType = retType.asPointer().getTargetType().asPointer().getTargetType(); + } else { + baseType = retType.asArray().getElementType().asPointer().getTargetType(); + } + int sz = baseType.getSize(); + if (sz < 0) + sz = 0; + writer.println(" (*env)->SetObjectArrayElement(env, " + arrayRes + ", " + arrayIdx + + ", (*env)->NewDirectByteBuffer(env, _res[" + arrayIdx + "], " + sz + "));"); + writer.println(" }"); + writer.println(" return " + arrayRes + ";"); + } else { + // FIXME: must have user provide length of array in .cfg file + // by providing a constant value, input parameter, or + // expression which computes the array size (already present + // as ReturnValueCapacity, not yet implemented / tested here) + + throw new RuntimeException( + "Could not emit native code for function \"" + binding + + "\": array return values for non-char types not implemented yet"); + + // FIXME: This is approximately what will be required here + // + //writer.print(" "); + //writer.print(arrayRes); + //writer.print(" = (*env)->New"); + //writer.print(capitalizedComponentType); + //writer.print("Array(env, "); + //writer.print(arrayResLength); + //writer.println(");"); + //writer.print(" (*env)->Set"); + //writer.print(capitalizedComponentType); + //writer.print("ArrayRegion(env, "); + //writer.print(arrayRes); + //writer.print(", 0, "); + //writer.print(arrayResLength); + //writer.println(", _res);"); + //writer.print(" return "); + //writer.print(arrayRes); + //writer.println(";"); + } } } } @@ -1034,6 +1145,11 @@ public class CMethodBindingEmitter extends FunctionEmitter throw new RuntimeException("Unsupported pointer type: \"" + cType.getName() + "\""); } } + else if (javaType.isArrayOfCompoundTypeWrappers()) + { + // FIXME + throw new RuntimeException("Outgoing arrays of StructAccessors not yet implemented"); + } else { ptrTypeString = cType.getName(); diff --git a/src/net/java/games/gluegen/DebugEmitter.java b/src/net/java/games/gluegen/DebugEmitter.java index 677c3e7e6..8618e1576 100644 --- a/src/net/java/games/gluegen/DebugEmitter.java +++ b/src/net/java/games/gluegen/DebugEmitter.java @@ -95,8 +95,13 @@ public class DebugEmitter implements GlueEmitter { TypeDictionary structDictionary, Map canonMap) { } - public void emitStruct(CompoundType t) { - System.out.println("Referenced type \"" + t.getName() + "\""); + public void emitStruct(CompoundType t, String alternateName) { + String name = t.getName(); + if (name == null && alternateName != null) { + name = alternateName; + } + + System.out.println("Referenced type \"" + name + "\""); } public void endStructs() {} } diff --git a/src/net/java/games/gluegen/GlueEmitter.java b/src/net/java/games/gluegen/GlueEmitter.java index 3d55474e0..e816ffc9d 100644 --- a/src/net/java/games/gluegen/GlueEmitter.java +++ b/src/net/java/games/gluegen/GlueEmitter.java @@ -93,7 +93,11 @@ public interface GlueEmitter { public void beginStructs(TypeDictionary typedefDictionary, TypeDictionary structDictionary, Map canonMap) throws Exception; - /** Emit glue code for the given CompoundType. */ - public void emitStruct(CompoundType t) throws Exception; + /** Emit glue code for the given CompoundType. alternateName is + provided when the CompoundType (e.g. "struct foo_t") has not + been typedefed to anything but the type of "pointer to struct + foo_t" has (e.g. "typedef struct foo_t {} *Foo"); in this case + alternateName would be set to Foo. */ + public void emitStruct(CompoundType t, String alternateName) throws Exception; public void endStructs() throws Exception; } diff --git a/src/net/java/games/gluegen/GlueGen.java b/src/net/java/games/gluegen/GlueGen.java index 374611c81..2e5f3421a 100644 --- a/src/net/java/games/gluegen/GlueGen.java +++ b/src/net/java/games/gluegen/GlueGen.java @@ -241,20 +241,35 @@ public class GlueGen implements GlueEmitterControls { System.err.println("WARNING: during forced struct emission: type \"" + name + "\" was not a struct"); } else { type.visit(referencedStructs); - } + } } // Lay out structs emit.beginStructLayout(); for (Iterator iter = referencedStructs.results(); iter.hasNext(); ) { - emit.layoutStruct((CompoundType) iter.next()); + Type t = (Type) iter.next(); + if (t.isCompound()) { + emit.layoutStruct(t.asCompound()); + } else if (t.isPointer()) { + PointerType p = t.asPointer(); + CompoundType c = p.getTargetType().asCompound(); + emit.layoutStruct(c); + } } emit.endStructLayout(); // Emit structs emit.beginStructs(td, sd, headerParser.getCanonMap()); for (Iterator iter = referencedStructs.results(); iter.hasNext(); ) { - emit.emitStruct((CompoundType) iter.next()); + Type t = (Type) iter.next(); + if (t.isCompound()) { + emit.emitStruct(t.asCompound(), null); + } else if (t.isPointer()) { + PointerType p = t.asPointer(); + CompoundType c = p.getTargetType().asCompound(); + assert p.hasTypedefedName() && c.getName() == null : "ReferencedStructs incorrectly recorded pointer type " + p; + emit.emitStruct(c, p.getName()); + } } emit.endStructs(); diff --git a/src/net/java/games/gluegen/JavaConfiguration.java b/src/net/java/games/gluegen/JavaConfiguration.java index ecd953480..3950a7014 100644 --- a/src/net/java/games/gluegen/JavaConfiguration.java +++ b/src/net/java/games/gluegen/JavaConfiguration.java @@ -105,6 +105,7 @@ public class JavaConfiguration { private List/*<String>*/ forcedStructs = new ArrayList(); private Map/*<String,List<Integer>>*/ mirroredArgs = new HashMap(); private Map/*<String, String>*/ returnValueCapacities = new HashMap(); + private Map/*<String, String>*/ returnValueLengths = new HashMap(); private Map/*<String, List<String>>*/ temporaryCVariableDeclarations = new HashMap(); private Map/*<String, List<String>>*/ temporaryCVariableAssignments = new HashMap(); private Map/*<String,List<String>>*/ extendedInterfaces = new HashMap(); @@ -288,7 +289,9 @@ public class JavaConfiguration { /** Provides a Java MessageFormat expression indicating the number of elements in the returned array from the specified function - name as defined by the ReturnsArray directive. */ + name. Indicates that the given return value, which must be a + pointer to a CompoundType, is actually an array of the + CompoundType rather than a pointer to a single object. */ public String returnedArrayLength(String functionName) { return (String) returnedArrayLengths.get(functionName); } @@ -378,6 +381,13 @@ public class JavaConfiguration { return (String) returnValueCapacities.get(functionName); } + /** Returns a MessageFormat string of the C expression calculating + the length of the array being returned from a native method, or + null if no expression has been specified. */ + public String returnValueLength(String functionName) { + return (String) returnValueLengths.get(functionName); + } + /** Returns a List of Strings of expressions declaring temporary C variables in the glue code for the specified function. */ public List/*<String>*/ temporaryCVariableDeclarations(String functionName) { @@ -582,6 +592,10 @@ public class JavaConfiguration { readReturnValueCapacity(tok, filename, lineNo); // Warning: make sure delimiters are reset at the top of this loop // because ReturnValueCapacity changes them. + } else if (cmd.equalsIgnoreCase("ReturnValueLength")) { + readReturnValueLength(tok, filename, lineNo); + // Warning: make sure delimiters are reset at the top of this loop + // because ReturnValueLength changes them. } else if (cmd.equalsIgnoreCase("Include")) { doInclude(tok, file, filename, lineNo); } else if (cmd.equalsIgnoreCase("IncludeAs")) { @@ -926,6 +940,18 @@ public class JavaConfiguration { } } + protected void readReturnValueLength(StringTokenizer tok, String filename, int lineNo) { + try { + String functionName = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + restOfLine = restOfLine.trim(); + returnValueLengths.put(functionName, restOfLine); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"ReturnValueLength\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + protected void readTemporaryCVariableDeclaration(StringTokenizer tok, String filename, int lineNo) { try { String functionName = tok.nextToken(); diff --git a/src/net/java/games/gluegen/JavaEmitter.java b/src/net/java/games/gluegen/JavaEmitter.java index 228b5aff2..26e2a4f23 100644 --- a/src/net/java/games/gluegen/JavaEmitter.java +++ b/src/net/java/games/gluegen/JavaEmitter.java @@ -451,18 +451,26 @@ public class JavaEmitter implements GlueEmitter { this.canonMap = canonMap; } - public void emitStruct(CompoundType structType) throws Exception { - if (structType.getName() == null) { + public void emitStruct(CompoundType structType, String alternateName) throws Exception { + String name = structType.getName(); + if (name == null && alternateName != null) { + name = alternateName; + } + + if (name == null) { System.err.println("WARNING: skipping emission of unnamed struct \"" + structType + "\""); return; } - if (cfg.shouldIgnore(structType.getName())) { + if (cfg.shouldIgnore(name)) { return; } Type containingCType = canonicalize(new PointerType(machDesc.pointerSizeInBytes(), structType, 0)); JavaType containingType = typeToJavaType(containingCType, false); + if (!containingType.isCompoundTypeWrapper()) { + return; + } String containingTypeName = containingType.getName(); boolean needsNativeCode = false; @@ -473,7 +481,7 @@ public class JavaEmitter implements GlueEmitter { } } - String structClassPkg = cfg.packageForStruct(structType.getName()); + String structClassPkg = cfg.packageForStruct(name); PrintWriter writer = null; PrintWriter cWriter = null; try @@ -534,7 +542,7 @@ public class JavaEmitter implements GlueEmitter { for (int i = 0; i < structType.getNumFields(); i++) { Field field = structType.getField(i); Type fieldType = field.getType(); - if (!cfg.shouldIgnore(structType.getName() + " " + field.getName())) { + if (!cfg.shouldIgnore(name + " " + field.getName())) { if (fieldType.isFunctionPointer()) { try { // Emit method call and associated native code @@ -566,7 +574,7 @@ public class JavaEmitter implements GlueEmitter { cWriter); cEmitter.emit(); } catch (Exception e) { - System.err.println("While processing field " + field + " of type " + structType.getName() + ":"); + System.err.println("While processing field " + field + " of type " + name + ":"); throw(e); } } else if (fieldType.isCompound()) { @@ -575,7 +583,7 @@ public class JavaEmitter implements GlueEmitter { // a name?) if (fieldType.getName() == null) { throw new RuntimeException("Anonymous structs as fields not supported yet (field \"" + - field + "\" in type \"" + structType.getName() + "\")"); + field + "\" in type \"" + name + "\")"); } writer.println(); @@ -586,7 +594,7 @@ public class JavaEmitter implements GlueEmitter { // FIXME: add setter by autogenerating "copyTo" for all compound type wrappers } else if (fieldType.isArray()) { - System.err.println("WARNING: Array fields (field \"" + field + "\" of type \"" + structType.getName() + + System.err.println("WARNING: Array fields (field \"" + field + "\" of type \"" + name + "\") not implemented yet"); } else { JavaType javaType = null; @@ -594,7 +602,7 @@ public class JavaEmitter implements GlueEmitter { javaType = typeToJavaType(fieldType, false); } catch (Exception e) { System.err.println("Error occurred while creating accessor for field \"" + - field.getName() + "\" in type \"" + structType.getName() + "\""); + field.getName() + "\" in type \"" + name + "\""); e.printStackTrace(); throw(e); } @@ -629,10 +637,6 @@ public class JavaEmitter implements GlueEmitter { writer.println(" }"); } else { // FIXME - String name = structType.getName(); - if (name == null) { - name = structType.toString(); - } System.err.println("WARNING: Complicated fields (field \"" + field + "\" of type \"" + name + "\") not implemented yet"); // throw new RuntimeException("Complicated fields (field \"" + field + "\" of type \"" + t + @@ -663,6 +667,7 @@ public class JavaEmitter implements GlueEmitter { String bindingJavaClassName, PrintWriter output) { MessageFormat returnValueCapacityFormat = null; + MessageFormat returnValueLengthFormat = null; JavaType javaReturnType = binding.getJavaReturnType(); if (javaReturnType.isNIOBuffer()) { // See whether capacity has been specified @@ -670,6 +675,12 @@ public class JavaEmitter implements GlueEmitter { if (capacity != null) { returnValueCapacityFormat = new MessageFormat(capacity); } + } else if (javaReturnType.isArray()) { + // See whether length has been specified + String len = cfg.returnValueLength(binding.getName()); + if (len != null) { + returnValueLengthFormat = new MessageFormat(len); + } } CMethodBindingEmitter cEmitter; if (doingImplRoutine) { @@ -686,6 +697,9 @@ public class JavaEmitter implements GlueEmitter { if (returnValueCapacityFormat != null) { cEmitter.setReturnValueCapacityExpression(returnValueCapacityFormat); } + if (returnValueLengthFormat != null) { + cEmitter.setReturnValueLengthExpression(returnValueLengthFormat); + } cEmitter.setTemporaryCVariableDeclarations(cfg.temporaryCVariableDeclarations(binding.getName())); cEmitter.setTemporaryCVariableAssignments(cfg.temporaryCVariableAssignments(binding.getName())); return cEmitter; @@ -758,12 +772,21 @@ public class JavaEmitter implements GlueEmitter { throw new RuntimeException("Arrays of compound types not handled yet"); } // Special cases for known JNI types (in particular for converting jawt.h) - if (cType.getName() != null && - cType.getName().equals("jobject")) { + if (t.getName() != null && + t.getName().equals("jobject")) { return javaType(java.lang.Object.class); } - return JavaType.createForCStruct(cfg.renameJavaType(targetType.getName())); + String name = targetType.getName(); + if (name == null) { + // Try containing pointer type for any typedefs + name = t.getName(); + if (name == null) { + throw new RuntimeException("Couldn't find a proper type name for pointer type " + t); + } + } + + return JavaType.createForCStruct(cfg.renameJavaType(name)); } else { throw new RuntimeException("Don't know how to convert pointer/array type \"" + t + "\""); @@ -775,15 +798,16 @@ public class JavaEmitter implements GlueEmitter { // Get the target type of the target type (targetType was computer earlier // as to be a pointer to the target type, so now we need to get its // target type) + Type bottomType; if (targetType.isPointer()) { // t is<type>**, targetType is <type>*, we need to get <type> - targetType = targetType.asPointer().getTargetType(); + bottomType = targetType.asPointer().getTargetType(); } else { // t is<type>[][], targetType is <type>[], we need to get <type> - targetType = targetType.asArray().getElementType(); + bottomType = targetType.asArray().getElementType(); } - if (targetType.isInt()) { - switch (targetType.getSize()) + if (bottomType.isInt()) { + switch (bottomType.getSize()) { case 1: return javaType(ArrayTypes.byteArrayArrayClass); // FIXME: handle 2,4,8-byte int types here @@ -793,10 +817,13 @@ public class JavaEmitter implements GlueEmitter { "Java type; Currently, the only supported depth=2 " + "pointer/array integer types are \"char**\" and \"char[][]\""); } + } else if (targetType.isPointer() && (targetType.pointerDepth() == 1)) { + // Array of pointers; convert as array of StructAccessors + return JavaType.createForCArray(targetType); } else { throw new RuntimeException( "Could not convert C type \"" + t + "\" " + - "to appropriate Java type; need to add support for " + + "to appropriate Java type; need to add more support for " + "depth=2 pointer/array types with non-integral target " + "types [debug info: targetType=\"" + targetType + "\"]"); } diff --git a/src/net/java/games/gluegen/JavaMethodBindingEmitter.java b/src/net/java/games/gluegen/JavaMethodBindingEmitter.java index 003ea8643..850a2c701 100644 --- a/src/net/java/games/gluegen/JavaMethodBindingEmitter.java +++ b/src/net/java/games/gluegen/JavaMethodBindingEmitter.java @@ -122,7 +122,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter } protected String getReturnTypeString(boolean skipArray) { - if (skipArray || getReturnedArrayLengthExpression() == null) { + if (skipArray || (getReturnedArrayLengthExpression() == null && !binding.getJavaReturnType().isArrayOfCompoundTypeWrappers())) { return binding.getJavaReturnType().getName(); } return binding.getJavaReturnType().getName() + "[]"; diff --git a/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java b/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java index 2357e7c38..2ec2723bd 100644 --- a/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java +++ b/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java @@ -113,6 +113,9 @@ public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter returnType.isNIOByteBuffer()) { writer.println("ByteBuffer _res;"); writer.print(" _res = "); + } else if (returnType.isArrayOfCompoundTypeWrappers()) { + writer.println("ByteBuffer[] _res;"); + writer.print(" _res = "); } else { writer.print("return "); } @@ -205,7 +208,7 @@ public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter writer.println(" _tmp.order(ByteOrder.nativeOrder());"); writer.println(" _res.position(0);"); writer.println(" _res.limit(_res.capacity());"); - writer.println(" _retarray[_count] = new " + returnType.getName() + "(_tmp);"); + writer.println(" _retarray[_count] = new " + getReturnTypeString(true) + "(_tmp);"); writer.println(" }"); writer.print (" return _retarray"); } @@ -213,6 +216,14 @@ public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter writer.println(";"); writer.println(" if (_res == null) return null;"); writer.print(" return _res.order(ByteOrder.nativeOrder())"); + } else if (returnType.isArrayOfCompoundTypeWrappers()) { + writer.println(";"); + writer.println(" if (_res == null) return null;"); + writer.println(" " + getReturnTypeString(false) + " _retarray = new " + getReturnTypeString(true) + "[_res.length];"); + writer.println(" for (int _count = 0; _count < _res.length; _count++) {"); + writer.println(" _retarray[_count] = new " + getReturnTypeString(true) + "(_res[_count]);"); + writer.println(" }"); + writer.print (" return _retarray"); } writer.println(";"); } diff --git a/src/net/java/games/gluegen/JavaType.java b/src/net/java/games/gluegen/JavaType.java index ac42aa0f1..ea771613e 100644 --- a/src/net/java/games/gluegen/JavaType.java +++ b/src/net/java/games/gluegen/JavaType.java @@ -39,6 +39,10 @@ package net.java.games.gluegen; +import java.nio.*; + +import net.java.games.gluegen.cgram.types.*; + /** * Describes a java-side representation of a type that is used to represent * the same data on both the Java-side and C-side during a JNI operation. Also @@ -47,8 +51,10 @@ package net.java.games.gluegen; public class JavaType { private Class clazz; // Primitive types and other types representable as Class objects private String name; // Types we're generating glue code for (i.e., C structs) + private Type elementType; // Element type if this JavaType represents a C array private static JavaType nioBufferType; private static JavaType nioByteBufferType; + private static JavaType nioByteBufferArrayType; public boolean equals(Object arg) { if ((arg == null) || (!(arg instanceof JavaType))) { @@ -70,14 +76,28 @@ public class JavaType { return clazz.hashCode(); } + /** Creates a JavaType corresponding to the given Java type. This + can be used to represent arrays of primitive values or Strings; + the emitters understand how to perform proper conversion from + the corresponding C type. */ public static JavaType createForClass(Class clazz) { return new JavaType(clazz); } + /** Creates a JavaType corresponding to the specified C CompoundType + name; for example, if "Foo" is supplied, then this JavaType + represents a "Foo *" by way of a StructAccessor. */ public static JavaType createForCStruct(String name) { return new JavaType(name); } + /** Creates a JavaType corresponding to an array of the given + element type. This is used to represent arrays of "Foo **" which + should be mapped to Foo[] in Java. */ + public static JavaType createForCArray(Type elementType) { + return new JavaType(elementType); + } + public static JavaType createForVoidPointer() { return new JavaType(); } @@ -100,6 +120,14 @@ public class JavaType { return nioByteBufferType; } + public static JavaType forNIOByteBufferArrayClass() { + if (nioByteBufferArrayType == null) { + ByteBuffer[] tmp = new ByteBuffer[0]; + nioByteBufferArrayType = createForClass(tmp.getClass()); + } + return nioByteBufferArrayType; + } + /** * Returns the Java Class corresponding to this type. Returns null if this * object corresponds to a C "void*" type. @@ -119,6 +147,9 @@ public class JavaType { } return clazz.getName(); } + if (elementType != null) { + return elementType.getName(); + } return name; } @@ -151,6 +182,10 @@ public class JavaType { return "jobjectArray /*elements are String*/"; //return "jobjectArray"; } + else if (elementType == java.nio.ByteBuffer.class) + { + return "jobjectArray /*elements are ByteBuffer*/"; + } else if (elementType.isArray()) { // Type is array-of-arrays-of-something @@ -189,6 +224,10 @@ public class JavaType { return (clazz == java.nio.ByteBuffer.class); } + public boolean isNIOByteBufferArray() { + return (this == nioByteBufferArrayType); + } + public boolean isString() { return (clazz == java.lang.String.class); } @@ -205,17 +244,16 @@ public class JavaType { return (clazz == Void.TYPE); } - public boolean isObjectType() { - // FIXME: what about char* -> String conversion? - return (isNIOBuffer() || isArray()); - } - public boolean isCompoundTypeWrapper() { return (clazz == null && name != null && !isJNIEnv()); } + public boolean isArrayOfCompoundTypeWrappers() { + return (elementType != null); + } + public boolean isVoidPointerType() { - return (clazz == null && name == null); + return (clazz == null && name == null && elementType == null); } public boolean isJNIEnv() { @@ -252,6 +290,11 @@ public class JavaType { this.name = name; } + /** Constructs a type representing an array of C pointers. */ + private JavaType(Type elementType) { + this.elementType = elementType; + } + /** * Default constructor; the type is initialized to the equivalent of a * C-language "void *". diff --git a/src/net/java/games/gluegen/MethodBinding.java b/src/net/java/games/gluegen/MethodBinding.java index 9cdcd4b0b..b8f0eefdf 100644 --- a/src/net/java/games/gluegen/MethodBinding.java +++ b/src/net/java/games/gluegen/MethodBinding.java @@ -174,7 +174,8 @@ public class MethodBinding { public boolean needsBody() { if (!computedNeedsBody) { if (javaReturnType.isCompoundTypeWrapper() || - javaReturnType.isNIOByteBuffer()) { + javaReturnType.isNIOByteBuffer() || + javaReturnType.isArrayOfCompoundTypeWrappers()) { // Needs wrapping and/or setting of byte order (neither of // which can be done easily from native code) needsBody = true; @@ -204,6 +205,8 @@ public class MethodBinding { binding.thisPointerIndex = thisPointerIndex; if (javaReturnType.isCompoundTypeWrapper()) { binding.setJavaReturnType(JavaType.forNIOByteBufferClass()); + } else if (javaReturnType.isArrayOfCompoundTypeWrappers()) { + binding.setJavaReturnType(JavaType.forNIOByteBufferArrayClass()); } else { binding.setJavaReturnType(javaReturnType); } diff --git a/src/net/java/games/gluegen/ReferencedStructs.java b/src/net/java/games/gluegen/ReferencedStructs.java index 1b3b75198..4a7a8aadf 100644 --- a/src/net/java/games/gluegen/ReferencedStructs.java +++ b/src/net/java/games/gluegen/ReferencedStructs.java @@ -54,7 +54,18 @@ public class ReferencedStructs implements TypeVisitor { } public void visitType(Type t) { - if (t.isCompound()) { + if (t.isPointer()) { + PointerType p = t.asPointer(); + if (p.hasTypedefedName()) { + CompoundType c = p.getTargetType().asCompound(); + if (c != null && c.getName() == null) { + // This otherwise-unnamed CompoundType is referred to by a + // PointerType that has a typedef name. Assume that it is + // referred to in the glue code and emit it. + results.add(p); + } + } + } else if (t.isCompound()) { results.add(t); } } diff --git a/src/net/java/games/gluegen/cgram/types/PointerType.java b/src/net/java/games/gluegen/cgram/types/PointerType.java index 62bfe9c1a..64acbade4 100644 --- a/src/net/java/games/gluegen/cgram/types/PointerType.java +++ b/src/net/java/games/gluegen/cgram/types/PointerType.java @@ -100,6 +100,10 @@ public class PointerType extends Type { } } + public boolean hasTypedefedName() { + return hasTypedefedName; + } + public PointerType asPointer() { return this; } public Type getTargetType() { return targetType; } diff --git a/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java b/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java index 47476a54c..c6ac77c7b 100644 --- a/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java +++ b/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java @@ -209,7 +209,7 @@ public class BuildComposablePipeline { output.print(" public "); output.print(' '); - output.print(m.getReturnType().getName()); + output.print(JavaType.createForClass(m.getReturnType()).getName()); output.print(' '); output.print(m.getName()); output.print('('); diff --git a/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java b/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java index cd0554dc3..2a1559e52 100644 --- a/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java +++ b/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java @@ -68,6 +68,15 @@ public class CGLPAWrapperEmitter extends CMethodBindingEmitter methodToWrap.getIsJavaMethodStatic(), methodToWrap.getDefaultOutput() ); + + if (methodToWrap.getReturnValueCapacityExpression() != null) { + setReturnValueCapacityExpression(methodToWrap.getReturnValueCapacityExpression()); + } + if (methodToWrap.getReturnValueLengthExpression() != null) { + setReturnValueLengthExpression(methodToWrap.getReturnValueLengthExpression()); + } + setTemporaryCVariableDeclarations(methodToWrap.getTemporaryCVariableDeclarations()); + setTemporaryCVariableAssignments (methodToWrap.getTemporaryCVariableAssignments ()); setCommentEmitter(defaultCommentEmitter); } @@ -192,6 +201,13 @@ public class CGLPAWrapperEmitter extends CMethodBindingEmitter writer.println(");"); } + protected String jniMangle(MethodBinding binding) { + StringBuffer buf = new StringBuffer(); + buf.append(super.jniMangle(binding)); + jniMangle(Long.TYPE, buf); + return buf.toString(); + } + /** This class emits the comment for the wrapper method */ private static class CGLPAWrapperCommentEmitter extends CMethodBindingEmitter.DefaultCommentEmitter { protected void emitBeginning(FunctionEmitter methodEmitter, PrintWriter writer) { diff --git a/src/net/java/games/gluegen/opengl/GLEmitter.java b/src/net/java/games/gluegen/opengl/GLEmitter.java index 27cc07e2c..f6e26fd3b 100644 --- a/src/net/java/games/gluegen/opengl/GLEmitter.java +++ b/src/net/java/games/gluegen/opengl/GLEmitter.java @@ -44,6 +44,7 @@ import java.text.MessageFormat; import java.util.*; import net.java.games.gluegen.*; import net.java.games.gluegen.cgram.types.*; +import net.java.games.gluegen.runtime.*; /** * A subclass of JavaEmitter that modifies the normal emission of C and Java @@ -52,11 +53,12 @@ import net.java.games.gluegen.cgram.types.*; */ public class GLEmitter extends JavaEmitter { - public static final String PROCADDRESS_VAR_PREFIX = "_addressof_"; + public static final String PROCADDRESS_VAR_PREFIX = ProcAddressHelper.PROCADDRESS_VAR_PREFIX; protected static final String WRAP_PREFIX = "dispatch_"; private TypeDictionary typedefDictionary; private PrintWriter tableWriter; - private String tableClassName = "ProcAddressTable"; + private String tableClassPackage; + private String tableClassName; private int numProcAddressEntries; public void beginFunctions(TypeDictionary typedefDictionary, @@ -70,7 +72,7 @@ public class GLEmitter extends JavaEmitter cWriter().println(); } - if (((GLConfiguration)getConfig()).emitProcAddressTable()) + if (getGLConfig().emitProcAddressTable()) { beginGLProcAddressTable(); } @@ -79,7 +81,7 @@ public class GLEmitter extends JavaEmitter public void endFunctions() throws Exception { - if (((GLConfiguration)getConfig()).emitProcAddressTable()) + if (getGLConfig().emitProcAddressTable()) { endGLProcAddressTable(); } @@ -118,7 +120,7 @@ public class GLEmitter extends JavaEmitter // 9 is default # expanded bindings for void* ArrayList modifiedEmitters = new ArrayList(9); - if (((GLConfiguration)getConfig()).emitProcAddressTable()) + if (getGLConfig().emitProcAddressTable()) { // emit an entry in the GL proc address table for this method. emitGLProcAddressTableEntryForSymbol(sym); @@ -185,7 +187,7 @@ public class GLEmitter extends JavaEmitter return null; return baseJavaEmitter; } - return new JavaGLPAWrapperEmitter(baseJavaEmitter); + return new JavaGLPAWrapperEmitter(baseJavaEmitter, getGLConfig().getProcAddressTableExpr()); } private CMethodBindingEmitter generateModifiedEmitter(CMethodBindingEmitter baseCEmitter) @@ -205,11 +207,17 @@ public class GLEmitter extends JavaEmitter { String symName = sym.getName(); + GLConfiguration config = getGLConfig(); + // We should only wrap the GL symbol if its function pointer typedef has // been defined (most likely in glext.h). String funcPointerTypedefName = getGLFunctionPointerTypedefName(sym); boolean shouldWrap = typedefDictionary.containsKey(funcPointerTypedefName); //System.err.println(funcPointerTypedefName + " defined: " + shouldWrap); + + if (config.skipProcAddressGen(symName)) { + shouldWrap = false; + } if (!shouldWrap) { @@ -221,26 +229,27 @@ public class GLEmitter extends JavaEmitter private void beginGLProcAddressTable() throws Exception { - String implPackageName = getImplPackageName(); + tableClassPackage = getGLConfig().tableClassPackage(); + tableClassName = getGLConfig().tableClassName(); + + // Table defaults to going into the impl directory unless otherwise overridden + String implPackageName = tableClassPackage; + if (implPackageName == null) { + implPackageName = getImplPackageName(); + } String jImplRoot = getJavaOutputDir() + File.separator + CodeGenUtils.packageAsPath(implPackageName); - // HACK: until we have a way to make the impl dir different from the - // WindowsGLImpl dir and the interface dir - //tableWriter = openFile(jImplRoot + File.separator + tableClassName + ".java"); - File tmpFile = new File(jImplRoot); - tmpFile = tmpFile.getParentFile(); - tmpFile = new File(tmpFile, tableClassName + ".java"); - tableWriter = openFile(tmpFile.getPath()); - // tableWriter = openFile(jImplRoot + File.separator + ".." + File.separator + tableClassName + ".java"); + tableWriter = openFile(jImplRoot + File.separator + tableClassName + ".java"); CodeGenUtils.emitAutogeneratedWarning(tableWriter, this); - // HACK: until we have a way to make the impl dir different from the - // WindowsGLImpl dir and the interface dir - //tableWriter.println("package " + implPackageName + ";"); - tableWriter.println("package " + getJavaPackageName() + ".impl;"); + tableWriter.println("package " + implPackageName + ";"); + tableWriter.println(); + for (Iterator iter = getConfig().imports().iterator(); iter.hasNext(); ) { + tableWriter.println("import " + ((String) iter.next()) + ";"); + } tableWriter.println(); tableWriter.println("/**"); tableWriter.println(" * This table is a cache of the native pointers to OpenGL extension"); @@ -260,9 +269,6 @@ public class GLEmitter extends JavaEmitter private void endGLProcAddressTable() throws Exception { PrintWriter w = tableWriter; - w.print(" protected static long __PROCADDRESSINDEX__LASTINDEX = "); - w.print(numProcAddressEntries-1); - w.println(';'); w.println(); w.println(" /**"); @@ -301,30 +307,69 @@ public class GLEmitter extends JavaEmitter private void emitGLProcAddressTableEntryForSymbol(FunctionSymbol cFunc) { - tableWriter.print(" public static long "); + tableWriter.print(" public long "); tableWriter.print(PROCADDRESS_VAR_PREFIX); tableWriter.print(cFunc.getName()); tableWriter.println(";"); ++numProcAddressEntries; } + private GLConfiguration getGLConfig() { + return (GLConfiguration) getConfig(); + } + protected static class GLConfiguration extends JavaConfiguration { private boolean emitProcAddressTable = false; - + private String tableClassPackage; + private String tableClassName = "ProcAddressTable"; + private Set/*<String>*/ skipProcAddressGen = new HashSet(); + private String getProcAddressTableExpr = "context.getGLProcAddressTable()"; + protected void dispatch(String cmd, StringTokenizer tok, File file, String filename, int lineNo) throws IOException { if (cmd.equalsIgnoreCase("EmitProcAddressTable")) { emitProcAddressTable = readBoolean("EmitProcAddressTable", tok, filename, lineNo).booleanValue(); } + else if (cmd.equalsIgnoreCase("ProcAddressTablePackage")) + { + tableClassPackage = readString("ProcAddressTablePackage", tok, filename, lineNo); + } + else if (cmd.equalsIgnoreCase("ProcAddressTableClassName")) + { + tableClassName = readString("ProcAddressTableClassName", tok, filename, lineNo); + } + else if (cmd.equalsIgnoreCase("SkipProcAddressGen")) + { + String sym = readString("SkipProcAddressGen", tok, filename, lineNo); + skipProcAddressGen.add(sym); + } + else if (cmd.equalsIgnoreCase("GetProcAddressTableExpr")) + { + getProcAddressTableExpr = readGetProcAddressTableExpr(tok, filename, lineNo); + } else { super.dispatch(cmd,tok,file,filename,lineNo); } } - public boolean emitProcAddressTable() { return emitProcAddressTable; } + protected String readGetProcAddressTableExpr(StringTokenizer tok, String filename, int lineNo) { + try { + String restOfLine = tok.nextToken("\n\r\f"); + return restOfLine.trim(); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"GetProcAddressTableExpr\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + public boolean emitProcAddressTable() { return emitProcAddressTable; } + public String tableClassPackage() { return tableClassPackage; } + public String tableClassName() { return tableClassName; } + public boolean skipProcAddressGen (String name) { return skipProcAddressGen.contains(name); } + public String getProcAddressTableExpr() { return getProcAddressTableExpr; } } // end class GLConfiguration } diff --git a/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java b/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java index e0a098f2a..c3b17f6cb 100644 --- a/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java +++ b/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java @@ -50,10 +50,12 @@ public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter new WrappedMethodCommentEmitter(); private JavaMethodBindingEmitter emitterBeingWrapped; + private String getProcAddressTableExpr; - public JavaGLPAWrapperEmitter(JavaMethodBindingEmitter methodToWrap) + public JavaGLPAWrapperEmitter(JavaMethodBindingEmitter methodToWrap, String getProcAddressTableExpr) { super(methodToWrap.getBinding(), methodToWrap.getDefaultOutput(), methodToWrap.getRuntimeExceptionType()); + this.getProcAddressTableExpr = getProcAddressTableExpr; if (methodToWrap.getBinding().hasContainingType()) { @@ -119,6 +121,9 @@ public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter // Now make our binding use the original access of the wrapped method this.addModifier(origAccess); + if (emitterBeingWrapped.hasModifier(STATIC)) { + this.addModifier(STATIC); + } } protected boolean needsBody() { @@ -152,9 +157,7 @@ public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter String procAddressVariable = GLEmitter.PROCADDRESS_VAR_PREFIX + wrappedBinding.getName(); - writer.print(" final long addr = context.getGLProcAddressTable()."); - writer.print(procAddressVariable); - writer.println(';'); + writer.println(" final long addr = " + getProcAddressTableExpr + "." + procAddressVariable + ";"); writer.println(" if (addr == 0) {"); writer.println(" throw new GLException(\"Method \\\"" + binding.getName() + "\\\" not available\");"); writer.println(" }"); diff --git a/src/net/java/games/gluegen/runtime/ProcAddressHelper.java b/src/net/java/games/gluegen/runtime/ProcAddressHelper.java new file mode 100644 index 000000000..fec0eb366 --- /dev/null +++ b/src/net/java/games/gluegen/runtime/ProcAddressHelper.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package net.java.games.gluegen.runtime; + +/** Contains constants used in glue code generation. */ + +public class ProcAddressHelper { + public static final String PROCADDRESS_VAR_PREFIX = "_addressof_"; +} diff --git a/src/net/java/games/jogl/impl/GLContext.java b/src/net/java/games/jogl/impl/GLContext.java index 98b1c6065..eb7d21988 100644 --- a/src/net/java/games/jogl/impl/GLContext.java +++ b/src/net/java/games/jogl/impl/GLContext.java @@ -42,6 +42,7 @@ package net.java.games.jogl.impl; import java.awt.Component; import java.awt.EventQueue; import net.java.games.jogl.*; +import net.java.games.gluegen.runtime.*; public abstract class GLContext { protected static final boolean DEBUG = false; @@ -75,8 +76,10 @@ public abstract class GLContext { // All GLU interfaces eventually route calls down to gluRoot. It can be // static because GLU it doesn't actually need to own context, it just makes // GL calls and assumes some context is active. - protected static final GLU gluRoot = new GLUImpl(); - protected static GLU glu = gluRoot; // this is the context's GLU interface + protected static final GLUProcAddressTable gluProcAddressTable = new GLUProcAddressTable(); + protected static final GLU gluRoot = new GLUImpl(gluProcAddressTable); + protected static boolean haveResetGLUProcAddressTable; + protected GLU glu = gluRoot; // this is the context's GLU interface protected Thread renderingThread; protected Runnable deferredReshapeAction; @@ -282,6 +285,13 @@ public abstract class GLContext { */ protected void resetGLFunctionAvailability() { functionAvailability.flush(); + if (!haveResetGLUProcAddressTable) { + if (DEBUG) { + System.err.println("!!! Initializing GLU extension address table"); + } + resetProcAddressTable(gluProcAddressTable); + haveResetGLUProcAddressTable = true; // Only need to do this once globally + } } /** @@ -391,6 +401,40 @@ public abstract class GLContext { conditions cause a GLException to be thrown. */ protected abstract void swapBuffers() throws GLException; + /** Helper routine which resets a ProcAddressTable generated by the + GLEmitter by looking up anew all of its function pointers. */ + protected void resetProcAddressTable(Object table) { + Class tableClass = table.getClass(); + java.lang.reflect.Field[] fields = tableClass.getDeclaredFields(); + + for (int i = 0; i < fields.length; ++i) { + String addressFieldName = fields[i].getName(); + if (!addressFieldName.startsWith(ProcAddressHelper.PROCADDRESS_VAR_PREFIX)) { + // not a proc address variable + continue; + } + int startOfMethodName = ProcAddressHelper.PROCADDRESS_VAR_PREFIX.length(); + String glFuncName = addressFieldName.substring(startOfMethodName); + try { + java.lang.reflect.Field addressField = tableClass.getDeclaredField(addressFieldName); + assert(addressField.getType() == Long.TYPE); + long newProcAddress = dynamicLookupFunction(glFuncName); + // set the current value of the proc address variable in the table object + addressField.setLong(table, newProcAddress); + if (DEBUG) { + System.err.println(glFuncName + " = 0x" + Long.toHexString(newProcAddress)); + } + } catch (Exception e) { + throw new GLException("Cannot get GL proc address for method \"" + + glFuncName + "\": Couldn't set value of field \"" + addressFieldName + + "\" in class " + tableClass.getName(), e); + } + } + } + + /** Dynamically looks up the given function. */ + protected abstract long dynamicLookupFunction(String glFuncName); + //---------------------------------------------------------------------- // Internals only below this point // diff --git a/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java b/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java index 23367c045..d35392ac1 100644 --- a/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java +++ b/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java @@ -41,7 +41,7 @@ package net.java.games.jogl.impl.macosx; import java.awt.Component; import java.util.*; -import net.java.games.gluegen.opengl.*; // for PROCADDRESS_VAR_PREFIX +import net.java.games.gluegen.runtime.*; // for PROCADDRESS_VAR_PREFIX import net.java.games.jogl.*; import net.java.games.jogl.impl.*; @@ -49,6 +49,9 @@ public abstract class MacOSXGLContext extends GLContext { private static JAWT jawt; protected long nsContext; // NSOpenGLContext + // Table that holds the addresses of the native C-language entry points for + // OpenGL functions. + private GLProcAddressTable glProcAddressTable; public MacOSXGLContext(Component component, GLCapabilities capabilities, GLCapabilitiesChooser chooser) { @@ -90,74 +93,25 @@ public abstract class MacOSXGLContext extends GLContext protected abstract void swapBuffers() throws GLException; + protected long dynamicLookupFunction(String glFuncName) { + return CGL.getProcAddress(glFuncName); + } protected void resetGLFunctionAvailability() { super.resetGLFunctionAvailability(); - resetGLProcAddressTable(); - } - - protected void resetGLProcAddressTable() - { if (DEBUG) { System.err.println("!!! Initializing OpenGL extension address table"); } - - net.java.games.jogl.impl.ProcAddressTable table = getGLProcAddressTable(); - - // if GL is no longer an interface, we'll have to re-implement the code - // below so it only iterates through gl methods (a non-interface might - // have constructors, custom methods, etc). For now we assume all methods - // will be gl methods. - GL gl = getGL(); - - Class tableClass = table.getClass(); - - java.lang.reflect.Field[] fields = tableClass.getDeclaredFields(); - - for (int i = 0; i < fields.length; ++i) { - String addressFieldName = fields[i].getName(); - if (!addressFieldName.startsWith(GLEmitter.PROCADDRESS_VAR_PREFIX)) - { - // not a proc address variable - continue; - } - int startOfMethodName = GLEmitter.PROCADDRESS_VAR_PREFIX.length(); - String glFuncName = addressFieldName.substring(startOfMethodName); - try - { - java.lang.reflect.Field addressField = tableClass.getDeclaredField(addressFieldName); - assert(addressField.getType() == Long.TYPE); - // get the current value of the proc address variable in the table object - long oldProcAddress = addressField.getLong(table); - long newProcAddress = CGL.getProcAddress(glFuncName); - /* - System.err.println( - "!!! Address=" + (newProcAddress == 0 - ? "<NULL> " - : ("0x" + - Long.toHexString(newProcAddress))) + - "\tGL func: " + glFuncName); - */ - // set the current value of the proc address variable in the table object - addressField.setLong(gl, newProcAddress); - } catch (Exception e) { - throw new GLException( - "Cannot get GL proc address for method \"" + - glFuncName + "\": Couldn't get value of field \"" + addressFieldName + - "\" in class " + tableClass.getName(), e); - } - } - + resetProcAddressTable(getGLProcAddressTable()); } - public net.java.games.jogl.impl.ProcAddressTable getGLProcAddressTable() + public GLProcAddressTable getGLProcAddressTable() { if (glProcAddressTable == null) { // FIXME: cache ProcAddressTables by capability bits so we can // share them among contexts with the same capabilities - glProcAddressTable = - new net.java.games.jogl.impl.ProcAddressTable(); + glProcAddressTable = new GLProcAddressTable(); } return glProcAddressTable; } @@ -171,10 +125,6 @@ public abstract class MacOSXGLContext extends GLContext // Internals only below this point // - // Table that holds the addresses of the native C-language entry points for - // OpenGL functions. - private net.java.games.jogl.impl.ProcAddressTable glProcAddressTable; - protected JAWT getJAWT() { if (jawt == null) diff --git a/src/net/java/games/jogl/impl/windows/WindowsGLContext.java b/src/net/java/games/jogl/impl/windows/WindowsGLContext.java index 4575b6210..5a6021acf 100644 --- a/src/net/java/games/jogl/impl/windows/WindowsGLContext.java +++ b/src/net/java/games/jogl/impl/windows/WindowsGLContext.java @@ -41,7 +41,7 @@ package net.java.games.jogl.impl.windows; import java.awt.Component; import java.util.*; -import net.java.games.gluegen.opengl.*; // for PROCADDRESS_VAR_PREFIX +import net.java.games.gluegen.runtime.*; // for PROCADDRESS_VAR_PREFIX import net.java.games.jogl.*; import net.java.games.jogl.impl.*; @@ -53,6 +53,11 @@ public abstract class WindowsGLContext extends GLContext { private boolean wglGetExtensionsStringEXTAvailable; private static final Map/*<String, String>*/ functionNameMap; private static final Map/*<String, String>*/ extensionNameMap; + // Table that holds the addresses of the native C-language entry points for + // OpenGL functions. + private GLProcAddressTable glProcAddressTable; + // Handle to GLU32.dll + private long hglu32; static { functionNameMap = new HashMap(); @@ -132,72 +137,34 @@ public abstract class WindowsGLContext extends GLContext { protected abstract void swapBuffers() throws GLException; + protected long dynamicLookupFunction(String glFuncName) { + long res = WGL.wglGetProcAddress(glFuncName); + if (res == 0) { + // GLU routines aren't known to the OpenGL function lookup + if (hglu32 == 0) { + hglu32 = WGL.LoadLibraryA("GLU32"); + if (hglu32 == 0) { + throw new GLException("Error loading GLU32.DLL"); + } + } + res = WGL.GetProcAddress(hglu32, glFuncName); + } + return res; + } protected void resetGLFunctionAvailability() { super.resetGLFunctionAvailability(); - resetGLProcAddressTable(); - } - - protected void resetGLProcAddressTable() { - if (DEBUG) { System.err.println("!!! Initializing OpenGL extension address table"); } - - net.java.games.jogl.impl.ProcAddressTable table = getGLProcAddressTable(); - - // if GL is no longer an interface, we'll have to re-implement the code - // below so it only iterates through gl methods (a non-interface might - // have constructors, custom methods, etc). For now we assume all methods - // will be gl methods. - GL gl = getGL(); - - Class tableClass = table.getClass(); - - java.lang.reflect.Field[] fields = tableClass.getDeclaredFields(); - - for (int i = 0; i < fields.length; ++i) { - String addressFieldName = fields[i].getName(); - if (!addressFieldName.startsWith(GLEmitter.PROCADDRESS_VAR_PREFIX)) - { - // not a proc address variable - continue; - } - int startOfMethodName = GLEmitter.PROCADDRESS_VAR_PREFIX.length(); - String glFuncName = addressFieldName.substring(startOfMethodName); - try - { - java.lang.reflect.Field addressField = tableClass.getDeclaredField(addressFieldName); - assert(addressField.getType() == Long.TYPE); - // get the current value of the proc address variable in the table object - long oldProcAddress = addressField.getLong(table); - long newProcAddress = WGL.wglGetProcAddress(glFuncName); - /* - System.err.println( - "!!! Address=" + (newProcAddress == 0 - ? "<NULL> " - : ("0x" + - Long.toHexString(newProcAddress))) + - "\tGL func: " + glFuncName); - */ - // set the current value of the proc address variable in the table object - addressField.setLong(gl, newProcAddress); - } catch (Exception e) { - throw new GLException( - "Cannot get GL proc address for method \"" + - glFuncName + "\": Couldn't get value of field \"" + addressFieldName + - "\" in class " + tableClass.getName(), e); - } - } - + resetProcAddressTable(getGLProcAddressTable()); } - public net.java.games.jogl.impl.ProcAddressTable getGLProcAddressTable() { + public GLProcAddressTable getGLProcAddressTable() { if (glProcAddressTable == null) { // FIXME: cache ProcAddressTables by capability bits so we can // share them among contexts with the same capabilities - glProcAddressTable = - new net.java.games.jogl.impl.ProcAddressTable(); + glProcAddressTable = new GLProcAddressTable(); } return glProcAddressTable; } @@ -234,10 +201,6 @@ public abstract class WindowsGLContext extends GLContext { // Internals only below this point // - // Table that holds the addresses of the native C-language entry points for - // OpenGL functions. - private net.java.games.jogl.impl.ProcAddressTable glProcAddressTable; - protected JAWT getJAWT() { if (jawt == null) { JAWT j = new JAWT(); diff --git a/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java b/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java index 300e9cb85..484c8f8af 100644 --- a/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java +++ b/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java @@ -119,6 +119,10 @@ public class WindowsPbufferGLContext extends WindowsGLContext { public void createPbuffer(long parentHdc, long parentHglrc) { GL gl = getGL(); + // Must initally grab OpenGL function pointers while parent's + // context is current because otherwise we don't have the wgl + // extensions available to us + resetGLFunctionAvailability(); int[] iattributes = new int [2*MAX_ATTRIBS]; float[] fattributes = new float[2*MAX_ATTRIBS]; diff --git a/src/net/java/games/jogl/impl/x11/X11GLContext.java b/src/net/java/games/jogl/impl/x11/X11GLContext.java index 8e9804578..ec430bfe8 100644 --- a/src/net/java/games/jogl/impl/x11/X11GLContext.java +++ b/src/net/java/games/jogl/impl/x11/X11GLContext.java @@ -41,7 +41,7 @@ package net.java.games.jogl.impl.x11; import java.awt.Component; import java.util.*; -import net.java.games.gluegen.opengl.*; // for PROCADDRESS_VAR_PREFIX +import net.java.games.gluegen.runtime.*; // for PROCADDRESS_VAR_PREFIX import net.java.games.jogl.*; import net.java.games.jogl.impl.*; @@ -53,6 +53,11 @@ public abstract class X11GLContext extends GLContext { private boolean glXQueryExtensionsStringInitialized; private boolean glXQueryExtensionsStringAvailable; private static final Map/*<String, String>*/ functionNameMap; + private boolean isGLX13; + // Table that holds the addresses of the native C-language entry points for + // OpenGL functions. + private GLProcAddressTable glProcAddressTable; + private static boolean haveResetGLXProcAddressTable; static { functionNameMap = new HashMap(); @@ -105,6 +110,14 @@ public abstract class X11GLContext extends GLContext { */ protected abstract void create(); + public boolean isExtensionAvailable(String glExtensionName) { + if (glExtensionName.equals("GL_ARB_pbuffer") || + glExtensionName.equals("GL_ARB_pixel_format")) { + return isGLX13; + } + return super.isExtensionAvailable(glExtensionName); + } + protected synchronized boolean makeCurrent(Runnable initAction) throws GLException { boolean created = false; if (context == 0) { @@ -140,72 +153,44 @@ public abstract class X11GLContext extends GLContext { protected abstract void swapBuffers() throws GLException; + protected long dynamicLookupFunction(String glFuncName) { + long res = GLX.glXGetProcAddressARB(glFuncName); + if (res == 0) { + // GLU routines aren't known to the OpenGL function lookup + res = GLX.dlsym(glFuncName); + } + return res; + } protected void resetGLFunctionAvailability() { super.resetGLFunctionAvailability(); - resetGLProcAddressTable(); - } - - protected void resetGLProcAddressTable() { - if (DEBUG) { System.err.println("!!! Initializing OpenGL extension address table"); } + resetProcAddressTable(getGLProcAddressTable()); - net.java.games.jogl.impl.ProcAddressTable table = getGLProcAddressTable(); - - // if GL is no longer an interface, we'll have to re-implement the code - // below so it only iterates through gl methods (a non-interface might - // have constructors, custom methods, etc). For now we assume all methods - // will be gl methods. - GL gl = getGL(); - - Class tableClass = table.getClass(); - - java.lang.reflect.Field[] fields = tableClass.getDeclaredFields(); - - for (int i = 0; i < fields.length; ++i) { - String addressFieldName = fields[i].getName(); - if (!addressFieldName.startsWith(GLEmitter.PROCADDRESS_VAR_PREFIX)) - { - // not a proc address variable - continue; - } - int startOfMethodName = GLEmitter.PROCADDRESS_VAR_PREFIX.length(); - String glFuncName = addressFieldName.substring(startOfMethodName); - try - { - java.lang.reflect.Field addressField = tableClass.getDeclaredField(addressFieldName); - assert(addressField.getType() == Long.TYPE); - // get the current value of the proc address variable in the table object - long oldProcAddress = addressField.getLong(table); - long newProcAddress = GLX.glXGetProcAddressARB(glFuncName); - /* - System.err.println( - "!!! Address=" + (newProcAddress == 0 - ? "<NULL> " - : ("0x" + - Long.toHexString(newProcAddress))) + - "\tGL func: " + glFuncName); - */ - // set the current value of the proc address variable in the table object - addressField.setLong(gl, newProcAddress); - } catch (Exception e) { - throw new GLException( - "Cannot get GL proc address for method \"" + - glFuncName + "\": Couldn't get value of field \"" + addressFieldName + - "\" in class " + tableClass.getName(), e); - } + if (!haveResetGLXProcAddressTable) { + resetProcAddressTable(GLX.getGLXProcAddressTable()); } + // Figure out whether we are running GLX version 1.3 or above and + // therefore have pbuffer support + if (display == 0) { + throw new GLException("Expected non-null DISPLAY for querying GLX version"); + } + int[] major = new int[1]; + int[] minor = new int[1]; + if (!GLX.glXQueryVersion(display, major, minor)) { + throw new GLException("glXQueryVersion failed"); + } + isGLX13 = ((major[0] > 1) || (minor[0] > 2)); } - public net.java.games.jogl.impl.ProcAddressTable getGLProcAddressTable() { + public GLProcAddressTable getGLProcAddressTable() { if (glProcAddressTable == null) { // FIXME: cache ProcAddressTables by capability bits so we can // share them among contexts with the same capabilities - glProcAddressTable = - new net.java.games.jogl.impl.ProcAddressTable(); + glProcAddressTable = new GLProcAddressTable(); } return glProcAddressTable; } @@ -245,10 +230,6 @@ public abstract class X11GLContext extends GLContext { // Internals only below this point // - // Table that holds the addresses of the native C-language entry points for - // OpenGL functions. - private net.java.games.jogl.impl.ProcAddressTable glProcAddressTable; - protected JAWT getJAWT() { if (jawt == null) { JAWT j = new JAWT(); diff --git a/src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java b/src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java index 496e113a5..385874da7 100644 --- a/src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java +++ b/src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java @@ -40,6 +40,8 @@ package net.java.games.jogl.impl.x11; import java.awt.Component; +import java.util.*; + import net.java.games.jogl.*; import net.java.games.jogl.impl.*; @@ -49,6 +51,9 @@ public class X11OnscreenGLContext extends X11GLContext { private JAWT_DrawingSurfaceInfo dsi; private JAWT_X11DrawingSurfaceInfo x11dsi; + // Variables for pbuffer support + List pbuffersToInstantiate = new ArrayList(); + public X11OnscreenGLContext(Component component, GLCapabilities capabilities, GLCapabilitiesChooser chooser) { super(component, capabilities, chooser); } @@ -75,14 +80,16 @@ public class X11OnscreenGLContext extends X11GLContext { } public boolean canCreatePbufferContext() { - // For now say no - return false; + // FIXME: should we gate this on GLX 1.3 being available? + return true; } public synchronized GLContext createPbufferContext(GLCapabilities capabilities, int initialWidth, int initialHeight) { - throw new GLException("Not yet supported"); + X11PbufferGLContext ctx = new X11PbufferGLContext(capabilities, initialWidth, initialHeight); + pbuffersToInstantiate.add(ctx); + return ctx; } public void bindPbufferToTexture() { @@ -98,7 +105,16 @@ public class X11OnscreenGLContext extends X11GLContext { if (!lockSurface()) { return false; } - return super.makeCurrent(initAction); + boolean ret = super.makeCurrent(initAction); + if (ret) { + // Instantiate any pending pbuffers + while (!pbuffersToInstantiate.isEmpty()) { + X11PbufferGLContext ctx = + (X11PbufferGLContext) pbuffersToInstantiate.remove(pbuffersToInstantiate.size() - 1); + ctx.createPbuffer(display, context); + } + } + return ret; } catch (RuntimeException e) { try { unlockSurface(); diff --git a/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java b/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java new file mode 100644 index 000000000..264d60fc7 --- /dev/null +++ b/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package net.java.games.jogl.impl.x11; + +import net.java.games.jogl.*; +import net.java.games.jogl.impl.*; + +public class X11PbufferGLContext extends X11GLContext { + private static final boolean DEBUG = false; + + private int initWidth; + private int initHeight; + + private long buffer; // GLXPbuffer + private GLXFBConfig fbConfig; + private int width; + private int height; + + // FIXME: kept around because we create the OpenGL context lazily to + // better integrate with the X11GLContext framework + private long parentContext; + + private static final int MAX_PFORMATS = 256; + private static final int MAX_ATTRIBS = 256; + + // FIXME: figure out how to support render-to-texture and + // render-to-texture-rectangle (which appear to be supported, though + // it looks like floating-point buffers are not) + + public X11PbufferGLContext(GLCapabilities capabilities, int initialWidth, int initialHeight) { + super(null, capabilities, null); + this.initWidth = initialWidth; + this.initHeight = initialHeight; + if (initWidth <= 0 || initHeight <= 0) { + throw new GLException("Initial width and height of pbuffer must be positive (were (" + + initWidth + ", " + initHeight + "))"); + } + } + + public boolean canCreatePbufferContext() { + return false; + } + + public GLContext createPbufferContext(GLCapabilities capabilities, + int initialWidth, + int initialHeight) { + throw new GLException("Not supported"); + } + + public void bindPbufferToTexture() { + // FIXME: figure out how to implement this + throw new GLException("Not yet implemented"); + } + + public void releasePbufferFromTexture() { + // FIXME: figure out how to implement this + throw new GLException("Not yet implemented"); + } + + public void createPbuffer(long display, long parentContext) { + if (display == 0) { + throw new GLException("Null display"); + } + + if (parentContext == 0) { + throw new GLException("Null parentContext"); + } + + if (capabilities.getOffscreenFloatingPointBuffers()) { + throw new GLException("Floating-point pbuffers not supported yet on X11"); + } + + if (capabilities.getOffscreenRenderToTexture()) { + throw new GLException("Render-to-texture pbuffers not supported yet on X11"); + } + + if (capabilities.getOffscreenRenderToTextureRectangle()) { + throw new GLException("Render-to-texture-rectangle pbuffers not supported yet on X11"); + } + + int[] iattributes = new int [2*MAX_ATTRIBS]; + float[] fattributes = new float[2*MAX_ATTRIBS]; + int nfattribs = 0; + int niattribs = 0; + + // Since we are trying to create a pbuffer, the GLXFBConfig we + // request (and subsequently use) must be "p-buffer capable". + iattributes[niattribs++] = GL.GLX_DRAWABLE_TYPE; + iattributes[niattribs++] = GL.GLX_PBUFFER_BIT; + + iattributes[niattribs++] = GL.GLX_RENDER_TYPE; + iattributes[niattribs++] = GL.GLX_RGBA_BIT; + + iattributes[niattribs++] = GLX.GLX_DOUBLEBUFFER; + if (capabilities.getDoubleBuffered()) { + iattributes[niattribs++] = GL.GL_TRUE; + } else { + iattributes[niattribs++] = GL.GL_FALSE; + } + + iattributes[niattribs++] = GLX.GLX_DEPTH_SIZE; + iattributes[niattribs++] = capabilities.getDepthBits(); + + iattributes[niattribs++] = GLX.GLX_RED_SIZE; + iattributes[niattribs++] = capabilities.getRedBits(); + + iattributes[niattribs++] = GLX.GLX_GREEN_SIZE; + iattributes[niattribs++] = capabilities.getGreenBits(); + + iattributes[niattribs++] = GLX.GLX_BLUE_SIZE; + iattributes[niattribs++] = capabilities.getBlueBits(); + + iattributes[niattribs++] = GLX.GLX_ALPHA_SIZE; + iattributes[niattribs++] = capabilities.getAlphaBits(); + + if (capabilities.getStencilBits() > 0) { + iattributes[niattribs++] = GLX.GLX_STENCIL_SIZE; + iattributes[niattribs++] = capabilities.getStencilBits(); + } + + if (capabilities.getAccumRedBits() > 0 || + capabilities.getAccumGreenBits() > 0 || + capabilities.getAccumBlueBits() > 0) { + iattributes[niattribs++] = GLX.GLX_ACCUM_RED_SIZE; + iattributes[niattribs++] = capabilities.getAccumRedBits(); + iattributes[niattribs++] = GLX.GLX_ACCUM_GREEN_SIZE; + iattributes[niattribs++] = capabilities.getAccumGreenBits(); + iattributes[niattribs++] = GLX.GLX_ACCUM_BLUE_SIZE; + iattributes[niattribs++] = capabilities.getAccumBlueBits(); + } + + iattributes[niattribs++] = 0; // null-terminate + + int screen = 0; // FIXME: provide way to specify this? + int[] nelementsTmp = new int[1]; + GLXFBConfig[] fbConfigs = GLX.glXChooseFBConfig(display, screen, iattributes, nelementsTmp); + if (fbConfigs == null || fbConfigs.length == 0 || fbConfigs[0] == null) { + throw new GLException("pbuffer creation error: glXChooseFBConfig() failed"); + } + // Note that we currently don't allow selection of anything but + // the first GLXFBConfig in the returned list + GLXFBConfig fbConfig = fbConfigs[0]; + int nelements = nelementsTmp[0]; + if (nelements <= 0) { + throw new GLException("pbuffer creation error: couldn't find a suitable frame buffer configuration"); + } + + if (DEBUG) { + System.err.println("Found " + fbConfigs.length + " matching GLXFBConfigs"); + System.err.println("Parameters of default one:"); + System.err.println("render type: 0x" + Integer.toHexString(queryFBConfig(display, fbConfig, GLX.GLX_RENDER_TYPE))); + System.err.println("rgba: " + ((queryFBConfig(display, fbConfig, GLX.GLX_RENDER_TYPE) & GLX.GLX_RGBA_BIT) != 0)); + System.err.println("r: " + queryFBConfig(display, fbConfig, GLX.GLX_RED_SIZE)); + System.err.println("g: " + queryFBConfig(display, fbConfig, GLX.GLX_GREEN_SIZE)); + System.err.println("b: " + queryFBConfig(display, fbConfig, GLX.GLX_BLUE_SIZE)); + System.err.println("a: " + queryFBConfig(display, fbConfig, GLX.GLX_ALPHA_SIZE)); + System.err.println("depth: " + queryFBConfig(display, fbConfig, GLX.GLX_DEPTH_SIZE)); + System.err.println("double buffered: " + queryFBConfig(display, fbConfig, GLX.GLX_DOUBLEBUFFER)); + } + + // Create the p-buffer. + niattribs = 0; + + iattributes[niattribs++] = GL.GLX_PBUFFER_WIDTH; + iattributes[niattribs++] = initWidth; + iattributes[niattribs++] = GL.GLX_PBUFFER_HEIGHT; + iattributes[niattribs++] = initHeight; + + iattributes[niattribs++] = 0; + + int 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"); + } + + // Set up instance variables + this.display = display; + this.parentContext = parentContext; + buffer = tmpBuffer; + this.fbConfig = fbConfig; + + // Determine the actual width and height we were able to create. + int[] tmp = new int[1]; + GLX.glXQueryDrawable(display, (int) buffer, GL.GLX_WIDTH, tmp); + width = tmp[0]; + GLX.glXQueryDrawable(display, (int) buffer, GL.GLX_HEIGHT, tmp); + height = tmp[0]; + + if (DEBUG) { + System.err.println("Created pbuffer " + width + " x " + height); + } + } + + protected synchronized boolean makeCurrent(Runnable initAction) throws GLException { + if (buffer == 0) { + // pbuffer not instantiated yet + return false; + } + + lockAWT(); + try { + boolean created = false; + if (context == 0) { + create(); + if (DEBUG) { + System.err.println("!!! Created GL context for " + getClass().getName()); + } + 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)) { + throw new GLException("Error making context current"); + } + + if (created) { + resetGLFunctionAvailability(); + initAction.run(); + } + return true; + } finally { + unlockAWT(); + } + } + + protected synchronized void free() throws GLException { + lockAWT(); + try { + if (!GLX.glXMakeContextCurrent(display, 0, 0, 0)) { + throw new GLException("Error freeing OpenGL context"); + } + } finally { + unlockAWT(); + } + } + + public void handleModeSwitch(long parentHdc, long parentHglrc) { + throw new GLException("Not yet implemented"); + } + + protected boolean isOffscreen() { + // FIXME: currently the only caller of this won't cause proper + // resizing of the pbuffer anyway. + return false; + } + + public int getOffscreenContextBufferedImageType() { + throw new GLException("Should not call this"); + } + + public int getOffscreenContextReadBuffer() { + throw new GLException("Should not call this"); + } + + public boolean offscreenImageNeedsVerticalFlip() { + throw new GLException("Should not call this"); + } + + protected void create() { + if (DEBUG) { + System.err.println("Creating context for pbuffer " + width + " x " + height); + } + + // Create a gl context for the p-buffer. + // FIXME: provide option to not share display lists with subordinate pbuffer? + context = GLX.glXCreateNewContext(display, fbConfig, GL.GLX_RGBA_TYPE, parentContext, true); + if (context == 0) { + throw new GLException("pbuffer creation error: glXCreateNewContext() failed"); + } + + if (DEBUG) { + System.err.println("Created context for pbuffer " + width + " x " + height); + } + } + + protected void swapBuffers() throws GLException { + // FIXME: do we need to do anything if the pbuffer is double-buffered? + } + + private int queryFBConfig(long display, GLXFBConfig fbConfig, int attrib) { + int[] tmp = new int[1]; + if (GLX.glXGetFBConfigAttrib(display, fbConfig, attrib, tmp) != 0) { + throw new GLException("glXGetFBConfigAttrib failed"); + } + 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(); + } +} |