diff options
Diffstat (limited to 'src')
173 files changed, 9272 insertions, 3469 deletions
diff --git a/src/jogl/classes/com/jogamp/gluegen/opengl/BuildComposablePipeline.java b/src/jogl/classes/com/jogamp/gluegen/opengl/BuildComposablePipeline.java index f00f61116..75067a3b7 100644 --- a/src/jogl/classes/com/jogamp/gluegen/opengl/BuildComposablePipeline.java +++ b/src/jogl/classes/com/jogamp/gluegen/opengl/BuildComposablePipeline.java @@ -51,6 +51,8 @@ import java.lang.reflect.Method; import java.nio.Buffer; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -208,29 +210,39 @@ public class BuildComposablePipeline { final List<Method> publicMethodsRaw = Arrays.asList(classToComposeAround.getMethods()); - final Set<PlainMethod> publicMethodsPlain = new HashSet<PlainMethod>(); + final Set<PlainMethod> publicMethodsPlainSet = new HashSet<PlainMethod>(); for (final Iterator<Method> iter = publicMethodsRaw.iterator(); iter.hasNext();) { final Method method = iter.next(); // Don't hook methods which aren't real GL methods, // such as the synthetic "isGL2ES2" "getGL2ES2" final String name = method.getName(); - if ( !name.startsWith("getGL") && - !name.startsWith("isGL") && - !name.equals("getDownstreamGL") && + if ( !name.equals("getDownstreamGL") && !name.equals("toString") ) { - final boolean runHooks = name.startsWith("gl") || addedGLHooks.containsKey(name); - publicMethodsPlain.add(new PlainMethod(method, runHooks)); + final boolean syntheticIsGL = name.startsWith("isGL"); + final boolean syntheticGetGL = name.startsWith("getGL"); + final boolean runHooks = name.startsWith("gl") || syntheticIsGL || syntheticGetGL || addedGLHooks.containsKey(name); + publicMethodsPlainSet.add(new PlainMethod(method, runHooks, syntheticIsGL, syntheticGetGL)); } } + // sort methods to make them easier to find + final List<PlainMethod> publicMethodsPlainSorted = new ArrayList<PlainMethod>(); + publicMethodsPlainSorted.addAll(publicMethodsPlainSet); + Collections.sort(publicMethodsPlainSorted, new Comparator<PlainMethod>() { + @Override + public int compare(final PlainMethod o1, final PlainMethod o2) { + return o1.getWrappedMethod().getName().compareTo(o2.getWrappedMethod().getName()); + } + }); + if (0 != (mode & GEN_DEBUG)) { - (new DebugPipeline(outputDir, outputPackage, classToComposeAround, classDownstream)).emit(publicMethodsPlain.iterator()); + (new DebugPipeline(outputDir, outputPackage, classToComposeAround, classDownstream)).emit(publicMethodsPlainSorted.iterator()); } if (0 != (mode & GEN_TRACE)) { - (new TracePipeline(outputDir, outputPackage, classToComposeAround, classDownstream)).emit(publicMethodsPlain.iterator()); + (new TracePipeline(outputDir, outputPackage, classToComposeAround, classDownstream)).emit(publicMethodsPlainSorted.iterator()); } if (0 != (mode & GEN_CUSTOM)) { - (new CustomPipeline(mode, outputDir, outputPackage, outputName, classToComposeAround, classPrologOpt, classDownstream)).emit(publicMethodsPlain.iterator()); + (new CustomPipeline(mode, outputDir, outputPackage, outputName, classToComposeAround, classPrologOpt, classDownstream)).emit(publicMethodsPlainSorted.iterator()); } } @@ -255,12 +267,16 @@ public class BuildComposablePipeline { //------------------------------------------------------- protected static class PlainMethod { - Method m; - boolean runHooks; + final Method m; + final boolean runHooks; + final boolean isSynthethicIsGL; + final boolean isSynthethicGetGL; - PlainMethod(final Method m, final boolean runHooks) { + PlainMethod(final Method m, final boolean runHooks, final boolean isSynthethicIsGL, final boolean isSynthethicGetGL) { this.m = m; this.runHooks = runHooks; + this.isSynthethicIsGL = isSynthethicIsGL; + this.isSynthethicGetGL = isSynthethicGetGL; } public Method getWrappedMethod() { @@ -271,6 +287,10 @@ public class BuildComposablePipeline { return runHooks; } + public boolean isSynthetic() { return isSynthethicIsGL || isSynthethicGetGL; } + public boolean isSyntheticIsGL() { return isSynthethicIsGL; } + public boolean isSyntheticGetGL() { return isSynthethicGetGL; } + @Override public boolean equals(final Object obj) { if (obj instanceof PlainMethod) { @@ -309,6 +329,7 @@ public class BuildComposablePipeline { argsString.append(")"); return m.toString() + "\n\tname: " + m.getName() + + "\n\tsynt: isGL " + isSynthethicIsGL+", getGL "+isSynthethicGetGL + "\n\tmods: " + m.getModifiers() + "\n\tretu: " + m.getReturnType() + "\n\targs[" + args.length + "]: " + argsString.toString(); @@ -429,15 +450,14 @@ public class BuildComposablePipeline { constructorHook(output); - emitGLIsMethods(output); - emitGLGetMethods(output); + emitSyntheticGLMethods(output); while (methodsToWrap.hasNext()) { final PlainMethod pm = methodsToWrap.next(); final Method m = pm.getWrappedMethod(); emitMethodDocComment(output, m); emitSignature(output, m); - emitBody(output, m, pm.runHooks()); + emitBody(output, pm); } postMethodEmissionHook(output); @@ -479,12 +499,14 @@ public class BuildComposablePipeline { getArgListAsString(m, true, true)); } - protected void emitBody(final PrintWriter output, final Method m, final boolean runHooks) { + protected void emitBody(final PrintWriter output, final PlainMethod pm) { + final boolean runHooks = pm.runHooks(); + final Method m = pm.getWrappedMethod(); output.println(" {"); final Class<?> retType = m.getReturnType(); - final boolean callPreDownstreamHook = runHooks && hasPreDownstreamCallHook(m); - final boolean callPostDownstreamHook = runHooks && hasPostDownstreamCallHook(m); + final boolean callPreDownstreamHook = runHooks && hasPreDownstreamCallHook(pm); + final boolean callPostDownstreamHook = runHooks && hasPostDownstreamCallHook(pm); final boolean callDownstream = (null != getMethod(downstreamClass, m)) && !(0 != (GEN_PROLOG_XOR_DOWNSTREAM & getMode()) && callPreDownstreamHook); final boolean hasResult = (retType != Void.TYPE); @@ -514,38 +536,44 @@ public class BuildComposablePipeline { output.print(" return "); } } - preDownstreamCallHook(output, m); + preDownstreamCallHook(output, pm); } if (callDownstream) { - if (hasResult) { - if (callPostDownstreamHook) { - output.print(" " + JavaType.createForClass(retType).getName()); - output.print(" _res = "); - } else { - output.print(" return "); + if( pm.isSyntheticIsGL() ) { + emitGLIsMethodBody(output, pm); + } else if( pm.isSyntheticGetGL() ) { + emitGLGetMethodBody(output, pm); + } else { + if (hasResult) { + if (callPostDownstreamHook) { + output.print(" " + JavaType.createForClass(retType).getName()); + output.print(" _res = "); + } else { + output.print(" return "); + } } + else { + output.print(" "); + } + output.print(getDownstreamObjectName()); + output.print('.'); + output.print(m.getName()); + output.print('('); + output.print(getArgListAsString(m, false, true)); + output.println(");"); } - else { - output.print(" "); - } - output.print(getDownstreamObjectName()); - output.print('.'); - output.print(m.getName()); - output.print('('); - output.print(getArgListAsString(m, false, true)); - output.println(");"); } if (callPostDownstreamHook) { - postDownstreamCallHook(output, m); + postDownstreamCallHook(output, pm); } if (hasResult && callDownstream && callPostDownstreamHook) { output.println(" return _res;"); } - output.println(" }"); + output.println(" }"); } protected String getArgListAsString(final Method m, final boolean includeArgTypes, final boolean includeArgNames) { @@ -619,17 +647,17 @@ public class BuildComposablePipeline { /** * Called before the pipeline routes the call to the downstream object. */ - protected abstract void preDownstreamCallHook(PrintWriter output, Method m); + protected abstract void preDownstreamCallHook(PrintWriter output, PlainMethod pm); - protected abstract boolean hasPreDownstreamCallHook(Method m); + protected abstract boolean hasPreDownstreamCallHook(PlainMethod pm); /** * Called after the pipeline has routed the call to the downstream object, * but before the calling function exits or returns a value. */ - protected abstract void postDownstreamCallHook(PrintWriter output, Method m); + protected abstract void postDownstreamCallHook(PrintWriter output, PlainMethod pm); - protected abstract boolean hasPostDownstreamCallHook(Method m); + protected abstract boolean hasPostDownstreamCallHook(PlainMethod pm); protected abstract int getMode(); @@ -643,10 +671,17 @@ public class BuildComposablePipeline { /** * Emits one of the isGL* methods. */ - protected void emitGLIsMethod(final PrintWriter output, final String type) { - output.println(" @Override"); - output.println(" public final boolean is" + type + "() {"); - if( 0 != (GEN_GL_IDENTITY_BY_ASSIGNABLE_CLASS & getMode() ) ) { + protected void emitGLIsMethodBody(final PrintWriter output, final PlainMethod plainMethod) { + final String methodName = plainMethod.getWrappedMethod().getName(); + final String type = methodName.substring(2); + + if( type.equals("GL") ) { + output.println(" return true;"); + } else if( 0 != ( GEN_GL_IDENTITY_BY_ASSIGNABLE_CLASS & getMode() ) && + !type.equals("GLES") && + !type.endsWith("core") && + !type.endsWith("Compatible") ) + { final Class<?> clazz = BuildComposablePipeline.getClass("javax.media.opengl." + type); if (clazz.isAssignableFrom(baseInterfaceClass)) { output.println(" return true;"); @@ -656,111 +691,38 @@ public class BuildComposablePipeline { } else { output.println(" return " + getDownstreamObjectName() + ".is" + type + "();"); } - output.println(" }"); - } - - /** - * Emits all of the isGL* methods. - */ - protected void emitGLIsMethods(final PrintWriter output) { - output.println(" @Override"); - output.println(" public final boolean isGL() {"); - output.println(" return true;"); - output.println(" }"); - emitGLIsMethod(output, "GL4bc"); - emitGLIsMethod(output, "GL4"); - emitGLIsMethod(output, "GL3bc"); - emitGLIsMethod(output, "GL3"); - emitGLIsMethod(output, "GL2"); - emitGLIsMethod(output, "GLES1"); - emitGLIsMethod(output, "GLES2"); - emitGLIsMethod(output, "GLES3"); - emitGLIsMethod(output, "GL2ES1"); - emitGLIsMethod(output, "GL2ES2"); - emitGLIsMethod(output, "GL2ES3"); - emitGLIsMethod(output, "GL3ES3"); - emitGLIsMethod(output, "GL4ES3"); - emitGLIsMethod(output, "GL2GL3"); - if( 0 != (GEN_GL_IDENTITY_BY_ASSIGNABLE_CLASS & getMode() ) ) { - output.println(" @Override"); - output.println(" public final boolean isGLES() {"); - output.println(" return isGLES3() || isGLES2() || isGLES1();"); - output.println(" }"); - } else { - emitGLIsMethod(output, "GLES"); - } - output.println(" @Override"); - output.println(" public final boolean isGL4core() {"); - output.println(" return " + getDownstreamObjectName() + ".isGL4core();"); - output.println(" }"); - output.println(" @Override"); - output.println(" public final boolean isGL3core() {"); - output.println(" return " + getDownstreamObjectName() + ".isGL3core();"); - output.println(" }"); - output.println(" @Override"); - output.println(" public final boolean isGLcore() {"); - output.println(" return " + getDownstreamObjectName() + ".isGLcore();"); - output.println(" }"); - output.println(" @Override"); - output.println(" public final boolean isGLES2Compatible() {"); - output.println(" return " + getDownstreamObjectName() + ".isGLES2Compatible();"); - output.println(" }"); - output.println(" @Override"); - output.println(" public final boolean isGLES3Compatible() {"); - output.println(" return " + getDownstreamObjectName() + ".isGLES3Compatible();"); - output.println(" }"); } /** * Emits one of the getGL* methods. */ - protected void emitGLGetMethod(final PrintWriter output, final String type) { - output.println(" @Override"); - output.println(" public final javax.media.opengl." + type + " get" + type + "() {"); - final Class<?> clazz = BuildComposablePipeline.getClass("javax.media.opengl." + type); - if (clazz.isAssignableFrom(baseInterfaceClass)) { - if( 0 != (GEN_GL_IDENTITY_BY_ASSIGNABLE_CLASS & getMode() ) ) { - output.println(" return this;"); - } else { + protected void emitGLGetMethodBody(final PrintWriter output, final PlainMethod plainMethod) { + final String methodName = plainMethod.getWrappedMethod().getName(); + final String type = methodName.substring(3); + + if( type.equals("GL") ) { + output.println(" return this;"); + } else if( type.equals("GLProfile") ) { + output.println(" return " + getDownstreamObjectName() + ".getGLProfile();"); + } else { + final Class<?> clazz = BuildComposablePipeline.getClass("javax.media.opengl." + type); + if (clazz.isAssignableFrom(baseInterfaceClass)) { output.println(" if( is" + type + "() ) { return this; }"); output.println(" throw new GLException(\"Not a " + type + " implementation\");"); + } else { + output.println(" throw new GLException(\"Not a " + type + " implementation\");"); } - } else { - output.println(" throw new GLException(\"Not a " + type + " implementation\");"); } - output.println(" }"); } /** - * Emits all of the getGL* methods. + * Emits all synthetic GL* methods, but not isGL* nor getGL* */ - protected void emitGLGetMethods(final PrintWriter output) { - output.println(" @Override"); - output.println(" public final javax.media.opengl.GL getGL() {"); - output.println(" return this;"); - output.println(" }"); - emitGLGetMethod(output, "GL4bc"); - emitGLGetMethod(output, "GL4"); - emitGLGetMethod(output, "GL3bc"); - emitGLGetMethod(output, "GL3"); - emitGLGetMethod(output, "GL2"); - emitGLGetMethod(output, "GLES1"); - emitGLGetMethod(output, "GLES2"); - emitGLGetMethod(output, "GLES3"); - emitGLGetMethod(output, "GL2ES1"); - emitGLGetMethod(output, "GL2ES2"); - emitGLGetMethod(output, "GL2ES3"); - emitGLGetMethod(output, "GL3ES3"); - emitGLGetMethod(output, "GL4ES3"); - emitGLGetMethod(output, "GL2GL3"); + protected void emitSyntheticGLMethods(final PrintWriter output) { output.println(" @Override"); output.println(" public final GL getDownstreamGL() throws GLException {"); output.println(" return " + getDownstreamObjectName() + ";"); output.println(" }"); - output.println(" @Override"); - output.println(" public final GLProfile getGLProfile() {"); - output.println(" return " + getDownstreamObjectName() + ".getGLProfile();"); - output.println(" }"); } } // end class PipelineEmitter @@ -868,12 +830,13 @@ public class BuildComposablePipeline { } @Override - protected boolean hasPreDownstreamCallHook(final Method m) { - return null != getMethod(prologClassOpt, m); + protected boolean hasPreDownstreamCallHook(final PlainMethod pm) { + return null != getMethod(prologClassOpt, pm.getWrappedMethod()); } @Override - protected void preDownstreamCallHook(final PrintWriter output, final Method m) { + protected void preDownstreamCallHook(final PrintWriter output, final PlainMethod pm) { + final Method m = pm.getWrappedMethod(); if (null != prologNameOpt) { output.print(getPrologObjectNameOpt()); output.print('.'); @@ -885,12 +848,12 @@ public class BuildComposablePipeline { } @Override - protected boolean hasPostDownstreamCallHook(final Method m) { + protected boolean hasPostDownstreamCallHook(final PlainMethod pm) { return false; } @Override - protected void postDownstreamCallHook(final PrintWriter output, final Method m) { + protected void postDownstreamCallHook(final PrintWriter output, final PlainMethod pm) { } } // end class CustomPipeline @@ -1025,22 +988,23 @@ public class BuildComposablePipeline { } @Override - protected boolean hasPreDownstreamCallHook(final Method m) { - return true; + protected boolean hasPreDownstreamCallHook(final PlainMethod pm) { + return !pm.isSynthetic(); } @Override - protected void preDownstreamCallHook(final PrintWriter output, final Method m) { + protected void preDownstreamCallHook(final PrintWriter output, final PlainMethod pm) { output.println(" checkContext();"); } @Override - protected boolean hasPostDownstreamCallHook(final Method m) { - return true; + protected boolean hasPostDownstreamCallHook(final PlainMethod pm) { + return !pm.isSynthetic(); } @Override - protected void postDownstreamCallHook(final PrintWriter output, final Method m) { + protected void postDownstreamCallHook(final PrintWriter output, final PlainMethod pm) { + final Method m = pm.getWrappedMethod(); if (m.getName().equals("glBegin")) { output.println(" insideBeginEndPair = true;"); output.println(" // NOTE: can't check glGetError(); it's not allowed inside glBegin/glEnd pair"); @@ -1191,12 +1155,13 @@ public class BuildComposablePipeline { } @Override - protected boolean hasPreDownstreamCallHook(final Method m) { - return true; + protected boolean hasPreDownstreamCallHook(final PlainMethod pm) { + return !pm.isSynthetic(); } @Override - protected void preDownstreamCallHook(final PrintWriter output, final Method m) { + protected void preDownstreamCallHook(final PrintWriter output, final PlainMethod pm) { + final Method m = pm.getWrappedMethod(); if (m.getName().equals("glEnd") || m.getName().equals("glEndList")) { output.println(" indent-=2;"); output.println(" printIndent();"); @@ -1210,12 +1175,13 @@ public class BuildComposablePipeline { } @Override - protected boolean hasPostDownstreamCallHook(final Method m) { - return true; + protected boolean hasPostDownstreamCallHook(final PlainMethod pm) { + return !pm.isSynthetic(); } @Override - protected void postDownstreamCallHook(final PrintWriter output, final Method m) { + protected void postDownstreamCallHook(final PrintWriter output, final PlainMethod pm) { + final Method m = pm.getWrappedMethod(); final Class<?> ret = m.getReturnType(); if (ret != Void.TYPE) { output.println(" println(\" = \"+_res);"); diff --git a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java index e7c452fb4..bc500d87f 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java +++ b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java @@ -796,4 +796,10 @@ public class OutlineShape implements Comparable<OutlineShape> { public final int hashCode() { throw new InternalError("hashCode not designed"); } + + @Override + public String toString() { + // Avoid calling this.hashCode() ! + return getClass().getName() + "@" + Integer.toHexString(super.hashCode()); + } } diff --git a/src/jogl/classes/com/jogamp/graph/font/Font.java b/src/jogl/classes/com/jogamp/graph/font/Font.java index 92d35768b..52ad4076d 100644 --- a/src/jogl/classes/com/jogamp/graph/font/Font.java +++ b/src/jogl/classes/com/jogamp/graph/font/Font.java @@ -88,7 +88,7 @@ public interface Font { float getScale(final float pixelSize); /** * @param dest AABBox instance set to this metrics boundary w/ given pixelSize - * @param pixelSize + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)} * @param tmpV3 caller provided temporary 3-component vector * @return the given and set AABBox 'dest' */ @@ -113,14 +113,25 @@ public interface Font { public char getSymbol(); public short getID(); public AABBox getBBox(); + /** + * + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)} + * @return + */ public float getScale(final float pixelSize); /** * @param dest AABBox instance set to this metrics boundary w/ given pixelSize - * @param pixelSize + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)} * @param tmpV3 caller provided temporary 3-component vector * @return the given and set AABBox 'dest' */ public AABBox getBBox(final AABBox dest, final float pixelSize, float[] tmpV3); + /** + * + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)} + * @param useFrationalMetrics + * @return + */ public float getAdvance(final float pixelSize, boolean useFrationalMetrics); public OutlineShape getShape(); public int hashCode(); @@ -153,13 +164,37 @@ public interface Font { */ public float getPixelSize(final float fontSize /* points per inch */, final float resolution); + /** + * + * @param glyphID + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)} + * @return + */ public float getAdvanceWidth(final int glyphID, final float pixelSize); public Metrics getMetrics(); public Glyph getGlyph(final char symbol); public int getNumGlyphs(); + /** + * + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)} + * @return + */ public float getLineHeight(final float pixelSize); + /** + * + * @param string + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)} + * @return + */ public float getMetricWidth(final CharSequence string, final float pixelSize); + /** + * + * @param string + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)} + * @param tmp + * @return + */ public float getMetricHeight(final CharSequence string, final float pixelSize, final AABBox tmp); /** * Return the <i>layout</i> bounding box as computed by each glyph's metrics. @@ -168,7 +203,7 @@ public interface Font { * See {@link #getPointsBounds(AffineTransform, CharSequence, float, AffineTransform, AffineTransform)} for pixel correct results. * </p> * @param string string text - * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)} */ public AABBox getMetricBounds(final CharSequence string, final float pixelSize); @@ -176,7 +211,7 @@ public interface Font { * Return the bounding box by taking each glyph's point-based bounding box into account. * @param transform optional given transform * @param string string text - * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size. + * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)} * @param temp1 temporary AffineTransform storage, mandatory * @param temp2 temporary AffineTransform storage, mandatory */ diff --git a/src/jogl/classes/com/jogamp/graph/geom/Outline.java b/src/jogl/classes/com/jogamp/graph/geom/Outline.java index 4caf06e5d..075d957ea 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Outline.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Outline.java @@ -278,4 +278,9 @@ public class Outline implements Comparable<Outline> { public final int hashCode() { throw new InternalError("hashCode not designed"); } + @Override + public String toString() { + // Avoid calling this.hashCode() ! + return getClass().getName() + "@" + Integer.toHexString(super.hashCode()); + } } diff --git a/src/jogl/classes/com/jogamp/opengl/FBObject.java b/src/jogl/classes/com/jogamp/opengl/FBObject.java index 8c076e742..03693a688 100644 --- a/src/jogl/classes/com/jogamp/opengl/FBObject.java +++ b/src/jogl/classes/com/jogamp/opengl/FBObject.java @@ -37,12 +37,14 @@ import javax.media.opengl.GL2GL3; import javax.media.opengl.GL3; import javax.media.opengl.GLBase; import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import jogamp.opengl.Debug; +import com.jogamp.common.util.PropertyAccess; import com.jogamp.opengl.FBObject.Attachment.Type; /** @@ -60,15 +62,21 @@ import com.jogamp.opengl.FBObject.Attachment.Type; * <p>FIXME: Implement support for {@link Type#DEPTH_TEXTURE}, {@link Type#STENCIL_TEXTURE} .</p> */ public class FBObject { - protected static final boolean DEBUG = Debug.debug("FBObject"); + protected static final boolean DEBUG; + private static final int USER_MAX_TEXTURE_SIZE; private static final boolean FBOResizeQuirk = false; + static { + Debug.initSingleton(); + DEBUG = Debug.debug("FBObject"); + USER_MAX_TEXTURE_SIZE = PropertyAccess.getIntProperty("jogl.debug.FBObject.MaxTextureSize", true, 0); + } + private static enum DetachAction { NONE, DISPOSE, RECREATE }; /** - * Marker interface, denotes a color buffer attachment. + * Generic color buffer FBO attachment, either of type {@link ColorAttachment} or {@link TextureAttachment}. * <p>Always an instance of {@link Attachment}.</p> - * <p>Either an instance of {@link ColorAttachment} or {@link TextureAttachment}.</b> */ public static interface Colorbuffer { /** @@ -76,23 +84,55 @@ public class FBObject { * @return <code>true</code> if newly initialized, otherwise <code>false</code>. * @throws GLException if buffer generation or setup fails. The just created buffer name will be deleted in this case. */ - public boolean initialize(GL gl) throws GLException; + boolean initialize(final GL gl) throws GLException; /** * Releases the color buffer if initialized, i.e. name is not <code>zero</code>. * @throws GLException if buffer release fails. */ - public void free(GL gl) throws GLException; + void free(final GL gl) throws GLException; /** * Writes the internal format to the given GLCapabilities object. * @param caps the destination for format bits * @param rgba8Avail whether rgba8 is available */ - public void formatToGLCapabilities(GLCapabilities caps, boolean rgba8Avail); + void formatToGLCapabilities(final GLCapabilities caps, final boolean rgba8Avail); + + /** + * Returns <code>true</code> if instance is of type {@link TextureAttachment} + * and <code>false</code> if instance is of type {@link ColorAttachment}. + */ + boolean isTextureAttachment(); + + /** + * Casts this object to a {@link TextureAttachment} reference, see {@link #isTextureAttachment()}. + * @throws GLException if this object is not of type {@link TextureAttachment} + * @see #isTextureAttachment() + */ + TextureAttachment getTextureAttachment(); + + /** + * Casts this object to a {@link ColorAttachment} reference, see {@link #isTextureAttachment()}. + * @throws GLException if this object is not of type {@link ColorAttachment} + * @see #isTextureAttachment() + */ + ColorAttachment getColorAttachment(); + + /** internal format of colorbuffer */ + int getFormat(); + + /** width of colorbuffer */ + int getWidth(); + + /** height of colorbuffer */ + int getHeight(); + + /** colorbuffer name [1..max] */ + int getName(); } - /** Common super class of all attachments */ + /** Common super class of all FBO attachments */ public static abstract class Attachment { public enum Type { NONE, DEPTH, STENCIL, DEPTH_STENCIL, COLOR, COLOR_TEXTURE, DEPTH_TEXTURE, STENCIL_TEXTURE; @@ -220,6 +260,9 @@ public class FBObject { } } + /** immutable internal format of attachment */ + public final int getFormat() { return format; } + /** width of attachment */ public final int getWidth() { return width; } /** height of attachment */ @@ -242,7 +285,7 @@ public class FBObject { * @return <code>true</code> if newly initialized, otherwise <code>false</code>. * @throws GLException if buffer generation or setup fails. The just created buffer name will be deleted in this case. */ - public abstract boolean initialize(GL gl) throws GLException; + public abstract boolean initialize(final GL gl) throws GLException; /** * Releases the attachment if initialized, i.e. name is not <code>zero</code>. @@ -254,7 +297,7 @@ public class FBObject { * </pre> * @throws GLException if buffer release fails. */ - public abstract void free(GL gl) throws GLException; + public abstract void free(final GL gl) throws GLException; /** * <p> @@ -379,8 +422,10 @@ public class FBObject { public boolean initialize(final GL gl) throws GLException { final boolean init = 0 == getName(); if( init ) { - checkPreGLError(gl); - + final boolean checkError = DEBUG || GLContext.DEBUG_GL; + if( checkError ) { + checkPreGLError(gl); + } final int[] name = new int[] { -1 }; gl.glGenRenderbuffers(1, name, 0); setName(name[0]); @@ -391,11 +436,13 @@ public class FBObject { } else { gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, format, getWidth(), getHeight()); } - final int glerr = gl.glGetError(); - if(GL.GL_NO_ERROR != glerr) { - gl.glDeleteRenderbuffers(1, name, 0); - setName(0); - throw new GLException("GL Error "+toHexString(glerr)+" while creating "+this); + if( checkError ) { + final int glerr = gl.glGetError(); + if(GL.GL_NO_ERROR != glerr) { + gl.glDeleteRenderbuffers(1, name, 0); + setName(0); + throw new GLException("GL Error "+toHexString(glerr)+" while creating "+this); + } } if(DEBUG) { System.err.println("Attachment.init.X: "+this); @@ -423,14 +470,21 @@ public class FBObject { } } - /** Color render buffer attachment */ + /** Color render buffer FBO attachment */ public static class ColorAttachment extends RenderAttachment implements Colorbuffer { public ColorAttachment(final int iFormat, final int samples, final int width, final int height, final int name) { super(Type.COLOR, iFormat, samples, width, height, name); } + @Override + public final boolean isTextureAttachment() { return false; } + @Override + public final TextureAttachment getTextureAttachment() { throw new GLException("Not a TextureAttachment, but ColorAttachment"); } + @Override + public final ColorAttachment getColorAttachment() { return this; } + } - /** Texture attachment */ + /** Texture FBO attachment */ public static class TextureAttachment extends Attachment implements Colorbuffer { /** details of the texture setup */ public final int dataFormat, dataType, magFilter, minFilter, wrapS, wrapT; @@ -478,8 +532,10 @@ public class FBObject { public boolean initialize(final GL gl) throws GLException { final boolean init = 0 == getName(); if( init ) { - checkPreGLError(gl); - + final boolean checkError = DEBUG || GLContext.DEBUG_GL; + if( checkError ) { + checkPreGLError(gl); + } final int[] name = new int[] { -1 }; gl.glGenTextures(1, name, 0); if(0 == name[0]) { @@ -500,17 +556,21 @@ public class FBObject { if( 0 < wrapT ) { gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, wrapT); } - boolean preTexImage2D = true; - int glerr = gl.glGetError(); - if(GL.GL_NO_ERROR == glerr) { - preTexImage2D = false; + if( checkError ) { + boolean preTexImage2D = true; + int glerr = gl.glGetError(); + if(GL.GL_NO_ERROR == glerr) { + preTexImage2D = false; + gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, format, getWidth(), getHeight(), 0, dataFormat, dataType, null); + glerr = gl.glGetError(); + } + if(GL.GL_NO_ERROR != glerr) { + gl.glDeleteTextures(1, name, 0); + setName(0); + throw new GLException("GL Error "+toHexString(glerr)+" while creating (pre TexImage2D "+preTexImage2D+") "+this); + } + } else { gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, format, getWidth(), getHeight(), 0, dataFormat, dataType, null); - glerr = gl.glGetError(); - } - if(GL.GL_NO_ERROR != glerr) { - gl.glDeleteTextures(1, name, 0); - setName(0); - throw new GLException("GL Error "+toHexString(glerr)+" while creating (pre TexImage2D "+preTexImage2D+") "+this); } if(DEBUG) { System.err.println("Attachment.init.X: "+this); @@ -530,6 +590,14 @@ public class FBObject { setName(0); } } + + @Override + public final boolean isTextureAttachment() { return true; } + @Override + public final TextureAttachment getTextureAttachment() { return this; } + @Override + public final ColorAttachment getColorAttachment() { throw new GLException("Not a ColorAttachment, but TextureAttachment"); } + @Override public String toString() { return getClass().getSimpleName()+"[type "+type+", target GL_TEXTURE_2D, level 0, format "+toHexString(format)+ @@ -566,7 +634,7 @@ public class FBObject { * <p> * For GLES3, sampling-sink format <b>must be equal</b> w/ the sampling-source {@link Colorbuffer}, * see details below. Implementation aligns w/ {@link #createColorAttachment(boolean)} - * and is enforced via {@link #sampleSinkFormatMismatch(GL)}. + * and is enforced via {@link #sampleSinkExFormatMismatch(GL)}. * </p> * <p> * ES3 BlitFramebuffer Requirements: OpenGL ES 3.0.2 p194: 4.3.2 Copying Pixels @@ -607,23 +675,40 @@ public class FBObject { */ public static final TextureAttachment createColorTextureAttachment(final GL gl, final boolean alpha, final int width, final int height, final int magFilter, final int minFilter, final int wrapS, final int wrapT) { - final int textureInternalFormat, textureDataFormat, textureDataType; + final int internalFormat, dataFormat, dataType; if(gl.isGLES3()) { - textureInternalFormat = alpha ? GL.GL_RGBA8 : GL.GL_RGB8; - textureDataFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; - textureDataType = GL.GL_UNSIGNED_BYTE; + internalFormat = alpha ? GL.GL_RGBA8 : GL.GL_RGB8; + dataFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; + dataType = GL.GL_UNSIGNED_BYTE; } else if(gl.isGLES()) { - textureInternalFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; - textureDataFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; - textureDataType = GL.GL_UNSIGNED_BYTE; + internalFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; + dataFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; + dataType = GL.GL_UNSIGNED_BYTE; } else { - textureInternalFormat = alpha ? GL.GL_RGBA8 : GL.GL_RGB8; + internalFormat = alpha ? GL.GL_RGBA8 : GL.GL_RGB8; // textureInternalFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; // textureInternalFormat = alpha ? 4 : 3; - textureDataFormat = alpha ? GL.GL_BGRA : GL.GL_RGB; - textureDataType = alpha ? GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV : GL.GL_UNSIGNED_BYTE; + dataFormat = alpha ? GL.GL_BGRA : GL.GL_RGB; + dataType = alpha ? GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV : GL.GL_UNSIGNED_BYTE; + } + return createColorTextureAttachment(internalFormat, width, height, dataFormat, dataType, magFilter, minFilter, wrapS, wrapT); + } + + public static final TextureAttachment createColorTextureAttachment(final GL gl, final int internalFormat, final int width, final int height, + final int magFilter, final int minFilter, final int wrapS, final int wrapT) { + final int dataFormat, dataType; + final boolean alpha = hasAlpha(internalFormat); + if( gl.isGLES3() ) { + dataFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; + dataType = GL.GL_UNSIGNED_BYTE; + } else if( gl.isGLES() ) { + dataFormat = alpha ? GL.GL_RGBA : GL.GL_RGB; + dataType = GL.GL_UNSIGNED_BYTE; + } else { + dataFormat = alpha ? GL.GL_BGRA : GL.GL_RGB; + dataType = alpha ? GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV : GL.GL_UNSIGNED_BYTE; } - return createColorTextureAttachment(textureInternalFormat, width, height, textureDataFormat, textureDataType, magFilter, minFilter, wrapS, wrapT); + return createColorTextureAttachment(internalFormat, width, height, dataFormat, dataType, magFilter, minFilter, wrapS, wrapT); } /** @@ -677,12 +762,14 @@ public class FBObject { private int fbName; private boolean bound; - private int colorAttachmentCount; - private Colorbuffer[] colorAttachmentPoints; // colorbuffer attachment points + private int colorbufferCount; + private int textureAttachmentCount; + private Colorbuffer[] colorbufferAttachments; // colorbuffer attachment points private RenderAttachment depth, stencil; // depth and stencil maybe equal in case of packed-depth-stencil + private boolean modified; // size, sampleCount, or any attachment modified private FBObject samplingSink; // MSAA sink - private TextureAttachment samplingSinkTexture; + private Colorbuffer samplingColorSink; private boolean samplingSinkDirty; // @@ -693,39 +780,60 @@ public class FBObject { if(!initialized) { throw new GLException("FBO not initialized"); } - if(maxColorAttachments != colorAttachmentPoints.length) { - throw new InternalError("maxColorAttachments "+maxColorAttachments+", array.length "+colorAttachmentPoints.length); + if(maxColorAttachments != colorbufferAttachments.length) { + throw new InternalError(String.format("maxColorAttachments %d, array.length %d", + maxColorAttachments, colorbufferAttachments.length) ); } if(0 > point || point >= maxColorAttachments) { - throw new IllegalArgumentException("attachment point out of range: "+point+", should be within [0.."+(maxColorAttachments-1)+"], "+this); + throw new IllegalArgumentException(String.format("attachment point out of range: %d, should be within [0..%d], %s", + point, maxColorAttachments-1, this.toString() ) ); } } private final void validateAddColorAttachment(final int point, final Colorbuffer ca) { validateColorAttachmentPointRange(point); - if( null != colorAttachmentPoints[point] ) { - throw new IllegalArgumentException("Cannot attach "+ca+", attachment point already in use by "+colorAttachmentPoints[point]+", "+this); + if( null != colorbufferAttachments[point] ) { + throw new IllegalStateException(String.format("Cannot attach %s at %d, attachment point already in use by %s, %s", + ca.toString(), point, colorbufferAttachments[point].toString(), this.toString() ) ); } } - private final void addColorAttachment(final int point, final Colorbuffer ca) { - validateColorAttachmentPointRange(point); - final Colorbuffer c = colorAttachmentPoints[point]; - if( null != c && c != ca ) { - throw new IllegalArgumentException("Add failed: requested to add "+ca+" at "+point+", but slot is holding "+c+"; "+this); + private final void addColorAttachment(final int point, final Colorbuffer ca, final boolean validate) { + final Colorbuffer c = colorbufferAttachments[point]; + if( validate ) { + validateColorAttachmentPointRange(point); + if( null == ca ) { + throw new IllegalArgumentException("Colorbuffer is null"); + } + if( null != c ) { + throw new IllegalStateException(String.format("Cannot attach %s at %d, attachment point already in use by %s, %s", + ca.toString(), point, c.toString(), this.toString() ) ); + } + } + colorbufferAttachments[point] = ca; + colorbufferCount++; + if( ca.isTextureAttachment() ) { + textureAttachmentCount++; } - colorAttachmentPoints[point] = ca; - colorAttachmentCount++; + modified = true; } private final void removeColorAttachment(final int point, final Colorbuffer ca) { validateColorAttachmentPointRange(point); - final Colorbuffer c = colorAttachmentPoints[point]; - if( null != c && c != ca ) { - throw new IllegalArgumentException("Remove failed: requested to removed "+ca+" at "+point+", but slot is holding "+c+"; "+this); + if( null == ca ) { + throw new IllegalArgumentException("Colorbuffer is null"); + } + final Colorbuffer c = colorbufferAttachments[point]; + if( c != ca ) { + throw new IllegalStateException(String.format("Cannot detach %s at %d, slot is holding other: %s, %s", + ca.toString(), point, c.toString(), this.toString() ) ); + } + colorbufferAttachments[point] = null; + colorbufferCount--; + if( ca.isTextureAttachment() ) { + textureAttachmentCount--; } - colorAttachmentPoints[point] = null; - colorAttachmentCount--; + modified = true; } /** @@ -738,7 +846,7 @@ public class FBObject { */ public final Colorbuffer getColorbuffer(final int attachmentPoint) { validateColorAttachmentPointRange(attachmentPoint); - return colorAttachmentPoints[attachmentPoint]; + return colorbufferAttachments[attachmentPoint]; } /** @@ -751,8 +859,8 @@ public class FBObject { * @return -1 if the {@link Colorbuffer} could not be found, otherwise [0..{@link #getMaxColorAttachments()}-1] */ public final int getColorbufferAttachmentPoint(final Colorbuffer ca) { - for(int i=0; i<colorAttachmentPoints.length; i++) { - if( colorAttachmentPoints[i] == ca ) { + for(int i=0; i<colorbufferAttachments.length; i++) { + if( colorbufferAttachments[i] == ca ) { return i; } } @@ -783,7 +891,7 @@ public class FBObject { * otherwise false. */ public final boolean hasAttachmentUsingAlpha() { - final int caCount = getColorAttachmentCount(); + final int caCount = getColorbufferCount(); boolean hasAlpha = false; for(int i=0; i<caCount; i++) { final Attachment ca = (Attachment)getColorbuffer(i); @@ -830,19 +938,37 @@ public class FBObject { this.fbName = 0; this.bound = false; - this.colorAttachmentPoints = null; // at init .. - this.colorAttachmentCount = 0; + this.colorbufferAttachments = null; // at init .. + this.colorbufferCount = 0; + this.textureAttachmentCount = 0; this.depth = null; this.stencil = null; + this.modified = true; this.samplingSink = null; - this.samplingSinkTexture = null; + this.samplingColorSink = null; this.samplingSinkDirty = true; } - private void init(final GL gl, int width, int height, final int samples) throws GLException { - if(initialized) { - throw new GLException("FBO already initialized"); + /** + * Initializes this FBO's instance. + * <p> + * The sampling sink is not initializes, allowing manual assignment via {@link #setSamplingSink(FBObject)} + * if {@code newSamples > 0}. + * </p> + * + * <p>Leaves the FBO bound</p> + * + * @param gl the current GL context + * @param newWidth the initial width, it's minimum is capped to 1 + * @param newHeight the initial height, it's minimum is capped to 1 + * @param newSamples if > 0, MSAA will be used, otherwise no multisampling. Will be capped to {@link #getMaxSamples()}. + * @throws IllegalStateException if already initialized + * @throws GLException in case of an error, i.e. size too big, etc .. + */ + public void init(final GL gl, final int newWidth, final int newHeight, final int newSamples) throws IllegalStateException, GLException { + if( initialized ) { + throw new IllegalStateException("FBO already initialized"); } if( !gl.hasBasicFBOSupport() ) { throw new GLException("FBO not supported w/ context: "+gl.getContext()+", "+this); @@ -878,29 +1004,31 @@ public class FBObject { } maxColorAttachments = realMaxColorAttachments <= 8 ? realMaxColorAttachments : 8; // cap to limit array size - colorAttachmentPoints = new Colorbuffer[maxColorAttachments]; - colorAttachmentCount = 0; + colorbufferAttachments = new Colorbuffer[maxColorAttachments]; + colorbufferCount = 0; + textureAttachmentCount = 0; - maxSamples = gl.getMaxRenderbufferSamples(); + maxSamples = gl.getMaxRenderbufferSamples(); // if > 0 implies fullFBOSupport gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, val, 0); - maxTextureSize = val[0]; + final int _maxTextureSize = val[0]; + if( 0 < USER_MAX_TEXTURE_SIZE ) { + maxTextureSize = USER_MAX_TEXTURE_SIZE; + } else { + maxTextureSize = _maxTextureSize; + } gl.glGetIntegerv(GL.GL_MAX_RENDERBUFFER_SIZE, val, 0); maxRenderbufferSize = val[0]; - checkPreGLError(gl); - - if( 0 >= width ) { width = 1; } - if( 0 >= height ) { height = 1; } - this.width = width; - this.height = height; - this.samples = samples <= maxSamples ? samples : maxSamples; + this.width = 0 < newWidth ? newWidth : 1; + this.height = 0 < newHeight ? newHeight : 1; + this.samples = newSamples <= maxSamples ? newSamples : maxSamples; if(DEBUG) { - System.err.println("FBObject "+width+"x"+height+", "+samples+" -> "+this.samples+" samples"); + System.err.println("FBObject.init() START: "+width+"x"+height+", "+newSamples+" -> "+this.samples+" samples"); System.err.println("fullFBOSupport: "+fullFBOSupport); System.err.println("maxColorAttachments: "+maxColorAttachments+"/"+realMaxColorAttachments+" [capped/real]"); System.err.println("maxSamples: "+maxSamples); - System.err.println("maxTextureSize: "+maxTextureSize); + System.err.println("maxTextureSize: "+_maxTextureSize+" -> "+maxTextureSize); System.err.println("maxRenderbufferSize: "+maxRenderbufferSize); System.err.println("rgba8: "+rgba8Avail); System.err.println("depth24: "+depth24Avail); @@ -912,18 +1040,17 @@ public class FBObject { System.err.println("packedDepthStencil: "+packedDepthStencilAvail); System.err.println("NV_fbo_color_attachments: "+NV_fbo_color_attachments); System.err.println(gl.getContext().getGLVersion()); - System.err.println(JoglVersion.getGLStrings(gl, null).toString()); - System.err.println(gl.getContext()); + System.err.println(JoglVersion.getGLStrings(gl, null, false).toString()); } - checkNoError(null, gl.glGetError(), "FBObject Init.pre"); // throws GLException if error + checkPreGLError(gl); - if(width > 2 + maxTextureSize || height> 2 + maxTextureSize || - width > maxRenderbufferSize || height> maxRenderbufferSize ) { - throw new GLException("size "+width+"x"+height+" exceeds on of the maxima [texture "+maxTextureSize+", renderbuffer "+maxRenderbufferSize+"]"); + if( width > maxRenderbufferSize || height > maxRenderbufferSize ) { + throw new GLException("Size "+width+"x"+height+" exceeds on of the maxima renderbuffer size "+maxRenderbufferSize+": \n\t"+this); } - resetSamplingSink(gl); + modified = true; + samplingSinkDirty = true; // generate fbo .. gl.glGenFramebuffers(1, val, 0); @@ -939,48 +1066,26 @@ public class FBObject { checkNoError(gl, GL.GL_INVALID_VALUE, "FBObject Init.isFB"); // throws GLException } bound = true; - samplingSinkDirty = true; initialized = true; vStatus = GL.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; // always incomplete w/o attachments! if(DEBUG) { - System.err.println("FBObject.init(): "+this); + System.err.println("FBObject.init() END: "+this); + Thread.dumpStack(); } } /** - * Initializes or resets this FBO's instance. - * <p> - * In case the new parameters are compatible with the current ones - * no action will be performed. Otherwise all attachments will be recreated - * to match the new given parameters. - * </p> - * <p> - * Incompatibility and hence recreation is forced if - * the size or sample count doesn't match for subsequent calls. - * </p> - * - * <p>Leaves the FBO bound state untouched</p> - * - * @param gl the current GL context - * @param newWidth - * @param newHeight - * @throws GLException in case of an error - */ - public final void reset(final GL gl, final int newWidth, final int newHeight) { - reset(gl, newWidth, newHeight, 0, false); - } - - /** - * Initializes or resets this FBO's instance. + * Resets this FBO's instance. * <p> * In case the new parameters are compatible with the current ones - * no action will be performed. Otherwise all attachments will be recreated + * no action will be performed and method returns immediately.<br> + * Otherwise all attachments will be recreated * to match the new given parameters. * </p> * <p> - * Currently incompatibility and hence recreation of the attachments will be performed - * if the size or sample count doesn't match for subsequent calls. + * {@link #resetSamplingSink(GL)} is being issued immediately + * to match the new configuration. * </p> * * <p>Leaves the FBO bound state untouched</p> @@ -989,17 +1094,13 @@ public class FBObject { * @param newWidth the new width, it's minimum is capped to 1 * @param newHeight the new height, it's minimum is capped to 1 * @param newSamples if > 0, MSAA will be used, otherwise no multisampling. Will be capped to {@link #getMaxSamples()}. - * @param resetSamplingSink <code>true</code> calls {@link #resetSamplingSink(GL)} immediatly. - * <code>false</code> postpones resetting the sampling sink until {@link #use(GL, TextureAttachment)} or {@link #syncSamplingSink(GL)}, - * allowing to use the samples sink's FBO and texture until then. The latter is useful to benefit - * from implicit double buffering while resetting the sink just before it's being used, eg. at swap-buffer. - * + * @return {@code true} if this instance has been modified, otherwise {@code false}. + * @throws IllegalStateException if not initialized via {@link #init(GL, int, int, int)}. * @throws GLException in case of an error, i.e. size too big, etc .. */ - public final void reset(final GL gl, int newWidth, int newHeight, int newSamples, final boolean resetSamplingSink) { + public final boolean reset(final GL gl, int newWidth, int newHeight, int newSamples) throws GLException, IllegalStateException { if( !initialized ) { - init(gl, newWidth, newHeight, newSamples); - return; + throw new IllegalStateException("FBO not initialized"); } newSamples = newSamples <= maxSamples ? newSamples : maxSamples; // clamp @@ -1007,9 +1108,11 @@ public class FBObject { if( newWidth != width || newHeight != height || newSamples != samples ) { if( 0 >= newWidth ) { newWidth = 1; } if( 0 >= newHeight ) { newHeight = 1; } - if( newWidth > 2 + maxTextureSize || newHeight > 2 + maxTextureSize || - newWidth > maxRenderbufferSize || newHeight > maxRenderbufferSize ) { - throw new GLException("size "+width+"x"+height+" exceeds on of the maxima [texture "+maxTextureSize+", renderbuffer "+maxRenderbufferSize+"]"); + if( textureAttachmentCount > 0 && ( newWidth > 2 + maxTextureSize || newHeight > 2 + maxTextureSize ) ) { + throw new GLException("Size "+newWidth+"x"+newHeight+" exceeds on of the maximum texture size "+maxTextureSize+": \n\t"+this); + } + if( newWidth > maxRenderbufferSize || newHeight > maxRenderbufferSize ) { + throw new GLException("Size "+newWidth+"x"+newHeight+" exceeds on of the maxima renderbuffer size "+maxRenderbufferSize+": \n\t"+this); } if(DEBUG) { @@ -1018,29 +1121,76 @@ public class FBObject { final boolean wasBound = isBound(); + final int sampleCountChange; + if( 0 < samples && 0 < newSamples || 0 == samples && 0 == newSamples ) { + sampleCountChange = 0; // keep MSAA settings + } else if( 0 == samples && 0 < newSamples ) { + sampleCountChange = 1; // add MSAA + } else if( 0 < samples && 0 == newSamples ) { + sampleCountChange = -1; // remove MSAA + } else { + throw new IllegalArgumentException("Error in sampleCount change: "+samples+" -> "+newSamples); + } width = newWidth; height = newHeight; samples = newSamples; - if(0 < samples && null == samplingSink ) { - // needs valid samplingSink for detach*() -> bind() - samplingSink = new FBObject(); - samplingSink.init(gl, width, height, 0); - } - detachAllImpl(gl, true , true); - if(resetSamplingSink) { - resetSamplingSink(gl); - } - + modified = true; samplingSinkDirty = true; + detachAllImpl(gl, true, true, sampleCountChange); + resetSamplingSink(gl); + if(!wasBound) { unbind(gl); } if(DEBUG) { - System.err.println("FBObject.reset - END - "+this); + System.err.println("FBObject.reset - END - wasBound, "+wasBound+", "+this); } + return true; + } else { + return false; + } + } + + /** + * Simply resets this instance's size only, w/o validation. + * + * <p>Leaves the FBO bound</p> + * + * @param gl the current GL context + * @param newWidth the new width, it's minimum is capped to 1 + * @param newHeight the new height, it's minimum is capped to 1 + */ + private final void resetSizeImpl(final GL gl, final int newWidth, final int newHeight) { + if(DEBUG) { + System.err.println("FBObject.resetSize - START - "+width+"x"+height+", "+samples+" -> "+newWidth+"x"+newHeight); + } + + final int sampleCountChange = 0; // keep MSAA settings + width = newWidth; + height = newHeight; + + modified = true; + samplingSinkDirty = true; + + detachAllImpl(gl, true, true, sampleCountChange); + + if(DEBUG) { + System.err.println("FBObject.resetSize - END - "+this); + } + } + + private void validateAttachmentSize(final Attachment a) { + final int aWidth = a.getWidth(); + final int aHeight = a.getHeight(); + + if( a instanceof TextureAttachment && ( aWidth > 2 + maxTextureSize || aHeight > 2 + maxTextureSize ) ) { + throw new GLException("Size "+aWidth+"x"+aHeight+" of "+a+" exceeds on of the maximum texture size "+maxTextureSize+": \n\t"+this); + } + if( aWidth > maxRenderbufferSize || aHeight > maxRenderbufferSize ) { + throw new GLException("Size "+aWidth+"x"+aHeight+" of "+a+" exceeds on of the maxima renderbuffer size "+maxRenderbufferSize+": \n\t"+this); } } @@ -1134,7 +1284,7 @@ public class FBObject { case GL2GL3.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: case GL2ES3.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: case GL3.GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: - if(0 == colorAttachmentCount || null == depth) { + if(0 == colorbufferCount || null == depth) { // we are in transition return true; } @@ -1196,8 +1346,8 @@ public class FBObject { * @see #createColorTextureAttachment(GLProfile, boolean, int, int) */ public final TextureAttachment attachTexture2D(final GL gl, final int attachmentPoint, final boolean alpha) throws GLException { - return (TextureAttachment)attachColorbuffer(gl, attachmentPoint, - createColorTextureAttachment(gl, alpha, width, height)); + return attachColorbuffer(gl, attachmentPoint, + createColorTextureAttachment(gl, alpha, width, height)).getTextureAttachment(); } /** @@ -1218,8 +1368,8 @@ public class FBObject { * @see #createColorTextureAttachment(GLProfile, boolean, int, int, int, int, int, int) */ public final TextureAttachment attachTexture2D(final GL gl, final int attachmentPoint, final boolean alpha, final int magFilter, final int minFilter, final int wrapS, final int wrapT) throws GLException { - return (TextureAttachment)attachColorbuffer(gl, attachmentPoint, - createColorTextureAttachment(gl, alpha, width, height, magFilter, minFilter, wrapS, wrapT)); + return attachColorbuffer(gl, attachmentPoint, + createColorTextureAttachment(gl, alpha, width, height, magFilter, minFilter, wrapS, wrapT)).getTextureAttachment(); } /** @@ -1243,8 +1393,8 @@ public class FBObject { public final TextureAttachment attachTexture2D(final GL gl, final int attachmentPoint, final int internalFormat, final int dataFormat, final int dataType, final int magFilter, final int minFilter, final int wrapS, final int wrapT) throws GLException { - return (TextureAttachment)attachColorbuffer(gl, attachmentPoint, - createColorTextureAttachment(internalFormat, width, height, dataFormat, dataType, magFilter, minFilter, wrapS, wrapT)); + return attachColorbuffer(gl, attachmentPoint, + createColorTextureAttachment(internalFormat, width, height, dataFormat, dataType, magFilter, minFilter, wrapS, wrapT)).getTextureAttachment(); } /** @@ -1252,7 +1402,7 @@ public class FBObject { * <p> * For GLES3, sampling-sink {@link Colorbuffer} format <b>must be equal</b> w/ the sampling-source {@link Colorbuffer}. * Implementation aligns w/ {@link #createColorTextureAttachment(GLProfile, boolean, int, int, int, int, int, int)} - * and is enforced via {@link #sampleSinkFormatMismatch(GL)}. + * and is enforced via {@link #sampleSinkExFormatMismatch(GL)}. * </p> * * @param alpha set to <code>true</code> if you request alpha channel, otherwise <code>false</code>; @@ -1266,12 +1416,34 @@ public class FBObject { } else { internalFormat = alpha ? GL.GL_RGBA4 : GL.GL_RGB565; } - return new ColorAttachment(internalFormat, samples, width, height, 0); + return createColorAttachment(internalFormat, samples, width, height); } /** - * Attaches a {@link Colorbuffer}, i.e. {@link ColorAttachment}, to this FBO's instance at the given attachment point, - * selecting the format automatically. + * Creates a {@link ColorAttachment}, selecting the format automatically. + * <p> + * For GLES3, sampling-sink {@link Colorbuffer} format <b>must be equal</b> w/ the sampling-source {@link Colorbuffer}. + * Implementation aligns w/ {@link #createColorTextureAttachment(GLProfile, boolean, int, int, int, int, int, int)} + * and is enforced via {@link #sampleSinkExFormatMismatch(GL)}. + * </p> + * + * @param alpha set to <code>true</code> if you request alpha channel, otherwise <code>false</code>; + * @return uninitialized ColorAttachment instance describing the new attached colorbuffer + */ + public static final ColorAttachment createColorAttachment(final int internalFormat, final int samples, final int width, final int height) { + return new ColorAttachment(internalFormat, samples, width, height, 0 /* name not yet determined */); + } + + public static final RenderAttachment createRenderAttachment(final Type type, final int internalFormat, final int samples, final int width, final int height) { + return new RenderAttachment(type, internalFormat, samples, width, height, 0 /* name not yet determined */); + } + + /** + * Attaches a newly created and {@link Colorbuffer#initialize(GL) initialized} {@link Colorbuffer}, i.e. a {@link ColorAttachment}, + * at the given attachment point. + * <p> + * The {@link ColorAttachment} is created using {@code alpha} if {@code true} and current {@code sample count} and {@code size}. + * </p> * * <p>Leaves the FBO bound.</p> * @@ -1283,11 +1455,15 @@ public class FBObject { * @see #createColorAttachment(boolean) */ public final ColorAttachment attachColorbuffer(final GL gl, final int attachmentPoint, final boolean alpha) throws GLException { - return (ColorAttachment) attachColorbuffer(gl, attachmentPoint, createColorAttachment(alpha)); + return attachColorbuffer(gl, attachmentPoint, createColorAttachment(alpha)).getColorAttachment(); } /** - * Attaches a {@link Colorbuffer}, i.e. {@link ColorAttachment}, to this FBO's instance at the given attachment point. + * Attaches a newly created and {@link Colorbuffer#initialize(GL) initialized} {@link Colorbuffer}, i.e. a {@link ColorAttachment}, + * at the given attachment point. + * <p> + * The {@link ColorAttachment} is created using the given {@code internalFormat} and current {@code sample count} and {@code size}. + * </p> * * <p>Leaves the FBO bound.</p> * @@ -1304,13 +1480,15 @@ public class FBObject { throw new IllegalArgumentException("colorformat invalid: "+toHexString(internalFormat)+", "+this); } - return (ColorAttachment) attachColorbuffer(gl, attachmentPoint, new ColorAttachment(internalFormat, samples, width, height, 0)); + return attachColorbuffer(gl, attachmentPoint, createColorAttachment(internalFormat, samples, width, height)).getColorAttachment(); } /** - * Attaches a {@link Colorbuffer}, i.e. {@link ColorAttachment} or {@link TextureAttachment}, - * to this FBO's instance at the given attachment point. - * + * Attaches a {@link Colorbuffer} at the given attachment point + * and {@link Colorbuffer#initialize(GL) initializes} it, if not done yet. + * <p> + * {@link Colorbuffer} may be a {@link ColorAttachment} or {@link TextureAttachment}. + * </p> * <p> * If {@link Colorbuffer} is a {@link TextureAttachment} and is uninitialized, i.e. it's texture name is <code>zero</code>, * a new texture name is generated and setup w/ the texture parameter.<br/> @@ -1323,7 +1501,7 @@ public class FBObject { * @param gl * @param attachmentPoint the color attachment point ranging from [0..{@link #getMaxColorAttachments()}-1] * @param colbuf the to be attached {@link Colorbuffer} - * @return newly attached {@link Colorbuffer} instance if bound and configured successfully, otherwise GLException is thrown + * @return given {@link Colorbuffer} instance if bound and configured successfully, otherwise GLException is thrown * @throws GLException in case the colorbuffer couldn't be allocated or MSAA has been chosen in case of a {@link TextureAttachment} */ public final Colorbuffer attachColorbuffer(final GL gl, final int attachmentPoint, final Colorbuffer colbuf) throws GLException { @@ -1333,14 +1511,14 @@ public class FBObject { private final Colorbuffer attachColorbufferImpl(final GL gl, final int attachmentPoint, final Colorbuffer colbuf) throws GLException { validateAddColorAttachment(attachmentPoint, colbuf); + validateAttachmentSize((Attachment)colbuf); final boolean initializedColorbuf = colbuf.initialize(gl); - addColorAttachment(attachmentPoint, colbuf); + addColorAttachment(attachmentPoint, colbuf, false); - if(colbuf instanceof TextureAttachment) { - final TextureAttachment texA = (TextureAttachment) colbuf; - - if(samples>0) { + if( colbuf.isTextureAttachment() ) { + final TextureAttachment texA = colbuf.getTextureAttachment(); + if( samples > 0 ) { removeColorAttachment(attachmentPoint, texA); if(initializedColorbuf) { texA.free(gl); @@ -1360,8 +1538,8 @@ public class FBObject { throw new GLException("attachTexture2D "+texA+" at "+attachmentPoint+" failed "+getStatusString()+", "+this); } } - } else if(colbuf instanceof ColorAttachment) { - final ColorAttachment colA = (ColorAttachment) colbuf; + } else { + final ColorAttachment colA = colbuf.getColorAttachment(); // Attach the color buffer gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, @@ -1382,6 +1560,68 @@ public class FBObject { return colbuf; } + private final int getDepthIFormat(final int reqBits) { + if( 32 <= reqBits && depth32Avail ) { + return GL.GL_DEPTH_COMPONENT32; + } else if( 24 <= reqBits && ( depth24Avail || depth32Avail ) ) { + if( depth24Avail ) { + return GL.GL_DEPTH_COMPONENT24; + } else { + return GL.GL_DEPTH_COMPONENT32; + } + } else { + return GL.GL_DEPTH_COMPONENT16; + } + } + private final int getStencilIFormat(final int reqBits) { + if( 16 <= reqBits && stencil16Avail ) { + return GL2GL3.GL_STENCIL_INDEX16; + } else if( 8 <= reqBits && ( stencil08Avail || stencil16Avail ) ) { + if( stencil08Avail ) { + return GL.GL_STENCIL_INDEX8; + } else { + return GL2GL3.GL_STENCIL_INDEX16; + } + } else if( 4 <= reqBits && ( stencil04Avail || stencil08Avail || stencil16Avail ) ) { + if( stencil04Avail ) { + return GL.GL_STENCIL_INDEX4; + } else if( stencil08Avail ) { + return GL.GL_STENCIL_INDEX8; + } else { + return GL2GL3.GL_STENCIL_INDEX16; + } + } else if( 1 <= reqBits && ( stencil01Avail || stencil04Avail || stencil08Avail || stencil16Avail ) ) { + if( stencil01Avail ) { + return GL.GL_STENCIL_INDEX1; + } else if( stencil04Avail ) { + return GL.GL_STENCIL_INDEX4; + } else if( stencil08Avail ) { + return GL.GL_STENCIL_INDEX8; + } else { + return GL2GL3.GL_STENCIL_INDEX16; + } + } else { + throw new GLException("stencil buffer n/a"); + } + } + + /** Request default bit count for depth- or stencil buffer (depth 24 bits, stencil 8 bits), value {@value} */ + public static final int DEFAULT_BITS = 0; + + /** + * Request current context drawable's <i>requested</i> + * {@link GLCapabilitiesImmutable#getDepthBits() depth-} or {@link GLCapabilitiesImmutable#getStencilBits() stencil-}bits; value {@value} */ + public static final int REQUESTED_BITS = -1; + + /** + * Request current context drawable's <i>chosen</i> + * {@link GLCapabilitiesImmutable#getDepthBits() depth-} or {@link GLCapabilitiesImmutable#getStencilBits() stencil-}bits; value {@value} */ + public static final int CHOSEN_BITS = -2; + + /** Request maximum bit count for depth- or stencil buffer (depth 32 bits, stencil 16 bits), value {@value} */ + public static final int MAXIMUM_BITS = -3; + + /** * Attaches one depth, stencil or packed-depth-stencil buffer to this FBO's instance, * selecting the internalFormat automatically. @@ -1389,7 +1629,8 @@ public class FBObject { * Stencil and depth buffer can be attached only once. * </p> * <p> - * In case the desired type or bit-number is not supported, the next available one is chosen. + * In case the bit-count is not supported, + * the next available one is chosen, i.e. next higher (preferred) or lower bit-count. * </p> * <p> * Use {@link #getDepthAttachment()} and/or {@link #getStencilAttachment()} to retrieve details @@ -1401,68 +1642,58 @@ public class FBObject { * * @param gl * @param atype either {@link Type#DEPTH}, {@link Type#STENCIL} or {@link Type#DEPTH_STENCIL} - * @param reqBits desired bits for depth or -1 for default (24 bits) + * @param reqBits desired bits for depth or stencil, + * may use generic values {@link #DEFAULT_BITS}, {@link #REQUESTED_BITS}, {@link #CHOSEN_BITS} or {@link #MAXIMUM_BITS}. * @throws GLException in case the renderbuffer couldn't be allocated or one is already attached. * @throws IllegalArgumentException * @see #getDepthAttachment() * @see #getStencilAttachment() */ - public final void attachRenderbuffer(final GL gl, final Attachment.Type atype, int reqBits) throws GLException, IllegalArgumentException { - if( 0 > reqBits ) { - reqBits = 24; + public final void attachRenderbuffer(final GL gl, final Attachment.Type atype, final int reqBits) throws GLException, IllegalArgumentException { + final int reqDepth, reqStencil; + if( MAXIMUM_BITS > reqBits ) { + throw new IllegalArgumentException("reqBits out of range, shall be >= "+MAXIMUM_BITS); + } else if( MAXIMUM_BITS == reqBits ) { + reqDepth = 32; + reqStencil = 16; + } else if( CHOSEN_BITS == reqBits ) { + final GLCapabilitiesImmutable caps = gl.getContext().getGLDrawable().getChosenGLCapabilities(); + reqDepth = caps.getDepthBits(); + reqStencil = caps.getStencilBits(); + } else if( REQUESTED_BITS == reqBits ) { + final GLCapabilitiesImmutable caps = gl.getContext().getGLDrawable().getRequestedGLCapabilities(); + reqDepth = caps.getDepthBits(); + reqStencil = caps.getStencilBits(); + } else if( DEFAULT_BITS == reqBits ) { + reqDepth = 24; + reqStencil = 8; + } else { + reqDepth = reqBits; + reqStencil = reqBits; } final int internalFormat; int internalStencilFormat = -1; switch ( atype ) { case DEPTH: - if( 32 <= reqBits && depth32Avail ) { - internalFormat = GL.GL_DEPTH_COMPONENT32; - } else if( 24 <= reqBits && depth24Avail ) { - internalFormat = GL.GL_DEPTH_COMPONENT24; - } else { - internalFormat = GL.GL_DEPTH_COMPONENT16; - } + internalFormat = getDepthIFormat(reqDepth); break; case STENCIL: - if( 16 <= reqBits && stencil16Avail ) { - internalFormat = GL2GL3.GL_STENCIL_INDEX16; - } else if( 8 <= reqBits && stencil08Avail ) { - internalFormat = GL.GL_STENCIL_INDEX8; - } else if( 4 <= reqBits && stencil04Avail ) { - internalFormat = GL.GL_STENCIL_INDEX4; - } else if( 1 <= reqBits && stencil01Avail ) { - internalFormat = GL.GL_STENCIL_INDEX1; - } else { - throw new GLException("stencil buffer n/a"); - } + internalFormat = getStencilIFormat(reqStencil); break; case DEPTH_STENCIL: if( packedDepthStencilAvail ) { internalFormat = GL.GL_DEPTH24_STENCIL8; } else { - if( 24 <= reqBits && depth24Avail ) { - internalFormat = GL.GL_DEPTH_COMPONENT24; - } else { - internalFormat = GL.GL_DEPTH_COMPONENT16; - } - if( stencil08Avail ) { - internalStencilFormat = GL.GL_STENCIL_INDEX8; - } else if( stencil04Avail ) { - internalStencilFormat = GL.GL_STENCIL_INDEX4; - } else if( stencil01Avail ) { - internalStencilFormat = GL.GL_STENCIL_INDEX1; - } else { - throw new GLException("stencil buffer n/a"); - } + internalFormat = getDepthIFormat(reqDepth); + internalStencilFormat = getStencilIFormat(reqStencil); } break; default: throw new IllegalArgumentException("only depth/stencil types allowed, was "+atype+", "+this); } - attachRenderbufferImpl(gl, atype, internalFormat); if(0<=internalStencilFormat) { @@ -1514,32 +1745,36 @@ public class FBObject { } private final void attachRenderbufferImpl2(final GL gl, final Attachment.Type atype, final int internalFormat) throws GLException { + // atype and current depth and stencil instance are already validated in 'attachRenderbufferImpl(..)' if( Attachment.Type.DEPTH == atype ) { if(null == depth) { - depth = new RenderAttachment(Type.DEPTH, internalFormat, samples, width, height, 0); + depth = createRenderAttachment(Type.DEPTH, internalFormat, samples, width, height); } else { depth.setSize(width, height); depth.setSamples(samples); } + validateAttachmentSize(depth); depth.initialize(gl); } else if( Attachment.Type.STENCIL == atype ) { if(null == stencil) { - stencil = new RenderAttachment(Type.STENCIL, internalFormat, samples, width, height, 0); + stencil = createRenderAttachment(Type.STENCIL, internalFormat, samples, width, height); } else { stencil.setSize(width, height); stencil.setSamples(samples); } + validateAttachmentSize(stencil); stencil.initialize(gl); } else if( Attachment.Type.DEPTH_STENCIL == atype ) { if(null == depth) { if(null != stencil) { throw new InternalError("XXX: DEPTH_STENCIL, depth was null, stencil not: "+this.toString()); } - depth = new RenderAttachment(Type.DEPTH_STENCIL, internalFormat, samples, width, height, 0); + depth = createRenderAttachment(Type.DEPTH_STENCIL, internalFormat, samples, width, height); } else { depth.setSize(width, height); depth.setSamples(samples); } + validateAttachmentSize(depth); depth.initialize(gl); // DEPTH_STENCIL shares buffer w/ depth and stencil stencil = depth; @@ -1555,6 +1790,8 @@ public class FBObject { gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, stencil.getName()); } + modified = true; + if(!ignoreStatus) { updateStatus(gl); if( !isStatusValid() ) { @@ -1581,7 +1818,7 @@ public class FBObject { public final Colorbuffer detachColorbuffer(final GL gl, final int attachmentPoint, final boolean dispose) throws IllegalArgumentException { bind(gl); - final Colorbuffer res = detachColorbufferImpl(gl, attachmentPoint, dispose ? DetachAction.DISPOSE : DetachAction.NONE); + final Colorbuffer res = detachColorbufferImpl(gl, attachmentPoint, dispose ? DetachAction.DISPOSE : DetachAction.NONE, 0); if(null == res) { throw new IllegalArgumentException("ColorAttachment at "+attachmentPoint+", not attached, "+this); } @@ -1591,17 +1828,17 @@ public class FBObject { return res; } - private final Colorbuffer detachColorbufferImpl(final GL gl, final int attachmentPoint, final DetachAction detachAction) { - Colorbuffer colbuf = colorAttachmentPoints[attachmentPoint]; // shortcut, don't validate here + private final Colorbuffer detachColorbufferImpl(final GL gl, final int attachmentPoint, final DetachAction detachAction, final int sampleCountChange) { + final Colorbuffer colbufOld = colorbufferAttachments[attachmentPoint]; // shortcut, don't validate here - if(null == colbuf) { + if(null == colbufOld) { return null; } - removeColorAttachment(attachmentPoint, colbuf); + removeColorAttachment(attachmentPoint, colbufOld); - if(colbuf instanceof TextureAttachment) { - final TextureAttachment texA = (TextureAttachment) colbuf; + if( colbufOld.isTextureAttachment() ) { + final TextureAttachment texA = colbufOld.getTextureAttachment(); if( 0 != texA.getName() ) { gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, GL.GL_COLOR_ATTACHMENT0 + attachmentPoint, @@ -1616,17 +1853,19 @@ public class FBObject { } } if(DetachAction.RECREATE == detachAction) { - if(samples == 0) { - // stay non MSAA - texA.setSize(width, height); + final Colorbuffer colbufNew; + if( 0 < sampleCountChange ) { + // switch to MSAA: TextureAttachment -> ColorAttachment + colbufNew = createColorAttachment(hasAlpha(texA.format)); } else { - // switch to MSAA - colbuf = createColorAttachment(hasAlpha(texA.format)); + // keep MSAA settings + texA.setSize(width, height); + colbufNew = texA; } - attachColorbufferImpl(gl, attachmentPoint, colbuf); + attachColorbufferImpl(gl, attachmentPoint, colbufNew); } - } else if(colbuf instanceof ColorAttachment) { - final ColorAttachment colA = (ColorAttachment) colbuf; + } else { + final ColorAttachment colA = colbufOld.getColorAttachment(); if( 0 != colA.getName() ) { gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_COLOR_ATTACHMENT0+attachmentPoint, @@ -1640,37 +1879,42 @@ public class FBObject { } } if(DetachAction.RECREATE == detachAction) { - if(samples > 0) { - // stay MSAA + final Colorbuffer colbufNew; + if( 0 <= sampleCountChange || null == samplingColorSink ) { + // keep ColorAttachment, + // including 'switch to non-MSAA' if no samplingColorSink is available + // to determine whether a TextureAttachment or ColorAttachment is desired! colA.setSize(width, height); colA.setSamples(samples); + colbufNew = colA; } else { // switch to non MSAA - if(null != samplingSinkTexture) { - colbuf = createColorTextureAttachment(samplingSinkTexture.format, width, height, - samplingSinkTexture.dataFormat, samplingSinkTexture.dataType, - samplingSinkTexture.magFilter, samplingSinkTexture.minFilter, - samplingSinkTexture.wrapS, samplingSinkTexture.wrapT); + if( samplingColorSink.isTextureAttachment() ) { + final TextureAttachment samplingTextureSink = samplingColorSink.getTextureAttachment(); + colbufNew = createColorTextureAttachment(samplingTextureSink.format, width, height, + samplingTextureSink.dataFormat, samplingTextureSink.dataType, + samplingTextureSink.magFilter, samplingTextureSink.minFilter, + samplingTextureSink.wrapS, samplingTextureSink.wrapT); } else { - colbuf = createColorTextureAttachment(gl, true, width, height); + colbufNew = createColorAttachment(samplingColorSink.getFormat(), 0, width, height); } } - attachColorbuffer(gl, attachmentPoint, colbuf); + attachColorbuffer(gl, attachmentPoint, colbufNew); } } - return colbuf; + return colbufOld; } private final void freeAllColorbufferImpl(final GL gl) { for(int i=0; i<maxColorAttachments; i++) { - final Colorbuffer colbuf = colorAttachmentPoints[i]; // shortcut, don't validate here + final Colorbuffer colbuf = colorbufferAttachments[i]; // shortcut, don't validate here if(null == colbuf) { return; } - if(colbuf instanceof TextureAttachment) { - final TextureAttachment texA = (TextureAttachment) colbuf; + if( colbuf.isTextureAttachment() ) { + final TextureAttachment texA = colbuf.getTextureAttachment(); if( 0 != texA.getName() ) { gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, GL.GL_COLOR_ATTACHMENT0 + i, @@ -1678,8 +1922,8 @@ public class FBObject { gl.glBindTexture(GL.GL_TEXTURE_2D, 0); } texA.free(gl); - } else if(colbuf instanceof ColorAttachment) { - final ColorAttachment colA = (ColorAttachment) colbuf; + } else { + final ColorAttachment colA = colbuf.getColorAttachment(); if( 0 != colA.getName() ) { gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_COLOR_ATTACHMENT0 + i, @@ -1698,7 +1942,10 @@ public class FBObject { */ public final void detachRenderbuffer(final GL gl, final Attachment.Type atype, final boolean dispose) throws IllegalArgumentException { bind(gl); - detachRenderbufferImpl(gl, atype, dispose ? DetachAction.DISPOSE : DetachAction.NONE); + final RenderAttachment res = detachRenderbufferImpl(gl, atype, dispose ? DetachAction.DISPOSE : DetachAction.NONE); + if(null == res) { + throw new IllegalArgumentException("RenderAttachment type "+atype+", not attached, "+this); + } if(DEBUG) { System.err.println("FBObject.detachRenderbuffer.X: [attachmentType "+atype+", dispose "+dispose+"]: "+this); } @@ -1718,7 +1965,7 @@ public class FBObject { return res; } - private final void detachRenderbufferImpl(final GL gl, Attachment.Type atype, final DetachAction detachAction) throws IllegalArgumentException { + private final RenderAttachment detachRenderbufferImpl(final GL gl, Attachment.Type atype, final DetachAction detachAction) throws IllegalArgumentException { switch ( atype ) { case DEPTH: case STENCIL: @@ -1728,23 +1975,25 @@ public class FBObject { throw new IllegalArgumentException("only depth/stencil types allowed, was "+atype+", "+this); } if( null == depth && null == stencil ) { - return ; // nop + return null; // nop } final boolean packed = isDepthStencilPackedFormat(); if( packed ) { // Note: DEPTH_STENCIL shares buffer w/ depth and stencil atype = Attachment.Type.DEPTH_STENCIL; } + final RenderAttachment renderOld; switch ( atype ) { case DEPTH: - if( null != depth ) { - final int format = depth.format; - if( 0 != depth.getName() ) { + renderOld = depth; + if( null != renderOld ) { + final int format = renderOld.format; + if( 0 != renderOld.getName() ) { gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_DEPTH_ATTACHMENT, GL.GL_RENDERBUFFER, 0); switch(detachAction) { case DISPOSE: case RECREATE: - depth.free(gl); + renderOld.free(gl); break; default: } @@ -1757,14 +2006,15 @@ public class FBObject { } break; case STENCIL: - if( null != stencil ) { - final int format = stencil.format; - if(0 != stencil.getName()) { + renderOld = stencil; + if( null != renderOld ) { + final int format = renderOld.format; + if(0 != renderOld.getName()) { gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, 0); switch(detachAction) { case DISPOSE: case RECREATE: - stencil.free(gl); + renderOld.free(gl); break; default: } @@ -1777,9 +2027,10 @@ public class FBObject { } break; case DEPTH_STENCIL: - if( null != depth ) { - final int format = depth.format; - if(0 != depth.getName()) { + renderOld = depth; + if( null != renderOld ) { + final int format = renderOld.format; + if(0 != renderOld.getName()) { gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_DEPTH_ATTACHMENT, GL.GL_RENDERBUFFER, 0); if(packed) { gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_STENCIL_ATTACHMENT, GL.GL_RENDERBUFFER, 0); @@ -1787,7 +2038,7 @@ public class FBObject { switch(detachAction) { case DISPOSE: case RECREATE: - depth.free(gl); + renderOld.free(gl); break; default: } @@ -1820,8 +2071,11 @@ public class FBObject { } } break; - default: // handled + default: + throw new InternalError("XXX"); // handled by caller } + modified = true; + return renderOld; } private final void freeAllRenderbufferImpl(final GL gl) throws IllegalArgumentException { @@ -1849,7 +2103,7 @@ public class FBObject { * and disposes them. * <p>Leaves the FBO bound, if initialized!</p> * <p> - * An attached sampling sink texture will be detached as well, see {@link #getSamplingSink()}. + * An attached sampling sink texture will be detached as well, see {@link #getSamplingTextureSink()}. * </p> * @param gl the current GL context */ @@ -1857,7 +2111,7 @@ public class FBObject { if(null != samplingSink) { samplingSink.detachAll(gl); } - detachAllImpl(gl, true/* detachNonColorbuffer */, false /* recreate */); + detachAllImpl(gl, true/* detachNonColorbuffer */, false /* recreate */, 0); } /** @@ -1865,7 +2119,7 @@ public class FBObject { * and disposes them. * <p>Leaves the FBO bound, if initialized!</p> * <p> - * An attached sampling sink texture will be detached as well, see {@link #getSamplingSink()}. + * An attached sampling sink texture will be detached as well, see {@link #getSamplingTextureSink()}. * </p> * @param gl the current GL context */ @@ -1873,14 +2127,14 @@ public class FBObject { if(null != samplingSink) { samplingSink.detachAllColorbuffer(gl); } - detachAllImpl(gl, false/* detachNonColorbuffer */, false /* recreate */); + detachAllImpl(gl, false/* detachNonColorbuffer */, false /* recreate */, 0); } /** * Detaches all {@link TextureAttachment}s and disposes them. * <p>Leaves the FBO bound, if initialized!</p> * <p> - * An attached sampling sink texture will be detached as well, see {@link #getSamplingSink()}. + * An attached sampling sink texture will be detached as well, see {@link #getSamplingTextureSink()}. * </p> * @param gl the current GL context */ @@ -1893,8 +2147,8 @@ public class FBObject { } bind(gl); for(int i=0; i<maxColorAttachments; i++) { - if(colorAttachmentPoints[i] instanceof TextureAttachment) { - detachColorbufferImpl(gl, i, DetachAction.DISPOSE); + if( colorbufferAttachments[i].isTextureAttachment() ) { + detachColorbufferImpl(gl, i, DetachAction.DISPOSE, 0); } } if(DEBUG) { @@ -1913,7 +2167,7 @@ public class FBObject { detachRenderbufferImpl(gl, Attachment.Type.DEPTH_STENCIL, DetachAction.DISPOSE); } - private final void detachAllImpl(final GL gl, final boolean detachNonColorbuffer, final boolean recreate) { + private final void detachAllImpl(final GL gl, final boolean detachNonColorbuffer, final boolean recreate, final int sampleCountChange) { if( !isInitialized() ) { return; } @@ -1928,9 +2182,9 @@ public class FBObject { } } for(int i=0; i<maxColorAttachments; i++) { - detachColorbufferImpl(gl, i, recreate ? DetachAction.RECREATE : DetachAction.DISPOSE); + detachColorbufferImpl(gl, i, recreate ? DetachAction.RECREATE : DetachAction.DISPOSE, sampleCountChange); } - if( !recreate && colorAttachmentCount>0 ) { + if( !recreate && colorbufferCount>0 ) { throw new InternalError("Non zero ColorAttachments "+this); } @@ -1969,7 +2223,7 @@ public class FBObject { samplingSink.destroy(gl); } - detachAllImpl(gl, true /* detachNonColorbuffer */, false /* recreate */); + detachAllImpl(gl, true /* detachNonColorbuffer */, false /* recreate */, 0); // cache FB names, preset exposed to zero, // braking ties w/ GL/GLContext link to getReadFramebuffer()/getWriteFramebuffer() @@ -1991,41 +2245,50 @@ public class FBObject { private final boolean sampleSinkSizeMismatch() { return samplingSink.getWidth() != width || samplingSink.getHeight() != height ; } - private final boolean sampleSinkTexMismatch() { - return null == samplingSinkTexture || 0 == samplingSinkTexture.getName() ; - } private final boolean sampleSinkDepthStencilMismatch() { - final boolean depthMismatch = ( null != depth && null == samplingSink.depth ) || - ( null != depth && null != samplingSink.depth && - depth.format != samplingSink.depth.format ); + if ( ( null != depth && ( null == samplingSink.depth || depth.format != samplingSink.depth.format ) ) + || + ( null == depth && null != samplingSink.depth ) + ) { + return true; + } - final boolean stencilMismatch = ( null != stencil && null == samplingSink.stencil ) || - ( null != stencil && null != samplingSink.stencil && - stencil.format != samplingSink.stencil.format ); + if ( ( null != stencil && ( null == samplingSink.stencil || stencil.format != samplingSink.stencil.format ) ) + || + ( null == stencil && null != samplingSink.stencil ) + ) { + return true; + } - return depthMismatch || stencilMismatch; + return false; } /** - * For GLES3, sampling-sink {@link Colorbuffer} format <b>must be equal</b> w/ the sampling-source {@link Colorbuffer}. + * For GLES3, sampling-sink {@link Colorbuffer} <i>internal format</i> <b>must be equal</b> w/ the sampling-source {@link Colorbuffer}. * Implementation aligns w/ {@link #createColorTextureAttachment(GLProfile, boolean, int, int, int, int, int, int)} * and {@link #createColorAttachment(boolean)}. */ - private final boolean sampleSinkFormatMismatch(final GL gl) { - if( null != samplingSinkTexture && getColorAttachmentCount() > 0 && gl.isGL2ES3() ) { + private final boolean sampleSinkExFormatMismatch(final GL gl) { + if( null != samplingColorSink && getColorbufferCount() > 0 && gl.isGL2ES3() ) { final Attachment ca = (Attachment)getColorbuffer(0); // should be at attachment-point 0 - return ( null != ca && ca.format != samplingSinkTexture.format ) || - hasAlpha(samplingSinkTexture.format) != hasAttachmentUsingAlpha(); + // We cannot comply w/ attachment's format other than attachment point 0! + // return ( null != ca && ca.format != samplingColorSink.getFormat() ) || + // hasAlpha(samplingColorSink.getFormat()) != hasAttachmentUsingAlpha(); + return null != ca && ca.format != samplingColorSink.getFormat(); } return false; } /** - * Manually reset the MSAA sampling sink, if used. + * Manually validates the MSAA sampling sink, if used. * <p> * If MSAA is being used and no sampling sink is attached via {@link #setSamplingSink(FBObject)} * a new sampling sink is being created. * </p> * <p> + * If the sampling sink size or attributes differs from the source, its attachments are reset + * to match the source. + * </p> + * <p> * Automatically called by {@link #reset(GL, int, int, int, boolean)} * and {@link #syncSamplingSink(GL)}. * </p> @@ -2033,110 +2296,191 @@ public class FBObject { * It is recommended to call this method after initializing the FBO and attaching renderbuffer etc for the 1st time * if access to sampling sink resources is required. * </p> + * + * <p>Leaves the FBO bound state untouched</p> + * * @param gl the current GL context + * @return {@code true} if this instance has been modified, otherwise {@code false}. * @throws GLException in case of an error, i.e. size too big, etc .. */ - public final void resetSamplingSink(final GL gl) throws GLException { - if(0 == samples) { + public final boolean resetSamplingSink(final GL gl) throws GLException { + if(DEBUG) { + System.err.println("FBObject.resetSamplingSink.0"); + Thread.dumpStack(); + } + + if( 0 == samples ) { + final boolean modifiedInstance; // MSAA off - if(null != samplingSink && samplingSink.initialized) { + if( null != samplingSink ) { // cleanup - samplingSink.detachAll(gl); + if( samplingSink.initialized ) { + samplingSink.detachAll(gl); + } + samplingSink = null; + samplingColorSink = null; + modifiedInstance = true; + } else { + modifiedInstance = false; } - return; + this.modified = false; + if(DEBUG) { + System.err.println("FBObject.resetSamplingSink.X1: zero samples, mod "+modifiedInstance+"\n\tTHIS "+this); + } + return modifiedInstance; } - if(null == samplingSink ) { - samplingSink = new FBObject(); - } + boolean modifiedInstance = false; - if(!samplingSink.initialized) { + if( null == samplingSink ) { + samplingSink = new FBObject(); samplingSink.init(gl, width, height, 0); + samplingColorSink = null; + modifiedInstance = true; + } else if( !samplingSink.initialized ) { + throw new InternalError("InitState Mismatch: samplingSink set, but not initialized "+samplingSink); + } else if( null == samplingColorSink || 0 == samplingColorSink.getName() ) { + throw new InternalError("InitState Mismatch: samplingColorSink set, but not initialized "+samplingColorSink+", "+samplingSink); } - boolean sampleSinkFormatMismatch = sampleSinkFormatMismatch(gl); + if(DEBUG) { + System.err.println("FBObject.resetSamplingSink.1: mod "+modifiedInstance+"\n\tTHIS "+this+",\n\tSINK "+samplingSink); + } + boolean sampleSinkExFormatMismatch = sampleSinkExFormatMismatch(gl); boolean sampleSinkSizeMismatch = sampleSinkSizeMismatch(); - boolean sampleSinkTexMismatch = sampleSinkTexMismatch(); boolean sampleSinkDepthStencilMismatch = sampleSinkDepthStencilMismatch(); - /** if(DEBUG) { - System.err.println("FBObject.resetSamplingSink.0: \n\tTHIS "+this+",\n\tSINK "+samplesSink+ - "\n\t format "+sampleSinkFormatMismatch+", size "+sampleSinkSizeMismatch +", tex "+sampleSinkTexMismatch +", depthStencil "+sampleSinkDepthStencilMismatch); - } */ - - if(!sampleSinkFormatMismatch && !sampleSinkSizeMismatch && !sampleSinkTexMismatch && !sampleSinkDepthStencilMismatch) { - // all properties match .. - return; + if( modifiedInstance ) { + // samplingColorSink == null + // must match size, format and colorbuffer do not exist yet + if( sampleSinkExFormatMismatch || sampleSinkSizeMismatch ) { + throw new InternalError("InitState Mismatch: Matching exFormat "+!sampleSinkExFormatMismatch+ + ", size "+!sampleSinkSizeMismatch +", "+this); + } + } else { + // samplingColorSink != null + if(!sampleSinkExFormatMismatch && !sampleSinkSizeMismatch && !sampleSinkDepthStencilMismatch) { + if(DEBUG) { + System.err.println("FBObject.resetSamplingSink.X2: Matching: exFormat "+!sampleSinkExFormatMismatch+ + ", size "+!sampleSinkSizeMismatch +", depthStencil "+!sampleSinkDepthStencilMismatch+ + ", mod "+modifiedInstance); + } + // all properties match .. + samplingSink.modified = false; + this.modified = false; + return modifiedInstance; + } } - unbind(gl); + final boolean wasBound; + if( isBound() ) { + markUnbound(); // automatic GL unbind by sampleSink binding + wasBound = true; + } else { + wasBound = false; + } if(DEBUG) { - System.err.println("FBObject.resetSamplingSink: BEGIN\n\tTHIS "+this+",\n\tSINK "+samplingSink+ - "\n\t format "+sampleSinkFormatMismatch+", size "+sampleSinkSizeMismatch +", tex "+sampleSinkTexMismatch +", depthStencil "+sampleSinkDepthStencilMismatch); + System.err.println("FBObject.resetSamplingSink.2: wasBound "+wasBound+", matching: exFormat "+!sampleSinkExFormatMismatch+ + ", size "+!sampleSinkSizeMismatch +", depthStencil "+!sampleSinkDepthStencilMismatch); } - if( sampleSinkDepthStencilMismatch ) { + modifiedInstance = true; + + if( sampleSinkDepthStencilMismatch ) { // includes 1st init samplingSink.detachAllRenderbuffer(gl); } - if( sampleSinkFormatMismatch ) { + final boolean samplingColorSinkShallBeTA = null == samplingColorSink || samplingColorSink.isTextureAttachment(); + + if( sampleSinkExFormatMismatch ) { samplingSink.detachAllColorbuffer(gl); - samplingSinkTexture = null; + samplingColorSink = null; } else if( sampleSinkSizeMismatch ) { - samplingSink.reset(gl, width, height); - } - - if(null == samplingSinkTexture) { - final boolean hasAlpha = hasAttachmentUsingAlpha(); - samplingSinkTexture = samplingSink.attachTexture2D(gl, 0, hasAlpha); - } else if( 0 == samplingSinkTexture.getName() ) { - samplingSinkTexture.setSize(width, height); - samplingSink.attachColorbuffer(gl, 0, samplingSinkTexture); + samplingSink.resetSizeImpl(gl, width, height); + samplingColorSink = samplingSink.getColorbuffer(0); + } + + if( null == samplingColorSink ) { // sampleSinkFormatMismatch || 1st init + final Colorbuffer cb0 = getColorbuffer(0); // align with colorbuffer at attachment-point 0 + if( null != cb0 ) { + // match pre-existing format + if( samplingColorSinkShallBeTA ) { + samplingColorSink = createColorTextureAttachment(gl, cb0.getFormat(), width, height, + GL.GL_NEAREST, GL.GL_NEAREST, + GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); + } else { + samplingColorSink = createColorAttachment(cb0.getFormat(), 0, width, height); + } + samplingSink.attachColorbuffer(gl, 0, samplingColorSink); + } else { + // match default format + final boolean hasAlpha = hasAttachmentUsingAlpha(); + if( samplingColorSinkShallBeTA ) { + samplingColorSink = samplingSink.attachTexture2D(gl, 0, hasAlpha); + } else { + samplingColorSink = samplingSink.attachColorbuffer(gl, 0, hasAlpha); + } + } } - if( sampleSinkDepthStencilMismatch ) { + if( sampleSinkDepthStencilMismatch ) { // includes 1st init samplingSink.attachRenderbuffer(gl, depth.format); if( null != stencil && !isDepthStencilPackedFormat() ) { samplingSink.attachRenderbuffer(gl, stencil.format); } } - sampleSinkFormatMismatch = sampleSinkFormatMismatch(gl); + sampleSinkExFormatMismatch = sampleSinkExFormatMismatch(gl); sampleSinkSizeMismatch = sampleSinkSizeMismatch(); - sampleSinkTexMismatch = sampleSinkTexMismatch(); sampleSinkDepthStencilMismatch = sampleSinkDepthStencilMismatch(); - if(sampleSinkFormatMismatch || sampleSinkSizeMismatch || sampleSinkTexMismatch || sampleSinkDepthStencilMismatch) { + if(sampleSinkExFormatMismatch || sampleSinkSizeMismatch || sampleSinkDepthStencilMismatch) { throw new InternalError("Samples sink mismatch after reset: \n\tTHIS "+this+",\n\t SINK "+samplingSink+ - "\n\t format "+sampleSinkFormatMismatch+", size "+sampleSinkSizeMismatch +", tex "+sampleSinkTexMismatch +", depthStencil "+sampleSinkDepthStencilMismatch); + "\n\t Mismatch. Matching: exFormat "+!sampleSinkExFormatMismatch+ + ", size "+!sampleSinkSizeMismatch +", depthStencil "+!sampleSinkDepthStencilMismatch); + } + + samplingSink.modified = false; + samplingSink.unbind(gl); + this.modified = false; + + if(wasBound) { + bind(gl); } if(DEBUG) { - System.err.println("FBObject.resetSamplingSink: END\n\tTHIS "+this+",\n\tSINK "+samplingSink+ - "\n\t format "+sampleSinkFormatMismatch+", size "+sampleSinkSizeMismatch +", tex "+sampleSinkTexMismatch +", depthStencil "+sampleSinkDepthStencilMismatch); + System.err.println("FBObject.resetSamplingSink.XX: END mod "+modifiedInstance+"\n\tTHIS "+this+",\n\tSINK "+samplingSink+ + "\n\t Matching: exFormat "+!sampleSinkExFormatMismatch+ + ", size "+!sampleSinkSizeMismatch +", depthStencil "+!sampleSinkDepthStencilMismatch); } + return modifiedInstance; } /** * Setting this FBO sampling sink. - * @param newSamplingSink the new FBO sampling sink to use, or null to remove current sampling sink + * @param newSamplingSink the new and initialized FBO sampling sink to use, or null to remove current sampling sink * @return the previous sampling sink or null if none was attached * @throws GLException if this FBO doesn't use MSAA or the given sink uses MSAA itself + * @throws IllegalStateException if the {@code newSamplingSink} is not null and not initialized */ - public FBObject setSamplingSink(final FBObject newSamplingSink) throws GLException { + public FBObject setSamplingSink(final FBObject newSamplingSink) throws IllegalStateException, GLException { final FBObject prev = samplingSink; if( null == newSamplingSink) { samplingSink = null; - samplingSinkTexture = null; + samplingColorSink = null; } else if( samples > 0 ) { + if( !newSamplingSink.isInitialized() ) { + throw new IllegalStateException("SamplingSink not initialized: "+newSamplingSink); + } if( newSamplingSink.getNumSamples() > 0 ) { throw new GLException("SamplingSink FBO cannot use MSAA itself: "+newSamplingSink); } samplingSink = newSamplingSink; - samplingSinkTexture = (TextureAttachment) newSamplingSink.getColorbuffer(0); + samplingColorSink = newSamplingSink.getColorbuffer(0); } else { throw new GLException("Setting SamplingSink for non MSAA FBO not allowed: "+this); } + modified = true; samplingSinkDirty = true; return prev; } @@ -2144,9 +2488,9 @@ public class FBObject { /** * Bind this FBO, i.e. bind write framebuffer to {@link #getWriteFramebuffer()}. * - * <p>If multisampling is used, it sets the read framebuffer to the sampling sink {@link #getWriteFramebuffer()}, - * if full FBO is supported.</p> - * + * <p> + * If multisampling is used, it sets the read framebuffer to the sampling sink {@link #getWriteFramebuffer()}. + * </p> * <p> * In case you have attached more than one color buffer, * you may want to setup {@link GL2ES3#glDrawBuffers(int, int[], int)}. @@ -2157,15 +2501,12 @@ public class FBObject { public final void bind(final GL gl) throws GLException { if(!bound || fbName != gl.getBoundFramebuffer(GL.GL_FRAMEBUFFER)) { checkInitialized(); - if(samples > 0 && fullFBOSupport) { - // draw to multisampling - read from samplesSink - gl.glBindFramebuffer(GL2ES3.GL_DRAW_FRAMEBUFFER, getWriteFramebuffer()); - gl.glBindFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER, getReadFramebuffer()); + if( fullFBOSupport ) { + gl.glBindFramebuffer(GL2ES3.GL_DRAW_FRAMEBUFFER, getWriteFramebuffer()); // this fb, msaa or normal + gl.glBindFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER, getReadFramebuffer()); // msaa: sampling sink, normal: this fb } else { - // one for all - gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, getWriteFramebuffer()); + gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, getWriteFramebuffer()); // normal: read/write } - bound = true; samplingSinkDirty = true; } @@ -2213,7 +2554,7 @@ public class FBObject { * @param gl the current GL context */ public final boolean isBound(final GL gl) { - bound = bound && fbName != gl.getBoundFramebuffer(GL.GL_FRAMEBUFFER) ; + bound = bound && fbName == gl.getBoundFramebuffer(GL.GL_FRAMEBUFFER) ; return bound; } @@ -2222,7 +2563,7 @@ public class FBObject { /** * If multisampling is being used and flagged dirty by a previous call of {@link #bind(GL)} or after initialization, - * the msaa-buffers are sampled to it's sink {@link #getSamplingSink()}. + * the msaa-buffers are sampled to it's sink {@link #getSamplingTextureSink()}. * <p> * Method also resets the sampling sink configuration via {@link #resetSamplingSink(GL)} if used and required. * </p> @@ -2246,15 +2587,24 @@ public class FBObject { */ public final void syncSamplingSink(final GL gl) { markUnbound(); - if(samples>0 && samplingSinkDirty) { + if(samples>0 && samplingSinkDirty) { // implies fullFBOSupport samplingSinkDirty = false; - resetSamplingSink(gl); - checkPreGLError(gl); - gl.glBindFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER, fbName); - gl.glBindFramebuffer(GL2ES3.GL_DRAW_FRAMEBUFFER, samplingSink.getWriteFramebuffer()); + if( isModified() ) { + resetSamplingSink(gl); + } + final boolean checkError = DEBUG || GLContext.DEBUG_GL; + if( checkError ) { + checkPreGLError(gl); + } + gl.glBindFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER, fbName); // read from this MSAA fb + gl.glBindFramebuffer(GL2ES3.GL_DRAW_FRAMEBUFFER, samplingSink.getWriteFramebuffer()); // write to sampling sink ((GL2ES3)gl).glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, // since MSAA is supported, casting to GL2ES3 is OK GL.GL_COLOR_BUFFER_BIT, GL.GL_NEAREST); - checkNoError(null, gl.glGetError(), "FBObject syncSampleSink"); // throws GLException if error + if( checkError ) { + checkNoError(null, gl.glGetError(), "FBObject syncSampleSink"); // throws GLException if error + } + } else { + modified = false; } if(fullFBOSupport) { // default read/draw buffers, may utilize GLContext/GLDrawable override of @@ -2267,22 +2617,24 @@ public class FBObject { } /** - * Bind the given texture colorbuffer. + * {@link #syncSamplingSink(GL) Synchronize the sampling sink} and bind the given {@link TextureAttachment}, if not <code>null</code>. * - * <p>If using multiple texture units, ensure you call {@link GL#glActiveTexture(int)} first!</p> + * <p>If using a {@link TextureAttachment} and multiple texture units, ensure you call {@link GL#glActiveTexture(int)} first!</p> * * <p>{@link #syncSamplingSink(GL)} is being called</p> * * <p>Leaves the FBO unbound!</p> * * @param gl the current GL context - * @param ta {@link TextureAttachment} to use, prev. attached w/ {@link #attachTexture2D(GL, int, boolean, int, int, int, int) attachTexture2D(..)} + * @param ta {@link TextureAttachment} to use, prev. attached w/ {@link #attachTexture2D(GL, int, boolean, int, int, int, int) attachTexture2D(..)}, + * may be <code>null</code> in case no {@link TextureAttachment} is used. * @throws IllegalArgumentException */ public final void use(final GL gl, final TextureAttachment ta) throws IllegalArgumentException { - if(null == ta) { throw new IllegalArgumentException("Null TextureAttachment, this: "+toString()); } syncSamplingSink(gl); - gl.glBindTexture(GL.GL_TEXTURE_2D, ta.getName()); // use it .. + if( null != ta ) { + gl.glBindTexture(GL.GL_TEXTURE_2D, ta.getName()); // use it .. + } } /** @@ -2367,10 +2719,14 @@ public class FBObject { /** Returns the framebuffer name to render to. */ public final int getWriteFramebuffer() { return fbName; } /** Returns the framebuffer name to read from. Depending on multisampling, this may be a different framebuffer. */ - public final int getReadFramebuffer() { return ( samples > 0 ) ? samplingSink.getReadFramebuffer() : fbName; } + public final int getReadFramebuffer() { + return 0 < samples ? ( null != samplingSink ? samplingSink.getReadFramebuffer() : 0 ) : fbName; + } public final int getDefaultReadBuffer() { return GL.GL_COLOR_ATTACHMENT0; } - /** Return the number of color/texture attachments */ - public final int getColorAttachmentCount() { return colorAttachmentCount; } + /** Return the number of attached {@link Colorbuffer}s */ + public final int getColorbufferCount() { return colorbufferCount; } + /** Return the number of attached {@link TextureAttachment}s */ + public final int getTextureAttachmentCount() { return textureAttachmentCount; } /** Return the stencil {@link RenderAttachment} attachment, if exist. Maybe share the same {@link Attachment#getName()} as {@link #getDepthAttachment()}, if packed depth-stencil is being used. */ public final RenderAttachment getStencilAttachment() { return stencil; } /** Return the depth {@link RenderAttachment} attachment. Maybe share the same {@link Attachment#getName()} as {@link #getStencilAttachment()}, if packed depth-stencil is being used. */ @@ -2379,8 +2735,9 @@ public class FBObject { /** Return the complete multisampling {@link FBObject} sink, if using multisampling. */ public final FBObject getSamplingSinkFBO() { return samplingSink; } - /** Return the multisampling {@link TextureAttachment} sink, if using multisampling. */ - public final TextureAttachment getSamplingSink() { return samplingSinkTexture; } + /** Return the multisampling {@link Colorbuffer} sink, if using multisampling. */ + public final Colorbuffer getSamplingSink() { return samplingColorSink; } + /** * Returns <code>true</code> if the multisampling colorbuffer (msaa-buffer) * has been flagged dirty by a previous call of {@link #bind(GL)}, @@ -2388,15 +2745,26 @@ public class FBObject { */ public final boolean isSamplingBufferDirty() { return samplingSinkDirty; } + /** + * Returns <code>true</code> if size, sample-count or any attachment of this instance + * or its {@link #getSamplingSink() sampling-sink} has been modified since last {@link #syncSamplingSink(GL) sync}, + * {@link #use(GL, TextureAttachment) use}, {@link #reset(GL, int, int, int) reset} + * or {@link #resetSamplingSink(GL) resetSamplingSink}. + * <p> + * Otherwise method returns <code>false</code>. + * </p> + */ + public final boolean isModified() { return modified || ( null != samplingSink && samplingSink.modified ); } + int objectHashCode() { return super.hashCode(); } @Override public final String toString() { - final String caps = null != colorAttachmentPoints ? Arrays.asList(colorAttachmentPoints).toString() : null ; + final String caps = null != colorbufferAttachments ? Arrays.asList(colorbufferAttachments).toString() : null ; return "FBO[name r/w "+fbName+"/"+getReadFramebuffer()+", init "+initialized+", bound "+bound+", size "+width+"x"+height+ - ", samples "+samples+"/"+maxSamples+", depth "+depth+", stencil "+stencil+ - ", color attachments: "+colorAttachmentCount+"/"+maxColorAttachments+ - ": "+caps+", msaa["+samplingSinkTexture+", hasSink "+(null != samplingSink)+ + ", samples "+samples+"/"+maxSamples+", modified "+modified+"/"+isModified()+", depth "+depth+", stencil "+stencil+ + ", colorbuffer attachments: "+colorbufferCount+"/"+maxColorAttachments+", with "+textureAttachmentCount+" textures"+ + ": "+caps+", msaa["+samplingColorSink+", hasSink "+(null != samplingSink)+ ", dirty "+samplingSinkDirty+"], state "+getStatusString()+", obj "+toHexString(objectHashCode())+"]"; } diff --git a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java index f9a7ab029..b9f1fb10c 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java +++ b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawableDelegate.java @@ -38,6 +38,7 @@ import javax.media.opengl.GLDrawable; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; +import javax.media.opengl.GLSharedContextSetter; import com.jogamp.common.util.locks.LockFactory; import com.jogamp.common.util.locks.RecursiveLock; @@ -62,6 +63,12 @@ import jogamp.opengl.GLDrawableImpl; * <p> * See example {@link com.jogamp.opengl.test.junit.jogl.acore.TestGLAutoDrawableDelegateNEWT TestGLAutoDrawableDelegateNEWT}. * </p> + * <p> + * <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> + * To share a {@link GLContext} see the following note in the documentation overview: + * <a href="../../../overview-summary.html#SHARING">context sharing</a> + * as well as {@link GLSharedContextSetter}. + * </p> */ public class GLAutoDrawableDelegate extends GLAutoDrawableBase implements GLAutoDrawable { /** @@ -143,7 +150,7 @@ public class GLAutoDrawableDelegate extends GLAutoDrawableBase implements GLAuto private final RecursiveLock lock; @Override - protected final RecursiveLock getLock() { return lock; } + public final RecursiveLock getUpstreamLock() { return lock; } @Override public final Object getUpstreamWidget() { diff --git a/src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java b/src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java index a7ba141bb..bfd5fe115 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java +++ b/src/jogl/classes/com/jogamp/opengl/GLEventListenerState.java @@ -45,7 +45,9 @@ import javax.media.opengl.GLRunnable; import jogamp.opengl.Debug; +import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.nativewindow.MutableGraphicsConfiguration; +import com.jogamp.opengl.util.GLDrawableUtil; /** * GLEventListenerState is holding {@link GLAutoDrawable} components crucial @@ -72,11 +74,14 @@ public class GLEventListenerState { private GLEventListenerState(final AbstractGraphicsDevice upstreamDevice, final boolean proxyOwnsUpstreamDevice, final AbstractGraphicsDevice device, final GLCapabilitiesImmutable caps, + final RecursiveLock upstreamLock, final NativeSurface lockedSurface, final GLContext context, final int count, final GLAnimatorControl anim, final boolean animStarted) { this.upstreamDevice = upstreamDevice; this.proxyOwnsUpstreamDevice = proxyOwnsUpstreamDevice; this.device = device; this.caps = caps; + this.upstreamLock = upstreamLock; + this.lockedSurface = lockedSurface; this.context = context; this.listeners = new GLEventListener[count]; this.listenersInit = new boolean[count]; @@ -107,7 +112,29 @@ public class GLEventListenerState { public final GLAnimatorControl anim; public final boolean animStarted; - private boolean owner; + private volatile RecursiveLock upstreamLock; + private volatile NativeSurface lockedSurface; + private volatile boolean owner; + + /** + * Returns a {@link Runnable} {@link NativeSurface#unlockSurface() unlocking} an eventually locked {@link NativeSurface}, + * see {@link #moveFrom(GLAutoDrawable, boolean)} and {@link #moveTo(GLAutoDrawable, Runnable)}. + */ + public Runnable getUnlockSurfaceOp() { return unlockOp; } + + private final Runnable unlockOp = new Runnable() { + public void run() { + final RecursiveLock rl = upstreamLock; + final NativeSurface ls = lockedSurface; + upstreamLock = null; + lockedSurface = null; + if( null != rl ) { + rl.unlock(); + } + if( null != ls ) { + ls.unlockSurface(); + } + } }; /** * Last resort to destroy and loose ownership @@ -119,6 +146,7 @@ public class GLEventListenerState { listeners[i] = null; } // context.destroy(); - NPE (null drawable) + unlockOp.run(); device.close(); owner = false; } @@ -142,92 +170,143 @@ public class GLEventListenerState { * <p> * The returned GLEventListenerState instance is the {@link #isOwner() owner of the components}. * </p> + * <p> + * Locking is performed on the {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface}. + * See <a href="../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li> + * </p> * - * @param a {@link GLAutoDrawable} source to move components from + * @param src {@link GLAutoDrawable} source to move components from * @return new GLEventListenerState instance {@link #isOwner() owning} moved components. * * @see #moveTo(GLAutoDrawable) */ - public static GLEventListenerState moveFrom(final GLAutoDrawable a) { - final GLAnimatorControl aAnim = a.getAnimator(); - final boolean aAnimStarted; - if( null != aAnim ) { - aAnimStarted = aAnim.isStarted(); - aAnim.remove(a); // also handles ECT + public static GLEventListenerState moveFrom(final GLAutoDrawable src) { + return GLEventListenerState.moveFrom(src, false); + } + + /** + * Moves all GLEventListenerState components from the given {@link GLAutoDrawable} + * to a newly created instance. + * <p> + * Note that all components are removed from the {@link GLAutoDrawable}, + * i.e. the {@link GLContext}, all {@link GLEventListener}. + * </p> + * <p> + * If the {@link GLAutoDrawable} was added to a {@link GLAnimatorControl}, it is removed + * and the {@link GLAnimatorControl} added to the GLEventListenerState. + * </p> + * <p> + * The returned GLEventListenerState instance is the {@link #isOwner() owner of the components}. + * </p> + * <p> + * Locking is performed on the {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface}, + * which is <i>not released</i> if <code>keepLocked</code> is <code>true</code>. + * See <a href="../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li> + * </p> + * <p> + * <code>keepLocked</code> may be utilized if swapping a context between drawables + * and to ensure atomicity of operation. + * Here, the {@link #getUnlockSurfaceOp()} shall be passed to {@link #moveTo(GLAutoDrawable, Runnable)}. + * See {@link GLDrawableUtil#swapGLContextAndAllGLEventListener(GLAutoDrawable, GLAutoDrawable)}. + * </p> + * + * @param src {@link GLAutoDrawable} source to move components from + * @param keepLocked keep {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface} locked, see above + * @return new GLEventListenerState instance {@link #isOwner() owning} moved components. + * + * @see #moveTo(GLAutoDrawable, Runnable) + */ + public static GLEventListenerState moveFrom(final GLAutoDrawable src, final boolean keepLocked) { + final GLAnimatorControl srcAnim = src.getAnimator(); + final boolean srcAnimStarted; + if( null != srcAnim ) { + srcAnimStarted = srcAnim.isStarted(); + srcAnim.remove(src); // also handles ECT } else { - aAnimStarted = false; + srcAnimStarted = false; } final GLEventListenerState glls; - final NativeSurface aSurface = a.getNativeSurface(); - final boolean surfaceLocked = false; // NativeSurface.LOCK_SURFACE_NOT_READY < aSurface.lockSurface(); + final RecursiveLock srcUpstreamLock = src.getUpstreamLock(); + srcUpstreamLock.lock(); try { - final int aSz = a.getGLEventListenerCount(); - - // Create new AbstractGraphicsScreen w/ cloned AbstractGraphicsDevice for future GLAutoDrawable - // allowing this AbstractGraphicsDevice to loose ownership -> not closing display/device! - final AbstractGraphicsConfiguration aCfg = aSurface.getGraphicsConfiguration(); - final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) aCfg.getChosenCapabilities(); - final AbstractGraphicsDevice aDevice1 = aCfg.getScreen().getDevice(); - final AbstractGraphicsDevice aDevice2 = cloneDevice(aDevice1); - aDevice1.clearHandleOwner(); // don't close device handle - if( DEBUG ) { - System.err.println("GLEventListenerState.moveFrom.0a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1); - System.err.println("GLEventListenerState.moveFrom.0b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2); - System.err.println("GLEventListenerState.moveFrom.1: "+aSurface.getClass().getName()/*+", "+aSurface*/); + final NativeSurface srcSurface = src.getNativeSurface(); + final boolean srcSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < srcSurface.lockSurface(); + if( src.isRealized() && !srcSurfaceLocked ) { + throw new GLException("Could not lock realized surface "+src); } - final AbstractGraphicsDevice aUpDevice2; - final boolean proxyOwnsUpstreamDevice; - { - AbstractGraphicsDevice _aUpDevice2 = null; - if(aSurface instanceof ProxySurface) { - final ProxySurface aProxy = (ProxySurface)aSurface; - proxyOwnsUpstreamDevice = aProxy.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); - final NativeSurface aUpSurface = aProxy.getUpstreamSurface(); - if(DEBUG && null != aUpSurface) { - System.err.println("GLEventListenerState.moveFrom.2: "+aUpSurface.getClass().getName()+", "+aUpSurface); - } - if(null != aUpSurface) { - final AbstractGraphicsDevice aUpDevice1 = aUpSurface.getGraphicsConfiguration().getScreen().getDevice(); - _aUpDevice2 = cloneDevice(aUpDevice1); - aUpDevice1.clearHandleOwner(); // don't close device handle - if(DEBUG) { - System.err.println("GLEventListenerState.moveFrom.3a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1); - System.err.println("GLEventListenerState.moveFrom.3b: up-pres 0x"+Integer.toHexString(_aUpDevice2.hashCode())+", "+_aUpDevice2); - System.err.println("GLEventListenerState.moveFrom.3c: "+aSurface.getClass().getName()+", "+aSurface); - System.err.println("GLEventListenerState.moveFrom.3d: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString()); + + try { + final int aSz = src.getGLEventListenerCount(); + + // Create new AbstractGraphicsScreen w/ cloned AbstractGraphicsDevice for future GLAutoDrawable + // allowing this AbstractGraphicsDevice to loose ownership -> not closing display/device! + final AbstractGraphicsConfiguration aCfg = srcSurface.getGraphicsConfiguration(); + final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) aCfg.getChosenCapabilities(); + final AbstractGraphicsDevice aDevice1 = aCfg.getScreen().getDevice(); + final AbstractGraphicsDevice aDevice2 = cloneDevice(aDevice1); + aDevice1.clearHandleOwner(); // don't close device handle + if( DEBUG ) { + System.err.println("GLEventListenerState.moveFrom.0a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1); + System.err.println("GLEventListenerState.moveFrom.0b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2); + System.err.println("GLEventListenerState.moveFrom.1: "+srcSurface.getClass().getName()/*+", "+aSurface*/); + } + final AbstractGraphicsDevice aUpDevice2; + final boolean proxyOwnsUpstreamDevice; + { + AbstractGraphicsDevice _aUpDevice2 = null; + if(srcSurface instanceof ProxySurface) { + final ProxySurface aProxy = (ProxySurface)srcSurface; + proxyOwnsUpstreamDevice = aProxy.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + final NativeSurface aUpSurface = aProxy.getUpstreamSurface(); + if(DEBUG && null != aUpSurface) { + System.err.println("GLEventListenerState.moveFrom.2: "+aUpSurface.getClass().getName()+", "+aUpSurface); + } + if(null != aUpSurface) { + final AbstractGraphicsDevice aUpDevice1 = aUpSurface.getGraphicsConfiguration().getScreen().getDevice(); + _aUpDevice2 = cloneDevice(aUpDevice1); + aUpDevice1.clearHandleOwner(); // don't close device handle + if(DEBUG) { + System.err.println("GLEventListenerState.moveFrom.3a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1); + System.err.println("GLEventListenerState.moveFrom.3b: up-pres 0x"+Integer.toHexString(_aUpDevice2.hashCode())+", "+_aUpDevice2); + System.err.println("GLEventListenerState.moveFrom.3c: "+srcSurface.getClass().getName()+", "+srcSurface); + System.err.println("GLEventListenerState.moveFrom.3d: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString()); + } } + } else { + proxyOwnsUpstreamDevice = false; } - } else { - proxyOwnsUpstreamDevice = false; + aUpDevice2 = _aUpDevice2; } - aUpDevice2 = _aUpDevice2; - } - glls = new GLEventListenerState(aUpDevice2, proxyOwnsUpstreamDevice, aDevice2, caps, a.getContext(), aSz, aAnim, aAnimStarted); - - // - // remove and cache all GLEventListener and their init-state - // - for(int i=0; i<aSz; i++) { - final GLEventListener l = a.getGLEventListener(0); - glls.listenersInit[i] = a.getGLEventListenerInitState(l); - glls.listeners[i] = a.removeGLEventListener( l ); - } - - // - // trigger glFinish to sync GL ctx - // - a.invoke(true, glFinish); + glls = new GLEventListenerState(aUpDevice2, proxyOwnsUpstreamDevice, aDevice2, caps, + keepLocked ? srcUpstreamLock : null, + srcSurfaceLocked && keepLocked ? srcSurface : null, + src.getContext(), aSz, srcAnim, srcAnimStarted); + + // + // remove and cache all GLEventListener and their init-state + // + for(int i=0; i<aSz; i++) { + final GLEventListener l = src.getGLEventListener(0); + glls.listenersInit[i] = src.getGLEventListenerInitState(l); + glls.listeners[i] = src.removeGLEventListener( l ); + } - a.setContext( null, false ); + src.setContext( null, false ); // implicit glFinish() ctx/drawable sync + } finally { + if( srcSurfaceLocked && !keepLocked ) { + srcSurface.unlockSurface(); + } + } } finally { - if( surfaceLocked ) { - aSurface.unlockSurface(); + if( !keepLocked ) { + srcUpstreamLock.unlock(); } } - return glls; } @@ -240,134 +319,184 @@ public class GLEventListenerState { * This operation is skipped, if the given {@link GLAutoDrawable} is already added to a {@link GLAnimatorControl} instance. * </p> * <p> + * Locking is performed on the {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface}. + * See <a href="../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li> + * </p> + * <p> * Note: After this operation, the GLEventListenerState reference should be released. * </p> * - * @param a {@link GLAutoDrawable} destination to move GLEventListenerState components to + * @param dest {@link GLAutoDrawable} destination to move GLEventListenerState components to * - * <!-- @throws GLException if the {@link GLAutoDrawable}'s configuration is incompatible, i.e. different {@link GLCapabilitiesImmutable}. --> + * @throws GLException if a realized surface could not be locked. * @throws GLException if this preserved {@link AbstractGraphicsDevice} is incompatible w/ the given destination one. + * <!-- @throws GLException if the {@link GLAutoDrawable}'s configuration is incompatible, i.e. different {@link GLCapabilitiesImmutable}. --> * * @see #moveFrom(GLAutoDrawable) * @see #isOwner() */ - public final void moveTo(final GLAutoDrawable a) { - final GLAnimatorControl aAnim = a.getAnimator(); - final boolean hasAnimator = null != aAnim; - final boolean aPaused; - if( hasAnimator ) { - aPaused = aAnim.pause(); - aAnim.remove(a); // also handles ECT - if( aPaused ) { - aAnim.resume(); - } + public final void moveTo(final GLAutoDrawable dest) throws GLException { + this.moveTo(dest, null); + } + + /** + * Moves all GLEventListenerState components to the given {@link GLAutoDrawable} + * from this instance, while loosing {@link #isOwner() ownership}. + * <p> + * If the previous {@link GLAutoDrawable} was removed from a {@link GLAnimatorControl} by previous {@link #moveFrom(GLAutoDrawable, boolean)}, + * the given {@link GLAutoDrawable} is added to the cached {@link GLAnimatorControl}. + * This operation is skipped, if the given {@link GLAutoDrawable} is already added to a {@link GLAnimatorControl} instance. + * </p> + * <p> + * Locking is performed on the {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-lock} and {@link GLAutoDrawable#getNativeSurface() surface}. + * See <a href="../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li> + * </p> + * <p> + * If the {@link GLAutoDrawable} <code>dest</code> has been kept locked by {@link #moveFrom(GLAutoDrawable, boolean)}, + * it's {@link #getUnlockSurfaceOp()} shall be passed here to <code>destUnlockOperation</code> to be unlocked. + * </p> + * <p> + * Note: After this operation, the GLEventListenerState reference should be released. + * </p> + * + * @param dest {@link GLAutoDrawable} destination to move GLEventListenerState components to + * @param destUnlockOperation optional unlock operation for <code>dest</code>, see {@link #moveFrom(GLAutoDrawable, boolean)}. + * + * @throws GLException if a realized surface could not be locked. + * @throws GLException if this preserved {@link AbstractGraphicsDevice} is incompatible w/ the given destination one. + * <!-- @throws GLException if the {@link GLAutoDrawable}'s configuration is incompatible, i.e. different {@link GLCapabilitiesImmutable}. --> + * + * @see #moveFrom(GLAutoDrawable, boolean) + * @see #isOwner() + */ + public final void moveTo(final GLAutoDrawable dest, final Runnable destUnlockOperation) throws GLException { + final GLAnimatorControl destAnim = dest.getAnimator(); + final boolean destAnimPaused; + if( null != destAnim ) { + destAnimPaused = destAnim.pause(); + destAnim.remove(dest); // also handles ECT } else { - aPaused = false; + destAnimPaused = false; } final List<GLRunnable> aGLCmds = new ArrayList<GLRunnable>(); final int aSz = listenerCount(); - final NativeSurface aSurface = a.getNativeSurface(); - final boolean surfaceLocked = false; // NativeSurface.LOCK_SURFACE_NOT_READY < aSurface.lockSurface(); - final boolean aRealized; + final RecursiveLock destUpstreamLock = dest.getUpstreamLock(); + destUpstreamLock.lock(); + final boolean destIsRealized; try { - - final MutableGraphicsConfiguration aCfg = (MutableGraphicsConfiguration) aSurface.getGraphicsConfiguration(); - /** - final GLCapabilitiesImmutable aCaps = (GLCapabilitiesImmutable) aCfg.getChosenCapabilities(); - if( caps.getVisualID(VisualIDHolder.VIDType.INTRINSIC) != aCaps.getVisualID(VisualIDHolder.VIDType.INTRINSIC) || - caps.getVisualID(VisualIDHolder.VIDType.NATIVE) != aCaps.getVisualID(VisualIDHolder.VIDType.NATIVE) ) { - throw new GLException("Incompatible Capabilities - Prev-Holder: "+caps+", New-Holder "+caps); - } */ - final DefaultGraphicsDevice aDevice1 = (DefaultGraphicsDevice) aCfg.getScreen().getDevice(); - final DefaultGraphicsDevice aDevice2 = (DefaultGraphicsDevice) device; - if( !aDevice1.getUniqueID().equals( aDevice2.getUniqueID() ) ) { - throw new GLException("Incompatible devices: Preserved <"+aDevice2.getUniqueID()+">, target <"+aDevice1.getUniqueID()+">"); - } - - // collect optional upstream surface info - final ProxySurface aProxy; - final NativeSurface aUpSurface; - if(aSurface instanceof ProxySurface) { - aProxy = (ProxySurface)aSurface; - aUpSurface = aProxy.getUpstreamSurface(); - } else { - aProxy = null; - aUpSurface = null; - } - if( DEBUG ) { - System.err.println("GLEventListenerState.moveTo.0 : has aProxy "+(null!=aProxy)); - System.err.println("GLEventListenerState.moveTo.0 : has aUpSurface "+(null!=aUpSurface)); - } - if( null==aUpSurface && null != upstreamDevice ) { - throw new GLException("Incompatible Surface config - Has Upstream-Surface: Prev-Holder = true, New-Holder = false"); - } - - // Destroy and remove currently associated GLContext, if any (will be replaced) - a.setContext( null, true ); - aRealized = a.isRealized(); - if( aRealized && null != aUpSurface ) { - // Unrealize due to device dependencies of an upstream surface, e.g. EGLUpstreamSurfaceHook - a.getDelegatedDrawable().setRealized(false); + final NativeSurface destSurface = dest.getNativeSurface(); + final boolean destSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < destSurface.lockSurface(); + if( dest.isRealized() && !destSurfaceLocked ) { + throw new GLException("Could not lock realized surface "+dest); } + try { + + final MutableGraphicsConfiguration aCfg = (MutableGraphicsConfiguration) destSurface.getGraphicsConfiguration(); + /** + final GLCapabilitiesImmutable aCaps = (GLCapabilitiesImmutable) aCfg.getChosenCapabilities(); + if( caps.getVisualID(VisualIDHolder.VIDType.INTRINSIC) != aCaps.getVisualID(VisualIDHolder.VIDType.INTRINSIC) || + caps.getVisualID(VisualIDHolder.VIDType.NATIVE) != aCaps.getVisualID(VisualIDHolder.VIDType.NATIVE) ) { + throw new GLException("Incompatible Capabilities - Prev-Holder: "+caps+", New-Holder "+caps); + } */ + final DefaultGraphicsDevice aDevice1 = (DefaultGraphicsDevice) aCfg.getScreen().getDevice(); + final DefaultGraphicsDevice aDevice2 = (DefaultGraphicsDevice) device; + if( !aDevice1.getUniqueID().equals( aDevice2.getUniqueID() ) ) { + throw new GLException("Incompatible devices: Preserved <"+aDevice2.getUniqueID()+">, target <"+aDevice1.getUniqueID()+">"); + } - // Set new Screen and close previous one - { - if( DEBUG ) { - System.err.println("GLEventListenerState.moveTo.0a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1); - System.err.println("GLEventListenerState.moveTo.0b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2); + // collect optional upstream surface info + final ProxySurface aProxy; + final NativeSurface aUpSurface; + if(destSurface instanceof ProxySurface) { + aProxy = (ProxySurface)destSurface; + aUpSurface = aProxy.getUpstreamSurface(); + } else { + aProxy = null; + aUpSurface = null; } - DefaultGraphicsDevice.swapDeviceHandleAndOwnership(aDevice1, aDevice2); - aDevice2.close(); if( DEBUG ) { - System.err.println("GLEventListenerState.moveTo.1a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1); - System.err.println("GLEventListenerState.moveTo.1b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2); + System.err.println("GLEventListenerState.moveTo.0 : has aProxy "+(null!=aProxy)); + System.err.println("GLEventListenerState.moveTo.0 : has aUpSurface "+(null!=aUpSurface)); + } + if( null==aUpSurface && null != upstreamDevice ) { + throw new GLException("Incompatible Surface config - Has Upstream-Surface: Prev-Holder = true, New-Holder = false"); } - } - // If using a ProxySurface w/ an upstream surface, set new Screen and close previous one on it - if( null != aUpSurface ) { - final MutableGraphicsConfiguration aUpCfg = (MutableGraphicsConfiguration) aUpSurface.getGraphicsConfiguration(); - if( null != upstreamDevice ) { - final DefaultGraphicsDevice aUpDevice1 = (DefaultGraphicsDevice) aUpCfg.getScreen().getDevice(); - final DefaultGraphicsDevice aUpDevice2 = (DefaultGraphicsDevice)upstreamDevice; - if( !aUpDevice1.getUniqueID().equals( aUpDevice2.getUniqueID() ) ) { - throw new GLException("Incompatible updtream devices: Preserved <"+aUpDevice2.getUniqueID()+">, target <"+aUpDevice1.getUniqueID()+">"); - } + // Destroy and remove currently associated GLContext, if any (will be replaced) + dest.setContext( null, true ); + destIsRealized = dest.isRealized(); + if( destIsRealized && null != aUpSurface ) { + // Unrealize due to device dependencies of an upstream surface, e.g. EGLUpstreamSurfaceHook + dest.getDelegatedDrawable().setRealized(false); + } + + // Set new Screen and close previous one + { if( DEBUG ) { - System.err.println("GLEventListenerState.moveTo.2a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1); - System.err.println("GLEventListenerState.moveTo.2b: up-pres 0x"+Integer.toHexString(aUpDevice2.hashCode())+", "+aUpDevice2); - System.err.println("GLEventListenerState.moveTo.2c: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString()); - } - DefaultGraphicsDevice.swapDeviceHandleAndOwnership(aUpDevice1, aUpDevice2); - aUpDevice2.close(); - if( proxyOwnsUpstreamDevice ) { - aProxy.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + System.err.println("GLEventListenerState.moveTo.0a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1); + System.err.println("GLEventListenerState.moveTo.0b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2); } + DefaultGraphicsDevice.swapDeviceHandleAndOwnership(aDevice1, aDevice2); + aDevice2.close(); if( DEBUG ) { - System.err.println("GLEventListenerState.moveTo.3a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1); - System.err.println("GLEventListenerState.moveTo.3b: up-pres 0x"+Integer.toHexString(aUpDevice2.hashCode())+", "+aUpDevice2); - System.err.println("GLEventListenerState.moveTo.3c: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString()); + System.err.println("GLEventListenerState.moveTo.1a: orig 0x"+Integer.toHexString(aDevice1.hashCode())+", "+aDevice1); + System.err.println("GLEventListenerState.moveTo.1b: pres 0x"+Integer.toHexString(aDevice2.hashCode())+", "+aDevice2); } - } else { - throw new GLException("Incompatible Surface config - Has Upstream-Surface: Prev-Holder = false, New-Holder = true"); } - } - if( aRealized && null != aUpSurface ) { - a.getDelegatedDrawable().setRealized(true); - } - if( DEBUG ) { - System.err.println("GLEventListenerState.moveTo.X : has aProxy "+(null!=aProxy)); - System.err.println("GLEventListenerState.moveTo.X : has aUpSurface "+(null!=aUpSurface)); + // If using a ProxySurface w/ an upstream surface, set new Screen and close previous one on it + if( null != aUpSurface ) { + final MutableGraphicsConfiguration aUpCfg = (MutableGraphicsConfiguration) aUpSurface.getGraphicsConfiguration(); + if( null != upstreamDevice ) { + final DefaultGraphicsDevice aUpDevice1 = (DefaultGraphicsDevice) aUpCfg.getScreen().getDevice(); + final DefaultGraphicsDevice aUpDevice2 = (DefaultGraphicsDevice)upstreamDevice; + if( !aUpDevice1.getUniqueID().equals( aUpDevice2.getUniqueID() ) ) { + throw new GLException("Incompatible updtream devices: Preserved <"+aUpDevice2.getUniqueID()+">, target <"+aUpDevice1.getUniqueID()+">"); + } + if( DEBUG ) { + System.err.println("GLEventListenerState.moveTo.2a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1); + System.err.println("GLEventListenerState.moveTo.2b: up-pres 0x"+Integer.toHexString(aUpDevice2.hashCode())+", "+aUpDevice2); + System.err.println("GLEventListenerState.moveTo.2c: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString()); + } + DefaultGraphicsDevice.swapDeviceHandleAndOwnership(aUpDevice1, aUpDevice2); + aUpDevice2.close(); + if( proxyOwnsUpstreamDevice ) { + aProxy.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); + } + if( DEBUG ) { + System.err.println("GLEventListenerState.moveTo.3a: up-orig 0x"+Integer.toHexString(aUpDevice1.hashCode())+", "+aUpDevice1); + System.err.println("GLEventListenerState.moveTo.3b: up-pres 0x"+Integer.toHexString(aUpDevice2.hashCode())+", "+aUpDevice2); + System.err.println("GLEventListenerState.moveTo.3c: "+aUpSurface.getClass().getName()/*+", "+aUpSurface+", "*/+aProxy.getUpstreamOptionBits(null).toString()); + } + } else { + throw new GLException("Incompatible Surface config - Has Upstream-Surface: Prev-Holder = false, New-Holder = true"); + } + } + + if( destIsRealized && null != aUpSurface ) { + dest.getDelegatedDrawable().setRealized(true); + } + if( DEBUG ) { + System.err.println("GLEventListenerState.moveTo.X : has aProxy "+(null!=aProxy)); + System.err.println("GLEventListenerState.moveTo.X : has aUpSurface "+(null!=aUpSurface)); + } + dest.setContext( context, false ); + } finally { + if( destSurfaceLocked ) { + destSurface.unlockSurface(); + } } - a.setContext( context, false ); } finally { - if( surfaceLocked ) { - aSurface.unlockSurface(); - } + destUpstreamLock.unlock(); + } + if( null != destUnlockOperation ) { + destUnlockOperation.run(); } + owner = false; // @@ -376,36 +505,36 @@ public class GLEventListenerState { aGLCmds.add(setViewport); for(int i=0; i<aSz; i++) { if( listenersInit[i] ) { - aGLCmds.add(new ReshapeGLEventListener( listeners[i] ) ); + aGLCmds.add(new GLDrawableUtil.ReshapeGLEventListener( listeners[i], false ) ); } } aGLCmds.add(glFinish); - a.invoke(aRealized, aGLCmds); // only wait if already realized + dest.invoke(destIsRealized, aGLCmds); // only wait if already realized // add all cached GLEventListener to their destination and fix their init-state for(int i=0; i<aSz; i++) { final GLEventListener l = listeners[i]; - a.addGLEventListener( l ); - a.setGLEventListenerInitState(l, listenersInit[i]); + dest.addGLEventListener( l ); + dest.setGLEventListenerInitState(l, listenersInit[i]); listeners[i] = null; } - if( hasAnimator ) { + if( null != destAnim ) { // prefer already bound animator - aAnim.add(a); - if( aPaused ) { - aAnim.resume(); + destAnim.add(dest); + if( destAnimPaused ) { + destAnim.resume(); } } else if ( null != anim ) { // use previously bound animator - anim.add(a); // also handles ECT + anim.add(dest); // also handles ECT if(animStarted) { anim.start(); } } } - public static final GLRunnable setViewport = new GLRunnable() { + private static final GLRunnable setViewport = new GLRunnable() { @Override public boolean run(final GLAutoDrawable drawable) { drawable.getGL().glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); @@ -413,23 +542,11 @@ public class GLEventListenerState { } }; - public static final GLRunnable glFinish = new GLRunnable() { + private static final GLRunnable glFinish = new GLRunnable() { @Override public boolean run(final GLAutoDrawable drawable) { drawable.getGL().glFinish(); return true; } }; - - public static class ReshapeGLEventListener implements GLRunnable { - private final GLEventListener listener; - public ReshapeGLEventListener(final GLEventListener listener) { - this.listener = listener; - } - @Override - public boolean run(final GLAutoDrawable drawable) { - listener.reshape(drawable, 0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); - return true; - } - } } diff --git a/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java b/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java index 83fceeb2f..e8039edf1 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java +++ b/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java @@ -191,7 +191,12 @@ public class GLRendererQuirks { * <li>GL_RENDERER: <i>Gallium 0.4 on SVGA3D; build: RELEASE;</i> </li> * </ul></li> * </ul> + * <p> + * Also enabled via {@link #BuggyColorRenderbuffer}. + * </p> + * <p> * Quirk can also be enabled via property: <code>jogl.fbo.force.min</code>. + * </p> */ public static final int NoFullFBOSupport = 11; @@ -312,8 +317,32 @@ public class GLRendererQuirks { */ public static final int NoMultiSamplingBuffers = 17; - /** Number of quirks known. */ - public static final int COUNT = 18; + /** + * With certain drivers no reliable FBO color renderbuffer target + * is available, read <i>a crash</i> may occur. + * <p> + * Appears on: + * <ul> + * <li>GL_VENDOR Brian Paul</li> + * <li>GL_RENDERER Mesa X11</li> + * <li>GL_VERSION 2.1 Mesa 7.2</li> + * </ul> + * TODO: We have to determine the exact version range, i.e. not adding the quirk with fixed driver version! + * </p> + * <p> + * Note: Also enables {@link #NoFullFBOSupport}. + * </p> + * <p> + * Note: GLFBODrawable always uses texture attachments if set. + * </p> + * <p> + * Quirk can also be enabled via property: <code>jogl.fbo.force.nocolorrenderbuffer</code>. + * </p> + */ + public static final int BuggyColorRenderbuffer = 18; + + /** Return the number of known quirks. */ + public static final int getCount() { return 19; } private static final String[] _names = new String[] { "NoDoubleBufferedPBuffer", "NoDoubleBufferedBitmap", "NoSetSwapInterval", "NoOffscreenBitmap", "NoSetSwapIntervalPostRetarget", "GLSLBuggyDiscard", @@ -321,7 +350,7 @@ public class GLRendererQuirks { "NeedCurrCtx4ARBPixFmtQueries", "NeedCurrCtx4ARBCreateContext", "NoFullFBOSupport", "GLSLNonCompliant", "GL4NeedsGL3Request", "GLSharedContextBuggy", "GLES3ViaEGLES2Config", "SingletonEGLDisplayOnly", - "NoMultiSamplingBuffers" + "NoMultiSamplingBuffers", "BuggyColorRenderbuffer" }; private static final IdentityHashMap<String, GLRendererQuirks> stickyDeviceQuirks = new IdentityHashMap<String, GLRendererQuirks>(); @@ -358,6 +387,17 @@ public class GLRendererQuirks { } /** + * {@link #addQuirk(int) Adding given quirk} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}. + * <p> + * Not thread safe. + * </p> + * @see #getStickyDeviceQuirks(AbstractGraphicsDevice) + */ + public static void addStickyDeviceQuirk(final AbstractGraphicsDevice device, final int quirk) throws IllegalArgumentException { + final GLRendererQuirks sq = getStickyDeviceQuirks(device); + sq.addQuirk(quirk); + } + /** * {@link #addQuirks(int[], int, int) Adding given quirks} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}. * <p> * Not thread safe. @@ -419,6 +459,15 @@ public class GLRendererQuirks { } /** + * @param quirk valid quirk to be added + * @throws IllegalArgumentException if the quirk is out of range + */ + public final void addQuirk(final int quirk) throws IllegalArgumentException { + validateQuirk(quirk); + _bitmask |= 1 << quirk; + } + + /** * @param quirks an array of valid quirks to be added * @param offset offset in quirks array to start reading * @param len number of quirks to read from offset within quirks array @@ -460,7 +509,7 @@ public class GLRendererQuirks { } sb.append("["); boolean first=true; - for(int i=0; i<COUNT; i++) { + for(int i=0; i<getCount(); i++) { final int testmask = 1 << i; if( 0 != ( _bitmask & testmask ) ) { if(!first) { sb.append(", "); } @@ -482,8 +531,8 @@ public class GLRendererQuirks { * @throws IllegalArgumentException if quirk is out of range */ public static void validateQuirk(final int quirk) throws IllegalArgumentException { - if( !( 0 <= quirk && quirk < COUNT ) ) { - throw new IllegalArgumentException("Quirks must be in range [0.."+COUNT+"[, but quirk: "+quirk); + if( !( 0 <= quirk && quirk < getCount() ) ) { + throw new IllegalArgumentException("Quirks must be in range [0.."+getCount()+"[, but quirk: "+quirk); } } diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java index e58d8c64a..28f572d7d 100644 --- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -85,6 +85,12 @@ import com.jogamp.opengl.JoglVersion; * <p> * Implementation allows use of custom {@link GLCapabilities}. * </p> + * <p> + * <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> + * To share a {@link GLContext} see the following note in the documentation overview: + * <a href="../../../../overview-summary.html#SHARING">context sharing</a> + * as well as {@link GLSharedContextSetter}. + * </p> */ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextSetter { private static final boolean DEBUG = Debug.debug("GLCanvas"); @@ -205,10 +211,9 @@ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextS animatorPaused = false; } - if ( null != context ) { + GLException exceptionOnDisposeGL = null; + if( null != context ) { if( context.isCreated() ) { - // Catch dispose GLExceptions by GLEventListener, just 'print' them - // so we can continue with the destruction. try { if( !GLCanvas.this.isDisposed() ) { helper.disposeGL(GLCanvas.this, context, true); @@ -216,28 +221,50 @@ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextS context.destroy(); } } catch (final GLException gle) { - gle.printStackTrace(); + exceptionOnDisposeGL = gle; } } context = null; } - if ( null != drawable ) { - drawable.setRealized(false); + + Throwable exceptionOnUnrealize = null; + if( null != drawable ) { + try { + drawable.setRealized(false); + } catch( final Throwable re ) { + exceptionOnUnrealize = re; + } drawable = null; } - if( 0 != x11Window) { - SWTAccessor.destroyX11Window(screen.getDevice(), x11Window); - x11Window = 0; - } else if( 0 != gdkWindow) { - SWTAccessor.destroyGDKWindow(gdkWindow); - gdkWindow = 0; + + Throwable exceptionOnDeviceClose = null; + try { + if( 0 != x11Window) { + SWTAccessor.destroyX11Window(screen.getDevice(), x11Window); + x11Window = 0; + } else if( 0 != gdkWindow) { + SWTAccessor.destroyGDKWindow(gdkWindow); + gdkWindow = 0; + } + screen.getDevice().close(); + } catch (final Throwable re) { + exceptionOnDeviceClose = re; } - screen.getDevice().close(); if (animatorPaused) { animator.resume(); } + // throw exception in order of occurrence .. + if( null != exceptionOnDisposeGL ) { + throw exceptionOnDisposeGL; + } + if( null != exceptionOnUnrealize ) { + throw GLException.newGLException(exceptionOnUnrealize); + } + if( null != exceptionOnDeviceClose ) { + throw GLException.newGLException(exceptionOnDeviceClose); + } } finally { _lock.unlock(); } @@ -638,6 +665,9 @@ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextS } @Override + public final RecursiveLock getUpstreamLock() { return lock; } + + @Override public int getSurfaceWidth() { return clientArea.width; } @@ -755,16 +785,21 @@ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextS } @Override - public boolean invoke(final boolean wait, final GLRunnable runnable) { + public boolean invoke(final boolean wait, final GLRunnable runnable) throws IllegalStateException { return helper.invoke(this, wait, runnable); } @Override - public boolean invoke(final boolean wait, final List<GLRunnable> runnables) { + public boolean invoke(final boolean wait, final List<GLRunnable> runnables) throws IllegalStateException { return helper.invoke(this, wait, runnables); } @Override + public void flushGLRunnables() { + helper.flushGLRunnables(); + } + + @Override public void setAnimator(final GLAnimatorControl arg0) throws GLException { helper.setAnimator(arg0); } @@ -829,14 +864,10 @@ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextS return null != _drawable ? (GLCapabilitiesImmutable)_drawable.getChosenGLCapabilities() : null; } - /** - * Accessor for the GLCapabilities that were requested (via the constructor parameter). - * - * @return Non-null GLCapabilities. - */ + @Override public GLCapabilitiesImmutable getRequestedGLCapabilities() { final GLDrawable _drawable = drawable; - return null != _drawable ? (GLCapabilitiesImmutable)_drawable.getNativeSurface().getGraphicsConfiguration().getRequestedCapabilities() : null; + return null != _drawable ? (GLCapabilitiesImmutable)_drawable.getRequestedGLCapabilities() : null; } @Override @@ -879,6 +910,15 @@ public class GLCanvas extends Canvas implements GLAutoDrawable, GLSharedContextS } /** + * {@inheritDoc} + * <p> + * Implementation always supports multithreading, hence method always returns <code>true</code>. + * </p> + */ + @Override + public final boolean isThreadGLCapable() { return true; } + + /** * Runs the specified action in an SWT compatible thread, which is: * <ul> * <li>Mac OSX diff --git a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java index 496fb88c0..62df3faca 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java @@ -41,12 +41,14 @@ import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; + import javax.swing.JComponent; import javax.swing.RepaintManager; import javax.swing.SwingUtilities; - import javax.media.opengl.GLAutoDrawable; +import com.jogamp.opengl.util.AnimatorBase.UncaughtAnimatorException; + /** Abstraction to factor out AWT dependencies from the Animator's implementation in a way that still allows the FPSAnimator to pick up this behavior if desired. */ @@ -61,7 +63,7 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { @Override public void display(final ArrayList<GLAutoDrawable> drawables, final boolean ignoreExceptions, - final boolean printExceptions) { + final boolean printExceptions) throws UncaughtAnimatorException { for (int i=0; i<drawables.size(); i++) { final GLAutoDrawable drawable = drawables.get(i); if (drawable instanceof JComponent) { @@ -73,13 +75,13 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { } else { try { drawable.display(); - } catch (final RuntimeException e) { + } catch (final Throwable t) { if (ignoreExceptions) { if (printExceptions) { - e.printStackTrace(); + t.printStackTrace(); } } else { - throw(e); + throw new UncaughtAnimatorException(drawable, t); } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/Animator.java b/src/jogl/classes/com/jogamp/opengl/util/Animator.java index ae09d81a4..b38a42ee3 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/Animator.java +++ b/src/jogl/classes/com/jogamp/opengl/util/Animator.java @@ -58,12 +58,12 @@ import javax.media.opengl.GLException; * </p> */ public class Animator extends AnimatorBase { - protected ThreadGroup threadGroup; + private ThreadGroup threadGroup; private Runnable runnable; private boolean runAsFastAsPossible; - protected boolean isAnimating; - protected boolean pauseIssued; - protected volatile boolean stopIssued; + boolean isAnimating; + volatile boolean pauseIssued; + volatile boolean stopIssued; /** * Creates a new, empty Animator. @@ -132,6 +132,9 @@ public class Animator extends AnimatorBase { @Override public void run() { + ThreadDeath caughtThreadDeath = null; + UncaughtAnimatorException caughtException = null; + try { synchronized (Animator.this) { if(DEBUG) { @@ -147,7 +150,7 @@ public class Animator extends AnimatorBase { synchronized (Animator.this) { // Pause; Also don't consume CPU unless there is work to be done and not paused boolean ectCleared = false; - while (!stopIssued && (pauseIssued || drawablesEmpty)) { + while ( !stopIssued && ( pauseIssued || drawablesEmpty ) ) { if( drawablesEmpty ) { pauseIssued = true; } @@ -158,7 +161,13 @@ public class Animator extends AnimatorBase { if ( exclusiveContext && !drawablesEmpty && !ectCleared ) { ectCleared = true; setDrawablesExclCtxState(false); - display(); // propagate exclusive change! + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + caughtException = dre; + stopIssued = true; + break; // end pause loop + } } isAnimating = false; Animator.this.notifyAll(); @@ -180,38 +189,72 @@ public class Animator extends AnimatorBase { // Resume from pause or drawablesEmpty, // implies !pauseIssued and !drawablesEmpty isAnimating = true; - setDrawablesExclCtxState(exclusiveContext); + setDrawablesExclCtxState(exclusiveContext); // may re-enable exclusive context Animator.this.notifyAll(); } } // sync Animator.this - if (!stopIssued) { - display(); - } - if (!stopIssued && !runAsFastAsPossible) { - // Avoid swamping the CPU - Thread.yield(); + if ( !pauseIssued && !stopIssued ) { + try { + display(); + } catch (final UncaughtAnimatorException dre) { + caughtException = dre; + stopIssued = true; + break; // end animation loop + } + if ( !runAsFastAsPossible ) { + // Avoid swamping the CPU + Thread.yield(); + } } } - } catch( final ThreadDeath td) { + } catch(final ThreadDeath td) { if(DEBUG) { System.err.println("Animator caught: "+td.getClass().getName()+": "+td.getMessage()); td.printStackTrace(); } - } finally { - if( exclusiveContext && !drawablesEmpty ) { - setDrawablesExclCtxState(false); - display(); // propagate exclusive change! + caughtThreadDeath = td; + } + if( exclusiveContext && !drawablesEmpty ) { + setDrawablesExclCtxState(false); + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + if( null == caughtException ) { + caughtException = dre; + } else { + System.err.println("Animator.setExclusiveContextThread: caught: "+dre.getMessage()); + dre.printStackTrace(); + } } - synchronized (Animator.this) { - if(DEBUG) { - System.err.println("Animator stop on " + animThread.getName() + ": " + toString()); + } + boolean flushGLRunnables = false; + boolean throwCaughtException = false; + synchronized (Animator.this) { + if(DEBUG) { + System.err.println("Animator stop on " + animThread.getName() + ": " + toString()); + if( null != caughtException ) { + System.err.println("Animator caught: "+caughtException.getMessage()); + caughtException.printStackTrace(); } - stopIssued = false; - pauseIssued = false; - animThread = null; - isAnimating = false; - Animator.this.notifyAll(); } + stopIssued = false; + pauseIssued = false; + isAnimating = false; + if( null != caughtException ) { + flushGLRunnables = true; + throwCaughtException = !handleUncaughtException(caughtException); + } + animThread = null; + Animator.this.notifyAll(); + } + if( flushGLRunnables ) { + flushGLRunnables(); + } + if( throwCaughtException ) { + throw caughtException; + } + if( null != caughtThreadDeath ) { + throw caughtThreadDeath; } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java index a5bdb9350..bc159ef5c 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java @@ -67,9 +67,26 @@ public abstract class AnimatorBase implements GLAnimatorControl { */ public static final int MODE_EXPECT_AWT_RENDERING_THREAD = 1 << 0; - public interface AnimatorImpl { - void display(ArrayList<GLAutoDrawable> drawables, boolean ignoreExceptions, boolean printExceptions); - boolean blockUntilDone(Thread thread); + + @SuppressWarnings("serial") + public static class UncaughtAnimatorException extends RuntimeException { + final GLAutoDrawable drawable; + public UncaughtAnimatorException(final GLAutoDrawable drawable, final Throwable cause) { + super(cause); + this.drawable = drawable; + } + public final GLAutoDrawable getGLAutoDrawable() { return drawable; } + } + + public static interface AnimatorImpl { + /** + * @param drawables + * @param ignoreExceptions + * @param printExceptions + * @throws UncaughtAnimatorException as caused by {@link GLAutoDrawable#display()} + */ + void display(final ArrayList<GLAutoDrawable> drawables, final boolean ignoreExceptions, final boolean printExceptions) throws UncaughtAnimatorException; + boolean blockUntilDone(final Thread thread); } protected int modeBits; @@ -83,6 +100,7 @@ public abstract class AnimatorBase implements GLAnimatorControl { protected boolean printExceptions; protected boolean exclusiveContext; protected Thread userExclusiveContextThread; + protected UncaughtExceptionHandler uncaughtExceptionHandler; protected FPSCounterImpl fpsCounter = new FPSCounterImpl(); private final static Class<?> awtAnimatorImplClazz; @@ -313,11 +331,16 @@ public abstract class AnimatorBase implements GLAnimatorControl { } } final Thread dECT = enable ? ( null != _exclusiveContextThread ? _exclusiveContextThread : animThread ) : null ; + UncaughtAnimatorException displayCaught = null; if( propagateState ) { setDrawablesExclCtxState(enable); if( !enable ) { if( Thread.currentThread() == getThread() || Thread.currentThread() == _exclusiveContextThread ) { - display(); + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + displayCaught = dre; + } } else { final boolean resumed = isAnimating() ? false : resume(); int counter = 10; @@ -338,6 +361,13 @@ public abstract class AnimatorBase implements GLAnimatorControl { } if(DEBUG) { System.err.println("AnimatorBase.setExclusiveContextThread: all-GLAD Ok: "+validateDrawablesExclCtxState(dECT)+", "+this); + if( null != displayCaught ) { + System.err.println("AnimatorBase.setExclusiveContextThread: caught: "+displayCaught.getMessage()); + displayCaught.printStackTrace(); + } + } + if( null != displayCaught ) { + throw displayCaught; } return oldExclusiveContext; } @@ -412,7 +442,7 @@ public abstract class AnimatorBase implements GLAnimatorControl { this to get the most optimized painting behavior for the set of components this Animator manages, in particular when multiple lightweight widgets are continually being redrawn. */ - protected final void display() { + protected final void display() throws UncaughtAnimatorException { impl.display(drawables, ignoreExceptions, printExceptions); fpsCounter.tickFPS(); } @@ -482,7 +512,47 @@ public abstract class AnimatorBase implements GLAnimatorControl { this.printExceptions = printExceptions; } - protected interface Condition { + @Override + public final UncaughtExceptionHandler getUncaughtExceptionHandler() { + return uncaughtExceptionHandler; + } + + @Override + public final void setUncaughtExceptionHandler(final UncaughtExceptionHandler handler) { + uncaughtExceptionHandler = handler; + } + + /** + * Should be called in case of an uncaught exception + * from within the animator thread, throws given exception if no handler has been installed. + * @return {@code true} if handled, otherwise {@code false}. In case of {@code false}, + * caller needs to propagate the exception. + */ + protected final synchronized boolean handleUncaughtException(final UncaughtAnimatorException ue) { + if( null != uncaughtExceptionHandler ) { + try { + uncaughtExceptionHandler.uncaughtException(this, ue.getGLAutoDrawable(), ue.getCause()); + } catch (final Throwable t) { /* ignore intentionally */ } + return true; + } else { + return false; + } + } + + /** + * Should be called in case of an uncaught exception + * from within the animator thread to flush all animator. + * <p> + * The animator instance shall not be locked when calling this method! + * </p> + */ + protected final void flushGLRunnables() { + for (int i=0; i<drawables.size(); i++) { + drawables.get(i).flushGLRunnables(); + } + } + + protected static interface Condition { /** * @return true if branching (continue waiting, action), otherwise false */ @@ -493,7 +563,8 @@ public abstract class AnimatorBase implements GLAnimatorControl { * @param waitCondition method will wait until TO is reached or {@link Condition#eval() waitCondition.eval()} returns <code>false</code>. * @param pollPeriod if <code>0</code>, method will wait until TO is reached or being notified. * if > <code>0</code>, method will wait for the given <code>pollPeriod</code> in milliseconds. - * @return <code>true</code> if {@link Condition#eval() waitCondition.eval()} returned <code>false</code>, otherwise <code>false</code>. + * @return <code>true</code> if {@link Condition#eval() waitCondition.eval()} returned <code>false</code> + * or if {@link AnimatorImpl#blockUntilDone(Thread) non-blocking}. Otherwise returns <code>false</code>. */ protected final synchronized boolean finishLifecycleAction(final Condition waitCondition, long pollPeriod) { /** @@ -506,6 +577,7 @@ public abstract class AnimatorBase implements GLAnimatorControl { final boolean blocking; long remaining; boolean nok; + if( impl.blockUntilDone(animThread) ) { blocking = true; remaining = TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION; @@ -539,12 +611,13 @@ public abstract class AnimatorBase implements GLAnimatorControl { nok = waitCondition.eval(); } } + final boolean res = !nok || !blocking; if(DEBUG || blocking && nok) { // Info only if DEBUG or ( blocking && not-ok ) ; !blocking possible if AWT if( blocking && remaining<=0 && nok ) { System.err.println("finishLifecycleAction(" + waitCondition.getClass().getName() + "): ++++++ timeout reached ++++++ " + getThreadName()); } System.err.println("finishLifecycleAction(" + waitCondition.getClass().getName() + "): OK "+(!nok)+ - "- pollPeriod "+pollPeriod+", blocking "+blocking+ + "- pollPeriod "+pollPeriod+", blocking "+blocking+" -> res "+res+ ", waited " + (blocking ? ( TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION - remaining ) : 0 ) + "/" + TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION + " - " + getThreadName()); System.err.println(" - "+toString()); @@ -552,7 +625,7 @@ public abstract class AnimatorBase implements GLAnimatorControl { Thread.dumpStack(); } } - return !nok; + return res; } @Override diff --git a/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java index 7fa4011f8..6b1485a6a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java @@ -34,8 +34,11 @@ package com.jogamp.opengl.util; import java.util.ArrayList; + import javax.media.opengl.GLAutoDrawable; +import com.jogamp.opengl.util.AnimatorBase.UncaughtAnimatorException; + /** Abstraction to factor out AWT dependencies from the Animator's implementation in a way that still allows the FPSAnimator to pick up this behavior if desired. */ @@ -44,18 +47,18 @@ class DefaultAnimatorImpl implements AnimatorBase.AnimatorImpl { @Override public void display(final ArrayList<GLAutoDrawable> drawables, final boolean ignoreExceptions, - final boolean printExceptions) { + final boolean printExceptions) throws UncaughtAnimatorException { for (int i=0; i<drawables.size(); i++) { final GLAutoDrawable drawable = drawables.get(i); try { drawable.display(); - } catch (final RuntimeException e) { + } catch (final Throwable t) { if (ignoreExceptions) { if (printExceptions) { - e.printStackTrace(); + t.printStackTrace(); } } else { - throw(e); + throw new UncaughtAnimatorException(drawable, t); } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java index 746b642c2..54d40f285 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java +++ b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java @@ -60,9 +60,9 @@ public class FPSAnimator extends AnimatorBase { private MainTask task = null; private int fps; private final boolean scheduleAtFixedRate; - private boolean isAnimating; // MainTask feedback - private volatile boolean shouldRun; // MainTask trigger - private volatile boolean shouldStop; // MainTask trigger + private boolean isAnimating; // MainTask feedback + private volatile boolean pauseIssued; // MainTask trigger + private volatile boolean stopIssued; // MainTask trigger @Override protected String getBaseName(final String prefix) { @@ -124,8 +124,9 @@ public class FPSAnimator extends AnimatorBase { public void start(final Timer timer) { fpsCounter.resetFPSCounter(); - shouldRun = true; - shouldStop = false; + pauseIssued = false; + stopIssued = false; + isAnimating = false; justStarted = true; alreadyStopped = false; @@ -143,11 +144,13 @@ public class FPSAnimator extends AnimatorBase { @Override public final String toString() { - return "Task[thread "+animThread+", stopped "+alreadyStopped+", paused "+alreadyPaused+" shouldRun "+shouldRun+", shouldStop "+shouldStop+" -- started "+isStarted()+", animating "+isAnimatingImpl()+", paused "+isPaused()+", drawable "+drawables.size()+", drawablesEmpty "+drawablesEmpty+"]"; + return "Task[thread "+animThread+", stopped "+alreadyStopped+", paused "+alreadyPaused+" pauseIssued "+pauseIssued+", stopIssued "+stopIssued+" -- started "+isStarted()+", animating "+isAnimatingImpl()+", paused "+isPaused()+", drawable "+drawables.size()+", drawablesEmpty "+drawablesEmpty+"]"; } @Override public void run() { + UncaughtAnimatorException caughtException = null; + if( justStarted ) { justStarted = false; synchronized (FPSAnimator.this) { @@ -157,59 +160,97 @@ public class FPSAnimator extends AnimatorBase { } isAnimating = true; if( drawablesEmpty ) { - shouldRun = false; // isAnimating:=false @ pause below + pauseIssued = true; // isAnimating:=false @ pause below } else { - shouldRun = true; - setDrawablesExclCtxState(exclusiveContext); - FPSAnimator.this.notifyAll(); + pauseIssued = false; + setDrawablesExclCtxState(exclusiveContext); // may re-enable exclusive context } + FPSAnimator.this.notifyAll(); // Wakes up 'waitForStartedCondition' sync -and resume from pause or drawablesEmpty if(DEBUG) { System.err.println("FPSAnimator P1:" + Thread.currentThread() + ": " + toString()); } } } - if( shouldRun ) { - display(); - } else if( shouldStop ) { // STOP + if( !pauseIssued && !stopIssued ) { // RUN + try { + display(); + } catch (final UncaughtAnimatorException dre) { + caughtException = dre; + stopIssued = true; + } + } else if( pauseIssued && !stopIssued ) { // PAUSE if(DEBUG) { - System.err.println("FPSAnimator P4: "+alreadyStopped+", "+ Thread.currentThread() + ": " + toString()); + System.err.println("FPSAnimator pausing: "+alreadyPaused+", "+ Thread.currentThread() + ": " + toString()); } this.cancel(); - if( !alreadyStopped ) { - alreadyStopped = true; + if( !alreadyPaused ) { // PAUSE + alreadyPaused = true; if( exclusiveContext && !drawablesEmpty ) { setDrawablesExclCtxState(false); - display(); // propagate exclusive change! + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + caughtException = dre; + stopIssued = true; + } } - synchronized (FPSAnimator.this) { - if(DEBUG) { - System.err.println("FPSAnimator stop " + Thread.currentThread() + ": " + toString()); + if( null == caughtException ) { + synchronized (FPSAnimator.this) { + if(DEBUG) { + System.err.println("FPSAnimator pause " + Thread.currentThread() + ": " + toString()); + } + isAnimating = false; + FPSAnimator.this.notifyAll(); } - animThread = null; - isAnimating = false; - FPSAnimator.this.notifyAll(); } } - } else { + } + if( stopIssued ) { // STOP incl. immediate exception handling of 'displayCaught' if(DEBUG) { - System.err.println("FPSAnimator P5: "+alreadyPaused+", "+ Thread.currentThread() + ": " + toString()); + System.err.println("FPSAnimator stopping: "+alreadyStopped+", "+ Thread.currentThread() + ": " + toString()); } this.cancel(); - if( !alreadyPaused ) { // PAUSE - alreadyPaused = true; + if( !alreadyStopped ) { + alreadyStopped = true; if( exclusiveContext && !drawablesEmpty ) { setDrawablesExclCtxState(false); - display(); // propagate exclusive change! + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + if( null == caughtException ) { + caughtException = dre; + } else { + System.err.println("FPSAnimator.setExclusiveContextThread: caught: "+dre.getMessage()); + dre.printStackTrace(); + } + } } + boolean flushGLRunnables = false; + boolean throwCaughtException = false; synchronized (FPSAnimator.this) { if(DEBUG) { - System.err.println("FPSAnimator pause " + Thread.currentThread() + ": " + toString()); + System.err.println("FPSAnimator stop " + Thread.currentThread() + ": " + toString()); + if( null != caughtException ) { + System.err.println("Animator caught: "+caughtException.getMessage()); + caughtException.printStackTrace(); + } } isAnimating = false; + if( null != caughtException ) { + flushGLRunnables = true; + throwCaughtException = !handleUncaughtException(caughtException); + } + animThread = null; FPSAnimator.this.notifyAll(); } + if( flushGLRunnables ) { + flushGLRunnables(); + } + if( throwCaughtException ) { + throw caughtException; + } } } } @@ -224,7 +265,7 @@ public class FPSAnimator extends AnimatorBase { @Override public final synchronized boolean isPaused() { - return animThread != null && ( !shouldRun && !shouldStop ) ; + return animThread != null && pauseIssued; } static int timerNo = 0; @@ -279,8 +320,7 @@ public class FPSAnimator extends AnimatorBase { // start/resume case w/ drawablesEmpty res = true; } else { - shouldRun = false; - shouldStop = true; + stopIssued = true; res = finishLifecycleAction(waitForStoppedCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION); } @@ -306,7 +346,7 @@ public class FPSAnimator extends AnimatorBase { @Override public final synchronized boolean pause() { - if ( !isStarted() || ( null != task && isPaused() ) ) { + if ( !isStarted() || pauseIssued ) { return false; } if(DEBUG) { @@ -317,7 +357,7 @@ public class FPSAnimator extends AnimatorBase { // start/resume case w/ drawablesEmpty res = true; } else { - shouldRun = false; + pauseIssued = true; res = finishLifecycleAction(waitForPausedCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION); } @@ -334,12 +374,12 @@ public class FPSAnimator extends AnimatorBase { @Override public boolean eval() { // end waiting if stopped as well - return isAnimating && isStarted(); + return isStarted() && isAnimating; } }; @Override public final synchronized boolean resume() { - if ( null != task || !isStarted() || !isPaused() ) { + if ( !isStarted() || !pauseIssued ) { return false; } if(DEBUG) { @@ -349,6 +389,14 @@ public class FPSAnimator extends AnimatorBase { if( drawablesEmpty ) { res = true; } else { + if( null != task ) { + if( DEBUG ) { + System.err.println("FPSAnimator.resume() Ops: !pauseIssued, but task != null: "+toString()); + Thread.dumpStack(); + } + task.cancel(); + task = null; + } task = new MainTask(); task.start(timer); res = finishLifecycleAction(waitForResumeCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION); diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLBuffers.java b/src/jogl/classes/com/jogamp/opengl/util/GLBuffers.java index 44be29957..d4ab4e4f4 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLBuffers.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLBuffers.java @@ -46,6 +46,7 @@ import javax.media.opengl.GL2; import javax.media.opengl.GL2ES2; import javax.media.opengl.GL2ES3; import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLContext; import javax.media.opengl.GLES2; import javax.media.opengl.GLException; @@ -372,25 +373,29 @@ public class GLBuffers extends Buffers { if (pack) { alignment = glGetInteger(gl, GL.GL_PACK_ALIGNMENT, tmp); - if(gl.isGL2GL3()) { + if( gl.isGL2ES3() ) { rowLength = glGetInteger(gl, GL2ES3.GL_PACK_ROW_LENGTH, tmp); skipRows = glGetInteger(gl, GL2ES3.GL_PACK_SKIP_ROWS, tmp); skipPixels = glGetInteger(gl, GL2ES3.GL_PACK_SKIP_PIXELS, tmp); - if (depth > 1) { + if (depth > 1 && gl.isGL2GL3() && gl.getContext().getGLVersionNumber().compareTo(GLContext.Version120) >= 0 ) { imageHeight = glGetInteger(gl, GL2GL3.GL_PACK_IMAGE_HEIGHT, tmp); skipImages = glGetInteger(gl, GL2GL3.GL_PACK_SKIP_IMAGES, tmp); } } } else { alignment = glGetInteger(gl, GL.GL_UNPACK_ALIGNMENT, tmp); - if(gl.isGL2GL3 ()) { + if( gl.isGL2ES3() ) { rowLength = glGetInteger(gl, GL2ES2.GL_UNPACK_ROW_LENGTH, tmp); skipRows = glGetInteger(gl, GL2ES2.GL_UNPACK_SKIP_ROWS, tmp); skipPixels = glGetInteger(gl, GL2ES2.GL_UNPACK_SKIP_PIXELS, tmp); - if (depth > 1) { + if( depth > 1 && + ( gl.isGL3ES3() || + ( gl.isGL2GL3() && gl.getContext().getGLVersionNumber().compareTo(GLContext.Version120) >= 0 ) + ) + ) { imageHeight = glGetInteger(gl, GL2ES3.GL_UNPACK_IMAGE_HEIGHT, tmp); skipImages = glGetInteger(gl, GL2ES3.GL_UNPACK_SKIP_IMAGES, tmp); - } + } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java index c74284299..956693c4a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java @@ -28,6 +28,7 @@ package com.jogamp.opengl.util; import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeSurface; import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLBase; @@ -36,7 +37,10 @@ import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawable; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; +import javax.media.opengl.GLRunnable; +import javax.media.opengl.Threading; +import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.opengl.GLEventListenerState; import jogamp.opengl.Debug; @@ -45,148 +49,298 @@ import jogamp.opengl.Debug; * Providing utility functions dealing w/ {@link GLDrawable}s, {@link GLAutoDrawable} and their {@link GLEventListener}. */ public class GLDrawableUtil { - protected static final boolean DEBUG = Debug.debug("GLDrawable"); - - public static final boolean isAnimatorStartedOnOtherThread(final GLAnimatorControl animatorCtrl) { - return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ; - } - - public static final boolean isAnimatorStarted(final GLAnimatorControl animatorCtrl) { - return ( null != animatorCtrl ) ? animatorCtrl.isStarted() : false ; - } - - public static final boolean isAnimatorAnimatingOnOtherThread(final GLAnimatorControl animatorCtrl) { - return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ; - } - - public static final boolean isAnimatorAnimating(final GLAnimatorControl animatorCtrl) { - return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() : false ; - } - - /** - * Moves the designated {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>. - * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved - * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call. - * <p> - * Note that it is only legal to pass <code>preserveInitState := true</code>, - * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>. - * </p> - * <p> - * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}. - * </p> - * @param src - * @param dest - * @param listener - * @param preserveInitState - */ - public static final void moveGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final GLEventListener listener, final boolean preserveInitState) { - final boolean initialized = src.getGLEventListenerInitState(listener); - src.removeGLEventListener(listener); - dest.addGLEventListener(listener); - if(preserveInitState && initialized) { - dest.setGLEventListenerInitState(listener, true); - dest.invoke(false, new GLEventListenerState.ReshapeGLEventListener(listener)); - } // else .. !init state is default - } - - /** - * Moves all {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>. - * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved - * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call. - * <p> - * Note that it is only legal to pass <code>preserveInitState := true</code>, - * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>. - * </p> - * <p> - * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}. - * </p> - * @param src - * @param dest - * @param listener - * @param preserveInitState - */ - public static final void moveAllGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final boolean preserveInitState) { - for(int count = src.getGLEventListenerCount(); 0<count; count--) { - final GLEventListener listener = src.getGLEventListener(0); - moveGLEventListener(src, dest, listener, preserveInitState); + protected static final boolean DEBUG = Debug.debug("GLDrawable"); + + public static final boolean isAnimatorStartedOnOtherThread(final GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ; + } + + public static final boolean isAnimatorStarted(final GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isStarted() : false ; } - } - - /** - * Swaps the {@link GLContext} and all {@link GLEventListener} between {@link GLAutoDrawable} <code>a</code> and <code>b</code>, - * while preserving it's initialized state, resets the GL-Viewport and issuing {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)}. - * <p> - * The {@link GLAutoDrawable} to {@link GLAnimatorControl} association - * is also swapped. - * </p> - * <p> - * If an {@link GLAnimatorControl} is being attached to {@link GLAutoDrawable} <code>a</code> or <code>b</code> - * and the current thread is different than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. - * </p> - * @param a - * @param b - * @throws GLException if the {@link AbstractGraphicsDevice} are incompatible w/ each other. - */ - public static final void swapGLContextAndAllGLEventListener(final GLAutoDrawable a, final GLAutoDrawable b) { - final GLEventListenerState gllsA = GLEventListenerState.moveFrom(a); - final GLEventListenerState gllsB = GLEventListenerState.moveFrom(b); - - gllsA.moveTo(b); - gllsB.moveTo(a); - } - - /** - * Swaps the {@link GLContext} of given {@link GLAutoDrawable} - * and {@link GLAutoDrawable#disposeGLEventListener(GLEventListener, boolean) disposes} - * each {@link GLEventListener} w/o removing it. - * <p> - * The GL-Viewport is reset and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued implicit. - * </p> - * <p> - * If an {@link GLAnimatorControl} is being attached to GLAutoDrawable src or dest and the current thread is different - * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. - * </p> - * @param src - * @param dest - */ - public static final void swapGLContext(final GLAutoDrawable src, final GLAutoDrawable dest) { - final GLAnimatorControl aAnim = src.getAnimator(); - final GLAnimatorControl bAnim = dest.getAnimator(); - final boolean aIsPaused = isAnimatorAnimatingOnOtherThread(aAnim) && aAnim.pause(); - final boolean bIsPaused = isAnimatorAnimatingOnOtherThread(bAnim) && bAnim.pause(); - - for(int i = src.getGLEventListenerCount() - 1; 0 <= i; i--) { - src.disposeGLEventListener(src.getGLEventListener(i), false); + + public static final boolean isAnimatorAnimatingOnOtherThread(final GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ; + } + + public static final boolean isAnimatorAnimating(final GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() : false ; } - for(int i = dest.getGLEventListenerCount() - 1; 0 <= i; i--) { - dest.disposeGLEventListener(dest.getGLEventListener(i), false); + + /** + * {@link GLRunnable} to issue {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int)}, + * returning <code>true</code> on {@link GLRunnable#run(GLAutoDrawable)}. + */ + public static class ReshapeGLEventListener implements GLRunnable { + private final GLEventListener listener; + private final boolean displayAfterReshape; + /** + * + * @param listener + * @param displayAfterReshape <code>true</code> to issue {@link GLEventListener#display(GLAutoDrawable)} + * after {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int)}, + * otherwise false. + */ + public ReshapeGLEventListener(final GLEventListener listener, final boolean displayAfterReshape) { + this.listener = listener; + this.displayAfterReshape = displayAfterReshape; + } + @Override + public boolean run(final GLAutoDrawable drawable) { + listener.reshape(drawable, 0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + if( displayAfterReshape ) { + listener.display(drawable); + } + return true; + } + } + + /** + * Moves the designated {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>. + * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved + * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call. + * <p> + * Note that it is only legal to pass <code>preserveInitState := true</code>, + * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>. + * </p> + * <p> + * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}. + * </p> + * @param src + * @param dest + * @param listener + * @param preserveInitState + */ + public static final void moveGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final GLEventListener listener, final boolean preserveInitState) { + final boolean initialized = src.getGLEventListenerInitState(listener); + if( preserveInitState ) { + src.removeGLEventListener(listener); + dest.addGLEventListener(listener); + if( initialized ) { + dest.setGLEventListenerInitState(listener, true); + dest.invoke(false, new ReshapeGLEventListener(listener, true)); + } + } else { + src.disposeGLEventListener(listener, true); + dest.addGLEventListener(listener); + } + } + + /** + * Moves all {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>. + * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved + * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call. + * <p> + * Note that it is only legal to pass <code>preserveInitState := true</code>, + * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>. + * </p> + * <p> + * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}. + * </p> + * @param src + * @param dest + * @param listener + * @param preserveInitState + */ + public static final void moveAllGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final boolean preserveInitState) { + for(int count = src.getGLEventListenerCount(); 0<count; count--) { + final GLEventListener listener = src.getGLEventListener(0); + moveGLEventListener(src, dest, listener, preserveInitState); + } + } + + /** + * Return a heuristic value whether switching the {@link GLContext} is safe between {@lin GLAutoDrawable}s, + * i.e. via {@link #swapGLContext(GLAutoDrawable, GLAutoDrawable)} or {@link #swapGLContextAndAllGLEventListener(GLAutoDrawable, GLAutoDrawable)}. + * <p> + * Method currently returns <code>false</code> if: + * <ul> + * <li>Switching between on- and offscreen and one of the following is <code>true</code>: + * <ul> + * <li>{@link GLCapabilitiesImmutable#getSampleBuffers() MSAA is <i>used</i>} [1] in <code>chosenCapsA</code> or <code>chosenCapsB</code></li> + * <li>{@link GLCapabilitiesImmutable#getStereo() Stereo is <i>used</i>} in <code>chosenCapsA</code> or <code>chosenCapsB</code></li> + * <li>{@link GLCapabilitiesImmutable#getAccumAlphaBits() Accumulator Buffer is <i>requested</i>} [2] in <code>requestedCaps</code></li> + * </ul></li> + * </ul> + * Otherwise method returns <code>true</code> + * </p> + * <pre> + * [1] See Bug 830: swapGLContextAndAllGLEventListener and onscreen MSAA w/ NV/GLX + * On NVidia GPUs w/ it's proprietary driver context swapping does not work if MSAA is involved + * and when swapping on- to offscreen. + * </pre> + * <pre> + * [2] On AMD GPUs w/ it's proprietary driver, requesting an accumulator buffer leads to receive an accumulator buffer configuration, + * for which context swapping does not work when swapping on- to offscreen and vice-versa, i.e. cannot make context current. + * With AMD and Mesa drivers we only receive an accumulator buffer if requested, + * where on NVidia drivers all configurations contain the accumulator buffer. + * On both drivers, NVidia and Mesa, context swapping with accumulator buffer works. + * </pre> + * @param requestedCaps requested {@link GLCapabilitiesImmutable} which are intended for usage by both {@link GLAutoDrawable}s A and B + * @param chosenCapsA chosen {@link GLCapabilitiesImmutable} of {@link GLAutoDrawable} A, which {@link GLContext} is intended to be swapped + * @param chosenCapsB chosen {@link GLCapabilitiesImmutable} of {@link GLAutoDrawable} B, which {@link GLContext} is intended to be swapped + * @see #swapGLContext(GLAutoDrawable, GLAutoDrawable) + * @see #swapGLContextAndAllGLEventListener(GLAutoDrawable, GLAutoDrawable) + */ + public static boolean isSwapGLContextSafe(final GLCapabilitiesImmutable requestedCaps, final GLCapabilitiesImmutable chosenCapsA, final GLCapabilitiesImmutable chosenCapsB) { + final boolean usingAccumulatorBuffer = requestedCaps.getAccumAlphaBits() > 0 || + requestedCaps.getAccumRedBits() > 0 || + requestedCaps.getAccumGreenBits() > 0 || + requestedCaps.getAccumBlueBits() > 0; + if( ( chosenCapsA.isOnscreen() && !chosenCapsB.isOnscreen() || !chosenCapsA.isOnscreen() && chosenCapsB.isOnscreen() ) && // switching between on- and offscreen + ( + ( chosenCapsA.getSampleBuffers() || chosenCapsB.getSampleBuffers() ) || // MSAA involved + ( chosenCapsA.getStereo() || chosenCapsB.getStereo() ) || // Stereo involved + usingAccumulatorBuffer // Using accumulator buffer + ) + ) + { + return false; + } else { + return true; + } + } + /** + * Swaps the {@link GLContext} and all {@link GLEventListener} between {@link GLAutoDrawable} <code>a</code> and <code>b</code>, + * while preserving it's initialized state, resets the GL-Viewport and issuing {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)}. + * <p> + * The {@link GLAutoDrawable} to {@link GLAnimatorControl} association + * is also swapped. + * </p> + * <p> + * If an {@link GLAnimatorControl} is being attached to {@link GLAutoDrawable} <code>a</code> or <code>b</code> + * and the current thread is different than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. + * </p> + * <p> + * During operation, both {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-locks} and {@link GLAutoDrawable#getNativeSurface() surfaces} are locked, + * hence atomicity of operation is guaranteed, + * see <a href="../../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>. + * </p> + * <p> + * Because of above mentioned locking, if this method is not performed + * on {@link GLAutoDrawable#isThreadGLCapable() a OpenGL capable thread} of <i>both</i> + * {@link GLAutoDrawable}s, it must be invoked on such an OpenGL capable thread, + * e.g. via {@link Threading#invokeOnOpenGLThread(boolean, Runnable)}. + * </p> + * @throws GLException if the {@link AbstractGraphicsDevice} are incompatible w/ each other. + * @see #isSwapGLContextSafe(GLCapabilitiesImmutable, GLCapabilitiesImmutable, GLCapabilitiesImmutable) + */ + public static final void swapGLContextAndAllGLEventListener(final GLAutoDrawable a, final GLAutoDrawable b) { + final GLEventListenerState gllsA = GLEventListenerState.moveFrom(a, true); + final GLEventListenerState gllsB = GLEventListenerState.moveFrom(b, true); + final Runnable gllsAUnlockOp = gllsA.getUnlockSurfaceOp(); + final Runnable gllsBUnlockOp = gllsB.getUnlockSurfaceOp(); + try { + gllsA.moveTo(b, gllsBUnlockOp); + gllsB.moveTo(a, gllsAUnlockOp); + } finally { + // guarantee unlock in case of an exception + gllsBUnlockOp.run(); + gllsAUnlockOp.run(); + } + } + + /** + * Swaps the {@link GLContext} of given {@link GLAutoDrawable} + * and {@link GLAutoDrawable#disposeGLEventListener(GLEventListener, boolean) disposes} + * each {@link GLEventListener} w/o removing it. + * <p> + * The GL-Viewport is reset and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued implicit. + * </p> + * <p> + * If an {@link GLAnimatorControl} is being attached to GLAutoDrawable src or dest and the current thread is different + * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. + * </p> + * <p> + * During operation, both {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-locks} and {@link GLAutoDrawable#getNativeSurface() surfaces} are locked, + * hence atomicity of operation is guaranteed, + * see <a href="../../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>. + * </p> + * <p> + * Because of above mentioned locking, if this method is not performed + * on {@link GLAutoDrawable#isThreadGLCapable() a OpenGL capable thread} of <i>both</i> + * {@link GLAutoDrawable}s, it must be invoked on such an OpenGL capable thread, + * e.g. via {@link Threading#invokeOnOpenGLThread(boolean, Runnable)}. + * </p> + * @param a + * @param b + * @see #isSwapGLContextSafe(GLCapabilitiesImmutable, GLCapabilitiesImmutable, GLCapabilitiesImmutable) + */ + public static final void swapGLContext(final GLAutoDrawable a, final GLAutoDrawable b) { + final GLAnimatorControl aAnim = a.getAnimator(); + final GLAnimatorControl bAnim = b.getAnimator(); + final boolean aIsPaused = isAnimatorAnimatingOnOtherThread(aAnim) && aAnim.pause(); + final boolean bIsPaused = isAnimatorAnimatingOnOtherThread(bAnim) && bAnim.pause(); + + final RecursiveLock aUpstreamLock = a.getUpstreamLock(); + final RecursiveLock bUpstreamLock = b.getUpstreamLock(); + aUpstreamLock.lock(); + bUpstreamLock.lock(); + try { + final NativeSurface aSurface = a.getNativeSurface(); + final boolean aSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < aSurface.lockSurface(); + if( a.isRealized() && !aSurfaceLocked ) { + throw new GLException("Could not lock realized a surface "+a); + } + final NativeSurface bSurface = b.getNativeSurface(); + final boolean bSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < bSurface.lockSurface(); + if( b.isRealized() && !bSurfaceLocked ) { + throw new GLException("Could not lock realized b surface "+b); + } + try { + for(int i = a.getGLEventListenerCount() - 1; 0 <= i; i--) { + a.disposeGLEventListener(a.getGLEventListener(i), false); + } + for(int i = b.getGLEventListenerCount() - 1; 0 <= i; i--) { + b.disposeGLEventListener(b.getGLEventListener(i), false); + } + b.setContext( a.setContext( b.getContext(), false ), false ); + + } finally { + if( bSurfaceLocked ) { + bSurface.unlockSurface(); + } + if( aSurfaceLocked ) { + aSurface.unlockSurface(); + } + } + } finally { + bUpstreamLock.unlock(); + aUpstreamLock.unlock(); + } + a.invoke(true, setViewport); + b.invoke(true, setViewport); + if(aIsPaused) { aAnim.resume(); } + if(bIsPaused) { bAnim.resume(); } + } + + private static final GLRunnable setViewport = new GLRunnable() { + @Override + public boolean run(final GLAutoDrawable drawable) { + drawable.getGL().glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + return false; // issue re-display w/ new viewport! + } + }; + + /** + * Determines whether the chosen {@link GLCapabilitiesImmutable} + * requires a {@link GLDrawable#swapBuffers() swap-buffers} + * before reading pixels. + * <p> + * Usually one uses the {@link GLBase#getDefaultReadBuffer() default-read-buffer} + * in which case {@link GLDrawable#swapBuffers() swap-buffers} shall happen <b>after</b> calling reading pixels, the default. + * </p> + * <p> + * However, <i>multisampling</i> offscreen {@link javax.media.opengl.GLFBODrawable}s + * utilize {@link GLDrawable#swapBuffers() swap-buffers} to <i>downsample</i> + * the multisamples into the readable sampling sink. + * In this case, we require {@link GLDrawable#swapBuffers() swap-buffers} <b>before</b> reading pixels. + * </p> + * @return chosenCaps.isFBO() && chosenCaps.getSampleBuffers() + */ + public static final boolean swapBuffersBeforeRead(final GLCapabilitiesImmutable chosenCaps) { + return chosenCaps.isFBO() && chosenCaps.getSampleBuffers(); } - dest.setContext( src.setContext( dest.getContext(), false ), false ); - - src.invoke(true, GLEventListenerState.setViewport); - dest.invoke(true, GLEventListenerState.setViewport); - - if(aIsPaused) { aAnim.resume(); } - if(bIsPaused) { bAnim.resume(); } - } - - /** - * Determines whether the chosen {@link GLCapabilitiesImmutable} - * requires a {@link GLDrawable#swapBuffers() swap-buffers} - * before reading pixels. - * <p> - * Usually one uses the {@link GLBase#getDefaultReadBuffer() default-read-buffer} - * in which case {@link GLDrawable#swapBuffers() swap-buffers} shall happen <b>after</b> calling reading pixels, the default. - * </p> - * <p> - * However, <i>multisampling</i> offscreen {@link javax.media.opengl.GLFBODrawable}s - * utilize {@link GLDrawable#swapBuffers() swap-buffers} to <i>downsample</i> - * the multisamples into the readable sampling sink. - * In this case, we require {@link GLDrawable#swapBuffers() swap-buffers} <b>before</b> reading pixels. - * </p> - * @return chosenCaps.isFBO() && chosenCaps.getSampleBuffers() - */ - public static final boolean swapBuffersBeforeRead(final GLCapabilitiesImmutable chosenCaps) { - return chosenCaps.isFBO() && chosenCaps.getSampleBuffers(); - } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java b/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java index 52612d224..290033e99 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java @@ -33,6 +33,7 @@ import javax.media.opengl.GL2; import javax.media.opengl.GL2ES2; import javax.media.opengl.GL2ES3; import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLContext; import javax.media.opengl.GLException; /** @@ -60,7 +61,7 @@ public class GLPixelStorageModes { /** * Sets the {@link GL#GL_PACK_ALIGNMENT}. * <p> - * Saves the PACK pixel storage modes if not saved yet, see {@link #savePack(GL)}. + * Saves the PACK pixel storage modes and {@link #resetPack(GL) resets} them if not saved yet, see {@link #savePack(GL)}. * </p> */ public final void setPackAlignment(final GL gl, final int packAlignment) { @@ -71,7 +72,7 @@ public class GLPixelStorageModes { /** * Sets the {@link GL#GL_UNPACK_ALIGNMENT}. * <p> - * Saves the UNPACK pixel storage modes if not saved yet, see {@link #saveUnpack(GL)}. + * Saves the UNPACK pixel storage modes and {@link #resetUnpack(GL) resets} them if not saved yet, see {@link #saveUnpack(GL)}. * </p> */ public final void setUnpackAlignment(final GL gl, final int unpackAlignment) { @@ -82,7 +83,7 @@ public class GLPixelStorageModes { /** * Sets the {@link GL#GL_PACK_ALIGNMENT} and {@link GL#GL_UNPACK_ALIGNMENT}. * <p> - * Saves the PACK and UNPACK pixel storage modes if not saved yet, see {@link #saveAll(GL)}. + * Saves the PACK and UNPACK pixel storage modes and resets them if not saved yet, see {@link #saveAll(GL)}. * </p> */ public final void setAlignment(final GL gl, final int packAlignment, final int unpackAlignment) { @@ -93,7 +94,7 @@ public class GLPixelStorageModes { /** * Sets the {@link GL2ES3#GL_PACK_ROW_LENGTH}. * <p> - * Saves the PACK pixel storage modes if not saved yet, see {@link #savePack(GL)}. + * Saves the PACK pixel storage modes and {@link #resetPack(GL) resets} them if not saved yet, see {@link #savePack(GL)}. * </p> */ public final void setPackRowLength(final GL2ES3 gl, final int packRowLength) { @@ -104,18 +105,18 @@ public class GLPixelStorageModes { /** * Sets the {@link GL2ES2#GL_UNPACK_ROW_LENGTH}. * <p> - * Saves the UNPACK pixel storage modes if not saved yet, see {@link #saveUnpack(GL)}. + * Saves the UNPACK pixel storage modes and {@link #resetUnpack(GL) resets} them if not saved yet, see {@link #saveUnpack(GL)}. * </p> */ - public final void setUnpackRowLength(final GL2ES2 gl, final int unpackRowLength) { + public final void setUnpackRowLength(final GL2ES3 gl, final int unpackRowLength) { saveUnpack(gl); gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, unpackRowLength); } /** - * Sets the {@link GL2ES3#GL_PACK_ROW_LENGTH} and {@link GL2ES2#GL_UNPACK_ROW_LENGTH}. + * Sets the {@link GL2ES3#GL_PACK_ROW_LENGTH} and {@link GL2ES2#GL_UNPACK_ROW_LENGTH} if {@link GL#isGL2ES3()}. * <p> - * Saves the PACK and UNPACK pixel storage modes if not saved yet, see {@link #saveAll(GL)}. + * Saves the PACK and UNPACK pixel storage modes and resets them if not saved yet, see {@link #saveAll(GL)}. * </p> */ public final void setRowLength(final GL2ES3 gl, final int packRowLength, final int unpackRowLength) { @@ -171,7 +172,7 @@ public class GLPixelStorageModes { */ public final void resetPack(final GL gl) { // Compared w/ ES2, ES3 and GL3-core spec - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 4); // es2, es3, gl3 + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 4); // es2, es3, gl3 if( gl.isGL2ES3() ) { gl.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, 0); // es3, gl3 gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_ROWS, 0); // es3, gl3 @@ -179,8 +180,10 @@ public class GLPixelStorageModes { if( gl.isGL2GL3() ) { gl.glPixelStorei(GL2GL3.GL_PACK_SWAP_BYTES, GL.GL_FALSE); // gl3 gl.glPixelStorei(GL2GL3.GL_PACK_LSB_FIRST, GL.GL_FALSE); // gl3 - gl.glPixelStorei(GL2GL3.GL_PACK_IMAGE_HEIGHT, 0); // gl3 - gl.glPixelStorei(GL2GL3.GL_PACK_SKIP_IMAGES, 0); // gl3 + if( gl.getContext().getGLVersionNumber().compareTo(GLContext.Version120) >= 0 ) { + gl.glPixelStorei(GL2GL3.GL_PACK_IMAGE_HEIGHT, 0); // gl3, GL_VERSION_1_2 + gl.glPixelStorei(GL2GL3.GL_PACK_SKIP_IMAGES, 0); // gl3, GL_VERSION_1_2 + } } } } @@ -242,16 +245,21 @@ public class GLPixelStorageModes { */ public final void resetUnpack(final GL gl) { // Compared w/ ES2, ES3 and GL3-core spec - gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 4); // es2, es3, gl3 + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 4); // es2, es3, gl3 if( gl.isGL2ES3() ) { gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, 0); // es3, gl3 gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_ROWS, 0); // es3, gl3 gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_PIXELS, 0); // es3, gl3 - gl.glPixelStorei(GL2ES3.GL_UNPACK_IMAGE_HEIGHT, 0); // es3, gl3 - gl.glPixelStorei(GL2ES3.GL_UNPACK_SKIP_IMAGES, 0); // es3, gl3 if( gl.isGL2GL3() ) { + if( gl.getContext().getGLVersionNumber().compareTo(GLContext.Version120) >= 0 ) { + gl.glPixelStorei(GL2ES3.GL_UNPACK_IMAGE_HEIGHT, 0); // es3, gl3, GL_VERSION_1_2 + gl.glPixelStorei(GL2ES3.GL_UNPACK_SKIP_IMAGES, 0); // es3, gl3, GL_VERSION_1_2 + } gl.glPixelStorei(GL2GL3.GL_UNPACK_SWAP_BYTES, GL.GL_FALSE); // gl3 gl.glPixelStorei(GL2GL3.GL_UNPACK_LSB_FIRST, GL.GL_FALSE); // gl3 + } else { + gl.glPixelStorei(GL2ES3.GL_UNPACK_IMAGE_HEIGHT, 0); // es3, gl3, GL_VERSION_1_2 + gl.glPixelStorei(GL2ES3.GL_UNPACK_SKIP_IMAGES, 0); // es3, gl3, GL_VERSION_1_2 } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java index abdb7c5f9..e84a1d874 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java @@ -223,7 +223,7 @@ public class GLReadBufferUtil { psm.setPackAlignment(gl, alignment); if(gl.isGL2ES3()) { final GL2ES3 gl2es3 = gl.getGL2ES3(); - gl2es3.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, width); + psm.setPackRowLength(gl2es3, width); gl2es3.glReadBuffer(gl2es3.getDefaultReadBuffer()); } readPixelBuffer.clear(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/Gamma.java b/src/jogl/classes/com/jogamp/opengl/util/Gamma.java index 4e44c997a..bab85e531 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/Gamma.java +++ b/src/jogl/classes/com/jogamp/opengl/util/Gamma.java @@ -39,69 +39,79 @@ package com.jogamp.opengl.util; -import javax.media.opengl.*; -import jogamp.opengl.*; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; -/** Provides control over the primary display's gamma, brightness and - contrast controls via the hardware gamma ramp tables. Not - supported on all platforms or graphics hardware. <P> - - Thanks to the LWJGL project for illustrating how to access gamma - control on the various platforms. -*/ +import com.jogamp.common.util.locks.RecursiveLock; +/** + * Provides convenient wrapper for {@link GLDrawableFactory} control over + * individual display's gamma, brightness and contrast values + * via the hardware gamma ramp tables. + * <p> + * Not supported on all platforms or graphics hardware. + * </p> + * <p> + * Thanks to the LWJGL project for illustrating how to access gamma + * control on the various platforms. + * </p> + */ public class Gamma { private Gamma() {} /** - * Sets the gamma, brightness, and contrast of the current main - * display. This functionality is not available on all platforms and - * graphics hardware. Returns true if the settings were successfully - * changed, false if not. This method may return false for some - * values of the incoming arguments even on hardware which does - * support the underlying functionality. <P> - * - * If this method returns true, the display settings will - * automatically be reset to their original values upon JVM exit - * (assuming the JVM does not crash); if the user wishes to change - * the display settings back to normal ahead of time, use {@link - * #resetDisplayGamma resetDisplayGamma}(). It is recommended to - * call {@link #resetDisplayGamma resetDisplayGamma} before calling - * e.g. <code>System.exit()</code> from the application rather than - * rely on the shutdown hook functionality due to inevitable race - * conditions and unspecified behavior during JVM teardown. <P> - * - * This method may be called multiple times during the application's - * execution, but calling {@link #resetDisplayGamma - * resetDisplayGamma} will only reset the settings to the values - * before the first call to this method. <P> - * - * @param gamma The gamma value, typically > 1.0 (default values - * vary, but typically roughly 1.0) - * @param brightness The brightness value between -1.0 and 1.0, - * inclusive (default values vary, but typically 0) - * @param contrast The contrast, greater than 0.0 (default values - * vary, but typically 1) - * @return true if gamma settings were successfully changed, false - * if not - * @throws IllegalArgumentException if any of the parameters were - * out-of-bounds + * Convenient wrapper for {@link GLDrawableFactory#setDisplayGamma(javax.media.nativewindow.NativeSurface, float, float, float)}. + * <p> + * Use {@link #setDisplayGamma(GLAutoDrawable, float, float, float)} in case of using an {#link GLAutoDrawable}. + * </p> + */ + public static boolean setDisplayGamma(final GLDrawable drawable, final float gamma, final float brightness, final float contrast) throws IllegalArgumentException { + return GLDrawableFactory.getFactory(drawable.getGLProfile()).setDisplayGamma(drawable.getNativeSurface(), gamma, brightness, contrast); + } + + /** + * Convenient wrapper for {@link GLDrawableFactory#setDisplayGamma(javax.media.nativewindow.NativeSurface, float, float, float)} + * locking {@link GLAutoDrawable#getUpstreamLock()} to ensure proper atomic operation. + */ + public static boolean setDisplayGamma(final GLAutoDrawable drawable, final float gamma, final float brightness, final float contrast) throws IllegalArgumentException { + final RecursiveLock lock = drawable.getUpstreamLock(); + lock.lock(); + try { + return GLDrawableFactory.getFactory(drawable.getGLProfile()).setDisplayGamma(drawable.getNativeSurface(), gamma, brightness, contrast); + } finally { + lock.unlock(); + } + } + + /** + * Convenient wrapper for {@link GLDrawableFactory#resetDisplayGamma(javax.media.nativewindow.NativeSurface)}. + * <p> + * Use {@link #resetDisplayGamma(GLAutoDrawable)} in case of using an {#link GLAutoDrawable}. + * </p> + */ + public static void resetDisplayGamma(final GLDrawable drawable) { + GLDrawableFactory.getFactory(drawable.getGLProfile()).resetDisplayGamma(drawable.getNativeSurface()); + } + + /** + * Convenient wrapper for {@link GLDrawableFactory#resetDisplayGamma(javax.media.nativewindow.NativeSurface)} + * locking {@link GLAutoDrawable#getUpstreamLock()} to ensure proper atomic operation. */ - public static boolean setDisplayGamma(final GL gl, final float gamma, final float brightness, final float contrast) throws IllegalArgumentException { - return GLDrawableFactoryImpl.getFactoryImpl(gl.getContext().getGLDrawable().getGLProfile()).setDisplayGamma(gamma, brightness, contrast); + public static void resetDisplayGamma(final GLAutoDrawable drawable) { + final RecursiveLock lock = drawable.getUpstreamLock(); + lock.lock(); + try { + GLDrawableFactory.getFactory(drawable.getGLProfile()).resetDisplayGamma(drawable.getNativeSurface()); + } finally { + lock.unlock(); + } } /** - * Resets the gamma, brightness and contrast values for the primary - * display to their original values before {@link #setDisplayGamma - * setDisplayGamma} was called the first time. {@link - * #setDisplayGamma setDisplayGamma} must be called before calling - * this method or an unspecified exception will be thrown. While it - * is not explicitly required that this method be called before - * exiting, calling it is recommended because of the inevitable - * unspecified behavior during JVM teardown. + * Convenient wrapper for {@link GLDrawableFactory#resetAllDisplayGamma()}. */ - public static void resetDisplayGamma(final GL gl) { - GLDrawableFactoryImpl.getFactoryImpl(gl.getContext().getGLDrawable().getGLProfile()).resetDisplayGamma(); + public static void resetAllDisplayGamma(final GLDrawable drawable) { + GLDrawableFactory.getFactory(drawable.getGLProfile()).resetAllDisplayGamma(); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java index 4fffd9fab..2ad102235 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java @@ -27,14 +27,13 @@ */ package com.jogamp.opengl.util.av; -import java.net.URI; - import javax.media.opengl.GL; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import jogamp.opengl.Debug; +import com.jogamp.common.net.Uri; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureSequence; import com.jogamp.opengl.util.TimeFrameI; @@ -46,18 +45,18 @@ import com.jogamp.opengl.util.TimeFrameI; * Audio maybe supported and played back internally or via an {@link AudioSink} implementation. * </p> * <p> - * Audio and video streams can be selected or muted via {@link #initStream(URI, int, int, int)} + * Audio and video streams can be selected or muted via {@link #initStream(Uri, int, int, int)} * using the appropriate <a href="#streamIDs">stream id</a>'s. * </p> * <p> - * Camera input can be selected using the {@link #CameraInputScheme} URI. + * Camera input can be selected using the {@link #CameraInputScheme} Uri. * </p> * * <a name="streamworker"><h5><i>StreamWorker</i> Decoding Thread</h5></a> * <p> * Most of the stream processing is performed on the decoding thread, a.k.a. <i>StreamWorker</i>: * <ul> - * <li>Stream initialization triggered by {@link #initStream(URI, int, int, int) initStream(..)} - User gets notified whether the stream has been initialized or not via {@link GLMediaEventListener#attributesChanged(GLMediaPlayer, int, long) attributesChanges(..)}.</li> + * <li>Stream initialization triggered by {@link #initStream(Uri, int, int, int) initStream(..)} - User gets notified whether the stream has been initialized or not via {@link GLMediaEventListener#attributesChanged(GLMediaPlayer, int, long) attributesChanges(..)}.</li> * <li>Stream decoding - User gets notified of a new frame via {@link GLMediaEventListener#newFrameAvailable(GLMediaPlayer, com.jogamp.opengl.util.texture.TextureSequence.TextureFrame, long) newFrameAvailable(...)}.</li> * <li>Caught <a href="#streamerror">exceptions on the decoding thread</a> are delivered as {@link StreamException}s.</li> * </ul> @@ -83,7 +82,7 @@ import com.jogamp.opengl.util.TimeFrameI; * <p> * <table border="1"> * <tr><th>Action</th> <th>{@link State} Before</th> <th>{@link State} After</th> <th>{@link GLMediaEventListener Event}</th></tr> - * <tr><td>{@link #initStream(URI, int, int, int)}</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link State#Initialized Initialized}<sup><a href="#streamworker">1</a></sup>, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_INIT EVENT_CHANGE_INIT} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr> + * <tr><td>{@link #initStream(Uri, int, int, int)}</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link State#Initialized Initialized}<sup><a href="#streamworker">1</a></sup>, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_INIT EVENT_CHANGE_INIT} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr> * <tr><td>{@link #initGL(GL)}</td> <td>{@link State#Initialized Initialized}</td> <td>{@link State#Paused Paused}, , {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr> * <tr><td>{@link #play()}</td> <td>{@link State#Paused Paused}</td> <td>{@link State#Playing Playing}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PLAY EVENT_CHANGE_PLAY}</td></tr> * <tr><td>{@link #pause(boolean)}</td> <td>{@link State#Playing Playing}</td> <td>{@link State#Paused Paused}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE}</td></tr> @@ -183,6 +182,9 @@ import com.jogamp.opengl.util.TimeFrameI; * <!-- <tr><td> title </td><td colspan=3> url1 </td><td> url2 </td></tr> * </table> * </p> + * <p> + * Since 2.3.0 this interface uses {@link Uri} instead of {@link java.net.URI}. + * </p> */ public interface GLMediaPlayer extends TextureSequence { public static final boolean DEBUG = Debug.debug("GLMediaPlayer"); @@ -200,10 +202,10 @@ public interface GLMediaPlayer extends TextureSequence { public static final int STREAM_ID_AUTO = -1; /** - * {@link URI#getScheme() URI scheme} name {@value} for camera input. E.g. <code>camera:/0</code> + * {@link Uri#scheme Uri scheme} name {@value} for camera input. E.g. <code>camera:/0</code> * for the 1st camera device. * <p> - * The {@link URI#getRawPath() URI path} is being used to identify the camera (<i>ID</i>), + * The {@link Uri#path Uri path} is being used to identify the camera (<i>ID</i>), * where the root fwd-slash is being cut-off. * </p> * <p> @@ -211,7 +213,7 @@ public interface GLMediaPlayer extends TextureSequence { * ranging from [0..<i>max-number</i>]. * </p> * <p> - * The {@link URI#getRawQuery() URI query} is used to pass options to the camera + * The {@link Uri#query Uri query} is used to pass options to the camera * using <i>;</i> as the separator. The latter avoids trouble w/ escaping. * </p> * <pre> @@ -221,13 +223,13 @@ public interface GLMediaPlayer extends TextureSequence { * camera://somewhere/<id>?size=640x480;rate=15 * </pre> * <pre> - * URI: [scheme:][//authority][path][?query][#fragment] + * Uri: [scheme:][//authority][path][?query][#fragment] * w/ authority: [user-info@]host[:port] * Note: 'path' starts w/ fwd slash * </pre> * </p> */ - public static final String CameraInputScheme = "camera"; + public static final Uri.Encoded CameraInputScheme = Uri.Encoded.cast("camera"); /** Camera property {@value}, size as string, e.g. <code>1280x720</code>, <code>hd720</code>. May not be supported on all platforms. See {@link #CameraInputScheme}. */ public static final String CameraPropSizeS = "size"; /** Camera property {@value}. See {@link #CameraInputScheme}. */ @@ -361,8 +363,9 @@ public interface GLMediaPlayer extends TextureSequence { * Ignored if video is muted. * @throws IllegalStateException if not invoked in {@link State#Uninitialized} * @throws IllegalArgumentException if arguments are invalid + * @since 2.3.0 */ - public void initStream(URI streamLoc, int vid, int aid, int textureCount) throws IllegalStateException, IllegalArgumentException; + public void initStream(Uri streamLoc, int vid, int aid, int textureCount) throws IllegalStateException, IllegalArgumentException; /** * Returns the {@link StreamException} caught in the decoder thread, or <code>null</code> if none occured. @@ -379,7 +382,7 @@ public interface GLMediaPlayer extends TextureSequence { * <p> * <a href="#lifecycle">Lifecycle</a>: {@link State#Initialized} -> {@link State#Paused} or {@link State#Initialized} * </p> - * Argument <code>gl</code> is ignored if video is muted, see {@link #initStream(URI, int, int, int)}. + * Argument <code>gl</code> is ignored if video is muted, see {@link #initStream(Uri, int, int, int)}. * * @param gl current GL object. Maybe <code>null</code>, for audio only. * @throws IllegalStateException if not invoked in {@link State#Initialized}. @@ -391,7 +394,7 @@ public interface GLMediaPlayer extends TextureSequence { /** * If implementation uses a {@link AudioSink}, it's instance will be returned. * <p> - * The {@link AudioSink} instance is available after {@link #initStream(URI, int, int, int)}, + * The {@link AudioSink} instance is available after {@link #initStream(Uri, int, int, int)}, * if used by implementation. * </p> */ @@ -536,8 +539,11 @@ public interface GLMediaPlayer extends TextureSequence { @Override public TextureSequence.TextureFrame getNextTexture(GL gl) throws IllegalStateException; - /** Return the stream location, as set by {@link #initStream(URI, int, int, int)}. */ - public URI getURI(); + /** + * Return the stream location, as set by {@link #initStream(Uri, int, int, int)}. + * @since 2.3.0 + */ + public Uri getUri(); /** * <i>Warning:</i> Optional information, may not be supported by implementation. diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java index 29dce40f5..8eed35ebb 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.StringReader; +import java.net.URISyntaxException; import java.net.URLConnection; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -46,12 +47,14 @@ import java.util.Set; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GL3; +import javax.media.opengl.GL4; import javax.media.opengl.GLES2; import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import jogamp.opengl.Debug; +import com.jogamp.common.net.Uri; import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.VersionNumber; @@ -63,33 +66,62 @@ import com.jogamp.common.util.VersionNumber; * {@link #create(GL2ES2, int, Class, String, String, String, boolean) here} and * {@link #create(GL2ES2, int, int, Class, String, String[], String, String) here}. * </p> + * <p> + * Support for {@link GL4#GL_TESS_CONTROL_SHADER} and {@link GL4#GL_TESS_EVALUATION_SHADER} + * was added since 2.2.1. + * </p> */ public class ShaderCode { public static final boolean DEBUG_CODE = Debug.isPropertyDefined("jogl.debug.GLSLCode", true); - /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in source code: <code>vp</code> */ + /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in source code: <code>{@value}</code> */ public static final String SUFFIX_VERTEX_SOURCE = "vp" ; - /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in binary: <code>bvp</code> */ + /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in binary: <code>{@value}</code> */ public static final String SUFFIX_VERTEX_BINARY = "bvp" ; - /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in source code: <code>gp</code> */ + /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in source code: <code>{@value}</code> */ public static final String SUFFIX_GEOMETRY_SOURCE = "gp" ; - /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in binary: <code>bgp</code> */ + /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in binary: <code>{@value}</code> */ public static final String SUFFIX_GEOMETRY_BINARY = "bgp" ; - /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in source code: <code>fp</code> */ + /** + * Unique resource suffix for {@link GL4#GL_TESS_CONTROL_SHADER} in source code: <code>{@value}</code> + * @since 2.2.1 + */ + public static final String SUFFIX_TESS_CONTROL_SOURCE = "tcp" ; + + /** + * Unique resource suffix for {@link GL4#GL_TESS_CONTROL_SHADER} in binary: <code>{@value}</code> + * @since 2.2.1 + */ + public static final String SUFFIX_TESS_CONTROL_BINARY = "btcp" ; + + /** + * Unique resource suffix for {@link GL4#GL_TESS_EVALUATION_SHADER} in source code: <code>{@value}</code> + * @since 2.2.1 + */ + public static final String SUFFIX_TESS_EVALUATION_SOURCE = "tep" ; + + /** + * Unique resource suffix for {@link GL4#GL_TESS_EVALUATION_SHADER} in binary: <code>{@value}</code> + * @since 2.2.1 + */ + public static final String SUFFIX_TESS_EVALUATION_BINARY = "btep" ; + + /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in source code: <code>{@value}</code> */ public static final String SUFFIX_FRAGMENT_SOURCE = "fp" ; - /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in binary: <code>bfp</code> */ + /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in binary: <code>{@value}</code> */ public static final String SUFFIX_FRAGMENT_BINARY = "bfp" ; - /** Unique relative path for binary shader resources for {@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: <code>nvidia</code> */ + /** Unique relative path for binary shader resources for {@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: <code>{@value}</code> */ public static final String SUB_PATH_NVIDIA = "nvidia" ; /** - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param source CharSequence array containing the shader sources, organized as <code>source[count][strings-per-shader]</code>. * May be either an immutable <code>String</code> - or mutable <code>StringBuilder</code> array. @@ -104,6 +136,8 @@ public class ShaderCode { case GL2ES2.GL_VERTEX_SHADER: case GL2ES2.GL_FRAGMENT_SHADER: case GL3.GL_GEOMETRY_SHADER: + case GL4.GL_TESS_CONTROL_SHADER: + case GL4.GL_TESS_EVALUATION_SHADER: break; default: throw new GLException("Unknown shader type: "+type); @@ -122,7 +156,8 @@ public class ShaderCode { } /** - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param binary binary buffer containing the shader binaries, */ @@ -131,6 +166,8 @@ public class ShaderCode { case GL2ES2.GL_VERTEX_SHADER: case GL2ES2.GL_FRAGMENT_SHADER: case GL3.GL_GEOMETRY_SHADER: + case GL4.GL_TESS_CONTROL_SHADER: + case GL4.GL_TESS_EVALUATION_SHADER: break; default: throw new GLException("Unknown shader type: "+type); @@ -148,7 +185,8 @@ public class ShaderCode { * which location is resolved using the <code>context</code> class, see {@link #readShaderSource(Class, String)}. * * @param gl current GL object to determine whether a shader compiler is available. If null, no validation is performed. - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param context class used to help resolving the source location * @param sourceFiles array of source locations, organized as <code>sourceFiles[count]</code> @@ -192,7 +230,8 @@ public class ShaderCode { * Creates a complete {@link ShaderCode} object while reading the shader binary of <code>binaryFile</code>, * which location is resolved using the <code>context</code> class, see {@link #readShaderBinary(Class, String)}. * - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param context class used to help resolving the source location * @param binFormat a valid native binary format as they can be queried by {@link ShaderUtil#getShaderBinaryFormats(GL)}. @@ -225,14 +264,21 @@ public class ShaderCode { * <li>Source<ul> * <li>{@link GL2ES2#GL_VERTEX_SHADER vertex}: {@link #SUFFIX_VERTEX_SOURCE}</li> * <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_SOURCE}</li> - * <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_SOURCE}</li></ul></li> + * <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_SOURCE}</li> + * <li>{@link GL4#GL_TESS_CONTROL_SHADER tess-ctrl}: {@link #SUFFIX_TESS_CONTROL_SOURCE}</li> + * <li>{@link GL4#GL_TESS_EVALUATION_SHADER tess-eval}: {@link #SUFFIX_TESS_EVALUATION_SOURCE}</li> + * </ul></li> * <li>Binary<ul> * <li>{@link GL2ES2#GL_VERTEX_SHADER vertex}: {@link #SUFFIX_VERTEX_BINARY}</li> * <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_BINARY}</li> - * <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_BINARY}</li></ul></li> + * <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_BINARY}</li> + * <li>{@link GL4#GL_TESS_CONTROL_SHADER tess-ctrl}: {@link #SUFFIX_TESS_CONTROL_BINARY}</li> + * <li>{@link GL4#GL_TESS_EVALUATION_SHADER tess-eval}: {@link #SUFFIX_TESS_EVALUATION_BINARY}</li> + * </ul></li> * </ul> * @param binary true for a binary resource, false for a source resource - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * * @throws GLException if <code>type</code> is not supported * @@ -246,6 +292,10 @@ public class ShaderCode { return binary?SUFFIX_FRAGMENT_BINARY:SUFFIX_FRAGMENT_SOURCE; case GL3.GL_GEOMETRY_SHADER: return binary?SUFFIX_GEOMETRY_BINARY:SUFFIX_GEOMETRY_SOURCE; + case GL4.GL_TESS_CONTROL_SHADER: + return binary?SUFFIX_TESS_CONTROL_BINARY:SUFFIX_TESS_CONTROL_SOURCE; + case GL4.GL_TESS_EVALUATION_SHADER: + return binary?SUFFIX_TESS_EVALUATION_BINARY:SUFFIX_TESS_EVALUATION_SOURCE; default: throw new GLException("illegal shader type: "+type); } @@ -324,7 +374,8 @@ public class ShaderCode { * * @param gl current GL object to determine whether a shader compiler is available (if <code>source</code> is used), * or to determine the shader binary format (if <code>binary</code> is used). - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param context class used to help resolving the source and binary location * @param srcRoot relative <i>root</i> path for <code>srcBasenames</code> optional @@ -430,7 +481,8 @@ public class ShaderCode { * * @param gl current GL object to determine whether a shader compiler is available (if <code>source</code> is used), * or to determine the shader binary format (if <code>binary</code> is used). - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param context class used to help resolving the source and binary location * @param srcRoot relative <i>root</i> path for <code>basename</code> optional * @param binRoot relative <i>root</i> path for <code>basename</code> @@ -468,6 +520,10 @@ public class ShaderCode { return "FRAGMENT_SHADER"; case GL3.GL_GEOMETRY_SHADER: return "GEOMETRY_SHADER"; + case GL4.GL_TESS_CONTROL_SHADER: + return "TESS_CONTROL_SHADER"; + case GL4.GL_TESS_EVALUATION_SHADER: + return "TESS_EVALUATION_SHADER"; } return "UNKNOWN_SHADER"; } @@ -778,7 +834,8 @@ public class ShaderCode { URLConnection nextConn = null; // Try relative of current shader location - nextConn = IOUtil.openURL(IOUtil.getRelativeOf(conn.getURL(), includeFile), "ShaderCode.relativeOf "); + final Uri relUri = Uri.valueOf( conn.getURL() ).getRelativeOf(new Uri.Encoded( includeFile, Uri.PATH_LEGAL )); + nextConn = IOUtil.openURL(relUri.toURL(), "ShaderCode.relativeOf "); if (nextConn == null) { // Try relative of class and absolute nextConn = IOUtil.getResource(context, includeFile); @@ -792,6 +849,8 @@ public class ShaderCode { result.append(line + "\n"); } } + } catch (final URISyntaxException e) { + throw new IOException(e); } finally { IOUtil.close(reader, false); } @@ -874,8 +933,12 @@ public class ShaderCode { /** Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}: {@value #es3_default_precision_vp} */ public static final String es3_default_precision_vp = es2_default_precision_vp; - /** Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #es3_default_precision_fp} */ - public static final String es3_default_precision_fp = es2_default_precision_fp; + /** + * Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #es3_default_precision_fp}, + * same as for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}, i.e {@link #es3_default_precision_vp}, + * due to ES 3.x requirements of using same precision for uniforms! + */ + public static final String es3_default_precision_fp = es3_default_precision_vp; /** Default precision of GLSL ≥ 1.30 as required until < 1.50 for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader} or {@link GL3#GL_GEOMETRY_SHADER geometry-shader}: {@value #gl3_default_precision_vp_gp}. See GLSL Spec 1.30-1.50 Section 4.5.3. */ public static final String gl3_default_precision_vp_gp = "\nprecision highp float;\nprecision highp int;\n"; @@ -956,6 +1019,8 @@ public class ShaderCode { switch ( shaderType ) { case GL2ES2.GL_VERTEX_SHADER: case GL3.GL_GEOMETRY_SHADER: + case GL4.GL_TESS_CONTROL_SHADER: + case GL4.GL_TESS_EVALUATION_SHADER: defaultPrecision = gl3_default_precision_vp_gp; break; case GL2ES2.GL_FRAGMENT_SHADER: defaultPrecision = gl3_default_precision_fp; break; diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java index f113be2b2..1d629131e 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java @@ -109,8 +109,7 @@ public abstract class CompileShader { tmpFile.getAbsolutePath(), outputFile.getAbsolutePath() }); // , null, processorDir); - new StreamMonitor(process.getInputStream()); - new StreamMonitor(process.getErrorStream()); + new IOUtil.StreamMonitor( new InputStream[] { process.getInputStream(), process.getErrorStream() }, System.out, null ); process.waitFor(); // Delete the temporary file // tmpFile.delete(); @@ -153,35 +152,4 @@ public abstract class CompileShader { e.printStackTrace(); } } - - private static class StreamMonitor implements Runnable { - private final InputStream istream; - public StreamMonitor(final InputStream stream) { - istream = stream; - new Thread(this, "Output Reader Thread").start(); - } - - @Override - public void run() - { - final byte[] buffer = new byte[4096]; - try { - int numRead = 0; - do { - numRead = istream.read(buffer); - if (numRead > 0) { - System.out.write(buffer, 0, numRead); - System.out.flush(); - } - } while (numRead >= 0); - } - catch (final IOException e) { - try { - istream.close(); - } catch (final IOException e2) { - } - // Should allow clean exit when process shuts down - } - } - } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java index 0801b65fa..f70ebf928 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java @@ -87,8 +87,7 @@ public class StereoClientRenderer implements GLEventListener { private void initFBOs(final GL gl, final DimensionImmutable size) { for(int i=0; i<fbos.length; i++) { - fbos[i].detachAllColorbuffer(gl); - fbos[i].reset(gl, size.getWidth(), size.getHeight(), numSamples, false); + fbos[i].init(gl, size.getWidth(), size.getHeight(), numSamples); if( i>0 && fbos[i-1].getNumSamples() != fbos[i].getNumSamples()) { throw new InternalError("sample size mismatch: \n\t0: "+fbos[i-1]+"\n\t1: "+fbos[i]); } @@ -96,19 +95,19 @@ public class StereoClientRenderer implements GLEventListener { if(numSamples>0) { fbos[i].attachColorbuffer(gl, 0, true); // MSAA requires alpha - fbos[i].attachRenderbuffer(gl, Type.DEPTH, 24); + fbos[i].attachRenderbuffer(gl, Type.DEPTH, FBObject.DEFAULT_BITS); final FBObject ssink = new FBObject(); { - ssink.reset(gl, size.getWidth(), size.getHeight()); + ssink.init(gl, size.getWidth(), size.getHeight(), 0); ssink.attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); - ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, FBObject.DEFAULT_BITS); } fbos[i].setSamplingSink(ssink); fbos[i].resetSamplingSink(gl); // validate - fboTexs[i] = fbos[i].getSamplingSink(); + fboTexs[i] = fbos[i].getSamplingSink().getTextureAttachment(); } else { fboTexs[i] = fbos[i].attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); - fbos[i].attachRenderbuffer(gl, Type.DEPTH, 24); + fbos[i].attachRenderbuffer(gl, Type.DEPTH, FBObject.DEFAULT_BITS); } fbos[i].unbind(gl); System.err.println("FBO["+i+"]: "+fbos[i]); @@ -119,15 +118,15 @@ public class StereoClientRenderer implements GLEventListener { @SuppressWarnings("unused") private void resetFBOs(final GL gl, final DimensionImmutable size) { for(int i=0; i<fbos.length; i++) { - fbos[i].reset(gl, size.getWidth(), size.getHeight(), numSamples, true); + fbos[i].reset(gl, size.getWidth(), size.getHeight(), numSamples); if( i>0 && fbos[i-1].getNumSamples() != fbos[i].getNumSamples()) { throw new InternalError("sample size mismatch: \n\t0: "+fbos[i-1]+"\n\t1: "+fbos[i]); } numSamples = fbos[i].getNumSamples(); if(numSamples>0) { - fboTexs[i] = fbos[i].getSamplingSink(); + fboTexs[i] = fbos[i].getSamplingSink().getTextureAttachment(); } else { - fboTexs[i] = (TextureAttachment) fbos[i].getColorbuffer(0); + fboTexs[i] = fbos[i].getColorbuffer(0).getTextureAttachment(); } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java index 2091d0843..d32c981a3 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java @@ -46,6 +46,9 @@ public interface StereoDevice { // NOP } + /** Return the factory used to create this device. */ + public StereoDeviceFactory getFactory(); + /** Disposes this {@link StereoDevice}. */ public void dispose(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java index 46ce82f03..c4180585c 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java @@ -45,7 +45,25 @@ public abstract class StereoDeviceFactory { private static final String GenericStereoDeviceClazzName = "jogamp.opengl.util.stereo.GenericStereoDeviceFactory"; private static final String isAvailableMethodName = "isAvailable"; - public static enum DeviceType { Default, Generic, OculusVR }; + /** {@link StereoDevice} type used for {@link StereoDeviceFactory#createFactory(DeviceType) createFactory(type)}. */ + public static enum DeviceType { + /** + * Auto selection of device in the following order: + * <ol> + * <li>{@link DeviceType#OculusVR}</li> + * <li>{@link DeviceType#Generic}</li> + * </ol> + */ + Default, + /** + * Generic software implementation. + */ + Generic, + /** + * OculusVR implementation. + */ + OculusVR + }; public static StereoDeviceFactory createDefaultFactory() { final ClassLoader cl = StereoDeviceFactory.class.getClassLoader(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java index 19f2fc05b..6011afe7b 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java @@ -60,7 +60,6 @@ import javax.media.nativewindow.util.DimensionImmutable; import javax.media.nativewindow.util.PixelFormat; import javax.media.opengl.GL; import javax.media.opengl.GL2; -import javax.media.opengl.GL2ES3; import javax.media.opengl.GL2GL3; import javax.media.opengl.GLContext; import javax.media.opengl.GLException; @@ -69,6 +68,7 @@ import javax.media.opengl.GLProfile; import jogamp.opengl.Debug; import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.util.GLPixelStorageModes; import com.jogamp.opengl.util.PNGPixelRect; import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; import com.jogamp.opengl.util.texture.spi.DDSImage; @@ -575,6 +575,10 @@ public class TextureIO { * when it is written to disk, regardless of whether the underlying * file format supports multiple mipmaps in a given file. * + * <p> + * Method required a {@link GL2GL3} {@link GLProfile#GL2GL3 profile}. + * </p> + * * @throws IOException if an error occurred during writing or no * suitable writer was found * @throws GLException if no OpenGL context was current or an @@ -590,7 +594,7 @@ public class TextureIO { if (!_gl.isGL2GL3()) { throw new GLException("Implementation only supports GL2GL3 (Use GLReadBufferUtil and the TextureData variant), have: " + _gl); } - final GL2GL3 gl = _gl.getGL2(); + final GL2GL3 gl = _gl.getGL2GL3(); texture.bind(gl); final int internalFormat = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2GL3.GL_TEXTURE_INTERNAL_FORMAT); @@ -630,17 +634,8 @@ public class TextureIO { } // Fetch using glGetTexImage - final int packAlignment = glGetInteger(gl, GL.GL_PACK_ALIGNMENT); - final int packRowLength = glGetInteger(gl, GL2ES3.GL_PACK_ROW_LENGTH); - final int packSkipRows = glGetInteger(gl, GL2ES3.GL_PACK_SKIP_ROWS); - final int packSkipPixels = glGetInteger(gl, GL2ES3.GL_PACK_SKIP_PIXELS); - final int packSwapBytes = glGetInteger(gl, GL2GL3.GL_PACK_SWAP_BYTES); - - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1); - gl.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, 0); - gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_ROWS, 0); - gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_PIXELS, 0); - gl.glPixelStorei(GL2GL3.GL_PACK_SWAP_BYTES, 0); + final GLPixelStorageModes psm = new GLPixelStorageModes(); + psm.setPackAlignment(gl, 1); final ByteBuffer res = ByteBuffer.allocate((width + (2 * border)) * (height + (2 * border)) * @@ -651,11 +646,7 @@ public class TextureIO { } gl.glGetTexImage(GL.GL_TEXTURE_2D, 0, fetchedFormat, GL.GL_UNSIGNED_BYTE, res); - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, packAlignment); - gl.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, packRowLength); - gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_ROWS, packSkipRows); - gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_PIXELS, packSkipPixels); - gl.glPixelStorei(GL2GL3.GL_PACK_SWAP_BYTES, packSwapBytes); + psm.restore(gl); data = new TextureData(gl.getGLProfile(), internalFormat, width, height, border, fetchedFormat, GL.GL_UNSIGNED_BYTE, false, false, false, res, null); @@ -1404,12 +1395,6 @@ public class TextureIO { // Helper routines // - private static int glGetInteger(final GL gl, final int pname) { - final int[] tmp = new int[1]; - gl.glGetIntegerv(pname, tmp, 0); - return tmp[0]; - } - private static int glGetTexLevelParameteri(final GL2GL3 gl, final int target, final int level, final int pname) { final int[] tmp = new int[1]; gl.glGetTexLevelParameteriv(target, 0, pname, tmp, 0); diff --git a/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java b/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java index 827145654..d14ada48b 100644 --- a/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java +++ b/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java @@ -33,6 +33,38 @@ package javax.media.opengl; * which implementation may drive a {@link javax.media.opengl.GLAutoDrawable} animation. */ public interface GLAnimatorControl extends FPSCounter { + /** + * A {@link GLAnimatorControl#setUncaughtExceptionHandler(UncaughtExceptionHandler) registered} + * {@link UncaughtExceptionHandler} instance is invoked when an {@link GLAnimatorControl animator} abruptly {@link #stop() stops} + * due to an uncaught exception from one of its {@link GLAutoDrawable}s. + * @see #uncaughtException(GLAnimatorControl, GLAutoDrawable, Throwable) + * @see GLAnimatorControl#setUncaughtExceptionHandler(UncaughtExceptionHandler) + * @since 2.2 + */ + public static interface UncaughtExceptionHandler { + /** + * Method invoked when the given {@link GLAnimatorControl} is {@link GLAnimatorControl#stop() stopped} due to the + * given uncaught exception happened on the given {@link GLAutoDrawable}. + * <p> + * The animator thread can still be retrieved via {@link GLAnimatorControl#getThread()}. + * </p> + * <p> + * All {@link GLAnimatorControl} states already reflect its stopped state. + * </p> + * <p> + * After this handler method is called, the {@link GLAnimatorControl} is stopped. + * </p> + * <p> + * Any exception thrown by this method will be ignored. + * </p> + * @param animator the {@link GLAnimatorControl} + * @param drawable the causing {@link GLAutoDrawable} + * @param cause the uncaught exception + * @see GLAnimatorControl#setUncaughtExceptionHandler(UncaughtExceptionHandler) + * @since 2.2 + */ + void uncaughtException(final GLAnimatorControl animator, final GLAutoDrawable drawable, final Throwable cause); + } /** * Indicates whether this animator has been {@link #start() started}. @@ -181,4 +213,24 @@ public interface GLAnimatorControl extends FPSCounter { * @throws IllegalArgumentException if drawable was not added to this animator */ void remove(GLAutoDrawable drawable); + + /** + * Returns the {@link UncaughtExceptionHandler} invoked when this {@link GLAnimatorControl animator} abruptly {@link #stop() stops} + * due to an uncaught exception from one of its {@link GLAutoDrawable}s. + * <p> + * Default is <code>null</code>. + * </p> + * @since 2.2 + */ + UncaughtExceptionHandler getUncaughtExceptionHandler(); + + /** + * Set the handler invoked when this {@link GLAnimatorControl animator} abruptly {@link #stop() stops} + * due to an uncaught exception from one of its {@link GLAutoDrawable}s. + * @param handler the {@link UncaughtExceptionHandler} to use as this {@link GLAnimatorControl animator}'s uncaught exception + * handler. Pass <code>null</code> to unset the handler. + * @see UncaughtExceptionHandler#uncaughtException(GLAnimatorControl, GLAutoDrawable, Throwable) + * @since 2.2 + */ + void setUncaughtExceptionHandler(final UncaughtExceptionHandler handler); } diff --git a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java index 377dce190..bded88d20 100644 --- a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java +++ b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java @@ -42,6 +42,10 @@ package javax.media.opengl; import java.util.List; +import javax.media.nativewindow.NativeSurface; + +import com.jogamp.common.util.locks.RecursiveLock; + import jogamp.opengl.Debug; /** A higher-level abstraction than {@link GLDrawable} which supplies @@ -116,6 +120,28 @@ import jogamp.opengl.Debug; -Djogl.screenchange.action=true Enable the {@link GLDrawable} reconfiguration </PRE> </p> + <h5><a name="locking">GLAutoDrawable Locking</a></h5> + GLAutoDrawable implementations perform locking in the following order: + <ol> + <li> {@link #getUpstreamLock()}.{@link RecursiveLock#lock() lock()}</li> + <li> {@link #getNativeSurface()}.{@link NativeSurface#lockSurface() lockSurface()} </li> + </ol> + and releases the locks accordingly: + <ol> + <li> {@link #getNativeSurface()}.{@link NativeSurface#unlockSurface() unlockSurface()} </li> + <li> {@link #getUpstreamLock()}.{@link RecursiveLock#unlock() unlock()}</li> + </ol> + Above <i>locking order</i> is mandatory to guarantee + atomicity of operation and to avoid race-conditions. + A custom implementation or user applications requiring exclusive access + shall follow the <i>locking order</i>. + See: + <ul> + <li>{@link #getUpstreamLock()}</li> + <li>{@link #invoke(boolean, GLRunnable)}</li> + <li>{@link #invoke(boolean, List)}</li> + </ul> + </p> */ public interface GLAutoDrawable extends GLDrawable { /** Flag reflecting whether the {@link GLDrawable} reconfiguration will be issued in @@ -139,19 +165,22 @@ public interface GLAutoDrawable extends GLDrawable { /** * Associate the new context, <code>newtCtx</code>, to this auto-drawable. * <p> - * The current context will be destroyed if <code>destroyPrevCtx</code> is <code>true</code>, - * otherwise it will be dis-associated from this auto-drawable - * via {@link GLContext#setGLDrawable(GLDrawable, boolean) setGLDrawable(null, true);} first. - * </p> - * <p> - * The new context will be associated with this auto-drawable - * via {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}. - * </p> - * <p> - * If the old or new context was current on this thread, it is being released before switching the association. - * The new context will be made current afterwards, if it was current before. - * However the user shall take extra care that no other thread - * attempts to make this context current. + * Remarks: + * <ul> + * <li>The currently associated context will be destroyed if <code>destroyPrevCtx</code> is <code>true</code>, + * otherwise it will be disassociated from this auto-drawable + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) setGLDrawable(null, true);} including {@link GL#glFinish() glFinish()}.</li> + * <li>The new context will be associated with this auto-drawable + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}.</li> + * <li>If the old context was current on this thread, it is being released after disassociating this auto-drawable.</li> + * <li>If the new context was current on this thread, it is being released before associating this auto-drawable + * and made current afterwards.</li> + * <li>Implementation may issue {@link #makeCurrent()} and {@link #release()} while drawable reassociation.</li> + * <li>The user shall take extra care of thread synchronization, + * i.e. lock the involved {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-locks} and {@link GLAutoDrawable#getNativeSurface() surfaces} + * to avoid a race condition. See <a href="#locking">GLAutoDrawable Locking</a>.</li> + * </ul> * </p> * * @param newCtx the new context, maybe <code>null</code> for dis-association. @@ -200,6 +229,7 @@ public interface GLAutoDrawable extends GLDrawable { /** * Returns true if all added {@link GLEventListener} are initialized, otherwise false. + * @since 2.2 */ boolean areAllGLEventListenerInitialized(); @@ -410,17 +440,28 @@ public interface GLAutoDrawable extends GLDrawable { * The internal queue of {@link GLRunnable}'s is being flushed with {@link #destroy()} * where all blocked callers are being notified. * </p> + * <p> + * To avoid a deadlock situation which causes an {@link IllegalStateException} one should + * avoid issuing {@link #invoke(boolean, GLRunnable) invoke} while this <a href="#locking">GLAutoDrawable is being locked</a>.<br> + * Detected deadlock situations throwing an {@link IllegalStateException} are: + * <ul> + * <li>{@link #getAnimator() Animator} is running on another thread and waiting and is locked on current thread, but is not {@link #isThreadGLCapable() GL-Thread}</li> + * <li>No {@link #getAnimator() Animator} is running on another thread and is locked on current thread, but is not {@link #isThreadGLCapable() GL-Thread}</li> + * </ul> + * </p> * * @param wait if <code>true</code> block until execution of <code>glRunnable</code> is finished, otherwise return immediately w/o waiting * @param glRunnable the {@link GLRunnable} to execute within {@link #display()} * @return <code>true</code> if the {@link GLRunnable} has been processed or queued, otherwise <code>false</code>. + * @throws IllegalStateException in case of a detected deadlock situation ahead, see above. * * @see #setAnimator(GLAnimatorControl) * @see #display() * @see GLRunnable * @see #invoke(boolean, List) + * @see #flushGLRunnables() */ - public boolean invoke(boolean wait, GLRunnable glRunnable); + public boolean invoke(boolean wait, GLRunnable glRunnable) throws IllegalStateException ; /** * Extends {@link #invoke(boolean, GLRunnable)} functionality @@ -428,9 +469,23 @@ public interface GLAutoDrawable extends GLDrawable { * @param wait if <code>true</code> block until execution of the last <code>glRunnable</code> is finished, otherwise return immediately w/o waiting * @param glRunnables the {@link GLRunnable}s to execute within {@link #display()} * @return <code>true</code> if the {@link GLRunnable}s has been processed or queued, otherwise <code>false</code>. + * @throws IllegalStateException in case of a detected deadlock situation ahead, see {@link #invoke(boolean, GLRunnable)}. * @see #invoke(boolean, GLRunnable) + * @see #flushGLRunnables() */ - public boolean invoke(boolean wait, List<GLRunnable> glRunnables); + public boolean invoke(boolean wait, List<GLRunnable> glRunnables) throws IllegalStateException; + + /** + * Flushes all {@link #invoke(boolean, GLRunnable) enqueued} {@link GLRunnable} of this {@link GLAutoDrawable} + * including notifying waiting executor. + * <p> + * The executor which might have been blocked until notified + * will be unblocked and all tasks removed from the queue. + * </p> + * @see #invoke(boolean, GLRunnable) + * @since 2.2 + */ + public void flushGLRunnables(); /** Destroys all resources associated with this GLAutoDrawable, inclusive the GLContext. @@ -556,4 +611,26 @@ public interface GLAutoDrawable extends GLDrawable { */ public Object getUpstreamWidget(); + /** + * Returns the recursive lock object of the {@link #getUpstreamWidget() upstream widget} + * to synchronize multithreaded access on top of {@link NativeSurface#lockSurface()}. + * <p> + * See <a href="#locking">GLAutoDrawable Locking</a>. + * </p> + * @since 2.2 + */ + public RecursiveLock getUpstreamLock(); + + /** + * Indicates whether the current thread is capable of + * performing OpenGL-related work. + * <p> + * Implementation utilizes this knowledge to determine + * whether {@link #display()} performs the OpenGL commands on the current thread directly + * or spawns them on the dedicated OpenGL thread. + * </p> + * @since 2.2 + */ + public boolean isThreadGLCapable(); + } diff --git a/src/jogl/classes/javax/media/opengl/GLContext.java b/src/jogl/classes/javax/media/opengl/GLContext.java index d5d8792d8..e2498e6f1 100644 --- a/src/jogl/classes/javax/media/opengl/GLContext.java +++ b/src/jogl/classes/javax/media/opengl/GLContext.java @@ -48,6 +48,7 @@ import java.util.List; import java.util.Set; import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeSurface; import jogamp.opengl.Debug; import jogamp.opengl.GLContextImpl; @@ -107,6 +108,7 @@ public abstract class GLContext { protected static final boolean FORCE_NO_FBO_SUPPORT = Debug.isPropertyDefined("jogl.fbo.force.none", true); protected static final boolean FORCE_MIN_FBO_SUPPORT = Debug.isPropertyDefined("jogl.fbo.force.min", true); + protected static final boolean FORCE_NO_COLOR_RENDERBUFFER = Debug.isPropertyDefined("jogl.fbo.force.nocolorrenderbuffer", true); /** Reflects property jogl.debug.DebugGL. If true, the debug pipeline is enabled at context creation. */ public static final boolean DEBUG_GL = Debug.isPropertyDefined("jogl.debug.DebugGL", true); @@ -147,6 +149,8 @@ public abstract class GLContext { protected static final VersionNumber Version800 = new VersionNumber(8, 0, 0); + private static final String S_EMPTY = ""; + // // Cached keys, bits [0..15] // @@ -204,7 +208,7 @@ public abstract class GLContext { private final HashMap<String, Object> attachedObjects = new HashMap<String, Object>(); // RecursiveLock maintains a queue of waiting Threads, ensuring the longest waiting thread will be notified at unlock. - protected final RecursiveLock lock = LockFactory.createRecursiveLock(); + protected final RecursiveLock lock = LockFactory.createRecursiveLock(); // FIXME: Move to GLContextImpl when incr. minor version (incompatible change) /** The underlying native OpenGL context */ protected volatile long contextHandle; // volatile: avoid locking for read-only access @@ -249,6 +253,17 @@ public abstract class GLContext { return GLContextShareSet.isShared(this); } + /** + * Returns the shared master GLContext of this GLContext if shared, otherwise return <code>null</code>. + * <p> + * Returns this GLContext, if it is a shared master. + * </p> + * @since 2.2.1 + */ + public final GLContext getSharedMaster() { + return GLContextShareSet.getSharedMaster(this); + } + /** Returns a new list of created GLContext shared with this GLContext. */ public final List<GLContext> getCreatedShares() { return GLContextShareSet.getCreatedShares(this); @@ -282,15 +297,24 @@ public abstract class GLContext { } /** - * Sets the read/write drawable for framebuffer operations. + * Sets the read/write drawable for framebuffer operations, i.e. reassociation of the context's drawable. * <p> * If the arguments reflect the current state of this context * this method is a no-operation and returns the old and current {@link GLDrawable}. * </p> * <p> - * If the context was current on this thread, it is being released before switching the drawable - * and made current afterwards. However the user shall take extra care that not other thread - * attempts to make this context current. Otherwise a race condition may happen. + * Remarks: + * <ul> + * <li>{@link GL#glFinish() glFinish()} is issued if context {@link #isCreated()} and a {@link #getGLDrawable() previous drawable} was bound before disassociation.</li> + * <li>If the context was current on this thread, it is being released before drawable reassociation + * and made current afterwards.</li> + * <li>Implementation may issue {@link #makeCurrent()} and {@link #release()} while drawable reassociation.</li> + * <li>The user shall take extra care of thread synchronization, + * i.e. lock the involved {@link GLDrawable#getNativeSurface() drawable's} {@link NativeSurface}s + * to avoid a race condition. In case {@link GLAutoDrawable auto-drawable's} are used, + * their {@link GLAutoDrawable#getUpstreamLock() upstream-lock} must be locked beforehand + * see <a href="GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li> + * </ul> * </p> * @param readWrite The read/write drawable for framebuffer operations, maybe <code>null</code> to remove association. * @param setWriteOnly Only change the write-drawable, if <code>setWriteOnly</code> is <code>true</code> and @@ -316,6 +340,9 @@ public abstract class GLContext { * If the read-drawable has not been changed manually via {@link #setGLReadDrawable(GLDrawable)}, * it equals to the write-drawable (default). * </p> + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> * @see #setGLDrawable(GLDrawable, boolean) * @see #setGLReadDrawable(GLDrawable) */ @@ -354,6 +381,9 @@ public abstract class GLContext { * If the read-drawable has not been changed manually via {@link #setGLReadDrawable(GLDrawable)}, * it equals to the write-drawable (default). * </p> + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> * @see #isGLReadDrawableAvailable() * @see #setGLReadDrawable(GLDrawable) * @see #getGLReadDrawable() @@ -792,8 +822,8 @@ public abstract class GLContext { * <pre> * #version 110 * .. - * #version 150 - * #version 330 + * #version 150 core + * #version 330 compatibility * ... * </pre> * And for ES: @@ -809,11 +839,20 @@ public abstract class GLContext { */ public final String getGLSLVersionString() { if( ctxGLSLVersion.isZero() ) { - return ""; + return S_EMPTY; } final int minor = ctxGLSLVersion.getMinor(); - final String esSuffix = isGLES() && ctxGLSLVersion.compareTo(Version300) >= 0 ? " es" : ""; - return "#version " + ctxGLSLVersion.getMajor() + ( minor < 10 ? "0"+minor : minor ) + esSuffix + "\n" ; + final String profileOpt; + if( isGLES() ) { + profileOpt = ctxGLSLVersion.compareTo(Version300) >= 0 ? " es" : S_EMPTY; + } else if( isGLCoreProfile() ) { + profileOpt = ctxGLSLVersion.compareTo(Version150) >= 0 ? " core" : S_EMPTY; + } else if( isGLCompatibilityProfile() ) { + profileOpt = ctxGLSLVersion.compareTo(Version150) >= 0 ? " compatibility" : S_EMPTY; + } else { + throw new InternalError("Neither ES, Core nor Compat: "+this); // see validateProfileBits(..) + } + return "#version " + ctxGLSLVersion.getMajor() + ( minor < 10 ? "0"+minor : minor ) + profileOpt + "\n" ; } protected static final VersionNumber getStaticGLSLVersionNumber(final int glMajorVersion, final int glMinorVersion, final int ctxOptions) { @@ -966,8 +1005,7 @@ public abstract class GLContext { * @see GLProfile#isGL4bc() */ public final boolean isGL4bc() { - return 0 != (ctxOptions & CTX_IS_ARB_CREATED) && - 0 != (ctxOptions & CTX_PROFILE_COMPAT) && + return 0 != (ctxOptions & CTX_PROFILE_COMPAT) && ctxVersion.getMajor() >= 4; } @@ -976,8 +1014,7 @@ public abstract class GLContext { * @see GLProfile#isGL4() */ public final boolean isGL4() { - return 0 != (ctxOptions & CTX_IS_ARB_CREATED) && - 0 != (ctxOptions & (CTX_PROFILE_COMPAT|CTX_PROFILE_CORE)) && + return 0 != (ctxOptions & (CTX_PROFILE_COMPAT|CTX_PROFILE_CORE)) && ctxVersion.getMajor() >= 4; } @@ -985,8 +1022,7 @@ public abstract class GLContext { * Indicates whether this GLContext uses a GL4 core profile. <p>Includes [ GL4 ].</p> */ public final boolean isGL4core() { - return 0 != ( ctxOptions & CTX_IS_ARB_CREATED ) && - 0 != ( ctxOptions & CTX_PROFILE_CORE ) && + return 0 != ( ctxOptions & CTX_PROFILE_CORE ) && ctxVersion.getMajor() >= 4; } @@ -995,8 +1031,7 @@ public abstract class GLContext { * @see GLProfile#isGL3bc() */ public final boolean isGL3bc() { - return 0 != (ctxOptions & CTX_IS_ARB_CREATED) && - 0 != (ctxOptions & CTX_PROFILE_COMPAT) && + return 0 != (ctxOptions & CTX_PROFILE_COMPAT) && ctxVersion.compareTo(Version310) >= 0 ; } @@ -1005,8 +1040,7 @@ public abstract class GLContext { * @see GLProfile#isGL3() */ public final boolean isGL3() { - return 0 != (ctxOptions & CTX_IS_ARB_CREATED) && - 0 != (ctxOptions & (CTX_PROFILE_COMPAT|CTX_PROFILE_CORE)) && + return 0 != (ctxOptions & (CTX_PROFILE_COMPAT|CTX_PROFILE_CORE)) && ctxVersion.compareTo(Version310) >= 0 ; } @@ -1014,8 +1048,7 @@ public abstract class GLContext { * Indicates whether this GLContext uses a GL3 core profile. <p>Includes [ GL4, GL3 ].</p> */ public final boolean isGL3core() { - return 0 != ( ctxOptions & CTX_IS_ARB_CREATED ) && - 0 != ( ctxOptions & CTX_PROFILE_CORE ) && + return 0 != ( ctxOptions & CTX_PROFILE_CORE ) && ctxVersion.compareTo(Version310) >= 0; } @@ -1024,8 +1057,7 @@ public abstract class GLContext { */ public final boolean isGLcore() { return ( 0 != ( ctxOptions & CTX_PROFILE_ES ) && ctxVersion.getMajor() >= 2 ) || - ( 0 != ( ctxOptions & CTX_IS_ARB_CREATED ) && - 0 != ( ctxOptions & CTX_PROFILE_CORE ) && + ( 0 != ( ctxOptions & CTX_PROFILE_CORE ) && ctxVersion.compareTo(Version310) >= 0 ) ; } @@ -1041,9 +1073,9 @@ public abstract class GLContext { } /** - * Indicates whether this GLContext's native profile does not implement a default <i>vertex array object</i> (VAO), - * starting w/ OpenGL 3.1 core and GLES3. - * <p>Includes [ GL4, GL3, GLES3 ].</p> + * Indicates whether this GLContext's native profile does not implement a <i>default vertex array object</i> (VAO), + * starting w/ OpenGL 3.1 core. + * <p>Includes [ GL4, GL3 ].</p> * <pre> Due to GL 3.1 core spec: E.1. DEPRECATED AND REMOVED FEATURES (p 296), GL 3.2 core spec: E.2. DEPRECATED AND REMOVED FEATURES (p 331) @@ -1052,8 +1084,17 @@ public abstract class GLContext { More clear is GL 4.3 core spec: 10.4 (p 307). * </pre> * <pre> - GLES3 is included, since upcoming ES releases > 3.0 may behave the same: + ES 3.x is <i>not</i> included here. + Due to it's ES 2.0 backward compatibility it still supports the following features: + <i>client side vertex arrays</i> + <i>default vertex array object</i> + + Binding a custom VAO with ES 3.0 would cause <i>client side vertex arrays</i> via {@link GL2ES1#glVertexPointer(int, int, int, java.nio.Buffer) glVertexPointer} + to produce <code>GL_INVALID_OPERATION</code>. + + However, they are marked <i>deprecated</i>: GL ES 3.0 spec F.1. Legacy Features (p 322). + GL ES 3.1 spec F.1. Legacy Features (p 454). * </pre> * <p> * If no default VAO is implemented in the native OpenGL profile, @@ -1062,7 +1103,7 @@ public abstract class GLContext { * @see #getDefaultVAO() */ public final boolean hasNoDefaultVAO() { - return ( 0 != ( ctxOptions & CTX_PROFILE_ES ) && ctxVersion.getMajor() >= 3 ) || + return // ES 3.x not included, see above. ( 0 != ( ctxOptions & CTX_PROFILE_ES ) && ctxVersion.getMajor() >= 3 ) || ( 0 != ( ctxOptions & CTX_IS_ARB_CREATED ) && 0 != ( ctxOptions & CTX_PROFILE_CORE ) && ctxVersion.compareTo(Version310) >= 0 @@ -1250,6 +1291,9 @@ public abstract class GLContext { /** * Return the framebuffer name bound to this context, * see {@link GL#glBindFramebuffer(int, int)}. + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> */ public abstract int getBoundFramebuffer(int target); @@ -1260,6 +1304,9 @@ public abstract class GLContext { * in case an framebuffer object ({@link com.jogamp.opengl.FBObject}) based drawable * is being used. * </p> + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> */ public abstract int getDefaultDrawFramebuffer(); @@ -1270,6 +1317,9 @@ public abstract class GLContext { * in case an framebuffer object ({@link com.jogamp.opengl.FBObject}) based drawable * is being used. * </p> + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> */ public abstract int getDefaultReadFramebuffer(); @@ -1294,13 +1344,26 @@ public abstract class GLContext { * Note-3: See {@link com.jogamp.opengl.util.GLDrawableUtil#swapBuffersBeforeRead(GLCapabilitiesImmutable) swapBuffersBeforeRead} * for read-pixels and swap-buffers implications. * </p> + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> */ public abstract int getDefaultReadBuffer(); - /** Get the default pixel data type, as required by e.g. {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer)}. */ + /** + * Get the default pixel data type, as required by e.g. {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer)}. + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> + */ public abstract int getDefaultPixelDataType(); - /** Get the default pixel data format, as required by e.g. {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer)}. */ + /** + * Get the default pixel data format, as required by e.g. {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer)}. + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> + */ public abstract int getDefaultPixelDataFormat(); /** diff --git a/src/jogl/classes/javax/media/opengl/GLDrawable.java b/src/jogl/classes/javax/media/opengl/GLDrawable.java index 57883c8ac..5c881ab73 100644 --- a/src/jogl/classes/javax/media/opengl/GLDrawable.java +++ b/src/jogl/classes/javax/media/opengl/GLDrawable.java @@ -60,8 +60,9 @@ public interface GLDrawable extends NativeSurfaceHolder { * The GLContext <code>share</code> need not be associated with this * GLDrawable and may be null if sharing of display lists and other * objects is not desired. See the note in the overview - * documentation on - * <a href="../../../spec-overview.html#SHARING">context sharing</a>. + * documentation + * <a href="../../../overview-summary.html#SHARING">context sharing</a> + * as well as {@link GLSharedContextSetter}. * </p> */ public GLContext createContext(GLContext shareWith); @@ -174,17 +175,37 @@ public interface GLDrawable extends NativeSurfaceHolder { public void swapBuffers() throws GLException; /** Fetches the {@link GLCapabilitiesImmutable} corresponding to the chosen - OpenGL capabilities (pixel format / visual / GLProfile) for this drawable.<br> + OpenGL capabilities (pixel format / visual / GLProfile) for this drawable. + <p> + This query only returns the chosen capabilities if {@link #isRealized()}. + </p> + <p> On some platforms, the pixel format is not directly associated with the drawable; a best attempt is made to return a reasonable - value in this case. <br> + value in this case. + </p> + <p> This object shall be directly associated to the attached {@link NativeSurface}'s {@link AbstractGraphicsConfiguration}, and if changes are necessary, they should reflect those as well. + </p> @return The immutable queried instance. + @see #getRequestedGLCapabilities() */ public GLCapabilitiesImmutable getChosenGLCapabilities(); + /** Fetches the {@link GLCapabilitiesImmutable} corresponding to the user requested + OpenGL capabilities (pixel format / visual / GLProfile) for this drawable. + <p> + If {@link #isRealized() realized}, {@link #getChosenGLCapabilities() the chosen capabilities} + reflect the actual selected OpenGL capabilities. + </p> + @return The immutable queried instance. + @see #getChosenGLCapabilities() + @since 2.2 + */ + public GLCapabilitiesImmutable getRequestedGLCapabilities(); + /** Fetches the {@link GLProfile} for this drawable. Returns the GLProfile object, no copy. */ diff --git a/src/jogl/classes/javax/media/opengl/GLDrawableFactory.java b/src/jogl/classes/javax/media/opengl/GLDrawableFactory.java index 82808a3cb..71568ee76 100644 --- a/src/jogl/classes/javax/media/opengl/GLDrawableFactory.java +++ b/src/jogl/classes/javax/media/opengl/GLDrawableFactory.java @@ -98,23 +98,6 @@ public abstract class GLDrawableFactory { protected static final boolean DEBUG = Debug.debug("GLDrawable"); - /** - * We have to disable support for ANGLE, the D3D ES2 emulation on Windows provided w/ Firefox and Chrome. - * When run in the mentioned browsers, the eglInitialize(..) implementation crashes. - * <p> - * This can be overridden by explicitly enabling ANGLE on Windows by setting the property - * <code>jogl.enable.ANGLE</code>. - * </p> - */ - protected static final boolean enableANGLE = Debug.isPropertyDefined("jogl.enable.ANGLE", true); - - /** - * In case no OpenGL ES implementation is required - * and if the running platform may have a buggy implementation, - * setting the property <code>jogl.disable.opengles</code> disables querying a possible existing OpenGL ES implementation. - */ - protected static final boolean disableOpenGLES = Debug.isPropertyDefined("jogl.disable.opengles", true); - private static volatile boolean isInit = false; private static GLDrawableFactory eglFactory; private static GLDrawableFactory nativeOSFactory; @@ -179,7 +162,7 @@ public abstract class GLDrawableFactory { } tmp = null; - if(!disableOpenGLES) { + if(!GLProfile.disableOpenGLES) { try { tmp = (GLDrawableFactory) ReflectionUtil.createInstance("jogamp.opengl.egl.EGLDrawableFactory", cl); } catch (final Exception jre) { @@ -222,7 +205,7 @@ public abstract class GLDrawableFactory { System.err.println("GLDrawableFactory.shutdownAll["+(i+1)+"/"+gldfCount+"]: "+gldf.getClass().getName()); } try { - gldf.resetDisplayGamma(); + gldf.resetAllDisplayGammaNoSync(); gldf.shutdownImpl(); } catch (final Throwable t) { System.err.println("GLDrawableFactory.shutdownImpl: Caught "+t.getClass().getName()+" during factory shutdown #"+(i+1)+"/"+gldfCount+" "+gldf.getClass().getName()); @@ -259,7 +242,70 @@ public abstract class GLDrawableFactory { protected abstract void shutdownImpl(); - public abstract void resetDisplayGamma(); + /** + * Sets the gamma, brightness, and contrast of the display associated with the given <code>surface</code>. + * <p> + * This functionality is not available on all platforms and + * graphics hardware. Returns true if the settings were successfully + * changed, false if not. This method may return false for some + * values of the incoming arguments even on hardware which does + * support the underlying functionality. </p> + * <p> + * If this method returns true, the display settings will + * automatically be reset to their original values upon JVM exit + * (assuming the JVM does not crash); if the user wishes to change + * the display settings back to normal ahead of time, + * use {@link #resetDisplayGamma(NativeSurface)} or {@link #resetAllDisplayGamma()}. + * </p> + * <p> + * It is recommended to call {@link #resetDisplayGamma(NativeSurface)} or {@link #resetAllDisplayGamma()} + * before calling e.g. <code>System.exit()</code> from the application rather than + * rely on the shutdown hook functionality due to inevitable race + * conditions and unspecified behavior during JVM teardown. + * </p> + * <p> + * This method may be called multiple times during the application's + * execution, but calling {@link #resetDisplayGamma(NativeSurface)} + * will only reset the settings to the values + * before the first call to this method. </p> + * + * @param surface denominates the display device + * @param gamma The gamma value, typically > 1.0 (default values vary, but typically roughly 1.0) + * @param brightness The brightness value between -1.0 and 1.0, inclusive (default values vary, but typically 0) + * @param contrast The contrast, greater than 0.0 (default values vary, but typically 1) + * + * @return true if gamma settings were successfully changed, false if not + * @throws IllegalArgumentException if any of the parameters were out-of-bounds + * @see #resetDisplayGamma(NativeSurface) + * @see #resetAllDisplayGamma() + */ + public abstract boolean setDisplayGamma(final NativeSurface surface, final float gamma, final float brightness, final float contrast) throws IllegalArgumentException; + + /** + * Resets the gamma, brightness and contrast values of the display associated with the given <code>surface</code> + * to its original values before {@link #setDisplayGamma(NativeSurface, float, float, float) setDisplayGamma} + * was called the first time. + * <p> + * While it is not explicitly required that this method be called before + * exiting manually, calling it is recommended because of the inevitable + * unspecified behavior during JVM teardown. + * </p> + */ + public abstract void resetDisplayGamma(final NativeSurface surface); + + /** + * Resets the gamma, brightness and contrast values of all modified + * displays to their original values before {@link #setDisplayGamma(NativeSurface, float, float, float) setDisplayGamma} + * was called the first time. + * <p> + * While it is not explicitly required that this method be called before + * exiting manually, calling it is recommended because of the inevitable + * unspecified behavior during JVM teardown. + * </p> + */ + public abstract void resetAllDisplayGamma(); + + protected abstract void resetAllDisplayGammaNoSync(); /** * Retrieve the default <code>device</code> {@link AbstractGraphicsDevice#getConnection() connection}, diff --git a/src/jogl/classes/javax/media/opengl/GLException.java b/src/jogl/classes/javax/media/opengl/GLException.java index 6a287c969..3f76a6299 100644 --- a/src/jogl/classes/javax/media/opengl/GLException.java +++ b/src/jogl/classes/javax/media/opengl/GLException.java @@ -41,7 +41,7 @@ package javax.media.opengl; /** A generic exception for OpenGL errors used throughout the binding as a substitute for {@link RuntimeException}. */ - +@SuppressWarnings("serial") public class GLException extends RuntimeException { /** Constructs a GLException object. */ public GLException() { @@ -65,4 +65,22 @@ public class GLException extends RuntimeException { public GLException(final Throwable cause) { super(cause); } + + /** + * Constructs a GLException object with the specified root + * cause with a decorating message including the current thread name. + * @since 2.2 + */ + public static GLException newGLException(final Throwable t) { + return new GLException("Caught "+t.getClass().getSimpleName()+": "+t.getMessage()+" on thread "+Thread.currentThread().getName(), t); + } + + /** + * Dumps a Throwable in a decorating message including the current thread name, and stack trace. + * @since 2.2 + */ + public static void dumpThrowable(final String additionalDescr, final Throwable t) { + System.err.println("Caught "+additionalDescr+" "+t.getClass().getSimpleName()+": "+t.getMessage()+" on thread "+Thread.currentThread().getName()); + t.printStackTrace(); + } } diff --git a/src/jogl/classes/javax/media/opengl/GLFBODrawable.java b/src/jogl/classes/javax/media/opengl/GLFBODrawable.java index a34fca0fa..524c77e9d 100644 --- a/src/jogl/classes/javax/media/opengl/GLFBODrawable.java +++ b/src/jogl/classes/javax/media/opengl/GLFBODrawable.java @@ -31,7 +31,10 @@ package javax.media.opengl; import javax.media.nativewindow.NativeWindowException; import com.jogamp.opengl.FBObject; +import com.jogamp.opengl.FBObject.Colorbuffer; +import com.jogamp.opengl.FBObject.ColorAttachment; import com.jogamp.opengl.FBObject.TextureAttachment; +import com.jogamp.opengl.GLRendererQuirks; /** * Platform-independent {@link GLDrawable} specialization, @@ -62,12 +65,12 @@ import com.jogamp.opengl.FBObject.TextureAttachment; * </p> * <p> * It would be possible to implement double buffering simply using - * {@link TextureAttachment}s with one {@link FBObject framebuffer}. + * {@link Colorbuffer}s with one {@link FBObject framebuffer}. * This would require mode selection and hence complicate the API. Besides, it would * not support differentiation of read and write framebuffer and hence not be spec compliant. * </p> * <p> - * Actual swapping of the {@link TextureAttachment texture}s and/or {@link FBObject framebuffer} + * Actual swapping of the {@link Colorbuffer}s and/or {@link FBObject framebuffer} * is performed either in the {@link jogamp.opengl.GLContextImpl#contextMadeCurrent(boolean) context current hook} * or when {@link jogamp.opengl.GLDrawableImpl#swapBuffersImpl(boolean) swapping buffers}, whatever comes first. * </p> @@ -75,19 +78,42 @@ import com.jogamp.opengl.FBObject.TextureAttachment; public interface GLFBODrawable extends GLDrawable { // public enum DoubleBufferMode { NONE, TEXTURE, FBO }; // TODO: Add or remove TEXTURE (only) DoubleBufferMode support + /** FBO Mode Bit: Use a {@link TextureAttachment} for the {@link #getColorbuffer(int) render colorbuffer}, see {@link #setFBOMode(int)}. */ + public static final int FBOMODE_USE_TEXTURE = 1 << 0; + /** * @return <code>true</code> if initialized, i.e. a {@link GLContext} is bound and made current once, otherwise <code>false</code>. */ public boolean isInitialized(); /** + * Set the FBO mode bits used for FBO creation. + * <p> + * Default value is: {@link #FBOMODE_USE_TEXTURE}. + * </p> + * <p> + * If {@link GLRendererQuirks#BuggyColorRenderbuffer} is set, + * {@link #FBOMODE_USE_TEXTURE} is always added at initialization. + * </p> + * + * @param modeBits custom FBO mode bits like {@link #FBOMODE_USE_TEXTURE}. + * @throws IllegalStateException if already initialized, see {@link #isInitialized()}. + */ + void setFBOMode(final int modeBits) throws IllegalStateException; + + /** + * @return the used FBO mode bits, mutable via {@link #setFBOMode(int)} + */ + int getFBOMode(); + + /** * Notify this instance about upstream size change * to reconfigure the {@link FBObject}. * @param gl GL context object bound to this drawable, will be made current during operation. * A prev. current context will be make current after operation. * @throws GLException if resize operation failed */ - void resetSize(GL gl) throws GLException; + void resetSize(final GL gl) throws GLException; /** * @return the used texture unit @@ -98,7 +124,7 @@ public interface GLFBODrawable extends GLDrawable { * * @param unit the texture unit to be used */ - void setTextureUnit(int unit); + void setTextureUnit(final int unit); /** * Set the number of sample buffers if using MSAA @@ -108,7 +134,7 @@ public interface GLFBODrawable extends GLDrawable { * @param newSamples new sample size * @throws GLException if resetting the FBO failed */ - void setNumSamples(GL gl, int newSamples) throws GLException; + void setNumSamples(final GL gl, final int newSamples) throws GLException; /** * @return the number of sample buffers if using MSAA, otherwise 0 @@ -124,9 +150,9 @@ public interface GLFBODrawable extends GLDrawable { * Must be called before {@link #isInitialized() initialization}, otherwise an exception is thrown. * </p> * @return the new number of buffers (FBO) used, maybe different than the requested <code>bufferCount</code> (see above) - * @throws GLException if already initialized, see {@link #isInitialized()}. + * @throws IllegalStateException if already initialized, see {@link #isInitialized()}. */ - int setNumBuffers(int bufferCount) throws GLException; + int setNumBuffers(final int bufferCount) throws IllegalStateException, GLException; /** * @return the number of buffers (FBO) being used. 1 if not using {@link GLCapabilities#getDoubleBuffered() double buffering}, @@ -162,19 +188,25 @@ public interface GLFBODrawable extends GLDrawable { * @return the named {@link FBObject} * @throws IllegalArgumentException if an illegal buffer name is being used */ - FBObject getFBObject(int bufferName) throws IllegalArgumentException; + FBObject getFBObject(final int bufferName) throws IllegalArgumentException; /** - * Returns the named texture buffer. + * Returns the named {@link Colorbuffer} instance. * <p> * If MSAA is being used, only the {@link GL#GL_FRONT} buffer is accessible * and an exception is being thrown if {@link GL#GL_BACK} is being requested. * </p> + * <p> + * Depending on the {@link #setFBOMode(int) fbo mode} the resulting {@link Colorbuffer} + * is either a {@link TextureAttachment} if {@link #FBOMODE_USE_TEXTURE} is set, + * otherwise a {@link ColorAttachment}. + * See {@link Colorbuffer#isTextureAttachment()}. + * </p> * @param bufferName {@link GL#GL_FRONT} and {@link GL#GL_BACK} are valid buffer names - * @return the named {@link TextureAttachment} + * @return the named {@link Colorbuffer} * @throws IllegalArgumentException if using MSAA and {@link GL#GL_BACK} is requested or an illegal buffer name is being used */ - FBObject.TextureAttachment getTextureBuffer(int bufferName) throws IllegalArgumentException; + Colorbuffer getColorbuffer(final int bufferName) throws IllegalArgumentException; /** Resizeable {@link GLFBODrawable} specialization */ public interface Resizeable extends GLFBODrawable { diff --git a/src/jogl/classes/javax/media/opengl/GLOffscreenAutoDrawable.java b/src/jogl/classes/javax/media/opengl/GLOffscreenAutoDrawable.java index a69480242..62d10d4cb 100644 --- a/src/jogl/classes/javax/media/opengl/GLOffscreenAutoDrawable.java +++ b/src/jogl/classes/javax/media/opengl/GLOffscreenAutoDrawable.java @@ -39,6 +39,12 @@ import com.jogamp.opengl.FBObject; * This class distinguishes itself from {@link GLAutoDrawable} * with it's {@link #setSurfaceSize(int, int)} functionality. * </p> + * <p> + * <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> + * To share a {@link GLContext} see the following note in the documentation overview: + * <a href="../../../overview-summary.html#SHARING">context sharing</a> + * as well as {@link GLSharedContextSetter}. + * </p> */ public interface GLOffscreenAutoDrawable extends GLAutoDrawable, GLSharedContextSetter { diff --git a/src/jogl/classes/javax/media/opengl/GLProfile.java b/src/jogl/classes/javax/media/opengl/GLProfile.java index 324fdee92..08712e488 100644 --- a/src/jogl/classes/javax/media/opengl/GLProfile.java +++ b/src/jogl/classes/javax/media/opengl/GLProfile.java @@ -44,6 +44,7 @@ import jogamp.opengl.DesktopGLDynamicLookupHelper; import com.jogamp.common.GlueGenVersion; import com.jogamp.common.jvm.JNILibLoaderBase; import com.jogamp.common.os.Platform; +import com.jogamp.common.util.PropertyAccess; import com.jogamp.common.util.ReflectionUtil; import com.jogamp.common.util.VersionUtil; import com.jogamp.common.util.cache.TempJarCache; @@ -75,11 +76,70 @@ import java.util.Map; */ public class GLProfile { - public static final boolean DEBUG = Debug.debug("GLProfile"); + public static final boolean DEBUG; + + /** + * In case no OpenGL ES profiles are required + * and if one platform may have a buggy implementation, + * setting the property <code>jogl.disable.opengles</code> disables querying possible existing OpenGL ES profiles. + */ + public static final boolean disableOpenGLES; + + /** + * In case no native OpenGL core profiles are required + * and if one platform may have a buggy implementation, + * setting the property <code>jogl.disable.openglcore</code> disables querying possible existing native OpenGL core profiles. + * <p> + * This exclusion is disabled for {@link Platform.OSType#MACOS}. + * </p> + */ + public static final boolean disableOpenGLCore; + + /** + * In case the implementation of the <i>ARB_create_context</i> + * context creation extension is buggy on one platform, + * setting the property <code>jogl.disable.openglarbcontext</code> disables utilizing it. + * <p> + * This exclusion is disabled for {@link Platform.OSType#MACOS}. + * </p> + */ + public static final boolean disableOpenGLARBContext; + + /** + * We have to disable support for ANGLE, the D3D ES2 emulation on Windows provided w/ Firefox and Chrome. + * When run in the mentioned browsers, the eglInitialize(..) implementation crashes. + * <p> + * This can be overridden by explicitly enabling ANGLE on Windows by setting the property + * <code>jogl.enable.ANGLE</code>. + * </p> + */ + public static final boolean enableANGLE; static { // Also initializes TempJarCache if shall be used. Platform.initSingleton(); + final boolean isOSX = Platform.OSType.MACOS == Platform.getOSType(); + + DEBUG = Debug.debug("GLProfile"); + disableOpenGLES = PropertyAccess.isPropertyDefined("jogl.disable.opengles", true); + disableOpenGLCore = PropertyAccess.isPropertyDefined("jogl.disable.openglcore", true) && !isOSX; + disableOpenGLARBContext = PropertyAccess.isPropertyDefined("jogl.disable.openglarbcontext", true) && !isOSX; + enableANGLE = PropertyAccess.isPropertyDefined("jogl.enable.ANGLE", true); + } + + /** + * @return <code>true</code> if JOGL has been initialized, i.e. manually via {@link #initSingleton()} or implicit, + * otherwise returns <code>false</code>. + * + * @since 2.2.1 + */ + public static boolean isInitialized() { + initLock.lock(); + try { + return initialized; + } finally { + initLock.unlock(); + } } /** @@ -425,7 +485,7 @@ public class GLProfile { if(null != map) { for (final Map.Entry<String,GLProfile> entry : map.entrySet()) { - if( !GL_DEFAULT.equals(entry.getKey()) ) { + if( GL_DEFAULT != entry.getKey() ) { if(useIndent) { doIndent(sb.append(Platform.getNewline()), indent, indentCount); } @@ -468,44 +528,46 @@ public class GLProfile { /** The desktop OpenGL compatibility profile 4.x, with x >= 0, ie GL2 plus GL4.<br> <code>bc</code> stands for backward compatibility. */ - public static final String GL4bc = "GL4bc"; + public static final String GL4bc = "GL4bc"; // Implicitly intern(), see Bug 1059 /** The desktop OpenGL core profile 4.x, with x >= 0 */ - public static final String GL4 = "GL4"; + public static final String GL4 = "GL4"; // Implicitly intern(), see Bug 1059 /** The desktop OpenGL compatibility profile 3.x, with x >= 1, ie GL2 plus GL3.<br> <code>bc</code> stands for backward compatibility. */ - public static final String GL3bc = "GL3bc"; + public static final String GL3bc = "GL3bc"; // Implicitly intern(), see Bug 1059 /** The desktop OpenGL core profile 3.x, with x >= 1 */ - public static final String GL3 = "GL3"; + public static final String GL3 = "GL3"; // Implicitly intern(), see Bug 1059 /** The desktop OpenGL profile 1.x up to 3.0 */ - public static final String GL2 = "GL2"; + public static final String GL2 = "GL2"; // Implicitly intern(), see Bug 1059 /** The embedded OpenGL profile ES 1.x, with x >= 0 */ - public static final String GLES1 = "GLES1"; + public static final String GLES1 = "GLES1"; // Implicitly intern(), see Bug 1059 /** The embedded OpenGL profile ES 2.x, with x >= 0 */ - public static final String GLES2 = "GLES2"; + public static final String GLES2 = "GLES2"; // Implicitly intern(), see Bug 1059 /** The embedded OpenGL profile ES 3.x, with x >= 0 */ - public static final String GLES3 = "GLES3"; + public static final String GLES3 = "GLES3"; // Implicitly intern(), see Bug 1059 /** The intersection of the desktop GL2 and embedded ES1 profile */ - public static final String GL2ES1 = "GL2ES1"; + public static final String GL2ES1 = "GL2ES1"; // Implicitly intern(), see Bug 1059 /** The intersection of the desktop GL3, GL2 and embedded ES2 profile */ - public static final String GL2ES2 = "GL2ES2"; + public static final String GL2ES2 = "GL2ES2"; // Implicitly intern(), see Bug 1059 /** The intersection of the desktop GL3 and GL2 profile */ - public static final String GL2GL3 = "GL2GL3"; + public static final String GL2GL3 = "GL2GL3"; // Implicitly intern(), see Bug 1059 /** The intersection of the desktop GL4 and ES3 profile, available only if either ES3 or GL4 w/ <code>GL_ARB_ES3_compatibility</code> is available. */ - public static final String GL4ES3 = "GL4ES3"; + public static final String GL4ES3 = "GL4ES3"; // Implicitly intern(), see Bug 1059 /** The default profile, used for the device default profile map */ - private static final String GL_DEFAULT = "GL_DEFAULT"; + private static final String GL_DEFAULT = "GL_DEFAULT"; // Implicitly intern(), see Bug 1059 + /** The default profile, used for the device default profile map */ + private static final String GL_GL = "GL"; // Implicitly intern(), see Bug 1059 /** * All GL Profiles in the order of default detection. @@ -891,7 +953,7 @@ public class GLProfile { public static GLProfile get(final AbstractGraphicsDevice device, String profile) throws GLException { - if(null==profile || profile.equals("GL")) { + if(null==profile || profile == GL_GL) { profile = GL_DEFAULT; } final HashMap<String /*GLProfile_name*/, GLProfile> glpMap = getProfileMap(device, true); @@ -963,21 +1025,21 @@ public class GLProfile { * This requires an EGL interface. */ public static boolean usesNativeGLES1(final String profileImpl) { - return GLES1.equals(profileImpl); + return GLES1 == profileImpl; } /** Indicates whether the native OpenGL ES3 or ES2 profile is in use. * This requires an EGL, ES3 or ES2 compatible interface. */ public static boolean usesNativeGLES2(final String profileImpl) { - return GLES3.equals(profileImpl) || GLES2.equals(profileImpl); + return GLES3 == profileImpl || GLES2 == profileImpl; } /** Indicates whether the native OpenGL ES2 profile is in use. * This requires an EGL, ES3 compatible interface. */ public static boolean usesNativeGLES3(final String profileImpl) { - return GLES3.equals(profileImpl); + return GLES3 == profileImpl; } /** Indicates whether either of the native OpenGL ES profiles are in use. */ @@ -1529,7 +1591,7 @@ public class GLProfile { @Override public String toString() { - return "GLProfile[" + getName() + "/" + getImplName() + "."+(this.isHardwareRasterizer?"hw":"sw")+"]"; + return "GLProfile[" + getName() + "/" + getImplName() + "."+(this.isHardwareRasterizer?"hw":"sw")+(isCustom?".custom":"")+"]"; } private static /*final*/ boolean isAWTAvailable; @@ -1952,13 +2014,13 @@ public class GLProfile { if( null != profileImpl ) { final GLProfile glProfile; if( profile.equals( profileImpl ) ) { - glProfile = new GLProfile(profile, null, isHardwareRasterizer[0]); + glProfile = new GLProfile(profile, null, isHardwareRasterizer[0], false /* custom */); } else { final GLProfile _mglp = _mappedProfiles.get( profileImpl ); if( null == _mglp ) { throw new InternalError("XXX0 profile["+i+"]: "+profile+" -> profileImpl "+profileImpl+" !!! not mapped "); } - glProfile = new GLProfile(profile, _mglp, isHardwareRasterizer[0]); + glProfile = new GLProfile(profile, _mglp, isHardwareRasterizer[0], false /* custom */); } _mappedProfiles.put(profile, glProfile); if (DEBUG) { @@ -1994,7 +2056,7 @@ public class GLProfile { * Returns the profile implementation */ private static String computeProfileImpl(final AbstractGraphicsDevice device, final String profile, final boolean desktopCtxUndef, final boolean esCtxUndef, final boolean isHardwareRasterizer[]) { - if (GL2ES1.equals(profile)) { + if (GL2ES1 == profile) { final boolean es1HardwareRasterizer[] = new boolean[1]; final boolean gles1Available = hasGLES1Impl && ( esCtxUndef || GLContext.isGLES1Available(device, es1HardwareRasterizer) ); final boolean gles1HWAvailable = gles1Available && es1HardwareRasterizer[0] ; @@ -2019,7 +2081,7 @@ public class GLProfile { isHardwareRasterizer[0] = es1HardwareRasterizer[0]; return GLES1; } - } else if (GL2ES2.equals(profile)) { + } else if (GL2ES2 == profile) { final boolean es2HardwareRasterizer[] = new boolean[1]; final boolean gles2Available = hasGLES3Impl && ( esCtxUndef || GLContext.isGLES2Available(device, es2HardwareRasterizer) ); final boolean gles2HWAvailable = gles2Available && es2HardwareRasterizer[0] ; @@ -2061,7 +2123,7 @@ public class GLProfile { isHardwareRasterizer[0] = es2HardwareRasterizer[0]; return GLES2; } - } else if (GL4ES3.equals(profile)) { + } else if (GL4ES3 == profile) { final boolean gles3CompatAvail = GLContext.isGLES3CompatibleAvailable(device); if( desktopCtxUndef || esCtxUndef || gles3CompatAvail ) { final boolean es3HardwareRasterizer[] = new boolean[1]; @@ -2078,23 +2140,13 @@ public class GLProfile { return GL4bc; } } - if(GLContext.isGL3Available(device, isHardwareRasterizer)) { - if(!gles3HWAvailable || isHardwareRasterizer[0]) { - return GL3; - } - } - if( desktopCtxUndef || GLContext.isGL3bcAvailable(device, isHardwareRasterizer)) { - if(!gles3HWAvailable || isHardwareRasterizer[0]) { - return GL3bc; - } - } } if(gles3Available) { isHardwareRasterizer[0] = es3HardwareRasterizer[0]; return GLES3; } } - } else if(GL2GL3.equals(profile)) { + } else if(GL2GL3 == profile) { if(hasGL234Impl) { if( GLContext.isGL4bcAvailable(device, isHardwareRasterizer)) { return GL4bc; @@ -2108,21 +2160,21 @@ public class GLProfile { return GL2; } } - } else if(GL4bc.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL4bcAvailable(device, isHardwareRasterizer))) { + } else if(GL4bc == profile && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL4bcAvailable(device, isHardwareRasterizer))) { return desktopCtxUndef ? GL4bc : GLContext.getAvailableGLProfileName(device, 4, GLContext.CTX_PROFILE_COMPAT); - } else if(GL4.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL4Available(device, isHardwareRasterizer))) { + } else if(GL4 == profile && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL4Available(device, isHardwareRasterizer))) { return desktopCtxUndef ? GL4 : GLContext.getAvailableGLProfileName(device, 4, GLContext.CTX_PROFILE_CORE); - } else if(GL3bc.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL3bcAvailable(device, isHardwareRasterizer))) { + } else if(GL3bc == profile && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL3bcAvailable(device, isHardwareRasterizer))) { return desktopCtxUndef ? GL3bc : GLContext.getAvailableGLProfileName(device, 3, GLContext.CTX_PROFILE_COMPAT); - } else if(GL3.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL3Available(device, isHardwareRasterizer))) { + } else if(GL3 == profile && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL3Available(device, isHardwareRasterizer))) { return desktopCtxUndef ? GL3 : GLContext.getAvailableGLProfileName(device, 3, GLContext.CTX_PROFILE_CORE); - } else if(GL2.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL2Available(device, isHardwareRasterizer))) { + } else if(GL2 == profile && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL2Available(device, isHardwareRasterizer))) { return desktopCtxUndef ? GL2 : GLContext.getAvailableGLProfileName(device, 2, GLContext.CTX_PROFILE_COMPAT); - } else if(GLES3.equals(profile) && hasGLES3Impl && ( esCtxUndef || GLContext.isGLES3Available(device, isHardwareRasterizer))) { + } else if(GLES3 == profile && hasGLES3Impl && ( esCtxUndef || GLContext.isGLES3Available(device, isHardwareRasterizer))) { return esCtxUndef ? GLES3 : GLContext.getAvailableGLProfileName(device, 3, GLContext.CTX_PROFILE_ES); - } else if(GLES2.equals(profile) && hasGLES3Impl && ( esCtxUndef || GLContext.isGLES2Available(device, isHardwareRasterizer))) { + } else if(GLES2 == profile && hasGLES3Impl && ( esCtxUndef || GLContext.isGLES2Available(device, isHardwareRasterizer))) { return esCtxUndef ? GLES2 : GLContext.getAvailableGLProfileName(device, 2, GLContext.CTX_PROFILE_ES); - } else if(GLES1.equals(profile) && hasGLES1Impl && ( esCtxUndef || GLContext.isGLES1Available(device, isHardwareRasterizer))) { + } else if(GLES1 == profile && hasGLES1Impl && ( esCtxUndef || GLContext.isGLES1Available(device, isHardwareRasterizer))) { return esCtxUndef ? GLES1 : GLContext.getAvailableGLProfileName(device, 1, GLContext.CTX_PROFILE_ES); } return null; @@ -2180,13 +2232,19 @@ public class GLProfile { } } - private GLProfile(final String profile, final GLProfile profileImpl, final boolean isHardwareRasterizer) { + private GLProfile(final String profile, final GLProfile profileImpl, final boolean isHardwareRasterizer, final boolean isCustom) { this.profile = profile; this.profileImpl = profileImpl; this.isHardwareRasterizer = isHardwareRasterizer; + this.isCustom = isCustom; + } + + public static GLProfile createCustomGLProfile(final String profile, final GLProfile profileImpl) { + return new GLProfile(profile, profileImpl, profileImpl.isHardwareRasterizer, true); } private final GLProfile profileImpl; private final String profile; private final boolean isHardwareRasterizer; + private final boolean isCustom; } diff --git a/src/jogl/classes/javax/media/opengl/GLSharedContextSetter.java b/src/jogl/classes/javax/media/opengl/GLSharedContextSetter.java index 679898dca..526967d69 100644 --- a/src/jogl/classes/javax/media/opengl/GLSharedContextSetter.java +++ b/src/jogl/classes/javax/media/opengl/GLSharedContextSetter.java @@ -35,9 +35,12 @@ package javax.media.opengl; * and textures among OpenGL contexts is supported with this interface. * </p> * <p> - * A <i>master</i> {@link GLContext} is the {@link GLContext} which is created first, - * shared {@link GLContext} w/ this master are referred as slave {@link GLContext} - * and controls the shared object's lifecycle, i.e. their construction and destruction. + * A <i>master</i> {@link GLContext} is the {@link GLContext} which is created first. + * Subsequent shared {@link GLContext} w/ the <i>master</i> are referred as <i>slave</i> {@link GLContext}. + * </p> + * <p> + * Implementations of this interface control the <i>slave's</i> {@link GLContext} and {@link GLAutoDrawable} realization, + * i.e. the <i>slave</i> {@link GLAutoDrawable} will not be realized before their associated <i>master</i>. * </p> * <p> * Using the nearest or same {@link GLCapabilitiesImmutable#getVisualID(javax.media.nativewindow.VisualIDHolder.VIDType) visual ID} @@ -50,12 +53,17 @@ package javax.media.opengl; * At least this has been experienced w/ OSX 10.9. * </p> * <p> - * Be aware that the <i>master</i> {@link GLContext} and related resources - * <i>shall not</i> be destroyed before it's <i>slave</i> {@link GLContext} instances <i>while they are using them</i>.<br> - * Otherwise the OpenGL driver implementation may crash w/ SIGSEGV, since using already destroyed resources, - * e.g. OpenGL buffer objects, may not be validated by the driver!<br> + * In general, destroying a <i>master</i> {@link GLContext} before their shared <i>slaves</i> + * shall be permissible, i.e. the OpenGL driver needs to handle pending destruction of shared resources. + * This is confirmed to work properly on most platform/driver combinations, + * see unit test <code>com.jogamp.opengl.test.junit.jogl.acore.TestSharedContextVBOES2NEWT3</code> and similar. * </p> * <p> + * However, to avoid scenarios with buggy drivers, users <i>may not</i> destroy the + * <i>master</i> {@link GLContext} before its shared <i>slave</i> {@link GLContext} instances + * <i>as long as they are using them</i>.<br> + * Otherwise the OpenGL driver may crash w/ SIGSEGV, due to using already destroyed shared resources, + * if not handling the pending destruction of the latter!<br> * Either proper lifecycle synchronization is implemented, e.g. by notifying the <i>slaves</i> about the loss of the shared resources, * <i>or</i> the <i>slaves</i> validate whether the resources are still valid. * </p> diff --git a/src/jogl/classes/javax/media/opengl/Threading.java b/src/jogl/classes/javax/media/opengl/Threading.java index 6c64cbe31..c8d8d0071 100644 --- a/src/jogl/classes/javax/media/opengl/Threading.java +++ b/src/jogl/classes/javax/media/opengl/Threading.java @@ -117,10 +117,36 @@ import jogamp.opengl.ThreadingImpl; */ public class Threading { + public static enum Mode { + /** + * Full multithreaded OpenGL, + * i.e. any {@link Threading#invoke(boolean, Runnable, Object) invoke} + * {@link Threading#invokeOnOpenGLThread(boolean, Runnable) commands} + * will be issued on the current thread immediately. + */ + MT(0), + + /** Single-Threaded OpenGL on AWT EDT */ + ST_AWT(1), + + /** Single-Threaded OpenGL on dedicated worker thread. */ + ST_WORKER(2); + + public final int id; + + Mode(final int id){ + this.id = id; + } + } /** No reason to ever instantiate this class */ private Threading() {} + /** Returns the threading mode */ + public static Mode getMode() { + return ThreadingImpl.getMode(); + } + /** If an implementation of the javax.media.opengl APIs offers a multithreading option but the default behavior is single-threading, this API provides a mechanism for end users to disable single-threading @@ -150,10 +176,14 @@ public class Threading { return ThreadingImpl.isToolkitThread(); } - /** Indicates whether the current thread is the single thread on - which this implementation of the javax.media.opengl APIs - performs all of its OpenGL-related work. This method should only - be called if the single-thread model is in effect. */ + /** + * Indicates whether the current thread is capable of + * performing OpenGL-related work. + * <p> + * Method always returns <code>true</code> + * if {@link #getMode()} == {@link Mode#MT} or {@link #isSingleThreaded()} == <code>false</code>. + * </p> + */ public static final boolean isOpenGLThread() throws GLException { return ThreadingImpl.isOpenGLThread(); } @@ -173,7 +203,7 @@ public class Threading { } /** - * If {@link #isSingleThreaded()} <b>and</b> not {@link #isOpenGLThread()} + * If not {@link #isOpenGLThread()} * <b>and</b> the <code>lock</code> is not being hold by this thread, * invoke Runnable <code>r</code> on the OpenGL thread via {@link #invokeOnOpenGLThread(boolean, Runnable)}. * <p> @@ -186,7 +216,7 @@ public class Threading { * @throws GLException */ public static final void invoke(final boolean wait, final Runnable r, final Object lock) throws GLException { - if ( isSingleThreaded() && !isOpenGLThread() && + if ( !isOpenGLThread() && ( null == lock || !Thread.holdsLock(lock) ) ) { invokeOnOpenGLThread(wait, r); } else { diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index 2d5e12429..563158a72 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -156,10 +156,12 @@ import jogamp.opengl.awt.AWTTilePainter; * <li><pre>sun.awt.noerasebackground=true</pre></li> * </ul> * + * <p> * <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> * To share a {@link GLContext} see the following note in the documentation overview: - * <a href="../../../spec-overview.html#SHARING">context sharing</a> + * <a href="../../../../overview-summary.html#SHARING">context sharing</a> * as well as {@link GLSharedContextSetter}. + * </p> */ @SuppressWarnings("serial") @@ -300,6 +302,12 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } @Override + public final RecursiveLock getUpstreamLock() { return lock; } + + @Override + public final boolean isThreadGLCapable() { return Threading.isOpenGLThread(); } + + @Override public void setShallUseOffscreenLayer(final boolean v) { shallUseOffscreenLayer = v; } @@ -834,65 +842,74 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing private final Runnable setupPrintOnEDT = new Runnable() { @Override public void run() { - if( !validateGLDrawable() ) { - if(DEBUG) { - System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, drawable not valid yet"); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( !validateGLDrawable() ) { + if(DEBUG) { + System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, drawable not valid yet"); + } + printActive = false; + return; // not yet available .. } - printActive = false; - return; // not yet available .. - } - if( !isVisible() ) { - if(DEBUG) { - System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, canvas not visible"); + if( !isVisible() ) { + if(DEBUG) { + System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, canvas not visible"); + } + printActive = false; + return; // not yet available .. } - printActive = false; - return; // not yet available .. - } - sendReshape = false; // clear reshape flag - printAnimator = helper.getAnimator(); - if( null != printAnimator ) { - printAnimator.remove(GLCanvas.this); - } - printGLAD = GLCanvas.this; // _not_ default, shall be replaced by offscreen GLAD - final GLCapabilities caps = (GLCapabilities)getChosenGLCapabilities().cloneMutable(); - final int printNumSamples = printAWTTiles.getNumSamples(caps); - GLDrawable printDrawable = printGLAD.getDelegatedDrawable(); - final boolean reqNewGLADSamples = printNumSamples != caps.getNumSamples(); - final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() || - printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight(); - final boolean reqNewGLADOnscrn = caps.isOnscreen(); - // It is desired to use a new offscreen GLAD, however Bug 830 forbids this for AA onscreen context. - // Bug 830: swapGLContextAndAllGLEventListener and onscreen MSAA w/ NV/GLX - final boolean reqNewGLAD = !caps.getSampleBuffers() && ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize ); - if( DEBUG ) { - System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+"], "+ - ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+ - ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+ - ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+ - ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator); - } - if( reqNewGLAD ) { - caps.setDoubleBuffered(false); - caps.setOnscreen(false); - if( printNumSamples != caps.getNumSamples() ) { - caps.setSampleBuffers(0 < printNumSamples); - caps.setNumSamples(printNumSamples); + sendReshape = false; // clear reshape flag + printAnimator = helper.getAnimator(); + if( null != printAnimator ) { + printAnimator.remove(GLCanvas.this); } - final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); - printGLAD = factory.createOffscreenAutoDrawable(null, caps, null, - printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE, - printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE); - GLDrawableUtil.swapGLContextAndAllGLEventListener(GLCanvas.this, printGLAD); - printDrawable = printGLAD.getDelegatedDrawable(); - } - printAWTTiles.setGLOrientation(printGLAD.isGLOriented(), printGLAD.isGLOriented()); - printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0); - printAWTTiles.renderer.attachAutoDrawable(printGLAD); - if( DEBUG ) { - System.err.println("AWT print.setup "+printAWTTiles); - System.err.println("AWT print.setup AA "+printNumSamples+", "+caps); - System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD); - System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable); + printGLAD = GLCanvas.this; // _not_ default, shall be replaced by offscreen GLAD + final GLCapabilitiesImmutable gladCaps = getChosenGLCapabilities(); + final int printNumSamples = printAWTTiles.getNumSamples(gladCaps); + GLDrawable printDrawable = printGLAD.getDelegatedDrawable(); + final boolean reqNewGLADSamples = printNumSamples != gladCaps.getNumSamples(); + final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() || + printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight(); + final boolean reqNewGLADOnscrn = gladCaps.isOnscreen(); + + final GLCapabilities newGLADCaps = (GLCapabilities)gladCaps.cloneMutable(); + newGLADCaps.setDoubleBuffered(false); + newGLADCaps.setOnscreen(false); + if( printNumSamples != newGLADCaps.getNumSamples() ) { + newGLADCaps.setSampleBuffers(0 < printNumSamples); + newGLADCaps.setNumSamples(printNumSamples); + } + final boolean reqNewGLADSafe = GLDrawableUtil.isSwapGLContextSafe(getRequestedGLCapabilities(), gladCaps, newGLADCaps); + + final boolean reqNewGLAD = ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize ) && reqNewGLADSafe; + + if( DEBUG ) { + System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+", safe "+reqNewGLADSafe+"], "+ + ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+ + ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+ + ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+ + ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator); + } + if( reqNewGLAD ) { + final GLDrawableFactory factory = GLDrawableFactory.getFactory(newGLADCaps.getGLProfile()); + printGLAD = factory.createOffscreenAutoDrawable(null, newGLADCaps, null, + printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE, + printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE); + GLDrawableUtil.swapGLContextAndAllGLEventListener(GLCanvas.this, printGLAD); + printDrawable = printGLAD.getDelegatedDrawable(); + } + printAWTTiles.setGLOrientation(printGLAD.isGLOriented(), printGLAD.isGLOriented()); + printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0); + printAWTTiles.renderer.attachAutoDrawable(printGLAD); + if( DEBUG ) { + System.err.println("AWT print.setup "+printAWTTiles); + System.err.println("AWT print.setup AA "+printNumSamples+", "+newGLADCaps); + System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD); + System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable); + } + } finally { + _lock.unlock(); } } }; @@ -908,23 +925,29 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing private final Runnable releasePrintOnEDT = new Runnable() { @Override public void run() { - if( DEBUG ) { - System.err.println("AWT print.release "+printAWTTiles); - } - printAWTTiles.dispose(); - printAWTTiles= null; - if( printGLAD != GLCanvas.this ) { - GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLCanvas.this); - printGLAD.destroy(); - } - printGLAD = null; - if( null != printAnimator ) { - printAnimator.add(GLCanvas.this); - printAnimator = null; + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( DEBUG ) { + System.err.println("AWT print.release "+printAWTTiles); + } + printAWTTiles.dispose(); + printAWTTiles= null; + if( printGLAD != GLCanvas.this ) { + GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLCanvas.this); + printGLAD.destroy(); + } + printGLAD = null; + if( null != printAnimator ) { + printAnimator.add(GLCanvas.this); + printAnimator = null; + } + sendReshape = true; // trigger reshape, i.e. gl-viewport and -listener - this component might got resized! + printActive = false; + display(); + } finally { + _lock.unlock(); } - sendReshape = true; // trigger reshape, i.e. gl-viewport and -listener - this component might got resized! - printActive = false; - display(); } }; @@ -1040,16 +1063,21 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } @Override - public boolean invoke(final boolean wait, final GLRunnable glRunnable) { + public boolean invoke(final boolean wait, final GLRunnable glRunnable) throws IllegalStateException { return helper.invoke(this, wait, glRunnable); } @Override - public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) { + public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) throws IllegalStateException { return helper.invoke(this, wait, glRunnables); } @Override + public void flushGLRunnables() { + helper.flushGLRunnables(); + } + + @Override public GLContext setContext(final GLContext newCtx, final boolean destroyPrevCtx) { final RecursiveLock _lock = lock; _lock.lock(); @@ -1137,6 +1165,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing return (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities(); } + @Override public GLCapabilitiesImmutable getRequestedGLCapabilities() { if( null == awtConfig ) { return capsReqUser; @@ -1222,26 +1251,32 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing animatorPaused = false; } + GLException exceptionOnDisposeGL = null; + // OLS will be detached by disposeGL's context destruction below if( null != context ) { if( context.isCreated() ) { - // Catch dispose GLExceptions by GLEventListener, just 'print' them - // so we can continue with the destruction. try { helper.disposeGL(GLCanvas.this, context, true); if(DEBUG) { System.err.println(getThreadName()+": destroyOnEDTAction() - post ctx: "+context); } } catch (final GLException gle) { - gle.printStackTrace(); + exceptionOnDisposeGL = gle; } } context = null; } + + Throwable exceptionOnUnrealize = null; if( null != drawable ) { - drawable.setRealized(false); - if(DEBUG) { - System.err.println(getThreadName()+": destroyOnEDTAction() - post drawable: "+drawable); + try { + drawable.setRealized(false); + if(DEBUG) { + System.err.println(getThreadName()+": destroyOnEDTAction() - post drawable: "+drawable); + } + } catch( final Throwable re ) { + exceptionOnUnrealize = re; } drawable = null; } @@ -1250,6 +1285,14 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing animator.resume(); } + // throw exception in order of occurrence .. + if( null != exceptionOnDisposeGL ) { + throw exceptionOnDisposeGL; + } + if( null != exceptionOnUnrealize ) { + throw GLException.newGLException(exceptionOnUnrealize); + } + if(DEBUG) { System.err.println(getThreadName()+": dispose() - END, animator "+animator); } diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index 549b6e96f..5bc4c9a60 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -98,6 +98,8 @@ import jogamp.opengl.util.glsl.GLSLTextureRaster; import com.jogamp.common.util.PropertyAccess; import com.jogamp.common.util.awt.AWTEDTExecutor; +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.nativewindow.awt.AWTPrintLifecycle; import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol; import com.jogamp.opengl.FBObject; @@ -128,19 +130,19 @@ import com.jogamp.opengl.util.texture.TextureState; using {@link GLDrawableFactory#createOffscreenDrawable(AbstractGraphicsDevice, GLCapabilitiesImmutable, GLCapabilitiesChooser, int, int) GLDrawableFactory.createOffscreenDrawable(..)}.<br/> </p> <p> - <a name="verticalFlip"> - In case</a> the drawable {@link #isGLOriented()} and {@link #setSkipGLOrientationVerticalFlip(boolean) vertical flip is not skipped}, - this component performs the required vertical flip to bring the content from OpenGL's orientation into AWT's orientation. + <a name="verticalFlip">A vertical-flip is required</a>, if the drawable {@link #isGLOriented()} and {@link #setSkipGLOrientationVerticalFlip(boolean) vertical flip is not skipped}.<br> + In this case this component performs the required vertical flip to bring the content from OpenGL's orientation into AWT's orientation.<br> + In case <a href="#fboGLSLVerticalFlip">GLSL based vertical-flip</a> is not available, + the CPU intensive {@link System#arraycopy(Object, int, Object, int, int) System.arraycopy(..)} is used line by line. See details about <a href="#fboGLSLVerticalFlip">FBO and GLSL vertical flipping</a>. </p> <p> - The OpenGL path is concluded by copying the rendered pixels an {@link BufferedImage} via {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer) glReadPixels(..)} - for later Java2D composition. + For performance reasons, as well as for <a href="#bug842">GL state sideeffects</a>, + <b>{@link #setSkipGLOrientationVerticalFlip(boolean) skipping vertical flip} is highly recommended</b>! </p> <p> - In case {@link #setSkipGLOrientationVerticalFlip(boolean) vertical-flip is not skipped} and <a href="#fboGLSLVerticalFlip">GLSL based vertical-flip</a> is not performed, - {@link System#arraycopy(Object, int, Object, int, int) System.arraycopy(..)} is used line by line. - This step causes more CPU load per frame and is not hardware-accelerated. + The OpenGL path is concluded by copying the rendered pixels an {@link BufferedImage} via {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer) glReadPixels(..)} + for later Java2D composition. </p> <p> Finally the Java2D compositioning takes place via via {@link Graphics#drawImage(java.awt.Image, int, int, int, int, java.awt.image.ImageObserver) Graphics.drawImage(...)} @@ -151,7 +153,8 @@ import com.jogamp.opengl.util.texture.TextureState; * </P> * <a name="fboGLSLVerticalFlip"><h5>FBO / GLSL Vertical Flip</h5></a> - In case FBO is used and GLSL is available and {@link #setSkipGLOrientationVerticalFlip(boolean) vertical flip is not skipped}, a fragment shader is utilized + If <a href="#verticalFlip">vertical flip is required</a>, + FBO is used, GLSL is available and {@link #setSkipGLOrientationVerticalFlip(boolean) vertical flip is not skipped}, a fragment shader is utilized to flip the FBO texture vertically. This hardware-accelerated step can be disabled via system property <code>jogl.gljpanel.noglsl</code>. <p> The FBO / GLSL code path uses one texture-unit and binds the FBO texture to it's active texture-target, @@ -165,20 +168,23 @@ import com.jogamp.opengl.util.texture.TextureState; The current gl-viewport is preserved. </p> <p> - <i>Warning (Bug 842)</i>: Certain GL states other than viewport and texture (see above) + <a name="bug842"><i>Warning (Bug 842)</i></a>: Certain GL states other than viewport and texture (see above) influencing rendering, will also influence the GLSL vertical flip, e.g. {@link GL#glFrontFace(int) glFrontFace}({@link GL#GL_CCW}). It is recommended to reset those states to default when leaving the {@link GLEventListener#display(GLAutoDrawable)} method! We may change this behavior in the future, i.e. preserve all influencing states. </p> + <p> <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> To share a {@link GLContext} see the following note in the documentation overview: - <a href="../../../spec-overview.html#SHARING">context sharing</a> + <a href="../../../../overview-summary.html#SHARING">context sharing</a> as well as {@link GLSharedContextSetter}. + </p> */ @SuppressWarnings("serial") public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosingProtocol, AWTPrintLifecycle, GLSharedContextSetter, ScalableSurface { private static final boolean DEBUG; + private static final boolean DEBUG_FRAMES; private static final boolean DEBUG_VIEWPORT; private static final boolean USE_GLSL_TEXTURE_RASTERIZER; private static final boolean SKIP_VERTICAL_FLIP_DEFAULT; @@ -195,6 +201,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing static { Debug.initSingleton(); DEBUG = Debug.debug("GLJPanel"); + DEBUG_FRAMES = PropertyAccess.isPropertyDefined("jogl.debug.GLJPanel.Frames", true); DEBUG_VIEWPORT = PropertyAccess.isPropertyDefined("jogl.debug.GLJPanel.Viewport", true); USE_GLSL_TEXTURE_RASTERIZER = !PropertyAccess.isPropertyDefined("jogl.gljpanel.noglsl", true); SKIP_VERTICAL_FLIP_DEFAULT = PropertyAccess.isPropertyDefined("jogl.gljpanel.noverticalflip", true); @@ -232,6 +239,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing return singleAWTGLPixelBufferProvider; } + private final RecursiveLock lock = LockFactory.createRecursiveLock(); + private final GLDrawableHelper helper; private boolean autoSwapBufferMode; @@ -241,10 +250,9 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // Data used for either pbuffers or pixmap-based offscreen surfaces // private AWTGLPixelBufferProvider customPixelBufferProvider = null; - /** Single buffered offscreen caps */ - private GLCapabilitiesImmutable offscreenCaps; - private final GLProfile glProfile; - private final GLDrawableFactoryImpl factory; + /** Requested single buffered offscreen caps */ + private volatile GLCapabilitiesImmutable reqOffscreenCaps; + private volatile GLDrawableFactoryImpl factory; private final GLCapabilitiesChooser chooser; private int additionalCtxCreationFlags = 0; @@ -349,10 +357,9 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing caps = new GLCapabilities(GLProfile.getDefault(GLProfile.getDefaultDevice())); } caps.setDoubleBuffered(false); - offscreenCaps = caps; + reqOffscreenCaps = caps; } - this.glProfile = offscreenCaps.getGLProfile(); - this.factory = GLDrawableFactoryImpl.getFactoryImpl(glProfile); + this.factory = GLDrawableFactoryImpl.getFactoryImpl( reqOffscreenCaps.getGLProfile() ); // pre-fetch, reqOffscreenCaps may changed this.chooser = chooser; helper = new GLDrawableHelper(); @@ -432,6 +439,12 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } @Override + public final RecursiveLock getUpstreamLock() { return lock; } + + @Override + public final boolean isThreadGLCapable() { return EventQueue.isDispatchThread(); } + + @Override public void display() { if( isShowing || ( printActive && isVisible() ) ) { if (EventQueue.isDispatchThread()) { @@ -449,17 +462,19 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } } - protected void dispose() { + protected void dispose(final Runnable post) { if(DEBUG) { System.err.println(getThreadName()+": GLJPanel.dispose() - start"); // Thread.dumpStack(); } if (backend != null && backend.getContext() != null) { - boolean animatorPaused = false; + final boolean animatorPaused; final GLAnimatorControl animator = getAnimator(); if(null!=animator) { animatorPaused = animator.pause(); + } else { + animatorPaused = false; } if(backend.getContext().isCreated()) { @@ -470,15 +485,14 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing backend.destroy(); isInitialized = false; } + if( null != post ) { + post.run(); + } - if(animatorPaused) { + if( animatorPaused ) { animator.resume(); } } - hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; - hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; - nativePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; - nativePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; if(DEBUG) { System.err.println(getThreadName()+": GLJPanel.dispose() - stop"); @@ -522,28 +536,35 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing return; } - if( !isInitialized ) { - initializeBackendImpl(); - } - - if (!isInitialized || printActive) { - return; - } + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( !isInitialized ) { + handleReshape = false; + initializeBackendImpl(); + } - // NOTE: must do this when the context is not current as it may - // involve destroying the pbuffer (current context) and - // re-creating it -- tricky to do properly while the context is - // current - if( !printActive ) { - if (handleReshape) { - handleReshape = false; - sendReshape = handleReshape(); + if (!isInitialized || printActive) { + return; } - if( isShowing ) { - updater.setGraphics(g); - backend.doPaintComponent(g); + // NOTE: must do this when the context is not current as it may + // involve destroying the pbuffer (current context) and + // re-creating it -- tricky to do properly while the context is + // current + if( !printActive ) { + if ( handleReshape ) { + handleReshape = false; + sendReshape = handleReshape(); + } + + if( isShowing ) { + updater.setGraphics(g); + backend.doPaintComponent(g); + } } + } finally { + _lock.unlock(); } } @@ -622,7 +643,12 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing public void removeNotify() { awtWindowClosingProtocol.removeClosingListener(); - dispose(); + dispose(null); + hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + nativePixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + nativePixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + super.removeNotify(); } @@ -643,17 +669,16 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private void reshapeImpl(final int width, final int height) { final int scaledWidth = width * hasPixelScale[0]; final int scaledHeight = height * hasPixelScale[1]; + if( !printActive && ( scaledWidth != panelWidth || scaledHeight != panelHeight ) ) { + reshapeWidth = scaledWidth; + reshapeHeight = scaledHeight; + handleReshape = true; + } if( DEBUG ) { System.err.println(getThreadName()+": GLJPanel.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+ " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+getPixelScaleStr()+ ", panel "+panelWidth+"x"+panelHeight + - ", reshape: " +reshapeWidth+"x"+reshapeHeight + - "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+getPixelScaleStr()+" -> "+scaledWidth+"x"+scaledHeight); - } - if( !printActive ) { - reshapeWidth = scaledWidth; - reshapeHeight = scaledHeight; - handleReshape = true; + "] -> "+(handleReshape?"":"[skipped] ") + width+"x"+height+" * "+getPixelScaleStr()+" -> "+scaledWidth+"x"+scaledHeight); } } @@ -673,65 +698,78 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private final Runnable setupPrintOnEDT = new Runnable() { @Override public void run() { - if( !isInitialized ) { - initializeBackendImpl(); - } - if (!isInitialized) { - if(DEBUG) { - System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, drawable not valid yet"); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( !isInitialized ) { + initializeBackendImpl(); } - printActive = false; - return; // not yet available .. - } - if( !isVisible() ) { - if(DEBUG) { - System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, panel not visible"); + if (!isInitialized) { + if(DEBUG) { + System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, drawable not valid yet"); + } + printActive = false; + return; // not yet available .. + } + if( !isVisible() ) { + if(DEBUG) { + System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, panel not visible"); + } + printActive = false; + return; // not yet available .. + } + sendReshape = false; // clear reshape flag + handleReshape = false; // ditto + printAnimator = helper.getAnimator(); + if( null != printAnimator ) { + printAnimator.remove(GLJPanel.this); } - printActive = false; - return; // not yet available .. - } - sendReshape = false; // clear reshape flag - handleReshape = false; // ditto - printAnimator = helper.getAnimator(); - if( null != printAnimator ) { - printAnimator.remove(GLJPanel.this); - } - printGLAD = GLJPanel.this; // default: re-use - final GLCapabilities caps = (GLCapabilities)getChosenGLCapabilities().cloneMutable(); - final int printNumSamples = printAWTTiles.getNumSamples(caps); - GLDrawable printDrawable = printGLAD.getDelegatedDrawable(); - final boolean reqNewGLADSamples = printNumSamples != caps.getNumSamples(); - final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() || - printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight(); - final boolean reqNewGLAD = reqNewGLADSamples || reqNewGLADSize ; - if( DEBUG ) { - System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ samples "+reqNewGLADSamples+", size "+reqNewGLADSize+"], "+ - ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+ - ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+ - ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+ - ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator); - } - if( reqNewGLAD ) { - caps.setDoubleBuffered(false); - caps.setOnscreen(false); - caps.setSampleBuffers(0 < printNumSamples); - caps.setNumSamples(printNumSamples); - final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); - printGLAD = factory.createOffscreenAutoDrawable(null, caps, null, - printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE, - printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE); - GLDrawableUtil.swapGLContextAndAllGLEventListener(GLJPanel.this, printGLAD); - printDrawable = printGLAD.getDelegatedDrawable(); - } - printAWTTiles.setGLOrientation( !GLJPanel.this.skipGLOrientationVerticalFlip && printGLAD.isGLOriented(), printGLAD.isGLOriented() ); - printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0); - printAWTTiles.renderer.attachAutoDrawable(printGLAD); - if( DEBUG ) { - System.err.println("AWT print.setup "+printAWTTiles); - System.err.println("AWT print.setup AA "+printNumSamples+", "+caps); - System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD); - System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable); + printGLAD = GLJPanel.this; // default: re-use + final GLCapabilitiesImmutable gladCaps = getChosenGLCapabilities(); + final int printNumSamples = printAWTTiles.getNumSamples(gladCaps); + GLDrawable printDrawable = printGLAD.getDelegatedDrawable(); + final boolean reqNewGLADSamples = printNumSamples != gladCaps.getNumSamples(); + final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() || + printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight(); + + final GLCapabilities newGLADCaps = (GLCapabilities)gladCaps.cloneMutable(); + newGLADCaps.setDoubleBuffered(false); + newGLADCaps.setOnscreen(false); + if( printNumSamples != newGLADCaps.getNumSamples() ) { + newGLADCaps.setSampleBuffers(0 < printNumSamples); + newGLADCaps.setNumSamples(printNumSamples); + } + final boolean reqNewGLADSafe = GLDrawableUtil.isSwapGLContextSafe(getRequestedGLCapabilities(), gladCaps, newGLADCaps); + + final boolean reqNewGLAD = ( reqNewGLADSamples || reqNewGLADSize ) && reqNewGLADSafe; + + if( DEBUG ) { + System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ samples "+reqNewGLADSamples+", size "+reqNewGLADSize+", safe "+reqNewGLADSafe+"], "+ + ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+ + ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+ + ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+ + ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator); + } + if( reqNewGLAD ) { + final GLDrawableFactory factory = GLDrawableFactory.getFactory(newGLADCaps.getGLProfile()); + printGLAD = factory.createOffscreenAutoDrawable(null, newGLADCaps, null, + printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE, + printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE); + GLDrawableUtil.swapGLContextAndAllGLEventListener(GLJPanel.this, printGLAD); + printDrawable = printGLAD.getDelegatedDrawable(); + } + printAWTTiles.setGLOrientation( !GLJPanel.this.skipGLOrientationVerticalFlip && printGLAD.isGLOriented(), printGLAD.isGLOriented() ); + printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0); + printAWTTiles.renderer.attachAutoDrawable(printGLAD); + if( DEBUG ) { + System.err.println("AWT print.setup "+printAWTTiles); + System.err.println("AWT print.setup AA "+printNumSamples+", "+newGLADCaps); + System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD); + System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable); + } + } finally { + _lock.unlock(); } } }; @@ -749,43 +787,49 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private final Runnable releasePrintOnEDT = new Runnable() { @Override public void run() { - if( DEBUG ) { - System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0 "+printAWTTiles); - } - printAWTTiles.dispose(); - printAWTTiles= null; - if( printGLAD != GLJPanel.this ) { - GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLJPanel.this); - printGLAD.destroy(); - } - printGLAD = null; - if( null != printAnimator ) { - printAnimator.add(GLJPanel.this); - printAnimator = null; - } + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( DEBUG ) { + System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0 "+printAWTTiles); + } + printAWTTiles.dispose(); + printAWTTiles= null; + if( printGLAD != GLJPanel.this ) { + GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLJPanel.this); + printGLAD.destroy(); + } + printGLAD = null; + if( null != printAnimator ) { + printAnimator.add(GLJPanel.this); + printAnimator = null; + } - // trigger reshape, i.e. gl-viewport and -listener - this component might got resized! - final int awtWidth = GLJPanel.this.getWidth(); - final int awtHeight= GLJPanel.this.getHeight(); - final int scaledAWTWidth = awtWidth * hasPixelScale[0]; - final int scaledAWTHeight= awtHeight * hasPixelScale[1]; - final GLDrawable drawable = GLJPanel.this.getDelegatedDrawable(); - if( scaledAWTWidth != panelWidth || scaledAWTHeight != panelHeight || - drawable.getSurfaceWidth() != panelWidth || drawable.getSurfaceHeight() != panelHeight ) { - // -> !( awtSize == panelSize == drawableSize ) - if ( DEBUG ) { - System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0: resizeWithinPrint panel " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScaleStr()+ - ", draw "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight()+ - " -> " + awtWidth+"x"+awtHeight+" * "+getPixelScaleStr()+" -> "+scaledAWTWidth+"x"+scaledAWTHeight); + // trigger reshape, i.e. gl-viewport and -listener - this component might got resized! + final int awtWidth = GLJPanel.this.getWidth(); + final int awtHeight= GLJPanel.this.getHeight(); + final int scaledAWTWidth = awtWidth * hasPixelScale[0]; + final int scaledAWTHeight= awtHeight * hasPixelScale[1]; + final GLDrawable drawable = GLJPanel.this.getDelegatedDrawable(); + if( scaledAWTWidth != panelWidth || scaledAWTHeight != panelHeight || + drawable.getSurfaceWidth() != panelWidth || drawable.getSurfaceHeight() != panelHeight ) { + // -> !( awtSize == panelSize == drawableSize ) + if ( DEBUG ) { + System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0: resizeWithinPrint panel " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScaleStr()+ + ", draw "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight()+ + " -> " + awtWidth+"x"+awtHeight+" * "+getPixelScaleStr()+" -> "+scaledAWTWidth+"x"+scaledAWTHeight); + } + reshapeWidth = scaledAWTWidth; + reshapeHeight = scaledAWTHeight; + sendReshape = handleReshape(); // reshapeSize -> panelSize, backend reshape w/ GL reshape + } else { + sendReshape = true; // only GL reshape } - reshapeWidth = scaledAWTWidth; - reshapeHeight = scaledAWTHeight; - sendReshape = handleReshape(); // reshapeSize -> panelSize, backend reshape w/ GL reshape - } else { - sendReshape = true; // only GL reshape + printActive = false; + display(); + } finally { + _lock.unlock(); } - printActive = false; - display(); } }; @@ -927,22 +971,33 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } @Override - public boolean invoke(final boolean wait, final GLRunnable glRunnable) { + public boolean invoke(final boolean wait, final GLRunnable glRunnable) throws IllegalStateException { return helper.invoke(this, wait, glRunnable); } @Override - public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) { + public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) throws IllegalStateException { return helper.invoke(this, wait, glRunnables); } @Override + public void flushGLRunnables() { + helper.flushGLRunnables(); + } + + @Override public GLContext createContext(final GLContext shareWith) { - final Backend b = backend; - if ( null == b ) { - return null; + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final Backend b = backend; + if ( null == b ) { + return null; + } + return b.createContext(shareWith); + } finally { + _lock.unlock(); } - return b.createContext(shareWith); } @Override @@ -956,14 +1011,20 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public GLContext setContext(final GLContext newCtx, final boolean destroyPrevCtx) { - final Backend b = backend; - if ( null == b ) { - return null; + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final Backend b = backend; + if ( null == b ) { + return null; + } + final GLContext oldCtx = b.getContext(); + GLDrawableHelper.switchContext(b.getDrawable(), oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); + b.setContext(newCtx); + return oldCtx; + } finally { + _lock.unlock(); } - final GLContext oldCtx = b.getContext(); - GLDrawableHelper.switchContext(b.getDrawable(), oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); - b.setContext(newCtx); - return oldCtx; } @@ -1062,12 +1123,12 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public int getSurfaceWidth() { - return panelWidth; // FIXME HiDPI: Accurate or: getWidth() * hasPixelScale[0]; + return panelWidth; // scaled surface width in pixel units, current as-from reshape } @Override public int getSurfaceHeight() { - return panelHeight; // FIXME HiDPI: Accurate or: getHeight() * hasPixelScale[1]; + return panelHeight; // scaled surface height in pixel units, current as-from reshape } /** @@ -1089,13 +1150,13 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } /** - * Set skipping {@link #isGLOriented()} based vertical flip, + * Skip {@link #isGLOriented()} based vertical flip, * which usually is required by the offscreen backend, * see details about <a href="#verticalFlip">vertical flip</a> * and <a href="#fboGLSLVerticalFlip">FBO / GLSL vertical flip</a>. * <p> * If set to <code>true</code>, user needs to flip the OpenGL rendered scene - * <i>if {@link #isGLOriented()} == true</i>, e.g. via the PMV matrix.<br/> + * <i>if {@link #isGLOriented()} == true</i>, e.g. via the projection matrix.<br/> * See constraints of {@link #isGLOriented()}. * </p> */ @@ -1117,8 +1178,45 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } @Override + public final GLCapabilitiesImmutable getRequestedGLCapabilities() { + return reqOffscreenCaps; + } + + /** + * Set a new requested {@link GLCapabilitiesImmutable} for this GLJPanel + * allowing reconfiguration. + * <p> + * Method shall be invoked from the {@link #isThreadGLCapable() AWT-EDT thread}. + * In case it is not invoked on the AWT-EDT thread, an attempt is made to do so. + * </p> + * <p> + * Method will dispose a previous {@link #isRealized() realized} GLContext and offscreen backend! + * </p> + * @param caps new capabilities. + */ + public final void setRequestedGLCapabilities(final GLCapabilitiesImmutable caps) { + if( null == caps ) { + throw new IllegalArgumentException("null caps"); + } + Threading.invoke(true, + new Runnable() { + @Override + public void run() { + dispose( new Runnable() { + @Override + public void run() { + // switch to new caps and re-init backend + // after actual dispose, but before resume animator + reqOffscreenCaps = caps; + initializeBackendImpl(); + } } ); + } + }, getTreeLock()); + } + + @Override public final GLProfile getGLProfile() { - return glProfile; + return reqOffscreenCaps.getGLProfile(); } @Override @@ -1209,12 +1307,13 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing if ( oglPipelineUsable() ) { backend = new J2DOGLBackend(); } else { - backend = new OffscreenBackend(glProfile, customPixelBufferProvider); + backend = new OffscreenBackend(customPixelBufferProvider); } isInitialized = false; } if (!isInitialized) { + this.factory = GLDrawableFactoryImpl.getFactoryImpl( reqOffscreenCaps.getGLProfile() ); // reqOffscreenCaps may have changed backend.initialize(); } return isInitialized; @@ -1309,23 +1408,42 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private final Runnable disposeAction = new Runnable() { @Override public void run() { - if ( null != backend ) { - final GLContext _context = backend.getContext(); - final boolean backendDestroy = !backend.isUsingOwnLifecycle(); - if( null != _context && _context.isCreated() ) { - // Catch dispose GLExceptions by GLEventListener, just 'print' them - // so we can continue with the destruction. - try { - helper.disposeGL(GLJPanel.this, _context, !backendDestroy); - } catch (final GLException gle) { - gle.printStackTrace(); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if ( null != backend ) { + final GLContext _context = backend.getContext(); + final boolean backendDestroy = !backend.isUsingOwnLifecycle(); + + GLException exceptionOnDisposeGL = null; + if( null != _context && _context.isCreated() ) { + try { + helper.disposeGL(GLJPanel.this, _context, !backendDestroy); + } catch (final GLException gle) { + exceptionOnDisposeGL = gle; + } + } + Throwable exceptionBackendDestroy = null; + if ( backendDestroy ) { + try { + backend.destroy(); + } catch( final Throwable re ) { + exceptionBackendDestroy = re; + } + backend = null; + isInitialized = false; + } + + // throw exception in order of occurrence .. + if( null != exceptionOnDisposeGL ) { + throw exceptionOnDisposeGL; + } + if( null != exceptionBackendDestroy ) { + throw GLException.newGLException(exceptionBackendDestroy); } } - if ( backendDestroy ) { - backend.destroy(); - backend = null; - isInitialized = false; - } + } finally { + _lock.unlock(); } } }; @@ -1483,7 +1601,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing protected IntBuffer readBackIntsForCPUVFlip; // Implementation using software rendering - private volatile GLDrawableImpl offscreenDrawable; // volatile: avoid locking for read-only access + private volatile GLDrawable offscreenDrawable; // volatile: avoid locking for read-only access private boolean offscreenIsFBO; private FBObject fboFlipped; private GLSLTextureRaster glslTextureRaster; @@ -1495,7 +1613,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // For saving/restoring of OpenGL state during ReadPixels private final GLPixelStorageModes psm = new GLPixelStorageModes(); - OffscreenBackend(final GLProfile glp, final AWTGLPixelBufferProvider custom) { + OffscreenBackend(final AWTGLPixelBufferProvider custom) { if(null == custom) { pixelBufferProvider = getSingleAWTGLPixelBufferProvider(); } else { @@ -1516,19 +1634,20 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing if(DEBUG) { System.err.println(getThreadName()+": OffscreenBackend: initialize() - frameCount "+frameCount); } + GLException glException = null; try { final GLContext[] shareWith = { null }; if( helper.isSharedGLContextPending(shareWith) ) { return; // pending .. } - offscreenDrawable = (GLDrawableImpl) factory.createOffscreenDrawable( + offscreenDrawable = factory.createOffscreenDrawable( null /* default platform device */, - offscreenCaps, + reqOffscreenCaps, chooser, panelWidth, panelHeight); updateWrappedSurfaceScale(offscreenDrawable); offscreenDrawable.setRealized(true); - if( DEBUG ) { + if( DEBUG_FRAMES ) { offscreenDrawable.getNativeSurface().addSurfaceUpdatedListener(new SurfaceUpdatedListener() { @Override public final void surfaceUpdated(final Object updater, final NativeSurface ns, final long when) { @@ -1536,6 +1655,17 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } } ); } + // + // Pre context configuration + // + flipVertical = !GLJPanel.this.skipGLOrientationVerticalFlip && offscreenDrawable.isGLOriented(); + offscreenIsFBO = offscreenDrawable.getRequestedGLCapabilities().isFBO(); + final boolean useGLSLFlip_pre = flipVertical && offscreenIsFBO && reqOffscreenCaps.getGLProfile().isGL2ES2() && USE_GLSL_TEXTURE_RASTERIZER; + if( offscreenIsFBO && !useGLSLFlip_pre ) { + // Texture attachment only required for GLSL vertical flip, hence simply use a color-renderbuffer attachment. + ((GLFBODrawable)offscreenDrawable).setFBOMode(0); + } + offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith[0]); offscreenContext.setContextCreationFlags(additionalCtxCreationFlags); if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) { @@ -1543,28 +1673,32 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing helper.setAutoSwapBufferMode(false); // we handle swap-buffers, see handlesSwapBuffer() final GL gl = offscreenContext.getGL(); - flipVertical = !GLJPanel.this.skipGLOrientationVerticalFlip && offscreenDrawable.isGLOriented(); + // Remedy for Bug 1020, i.e. OSX/Nvidia's FBO needs to be cleared before blitting, + // otherwise first MSAA frame lacks antialiasing. + // Clearing of FBO is performed within GLFBODrawableImpl.initialize(..): + // gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + final GLCapabilitiesImmutable chosenCaps = offscreenDrawable.getChosenGLCapabilities(); - offscreenIsFBO = chosenCaps.isFBO(); final boolean glslCompliant = !offscreenContext.hasRendererQuirk(GLRendererQuirks.GLSLNonCompliant); - final boolean useGLSLFlip = flipVertical && offscreenIsFBO && gl.isGL2ES2() && USE_GLSL_TEXTURE_RASTERIZER && glslCompliant; + final boolean useGLSLFlip = useGLSLFlip_pre && gl.isGL2ES2() && glslCompliant; if( DEBUG ) { System.err.println(getThreadName()+": OffscreenBackend.initialize: useGLSLFlip "+useGLSLFlip+ " [flip "+flipVertical+", isFBO "+offscreenIsFBO+", isGL2ES2 "+gl.isGL2ES2()+ ", noglsl "+!USE_GLSL_TEXTURE_RASTERIZER+", glslNonCompliant "+!glslCompliant+ - ", isGL2ES2 " + gl.isGL2ES2()+"]"); + ", isGL2ES2 " + gl.isGL2ES2()+"\n "+offscreenDrawable+"]"); } if( useGLSLFlip ) { final GLFBODrawable fboDrawable = (GLFBODrawable) offscreenDrawable; fboDrawable.setTextureUnit( GLJPanel.this.requestedTextureUnit ); try { fboFlipped = new FBObject(); - fboFlipped.reset(gl, fboDrawable.getSurfaceWidth(), fboDrawable.getSurfaceHeight(), 0, false); - fboFlipped.attachTexture2D(gl, 0, chosenCaps.getAlphaBits()>0); + fboFlipped.init(gl, panelWidth, panelHeight, 0); + fboFlipped.attachColorbuffer(gl, 0, chosenCaps.getAlphaBits()>0); // fboFlipped.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + gl.glClear(GL.GL_COLOR_BUFFER_BIT); // Bug 1020 (see above), cannot do in FBObject due to unknown 'first bind' state. glslTextureRaster = new GLSLTextureRaster(fboDrawable.getTextureUnit(), true); glslTextureRaster.init(gl.getGL2ES2()); - glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, fboDrawable.getSurfaceWidth(), fboDrawable.getSurfaceHeight()); + glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, panelWidth, panelHeight); } catch (final Exception ex) { ex.printStackTrace(); if(null != glslTextureRaster) { @@ -1584,6 +1718,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } else { isInitialized = false; } + } catch( final GLException gle ) { + glException = gle; } finally { if( !isInitialized ) { if(null != offscreenContext) { @@ -1595,6 +1731,9 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing offscreenDrawable = null; } } + if( null != glException ) { + throw new GLException("Caught GLException: "+glException.getMessage(), glException); + } } } @@ -1677,7 +1816,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public final void postGL(final Graphics g, final boolean isDisplay) { if (isDisplay) { - if(DEBUG) { + if( DEBUG_FRAMES ) { System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: - frameCount "+frameCount); } @@ -1772,15 +1911,15 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } // Must now copy pixels from offscreen context into surface - if(DEBUG) { + if( DEBUG_FRAMES ) { System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.readPixels: - frameCount "+frameCount); } // Save PACK modes, reset them to defaults and set alignment psm.setPackAlignment(gl, alignment); - if(gl.isGL2ES3()) { + if( gl.isGL2ES3() ) { final GL2ES3 gl2es3 = gl.getGL2ES3(); - gl2es3.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, panelWidth); + psm.setPackRowLength(gl2es3, panelWidth); gl2es3.glReadBuffer(gl2es3.getDefaultReadBuffer()); } @@ -1789,19 +1928,19 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing final int[] usrViewport = new int[] { 0, 0, 0, 0 }; gl.glGetIntegerv(GL.GL_VIEWPORT, usrViewport, 0); viewportChange = 0 != usrViewport[0] || 0 != usrViewport[1] || - offscreenDrawable.getSurfaceWidth() != usrViewport[2] || offscreenDrawable.getSurfaceHeight() != usrViewport[3]; + panelWidth != usrViewport[2] || panelHeight != usrViewport[3]; if( DEBUG_VIEWPORT ) { System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL: "+GLJPanel.this.getName()+" Viewport: change "+viewportChange+ ", "+usrViewport[0]+"/"+usrViewport[1]+" "+usrViewport[2]+"x"+usrViewport[3]+ - " -> 0/0 "+offscreenDrawable.getSurfaceWidth()+"x"+offscreenDrawable.getSurfaceHeight()); + " -> 0/0 "+panelWidth+"x"+panelHeight); } if( viewportChange ) { - gl.glViewport(0, 0, offscreenDrawable.getSurfaceWidth(), offscreenDrawable.getSurfaceHeight()); + gl.glViewport(0, 0, panelWidth, panelHeight); } // perform vert-flipping via OpenGL/FBO final GLFBODrawable fboDrawable = (GLFBODrawable)offscreenDrawable; - final FBObject.TextureAttachment fboTex = fboDrawable.getTextureBuffer(GL.GL_FRONT); + final FBObject.TextureAttachment fboTex = fboDrawable.getColorbuffer(GL.GL_FRONT).getTextureAttachment(); fboFlipped.bind(gl); @@ -1863,7 +2002,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing helper.invokeGL(offscreenDrawable, offscreenContext, updaterDisplayAction, updaterInitAction); if ( null != alignedImage ) { - if( DEBUG ) { + if( DEBUG_FRAMES ) { System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.doPaintComponent.drawImage: - frameCount "+frameCount); } // Draw resulting image in one shot @@ -1879,7 +2018,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public final boolean handleReshape() { - GLDrawableImpl _drawable = offscreenDrawable; + GLDrawableImpl _drawable = (GLDrawableImpl)offscreenDrawable; { final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, offscreenContext, panelWidth, panelHeight); if(_drawable != _drawableNew) { @@ -1899,8 +2038,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) { try { final GL gl = offscreenContext.getGL(); - fboFlipped.reset(gl, _drawable.getSurfaceWidth(), _drawable.getSurfaceHeight(), 0, false); - glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, _drawable.getSurfaceWidth(), _drawable.getSurfaceHeight()); + fboFlipped.reset(gl, panelWidth, panelHeight, 0); + glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, panelWidth, panelHeight); } finally { offscreenContext.release(); } @@ -2297,29 +2436,29 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing j2dContext.makeCurrent(); final GL gl = j2dContext.getGL(); - if ((getGLInteger(gl, GL.GL_RED_BITS) < offscreenCaps.getRedBits()) || - (getGLInteger(gl, GL.GL_GREEN_BITS) < offscreenCaps.getGreenBits()) || - (getGLInteger(gl, GL.GL_BLUE_BITS) < offscreenCaps.getBlueBits()) || + if ((getGLInteger(gl, GL.GL_RED_BITS) < reqOffscreenCaps.getRedBits()) || + (getGLInteger(gl, GL.GL_GREEN_BITS) < reqOffscreenCaps.getGreenBits()) || + (getGLInteger(gl, GL.GL_BLUE_BITS) < reqOffscreenCaps.getBlueBits()) || // (getGLInteger(gl, GL.GL_ALPHA_BITS) < offscreenCaps.getAlphaBits()) || - (getGLInteger(gl, GL2.GL_ACCUM_RED_BITS) < offscreenCaps.getAccumRedBits()) || - (getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) < offscreenCaps.getAccumGreenBits()) || - (getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS) < offscreenCaps.getAccumBlueBits()) || - (getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) < offscreenCaps.getAccumAlphaBits()) || + (getGLInteger(gl, GL2.GL_ACCUM_RED_BITS) < reqOffscreenCaps.getAccumRedBits()) || + (getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) < reqOffscreenCaps.getAccumGreenBits()) || + (getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS) < reqOffscreenCaps.getAccumBlueBits()) || + (getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) < reqOffscreenCaps.getAccumAlphaBits()) || // (getGLInteger(gl, GL2.GL_DEPTH_BITS) < offscreenCaps.getDepthBits()) || - (getGLInteger(gl, GL.GL_STENCIL_BITS) < offscreenCaps.getStencilBits())) { + (getGLInteger(gl, GL.GL_STENCIL_BITS) < reqOffscreenCaps.getStencilBits())) { if (DEBUG) { System.err.println(getThreadName()+": GLJPanel: Falling back to pbuffer-based support because Java2D context insufficient"); System.err.println(" Available Required"); - System.err.println("GL_RED_BITS " + getGLInteger(gl, GL.GL_RED_BITS) + " " + offscreenCaps.getRedBits()); - System.err.println("GL_GREEN_BITS " + getGLInteger(gl, GL.GL_GREEN_BITS) + " " + offscreenCaps.getGreenBits()); - System.err.println("GL_BLUE_BITS " + getGLInteger(gl, GL.GL_BLUE_BITS) + " " + offscreenCaps.getBlueBits()); - System.err.println("GL_ALPHA_BITS " + getGLInteger(gl, GL.GL_ALPHA_BITS) + " " + offscreenCaps.getAlphaBits()); - System.err.println("GL_ACCUM_RED_BITS " + getGLInteger(gl, GL2.GL_ACCUM_RED_BITS) + " " + offscreenCaps.getAccumRedBits()); - System.err.println("GL_ACCUM_GREEN_BITS " + getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) + " " + offscreenCaps.getAccumGreenBits()); - System.err.println("GL_ACCUM_BLUE_BITS " + getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS) + " " + offscreenCaps.getAccumBlueBits()); - System.err.println("GL_ACCUM_ALPHA_BITS " + getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) + " " + offscreenCaps.getAccumAlphaBits()); - System.err.println("GL_DEPTH_BITS " + getGLInteger(gl, GL.GL_DEPTH_BITS) + " " + offscreenCaps.getDepthBits()); - System.err.println("GL_STENCIL_BITS " + getGLInteger(gl, GL.GL_STENCIL_BITS) + " " + offscreenCaps.getStencilBits()); + System.err.println("GL_RED_BITS " + getGLInteger(gl, GL.GL_RED_BITS) + " " + reqOffscreenCaps.getRedBits()); + System.err.println("GL_GREEN_BITS " + getGLInteger(gl, GL.GL_GREEN_BITS) + " " + reqOffscreenCaps.getGreenBits()); + System.err.println("GL_BLUE_BITS " + getGLInteger(gl, GL.GL_BLUE_BITS) + " " + reqOffscreenCaps.getBlueBits()); + System.err.println("GL_ALPHA_BITS " + getGLInteger(gl, GL.GL_ALPHA_BITS) + " " + reqOffscreenCaps.getAlphaBits()); + System.err.println("GL_ACCUM_RED_BITS " + getGLInteger(gl, GL2.GL_ACCUM_RED_BITS) + " " + reqOffscreenCaps.getAccumRedBits()); + System.err.println("GL_ACCUM_GREEN_BITS " + getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) + " " + reqOffscreenCaps.getAccumGreenBits()); + System.err.println("GL_ACCUM_BLUE_BITS " + getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS) + " " + reqOffscreenCaps.getAccumBlueBits()); + System.err.println("GL_ACCUM_ALPHA_BITS " + getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) + " " + reqOffscreenCaps.getAccumAlphaBits()); + System.err.println("GL_DEPTH_BITS " + getGLInteger(gl, GL.GL_DEPTH_BITS) + " " + reqOffscreenCaps.getDepthBits()); + System.err.println("GL_STENCIL_BITS " + getGLInteger(gl, GL.GL_STENCIL_BITS) + " " + reqOffscreenCaps.getStencilBits()); } isInitialized = false; backend = null; diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PMSAAES2.java b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PMSAAES2.java index fa81e2a3f..fbd40ebdd 100644 --- a/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PMSAAES2.java +++ b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PMSAAES2.java @@ -427,7 +427,7 @@ public class VBORegion2PMSAAES2 extends GLRegion { gl.glActiveTexture(GL.GL_TEXTURE0 + gcu_FboTexUnit.intValue()); - fbo.use(gl, fbo.getSamplingSink()); + fbo.use(gl, fbo.getSamplingSink().getTextureAttachment()); gca_FboVerticesAttr.enableBuffer(gl, true); gca_FboTexCoordsAttr.enableBuffer(gl, true); indicesFbo.bindBuffer(gl, true); // keeps VBO binding @@ -454,19 +454,22 @@ public class VBORegion2PMSAAES2 extends GLRegion { fboWidth = targetFboWidth; fboHeight = targetFboHeight; fbo = new FBObject(); - fbo.reset(gl, fboWidth, fboHeight, sampleCount[0], false); + fbo.init(gl, fboWidth, fboHeight, sampleCount[0]); sampleCount[0] = fbo.getNumSamples(); fbo.attachColorbuffer(gl, 0, true); - fbo.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + if( !blendingEnabled ) { + // no depth-buffer w/ blending + fbo.attachRenderbuffer(gl, Attachment.Type.DEPTH, FBObject.DEFAULT_BITS); + } final FBObject ssink = new FBObject(); { - ssink.reset(gl, fboWidth, fboHeight); + ssink.init(gl, fboWidth, fboHeight, 0); // FIXME: shall not use bilinear (GL_LINEAR), due to MSAA ??? // ssink.attachTexture2D(gl, 0, true, GL2ES2.GL_LINEAR, GL2ES2.GL_LINEAR, GL2ES2.GL_CLAMP_TO_EDGE, GL2ES2.GL_CLAMP_TO_EDGE); ssink.attachTexture2D(gl, 0, true, GL.GL_NEAREST, GL.GL_NEAREST, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); if( !blendingEnabled ) { // no depth-buffer w/ blending - ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, FBObject.DEFAULT_BITS); } } fbo.setSamplingSink(ssink); @@ -475,7 +478,7 @@ public class VBORegion2PMSAAES2 extends GLRegion { System.err.printf("XXX.createFBO: blending %b, %dx%d%n%s%n", blendingEnabled, fboWidth, fboHeight, fbo.toString()); } } else if( targetFboWidth != fboWidth || targetFboHeight != fboHeight || fbo.getNumSamples() != sampleCount[0] ) { - fbo.reset(gl, targetFboWidth, targetFboHeight, sampleCount[0], true /* resetSamplingSink */); + fbo.reset(gl, targetFboWidth, targetFboHeight, sampleCount[0]); sampleCount[0] = fbo.getNumSamples(); if( DEBUG_FBO_1 ) { System.err.printf("XXX.resetFBO: %dx%d -> %dx%d%n%s%n", fboWidth, fboHeight, targetFboWidth, targetFboHeight, fbo ); diff --git a/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PVBAAES2.java b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PVBAAES2.java index 24fa09058..8f1de9157 100644 --- a/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PVBAAES2.java +++ b/src/jogl/classes/jogamp/graph/curve/opengl/VBORegion2PVBAAES2.java @@ -573,20 +573,20 @@ public class VBORegion2PVBAAES2 extends GLRegion { fboTexSize.put(1, fboHeight); } fbo = new FBObject(); - fbo.reset(gl, fboWidth, fboHeight); + fbo.init(gl, fboWidth, fboHeight, 0); // Shall not use bilinear (GL_LINEAR), due to own VBAA. Result is smooth w/o it now! // FIXME: FXAA requires bilinear filtering! // texA = fbo.attachTexture2D(gl, 0, true, GL.GL_LINEAR, GL.GL_LINEAR, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); texA = fbo.attachTexture2D(gl, 0, true, GL.GL_NEAREST, GL.GL_NEAREST, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); if( !blendingEnabled ) { // no depth-buffer w/ blending - fbo.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + fbo.attachRenderbuffer(gl, Attachment.Type.DEPTH, FBObject.DEFAULT_BITS); } if( DEBUG_FBO_1 ) { System.err.printf("XXX.createFBO: %dx%d%n%s%n", fboWidth, fboHeight, fbo.toString()); } } else if( newFboWidth != fboWidth || newFboHeight != fboHeight ) { - fbo.reset(gl, newFboWidth, newFboHeight); + fbo.reset(gl, newFboWidth, newFboHeight, 0); fbo.bind(gl); if( DEBUG_FBO_1 ) { System.err.printf("XXX.resetFBO: %dx%d -> %dx%d, target %dx%d%n", fboWidth, fboHeight, newFboWidth, newFboHeight, targetFboWidth, targetFboHeight); diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java index 5bd49dce9..97570d605 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java @@ -32,8 +32,8 @@ import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.font.Font; import com.jogamp.opengl.math.geom.AABBox; -public class TypecastGlyph implements Font.Glyph { - public static class Advance +public final class TypecastGlyph implements Font.Glyph { + public static final class Advance { private final Font font; private final float advance; @@ -46,40 +46,42 @@ public class TypecastGlyph implements Font.Glyph { size2advanceI.setKeyNotFoundValue(0); } - public void reset() { + public final void reset() { size2advanceI.clear(); } - public float getScale(final float pixelSize) + public final Font getFont() { return font; } + + public final float getScale(final float pixelSize) { return this.font.getMetrics().getScale(pixelSize); } - public void add(final float advance, final float size) + public final void add(final float advance, final float size) { size2advanceI.put(Float.floatToIntBits(size), Float.floatToIntBits(advance)); } - public float get(final float size, final boolean useFrationalMetrics) + public final float get(final float pixelSize, final boolean useFrationalMetrics) { - final int sI = Float.floatToIntBits(size); + final int sI = Float.floatToIntBits(pixelSize); final int aI = size2advanceI.get(sI); if( 0 != aI ) { return Float.intBitsToFloat(aI); } final float a; if ( useFrationalMetrics ) { - a = this.advance * getScale(size); + a = this.advance * getScale(pixelSize); } else { - // a = Math.ceil(this.advance * getScale(size)); - a = Math.round(this.advance * getScale(size)); // TODO: check whether ceil should be used instead? + // a = Math.ceil(this.advance * getScale(pixelSize)); + a = Math.round(this.advance * getScale(pixelSize)); // TODO: check whether ceil should be used instead? } size2advanceI.put(sI, Float.floatToIntBits(a)); return a; } @Override - public String toString() + public final String toString() { return "\nAdvance:"+ "\n advance: "+this.advance+ @@ -87,7 +89,7 @@ public class TypecastGlyph implements Font.Glyph { } } - public static class Metrics + public static final class Metrics { private final AABBox bbox; private final Advance advance; @@ -98,32 +100,34 @@ public class TypecastGlyph implements Font.Glyph { this.advance = new Advance(font, advance); } - public void reset() { + public final void reset() { advance.reset(); } - public float getScale(final float pixelSize) + public final Font getFont() { return advance.getFont(); } + + public final float getScale(final float pixelSize) { return this.advance.getScale(pixelSize); } - public AABBox getBBox() + public final AABBox getBBox() { return this.bbox; } - public void addAdvance(final float advance, final float size) + public final void addAdvance(final float advance, final float size) { this.advance.add(advance, size); } - public float getAdvance(final float size, final boolean useFrationalMetrics) + public final float getAdvance(final float pixelSize, final boolean useFrationalMetrics) { - return this.advance.get(size, useFrationalMetrics); + return this.advance.get(pixelSize, useFrationalMetrics); } @Override - public String toString() + public final String toString() { return "\nMetrics:"+ "\n bbox: "+this.bbox+ @@ -134,31 +138,21 @@ public class TypecastGlyph implements Font.Glyph { public static final short INVALID_ID = (short)((1 << 16) - 1); public static final short MAX_ID = (short)((1 << 16) - 2); - private final Font font; private final char symbol; private final OutlineShape shape; // in EM units private final short id; - private final int advance; private final Metrics metrics; protected TypecastGlyph(final Font font, final char symbol, final short id, final AABBox bbox, final int advance, final OutlineShape shape) { - this.font = font; this.symbol = symbol; this.shape = shape; this.id = id; - this.advance = advance; - this.metrics = new Metrics(this.font, bbox, this.advance); + this.metrics = new Metrics(font, bbox, advance); } - /** - public void reset(Path2D path) { - this.path = path; - this.metrics.reset(); - } */ - @Override public final Font getFont() { - return this.font; + return this.metrics.getFont(); } @Override @@ -211,7 +205,7 @@ public class TypecastGlyph implements Font.Glyph { @Override public final int hashCode() { // 31 * x == (x << 5) - x - final int hash = 31 + font.getName(Font.NAME_UNIQUNAME).hashCode(); + final int hash = 31 + getFont().getName(Font.NAME_UNIQUNAME).hashCode(); return ((hash << 5) - hash) + id; } } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/OTGlyph.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/OTGlyph.java index 7bfffd58c..8ed450326 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/OTGlyph.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/OTGlyph.java @@ -65,10 +65,10 @@ import com.jogamp.opengl.math.geom.AABBox; * @version $Id: Glyph.java,v 1.3 2007-02-21 12:23:54 davidsch Exp $ * @author <a href="mailto:[email protected]">David Schweinsberg</a>, Sven Gothel */ -public class OTGlyph { +public final class OTGlyph { - protected short _leftSideBearing; - protected int _advanceWidth; + private final short _leftSideBearing; + private final int _advanceWidth; private Point[] _points; AABBox _bbox; @@ -102,33 +102,32 @@ public class OTGlyph { } } - public void clearPointData() { + public final void clearPointData() { _points = null; } - public AABBox getBBox() { + public final AABBox getBBox() { return _bbox; } - public int getAdvanceWidth() { + public final int getAdvanceWidth() { return _advanceWidth; } - public short getLeftSideBearing() { + public final short getLeftSideBearing() { return _leftSideBearing; } - public Point getPoint(final int i) { + public final Point getPoint(final int i) { return _points[i]; } - public int getPointCount() { + public final int getPointCount() { return null != _points ? _points.length : 0; } /** * @param factor a 16.16 fixed value - */ public void scale(final int factor) { for (int i = 0; i < _points.length; i++) { //points[i].x = ( points[i].x * factor ) >> 6; @@ -139,11 +138,12 @@ public class OTGlyph { _leftSideBearing = (short)(( _leftSideBearing * factor) >> 6); _advanceWidth = (_advanceWidth * factor) >> 6; } + */ /** * Set the points of a glyph from the GlyphDescription */ - private void describe(final GlyphDescription gd) { + private final void describe(final GlyphDescription gd) { int endPtIndex = 0; _points = new Point[gd.getPointCount() /* + 2 */ ]; for (int i = 0; i < gd.getPointCount(); i++) { diff --git a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java index fce5c1fcc..6e6aaf58d 100644 --- a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java +++ b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java @@ -57,9 +57,11 @@ import com.jogamp.opengl.GLStateKeeper; /** - * Abstract common code for GLAutoDrawable implementations. + * Abstract common code for GLAutoDrawable implementations + * utilizing multithreading, i.e. {@link #isThreadGLCapable()} always returns <code>true</code>. * * @see GLAutoDrawable + * @see GLAutoDrawable#getThreadingMode() * @see GLAutoDrawableDelegate * @see GLOffscreenAutoDrawable * @see GLOffscreenAutoDrawableImpl @@ -123,9 +125,6 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe helper.setSharedAutoDrawable(this, sharedAutoDrawable); } - /** Returns the recursive lock object of the upstream implementation, which synchronizes multithreaded access on top of {@link NativeSurface#lockSurface()}. */ - protected abstract RecursiveLock getLock(); - @Override public final GLStateKeeper.Listener setGLStateKeeperListener(final Listener l) { final GLStateKeeper.Listener pre = glStateKeeperListener; @@ -240,7 +239,7 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe System.err.println("GLAutoDrawableBase.sizeChanged: ("+getThreadName()+"): "+newWidth+"x"+newHeight+" - surfaceHandle 0x"+Long.toHexString(surfaceHandle)); } if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) { - final RecursiveLock _lock = getLock(); + final RecursiveLock _lock = getUpstreamLock(); _lock.lock(); try { final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, newWidth, newHeight); @@ -280,7 +279,13 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe shallClose = true; } if( shallClose ) { - destroyAvoidAwareOfLocking(); + try { + destroyAvoidAwareOfLocking(); + } catch( final Throwable t ) { + // Intentionally catch and ignore exception, + // so the destroy mechanism of the native windowing system is not corrupted! + GLException.dumpThrowable("ignored", t); + } } } @@ -332,7 +337,7 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe * Calls {@link #destroyImplInLock()} while claiming the lock. */ protected final void defaultDestroy() { - final RecursiveLock lock = getLock(); + final RecursiveLock lock = getUpstreamLock(); lock.lock(); try { destroyImplInLock(); @@ -357,30 +362,52 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe preserveGLStateAtDestroy(false); preserveGLEventListenerState(); } + + GLException exceptionOnDisposeGL = null; if( null != context ) { if( context.isCreated() ) { - // Catch dispose GLExceptions by GLEventListener, just 'print' them - // so we can continue with the destruction. try { helper.disposeGL(this, context, true); } catch (final GLException gle) { - gle.printStackTrace(); + exceptionOnDisposeGL = gle; } } context = null; } + + Throwable exceptionOnUnrealize = null; + Throwable exceptionOnDeviceClose = null; if( null != drawable ) { final AbstractGraphicsDevice device = drawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); - drawable.setRealized(false); + try { + drawable.setRealized(false); + } catch( final Throwable re ) { + exceptionOnUnrealize = re; + } drawable = null; - if( ownsDevice ) { - device.close(); + try { + if( ownsDevice ) { + device.close(); + } + } catch (final Throwable re) { + exceptionOnDeviceClose = re; } } + + // throw exception in order of occurrence .. + if( null != exceptionOnDisposeGL ) { + throw exceptionOnDisposeGL; + } + if( null != exceptionOnUnrealize ) { + throw GLException.newGLException(exceptionOnUnrealize); + } + if( null != exceptionOnDeviceClose ) { + throw GLException.newGLException(exceptionOnDeviceClose); + } } public final void defaultSwapBuffers() throws GLException { - final RecursiveLock _lock = getLock(); + final RecursiveLock _lock = getUpstreamLock(); _lock.lock(); try { if(null != drawable) { @@ -421,7 +448,7 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe destroy(); return; } - final RecursiveLock _lock = getLock(); + final RecursiveLock _lock = getUpstreamLock(); _lock.lock(); try { if( null == context ) { @@ -452,7 +479,7 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe } protected final GLEventListener defaultDisposeGLEventListener(final GLEventListener listener, final boolean remove) { - final RecursiveLock _lock = getLock(); + final RecursiveLock _lock = getUpstreamLock(); _lock.lock(); try { return helper.disposeGLEventListener(GLAutoDrawableBase.this, drawable, context, listener, remove); @@ -473,7 +500,7 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe @Override public final GLContext setContext(final GLContext newCtx, final boolean destroyPrevCtx) { - final RecursiveLock lock = getLock(); + final RecursiveLock lock = getUpstreamLock(); lock.lock(); try { final GLContext oldCtx = context; @@ -571,16 +598,21 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe } @Override - public final boolean invoke(final boolean wait, final GLRunnable glRunnable) { + public final boolean invoke(final boolean wait, final GLRunnable glRunnable) throws IllegalStateException { return helper.invoke(this, wait, glRunnable); } @Override - public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) { + public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) throws IllegalStateException { return helper.invoke(this, wait, glRunnables); } @Override + public void flushGLRunnables() { + helper.flushGLRunnables(); + } + + @Override public final void setAutoSwapBufferMode(final boolean enable) { helper.setAutoSwapBufferMode(enable); } @@ -604,6 +636,15 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe return additionalCtxCreationFlags; } + /** + * {@inheritDoc} + * <p> + * Implementation always supports multithreading, hence method always returns <code>true</code>. + * </p> + */ + @Override + public final boolean isThreadGLCapable() { return true; } + // // FPSCounter // @@ -664,7 +705,7 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe @Override public final GLContext createContext(final GLContext shareWith) { - final RecursiveLock lock = getLock(); + final RecursiveLock lock = getUpstreamLock(); lock.lock(); try { if(drawable != null) { @@ -680,7 +721,7 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe @Override public final void setRealized(final boolean realized) { - final RecursiveLock _lock = getLock(); + final RecursiveLock _lock = getUpstreamLock(); _lock.lock(); try { final GLDrawable _drawable = drawable; @@ -727,6 +768,12 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, GLStateKeepe } @Override + public final GLCapabilitiesImmutable getRequestedGLCapabilities() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.getRequestedGLCapabilities() : null; + } + + @Override public final GLProfile getGLProfile() { final GLDrawable _drawable = drawable; return null != _drawable ? _drawable.getGLProfile() : null; diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java index c175243ae..01478a422 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java @@ -108,10 +108,17 @@ public abstract class GLContextImpl extends GLContext { private final int[] boundFBOTarget = new int[] { 0, 0 }; // { draw, read } private int defaultVAO = 0; + /** + * <ul> + * <li>[GLAutoDrawable.upstreamLock].lock()</li> + * <li>drawable.surface.lock()</li> + * <li>contextLock.lock()</li> + * </ul> + */ protected GLDrawableImpl drawable; protected GLDrawableImpl drawableRead; - private volatile boolean pixelDataEvaluated; + private boolean pixelDataEvaluated; private int /* pixelDataInternalFormat, */ pixelDataFormat, pixelDataType; protected GL gl; @@ -137,11 +144,16 @@ public abstract class GLContextImpl extends GLContext { public GLContextImpl(final GLDrawableImpl drawable, final GLContext shareWith) { super(); + if( null == drawable ) { + throw new IllegalArgumentException("Null drawable"); + } bufferStateTracker = new GLBufferStateTracker(); if ( null != shareWith ) { GLContextShareSet.registerSharing(this, shareWith); bufferObjectTracker = ((GLContextImpl)shareWith).getBufferObjectTracker(); - assert (bufferObjectTracker != null) : "shared context hash null GLBufferObjectTracker: "+shareWith; + if( null == bufferObjectTracker ) { + throw new InternalError("shared-master context hash null GLBufferObjectTracker: "+toHexString(shareWith.hashCode())); + } } else { bufferObjectTracker = new GLBufferObjectTracker(); } @@ -189,21 +201,28 @@ public abstract class GLContextImpl extends GLContext { @Override public final GLDrawable setGLReadDrawable(final GLDrawable read) { - if(!isGLReadDrawableAvailable()) { - throw new GLException("Setting read drawable feature not available"); - } - final boolean lockHeld = lock.isOwner(Thread.currentThread()); - if(lockHeld) { - release(); - } else if(lock.isLockedByOtherThread()) { // still could glitch .. - throw new GLException("GLContext current by other thread ("+lock.getOwner()+"), operation not allowed."); - } - final GLDrawable old = drawableRead; - drawableRead = ( null != read ) ? (GLDrawableImpl) read : drawable; - if(lockHeld) { - makeCurrent(); - } - return old; + // Validate constraints first! + if(!isGLReadDrawableAvailable()) { + throw new GLException("Setting read drawable feature not available"); + } + final Thread currentThread = Thread.currentThread(); + if( lock.isLockedByOtherThread() ) { + throw new GLException("GLContext current by other thread "+lock.getOwner().getName()+", operation not allowed on this thread "+currentThread.getName()); + } + final boolean lockHeld = lock.isOwner(currentThread); + if( lockHeld && lock.getHoldCount() > 1 ) { + // would need to makeCurrent * holdCount + throw new GLException("GLContext is recursively locked - unsupported for setGLDrawable(..)"); + } + if(lockHeld) { + release(false); + } + final GLDrawable old = drawableRead; + drawableRead = ( null != read ) ? (GLDrawableImpl) read : drawable; + if(lockHeld) { + makeCurrent(); + } + return old; } @Override @@ -213,43 +232,46 @@ public abstract class GLContextImpl extends GLContext { @Override public final GLDrawable setGLDrawable(final GLDrawable readWrite, final boolean setWriteOnly) { - if( drawable == readWrite && ( setWriteOnly || drawableRead == readWrite ) ) { - return drawable; // no change. - } - final Thread currentThread = Thread.currentThread(); - if( lock.isLockedByOtherThread() ) { - throw new GLException("GLContext current by other thread "+lock.getOwner().getName()+", operation not allowed on this thread "+currentThread.getName()); - } - final boolean lockHeld = lock.isOwner(currentThread); - if( lockHeld && lock.getHoldCount() > 1 ) { - // would need to makeCurrent * holdCount - throw new GLException("GLContext is recursively locked - unsupported for setGLDrawable(..)"); - } - final GLDrawableImpl old = drawable; - if( isCreated() && null != old && old.isRealized() ) { - if(!lockHeld) { - makeCurrent(); - } - associateDrawable(false); - if(!lockHeld) { - release(); - } - } - if(lockHeld) { - release(); - } - if( !setWriteOnly || drawableRead == drawable ) { // if !setWriteOnly || !explicitReadDrawable - drawableRead = (GLDrawableImpl) readWrite; - } - drawableRetargeted |= null != drawable && readWrite != drawable; - drawable = (GLDrawableImpl) readWrite ; - if( isCreated() && null != drawable && drawable.isRealized() ) { - makeCurrent(true); // implicit: associateDrawable(true) - if( !lockHeld ) { - release(); - } - } - return old; + // Validate constraints first! + final Thread currentThread = Thread.currentThread(); + if( lock.isLockedByOtherThread() ) { + throw new GLException("GLContext current by other thread "+lock.getOwner().getName()+", operation not allowed on this thread "+currentThread.getName()); + } + final boolean lockHeld = lock.isOwner(currentThread); + if( lockHeld && lock.getHoldCount() > 1 ) { + // would need to makeCurrent * holdCount + throw new GLException("GLContext is recursively locked - unsupported for setGLDrawable(..)"); + } + if( drawable == readWrite && ( setWriteOnly || drawableRead == readWrite ) ) { + return drawable; // no change. + } + final GLDrawableImpl old = drawable; + if( isCreated() && null != old && old.isRealized() ) { + if(!lockHeld) { + makeCurrent(); + } + // sync GL ctx w/ drawable's framebuffer before de-association + gl.glFinish(); + associateDrawable(false); + if(!lockHeld) { + release(false); + } + } + if(lockHeld) { + release(false); + } + if( !setWriteOnly || drawableRead == drawable ) { // if !setWriteOnly || !explicitReadDrawable + drawableRead = (GLDrawableImpl) readWrite; + } + drawableRetargeted |= null != drawable && readWrite != drawable; + drawable = (GLDrawableImpl) readWrite ; + if( isCreated() && null != drawable && drawable.isRealized() ) { + makeCurrent(true); // implicit: associateDrawable(true) + if( !lockHeld ) { + release(false); + } + } + return old; } @Override @@ -258,7 +280,7 @@ public abstract class GLContextImpl extends GLContext { } public final GLDrawableImpl getDrawableImpl() { - return (GLDrawableImpl) getGLDrawable(); + return drawable; } @Override @@ -315,56 +337,57 @@ public abstract class GLContextImpl extends GLContext { release(false); } private void release(final boolean inDestruction) throws GLException { - if( TRACE_SWITCH ) { - System.err.println(getThreadName() +": GLContext.ContextSwitch[release.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+", inDestruction: "+inDestruction+", "+lock); - } - if ( !lock.isOwner(Thread.currentThread()) ) { - final String msg = getThreadName() +": Context not current on thread, obj " + toHexString(hashCode())+", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+", inDestruction: "+inDestruction+", "+lock; - if( DEBUG_TRACE_SWITCH ) { - System.err.println(msg); - if( null != lastCtxReleaseStack ) { - System.err.print("Last release call: "); - lastCtxReleaseStack.printStackTrace(); - } else { - System.err.println("Last release call: NONE"); - } - } - throw new GLException(msg); - } - - Throwable drawableContextMadeCurrentException = null; - final boolean actualRelease = ( inDestruction || lock.getHoldCount() == 1 ) && 0 != contextHandle; - try { - if( actualRelease ) { - if( !inDestruction ) { - try { - contextMadeCurrent(false); - } catch (final Throwable t) { - drawableContextMadeCurrentException = t; - } - } - releaseImpl(); - } - } finally { - // exception prone .. - if( actualRelease ) { - setCurrent(null); + if( TRACE_SWITCH ) { + final long drawH = null != drawable ? drawable.getHandle() : 0; + System.err.println(getThreadName() +": GLContext.ContextSwitch[release.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+(null!=drawable)+" "+toHexString(drawH)+", inDestruction: "+inDestruction+", "+lock); } - drawable.unlockSurface(); - lock.unlock(); - if( DEBUG_TRACE_SWITCH ) { - final String msg = getThreadName() +": GLContext.ContextSwitch[release.X]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+(actualRelease?"switch":"keep ")+" - "+lock; - lastCtxReleaseStack = new Throwable(msg); - if( TRACE_SWITCH ) { + if ( !lock.isOwner(Thread.currentThread()) ) { + final long drawH = null != drawable ? drawable.getHandle() : 0; + final String msg = getThreadName() +": Context not current on thread, obj " + toHexString(hashCode())+", ctx "+toHexString(contextHandle)+", surf "+(null!=drawable)+" "+toHexString(drawH)+", inDestruction: "+inDestruction+", "+lock; + if( DEBUG_TRACE_SWITCH ) { System.err.println(msg); - // Thread.dumpStack(); + if( null != lastCtxReleaseStack ) { + System.err.print("Last release call: "); + lastCtxReleaseStack.printStackTrace(); + } else { + System.err.println("Last release call: NONE"); + } } + throw new GLException(msg); } - } - if(null != drawableContextMadeCurrentException) { - throw new GLException("GLContext.release(false) during GLDrawableImpl.contextMadeCurrent(this, false)", drawableContextMadeCurrentException); - } + Throwable drawableContextMadeCurrentException = null; + final boolean actualRelease = ( inDestruction || lock.getHoldCount() == 1 ) && 0 != contextHandle; + try { + if( actualRelease ) { + if( !inDestruction ) { + try { + contextMadeCurrent(false); + } catch (final Throwable t) { + drawableContextMadeCurrentException = t; + } + } + releaseImpl(); + } + } finally { + // exception prone .. + if( actualRelease ) { + setCurrent(null); + } + lock.unlock(); + drawable.unlockSurface(); + if( DEBUG_TRACE_SWITCH ) { + final String msg = getThreadName() +": GLContext.ContextSwitch[release.X]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+(actualRelease?"switch":"keep ")+" - "+lock; + lastCtxReleaseStack = new Throwable(msg); + if( TRACE_SWITCH ) { + System.err.println(msg); + // Thread.dumpStack(); + } + } + } + if(null != drawableContextMadeCurrentException) { + throw new GLException("GLContext.release(false) during GLDrawableImpl.contextMadeCurrent(this, false)", drawableContextMadeCurrentException); + } } private Throwable lastCtxReleaseStack = null; protected abstract void releaseImpl() throws GLException; @@ -374,7 +397,7 @@ public abstract class GLContextImpl extends GLContext { if ( DEBUG_TRACE_SWITCH ) { final long drawH = null != drawable ? drawable.getHandle() : 0; System.err.println(getThreadName() + ": GLContextImpl.destroy.0: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle) + - ", surf "+toHexString(drawH)+", isShared "+GLContextShareSet.isShared(this)+" - "+lock); + ", surf "+(null!=drawable)+" "+toHexString(drawH)+", isShared "+GLContextShareSet.isShared(this)+" - "+lock); } if ( 0 != contextHandle ) { // isCreated() ? if ( null == drawable ) { @@ -393,9 +416,9 @@ public abstract class GLContextImpl extends GLContext { // Must hold the lock around the destroy operation to make sure we // don't destroy the context while another thread renders to it. lock.lock(); // holdCount++ -> 1 - n (1: not locked, 2-n: destroy while rendering) - if ( lock.getHoldCount() > 2 ) { - final String msg = getThreadName() + ": GLContextImpl.destroy: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle); - if ( DEBUG_TRACE_SWITCH ) { + if ( DEBUG_TRACE_SWITCH ) { + if ( lock.getHoldCount() > 2 ) { + final String msg = getThreadName() + ": GLContextImpl.destroy: obj " + toHexString(hashCode()) + ", ctx " + toHexString(contextHandle); System.err.println(msg+" - Lock was hold more than once - makeCurrent/release imbalance: "+lock); Thread.dumpStack(); } @@ -519,8 +542,16 @@ public abstract class GLContextImpl extends GLContext { } protected final int makeCurrent(boolean forceDrawableAssociation) throws GLException { + final boolean hasDrawable = null != drawable; if( TRACE_SWITCH ) { - System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+lock); + final long drawH = null != drawable ? drawable.getHandle() : 0; + System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+hasDrawable+" "+toHexString(drawH)+" - "+lock); + } + if( !hasDrawable ) { + if( DEBUG_TRACE_SWITCH ) { + System.err.println(getThreadName() +": GLContext.ContextSwitch[makeCurrent.X0]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+" - NULL Drawable - CONTEXT_NOT_CURRENT - "+lock); + } + return CONTEXT_NOT_CURRENT; } // Note: the surface is locked within [makeCurrent .. swap .. release] @@ -589,12 +620,12 @@ public abstract class GLContextImpl extends GLContext { } } - if (res != CONTEXT_NOT_CURRENT) { + if (res != CONTEXT_NOT_CURRENT) { // still locked! setCurrent(this); if(res == CONTEXT_CURRENT_NEW) { // check if the drawable's and the GL's GLProfile are equal // throws an GLException if not - getGLDrawable().getGLProfile().verifyEquality(gl.getGLProfile()); + drawable.getGLProfile().verifyEquality(gl.getGLProfile()); glDebugHandler.init( isGL2GL3() && isGLDebugEnabled() ); @@ -632,6 +663,10 @@ public abstract class GLContextImpl extends GLContext { return res; } + private final GLContextImpl getOtherSharedMaster() { + final GLContextImpl sharedMaster = (GLContextImpl) GLContextShareSet.getSharedMaster(this); + return this != sharedMaster ? sharedMaster : null; + } private final int makeCurrentWithinLock(final int surfaceLockRes) throws GLException { if (!isCreated()) { if( 0 >= drawable.getSurfaceWidth() || 0 >= drawable.getSurfaceHeight() ) { @@ -646,20 +681,23 @@ public abstract class GLContextImpl extends GLContext { additionalCtxCreationFlags |= GLContext.CTX_OPTION_DEBUG ; } - final GLContextImpl shareWith = (GLContextImpl) GLContextShareSet.getCreatedShare(this); - final long shareWithHandle; - if (null != shareWith) { - shareWith.getDrawableImpl().lockSurface(); - shareWithHandle = shareWith.getHandle(); - if (0 == shareWithHandle) { - throw new GLException("GLContextShareSet returned an invalid OpenGL context: "+this); + final boolean created; + final GLContextImpl sharedMaster = getOtherSharedMaster(); + if ( null != sharedMaster ) { + if ( NativeSurface.LOCK_SURFACE_NOT_READY >= sharedMaster.drawable.lockSurface() ) { + throw new GLException("GLContextShareSet could not lock sharedMaster surface: "+sharedMaster.drawable); } - } else { - shareWithHandle = 0; } - final boolean created; try { - created = createImpl(shareWithHandle); // may throws exception if fails + if ( null != sharedMaster ) { + final long sharedMasterHandle = sharedMaster.getHandle(); + if ( 0 == sharedMasterHandle ) { + throw new GLException("GLContextShareSet returned an invalid sharedMaster context: "+sharedMaster); + } + created = createImpl(sharedMasterHandle); // may throws exception if fails + } else { + created = createImpl(0); // may throws exception if fails + } if( created && hasNoDefaultVAO() ) { final int[] tmp = new int[1]; final GL rootGL = gl.getRootGL(); @@ -671,8 +709,8 @@ public abstract class GLContextImpl extends GLContext { } } } finally { - if (null != shareWith) { - shareWith.getDrawableImpl().unlockSurface(); + if ( null != sharedMaster ) { + sharedMaster.drawable.unlockSurface(); } } if ( DEBUG_TRACE_SWITCH ) { @@ -702,13 +740,32 @@ public abstract class GLContextImpl extends GLContext { } else { reqMajor = ctxVersion.getMajor(); } + final boolean isCompat; if( 0 != ( ctxOptions & GLContext.CTX_PROFILE_CORE) ) { reqProfile = GLContext.CTX_PROFILE_CORE; + isCompat = false; } else { reqProfile = GLContext.CTX_PROFILE_COMPAT; + isCompat = true; + } + GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + // Perform all required profile mappings + if( isCompat ) { + // COMPAT via non ARB + GLContext.mapAvailableGLVersion(device, reqMajor, GLContext.CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + if( reqMajor >= 4 ) { + GLContext.mapAvailableGLVersion(device, 3, reqProfile, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + GLContext.mapAvailableGLVersion(device, 3, GLContext.CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + } + if( reqMajor >= 3 ) { + GLContext.mapAvailableGLVersion(device, 2, reqProfile, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + } + } else { + // CORE via non ARB, unlikely, however .. + if( reqMajor >= 4 ) { + GLContext.mapAvailableGLVersion(device, 3, reqProfile, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + } } - GLContext.mapAvailableGLVersion(device, reqMajor, reqProfile, - ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); GLContext.setAvailableGLVersionsSet(device); if (DEBUG) { @@ -749,8 +806,8 @@ public abstract class GLContextImpl extends GLContext { * * The implementation <b>must</b> leave the context current.<br> * - * @param share the shared context or null - * @return the valid and current context if successful, or null + * @param sharedWithHandle the shared context handle or 0 + * @return true if successful, or false * @throws GLException */ protected abstract boolean createImpl(long sharedWithHandle) throws GLException ; @@ -818,6 +875,9 @@ public abstract class GLContextImpl extends GLContext { */ protected final long createContextARB(final long share, final boolean direct) { + if( GLProfile.disableOpenGLARBContext ) { + return 0; + } final AbstractGraphicsConfiguration config = drawable.getNativeSurface().getGraphicsConfiguration(); final AbstractGraphicsDevice device = config.getScreen().getDevice(); @@ -879,7 +939,7 @@ public abstract class GLContextImpl extends GLContext { /** * OSX 10.9 GLRendererQuirks.GL4NeedsGL3Request, quirk is added as usual @ setRendererQuirks(..) */ - if( !hasGL4 && !hasGL3 ) { + if( !GLProfile.disableOpenGLCore && !hasGL4 && !hasGL3 ) { hasGL3 = createContextARBMapVersionsAvailable(3, CTX_PROFILE_CORE); // GL3 success |= hasGL3; if( hasGL3 ) { @@ -896,25 +956,27 @@ public abstract class GLContextImpl extends GLContext { } } } - if( !hasGL4 ) { - hasGL4 = createContextARBMapVersionsAvailable(4, CTX_PROFILE_CORE); // GL4 - success |= hasGL4; - if( hasGL4 ) { - if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ) ) { - // Map hw-accel GL4 to all lower core profiles: GL3 - GLContext.mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); - if( PROFILE_ALIASING ) { - hasGL3 = true; + if( !GLProfile.disableOpenGLCore ) { + if( !hasGL4 ) { + hasGL4 = createContextARBMapVersionsAvailable(4, CTX_PROFILE_CORE); // GL4 + success |= hasGL4; + if( hasGL4 ) { + if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctxOptions ) ) { + // Map hw-accel GL4 to all lower core profiles: GL3 + GLContext.mapAvailableGLVersion(device, 3, CTX_PROFILE_CORE, ctxVersion.getMajor(), ctxVersion.getMinor(), ctxOptions); + if( PROFILE_ALIASING ) { + hasGL3 = true; + } } + resetStates(false); // clean context states, since creation was temporary } - resetStates(false); // clean context states, since creation was temporary } - } - if( !hasGL3 ) { - hasGL3 = createContextARBMapVersionsAvailable(3, CTX_PROFILE_CORE); // GL3 - success |= hasGL3; - if( hasGL3 ) { - resetStates(false); // clean this context states, since creation was temporary + if( !hasGL3 ) { + hasGL3 = createContextARBMapVersionsAvailable(3, CTX_PROFILE_CORE); // GL3 + success |= hasGL3; + if( hasGL3 ) { + resetStates(false); // clean this context states, since creation was temporary + } } } if( !hasGL4bc ) { @@ -1372,7 +1434,7 @@ public abstract class GLContextImpl extends GLContext { } if(null==this.gl || !verifyInstance(gl.getGLProfile(), "Impl", this.gl)) { - setGL( createGL( getGLDrawable().getGLProfile() ) ); + setGL( createGL( drawable.getGLProfile() ) ); } updateGLXProcAddressTable(); @@ -1545,6 +1607,10 @@ public abstract class GLContextImpl extends GLContext { ctxProfileBits &= ~ ( GLContext.CTX_IMPL_ES2_COMPAT | GLContext.CTX_IMPL_ES3_COMPAT ) ; } + if(!isCurrentContextHardwareRasterizer()) { + ctxProfileBits |= GLContext.CTX_IMPL_ACCEL_SOFT; + } + final VersionNumberString vendorVersion = GLVersionNumber.createVendorVersion(glVersion); setRendererQuirks(adevice, getDrawableImpl().getFactoryImpl(), @@ -1558,10 +1624,6 @@ public abstract class GLContextImpl extends GLContext { return false; } - if(!isCurrentContextHardwareRasterizer()) { - ctxProfileBits |= GLContext.CTX_IMPL_ACCEL_SOFT; - } - contextFQN = getContextFQN(adevice, major, minor, ctxProfileBits); if (DEBUG) { System.err.println(getThreadName() + ": GLContext.setGLFuncAvail.0 validated FQN: "+contextFQN+" - "+GLContext.getGLVersion(major, minor, ctxProfileBits, glVersion)); @@ -1669,9 +1731,6 @@ public abstract class GLContextImpl extends GLContext { final int reqMajor, final int reqMinor, final int reqCTP, final int major, final int minor, final int ctp, final VersionNumberString vendorVersion, final boolean withinGLVersionsMapping) { - final int[] quirks = new int[GLRendererQuirks.COUNT + 1]; // + 1 ( NoFullFBOSupport ) - int i = 0; - final String MesaSP = "Mesa "; // final String MesaRendererAMDsp = " AMD "; final String MesaRendererIntelsp = "Intel(R)"; @@ -1684,6 +1743,8 @@ public abstract class GLContextImpl extends GLContext { final boolean isDriverATICatalyst = !isDriverMesa && ( glVendor.contains("ATI Technologies") || glRenderer.startsWith("ATI ") ); final boolean isDriverNVIDIAGeForce = !isDriverMesa && ( glVendor.contains("NVIDIA Corporation") || glRenderer.contains("NVIDIA ") ); + final GLRendererQuirks quirks = new GLRendererQuirks(); + // // General Quirks // @@ -1693,14 +1754,14 @@ public abstract class GLContextImpl extends GLContext { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: ES req "+reqMajor+" and 2 < "+major); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); if( withinGLVersionsMapping ) { // Thread safe due to single threaded initialization! - GLRendererQuirks.addStickyDeviceQuirks(adevice, quirks, i-1, 1); + GLRendererQuirks.addStickyDeviceQuirk(adevice, quirk); } else { // FIXME: Remove when moving EGL/ES to ARB ctx creation synchronized(GLContextImpl.class) { - GLRendererQuirks.addStickyDeviceQuirks(adevice, quirks, i-1, 1); + GLRendererQuirks.addStickyDeviceQuirk(adevice, quirk); } } } @@ -1718,17 +1779,17 @@ public abstract class GLContextImpl extends GLContext { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); } if( Platform.getOSVersionNumber().compareTo(Platform.OSXVersion.Mavericks) >= 0 && 3==reqMajor && 4==major ) { final int quirk = GLRendererQuirks.GL4NeedsGL3Request; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", req "+reqMajor+"."+reqMinor); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); if( withinGLVersionsMapping ) { // Thread safe due to single threaded initialization! - GLRendererQuirks.addStickyDeviceQuirks(adevice, quirks, i-1, 1); + GLRendererQuirks.addStickyDeviceQuirk(adevice, quirk); } } if( isDriverNVIDIAGeForce ) { @@ -1738,14 +1799,14 @@ public abstract class GLContextImpl extends GLContext { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", Renderer "+glRenderer); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); } if( Platform.getOSVersionNumber().compareTo(Platform.OSXVersion.Lion) < 0 ) { // < OSX 10.7.0 w/ NV has unstable GLSL final int quirk = GLRendererQuirks.GLSLNonCompliant; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", OS Version "+Platform.getOSVersionNumber()+", Renderer "+glRenderer); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); } } } else if( isWindows ) { @@ -1757,7 +1818,7 @@ public abstract class GLContextImpl extends GLContext { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); } if( isDriverATICatalyst ) { @@ -1769,7 +1830,7 @@ public abstract class GLContextImpl extends GLContext { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()+", [Vendor "+glVendor+" or Renderer "+glRenderer+"], driverVersion "+vendorVersion); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); } if( Platform.getOSVersionNumber().compareTo(winXPVersionNumber) <= 0 ) { @@ -1777,7 +1838,7 @@ public abstract class GLContextImpl extends GLContext { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS-Version "+Platform.getOSType()+" "+Platform.getOSVersionNumber()+", [Vendor "+glVendor+" or Renderer "+glRenderer+"]"); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); } } } else if( Platform.OSType.ANDROID == Platform.getOSType() ) { @@ -1790,14 +1851,14 @@ public abstract class GLContextImpl extends GLContext { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + ", Renderer " + glRenderer); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); } if( glRenderer.contains("Immersion.16") ) { - final int quirk = GLRendererQuirks.GLSharedContextBuggy; - if(DEBUG) { - System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + ", Renderer " + glRenderer); - } - quirks[i++] = quirk; + final int quirk = GLRendererQuirks.GLSharedContextBuggy; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + ", Renderer " + glRenderer); + } + quirks.addQuirk( quirk ); } } @@ -1818,21 +1879,21 @@ public abstract class GLContextImpl extends GLContext { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 Renderer=" + glRenderer + ", Version=[vendor " + vendorVersion + ", GL " + glVersion+"]"); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); } } else if( isDriverATICatalyst ) { { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 Renderer=" + glRenderer); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); } } else if( jogamp.nativewindow.x11.X11Util.getMarkAllDisplaysUnclosable() ) { { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11Util Downstream"); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); } } } @@ -1843,6 +1904,7 @@ public abstract class GLContextImpl extends GLContext { // RENDERER related quirks // if( isDriverMesa ) { + final VersionNumber mesaSafeFBOVersion = new VersionNumber(8, 0, 0); final VersionNumber mesaIntelBuggySharedCtx921 = new VersionNumber(9, 2, 1); { @@ -1850,88 +1912,103 @@ public abstract class GLContextImpl extends GLContext { if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); } - if( hwAccel /* glRenderer.contains( MesaRendererIntelsp ) || glRenderer.contains( MesaRendererAMDsp ) */ ) - { + if( hwAccel ) { + // hardware-acceleration final int quirk = GLRendererQuirks.NoDoubleBufferedPBuffer; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); + } else { + // software + if( vendorVersion.compareTo(mesaSafeFBOVersion) < 0 ) { // FIXME: Is it fixed in >= 8.0.0 ? + final int quirk = GLRendererQuirks.BuggyColorRenderbuffer; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); + } + quirks.addQuirk( quirk ); + } } if (compatCtx && (major > 3 || (major == 3 && minor >= 1))) { - // FIXME: Apply vendor version constraints! - final int quirk = GLRendererQuirks.GLNonCompliant; - if(DEBUG) { - System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer); - } - quirks[i++] = quirk; + // FIXME: Apply vendor version constraints! + final int quirk = GLRendererQuirks.GLNonCompliant; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: Renderer " + glRenderer); + } + quirks.addQuirk( quirk ); } if( glRenderer.contains( MesaRendererIntelsp ) && vendorVersion.compareTo(mesaIntelBuggySharedCtx921) >= 0 && isX11 ) { // FIXME: When is it fixed ? - final int quirk = GLRendererQuirks.GLSharedContextBuggy; - if(DEBUG) { - System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 / Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); - } - quirks[i++] = quirk; + final int quirk = GLRendererQuirks.GLSharedContextBuggy; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 / Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); + } + quirks.addQuirk( quirk ); } if( glVendor.contains( "nouveau" ) // FIXME: && vendorVersion.compareTo(nouveauBuggyMSAAFixed) < 0 ) { - final int quirk = GLRendererQuirks.NoMultiSamplingBuffers; - if(DEBUG) { - System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 / Renderer " + glRenderer + " / Vendor "+glVendor); - } - quirks[i++] = quirk; - if( withinGLVersionsMapping ) { - // Thread safe due to single threaded initialization! - GLRendererQuirks.addStickyDeviceQuirks(adevice, quirks, i-1, 1); - } + final int quirk = GLRendererQuirks.NoMultiSamplingBuffers; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: X11 / Renderer " + glRenderer + " / Vendor "+glVendor); + } + quirks.addQuirk( quirk ); + if( withinGLVersionsMapping ) { + // Thread safe due to single threaded initialization! + GLRendererQuirks.addStickyDeviceQuirk(adevice, quirk); + } } - if( isWindows && glRenderer.contains("SVGA3D") ) { - final VersionNumber mesaSafeFBOVersion = new VersionNumber(8, 0, 0); - if ( vendorVersion.compareTo(mesaSafeFBOVersion) < 0 ) { // includes: vendorVersion.isZero() - final int quirk = GLRendererQuirks.NoFullFBOSupport; - if(DEBUG) { - System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + " / Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); - } - quirks[i++] = quirk; + if( isWindows && glRenderer.contains("SVGA3D") && vendorVersion.compareTo(mesaSafeFBOVersion) < 0 ) { + final int quirk = GLRendererQuirks.NoFullFBOSupport; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType() + " / Renderer " + glRenderer + " / Mesa-Version "+vendorVersion); } + quirks.addQuirk( quirk ); } } // // Property related quirks // - if( FORCE_MIN_FBO_SUPPORT ) { - final int quirk = GLRendererQuirks.NoFullFBOSupport; + if( FORCE_NO_COLOR_RENDERBUFFER ) { + final int quirk = GLRendererQuirks.BuggyColorRenderbuffer; if(DEBUG) { System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: property"); } - quirks[i++] = quirk; + quirks.addQuirk( quirk ); + } + if( FORCE_MIN_FBO_SUPPORT || quirks.exist(GLRendererQuirks.BuggyColorRenderbuffer) ) { + final int quirk = GLRendererQuirks.NoFullFBOSupport; + if(DEBUG) { + final String causeProps = FORCE_MIN_FBO_SUPPORT ? "property, " : ""; + final String causeQuirk = quirks.exist(GLRendererQuirks.BuggyColorRenderbuffer) ? "BuggyColorRenderbuffer" : ""; + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: "+causeProps+causeQuirk); + } + quirks.addQuirk( quirk ); } - glRendererQuirks = new GLRendererQuirks(quirks, 0, i); if(DEBUG) { - System.err.println("Quirks local.0: "+glRendererQuirks); + System.err.println("Quirks local.0: "+quirks); } { // Merge sticky quirks, thread safe due to single threaded initialization! - GLRendererQuirks.pushStickyDeviceQuirks(adevice, glRendererQuirks); + GLRendererQuirks.pushStickyDeviceQuirks(adevice, quirks); final AbstractGraphicsDevice factoryDefaultDevice = factory.getDefaultDevice(); if( !GLRendererQuirks.areSameStickyDevice(factoryDefaultDevice, adevice) ) { - GLRendererQuirks.pushStickyDeviceQuirks(factoryDefaultDevice, glRendererQuirks); + GLRendererQuirks.pushStickyDeviceQuirks(factoryDefaultDevice, quirks); } if( esCtx ) { final AbstractGraphicsDevice eglFactoryDefaultDevice = GLDrawableFactory.getEGLFactory().getDefaultDevice(); if( !GLRendererQuirks.areSameStickyDevice(eglFactoryDefaultDevice, adevice) && !GLRendererQuirks.areSameStickyDevice(eglFactoryDefaultDevice, factoryDefaultDevice) ) { - GLRendererQuirks.pushStickyDeviceQuirks(eglFactoryDefaultDevice, glRendererQuirks); + GLRendererQuirks.pushStickyDeviceQuirks(eglFactoryDefaultDevice, quirks); } } } + glRendererQuirks = quirks; if(DEBUG) { System.err.println("Quirks local.X: "+glRendererQuirks); System.err.println("Quirks sticky on "+adevice+": "+GLRendererQuirks.getStickyDeviceQuirks(adevice)); @@ -2106,35 +2183,31 @@ public abstract class GLContextImpl extends GLContext { } private final void evalPixelDataType() { - if(!pixelDataEvaluated) { - synchronized(this) { - if(!pixelDataEvaluated) { - boolean ok = false; - /* if(isGL2GL3() && 3 == components) { - pixelDataInternalFormat=GL.GL_RGB; - pixelDataFormat=GL.GL_RGB; - pixelDataType = GL.GL_UNSIGNED_BYTE; - ok = true; - } else */ if( isGLES2Compatible() || isExtensionAvailable(GLExtensions.OES_read_format) ) { - final int[] glImplColorReadVals = new int[] { 0, 0 }; - gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_FORMAT, glImplColorReadVals, 0); - gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_TYPE, glImplColorReadVals, 1); - // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB; - pixelDataFormat = glImplColorReadVals[0]; - pixelDataType = glImplColorReadVals[1]; - ok = 0 != pixelDataFormat && 0 != pixelDataType; - } - if( !ok ) { - // RGBA read is safe for all GL profiles - // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB; - pixelDataFormat=GL.GL_RGBA; - pixelDataType = GL.GL_UNSIGNED_BYTE; - } - // TODO: Consider: - // return gl.isGL2GL3()?GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV:GL.GL_UNSIGNED_SHORT_5_5_5_1; - pixelDataEvaluated = true; - } - } + if(!pixelDataEvaluated) { // only valid while context is made current + boolean ok = false; + /* if(isGL2GL3() && 3 == components) { + pixelDataInternalFormat=GL.GL_RGB; + pixelDataFormat=GL.GL_RGB; + pixelDataType = GL.GL_UNSIGNED_BYTE; + ok = true; + } else */ if( isGLES2Compatible() || isExtensionAvailable(GLExtensions.OES_read_format) ) { + final int[] glImplColorReadVals = new int[] { 0, 0 }; + gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_FORMAT, glImplColorReadVals, 0); + gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_TYPE, glImplColorReadVals, 1); + // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB; + pixelDataFormat = glImplColorReadVals[0]; + pixelDataType = glImplColorReadVals[1]; + ok = 0 != pixelDataFormat && 0 != pixelDataType; + } + if( !ok ) { + // RGBA read is safe for all GL profiles + // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB; + pixelDataFormat=GL.GL_RGBA; + pixelDataType = GL.GL_UNSIGNED_BYTE; + } + // TODO: Consider: + // return gl.isGL2GL3()?GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV:GL.GL_UNSIGNED_SHORT_5_5_5_1; + pixelDataEvaluated = true; } } diff --git a/src/jogl/classes/jogamp/opengl/GLContextShareSet.java b/src/jogl/classes/jogamp/opengl/GLContextShareSet.java index 209707f33..aed611edd 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextShareSet.java +++ b/src/jogl/classes/jogamp/opengl/GLContextShareSet.java @@ -61,21 +61,33 @@ public class GLContextShareSet { // to a share set, containing all shared contexts itself. private static final Map<GLContext, ShareSet> shareMap = new IdentityHashMap<GLContext, ShareSet>(); - private static final Object dummyValue = new Object(); private static class ShareSet { - private final Map<GLContext, Object> allShares = new IdentityHashMap<GLContext, Object>(); - private final Map<GLContext, Object> createdShares = new IdentityHashMap<GLContext, Object>(); - private final Map<GLContext, Object> destroyedShares = new IdentityHashMap<GLContext, Object>(); + private final Map<GLContext, GLContext> createdShares = new IdentityHashMap<GLContext, GLContext>(); + private final Map<GLContext, GLContext> destroyedShares = new IdentityHashMap<GLContext, GLContext>(); - public void add(final GLContext ctx) { - if (allShares.put(ctx, dummyValue) == null) { - if (ctx.isCreated()) { - createdShares.put(ctx, dummyValue); + public final void addNew(final GLContext slave, final GLContext master) { + final GLContext preMaster; + if ( slave.isCreated() ) { + preMaster = createdShares.put(slave, master); } else { - destroyedShares.put(ctx, dummyValue); + preMaster= destroyedShares.put(slave, master); + } + if( null != preMaster ) { + throw new InternalError("State of ShareSet corrupted: Slave "+toHexString(slave.hashCode())+ + " is not new w/ master "+toHexString(preMaster.hashCode())); + } + } + public final void addIfNew(final GLContext slave, final GLContext master) { + final GLContext preMaster = getMaster(master); + if( null == preMaster ) { + addNew(slave, master); } - } + } + + public final GLContext getMaster(final GLContext ctx) { + final GLContext c = createdShares.get(ctx); + return null != c ? c : destroyedShares.get(ctx); } public Set<GLContext> getCreatedShares() { @@ -86,57 +98,55 @@ public class GLContextShareSet { return destroyedShares.keySet(); } - public GLContext getCreatedShare(final GLContext ignore) { - for (final Iterator<GLContext> iter = createdShares.keySet().iterator(); iter.hasNext(); ) { - final GLContext ctx = iter.next(); - if (ctx != ignore) { - return ctx; - } - } - return null; - } - public void contextCreated(final GLContext ctx) { - final Object res = destroyedShares.remove(ctx); - assert res != null : "State of ShareSet corrupted; thought context " + - ctx + " should have been in destroyed set but wasn't"; - final Object res2 = createdShares.put(ctx, dummyValue); - assert res2 == null : "State of ShareSet corrupted; thought context " + - ctx + " shouldn't have been in created set but was"; + final GLContext ctxMaster = destroyedShares.remove(ctx); + if( null == ctxMaster ) { + throw new InternalError("State of ShareSet corrupted: Context "+toHexString(ctx.hashCode())+ + " should have been in destroyed-set"); + } + final GLContext delMaster = createdShares.put(ctx, ctxMaster); + if( null != delMaster ) { + throw new InternalError("State of ShareSet corrupted: Context "+toHexString(ctx.hashCode())+ + " shouldn't have been in created-set"); + } } public void contextDestroyed(final GLContext ctx) { - final Object res = createdShares.remove(ctx); - assert res != null : "State of ShareSet corrupted; thought context " + - ctx + " should have been in created set but wasn't"; - final Object res2 = destroyedShares.put(ctx, dummyValue); - assert res2 == null : "State of ShareSet corrupted; thought context " + - ctx + " shouldn't have been in destroyed set but was"; + final GLContext ctxMaster = createdShares.remove(ctx); + if( null == ctxMaster ) { + throw new InternalError("State of ShareSet corrupted: Context "+toHexString(ctx.hashCode())+ + " should have been in created-set"); + } + final GLContext delMaster = destroyedShares.put(ctx, ctxMaster); + if( null != delMaster ) { + throw new InternalError("State of ShareSet corrupted: Context "+toHexString(ctx.hashCode())+ + " shouldn't have been in destroyed-set"); + } } } - /** Indicate that contexts <code>share1</code> and - <code>share2</code> will share textures and display lists. Both + /** Indicate that contexts <code>slave</code> and + <code>master</code> will share textures and display lists. Both must be non-null. */ - public static synchronized void registerSharing(final GLContext share1, final GLContext share2) { - if (share1 == null || share2 == null) { - throw new IllegalArgumentException("Both share1 and share2 must be non-null"); - } - ShareSet share = entryFor(share1); - if (share == null) { - share = entryFor(share2); - } - if (share == null) { - share = new ShareSet(); - } - share.add(share1); - share.add(share2); - addEntry(share1, share); - addEntry(share2, share); - if (DEBUG) { - System.err.println("GLContextShareSet: registereSharing: 1: " + - toHexString(share1.getHandle()) + ", 2: " + toHexString(share2.getHandle())); - } + public static synchronized void registerSharing(final GLContext slave, final GLContext master) { + if (slave == null || master == null) { + throw new IllegalArgumentException("Both slave and master must be non-null"); + } + ShareSet share = entryFor(slave); + if ( null == share ) { + share = entryFor(master); + } + if ( null == share ) { + share = new ShareSet(); + } + share.addNew(slave, master); + share.addIfNew(master, master); // this master could have a different master shared registered earlier! + addEntry(slave, share); + addEntry(master, share); + if (DEBUG) { + System.err.println("GLContextShareSet: registereSharing: 1: " + + toHexString(slave.hashCode()) + ", 2: " + toHexString(master.hashCode())); + } } public static synchronized void unregisterSharing(final GLContext lastContext) { @@ -157,7 +167,7 @@ public class GLContextShareSet { } if (DEBUG) { System.err.println("GLContextShareSet: unregisterSharing: " + - toHexString(lastContext.getHandle())+", entries: "+s.size()); + toHexString(lastContext.hashCode())+", entries: "+s.size()); } for(final Iterator<GLContext> iter = s.iterator() ; iter.hasNext() ; ) { final GLContext ctx = iter.next(); @@ -176,13 +186,18 @@ public class GLContextShareSet { return share != null; } - /** Returns one created GLContext shared with the given <code>context</code>, otherwise return <code>null</code>. */ - public static synchronized GLContext getCreatedShare(final GLContext context) { + /** + * Returns the shared master GLContext of the given <code>context</code> if shared, otherwise return <code>null</code>. + * <p> + * Returns the given <code>context</code>, if it is a shared master. + * </p> + */ + public static synchronized GLContext getSharedMaster(final GLContext context) { final ShareSet share = entryFor(context); if (share == null) { return null; } - return share.getCreatedShare(context); + return share.getMaster(context); } private static synchronized Set<GLContext> getCreatedSharesImpl(final GLContext context) { diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableFactoryImpl.java b/src/jogl/classes/jogamp/opengl/GLDrawableFactoryImpl.java index 0b119b50d..8d65f16d3 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableFactoryImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableFactoryImpl.java @@ -41,8 +41,14 @@ package jogamp.opengl; import java.nio.Buffer; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.OffscreenLayerSurface; @@ -516,64 +522,98 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - /** - * Sets the gamma, brightness, and contrast of the current main - * display. Returns true if the settings were changed, false if - * not. If this method returns true, the display settings will - * automatically be reset upon JVM exit (assuming the JVM does not - * crash); if the user wishes to change the display settings back to - * normal ahead of time, use resetDisplayGamma(). Throws - * IllegalArgumentException if any of the parameters were - * out-of-bounds. - * - * @param gamma The gamma value, typically > 1.0 (default value is - * 1.0) - * @param brightness The brightness value between -1.0 and 1.0, - * inclusive (default value is 0) - * @param contrast The contrast, greater than 0.0 (default value is 1) - * @throws IllegalArgumentException if any of the parameters were - * out-of-bounds - */ - public boolean setDisplayGamma(final float gamma, final float brightness, final float contrast) throws IllegalArgumentException { + @Override + public synchronized final boolean setDisplayGamma(final NativeSurface surface, final float gamma, final float brightness, final float contrast) throws IllegalArgumentException { if ((brightness < -1.0f) || (brightness > 1.0f)) { throw new IllegalArgumentException("Brightness must be between -1.0 and 1.0"); } if (contrast < 0) { throw new IllegalArgumentException("Contrast must be greater than 0.0"); } - // FIXME: ensure gamma is > 1.0? Are smaller / negative values legal? - final int rampLength = getGammaRampLength(); - if (rampLength == 0) { - return false; + if( NativeSurface.LOCK_SURFACE_NOT_READY >= surface.lockSurface() ) { + return false; + } + try { + // FIXME: ensure gamma is > 1.0? Are smaller / negative values legal? + final int rampLength = getGammaRampLength(surface); + if (rampLength == 0) { + return false; + } + final float[] gammaRamp = new float[rampLength]; + for (int i = 0; i < rampLength; i++) { + final float intensity = (float) i / (float) (rampLength - 1); + // apply gamma + float rampEntry = (float) java.lang.Math.pow(intensity, gamma); + // apply brightness + rampEntry += brightness; + // apply contrast + rampEntry = (rampEntry - 0.5f) * contrast + 0.5f; + // Clamp entry to [0, 1] + if (rampEntry > 1.0f) + rampEntry = 1.0f; + else if (rampEntry < 0.0f) + rampEntry = 0.0f; + gammaRamp[i] = rampEntry; + } + final AbstractGraphicsScreen screen = surface.getGraphicsConfiguration().getScreen(); + final DeviceScreenID deviceScreenID = new DeviceScreenID(screen.getDevice().getConnection(), screen.getIndex()); + if( null == screen2OrigGammaRamp.get(deviceScreenID) ) { + screen2OrigGammaRamp.put(deviceScreenID, getGammaRamp(surface)); // cache original gamma ramp once + if( DEBUG ) { + System.err.println("DisplayGamma: Stored: "+deviceScreenID); + dumpGammaStore(); + } + } + return setGammaRamp(surface, gammaRamp); + } finally { + surface.unlockSurface(); } - final float[] gammaRamp = new float[rampLength]; - for (int i = 0; i < rampLength; i++) { - final float intensity = (float) i / (float) (rampLength - 1); - // apply gamma - float rampEntry = (float) java.lang.Math.pow(intensity, gamma); - // apply brightness - rampEntry += brightness; - // apply contrast - rampEntry = (rampEntry - 0.5f) * contrast + 0.5f; - // Clamp entry to [0, 1] - if (rampEntry > 1.0f) - rampEntry = 1.0f; - else if (rampEntry < 0.0f) - rampEntry = 0.0f; - gammaRamp[i] = rampEntry; + } + + @Override + public synchronized final void resetDisplayGamma(final NativeSurface surface) { + if( NativeSurface.LOCK_SURFACE_NOT_READY >= surface.lockSurface() ) { + return; } - if( !needsGammaRampReset ) { - originalGammaRamp = getGammaRamp(); - needsGammaRampReset = true; + try { + final AbstractGraphicsScreen screen = surface.getGraphicsConfiguration().getScreen(); + final DeviceScreenID deviceScreenID = new DeviceScreenID(screen.getDevice().getConnection(), screen.getIndex()); + final Buffer originalGammaRamp = screen2OrigGammaRamp.remove(deviceScreenID); + if( null != originalGammaRamp ) { + resetGammaRamp(surface, originalGammaRamp); + } + } finally { + surface.unlockSurface(); } - return setGammaRamp(gammaRamp); } @Override - public synchronized void resetDisplayGamma() { - if( needsGammaRampReset ) { - resetGammaRamp(originalGammaRamp); - needsGammaRampReset = false; + public synchronized final void resetAllDisplayGamma() { + resetAllDisplayGammaNoSync(); + } + + @Override + protected final void resetAllDisplayGammaNoSync() { + if( DEBUG ) { + System.err.println("DisplayGamma: Reset"); + dumpGammaStore(); + } + final Set<DeviceScreenID> deviceScreenIDs = screen2OrigGammaRamp.keySet(); + for( final Iterator<DeviceScreenID> i = deviceScreenIDs.iterator(); i.hasNext(); ) { + final DeviceScreenID deviceScreenID = i.next(); + final Buffer originalGammaRamp = screen2OrigGammaRamp.remove(deviceScreenID); + if( null != originalGammaRamp ) { + resetGammaRamp(deviceScreenID, originalGammaRamp); + } + } + } + private void dumpGammaStore() { + final Set<DeviceScreenID> deviceScreenIDs = screen2OrigGammaRamp.keySet(); + int count = 0; + for( final Iterator<DeviceScreenID> i = deviceScreenIDs.iterator(); i.hasNext(); count++) { + final DeviceScreenID deviceScreenID = i.next(); + final Buffer originalGammaRamp = screen2OrigGammaRamp.get(deviceScreenID); + System.err.printf("%4d/%4d: %s -> %s%n", count, deviceScreenIDs.size(), deviceScreenID, originalGammaRamp); } } @@ -582,30 +622,64 @@ public abstract class GLDrawableFactoryImpl extends GLDrawableFactory { // /** Returns the length of the computed gamma ramp for this OS and - hardware. Returns 0 if gamma changes are not supported. */ - protected int getGammaRampLength() { + hardware. Returns 0 if gamma changes are not supported. + * @param surface TODO*/ + protected int getGammaRampLength(final NativeSurface surface) { return 0; } /** Sets the gamma ramp for the main screen. Returns false if gamma - ramp changes were not supported. */ - protected boolean setGammaRamp(final float[] ramp) { + ramp changes were not supported. + * @param surface TODO*/ + protected boolean setGammaRamp(final NativeSurface surface, final float[] ramp) { return false; } /** Gets the current gamma ramp. This is basically an opaque value used only on some platforms to reset the gamma ramp to its - original settings. */ - protected Buffer getGammaRamp() { + original settings. + * @param surface TODO*/ + protected Buffer getGammaRamp(final NativeSurface surface) { return null; } /** Resets the gamma ramp, potentially using the specified Buffer as - data to restore the original values. */ - protected void resetGammaRamp(final Buffer originalGammaRamp) { + data to restore the original values. + * @param surface TODO*/ + protected void resetGammaRamp(final NativeSurface surface, final Buffer originalGammaRamp) { + } + protected void resetGammaRamp(final DeviceScreenID deviceScreenID, final Buffer originalGammaRamp) { } // Shutdown hook mechanism for resetting gamma - private volatile Buffer originalGammaRamp; - private volatile boolean needsGammaRampReset = false; + public final class DeviceScreenID { + public final String deviceConnection; + public final int screenIdx; + DeviceScreenID(final String deviceConnection, final int screenIdx) { + this.deviceConnection = deviceConnection; + this.screenIdx = screenIdx; + } + @Override + public int hashCode() { + // 31 * x == (x << 5) - x + int hash = 31 + deviceConnection.hashCode(); + hash = ((hash << 5) - hash) + screenIdx; + return hash; + } + @Override + public boolean equals(final Object obj) { + if(this == obj) { return true; } + if (obj instanceof DeviceScreenID) { + final DeviceScreenID other = (DeviceScreenID)obj; + return this.deviceConnection.equals(other.deviceConnection) && + this.screenIdx == other.screenIdx; + } + return false; + } + @Override + public String toString() { + return "DeviceScreenID[devCon "+deviceConnection+", screenIdx "+screenIdx+", hash 0x"+Integer.toHexString(hashCode())+"]"; + } + } + private final Map<DeviceScreenID, Buffer> screen2OrigGammaRamp = new HashMap<DeviceScreenID, Buffer>(); } diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java index eac14fdff..3deeafd27 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java @@ -40,6 +40,7 @@ package jogamp.opengl; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.HashSet; @@ -77,7 +78,8 @@ public class GLDrawableHelper { private final ArrayList<GLEventListener> listeners = new ArrayList<GLEventListener>(); private final HashSet<GLEventListener> listenersToBeInit = new HashSet<GLEventListener>(); private final Object glRunnablesLock = new Object(); - private volatile ArrayList<GLRunnableTask> glRunnables = new ArrayList<GLRunnableTask>(); + private ArrayList<GLRunnableTask> glRunnables = new ArrayList<GLRunnableTask>(); + private volatile int glRunnableCount = 0; private boolean autoSwapBufferMode; private volatile Thread exclusiveContextThread; /** -1 release, 0 nop, 1 claim */ @@ -102,6 +104,7 @@ public class GLDrawableHelper { exclusiveContextThread = null; exclusiveContextSwitch = 0; synchronized(glRunnablesLock) { + glRunnableCount = 0; glRunnables.clear(); } animatorCtrl = null; @@ -208,19 +211,23 @@ public class GLDrawableHelper { /** * Switch {@link GLContext} / {@link GLDrawable} association. * <p> - * The <code>oldCtx</code> will be destroyed if <code>destroyPrevCtx</code> is <code>true</code>, - * otherwise dis-associate <code>oldCtx</code> from <code>drawable</code> - * via {@link GLContext#setGLDrawable(GLDrawable, boolean) oldCtx.setGLDrawable(null, true);}. - * </p> - * <p> - * Re-associate <code>newCtx</code> with <code>drawable</code> - * via {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}. - * </p> - * <p> - * If the old or new context was current on this thread, it is being released before switching the drawable. - * </p> - * <p> - * No locking is being performed on the drawable, caller is required to take care of it. + * Remarks: + * <ul> + * <li>The <code>oldCtx</code> will be destroyed if <code>destroyPrevCtx</code> is <code>true</code>, + * otherwise disassociate <code>oldCtx</code> from <code>drawable</code> + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) oldCtx.setGLDrawable(null, true);} including {@link GL#glFinish() glFinish()}.</li> + * <li>Reassociate <code>newCtx</code> with <code>drawable</code> + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}.</li> + * <li>If the old context was current on this thread, it is being released after disassociating the drawable.</li> + * <li>If the new context was current on this thread, it is being released before associating the drawable + * and made current afterwards.</li> + * <li>Implementation may issue {@link #makeCurrent()} and {@link #release()} while drawable reassociation.</li> + * <li>The user shall take extra care of thread synchronization, + * i.e. lock the involved {@link GLDrawable#getNativeSurface() drawable's} {@link NativeSurface}s + * to avoid a race condition. In case {@link GLAutoDrawable auto-drawable's} are used, + * their {@link GLAutoDrawable#getUpstreamLock() upstream-lock} must be locked beforehand + * see <a href="../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li> + * </ul> * </p> * * @param drawable the drawable which context is changed @@ -277,7 +284,6 @@ public class GLDrawableHelper { if( currentContext != context ) { context.makeCurrent(); } - context.getGL().glFinish(); context.setGLDrawable(null, true); // dis-associate } @@ -295,7 +301,7 @@ public class GLDrawableHelper { } if(null != context) { - context.setGLDrawable(drawable, true); // re-association + context.setGLDrawable(drawable, true); // re-association, implicit glFinish() ctx/drawable sync } if( null != currentContext ) { @@ -506,15 +512,25 @@ public class GLDrawableHelper { * </p> * @param autoDrawable * @return the disposal count + * @throws GLException caused by {@link GLEventListener#dispose(GLAutoDrawable)} */ - public final int disposeAllGLEventListener(final GLAutoDrawable autoDrawable, final boolean remove) { + public final int disposeAllGLEventListener(final GLAutoDrawable autoDrawable, final boolean remove) throws GLException { + Throwable firstCaught = null; int disposeCount = 0; synchronized(listenersLock) { if( remove ) { for (int count = listeners.size(); 0 < count && 0 < listeners.size(); count--) { final GLEventListener listener = listeners.remove(0); if( !listenersToBeInit.remove(listener) ) { - listener.dispose(autoDrawable); + try { + listener.dispose(autoDrawable); + } catch (final Throwable t) { + if( null == firstCaught ) { + firstCaught = t; + } else { + GLException.dumpThrowable("subsequent", t); + } + } disposeCount++; } } @@ -522,13 +538,25 @@ public class GLDrawableHelper { for (int i = 0; i < listeners.size(); i++) { final GLEventListener listener = listeners.get(i); if( !listenersToBeInit.contains(listener) ) { - listener.dispose(autoDrawable); + try { + listener.dispose(autoDrawable); + } catch (final Throwable t) { + if( null == firstCaught ) { + firstCaught = t; + } else { + GLException.dumpThrowable("subsequent", t); + } + } listenersToBeInit.add(listener); disposeCount++; } } } } + if( null != firstCaught ) { + flushGLRunnables(); + throw GLException.newGLException(firstCaught); + } return disposeCount; } @@ -634,7 +662,7 @@ public class GLDrawableHelper { init( listener, drawable, sendReshape, 0==i /* setViewport */); } } else { - // Expose same GL initialization if not using GLEventListener + // Expose same GL initialization if not using any GLEventListener drawable.getGL().glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); } } @@ -643,7 +671,7 @@ public class GLDrawableHelper { public final void display(final GLAutoDrawable drawable) { displayImpl(drawable); // runForAllGLEventListener(drawable, displayAction); - if( glRunnables.size()>0 && !execGLRunnables(drawable) ) { // glRunnables volatile OK; execGL.. only executed if size > 0 + if( glRunnableCount > 0 && !execGLRunnables(drawable) ) { // glRunnableCount volatile OK; execGL.. only executed if size > 0 displayImpl(drawable); // runForAllGLEventListener(drawable, displayAction); } @@ -701,11 +729,10 @@ public class GLDrawableHelper { } } if(setViewport) { - final GL gl = drawable.getGL(); - final int glerr0 = gl.glGetError(); - if( GL.GL_NO_ERROR != glerr0 ) { - System.err.println("Info: GLDrawableHelper.reshape: pre-exisiting GL error 0x"+Integer.toHexString(glerr0)); - if(DEBUG) { + if( GLContext.DEBUG_GL || DEBUG ) { + final int glerr0 = drawable.getGL().glGetError(); + if( GL.GL_NO_ERROR != glerr0 ) { + System.err.println("Info: GLDrawableHelper.reshape: pre-exisiting GL error 0x"+Integer.toHexString(glerr0)); Thread.dumpStack(); } } @@ -723,43 +750,29 @@ public class GLDrawableHelper { } private final boolean execGLRunnables(final GLAutoDrawable drawable) { // glRunnables.size()>0 - boolean res = true; // swap one-shot list asap final ArrayList<GLRunnableTask> _glRunnables; synchronized(glRunnablesLock) { - if(glRunnables.size()>0) { + if( glRunnables.size() > 0 ) { + glRunnableCount = 0; _glRunnables = glRunnables; glRunnables = new ArrayList<GLRunnableTask>(); } else { - _glRunnables = null; + return true; } } - - if(null!=_glRunnables) { - for (int i=0; i < _glRunnables.size(); i++) { - res = _glRunnables.get(i).run(drawable) && res; - } + boolean res = true; + for (int i=0; i < _glRunnables.size(); i++) { + res = _glRunnables.get(i).run(drawable) && res; } return res; } public final void flushGLRunnables() { - if(glRunnables.size()>0) { // volatile OK - // swap one-shot list asap - final ArrayList<GLRunnableTask> _glRunnables; - synchronized(glRunnablesLock) { - if(glRunnables.size()>0) { - _glRunnables = glRunnables; - glRunnables = new ArrayList<GLRunnableTask>(); - } else { - _glRunnables = null; - } - } - - if(null!=_glRunnables) { - for (int i=0; i < _glRunnables.size(); i++) { - _glRunnables.get(i).flush(); - } + synchronized(glRunnablesLock) { + glRunnableCount = 0; + while( glRunnables.size() > 0 ) { + glRunnables.remove(0).flush(); } } } @@ -795,6 +808,30 @@ public class GLDrawableHelper { return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() : false ; } + public static final boolean isLockedByOtherThread(final GLAutoDrawable d) { + final Thread currentThread = Thread.currentThread(); + final Thread upstreamLockOwner = d.getUpstreamLock().getOwner(); + if( null != upstreamLockOwner && currentThread != upstreamLockOwner ) { + return true; + } else { + final NativeSurface s = d.getNativeSurface(); + final Thread surfaceLockOwner = null != s ? s.getSurfaceLockOwner() : null; + return null != surfaceLockOwner && currentThread != surfaceLockOwner; + } + } + + public static final boolean isLockedByThisThread(final GLAutoDrawable d) { + final Thread currentThread = Thread.currentThread(); + final Thread upstreamLockOwner = d.getUpstreamLock().getOwner(); + if( currentThread == upstreamLockOwner ) { + return true; + } else { + final NativeSurface s = d.getNativeSurface(); + final Thread surfaceLockOwner = null != s ? s.getSurfaceLockOwner() : null; + return currentThread == surfaceLockOwner; + } + } + /** * <p> * If <code>wait</code> is <code>true</code> the call blocks until the <code>glRunnable</code> @@ -805,13 +842,32 @@ public class GLDrawableHelper { * the call is ignored and returns <code>false</code>.<br> * This helps avoiding deadlocking the caller. * </p> + * <p> + * <pre> + * 0 == deferredHere && 0 == isGLThread -> display() will issue on GL thread, blocking! + * + * deferredHere wait isGLThread lockedByThisThread Note + * OK 0 x 1 x + * OK 0 x 0 0 + * ERROR 0 x 0 1 Will be deferred on GL thread by display() (blocking), + * but locked by this thread -> ERROR + * + * 1 0 x x All good, due to no wait, non blocking + * + * 1 1 1 0 + * 1 1 0 0 + * SWITCH 1 1 1 1 Run immediately, don't defer since locked by this thread, but isGLThread + * ERROR 1 1 0 1 Locked by this thread, but _not_ isGLThread -> ERROR + * </pre> + * </p> * * @param drawable the {@link GLAutoDrawable} to be used * @param wait if <code>true</code> block until execution of <code>glRunnable</code> is finished, otherwise return immediatly w/o waiting * @param glRunnable the {@link GLRunnable} to execute within {@link #display()} * @return <code>true</code> if the {@link GLRunnable} has been processed or queued, otherwise <code>false</code>. + * @throws IllegalStateException in case the drawable is locked by this thread, no animator is running on another thread and <code>wait</code> is <code>true</code>. */ - public final boolean invoke(final GLAutoDrawable drawable, boolean wait, final GLRunnable glRunnable) { + public final boolean invoke(final GLAutoDrawable drawable, boolean wait, final GLRunnable glRunnable) throws IllegalStateException { if( null == glRunnable || null == drawable || wait && ( !drawable.isRealized() || null==drawable.getContext() ) ) { return false; @@ -821,18 +877,35 @@ public class GLDrawableHelper { final Object rTaskLock = new Object(); Throwable throwable = null; synchronized(rTaskLock) { - final boolean deferred; + boolean deferredHere; synchronized(glRunnablesLock) { - deferred = isAnimatorAnimatingOnOtherThread(); - if(!deferred) { - wait = false; // don't wait if exec immediatly + final boolean isGLThread = drawable.isThreadGLCapable(); + deferredHere = isAnimatorAnimatingOnOtherThread(); + if( deferredHere ) { + if( wait && isLockedByThisThread(drawable) ) { + if( isGLThread ) { + // Run immediately, don't defer since locked by this thread, but isGLThread + deferredHere = false; + wait = false; + } else { + // Locked by this thread, but _not_ isGLThread -> ERROR + throw new IllegalStateException("Deferred, wait, isLocked on current and not GL-Thread: thread "+Thread.currentThread()); + } + } + } else { + if( !isGLThread && isLockedByThisThread(drawable) ) { + // Will be deferred on GL thread by display() (blocking), but locked by this thread -> ERROR + throw new IllegalStateException("Not deferred, isLocked on current and not GL-Thread: thread "+Thread.currentThread()); + } + wait = false; // don't wait if exec immediately } rTask = new GLRunnableTask(glRunnable, wait ? rTaskLock : null, wait /* catch Exceptions if waiting for result */); + glRunnableCount++; glRunnables.add(rTask); } - if( !deferred ) { + if( !deferredHere ) { drawable.display(); } else if( wait ) { try { @@ -851,7 +924,16 @@ public class GLDrawableHelper { return true; } - public final boolean invoke(final GLAutoDrawable drawable, boolean wait, final List<GLRunnable> newGLRunnables) { + /** + * @see #invoke(GLAutoDrawable, boolean, GLRunnable) + * + * @param drawable + * @param wait + * @param newGLRunnables + * @return + * @throws IllegalStateException + */ + public final boolean invoke(final GLAutoDrawable drawable, boolean wait, final List<GLRunnable> newGLRunnables) throws IllegalStateException { if( null == newGLRunnables || newGLRunnables.size() == 0 || null == drawable || wait && ( !drawable.isRealized() || null==drawable.getContext() ) ) { return false; @@ -862,21 +944,39 @@ public class GLDrawableHelper { final Object rTaskLock = new Object(); Throwable throwable = null; synchronized(rTaskLock) { - final boolean deferred; + boolean deferredHere; synchronized(glRunnablesLock) { - deferred = isAnimatorAnimatingOnOtherThread() || !drawable.isRealized(); - if(!deferred) { + final boolean isGLThread = drawable.isThreadGLCapable(); + deferredHere = isAnimatorAnimatingOnOtherThread(); + if( deferredHere ) { + if( wait && isLockedByThisThread(drawable) ) { + if( isGLThread ) { + // Run immediately, don't defer since locked by this thread, but isGLThread + deferredHere = false; + wait = false; + } else { + // Locked by this thread, but _not_ isGLThread -> ERROR + throw new IllegalStateException("Deferred, wait, isLocked on current and not GL-Thread: thread "+Thread.currentThread()); + } + } + } else { + if( !isGLThread && isLockedByThisThread(drawable) ) { + // Will be deferred on GL thread by display() (blocking), but locked by this thread -> ERROR + throw new IllegalStateException("Not deferred, isLocked on current and not GL-Thread: thread "+Thread.currentThread()); + } wait = false; // don't wait if exec immediately } for(int i=0; i<count-1; i++) { + glRunnableCount++; glRunnables.add( new GLRunnableTask(newGLRunnables.get(i), null, false) ); } rTask = new GLRunnableTask(newGLRunnables.get(count-1), wait ? rTaskLock : null, wait /* catch Exceptions if waiting for result */); + glRunnableCount++; glRunnables.add(rTask); } - if( !deferred ) { + if( !deferredHere ) { drawable.display(); } else if( wait ) { try { @@ -900,6 +1000,7 @@ public class GLDrawableHelper { return; } synchronized(glRunnablesLock) { + glRunnableCount++; glRunnables.add( new GLRunnableTask(glRunnable, null, false) ); } } @@ -961,8 +1062,8 @@ public class GLDrawableHelper { try { forceNativeRelease(context); } catch (final Throwable ex) { - ex.printStackTrace(); - throw new GLException(ex); + flushGLRunnables(); + throw GLException.newGLException(ex); } } exclusiveContextThread = t; @@ -980,7 +1081,21 @@ public class GLDrawableHelper { return exclusiveContextThread; } - private static final ThreadLocal<Runnable> perThreadInitAction = new ThreadLocal<Runnable>(); + private static final ThreadLocal<WeakReference<Runnable>> perThreadInitAction = new ThreadLocal<WeakReference<Runnable>>(); + private static final Runnable getLastInitAction() { + final WeakReference<Runnable> lastInitActionWR = perThreadInitAction.get(); + if( null != lastInitActionWR ) { + final Runnable lastInitAction = lastInitActionWR.get(); + if( null == lastInitAction ) { + perThreadInitAction.set(null); + } + return lastInitAction; + } + return null; + } + private static final void setLastInitAction(final Runnable initAction) { + perThreadInitAction.set(new WeakReference<Runnable>(initAction)); + } /** Principal helper method which runs a Runnable with the context made current. This could have been made part of GLContext, but a @@ -1004,8 +1119,7 @@ public class GLDrawableHelper { final Runnable initAction) { if(null==context) { if (DEBUG) { - final Exception e = new GLException(getThreadName()+" Info: GLDrawableHelper " + this + ".invokeGL(): NULL GLContext"); - e.printStackTrace(); + GLException.dumpThrowable("informal", new GLException("Info: GLDrawableHelper " + this + ".invokeGL(): NULL GLContext")); } return; } @@ -1030,9 +1144,11 @@ public class GLDrawableHelper { * @param autoDrawable * @param context * @param destroyContext destroy context in the end while holding the lock + * @throws GLException caused by {@link GLEventListener#dispose(GLAutoDrawable)} or context closing + * */ public final void disposeGL(final GLAutoDrawable autoDrawable, - final GLContext context, final boolean destroyContext) { + final GLContext context, final boolean destroyContext) throws GLException { // Support for recursive makeCurrent() calls as well as calling // other drawables' display() methods from within another one's GLContext lastContext = GLContext.getCurrent(); @@ -1042,20 +1158,27 @@ public class GLDrawableHelper { lastContext = null; } else { // utilize recursive locking - lastInitAction = perThreadInitAction.get(); + lastInitAction = getLastInitAction(); lastContext.release(); } } + GLException disposeCaught = null; + Throwable contextCloseCaught = null; + int res; try { res = context.makeCurrent(); if (GLContext.CONTEXT_NOT_CURRENT != res) { if(GLContext.CONTEXT_CURRENT_NEW == res) { - throw new GLException(getThreadName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context was not created (new ctx): "+context); + throw new GLException(GLDrawableHelper.getThreadName()+" GLDrawableHelper " + this + ".invokeGL(): Dispose case (no init action given): Native context was not created (new ctx): "+context); } if( listeners.size() > 0 && null != autoDrawable ) { - disposeAllGLEventListener(autoDrawable, false); + try { + disposeAllGLEventListener(autoDrawable, false); + } catch(final GLException t) { + disposeCaught = t; + } } } } finally { @@ -1065,17 +1188,26 @@ public class GLDrawableHelper { } else { forceNativeRelease(context); } - flushGLRunnables(); - } catch (final Exception e) { - System.err.println("Caught exception on thread "+getThreadName()); - e.printStackTrace(); + } catch (final Throwable t) { + contextCloseCaught = t; } + flushGLRunnables(); // always flush GLRunnables at dispose + if (lastContext != null) { final int res2 = lastContext.makeCurrent(); if (null != lastInitAction && res2 == GLContext.CONTEXT_CURRENT_NEW) { lastInitAction.run(); } } + if( null != disposeCaught ) { + if( null != contextCloseCaught ) { + GLException.dumpThrowable("subsequent", contextCloseCaught); + } + throw disposeCaught; + } + if( null != contextCloseCaught ) { + throw GLException.newGLException(contextCloseCaught); + } } } @@ -1085,6 +1217,9 @@ public class GLDrawableHelper { final Runnable initAction) { final Thread currentThread = Thread.currentThread(); + Throwable glEventListenerCaught = null; + Throwable contextReleaseCaught = null; + // Exclusive Cases: // 1: lock - unlock : default // 2: lock - - : exclusive, not locked yet @@ -1116,7 +1251,7 @@ public class GLDrawableHelper { lastContext = null; } else { // utilize recursive locking - lastInitAction = perThreadInitAction.get(); + lastInitAction = getLastInitAction(); lastContext.release(); } } @@ -1131,7 +1266,7 @@ public class GLDrawableHelper { } if (GLContext.CONTEXT_NOT_CURRENT != res) { try { - perThreadInitAction.set(initAction); + setLastInitAction(initAction); if (GLContext.CONTEXT_CURRENT_NEW == res) { if (DEBUG) { System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction"); @@ -1142,6 +1277,8 @@ public class GLDrawableHelper { if ( autoSwapBufferMode ) { drawable.swapBuffers(); } + } catch (final Throwable t) { + glEventListenerCaught = t; } finally { if( _releaseExclusiveThread ) { exclusiveContextThread = null; @@ -1152,9 +1289,8 @@ public class GLDrawableHelper { if( releaseContext ) { try { context.release(); - } catch (final Exception e) { - System.err.println("Caught exception on thread "+getThreadName()); - e.printStackTrace(); + } catch (final Throwable t) { + contextReleaseCaught = t; } } } @@ -1166,6 +1302,17 @@ public class GLDrawableHelper { lastInitAction.run(); } } + if( null != glEventListenerCaught ) { + flushGLRunnables(); + if( null != contextReleaseCaught ) { + GLException.dumpThrowable("subsequent", contextReleaseCaught); + } + throw GLException.newGLException(glEventListenerCaught); + } + if( null != contextReleaseCaught ) { + flushGLRunnables(); + throw GLException.newGLException(contextReleaseCaught); + } } } @@ -1175,6 +1322,9 @@ public class GLDrawableHelper { final Runnable initAction) { final Thread currentThread = Thread.currentThread(); + Throwable glEventListenerCaught = null; + Throwable contextReleaseCaught = null; + // Exclusive Cases: // 1: lock - unlock : default // 2: lock - - : exclusive, not locked yet @@ -1205,7 +1355,7 @@ public class GLDrawableHelper { lastContext = null; } else { // utilize recursive locking - lastInitAction = perThreadInitAction.get(); + lastInitAction = getLastInitAction(); lastContext.release(); } } @@ -1229,7 +1379,7 @@ public class GLDrawableHelper { } if (GLContext.CONTEXT_NOT_CURRENT != res) { try { - perThreadInitAction.set(initAction); + setLastInitAction(initAction); if (GLContext.CONTEXT_CURRENT_NEW == res) { if (DEBUG) { System.err.println("GLDrawableHelper " + this + ".invokeGL(): Running initAction"); @@ -1246,6 +1396,8 @@ public class GLDrawableHelper { tdX = System.currentTimeMillis(); tdS = tdX - tdS; // swapBuffers } + } catch (final Throwable t) { + glEventListenerCaught = t; } finally { if( _releaseExclusiveThread ) { exclusiveContextSwitch = 0; @@ -1258,9 +1410,8 @@ public class GLDrawableHelper { try { context.release(); ctxReleased = true; - } catch (final Exception e) { - System.err.println("Caught exception on thread "+getThreadName()); - e.printStackTrace(); + } catch (final Throwable t) { + contextReleaseCaught = t; } } } @@ -1273,11 +1424,21 @@ public class GLDrawableHelper { lastInitAction.run(); } } + if( null != glEventListenerCaught ) { + flushGLRunnables(); + if( null != contextReleaseCaught ) { + GLException.dumpThrowable("subsequent", contextReleaseCaught); + } + throw GLException.newGLException(glEventListenerCaught); + } + if( null != contextReleaseCaught ) { + flushGLRunnables(); + throw GLException.newGLException(contextReleaseCaught); + } } final long td = System.currentTimeMillis() - t0; System.err.println("td0 "+td+"ms, fps "+(1.0/(td/1000.0))+", td-makeCurrent: "+tdA+"ms, td-render "+tdR+"ms, td-swap "+tdS+"ms, td-release "+tdX+"ms, ctx claimed: "+ctxClaimed+", ctx release: "+ctxReleased+", ctx destroyed "+ctxDestroyed); } protected static String getThreadName() { return Thread.currentThread().getName(); } - } diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java b/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java index 3bb22612f..544aaf064 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableImpl.java @@ -130,10 +130,11 @@ public abstract class GLDrawableImpl implements GLDrawable { } @Override - public GLCapabilitiesImmutable getChosenGLCapabilities() { + public final GLCapabilitiesImmutable getChosenGLCapabilities() { return (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getChosenCapabilities(); } + @Override public final GLCapabilitiesImmutable getRequestedGLCapabilities() { return requestedCapabilities; } diff --git a/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java b/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java index 6046527d1..991a351e6 100644 --- a/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLFBODrawableImpl.java @@ -15,6 +15,7 @@ import com.jogamp.common.util.PropertyAccess; import com.jogamp.common.util.VersionUtil; import com.jogamp.nativewindow.MutableGraphicsConfiguration; import com.jogamp.opengl.FBObject; +import com.jogamp.opengl.GLRendererQuirks; import com.jogamp.opengl.FBObject.Attachment; import com.jogamp.opengl.FBObject.Colorbuffer; import com.jogamp.opengl.FBObject.TextureAttachment; @@ -44,13 +45,15 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { static { Debug.initSingleton(); DEBUG = GLDrawableImpl.DEBUG || Debug.debug("FBObject"); - DEBUG_SWAP = DEBUG || PropertyAccess.isPropertyDefined("jogl.debug.FBObject.Swap", true); + DEBUG_SWAP = PropertyAccess.isPropertyDefined("jogl.debug.FBObject.Swap", true); } private final GLDrawableImpl parent; private GLCapabilitiesImmutable origParentChosenCaps; private boolean initialized; + private int maxSamples; + private int fboModeBits; private int texUnit; private int samples; private boolean fboResetQuirk; @@ -59,9 +62,9 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { private int fboIBack; // points to GL_BACK buffer private int fboIFront; // points to GL_FRONT buffer private int pendingFBOReset = -1; - /** Indicated whether the FBO is bound. */ + /** Indicates whether the FBO is bound. */ private boolean fboBound; - /** Indicated whether the FBO is swapped, resets to false after makeCurrent -> contextMadeCurrent. */ + /** Indicates whether the FBO is swapped, resets to false after makeCurrent -> contextMadeCurrent. */ private boolean fboSwapped; /** dump fboResetQuirk info only once pre ClassLoader and only in DEBUG mode */ @@ -89,17 +92,79 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { final GLCapabilitiesImmutable fboCaps, final int textureUnit) { super(factory, surface, fboCaps, false); this.initialized = false; + this.fboModeBits = FBOMODE_USE_TEXTURE; this.parent = parent; this.origParentChosenCaps = getChosenGLCapabilities(); // just to avoid null, will be reset at initialize(..) this.texUnit = textureUnit; this.samples = fboCaps.getNumSamples(); - fboResetQuirk = false; + this.fboResetQuirk = false; + this.swapBufferContext = null; + } - // default .. // TODO: Add or remove TEXTURE (only) DoubleBufferMode support - // this.doubleBufferMode = ( samples > 0 || fboCaps.getDoubleBuffered() ) ? DoubleBufferMode.FBO : DoubleBufferMode.NONE ; + private final void setupFBO(final GL gl, final int idx, final int width, final int height, final int samples, + final boolean useAlpha, final int depthBits, final int stencilBits, + final boolean useTexture, final boolean realUnbind) { + final FBObject fbo = new FBObject(); + fbos[idx] = fbo; - this.swapBufferContext = null; + final boolean useDepth = depthBits > 0; + final boolean useStencil = stencilBits > 0; + + fbo.init(gl, width, height, samples); + if(fbo.getNumSamples() != samples) { + throw new InternalError("Sample number mismatch: "+samples+", fbos["+idx+"] "+fbo); + } + if(samples > 0 || !useTexture) { + fbo.attachColorbuffer(gl, 0, useAlpha); + } else { + fbo.attachTexture2D(gl, 0, useAlpha); + } + if( useStencil ) { + if( useDepth ) { + fbo.attachRenderbuffer(gl, Attachment.Type.DEPTH_STENCIL, depthBits); + } else { + fbo.attachRenderbuffer(gl, Attachment.Type.STENCIL, stencilBits); + } + } else if( useDepth ) { + fbo.attachRenderbuffer(gl, Attachment.Type.DEPTH, depthBits); + } + if(samples > 0) { + final FBObject ssink = new FBObject(); + { + ssink.init(gl, width, height, 0); + if( !useTexture ) { + ssink.attachColorbuffer(gl, 0, useAlpha); + } else { + ssink.attachTexture2D(gl, 0, useAlpha); + } + if( useStencil ) { + if( useDepth ) { + ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH_STENCIL, depthBits); + } else { + ssink.attachRenderbuffer(gl, Attachment.Type.STENCIL, stencilBits); + } + } else if( useDepth ) { + ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, depthBits); + } + } + fbo.setSamplingSink(ssink); + fbo.resetSamplingSink(gl); // validate + } + // Clear the framebuffer allowing defined state not exposing previous content. + // Also remedy for Bug 1020, i.e. OSX/Nvidia's FBO needs to be cleared before blitting, + // otherwise first MSAA frame lacks antialiasing. + fbo.bind(gl); + if( useDepth ) { + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + } else { + gl.glClear(GL.GL_COLOR_BUFFER_BIT); + } + if( realUnbind ) { + fbo.unbind(gl); + } else { + fbo.markUnbound(); + } } private final void initialize(final boolean realize, final GL gl) { @@ -116,7 +181,7 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { if(realize) { final GLCapabilities chosenFBOCaps = (GLCapabilities) getChosenGLCapabilities(); // cloned at setRealized(true) - final int maxSamples = gl.getMaxRenderbufferSamples(); + maxSamples = gl.getMaxRenderbufferSamples(); // if > 0 implies fullFBOSupport { final int newSamples = samples <= maxSamples ? samples : maxSamples; if(DEBUG) { @@ -138,25 +203,21 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { fboIBack = 0; // head fboIFront = fbos.length - 1; // tail - for(int i=0; i<fbosN; i++) { - fbos[i] = new FBObject(); - fbos[i].reset(gl, getSurfaceWidth(), getSurfaceHeight(), samples, false); - if(fbos[i].getNumSamples() != samples) { - throw new InternalError("Sample number mismatch: "+samples+", fbos["+i+"] "+fbos[i]); - } - if(samples > 0) { - fbos[i].attachColorbuffer(gl, 0, chosenFBOCaps.getAlphaBits()>0); - } else { - fbos[i].attachTexture2D(gl, 0, chosenFBOCaps.getAlphaBits()>0); - } - if( chosenFBOCaps.getStencilBits() > 0 ) { - fbos[i].attachRenderbuffer(gl, Attachment.Type.DEPTH_STENCIL, 24); - } else { - fbos[i].attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); - } + if( 0 == ( FBOMODE_USE_TEXTURE & fboModeBits ) && + gl.getContext().hasRendererQuirk(GLRendererQuirks.BuggyColorRenderbuffer) ) { + // GLRendererQuirks.BuggyColorRenderbuffer also disables MSAA, i.e. full FBO support + fboModeBits |= FBOMODE_USE_TEXTURE; } - fbos[fboIFront].resetSamplingSink(gl); + final boolean useTexture = 0 != ( FBOMODE_USE_TEXTURE & fboModeBits ); + final boolean useAlpha = chosenFBOCaps.getAlphaBits() > 0; + final int width = getSurfaceWidth(); + final int height = getSurfaceHeight(); + + for(int i=0; i<fbosN; i++) { + setupFBO(gl, i, width, height, samples, useAlpha, + chosenFBOCaps.getDepthBits(), chosenFBOCaps.getStencilBits(), useTexture, fbosN-1==i); + } fbos[0].formatToGLCapabilities(chosenFBOCaps); chosenFBOCaps.setDoubleBuffered( chosenFBOCaps.getDoubleBuffered() || samples > 0 ); } else { @@ -180,29 +241,26 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { swapBufferContext = sbc; } - private final void reset(final GL gl, final int idx, final int width, final int height, final int samples, final int alphaBits, final int stencilBits) { + private final void reset(final GL gl, final int idx, final int width, final int height, final int samples, + final boolean useAlpha, final int depthBits, final int stencilBits) { if( !fboResetQuirk ) { try { - fbos[idx].reset(gl, width, height, samples, false); + fbos[idx].reset(gl, width, height, samples); if(fbos[idx].getNumSamples() != samples) { throw new InternalError("Sample number mismatch: "+samples+", fbos["+idx+"] "+fbos[idx]); } return; } catch (final GLException e) { fboResetQuirk = true; - if(DEBUG) { + if( DEBUG ) { if(!resetQuirkInfoDumped) { resetQuirkInfoDumped = true; System.err.println("GLFBODrawable: FBO Reset failed: "+e.getMessage()); System.err.println("GLFBODrawable: Enabling FBOResetQuirk, due to GL driver bug."); final JoglVersion joglVersion = JoglVersion.getInstance(); - if(DEBUG) { - System.err.println(VersionUtil.getPlatformInfo()); - System.err.println(joglVersion.toString()); - System.err.println(JoglVersion.getGLInfo(gl, null)); - } else { - System.err.println(joglVersion.getBriefOSGLBuildInfo(gl, null)); - } + System.err.println(VersionUtil.getPlatformInfo()); + System.err.println(joglVersion.toString()); + System.err.println(JoglVersion.getGLInfo(gl, null)); e.printStackTrace(); } } @@ -211,21 +269,8 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { } // resetQuirk fallback fbos[idx].destroy(gl); - fbos[idx] = new FBObject(); - fbos[idx].reset(gl, getSurfaceWidth(), getSurfaceHeight(), samples, false); - if(fbos[idx].getNumSamples() != samples) { - throw new InternalError("Sample number mismatch: "+samples+", fbos["+idx+"] "+fbos[idx]); - } - if(samples > 0) { - fbos[idx].attachColorbuffer(gl, 0, alphaBits>0); - } else { - fbos[idx].attachTexture2D(gl, 0, alphaBits>0); - } - if( stencilBits > 0 ) { - fbos[idx].attachRenderbuffer(gl, Attachment.Type.DEPTH_STENCIL, 24); - } else { - fbos[idx].attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); - } + final boolean useTexture = 0 != ( FBOMODE_USE_TEXTURE & fboModeBits ); + setupFBO(gl, idx, width, height, samples, useAlpha, depthBits, stencilBits, useTexture, true); } private final void reset(final GL gl, int newSamples) throws GLException { @@ -248,7 +293,6 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { fboBound = false; // clear bound-flag immediatly, caused by contextMadeCurrent(..) - otherwise we would swap @ release fboSwapped = false; try { - final int maxSamples = gl.getMaxRenderbufferSamples(); newSamples = newSamples <= maxSamples ? newSamples : maxSamples; if(0==samples && 0<newSamples || 0<samples && 0==newSamples) { @@ -270,7 +314,7 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getChosenCapabilities(); for(int i=0; i<fbos.length; i++) { if( pendingFBOReset != i ) { - reset(gl, i, nWidth, nHeight, samples, caps.getAlphaBits(), caps.getStencilBits()); + reset(gl, i, nWidth, nHeight, samples, caps.getAlphaBits()>0, caps.getDepthBits(), caps.getStencilBits()); } } final GLCapabilities fboCapsNative = (GLCapabilities) surface.getGraphicsConfiguration().getChosenCapabilities(); @@ -289,10 +333,10 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { } } if(null != tFBO) { - throw new GLException("GLFBODrawableImpl.reset(..) FBObject.reset(..) exception", tFBO); + throw GLException.newGLException(tFBO); } if(null != tGL) { - throw new GLException("GLFBODrawableImpl.reset(..) GLContext.release() exception", tGL); + throw GLException.newGLException(tGL); } if(DEBUG) { System.err.println("GLFBODrawableImpl.reset(newSamples "+newSamples+"): END "+this); @@ -397,7 +441,8 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { // Safely reset the previous front FBO - after completing propagating swap if(0 <= pendingFBOReset) { final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) surface.getGraphicsConfiguration().getChosenCapabilities(); - reset(glc.getGL(), pendingFBOReset, getSurfaceWidth(), getSurfaceHeight(), samples, caps.getAlphaBits(), caps.getStencilBits()); + reset(glc.getGL(), pendingFBOReset, getSurfaceWidth(), getSurfaceHeight(), samples, + caps.getAlphaBits()>0, caps.getDepthBits(), caps.getStencilBits()); pendingFBOReset = -1; } } @@ -414,17 +459,16 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { fboIBack = ( fboIBack + 1 ) % fbos.length; final Colorbuffer colorbuffer = samples > 0 ? fbos[fboIFront].getSamplingSink() : fbos[fboIFront].getColorbuffer(0); + if(null == colorbuffer) { + throw new GLException("Front colorbuffer is null: samples "+samples+", "+this); + } final TextureAttachment texAttachment; - if(colorbuffer instanceof TextureAttachment) { - texAttachment = (TextureAttachment) colorbuffer; + if( colorbuffer.isTextureAttachment() ) { + texAttachment = colorbuffer.getTextureAttachment(); + gl.glActiveTexture(GL.GL_TEXTURE0 + texUnit); } else { - if(null == colorbuffer) { - throw new GLException("Front colorbuffer is null: samples "+samples+", "+this); - } else { - throw new GLException("Front colorbuffer is not a texture: "+colorbuffer.getClass().getName()+": samples "+samples+", "+colorbuffer+", "+this); - } + texAttachment = null; } - gl.glActiveTexture(GL.GL_TEXTURE0 + texUnit); fbos[fboIFront].use(gl, texAttachment); /* Included in above use command: @@ -447,6 +491,19 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { } @Override + public final void setFBOMode(final int modeBits) throws IllegalStateException { + if( isInitialized() ) { + throw new IllegalStateException("Already initialized: "+this); + } + this.fboModeBits = modeBits; + } + + @Override + public final int getFBOMode() { + return fboModeBits; + } + + @Override public final void resetSize(final GL gl) throws GLException { reset(gl, samples); } @@ -468,9 +525,12 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { } @Override - public final int setNumBuffers(final int bufferCount) throws GLException { + public final int setNumBuffers(final int bufferCount) throws IllegalStateException, GLException { + if( isInitialized() ) { + throw new IllegalStateException("Already initialized: "+this); + } // FIXME: Implement - return bufferCount; + return GLFBODrawableImpl.bufferCount; } @Override @@ -519,24 +579,24 @@ public class GLFBODrawableImpl extends GLDrawableImpl implements GLFBODrawable { } @Override - public final TextureAttachment getTextureBuffer(final int bufferName) throws IllegalArgumentException { + public final Colorbuffer getColorbuffer(final int bufferName) throws IllegalArgumentException { if(!initialized) { return null; } - final TextureAttachment res; + final Colorbuffer res; switch(bufferName) { case GL.GL_FRONT: if( samples > 0 ) { res = fbos[0].getSamplingSink(); } else { - res = (TextureAttachment) fbos[fboIFront].getColorbuffer(0); + res = fbos[fboIFront].getColorbuffer(0); } break; case GL.GL_BACK: if( samples > 0 ) { throw new IllegalArgumentException("Cannot access GL_BACK buffer of MSAA FBO: "+this); } else { - res = (TextureAttachment) fbos[fboIBack].getColorbuffer(0); + res = fbos[fboIBack].getColorbuffer(0); } break; default: diff --git a/src/jogl/classes/jogamp/opengl/GLOffscreenAutoDrawableImpl.java b/src/jogl/classes/jogamp/opengl/GLOffscreenAutoDrawableImpl.java index 95c4ceb98..721dc7384 100644 --- a/src/jogl/classes/jogamp/opengl/GLOffscreenAutoDrawableImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLOffscreenAutoDrawableImpl.java @@ -102,7 +102,7 @@ public class GLOffscreenAutoDrawableImpl extends GLAutoDrawableDelegate implemen } @Override - public final int setNumBuffers(final int bufferCount) throws GLException { + public final int setNumBuffers(final int bufferCount) throws /* IllegalStateException, */ GLException { return ((GLFBODrawableImpl)drawable).setNumBuffers(bufferCount); } @@ -128,13 +128,24 @@ public class GLOffscreenAutoDrawableImpl extends GLAutoDrawableDelegate implemen } @Override - public final FBObject.TextureAttachment getTextureBuffer(final int bufferName) { - return ((GLFBODrawableImpl)drawable).getTextureBuffer(bufferName); + public final FBObject.Colorbuffer getColorbuffer(final int bufferName) { + return ((GLFBODrawableImpl)drawable).getColorbuffer(bufferName); } @Override public void resetSize(final GL gl) throws GLException { ((GLFBODrawableImpl)drawable).resetSize(gl); } + + @Override + public final void setFBOMode(final int modeBits) throws IllegalStateException { + ((GLFBODrawableImpl)drawable).setFBOMode(modeBits); + + } + + @Override + public final int getFBOMode() { + return ((GLFBODrawableImpl)drawable).getFBOMode(); + } } } diff --git a/src/jogl/classes/jogamp/opengl/GLRunnableTask.java b/src/jogl/classes/jogamp/opengl/GLRunnableTask.java index 0ceef6bf7..ca1c1869e 100644 --- a/src/jogl/classes/jogamp/opengl/GLRunnableTask.java +++ b/src/jogl/classes/jogamp/opengl/GLRunnableTask.java @@ -90,8 +90,10 @@ public class GLRunnableTask implements GLRunnable { /** * Simply flush this task and notify a waiting executor. + * <p> * The executor which might have been blocked until notified * will be unblocked and the task removed from the queue. + * </p> * * @see #isFlushed() * @see #isInQueue() diff --git a/src/jogl/classes/jogamp/opengl/GLStateTracker.java b/src/jogl/classes/jogamp/opengl/GLStateTracker.java index d532a2567..dc49b35f4 100644 --- a/src/jogl/classes/jogamp/opengl/GLStateTracker.java +++ b/src/jogl/classes/jogamp/opengl/GLStateTracker.java @@ -68,7 +68,7 @@ public class GLStateTracker { private IntIntHashMap pixelStateMap; private final ArrayList<SavedState> stack; - private static class SavedState { + static class SavedState { /** * Empty pixel-store state @@ -78,15 +78,14 @@ public class GLStateTracker { /** * set (client) pixel-store state, deep copy */ - private final void setPixelStateMap(final IntIntHashMap pixelStateMap) { + final void setPixelStateMap(final IntIntHashMap pixelStateMap) { this.pixelStateMap = (IntIntHashMap) pixelStateMap.clone(); } /** * get (client) pixel-store state, return reference */ - private final IntIntHashMap getPixelStateMap() { return pixelStateMap; } - + final IntIntHashMap getPixelStateMap() { return pixelStateMap; } } @@ -163,10 +162,11 @@ public class GLStateTracker { if(null==state) { throw new GLException("null stack element (remaining stack size "+stack.size()+")"); } + final IntIntHashMap statePixelStateMap = state.getPixelStateMap(); - if ( null != state.getPixelStateMap() ) { + if ( null != statePixelStateMap ) { // use pulled client pixel-store state from stack - pixelStateMap = state.getPixelStateMap(); + pixelStateMap = statePixelStateMap; } // else: empty-slot, not pushed by GL_CLIENT_PIXEL_STORE_BIT } } diff --git a/src/jogl/classes/jogamp/opengl/ThreadingImpl.java b/src/jogl/classes/jogamp/opengl/ThreadingImpl.java index 2b017e8e9..7b405e524 100644 --- a/src/jogl/classes/jogamp/opengl/ThreadingImpl.java +++ b/src/jogl/classes/jogamp/opengl/ThreadingImpl.java @@ -41,6 +41,7 @@ import java.security.PrivilegedAction; import javax.media.nativewindow.NativeWindowFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; +import javax.media.opengl.Threading.Mode; import com.jogamp.common.JogampRuntimeException; import com.jogamp.common.util.PropertyAccess; @@ -49,16 +50,6 @@ import com.jogamp.common.util.ReflectionUtil; /** Implementation of the {@link javax.media.opengl.Threading} class. */ public class ThreadingImpl { - public enum Mode { - MT(0), ST_AWT(1), ST_WORKER(2); - - public final int id; - - Mode(final int id){ - this.id = id; - } - } - protected static final boolean DEBUG = Debug.debug("Threading"); private static boolean singleThreaded; @@ -93,28 +84,23 @@ public class ThreadingImpl { _isX11 = NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(false); - // default setting - singleThreaded = true; - mode = ( hasAWT ? Mode.ST_AWT : Mode.ST_WORKER ); - if (singleThreadProp != null) { if (singleThreadProp.equals("true") || singleThreadProp.equals("auto")) { - singleThreaded = true; - mode = ( hasAWT ? Mode.ST_AWT : Mode.ST_WORKER ); + mode = ( hasAWT ? Mode.ST_AWT : Mode.MT ); } else if (singleThreadProp.equals("worker")) { - singleThreaded = true; mode = Mode.ST_WORKER; } else if (hasAWT && singleThreadProp.equals("awt")) { - singleThreaded = true; mode = Mode.ST_AWT; } else if (singleThreadProp.equals("false")) { - singleThreaded = false; mode = Mode.MT; } else { throw new RuntimeException("Unsupported value for property jogl.1thread: "+singleThreadProp+", should be [true/auto, worker, awt or false]"); } + } else { + mode = ( hasAWT ? Mode.ST_AWT : Mode.MT ); } + singleThreaded = Mode.MT != mode; ToolkitThreadingPlugin threadingPlugin=null; if(hasAWT) { @@ -155,9 +141,11 @@ public class ThreadingImpl { method. This method should be called as early as possible in an application. */ public static final void disableSingleThreading() { - singleThreaded = false; - if (Debug.verbose()) { - System.err.println("Application forced disabling of single-threading of javax.media.opengl implementation"); + if( Mode.MT != mode ) { + singleThreaded = false; + if (Debug.verbose()) { + System.err.println("Application forced disabling of single-threading of javax.media.opengl implementation"); + } } } @@ -167,22 +155,28 @@ public class ThreadingImpl { return singleThreaded; } - /** Indicates whether the current thread is the single thread on - which this implementation of the javax.media.opengl APIs - performs all of its OpenGL-related work. This method should only - be called if the single-thread model is in effect. */ + /** + * Indicates whether the current thread is capable of + * performing OpenGL-related work. + * <p> + * Method always returns <code>true</code> + * if {@link #getMode()} == {@link Mode#MT} or {@link #isSingleThreaded()} == <code>false</code>. + * </p> + */ public static final boolean isOpenGLThread() throws GLException { - if(null!=threadingPlugin) { + if( Mode.MT == mode || !singleThreaded ) { + return true; + } else if( null != threadingPlugin ) { return threadingPlugin.isOpenGLThread(); - } - - switch (mode) { - case ST_AWT: - throw new InternalError(); - case ST_WORKER: - return GLWorkerThread.isWorkerThread(); - default: - throw new InternalError("Illegal single-threading mode " + mode); + } else { + switch (mode) { + case ST_AWT: + throw new InternalError(); + case ST_WORKER: + return GLWorkerThread.isWorkerThread(); + default: + throw new InternalError("Illegal single-threading mode " + mode); + } } } @@ -213,6 +207,10 @@ public class ThreadingImpl { invokeOnWorkerThread(wait, r); break; + case MT: + r.run(); + break; + default: throw new InternalError("Illegal single-threading mode " + mode); } diff --git a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java index 24b89cd02..495887e0f 100644 --- a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java +++ b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java @@ -254,7 +254,7 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { @Override protected final void initStreamImpl(final int vid, final int aid) throws IOException { - if( null == getURI() ) { + if( null == getUri() ) { return; } if( null == mp && null == cam ) { @@ -263,7 +263,7 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { } else { int cameraId = 0; try { - cameraId = Integer.parseInt(cameraPath); + cameraId = Integer.parseInt(cameraPath.decode()); } catch (final NumberFormatException nfe) {} if( 0 <= cameraId && cameraId < Camera.getNumberOfCameras() ) { cam = Camera.open(cameraId); @@ -280,7 +280,7 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { } // else FIXME: Select aid ! // Note: Both FIXMEs seem to be n/a via Android's MediaPlayer -> Switch to API level 16 MediaCodec/MediaExtractor .. try { - final Uri _uri = Uri.parse(getURI().toString()); + final Uri _uri = Uri.parse(getUri().toString()); mp.setDataSource(StaticContext.getContext(), _uri); } catch (final IllegalArgumentException e) { throw new RuntimeException(e); @@ -293,7 +293,7 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { try { mp.prepare(); } catch (final IOException ioe) { - throw new IOException("MediaPlayer failed to process stream <"+getURI().toString()+">: "+ioe.getMessage(), ioe); + throw new IOException("MediaPlayer failed to process stream <"+getUri().toString()+">: "+ioe.getMessage(), ioe); } final int r_aid = GLMediaPlayer.STREAM_ID_NONE == aid ? GLMediaPlayer.STREAM_ID_NONE : 1 /* fake */; final String icodec = "android"; @@ -381,7 +381,7 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl { cam.setPreviewTexture(sTexFrame.surfaceTex); cam.startPreview(); } catch (final IOException ioe) { - throw new RuntimeException("MediaPlayer failed to process stream <"+getURI().toString()+">: "+ioe.getMessage(), ioe); + throw new RuntimeException("MediaPlayer failed to process stream <"+getUri().toString()+">: "+ioe.getMessage(), ioe); } } if( null != surface ) { diff --git a/src/jogl/classes/jogamp/opengl/awt/AWTThreadingPlugin.java b/src/jogl/classes/jogamp/opengl/awt/AWTThreadingPlugin.java index 26ec62785..3f8910fb5 100644 --- a/src/jogl/classes/jogamp/opengl/awt/AWTThreadingPlugin.java +++ b/src/jogl/classes/jogamp/opengl/awt/AWTThreadingPlugin.java @@ -108,6 +108,10 @@ public class AWTThreadingPlugin implements ToolkitThreadingPlugin { ThreadingImpl.invokeOnWorkerThread(wait, r); break; + case MT: + r.run(); + break; + default: throw new InternalError("Illegal single-threading mode " + ThreadingImpl.getMode()); } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java index 5a9a30313..2edb22314 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java @@ -184,7 +184,7 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl { } } if( null != eglES2DynamicLookupHelper || null != eglES1DynamicLookupHelper ) { - if(isANGLE && !enableANGLE) { + if(isANGLE && !GLProfile.enableANGLE) { if(DEBUG || GLProfile.DEBUG) { System.err.println("Info: EGLDrawableFactory.init - EGL/ES2 ANGLE disabled"); } diff --git a/src/jogl/classes/jogamp/opengl/glu/mipmap/Mipmap.java b/src/jogl/classes/jogamp/opengl/glu/mipmap/Mipmap.java index 9ff6bd637..51d8ca6fe 100644 --- a/src/jogl/classes/jogamp/opengl/glu/mipmap/Mipmap.java +++ b/src/jogl/classes/jogamp/opengl/glu/mipmap/Mipmap.java @@ -49,6 +49,7 @@ import javax.media.opengl.GL2; import javax.media.opengl.GL2ES2; import javax.media.opengl.GL2ES3; import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLContext; import javax.media.opengl.glu.GLU; import javax.media.opengl.GLException; @@ -255,8 +256,8 @@ public class Mipmap { */ public static void closestFit( final GL gl, final int target, final int width, final int height, final int internalFormat, final int format, final int type, final int[] newWidth, final int[] newHeight ) { - // Use proxy textures if OpenGL version >= 1.1 - if( Double.parseDouble( gl.glGetString( GL.GL_VERSION ).trim().substring( 0, 3 ) ) >= 1.1 ) { + // Use proxy textures if OpenGL GL2/GL3 version >= 1.1 + if( gl.isGL2GL3() && gl.getContext().getGLVersionNumber().compareTo(GLContext.Version110) >= 0 ) { int widthPowerOf2 = nearestPower( width ); int heightPowerOf2 = nearestPower( height ); final int[] proxyWidth = new int[1]; diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java index dbca7c2e8..7066a6db5 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java @@ -828,7 +828,7 @@ public class MacOSXCGLContext extends GLContextImpl final long drawableHandle = drawable.getHandle(); if(drawable instanceof GLFBODrawableImpl) { final GLFBODrawableImpl fbod = (GLFBODrawableImpl)drawable; - texID = fbod.getTextureBuffer(GL.GL_FRONT).getName(); + texID = fbod.getColorbuffer(GL.GL_FRONT).getName(); pbufferHandle = 0; fbod.setSwapBufferContext(new GLFBODrawableImpl.SwapBufferContext() { @Override @@ -1043,7 +1043,7 @@ public class MacOSXCGLContext extends GLContextImpl final boolean valid; final boolean isFBO = drawable instanceof GLFBODrawableImpl; if( isFBO ){ - texID = ((GLFBODrawableImpl)drawable).getTextureBuffer(GL.GL_FRONT).getName(); + texID = ((GLFBODrawableImpl)drawable).getColorbuffer(GL.GL_FRONT).getName(); valid = 0 != texID; } else { texID = 0; diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java index 7c05b8eab..045abca4c 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java @@ -42,6 +42,7 @@ package jogamp.opengl.macosx.cgl; import java.nio.Buffer; import java.nio.FloatBuffer; +import java.nio.ShortBuffer; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -404,24 +405,29 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { /** Returns the length of the computed gamma ramp for this OS and hardware. Returns 0 if gamma changes are not supported. */ @Override - protected int getGammaRampLength() { + protected int getGammaRampLength(final NativeSurface surface) { return GAMMA_RAMP_LENGTH; } @Override - protected boolean setGammaRamp(final float[] ramp) { + protected boolean setGammaRamp(final NativeSurface surface, final float[] ramp) { final FloatBuffer rampNIO = Buffers.newDirectFloatBuffer(ramp); - return CGL.setGammaRamp(ramp.length, rampNIO, rampNIO, rampNIO); } @Override - protected Buffer getGammaRamp() { - return null; + protected Buffer getGammaRamp(final NativeSurface surface) { + return ShortBuffer.allocate(0); // return a dummy gamma ramp default for reset } @Override - protected void resetGammaRamp(final Buffer originalGammaRamp) { + protected void resetGammaRamp(final NativeSurface surface, final Buffer originalGammaRamp) { CGL.resetGammaRamp(); } + + @Override + protected final void resetGammaRamp(final DeviceScreenID deviceScreenID, final Buffer originalGammaRamp) { + CGL.resetGammaRamp(); + } + } diff --git a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java index 05e192bbc..0969199c6 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java +++ b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java @@ -28,7 +28,6 @@ package jogamp.opengl.util.av; import java.io.IOException; -import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -46,7 +45,8 @@ import javax.media.opengl.GLProfile; import jogamp.opengl.Debug; -import com.jogamp.common.net.URIQueryProps; +import com.jogamp.common.net.UriQueryProps; +import com.jogamp.common.net.Uri; import com.jogamp.common.os.Platform; import com.jogamp.common.util.LFRingbuffer; import com.jogamp.common.util.Ringbuffer; @@ -92,15 +92,15 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { private final int[] texWrapST = { GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE }; /** User requested URI stream location. */ - private URI streamLoc = null; + private Uri streamLoc = null; /** * In case {@link #streamLoc} is a {@link GLMediaPlayer#CameraInputScheme}, * {@link #cameraPath} holds the URI's path portion - * as parsed in {@link #initStream(URI, int, int, int)}. + * as parsed in {@link #initStream(Uri, int, int, int)}. * @see #cameraProps */ - protected String cameraPath = null; + protected Uri.Encoded cameraPath = null; /** Optional camera properties, see {@link #cameraPath}. */ protected Map<String, String> cameraProps = null; @@ -530,7 +530,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } @Override - public final void initStream(final URI streamLoc, final int vid, final int aid, final int reqTextureCount) throws IllegalStateException, IllegalArgumentException { + public final void initStream(final Uri streamLoc, final int vid, final int aid, final int reqTextureCount) throws IllegalStateException, IllegalArgumentException { synchronized( stateLock ) { if(State.Uninitialized != state) { throw new IllegalStateException("Instance not in state unintialized: "+this); @@ -556,13 +556,13 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { // Pre-parse for camera-input scheme cameraPath = null; cameraProps = null; - final String streamLocScheme = streamLoc.getScheme(); + final Uri.Encoded streamLocScheme = streamLoc.scheme; if( null != streamLocScheme && streamLocScheme.equals(CameraInputScheme) ) { - final String rawPath = streamLoc.getRawPath(); + final Uri.Encoded rawPath = streamLoc.path; if( null != rawPath && rawPath.length() > 0 ) { // cut-off root fwd-slash cameraPath = rawPath.substring(1); - final URIQueryProps props = URIQueryProps.create(streamLoc, ';'); + final UriQueryProps props = UriQueryProps.create(streamLoc, ';'); cameraProps = props.getProperties(); } else { throw new IllegalArgumentException("Camera path is empty: "+streamLoc.toString()); @@ -1472,7 +1472,7 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer { } @Override - public final URI getURI() { return streamLoc; } + public final Uri getUri() { return streamLoc; } @Override public final int getVID() { return vid; } diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java index 8ac1232b5..4601df67d 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java @@ -37,7 +37,6 @@ import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GLException; -import com.jogamp.common.os.Platform; import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.VersionNumber; import com.jogamp.gluegen.runtime.ProcAddressTable; @@ -292,7 +291,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { System.err.println("initStream: p1 "+this); } - final String streamLocS = IOUtil.decodeURIIfFilePath(getURI()); + final String streamLocS = IOUtil.getUriFilePathOrASCII(getUri()); destroyAudioSink(); if( GLMediaPlayer.STREAM_ID_NONE == aid ) { audioSink = AudioSinkFactory.createNull(); @@ -317,10 +316,10 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { case HPUX: case LINUX: case SUNOS: - resStreamLocS = dev_video_linux + cameraPath; + resStreamLocS = dev_video_linux + cameraPath.decode(); break; case WINDOWS: - resStreamLocS = cameraPath; + resStreamLocS = cameraPath.decode(); break; case MACOS: case OPENKODE: @@ -345,7 +344,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl { // setStream(..) issues updateAttributes*(..), and defines avChosenAudioFormat, vid, aid, .. etc if(DEBUG) { System.err.println("initStream: p3 cameraPath "+cameraPath+", isCameraInput "+isCameraInput); - System.err.println("initStream: p3 stream "+getURI()+" -> "+streamLocS+" -> "+resStreamLocS); + System.err.println("initStream: p3 stream "+getUri()+" -> "+streamLocS+" -> "+resStreamLocS); System.err.println("initStream: p3 vid "+vid+", sizes "+sizes+", reqVideo "+rw+"x"+rh+"@"+rr+", aid "+aid+", aMaxChannelCount "+aMaxChannelCount+", aPrefSampleRate "+aPrefSampleRate); } natives.setStream0(moviePtr, resStreamLocS, isCameraInput, vid, sizes, rw, rh, rr, aid, aMaxChannelCount, aPrefSampleRate); diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java index 0eeb54bf6..5baf9e543 100644 --- a/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java +++ b/src/jogl/classes/jogamp/opengl/util/av/impl/OMXGLMediaPlayer.java @@ -100,10 +100,10 @@ public class OMXGLMediaPlayer extends EGLMediaPlayerImpl { if(0==moviePtr) { throw new GLException("OMX native instance null"); } - if(!getURI().getScheme().equals("file")) { - throw new IOException("Only file schemes are allowed: "+getURI()); + if( !getUri().isFileScheme() ) { + throw new IOException("Only file schemes are allowed: "+getUri()); } - final String path=getURI().getPath(); + final String path=getUri().path.decode(); if(DEBUG) { System.out.println("initGLStream: clean path "+path); } diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncHook.java b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncHook.java index a9848f899..2dde27b1d 100644 --- a/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncHook.java +++ b/src/jogl/classes/jogamp/opengl/util/glsl/fixedfunc/FixedFuncHook.java @@ -36,6 +36,7 @@ import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GLArrayData; import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; import javax.media.opengl.fixedfunc.GLLightingFunc; import javax.media.opengl.fixedfunc.GLMatrixFunc; import javax.media.opengl.fixedfunc.GLPointerFunc; @@ -50,6 +51,7 @@ import com.jogamp.opengl.util.glsl.fixedfunc.ShaderSelectionMode; public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFunc { public static final int MAX_TEXTURE_UNITS = 8; + protected final GLProfile gl2es1GLProfile; protected FixedFuncPipeline fixedFunction; protected PMVMatrix pmvMatrix; protected boolean ownsPMVMatrix; @@ -61,6 +63,7 @@ public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFun * @param pmvMatrix optional pass through PMVMatrix for the {@link FixedFuncHook} and {@link FixedFuncPipeline} */ public FixedFuncHook (final GL2ES2 gl, final ShaderSelectionMode mode, final PMVMatrix pmvMatrix) { + this.gl2es1GLProfile = GLProfile.createCustomGLProfile(GLProfile.GL2ES1, gl.getGLProfile().getImpl()); this.gl = gl; if(null != pmvMatrix) { this.ownsPMVMatrix = false; @@ -81,6 +84,7 @@ public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFun final Class<?> shaderRootClass, final String shaderSrcRoot, final String shaderBinRoot, final String vertexColorFile, final String vertexColorLightFile, final String fragmentColorFile, final String fragmentColorTextureFile) { + this.gl2es1GLProfile = GLProfile.createCustomGLProfile(GLProfile.GL2ES1, gl.getGLProfile().getImpl()); this.gl = gl; if(null != pmvMatrix) { this.ownsPMVMatrix = false; @@ -110,6 +114,24 @@ public class FixedFuncHook implements GLLightingFunc, GLMatrixFunc, GLPointerFun // // FixedFuncHookIf - hooks // + public final boolean isGL4core() { + return false; + } + public final boolean isGL3core() { + return false; + } + public final boolean isGLcore() { + return false; + } + public final boolean isGLES2Compatible() { + return false; + } + public final boolean isGLES3Compatible() { + return false; + } + public final GLProfile getGLProfile() { + return gl2es1GLProfile; + } public void glDrawArrays(final int mode, final int first, final int count) { fixedFunction.glDrawArrays(gl, mode, first, count); } diff --git a/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDevice.java b/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDevice.java index 30559924d..36e8bc5a5 100644 --- a/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDevice.java +++ b/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDevice.java @@ -41,6 +41,7 @@ import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.FovHVHalves; import com.jogamp.opengl.util.stereo.EyeParameter; import com.jogamp.opengl.util.stereo.StereoDevice; +import com.jogamp.opengl.util.stereo.StereoDeviceFactory; import com.jogamp.opengl.util.stereo.StereoDeviceRenderer; import com.jogamp.opengl.util.stereo.StereoUtil; @@ -207,16 +208,20 @@ public class GenericStereoDevice implements StereoDevice { final float[] DEFAULT_EYE_POSITION_OFFSET_STEREO = { 0.0f, 0.3f, 3.0f }; // 0.3 up, 3 back final float[] DEFAULT_EYE_POSITION_OFFSET_MONO = { 0.0f, 0.0f, 3.0f }; // 3 back + final DimensionImmutable surfaceSizeInPixel = new Dimension(1280, 800); + final float[] screenSizeInMeters = new float[] { 0.1498f, 0.0936f }; + final float interpupillaryDistanceInMeters = 0.0635f; + final float pupilCenterFromScreenTopInMeters = screenSizeInMeters[1] / 2f; final float d2r = FloatUtil.PI / 180.0f; { config01Mono01 = new Config( "Def01Mono01", ShutterType.RollingTopToBottom, - new Dimension(1280, 800), // resolution - new float[] { 0.1498f, 0.0936f }, // screenSize [m] - new Dimension(1280, 800), // eye textureSize - 0.0936f/2f, // pupilCenterFromScreenTop [m] - 0.0635f, // IPD [m] + surfaceSizeInPixel, // resolution + screenSizeInMeters, // screenSize [m] + surfaceSizeInPixel, // eye textureSize + pupilCenterFromScreenTopInMeters, // pupilCenterFromScreenTop [m] + interpupillaryDistanceInMeters, // IPD [m] new int[] { 0 }, // eye order new EyeParameter[] { new EyeParameter(0, DEFAULT_EYE_POSITION_OFFSET_MONO, @@ -231,11 +236,7 @@ public class GenericStereoDevice implements StereoDevice { } { - final DimensionImmutable surfaceSizeInPixel = new Dimension(1280, 800); - final float[] screenSizeInMeters = new float[] { 0.1498f, 0.0936f }; final DimensionImmutable eyeTextureSize = new Dimension(surfaceSizeInPixel.getWidth()/2, surfaceSizeInPixel.getHeight()); - final float interpupillaryDistanceInMeters = 0.0635f; - final float pupilCenterFromScreenTopInMeters = screenSizeInMeters[1] / 2f; final float[] horizPupilCenterFromLeft = Config.getHorizPupilCenterFromLeft(screenSizeInMeters[0], interpupillaryDistanceInMeters); final float vertPupilCenterFromTop = Config.getVertPupilCenterFromTop(screenSizeInMeters[1], pupilCenterFromScreenTopInMeters); final float fovy = 45f; @@ -249,7 +250,7 @@ public class GenericStereoDevice implements StereoDevice { surfaceSizeInPixel, // resolution screenSizeInMeters, // screenSize [m] eyeTextureSize, // eye textureSize - 0.0936f/2f, // pupilCenterFromScreenTop [m] + pupilCenterFromScreenTopInMeters, // pupilCenterFromScreenTop [m] interpupillaryDistanceInMeters, // IPD [m] new int[] { 0, 1 }, // eye order new EyeParameter[] { @@ -274,11 +275,7 @@ public class GenericStereoDevice implements StereoDevice { if(StereoDevice.DEBUG) { System.err.println("Caught: "+t.getMessage()); t.printStackTrace(); } } - final DimensionImmutable surfaceSizeInPixel = new Dimension(1280, 800); - final float[] screenSizeInMeters = new float[] { 0.1498f, 0.0936f }; final DimensionImmutable eyeTextureSize = new Dimension(1122, 1553); - final float interpupillaryDistanceInMeters = 0.0635f; - final float pupilCenterFromScreenTopInMeters = screenSizeInMeters[1] / 2f; final float[] horizPupilCenterFromLeft = Config.getHorizPupilCenterFromLeft(screenSizeInMeters[0], interpupillaryDistanceInMeters); final float vertPupilCenterFromTop = Config.getVertPupilCenterFromTop(screenSizeInMeters[1], pupilCenterFromScreenTopInMeters); final float fovy = 129f; @@ -313,6 +310,7 @@ public class GenericStereoDevice implements StereoDevice { configs = new Config[] { config01Mono01, config02StereoSBS01, config03StereoSBSLense01 }; } + private final StereoDeviceFactory factory; public final int deviceIndex; public final Config config; @@ -321,7 +319,8 @@ public class GenericStereoDevice implements StereoDevice { private boolean sensorsStarted = false; - public GenericStereoDevice(final int deviceIndex, final StereoDevice.Config customConfig) { + public GenericStereoDevice(final StereoDeviceFactory factory, final int deviceIndex, final StereoDevice.Config customConfig) { + this.factory = factory; this.deviceIndex = deviceIndex; if( customConfig instanceof GenericStereoDevice.Config) { @@ -341,6 +340,9 @@ public class GenericStereoDevice implements StereoDevice { } @Override + public final StereoDeviceFactory getFactory() { return factory; } + + @Override public String toString() { return "GenericStereoDevice["+config+", surfacePos "+surfacePos+"]"; } @@ -431,26 +433,34 @@ public class GenericStereoDevice implements StereoDevice { defaultEyeParam.distNoseToPupilX, defaultEyeParam.distMiddleToPupilY, defaultEyeParam.eyeReliefZ); } + final boolean usePP = null != config.distortionMeshProducer && 0 != distortionBits; // use post-processing + final RectangleImmutable[] eyeViewports = new RectangleImmutable[eyeParam.length]; final DimensionImmutable eyeTextureSize = config.eyeTextureSize; final DimensionImmutable totalTextureSize; if( 1 < eyeParam.length ) { // Stereo SBS totalTextureSize = new Dimension(eyeTextureSize.getWidth()*2, eyeTextureSize.getHeight()); + if( 1 == textureCount ) { // validated in ctor below! eyeViewports[0] = new Rectangle(0, 0, - totalTextureSize.getWidth() / 2, totalTextureSize.getHeight()); + eyeTextureSize.getWidth(), eyeTextureSize.getHeight()); - eyeViewports[1] = new Rectangle((totalTextureSize.getWidth() + 1) / 2, 0, - totalTextureSize.getWidth() / 2, totalTextureSize.getHeight()); + eyeViewports[1] = new Rectangle(eyeTextureSize.getWidth(), 0, + eyeTextureSize.getWidth(), eyeTextureSize.getHeight()); } else { eyeViewports[0] = new Rectangle(0, 0, eyeTextureSize.getWidth(), eyeTextureSize.getHeight()); - eyeViewports[1] = eyeViewports[0]; + if( usePP ) { + eyeViewports[1] = eyeViewports[0]; + } else { + eyeViewports[1] = new Rectangle(eyeTextureSize.getWidth(), 0, + eyeTextureSize.getWidth(), eyeTextureSize.getHeight()); + } } } else { // Mono totalTextureSize = eyeTextureSize; - eyeViewports[0] = new Rectangle(0, 0, totalTextureSize.getWidth(), totalTextureSize.getHeight()); + eyeViewports[0] = new Rectangle(0, 0, eyeTextureSize.getWidth(), eyeTextureSize.getHeight()); } return new GenericStereoDeviceRenderer(this, distortionBits, textureCount, eyePositionOffset, eyeParam, pixelsPerDisplayPixel, textureUnit, eyeTextureSize, totalTextureSize, eyeViewports); diff --git a/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDeviceFactory.java b/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDeviceFactory.java index a59e8d833..f2fa74743 100644 --- a/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDeviceFactory.java +++ b/src/jogl/classes/jogamp/opengl/util/stereo/GenericStereoDeviceFactory.java @@ -38,6 +38,6 @@ public class GenericStereoDeviceFactory extends StereoDeviceFactory { @Override public final StereoDevice createDevice(final int deviceIndex, final StereoDevice.Config config, final boolean verbose) { - return new GenericStereoDevice(deviceIndex, config); + return new GenericStereoDevice(this, deviceIndex, config); } } diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java index 9c5a5b272..33980d663 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java @@ -51,6 +51,7 @@ import javax.media.nativewindow.NativeSurface; import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLProfile; import com.jogamp.common.nio.Buffers; import com.jogamp.gluegen.runtime.ProcAddressTable; @@ -330,7 +331,7 @@ public class WindowsWGLContext extends GLContextImpl { isProcCreateContextAttribsARBAvailable = false; isExtARBCreateContextAvailable = false; } - if ( isProcCreateContextAttribsARBAvailable && isExtARBCreateContextAvailable ) { + if ( isProcCreateContextAttribsARBAvailable && isExtARBCreateContextAvailable && !GLProfile.disableOpenGLARBContext ) { // initial ARB context creation contextHandle = createContextARB(shareWithHandle, true); createContextARBTried=true; @@ -343,7 +344,9 @@ public class WindowsWGLContext extends GLContextImpl { } } else if (DEBUG) { System.err.println(getThreadName() + ": createContextImpl: NOT OK (ARB, initial) - extension not available - share "+toHexString(shareWithHandle)+ - ", isProcCreateContextAttribsARBAvailable "+isProcCreateContextAttribsARBAvailable+", isExtGLXARBCreateContextAvailable "+isExtARBCreateContextAvailable); + ", isProcCreateContextAttribsARBAvailable "+isProcCreateContextAttribsARBAvailable+ + ", isExtGLXARBCreateContextAvailable "+isExtARBCreateContextAvailable+ + ", disableOpenGLARBContext "+GLProfile.disableOpenGLARBContext); } } } else { @@ -360,16 +363,18 @@ public class WindowsWGLContext extends GLContextImpl { } } } else { - if( glCaps.getGLProfile().isGL3() ) { - WGL.wglMakeCurrent(0, 0); - WGL.wglDeleteContext(temp_ctx); - throw new GLException(getThreadName()+": WindowsWGLContex.createContextImpl ctx !ARB, profile > GL2 requested (OpenGL >= 3.0.1). Requested: "+glCaps.getGLProfile()+", current: "+getGLVersion()); + if( glCaps.getGLProfile().isGL3() && createContextARBTried ) { + // We shall not allow context creation >= GL3 w/ non ARB methods if ARB is used, + // otherwise context of similar profile but different creation method may not be share-able. + WGL.wglMakeCurrent(0, 0); + WGL.wglDeleteContext(temp_ctx); + throw new GLException(getThreadName()+": WindowsWGLContex.createContextImpl ctx !ARB but ARB is used, profile > GL2 requested (OpenGL >= 3.0.1). Requested: "+glCaps.getGLProfile()+", current: "+getGLVersion()); } if(DEBUG) { - System.err.println("WindowsWGLContext.createContext failed, fall back to !ARB context "+getGLVersion()); + System.err.println("WindowsWGLContext.createContext ARB not used, fall back to !ARB context "+getGLVersion()); } - // continue with temp context for GL < 3.0 + // continue with temp context contextHandle = temp_ctx; if ( !wglMakeContextCurrent(drawable.getHandle(), drawableRead.getHandle(), contextHandle) ) { WGL.wglMakeCurrent(0, 0); diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java index 4d8c85137..fa052d784 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java @@ -68,6 +68,7 @@ import jogamp.nativewindow.windows.GDI; import jogamp.nativewindow.windows.GDIDummyUpstreamSurfaceHook; import jogamp.nativewindow.windows.GDISurface; import jogamp.nativewindow.windows.RegisteredClassFactory; +import jogamp.opengl.Debug; import jogamp.opengl.DesktopGLDynamicLookupHelper; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableFactoryImpl; @@ -77,17 +78,80 @@ import jogamp.opengl.GLGraphicsConfigurationUtil; import jogamp.opengl.SharedResourceRunner; import com.jogamp.common.nio.PointerBuffer; +import com.jogamp.common.util.PropertyAccess; import com.jogamp.common.util.ReflectionUtil; import com.jogamp.nativewindow.windows.WindowsGraphicsDevice; import com.jogamp.opengl.GLExtensions; import com.jogamp.opengl.GLRendererQuirks; public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { + /** + * Bug 1036: NVidia Windows Driver 'Threaded optimization' workaround. + * <p> + * https://jogamp.org/bugzilla/show_bug.cgi?id=1036 + * </p> + * <p> + * Since NV driver 260.99 from 2010-12-11 a 'Threaded optimization' feature has been introduced. + * The driver spawns off a dedicated thread to off-load certain OpenGL tasks from the calling thread + * to perform them async and off-thread. + * </p> + * <p> + * If 'Threaded optimization' is manually enabled 'on', the driver may crash with JOGL's consistent + * multi-threaded usage - this is a driver bug. + * </p> + * <p> + * If 'Threaded optimization' is manually disabled 'off', the driver always works correctly. + * </p> + * <p> + * 'Threaded optimization' default setting is 'auto' and the driver may crash without this workaround. + * </p> + * <p> + * If setting the process affinity to '1' (1st CPU) while initialization and launching + * the {@link SharedResourceRunner}, the driver does not crash anymore in 'auto' mode. + * This might be either because the driver does not enable 'Threaded optimization' + * or because the driver's worker thread is bound to the same CPU. + * </p> + * <p> + * Property integer value <code>jogl.windows.cpu_affinity_mode</code>: + * <ul> + * <li>0 - none (no affinity, may cause driver crash with 'Threaded optimization' = ['auto', 'on'])</li> + * <li>1 - process affinity (default, workaround for driver crash for 'Threaded optimization' = 'auto', still crashes if set to 'on')</li> + * </ul> + * </p> + * <p> + * Test case reproducing the crash reliable is: com.jogamp.opengl.test.junit.jogl.caps.TestTranslucencyNEWT<br> + * (don't ask why ..) + * </p> + */ + private static final int CPU_AFFINITY_MODE; + + static { + Debug.initSingleton(); + CPU_AFFINITY_MODE = PropertyAccess.getIntProperty("jogl.windows.cpu_affinity_mode", true, 1); + } + private static DesktopGLDynamicLookupHelper windowsWGLDynamicLookupHelper = null; + private final CPUAffinity cpuAffinity; + public WindowsWGLDrawableFactory() { super(); + switch( CPU_AFFINITY_MODE ) { + case 0: + cpuAffinity = new NopCPUAffinity(); + break; + /** + * Doesn't work ! + case 2: + cpuAffinity = new WindowsThreadAffinity(); + break; + */ + default: + cpuAffinity = new WindowsProcessAffinity(); + break; + } + synchronized(WindowsWGLDrawableFactory.class) { if( null == windowsWGLDynamicLookupHelper ) { windowsWGLDynamicLookupHelper = AccessController.doPrivileged(new PrivilegedAction<DesktopGLDynamicLookupHelper>() { @@ -168,45 +232,23 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { return windowsWGLDynamicLookupHelper; } + /* pp */ static String toHexString(final long l) { return "0x"+Long.toHexString(l); } + private WindowsGraphicsDevice defaultDevice; private SharedResourceRunner sharedResourceRunner; private HashMap<String /*connection*/, SharedResourceRunner.Resource> sharedMap; - private long processAffinityChanges = 0; - private final PointerBuffer procMask = PointerBuffer.allocateDirect(1); - private final PointerBuffer sysMask = PointerBuffer.allocateDirect(1); - @Override protected void enterThreadCriticalZone() { - synchronized (sysMask) { - if( 0 == processAffinityChanges) { - final long pid = GDI.GetCurrentProcess(); - if ( GDI.GetProcessAffinityMask(pid, procMask, sysMask) ) { - if(DEBUG) { - System.err.println("WindowsWGLDrawableFactory.enterThreadCriticalZone() - 0x" + Long.toHexString(pid) + " - " + getThreadName()); - // Thread.dumpStack(); - } - processAffinityChanges = pid; - GDI.SetProcessAffinityMask(pid, 1); - } - } + synchronized (cpuAffinity) { + cpuAffinity.set(1); } } @Override protected void leaveThreadCriticalZone() { - synchronized (sysMask) { - if( 0 != processAffinityChanges) { - final long pid = GDI.GetCurrentProcess(); - if( pid != processAffinityChanges) { - throw new GLException("PID doesn't match: set PID 0x" + Long.toHexString(processAffinityChanges) + - " this PID 0x" + Long.toHexString(pid) ); - } - if(DEBUG) { - System.err.println("WindowsWGLDrawableFactory.leaveThreadCriticalZone() - 0x" + Long.toHexString(pid) + " - " + getThreadName()); - } - GDI.SetProcessAffinityMask(pid, sysMask.get(0)); - } + synchronized (cpuAffinity) { + cpuAffinity.reset(); } } @@ -344,6 +386,7 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { if (null != sr.context) { // may cause JVM SIGSEGV: sharedContext.destroy(); + sr.context.destroy(); // will also pull the dummy MutableSurface sr.context = null; } @@ -556,12 +599,12 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { private static final int GAMMA_RAMP_LENGTH = 256; @Override - protected final int getGammaRampLength() { + protected final int getGammaRampLength(final NativeSurface surface) { return GAMMA_RAMP_LENGTH; } @Override - protected final boolean setGammaRamp(final float[] ramp) { + protected final boolean setGammaRamp(final NativeSurface surface, final float[] ramp) { final short[] rampData = new short[3 * GAMMA_RAMP_LENGTH]; for (int i = 0; i < GAMMA_RAMP_LENGTH; i++) { final short scaledValue = (short) (ramp[i] * 65535); @@ -570,18 +613,26 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { rampData[i + 2 * GAMMA_RAMP_LENGTH] = scaledValue; } - final long screenDC = GDI.GetDC(0); - final boolean res = GDI.SetDeviceGammaRamp(screenDC, ShortBuffer.wrap(rampData)); - GDI.ReleaseDC(0, screenDC); + final long hDC = surface.getSurfaceHandle(); + if( 0 == hDC ) { + return false; + } + // final long screenDC = GDI.GetDC(0); + final boolean res = GDI.SetDeviceGammaRamp(hDC, ShortBuffer.wrap(rampData)); + // GDI.ReleaseDC(0, screenDC); return res; } @Override - protected final Buffer getGammaRamp() { + protected final Buffer getGammaRamp(final NativeSurface surface) { final ShortBuffer rampData = ShortBuffer.wrap(new short[3 * GAMMA_RAMP_LENGTH]); - final long screenDC = GDI.GetDC(0); - final boolean res = GDI.GetDeviceGammaRamp(screenDC, rampData); - GDI.ReleaseDC(0, screenDC); + final long hDC = surface.getSurfaceHandle(); + if( 0 == hDC ) { + return null; + } + // final long screenDC = GDI.GetDC(0); + final boolean res = GDI.GetDeviceGammaRamp(hDC, rampData); + // GDI.ReleaseDC(0, screenDC); if (!res) { return null; } @@ -589,7 +640,22 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { } @Override - protected final void resetGammaRamp(final Buffer originalGammaRamp) { + protected final void resetGammaRamp(final NativeSurface surface, final Buffer originalGammaRamp) { + if (originalGammaRamp == null) { + // getGammaRamp failed earlier + return; + } + final long hDC = surface.getSurfaceHandle(); + if( 0 == hDC ) { + return; + } + // final long screenDC = GDI.GetDC(0); + GDI.SetDeviceGammaRamp(hDC, originalGammaRamp); + // GDI.ReleaseDC(0, hDC); + } + + @Override + protected final void resetGammaRamp(final DeviceScreenID deviceScreenID, final Buffer originalGammaRamp) { if (originalGammaRamp == null) { // getGammaRamp failed earlier return; @@ -598,4 +664,143 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl { GDI.SetDeviceGammaRamp(screenDC, originalGammaRamp); GDI.ReleaseDC(0, screenDC); } + + + static interface CPUAffinity { + boolean set(final int newAffinity); + boolean reset(); + } + static final class WindowsThreadAffinity implements CPUAffinity { + private long threadHandle; + private long threadOrigAffinity; + private long threadNewAffinity; + public WindowsThreadAffinity() { + threadHandle = 0; + threadOrigAffinity = 0; + threadNewAffinity = 0; + } + @Override + public boolean set(final int newAffinity) { + final long tid = GDI.GetCurrentThread(); + if( 0 != threadHandle ) { + throw new IllegalStateException("Affinity already set"); + } + final long threadLastAffinity = GDI.SetThreadAffinityMask(tid, newAffinity); + final int werr = GDI.GetLastError(); + final boolean res; + if( 0 != threadLastAffinity ) { + res = true; + this.threadHandle = tid; + this.threadNewAffinity = newAffinity; + this.threadOrigAffinity = threadLastAffinity; + } else { + res = false; + } + if(DEBUG) { + System.err.println("WindowsThreadAffinity.set() - tid " + toHexString(tid) + " - " + getThreadName() + + ": OK "+res+" (werr "+werr+"), Affinity: "+toHexString(threadOrigAffinity) + " -> " + toHexString(newAffinity)); + } + return res; + } + @Override + public boolean reset() { + if( 0 == threadHandle ) { + return true; + } + final long tid = GDI.GetCurrentThread(); + if( tid != threadHandle) { + throw new IllegalStateException("TID doesn't match: set TID " + toHexString(threadHandle) + + " this TID " + toHexString(tid) ); + } + final long preThreadAffinity = GDI.SetThreadAffinityMask(threadHandle, threadOrigAffinity); + final boolean res = 0 != preThreadAffinity; + if(DEBUG) { + System.err.println("WindowsThreadAffinity.reset() - tid " + toHexString(threadHandle) + " - " + getThreadName() + + ": OK "+res+" (werr "+GDI.GetLastError()+"), Affinity: "+toHexString(threadNewAffinity)+" -> orig "+ toHexString(threadOrigAffinity)); + } + this.threadHandle = 0; + this.threadNewAffinity = this.threadOrigAffinity; + return res; + } + } + static final class WindowsProcessAffinity implements CPUAffinity { + private long processHandle; + private long newAffinity; + private final PointerBuffer procMask; + private final PointerBuffer sysMask; + + public WindowsProcessAffinity() { + processHandle = 0; + newAffinity = 0; + procMask = PointerBuffer.allocateDirect(1); + sysMask = PointerBuffer.allocateDirect(1); + } + @Override + public boolean set(final int newAffinity) { + if( 0 != processHandle ) { + throw new IllegalStateException("Affinity already set"); + } + final long pid = GDI.GetCurrentProcess(); + final boolean res; + if ( GDI.GetProcessAffinityMask(pid, procMask, sysMask) ) { + if( GDI.SetProcessAffinityMask(pid, newAffinity) ) { + this.processHandle = pid; + this.newAffinity = newAffinity; + res = true; + } else { + res = false; + } + if(DEBUG) { + System.err.println("WindowsProcessAffinity.set() - pid " + toHexString(pid) + " - " + getThreadName() + + ": OK "+res+" (werr "+GDI.GetLastError()+"), Affinity: procMask "+ toHexString(procMask.get(0)) + ", sysMask "+ toHexString(sysMask.get(0)) + + " -> "+toHexString(newAffinity)); + } + } else { + if(DEBUG) { + System.err.println("WindowsProcessAffinity.set() - pid " + toHexString(pid) + " - " + getThreadName() + + ": Error, could not GetProcessAffinityMask, werr "+GDI.GetLastError()); + } + res = false; + } + return res; + } + @Override + public boolean reset() { + if( 0 == processHandle ) { + return true; + } + final long pid = GDI.GetCurrentProcess(); + if( pid != processHandle) { + throw new IllegalStateException("PID doesn't match: set PID " + toHexString(processHandle) + + " this PID " + toHexString(pid) ); + } + final long origProcAffinity = procMask.get(0); + final boolean res = GDI.SetProcessAffinityMask(processHandle, origProcAffinity); + if(DEBUG) { + final int werr = GDI.GetLastError(); + System.err.println("WindowsProcessAffinity.reset() - pid " + toHexString(processHandle) + " - " + getThreadName() + + ": OK "+res+" (werr "+werr+"), Affinity: "+toHexString(newAffinity)+" -> procMask "+ toHexString(origProcAffinity)); + } + this.processHandle = 0; + this.newAffinity = origProcAffinity; + return res; + } + } + static final class NopCPUAffinity implements CPUAffinity { + public NopCPUAffinity() { } + @Override + public boolean set(final int newAffinity) { + if(DEBUG) { + System.err.println("NopCPUAffinity.set() - " + getThreadName()); + } + return false; + } + @Override + public boolean reset() { + if(DEBUG) { + System.err.println("NopCPUAffinity.reset() - " + getThreadName()); + } + return false; + } + } } diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java index d4c3abc49..9631dbb5b 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java @@ -303,8 +303,8 @@ public class X11GLXContext extends GLContextImpl { if( !config.hasFBConfig() ) { // not able to use FBConfig -> GLX 1.1 forceGLXVersionOneOne(); - if(glp.isGL3()) { - throw new GLException(getThreadName()+": Unable to create OpenGL >= 3.1 context"); + if( glp.isGL3() ) { + throw new GLException(getThreadName()+": Unable to create OpenGL >= 3.1 context w/o FBConfig"); } contextHandle = GLX.glXCreateContext(display, config.getXVisualInfo(), shareWithHandle, direct); if ( 0 == contextHandle ) { @@ -343,7 +343,7 @@ public class X11GLXContext extends GLContextImpl { throw new GLException(getThreadName()+": Unable to create temp OpenGL context(1)"); } if ( !glXMakeContextCurrent(display, drawable.getHandle(), drawableRead.getHandle(), temp_ctx) ) { - throw new GLException(getThreadName()+": Error making temp context(1) current: display "+toHexString(display)+", context "+toHexString(temp_ctx)+", drawable "+drawable); + throw new GLException(getThreadName()+": Error making temp context(1) current: display "+toHexString(display)+", context "+toHexString(temp_ctx)+", drawable "+drawable); } setGLFunctionAvailability(true, 0, 0, CTX_PROFILE_COMPAT, false /* strictMatch */, null == sharedContext /* withinGLVersionsMapping */); // use GL_VERSION glXMakeContextCurrent(display, 0, 0, 0); // release temp context @@ -351,7 +351,7 @@ public class X11GLXContext extends GLContextImpl { // is*Available calls are valid since setGLFunctionAvailability(..) was called final boolean isProcCreateContextAttribsARBAvailable = isFunctionAvailable("glXCreateContextAttribsARB"); final boolean isExtARBCreateContextAvailable = isExtensionAvailable("GLX_ARB_create_context"); - if ( isProcCreateContextAttribsARBAvailable && isExtARBCreateContextAvailable ) { + if ( isProcCreateContextAttribsARBAvailable && isExtARBCreateContextAvailable && !GLProfile.disableOpenGLARBContext ) { // initial ARB context creation contextHandle = createContextARB(shareWithHandle, direct); createContextARBTried=true; @@ -362,9 +362,11 @@ public class X11GLXContext extends GLContextImpl { System.err.println(getThreadName() + ": createContextImpl: NOT OK (ARB, initial) - creation failed - share "+toHexString(shareWithHandle)); } } - } else if (DEBUG) { + } else if( DEBUG ) { System.err.println(getThreadName() + ": createContextImpl: NOT OK (ARB, initial) - extension not available - share "+toHexString(shareWithHandle)+ - ", isProcCreateContextAttribsARBAvailable "+isProcCreateContextAttribsARBAvailable+", isExtGLXARBCreateContextAvailable "+isExtARBCreateContextAvailable); + ", isProcCreateContextAttribsARBAvailable "+isProcCreateContextAttribsARBAvailable+ + ", isExtGLXARBCreateContextAvailable "+isExtARBCreateContextAvailable+ + ", disableOpenGLARBContext "+GLProfile.disableOpenGLARBContext); } } } else { @@ -380,21 +382,24 @@ public class X11GLXContext extends GLContextImpl { } } } else { - if( glp.isGL3() ) { - glXMakeContextCurrent(display, 0, 0, 0); - GLX.glXDestroyContext(display, temp_ctx); - throw new GLException(getThreadName()+": X11GLXContext.createContextImpl ctx !ARB, profile > GL2 requested (OpenGL >= 3.0.1). Requested: "+glp+", current: "+getGLVersion()); + if( glp.isGL3() && createContextARBTried ) { + // We shall not allow context creation >= GL3 w/ non ARB methods if ARB is used, + // otherwise context of similar profile but different creation method may not be share-able. + glXMakeContextCurrent(display, 0, 0, 0); + GLX.glXDestroyContext(display, temp_ctx); + throw new GLException(getThreadName()+": X11GLXContext.createContextImpl ctx !ARB but ARB is used, profile > GL2 requested (OpenGL >= 3.0.1). Requested: "+glp+", current: "+getGLVersion()); } + if(DEBUG) { - System.err.println(getThreadName()+": X11GLXContext.createContextImpl failed, fall back to !ARB context "+getGLVersion()); + System.err.println(getThreadName()+": X11GLXContext.createContextImpl ARB not used, fall back to !ARB context "+getGLVersion()); } - // continue with temp context for GL <= 3.0 + // continue with temp context contextHandle = temp_ctx; if ( !glXMakeContextCurrent(display, drawable.getHandle(), drawableRead.getHandle(), contextHandle) ) { - glXMakeContextCurrent(display, 0, 0, 0); - GLX.glXDestroyContext(display, temp_ctx); - throw new GLException(getThreadName()+": Error making context(1) current: display "+toHexString(display)+", context "+toHexString(contextHandle)+", drawable "+drawable); + glXMakeContextCurrent(display, 0, 0, 0); + GLX.glXDestroyContext(display, temp_ctx); + throw new GLException(getThreadName()+": Error making context(1) current: display "+toHexString(display)+", context "+toHexString(contextHandle)+", drawable "+drawable); } if (DEBUG) { System.err.println(getThreadName() + ": createContextImpl: OK (old-2) share "+toHexString(shareWithHandle)); diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java index fbab32963..60e4438d0 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java @@ -72,6 +72,7 @@ import jogamp.opengl.GLDynamicLookupHelper; import jogamp.opengl.GLGraphicsConfigurationUtil; import jogamp.opengl.SharedResourceRunner; +import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.VersionNumber; import com.jogamp.nativewindow.x11.X11GraphicsDevice; import com.jogamp.nativewindow.x11.X11GraphicsScreen; @@ -562,34 +563,34 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { private boolean gotGammaRampLength; private int gammaRampLength; @Override - protected final synchronized int getGammaRampLength() { + protected final synchronized int getGammaRampLength(final NativeSurface surface) { if (gotGammaRampLength) { return gammaRampLength; } - - final long display = getOrCreateSharedDpy(defaultDevice); + final long display = surface.getDisplayHandle(); if(0 == display) { return 0; } + final int screenIdx = surface.getScreenIndex(); final int[] size = new int[1]; - final boolean res = X11Lib.XF86VidModeGetGammaRampSize(display, - X11Lib.DefaultScreen(display), - size, 0); + final boolean res = X11Lib.XF86VidModeGetGammaRampSize(display, screenIdx, size, 0); if (!res) { return 0; } gotGammaRampLength = true; gammaRampLength = size[0]; + System.err.println("XXX: Gamma ramp size: "+gammaRampLength); return gammaRampLength; } @Override - protected final boolean setGammaRamp(final float[] ramp) { - final long display = getOrCreateSharedDpy(defaultDevice); + protected final boolean setGammaRamp(final NativeSurface surface, final float[] ramp) { + final long display = surface.getDisplayHandle(); if(0 == display) { return false; } + final int screenIdx = surface.getScreenIndex(); final int len = ramp.length; final short[] rampData = new short[len]; @@ -597,36 +598,52 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { rampData[i] = (short) (ramp[i] * 65535); } - final boolean res = X11Lib.XF86VidModeSetGammaRamp(display, - X11Lib.DefaultScreen(display), + final boolean res = X11Lib.XF86VidModeSetGammaRamp(display, screenIdx, rampData.length, rampData, 0, rampData, 0, rampData, 0); + if( DEBUG ) { + dumpRamp("SET__", rampData.length, rampData, rampData, rampData); + } return res; } + private static void dumpRamp(final String prefix, final int size, final ShortBuffer r, final ShortBuffer g, final ShortBuffer b) { + for(int i=0; i<size; i++) { + if( 0 == i % 4 ) { + System.err.printf("%n%4d/%4d %s: ", i, size, prefix); + } + System.err.printf(" [%04X %04X %04X], ", r.get(i), g.get(i), b.get(i)); + } + System.err.println(); + } + private static void dumpRamp(final String prefix, final int size, final short[] r, final short[] g, final short[] b) { + for(int i=0; i<size; i++) { + if( 0 == i % 4 ) { + System.err.printf("%n%4d/%4d %s: ", i, size, prefix); + } + System.err.printf(" [%04X %04X %04X], ", r[i], g[i], b[i]); + } + System.err.println(); + } + @Override - protected final Buffer getGammaRamp() { - final long display = getOrCreateSharedDpy(defaultDevice); + protected final Buffer getGammaRamp(final NativeSurface surface) { + final long display = surface.getDisplayHandle(); if(0 == display) { return null; } + final int screenIdx = surface.getScreenIndex(); + + final int size = getGammaRampLength(surface); - final int size = getGammaRampLength(); - final ShortBuffer rampData = ShortBuffer.wrap(new short[3 * size]); - rampData.position(0); - rampData.limit(size); - final ShortBuffer redRampData = rampData.slice(); - rampData.position(size); - rampData.limit(2 * size); - final ShortBuffer greenRampData = rampData.slice(); - rampData.position(2 * size); - rampData.limit(3 * size); - final ShortBuffer blueRampData = rampData.slice(); - - final boolean res = X11Lib.XF86VidModeGetGammaRamp(display, - X11Lib.DefaultScreen(display), + final ShortBuffer rampData = Buffers.newDirectShortBuffer(3 * size); + final ShortBuffer redRampData = Buffers.slice(rampData, 0 * size, size); + final ShortBuffer greenRampData = Buffers.slice(rampData, 1 * size, size); + final ShortBuffer blueRampData = Buffers.slice(rampData, 2 * size, size); + + final boolean res = X11Lib.XF86VidModeGetGammaRamp(display, screenIdx, size, redRampData, greenRampData, @@ -634,40 +651,62 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl { if (!res) { return null; } + if( DEBUG ) { + dumpRamp("GET__", size, redRampData, greenRampData, blueRampData); + } return rampData; } @Override - protected final void resetGammaRamp(final Buffer originalGammaRamp) { + protected final void resetGammaRamp(final NativeSurface surface, final Buffer originalGammaRamp) { if (originalGammaRamp == null) { return; // getGammaRamp failed originally } - final long display = getOrCreateSharedDpy(defaultDevice); + final long display = surface.getDisplayHandle(); if(0 == display) { return; } + final int screenIdx = surface.getScreenIndex(); + + resetGammaRamp(display, screenIdx, originalGammaRamp); + } + + @Override + protected final void resetGammaRamp(final DeviceScreenID deviceScreenID, final Buffer originalGammaRamp) { + if (originalGammaRamp == null) { + return; // getGammaRamp failed originally + } + final long display = X11Util.openDisplay(deviceScreenID.deviceConnection); + if( 0 == display ) { + return; + } + try { + resetGammaRamp(display, deviceScreenID.screenIdx, originalGammaRamp); + } finally { + X11Util.closeDisplay(display); + } + } + private static final void resetGammaRamp(final long display, final int screenIdx, final Buffer originalGammaRamp) { final ShortBuffer rampData = (ShortBuffer) originalGammaRamp; final int capacity = rampData.capacity(); if ((capacity % 3) != 0) { throw new IllegalArgumentException("Must not be the original gamma ramp"); } final int size = capacity / 3; - rampData.position(0); - rampData.limit(size); - final ShortBuffer redRampData = rampData.slice(); - rampData.position(size); - rampData.limit(2 * size); - final ShortBuffer greenRampData = rampData.slice(); - rampData.position(2 * size); - rampData.limit(3 * size); - final ShortBuffer blueRampData = rampData.slice(); - - X11Lib.XF86VidModeSetGammaRamp(display, - X11Lib.DefaultScreen(display), - size, - redRampData, - greenRampData, - blueRampData); + + final ShortBuffer redRampData = Buffers.slice(rampData, 0 * size, size); + final ShortBuffer greenRampData = Buffers.slice(rampData, 1 * size, size); + final ShortBuffer blueRampData = Buffers.slice(rampData, 2 * size, size); + if( DEBUG ) { + dumpRamp("RESET", size, redRampData, greenRampData, blueRampData); + } + + X11Lib.XF86VidModeSetGammaRamp(display, screenIdx, + size, + redRampData, + greenRampData, + blueRampData); } + } diff --git a/src/nativewindow/classes/javax/media/nativewindow/NativeSurface.java b/src/nativewindow/classes/javax/media/nativewindow/NativeSurface.java index 733f12d49..4d764dc4f 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/NativeSurface.java +++ b/src/nativewindow/classes/javax/media/nativewindow/NativeSurface.java @@ -43,16 +43,16 @@ package javax.media.nativewindow; * </p> */ public interface NativeSurface extends SurfaceUpdatedListener { - /** Unlocked state */ + /** Unlocked state, {@value}. */ public static final int LOCK_SURFACE_UNLOCKED = 0; - /** Returned by {@link #lockSurface()} if the surface is not ready to be locked. */ + /** Returned by {@link #lockSurface()} if the surface is not ready to be locked, {@value}. */ public static final int LOCK_SURFACE_NOT_READY = 1; - /** Returned by {@link #lockSurface()} if the surface is locked, but has changed. */ + /** Returned by {@link #lockSurface()} if the surface is locked, but has changed, {@value}. */ public static final int LOCK_SURFACE_CHANGED = 2; - /** Returned by {@link #lockSurface()} if the surface is locked, and is unchanged. */ + /** Returned by {@link #lockSurface()} if the surface is locked, and is unchanged, {@value}. */ public static final int LOCK_SUCCESS = 3; /** diff --git a/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java b/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java index 31e85dcee..c409b6a39 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/windows/GDIUtil.java @@ -128,6 +128,10 @@ public class GDIUtil implements ToolkitProperties { return IsChild0(win); } + public static void SetProcessThreadsAffinityMask(final long affinityMask, final boolean verbose) { + SetProcessThreadsAffinityMask0(affinityMask, verbose); + } + private static final void dumpStack() { Thread.dumpStack(); } // Callback for JNI /** Creates WNDCLASSEX instance */ @@ -142,6 +146,8 @@ public class GDIUtil implements ToolkitProperties { private static native boolean IsChild0(long win); private static native boolean IsUndecorated0(long win); + private static native void SetProcessThreadsAffinityMask0(long affinityMask, boolean verbose); + private static native long CreateDummyWindow0(long hInstance, String className, long dispThreadCtx, String windowName, int x, int y, int width, int height); private static native boolean DestroyWindow0(long dispThreadCtx, long win); } diff --git a/src/nativewindow/native/win32/GDImisc.c b/src/nativewindow/native/win32/GDImisc.c index bec1d4922..76143c426 100644 --- a/src/nativewindow/native/win32/GDImisc.c +++ b/src/nativewindow/native/win32/GDImisc.c @@ -572,3 +572,107 @@ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_windows_GDIUtil_IsUndecorate return bIsUndecorated ? JNI_TRUE : JNI_FALSE; } +#if 0 + +#include <tlhelp32.h> +#include <tchar.h> + +static void printError( TCHAR* msg ) +{ + DWORD eNum; + TCHAR sysMsg[256]; + TCHAR* p; + + eNum = GetLastError( ); + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, eNum, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + sysMsg, 256, NULL ); + + // Trim the end of the line and terminate it with a null + p = sysMsg; + while( ( *p > 31 ) || ( *p == 9 ) ) + ++p; + do { *p-- = 0; } while( ( p >= sysMsg ) && + ( ( *p == '.' ) || ( *p < 33 ) ) ); + + // Display the message + _ftprintf(stderr, TEXT("\n WARNING: %s failed with error %d (%s)"), msg, eNum, sysMsg ); +} + +static BOOL SetProcessThreadsAffinityMask( DWORD dwOwnerPID, DWORD_PTR newTAffinity, BOOL verbose ) +{ + HANDLE hThreadSnap = INVALID_HANDLE_VALUE; + THREADENTRY32 te32; + DWORD_PTR preTAffinity; + HANDLE threadHandle; + + // Take a snapshot of all running threads + hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); + if( hThreadSnap == INVALID_HANDLE_VALUE ) + return( FALSE ); + + // Fill in the size of the structure before using it. + te32.dwSize = sizeof(THREADENTRY32 ); + + // Retrieve information about the first thread, + // and exit if unsuccessful + if( !Thread32First( hThreadSnap, &te32 ) ) + { + if( verbose ) { + printError( TEXT("Thread32First") ); // Show cause of failure + } + CloseHandle( hThreadSnap ); // Must clean up the snapshot object! + return( FALSE ); + } + + // Now walk the thread list of the system, + // and display information about each thread + // associated with the specified process + do + { + if( te32.th32OwnerProcessID == dwOwnerPID ) + { + if( verbose ) { + _ftprintf(stderr, TEXT("\n THREAD ID = 0x%08X, %d"), te32.th32ThreadID, te32.th32ThreadID); + _ftprintf(stderr, TEXT("\n base priority = %d"), te32.tpBasePri ); + _ftprintf(stderr, TEXT("\n delta priority = %d"), te32.tpDeltaPri ); + } + threadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID); + if( NULL != threadHandle ) { + preTAffinity = SetThreadAffinityMask(threadHandle, newTAffinity); + CloseHandle(threadHandle); + if( verbose ) { + _ftprintf(stderr, TEXT("\n affinity %p -> %p"), preTAffinity, newTAffinity); + } + } else { + if( verbose ) { + _ftprintf(stderr, TEXT("\n OpenThread failed %d"), (int)GetLastError()); + } + } + } + } while( Thread32Next(hThreadSnap, &te32 ) ); + + if( verbose ) { + _ftprintf(stderr, TEXT("\n")); + } + +// Don't forget to clean up the snapshot object. + CloseHandle( hThreadSnap ); + return( TRUE ); +} + +JNIEXPORT void JNICALL Java_jogamp_nativewindow_windows_GDIUtil_SetProcessThreadsAffinityMask0 + (JNIEnv *env, jclass unused, jlong affinityMask, jboolean verbose) +{ + SetProcessThreadsAffinityMask( GetCurrentProcessId(), (DWORD_PTR)(intptr_t)affinityMask, (BOOL)verbose ); +} + +#else + +JNIEXPORT void JNICALL Java_jogamp_nativewindow_windows_GDIUtil_SetProcessThreadsAffinityMask0 + (JNIEnv *env, jclass unused, jlong affinityMask, jboolean verbose) +{ +} + +#endif diff --git a/src/newt/classes/com/jogamp/newt/Window.java b/src/newt/classes/com/jogamp/newt/Window.java index 600ecee52..ec6d767fa 100644 --- a/src/newt/classes/com/jogamp/newt/Window.java +++ b/src/newt/classes/com/jogamp/newt/Window.java @@ -304,6 +304,11 @@ public interface Window extends NativeWindow, WindowClosingProtocol, ScalableSur * Sets the size of the window's surface in pixel units which claims the window's client area excluding decorations. * * <p> + * In multiple monitor mode, setting the window's surface size in pixel units + * might not be possible due to unknown <i>scale</i> values of the target display. + * Hence re-setting the pixel unit size after window creation is recommended. + * </p> + * <p> * Zero size semantics are respected, see {@link #setVisible(boolean)}:<br> * <pre> * if ( visible && 0 != windowHandle && ( 0 ≥ width || 0 ≥ height ) ) { diff --git a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java index c44584d78..397c8109b 100644 --- a/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java +++ b/src/newt/classes/com/jogamp/newt/awt/NewtCanvasAWT.java @@ -53,6 +53,7 @@ import javax.media.nativewindow.WindowClosingProtocol; import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLDrawable; import javax.media.opengl.GLDrawableFactory; import javax.swing.MenuSelectionManager; @@ -641,33 +642,35 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto printAnimator.remove(glad); } printGLAD = glad; // _not_ default, shall be replaced by offscreen GLAD - final GLCapabilities caps = (GLCapabilities)glad.getChosenGLCapabilities().cloneMutable(); - final int printNumSamples = printAWTTiles.getNumSamples(caps); + final GLCapabilitiesImmutable gladCaps = glad.getChosenGLCapabilities(); + final int printNumSamples = printAWTTiles.getNumSamples(gladCaps); GLDrawable printDrawable = printGLAD.getDelegatedDrawable(); - final boolean reqNewGLADSamples = printNumSamples != caps.getNumSamples(); + final boolean reqNewGLADSamples = printNumSamples != gladCaps.getNumSamples(); final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() || printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight(); - final boolean reqNewGLADOnscrn = caps.isOnscreen(); + final boolean reqNewGLADOnscrn = gladCaps.isOnscreen(); + + final GLCapabilities newGLADCaps = (GLCapabilities)gladCaps.cloneMutable(); + newGLADCaps.setDoubleBuffered(false); + newGLADCaps.setOnscreen(false); + if( printNumSamples != newGLADCaps.getNumSamples() ) { + newGLADCaps.setSampleBuffers(0 < printNumSamples); + newGLADCaps.setNumSamples(printNumSamples); + } + final boolean reqNewGLADSafe = GLDrawableUtil.isSwapGLContextSafe(glad.getRequestedGLCapabilities(), gladCaps, newGLADCaps); + + final boolean reqNewGLAD = ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize ) && reqNewGLADSafe; - // It is desired to use a new offscreen GLAD, however Bug 830 forbids this for AA onscreen context. - // Bug 830: swapGLContextAndAllGLEventListener and onscreen MSAA w/ NV/GLX - final boolean reqNewGLAD = !caps.getSampleBuffers() && ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize ); if( DEBUG ) { - System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+"], "+ + System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+", safe "+reqNewGLADSafe+"], "+ ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+ ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+ ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+ ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator); } if( reqNewGLAD ) { - caps.setDoubleBuffered(false); - caps.setOnscreen(false); - if( printNumSamples != caps.getNumSamples() ) { - caps.setSampleBuffers(0 < printNumSamples); - caps.setNumSamples(printNumSamples); - } - final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); - printGLAD = factory.createOffscreenAutoDrawable(null, caps, null, + final GLDrawableFactory factory = GLDrawableFactory.getFactory(newGLADCaps.getGLProfile()); + printGLAD = factory.createOffscreenAutoDrawable(null, newGLADCaps, null, printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE, printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE); GLDrawableUtil.swapGLContextAndAllGLEventListener(glad, printGLAD); @@ -678,7 +681,7 @@ public class NewtCanvasAWT extends java.awt.Canvas implements WindowClosingProto printAWTTiles.renderer.attachAutoDrawable(printGLAD); if( DEBUG ) { System.err.println("AWT print.setup "+printAWTTiles); - System.err.println("AWT print.setup AA "+printNumSamples+", "+caps); + System.err.println("AWT print.setup AA "+printNumSamples+", "+newGLADCaps); System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD); System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable); } diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index b9c4e35f2..2ba030f76 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -65,6 +65,7 @@ import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import javax.media.opengl.GLRunnable; +import javax.media.opengl.GLSharedContextSetter; import jogamp.newt.WindowImpl; import jogamp.opengl.GLAutoDrawableBase; @@ -108,6 +109,12 @@ import com.jogamp.opengl.GLStateKeeper; * you can inject {@link GLRunnable} objects * via {@link #invoke(boolean, GLRunnable)} to the OpenGL command stream.<br> * </p> + * <p> + * <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> + * To share a {@link GLContext} see the following note in the documentation overview: + * <a href="../../../../overview-summary.html#SHARING">context sharing</a> + * as well as {@link GLSharedContextSetter}. + * </p> */ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Window, NEWTEventConsumer, FPSCounter { private final WindowImpl window; @@ -143,6 +150,11 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind return window; } + @Override + public final RecursiveLock getUpstreamLock() { + return window.getLock(); + } + /** * Creates a new GLWindow attaching a new Window referencing a * new default Screen and default Display with the given GLCapabilities. @@ -594,10 +606,12 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind @Override public synchronized boolean pauseRenderingAction() { - boolean animatorPaused = false; + final boolean animatorPaused; savedAnimator = GLWindow.this.getAnimator(); if ( null != savedAnimator ) { animatorPaused = savedAnimator.pause(); + } else { + animatorPaused = false; } return animatorPaused; } @@ -643,11 +657,6 @@ public class GLWindow extends GLAutoDrawableBase implements GLAutoDrawable, Wind // @Override - protected final RecursiveLock getLock() { - return window.getLock(); - } - - @Override public void display() { if( !isNativeValid() || !isVisible() ) { return; } diff --git a/src/newt/classes/jogamp/newt/WindowImpl.java b/src/newt/classes/jogamp/newt/WindowImpl.java index 68d3e93d6..b62628962 100644 --- a/src/newt/classes/jogamp/newt/WindowImpl.java +++ b/src/newt/classes/jogamp/newt/WindowImpl.java @@ -1104,6 +1104,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(null!=lifecycleHook) { lifecycleHook.destroyActionPreLock(); } + RuntimeException lifecycleCaughtInLock = null; final RecursiveLock _lock = windowLock; _lock.lock(); try { @@ -1119,8 +1120,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(childWindows.size()>0) { // avoid ConcurrentModificationException: parent -> child -> parent.removeChild(this) @SuppressWarnings("unchecked") - final - ArrayList<NativeWindow> clonedChildWindows = (ArrayList<NativeWindow>) childWindows.clone(); + final ArrayList<NativeWindow> clonedChildWindows = (ArrayList<NativeWindow>) childWindows.clone(); while( clonedChildWindows.size() > 0 ) { final NativeWindow nw = clonedChildWindows.remove(0); if(nw instanceof WindowImpl) { @@ -1134,7 +1134,11 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(null!=lifecycleHook) { // send synced destroy notification for proper cleanup, eg GLWindow/OpenGL - lifecycleHook.destroyActionInLock(); + try { + lifecycleHook.destroyActionInLock(); + } catch (final RuntimeException re) { + lifecycleCaughtInLock = re; + } } if( isNativeValid() ) { @@ -1157,6 +1161,13 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer if(DEBUG_IMPLEMENTATION) { System.err.println("Window.destroy() END "+getThreadName()/*+", "+WindowImpl.this*/); + if( null != lifecycleCaughtInLock ) { + System.err.println("Window.destroy() caught: "+lifecycleCaughtInLock.getMessage()); + lifecycleCaughtInLock.printStackTrace(); + } + } + if( null != lifecycleCaughtInLock ) { + throw lifecycleCaughtInLock; } } finally { // update states before release window lock diff --git a/src/newt/native/NewtMacWindow.m b/src/newt/native/NewtMacWindow.m index 5a1963155..caf9e54e0 100644 --- a/src/newt/native/NewtMacWindow.m +++ b/src/newt/native/NewtMacWindow.m @@ -124,6 +124,9 @@ static CFStringRef CKCH_CreateStringForKey(CGKeyCode keyCode, const UCKeyboardLa static CFMutableDictionaryRef CKCH_CreateCodeToCharDict(TISInputSourceRef keyboard) { CFDataRef layoutData = (CFDataRef) TISGetInputSourceProperty(keyboard, kTISPropertyUnicodeKeyLayoutData); + if( NULL == layoutData ) { + return NULL; + } const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData); CFMutableDictionaryRef codeToCharDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 128, NULL, NULL); @@ -149,8 +152,10 @@ static CFMutableDictionaryRef CKCH_USCodeToNNChar = NULL; static void CKCH_CreateDictionaries() { TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); - CKCH_USCodeToNNChar = CKCH_CreateCodeToCharDict(currentKeyboard); - CFRelease(currentKeyboard); + if( NULL != currentKeyboard ) { + CKCH_USCodeToNNChar = CKCH_CreateCodeToCharDict(currentKeyboard); + CFRelease(currentKeyboard); + } } static UniChar CKCH_CharForKeyCode(jshort keyCode) { diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDevice.java b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDevice.java index 2832012e4..f6dc8bf9f 100644 --- a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDevice.java +++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDevice.java @@ -41,6 +41,7 @@ import com.jogamp.oculusvr.ovrHmdDesc; import com.jogamp.oculusvr.ovrSizei; import com.jogamp.opengl.math.FovHVHalves; import com.jogamp.opengl.util.stereo.StereoDevice; +import com.jogamp.opengl.util.stereo.StereoDeviceFactory; import com.jogamp.opengl.util.stereo.StereoDeviceRenderer; import com.jogamp.opengl.util.stereo.StereoUtil; @@ -48,6 +49,7 @@ public class OVRStereoDevice implements StereoDevice { /** 1.6 up, 5 forward */ private static final float[] DEFAULT_EYE_POSITION_OFFSET = { 0.0f, 1.6f, -5.0f }; + private final StereoDeviceFactory factory; public final OvrHmdContext handle; public final int deviceIndex; public final ovrHmdDesc hmdDesc; @@ -57,7 +59,11 @@ public class OVRStereoDevice implements StereoDevice { private final int[] eyeRenderOrder; private final int supportedDistortionBits, recommendedDistortionBits, minimumDistortionBits; - public OVRStereoDevice(final OvrHmdContext nativeContext, final int deviceIndex) { + public OVRStereoDevice(final StereoDeviceFactory factory, final OvrHmdContext nativeContext, final int deviceIndex) { + if( null == nativeContext ) { + throw new IllegalArgumentException("Passed null nativeContext"); + } + this.factory = factory; this.handle = nativeContext; this.deviceIndex = deviceIndex; this.hmdDesc = ovrHmdDesc.create(); @@ -75,6 +81,9 @@ public class OVRStereoDevice implements StereoDevice { } @Override + public final StereoDeviceFactory getFactory() { return factory; } + + @Override public final String toString() { final StringBuilder sb = new StringBuilder(); sb.append("OVRStereoDevice[product "+hmdDesc.getProductNameAsString()); diff --git a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceFactory.java b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceFactory.java index ebd3699e1..06f716ddc 100644 --- a/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceFactory.java +++ b/src/oculusvr/classes/jogamp/opengl/oculusvr/OVRStereoDeviceFactory.java @@ -37,13 +37,23 @@ import com.jogamp.opengl.util.stereo.StereoDeviceFactory; public class OVRStereoDeviceFactory extends StereoDeviceFactory { public static boolean isAvailable() { - return OVR.ovr_Initialize(); // recursive .. + if( OVR.ovr_Initialize() ) { // recursive .. + return 0 < OVR.ovrHmd_Detect(); + } + return false; } @Override - public final StereoDevice createDevice(final int deviceIndex, Config config, final boolean verbose) { + public final StereoDevice createDevice(final int deviceIndex, final Config config, final boolean verbose) { final OvrHmdContext hmdCtx = OVR.ovrHmd_Create(deviceIndex); - final OVRStereoDevice ctx = new OVRStereoDevice(hmdCtx, deviceIndex); + if( null == hmdCtx ) { + if( verbose ) { + System.err.println("Failed to create hmdCtx for device index "+deviceIndex+" on thread "+Thread.currentThread().getName()); + Thread.dumpStack(); + } + return null; + } + final OVRStereoDevice ctx = new OVRStereoDevice(this, hmdCtx, deviceIndex); if( verbose ) { System.err.println(OVRVersion.getAvailableCapabilitiesInfo(ctx.hmdDesc, deviceIndex, null).toString()); } diff --git a/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0a.java b/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0a.java index 79b6952ee..66959e90f 100644 --- a/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0a.java +++ b/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0a.java @@ -29,7 +29,6 @@ package com.jogamp.opengl.test.android; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; import java.net.URISyntaxException; import java.net.URLConnection; import java.util.Arrays; @@ -39,12 +38,12 @@ import javax.media.opengl.GLProfile; import jogamp.newt.driver.android.NewtBaseActivity; +import com.jogamp.common.net.Uri; import com.jogamp.common.util.IOUtil; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.event.MouseAdapter; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.opengl.GLWindow; - import com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieCube; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.av.GLMediaPlayer; @@ -75,7 +74,7 @@ public class MovieCubeActivity0a extends NewtBaseActivity { System.getProperty("jnlp.media0_url0"), System.getProperty("jnlp.media0_url1"), System.getProperty("jnlp.media0_url2") }; - final URI streamLoc = getURI(streamLocs, 0, false); + final Uri streamLoc = getUri(streamLocs, 0, false); if(null == streamLoc) { throw new RuntimeException("no media reachable: "+Arrays.asList(streamLocs)); } // also initializes JOGL @@ -136,15 +135,15 @@ public class MovieCubeActivity0a extends NewtBaseActivity { Log.d(TAG, "onCreate - X"); } - static URI getURI(final String path[], final int off, final boolean checkAvail) { - URI uri = null; + static Uri getUri(final String path[], final int off, final boolean checkAvail) { + Uri uri = null; for(int i=off; null==uri && i<path.length; i++) { if(null != path[i] && path[i].length()>0) { if( checkAvail ) { final URLConnection uc = IOUtil.getResource(path[i], null); if( null != uc ) { try { - uri = uc.getURL().toURI(); + uri = Uri.valueOf(uc.getURL()); } catch (final URISyntaxException e) { uri = null; } @@ -154,7 +153,7 @@ public class MovieCubeActivity0a extends NewtBaseActivity { } } else { try { - uri = new URI(path[i]); + uri = Uri.cast(path[i]); } catch (final URISyntaxException e) { uri = null; } diff --git a/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0b.java b/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0b.java index 5303276a6..33dbe8fc5 100644 --- a/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0b.java +++ b/src/test/com/jogamp/opengl/test/android/MovieCubeActivity0b.java @@ -29,7 +29,6 @@ package com.jogamp.opengl.test.android; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; import java.net.URISyntaxException; import java.net.URLConnection; import java.util.Arrays; @@ -39,12 +38,12 @@ import javax.media.opengl.GLProfile; import jogamp.newt.driver.android.NewtBaseActivity; +import com.jogamp.common.net.Uri; import com.jogamp.common.util.IOUtil; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.event.MouseAdapter; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.opengl.GLWindow; - import com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieCube; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.av.GLMediaPlayer; @@ -75,7 +74,7 @@ public class MovieCubeActivity0b extends NewtBaseActivity { System.getProperty("jnlp.media0_url0"), System.getProperty("jnlp.media0_url1"), System.getProperty("jnlp.media0_url2") }; - final URI streamLoc = getURI(streamLocs, 0, false); + final Uri streamLoc = getUri(streamLocs, 0, false); if(null == streamLoc) { throw new RuntimeException("no media reachable: "+Arrays.asList(streamLocs)); } // also initializes JOGL @@ -138,15 +137,15 @@ public class MovieCubeActivity0b extends NewtBaseActivity { Log.d(TAG, "onCreate - X"); } - static URI getURI(final String path[], final int off, final boolean checkAvail) { - URI uri = null; + static Uri getUri(final String path[], final int off, final boolean checkAvail) { + Uri uri = null; for(int i=off; null==uri && i<path.length; i++) { if(null != path[i] && path[i].length()>0) { if( checkAvail ) { final URLConnection uc = IOUtil.getResource(path[i], null); if( null != uc ) { try { - uri = uc.getURL().toURI(); + uri = Uri.valueOf(uc.getURL()); } catch (final URISyntaxException e) { uri = null; } @@ -156,7 +155,7 @@ public class MovieCubeActivity0b extends NewtBaseActivity { } } else { try { - uri = new URI(path[i]); + uri = Uri.cast(path[i]); } catch (final URISyntaxException e) { uri = null; } diff --git a/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java b/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java index 47e1b1796..5991e28ee 100644 --- a/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java +++ b/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity0.java @@ -28,7 +28,6 @@ package com.jogamp.opengl.test.android; import java.net.HttpURLConnection; -import java.net.URI; import java.net.URISyntaxException; import java.net.URLConnection; import java.util.Arrays; @@ -38,13 +37,13 @@ import javax.media.opengl.GLProfile; import jogamp.newt.driver.android.NewtBaseActivity; +import com.jogamp.common.net.Uri; import com.jogamp.common.util.IOUtil; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Window; import com.jogamp.newt.event.MouseAdapter; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.opengl.GLWindow; - import com.jogamp.opengl.test.junit.jogl.demos.es2.av.MovieSimple; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.av.GLMediaPlayer; @@ -74,7 +73,7 @@ public class MovieSimpleActivity0 extends NewtBaseActivity { System.getProperty("jnlp.media0_url0"), System.getProperty("jnlp.media0_url1"), System.getProperty("jnlp.media0_url2") }; - final URI streamLoc = getURI(streamLocs, 0, false); + final Uri streamLoc = getUri(streamLocs, 0, false); if(null == streamLoc) { throw new RuntimeException("no media reachable: "+Arrays.asList(streamLocs)); } // also initializes JOGL @@ -129,15 +128,15 @@ public class MovieSimpleActivity0 extends NewtBaseActivity { Log.d(TAG, "onCreate - X"); } - static URI getURI(final String path[], final int off, final boolean checkAvail) { - URI uri = null; + static Uri getUri(final String path[], final int off, final boolean checkAvail) { + Uri uri = null; for(int i=off; null==uri && i<path.length; i++) { if(null != path[i] && path[i].length()>0) { if( checkAvail ) { final URLConnection uc = IOUtil.getResource(path[i], null); if( null != uc ) { try { - uri = uc.getURL().toURI(); + uri = Uri.valueOf(uc.getURL()); } catch (final URISyntaxException e) { uri = null; } @@ -147,7 +146,7 @@ public class MovieSimpleActivity0 extends NewtBaseActivity { } } else { try { - uri = new URI(path[i]); + uri = Uri.cast(path[i]); } catch (final URISyntaxException e) { uri = null; } diff --git a/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java b/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java index 7c99e2cf3..0267db514 100644 --- a/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java +++ b/src/test/com/jogamp/opengl/test/android/MovieSimpleActivity1.java @@ -28,7 +28,6 @@ package com.jogamp.opengl.test.android; import java.net.HttpURLConnection; -import java.net.URI; import java.net.URISyntaxException; import java.net.URLConnection; import java.util.Arrays; @@ -41,6 +40,7 @@ import javax.media.opengl.GLRunnable; import jogamp.newt.driver.android.NewtBaseActivity; +import com.jogamp.common.net.Uri; import com.jogamp.common.util.IOUtil; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Window; @@ -82,15 +82,15 @@ public class MovieSimpleActivity1 extends NewtBaseActivity { System.getProperty("jnlp.media0_url0"), System.getProperty("jnlp.media0_url1"), System.getProperty("jnlp.media0_url2") }; - final URI streamLoc0 = getURI(streamLocs, 2, false); + final Uri streamLoc0 = getUri(streamLocs, 2, false); if(null == streamLoc0) { throw new RuntimeException("no media reachable: "+Arrays.asList(streamLocs)); } - final URI streamLoc1; + final Uri streamLoc1; { - URI _streamLoc1 = null; + Uri _streamLoc1 = null; if(mPlayerHUD && !mPlayerSharedHUD) { final String[] urls1 = new String[] { System.getProperty("jnlp.media1_url0") }; - _streamLoc1 = getURI(urls1, 1, false); + _streamLoc1 = getUri(urls1, 1, false); } if(null == _streamLoc1) { _streamLoc1 = streamLoc0; } streamLoc1 = _streamLoc1; @@ -231,15 +231,15 @@ public class MovieSimpleActivity1 extends NewtBaseActivity { Log.d(TAG, "onCreate - X"); } - static URI getURI(final String path[], final int off, final boolean checkAvail) { - URI uri = null; + static Uri getUri(final String path[], final int off, final boolean checkAvail) { + Uri uri = null; for(int i=off; null==uri && i<path.length; i++) { if(null != path[i] && path[i].length()>0) { if( checkAvail ) { final URLConnection uc = IOUtil.getResource(path[i], null); if( null != uc ) { try { - uri = uc.getURL().toURI(); + uri = Uri.valueOf(uc.getURL()); } catch (final URISyntaxException e) { uri = null; } @@ -249,7 +249,7 @@ public class MovieSimpleActivity1 extends NewtBaseActivity { } } else { try { - uri = new URI(path[i]); + uri = Uri.cast(path[i]); } catch (final URISyntaxException e) { uri = null; } diff --git a/src/test/com/jogamp/opengl/test/junit/graph/FontSet01.java b/src/test/com/jogamp/opengl/test/junit/graph/FontSet01.java new file mode 100644 index 000000000..03a00af78 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/FontSet01.java @@ -0,0 +1,35 @@ +package com.jogamp.opengl.test.junit.graph; + +import java.io.IOException; + +import com.jogamp.common.util.IOUtil; +import com.jogamp.graph.font.Font; +import com.jogamp.graph.font.FontFactory; +import com.jogamp.graph.font.FontSet; + +public class FontSet01 { + public static Font[] getSet01() throws IOException { + final Font[] fonts = new Font[11]; + int i = 0; + fonts[i++] = FontFactory.get(FontFactory.UBUNTU).getDefault(); // FontSet.FAMILY_REGULAR, FontSet.STYLE_NONE + fonts[i++] = FontFactory.get(FontFactory.UBUNTU).get(FontSet.FAMILY_REGULAR, FontSet.STYLE_ITALIC); + fonts[i++] = FontFactory.get(FontFactory.UBUNTU).get(FontSet.FAMILY_REGULAR, FontSet.STYLE_BOLD); + fonts[i++] = FontFactory.get(IOUtil.getResource(TestTextRendererNEWTBugXXXX.class, + "fonts/freefont/FreeMono.ttf")); + fonts[i++] = FontFactory.get(IOUtil.getResource(TestTextRendererNEWTBugXXXX.class, + "fonts/freefont/FreeMonoBold.ttf")); + fonts[i++] = FontFactory.get(IOUtil.getResource(TestTextRendererNEWTBugXXXX.class, + "fonts/freefont/FreeSans.ttf")); + fonts[i++] = FontFactory.get(IOUtil.getResource(TestTextRendererNEWTBugXXXX.class, + "fonts/freefont/FreeSansBold.ttf")); + fonts[i++] = FontFactory.get(IOUtil.getResource(TestTextRendererNEWTBugXXXX.class, + "fonts/freefont/FreeSerif.ttf")); + fonts[i++] = FontFactory.get(IOUtil.getResource(TestTextRendererNEWTBugXXXX.class, + "fonts/freefont/FreeSerifBold.ttf")); + fonts[i++] = FontFactory.get(IOUtil.getResource(TestTextRendererNEWTBugXXXX.class, + "fonts/freefont/FreeSerifBoldItalic.ttf")); + fonts[i++] = FontFactory.get(IOUtil.getResource(TestTextRendererNEWTBugXXXX.class, + "fonts/freefont/FreeSerifItalic.ttf")); + return fonts; + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/graph/TestFontsNEWT00.java b/src/test/com/jogamp/opengl/test/junit/graph/TestFontsNEWT00.java new file mode 100644 index 000000000..e0c6d2ac0 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/TestFontsNEWT00.java @@ -0,0 +1,93 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.test.junit.graph; + +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +import com.jogamp.graph.font.Font; +import com.jogamp.graph.font.Font.Glyph; +import com.jogamp.opengl.test.junit.util.UITestCase; + + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestFontsNEWT00 extends UITestCase { + static boolean mainRun = false; + + static int atoi(final String a) { + try { + return Integer.parseInt(a); + } catch (final Exception ex) { throw new RuntimeException(ex); } + } + + public static void main(final String args[]) throws IOException { + mainRun = true; + final String tstname = TestFontsNEWT00.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } + + @Test + public void test00() throws InterruptedException, IOException { + testFontImpl(FontSet01.getSet01()); + } + void testFontImpl(final Font[] fonts) throws InterruptedException, IOException { + final float fontSize = 10; + final float dpi = 96; + for(int i=0; i<fonts.length; i++) { + final Font font = fonts[i]; + final float pixelSize = font.getPixelSize(fontSize, dpi); + System.err.println(font.getFullFamilyName(null).toString()+": "+fontSize+"p, "+dpi+"dpi -> "+pixelSize+"px:"); + testFontGlyphAdvancedSize(font, ' ', Glyph.ID_SPACE, fontSize, dpi, pixelSize); + testFontGlyphAdvancedSize(font, 'X', 'X', fontSize, dpi, pixelSize); + } + } + void testFontGlyphAdvancedSize(final Font font, final char c, final int glyphID, + final float fontSize, final float dpi, final float pixelSize) { + final float glyphScale = font.getGlyph(c).getScale(pixelSize); + final float fontScale = font.getMetrics().getScale(pixelSize); + + // return this.metrics.getAdvance(pixelSize, useFrationalMetrics); + // this.metrics.getAdvance(pixelSize, useFrationalMetrics) + // this.advance * this.font.getMetrics().getScale(pixelSize) + // font.getHmtxTable().getAdvanceWidth(glyphID) * this.font.getMetrics().getScale(pixelSize) + final float spaceAdvanceSizeOfGlyph = font.getGlyph(c).getAdvance(pixelSize, true); + + // font.getHmtxTable().getAdvanceWidth(glyphID) * metrics.getScale(pixelSize); + // font.getHmtxTable().getAdvanceWidth(glyphID) * pixelSize * unitsPerEM_Inv; + final float spaceAdvanceWidth = font.getAdvanceWidth(glyphID, pixelSize); + System.err.println(" Char '"+c+"', "+glyphID+":"); + System.err.println(" glyphScale "+glyphScale); + System.err.println(" glyphSize "+spaceAdvanceSizeOfGlyph); + System.err.println(" fontScale "+fontScale); + System.err.println(" fontWidth "+spaceAdvanceWidth); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/graph/TestTextRendererNEWT10.java b/src/test/com/jogamp/opengl/test/junit/graph/TestTextRendererNEWT10.java index cb9f3e3d4..c0753ba85 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/TestTextRendererNEWT10.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/TestTextRendererNEWT10.java @@ -27,12 +27,14 @@ */ package com.jogamp.opengl.test.junit.graph; +import java.io.File; import java.io.IOException; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import javax.media.opengl.fixedfunc.GLMatrixFunc; @@ -42,6 +44,8 @@ import org.junit.Test; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; +import com.jogamp.common.util.IOUtil; +import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.RenderState; import com.jogamp.graph.curve.opengl.RegionRenderer; import com.jogamp.graph.curve.opengl.TextRegionUtil; @@ -49,8 +53,10 @@ import com.jogamp.graph.font.Font; import com.jogamp.graph.font.FontFactory; import com.jogamp.graph.geom.SVertex; import com.jogamp.opengl.math.geom.AABBox; +import com.jogamp.opengl.test.junit.util.MiscUtils; import com.jogamp.opengl.test.junit.util.NEWTGLContext; import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.GLReadBufferUtil; import com.jogamp.opengl.util.PMVMatrix; @@ -64,13 +70,16 @@ public class TestTextRendererNEWT10 extends UITestCase { static boolean mainRun = false; static boolean useMSAA = true; - static final int[] texSize = new int[] { 0 }; - static final int fontSize = 24; static Font font; + static float fontSize = 24; + static String customStr = null; @BeforeClass public static void setup() throws IOException { - font = FontFactory.get(FontFactory.UBUNTU).getDefault(); + if( null == font ) { + font = FontFactory.get(FontFactory.UBUNTU).getDefault(); + // font = FontFactory.get(FontFactory.JAVA).getDefault(); + } } static int atoi(final String a) { @@ -91,6 +100,15 @@ public class TestTextRendererNEWT10 extends UITestCase { forceES2 = true; } else if(args[i].equals("-gl3")) { forceGL3 = true; + } else if(args[i].equals("-font")) { + i++; + font = FontFactory.get(IOUtil.getResource(TestTextRendererNEWT10.class, args[i])); + } else if(args[i].equals("-fontSize")) { + i++; + fontSize = MiscUtils.atof(args[i], fontSize); + } else if(args[i].equals("-text")) { + i++; + customStr = args[i]; } } final String tstname = TestTextRendererNEWT10.class.getName(); @@ -104,17 +122,22 @@ public class TestTextRendererNEWT10 extends UITestCase { } catch (final InterruptedException ie) {} } - // @Test - public void test00TextRendererNONE01() throws InterruptedException { - testTextRendererImpl(0); + @Test + public void test00TextRendererNONE00() throws InterruptedException, GLException, IOException { + testTextRendererImpl(0, 0); } @Test - public void testTextRendererMSAA01() throws InterruptedException { - testTextRendererImpl(4); + public void test01TextRendererMSAA04() throws InterruptedException, GLException, IOException { + testTextRendererImpl(0, 4); } - void testTextRendererImpl(final int sampleCount) throws InterruptedException { + @Test + public void test02TextRendererVBAA04() throws InterruptedException, GLException, IOException { + testTextRendererImpl(Region.VBAA_RENDERING_BIT, 4); + } + + void testTextRendererImpl(final int renderModes, final int sampleCount) throws InterruptedException, GLException, IOException { final GLProfile glp; if(forceGL3) { glp = GLProfile.get(GLProfile.GL3); @@ -123,15 +146,17 @@ public class TestTextRendererNEWT10 extends UITestCase { } else { glp = GLProfile.getGL2ES2(); } + final GLCapabilities caps = new GLCapabilities( glp ); caps.setAlphaBits(4); - if( 0 < sampleCount ) { + if( 0 < sampleCount && !Region.isVBAA(renderModes) ) { caps.setSampleBuffers(true); caps.setNumSamples(sampleCount); } System.err.println("Requested: "+caps); + System.err.println("Requested: "+Region.getRenderModeString(renderModes)); - final NEWTGLContext.WindowContext winctx = NEWTGLContext.createOnscreenWindow(caps, 800, 400, true); + final NEWTGLContext.WindowContext winctx = NEWTGLContext.createWindow(caps, 800, 400, true); final GLDrawable drawable = winctx.context.getGLDrawable(); final GL2ES2 gl = winctx.context.getGL().getGL2ES2(); @@ -142,12 +167,13 @@ public class TestTextRendererNEWT10 extends UITestCase { final RenderState rs = RenderState.createRenderState(SVertex.factory()); final RegionRenderer renderer = RegionRenderer.create(rs, RegionRenderer.defaultBlendEnable, RegionRenderer.defaultBlendDisable); rs.setHintMask(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED); - final TextRegionUtil textRenderUtil = new TextRegionUtil(0); + final TextRegionUtil textRenderUtil = new TextRegionUtil(renderModes); // init gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); renderer.init(gl, 0); rs.setColorStatic(0.1f, 0.1f, 0.1f, 1.0f); + screenshot = new GLReadBufferUtil(false, false); // reshape gl.glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); @@ -155,32 +181,40 @@ public class TestTextRendererNEWT10 extends UITestCase { // renderer.reshapePerspective(gl, 45.0f, drawable.getWidth(), drawable.getHeight(), 0.1f, 1000.0f); renderer.reshapeOrtho(drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), 0.1f, 1000.0f); + final int[] sampleCountIO = { sampleCount }; // display gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); - renderString(drawable, gl, renderer, textRenderUtil, "012345678901234567890123456789", 0, 0, -1000); - renderString(drawable, gl, renderer, textRenderUtil, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 0, -1, -1000); - renderString(drawable, gl, renderer, textRenderUtil, "Hello World", 0, -1, -1000); - renderString(drawable, gl, renderer, textRenderUtil, "4567890123456", 4, -1, -1000); - renderString(drawable, gl, renderer, textRenderUtil, "I like JogAmp", 4, -1, -1000); - - int c = 0; - renderString(drawable, gl, renderer, textRenderUtil, "GlueGen", c++, -1, -1000); - renderString(drawable, gl, renderer, textRenderUtil, "JOAL", c++, -1, -1000); - renderString(drawable, gl, renderer, textRenderUtil, "JOGL", c++, -1, -1000); - renderString(drawable, gl, renderer, textRenderUtil, "JOCL", c++, -1, -1000); - + if( null == customStr ) { + renderString(drawable, gl, renderer, textRenderUtil, "012345678901234567890123456789", 0, 0, -1000, sampleCountIO); + renderString(drawable, gl, renderer, textRenderUtil, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 0, -1, -1000, sampleCountIO); + renderString(drawable, gl, renderer, textRenderUtil, "Hello World", 0, -1, -1000, sampleCountIO); + renderString(drawable, gl, renderer, textRenderUtil, "4567890123456", 4, -1, -1000,sampleCountIO); + renderString(drawable, gl, renderer, textRenderUtil, "I like JogAmp", 4, -1, -1000, sampleCountIO); + + int c = 0; + renderString(drawable, gl, renderer, textRenderUtil, "GlueGen", c++, -1, -1000, sampleCountIO); + renderString(drawable, gl, renderer, textRenderUtil, "JOAL", c++, -1, -1000, sampleCountIO); + renderString(drawable, gl, renderer, textRenderUtil, "JOGL", c++, -1, -1000, sampleCountIO); + renderString(drawable, gl, renderer, textRenderUtil, "JOCL", c++, -1, -1000, sampleCountIO); + } else { + renderString(drawable, gl, renderer, textRenderUtil, customStr, 0, 0, -1000, sampleCountIO); + } drawable.swapBuffers(); + printScreen(renderModes, drawable, gl, false, sampleCount); + sleep(); // dispose + screenshot.dispose(gl); renderer.destroy(gl); NEWTGLContext.destroyWindow(winctx); } + private GLReadBufferUtil screenshot; int lastRow = -1; - void renderString(final GLDrawable drawable, final GL2ES2 gl, final RegionRenderer renderer, final TextRegionUtil textRenderUtil, final String text, final int column, int row, final int z0) { + void renderString(final GLDrawable drawable, final GL2ES2 gl, final RegionRenderer renderer, final TextRegionUtil textRenderUtil, final String text, final int column, int row, final int z0, final int[] sampleCount) { final int height = drawable.getSurfaceHeight(); int dx = 0; @@ -196,8 +230,25 @@ public class TestTextRendererNEWT10 extends UITestCase { pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); pmv.glLoadIdentity(); pmv.glTranslatef(dx, dy, z0); - textRenderUtil.drawString3D(gl, renderer, font, fontSize, text, null, texSize); + textRenderUtil.drawString3D(gl, renderer, font, fontSize, text, null, sampleCount); lastRow = row; } + + private int screenshot_num = 0; + + public void printScreen(final int renderModes, final GLDrawable drawable, final GL gl, final boolean exportAlpha, final int sampleCount) throws GLException, IOException { + final String dir = "./"; + final String objName = getSimpleTestName(".")+"-snap"+screenshot_num; + screenshot_num++; + final String modeS = Region.getRenderModeString(renderModes); + final String bname = String.format("%s-msaa%02d-fontsz%02.1f-%03dx%03d-%s%04d", objName, + drawable.getChosenGLCapabilities().getNumSamples(), + TestTextRendererNEWT10.fontSize, drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), modeS, sampleCount); + final String filename = dir + bname +".png"; + if(screenshot.readPixels(gl, false)) { + screenshot.write(new File(filename)); + } + } + } diff --git a/src/test/com/jogamp/opengl/test/junit/graph/TestTextRendererNEWTBugXXXX.java b/src/test/com/jogamp/opengl/test/junit/graph/TestTextRendererNEWTBugXXXX.java new file mode 100644 index 000000000..d6affd7e2 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/TestTextRendererNEWTBugXXXX.java @@ -0,0 +1,224 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.test.junit.graph; + +import java.io.File; +import java.io.IOException; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +import com.jogamp.graph.curve.Region; +import com.jogamp.graph.curve.opengl.RenderState; +import com.jogamp.graph.curve.opengl.RegionRenderer; +import com.jogamp.graph.curve.opengl.TextRegionUtil; +import com.jogamp.graph.font.Font; +import com.jogamp.graph.geom.SVertex; +import com.jogamp.opengl.math.geom.AABBox; +import com.jogamp.opengl.test.junit.util.NEWTGLContext; +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.GLReadBufferUtil; +import com.jogamp.opengl.util.PMVMatrix; + + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestTextRendererNEWTBugXXXX extends UITestCase { + static long duration = 100; // ms + static boolean forceES2 = false; + static boolean forceGL3 = false; + static boolean mainRun = false; + static boolean useMSAA = true; + static boolean onlyIssues = false; + + static final float fontSize = 24; + + static int atoi(final String a) { + try { + return Integer.parseInt(a); + } catch (final Exception ex) { throw new RuntimeException(ex); } + } + + public static void main(final String args[]) throws IOException { + mainRun = true; + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + duration = atoi(args[i]); + } else if(args[i].equals("-noMSAA")) { + useMSAA = false; + } else if(args[i].equals("-es2")) { + forceES2 = true; + } else if(args[i].equals("-gl3")) { + forceGL3 = true; + } + } + final String tstname = TestTextRendererNEWTBugXXXX.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } + + static void sleep() { + try { + System.err.println("** new frame ** (sleep: "+duration+"ms)"); + Thread.sleep(duration); + } catch (final InterruptedException ie) {} + } + + @Test + public void test00All() throws InterruptedException, GLException, IOException { + testTextRendererImpl(FontSet01.getSet01(), Region.VBAA_RENDERING_BIT, 4, false); + } + @Test + public void test01OnlyIssues() throws InterruptedException, GLException, IOException { + testTextRendererImpl(FontSet01.getSet01(), Region.VBAA_RENDERING_BIT, 4, true); + } + void testTextRendererImpl(final Font[] fonts, final int renderModes, final int sampleCount, final boolean onlyIssues) throws InterruptedException, GLException, IOException { + final GLProfile glp; + if(forceGL3) { + glp = GLProfile.get(GLProfile.GL3); + } else if(forceES2) { + glp = GLProfile.get(GLProfile.GLES2); + } else { + glp = GLProfile.getGL2ES2(); + } + + final GLCapabilities caps = new GLCapabilities( glp ); + caps.setAlphaBits(4); + if( 0 < sampleCount && !Region.isVBAA(renderModes) ) { + caps.setSampleBuffers(true); + caps.setNumSamples(sampleCount); + } + caps.setOnscreen(false); + System.err.println("Requested: "+caps); + System.err.println("Requested: "+Region.getRenderModeString(renderModes)); + + final int totalHeight = ( (int)fontSize + 1 ) * ( onlyIssues ? 3 : 6 ) * fonts.length; + final NEWTGLContext.WindowContext winctx = + NEWTGLContext.createWindow(caps, 800, totalHeight, true); + final GLDrawable drawable = winctx.context.getGLDrawable(); + final GL2ES2 gl = winctx.context.getGL().getGL2ES2(); + + Assert.assertEquals(GL.GL_NO_ERROR, gl.glGetError()); + + System.err.println("Chosen: "+winctx.window.getChosenCapabilities()); + + final RenderState rs = RenderState.createRenderState(SVertex.factory()); + final RegionRenderer renderer = RegionRenderer.create(rs, RegionRenderer.defaultBlendEnable, RegionRenderer.defaultBlendDisable); + rs.setHintMask(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED); + final TextRegionUtil textRenderUtil = new TextRegionUtil(renderModes); + + // init + gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + renderer.init(gl, 0); + rs.setColorStatic(0.1f, 0.1f, 0.1f, 1.0f); + screenshot = new GLReadBufferUtil(false, false); + + // reshape + gl.glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + + // renderer.reshapePerspective(gl, 45.0f, drawable.getWidth(), drawable.getHeight(), 0.1f, 1000.0f); + renderer.reshapeOrtho(drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), 0.1f, 1000.0f); + + final int[] sampleCountIO = { sampleCount }; + // display + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + for(int i=0; i<fonts.length; i++) { + final Font font = fonts[i]; + renderString(drawable, gl, renderer, font, textRenderUtil, font.getFullFamilyName(null).toString()+": "+issues, 0, 0==i?0:-1, -1000, sampleCountIO); + if(!onlyIssues) { + renderString(drawable, gl, renderer, font, textRenderUtil, "012345678901234567890123456789", 0, -1, -1000, sampleCountIO); + renderString(drawable, gl, renderer, font, textRenderUtil, "abcdefghijklmnopqrstuvwxyz", 0, -1, -1000, sampleCountIO); + renderString(drawable, gl, renderer, font, textRenderUtil, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0, -1, -1000, sampleCountIO); + } + renderString(drawable, gl, renderer, font, textRenderUtil, "", 0, -1, -1000, sampleCountIO); + renderString(drawable, gl, renderer, font, textRenderUtil, "", 0, -1, -1000, sampleCountIO); + } + + drawable.swapBuffers(); + printScreen(renderModes, drawable, gl, false, sampleCount); + + sleep(); + + // dispose + screenshot.dispose(gl); + renderer.destroy(gl); + + NEWTGLContext.destroyWindow(winctx); + } + + private static final String issues = "m M n u 8 g q Q"; + private GLReadBufferUtil screenshot; + int lastRow = -1; + + void renderString(final GLDrawable drawable, final GL2ES2 gl, final RegionRenderer renderer, final Font font, final TextRegionUtil textRenderUtil, final String text, final int column, int row, final int z0, final int[] sampleCount) { + final int height = drawable.getSurfaceHeight(); + + int dx = 0; + int dy = height; + if(0>row) { + row = lastRow + 1; + } + final AABBox textBox = font.getMetricBounds(text, fontSize); + dx += font.getAdvanceWidth('X', fontSize) * column; + dy -= (int)textBox.getHeight() * ( row + 1 ); + + final PMVMatrix pmv = renderer.getMatrix(); + pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmv.glLoadIdentity(); + pmv.glTranslatef(dx, dy, z0); + textRenderUtil.drawString3D(gl, renderer, font, fontSize, text, null, sampleCount); + + lastRow = row; + } + + private int screenshot_num = 0; + + public void printScreen(final int renderModes, final GLDrawable drawable, final GL gl, final boolean exportAlpha, final int sampleCount) throws GLException, IOException { + final String dir = "./"; + final String objName = getSimpleTestName(".")+"-snap"+screenshot_num; + screenshot_num++; + final String modeS = Region.getRenderModeString(renderModes); + final String bname = String.format("%s-msaa%02d-fontsz%02.1f-%03dx%03d-%s%04d", objName, + drawable.getChosenGLCapabilities().getNumSamples(), + TestTextRendererNEWTBugXXXX.fontSize, drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), modeS, sampleCount); + final String filename = dir + bname +".png"; + if(screenshot.readPixels(gl, false)) { + screenshot.write(new File(filename)); + } + } + +} diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUUISceneGLListener0A.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUUISceneGLListener0A.java index 784fe1009..b10a89a8f 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUUISceneGLListener0A.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/GPUUISceneGLListener0A.java @@ -2,7 +2,6 @@ package com.jogamp.opengl.test.junit.graph.demos; import java.io.File; import java.io.IOException; -import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; @@ -17,6 +16,7 @@ import javax.media.opengl.GLEventListener; import javax.media.opengl.GLPipelineFactory; import javax.media.opengl.GLRunnable; +import com.jogamp.common.net.Uri; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.RegionRenderer; import com.jogamp.graph.curve.opengl.RenderState; @@ -536,7 +536,7 @@ public class GPUUISceneGLListener0A implements GLEventListener { } } ); buttons.add(mPlayerButton); try { - final URI streamLoc = new URI("http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"); + final Uri streamLoc = Uri.cast("http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"); mPlayer.initStream(streamLoc, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.TEXTURE_COUNT_DEFAULT); } catch (final URISyntaxException e1) { e1.printStackTrace(); diff --git a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/GLEventListenerButton.java b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/GLEventListenerButton.java index bdbb77f2d..a8cd32035 100644 --- a/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/GLEventListenerButton.java +++ b/src/test/com/jogamp/opengl/test/junit/graph/demos/ui/GLEventListenerButton.java @@ -89,8 +89,8 @@ public class GLEventListenerButton extends TextureSeqButton { final GLContext ctx = gl.getContext(); final GLDrawable drawable = ctx.getGLDrawable(); - final GLCapabilitiesImmutable capsHas = drawable.getChosenGLCapabilities(); - final GLCapabilities caps = (GLCapabilities) capsHas.cloneMutable(); + final GLCapabilitiesImmutable reqCaps = drawable.getRequestedGLCapabilities(); + final GLCapabilities caps = (GLCapabilities) reqCaps.cloneMutable(); caps.setFBO(true); caps.setDoubleBuffered(false); if( !useAlpha ) { @@ -108,7 +108,7 @@ public class GLEventListenerButton extends TextureSeqButton { fboGLAD.addGLEventListener(glel); fboGLAD.display(); // 1st init! - final FBObject.TextureAttachment texA01 = fboGLAD.getTextureBuffer(GL.GL_FRONT); + final FBObject.TextureAttachment texA01 = fboGLAD.getColorbuffer(GL.GL_FRONT).getTextureAttachment(); final Texture tex = new Texture(texA01.getName(), imgSeq.getTextureTarget(), fboGLAD.getSurfaceWidth(), fboGLAD.getSurfaceHeight(), fboGLAD.getSurfaceWidth(), fboGLAD.getSurfaceHeight(), false /* mustFlipVertically */); diff --git a/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeMono.ttf b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeMono.ttf Binary files differnew file mode 100644 index 000000000..c4200565a --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeMono.ttf diff --git a/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeMonoBold.ttf b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeMonoBold.ttf Binary files differnew file mode 100644 index 000000000..0bee057ec --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeMonoBold.ttf diff --git a/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeMonoBoldOblique.ttf b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeMonoBoldOblique.ttf Binary files differnew file mode 100644 index 000000000..91bbc0e8a --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeMonoBoldOblique.ttf diff --git a/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeMonoOblique.ttf b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeMonoOblique.ttf Binary files differnew file mode 100644 index 000000000..3252bdda6 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeMonoOblique.ttf diff --git a/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSans.ttf b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSans.ttf Binary files differnew file mode 100644 index 000000000..e56dc6e90 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSans.ttf diff --git a/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSansBold.ttf b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSansBold.ttf Binary files differnew file mode 100644 index 000000000..66e19ecb0 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSansBold.ttf diff --git a/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSansBoldOblique.ttf b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSansBoldOblique.ttf Binary files differnew file mode 100644 index 000000000..de8a9e153 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSansBoldOblique.ttf diff --git a/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSansOblique.ttf b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSansOblique.ttf Binary files differnew file mode 100644 index 000000000..b0357eabb --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSansOblique.ttf diff --git a/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSerif.ttf b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSerif.ttf Binary files differnew file mode 100644 index 000000000..dffa1aedb --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSerif.ttf diff --git a/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSerifBold.ttf b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSerifBold.ttf Binary files differnew file mode 100644 index 000000000..e2393ad22 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSerifBold.ttf diff --git a/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSerifBoldItalic.ttf b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSerifBoldItalic.ttf Binary files differnew file mode 100644 index 000000000..46bc4695f --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSerifBoldItalic.ttf diff --git a/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSerifItalic.ttf b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSerifItalic.ttf Binary files differnew file mode 100644 index 000000000..d173e3566 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/graph/fonts/freefont/FreeSerifItalic.ttf diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/GLReadBuffer00Base.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/GLReadBuffer00Base.java index 26974eb30..710f53f92 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/GLReadBuffer00Base.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/GLReadBuffer00Base.java @@ -48,7 +48,18 @@ import com.jogamp.opengl.test.junit.graph.TextRendererGLELBase; import com.jogamp.opengl.test.junit.util.UITestCase; /** - * Multiple GLJPanels in a JFrame + * Test synchronous GLAutoDrawable display, swap-buffer and read-pixels + * including non-MSAA and MSAA framebuffer. + * <p> + * Analyzes behavior of reported bugs + * <ul> + * <li>Bug 841 - GLJPanel "lagging" by one frame, https://jogamp.org/bugzilla/show_bug.cgi?id=841</li> + * <li>Bug 975 - GLJPanel's OffscreenDrawable shall not double swap (custom swap by GLEventListener using [AWT]GLReadBufferUtil), + * https://jogamp.org/bugzilla/show_bug.cgi?id=975</li> + * <li>Bug 1020 - First frame on a mac nvidia card not antialiased, https://jogamp.org/bugzilla/show_bug.cgi?id=841</li> + * </ul> + * </p> + * */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public abstract class GLReadBuffer00Base extends UITestCase { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/GLReadBuffer00BaseAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/GLReadBuffer00BaseAWT.java new file mode 100644 index 000000000..be0d38357 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/GLReadBuffer00BaseAWT.java @@ -0,0 +1,120 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.test.junit.jogl.acore; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.media.opengl.GL; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLEventListener; + +import com.jogamp.opengl.util.GLDrawableUtil; +import com.jogamp.opengl.util.awt.AWTGLReadBufferUtil; +import com.jogamp.opengl.util.texture.TextureIO; + +/** + * Test synchronous GLAutoDrawable display, swap-buffer and read-pixels + * including non-MSAA and MSAA framebuffer. + * <p> + * See {@link GLReadBuffer00Base} for related bugs and further details. + * </p> + */ +public abstract class GLReadBuffer00BaseAWT extends GLReadBuffer00Base { + + protected class SnapshotGLELAWT implements GLEventListener { + final TextRendererGLEL textRendererGLEL; + final AWTGLReadBufferUtil glReadBufferUtil; + final boolean skipGLOrientationVerticalFlip; + boolean defAutoSwapMode; + boolean swapBuffersBeforeRead; + int i; + + SnapshotGLELAWT(final TextRendererGLEL textRendererGLEL, final AWTGLReadBufferUtil glReadBufferUtil, final boolean skipGLOrientationVerticalFlip) { + this.textRendererGLEL = textRendererGLEL; + this.glReadBufferUtil = glReadBufferUtil; + this.skipGLOrientationVerticalFlip = skipGLOrientationVerticalFlip; + this.defAutoSwapMode = true; + this.swapBuffersBeforeRead = false; + i = 0; + } + + @Override + public void init(final GLAutoDrawable drawable) { + defAutoSwapMode = drawable.getAutoSwapBufferMode(); + swapBuffersBeforeRead = GLDrawableUtil.swapBuffersBeforeRead(drawable.getChosenGLCapabilities()); + drawable.setAutoSwapBufferMode( !swapBuffersBeforeRead ); + } + @Override + public void dispose(final GLAutoDrawable drawable) { + drawable.setAutoSwapBufferMode( defAutoSwapMode ); + } + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { } + @Override + public void display(final GLAutoDrawable drawable) { + snapshot(i++, drawable.getGL(), TextureIO.PNG, null); + } + public void snapshot(final int sn, final GL gl, final String fileSuffix, final String destPath) { + final GLDrawable drawable = gl.getContext().getGLReadDrawable(); + final String postSNDetail = String.format("awt-usr%03d", textRendererGLEL.userCounter); + final String filenameAWT = getSnapshotFilename(sn, postSNDetail, + drawable.getChosenGLCapabilities(), drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), + glReadBufferUtil.hasAlpha(), fileSuffix, destPath); + if( swapBuffersBeforeRead ) { + drawable.swapBuffers(); + // Just to test whether we use the right buffer, + // i.e. back-buffer shall no more be required .. + gl.glClear(GL.GL_COLOR_BUFFER_BIT); + } else { + gl.glFinish(); // just make sure rendering finished .. + } + + final boolean awtOrientation = !( drawable.isGLOriented() && skipGLOrientationVerticalFlip ); + System.err.println(Thread.currentThread().getName()+": ** screenshot: awtOrient/v-flip "+awtOrientation+", swapBuffersBeforeRead "+swapBuffersBeforeRead+", "+filenameAWT); + + final BufferedImage image = glReadBufferUtil.readPixelsToBufferedImage(gl, awtOrientation); + final File fout = new File(filenameAWT); + try { + ImageIO.write(image, "png", fout); + } catch (final IOException e) { + e.printStackTrace(); + } + /** + final String filenameJGL = getSnapshotFilename(sn, "jgl", + drawable.getChosenGLCapabilities(), drawable.getWidth(), drawable.getHeight(), + glReadBufferUtil.hasAlpha(), fileSuffix, destPath); + glReadBufferUtil.write(new File(filenameJGL)); + */ + } + }; + +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/InitConcurrentBaseNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/InitConcurrentBaseNEWT.java index eb9203b75..6bfe73e95 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/InitConcurrentBaseNEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/InitConcurrentBaseNEWT.java @@ -52,6 +52,10 @@ import com.jogamp.opengl.util.Animator; * <p> * Rendering is always lock-free and independent of the EDT. * </p> + * <p> + * Each test is decorated w/ {@link GLProfile#shutdown()} to ensure that + * implicit {@link GLProfile#initSingleton()} is also being tested. + * </p> */ public abstract class InitConcurrentBaseNEWT extends UITestCase { @@ -185,6 +189,7 @@ public abstract class InitConcurrentBaseNEWT extends UITestCase { } protected void runJOGLTasks(final int num, final boolean reuse) throws InterruptedException { + GLProfile.shutdown(); System.err.println("InitConcurrentBaseNEWT "+num+" threads, reuse display: "+reuse); final String currentThreadName = Thread.currentThread().getName(); final Object syncDone = new Object(); @@ -222,5 +227,6 @@ public abstract class InitConcurrentBaseNEWT extends UITestCase { i++; } Assert.assertTrue("Threads are still alive after 3s. Alive: "+isAliveDump(threads), isDead(threads)); + GLProfile.shutdown(); } } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOAutoDrawableFactoryNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOAutoDrawableFactoryNEWT.java index a0a5e8969..327fecd25 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOAutoDrawableFactoryNEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOAutoDrawableFactoryNEWT.java @@ -34,6 +34,7 @@ import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLFBODrawable; import javax.media.opengl.GLOffscreenAutoDrawable; import javax.media.opengl.GLProfile; @@ -71,86 +72,120 @@ public class TestFBOAutoDrawableFactoryNEWT extends UITestCase { } @Test - public void test01_GL2ES2_Demo1_SingleBuffer_Normal() throws InterruptedException { + public void test01a_GL2ES2_Demo1_SingleBuffer_Normal() throws InterruptedException { final GLProfile glp = GLProfile.getGL2ES2(); final GLCapabilities caps = new GLCapabilities(glp); caps.setDoubleBuffered(false); - testGLFBODrawableImpl(caps, new GearsES2(0)); + testGLFBODrawableImpl(caps, GLFBODrawable.FBOMODE_USE_TEXTURE, new GearsES2(0)); + } + @Test + public void test01b_GL2ES2_Demo1_SingleBuffer_NoTex() throws InterruptedException { + final GLProfile glp = GLProfile.getGL2ES2(); + final GLCapabilities caps = new GLCapabilities(glp); + caps.setDoubleBuffered(false); + testGLFBODrawableImpl(caps, 0, new GearsES2(0)); + } + @Test + public void test01c_GL2ES2_Demo1_SingleBuffer_NoTexNoDepth() throws InterruptedException { + final GLProfile glp = GLProfile.getGL2ES2(); + final GLCapabilities caps = new GLCapabilities(glp); + caps.setDoubleBuffered(false); + caps.setDepthBits(0); + testGLFBODrawableImpl(caps, 0, new GearsES2(0)); } @Test - public void test02_GL2ES2_Demo1_DoubleBuffer_Normal() throws InterruptedException { + public void test02a_GL2ES2_Demo1_DoubleBuffer_Normal() throws InterruptedException { final GLProfile glp = GLProfile.getGL2ES2(); final GLCapabilities caps = new GLCapabilities(glp); caps.setDoubleBuffered(true); // default - testGLFBODrawableImpl(caps, new GearsES2(0)); + testGLFBODrawableImpl(caps, GLFBODrawable.FBOMODE_USE_TEXTURE, new GearsES2(0)); } @Test - public void test03_GL2ES2_Demo2MSAA4() throws InterruptedException { + public void test03a_GL2ES2_Demo2MSAA4_Normal() throws InterruptedException { final GLProfile glp = GLProfile.getGL2ES2(); final GLCapabilities caps = new GLCapabilities(glp); caps.setSampleBuffers(true); caps.setNumSamples(4); - testGLFBODrawableImpl(caps, new MultisampleDemoES2(true)); + testGLFBODrawableImpl(caps, GLFBODrawable.FBOMODE_USE_TEXTURE, new MultisampleDemoES2(true)); + } + @Test + public void test03b_GL2ES2_Demo2MSAA4_NoTex() throws InterruptedException { + final GLProfile glp = GLProfile.getGL2ES2(); + final GLCapabilities caps = new GLCapabilities(glp); + caps.setSampleBuffers(true); + caps.setNumSamples(4); + testGLFBODrawableImpl(caps, 0, new MultisampleDemoES2(true)); + } + @Test + public void test03c_GL2ES2_Demo2MSAA4_NoTexNoDepth() throws InterruptedException { + final GLProfile glp = GLProfile.getGL2ES2(); + final GLCapabilities caps = new GLCapabilities(glp); + caps.setSampleBuffers(true); + caps.setNumSamples(4); + caps.setDepthBits(0); + testGLFBODrawableImpl(caps, 0, new MultisampleDemoES2(true)); } @Test - public void test04_GL2ES2_FBODemoMSAA4() throws InterruptedException { + public void test04_GL2ES2_FBODemoMSAA4_Normal() throws InterruptedException { final GLProfile glp = GLProfile.getGL2ES2(); final FBOMix2DemosES2 demo = new FBOMix2DemosES2(0); demo.setDoRotation(false); final GLCapabilities caps = new GLCapabilities(glp); caps.setSampleBuffers(true); caps.setNumSamples(4); - testGLFBODrawableImpl(caps, demo); + testGLFBODrawableImpl(caps, GLFBODrawable.FBOMODE_USE_TEXTURE, demo); } @Test - public void test05_EGLES2_Demo0Normal() throws InterruptedException { + public void test11_EGLES2_Demo0Normal() throws InterruptedException { if( GLProfile.isAvailable(GLProfile.GLES2) ) { final GLProfile glp = GLProfile.get(GLProfile.GLES2); final GLCapabilities caps = new GLCapabilities(glp); - testGLFBODrawableImpl(caps, new GearsES2(0)); + testGLFBODrawableImpl(caps, GLFBODrawable.FBOMODE_USE_TEXTURE, new GearsES2(0)); } else { System.err.println("EGL ES2 n/a"); } } @Test - public void test06_GL3_Demo0Normal() throws InterruptedException { - if( GLProfile.isAvailable(GLProfile.GL3) ) { - final GLProfile glp = GLProfile.get(GLProfile.GL3); + public void test13_EGLES2_Demo0MSAA4() throws InterruptedException { + if( GLProfile.isAvailable(GLProfile.GLES2) ) { + final GLProfile glp = GLProfile.get(GLProfile.GLES2); final GLCapabilities caps = new GLCapabilities(glp); - testGLFBODrawableImpl(caps, new GearsES2(0)); + caps.setSampleBuffers(true); + caps.setNumSamples(4); + testGLFBODrawableImpl(caps, GLFBODrawable.FBOMODE_USE_TEXTURE, new GearsES2(0)); } else { - System.err.println("GL3 n/a"); + System.err.println("EGL ES2 n/a"); } } @Test - public void test07_EGLES2_Demo0MSAA4() throws InterruptedException { - if( GLProfile.isAvailable(GLProfile.GLES2) ) { - final GLProfile glp = GLProfile.get(GLProfile.GLES2); + public void test21_GL3_Demo0Normal() throws InterruptedException { + if( GLProfile.isAvailable(GLProfile.GL3) ) { + final GLProfile glp = GLProfile.get(GLProfile.GL3); final GLCapabilities caps = new GLCapabilities(glp); - caps.setSampleBuffers(true); - caps.setNumSamples(4); - testGLFBODrawableImpl(caps, new GearsES2(0)); + testGLFBODrawableImpl(caps, GLFBODrawable.FBOMODE_USE_TEXTURE, new GearsES2(0)); } else { - System.err.println("EGL ES2 n/a"); + System.err.println("GL3 n/a"); } } - void testGLFBODrawableImpl(final GLCapabilities caps, final GLEventListener demo) throws InterruptedException { + void testGLFBODrawableImpl(final GLCapabilities caps, final int fboMode, final GLEventListener demo) throws InterruptedException { caps.setFBO(true); final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); final GLOffscreenAutoDrawable.FBO glad = (GLOffscreenAutoDrawable.FBO) factory.createOffscreenAutoDrawable(null, caps, null, widthStep*szStep, heightStep*szStep); Assert.assertNotNull(glad); + System.out.println("Requested: "+caps); System.out.println("Realized GLAD: "+glad); System.out.println("Realized GLAD: "+glad.getChosenGLCapabilities()); Assert.assertTrue("FBO drawable is initialized before ctx creation", !glad.isInitialized()); + glad.setFBOMode(fboMode); glad.display(); // initial display incl. init! { @@ -160,6 +195,13 @@ public class TestFBOAutoDrawableFactoryNEWT extends UITestCase { } Assert.assertTrue("FBO drawable is not initialized after ctx creation", glad.isInitialized()); + final boolean expDepth = caps.getDepthBits() > 0; + final boolean reqDepth = glad.getRequestedGLCapabilities().getDepthBits() > 0; + final boolean hasDepth = glad.getChosenGLCapabilities().getDepthBits() > 0; + System.out.println("Depth: exp "+expDepth+", req "+reqDepth+", has "+hasDepth); + Assert.assertEquals("Depth: expected not passed to requested", expDepth, reqDepth); + Assert.assertEquals("Depth: requested not passed to chosen", reqDepth, hasDepth); + // // FBO incl. MSAA is fully initialized now // @@ -179,20 +221,27 @@ public class TestFBOAutoDrawableFactoryNEWT extends UITestCase { Assert.assertTrue("FBO Back is not initialized before ctx creation", fboBack.isInitialized()); if( chosenCaps.getDoubleBuffered() ) { - Assert.assertTrue("FBO are equal: "+fboFront+" == "+fboBack, !fboFront.equals(fboBack)); + Assert.assertNotEquals("FBO are equal: "+fboFront+" == "+fboBack, fboFront, fboBack); Assert.assertNotSame(fboFront, fboBack); } else { - Assert.assertTrue("FBO are not equal: "+fboFront+" != "+fboBack, fboFront.equals(fboBack)); + Assert.assertEquals("FBO are not equal: "+fboFront+" != "+fboBack, fboFront, fboBack); Assert.assertSame(fboFront, fboBack); } - final FBObject.TextureAttachment texAttachA, texAttachB; + final FBObject.Colorbuffer color0, color1; - texAttachA = glad.getTextureBuffer(GL.GL_FRONT); + color0 = glad.getColorbuffer(GL.GL_FRONT); if(0==glad.getNumSamples()) { - texAttachB = glad.getTextureBuffer(GL.GL_BACK); + color1 = glad.getColorbuffer(GL.GL_BACK); } else { - texAttachB = null; + color1 = null; + } + + final boolean expTexture = 0 != ( GLFBODrawable.FBOMODE_USE_TEXTURE & glad.getFBOMode() ); + System.out.println("Texture: exp "+expTexture+", hasFront "+color0.isTextureAttachment()); + Assert.assertEquals("Texture: Front", expTexture, color0.isTextureAttachment()); + if(0==glad.getNumSamples()) { + Assert.assertEquals("Texture: Back", expTexture, color1.isTextureAttachment()); } final FBObject.Colorbuffer colorA, colorB; @@ -203,19 +252,33 @@ public class TestFBOAutoDrawableFactoryNEWT extends UITestCase { colorB = fboBack.getColorbuffer(0); Assert.assertNotNull(colorB); - depthA = fboFront.getDepthAttachment(); - Assert.assertNotNull(depthA); - depthB = fboBack.getDepthAttachment(); - Assert.assertNotNull(depthB); + Assert.assertEquals("Texture: Front", expTexture, colorA.isTextureAttachment()); + if(0==glad.getNumSamples()) { + Assert.assertEquals("Texture: Back", expTexture, colorB.isTextureAttachment()); + } else { + Assert.assertEquals("Texture: MSAA Back is Texture", false, colorB.isTextureAttachment()); + } + + if( hasDepth ) { + depthA = fboFront.getDepthAttachment(); + Assert.assertNotNull(depthA); + depthB = fboBack.getDepthAttachment(); + Assert.assertNotNull(depthB); + } else { + depthA = null; + depthB = null; + } glad.display(); // SWAP_ODD if( chosenCaps.getDoubleBuffered() ) { // double buffer or MSAA - Assert.assertTrue("Color attachments are equal: "+colorB+" == "+colorA, !colorB.equals(colorA)); + Assert.assertNotEquals("Color attachments are equal: "+colorB+" == "+colorA, colorA, colorB); Assert.assertNotSame(colorB, colorA); - Assert.assertTrue("Depth attachments are equal: "+depthB+" == "+depthA, !depthB.equals(depthA)); - Assert.assertNotSame(depthB, depthA); + if( hasDepth ) { + Assert.assertNotEquals("Depth attachments are equal: "+depthB+" == "+depthA, depthA, depthB); + Assert.assertNotSame(depthB, depthA); + } } else { // single buffer Assert.assertEquals(colorA, colorB); @@ -224,40 +287,40 @@ public class TestFBOAutoDrawableFactoryNEWT extends UITestCase { Assert.assertSame(depthA, depthB); } - Assert.assertEquals(texAttachA, colorA); - Assert.assertSame(texAttachA, colorA); + Assert.assertEquals(color0, colorA); + Assert.assertSame(color0, colorA); if(0==glad.getNumSamples()) { - Assert.assertEquals(texAttachB, colorB); - Assert.assertSame(texAttachB, colorB); + Assert.assertEquals(color1, colorB); + Assert.assertSame(color1, colorB); } if( chosenCaps.getNumSamples() > 0 ) { // MSAA final FBObject _fboFront = glad.getFBObject(GL.GL_FRONT); final FBObject _fboBack = glad.getFBObject(GL.GL_BACK); - Assert.assertTrue("FBO are not equal: "+fboFront+" != "+_fboFront, fboFront.equals(_fboFront)); + Assert.assertEquals("FBO are not equal: "+fboFront+" != "+_fboFront, fboFront, _fboFront); Assert.assertSame(fboFront, _fboFront); - Assert.assertTrue("FBO are not equal: "+fboBack+" != "+_fboBack, fboBack.equals(_fboBack)); + Assert.assertEquals("FBO are not equal: "+fboBack+" != "+_fboBack, fboBack, _fboBack); Assert.assertSame(fboBack, _fboBack); } else if( chosenCaps.getDoubleBuffered() ) { // real double buffer final FBObject _fboFront = glad.getFBObject(GL.GL_FRONT); final FBObject _fboBack = glad.getFBObject(GL.GL_BACK); - Assert.assertTrue("FBO are not equal: "+fboBack+" != "+_fboFront, fboBack.equals(_fboFront)); + Assert.assertEquals("FBO are not equal: "+fboBack+" != "+_fboFront, fboBack, _fboFront); Assert.assertSame(fboBack, _fboFront); - Assert.assertTrue("FBO are not equal: "+fboFront+" != "+_fboBack, fboFront.equals(_fboBack)); + Assert.assertEquals("FBO are not equal: "+fboFront+" != "+_fboBack, fboFront, _fboBack); Assert.assertSame(fboFront, _fboBack); } else { // single buffer final FBObject _fboFront = glad.getFBObject(GL.GL_FRONT); final FBObject _fboBack = glad.getFBObject(GL.GL_BACK); - Assert.assertTrue("FBO are not equal: "+fboFront+" != "+_fboFront, fboFront.equals(_fboFront)); + Assert.assertEquals("FBO are not equal: "+fboFront+" != "+_fboFront, fboFront, _fboFront); Assert.assertSame(fboFront, _fboFront); - Assert.assertTrue("FBO are not equal: "+fboBack+" != "+_fboFront, fboBack.equals(_fboFront)); + Assert.assertEquals("FBO are not equal: "+fboBack+" != "+_fboFront, fboBack, _fboFront); Assert.assertSame(fboBack, _fboFront); - Assert.assertTrue("FBO are not equal: "+fboBack+" != "+_fboBack, fboBack.equals(_fboBack)); + Assert.assertEquals("FBO are not equal: "+fboBack+" != "+_fboBack, fboBack, _fboBack); Assert.assertSame(fboBack, _fboBack); - Assert.assertTrue("FBO are not equal: "+fboFront+" != "+_fboBack, fboFront.equals(_fboBack)); + Assert.assertEquals("FBO are not equal: "+fboFront+" != "+_fboBack, fboFront, _fboBack); Assert.assertSame(fboFront, _fboBack); } @@ -301,12 +364,16 @@ public class TestFBOAutoDrawableFactoryNEWT extends UITestCase { FBObject.RenderAttachment _depth = _fboFront.getDepthAttachment(); System.err.println("Resize1.oldDepth "+depthA); System.err.println("Resize1.newDepth "+_depth); - Assert.assertNotNull(_depth); + if( hasDepth ) { + Assert.assertNotNull(_depth); + } Assert.assertEquals(depthA, _depth); Assert.assertSame(depthA, _depth); _depth = _fboBack.getDepthAttachment(); - Assert.assertNotNull(_depth); + if( hasDepth ) { + Assert.assertNotNull(_depth); + } Assert.assertEquals(depthB, _depth); Assert.assertSame(depthB, _depth); @@ -348,12 +415,16 @@ public class TestFBOAutoDrawableFactoryNEWT extends UITestCase { Assert.assertSame(colorB, _color); FBObject.RenderAttachment _depth = fboBack.getDepthAttachment(); - Assert.assertNotNull(_depth); // MSAA back w/ depth + if( hasDepth ) { + Assert.assertNotNull(_depth); // MSAA back w/ depth + } Assert.assertEquals(depthB, _depth); Assert.assertSame(depthB, _depth); _depth = fboFront.getDepthAttachment(); - Assert.assertNotNull(_depth); + if( hasDepth ) { + Assert.assertNotNull(_depth); + } Assert.assertEquals(depthA, _depth); Assert.assertSame(depthA, _depth); @@ -364,7 +435,10 @@ public class TestFBOAutoDrawableFactoryNEWT extends UITestCase { } // 6 + 7 (samples + display) - glad.setNumSamples(glad.getGL(), chosenCaps.getNumSamples() > 0 ? 0 : 4); // triggers repaint + final int oldSampleCount = chosenCaps.getNumSamples(); + final int newSampleCount = oldSampleCount > 0 ? 0 : 4; + System.out.println("Resize3.sampleCount: "+oldSampleCount+" -> "+newSampleCount); + glad.setNumSamples(glad.getGL(), newSampleCount); // triggers repaint snapshotGLEventListener.setMakeSnapshot(); glad.display(); // actual screenshot diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOMRTNEWT01.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOMRTNEWT01.java index ba57c6dbd..977ecbf03 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOMRTNEWT01.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOMRTNEWT01.java @@ -73,7 +73,7 @@ public class TestFBOMRTNEWT01 extends UITestCase { System.err.println("Test requires GL2/GL3 profile."); return; } - final NEWTGLContext.WindowContext winctx = NEWTGLContext.createOnscreenWindow( + final NEWTGLContext.WindowContext winctx = NEWTGLContext.createWindow( new GLCapabilities(GLProfile.getGL2GL3()), width/step, height/step, true); final GLDrawable drawable = winctx.context.getGLDrawable(); final GL2GL3 gl = winctx.context.getGL().getGL2GL3(); @@ -167,7 +167,7 @@ public class TestFBOMRTNEWT01 extends UITestCase { // FBO w/ 2 texture2D color buffers final FBObject fbo_mrt = new FBObject(); - fbo_mrt.reset(gl, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + fbo_mrt.init(gl, drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), 0); final TextureAttachment texA0 = fbo_mrt.attachTexture2D(gl, texA0Point, true, GL.GL_NEAREST, GL.GL_NEAREST, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); final TextureAttachment texA1; if(fbo_mrt.getMaxColorAttachments() > 1) { @@ -176,7 +176,7 @@ public class TestFBOMRTNEWT01 extends UITestCase { texA1 = null; System.err.println("FBO supports only one attachment, no MRT available!"); } - fbo_mrt.attachRenderbuffer(gl, Type.DEPTH, 24); + fbo_mrt.attachRenderbuffer(gl, Type.DEPTH, FBObject.CHOSEN_BITS); Assert.assertTrue( fbo_mrt.isStatusValid() ) ; fbo_mrt.unbind(gl); @@ -257,7 +257,7 @@ public class TestFBOMRTNEWT01 extends UITestCase { final int w = width/step * j; final int h = height/step * j; System.err.println("resize: "+step_i+" -> "+j+" - "+w+"x"+h); - fbo_mrt.reset(gl, w, h); + fbo_mrt.reset(gl, w, h, 0); winctx.window.setSize(w, h); step_i = j; } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOMix2DemosES2NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOMix2DemosES2NEWT.java index f8feefdcf..5eebf9cdd 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOMix2DemosES2NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOMix2DemosES2NEWT.java @@ -62,14 +62,14 @@ import org.junit.runners.MethodSorters; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestFBOMix2DemosES2NEWT extends UITestCase { - static long duration = 500; // ms + static long duration = 1000; // ms static int swapInterval = 1; static boolean showFPS = false; static boolean forceES2 = false; static boolean doRotate = true; static boolean demo0Only = false; static int globalNumSamples = 0; - static boolean mainRun = false; + static boolean manual = false; @AfterClass public static void releaseClass() { @@ -81,7 +81,7 @@ public class TestFBOMix2DemosES2NEWT extends UITestCase { final GLWindow glWindow = GLWindow.create(caps); Assert.assertNotNull(glWindow); glWindow.setTitle("Gears NEWT Test (translucent "+!caps.isBackgroundOpaque()+"), swapInterval "+swapInterval); - if(mainRun) { + if(manual) { glWindow.setSize(512, 512); } else { glWindow.setSize(128, 128); @@ -100,7 +100,7 @@ public class TestFBOMix2DemosES2NEWT extends UITestCase { } public void dispose(final GLAutoDrawable drawable) {} public void display(final GLAutoDrawable drawable) { - if(mainRun) return; + if(manual) return; final int dw = drawable.getSurfaceWidth(); final int dh = drawable.getSurfaceHeight(); @@ -112,18 +112,24 @@ public class TestFBOMix2DemosES2NEWT extends UITestCase { snapshot(i++, "msaa"+demo.getMSAA(), drawable.getGL(), screenshot, TextureIO.PNG, null); } if( 3 == c ) { + demo.setMSAA(4); + } else if( 6 == c ) { new Thread() { @Override public void run() { - demo.setMSAA(4); + glWindow.setSize(dw+64, dh+64); } }.start(); - } else if( 6 == c ) { + } else if( 9 == c ) { + demo.setMSAA(8); + } else if( 12 == c ) { + demo.setMSAA(0); + } else if( 15 == c ) { new Thread() { @Override public void run() { - demo.setMSAA(8); + glWindow.setSize(dw+128, dh+128); } }.start(); - } else if(9 == c) { + } else if( 18 == c ) { c=0; new Thread() { @Override @@ -203,8 +209,8 @@ public class TestFBOMix2DemosES2NEWT extends UITestCase { } @Test - public void test01_Main() throws InterruptedException { - if( mainRun ) { + public void test00_Manual() throws InterruptedException { + if( manual ) { final GLCapabilities caps = new GLCapabilities(forceES2 ? GLProfile.get(GLProfile.GLES2) : GLProfile.getGL2ES2()); caps.setAlphaBits(1); runTestGL(caps, globalNumSamples); @@ -212,17 +218,25 @@ public class TestFBOMix2DemosES2NEWT extends UITestCase { } @Test - public void test01() throws InterruptedException { - if( mainRun ) return ; + public void test01_startMSAA0() throws InterruptedException { + if( manual ) return ; final GLCapabilities caps = new GLCapabilities(forceES2 ? GLProfile.get(GLProfile.GLES2) : GLProfile.getGL2ES2()); caps.setAlphaBits(1); runTestGL(caps, 0); } + @Test + public void test02_startMSAA4() throws InterruptedException { + if( manual ) return ; + final GLCapabilities caps = new GLCapabilities(forceES2 ? GLProfile.get(GLProfile.GLES2) : GLProfile.getGL2ES2()); + caps.setAlphaBits(1); + runTestGL(caps, 4); + } + public static void main(final String args[]) throws IOException { boolean waitForKey = false; - mainRun = true; + manual = false; for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { @@ -244,13 +258,14 @@ public class TestFBOMix2DemosES2NEWT extends UITestCase { demo0Only = true; } else if(args[i].equals("-wait")) { waitForKey = true; - } else if(args[i].equals("-nomain")) { - mainRun = false; + } else if(args[i].equals("-manual")) { + manual = true; } } System.err.println("swapInterval "+swapInterval); System.err.println("forceES2 "+forceES2); + System.err.println("manual "+manual); if(waitForKey) { final BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOOffThreadSharedContextMix2DemosES2NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOOffThreadSharedContextMix2DemosES2NEWT.java index e1e1c3fbc..13feb2372 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOOffThreadSharedContextMix2DemosES2NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOOffThreadSharedContextMix2DemosES2NEWT.java @@ -147,7 +147,7 @@ public class TestFBOOffThreadSharedContextMix2DemosES2NEWT extends UITestCase { fbod1.getNativeSurface().addSurfaceUpdatedListener(new SurfaceUpdatedListener() { @Override public void surfaceUpdated(final Object updater, final NativeSurface ns, final long when) { - mixerDemo.setTexID0(fbod1.getTextureBuffer(GL.GL_FRONT).getName()); + mixerDemo.setTexID0(fbod1.getColorbuffer(GL.GL_FRONT).getName()); } }); fbod1.display(); // init System.err.println("FBOD1 "+fbod1); @@ -163,15 +163,15 @@ public class TestFBOOffThreadSharedContextMix2DemosES2NEWT extends UITestCase { fbod2.getNativeSurface().addSurfaceUpdatedListener(new SurfaceUpdatedListener() { @Override public void surfaceUpdated(final Object updater, final NativeSurface ns, final long when) { - mixerDemo.setTexID1(fbod2.getTextureBuffer(GL.GL_FRONT).getName()); + mixerDemo.setTexID1(fbod2.getColorbuffer(GL.GL_FRONT).getName()); } }); fbod2.display(); // init System.err.println("FBOD2 "+fbod2); Assert.assertTrue(fbod2.isInitialized()); // preinit texIDs - mixerDemo.setTexID0(fbod1.getTextureBuffer(GL.GL_FRONT).getName()); - mixerDemo.setTexID1(fbod2.getTextureBuffer(GL.GL_FRONT).getName()); + mixerDemo.setTexID0(fbod1.getColorbuffer(GL.GL_FRONT).getName()); + mixerDemo.setTexID1(fbod2.getColorbuffer(GL.GL_FRONT).getName()); glWindow.addGLEventListener(mixerDemo); glWindow.addGLEventListener(new GLEventListener() { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOOnThreadSharedContext1DemoES2NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOOnThreadSharedContext1DemoES2NEWT.java index c9c156218..4ed762a4e 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOOnThreadSharedContext1DemoES2NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestFBOOnThreadSharedContext1DemoES2NEWT.java @@ -144,14 +144,14 @@ public class TestFBOOnThreadSharedContext1DemoES2NEWT extends UITestCase { fbod1.getNativeSurface().addSurfaceUpdatedListener(new SurfaceUpdatedListener() { @Override public void surfaceUpdated(final Object updater, final NativeSurface ns, final long when) { - mixerDemo.setTexID0(fbod1.getTextureBuffer(GL.GL_FRONT).getName()); + mixerDemo.setTexID0(fbod1.getColorbuffer(GL.GL_FRONT).getName()); } }); fbod1.display(); // init System.err.println("FBOD1 "+fbod1); Assert.assertTrue(fbod1.isInitialized()); // preinit texIDs - mixerDemo.setTexID0(fbod1.getTextureBuffer(GL.GL_FRONT).getName()); + mixerDemo.setTexID0(fbod1.getColorbuffer(GL.GL_FRONT).getName()); glWindow.addWindowListener(new WindowAdapter() { @Override diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLException01NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLException01NEWT.java new file mode 100644 index 000000000..cdbe3af94 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLException01NEWT.java @@ -0,0 +1,412 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.jogl.acore; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.test.junit.util.MiscUtils; +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.Animator; +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; + +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLProfile; +import javax.media.opengl.GLRunnable; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestGLException01NEWT extends UITestCase { + static GLProfile glp; + static int width, height; + + @SuppressWarnings("serial") + static class AnimException extends RuntimeException { + final Thread thread; + final GLAnimatorControl animator; + final GLAutoDrawable drawable; + public AnimException(final Thread thread, final GLAnimatorControl animator, final GLAutoDrawable drawable, final Throwable cause) { + super(cause); + this.thread = thread; + this.animator = animator; + this.drawable = drawable; + } + } + + @BeforeClass + public static void initClass() { + glp = GLProfile.getGL2ES2(); + Assert.assertNotNull(glp); + width = 512; + height = 512; + } + + public static void dumpThrowable(final Throwable t) { + System.err.println("User caught exception "+t.getClass().getSimpleName()+": "+t.getMessage()+" on thread "+Thread.currentThread().getName()); + t.printStackTrace(); + } + + protected void runTestGL(final GLCapabilities caps, final boolean onThread, + final boolean throwInInit, final boolean throwInDisplay, + final boolean throwInReshape, final boolean throwInInvoke, + final boolean throwInDispose) throws InterruptedException { + final GLWindow glWindow = GLWindow.create(caps); + Assert.assertNotNull(glWindow); + glWindow.setTitle(getTestMethodName()); + final GearsES2 demo1 = new GearsES2(); + demo1.setVerbose(false); + glWindow.addGLEventListener(demo1); + final AtomicInteger cleanInitCount = new AtomicInteger(); + final AtomicInteger cleanDisposeCount = new AtomicInteger(); + final AtomicInteger cleanDisplayCount = new AtomicInteger(); + final AtomicInteger cleanReshapeCount = new AtomicInteger(); + final AtomicInteger cleanInvokeCount = new AtomicInteger(); + final AtomicInteger allInitCount = new AtomicInteger(); + final AtomicInteger allDisposeCount = new AtomicInteger(); + final AtomicInteger allDisplayCount = new AtomicInteger(); + final AtomicInteger allReshapeCount = new AtomicInteger(); + final AtomicInteger allInvokeCount = new AtomicInteger(); + final AtomicInteger exceptionSent = new AtomicInteger(); + + glWindow.addGLEventListener(new GLEventListener() { + @Override + public void init(final GLAutoDrawable drawable) { + if( throwInInit ) { + exceptionSent.incrementAndGet(); + throw new RuntimeException("<Injected GLEventListener exception in init: #"+exceptionSent.get()+" on thread "+Thread.currentThread().getName()+">"); + } + } + @Override + public void dispose(final GLAutoDrawable drawable) { + if( throwInDispose ) { + exceptionSent.incrementAndGet(); + throw new RuntimeException("<Injected GLEventListener exception in dispose: #"+exceptionSent.get()+" on thread "+Thread.currentThread().getName()+">"); + } + } + @Override + public void display(final GLAutoDrawable drawable) { + if( throwInDisplay ) { + exceptionSent.incrementAndGet(); + throw new RuntimeException("<Injected GLEventListener exception in display: #"+exceptionSent.get()+" on thread "+Thread.currentThread().getName()+">"); + } + } + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { + if( throwInReshape ) { + exceptionSent.incrementAndGet(); + throw new RuntimeException("<Injected GLEventListener exception in reshape: #"+exceptionSent.get()+" on thread "+Thread.currentThread().getName()+">"); + } + } + }); + final GLRunnable glRunnableInject = new GLRunnable() { + @Override + public boolean run(final GLAutoDrawable drawable) { + if( throwInInvoke ) { + exceptionSent.incrementAndGet(); + throw new RuntimeException("<Injected GLEventListener exception in invoke: #"+exceptionSent.get()+" on thread "+Thread.currentThread().getName()+">"); + } + return true; + } + }; + final GLRunnable glRunnableCount = new GLRunnable() { + @Override + public boolean run(final GLAutoDrawable drawable) { + if( 0 == exceptionSent.get() ) { + cleanInvokeCount.incrementAndGet(); + } + allInvokeCount.incrementAndGet(); + return true; + } + }; + + glWindow.addGLEventListener(new GLEventListener() { + @Override + public void init(final GLAutoDrawable drawable) { + if( 0 == exceptionSent.get() ) { + cleanInitCount.incrementAndGet(); + } + allInitCount.incrementAndGet(); + } + @Override + public void dispose(final GLAutoDrawable drawable) { + if( 0 == exceptionSent.get() ) { + cleanDisposeCount.incrementAndGet(); + } + allDisposeCount.incrementAndGet(); + } + @Override + public void display(final GLAutoDrawable drawable) { + if( 0 == exceptionSent.get() ) { + cleanDisplayCount.incrementAndGet(); + } + allDisplayCount.incrementAndGet(); + } + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { + if( 0 == exceptionSent.get() ) { + cleanReshapeCount.incrementAndGet(); + } + allReshapeCount.incrementAndGet(); + } + }); + + RuntimeException exceptionAtInitReshapeDisplay = null; + RuntimeException exceptionAtInvoke = null; + RuntimeException exceptionAtDispose = null; + final List<AnimException> exceptionsAtGLAnimatorControl = new ArrayList<AnimException>(); + final GLAnimatorControl.UncaughtExceptionHandler uncaughtExceptionHandler; + + final Animator animator; + if( onThread ) { + animator = null; + uncaughtExceptionHandler = null; + } else { + animator = new Animator(glWindow); + uncaughtExceptionHandler = new GLAnimatorControl.UncaughtExceptionHandler() { + @Override + public void uncaughtException(final GLAnimatorControl animator, final GLAutoDrawable drawable, final Throwable cause) { + final AnimException ae = new AnimException(animator.getThread(), animator, drawable, cause); + exceptionsAtGLAnimatorControl.add(ae); + dumpThrowable(ae); + } }; + animator.setUncaughtExceptionHandler(uncaughtExceptionHandler); + } + + glWindow.setSize(width, height); + + if( !onThread ) { + animator.setUpdateFPSFrames(1, null); + animator.start(); + } + try { + glWindow.setVisible(true); + } catch (final RuntimeException re) { + exceptionAtInitReshapeDisplay = re; + dumpThrowable(re); + } + + try { + glWindow.invoke(true, glRunnableInject); + glWindow.invoke(true, glRunnableCount); + } catch (final RuntimeException re) { + exceptionAtInvoke = re; + dumpThrowable(re); + } + + final long t0 = System.currentTimeMillis(); + long t1 = t0; + while(0 == exceptionSent.get() && ( onThread || animator.isAnimating() ) && t1-t0<duration ) { + if( onThread ) { + try { + glWindow.display(); + } catch (final RuntimeException re) { + exceptionAtInitReshapeDisplay = re; + dumpThrowable(re); + } + } + Thread.sleep(100); + t1 = System.currentTimeMillis(); + } + + if( !onThread ) { + animator.stop(); + } + try { + glWindow.destroy(); + } catch (final RuntimeException re) { + exceptionAtDispose = re; + dumpThrowable(re); + } + + final boolean onAnimThread = !onThread && !throwInDispose; /** dispose happens on [AWT|NEWT] EDT, not on animator thread! */ + + System.err.println("This-Thread : "+onThread); + System.err.println("Anim-Thread : "+onAnimThread); + System.err.println("ExceptionSent : "+exceptionSent.get()); + System.err.println("Exception @ Init/Reshape/Display: "+(null != exceptionAtInitReshapeDisplay)); + System.err.println("Exception @ Invoke : "+(null != exceptionAtInvoke)); + System.err.println("Exception @ Dispose : "+(null != exceptionAtDispose)); + System.err.println("Exception @ GLAnimatorControl : "+exceptionsAtGLAnimatorControl.size()); + System.err.println("Init Count : "+cleanInitCount.get()+" / "+allInitCount.get()); + System.err.println("Reshape Count : "+cleanReshapeCount.get()+" / "+allReshapeCount.get()); + System.err.println("Display Count : "+cleanDisplayCount.get()+" / "+allDisplayCount.get()); + System.err.println("Invoke Count : "+cleanInvokeCount.get()+" / "+allInvokeCount.get()); + System.err.println("Dispose Count : "+cleanDisposeCount.get()+" / "+allDisposeCount.get()); + + if( throwInInit || throwInReshape || throwInDisplay || throwInDispose || throwInInvoke ) { + Assert.assertTrue("Not one exception sent, but "+exceptionSent.get(), 0 < exceptionSent.get()); + if( onAnimThread ) { + Assert.assertEquals("No exception forwarded from init to animator-handler", 1, exceptionsAtGLAnimatorControl.size()); + Assert.assertNull("Exception forwarded from init, on-thread", exceptionAtInitReshapeDisplay); + } + if( throwInInit ) { + if( !onAnimThread ) { + Assert.assertNotNull("No exception forwarded from init, on-thread", exceptionAtInitReshapeDisplay); + Assert.assertEquals("Exception forwarded from init to animator-handler", 0, exceptionsAtGLAnimatorControl.size()); + } + Assert.assertEquals("Init Count", 0, cleanInitCount.get()); + Assert.assertEquals("Reshape Count", 0, cleanReshapeCount.get()); + Assert.assertEquals("Display Count", 0, cleanDisplayCount.get()); + Assert.assertEquals("Invoke Count", 0, cleanInvokeCount.get()); + Assert.assertEquals("Dispose Count", 0, cleanDisposeCount.get()); + } else if( throwInReshape ) { + if( !onAnimThread ) { + Assert.assertNotNull("No exception forwarded from reshape, on-thread", exceptionAtInitReshapeDisplay); + Assert.assertEquals("Exception forwarded from init to animator-handler", 0, exceptionsAtGLAnimatorControl.size()); + } + Assert.assertEquals("Init Count", 1, cleanInitCount.get()); + Assert.assertEquals("Reshape Count", 0, cleanReshapeCount.get()); + Assert.assertEquals("Display Count", 0, cleanDisplayCount.get()); + Assert.assertEquals("Invoke Count", 0, cleanInvokeCount.get()); + Assert.assertEquals("Dispose Count", 0, cleanDisposeCount.get()); + } else if( throwInDisplay ) { + if( !onAnimThread ) { + Assert.assertNotNull("No exception forwarded from display, on-thread", exceptionAtInitReshapeDisplay); + Assert.assertEquals("Exception forwarded from init to animator-handler", 0, exceptionsAtGLAnimatorControl.size()); + } + Assert.assertEquals("Init Count", 1, cleanInitCount.get()); + Assert.assertEquals("Reshape Count", 1, cleanReshapeCount.get()); + Assert.assertEquals("Display Count", 0, cleanDisplayCount.get()); + Assert.assertEquals("Invoke Count", 0, cleanInvokeCount.get()); + Assert.assertEquals("Dispose Count", 0, cleanDisposeCount.get()); + } else if( throwInInvoke ) { + if( !onAnimThread ) { + Assert.assertNotNull("No exception forwarded from invoke, on-thread", exceptionAtInvoke); + Assert.assertEquals("Exception forwarded from init to animator-handler", 0, exceptionsAtGLAnimatorControl.size()); + } + Assert.assertEquals("Init Count", 1, cleanInitCount.get()); + Assert.assertEquals("Reshape Count", 1, cleanReshapeCount.get()); + Assert.assertTrue ("Display count not greater-equal 1, but "+cleanDisplayCount.get(), 1 <= cleanDisplayCount.get()); + Assert.assertEquals("Invoke Count", 0, cleanInvokeCount.get()); + Assert.assertEquals("Dispose Count", 0, cleanDisposeCount.get()); + } else if( throwInDispose ) { + if( !onAnimThread ) { + Assert.assertNotNull("No exception forwarded from dispose, on-thread", exceptionAtDispose); + Assert.assertEquals("Exception forwarded from init to animator-handler", 0, exceptionsAtGLAnimatorControl.size()); + } + Assert.assertEquals("Init Count", 1, cleanInitCount.get()); + Assert.assertEquals("Reshape Count", 1, cleanReshapeCount.get()); + Assert.assertTrue ("Display count not greater-equal 1, but "+cleanDisplayCount.get(), 1 <= cleanDisplayCount.get()); + Assert.assertEquals("Invoke Count", 1, cleanInvokeCount.get()); + Assert.assertEquals("Dispose Count", 0, cleanDisposeCount.get()); + } + } + } + + @Test + public void test01OnThreadAtInit() throws InterruptedException { + final GLCapabilities caps = new GLCapabilities(glp); + caps.setBackgroundOpaque(true); // default + runTestGL(caps, true /* onThread */, true /* init */, false /* display */, false /* reshape */, false /* invoke */, false /* dispose */); + } + @Test + public void test02OnThreadAtReshape() throws InterruptedException { + final GLCapabilities caps = new GLCapabilities(glp); + caps.setBackgroundOpaque(true); // default + runTestGL(caps, true /* onThread */, false /* init */, false /* display */, true /* reshape */, false /* invoke */, false /* dispose */); + } + @Test + public void test03OnThreadAtDisplay() throws InterruptedException { + final GLCapabilities caps = new GLCapabilities(glp); + caps.setBackgroundOpaque(true); // default + runTestGL(caps, true /* onThread */, false /* init */, true /* display */, false /* reshape */, false /* invoke */, false /* dispose */); + } + @Test + public void test04OnThreadAtInvoke() throws InterruptedException { + final GLCapabilities caps = new GLCapabilities(glp); + caps.setBackgroundOpaque(true); // default + runTestGL(caps, true /* onThread */, false /* init */, true /* display */, false /* reshape */, true /* invoke */, false /* dispose */); + } + @Test + public void test05OnThreadAtDispose() throws InterruptedException { + final GLCapabilities caps = new GLCapabilities(glp); + caps.setBackgroundOpaque(true); // default + runTestGL(caps, true /* onThread */, false /* init */, false /* display */, false /* reshape */, false /* invoke */, true /* dispose */); + } + + @Test + public void test11OffThreadAtInit() throws InterruptedException { + final GLCapabilities caps = new GLCapabilities(glp); + caps.setBackgroundOpaque(true); // default + runTestGL(caps, false /* onThread */, true /* init */, false /* display */, false /* reshape */, false /* invoke */, false /* dispose */); + } + @Test + public void test12OffThreadAtReshape() throws InterruptedException { + final GLCapabilities caps = new GLCapabilities(glp); + caps.setBackgroundOpaque(true); // default + runTestGL(caps, false /* onThread */, false /* init */, false /* display */, true /* reshape */, false /* invoke */, false /* dispose */); + } + @Test + public void test13OffThreadAtDisplay() throws InterruptedException { + final GLCapabilities caps = new GLCapabilities(glp); + caps.setBackgroundOpaque(true); // default + runTestGL(caps, false /* onThread */, false /* init */, true /* display */, false /* reshape */, false /* invoke */, false /* dispose */); + } + @Test + public void test14OffThreadAtInvoke() throws InterruptedException { + final GLCapabilities caps = new GLCapabilities(glp); + caps.setBackgroundOpaque(true); // default + runTestGL(caps, false /* onThread */, false /* init */, true /* display */, false /* reshape */, true /* invoke */, false /* dispose */); + } + @Test + public void test15OffThreadAtDispose() throws InterruptedException { + final GLCapabilities caps = new GLCapabilities(glp); + caps.setBackgroundOpaque(true); // default + runTestGL(caps, false /* onThread */, false /* init */, false /* display */, false /* reshape */, false /* invoke */, true /* dispose */); + } + + static long duration = 500; // ms + + public static void main(final String args[]) { + boolean waitForKey = false; + + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + duration = MiscUtils.atol(args[i], duration); + } else if(args[i].equals("-wait")) { + waitForKey = true; + } + } + if( waitForKey ) { + UITestCase.waitForKey("main"); + } + org.junit.runner.JUnitCore.main(TestGLException01NEWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLOffscreenAutoDrawableBug1044AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLOffscreenAutoDrawableBug1044AWT.java new file mode 100644 index 000000000..18ac7abc2 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLOffscreenAutoDrawableBug1044AWT.java @@ -0,0 +1,106 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.test.junit.jogl.acore; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLProfile; +import javax.media.opengl.fixedfunc.GLLightingFunc; + +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.GLReadBufferUtil; +import com.jogamp.opengl.util.awt.AWTGLReadBufferUtil; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestGLOffscreenAutoDrawableBug1044AWT extends UITestCase { + + @Test + public void test01GLOffscreenDrawable() throws InterruptedException { + final GLReadBufferUtil readBufferUtilRGB888 = new GLReadBufferUtil(false, false); + final GLReadBufferUtil readBufferUtilRGBA8888 = new GLReadBufferUtil(true, false); + final GLDrawableFactory fac = GLDrawableFactory.getFactory(GLProfile.getDefault()); + final GLCapabilities glCap = new GLCapabilities(GLProfile.getMaxFixedFunc(true)); + // Without line below, there is an error on Windows. + glCap.setDoubleBuffered(false); + //makes a new buffer 100x100 + final GLDrawable glad = fac.createOffscreenDrawable(null, glCap, null, 100, 100); + glad.setRealized(true); + final GLContext context = glad.createContext(null); + context.makeCurrent(); + + System.err.println("Chosen: "+glad.getChosenGLCapabilities()); + + final GL2 gl2 = context.getGL().getGL2(); + gl2.glViewport(0, 0, 100, 100); + + gl2.glShadeModel(GLLightingFunc.GL_SMOOTH); + gl2.glClearColor(1.0f, 0.80f, 0.80f, 1); // This Will Clear The Background Color + gl2.glClearDepth(1.0); // Enables Clearing Of The Depth Buffer + gl2.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + gl2.glLoadIdentity(); // Reset The Projection Matrix + + final AWTGLReadBufferUtil agb = new AWTGLReadBufferUtil(glad.getGLProfile(), true); + final BufferedImage image = agb.readPixelsToBufferedImage(context.getGL(), true); + try { + ImageIO.write(image, "PNG", new File(getSimpleTestName(".")+"-AWTImageIO.png")); + } catch (final IOException e) { + e.printStackTrace(); + } + + if(readBufferUtilRGB888.readPixels(gl2, false)) { + readBufferUtilRGB888.write(new File(getSimpleTestName(".")+"-PNGJ-rgb_.png")); + } + readBufferUtilRGB888.dispose(gl2); + if(readBufferUtilRGBA8888.readPixels(gl2, false)) { + readBufferUtilRGBA8888.write(new File(getSimpleTestName(".")+"-PNGJ-rgba.png")); + } + readBufferUtilRGBA8888.dispose(gl2); + + context.destroy(); + glad.setRealized(false); + System.out.println("Done!"); + } + + public static void main(final String[] args) { + org.junit.runner.JUnitCore.main(TestGLOffscreenAutoDrawableBug1044AWT.class.getName()); + } +} + diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLProfile00NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLProfile00NEWT.java index 5861d4233..69ddb7771 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLProfile00NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLProfile00NEWT.java @@ -30,6 +30,7 @@ package com.jogamp.opengl.test.junit.jogl.acore; import java.io.IOException; +import org.junit.Assert; import org.junit.Test; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; @@ -44,8 +45,27 @@ import com.jogamp.opengl.test.junit.util.UITestCase; public class TestGLProfile00NEWT extends UITestCase { @Test - public void testInitSingleton() throws InterruptedException { + public void test01InternedString() { + final String s1 = "GL2"; + final String s2 = "GL2"; + Assert.assertEquals(s1, s2); + Assert.assertTrue("s1-ref != s2-ref", s1 == s2); + Assert.assertTrue("s1-ref != 'GL2'-ref", s1 == "GL2"); + + Assert.assertEquals("GL2", GLProfile.GL2); + Assert.assertTrue("GLProfile-ref != 'GL2'-ref", GLProfile.GL2 == "GL2"); + } + + @Test + public void test02InitSingleton() throws InterruptedException { + Assert.assertFalse("JOGL is initialized before usage", GLProfile.isInitialized()); GLProfile.initSingleton(); + Assert.assertTrue("JOGL is not initialized after enforced initialization", GLProfile.isInitialized()); + } + + @Test + public void test11DumpDesktopGLInfo() throws InterruptedException { + Assert.assertTrue("JOGL is not initialized ...", GLProfile.isInitialized()); System.err.println("Desktop"); final GLDrawableFactory desktopFactory = GLDrawableFactory.getDesktopFactory(); if( null != desktopFactory ) { @@ -54,7 +74,11 @@ public class TestGLProfile00NEWT extends UITestCase { } else { System.err.println("\tNULL"); } + } + @Test + public void test12DumpEGLGLInfo() throws InterruptedException { + Assert.assertTrue("JOGL is not initialized ...", GLProfile.isInitialized()); System.err.println("EGL"); final GLDrawableFactory eglFactory = GLDrawableFactory.getEGLFactory(); if( null != eglFactory ) { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLProfile01NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLProfile01NEWT.java index 633a2822e..3262ff996 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLProfile01NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLProfile01NEWT.java @@ -36,6 +36,7 @@ import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import org.junit.Assert; @@ -72,12 +73,562 @@ public class TestGLProfile01NEWT extends UITestCase { } } - static void validate(final GLProfile glp) { + // + // GL4bc, GL4, GL3bc, GL3, GL2, GL2GL3, GL4ES3, GL3ES3, GL2ES2, GL2ES1, GLES3, GLES2, GLES1 + // + // Real: GL4bc, GL4, GL3bc, GL3, GL2, GLES3, GLES2, GLES1 + // Maps: GL2GL3, GL4ES3, GL3ES3, GL2ES2, GL2ES1 + // + + private static void validateGLProfileGL4bc(final GLProfile glp) { + Assert.assertTrue(glp.isGL4bc()); + Assert.assertTrue(glp.isGL4()); + Assert.assertTrue(glp.isGL3bc()); + Assert.assertTrue(glp.isGL3()); + Assert.assertTrue(glp.isGL2()); + Assert.assertFalse(glp.isGLES3()); + Assert.assertFalse(glp.isGLES2()); + Assert.assertFalse(glp.isGLES1()); + Assert.assertTrue(glp.isGL2GL3()); + Assert.assertTrue(glp.isGL4ES3()); + Assert.assertTrue(glp.isGL3ES3()); + Assert.assertTrue(glp.isGL2ES2()); + Assert.assertTrue(glp.isGL2ES1()); + } + private static void validateGL4bc(final GL gl) { + final GLContext ctx = gl.getContext(); + final boolean gles3CompatAvail = ctx.isGLES3Compatible(); + + Assert.assertTrue(gl.isGL4bc()); + Assert.assertTrue(gl.isGL4()); + Assert.assertTrue(gl.isGL3bc()); + Assert.assertTrue(gl.isGL3()); + Assert.assertTrue(gl.isGL2()); + Assert.assertTrue(gl.isGL2GL3()); + if( gles3CompatAvail ) { + Assert.assertTrue(gl.isGL4ES3()); + } else { + Assert.assertFalse(gl.isGL4ES3()); + } + Assert.assertTrue(gl.isGL3ES3()); + Assert.assertTrue(gl.isGL2ES2()); + Assert.assertTrue(gl.isGL2ES1()); + Assert.assertFalse(gl.isGLES3()); + Assert.assertFalse(gl.isGLES2()); + Assert.assertFalse(gl.isGLES1()); + + Assert.assertTrue(ctx.isGL4bc()); + Assert.assertTrue(ctx.isGL4()); + Assert.assertTrue(ctx.isGL3bc()); + Assert.assertTrue(ctx.isGL3()); + Assert.assertTrue(ctx.isGL2()); + Assert.assertTrue(ctx.isGL2GL3()); + if( gles3CompatAvail ) { + Assert.assertTrue(ctx.isGL4ES3()); + } else { + Assert.assertFalse(ctx.isGL4ES3()); + } + Assert.assertTrue(ctx.isGL3ES3()); + Assert.assertTrue(ctx.isGL2ES2()); + Assert.assertTrue(ctx.isGL2ES1()); + Assert.assertFalse(ctx.isGLES3()); + Assert.assertFalse(ctx.isGLES2()); + Assert.assertFalse(ctx.isGLES1()); + } + + private static void validateGLProfileGL4(final GLProfile glp) { + Assert.assertFalse(glp.isGL4bc()); + Assert.assertTrue(glp.isGL4()); + Assert.assertFalse(glp.isGL3bc()); + Assert.assertTrue(glp.isGL3()); + Assert.assertFalse(glp.isGL2()); + Assert.assertFalse(glp.isGLES3()); + Assert.assertFalse(glp.isGLES2()); + Assert.assertFalse(glp.isGLES1()); + Assert.assertTrue(glp.isGL2GL3()); + Assert.assertTrue(glp.isGL4ES3()); + Assert.assertTrue(glp.isGL3ES3()); + Assert.assertTrue(glp.isGL2ES2()); + Assert.assertFalse(glp.isGL2ES1()); + } + private static void validateGL4(final GL gl) { + final GLContext ctx = gl.getContext(); + final boolean gles3CompatAvail = ctx.isGLES3Compatible(); + + Assert.assertFalse(gl.isGL4bc()); + Assert.assertTrue(gl.isGL4()); + Assert.assertFalse(gl.isGL3bc()); + Assert.assertTrue(gl.isGL3()); + Assert.assertFalse(gl.isGL2()); + Assert.assertTrue(gl.isGL2GL3()); + if( gles3CompatAvail ) { + Assert.assertTrue(gl.isGL4ES3()); + } else { + Assert.assertFalse(gl.isGL4ES3()); + } + Assert.assertTrue(gl.isGL3ES3()); + Assert.assertTrue(gl.isGL2ES2()); + Assert.assertFalse(gl.isGL2ES1()); + Assert.assertFalse(gl.isGLES3()); + Assert.assertFalse(gl.isGLES2()); + Assert.assertFalse(gl.isGLES1()); + + Assert.assertFalse(ctx.isGL4bc()); + Assert.assertTrue(ctx.isGL4()); + Assert.assertFalse(ctx.isGL3bc()); + Assert.assertTrue(ctx.isGL3()); + Assert.assertFalse(ctx.isGL2()); + Assert.assertTrue(ctx.isGL2GL3()); + if( gles3CompatAvail ) { + Assert.assertTrue(ctx.isGL4ES3()); + } else { + Assert.assertFalse(ctx.isGL4ES3()); + } + Assert.assertTrue(ctx.isGL3ES3()); + Assert.assertTrue(ctx.isGL2ES2()); + Assert.assertFalse(ctx.isGL2ES1()); + Assert.assertFalse(ctx.isGLES3()); + Assert.assertFalse(ctx.isGLES2()); + Assert.assertFalse(ctx.isGLES1()); + } + + private static void validateGLProfileGL3bc(final GLProfile glp) { + Assert.assertFalse(glp.isGL4bc()); + Assert.assertFalse(glp.isGL4()); + Assert.assertTrue(glp.isGL3bc()); + Assert.assertTrue(glp.isGL3()); + Assert.assertTrue(glp.isGL2()); + Assert.assertFalse(glp.isGLES3()); + Assert.assertFalse(glp.isGLES2()); + Assert.assertFalse(glp.isGLES1()); + Assert.assertTrue(glp.isGL2GL3()); + Assert.assertFalse(glp.isGL4ES3()); + Assert.assertTrue(glp.isGL3ES3()); + Assert.assertTrue(glp.isGL2ES2()); + Assert.assertTrue(glp.isGL2ES1()); + } + private static void validateGL3bc(final GL gl) { + final GLContext ctx = gl.getContext(); + final boolean gles3CompatAvail = ctx.isGLES3Compatible(); + + Assert.assertFalse(gl.isGL4bc()); + Assert.assertFalse(gl.isGL4()); + Assert.assertTrue(gl.isGL3bc()); + Assert.assertTrue(gl.isGL3()); + Assert.assertTrue(gl.isGL2()); + Assert.assertTrue(gl.isGL2GL3()); + if( gles3CompatAvail ) { // possible w/ GL3 implementations! + Assert.assertTrue(gl.isGL4ES3()); + } else { + Assert.assertFalse(gl.isGL4ES3()); + } + Assert.assertTrue(gl.isGL3ES3()); + Assert.assertTrue(gl.isGL2ES2()); + Assert.assertTrue(gl.isGL2ES1()); + Assert.assertFalse(gl.isGLES3()); + Assert.assertFalse(gl.isGLES2()); + Assert.assertFalse(gl.isGLES1()); + + Assert.assertFalse(ctx.isGL4bc()); + Assert.assertFalse(ctx.isGL4()); + Assert.assertTrue(ctx.isGL3bc()); + Assert.assertTrue(ctx.isGL3()); + Assert.assertTrue(ctx.isGL2()); + Assert.assertTrue(ctx.isGL2GL3()); + if( gles3CompatAvail ) { // possible w/ GL3 implementations! + Assert.assertTrue(ctx.isGL4ES3()); + } else { + Assert.assertFalse(ctx.isGL4ES3()); + } + Assert.assertTrue(ctx.isGL3ES3()); + Assert.assertTrue(ctx.isGL2ES2()); + Assert.assertTrue(ctx.isGL2ES1()); + Assert.assertFalse(ctx.isGLES3()); + Assert.assertFalse(ctx.isGLES2()); + Assert.assertFalse(ctx.isGLES1()); + } + + private static void validateGLProfileGL3(final GLProfile glp) { + Assert.assertFalse(glp.isGL4bc()); + Assert.assertFalse(glp.isGL4()); + Assert.assertFalse(glp.isGL3bc()); + Assert.assertTrue(glp.isGL3()); + Assert.assertFalse(glp.isGL2()); + Assert.assertFalse(glp.isGLES3()); + Assert.assertFalse(glp.isGLES2()); + Assert.assertFalse(glp.isGLES1()); + Assert.assertTrue(glp.isGL2GL3()); + Assert.assertFalse(glp.isGL4ES3()); + Assert.assertTrue(glp.isGL3ES3()); + Assert.assertTrue(glp.isGL2ES2()); + Assert.assertFalse(glp.isGL2ES1()); + } + private static void validateGL3(final GL gl) { + final GLContext ctx = gl.getContext(); + final boolean gles3CompatAvail = ctx.isGLES3Compatible(); + + Assert.assertFalse(gl.isGL4bc()); + Assert.assertFalse(gl.isGL4()); + Assert.assertFalse(gl.isGL3bc()); + Assert.assertTrue(gl.isGL3()); + Assert.assertFalse(gl.isGL2()); + Assert.assertTrue(gl.isGL2GL3()); + if( gles3CompatAvail ) { // possible w/ GL3 implementations! + Assert.assertTrue(gl.isGL4ES3()); + } else { + Assert.assertFalse(gl.isGL4ES3()); + } + Assert.assertTrue(gl.isGL3ES3()); + Assert.assertTrue(gl.isGL2ES2()); + Assert.assertFalse(gl.isGL2ES1()); + Assert.assertFalse(gl.isGLES3()); + Assert.assertFalse(gl.isGLES2()); + Assert.assertFalse(gl.isGLES1()); + + Assert.assertFalse(ctx.isGL4bc()); + Assert.assertFalse(ctx.isGL4()); + Assert.assertFalse(ctx.isGL3bc()); + Assert.assertTrue(ctx.isGL3()); + Assert.assertFalse(ctx.isGL2()); + Assert.assertTrue(ctx.isGL2GL3()); + if( gles3CompatAvail ) { // possible w/ GL3 implementations! + Assert.assertTrue(ctx.isGL4ES3()); + } else { + Assert.assertFalse(ctx.isGL4ES3()); + } + Assert.assertTrue(ctx.isGL3ES3()); + Assert.assertTrue(ctx.isGL2ES2()); + Assert.assertFalse(ctx.isGL2ES1()); + Assert.assertFalse(ctx.isGLES3()); + Assert.assertFalse(ctx.isGLES2()); + Assert.assertFalse(ctx.isGLES1()); + } + + private static void validateGLProfileGL2(final GLProfile glp) { + Assert.assertFalse(glp.isGL4bc()); + Assert.assertFalse(glp.isGL4()); + Assert.assertFalse(glp.isGL3bc()); + Assert.assertFalse(glp.isGL3()); + Assert.assertTrue(glp.isGL2()); + Assert.assertFalse(glp.isGLES3()); + Assert.assertFalse(glp.isGLES2()); + Assert.assertFalse(glp.isGLES1()); + Assert.assertTrue(glp.isGL2GL3()); + Assert.assertFalse(glp.isGL4ES3()); + Assert.assertFalse(glp.isGL3ES3()); + Assert.assertTrue(glp.isGL2ES2()); + Assert.assertTrue(glp.isGL2ES1()); + } + private static void validateGL2(final GL gl) { + final GLContext ctx = gl.getContext(); + final boolean gles3CompatAvail = ctx.isGLES3Compatible(); + + Assert.assertFalse(gl.isGL4bc()); + Assert.assertFalse(gl.isGL4()); + Assert.assertFalse(gl.isGL3bc()); + Assert.assertFalse(gl.isGL3()); + Assert.assertTrue(gl.isGL2()); + Assert.assertTrue(gl.isGL2GL3()); + Assert.assertFalse(gl.isGL4ES3()); + Assert.assertFalse(gl.isGL3ES3()); + Assert.assertTrue(gl.isGL2ES2()); + Assert.assertTrue(gl.isGL2ES1()); + Assert.assertFalse(gl.isGLES3()); + Assert.assertFalse(gl.isGLES2()); + Assert.assertFalse(gl.isGLES1()); + + Assert.assertFalse(ctx.isGL4bc()); + Assert.assertFalse(ctx.isGL4()); + Assert.assertFalse(ctx.isGL3bc()); + Assert.assertFalse(ctx.isGL3()); + Assert.assertTrue(ctx.isGL2()); + Assert.assertTrue(ctx.isGL2GL3()); + Assert.assertFalse(ctx.isGL4ES3()); + Assert.assertFalse(ctx.isGL3ES3()); + Assert.assertFalse(gles3CompatAvail); + Assert.assertTrue(ctx.isGL2ES2()); + Assert.assertTrue(ctx.isGL2ES1()); + Assert.assertFalse(ctx.isGLES3()); + Assert.assertFalse(ctx.isGLES2()); + Assert.assertFalse(ctx.isGLES1()); + } + + private static void validateGLProfileGLES3(final GLProfile glp) { + Assert.assertFalse(glp.isGL4bc()); + Assert.assertFalse(glp.isGL4()); + Assert.assertFalse(glp.isGL3bc()); + Assert.assertFalse(glp.isGL3()); + Assert.assertFalse(glp.isGL2()); + Assert.assertTrue(glp.isGLES3()); + Assert.assertTrue(glp.isGLES2()); + Assert.assertFalse(glp.isGLES1()); + Assert.assertFalse(glp.isGL2GL3()); + Assert.assertTrue(glp.isGL4ES3()); + Assert.assertTrue(glp.isGL3ES3()); + Assert.assertTrue(glp.isGL2ES2()); + Assert.assertFalse(glp.isGL2ES1()); + } + private static void validateGLES3(final GL gl) { + final GLContext ctx = gl.getContext(); + final boolean gles3CompatAvail = ctx.isGLES3Compatible(); + + Assert.assertFalse(gl.isGL4bc()); + Assert.assertFalse(gl.isGL4()); + Assert.assertFalse(gl.isGL3bc()); + Assert.assertFalse(gl.isGL3()); + Assert.assertFalse(gl.isGL2()); + Assert.assertFalse(gl.isGL2GL3()); + Assert.assertTrue(gl.isGL4ES3()); + Assert.assertTrue(gl.isGL3ES3()); + Assert.assertTrue(gl.isGL2ES2()); + Assert.assertFalse(gl.isGL2ES1()); + Assert.assertTrue(gl.isGLES3()); + Assert.assertTrue(gl.isGLES2()); + Assert.assertFalse(gl.isGLES1()); + + Assert.assertFalse(ctx.isGL4bc()); + Assert.assertFalse(ctx.isGL4()); + Assert.assertFalse(ctx.isGL3bc()); + Assert.assertFalse(ctx.isGL3()); + Assert.assertFalse(ctx.isGL2()); + Assert.assertFalse(ctx.isGL2GL3()); + Assert.assertTrue(ctx.isGL4ES3()); + Assert.assertTrue(ctx.isGL3ES3()); + Assert.assertTrue(gles3CompatAvail); + Assert.assertTrue(ctx.isGL2ES2()); + Assert.assertFalse(ctx.isGL2ES1()); + Assert.assertTrue(ctx.isGLES3()); + Assert.assertTrue(ctx.isGLES2()); + Assert.assertFalse(ctx.isGLES1()); + } + + private static void validateGLProfileGLES2(final GLProfile glp) { + Assert.assertFalse(glp.isGL4bc()); + Assert.assertFalse(glp.isGL4()); + Assert.assertFalse(glp.isGL3bc()); + Assert.assertFalse(glp.isGL3()); + Assert.assertFalse(glp.isGL2()); + Assert.assertFalse(glp.isGLES3()); + Assert.assertTrue(glp.isGLES2()); + Assert.assertFalse(glp.isGLES1()); + Assert.assertFalse(glp.isGL2GL3()); + Assert.assertFalse(glp.isGL4ES3()); + Assert.assertFalse(glp.isGL3ES3()); + Assert.assertTrue(glp.isGL2ES2()); + Assert.assertFalse(glp.isGL2ES1()); + } + private static void validateGLES2(final GL gl) { + final GLContext ctx = gl.getContext(); + final boolean gles3CompatAvail = ctx.isGLES3Compatible(); + + Assert.assertFalse(gl.isGL4bc()); + Assert.assertFalse(gl.isGL4()); + Assert.assertFalse(gl.isGL3bc()); + Assert.assertFalse(gl.isGL3()); + Assert.assertFalse(gl.isGL2()); + Assert.assertFalse(gl.isGL2GL3()); + Assert.assertFalse(gl.isGL4ES3()); + Assert.assertFalse(gl.isGL3ES3()); + Assert.assertTrue(gl.isGL2ES2()); + Assert.assertFalse(gl.isGL2ES1()); + Assert.assertFalse(gl.isGLES3()); + Assert.assertTrue(gl.isGLES2()); + Assert.assertFalse(gl.isGLES1()); + + Assert.assertFalse(ctx.isGL4bc()); + Assert.assertFalse(ctx.isGL4()); + Assert.assertFalse(ctx.isGL3bc()); + Assert.assertFalse(ctx.isGL3()); + Assert.assertFalse(ctx.isGL2()); + Assert.assertFalse(ctx.isGL2GL3()); + Assert.assertFalse(ctx.isGL4ES3()); + Assert.assertFalse(ctx.isGL3ES3()); + Assert.assertFalse(gles3CompatAvail); + Assert.assertTrue(ctx.isGL2ES2()); + Assert.assertFalse(ctx.isGL2ES1()); + Assert.assertFalse(ctx.isGLES3()); + Assert.assertTrue(ctx.isGLES2()); + Assert.assertFalse(ctx.isGLES1()); + } + + private static void validateGLProfileGLES1(final GLProfile glp) { + Assert.assertFalse(glp.isGL4bc()); + Assert.assertFalse(glp.isGL4()); + Assert.assertFalse(glp.isGL3bc()); + Assert.assertFalse(glp.isGL3()); + Assert.assertFalse(glp.isGL2()); + Assert.assertFalse(glp.isGLES3()); + Assert.assertFalse(glp.isGLES2()); + Assert.assertTrue(glp.isGLES1()); + Assert.assertFalse(glp.isGL2GL3()); + Assert.assertFalse(glp.isGL4ES3()); + Assert.assertFalse(glp.isGL3ES3()); + Assert.assertFalse(glp.isGL2ES2()); + Assert.assertTrue(glp.isGL2ES1()); + } + private static void validateGLES1(final GL gl) { + final GLContext ctx = gl.getContext(); + final boolean gles3CompatAvail = ctx.isGLES3Compatible(); + + Assert.assertFalse(gl.isGL4bc()); + Assert.assertFalse(gl.isGL4()); + Assert.assertFalse(gl.isGL3bc()); + Assert.assertFalse(gl.isGL3()); + Assert.assertFalse(gl.isGL2()); + Assert.assertFalse(gl.isGL2GL3()); + Assert.assertFalse(gl.isGL4ES3()); + Assert.assertFalse(gl.isGL3ES3()); + Assert.assertFalse(gl.isGL2ES2()); + Assert.assertTrue(gl.isGL2ES1()); + Assert.assertFalse(gl.isGLES3()); + Assert.assertFalse(gl.isGLES2()); + Assert.assertTrue(gl.isGLES1()); + + Assert.assertFalse(ctx.isGL4bc()); + Assert.assertFalse(ctx.isGL4()); + Assert.assertFalse(ctx.isGL3bc()); + Assert.assertFalse(ctx.isGL3()); + Assert.assertFalse(ctx.isGL2()); + Assert.assertFalse(ctx.isGL2GL3()); + Assert.assertFalse(ctx.isGL4ES3()); + Assert.assertFalse(ctx.isGL3ES3()); + Assert.assertFalse(gles3CompatAvail); + Assert.assertFalse(ctx.isGL2ES2()); + Assert.assertTrue(ctx.isGL2ES1()); + Assert.assertFalse(ctx.isGLES3()); + Assert.assertFalse(ctx.isGLES2()); + Assert.assertTrue(ctx.isGLES1()); + } + + private static void validateGLProfileGL2GL3(final GLProfile glp) { + if( glp.isGL4bc() ) { + validateGLProfileGL4bc(glp); + } else if(glp.isGL3bc()) { + validateGLProfileGL3bc(glp); + } else if(glp.isGL2()) { + validateGLProfileGL2(glp); + } else if(glp.isGL4()) { + validateGLProfileGL4(glp); + } else if(glp.isGL3()) { + validateGLProfileGL3(glp); + } else { + throw new GLException("GL2GL3 is neither GL4bc, GL3bc, GL2, GL4 nor GL3"); + } + } + private static void validateGL2GL3(final GL gl) { + if( gl.isGL4bc() ) { + validateGL4bc(gl); + } else if(gl.isGL3bc()) { + validateGL3bc(gl); + } else if(gl.isGL2()) { + validateGL2(gl); + } else if(gl.isGL4()) { + validateGL4(gl); + } else if(gl.isGL3()) { + validateGL3(gl); + } else { + throw new GLException("GL2GL3 is neither GL4bc, GL3bc, GL2, GL4 nor GL3"); + } + } + + private static void validateGLProfileGL4ES3(final GLProfile glp) { + if( glp.isGL4bc() ) { + validateGLProfileGL4bc(glp); + } else if( glp.isGL4() ) { + validateGLProfileGL4(glp); + } else if( glp.isGLES3() ) { + validateGLProfileGLES3(glp); + } else { + throw new GLException("GL4ES3 is neither GL4bc, GL4 nor GLES3"); + } + } + private static void validateGL4ES3(final GL gl) { + if( gl.isGL4bc() ) { + validateGL4bc(gl); + } else if( gl.isGL4() ) { + validateGL4(gl); + } else if( gl.isGLES3() ) { + validateGLES3(gl); + } else { + throw new GLException("GL4ES3 is neither GL4bc, GL4 nor GLES3"); + } + } + + private static void validateGLProfileGL2ES2(final GLProfile glp) { + if( glp.isGL4bc() ) { + validateGLProfileGL4bc(glp); + } else if(glp.isGL3bc()) { + validateGLProfileGL3bc(glp); + } else if(glp.isGL2()) { + validateGLProfileGL2(glp); + } else if(glp.isGL4()) { + validateGLProfileGL4(glp); + } else if(glp.isGL3()) { + validateGLProfileGL3(glp); + } else if(glp.isGLES3()) { + validateGLProfileGLES3(glp); + } else if(glp.isGLES2()) { + validateGLProfileGLES2(glp); + } else { + throw new GLException("GL2ES2 is neither GL4bc, GL3bc, GL2, GL4, GL3, GLES3 nor GLES2"); + } + } + private static void validateGL2ES2(final GL gl) { + if( gl.isGL4bc() ) { + validateGL4bc(gl); + } else if(gl.isGL3bc()) { + validateGL3bc(gl); + } else if(gl.isGL2()) { + validateGL2(gl); + } else if(gl.isGL4()) { + validateGL4(gl); + } else if(gl.isGL3()) { + validateGL3(gl); + } else if(gl.isGLES3()) { + validateGLES3(gl); + } else if(gl.isGLES2()) { + validateGLES2(gl); + } else { + throw new GLException("GL2ES2 is neither GL4bc, GL3bc, GL2, GL4, GL3, GLES3 nor GLES2"); + } + } + + private static void validateGLProfileGL2ES1(final GLProfile glp) { + if( glp.isGL4bc() ) { + validateGLProfileGL4bc(glp); + } else if(glp.isGL3bc()) { + validateGLProfileGL3bc(glp); + } else if(glp.isGL2()) { + validateGLProfileGL2(glp); + } else if(glp.isGLES1()) { + validateGLProfileGLES1(glp); + } else { + throw new GLException("GL2ES1 is neither GL4bc, GL3bc, GL2 nor GLES1"); + } + } + private static void validateGL2ES1(final GL gl) { + if( gl.isGL4bc() ) { + validateGL4bc(gl); + } else if(gl.isGL3bc()) { + validateGL3bc(gl); + } else if(gl.isGL2()) { + validateGL2(gl); + } else if(gl.isGLES1()) { + validateGLES1(gl); + } else { + throw new GLException("GL2ES1 is neither GL4bc, GL3bc, GL2 nor GLES1"); + } + } + + private static void validateOffline(final String requestedProfile, final GLProfile glp) { + System.err.println("GLProfile Mapping "+requestedProfile+" -> "+glp); + final boolean gles3CompatAvail = GLContext.isGLES3CompatibleAvailable(GLProfile.getDefaultDevice()); if( glp.getImplName().equals(GLProfile.GL4bc) ) { Assert.assertTrue(GLProfile.isAvailable(GLProfile.GL4bc)); Assert.assertTrue(GLProfile.isAvailable(GLProfile.GL4)); Assert.assertTrue(GLProfile.isAvailable(GLProfile.GL3bc)); + Assert.assertTrue(GLProfile.isAvailable(GLProfile.GL3)); Assert.assertTrue(GLProfile.isAvailable(GLProfile.GL2)); Assert.assertTrue(GLProfile.isAvailable(GLProfile.GL2GL3)); if( gles3CompatAvail ) { @@ -129,163 +680,233 @@ public class TestGLProfile01NEWT extends UITestCase { Assert.assertTrue(GLProfile.isAvailable(GLProfile.GL2ES1)); } if( glp.isGL4bc() ) { - Assert.assertTrue(glp.isGL4()); - Assert.assertTrue(glp.isGL3bc()); - Assert.assertTrue(glp.isGL3()); - Assert.assertTrue(glp.isGL2()); - Assert.assertTrue(glp.isGL2GL3()); - Assert.assertTrue(glp.isGL4ES3()); - Assert.assertTrue(glp.isGL3ES3()); - Assert.assertTrue(glp.isGL2ES1()); - Assert.assertTrue(glp.isGL2ES2()); + validateGLProfileGL4bc(glp); } else if(glp.isGL3bc()) { - Assert.assertTrue(glp.isGL3()); - Assert.assertTrue(glp.isGL2()); - Assert.assertTrue(glp.isGL2GL3()); - Assert.assertTrue(glp.isGL2ES1()); - Assert.assertTrue(glp.isGL2ES2()); + validateGLProfileGL3bc(glp); } else if(glp.isGL2()) { - Assert.assertTrue(glp.isGL2GL3()); - Assert.assertTrue(glp.isGL2ES1()); - Assert.assertTrue(glp.isGL2ES2()); + validateGLProfileGL2(glp); } else if(glp.isGL4()) { - Assert.assertTrue(glp.isGL3()); - Assert.assertTrue(glp.isGL2GL3()); - Assert.assertTrue(glp.isGL4ES3()); - Assert.assertTrue(glp.isGL3ES3()); - Assert.assertTrue(glp.isGL2ES2()); + validateGLProfileGL4(glp); } else if(glp.isGL3()) { - Assert.assertTrue(glp.isGL2GL3()); - Assert.assertTrue(glp.isGL3ES3()); - Assert.assertTrue(glp.isGL2ES2()); + validateGLProfileGL3(glp); } else if(glp.isGLES3()) { - Assert.assertTrue(glp.isGL4ES3()); - Assert.assertTrue(glp.isGL3ES3()); - Assert.assertTrue(glp.isGL2ES2()); + validateGLProfileGLES3(glp); } else if(glp.isGLES2()) { - Assert.assertTrue(glp.isGL2ES2()); + validateGLProfileGLES2(glp); } else if(glp.isGLES1()) { - Assert.assertTrue(glp.isGL2ES1()); + validateGLProfileGLES1(glp); } + + if( requestedProfile == GLProfile.GL4bc ) { + validateGLProfileGL4bc(glp); + } else if( requestedProfile == GLProfile.GL3bc ) { + validateGLProfileGL3bc(glp); + } else if( requestedProfile == GLProfile.GL2 ) { + validateGLProfileGL2(glp); + } else if( requestedProfile == GLProfile.GL4 ) { + validateGLProfileGL4(glp); + } else if( requestedProfile == GLProfile.GL3 ) { + validateGLProfileGL3(glp); + } else if( requestedProfile == GLProfile.GLES3 ) { + validateGLProfileGLES3(glp); + } else if( requestedProfile == GLProfile.GLES2 ) { + validateGLProfileGLES2(glp); + } else if( requestedProfile == GLProfile.GLES1 ) { + validateGLProfileGLES1(glp); + } else if( requestedProfile == GLProfile.GL2GL3 ) { + validateGLProfileGL2GL3(glp); + } else if( requestedProfile == GLProfile.GL4ES3 ) { + validateGLProfileGL4ES3(glp); + } else if( requestedProfile == GLProfile.GL2ES2 ) { + validateGLProfileGL2ES2(glp); + } else if( requestedProfile == GLProfile.GL2ES1 ) { + validateGLProfileGL2ES1(glp); + } + } - static void validate(final GL gl) { + static void validateOnline(final String requestedProfile, final GLProfile glpReq, final GL gl) { final GLContext ctx = gl.getContext(); - final boolean gles3CompatAvail = ctx.isGLES3Compatible(); + final GLProfile glp = gl.getGLProfile(); + System.err.println("GLContext Mapping "+requestedProfile+" -> "+glpReq+" -> "+glp+" -> "+ctx.getGLVersion()); + + System.err.println("GL impl. class "+gl.getClass().getName()); + if( gl.isGL4() ) { + Assert.assertNotNull( gl.getGL4() ); + System.err.println("GL Mapping "+glp+" -> GL4"); + } if( gl.isGL4bc() ) { - Assert.assertTrue(gl.isGL4()); - Assert.assertTrue(gl.isGL3bc()); - Assert.assertTrue(gl.isGL3()); - Assert.assertTrue(gl.isGL2()); - Assert.assertTrue(gl.isGL2GL3()); - if( gles3CompatAvail ) { - Assert.assertTrue(gl.isGL4ES3()); - } else { - Assert.assertFalse(gl.isGL4ES3()); - } - Assert.assertTrue(gl.isGL3ES3()); - Assert.assertTrue(gl.isGL2ES1()); - Assert.assertTrue(gl.isGL2ES2()); + Assert.assertNotNull( gl.getGL4bc() ); + System.err.println("GL Mapping "+glp+" -> GL4bc"); + } + if( gl.isGL3() ) { + Assert.assertNotNull( gl.getGL3() ); + System.err.println("GL Mapping "+glp+" -> GL3"); + } + if( gl.isGL3bc() ) { + Assert.assertNotNull( gl.getGL3bc() ); + System.err.println("GL Mapping "+glp+" -> GL3bc"); + } + if( gl.isGL2() ) { + Assert.assertNotNull( gl.getGL2() ); + System.err.println("GL Mapping "+glp+" -> GL2"); + } + if( gl.isGLES3() ) { + Assert.assertNotNull( gl.getGLES3() ); + System.err.println("GL Mapping "+glp+" -> GLES3"); + } + if( gl.isGLES2() ) { + Assert.assertNotNull( gl.getGLES2() ); + System.err.println("GL Mapping "+glp+" -> GLES2"); + } + if( gl.isGLES1() ) { + Assert.assertNotNull( gl.getGLES1() ); + System.err.println("GL Mapping "+glp+" -> GLES1"); + } + if( gl.isGL4ES3() ) { + Assert.assertNotNull( gl.getGL4ES3() ); + System.err.println("GL Mapping "+glp+" -> GL4ES3"); + } + if( gl.isGL3ES3() ) { + Assert.assertNotNull( gl.getGL3ES3() ); + System.err.println("GL Mapping "+glp+" -> GL3ES3"); + } + if( gl.isGL2GL3() ) { + Assert.assertNotNull( gl.getGL2GL3() ); + System.err.println("GL Mapping "+glp+" -> GL2GL3"); + } + if( gl.isGL2ES2() ) { + Assert.assertNotNull( gl.getGL2ES2() ); + System.err.println("GL Mapping "+glp+" -> GL2ES2"); + } + if( gl.isGL2ES1() ) { + Assert.assertNotNull( gl.getGL2ES1() ); + System.err.println("GL Mapping "+glp+" -> GL2ES1"); + } + + if( gl.isGL4bc() ) { + validateGL4bc(gl); } else if(gl.isGL3bc()) { - Assert.assertTrue(gl.isGL3()); - Assert.assertTrue(gl.isGL2()); - Assert.assertTrue(gl.isGL2GL3()); - Assert.assertTrue(gl.isGL2ES1()); - Assert.assertTrue(gl.isGL2ES2()); + validateGL3bc(gl); } else if(gl.isGL2()) { - Assert.assertTrue(gl.isGL2GL3()); - Assert.assertTrue(gl.isGL2ES1()); - Assert.assertTrue(gl.isGL2ES2()); + validateGL2(gl); } else if(gl.isGL4()) { - Assert.assertTrue(gl.isGL3()); - Assert.assertTrue(gl.isGL2GL3()); - if( gles3CompatAvail ) { - Assert.assertTrue(gl.isGL4ES3()); - } else { - Assert.assertFalse(gl.isGL4ES3()); - } - Assert.assertTrue(gl.isGL3ES3()); - Assert.assertTrue(gl.isGL2ES2()); + validateGL4(gl); } else if(gl.isGL3()) { - Assert.assertTrue(gl.isGL2GL3()); - Assert.assertTrue(gl.isGL3ES3()); - Assert.assertTrue(gl.isGL2ES2()); + validateGL3(gl); } else if(gl.isGLES3()) { - if( gles3CompatAvail ) { - Assert.assertTrue(gl.isGL4ES3()); - } else { - Assert.assertFalse(gl.isGL4ES3()); - } - Assert.assertTrue(gl.isGL3ES3()); - Assert.assertTrue(gl.isGL2ES2()); + validateGLES3(gl); } else if(gl.isGLES2()) { - Assert.assertTrue(gl.isGL2ES2()); + validateGLES2(gl); } else if(gl.isGLES1()) { - Assert.assertTrue(gl.isGL2ES1()); + validateGLES1(gl); } - if( ctx.isGL4bc() ) { - Assert.assertTrue(ctx.isGL4()); - Assert.assertTrue(ctx.isGL3bc()); - Assert.assertTrue(ctx.isGL3()); - Assert.assertTrue(ctx.isGL2()); - Assert.assertTrue(ctx.isGL2GL3()); - if( gles3CompatAvail ) { - Assert.assertTrue(ctx.isGL4ES3()); + if( requestedProfile == GLProfile.GL4bc ) { + validateGL4bc(gl); + } else if( requestedProfile == GLProfile.GL3bc ) { + if( gl.isGL4bc() ) { + validateGL4bc(gl); + } else if( gl.isGL3bc() ) { + validateGL3bc(gl); } else { - Assert.assertFalse(ctx.isGL4ES3()); + throw new GLException("GL3bc is neither GL4bc nor GL3bc"); } - Assert.assertTrue(ctx.isGL3ES3()); - Assert.assertTrue(ctx.isGL2ES1()); - Assert.assertTrue(ctx.isGL2ES2()); - } else if(ctx.isGL3bc()) { - Assert.assertTrue(ctx.isGL3()); - Assert.assertTrue(ctx.isGL2()); - Assert.assertTrue(ctx.isGL2GL3()); - Assert.assertTrue(ctx.isGL2ES1()); - Assert.assertTrue(ctx.isGL2ES2()); - } else if(ctx.isGL2()) { - Assert.assertTrue(ctx.isGL2GL3()); - Assert.assertTrue(ctx.isGL2ES1()); - Assert.assertTrue(ctx.isGL2ES2()); - } else if(ctx.isGL4()) { - Assert.assertTrue(ctx.isGL3()); - Assert.assertTrue(ctx.isGL2GL3()); - if( gles3CompatAvail ) { - Assert.assertTrue(ctx.isGL4ES3()); + } else if( requestedProfile == GLProfile.GL2 ) { + if( gl.isGL4bc() ) { + validateGL4bc(gl); + } else if( gl.isGL3bc() ) { + validateGL3bc(gl); + } else if( gl.isGL2() ) { + validateGL2(gl); } else { - Assert.assertFalse(ctx.isGL4ES3()); + throw new GLException("GL2 is neither GL4bc, GL3bc, GL2"); } - Assert.assertTrue(ctx.isGL3ES3()); - Assert.assertTrue(ctx.isGL2ES2()); - } else if(ctx.isGL3()) { - Assert.assertTrue(ctx.isGL2GL3()); - Assert.assertTrue(ctx.isGL3ES3()); - Assert.assertTrue(ctx.isGL2ES2()); - } else if(ctx.isGLES3()) { - if( gles3CompatAvail ) { - Assert.assertTrue(ctx.isGL4ES3()); + } else if( requestedProfile == GLProfile.GL4 ) { + if( gl.isGL4bc() ) { + validateGL4bc(gl); + } else if( gl.isGL4() ) { + validateGL4(gl); + } else { + throw new GLException("GL4 is neither GL4bc, nor GL4"); + } + } else if( requestedProfile == GLProfile.GL3 ) { + if( gl.isGL4bc() ) { + validateGL4bc(gl); + } else if( gl.isGL3bc() ) { + validateGL3bc(gl); + } else if( gl.isGL4() ) { + validateGL4(gl); + } else if( gl.isGL3() ) { + validateGL3(gl); + } else { + throw new GLException("GL3 is neither GL4bc, GL3bc, GL4 nor GL3"); + } + } else if( requestedProfile == GLProfile.GLES3 ) { + validateGLES3(gl); + } else if( requestedProfile == GLProfile.GLES2 ) { + if( gl.isGLES3() ) { + validateGLES3(gl); + } else if( gl.isGLES2() ) { + validateGLES2(gl); } else { - Assert.assertFalse(ctx.isGL4ES3()); + throw new GLException("GLES2 is neither GLES3 nor GLES2"); } - Assert.assertTrue(ctx.isGL3ES3()); - Assert.assertTrue(ctx.isGL2ES2()); - } else if(ctx.isGLES2()) { - Assert.assertTrue(ctx.isGL2ES2()); - } else if(ctx.isGLES1()) { - Assert.assertTrue(ctx.isGL2ES1()); + } else if( requestedProfile == GLProfile.GLES1 ) { + validateGLES1(gl); + } else if( requestedProfile == GLProfile.GL2GL3 ) { + validateGL2GL3(gl); + } else if( requestedProfile == GLProfile.GL4ES3 ) { + validateGL4ES3(gl); + } else if( requestedProfile == GLProfile.GL2ES2 ) { + validateGL2ES2(gl); + } else if( requestedProfile == GLProfile.GL2ES1 ) { + validateGL2ES1(gl); } } + void validateOnline(final String requestedProfile, final GLProfile glp) throws InterruptedException { + final GLCapabilities caps = new GLCapabilities(glp); + final GLWindow glWindow = GLWindow.create(caps); + Assert.assertNotNull(glWindow); + glWindow.setTitle(getSimpleTestName(".")); + + glWindow.addGLEventListener(new GLEventListener() { + + public void init(final GLAutoDrawable drawable) { + final GL gl = drawable.getGL(); + System.err.println(JoglVersion.getGLStrings(gl, null, false)); + + validateOnline(requestedProfile, glp, gl); + } + + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { + } + + public void display(final GLAutoDrawable drawable) { + } + + public void dispose(final GLAutoDrawable drawable) { + } + }); + + glWindow.setSize(128, 128); + glWindow.setVisible(true); + + glWindow.display(); + Thread.sleep(100); + glWindow.destroy(); + } + @Test public void test01GLProfileDefault() throws InterruptedException { System.out.println("GLProfile "+GLProfile.glAvailabilityToString()); System.out.println("GLProfile.getDefaultDevice(): "+GLProfile.getDefaultDevice()); final GLProfile glp = GLProfile.getDefault(); System.out.println("GLProfile.getDefault(): "+glp); - validate(glp); - dumpVersion(glp); + validateOffline("default", glp); + validateOnline("default", glp); } @Test @@ -293,8 +914,8 @@ public class TestGLProfile01NEWT extends UITestCase { // Assuming at least one programmable profile is available final GLProfile glp = GLProfile.getMaxProgrammable(true); System.out.println("GLProfile.getMaxProgrammable(): "+glp); - validate(glp); - dumpVersion(glp); + validateOffline("maxProgrammable", glp); + validateOnline("maxProgrammable", glp); } @Test @@ -302,8 +923,8 @@ public class TestGLProfile01NEWT extends UITestCase { // Assuming at least one fixed function profile is available final GLProfile glp = GLProfile.getMaxFixedFunc(true); System.out.println("GLProfile.getMaxFixedFunc(): "+glp); - validate(glp); - dumpVersion(glp); + validateOffline("maxFixedFunc", glp); + validateOnline("maxFixedFunc", glp); } @Test @@ -313,9 +934,8 @@ public class TestGLProfile01NEWT extends UITestCase { return; } final GLProfile glp = GLProfile.getGL2ES1(); - System.out.println("GLProfile GL2ES1: "+glp); - validate(glp); - dumpVersion(glp); + validateOffline(GLProfile.GL2ES1, glp); + validateOnline(GLProfile.GL2ES1, glp); } @Test @@ -325,9 +945,8 @@ public class TestGLProfile01NEWT extends UITestCase { return; } final GLProfile glp = GLProfile.getGL2ES2(); - System.out.println("GLProfile GL2ES2: "+glp); - validate(glp); - dumpVersion(glp); + validateOffline(GLProfile.GL2ES2, glp); + validateOnline(GLProfile.GL2ES2, glp); } @Test @@ -337,16 +956,26 @@ public class TestGLProfile01NEWT extends UITestCase { return; } final GLProfile glp = GLProfile.getGL4ES3(); - System.out.println("GLProfile GL4ES3: "+glp); - validate(glp); - dumpVersion(glp); + validateOffline(GLProfile.GL4ES3, glp); + validateOnline(GLProfile.GL4ES3, glp); + } + + @Test + public void test07GLProfileGL2GL3() throws InterruptedException { + if(!GLProfile.isAvailable(GLProfile.GL2GL3)) { + System.out.println("GLProfile GL2GL3 n/a"); + return; + } + final GLProfile glp = GLProfile.getGL2GL3(); + validateOffline(GLProfile.GL2GL3, glp); + validateOnline(GLProfile.GL2GL3, glp); } void testSpecificProfile(final String glps) throws InterruptedException { if(GLProfile.isAvailable(glps)) { final GLProfile glp = GLProfile.get(glps); - validate(glp); - dumpVersion(glp); + validateOffline(glps, glp); + validateOnline(glps, glp); } else { System.err.println("Profile "+glps+" n/a"); } @@ -392,78 +1021,6 @@ public class TestGLProfile01NEWT extends UITestCase { testSpecificProfile(GLProfile.GLES3); } - protected void dumpVersion(final GLProfile glp) throws InterruptedException { - final GLCapabilities caps = new GLCapabilities(glp); - final GLWindow glWindow = GLWindow.create(caps); - Assert.assertNotNull(glWindow); - glWindow.setTitle("TestGLProfile01NEWT"); - - glWindow.addGLEventListener(new GLEventListener() { - - public void init(final GLAutoDrawable drawable) { - final GL gl = drawable.getGL(); - System.err.println(JoglVersion.getGLStrings(gl, null, true)); - - validate(gl); - - final GLProfile glp = gl.getGLProfile(); - System.err.println("GL impl. class "+gl.getClass().getName()); - if( gl.isGL4() ) { - Assert.assertNotNull( gl.getGL4() ); - System.err.println("GL Mapping "+glp+" -> GL4"); - } - if( gl.isGL4bc() ) { - Assert.assertNotNull( gl.getGL4bc() ); - System.err.println("GL Mapping "+glp+" -> GL4bc"); - } - if( gl.isGL3() ) { - Assert.assertNotNull( gl.getGL3() ); - System.err.println("GL Mapping "+glp+" -> GL3"); - } - if( gl.isGL3bc() ) { - Assert.assertNotNull( gl.getGL3bc() ); - System.err.println("GL Mapping "+glp+" -> GL3bc"); - } - if( gl.isGLES3() ) { - Assert.assertNotNull( gl.getGLES3() ); - System.err.println("GL Mapping "+glp+" -> GLES3"); - } - if( gl.isGLES2() ) { - Assert.assertNotNull( gl.getGLES2() ); - System.err.println("GL Mapping "+glp+" -> GLES2"); - } - if( gl.isGL4ES3() ) { - Assert.assertNotNull( gl.getGL4ES3() ); - System.err.println("GL Mapping "+glp+" -> GL4ES3"); - } - if( gl.isGL2ES2() ) { - Assert.assertNotNull( gl.getGL2ES2() ); - System.err.println("GL Mapping "+glp+" -> GL2ES2"); - } - if( gl.isGL2ES1() ) { - Assert.assertNotNull( gl.getGL2ES1() ); - System.err.println("GL Mapping "+glp+" -> GL2ES1"); - } - } - - public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { - } - - public void display(final GLAutoDrawable drawable) { - } - - public void dispose(final GLAutoDrawable drawable) { - } - }); - - glWindow.setSize(128, 128); - glWindow.setVisible(true); - - glWindow.display(); - Thread.sleep(100); - glWindow.destroy(); - } - public static void main(final String args[]) throws IOException { final String tstname = TestGLProfile01NEWT.class.getName(); org.junit.runner.JUnitCore.main(tstname); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLCanvasAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLCanvasAWT.java index 2b6c84bf3..37f1c7555 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLCanvasAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLCanvasAWT.java @@ -30,15 +30,10 @@ package com.jogamp.opengl.test.junit.jogl.acore; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Frame; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import javax.imageio.ImageIO; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilitiesImmutable; -import javax.media.opengl.GLDrawable; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLProfile; import javax.media.opengl.awt.GLCanvas; @@ -49,18 +44,23 @@ import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; +import com.jogamp.common.util.VersionUtil; +import com.jogamp.opengl.JoglVersion; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.jogl.demos.es2.MultisampleDemoES2; import com.jogamp.opengl.test.junit.util.MiscUtils; import com.jogamp.opengl.util.Animator; -import com.jogamp.opengl.util.GLDrawableUtil; import com.jogamp.opengl.util.awt.AWTGLReadBufferUtil; -import com.jogamp.opengl.util.texture.TextureIO; /** - * Multiple GLJPanels in a JFrame + * Test synchronous GLAutoDrawable display, swap-buffer and read-pixels with AWT GLCanvas + * including non-MSAA and MSAA framebuffer. + * <p> + * See {@link GLReadBuffer00Base} for related bugs and further details. + * </p> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestGLReadBuffer01GLCanvasAWT extends GLReadBuffer00Base { +public class TestGLReadBuffer01GLCanvasAWT extends GLReadBuffer00BaseAWT { @BeforeClass public static void initClass() { @@ -77,7 +77,7 @@ public class TestGLReadBuffer01GLCanvasAWT extends GLReadBuffer00Base { final Dimension d = new Dimension(320, 240); final GLCanvas glad = createGLCanvas(caps, d); final TextRendererGLEL textRendererGLEL = new TextRendererGLEL(); - final SnapshotGLELAWT snapshotGLEL = doSnapshot ? new SnapshotGLELAWT(textRendererGLEL, awtGLReadBufferUtil) : null; + final SnapshotGLELAWT snapshotGLEL = doSnapshot ? new SnapshotGLELAWT(textRendererGLEL, awtGLReadBufferUtil, false) : null; try { javax.swing.SwingUtilities.invokeAndWait(new Runnable() { public void run() { @@ -88,9 +88,34 @@ public class TestGLReadBuffer01GLCanvasAWT extends GLReadBuffer00Base { // panel.setDoubleBuffered(useSwingDoubleBuffer); // frame.getContentPane().add(panel); - final GearsES2 gears = new GearsES2(1); - gears.setVerbose(false); - glad.addGLEventListener(gears); + glad.addGLEventListener(new GLEventListener() { + @Override + public void init(final GLAutoDrawable drawable) { + final GL gl = drawable.getGL(); + System.err.println(VersionUtil.getPlatformInfo()); + System.err.println("GLEventListener init on "+Thread.currentThread()); + System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities()); + System.err.println("INIT GL IS: " + gl.getClass().getName()); + System.err.println(JoglVersion.getGLStrings(gl, null, false).toString()); + } + @Override + public void dispose(final GLAutoDrawable drawable) {} + @Override + public void display(final GLAutoDrawable drawable) {} + @Override + public void reshape(final GLAutoDrawable drawable, final int x,final int y, final int width, final int height) {} + }); + { + final GearsES2 gears = new GearsES2(1); + gears.setVerbose(false); + glad.addGLEventListener(gears); + } + { + final MultisampleDemoES2 demo = new MultisampleDemoES2(caps.getSampleBuffers()); + demo.setClearBuffers(false);; + glad.addGLEventListener(demo); + } + glad.addGLEventListener(textRendererGLEL); if( doSnapshot ) { glad.addGLEventListener(snapshotGLEL); @@ -166,71 +191,6 @@ public class TestGLReadBuffer01GLCanvasAWT extends GLReadBuffer00Base { return canvas; } - private class SnapshotGLELAWT implements GLEventListener { - final TextRendererGLEL textRendererGLEL; - final AWTGLReadBufferUtil glReadBufferUtil; - boolean defAutoSwapMode; - boolean swapBuffersBeforeRead; - int i; - - SnapshotGLELAWT(final TextRendererGLEL textRendererGLEL, final AWTGLReadBufferUtil glReadBufferUtil) { - this.textRendererGLEL = textRendererGLEL; - this.glReadBufferUtil = glReadBufferUtil; - this.defAutoSwapMode = true; - this.swapBuffersBeforeRead = false; - i = 0; - } - - @Override - public void init(final GLAutoDrawable drawable) { - defAutoSwapMode = drawable.getAutoSwapBufferMode(); - swapBuffersBeforeRead = GLDrawableUtil.swapBuffersBeforeRead(drawable.getChosenGLCapabilities()); - drawable.setAutoSwapBufferMode( !swapBuffersBeforeRead ); - } - @Override - public void dispose(final GLAutoDrawable drawable) { - drawable.setAutoSwapBufferMode( defAutoSwapMode ); - } - @Override - public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { } - @Override - public void display(final GLAutoDrawable drawable) { - snapshot(i++, drawable.getGL(), TextureIO.PNG, null); - } - public void snapshot(final int sn, final GL gl, final String fileSuffix, final String destPath) { - final GLDrawable drawable = gl.getContext().getGLReadDrawable(); - final String postSNDetail = String.format("awt-usr%03d", textRendererGLEL.userCounter); - final String filenameAWT = getSnapshotFilename(sn, postSNDetail, - drawable.getChosenGLCapabilities(), drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), - glReadBufferUtil.hasAlpha(), fileSuffix, destPath); - if( swapBuffersBeforeRead ) { - drawable.swapBuffers(); - // Just to test whether we use the right buffer, - // i.e. back-buffer shall no more be required .. - gl.glClear(GL.GL_COLOR_BUFFER_BIT); - } else { - gl.glFinish(); // just make sure rendering finished .. - } - - final boolean awtOrientation = drawable.isGLOriented(); - System.err.println(Thread.currentThread().getName()+": ** screenshot: awtOrient/v-flip "+awtOrientation+", swapBuffersBeforeRead "+swapBuffersBeforeRead+", "+filenameAWT); - - final BufferedImage image = glReadBufferUtil.readPixelsToBufferedImage(gl, awtOrientation); - final File fout = new File(filenameAWT); - try { - ImageIO.write(image, "png", fout); - } catch (final IOException e) { - e.printStackTrace(); - } - /** - final String filenameJGL = getSnapshotFilename(sn, "jgl", - drawable.getChosenGLCapabilities(), drawable.getWidth(), drawable.getHeight(), - glReadBufferUtil.hasAlpha(), fileSuffix, destPath); - glReadBufferUtil.write(new File(filenameJGL)); - */ - } - }; - static GLCapabilitiesImmutable caps = null; static boolean doSnapshot = true; static boolean keyFrame = false; diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLJPanelAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLJPanelAWT.java index 7f5755fc7..599392d0c 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLJPanelAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLJPanelAWT.java @@ -29,15 +29,10 @@ package com.jogamp.opengl.test.junit.jogl.acore; import java.awt.BorderLayout; import java.awt.Dimension; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import javax.imageio.ImageIO; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilitiesImmutable; -import javax.media.opengl.GLDrawable; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLProfile; import javax.media.opengl.awt.GLJPanel; @@ -50,18 +45,24 @@ import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; +import com.jogamp.common.GlueGenVersion; +import com.jogamp.common.util.VersionUtil; +import com.jogamp.opengl.JoglVersion; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.jogl.demos.es2.MultisampleDemoES2; import com.jogamp.opengl.test.junit.util.MiscUtils; import com.jogamp.opengl.util.Animator; -import com.jogamp.opengl.util.GLDrawableUtil; import com.jogamp.opengl.util.awt.AWTGLReadBufferUtil; -import com.jogamp.opengl.util.texture.TextureIO; /** - * Multiple GLJPanels in a JFrame + * Test synchronous GLAutoDrawable display, swap-buffer and read-pixels with AWT GLJPanel + * including non-MSAA and MSAA framebuffer. + * <p> + * See {@link GLReadBuffer00Base} for related bugs and further details. + * </p> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestGLReadBuffer01GLJPanelAWT extends GLReadBuffer00Base { +public class TestGLReadBuffer01GLJPanelAWT extends GLReadBuffer00BaseAWT { @BeforeClass public static void initClass() { @@ -85,10 +86,34 @@ public class TestGLReadBuffer01GLJPanelAWT extends GLReadBuffer00Base { panel.setDoubleBuffered(useSwingDoubleBuffer); frame.getContentPane().add(panel); - final GearsES2 gears = new GearsES2(1); - gears.setFlipVerticalInGLOrientation(skipGLOrientationVerticalFlip); - gears.setVerbose(false); - glad.addGLEventListener(gears); + glad.addGLEventListener(new GLEventListener() { + @Override + public void init(final GLAutoDrawable drawable) { + final GL gl = drawable.getGL(); + System.err.println(VersionUtil.getPlatformInfo()); + System.err.println("GLEventListener init on "+Thread.currentThread()); + System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities()); + System.err.println("INIT GL IS: " + gl.getClass().getName()); + System.err.println(JoglVersion.getGLStrings(gl, null, false).toString()); + } + @Override + public void dispose(final GLAutoDrawable drawable) {} + @Override + public void display(final GLAutoDrawable drawable) {} + @Override + public void reshape(final GLAutoDrawable drawable, final int x,final int y, final int width, final int height) {} + }); + { + final GearsES2 gears = new GearsES2(1); + gears.setFlipVerticalInGLOrientation(skipGLOrientationVerticalFlip); + gears.setVerbose(false); + glad.addGLEventListener(gears); + } + { + final MultisampleDemoES2 demo = new MultisampleDemoES2(caps.getSampleBuffers()); + demo.setClearBuffers(false);; + glad.addGLEventListener(demo); + } textRendererGLEL.setFlipVerticalInGLOrientation(skipGLOrientationVerticalFlip); glad.addGLEventListener(textRendererGLEL); if( doSnapshot ) { @@ -102,6 +127,9 @@ public class TestGLReadBuffer01GLJPanelAWT extends GLReadBuffer00Base { throwable.printStackTrace(); Assume.assumeNoException( throwable ); } + if( keyFrame ) { + waitForKey("Post init: Frame# "+textRendererGLEL.frameNo); + } glad.display(); // trigger initialization to get chosen-caps! final Dimension size0 = frame.getSize(); final Dimension size1 = new Dimension(size0.width+100, size0.height+100); @@ -166,73 +194,6 @@ public class TestGLReadBuffer01GLJPanelAWT extends GLReadBuffer00Base { return canvas; } - private class SnapshotGLELAWT implements GLEventListener { - final TextRendererGLEL textRendererGLEL; - final AWTGLReadBufferUtil glReadBufferUtil; - final boolean skipGLOrientationVerticalFlip; - boolean defAutoSwapMode; - boolean swapBuffersBeforeRead; - int i; - - SnapshotGLELAWT(final TextRendererGLEL textRendererGLEL, final AWTGLReadBufferUtil glReadBufferUtil, final boolean skipGLOrientationVerticalFlip) { - this.textRendererGLEL = textRendererGLEL; - this.glReadBufferUtil = glReadBufferUtil; - this.skipGLOrientationVerticalFlip = skipGLOrientationVerticalFlip; - this.defAutoSwapMode = true; - this.swapBuffersBeforeRead = false; - i = 0; - } - - @Override - public void init(final GLAutoDrawable drawable) { - defAutoSwapMode = drawable.getAutoSwapBufferMode(); - swapBuffersBeforeRead = GLDrawableUtil.swapBuffersBeforeRead(drawable.getChosenGLCapabilities()); - drawable.setAutoSwapBufferMode( !swapBuffersBeforeRead ); - } - @Override - public void dispose(final GLAutoDrawable drawable) { - drawable.setAutoSwapBufferMode( defAutoSwapMode ); - } - @Override - public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { } - @Override - public void display(final GLAutoDrawable drawable) { - snapshot(i++, drawable.getGL(), TextureIO.PNG, null); - } - public void snapshot(final int sn, final GL gl, final String fileSuffix, final String destPath) { - final GLDrawable drawable = gl.getContext().getGLReadDrawable(); - final String postSNDetail = String.format("awt-usr%03d", textRendererGLEL.userCounter); - final String filenameAWT = getSnapshotFilename(sn, postSNDetail, - drawable.getChosenGLCapabilities(), drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), - glReadBufferUtil.hasAlpha(), fileSuffix, destPath); - if( swapBuffersBeforeRead ) { - drawable.swapBuffers(); - // Just to test whether we use the right buffer, - // i.e. back-buffer shall no more be required .. - gl.glClear(GL.GL_COLOR_BUFFER_BIT); - } else { - gl.glFinish(); // just make sure rendering finished .. - } - - final boolean awtOrientation = !( drawable.isGLOriented() && skipGLOrientationVerticalFlip ); - System.err.println(Thread.currentThread().getName()+": ** screenshot: awtOrient/v-flip "+awtOrientation+", swapBuffersBeforeRead "+swapBuffersBeforeRead+", "+filenameAWT); - - final BufferedImage image = glReadBufferUtil.readPixelsToBufferedImage(gl, awtOrientation); - final File fout = new File(filenameAWT); - try { - ImageIO.write(image, "png", fout); - } catch (final IOException e) { - e.printStackTrace(); - } - /** - final String filenameJGL = getSnapshotFilename(sn, "jgl", - drawable.getChosenGLCapabilities(), drawable.getWidth(), drawable.getHeight(), - glReadBufferUtil.hasAlpha(), fileSuffix, destPath); - glReadBufferUtil.write(new File(filenameJGL)); - */ - } - }; - static GLCapabilitiesImmutable caps = null; static boolean doSnapshot = true; static boolean keyFrame = false; diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLWindowNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLWindowNEWT.java index 8e0e6bfb9..4da73dbca 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLWindowNEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLWindowNEWT.java @@ -42,8 +42,11 @@ import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; +import com.jogamp.common.util.VersionUtil; import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.JoglVersion; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.jogl.demos.es2.MultisampleDemoES2; import com.jogamp.opengl.test.junit.util.MiscUtils; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.GLDrawableUtil; @@ -51,7 +54,11 @@ import com.jogamp.opengl.util.GLReadBufferUtil; import com.jogamp.opengl.util.texture.TextureIO; /** - * Multiple GLJPanels in a JFrame + * Test synchronous GLAutoDrawable display, swap-buffer and read-pixels with NEWT + * including non-MSAA and MSAA framebuffer. + * <p> + * See {@link GLReadBuffer00Base} for related bugs and further details. + * </p> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestGLReadBuffer01GLWindowNEWT extends GLReadBuffer00Base { @@ -73,9 +80,33 @@ public class TestGLReadBuffer01GLWindowNEWT extends GLReadBuffer00Base { try { glad.setPosition(64, 64); glad.setSize(320, 240); - final GearsES2 gears = new GearsES2(1); - gears.setVerbose(false); - glad.addGLEventListener(gears); + glad.addGLEventListener(new GLEventListener() { + @Override + public void init(final GLAutoDrawable drawable) { + final GL gl = drawable.getGL(); + System.err.println(VersionUtil.getPlatformInfo()); + System.err.println("GLEventListener init on "+Thread.currentThread()); + System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities()); + System.err.println("INIT GL IS: " + gl.getClass().getName()); + System.err.println(JoglVersion.getGLStrings(gl, null, false).toString()); + } + @Override + public void dispose(final GLAutoDrawable drawable) {} + @Override + public void display(final GLAutoDrawable drawable) {} + @Override + public void reshape(final GLAutoDrawable drawable, final int x,final int y, final int width, final int height) {} + }); + { + final GearsES2 gears = new GearsES2(1); + gears.setVerbose(false); + glad.addGLEventListener(gears); + } + { + final MultisampleDemoES2 demo = new MultisampleDemoES2(caps.getSampleBuffers()); + demo.setClearBuffers(false);; + glad.addGLEventListener(demo); + } textRendererGLEL.setFlipVerticalInGLOrientation(skipGLOrientationVerticalFlip); glad.addGLEventListener(textRendererGLEL); if( doSnapshot ) { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGPUMemSec01NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGPUMemSec01NEWT.java index 1f9e6e877..aabd4821d 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGPUMemSec01NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGPUMemSec01NEWT.java @@ -61,8 +61,10 @@ public class TestGPUMemSec01NEWT extends UITestCase { } static NEWTGLContext.WindowContext createCurrentGLOffscreenWindow(final GLProfile glp, final int width, final int height) throws GLException, InterruptedException { - final NEWTGLContext.WindowContext winctx = NEWTGLContext.createOffscreenWindow( - new GLCapabilities(glp), width, height, true); + final GLCapabilities caps = new GLCapabilities(glp); + caps.setOnscreen(false); + final NEWTGLContext.WindowContext winctx = NEWTGLContext.createWindow( + caps, width, height, true); final GL gl = winctx.context.getGL(); // System.err.println("Pre GL Error: 0x"+Integer.toHexString(gl.glGetError())); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestInitConcurrent01NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestInitConcurrent01NEWT.java index 2fc3071c6..a3c5d44cd 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestInitConcurrent01NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestInitConcurrent01NEWT.java @@ -30,6 +30,8 @@ package com.jogamp.opengl.test.junit.jogl.acore; import java.io.IOException; +import javax.media.opengl.GLProfile; + import org.junit.Test; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; @@ -42,6 +44,10 @@ import com.jogamp.common.os.Platform; * Rendering is always lock-free and independent of the EDT, however shared NEWT Display instances * perform lifecycle actions (window creation etc) with locking. * </p> + * <p> + * Each test is decorated w/ {@link GLProfile#shutdown()} to ensure that + * implicit {@link GLProfile#initSingleton()} is also being tested. + * </p> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestInitConcurrent01NEWT extends InitConcurrentBaseNEWT { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestInitConcurrent02NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestInitConcurrent02NEWT.java index 71796e5c2..5cbf15b57 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestInitConcurrent02NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestInitConcurrent02NEWT.java @@ -30,6 +30,8 @@ package com.jogamp.opengl.test.junit.jogl.acore; import java.io.IOException; +import javax.media.opengl.GLProfile; + import org.junit.Test; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; @@ -39,9 +41,13 @@ import com.jogamp.common.os.Platform; /** * Concurrent and lock-free initialization and rendering using exclusive NEWT Display EDT instances. * <p> - * Rendering is always lock-free and independent of the EDT, however exclusive NEWT Display instances + * Rendering is always lock-free and independent of the EDT, using exclusive NEWT Display instances * perform lifecycle actions (window creation etc) w/o locking. * </p> + * <p> + * Each test is decorated w/ {@link GLProfile#shutdown()} to ensure that + * implicit {@link GLProfile#initSingleton()} is also being tested. + * </p> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestInitConcurrent02NEWT extends InitConcurrentBaseNEWT { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestMapBufferRead01NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestMapBufferRead01NEWT.java index d94c24af0..5ba0c7794 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestMapBufferRead01NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestMapBufferRead01NEWT.java @@ -99,8 +99,10 @@ public class TestMapBufferRead01NEWT extends UITestCase { private void testWriteRead01(final ByteBuffer verticiesBB, final boolean useRange) throws InterruptedException { final GLProfile glp = GLProfile.getMaxProgrammable(true); - final NEWTGLContext.WindowContext winctx = NEWTGLContext.createOffscreenWindow( - new GLCapabilities(glp), 800, 600, true); + final GLCapabilities caps = new GLCapabilities(glp); + caps.setOnscreen(false); + final NEWTGLContext.WindowContext winctx = NEWTGLContext.createWindow( + caps, 800, 600, true); try { final GL gl = winctx.context.getGL(); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextNewtAWTBug523.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextNewtAWTBug523.java index 11ca64258..d3cdf5e95 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextNewtAWTBug523.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextNewtAWTBug523.java @@ -79,7 +79,6 @@ import com.jogamp.opengl.test.junit.util.AWTRobotUtil; import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.test.junit.util.AWTRobotUtil.WindowClosingListener; import com.jogamp.opengl.util.Animator; -import com.jogamp.opengl.util.GLBuffers; /** @@ -819,19 +818,7 @@ public class TestSharedContextNewtAWTBug523 extends UITestCase { } } } - - final String testname = TestSharedContextNewtAWTBug523.class.getName(); - org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(new String[] { - testname, - "filtertrace=true", - "haltOnError=false", - "haltOnFailure=false", - "showoutput=true", - "outputtoformatters=true", - "logfailedtests=true", - "logtestlistenerevents=true", - "formatter=org.apache.tools.ant.taskdefs.optional.junit.PlainJUnitResultFormatter", - "formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,TEST-"+testname+".xml" } ); + org.junit.runner.JUnitCore.main(TestSharedContextNewtAWTBug523.class.getName()); } } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES1NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES1NEWT.java index 8f84e293d..f1c72cf57 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES1NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES1NEWT.java @@ -138,12 +138,13 @@ public class TestSharedContextVBOES1NEWT extends UITestCase { Assert.assertTrue(AWTRobotUtil.waitForVisible(glWindow, true)); Assert.assertTrue(AWTRobotUtil.waitForContextCreated(glWindow, true)); - System.err.println("Master Context: "); - MiscUtils.dumpSharedGLContext(sharedDrawable.getContext()); - System.err.println("New Context: "); - MiscUtils.dumpSharedGLContext(glWindow.getContext()); + MiscUtils.dumpSharedGLContext("Master Context", sharedDrawable.getContext()); + MiscUtils.dumpSharedGLContext("New Context", glWindow.getContext()); if( useShared ) { Assert.assertEquals("Master Context not shared as expected", true, sharedDrawable.getContext().isShared()); + Assert.assertEquals("Master Context is different", sharedDrawable.getContext(), glWindow.getContext().getSharedMaster()); + } else { + } Assert.assertEquals("New Context not shared as expected", useShared, glWindow.getContext().isShared()); Assert.assertEquals("Gears is not shared as expected", useShared, gears.usesSharedGears()); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2AWT3.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2AWT3.java index a5b5653c0..fcbfcb19b 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2AWT3.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2AWT3.java @@ -157,12 +157,9 @@ public class TestSharedContextVBOES2AWT3 extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-C-3.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-C-3.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-C-3.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-3.3", ctx3); Assert.assertTrue("Ctx1 is not shared", ctx1.isShared()); Assert.assertTrue("Ctx2 is not shared", ctx2.isShared()); @@ -307,12 +304,9 @@ public class TestSharedContextVBOES2AWT3 extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-C-3.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-C-3.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-C-3.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-3.3", ctx3); Assert.assertTrue("Ctx1 is not shared", ctx1.isShared()); Assert.assertTrue("Ctx2 is not shared", ctx2.isShared()); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2AWT3b.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2AWT3b.java index 4c9cb7253..bac84d4fb 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2AWT3b.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2AWT3b.java @@ -148,12 +148,9 @@ public class TestSharedContextVBOES2AWT3b extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-C-3.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-C-3.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-C-3.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-3.3", ctx3); Assert.assertTrue("Ctx1 is not shared", ctx1.isShared()); Assert.assertTrue("Ctx2 is not shared", ctx2.isShared()); @@ -260,12 +257,9 @@ public class TestSharedContextVBOES2AWT3b extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-C-3.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-C-3.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-C-3.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-3.3", ctx3); Assert.assertTrue("Ctx1 is not shared", ctx1.isShared()); Assert.assertTrue("Ctx2 is not shared", ctx2.isShared()); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT0.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT0.java index d2d1384e4..eeab0869a 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT0.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT0.java @@ -122,8 +122,7 @@ public class TestSharedContextVBOES2NEWT0 extends UITestCase { Assert.assertTrue("Ctx is shared before shared creation", !ctx1.isShared()); final InsetsImmutable insets = f1.getInsets(); - System.err.println("XXX-C-2.1:"); - MiscUtils.dumpSharedGLContext(ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-1.1", ctx1); // // 2nd @@ -139,10 +138,8 @@ public class TestSharedContextVBOES2NEWT0 extends UITestCase { { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); - System.err.println("XXX-C-2.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-C-2.2:"); - MiscUtils.dumpSharedGLContext(ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-2.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-2.2", ctx2); Assert.assertEquals("Ctx1 has unexpected number of created shares", 1, ctx1Shares.size()); Assert.assertEquals("Ctx2 has unexpected number of created shares", 1, ctx2Shares.size()); @@ -165,12 +162,9 @@ public class TestSharedContextVBOES2NEWT0 extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-C-3.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-C-3.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-C-3.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-3.3", ctx3); Assert.assertEquals("Ctx1 has unexpected number of created shares", 2, ctx1Shares.size()); Assert.assertEquals("Ctx2 has unexpected number of created shares", 2, ctx2Shares.size()); @@ -197,12 +191,9 @@ public class TestSharedContextVBOES2NEWT0 extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-D-0.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-D-0.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-D-0.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-D-0.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-D-0.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-D-0.3", ctx3); Assert.assertTrue("Ctx1 is shared", ctx1.isShared()); Assert.assertTrue("Ctx2 is shared", ctx2.isShared()); @@ -221,12 +212,9 @@ public class TestSharedContextVBOES2NEWT0 extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-D-1.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-D-1.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-D-1.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-D-1.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-D-1.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-D-1.3", ctx3); Assert.assertTrue("Ctx1 is not shared", ctx1.isShared()); Assert.assertTrue("Ctx2 is not shared", ctx2.isShared()); @@ -245,12 +233,9 @@ public class TestSharedContextVBOES2NEWT0 extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-D-2.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-D-2.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-D-2.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-D-2.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-D-2.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-D-2.3", ctx3); Assert.assertTrue("Ctx1 is not shared", !ctx1.isShared()); Assert.assertTrue("Ctx2 is not shared", !ctx2.isShared()); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT1.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT1.java index fb15509d0..e98957464 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT1.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT1.java @@ -153,12 +153,14 @@ public class TestSharedContextVBOES2NEWT1 extends UITestCase { Assert.assertTrue(AWTRobotUtil.waitForVisible(glWindow, true)); Assert.assertTrue(AWTRobotUtil.waitForContextCreated(glWindow, true)); - System.err.println("Master Context: "); - MiscUtils.dumpSharedGLContext(sharedDrawable.getContext()); - System.err.println("New Context: "); - MiscUtils.dumpSharedGLContext(glWindow.getContext()); + final GLContext sharedMasterContext = sharedDrawable.getContext(); + MiscUtils.dumpSharedGLContext("Master Context", sharedMasterContext); + MiscUtils.dumpSharedGLContext("New Context", glWindow.getContext()); if( useShared ) { - Assert.assertEquals("Master Context not shared as expected", true, sharedDrawable.getContext().isShared()); + Assert.assertEquals("Master Context not shared as expected", true, sharedMasterContext.isShared()); + Assert.assertEquals("Master Context is different", sharedMasterContext, glWindow.getContext().getSharedMaster()); + } else { + Assert.assertEquals("Master Context is not null", null, glWindow.getContext().getSharedMaster()); } Assert.assertEquals("New Context not shared as expected", useShared, glWindow.getContext().isShared()); @@ -169,7 +171,7 @@ public class TestSharedContextVBOES2NEWT1 extends UITestCase { return glWindow; } - @Test + // @Test public void test01CommonAnimatorSharedOnscreen() throws InterruptedException { initShared(true); final Animator animator = new Animator(); @@ -202,7 +204,7 @@ public class TestSharedContextVBOES2NEWT1 extends UITestCase { releaseShared(); } - @Test + // @Test public void test02CommonAnimatorSharedOffscreen() throws InterruptedException { initShared(false); final Animator animator = new Animator(); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT2.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT2.java index a101c05d0..fb09e86ef 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT2.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT2.java @@ -55,9 +55,8 @@ import org.junit.runners.MethodSorters; * Sharing the VBO of 3 GearsES2 instances, each in their own GLWindow. * <p> * This is achieved by using the 1st GLWindow's GLContext as the <i>master</i> - * and manually triggering creation of the 2nd and 3rd GLWindow when the 1st GLWindow's - * GLContext becomes created. The trigger is performed by simply - * inserting a GLRunnable in the 1st GLWindow, which makes the other visible. + * and synchronizing via GLSharedContextSetter to postpone creation + * of the 2nd and 3rd GLWindow until the 1st GLWindow's GLContext becomes created. * </p> * <p> * Above method allows random creation of the 1st GLWindow, which triggers @@ -107,6 +106,8 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { public void syncedOneAnimator(final boolean destroyCleanOrder) throws InterruptedException { final Animator animator = new Animator(); + animator.start(); + final GearsES2 g1 = new GearsES2(0); final GLWindow f1 = createGLWindow(0, 0, g1); animator.add(f1); @@ -116,31 +117,31 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { g2.setSharedGears(g1); final GLWindow f2 = createGLWindow(f1.getX()+width+insets.getTotalWidth(), f1.getY()+0, g2); + f2.setSharedAutoDrawable(f1); animator.add(f2); + f2.setVisible(true); final GearsES2 g3 = new GearsES2(0); g3.setSharedGears(g1); final GLWindow f3 = createGLWindow(f1.getX()+0, f1.getY()+height+insets.getTotalHeight(), g3); + f3.setSharedAutoDrawable(f1); animator.add(f3); + f3.setVisible(true); - // f1's shared GLContext is ready ! - f1.invoke(false, new GLRunnable() { - @Override - public boolean run(final GLAutoDrawable drawable) { - Assert.assertTrue("Ctx is shared before shared creation", !f1.getContext().isShared()); - f2.setSharedAutoDrawable(f1); - f2.setVisible(true); - f2.display(); // kick off GLContext .. - f3.setSharedAutoDrawable(f1); - f3.setVisible(true); - f3.display(); // kick off GLContext .. - return true; - } - }); + Assert.assertTrue(AWTRobotUtil.waitForRealized(f1, false)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f1, false)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f1, false)); - f1.setVisible(true); - f1.display(); // kick off GLContext .. and hence f2 + f3 creation + Assert.assertTrue(AWTRobotUtil.waitForRealized(f2, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f2, true)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f2, false)); + + Assert.assertTrue(AWTRobotUtil.waitForRealized(f3, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f3, true)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f3, false)); + + f1.setVisible(true); // kick off f1 GLContext .. and hence allow f2 + f3 creation Assert.assertTrue(AWTRobotUtil.waitForRealized(f1, true)); Assert.assertTrue(AWTRobotUtil.waitForVisible(f1, true)); @@ -157,8 +158,6 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f3, true)); Assert.assertTrue("Gears3 not initialized", g3.waitForInit(true)); - animator.start(); // post start animator, otherwise display will be suppressed - final GLContext ctx1 = f1.getContext(); final GLContext ctx2 = f2.getContext(); final GLContext ctx3 = f3.getContext(); @@ -166,12 +165,9 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-C-3.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-C-3.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-C-3.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-3.3", ctx3); Assert.assertTrue("Ctx1 is not shared", ctx1.isShared()); Assert.assertTrue("Ctx2 is not shared", ctx2.isShared()); @@ -179,6 +175,9 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { Assert.assertEquals("Ctx1 has unexpected number of created shares", 2, ctx1Shares.size()); Assert.assertEquals("Ctx2 has unexpected number of created shares", 2, ctx2Shares.size()); Assert.assertEquals("Ctx3 has unexpected number of created shares", 2, ctx3Shares.size()); + Assert.assertEquals("Ctx1 Master Context is different", ctx1, ctx1.getSharedMaster()); + Assert.assertEquals("Ctx2 Master Context is different", ctx1, ctx2.getSharedMaster()); + Assert.assertEquals("Ctx3 Master Context is different", ctx1, ctx3.getSharedMaster()); } Assert.assertTrue("Gears1 is shared", !g1.usesSharedGears()); @@ -228,7 +227,6 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { final GLWindow f1 = createGLWindow(0, 0, g1); a1.add(f1); a1.start(); - f1.setVisible(true); final InsetsImmutable insets = f1.getInsets(); @@ -237,29 +235,34 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { g2.setSharedGears(g1); final GLWindow f2 = createGLWindow(f1.getX()+width+insets.getTotalWidth(), f1.getY()+0, g2); + f2.setSharedAutoDrawable(f1); a2.add(f2); a2.start(); + f2.setVisible(true); final Animator a3 = new Animator(); final GearsES2 g3 = new GearsES2(0); g3.setSharedGears(g1); final GLWindow f3 = createGLWindow(f1.getX()+0, f1.getY()+height+insets.getTotalHeight(), g3); + f3.setSharedAutoDrawable(f1); a3.add(f3); a3.start(); + f3.setVisible(true); - // f1's shared GLContext is ready ! - f1.invoke(false, new GLRunnable() { - @Override - public boolean run(final GLAutoDrawable drawable) { - Assert.assertTrue("Ctx is shared before shared creation", !f1.getContext().isShared()); - f2.setSharedAutoDrawable(f1); - f2.setVisible(true); - f3.setSharedAutoDrawable(f1); - f3.setVisible(true); - return true; - } - }); + Assert.assertTrue(AWTRobotUtil.waitForRealized(f1, false)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f1, false)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f1, false)); + + Assert.assertTrue(AWTRobotUtil.waitForRealized(f2, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f2, true)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f2, false)); + + Assert.assertTrue(AWTRobotUtil.waitForRealized(f3, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f3, true)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f3, false)); + + f1.setVisible(true); // kicks off f1 GLContext .. and hence gears of f2 + f3 completion Assert.assertTrue(AWTRobotUtil.waitForRealized(f1, true)); Assert.assertTrue(AWTRobotUtil.waitForVisible(f1, true)); @@ -282,12 +285,9 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-C-3.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-C-3.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-C-3.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-3.3", ctx3); Assert.assertTrue("Ctx1 is not shared", ctx1.isShared()); Assert.assertTrue("Ctx2 is not shared", ctx2.isShared()); @@ -295,6 +295,9 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { Assert.assertEquals("Ctx1 has unexpected number of created shares", 2, ctx1Shares.size()); Assert.assertEquals("Ctx2 has unexpected number of created shares", 2, ctx2Shares.size()); Assert.assertEquals("Ctx3 has unexpected number of created shares", 2, ctx3Shares.size()); + Assert.assertEquals("Ctx1 Master Context is different", ctx1, ctx1.getSharedMaster()); + Assert.assertEquals("Ctx2 Master Context is different", ctx1, ctx2.getSharedMaster()); + Assert.assertEquals("Ctx3 Master Context is different", ctx1, ctx3.getSharedMaster()); } Assert.assertTrue("Gears1 is shared", !g1.usesSharedGears()); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT3.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT3.java index bfd1a92ce..852b7193e 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT3.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT3.java @@ -53,7 +53,7 @@ import org.junit.runners.MethodSorters; * Sharing the VBO of 3 GearsES2 instances, each in their own GLWindow. * <p> * This is achieved by using the 1st GLWindow as the <i>master</i> - * and using the build-in blocking mechanism to postpone creation + * and synchronizing via GLSharedContextSetter to postpone creation * of the 2nd and 3rd GLWindow until the 1st GLWindow's GLContext becomes created. * </p> * <p> @@ -112,6 +112,8 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { public void syncedOneAnimator(final boolean destroyCleanOrder, final boolean useMappedBuffers) throws InterruptedException { final Animator animator = new Animator(); + animator.start(); + final GearsES2 g1 = new GearsES2(0); g1.setUseMappedBuffers(useMappedBuffers); g1.setValidateBuffers(true); @@ -125,20 +127,17 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { f1.getY()+0, g2); f2.setSharedAutoDrawable(f1); animator.add(f2); + f2.setVisible(true); // shall wait until f1 is ready - final GearsES2 g3 = new GearsES2(0); - g3.setSharedGears(g1); - final GLWindow f3 = createGLWindow(f1.getX()+0, - f1.getY()+height+insets.getTotalHeight(), g3); - f3.setSharedAutoDrawable(f1); - animator.add(f3); + Assert.assertTrue(AWTRobotUtil.waitForRealized(f1, false)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f1, false)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f1, false)); - f2.setVisible(true); // shall wait until f1 is ready - f1.setVisible(true); // master .. - f3.setVisible(true); // shall wait until f1 is ready - animator.start(); // kicks off GLContext .. and hence gears of f2 + f3 completion + Assert.assertTrue(AWTRobotUtil.waitForRealized(f2, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f2, true)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f2, false)); - Thread.sleep(1000/60*10); // wait ~10 frames giving a chance to create (blocking until master share is valid) + f1.setVisible(true); // kicks off f1 GLContext .. and hence gears of f2 + f3 completion Assert.assertTrue(AWTRobotUtil.waitForRealized(f1, true)); Assert.assertTrue(AWTRobotUtil.waitForVisible(f1, true)); @@ -150,6 +149,14 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f2, true)); Assert.assertTrue("Gears2 not initialized", g2.waitForInit(true)); + final GearsES2 g3 = new GearsES2(0); + g3.setSharedGears(g1); + final GLWindow f3 = createGLWindow(f1.getX()+0, + f1.getY()+height+insets.getTotalHeight(), g3); + f3.setSharedAutoDrawable(f1); + animator.add(f3); + f3.setVisible(true); // shall wait until f1 is ready + Assert.assertTrue(AWTRobotUtil.waitForRealized(f3, true)); Assert.assertTrue(AWTRobotUtil.waitForVisible(f3, true)); Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f3, true)); @@ -162,12 +169,9 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-C-3.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-C-3.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-C-3.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-3.3", ctx3); Assert.assertTrue("Ctx1 is not shared", ctx1.isShared()); Assert.assertTrue("Ctx2 is not shared", ctx2.isShared()); @@ -175,6 +179,9 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { Assert.assertEquals("Ctx1 has unexpected number of created shares", 2, ctx1Shares.size()); Assert.assertEquals("Ctx2 has unexpected number of created shares", 2, ctx2Shares.size()); Assert.assertEquals("Ctx3 has unexpected number of created shares", 2, ctx3Shares.size()); + Assert.assertEquals("Ctx1 Master Context is different", ctx1, ctx1.getSharedMaster()); + Assert.assertEquals("Ctx2 Master Context is different", ctx1, ctx2.getSharedMaster()); + Assert.assertEquals("Ctx3 Master Context is different", ctx1, ctx3.getSharedMaster()); } Assert.assertTrue("Gears1 is shared", !g1.usesSharedGears()); @@ -246,7 +253,6 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { final GLWindow f1 = createGLWindow(0, 0, g1); a1.add(f1); a1.start(); - // f1.setVisible(true); // we do this post f2 .. to test pending creation! final InsetsImmutable insets = f1.getInsets(); @@ -260,19 +266,15 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { a2.start(); f2.setVisible(true); - f1.setVisible(true); // test pending creation of f2 + Assert.assertTrue(AWTRobotUtil.waitForRealized(f1, false)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f1, false)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f1, false)); - final Animator a3 = new Animator(); - final GearsES2 g3 = new GearsES2(0); - g3.setSharedGears(g1); - final GLWindow f3 = createGLWindow(f1.getX()+0, - f1.getY()+height+insets.getTotalHeight(), g3); - f3.setSharedAutoDrawable(f1); - a3.add(f3); - a3.start(); - f3.setVisible(true); + Assert.assertTrue(AWTRobotUtil.waitForRealized(f2, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f2, true)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f2, false)); - Thread.sleep(1000/60*10); // wait ~10 frames giving a chance to create (blocking until master share is valid) + f1.setVisible(true); // test pending creation of f2 Assert.assertTrue(AWTRobotUtil.waitForRealized(f1, true)); Assert.assertTrue(AWTRobotUtil.waitForVisible(f1, true)); @@ -284,6 +286,16 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f2, true)); Assert.assertTrue("Gears2 not initialized", g2.waitForInit(true)); + final Animator a3 = new Animator(); + final GearsES2 g3 = new GearsES2(0); + g3.setSharedGears(g1); + final GLWindow f3 = createGLWindow(f1.getX()+0, + f1.getY()+height+insets.getTotalHeight(), g3); + f3.setSharedAutoDrawable(f1); + a3.add(f3); + a3.start(); + f3.setVisible(true); + Assert.assertTrue(AWTRobotUtil.waitForRealized(f3, true)); Assert.assertTrue(AWTRobotUtil.waitForVisible(f3, true)); Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f3, true)); @@ -296,12 +308,9 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-C-3.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-C-3.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-C-3.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-3.3", ctx3); Assert.assertTrue("Ctx1 is not shared", ctx1.isShared()); Assert.assertTrue("Ctx2 is not shared", ctx2.isShared()); @@ -309,6 +318,9 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { Assert.assertEquals("Ctx1 has unexpected number of created shares", 2, ctx1Shares.size()); Assert.assertEquals("Ctx2 has unexpected number of created shares", 2, ctx2Shares.size()); Assert.assertEquals("Ctx3 has unexpected number of created shares", 2, ctx3Shares.size()); + Assert.assertEquals("Ctx1 Master Context is different", ctx1, ctx1.getSharedMaster()); + Assert.assertEquals("Ctx2 Master Context is different", ctx1, ctx2.getSharedMaster()); + Assert.assertEquals("Ctx3 Master Context is different", ctx1, ctx3.getSharedMaster()); } Assert.assertTrue("Gears1 is shared", !g1.usesSharedGears()); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT4.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT4.java new file mode 100644 index 000000000..804badbc3 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT4.java @@ -0,0 +1,231 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.jogl.acore; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.jogamp.newt.opengl.GLWindow; + +import javax.media.nativewindow.util.InsetsImmutable; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLProfile; + +import com.jogamp.opengl.util.Animator; +import com.jogamp.opengl.test.junit.util.AWTRobotUtil; +import com.jogamp.opengl.test.junit.util.MiscUtils; +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +/** + * Test sharing w/ different shared-master context. + * <p> + * This is achieved by using the 1st GLWindow as the <i>master</i> + * and synchronizing via GLSharedContextSetter to postpone creation + * of the 2nd and 3rd GLWindow until the 1st GLWindow's GLContext becomes created. + * </p> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestSharedContextVBOES2NEWT4 extends UITestCase { + static GLProfile glp; + static GLCapabilities caps; + static int width, height; + + @BeforeClass + public static void initClass() { + if(GLProfile.isAvailable(GLProfile.GL2ES2)) { + glp = GLProfile.get(GLProfile.GL2ES2); + Assert.assertNotNull(glp); + caps = new GLCapabilities(glp); + Assert.assertNotNull(caps); + width = 256; + height = 256; + } else { + setTestSupported(false); + } + } + + protected GLWindow createGLWindow(final int x, final int y, final GearsES2 gears) throws InterruptedException { + final GLWindow glWindow = GLWindow.create(caps); + Assert.assertNotNull(glWindow); + glWindow.setPosition(x, y); + glWindow.setTitle("Shared Gears NEWT Test: "+x+"/"+y+" shared true"); + glWindow.setSize(width, height); + glWindow.addGLEventListener(gears); + + return glWindow; + } + + @Test + public void test01() throws InterruptedException { + final Animator animator = new Animator(); + animator.start(); + + final GearsES2 g1 = new GearsES2(0); + final GLWindow f1 = createGLWindow(0, 0, g1); + animator.add(f1); + final InsetsImmutable insets = f1.getInsets(); + + final GearsES2 g2 = new GearsES2(0); + g2.setSharedGears(g1); + final GLWindow f2 = createGLWindow(f1.getX()+width+insets.getTotalWidth(), + f1.getY()+0, g2); + f2.setSharedAutoDrawable(f1); + animator.add(f2); + f2.setVisible(true); + + final GearsES2 g3 = new GearsES2(0); + g3.setSharedGears(g1); + final GLWindow f3 = createGLWindow(f1.getX()+0, + f1.getY()+height+insets.getTotalHeight(), g3); + f3.setSharedAutoDrawable(f2); // Mixed master! + animator.add(f3); + final AtomicBoolean gotAnimException = new AtomicBoolean(false); + final AtomicBoolean gotOtherException = new AtomicBoolean(false); + animator.setUncaughtExceptionHandler(new GLAnimatorControl.UncaughtExceptionHandler() { + @Override + public void uncaughtException(final GLAnimatorControl _animator, final GLAutoDrawable _drawable, final Throwable _cause) { + if( _animator == animator && _drawable == f3 && _cause instanceof RuntimeException ) { + System.err.println("Caught expected exception: "+_cause.getMessage()); + gotAnimException.set(true); + } else { + System.err.println("Caught unexpected exception: "+_cause.getMessage()); + _cause.printStackTrace(); + gotOtherException.set(true); + } + } + }); + f3.setVisible(true); + + Assert.assertTrue(AWTRobotUtil.waitForRealized(f1, false)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f1, false)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f1, false)); + + Assert.assertTrue(AWTRobotUtil.waitForRealized(f2, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f2, true)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f2, false)); + + Assert.assertTrue(AWTRobotUtil.waitForRealized(f3, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f3, true)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f3, false)); + + f1.setVisible(true); // kick off f1 GLContext .. and hence allow f2 + f3 creation + + Assert.assertTrue(AWTRobotUtil.waitForRealized(f1, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f1, true)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f1, true)); + Assert.assertTrue("Gears1 not initialized", g1.waitForInit(true)); + + Assert.assertTrue(AWTRobotUtil.waitForRealized(f2, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f2, true)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f2, true)); + Assert.assertTrue("Gears2 not initialized", g2.waitForInit(true)); + + Assert.assertTrue(AWTRobotUtil.waitForRealized(f3, true)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f3, true)); + Assert.assertTrue(AWTRobotUtil.waitForContextCreated(f3, true)); + Assert.assertTrue("Gears3 not initialized", g3.waitForInit(true)); + + Assert.assertFalse("Unexpected exception (animator) caught", gotAnimException.get()); + Assert.assertFalse("Unexpected exception (other) caught", gotOtherException.get()); + + final GLContext ctx1 = f1.getContext(); + final GLContext ctx2 = f2.getContext(); + final GLContext ctx3 = f3.getContext(); + { + final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); + final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); + final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); + MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-3.3", ctx3); + + Assert.assertTrue("Ctx1 is not shared", ctx1.isShared()); + Assert.assertTrue("Ctx2 is not shared", ctx2.isShared()); + Assert.assertTrue("Ctx3 is not shared", ctx3.isShared()); + Assert.assertEquals("Ctx1 has unexpected number of created shares", 2, ctx1Shares.size()); + Assert.assertEquals("Ctx2 has unexpected number of created shares", 2, ctx2Shares.size()); + Assert.assertEquals("Ctx3 has unexpected number of created shares", 2, ctx3Shares.size()); + Assert.assertEquals("Ctx1 Master Context is different", ctx1, ctx1.getSharedMaster()); + Assert.assertEquals("Ctx2 Master Context is different", ctx1, ctx2.getSharedMaster()); + Assert.assertEquals("Ctx3 Master Context is different", ctx2, ctx3.getSharedMaster()); // Mixed master! + } + + Assert.assertTrue("Gears1 is shared", !g1.usesSharedGears()); + Assert.assertTrue("Gears2 is not shared", g2.usesSharedGears()); + Assert.assertTrue("Gears3 is not shared", g3.usesSharedGears()); + + try { + Thread.sleep(duration); + } catch(final Exception e) { + e.printStackTrace(); + } + animator.stop(); + Assert.assertEquals(false, animator.isAnimating()); + + System.err.println("XXX Destroy in clean order NOW"); + f3.destroy(); + f2.destroy(); + f1.destroy(); + + Assert.assertTrue(AWTRobotUtil.waitForVisible(f1, false)); + Assert.assertTrue(AWTRobotUtil.waitForRealized(f1, false)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f2, false)); + Assert.assertTrue(AWTRobotUtil.waitForRealized(f2, false)); + Assert.assertTrue(AWTRobotUtil.waitForVisible(f3, false)); + Assert.assertTrue(AWTRobotUtil.waitForRealized(f3, false)); + } + + static long duration = 1000; // ms + + public static void main(final String args[]) { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + try { + duration = Integer.parseInt(args[i]); + } catch (final Exception ex) { ex.printStackTrace(); } + } + } + /** + BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); + System.err.println("Press enter to continue"); + System.err.println(stdin.readLine()); */ + org.junit.runner.JUnitCore.main(TestSharedContextVBOES2NEWT4.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2SWT3.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2SWT3.java index 90cb503ce..09f4408f1 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2SWT3.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2SWT3.java @@ -220,12 +220,9 @@ public class TestSharedContextVBOES2SWT3 extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-C-3.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-C-3.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-C-3.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-3.3", ctx3); Assert.assertTrue("Ctx1 is not shared", ctx1.isShared()); Assert.assertTrue("Ctx2 is not shared", ctx2.isShared()); @@ -314,12 +311,9 @@ public class TestSharedContextVBOES2SWT3 extends UITestCase { final List<GLContext> ctx1Shares = ctx1.getCreatedShares(); final List<GLContext> ctx2Shares = ctx2.getCreatedShares(); final List<GLContext> ctx3Shares = ctx3.getCreatedShares(); - System.err.println("XXX-C-3.1:"); - MiscUtils.dumpSharedGLContext(ctx1); - System.err.println("XXX-C-3.2:"); - MiscUtils.dumpSharedGLContext(ctx2); - System.err.println("XXX-C-3.3:"); - MiscUtils.dumpSharedGLContext(ctx3); + MiscUtils.dumpSharedGLContext("XXX-C-3.1", ctx1); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx2); + MiscUtils.dumpSharedGLContext("XXX-C-3.2", ctx3); Assert.assertTrue("Ctx1 is not shared", ctx1.isShared()); Assert.assertTrue("Ctx2 is not shared", ctx2.isShared()); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestVersionSemanticsNOUI.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestVersionSemanticsNOUI.java index 6f38c4b2f..0c2a7510b 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestVersionSemanticsNOUI.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestVersionSemanticsNOUI.java @@ -37,7 +37,6 @@ import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; import org.osjava.jardiff.DiffCriteria; -import org.osjava.jardiff.SimpleDiffCriteria; import org.semver.Delta; import com.jogamp.common.util.JogampVersion; @@ -49,39 +48,48 @@ import com.jogamp.opengl.JoglVersion; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestVersionSemanticsNOUI extends JunitTracer { static final String jarFile = "jogl-all.jar"; - static final VersionNumberString preVersionNumber = new VersionNumberString("2.1.5"); - static final Delta.CompatibilityType expectedCompatibilityType = Delta.CompatibilityType.NON_BACKWARD_COMPATIBLE; - static final DiffCriteria diffCriteria = new SimpleDiffCriteria(); - // static final DiffCriteria diffCriteria = new PublicDiffCriteria(); + static final DiffCriteria diffCriteria = new org.osjava.jardiff.SimpleDiffCriteria(); + // static final DiffCriteria diffCriteria = new org.osjava.jardiff.PublicDiffCriteria(); static final JogampVersion curVersion = JoglVersion.getInstance(); static final VersionNumberString curVersionNumber = new VersionNumberString(curVersion.getImplementationVersion()); - static final Set<String> excludes; + static final Set<String> excludesDefault; static { - excludes = new HashSet<String>(); - excludes.add("^\\Qjogamp/\\E.*$"); + excludesDefault = new HashSet<String>(); + excludesDefault.add("^\\Qjogamp/\\E.*$"); } // @Test public void testVersionV212V213() throws IllegalArgumentException, IOException, URISyntaxException { - testVersions(diffCriteria, Delta.CompatibilityType.BACKWARD_COMPATIBLE_USER, "2.1.2", "2.1.3"); + testVersions(diffCriteria, Delta.CompatibilityType.BACKWARD_COMPATIBLE_USER, "2.1.2", "2.1.3", excludesDefault); } // @Test public void testVersionV213V214() throws IllegalArgumentException, IOException, URISyntaxException { - testVersions(diffCriteria, Delta.CompatibilityType.BACKWARD_COMPATIBLE_USER, "2.1.3", "2.1.4"); + testVersions(diffCriteria, Delta.CompatibilityType.BACKWARD_COMPATIBLE_USER, "2.1.3", "2.1.4", excludesDefault); } // @Test public void testVersionV214V215() throws IllegalArgumentException, IOException, URISyntaxException { - testVersions(diffCriteria, Delta.CompatibilityType.BACKWARD_COMPATIBLE_USER, "2.1.4", "2.1.5"); + testVersions(diffCriteria, Delta.CompatibilityType.BACKWARD_COMPATIBLE_USER, "2.1.4", "2.1.5", excludesDefault); + } + + // @Test + public void testVersionV215V220() throws IllegalArgumentException, IOException, URISyntaxException { + testVersions(diffCriteria, Delta.CompatibilityType.BACKWARD_COMPATIBLE_USER, "2.1.5", "2.2.0", excludesDefault); + } + + @Test + public void testVersionV220V221() throws IllegalArgumentException, IOException, URISyntaxException { + testVersions(diffCriteria, Delta.CompatibilityType.BACKWARD_COMPATIBLE_USER, "2.2.0", "2.2.1", excludesDefault); } void testVersions(final DiffCriteria diffCriteria, final Delta.CompatibilityType expectedCompatibilityType, - final String v1, final String v2) throws IllegalArgumentException, IOException, URISyntaxException { + final String v1, final String v2, final Set<String> excludes) + throws IllegalArgumentException, IOException, URISyntaxException { final VersionNumberString preVersionNumber = new VersionNumberString(v1); final File previousJar = new File("lib/v"+v1+"/"+jarFile); @@ -95,7 +103,11 @@ public class TestVersionSemanticsNOUI extends JunitTracer { } @Test - public void testVersionLatest() throws IllegalArgumentException, IOException, URISyntaxException { + public void testVersionV221V23x() throws IllegalArgumentException, IOException, URISyntaxException { + final Delta.CompatibilityType expectedCompatibilityType = Delta.CompatibilityType.NON_BACKWARD_COMPATIBLE; + // final Delta.CompatibilityType expectedCompatibilityType = Delta.CompatibilityType.BACKWARD_COMPATIBLE_USER; + + final VersionNumberString preVersionNumber = new VersionNumberString("2.2.1"); final File previousJar = new File("lib/v"+preVersionNumber.getVersionString()+"/"+jarFile); final ClassLoader currentCL = TestVersionSemanticsNOUI.class.getClassLoader(); @@ -103,7 +115,7 @@ public class TestVersionSemanticsNOUI extends JunitTracer { VersionSemanticsUtil.testVersion(diffCriteria, expectedCompatibilityType, previousJar, preVersionNumber, curVersion.getClass(), currentCL, curVersionNumber, - excludes); + excludesDefault); } public static void main(final String args[]) throws IOException { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/GLContextDrawableSwitchBase0.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/GLContextDrawableSwitchBase0.java new file mode 100644 index 000000000..08614f9fb --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/GLContextDrawableSwitchBase0.java @@ -0,0 +1,264 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.jogl.acore.glels; + +import java.lang.reflect.InvocationTargetException; + +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLProfile; +import javax.media.opengl.Threading; + +import com.jogamp.opengl.util.Animator; +import com.jogamp.opengl.util.GLDrawableUtil; +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.util.QuitAdapter; +import com.jogamp.opengl.test.junit.util.UITestCase; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +/** + * Test re-association (switching) of GLWindow /GLDrawables, + * from GLWindow/GLOffscreenAutoDrawable to an GLOffscreenAutoDrawable and back. + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public abstract class GLContextDrawableSwitchBase0 extends UITestCase { + static int width, height; + static boolean testEvenUnsafeSwapGLContext = false; + + static GLCapabilities getCaps(final String profile) { + if( !GLProfile.isAvailable(profile) ) { + System.err.println("Profile "+profile+" n/a"); + return null; + } + return new GLCapabilities(GLProfile.get(profile)); + } + + @BeforeClass + public static void initClass() { + width = 256; + height = 256; + } + + public abstract GLAutoDrawable createGLAutoDrawable(final QuitAdapter quitAdapter, final GLCapabilitiesImmutable caps, final int width, final int height) throws InterruptedException, InvocationTargetException; + public abstract void destroyGLAutoDrawable(final GLAutoDrawable glad) throws InterruptedException, InvocationTargetException; + + @Test(timeout=30000) + public void test01aSwitch2Onscreen2OnscreenGL2ES2_Def() throws InterruptedException, InvocationTargetException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + testImpl(reqGLCaps, true); + } + + @Test(timeout=30000) + public void test01bSwitch2Onscreen2OffscreenGL2ES2_Def() throws InterruptedException, InvocationTargetException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + testImpl(reqGLCaps, false); + } + + @Test(timeout=30000) + public void test01cSwitch2Offscreen2OffscreenGL2ES2_Def() throws InterruptedException, InvocationTargetException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + reqGLCaps.setOnscreen(false); + testImpl(reqGLCaps, false); + } + + @Test(timeout=30000) + public void test01dSwitch2Offscreen2OnscreenGL2ES2_Def() throws InterruptedException, InvocationTargetException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + reqGLCaps.setOnscreen(false); + testImpl(reqGLCaps, true); + } + + @Test(timeout=30000) + public void test02aSwitch2Onscreen2OnscreenGL2ES2_MSAA() throws InterruptedException, InvocationTargetException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + reqGLCaps.setNumSamples(4); + reqGLCaps.setSampleBuffers(true); + testImpl(reqGLCaps, true); + } + + @Test(timeout=30000) + public void test02bSwitch2Onscreen2OffscreenGL2ES2_MSAA() throws InterruptedException, InvocationTargetException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + reqGLCaps.setNumSamples(4); + reqGLCaps.setSampleBuffers(true); + testImpl(reqGLCaps, false); + } + + @Test(timeout=30000) + public void test02cSwitch2Offscreen2OffscreenGL2ES2_MSAA() throws InterruptedException, InvocationTargetException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + reqGLCaps.setOnscreen(false); + reqGLCaps.setNumSamples(4); + reqGLCaps.setSampleBuffers(true); + testImpl(reqGLCaps, false); + } + + @Test(timeout=30000) + public void test02dSwitch2Offscreen2OnscreenGL2ES2_MSAA() throws InterruptedException, InvocationTargetException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + reqGLCaps.setOnscreen(false); + reqGLCaps.setNumSamples(4); + reqGLCaps.setSampleBuffers(true); + testImpl(reqGLCaps, true); + } + + @Test(timeout=30000) + public void test03aSwitch2Onscreen2OnscreenGL2ES2_Accu() throws InterruptedException, InvocationTargetException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + reqGLCaps.setAccumRedBits(1); + reqGLCaps.setAccumGreenBits(1); + reqGLCaps.setAccumBlueBits(1); + testImpl(reqGLCaps, true); + } + + @Test(timeout=30000) + public void test03bSwitch2Onscreen2OffscreenGL2ES2_Accu() throws InterruptedException, InvocationTargetException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + reqGLCaps.setAccumRedBits(1); + reqGLCaps.setAccumGreenBits(1); + reqGLCaps.setAccumBlueBits(1); + testImpl(reqGLCaps, false); + } + + @Test(timeout=30000) + public void test03cSwitch2Offscreen2OffscreenGL2ES2_Accu() throws InterruptedException, InvocationTargetException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + reqGLCaps.setOnscreen(false); + reqGLCaps.setAccumRedBits(1); + reqGLCaps.setAccumGreenBits(1); + reqGLCaps.setAccumBlueBits(1); + testImpl(reqGLCaps, false); + } + + @Test(timeout=30000) + public void test03dSwitch2Offscreen2OnscreenGL2ES2_Accu() throws InterruptedException, InvocationTargetException { + final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); + if(null == reqGLCaps) return; + reqGLCaps.setOnscreen(false); + reqGLCaps.setAccumRedBits(1); + reqGLCaps.setAccumGreenBits(1); + reqGLCaps.setAccumBlueBits(1); + testImpl(reqGLCaps, true); + } + + private void testImpl(final GLCapabilitiesImmutable srcCapsRequested, final boolean dstOnscreen) throws InterruptedException, InvocationTargetException { + final QuitAdapter quitAdapter = new QuitAdapter(); + final GLAutoDrawable gladSource = createGLAutoDrawable(quitAdapter, srcCapsRequested, width, height); + + final GLCapabilitiesImmutable srcCapsChosen = gladSource.getChosenGLCapabilities(); + + final GLCapabilities dstCaps = (GLCapabilities) srcCapsChosen.cloneMutable(); + dstCaps.setOnscreen(dstOnscreen); + + final boolean isSwapGLContextSafe = GLDrawableUtil.isSwapGLContextSafe(srcCapsRequested, srcCapsChosen, dstCaps); + System.err.println("Source Caps Requested: "+srcCapsRequested); + System.err.println("Source Caps Chosen : "+srcCapsChosen); + System.err.println("Dest Caps Requested: "+dstCaps); + System.err.println("Is SwapGLContext safe: "+isSwapGLContextSafe); + + if( !isSwapGLContextSafe && !testEvenUnsafeSwapGLContext ) { + System.err.println("Supressing unsafe tests ..."); + destroyGLAutoDrawable(gladSource); + return; + } + + final SnapshotGLEventListener snapshotGLEventListener = new SnapshotGLEventListener(); + final GearsES2 gears = new GearsES2(1); + gears.setVerbose(false); + gladSource.addGLEventListener(gears); + gladSource.addGLEventListener(snapshotGLEventListener); + snapshotGLEventListener.setMakeSnapshot(); + + final Animator animator = new Animator(); + animator.add(gladSource); + animator.start(); + + int s = 0; + final long t0 = System.currentTimeMillis(); + long t1 = t0; + + final GLAutoDrawable gladDest = createGLAutoDrawable(quitAdapter, dstCaps, width, height); + RuntimeException caught = null; + try { + while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration ) { + if( ( t1 - t0 ) / period > s) { + s++; + System.err.println(s+" - switch - START "+ ( t1 - t0 )); + + final Runnable switchAction = new Runnable() { + public void run() { + GLDrawableUtil.swapGLContextAndAllGLEventListener(gladSource, gladDest); + } }; + + // switch context _and_ the demo synchronously + if( gladSource.isThreadGLCapable() && gladDest.isThreadGLCapable() ) { + switchAction.run(); + } else { + Threading.invokeOnOpenGLThread(true, switchAction); + } + snapshotGLEventListener.setMakeSnapshot(); + + System.err.println(s+" - switch - END "+ ( t1 - t0 )); + } + Thread.sleep(100); + t1 = System.currentTimeMillis(); + } + } catch (final RuntimeException t) { + caught = t; + } + + animator.stop(); + destroyGLAutoDrawable(gladDest); + destroyGLAutoDrawable(gladSource); + + if( null != caught ) { + throw caught; + } + } + + // default timing for 2 switches + static long duration = 2900; // ms + static long period = 1000; // ms +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/GLContextDrawableSwitchBase.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/GLContextDrawableSwitchBase1.java index b19dc38f2..0e7ea3e83 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/GLContextDrawableSwitchBase.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/GLContextDrawableSwitchBase1.java @@ -68,7 +68,7 @@ import org.junit.BeforeClass; * See Bug 665 - https://jogamp.org/bugzilla/show_bug.cgi?id=665. * </p> */ -public abstract class GLContextDrawableSwitchBase extends UITestCase { +public abstract class GLContextDrawableSwitchBase1 extends UITestCase { static protected enum GLADType { GLCanvasOnscreen, GLCanvasOffscreen, GLWindow, GLOffscreen }; // default period for 1 GLAD cycle diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestBug722GLContextDrawableSwitchNewt2AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestBug722GLContextDrawableSwitchNewt2AWT.java index 512ebc974..e961fe32d 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestBug722GLContextDrawableSwitchNewt2AWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestBug722GLContextDrawableSwitchNewt2AWT.java @@ -54,7 +54,7 @@ import org.junit.runners.MethodSorters; * </p> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestBug722GLContextDrawableSwitchNewt2AWT extends GLContextDrawableSwitchBase { +public class TestBug722GLContextDrawableSwitchNewt2AWT extends GLContextDrawableSwitchBase1 { static int loops = 10; static long duration2 = 100; // ms diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch02AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch02AWT.java index d665c1a87..d9f2d0e18 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch02AWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch02AWT.java @@ -29,6 +29,7 @@ package com.jogamp.opengl.test.junit.jogl.acore.glels; import java.awt.BorderLayout; +import java.awt.Component; import java.awt.Dimension; import java.awt.Frame; import java.io.IOException; @@ -38,20 +39,15 @@ import com.jogamp.newt.event.TraceWindowAdapter; import com.jogamp.newt.event.awt.AWTWindowAdapter; import javax.media.opengl.GLAutoDrawable; -import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLDrawableFactory; -import javax.media.opengl.GLProfile; import javax.media.opengl.awt.GLCanvas; -import com.jogamp.opengl.util.Animator; -import com.jogamp.opengl.util.GLDrawableUtil; -import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import jogamp.nativewindow.awt.AWTMisc; + import com.jogamp.opengl.test.junit.util.QuitAdapter; -import com.jogamp.opengl.test.junit.util.UITestCase; import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; @@ -60,26 +56,15 @@ import org.junit.runners.MethodSorters; * from GLCanvas to an GLOffscreenAutoDrawable and back. */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestGLContextDrawableSwitch02AWT extends UITestCase { - static int width, height; - - static GLCapabilities getCaps(final String profile) { - if( !GLProfile.isAvailable(profile) ) { - System.err.println("Profile "+profile+" n/a"); - return null; - } - return new GLCapabilities(GLProfile.get(profile)); - } +public class TestGLContextDrawableSwitch02AWT extends GLContextDrawableSwitchBase0 { - @BeforeClass - public static void initClass() { - width = 256; - height = 256; - } - - private GLAutoDrawable createGLAutoDrawable(final Frame frame, final GLCapabilities caps, final int width, final int height) throws InterruptedException, InvocationTargetException { + @Override + public GLAutoDrawable createGLAutoDrawable(final QuitAdapter quitAdapter, final GLCapabilitiesImmutable caps, final int width, final int height) throws InterruptedException, InvocationTargetException { final GLAutoDrawable glad; if( caps.isOnscreen() ) { + final Frame frame = new Frame("Gears AWT Test"); + Assert.assertNotNull(frame); + final GLCanvas glCanvas = new GLCanvas(caps); Assert.assertNotNull(glCanvas); final Dimension glc_sz = new Dimension(width, height); @@ -88,6 +73,8 @@ public class TestGLContextDrawableSwitch02AWT extends UITestCase { glCanvas.setSize(glc_sz); glad = glCanvas; + new AWTWindowAdapter(new TraceWindowAdapter(quitAdapter), glCanvas).addTo(frame); + frame.setLayout(new BorderLayout()); frame.add(glCanvas, BorderLayout.CENTER); javax.swing.SwingUtilities.invokeAndWait(new Runnable() { @@ -95,7 +82,6 @@ public class TestGLContextDrawableSwitch02AWT extends UITestCase { frame.pack(); frame.setVisible(true); }}); - } else { final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); glad = factory.createOffscreenAutoDrawable(null, caps, null, width, height); @@ -104,70 +90,20 @@ public class TestGLContextDrawableSwitch02AWT extends UITestCase { return glad; } - @Test(timeout=30000) - public void testSwitch2AWTGLCanvas2OffscreenGL2ES2() throws InterruptedException, InvocationTargetException { - final GLCapabilities reqGLCaps = getCaps(GLProfile.GL2ES2); - if(null == reqGLCaps) return; - testSwitch2AWTGLCanvas2OffscreenImpl(reqGLCaps); - } - - private void testSwitch2AWTGLCanvas2OffscreenImpl(final GLCapabilities capsOnscreen) throws InterruptedException, InvocationTargetException { - final GLCapabilities capsOffscreen = (GLCapabilities) capsOnscreen.clone(); - capsOffscreen.setOnscreen(false); - - final Frame frame = new Frame("Gears AWT Test"); - Assert.assertNotNull(frame); - - final GLAutoDrawable glCanvas = createGLAutoDrawable(frame, capsOnscreen, width, height); - - final QuitAdapter quitAdapter = new QuitAdapter(); - new AWTWindowAdapter(new TraceWindowAdapter(quitAdapter), glCanvas).addTo(frame); - - final SnapshotGLEventListener snapshotGLEventListener = new SnapshotGLEventListener(); - final GearsES2 gears = new GearsES2(1); - glCanvas.addGLEventListener(gears); - glCanvas.addGLEventListener(snapshotGLEventListener); - snapshotGLEventListener.setMakeSnapshot(); - - final Animator animator = new Animator(); - animator.add(glCanvas); - animator.start(); - - int s = 0; - final long t0 = System.currentTimeMillis(); - long t1 = t0; - - final GLAutoDrawable glOffscreen = createGLAutoDrawable(null, capsOffscreen, width, height); - while( !quitAdapter.shouldQuit() && ( t1 - t0 ) < duration ) { - if( ( t1 - t0 ) / period > s) { - s++; - System.err.println(s+" - switch - START "+ ( t1 - t0 )); - - // switch context _and_ the demo synchronously - GLDrawableUtil.swapGLContextAndAllGLEventListener(glCanvas, glOffscreen); - snapshotGLEventListener.setMakeSnapshot(); - - System.err.println(s+" - switch - END "+ ( t1 - t0 )); - } - Thread.sleep(100); - t1 = System.currentTimeMillis(); + @Override + public void destroyGLAutoDrawable(final GLAutoDrawable glad) throws InterruptedException, InvocationTargetException { + if( glad.getChosenGLCapabilities().isOnscreen() ) { + final Frame frame = AWTMisc.getFrame((Component)glad); + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + final Frame _frame = frame; + _frame.dispose(); + }}); + } else { + glad.destroy(); } - - animator.stop(); - // glCanvas.destroy(); - glOffscreen.destroy(); - - javax.swing.SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - final Frame _frame = frame; - _frame.dispose(); - }}); } - // default timing for 2 switches - static long duration = 2900; // ms - static long period = 1000; // ms - public static void main(final String args[]) throws IOException { for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { @@ -180,6 +116,8 @@ public class TestGLContextDrawableSwitch02AWT extends UITestCase { try { period = Integer.parseInt(args[i]); } catch (final Exception ex) { ex.printStackTrace(); } + } else if(args[i].equals("-testUnsafe")) { + testEvenUnsafeSwapGLContext = true; } } /** diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch02NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch02NEWT.java new file mode 100644 index 000000000..c8a78d05f --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch02NEWT.java @@ -0,0 +1,100 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.jogl.acore.glels; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +import com.jogamp.newt.opengl.GLWindow; + +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLDrawableFactory; + +import com.jogamp.opengl.test.junit.util.QuitAdapter; + +import org.junit.Assert; +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +/** + * Test re-association (switching) of GLWindow /GLDrawables, + * from GLWindow/GLOffscreenAutoDrawable to an GLOffscreenAutoDrawable and back. + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestGLContextDrawableSwitch02NEWT extends GLContextDrawableSwitchBase0 { + @Override + public GLAutoDrawable createGLAutoDrawable(final QuitAdapter quitAdapter, final GLCapabilitiesImmutable caps, final int width, final int height) throws InterruptedException, InvocationTargetException { + final GLAutoDrawable glad; + if( caps.isOnscreen() ) { + final GLWindow glWindow = GLWindow.create(caps); + Assert.assertNotNull(glWindow); + glad = glWindow; + + if( null != quitAdapter ) { + glWindow.addWindowListener(quitAdapter); + } + + glWindow.setVisible(true); + } else { + final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); + glad = factory.createOffscreenAutoDrawable(null, caps, null, width, height); + Assert.assertNotNull(glad); + } + return glad; + } + + @Override + public void destroyGLAutoDrawable(final GLAutoDrawable glad) throws InterruptedException, InvocationTargetException { + glad.destroy(); + } + + public static void main(final String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + try { + duration = Integer.parseInt(args[i]); + } catch (final Exception ex) { ex.printStackTrace(); } + } else if(args[i].equals("-period")) { + i++; + try { + period = Integer.parseInt(args[i]); + } catch (final Exception ex) { ex.printStackTrace(); } + } else if(args[i].equals("-testUnsafe")) { + testEvenUnsafeSwapGLContext = true; + } + } + /** + BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); + System.err.println("Press enter to continue"); + System.err.println(stdin.readLine()); */ + org.junit.runner.JUnitCore.main(TestGLContextDrawableSwitch02NEWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch11NewtAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch11NewtAWT.java index 9697869bc..f1c08c889 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch11NewtAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch11NewtAWT.java @@ -60,7 +60,7 @@ import org.junit.runners.MethodSorters; * </p> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestGLContextDrawableSwitch11NewtAWT extends GLContextDrawableSwitchBase { +public class TestGLContextDrawableSwitch11NewtAWT extends GLContextDrawableSwitchBase1 { @Test(timeout=30000) public void test21GLWindowGL2ES2() throws InterruptedException { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch12AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch12AWT.java index 4ad0b1dc5..795e53768 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch12AWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch12AWT.java @@ -61,7 +61,7 @@ import org.junit.runners.MethodSorters; * </p> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestGLContextDrawableSwitch12AWT extends GLContextDrawableSwitchBase { +public class TestGLContextDrawableSwitch12AWT extends GLContextDrawableSwitchBase1 { @Test(timeout=30000) public void test01GLCanvasOnscreenGL2ES2() throws InterruptedException { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch21Newt2AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch13Newt2AWT.java index eaf3d37d4..27d5ff80a 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch21Newt2AWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/glels/TestGLContextDrawableSwitch13Newt2AWT.java @@ -73,7 +73,7 @@ import org.junit.runners.MethodSorters; * </p> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class TestGLContextDrawableSwitch21Newt2AWT extends GLContextDrawableSwitchBase { +public class TestGLContextDrawableSwitch13Newt2AWT extends GLContextDrawableSwitchBase1 { @Test(timeout=30000) public void test01GLCanvasOnScrn2GLWindowGL2ES2() throws InterruptedException { @@ -190,6 +190,6 @@ public class TestGLContextDrawableSwitch21Newt2AWT extends GLContextDrawableSwit BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); System.err.println("Press enter to continue"); System.err.println(stdin.readLine()); */ - org.junit.runner.JUnitCore.main(TestGLContextDrawableSwitch21Newt2AWT.class.getName()); + org.junit.runner.JUnitCore.main(TestGLContextDrawableSwitch13Newt2AWT.class.getName()); } } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/caps/TestTranslucencyNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/caps/TestTranslucencyNEWT.java index 8f23b57a0..10ac9417a 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/caps/TestTranslucencyNEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/caps/TestTranslucencyNEWT.java @@ -31,11 +31,10 @@ package com.jogamp.opengl.test.junit.jogl.caps; import com.jogamp.newt.event.KeyAdapter; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.test.junit.util.MiscUtils; import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.test.junit.util.QuitAdapter; - import com.jogamp.opengl.util.Animator; - import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import javax.media.opengl.GLCapabilities; @@ -147,14 +146,19 @@ public class TestTranslucencyNEWT extends UITestCase { static long duration = 500; // ms public static void main(final String args[]) { + boolean waitForKey = false; + for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { i++; - try { - duration = Integer.parseInt(args[i]); - } catch (final Exception ex) { ex.printStackTrace(); } + duration = MiscUtils.atol(args[i], duration); + } else if(args[i].equals("-wait")) { + waitForKey = true; } } + if( waitForKey ) { + UITestCase.waitForKey("main"); + } org.junit.runner.JUnitCore.main(TestTranslucencyNEWT.class.getName()); } } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/GearsES1.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/GearsES1.java index b1df926dc..3fdafdfd8 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/GearsES1.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/GearsES1.java @@ -248,7 +248,7 @@ public class GearsES1 implements GLEventListener { public void display(final GLAutoDrawable drawable) { // Turn the gears' teeth - angle += 2.0f; + angle += 0.5f; // Get the GL corresponding to the drawable we are animating final GL2ES1 gl = drawable.getGL().getGL2ES1(); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/FBOMix2DemosES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/FBOMix2DemosES2.java index f00053989..42f4c5f6e 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/FBOMix2DemosES2.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/FBOMix2DemosES2.java @@ -49,7 +49,7 @@ public class FBOMix2DemosES2 implements GLEventListener { private final GearsES2 demo0; private final RedSquareES2 demo1; private final int swapInterval; - private int numSamples; + private volatile int numSamples; private boolean demo0Only; @@ -156,13 +156,8 @@ public class FBOMix2DemosES2 implements GLEventListener { } private void initFBOs(final GL gl, final GLAutoDrawable drawable) { - // remove all texture attachments, since MSAA uses just color-render-buffer - // and non-MSAA uses texture2d-buffer - fbo0.detachAllColorbuffer(gl); - fbo1.detachAllColorbuffer(gl); - - fbo0.reset(gl, drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), numSamples, false); - fbo1.reset(gl, drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), numSamples, false); + fbo0.init(gl, drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), numSamples); + fbo1.init(gl, drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), numSamples); if(fbo0.getNumSamples() != fbo1.getNumSamples()) { throw new InternalError("sample size mismatch: \n\t0: "+fbo0+"\n\t1: "+fbo1); } @@ -173,32 +168,32 @@ public class FBOMix2DemosES2 implements GLEventListener { fbo0.resetSamplingSink(gl); fbo1.attachColorbuffer(gl, 0, true); fbo1.resetSamplingSink(gl); - fbo0Tex = fbo0.getSamplingSink(); - fbo1Tex = fbo1.getSamplingSink(); + fbo0Tex = fbo0.getSamplingSink().getTextureAttachment(); + fbo1Tex = fbo1.getSamplingSink().getTextureAttachment(); } else { fbo0Tex = fbo0.attachTexture2D(gl, 0, true); fbo1Tex = fbo1.attachTexture2D(gl, 0, true); } numSamples=fbo0.getNumSamples(); - fbo0.attachRenderbuffer(gl, Type.DEPTH, 24); + fbo0.attachRenderbuffer(gl, Type.DEPTH, FBObject.CHOSEN_BITS); fbo0.unbind(gl); - fbo1.attachRenderbuffer(gl, Type.DEPTH, 24); + fbo1.attachRenderbuffer(gl, Type.DEPTH, FBObject.CHOSEN_BITS); fbo1.unbind(gl); } private void resetFBOs(final GL gl, final GLAutoDrawable drawable) { - fbo0.reset(gl, drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), numSamples, true); - fbo1.reset(gl, drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), numSamples, true); + fbo0.reset(gl, drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), numSamples); + fbo1.reset(gl, drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), numSamples); if(fbo0.getNumSamples() != fbo1.getNumSamples()) { throw new InternalError("sample size mismatch: \n\t0: "+fbo0+"\n\t1: "+fbo1); } numSamples = fbo0.getNumSamples(); if(numSamples>0) { - fbo0Tex = fbo0.getSamplingSink(); - fbo1Tex = fbo1.getSamplingSink(); + fbo0Tex = fbo0.getSamplingSink().getTextureAttachment(); + fbo1Tex = fbo1.getSamplingSink().getTextureAttachment(); } else { - fbo0Tex = (TextureAttachment) fbo0.getColorbuffer(0); - fbo1Tex = (TextureAttachment) fbo1.getColorbuffer(0); + fbo0Tex = fbo0.getColorbuffer(0).getTextureAttachment(); + fbo1Tex = fbo1.getColorbuffer(0).getTextureAttachment(); } } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java index 84cd8936e..b3da5a53a 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java @@ -352,9 +352,15 @@ public class GearsES2 implements StereoGLEventListener, TileRendererBase.TileRen reshapeImpl(gl, tileX, tileY, tileWidth, tileHeight, imageWidth, imageHeight); } - private final float zNear = 2f; - private final float zFar = 10000f; - private final float zViewDist = 20.0f; + private float zNear = 5f; + private float zFar = 10000f; + private float zViewDist = 40.0f; + + public void setZ(final float zNear, final float zFar, final float zViewDist) { + this.zNear = zNear; + this.zFar = zFar; + this.zViewDist = zViewDist; + } void reshapeImpl(final GL2ES2 gl, final int tileX, final int tileY, final int tileWidth, final int tileHeight, final int imageWidth, final int imageHeight) { final boolean msaa = gl.getContext().getGLDrawable().getChosenGLCapabilities().getSampleBuffers(); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/MultisampleDemoES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/MultisampleDemoES2.java index 7c4c5106b..8e4dbfefe 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/MultisampleDemoES2.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/MultisampleDemoES2.java @@ -55,7 +55,7 @@ import com.jogamp.opengl.util.glsl.ShaderState; public class MultisampleDemoES2 implements GLEventListener { - private boolean multisample; + private boolean multisample, clearBuffers; private final ShaderState st; private final PMVMatrix pmvMatrix; private ShaderProgram sp0; @@ -64,11 +64,14 @@ public class MultisampleDemoES2 implements GLEventListener { public MultisampleDemoES2(final boolean multisample) { this.multisample = multisample; + this.clearBuffers = true; st = new ShaderState(); st.setVerbose(true); pmvMatrix = new PMVMatrix(); } + public void setClearBuffers(final boolean v) { clearBuffers = v; } + public void init(final GLAutoDrawable glad) { final GL2ES2 gl = glad.getGL().getGL2ES2(); @@ -135,10 +138,12 @@ public class MultisampleDemoES2 implements GLEventListener { if (multisample) { gl.glEnable(GL.GL_MULTISAMPLE); } - gl.glClearColor(0, 0, 0, 0); - // gl.glEnable(GL.GL_DEPTH_TEST); - // gl.glDepthFunc(GL.GL_LESS); - gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + if( clearBuffers ) { + gl.glClearColor(0, 0, 0, 0); + // gl.glEnable(GL.GL_DEPTH_TEST); + // gl.glDepthFunc(GL.GL_LESS); + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + } st.useProgram(gl, true); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureDraw02ES2ListenerFBO.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureDraw02ES2ListenerFBO.java index 18cb35929..27395ea45 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureDraw02ES2ListenerFBO.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/TextureDraw02ES2ListenerFBO.java @@ -138,36 +138,34 @@ public class TextureDraw02ES2ListenerFBO implements GLEventListener { st.useProgram(gl, false); + initFBOs(gl, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + gl.glEnable(GL.GL_DEPTH_TEST); } private void initFBOs(final GL gl, final int width, final int height) { - // remove all texture attachments, since MSAA uses just color-render-buffer - // and non-MSAA uses texture2d-buffer - fbo0.detachAllColorbuffer(gl); - - fbo0.reset(gl, width, height, numSamples, false); + fbo0.init(gl, width, height, numSamples); numSamples = fbo0.getNumSamples(); if(numSamples>0) { fbo0.attachColorbuffer(gl, 0, true); fbo0.resetSamplingSink(gl); - fbo0Tex = fbo0.getSamplingSink(); + fbo0Tex = fbo0.getSamplingSink().getTextureAttachment(); } else { fbo0Tex = fbo0.attachTexture2D(gl, 0, true); } numSamples=fbo0.getNumSamples(); - fbo0.attachRenderbuffer(gl, Type.DEPTH, 24); + fbo0.attachRenderbuffer(gl, Type.DEPTH, FBObject.CHOSEN_BITS); fbo0.unbind(gl); } private void resetFBOs(final GL gl, final int width, final int height) { - fbo0.reset(gl, width, height, numSamples, true); + fbo0.reset(gl, width, height, numSamples); numSamples = fbo0.getNumSamples(); if(numSamples>0) { - fbo0Tex = fbo0.getSamplingSink(); + fbo0Tex = fbo0.getSamplingSink().getTextureAttachment(); } else { - fbo0Tex = (TextureAttachment) fbo0.getColorbuffer(0); + fbo0Tex = fbo0.getColorbuffer(0).getTextureAttachment(); } } @@ -236,16 +234,11 @@ public class TextureDraw02ES2ListenerFBO implements GLEventListener { gl.setSwapInterval(swapInterval); // in case switching the drawable (impl. may bound attribute there) } - if( !fbo0.isInitialized() ) { - System.err.println("**** Reshape.Init: "+width+"x"+height); - initFBOs(gl, width, height); - } else { - System.err.println("**** Reshape.Reset: "+width+"x"+height); - if( keepTextureBound ) { - fbo0.unuse(gl); - } - resetFBOs(gl, width, height); + System.err.println("**** Reshape.Reset: "+width+"x"+height); + if( keepTextureBound ) { + fbo0.unuse(gl); } + resetFBOs(gl, width, height); fbo0.bind(gl); demo.reshape(drawable, x, y, width, height); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/CrossFadePlayer.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/CrossFadePlayer.java index e5e752d96..b2ebc7068 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/CrossFadePlayer.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/CrossFadePlayer.java @@ -28,6 +28,7 @@ package com.jogamp.opengl.test.junit.jogl.demos.es2.av;
+import com.jogamp.common.net.Uri;
import com.jogamp.opengl.util.av.AudioSink;
import com.jogamp.opengl.util.av.GLMediaPlayer;
import com.jogamp.opengl.util.av.GLMediaPlayer.GLMediaEventListener;
@@ -36,7 +37,6 @@ import com.jogamp.opengl.util.av.GLMediaPlayerFactory; import com.jogamp.opengl.util.texture.TextureSequence.TextureFrame;
import java.io.File;
-import java.net.URI;
/**
* Parallel media player that demonstrate CrossFade of audio volume during playback.
@@ -141,7 +141,7 @@ public class CrossFadePlayer if(!file.exists()){
System.out.println("File do not exist");
} else {
- final URI uri = file.toURI();
+ final Uri uri = Uri.valueOf(file);
System.out.println("State of player "+ i +": " + player[i].getState().toString());
System.out.println("...initializing stream "+ i +"...");
player[i].initStream(uri, GLMediaPlayer.STREAM_ID_NONE, GLMediaPlayer.STREAM_ID_AUTO, GLMediaPlayer.TEXTURE_COUNT_DEFAULT);
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieCube.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieCube.java index 607bfd1b0..428eea42c 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieCube.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieCube.java @@ -30,7 +30,6 @@ package com.jogamp.opengl.test.junit.jogl.demos.es2.av; import java.io.File; import java.io.IOException; -import java.net.URI; import java.net.URISyntaxException; import javax.media.opengl.GL; @@ -42,7 +41,7 @@ import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; -import com.jogamp.common.util.IOUtil; +import com.jogamp.common.net.Uri; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.GLRegion; import com.jogamp.graph.curve.opengl.RegionRenderer; @@ -81,13 +80,13 @@ public class MovieCube implements GLEventListener { private volatile boolean resetGLState = false; /** Blender's Big Buck Bunny: 24f 416p H.264, AAC 48000 Hz, 2 ch, mpeg stream. */ - public static final URI defURI; + public static final Uri defURI; static { - URI _defURI = null; + Uri _defURI = null; try { // Blender's Big Buck Bunny Trailer: 24f 640p VP8, Vorbis 44100Hz mono, WebM/Matroska Stream. // _defURI = new URI("http://video.webmfiles.org/big-buck-bunny_trailer.webm"); - _defURI = new URI("http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"); + _defURI = Uri.cast("http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"); } catch (final URISyntaxException e) { e.printStackTrace(); } @@ -151,7 +150,7 @@ public class MovieCube implements GLEventListener { mPlayer = GLMediaPlayerFactory.createDefault(); } - public void initStream(final URI streamLoc, final int vid, final int aid, final int textureCount) { + public void initStream(final Uri streamLoc, final int vid, final int aid, final int textureCount) { mPlayer.initStream(streamLoc, vid, aid, textureCount); System.out.println("pC.1b "+mPlayer); } @@ -252,7 +251,7 @@ public class MovieCube implements GLEventListener { mPlayer.getAID(), mPlayer.getAudioBitrate()/1000, mPlayer.getAudioCodec()); final String text3 = String.format("video: id %d, kbps %d, codec %s", mPlayer.getVID(), mPlayer.getVideoBitrate()/1000, mPlayer.getVideoCodec()); - final String text4 = mPlayer.getURI().getRawPath(); + final String text4 = mPlayer.getUri().path.decode(); if( displayOSD && null != renderer ) { gl.glClearColor(1.0f, 1.0f, 1.0f, 0.0f); if( null != regionFPS ) { @@ -517,11 +516,11 @@ public class MovieCube implements GLEventListener { } origSize = _origSize; } - final URI streamLoc; + final Uri streamLoc; if( null != url_s ) { - streamLoc = new URI(url_s); + streamLoc = Uri.cast( url_s ); } else if( null != file_s ) { - streamLoc = IOUtil.toURISimple(new File(file_s)); + streamLoc = Uri.valueOf(new File(file_s)); } else { streamLoc = defURI; } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSBSStereo.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSBSStereo.java index 0c7bdc81d..7a3a1ffc7 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSBSStereo.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSBSStereo.java @@ -28,7 +28,6 @@ package com.jogamp.opengl.test.junit.jogl.demos.es2.av; -import java.net.URI; import java.net.URISyntaxException; import java.nio.FloatBuffer; @@ -41,6 +40,7 @@ import javax.media.opengl.GLException; import javax.media.opengl.GLUniformData; import javax.media.opengl.fixedfunc.GLMatrixFunc; +import com.jogamp.common.net.Uri; import com.jogamp.common.os.Platform; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.GLRegion; @@ -117,13 +117,13 @@ public class MovieSBSStereo implements StereoGLEventListener { private static final String myTextureLookupName = "myTexture2D"; /** Blender's Big Buck Bunny: 24f 416p H.264, AAC 48000 Hz, 2 ch, mpeg stream. */ - public static final URI defURI; + public static final Uri defURI; static { - URI _defURI = null; + Uri _defURI = null; try { // Blender's Big Buck Bunny Trailer: 24f 640p VP8, Vorbis 44100Hz mono, WebM/Matroska Stream. // _defURI = new URI("http://video.webmfiles.org/big-buck-bunny_trailer.webm"); - _defURI = new URI("http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"); + _defURI = Uri.cast("http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"); } catch (final URISyntaxException e) { e.printStackTrace(); } @@ -189,7 +189,7 @@ public class MovieSBSStereo implements StereoGLEventListener { mPlayer.getAID(), mPlayer.getAudioBitrate()/1000, mPlayer.getAudioCodec()); final String text3 = String.format("video: id %d, kbps %d, codec %s", mPlayer.getVID(), mPlayer.getVideoBitrate()/1000, mPlayer.getVideoCodec()); - final String text4 = mPlayer.getURI().getRawPath(); + final String text4 = mPlayer.getUri().path.decode(); if( displayOSD && null != renderer ) { // We share ClearColor w/ MovieSimple's init ! final float pixelSize = font.getPixelSize(fontSize, dpiH); @@ -203,6 +203,7 @@ public class MovieSBSStereo implements StereoGLEventListener { renderString(drawable, font, pixelSize, text4, 1 /* col */, -2 /* row */, 0, height, 1, true); } } }; + private final boolean enableTextRendererGLEL = false; private InfoTextRendererGLELBase textRendererGLEL = null; private boolean displayOSD = false; @@ -326,7 +327,7 @@ public class MovieSBSStereo implements StereoGLEventListener { System.out.println("pC.1a "+mPlayer); } - public void initStream(final URI streamLoc, final int vid, final int aid, final int textureCount) { + public void initStream(final Uri streamLoc, final int vid, final int aid, final int textureCount) { mPlayer.initStream(streamLoc, vid, aid, textureCount); System.out.println("pC.1b "+mPlayer); } @@ -534,8 +535,12 @@ public class MovieSBSStereo implements StereoGLEventListener { } final int rmode = drawable.getChosenGLCapabilities().getSampleBuffers() ? 0 : Region.VBAA_RENDERING_BIT; final boolean lowPerfDevice = gl.isGLES(); - textRendererGLEL = new InfoTextRendererGLELBase(rmode, lowPerfDevice); - textRendererGLEL.init(drawable); + if( enableTextRendererGLEL ) { + textRendererGLEL = new InfoTextRendererGLELBase(rmode, lowPerfDevice); + textRendererGLEL.init(drawable); + } else { + textRendererGLEL = null; + } } protected void updateInterleavedVBO(final GL gl, final GLArrayDataServer iVBO, final Texture tex, final int eyeNum) { @@ -608,7 +613,9 @@ public class MovieSBSStereo implements StereoGLEventListener { } System.out.println("pR "+mPlayer); - textRendererGLEL.reshape(drawable, x, y, width, height); + if( null != textRendererGLEL ) { + textRendererGLEL.reshape(drawable, 0, 0, width, height); + } } private final float zNear = 0.1f; @@ -666,13 +673,17 @@ public class MovieSBSStereo implements StereoGLEventListener { st.useProgram(gl, true); st.uniform(gl, pmvMatrixUniform); st.useProgram(gl, false); - textRendererGLEL.reshape(drawable, x, y, width, height); + if( null != textRendererGLEL ) { + textRendererGLEL.reshape(drawable, 0, 0, width, height); + } } @Override public void dispose(final GLAutoDrawable drawable) { - textRendererGLEL.dispose(drawable); - textRendererGLEL = null; + if( null != textRendererGLEL ) { + textRendererGLEL.dispose(drawable); + textRendererGLEL = null; + } disposeImpl(drawable, true); } @@ -747,7 +758,7 @@ public class MovieSBSStereo implements StereoGLEventListener { pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); pmvMatrix.glPushMatrix(); pmvMatrix.glTranslatef(0, 0, zoom); - if(rotate > 0) { + if( rotate > 0) { final float ang = ((System.currentTimeMillis() - startTime) * 360.0f) / 8000.0f; pmvMatrix.glRotatef(ang, 0, 0, 1); } else { @@ -778,7 +789,9 @@ public class MovieSBSStereo implements StereoGLEventListener { st.useProgram(gl, false); pmvMatrix.glPopMatrix(); - textRendererGLEL.display(drawable); + if( null != textRendererGLEL ) { + textRendererGLEL.display(drawable); + } } static class StereoGLMediaEventListener implements GLMediaEventListener { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java index 0d1191528..d963294b4 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/av/MovieSimple.java @@ -30,7 +30,6 @@ package com.jogamp.opengl.test.junit.jogl.demos.es2.av; import java.io.File; import java.io.IOException; -import java.net.URI; import java.net.URISyntaxException; import java.nio.FloatBuffer; @@ -46,8 +45,8 @@ import javax.media.opengl.GLProfile; import javax.media.opengl.GLUniformData; import javax.media.opengl.fixedfunc.GLMatrixFunc; +import com.jogamp.common.net.Uri; import com.jogamp.common.os.Platform; -import com.jogamp.common.util.IOUtil; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.GLRegion; import com.jogamp.graph.curve.opengl.RegionRenderer; @@ -122,13 +121,13 @@ public class MovieSimple implements GLEventListener { private static final String myTextureLookupName = "myTexture2D"; /** Blender's Big Buck Bunny: 24f 416p H.264, AAC 48000 Hz, 2 ch, mpeg stream. */ - public static final URI defURI; + public static final Uri defURI; static { - URI _defURI = null; + Uri _defURI = null; try { // Blender's Big Buck Bunny Trailer: 24f 640p VP8, Vorbis 44100Hz mono, WebM/Matroska Stream. // _defURI = new URI("http://video.webmfiles.org/big-buck-bunny_trailer.webm"); - _defURI = new URI("http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"); + _defURI = Uri.cast("http://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"); } catch (final URISyntaxException e) { e.printStackTrace(); } @@ -194,7 +193,7 @@ public class MovieSimple implements GLEventListener { mPlayer.getAID(), mPlayer.getAudioBitrate()/1000, mPlayer.getAudioCodec()); final String text3 = String.format("video: id %d, kbps %d, codec %s", mPlayer.getVID(), mPlayer.getVideoBitrate()/1000, mPlayer.getVideoCodec()); - final String text4 = mPlayer.getURI().getRawPath(); + final String text4 = mPlayer.getUri().path.decode(); if( displayOSD && null != renderer ) { // We share ClearColor w/ MovieSimple's init ! final float pixelSize = font.getPixelSize(fontSize, dpiH); @@ -388,7 +387,7 @@ public class MovieSimple implements GLEventListener { System.out.println("pC.1a shared "+mPlayerShared+", "+mPlayer); } - public void initStream(final URI streamLoc, final int vid, final int aid, final int textureCount) { + public void initStream(final Uri streamLoc, final int vid, final int aid, final int textureCount) { mPlayer.initStream(streamLoc, vid, aid, textureCount); System.out.println("pC.1b "+mPlayer); } @@ -988,14 +987,14 @@ public class MovieSimple implements GLEventListener { } origSize = _origSize; } - final URI streamLoc0; + final Uri streamLoc0; if( null != urls_s[0] ) { - streamLoc0 = new URI(urls_s[0]); + streamLoc0 = Uri.cast( urls_s[0] ); } else if( null != file_s1 ) { final File movieFile = new File(file_s1); - streamLoc0 = movieFile.toURI(); + streamLoc0 = Uri.valueOf(movieFile); } else if( null != file_s2 ) { - streamLoc0 = IOUtil.toURISimple(new File(file_s2)); + streamLoc0 = Uri.valueOf(new File(file_s2)); } else { streamLoc0 = defURI; } @@ -1049,12 +1048,12 @@ public class MovieSimple implements GLEventListener { windows[i].setVisible(true); anim.add(windows[i]); - final URI streamLocN; + final Uri streamLocN; if( 0 == i ) { streamLocN = streamLoc0; } else { if( null != urls_s[i] ) { - streamLocN = new URI(urls_s[i]); + streamLocN = Uri.cast(urls_s[i]); } else { streamLocN = defURI; } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2GLJPanelAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2GLJPanelAWT.java index dd16b88cc..ae11c9d54 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2GLJPanelAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/awt/TestGearsES2GLJPanelAWT.java @@ -36,8 +36,10 @@ import java.awt.event.ComponentListener; import java.lang.reflect.InvocationTargetException; import javax.media.nativewindow.ScalableSurface; +import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLEventListener; import javax.media.opengl.GLProfile; import javax.media.opengl.awt.GLJPanel; import javax.swing.JFrame; @@ -137,6 +139,19 @@ public class TestGearsES2GLJPanelAWT extends UITestCase { } final SnapshotGLEventListener snap = new SnapshotGLEventListener(); glJPanel.addGLEventListener(snap); + glJPanel.addGLEventListener(new GLEventListener() { + @Override + public void init(final GLAutoDrawable drawable) { } + @Override + public void dispose(final GLAutoDrawable drawable) { } + @Override + public void display(final GLAutoDrawable drawable) { } + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { + setTitle(frame, glJPanel, caps); + } + + }); setTitle(frame, glJPanel, caps); frame.setLocation(xpos, ypos); @@ -213,6 +228,25 @@ public class TestGearsES2GLJPanelAWT extends UITestCase { hasSurfacePixelScale1[0]+"x"+hasSurfacePixelScale1[1]+" (has)"); setTitle(frame, glJPanel, caps); Assert.assertArrayEquals(hasSurfacePixelScale0, hasSurfacePixelScale1); + } else if(e.getKeyChar()=='m') { + final GLCapabilitiesImmutable capsPre = glJPanel.getChosenGLCapabilities(); + final GLCapabilities capsNew = new GLCapabilities(capsPre.getGLProfile()); + capsNew.copyFrom(capsPre); + final boolean msaa; + if( capsPre.getSampleBuffers() ) { + capsNew.setSampleBuffers(false); + msaa = false; + } else { + capsNew.setSampleBuffers(true); + capsNew.setNumSamples(4); + msaa = true; + } + System.err.println("[set MSAA "+msaa+" Caps had]: "+capsPre); + System.err.println("[set MSAA "+msaa+" Caps new]: "+capsNew); + System.err.println("XXX-A1: "+animator.toString()); + glJPanel.setRequestedGLCapabilities(capsNew); + System.err.println("XXX-A2: "+animator.toString()); + System.err.println("XXX: "+glJPanel.toString()); } } }; new AWTKeyAdapter(kl, glJPanel).addTo(glJPanel); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java index cd5e20c40..29c5aac70 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/newt/TestGearsES2NEWT.java @@ -52,6 +52,7 @@ import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.test.junit.util.QuitAdapter; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.AnimatorBase; +import com.jogamp.opengl.util.Gamma; import com.jogamp.opengl.util.PNGPixelRect; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; @@ -241,6 +242,9 @@ public class TestGearsES2NEWT extends UITestCase { glWindow.addKeyListener(new KeyAdapter() { int pointerIconIdx = 0; + float gamma = 1f; + float brightness = 0f; + float contrast = 1f; @Override public void keyPressed(final KeyEvent e) { @@ -264,6 +268,15 @@ public class TestGearsES2NEWT extends UITestCase { System.err.println("[set fullscreen post]: "+glWindow.getX()+"/"+glWindow.getY()+" "+glWindow.getSurfaceWidth()+"x"+glWindow.getSurfaceHeight()+", f "+glWindow.isFullscreen()+", a "+glWindow.isAlwaysOnTop()+", "+glWindow.getInsets()); glWindow.setExclusiveContextThread(t); } }.start(); + } else if( e.getKeySymbol()== KeyEvent.VK_G ) { + new Thread() { + public void run() { + final float newGamma = gamma + ( e.isShiftDown() ? -0.1f : 0.1f ); + System.err.println("[set gamma]: "+gamma+" -> "+newGamma); + if( Gamma.setDisplayGamma(glWindow, newGamma, brightness, contrast) ) { + gamma = newGamma; + } + } }.start(); } else if(e.getKeyChar()=='a') { new Thread() { public void run() { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/Gears.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/Gears.java index dbc760d15..9f97b4a21 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/Gears.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/Gears.java @@ -117,6 +117,36 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList } } + boolean enableCullFace = false; + + private void enableStates(final GL gl, final boolean enable) { + final boolean msaa = gl.getContext().getGLDrawable().getChosenGLCapabilities().getSampleBuffers(); + if( enable ) { + if( enableCullFace ) { + gl.glEnable(GL.GL_CULL_FACE); + } + gl.glEnable(GLLightingFunc.GL_LIGHTING); + gl.glEnable(GLLightingFunc.GL_LIGHT0); + gl.glEnable(GL.GL_DEPTH_TEST); + gl.glDepthFunc(GL.GL_LESS); // default + gl.glEnable(GLLightingFunc.GL_NORMALIZE); + if( msaa ) { + gl.glEnable(GL.GL_MULTISAMPLE); + } + } else { + if( enableCullFace ) { + gl.glDisable(GL.GL_CULL_FACE); + } + gl.glDisable(GLLightingFunc.GL_LIGHTING); + gl.glDisable(GLLightingFunc.GL_LIGHT0); + gl.glDisable(GL.GL_DEPTH_TEST); + gl.glDisable(GLLightingFunc.GL_NORMALIZE); + if( msaa ) { + gl.glDisable(GL.GL_MULTISAMPLE); + } + } + } + public void init(final GL2 gl) { final float lightPos[] = { 5.0f, 5.0f, 10.0f, 0.0f }; final float red[] = { 0.8f, 0.1f, 0.0f, 0.7f }; @@ -134,11 +164,11 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList gl.glLightfv(GLLightingFunc.GL_LIGHT0, GLLightingFunc.GL_POSITION, lightPos, 0); if( ! ( flipVerticalInGLOrientation && gl.getContext().getGLDrawable().isGLOriented() ) ) { // Only possible if we do not flip the projection matrix - gl.glEnable(GL.GL_CULL_FACE); + enableCullFace = true; + } else { + enableCullFace = false; } - gl.glEnable(GLLightingFunc.GL_LIGHTING); - gl.glEnable(GLLightingFunc.GL_LIGHT0); - gl.glEnable(GL.GL_DEPTH_TEST); + enableStates(gl, true); /* make the gears */ if(0>=gear1) { @@ -174,7 +204,7 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList System.err.println("gear3 list reused: "+gear3); } - gl.glEnable(GLLightingFunc.GL_NORMALIZE); + enableStates(gl, false); } @Override @@ -196,12 +226,7 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList } public void reshape(final GL2 gl, final int tileX, final int tileY, final int tileWidth, final int tileHeight, final int imageWidth, final int imageHeight) { - final boolean msaa = gl.getContext().getGLDrawable().getChosenGLCapabilities().getSampleBuffers(); - System.err.println(Thread.currentThread()+" Gears.reshape "+tileX+"/"+tileY+" "+tileWidth+"x"+tileHeight+" of "+imageWidth+"x"+imageHeight+", swapInterval "+swapInterval+", drawable 0x"+Long.toHexString(gl.getContext().getGLDrawable().getHandle())+", msaa "+msaa+", tileRendererInUse "+tileRendererInUse); - - if( msaa ) { - gl.glEnable(GL.GL_MULTISAMPLE); - } + System.err.println(Thread.currentThread()+" Gears.reshape "+tileX+"/"+tileY+" "+tileWidth+"x"+tileHeight+" of "+imageWidth+"x"+imageHeight+", swapInterval "+swapInterval+", drawable 0x"+Long.toHexString(gl.getContext().getGLDrawable().getHandle())+", tileRendererInUse "+tileRendererInUse); // compute projection parameters 'normal' float left, right, bottom, top; @@ -244,10 +269,6 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); gl.glLoadIdentity(); gl.glTranslatef(0.0f, 0.0f, -40.0f); - - if( msaa ) { - gl.glDisable(GL.GL_MULTISAMPLE); - } } @Override @@ -268,11 +289,8 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList public void display(final GLAutoDrawable drawable) { // Get the GL corresponding to the drawable we are animating final GL2 gl = drawable.getGL().getGL2(); - final boolean msaa = gl.getContext().getGLDrawable().getChosenGLCapabilities().getSampleBuffers(); - if( msaa ) { - gl.glEnable(GL.GL_MULTISAMPLE); - } + enableStates(gl, true); if( null == tileRendererInUse ) { gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -291,15 +309,13 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); } displayImpl(gl); - if( msaa ) { - gl.glDisable(GL.GL_MULTISAMPLE); - } + + enableStates(gl, false); } + public void display(final GL2 gl) { - final boolean msaa = gl.getContext().getGLDrawable().getChosenGLCapabilities().getSampleBuffers(); - if( msaa ) { - gl.glEnable(GL.GL_MULTISAMPLE); - } + enableStates(gl, true); + if( null == tileRendererInUse ) { gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); } else { @@ -307,14 +323,14 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList } gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); displayImpl(gl); - if( msaa ) { - gl.glDisable(GL.GL_MULTISAMPLE); - } + + enableStates(gl, false); } + private void displayImpl(final GL2 gl) { if( doRotate ) { // Turn the gears' teeth - angle += 2.0f; + angle += 0.5f; } // Rotate the entire assembly of gears based on how the user // dragged the mouse around @@ -451,7 +467,7 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList gl.glVertex3f(r1 * (float)Math.cos(0), r1 * (float)Math.sin(0), -width * 0.5f); gl.glEnd(); - gl.glShadeModel(GLLightingFunc.GL_SMOOTH); + gl.glShadeModel(GLLightingFunc.GL_SMOOTH); // default /* draw inside radius cylinder */ gl.glBegin(GL2.GL_QUAD_STRIP); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/Teapot.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/Teapot.java index d29688b96..96d6fdba0 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/Teapot.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/Teapot.java @@ -24,10 +24,46 @@ public class Teapot implements GLEventListener { private GLUT glut; /* glTexGen stuff: */ - private final float sgenparams[] = { 1.0f, 1.0f, 1.0f, 0.0f }; + // private final float sgenparams[] = { 1.0f, 1.0f, 1.0f, 0.0f }; private Texture tex = null; + private void enableStates(final GL2 gl, final boolean enable) { + if( enable ) { + if( null != tex ) { + tex.bind(gl); + } + gl.glEnable(GL.GL_DEPTH_TEST); + gl.glDepthFunc(GL.GL_LESS); // default + // gl.glEnable(GL2.GL_TEXTURE_GEN_S); + // gl.glEnable(GL2.GL_TEXTURE_1D); + gl.glEnable(GL.GL_TEXTURE_2D); + gl.glEnable(GL.GL_CULL_FACE); + gl.glEnable(GLLightingFunc.GL_LIGHTING); + gl.glEnable(GLLightingFunc.GL_LIGHT0); + gl.glEnable(GL2.GL_AUTO_NORMAL); + gl.glEnable(GLLightingFunc.GL_NORMALIZE); + gl.glFrontFace(GL.GL_CW); + gl.glCullFace(GL.GL_BACK); // default + gl.glMaterialf(GL.GL_FRONT, GLLightingFunc.GL_SHININESS, 64.0f); + gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT); + } else { + if( null != tex ) { + gl.glBindTexture(tex.getTarget(), 0); + } + gl.glDisable(GL.GL_DEPTH_TEST); + // gl.glDisable(GL2.GL_TEXTURE_GEN_S); + // gl.glDisable(GL2.GL_TEXTURE_1D); + gl.glDisable(GL.GL_TEXTURE_2D); + gl.glDisable(GL.GL_CULL_FACE); + gl.glDisable(GLLightingFunc.GL_LIGHTING); + gl.glDisable(GLLightingFunc.GL_LIGHT0); + gl.glDisable(GL2.GL_AUTO_NORMAL); + gl.glDisable(GLLightingFunc.GL_NORMALIZE); + gl.glFrontFace(GL.GL_CCW); // default + } + } + @Override public void init(final GLAutoDrawable drawable) { final GL2 gl = drawable.getGL().getGL2(); @@ -41,7 +77,7 @@ public class Teapot implements GLEventListener { } catch (final Exception e) { e.printStackTrace(); } - tex.bind(gl); + // tex.bind(gl); // uncomment this and comment the above to see a working texture // makeStripeImage(); @@ -57,24 +93,10 @@ public class Teapot implements GLEventListener { // gl.glTexImage1D(GL2.GL_TEXTURE_1D, 0, 3, stripeImageWidth, 0, // GL.GL_RGB, GL.GL_UNSIGNED_BYTE, stripeImageBuf); - gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT); + // gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT); // gl.glTexGeni(GL2.GL_S, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_OBJECT_LINEAR); // gl.glTexGenfv(GL2.GL_S, GL2.GL_OBJECT_PLANE, sgenparams, 0); - - gl.glEnable(GL.GL_DEPTH_TEST); - gl.glDepthFunc(GL.GL_LESS); - // gl.glEnable(GL2.GL_TEXTURE_GEN_S); - // gl.glEnable(GL2.GL_TEXTURE_1D); - gl.glEnable(GL.GL_TEXTURE_2D); - gl.glEnable(GL.GL_CULL_FACE); - gl.glEnable(GLLightingFunc.GL_LIGHTING); - gl.glEnable(GLLightingFunc.GL_LIGHT0); - gl.glEnable(GL2.GL_AUTO_NORMAL); - gl.glEnable(GLLightingFunc.GL_NORMALIZE); - gl.glFrontFace(GL.GL_CW); - gl.glCullFace(GL.GL_BACK); - gl.glMaterialf(GL.GL_FRONT, GLLightingFunc.GL_SHININESS, 64.0f); } float angleZ = 0.0f; @@ -85,8 +107,7 @@ public class Teapot implements GLEventListener { public void display(final GLAutoDrawable gLDrawable) { final GL2 gl = gLDrawable.getGL().getGL2(); - tex.bind(gl); - gl.glEnable(GL.GL_TEXTURE_2D); + enableStates(gl, true); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); gl.glPushMatrix(); @@ -101,13 +122,14 @@ public class Teapot implements GLEventListener { rotDir = +1.0f; } angleZ += rotIncr * rotDir; + + enableStates(gl, false); } @Override public void reshape(final GLAutoDrawable gLDrawable, final int x, final int y, final int w, final int h) { final GL2 gl = gLDrawable.getGL().getGL2(); - gl.glViewport(0, 0, w, h); gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION); gl.glLoadIdentity(); if (w <= h) { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/awt/Bug818GLJPanelApplet.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/awt/Bug818GLJPanelAndGLCanvasApplet.java index 8280919e6..a153fc7ae 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/awt/Bug818GLJPanelApplet.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/awt/Bug818GLJPanelAndGLCanvasApplet.java @@ -27,7 +27,9 @@ */
package com.jogamp.opengl.test.junit.jogl.demos.gl2.awt;
+import java.awt.ComponentOrientation;
import java.awt.Dimension;
+import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.InputStream;
@@ -47,8 +49,10 @@ import javax.media.opengl.fixedfunc.GLLightingFunc; import javax.media.opengl.fixedfunc.GLMatrixFunc;
import javax.media.opengl.fixedfunc.GLPointerFunc;
import javax.swing.JApplet;
+import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JFrame;
+import javax.swing.SwingConstants;
import com.jogamp.common.util.VersionUtil;
import com.jogamp.opengl.JoglVersion;
@@ -57,7 +61,7 @@ import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureIO;
/**
- * Bug 818: OSX GLJPanel Crash
+ * Bug 818: OSX GLJPanel [and GLCanvas] Crash
* <pre>
* - NVIDIA GeForce GT 330M
* - GL_VENDOR: "NVIDIA Corporation"
@@ -66,7 +70,7 @@ import com.jogamp.opengl.util.texture.TextureIO; * - Mac OSX 10.6.8
* </pre>
*/
-public class Bug818GLJPanelApplet extends JApplet {
+public class Bug818GLJPanelAndGLCanvasApplet extends JApplet {
private static final long serialVersionUID = 1L;
@@ -81,7 +85,7 @@ public class Bug818GLJPanelApplet extends JApplet { static public void main(final String args[]) {
isApplet = false;
- final JApplet myApplet = new Bug818GLJPanelApplet();
+ final JApplet myApplet = new Bug818GLJPanelAndGLCanvasApplet();
appletHolder = new JPanel();
@@ -115,19 +119,25 @@ public class Bug818GLJPanelApplet extends JApplet { public void init() {
final JPanel panel = new JPanel();
+ panel.setLayout(new GridLayout(2, 2));
+ System.err.println("Pre Orientation L2R: "+panel.getComponentOrientation().isLeftToRight());
+ panel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+ System.err.println("Post Orientation L2R: "+panel.getComponentOrientation().isLeftToRight());
setContentPane(panel);
-
- final GLCanvas glCanvas = new GLCanvas();
- glCanvas.addGLEventListener(new JOGLQuad(true));
- animatorCanvas = new Animator(glCanvas);
- glCanvas.setPreferredSize(new Dimension(300, 300));
- panel.add(glCanvas);
+ panel.add(new JLabel("GLJPanel", SwingConstants.CENTER));
+ panel.add(new JLabel("GLCanvas", SwingConstants.CENTER));
final GLJPanel gljPanel = new GLJPanel();
gljPanel.addGLEventListener(new JOGLQuad(false));
animatorPanel = new Animator(gljPanel);
gljPanel.setPreferredSize(new Dimension(300, 300));
panel.add(gljPanel);
+
+ final GLCanvas glCanvas = new GLCanvas();
+ glCanvas.addGLEventListener(new JOGLQuad(true));
+ animatorCanvas = new Animator(glCanvas);
+ glCanvas.setPreferredSize(new Dimension(300, 300));
+ panel.add(glCanvas);
}
@Override
@@ -271,7 +281,7 @@ public class Bug818GLJPanelApplet extends JApplet { // set the color of the quad
if (canvas) {
- gl.glColor3f(0.2f, 1.0f, 1.0f);
+ gl.glColor3f(0.2f, 0.2f, 1.0f);
} else {
gl.glColor3f(1.0f, 0.2f, 0.2f);
}
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/awt/GLJPanelsAndGLCanvasDemoGL2Applet.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/awt/GLJPanelsAndGLCanvasDemoGL2Applet.java new file mode 100644 index 000000000..fcff9e56b --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/awt/GLJPanelsAndGLCanvasDemoGL2Applet.java @@ -0,0 +1,180 @@ +/**
+ * Copyright 2013 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.opengl.test.junit.jogl.demos.gl2.awt;
+
+import java.awt.ComponentOrientation;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.media.opengl.awt.GLCanvas;
+import javax.media.opengl.awt.GLJPanel;
+import javax.swing.JApplet;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JFrame;
+import javax.swing.SwingConstants;
+
+import com.jogamp.opengl.test.junit.jogl.demos.gl2.Gears;
+import com.jogamp.opengl.test.junit.jogl.demos.gl2.Teapot;
+import com.jogamp.opengl.util.Animator;
+
+/**
+ */
+public class GLJPanelsAndGLCanvasDemoGL2Applet extends JApplet {
+
+ private static final long serialVersionUID = 1L;
+
+ private Animator[] animator;
+
+ public static JFrame frame;
+ public static JPanel appletHolder;
+ public static boolean isApplet = true;
+
+ static public void main(final String args[]) {
+ isApplet = false;
+
+ final JApplet myApplet = new GLJPanelsAndGLCanvasDemoGL2Applet();
+
+ appletHolder = new JPanel();
+
+ frame = new JFrame("Bug818GLJPanelApplet");
+ frame.getContentPane().add(myApplet);
+
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowClosing(final WindowEvent e) {
+ System.exit(0);
+ }
+ });
+
+ try {
+ javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
+ public void run() {
+ myApplet.init();
+ frame.validate();
+ frame.pack();
+ frame.setVisible(true);
+ } } );
+ } catch( final Throwable throwable ) {
+ throwable.printStackTrace();
+ }
+
+ myApplet.start();
+ }
+
+
+ @Override
+ public void init() {
+
+ final JPanel panel = new JPanel();
+ panel.setLayout(new GridLayout(3, 2));
+ System.err.println("Pre Orientation L2R: "+panel.getComponentOrientation().isLeftToRight());
+ panel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
+ System.err.println("Post Orientation L2R: "+panel.getComponentOrientation().isLeftToRight());
+ setContentPane(panel);
+
+ animator = new Animator[3*2];
+ int animIdx = 0;
+
+ panel.add(new JLabel("GLJPanel Auto V-Flip", SwingConstants.CENTER));
+ {
+ {
+ final GLJPanel gljPanel = new GLJPanel();
+ gljPanel.addGLEventListener(new Teapot());
+ animator[animIdx++] = new Animator(gljPanel);
+ gljPanel.setPreferredSize(new Dimension(300, 300));
+ panel.add(gljPanel);
+ }
+ {
+ final GLJPanel gljPanel = new GLJPanel();
+ gljPanel.addGLEventListener(new Gears(0));
+ animator[animIdx++] = new Animator(gljPanel);
+ gljPanel.setPreferredSize(new Dimension(300, 300));
+ panel.add(gljPanel);
+ }
+ }
+ panel.add(new JLabel("GLJPanel User V-Flip", SwingConstants.CENTER));
+ {
+ {
+ final GLJPanel gljPanel = new GLJPanel();
+ gljPanel.setSkipGLOrientationVerticalFlip(true);
+ gljPanel.addGLEventListener(new Teapot());
+ animator[animIdx++] = new Animator(gljPanel);
+ gljPanel.setPreferredSize(new Dimension(300, 300));
+ panel.add(gljPanel);
+ }
+ {
+ final GLJPanel gljPanel = new GLJPanel();
+ gljPanel.setSkipGLOrientationVerticalFlip(true);
+ gljPanel.addGLEventListener(new Gears(0));
+ animator[animIdx++] = new Animator(gljPanel);
+ gljPanel.setPreferredSize(new Dimension(300, 300));
+ panel.add(gljPanel);
+ }
+ }
+
+ panel.add(new JLabel("GLCanvas", SwingConstants.CENTER));
+ {
+ {
+ final GLCanvas glCanvas = new GLCanvas();
+ glCanvas.addGLEventListener(new Teapot());
+ animator[animIdx++] = new Animator(glCanvas);
+ glCanvas.setPreferredSize(new Dimension(300, 300));
+ panel.add(glCanvas);
+ }
+ {
+ final GLCanvas glCanvas = new GLCanvas();
+ glCanvas.addGLEventListener(new Gears(1));
+ animator[animIdx++] = new Animator(glCanvas);
+ glCanvas.setPreferredSize(new Dimension(300, 300));
+ panel.add(glCanvas);
+ }
+ }
+ }
+
+ @Override
+ public void start() {
+ for(int i=0; i<animator.length; i++) {
+ animator[i].start();
+ animator[i].setUpdateFPSFrames(60, System.err);
+ }
+ }
+
+ @Override
+ public void stop() {
+ for(int i=0; i<animator.length; i++) {
+ animator[i].stop();
+ }
+ }
+
+ @Override
+ public void destroy() {}
+}
+
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/awt/TestGearsGLJPanelAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/awt/TestGearsGLJPanelAWT.java index 3650e4955..f47a9b794 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/awt/TestGearsGLJPanelAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/awt/TestGearsGLJPanelAWT.java @@ -30,6 +30,7 @@ package com.jogamp.opengl.test.junit.jogl.demos.gl2.awt; import javax.media.opengl.*; +import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.event.TraceKeyAdapter; import com.jogamp.newt.event.TraceWindowAdapter; import com.jogamp.newt.event.awt.AWTKeyAdapter; @@ -121,6 +122,56 @@ public class TestGearsGLJPanelAWT extends UITestCase { new AWTKeyAdapter(new TraceKeyAdapter(quitAdapter), glJPanel).addTo(glJPanel); new AWTWindowAdapter(new TraceWindowAdapter(quitAdapter), glJPanel).addTo(frame); + final com.jogamp.newt.event.KeyListener kl = new com.jogamp.newt.event.KeyAdapter() { + @Override + public void keyPressed(final KeyEvent e) { + if( e.isAutoRepeat() ) { + return; + } + if(e.getKeyChar()=='m') { + final GLCapabilitiesImmutable capsPre = glJPanel.getChosenGLCapabilities(); + final GLCapabilities capsNew = new GLCapabilities(capsPre.getGLProfile()); + capsNew.copyFrom(capsPre); + final boolean msaa; + if( capsPre.getSampleBuffers() ) { + capsNew.setSampleBuffers(false); + capsNew.setDoubleBuffered(false); + msaa = false; + } else { + capsNew.setSampleBuffers(true); + capsNew.setNumSamples(4); + msaa = true; + } + System.err.println("[set MSAA "+msaa+" Caps had]: "+capsPre); + System.err.println("[set MSAA "+msaa+" Caps new]: "+capsNew); + System.err.println("XXX-A1: "+animator.toString()); + glJPanel.setRequestedGLCapabilities(capsNew); + System.err.println("XXX-A2: "+animator.toString()); + System.err.println("XXX: "+glJPanel.toString()); + } else if(e.getKeyChar()=='b') { + final GLCapabilitiesImmutable capsPre = glJPanel.getChosenGLCapabilities(); + final GLCapabilities capsNew = new GLCapabilities(capsPre.getGLProfile()); + capsNew.copyFrom(capsPre); + final boolean bmp; + if( capsPre.isBitmap() ) { + capsNew.setBitmap(false); // auto-choose + bmp = false; + } else { + capsNew.setBitmap(true); + capsNew.setFBO(false); + capsNew.setPBuffer(false); + bmp = true; + } + System.err.println("[set Bitmap "+bmp+" Caps had]: "+capsPre); + System.err.println("[set Bitmap "+bmp+" Caps new]: "+capsNew); + System.err.println("XXX-A1: "+animator.toString()); + glJPanel.setRequestedGLCapabilities(capsNew); + System.err.println("XXX-A2: "+animator.toString()); + System.err.println("XXX: "+glJPanel.toString()); + } + } }; + new AWTKeyAdapter(kl, glJPanel).addTo(glJPanel); + final long t0 = System.currentTimeMillis(); long t1 = t0; boolean triggerSnap = false; diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/TessellationShader01aGL4.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/TessellationShader01aGL4.java new file mode 100644 index 000000000..a5807a096 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/TessellationShader01aGL4.java @@ -0,0 +1,191 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.test.junit.jogl.demos.gl4; + +import java.nio.FloatBuffer; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL2ES3; +import javax.media.opengl.GL2GL3; +import javax.media.opengl.GL4; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLEventListener; + +import com.jogamp.opengl.util.glsl.ShaderCode; +import com.jogamp.opengl.util.glsl.ShaderProgram; + +/** + * JOGL Tessellation ShaderCode GL4 test case. + * <p> + * Demonstrates tessellation-control and -evaluation shaders. + * </p> + * + * @author Raymond L. Rivera, 2014 + * @author Sven Gothel + */ +public class TessellationShader01aGL4 implements GLEventListener { + private static final double ANIMATION_RATE = 950.0; + + private ShaderProgram program; + private final int[] vertexArray = new int[1]; + private FloatBuffer vertexOffset; + private FloatBuffer backgroundColor; + + + @Override + public void init(final GLAutoDrawable auto) { + final double theta = System.currentTimeMillis() / ANIMATION_RATE; + vertexOffset = FloatBuffer.allocate(4); + vertexOffset.put(0, (float)(Math.sin(theta) * 0.5f)); + vertexOffset.put(1, (float)(Math.cos(theta) * 0.6f)); + vertexOffset.put(2, 0.0f); + vertexOffset.put(3, 0.0f); + + backgroundColor = FloatBuffer.allocate(4); + backgroundColor.put(0, 0.25f); + backgroundColor.put(1, 0.25f); + backgroundColor.put(2, 0.25f); + backgroundColor.put(3, 1.0f); + + final GL4 gl = auto.getGL().getGL4(); + program = createProgram(auto); + gl.glGenVertexArrays(vertexArray.length, vertexArray, 0); + gl.glBindVertexArray(vertexArray[0]); + gl.glPatchParameteri(GL4.GL_PATCH_VERTICES, 3); + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_LINE); + } + + @Override + public void display(final GLAutoDrawable auto) { + final GL4 gl = auto.getGL().getGL4(); + final double value = System.currentTimeMillis() / ANIMATION_RATE; + gl.glClearBufferfv(GL2ES3.GL_COLOR, 0, backgroundColor); + gl.glUseProgram(program.program()); + vertexOffset.put(0, (float)(Math.sin(value) * 0.5f)); + vertexOffset.put(1, (float)(Math.cos(value) * 0.6f)); + gl.glVertexAttrib4fv(0, vertexOffset); + gl.glDrawArrays(GL4.GL_PATCHES, 0, 3); + } + + @Override + public void dispose(final GLAutoDrawable auto) { + final GL4 gl = auto.getGL().getGL4(); + gl.glDeleteVertexArrays(vertexArray.length, vertexArray, 0); + program.destroy(gl); + } + + @Override + public void reshape(final GLAutoDrawable auto, final int x, final int y, final int width, final int height) { + // final GL4 gl = auto.getGL().getGL4(); + } + + private ShaderProgram createProgram(final GLAutoDrawable auto) { + final GL4 gl = auto.getGL().getGL4(); + final String vertexSource = + "#version 440 core \n" + + " \n" + + "layout (location = 0) in vec4 offset; \n" + + " \n" + + "void main(void) \n" + + "{ \n" + + " const vec4 vertices[3] = vec4[3] ( \n" + + " vec4( 0.25, 0.25, 0.5, 1.0), \n" + + " vec4(-0.25, -0.25, 0.5, 1.0), \n" + + " vec4( 0.25, -0.25, 0.5, 1.0)); \n" + + " gl_Position = vertices[gl_VertexID] + offset; \n" + + "} \n"; + final String tessCtrlSource = + "#version 440 core \n" + + "layout (vertices = 3) out; \n" + + " \n" + + "void main(void) \n" + + "{ \n" + + " if (gl_InvocationID == 0) \n" + + " { \n" + + " gl_TessLevelInner[0] = 5.0; \n" + + " gl_TessLevelOuter[0] = 5.0; \n" + + " gl_TessLevelOuter[1] = 5.0; \n" + + " gl_TessLevelOuter[2] = 5.0; \n" + + " } \n" + + " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" + + "} \n"; + final String tessEvalSource = + "#version 440 core \n" + + " \n" + + "layout (triangles, equal_spacing, cw) in; \n" + + " \n" + + "void main(void) \n" + + "{ \n" + + " gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) + \n" + + " (gl_TessCoord.y * gl_in[1].gl_Position) + \n" + + " (gl_TessCoord.z * gl_in[2].gl_Position); \n" + + "} \n"; + final String fragmentSource = + "#version 440 core \n" + + " \n" + + "out vec4 color; \n" + + " \n" + + "void main(void) \n" + + "{ \n" + + " color = vec4(1.0, 1.0, 1.0, 1.0); \n" + + "} \n"; + + final ShaderCode vertexShader = createShader(gl, GL2ES2.GL_VERTEX_SHADER, vertexSource); + final ShaderCode tessCtrlShader = createShader(gl, GL4.GL_TESS_CONTROL_SHADER, tessCtrlSource); + final ShaderCode tessEvalShader = createShader(gl, GL4.GL_TESS_EVALUATION_SHADER, tessEvalSource); + final ShaderCode fragmentShader = createShader(gl, GL2ES2.GL_FRAGMENT_SHADER, fragmentSource); + + final ShaderProgram program = new ShaderProgram(); + + program.init(gl); + program.add(vertexShader); + program.add(tessCtrlShader); + program.add(tessEvalShader); + program.add(fragmentShader); + + program.link(gl, System.err); + if(!program.validateProgram(gl, System.out)) + System.err.println("[error] Program linking failed."); + + return program; + } + + private ShaderCode createShader(final GL4 gl, final int shaderType, final String source) { + final String[][] sources = new String[1][1]; + sources[0] = new String[]{ source }; + final ShaderCode shader = new ShaderCode(shaderType, sources.length, sources); + + final boolean compiled = shader.compile(gl, System.err); + if (!compiled) + System.err.println("[error] Shader compilation failed."); + + return shader; + } + +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/TessellationShader01bGL4.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/TessellationShader01bGL4.java new file mode 100644 index 000000000..7be26e400 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/TessellationShader01bGL4.java @@ -0,0 +1,142 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.test.junit.jogl.demos.gl4; + +import java.nio.FloatBuffer; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL2ES3; +import javax.media.opengl.GL2GL3; +import javax.media.opengl.GL4; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; + +import com.jogamp.opengl.util.glsl.ShaderCode; +import com.jogamp.opengl.util.glsl.ShaderProgram; + +/** + * JOGL Tessellation ShaderCode GL4 test case. + * <p> + * Demonstrates tessellation-control and -evaluation shaders. + * </p> + * + * @author Raymond L. Rivera, 2014 + * @author Sven Gothel + */ +public class TessellationShader01bGL4 implements GLEventListener { + private static final double ANIMATION_RATE = 950.0; + + private ShaderProgram program; + private final int[] vertexArray = new int[1]; + private FloatBuffer vertexOffset; + private FloatBuffer backgroundColor; + + + @Override + public void init(final GLAutoDrawable auto) { + final double theta = System.currentTimeMillis() / ANIMATION_RATE; + vertexOffset = FloatBuffer.allocate(4); + vertexOffset.put(0, (float)(Math.sin(theta) * 0.5f)); + vertexOffset.put(1, (float)(Math.cos(theta) * 0.6f)); + vertexOffset.put(2, 0.0f); + vertexOffset.put(3, 0.0f); + + backgroundColor = FloatBuffer.allocate(4); + backgroundColor.put(0, 0.25f); + backgroundColor.put(1, 0.25f); + backgroundColor.put(2, 0.25f); + backgroundColor.put(3, 1.0f); + + final GL4 gl = auto.getGL().getGL4(); + program = createProgram(auto); + gl.glGenVertexArrays(vertexArray.length, vertexArray, 0); + gl.glBindVertexArray(vertexArray[0]); + gl.glPatchParameteri(GL4.GL_PATCH_VERTICES, 3); + gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_LINE); + } + + @Override + public void display(final GLAutoDrawable auto) { + final GL4 gl = auto.getGL().getGL4(); + final double value = System.currentTimeMillis() / ANIMATION_RATE; + gl.glClearBufferfv(GL2ES3.GL_COLOR, 0, backgroundColor); + gl.glUseProgram(program.program()); + vertexOffset.put(0, (float)(Math.sin(value) * 0.5f)); + vertexOffset.put(1, (float)(Math.cos(value) * 0.6f)); + gl.glVertexAttrib4fv(0, vertexOffset); + gl.glDrawArrays(GL4.GL_PATCHES, 0, 3); + } + + @Override + public void dispose(final GLAutoDrawable auto) { + final GL4 gl = auto.getGL().getGL4(); + gl.glDeleteVertexArrays(vertexArray.length, vertexArray, 0); + program.destroy(gl); + } + + @Override + public void reshape(final GLAutoDrawable auto, final int x, final int y, final int width, final int height) { + // final GL4 gl = auto.getGL().getGL4(); + } + + static final String shaderBasename = "tess_example01"; + + private ShaderProgram createProgram(final GLAutoDrawable auto) { + final GL4 gl = auto.getGL().getGL4(); + + final ShaderProgram sp; + { + final ShaderCode vs, tcs, tes, fs; + vs = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(), + "shader", "shader/bin", shaderBasename, true); + tcs = ShaderCode.create(gl, GL4.GL_TESS_CONTROL_SHADER, this.getClass(), + "shader", "shader/bin", shaderBasename, true); + tes = ShaderCode.create(gl, GL4.GL_TESS_EVALUATION_SHADER, this.getClass(), + "shader", "shader/bin", shaderBasename, true); + fs = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(), + "shader", "shader/bin", shaderBasename, true); + vs.defaultShaderCustomization(gl, true, true); + tcs.defaultShaderCustomization(gl, true, true); + tes.defaultShaderCustomization(gl, true, true); + fs.defaultShaderCustomization(gl, true, true); + + sp = new ShaderProgram(); + sp.add(gl, vs, System.err); + sp.add(gl, tcs, System.err); + sp.add(gl, tes, System.err); + sp.add(gl, fs, System.err); + if(!sp.link(gl, System.err)) { + throw new GLException("Couldn't link program: "+sp); + } + } + + return sp; + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/newt/TestTessellationShader01GL4NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/newt/TestTessellationShader01GL4NEWT.java new file mode 100644 index 000000000..a05dcec06 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/newt/TestTessellationShader01GL4NEWT.java @@ -0,0 +1,117 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.test.junit.jogl.demos.gl4.newt; + +import java.io.IOException; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLProfile; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.test.junit.jogl.demos.gl4.TessellationShader01aGL4; +import com.jogamp.opengl.test.junit.jogl.demos.gl4.TessellationShader01bGL4; +import com.jogamp.opengl.test.junit.util.MiscUtils; +import com.jogamp.opengl.test.junit.util.QuitAdapter; +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.Animator; + +/** + * Test Geometry shader demo TessellationShader01aGL4 and TessellationShader01bGL4 + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestTessellationShader01GL4NEWT extends UITestCase { + static long duration = 500; // ms + + static GLCapabilities getCaps(final String profile) { + if( !GLProfile.isAvailable(profile) ) { + System.err.println("Profile "+profile+" n/a"); + return null; + } + return new GLCapabilities(GLProfile.get(profile)); + } + + @Test + public void test01_01a() throws InterruptedException { + final GLCapabilities caps = getCaps(GLProfile.GL4); + if( null == caps ) { return; } + testImpl(caps, new TessellationShader01aGL4()); + } + + @Test + public void test02_01b() throws InterruptedException { + final GLCapabilities caps = getCaps(GLProfile.GL4); + if( null == caps ) { return; } + testImpl(caps, new TessellationShader01bGL4()); + } + + private void testImpl(final GLCapabilities caps, final GLEventListener glel) throws InterruptedException { + final GLWindow glWindow = GLWindow.create(caps); + Assert.assertNotNull(glWindow); + glWindow.setSize(800, 600); + glWindow.setVisible(true); + glWindow.setTitle("JOGL Tessellation Shader Test"); + Assert.assertTrue(glWindow.isNativeValid()); + + final QuitAdapter quitAdapter = new QuitAdapter(); + glWindow.addKeyListener(quitAdapter); + glWindow.addWindowListener(quitAdapter); + glWindow.addGLEventListener( glel ); + + final SnapshotGLEventListener snapshotGLEventListener = new SnapshotGLEventListener(); + glWindow.addGLEventListener(snapshotGLEventListener); + + final Animator animator = new Animator(glWindow); + animator.start(); + + animator.setUpdateFPSFrames(60, System.err); + snapshotGLEventListener.setMakeSnapshot(); + + while(!quitAdapter.shouldQuit() && animator.isAnimating() && animator.getTotalFPSDuration()<duration) { + Thread.sleep(100); + } + + animator.stop(); + glWindow.destroy(); + } + + public static void main(final String args[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + i++; + duration = MiscUtils.atol(args[i], duration); + } + } + org.junit.runner.JUnitCore.main(TestTessellationShader01GL4NEWT.class.getName()); + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/shader/tess_example01.fp b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/shader/tess_example01.fp new file mode 100644 index 000000000..8a3b23203 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/shader/tess_example01.fp @@ -0,0 +1,6 @@ + +out vec4 color; + +void main(void) { + color = vec4(1.0, 1.0, 1.0, 1.0); +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/shader/tess_example01.tcp b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/shader/tess_example01.tcp new file mode 100644 index 000000000..b76aa580d --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/shader/tess_example01.tcp @@ -0,0 +1,12 @@ + +layout (vertices = 3) out; + +void main(void) { + if (gl_InvocationID == 0) { + gl_TessLevelInner[0] = 5.0; + gl_TessLevelOuter[0] = 5.0; + gl_TessLevelOuter[1] = 5.0; + gl_TessLevelOuter[2] = 5.0; + } + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/shader/tess_example01.tep b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/shader/tess_example01.tep new file mode 100644 index 000000000..9c307f5d4 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/shader/tess_example01.tep @@ -0,0 +1,8 @@ + +layout (triangles, equal_spacing, cw) in; + +void main(void) { + gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) + + (gl_TessCoord.y * gl_in[1].gl_Position) + + (gl_TessCoord.z * gl_in[2].gl_Position); +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/shader/tess_example01.vp b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/shader/tess_example01.vp new file mode 100644 index 000000000..c207c1335 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl4/shader/tess_example01.vp @@ -0,0 +1,12 @@ + +layout (location = 0) in vec4 offset; + +void main(void) +{ + const vec4 vertices[3] = + vec4[3] ( + vec4( 0.25, 0.25, 0.5, 1.0), + vec4(-0.25, -0.25, 0.5, 1.0), + vec4( 0.25, -0.25, 0.5, 1.0) ); + gl_Position = vertices[gl_VertexID] + offset; +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/glsl/TestGLSLShaderState01NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/glsl/TestGLSLShaderState01NEWT.java index b3e01ac0f..57d6caa85 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/glsl/TestGLSLShaderState01NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/glsl/TestGLSLShaderState01NEWT.java @@ -67,7 +67,7 @@ public class TestGLSLShaderState01NEWT extends UITestCase { @Test public void test00NoShaderState_Validation() throws InterruptedException { // preset .. - final NEWTGLContext.WindowContext winctx = NEWTGLContext.createOnscreenWindow( + final NEWTGLContext.WindowContext winctx = NEWTGLContext.createWindow( new GLCapabilities(GLProfile.getGL2ES2()), 480, 480, true); final GLDrawable drawable = winctx.context.getGLDrawable(); final GL2ES2 gl = winctx.context.getGL().getGL2ES2(); @@ -183,7 +183,7 @@ public class TestGLSLShaderState01NEWT extends UITestCase { @Test public void test01ShaderState_Validation() throws InterruptedException { // preset .. - final NEWTGLContext.WindowContext winctx = NEWTGLContext.createOnscreenWindow( + final NEWTGLContext.WindowContext winctx = NEWTGLContext.createWindow( new GLCapabilities(GLProfile.getGL2ES2()), 480, 480, true); final GLDrawable drawable = winctx.context.getGLDrawable(); final GL2ES2 gl = winctx.context.getGL().getGL2ES2(); @@ -306,7 +306,7 @@ public class TestGLSLShaderState01NEWT extends UITestCase { private void testShaderState_PerformanceSingleImpl(final boolean toggleEnable) throws InterruptedException { // preset .. - final NEWTGLContext.WindowContext winctx = NEWTGLContext.createOnscreenWindow( + final NEWTGLContext.WindowContext winctx = NEWTGLContext.createWindow( new GLCapabilities(GLProfile.getGL2ES2()), 480, 480, false); final GLDrawable drawable = winctx.context.getGLDrawable(); final GL2ES2 gl = winctx.context.getGL().getGL2ES2(); @@ -395,7 +395,7 @@ public class TestGLSLShaderState01NEWT extends UITestCase { @Test(timeout=240000) public void test04ShaderState_PerformanceDouble() throws InterruptedException { // preset .. - final NEWTGLContext.WindowContext winctx = NEWTGLContext.createOnscreenWindow( + final NEWTGLContext.WindowContext winctx = NEWTGLContext.createWindow( new GLCapabilities(GLProfile.getGL2ES2()), 480, 480, false); final GLDrawable drawable = winctx.context.getGLDrawable(); final GL2ES2 gl = winctx.context.getGL().getGL2ES2(); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/glsl/TestGLSLShaderState02NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/glsl/TestGLSLShaderState02NEWT.java index 21d945470..501850c4f 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/glsl/TestGLSLShaderState02NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/glsl/TestGLSLShaderState02NEWT.java @@ -74,7 +74,7 @@ public class TestGLSLShaderState02NEWT extends UITestCase { private void testShaderState01Validation(final boolean linkSP1) throws InterruptedException { // preset .. - final NEWTGLContext.WindowContext winctx = NEWTGLContext.createOnscreenWindow( + final NEWTGLContext.WindowContext winctx = NEWTGLContext.createWindow( new GLCapabilities(GLProfile.getGL2ES2()), 480, 480, true); final GLDrawable drawable = winctx.context.getGLDrawable(); final GL2ES2 gl = winctx.context.getGL().getGL2ES2(); @@ -240,7 +240,7 @@ public class TestGLSLShaderState02NEWT extends UITestCase { @Test(timeout=240000) public void testShaderState01PerformanceDouble() throws InterruptedException { // preset .. - final NEWTGLContext.WindowContext winctx = NEWTGLContext.createOnscreenWindow( + final NEWTGLContext.WindowContext winctx = NEWTGLContext.createWindow( new GLCapabilities(GLProfile.getGL2ES2()), 480, 480, false); final GLDrawable drawable = winctx.context.getGLDrawable(); final GL2ES2 gl = winctx.context.getGL().getGL2ES2(); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/glsl/TestTransformFeedbackVaryingsBug407NEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/glsl/TestTransformFeedbackVaryingsBug407NEWT.java index 71b2d488e..e267d93b1 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/glsl/TestTransformFeedbackVaryingsBug407NEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/glsl/TestTransformFeedbackVaryingsBug407NEWT.java @@ -88,7 +88,7 @@ public class TestTransformFeedbackVaryingsBug407NEWT extends UITestCase { final static String glps = GLProfile.GL3; private NEWTGLContext.WindowContext prepareTest() throws GLException, InterruptedException { - final NEWTGLContext.WindowContext winctx = NEWTGLContext.createOnscreenWindow( + final NEWTGLContext.WindowContext winctx = NEWTGLContext.createWindow( new GLCapabilities(GLProfile.getMaxProgrammable(true)), 480, 480, debugGL); if(!winctx.context.getGL().isGL3()) { System.err.println("GL3 not available"); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/perf/TestPerf001GLJPanelInit01AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/perf/TestPerf001GLJPanelInit01AWT.java index 92785ef6e..0db2afc32 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/perf/TestPerf001GLJPanelInit01AWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/perf/TestPerf001GLJPanelInit01AWT.java @@ -31,6 +31,8 @@ import java.awt.Component; import java.awt.Dimension; import java.awt.GridLayout; import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import javax.media.opengl.GLAnimatorControl; @@ -52,13 +54,15 @@ import org.junit.Test; import org.junit.runners.MethodSorters; import com.jogamp.common.os.Platform; +import com.jogamp.newt.awt.NewtCanvasAWT; +import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import com.jogamp.opengl.test.junit.util.MiscUtils; import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.util.Animator; /** - * Multiple GLJPanels in a JFrame's Grid + * Tests multiple [GLJPanels, GLCanvas or NewtCanvasAWT] in a JFrame's Grid */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestPerf001GLJPanelInit01AWT extends UITestCase { @@ -69,15 +73,26 @@ public class TestPerf001GLJPanelInit01AWT extends UITestCase { GLProfile.initSingleton(); } + static enum CanvasType { GLCanvas_T, GLJPanel_T, NewtCanvasAWT_T }; + + static class GLADComp { + GLADComp(final GLAutoDrawable glad, final Component comp) { + this.glad = glad; + this.comp = comp; + } + final GLAutoDrawable glad; + final Component comp; + } public void test(final GLCapabilitiesImmutable caps, final boolean useGears, final int width, final int height, final int rows, - final int columns, final boolean useGLJPanel, final boolean useAnim) { + final int columns, final CanvasType canvasType, final boolean useAnim) { final GLAnimatorControl animator = useAnim ? new Animator() : null; final JFrame frame; final JPanel panel; + final List<NewtCanvasAWT> newtCanvasAWTList = new ArrayList<NewtCanvasAWT>(); panel = new JPanel(); - frame = new JFrame("DemoGLJPanelGridAWT"); + frame = new JFrame(getSimpleTestName(".")); panel.setLayout(new GridLayout(rows, columns)); // panel.setBounds(0, 0, width, height); @@ -94,8 +109,21 @@ public class TestPerf001GLJPanelInit01AWT extends UITestCase { public void run() { t[0] = Platform.currentTimeMillis(); for(int i=0; i<panelCount; i++) { - final GLAutoDrawable glad = useGLJPanel ? createGLJPanel(caps, useGears, animator, eSize) : createGLCanvas(caps, useGears, animator, eSize); - glad.addGLEventListener(new GLEventListener() { + final GLADComp gladComp; + switch(canvasType) { + case GLCanvas_T: + gladComp = createGLCanvas(caps, useGears, animator, eSize); + break; + case GLJPanel_T: + gladComp = createGLJPanel(caps, useGears, animator, eSize); + break; + case NewtCanvasAWT_T: + gladComp = createNewtCanvasAWT(caps, useGears, animator, eSize); + newtCanvasAWTList.add((NewtCanvasAWT)gladComp.comp); + break; + default: throw new InternalError("XXX"); + } + gladComp.glad.addGLEventListener(new GLEventListener() { @Override public void init(final GLAutoDrawable drawable) { initCount.getAndIncrement(); @@ -107,7 +135,7 @@ public class TestPerf001GLJPanelInit01AWT extends UITestCase { @Override public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {} }); - panel.add((Component)glad); + panel.add(gladComp.comp); } t[1] = Platform.currentTimeMillis(); frame.getContentPane().add(panel); @@ -136,7 +164,7 @@ public class TestPerf001GLJPanelInit01AWT extends UITestCase { final double panelCountF = initCount.get(); System.err.printf("P: %d %s:%n\tctor\t%6d/t %6.2f/1%n\tvisible\t%6d/t %6.2f/1%n\tsum-i\t%6d/t %6.2f/1%n", initCount.get(), - useGLJPanel?"GLJPanel":"GLCanvas", + canvasType, t[1]-t[0], (t[1]-t[0])/panelCountF, t[3]-t[1], (t[3]-t[1])/panelCountF, t[3]-t[0], (t[3]-t[0])/panelCountF); @@ -144,15 +172,24 @@ public class TestPerf001GLJPanelInit01AWT extends UITestCase { if( wait ) { UITestCase.waitForKey("Post-Init"); } + if( null != animator ) { + animator.start(); + } try { Thread.sleep(duration); } catch (final InterruptedException e1) { e1.printStackTrace(); } + if( null != animator ) { + animator.stop(); + } t[4] = Platform.currentTimeMillis(); try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { + while( !newtCanvasAWTList.isEmpty() ) { + newtCanvasAWTList.remove(0).destroy(); // removeNotify does not destroy GLWindow + } frame.dispose(); } } ); } catch (final Exception e1) { @@ -166,46 +203,76 @@ public class TestPerf001GLJPanelInit01AWT extends UITestCase { System.err.println("Total: "+(t[4]-t[0])); } - private GLAutoDrawable createGLCanvas(final GLCapabilitiesImmutable caps, final boolean useGears, final GLAnimatorControl anim, final Dimension size) { + private GLADComp createNewtCanvasAWT(final GLCapabilitiesImmutable caps, final boolean useGears, final GLAnimatorControl anim, final Dimension size) { + final GLWindow window = GLWindow.create(caps); + final NewtCanvasAWT canvas = new NewtCanvasAWT(window); + canvas.setSize(size); + canvas.setPreferredSize(size); + if( useGears ) { + final GearsES2 g = new GearsES2(0); + g.setVerbose(false); + window.addGLEventListener(g); + } + if( null != anim ) { + anim.add(window); + } + return new GLADComp(window, canvas); + } + private GLADComp createGLCanvas(final GLCapabilitiesImmutable caps, final boolean useGears, final GLAnimatorControl anim, final Dimension size) { final GLCanvas canvas = new GLCanvas(caps); canvas.setSize(size); canvas.setPreferredSize(size); if( useGears ) { - canvas.addGLEventListener(new GearsES2()); + canvas.addGLEventListener(new GearsES2(0)); } if( null != anim ) { anim.add(canvas); } - return canvas; + return new GLADComp(canvas, canvas); } - private GLAutoDrawable createGLJPanel(final GLCapabilitiesImmutable caps, final boolean useGears, final GLAnimatorControl anim, final Dimension size) { + private GLADComp createGLJPanel(final GLCapabilitiesImmutable caps, final boolean useGears, final GLAnimatorControl anim, final Dimension size) { final GLJPanel canvas = new GLJPanel(caps); canvas.setSize(size); canvas.setPreferredSize(size); if( useGears ) { - canvas.addGLEventListener(new GearsES2()); + canvas.addGLEventListener(new GearsES2(0)); } if( null != anim ) { anim.add(canvas); } - return canvas; + return new GLADComp(canvas, canvas); } - @Test - public void test01NopGLJPanelDef() throws InterruptedException, InvocationTargetException { - test(new GLCapabilities(null), false /*useGears*/, width, height, rows, cols, true /* useGLJPanel */, false /*useAnim*/); + // @Test + public void test01NopGLJPanel() throws InterruptedException, InvocationTargetException { + test(new GLCapabilities(null), false /*useGears*/, width, height, rows, cols, CanvasType.GLJPanel_T, false /*useAnim*/); } - @Test - public void test02NopGLJPanelBitmap() throws InterruptedException, InvocationTargetException { + // @Test + public void test02NopGLJPanelBMP() throws InterruptedException, InvocationTargetException { final GLCapabilities caps = new GLCapabilities(null); caps.setBitmap(true); - test(caps, false /*useGears*/, width, height, rows, cols, true /* useGLJPanel */, false /*useAnim*/); + test(caps, false /*useGears*/, width, height, rows, cols, CanvasType.GLJPanel_T, false /*useAnim*/); + } + + // @Test + public void test03NopGLCanvas() throws InterruptedException, InvocationTargetException { + test(new GLCapabilities(null), false /*useGears*/, width, height, rows, cols, CanvasType.GLCanvas_T, false /*useAnim*/); + } + + // @Test + public void test11GearsGLJPanel() throws InterruptedException, InvocationTargetException { + test(new GLCapabilities(null), true /*useGears*/, width, height, rows, cols, CanvasType.GLJPanel_T, true /*useAnim*/); + } + + // @Test + public void test13GearsGLCanvas() throws InterruptedException, InvocationTargetException { + test(new GLCapabilities(null), true /*useGears*/, width, height, rows, cols, CanvasType.GLCanvas_T, true /*useAnim*/); } @Test - public void test11NopGLCanvasDef() throws InterruptedException, InvocationTargetException { - test(new GLCapabilities(null), false /*useGears*/, width, height, rows, cols, false /* useGLJPanel */, false /*useAnim*/); + public void test14GearsNewtCanvasAWT() throws InterruptedException, InvocationTargetException { + test(new GLCapabilities(null), true /*useGears*/, width, height, rows, cols, CanvasType.NewtCanvasAWT_T, true /*useAnim*/); } static long duration = 0; // ms @@ -215,7 +282,8 @@ public class TestPerf001GLJPanelInit01AWT extends UITestCase { AtomicInteger initCount = new AtomicInteger(0); public static void main(final String[] args) { - boolean useGLJPanel = true, useGears = false, manual=false; + CanvasType canvasType = CanvasType.GLJPanel_T; + boolean useGears = false, manual=false; boolean waitMain = false; for(int i=0; i<args.length; i++) { @@ -230,8 +298,10 @@ public class TestPerf001GLJPanelInit01AWT extends UITestCase { rows = MiscUtils.atoi(args[++i], rows); } else if(args[i].equals("-cols")) { cols = MiscUtils.atoi(args[++i], cols); - } else if(args[i].equals("-glcanvas")) { - useGLJPanel = false; + } else if(args[i].equals("-type")) { + i++; + canvasType = CanvasType.valueOf(args[i]); + manual = true; } else if(args[i].equals("-gears")) { useGears = true; } else if(args[i].equals("-wait")) { @@ -250,7 +320,7 @@ public class TestPerf001GLJPanelInit01AWT extends UITestCase { if( manual ) { GLProfile.initSingleton(); final TestPerf001GLJPanelInit01AWT demo = new TestPerf001GLJPanelInit01AWT(); - demo.test(null, useGears, width, height, rows, cols, useGLJPanel, false /*useAnim*/); + demo.test(null, useGears, width, height, rows, cols, canvasType, useGears /*useAnim*/); } else { org.junit.runner.JUnitCore.main(TestPerf001GLJPanelInit01AWT.class.getName()); } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/perf/TestPerf001GLJPanelInit02AWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/perf/TestPerf001GLJPanelInit02AWT.java index c60c395eb..9f2e25fd7 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/perf/TestPerf001GLJPanelInit02AWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/perf/TestPerf001GLJPanelInit02AWT.java @@ -32,6 +32,8 @@ import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import javax.media.opengl.GLAnimatorControl; @@ -54,13 +56,15 @@ import org.junit.Test; import org.junit.runners.MethodSorters; import com.jogamp.common.os.Platform; +import com.jogamp.newt.awt.NewtCanvasAWT; +import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import com.jogamp.opengl.test.junit.util.MiscUtils; import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.util.Animator; /** - * Multiple GLJPanels in a JFrame + * Tests multiple JFrames each with a [GLJPanels, GLCanvas or NewtCanvasAWT] */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestPerf001GLJPanelInit02AWT extends UITestCase { @@ -71,13 +75,23 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { GLProfile.initSingleton(); } + static enum CanvasType { NOP_T, GLCanvas_T, GLJPanel_T, NewtCanvasAWT_T }; + + static class GLADComp { + GLADComp(final GLAutoDrawable glad, final Component comp) { + this.glad = glad; + this.comp = comp; + } + final GLAutoDrawable glad; + final Component comp; + } + public void test(final GLCapabilitiesImmutable caps, final boolean useGears, final boolean skipGLOrientationVerticalFlip, final int width, - final int height, final int frameCount, final boolean initMT, final boolean useGLJPanel, - final boolean useSwingDoubleBuffer, final boolean useGLCanvas, final boolean useAnim, final boolean overlap) { + final int height, final int frameCount, final boolean initMT, + final boolean useSwingDoubleBuffer, final CanvasType canvasType, final boolean useAnim, final boolean overlap) { final GLAnimatorControl animator; if( useAnim ) { animator = new Animator(); - animator.start(); } else { animator = null; } @@ -91,6 +105,8 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { System.err.println("Frame size: "+width+"x"+height+" -> "+frameCount+" x "+eWidth+"x"+eHeight+", overlap "+overlap); System.err.println("SkipGLOrientationVerticalFlip "+skipGLOrientationVerticalFlip+", useGears "+useGears+", initMT "+initMT+", useAnim "+useAnim); final JFrame[] frame = new JFrame[frameCount]; + final List<NewtCanvasAWT> newtCanvasAWTList = new ArrayList<NewtCanvasAWT>(); + final long[] t = new long[10]; if( wait ) { UITestCase.waitForKey("Pre-Init"); @@ -103,7 +119,7 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { t[0] = Platform.currentTimeMillis(); int x = 32, y = 32; for(int i=0; i<frameCount; i++) { - frame[i] = new JFrame("frame_"+i+"/"+frameCount); + frame[i] = new JFrame(i+"/"+frameCount); frame[i].setLocation(x, y); if(!overlap) { x+=eWidth+32; @@ -117,9 +133,26 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { panel.setDoubleBuffered(useSwingDoubleBuffer); // panel.setBounds(0, 0, width, height); final Dimension eSize = new Dimension(eWidth, eHeight); - final GLAutoDrawable glad = useGLJPanel ? createGLJPanel(initMT, useSwingDoubleBuffer, caps, useGears, skipGLOrientationVerticalFlip, animator, eSize) : ( useGLCanvas ? createGLCanvas(caps, useGears, animator, eSize) : null ); - if( null != glad ) { - glad.addGLEventListener(new GLEventListener() { + final GLADComp gladComp; + switch(canvasType) { + case GLCanvas_T: + gladComp = createGLCanvas(caps, useGears, animator, eSize); + break; + case GLJPanel_T: + gladComp = createGLJPanel(initMT, useSwingDoubleBuffer, caps, useGears, skipGLOrientationVerticalFlip, animator, eSize); + break; + case NewtCanvasAWT_T: + gladComp = createNewtCanvasAWT(caps, useGears, animator, eSize); + newtCanvasAWTList.add((NewtCanvasAWT)gladComp.comp); + break; + case NOP_T: + gladComp = null; + break; + default: throw new InternalError("XXX"); + } + + if( null != gladComp ) { + gladComp.glad.addGLEventListener(new GLEventListener() { @Override public void init(final GLAutoDrawable drawable) { initCount.incrementAndGet(); @@ -131,7 +164,7 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { @Override public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {} }); - panel.add((Component)glad); + panel.add(gladComp.comp); } else { @SuppressWarnings("serial") final JTextArea c = new JTextArea("area "+i) { @@ -192,7 +225,7 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { final double panelCountF = initCount.get(); System.err.printf("P: %d %s%s:%n\tctor\t%6d/t %6.2f/1%n\tvisible\t%6d/t %6.2f/1%n\tsum-i\t%6d/t %6.2f/1%n", initCount.get(), - useGLJPanel?"GLJPanel":(useGLCanvas?"GLCanvas":"No_GL"), initMT?" (mt)":" (01)", + canvasType, initMT?" (mt)":" (01)", t[1]-t[0], (t[1]-t[0])/panelCountF, t[3]-t[1], (t[3]-t[1])/panelCountF, t[3]-t[0], (t[3]-t[0])/panelCountF); @@ -201,15 +234,24 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { if( wait ) { UITestCase.waitForKey("Post-Init"); } + if( null != animator ) { + animator.start(); + } try { Thread.sleep(duration); } catch (final InterruptedException e1) { e1.printStackTrace(); } + if( null != animator ) { + animator.stop(); + } t[4] = Platform.currentTimeMillis(); try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { + while( !newtCanvasAWTList.isEmpty() ) { + newtCanvasAWTList.remove(0).destroy(); // removeNotify does not destroy GLWindow + } for(int i=0; i<frameCount; i++) { frame[i].dispose(); } @@ -226,7 +268,22 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { System.err.println("Total: "+(t[4]-t[0])); } - private GLAutoDrawable createGLCanvas(final GLCapabilitiesImmutable caps, final boolean useGears, final GLAnimatorControl anim, final Dimension size) { + private GLADComp createNewtCanvasAWT(final GLCapabilitiesImmutable caps, final boolean useGears, final GLAnimatorControl anim, final Dimension size) { + final GLWindow window = GLWindow.create(caps); + final NewtCanvasAWT canvas = new NewtCanvasAWT(window); + canvas.setSize(size); + canvas.setPreferredSize(size); + if( useGears ) { + final GearsES2 g = new GearsES2(0); + g.setVerbose(false); + window.addGLEventListener(g); + } + if( null != anim ) { + anim.add(window); + } + return new GLADComp(window, canvas); + } + private GLADComp createGLCanvas(final GLCapabilitiesImmutable caps, final boolean useGears, final GLAnimatorControl anim, final Dimension size) { final GLCanvas canvas = new GLCanvas(caps); canvas.setSize(size); canvas.setPreferredSize(size); @@ -238,9 +295,9 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { if( null != anim ) { anim.add(canvas); } - return canvas; + return new GLADComp(canvas, canvas); } - private GLAutoDrawable createGLJPanel(final boolean initMT, final boolean useSwingDoubleBuffer, final GLCapabilitiesImmutable caps, final boolean useGears, final boolean skipGLOrientationVerticalFlip, final GLAnimatorControl anim, final Dimension size) { + private GLADComp createGLJPanel(final boolean initMT, final boolean useSwingDoubleBuffer, final GLCapabilitiesImmutable caps, final boolean useGears, final boolean skipGLOrientationVerticalFlip, final GLAnimatorControl anim, final Dimension size) { final GLJPanel canvas = new GLJPanel(caps); canvas.setSize(size); canvas.setPreferredSize(size); @@ -260,7 +317,7 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { if( initMT ) { canvas.initializeBackend(true /* offthread */); } - return canvas; + return new GLADComp(canvas, canvas); } static GLCapabilitiesImmutable caps = null; @@ -272,31 +329,37 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { @Test public void test00NopNoGLDefGrid() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), false /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - false /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, false /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.NOP_T, false /*useAnim*/, false /* overlap */); } @Test public void test01NopGLCanvasDefGrid() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), false /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - false /* useGLJPanel */, false /*useSwingDoubleBuffer*/, true /* useGLCanvas */, false /*useAnim*/, false /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.GLCanvas_T, false /*useAnim*/, false /* overlap */); } @Test public void test02NopGLJPanelDefGridSingleAutoFlip() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), false /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - true /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, false /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.GLJPanel_T, false /*useAnim*/, false /* overlap */); } @Test public void test03NopGLJPanelDefGridSingleManualFlip() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), false /*useGears*/, true /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - true /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, false /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.GLJPanel_T, false /*useAnim*/, false /* overlap */); } @Test public void test04NopGLJPanelDefGridMTManualFlip() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), false /*useGears*/, true /*skipGLOrientationVerticalFlip*/, width , height, frameCount, true /* initMT */, - true /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, false /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.GLJPanel_T, false /*useAnim*/, false /* overlap */); + } + + @Test + public void test05NopNewtCanvasAWTDefGrid() throws InterruptedException, InvocationTargetException { + test(new GLCapabilities(null), false /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, + false /*useSwingDoubleBuffer*/, CanvasType.NewtCanvasAWT_T, false /*useAnim*/, false /* overlap */); } // @@ -304,35 +367,34 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { // @Test - public void test10GearsNoGLDefGrid() throws InterruptedException, InvocationTargetException { - test(new GLCapabilities(null), true /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - false /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, false /* overlap */); - } - - @Test public void test11GearsGLCanvasDefGrid() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), true /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - false /* useGLJPanel */, false /*useSwingDoubleBuffer*/, true /* useGLCanvas */, false /*useAnim*/, false /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.GLCanvas_T, true /*useAnim*/, false /* overlap */); } @Test public void test12GearsGLJPanelDefGridSingleAutoFlip() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), true /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - true /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, false /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.GLJPanel_T, true /*useAnim*/, false /* overlap */); } @Test public void test13GearsGLJPanelDefGridSingleManualFlip() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), true /*useGears*/, true /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - true /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, false /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.GLJPanel_T, true /*useAnim*/, false /* overlap */); } @Test public void test14GearsGLJPanelDefGridMTManualFlip() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), true /*useGears*/, true /*skipGLOrientationVerticalFlip*/, width , height, frameCount, true /* initMT */, - true /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, false /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.GLJPanel_T, true /*useAnim*/, false /* overlap */); } + @Test + public void test15GearsNewtCanvasAWTDefGrid() throws InterruptedException, InvocationTargetException { + test(new GLCapabilities(null), true /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, + false /*useSwingDoubleBuffer*/, CanvasType.NewtCanvasAWT_T, true /*useAnim*/, false /* overlap */); + } // // Overlap + NOP @@ -342,31 +404,37 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { @Test public void test20NopNoGLDefOverlap() throws InterruptedException, InvocationTargetException { test(null, false /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - false /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, true /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.NOP_T, false /*useAnim*/, true /* overlap */); } @Test public void test21NopGLCanvasDefOverlap() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), false /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - false /* useGLJPanel */, false /*useSwingDoubleBuffer*/, true /* useGLCanvas */, false /*useAnim*/, true /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.GLCanvas_T, false /*useAnim*/, true /* overlap */); } @Test public void test22NopGLJPanelDefOverlapSingle() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), false /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - true /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, true /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.GLJPanel_T, false /*useAnim*/, true /* overlap */); } @Test public void test23NopGLJPanelDefOverlapMT() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), false /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, true /* initMT */, - true /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, true /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.GLJPanel_T, false /*useAnim*/, true /* overlap */); + } + + @Test + public void test25NopNewtCanvasAWTDefOverlap() throws InterruptedException, InvocationTargetException { + test(new GLCapabilities(null), false /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, + false /*useSwingDoubleBuffer*/, CanvasType.NewtCanvasAWT_T, false /*useAnim*/, true /* overlap */); } // @Test public void testXXNopGLJPanelDefOverlapSingle() throws InterruptedException, InvocationTargetException { test(new GLCapabilities(null), false /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - true /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, true /* overlap */); + false /*useSwingDoubleBuffer*/, CanvasType.GLJPanel_T, false /*useAnim*/, true /* overlap */); } // @Test @@ -374,7 +442,7 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { final GLCapabilities caps = new GLCapabilities(null); caps.setBitmap(true); test(caps, false /*useGears*/, false /*skipGLOrientationVerticalFlip*/, width , height, frameCount, false /* initMT */, - true /* useGLJPanel */, false /*useSwingDoubleBuffer*/, false /* useGLCanvas */, false /*useAnim*/, false); + false /*useSwingDoubleBuffer*/, CanvasType.GLJPanel_T, false /*useAnim*/, false); } static long duration = 0; // ms @@ -386,7 +454,8 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { public static void main(final String[] args) { boolean manual=false; boolean waitMain = false; - boolean useGLJPanel = true, initMT = false, useGLCanvas = false, useSwingDoubleBuffer=false; + CanvasType canvasType = CanvasType.GLJPanel_T; + boolean initMT = false, useSwingDoubleBuffer=false; boolean useGears = false, skipGLOrientationVerticalFlip=false, useAnim = false; boolean overlap = false; @@ -403,16 +472,12 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { } else if(args[i].equals("-initMT")) { initMT = true; manual = true; - } else if(args[i].equals("-glcanvas")) { - useGLJPanel = false; - useGLCanvas = true; + } else if(args[i].equals("-type")) { + i++; + canvasType = CanvasType.valueOf(args[i]); manual = true; } else if(args[i].equals("-swingDoubleBuffer")) { useSwingDoubleBuffer = true; - } else if(args[i].equals("-glnone")) { - useGLJPanel = false; - useGLCanvas = false; - manual = true; } else if(args[i].equals("-gears")) { useGears = true; } else if(args[i].equals("-anim")) { @@ -438,7 +503,7 @@ public class TestPerf001GLJPanelInit02AWT extends UITestCase { GLProfile.initSingleton(); final TestPerf001GLJPanelInit02AWT demo = new TestPerf001GLJPanelInit02AWT(); demo.test(null, useGears, skipGLOrientationVerticalFlip, width, height, frameCount, - initMT, useGLJPanel, useSwingDoubleBuffer, useGLCanvas, useAnim, overlap); + initMT, useSwingDoubleBuffer, canvasType, useAnim, overlap); } else { org.junit.runner.JUnitCore.main(TestPerf001GLJPanelInit02AWT.class.getName()); } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/stereo/StereoDemo01.java b/src/test/com/jogamp/opengl/test/junit/jogl/stereo/StereoDemo01.java index 05685c05a..58aa8cd23 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/stereo/StereoDemo01.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/stereo/StereoDemo01.java @@ -28,7 +28,6 @@ package com.jogamp.opengl.test.junit.jogl.stereo; import java.io.File; -import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; @@ -40,8 +39,7 @@ import javax.media.opengl.GLProfile; import jogamp.opengl.util.stereo.GenericStereoDevice; -import com.jogamp.common.util.IOUtil; -import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.common.net.Uri; import com.jogamp.newt.event.KeyAdapter; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.opengl.GLWindow; @@ -88,6 +86,16 @@ import com.jogamp.opengl.util.stereo.StereoUtil; * java StereoDemo01 -time 10000000 -filmURI http://whoknows.not/Some_SBS_3D_Movie.mkv * </pre> * <p> + * In case user likes to utilize the {@link StereoDeviceFactory.DeviceType#Generic Generic} software implementation, + * which is selected {@link StereoDeviceFactory.DeviceType#Default Default} if no other device is available + * or explicit via <code>-device Generic</code>, the user can chose between different <i>generic</i> stereo modes: + * <pre> + * mono : <code>-device Generic -deviceIndex 0</code> + * stereo-sbs : <code>-device Generic -deviceIndex 1</code> + * stereo-sbs-lense: <code>-device Generic -deviceIndex 2</code> + * </pre> + * </p> + * <p> * Key 'R' enables/disables the VR's sensors, i.e. head rotation .. * </p> * @@ -106,7 +114,6 @@ public class StereoDemo01 { static boolean useAutoSwap = false; static String useFilmFile = null; static String useFilmURI = null; - static String stereoRendererListenerName = null; static StereoDeviceFactory.DeviceType deviceType = StereoDeviceFactory.DeviceType.Default; static int deviceIndex = 0; @@ -162,9 +169,6 @@ public class StereoDemo01 { } else if(args[i].equals("-autoSwap")) { i++; useAutoSwap = MiscUtils.atob(args[i], useAutoSwap); - } else if(args[i].equals("-test")) { - i++; - stereoRendererListenerName = args[i]; } else if(args[i].equals("-filmFile")) { i++; useFilmFile = args[i]; @@ -173,26 +177,20 @@ public class StereoDemo01 { useFilmURI = args[i]; } } - if( null != stereoRendererListenerName ) { - try { - final Object stereoRendererListener = ReflectionUtil.createInstance(stereoRendererListenerName, null); - } catch (final Exception e) { - e.printStackTrace(); - } - } final StereoGLEventListener upstream; final MovieSBSStereo movieSimple; - final URI movieURI; + final Uri movieURI; if( null != useFilmFile ) { movieSimple = new MovieSBSStereo(); - movieURI = IOUtil.toURISimple(new File(useFilmFile)); + movieURI = Uri.valueOf(new File(useFilmFile)); upstream = movieSimple; } else if( null != useFilmURI ) { movieSimple = new MovieSBSStereo(); - movieURI = new URI(useFilmURI); + movieURI = Uri.cast(useFilmURI); upstream = movieSimple; } else { final GearsES2 demo = new GearsES2(0); + demo.setZ(2f, 10000f, 20f); // start closer to eye demo.setVerbose(false); upstream = demo; movieSimple = null; @@ -206,7 +204,7 @@ public class StereoDemo01 { } public void doIt(final StereoDeviceFactory.DeviceType deviceType, final int deviceIndex, final int posx, final int posy, - final StereoGLEventListener upstream, final MovieSBSStereo movieSimple, final URI movieURI, + final StereoGLEventListener upstream, final MovieSBSStereo movieSimple, final Uri movieURI, final boolean biLinear, final int numSamples, final boolean useSingleFBO, final boolean useRecommendedDistortionBits, final boolean useVignette, final boolean useChromatic, final boolean useTimewarp, final boolean useAutoSwap, final boolean useAnimator, final boolean exclusiveContext) throws InterruptedException { @@ -256,10 +254,10 @@ public class StereoDemo01 { final PointImmutable devicePos = stereoDevice.getPosition(); final DimensionImmutable deviceRes = stereoDevice.getSurfaceSize(); - window.setSize(deviceRes.getWidth(), deviceRes.getHeight()); if( useStereoScreen ) { window.setPosition(devicePos.getX(), devicePos.getY()); } + window.setSurfaceSize(deviceRes.getWidth(), deviceRes.getHeight()); // might be not correct .. window.setAutoSwapBufferMode(useAutoSwap); window.setUndecorated(true); @@ -281,8 +279,9 @@ public class StereoDemo01 { System.err.println("Default Fov[1]: "+defaultEyeFov[1].toStringInDegrees()); } - final float[] eyePositionOffset = null == movieSimple || isGenericDevice ? stereoDevice.getDefaultEyePositionOffset() // default - : new float[] { 0f, 0.3f, 0f }; // better fixed movie position + final boolean usesLenses = 0 != ( StereoDeviceRenderer.DISTORTION_BARREL & stereoDevice.getMinimumDistortionBits() ); + final float[] eyePositionOffset = null != movieSimple && usesLenses ? new float[] { 0f, 0.3f, 0f } // better fixed movie position w/ lenses + : stereoDevice.getDefaultEyePositionOffset(); // default System.err.println("Eye Position Offset: "+Arrays.toString(eyePositionOffset)); final int textureUnit = 0; @@ -338,6 +337,15 @@ public class StereoDemo01 { animator.start(); } window.setVisible(true); + + // Correct window size to actual pixel size, + // which ration is unknown before window creation when using multiple displays! + System.err.println("Window.0.windowSize : "+window.getWidth()+" x "+window.getHeight()); + System.err.println("Window.0.surfaceSize: "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight()); + window.setSurfaceSize(deviceRes.getWidth(), deviceRes.getHeight()); + System.err.println("Window.1.windowSize : "+window.getWidth()+" x "+window.getHeight()); + System.err.println("Window.1.surfaceSize: "+window.getSurfaceWidth()+" x "+window.getSurfaceHeight()); + if( useAnimator ) { animator.setUpdateFPSFrames(60, System.err); } diff --git a/src/test/com/jogamp/opengl/test/junit/newt/TestMultipleNewtCanvasAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/TestMultipleNewtCanvasAWT.java new file mode 100644 index 000000000..0e11ff1e8 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/newt/TestMultipleNewtCanvasAWT.java @@ -0,0 +1,185 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.newt; + +import java.awt.Component; +import java.awt.Dimension; +import java.io.IOException; + +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.FixMethodOrder; +import org.junit.runners.MethodSorters; + +import com.jogamp.newt.awt.NewtCanvasAWT; +import com.jogamp.newt.opengl.GLWindow; +import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; +import com.jogamp.opengl.test.junit.util.AWTRobotUtil; +import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.Animator; + + +/** + * TestMultipleNewtCanvasAWT + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestMultipleNewtCanvasAWT extends UITestCase { + + static long durationPerTest = 1000; + + @BeforeClass + public static void initClass() { + if(!GLProfile.isAvailable(GLProfile.GL2ES2)) { + setTestSupported(false); + } + } + + @Test + public void test01() throws InterruptedException { + testImpl(); + } + + public void testImpl() throws InterruptedException { + final JFrame frame = new JFrame(this.getSimpleTestName(".")); + + // + // GLDrawableFactory factory = GLDrawableFactory.getFactory(GLProfile.get(GLProfile.GL2)); + // GLContext sharedContext = factory.getOrCreateSharedContext(factory.getDefaultDevice()); + // + final GLCapabilities glCapabilities = new GLCapabilities(GLProfile.get(GLProfile.GL2)); + glCapabilities.setSampleBuffers(true); + glCapabilities.setNumSamples(4); + + final GearsES2 eventListener1 = new GearsES2(0); + final GearsES2 eventListener2 = new GearsES2(1); + + final Component openGLComponent1; + final Component openGLComponent2; + final GLAutoDrawable openGLAutoDrawable1; + final GLAutoDrawable openGLAutoDrawable2; + + final GLWindow glWindow1 = GLWindow.create(glCapabilities); + final NewtCanvasAWT newtCanvasAWT1 = new NewtCanvasAWT(glWindow1); + newtCanvasAWT1.setPreferredSize(new Dimension(640, 480)); + glWindow1.addGLEventListener(eventListener1); + // + final GLWindow glWindow2 = GLWindow.create(glCapabilities); + final NewtCanvasAWT newtCanvasAWT2 = new NewtCanvasAWT(glWindow2); + newtCanvasAWT2.setPreferredSize(new Dimension(640, 480)); + glWindow2.addGLEventListener(eventListener2); + + openGLComponent1 = newtCanvasAWT1; + openGLComponent2 = newtCanvasAWT2; + openGLAutoDrawable1 = glWindow1; + openGLAutoDrawable2 = glWindow2; + + // group both OpenGL canvases / windows into a horizontal panel + final JPanel openGLPanel = new JPanel(); + openGLPanel.setLayout(new BoxLayout(openGLPanel, BoxLayout.LINE_AXIS)); + openGLPanel.add(openGLComponent1); + openGLPanel.add(Box.createHorizontalStrut(5)); + openGLPanel.add(openGLComponent2); + + final JPanel mainPanel = (JPanel) frame.getContentPane(); + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.LINE_AXIS)); + mainPanel.add(Box.createHorizontalGlue()); + mainPanel.add(openGLPanel); + mainPanel.add(Box.createHorizontalGlue()); + + final Animator animator = new Animator(Thread.currentThread().getThreadGroup()); + animator.setUpdateFPSFrames(1, null); + animator.add(openGLAutoDrawable1); + animator.add(openGLAutoDrawable2); + + // make the window visible using the EDT + SwingUtilities.invokeLater( new Runnable() { + public void run() { + frame.pack(); + frame.setVisible(true); + } + }); + + Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, true)); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(openGLComponent1, true)); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(openGLComponent2, true)); + + animator.start(); + + // sleep for test duration, then request the window to close, wait for the window to close,s and stop the animation + while(animator.isAnimating() && animator.getTotalFPSDuration() < durationPerTest) { + Thread.sleep(100); + } + + animator.stop(); + + // ask the EDT to dispose of the frame; + // if using newt, explicitly dispose of the canvases because otherwise it seems our destroy methods are not called + SwingUtilities.invokeLater( new Runnable() { + public void run() { + newtCanvasAWT1.destroy(); // removeNotify does not destroy GLWindow + newtCanvasAWT2.destroy(); // removeNotify does not destroy GLWindow + frame.dispose(); + } + }); + Assert.assertEquals(true, AWTRobotUtil.waitForVisible(frame, false)); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(openGLComponent1, false)); + Assert.assertEquals(true, AWTRobotUtil.waitForRealized(openGLComponent2, false)); + } + + static int atoi(final String a) { + int i=0; + try { + i = Integer.parseInt(a); + } catch (final Exception ex) { ex.printStackTrace(); } + return i; + } + + public static void main(final String[] args) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + if (++i < args.length) { + durationPerTest = atoi(args[i]); + } + } + } + org.junit.runner.JUnitCore.main(TestMultipleNewtCanvasAWT.class.getName()); + } + +} + diff --git a/src/test/com/jogamp/opengl/test/junit/util/MiscUtils.java b/src/test/com/jogamp/opengl/test/junit/util/MiscUtils.java index e401534bd..7100e1e1a 100644 --- a/src/test/com/jogamp/opengl/test/junit/util/MiscUtils.java +++ b/src/test/com/jogamp/opengl/test/junit/util/MiscUtils.java @@ -214,21 +214,23 @@ public class MiscUtils { } } - public static void dumpSharedGLContext(final GLContext self) { + public static void dumpSharedGLContext(final String prefix, final GLContext self) { int i = 0, j = 0; - System.err.println("Myself: hash 0x"+Integer.toHexString(self.hashCode())+", \t(isShared "+self.isShared()+", created "+self.isCreated()+")"); + final GLContext master = self.getSharedMaster(); + final int masterHash = null != master ? master.hashCode() : 0; + System.err.println(prefix+": hash 0x"+Integer.toHexString(self.hashCode())+", \t(isShared "+self.isShared()+", created "+self.isCreated()+", master 0x"+Integer.toHexString(masterHash)+")"); { final List<GLContext> set = self.getCreatedShares(); for (final Iterator<GLContext> iter = set.iterator(); iter.hasNext(); ) { final GLContext c = iter.next(); - System.err.println("Ctx #"+(i++)+": hash 0x"+Integer.toHexString(c.hashCode())+", \t(created "+c.isCreated()+")"); + System.err.println(" Created Ctx #"+(i++)+": hash 0x"+Integer.toHexString(c.hashCode())+", \t(created "+c.isCreated()+")"); } } { final List<GLContext> set = self.getDestroyedShares(); for (final Iterator<GLContext> iter = set.iterator(); iter.hasNext(); ) { final GLContext c = iter.next(); - System.err.println("Ctx #"+(j++)+": hash 0x"+Integer.toHexString(c.hashCode())+", \t(created "+c.isCreated()+")"); + System.err.println(" Destroyed Ctx #"+(j++)+": hash 0x"+Integer.toHexString(c.hashCode())+", \t(created "+c.isCreated()+")"); } } System.err.println("\t Total created "+i+" + destroyed "+j+" = "+(i+j)); diff --git a/src/test/com/jogamp/opengl/test/junit/util/NEWTGLContext.java b/src/test/com/jogamp/opengl/test/junit/util/NEWTGLContext.java index cd3a1eaa2..d897a51a1 100644 --- a/src/test/com/jogamp/opengl/test/junit/util/NEWTGLContext.java +++ b/src/test/com/jogamp/opengl/test/junit/util/NEWTGLContext.java @@ -53,45 +53,7 @@ public class NEWTGLContext { } } - public static WindowContext createOffscreenWindow(final GLCapabilities caps, final int width, final int height, final boolean debugGL) throws InterruptedException { - caps.setOnscreen(false); - caps.setPBuffer(true); - - // - // Create native windowing resources .. X11/Win/OSX - // - final Display display = NewtFactory.createDisplay(null); // local display - Assert.assertNotNull(display); - - final Screen screen = NewtFactory.createScreen(display, 0); // screen 0 - Assert.assertNotNull(screen); - - final Window window = NewtFactory.createWindow(screen, caps); - Assert.assertNotNull(window); - window.setSize(width, height); - window.setVisible(true); - Assert.assertTrue(AWTRobotUtil.waitForVisible(window, true)); - Assert.assertTrue(AWTRobotUtil.waitForRealized(window, true)); - - final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); - final GLDrawable drawable = factory.createGLDrawable(window); - Assert.assertNotNull(drawable); - - drawable.setRealized(true); - Assert.assertTrue(drawable.isRealized()); - - final GLContext context = drawable.createContext(null); - Assert.assertNotNull(context); - - context.enableGLDebugMessage(debugGL); - - final int res = context.makeCurrent(); - Assert.assertTrue(GLContext.CONTEXT_CURRENT_NEW==res || GLContext.CONTEXT_CURRENT==res); - - return new WindowContext(window, drawable, context); - } - - public static WindowContext createOnscreenWindow(final GLCapabilities caps, final int width, final int height, final boolean debugGL) throws InterruptedException { + public static WindowContext createWindow(final GLCapabilities caps, final int width, final int height, final boolean debugGL) throws InterruptedException { // // Create native windowing resources .. X11/Win/OSX // |