diff options
Diffstat (limited to 'src/jogl/classes/com/jogamp/opengl/util')
17 files changed, 787 insertions, 397 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java index 496fb88c0..62df3faca 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AWTAnimatorImpl.java @@ -41,12 +41,14 @@ import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; + import javax.swing.JComponent; import javax.swing.RepaintManager; import javax.swing.SwingUtilities; - import javax.media.opengl.GLAutoDrawable; +import com.jogamp.opengl.util.AnimatorBase.UncaughtAnimatorException; + /** Abstraction to factor out AWT dependencies from the Animator's implementation in a way that still allows the FPSAnimator to pick up this behavior if desired. */ @@ -61,7 +63,7 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { @Override public void display(final ArrayList<GLAutoDrawable> drawables, final boolean ignoreExceptions, - final boolean printExceptions) { + final boolean printExceptions) throws UncaughtAnimatorException { for (int i=0; i<drawables.size(); i++) { final GLAutoDrawable drawable = drawables.get(i); if (drawable instanceof JComponent) { @@ -73,13 +75,13 @@ class AWTAnimatorImpl implements AnimatorBase.AnimatorImpl { } else { try { drawable.display(); - } catch (final RuntimeException e) { + } catch (final Throwable t) { if (ignoreExceptions) { if (printExceptions) { - e.printStackTrace(); + t.printStackTrace(); } } else { - throw(e); + throw new UncaughtAnimatorException(drawable, t); } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/Animator.java b/src/jogl/classes/com/jogamp/opengl/util/Animator.java index ae09d81a4..b38a42ee3 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/Animator.java +++ b/src/jogl/classes/com/jogamp/opengl/util/Animator.java @@ -58,12 +58,12 @@ import javax.media.opengl.GLException; * </p> */ public class Animator extends AnimatorBase { - protected ThreadGroup threadGroup; + private ThreadGroup threadGroup; private Runnable runnable; private boolean runAsFastAsPossible; - protected boolean isAnimating; - protected boolean pauseIssued; - protected volatile boolean stopIssued; + boolean isAnimating; + volatile boolean pauseIssued; + volatile boolean stopIssued; /** * Creates a new, empty Animator. @@ -132,6 +132,9 @@ public class Animator extends AnimatorBase { @Override public void run() { + ThreadDeath caughtThreadDeath = null; + UncaughtAnimatorException caughtException = null; + try { synchronized (Animator.this) { if(DEBUG) { @@ -147,7 +150,7 @@ public class Animator extends AnimatorBase { synchronized (Animator.this) { // Pause; Also don't consume CPU unless there is work to be done and not paused boolean ectCleared = false; - while (!stopIssued && (pauseIssued || drawablesEmpty)) { + while ( !stopIssued && ( pauseIssued || drawablesEmpty ) ) { if( drawablesEmpty ) { pauseIssued = true; } @@ -158,7 +161,13 @@ public class Animator extends AnimatorBase { if ( exclusiveContext && !drawablesEmpty && !ectCleared ) { ectCleared = true; setDrawablesExclCtxState(false); - display(); // propagate exclusive change! + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + caughtException = dre; + stopIssued = true; + break; // end pause loop + } } isAnimating = false; Animator.this.notifyAll(); @@ -180,38 +189,72 @@ public class Animator extends AnimatorBase { // Resume from pause or drawablesEmpty, // implies !pauseIssued and !drawablesEmpty isAnimating = true; - setDrawablesExclCtxState(exclusiveContext); + setDrawablesExclCtxState(exclusiveContext); // may re-enable exclusive context Animator.this.notifyAll(); } } // sync Animator.this - if (!stopIssued) { - display(); - } - if (!stopIssued && !runAsFastAsPossible) { - // Avoid swamping the CPU - Thread.yield(); + if ( !pauseIssued && !stopIssued ) { + try { + display(); + } catch (final UncaughtAnimatorException dre) { + caughtException = dre; + stopIssued = true; + break; // end animation loop + } + if ( !runAsFastAsPossible ) { + // Avoid swamping the CPU + Thread.yield(); + } } } - } catch( final ThreadDeath td) { + } catch(final ThreadDeath td) { if(DEBUG) { System.err.println("Animator caught: "+td.getClass().getName()+": "+td.getMessage()); td.printStackTrace(); } - } finally { - if( exclusiveContext && !drawablesEmpty ) { - setDrawablesExclCtxState(false); - display(); // propagate exclusive change! + caughtThreadDeath = td; + } + if( exclusiveContext && !drawablesEmpty ) { + setDrawablesExclCtxState(false); + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + if( null == caughtException ) { + caughtException = dre; + } else { + System.err.println("Animator.setExclusiveContextThread: caught: "+dre.getMessage()); + dre.printStackTrace(); + } } - synchronized (Animator.this) { - if(DEBUG) { - System.err.println("Animator stop on " + animThread.getName() + ": " + toString()); + } + boolean flushGLRunnables = false; + boolean throwCaughtException = false; + synchronized (Animator.this) { + if(DEBUG) { + System.err.println("Animator stop on " + animThread.getName() + ": " + toString()); + if( null != caughtException ) { + System.err.println("Animator caught: "+caughtException.getMessage()); + caughtException.printStackTrace(); } - stopIssued = false; - pauseIssued = false; - animThread = null; - isAnimating = false; - Animator.this.notifyAll(); } + stopIssued = false; + pauseIssued = false; + isAnimating = false; + if( null != caughtException ) { + flushGLRunnables = true; + throwCaughtException = !handleUncaughtException(caughtException); + } + animThread = null; + Animator.this.notifyAll(); + } + if( flushGLRunnables ) { + flushGLRunnables(); + } + if( throwCaughtException ) { + throw caughtException; + } + if( null != caughtThreadDeath ) { + throw caughtThreadDeath; } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java index a5bdb9350..bc159ef5c 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java +++ b/src/jogl/classes/com/jogamp/opengl/util/AnimatorBase.java @@ -67,9 +67,26 @@ public abstract class AnimatorBase implements GLAnimatorControl { */ public static final int MODE_EXPECT_AWT_RENDERING_THREAD = 1 << 0; - public interface AnimatorImpl { - void display(ArrayList<GLAutoDrawable> drawables, boolean ignoreExceptions, boolean printExceptions); - boolean blockUntilDone(Thread thread); + + @SuppressWarnings("serial") + public static class UncaughtAnimatorException extends RuntimeException { + final GLAutoDrawable drawable; + public UncaughtAnimatorException(final GLAutoDrawable drawable, final Throwable cause) { + super(cause); + this.drawable = drawable; + } + public final GLAutoDrawable getGLAutoDrawable() { return drawable; } + } + + public static interface AnimatorImpl { + /** + * @param drawables + * @param ignoreExceptions + * @param printExceptions + * @throws UncaughtAnimatorException as caused by {@link GLAutoDrawable#display()} + */ + void display(final ArrayList<GLAutoDrawable> drawables, final boolean ignoreExceptions, final boolean printExceptions) throws UncaughtAnimatorException; + boolean blockUntilDone(final Thread thread); } protected int modeBits; @@ -83,6 +100,7 @@ public abstract class AnimatorBase implements GLAnimatorControl { protected boolean printExceptions; protected boolean exclusiveContext; protected Thread userExclusiveContextThread; + protected UncaughtExceptionHandler uncaughtExceptionHandler; protected FPSCounterImpl fpsCounter = new FPSCounterImpl(); private final static Class<?> awtAnimatorImplClazz; @@ -313,11 +331,16 @@ public abstract class AnimatorBase implements GLAnimatorControl { } } final Thread dECT = enable ? ( null != _exclusiveContextThread ? _exclusiveContextThread : animThread ) : null ; + UncaughtAnimatorException displayCaught = null; if( propagateState ) { setDrawablesExclCtxState(enable); if( !enable ) { if( Thread.currentThread() == getThread() || Thread.currentThread() == _exclusiveContextThread ) { - display(); + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + displayCaught = dre; + } } else { final boolean resumed = isAnimating() ? false : resume(); int counter = 10; @@ -338,6 +361,13 @@ public abstract class AnimatorBase implements GLAnimatorControl { } if(DEBUG) { System.err.println("AnimatorBase.setExclusiveContextThread: all-GLAD Ok: "+validateDrawablesExclCtxState(dECT)+", "+this); + if( null != displayCaught ) { + System.err.println("AnimatorBase.setExclusiveContextThread: caught: "+displayCaught.getMessage()); + displayCaught.printStackTrace(); + } + } + if( null != displayCaught ) { + throw displayCaught; } return oldExclusiveContext; } @@ -412,7 +442,7 @@ public abstract class AnimatorBase implements GLAnimatorControl { this to get the most optimized painting behavior for the set of components this Animator manages, in particular when multiple lightweight widgets are continually being redrawn. */ - protected final void display() { + protected final void display() throws UncaughtAnimatorException { impl.display(drawables, ignoreExceptions, printExceptions); fpsCounter.tickFPS(); } @@ -482,7 +512,47 @@ public abstract class AnimatorBase implements GLAnimatorControl { this.printExceptions = printExceptions; } - protected interface Condition { + @Override + public final UncaughtExceptionHandler getUncaughtExceptionHandler() { + return uncaughtExceptionHandler; + } + + @Override + public final void setUncaughtExceptionHandler(final UncaughtExceptionHandler handler) { + uncaughtExceptionHandler = handler; + } + + /** + * Should be called in case of an uncaught exception + * from within the animator thread, throws given exception if no handler has been installed. + * @return {@code true} if handled, otherwise {@code false}. In case of {@code false}, + * caller needs to propagate the exception. + */ + protected final synchronized boolean handleUncaughtException(final UncaughtAnimatorException ue) { + if( null != uncaughtExceptionHandler ) { + try { + uncaughtExceptionHandler.uncaughtException(this, ue.getGLAutoDrawable(), ue.getCause()); + } catch (final Throwable t) { /* ignore intentionally */ } + return true; + } else { + return false; + } + } + + /** + * Should be called in case of an uncaught exception + * from within the animator thread to flush all animator. + * <p> + * The animator instance shall not be locked when calling this method! + * </p> + */ + protected final void flushGLRunnables() { + for (int i=0; i<drawables.size(); i++) { + drawables.get(i).flushGLRunnables(); + } + } + + protected static interface Condition { /** * @return true if branching (continue waiting, action), otherwise false */ @@ -493,7 +563,8 @@ public abstract class AnimatorBase implements GLAnimatorControl { * @param waitCondition method will wait until TO is reached or {@link Condition#eval() waitCondition.eval()} returns <code>false</code>. * @param pollPeriod if <code>0</code>, method will wait until TO is reached or being notified. * if > <code>0</code>, method will wait for the given <code>pollPeriod</code> in milliseconds. - * @return <code>true</code> if {@link Condition#eval() waitCondition.eval()} returned <code>false</code>, otherwise <code>false</code>. + * @return <code>true</code> if {@link Condition#eval() waitCondition.eval()} returned <code>false</code> + * or if {@link AnimatorImpl#blockUntilDone(Thread) non-blocking}. Otherwise returns <code>false</code>. */ protected final synchronized boolean finishLifecycleAction(final Condition waitCondition, long pollPeriod) { /** @@ -506,6 +577,7 @@ public abstract class AnimatorBase implements GLAnimatorControl { final boolean blocking; long remaining; boolean nok; + if( impl.blockUntilDone(animThread) ) { blocking = true; remaining = TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION; @@ -539,12 +611,13 @@ public abstract class AnimatorBase implements GLAnimatorControl { nok = waitCondition.eval(); } } + final boolean res = !nok || !blocking; if(DEBUG || blocking && nok) { // Info only if DEBUG or ( blocking && not-ok ) ; !blocking possible if AWT if( blocking && remaining<=0 && nok ) { System.err.println("finishLifecycleAction(" + waitCondition.getClass().getName() + "): ++++++ timeout reached ++++++ " + getThreadName()); } System.err.println("finishLifecycleAction(" + waitCondition.getClass().getName() + "): OK "+(!nok)+ - "- pollPeriod "+pollPeriod+", blocking "+blocking+ + "- pollPeriod "+pollPeriod+", blocking "+blocking+" -> res "+res+ ", waited " + (blocking ? ( TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION - remaining ) : 0 ) + "/" + TO_WAIT_FOR_FINISH_LIFECYCLE_ACTION + " - " + getThreadName()); System.err.println(" - "+toString()); @@ -552,7 +625,7 @@ public abstract class AnimatorBase implements GLAnimatorControl { Thread.dumpStack(); } } - return !nok; + return res; } @Override diff --git a/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java b/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java index 7fa4011f8..6b1485a6a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/util/DefaultAnimatorImpl.java @@ -34,8 +34,11 @@ package com.jogamp.opengl.util; import java.util.ArrayList; + import javax.media.opengl.GLAutoDrawable; +import com.jogamp.opengl.util.AnimatorBase.UncaughtAnimatorException; + /** Abstraction to factor out AWT dependencies from the Animator's implementation in a way that still allows the FPSAnimator to pick up this behavior if desired. */ @@ -44,18 +47,18 @@ class DefaultAnimatorImpl implements AnimatorBase.AnimatorImpl { @Override public void display(final ArrayList<GLAutoDrawable> drawables, final boolean ignoreExceptions, - final boolean printExceptions) { + final boolean printExceptions) throws UncaughtAnimatorException { for (int i=0; i<drawables.size(); i++) { final GLAutoDrawable drawable = drawables.get(i); try { drawable.display(); - } catch (final RuntimeException e) { + } catch (final Throwable t) { if (ignoreExceptions) { if (printExceptions) { - e.printStackTrace(); + t.printStackTrace(); } } else { - throw(e); + throw new UncaughtAnimatorException(drawable, t); } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java index 746b642c2..54d40f285 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java +++ b/src/jogl/classes/com/jogamp/opengl/util/FPSAnimator.java @@ -60,9 +60,9 @@ public class FPSAnimator extends AnimatorBase { private MainTask task = null; private int fps; private final boolean scheduleAtFixedRate; - private boolean isAnimating; // MainTask feedback - private volatile boolean shouldRun; // MainTask trigger - private volatile boolean shouldStop; // MainTask trigger + private boolean isAnimating; // MainTask feedback + private volatile boolean pauseIssued; // MainTask trigger + private volatile boolean stopIssued; // MainTask trigger @Override protected String getBaseName(final String prefix) { @@ -124,8 +124,9 @@ public class FPSAnimator extends AnimatorBase { public void start(final Timer timer) { fpsCounter.resetFPSCounter(); - shouldRun = true; - shouldStop = false; + pauseIssued = false; + stopIssued = false; + isAnimating = false; justStarted = true; alreadyStopped = false; @@ -143,11 +144,13 @@ public class FPSAnimator extends AnimatorBase { @Override public final String toString() { - return "Task[thread "+animThread+", stopped "+alreadyStopped+", paused "+alreadyPaused+" shouldRun "+shouldRun+", shouldStop "+shouldStop+" -- started "+isStarted()+", animating "+isAnimatingImpl()+", paused "+isPaused()+", drawable "+drawables.size()+", drawablesEmpty "+drawablesEmpty+"]"; + return "Task[thread "+animThread+", stopped "+alreadyStopped+", paused "+alreadyPaused+" pauseIssued "+pauseIssued+", stopIssued "+stopIssued+" -- started "+isStarted()+", animating "+isAnimatingImpl()+", paused "+isPaused()+", drawable "+drawables.size()+", drawablesEmpty "+drawablesEmpty+"]"; } @Override public void run() { + UncaughtAnimatorException caughtException = null; + if( justStarted ) { justStarted = false; synchronized (FPSAnimator.this) { @@ -157,59 +160,97 @@ public class FPSAnimator extends AnimatorBase { } isAnimating = true; if( drawablesEmpty ) { - shouldRun = false; // isAnimating:=false @ pause below + pauseIssued = true; // isAnimating:=false @ pause below } else { - shouldRun = true; - setDrawablesExclCtxState(exclusiveContext); - FPSAnimator.this.notifyAll(); + pauseIssued = false; + setDrawablesExclCtxState(exclusiveContext); // may re-enable exclusive context } + FPSAnimator.this.notifyAll(); // Wakes up 'waitForStartedCondition' sync -and resume from pause or drawablesEmpty if(DEBUG) { System.err.println("FPSAnimator P1:" + Thread.currentThread() + ": " + toString()); } } } - if( shouldRun ) { - display(); - } else if( shouldStop ) { // STOP + if( !pauseIssued && !stopIssued ) { // RUN + try { + display(); + } catch (final UncaughtAnimatorException dre) { + caughtException = dre; + stopIssued = true; + } + } else if( pauseIssued && !stopIssued ) { // PAUSE if(DEBUG) { - System.err.println("FPSAnimator P4: "+alreadyStopped+", "+ Thread.currentThread() + ": " + toString()); + System.err.println("FPSAnimator pausing: "+alreadyPaused+", "+ Thread.currentThread() + ": " + toString()); } this.cancel(); - if( !alreadyStopped ) { - alreadyStopped = true; + if( !alreadyPaused ) { // PAUSE + alreadyPaused = true; if( exclusiveContext && !drawablesEmpty ) { setDrawablesExclCtxState(false); - display(); // propagate exclusive change! + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + caughtException = dre; + stopIssued = true; + } } - synchronized (FPSAnimator.this) { - if(DEBUG) { - System.err.println("FPSAnimator stop " + Thread.currentThread() + ": " + toString()); + if( null == caughtException ) { + synchronized (FPSAnimator.this) { + if(DEBUG) { + System.err.println("FPSAnimator pause " + Thread.currentThread() + ": " + toString()); + } + isAnimating = false; + FPSAnimator.this.notifyAll(); } - animThread = null; - isAnimating = false; - FPSAnimator.this.notifyAll(); } } - } else { + } + if( stopIssued ) { // STOP incl. immediate exception handling of 'displayCaught' if(DEBUG) { - System.err.println("FPSAnimator P5: "+alreadyPaused+", "+ Thread.currentThread() + ": " + toString()); + System.err.println("FPSAnimator stopping: "+alreadyStopped+", "+ Thread.currentThread() + ": " + toString()); } this.cancel(); - if( !alreadyPaused ) { // PAUSE - alreadyPaused = true; + if( !alreadyStopped ) { + alreadyStopped = true; if( exclusiveContext && !drawablesEmpty ) { setDrawablesExclCtxState(false); - display(); // propagate exclusive change! + try { + display(); // propagate exclusive context -> off! + } catch (final UncaughtAnimatorException dre) { + if( null == caughtException ) { + caughtException = dre; + } else { + System.err.println("FPSAnimator.setExclusiveContextThread: caught: "+dre.getMessage()); + dre.printStackTrace(); + } + } } + boolean flushGLRunnables = false; + boolean throwCaughtException = false; synchronized (FPSAnimator.this) { if(DEBUG) { - System.err.println("FPSAnimator pause " + Thread.currentThread() + ": " + toString()); + System.err.println("FPSAnimator stop " + Thread.currentThread() + ": " + toString()); + if( null != caughtException ) { + System.err.println("Animator caught: "+caughtException.getMessage()); + caughtException.printStackTrace(); + } } isAnimating = false; + if( null != caughtException ) { + flushGLRunnables = true; + throwCaughtException = !handleUncaughtException(caughtException); + } + animThread = null; FPSAnimator.this.notifyAll(); } + if( flushGLRunnables ) { + flushGLRunnables(); + } + if( throwCaughtException ) { + throw caughtException; + } } } } @@ -224,7 +265,7 @@ public class FPSAnimator extends AnimatorBase { @Override public final synchronized boolean isPaused() { - return animThread != null && ( !shouldRun && !shouldStop ) ; + return animThread != null && pauseIssued; } static int timerNo = 0; @@ -279,8 +320,7 @@ public class FPSAnimator extends AnimatorBase { // start/resume case w/ drawablesEmpty res = true; } else { - shouldRun = false; - shouldStop = true; + stopIssued = true; res = finishLifecycleAction(waitForStoppedCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION); } @@ -306,7 +346,7 @@ public class FPSAnimator extends AnimatorBase { @Override public final synchronized boolean pause() { - if ( !isStarted() || ( null != task && isPaused() ) ) { + if ( !isStarted() || pauseIssued ) { return false; } if(DEBUG) { @@ -317,7 +357,7 @@ public class FPSAnimator extends AnimatorBase { // start/resume case w/ drawablesEmpty res = true; } else { - shouldRun = false; + pauseIssued = true; res = finishLifecycleAction(waitForPausedCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION); } @@ -334,12 +374,12 @@ public class FPSAnimator extends AnimatorBase { @Override public boolean eval() { // end waiting if stopped as well - return isAnimating && isStarted(); + return isStarted() && isAnimating; } }; @Override public final synchronized boolean resume() { - if ( null != task || !isStarted() || !isPaused() ) { + if ( !isStarted() || !pauseIssued ) { return false; } if(DEBUG) { @@ -349,6 +389,14 @@ public class FPSAnimator extends AnimatorBase { if( drawablesEmpty ) { res = true; } else { + if( null != task ) { + if( DEBUG ) { + System.err.println("FPSAnimator.resume() Ops: !pauseIssued, but task != null: "+toString()); + Thread.dumpStack(); + } + task.cancel(); + task = null; + } task = new MainTask(); task.start(timer); res = finishLifecycleAction(waitForResumeCondition, POLLP_WAIT_FOR_FINISH_LIFECYCLE_ACTION); diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLBuffers.java b/src/jogl/classes/com/jogamp/opengl/util/GLBuffers.java index 44be29957..d4ab4e4f4 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLBuffers.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLBuffers.java @@ -46,6 +46,7 @@ import javax.media.opengl.GL2; import javax.media.opengl.GL2ES2; import javax.media.opengl.GL2ES3; import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLContext; import javax.media.opengl.GLES2; import javax.media.opengl.GLException; @@ -372,25 +373,29 @@ public class GLBuffers extends Buffers { if (pack) { alignment = glGetInteger(gl, GL.GL_PACK_ALIGNMENT, tmp); - if(gl.isGL2GL3()) { + if( gl.isGL2ES3() ) { rowLength = glGetInteger(gl, GL2ES3.GL_PACK_ROW_LENGTH, tmp); skipRows = glGetInteger(gl, GL2ES3.GL_PACK_SKIP_ROWS, tmp); skipPixels = glGetInteger(gl, GL2ES3.GL_PACK_SKIP_PIXELS, tmp); - if (depth > 1) { + if (depth > 1 && gl.isGL2GL3() && gl.getContext().getGLVersionNumber().compareTo(GLContext.Version120) >= 0 ) { imageHeight = glGetInteger(gl, GL2GL3.GL_PACK_IMAGE_HEIGHT, tmp); skipImages = glGetInteger(gl, GL2GL3.GL_PACK_SKIP_IMAGES, tmp); } } } else { alignment = glGetInteger(gl, GL.GL_UNPACK_ALIGNMENT, tmp); - if(gl.isGL2GL3 ()) { + if( gl.isGL2ES3() ) { rowLength = glGetInteger(gl, GL2ES2.GL_UNPACK_ROW_LENGTH, tmp); skipRows = glGetInteger(gl, GL2ES2.GL_UNPACK_SKIP_ROWS, tmp); skipPixels = glGetInteger(gl, GL2ES2.GL_UNPACK_SKIP_PIXELS, tmp); - if (depth > 1) { + if( depth > 1 && + ( gl.isGL3ES3() || + ( gl.isGL2GL3() && gl.getContext().getGLVersionNumber().compareTo(GLContext.Version120) >= 0 ) + ) + ) { imageHeight = glGetInteger(gl, GL2ES3.GL_UNPACK_IMAGE_HEIGHT, tmp); skipImages = glGetInteger(gl, GL2ES3.GL_UNPACK_SKIP_IMAGES, tmp); - } + } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java index c74284299..956693c4a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLDrawableUtil.java @@ -28,6 +28,7 @@ package com.jogamp.opengl.util; import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeSurface; import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLBase; @@ -36,7 +37,10 @@ import javax.media.opengl.GLContext; import javax.media.opengl.GLDrawable; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; +import javax.media.opengl.GLRunnable; +import javax.media.opengl.Threading; +import com.jogamp.common.util.locks.RecursiveLock; import com.jogamp.opengl.GLEventListenerState; import jogamp.opengl.Debug; @@ -45,148 +49,298 @@ import jogamp.opengl.Debug; * Providing utility functions dealing w/ {@link GLDrawable}s, {@link GLAutoDrawable} and their {@link GLEventListener}. */ public class GLDrawableUtil { - protected static final boolean DEBUG = Debug.debug("GLDrawable"); - - public static final boolean isAnimatorStartedOnOtherThread(final GLAnimatorControl animatorCtrl) { - return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ; - } - - public static final boolean isAnimatorStarted(final GLAnimatorControl animatorCtrl) { - return ( null != animatorCtrl ) ? animatorCtrl.isStarted() : false ; - } - - public static final boolean isAnimatorAnimatingOnOtherThread(final GLAnimatorControl animatorCtrl) { - return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ; - } - - public static final boolean isAnimatorAnimating(final GLAnimatorControl animatorCtrl) { - return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() : false ; - } - - /** - * Moves the designated {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>. - * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved - * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call. - * <p> - * Note that it is only legal to pass <code>preserveInitState := true</code>, - * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>. - * </p> - * <p> - * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}. - * </p> - * @param src - * @param dest - * @param listener - * @param preserveInitState - */ - public static final void moveGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final GLEventListener listener, final boolean preserveInitState) { - final boolean initialized = src.getGLEventListenerInitState(listener); - src.removeGLEventListener(listener); - dest.addGLEventListener(listener); - if(preserveInitState && initialized) { - dest.setGLEventListenerInitState(listener, true); - dest.invoke(false, new GLEventListenerState.ReshapeGLEventListener(listener)); - } // else .. !init state is default - } - - /** - * Moves all {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>. - * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved - * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call. - * <p> - * Note that it is only legal to pass <code>preserveInitState := true</code>, - * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>. - * </p> - * <p> - * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}. - * </p> - * @param src - * @param dest - * @param listener - * @param preserveInitState - */ - public static final void moveAllGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final boolean preserveInitState) { - for(int count = src.getGLEventListenerCount(); 0<count; count--) { - final GLEventListener listener = src.getGLEventListener(0); - moveGLEventListener(src, dest, listener, preserveInitState); + protected static final boolean DEBUG = Debug.debug("GLDrawable"); + + public static final boolean isAnimatorStartedOnOtherThread(final GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isStarted() && animatorCtrl.getThread() != Thread.currentThread() : false ; + } + + public static final boolean isAnimatorStarted(final GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isStarted() : false ; } - } - - /** - * Swaps the {@link GLContext} and all {@link GLEventListener} between {@link GLAutoDrawable} <code>a</code> and <code>b</code>, - * while preserving it's initialized state, resets the GL-Viewport and issuing {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)}. - * <p> - * The {@link GLAutoDrawable} to {@link GLAnimatorControl} association - * is also swapped. - * </p> - * <p> - * If an {@link GLAnimatorControl} is being attached to {@link GLAutoDrawable} <code>a</code> or <code>b</code> - * and the current thread is different than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. - * </p> - * @param a - * @param b - * @throws GLException if the {@link AbstractGraphicsDevice} are incompatible w/ each other. - */ - public static final void swapGLContextAndAllGLEventListener(final GLAutoDrawable a, final GLAutoDrawable b) { - final GLEventListenerState gllsA = GLEventListenerState.moveFrom(a); - final GLEventListenerState gllsB = GLEventListenerState.moveFrom(b); - - gllsA.moveTo(b); - gllsB.moveTo(a); - } - - /** - * Swaps the {@link GLContext} of given {@link GLAutoDrawable} - * and {@link GLAutoDrawable#disposeGLEventListener(GLEventListener, boolean) disposes} - * each {@link GLEventListener} w/o removing it. - * <p> - * The GL-Viewport is reset and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued implicit. - * </p> - * <p> - * If an {@link GLAnimatorControl} is being attached to GLAutoDrawable src or dest and the current thread is different - * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. - * </p> - * @param src - * @param dest - */ - public static final void swapGLContext(final GLAutoDrawable src, final GLAutoDrawable dest) { - final GLAnimatorControl aAnim = src.getAnimator(); - final GLAnimatorControl bAnim = dest.getAnimator(); - final boolean aIsPaused = isAnimatorAnimatingOnOtherThread(aAnim) && aAnim.pause(); - final boolean bIsPaused = isAnimatorAnimatingOnOtherThread(bAnim) && bAnim.pause(); - - for(int i = src.getGLEventListenerCount() - 1; 0 <= i; i--) { - src.disposeGLEventListener(src.getGLEventListener(i), false); + + public static final boolean isAnimatorAnimatingOnOtherThread(final GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() && animatorCtrl.getThread() != Thread.currentThread() : false ; + } + + public static final boolean isAnimatorAnimating(final GLAnimatorControl animatorCtrl) { + return ( null != animatorCtrl ) ? animatorCtrl.isAnimating() : false ; } - for(int i = dest.getGLEventListenerCount() - 1; 0 <= i; i--) { - dest.disposeGLEventListener(dest.getGLEventListener(i), false); + + /** + * {@link GLRunnable} to issue {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int)}, + * returning <code>true</code> on {@link GLRunnable#run(GLAutoDrawable)}. + */ + public static class ReshapeGLEventListener implements GLRunnable { + private final GLEventListener listener; + private final boolean displayAfterReshape; + /** + * + * @param listener + * @param displayAfterReshape <code>true</code> to issue {@link GLEventListener#display(GLAutoDrawable)} + * after {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int)}, + * otherwise false. + */ + public ReshapeGLEventListener(final GLEventListener listener, final boolean displayAfterReshape) { + this.listener = listener; + this.displayAfterReshape = displayAfterReshape; + } + @Override + public boolean run(final GLAutoDrawable drawable) { + listener.reshape(drawable, 0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + if( displayAfterReshape ) { + listener.display(drawable); + } + return true; + } + } + + /** + * Moves the designated {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>. + * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved + * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call. + * <p> + * Note that it is only legal to pass <code>preserveInitState := true</code>, + * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>. + * </p> + * <p> + * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}. + * </p> + * @param src + * @param dest + * @param listener + * @param preserveInitState + */ + public static final void moveGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final GLEventListener listener, final boolean preserveInitState) { + final boolean initialized = src.getGLEventListenerInitState(listener); + if( preserveInitState ) { + src.removeGLEventListener(listener); + dest.addGLEventListener(listener); + if( initialized ) { + dest.setGLEventListenerInitState(listener, true); + dest.invoke(false, new ReshapeGLEventListener(listener, true)); + } + } else { + src.disposeGLEventListener(listener, true); + dest.addGLEventListener(listener); + } + } + + /** + * Moves all {@link GLEventListener} from {@link GLAutoDrawable} <code>src</code> to <code>dest</code>. + * If <code>preserveInitState</code> is <code>true</code>, it's initialized state is preserved + * and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued w/ the next {@link GLAutoDrawable#display()} call. + * <p> + * Note that it is only legal to pass <code>preserveInitState := true</code>, + * if the {@link GLContext} of both <code>src</code> and <code>dest</code> are shared, or has itself moved from <code>src</code> to <code>dest</code>. + * </p> + * <p> + * Also note that the caller is encouraged to pause an attached {@link GLAnimatorControl}. + * </p> + * @param src + * @param dest + * @param listener + * @param preserveInitState + */ + public static final void moveAllGLEventListener(final GLAutoDrawable src, final GLAutoDrawable dest, final boolean preserveInitState) { + for(int count = src.getGLEventListenerCount(); 0<count; count--) { + final GLEventListener listener = src.getGLEventListener(0); + moveGLEventListener(src, dest, listener, preserveInitState); + } + } + + /** + * Return a heuristic value whether switching the {@link GLContext} is safe between {@lin GLAutoDrawable}s, + * i.e. via {@link #swapGLContext(GLAutoDrawable, GLAutoDrawable)} or {@link #swapGLContextAndAllGLEventListener(GLAutoDrawable, GLAutoDrawable)}. + * <p> + * Method currently returns <code>false</code> if: + * <ul> + * <li>Switching between on- and offscreen and one of the following is <code>true</code>: + * <ul> + * <li>{@link GLCapabilitiesImmutable#getSampleBuffers() MSAA is <i>used</i>} [1] in <code>chosenCapsA</code> or <code>chosenCapsB</code></li> + * <li>{@link GLCapabilitiesImmutable#getStereo() Stereo is <i>used</i>} in <code>chosenCapsA</code> or <code>chosenCapsB</code></li> + * <li>{@link GLCapabilitiesImmutable#getAccumAlphaBits() Accumulator Buffer is <i>requested</i>} [2] in <code>requestedCaps</code></li> + * </ul></li> + * </ul> + * Otherwise method returns <code>true</code> + * </p> + * <pre> + * [1] See Bug 830: swapGLContextAndAllGLEventListener and onscreen MSAA w/ NV/GLX + * On NVidia GPUs w/ it's proprietary driver context swapping does not work if MSAA is involved + * and when swapping on- to offscreen. + * </pre> + * <pre> + * [2] On AMD GPUs w/ it's proprietary driver, requesting an accumulator buffer leads to receive an accumulator buffer configuration, + * for which context swapping does not work when swapping on- to offscreen and vice-versa, i.e. cannot make context current. + * With AMD and Mesa drivers we only receive an accumulator buffer if requested, + * where on NVidia drivers all configurations contain the accumulator buffer. + * On both drivers, NVidia and Mesa, context swapping with accumulator buffer works. + * </pre> + * @param requestedCaps requested {@link GLCapabilitiesImmutable} which are intended for usage by both {@link GLAutoDrawable}s A and B + * @param chosenCapsA chosen {@link GLCapabilitiesImmutable} of {@link GLAutoDrawable} A, which {@link GLContext} is intended to be swapped + * @param chosenCapsB chosen {@link GLCapabilitiesImmutable} of {@link GLAutoDrawable} B, which {@link GLContext} is intended to be swapped + * @see #swapGLContext(GLAutoDrawable, GLAutoDrawable) + * @see #swapGLContextAndAllGLEventListener(GLAutoDrawable, GLAutoDrawable) + */ + public static boolean isSwapGLContextSafe(final GLCapabilitiesImmutable requestedCaps, final GLCapabilitiesImmutable chosenCapsA, final GLCapabilitiesImmutable chosenCapsB) { + final boolean usingAccumulatorBuffer = requestedCaps.getAccumAlphaBits() > 0 || + requestedCaps.getAccumRedBits() > 0 || + requestedCaps.getAccumGreenBits() > 0 || + requestedCaps.getAccumBlueBits() > 0; + if( ( chosenCapsA.isOnscreen() && !chosenCapsB.isOnscreen() || !chosenCapsA.isOnscreen() && chosenCapsB.isOnscreen() ) && // switching between on- and offscreen + ( + ( chosenCapsA.getSampleBuffers() || chosenCapsB.getSampleBuffers() ) || // MSAA involved + ( chosenCapsA.getStereo() || chosenCapsB.getStereo() ) || // Stereo involved + usingAccumulatorBuffer // Using accumulator buffer + ) + ) + { + return false; + } else { + return true; + } + } + /** + * Swaps the {@link GLContext} and all {@link GLEventListener} between {@link GLAutoDrawable} <code>a</code> and <code>b</code>, + * while preserving it's initialized state, resets the GL-Viewport and issuing {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)}. + * <p> + * The {@link GLAutoDrawable} to {@link GLAnimatorControl} association + * is also swapped. + * </p> + * <p> + * If an {@link GLAnimatorControl} is being attached to {@link GLAutoDrawable} <code>a</code> or <code>b</code> + * and the current thread is different than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. + * </p> + * <p> + * During operation, both {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-locks} and {@link GLAutoDrawable#getNativeSurface() surfaces} are locked, + * hence atomicity of operation is guaranteed, + * see <a href="../../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>. + * </p> + * <p> + * Because of above mentioned locking, if this method is not performed + * on {@link GLAutoDrawable#isThreadGLCapable() a OpenGL capable thread} of <i>both</i> + * {@link GLAutoDrawable}s, it must be invoked on such an OpenGL capable thread, + * e.g. via {@link Threading#invokeOnOpenGLThread(boolean, Runnable)}. + * </p> + * @throws GLException if the {@link AbstractGraphicsDevice} are incompatible w/ each other. + * @see #isSwapGLContextSafe(GLCapabilitiesImmutable, GLCapabilitiesImmutable, GLCapabilitiesImmutable) + */ + public static final void swapGLContextAndAllGLEventListener(final GLAutoDrawable a, final GLAutoDrawable b) { + final GLEventListenerState gllsA = GLEventListenerState.moveFrom(a, true); + final GLEventListenerState gllsB = GLEventListenerState.moveFrom(b, true); + final Runnable gllsAUnlockOp = gllsA.getUnlockSurfaceOp(); + final Runnable gllsBUnlockOp = gllsB.getUnlockSurfaceOp(); + try { + gllsA.moveTo(b, gllsBUnlockOp); + gllsB.moveTo(a, gllsAUnlockOp); + } finally { + // guarantee unlock in case of an exception + gllsBUnlockOp.run(); + gllsAUnlockOp.run(); + } + } + + /** + * Swaps the {@link GLContext} of given {@link GLAutoDrawable} + * and {@link GLAutoDrawable#disposeGLEventListener(GLEventListener, boolean) disposes} + * each {@link GLEventListener} w/o removing it. + * <p> + * The GL-Viewport is reset and {@link GLEventListener#reshape(GLAutoDrawable, int, int, int, int) reshape(..)} issued implicit. + * </p> + * <p> + * If an {@link GLAnimatorControl} is being attached to GLAutoDrawable src or dest and the current thread is different + * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. + * </p> + * <p> + * During operation, both {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-locks} and {@link GLAutoDrawable#getNativeSurface() surfaces} are locked, + * hence atomicity of operation is guaranteed, + * see <a href="../../../../javax/media/opengl/GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>. + * </p> + * <p> + * Because of above mentioned locking, if this method is not performed + * on {@link GLAutoDrawable#isThreadGLCapable() a OpenGL capable thread} of <i>both</i> + * {@link GLAutoDrawable}s, it must be invoked on such an OpenGL capable thread, + * e.g. via {@link Threading#invokeOnOpenGLThread(boolean, Runnable)}. + * </p> + * @param a + * @param b + * @see #isSwapGLContextSafe(GLCapabilitiesImmutable, GLCapabilitiesImmutable, GLCapabilitiesImmutable) + */ + public static final void swapGLContext(final GLAutoDrawable a, final GLAutoDrawable b) { + final GLAnimatorControl aAnim = a.getAnimator(); + final GLAnimatorControl bAnim = b.getAnimator(); + final boolean aIsPaused = isAnimatorAnimatingOnOtherThread(aAnim) && aAnim.pause(); + final boolean bIsPaused = isAnimatorAnimatingOnOtherThread(bAnim) && bAnim.pause(); + + final RecursiveLock aUpstreamLock = a.getUpstreamLock(); + final RecursiveLock bUpstreamLock = b.getUpstreamLock(); + aUpstreamLock.lock(); + bUpstreamLock.lock(); + try { + final NativeSurface aSurface = a.getNativeSurface(); + final boolean aSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < aSurface.lockSurface(); + if( a.isRealized() && !aSurfaceLocked ) { + throw new GLException("Could not lock realized a surface "+a); + } + final NativeSurface bSurface = b.getNativeSurface(); + final boolean bSurfaceLocked = NativeSurface.LOCK_SURFACE_NOT_READY < bSurface.lockSurface(); + if( b.isRealized() && !bSurfaceLocked ) { + throw new GLException("Could not lock realized b surface "+b); + } + try { + for(int i = a.getGLEventListenerCount() - 1; 0 <= i; i--) { + a.disposeGLEventListener(a.getGLEventListener(i), false); + } + for(int i = b.getGLEventListenerCount() - 1; 0 <= i; i--) { + b.disposeGLEventListener(b.getGLEventListener(i), false); + } + b.setContext( a.setContext( b.getContext(), false ), false ); + + } finally { + if( bSurfaceLocked ) { + bSurface.unlockSurface(); + } + if( aSurfaceLocked ) { + aSurface.unlockSurface(); + } + } + } finally { + bUpstreamLock.unlock(); + aUpstreamLock.unlock(); + } + a.invoke(true, setViewport); + b.invoke(true, setViewport); + if(aIsPaused) { aAnim.resume(); } + if(bIsPaused) { bAnim.resume(); } + } + + private static final GLRunnable setViewport = new GLRunnable() { + @Override + public boolean run(final GLAutoDrawable drawable) { + drawable.getGL().glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight()); + return false; // issue re-display w/ new viewport! + } + }; + + /** + * Determines whether the chosen {@link GLCapabilitiesImmutable} + * requires a {@link GLDrawable#swapBuffers() swap-buffers} + * before reading pixels. + * <p> + * Usually one uses the {@link GLBase#getDefaultReadBuffer() default-read-buffer} + * in which case {@link GLDrawable#swapBuffers() swap-buffers} shall happen <b>after</b> calling reading pixels, the default. + * </p> + * <p> + * However, <i>multisampling</i> offscreen {@link javax.media.opengl.GLFBODrawable}s + * utilize {@link GLDrawable#swapBuffers() swap-buffers} to <i>downsample</i> + * the multisamples into the readable sampling sink. + * In this case, we require {@link GLDrawable#swapBuffers() swap-buffers} <b>before</b> reading pixels. + * </p> + * @return chosenCaps.isFBO() && chosenCaps.getSampleBuffers() + */ + public static final boolean swapBuffersBeforeRead(final GLCapabilitiesImmutable chosenCaps) { + return chosenCaps.isFBO() && chosenCaps.getSampleBuffers(); } - dest.setContext( src.setContext( dest.getContext(), false ), false ); - - src.invoke(true, GLEventListenerState.setViewport); - dest.invoke(true, GLEventListenerState.setViewport); - - if(aIsPaused) { aAnim.resume(); } - if(bIsPaused) { bAnim.resume(); } - } - - /** - * Determines whether the chosen {@link GLCapabilitiesImmutable} - * requires a {@link GLDrawable#swapBuffers() swap-buffers} - * before reading pixels. - * <p> - * Usually one uses the {@link GLBase#getDefaultReadBuffer() default-read-buffer} - * in which case {@link GLDrawable#swapBuffers() swap-buffers} shall happen <b>after</b> calling reading pixels, the default. - * </p> - * <p> - * However, <i>multisampling</i> offscreen {@link javax.media.opengl.GLFBODrawable}s - * utilize {@link GLDrawable#swapBuffers() swap-buffers} to <i>downsample</i> - * the multisamples into the readable sampling sink. - * In this case, we require {@link GLDrawable#swapBuffers() swap-buffers} <b>before</b> reading pixels. - * </p> - * @return chosenCaps.isFBO() && chosenCaps.getSampleBuffers() - */ - public static final boolean swapBuffersBeforeRead(final GLCapabilitiesImmutable chosenCaps) { - return chosenCaps.isFBO() && chosenCaps.getSampleBuffers(); - } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java b/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java index 52612d224..290033e99 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLPixelStorageModes.java @@ -33,6 +33,7 @@ import javax.media.opengl.GL2; import javax.media.opengl.GL2ES2; import javax.media.opengl.GL2ES3; import javax.media.opengl.GL2GL3; +import javax.media.opengl.GLContext; import javax.media.opengl.GLException; /** @@ -60,7 +61,7 @@ public class GLPixelStorageModes { /** * Sets the {@link GL#GL_PACK_ALIGNMENT}. * <p> - * Saves the PACK pixel storage modes if not saved yet, see {@link #savePack(GL)}. + * Saves the PACK pixel storage modes and {@link #resetPack(GL) resets} them if not saved yet, see {@link #savePack(GL)}. * </p> */ public final void setPackAlignment(final GL gl, final int packAlignment) { @@ -71,7 +72,7 @@ public class GLPixelStorageModes { /** * Sets the {@link GL#GL_UNPACK_ALIGNMENT}. * <p> - * Saves the UNPACK pixel storage modes if not saved yet, see {@link #saveUnpack(GL)}. + * Saves the UNPACK pixel storage modes and {@link #resetUnpack(GL) resets} them if not saved yet, see {@link #saveUnpack(GL)}. * </p> */ public final void setUnpackAlignment(final GL gl, final int unpackAlignment) { @@ -82,7 +83,7 @@ public class GLPixelStorageModes { /** * Sets the {@link GL#GL_PACK_ALIGNMENT} and {@link GL#GL_UNPACK_ALIGNMENT}. * <p> - * Saves the PACK and UNPACK pixel storage modes if not saved yet, see {@link #saveAll(GL)}. + * Saves the PACK and UNPACK pixel storage modes and resets them if not saved yet, see {@link #saveAll(GL)}. * </p> */ public final void setAlignment(final GL gl, final int packAlignment, final int unpackAlignment) { @@ -93,7 +94,7 @@ public class GLPixelStorageModes { /** * Sets the {@link GL2ES3#GL_PACK_ROW_LENGTH}. * <p> - * Saves the PACK pixel storage modes if not saved yet, see {@link #savePack(GL)}. + * Saves the PACK pixel storage modes and {@link #resetPack(GL) resets} them if not saved yet, see {@link #savePack(GL)}. * </p> */ public final void setPackRowLength(final GL2ES3 gl, final int packRowLength) { @@ -104,18 +105,18 @@ public class GLPixelStorageModes { /** * Sets the {@link GL2ES2#GL_UNPACK_ROW_LENGTH}. * <p> - * Saves the UNPACK pixel storage modes if not saved yet, see {@link #saveUnpack(GL)}. + * Saves the UNPACK pixel storage modes and {@link #resetUnpack(GL) resets} them if not saved yet, see {@link #saveUnpack(GL)}. * </p> */ - public final void setUnpackRowLength(final GL2ES2 gl, final int unpackRowLength) { + public final void setUnpackRowLength(final GL2ES3 gl, final int unpackRowLength) { saveUnpack(gl); gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, unpackRowLength); } /** - * Sets the {@link GL2ES3#GL_PACK_ROW_LENGTH} and {@link GL2ES2#GL_UNPACK_ROW_LENGTH}. + * Sets the {@link GL2ES3#GL_PACK_ROW_LENGTH} and {@link GL2ES2#GL_UNPACK_ROW_LENGTH} if {@link GL#isGL2ES3()}. * <p> - * Saves the PACK and UNPACK pixel storage modes if not saved yet, see {@link #saveAll(GL)}. + * Saves the PACK and UNPACK pixel storage modes and resets them if not saved yet, see {@link #saveAll(GL)}. * </p> */ public final void setRowLength(final GL2ES3 gl, final int packRowLength, final int unpackRowLength) { @@ -171,7 +172,7 @@ public class GLPixelStorageModes { */ public final void resetPack(final GL gl) { // Compared w/ ES2, ES3 and GL3-core spec - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 4); // es2, es3, gl3 + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 4); // es2, es3, gl3 if( gl.isGL2ES3() ) { gl.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, 0); // es3, gl3 gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_ROWS, 0); // es3, gl3 @@ -179,8 +180,10 @@ public class GLPixelStorageModes { if( gl.isGL2GL3() ) { gl.glPixelStorei(GL2GL3.GL_PACK_SWAP_BYTES, GL.GL_FALSE); // gl3 gl.glPixelStorei(GL2GL3.GL_PACK_LSB_FIRST, GL.GL_FALSE); // gl3 - gl.glPixelStorei(GL2GL3.GL_PACK_IMAGE_HEIGHT, 0); // gl3 - gl.glPixelStorei(GL2GL3.GL_PACK_SKIP_IMAGES, 0); // gl3 + if( gl.getContext().getGLVersionNumber().compareTo(GLContext.Version120) >= 0 ) { + gl.glPixelStorei(GL2GL3.GL_PACK_IMAGE_HEIGHT, 0); // gl3, GL_VERSION_1_2 + gl.glPixelStorei(GL2GL3.GL_PACK_SKIP_IMAGES, 0); // gl3, GL_VERSION_1_2 + } } } } @@ -242,16 +245,21 @@ public class GLPixelStorageModes { */ public final void resetUnpack(final GL gl) { // Compared w/ ES2, ES3 and GL3-core spec - gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 4); // es2, es3, gl3 + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 4); // es2, es3, gl3 if( gl.isGL2ES3() ) { gl.glPixelStorei(GL2ES2.GL_UNPACK_ROW_LENGTH, 0); // es3, gl3 gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_ROWS, 0); // es3, gl3 gl.glPixelStorei(GL2ES2.GL_UNPACK_SKIP_PIXELS, 0); // es3, gl3 - gl.glPixelStorei(GL2ES3.GL_UNPACK_IMAGE_HEIGHT, 0); // es3, gl3 - gl.glPixelStorei(GL2ES3.GL_UNPACK_SKIP_IMAGES, 0); // es3, gl3 if( gl.isGL2GL3() ) { + if( gl.getContext().getGLVersionNumber().compareTo(GLContext.Version120) >= 0 ) { + gl.glPixelStorei(GL2ES3.GL_UNPACK_IMAGE_HEIGHT, 0); // es3, gl3, GL_VERSION_1_2 + gl.glPixelStorei(GL2ES3.GL_UNPACK_SKIP_IMAGES, 0); // es3, gl3, GL_VERSION_1_2 + } gl.glPixelStorei(GL2GL3.GL_UNPACK_SWAP_BYTES, GL.GL_FALSE); // gl3 gl.glPixelStorei(GL2GL3.GL_UNPACK_LSB_FIRST, GL.GL_FALSE); // gl3 + } else { + gl.glPixelStorei(GL2ES3.GL_UNPACK_IMAGE_HEIGHT, 0); // es3, gl3, GL_VERSION_1_2 + gl.glPixelStorei(GL2ES3.GL_UNPACK_SKIP_IMAGES, 0); // es3, gl3, GL_VERSION_1_2 } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java b/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java index abdb7c5f9..e84a1d874 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLReadBufferUtil.java @@ -223,7 +223,7 @@ public class GLReadBufferUtil { psm.setPackAlignment(gl, alignment); if(gl.isGL2ES3()) { final GL2ES3 gl2es3 = gl.getGL2ES3(); - gl2es3.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, width); + psm.setPackRowLength(gl2es3, width); gl2es3.glReadBuffer(gl2es3.getDefaultReadBuffer()); } readPixelBuffer.clear(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/Gamma.java b/src/jogl/classes/com/jogamp/opengl/util/Gamma.java index 4e44c997a..bab85e531 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/Gamma.java +++ b/src/jogl/classes/com/jogamp/opengl/util/Gamma.java @@ -39,69 +39,79 @@ package com.jogamp.opengl.util; -import javax.media.opengl.*; -import jogamp.opengl.*; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; -/** Provides control over the primary display's gamma, brightness and - contrast controls via the hardware gamma ramp tables. Not - supported on all platforms or graphics hardware. <P> - - Thanks to the LWJGL project for illustrating how to access gamma - control on the various platforms. -*/ +import com.jogamp.common.util.locks.RecursiveLock; +/** + * Provides convenient wrapper for {@link GLDrawableFactory} control over + * individual display's gamma, brightness and contrast values + * via the hardware gamma ramp tables. + * <p> + * Not supported on all platforms or graphics hardware. + * </p> + * <p> + * Thanks to the LWJGL project for illustrating how to access gamma + * control on the various platforms. + * </p> + */ public class Gamma { private Gamma() {} /** - * Sets the gamma, brightness, and contrast of the current main - * display. This functionality is not available on all platforms and - * graphics hardware. Returns true if the settings were successfully - * changed, false if not. This method may return false for some - * values of the incoming arguments even on hardware which does - * support the underlying functionality. <P> - * - * If this method returns true, the display settings will - * automatically be reset to their original values upon JVM exit - * (assuming the JVM does not crash); if the user wishes to change - * the display settings back to normal ahead of time, use {@link - * #resetDisplayGamma resetDisplayGamma}(). It is recommended to - * call {@link #resetDisplayGamma resetDisplayGamma} before calling - * e.g. <code>System.exit()</code> from the application rather than - * rely on the shutdown hook functionality due to inevitable race - * conditions and unspecified behavior during JVM teardown. <P> - * - * This method may be called multiple times during the application's - * execution, but calling {@link #resetDisplayGamma - * resetDisplayGamma} will only reset the settings to the values - * before the first call to this method. <P> - * - * @param gamma The gamma value, typically > 1.0 (default values - * vary, but typically roughly 1.0) - * @param brightness The brightness value between -1.0 and 1.0, - * inclusive (default values vary, but typically 0) - * @param contrast The contrast, greater than 0.0 (default values - * vary, but typically 1) - * @return true if gamma settings were successfully changed, false - * if not - * @throws IllegalArgumentException if any of the parameters were - * out-of-bounds + * Convenient wrapper for {@link GLDrawableFactory#setDisplayGamma(javax.media.nativewindow.NativeSurface, float, float, float)}. + * <p> + * Use {@link #setDisplayGamma(GLAutoDrawable, float, float, float)} in case of using an {#link GLAutoDrawable}. + * </p> + */ + public static boolean setDisplayGamma(final GLDrawable drawable, final float gamma, final float brightness, final float contrast) throws IllegalArgumentException { + return GLDrawableFactory.getFactory(drawable.getGLProfile()).setDisplayGamma(drawable.getNativeSurface(), gamma, brightness, contrast); + } + + /** + * Convenient wrapper for {@link GLDrawableFactory#setDisplayGamma(javax.media.nativewindow.NativeSurface, float, float, float)} + * locking {@link GLAutoDrawable#getUpstreamLock()} to ensure proper atomic operation. + */ + public static boolean setDisplayGamma(final GLAutoDrawable drawable, final float gamma, final float brightness, final float contrast) throws IllegalArgumentException { + final RecursiveLock lock = drawable.getUpstreamLock(); + lock.lock(); + try { + return GLDrawableFactory.getFactory(drawable.getGLProfile()).setDisplayGamma(drawable.getNativeSurface(), gamma, brightness, contrast); + } finally { + lock.unlock(); + } + } + + /** + * Convenient wrapper for {@link GLDrawableFactory#resetDisplayGamma(javax.media.nativewindow.NativeSurface)}. + * <p> + * Use {@link #resetDisplayGamma(GLAutoDrawable)} in case of using an {#link GLAutoDrawable}. + * </p> + */ + public static void resetDisplayGamma(final GLDrawable drawable) { + GLDrawableFactory.getFactory(drawable.getGLProfile()).resetDisplayGamma(drawable.getNativeSurface()); + } + + /** + * Convenient wrapper for {@link GLDrawableFactory#resetDisplayGamma(javax.media.nativewindow.NativeSurface)} + * locking {@link GLAutoDrawable#getUpstreamLock()} to ensure proper atomic operation. */ - public static boolean setDisplayGamma(final GL gl, final float gamma, final float brightness, final float contrast) throws IllegalArgumentException { - return GLDrawableFactoryImpl.getFactoryImpl(gl.getContext().getGLDrawable().getGLProfile()).setDisplayGamma(gamma, brightness, contrast); + public static void resetDisplayGamma(final GLAutoDrawable drawable) { + final RecursiveLock lock = drawable.getUpstreamLock(); + lock.lock(); + try { + GLDrawableFactory.getFactory(drawable.getGLProfile()).resetDisplayGamma(drawable.getNativeSurface()); + } finally { + lock.unlock(); + } } /** - * Resets the gamma, brightness and contrast values for the primary - * display to their original values before {@link #setDisplayGamma - * setDisplayGamma} was called the first time. {@link - * #setDisplayGamma setDisplayGamma} must be called before calling - * this method or an unspecified exception will be thrown. While it - * is not explicitly required that this method be called before - * exiting, calling it is recommended because of the inevitable - * unspecified behavior during JVM teardown. + * Convenient wrapper for {@link GLDrawableFactory#resetAllDisplayGamma()}. */ - public static void resetDisplayGamma(final GL gl) { - GLDrawableFactoryImpl.getFactoryImpl(gl.getContext().getGLDrawable().getGLProfile()).resetDisplayGamma(); + public static void resetAllDisplayGamma(final GLDrawable drawable) { + GLDrawableFactory.getFactory(drawable.getGLProfile()).resetAllDisplayGamma(); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java index 4fffd9fab..2ad102235 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java @@ -27,14 +27,13 @@ */ package com.jogamp.opengl.util.av; -import java.net.URI; - import javax.media.opengl.GL; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import jogamp.opengl.Debug; +import com.jogamp.common.net.Uri; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureSequence; import com.jogamp.opengl.util.TimeFrameI; @@ -46,18 +45,18 @@ import com.jogamp.opengl.util.TimeFrameI; * Audio maybe supported and played back internally or via an {@link AudioSink} implementation. * </p> * <p> - * Audio and video streams can be selected or muted via {@link #initStream(URI, int, int, int)} + * Audio and video streams can be selected or muted via {@link #initStream(Uri, int, int, int)} * using the appropriate <a href="#streamIDs">stream id</a>'s. * </p> * <p> - * Camera input can be selected using the {@link #CameraInputScheme} URI. + * Camera input can be selected using the {@link #CameraInputScheme} Uri. * </p> * * <a name="streamworker"><h5><i>StreamWorker</i> Decoding Thread</h5></a> * <p> * Most of the stream processing is performed on the decoding thread, a.k.a. <i>StreamWorker</i>: * <ul> - * <li>Stream initialization triggered by {@link #initStream(URI, int, int, int) initStream(..)} - User gets notified whether the stream has been initialized or not via {@link GLMediaEventListener#attributesChanged(GLMediaPlayer, int, long) attributesChanges(..)}.</li> + * <li>Stream initialization triggered by {@link #initStream(Uri, int, int, int) initStream(..)} - User gets notified whether the stream has been initialized or not via {@link GLMediaEventListener#attributesChanged(GLMediaPlayer, int, long) attributesChanges(..)}.</li> * <li>Stream decoding - User gets notified of a new frame via {@link GLMediaEventListener#newFrameAvailable(GLMediaPlayer, com.jogamp.opengl.util.texture.TextureSequence.TextureFrame, long) newFrameAvailable(...)}.</li> * <li>Caught <a href="#streamerror">exceptions on the decoding thread</a> are delivered as {@link StreamException}s.</li> * </ul> @@ -83,7 +82,7 @@ import com.jogamp.opengl.util.TimeFrameI; * <p> * <table border="1"> * <tr><th>Action</th> <th>{@link State} Before</th> <th>{@link State} After</th> <th>{@link GLMediaEventListener Event}</th></tr> - * <tr><td>{@link #initStream(URI, int, int, int)}</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link State#Initialized Initialized}<sup><a href="#streamworker">1</a></sup>, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_INIT EVENT_CHANGE_INIT} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr> + * <tr><td>{@link #initStream(Uri, int, int, int)}</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link State#Initialized Initialized}<sup><a href="#streamworker">1</a></sup>, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_INIT EVENT_CHANGE_INIT} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr> * <tr><td>{@link #initGL(GL)}</td> <td>{@link State#Initialized Initialized}</td> <td>{@link State#Paused Paused}, , {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr> * <tr><td>{@link #play()}</td> <td>{@link State#Paused Paused}</td> <td>{@link State#Playing Playing}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PLAY EVENT_CHANGE_PLAY}</td></tr> * <tr><td>{@link #pause(boolean)}</td> <td>{@link State#Playing Playing}</td> <td>{@link State#Paused Paused}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE}</td></tr> @@ -183,6 +182,9 @@ import com.jogamp.opengl.util.TimeFrameI; * <!-- <tr><td> title </td><td colspan=3> url1 </td><td> url2 </td></tr> * </table> * </p> + * <p> + * Since 2.3.0 this interface uses {@link Uri} instead of {@link java.net.URI}. + * </p> */ public interface GLMediaPlayer extends TextureSequence { public static final boolean DEBUG = Debug.debug("GLMediaPlayer"); @@ -200,10 +202,10 @@ public interface GLMediaPlayer extends TextureSequence { public static final int STREAM_ID_AUTO = -1; /** - * {@link URI#getScheme() URI scheme} name {@value} for camera input. E.g. <code>camera:/0</code> + * {@link Uri#scheme Uri scheme} name {@value} for camera input. E.g. <code>camera:/0</code> * for the 1st camera device. * <p> - * The {@link URI#getRawPath() URI path} is being used to identify the camera (<i>ID</i>), + * The {@link Uri#path Uri path} is being used to identify the camera (<i>ID</i>), * where the root fwd-slash is being cut-off. * </p> * <p> @@ -211,7 +213,7 @@ public interface GLMediaPlayer extends TextureSequence { * ranging from [0..<i>max-number</i>]. * </p> * <p> - * The {@link URI#getRawQuery() URI query} is used to pass options to the camera + * The {@link Uri#query Uri query} is used to pass options to the camera * using <i>;</i> as the separator. The latter avoids trouble w/ escaping. * </p> * <pre> @@ -221,13 +223,13 @@ public interface GLMediaPlayer extends TextureSequence { * camera://somewhere/<id>?size=640x480;rate=15 * </pre> * <pre> - * URI: [scheme:][//authority][path][?query][#fragment] + * Uri: [scheme:][//authority][path][?query][#fragment] * w/ authority: [user-info@]host[:port] * Note: 'path' starts w/ fwd slash * </pre> * </p> */ - public static final String CameraInputScheme = "camera"; + public static final Uri.Encoded CameraInputScheme = Uri.Encoded.cast("camera"); /** Camera property {@value}, size as string, e.g. <code>1280x720</code>, <code>hd720</code>. May not be supported on all platforms. See {@link #CameraInputScheme}. */ public static final String CameraPropSizeS = "size"; /** Camera property {@value}. See {@link #CameraInputScheme}. */ @@ -361,8 +363,9 @@ public interface GLMediaPlayer extends TextureSequence { * Ignored if video is muted. * @throws IllegalStateException if not invoked in {@link State#Uninitialized} * @throws IllegalArgumentException if arguments are invalid + * @since 2.3.0 */ - public void initStream(URI streamLoc, int vid, int aid, int textureCount) throws IllegalStateException, IllegalArgumentException; + public void initStream(Uri streamLoc, int vid, int aid, int textureCount) throws IllegalStateException, IllegalArgumentException; /** * Returns the {@link StreamException} caught in the decoder thread, or <code>null</code> if none occured. @@ -379,7 +382,7 @@ public interface GLMediaPlayer extends TextureSequence { * <p> * <a href="#lifecycle">Lifecycle</a>: {@link State#Initialized} -> {@link State#Paused} or {@link State#Initialized} * </p> - * Argument <code>gl</code> is ignored if video is muted, see {@link #initStream(URI, int, int, int)}. + * Argument <code>gl</code> is ignored if video is muted, see {@link #initStream(Uri, int, int, int)}. * * @param gl current GL object. Maybe <code>null</code>, for audio only. * @throws IllegalStateException if not invoked in {@link State#Initialized}. @@ -391,7 +394,7 @@ public interface GLMediaPlayer extends TextureSequence { /** * If implementation uses a {@link AudioSink}, it's instance will be returned. * <p> - * The {@link AudioSink} instance is available after {@link #initStream(URI, int, int, int)}, + * The {@link AudioSink} instance is available after {@link #initStream(Uri, int, int, int)}, * if used by implementation. * </p> */ @@ -536,8 +539,11 @@ public interface GLMediaPlayer extends TextureSequence { @Override public TextureSequence.TextureFrame getNextTexture(GL gl) throws IllegalStateException; - /** Return the stream location, as set by {@link #initStream(URI, int, int, int)}. */ - public URI getURI(); + /** + * Return the stream location, as set by {@link #initStream(Uri, int, int, int)}. + * @since 2.3.0 + */ + public Uri getUri(); /** * <i>Warning:</i> Optional information, may not be supported by implementation. diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java index 29dce40f5..8eed35ebb 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.StringReader; +import java.net.URISyntaxException; import java.net.URLConnection; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -46,12 +47,14 @@ import java.util.Set; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GL3; +import javax.media.opengl.GL4; import javax.media.opengl.GLES2; import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import jogamp.opengl.Debug; +import com.jogamp.common.net.Uri; import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.IOUtil; import com.jogamp.common.util.VersionNumber; @@ -63,33 +66,62 @@ import com.jogamp.common.util.VersionNumber; * {@link #create(GL2ES2, int, Class, String, String, String, boolean) here} and * {@link #create(GL2ES2, int, int, Class, String, String[], String, String) here}. * </p> + * <p> + * Support for {@link GL4#GL_TESS_CONTROL_SHADER} and {@link GL4#GL_TESS_EVALUATION_SHADER} + * was added since 2.2.1. + * </p> */ public class ShaderCode { public static final boolean DEBUG_CODE = Debug.isPropertyDefined("jogl.debug.GLSLCode", true); - /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in source code: <code>vp</code> */ + /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in source code: <code>{@value}</code> */ public static final String SUFFIX_VERTEX_SOURCE = "vp" ; - /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in binary: <code>bvp</code> */ + /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in binary: <code>{@value}</code> */ public static final String SUFFIX_VERTEX_BINARY = "bvp" ; - /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in source code: <code>gp</code> */ + /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in source code: <code>{@value}</code> */ public static final String SUFFIX_GEOMETRY_SOURCE = "gp" ; - /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in binary: <code>bgp</code> */ + /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in binary: <code>{@value}</code> */ public static final String SUFFIX_GEOMETRY_BINARY = "bgp" ; - /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in source code: <code>fp</code> */ + /** + * Unique resource suffix for {@link GL4#GL_TESS_CONTROL_SHADER} in source code: <code>{@value}</code> + * @since 2.2.1 + */ + public static final String SUFFIX_TESS_CONTROL_SOURCE = "tcp" ; + + /** + * Unique resource suffix for {@link GL4#GL_TESS_CONTROL_SHADER} in binary: <code>{@value}</code> + * @since 2.2.1 + */ + public static final String SUFFIX_TESS_CONTROL_BINARY = "btcp" ; + + /** + * Unique resource suffix for {@link GL4#GL_TESS_EVALUATION_SHADER} in source code: <code>{@value}</code> + * @since 2.2.1 + */ + public static final String SUFFIX_TESS_EVALUATION_SOURCE = "tep" ; + + /** + * Unique resource suffix for {@link GL4#GL_TESS_EVALUATION_SHADER} in binary: <code>{@value}</code> + * @since 2.2.1 + */ + public static final String SUFFIX_TESS_EVALUATION_BINARY = "btep" ; + + /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in source code: <code>{@value}</code> */ public static final String SUFFIX_FRAGMENT_SOURCE = "fp" ; - /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in binary: <code>bfp</code> */ + /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in binary: <code>{@value}</code> */ public static final String SUFFIX_FRAGMENT_BINARY = "bfp" ; - /** Unique relative path for binary shader resources for {@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: <code>nvidia</code> */ + /** Unique relative path for binary shader resources for {@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: <code>{@value}</code> */ public static final String SUB_PATH_NVIDIA = "nvidia" ; /** - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param source CharSequence array containing the shader sources, organized as <code>source[count][strings-per-shader]</code>. * May be either an immutable <code>String</code> - or mutable <code>StringBuilder</code> array. @@ -104,6 +136,8 @@ public class ShaderCode { case GL2ES2.GL_VERTEX_SHADER: case GL2ES2.GL_FRAGMENT_SHADER: case GL3.GL_GEOMETRY_SHADER: + case GL4.GL_TESS_CONTROL_SHADER: + case GL4.GL_TESS_EVALUATION_SHADER: break; default: throw new GLException("Unknown shader type: "+type); @@ -122,7 +156,8 @@ public class ShaderCode { } /** - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param binary binary buffer containing the shader binaries, */ @@ -131,6 +166,8 @@ public class ShaderCode { case GL2ES2.GL_VERTEX_SHADER: case GL2ES2.GL_FRAGMENT_SHADER: case GL3.GL_GEOMETRY_SHADER: + case GL4.GL_TESS_CONTROL_SHADER: + case GL4.GL_TESS_EVALUATION_SHADER: break; default: throw new GLException("Unknown shader type: "+type); @@ -148,7 +185,8 @@ public class ShaderCode { * which location is resolved using the <code>context</code> class, see {@link #readShaderSource(Class, String)}. * * @param gl current GL object to determine whether a shader compiler is available. If null, no validation is performed. - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param context class used to help resolving the source location * @param sourceFiles array of source locations, organized as <code>sourceFiles[count]</code> @@ -192,7 +230,8 @@ public class ShaderCode { * Creates a complete {@link ShaderCode} object while reading the shader binary of <code>binaryFile</code>, * which location is resolved using the <code>context</code> class, see {@link #readShaderBinary(Class, String)}. * - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param context class used to help resolving the source location * @param binFormat a valid native binary format as they can be queried by {@link ShaderUtil#getShaderBinaryFormats(GL)}. @@ -225,14 +264,21 @@ public class ShaderCode { * <li>Source<ul> * <li>{@link GL2ES2#GL_VERTEX_SHADER vertex}: {@link #SUFFIX_VERTEX_SOURCE}</li> * <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_SOURCE}</li> - * <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_SOURCE}</li></ul></li> + * <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_SOURCE}</li> + * <li>{@link GL4#GL_TESS_CONTROL_SHADER tess-ctrl}: {@link #SUFFIX_TESS_CONTROL_SOURCE}</li> + * <li>{@link GL4#GL_TESS_EVALUATION_SHADER tess-eval}: {@link #SUFFIX_TESS_EVALUATION_SOURCE}</li> + * </ul></li> * <li>Binary<ul> * <li>{@link GL2ES2#GL_VERTEX_SHADER vertex}: {@link #SUFFIX_VERTEX_BINARY}</li> * <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_BINARY}</li> - * <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_BINARY}</li></ul></li> + * <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_BINARY}</li> + * <li>{@link GL4#GL_TESS_CONTROL_SHADER tess-ctrl}: {@link #SUFFIX_TESS_CONTROL_BINARY}</li> + * <li>{@link GL4#GL_TESS_EVALUATION_SHADER tess-eval}: {@link #SUFFIX_TESS_EVALUATION_BINARY}</li> + * </ul></li> * </ul> * @param binary true for a binary resource, false for a source resource - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * * @throws GLException if <code>type</code> is not supported * @@ -246,6 +292,10 @@ public class ShaderCode { return binary?SUFFIX_FRAGMENT_BINARY:SUFFIX_FRAGMENT_SOURCE; case GL3.GL_GEOMETRY_SHADER: return binary?SUFFIX_GEOMETRY_BINARY:SUFFIX_GEOMETRY_SOURCE; + case GL4.GL_TESS_CONTROL_SHADER: + return binary?SUFFIX_TESS_CONTROL_BINARY:SUFFIX_TESS_CONTROL_SOURCE; + case GL4.GL_TESS_EVALUATION_SHADER: + return binary?SUFFIX_TESS_EVALUATION_BINARY:SUFFIX_TESS_EVALUATION_SOURCE; default: throw new GLException("illegal shader type: "+type); } @@ -324,7 +374,8 @@ public class ShaderCode { * * @param gl current GL object to determine whether a shader compiler is available (if <code>source</code> is used), * or to determine the shader binary format (if <code>binary</code> is used). - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param count number of shaders * @param context class used to help resolving the source and binary location * @param srcRoot relative <i>root</i> path for <code>srcBasenames</code> optional @@ -430,7 +481,8 @@ public class ShaderCode { * * @param gl current GL object to determine whether a shader compiler is available (if <code>source</code> is used), * or to determine the shader binary format (if <code>binary</code> is used). - * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, + * {@link GL4#GL_TESS_CONTROL_SHADER} or {@link GL4#GL_TESS_EVALUATION_SHADER}. * @param context class used to help resolving the source and binary location * @param srcRoot relative <i>root</i> path for <code>basename</code> optional * @param binRoot relative <i>root</i> path for <code>basename</code> @@ -468,6 +520,10 @@ public class ShaderCode { return "FRAGMENT_SHADER"; case GL3.GL_GEOMETRY_SHADER: return "GEOMETRY_SHADER"; + case GL4.GL_TESS_CONTROL_SHADER: + return "TESS_CONTROL_SHADER"; + case GL4.GL_TESS_EVALUATION_SHADER: + return "TESS_EVALUATION_SHADER"; } return "UNKNOWN_SHADER"; } @@ -778,7 +834,8 @@ public class ShaderCode { URLConnection nextConn = null; // Try relative of current shader location - nextConn = IOUtil.openURL(IOUtil.getRelativeOf(conn.getURL(), includeFile), "ShaderCode.relativeOf "); + final Uri relUri = Uri.valueOf( conn.getURL() ).getRelativeOf(new Uri.Encoded( includeFile, Uri.PATH_LEGAL )); + nextConn = IOUtil.openURL(relUri.toURL(), "ShaderCode.relativeOf "); if (nextConn == null) { // Try relative of class and absolute nextConn = IOUtil.getResource(context, includeFile); @@ -792,6 +849,8 @@ public class ShaderCode { result.append(line + "\n"); } } + } catch (final URISyntaxException e) { + throw new IOException(e); } finally { IOUtil.close(reader, false); } @@ -874,8 +933,12 @@ public class ShaderCode { /** Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}: {@value #es3_default_precision_vp} */ public static final String es3_default_precision_vp = es2_default_precision_vp; - /** Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #es3_default_precision_fp} */ - public static final String es3_default_precision_fp = es2_default_precision_fp; + /** + * Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #es3_default_precision_fp}, + * same as for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}, i.e {@link #es3_default_precision_vp}, + * due to ES 3.x requirements of using same precision for uniforms! + */ + public static final String es3_default_precision_fp = es3_default_precision_vp; /** Default precision of GLSL ≥ 1.30 as required until < 1.50 for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader} or {@link GL3#GL_GEOMETRY_SHADER geometry-shader}: {@value #gl3_default_precision_vp_gp}. See GLSL Spec 1.30-1.50 Section 4.5.3. */ public static final String gl3_default_precision_vp_gp = "\nprecision highp float;\nprecision highp int;\n"; @@ -956,6 +1019,8 @@ public class ShaderCode { switch ( shaderType ) { case GL2ES2.GL_VERTEX_SHADER: case GL3.GL_GEOMETRY_SHADER: + case GL4.GL_TESS_CONTROL_SHADER: + case GL4.GL_TESS_EVALUATION_SHADER: defaultPrecision = gl3_default_precision_vp_gp; break; case GL2ES2.GL_FRAGMENT_SHADER: defaultPrecision = gl3_default_precision_fp; break; diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java index f113be2b2..1d629131e 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java @@ -109,8 +109,7 @@ public abstract class CompileShader { tmpFile.getAbsolutePath(), outputFile.getAbsolutePath() }); // , null, processorDir); - new StreamMonitor(process.getInputStream()); - new StreamMonitor(process.getErrorStream()); + new IOUtil.StreamMonitor( new InputStream[] { process.getInputStream(), process.getErrorStream() }, System.out, null ); process.waitFor(); // Delete the temporary file // tmpFile.delete(); @@ -153,35 +152,4 @@ public abstract class CompileShader { e.printStackTrace(); } } - - private static class StreamMonitor implements Runnable { - private final InputStream istream; - public StreamMonitor(final InputStream stream) { - istream = stream; - new Thread(this, "Output Reader Thread").start(); - } - - @Override - public void run() - { - final byte[] buffer = new byte[4096]; - try { - int numRead = 0; - do { - numRead = istream.read(buffer); - if (numRead > 0) { - System.out.write(buffer, 0, numRead); - System.out.flush(); - } - } while (numRead >= 0); - } - catch (final IOException e) { - try { - istream.close(); - } catch (final IOException e2) { - } - // Should allow clean exit when process shuts down - } - } - } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java index 0801b65fa..f70ebf928 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoClientRenderer.java @@ -87,8 +87,7 @@ public class StereoClientRenderer implements GLEventListener { private void initFBOs(final GL gl, final DimensionImmutable size) { for(int i=0; i<fbos.length; i++) { - fbos[i].detachAllColorbuffer(gl); - fbos[i].reset(gl, size.getWidth(), size.getHeight(), numSamples, false); + fbos[i].init(gl, size.getWidth(), size.getHeight(), numSamples); if( i>0 && fbos[i-1].getNumSamples() != fbos[i].getNumSamples()) { throw new InternalError("sample size mismatch: \n\t0: "+fbos[i-1]+"\n\t1: "+fbos[i]); } @@ -96,19 +95,19 @@ public class StereoClientRenderer implements GLEventListener { if(numSamples>0) { fbos[i].attachColorbuffer(gl, 0, true); // MSAA requires alpha - fbos[i].attachRenderbuffer(gl, Type.DEPTH, 24); + fbos[i].attachRenderbuffer(gl, Type.DEPTH, FBObject.DEFAULT_BITS); final FBObject ssink = new FBObject(); { - ssink.reset(gl, size.getWidth(), size.getHeight()); + ssink.init(gl, size.getWidth(), size.getHeight(), 0); ssink.attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); - ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + ssink.attachRenderbuffer(gl, Attachment.Type.DEPTH, FBObject.DEFAULT_BITS); } fbos[i].setSamplingSink(ssink); fbos[i].resetSamplingSink(gl); // validate - fboTexs[i] = fbos[i].getSamplingSink(); + fboTexs[i] = fbos[i].getSamplingSink().getTextureAttachment(); } else { fboTexs[i] = fbos[i].attachTexture2D(gl, 0, false, magFilter, minFilter, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); - fbos[i].attachRenderbuffer(gl, Type.DEPTH, 24); + fbos[i].attachRenderbuffer(gl, Type.DEPTH, FBObject.DEFAULT_BITS); } fbos[i].unbind(gl); System.err.println("FBO["+i+"]: "+fbos[i]); @@ -119,15 +118,15 @@ public class StereoClientRenderer implements GLEventListener { @SuppressWarnings("unused") private void resetFBOs(final GL gl, final DimensionImmutable size) { for(int i=0; i<fbos.length; i++) { - fbos[i].reset(gl, size.getWidth(), size.getHeight(), numSamples, true); + fbos[i].reset(gl, size.getWidth(), size.getHeight(), numSamples); if( i>0 && fbos[i-1].getNumSamples() != fbos[i].getNumSamples()) { throw new InternalError("sample size mismatch: \n\t0: "+fbos[i-1]+"\n\t1: "+fbos[i]); } numSamples = fbos[i].getNumSamples(); if(numSamples>0) { - fboTexs[i] = fbos[i].getSamplingSink(); + fboTexs[i] = fbos[i].getSamplingSink().getTextureAttachment(); } else { - fboTexs[i] = (TextureAttachment) fbos[i].getColorbuffer(0); + fboTexs[i] = fbos[i].getColorbuffer(0).getTextureAttachment(); } } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java index 2091d0843..d32c981a3 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java @@ -46,6 +46,9 @@ public interface StereoDevice { // NOP } + /** Return the factory used to create this device. */ + public StereoDeviceFactory getFactory(); + /** Disposes this {@link StereoDevice}. */ public void dispose(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java index 46ce82f03..c4180585c 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDeviceFactory.java @@ -45,7 +45,25 @@ public abstract class StereoDeviceFactory { private static final String GenericStereoDeviceClazzName = "jogamp.opengl.util.stereo.GenericStereoDeviceFactory"; private static final String isAvailableMethodName = "isAvailable"; - public static enum DeviceType { Default, Generic, OculusVR }; + /** {@link StereoDevice} type used for {@link StereoDeviceFactory#createFactory(DeviceType) createFactory(type)}. */ + public static enum DeviceType { + /** + * Auto selection of device in the following order: + * <ol> + * <li>{@link DeviceType#OculusVR}</li> + * <li>{@link DeviceType#Generic}</li> + * </ol> + */ + Default, + /** + * Generic software implementation. + */ + Generic, + /** + * OculusVR implementation. + */ + OculusVR + }; public static StereoDeviceFactory createDefaultFactory() { final ClassLoader cl = StereoDeviceFactory.class.getClassLoader(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java index 19f2fc05b..6011afe7b 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureIO.java @@ -60,7 +60,6 @@ import javax.media.nativewindow.util.DimensionImmutable; import javax.media.nativewindow.util.PixelFormat; import javax.media.opengl.GL; import javax.media.opengl.GL2; -import javax.media.opengl.GL2ES3; import javax.media.opengl.GL2GL3; import javax.media.opengl.GLContext; import javax.media.opengl.GLException; @@ -69,6 +68,7 @@ import javax.media.opengl.GLProfile; import jogamp.opengl.Debug; import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.util.GLPixelStorageModes; import com.jogamp.opengl.util.PNGPixelRect; import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; import com.jogamp.opengl.util.texture.spi.DDSImage; @@ -575,6 +575,10 @@ public class TextureIO { * when it is written to disk, regardless of whether the underlying * file format supports multiple mipmaps in a given file. * + * <p> + * Method required a {@link GL2GL3} {@link GLProfile#GL2GL3 profile}. + * </p> + * * @throws IOException if an error occurred during writing or no * suitable writer was found * @throws GLException if no OpenGL context was current or an @@ -590,7 +594,7 @@ public class TextureIO { if (!_gl.isGL2GL3()) { throw new GLException("Implementation only supports GL2GL3 (Use GLReadBufferUtil and the TextureData variant), have: " + _gl); } - final GL2GL3 gl = _gl.getGL2(); + final GL2GL3 gl = _gl.getGL2GL3(); texture.bind(gl); final int internalFormat = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2GL3.GL_TEXTURE_INTERNAL_FORMAT); @@ -630,17 +634,8 @@ public class TextureIO { } // Fetch using glGetTexImage - final int packAlignment = glGetInteger(gl, GL.GL_PACK_ALIGNMENT); - final int packRowLength = glGetInteger(gl, GL2ES3.GL_PACK_ROW_LENGTH); - final int packSkipRows = glGetInteger(gl, GL2ES3.GL_PACK_SKIP_ROWS); - final int packSkipPixels = glGetInteger(gl, GL2ES3.GL_PACK_SKIP_PIXELS); - final int packSwapBytes = glGetInteger(gl, GL2GL3.GL_PACK_SWAP_BYTES); - - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1); - gl.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, 0); - gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_ROWS, 0); - gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_PIXELS, 0); - gl.glPixelStorei(GL2GL3.GL_PACK_SWAP_BYTES, 0); + final GLPixelStorageModes psm = new GLPixelStorageModes(); + psm.setPackAlignment(gl, 1); final ByteBuffer res = ByteBuffer.allocate((width + (2 * border)) * (height + (2 * border)) * @@ -651,11 +646,7 @@ public class TextureIO { } gl.glGetTexImage(GL.GL_TEXTURE_2D, 0, fetchedFormat, GL.GL_UNSIGNED_BYTE, res); - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, packAlignment); - gl.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, packRowLength); - gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_ROWS, packSkipRows); - gl.glPixelStorei(GL2ES3.GL_PACK_SKIP_PIXELS, packSkipPixels); - gl.glPixelStorei(GL2GL3.GL_PACK_SWAP_BYTES, packSwapBytes); + psm.restore(gl); data = new TextureData(gl.getGLProfile(), internalFormat, width, height, border, fetchedFormat, GL.GL_UNSIGNED_BYTE, false, false, false, res, null); @@ -1404,12 +1395,6 @@ public class TextureIO { // Helper routines // - private static int glGetInteger(final GL gl, final int pname) { - final int[] tmp = new int[1]; - gl.glGetIntegerv(pname, tmp, 0); - return tmp[0]; - } - private static int glGetTexLevelParameteri(final GL2GL3 gl, final int target, final int level, final int pname) { final int[] tmp = new int[1]; gl.glGetTexLevelParameteriv(target, 0, pname, tmp, 0); |