diff options
author | Sven Gothel <[email protected]> | 2014-02-23 14:51:06 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-02-23 14:51:06 +0100 |
commit | 3352601e0860584509adf2b76f993d03893ded4b (patch) | |
tree | 974fccc8c0eb2f5ad9d4ffd741dfc35869ed67b5 /src/jogl/classes/javax/media | |
parent | f51933f0ebe9ae030c26c066e59a728ce08b8559 (diff) | |
parent | c67de337a8aaf52e36104c3f13e273aa19d21f1f (diff) |
Merge branch 'master' into stash_glyphcache
Conflicts:
make/scripts/tests.sh
src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java
src/jogl/classes/com/jogamp/graph/curve/Region.java
src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java
src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java
src/jogl/classes/com/jogamp/graph/curve/opengl/Renderer.java
src/jogl/classes/com/jogamp/graph/curve/opengl/TextRenderer.java
src/jogl/classes/com/jogamp/graph/font/Font.java
src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java
src/jogl/classes/jogamp/graph/curve/text/GlyphShape.java
src/jogl/classes/jogamp/graph/curve/text/GlyphString.java
src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java
Diffstat (limited to 'src/jogl/classes/javax/media')
45 files changed, 6837 insertions, 2598 deletions
diff --git a/src/jogl/classes/javax/media/opengl/DebugGL2.java b/src/jogl/classes/javax/media/opengl/DebugGL2.java new file mode 100644 index 000000000..3c064a18f --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/DebugGL2.java @@ -0,0 +1,21 @@ +package javax.media.opengl; + +/** + * <p> + * Composable pipeline which wraps an underlying {@link GL} implementation, + * providing error checking after each OpenGL method call. If an error occurs, + * causes a {@link GLException} to be thrown at exactly the point of failure. + * </p> + * <p> + * Sample code which installs this pipeline, manual: + * <pre> + * gl = drawable.setGL(new DebugGL(drawable.getGL())); + * </pre> + * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}. + * </p> + */ +public class DebugGL2 extends DebugGL4bc { + public DebugGL2(GL2 downstream) { + super((GL4bc)downstream); + } +} diff --git a/src/jogl/classes/javax/media/opengl/DebugGL3.java b/src/jogl/classes/javax/media/opengl/DebugGL3.java new file mode 100644 index 000000000..b490a63f4 --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/DebugGL3.java @@ -0,0 +1,21 @@ +package javax.media.opengl; + +/** + * <p> + * Composable pipeline which wraps an underlying {@link GL} implementation, + * providing error checking after each OpenGL method call. If an error occurs, + * causes a {@link GLException} to be thrown at exactly the point of failure. + * </p> + * <p> + * Sample code which installs this pipeline, manual: + * <pre> + * gl = drawable.setGL(new DebugGL(drawable.getGL())); + * </pre> + * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}. + * </p> + */ +public class DebugGL3 extends DebugGL4bc { + public DebugGL3(GL3 downstream) { + super((GL4bc)downstream); + } +} diff --git a/src/jogl/classes/javax/media/opengl/DebugGL3bc.java b/src/jogl/classes/javax/media/opengl/DebugGL3bc.java new file mode 100644 index 000000000..1d42afbc6 --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/DebugGL3bc.java @@ -0,0 +1,21 @@ +package javax.media.opengl; + +/** + * <p> + * Composable pipeline which wraps an underlying {@link GL} implementation, + * providing error checking after each OpenGL method call. If an error occurs, + * causes a {@link GLException} to be thrown at exactly the point of failure. + * </p> + * <p> + * Sample code which installs this pipeline, manual: + * <pre> + * gl = drawable.setGL(new DebugGL(drawable.getGL())); + * </pre> + * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}. + * </p> + */ +public class DebugGL3bc extends DebugGL4bc { + public DebugGL3bc(GL3bc downstream) { + super((GL4bc)downstream); + } +} diff --git a/src/jogl/classes/javax/media/opengl/DebugGL4.java b/src/jogl/classes/javax/media/opengl/DebugGL4.java new file mode 100644 index 000000000..249d850a3 --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/DebugGL4.java @@ -0,0 +1,21 @@ +package javax.media.opengl; + +/** + * <p> + * Composable pipeline which wraps an underlying {@link GL} implementation, + * providing error checking after each OpenGL method call. If an error occurs, + * causes a {@link GLException} to be thrown at exactly the point of failure. + * </p> + * <p> + * Sample code which installs this pipeline, manual: + * <pre> + * gl = drawable.setGL(new DebugGL(drawable.getGL())); + * </pre> + * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}. + * </p> + */ +public class DebugGL4 extends DebugGL4bc { + public DebugGL4(GL4 downstream) { + super((GL4bc)downstream); + } +} diff --git a/src/jogl/classes/javax/media/opengl/DebugGLES2.java b/src/jogl/classes/javax/media/opengl/DebugGLES2.java new file mode 100644 index 000000000..6d666d82a --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/DebugGLES2.java @@ -0,0 +1,21 @@ +package javax.media.opengl; + +/** + * <p> + * Composable pipeline which wraps an underlying {@link GL} implementation, + * providing error checking after each OpenGL method call. If an error occurs, + * causes a {@link GLException} to be thrown at exactly the point of failure. + * </p> + * <p> + * Sample code which installs this pipeline, manual: + * <pre> + * gl = drawable.setGL(new DebugGL(drawable.getGL())); + * </pre> + * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}. + * </p> + */ +public class DebugGLES2 extends DebugGLES3 { + public DebugGLES2(GLES2 downstream) { + super((GLES3)downstream); + } +} diff --git a/src/jogl/classes/javax/media/opengl/DefaultGLCapabilitiesChooser.java b/src/jogl/classes/javax/media/opengl/DefaultGLCapabilitiesChooser.java index f678c709e..7e243471e 100644 --- a/src/jogl/classes/javax/media/opengl/DefaultGLCapabilitiesChooser.java +++ b/src/jogl/classes/javax/media/opengl/DefaultGLCapabilitiesChooser.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003-2009 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -85,25 +85,31 @@ import jogamp.opengl.Debug; */ public class DefaultGLCapabilitiesChooser implements GLCapabilitiesChooser { - private static final boolean DEBUG = Debug.isPropertyDefined("jogl.debug.CapabilitiesChooser", true); + private static final boolean DEBUG; - final static int NO_SCORE = -9999999; - final static int DOUBLE_BUFFER_MISMATCH_PENALTY = 1000; - final static int OPAQUE_MISMATCH_PENALTY = 750; - final static int STENCIL_MISMATCH_PENALTY = 500; - final static int MULTISAMPLE_MISMATCH_PENALTY = 500; - final static int MULTISAMPLE_EXTENSION_MISMATCH_PENALTY = 250; // just a little drop, no scale + static { + Debug.initSingleton(); + DEBUG = Debug.isPropertyDefined("jogl.debug.CapabilitiesChooser", true); + } + + private final static int NO_SCORE = -9999999; + private final static int DOUBLE_BUFFER_MISMATCH_PENALTY = 1000; + private final static int OPAQUE_MISMATCH_PENALTY = 750; + private final static int STENCIL_MISMATCH_PENALTY = 500; + private final static int MULTISAMPLE_MISMATCH_PENALTY = 500; + private final static int MULTISAMPLE_EXTENSION_MISMATCH_PENALTY = 250; // just a little drop, no scale // Pseudo attempt to keep equal rank penalties scale-equivalent // (e.g., stencil mismatch is 3 * accum because there are 3 accum // components) - final static int COLOR_MISMATCH_PENALTY_SCALE = 36; - final static int DEPTH_MISMATCH_PENALTY_SCALE = 6; - final static int ACCUM_MISMATCH_PENALTY_SCALE = 1; - final static int STENCIL_MISMATCH_PENALTY_SCALE = 3; - final static int MULTISAMPLE_MISMATCH_PENALTY_SCALE = 3; - + private final static int COLOR_MISMATCH_PENALTY_SCALE = 36; + private final static int DEPTH_MISMATCH_PENALTY_SCALE = 6; + private final static int ACCUM_MISMATCH_PENALTY_SCALE = 1; + private final static int STENCIL_MISMATCH_PENALTY_SCALE = 3; + private final static int MULTISAMPLE_MISMATCH_PENALTY_SCALE = 3; + + @Override public int chooseCapabilities(final CapabilitiesImmutable desired, - final List /*<CapabilitiesImmutable>*/ available, + final List<? extends CapabilitiesImmutable> available, final int windowSystemRecommendedChoice) { if ( null == desired ) { throw new NativeWindowException("Null desired capabilities"); @@ -116,6 +122,7 @@ public class DefaultGLCapabilitiesChooser implements GLCapabilitiesChooser { final int availnum = available.size(); if (DEBUG) { + Thread.dumpStack(); System.err.println("Desired: " + gldes); System.err.println("Available: " + availnum); for (int i = 0; i < available.size(); i++) { @@ -136,38 +143,46 @@ public class DefaultGLCapabilitiesChooser implements GLCapabilitiesChooser { // Create score array int[] scores = new int[availnum]; - + for (int i = 0; i < scores.length; i++) { scores[i] = NO_SCORE; } - final int gldes_samples = gldes.getSampleBuffers() ? gldes.getNumSamples() : 0; - + final int gldes_samples = gldes.getNumSamples(); + // Compute score for each for (int i = 0; i < availnum; i++) { - GLCapabilitiesImmutable cur = (GLCapabilitiesImmutable) available.get(i); + final GLCapabilitiesImmutable cur = (GLCapabilitiesImmutable) available.get(i); if (cur == null) { continue; } - if (gldes.isOnscreen() != cur.isOnscreen()) { - continue; + if (gldes.isOnscreen() && !cur.isOnscreen()) { + continue; // requested onscreen, but n/a } - if (!gldes.isOnscreen() && gldes.isPBuffer() && !cur.isPBuffer()) { - continue; // only skip if requested Offscreen && PBuffer, but no PBuffer available + if (!gldes.isOnscreen()) { + /** FBO is generic .. + if (gldes.isFBO() && !cur.isFBO()) { + continue; // requested FBO, but n/a + } */ + if (gldes.isPBuffer() && !cur.isPBuffer()) { + continue; // requested pBuffer, but n/a + } + if (gldes.isBitmap() && !cur.isBitmap()) { + continue; // requested pBuffer, but n/a + } } if (gldes.getStereo() != cur.getStereo()) { continue; } - final int cur_samples = - cur.getSampleBuffers() ? cur.getNumSamples() : 0; + final int cur_samples = cur.getNumSamples() ; int score = 0; - + // Compute difference in color depth // (Note that this decides the direction of all other penalties) score += (COLOR_MISMATCH_PENALTY_SCALE * ((cur.getRedBits() + cur.getGreenBits() + cur.getBlueBits() + cur.getAlphaBits()) - (gldes.getRedBits() + gldes.getGreenBits() + gldes.getBlueBits() + gldes.getAlphaBits()))); // Compute difference in depth buffer depth - score += (DEPTH_MISMATCH_PENALTY_SCALE * sign(score) * + score += (DEPTH_MISMATCH_PENALTY_SCALE * sign(score) * Math.abs(cur.getDepthBits() - gldes.getDepthBits())); // Compute difference in accumulation buffer depth score += (ACCUM_MISMATCH_PENALTY_SCALE * sign(score) * @@ -207,7 +222,7 @@ public class DefaultGLCapabilitiesChooser implements GLCapabilitiesChooser { if (score == NO_SCORE) { continue; } - GLCapabilitiesImmutable cur = (GLCapabilitiesImmutable) available.get(i); + final GLCapabilitiesImmutable cur = (GLCapabilitiesImmutable) available.get(i); if (cur.getHardwareAccelerated()) { int absScore = Math.abs(score); if (!gotHW || @@ -223,7 +238,7 @@ public class DefaultGLCapabilitiesChooser implements GLCapabilitiesChooser { if (score == NO_SCORE) { continue; } - GLCapabilitiesImmutable cur = (GLCapabilitiesImmutable) available.get(i); + final GLCapabilitiesImmutable cur = (GLCapabilitiesImmutable) available.get(i); if (!cur.getHardwareAccelerated()) { if (score <= 0) { score -= maxAbsoluteHWScore; @@ -241,12 +256,12 @@ public class DefaultGLCapabilitiesChooser implements GLCapabilitiesChooser { if (i > 0) { System.err.print(","); } - System.err.print(" " + scores[i]); + System.err.print(" " + i +": " + scores[i]); } System.err.println(" ]"); } - // Ready to select. Choose score closest to 0. + // Ready to select. Choose score closest to 0. int scoreClosestToZero = NO_SCORE; int chosenIndex = -1; for (int i = 0; i < availnum; i++) { diff --git a/src/jogl/classes/javax/media/opengl/FPSCounter.java b/src/jogl/classes/javax/media/opengl/FPSCounter.java index 9c07b58e4..4997258e0 100644 --- a/src/jogl/classes/javax/media/opengl/FPSCounter.java +++ b/src/jogl/classes/javax/media/opengl/FPSCounter.java @@ -36,28 +36,28 @@ import java.io.PrintStream; */ public interface FPSCounter { public static final int DEFAULT_FRAMES_PER_INTERVAL = 5*60; - + /** - * @param frames Update interval in frames.<br> At every rendered <i>frames</i> interval the currentTime and fps values are updated. + * @param frames Update interval in frames.<br> At every rendered <i>frames</i> interval the currentTime and fps values are updated. * If the <i>frames</i> interval is <= 0, no update will be issued, ie the FPSCounter feature is turned off. You may choose {@link #DEFAULT_FRAMES_PER_INTERVAL}. - * @param out optional print stream where the fps values gets printed if not null at every <i>frames</i> interval + * @param out optional print stream where the fps values gets printed if not null at every <i>frames</i> interval */ void setUpdateFPSFrames(int frames, PrintStream out); - + /** * Reset all performance counter (startTime, currentTime, frame number) */ void resetFPSCounter(); - + /** * @return update interval in frames - * + * * @see #setUpdateFPSFrames(int, PrintStream) */ int getUpdateFPSFrames(); - + /** - * Returns the time of the first display call in milliseconds after enabling this feature via {@link #setUpdateFPSFrames(int, PrintStream)}.<br> + * Returns the time of the first display call in milliseconds after enabling this feature via {@link #setUpdateFPSFrames(int, PrintStream)}.<br> * This value is reset via {@link #resetFPSCounter()}. * * @see #setUpdateFPSFrames(int, PrintStream) @@ -81,18 +81,18 @@ public interface FPSCounter { * @see #resetFPSCounter() */ long getLastFPSPeriod(); - + /** * @return Last update interval's frames per seconds, {@link #getUpdateFPSFrames()} / {@link #getLastFPSPeriod()} - * + * * @see #setUpdateFPSFrames(int, PrintStream) * @see #resetFPSCounter() */ - float getLastFPS(); - + float getLastFPS(); + /** * @return Number of frame rendered since {@link #getFPSStartTime()} up to {@link #getLastFPSUpdateTime()} - * + * * @see #setUpdateFPSFrames(int, PrintStream) * @see #resetFPSCounter() */ @@ -108,10 +108,10 @@ public interface FPSCounter { /** - * @return Total frames per seconds, {@link #getTotalFPSFrames()} / {@link #getTotalFPSDuration()} - * + * @return Total frames per seconds, {@link #getTotalFPSFrames()} / {@link #getTotalFPSDuration()} + * * @see #setUpdateFPSFrames(int, PrintStream) * @see #resetFPSCounter() */ - float getTotalFPS(); + float getTotalFPS(); } diff --git a/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java b/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java index 83e9e22c4..827145654 100644 --- a/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java +++ b/src/jogl/classes/javax/media/opengl/GLAnimatorControl.java @@ -29,24 +29,24 @@ package javax.media.opengl; /** - * An animator control interface, + * An animator control interface, * which implementation may drive a {@link javax.media.opengl.GLAutoDrawable} animation. */ public interface GLAnimatorControl extends FPSCounter { /** - * Indicates whether this animator is running, ie. has been started and not stopped. + * Indicates whether this animator has been {@link #start() started}. * * @see #start() * @see #stop() + * @see #isPaused() * @see #pause() * @see #resume() */ boolean isStarted(); /** - * Indicates whether this animator is running and animating,<br> - * the latter is true if it has {@link GLAutoDrawable}s to render and is not paused. + * Indicates whether this animator {@link #isStarted() is started} and {@link #isPaused() is not paused}. * * @see #start() * @see #stop() @@ -56,7 +56,9 @@ public interface GLAnimatorControl extends FPSCounter { boolean isAnimating(); /** - * Indicates whether this animator is running and paused. + * Indicates whether this animator {@link #isStarted() is started} + * and either {@link #pause() manually paused} or paused + * automatically due to no {@link #add(GLAutoDrawable) added} {@link GLAutoDrawable}s. * * @see #start() * @see #stop() @@ -75,30 +77,38 @@ public interface GLAnimatorControl extends FPSCounter { /** * Starts this animator, if not running. - * <P> + * <p> * In most situations this method blocks until * completion, except when called from the animation thread itself * or in some cases from an implementation-internal thread like the * AWT event queue thread. - * <P> + * </p> + * <p> + * Note that an animator w/o {@link #add(GLAutoDrawable) added drawables} + * will be paused automatically. + * </p> + * <p> * If started, all counters (time, frames, ..) are reset to zero. + * </p> * * @return true is started due to this call, * otherwise false, ie started already or unable to start. * * @see #stop() * @see #isAnimating() + * @see #isPaused() * @see #getThread() */ boolean start(); /** * Stops this animator. - * <P> + * <p> * In most situations this method blocks until * completion, except when called from the animation thread itself * or in some cases from an implementation-internal thread like the * AWT event queue thread. + * </p> * * @return true is stopped due to this call, * otherwise false, ie not started or unable to stop. @@ -111,13 +121,14 @@ public interface GLAnimatorControl extends FPSCounter { /** * Pauses this animator. - * <P> + * <p> * In most situations this method blocks until * completion, except when called from the animation thread itself * or in some cases from an implementation-internal thread like the * AWT event queue thread. + * </p> * - * @return false if if not started or already paused, otherwise true + * @return false if not started, already paused or failed to pause, otherwise true * * @see #resume() * @see #isAnimating() @@ -126,15 +137,17 @@ public interface GLAnimatorControl extends FPSCounter { /** * Resumes animation if paused. - * <P> + * <p> * In most situations this method blocks until * completion, except when called from the animation thread itself * or in some cases from an implementation-internal thread like the * AWT event queue thread. - * <P> + * </p> + * <p> * If resumed, all counters (time, frames, ..) are reset to zero. + * </p> * - * @return false if if not started or not paused, otherwise true + * @return false if not started, not paused or unable to resume, otherwise true * * @see #pause() * @see #isAnimating() @@ -142,13 +155,30 @@ public interface GLAnimatorControl extends FPSCounter { boolean resume(); /** - * Removes a drawable from the animator's list of rendering drawables.<br> + * Adds a drawable to this animator's list of rendering drawables. + * <p> + * This allows the animator thread to become {@link #isAnimating() animating}, + * in case the first drawable is added and the animator {@link #isStarted() is started}. + * </p> + * + * @param drawable the drawable to be added + * @throws IllegalArgumentException if drawable was already added to this animator + */ + void add(GLAutoDrawable drawable); + + /** + * Removes a drawable from the animator's list of rendering drawables. + * <p> * This method should get called in case a drawable becomes invalid, - * and will not be recovered.<br> - * This allows the animator thread to become idle in case the last drawable - * has reached it's end of life.<br> - * - * @param drawable the to be removed drawable + * and will not be recovered. + * </p> + * <p> + * This allows the animator thread to become {@link #isAnimating() not animating}, + * in case the last drawable has been removed. + * </p> + * + * @param drawable the drawable to be removed + * @throws IllegalArgumentException if drawable was not added to this animator */ void remove(GLAutoDrawable drawable); } diff --git a/src/jogl/classes/javax/media/opengl/GLArrayData.java b/src/jogl/classes/javax/media/opengl/GLArrayData.java index 7c56b53cb..97f58a92a 100644 --- a/src/jogl/classes/javax/media/opengl/GLArrayData.java +++ b/src/jogl/classes/javax/media/opengl/GLArrayData.java @@ -30,6 +30,8 @@ package javax.media.opengl; import java.nio.Buffer; +import javax.media.opengl.fixedfunc.GLPointerFunc; + /** * * The total number of bytes hold by the referenced buffer is: @@ -38,6 +40,19 @@ import java.nio.Buffer; */ public interface GLArrayData { /** + * Implementation and type dependent object association. + * <p> + * One currently known use case is to associate a {@link com.jogamp.opengl.util.glsl.ShaderState ShaderState} + * to an GLSL aware vertex attribute object, allowing to use the ShaderState to handle it's + * data persistence, location and state change.<br/> + * This is implicitly done via {@link com.jogamp.opengl.util.glsl.ShaderState#ownAttribute(GLArrayData, boolean) shaderState.ownAttribute(GLArrayData, boolean)}. + * </p> + * @param obj implementation and type dependent association + * @param enable pass true to enable the association and false to disable it. + */ + public void associate(Object obj, boolean enable); + + /** * Returns true if this data set is intended for a GLSL vertex shader attribute, * otherwise false, ie intended for fixed function vertex pointer */ @@ -47,10 +62,10 @@ public interface GLArrayData { * The index of the predefined array index, see list below, * or -1 in case of a shader attribute array. * - * @see javax.media.opengl.GL2#GL_VERTEX_ARRAY - * @see javax.media.opengl.GL2#GL_NORMAL_ARRAY - * @see javax.media.opengl.GL2#GL_COLOR_ARRAY - * @see javax.media.opengl.GL2#GL_TEXTURE_COORD_ARRAY + * @see GLPointerFunc#GL_VERTEX_ARRAY + * @see GLPointerFunc#GL_NORMAL_ARRAY + * @see GLPointerFunc#GL_COLOR_ARRAY + * @see GLPointerFunc#GL_TEXTURE_COORD_ARRAY */ public int getIndex(); @@ -61,6 +76,11 @@ public interface GLArrayData { /** * Set a new name for this array. + * <p> + * This clears the location, i.e. sets it to -1. + * </p> + * @see #setLocation(int) + * @see #setLocation(GL2ES2, int) */ public void setName(String newName); @@ -72,12 +92,35 @@ public interface GLArrayData { public int getLocation(); /** - * Sets the determined location of the shader attribute - * This is usually done within ShaderState. + * Sets the given location of the shader attribute * + * @return the given location * @see com.jogamp.opengl.util.glsl.ShaderState#vertexAttribPointer(GL2ES2, GLArrayData) */ - public void setLocation(int v); + public int setLocation(int v); + + /** + * Retrieves the location of the shader attribute from the linked shader program. + * <p> + * No validation is performed within the implementation. + * </p> + * @param gl + * @param program + * @return ≥0 denotes a valid attribute location as found and used in the given shader program. + * <0 denotes an invalid location, i.e. not found or used in the given shader program. + */ + public int setLocation(GL2ES2 gl, int program); + + /** + * Binds the location of the shader attribute to the given location for the unlinked shader program. + * <p> + * No validation is performed within the implementation. + * </p> + * @param gl + * @param program + * @return the given location + */ + public int setLocation(GL2ES2 gl, int program, int location); /** * Determines whether the data is server side (VBO) and enabled, @@ -107,7 +150,7 @@ public interface GLArrayData { */ public int getVBOTarget(); - + /** * The Buffer holding the data, may be null if a GPU buffer without client bound data */ @@ -136,7 +179,7 @@ public interface GLArrayData { * In case the buffer's position is 0 (sealed, flipped), it's based on it's limit instead of it's position. */ public int getElementCount(); - + /** * The currently used size in bytes.<br> * In case the buffer's position is 0 (sealed, flipped), it's based on it's limit instead of it's position. @@ -144,16 +187,21 @@ public interface GLArrayData { public int getSizeInBytes(); /** - * True, if GL shall normalize fixed point data while converting - * them into float + * True, if GL shall normalize fixed point data while converting + * them into float. + * <p> + * Default behavior (of the fixed function pipeline) is <code>true</code> + * for fixed point data type and <code>false</code> for floating point data types. + * </p> */ public boolean getNormalized(); - /** + /** * @return the byte offset between consecutive components - */ + */ public int getStride(); + @Override public String toString(); public void destroy(GL gl); diff --git a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java index c676535c8..bf8fe65ac 100644 --- a/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java +++ b/src/jogl/classes/javax/media/opengl/GLAutoDrawable.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,53 +29,62 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package javax.media.opengl; +import java.util.List; + import jogamp.opengl.Debug; /** A higher-level abstraction than {@link GLDrawable} which supplies an event based mechanism ({@link GLEventListener}) for performing OpenGL rendering. A GLAutoDrawable automatically creates a primary rendering context which is associated with the GLAutoDrawable for - the lifetime of the object. This context has the {@link - GLContext#setSynchronized synchronized} property enabled so that - calls to {@link GLContext#makeCurrent makeCurrent} will block if - the context is current on another thread. This allows the internal - GLContext for the GLAutoDrawable to be used both by the event - based rendering mechanism as well by end users directly.<P> - + the lifetime of the object. + <p> + Since the {@link GLContext} {@link GLContext#makeCurrent makeCurrent} + implementation is synchronized, i.e. blocks if the context + is current on another thread, the internal + {@link GLContext} for the GLAutoDrawable can be used for the event + based rendering mechanism and by end users directly. + </p> + <h5><a name="initialization">GLAutoDrawable Initialization</a></h5> + <p> The implementation shall initialize itself as soon as possible, - ie if the attached {@link javax.media.nativewindow.NativeSurface NativeSurface} becomes visible/realized. - The following protocol shall be satisfied: + which is only possible <i>after</i> the attached {@link javax.media.nativewindow.NativeSurface NativeSurface} becomes visible and and is realized.<br> + The following initialization sequence should be implemented: <ul> <li> Create the {@link GLDrawable} with the requested {@link GLCapabilities}</li> <li> Notify {@link GLDrawable} to validate the {@link GLCapabilities} by calling {@link GLDrawable#setRealized setRealized(true)}.</li> <li> Create the new {@link GLContext}.</li> <li> Initialize all OpenGL resources by calling {@link GLEventListener#init init(..)} for all - registered {@link GLEventListener}s. This can be done immediatly, or with the followup {@link #display display(..)} call.</li> + registered {@link GLEventListener}s. This can be done immediately, or with the followup {@link #display display(..)} call.</li> <li> Send a reshape event by calling {@link GLEventListener#reshape reshape(..)} for all registered {@link GLEventListener}s. This shall be done after the {@link GLEventListener#init init(..)} calls.</li> - </ul><P> - - Another implementation detail is the drawable reconfiguration. One use case is where a window is being - dragged to another screen with a different pixel configuration, ie {@link GLCapabilities}. The implementation - shall be able to detect such cases in conjunction with the associated {@link javax.media.nativewindow.NativeSurface NativeSurface}.<br> + </ul> + Note: The last to {@link GLEventListener} actions shall be also performed, when {@link #addGLEventListener(GLEventListener) adding} + a new one to an already initialized {@link GLAutoDrawable}. + </p> + <h5><a name="reconfiguration">GLAutoDrawable Reconfiguration</a></h5> + <p> + Another implementation detail is the {@link GLDrawable} reconfiguration. One use case is where a window is being + dragged to another screen with a different pixel configuration, ie {@link GLCapabilities}. The implementation + shall be able to detect such cases in conjunction with the associated {@link javax.media.nativewindow.NativeSurface NativeSurface}.<br/> For example, AWT's {@link java.awt.Canvas} 's {@link java.awt.Canvas#getGraphicsConfiguration getGraphicsConfiguration()} - is capable to determine a display device change. This is demonstrated within {@link javax.media.opengl.awt.GLCanvas}'s - and NEWT's <code>AWTCanvas</code> {@link javax.media.opengl.awt.GLCanvas#getGraphicsConfiguration getGraphicsConfiguration()} - specialization. Another demonstration is NEWT's {@link javax.media.nativewindow.NativeWindow NativeWindow} - implementation on the Windows platform, which utilizes the native platform's <i>MonitorFromWindow(HWND)</i> function.<br> - All OpenGL resources shall be regenerated, while the drawable's {@link GLCapabilities} has - to be choosen again. The following protocol shall be satisfied. + is capable to determine a display device change. This is demonstrated within {@link javax.media.opengl.awt.GLCanvas}'s + and NEWT's <code>AWTCanvas</code> {@link javax.media.opengl.awt.GLCanvas#getGraphicsConfiguration getGraphicsConfiguration()} + specialization. Another demonstration is NEWT's {@link javax.media.nativewindow.NativeWindow NativeWindow} + implementation on the Windows platform, which utilizes the native platform's <i>MonitorFromWindow(HWND)</i> function.<br/> + All OpenGL resources shall be regenerated, while the drawable's {@link GLCapabilities} has + to be chosen again. The following protocol shall be satisfied. <ul> <li> Controlled disposal:</li> <ul> @@ -95,26 +104,32 @@ import jogamp.opengl.Debug; registered {@link GLEventListener}s. This shall be done after the {@link GLEventListener#init init(..)} calls.</li> </ul> </ul> - Note: Current graphics driver keep the surface configuration for a given window, even if the window is moved to + Note: Current graphics driver keep the surface configuration for a given window, even if the window is moved to a monitor with a different pixel configuration, ie 32bpp to 16bpp. However, it is best to not assume such behavior - and make your application comply with the above protocol.<P> - - However, to not introduce to much breakage with older applications and because of the situation - mentioned above, the <code>boolean</code> system property <code>jogl.screenchange.action</code> will control the - screen change action as follows:<br> - + and make your application comply with the above protocol. + <p> + Avoiding breakage with older applications and because of the situation + mentioned above, the <code>boolean</code> system property <code>jogl.screenchange.action</code> will control the + screen change action as follows:<br/> <PRE> - -Djogl.screenchange.action=false Disable the drawable reconfiguration (the default) - -Djogl.screenchange.action=true Enable the drawable reconfiguration - </PRE> + -Djogl.screenchange.action=false Disable the {@link GLDrawable} reconfiguration (the default) + -Djogl.screenchange.action=true Enable the {@link GLDrawable} reconfiguration + </PRE> + </p> */ public interface GLAutoDrawable extends GLDrawable { - /** Flag reflecting wheather the drawable reconfiguration will be issued in - * case a screen device change occured, e.g. in a multihead environment, + /** Flag reflecting whether the {@link GLDrawable} reconfiguration will be issued in + * case a screen device change occurred, e.g. in a multihead environment, * where you drag the window to another monitor. */ public static final boolean SCREEN_CHANGE_ACTION_ENABLED = Debug.getBooleanProperty("jogl.screenchange.action", true); /** + * If the implementation uses delegation, return the delegated {@link GLDrawable} instance, + * otherwise return <code>this</code> instance. + */ + public GLDrawable getDelegatedDrawable(); + + /** * Returns the context associated with this drawable. The returned * context will be synchronized. * Don't rely on it's identity, the context may change. @@ -122,53 +137,195 @@ public interface GLAutoDrawable extends GLDrawable { public GLContext getContext(); /** - * Associate a new context to this drawable. + * 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. + * </p> + * + * @param newCtx the new context, maybe <code>null</code> for dis-association. + * @param destroyPrevCtx if <code>true</code>, destroy the previous context if exists + * @return the previous GLContext, maybe <code>null</code> + * + * @see GLContext#setGLDrawable(GLDrawable, boolean) + * @see GLContext#setGLReadDrawable(GLDrawable) + * @see jogamp.opengl.GLDrawableHelper#switchContext(GLDrawable, GLContext, boolean, GLContext, int) */ - public void setContext(GLContext context); + public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx); - /** Adds a {@link GLEventListener} to the end of this drawable queue. - The listeners are notified of events in the order of the queue. */ + /** + * Adds the given {@link GLEventListener listener} to the end of this drawable queue. + * The {@link GLEventListener listeners} are notified of events in the order of the queue. + * <p> + * The newly added listener's {@link GLEventListener#init(GLAutoDrawable) init(..)} + * method will be called once before any other of it's callback methods. + * See {@link #getGLEventListenerInitState(GLEventListener)} for details. + * </p> + * @param listener The GLEventListener object to be inserted + */ public void addGLEventListener(GLEventListener listener); - /** - * Adds a {@link GLEventListener} at the given index of this drawable queue. - * The listeners are notified of events in the order of the queue. - * @param index Position where the listener will be inserted. + /** + * Adds the given {@link GLEventListener listener} at the given index of this drawable queue. + * The {@link GLEventListener listeners} are notified of events in the order of the queue. + * <p> + * The newly added listener's {@link GLEventListener#init(GLAutoDrawable) init(..)} + * method will be called once before any other of it's callback methods. + * See {@link #getGLEventListenerInitState(GLEventListener)} for details. + * </p> + * @param index Position where the listener will be inserted. * Should be within (0 <= index && index <= size()). * An index value of -1 is interpreted as the end of the list, size(). * @param listener The GLEventListener object to be inserted * @throws IndexOutOfBoundsException If the index is not within (0 <= index && index <= size()), or -1 */ - public void addGLEventListener(int index, GLEventListener listener) - throws IndexOutOfBoundsException; + public void addGLEventListener(int index, GLEventListener listener) throws IndexOutOfBoundsException; + + /** + * Returns the number of {@link GLEventListener} of this drawable queue. + * @return The number of GLEventListener objects of this drawable queue. + */ + public int getGLEventListenerCount(); + + /** + * Returns the {@link GLEventListener} at the given index of this drawable queue. + * @param index Position of the listener to be returned. + * Should be within (0 <= index && index < size()). + * An index value of -1 is interpreted as last listener, size()-1. + * @return The GLEventListener object at the given index. + * @throws IndexOutOfBoundsException If the index is not within (0 <= index && index < size()), or -1 + */ + public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException; - /** Removes a {@link GLEventListener} from this drawable. Note that - if this is done from within a particular drawable's {@link - GLEventListener} handler (reshape, display, etc.) that it is not - guaranteed that all other listeners will be evaluated properly - during this update cycle. */ - public void removeGLEventListener(GLEventListener listener); + /** + * Retrieves whether the given {@link GLEventListener listener} is initialized or not. + * <p> + * After {@link #addGLEventListener(GLEventListener) adding} a {@link GLEventListener} it is + * marked <i>uninitialized</i> and added to a list of to be initialized {@link GLEventListener}. + * If such <i>uninitialized</i> {@link GLEventListener}'s handler methods (reshape, display) + * are about to be invoked, it's {@link GLEventListener#init(GLAutoDrawable) init(..)} method is invoked first. + * Afterwards the {@link GLEventListener} is marked <i>initialized</i> + * and removed from the list of to be initialized {@link GLEventListener}. + * </p> + * <p> + * This methods returns the {@link GLEventListener} initialized state, + * i.e. returns <code>false</code> if it is included in the list of to be initialized {@link GLEventListener}, + * otherwise <code>true</code>. + * </p> + * @param listener the GLEventListener object to query it's initialized state. + */ + public boolean getGLEventListenerInitState(GLEventListener listener); + + /** + * Sets the given {@link GLEventListener listener's} initialized state. + * <p> + * This methods allows manually setting the {@link GLEventListener} initialized state, + * i.e. adding it to, or removing it from the list of to be initialized {@link GLEventListener}. + * See {@link #getGLEventListenerInitState(GLEventListener)} for details. + * </p> + * <p> + * <b>Warning:</b> This method does not validate whether the given {@link GLEventListener listener's} + * is member of this drawable queue, i.e. {@link #addGLEventListener(GLEventListener) added}. + * </p> + * <p> + * This method is only exposed to allow users full control over the {@link GLEventListener}'s state + * and is usually not recommended to change. + * </p> + * <p> + * One use case is moving a {@link GLContext} and their initialized {@link GLEventListener} + * from one {@link GLAutoDrawable} to another, + * where a subsequent {@link GLEventListener#init(GLAutoDrawable) init(..)} call after adding it + * to the new owner is neither required nor desired. + * See {@link com.jogamp.opengl.util.GLDrawableUtil#swapGLContextAndAllGLEventListener(GLAutoDrawable, GLAutoDrawable) swapGLContextAndAllGLEventListener(..)}. + * </p> + * @param listener the GLEventListener object to perform a state change. + * @param initialized if <code>true</code>, mark the listener initialized, otherwise uninitialized. + */ + public void setGLEventListenerInitState(GLEventListener listener, boolean initialized); + + /** + * Disposes the given {@link GLEventListener listener} via {@link GLEventListener#dispose(GLAutoDrawable) dispose(..)} + * if it has been initialized and added to this queue. + * <p> + * If <code>remove</code> is <code>true</code>, the {@link GLEventListener} is removed from this drawable queue before disposal, + * otherwise marked uninitialized. + * </p> + * <p> + * If an {@link GLAnimatorControl} is being attached and the current thread is different + * than {@link GLAnimatorControl#getThread() the animator's thread}, it is paused during the operation. + * </p> + * <p> + * Note that this is an expensive operation, since {@link GLEventListener#dispose(GLAutoDrawable) dispose(..)} + * is decorated by {@link GLContext#makeCurrent()} and {@link GLContext#release()}. + * </p> + * <p> + * Use {@link #removeGLEventListener(GLEventListener) removeGLEventListener(listener)} instead + * if you just want to remove the {@link GLEventListener listener} and <i>don't care</i> about the disposal of the it's (OpenGL) resources. + * </p> + * <p> + * Also note that this is done from within a particular drawable's + * {@link GLEventListener} handler (reshape, display, etc.), that it is not + * guaranteed that all other listeners will be evaluated properly + * during this update cycle. + * </p> + * @param listener The GLEventListener object to be disposed and removed if <code>remove</code> is <code>true</code> + * @param remove pass <code>true</code> to have the <code>listener</code> removed from this drawable queue, otherwise pass <code>false</code> + * @return the disposed and/or removed GLEventListener, or null if no action was performed, i.e. listener was not added + */ + public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove); /** + * Removes the given {@link GLEventListener listener} from this drawable queue. * <p> + * This is an inexpensive operation, since the removed listener's + * {@link GLEventListener#dispose(GLAutoDrawable) dispose(..)} method will <i>not</i> be called. + * </p> + * <p> + * Use {@link #disposeGLEventListener(GLEventListener, boolean) disposeGLEventListener(listener, true)} + * instead to ensure disposal of the {@link GLEventListener listener}'s (OpenGL) resources. + * </p> + * <p> + * Note that if this is done from within a particular drawable's + * {@link GLEventListener} handler (reshape, display, etc.), that it is not + * guaranteed that all other listeners will be evaluated properly + * during this update cycle. + * </p> + * @param listener The GLEventListener object to be removed + * @return the removed GLEventListener, or null if listener was not added + */ + public GLEventListener removeGLEventListener(GLEventListener listener); + + /** * Registers the usage of an animator, an {@link javax.media.opengl.GLAnimatorControl} implementation. - * The animator will be queried whether it's animating, ie periodically issuing {@link #display()} calls or not.</p><br> + * The animator will be queried whether it's animating, ie periodically issuing {@link #display()} calls or not. * <p> * This method shall be called by an animator implementation only,<br> * e.g. {@link com.jogamp.opengl.util.Animator#add(javax.media.opengl.GLAutoDrawable)}, passing it's control implementation,<br> - * and {@link com.jogamp.opengl.util.Animator#remove(javax.media.opengl.GLAutoDrawable)}, passing <code>null</code>.</p><br> + * and {@link com.jogamp.opengl.util.Animator#remove(javax.media.opengl.GLAutoDrawable)}, passing <code>null</code>. + * </p> * <p> * Impacts {@link #display()} and {@link #invoke(boolean, GLRunnable)} semantics.</p><br> * - * @param animator <code>null</code> reference indicates no animator is using - * this <code>GLAutoDrawable</code>,<br> - * a valid reference indicates an animator is using this <code>GLAutoDrawable</code>. + * @param animatorControl <code>null</code> reference indicates no animator is using + * this <code>GLAutoDrawable</code>,<br> + * a valid reference indicates an animator is using this <code>GLAutoDrawable</code>. * * @throws GLException if an animator is already registered. * @see #display() * @see #invoke(boolean, GLRunnable) * @see javax.media.opengl.GLAnimatorControl - */ + */ public abstract void setAnimator(GLAnimatorControl animatorControl) throws GLException; /** @@ -179,30 +336,96 @@ public interface GLAutoDrawable extends GLDrawable { */ public GLAnimatorControl getAnimator(); - /** + /** + * Dedicates this instance's {@link GLContext} to the given thread.<br/> + * The thread will exclusively claim the {@link GLContext} via {@link #display()} and not release it + * until {@link #destroy()} or <code>setExclusiveContextThread(null)</code> has been called. + * <p> + * Default non-exclusive behavior is <i>requested</i> via <code>setExclusiveContextThread(null)</code>, + * which will cause the next call of {@link #display()} on the exclusive thread to + * release the {@link GLContext}. Only after it's async release, {@link #getExclusiveContextThread()} + * will return <code>null</code>. + * </p> + * <p> + * To release a previous made exclusive thread, a user issues <code>setExclusiveContextThread(null)</code> + * and may poll {@link #getExclusiveContextThread()} until it returns <code>null</code>, + * <i>while</i> the exclusive thread is still running. + * </p> * <p> + * Note: Setting a new exclusive thread without properly releasing a previous one + * will throw an GLException. + * </p> + * <p> + * Note: Utilizing this feature w/ AWT could lead to an AWT-EDT deadlock, depending on the AWT implementation. + * Hence it is advised not to use it with native AWT GLAutoDrawable like GLCanvas. + * </p> + * <p> + * One scenario could be to dedicate the context to the {@link GLAnimatorControl#getThread() animator thread} + * and spare redundant context switches, see {@link com.jogamp.opengl.util.AnimatorBase#setExclusiveContext(boolean)}. + * </p> + * @param t the exclusive thread to claim the context, or <code>null</code> for default operation. + * @return previous exclusive context thread + * @throws GLException If an exclusive thread is still active but a new one is attempted to be set + * @see com.jogamp.opengl.util.AnimatorBase#setExclusiveContext(boolean) + */ + public Thread setExclusiveContextThread(Thread t) throws GLException; + + /** + * @see #setExclusiveContextThread(Thread) + */ + public Thread getExclusiveContextThread(); + + /** * Enqueues a one-shot {@link GLRunnable}, * which will be executed within the next {@link #display()} call - * after all registered {@link GLEventListener}s - * {@link GLEventListener#display(GLAutoDrawable) display(GLAutoDrawable)} - * methods has been called. - * </p> + * after all registered {@link GLEventListener}s + * {@link GLEventListener#display(GLAutoDrawable) display(GLAutoDrawable)} + * methods have been called. * <p> * If no {@link GLAnimatorControl} is animating (default),<br> * or if the current thread is the animator thread,<br> - * a {@link #display()} call is issued after enqueue the <code>GLRunnable</code>.<br> - * No extra synchronization is performed in case <code>wait</code> is true, since it is executed in the current thread.</p> + * a {@link #display()} call is issued after enqueue the <code>GLRunnable</code>, + * hence the {@link GLRunnable} will be executed right away.<br/> + * </p> * <p> - * If an {@link GLAnimatorControl} is animating,<br> - * no {@link #display()} call is issued, since the animator thread performs it.<br> - * If <code>wait</code> is true, the implementation waits until the <code>GLRunnable</code> is executed.<br> - * </p><br> + * If an {@link GLAnimatorControl animator} is running,<br> + * no explicit {@link #display()} call is issued, allowing the {@link GLAnimatorControl animator} to perform at due time.<br> + * </p> + * <p> + * If <code>wait</code> is <code>true</code> the call blocks until the <code>glRunnable</code> + * has been executed by the {@link GLAnimatorControl animator}, otherwise the method returns immediately. + * </p> + * <p> + * If <code>wait</code> is <code>true</code> <b>and</b> + * {@link #isRealized()} returns <code>false</code> <i>or</i> {@link #getContext()} returns <code>null</code>, + * the call is ignored and returns <code>false</code>.<br> + * This helps avoiding deadlocking the caller. + * </p> + * <p> + * The internal queue of {@link GLRunnable}'s is being flushed with {@link #destroy()} + * where all blocked callers are being notified. + * </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>. * * @see #setAnimator(GLAnimatorControl) * @see #display() * @see GLRunnable - */ - public void invoke(boolean wait, GLRunnable glRunnable); + * @see #invoke(boolean, List) + */ + public boolean invoke(boolean wait, GLRunnable glRunnable); + + /** + * Extends {@link #invoke(boolean, GLRunnable)} functionality + * allowing to inject a list of {@link GLRunnable}s. + * @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>. + * @see #invoke(boolean, GLRunnable) + */ + public boolean invoke(boolean wait, List<GLRunnable> glRunnables); /** Destroys all resources associated with this GLAutoDrawable, inclusive the GLContext. @@ -214,7 +437,7 @@ public interface GLAutoDrawable extends GLDrawable { routine may be called manually. */ public void destroy(); - /** + /** * <p> * Causes OpenGL rendering to be performed for this GLAutoDrawable * in the following order: @@ -224,21 +447,21 @@ public interface GLAutoDrawable extends GLDrawable { * <li> Executes all one-shot {@link javax.media.opengl.GLRunnable GLRunnable}, * enqueued via {@link #invoke(boolean, GLRunnable)}.</li> * </ul></p> - * <p> + * <p> * May be called periodically by a running {@link javax.media.opengl.GLAnimatorControl} implementation,<br> * which must register itself with {@link #setAnimator(javax.media.opengl.GLAnimatorControl)}.</p> * <p> * Called automatically by the window system toolkit upon receiving a repaint() request, <br> * except an {@link javax.media.opengl.GLAnimatorControl} implementation {@link javax.media.opengl.GLAnimatorControl#isAnimating()}.</p> - * <p> + * <p> * This routine may also be called manually for better control over the * rendering process. It is legal to call another GLAutoDrawable's * display method from within the {@link GLEventListener#display * display(..)} callback.</p> * <p> - * In case of a new generated OpenGL context, + * In case of a new generated OpenGL context, * the implementation shall call {@link GLEventListener#init init(..)} for all - * registered {@link GLEventListener}s <i>before</i> making the + * registered {@link GLEventListener}s <i>before</i> making the * actual {@link GLEventListener#display display(..)} calls, * in case this has not been done yet.</p> * @@ -260,17 +483,32 @@ public interface GLAutoDrawable extends GLDrawable { /** * @param flags Additional context creation flags. - * + * * @see GLContext#setContextCreationFlags(int) * @see GLContext#enableGLDebugMessage(boolean) */ public void setContextCreationFlags(int flags); - + /** * @return Additional context creation flags */ public int getContextCreationFlags(); + /** + * {@inheritDoc} + * <p> + * This GLAutoDrawable implementation holds it's own GLContext reference, + * thus created a GLContext using this methods won't replace it implicitly. + * To replace or set this GLAutoDrawable's GLContext you need to call {@link #setContext(GLContext, boolean)}. + * </p> + * <p> + * The GLAutoDrawable implementation shall also set the + * context creation flags as customized w/ {@link #setContextCreationFlags(int)}. + * </p> + */ + @Override + public GLContext createContext(GLContext shareWith); + /** Returns the {@link GL} pipeline object this GLAutoDrawable uses. If this method is called outside of the {@link GLEventListener}'s callback methods (init, display, etc.) it may @@ -290,4 +528,27 @@ public interface GLAutoDrawable extends GLDrawable { @return the set GL pipeline or null if not successful */ public GL setGL(GL gl); + /** + * Method <i>may</i> return the upstream UI toolkit object + * holding this {@link GLAutoDrawable} instance, if exist. + * <p> + * Currently known Java UI toolkits and it's known return types are: + * + * <table border="1"> + * <tr><td>Toolkit</td> <td>GLAutoDrawable Implementation</td> <td>~</td> <td>Return Type of getUpstreamWidget()</td</tr> + * <tr><td>NEWT</td> <td>{@link com.jogamp.newt.opengl.GLWindow}</td> <td>has a</td> <td>{@link com.jogamp.newt.Window}</td</tr> + * <tr><td>SWT</td> <td>{@link com.jogamp.opengl.swt.GLCanvas}</td> <td>is a</td> <td>{@link org.eclipse.swt.widgets.Canvas}</td</tr> + * <tr><td>AWT</td> <td>{@link javax.media.opengl.awt.GLCanvas}</td> <td>is a</td> <td>{@link java.awt.Canvas}</td</tr> + * <tr><td>AWT</td> <td>{@link javax.media.opengl.awt.GLJPanel}</td> <td>is a</td> <td>{@link javax.swing.JPanel}</td</tr> + * </table> + * However, the result may be other object types than the listed above + * due to new supported toolkits. + * </p> + * <p> + * This method may also return <code>null</code> if no UI toolkit is being used, + * as common for offscreen rendering. + * </p> + */ + public Object getUpstreamWidget(); + } diff --git a/src/jogl/classes/javax/media/opengl/GLBase.java b/src/jogl/classes/javax/media/opengl/GLBase.java index bd24b15bc..3f0c77949 100644 --- a/src/jogl/classes/javax/media/opengl/GLBase.java +++ b/src/jogl/classes/javax/media/opengl/GLBase.java @@ -32,7 +32,7 @@ package javax.media.opengl; /** * <P>The base interface from which all GL profiles derive, providing * checked conversion down to concrete profiles, access to the - * OpenGL context associated with the GL and extension/function + * OpenGL context associated with the GL and extension/function * availability queries as described below.</P> * * <P> While the APIs for vendor extensions are unconditionally @@ -79,7 +79,7 @@ package javax.media.opengl; * */ public interface GLBase { - + /** * Indicates whether this GL object conforms to any of the OpenGL profiles. */ @@ -88,35 +88,41 @@ public interface GLBase { /** * Indicates whether this GL object conforms to the OpenGL ≥ 4.0 compatibility profile. * The GL4 compatibility profile includes the GL2, GL2ES1, GL2ES2, GL3, GL3bc and GL4 profile. + * @see GLContext#isGL4bc() */ public boolean isGL4bc(); /** * Indicates whether this GL object conforms to the OpenGL ≥ 4.0 core profile. * The GL4 core profile includes the GL2ES2, and GL3 profile. + * @see GLContext#isGL4() */ public boolean isGL4(); /** * Indicates whether this GL object conforms to the OpenGL ≥ 3.1 compatibility profile. * The GL3 compatibility profile includes the GL2, GL2ES1, GL2ES2 and GL3 profile. + * @see GLContext#isGL3bc() */ public boolean isGL3bc(); /** * Indicates whether this GL object conforms to the OpenGL ≥ 3.1 core profile. * The GL3 core profile includes the GL2ES2 profile. + * @see GLContext#isGL3() */ public boolean isGL3(); /** * Indicates whether this GL object conforms to the OpenGL ≤ 3.0 profile. * The GL2 profile includes the GL2ES1 and GL2ES2 profile. + * @see GLContext#isGL2() */ public boolean isGL2(); /** * Indicates whether this GL object conforms to the OpenGL ES1 ≥ 1.0 profile. + * @see GLContext#isGLES1() */ public boolean isGLES1(); @@ -125,105 +131,219 @@ public interface GLBase { * <p> * Remark: ES2 compatible desktop profiles are not included. * To query whether core ES2 functionality is provided, use {@link #isGLES2Compatible()}. - * </p> + * </p> * @see #isGLES2Compatible() + * @see GLContext#isGLES2() */ public boolean isGLES2(); /** + * Indicates whether this GL object conforms to the OpenGL ES2 ≥ 3.0 profile. + * <p> + * Remark: ES3 compatible desktop profiles are not included. + * To query whether core ES3 functionality is provided, use {@link #isGLES3Compatible()}. + * </p> + * @see #isGLES3Compatible() + * @see GLContext#isGLES3() + */ + public boolean isGLES3(); + + /** * Indicates whether this GL object conforms to one of the OpenGL ES profiles, - * see {@link #isGLES1()} and {@link #isGLES2()}. + * see {@link #isGLES1()}, {@link #isGLES2()} and {@link #isGLES3()}. + * @see GLContext#isGLES() */ public boolean isGLES(); /** * Indicates whether this GL object conforms to a GL2ES1 compatible profile. + * @see GLContext#isGL2ES1() */ public boolean isGL2ES1(); /** * Indicates whether this GL object conforms to a GL2ES2 compatible profile. + * @see GLContext#isGL2ES2() */ public boolean isGL2ES2(); /** - * Indicates whether this GL object is compatible with the core OpenGL ES2 functionality. - * @return true if this context is an ES2 context or implements - * the extension <code>GL_ARB_ES2_compatibility</code>, otherwise false + * Indicates whether this GL object conforms to a either a GL2GL3 or GL3ES3 compatible profile. + * @see GLContext#isGL2ES3() */ - public boolean isGLES2Compatible(); + public boolean isGL2ES3(); + + /** + * Indicates whether this GL object conforms to a GL3ES3 compatible profile. + * @see GLContext#isGL3ES3() + */ + public boolean isGL3ES3(); + + /** + * Returns true if this GL object conforms to a GL4ES3 compatible profile, i.e. if {@link #isGLES3Compatible()} returns true. + * <p>Includes [ GL ≥ 4.3, GL ≥ 3.1 w/ GL_ARB_ES3_compatibility and GLES3 ]</p> + * @see GLContext#isGL4ES3() + */ + public boolean isGL4ES3(); /** * Indicates whether this GL object conforms to a GL2GL3 compatible profile. + * @see GLContext#isGL2GL3() */ public boolean isGL2GL3(); - /** Indicates whether this GL object supports GLSL. */ + /** + * Indicates whether this GL object uses a GL4 core profile. <p>Includes [ GL4 ].</p> + * @see GLContext#isGL4core() + */ + public boolean isGL4core(); + + /** + * Indicates whether this GL object uses a GL3 core profile. <p>Includes [ GL4, GL3 ].</p> + * @see GLContext#isGL3core() + */ + public boolean isGL3core(); + + /** + * Indicates whether this GL object uses a GL core profile. <p>Includes [ GL4, GL3, GLES3, GL2ES2 ].</p> + * @see GLContext#isGLcore() + */ + public boolean isGLcore(); + + /** + * Indicates whether this GL object is compatible with the core OpenGL ES2 functionality. + * @return true if this context is an ES2 context or implements + * the extension <code>GL_ARB_ES2_compatibility</code>, otherwise false + * @see GLContext#isGLES2Compatible() + */ + public boolean isGLES2Compatible(); + + /** + * Indicates whether this GL object is compatible with the core OpenGL ES3 functionality. + * <p> + * Return true if the underlying context is an ES3 context or implements + * the extension <code>GL_ARB_ES3_compatibility</code>, otherwise false. + * </p> + * <p> + * Includes [ GL ≥ 4.3, GL ≥ 3.1 w/ GL_ARB_ES3_compatibility and GLES3 ] + * </p> + * @see GLContext#isGLES3Compatible() + */ + public boolean isGLES3Compatible(); + + /** + * Indicates whether this GL object supports GLSL. + * @see GLContext#hasGLSL() + */ public boolean hasGLSL(); /** + * Returns the downstream GL instance in case this is a wrapping pipeline, otherwise <code>null</code>. + * <p> + * See {@link #getRootGL()} for retrieving the implementing root instance. + * </p> + * @throws GLException if the downstream instance is not null and not a GL implementation + * @see #getRootGL() + */ + public GL getDownstreamGL() throws GLException; + + /** + * Returns the implementing root instance, considering a wrapped pipelined hierarchy, see {@link #getDownstreamGL()}. + * <p> + * If this instance is not a wrapping pipeline, i.e. has no downstream instance, + * this instance is returned. + * </p> + * @throws GLException if the root instance is not a GL implementation + */ + public GL getRootGL() throws GLException; + + /** * Casts this object to the GL interface. - * @throws GLException if this GLObject is not a GL implementation + * @throws GLException if this object is not a GL implementation */ public GL getGL() throws GLException; /** * Casts this object to the GL4bc interface. - * @throws GLException if this GLObject is not a GL4bc implementation + * @throws GLException if this object is not a GL4bc implementation */ public GL4bc getGL4bc() throws GLException; /** * Casts this object to the GL4 interface. - * @throws GLException if this GLObject is not a GL4 implementation + * @throws GLException if this object is not a GL4 implementation */ public GL4 getGL4() throws GLException; /** * Casts this object to the GL3bc interface. - * @throws GLException if this GLObject is not a GL3bc implementation + * @throws GLException if this object is not a GL3bc implementation */ public GL3bc getGL3bc() throws GLException; /** * Casts this object to the GL3 interface. - * @throws GLException if this GLObject is not a GL3 implementation + * @throws GLException if this object is not a GL3 implementation */ public GL3 getGL3() throws GLException; /** * Casts this object to the GL2 interface. - * @throws GLException if this GLObject is not a GL2 implementation + * @throws GLException if this object is not a GL2 implementation */ public GL2 getGL2() throws GLException; /** * Casts this object to the GLES1 interface. - * @throws GLException if this GLObject is not a GLES1 implementation + * @throws GLException if this object is not a GLES1 implementation */ public GLES1 getGLES1() throws GLException; /** * Casts this object to the GLES2 interface. - * @throws GLException if this GLObject is not a GLES2 implementation + * @throws GLException if this object is not a GLES2 implementation */ public GLES2 getGLES2() throws GLException; /** + * Casts this object to the GLES3 interface. + * @throws GLException if this object is not a GLES3 implementation + */ + public GLES3 getGLES3() throws GLException; + + /** * Casts this object to the GL2ES1 interface. - * @throws GLException if this GLObject is not a GL2ES1 implementation + * @throws GLException if this object is not a GL2ES1 implementation */ public GL2ES1 getGL2ES1() throws GLException; /** * Casts this object to the GL2ES2 interface. - * @throws GLException if this GLObject is not a GL2ES2 implementation + * @throws GLException if this object is not a GL2ES2 implementation */ public GL2ES2 getGL2ES2() throws GLException; /** + * Casts this object to the GL2ES3 interface. + * @throws GLException if this object is not a GL2ES3 implementation + */ + public GL2ES3 getGL2ES3() throws GLException; + + /** + * Casts this object to the GL3ES3 interface. + * @throws GLException if this object is not a GL3ES3 implementation + */ + public GL3ES3 getGL3ES3() throws GLException; + + /** + * Casts this object to the GL4ES3 interface. + * @throws GLException if this object is not a GL4ES3 implementation + */ + public GL4ES3 getGL4ES3() throws GLException; + + /** * Casts this object to the GL2GL3 interface. - * @throws GLException if this GLObject is not a GL2GL3 implementation + * @throws GLException if this object is not a GL2GL3 implementation */ public GL2GL3 getGL2GL3() throws GLException; @@ -240,14 +360,14 @@ public interface GLBase { /** * Returns true if the specified OpenGL core- or extension-function can be * used successfully through this GL instance given the current host (OpenGL - * <i>client</i>) and display (OpenGL <i>server</i>) configuration.<P> - * By "successfully" we mean that the function is both <i>callable</i> - * on the machine running the program and <i>available</i> on the current - * display.<P> + * <i>client</i>) and display (OpenGL <i>server</i>) configuration.<P> + * By "successfully" we mean that the function is both <i>callable</i> + * on the machine running the program and <i>available</i> on the current + * display.<P> * * In order to call a function successfully, the function must be both - * <i>callable</i> on the machine running the program and <i>available</i> on - * the display device that is rendering the output (note: on non-networked, + * <i>callable</i> on the machine running the program and <i>available</i> on + * the display device that is rendering the output (note: on non-networked, * single-display machines these two conditions are identical; on networked and/or * multi-display machines this becomes more complicated). These conditions are * met if the function is either part of the core OpenGL version supported by @@ -256,7 +376,7 @@ public interface GLBase { * * A GL function is <i>callable</i> if it is successfully linked at runtime, * hence the GLContext must be made current at least once. - * + * * @param glFunctionName the name of the OpenGL function (e.g., use * "glBindRenderbufferEXT" or "glBindRenderbuffer" to check if {@link * GL#glBindRenderbuffer(int,int)} is available). @@ -266,7 +386,7 @@ public interface GLBase { /** * Returns true if the specified OpenGL extension can be * used successfully through this GL instance given the current host (OpenGL - * <i>client</i>) and display (OpenGL <i>server</i>) configuration.<P> + * <i>client</i>) and display (OpenGL <i>server</i>) configuration.<P> * * @param glExtensionName the name of the OpenGL extension (e.g., * "GL_ARB_vertex_program"). @@ -274,16 +394,53 @@ public interface GLBase { public boolean isExtensionAvailable(String glExtensionName); /** + * Returns <code>true</code> if basic FBO support is available, otherwise <code>false</code>. + * <p> + * Basic FBO is supported if the context is either GL-ES >= 2.0, GL >= 3.0 [core, compat] or implements the extensions + * <code>GL_ARB_ES2_compatibility</code>, <code>GL_ARB_framebuffer_object</code>, <code>GL_EXT_framebuffer_object</code> or <code>GL_OES_framebuffer_object</code>. + * </p> + * <p> + * Basic FBO support may only include one color attachment and no multisampling, + * as well as limited internal formats for renderbuffer. + * </p> + * @see GLContext#hasBasicFBOSupport() + */ + public boolean hasBasicFBOSupport(); + + /** + * Returns <code>true</code> if full FBO support is available, otherwise <code>false</code>. + * <p> + * Full FBO is supported if the context is either GL >= core 3.0 [ES, core, compat] or implements the extensions + * <code>ARB_framebuffer_object</code>, or all of + * <code>EXT_framebuffer_object</code>, <code>EXT_framebuffer_multisample</code>, + * <code>EXT_framebuffer_blit</code>, <code>GL_EXT_packed_depth_stencil</code>. + * </p> + * <p> + * Full FBO support includes multiple color attachments and multisampling. + * </p> + * @see GLContext#hasFullFBOSupport() + */ + public boolean hasFullFBOSupport(); + + /** + * Returns the maximum number of FBO RENDERBUFFER samples + * if {@link #hasFullFBOSupport() full FBO is supported}, otherwise false. + * @see GLContext#getMaxRenderbufferSamples() + */ + public int getMaxRenderbufferSamples(); + + /** * Returns true if the GL context supports non power of two (NPOT) textures, * otherwise false. * <p> * NPOT textures are supported in OpenGL >= 3, GLES2 or if the * 'GL_ARB_texture_non_power_of_two' extension is available. * </p> - * @return */ public boolean isNPOTTextureAvailable(); + public boolean isTextureFormatBGRA8888Available(); + /** Provides a platform-independent way to specify the minimum swap interval for buffer swaps. An argument of 0 disables sync-to-vertical-refresh completely, while an argument of 1 @@ -292,7 +449,7 @@ public interface GLBase { is usually either 0 or 1. This function is not guaranteed to have an effect, and in particular only affects heavyweight onscreen components. - + @see #getSwapInterval @throws GLException if this context is not the current */ @@ -301,8 +458,8 @@ public interface GLBase { /** Provides a platform-independent way to get the swap interval set by {@link #setSwapInterval}. <br> - If the interval is not set by {@link #setSwapInterval} yet, - -1 is returned, indicating that the platforms default + If the interval is not set by {@link #setSwapInterval} yet, + -1 is returned, indicating that the platforms default is being used. @see #setSwapInterval @@ -340,5 +497,154 @@ public interface GLBase { * completeness. */ public Object getExtension(String extensionName); + + /** Aliased entrypoint of <code> void {@native glClearDepth}(GLclampd depth); </code> and <code> void {@native glClearDepthf}(GLclampf depth); </code>. */ + public void glClearDepth( double depth ); + + /** Aliased entrypoint of <code> void {@native glDepthRange}(GLclampd depth); </code> and <code> void {@native glDepthRangef}(GLclampf depth); </code>. */ + public void glDepthRange(double zNear, double zFar); + + /** + * @deprecated Avoid original GL API namespace conflict. Use {@link #getBoundBuffer(int)} + */ + public int glGetBoundBuffer(int target); + /** + * @param target a GL buffer (VBO) target as used in {@link GL#glBindBuffer(int, int)}, ie {@link GL#GL_ELEMENT_ARRAY_BUFFER}, {@link GL#GL_ARRAY_BUFFER}, .. + * @return the GL buffer name bound to a target via {@link GL#glBindBuffer(int, int)} or 0 if unbound. + * @see #getBufferStorage(int) + */ + public int getBoundBuffer(int target); + + /** + * @deprecated Use {@link #getBufferStorage(int)}. + */ + public long glGetBufferSize(int bufferName); + /** + * @param bufferName a GL buffer name, generated with e.g. {@link GL#glGenBuffers(int, int[], int)} and used in {@link GL#glBindBuffer(int, int)}, {@link GL#glBufferData(int, long, java.nio.Buffer, int)} or {@link GL2#glNamedBufferDataEXT(int, long, java.nio.Buffer, int)}. + * @return the size of the given GL buffer storage, see {@link GLBufferStorage} + * @see #getBoundBuffer(int) + */ + public GLBufferStorage getBufferStorage(int bufferName); + + /** + * Returns the {@link GLBufferStorage} instance as mapped via OpenGL's native {@link GL#glMapBuffer(int, int) glMapBuffer(..)} implementation. + * <p> + * Throws a {@link GLException} if GL-function constraints are not met. + * </p> + * <p> + * {@link GL#glMapBuffer(int, int)} wrapper calls this method and returns {@link GLBufferStorage#getMappedBuffer()}. + * </p> + * <p> + * A zero {@link GLBufferStorage#getSize()} will avoid a native call and returns the unmapped {@link GLBufferStorage}. + * </p> + * <p> + * A null native mapping result indicating an error will + * not cause a GLException but returns the unmapped {@link GLBufferStorage}. + * This allows the user to handle this case. + * </p> + * @param target denotes the buffer via it's bound target + * @param access the mapping access mode + * @throws GLException if buffer is not bound to target + * @throws GLException if buffer is not tracked + * @throws GLException if buffer is already mapped + * @throws GLException if buffer has invalid store size, i.e. less-than zero + */ + public GLBufferStorage mapBuffer(int target, int access) throws GLException; + + /** + * Returns the {@link GLBufferStorage} instance as mapped via OpenGL's native {@link GL#glMapBufferRange(int, long, long, int) glMapBufferRange(..)} implementation. + * <p> + * Throws a {@link GLException} if GL-function constraints are not met. + * </p> + * <p> + * {@link GL#glMapBufferRange(int, long, long, int)} wrapper calls this method and returns {@link GLBufferStorage#getMappedBuffer()}. + * </p> + * <p> + * A zero {@link GLBufferStorage#getSize()} will avoid a native call and returns the unmapped {@link GLBufferStorage}. + * </p> + * <p> + * A null native mapping result indicating an error will + * not cause a GLException but returns the unmapped {@link GLBufferStorage}. + * This allows the user to handle this case. + * </p> + * @param target denotes the buffer via it's bound target + * @param offset offset of the mapped buffer's storage + * @param length length of the mapped buffer's storage + * @param access the mapping access mode + * @throws GLException if buffer is not bound to target + * @throws GLException if buffer is not tracked + * @throws GLException if buffer is already mapped + * @throws GLException if buffer has invalid store size, i.e. less-than zero + * @throws GLException if buffer mapping range does not fit, incl. offset + */ + public GLBufferStorage mapBufferRange(final int target, final long offset, final long length, final int access) throws GLException; + + /** + * @deprecated Avoid original GL API namespace conflict. Use {@link #isVBOArrayBound()} + */ + public boolean glIsVBOArrayBound(); + /** + * @return true if a VBO is bound to {@link GL#GL_ARRAY_BUFFER} via {@link GL#glBindBuffer(int, int)}, otherwise false + */ + public boolean isVBOArrayBound(); + + /** + * @deprecated Avoid original GL API namespace conflict. Use {@link #isVBOElementArrayBound()} + */ + public boolean glIsVBOElementArrayBound(); + /** + * @return true if a VBO is bound to {@link GL#GL_ELEMENT_ARRAY_BUFFER} via {@link GL#glBindBuffer(int, int)}, otherwise false + */ + public boolean isVBOElementArrayBound(); + + /** + * Return the framebuffer name bound to this context, + * see {@link GL#glBindFramebuffer(int, int)}. + */ + public int getBoundFramebuffer(int target); + + /** + * Return the default draw framebuffer name. + * <p> + * May differ from it's default <code>zero</code> + * in case an framebuffer object ({@link com.jogamp.opengl.FBObject}) based drawable + * is being used. + * </p> + */ + public int getDefaultDrawFramebuffer(); + + /** + * Return the default read framebuffer name. + * <p> + * May differ from it's default <code>zero</code> + * in case an framebuffer object ({@link com.jogamp.opengl.FBObject}) based drawable + * is being used. + * </p> + */ + public int getDefaultReadFramebuffer(); + + /** + * Returns the default color buffer within the current bound + * {@link #getDefaultReadFramebuffer()}, i.e. GL_READ_FRAMEBUFFER, + * which will be used as the source for pixel reading commands, + * like {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer) glReadPixels} etc. + * <p> + * For offscreen framebuffer objects this is {@link GL#GL_COLOR_ATTACHMENT0}, + * otherwise this is {@link GL#GL_FRONT} for single buffer configurations + * and {@link GL#GL_BACK} for double buffer configurations. + * </p> + * <p> + * Note-1: Neither ES1 nor ES2 supports selecting the read buffer via glReadBuffer + * and {@link GL#GL_BACK} is the default. + * </p> + * <p> + * Note-2: ES3 only supports {@link GL#GL_BACK}, {@link GL#GL_NONE} or {@link GL#GL_COLOR_ATTACHMENT0}+i + * </p> + * <p> + * Note-3: See {@link com.jogamp.opengl.util.GLDrawableUtil#swapBuffersBeforeRead(GLCapabilitiesImmutable) swapBuffersBeforeRead} + * for read-pixels and swap-buffers implications. + * </p> + */ + public int getDefaultReadBuffer(); } diff --git a/src/jogl/classes/javax/media/opengl/GLBufferStorage.java b/src/jogl/classes/javax/media/opengl/GLBufferStorage.java new file mode 100644 index 000000000..929ecf60a --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/GLBufferStorage.java @@ -0,0 +1,150 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package javax.media.opengl; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +/** + * OpenGL buffer storage object reflecting it's + * <ul> + * <li>storage size</li> + * <li>storage memory if mapped</li> + * <li>mutable usage or immutable flags</li> + * </ul> + * <p> + * Buffer storage is created via: + * <ul> + * <li><code>glBufferStorage</code> - storage creation with target</li> + * <li>{@link GL#glBufferData(int, long, java.nio.Buffer, int)} - storage recreation with target</li> + * <li>{@link GL2#glNamedBufferDataEXT(int, long, java.nio.Buffer, int)} - storage recreation, direct</li> + * </ul> + * Note that storage <i>recreation</i> as mentioned above also invalidate a previous storage instance, + * i.e. disposed the buffer's current storage if exist and attaches a new storage instance. + * </p> + * <p> + * Buffer storage is disposed via: + * <ul> + * <li>{@link GL#glDeleteBuffers(int, IntBuffer)} - explicit, direct, via {@link #notifyBuffersDeleted(int, IntBuffer)} or {@link #notifyBuffersDeleted(int, int[], int)}</li> + * <li>{@link GL#glBufferData(int, long, java.nio.Buffer, int)} - storage recreation via target</li> + * <li>{@link GL2#glNamedBufferDataEXT(int, long, java.nio.Buffer, int)} - storage recreation, direct</li> + * </ul> + * </p> + * <p> + * GL buffer storage is mapped via + * <ul> + * + * <li>{@link GL#mapBuffer(int, int)}</li> + * <li>{@link GL#mapBufferRange(int, long, long, int)}</li> + * <li>{@link GL2#mapNamedBuffer(int, int)}</li> + * <li>{@link GL2#mapNamedBufferRange(int, long, long, int)}</li> + * </ul> + * </p> + * <p> + * GL buffer storage is unmapped via + * <ul> + * <li>{@link GL#glUnmapBuffer(int)} - explicit via target</li> + * <li>{@link GL2#glUnmapNamedBufferEXT(int)} - explicit direct</li> + * <li>{@link GL#glBufferData(int, long, java.nio.Buffer, int)} - storage recreation via target</li> + * <li>{@link GL2#glNamedBufferDataEXT(int, long, java.nio.Buffer, int)} - storage recreation, direct</li> + * <li>{@link GL#glDeleteBuffers(int, IntBuffer)} - buffer deletion</li> + * </ul> + * </p> + */ +public abstract class GLBufferStorage { + private final int name; + private final long size; + private final int mutableUsage; + private final int immutableFlags; + protected ByteBuffer mappedBuffer; + + protected GLBufferStorage(final int name, final long size, final int mutableUsage, final int immutableFlags) { + this.name = name; + this.size = size; + this.mutableUsage = mutableUsage; + this.immutableFlags = immutableFlags; + this.mappedBuffer = null; + } + + /** Return the buffer name */ + public final int getName() { return name; } + + /** Return the buffer's storage size. */ + public final long getSize() { return size; } + + /** + * Returns <code>true</code> if buffer's storage is mutable, i.e. + * created via {@link GL#glBufferData(int, long, java.nio.Buffer, int)}. + * <p> + * Returns <code>false</code> if buffer's storage is immutable, i.e. + * created via <code>glBufferStorage</code>. FIXME: Add GL 4.4 support! + * </p> + * @return + */ + public final boolean isMutableStorage() { return 0 != mutableUsage; } + + /** + * Returns the mutable storage usage or 0 if storage is not {@link #isMutableStorage() mutable}. + */ + public final int getMutableUsage() { return mutableUsage; } + + /** + * Returns the immutable storage flags, invalid if storage is {@link #isMutableStorage() mutable}. + */ + public final int getImmutableFlags() { return immutableFlags; } + + /** + * Returns the mapped ByteBuffer, or null if not mapped. + * Mapping may occur via: + * <ul> + * <li>{@link GL#glMapBuffer(int, int)}</li> + * <li>{@link GL#glMapBufferRange(int, long, long, int)}</li> + * <li>{@link GL2#glMapNamedBufferEXT(int, int)}</li> + * <li>{@link GL2#glMapNamedBufferRangeEXT(int, long, long, int)} + * </ul> + */ + public final ByteBuffer getMappedBuffer() { return mappedBuffer; } + + public final String toString() { + return toString(false); + } + public final String toString(final boolean skipMappedBuffer) { + final String s0; + if( isMutableStorage() ) { + s0 = String.format("%s[name %s, size %d, mutable usage 0x%X", msgClazzName, name, size, mutableUsage); + } else { + s0 = String.format("%s[name %s, size %d, immutable flags 0x%X", msgClazzName, name, size, immutableFlags); + } + if(skipMappedBuffer) { + return s0+"]"; + } else { + return s0+", mapped "+mappedBuffer+"]"; + } + } + private final String msgClazzName = "GLBufferStorage"; +} diff --git a/src/jogl/classes/javax/media/opengl/GLCapabilities.java b/src/jogl/classes/javax/media/opengl/GLCapabilities.java index 8b832b310..b825d6388 100644 --- a/src/jogl/classes/javax/media/opengl/GLCapabilities.java +++ b/src/jogl/classes/javax/media/opengl/GLCapabilities.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003-2009 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -41,6 +41,7 @@ package javax.media.opengl; import javax.media.nativewindow.Capabilities; +import javax.media.nativewindow.CapabilitiesImmutable; /** Specifies a set of OpenGL capabilities.<br> At creation time of a {@link GLDrawable} using {@link GLDrawableFactory}, @@ -49,13 +50,15 @@ import javax.media.nativewindow.Capabilities; must support, such as the OpenGL profile, color depth and whether stereo is enabled.<br> The actual capabilites of created {@link GLDrawable}s are then reflected by their own - GLCapabilites instance, which can be queried with {@link GLDrawable#getGLCapabilities()}.<br> + GLCapabilites instance, which can be queried with {@link GLDrawable#getChosenGLCapabilities()}. + <br> It currently contains the minimal number of routines which allow configuration on all supported window systems. */ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabilitiesImmutable { - private GLProfile glProfile = null; - private boolean pbuffer = false; + private GLProfile glProfile = null; + private boolean isPBuffer = false; + private boolean isFBO = false; private boolean doubleBuffered = true; private boolean stereo = false; private boolean hardwareAccelerated = true; @@ -73,11 +76,6 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil private boolean sampleBuffers = false; private int numSamples = 2; - // Bits for pbuffer creation - private boolean pbufferFloatingPointBuffers; - private boolean pbufferRenderToTexture; - private boolean pbufferRenderToTextureRectangle; - /** Creates a GLCapabilities object. All attributes are in a default state. * @param glp GLProfile, or null for the default GLProfile * @throws GLException if no profile is given and no default profile is available for the default device. @@ -86,10 +84,12 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil glProfile = (null!=glp)?glp:GLProfile.getDefault(GLProfile.getDefaultDevice()); } + @Override public Object cloneMutable() { return clone(); } + @Override public Object clone() { try { return super.clone(); @@ -98,27 +98,53 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil } } + /** + * Copies all {@link GLCapabilities} and {@link Capabilities} values + * from <code>source</code> into this instance. + * @return this instance + */ + public GLCapabilities copyFrom(GLCapabilitiesImmutable source) { + super.copyFrom(source); + glProfile = source.getGLProfile(); + isPBuffer = source.isPBuffer(); + isFBO = source.isFBO(); + doubleBuffered = source.getDoubleBuffered(); + stereo = source.getStereo(); + hardwareAccelerated = source.getHardwareAccelerated(); + depthBits = source.getDepthBits(); + stencilBits = source.getStencilBits(); + accumRedBits = source.getAccumRedBits(); + accumGreenBits = source.getAccumGreenBits(); + accumBlueBits = source.getAccumBlueBits(); + accumAlphaBits = source.getAccumAlphaBits(); + sampleBuffers = source.getSampleBuffers(); + numSamples = source.getNumSamples(); + sampleExtension = source.getSampleExtension(); + return this; + } + + @Override public int hashCode() { // 31 * x == (x << 5) - x - int hash = 31 + this.glProfile.hashCode() ; - hash = ((hash << 5) - hash) + ( this.pbuffer ? 1 : 0 ); - hash = ((hash << 5) - hash) + ( this.stereo ? 1 : 0 ); + int hash = super.hashCode(); + hash = ((hash << 5) - hash) + this.glProfile.hashCode() ; hash = ((hash << 5) - hash) + ( this.hardwareAccelerated ? 1 : 0 ); + hash = ((hash << 5) - hash) + ( this.stereo ? 1 : 0 ); + hash = ((hash << 5) - hash) + ( this.isFBO ? 1 : 0 ); + hash = ((hash << 5) - hash) + ( this.isPBuffer ? 1 : 0 ); + hash = ((hash << 5) - hash) + ( this.sampleBuffers ? 1 : 0 ); + hash = ((hash << 5) - hash) + this.getNumSamples(); + hash = ((hash << 5) - hash) + this.sampleExtension.hashCode(); hash = ((hash << 5) - hash) + this.depthBits; hash = ((hash << 5) - hash) + this.stencilBits; hash = ((hash << 5) - hash) + this.accumRedBits; hash = ((hash << 5) - hash) + this.accumGreenBits; hash = ((hash << 5) - hash) + this.accumBlueBits; hash = ((hash << 5) - hash) + this.accumAlphaBits; - hash = ((hash << 5) - hash) + ( this.sampleBuffers ? 1 : 0 ); - hash = ((hash << 5) - hash) + this.numSamples; - hash = ((hash << 5) - hash) + this.sampleExtension.hashCode(); - hash = ((hash << 5) - hash) + ( this.pbufferFloatingPointBuffers ? 1 : 0 ); - hash = ((hash << 5) - hash) + ( this.pbufferRenderToTexture ? 1 : 0 ); - hash = ((hash << 5) - hash) + ( this.pbufferRenderToTextureRectangle ? 1 : 0 ); return hash; } + @Override public boolean equals(Object obj) { if(this == obj) { return true; } if(!(obj instanceof GLCapabilitiesImmutable)) { @@ -127,7 +153,9 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil GLCapabilitiesImmutable other = (GLCapabilitiesImmutable)obj; boolean res = super.equals(obj) && other.getGLProfile()==glProfile && - other.isPBuffer()==pbuffer && + other.isPBuffer()==isPBuffer && + other.isFBO()==isFBO && + other.getDoubleBuffered() == doubleBuffered && other.getStereo()==stereo && other.getHardwareAccelerated()==hardwareAccelerated && other.getDepthBits()==depthBits && @@ -136,52 +164,54 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil other.getAccumGreenBits()==accumGreenBits && other.getAccumBlueBits()==accumBlueBits && other.getAccumAlphaBits()==accumAlphaBits && - other.getSampleBuffers()==sampleBuffers && - other.getPbufferFloatingPointBuffers()==pbufferFloatingPointBuffers && - other.getPbufferRenderToTexture()==pbufferRenderToTexture && - other.getPbufferRenderToTextureRectangle()==pbufferRenderToTextureRectangle; - if(sampleBuffers) { - res = res && - other.getNumSamples()==numSamples && + other.getSampleBuffers()==sampleBuffers; + if(res && sampleBuffers) { + res = other.getNumSamples()==getNumSamples() && other.getSampleExtension().equals(sampleExtension) ; } return res; } /** comparing hw/sw, stereo, multisample, stencil, RGBA and depth only */ - public int compareTo(Object o) { - if ( ! ( o instanceof GLCapabilities ) ) { + @Override + public int compareTo(final CapabilitiesImmutable o) { + if ( ! ( o instanceof GLCapabilitiesImmutable ) ) { Class<?> c = (null != o) ? o.getClass() : null ; - throw new ClassCastException("Not a GLCapabilities object: " + c); + throw new ClassCastException("Not a GLCapabilitiesImmutable object, but " + c); } + final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) o; - final GLCapabilities caps = (GLCapabilities) o; + if(hardwareAccelerated && !caps.getHardwareAccelerated()) { + return 1; + } else if(!hardwareAccelerated && caps.getHardwareAccelerated()) { + return -1; + } - if(hardwareAccelerated && !caps.hardwareAccelerated) { + if(stereo && !caps.getStereo()) { return 1; - } else if(!hardwareAccelerated && caps.hardwareAccelerated) { + } else if(!stereo && caps.getStereo()) { return -1; } - if(stereo && !caps.stereo) { + if(doubleBuffered && !caps.getDoubleBuffered()) { return 1; - } else if(!stereo && caps.stereo) { + } else if(!doubleBuffered && caps.getDoubleBuffered()) { return -1; } - final int ms = sampleBuffers ? numSamples : 0; - final int xms = caps.sampleBuffers ? caps.numSamples : 0; + final int ms = getNumSamples(); + final int xms = caps.getNumSamples() ; if(ms > xms) { return 1; } else if( ms < xms ) { return -1; } - // ignore the sample extension + // ignore the sample extension - if(stencilBits > caps.stencilBits) { + if(stencilBits > caps.getStencilBits()) { return 1; - } else if(stencilBits < caps.stencilBits) { + } else if(stencilBits < caps.getStencilBits()) { return -1; } @@ -190,16 +220,17 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil return sc; } - if(depthBits > caps.depthBits) { + if(depthBits > caps.getDepthBits()) { return 1; - } else if(depthBits < caps.depthBits) { + } else if(depthBits < caps.getDepthBits()) { return -1; } return 0; // they are equal: hw/sw, stereo, multisample, stencil, RGBA and depth } - public GLProfile getGLProfile() { + @Override + public final GLProfile getGLProfile() { return glProfile; } @@ -208,35 +239,56 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil glProfile=profile; } - public boolean isPBuffer() { - return pbuffer; + @Override + public final boolean isPBuffer() { + return isPBuffer; } - /** - * Enables or disables pbuffer usage.<br> - * If enabled this method also invokes {@link #setOnscreen(int) setOnscreen(false)}<br> + /** + * Requesting offscreen pbuffer mode. + * <p> + * If enabled this method also invokes {@link #setOnscreen(boolean) setOnscreen(false)}. + * </p> + * <p> * Defaults to false. + * </p> + * <p> + * Requesting offscreen pbuffer mode disables the offscreen auto selection. + * </p> */ public void setPBuffer(boolean enable) { if(enable) { setOnscreen(false); } - pbuffer = enable; + isPBuffer = enable; + } + + @Override + public final boolean isFBO() { + return isFBO; } /** - * Sets whether the drawable surface supports onscreen.<br> - * If enabled this method also invokes {@link #setPBuffer(int) setPBuffer(false)}<br> - * Defaults to true. - */ - public void setOnscreen(boolean onscreen) { - if(onscreen) { - setPBuffer(false); + * Requesting offscreen FBO mode. + * <p> + * If enabled this method also invokes {@link #setOnscreen(boolean) setOnscreen(false)}. + * </p> + * <p> + * Defaults to false. + * </p> + * <p> + * Requesting offscreen FBO mode disables the offscreen auto selection. + * </p> + */ + public void setFBO(boolean enable) { + if(enable) { + setOnscreen(false); } - super.setOnscreen(onscreen); + isFBO = enable; } - public boolean getDoubleBuffered() { + @Override + public final boolean getDoubleBuffered() { return doubleBuffered; } @@ -245,25 +297,28 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil doubleBuffered = enable; } - public boolean getStereo() { + @Override + public final boolean getStereo() { return stereo; } - + /** Enables or disables stereo viewing. */ public void setStereo(boolean enable) { stereo = enable; } - public boolean getHardwareAccelerated() { + @Override + public final boolean getHardwareAccelerated() { return hardwareAccelerated; } - + /** Enables or disables hardware acceleration. */ public void setHardwareAccelerated(boolean enable) { hardwareAccelerated = enable; } - public int getDepthBits() { + @Override + public final int getDepthBits() { return depthBits; } @@ -271,8 +326,9 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil public void setDepthBits(int depthBits) { this.depthBits = depthBits; } - - public int getStencilBits() { + + @Override + public final int getStencilBits() { return stencilBits; } @@ -280,8 +336,9 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil public void setStencilBits(int stencilBits) { this.stencilBits = stencilBits; } - - public int getAccumRedBits() { + + @Override + public final int getAccumRedBits() { return accumRedBits; } @@ -293,7 +350,8 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil this.accumRedBits = accumRedBits; } - public int getAccumGreenBits() { + @Override + public final int getAccumGreenBits() { return accumGreenBits; } @@ -305,7 +363,8 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil this.accumGreenBits = accumGreenBits; } - public int getAccumBlueBits() { + @Override + public final int getAccumBlueBits() { return accumBlueBits; } @@ -317,7 +376,8 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil this.accumBlueBits = accumBlueBits; } - public int getAccumAlphaBits() { + @Override + public final int getAccumAlphaBits() { return accumAlphaBits; } @@ -333,14 +393,15 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil * Sets the desired extension for full-scene antialiasing * (FSAA), default is {@link #DEFAULT_SAMPLE_EXTENSION}. */ - public void setSampleExtension(String se) { - sampleExtension = se; + public void setSampleExtension(String se) { + sampleExtension = se; } - - public String getSampleExtension() { - return sampleExtension; + + @Override + public final String getSampleExtension() { + return sampleExtension; } - + /** * Defaults to false.<br> * Indicates whether sample buffers for full-scene antialiasing @@ -353,65 +414,40 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil sampleBuffers = enable; if(sampleBuffers && getAlphaBits()==0) { setAlphaBits(1); - } + } } - public boolean getSampleBuffers() { + @Override + public final boolean getSampleBuffers() { return sampleBuffers; } - /** If sample buffers are enabled, indicates the number of buffers - to be allocated. Defaults to 2. */ + /** + * If sample buffers are enabled, indicates the number of buffers + * to be allocated. Defaults to 2. + * @see #getNumSamples() + */ public void setNumSamples(int numSamples) { this.numSamples = numSamples; } - public int getNumSamples() { - return numSamples; - } - - /** For pbuffers only, indicates whether floating-point buffers - should be used if available. Defaults to false. */ - public void setPbufferFloatingPointBuffers(boolean enable) { - pbufferFloatingPointBuffers = enable; - } - - public boolean getPbufferFloatingPointBuffers() { - return pbufferFloatingPointBuffers; - } - - /** For pbuffers only, indicates whether the render-to-texture - extension should be used if available. Defaults to false. */ - public void setPbufferRenderToTexture(boolean enable) { - pbufferRenderToTexture = enable; - } - - public boolean getPbufferRenderToTexture() { - return pbufferRenderToTexture; - } - - /** For pbuffers only, indicates whether the - render-to-texture-rectangle extension should be used if - available. Defaults to false. */ - public void setPbufferRenderToTextureRectangle(boolean enable) { - pbufferRenderToTextureRectangle = enable; - } - - public boolean getPbufferRenderToTextureRectangle() { - return pbufferRenderToTextureRectangle; + @Override + public final int getNumSamples() { + return sampleBuffers ? numSamples : 0; } + @Override public StringBuilder toString(StringBuilder sink) { if(null == sink) { sink = new StringBuilder(); } - int samples = sampleBuffers ? numSamples : 0 ; + final int samples = sampleBuffers ? numSamples : 0 ; - super.toString(sink); + super.toString(sink, false); - sink.append(", accum-rgba ").append(accumRedBits).append("/").append(accumGreenBits).append("/").append(accumBlueBits).append("/").append(accumAlphaBits); - sink.append(", dp/st/ms: ").append(depthBits).append("/").append(stencilBits).append("/").append(samples); + sink.append(", accum-rgba ").append(accumRedBits).append(ESEP).append(accumGreenBits).append(ESEP).append(accumBlueBits).append(ESEP).append(accumAlphaBits); + sink.append(", dp/st/ms ").append(depthBits).append(ESEP).append(stencilBits).append(ESEP).append(samples); if(samples>0) { sink.append(", sample-ext ").append(sampleExtension); } @@ -431,22 +467,41 @@ public class GLCapabilities extends Capabilities implements Cloneable, GLCapabil sink.append(", sw, "); } sink.append(glProfile); - if(!isOnscreen()) { - if(pbuffer) { - sink.append(", pbuffer [r2t ").append(pbufferRenderToTexture?1:0) - .append(", r2tr ").append(pbufferRenderToTextureRectangle?1:0) - .append(", float ").append(pbufferFloatingPointBuffers?1:0) - .append("]"); + if(isOnscreen()) { + sink.append(", on-scr["); + } else { + sink.append(", offscr["); + } + boolean ns=false; + if(isFBO()) { + sink.append("fbo"); + ns = true; + } + if(isPBuffer()) { + if(ns) { sink.append(CSEP); } + sink.append("pbuffer"); + ns = true; + } + if(isBitmap()) { + if(ns) { sink.append(CSEP); } + sink.append("bitmap"); + ns = true; + } + if(!ns) { // !FBO !PBuffer !Bitmap + if(isOnscreen()) { + sink.append("."); // no additional off-screen modes besides on-screen } else { - sink.append(", pixmap"); + sink.append("auto-cfg"); // auto-config off-screen mode } } - + sink.append("]"); + return sink; } /** Returns a textual representation of this GLCapabilities - object. */ + object. */ + @Override public String toString() { StringBuilder msg = new StringBuilder(); msg.append("GLCaps["); diff --git a/src/jogl/classes/javax/media/opengl/GLCapabilitiesChooser.java b/src/jogl/classes/javax/media/opengl/GLCapabilitiesChooser.java index 73a77de27..2e0bec1f9 100644 --- a/src/jogl/classes/javax/media/opengl/GLCapabilitiesChooser.java +++ b/src/jogl/classes/javax/media/opengl/GLCapabilitiesChooser.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003-2009 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,28 +29,34 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package javax.media.opengl; +import java.util.List; + +import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.CapabilitiesChooser; +import javax.media.opengl.GLCapabilitiesImmutable; + /** Provides a mechanism by which applications can customize the window type selection for a given {@link GLCapabilities}. Developers can implement this interface and pass an instance into the appropriate method of {@link GLDrawableFactory}; the chooser will be called during the OpenGL context creation process. Note that this is only a marker interface; its signature is the same as - {@link CapabilitiesChooser}, but the array of {@link Capabilities} - objects passed to {@link #chooseCapabilities chooseCapabilities} - will actually be an array of {@link GLCapabilities}. */ + {@link CapabilitiesChooser} and the {@link List} of + objects extending {@link CapabilitiesImmutable} + passed to {@link #chooseCapabilities chooseCapabilities} + is actually a {@link List} of type {@link GLCapabilitiesImmutable}. */ public interface GLCapabilitiesChooser extends CapabilitiesChooser { } diff --git a/src/jogl/classes/javax/media/opengl/GLCapabilitiesImmutable.java b/src/jogl/classes/javax/media/opengl/GLCapabilitiesImmutable.java index 5f8795edc..dc28539a0 100644 --- a/src/jogl/classes/javax/media/opengl/GLCapabilitiesImmutable.java +++ b/src/jogl/classes/javax/media/opengl/GLCapabilitiesImmutable.java @@ -37,20 +37,20 @@ import javax.media.nativewindow.CapabilitiesImmutable; * @see javax.media.nativewindow.CapabilitiesImmutable */ public interface GLCapabilitiesImmutable extends CapabilitiesImmutable { - /** - * One of the platform's default sample extension + /** + * One of the platform's default sample extension * <code>EGL.EGL_SAMPLES, GLX.GLX_SAMPLES, WGLExt.WGL_SAMPLES_ARB</code> * if available, or any other <i>known</i> fallback one, ie <code>EGLExt.EGL_COVERAGE_SAMPLES_NV</code> */ public static final String DEFAULT_SAMPLE_EXTENSION = "default" ; - + /** * Returns the GL profile you desire or used by the drawable. */ GLProfile getGLProfile(); /** - * Returns the number of bits requested for the accumulation + * Returns the number of bits for the accumulation * buffer's alpha component. On some systems only the accumulation * buffer depth, which is the sum of the red, green, and blue bits, * is considered. @@ -58,7 +58,7 @@ public interface GLCapabilitiesImmutable extends CapabilitiesImmutable { int getAccumAlphaBits(); /** - * Returns the number of bits requested for the accumulation + * Returns the number of bits for the accumulation * buffer's blue component. On some systems only the accumulation * buffer depth, which is the sum of the red, green, and blue bits, * is considered. @@ -66,7 +66,7 @@ public interface GLCapabilitiesImmutable extends CapabilitiesImmutable { int getAccumBlueBits(); /** - * Returns the number of bits requested for the accumulation + * Returns the number of bits for the accumulation * buffer's green component. On some systems only the accumulation * buffer depth, which is the sum of the red, green, and blue bits, * is considered. @@ -74,7 +74,7 @@ public interface GLCapabilitiesImmutable extends CapabilitiesImmutable { int getAccumGreenBits(); /** - * Returns the number of bits requested for the accumulation + * Returns the number of bits for the accumulation * buffer's red component. On some systems only the accumulation * buffer depth, which is the sum of the red, green, and blue bits, * is considered. @@ -82,72 +82,91 @@ public interface GLCapabilitiesImmutable extends CapabilitiesImmutable { int getAccumRedBits(); /** - * Returns the number of bits requested for the depth buffer. + * Returns the number of depth buffer bits. */ int getDepthBits(); /** - * Indicates whether double-buffering is enabled. + * Returns whether double-buffering is requested, available or chosen. + * <p> + * Default is true. + * </p> */ boolean getDoubleBuffered(); /** - * Indicates whether hardware acceleration is enabled. + * Returns whether hardware acceleration is requested, available or chosen. + * <p> + * Default is true. + * </p> */ boolean getHardwareAccelerated(); /** - * Returns the used extension for full-scene antialiasing - * (FSAA), default is {@link #DEFAULT_SAMPLE_EXTENSION}. + * Returns the extension for full-scene antialiasing + * (FSAA). + * <p> + * Default is {@link #DEFAULT_SAMPLE_EXTENSION}. + * </p> */ String getSampleExtension(); - + /** * Returns whether sample buffers for full-scene antialiasing - * (FSAA) should be allocated for this drawable. Defaults to - * false. + * (FSAA) should be allocated for this drawable. + * <p> + * Default is false. + * </p> */ boolean getSampleBuffers(); /** * Returns the number of sample buffers to be allocated if sample - * buffers are enabled. Defaults to 2. + * buffers are enabled, otherwise returns 0. + * <p> + * Default is 0 due to disable sample buffers per default. + * </p> */ int getNumSamples(); /** - * For pbuffers only, returns whether floating-point buffers should - * be used if available. Defaults to false. - */ - boolean getPbufferFloatingPointBuffers(); - - /** - * For pbuffers only, returns whether the render-to-texture - * extension should be used if available. Defaults to false. - */ - boolean getPbufferRenderToTexture(); - - /** - * For pbuffers only, returns whether the render-to-texture - * extension should be used. Defaults to false. - */ - boolean getPbufferRenderToTextureRectangle(); - - /** - * Returns the number of bits requested for the stencil buffer. + * Returns the number of stencil buffer bits. + * <p> + * Default is 0. + * </p> */ int getStencilBits(); /** - * Indicates whether stereo is enabled. + * Returns whether stereo is requested, available or chosen. + * <p> + * Default is false. + * </p> */ boolean getStereo(); /** - * Indicates whether pbuffer is used/requested. + * Returns whether pbuffer offscreen mode is requested, available or chosen. + * <p> + * Default is false. + * </p> + * <p> + * For chosen capabilities, only the selected offscreen surface is set to <code>true</code>. + * </p> */ boolean isPBuffer(); + /** + * Returns whether FBO offscreen mode is requested, available or chosen. + * <p> + * Default is false. + * </p> + * <p> + * For chosen capabilities, only the selected offscreen surface is set to <code>true</code>. + * </p> + */ + boolean isFBO(); + @Override boolean equals(Object obj); diff --git a/src/jogl/classes/javax/media/opengl/GLContext.java b/src/jogl/classes/javax/media/opengl/GLContext.java index e23fa74f9..9a4311772 100644 --- a/src/jogl/classes/javax/media/opengl/GLContext.java +++ b/src/jogl/classes/javax/media/opengl/GLContext.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -42,16 +42,24 @@ package javax.media.opengl; import java.nio.IntBuffer; import java.util.HashMap; -import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Set; import javax.media.nativewindow.AbstractGraphicsDevice; -import com.jogamp.common.util.IntObjectHashMap; -import com.jogamp.common.util.locks.LockFactory; -import com.jogamp.common.util.locks.RecursiveLock; - import jogamp.opengl.Debug; import jogamp.opengl.GLContextImpl; +import jogamp.opengl.GLContextShareSet; + +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.VersionNumber; +import com.jogamp.common.util.VersionNumberString; +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; +import com.jogamp.opengl.GLExtensions; +import com.jogamp.opengl.GLRendererQuirks; /** Abstraction for an OpenGL rendering context. In order to perform OpenGL rendering, a context must be "made current" on the current @@ -66,144 +74,331 @@ import jogamp.opengl.GLContextImpl; abstraction provides a stable object which clients can use to refer to a given context. */ public abstract class GLContext { - public static final boolean DEBUG = Debug.debug("GLContext"); + + public static final boolean DEBUG = Debug.debug("GLContext"); public static final boolean TRACE_SWITCH = Debug.isPropertyDefined("jogl.debug.GLContext.TraceSwitch", true); - + public static final boolean DEBUG_TRACE_SWITCH = DEBUG || TRACE_SWITCH; + + /** + * If <code>true</code> (default), bootstrapping the available GL profiles + * will use the highest compatible GL context for each profile, + * hence skipping querying lower profiles if a compatible higher one is found: + * <ul> + * <li>4.2-core -> 4.2-core, 3.3-core</li> + * <li>4.2-comp -> 4.2-comp, 3.3-comp, 2</li> + * </ul> + * Otherwise the dedicated GL context would be queried and used: + * <ul> + * <li>4.2-core -> 4.2-core</li> + * <li>3.3-core -> 3.3-core</li> + * <li>4.2-comp -> 4.2-comp</li> + * <li>3.3-comp -> 3.3-comp</li> + * <li>3.0-comp -> 2</li> + * </ul> + * Using aliasing speeds up initialization about: + * <ul> + * <li>Linux x86_64 - Nvidia: 28%, 700ms down to 500ms</li> + * <li>Linux x86_64 - AMD : 40%, 1500ms down to 900ms</li> + * <p> + * Can be turned off with property <code>jogl.debug.GLContext.NoProfileAliasing</code>. + * </p> + */ + public static final boolean PROFILE_ALIASING = !Debug.isPropertyDefined("jogl.debug.GLContext.NoProfileAliasing", true); + + 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); + /** Reflects property jogl.debug.DebugGL. If true, the debug pipeline is enabled at context creation. */ - public final static boolean DEBUG_GL = Debug.isPropertyDefined("jogl.debug.DebugGL", true); + public static final boolean DEBUG_GL = Debug.isPropertyDefined("jogl.debug.DebugGL", true); /** Reflects property jogl.debug.TraceGL. If true, the trace pipeline is enabled at context creation. */ - public final static boolean TRACE_GL = Debug.isPropertyDefined("jogl.debug.TraceGL", true); - - /** Indicates that the context was not made current during the last call to {@link #makeCurrent makeCurrent}. */ + public static final boolean TRACE_GL = Debug.isPropertyDefined("jogl.debug.TraceGL", true); + + /** Indicates that the context was not made current during the last call to {@link #makeCurrent makeCurrent}, value {@value}. */ public static final int CONTEXT_NOT_CURRENT = 0; - /** Indicates that the context was made current during the last call to {@link #makeCurrent makeCurrent}. */ + /** Indicates that the context was made current during the last call to {@link #makeCurrent makeCurrent}, value {@value}. */ public static final int CONTEXT_CURRENT = 1; - /** Indicates that a newly-created context was made current during the last call to {@link #makeCurrent makeCurrent}. */ + /** Indicates that a newly-created context was made current during the last call to {@link #makeCurrent makeCurrent}, value {@value}. */ public static final int CONTEXT_CURRENT_NEW = 2; - /** <code>ARB_create_context</code> related: created via ARB_create_context. Cache key value. */ + /** Version 1.00, i.e. GLSL 1.00 for ES 2.0. */ + public static final VersionNumber Version100 = new VersionNumber(1, 0, 0); + /** Version 1.10, i.e. GLSL 1.10 for GL 2.0. */ + public static final VersionNumber Version110 = new VersionNumber(1, 10, 0); + /** Version 1.20, i.e. GLSL 1.20 for GL 2.1. */ + public static final VersionNumber Version120 = new VersionNumber(1, 20, 0); + /** Version 1.30, i.e. GLSL 1.30 for GL 3.0. */ + public static final VersionNumber Version130 = new VersionNumber(1, 30, 0); + /** Version 1.40, i.e. GLSL 1.40 for GL 3.1. */ + public static final VersionNumber Version140 = new VersionNumber(1, 40, 0); + /** Version 1.50, i.e. GLSL 1.50 for GL 3.2. */ + public static final VersionNumber Version150 = new VersionNumber(1, 50, 0); + + /** Version 3.0. As an OpenGL version, it qualifies for desktop {@link #isGL2()} only, or ES 3.0. Or GLSL 3.00 for ES 3.0. */ + public static final VersionNumber Version300 = new VersionNumber(3, 0, 0); + + /** Version 3.1. As an OpenGL version, it qualifies for {@link #isGL3core()}, {@link #isGL3bc()} and {@link #isGL3()} */ + public static final VersionNumber Version310 = new VersionNumber(3, 1, 0); + + /** Version 3.2. As an OpenGL version, it qualifies for geometry shader */ + public static final VersionNumber Version320 = new VersionNumber(3, 2, 0); + + /** Version 4.3. As an OpenGL version, it qualifies for <code>GL_ARB_ES3_compatibility</code> */ + public static final VersionNumber Version430 = new VersionNumber(4, 3, 0); + + protected static final VersionNumber Version800 = new VersionNumber(8, 0, 0); + + // + // Cached keys, bits [0..15] + // + + /** Context option bits, full bit mask covering 16 bits [0..15], i.e. <code>0x0000FFFF</code>, {@value}. */ + protected static final int CTX_IMPL_FULL_MASK = 0x0000FFFF; + + /** Context option bits, cached bit mask covering 10 bits [0..9], i.e. <code>0x000003FF</code>, {@value}. Leaving 6 bits for non cached options, i.e. 10:6. */ + protected static final int CTX_IMPL_CACHE_MASK = 0x000003FF; + + /** <code>ARB_create_context</code> related: created via ARB_create_context. Cache key value. See {@link #getAvailableContextProperties(AbstractGraphicsDevice, GLProfile)}. */ protected static final int CTX_IS_ARB_CREATED = 1 << 0; - /** <code>ARB_create_context</code> related: compatibility profile. Cache key value. */ + /** <code>ARB_create_context</code> related: desktop compatibility profile. Cache key value. See {@link #isGLCompatibilityProfile()}, {@link #getAvailableContextProperties(AbstractGraphicsDevice, GLProfile)}. */ protected static final int CTX_PROFILE_COMPAT = 1 << 1; - /** <code>ARB_create_context</code> related: core profile. Cache key value. */ + /** <code>ARB_create_context</code> related: desktop core profile. Cache key value. See {@link #isGLCoreProfile()}, {@link #getAvailableContextProperties(AbstractGraphicsDevice, GLProfile)}. */ protected static final int CTX_PROFILE_CORE = 1 << 2; - /** <code>ARB_create_context</code> related: ES profile. Cache key value. */ + /** <code>ARB_create_context</code> related: ES profile. Cache key value. See {@link #isGLES()}, {@link #getAvailableContextProperties(AbstractGraphicsDevice, GLProfile)}. */ protected static final int CTX_PROFILE_ES = 1 << 3; - /** <code>ARB_create_context</code> related: flag forward compatible. Cache key value. */ + /** <code>ARB_create_context</code> related: flag forward compatible. Cache key value. See {@link #getAvailableContextProperties(AbstractGraphicsDevice, GLProfile)}. */ protected static final int CTX_OPTION_FORWARD = 1 << 4; - /** <code>ARB_create_context</code> related: flag debug. Not a cache key. */ + /** <code>ARB_create_context</code> related: flag debug. Cache key value. See {@link #setContextCreationFlags(int)}, {@link GLAutoDrawable#setContextCreationFlags(int)}, {@link #getAvailableContextProperties(AbstractGraphicsDevice, GLProfile)}. */ public static final int CTX_OPTION_DEBUG = 1 << 5; - - /** <code>GL_ARB_ES2_compatibility</code> implementation related: Context is compatible w/ ES2. Not a cache key. */ - protected static final int CTX_IMPL_ES2_COMPAT = 1 << 8; + /** Context uses software rasterizer, otherwise hardware rasterizer. Cache key value. See {@link #isHardwareRasterizer()}, {@link #getAvailableContextProperties(AbstractGraphicsDevice, GLProfile)}. */ + protected static final int CTX_IMPL_ACCEL_SOFT = 1 << 6; + + // + // Non cached keys, 6 bits [10..15] + // - /** Context uses software rasterizer, otherwise hardware rasterizer. Cache key value. */ - protected static final int CTX_IMPL_ACCEL_SOFT = 1 << 15; - - private static ThreadLocal<GLContext> currentContext = new ThreadLocal<GLContext>(); + /** <code>GL_ARB_ES2_compatibility</code> implementation related: Context is compatible w/ ES2. Not a cache key. See {@link #isGLES2Compatible()}, {@link #getAvailableContextProperties(AbstractGraphicsDevice, GLProfile)}. */ + protected static final int CTX_IMPL_ES2_COMPAT = 1 << 10; - private HashMap<String, Object> attachedObjectsByString = new HashMap<String, Object>(); - private IntObjectHashMap attachedObjectsByInt = new IntObjectHashMap(); + /** <code>GL_ARB_ES3_compatibility</code> implementation related: Context is compatible w/ ES3. Not a cache key. See {@link #isGLES3Compatible()}, {@link #getAvailableContextProperties(AbstractGraphicsDevice, GLProfile)}. */ + protected static final int CTX_IMPL_ES3_COMPAT = 1 << 11; - // RecursiveLock maintains a queue of waiting Threads, ensuring the longest waiting thread will be notified at unlock. - protected RecursiveLock lock = LockFactory.createRecursiveLock(); + /** + * Context supports basic FBO, details see {@link #hasBasicFBOSupport()}. + * Not a cache key. + * @see #hasBasicFBOSupport() + * @see #getAvailableContextProperties(AbstractGraphicsDevice, GLProfile) + */ + protected static final int CTX_IMPL_FBO = 1 << 12; + + /** + * Context supports <code>OES_single_precision</code>, fp32, fixed function point (FFP) compatibility entry points, + * see {@link #hasFP32CompatAPI()}. + * Not a cache key. + * @see #hasFP32CompatAPI() + * @see #getAvailableContextProperties(AbstractGraphicsDevice, GLProfile) + */ + protected static final int CTX_IMPL_FP32_COMPAT_API = 1 << 13; + + private static final ThreadLocal<GLContext> currentContext = new ThreadLocal<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(); /** The underlying native OpenGL context */ - protected long contextHandle; + protected volatile long contextHandle; // volatile: avoid locking for read-only access protected GLContext() { - resetStates(); + resetStates(true); } - - protected int ctxMajorVersion; - protected int ctxMinorVersion; + + protected VersionNumber ctxVersion; protected int ctxOptions; protected String ctxVersionString; + protected VersionNumberString ctxVendorVersion; + protected VersionNumber ctxGLSLVersion; private int currentSwapInterval; + protected GLRendererQuirks glRendererQuirks; + + /** Did the drawable association changed ? see {@link GLRendererQuirks#NoSetSwapIntervalPostRetarget} */ + protected boolean drawableRetargeted; - protected void resetStates() { + /** + * @param isInit true if called for class initialization, otherwise false (re-init or destruction). + */ + protected void resetStates(boolean isInit) { if (DEBUG) { - System.err.println(getThreadName() + ": GLContext.resetStates()"); + System.err.println(getThreadName() + ": GLContext.resetStates(isInit "+isInit+")"); // Thread.dumpStack(); } - ctxMajorVersion=-1; - ctxMinorVersion=-1; + ctxVersion = VersionNumberString.zeroVersion; + ctxVendorVersion = VersionNumberString.zeroVersion; ctxOptions=0; ctxVersionString=null; - attachedObjectsByString.clear(); - attachedObjectsByInt.clear(); + ctxGLSLVersion = VersionNumber.zeroVersion; + attachedObjects.clear(); contextHandle=0; currentSwapInterval = -1; + glRendererQuirks = null; + drawableRetargeted = false; + } + + /** Returns true if this GLContext is shared, otherwise false. */ + public final boolean isShared() { + return GLContextShareSet.isShared(this); + } + + /** Returns a new list of created GLContext shared with this GLContext. */ + public final List<GLContext> getCreatedShares() { + return GLContextShareSet.getCreatedShares(this); + } + + /** Returns a new list of destroyed GLContext shared with this GLContext. */ + public final List<GLContext> getDestroyedShares() { + return GLContextShareSet.getDestroyedShares(this); } /** - * Returns the GLDrawable to which this context may be used to - * draw. + * Returns the instance of {@link GLRendererQuirks}, allowing one to determine workarounds. + * @return instance of {@link GLRendererQuirks} if context was made current once, otherwise <code>null</code>. + */ + public final GLRendererQuirks getRendererQuirks() { return glRendererQuirks; } + + /** + * Returns true if the <code>quirk</code> exist in {@link #getRendererQuirks()}, otherwise false. + * <p> + * Convenience method for: + * <pre> + * final GLRendererQuirks glrq = ctx.getRendererQuirks(); + * boolean hasQuirk = null != glrq ? glrq.exist(quirk) : false ; + * </pre> + * </p> + * @param quirk the quirk to be tested, e.g. {@link GLRendererQuirks#NoDoubleBufferedPBuffer}. + * @throws IllegalArgumentException if the quirk is out of range + */ + public final boolean hasRendererQuirk(int quirk) throws IllegalArgumentException { + return null != glRendererQuirks ? glRendererQuirks.exist(quirk) : false ; + } + + /** + * Sets the read/write drawable for framebuffer operations. + * <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. + * </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 + * if the {@link #getGLReadDrawable() read-drawable} differs + * from the {@link #getGLDrawable() write-drawable}. + * Otherwise set both drawables, read and write. + * @return The previous read/write drawable + * + * @throws GLException in case <code>null</code> is being passed or + * this context is made current on another thread. + * + * @see #isGLReadDrawableAvailable() + * @see #setGLReadDrawable(GLDrawable) + * @see #getGLReadDrawable() + * @see #setGLDrawable(GLDrawable, boolean) + * @see #getGLDrawable() + */ + public abstract GLDrawable setGLDrawable(GLDrawable readWrite, boolean setWriteOnly); + + /** + * Returns the write-drawable this context uses for framebuffer operations. + * <p> + * If the read-drawable has not been changed manually via {@link #setGLReadDrawable(GLDrawable)}, + * it equals to the write-drawable (default). + * </p> + * @see #setGLDrawable(GLDrawable, boolean) + * @see #setGLReadDrawable(GLDrawable) */ public abstract GLDrawable getGLDrawable(); /** - * Return availability of GL read drawable. - * @return true if a GL read drawable is supported with your driver, otherwise false. + * Query whether using a distinguished read-drawable is supported. + * @return true if using a read-drawable is supported with your driver/OS, otherwise false. */ public abstract boolean isGLReadDrawableAvailable(); /** - * Set the read GLDrawable for read framebuffer operations.<br> + * Set the read-Drawable for read framebuffer operations.<br> * The caller should query if this feature is supported via {@link #isGLReadDrawableAvailable()}. + * <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. + * </p> * - * @param read the read GLDrawable for read framebuffer operations. - * If null is passed, the default write drawable will be set. + * @param read the read-drawable for read framebuffer operations. + * If null is passed, the default write drawable will be set. + * @return the previous read-drawable * - * @throws GLException in case a read drawable is not supported - * and the given drawable is not null and not equal to the internal write drawable. + * @throws GLException in case a read drawable is not supported or + * this context is made current on another thread. * * @see #isGLReadDrawableAvailable() * @see #getGLReadDrawable() */ - public abstract void setGLReadDrawable(GLDrawable read); + public abstract GLDrawable setGLReadDrawable(GLDrawable read); /** - * Returns the read GLDrawable this context uses for read framebuffer operations. + * Returns the read-Drawable this context uses for read framebuffer operations. + * <p> + * If the read-drawable has not been changed manually via {@link #setGLReadDrawable(GLDrawable)}, + * it equals to the write-drawable (default). + * </p> * @see #isGLReadDrawableAvailable() - * @see #setGLReadDrawable(javax.media.opengl.GLDrawable) + * @see #setGLReadDrawable(GLDrawable) + * @see #getGLReadDrawable() */ public abstract GLDrawable getGLReadDrawable(); /** * Makes this GLContext current on the calling thread. * <p> + * Recursive call to {@link #makeCurrent()} and hence {@link #release()} are supported. + * </p> + * <p> * There are two return values that indicate success and one that - * indicates failure. A return value of CONTEXT_CURRENT_NEW - * indicates that that context has been made current, and that - * this is the first time this context has been made current, or - * that the state of the underlying context or drawable may have - * changed since the last time this context was made current. In - * this case, the application may wish to initialize the state. A - * return value of CONTEXT_CURRENT indicates that the context has - * been made currrent, with its previous state restored. + * indicates failure. + * </p> + * <p> + * A return value of {@link #CONTEXT_CURRENT_NEW} + * indicates that that context has been made current for the 1st time, + * or that the state of the underlying context or drawable has + * changed since the last time this context was current. + * In this case, the application may wish to initialize the render state. + * </p> + * <p> + * A return value of {@link #CONTEXT_CURRENT} indicates that the context has + * been made current, with its previous state restored. * </p> * <p> * If the context could not be made current (for example, because * the underlying drawable has not ben realized on the display) , - * a value of CONTEXT_NOT_CURRENT is returned. + * a value of {@link #CONTEXT_NOT_CURRENT} is returned. * </p> - * <p> - * If the context is in use by another thread at the time of the - * call, then if isSynchronized() is true the call will - * block. If isSynchronized() is false, an exception will be - * thrown and the context will remain current on the other thread. + * <p> + * This method is blocking, i.e. waits until another thread has + * released the context. * </p> * <p> * The drawable's surface is being locked at entry - * and unlocked at {@link #release()} + * and unlocked at {@link #release()} * </p> * - * @return CONTEXT_CURRENT if the context was successfully made current - * @return CONTEXT_CURRENT_NEW if the context was successfully made - * current, but need to be initialized. - * - * @return CONTEXT_NOT_CURRENT if the context could not be made current. + * @return <ul> + * <li>{@link #CONTEXT_CURRENT_NEW} if the context was successfully made current the 1st time,</li> + * <li>{@link #CONTEXT_CURRENT} if the context was successfully made current,</li> + * <li>{@link #CONTEXT_NOT_CURRENT} if the context could not be made current.</li> + * </ul> * * @throws GLException if synchronization is disabled and the * context is current on another thread, or because the context @@ -215,8 +410,11 @@ public abstract class GLContext { /** * Releases control of this GLContext from the current thread. * <p> + * Recursive call to {@link #release()} and hence {@link #makeCurrent()} are supported. + * </p> + * <p> * The drawable's surface is being unlocked at exit, - * assumed to be locked by {@link #makeCurrent()}. + * assumed to be locked by {@link #makeCurrent()}. * </p> * * @throws GLException if the context had not previously been made @@ -230,7 +428,7 @@ public abstract class GLContext { * parameter indicates which groups of state variables are to be * copied. <code>mask</code> contains the bitwise OR of the same * symbolic names that are passed to the GL command {@link - * GL#glPushAttrib glPushAttrib}. The single symbolic constant + * GL2#glPushAttrib glPushAttrib}. The single symbolic constant * {@link GL2#GL_ALL_ATTRIB_BITS GL_ALL_ATTRIB_BITS} can be used to * copy the maximum possible portion of rendering state. <P> * @@ -284,41 +482,66 @@ public abstract class GLContext { * @return true if this GLContext is current on this thread */ public final boolean isCurrent() { - return getCurrent() == this ; + return getCurrent() == this ; } /** * @throws GLException if this GLContext is not current on this thread */ - public final void validateCurrent() throws GLException { + public final void validateCurrent() throws GLException { if(getCurrent() != this) { throw new GLException(getThreadName()+": This context is not current. Current context: "+getCurrent()+", this context "+this); } } - + + /** Returns a String representation of the {@link #makeCurrent()} result. */ + public static final String makeCurrentResultToString(int res) { + switch(res) { + case CONTEXT_NOT_CURRENT: return "CONTEXT_NOT_CURRENT"; + case CONTEXT_CURRENT: return "CONTEXT_CURRENT"; + case CONTEXT_CURRENT_NEW: return "CONTEXT_NOT_CURRENT"; + default: return "INVALID_VALUE"; + } + } + /** * Sets the thread-local variable returned by {@link #getCurrent} * and has no other side-effects. For use by third parties adding * new GLContext implementations; not for use by end users. */ protected static void setCurrent(GLContext cur) { - if(TRACE_SWITCH) { - System.err.println(getThreadName()+": GLContext.ContextSwitch: - setCurrent() - "+cur); + if( TRACE_SWITCH ) { + if(null == cur) { + System.err.println(getThreadName()+": GLContext.ContextSwitch: - setCurrent() - NULL"); + } else { + System.err.println(getThreadName()+": GLContext.ContextSwitch: - setCurrent() - obj " + toHexString(cur.hashCode()) + ", ctx " + toHexString(cur.getHandle())); + } } currentContext.set(cur); } - + /** * Destroys this OpenGL context and frees its associated * resources. * <p> * The context may be current w/o recursion when calling <code>destroy()</code>, * in which case this method destroys the context and releases the lock. - * </p> + * </p> */ public abstract void destroy(); /** + * Returns the implementing root GL instance of this GLContext's GL object, + * considering a wrapped pipelined hierarchy, see {@link GLBase#getDownstreamGL()}. + * @throws GLException if the root instance is not a GL implementation + * @see GLBase#getRootGL() + * @see GLBase#getDownstreamGL() + * @see #getGL() + * @see #setGL(GL) + */ + public abstract GL getRootGL(); + + /** * Returns the GL pipeline object for this GLContext. * * @return the aggregated GL instance, or null if this context was not yet made current. @@ -333,12 +556,12 @@ public abstract class GLContext { public abstract GL setGL(GL gl); /** - * Returns the native GL context handle + * Returns the underlying native OpenGL context handle */ public final long getHandle() { return contextHandle; } - /** - * Indicates whether the underlying OpenGL context has been created. + /** + * Indicates whether the underlying native OpenGL context has been created. */ public final boolean isCreated() { return 0 != contextHandle; @@ -347,44 +570,26 @@ public abstract class GLContext { /** * Returns the attached user object for the given name to this GLContext. */ - public final Object getAttachedObject(int name) { - return attachedObjectsByInt.get(name); - } - - /** - * Returns the attached user object for the given name to this GLContext. - */ public final Object getAttachedObject(String name) { - return attachedObjectsByString.get(name); - } - - /** - * Sets the attached user object for the given name to this GLContext. - * Returns the previously set object or null. - */ - public final Object attachObject(int name, Object obj) { - return attachedObjectsByInt.put(name, obj); + return attachedObjects.get(name); } - public final Object detachObject(int name) { - return attachedObjectsByInt.remove(name); - } - /** * Sets the attached user object for the given name to this GLContext. * Returns the previously set object or null. */ public final Object attachObject(String name, Object obj) { - return attachedObjectsByString.put(name, obj); + return attachedObjects.put(name, obj); } public final Object detachObject(String name) { - return attachedObjectsByString.remove(name); + return attachedObjects.remove(name); } - + /** * Classname, GL, GLDrawable */ + @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); @@ -395,18 +600,21 @@ public abstract class GLContext { } public final StringBuilder append(StringBuilder sb) { - sb.append("OpenGL "); - sb.append(getGLVersionMajor()); - sb.append("."); - sb.append(getGLVersionMinor()); - sb.append(", options 0x"); + sb.append("Version ").append(getGLVersion()).append(" [GL ").append(getGLVersionNumber()).append(", vendor ").append(getGLVendorVersionNumber()); + sb.append("], options 0x"); sb.append(Integer.toHexString(ctxOptions)); - sb.append(", "); - sb.append(getGLVersion()); + sb.append(", this "); + sb.append(toHexString(hashCode())); sb.append(", handle "); sb.append(toHexString(contextHandle)); - sb.append(", "); + sb.append(", isShared "+isShared()+", "); sb.append(getGL()); + sb.append(",\n\t quirks: "); + if(null != glRendererQuirks) { + glRendererQuirks.toString(sb); + } else { + sb.append("n/a"); + } if(getGLDrawable()!=getGLReadDrawable()) { sb.append(",\n\tRead Drawable : "); sb.append(getGLReadDrawable()); @@ -416,8 +624,6 @@ public abstract class GLContext { sb.append(",\n\tDrawable: "); sb.append(getGLDrawable()); } - sb.append(", lock "); - sb.append(lock.toString()); return sb; } @@ -433,7 +639,7 @@ public abstract class GLContext { * javax.media.opengl.GL#glPolygonOffset(float,float)} is available). */ public abstract boolean isFunctionAvailable(String glFunctionName); - + /** * Returns true if the specified OpenGL extension can be * successfully called using this GL context given the current host (OpenGL @@ -445,10 +651,10 @@ public abstract class GLContext { * "GL_VERTEX_PROGRAM_ARB"). */ public abstract boolean isExtensionAvailable(String glExtensionName); - + /** Returns the number of platform extensions */ public abstract int getPlatformExtensionCount(); - + /** Returns a non-null (but possibly empty) string containing the space-separated list of available platform-dependent (e.g., WGL, GLX) extensions. Can only be called while this context is @@ -457,7 +663,7 @@ public abstract class GLContext { /** Returns the number of OpenGL extensions */ public abstract int getGLExtensionCount(); - + /** Returns a non-null (but possibly empty) string containing the space-separated list of available extensions. Can only be called while this context is current. @@ -479,8 +685,8 @@ public abstract class GLContext { * @see GLAutoDrawable#setContextCreationFlags(int) */ public abstract void setContextCreationFlags(int flags); - - /** + + /** * Returns a valid OpenGL version string, ie<br> * <pre> * major.minor ([option]?[options,]*) - gl-version @@ -509,7 +715,7 @@ public abstract class GLContext { * <td>row 2, cell 1</td> * <td>row 2, cell 2</td> * </tr> - * </table> + * </table> * * <table border="0"> * <tr><td></td> <td>ES2</td> <td><code>2.0 (ES profile, ES2 compatible, hardware) - 2.0 ES Profile</code></td></tr> @@ -520,131 +726,495 @@ public abstract class GLContext { * <tr><td>NV</td><td>GL3</td> <td><code>3.3 (Core profile, arb, hardware) - 3.3.0 NVIDIA 195.36.07.03</code></td></tr> * <tr><td>NV</td><td>GL3bc</td> <td><code>3.3 (Compatibility profile, arb, hardware) - 3.3.0 NVIDIA 195.36.07.03</code></td></tr> * <tr><td>NV</td><td>GL2</td> <td><code>3.0 (Compatibility profile, arb, ES2 compatible, hardware) - 3.0.0 NVIDIA 290.10</code></td></tr> - * </table> + * </table> */ public final String getGLVersion() { return ctxVersionString; } - public final int getGLVersionMajor() { return ctxMajorVersion; } - public final int getGLVersionMinor() { return ctxMinorVersion; } + /** + * Returns this context OpenGL version. + * @see #getGLSLVersionNumber() + **/ + public final VersionNumber getGLVersionNumber() { return ctxVersion; } + /** + * Returns the vendor's version, i.e. version number at the end of <code>GL_VERSION</code> not being the GL version. + * <p> + * In case no such version exists within <code>GL_VERSION</code>, + * the {@link VersionNumberString#zeroVersion zero version} instance is returned. + * </p> + * <p> + * The vendor's version is usually the vendor's OpenGL driver version. + * </p> + */ + public final VersionNumberString getGLVendorVersionNumber() { return ctxVendorVersion; } public final boolean isGLCompatibilityProfile() { return ( 0 != ( CTX_PROFILE_COMPAT & ctxOptions ) ); } public final boolean isGLCoreProfile() { return ( 0 != ( CTX_PROFILE_CORE & ctxOptions ) ); } + public final boolean isGLESProfile() { return ( 0 != ( CTX_PROFILE_ES & ctxOptions ) ); } public final boolean isGLForwardCompatible() { return ( 0 != ( CTX_OPTION_FORWARD & ctxOptions ) ); } public final boolean isGLDebugEnabled() { return ( 0 != ( CTX_OPTION_DEBUG & ctxOptions ) ); } public final boolean isCreatedWithARBMethod() { return ( 0 != ( CTX_IS_ARB_CREATED & ctxOptions ) ); } - + /** - * @return true if this context is an ES2 context or implements - * the extension <code>GL_ARB_ES2_compatibility</code>, otherwise false + * Returns the matching GLSL version number, queried by this context GL + * via {@link GL2ES2#GL_SHADING_LANGUAGE_VERSION} if ≥ ES2.0 or GL2.0, + * otherwise a static match is being utilized. + * <p> + * The context must have been current once, + * otherwise the {@link VersionNumberString#zeroVersion zero version} instance is returned. + * </p> + * <p> + * Examples w/ <code>major.minor</code>: + * <pre> + * 1.00 (ES 2.0), 3.00 (ES 3.0) + * 1.10 (GL 2.0), 1.20 (GL 2.1), 1.50 (GL 3.2), + * 3.30 (GL 3.3), 4.00 (GL 4.0), 4.10 (GL 4.1), 4.20 (GL 4.2) + * </pre > + * </p> + * <p> + * <i>Matching</i> could also refer to the maximum GLSL version usable by this context + * since <i>normal</i> GL implementations are capable of using a lower GLSL version as well. + * The latter is not true on OSX w/ a GL3 context. + * </p> + * + * @return GLSL version number if context has been made current at least once, + * otherwise the {@link VersionNumberString#zeroVersion zero version} instance is returned. + * + * @see #getGLVersionNumber() + */ + public final VersionNumber getGLSLVersionNumber() { + return ctxGLSLVersion; + } + + /** + * Returns the GLSL version string as to be used in a shader program, including a terminating newline '\n', + * i.e. for desktop + * <pre> + * #version 110 + * .. + * #version 150 + * #version 330 + * ... + * </pre> + * And for ES: + * <pre> + * #version 100 + * #version 300 es + * .. + * </pre> + * <p> + * If context has not been made current yet, a string of zero length is returned. + * </p> + * @see #getGLSLVersionNumber() + */ + public final String getGLSLVersionString() { + if( ctxGLSLVersion.isZero() ) { + return ""; + } + 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" ; + } + + protected static final VersionNumber getStaticGLSLVersionNumber(int glMajorVersion, int glMinorVersion, int ctxOptions) { + if( 0 != ( CTX_PROFILE_ES & ctxOptions ) ) { + if( 3 == glMajorVersion ) { + return Version300; // ES 3.0 -> GLSL 3.00 + } else if( 2 == glMajorVersion ) { + return Version100; // ES 2.0 -> GLSL 1.00 + } + } else if( 1 == glMajorVersion ) { + return Version110; // GL 1.x -> GLSL 1.10 + } else if( 2 == glMajorVersion ) { + switch ( glMinorVersion ) { + case 0: return Version110; // GL 2.0 -> GLSL 1.10 + default: return Version120; // GL 2.1 -> GLSL 1.20 + } + } else if( 3 == glMajorVersion && 2 >= glMinorVersion ) { + switch ( glMinorVersion ) { + case 0: return Version130; // GL 3.0 -> GLSL 1.30 + case 1: return Version140; // GL 3.1 -> GLSL 1.40 + default: return Version150; // GL 3.2 -> GLSL 1.50 + } + } + // The new default: GL >= 3.3, ES >= 3.0 + return new VersionNumber(glMajorVersion, glMinorVersion * 10, 0); // GL M.N -> GLSL M.N + } + + /** + * @return true if this context is an ES2 context or implements + * the extension <code>GL_ARB_ES3_compatibility</code> or <code>GL_ARB_ES2_compatibility</code>, otherwise false */ public final boolean isGLES2Compatible() { - return 0 != ( ctxOptions & CTX_IMPL_ES2_COMPAT ) ; + return 0 != ( ctxOptions & ( CTX_IMPL_ES3_COMPAT | CTX_IMPL_ES2_COMPAT ) ) ; } - + + /** + * Return true if this context is an ES3 context or implements + * the extension <code>GL_ARB_ES3_compatibility</code>, otherwise false. + * <p> + * Includes [ GL ≥ 4.3, GL ≥ 3.1 w/ GL_ARB_ES3_compatibility and GLES3 ] + * </p> + */ + public final boolean isGLES3Compatible() { + return 0 != ( ctxOptions & CTX_IMPL_ES3_COMPAT ) ; + } + + /** + * @return true if impl. is a hardware rasterizer, otherwise false. + * @see #isHardwareRasterizer(AbstractGraphicsDevice, GLProfile) + * @see GLProfile#isHardwareRasterizer() + */ + public final boolean isHardwareRasterizer() { + return 0 == ( ctxOptions & CTX_IMPL_ACCEL_SOFT ) ; + } + + /** + * @return true if context supports GLSL, i.e. is either {@link #isGLES3()}, {@link #isGLES2()}, {@link #isGL3()} or {@link #isGL2()} <i>and</i> major-version > 1. + * @see GLProfile#hasGLSL() + */ public final boolean hasGLSL() { - return isGL2ES2() ; + return isGLES3() || + isGLES2() || + isGL3() || + isGL2() && ctxVersion.getMajor()>1 ; + } + + /** + * Returns <code>true</code> if basic FBO support is available, otherwise <code>false</code>. + * <p> + * Basic FBO is supported if the context is either GL-ES >= 2.0, GL >= 3.0 [core, compat] or implements the extensions + * <code>GL_ARB_ES2_compatibility</code>, <code>GL_ARB_framebuffer_object</code>, <code>GL_EXT_framebuffer_object</code> or <code>GL_OES_framebuffer_object</code>. + * </p> + * <p> + * Basic FBO support may only include one color attachment and no multisampling, + * as well as limited internal formats for renderbuffer. + * </p> + * @see #CTX_IMPL_FBO + */ + public final boolean hasBasicFBOSupport() { + return 0 != ( ctxOptions & CTX_IMPL_FBO ) ; + } + + /** + * Returns <code>true</code> if full FBO support is available, otherwise <code>false</code>. + * <p> + * Full FBO is supported if the context is either GL >= 3.0 [ES, core, compat] or implements the extensions + * <code>ARB_framebuffer_object</code>, or all of + * <code>EXT_framebuffer_object</code>, <code>EXT_framebuffer_multisample</code>, + * <code>EXT_framebuffer_blit</code>, <code>GL_EXT_packed_depth_stencil</code>. + * </p> + * <p> + * Full FBO support includes multiple color attachments and multisampling. + * </p> + */ + public final boolean hasFullFBOSupport() { + return hasBasicFBOSupport() && !hasRendererQuirk(GLRendererQuirks.NoFullFBOSupport) && + ( isGL3ES3() || // GL >= 3.0 [ES, core, compat] + isExtensionAvailable(GLExtensions.ARB_framebuffer_object) || // ARB_framebuffer_object + ( isExtensionAvailable(GLExtensions.EXT_framebuffer_object) && // All EXT_framebuffer_object* + isExtensionAvailable(GLExtensions.EXT_framebuffer_multisample) && + isExtensionAvailable(GLExtensions.EXT_framebuffer_blit) && + isExtensionAvailable(GLExtensions.EXT_packed_depth_stencil) + ) + ) ; } - + + /** + * Returns <code>true</code> if <code>OES_single_precision</code>, fp32, fixed function point (FFP) compatibility entry points available, + * otherwise <code>false</code>. + * @see #CTX_IMPL_FP32_COMPAT_API + */ + public final boolean hasFP32CompatAPI() { + return 0 != ( ctxOptions & CTX_IMPL_FP32_COMPAT_API ) ; + } + + /** + * Returns the maximum number of FBO RENDERBUFFER samples + * if {@link #hasFullFBOSupport() full FBO is supported}, otherwise false. + */ + public final int getMaxRenderbufferSamples() { + if( hasFullFBOSupport() ) { + final GL gl = getGL(); + final int[] val = new int[] { 0 } ; + try { + gl.glGetIntegerv(GL2GL3.GL_MAX_SAMPLES, val, 0); + final int glerr = gl.glGetError(); + if(GL.GL_NO_ERROR == glerr) { + return val[0]; + } else if(DEBUG) { + System.err.println("GLContext.getMaxRenderbufferSamples: GL_MAX_SAMPLES query GL Error 0x"+Integer.toHexString(glerr)); + } + } catch (GLException gle) { gle.printStackTrace(); } + } + return 0; + } + /** Note: The GL impl. may return a const value, ie {@link GLES2#isNPOTTextureAvailable()} always returns <code>true</code>. */ public boolean isNPOTTextureAvailable() { - return isGL3() || isGLES2Compatible() || isExtensionAvailable(GL_ARB_texture_non_power_of_two); + return isGL3() || isGLES2Compatible() || isExtensionAvailable(GLExtensions.ARB_texture_non_power_of_two); } - private static final String GL_ARB_texture_non_power_of_two = "GL_ARB_texture_non_power_of_two"; - + public boolean isTextureFormatBGRA8888Available() { - return isGL2GL3() || - isExtensionAvailable("GL_EXT_texture_format_BGRA8888") || - isExtensionAvailable("GL_IMG_texture_format_BGRA8888") ; + return isGL2GL3() || + isExtensionAvailable(GLExtensions.EXT_texture_format_BGRA8888) || + isExtensionAvailable(GLExtensions.IMG_texture_format_BGRA8888) ; } + /** + * Indicates whether this GLContext is capable of GL4bc. <p>Includes [ GL4bc ].</p> + * @see GLProfile#isGL4bc() + */ public final boolean isGL4bc() { - return ctxMajorVersion>=4 && 0 != (ctxOptions & CTX_IS_ARB_CREATED) - && 0 != (ctxOptions & CTX_PROFILE_COMPAT); + return 0 != (ctxOptions & CTX_IS_ARB_CREATED) && + 0 != (ctxOptions & CTX_PROFILE_COMPAT) && + ctxVersion.getMajor() >= 4; } + /** + * Indicates whether this GLContext is capable of GL4. <p>Includes [ GL4bc, GL4 ].</p> + * @see GLProfile#isGL4() + */ public final boolean isGL4() { - return ctxMajorVersion>=4 && 0 != (ctxOptions & CTX_IS_ARB_CREATED) - && 0 != (ctxOptions & (CTX_PROFILE_COMPAT|CTX_PROFILE_CORE)); + return 0 != (ctxOptions & CTX_IS_ARB_CREATED) && + 0 != (ctxOptions & (CTX_PROFILE_COMPAT|CTX_PROFILE_CORE)) && + ctxVersion.getMajor() >= 4; } + /** + * 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 ) && + ctxVersion.getMajor() >= 4; + } + + /** + * Indicates whether this GLContext is capable of GL3bc. <p>Includes [ GL4bc, GL3bc ].</p> + * @see GLProfile#isGL3bc() + */ public final boolean isGL3bc() { - return ( ctxMajorVersion>3 || ctxMajorVersion==3 && ctxMinorVersion>=1 ) - && 0 != (ctxOptions & CTX_IS_ARB_CREATED) - && 0 != (ctxOptions & CTX_PROFILE_COMPAT); + return 0 != (ctxOptions & CTX_IS_ARB_CREATED) && + 0 != (ctxOptions & CTX_PROFILE_COMPAT) && + ctxVersion.compareTo(Version310) >= 0 ; } + /** + * Indicates whether this GLContext is capable of GL3. <p>Includes [ GL4bc, GL4, GL3bc, GL3 ].</p> + * @see GLProfile#isGL3() + */ public final boolean isGL3() { - return ( ctxMajorVersion>3 || ctxMajorVersion==3 && ctxMinorVersion>=1 ) - && 0 != (ctxOptions & CTX_IS_ARB_CREATED) - && 0 != (ctxOptions & (CTX_PROFILE_COMPAT|CTX_PROFILE_CORE)); + return 0 != (ctxOptions & CTX_IS_ARB_CREATED) && + 0 != (ctxOptions & (CTX_PROFILE_COMPAT|CTX_PROFILE_CORE)) && + ctxVersion.compareTo(Version310) >= 0 ; + } + + /** + * 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 ) && + ctxVersion.compareTo(Version310) >= 0; + } + + /** + * Indicates whether this GLContext uses a GL core profile. <p>Includes [ GL4, GL3, GLES3, GLES2 ].</p> + */ + public final boolean isGLcore() { + return ( 0 != ( ctxOptions & CTX_PROFILE_ES ) && ctxVersion.getMajor() >= 2 ) || + ( 0 != ( ctxOptions & CTX_IS_ARB_CREATED ) && + 0 != ( ctxOptions & CTX_PROFILE_CORE ) && + ctxVersion.compareTo(Version310) >= 0 + ) ; + } + + /** + * Indicates whether this GLContext allows CPU data sourcing (indices, vertices ..) as opposed to using a GPU buffer source (VBO), + * e.g. {@link GL2#glDrawElements(int, int, int, java.nio.Buffer)}. + * <p>Includes [GL2ES1, GLES2] == [ GL4bc, GL3bc, GL2, GLES1, GL2ES1, GLES2 ].</p> + * <p>See Bug 852 - https://jogamp.org/bugzilla/show_bug.cgi?id=852 </p> + */ + public final boolean isCPUDataSourcingAvail() { + return isGL2ES1() || isGLES2(); } + /** + * 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> + * <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) + there is no more default VAO buffer 0 bound, hence generating and binding one + to avoid INVALID_OPERATION at VertexAttribPointer. + 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: + GL ES 3.0 spec F.1. Legacy Features (p 322). + * </pre> + * <p> + * If no default VAO is implemented in the native OpenGL profile, + * an own default VAO is being used, see {@link #getDefaultVAO()}. + * </p> + * @see #getDefaultVAO() + */ + public final boolean hasNoDefaultVAO() { + return ( 0 != ( ctxOptions & CTX_PROFILE_ES ) && ctxVersion.getMajor() >= 3 ) || + ( 0 != ( ctxOptions & CTX_IS_ARB_CREATED ) && + 0 != ( ctxOptions & CTX_PROFILE_CORE ) && + ctxVersion.compareTo(Version310) >= 0 + ) ; + } + + /** + * If this GLContext does not implement a default VAO, see {@link #hasNoDefaultVAO()}, + * an <i>own default VAO</i> will be created and bound at context creation. + * <p> + * If this GLContext does implement a default VAO, i.e. {@link #hasNoDefaultVAO()} + * returns <code>false</code>, this method returns <code>0</code>. + * </p> + * <p> + * Otherwise this method returns the VAO object name + * representing this GLContext's <i>own default VAO</i>. + * </p> + * @see #hasNoDefaultVAO() + */ + public abstract int getDefaultVAO(); + + /** + * Indicates whether this GLContext is capable of GL2. <p>Includes [ GL4bc, GL3bc, GL2 ].</p> + * @see GLProfile#isGL2() + */ public final boolean isGL2() { - return ctxMajorVersion>=1 && 0!=(ctxOptions & CTX_PROFILE_COMPAT); + return 0 != ( ctxOptions & CTX_PROFILE_COMPAT ) && ctxVersion.getMajor()>=1 ; } + /** + * Indicates whether this GLContext is capable of GL2GL3. <p>Includes [ GL4bc, GL4, GL3bc, GL3, GL2, GL2GL3 ].</p> + * @see GLProfile#isGL2GL3() + */ public final boolean isGL2GL3() { return isGL2() || isGL3(); } + /** + * Indicates whether this GLContext is capable of GLES1. <p>Includes [ GLES1 ].</p> + * @see GLProfile#isGLES1() + */ public final boolean isGLES1() { - return ctxMajorVersion==1 && 0 != ( ctxOptions & CTX_PROFILE_ES ) ; + return 0 != ( ctxOptions & CTX_PROFILE_ES ) && ctxVersion.getMajor() == 1 ; } + /** + * Indicates whether this GLContext is capable of GLES2. <p>Includes [ GLES2, GLES3 ].</p> + * @see GLProfile#isGLES2() + */ public final boolean isGLES2() { - return ctxMajorVersion==2 && 0 != ( ctxOptions & CTX_PROFILE_ES ) ; + if( 0 != ( ctxOptions & CTX_PROFILE_ES ) ) { + final int major = ctxVersion.getMajor(); + return 2 == major || 3 == major; + } + return false; } + /** + * Indicates whether this GLContext is capable of GLES3. <p>Includes [ GLES3 ].</p> + * @see GLProfile#isGLES3() + */ + public final boolean isGLES3() { + return 0 != ( ctxOptions & CTX_PROFILE_ES ) && ctxVersion.getMajor() == 3 ; + } + + /** + * Indicates whether this GLContext is capable of GLES. <p>Includes [ GLES3, GLES1, GLES2 ].</p> + * @see GLProfile#isGLES() + */ public final boolean isGLES() { return 0 != ( CTX_PROFILE_ES & ctxOptions ) ; } + /** + * Indicates whether this GLContext is capable of GL2ES1. <p>Includes [ GL4bc, GL3bc, GL2, GLES1, GL2ES1 ].</p> + * @see GLProfile#isGL2ES1() + */ public final boolean isGL2ES1() { - return isGL2() || isGLES1() ; + return isGLES1() || isGL2(); } + /** + * Indicates whether this GLContext is capable of GL2ES2. <p>Includes [ GL4bc, GL4, GL3bc, GL3, GLES3, GL2, GL2GL3, GL2ES2, GLES2 ].</p> + * @see GLProfile#isGL2ES2() + */ public final boolean isGL2ES2() { - return isGL2GL3() || isGLES2() ; + return isGLES2() || isGL2GL3(); } /** - * Set the swap interval if the current context. - * @param interval Should be ≥ 0. 0 Disables the vertical synchronisation, + * Indicates whether this GLContext is capable of GL2ES3. <p>Includes [ GL4bc, GL4, GL3bc, GL3, GLES3, GL3ES3, GL2, GL2GL3 ].</p> + * @see GLProfile#isGL2ES3() + * @see #isGL3ES3() + * @see #isGL2GL3() + */ + public final boolean isGL2ES3() { + return isGL3ES3() || isGL2GL3(); + } + + /** + * Indicates whether this GLContext is capable of GL3ES3. <p>Includes [ GL4bc, GL4, GL3bc, GL3, GLES3 ].</p> + * @see GLProfile#isGL3ES3() + */ + public final boolean isGL3ES3() { + return isGL4ES3() || isGL3(); + } + + /** + * Returns true if this profile is capable of GL4ES3, i.e. if {@link #isGLES3Compatible()} returns true. + * <p>Includes [ GL ≥ 4.3, GL ≥ 3.1 w/ GL_ARB_ES3_compatibility and GLES3 ]</p> + * @see GLProfile#isGL4ES3() + */ + public final boolean isGL4ES3() { + return isGLES3Compatible() ; + } + + /** + * Set the swap interval of the current context and attached drawable. + * @param interval Should be ≥ 0. 0 disables the vertical synchronization, * where ≥ 1 is the number of vertical refreshes before a swap buffer occurs. * A value < 0 is ignored. - * @return true if the operation was successful, otherwise false - * + * @return true if the operation was successful, otherwise false + * * @throws GLException if the context is not current. */ public final boolean setSwapInterval(int interval) throws GLException { validateCurrent(); if(0<=interval) { - if( setSwapIntervalImpl(interval) ) { - currentSwapInterval = interval; - return true; + if( !drawableRetargeted || !hasRendererQuirk(GLRendererQuirks.NoSetSwapIntervalPostRetarget) ) { + if( setSwapIntervalImpl(interval) ) { + currentSwapInterval = interval; + return true; + } } } return false; } - protected boolean setSwapIntervalImpl(int interval) { - return false; - } - /** Return the current swap interval. + protected boolean setSwapIntervalImpl(int interval) { + return false; + } + /** Return the current swap interval. * <p> * If the context has not been made current at all, * the default value <code>-1</code> is returned. * </p> * <p> - * The default value for a valid context is <code>1</code> for - * an EGL based profile (ES1 or ES2) and <code>-1</code> (undefined) - * for desktop. + * For a valid context the default value is <code>1</code> + * in case of an EGL based profile (ES1 or ES2) and <code>-1</code> + * (undefined) for desktop. * </p> */ public final int getSwapInterval() { - if(-1 == currentSwapInterval && this.isGLES()) { - currentSwapInterval = 1; - } return currentSwapInterval; } protected final void setDefaultSwapInterval() { @@ -652,9 +1222,9 @@ public abstract class GLContext { currentSwapInterval = 1; } else { currentSwapInterval = -1; - } + } } - + public final boolean queryMaxSwapGroups(int[] maxGroups, int maxGroups_offset, int[] maxBarriers, int maxBarriers_offset) { validateCurrent(); @@ -667,147 +1237,286 @@ public abstract class GLContext { return joinSwapGroupImpl(group); } protected boolean joinSwapGroupImpl(int group) { /** nop per default .. **/ return false; } - protected int currentSwapGroup = -1; // default: not set yet .. + protected int currentSwapGroup = -1; // default: not set yet .. public int getSwapGroup() { return currentSwapGroup; } public final boolean bindSwapBarrier(int group, int barrier) { validateCurrent(); - return bindSwapBarrierImpl(group, barrier); + return bindSwapBarrierImpl(group, barrier); } protected boolean bindSwapBarrierImpl(int group, int barrier) { /** nop per default .. **/ return false; } - /** - * @return The extension implementing the GLDebugOutput feature, - * either <i>GL_ARB_debug_output</i> or <i>GL_AMD_debug_output</i>. - * If unavailable or called before initialized via {@link #makeCurrent()}, <i>null</i> is returned. + * Return the framebuffer name bound to this context, + * see {@link GL#glBindFramebuffer(int, int)}. + */ + public abstract int getBoundFramebuffer(int target); + + /** + * Return the default draw framebuffer name. + * <p> + * May differ from it's default <code>zero</code> + * in case an framebuffer object ({@link com.jogamp.opengl.FBObject}) based drawable + * is being used. + * </p> + */ + public abstract int getDefaultDrawFramebuffer(); + + /** + * Return the default read framebuffer name. + * <p> + * May differ from it's default <code>zero</code> + * in case an framebuffer object ({@link com.jogamp.opengl.FBObject}) based drawable + * is being used. + * </p> + */ + public abstract int getDefaultReadFramebuffer(); + + /** + * Returns the default color buffer within the current bound + * {@link #getDefaultReadFramebuffer()}, i.e. GL_READ_FRAMEBUFFER​, + * which will be used as the source for pixel reading commands, + * like {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer) glReadPixels} etc. + * <p> + * For offscreen framebuffer objects this is {@link GL#GL_COLOR_ATTACHMENT0}, + * otherwise this is {@link GL#GL_FRONT} for single buffer configurations + * and {@link GL#GL_BACK} for double buffer configurations. + * </p> + * <p> + * Note-1: Neither ES1 nor ES2 supports selecting the read buffer via glReadBuffer + * and {@link GL#GL_BACK} is the default. + * </p> + * <p> + * Note-2: ES3 only supports {@link GL#GL_BACK}, {@link GL#GL_NONE} or {@link GL#GL_COLOR_ATTACHMENT0}+i + * </p> + * <p> + * Note-3: See {@link com.jogamp.opengl.util.GLDrawableUtil#swapBuffersBeforeRead(GLCapabilitiesImmutable) swapBuffersBeforeRead} + * for read-pixels and swap-buffers implications. + * </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)}. */ + 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)}. */ + public abstract int getDefaultPixelDataFormat(); + + /** + * @return The extension implementing the GLDebugOutput feature, + * either {@link GLExtensions#ARB_debug_output} or {@link GLExtensions#AMD_debug_output}. + * If unavailable or called before initialized via {@link #makeCurrent()}, <i>null</i> is returned. */ public abstract String getGLDebugMessageExtension(); /** - * @return the current synchronous debug behavior via - * @see #setSynchronous(boolean) + * @return the current synchronous debug behavior, set via {@link #setGLDebugSynchronous(boolean)}. */ public abstract boolean isGLDebugSynchronous(); - + /** * Enables or disables the synchronous debug behavior via - * {@link GL2GL3#GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB glEnable/glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB)}, - * if extension is {@link #GL_ARB_debug_output}. - * There is no equivalent for {@link #GL_AMD_debug_output}. - * <p> The default is <code>true</code>, ie {@link GL2GL3#GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB}.</p> + * {@link GL2GL3#GL_DEBUG_OUTPUT_SYNCHRONOUS glEnable/glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS)}, + * if extension is {@link GLExtensions#ARB_debug_output}. + * There is no equivalent for {@link GLExtensions#AMD_debug_output}. + * <p> The default is <code>true</code>, ie {@link GL2GL3#GL_DEBUG_OUTPUT_SYNCHRONOUS}.</p> + * @link {@link #isGLDebugSynchronous()} */ public abstract void setGLDebugSynchronous(boolean synchronous); - + /** * @return true if the GLDebugOutput feature is enabled or not. */ public abstract boolean isGLDebugMessageEnabled(); - + /** - * Enables or disables the GLDebugOutput feature of extension <i>GL_ARB_debug_output</i> - * or <i>GL_AMD_debug_output</i>, if available. - * + * Enables or disables the GLDebugOutput feature of extension {@link GLExtensions#ARB_debug_output} + * or {@link GLExtensions#AMD_debug_output}, if available. + * * <p>To enable the GLDebugOutput feature {@link #enableGLDebugMessage(boolean) enableGLDebugMessage(true)} * or {@link #setContextCreationFlags(int) setContextCreationFlags}({@link GLContext#CTX_OPTION_DEBUG}) * shall be called <b>before</b> context creation via {@link #makeCurrent()}!</p> - * - * <p>In case {@link GLAutoDrawable} are being used, + * + * <p>In case {@link GLAutoDrawable} are being used, * {@link GLAutoDrawable#setContextCreationFlags(int) glAutoDrawable.setContextCreationFlags}({@link GLContext#CTX_OPTION_DEBUG}) * shall be issued before context creation via {@link #makeCurrent()}!</p> - * + * * <p>After context creation, the GLDebugOutput feature may be enabled or disabled at any time using this method.</p> - * + * * @param enable If true enables, otherwise disables the GLDebugOutput feature. - * + * * @throws GLException if this context is not current or GLDebugOutput registration failed (enable) - * + * * @see #setContextCreationFlags(int) * @see #addGLDebugListener(GLDebugListener) * @see GLAutoDrawable#setContextCreationFlags(int) */ public abstract void enableGLDebugMessage(boolean enable) throws GLException; - + /** * Add {@link GLDebugListener}.<br> - * - * @param listener {@link GLDebugListener} handling {@GLDebugMessage}s - * @see #enableGLDebugMessage(boolean) + * + * @param listener {@link GLDebugListener} handling {@link GLDebugMessage}s + * @see #enableGLDebugMessage(boolean) * @see #removeGLDebugListener(GLDebugListener) */ public abstract void addGLDebugListener(GLDebugListener listener); - + /** * Remove {@link GLDebugListener}.<br> - * - * @param listener {@link GLDebugListener} handling {@GLDebugMessage}s - * @see #enableGLDebugMessage(boolean) + * + * @param listener {@link GLDebugListener} handling {@link GLDebugMessage}s + * @see #enableGLDebugMessage(boolean) * @see #addGLDebugListener(GLDebugListener) */ public abstract void removeGLDebugListener(GLDebugListener listener); - + /** - * Generic entry for {@link GL2GL3#glDebugMessageControlARB(int, int, int, int, IntBuffer, boolean)} + * Generic entry for {@link GL2GL3#glDebugMessageControl(int, int, int, int, IntBuffer, boolean)} * and {@link GL2GL3#glDebugMessageEnableAMD(int, int, int, IntBuffer, boolean)} of the GLDebugOutput feature. - * @see #enableGLDebugMessage(boolean) + * @see #enableGLDebugMessage(boolean) */ public abstract void glDebugMessageControl(int source, int type, int severity, int count, IntBuffer ids, boolean enabled); - + /** - * Generic entry for {@link GL2GL3#glDebugMessageControlARB(int, int, int, int, int[], int, boolean)} - * and {@link GL2GL3#glDebugMessageEnableAMD(int, int, int, int[], int, boolean)} of the GLDebugOutput feature. - * @see #enableGLDebugMessage(boolean) + * Generic entry for {@link GL2GL3#glDebugMessageControl(int, int, int, int, int[], int, boolean)} + * and {@link GL2GL3#glDebugMessageEnableAMD(int, int, int, int[], int, boolean)} of the GLDebugOutput feature. + * @see #enableGLDebugMessage(boolean) */ public abstract void glDebugMessageControl(int source, int type, int severity, int count, int[] ids, int ids_offset, boolean enabled); - + /** - * Generic entry for {@link GL2GL3#glDebugMessageInsertARB(int, int, int, int, int, String)} + * Generic entry for {@link GL2GL3#glDebugMessageInsert(int, int, int, int, int, String)} * and {@link GL2GL3#glDebugMessageInsertAMD(int, int, int, int, String)} of the GLDebugOutput feature. - * @see #enableGLDebugMessage(boolean) + * @see #enableGLDebugMessage(boolean) */ public abstract void glDebugMessageInsert(int source, int type, int id, int severity, String buf); - + public static final int GL_VERSIONS[][] = { /* 0.*/ { -1 }, /* 1.*/ { 0, 1, 2, 3, 4, 5 }, /* 2.*/ { 0, 1 }, /* 3.*/ { 0, 1, 2, 3 }, - /* 4.*/ { 0, 1, 2 } }; + /* 4.*/ { 0, 1, 2, 3, 4 } }; - public static final int getMaxMajor() { - return GL_VERSIONS.length-1; + public static final int ES_VERSIONS[][] = { + /* 0.*/ { -1 }, + /* 1.*/ { 0, 1 }, + /* 2.*/ { 0 }, + /* 3.*/ { 0 } }; + + public static final int getMaxMajor(int ctxProfile) { + return ( 0 != ( CTX_PROFILE_ES & ctxProfile ) ) ? ES_VERSIONS.length-1 : GL_VERSIONS.length-1; } - public static final int getMaxMinor(int major) { - if(1>major || major>=GL_VERSIONS.length) return -1; - return GL_VERSIONS[major].length-1; + public static final int getMaxMinor(int ctxProfile, int major) { + if( 1>major ) { + return -1; + } + if( ( 0 != ( CTX_PROFILE_ES & ctxProfile ) ) ) { + if( major>=ES_VERSIONS.length ) return -1; + return ES_VERSIONS[major].length-1; + } else { + if( major>=GL_VERSIONS.length ) return -1; + return GL_VERSIONS[major].length-1; + } } - public static final boolean isValidGLVersion(int major, int minor) { - if(1>major || major>=GL_VERSIONS.length) return false; - if(0>minor || minor>=GL_VERSIONS[major].length) return false; + /** + * Returns true, if the major.minor is not inferior to the lowest + * valid version and does not exceed the highest known major number by more than one. + * <p> + * The minor version number is ignored by the upper limit validation + * and the major version number may exceed by one. + * </p> + * <p> + * The upper limit check is relaxed since we don't want to cut-off + * unforseen new GL version since the release of JOGL. + * </p> + * <p> + * Hence it is important to iterate through GL version from the upper limit + * and {@link #decrementGLVersion(int, int[], int[])} until invalid. + * </p> + */ + public static final boolean isValidGLVersion(int ctxProfile, int major, int minor) { + if( 1>major || 0>minor ) { + return false; + } + if( 0 != ( CTX_PROFILE_ES & ctxProfile ) ) { + if( major >= ES_VERSIONS.length + 1 ) return false; + } else { + if( major>=GL_VERSIONS.length + 1 ) return false; + } return true; } - public static final boolean decrementGLVersion(int major[], int minor[]) { - if(null==major || major.length<1 ||null==minor || minor.length<1) { - throw new GLException("invalid array arguments"); - } - int m = major[0]; - int n = minor[0]; - if(!isValidGLVersion(m, n)) return false; - - // decrement .. - n -= 1; - if(n < 0) { - m -= 1; - n = GL_VERSIONS[m].length-1; + /** + * Clip the given GL version to the maximum known valid version if exceeding. + * @return true if clipped, i.e. given value exceeds maximum, otherwise false. + */ + public static final boolean clipGLVersion(int ctxProfile, int major[], int minor[]) { + final int m = major[0]; + final int n = minor[0]; + + if( 0 != ( CTX_PROFILE_ES & ctxProfile ) ) { + if( m >= ES_VERSIONS.length ) { + major[0] = ES_VERSIONS.length - 1; + minor[0] = ES_VERSIONS[major[0]].length - 1; + return true; + } + if( n >= ES_VERSIONS[m].length ) { + minor[0] = ES_VERSIONS[m].length - 1; + return true; + } + } else if( m >= GL_VERSIONS.length ) { // !isES + major[0] = GL_VERSIONS.length - 1; + minor[0] = GL_VERSIONS[major[0]].length - 1; + return true; + } else if( n >= GL_VERSIONS[m].length ) { // !isES + minor[0] = GL_VERSIONS[m].length - 1; + return true; } - if(!isValidGLVersion(m, n)) return false; - major[0]=m; - minor[0]=n; + return false; + } + /** + * Decrement the given GL version by one + * and return true if still valid, otherwise false. + * <p> + * If the given version exceeds the maximum known valid version, + * it is {@link #clipGLVersion(int, int[], int[]) clipped} and + * true is returned. + * </p> + * + * @param ctxProfile + * @param major + * @param minor + * @return + */ + public static final boolean decrementGLVersion(int ctxProfile, int major[], int minor[]) { + if( !clipGLVersion(ctxProfile, major, minor) ) { + int m = major[0]; + int n = minor[0] - 1; + if(n < 0) { + if( 0 != ( CTX_PROFILE_ES & ctxProfile ) ) { + if( m >= 3 ) { + m -= 1; + } else { + m = 0; // major decr [1,2] -> 0 + } + n = ES_VERSIONS[m].length-1; + } else { + m -= 1; + n = GL_VERSIONS[m].length-1; + } + } + if( !isValidGLVersion(ctxProfile, m, n) ) { + return false; + } + major[0]=m; + minor[0]=n; + } return true; } @@ -832,48 +1541,53 @@ public abstract class GLContext { // /** - * @see #getDeviceVersionAvailableKey(javax.media.nativewindow.AbstractGraphicsDevice, int, int) + * @see #getDeviceVersionAvailableKey(javax.media.nativewindow.AbstractGraphicsDevice, int, int) */ - protected static /*final*/ HashMap<String, Integer> deviceVersionAvailable = new HashMap<String, Integer>(); + protected static final IdentityHashMap<String, Integer> deviceVersionAvailable = new IdentityHashMap<String, Integer>(); /** * @see #getUniqueDeviceString(javax.media.nativewindow.AbstractGraphicsDevice) */ - private static /*final*/ HashSet<String> deviceVersionsAvailableSet = new HashSet<String>(); + private static final IdentityHashMap<String, String> deviceVersionsAvailableSet = new IdentityHashMap<String, String>(); - protected static String getDeviceVersionAvailableKey(AbstractGraphicsDevice device, int major, int profile) { - return device.getUniqueID() + "-" + toHexString(composeBits(major, profile, 0)); + /** clears the device/context mappings as well as the GL/GLX proc address tables. */ + protected static void shutdown() { + deviceVersionAvailable.clear(); + deviceVersionsAvailableSet.clear(); + GLContextImpl.shutdownImpl(); // well .. } protected static boolean getAvailableGLVersionsSet(AbstractGraphicsDevice device) { synchronized ( deviceVersionsAvailableSet ) { - return deviceVersionsAvailableSet.contains(device.getUniqueID()); + return deviceVersionsAvailableSet.containsKey(device.getUniqueID()); } } protected static void setAvailableGLVersionsSet(AbstractGraphicsDevice device) { synchronized ( deviceVersionsAvailableSet ) { - String devKey = device.getUniqueID(); - if ( deviceVersionsAvailableSet.contains(devKey) ) { + final String devKey = device.getUniqueID(); + if( null != deviceVersionsAvailableSet.put(devKey, devKey) ) { throw new InternalError("Already set: "+devKey); } - deviceVersionsAvailableSet.add(devKey); if (DEBUG) { System.err.println(getThreadName() + ": createContextARB: SET mappedVersionsAvailableSet "+devKey); + System.err.println(GLContext.dumpAvailableGLVersions(null).toString()); } } } - - /** clears the device/context mappings as well as the GL/GLX proc address tables. */ - protected static void shutdown() { - deviceVersionAvailable.clear(); - deviceVersionsAvailableSet.clear(); - GLContextImpl.shutdownImpl(); // well .. + + /** + * Returns a unique String object using {@link String#intern()} for the given arguments, + * which object reference itself can be used as a key. + */ + protected static String getDeviceVersionAvailableKey(AbstractGraphicsDevice device, int major, int profile) { + final String r = device.getUniqueID() + "-" + toHexString(composeBits(major, profile, 0)); + return r.intern(); } /** - * Called by {@link jogamp.opengl.GLContextImpl#createContextARBMapVersionsAvailable} not intended to be used by - * implementations. However, if {@link #createContextARB} is not being used within + * Called by {@link jogamp.opengl.GLContextImpl#createContextARBMapVersionsAvailable(int,int)} not intended to be used by + * implementations. However, if {@link jogamp.opengl.GLContextImpl#createContextARB(long, boolean)} is not being used within * {@link javax.media.opengl.GLDrawableFactory#getOrCreateSharedContext(javax.media.nativewindow.AbstractGraphicsDevice)}, * GLProfile has to map the available versions. * @@ -889,12 +1603,47 @@ public abstract class GLContext { validateProfileBits(profile, "profile"); validateProfileBits(resCtp, "resCtp"); - String key = getDeviceVersionAvailableKey(device, reqMajor, profile); - Integer val = new Integer(composeBits(resMajor, resMinor, resCtp)); + if(FORCE_NO_FBO_SUPPORT) { + resCtp &= ~CTX_IMPL_FBO ; + } + if(DEBUG) { + System.err.println("GLContext.mapAvailableGLVersion: "+device+": "+getGLVersion(reqMajor, 0, profile, null)+" -> "+getGLVersion(resMajor, resMinor, resCtp, null)); + // Thread.dumpStack(); + } + final String objectKey = getDeviceVersionAvailableKey(device, reqMajor, profile); + final Integer val = new Integer(composeBits(resMajor, resMinor, resCtp)); synchronized(deviceVersionAvailable) { - val = deviceVersionAvailable.put( key, val ); + return deviceVersionAvailable.put( objectKey, val ); } - return val; + } + + protected static StringBuilder dumpAvailableGLVersions(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } + synchronized(deviceVersionAvailable) { + final Set<String> keys = deviceVersionAvailable.keySet(); + boolean needsSeparator = false; + for(Iterator<String> i = keys.iterator(); i.hasNext(); ) { + if(needsSeparator) { + sb.append(Platform.getNewline()); + } + final String key = i.next(); + sb.append(key).append(": "); + final Integer valI = deviceVersionAvailable.get(key); + if(null != valI) { + final int bits32 = valI.intValue(); + final int major = ( bits32 & 0xFF000000 ) >>> 24 ; + final int minor = ( bits32 & 0x00FF0000 ) >>> 16 ; + final int ctp = ( bits32 & 0x0000FFFF ) ; + sb.append(GLContext.getGLVersion(major, minor, ctp, null)); + } else { + sb.append("n/a"); + } + needsSeparator = true; + } + } + return sb; } /** @@ -904,10 +1653,10 @@ public abstract class GLContext { * @return the available GL version as encoded with {@link #composeBits(int, int, int), otherwise <code>null</code> */ protected static Integer getAvailableGLVersion(AbstractGraphicsDevice device, int reqMajor, int reqProfile) { - String key = getDeviceVersionAvailableKey(device, reqMajor, reqProfile); + final String objectKey = getDeviceVersionAvailableKey(device, reqMajor, reqProfile); Integer val; synchronized(deviceVersionAvailable) { - val = deviceVersionAvailable.get( key ); + val = deviceVersionAvailable.get( objectKey ); } return val; } @@ -919,55 +1668,109 @@ public abstract class GLContext { * @param minor if not null, returns the used minor version * @param ctp if not null, returns the used context profile */ - protected static boolean getAvailableGLVersion(AbstractGraphicsDevice device, int reqMajor, int reqProfile, + protected static boolean getAvailableGLVersion(AbstractGraphicsDevice device, int reqMajor, int reqProfile, int[] major, int minor[], int ctp[]) { - Integer valI = getAvailableGLVersion(device, reqMajor, reqProfile); + final Integer valI = getAvailableGLVersion(device, reqMajor, reqProfile); if(null==valI) { return false; } - int bits32 = valI.intValue(); + final int bits32 = valI.intValue(); if(null!=major) { - major[0] = ( bits32 & 0xFF000000 ) >> 24 ; + major[0] = ( bits32 & 0xFF000000 ) >>> 24 ; } if(null!=minor) { - minor[0] = ( bits32 & 0x00FF0000 ) >> 16 ; + minor[0] = ( bits32 & 0x00FF0000 ) >>> 16 ; } if(null!=ctp) { ctp[0] = ( bits32 & 0x0000FFFF ) ; } return true; } - - /** + + /** * returns the highest GLProfile string regarding the implementation version and context profile bits. * @throws GLException if version and context profile bits could not be mapped to a GLProfile */ - protected static String getGLProfile(int major, int minor, int ctp) + protected static String getGLProfile(int major, int minor, int ctp) throws GLException { - if(0 != ( CTX_PROFILE_COMPAT & ctp )) { + if(0 != ( CTX_PROFILE_COMPAT & ctp )) { if(major >= 4) { return GLProfile.GL4bc; } else if(major == 3 && minor >= 1) { return GLProfile.GL3bc; } - else { return GLProfile.GL2; } + else { return GLProfile.GL2; } } else if(0 != ( CTX_PROFILE_CORE & ctp )) { if(major >= 4) { return GLProfile.GL4; } - else if(major == 3 && minor >= 1) { return GLProfile.GL3; } + else if(major == 3 && minor >= 1) { return GLProfile.GL3; } } else if(0 != ( CTX_PROFILE_ES & ctp )) { - if(major == 2) { return GLProfile.GLES2; } - else if(major == 1) { return GLProfile.GLES1; } + if(major == 3) { return GLProfile.GLES3; } + else if(major == 2) { return GLProfile.GLES2; } + else if(major == 1) { return GLProfile.GLES1; } } throw new GLException("Unhandled OpenGL version/profile: "+GLContext.getGLVersion(major, minor, ctp, null)); } - - /** + + /** + * Returns the GLProfile's major version number at reqMajorCTP[0] and it's context property (CTP) at reqMajorCTP[1] for availability mapping request. + */ + protected static final void getRequestMajorAndCompat(final GLProfile glp, int[/*2*/] reqMajorCTP) { + final GLProfile glpImpl = glp.getImpl(); + if( glpImpl.isGL4() ) { + reqMajorCTP[0]=4; + } else if ( glpImpl.isGL3() || glpImpl.isGLES3() ) { + reqMajorCTP[0]=3; + } else if (glpImpl.isGLES1()) { + reqMajorCTP[0]=1; + } else /* if (glpImpl.isGL2() || glpImpl.isGLES2()) */ { + reqMajorCTP[0]=2; + } + if( glpImpl.isGLES() ) { + reqMajorCTP[1]=CTX_PROFILE_ES; + } else if( glpImpl.isGL2() ) { // incl GL3bc and GL4bc + reqMajorCTP[1]=CTX_PROFILE_COMPAT; + } else { + reqMajorCTP[1]=CTX_PROFILE_CORE; + } + } + + /** + * @param device the device the context profile is being requested for + * @param GLProfile the GLProfile the context profile is being requested for + * @return the GLProfile's context property (CTP) if available, otherwise <code>0</code> + */ + protected static final int getAvailableContextProperties(final AbstractGraphicsDevice device, final GLProfile glp) { + final int[] reqMajorCTP = new int[] { 0, 0 }; + getRequestMajorAndCompat(glp, reqMajorCTP); + + int _major[] = { 0 }; + int _minor[] = { 0 }; + int _ctp[] = { 0 }; + if( GLContext.getAvailableGLVersion(device, reqMajorCTP[0], reqMajorCTP[1], _major, _minor, _ctp)) { + return _ctp[0]; + } + return 0; // n/a + } + + /** + * @param device the device the profile is being requested * @param major Key Value either 1, 2, 3 or 4 * @param profile Key Value either {@link #CTX_PROFILE_COMPAT}, {@link #CTX_PROFILE_CORE} or {@link #CTX_PROFILE_ES} - * @return the highest GLProfile string regarding the version and profile bits. - * @throws GLException if version and context profile bits could not be mapped to a GLProfile - */ - public static String getAvailableGLProfile(AbstractGraphicsDevice device, int reqMajor, int reqProfile) + * @return the highest GLProfile for the device regarding availability, version and profile bits. + */ + protected static GLProfile getAvailableGLProfile(AbstractGraphicsDevice device, int reqMajor, int reqProfile) + throws GLException { + final String glpName = getAvailableGLProfileName(device, reqMajor, reqProfile); + return null != glpName ? GLProfile.get(device, glpName) : null; + } + + /** + * @param device the device the profile is being requested + * @param major Key Value either 1, 2, 3 or 4 + * @param profile Key Value either {@link #CTX_PROFILE_COMPAT}, {@link #CTX_PROFILE_CORE} or {@link #CTX_PROFILE_ES} + * @return the highest GLProfile name for the device regarding availability, version and profile bits. + */ + /* package */ static String getAvailableGLProfileName(AbstractGraphicsDevice device, int reqMajor, int reqProfile) throws GLException { int major[] = { 0 }; int minor[] = { 0 }; @@ -979,13 +1782,63 @@ public abstract class GLContext { } /** + * @param device the device the profile is being requested + * @param major Key Value either 1, 2, 3 or 4 + * @param profile Key Value either {@link #CTX_PROFILE_COMPAT}, {@link #CTX_PROFILE_CORE} or {@link #CTX_PROFILE_ES} + */ + protected static String getAvailableGLVersionAsString(AbstractGraphicsDevice device, int major, int profile) { + int _major[] = { 0 }; + int _minor[] = { 0 }; + int _ctp[] = { 0 }; + if(getAvailableGLVersion(device, major, profile, _major, _minor, _ctp)) { + return getGLVersion(_major[0], _minor[0], _ctp[0], null); + } + return null; + } + + /** + * Returns true if it is possible to create an <i>framebuffer object</i> (FBO). + * <p> + * FBO feature is implemented in OpenGL, hence it is {@link GLProfile} dependent. + * </p> + * <p> + * FBO support is queried as described in {@link #hasBasicFBOSupport()}. + * </p> + * + * @param device the device to request whether FBO is available for + * @param glp {@link GLProfile} to check for FBO capabilities + * @see GLContext#hasBasicFBOSupport() + */ + public static final boolean isFBOAvailable(AbstractGraphicsDevice device, GLProfile glp) { + return 0 != ( CTX_IMPL_FBO & getAvailableContextProperties(device, glp) ); + } + + /** + * @return <code>1</code> if using a hardware rasterizer, <code>0</code> if using a software rasterizer and <code>-1</code> if not determined yet. + * @see GLContext#isHardwareRasterizer() + * @see GLProfile#isHardwareRasterizer() + */ + public static final int isHardwareRasterizer(AbstractGraphicsDevice device, GLProfile glp) { + final int r; + final int ctp = getAvailableContextProperties(device, glp); + if(0 == ctp) { + r = -1; + } else if( 0 == ( CTX_IMPL_ACCEL_SOFT & ctp ) ) { + r = 1; + } else { + r = 0; + } + return r; + } + + /** * @param device the device to request whether the profile is available for * @param reqMajor Key Value either 1, 2, 3 or 4 * @param reqProfile Key Value either {@link #CTX_PROFILE_COMPAT}, {@link #CTX_PROFILE_CORE} or {@link #CTX_PROFILE_ES} * @param isHardware return value of one boolean, whether the profile is a hardware rasterizer or not * @return true if the requested GL version is available regardless of a software or hardware rasterizer, otherwise false. */ - public static boolean isGLVersionAvailable(AbstractGraphicsDevice device, int reqMajor, int reqProfile, boolean isHardware[]) { + protected static boolean isGLVersionAvailable(AbstractGraphicsDevice device, int reqMajor, int reqProfile, boolean isHardware[]) { Integer valI = getAvailableGLVersion(device, reqMajor, reqProfile); if(null==valI) { return false; @@ -1002,6 +1855,34 @@ public abstract class GLContext { return isGLVersionAvailable(device, 2, GLContext.CTX_PROFILE_ES, isHardware); } + public static boolean isGLES3Available(AbstractGraphicsDevice device, boolean isHardware[]) { + return isGLVersionAvailable(device, 3, GLContext.CTX_PROFILE_ES, isHardware); + } + + /** + * Returns true if a ES3 compatible profile is available, + * i.e. either a ≥ 4.3 context or a ≥ 3.1 context supporting <code>GL_ARB_ES3_compatibility</code>, + * otherwise false. + * <p> + * Includes [ GL ≥ 4.3, GL ≥ 3.1 w/ GL_ARB_ES3_compatibility and GLES3 ] + * </p> + */ + public static final boolean isGLES3CompatibleAvailable(AbstractGraphicsDevice device) { + int major[] = { 0 }; + int minor[] = { 0 }; + int ctp[] = { 0 }; + boolean ok; + + ok = GLContext.getAvailableGLVersion(device, 3, GLContext.CTX_PROFILE_ES, major, minor, ctp); + if( !ok ) { + ok = GLContext.getAvailableGLVersion(device, 3, GLContext.CTX_PROFILE_CORE, major, minor, ctp); + } + if( !ok ) { + ok = GLContext.getAvailableGLVersion(device, 3, GLContext.CTX_PROFILE_COMPAT, major, minor, ctp); + } + return 0 != ( ctp[0] & CTX_IMPL_ES3_COMPAT ); + } + public static boolean isGL4bcAvailable(AbstractGraphicsDevice device, boolean isHardware[]) { return isGLVersionAvailable(device, 4, CTX_PROFILE_COMPAT, isHardware); } @@ -1022,21 +1903,7 @@ public abstract class GLContext { return isGLVersionAvailable(device, 2, CTX_PROFILE_COMPAT, isHardware); } - /** - * @param major Key Value either 1, 2, 3 or 4 - * @param profile Key Value either {@link #CTX_PROFILE_COMPAT}, {@link #CTX_PROFILE_CORE} or {@link #CTX_PROFILE_ES} - */ - public static String getAvailableGLVersionAsString(AbstractGraphicsDevice device, int major, int profile) { - int _major[] = { 0 }; - int _minor[] = { 0 }; - int _ctp[] = { 0 }; - if(getAvailableGLVersion(device, major, profile, _major, _minor, _ctp)) { - return getGLVersion(_major[0], _minor[0], _ctp[0], null); - } - return null; - } - - public static String getGLVersion(int major, int minor, int ctp, String gl_version) { + protected static String getGLVersion(int major, int minor, int ctp, String gl_version) { boolean needColon = false; StringBuilder sb = new StringBuilder(); sb.append(major); @@ -1044,12 +1911,15 @@ public abstract class GLContext { sb.append(minor); sb.append(" ("); needColon = appendString(sb, "ES profile", needColon, 0 != ( CTX_PROFILE_ES & ctp )); - needColon = appendString(sb, "Compatibility profile", needColon, 0 != ( CTX_PROFILE_COMPAT & ctp )); + needColon = appendString(sb, "Compat profile", needColon, 0 != ( CTX_PROFILE_COMPAT & ctp )); needColon = appendString(sb, "Core profile", needColon, 0 != ( CTX_PROFILE_CORE & ctp )); - needColon = appendString(sb, "forward", needColon, 0 != ( CTX_OPTION_FORWARD & ctp )); + needColon = appendString(sb, "forward", needColon, 0 != ( CTX_OPTION_FORWARD & ctp )); needColon = appendString(sb, "arb", needColon, 0 != ( CTX_IS_ARB_CREATED & ctp )); - needColon = appendString(sb, "debug", needColon, 0 != ( CTX_OPTION_DEBUG & ctp )); - needColon = appendString(sb, "ES2 compatible", needColon, 0 != ( CTX_IMPL_ES2_COMPAT & ctp )); + needColon = appendString(sb, "debug", needColon, 0 != ( CTX_OPTION_DEBUG & ctp )); + needColon = appendString(sb, "ES2 compat", needColon, 0 != ( CTX_IMPL_ES2_COMPAT & ctp )); + needColon = appendString(sb, "ES3 compat", needColon, 0 != ( CTX_IMPL_ES3_COMPAT & ctp )); + needColon = appendString(sb, "FBO", needColon, 0 != ( CTX_IMPL_FBO & ctp )); + needColon = appendString(sb, "FP32 compat", needColon, 0 != ( CTX_IMPL_FP32_COMPAT_API & ctp )); if( 0 != ( CTX_IMPL_ACCEL_SOFT & ctp ) ) { needColon = appendString(sb, "software", needColon, true); } else { @@ -1064,9 +1934,9 @@ public abstract class GLContext { } // - // internal string utils + // internal string utils // - + protected static String toHexString(int hex) { return "0x" + Integer.toHexString(hex); } @@ -1085,10 +1955,8 @@ public abstract class GLContext { } return needColon; } - - protected static String getThreadName() { - return Thread.currentThread().getName(); - } + + protected static String getThreadName() { return Thread.currentThread().getName(); } } diff --git a/src/jogl/classes/javax/media/opengl/GLDebugListener.java b/src/jogl/classes/javax/media/opengl/GLDebugListener.java index 8887d022a..ec7f7cec1 100644 --- a/src/jogl/classes/javax/media/opengl/GLDebugListener.java +++ b/src/jogl/classes/javax/media/opengl/GLDebugListener.java @@ -29,16 +29,16 @@ package javax.media.opengl; /** * Listener for {@link GLDebugMessage}s. - * + * * <p>One can enable GLDebugOutput via {@link GLContext#enableGLDebugMessage(boolean)} * and add listeners via {@link GLContext#addGLDebugListener(GLDebugListener)}. */ public interface GLDebugListener { - /** + /** * Handle {@link GLDebugMessage} message sent from native GL implementation. - * + * * <p>Since this method is invoked directly by the GL implementation, it shall * return as fast as possible.</p> */ - void messageSent(GLDebugMessage event); + void messageSent(GLDebugMessage event); } diff --git a/src/jogl/classes/javax/media/opengl/GLDebugMessage.java b/src/jogl/classes/javax/media/opengl/GLDebugMessage.java index 3ab0683c6..acb33b058 100644 --- a/src/jogl/classes/javax/media/opengl/GLDebugMessage.java +++ b/src/jogl/classes/javax/media/opengl/GLDebugMessage.java @@ -29,15 +29,19 @@ package javax.media.opengl; import com.jogamp.common.os.Platform; +/** + * OpenGL debug message generated by the driver + * and delivered via {@link GLDebugListener}. + */ public class GLDebugMessage { final GLContext source; - final long when; + final long when; final int dbgSource; final int dbgType; final int dbgId; final int dbgSeverity; final String dbgMsg; - + /** * @param source The source of the event * @param when The time of the event @@ -56,9 +60,9 @@ public class GLDebugMessage { this.dbgSeverity = dbgSeverity; this.dbgMsg = dbgMsg; } - + /** - * + * * @param source * @param when * @param dbgId @@ -69,88 +73,88 @@ public class GLDebugMessage { */ public static GLDebugMessage translateAMDEvent(GLContext source, long when, int dbgId, int amdDbgCategory, int dbgSeverity, String dbgMsg) { int dbgSource, dbgType; - + // AMD category == ARB source/type switch(amdDbgCategory) { - case GL2GL3.GL_DEBUG_CATEGORY_API_ERROR_AMD: - dbgSource = GL2GL3.GL_DEBUG_SOURCE_API_ARB; - dbgType = GL2GL3.GL_DEBUG_TYPE_ERROR_ARB; + case GL2GL3.GL_DEBUG_CATEGORY_API_ERROR_AMD: + dbgSource = GL2GL3.GL_DEBUG_SOURCE_API; + dbgType = GL2GL3.GL_DEBUG_TYPE_ERROR; break; // // def source / other type // - - case GL2GL3.GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD: - dbgSource = GL2GL3.GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB; - dbgType = GL2GL3.GL_DEBUG_TYPE_OTHER_ARB; + + case GL2GL3.GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD: + dbgSource = GL2GL3.GL_DEBUG_SOURCE_WINDOW_SYSTEM; + dbgType = GL2GL3.GL_DEBUG_TYPE_OTHER; break; - + case GL2GL3.GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD: - dbgSource = GL2GL3.GL_DEBUG_SOURCE_SHADER_COMPILER_ARB; - dbgType = GL2GL3.GL_DEBUG_TYPE_OTHER_ARB; + dbgSource = GL2GL3.GL_DEBUG_SOURCE_SHADER_COMPILER; + dbgType = GL2GL3.GL_DEBUG_TYPE_OTHER; break; - + case GL2GL3.GL_DEBUG_CATEGORY_APPLICATION_AMD: - dbgSource = GL2GL3.GL_DEBUG_SOURCE_APPLICATION_ARB; - dbgType = GL2GL3.GL_DEBUG_TYPE_OTHER_ARB; + dbgSource = GL2GL3.GL_DEBUG_SOURCE_APPLICATION; + dbgType = GL2GL3.GL_DEBUG_TYPE_OTHER; break; - - + + // // other source / def type // - + case GL2GL3.GL_DEBUG_CATEGORY_DEPRECATION_AMD: - dbgSource = GL2GL3.GL_DEBUG_SOURCE_OTHER_ARB; - dbgType = GL2GL3.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB; + dbgSource = GL2GL3.GL_DEBUG_SOURCE_OTHER; + dbgType = GL2GL3.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR; break; - + case GL2GL3.GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD: - dbgSource = GL2GL3.GL_DEBUG_SOURCE_OTHER_ARB; - dbgType = GL2GL3.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB; + dbgSource = GL2GL3.GL_DEBUG_SOURCE_OTHER; + dbgType = GL2GL3.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR; break; - + case GL2GL3.GL_DEBUG_CATEGORY_PERFORMANCE_AMD: - dbgSource = GL2GL3.GL_DEBUG_SOURCE_OTHER_ARB; - dbgType = GL2GL3.GL_DEBUG_TYPE_PERFORMANCE_ARB; + dbgSource = GL2GL3.GL_DEBUG_SOURCE_OTHER; + dbgType = GL2GL3.GL_DEBUG_TYPE_PERFORMANCE; break; - - case GL2GL3.GL_DEBUG_CATEGORY_OTHER_AMD: + + case GL2GL3.GL_DEBUG_CATEGORY_OTHER_AMD: default: - dbgSource = GL2GL3.GL_DEBUG_SOURCE_OTHER_ARB; - dbgType = GL2GL3.GL_DEBUG_TYPE_OTHER_ARB; + dbgSource = GL2GL3.GL_DEBUG_SOURCE_OTHER; + dbgType = GL2GL3.GL_DEBUG_TYPE_OTHER; } - - return new GLDebugMessage(source, when, dbgSource, dbgType, dbgId, dbgSeverity, dbgMsg); + + return new GLDebugMessage(source, when, dbgSource, dbgType, dbgId, dbgSeverity, dbgMsg); } public static int translateARB2AMDCategory(int dbgSource, int dbgType) { switch (dbgSource) { - case GL2GL3.GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: - return GL2GL3.GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD; - - case GL2GL3.GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: + case GL2GL3.GL_DEBUG_SOURCE_WINDOW_SYSTEM: + return GL2GL3.GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD; + + case GL2GL3.GL_DEBUG_SOURCE_SHADER_COMPILER: return GL2GL3.GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD; - - case GL2GL3.GL_DEBUG_SOURCE_APPLICATION_ARB: + + case GL2GL3.GL_DEBUG_SOURCE_APPLICATION: return GL2GL3.GL_DEBUG_CATEGORY_APPLICATION_AMD; } - + switch(dbgType) { - case GL2GL3.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: + case GL2GL3.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return GL2GL3.GL_DEBUG_CATEGORY_DEPRECATION_AMD; - - case GL2GL3.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: + + case GL2GL3.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return GL2GL3.GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD; - - case GL2GL3.GL_DEBUG_TYPE_PERFORMANCE_ARB: + + case GL2GL3.GL_DEBUG_TYPE_PERFORMANCE: return GL2GL3.GL_DEBUG_CATEGORY_PERFORMANCE_AMD; } - - return GL2GL3.GL_DEBUG_CATEGORY_OTHER_AMD; + + return GL2GL3.GL_DEBUG_CATEGORY_OTHER_AMD; } - + public GLContext getSource() { return source; } @@ -158,7 +162,7 @@ public class GLDebugMessage { public long getWhen() { return when; } - + public int getDbgSource() { return dbgSource; } @@ -178,14 +182,14 @@ public class GLDebugMessage { public String getDbgMsg() { return dbgMsg; } - + public StringBuilder toString(StringBuilder sb) { - final String crtab = Platform.getNewline()+"\t"; + final String crtab = Platform.getNewline()+"\t"; if(null==sb) { sb = new StringBuilder(); - } + } sb.append("GLDebugEvent[ id "); - toHexString(sb, dbgId) + toHexString(sb, dbgId) .append(crtab).append("type ").append(getDbgTypeString(dbgType)) .append(crtab).append("severity ").append(getDbgSeverityString(dbgSeverity)) .append(crtab).append("source ").append(getDbgSourceString(dbgSource)) @@ -195,46 +199,47 @@ public class GLDebugMessage { sb.append(crtab).append("source ").append(source.getGLVersion()).append(" - hash 0x").append(Integer.toHexString(source.hashCode())); } sb.append("]"); - return sb; + return sb; } + @Override public String toString() { return toString(null).toString(); } - + public static String getDbgSourceString(int dbgSource) { switch(dbgSource) { - case GL2GL3.GL_DEBUG_SOURCE_API_ARB: return "GL API"; - case GL2GL3.GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: return "GLSL or extension compiler"; - case GL2GL3.GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: return "Native Windowing binding"; - case GL2GL3.GL_DEBUG_SOURCE_THIRD_PARTY_ARB: return "Third party"; - case GL2GL3.GL_DEBUG_SOURCE_APPLICATION_ARB: return "Application"; - case GL2GL3.GL_DEBUG_SOURCE_OTHER_ARB: return "generic"; + case GL2GL3.GL_DEBUG_SOURCE_API: return "GL API"; + case GL2GL3.GL_DEBUG_SOURCE_SHADER_COMPILER: return "GLSL or extension compiler"; + case GL2GL3.GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "Native Windowing binding"; + case GL2GL3.GL_DEBUG_SOURCE_THIRD_PARTY: return "Third party"; + case GL2GL3.GL_DEBUG_SOURCE_APPLICATION: return "Application"; + case GL2GL3.GL_DEBUG_SOURCE_OTHER: return "generic"; default: return "Unknown (" + toHexString(dbgSource) + ")"; } } - + public static String getDbgTypeString(int dbgType) { switch(dbgType) { - case GL2GL3.GL_DEBUG_TYPE_ERROR_ARB: return "Error"; - case GL2GL3.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: return "Warning: marked for deprecation"; - case GL2GL3.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: return "Warning: undefined behavior"; - case GL2GL3.GL_DEBUG_TYPE_PERFORMANCE_ARB: return "Warning: implementation dependent performance"; - case GL2GL3.GL_DEBUG_TYPE_PORTABILITY_ARB: return "Warning: vendor-specific extension use"; - case GL2GL3.GL_DEBUG_TYPE_OTHER_ARB: return "Warning: generic"; + case GL2GL3.GL_DEBUG_TYPE_ERROR: return "Error"; + case GL2GL3.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "Warning: marked for deprecation"; + case GL2GL3.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "Warning: undefined behavior"; + case GL2GL3.GL_DEBUG_TYPE_PERFORMANCE: return "Warning: implementation dependent performance"; + case GL2GL3.GL_DEBUG_TYPE_PORTABILITY: return "Warning: vendor-specific extension use"; + case GL2GL3.GL_DEBUG_TYPE_OTHER: return "Warning: generic"; default: return "Unknown (" + toHexString(dbgType) + ")"; } } - + public static String getDbgSeverityString(int dbgSeverity) { switch(dbgSeverity) { - case GL2GL3.GL_DEBUG_SEVERITY_HIGH_ARB: return "High: dangerous undefined behavior"; - case GL2GL3.GL_DEBUG_SEVERITY_MEDIUM_ARB: return "Medium: Severe performance/deprecation/other warnings"; - case GL2GL3.GL_DEBUG_SEVERITY_LOW_ARB: return "Low: Performance warnings (redundancy/undefined)"; + case GL2GL3.GL_DEBUG_SEVERITY_HIGH: return "High: dangerous undefined behavior"; + case GL2GL3.GL_DEBUG_SEVERITY_MEDIUM: return "Medium: Severe performance/deprecation/other warnings"; + case GL2GL3.GL_DEBUG_SEVERITY_LOW: return "Low: Performance warnings (redundancy/undefined)"; default: return "Unknown (" + toHexString(dbgSeverity) + ")"; } } - + public static StringBuilder toHexString(StringBuilder sb, int i) { if(null==sb) { sb = new StringBuilder(); @@ -243,6 +248,6 @@ public class GLDebugMessage { } public static String toHexString(int i) { return "0x"+Integer.toHexString(i); - } - + } + } diff --git a/src/jogl/classes/javax/media/opengl/GLDrawable.java b/src/jogl/classes/javax/media/opengl/GLDrawable.java index 2b86a04ba..3c354a240 100644 --- a/src/jogl/classes/javax/media/opengl/GLDrawable.java +++ b/src/jogl/classes/javax/media/opengl/GLDrawable.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -53,41 +53,44 @@ import javax.media.nativewindow.NativeSurface; public interface GLDrawable { /** * Creates a new context for drawing to this drawable that will - * optionally share display lists and other server-side OpenGL - * objects with the specified GLContext. <P> - * + * optionally share buffer objects, textures and other server-side OpenGL + * objects with the specified GLContext. + * <p> * 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="../../../overview-summary.html#SHARING">context sharing</a>. + * <a href="../../../spec-overview.html#SHARING">context sharing</a>. + * </p> */ public GLContext createContext(GLContext shareWith); /** - * Indicates to on-screen GLDrawable implementations whether the - * underlying window has been created and can be drawn into. End - * users do not need to call this method; it is not necessary to - * call <code>setRealized</code> on a GLCanvas, a GLJPanel, or a - * GLPbuffer, as these perform the appropriate calls on their - * underlying GLDrawables internally. - * - * <P> - * + * Indicates to GLDrawable implementations whether the + * underlying {@link NativeSurface surface} has been created and can be drawn into. + * <p> + * If realized, the {@link #getHandle() drawable handle} may become + * valid while it's {@link NativeSurface surface} is being {@link NativeSurface#lockSurface() locked}. + * </p> + * <p> + * End users do not need to call this method; it is not necessary to + * call <code>setRealized</code> on a {@link GLAutoDrawable} + * as these perform the appropriate calls on their underlying GLDrawables internally. + * </p> + * <p> * Developers implementing new OpenGL components for various window * toolkits need to call this method against GLDrawables obtained - * from the GLDrawableFactory via the {@link - * GLDrawableFactory#getGLDrawable - * GLDrawableFactory.getGLDrawable()} method. It must typically be + * from the GLDrawableFactory via the + * {@link GLDrawableFactory#createGLDrawable(NativeSurface)} method. + * It must typically be * called with an argument of <code>true</code> when the component * associated with the GLDrawable is realized and with an argument * of <code>false</code> just before the component is unrealized. * For the AWT, this means calling <code>setRealized(true)</code> in * the <code>addNotify</code> method and with an argument of * <code>false</code> in the <code>removeNotify</code> method. - * - * <P> - * + * </p> + * <p> * <code>GLDrawable</code> implementations should handle multiple * cycles of <code>setRealized(true)</code> / * <code>setRealized(false)</code> calls. Most, if not all, Java @@ -101,32 +104,40 @@ public interface GLDrawable { * <code>GLDrawable</code> to re-initialize and destroy any * associated resources as the component becomes realized and * unrealized, respectively. - * - * <P> - * - * With an argument of <code>true</code>, - * the minimum implementation shall call - * {@link NativeSurface#lockSurface() NativeSurface's lockSurface()} and if successfull: + * </p> + * <p> + * With an argument of <code>true</code>, + * the minimum implementation shall call + * {@link NativeSurface#lockSurface() NativeSurface's lockSurface()} and if successful: * <ul> - * <li> Update the {@link GLCapabilities}, which are associated with + * <li> Update the {@link GLCapabilities}, which are associated with * the attached {@link NativeSurface}'s {@link AbstractGraphicsConfiguration}.</li> * <li> Release the lock with {@link NativeSurface#unlockSurface() NativeSurface's unlockSurface()}.</li> * </ul><br> * This is important since {@link NativeSurface#lockSurface() NativeSurface's lockSurface()} * ensures resolving the window/surface handles, and the drawable's {@link GLCapabilities} * might have changed. - * - * <P> - * + * </p> + * <p> * Calling this method has no other effects. For example, if * <code>removeNotify</code> is called on a Canvas implementation * for which a GLDrawable has been created, it is also necessary to * destroy all OpenGL contexts associated with that GLDrawable. This * is not done automatically by the implementation. + * </p> + * @see #isRealized() + * @see #getHandle() + * @see NativeSurface#lockSurface() */ public void setRealized(boolean realized); - /** @return true if this drawable is realized, otherwise false */ + /** + * Returns <code>true</code> if this drawable is realized, otherwise <code>true</code>. + * <p> + * A drawable can be realized and unrealized via {@link #setRealized(boolean)}. + * </p> + * @see #setRealized(boolean) + */ public boolean isRealized(); /** Returns the current width of this GLDrawable. */ @@ -135,6 +146,20 @@ public interface GLDrawable { /** Returns the current height of this GLDrawable. */ public int getHeight(); + /** + * Returns <code>true</code> if the drawable is rendered in + * OpenGL's coordinate system, <i>origin at bottom left</i>. + * Otherwise returns <code>false</code>, i.e. <i>origin at top left</i>. + * <p> + * Default impl. is <code>true</code>, i.e. OpenGL coordinate system. + * </p> + * <p> + * Currently only MS-Windows bitmap offscreen drawable uses a non OpenGL orientation and hence returns <code>false</code>.<br/> + * This removes the need of a vertical flip when used in AWT or Windows applications. + * </p> + */ + public boolean isGLOriented(); + /** Swaps the front and back buffers of this drawable. For {@link GLAutoDrawable} implementations, when automatic buffer swapping is enabled (as is the default), this method is called @@ -146,10 +171,10 @@ public interface GLDrawable { 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> - This object shall be directly associated to the attached {@link NativeSurface}'s + 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. - @return A copy of the queried object. + @return The immutable queried instance. */ public GLCapabilitiesImmutable getChosenGLCapabilities(); @@ -159,23 +184,36 @@ public interface GLDrawable { public GLProfile getGLProfile(); /** - * Returns the underlying native surface which surface handle + * Returns the underlying native surface which surface handle * represents this OpenGL drawable's native resource. - * + * * @see #getHandle() */ public NativeSurface getNativeSurface(); - /** - * This is the GL/Windowing drawable handle.<br> - * It is usually the {@link javax.media.nativewindow.NativeSurface#getSurfaceHandle()}, - * ie the native surface handle of the underlying windowing toolkit.<br> - * However, on X11/GLX this reflects a GLXDrawable, which represents a GLXWindow, GLXPixmap, or GLXPbuffer.<br> - * On EGL, this represents the EGLSurface.<br> + /** + * Returns the GL drawable handle, + * guaranteed to be valid after {@link #setRealized(boolean) realization} + * <i>and</i> while it's {@link NativeSurface surface} is being {@link NativeSurface#lockSurface() locked}. + * <p> + * It is usually identical to the underlying windowing toolkit {@link NativeSurface surface}'s + * {@link javax.media.nativewindow.NativeSurface#getSurfaceHandle() handle} + * or an intermediate layer to suite GL, e.g. an EGL surface. + * </p> + * <p> + * On EGL it is represented by the EGLSurface.<br> + * On X11/GLX it is represented by either the Window XID, GLXPixmap, or GLXPbuffer.<br> + * On Windows it is represented by the HDC, which may change with each {@link NativeSurface#lockSurface()}.<br> + * </p> + * @see #setRealized(boolean) + * @see NativeSurface#lockSurface() + * @see NativeSurface#unlockSurface() */ public long getHandle(); + /** Return the {@link GLDrawableFactory} being used to create this instance. */ public GLDrawableFactory getFactory(); + @Override public String toString(); } diff --git a/src/jogl/classes/javax/media/opengl/GLDrawableFactory.java b/src/jogl/classes/javax/media/opengl/GLDrawableFactory.java index 1093685d6..9b823a6f8 100644 --- a/src/jogl/classes/javax/media/opengl/GLDrawableFactory.java +++ b/src/jogl/classes/javax/media/opengl/GLDrawableFactory.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,31 +29,32 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package javax.media.opengl; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.List; -import com.jogamp.common.JogampRuntimeException; - import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.opengl.GLAutoDrawableDelegate; +import com.jogamp.opengl.GLRendererQuirks; import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.AbstractGraphicsScreen; +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.ProxySurface; -import javax.media.opengl.GLProfile.ShutdownType; +import javax.media.nativewindow.UpstreamSurfaceHook; import jogamp.opengl.Debug; @@ -82,7 +83,7 @@ import jogamp.opengl.Debug; during the first repaint of the {@link javax.media.opengl.awt.GLCanvas} or {@link javax.media.opengl.awt.GLJPanel} if the capabilities can not be met.<br> {@link javax.media.opengl.GLPbuffer} are always - created immediately and their creation will fail with a + created immediately and their creation will fail with a {@link javax.media.opengl.GLException} if errors occur. </P> <P> The concrete GLDrawableFactory subclass instantiated by {@link @@ -92,23 +93,35 @@ import jogamp.opengl.Debug; */ public abstract class GLDrawableFactory { - static final String macosxFactoryClassNameCGL = "jogamp.opengl.macosx.cgl.MacOSXCGLDrawableFactory"; - static final String macosxFactoryClassNameAWTCGL = "jogamp.opengl.macosx.cgl.awt.MacOSXAWTCGLDrawableFactory"; - + 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; - protected static ArrayList<GLDrawableFactory> glDrawableFactories = new ArrayList<GLDrawableFactory>(); - - // Shutdown hook mechanism for the factory - private static boolean factoryShutdownHookRegistered = false; - private static Thread factoryShutdownHook = null; + private static ArrayList<GLDrawableFactory> glDrawableFactories = new ArrayList<GLDrawableFactory>(); /** * Instantiate singleton factories if available, EGLES1, EGLES2 and the OS native ones. */ - public static final void initSingleton() { + public static final void initSingleton() { if (!isInit) { // volatile: ok synchronized (GLDrawableFactory.class) { if (!isInit) { @@ -117,125 +130,133 @@ public abstract class GLDrawableFactory { } } } - } + } private static final void initSingletonImpl() { - registerFactoryShutdownHook(); - - final String nativeOSType = NativeWindowFactory.getNativeWindowType(true); + NativeWindowFactory.initSingleton(); + NativeWindowFactory.addCustomShutdownHook(false /* head */, new Runnable() { + @Override + public void run() { + shutdown0(); + } + }); + + final String nwt = NativeWindowFactory.getNativeWindowType(true); GLDrawableFactory tmp = null; String factoryClassName = Debug.getProperty("jogl.gldrawablefactory.class.name", true); ClassLoader cl = GLDrawableFactory.class.getClassLoader(); if (null == factoryClassName) { - if ( nativeOSType.equals(NativeWindowFactory.TYPE_X11) ) { + if ( nwt == NativeWindowFactory.TYPE_X11 ) { factoryClassName = "jogamp.opengl.x11.glx.X11GLXDrawableFactory"; - } else if ( nativeOSType.equals(NativeWindowFactory.TYPE_WINDOWS) ) { + } else if ( nwt == NativeWindowFactory.TYPE_WINDOWS ) { factoryClassName = "jogamp.opengl.windows.wgl.WindowsWGLDrawableFactory"; - } else if ( nativeOSType.equals(NativeWindowFactory.TYPE_MACOSX) ) { - if(ReflectionUtil.isClassAvailable(macosxFactoryClassNameAWTCGL, cl)) { - factoryClassName = macosxFactoryClassNameAWTCGL; - } else { - factoryClassName = macosxFactoryClassNameCGL; - } + } else if ( nwt == NativeWindowFactory.TYPE_MACOSX ) { + factoryClassName = "jogamp.opengl.macosx.cgl.MacOSXCGLDrawableFactory"; } else { // may use egl*Factory .. - if (GLProfile.DEBUG) { - System.err.println("GLDrawableFactory.static - No native OS Factory for: "+nativeOSType+"; May use EGLDrawableFactory, if available." ); + if (DEBUG || GLProfile.DEBUG) { + System.err.println("GLDrawableFactory.static - No native Windowing Factory for: "+nwt+"; May use EGLDrawableFactory, if available." ); } } } if (null != factoryClassName) { - if (GLProfile.DEBUG) { - System.err.println("GLDrawableFactory.static - Native OS Factory for: "+nativeOSType+": "+factoryClassName); + if (DEBUG || GLProfile.DEBUG) { + System.err.println("GLDrawableFactory.static - Native OS Factory for: "+nwt+": "+factoryClassName); } try { tmp = (GLDrawableFactory) ReflectionUtil.createInstance(factoryClassName, cl); - } catch (JogampRuntimeException jre) { - if (GLProfile.DEBUG) { - System.err.println("Info: GLDrawableFactory.static - Native Platform: "+nativeOSType+" - not available: "+factoryClassName); + } catch (Exception jre) { + if (DEBUG || GLProfile.DEBUG) { + System.err.println("Info: GLDrawableFactory.static - Native Platform: "+nwt+" - not available: "+factoryClassName); jre.printStackTrace(); } } } - nativeOSFactory = tmp; - + if(null != tmp && tmp.isComplete()) { + nativeOSFactory = tmp; + } tmp = null; - try { - tmp = (GLDrawableFactory) ReflectionUtil.createInstance("jogamp.opengl.egl.EGLDrawableFactory", cl); - } catch (JogampRuntimeException jre) { - if (GLProfile.DEBUG) { - System.err.println("Info: GLDrawableFactory.static - EGLDrawableFactory - not available"); - jre.printStackTrace(); + + if(!disableOpenGLES) { + try { + tmp = (GLDrawableFactory) ReflectionUtil.createInstance("jogamp.opengl.egl.EGLDrawableFactory", cl); + } catch (Exception jre) { + if (DEBUG || GLProfile.DEBUG) { + System.err.println("Info: GLDrawableFactory.static - EGLDrawableFactory - not available"); + jre.printStackTrace(); + } } + if(null != tmp && tmp.isComplete()) { + eglFactory = tmp; + } + } else if( DEBUG || GLProfile.DEBUG ) { + System.err.println("Info: GLDrawableFactory.static - EGLDrawableFactory - disabled!"); } - eglFactory = tmp; } - protected static void shutdown(ShutdownType shutdownType) { + protected static void shutdown() { if (isInit) { // volatile: ok synchronized (GLDrawableFactory.class) { if (isInit) { isInit=false; - unregisterFactoryShutdownHook(); - shutdownImpl(shutdownType); + shutdown0(); } } } } - private static void shutdownImpl(ShutdownType shutdownType) { + + private static void shutdown0() { + // Following code will _always_ remain in shutdown hook + // due to special semantics of native utils, i.e. X11Utils. + // The latter requires shutdown at JVM-Shutdown only. synchronized(glDrawableFactories) { - for(int i=0; i<glDrawableFactories.size(); i++) { - glDrawableFactories.get(i).destroy(shutdownType); + final int gldfCount = glDrawableFactories.size(); + if( DEBUG ) { + System.err.println("GLDrawableFactory.shutdownAll "+gldfCount+" instances, on thread "+getThreadName()); + } + for(int i=0; i<gldfCount; i++) { + final GLDrawableFactory gldf = glDrawableFactories.get(i); + if( DEBUG ) { + System.err.println("GLDrawableFactory.shutdownAll["+(i+1)+"/"+gldfCount+"]: "+gldf.getClass().getName()); + } + try { + gldf.resetDisplayGamma(); + gldf.shutdownImpl(); + } catch (Throwable t) { + System.err.println("GLDrawableFactory.shutdownImpl: Catched "+t.getClass().getName()+" during factory shutdown #"+(i+1)+"/"+gldfCount+" "+gldf.getClass().getName()); + if( DEBUG ) { + t.printStackTrace(); + } + } } glDrawableFactories.clear(); - - // both were members of glDrawableFactories and are shutdown already + + // both were members of glDrawableFactories and are shutdown already nativeOSFactory = null; eglFactory = null; } - } - - private static synchronized void registerFactoryShutdownHook() { - if (factoryShutdownHookRegistered) { - return; - } - factoryShutdownHook = new Thread(new Runnable() { - public void run() { - GLDrawableFactory.shutdownImpl(GLProfile.ShutdownType.COMPLETE); - } - }); - AccessController.doPrivileged(new PrivilegedAction<Object>() { - public Object run() { - Runtime.getRuntime().addShutdownHook(factoryShutdownHook); - return null; - } - }); - factoryShutdownHookRegistered = true; - } - - private static synchronized void unregisterFactoryShutdownHook() { - if (!factoryShutdownHookRegistered) { - return; + GLContext.shutdown(); + if( DEBUG ) { + System.err.println("GLDrawableFactory.shutdownAll.X on thread "+getThreadName()); } - AccessController.doPrivileged(new PrivilegedAction<Object>() { - public Object run() { - Runtime.getRuntime().removeShutdownHook(factoryShutdownHook); - return null; - } - }); - factoryShutdownHookRegistered = false; } - protected GLDrawableFactory() { synchronized(glDrawableFactories) { glDrawableFactories.add(this); } } + protected static String getThreadName() { return Thread.currentThread().getName(); } + + /** Returns true if this factory is complete, i.e. ready to be used. Otherwise return false. */ + protected abstract boolean isComplete(); + protected void enterThreadCriticalZone() {}; protected void leaveThreadCriticalZone() {}; - protected abstract void destroy(ShutdownType shutdownType); + protected abstract void shutdownImpl(); + + public abstract void resetDisplayGamma(); /** * Retrieve the default <code>device</code> {@link AbstractGraphicsDevice#getConnection() connection}, @@ -243,7 +264,7 @@ public abstract class GLDrawableFactory { * The implementation must return a non <code>null</code> default device, which must not be opened, ie. it's native handle is <code>null</code>. * <p> * This method shall return the default device if available - * even if the GLDrawableFactory is not functional and hence not compatible. + * even if the GLDrawableFactory is not functional and hence not compatible. * The latter situation may happen because no native OpenGL implementation is available for the specific implementation. * </p> * @return the default shared device for this factory, eg. :0.0 on X11 desktop. @@ -252,10 +273,10 @@ public abstract class GLDrawableFactory { public abstract AbstractGraphicsDevice getDefaultDevice(); /** - * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. * @return true if the device is compatible with this factory, ie. if it can be used for GLDrawable creation. Otherwise false. * This implies validation whether the implementation is functional. - * + * * @see #getDefaultDevice() */ public abstract boolean getIsDeviceCompatible(AbstractGraphicsDevice device); @@ -264,14 +285,14 @@ public abstract class GLDrawableFactory { if(null==device) { device = getDefaultDevice(); if(null==device) { - throw new InternalError("no default device"); + throw new InternalError("no default device available"); } if (GLProfile.DEBUG) { System.err.println("Info: "+getClass().getSimpleName()+".validateDevice: using default device : "+device); } } - - // Always validate the device, + + // Always validate the device, // since even the default device may not be used by this factory. if( !getIsDeviceCompatible(device) ) { if (GLProfile.DEBUG) { @@ -283,31 +304,70 @@ public abstract class GLDrawableFactory { } /** - * Validate and start the shared resource runner thread if necessary and + * Validate and start the shared resource runner thread if necessary and * if the implementation uses it. - * + * * @return the shared resource runner thread, if implementation uses it. */ protected abstract Thread getSharedResourceThread(); - + /** * Create the shared resource used internally as a reference for capabilities etc. * <p> - * Returns true if a shared resource could be created + * Returns true if a shared resource could be created * for the <code>device</code> {@link AbstractGraphicsDevice#getConnection()}.<br> * This does not imply a shared resource is mapped (ie. made persistent), but is available in general<br>. * </p> * - * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. - * @return true if a shared resource could been created, otherwise false. + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * @return true if a shared resource could been created, otherwise false. + */ + protected final boolean createSharedResource(AbstractGraphicsDevice device) { + return createSharedResourceImpl(device); + } + protected abstract boolean createSharedResourceImpl(AbstractGraphicsDevice device); + + /** + * Returns true if the <code>quirk</code> exist in the shared resource's context {@link GLRendererQuirks}. + * <p> + * Convenience method for: + * <pre> + final GLRendererQuirks glrq = factory.getRendererQuirks(device); + return null != glrq ? glrq.exist(quirk) : false; + * </pre> + * </p> + * + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * @param quirk the quirk to be tested, e.g. {@link GLRendererQuirks#NoDoubleBufferedPBuffer}. + * @throws IllegalArgumentException if the quirk is out of range + * @see #getRendererQuirks(AbstractGraphicsDevice) + * @see GLRendererQuirks + */ + public final boolean hasRendererQuirk(AbstractGraphicsDevice device, int quirk) { + final GLRendererQuirks glrq = getRendererQuirks(device); + return null != glrq ? glrq.exist(quirk) : false; + } + + /** + * Returns the shared resource's context {@link GLRendererQuirks}. + * <p> + * Implementation calls {@link GLContext#getRendererQuirks()} on the shared resource context. + * </p> + * <p> + * In case no shared device exist yet or the implementation doesn't support tracking quirks, + * the result is always <code>null</code>. + * </p> + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * @see GLContext#getRendererQuirks() + * @see GLRendererQuirks */ - protected abstract boolean createSharedResource(AbstractGraphicsDevice device); - + public abstract GLRendererQuirks getRendererQuirks(AbstractGraphicsDevice device); + /** * Returns the sole GLDrawableFactory instance for the desktop (X11, WGL, ..) if exist or null */ public static GLDrawableFactory getDesktopFactory() { - GLProfile.initSingleton(); + GLProfile.initSingleton(); return nativeOSFactory; } @@ -315,14 +375,14 @@ public abstract class GLDrawableFactory { * Returns the sole GLDrawableFactory instance for EGL if exist or null */ public static GLDrawableFactory getEGLFactory() { - GLProfile.initSingleton(); + GLProfile.initSingleton(); return eglFactory; } - /** - * Returns the sole GLDrawableFactory instance. - * - * @param glProfile GLProfile to determine the factory type, ie EGLDrawableFactory, + /** + * Returns the sole GLDrawableFactory instance. + * + * @param glProfile GLProfile to determine the factory type, ie EGLDrawableFactory, * or one of the native GLDrawableFactory's, ie X11/GLX, Windows/WGL or MacOSX/CGL. */ public static GLDrawableFactory getFactory(GLProfile glProfile) throws GLException { @@ -331,7 +391,7 @@ public abstract class GLDrawableFactory { protected static GLDrawableFactory getFactoryImpl(String glProfileImplName) throws GLException { if ( GLProfile.usesNativeGLES(glProfileImplName) ) { - if(null!=eglFactory) { + if(null!=eglFactory) { return eglFactory; } } else if(null!=nativeOSFactory) { @@ -356,13 +416,13 @@ public abstract class GLDrawableFactory { * The chosen GLProfile statement in the result may not refer to the maximum available profile * due to implementation constraints, ie using the shared resource. * - * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. * @return A list of {@link javax.media.opengl.GLCapabilitiesImmutable}'s, maybe empty if none is available. */ public final List<GLCapabilitiesImmutable> getAvailableCapabilities(AbstractGraphicsDevice device) { device = validateDevice(device); if(null!=device) { - return getAvailableCapabilitiesImpl(device); + return getAvailableCapabilitiesImpl(device); } return null; } @@ -372,117 +432,311 @@ public abstract class GLDrawableFactory { // Methods to create high-level objects /** - * Returns a GLDrawable according to it's chosen Capabilities,<br> + * Returns an {@link GLDrawable#isRealized() unrealized} GLDrawable according to it's chosen {@link GLCapabilitiesImmutable},<br> * which determines pixel format, on- and offscreen incl. PBuffer type. * <p> - * The native platform's chosen Capabilties are referenced within the target - * NativeSurface's AbstractGraphicsConfiguration.<p> - * - * In case target's {@link javax.media.nativewindow.Capabilities#isOnscreen()} is true,<br> - * an onscreen GLDrawable will be realized. + * The chosen {@link GLCapabilitiesImmutable} are referenced within the target + * {@link NativeSurface}'s {@link AbstractGraphicsConfiguration}.<p> + * </p> + * <p> + * An onscreen GLDrawable is created if {@link CapabilitiesImmutable#isOnscreen() caps.isOnscreen()} is true. + * </p> * <p> - * In case target's {@link javax.media.nativewindow.Capabilities#isOnscreen()} is false,<br> - * either a Pbuffer drawable is created if target's {@link javax.media.opengl.GLCapabilities#isPBuffer()} is true,<br> - * or a simple pixmap/bitmap drawable is created. The latter is unlikely to be hardware accelerated.<br> + * A FBO drawable is created if both {@link GLCapabilitiesImmutable#isFBO() caps.isFBO()} + * and {@link GLContext#isFBOAvailable(AbstractGraphicsDevice, GLProfile) canCreateFBO(device, caps.getGLProfile())} is true. + * </p> * <p> + * A Pbuffer drawable is created if both {@link GLCapabilitiesImmutable#isPBuffer() caps.isPBuffer()} + * and {@link #canCreateGLPbuffer(AbstractGraphicsDevice, GLProfile) canCreateGLPbuffer(device)} is true. + * </p> + * <p> + * If not onscreen and neither FBO nor Pbuffer is available, + * a simple pixmap/bitmap drawable/surface is created, which is unlikely to be hardware accelerated. + * </p> * * @throws IllegalArgumentException if the passed target is null * @throws GLException if any window system-specific errors caused * the creation of the GLDrawable to fail. * - * @see javax.media.nativewindow.GraphicsConfigurationFactory#chooseGraphicsConfiguration(Capabilities, CapabilitiesChooser, AbstractGraphicsScreen) + * @see #canCreateGLPbuffer(AbstractGraphicsDevice, GLProfile) + * @see GLContext#isFBOAvailable(AbstractGraphicsDevice, GLProfile) + * @see javax.media.opengl.GLCapabilities#isOnscreen() + * @see javax.media.opengl.GLCapabilities#isFBO() + * @see javax.media.opengl.GLCapabilities#isPBuffer() + * @see GraphicsConfigurationFactory#chooseGraphicsConfiguration(CapabilitiesImmutable, CapabilitiesImmutable, CapabilitiesChooser, AbstractGraphicsScreen, int) */ public abstract GLDrawable createGLDrawable(NativeSurface target) throws IllegalArgumentException, GLException; /** - * Creates a Offscreen GLDrawable incl it's offscreen {@link javax.media.nativewindow.NativeSurface} with the given capabilites and dimensions. + * Creates a {@link GLDrawable#isRealized() realized} {@link GLOffscreenAutoDrawable} + * incl it's offscreen {@link NativeSurface} with the given capabilites and dimensions. + * <p> + * The {@link GLOffscreenAutoDrawable}'s {@link GLDrawable} is {@link GLDrawable#isRealized() realized} + * and it's {@link GLContext} assigned but not yet made current. + * </p> + * <p> + * In case the passed {@link GLCapabilitiesImmutable} contains default values, i.e. + * {@link GLCapabilitiesImmutable#isOnscreen() caps.isOnscreen()} <code> == true</code>, + * it is auto-configured. Auto configuration will set {@link GLCapabilitiesImmutable caps} to offscreen + * and FBO <i>or</i> Pbuffer, whichever is available in that order. + * </p> + * <p> + * A FBO based auto drawable, {@link GLOffscreenAutoDrawable.FBO}, is created if both {@link GLCapabilitiesImmutable#isFBO() caps.isFBO()} + * and {@link GLContext#isFBOAvailable(AbstractGraphicsDevice, GLProfile) canCreateFBO(device, caps.getGLProfile())} is true. + * </p> + * <p> + * A Pbuffer based auto drawable is created if both {@link GLCapabilitiesImmutable#isPBuffer() caps.isPBuffer()} + * and {@link #canCreateGLPbuffer(AbstractGraphicsDevice, GLProfile) canCreateGLPbuffer(device)} is true. + * </p> + * <p> + * If neither FBO nor Pbuffer is available, + * a simple pixmap/bitmap auto drawable is created, which is unlikely to be hardware accelerated. + * </p> * <p> - * A Pbuffer drawable/surface is created if both {@link javax.media.opengl.GLCapabilities#isPBuffer() caps.isPBuffer()} - * and {@link #canCreateGLPbuffer(javax.media.nativewindow.AbstractGraphicsDevice) canCreateGLPbuffer(device)} is true.<br> - * Otherwise a simple pixmap/bitmap drawable/surface is created, which is unlikely to be hardware accelerated.<br> + * The resulting {@link GLOffscreenAutoDrawable} has it's own independent device instance using <code>device</code> details. * </p> * - * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared device to be used, may be <code>null</code> for the platform's default device. + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared device to be used, may be <code>null</code> for the platform's default device. * @param caps the requested GLCapabilties * @param chooser the custom chooser, may be null for default * @param width the requested offscreen width * @param height the requested offscreen height + * @return the created and initialized offscreen {@link GLOffscreenAutoDrawable} instance * - * @return the created offscreen GLDrawable + * @throws GLException if any window system-specific errors caused + * the creation of the Offscreen to fail. + * + * @see #createOffscreenDrawable(AbstractGraphicsDevice, GLCapabilitiesImmutable, GLCapabilitiesChooser, int, int) + * @deprecated Use {@link #createOffscreenAutoDrawable(AbstractGraphicsDevice, GLCapabilitiesImmutable, GLCapabilitiesChooser, int, int) + */ + public abstract GLOffscreenAutoDrawable createOffscreenAutoDrawable(AbstractGraphicsDevice device, + GLCapabilitiesImmutable caps, + GLCapabilitiesChooser chooser, + int width, int height, + GLContext shareWith) throws GLException; + + /** + * Creates a {@link GLDrawable#isRealized() realized} {@link GLOffscreenAutoDrawable} + * incl it's offscreen {@link NativeSurface} with the given capabilites and dimensions. + * <p> + * The {@link GLOffscreenAutoDrawable}'s {@link GLDrawable} is {@link GLDrawable#isRealized() realized} + * <i>without</i> an assigned {@link GLContext}, hence not initialized completely.<br> + * The {@link GLContext} can be assigned later manually via {@link GLAutoDrawable#setContext(GLContext, boolean) setContext(ctx)} + * <i>or</i> it will be created <i>lazily</i> at the 1st {@link GLAutoDrawable#display() display()} method call.<br> + * <i>Lazy</i> {@link GLContext} creation will take a shared {@link GLContext} into account + * which has been set {@link GLOffscreenAutoDrawable#setSharedContext(GLContext) directly} + * or {@link GLOffscreenAutoDrawable#setSharedAutoDrawable(GLAutoDrawable) via another GLAutoDrawable}. + * </p> + * <p> + * In case the passed {@link GLCapabilitiesImmutable} contains default values, i.e. + * {@link GLCapabilitiesImmutable#isOnscreen() caps.isOnscreen()} <code> == true</code>, + * it is auto-configured. Auto configuration will set {@link GLCapabilitiesImmutable caps} to offscreen + * and FBO <i>or</i> Pbuffer, whichever is available in that order. + * </p> + * <p> + * A FBO based auto drawable, {@link GLOffscreenAutoDrawable.FBO}, is created if both {@link GLCapabilitiesImmutable#isFBO() caps.isFBO()} + * and {@link GLContext#isFBOAvailable(AbstractGraphicsDevice, GLProfile) canCreateFBO(device, caps.getGLProfile())} is true. + * </p> + * <p> + * A Pbuffer based auto drawable is created if both {@link GLCapabilitiesImmutable#isPBuffer() caps.isPBuffer()} + * and {@link #canCreateGLPbuffer(AbstractGraphicsDevice, GLProfile) canCreateGLPbuffer(device)} is true. + * </p> + * <p> + * If neither FBO nor Pbuffer is available, + * a simple pixmap/bitmap auto drawable is created, which is unlikely to be hardware accelerated. + * </p> + * <p> + * The resulting {@link GLOffscreenAutoDrawable} has it's own independent device instance using <code>device</code> details. + * </p> + * + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared device to be used, may be <code>null</code> for the platform's default device. + * @param caps the requested GLCapabilties + * @param chooser the custom chooser, may be null for default + * @param width the requested offscreen width + * @param height the requested offscreen height + * @return the created and realized offscreen {@link GLOffscreenAutoDrawable} instance * * @throws GLException if any window system-specific errors caused * the creation of the Offscreen to fail. + * + * @see #createOffscreenDrawable(AbstractGraphicsDevice, GLCapabilitiesImmutable, GLCapabilitiesChooser, int, int) */ - public abstract GLDrawable createOffscreenDrawable(AbstractGraphicsDevice device, - GLCapabilitiesImmutable capabilities, - GLCapabilitiesChooser chooser, - int width, int height) - throws GLException; + public abstract GLOffscreenAutoDrawable createOffscreenAutoDrawable(AbstractGraphicsDevice device, + GLCapabilitiesImmutable caps, + GLCapabilitiesChooser chooser, + int width, int height) throws GLException; + + /** + * Creates a {@link GLDrawable#isRealized() realized} <i>dummy</i> {@link GLAutoDrawable} + * incl it's <i>dummy, invisible</i> {@link NativeSurface} + * as created with {@link #createDummyDrawable(AbstractGraphicsDevice, boolean, GLCapabilitiesImmutable, GLCapabilitiesChooser)}. + * <p> + * The <i>dummy</i> {@link GLAutoDrawable}'s {@link GLDrawable} is {@link GLDrawable#isRealized() realized} + * <i>without</i> an assigned {@link GLContext}, hence not initialized completely.<br> + * The {@link GLContext} can be assigned later manually via {@link GLAutoDrawable#setContext(GLContext, boolean) setContext(ctx)} + * <i>or</i> it will be created <i>lazily</i> at the 1st {@link GLAutoDrawable#display() display()} method call.<br> + * <i>Lazy</i> {@link GLContext} creation will take a shared {@link GLContext} into account + * which has been set {@link GLOffscreenAutoDrawable#setSharedContext(GLContext) directly} + * or {@link GLOffscreenAutoDrawable#setSharedAutoDrawable(GLAutoDrawable) via another GLAutoDrawable}. + * </p> + * + * @param deviceReq which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared device to be used, may be <code>null</code> for the platform's default device. + * @param createNewDevice if <code>true</code> a new independent device instance is created from the <code>deviceReq</code>, otherwise <code>deviceReq</code> is used as-is and must be valid! + * @param capsRequested the desired {@link GLCapabilitiesImmutable}, incl. it's {@link GLProfile}. + * For shared context, same {@link GLCapabilitiesImmutable#getVisualID(javax.media.nativewindow.VisualIDHolder.VIDType)} + * across shared drawables will yield best compatibility. + * @param chooser the custom chooser, may be null for default + * @return the created and realized <i>dummy</i> {@link GLAutoDrawable} instance + * + * @see #createDummyDrawable(AbstractGraphicsDevice, boolean, GLCapabilitiesImmutable, GLCapabilitiesChooser) + */ + public abstract GLAutoDrawable createDummyAutoDrawable(AbstractGraphicsDevice deviceReq, boolean createNewDevice, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser); /** - * Creates an offscreen NativeSurface.<br> - * A Pbuffer surface is created if both {@link javax.media.opengl.GLCapabilities#isPBuffer() caps.isPBuffer()} - * and {@link #canCreateGLPbuffer(javax.media.nativewindow.AbstractGraphicsDevice) canCreateGLPbuffer(device)} is true.<br> - * Otherwise a simple pixmap/bitmap surface is created. The latter is unlikely to be hardware accelerated.<br> + * Creates an {@link GLDrawable#isRealized() unrealized} offscreen {@link GLDrawable} + * incl it's offscreen {@link NativeSurface} with the given capabilites and dimensions. + * <p> + * In case the passed {@link GLCapabilitiesImmutable} contains default values, i.e. + * {@link GLCapabilitiesImmutable#isOnscreen() caps.isOnscreen()} <code> == true</code>, + * it is auto-configured. The latter will set offscreen and also FBO <i>or</i> Pbuffer, whichever is available in that order. + * </p> + * <p> + * A resizeable FBO drawable, {@link GLFBODrawable.Resizeable}, is created if both {@link GLCapabilitiesImmutable#isFBO() caps.isFBO()} + * and {@link GLContext#isFBOAvailable(AbstractGraphicsDevice, GLProfile) canCreateFBO(device, caps.getGLProfile())} is true. + * </p> + * <p> + * A Pbuffer drawable is created if both {@link GLCapabilitiesImmutable#isPBuffer() caps.isPBuffer()} + * and {@link #canCreateGLPbuffer(AbstractGraphicsDevice, GLProfile) canCreateGLPbuffer(device)} is true. + * </p> + * <p> + * If neither FBO nor Pbuffer is available, + * a simple pixmap/bitmap drawable is created, which is unlikely to be hardware accelerated. + * </p> + * <p> + * The resulting {@link GLDrawable} has it's own independent device instance using <code>device</code> details. + * </p> * - * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared device to be used, may be <code>null</code> for the platform's default device. * @param caps the requested GLCapabilties * @param chooser the custom chooser, may be null for default * @param width the requested offscreen width * @param height the requested offscreen height - * @return the created offscreen native surface + * + * @return the created unrealized offscreen {@link GLDrawable} * * @throws GLException if any window system-specific errors caused - * the creation of the GLDrawable to fail. + * the creation of the Offscreen to fail. + * + * @see #createOffscreenAutoDrawable(AbstractGraphicsDevice, GLCapabilitiesImmutable, GLCapabilitiesChooser, int, int, GLContext) + */ + public abstract GLDrawable createOffscreenDrawable(AbstractGraphicsDevice device, + GLCapabilitiesImmutable caps, + GLCapabilitiesChooser chooser, + int width, int height) throws GLException; + + /** + * Creates an {@link GLDrawable#isRealized() unrealized} dummy {@link GLDrawable}. + * A dummy drawable is not visible on screen and will not be used to render directly to, it maybe on- or offscreen. + * <p> + * It is used to allow the creation of a {@link GLContext} to query information. + * It also allows creation of framebuffer objects which are used for rendering or creating a shared GLContext w/o actually rendering to this dummy drawable's framebuffer. + * </p> + * @param deviceReq which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared device to be used, may be <code>null</code> for the platform's default device. + * @param createNewDevice if <code>true</code> a new independent device instance is created from the <code>deviceReq</code>, otherwise <code>deviceReq</code> is used as-is and must be valid! + * @param capsRequested the desired {@link GLCapabilitiesImmutable}, incl. it's {@link GLProfile}. + * For shared context, same {@link GLCapabilitiesImmutable#getVisualID(javax.media.nativewindow.VisualIDHolder.VIDType) visual ID} + * or {@link GLCapabilitiesImmutable caps} + * across shared drawables will yield best compatibility. + * @param chooser the custom chooser, may be null for default + * @return the created unrealized dummy {@link GLDrawable} */ - public abstract NativeSurface createOffscreenSurface(AbstractGraphicsDevice device, - GLCapabilitiesImmutable caps, - GLCapabilitiesChooser chooser, - int width, int height); + public abstract GLDrawable createDummyDrawable(AbstractGraphicsDevice deviceReq, boolean createNewDevice, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser); /** - * Highly experimental API entry, allowing developer of new windowing system bindings - * to leverage the native window handle to produce a NativeSurface implementation (ProxySurface), having the required GLCapabilities.<br> - * Such surface can be used to instantiate a GLDrawable and hence test your new binding w/o the - * costs of providing a full set of abstraction like the AWT GLCanvas or even the native NEWT bindings. - * - * @param device the platform's target device, shall not be <code>null</code> + * Creates a proxy {@link NativeSurface} w/ defined surface handle, + * i.e. a {@link jogamp.nativewindow.WrappedSurface} or {@link jogamp.nativewindow.windows.GDISurface} instance. + * <p> + * It's {@link AbstractGraphicsConfiguration} is properly set according to the given + * <code>windowHandle</code>'s native visualID if set or the given {@link GLCapabilitiesImmutable}. + * </p> + * <p> + * Lifecycle (creation and destruction) of the given surface handle shall be handled by the caller + * via {@link ProxySurface#createNotify()} and {@link ProxySurface#destroyNotify()}. + * </p> + * <p> + * Such surface can be used to instantiate a GLDrawable. With the help of {@link GLAutoDrawableDelegate} + * you will be able to implement a new native windowing system binding almost on-the-fly, + * see {@link com.jogamp.opengl.swt.GLCanvas}. + * </p> + * <p> + * The resulting {@link GLOffscreenAutoDrawable} has it's own independent device instance using <code>device</code> details + * which may be blocking depending on platform and windowing-toolkit requirements. + * </p> + * + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * Caller has to ensure it is compatible w/ the given <code>windowHandle</code> + * @param screenIdx matching screen index of given <code>windowHandle</code> * @param windowHandle the native window handle * @param caps the requested GLCapabilties * @param chooser the custom chooser, may be null for default - * @return The proxy surface wrapping the windowHandle on the device + * @param upstream optional {@link UpstreamSurfaceHook} allowing control of the {@link ProxySurface}'s lifecycle and data it presents. + * @return the created {@link ProxySurface} instance w/ defined surface handle. + */ + public abstract ProxySurface createProxySurface(AbstractGraphicsDevice device, + int screenIdx, + long windowHandle, + GLCapabilitiesImmutable caps, GLCapabilitiesChooser chooser, UpstreamSurfaceHook upstream); + + /** + * Returns true if it is possible to create an <i>framebuffer object</i> (FBO). + * <p> + * FBO feature is implemented in OpenGL, hence it is {@link GLProfile} dependent. + * </p> + * <p> + * FBO support is queried as described in {@link GLContext#hasBasicFBOSupport()}. + * </p> + * + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * @param glp {@link GLProfile} to check for FBO capabilities + * @see GLContext#hasBasicFBOSupport() */ - public abstract ProxySurface createProxySurface(AbstractGraphicsDevice device, - long windowHandle, - GLCapabilitiesImmutable caps, - GLCapabilitiesChooser chooser); + public abstract boolean canCreateFBO(AbstractGraphicsDevice device, GLProfile glp); /** - * Returns true if it is possible to create a GLPbuffer. Some older - * graphics cards do not have this capability. + * Returns true if it is possible to create an <i>pbuffer surface</i>. + * <p> + * Some older graphics cards do not have this capability, + * as well as some new GL implementation, i.e. OpenGL 3 core on OSX. + * </p> * - * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * @param glp {@link GLProfile} to check for FBO capabilities */ - public abstract boolean canCreateGLPbuffer(AbstractGraphicsDevice device); + public abstract boolean canCreateGLPbuffer(AbstractGraphicsDevice device, GLProfile glp); /** - * Creates a GLPbuffer with the given capabilites and dimensions. <P> + * Creates a GLPbuffer {@link GLAutoDrawable} with the given capabilites and dimensions. + * <p> + * The GLPbuffer drawable is realized and initialized eagerly. + * </p> * - * See the note in the overview documentation on - * <a href="../../../overview-summary.html#SHARING">context sharing</a>. + * See the note in the overview documentation in {@link GLSharedContextSetter} and on + * <a href="../../../spec-overview.html#SHARING">context sharing</a>. * - * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. * @param capabilities the requested capabilities * @param chooser the custom chooser, may be null for default * @param initialWidth initial width of pbuffer * @param initialHeight initial height of pbuffer * @param shareWith a shared GLContext this GLPbuffer shall use * - * @return the new {@link GLPbuffer} specific {@link GLAutoDrawable} + * @return the created and initialized {@link GLPbuffer} instance * * @throws GLException if any window system-specific errors caused * the creation of the GLPbuffer to fail. + * + * @deprecated {@link GLPbuffer} is deprecated, use {@link #createOffscreenAutoDrawable(AbstractGraphicsDevice, GLCapabilitiesImmutable, GLCapabilitiesChooser, int, int, GLContext)} */ public abstract GLPbuffer createGLPbuffer(AbstractGraphicsDevice device, GLCapabilitiesImmutable capabilities, @@ -492,6 +746,7 @@ public abstract class GLDrawableFactory { GLContext shareWith) throws GLException; + //---------------------------------------------------------------------- // Methods for interacting with third-party OpenGL libraries @@ -524,7 +779,7 @@ public abstract class GLDrawableFactory { * Returns true if it is possible to create an external GLDrawable * object via {@link #createExternalGLDrawable}. * - * @param device which {@link javax.media.nativewindow.AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. + * @param device which {@link AbstractGraphicsDevice#getConnection() connection} denotes the shared the target device, may be <code>null</code> for the platform's default device. */ public abstract boolean canCreateExternalGLDrawable(AbstractGraphicsDevice device); diff --git a/src/jogl/classes/javax/media/opengl/GLEventListener.java b/src/jogl/classes/javax/media/opengl/GLEventListener.java index 15fae4a39..c8c3440b5 100644 --- a/src/jogl/classes/javax/media/opengl/GLEventListener.java +++ b/src/jogl/classes/javax/media/opengl/GLEventListener.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -49,7 +49,7 @@ import java.util.EventListener; public interface GLEventListener extends EventListener { /** Called by the drawable immediately after the OpenGL context is initialized. Can be used to perform one-time OpenGL - initialization per GLContext, such as setup of lights and display lists.<p> + initialization per GLContext, such as setup of lights and display lists.<p> Note that this method may be called more than once if the underlying OpenGL context for the GLAutoDrawable is destroyed and @@ -57,7 +57,7 @@ public interface GLEventListener extends EventListener { hierarchy and later added again. */ public void init(GLAutoDrawable drawable); - + /** Notifies the listener to perform the release of all OpenGL resources per GLContext, such as memory buffers and GLSL programs.<P> @@ -68,11 +68,11 @@ public interface GLEventListener extends EventListener { Note that this event does not imply the end of life of the application. It could be produced with a followup call to {@link #init(GLAutoDrawable)} - in case the GLContext has been recreated, + in case the GLContext has been recreated, e.g. due to a pixel configuration change in a multihead environment. */ public void dispose(GLAutoDrawable drawable); - + /** Called by the drawable to initiate OpenGL rendering by the client. After all GLEventListeners have been notified of a display event, the drawable will swap its buffers if {@link diff --git a/src/jogl/classes/javax/media/opengl/GLException.java b/src/jogl/classes/javax/media/opengl/GLException.java index 644042e15..460f17be9 100644 --- a/src/jogl/classes/javax/media/opengl/GLException.java +++ b/src/jogl/classes/javax/media/opengl/GLException.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/javax/media/opengl/GLFBODrawable.java b/src/jogl/classes/javax/media/opengl/GLFBODrawable.java new file mode 100644 index 000000000..052b08a4b --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/GLFBODrawable.java @@ -0,0 +1,195 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package javax.media.opengl; + +import javax.media.nativewindow.NativeWindowException; + +import com.jogamp.opengl.FBObject; +import com.jogamp.opengl.FBObject.TextureAttachment; + +/** + * Platform-independent {@link GLDrawable} specialization, + * exposing {@link FBObject} functionality. + * + * <p> + * A {@link GLFBODrawable} is uninitialized until a {@link GLContext} is bound + * and made current the first time, hence only then it's capabilities <i>fully</i> reflect expectations, + * i.e. color, depth, stencil and MSAA bits will be <i>valid</i> only after the first {@link GLContext#makeCurrent() makeCurrent()} call. + * On-/offscreen bits are <i>valid</i> after {@link #setRealized(boolean) setRealized(true)}. + * </p> + * + * <p> + * MSAA is used if {@link GLCapabilitiesImmutable#getNumSamples() requested}. + * </p> + * <p> + * Double buffering is used if {@link GLCapabilitiesImmutable#getDoubleBuffered() requested}. + * </p> + * <p> + * In MSAA mode, it always uses the implicit 2nd {@link FBObject framebuffer} {@link FBObject#getSamplingSinkFBO() sink}. + * Hence double buffering is always the case w/ MSAA. + * </p> + * <p> + * In non MSAA a second explicit {@link FBObject framebuffer} is being used. + * This method allows compliance w/ the spec, i.e. read and draw framebuffer selection + * and double buffer usage for e.g. {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer) glReadPixels(..)}. + * This method also allows usage of both textures seperately. + * </p> + * <p> + * It would be possible to implement double buffering simply using + * {@link TextureAttachment}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} + * 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> + */ +public interface GLFBODrawable extends GLDrawable { + // public enum DoubleBufferMode { NONE, TEXTURE, FBO }; // TODO: Add or remove TEXTURE (only) DoubleBufferMode support + + /** + * @return <code>true</code> if initialized, i.e. a {@link GLContext} is bound and made current once, otherwise <code>false</code>. + */ + public boolean isInitialized(); + + /** + * 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; + + /** + * @return the used texture unit + */ + int getTextureUnit(); + + /** + * + * @param unit the texture unit to be used + */ + void setTextureUnit(int unit); + + /** + * Set the number of sample buffers if using MSAA + * + * @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. + * @param newSamples new sample size + * @throws GLException if resetting the FBO failed + */ + void setNumSamples(GL gl, int newSamples) throws GLException; + + /** + * @return the number of sample buffers if using MSAA, otherwise 0 + */ + int getNumSamples(); + + /** + * Sets the number of buffers (FBO) being used if using {@link GLCapabilities#getDoubleBuffered() double buffering}. + * <p> + * If {@link GLCapabilities#getDoubleBuffered() double buffering} is not chosen, this is a NOP. + * </p> + * <p> + * 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()}. + */ + int setNumBuffers(int bufferCount) throws GLException; + + /** + * @return the number of buffers (FBO) being used. 1 if not using {@link GLCapabilities#getDoubleBuffered() double buffering}, + * otherwise ≥ 2, depending on {@link #setNumBuffers(int)}. + */ + int getNumBuffers(); + + /** + * @return the used {@link DoubleBufferMode} + */ + // DoubleBufferMode getDoubleBufferMode(); // TODO: Add or remove TEXTURE (only) DoubleBufferMode support + + /** + * Sets the {@link DoubleBufferMode}. Must be called before {@link #isInitialized() initialization}, + * otherwise an exception is thrown. + * <p> + * This call has no effect is MSAA is selected, since MSAA always forces the mode to {@link DoubleBufferMode#FBO FBO}. + * Also setting the mode to {@link DoubleBufferMode#NONE NONE} where double buffering is {@link GLCapabilitiesImmutable#getDoubleBuffered() requested} + * or setting a double buffering mode w/o {@link GLCapabilitiesImmutable#getDoubleBuffered() request} will be ignored. + * </p> + * <p> + * Since {@link DoubleBufferMode#TEXTURE TEXTURE} mode is currently not implemented, this method has no effect. + * </p> + * @throws GLException if already initialized, see {@link #isInitialized()}. + */ + // void setDoubleBufferMode(DoubleBufferMode mode) throws GLException; // TODO: Add or remove TEXTURE (only) DoubleBufferMode support + + /** + * If MSAA is being used and {@link GL#GL_FRONT} is requested, + * the internal {@link FBObject} {@link FBObject#getSamplingSinkFBO() sample sink} is being returned. + * + * @param bufferName {@link GL#GL_FRONT} and {@link GL#GL_BACK} are valid buffer names + * @return the named {@link FBObject} + * @throws IllegalArgumentException if an illegal buffer name is being used + */ + FBObject getFBObject(int bufferName) throws IllegalArgumentException; + + /** + * Returns the named texture buffer. + * <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> + * @param bufferName {@link GL#GL_FRONT} and {@link GL#GL_BACK} are valid buffer names + * @return the named {@link TextureAttachment} + * @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; + + /** Resizeable {@link GLFBODrawable} specialization */ + public interface Resizeable extends GLFBODrawable { + /** + * Resize this drawable. + * <p> + * This drawable is being locked during operation. + * </p> + * @param context the {@link GLContext} bound to this drawable, will be made current during operation + * A prev. current context will be make current after operation. + * @param newWidth + * @param newHeight + * @throws NativeWindowException in case the surface could no be locked + * @throws GLException in case an error during the resize operation occurred + */ + void setSize(GLContext context, int newWidth, int newHeight) throws NativeWindowException, GLException; + } +} diff --git a/src/jogl/classes/javax/media/opengl/GLOffscreenAutoDrawable.java b/src/jogl/classes/javax/media/opengl/GLOffscreenAutoDrawable.java new file mode 100644 index 000000000..d34edaf2e --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/GLOffscreenAutoDrawable.java @@ -0,0 +1,63 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package javax.media.opengl; + +import javax.media.nativewindow.NativeWindowException; + +import com.jogamp.opengl.FBObject; + +/** + * Platform-independent {@link GLAutoDrawable} specialization, + * exposing offscreen functionality. + * <p> + * This class distinguishes itself from {@link GLAutoDrawable} + * with it's {@link #setSize(int, int)} functionality. + * </p> + */ +public interface GLOffscreenAutoDrawable extends GLAutoDrawable, GLSharedContextSetter { + + /** + * Resize this auto drawable. + * @param newWidth + * @param newHeight + * @throws NativeWindowException in case the surface could no be locked + * @throws GLException in case of an error during the resize operation + */ + void setSize(int newWidth, int newHeight) throws NativeWindowException, GLException; + + /** + * Set the upstream UI toolkit object. + * @see #getUpstreamWidget() + */ + void setUpstreamWidget(Object newUpstreamWidget); + + /** {@link FBObject} based {@link GLOffscreenAutoDrawable} specialization */ + public interface FBO extends GLOffscreenAutoDrawable, GLFBODrawable { + } +} diff --git a/src/jogl/classes/javax/media/opengl/GLPbuffer.java b/src/jogl/classes/javax/media/opengl/GLPbuffer.java index 0250365b0..f36a4bf29 100644 --- a/src/jogl/classes/javax/media/opengl/GLPbuffer.java +++ b/src/jogl/classes/javax/media/opengl/GLPbuffer.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -45,47 +45,18 @@ package javax.media.opengl; contains experimental methods for accessing the pbuffer's contents as a texture map and enabling rendering to floating-point frame buffers. These methods are not guaranteed to be supported on all - platforms and may be deprecated in a future release. */ + platforms and may be deprecated in a future release. + @deprecated Use {@link GLOffscreenAutoDrawable} w/ {@link GLCapabilities#setFBO(boolean)} + via {@link GLDrawableFactory#createOffscreenAutoDrawable(javax.media.nativewindow.AbstractGraphicsDevice, GLCapabilitiesImmutable, GLCapabilitiesChooser, int, int, GLContext) GLDrawableFactory.createOffscreenAutoDrawable(..)}. + */ public interface GLPbuffer extends GLAutoDrawable { - /** Indicates the GL_APPLE_float_pixels extension is being used for this pbuffer. */ - public static final int APPLE_FLOAT = 1; - - /** Indicates the GL_ATI_texture_float extension is being used for this pbuffer. */ - public static final int ATI_FLOAT = 2; - - /** Indicates the GL_NV_float_buffer extension is being used for this pbuffer. */ - public static final int NV_FLOAT = 3; - - /** Binds this pbuffer to its internal texture target. Only valid to - call if offscreen render-to-texture has been specified in the - NWCapabilities for this GLPbuffer. If the - render-to-texture-rectangle capability has also been specified, - this will use e.g. wglBindTexImageARB as its implementation and - cause the texture to be bound to e.g. the - GL_TEXTURE_RECTANGLE_NV state; otherwise, during the display() - phase the pixels will have been copied into an internal texture - target and this will cause that to be bound to the GL_TEXTURE_2D - state. */ - public void bindTexture(); - - /** Unbinds the pbuffer from its internal texture target. */ - public void releaseTexture(); - /** Destroys the native resources associated with this pbuffer. It is not valid to call display() or any other routines on this pbuffer after it has been destroyed. Before destroying the pbuffer, the application must destroy any additional OpenGL contexts which have been created for the pbuffer via {@link #createContext}. */ + @Override public void destroy(); - - /** Indicates which vendor's extension is being used to support - floating point channels in this pbuffer if that capability was - requested in the NWCapabilities during pbuffer creation. Returns - one of NV_FLOAT, ATI_FLOAT or APPLE_FLOAT, or throws GLException - if floating-point channels were not requested for this pbuffer. - This function may only be called once the init method for this - pbuffer's GLEventListener has been called. */ - public int getFloatingPointMode(); } diff --git a/src/jogl/classes/javax/media/opengl/GLPipelineFactory.java b/src/jogl/classes/javax/media/opengl/GLPipelineFactory.java index 2bfc77d4a..d947bada2 100644 --- a/src/jogl/classes/javax/media/opengl/GLPipelineFactory.java +++ b/src/jogl/classes/javax/media/opengl/GLPipelineFactory.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,7 +28,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. @@ -49,10 +49,25 @@ import jogamp.opengl.*; public class GLPipelineFactory { public static final boolean DEBUG = Debug.debug("GLPipelineFactory"); - /** + /** * Creates a pipelined GL instance using the given downstream <code>downstream</code> - * and optional arguments <code>additionalArgs</code> for the constructor.<br> + * and optional arguments <code>additionalArgs</code> for the constructor. * + * <p> + * Sample code which installs a Debug and Trace pipeline + * automatic w/ user defined interface, here: GL2ES2: + * <pre> + * gl = drawable.setGL( GLPipelineFactory.create("javax.media.opengl.Debug", GL2ES2.class, gl, null) ); + * gl = drawable.setGL( GLPipelineFactory.create("javax.media.opengl.Trace", GL2ES2.class, gl, new Object[] { System.err } ) ); + * </pre> + * or automatic w/ automatic defined class: + * <pre> + * gl = drawable.setGL( GLPipelineFactory.create("javax.media.opengl.Debug", null, gl, null) ); + * gl = drawable.setGL( GLPipelineFactory.create("javax.media.opengl.Trace", null, gl, new Object[] { System.err } ) ); + * </pre> + * </p> + * + * <p> * The upstream GL instance is determined as follows: * <ul> * <li> Use <code>pipelineClazzBaseName</code> as the class name's full basename, incl. package name</li> @@ -61,11 +76,12 @@ public class GLPipelineFactory { * <li> For all <code>downstream</code> class and superclass interfaces, do:</li> * <ul> * <li> If <code>reqInterface</code> is not null and the interface is unequal, continue loop.</li> - * <li> If <code>downstream</code> is not instance of interface, continue loop.</li> + * <li> If <code>downstream</code> is not instance of interface, continue loop.</li> * <li> If upstream class is available use it, end loop.</li> * </ul> * </ul> - * </ul><br> + * </ul> + * </p> * * @param pipelineClazzBaseName the basename of the pipline class name * @param reqInterface optional requested interface to be used, may be null, in which case the first matching one is used @@ -100,7 +116,7 @@ public class GLPipelineFactory { if(DEBUG) { System.out.println("GLPipelineFactory: "+downstream.getClass().getName() + " is _not_ instance of "+ clazzes[i].getName()); } - continue; // not a compatible one + continue; // not a compatible one } else { if(DEBUG) { System.out.println("GLPipelineFactory: "+downstream.getClass().getName() + " _is_ instance of "+ clazzes[i].getName()); @@ -137,7 +153,7 @@ public class GLPipelineFactory { // throws exception if cstr not found! Constructor<?> cstr = ReflectionUtil.getConstructor(upstreamClazz, cstrArgTypes); Object instance = null; - try { + try { Object[] cstrArgs = new Object[ 1 + ( ( null==additionalArgs ) ? 0 : additionalArgs.length ) ] ; { int i = 0; diff --git a/src/jogl/classes/javax/media/opengl/GLProfile.java b/src/jogl/classes/javax/media/opengl/GLProfile.java index f85c6ba23..80f46955d 100644 --- a/src/jogl/classes/javax/media/opengl/GLProfile.java +++ b/src/jogl/classes/javax/media/opengl/GLProfile.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,7 +29,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. @@ -37,6 +37,7 @@ package javax.media.opengl; +import jogamp.nativewindow.NWJNILibLoader; import jogamp.opengl.Debug; import jogamp.opengl.GLDrawableFactoryImpl; import jogamp.opengl.DesktopGLDynamicLookupHelper; @@ -49,6 +50,7 @@ import com.jogamp.common.util.VersionUtil; import com.jogamp.common.util.cache.TempJarCache; import com.jogamp.common.util.locks.LockFactory; import com.jogamp.common.util.locks.RecursiveThreadGroupLock; +import com.jogamp.gluegen.runtime.FunctionAddressResolver; import com.jogamp.nativewindow.NativeWindowVersion; import com.jogamp.opengl.JoglVersion; @@ -56,158 +58,138 @@ import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowFactory; import javax.media.opengl.fixedfunc.GLPointerFunc; +import java.lang.reflect.Constructor; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.HashMap; -import java.util.Iterator; import java.util.List; +import java.util.Map; /** * Specifies the the OpenGL profile. - * + * * This class static singleton initialization queries the availability of all OpenGL Profiles * and instantiates singleton GLProfile objects for each available profile. * - * The platform default profile may be used, using {@link GLProfile#GetProfileDefault()}, + * The platform default profile may be used, using {@link GLProfile#GetProfileDefault()}, * or more specialized versions using the other static GetProfile methods. */ public class GLProfile { - + public static final boolean DEBUG = Debug.debug("GLProfile"); - - /** - * 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> - */ - private static final boolean enableANGLE = Debug.isPropertyDefined("jogl.enable.ANGLE", true); static { // Also initializes TempJarCache if shall be used. Platform.initSingleton(); } - + /** * Static initialization of JOGL. + * * <p> - * The parameter <code>firstUIActionOnProcess</code> has an impact on concurrent locking,<br> - * see {@link javax.media.nativewindow.NativeWindowFactory#initSingleton(boolean) NativeWindowFactory.initSingleton(firstUIActionOnProcess)}. + * This method shall not need to be called for other reasons than having a defined initialization sequence. * </p> - * <p> - * Applications using this method may place it's call before any other UI invocation - * in the <code>main class</code>'s static block or within the <code>main function</code>. - * In such case, applications may pass <code>firstUIActionOnProcess=true</code> to use native toolkit locking.</P> - * <P> - * RCP Application (Applet's, Webstart, Netbeans, ..) using JOGL are not be able to initialize JOGL - * before the first UI action. - * In such case you shall pass <code>firstUIActionOnProcess=false</code>.</P> + * * <P> * In case this method is not invoked, GLProfile is initialized implicit by - * the first call to {@link #getDefault()}, {@link #get(java.lang.String)} passing <code>firstUIActionOnProcess=false</code>. + * the first call to {@link #getDefault()}, {@link #get(java.lang.String)}. * <P> * - * @param firstUIActionOnProcess Should be <code>true</code> if called before the first UI action of the running program, - * otherwise <code>false</code>. - * - * @deprecated This method shall not need to be called for other reasons than having a defined initialization sequence. - * To ensure homogeneous behavior with application not calling this method, you shall pass <code>firstUIActionOnProcess=false</code>. - * This method is subject to be removed in future versions of JOGL. + * <p> + * To initialize JOGL at startup ASAP, this method may be invoked in the <i>main class</i>'s + * static initializer block, in the <i>static main() method</i> or in the <i>Applet init() method</i>. + * </p> + * + * <p> + * Since JOGL's initialization is complex and involves multi threading, it is <b>not</b> recommended + * to be have it invoked on the AWT EDT thread. In case all JOGL usage is performed + * on the AWT EDT, invoke this method outside the AWT EDT - see above. + * </p> + * */ - public static void initSingleton(final boolean firstUIActionOnProcess) { + public static void initSingleton() { + final boolean justInitialized; initLock.lock(); try { - if(!initialized) { // volatile: ok + if(!initialized) { initialized = true; + justInitialized = true; if(DEBUG) { - System.err.println("GLProfile.initSingleton(firstUIActionOnProcess: "+firstUIActionOnProcess+") - thread "+Thread.currentThread().getName()); + System.err.println("GLProfile.initSingleton() - thread "+Thread.currentThread().getName()); Thread.dumpStack(); } - + + if(ReflectionUtil.DEBUG_STATS_FORNAME) { + ReflectionUtil.resetForNameCount(); + } + // run the whole static initialization privileged to speed up, // since this skips checking further access AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override public Object run() { Platform.initSingleton(); - + if(TempJarCache.isInitialized()) { - String[] atomicNativeJarBaseNames = new String[] { "nativewindow", "jogl", null }; - if( ReflectionUtil.isClassAvailable("com.jogamp.newt.NewtFactory", GLProfile.class.getClassLoader()) ) { - atomicNativeJarBaseNames[2] = "newt"; + final ClassLoader cl = GLProfile.class.getClassLoader(); + final String newtFactoryClassName = "com.jogamp.newt.NewtFactory"; + final Class<?>[] classesFromJavaJars = new Class<?>[] { NWJNILibLoader.class, GLProfile.class, null }; + if( ReflectionUtil.isClassAvailable(newtFactoryClassName, cl) ) { + classesFromJavaJars[2] = ReflectionUtil.getClass(newtFactoryClassName, false, cl); } - JNILibLoaderBase.addNativeJarLibs(GLProfile.class, "jogl-all", atomicNativeJarBaseNames); + JNILibLoaderBase.addNativeJarLibsJoglCfg(classesFromJavaJars); } - initProfilesForDefaultDevices(firstUIActionOnProcess); + initProfilesForDefaultDevices(); return null; } }); + if( ReflectionUtil.DEBUG_STATS_FORNAME ) { + if( justInitialized ) { + System.err.println(ReflectionUtil.getForNameStats(null).toString()); + } + } + } else { + justInitialized = false; } } finally { initLock.unlock(); } - } - - /** - * Static initialization of JOGL. - * - * <p> - * This method shall not need to be called for other reasons than having a defined initialization sequence. - * </p> - */ - public static void initSingleton() { - GLProfile.initSingleton(false); + if(DEBUG) { + if( justInitialized && ( hasGL234Impl || hasGLES1Impl || hasGLES3Impl ) ) { + System.err.println(JoglVersion.getDefaultOpenGLInfo(defaultDevice, null, true)); + } + } } /** * Trigger eager initialization of GLProfiles for the given device, * in case it isn't done yet. - * + * * @throws GLException if no profile for the given device is available. */ public static void initProfiles(AbstractGraphicsDevice device) throws GLException { getProfileMap(device, true); } - /** - * Shutdown type for {@link GLProfile#shutdown(ShutdownType)}. - * <p> - * {@link #SHARED_ONLY} For thread based resources only, suitable for eg. {@link java.applet.Applet Applet} restart.<br> - * {@link #COMPLETE} Everything.<br> - * </p> - */ - public enum ShutdownType { - /* Shared thread based resources only, eg. for Applets */ - SHARED_ONLY, - /* Everything */ - COMPLETE; - } - /** * Manual shutdown method, may be called after your last JOGL use * within the running JVM.<br> * It releases all temporary created resources, ie issues {@link javax.media.opengl.GLDrawableFactory#shutdown()}.<br> * The shutdown implementation is called via the JVM shutdown hook, if not manually invoked.<br> * <p> - * This method shall not need to be called for other reasons than issuing a proper shutdown of resources. + * This method shall not need to be called for other reasons than issuing a proper shutdown of resources at a defined time. * </p> - * @param type the shutdown type, see {@link ShutdownType}. */ - public static void shutdown(ShutdownType type) { + public static void shutdown() { initLock.lock(); try { - if(initialized) { // volatile: ok + if(initialized) { initialized = false; if(DEBUG) { - System.err.println("GLProfile.shutdown(type: "+type+") - thread "+Thread.currentThread().getName()); + System.err.println("GLProfile.shutdown() - thread "+Thread.currentThread().getName()); Thread.dumpStack(); - } - GLDrawableFactory.shutdown(type); - if(ShutdownType.COMPLETE == type) { - GLContext.shutdown(); } - NativeWindowFactory.shutdown(); + GLDrawableFactory.shutdown(); } } finally { initLock.unlock(); @@ -218,11 +200,11 @@ public class GLProfile { // Query platform available OpenGL implementation // - /** + /** * Returns the availability of a profile on a device. - * + * * @param device a valid AbstractGraphicsDevice, or <code>null</null> for the default device. - * @param profile a valid GLProfile name ({@link #GL4bc}, {@link #GL4}, {@link #GL2}, ..), + * @param profile a valid GLProfile name ({@link #GL4bc}, {@link #GL4}, {@link #GL2}, ..), * or <code>[ null, GL ]</code> for the default profile. * @return true if the profile is available for the device, otherwise false. */ @@ -233,31 +215,31 @@ public class GLProfile { private static boolean isAvailableImpl(HashMap<String /*GLProfile_name*/, GLProfile> map, String profile) { return null != map && null != map.get(profile); } - - /** + + /** * Returns the availability of a profile on the default device. - * - * @param profile a valid GLProfile name ({@link #GL4bc}, {@link #GL4}, {@link #GL2}, ..), + * + * @param profile a valid GLProfile name ({@link #GL4bc}, {@link #GL4}, {@link #GL2}, ..), * or <code>[ null, GL ]</code> for the default profile. * @return true if the profile is available for the default device, otherwise false. */ public static boolean isAvailable(String profile) { return isAvailable(null, profile); } - - /** + + /** * Returns the availability of any profile on the default device. - * + * * @return true if any profile is available for the default device, otherwise false. */ public static boolean isAnyAvailable() { return isAvailable(null, null); } - + public static String glAvailabilityToString(AbstractGraphicsDevice device) { return glAvailabilityToString(device, null).toString(); } - + public static StringBuilder glAvailabilityToString(AbstractGraphicsDevice device, StringBuilder sb) { return glAvailabilityToString(device, sb, null, 0); } @@ -276,121 +258,183 @@ public class GLProfile { final boolean useIndent = null != indent; initSingleton(); - + + int allCount = 0; + int nativeCount = 0; + if(null==device) { device = defaultDevice; } final HashMap<String /*GLProfile_name*/, GLProfile> map = getProfileMap(device, false); - + if(useIndent) { - doIndent(sb, indent, indentCount).append("Native"); + doIndent(sb, indent, indentCount).append("Natives"); indentCount++; - doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("GL4bc").append(indent); + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GL4bc+" ").append(indent); } else { - sb.append("Native[GL4bc "); - } + sb.append("Natives["+GL4bc+" "); + } avail=isAvailableImpl(map, GL4bc); sb.append(avail); if(avail) { + nativeCount++; glAvailabilityToString(device, sb.append(" "), 4, GLContext.CTX_PROFILE_COMPAT); } + allCount++; if(useIndent) { - doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("GL4").append(indent); + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GL4+" ").append(indent); } else { - sb.append(", GL4 "); + sb.append(", "+GL4+" "); } avail=isAvailableImpl(map, GL4); sb.append(avail); if(avail) { + nativeCount++; glAvailabilityToString(device, sb.append(" "), 4, GLContext.CTX_PROFILE_CORE); } + allCount++; + + if(useIndent) { + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GLES3+" ").append(indent); + } else { + sb.append(", "+GLES3+" "); + } + avail=isAvailableImpl(map, GLES3); + sb.append(avail); + if(avail) { + nativeCount++; + glAvailabilityToString(device, sb.append(" "), 3, GLContext.CTX_PROFILE_ES); + } + allCount++; if(useIndent) { - doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("GL3bc").append(indent); + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GL3bc+" ").append(indent); } else { - sb.append(", GL3bc "); + sb.append(", "+GL3bc+" "); } avail=isAvailableImpl(map, GL3bc); sb.append(avail); if(avail) { + nativeCount++; glAvailabilityToString(device, sb.append(" "), 3, GLContext.CTX_PROFILE_COMPAT); } + allCount++; if(useIndent) { - doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("GL3").append(indent); + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GL3+" ").append(indent); } else { - sb.append(", GL3 "); + sb.append(", "+GL3+" "); } avail=isAvailableImpl(map, GL3); sb.append(avail); if(avail) { + nativeCount++; glAvailabilityToString(device, sb.append(" "), 3, GLContext.CTX_PROFILE_CORE); } + allCount++; if(useIndent) { - doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("GL2").append(indent); + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GL2+" ").append(indent); } else { - sb.append(", GL2 "); + sb.append(", "+GL2+" "); } avail=isAvailableImpl(map, GL2); sb.append(avail); if(avail) { + nativeCount++; glAvailabilityToString(device, sb.append(" "), 2, GLContext.CTX_PROFILE_COMPAT); } + allCount++; if(useIndent) { - doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("GL2ES1").append(indent); + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GLES2+" ").append(indent); } else { - sb.append(", GL2ES1 "); + sb.append(", "+GLES2+" "); } - sb.append(isAvailableImpl(map, GL2ES1)); + avail=isAvailableImpl(map, GLES2); + sb.append(avail); + if(avail) { + nativeCount++; + glAvailabilityToString(device, sb.append(" "), 2, GLContext.CTX_PROFILE_ES); + } + allCount++; if(useIndent) { - doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("GLES1").append(indent); + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GLES1+" ").append(indent); } else { - sb.append(", GLES1 "); + sb.append(", "+GLES1+" "); } avail=isAvailableImpl(map, GLES1); sb.append(avail); if(avail) { + nativeCount++; glAvailabilityToString(device, sb.append(" "), 1, GLContext.CTX_PROFILE_ES); } + allCount++; if(useIndent) { - doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("GL2ES2").append(indent); + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("Count\t"+nativeCount+" / "+allCount); + indentCount--; + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("Common"); + indentCount++; } else { - sb.append(", GL2ES2 "); + sb.append(", count "+nativeCount+" / "+allCount+"], Common["); } - sb.append(isAvailableImpl(map, GL2ES2)); if(useIndent) { - doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("GLES2").append(indent); + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GL4ES3+" ").append(indent); } else { - sb.append(", GLES2 "); + sb.append(", "+GL4ES3+" "); } - avail=isAvailableImpl(map, GLES2); - sb.append(avail); - if(avail) { - glAvailabilityToString(device, sb.append(" "), 2, GLContext.CTX_PROFILE_ES); + sb.append(isAvailableImpl(map, GL4ES3)); + allCount++; + + if(useIndent) { + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GL2GL3+" ").append(indent); + } else { + sb.append(", "+GL2GL3+" "); } + sb.append(isAvailableImpl(map, GL2GL3)); + allCount++; + + if(useIndent) { + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GL2ES2+" ").append(indent); + } else { + sb.append(", "+GL2ES2+" "); + } + sb.append(isAvailableImpl(map, GL2ES2)); + allCount++; + + if(useIndent) { + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GL2ES1+" ").append(indent); + } else { + sb.append(", "+GL2ES1+" "); + } + sb.append(isAvailableImpl(map, GL2ES1)); + allCount++; if(useIndent) { indentCount--; - doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("Profiles"); + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("Mappings"); indentCount++; } else { - sb.append("], Profiles["); + sb.append("], Mappings["); } - + + int profileCount = 0; + if(null != map) { - for(Iterator<GLProfile> i=map.values().iterator(); i.hasNext(); ) { - if(useIndent) { - doIndent(sb.append(Platform.getNewline()), indent, indentCount); - } - sb.append(i.next().toString()); - if(!useIndent) { - sb.append(", "); + for (Map.Entry<String,GLProfile> entry : map.entrySet()) { + if( !GL_DEFAULT.equals(entry.getKey()) ) { + if(useIndent) { + doIndent(sb.append(Platform.getNewline()), indent, indentCount); + } + sb.append(entry.getKey()+(useIndent?" \t":" ")+entry.getValue()); + if(!useIndent) { + sb.append(", "); + } + profileCount++; } } if(useIndent) { @@ -405,14 +449,15 @@ public class GLProfile { } } if(useIndent) { + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("Count\t"+profileCount+" / "+allCount); sb.append(Platform.getNewline()); } else { - sb.append("]"); + sb.append(", count "+profileCount+" / "+allCount+"]"); } return sb; } - + /** Uses the default device */ public static String glAvailabilityToString() { return glAvailabilityToString(null); @@ -445,6 +490,9 @@ public class GLProfile { /** The embedded OpenGL profile ES 2.x, with x >= 0 */ public static final String GLES2 = "GLES2"; + /** The embedded OpenGL profile ES 3.x, with x >= 0 */ + public static final String GLES3 = "GLES3"; + /** The intersection of the desktop GL2 and embedded ES1 profile */ public static final String GL2ES1 = "GL2ES1"; @@ -454,85 +502,92 @@ public class GLProfile { /** The intersection of the desktop GL3 and GL2 profile */ public static final String GL2GL3 = "GL2GL3"; + /** 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"; + /** The default profile, used for the device default profile map */ private static final String GL_DEFAULT = "GL_DEFAULT"; - /** + /** * All GL Profiles in the order of default detection. * Desktop compatibility profiles (the one with fixed function pipeline) comes first * from highest to lowest version. * <p> This includes the generic subset profiles GL2GL3, GL2ES2 and GL2ES1.</p> * * <ul> - * <li> GL4bc - * <li> GL3bc - * <li> GL2 - * <li> GL4 - * <li> GL3 - * <li> GL2GL3 - * <li> GLES2 - * <li> GL2ES2 - * <li> GLES1 - * <li> GL2ES1 + * <li> GL4bc </li> + * <li> GL3bc </li> + * <li> GL2 </li> + * <li> GL4 </li> + * <li> GL3 </li> + * <li> GLES3 </li> + * <li> GL4ES3 </li> + * <li> GL2GL3 </li> + * <li> GLES2 </li> + * <li> GL2ES2 </li> + * <li> GLES1 </li> + * <li> GL2ES1 </li> * </ul> * */ - public static final String[] GL_PROFILE_LIST_ALL = new String[] { GL4bc, GL3bc, GL2, GL4, GL3, GL2GL3, GLES2, GL2ES2, GLES1, GL2ES1 }; + public static final String[] GL_PROFILE_LIST_ALL = new String[] { GL4bc, GL3bc, GL2, GL4, GL3, GLES3, GL4ES3, GL2GL3, GLES2, GL2ES2, GLES1, GL2ES1 }; /** * Order of maximum profiles. * * <ul> - * <li> GL4bc - * <li> GL4 - * <li> GL3bc - * <li> GL3 - * <li> GL2 - * <li> GLES2 - * <li> GLES1 + * <li> GL4bc </li> + * <li> GL4 </li> + * <li> GL3bc </li> + * <li> GL3 </li> + * <li> GLES3 </li> + * <li> GL2 </li> + * <li> GLES2 </li> + * <li> GLES1 </li> * </ul> * */ - public static final String[] GL_PROFILE_LIST_MAX = new String[] { GL4bc, GL4, GL3bc, GL3, GL2, GLES2, GLES1 }; + public static final String[] GL_PROFILE_LIST_MAX = new String[] { GL4bc, GL4, GL3bc, GL3, GLES3, GL2, GLES2, GLES1 }; /** * Order of minimum profiles. * * <ul> - * <li> GLES1 - * <li> GLES2 - * <li> GL2 - * <li> GL3 - * <li> GL3bc - * <li> GL4 - * <li> GL4bc + * <li> GLES1 </li> + * <li> GLES2 </li> + * <li> GL2 </li> + * <li> GLES3 </li> + * <li> GL3 </li> + * <li> GL3bc </li> + * <li> GL4 </li> + * <li> GL4bc </li> * </ul> * */ - public static final String[] GL_PROFILE_LIST_MIN = new String[] { GLES1, GLES2, GL2, GL3, GL3bc, GL4, GL4bc }; - + public static final String[] GL_PROFILE_LIST_MIN = new String[] { GLES1, GLES2, GL2, GLES3, GL3, GL3bc, GL4, GL4bc }; + /** * Order of minimum original desktop profiles. * * <ul> - * <li> GL2 - * <li> GL3bc - * <li> GL4bc - * <li> GL3 - * <li> GL4 + * <li> GL2 </li> + * <li> GL3bc </li> + * <li> GL4bc </li> + * <li> GL3 </li> + * <li> GL4 </li> * </ul> * */ public static final String[] GL_PROFILE_LIST_MIN_DESKTOP = new String[] { GL2, GL3bc, GL4bc, GL3, GL4 }; - + /** * Order of maximum fixed function profiles * * <ul> - * <li> GL4bc - * <li> GL3bc - * <li> GL2 - * <li> GLES1 + * <li> GL4bc </li> + * <li> GL3bc </li> + * <li> GL2 </li> + * <li> GLES1 </li> * </ul> * */ @@ -542,16 +597,30 @@ public class GLProfile { * Order of maximum programmable shader profiles * * <ul> - * <li> GL4bc - * <li> GL4 - * <li> GL3bc - * <li> GL3 - * <li> GL2 - * <li> GLES2 + * <li> GL4bc </li> + * <li> GL4 </li> + * <li> GL3bc </li> + * <li> GL3 </li> + * <li> GLES3 </li> + * <li> GL2 </li> + * <li> GLES2 </li> * </ul> * */ - public static final String[] GL_PROFILE_LIST_MAX_PROGSHADER = new String[] { GL4bc, GL4, GL3bc, GL3, GL2, GLES2 }; + public static final String[] GL_PROFILE_LIST_MAX_PROGSHADER = new String[] { GL4bc, GL4, GL3bc, GL3, GLES3, GL2, GLES2 }; + + /** + * Order of maximum programmable shader <i>core only</i> profiles + * + * <ul> + * <li> GL4 </li> + * <li> GL3 </li> + * <li> GLES3 </li> + * <li> GLES2 </li> + * </ul> + * + */ + public static final String[] GL_PROFILE_LIST_MAX_PROGSHADER_CORE = new String[] { GL4, GL3, GLES3, GLES2 }; /** Returns a default GLProfile object, reflecting the best for the running platform. * It selects the first of the set {@link GLProfile#GL_PROFILE_LIST_ALL} @@ -567,7 +636,7 @@ public class GLProfile { /** Returns a default GLProfile object, reflecting the best for the running platform. * It selects the first of the set {@link GLProfile#GL_PROFILE_LIST_ALL} * and favors hardware acceleration. - * <p>Uses the default device.</p> + * <p>Uses the default device.</p> * @throws GLException if no profile is available for the default device. */ public static GLProfile getDefault() { @@ -587,7 +656,7 @@ public class GLProfile { return get(device, GL_PROFILE_LIST_MAX, favorHardwareRasterizer); } - /** Uses the default device + /** Uses the default device * @throws GLException if no profile is available for the default device. * @see #GL_PROFILE_LIST_MAX */ @@ -610,7 +679,7 @@ public class GLProfile { return get(device, GL_PROFILE_LIST_MIN, favorHardwareRasterizer); } - /** Uses the default device + /** Uses the default device * @throws GLException if no desktop profile is available for the default device. * @see #GL_PROFILE_LIST_MIN */ @@ -634,7 +703,7 @@ public class GLProfile { return get(device, GL_PROFILE_LIST_MAX_FIXEDFUNC, favorHardwareRasterizer); } - /** Uses the default device + /** Uses the default device * @throws GLException if no fixed function profile is available for the default device. * @see #GL_PROFILE_LIST_MAX_FIXEDFUNC */ @@ -657,7 +726,7 @@ public class GLProfile { return get(device, GL_PROFILE_LIST_MAX_PROGSHADER, favorHardwareRasterizer); } - /** Uses the default device + /** Uses the default device * @throws GLException if no programmable profile is available for the default device. * @see #GL_PROFILE_LIST_MAX_PROGSHADER */ @@ -667,7 +736,30 @@ public class GLProfile { return get(GL_PROFILE_LIST_MAX_PROGSHADER, favorHardwareRasterizer); } - /** + /** + * Returns the highest profile, implementing the programmable shader <i>core</i> pipeline <i>only</i>. + * It selects the first of the set: {@link GLProfile#GL_PROFILE_LIST_MAX_PROGSHADER_CORE} + * + * @throws GLException if no programmable core profile is available for the device. + * @see #GL_PROFILE_LIST_MAX_PROGSHADER_CORE + */ + public static GLProfile getMaxProgrammableCore(AbstractGraphicsDevice device, boolean favorHardwareRasterizer) + throws GLException + { + return get(device, GL_PROFILE_LIST_MAX_PROGSHADER_CORE, favorHardwareRasterizer); + } + + /** Uses the default device + * @throws GLException if no programmable core profile is available for the default device. + * @see #GL_PROFILE_LIST_MAX_PROGSHADER_CORE + */ + public static GLProfile getMaxProgrammableCore(boolean favorHardwareRasterizer) + throws GLException + { + return get(GL_PROFILE_LIST_MAX_PROGSHADER_CORE, favorHardwareRasterizer); + } + + /** * Returns the GL2ES1 profile implementation, hence compatible w/ GL2ES1.<br/> * It returns: * <pre> @@ -686,8 +778,8 @@ public class GLProfile { return get(device, GL2ES1).getImpl(); } - /** - * Calls {@link #getGL2ES1(AbstractGraphicsDevice)} using the default device. + /** + * Calls {@link #getGL2ES1(AbstractGraphicsDevice)} using the default device. * <p>Selection favors hardware rasterizer.</p> * @see #getGL2ES1(AbstractGraphicsDevice) */ @@ -697,7 +789,7 @@ public class GLProfile { return get(defaultDevice, GL2ES1).getImpl(); } - /** + /** * Returns the GL2ES2 profile implementation, hence compatible w/ GL2ES2.<br/> * It returns: * <pre> @@ -716,8 +808,8 @@ public class GLProfile { return get(device, GL2ES2).getImpl(); } - /** - * Calls {@link #getGL2ES2(AbstractGraphicsDevice)} using the default device. + /** + * Calls {@link #getGL2ES2(AbstractGraphicsDevice)} using the default device. * <p>Selection favors hardware rasterizer.</p> * @see #getGL2ES2(AbstractGraphicsDevice) */ @@ -727,7 +819,37 @@ public class GLProfile { return get(defaultDevice, GL2ES2).getImpl(); } - /** + /** + * Returns the GL4ES3 profile implementation, hence compatible w/ GL4ES3.<br/> + * It returns: + * <pre> + * GLProfile.get(device, GLProfile.GL4ES3).getImpl()); + * </pre> + * <p>Selection favors hardware rasterizer.</p> + * + * @throws GLException if no GL4ES3 compatible profile is available for the default device. + * @see #isGL4ES3() + * @see #get(AbstractGraphicsDevice, String) + * @see #getImpl() + */ + public static GLProfile getGL4ES3(AbstractGraphicsDevice device) + throws GLException + { + return get(device, GL4ES3).getImpl(); + } + + /** + * Calls {@link #getGL4ES3(AbstractGraphicsDevice)} using the default device. + * <p>Selection favors hardware rasterizer.</p> + * @see #getGL4ES3(AbstractGraphicsDevice) + */ + public static GLProfile getGL4ES3() + throws GLException + { + return get(defaultDevice, GL4ES3).getImpl(); + } + + /** * Returns the GL2GL3 profile implementation, hence compatible w/ GL2GL3.<br/> * It returns: * <pre> @@ -746,8 +868,8 @@ public class GLProfile { return get(device, GL2GL3).getImpl(); } - /** - * Calls {@link #getGL2GL3(AbstractGraphicsDevice)} using the default device. + /** + * Calls {@link #getGL2GL3(AbstractGraphicsDevice)} using the default device. * <p>Selection favors hardware rasterizer.</p> * @see #getGL2GL3(AbstractGraphicsDevice) */ @@ -763,7 +885,7 @@ public class GLProfile { * the default profile. * * @param device a valid AbstractGraphicsDevice, or <code>null</null> for the default device. - * @param profile a valid GLProfile name ({@link #GL4bc}, {@link #GL4}, {@link #GL2}, ..), + * @param profile a valid GLProfile name ({@link #GL4bc}, {@link #GL4}, {@link #GL2}, ..), * or <code>[ null, GL ]</code> for the default profile. * @throws GLException if the requested profile is not available for the device. */ @@ -781,8 +903,8 @@ public class GLProfile { return glp; } - /** Uses the default device - * @param profile a valid GLProfile name ({@link #GL4bc}, {@link #GL4}, {@link #GL2}, ..), + /** Uses the default device + * @param profile a valid GLProfile name ({@link #GL4bc}, {@link #GL4}, {@link #GL2}, ..), * or <code>[ null, GL ]</code> for the default profile. * @throws GLException if the requested profile is not available for the default device. */ @@ -798,14 +920,14 @@ public class GLProfile { * * @param device a valid AbstractGraphicsDevice, or <code>null</null> for the default device. * @param profiles array of valid GLProfile name ({@link #GL4bc}, {@link #GL4}, {@link #GL2}, ..) - * @param favorHardwareRasterizer set to true, if hardware rasterizer shall be favored, otherwise false. + * @param favorHardwareRasterizer set to true, if hardware rasterizer shall be favored, otherwise false. * @throws GLException if the non of the requested profiles is available for the device. */ public static GLProfile get(AbstractGraphicsDevice device, String[] profiles, boolean favorHardwareRasterizer) throws GLException { GLProfile glProfileAny = null; - + HashMap<String /*GLProfile_name*/, GLProfile> map = getProfileMap(device, true); for(int i=0; i<profiles.length; i++) { final GLProfile glProfile = map.get(profiles[i]); @@ -818,18 +940,18 @@ public class GLProfile { } if(null==glProfileAny) { glProfileAny = glProfile; - } + } } } if(null!=glProfileAny) { return glProfileAny; - } + } throw new GLException("Profiles "+array2String(profiles)+" not available on device "+device); } - /** Uses the default device - * @param profiles array of valid GLProfile name ({@link #GL4bc}, {@link #GL4}, {@link #GL2}, ..) - * @param favorHardwareRasterizer set to true, if hardware rasterizer shall be favored, otherwise false. + /** Uses the default device + * @param profiles array of valid GLProfile name ({@link #GL4bc}, {@link #GL4}, {@link #GL2}, ..) + * @param favorHardwareRasterizer set to true, if hardware rasterizer shall be favored, otherwise false. * @throws GLException if the non of the requested profiles is available for the default device. */ public static GLProfile get(String[] profiles, boolean favorHardwareRasterizer) @@ -837,19 +959,26 @@ public class GLProfile { { return get(defaultDevice, profiles, favorHardwareRasterizer); } - - /** Indicates whether the native OpenGL ES1 profile is in use. + + /** Indicates whether the native OpenGL ES1 profile is in use. * This requires an EGL interface. */ public static boolean usesNativeGLES1(String profileImpl) { return GLES1.equals(profileImpl); } - /** Indicates whether the native OpenGL ES2 profile is in use. - * This requires an EGL or ES2 compatible interface. + /** 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(String profileImpl) { - return GLES2.equals(profileImpl); + return GLES3.equals(profileImpl) || GLES2.equals(profileImpl); + } + + /** Indicates whether the native OpenGL ES2 profile is in use. + * This requires an EGL, ES3 compatible interface. + */ + public static boolean usesNativeGLES3(String profileImpl) { + return GLES3.equals(profileImpl); } /** Indicates whether either of the native OpenGL ES profiles are in use. */ @@ -908,28 +1037,64 @@ public class GLProfile { public final String getGLImplBaseClassName() { return getGLImplBaseClassName(getImplName()); } + private static final String getGLImplBaseClassName(String profileImpl) { + if( GLES2 == profileImpl || GLES3 == profileImpl ) { + return "jogamp.opengl.es3.GLES3"; + } else if( GLES1 == profileImpl ) { + return "jogamp.opengl.es1.GLES1"; + } else if ( GL4bc == profileImpl || + GL4 == profileImpl || + GL3bc == profileImpl || + GL3 == profileImpl || + GL2 == profileImpl ) { + return "jogamp.opengl.gl4.GL4bc"; + } else { + throw new GLException("unsupported profile \"" + profileImpl + "\""); + } + } + + public final Constructor<?> getGLCtor(boolean glObject) { + return getGLCtor(getImplName(), glObject); + } + private static final Constructor<?> getGLCtor(String profileImpl, boolean glObject) { + if( GLES2 == profileImpl || GLES3 == profileImpl ) { + return glObject ? ctorGLES3Impl : ctorGLES3ProcAddr; + } else if( GLES1 == profileImpl ) { + return glObject ? ctorGLES1Impl : ctorGLES1ProcAddr; + } else if ( GL4bc == profileImpl || + GL4 == profileImpl || + GL3bc == profileImpl || + GL3 == profileImpl || + GL2 == profileImpl ) { + return glObject ? ctorGL234Impl : ctorGL234ProcAddr; + } else { + throw new GLException("unsupported profile \"" + profileImpl + "\""); + } + } /** * @param o GLProfile object to compare with * @return true if given Object is a GLProfile and * if both, profile and profileImpl is equal with this. */ + @Override public final boolean equals(Object o) { if(this==o) { return true; } if(o instanceof GLProfile) { - GLProfile glp = (GLProfile)o; - return getName().equals(glp.getName()) && getImplName().equals(glp.getImplName()) ; + final GLProfile glp = (GLProfile)o; + return getName() == glp.getName() && getImplName() == glp.getImplName() ; } return false; } + @Override public int hashCode() { int hash = 5; hash = 97 * hash + getImplName().hashCode(); hash = 97 * hash + getName().hashCode(); return hash; } - + /** * @param glp GLProfile to compare with * @throws GLException if given GLProfile and this aren't equal @@ -949,14 +1114,14 @@ public class GLProfile { public final GLProfile getImpl() { return null != profileImpl ? profileImpl : this; } - + /** return true if impl. is a hardware rasterizer, otherwise false. */ public final boolean isHardwareRasterizer() { return isHardwareRasterizer; } - - /** - * return this profiles implementation name, eg. GL2ES2 -> GL2, or GL3 -> GL3 + + /** + * return this profiles implementation name, eg. GL2ES2 -> GL2, or GL3 -> GL3 */ public final String getImplName() { return null != profileImpl ? profileImpl.getName() : getName(); @@ -982,7 +1147,7 @@ public class GLProfile { return isGL4() || isGL3bc() || GL3 == profile; } - /** Indicates whether this context is a GL2 context <p>Includes [ GL4bc, GL3bc, GL2 ].</p> */ + /** Indicates whether this profile is capable of GL2 . <p>Includes [ GL4bc, GL3bc, GL2 ].</p> */ public final boolean isGL2() { return isGL3bc() || GL2 == profile; } @@ -992,9 +1157,19 @@ public class GLProfile { return GLES1 == profile; } - /** Indicates whether this profile is capable of GLES2. <p>Includes [ GLES2 ].</p> */ + /** Indicates whether this profile is capable of GLES2. <p>Includes [ GLES2, GLES3 ].</p> */ public final boolean isGLES2() { - return GLES2 == profile; + return isGLES3() || GLES2 == profile; + } + + /** Indicates whether this profile is capable of GLES3. <p>Includes [ GLES3 ].</p> */ + public final boolean isGLES3() { + return GLES3 == profile; + } + + /** Indicates whether this profile is capable of GLES. <p>Includes [ GLES1, GLES2, GLES3 ].</p> */ + public final boolean isGLES() { + return GLES3 == profile || GLES2 == profile || GLES1 == profile; } /** Indicates whether this profile is capable of GL2ES1. <p>Includes [ GL4bc, GL3bc, GL2, GLES1, GL2ES1 ].</p> */ @@ -1002,17 +1177,36 @@ public class GLProfile { return GL2ES1 == profile || isGLES1() || isGL2(); } - /** Indicates whether this profile is capable os GL2GL3. <p>Includes [ GL4bc, GL4, GL3bc, GL3, GL2, GL2GL3 ].</p> */ + /** Indicates whether this profile is capable of GL2GL3. <p>Includes [ GL4bc, GL4, GL3bc, GL3, GL2, GL2GL3 ].</p> */ public final boolean isGL2GL3() { return GL2GL3 == profile || isGL3() || isGL2(); } - /** Indicates whether this profile is capable os GL2ES2. <p>Includes [ GL4bc, GL4, GL3bc, GL3, GL2, GL2GL3, GL2ES2, GLES2 ].</p> */ + /** Indicates whether this profile is capable of GL2ES2. <p>Includes [ GL4bc, GL4, GL3bc, GL3, GLES3, GL2, GL2GL3, GL2ES2, GLES2 ].</p> */ public final boolean isGL2ES2() { return GL2ES2 == profile || isGLES2() || isGL2GL3(); } - /** Indicates whether this profile supports GLSL, ie. {@link #isGL2ES2()}. */ + /** + * Indicates whether this profile is capable of GL2ES3. <p>Includes [ GL4bc, GL4, GL3bc, GL3, GLES3, GL3ES3, GL2, GL2GL3 ].</p> + * @see #isGL3ES3() + * @see #isGL2GL3() + */ + public final boolean isGL2ES3() { + return isGL3ES3() || isGL2GL3(); + } + + /** Indicates whether this profile is capable of GL3ES3. <p>Includes [ GL4bc, GL4, GL3bc, GL3, GLES3 ].</p> */ + public final boolean isGL3ES3() { + return isGL4ES3() || isGL3(); + } + + /** Indicates whether this profile is capable of GL4ES3. <p>Includes [ GL4bc, GL4, GLES3 ].</p> */ + public final boolean isGL4ES3() { + return GL4ES3 == profile || isGLES3() || isGL4(); + } + + /** Indicates whether this profile supports GLSL, i.e. {@link #isGL2ES2()}. */ public final boolean hasGLSL() { return isGL2ES2() ; } @@ -1027,12 +1221,17 @@ public class GLProfile { return GLES2 == getImplName(); } + /** Indicates whether this profile uses the native OpenGL ES3 implementations. */ + public final boolean usesNativeGLES3() { + return GLES3 == getImplName(); + } + /** Indicates whether this profile uses either of the native OpenGL ES implementations. */ public final boolean usesNativeGLES() { - return usesNativeGLES2() || usesNativeGLES1(); + return usesNativeGLES3() || usesNativeGLES2() || usesNativeGLES1(); } - /** + /** * General validation if type is a valid GL data type * for the current profile */ @@ -1060,17 +1259,17 @@ public class GLProfile { if( isGL2() ) { return true; } - } + } if(throwException) { throw new GLException("Illegal data type on profile "+this+": "+type); } return false; } - - public boolean isValidArrayDataType(int index, int comps, int type, + + public boolean isValidArrayDataType(int index, int comps, int type, boolean isVertexAttribPointer, boolean throwException) { - String arrayName = getGLArrayName(index); - if(isGLES1()) { + final String arrayName = getGLArrayName(index); + if( isGLES1() ) { if(isVertexAttribPointer) { if(throwException) { throw new GLException("Illegal array type for "+arrayName+" on profile GLES1: VertexAttribPointer"); @@ -1086,7 +1285,7 @@ public class GLProfile { case GL.GL_FIXED: case GL.GL_FLOAT: break; - default: + default: if(throwException) { throw new GLException("Illegal data type for "+arrayName+" on profile GLES1: "+type); } @@ -1098,7 +1297,7 @@ public class GLProfile { case 3: case 4: break; - default: + default: if(throwException) { throw new GLException("Illegal component number for "+arrayName+" on profile GLES1: "+comps); } @@ -1112,7 +1311,7 @@ public class GLProfile { case GL.GL_FIXED: case GL.GL_FLOAT: break; - default: + default: if(throwException) { throw new GLException("Illegal data type for "+arrayName+" on profile GLES1: "+type); } @@ -1122,7 +1321,7 @@ public class GLProfile { case 0: case 3: break; - default: + default: if(throwException) { throw new GLException("Illegal component number for "+arrayName+" on profile GLES1: "+comps); } @@ -1135,7 +1334,7 @@ public class GLProfile { case GL.GL_FIXED: case GL.GL_FLOAT: break; - default: + default: if(throwException) { throw new GLException("Illegal data type for "+arrayName+" on profile GLES1: "+type); } @@ -1145,7 +1344,7 @@ public class GLProfile { case 0: case 4: break; - default: + default: if(throwException) { throw new GLException("Illegal component number for "+arrayName+" on profile GLES1: "+comps); } @@ -1153,7 +1352,7 @@ public class GLProfile { } break; } - } else if(isGLES2()) { + } else if( isGLES2() ) { // simply ignore !isVertexAttribPointer case, since it is simulated anyway .. switch(type) { case GL.GL_UNSIGNED_BYTE: @@ -1163,7 +1362,7 @@ public class GLProfile { case GL.GL_FLOAT: case GL.GL_FIXED: break; - default: + default: if(throwException) { throw new GLException("Illegal data type for "+arrayName+" on profile GLES2: "+type); } @@ -1177,7 +1376,7 @@ public class GLProfile { case 3: case 4: break; - default: + default: if(throwException) { throw new GLException("Illegal component number for "+arrayName+" on profile GLES2: "+comps); } @@ -1195,7 +1394,7 @@ public class GLProfile { case javax.media.opengl.GL2ES2.GL_UNSIGNED_INT: case javax.media.opengl.GL2.GL_DOUBLE: break; - default: + default: if(throwException) { throw new GLException("Illegal data type for "+arrayName+" on profile GL2: "+type); } @@ -1208,7 +1407,7 @@ public class GLProfile { case 3: case 4: break; - default: + default: if(throwException) { throw new GLException("Illegal component number for "+arrayName+" on profile GL2: "+comps); } @@ -1223,7 +1422,7 @@ public class GLProfile { case javax.media.opengl.GL2ES2.GL_INT: case javax.media.opengl.GL2.GL_DOUBLE: break; - default: + default: if(throwException) { throw new GLException("Illegal data type for "+arrayName+" on profile GL2: "+type); } @@ -1235,7 +1434,7 @@ public class GLProfile { case 3: case 4: break; - default: + default: if(throwException) { throw new GLException("Illegal component number for "+arrayName+" on profile GL2: "+comps); } @@ -1250,7 +1449,7 @@ public class GLProfile { case javax.media.opengl.GL2ES2.GL_INT: case javax.media.opengl.GL2.GL_DOUBLE: break; - default: + default: if(throwException) { throw new GLException("Illegal data type for "+arrayName+" on profile GL2: "+type); } @@ -1260,7 +1459,7 @@ public class GLProfile { case 0: case 3: break; - default: + default: if(throwException) { throw new GLException("Illegal component number for "+arrayName+" on profile GLES1: "+comps); } @@ -1278,7 +1477,7 @@ public class GLProfile { case javax.media.opengl.GL2ES2.GL_UNSIGNED_INT: case javax.media.opengl.GL2.GL_DOUBLE: break; - default: + default: if(throwException) { throw new GLException("Illegal data type for "+arrayName+" on profile GL2: "+type); } @@ -1289,7 +1488,7 @@ public class GLProfile { case 3: case 4: break; - default: + default: if(throwException) { throw new GLException("Illegal component number for "+arrayName+" on profile GL2: "+comps); } @@ -1303,7 +1502,7 @@ public class GLProfile { case javax.media.opengl.GL2ES2.GL_INT: case javax.media.opengl.GL2.GL_DOUBLE: break; - default: + default: if(throwException) { throw new GLException("Illegal data type for "+arrayName+" on profile GL2: "+type); } @@ -1316,7 +1515,7 @@ public class GLProfile { case 3: case 4: break; - default: + default: if(throwException) { throw new GLException("Illegal component number for "+arrayName+" on profile GL2: "+comps); } @@ -1329,52 +1528,120 @@ public class GLProfile { return true; } + @Override public String toString() { return "GLProfile[" + getName() + "/" + getImplName() + "."+(this.isHardwareRasterizer?"hw":"sw")+"]"; } private static /*final*/ boolean isAWTAvailable; - private static /*final*/ boolean hasDesktopGLFactory; + private static /*final*/ boolean hasDesktopGLFactory; private static /*final*/ boolean hasGL234Impl; private static /*final*/ boolean hasEGLFactory; - private static /*final*/ boolean hasGLES2Impl; + private static /*final*/ boolean hasGLES3Impl; private static /*final*/ boolean hasGLES1Impl; + private static /*final*/ Constructor<?> ctorGL234Impl; + private static /*final*/ Constructor<?> ctorGLES3Impl; + private static /*final*/ Constructor<?> ctorGLES1Impl; + private static /*final*/ Constructor<?> ctorGL234ProcAddr; + private static /*final*/ Constructor<?> ctorGLES3ProcAddr; + private static /*final*/ Constructor<?> ctorGLES1ProcAddr; private static /*final*/ GLDrawableFactoryImpl eglFactory = null; private static /*final*/ GLDrawableFactoryImpl desktopFactory = null; private static /*final*/ AbstractGraphicsDevice defaultDevice = null; - private static volatile boolean initialized = false; - private static RecursiveThreadGroupLock initLock = LockFactory.createRecursiveThreadGroupLock(); + private static boolean initialized = false; + private static final RecursiveThreadGroupLock initLock = LockFactory.createRecursiveThreadGroupLock(); + + private static final Class<?>[] ctorGLArgs = new Class<?>[] { GLProfile.class, jogamp.opengl.GLContextImpl.class }; + private static final Class<?>[] ctorProcArgs = new Class<?>[] { FunctionAddressResolver.class }; + private static final String GL4bcImplClassName = "jogamp.opengl.gl4.GL4bcImpl"; + private static final String GL4bcProcClassName = "jogamp.opengl.gl4.GL4bcProcAddressTable"; + private static final String GLES1ImplClassName = "jogamp.opengl.es1.GLES1Impl"; + private static final String GLES1ProcClassName = "jogamp.opengl.es1.GLES1ProcAddressTable"; + private static final String GLES3ImplClassName = "jogamp.opengl.es3.GLES3Impl"; + private static final String GLES3ProcClassName = "jogamp.opengl.es3.GLES3ProcAddressTable"; + + private static final Constructor<?> getCtor(final String clazzName, final boolean glObject, final ClassLoader cl) { + try { + return ReflectionUtil.getConstructor(clazzName, glObject ? ctorGLArgs : ctorProcArgs, cl); + } catch (Throwable t) { + if( DEBUG ) { + System.err.println("Catched: "+t.getMessage()); + t.printStackTrace(); + } + return null; + } + } + + private static final void initGLCtorImpl() { + final ClassLoader classloader = GLProfile.class.getClassLoader(); + + // depends on hasDesktopGLFactory + { + final Constructor<?> ctorGL = getCtor(GL4bcImplClassName, true, classloader); + final Constructor<?> ctorProc = null != ctorGL ? getCtor(GL4bcProcClassName, false, classloader) : null; + if( null != ctorProc ) { + hasGL234Impl = true; + ctorGL234Impl = ctorGL; + ctorGL234ProcAddr = ctorProc; + } else { + hasGL234Impl = false; + ctorGL234Impl = null; + ctorGL234ProcAddr = null; + } + } + + // depends on hasEGLFactory + { + final Constructor<?> ctorGL = getCtor(GLES1ImplClassName, true, classloader); + final Constructor<?> ctorProc = null != ctorGL ? getCtor(GLES1ProcClassName, false, classloader) : null; + if( null != ctorProc ) { + hasGLES1Impl = true; + ctorGLES1Impl = ctorGL; + ctorGLES1ProcAddr = ctorProc; + } else { + hasGLES1Impl = false; + ctorGLES1Impl = null; + ctorGLES1ProcAddr = null; + } + } + { + final Constructor<?> ctorGL = getCtor(GLES3ImplClassName, true, classloader); + final Constructor<?> ctorProc = null != ctorGL ? getCtor(GLES3ProcClassName, false, classloader) : null; + if( null != ctorProc ) { + hasGLES3Impl = true; + ctorGLES3Impl = ctorGL; + ctorGLES3ProcAddr = ctorProc; + } else { + hasGLES3Impl = false; + ctorGLES3Impl = null; + ctorGLES3ProcAddr = null; + } + } + } /** * Tries the profiles implementation and native libraries. */ - private static void initProfilesForDefaultDevices(boolean firstUIActionOnProcess) { - NativeWindowFactory.initSingleton(firstUIActionOnProcess); - + private static void initProfilesForDefaultDevices() { + NativeWindowFactory.initSingleton(); if(DEBUG) { - System.err.println("GLProfile.init firstUIActionOnProcess: "+ firstUIActionOnProcess - + ", thread: " + Thread.currentThread().getName()); + System.err.println("GLProfile.init - thread: " + Thread.currentThread().getName()); System.err.println(VersionUtil.getPlatformInfo()); System.err.println(GlueGenVersion.getInstance()); System.err.println(NativeWindowVersion.getInstance()); System.err.println(JoglVersion.getInstance()); } - ClassLoader classloader = GLProfile.class.getClassLoader(); + final ClassLoader classloader = GLProfile.class.getClassLoader(); isAWTAvailable = NativeWindowFactory.isAWTAvailable() && ReflectionUtil.isClassAvailable("javax.media.opengl.awt.GLCanvas", classloader) ; // JOGL - // depends on hasDesktopGLFactory - hasGL234Impl = ReflectionUtil.isClassAvailable("jogamp.opengl.gl4.GL4bcImpl", classloader); - - // depends on hasEGLFactory - hasGLES1Impl = ReflectionUtil.isClassAvailable("jogamp.opengl.es1.GLES1Impl", classloader); - hasGLES2Impl = ReflectionUtil.isClassAvailable("jogamp.opengl.es2.GLES2Impl", classloader); - + initGLCtorImpl(); + // // Iteration of desktop GL availability detection // utilizing the detected GL version in the shared context. @@ -1383,7 +1650,7 @@ public class GLProfile { // which will register at GLContext .. // GLDrawableFactory.initSingleton(); - + Throwable t=null; // if successfull it has a shared dummy drawable and context created try { @@ -1417,10 +1684,6 @@ public class GLProfile { } } else { defaultDesktopDevice = desktopFactory.getDefaultDevice(); - defaultDevice = defaultDesktopDevice; - if(DEBUG) { - System.err.println("Info: GLProfile.init - Default device is desktop derived: "+defaultDevice); - } } if ( ReflectionUtil.isClassAvailable("jogamp.opengl.egl.EGLDrawableFactory", classloader) ) { @@ -1428,23 +1691,10 @@ public class GLProfile { try { eglFactory = (GLDrawableFactoryImpl) GLDrawableFactory.getFactoryImpl(GLES2); if(null != eglFactory) { - final boolean isANGLE = ((jogamp.opengl.egl.EGLDrawableFactory)eglFactory).isANGLE(); - if(isANGLE && !enableANGLE) { - if(DEBUG) { - System.err.println("Info: GLProfile.init - EGL/ES2 ANGLE disabled"); - } - eglFactory.destroy(ShutdownType.COMPLETE); - eglFactory = null; - hasEGLFactory = false; - } else { - if(DEBUG && isANGLE) { - System.err.println("Info: GLProfile.init - EGL/ES2 ANGLE enabled"); - } - hasEGLFactory = true; - // update hasGLES1Impl, hasGLES2Impl based on EGL - hasGLES2Impl = null!=eglFactory.getGLDynamicLookupHelper(2) && hasGLES2Impl; - hasGLES1Impl = null!=eglFactory.getGLDynamicLookupHelper(1) && hasGLES1Impl; - } + hasEGLFactory = true; + // update hasGLES1Impl, hasGLES3Impl based on EGL + hasGLES3Impl = null!=eglFactory.getGLDynamicLookupHelper(2) && hasGLES3Impl; + hasGLES1Impl = null!=eglFactory.getGLDynamicLookupHelper(1) && hasGLES1Impl; } } catch (LinkageError le) { t=le; @@ -1462,9 +1712,9 @@ public class GLProfile { } } - final AbstractGraphicsDevice defaultEGLDevice; + final AbstractGraphicsDevice defaultEGLDevice; if(null == eglFactory) { - hasGLES2Impl = false; + hasGLES3Impl = false; hasGLES1Impl = false; defaultEGLDevice = null; if(DEBUG) { @@ -1472,33 +1722,42 @@ public class GLProfile { } } else { defaultEGLDevice = eglFactory.getDefaultDevice(); - if(null == defaultDevice) { - defaultDevice = defaultEGLDevice; - if(DEBUG) { - System.err.println("Info: GLProfile.init - Default device is EGL derived: "+defaultDevice); - } + } + + if( null != defaultDesktopDevice ) { + defaultDevice = defaultDesktopDevice; + if(DEBUG) { + System.err.println("Info: GLProfile.init - Default device is desktop derived: "+defaultDevice); + } + } else if ( null != defaultEGLDevice ) { + defaultDevice = defaultEGLDevice; + if(DEBUG) { + System.err.println("Info: GLProfile.init - Default device is EGL derived: "+defaultDevice); } + } else { + if(DEBUG) { + System.err.println("Info: GLProfile.init - Default device not available"); + } + defaultDevice = null; } - /** Should not be required .. but keep it here if simple probe on defaultDevice ain't enough. - final boolean addedDesktopProfile = initProfilesForDevice(defaultDesktopDevice); - final boolean addedEGLProfile = initProfilesForDevice(defaultEGLDevice); - final boolean addedAnyProfile = addedDesktopProfile || addedEGLProfile ; - */ - final boolean addedAnyProfile = initProfilesForDevice(defaultDevice); + // we require to initialize the EGL device 1st, if available + final boolean addedEGLProfile = null != defaultEGLDevice ? initProfilesForDevice(defaultEGLDevice) : false; + final boolean addedDesktopProfile = null != defaultDesktopDevice ? initProfilesForDevice(defaultDesktopDevice) : false; + final boolean addedAnyProfile = addedEGLProfile || addedDesktopProfile ; if(DEBUG) { - // System.err.println("GLProfile.init addedAnyProfile "+addedAnyProfile+" (desktop: "+addedDesktopProfile+", egl "+addedEGLProfile+")"); - System.err.println("GLProfile.init addedAnyProfile "+addedAnyProfile); - System.err.println("GLProfile.init isAWTAvailable "+isAWTAvailable); - System.err.println("GLProfile.init hasDesktopGLFactory "+hasDesktopGLFactory); - System.err.println("GLProfile.init hasGL234Impl "+hasGL234Impl); - System.err.println("GLProfile.init hasEGLFactory "+hasEGLFactory); - System.err.println("GLProfile.init hasGLES1Impl "+hasGLES1Impl); - System.err.println("GLProfile.init hasGLES2Impl "+hasGLES2Impl); - System.err.println("GLProfile.init defaultDevice "+defaultDevice); - System.err.println("GLProfile.init profile order "+array2String(GL_PROFILE_LIST_ALL)); - System.err.println(JoglVersion.getDefaultOpenGLInfo(null)); + System.err.println("GLProfile.init addedAnyProfile "+addedAnyProfile+" (desktop: "+addedDesktopProfile+", egl "+addedEGLProfile+")"); + System.err.println("GLProfile.init isAWTAvailable "+isAWTAvailable); + System.err.println("GLProfile.init hasDesktopGLFactory "+hasDesktopGLFactory); + System.err.println("GLProfile.init hasGL234Impl "+hasGL234Impl); + System.err.println("GLProfile.init hasEGLFactory "+hasEGLFactory); + System.err.println("GLProfile.init hasGLES1Impl "+hasGLES1Impl); + System.err.println("GLProfile.init hasGLES3Impl "+hasGLES3Impl); + System.err.println("GLProfile.init defaultDevice "+defaultDevice); + System.err.println("GLProfile.init defaultDevice Desktop "+defaultDesktopDevice); + System.err.println("GLProfile.init defaultDevice EGL "+defaultEGLDevice); + System.err.println("GLProfile.init profile order "+array2String(GL_PROFILE_LIST_ALL)); } } @@ -1527,7 +1786,7 @@ public class GLProfile { boolean isSet = GLContext.getAvailableGLVersionsSet(device); if(DEBUG) { - System.err.println("Info: GLProfile.initProfilesForDevice: "+device+", isSet "+isSet); + System.err.println("Info: GLProfile.initProfilesForDevice: "+device+" ("+device.getClass().getName()+"), isSet "+isSet+", hasDesktopGLFactory "+hasDesktopGLFactory+", hasEGLFactory "+hasEGLFactory); } if(isSet) { // Avoid recursion and check whether impl. is sane! @@ -1541,9 +1800,9 @@ public class GLProfile { boolean addedDesktopProfile = false; boolean addedEGLProfile = false; - + final boolean deviceIsDesktopCompatible = hasDesktopGLFactory && desktopFactory.getIsDeviceCompatible(device); - + if( deviceIsDesktopCompatible ) { // 1st pretend we have all Desktop and EGL profiles .. computeProfileMap(device, true /* desktopCtxUndef*/, true /* esCtxUndef */); @@ -1568,11 +1827,11 @@ public class GLProfile { } addedDesktopProfile = computeProfileMap(device, false /* desktopCtxUndef*/, false /* esCtxUndef */); } - + final boolean deviceIsEGLCompatible = hasEGLFactory && eglFactory.getIsDeviceCompatible(device); - - // also test GLES1 and GLES2 on desktop, since we have implementations / emulations available. - if( deviceIsEGLCompatible && ( hasGLES2Impl || hasGLES1Impl ) ) { + + // also test GLES1, GLES2 and GLES3 on desktop, since we have implementations / emulations available. + if( deviceIsEGLCompatible && ( hasGLES3Impl || hasGLES1Impl ) ) { // 1st pretend we have all EGL profiles .. computeProfileMap(device, false /* desktopCtxUndef*/, true /* esCtxUndef */); @@ -1588,36 +1847,18 @@ public class GLProfile { } if(!eglSharedCtxAvail) { // Remark: On Windows there is a libEGL.dll delivered w/ Chrome 15.0.874.121m and Firefox 8.0.1 - // but it seems even EGL.eglInitialize(eglDisplay, null, null) + // but it seems even EGL.eglInitialize(eglDisplay, null, null) // fails in some scenarios (eg VirtualBox 4.1.6) w/ EGL error 0x3001 (EGL_NOT_INITIALIZED). hasEGLFactory = false; - hasGLES2Impl = false; + hasGLES3Impl = false; hasGLES1Impl = false; } if (DEBUG) { System.err.println("GLProfile.initProfilesForDevice: "+device+": egl Shared Ctx "+eglSharedCtxAvail); } - if( hasGLES2Impl ) { - // The native ES2 impl. overwrites a previous mapping using 'ES2 compatibility' by a desktop profile - GLContext.mapAvailableGLVersion(device, - 2, GLContext.CTX_PROFILE_ES, - 2, 0, GLContext.CTX_PROFILE_ES|GLContext.CTX_IMPL_ES2_COMPAT); - if (DEBUG) { - System.err.println(GLContext.getThreadName() + ": initProfilesForDeviceCritical-MapVersionsAvailable HAVE: ES2 -> ES 2.0"); - } - } - if( hasGLES1Impl ) { - // Always favor the native ES1 impl. - GLContext.mapAvailableGLVersion(device, - 1, GLContext.CTX_PROFILE_ES, - 1, 0, GLContext.CTX_PROFILE_ES); - if (DEBUG) { - System.err.println(GLContext.getThreadName() + ": initProfilesForDeviceCritical-MapVersionsAvailable HAVE: ES1 -> ES 1.0"); - } - } addedEGLProfile = computeProfileMap(device, false /* desktopCtxUndef*/, false /* esCtxUndef */); - } - + } + if( !addedDesktopProfile && !addedEGLProfile ) { setProfileMap(device, new HashMap<String /*GLProfile_name*/, GLProfile>()); // empty if(DEBUG) { @@ -1627,7 +1868,7 @@ public class GLProfile { System.err.println("GLProfile: desktoplFactory "+desktopFactory); System.err.println("GLProfile: eglFactory "+eglFactory); System.err.println("GLProfile: hasGLES1Impl "+hasGLES1Impl); - System.err.println("GLProfile: hasGLES2Impl "+hasGLES2Impl); + System.err.println("GLProfile: hasGLES3Impl "+hasGLES3Impl); } } @@ -1705,31 +1946,31 @@ public class GLProfile { final boolean isHardwareRasterizer[] = new boolean[1]; GLProfile defaultGLProfileAny = null; GLProfile defaultGLProfileHW = null; - HashMap<String, GLProfile> _mappedProfiles = new HashMap<String, GLProfile>(GL_PROFILE_LIST_ALL.length + 1 /* default */); + final HashMap<String, GLProfile> _mappedProfiles = new HashMap<String, GLProfile>(GL_PROFILE_LIST_ALL.length + 1 /* default */); for(int i=0; i<GL_PROFILE_LIST_ALL.length; i++) { - String profile = GL_PROFILE_LIST_ALL[i]; - String profileImpl = computeProfileImpl(device, profile, desktopCtxUndef, esCtxUndef, isHardwareRasterizer); - if(null!=profileImpl) { + final String profile = GL_PROFILE_LIST_ALL[i]; + final String profileImpl = computeProfileImpl(device, profile, desktopCtxUndef, esCtxUndef, isHardwareRasterizer); + if( null != profileImpl ) { final GLProfile glProfile; - if(profile.equals(profileImpl)) { + if( profile.equals( profileImpl ) ) { glProfile = new GLProfile(profile, null, isHardwareRasterizer[0]); } else { - final GLProfile _mglp = _mappedProfiles.get(profileImpl); - if(null == _mglp) { - throw new InternalError("XXX0"); + 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]); } _mappedProfiles.put(profile, glProfile); if (DEBUG) { - System.err.println("GLProfile.init map "+glProfile+" on devide "+device.getConnection()); + System.err.println("GLProfile.init map "+glProfile+" on device "+device.getConnection()); } - if(null==defaultGLProfileHW && isHardwareRasterizer[0]) { + if( null == defaultGLProfileHW && isHardwareRasterizer[0] ) { defaultGLProfileHW=glProfile; if (DEBUG) { System.err.println("GLProfile.init map defaultHW "+glProfile+" on device "+device.getConnection()); } - } else if(null==defaultGLProfileAny) { + } else if( null == defaultGLProfileAny ) { defaultGLProfileAny=glProfile; if (DEBUG) { System.err.println("GLProfile.init map defaultAny "+glProfile+" on device "+device.getConnection()); @@ -1741,9 +1982,9 @@ public class GLProfile { } } } - if(null!=defaultGLProfileHW) { + if( null != defaultGLProfileHW ) { _mappedProfiles.put(GL_DEFAULT, defaultGLProfileHW); - } else if(null!=defaultGLProfileAny) { + } else if( null != defaultGLProfileAny ) { _mappedProfiles.put(GL_DEFAULT, defaultGLProfileAny); } setProfileMap(device, _mappedProfiles); @@ -1754,10 +1995,6 @@ public class GLProfile { * Returns the profile implementation */ private static String computeProfileImpl(AbstractGraphicsDevice device, String profile, boolean desktopCtxUndef, boolean esCtxUndef, boolean isHardwareRasterizer[]) { - // OSX GL3.. doesn't support GLSL<150, - // hence GL2ES2 and GL2GL3 need to be mapped on GL2 on OSX for GLSL compatibility. - final boolean isOSX = Platform.OS_TYPE == Platform.OSType.MACOS; - if (GL2ES1.equals(profile)) { final boolean es1HardwareRasterizer[] = new boolean[1]; final boolean gles1Available = hasGLES1Impl && ( esCtxUndef || GLContext.isGLES1Available(device, es1HardwareRasterizer) ); @@ -1785,104 +2022,114 @@ public class GLProfile { } } else if (GL2ES2.equals(profile)) { final boolean es2HardwareRasterizer[] = new boolean[1]; - final boolean gles2Available = hasGLES2Impl && ( esCtxUndef || GLContext.isGLES2Available(device, es2HardwareRasterizer) ); + final boolean gles2Available = hasGLES3Impl && ( esCtxUndef || GLContext.isGLES2Available(device, es2HardwareRasterizer) ); final boolean gles2HWAvailable = gles2Available && es2HardwareRasterizer[0] ; + final boolean es3HardwareRasterizer[] = new boolean[1]; + final boolean gles3Available = hasGLES3Impl && ( esCtxUndef || GLContext.isGLES3Available(device, es3HardwareRasterizer) ); + final boolean gles3HWAvailable = gles3Available && es3HardwareRasterizer[0] ; if(hasGL234Impl) { - if(!isOSX) { - if(GLContext.isGL4bcAvailable(device, isHardwareRasterizer)) { - if(!gles2HWAvailable || isHardwareRasterizer[0]) { - return GL4bc; - } + if(GLContext.isGL4Available(device, isHardwareRasterizer)) { + if( (!gles3HWAvailable && !gles2HWAvailable ) || isHardwareRasterizer[0] ) { + return GL4; } - if(GLContext.isGL4Available(device, isHardwareRasterizer)) { - if(!gles2HWAvailable || isHardwareRasterizer[0]) { - return GL4; - } + } + if(GLContext.isGL4bcAvailable(device, isHardwareRasterizer)) { + if( (!gles3HWAvailable && !gles2HWAvailable ) || isHardwareRasterizer[0] ) { + return GL4bc; } - if(GLContext.isGL3bcAvailable(device, isHardwareRasterizer)) { - if(!gles2HWAvailable || isHardwareRasterizer[0]) { - return GL3bc; - } + } + if(GLContext.isGL3Available(device, isHardwareRasterizer)) { + if( (!gles3HWAvailable && !gles2HWAvailable ) || isHardwareRasterizer[0] ) { + return GL3; } - if(GLContext.isGL3Available(device, isHardwareRasterizer)) { - if(!gles2HWAvailable || isHardwareRasterizer[0]) { - return GL3; - } + } + if(GLContext.isGL3bcAvailable(device, isHardwareRasterizer)) { + if( (!gles3HWAvailable && !gles2HWAvailable ) || isHardwareRasterizer[0] ) { + return GL3bc; } } if(desktopCtxUndef || GLContext.isGL2Available(device, isHardwareRasterizer)) { - if(!gles2HWAvailable || isHardwareRasterizer[0]) { + if( (!gles3HWAvailable && !gles2HWAvailable ) || isHardwareRasterizer[0] ) { return GL2; } } } + if(gles3Available && ( !gles2HWAvailable || gles3HWAvailable ) ) { + isHardwareRasterizer[0] = es3HardwareRasterizer[0]; + return GLES3; + } if(gles2Available) { isHardwareRasterizer[0] = es2HardwareRasterizer[0]; return GLES2; } + } else if (GL4ES3.equals(profile)) { + final boolean gles3CompatAvail = GLContext.isGLES3CompatibleAvailable(device); + if( desktopCtxUndef || esCtxUndef || gles3CompatAvail ) { + final boolean es3HardwareRasterizer[] = new boolean[1]; + final boolean gles3Available = hasGLES3Impl && ( esCtxUndef || GLContext.isGLES3Available(device, es3HardwareRasterizer) ); + final boolean gles3HWAvailable = gles3Available && es3HardwareRasterizer[0] ; + if(hasGL234Impl) { + if(GLContext.isGL4Available(device, isHardwareRasterizer)) { + if(!gles3HWAvailable || isHardwareRasterizer[0]) { + return GL4; + } + } + if( GLContext.isGL4bcAvailable(device, isHardwareRasterizer)) { + if(!gles3HWAvailable || isHardwareRasterizer[0]) { + 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)) { if(hasGL234Impl) { - if(!isOSX && GLContext.isGL4bcAvailable(device, isHardwareRasterizer)) { + if( GLContext.isGL4bcAvailable(device, isHardwareRasterizer)) { return GL4bc; - } else if(!isOSX && GLContext.isGL4Available(device, isHardwareRasterizer)) { + } else if( GLContext.isGL4Available(device, isHardwareRasterizer)) { return GL4; - } else if(!isOSX && GLContext.isGL3bcAvailable(device, isHardwareRasterizer)) { + } else if( GLContext.isGL3bcAvailable(device, isHardwareRasterizer)) { return GL3bc; - } else if(!isOSX && GLContext.isGL3Available(device, isHardwareRasterizer)) { + } else if( GLContext.isGL3Available(device, isHardwareRasterizer)) { return GL3; } else if(desktopCtxUndef || GLContext.isGL2Available(device, isHardwareRasterizer)) { return GL2; } } } else if(GL4bc.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL4bcAvailable(device, isHardwareRasterizer))) { - return GL4bc; + return desktopCtxUndef ? GL4bc : GLContext.getAvailableGLProfileName(device, 4, GLContext.CTX_PROFILE_COMPAT); } else if(GL4.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL4Available(device, isHardwareRasterizer))) { - return GL4; + return desktopCtxUndef ? GL4 : GLContext.getAvailableGLProfileName(device, 4, GLContext.CTX_PROFILE_CORE); } else if(GL3bc.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL3bcAvailable(device, isHardwareRasterizer))) { - return GL3bc; + return desktopCtxUndef ? GL3bc : GLContext.getAvailableGLProfileName(device, 3, GLContext.CTX_PROFILE_COMPAT); } else if(GL3.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL3Available(device, isHardwareRasterizer))) { - return GL3; + return desktopCtxUndef ? GL3 : GLContext.getAvailableGLProfileName(device, 3, GLContext.CTX_PROFILE_CORE); } else if(GL2.equals(profile) && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL2Available(device, isHardwareRasterizer))) { - return GL2; - } else if(GLES2.equals(profile) && hasGLES2Impl && ( esCtxUndef || GLContext.isGLES2Available(device, isHardwareRasterizer))) { - return GLES2; - /** - * TODO: GLES2_TRUE_DESKTOP (see: GLContextImpl, GLProfile) - * Hack to enable GLES2 for desktop profiles w/ ES2 compatibility, - * however .. a consequent implementation would need to have all GL2ES2 - * implementing profile to also implement GLES2! - * Let's rely on GL2ES2 and let the user/impl. query isGLES2Compatible() - } else if(GLES2.equals(profile)) { - if(hasGL234Impl || hasGLES2Impl) { - if(esCtxUndef) { - return GLES2; - } - return GLContext.getAvailableGLProfile(device, 2, GLContext.CTX_PROFILE_ES); - } - */ + return desktopCtxUndef ? GL2 : GLContext.getAvailableGLProfileName(device, 2, GLContext.CTX_PROFILE_COMPAT); + } else if(GLES3.equals(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))) { + return esCtxUndef ? GLES2 : GLContext.getAvailableGLProfileName(device, 2, GLContext.CTX_PROFILE_ES); } else if(GLES1.equals(profile) && hasGLES1Impl && ( esCtxUndef || GLContext.isGLES1Available(device, isHardwareRasterizer))) { - return GLES1; + return esCtxUndef ? GLES1 : GLContext.getAvailableGLProfileName(device, 1, GLContext.CTX_PROFILE_ES); } return null; } - private static String getGLImplBaseClassName(String profileImpl) { - if ( GL4bc.equals(profileImpl) || - GL4.equals(profileImpl) || - GL3bc.equals(profileImpl) || - GL3.equals(profileImpl) || - GL2.equals(profileImpl) ) { - return "jogamp.opengl.gl4.GL4bc"; - } else if(GLES1.equals(profileImpl) || GL2ES1.equals(profileImpl)) { - return "jogamp.opengl.es1.GLES1"; - } else if(GLES2.equals(profileImpl) || GL2ES2.equals(profileImpl)) { - return "jogamp.opengl.es2.GLES2"; - } else { - throw new GLException("unsupported profile \"" + profileImpl + "\""); - } - } - - private static /*final*/ HashMap<String /*device_connection*/, HashMap<String /*GLProfile_name*/, GLProfile>> deviceConn2ProfileMap = + private static /*final*/ HashMap<String /*device_connection*/, HashMap<String /*GLProfile_name*/, GLProfile>> deviceConn2ProfileMap = new HashMap<String /*device_connection*/, HashMap<String /*GLProfile_name*/, GLProfile>>(); /** @@ -1893,17 +2140,22 @@ public class GLProfile { * * @param device the key 'device -> GLProfiles-Map' * @param throwExceptionOnZeroProfile true if <code>GLException</code> shall be thrown in case of no mapped profile, otherwise false. - * @return the GLProfile HashMap if exists, otherwise null + * @return the GLProfile HashMap if exists, otherwise null * @throws GLException if no profile for the given device is available. */ - private static HashMap<String /*GLProfile_name*/, GLProfile> getProfileMap(AbstractGraphicsDevice device, boolean throwExceptionOnZeroProfile) - throws GLException + private static HashMap<String /*GLProfile_name*/, GLProfile> getProfileMap(AbstractGraphicsDevice device, boolean throwExceptionOnZeroProfile) + throws GLException { initSingleton(); + if(null==defaultDevice) { // avoid NPE and notify of incomplete initialization + throw new GLException("No default device available"); + } + if(null==device) { device = defaultDevice; } + final String deviceKey = device.getUniqueID(); HashMap<String /*GLProfile_name*/, GLProfile> map = deviceConn2ProfileMap.get(deviceKey); if( null != map ) { diff --git a/src/jogl/classes/javax/media/opengl/GLRunnable.java b/src/jogl/classes/javax/media/opengl/GLRunnable.java index 1ae1c9b22..ad68662ce 100644 --- a/src/jogl/classes/javax/media/opengl/GLRunnable.java +++ b/src/jogl/classes/javax/media/opengl/GLRunnable.java @@ -3,14 +3,14 @@ * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR @@ -20,12 +20,12 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ - + package javax.media.opengl; /** @@ -33,8 +33,8 @@ package javax.media.opengl; * Declares a one-shot OpenGL command usable for injection * via {@link GLAutoDrawable#invoke(boolean, javax.media.opengl.GLRunnable)}.<br> * {@link GLAutoDrawable} executes the GLRunnables within it's {@link GLAutoDrawable#display() display()} - * method after all registered {@link GLEventListener}s - * {@link GLEventListener#display(GLAutoDrawable) display(GLAutoDrawable)} + * method after all registered {@link GLEventListener}s + * {@link GLEventListener#display(GLAutoDrawable) display(GLAutoDrawable)} * methods has been called. * </p> * <p> @@ -44,13 +44,13 @@ package javax.media.opengl; * This might be useful to inject OpenGL commands from an I/O event listener. * </p> */ -public interface GLRunnable { +public interface GLRunnable { /** * @param drawable the associated drawable and current context for this call * @return true if the GL [back] framebuffer remains intact by this runnable, otherwise false. * If returning false {@link GLAutoDrawable} will call - * {@link GLEventListener#display(GLAutoDrawable) display(GLAutoDrawable)} - * of all registered {@link GLEventListener}s once more. + * {@link GLEventListener#display(GLAutoDrawable) display(GLAutoDrawable)} + * of all registered {@link GLEventListener}s once more. * @see GLRunnable */ boolean run(GLAutoDrawable drawable); diff --git a/src/jogl/classes/javax/media/opengl/GLRunnable2.java b/src/jogl/classes/javax/media/opengl/GLRunnable2.java new file mode 100644 index 000000000..5f0393257 --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/GLRunnable2.java @@ -0,0 +1,44 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package javax.media.opengl; + +/** + * <p> + * Declares a one-shot OpenGL command. + * </p> + */ +public interface GLRunnable2<T,U> { + /** + * @param gl a current GL object + * @param args custom arguments + * @return the desired object + */ + T run(GL gl, U args); +} + diff --git a/src/jogl/classes/javax/media/opengl/GLSharedContextSetter.java b/src/jogl/classes/javax/media/opengl/GLSharedContextSetter.java new file mode 100644 index 000000000..2ea4e4cd6 --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/GLSharedContextSetter.java @@ -0,0 +1,157 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package javax.media.opengl; + +/** + * Adds capabilities to set a shared {@link GLContext} directly or via an {@link GLAutoDrawable}. + * <p> + * Sharing of server-side OpenGL objects such as buffer objects, e.g. VBOs, + * 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. + * </p> + * <p> + * Using the nearest or same {@link GLCapabilitiesImmutable#getVisualID(javax.media.nativewindow.VisualIDHolder.VIDType) visual ID} + * or {@link GLCapabilitiesImmutable caps} across the shared {@link GLDrawable}s will yield best compatibility. + * </p> + * <h5><a name="lifecycle">Lifecycle Considerations</a></h5> + * <p> + * After shared objects are created on the <i>master</i>, the OpenGL pipeline + * might need to be synchronized w/ the <i>slaves</i>, e.g. via {@link GL#glFinish()}. + * 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> + * </p> + * <p> + * 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> + * <p> + * To simplify above lifecycle issues, one may use a {@link GLDrawableFactory#createDummyDrawable(javax.media.nativewindow.AbstractGraphicsDevice, boolean, GLCapabilitiesImmutable, GLCapabilitiesChooser) dummy} + * {@link GLDrawable} and it's {@link GLContext} as the <i>master</i> of all shared <i>slave</i> {@link GLContext}. + * Since this <i>dummy instance</i> does not depend on any native windowing system, it can be controlled easily w/o being <i>in sight</i>.<br> + * Below code creates a {@link GLAutoDrawable} based on a <i>dummy GLDrawable</i>: + * <pre> + // GLProfile and GLCapabilities should be equal across all shared GL drawable/context. + final GLCapabilitiesImmutable caps = ... ; + final GLProfile glp = caps.getGLProfile(); + .. + final boolean createNewDevice = true; // use 'own' display device! + final GLAutoDrawable sharedDrawable = GLDrawableFactory.getFactory(glp).createDummyAutoDrawable(null, createNewDevice, caps, null); + sharedDrawable.display(); // triggers GLContext object creation and native realization. + ... + // Later a shared 'slave' can be created e.g.: + GLWindow glad = GLWindow.create(caps); // or any other GLAutoDrawable supporting GLSharedContextSetter + glad.setSharedAutoDrawable(sharedDrawable); + glad.addGLEventListener(..); + glad.setVisible(true); // GLWindow creation .. + * </pre> + * </p> + * <h5><a name="driverissues">Known Driver Issues</a></h5> + * <h7><a name="intelmesa">Intel's Mesa >= 9.1.2 Backend for [Sandybridge/Ivybridge] on GNU/Linux</a></h7> + * <p> + * <pre> + * Error: 'intel_do_flush_locked: No such file or directory' + * JogAmp: https://jogamp.org/bugzilla/show_bug.cgi?id=873 + * freedesktop.org: https://bugs.freedesktop.org/show_bug.cgi?id=41736#c8 + * </pre> + * Shared context seems not to be supported w/ lock-free bound X11 display connections + * per OpenGL drawable/context. The error message above is thrown in this case. + * Hence the driver bug renders shared context use w/ JOGL impossible. + * </p> + * <h7><a name="hisilicon">Hisilicon's Immersion.16 on Android</a></h7> + * <p> + * We failed to create a shared ES2 context on another thread. + * </p> + */ +public interface GLSharedContextSetter extends GLAutoDrawable { + /** + * Returns true if all {@link GLEventListener} are initialized, otherwise false. + * @deprecated Promote method to {@link GLAutoDrawable} + */ + boolean areAllGLEventListenerInitialized(); + + /** + * Specifies an {@link GLContext OpenGL context}, which shall be shared by this {@link GLAutoDrawable}'s {@link GLContext}. + * <p> + * Since the {@link GLDrawable drawable} and {@link GLContext context} is created + * at {@link GLAutoDrawable#initialization GLAutoDrawable initialization} + * this method shall be called beforehand to have any effect. + * </p> + * <p> + * A set <i>sharedContext</i> will block context creation, i.e. {@link GLAutoDrawable#initialization GLAutoDrawable initialization}, + * as long it is not {@link GLContext#isCreated() created natively}. + * </p> + * <p> + * See <a href="#lifecycle">Lifecycle Considerations</a>. + * </p> + * + * @param sharedContext The OpenGL context to be shared by this {@link GLAutoDrawable}'s {@link GLContext}. + * @throws IllegalStateException if a {@link #setSharedContext(GLContext) shared GLContext} + * or {@link #setSharedAutoDrawable(GLAutoDrawable) shared GLAutoDrawable} is already set, + * the given sharedContext is null or equal to this {@link GLAutoDrawable}'s context. + * @see #setSharedAutoDrawable(GLAutoDrawable) + */ + void setSharedContext(GLContext sharedContext) throws IllegalStateException; + + /** + * Specifies an {@link GLAutoDrawable}, which {@link GLContext OpenGL context} shall be shared by this {@link GLAutoDrawable}'s {@link GLContext}. + * <p> + * Since the {@link GLDrawable drawable} and {@link GLContext context} is created + * at {@link GLAutoDrawable#initialization GLAutoDrawable initialization} + * this method shall be called beforehand to have any effect. + * </p> + * <p> + * A set <i>sharedAutoDrawable</i> will block context creation, i.e. {@link GLAutoDrawable#initialization GLAutoDrawable initialization}, + * as long it's + * <ul> + * <li>{@link GLContext} is <code>null</code>, or</li> + * <li>{@link GLContext} has not been {@link GLContext#isCreated() created natively}, or</li> + * <li>{@link GLEventListener} are <i>not</i> {@link GLSharedContextSetter#areAllGLEventListenerInitialized() completely initialized}</li> + * </ul> + * </p> + * <p> + * See <a href="#lifecycle">Lifecycle Considerations</a>. + * </p> + * + * @param sharedContext The GLAutoDrawable, which OpenGL context shall be shared by this {@link GLAutoDrawable}'s {@link GLContext}. + * @throws IllegalStateException if a {@link #setSharedContext(GLContext) shared GLContext} + * or {@link #setSharedAutoDrawable(GLAutoDrawable) shared GLAutoDrawable} is already set, + * the given sharedAutoDrawable is null or equal to this GLAutoDrawable. + * @see #setSharedContext(GLContext) + */ + void setSharedAutoDrawable(GLAutoDrawable sharedAutoDrawable) throws IllegalStateException; +} diff --git a/src/jogl/classes/javax/media/opengl/GLUniformData.java b/src/jogl/classes/javax/media/opengl/GLUniformData.java index 475ff4546..412bfb0a9 100644 --- a/src/jogl/classes/javax/media/opengl/GLUniformData.java +++ b/src/jogl/classes/javax/media/opengl/GLUniformData.java @@ -3,6 +3,9 @@ package javax.media.opengl; import java.nio.*; +import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.math.FloatUtil; + public class GLUniformData { /** @@ -69,14 +72,34 @@ public class GLUniformData { public IntBuffer intBufferValue() { return (IntBuffer)data; }; public FloatBuffer floatBufferValue() { return (FloatBuffer)data; }; + public StringBuilder toString(StringBuilder sb) { + if(null == sb) { + sb = new StringBuilder(); + } + sb.append("GLUniformData[name ").append(name). + append(", location ").append(location). + append(", size ").append(rows).append("x").append(columns). + append(", count ").append(count). + append(", data "); + if(isMatrix() && data instanceof FloatBuffer) { + sb.append("\n"); + final FloatBuffer fb = (FloatBuffer)getBuffer(); + for(int i=0; i<count; i++) { + FloatUtil.matrixToString(sb, i+": ", "%10.5f", fb, i*rows*columns, rows, columns, false); + sb.append(",\n"); + } + } else if(isBuffer()) { + Buffers.toString(sb, null, getBuffer()); + } else { + sb.append(data); + } + sb.append("]"); + return sb; + } + + @Override public String toString() { - return "GLUniformData[name "+name+ - ", location "+location+ - ", size "+rows+"*"+columns+ - ", count "+count+ - ", matrix "+isMatrix+ - ", data "+data+ - "]"; + return toString(null).toString(); } private void init(String name, int rows, int columns, Object data) { @@ -125,9 +148,25 @@ public class GLUniformData { public int getLocation() { return location; } /** - * Sets the determined location of the shader uniform. + * Sets the given location of the shader uniform. + * @return the given location */ - public GLUniformData setLocation(int location) { this.location=location; return this; } + public int setLocation(int location) { this.location=location; return location; } + + /** + * Retrieves the location of the shader uniform from the linked shader program. + * <p> + * No validation is performed within the implementation. + * </p> + * @param gl + * @param program + * @return ≥0 denotes a valid uniform location as found and used in the given shader program. + * <0 denotes an invalid location, i.e. not found or used in the given shader program. + */ + public int setLocation(GL2ES2 gl, int program) { + location = gl.glGetUniformLocation(program, name); + return location; + } public Object getObject() { return data; diff --git a/src/jogl/classes/javax/media/opengl/Threading.java b/src/jogl/classes/javax/media/opengl/Threading.java index 4788f9cf6..7503e9cf7 100644 --- a/src/jogl/classes/javax/media/opengl/Threading.java +++ b/src/jogl/classes/javax/media/opengl/Threading.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2012 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -42,7 +42,7 @@ package javax.media.opengl; import jogamp.opengl.ThreadingImpl; -/** This API provides access to the threading model for the implementation of +/** This API provides access to the threading model for the implementation of the classes in this package. <P> @@ -59,12 +59,12 @@ import jogamp.opengl.ThreadingImpl; Due to these limitations, and due to the inherent multithreading in the Java platform (in particular, in the Abstract Window - Toolkit), it is often necessary to limit the multithreading - occurring in the typical application using the OpenGL API. + Toolkit), it is often necessary to limit the multithreading + occurring in the typical application using the OpenGL API. <P> - In the current reference implementation, for instance, multithreading + In the current reference implementation, for instance, multithreading has been limited by forcing all OpenGL-related work for GLAutoDrawables on to a single thread. In other words, if an application uses only the @@ -93,9 +93,9 @@ import jogamp.opengl.ThreadingImpl; This class also provides mechanisms for querying whether this internal serialization of OpenGL work is in effect, and a - programmatic way of disabling it. In the current reference - implementation it is enabled by default, although it could be - disabled in the future if OpenGL drivers become more robust on + programmatic way of disabling it. In the current reference + implementation it is enabled by default, although it could be + disabled in the future if OpenGL drivers become more robust on all platforms. <P> @@ -113,7 +113,7 @@ import jogamp.opengl.ThreadingImpl; platforms, and also the default behavior older releases) -Djogl.1thread=worker Enable single-threading of OpenGL work on newly-created worker thread (not suitable for Mac OS X or X11 platforms, and risky on Windows in applet environments) - </PRE> + </PRE> */ public class Threading { @@ -121,9 +121,9 @@ public class Threading { /** No reason to ever instantiate this class */ private Threading() {} - /** 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 + /** 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 in this implementation. Users are strongly discouraged from calling this method unless they are aware of all of the consequences and are prepared to enforce some amount of @@ -133,7 +133,7 @@ public class Threading { GLPbuffer. Currently there is no supported way to re-enable it once disabled, partly to discourage careless use of this method. This method should be called as early as possible in an - application. */ + application. */ public static final void disableSingleThreading() { ThreadingImpl.disableSingleThreading(); } @@ -145,11 +145,11 @@ public class Threading { } /** Indicates whether the current thread is the designated toolkit thread, - if such semantics exists. */ + if such semantics exists. */ public static final boolean isToolkitThread() throws GLException { 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 @@ -166,12 +166,12 @@ public class Threading { thread (i.e., if <code>isOpenGLThread()</code> returns false). It is up to the end user to check to see whether the current thread is the OpenGL thread and either execute the - Runnable directly or perform the work inside it. + Runnable directly or perform the work inside it. **/ public static final void invokeOnOpenGLThread(boolean wait, Runnable r) throws GLException { ThreadingImpl.invokeOnOpenGLThread(wait, r); } - + /** * If {@link #isSingleThreaded()} <b>and</b> not {@link #isOpenGLThread()} * <b>and</b> the <code>lock</code> is not being hold by this thread, @@ -179,14 +179,14 @@ public class Threading { * <p> * Otherwise invoke Runnable <code>r</code> on the current thread. * </p> - * - * @param wait set to true for waiting until Runnable <code>r</code> is finished, otherwise false. + * + * @param wait set to true for waiting until Runnable <code>r</code> is finished, otherwise false. * @param r the Runnable to be executed * @param lock optional lock object to be tested * @throws GLException */ public static final void invoke(boolean wait, Runnable r, Object lock) throws GLException { - if ( isSingleThreaded() && !isOpenGLThread() && + if ( isSingleThreaded() && !isOpenGLThread() && ( null == lock || !Thread.holdsLock(lock) ) ) { invokeOnOpenGLThread(wait, r); } else { diff --git a/src/jogl/classes/javax/media/opengl/TraceGL2.java b/src/jogl/classes/javax/media/opengl/TraceGL2.java new file mode 100644 index 000000000..c577332e9 --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/TraceGL2.java @@ -0,0 +1,23 @@ +package javax.media.opengl; + +import java.io.PrintStream; + +/** + * <p> + * Composable pipeline which wraps an underlying {@link GL} implementation, + * providing tracing information to a user-specified {@link java.io.PrintStream} + * before and after each OpenGL method call. + * </p> + * <p> + * Sample code which installs this pipeline, manual: + * <pre> + * gl = drawable.setGL(new TraceGL(drawable.getGL(), System.err)); + * </pre> + * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}. + * </p> + */ +public class TraceGL2 extends TraceGL4bc { + public TraceGL2(GL2 downstream, PrintStream stream) { + super((GL4bc)downstream, stream); + } +} diff --git a/src/jogl/classes/javax/media/opengl/TraceGL3.java b/src/jogl/classes/javax/media/opengl/TraceGL3.java new file mode 100644 index 000000000..5fccf40c7 --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/TraceGL3.java @@ -0,0 +1,23 @@ +package javax.media.opengl; + +import java.io.PrintStream; + +/** + * <p> + * Composable pipeline which wraps an underlying {@link GL} implementation, + * providing tracing information to a user-specified {@link java.io.PrintStream} + * before and after each OpenGL method call. + * </p> + * <p> + * Sample code which installs this pipeline, manual: + * <pre> + * gl = drawable.setGL(new TraceGL(drawable.getGL(), System.err)); + * </pre> + * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}. + * </p> + */ +public class TraceGL3 extends TraceGL4bc { + public TraceGL3(GL3 downstream, PrintStream stream) { + super((GL4bc)downstream, stream); + } +} diff --git a/src/jogl/classes/javax/media/opengl/TraceGL3bc.java b/src/jogl/classes/javax/media/opengl/TraceGL3bc.java new file mode 100644 index 000000000..84f537f60 --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/TraceGL3bc.java @@ -0,0 +1,23 @@ +package javax.media.opengl; + +import java.io.PrintStream; + +/** + * <p> + * Composable pipeline which wraps an underlying {@link GL} implementation, + * providing tracing information to a user-specified {@link java.io.PrintStream} + * before and after each OpenGL method call. + * </p> + * <p> + * Sample code which installs this pipeline, manual: + * <pre> + * gl = drawable.setGL(new TraceGL(drawable.getGL(), System.err)); + * </pre> + * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}. + * </p> + */ +public class TraceGL3bc extends TraceGL4bc { + public TraceGL3bc(GL3bc downstream, PrintStream stream) { + super((GL4bc)downstream, stream); + } +} diff --git a/src/jogl/classes/javax/media/opengl/TraceGL4.java b/src/jogl/classes/javax/media/opengl/TraceGL4.java new file mode 100644 index 000000000..afe2cd9a1 --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/TraceGL4.java @@ -0,0 +1,23 @@ +package javax.media.opengl; + +import java.io.PrintStream; + +/** + * <p> + * Composable pipeline which wraps an underlying {@link GL} implementation, + * providing tracing information to a user-specified {@link java.io.PrintStream} + * before and after each OpenGL method call. + * </p> + * <p> + * Sample code which installs this pipeline, manual: + * <pre> + * gl = drawable.setGL(new TraceGL(drawable.getGL(), System.err)); + * </pre> + * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}. + * </p> + */ +public class TraceGL4 extends TraceGL4bc { + public TraceGL4(GL4 downstream, PrintStream stream) { + super((GL4bc)downstream, stream); + } +} diff --git a/src/jogl/classes/javax/media/opengl/TraceGLES2.java b/src/jogl/classes/javax/media/opengl/TraceGLES2.java new file mode 100644 index 000000000..4740e2e72 --- /dev/null +++ b/src/jogl/classes/javax/media/opengl/TraceGLES2.java @@ -0,0 +1,23 @@ +package javax.media.opengl; + +import java.io.PrintStream; + +/** + * <p> + * Composable pipeline which wraps an underlying {@link GL} implementation, + * providing tracing information to a user-specified {@link java.io.PrintStream} + * before and after each OpenGL method call. + * </p> + * <p> + * Sample code which installs this pipeline, manual: + * <pre> + * gl = drawable.setGL(new TraceGL(drawable.getGL(), System.err)); + * </pre> + * For automatic instantiation see {@link GLPipelineFactory#create(String, Class, GL, Object[])}. + * </p> + */ +public class TraceGLES2 extends TraceGLES3 { + public TraceGLES2(GLES2 downstream, PrintStream stream) { + super((GLES3)downstream, stream); + } +} diff --git a/src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java b/src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java index d1e725b00..2698678af 100644 --- a/src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java +++ b/src/jogl/classes/javax/media/opengl/awt/AWTGLAutoDrawable.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,18 +28,18 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package javax.media.opengl.awt; -import javax.media.opengl.*; +import javax.media.opengl.GLAutoDrawable; public interface AWTGLAutoDrawable extends GLAutoDrawable, ComponentEvents { /** Requests a new width and height for this AWTGLAutoDrawable. */ diff --git a/src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java b/src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java index 0c4f63c2d..5feaa5760 100644 --- a/src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java +++ b/src/jogl/classes/javax/media/opengl/awt/ComponentEvents.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,11 +28,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index 0a75865e2..abf670c95 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,11 +29,11 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ @@ -44,29 +44,32 @@ import java.beans.Beans; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; - import java.awt.Canvas; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Frame; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; +import java.awt.event.HierarchyEvent; +import java.awt.event.HierarchyListener; +import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Rectangle2D; - import java.awt.EventQueue; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.List; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.OffscreenLayerOption; +import javax.media.nativewindow.VisualIDHolder; import javax.media.nativewindow.WindowClosingProtocol; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.NativeWindowFactory; - import javax.media.opengl.GL; import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; @@ -80,21 +83,29 @@ import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import javax.media.opengl.GLRunnable; +import javax.media.opengl.GLSharedContextSetter; import javax.media.opengl.Threading; import com.jogamp.common.GlueGenVersion; import com.jogamp.common.util.VersionUtil; +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.AWTGraphicsConfiguration; import com.jogamp.nativewindow.awt.AWTGraphicsDevice; import com.jogamp.nativewindow.awt.AWTGraphicsScreen; +import com.jogamp.nativewindow.awt.AWTPrintLifecycle; import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol; import com.jogamp.nativewindow.awt.JAWTWindow; import com.jogamp.opengl.JoglVersion; +import com.jogamp.opengl.util.GLDrawableUtil; +import com.jogamp.opengl.util.TileRenderer; -import jogamp.common.awt.AWTEDTExecutor; import jogamp.opengl.Debug; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableHelper; +import jogamp.opengl.GLDrawableImpl; +import jogamp.opengl.awt.AWTTilePainter; // FIXME: Subclasses need to call resetGLFunctionAvailability() on their // context whenever the displayChanged() function is called on our @@ -106,7 +117,17 @@ import jogamp.opengl.GLDrawableHelper; interfaces when adding a heavyweight doesn't work either because of Z-ordering or LayoutManager problems. * - * <h5><A NAME="java2dgl">Java2D OpenGL Remarks</A></h5> + * <h5><a name="offscreenlayer">Offscreen Layer Remarks</a></h5> + * + * {@link OffscreenLayerOption#setShallUseOffscreenLayer(boolean) setShallUseOffscreenLayer(true)} + * maybe called to use an offscreen drawable (FBO or PBuffer) allowing + * the underlying JAWT mechanism to composite the image, if supported. + * <p> + * {@link OffscreenLayerOption#setShallUseOffscreenLayer(boolean) setShallUseOffscreenLayer(true)} + * is being called if {@link GLCapabilitiesImmutable#isOnscreen()} is <code>false</code>. + * </p> + * + * <h5><a name="java2dgl">Java2D OpenGL Remarks</a></h5> * * To avoid any conflicts with a potential Java2D OpenGL context,<br> * you shall consider setting the following JVM properties:<br> @@ -123,7 +144,7 @@ import jogamp.opengl.GLDrawableHelper; * <li><pre>sun.java2d.noddraw=true</pre></li> * </ul> * - * <h5><A NAME="backgrounderase">Disable Background Erase</A></h5> + * <h5><a name="backgrounderase">Disable Background Erase</a></h5> * * GLCanvas tries to disable background erase for the AWT Canvas * before native peer creation (X11) and after it (Windows), <br> @@ -135,34 +156,44 @@ import jogamp.opengl.GLDrawableHelper; */ @SuppressWarnings("serial") -public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol, OffscreenLayerOption { +public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol, OffscreenLayerOption, AWTPrintLifecycle, GLSharedContextSetter { private static final boolean DEBUG = Debug.debug("GLCanvas"); - private GLDrawableHelper drawableHelper = new GLDrawableHelper(); + private final RecursiveLock lock = LockFactory.createRecursiveLock(); + private final GLDrawableHelper helper = new GLDrawableHelper(); private AWTGraphicsConfiguration awtConfig; - private volatile GLDrawable drawable; - private GLContextImpl context; - private boolean sendReshape = false; - + private volatile GLDrawableImpl drawable; // volatile: avoid locking for read-only access + private volatile JAWTWindow jawtWindow; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle + private volatile GLContextImpl context; // volatile: avoid locking for read-only access + private volatile boolean sendReshape = false; // volatile: maybe written by EDT w/o locking + // copy of the cstr args, mainly for recreation - private GLCapabilitiesImmutable capsReqUser; - private GLCapabilitiesChooser chooser; - private GLContext shareWith; - private int additionalCtxCreationFlags = 0; - private GraphicsDevice device; + private final GLCapabilitiesImmutable capsReqUser; + private final GLCapabilitiesChooser chooser; + private int additionalCtxCreationFlags = 0; + private final GraphicsDevice device; private boolean shallUseOffscreenLayer = false; - private AWTWindowClosingProtocol awtWindowClosingProtocol = + private volatile boolean isShowing; + private final HierarchyListener hierarchyListener = new HierarchyListener() { + @Override + public void hierarchyChanged(HierarchyEvent e) { + isShowing = GLCanvas.this.isShowing(); + } + }; + + private final AWTWindowClosingProtocol awtWindowClosingProtocol = new AWTWindowClosingProtocol(this, new Runnable() { + @Override public void run() { - GLCanvas.this.destroy(); + GLCanvas.this.destroyImpl( true ); } - }); + }, null); /** Creates a new GLCanvas component with a default set of OpenGL capabilities, using the default OpenGL capabilities selection - mechanism, on the default screen device. + mechanism, on the default screen device. * @throws GLException if no default profile is available for the default desktop device. */ public GLCanvas() throws GLException { @@ -171,7 +202,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing /** Creates a new GLCanvas component with the requested set of OpenGL capabilities, using the default OpenGL capabilities - selection mechanism, on the default screen device. + selection mechanism, on the default screen device. * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. * @see GLCanvas#GLCanvas(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, javax.media.opengl.GLContext, java.awt.GraphicsDevice) */ @@ -186,9 +217,11 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing * * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. * @see GLCanvas#GLCanvas(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, javax.media.opengl.GLContext, java.awt.GraphicsDevice) + * @deprecated Use {@link #GLCanvas(GLCapabilitiesImmutable)} + * and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}. */ - public GLCanvas(GLCapabilitiesImmutable capsReqUser, GLContext shareWith) - throws GLException + public GLCanvas(GLCapabilitiesImmutable capsReqUser, GLContext shareWith) + throws GLException { this(capsReqUser, null, shareWith, null); } @@ -198,23 +231,45 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing default set of capabilities is used. The GLCapabilitiesChooser specifies the algorithm for selecting one of the available GLCapabilities for the component; a DefaultGLCapabilitesChooser + is used if null is passed for this argument. + The passed GraphicsDevice indicates the screen on + which to create the GLCanvas; the GLDrawableFactory uses the + default screen device of the local GraphicsEnvironment if null + is passed for this argument. + * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. + */ + public GLCanvas(GLCapabilitiesImmutable capsReqUser, + GLCapabilitiesChooser chooser, + GraphicsDevice device) + throws GLException + { + this(capsReqUser, chooser, null, device); + } + + /** Creates a new GLCanvas component. The passed GLCapabilities + specifies the OpenGL capabilities for the component; if null, a + default set of capabilities is used. The GLCapabilitiesChooser + specifies the algorithm for selecting one of the available + GLCapabilities for the component; a DefaultGLCapabilitesChooser is used if null is passed for this argument. The passed GLContext specifies an OpenGL context with which to share textures, display lists and other OpenGL state, and may be null if sharing is not desired. See the note in the overview documentation on <a - href="../../../overview-summary.html#SHARING">context + href="../../../spec-overview.html#SHARING">context sharing</a>. The passed GraphicsDevice indicates the screen on which to create the GLCanvas; the GLDrawableFactory uses the default screen device of the local GraphicsEnvironment if null - is passed for this argument. + is passed for this argument. * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. + * @deprecated Use {@link #GLCanvas(GLCapabilitiesImmutable, GLCapabilitiesChooser, GraphicsDevice)} + * and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}. */ public GLCanvas(GLCapabilitiesImmutable capsReqUser, GLCapabilitiesChooser chooser, GLContext shareWith, - GraphicsDevice device) - throws GLException + GraphicsDevice device) + throws GLException { /* * Determination of the native window is made in 'super.addNotify()', @@ -231,6 +286,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing // don't allow the user to change data capsReqUser = (GLCapabilitiesImmutable) capsReqUser.cloneMutable(); } + if(!capsReqUser.isOnscreen()) { + setShallUseOffscreenLayer(true); // trigger offscreen layer - if supported + } if(null==device) { GraphicsConfiguration gc = super.getGraphicsConfiguration(); @@ -242,37 +300,61 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing // instantiation will be issued in addNotify() this.capsReqUser = capsReqUser; this.chooser = chooser; - this.shareWith = shareWith; + if( null != shareWith ) { + helper.setSharedContext(null, shareWith); + } this.device = device; + + this.addHierarchyListener(hierarchyListener); + this.isShowing = isShowing(); + } + + @Override + public final void setSharedContext(GLContext sharedContext) throws IllegalStateException { + helper.setSharedContext(this.context, sharedContext); + } + + @Override + public final void setSharedAutoDrawable(GLAutoDrawable sharedAutoDrawable) throws IllegalStateException { + helper.setSharedAutoDrawable(this, sharedAutoDrawable); + } + + @Override + public final Object getUpstreamWidget() { + return this; } + @Override public void setShallUseOffscreenLayer(boolean v) { shallUseOffscreenLayer = v; } + @Override public final boolean getShallUseOffscreenLayer() { - return shallUseOffscreenLayer; + return shallUseOffscreenLayer; } + @Override public final boolean isOffscreenLayerSurfaceEnabled() { - if(null != drawable) { - return ((JAWTWindow)drawable.getNativeSurface()).isOffscreenLayerSurfaceEnabled(); + final JAWTWindow _jawtWindow = jawtWindow; + if(null != _jawtWindow) { + return _jawtWindow.isOffscreenLayerSurfaceEnabled(); } return false; } - + /** * Overridden to choose a GraphicsConfiguration on a parent container's * GraphicsDevice because both devices */ - @Override + @Override public GraphicsConfiguration getGraphicsConfiguration() { /* * Workaround for problems with Xinerama and java.awt.Component.checkGD * when adding to a container on a different graphics device than the * one that this Canvas is associated with. - * + * * GC will be null unless: * - A native peer has assigned it. This means we have a native * peer, and are already comitted to a graphics configuration. @@ -282,11 +364,16 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing * all platforms since the peer hasn't been created. */ final GraphicsConfiguration gc = super.getGraphicsConfiguration(); + + if( Beans.isDesignTime() ) { + return gc; + } + /* * chosen is only non-null on platforms where the GLDrawableFactory * returns a non-null GraphicsConfiguration (in the GLCanvas * constructor). - * + * * if gc is from this Canvas' native peer then it should equal chosen, * otherwise it is from an ancestor component that this Canvas is being * added to, and we go into this block. @@ -298,21 +385,21 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing * Check for compatibility with gc. If they differ by only the * device then return a new GCconfig with the super-class' GDevice * (and presumably the same visual ID in Xinerama). - * + * */ if (!chosen.getDevice().getIDstring().equals(gc.getDevice().getIDstring())) { /* * Here we select a GraphicsConfiguration on the alternate * device that is presumably identical to the chosen * configuration, but on the other device. - * + * * Should really check to ensure that we select a configuration * with the same X visual ID for Xinerama screens, otherwise the * GLDrawable may have the wrong visual ID (I don't think this * ever gets updated). May need to add a method to * X11GLDrawableFactory to do this in a platform specific * manner. - * + * * However, on platforms where we can actually get into this * block, both devices should have the same visual list, and the * same configuration should be selected here. @@ -330,7 +417,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing System.err.println("Created Config (n): HAVE CF "+awtConfig); System.err.println("Created Config (n): Choosen CF "+config); System.err.println("Created Config (n): EQUALS CAPS "+equalCaps); - Thread.dumpStack(); + // Thread.dumpStack(); } if (compatible != null) { @@ -340,10 +427,15 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing */ chosen = compatible; - awtConfig = config; - if( !equalCaps && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) { - dispose(true); + // complete destruction! + destroyImpl( true ); + // recreation! + awtConfig = config; + createJAWTDrawableAndContext(); + validateGLDrawable(); + } else { + awtConfig = config; } } } @@ -353,7 +445,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing * return the GC that was selected in the constructor (and might * cause an exception in Component.checkGD when adding to a * container, but in this case that would be the desired behavior). - * + * */ return chosen; } else if (gc == null) { @@ -372,29 +464,72 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing */ return gc; } - - public GLContext createContext(GLContext shareWith) { - return (null != drawable) ? drawable.createContext(shareWith) : null; + + @Override + public GLContext createContext(final GLContext shareWith) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if(drawable != null) { + final GLContext _ctx = drawable.createContext(shareWith); + _ctx.setContextCreationFlags(additionalCtxCreationFlags); + return _ctx; + } + return null; + } finally { + _lock.unlock(); + } + } + + private final void setRealizedImpl(boolean realized) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLDrawable _drawable = drawable; + if( null == _drawable || realized == _drawable.isRealized() || + realized && ( 0 >= _drawable.getWidth() || 0 >= _drawable.getHeight() ) ) { + return; + } + _drawable.setRealized(realized); + if( realized && _drawable.isRealized() ) { + sendReshape=true; // ensure a reshape is being send .. + } + } finally { + _lock.unlock(); + } } + private final Runnable realizeOnEDTAction = new Runnable() { + @Override + public void run() { setRealizedImpl(true); } + }; + private final Runnable unrealizeOnEDTAction = new Runnable() { + @Override + public void run() { setRealizedImpl(false); } + }; - public void setRealized(boolean realized) { + @Override + public final void setRealized(boolean realized) { + // Make sure drawable realization happens on AWT-EDT and only there. Consider the AWTTree lock! + AWTEDTExecutor.singleton.invoke(getTreeLock(), false /* allowOnNonEDT */, true /* wait */, realized ? realizeOnEDTAction : unrealizeOnEDTAction); } + @Override public boolean isRealized() { - return ( null != drawable ) ? drawable.isRealized() : false; - } - protected final boolean isRealizedImpl() { - return ( null != drawable ) ? drawable.isRealized() : false; + final GLDrawable _drawable = drawable; + return ( null != _drawable ) ? _drawable.isRealized() : false; } + @Override public WindowClosingMode getDefaultCloseOperation() { return awtWindowClosingProtocol.getDefaultCloseOperation(); } + @Override public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { return awtWindowClosingProtocol.setDefaultCloseOperation(op); } + @Override public void display() { if( !validateGLDrawable() ) { if(DEBUG) { @@ -402,63 +537,43 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } return; // not yet available .. } - Threading.invoke(true, displayOnEventDispatchThreadAction, getTreeLock()); - - awtWindowClosingProtocol.addClosingListenerOneShot(); - } - - private void dispose(boolean regenerate) { - final GLAnimatorControl animator = getAnimator(); - if(DEBUG) { - System.err.println(getThreadName()+": Info: dispose("+regenerate+") - START, hasContext " + - (null!=context) + ", hasDrawable " + (null!=drawable)+", "+animator); - Thread.dumpStack(); - } - - if(null!=context) { - boolean animatorPaused = false; - if(null!=animator) { - // can't remove us from animator for recreational addNotify() - animatorPaused = animator.pause(); - } - - disposeRegenerate=regenerate; - - if(context.isCreated()) { - Threading.invoke(true, disposeOnEventDispatchThreadAction, getTreeLock()); - } - - if(animatorPaused) { - animator.resume(); - } - } - - if(!regenerate) { - disposeAbstractGraphicsDevice(); - } - - if(DEBUG) { - System.err.println(getThreadName()+": dispose("+regenerate+") - END, "+animator); + if( isShowing && !printActive ) { + Threading.invoke(true, displayOnEDTAction, getTreeLock()); } } /** - * Just an alias for removeNotify + * {@inheritDoc} + * + * <p> + * This impl. only destroys all GL related resources. + * </p> + * <p> + * This impl. does not remove the GLCanvas from it's parent AWT container + * so this class's {@link #removeNotify()} AWT override won't get called. + * To do so, remove this component from it's parent AWT container. + * </p> */ + @Override public void destroy() { - removeNotify(); + destroyImpl( false ); + } + + protected void destroyImpl(boolean destroyJAWTWindowAndAWTDevice) { + Threading.invoke(true, destroyOnEDTAction, getTreeLock()); + if( destroyJAWTWindowAndAWTDevice ) { + AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, disposeJAWTWindowAndAWTDeviceOnEDT); + } } /** Overridden to cause OpenGL rendering to be performed during repaint cycles. Subclasses which override this method must call super.paint() in their paint() method in order to function - properly. <P> - - <B>Overrides:</B> - <DL><DD><CODE>paint</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ - @Override + properly. + */ + @Override public void paint(Graphics g) { - if (Beans.isDesignTime()) { + if( Beans.isDesignTime() ) { // Make GLCanvas behave better in NetBeans GUI builder g.setColor(Color.BLACK); g.fillRect(0, 0, getWidth(), getHeight()); @@ -476,9 +591,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing g.drawString(name, (int) ((getWidth() - bounds.getWidth()) / 2), (int) ((getHeight() + bounds.getHeight()) / 2)); - return; - } - if( ! this.drawableHelper.isAnimatorAnimating() ) { + } else if( !this.helper.isAnimatorAnimatingOnOtherThread() ) { display(); } } @@ -493,83 +606,114 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @SuppressWarnings("deprecation") @Override public void addNotify() { - if(DEBUG) { - System.err.println(getThreadName()+": Info: addNotify - start, bounds: "+this.getBounds()); - Thread.dumpStack(); - } + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final boolean isBeansDesignTime = Beans.isDesignTime(); - /** - * 'super.addNotify()' determines the GraphicsConfiguration, - * while calling this class's overriden 'getGraphicsConfiguration()' method - * after which it creates the native peer. - * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration - * is being used in getGraphicsConfiguration(). - * This code order also allows recreation, ie re-adding the GLCanvas. - */ - awtConfig = chooseGraphicsConfiguration(capsReqUser, capsReqUser, chooser, device); - if(null==awtConfig) { - throw new GLException("Error: NULL AWTGraphicsConfiguration"); - } + if(DEBUG) { + System.err.println(getThreadName()+": Info: addNotify - start, bounds: "+this.getBounds()+", isBeansDesignTime "+isBeansDesignTime); + // Thread.dumpStack(); + } - // before native peer is valid: X11 - disableBackgroundErase(); + if( isBeansDesignTime ) { + super.addNotify(); + } else { + /** + * 'super.addNotify()' determines the GraphicsConfiguration, + * while calling this class's overriden 'getGraphicsConfiguration()' method + * after which it creates the native peer. + * Hence we have to set the 'awtConfig' before since it's GraphicsConfiguration + * is being used in getGraphicsConfiguration(). + * This code order also allows recreation, ie re-adding the GLCanvas. + */ + awtConfig = chooseGraphicsConfiguration(capsReqUser, capsReqUser, chooser, device); + if(null==awtConfig) { + throw new GLException("Error: NULL AWTGraphicsConfiguration"); + } - // issues getGraphicsConfiguration() and creates the native peer - super.addNotify(); + // before native peer is valid: X11 + disableBackgroundErase(); - // after native peer is valid: Windows - disableBackgroundErase(); + // issues getGraphicsConfiguration() and creates the native peer + super.addNotify(); - if (!Beans.isDesignTime()) { - createDrawableAndContext(); - } + // after native peer is valid: Windows + disableBackgroundErase(); - // init drawable by paint/display makes the init sequence more equal - // for all launch flavors (applet/javaws/..) - // validateGLDrawable(); + createJAWTDrawableAndContext(); - if(DEBUG) { - System.err.println(getThreadName()+": Info: addNotify - end: peer: "+getPeer()); + // init drawable by paint/display makes the init sequence more equal + // for all launch flavors (applet/javaws/..) + // validateGLDrawable(); + } + awtWindowClosingProtocol.addClosingListener(); + + if(DEBUG) { + System.err.println(getThreadName()+": Info: addNotify - end: peer: "+getPeer()); + } + } finally { + _lock.unlock(); } } - private void createDrawableAndContext() { - // no lock required, since this resource ain't available yet - final JAWTWindow jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); - jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); - jawtWindow.lockSurface(); - try { - drawable = GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow); - context = (GLContextImpl) drawable.createContext(shareWith); - context.setContextCreationFlags(additionalCtxCreationFlags); - } finally { - jawtWindow.unlockSurface(); + private void createJAWTDrawableAndContext() { + if ( !Beans.isDesignTime() ) { + jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); + jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); + jawtWindow.lockSurface(); + try { + drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow); + createContextImpl(drawable); + } finally { + jawtWindow.unlockSurface(); + } } } - - private boolean validateGLDrawable() { - boolean realized = false; - if (!Beans.isDesignTime()) { - if ( null != drawable ) { // OK: drawable is volatile - realized = drawable.isRealized(); - if ( !realized && 0 < drawable.getWidth() * drawable.getHeight() ) { - // make sure drawable realization happens on AWT EDT, due to AWTTree lock - AWTEDTExecutor.singleton.invoke(true, setRealizedOnEventDispatchThreadAction); - realized = true; - sendReshape=true; // ensure a reshape is being send .. - if(DEBUG) { - System.err.println(getThreadName()+": Realized Drawable: "+drawable.toString()); - Thread.dumpStack(); - } - } + private boolean createContextImpl(final GLDrawable drawable) { + final GLContext[] shareWith = { null }; + if( !helper.isSharedGLContextPending(shareWith) ) { + context = (GLContextImpl) drawable.createContext(shareWith[0]); + context.setContextCreationFlags(additionalCtxCreationFlags); + if(DEBUG) { + System.err.println(getThreadName()+": Context created: has shared "+(null != shareWith[0])); } + return true; + } else { + if(DEBUG) { + System.err.println(getThreadName()+": Context !created: pending share"); + } + return false; } - return realized; } - private Runnable setRealizedOnEventDispatchThreadAction = new Runnable() { - public void run() { - drawable.setRealized(true); - } }; + + private boolean validateGLDrawable() { + if( Beans.isDesignTime() || !isDisplayable() ) { + return false; // early out! + } + final GLDrawable _drawable = drawable; + if ( null != _drawable ) { + boolean res = _drawable.isRealized(); + if( !res ) { + // re-try drawable creation + if( 0 >= _drawable.getWidth() || 0 >= _drawable.getHeight() ) { + return false; // early out! + } + setRealized(true); + res = _drawable.isRealized(); + if(DEBUG) { + System.err.println(getThreadName()+": Realized Drawable: isRealized "+res+", "+_drawable.toString()); + // Thread.dumpStack(); + } + } + if( res && null == context ) { + // re-try context creation + res = createContextImpl(_drawable); // pending creation. + } + return res; + } + return false; + } /** <p>Overridden to track when this component is removed from a container. Subclasses which override this method must call @@ -584,20 +728,17 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing public void removeNotify() { if(DEBUG) { System.err.println(getThreadName()+": Info: removeNotify - start"); - Thread.dumpStack(); + // Thread.dumpStack(); } awtWindowClosingProtocol.removeClosingListener(); - if (Beans.isDesignTime()) { + if( Beans.isDesignTime() ) { super.removeNotify(); } else { try { - dispose(false); + destroyImpl( true ); } finally { - context=null; - drawable=null; - awtConfig=null; super.removeNotify(); } } @@ -616,229 +757,524 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing @SuppressWarnings("deprecation") @Override public void reshape(int x, int y, int width, int height) { - super.reshape(x, y, width, height); - if(null != drawable && drawable.isRealized() && !drawable.getChosenGLCapabilities().isOnscreen()) { - dispose(true); - } else { - sendReshape = true; + synchronized (getTreeLock()) { // super.reshape(..) claims tree lock, so we do extend it's lock over reshape + super.reshape(x, y, width, height); + + if(DEBUG) { + final NativeSurface ns = getNativeSurface(); + final long nsH = null != ns ? ns.getSurfaceHandle() : 0; + System.err.println("GLCanvas.sizeChanged: ("+getThreadName()+"): "+width+"x"+height+" - surfaceHandle 0x"+Long.toHexString(nsH)); + // Thread.dumpStack(); + } + if( validateGLDrawable() && !printActive ) { + final GLDrawableImpl _drawable = drawable; + if( ! _drawable.getChosenGLCapabilities().isOnscreen() ) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, context, width, height); + if(_drawable != _drawableNew) { + // write back + drawable = _drawableNew; + } + } finally { + _lock.unlock(); + } + } + sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock + } } } - /** <B>Overrides:</B> - <DL><DD><CODE>update</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ - /** + /** * Overridden from Canvas to prevent the AWT's clearing of the * canvas from interfering with the OpenGL rendering. */ - @Override + @Override public void update(Graphics g) { paint(g); } - + + private volatile boolean printActive = false; + private GLAnimatorControl printAnimator = null; + private GLAutoDrawable printGLAD = null; + private AWTTilePainter printAWTTiles = null; + + @Override + public void setupPrint(double scaleMatX, double scaleMatY, int numSamples, int tileWidth, int tileHeight) { + printActive = true; + final int componentCount = isOpaque() ? 3 : 4; + final TileRenderer printRenderer = new TileRenderer(); + printAWTTiles = new AWTTilePainter(printRenderer, componentCount, scaleMatX, scaleMatY, numSamples, tileWidth, tileHeight, DEBUG); + AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, setupPrintOnEDT); + } + 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"); + } + printActive = false; + return; // not yet available .. + } + if( !isShowing ) { + if(DEBUG) { + System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, drawable valid, canvas not showing"); + } + 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.getWidth() || + printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getHeight(); + 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.getWidth()+"x"+printDrawable.getHeight()+ + ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+ + ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+ + ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator); + } + if( reqNewGLAD ) { + caps.setDoubleBuffered(false); + caps.setOnscreen(false); + if( printNumSamples != caps.getNumSamples() ) { + caps.setSampleBuffers(0 < printNumSamples); + caps.setNumSamples(printNumSamples); + } + final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); + printGLAD = factory.createOffscreenAutoDrawable(null, caps, null, + 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.getWidth(), printDrawable.getHeight(), 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.getWidth()+"x"+printGLAD.getHeight()+", "+printGLAD); + System.err.println("AWT print.setup printDraw: "+printDrawable.getWidth()+"x"+printDrawable.getHeight()+", "+printDrawable); + } + } + }; + + @Override + public void releasePrint() { + if( !printActive || null == printGLAD ) { + throw new IllegalStateException("setupPrint() not called"); + } + sendReshape = false; // clear reshape flag + AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, releasePrintOnEDT); + } + 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; + } + sendReshape = true; // trigger reshape, i.e. gl-viewport and -listener - this component might got resized! + printActive = false; + display(); + } + }; + + @Override + public void print(Graphics graphics) { + if( !printActive || null == printGLAD ) { + throw new IllegalStateException("setupPrint() not called"); + } + if(DEBUG && !EventQueue.isDispatchThread()) { + System.err.println(getThreadName()+": Warning: GLCanvas print - not called from AWT-EDT"); + // we cannot dispatch print on AWT-EDT due to printing internal locking .. + } + sendReshape = false; // clear reshape flag + + final Graphics2D g2d = (Graphics2D)graphics; + try { + printAWTTiles.setupGraphics2DAndClipBounds(g2d, getWidth(), getHeight()); + final TileRenderer tileRenderer = printAWTTiles.renderer; + if( DEBUG ) { + System.err.println("AWT print.0: "+tileRenderer); + } + if( !tileRenderer.eot() ) { + try { + do { + if( printGLAD != GLCanvas.this ) { + tileRenderer.display(); + } else { + Threading.invoke(true, displayOnEDTAction, getTreeLock()); + } + } while ( !tileRenderer.eot() ); + if( DEBUG ) { + System.err.println("AWT print.1: "+printAWTTiles); + } + } finally { + tileRenderer.reset(); + printAWTTiles.resetGraphics2D(); + } + } + } catch (NoninvertibleTransformException nte) { + System.err.println("Catched: Inversion failed of: "+g2d.getTransform()); + nte.printStackTrace(); + } + if( DEBUG ) { + System.err.println("AWT print.X: "+printAWTTiles); + } + } + + @Override public void addGLEventListener(GLEventListener listener) { - drawableHelper.addGLEventListener(listener); + helper.addGLEventListener(listener); + } + + @Override + public void addGLEventListener(int index, GLEventListener listener) throws IndexOutOfBoundsException { + helper.addGLEventListener(index, listener); + } + + @Override + public int getGLEventListenerCount() { + return helper.getGLEventListenerCount(); + } + + @Override + public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException { + return helper.getGLEventListener(index); + } + + @Override + public boolean areAllGLEventListenerInitialized() { + return helper.areAllGLEventListenerInitialized(); + } + + @Override + public boolean getGLEventListenerInitState(GLEventListener listener) { + return helper.getGLEventListenerInitState(listener); } - public void addGLEventListener(int index, GLEventListener listener) { - drawableHelper.addGLEventListener(index, listener); + @Override + public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) { + helper.setGLEventListenerInitState(listener, initialized); } - public void removeGLEventListener(GLEventListener listener) { - drawableHelper.removeGLEventListener(listener); + @Override + public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) { + final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove); + Threading.invoke(true, r, getTreeLock()); + return r.listener; } + @Override + public GLEventListener removeGLEventListener(GLEventListener listener) { + return helper.removeGLEventListener(listener); + } + + @Override public void setAnimator(GLAnimatorControl animatorControl) { - drawableHelper.setAnimator(animatorControl); + helper.setAnimator(animatorControl); } + @Override public GLAnimatorControl getAnimator() { - return drawableHelper.getAnimator(); + return helper.getAnimator(); } - public void invoke(boolean wait, GLRunnable glRunnable) { - drawableHelper.invoke(this, wait, glRunnable); + @Override + public final Thread setExclusiveContextThread(Thread t) throws GLException { + return helper.setExclusiveContextThread(t, context); } - public void setContext(GLContext ctx) { - context=(GLContextImpl)ctx; - if(null != context) { - context.setContextCreationFlags(additionalCtxCreationFlags); - } + @Override + public final Thread getExclusiveContextThread() { + return helper.getExclusiveContextThread(); + } + + @Override + public boolean invoke(boolean wait, GLRunnable glRunnable) { + return helper.invoke(this, wait, glRunnable); + } + + @Override + public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) { + return helper.invoke(this, wait, glRunnables); } + @Override + public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLContext oldCtx = context; + GLDrawableHelper.switchContext(drawable, oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); + context=(GLContextImpl)newCtx; + return oldCtx; + } finally { + _lock.unlock(); + } + } + + @Override + public final GLDrawable getDelegatedDrawable() { + return drawable; + } + + @Override public GLContext getContext() { return context; } + @Override public GL getGL() { - if (Beans.isDesignTime()) { + if( Beans.isDesignTime() ) { return null; } - GLContext ctx = getContext(); - return (ctx == null) ? null : ctx.getGL(); + final GLContext _context = context; + return (_context == null) ? null : _context.getGL(); } + @Override public GL setGL(GL gl) { - GLContext ctx = getContext(); - if (ctx != null) { - ctx.setGL(gl); + final GLContext _context = context; + if (_context != null) { + _context.setGL(gl); return gl; } return null; } + @Override public void setAutoSwapBufferMode(boolean onOrOff) { - drawableHelper.setAutoSwapBufferMode(onOrOff); + helper.setAutoSwapBufferMode(onOrOff); } + @Override public boolean getAutoSwapBufferMode() { - return drawableHelper.getAutoSwapBufferMode(); + return helper.getAutoSwapBufferMode(); } + @Override public void swapBuffers() { - Threading.invoke(true, swapBuffersOnEventDispatchThreadAction, getTreeLock()); + Threading.invoke(true, swapBuffersOnEDTAction, getTreeLock()); } + @Override public void setContextCreationFlags(int flags) { additionalCtxCreationFlags = flags; + final GLContext _context = context; + if(null != _context) { + _context.setContextCreationFlags(additionalCtxCreationFlags); + } } - + + @Override public int getContextCreationFlags() { - return additionalCtxCreationFlags; + return additionalCtxCreationFlags; } - + + @Override public GLProfile getGLProfile() { return capsReqUser.getGLProfile(); } + @Override public GLCapabilitiesImmutable getChosenGLCapabilities() { - if (awtConfig == null) { + if( Beans.isDesignTime() ) { + return capsReqUser; + } else if( null == awtConfig ) { throw new GLException("No AWTGraphicsConfiguration: "+this); } - return (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities(); } public GLCapabilitiesImmutable getRequestedGLCapabilities() { - if (awtConfig == null) { + if( null == awtConfig ) { return capsReqUser; } - return (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities(); } + @Override + public boolean isGLOriented() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.isGLOriented() : true; + } + + @Override public NativeSurface getNativeSurface() { - return (null != drawable) ? drawable.getNativeSurface() : null; + final GLDrawable _drawable = drawable; + return (null != _drawable) ? _drawable.getNativeSurface() : null; } + @Override public long getHandle() { - return (null != drawable) ? drawable.getHandle() : 0; + final GLDrawable _drawable = drawable; + return (null != _drawable) ? _drawable.getHandle() : 0; } + @Override public GLDrawableFactory getFactory() { - return (null != drawable) ? drawable.getFactory() : null; + final GLDrawable _drawable = drawable; + return (null != _drawable) ? _drawable.getFactory() : null; } @Override public String toString() { - final int dw = (null!=drawable) ? drawable.getWidth() : -1; - final int dh = (null!=drawable) ? drawable.getHeight() : -1; - + final GLDrawable _drawable = drawable; + final int dw = (null!=_drawable) ? _drawable.getWidth() : -1; + final int dh = (null!=_drawable) ? _drawable.getHeight() : -1; + return "AWT-GLCanvas[Realized "+isRealized()+ - ",\n\t"+((null!=drawable)?drawable.getClass().getName():"null-drawable")+ + ",\n\t"+((null!=_drawable)?_drawable.getClass().getName():"null-drawable")+ ",\n\tFactory "+getFactory()+ ",\n\thandle 0x"+Long.toHexString(getHandle())+ ",\n\tDrawable size "+dw+"x"+dh+ ",\n\tAWT pos "+getX()+"/"+getY()+", size "+getWidth()+"x"+getHeight()+ - ",\n\tvisible "+isVisible()+ + ",\n\tvisible "+isVisible()+", displayable "+isDisplayable()+", showing "+isShowing+ ",\n\t"+awtConfig+"]"; } - + //---------------------------------------------------------------------- // Internals only below this point // - private boolean disposeRegenerate; - private final Runnable postDisposeAction = new Runnable() { + private final Runnable destroyOnEDTAction = new Runnable() { + @Override public void run() { - context=null; - if(null!=drawable) { - final JAWTWindow jawtWindow = (JAWTWindow)drawable.getNativeSurface(); - drawable.setRealized(false); - drawable=null; - if(null!=jawtWindow) { - jawtWindow.destroy(); - } - } - - if(disposeRegenerate) { - // Similar process as in addNotify()! - - // Recreate GLDrawable/GLContext to reflect it's new graphics configuration - createDrawableAndContext(); - - if(DEBUG) { - System.err.println(getThreadName()+": GLCanvas.dispose(true): new drawable: "+drawable); - } - validateGLDrawable(); // immediate attempt to recreate the drawable - } - } - }; + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLAnimatorControl animator = getAnimator(); - private final Runnable disposeOnEventDispatchThreadAction = new Runnable() { - public void run() { - drawableHelper.disposeGL(GLCanvas.this, drawable, context, postDisposeAction); - } - }; + if(DEBUG) { + System.err.println(getThreadName()+": Info: destroyOnEDTAction() - START, hasContext " + + (null!=context) + ", hasDrawable " + (null!=drawable)+", "+animator); + // Thread.dumpStack(); + } - private final Runnable disposeAbstractGraphicsDeviceAction = new Runnable() { - public void run() { - if(null != awtConfig) { - final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration(); - final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); - final String adeviceMsg; - if(DEBUG) { - adeviceMsg = adevice.toString(); - } else { - adeviceMsg = null; - } - boolean closed = adevice.close(); - if(DEBUG) { - System.err.println(getThreadName()+": GLCanvas.dispose(false): closed GraphicsDevice: "+adeviceMsg+", result: "+closed); - } - awtConfig=null; - } + final boolean animatorPaused; + if(null!=animator) { + // can't remove us from animator for recreational addNotify() + animatorPaused = animator.pause(); + } else { + animatorPaused = false; + } + + // 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 (GLException gle) { + gle.printStackTrace(); + } + } + context = null; + } + if( null != drawable ) { + drawable.setRealized(false); + if(DEBUG) { + System.err.println(getThreadName()+": destroyOnEDTAction() - post drawable: "+drawable); + } + drawable = null; + } + + if(animatorPaused) { + animator.resume(); + } + + if(DEBUG) { + System.err.println(getThreadName()+": dispose() - END, animator "+animator); + } + + } finally { + _lock.unlock(); + } } }; /** - * Disposes the AbstractGraphicsDevice within EDT, + * Disposes the JAWTWindow and AbstractGraphicsDevice within EDT, * since resources created (X11: Display), must be destroyed in the same thread, where they have been created. + * <p> + * The drawable and context handle are null'ed as well, assuming {@link #destroy()} has been called already. + * </p> * * @see #chooseGraphicsConfiguration(javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesImmutable, javax.media.opengl.GLCapabilitiesChooser, java.awt.GraphicsDevice) */ - void disposeAbstractGraphicsDevice() { - if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) { - disposeAbstractGraphicsDeviceAction.run(); - } else { - try { - EventQueue.invokeAndWait(disposeAbstractGraphicsDeviceAction); - } catch (InvocationTargetException e) { - throw new GLException(e.getTargetException()); - } catch (InterruptedException e) { - throw new GLException(e); + private final Runnable disposeJAWTWindowAndAWTDeviceOnEDT = new Runnable() { + @Override + public void run() { + context=null; + drawable=null; + + if( null != jawtWindow ) { + jawtWindow.destroy(); + if(DEBUG) { + System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post JAWTWindow: "+jawtWindow); + } + jawtWindow=null; + } + + if(null != awtConfig) { + final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration(); + final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); + final String adeviceMsg; + if(DEBUG) { + adeviceMsg = adevice.toString(); + } else { + adeviceMsg = null; + } + boolean closed = adevice.close(); + if(DEBUG) { + System.err.println(getThreadName()+": GLCanvas.disposeJAWTWindowAndAWTDeviceOnEDT(): post GraphicsDevice: "+adeviceMsg+", result: "+closed); + } } + awtConfig=null; } - } + }; private final Runnable initAction = new Runnable() { + @Override public void run() { - drawableHelper.init(GLCanvas.this); + helper.init(GLCanvas.this, !sendReshape); } }; - + private final Runnable displayAction = new Runnable() { + @Override public void run() { if (sendReshape) { if(DEBUG) { @@ -846,33 +1282,62 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } // Note: we ignore the given x and y within the parent component // since we are drawing directly into this heavyweight component. - drawableHelper.reshape(GLCanvas.this, 0, 0, getWidth(), getHeight()); + helper.reshape(GLCanvas.this, 0, 0, getWidth(), getHeight()); sendReshape = false; } - drawableHelper.display(GLCanvas.this); + helper.display(GLCanvas.this); } }; - private final Runnable swapBuffersAction = new Runnable() { + private final Runnable displayOnEDTAction = new Runnable() { + @Override public void run() { - drawable.swapBuffers(); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( null != drawable && drawable.isRealized() ) { + helper.invokeGL(drawable, context, displayAction, initAction); + } + } finally { + _lock.unlock(); + } } }; - // Workaround for ATI driver bugs related to multithreading issues - // like simultaneous rendering via Animators to canvases that are - // being resized on the AWT event dispatch thread - private final Runnable displayOnEventDispatchThreadAction = new Runnable() { + private final Runnable swapBuffersOnEDTAction = new Runnable() { + @Override public void run() { - drawableHelper.invokeGL(drawable, context, displayAction, initAction); + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( null != drawable && drawable.isRealized() ) { + drawable.swapBuffers(); + } + } finally { + _lock.unlock(); + } } }; - - private final Runnable swapBuffersOnEventDispatchThreadAction = new Runnable() { + + private class DisposeGLEventListenerAction implements Runnable { + GLEventListener listener; + private final boolean remove; + private DisposeGLEventListenerAction(GLEventListener listener, boolean remove) { + this.listener = listener; + this.remove = remove; + } + + @Override public void run() { - drawableHelper.invokeGL(drawable, context, swapBuffersAction, initAction); - } + final RecursiveLock _lock = lock; + _lock.lock(); + try { + listener = helper.disposeGLEventListener(GLCanvas.this, drawable, context, listener, remove); + } finally { + _lock.unlock(); + } + } }; // Disables the AWT's erasing of this Canvas's background on Windows @@ -886,6 +1351,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing if (!disableBackgroundEraseInitialized) { try { AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override public Object run() { try { Class<?> clazz = getToolkit().getClass(); @@ -935,36 +1401,37 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing * @param device * @return the chosen AWTGraphicsConfiguration * - * @see #disposeAbstractGraphicsDevice() + * @see #disposeJAWTWindowAndAWTDeviceOnEDT */ private AWTGraphicsConfiguration chooseGraphicsConfiguration(final GLCapabilitiesImmutable capsChosen, final GLCapabilitiesImmutable capsRequested, final GLCapabilitiesChooser chooser, final GraphicsDevice device) { // Make GLCanvas behave better in NetBeans GUI builder - if (Beans.isDesignTime()) { + if( Beans.isDesignTime() ) { return null; } - final AbstractGraphicsScreen aScreen = null != device ? + final AbstractGraphicsScreen aScreen = null != device ? AWTGraphicsScreen.createScreenDevice(device, AbstractGraphicsDevice.DEFAULT_UNIT): AWTGraphicsScreen.createDefault(); AWTGraphicsConfiguration config = null; if( EventQueue.isDispatchThread() || Thread.holdsLock(getTreeLock()) ) { config = (AWTGraphicsConfiguration) - GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class).chooseGraphicsConfiguration(capsChosen, + GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen, capsRequested, - chooser, aScreen); + chooser, aScreen, VisualIDHolder.VID_UNDEFINED); } else { try { final ArrayList<AWTGraphicsConfiguration> bucket = new ArrayList<AWTGraphicsConfiguration>(1); EventQueue.invokeAndWait(new Runnable() { + @Override public void run() { AWTGraphicsConfiguration c = (AWTGraphicsConfiguration) - GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class).chooseGraphicsConfiguration(capsChosen, + GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen, capsRequested, - chooser, aScreen); + chooser, aScreen, VisualIDHolder.VID_UNDEFINED); bucket.add(c); } }); @@ -982,11 +1449,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing return config; } - - protected static String getThreadName() { - return Thread.currentThread().getName(); - } - + + protected static String getThreadName() { return Thread.currentThread().getName(); } + /** * A most simple JOGL AWT test entry */ @@ -996,7 +1461,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing // System.err.println(NativeWindowVersion.getInstance()); System.err.println(JoglVersion.getInstance()); - System.err.println(JoglVersion.getDefaultOpenGLInfo(null).toString()); + System.err.println(JoglVersion.getDefaultOpenGLInfo(null, null, true).toString()); final GLCapabilitiesImmutable caps = new GLCapabilities( GLProfile.getDefault(GLProfile.getDefaultDevice()) ); final Frame frame = new Frame("JOGL AWT Test"); @@ -1006,17 +1471,22 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing frame.setSize(128, 128); glCanvas.addGLEventListener(new GLEventListener() { + @Override public void init(GLAutoDrawable drawable) { GL gl = drawable.getGL(); System.err.println(JoglVersion.getGLInfo(gl, null)); } + @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { } + @Override public void display(GLAutoDrawable drawable) { } + @Override public void dispose(GLAutoDrawable drawable) { } }); try { javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + @Override public void run() { frame.setVisible(true); }}); @@ -1026,6 +1496,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing glCanvas.display(); try { javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + @Override public void run() { frame.dispose(); }}); diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index 167b99374..adc0a0d23 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 JogAmp Community. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,42 +29,42 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. - * + * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package javax.media.opengl.awt; -import java.nio.ByteBuffer; -import java.nio.IntBuffer; -import java.beans.Beans; - import java.awt.Color; import java.awt.EventQueue; import java.awt.FontMetrics; import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.Rectangle; +import java.awt.event.HierarchyEvent; +import java.awt.event.HierarchyListener; +import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; -import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; -import javax.swing.JPanel; +import java.beans.Beans; +import java.nio.IntBuffer; +import java.util.List; -import javax.media.nativewindow.WindowClosingProtocol; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeSurface; -import javax.media.nativewindow.WindowClosingProtocol.WindowClosingMode; - -import javax.media.opengl.DefaultGLCapabilitiesChooser; +import javax.media.nativewindow.SurfaceUpdatedListener; +import javax.media.nativewindow.WindowClosingProtocol; import javax.media.opengl.GL; import javax.media.opengl.GL2; +import javax.media.opengl.GL2ES3; import javax.media.opengl.GL2GL3; import javax.media.opengl.GLAnimatorControl; import javax.media.opengl.GLAutoDrawable; @@ -76,129 +76,219 @@ import javax.media.opengl.GLDrawable; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; -import javax.media.opengl.GLPbuffer; +import javax.media.opengl.GLFBODrawable; import javax.media.opengl.GLProfile; import javax.media.opengl.GLRunnable; +import javax.media.opengl.GLSharedContextSetter; import javax.media.opengl.Threading; - -import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol; -import com.jogamp.opengl.util.FBObject; -import com.jogamp.opengl.util.GLBuffers; +import javax.swing.JPanel; import jogamp.opengl.Debug; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableFactoryImpl; import jogamp.opengl.GLDrawableHelper; import jogamp.opengl.GLDrawableImpl; +import jogamp.opengl.awt.AWTTilePainter; import jogamp.opengl.awt.Java2D; -import jogamp.opengl.awt.Java2DGLContext; +import jogamp.opengl.util.glsl.GLSLTextureRaster; -// FIXME: Subclasses need to call resetGLFunctionAvailability() on their -// context whenever the displayChanged() function is called on their -// GLEventListeners +import com.jogamp.common.util.awt.AWTEDTExecutor; +import com.jogamp.nativewindow.awt.AWTPrintLifecycle; +import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol; +import com.jogamp.opengl.FBObject; +import com.jogamp.opengl.GLRendererQuirks; +import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes; +import com.jogamp.opengl.util.GLPixelBuffer.SingletonGLPixelBufferProvider; +import com.jogamp.opengl.util.GLDrawableUtil; +import com.jogamp.opengl.util.GLPixelStorageModes; +import com.jogamp.opengl.util.TileRenderer; +import com.jogamp.opengl.util.awt.AWTGLPixelBuffer; +import com.jogamp.opengl.util.awt.AWTGLPixelBuffer.AWTGLPixelBufferProvider; +import com.jogamp.opengl.util.awt.AWTGLPixelBuffer.SingleAWTGLPixelBufferProvider; +import com.jogamp.opengl.util.texture.TextureState; /** A lightweight Swing component which provides OpenGL rendering support. Provided for compatibility with Swing user interfaces when adding a heavyweight doesn't work either because of Z-ordering or LayoutManager problems. - <P> + <p> The GLJPanel can be made transparent by creating it with a GLCapabilities object with alpha bits specified and calling {@link #setOpaque}(false). Pixels with resulting OpenGL alpha values less - than 1.0 will be overlaid on any underlying Swing rendering. </P> - <P> - Notes specific to the Reference Implementation: This component - attempts to use hardware-accelerated rendering via pbuffers and - falls back on to software rendering if problems occur. - Note that because this component attempts to use pbuffers for - rendering, and because pbuffers can not be resized, somewhat - surprising behavior may occur during resize operations; the {@link - GLEventListener#init} method may be called multiple times as the - pbuffer is resized to be able to cover the size of the GLJPanel. - This behavior is correct, as the textures and display lists for - the GLJPanel will have been lost during the resize operation. The - application should attempt to make its GLEventListener.init() - methods as side-effect-free as possible. </P> + than 1.0 will be overlaid on any underlying Swing rendering. + </p> + <p> + This component attempts to use hardware-accelerated rendering via FBO or pbuffers and + falls back on to software rendering if none of the former are available + 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. + 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. + </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. + </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(...)} + on the prepared {@link BufferedImage} as described above. + </p> <P> - * Please read <A HREF="GLCanvas.html#java2dgl">Java2D OpenGL Remarks</A>. + * Please read <a href="GLCanvas.html#java2dgl">Java2D OpenGL Remarks</a>. * </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 + 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, + see {@link #setTextureUnit(int)} and {@link #getTextureUnit()}. + </p> + <p> + The active and dedicated texture-unit's {@link GL#GL_TEXTURE_2D} state is preserved via {@link TextureState}. + See also {@link Texture#textureCallOrder Order of Texture Commands}. + </p> + <p> + The current gl-viewport is preserved. + </p> + <p> + <i>Warning (Bug 842)</i>: 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> */ @SuppressWarnings("serial") -public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosingProtocol { - private static final boolean DEBUG = Debug.debug("GLJPanel"); +public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosingProtocol, AWTPrintLifecycle, GLSharedContextSetter { + private static final boolean DEBUG; + private static final boolean DEBUG_VIEWPORT; + private static final boolean USE_GLSL_TEXTURE_RASTERIZER; + private static final boolean SKIP_VERTICAL_FLIP_DEFAULT; + + /** Indicates whether the Java 2D OpenGL pipeline is requested by user. */ + private static final boolean java2dOGLEnabledByProp; + + /** Indicates whether the Java 2D OpenGL pipeline is enabled, resource-compatible and requested by user. */ + private static final boolean useJava2DGLPipeline; + + /** Indicates whether the Java 2D OpenGL pipeline's usage is error free. */ + private static boolean java2DGLPipelineOK; + + static { + Debug.initSingleton(); + DEBUG = Debug.debug("GLJPanel"); + DEBUG_VIEWPORT = Debug.isPropertyDefined("jogl.debug.GLJPanel.Viewport", true); + USE_GLSL_TEXTURE_RASTERIZER = !Debug.isPropertyDefined("jogl.gljpanel.noglsl", true); + SKIP_VERTICAL_FLIP_DEFAULT = Debug.isPropertyDefined("jogl.gljpanel.noverticalflip", true); + boolean enabled = Debug.getBooleanProperty("sun.java2d.opengl", false); + java2dOGLEnabledByProp = enabled && !Debug.isPropertyDefined("jogl.gljpanel.noogl", true); + + enabled = false; + if( java2dOGLEnabledByProp ) { + // Force eager initialization of part of the Java2D class since + // otherwise it's likely it will try to be initialized while on + // the Queue Flusher Thread, which is not allowed + if (Java2D.isOGLPipelineResourceCompatible() && Java2D.isFBOEnabled()) { + if( null != Java2D.getShareContext(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()) ) { + enabled = true; + } + } + } + useJava2DGLPipeline = enabled; + java2DGLPipelineOK = enabled; + if( DEBUG ) { + System.err.println("GLJPanel: DEBUG_VIEWPORT "+DEBUG_VIEWPORT); + System.err.println("GLJPanel: USE_GLSL_TEXTURE_RASTERIZER "+USE_GLSL_TEXTURE_RASTERIZER); + System.err.println("GLJPanel: SKIP_VERTICAL_FLIP_DEFAULT "+SKIP_VERTICAL_FLIP_DEFAULT); + System.err.println("GLJPanel: java2dOGLEnabledByProp "+java2dOGLEnabledByProp); + System.err.println("GLJPanel: useJava2DGLPipeline "+useJava2DGLPipeline); + System.err.println("GLJPanel: java2DGLPipelineOK "+java2DGLPipelineOK); + } + } + + private static SingleAWTGLPixelBufferProvider singleAWTGLPixelBufferProvider = null; + private static synchronized SingleAWTGLPixelBufferProvider getSingleAWTGLPixelBufferProvider() { + if( null == singleAWTGLPixelBufferProvider ) { + singleAWTGLPixelBufferProvider = new SingleAWTGLPixelBufferProvider( true /* allowRowStride */ ); + } + return singleAWTGLPixelBufferProvider; + } + + private final GLDrawableHelper helper; + private boolean autoSwapBufferMode; - private GLDrawableHelper drawableHelper = new GLDrawableHelper(); private volatile boolean isInitialized; + // // Data used for either pbuffers or pixmap-based offscreen surfaces + // + private AWTGLPixelBufferProvider customPixelBufferProvider = null; + /** Single buffered offscreen caps */ private GLCapabilitiesImmutable offscreenCaps; - private GLProfile glProfile; - private GLDrawableFactoryImpl factory; - private GLCapabilitiesChooser chooser; - private GLContext shareWith; + private final GLProfile glProfile; + private final GLDrawableFactoryImpl factory; + private final GLCapabilitiesChooser chooser; private int additionalCtxCreationFlags = 0; - - // Width of the actual GLJPanel - private int panelWidth = 0; - private int panelHeight = 0; - // Lazy reshape notification + + // Lazy reshape notification: reshapeWidth -> panelWidth -> backend.width private boolean handleReshape = false; private boolean sendReshape = true; - // The backend in use - private Backend backend; - - // Used by all backends either directly or indirectly to hook up callbacks - private Updater updater = new Updater(); - - // Turns off the pbuffer-based backend (used by default, unless the - // Java 2D / OpenGL pipeline is in use) - private static boolean hardwareAccelerationDisabled = - Debug.isPropertyDefined("jogl.gljpanel.nohw", true); - - // Turns off the fallback to software-based rendering from - // pbuffer-based rendering - private static boolean softwareRenderingDisabled = - Debug.isPropertyDefined("jogl.gljpanel.nosw", true); - - // Indicates whether the Java 2D OpenGL pipeline is enabled - private boolean oglPipelineEnabled = - Java2D.isOGLPipelineActive() && - !Debug.isPropertyDefined("jogl.gljpanel.noogl", true); - - // For handling reshape events lazily - // private int reshapeX; - // private int reshapeY; + // For handling reshape events lazily: reshapeWidth -> panelWidth -> backend.width private int reshapeWidth; private int reshapeHeight; + // Width of the actual GLJPanel: reshapeWidth -> panelWidth -> backend.width + private int panelWidth = 0; + private int panelHeight = 0; + // These are always set to (0, 0) except when the Java2D / OpenGL // pipeline is active private int viewportX; private int viewportY; - private AWTWindowClosingProtocol awtWindowClosingProtocol = + private int requestedTextureUnit = 0; // default + + // The backend in use + private volatile Backend backend; + + private boolean skipGLOrientationVerticalFlip = SKIP_VERTICAL_FLIP_DEFAULT; + + // Used by all backends either directly or indirectly to hook up callbacks + private final Updater updater = new Updater(); + + private boolean oglPipelineUsable() { + return null == customPixelBufferProvider && useJava2DGLPipeline && java2DGLPipelineOK; + } + + private volatile boolean isShowing; + private final HierarchyListener hierarchyListener = new HierarchyListener() { + @Override + public void hierarchyChanged(HierarchyEvent e) { + isShowing = GLJPanel.this.isShowing(); + } + }; + + private final AWTWindowClosingProtocol awtWindowClosingProtocol = new AWTWindowClosingProtocol(this, new Runnable() { + @Override public void run() { GLJPanel.this.destroy(); } - }); - - static { - // Force eager initialization of part of the Java2D class since - // otherwise it's likely it will try to be initialized while on - // the Queue Flusher Thread, which is not allowed - if (Java2D.isOGLPipelineActive() && Java2D.isFBOEnabled()) { - Java2D.getShareContext(GraphicsEnvironment. - getLocalGraphicsEnvironment(). - getDefaultScreenDevice()); - } - } + }, null); /** Creates a new GLJPanel component with a default set of OpenGL capabilities and using the default OpenGL capabilities selection - mechanism. + mechanism. * @throws GLException if no default profile is available for the default desktop device. */ public GLJPanel() throws GLException { @@ -207,7 +297,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing /** Creates a new GLJPanel component with the requested set of OpenGL capabilities, using the default OpenGL capabilities - selection mechanism. + selection mechanism. * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. */ public GLJPanel(GLCapabilitiesImmutable userCapsRequest) throws GLException { @@ -219,18 +309,34 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing default set of capabilities is used. The GLCapabilitiesChooser specifies the algorithm for selecting one of the available GLCapabilities for the component; a DefaultGLCapabilitesChooser + is used if null is passed for this argument. + * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. + */ + public GLJPanel(GLCapabilitiesImmutable userCapsRequest, GLCapabilitiesChooser chooser) + throws GLException + { + this(userCapsRequest, chooser, null); + } + + /** Creates a new GLJPanel component. The passed GLCapabilities + specifies the OpenGL capabilities for the component; if null, a + default set of capabilities is used. The GLCapabilitiesChooser + specifies the algorithm for selecting one of the available + GLCapabilities for the component; a DefaultGLCapabilitesChooser is used if null is passed for this argument. The passed GLContext specifies an OpenGL context with which to share textures, display lists and other OpenGL state, and may be null if sharing is not desired. See the note in the overview documentation on - <a href="../../../overview-summary.html#SHARING">context sharing</a>. + <a href="../../../spec-overview.html#SHARING">context sharing</a>. <P> Note: Sharing cannot be enabled using J2D OpenGL FBO sharing, since J2D GL Context must be shared and we can only share one context. * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. - */ - public GLJPanel(GLCapabilitiesImmutable userCapsRequest, GLCapabilitiesChooser chooser, GLContext shareWith) - throws GLException + * @deprecated Use {@link #GLJPanel(GLCapabilitiesImmutable, GLCapabilitiesChooser)} + * and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}. + */ + public GLJPanel(GLCapabilitiesImmutable userCapsRequest, GLCapabilitiesChooser chooser, GLContext shareWith) + throws GLException { super(); @@ -248,22 +354,102 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } this.glProfile = offscreenCaps.getGLProfile(); this.factory = GLDrawableFactoryImpl.getFactoryImpl(glProfile); - this.chooser = ((chooser != null) ? chooser : new DefaultGLCapabilitiesChooser()); - this.shareWith = shareWith; + this.chooser = chooser; + + helper = new GLDrawableHelper(); + if( null != shareWith ) { + helper.setSharedContext(null, shareWith); + } + autoSwapBufferMode = helper.getAutoSwapBufferMode(); + + this.setFocusable(true); // allow keyboard input! + this.addHierarchyListener(hierarchyListener); + this.isShowing = isShowing(); } - public void display() { - if (EventQueue.isDispatchThread()) { - // Want display() to be synchronous, so call paintImmediately() - paintImmediately(0, 0, getWidth(), getHeight()); + /** + * Attempts to initialize the backend, if not initialized yet. + * <p> + * If backend is already initialized method returns <code>true</code>. + * </p> + * <p> + * If <code>offthread</code> is <code>true</code>, initialization will kicked off + * on a <i>short lived</i> arbitrary thread and method returns immediately.<br/> + * If platform supports such <i>arbitrary thread</i> initialization method returns + * <code>true</code>, otherwise <code>false</code>. + * </p> + * <p> + * If <code>offthread</code> is <code>false</code>, initialization be performed + * on the current thread and method returns after initialization.<br/> + * Method returns <code>true</code> if initialization was successful, otherwise <code>false</code>. + * <p> + * @param offthread + */ + public final boolean initializeBackend(boolean offthread) { + if( offthread ) { + new Thread(getThreadName()+"-GLJPanel_Init") { + public void run() { + if( !isInitialized ) { + initializeBackendImpl(); + } + } }.start(); + return true; } else { - // Multithreaded redrawing of Swing components is not allowed, - // so do everything on the event dispatch thread - try { - EventQueue.invokeAndWait(paintImmediatelyAction); - } catch (Exception e) { - throw new GLException(e); + if( !isInitialized ) { + return initializeBackendImpl(); + } else { + return true; + } + } + } + + @Override + public final void setSharedContext(GLContext sharedContext) throws IllegalStateException { + helper.setSharedContext(this.getContext(), sharedContext); + } + + @Override + public final void setSharedAutoDrawable(GLAutoDrawable sharedAutoDrawable) throws IllegalStateException { + helper.setSharedAutoDrawable(this, sharedAutoDrawable); + } + + public AWTGLPixelBufferProvider getCustomPixelBufferProvider() { return customPixelBufferProvider; } + + /** + * @param custom custom {@link AWTGLPixelBufferProvider} + * @throws IllegalArgumentException if <code>custom</code> is <code>null</code> + * @throws IllegalStateException if backend is already realized, i.e. this instanced already painted once. + */ + public void setPixelBufferProvider(AWTGLPixelBufferProvider custom) throws IllegalArgumentException, IllegalStateException { + if( null == custom ) { + throw new IllegalArgumentException("Null PixelBufferProvider"); + } + if( null != backend ) { + throw new IllegalStateException("Backend already realized."); } + customPixelBufferProvider = custom; + } + + @Override + public final Object getUpstreamWidget() { + return this; + } + + @Override + public void display() { + if( isShowing ) { + if (EventQueue.isDispatchThread()) { + // Want display() to be synchronous, so call paintImmediately() + paintImmediately(0, 0, getWidth(), getHeight()); + } else { + // Multithreaded redrawing of Swing components is not allowed, + // so do everything on the event dispatch thread + try { + EventQueue.invokeAndWait(paintImmediatelyAction); + } catch (Exception e) { + throw new GLException(e); + } + } } } @@ -280,7 +466,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing animatorPaused = animator.pause(); } - if(backend.getContext().isCreated()) { + if(backend.getContext().isCreated()) { Threading.invoke(true, disposeAction, getTreeLock()); } if(null != backend) { @@ -291,9 +477,9 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing if(animatorPaused) { animator.resume(); - } + } } - + if(DEBUG) { System.err.println(getThreadName()+": GLJPanel.dispose() - stop"); } @@ -302,6 +488,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing /** * Just an alias for removeNotify */ + @Override public void destroy() { removeNotify(); } @@ -335,11 +522,11 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing return; } - if (backend == null || !isInitialized) { - createAndInitializeBackend(); + if( !isInitialized ) { + initializeBackendImpl(); } - if (!isInitialized) { + if (!isInitialized || printActive) { return; } @@ -347,16 +534,19 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // involve destroying the pbuffer (current context) and // re-creating it -- tricky to do properly while the context is // current - if (handleReshape) { - handleReshape(); - } + if( !printActive ) { + if (handleReshape) { + handleReshape = false; + sendReshape = handleReshape(); + } - updater.setGraphics(g); - - backend.doPaintComponent(g); + if( isShowing ) { + updater.setGraphics(g); + backend.doPaintComponent(g); + } + } } - /** Overridden to track when this component is added to a container. Subclasses which override this method must call super.addNotify() in their addNotify() method in order to @@ -366,6 +556,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public void addNotify() { super.addNotify(); + awtWindowClosingProtocol.addClosingListener(); if (DEBUG) { System.err.println(getThreadName()+": GLJPanel.addNotify()"); } @@ -389,18 +580,207 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing called on all registered {@link GLEventListener}s. Subclasses which override this method must call super.reshape() in their reshape() method in order to function properly. <P> - - <DL><DD><CODE>reshape</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ + * + * {@inheritDoc} + */ @SuppressWarnings("deprecation") @Override -public void reshape(int x, int y, int width, int height) { + public void reshape(int x, int y, int width, int height) { super.reshape(x, y, width, height); - // reshapeX = x; - // reshapeY = y; - reshapeWidth = width; - reshapeHeight = height; - handleReshape = true; + if( DEBUG ) { + System.err.println(getThreadName()+": GLJPanel.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+" [ this "+getWidth()+"x"+getHeight()+", panel "+ + panelWidth+"x"+panelHeight + + ", reshape: " +reshapeWidth+"x"+reshapeHeight + + "] -> "+(printActive?"skipped":"") + width+"x"+height); + } + if( !printActive ) { + reshapeWidth = width; + reshapeHeight = height; + handleReshape = true; + } + } + + private volatile boolean printActive = false; + private GLAnimatorControl printAnimator = null; + private GLAutoDrawable printGLAD = null; + private AWTTilePainter printAWTTiles = null; + + @Override + public void setupPrint(double scaleMatX, double scaleMatY, int numSamples, int tileWidth, int tileHeight) { + printActive = true; + final int componentCount = isOpaque() ? 3 : 4; + final TileRenderer printRenderer = new TileRenderer(); + printAWTTiles = new AWTTilePainter(printRenderer, componentCount, scaleMatX, scaleMatY, numSamples, tileWidth, tileHeight, DEBUG); + AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, setupPrintOnEDT); + } + 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"); + } + printActive = false; + return; // not yet available .. + } + if( !isShowing ) { + if(DEBUG) { + System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, drawable valid, panel not showing"); + } + 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.getWidth() || + printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getHeight(); + final boolean reqNewGLAD = reqNewGLADSamples || reqNewGLADSize ; + if( DEBUG ) { + System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ samples "+reqNewGLADSamples+", size "+reqNewGLADSize+"], "+ + ", drawableSize "+printDrawable.getWidth()+"x"+printDrawable.getHeight()+ + ", 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.getWidth(), printDrawable.getHeight(), 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.getWidth()+"x"+printGLAD.getHeight()+", "+printGLAD); + System.err.println("AWT print.setup printDraw: "+printDrawable.getWidth()+"x"+printDrawable.getHeight()+", "+printDrawable); + } + } + }; + + @Override + public void releasePrint() { + if( !printActive ) { + throw new IllegalStateException("setupPrint() not called"); + } + sendReshape = false; // clear reshape flag + handleReshape = false; // ditto + AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, releasePrintOnEDT); + } + + 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; + } + + // 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 GLDrawable drawable = GLJPanel.this.getDelegatedDrawable(); + if( awtWidth != panelWidth || awtHeight != panelHeight || + drawable.getWidth() != panelWidth || drawable.getHeight() != panelHeight ) { + // -> !( awtSize == panelSize == drawableSize ) + if ( DEBUG ) { + System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0: resizeWithinPrint panel " +panelWidth+"x"+panelHeight + + ", draw "+drawable.getWidth()+"x"+drawable.getHeight()+ + " -> " + awtWidth+"x"+awtHeight); + } + reshapeWidth = awtWidth; + reshapeHeight = awtHeight; + sendReshape = handleReshape(); // reshapeSize -> panelSize, backend reshape w/ GL reshape + } else { + sendReshape = true; // only GL reshape + } + printActive = false; + display(); + } + }; + + @Override + public void print(Graphics graphics) { + if( !printActive ) { + throw new IllegalStateException("setupPrint() not called"); + } + if(DEBUG && !EventQueue.isDispatchThread()) { + System.err.println(getThreadName()+": Warning: GLCanvas print - not called from AWT-EDT"); + // we cannot dispatch print on AWT-EDT due to printing internal locking .. + } + sendReshape = false; // clear reshape flag + handleReshape = false; // ditto + + final Graphics2D g2d = (Graphics2D)graphics; + try { + printAWTTiles.setupGraphics2DAndClipBounds(g2d, getWidth(), getHeight()); + final TileRenderer tileRenderer = printAWTTiles.renderer; + if( DEBUG ) { + System.err.println("AWT print.0: "+tileRenderer); + } + if( !tileRenderer.eot() ) { + try { + do { + if( printGLAD != GLJPanel.this ) { + tileRenderer.display(); + } else { + backend.doPlainPaint(); + } + } while ( !tileRenderer.eot() ); + if( DEBUG ) { + System.err.println("AWT print.1: "+printAWTTiles); + } + } finally { + tileRenderer.reset(); + printAWTTiles.resetGraphics2D(); + } + } + } catch (NoninvertibleTransformException nte) { + System.err.println("Catched: Inversion failed of: "+g2d.getTransform()); + nte.printStackTrace(); + } + if( DEBUG ) { + System.err.println("AWT print.X: "+printAWTTiles); + } + } + @Override + protected void printComponent(Graphics g) { + if( DEBUG ) { + System.err.println("AWT printComponent.X: "+printAWTTiles); + } + print(g); } @Override @@ -411,58 +791,143 @@ public void reshape(int x, int y, int width, int height) { super.setOpaque(opaque); } + @Override public void addGLEventListener(GLEventListener listener) { - drawableHelper.addGLEventListener(listener); + helper.addGLEventListener(listener); } + @Override public void addGLEventListener(int index, GLEventListener listener) { - drawableHelper.addGLEventListener(index, listener); + helper.addGLEventListener(index, listener); + } + + @Override + public int getGLEventListenerCount() { + return helper.getGLEventListenerCount(); + } + + @Override + public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException { + return helper.getGLEventListener(index); + } + + @Override + public boolean areAllGLEventListenerInitialized() { + return helper.areAllGLEventListenerInitialized(); + } + + @Override + public boolean getGLEventListenerInitState(GLEventListener listener) { + return helper.getGLEventListenerInitState(listener); + } + + @Override + public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) { + helper.setGLEventListenerInitState(listener, initialized); + } + + @Override + public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) { + final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove); + if (EventQueue.isDispatchThread()) { + r.run(); + } else { + // Multithreaded redrawing of Swing components is not allowed, + // so do everything on the event dispatch thread + try { + EventQueue.invokeAndWait(r); + } catch (Exception e) { + throw new GLException(e); + } + } + return r.listener; } - public void removeGLEventListener(GLEventListener listener) { - drawableHelper.removeGLEventListener(listener); + @Override + public GLEventListener removeGLEventListener(GLEventListener listener) { + return helper.removeGLEventListener(listener); } + @Override public void setAnimator(GLAnimatorControl animatorControl) { - drawableHelper.setAnimator(animatorControl); + helper.setAnimator(animatorControl); } + @Override public GLAnimatorControl getAnimator() { - return drawableHelper.getAnimator(); + return helper.getAnimator(); } - public void invoke(boolean wait, GLRunnable glRunnable) { - drawableHelper.invoke(this, wait, glRunnable); + @Override + public final Thread setExclusiveContextThread(Thread t) throws GLException { + return helper.setExclusiveContextThread(t, getContext()); } + @Override + public final Thread getExclusiveContextThread() { + return helper.getExclusiveContextThread(); + } + + @Override + public boolean invoke(boolean wait, GLRunnable glRunnable) { + return helper.invoke(this, wait, glRunnable); + } + + @Override + public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) { + return helper.invoke(this, wait, glRunnables); + } + + @Override public GLContext createContext(GLContext shareWith) { - return (null != backend) ? backend.createContext(shareWith) : null; + final Backend b = backend; + if ( null == b ) { + return null; + } + return b.createContext(shareWith); } + @Override public void setRealized(boolean realized) { } + @Override public boolean isRealized() { return isInitialized; } - public void setContext(GLContext ctx) { - if (backend == null) { - return; + @Override + public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) { + final Backend b = backend; + if ( null == b ) { + return null; } - if(null != ctx) { - ctx.setContextCreationFlags(additionalCtxCreationFlags); + final GLContext oldCtx = b.getContext(); + GLDrawableHelper.switchContext(b.getDrawable(), oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); + b.setContext(newCtx); + return oldCtx; + } + + + @Override + public final GLDrawable getDelegatedDrawable() { + final Backend b = backend; + if ( null == b ) { + return null; } - backend.setContext(ctx); + return b.getDrawable(); } + @Override public GLContext getContext() { - if (backend == null) { - return null; + final Backend b = backend; + if ( null == b ) { + return null; } - return backend.getContext(); + return b.getContext(); } + @Override public GL getGL() { if (Beans.isDesignTime()) { return null; @@ -471,6 +936,7 @@ public void reshape(int x, int y, int width, int height) { return (context == null) ? null : context.getGL(); } + @Override public GL setGL(GL gl) { GLContext context = getContext(); if (context != null) { @@ -480,39 +946,46 @@ public void reshape(int x, int y, int width, int height) { return null; } - public void setAutoSwapBufferMode(boolean onOrOff) { - // In the current implementation this is a no-op. Both the pbuffer - // and pixmap based rendering paths use a single-buffered surface - // so swapping the buffers doesn't do anything. We also don't - // currently have the provision to skip copying the data to the - // Swing portion of the GLJPanel in any of the rendering paths. + @Override + public void setAutoSwapBufferMode(boolean enable) { + this.autoSwapBufferMode = enable; + boolean backendHandlesSwapBuffer = false; + if( isInitialized ) { + final Backend b = backend; + if ( null != b ) { + backendHandlesSwapBuffer= b.handlesSwapBuffer(); + } + } + if( !backendHandlesSwapBuffer ) { + helper.setAutoSwapBufferMode(enable); + } } + @Override public boolean getAutoSwapBufferMode() { - // In the current implementation this is a no-op. Both the pbuffer - // and pixmap based rendering paths use a single-buffered surface - // so swapping the buffers doesn't do anything. We also don't - // currently have the provision to skip copying the data to the - // Swing portion of the GLJPanel in any of the rendering paths. - return true; - } - + return autoSwapBufferMode; + } + + @Override public void swapBuffers() { - // In the current implementation this is a no-op. Both the pbuffer - // and pixmap based rendering paths use a single-buffered surface - // so swapping the buffers doesn't do anything. We also don't - // currently have the provision to skip copying the data to the - // Swing portion of the GLJPanel in any of the rendering paths. + if( isInitialized ) { + final Backend b = backend; + if ( null != b ) { + b.swapBuffers(); + } + } } + @Override public void setContextCreationFlags(int flags) { additionalCtxCreationFlags = flags; } - + + @Override public int getContextCreationFlags() { - return additionalCtxCreationFlags; + return additionalCtxCreationFlags; } - + /** For a translucent GLJPanel (one for which {@link #setOpaque setOpaque}(false) has been called), indicates whether the application should preserve the OpenGL color buffer @@ -526,99 +999,181 @@ public void reshape(int x, int y, int width, int height) { to perform OpenGL rendering using the GLJPanel into the same OpenGL drawable as the Swing implementation uses. */ public boolean shouldPreserveColorBufferIfTranslucent() { - return oglPipelineEnabled; + return oglPipelineUsable(); + } + + /** + * {@inheritDoc} + * <p> + * Method returns a valid value only <i>after</i> + * the backend has been initialized, either {@link #initializeBackend(boolean) eagerly} + * or manually via the first display call.<br/> + * Method always returns a valid value when called from within a {@link GLEventListener}. + * </p> + */ + @Override + public boolean isGLOriented() { + final Backend b = backend; + if ( null == b ) { + return true; + } + return b.getDrawable().isGLOriented(); + } + + /** + * Set skipping {@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/> + * See constraints of {@link #isGLOriented()}. + * </p> + */ + public final void setSkipGLOrientationVerticalFlip(boolean v) { + skipGLOrientationVerticalFlip = v; + } + /** See {@link #setSkipGLOrientationVerticalFlip(boolean)}. */ + public final boolean getSkipGLOrientationVerticalFlip() { + return skipGLOrientationVerticalFlip; } + @Override public GLCapabilitiesImmutable getChosenGLCapabilities() { - return backend.getChosenGLCapabilities(); + final Backend b = backend; + if ( null == b ) { + return null; + } + return b.getChosenGLCapabilities(); } + @Override public final GLProfile getGLProfile() { return glProfile; } + @Override public NativeSurface getNativeSurface() { - throw new GLException("FIXME"); + final Backend b = backend; + if ( null == b ) { + return null; + } + return b.getDrawable().getNativeSurface(); } + @Override public long getHandle() { - throw new GLException("FIXME"); + final Backend b = backend; + if ( null == b ) { + return 0; + } + return b.getDrawable().getNativeSurface().getSurfaceHandle(); } + @Override public final GLDrawableFactory getFactory() { return factory; } + /** + * Returns the used texture unit, i.e. a value of [0..n], or -1 if non used. + * <p> + * If implementation uses a texture-unit, it will be known only after the first initialization, i.e. display call. + * </p> + * <p> + * See <a href="#fboGLSLVerticalFlip">FBO / GLSL Vertical Flip</a>. + * </p> + */ + public final int getTextureUnit() { + final Backend b = backend; + if ( null == b ) { + return -1; + } + return b.getTextureUnit(); + } + + /** + * Allows user to request a texture unit to be used, + * must be called before the first initialization, i.e. {@link #display()} call. + * <p> + * Defaults to <code>0</code>. + * </p> + * <p> + * See <a href="#fboGLSLVerticalFlip">FBO / GLSL Vertical Flip</a>. + * </p> + * + * @param v requested texture unit + * @see #getTextureUnit() + */ + public final void setTextureUnit(int v) { + requestedTextureUnit = v; + } + //---------------------------------------------------------------------- // Internals only below this point // - private void createAndInitializeBackend() { - if (panelWidth == 0 || - panelHeight == 0) { - // See whether we have a non-zero size yet and can go ahead with - // initialization - if (reshapeWidth == 0 || - reshapeHeight == 0) { - return; - } - - // Pull down reshapeWidth and reshapeHeight into panelWidth and - // panelHeight eagerly in order to complete initialization, and - // force a reshape later - panelWidth = reshapeWidth; - panelHeight = reshapeHeight; - } + private final Object initSync = new Object(); + private boolean initializeBackendImpl() { + synchronized(initSync) { + if( !isInitialized ) { + if ( 0 >= panelWidth || 0 >= panelHeight ) { + // See whether we have a non-zero size yet and can go ahead with + // initialization + if (0 >= reshapeWidth || 0 >= reshapeHeight ) { + return false; + } - do { - if (backend == null) { - if (oglPipelineEnabled) { - backend = new J2DOGLBackend(); - } else { - if (!hardwareAccelerationDisabled && - factory.canCreateGLPbuffer(null)) { - backend = new PbufferBackend(); - } else { - if (softwareRenderingDisabled) { - throw new GLException("Fallback to software rendering disabled by user"); + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel.createAndInitializeBackend: " +panelWidth+"x"+panelHeight + " -> " + reshapeWidth+"x"+reshapeHeight); + } + // Pull down reshapeWidth and reshapeHeight into panelWidth and + // panelHeight eagerly in order to complete initialization, and + // force a reshape later + panelWidth = reshapeWidth; + panelHeight = reshapeHeight; } - backend = new SoftwareBackend(); - } - } - } - if (!isInitialized) { - backend.initialize(); - } - // The backend might set itself to null, indicating it punted to - // a different implementation -- try again - } while (backend == null); + if ( null == backend ) { + if ( oglPipelineUsable() ) { + backend = new J2DOGLBackend(); + } else { + backend = new OffscreenBackend(glProfile, customPixelBufferProvider); + } + isInitialized = false; + } - awtWindowClosingProtocol.addClosingListenerOneShot(); + if (!isInitialized) { + backend.initialize(); + } + return isInitialized; + } else { + return true; + } + } } + @Override public WindowClosingMode getDefaultCloseOperation() { return awtWindowClosingProtocol.getDefaultCloseOperation(); } + @Override public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { return awtWindowClosingProtocol.setDefaultCloseOperation(op); } - private void handleReshape() { - panelWidth = reshapeWidth; - panelHeight = reshapeHeight; - + private boolean handleReshape() { if (DEBUG) { - System.err.println(getThreadName()+": GLJPanel.handleReshape: (w,h) = (" + - panelWidth + "," + panelHeight + ")"); + System.err.println(getThreadName()+": GLJPanel.handleReshape: " +panelWidth+"x"+panelHeight + " -> " + reshapeWidth+"x"+reshapeHeight); } + panelWidth = reshapeWidth; + panelHeight = reshapeHeight; - sendReshape = true; - backend.handleReshape(); - handleReshape = false; + return backend.handleReshape(); } - + // This is used as the GLEventListener for the pbuffer-based backend // as well as the callback mechanism for the other backends class Updater implements GLEventListener { @@ -628,18 +1183,21 @@ public void reshape(int x, int y, int width, int height) { this.g = g; } + @Override public void init(GLAutoDrawable drawable) { if (!backend.preGL(g)) { return; } - drawableHelper.init(GLJPanel.this); + helper.init(GLJPanel.this, !sendReshape); backend.postGL(g, false); } + @Override public void dispose(GLAutoDrawable drawable) { - drawableHelper.dispose(GLJPanel.this); + helper.disposeAllGLEventListener(GLJPanel.this, false); } + @Override public void display(GLAutoDrawable drawable) { if (!backend.preGL(g)) { return; @@ -648,68 +1206,100 @@ public void reshape(int x, int y, int width, int height) { if (DEBUG) { System.err.println(getThreadName()+": GLJPanel.display: reshape(" + viewportX + "," + viewportY + " " + panelWidth + "x" + panelHeight + ")"); } - drawableHelper.reshape(GLJPanel.this, viewportX, viewportY, panelWidth, panelHeight); + helper.reshape(GLJPanel.this, viewportX, viewportY, panelWidth, panelHeight); sendReshape = false; } - drawableHelper.display(GLJPanel.this); + helper.display(GLJPanel.this); backend.postGL(g, true); } - public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { - // This is handled above and dispatched directly to the appropriate context + public void plainPaint(GLAutoDrawable drawable) { + helper.display(GLJPanel.this); } - public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { + @Override + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + // This is handled above and dispatched directly to the appropriate context } } + @Override public String toString() { - return "AWT-GLJPanel[ "+((null!=backend)?backend.getDrawable().getClass().getName():"null-drawable")+"]"; + final GLDrawable d = ( null != backend ) ? backend.getDrawable() : null; + return "AWT-GLJPanel[ drawableType "+ ( ( null != d ) ? d.getClass().getName() : "null" ) + + ", chosenCaps " + getChosenGLCapabilities() + + "]"; } - private final Runnable postDisposeAction = new Runnable() { - public void run() { - if (backend != null && !backend.isUsingOwnThreadManagment()) { - backend.destroy(); - backend = null; - isInitialized = false; - } - } - }; - private final Runnable disposeAction = new Runnable() { + @Override public void run() { - drawableHelper.disposeGL(GLJPanel.this, backend.getDrawable(), backend.getContext(), postDisposeAction); + 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 (GLException gle) { + gle.printStackTrace(); + } + } + if ( backendDestroy ) { + backend.destroy(); + backend = null; + isInitialized = false; + } + } } - }; + }; private final Runnable updaterInitAction = new Runnable() { + @Override public void run() { updater.init(GLJPanel.this); } }; private final Runnable updaterDisplayAction = new Runnable() { + @Override public void run() { updater.display(GLJPanel.this); } }; + private final Runnable updaterPlainDisplayAction = new Runnable() { + @Override + public void run() { + updater.plainPaint(GLJPanel.this); + } + }; + private final Runnable paintImmediatelyAction = new Runnable() { + @Override public void run() { paintImmediately(0, 0, getWidth(), getHeight()); } - }; + }; - private int getNextPowerOf2(int number) { - // Workaround for problems where 0 width or height are transiently - // seen during layout - if (number == 0) { - return 2; - } - return GLBuffers.getNextPowerOf2(number); - } + private class DisposeGLEventListenerAction implements Runnable { + GLEventListener listener; + private final boolean remove; + private DisposeGLEventListenerAction(GLEventListener listener, boolean remove) { + this.listener = listener; + this.remove = remove; + } + + @Override + public void run() { + final Backend b = backend; + if ( null != b ) { + listener = helper.disposeGLEventListener(GLJPanel.this, b.getDrawable(), b.getContext(), listener, remove); + } + } + }; private int getGLInteger(GL gl, int which) { int[] tmp = new int[1]; @@ -717,492 +1307,555 @@ public void reshape(int x, int y, int width, int height) { return tmp[0]; } - protected static String getThreadName() { - return Thread.currentThread().getName(); - } - + protected static String getThreadName() { return Thread.currentThread().getName(); } + //---------------------------------------------------------------------- // Implementations of the various backends // - // Abstraction of the different rendering backends: i.e., pure - // software / pixmap rendering, pbuffer-based acceleration, Java 2D - // / JOGL bridge + /** + * Abstraction of the different rendering backends: i.e., pure + * software / pixmap rendering, pbuffer-based acceleration, Java 2D + * JOGL bridge + */ static interface Backend { - // Create, Destroy, .. - public boolean isUsingOwnThreadManagment(); - - // Called each time the backend needs to initialize itself + /** Create, Destroy, .. */ + public boolean isUsingOwnLifecycle(); + + /** Called each time the backend needs to initialize itself */ public void initialize(); - // Called when the backend should clean up its resources + /** Called when the backend should clean up its resources */ public void destroy(); - // Called when the opacity of the GLJPanel is changed + /** Called when the opacity of the GLJPanel is changed */ public void setOpaque(boolean opaque); - // Called to manually create an additional OpenGL context against - // this GLJPanel + /** + * Called to manually create an additional OpenGL context against + * this GLJPanel + */ public GLContext createContext(GLContext shareWith); - // Called to set the current backend's GLContext + /** Called to set the current backend's GLContext */ public void setContext(GLContext ctx); - // Called to get the current backend's GLContext + /** Called to get the current backend's GLContext */ public GLContext getContext(); - // Called to get the current backend's GLDrawable + /** Called to get the current backend's GLDrawable */ public GLDrawable getDrawable(); - // Called to fetch the "real" GLCapabilities for the backend + /** Returns the used texture unit, i.e. a value of [0..n], or -1 if non used. */ + public int getTextureUnit(); + + /** Called to fetch the "real" GLCapabilities for the backend */ public GLCapabilitiesImmutable getChosenGLCapabilities(); - // Called to fetch the "real" GLProfile for the backend + /** Called to fetch the "real" GLProfile for the backend */ public GLProfile getGLProfile(); - // Called to handle a reshape event. When this is called, the - // OpenGL context associated with the backend is not current, to - // make it easier to destroy and re-create pbuffers if necessary. - public void handleReshape(); - - // Called before the OpenGL work is done in init() and display(). - // If false is returned, this render is aborted. + /** + * Called to handle a reshape event. When this is called, the + * OpenGL context associated with the backend is not current, to + * make it easier to destroy and re-create pbuffers if necessary. + */ + public boolean handleReshape(); + + /** + * Called before the OpenGL work is done in init() and display(). + * If false is returned, this render is aborted. + */ public boolean preGL(Graphics g); - // Called after the OpenGL work is done in init() and display(). - // The isDisplay argument indicates whether this was called on - // behalf of a call to display() rather than init(). + /** + * Return true if backend handles 'swap buffer' itself + * and hence the helper's setAutoSwapBuffer(enable) shall not be called. + * In this case {@link GLJPanel#autoSwapBufferMode} is being used + * in the backend to determine whether to swap buffers or not. + */ + public boolean handlesSwapBuffer(); + + /** + * Shall invoke underlying drawable's swapBuffer. + */ + public void swapBuffers(); + + /** + * Called after the OpenGL work is done in init() and display(). + * The isDisplay argument indicates whether this was called on + * behalf of a call to display() rather than init(). + */ public void postGL(Graphics g, boolean isDisplay); - // Called from within paintComponent() to initiate the render + /** Called from within paintComponent() to initiate the render */ public void doPaintComponent(Graphics g); + + /** Called from print .. no backend update desired onscreen */ + public void doPlainPaint(); } // Base class used by both the software (pixmap) and pbuffer // backends, both of which rely on reading back the OpenGL frame // buffer and drawing it with a BufferedImage - abstract class AbstractReadbackBackend implements Backend { - // This image is exactly the correct size to render into the panel - protected BufferedImage offscreenImage; + class OffscreenBackend implements Backend { + private final AWTGLPixelBufferProvider pixelBufferProvider; + private final boolean useSingletonBuffer; + private AWTGLPixelBuffer pixelBuffer; + private BufferedImage alignedImage; + // One of these is used to store the read back pixels before storing // in the BufferedImage - protected ByteBuffer readBackBytes; - protected IntBuffer readBackInts; - protected int readBackWidthInPixels; - protected int readBackHeightInPixels; + protected IntBuffer readBackIntsForCPUVFlip; + + // Implementation using software rendering + private volatile GLDrawableImpl offscreenDrawable; // volatile: avoid locking for read-only access + private boolean offscreenIsFBO; + private FBObject fboFlipped; + private GLSLTextureRaster glslTextureRaster; - private int glFormat; - private int glType; + private volatile GLContextImpl offscreenContext; // volatile: avoid locking for read-only access + private boolean flipVertical; + private int frameCount = 0; // For saving/restoring of OpenGL state during ReadPixels - private int[] swapbytes = new int[1]; - private int[] rowlength = new int[1]; - private int[] skiprows = new int[1]; - private int[] skippixels = new int[1]; - private int[] alignment = new int[1]; - - public void setOpaque(boolean opaque) { - if (opaque != isOpaque()) { - if (offscreenImage != null) { - offscreenImage.flush(); - offscreenImage = null; - } - } - } + private final GLPixelStorageModes psm = new GLPixelStorageModes(); - public boolean preGL(Graphics g) { - // Empty in this implementation - return true; + OffscreenBackend(GLProfile glp, AWTGLPixelBufferProvider custom) { + if(null == custom) { + pixelBufferProvider = getSingleAWTGLPixelBufferProvider(); + } else { + pixelBufferProvider = custom; + } + if( pixelBufferProvider instanceof SingletonGLPixelBufferProvider ) { + useSingletonBuffer = true; + } else { + useSingletonBuffer = false; + } } - public void postGL(Graphics g, boolean isDisplay) { - if (isDisplay) { - // Must now copy pixels from offscreen context into surface - if (offscreenImage == null) { - if (panelWidth > 0 && panelHeight > 0) { - // It looks like NVidia's drivers (at least the ones on my - // notebook) are buggy and don't allow a sub-rectangle to be - // read from a pbuffer...this doesn't really matter because - // it's the Graphics.drawImage() calls that are the - // bottleneck - - int awtFormat = 0; - - // Should be more flexible in these BufferedImage formats; - // perhaps see what the preferred image types are on the - // given platform - if (isOpaque()) { - awtFormat = BufferedImage.TYPE_INT_RGB; - } else { - awtFormat = BufferedImage.TYPE_INT_ARGB; - } + @Override + public final boolean isUsingOwnLifecycle() { return false; } - offscreenImage = new BufferedImage(panelWidth, - panelHeight, - awtFormat); - switch (awtFormat) { - case BufferedImage.TYPE_3BYTE_BGR: - glFormat = GL2.GL_BGR; - glType = GL.GL_UNSIGNED_BYTE; - readBackBytes = ByteBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels * 3); - break; - - case BufferedImage.TYPE_INT_RGB: - case BufferedImage.TYPE_INT_ARGB: - glFormat = GL.GL_BGRA; - glType = getGLPixelType(); - readBackInts = IntBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels); - break; - - default: - // FIXME: Support more off-screen image types (current - // offscreen context implementations don't use others, and - // some of the OpenGL formats aren't supported in the 1.1 - // headers, which we're currently using) - throw new GLException("Unsupported offscreen image type " + awtFormat); - } + @Override + public final void initialize() { + if(DEBUG) { + System.err.println(getThreadName()+": OffscreenBackend: initialize() - frameCount "+frameCount); + } + try { + final GLContext[] shareWith = { null }; + if( helper.isSharedGLContextPending(shareWith) ) { + return; // pending .. } - } - - if (offscreenImage != null) { - GL2 gl = getGL().getGL2(); - // Save current modes - gl.glGetIntegerv(GL2.GL_PACK_SWAP_BYTES, swapbytes, 0); - gl.glGetIntegerv(GL2.GL_PACK_ROW_LENGTH, rowlength, 0); - gl.glGetIntegerv(GL2.GL_PACK_SKIP_ROWS, skiprows, 0); - gl.glGetIntegerv(GL2.GL_PACK_SKIP_PIXELS, skippixels, 0); - gl.glGetIntegerv(GL2.GL_PACK_ALIGNMENT, alignment, 0); - - gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, GL.GL_FALSE); - gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, readBackWidthInPixels); - gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, 0); - gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, 0); - gl.glPixelStorei(GL2.GL_PACK_ALIGNMENT, 1); - - // Actually read the pixels. - gl.glReadBuffer(GL2.GL_FRONT); - if (readBackBytes != null) { - gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackBytes); - } else if (readBackInts != null) { - gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackInts); + offscreenDrawable = (GLDrawableImpl) factory.createOffscreenDrawable( + null /* default platform device */, + offscreenCaps, + chooser, + panelWidth, panelHeight); + offscreenDrawable.setRealized(true); + if( DEBUG ) { + offscreenDrawable.getNativeSurface().addSurfaceUpdatedListener(new SurfaceUpdatedListener() { + @Override + public final void surfaceUpdated(Object updater, NativeSurface ns, long when) { + System.err.println(getThreadName()+": OffscreenBackend.swapBuffers - frameCount "+frameCount); + } } ); } - // Restore saved modes. - gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, swapbytes[0]); - gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, rowlength[0]); - gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, skiprows[0]); - gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, skippixels[0]); - gl.glPixelStorei(GL2.GL_PACK_ALIGNMENT, alignment[0]); - - if (readBackBytes != null || readBackInts != null) { - // Copy temporary data into raster of BufferedImage for faster - // blitting Note that we could avoid this copy in the cases - // where !offscreenContext.offscreenImageNeedsVerticalFlip(), - // but that's the software rendering path which is very slow - // anyway - Object src = null; - Object dest = null; - int srcIncr = 0; - int destIncr = 0; - - if (readBackBytes != null) { - src = readBackBytes.array(); - dest = ((DataBufferByte) offscreenImage.getRaster().getDataBuffer()).getData(); - srcIncr = readBackWidthInPixels * 3; - destIncr = offscreenImage.getWidth() * 3; - } else { - src = readBackInts.array(); - dest = ((DataBufferInt) offscreenImage.getRaster().getDataBuffer()).getData(); - srcIncr = readBackWidthInPixels; - destIncr = offscreenImage.getWidth(); - } - - if (flipVertically()) { - int srcPos = 0; - int destPos = (offscreenImage.getHeight() - 1) * destIncr; - for (; destPos >= 0; srcPos += srcIncr, destPos -= destIncr) { - System.arraycopy(src, srcPos, dest, destPos, destIncr); + offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith[0]); + offscreenContext.setContextCreationFlags(additionalCtxCreationFlags); + if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) { + isInitialized = true; + helper.setAutoSwapBufferMode(false); // we handle swap-buffers, see handlesSwapBuffer() + + final GL gl = offscreenContext.getGL(); + flipVertical = !GLJPanel.this.skipGLOrientationVerticalFlip && offscreenDrawable.isGLOriented(); + 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; + 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()+"]"); } - } else { - int srcPos = 0; - int destEnd = destIncr * offscreenImage.getHeight(); - for (int destPos = 0; destPos < destEnd; srcPos += srcIncr, destPos += destIncr) { - System.arraycopy(src, srcPos, dest, destPos, destIncr); + if( useGLSLFlip ) { + final GLFBODrawable fboDrawable = (GLFBODrawable) offscreenDrawable; + fboDrawable.setTextureUnit( GLJPanel.this.requestedTextureUnit ); + try { + fboFlipped = new FBObject(); + fboFlipped.reset(gl, fboDrawable.getWidth(), fboDrawable.getHeight(), 0, false); + fboFlipped.attachTexture2D(gl, 0, chosenCaps.getAlphaBits()>0); + // fboFlipped.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + glslTextureRaster = new GLSLTextureRaster(fboDrawable.getTextureUnit(), true); + glslTextureRaster.init(gl.getGL2ES2()); + glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, fboDrawable.getWidth(), fboDrawable.getHeight()); + } catch (Exception ex) { + ex.printStackTrace(); + if(null != glslTextureRaster) { + glslTextureRaster.dispose(gl.getGL2ES2()); + glslTextureRaster = null; + } + if(null != fboFlipped) { + fboFlipped.destroy(gl); + fboFlipped = null; + } + } + } else { + fboFlipped = null; + glslTextureRaster = null; + } + offscreenContext.release(); + } else { + isInitialized = false; + } + } finally { + if( !isInitialized ) { + if(null != offscreenContext) { + offscreenContext.destroy(); + offscreenContext = null; + } + if(null != offscreenDrawable) { + offscreenDrawable.setRealized(false); + offscreenDrawable = null; } - } - - // Note: image will be drawn back in paintComponent() for - // correctness on all platforms } - } - } - } - - public void doPaintComponent(Graphics g) { - doPaintComponentImpl(); - if (offscreenImage != null) { - // Draw resulting image in one shot - g.drawImage(offscreenImage, 0, 0, - offscreenImage.getWidth(), - offscreenImage.getHeight(), - GLJPanel.this); - } - } - - protected abstract void doPaintComponentImpl(); - protected abstract int getGLPixelType(); - protected abstract boolean flipVertically(); - } - - class SoftwareBackend extends AbstractReadbackBackend { - // Implementation using software rendering - private GLDrawableImpl offscreenDrawable; - private GLContextImpl offscreenContext; - - public boolean isUsingOwnThreadManagment() { return false; } - - public void initialize() { - if(DEBUG) { - System.err.println(getThreadName()+": SoftwareBackend: initialize()"); } - // Fall-through path: create an offscreen context instead - offscreenDrawable = (GLDrawableImpl) factory.createOffscreenDrawable( - null /* default platform device */, - offscreenCaps, - chooser, - Math.max(1, panelWidth), - Math.max(1, panelHeight)); - offscreenDrawable.setRealized(true); - offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith); - offscreenContext.setContextCreationFlags(additionalCtxCreationFlags); - - isInitialized = true; } - public void destroy() { + @Override + public final void destroy() { if(DEBUG) { - System.err.println(getThreadName()+": SoftwareBackend: destroy() - offscreenContext: "+(null!=offscreenContext)+" - offscreenDrawable: "+(null!=offscreenDrawable)); + System.err.println(getThreadName()+": OffscreenBackend: destroy() - offscreenContext: "+(null!=offscreenContext)+" - offscreenDrawable: "+(null!=offscreenDrawable)+" - frameCount "+frameCount); } - if (offscreenContext != null) { - offscreenContext.destroy(); - offscreenContext = null; + if ( null != offscreenContext && offscreenContext.isCreated() ) { + if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) { + try { + final GL gl = offscreenContext.getGL(); + if(null != glslTextureRaster) { + glslTextureRaster.dispose(gl.getGL2ES2()); + } + if(null != fboFlipped) { + fboFlipped.destroy(gl); + } + } finally { + offscreenContext.destroy(); + } + } } + offscreenContext = null; + glslTextureRaster = null; + fboFlipped = null; + offscreenContext = null; + if (offscreenDrawable != null) { final AbstractGraphicsDevice adevice = offscreenDrawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); - offscreenDrawable.destroy(); + offscreenDrawable.setRealized(false); offscreenDrawable = null; if(null != adevice) { adevice.close(); } } - } + offscreenIsFBO = false; - public GLContext createContext(GLContext shareWith) { - return (null != offscreenDrawable) ? offscreenDrawable.createContext(shareWith) : null; + if( null != readBackIntsForCPUVFlip ) { + readBackIntsForCPUVFlip.clear(); + readBackIntsForCPUVFlip = null; + } + if( null != pixelBuffer ) { + if( !useSingletonBuffer ) { + pixelBuffer.dispose(); + } + pixelBuffer = null; + } + alignedImage = null; } - public void setContext(GLContext ctx) { - offscreenContext=(GLContextImpl)ctx; + @Override + public final void setOpaque(boolean opaque) { + if ( opaque != isOpaque() && !useSingletonBuffer ) { + pixelBuffer.dispose(); + pixelBuffer = null; + alignedImage = null; + } } - public GLContext getContext() { - return offscreenContext; + @Override + public final boolean preGL(Graphics g) { + // Empty in this implementation + return true; } - public GLDrawable getDrawable() { - return offscreenDrawable; + @Override + public final boolean handlesSwapBuffer() { + return true; } - public GLCapabilitiesImmutable getChosenGLCapabilities() { - if (offscreenDrawable == null) { - return null; - } - return offscreenDrawable.getChosenGLCapabilities(); + @Override + public final void swapBuffers() { + final GLDrawable d = offscreenDrawable; + if( null != d ) { + d.swapBuffers(); + } } - public GLProfile getGLProfile() { - if (offscreenDrawable == null) { - return null; - } - return offscreenDrawable.getGLProfile(); - } + @Override + public final void postGL(Graphics g, boolean isDisplay) { + if (isDisplay) { + if(DEBUG) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: - frameCount "+frameCount); + } + if( autoSwapBufferMode ) { + // Since we only use a single-buffer non-MSAA or double-buffered MSAA offscreenDrawable, + // we can always swap! + offscreenDrawable.swapBuffers(); + } + final GL gl = offscreenContext.getGL(); + + final int componentCount; + final int alignment; + if( isOpaque() ) { + // w/o alpha + componentCount = 3; + alignment = 1; + } else { + // with alpha + componentCount = 4; + alignment = 4; + } - public void handleReshape() { - destroy(); - initialize(); - readBackWidthInPixels = Math.max(1, panelWidth); - readBackHeightInPixels = Math.max(1, panelHeight); + final GLPixelAttributes pixelAttribs = pixelBufferProvider.getAttributes(gl, componentCount); - if (offscreenImage != null) { - offscreenImage.flush(); - offscreenImage = null; - } - } + if( useSingletonBuffer ) { // attempt to fetch the latest AWTGLPixelBuffer + pixelBuffer = (AWTGLPixelBuffer) ((SingletonGLPixelBufferProvider)pixelBufferProvider).getSingleBuffer(pixelAttribs); + } + if( null != pixelBuffer && pixelBuffer.requiresNewBuffer(gl, panelWidth, panelHeight, 0) ) { + pixelBuffer.dispose(); + pixelBuffer = null; + alignedImage = null; + } + if ( null == pixelBuffer ) { + if (0 >= panelWidth || 0 >= panelHeight ) { + return; + } + pixelBuffer = pixelBufferProvider.allocate(gl, pixelAttribs, panelWidth, panelHeight, 1, true, 0); + if(DEBUG) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelBufferProvider isSingletonBufferProvider "+useSingletonBuffer+", 0x"+Integer.toHexString(pixelBufferProvider.hashCode())+", "+pixelBufferProvider.getClass().getSimpleName()); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelBuffer 0x"+Integer.toHexString(pixelBuffer.hashCode())+", "+pixelBuffer+", alignment "+alignment); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" flippedVertical "+flipVertical+", glslTextureRaster "+(null!=glslTextureRaster)); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" panelSize "+panelWidth+"x"+panelHeight); + } + } + if( offscreenDrawable.getWidth() != panelWidth || offscreenDrawable.getHeight() != panelHeight ) { + throw new InternalError("OffscreenDrawable panelSize mismatch (reshape missed): panelSize "+panelWidth+"x"+panelHeight+" != drawable "+offscreenDrawable.getWidth()+"x"+offscreenDrawable.getHeight()+", on thread "+getThreadName()); + } + if( null == alignedImage || + panelWidth != alignedImage.getWidth() || panelHeight != alignedImage.getHeight() || + !pixelBuffer.isDataBufferSource(alignedImage) ) { + alignedImage = pixelBuffer.getAlignedImage(panelWidth, panelHeight); + if(DEBUG) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" new alignedImage "+alignedImage.getWidth()+"x"+alignedImage.getHeight()+", "+alignedImage+", pixelBuffer "+pixelBuffer.width+"x"+pixelBuffer.height+", "+pixelBuffer); + } + } + final IntBuffer readBackInts; - protected void doPaintComponentImpl() { - drawableHelper.invokeGL(offscreenDrawable, offscreenContext, updaterDisplayAction, updaterInitAction); - } + if( !flipVertical || null != glslTextureRaster ) { + readBackInts = (IntBuffer) pixelBuffer.buffer; + } else { + if( null == readBackIntsForCPUVFlip || pixelBuffer.width * pixelBuffer.height > readBackIntsForCPUVFlip.remaining() ) { + readBackIntsForCPUVFlip = IntBuffer.allocate(pixelBuffer.width * pixelBuffer.height); + } + readBackInts = readBackIntsForCPUVFlip; + } - protected int getGLPixelType() { - return offscreenContext.getOffscreenContextPixelDataType(); - } + final TextureState usrTexState, fboTexState; + final int fboTexUnit = GL.GL_TEXTURE0 + ( offscreenIsFBO ? ((GLFBODrawable)offscreenDrawable).getTextureUnit() : 0 ); + + if( offscreenIsFBO ) { + usrTexState = new TextureState(gl, GL.GL_TEXTURE_2D); + if( fboTexUnit != usrTexState.getUnit() ) { + // glActiveTexture(..) + glBindTexture(..) are implicit performed in GLFBODrawableImpl's + // swapBuffers/contextMadeCurent -> swapFBOImpl. + // We need to cache the texture unit's bound texture-id before it's overwritten. + gl.glActiveTexture(fboTexUnit); + fboTexState = new TextureState(gl, fboTexUnit, GL.GL_TEXTURE_2D); + } else { + fboTexState = usrTexState; + } + } else { + usrTexState = null; + fboTexState = null; + } - protected boolean flipVertically() { - return offscreenContext.offscreenImageNeedsVerticalFlip(); - } - } + // Must now copy pixels from offscreen context into surface + if(DEBUG) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.readPixels: - frameCount "+frameCount); + } - class PbufferBackend extends AbstractReadbackBackend { - private GLPbuffer pbuffer; - private int pbufferWidth = 256; - private int pbufferHeight = 256; + // Save current modes + psm.setAlignment(gl, alignment, alignment); + if(gl.isGL2ES3()) { + final GL2ES3 gl2es3 = gl.getGL2ES3(); + gl2es3.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, panelWidth); + gl2es3.glReadBuffer(gl2es3.getDefaultReadBuffer()); + } - public boolean isUsingOwnThreadManagment() { return false; } - - public void initialize() { - if (pbuffer != null) { - throw new InternalError("Creating pbuffer twice without destroying it (memory leak / correctness bug)"); - } - if(DEBUG) { - System.err.println(getThreadName()+": PbufferBackend: initialize()"); - } - try { - pbuffer = factory.createGLPbuffer(null /* default platform device */, - offscreenCaps, - null, - pbufferWidth, - pbufferHeight, - shareWith); - pbuffer.setContextCreationFlags(additionalCtxCreationFlags); - pbuffer.addGLEventListener(updater); - isInitialized = true; - } catch (GLException e) { - if (DEBUG) { - e.printStackTrace(); - System.err.println(getThreadName()+": GLJPanel: Falling back on software rendering because of problems creating pbuffer"); + if(null != glslTextureRaster) { // implies flippedVertical + final boolean viewportChange; + final int[] usrViewport = new int[] { 0, 0, 0, 0 }; + gl.glGetIntegerv(GL.GL_VIEWPORT, usrViewport, 0); + viewportChange = 0 != usrViewport[0] || 0 != usrViewport[1] || + offscreenDrawable.getWidth() != usrViewport[2] || offscreenDrawable.getHeight() != 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.getWidth()+"x"+offscreenDrawable.getHeight()); + } + if( viewportChange ) { + gl.glViewport(0, 0, offscreenDrawable.getWidth(), offscreenDrawable.getHeight()); + } + + // perform vert-flipping via OpenGL/FBO + final GLFBODrawable fboDrawable = (GLFBODrawable)offscreenDrawable; + final FBObject.TextureAttachment fboTex = fboDrawable.getTextureBuffer(GL.GL_FRONT); + + fboFlipped.bind(gl); + + // gl.glActiveTexture(GL.GL_TEXTURE0 + fboDrawable.getTextureUnit()); // implicit by GLFBODrawableImpl: swapBuffers/contextMadeCurent -> swapFBOImpl + gl.glBindTexture(GL.GL_TEXTURE_2D, fboTex.getName()); + // gl.glClear(GL.GL_DEPTH_BUFFER_BIT); // fboFlipped runs w/o DEPTH! + + glslTextureRaster.display(gl.getGL2ES2()); + gl.glReadPixels(0, 0, panelWidth, panelHeight, pixelAttribs.format, pixelAttribs.type, readBackInts); + + fboFlipped.unbind(gl); + if( viewportChange ) { + gl.glViewport(usrViewport[0], usrViewport[1], usrViewport[2], usrViewport[3]); + } + fboTexState.restore(gl); + } else { + gl.glReadPixels(0, 0, panelWidth, panelHeight, pixelAttribs.format, pixelAttribs.type, readBackInts); + + if ( flipVertical ) { + // Copy temporary data into raster of BufferedImage for faster + // blitting Note that we could avoid this copy in the cases + // where !offscreenDrawable.isGLOriented(), + // but that's the software rendering path which is very slow anyway. + final BufferedImage image = alignedImage; + final int[] src = readBackInts.array(); + final int[] dest = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + final int incr = panelWidth; + int srcPos = 0; + int destPos = (panelHeight - 1) * panelWidth; + for (; destPos >= 0; srcPos += incr, destPos -= incr) { + System.arraycopy(src, srcPos, dest, destPos, incr); + } + } + } + if( offscreenIsFBO && fboTexUnit != usrTexState.getUnit() ) { + usrTexState.restore(gl); } - hardwareAccelerationDisabled = true; - backend = null; - isInitialized = false; - createAndInitializeBackend(); - } - } - public void destroy() { - if(DEBUG) { - System.err.println(getThreadName()+": PbufferBackend: destroy() - pbuffer: "+(null!=pbuffer)); - } - if (pbuffer != null) { - pbuffer.destroy(); - pbuffer = null; + // Restore saved modes. + psm.restore(gl); + + // Note: image will be drawn back in paintComponent() for + // correctness on all platforms } } - public GLContext createContext(GLContext shareWith) { - return (null != pbuffer) ? pbuffer.createContext(shareWith) : null; + @Override + public final int getTextureUnit() { + if(null != glslTextureRaster && null != offscreenDrawable) { // implies flippedVertical + return ((GLFBODrawable)offscreenDrawable).getTextureUnit(); + } + return -1; } - public void setContext(GLContext ctx) { - if (pbuffer == null && Beans.isDesignTime()) { - return; - } - pbuffer.setContext(ctx); - } + @Override + public final void doPaintComponent(Graphics g) { + helper.invokeGL(offscreenDrawable, offscreenContext, updaterDisplayAction, updaterInitAction); - public GLContext getContext() { - // Workaround for crashes in NetBeans GUI builder - if (pbuffer == null && Beans.isDesignTime()) { - return null; + if ( null != alignedImage ) { + if( DEBUG ) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.doPaintComponent.drawImage: - frameCount "+frameCount); + } + // Draw resulting image in one shot + g.drawImage(alignedImage, 0, 0, alignedImage.getWidth(), alignedImage.getHeight(), null); // Null ImageObserver since image data is ready. } - return pbuffer.getContext(); + frameCount++; } - public GLDrawable getDrawable() { - return pbuffer; + @Override + public final void doPlainPaint() { + helper.invokeGL(offscreenDrawable, offscreenContext, updaterPlainDisplayAction, updaterInitAction); } - public GLCapabilitiesImmutable getChosenGLCapabilities() { - if (pbuffer == null) { - return null; - } - return pbuffer.getChosenGLCapabilities(); - } - - public GLProfile getGLProfile() { - if (pbuffer == null) { - return null; - } - return pbuffer.getGLProfile(); - } - - public void handleReshape() { - // Use factor larger than 2 during shrinks for some hysteresis - float shrinkFactor = 2.5f; - if ((panelWidth > pbufferWidth) || (panelHeight > pbufferHeight) || - (panelWidth < (pbufferWidth / shrinkFactor)) || (panelHeight < (pbufferHeight / shrinkFactor))) { + @Override + public final boolean handleReshape() { + GLDrawableImpl _drawable = offscreenDrawable; + { + final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, offscreenContext, panelWidth, panelHeight); + if(_drawable != _drawableNew) { + // write back + _drawable = _drawableNew; + offscreenDrawable = _drawableNew; + } + } if (DEBUG) { - System.err.println(getThreadName()+": Resizing pbuffer from (" + pbufferWidth + ", " + pbufferHeight + ") " + - " to fit (" + panelWidth + ", " + panelHeight + ")"); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.handleReshape: " +panelWidth+"x"+panelHeight + " -> " + _drawable.getWidth()+"x"+_drawable.getHeight()); } - // Must destroy and recreate pbuffer to fit - if (pbuffer != null) { - // Watch for errors during pbuffer destruction (due to - // buggy / bad OpenGL drivers, in particular SiS) and fall - // back to software rendering - try { - pbuffer.destroy(); - } catch (GLException e) { - hardwareAccelerationDisabled = true; - backend = null; - isInitialized = false; - // Just disabled hardware acceleration during this resize operation; do a fixup - readBackWidthInPixels = Math.max(1, panelWidth); - readBackHeightInPixels = Math.max(1, panelHeight); - if (DEBUG) { - System.err.println(getThreadName()+": Warning: falling back to software rendering due to bugs in OpenGL drivers"); - e.printStackTrace(); + panelWidth = _drawable.getWidth(); + panelHeight = _drawable.getHeight(); + + if( null != glslTextureRaster ) { + if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) { + try { + final GL gl = offscreenContext.getGL(); + fboFlipped.reset(gl, _drawable.getWidth(), _drawable.getHeight(), 0, false); + glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, _drawable.getWidth(), _drawable.getHeight()); + } finally { + offscreenContext.release(); + } } - createAndInitializeBackend(); - return; - } } - pbuffer = null; - isInitialized = false; - pbufferWidth = getNextPowerOf2(panelWidth); - pbufferHeight = getNextPowerOf2(panelHeight); - if (DEBUG && !hardwareAccelerationDisabled) { - System.err.println(getThreadName()+": New pbuffer size is (" + pbufferWidth + ", " + pbufferHeight + ")"); - } - initialize(); - } + return _drawable.isRealized(); + } - // It looks like NVidia's drivers (at least the ones on my - // notebook) are buggy and don't allow a rectangle of less than - // the pbuffer's width to be read...this doesn't really matter - // because it's the Graphics.drawImage() calls that are the - // bottleneck. Should probably make the size of the offscreen - // image be the exact size of the pbuffer to save some work on - // resize operations... - readBackWidthInPixels = pbufferWidth; - readBackHeightInPixels = panelHeight; - - if (offscreenImage != null) { - offscreenImage.flush(); - offscreenImage = null; - } + @Override + public final GLContext createContext(GLContext shareWith) { + return (null != offscreenDrawable) ? offscreenDrawable.createContext(shareWith) : null; } - protected void doPaintComponentImpl() { - pbuffer.display(); + @Override + public final void setContext(GLContext ctx) { + offscreenContext=(GLContextImpl)ctx; } - protected int getGLPixelType() { - // This seems to be a good choice on all platforms - return GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + @Override + public final GLContext getContext() { + return offscreenContext; } - protected boolean flipVertically() { - return true; + @Override + public final GLDrawable getDrawable() { + return offscreenDrawable; + } + + @Override + public final GLCapabilitiesImmutable getChosenGLCapabilities() { + if (offscreenDrawable == null) { + return null; + } + return offscreenDrawable.getChosenGLCapabilities(); + } + + @Override + public final GLProfile getGLProfile() { + if (offscreenDrawable == null) { + return null; + } + return offscreenDrawable.getGLProfile(); } } @@ -1222,11 +1875,11 @@ public void reshape(int x, int y, int width, int height) { private GLContext joglContext; // State captured from Java2D OpenGL context necessary in order to // properly render into Java2D back buffer - private int[] drawBuffer = new int[1]; - private int[] readBuffer = new int[1]; + private final int[] drawBuffer = new int[1]; + private final int[] readBuffer = new int[1]; // This is required when the FBO option of the Java2D / OpenGL // pipeline is active - private int[] frameBuffer = new int[1]; + private final int[] frameBuffer = new int[1]; // Current (as of this writing) NVidia drivers have a couple of bugs // relating to the sharing of framebuffer and renderbuffer objects // between contexts. It appears we have to (a) reattach the color @@ -1255,9 +1908,11 @@ public void reshape(int x, int y, int width, int height) { // comment related to Issue 274 below private GraphicsConfiguration workaroundConfig; - public boolean isUsingOwnThreadManagment() { return true; } - - public void initialize() { + @Override + public final boolean isUsingOwnLifecycle() { return true; } + + @Override + public final void initialize() { if(DEBUG) { System.err.println(getThreadName()+": J2DOGL: initialize()"); } @@ -1265,8 +1920,10 @@ public void reshape(int x, int y, int width, int height) { isInitialized = true; } - public void destroy() { + @Override + public final void destroy() { Java2D.invokeWithOGLContextCurrent(null, new Runnable() { + @Override public void run() { if(DEBUG) { System.err.println(getThreadName()+": J2DOGL: destroy() - joglContext: "+(null!=joglContext)+" - joglDrawable: "+(null!=joglDrawable)); @@ -1284,45 +1941,58 @@ public void reshape(int x, int y, int width, int height) { }); } - public void setOpaque(boolean opaque) { + @Override + public final void setOpaque(boolean opaque) { // Empty in this implementation } - public GLContext createContext(GLContext shareWith) { + @Override + public final GLContext createContext(GLContext shareWith) { if(null != shareWith) { throw new GLException("J2DOGLBackend cannot create context w/ additional shared context, since it already needs to share the context w/ J2D."); } return (null != joglDrawable && null != j2dContext) ? joglDrawable.createContext(j2dContext) : null; } - public void setContext(GLContext ctx) { + @Override + public final void setContext(GLContext ctx) { joglContext=ctx; } - public GLContext getContext() { + @Override + public final GLContext getContext() { return joglContext; } - public GLDrawable getDrawable() { + @Override + public final GLDrawable getDrawable() { return joglDrawable; } - public GLCapabilitiesImmutable getChosenGLCapabilities() { - // FIXME: should do better than this; is it possible to using only platform-independent code? + @Override + public final int getTextureUnit() { return -1; } + + @Override + public final GLCapabilitiesImmutable getChosenGLCapabilities() { + // FIXME: should do better than this; is it possible to query J2D Caps ? return new GLCapabilities(null); } - public GLProfile getGLProfile() { - // FIXME: should do better than this; is it possible to using only platform-independent code? + @Override + public final GLProfile getGLProfile() { + // FIXME: should do better than this; is it possible to query J2D's Profile ? return GLProfile.getDefault(GLProfile.getDefaultDevice()); } - public void handleReshape() { + @Override + public final boolean handleReshape() { // Empty in this implementation + return true; } - public boolean preGL(Graphics g) { - GL2 gl = joglContext.getGL().getGL2(); + @Override + public final boolean preGL(Graphics g) { + final GL2 gl = joglContext.getGL().getGL2(); // Set up needed state in JOGL context from Java2D context gl.glEnable(GL2.GL_SCISSOR_TEST); Rectangle r = Java2D.getOGLScissorBox(g); @@ -1403,13 +2073,13 @@ public void reshape(int x, int y, int width, int height) { int[] height = new int[1]; gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2.GL_TEXTURE_WIDTH, width, 0); gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2.GL_TEXTURE_HEIGHT, height, 0); - + gl.glGenRenderbuffers(1, frameBufferDepthBuffer, 0); if (DEBUG) { System.err.println(getThreadName()+": GLJPanel: Generated frameBufferDepthBuffer " + frameBufferDepthBuffer[0] + " with width " + width[0] + ", height " + height[0]); } - + gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, frameBufferDepthBuffer[0]); // FIXME: may need a loop here like in Java2D gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, GL2GL3.GL_DEPTH_COMPONENT24, width[0], height[0]); @@ -1457,11 +2127,25 @@ public void reshape(int x, int y, int width, int height) { return true; } - public void postGL(Graphics g, boolean isDisplay) { + @Override + public final boolean handlesSwapBuffer() { + return false; + } + + @Override + public final void swapBuffers() { + final GLDrawable d = joglDrawable; + if( null != d ) { + d.swapBuffers(); + } + } + + @Override + public final void postGL(Graphics g, boolean isDisplay) { // Cause OpenGL pipeline to flush its results because // otherwise it's possible we will buffer up multiple frames' // rendering results, resulting in apparent mouse lag - GL gl = getGL(); + GL gl = joglContext.getGL(); gl.glFinish(); if (Java2D.isFBOEnabled() && @@ -1473,7 +2157,8 @@ public void reshape(int x, int y, int width, int height) { } } - public void doPaintComponent(final Graphics g) { + @Override + public final void doPaintComponent(final Graphics g) { // This is a workaround for an issue in the Java 2D / JOGL // bridge (reported by an end user as JOGL Issue 274) where Java // 2D can occasionally leave its internal OpenGL context current @@ -1498,10 +2183,12 @@ public void reshape(int x, int y, int width, int height) { getDefaultScreenDevice(). getDefaultConfiguration(); } - Java2D.invokeWithOGLSharedContextCurrent(workaroundConfig, new Runnable() { public void run() {}}); + Java2D.invokeWithOGLSharedContextCurrent(workaroundConfig, new Runnable() { @Override + public void run() {}}); } Java2D.invokeWithOGLContextCurrent(g, new Runnable() { + @Override public void run() { if (DEBUG) { System.err.println(getThreadName()+": GLJPanel.invokeWithOGLContextCurrent"); @@ -1549,7 +2236,7 @@ public void reshape(int x, int y, int width, int height) { } isInitialized = false; backend = null; - oglPipelineEnabled = false; + java2DGLPipelineOK = false; handleReshape = true; j2dContext.destroy(); j2dContext = null; @@ -1603,29 +2290,14 @@ public void reshape(int x, int y, int width, int height) { System.err.println("-- Created External Drawable: "+joglDrawable); System.err.println("-- Created Context: "+joglContext); } - } else if (factory.canCreateContextOnJava2DSurface(device)) { - // Mac OS X code path - joglContext = factory.createContextOnJava2DSurface(g, j2dContext); - if (DEBUG) { - System.err.println("-- Created Context: "+joglContext); - } } - /*if (DEBUG) { - joglContext.setGL(new DebugGL2(joglContext.getGL().getGL2())); - }*/ - if (Java2D.isFBOEnabled() && Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT && fbObjectWorkarounds) { createNewDepthBuffer = true; } } - if (joglContext instanceof Java2DGLContext) { - // Mac OS X code path - ((Java2DGLContext) joglContext).setGraphics(g); - } - - drawableHelper.invokeGL(joglDrawable, joglContext, updaterDisplayAction, updaterInitAction); + helper.invokeGL(joglDrawable, joglContext, updaterDisplayAction, updaterInitAction); } } finally { j2dContext.release(); @@ -1634,7 +2306,12 @@ public void reshape(int x, int y, int width, int height) { }); } - private void captureJ2DState(GL gl, Graphics g) { + @Override + public final void doPlainPaint() { + helper.invokeGL(joglDrawable, joglContext, updaterPlainDisplayAction, updaterInitAction); + } + + private final void captureJ2DState(GL gl, Graphics g) { gl.glGetIntegerv(GL2.GL_DRAW_BUFFER, drawBuffer, 0); gl.glGetIntegerv(GL2.GL_READ_BUFFER, readBuffer, 0); if (Java2D.isFBOEnabled() && diff --git a/src/jogl/classes/javax/media/opengl/fixedfunc/GLMatrixFunc.java b/src/jogl/classes/javax/media/opengl/fixedfunc/GLMatrixFunc.java index a34d490c0..b4d788329 100644 --- a/src/jogl/classes/javax/media/opengl/fixedfunc/GLMatrixFunc.java +++ b/src/jogl/classes/javax/media/opengl/fixedfunc/GLMatrixFunc.java @@ -31,69 +31,122 @@ package javax.media.opengl.fixedfunc; import java.nio.*; +import javax.media.opengl.GL; + +/** + * Subset of OpenGL fixed function pipeline's matrix operations. + */ public interface GLMatrixFunc { - public static final int GL_MATRIX_MODE = 0x0BA0; - public static final int GL_MODELVIEW = 0x1700; - public static final int GL_PROJECTION = 0x1701; - // public static final int GL_TEXTURE = 0x1702; // Use GL.GL_TEXTURE due to ambiguous GL usage - public static final int GL_MODELVIEW_MATRIX = 0x0BA6; - public static final int GL_PROJECTION_MATRIX = 0x0BA7; - public static final int GL_TEXTURE_MATRIX = 0x0BA8; - - /** - * glGetFloatv - * @param pname GL_MODELVIEW_MATRIX, GL_PROJECTION_MATRIX or GL_TEXTURE_MATRIX - * @param params the FloatBuffer's position remains unchanged, - * which is the same behavior than the native JOGL GL impl - */ - public void glGetFloatv(int pname, java.nio.FloatBuffer params); - public void glGetFloatv(int pname, float[] params, int params_offset); - /** - * glGetIntegerv - * @param pname GL_MATRIX_MODE - * @param params the FloatBuffer's position remains unchanged - * which is the same behavior than the native JOGL GL impl - */ - public void glGetIntegerv(int pname, IntBuffer params); - public void glGetIntegerv(int pname, int[] params, int params_offset); - - /** - * sets the current matrix - * @param pname GL_MODELVIEW, GL_PROJECTION or GL.GL_TEXTURE - */ - public void glMatrixMode(int mode) ; - - public void glPushMatrix(); - public void glPopMatrix(); - - public void glLoadIdentity() ; - - /** - * glLoadMatrixf - * @param params the FloatBuffer's position remains unchanged, - * which is the same behavior than the native JOGL GL impl - */ - public void glLoadMatrixf(java.nio.FloatBuffer m) ; - public void glLoadMatrixf(float[] m, int m_offset); - - /** - * glMultMatrixf - * @param m the FloatBuffer's position remains unchanged, - * which is the same behavior than the native JOGL GL impl - */ - public void glMultMatrixf(java.nio.FloatBuffer m) ; - public void glMultMatrixf(float[] m, int m_offset); - - public void glTranslatef(float x, float y, float z) ; - - public void glRotatef(float angle, float x, float y, float z); - - public void glScalef(float x, float y, float z) ; - - public void glOrthof(float left, float right, float bottom, float top, float zNear, float zFar) ; - - public void glFrustumf(float left, float right, float bottom, float top, float zNear, float zFar); + public static final int GL_MATRIX_MODE = 0x0BA0; + /** Matrix mode modelview */ + public static final int GL_MODELVIEW = 0x1700; + /** Matrix mode projection */ + public static final int GL_PROJECTION = 0x1701; + // public static final int GL_TEXTURE = 0x1702; // Use GL.GL_TEXTURE due to ambiguous GL usage + /** Matrix access name for modelview */ + public static final int GL_MODELVIEW_MATRIX = 0x0BA6; + /** Matrix access name for projection */ + public static final int GL_PROJECTION_MATRIX = 0x0BA7; + /** Matrix access name for texture */ + public static final int GL_TEXTURE_MATRIX = 0x0BA8; + + /** + * Copy the named matrix into the given storage. + * @param pname {@link #GL_MODELVIEW_MATRIX}, {@link #GL_PROJECTION_MATRIX} or {@link #GL_TEXTURE_MATRIX} + * @param params the FloatBuffer's position remains unchanged, + * which is the same behavior than the native JOGL GL impl + */ + public void glGetFloatv(int pname, java.nio.FloatBuffer params); + + /** + * Copy the named matrix to the given storage at offset. + * @param pname {@link #GL_MODELVIEW_MATRIX}, {@link #GL_PROJECTION_MATRIX} or {@link #GL_TEXTURE_MATRIX} + * @param params storage + * @param params_offset storage offset + */ + public void glGetFloatv(int pname, float[] params, int params_offset); + + /** + * glGetIntegerv + * @param pname {@link #GL_MATRIX_MODE} to receive the current matrix mode + * @param params the FloatBuffer's position remains unchanged + * which is the same behavior than the native JOGL GL impl + */ + public void glGetIntegerv(int pname, IntBuffer params); + public void glGetIntegerv(int pname, int[] params, int params_offset); + + /** + * Sets the current matrix mode. + * @param mode {@link #GL_MODELVIEW}, {@link #GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE}. + */ + public void glMatrixMode(int mode) ; + + /** + * Push the current matrix to it's stack, while preserving it's values. + * <p> + * There exist one stack per matrix mode, i.e. {@link #GL_MODELVIEW}, {@link #GL_PROJECTION} and {@link GL#GL_TEXTURE GL_TEXTURE}. + * </p> + */ + public void glPushMatrix(); + + /** + * Pop the current matrix from it's stack. + * @see #glPushMatrix() + */ + public void glPopMatrix(); + + /** + * Load the current matrix with the identity matrix + */ + public void glLoadIdentity() ; + + /** + * Load the current matrix w/ the provided one. + * @param params the FloatBuffer's position remains unchanged, + * which is the same behavior than the native JOGL GL impl + */ + public void glLoadMatrixf(java.nio.FloatBuffer m) ; + /** + * Load the current matrix w/ the provided one. + */ + public void glLoadMatrixf(float[] m, int m_offset); + + /** + * Multiply the current matrix + * @param m the FloatBuffer's position remains unchanged, + * which is the same behavior than the native JOGL GL impl + */ + public void glMultMatrixf(java.nio.FloatBuffer m) ; + /** + * Multiply the current matrix + */ + public void glMultMatrixf(float[] m, int m_offset); + + /** + * Translate the current matrix. + */ + public void glTranslatef(float x, float y, float z) ; + + /** + * Rotate the current matrix. + */ + public void glRotatef(float angle, float x, float y, float z); + + /** + * Scale the current matrix. + */ + public void glScalef(float x, float y, float z) ; + + /** + * {@link #glMultMatrixf(FloatBuffer) Multiply} the current matrix with the orthogonal matrix. + */ + public void glOrthof(float left, float right, float bottom, float top, float zNear, float zFar) ; + + /** + * {@link #glMultMatrixf(FloatBuffer) Multiply} the current matrix with the frustum matrix. + */ + public void glFrustumf(float left, float right, float bottom, float top, float zNear, float zFar); } diff --git a/src/jogl/classes/javax/media/opengl/fixedfunc/GLPointerFunc.java b/src/jogl/classes/javax/media/opengl/fixedfunc/GLPointerFunc.java index 786835f4d..4aff24b36 100644 --- a/src/jogl/classes/javax/media/opengl/fixedfunc/GLPointerFunc.java +++ b/src/jogl/classes/javax/media/opengl/fixedfunc/GLPointerFunc.java @@ -31,7 +31,7 @@ package javax.media.opengl.fixedfunc; import javax.media.opengl.*; -public interface GLPointerFunc { +public interface GLPointerFunc { public static final int GL_VERTEX_ARRAY = 0x8074; public static final int GL_NORMAL_ARRAY = 0x8075; public static final int GL_COLOR_ARRAY = 0x8076; diff --git a/src/jogl/classes/javax/media/opengl/fixedfunc/GLPointerFuncUtil.java b/src/jogl/classes/javax/media/opengl/fixedfunc/GLPointerFuncUtil.java index e52154c7d..9bd644223 100644 --- a/src/jogl/classes/javax/media/opengl/fixedfunc/GLPointerFuncUtil.java +++ b/src/jogl/classes/javax/media/opengl/fixedfunc/GLPointerFuncUtil.java @@ -28,7 +28,7 @@ package javax.media.opengl.fixedfunc; -public class GLPointerFuncUtil { +public class GLPointerFuncUtil { public static final String mgl_Vertex = "mgl_Vertex"; public static final String mgl_Normal = "mgl_Normal"; public static final String mgl_Color = "mgl_Color"; @@ -37,9 +37,18 @@ public class GLPointerFuncUtil { /** * @param glArrayIndex the fixed function array index - * @return default fixed function array name + * @return default fixed function array name */ public static String getPredefinedArrayIndexName(int glArrayIndex) { + return getPredefinedArrayIndexName(glArrayIndex, -1); + } + + /** + * @param glArrayIndex the fixed function array index + * @param multiTexCoordIndex index for multiTexCoordIndex + * @return default fixed function array name + */ + public static String getPredefinedArrayIndexName(int glArrayIndex, int multiTexCoordIndex) { switch(glArrayIndex) { case GLPointerFunc.GL_VERTEX_ARRAY: return mgl_Vertex; @@ -48,7 +57,11 @@ public class GLPointerFuncUtil { case GLPointerFunc.GL_COLOR_ARRAY: return mgl_Color; case GLPointerFunc.GL_TEXTURE_COORD_ARRAY: - return mgl_MultiTexCoord; + if(0<=multiTexCoordIndex) { + return mgl_MultiTexCoord+multiTexCoordIndex; + } else { + return mgl_MultiTexCoord+multiTexCoordIndex; + } } return null; } diff --git a/src/jogl/classes/javax/media/opengl/glu/GLUtessellatorCallbackAdapter.java b/src/jogl/classes/javax/media/opengl/glu/GLUtessellatorCallbackAdapter.java index bd12dfb9d..15a7bb2a1 100644 --- a/src/jogl/classes/javax/media/opengl/glu/GLUtessellatorCallbackAdapter.java +++ b/src/jogl/classes/javax/media/opengl/glu/GLUtessellatorCallbackAdapter.java @@ -64,20 +64,32 @@ package javax.media.opengl.glu; */ public class GLUtessellatorCallbackAdapter implements GLUtessellatorCallback { + @Override public void begin(int type) {} + @Override public void edgeFlag(boolean boundaryEdge) {} + @Override public void vertex(Object vertexData) {} + @Override public void end() {} // public void mesh(jogamp.opengl.tessellator.GLUmesh mesh) {} + @Override public void error(int errnum) {} + @Override public void combine(double[] coords, Object[] data, float[] weight, Object[] outData) {} + @Override public void beginData(int type, Object polygonData) {} + @Override public void edgeFlagData(boolean boundaryEdge, Object polygonData) {} + @Override public void vertexData(Object vertexData, Object polygonData) {} + @Override public void endData(Object polygonData) {} + @Override public void errorData(int errnum, Object polygonData) {} + @Override public void combineData(double[] coords, Object[] data, float[] weight, Object[] outData, Object polygonData) {} |