diff options
Diffstat (limited to 'src/jogl/classes/javax')
13 files changed, 1031 insertions, 438 deletions
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; |