diff options
Diffstat (limited to 'src/jogl/classes/com/jogamp/opengl')
49 files changed, 15774 insertions, 0 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/DebugGL2.java b/src/jogl/classes/com/jogamp/opengl/DebugGL2.java new file mode 100644 index 000000000..dc0f0df50 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/DebugGL2.java @@ -0,0 +1,21 @@ +package com.jogamp.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(final GL2 downstream) { + super((GL4bc)downstream); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/DebugGL3.java b/src/jogl/classes/com/jogamp/opengl/DebugGL3.java new file mode 100644 index 000000000..6b27d207b --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/DebugGL3.java @@ -0,0 +1,21 @@ +package com.jogamp.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(final GL3 downstream) { + super((GL4bc)downstream); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/DebugGL3bc.java b/src/jogl/classes/com/jogamp/opengl/DebugGL3bc.java new file mode 100644 index 000000000..d92f6043f --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/DebugGL3bc.java @@ -0,0 +1,21 @@ +package com.jogamp.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(final GL3bc downstream) { + super((GL4bc)downstream); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/DebugGL4.java b/src/jogl/classes/com/jogamp/opengl/DebugGL4.java new file mode 100644 index 000000000..76f5a4ac3 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/DebugGL4.java @@ -0,0 +1,21 @@ +package com.jogamp.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(final GL4 downstream) { + super((GL4bc)downstream); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/DebugGLES2.java b/src/jogl/classes/com/jogamp/opengl/DebugGLES2.java new file mode 100644 index 000000000..e2b280515 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/DebugGLES2.java @@ -0,0 +1,21 @@ +package com.jogamp.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(final GLES2 downstream) { + super((GLES3)downstream); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/DefaultGLCapabilitiesChooser.java b/src/jogl/classes/com/jogamp/opengl/DefaultGLCapabilitiesChooser.java new file mode 100644 index 000000000..88a88087f --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/DefaultGLCapabilitiesChooser.java @@ -0,0 +1,304 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl; + +import com.jogamp.nativewindow.NativeWindowException; + +import java.util.List; + +import com.jogamp.nativewindow.CapabilitiesImmutable; + +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.util.PropertyAccess; + +import jogamp.opengl.Debug; + +/** <P> The default implementation of the {@link + GLCapabilitiesChooser} interface, which provides consistent visual + selection behavior across platforms. The precise algorithm is + deliberately left loosely specified. Some properties are: </P> + + <UL> + + <LI> As long as there is at least one available non-null + GLCapabilities which matches the "stereo" option, will return a + valid index. + + <LI> Attempts to match as closely as possible the given + GLCapabilities, but will select one with fewer capabilities (i.e., + lower color depth) if necessary. + + <LI> Prefers hardware-accelerated visuals to + non-hardware-accelerated. + + <LI> If there is no exact match, prefers a more-capable visual to + a less-capable one. + + <LI> If there is more than one exact match, chooses an arbitrary + one. + + <LI> May select the opposite of a double- or single-buffered + visual (based on the user's request) in dire situations. + + <LI> Color depth (including alpha) mismatches are weighted higher + than depth buffer mismatches, which are in turn weighted higher + than accumulation buffer (including alpha) and stencil buffer + depth mismatches. + + <LI> If a valid windowSystemRecommendedChoice parameter is + supplied, chooses that instead of using the cross-platform code. + + </UL> +*/ + +public class DefaultGLCapabilitiesChooser implements GLCapabilitiesChooser { + private static final boolean DEBUG; + + static { + Debug.initSingleton(); + DEBUG = PropertyAccess.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) + 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<? extends CapabilitiesImmutable> available, + final int windowSystemRecommendedChoice) { + if ( null == desired ) { + throw new NativeWindowException("Null desired capabilities"); + } + if ( 0 == available.size() ) { + throw new NativeWindowException("Empty available capabilities"); + } + + final GLCapabilitiesImmutable gldes = (GLCapabilitiesImmutable) desired; + final int availnum = available.size(); + + if (DEBUG) { + ExceptionUtils.dumpStack(System.err); + System.err.println("Desired: " + gldes); + System.err.println("Available: " + availnum); + for (int i = 0; i < available.size(); i++) { + System.err.println(i + ": " + available.get(i)); + } + System.err.println("Window system's recommended choice: " + windowSystemRecommendedChoice); + } + + if (windowSystemRecommendedChoice >= 0 && + windowSystemRecommendedChoice < availnum && + null != available.get(windowSystemRecommendedChoice)) { + if (DEBUG) { + System.err.println("Choosing window system's recommended choice of " + windowSystemRecommendedChoice); + System.err.println(available.get(windowSystemRecommendedChoice)); + } + return windowSystemRecommendedChoice; + } + + // Create score array + final int[] scores = new int[availnum]; + + for (int i = 0; i < scores.length; i++) { + scores[i] = NO_SCORE; + } + final int gldes_samples = gldes.getNumSamples(); + + // Compute score for each + for (int i = 0; i < availnum; i++) { + final GLCapabilitiesImmutable cur = (GLCapabilitiesImmutable) available.get(i); + if (cur == null) { + continue; + } + if (gldes.isOnscreen() && !cur.isOnscreen()) { + continue; // requested onscreen, but n/a + } + 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.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) * + Math.abs(cur.getDepthBits() - gldes.getDepthBits())); + // Compute difference in accumulation buffer depth + score += (ACCUM_MISMATCH_PENALTY_SCALE * sign(score) * + Math.abs((cur.getAccumRedBits() + cur.getAccumGreenBits() + cur.getAccumBlueBits() + cur.getAccumAlphaBits()) - + (gldes.getAccumRedBits() + gldes.getAccumGreenBits() + gldes.getAccumBlueBits() + gldes.getAccumAlphaBits()))); + // Compute difference in stencil bits + score += STENCIL_MISMATCH_PENALTY_SCALE * sign(score) * (cur.getStencilBits() - gldes.getStencilBits()); + // Compute difference in multisampling bits + score += MULTISAMPLE_MISMATCH_PENALTY_SCALE * sign(score) * (cur_samples - gldes_samples); + // double buffer + if (cur.getDoubleBuffered() != gldes.getDoubleBuffered()) { + score += sign(score) * DOUBLE_BUFFER_MISMATCH_PENALTY; + } + // opaque + if (cur.isBackgroundOpaque() != gldes.isBackgroundOpaque()) { + score += sign(score) * OPAQUE_MISMATCH_PENALTY; + } + if ((gldes.getStencilBits() > 0) && (cur.getStencilBits() == 0)) { + score += sign(score) * STENCIL_MISMATCH_PENALTY; + } + if (gldes_samples > 0) { + if (cur_samples == 0) { + score += sign(score) * MULTISAMPLE_MISMATCH_PENALTY; + } + if (!gldes.getSampleExtension().equals(cur.getSampleExtension())) { + score += sign(score) * MULTISAMPLE_EXTENSION_MISMATCH_PENALTY; + } + } + scores[i] = score; + } + // Now prefer hardware-accelerated visuals by pushing scores of + // non-hardware-accelerated visuals out + boolean gotHW = false; + int maxAbsoluteHWScore = 0; + for (int i = 0; i < availnum; i++) { + final int score = scores[i]; + if (score == NO_SCORE) { + continue; + } + final GLCapabilitiesImmutable cur = (GLCapabilitiesImmutable) available.get(i); + if (cur.getHardwareAccelerated()) { + final int absScore = Math.abs(score); + if (!gotHW || + (absScore > maxAbsoluteHWScore)) { + gotHW = true; + maxAbsoluteHWScore = absScore; + } + } + } + if (gotHW) { + for (int i = 0; i < availnum; i++) { + int score = scores[i]; + if (score == NO_SCORE) { + continue; + } + final GLCapabilitiesImmutable cur = (GLCapabilitiesImmutable) available.get(i); + if (!cur.getHardwareAccelerated()) { + if (score <= 0) { + score -= maxAbsoluteHWScore; + } else if (score > 0) { + score += maxAbsoluteHWScore; + } + scores[i] = score; + } + } + } + + if (DEBUG) { + System.err.print("Scores: ["); + for (int i = 0; i < availnum; i++) { + if (i > 0) { + System.err.print(","); + } + System.err.print(" " + i +": " + scores[i]); + } + System.err.println(" ]"); + } + + // Ready to select. Choose score closest to 0. + int scoreClosestToZero = NO_SCORE; + int chosenIndex = -1; + for (int i = 0; i < availnum; i++) { + final int score = scores[i]; + if (score == NO_SCORE) { + continue; + } + // Don't substitute a positive score for a smaller negative score + if ((scoreClosestToZero == NO_SCORE) || + (Math.abs(score) < Math.abs(scoreClosestToZero) && + ((sign(scoreClosestToZero) < 0) || (sign(score) > 0)))) { + scoreClosestToZero = score; + chosenIndex = i; + } + } + if (chosenIndex < 0) { + throw new NativeWindowException("Unable to select one of the provided GLCapabilities"); + } + if (DEBUG) { + System.err.println("Chosen index: " + chosenIndex); + System.err.println("Chosen capabilities:"); + System.err.println(available.get(chosenIndex)); + } + + return chosenIndex; + } + + private static int sign(final int score) { + if (score < 0) { + return -1; + } + return 1; + } + +} diff --git a/src/jogl/classes/com/jogamp/opengl/FPSCounter.java b/src/jogl/classes/com/jogamp/opengl/FPSCounter.java new file mode 100644 index 000000000..a3b7ccb70 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/FPSCounter.java @@ -0,0 +1,117 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl; + +import java.io.PrintStream; + +/** + * FPSCounter feature.<br> + * An implementation initially has the FPSCounter feature disabled.<br> + * Use {@link #setUpdateFPSFrames(int, PrintStream)} to enable and disable the FPSCounter feature. + */ +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. + * 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 + */ + 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> + * This value is reset via {@link #resetFPSCounter()}. + * + * @see #setUpdateFPSFrames(int, PrintStream) + * @see #resetFPSCounter() + */ + long getFPSStartTime(); + + /** + * Returns the time of the last update interval in milliseconds, if this feature is enabled via {@link #setUpdateFPSFrames(int, PrintStream)}.<br> + * This value is reset via {@link #resetFPSCounter()}. + * + * @see #setUpdateFPSFrames(int, PrintStream) + * @see #resetFPSCounter() + */ + long getLastFPSUpdateTime(); + + /** + * @return Duration of the last update interval in milliseconds. + * + * @see #setUpdateFPSFrames(int, PrintStream) + * @see #resetFPSCounter() + */ + long getLastFPSPeriod(); + + /** + * @return Last update interval's frames per seconds, {@link #getUpdateFPSFrames()} / {@link #getLastFPSPeriod()} + * + * @see #setUpdateFPSFrames(int, PrintStream) + * @see #resetFPSCounter() + */ + float getLastFPS(); + + /** + * @return Number of frame rendered since {@link #getFPSStartTime()} up to {@link #getLastFPSUpdateTime()} + * + * @see #setUpdateFPSFrames(int, PrintStream) + * @see #resetFPSCounter() + */ + int getTotalFPSFrames(); + + /** + * @return Total duration in milliseconds, {@link #getLastFPSUpdateTime()} - {@link #getFPSStartTime()} + * + * @see #setUpdateFPSFrames(int, PrintStream) + * @see #resetFPSCounter() + */ + long getTotalFPSDuration(); + + + /** + * @return Total frames per seconds, {@link #getTotalFPSFrames()} / {@link #getTotalFPSDuration()} + * + * @see #setUpdateFPSFrames(int, PrintStream) + * @see #resetFPSCounter() + */ + float getTotalFPS(); +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLAnimatorControl.java b/src/jogl/classes/com/jogamp/opengl/GLAnimatorControl.java new file mode 100644 index 000000000..549efd569 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLAnimatorControl.java @@ -0,0 +1,236 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl; + +/** + * An animator control interface, + * which implementation may drive a {@link com.jogamp.opengl.GLAutoDrawable} animation. + */ +public interface GLAnimatorControl extends FPSCounter { + /** + * A {@link GLAnimatorControl#setUncaughtExceptionHandler(UncaughtExceptionHandler) registered} + * {@link UncaughtExceptionHandler} instance is invoked when an {@link GLAnimatorControl animator} abruptly {@link #stop() stops} + * due to an uncaught exception from one of its {@link GLAutoDrawable}s. + * @see #uncaughtException(GLAnimatorControl, GLAutoDrawable, Throwable) + * @see GLAnimatorControl#setUncaughtExceptionHandler(UncaughtExceptionHandler) + * @since 2.2 + */ + public static interface UncaughtExceptionHandler { + /** + * Method invoked when the given {@link GLAnimatorControl} is {@link GLAnimatorControl#stop() stopped} due to the + * given uncaught exception happened on the given {@link GLAutoDrawable}. + * <p> + * The animator thread can still be retrieved via {@link GLAnimatorControl#getThread()}. + * </p> + * <p> + * All {@link GLAnimatorControl} states already reflect its stopped state. + * </p> + * <p> + * After this handler method is called, the {@link GLAnimatorControl} is stopped. + * </p> + * <p> + * Any exception thrown by this method will be ignored. + * </p> + * @param animator the {@link GLAnimatorControl} + * @param drawable the causing {@link GLAutoDrawable} + * @param cause the uncaught exception + * @see GLAnimatorControl#setUncaughtExceptionHandler(UncaughtExceptionHandler) + * @since 2.2 + */ + void uncaughtException(final GLAnimatorControl animator, final GLAutoDrawable drawable, final Throwable cause); + } + + /** + * Indicates whether this animator has been {@link #start() started}. + * + * @see #start() + * @see #stop() + * @see #isPaused() + * @see #pause() + * @see #resume() + */ + boolean isStarted(); + + /** + * Indicates whether this animator {@link #isStarted() is started} and {@link #isPaused() is not paused}. + * + * @see #start() + * @see #stop() + * @see #pause() + * @see #resume() + */ + boolean isAnimating(); + + /** + * 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() + * @see #pause() + * @see #resume() + */ + boolean isPaused(); + + /** + * @return The animation thread if running, otherwise null. + * + * @see #start() + * @see #stop() + */ + Thread getThread(); + + /** + * Starts this animator, if not running. + * <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> + * 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> + * 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. + * + * @see #start() + * @see #isAnimating() + * @see #getThread() + */ + boolean stop(); + + /** + * Pauses this animator. + * <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 not started, already paused or failed to pause, otherwise true + * + * @see #resume() + * @see #isAnimating() + */ + boolean pause(); + + /** + * Resumes animation if paused. + * <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> + * If resumed, all counters (time, frames, ..) are reset to zero. + * </p> + * + * @return false if not started, not paused or unable to resume, otherwise true + * + * @see #pause() + * @see #isAnimating() + */ + boolean resume(); + + /** + * 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. + * </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); + + /** + * Returns the {@link UncaughtExceptionHandler} invoked when this {@link GLAnimatorControl animator} abruptly {@link #stop() stops} + * due to an uncaught exception from one of its {@link GLAutoDrawable}s. + * <p> + * Default is <code>null</code>. + * </p> + * @since 2.2 + */ + UncaughtExceptionHandler getUncaughtExceptionHandler(); + + /** + * Set the handler invoked when this {@link GLAnimatorControl animator} abruptly {@link #stop() stops} + * due to an uncaught exception from one of its {@link GLAutoDrawable}s. + * @param handler the {@link UncaughtExceptionHandler} to use as this {@link GLAnimatorControl animator}'s uncaught exception + * handler. Pass <code>null</code> to unset the handler. + * @see UncaughtExceptionHandler#uncaughtException(GLAnimatorControl, GLAutoDrawable, Throwable) + * @since 2.2 + */ + void setUncaughtExceptionHandler(final UncaughtExceptionHandler handler); +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLArrayData.java b/src/jogl/classes/com/jogamp/opengl/GLArrayData.java new file mode 100644 index 000000000..ea2dfb0f3 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLArrayData.java @@ -0,0 +1,210 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl; + +import java.nio.Buffer; + +import com.jogamp.opengl.fixedfunc.GLPointerFunc; + +/** + * + * The total number of bytes hold by the referenced buffer is: + * getComponentSize()* getComponentNumber() * getElementNumber() + * + */ +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 + */ + public boolean isVertexAttribute(); + + /** + * The index of the predefined array index, see list below, + * or -1 in case of a shader attribute 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(); + + /** + * The name of the reflecting shader array attribute. + */ + public String getName(); + + /** + * 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); + + + /** + * Returns the shader attribute location for this name, + * -1 if not yet determined + */ + public int getLocation(); + + /** + * Sets the given location of the shader attribute + * + * @return the given location + * @see com.jogamp.opengl.util.glsl.ShaderState#vertexAttribPointer(GL2ES2, GLArrayData) + */ + 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, + * or a client side array (false). + */ + public boolean isVBO(); + + /** + * The VBO buffer offset or 0 if not a VBO + */ + public long getVBOOffset(); + + /** + * The VBO name or 0 if not a VBO + */ + public int getVBOName(); + + /** + * The VBO usage or 0 if not a VBO + * @return 0 if not a GPU buffer, otherwise {@link GL2ES2#GL_STREAM_DRAW}, {@link GL#GL_STATIC_DRAW} or {@link GL#GL_DYNAMIC_DRAW} + */ + public int getVBOUsage(); + + /** + * The VBO target or 0 if not a VBO + * @return 0 if not a GPU buffer, otherwise {@link GL#GL_ARRAY_BUFFER} or {@link GL#GL_ELEMENT_ARRAY_BUFFER} + */ + public int getVBOTarget(); + + + /** + * The Buffer holding the data, may be null if a GPU buffer without client bound data + */ + public Buffer getBuffer(); + + /** + * The number of components per element + */ + public int getComponentCount(); + + /** + * The component's GL data type, ie. GL_FLOAT + */ + public int getComponentType(); + + /** + * The component's size in bytes + */ + public int getComponentSizeInBytes(); + + /** + * The current number of used elements. + * <p> + * On element consist out of {@link #getComponentCount()} components. + * </p> + * 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. + */ + public int getSizeInBytes(); + + /** + * 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/com/jogamp/opengl/GLAutoDrawable.java b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawable.java new file mode 100644 index 000000000..385acf082 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawable.java @@ -0,0 +1,636 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl; + +import java.util.List; + +import com.jogamp.nativewindow.NativeSurface; + +import com.jogamp.common.util.locks.RecursiveLock; + +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. + <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, + which is only possible <i>after</i> the attached {@link com.jogamp.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 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> + 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 com.jogamp.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 com.jogamp.opengl.awt.GLCanvas}'s + and NEWT's <code>AWTCanvas</code> {@link com.jogamp.opengl.awt.GLCanvas#getGraphicsConfiguration getGraphicsConfiguration()} + specialization. Another demonstration is NEWT's {@link com.jogamp.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> + <li> Dispose all OpenGL resources by calling {@link GLEventListener#dispose dispose(..)} for all + registered {@link GLEventListener}s.</li> + <li> Destroy the {@link GLContext}.</li> + <li> Notify {@link GLDrawable} of the invalid state by calling {@link GLDrawable#setRealized setRealized(false)}.</li> + </ul> + <li> Controlled regeneration:</li> + <ul> + <li> Create the new {@link GLDrawable} with the requested {@link GLCapabilities} + <li> Notify {@link GLDrawable} to revalidate 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> + <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> + </ul> + 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> + 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 {@link GLDrawable} reconfiguration (the default) + -Djogl.screenchange.action=true Enable the {@link GLDrawable} reconfiguration + </PRE> + </p> + <h5><a name="locking">GLAutoDrawable Locking</a></h5> + GLAutoDrawable implementations perform locking in the following order: + <ol> + <li> {@link #getUpstreamLock()}.{@link RecursiveLock#lock() lock()}</li> + <li> {@link #getNativeSurface()}.{@link NativeSurface#lockSurface() lockSurface()} </li> + </ol> + and releases the locks accordingly: + <ol> + <li> {@link #getNativeSurface()}.{@link NativeSurface#unlockSurface() unlockSurface()} </li> + <li> {@link #getUpstreamLock()}.{@link RecursiveLock#unlock() unlock()}</li> + </ol> + Above <i>locking order</i> is mandatory to guarantee + atomicity of operation and to avoid race-conditions. + A custom implementation or user applications requiring exclusive access + shall follow the <i>locking order</i>. + See: + <ul> + <li>{@link #getUpstreamLock()}</li> + <li>{@link #invoke(boolean, GLRunnable)}</li> + <li>{@link #invoke(boolean, List)}</li> + </ul> + </p> + */ +public interface GLAutoDrawable extends GLDrawable { + /** Flag reflecting whether the {@link GLDrawable} reconfiguration will be issued in + * 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. + */ + public GLContext getContext(); + + /** + * Associate the new context, <code>newtCtx</code>, to this auto-drawable. + * <p> + * Remarks: + * <ul> + * <li>The currently associated context will be destroyed if <code>destroyPrevCtx</code> is <code>true</code>, + * otherwise it will be disassociated from this auto-drawable + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) setGLDrawable(null, true);} including {@link GL#glFinish() glFinish()}.</li> + * <li>The new context will be associated with this auto-drawable + * via {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}.</li> + * <li>If the old context was current on this thread, it is being released after disassociating this auto-drawable.</li> + * <li>If the new context was current on this thread, it is being released before associating this auto-drawable + * and made current afterwards.</li> + * <li>Implementation may issue {@link #makeCurrent()} and {@link #release()} while drawable reassociation.</li> + * <li>The user shall take extra care of thread synchronization, + * i.e. lock the involved {@link GLAutoDrawable auto-drawable's} + * {@link GLAutoDrawable#getUpstreamLock() upstream-locks} and {@link GLAutoDrawable#getNativeSurface() surfaces} + * to avoid a race condition. See <a href="#locking">GLAutoDrawable Locking</a>.</li> + * </ul> + * </p> + * + * @param newCtx the new context, maybe <code>null</code> for dis-association. + * @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 GLContext setContext(GLContext newCtx, boolean destroyPrevCtx); + + /** + * 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 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; + + /** + * Returns the number of {@link GLEventListener} of this drawable queue. + * @return The number of GLEventListener objects of this drawable queue. + */ + public int getGLEventListenerCount(); + + /** + * Returns true if all added {@link GLEventListener} are initialized, otherwise false. + * @since 2.2 + */ + boolean areAllGLEventListenerInitialized(); + + /** + * 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; + + /** + * 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 com.jogamp.opengl.GLAnimatorControl} implementation. + * 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(com.jogamp.opengl.GLAutoDrawable)}, passing it's control implementation,<br> + * and {@link com.jogamp.opengl.util.Animator#remove(com.jogamp.opengl.GLAutoDrawable)}, passing <code>null</code>. + * </p> + * <p> + * Impacts {@link #display()} and {@link #invoke(boolean, GLRunnable)} semantics.</p><br> + * + * @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 com.jogamp.opengl.GLAnimatorControl + */ + public abstract void setAnimator(GLAnimatorControl animatorControl) throws GLException; + + /** + * @return the registered {@link com.jogamp.opengl.GLAnimatorControl} implementation, using this <code>GLAutoDrawable</code>. + * + * @see #setAnimator(com.jogamp.opengl.GLAnimatorControl) + * @see com.jogamp.opengl.GLAnimatorControl + */ + 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 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>, + * hence the {@link GLRunnable} will be executed right away.<br/> + * </p> + * <p> + * 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> + * <p> + * To avoid a deadlock situation which causes an {@link IllegalStateException} one should + * avoid issuing {@link #invoke(boolean, GLRunnable) invoke} while this <a href="#locking">GLAutoDrawable is being locked</a>.<br> + * Detected deadlock situations throwing an {@link IllegalStateException} are: + * <ul> + * <li>{@link #getAnimator() Animator} is running on another thread and waiting and is locked on current thread, but is not {@link #isThreadGLCapable() GL-Thread}</li> + * <li>No {@link #getAnimator() Animator} is running on another thread and is locked on current thread, but is not {@link #isThreadGLCapable() GL-Thread}</li> + * </ul> + * </p> + * + * @param wait if <code>true</code> block until execution of <code>glRunnable</code> is finished, otherwise return immediately w/o waiting + * @param glRunnable the {@link GLRunnable} to execute within {@link #display()} + * @return <code>true</code> if the {@link GLRunnable} has been processed or queued, otherwise <code>false</code>. + * @throws IllegalStateException in case of a detected deadlock situation ahead, see above. + * + * @see #setAnimator(GLAnimatorControl) + * @see #display() + * @see GLRunnable + * @see #invoke(boolean, List) + * @see #flushGLRunnables() + */ + public boolean invoke(boolean wait, GLRunnable glRunnable) throws IllegalStateException ; + + /** + * 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>. + * @throws IllegalStateException in case of a detected deadlock situation ahead, see {@link #invoke(boolean, GLRunnable)}. + * @see #invoke(boolean, GLRunnable) + * @see #flushGLRunnables() + */ + public boolean invoke(boolean wait, List<GLRunnable> glRunnables) throws IllegalStateException; + + /** + * Flushes all {@link #invoke(boolean, GLRunnable) enqueued} {@link GLRunnable} of this {@link GLAutoDrawable} + * including notifying waiting executor. + * <p> + * The executor which might have been blocked until notified + * will be unblocked and all tasks removed from the queue. + * </p> + * @see #invoke(boolean, GLRunnable) + * @since 2.2 + */ + public void flushGLRunnables(); + + /** Destroys all resources associated with this GLAutoDrawable, + inclusive the GLContext. + If a window is attached to it's implementation, it shall be closed. + Causes disposing of all OpenGL resources + by calling {@link GLEventListener#dispose dispose(..)} for all + registered {@link GLEventListener}s. Called automatically by the + window system toolkit upon receiving a destroy notification. This + routine may be called manually. */ + public void destroy(); + + /** + * <p> + * Causes OpenGL rendering to be performed for this GLAutoDrawable + * in the following order: + * <ul> + * <li> Calling {@link GLEventListener#display display(..)} for all + * registered {@link GLEventListener}s. </li> + * <li> Executes all one-shot {@link com.jogamp.opengl.GLRunnable GLRunnable}, + * enqueued via {@link #invoke(boolean, GLRunnable)}.</li> + * </ul></p> + * <p> + * May be called periodically by a running {@link com.jogamp.opengl.GLAnimatorControl} implementation,<br> + * which must register itself with {@link #setAnimator(com.jogamp.opengl.GLAnimatorControl)}.</p> + * <p> + * Called automatically by the window system toolkit upon receiving a repaint() request, <br> + * except an {@link com.jogamp.opengl.GLAnimatorControl} implementation {@link com.jogamp.opengl.GLAnimatorControl#isAnimating()}.</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, + * the implementation shall call {@link GLEventListener#init init(..)} for all + * registered {@link GLEventListener}s <i>before</i> making the + * actual {@link GLEventListener#display display(..)} calls, + * in case this has not been done yet.</p> + * + * @see #setAnimator(com.jogamp.opengl.GLAnimatorControl) + */ + public void display(); + + /** Enables or disables automatic buffer swapping for this drawable. + By default this property is set to true; when true, after all + GLEventListeners have been called for a display() event, the + front and back buffers are swapped, displaying the results of + the render. When disabled, the user is responsible for calling + {@link #swapBuffers(..)} manually. */ + public void setAutoSwapBufferMode(boolean enable); + + /** Indicates whether automatic buffer swapping is enabled for this + drawable. See {@link #setAutoSwapBufferMode}. */ + public boolean getAutoSwapBufferMode(); + + /** + * @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 + return null. Users should not rely on the identity of the + returned GL object; for example, users should not maintain a + hash table with the GL object as the key. Additionally, the GL + object should not be cached in client code, but should be + re-fetched from the GLAutoDrawable at the beginning of each call + to init, display, etc. */ + public GL getGL(); + + /** Sets the {@link GL} pipeline object this GLAutoDrawable uses. + This should only be called from within the GLEventListener's + callback methods, and usually only from within the init() + method, in order to install a composable pipeline. See the JOGL + demos for examples. + @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 com.jogamp.opengl.awt.GLCanvas}</td> <td>is a</td> <td>{@link java.awt.Canvas}</td</tr> + * <tr><td>AWT</td> <td>{@link com.jogamp.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(); + + /** + * Returns the recursive lock object of the {@link #getUpstreamWidget() upstream widget} + * to synchronize multithreaded access on top of {@link NativeSurface#lockSurface()}. + * <p> + * See <a href="#locking">GLAutoDrawable Locking</a>. + * </p> + * @since 2.2 + */ + public RecursiveLock getUpstreamLock(); + + /** + * Indicates whether the current thread is capable of + * performing OpenGL-related work. + * <p> + * Implementation utilizes this knowledge to determine + * whether {@link #display()} performs the OpenGL commands on the current thread directly + * or spawns them on the dedicated OpenGL thread. + * </p> + * @since 2.2 + */ + public boolean isThreadGLCapable(); + +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLBase.java b/src/jogl/classes/com/jogamp/opengl/GLBase.java new file mode 100644 index 000000000..19b7808fc --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLBase.java @@ -0,0 +1,646 @@ +/** + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl; + +/** + * <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 + * availability queries as described below.</P> + * + * <P> While the APIs for vendor extensions are unconditionally + * exposed, the underlying functions may not be present. The method + * {@link #isFunctionAvailable} should be used to query the + * availability of any non-core function before it is used for the + * first time; for example, + * <code>gl.isFunctionAvailable("glProgramStringARB")</code>. On + * certain platforms (Windows in particular), the most "core" + * functionality is only OpenGL 1.1, so in theory any routines first + * exposed in OpenGL 1.2, 1.3, and 1.4, 1.5, or 2.0 as well as vendor + * extensions should all be queried. Calling an unavailable function + * will cause a {@link GLException} to be raised. </P> + * + * {@link #isExtensionAvailable} may also be used to determine whether + * a specific extension is available before calling the routines or + * using the functionality it exposes: for example, + * <code>gl.isExtensionAvailable("GL_ARB_vertex_program");</code>. + * However, in this case it is up to the end user to know which + * routines or functionality are associated with which OpenGL + * extensions. It may also be used to test for the availability of a + * particular version of OpenGL: for example, + * <code>gl.isExtensionAvailable("GL_VERSION_1_5");</code>. + * + * <P> Exceptions to the window system extension naming rules: + * + * <UL> + * + * <LI> The memory allocators for the NVidia vertex_array_range (VAR) + * extension, in particular <code>wglAllocateMemoryNV</code> / + * <code>glXAllocateMemoryNV</code> and associated routines. {@link + * #glAllocateMemoryNV} has been provided for window system-independent + * access to VAR. {@link #isFunctionAvailable} will translate an argument + * of "glAllocateMemoryNV" or "glFreeMemoryNV" into the appropriate + * window system-specific name. </P> + * + * <LI> WGL_ARB_pbuffer, WGL_ARB_pixel_format, and other + * platform-specific pbuffer functionality; the availability of + * pbuffers can be queried on Windows, X11 and Mac OS X platforms by + * querying {@link #isExtensionAvailable} with an argument of + * "GL_ARB_pbuffer" or "GL_ARB_pixel_format". + * + * </UL> <P> + * + */ +public interface GLBase { + + /** + * Indicates whether this GL object conforms to any of the OpenGL profiles. + */ + public boolean isGL(); + + /** + * 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 ES ≥ 1.0 profile. + * @see GLContext#isGLES1() + */ + public boolean isGLES1(); + + /** + * Indicates whether this GL object conforms to the OpenGL ES ≥ 2.0 profile. + * <p> + * Remark: ES2 compatible desktop profiles are not included. + * To query whether core ES2 functionality is provided, use {@link #isGLES2Compatible()}. + * </p> + * @see #isGLES2Compatible() + * @see GLContext#isGLES2() + */ + public boolean isGLES2(); + + /** + * Indicates whether this GL object conforms to the OpenGL ES ≥ 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()}, {@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 conforms to a either a GL2GL3 or GL3ES3 compatible profile. + * @see GLContext#isGL2ES3() + */ + 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 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 object is not a GL implementation + */ + public GL getGL() throws GLException; + + /** + * Casts this object to the GL4bc interface. + * @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 object is not a GL4 implementation + */ + public GL4 getGL4() throws GLException; + + /** + * Casts this object to the GL3bc interface. + * @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 object is not a GL3 implementation + */ + public GL3 getGL3() throws GLException; + + /** + * Casts this object to the GL2 interface. + * @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 object is not a GLES1 implementation + */ + public GLES1 getGLES1() throws GLException; + + /** + * Casts this object to the GLES2 interface. + * @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 object is not a GL2ES1 implementation + */ + public GL2ES1 getGL2ES1() throws GLException; + + /** + * Casts this object to the GL2ES2 interface. + * @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 object is not a GL2GL3 implementation + */ + public GL2GL3 getGL2GL3() throws GLException; + + /** + * Returns the GLProfile associated with this GL object. + */ + public GLProfile getGLProfile(); + + /** + * Returns the GLContext associated which this GL object. + */ + public GLContext getContext(); + + /** + * 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> + * + * 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, + * 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 + * both the host and display, or it is an OpenGL extension function that both + * the host and display support. <P> + * + * 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). + */ + public boolean isFunctionAvailable(String glFunctionName); + + /** + * 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> + * + * @param glExtensionName the name of the OpenGL extension (e.g., + * "GL_ARB_vertex_program"). + */ + 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> + */ + 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 + causes the application to wait until the next vertical refresh + until swapping buffers. The default, which is platform-specific, + 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 + */ + public void setSwapInterval(int interval); + + /** 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 + is being used. + + @see #setSwapInterval + */ + public int getSwapInterval(); + + /** + * Returns an object through which platform-specific OpenGL extensions + * (EGL, GLX, WGL, etc.) may be accessed. The data type of the returned + * object and its associated capabilities are undefined. Most + * applications will never need to call this method. It is highly + * recommended that any applications which do call this method perform + * all accesses on the returned object reflectively to guard + * themselves against changes to the implementation. + */ + public Object getPlatformGLExtensions(); + + /** + * Returns an object providing access to the specified OpenGL + * extension. This is intended to provide a mechanism for vendors who + * wish to provide access to new OpenGL extensions without changing + * the public API of the core package. For example, a user may request + * access to extension "GL_VENDOR_foo" and receive back an object + * which implements a vendor-specified interface which can call the + * OpenGL extension functions corresponding to that extension. It is + * up to the vendor to specify both the extension name and Java API + * for accessing it, including which class or interface contains the + * functions. + * + * <P> + * + * Note: it is the intent to add new extensions as quickly as possible + * to the core GL API. Therefore it is unlikely that most vendors will + * use this extension mechanism, but it is being provided for + * 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); + + /** + * @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); + + /** + * @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; + + /** + * @return true if a VBO is bound to {@link GL#GL_ARRAY_BUFFER} via {@link GL#glBindBuffer(int, int)}, otherwise false + */ + public boolean isVBOArrayBound(); + + /** + * @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)}. + * <p> + * Calls {@link GLContext#getBoundFramebuffer(int)}. + * </p> + */ + 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> + * <p> + * Calls {@link GLContext#getDefaultDrawFramebuffer()}. + * </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> + * <p> + * Calls {@link GLContext#getDefaultReadFramebuffer()}. + * </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> + * <p> + * Calls {@link GLContext#getDefaultReadBuffer()}. + * </p> + */ + public int getDefaultReadBuffer(); +} + diff --git a/src/jogl/classes/com/jogamp/opengl/GLBufferStorage.java b/src/jogl/classes/com/jogamp/opengl/GLBufferStorage.java new file mode 100644 index 000000000..5db97d42f --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLBufferStorage.java @@ -0,0 +1,160 @@ +/** + * Copyright 2014 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl; + +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; + private 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; + } + + protected void reset(final long size, final int mutableUsage, final int immutableFlags) { + this.size = size; + this.mutableUsage = mutableUsage; + this.immutableFlags = immutableFlags; + this.mappedBuffer = null; + } + protected void setMappedBuffer(final ByteBuffer buffer) { + this.mappedBuffer = buffer; + } + + /** 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 static final String msgClazzName = "GLBufferStorage"; +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLCapabilities.java b/src/jogl/classes/com/jogamp/opengl/GLCapabilities.java new file mode 100644 index 000000000..02aa47119 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLCapabilities.java @@ -0,0 +1,512 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl; + +import com.jogamp.nativewindow.Capabilities; +import com.jogamp.nativewindow.CapabilitiesImmutable; + +/** Specifies a set of OpenGL capabilities.<br> + At creation time of a {@link GLDrawable} using {@link GLDrawableFactory}, + an instance of this class is passed, + describing the desired capabilities that a rendering context + 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#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 isPBuffer = false; + private boolean isFBO = false; + private boolean doubleBuffered = true; + private boolean stereo = false; + private boolean hardwareAccelerated = true; + private int depthBits = 16; + private int stencilBits = 0; + private int accumRedBits = 0; + private int accumGreenBits = 0; + private int accumBlueBits = 0; + private int accumAlphaBits = 0; + // Shift bits from PIXELFORMATDESCRIPTOR not present because they + // are unlikely to be supported on Windows anyway + + // Support for full-scene antialiasing (FSAA) + private String sampleExtension = DEFAULT_SAMPLE_EXTENSION; + private boolean sampleBuffers = false; + private int numSamples = 2; + + /** 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. + */ + public GLCapabilities(final GLProfile glp) throws GLException { + glProfile = (null!=glp)?glp:GLProfile.getDefault(GLProfile.getDefaultDevice()); + } + + @Override + public Object cloneMutable() { + return clone(); + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (final RuntimeException e) { + throw new GLException(e); + } + } + + /** + * Copies all {@link GLCapabilitiesImmutable} values + * from <code>source</code> into this instance. + * @return this instance + */ + public GLCapabilities copyFrom(final 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 = 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; + return hash; + } + + @Override + public boolean equals(final Object obj) { + if(this == obj) { return true; } + if(!(obj instanceof GLCapabilitiesImmutable)) { + return false; + } + final GLCapabilitiesImmutable other = (GLCapabilitiesImmutable)obj; + boolean res = super.equals(obj) && + other.getGLProfile()==glProfile && + other.isPBuffer()==isPBuffer && + other.isFBO()==isFBO && + other.getDoubleBuffered() == doubleBuffered && + other.getStereo()==stereo && + other.getHardwareAccelerated()==hardwareAccelerated && + other.getDepthBits()==depthBits && + other.getStencilBits()==stencilBits && + other.getAccumRedBits()==accumRedBits && + other.getAccumGreenBits()==accumGreenBits && + other.getAccumBlueBits()==accumBlueBits && + other.getAccumAlphaBits()==accumAlphaBits && + 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 */ + @Override + public int compareTo(final CapabilitiesImmutable o) { + if ( ! ( o instanceof GLCapabilitiesImmutable ) ) { + final Class<?> c = (null != o) ? o.getClass() : null ; + throw new ClassCastException("Not a GLCapabilitiesImmutable object, but " + c); + } + final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) o; + + if(hardwareAccelerated && !caps.getHardwareAccelerated()) { + return 1; + } else if(!hardwareAccelerated && caps.getHardwareAccelerated()) { + return -1; + } + + if(stereo && !caps.getStereo()) { + return 1; + } else if(!stereo && caps.getStereo()) { + return -1; + } + + if(doubleBuffered && !caps.getDoubleBuffered()) { + return 1; + } else if(!doubleBuffered && caps.getDoubleBuffered()) { + return -1; + } + + final int ms = getNumSamples(); + final int xms = caps.getNumSamples() ; + + if(ms > xms) { + return 1; + } else if( ms < xms ) { + return -1; + } + // ignore the sample extension + + if(stencilBits > caps.getStencilBits()) { + return 1; + } else if(stencilBits < caps.getStencilBits()) { + return -1; + } + + final int sc = super.compareTo(caps); // RGBA + if(0 != sc) { + return sc; + } + + if(depthBits > caps.getDepthBits()) { + return 1; + } else if(depthBits < caps.getDepthBits()) { + return -1; + } + + return 0; // they are equal: hw/sw, stereo, multisample, stencil, RGBA and depth + } + + @Override + public final GLProfile getGLProfile() { + return glProfile; + } + + /** Sets the GL profile you desire */ + public void setGLProfile(final GLProfile profile) { + glProfile=profile; + } + + @Override + public final boolean isPBuffer() { + return isPBuffer; + } + + /** + * 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(final boolean enable) { + if(enable) { + setOnscreen(false); + } + isPBuffer = enable; + } + + @Override + public final boolean isFBO() { + return isFBO; + } + + /** + * 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(final boolean enable) { + if(enable) { + setOnscreen(false); + } + isFBO = enable; + } + + @Override + public final boolean getDoubleBuffered() { + return doubleBuffered; + } + + /** Enables or disables double buffering. */ + public void setDoubleBuffered(final boolean enable) { + doubleBuffered = enable; + } + + @Override + public final boolean getStereo() { + return stereo; + } + + /** Enables or disables stereo viewing. */ + public void setStereo(final boolean enable) { + stereo = enable; + } + + @Override + public final boolean getHardwareAccelerated() { + return hardwareAccelerated; + } + + /** Enables or disables hardware acceleration. */ + public void setHardwareAccelerated(final boolean enable) { + hardwareAccelerated = enable; + } + + @Override + public final int getDepthBits() { + return depthBits; + } + + /** Sets the number of bits requested for the depth buffer. */ + public void setDepthBits(final int depthBits) { + this.depthBits = depthBits; + } + + @Override + public final int getStencilBits() { + return stencilBits; + } + + /** Sets the number of bits requested for the stencil buffer. */ + public void setStencilBits(final int stencilBits) { + this.stencilBits = stencilBits; + } + + @Override + public final int getAccumRedBits() { + return accumRedBits; + } + + /** Sets the number of bits requested 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. */ + public void setAccumRedBits(final int accumRedBits) { + this.accumRedBits = accumRedBits; + } + + @Override + public final int getAccumGreenBits() { + return accumGreenBits; + } + + /** Sets the number of bits requested 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. */ + public void setAccumGreenBits(final int accumGreenBits) { + this.accumGreenBits = accumGreenBits; + } + + @Override + public final int getAccumBlueBits() { + return accumBlueBits; + } + + /** Sets the number of bits requested 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. */ + public void setAccumBlueBits(final int accumBlueBits) { + this.accumBlueBits = accumBlueBits; + } + + @Override + public final int getAccumAlphaBits() { + return accumAlphaBits; + } + + /** Sets number of bits requested for 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. */ + public void setAccumAlphaBits(final int accumAlphaBits) { + this.accumAlphaBits = accumAlphaBits; + } + + /** + * Sets the desired extension for full-scene antialiasing + * (FSAA), default is {@link #DEFAULT_SAMPLE_EXTENSION}. + */ + public void setSampleExtension(final String se) { + sampleExtension = se; + } + + @Override + public final String getSampleExtension() { + return sampleExtension; + } + + /** + * Defaults to false.<br> + * Indicates whether sample buffers for full-scene antialiasing + * (FSAA) should be allocated for this drawable.<br> + * Mind that this requires the alpha component.<br> + * If enabled this method also invokes {@link #setAlphaBits(int) setAlphaBits(1)} + * if {@link #getAlphaBits()} == 0.<br> + */ + public void setSampleBuffers(final boolean enable) { + sampleBuffers = enable; + if(sampleBuffers && getAlphaBits()==0) { + setAlphaBits(1); + } + } + + @Override + public final boolean getSampleBuffers() { + return sampleBuffers; + } + + /** + * If sample buffers are enabled, indicates the number of buffers + * to be allocated. Defaults to 2. + * @see #getNumSamples() + */ + public void setNumSamples(final int numSamples) { + this.numSamples = numSamples; + } + + @Override + public final int getNumSamples() { + return sampleBuffers ? numSamples : 0; + } + + @Override + public StringBuilder toString(StringBuilder sink) { + if(null == sink) { + sink = new StringBuilder(); + } + + final int samples = sampleBuffers ? numSamples : 0 ; + + super.toString(sink, false); + + 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); + } + if(doubleBuffered) { + sink.append(", dbl"); + } else { + sink.append(", one"); + } + if(stereo) { + sink.append(", stereo"); + } else { + sink.append(", mono "); + } + if(hardwareAccelerated) { + sink.append(", hw, "); + } else { + sink.append(", sw, "); + } + sink.append(glProfile); + 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("auto-cfg"); // auto-config off-screen mode + } + } + sink.append("]"); + + return sink; + } + + /** Returns a textual representation of this GLCapabilities + object. */ + @Override + public String toString() { + final StringBuilder msg = new StringBuilder(); + msg.append("GLCaps["); + toString(msg); + msg.append("]"); + return msg.toString(); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLCapabilitiesChooser.java b/src/jogl/classes/com/jogamp/opengl/GLCapabilitiesChooser.java new file mode 100644 index 000000000..9911d1314 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLCapabilitiesChooser.java @@ -0,0 +1,62 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl; + +import java.util.List; + +import com.jogamp.nativewindow.CapabilitiesImmutable; +import com.jogamp.nativewindow.CapabilitiesChooser; + +import com.jogamp.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} 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/com/jogamp/opengl/GLCapabilitiesImmutable.java b/src/jogl/classes/com/jogamp/opengl/GLCapabilitiesImmutable.java new file mode 100644 index 000000000..25081a521 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLCapabilitiesImmutable.java @@ -0,0 +1,178 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl; + +import com.jogamp.nativewindow.CapabilitiesImmutable; + +/** + * Specifies an immutable set of OpenGL capabilities.<br> + * + * @see com.jogamp.opengl.GLCapabilities + * @see com.jogamp.nativewindow.CapabilitiesImmutable + */ +public interface GLCapabilitiesImmutable extends CapabilitiesImmutable { + /** + * 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 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. + */ + int getAccumAlphaBits(); + + /** + * 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. + */ + int getAccumBlueBits(); + + /** + * 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. + */ + int getAccumGreenBits(); + + /** + * 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. + */ + int getAccumRedBits(); + + /** + * Returns the number of depth buffer bits. + */ + int getDepthBits(); + + /** + * Returns whether double-buffering is requested, available or chosen. + * <p> + * Default is true. + * </p> + */ + boolean getDoubleBuffered(); + + /** + * Returns whether hardware acceleration is requested, available or chosen. + * <p> + * Default is true. + * </p> + */ + boolean getHardwareAccelerated(); + + /** + * 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. + * <p> + * Default is false. + * </p> + */ + boolean getSampleBuffers(); + + /** + * Returns the number of sample buffers to be allocated if sample + * buffers are enabled, otherwise returns 0. + * <p> + * Default is 0 due to disable sample buffers per default. + * </p> + */ + int getNumSamples(); + + /** + * Returns the number of stencil buffer bits. + * <p> + * Default is 0. + * </p> + */ + int getStencilBits(); + + /** + * Returns whether stereo is requested, available or chosen. + * <p> + * Default is false. + * </p> + */ + boolean getStereo(); + + /** + * 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); + + @Override + int hashCode(); + + @Override + String toString(); +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLContext.java b/src/jogl/classes/com/jogamp/opengl/GLContext.java new file mode 100644 index 000000000..6366c4e37 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLContext.java @@ -0,0 +1,2038 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl; + +import java.nio.IntBuffer; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import com.jogamp.nativewindow.AbstractGraphicsDevice; +import com.jogamp.nativewindow.NativeSurface; + +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 + thread. OpenGL rendering semantics specify that only one context + may be current on the current thread at any given time, and also + that a given context may be current on only one thread at any + given time. Because components can be added to and removed from + the component hierarchy at any time, it is possible that the + underlying OpenGL context may need to be destroyed and recreated + multiple times over the lifetime of a given component. This + process is handled by the implementation, and the GLContext + 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 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); + protected static final boolean FORCE_NO_COLOR_RENDERBUFFER = Debug.isPropertyDefined("jogl.fbo.force.nocolorrenderbuffer", true); + + /** Reflects property jogl.debug.DebugGL. If true, the debug pipeline is enabled at context creation. */ + public static final boolean DEBUG_GL = Debug.isPropertyDefined("jogl.debug.DebugGL", true); + /** Reflects property jogl.debug.TraceGL. If true, the trace pipeline is enabled at context creation. */ + 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}, 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}, value {@value}. */ + public static final int CONTEXT_CURRENT_NEW = 2; + + /** Version 1.00, i.e. GLSL 1.00 for ES 2.0. */ + public static final VersionNumber Version1_0 = new VersionNumber(1, 0, 0); + /** Version 1.10, i.e. GLSL 1.10 for GL 2.0. */ + public static final VersionNumber Version1_10 = new VersionNumber(1, 10, 0); + /** Version 1.20, i.e. GLSL 1.20 for GL 2.1. */ + public static final VersionNumber Version1_20 = new VersionNumber(1, 20, 0); + /** Version 1.30, i.e. GLSL 1.30 for GL 3.0. */ + public static final VersionNumber Version1_30 = new VersionNumber(1, 30, 0); + /** Version 1.40, i.e. GLSL 1.40 for GL 3.1. */ + public static final VersionNumber Version1_40 = new VersionNumber(1, 40, 0); + /** Version 1.50, i.e. GLSL 1.50 for GL 3.2. */ + public static final VersionNumber Version1_50 = new VersionNumber(1, 50, 0); + + /** Version 1.1, i.e. GL 1.1 */ + public static final VersionNumber Version1_1 = new VersionNumber(1, 1, 0); + + /** Version 1.2, i.e. GL 1.2 */ + public static final VersionNumber Version1_2 = new VersionNumber(1, 2, 0); + + /** Version 1.4, i.e. GL 1.4 */ + public static final VersionNumber Version1_4 = new VersionNumber(1, 4, 0); + + /** Version 1.5, i.e. GL 1.5 */ + public static final VersionNumber Version1_5 = new VersionNumber(1, 5, 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 Version3_0 = 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 Version3_1 = new VersionNumber(3, 1, 0); + + /** Version 3.2. As an OpenGL version, it qualifies for geometry shader */ + public static final VersionNumber Version3_2 = 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 Version4_3 = new VersionNumber(4, 3, 0); + + protected static final VersionNumber Version8_0 = new VersionNumber(8, 0, 0); + + private static final String S_EMPTY = ""; + + // + // 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: 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: 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. 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. See {@link #getAvailableContextProperties(AbstractGraphicsDevice, GLProfile)}. */ + protected static final int CTX_OPTION_FORWARD = 1 << 4; + /** <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; + /** 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] + // + + /** <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; + + /** <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; + + /** + * 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(); // FIXME: Move to GLContextImpl when incr. minor version (incompatible change) + + /** The underlying native OpenGL context */ + protected volatile long contextHandle; // volatile: avoid locking for read-only access + + protected GLContext() { + resetStates(true); + } + + 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; + + /** + * @param isInit true if called for class initialization, otherwise false (re-init or destruction). + */ + protected void resetStates(final boolean isInit) { + if (DEBUG) { + System.err.println(getThreadName() + ": GLContext.resetStates(isInit "+isInit+")"); + // Thread.dumpStack(); + } + ctxVersion = VersionNumberString.zeroVersion; + ctxVendorVersion = VersionNumberString.zeroVersion; + ctxOptions=0; + ctxVersionString=null; + 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 the shared master GLContext of this GLContext if shared, otherwise return <code>null</code>. + * <p> + * Returns this GLContext, if it is a shared master. + * </p> + * @since 2.2.1 + */ + public final GLContext getSharedMaster() { + return GLContextShareSet.getSharedMaster(this); + } + + /** Returns a new list of created GLContext shared with this GLContext. */ + public final List<GLContext> getCreatedShares() { + return GLContextShareSet.getCreatedShares(this); + } + + /** Returns a new list of destroyed GLContext shared with this GLContext. */ + public final List<GLContext> getDestroyedShares() { + return GLContextShareSet.getDestroyedShares(this); + } + + /** + * 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(final int quirk) throws IllegalArgumentException { + return null != glRendererQuirks ? glRendererQuirks.exist(quirk) : false ; + } + + /** + * Sets the read/write drawable for framebuffer operations, i.e. reassociation of the context's drawable. + * <p> + * If the arguments reflect the current state of this context + * this method is a no-operation and returns the old and current {@link GLDrawable}. + * </p> + * <p> + * Remarks: + * <ul> + * <li>{@link GL#glFinish() glFinish()} is issued if context {@link #isCreated()} and a {@link #getGLDrawable() previous drawable} was bound before disassociation.</li> + * <li>If the context was current on this thread, it is being released before drawable reassociation + * and made current afterwards.</li> + * <li>Implementation may issue {@link #makeCurrent()} and {@link #release()} while drawable reassociation.</li> + * <li>The user shall take extra care of thread synchronization, + * i.e. lock the involved {@link GLDrawable#getNativeSurface() drawable's} {@link NativeSurface}s + * to avoid a race condition. In case {@link GLAutoDrawable auto-drawable's} are used, + * their {@link GLAutoDrawable#getUpstreamLock() upstream-lock} must be locked beforehand + * see <a href="GLAutoDrawable.html#locking">GLAutoDrawable Locking</a>.</li> + * </ul> + * </p> + * @param readWrite The read/write drawable for framebuffer operations, maybe <code>null</code> to remove association. + * @param setWriteOnly Only change the write-drawable, if <code>setWriteOnly</code> is <code>true</code> and + * 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 if operation succeeds + * + * @throws GLException in case <code>null</code> is being passed, + * this context is made current on another thread + * or operation fails. + * + * @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> + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> + * @see #setGLDrawable(GLDrawable, boolean) + * @see #setGLReadDrawable(GLDrawable) + */ + public abstract GLDrawable getGLDrawable(); + + /** + * 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-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-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 or + * this context is made current on another thread. + * + * @see #isGLReadDrawableAvailable() + * @see #getGLReadDrawable() + */ + public abstract GLDrawable setGLReadDrawable(GLDrawable read); + + /** + * 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> + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> + * @see #isGLReadDrawableAvailable() + * @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. + * </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 {@link #CONTEXT_NOT_CURRENT} is returned. + * </p> + * <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()} + * </p> + * + * @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 the context could not be created + * or made current due to non-recoverable, system-specific errors. + */ + public abstract int makeCurrent() throws GLException; + + /** + * 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()}. + * </p> + * + * @throws GLException if the context had not previously been made + * current on the current thread + */ + public abstract void release() throws GLException; + + /** + * Copies selected groups of OpenGL state variables from the + * supplied source context into this one. The <code>mask</code> + * 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 + * 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> + * + * Not all values for GL state can be copied. For example, pixel + * pack and unpack state, render mode state, and select and feedback + * state are not copied. The state that can be copied is exactly the + * state that is manipulated by the GL command {@link + * GL2#glPushAttrib glPushAttrib}. <P> + * + * On most platforms, this context may not be current to any thread, + * including the calling thread, when this method is called. Some + * platforms have additional requirements such as whether this + * context or the source context must occasionally be made current + * in order for the results of the copy to be seen; these + * requirements are beyond the scope of this specification. + * + * @param source the source OpenGL context from which to copy state + * @param mask a mask of symbolic names indicating which groups of state to copy + + * @throws GLException if an OpenGL-related error occurred + */ + public abstract void copy(GLContext source, int mask) throws GLException; + + /** + * Returns the GL object bound to this thread current context. + * If no context is current, throw an GLException + * + * @return the current context's GL object on this thread + * @throws GLException if no context is current + */ + public static GL getCurrentGL() throws GLException { + final GLContext glc = getCurrent(); + if(null==glc) { + throw new GLException(getThreadName()+": No OpenGL context current on this thread"); + } + return glc.getGL(); + } + + /** + * Returns this thread current context. + * If no context is current, returns null. + * + * @return the context current on this thread, or null if no context + * is current. + */ + public static GLContext getCurrent() { + return currentContext.get(); + } + + /** + * @return true if this GLContext is current on this thread + */ + public final boolean isCurrent() { + return getCurrent() == this ; + } + + /** + * @throws GLException if this GLContext is not current on this thread + */ + 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(final int res) { + switch(res) { + case CONTEXT_NOT_CURRENT: return "CONTEXT_NOT_CURRENT"; + case CONTEXT_CURRENT: return "CONTEXT_CURRENT"; + case CONTEXT_CURRENT_NEW: return "CONTEXT_CURRENT_NEW"; + 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(final GLContext 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> + */ + 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. + */ + public abstract GL getGL(); + + /** + * Sets the GL pipeline object for this GLContext. + * + * @return the set GL pipeline or null if not successful + */ + public abstract GL setGL(GL gl); + + /** + * Returns the underlying native OpenGL context handle + */ + public final long getHandle() { return contextHandle; } + + /** + * Indicates whether the underlying native OpenGL context has been created. + */ + public final boolean isCreated() { + return 0 != contextHandle; + } + + /** + * Returns the attached user object for the given name to this GLContext. + */ + public final Object getAttachedObject(final String name) { + return attachedObjects.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(final String name, final Object obj) { + return attachedObjects.put(name, obj); + } + + public final Object detachObject(final String name) { + return attachedObjects.remove(name); + } + + /** + * Classname, GL, GLDrawable + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append(" ["); + this.append(sb); + sb.append("] "); + return sb.toString(); + } + + public final StringBuilder append(final StringBuilder sb) { + sb.append("Version ").append(getGLVersion()).append(" [GL ").append(getGLVersionNumber()).append(", vendor ").append(getGLVendorVersionNumber()); + sb.append("], options 0x"); + sb.append(Integer.toHexString(ctxOptions)); + sb.append(", this "); + sb.append(toHexString(hashCode())); + sb.append(", handle "); + sb.append(toHexString(contextHandle)); + 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()); + sb.append(",\n\tWrite Drawable: "); + sb.append(getGLDrawable()); + } else { + sb.append(",\n\tDrawable: "); + sb.append(getGLDrawable()); + } + return sb; + } + + /** + * Returns true if the specified OpenGL core- or extension-function can be + * successfully called using this GL context given the current host (OpenGL + * <i>client</i>) and display (OpenGL <i>server</i>) configuration. + * + * See {@link GL#isFunctionAvailable(String)} for more details. + * + * @param glFunctionName the name of the OpenGL function (e.g., use + * "glPolygonOffsetEXT" or "glPolygonOffset" to check if the {@link + * com.jogamp.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 + * <i>client</i>) and display (OpenGL <i>server</i>) configuration. + * + * See {@link GL#isExtensionAvailable(String)} for more details. + * + * @param glExtensionName the name of the OpenGL extension (e.g., + * "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 + current. */ + public abstract String getPlatformExtensionsString(); + + /** 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. + This is equivalent to + {@link com.jogamp.opengl.GL#glGetString(int) glGetString}({@link com.jogamp.opengl.GL#GL_EXTENSIONS GL_EXTENSIONS}) + */ + public abstract String getGLExtensionsString(); + + /** + * @return Additional context creation flags, supported: {@link GLContext#CTX_OPTION_DEBUG}. + */ + public abstract int getContextCreationFlags(); + + /** + * @param flags Additional context creation flags, supported: {@link GLContext#CTX_OPTION_DEBUG}. + * Unsupported flags are masked out. + * Only affects this context state if not created yet via {@link #makeCurrent()}. + * @see #enableGLDebugMessage(boolean) + * @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 + * </pre><br> + * + * <ul> + * <li> options + * <ul> + * <li> <code>ES profile</code> ES profile</li> + * <li> <code>Compatibility profile</code> Compatibility profile including fixed function pipeline and deprecated functionality</li> + * <li> <code>Core profile</code> Core profile</li> + * <li> <code>forward</code> Forward profile excluding deprecated functionality</li> + * <li> <code>arb</code> refers to an ARB_create_context created context</li> + * <li> <code>debug</code> refers to a debug context</li> + * <li> <code>ES2 compatible</code> refers to an ES2 compatible implementation</li> + * <li> <code>software</code> refers to a software implementation of the rasterizer</li> + * <li> <code>hardware</code> refers to a hardware implementation of the rasterizer</li> + * </ul></li> + * <li> <i>gl-version</i> the GL_VERSION string</li> + * </ul> + * + * e.g.: + * <table border="0"> + * <tr> <td></td> <td></td> </tr> + * <tr> + * <td>row 2, cell 1</td> + * <td>row 2, cell 2</td> + * </tr> + * </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> + * <tr><td>ATI</td><td>GL2</td> <td><code>3.0 (Compatibility profile, arb, hardware) - 3.2.9704 Compatibility Profile Context</code></td></tr> + * <tr><td>ATI</td><td>GL3</td> <td><code>3.3 (Core profile, any, new, hardware) - 1.4 (3.2.9704 Compatibility Profile Context)</code></td></tr> + * <tr><td>ATI</td><td>GL3bc</td><td><code>3.3 (Compatibility profile, arb, hardware) - 1.4 (3.2.9704 Compatibility Profile Context)</code></td></tr> + * <tr><td>NV</td><td>GL2</td> <td><code>3.0 (Compatibility profile, arb, hardware) - 3.0.0 NVIDIA 195.36.07.03</code></td></tr> + * <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> + */ + public final String getGLVersion() { + return ctxVersionString; + } + + /** + * 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 ) ); } + + /** + * 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 core + * #version 330 compatibility + * ... + * </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 S_EMPTY; + } + final int minor = ctxGLSLVersion.getMinor(); + final String profileOpt; + if( isGLES() ) { + profileOpt = ctxGLSLVersion.compareTo(Version3_0) >= 0 ? " es" : S_EMPTY; + } else if( isGLCoreProfile() ) { + profileOpt = ctxGLSLVersion.compareTo(Version1_50) >= 0 ? " core" : S_EMPTY; + } else if( isGLCompatibilityProfile() ) { + profileOpt = ctxGLSLVersion.compareTo(Version1_50) >= 0 ? " compatibility" : S_EMPTY; + } else { + throw new InternalError("Neither ES, Core nor Compat: "+this); // see validateProfileBits(..) + } + return "#version " + ctxGLSLVersion.getMajor() + ( minor < 10 ? "0"+minor : minor ) + profileOpt + "\n" ; + } + + protected static final VersionNumber getStaticGLSLVersionNumber(final int glMajorVersion, final int glMinorVersion, final int ctxOptions) { + if( 0 != ( CTX_PROFILE_ES & ctxOptions ) ) { + if( 3 == glMajorVersion ) { + return Version3_0; // ES 3.0 -> GLSL 3.00 + } else if( 2 == glMajorVersion ) { + return Version1_0; // ES 2.0 -> GLSL 1.00 + } + } else if( 1 == glMajorVersion ) { + return Version1_10; // GL 1.x -> GLSL 1.10 + } else if( 2 == glMajorVersion ) { + switch ( glMinorVersion ) { + case 0: return Version1_10; // GL 2.0 -> GLSL 1.10 + default: return Version1_20; // GL 2.1 -> GLSL 1.20 + } + } else if( 3 == glMajorVersion && 2 >= glMinorVersion ) { + switch ( glMinorVersion ) { + case 0: return Version1_30; // GL 3.0 -> GLSL 1.30 + case 1: return Version1_40; // GL 3.1 -> GLSL 1.40 + default: return Version1_50; // 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_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 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(GL2ES3.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 (final 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(GLExtensions.ARB_texture_non_power_of_two); + } + + public boolean isTextureFormatBGRA8888Available() { + 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 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 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_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 0 != (ctxOptions & CTX_PROFILE_COMPAT) && + ctxVersion.compareTo(Version3_1) >= 0 ; + } + + /** + * Indicates whether this GLContext is capable of GL3. <p>Includes [ GL4bc, GL4, GL3bc, GL3 ].</p> + * @see GLProfile#isGL3() + */ + public final boolean isGL3() { + return 0 != (ctxOptions & (CTX_PROFILE_COMPAT|CTX_PROFILE_CORE)) && + ctxVersion.compareTo(Version3_1) >= 0 ; + } + + /** + * Indicates whether this GLContext uses a GL3 core profile. <p>Includes [ GL4, GL3 ].</p> + */ + public final boolean isGL3core() { + return 0 != ( ctxOptions & CTX_PROFILE_CORE ) && + ctxVersion.compareTo(Version3_1) >= 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_PROFILE_CORE ) && + ctxVersion.compareTo(Version3_1) >= 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 <i>default vertex array object</i> (VAO), + * starting w/ OpenGL 3.1 core. + * <p>Includes [ GL4, GL3 ].</p> + * <pre> + Due to GL 3.1 core spec: E.1. DEPRECATED AND REMOVED FEATURES (p 296), + GL 3.2 core spec: E.2. DEPRECATED AND REMOVED FEATURES (p 331) + 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> + ES 3.x is <i>not</i> included here. + Due to it's ES 2.0 backward compatibility it still supports the following features: + <i>client side vertex arrays</i> + <i>default vertex array object</i> + + Binding a custom VAO with ES 3.0 would cause <i>client side vertex arrays</i> via {@link GL2ES1#glVertexPointer(int, int, int, java.nio.Buffer) glVertexPointer} + to produce <code>GL_INVALID_OPERATION</code>. + + However, they are marked <i>deprecated</i>: + GL ES 3.0 spec F.1. Legacy Features (p 322). + GL ES 3.1 spec F.1. Legacy Features (p 454). + * </pre> + * <p> + * If no default VAO is implemented in the native OpenGL profile, + * an own default VAO is being used, see {@link #getDefaultVAO()}. + * </p> + * @see #getDefaultVAO() + */ + public final boolean hasNoDefaultVAO() { + return // ES 3.x not included, see above. ( 0 != ( ctxOptions & CTX_PROFILE_ES ) && ctxVersion.getMajor() >= 3 ) || + ( 0 != ( ctxOptions & CTX_IS_ARB_CREATED ) && + 0 != ( ctxOptions & CTX_PROFILE_CORE ) && + ctxVersion.compareTo(Version3_1) >= 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 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 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() { + 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 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 isGLES2() || isGL2GL3(); + } + + /** + * 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 + * + * @throws GLException if the context is not current. + */ + public final boolean setSwapInterval(final int interval) throws GLException { + validateCurrent(); + if(0<=interval) { + if( !drawableRetargeted || !hasRendererQuirk(GLRendererQuirks.NoSetSwapIntervalPostRetarget) ) { + if( setSwapIntervalImpl(interval) ) { + currentSwapInterval = interval; + return true; + } + } + } + return false; + } + protected boolean setSwapIntervalImpl(final 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> + * 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() { + return currentSwapInterval; + } + protected final void setDefaultSwapInterval() { + if(this.isGLES()) { + currentSwapInterval = 1; + } else { + currentSwapInterval = -1; + } + } + + public final boolean queryMaxSwapGroups(final int[] maxGroups, final int maxGroups_offset, + final int[] maxBarriers, final int maxBarriers_offset) { + validateCurrent(); + return queryMaxSwapGroupsImpl(maxGroups, maxGroups_offset, maxBarriers, maxBarriers_offset); + } + protected boolean queryMaxSwapGroupsImpl(final int[] maxGroups, final int maxGroups_offset, + final int[] maxBarriers, final int maxBarriers_offset) { return false; } + public final boolean joinSwapGroup(final int group) { + validateCurrent(); + return joinSwapGroupImpl(group); + } + protected boolean joinSwapGroupImpl(final int group) { /** nop per default .. **/ return false; } + protected int currentSwapGroup = -1; // default: not set yet .. + public int getSwapGroup() { + return currentSwapGroup; + } + public final boolean bindSwapBarrier(final int group, final int barrier) { + validateCurrent(); + return bindSwapBarrierImpl(group, barrier); + } + protected boolean bindSwapBarrierImpl(final int group, final int barrier) { /** nop per default .. **/ return false; } + + /** + * Return the framebuffer name bound to this context, + * see {@link GL#glBindFramebuffer(int, int)}. + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> + */ + public abstract int getBoundFramebuffer(int target); + + /** + * 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> + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </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> + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </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> + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> + */ + public abstract int getDefaultReadBuffer(); + + /** + * Get the default pixel data type, as required by e.g. {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer)}. + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> + */ + public abstract int getDefaultPixelDataType(); + + /** + * Get the default pixel data format, as required by e.g. {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer)}. + * <p> + * Method is only thread-safe while context is {@link #makeCurrent() made current}. + * </p> + */ + 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, set via {@link #setGLDebugSynchronous(boolean)}. + */ + public abstract boolean isGLDebugSynchronous(); + + /** + * Enables or disables the synchronous debug behavior via + * {@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 {@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, + * {@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 {@link GLDebugMessage}s + * @see #enableGLDebugMessage(boolean) + * @see #removeGLDebugListener(GLDebugListener) + */ + public abstract void addGLDebugListener(GLDebugListener listener); + + /** + * Remove {@link GLDebugListener}.<br> + * + * @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#glDebugMessageControl(int, int, int, int, IntBuffer, boolean)} + * and {@link GL2GL3#glDebugMessageEnableAMD(int, int, int, IntBuffer, boolean)} of the GLDebugOutput feature. + * @see #enableGLDebugMessage(boolean) + */ + public abstract void glDebugMessageControl(int source, int type, int severity, int count, IntBuffer ids, boolean enabled); + + /** + * 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#glDebugMessageInsert(int, int, int, int, int, String)} + * and {@link GL2GL3#glDebugMessageInsertAMD(int, int, int, int, String)} of the GLDebugOutput feature. + * @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, 3, 4, 5 } }; + + public static final int ES_VERSIONS[][] = { + /* 0.*/ { -1 }, + /* 1.*/ { 0, 1 }, + /* 2.*/ { 0 }, + /* 3.*/ { 0, 1 } }; + + public static final int getMaxMajor(final int ctxProfile) { + return ( 0 != ( CTX_PROFILE_ES & ctxProfile ) ) ? ES_VERSIONS.length-1 : GL_VERSIONS.length-1; + } + + public static final int getMaxMinor(final int ctxProfile, final 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; + } + } + + /** + * 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(final int ctxProfile, final int major, final 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; + } + + /** + * 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(final int ctxProfile, final int major[], final 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; + } + 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(final int ctxProfile, final int major[], final 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; + } + + protected static int composeBits(final int a8, final int b8, final int c16) { + return ( ( a8 & 0x000000FF ) << 24 ) | + ( ( b8 & 0x000000FF ) << 16 ) | + ( ( c16 & 0x0000FFFF ) ) ; + } + + private static void validateProfileBits(final int bits, final String argName) { + int num = 0; + if( 0 != ( CTX_PROFILE_COMPAT & bits ) ) { num++; } + if( 0 != ( CTX_PROFILE_CORE & bits ) ) { num++; } + if( 0 != ( CTX_PROFILE_ES & bits ) ) { num++; } + if(1!=num) { + throw new GLException("Internal Error: "+argName+": 1 != num-profiles: "+num); + } + } + + // + // version mapping + // + + /** + * @see #getDeviceVersionAvailableKey(com.jogamp.nativewindow.AbstractGraphicsDevice, int, int) + */ + protected static final IdentityHashMap<String, Integer> deviceVersionAvailable = new IdentityHashMap<String, Integer>(); + + /** + * @see #getUniqueDeviceString(com.jogamp.nativewindow.AbstractGraphicsDevice) + */ + private static final IdentityHashMap<String, String> deviceVersionsAvailableSet = new IdentityHashMap<String, String>(); + + /** 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(final AbstractGraphicsDevice device) { + synchronized ( deviceVersionsAvailableSet ) { + return deviceVersionsAvailableSet.containsKey(device.getUniqueID()); + } + } + + protected static void setAvailableGLVersionsSet(final AbstractGraphicsDevice device, final boolean set) { + synchronized ( deviceVersionsAvailableSet ) { + final String devKey = device.getUniqueID(); + if( set ) { + deviceVersionsAvailableSet.put(devKey, devKey); + } else { + deviceVersionsAvailableSet.remove(devKey); + } + if (DEBUG) { + System.err.println(getThreadName() + ": createContextARB: SET mappedVersionsAvailableSet "+devKey); + System.err.println(GLContext.dumpAvailableGLVersions(null).toString()); + } + } + } + + /** + * 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(final AbstractGraphicsDevice device, final int major, final int profile) { + final String r = device.getUniqueID() + "-" + toHexString(composeBits(major, profile, 0)); + return r.intern(); + } + + /** + * 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 com.jogamp.opengl.GLDrawableFactory#getOrCreateSharedContext(com.jogamp.nativewindow.AbstractGraphicsDevice)}, + * GLProfile has to map the available versions. + * + * @param reqMajor 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 old mapped value + * + * @see #createContextARBMapVersionsAvailable + */ + protected static Integer mapAvailableGLVersion(final AbstractGraphicsDevice device, + final int reqMajor, final int profile, final int resMajor, final int resMinor, int resCtp) + { + validateProfileBits(profile, "profile"); + validateProfileBits(resCtp, "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 = Integer.valueOf(composeBits(resMajor, resMinor, resCtp)); + synchronized(deviceVersionAvailable) { + return deviceVersionAvailable.put( objectKey, 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(final 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; + } + + /** + * @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} + * @return the available GL version as encoded with {@link #composeBits(int, int, int), otherwise <code>null</code> + */ + protected static Integer getAvailableGLVersion(final AbstractGraphicsDevice device, final int reqMajor, final int reqProfile) { + final String objectKey = getDeviceVersionAvailableKey(device, reqMajor, reqProfile); + Integer val; + synchronized(deviceVersionAvailable) { + val = deviceVersionAvailable.get( objectKey ); + } + return val; + } + + /** + * @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 major if not null, returns the used major version + * @param minor if not null, returns the used minor version + * @param ctp if not null, returns the used context profile + */ + protected static boolean getAvailableGLVersion(final AbstractGraphicsDevice device, final int reqMajor, final int reqProfile, + final int[] major, final int minor[], final int ctp[]) { + + final Integer valI = getAvailableGLVersion(device, reqMajor, reqProfile); + if(null==valI) { + return false; + } + + final int bits32 = valI.intValue(); + + if(null!=major) { + major[0] = ( bits32 & 0xFF000000 ) >>> 24 ; + } + if(null!=minor) { + 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(final int major, final int minor, final int ctp) + throws GLException { + 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 if(0 != ( CTX_PROFILE_CORE & ctp )) { + if(major >= 4) { return GLProfile.GL4; } + else if(major == 3 && minor >= 1) { return GLProfile.GL3; } + } else if(0 != ( CTX_PROFILE_ES & ctp )) { + 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, final 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); + + final int _major[] = { 0 }; + final int _minor[] = { 0 }; + final 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 for the device regarding availability, version and profile bits. + */ + protected static GLProfile getAvailableGLProfile(final AbstractGraphicsDevice device, final int reqMajor, final 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(final AbstractGraphicsDevice device, final int reqMajor, final int reqProfile) + throws GLException { + final int major[] = { 0 }; + final int minor[] = { 0 }; + final int ctp[] = { 0 }; + if(GLContext.getAvailableGLVersion(device, reqMajor, reqProfile, major, minor, ctp)) { + return GLContext.getGLProfile(major[0], minor[0], ctp[0]); + } + return 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} + */ + protected static String getAvailableGLVersionAsString(final AbstractGraphicsDevice device, final int major, final int profile) { + final int _major[] = { 0 }; + final int _minor[] = { 0 }; + final 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(final AbstractGraphicsDevice device, final 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(final AbstractGraphicsDevice device, final 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. + */ + protected static boolean isGLVersionAvailable(final AbstractGraphicsDevice device, final int reqMajor, final int reqProfile, final boolean isHardware[]) { + final Integer valI = getAvailableGLVersion(device, reqMajor, reqProfile); + if(null==valI) { + return false; + } + isHardware[0] = 0 == ( valI.intValue() & GLContext.CTX_IMPL_ACCEL_SOFT ) ; + return true; + } + + public static boolean isGLES1Available(final AbstractGraphicsDevice device, final boolean isHardware[]) { + return isGLVersionAvailable(device, 1, GLContext.CTX_PROFILE_ES, isHardware); + } + + public static boolean isGLES2Available(final AbstractGraphicsDevice device, final boolean isHardware[]) { + return isGLVersionAvailable(device, 2, GLContext.CTX_PROFILE_ES, isHardware); + } + + public static boolean isGLES3Available(final AbstractGraphicsDevice device, final 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(final AbstractGraphicsDevice device) { + final int major[] = { 0 }; + final int minor[] = { 0 }; + final 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 ) { + GLContext.getAvailableGLVersion(device, 3, GLContext.CTX_PROFILE_COMPAT, major, minor, ctp); + } + return 0 != ( ctp[0] & CTX_IMPL_ES3_COMPAT ); + } + + public static boolean isGL4bcAvailable(final AbstractGraphicsDevice device, final boolean isHardware[]) { + return isGLVersionAvailable(device, 4, CTX_PROFILE_COMPAT, isHardware); + } + + public static boolean isGL4Available(final AbstractGraphicsDevice device, final boolean isHardware[]) { + return isGLVersionAvailable(device, 4, CTX_PROFILE_CORE, isHardware); + } + + public static boolean isGL3bcAvailable(final AbstractGraphicsDevice device, final boolean isHardware[]) { + return isGLVersionAvailable(device, 3, CTX_PROFILE_COMPAT, isHardware); + } + + public static boolean isGL3Available(final AbstractGraphicsDevice device, final boolean isHardware[]) { + return isGLVersionAvailable(device, 3, CTX_PROFILE_CORE, isHardware); + } + + public static boolean isGL2Available(final AbstractGraphicsDevice device, final boolean isHardware[]) { + return isGLVersionAvailable(device, 2, CTX_PROFILE_COMPAT, isHardware); + } + + protected static String getGLVersion(final int major, final int minor, final int ctp, final String gl_version) { + boolean needColon = false; + final StringBuilder sb = new StringBuilder(); + sb.append(major); + sb.append("."); + sb.append(minor); + sb.append(" ("); + needColon = appendString(sb, "ES profile", needColon, 0 != ( CTX_PROFILE_ES & 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, "arb", needColon, 0 != ( CTX_IS_ARB_CREATED & 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 { + needColon = appendString(sb, "hardware", needColon, true); + } + sb.append(")"); + if(null!=gl_version) { + sb.append(" - "); + sb.append(gl_version); + } + return sb.toString(); + } + + // + // internal string utils + // + + protected static String toHexString(final int hex) { + return "0x" + Integer.toHexString(hex); + } + + protected static String toHexString(final long hex) { + return "0x" + Long.toHexString(hex); + } + + private static boolean appendString(final StringBuilder sb, final String string, boolean needColon, final boolean condition) { + if(condition) { + if(needColon) { + sb.append(", "); + } + sb.append(string); + needColon=true; + } + return needColon; + } + + protected static String getThreadName() { return Thread.currentThread().getName(); } + +} + diff --git a/src/jogl/classes/com/jogamp/opengl/GLDebugListener.java b/src/jogl/classes/com/jogamp/opengl/GLDebugListener.java new file mode 100644 index 000000000..30e1a49c2 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLDebugListener.java @@ -0,0 +1,44 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl; + +/** + * 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); +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLDebugMessage.java b/src/jogl/classes/com/jogamp/opengl/GLDebugMessage.java new file mode 100644 index 000000000..a8868026b --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLDebugMessage.java @@ -0,0 +1,253 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl; + +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 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 + * @param dbgSource The ARB source + * @param dbgType The ARB type + * @param dbgId The ARB id + * @param dbgSeverity The ARB severity level + * @param dbgMsg The debug message + */ + public GLDebugMessage(final GLContext source, final long when, final int dbgSource, final int dbgType, final int dbgId, final int dbgSeverity, final String dbgMsg) { + this.source = source; + this.when = when; + this.dbgSource = dbgSource; + this.dbgType = dbgType; + this.dbgId = dbgId; + this.dbgSeverity = dbgSeverity; + this.dbgMsg = dbgMsg; + } + + /** + * + * @param source + * @param when + * @param dbgId + * @param amdDbgCategory + * @param dbgSeverity AMD severity level equals ARB severity level (value and semantic) + * @param dbgMsg + * @return + */ + public static GLDebugMessage translateAMDEvent(final GLContext source, final long when, final int dbgId, final int amdDbgCategory, final int dbgSeverity, final String dbgMsg) { + int dbgSource, dbgType; + + // AMD category == ARB source/type + switch(amdDbgCategory) { + case GL2GL3.GL_DEBUG_CATEGORY_API_ERROR_AMD: + dbgSource = GL2ES2.GL_DEBUG_SOURCE_API; + dbgType = GL2ES2.GL_DEBUG_TYPE_ERROR; + break; + + // + // def source / other type + // + + case GL2GL3.GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD: + dbgSource = GL2ES2.GL_DEBUG_SOURCE_WINDOW_SYSTEM; + dbgType = GL2ES2.GL_DEBUG_TYPE_OTHER; + break; + + case GL2GL3.GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD: + dbgSource = GL2ES2.GL_DEBUG_SOURCE_SHADER_COMPILER; + dbgType = GL2ES2.GL_DEBUG_TYPE_OTHER; + break; + + case GL2GL3.GL_DEBUG_CATEGORY_APPLICATION_AMD: + dbgSource = GL2ES2.GL_DEBUG_SOURCE_APPLICATION; + dbgType = GL2ES2.GL_DEBUG_TYPE_OTHER; + break; + + + // + // other source / def type + // + + case GL2GL3.GL_DEBUG_CATEGORY_DEPRECATION_AMD: + dbgSource = GL2ES2.GL_DEBUG_SOURCE_OTHER; + dbgType = GL2ES2.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR; + break; + + case GL2GL3.GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD: + dbgSource = GL2ES2.GL_DEBUG_SOURCE_OTHER; + dbgType = GL2ES2.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR; + break; + + case GL2GL3.GL_DEBUG_CATEGORY_PERFORMANCE_AMD: + dbgSource = GL2ES2.GL_DEBUG_SOURCE_OTHER; + dbgType = GL2ES2.GL_DEBUG_TYPE_PERFORMANCE; + break; + + case GL2GL3.GL_DEBUG_CATEGORY_OTHER_AMD: + default: + dbgSource = GL2ES2.GL_DEBUG_SOURCE_OTHER; + dbgType = GL2ES2.GL_DEBUG_TYPE_OTHER; + } + + return new GLDebugMessage(source, when, dbgSource, dbgType, dbgId, dbgSeverity, dbgMsg); + } + + public static int translateARB2AMDCategory(final int dbgSource, final int dbgType) { + switch (dbgSource) { + case GL2ES2.GL_DEBUG_SOURCE_WINDOW_SYSTEM: + return GL2GL3.GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD; + + case GL2ES2.GL_DEBUG_SOURCE_SHADER_COMPILER: + return GL2GL3.GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD; + + case GL2ES2.GL_DEBUG_SOURCE_APPLICATION: + return GL2GL3.GL_DEBUG_CATEGORY_APPLICATION_AMD; + } + + switch(dbgType) { + case GL2ES2.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + return GL2GL3.GL_DEBUG_CATEGORY_DEPRECATION_AMD; + + case GL2ES2.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + return GL2GL3.GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD; + + case GL2ES2.GL_DEBUG_TYPE_PERFORMANCE: + return GL2GL3.GL_DEBUG_CATEGORY_PERFORMANCE_AMD; + } + + return GL2GL3.GL_DEBUG_CATEGORY_OTHER_AMD; + } + + public GLContext getSource() { + return source; + } + + public long getWhen() { + return when; + } + + public int getDbgSource() { + return dbgSource; + } + + public int getDbgType() { + return dbgType; + } + + public int getDbgId() { + return dbgId; + } + + public int getDbgSeverity() { + return dbgSeverity; + } + + public String getDbgMsg() { + return dbgMsg; + } + + public StringBuilder toString(StringBuilder sb) { + final String crtab = Platform.getNewline()+"\t"; + if(null==sb) { + sb = new StringBuilder(); + } + sb.append("GLDebugEvent[ id "); + toHexString(sb, dbgId) + .append(crtab).append("type ").append(getDbgTypeString(dbgType)) + .append(crtab).append("severity ").append(getDbgSeverityString(dbgSeverity)) + .append(crtab).append("source ").append(getDbgSourceString(dbgSource)) + .append(crtab).append("msg ").append(dbgMsg) + .append(crtab).append("when ").append(when); + if(null != source) { + sb.append(crtab).append("source ").append(source.getGLVersion()).append(" - hash 0x").append(Integer.toHexString(source.hashCode())); + } + sb.append("]"); + return sb; + } + + @Override + public String toString() { + return toString(null).toString(); + } + + public static String getDbgSourceString(final int dbgSource) { + switch(dbgSource) { + case GL2ES2.GL_DEBUG_SOURCE_API: return "GL API"; + case GL2ES2.GL_DEBUG_SOURCE_SHADER_COMPILER: return "GLSL or extension compiler"; + case GL2ES2.GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "Native Windowing binding"; + case GL2ES2.GL_DEBUG_SOURCE_THIRD_PARTY: return "Third party"; + case GL2ES2.GL_DEBUG_SOURCE_APPLICATION: return "Application"; + case GL2ES2.GL_DEBUG_SOURCE_OTHER: return "generic"; + default: return "Unknown (" + toHexString(dbgSource) + ")"; + } + } + + public static String getDbgTypeString(final int dbgType) { + switch(dbgType) { + case GL2ES2.GL_DEBUG_TYPE_ERROR: return "Error"; + case GL2ES2.GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "Warning: marked for deprecation"; + case GL2ES2.GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "Warning: undefined behavior"; + case GL2ES2.GL_DEBUG_TYPE_PERFORMANCE: return "Warning: implementation dependent performance"; + case GL2ES2.GL_DEBUG_TYPE_PORTABILITY: return "Warning: vendor-specific extension use"; + case GL2ES2.GL_DEBUG_TYPE_OTHER: return "Warning: generic"; + default: return "Unknown (" + toHexString(dbgType) + ")"; + } + } + + public static String getDbgSeverityString(final int dbgSeverity) { + switch(dbgSeverity) { + case GL2ES2.GL_DEBUG_SEVERITY_HIGH: return "High: dangerous undefined behavior"; + case GL2ES2.GL_DEBUG_SEVERITY_MEDIUM: return "Medium: Severe performance/deprecation/other warnings"; + case GL2ES2.GL_DEBUG_SEVERITY_LOW: return "Low: Performance warnings (redundancy/undefined)"; + default: return "Unknown (" + toHexString(dbgSeverity) + ")"; + } + } + + public static StringBuilder toHexString(StringBuilder sb, final int i) { + if(null==sb) { + sb = new StringBuilder(); + } + return sb.append("0x").append(Integer.toHexString(i)); + } + public static String toHexString(final int i) { + return "0x"+Integer.toHexString(i); + } + +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLDrawable.java b/src/jogl/classes/com/jogamp/opengl/GLDrawable.java new file mode 100644 index 000000000..c801ba463 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLDrawable.java @@ -0,0 +1,251 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl; + +import com.jogamp.nativewindow.AbstractGraphicsConfiguration; +import com.jogamp.nativewindow.NativeSurface; +import com.jogamp.nativewindow.NativeSurfaceHolder; + + +/** An abstraction for an OpenGL rendering target. A GLDrawable's + primary functionality is to create OpenGL contexts which can be + used to perform rendering. A GLDrawable does not automatically + create an OpenGL context, but all implementations of {@link + GLAutoDrawable} do so upon creation. */ + +public interface GLDrawable extends NativeSurfaceHolder { + /** + * Creates a new context for drawing to this drawable that will + * 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 + * <a href="../../../overview-summary.html#SHARING">context sharing</a> + * as well as {@link GLSharedContextSetter}. + * </p> + */ + public GLContext createContext(GLContext shareWith); + + /** + * 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#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> + * <code>GLDrawable</code> implementations should handle multiple + * cycles of <code>setRealized(true)</code> / + * <code>setRealized(false)</code> calls. Most, if not all, Java + * window toolkits have a persistent object associated with a given + * component, regardless of whether that component is currently + * realized. The <CODE>GLDrawable</CODE> object associated with a + * particular component is intended to be similarly persistent. A + * <CODE>GLDrawable</CODE> is intended to be created for a given + * component when it is constructed and live as long as that + * component. <code>setRealized</code> allows the + * <code>GLDrawable</code> to re-initialize and destroy any + * associated resources as the component becomes realized and + * unrealized, respectively. + * </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 + * 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> + * 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); + + /** + * 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 width of this {@link GLDrawable}'s {@link #getNativeSurface() surface} client area in pixel units. + * @see NativeSurface#getSurfaceWidth() + */ + public int getSurfaceWidth(); + + /** + * Returns the height of this {@link GLDrawable}'s {@link #getNativeSurface() surface} client area in pixel units. + * @see NativeSurface#getSurfaceHeight() + */ + public int getSurfaceHeight(); + + /** + * 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 + automatically and should not be called by the end user. */ + public void swapBuffers() throws GLException; + + /** Fetches the {@link GLCapabilitiesImmutable} corresponding to the chosen + OpenGL capabilities (pixel format / visual / GLProfile) for this drawable. + <p> + This query only returns the chosen capabilities if {@link #isRealized()}. + </p> + <p> + On some platforms, the pixel format is not directly associated + with the drawable; a best attempt is made to return a reasonable + value in this case. + </p> + <p> + This object shall be directly associated to the attached {@link NativeSurface}'s + {@link AbstractGraphicsConfiguration}, and if changes are necessary, + they should reflect those as well. + </p> + @return The immutable queried instance. + @see #getRequestedGLCapabilities() + */ + public GLCapabilitiesImmutable getChosenGLCapabilities(); + + /** Fetches the {@link GLCapabilitiesImmutable} corresponding to the user requested + OpenGL capabilities (pixel format / visual / GLProfile) for this drawable. + <p> + If {@link #isRealized() realized}, {@link #getChosenGLCapabilities() the chosen capabilities} + reflect the actual selected OpenGL capabilities. + </p> + @return The immutable queried instance. + @see #getChosenGLCapabilities() + @since 2.2 + */ + public GLCapabilitiesImmutable getRequestedGLCapabilities(); + + /** Fetches the {@link GLProfile} for this drawable. + Returns the GLProfile object, no copy. + */ + public GLProfile getGLProfile(); + + /** + * {@inheritDoc} + * <p> + * Returns the underlying {@link NativeSurface} which {@link NativeSurface#getSurfaceHandle() native handle} + * represents this OpenGL drawable's native resource. + * </p> + * + * @see #getHandle() + */ + @Override + public NativeSurface getNativeSurface(); + + /** + * 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 com.jogamp.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/com/jogamp/opengl/GLDrawableFactory.java b/src/jogl/classes/com/jogamp/opengl/GLDrawableFactory.java new file mode 100644 index 000000000..07c3e77e0 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLDrawableFactory.java @@ -0,0 +1,798 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl; + +import java.util.ArrayList; +import java.util.List; + +import com.jogamp.common.util.PropertyAccess; +import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.opengl.GLAutoDrawableDelegate; +import com.jogamp.opengl.GLRendererQuirks; + +import com.jogamp.nativewindow.AbstractGraphicsDevice; +import com.jogamp.nativewindow.AbstractGraphicsScreen; +import com.jogamp.nativewindow.AbstractGraphicsConfiguration; +import com.jogamp.nativewindow.CapabilitiesImmutable; +import com.jogamp.nativewindow.NativeSurface; +import com.jogamp.nativewindow.NativeWindowFactory; +import com.jogamp.nativewindow.ProxySurface; +import com.jogamp.nativewindow.UpstreamSurfaceHook; + +import jogamp.opengl.Debug; + +/** <p> Provides a virtual machine- and operating system-independent + mechanism for creating {@link GLDrawable}s. + </p> + <p> The {@link com.jogamp.opengl.GLCapabilities} objects passed + in to the various factory methods are used as a hint for the + properties of the returned drawable. The default capabilities + selection algorithm (equivalent to passing in a null {@link + GLCapabilitiesChooser}) is described in {@link + DefaultGLCapabilitiesChooser}. Sophisticated applications needing + to change the selection algorithm may pass in their own {@link + GLCapabilitiesChooser} which can select from the available pixel + formats. The GLCapabilitiesChooser mechanism may not be supported + by all implementations or on all platforms, in which case any + passed GLCapabilitiesChooser will be ignored. + </p> + + <p> Because of the multithreaded nature of the Java platform's + Abstract Window Toolkit, it is typically not possible to immediately + reject a given {@link GLCapabilities} as being unsupportable by + either returning <code>null</code> from the creation routines or + raising a {@link GLException}. The semantics of the rejection + process are (unfortunately) left unspecified for now. The current + implementation will cause a {@link GLException} to be raised + during the first repaint of the {@link com.jogamp.opengl.awt.GLCanvas} or {@link + com.jogamp.opengl.awt.GLJPanel} if the capabilities can not be met.<br> + {@link GLOffscreenAutoDrawable} are created lazily, + see {@link #createOffscreenAutoDrawable(AbstractGraphicsDevice, GLCapabilitiesImmutable, GLCapabilitiesChooser, int, int) createOffscreenAutoDrawable(..)}. + </p> + + <p> The concrete GLDrawableFactory subclass instantiated by {@link + #getFactory getFactory} can be changed by setting the system + property <code>opengl.factory.class.name</code> to the + fully-qualified name of the desired class. + </p> +*/ +public abstract class GLDrawableFactory { + + protected static final boolean DEBUG = Debug.debug("GLDrawable"); + + private static volatile boolean isInit = false; + private static GLDrawableFactory eglFactory; + private static GLDrawableFactory nativeOSFactory; + + 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() { + if (!isInit) { // volatile: ok + synchronized (GLDrawableFactory.class) { + if (!isInit) { + isInit=true; + initSingletonImpl(); + } + } + } + } + private static final void initSingletonImpl() { + NativeWindowFactory.initSingleton(); + NativeWindowFactory.addCustomShutdownHook(false /* head */, new Runnable() { + @Override + public void run() { + shutdown0(); + } + }); + + final String nwt = NativeWindowFactory.getNativeWindowType(true); + GLDrawableFactory tmp = null; + String factoryClassName = PropertyAccess.getProperty("jogl.gldrawablefactory.class.name", true); + final ClassLoader cl = GLDrawableFactory.class.getClassLoader(); + if (null == factoryClassName) { + if ( nwt == NativeWindowFactory.TYPE_X11 ) { + factoryClassName = "jogamp.opengl.x11.glx.X11GLXDrawableFactory"; + } else if ( nwt == NativeWindowFactory.TYPE_WINDOWS ) { + factoryClassName = "jogamp.opengl.windows.wgl.WindowsWGLDrawableFactory"; + } else if ( nwt == NativeWindowFactory.TYPE_MACOSX ) { + factoryClassName = "jogamp.opengl.macosx.cgl.MacOSXCGLDrawableFactory"; + } else { + // may use egl*Factory .. + if (DEBUG || GLProfile.DEBUG) { + System.err.println("GLDrawableFactory.static - No native Windowing Factory for: "+nwt+"; May use EGLDrawableFactory, if available." ); + } + } + } + if (null != factoryClassName && !GLProfile.disableOpenGLDesktop) { + if (DEBUG || GLProfile.DEBUG) { + System.err.println("GLDrawableFactory.static - Native OS Factory for: "+nwt+": "+factoryClassName); + } + try { + tmp = (GLDrawableFactory) ReflectionUtil.createInstance(factoryClassName, cl); + } catch (final Exception jre) { + if (DEBUG || GLProfile.DEBUG) { + System.err.println("Info: GLDrawableFactory.static - Native Platform: "+nwt+" - not available: "+factoryClassName); + jre.printStackTrace(); + } + } + } + if(null != tmp && tmp.isComplete()) { + nativeOSFactory = tmp; + } + tmp = null; + + if(!GLProfile.disableOpenGLES) { + try { + tmp = (GLDrawableFactory) ReflectionUtil.createInstance("jogamp.opengl.egl.EGLDrawableFactory", cl); + } catch (final 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!"); + } + } + + protected static void shutdown() { + if (isInit) { // volatile: ok + synchronized (GLDrawableFactory.class) { + if (isInit) { + isInit=false; + shutdown0(); + } + } + } + } + + 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) { + 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.resetAllDisplayGammaNoSync(); + gldf.shutdownImpl(); + } catch (final Throwable t) { + System.err.println("GLDrawableFactory.shutdownImpl: Caught "+t.getClass().getName()+" during factory shutdown #"+(i+1)+"/"+gldfCount+" "+gldf.getClass().getName()); + if( DEBUG ) { + t.printStackTrace(); + } + } + } + glDrawableFactories.clear(); + + // both were members of glDrawableFactories and are shutdown already + nativeOSFactory = null; + eglFactory = null; + } + GLContext.shutdown(); + if( DEBUG ) { + System.err.println("GLDrawableFactory.shutdownAll.X on thread "+getThreadName()); + } + } + + 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 shutdownImpl(); + + /** + * Sets the gamma, brightness, and contrast of the display associated with the given <code>surface</code>. + * <p> + * This functionality is not available on all platforms and + * graphics hardware. Returns true if the settings were successfully + * changed, false if not. This method may return false for some + * values of the incoming arguments even on hardware which does + * support the underlying functionality. </p> + * <p> + * If this method returns true, the display settings will + * automatically be reset to their original values upon JVM exit + * (assuming the JVM does not crash); if the user wishes to change + * the display settings back to normal ahead of time, + * use {@link #resetDisplayGamma(NativeSurface)} or {@link #resetAllDisplayGamma()}. + * </p> + * <p> + * It is recommended to call {@link #resetDisplayGamma(NativeSurface)} or {@link #resetAllDisplayGamma()} + * before calling e.g. <code>System.exit()</code> from the application rather than + * rely on the shutdown hook functionality due to inevitable race + * conditions and unspecified behavior during JVM teardown. + * </p> + * <p> + * This method may be called multiple times during the application's + * execution, but calling {@link #resetDisplayGamma(NativeSurface)} + * will only reset the settings to the values + * before the first call to this method. </p> + * + * @param surface denominates the display device + * @param gamma The gamma value, typically > 1.0 (default values vary, but typically roughly 1.0) + * @param brightness The brightness value between -1.0 and 1.0, inclusive (default values vary, but typically 0) + * @param contrast The contrast, greater than 0.0 (default values vary, but typically 1) + * + * @return true if gamma settings were successfully changed, false if not + * @throws IllegalArgumentException if any of the parameters were out-of-bounds + * @see #resetDisplayGamma(NativeSurface) + * @see #resetAllDisplayGamma() + */ + public abstract boolean setDisplayGamma(final NativeSurface surface, final float gamma, final float brightness, final float contrast) throws IllegalArgumentException; + + /** + * Resets the gamma, brightness and contrast values of the display associated with the given <code>surface</code> + * to its original values before {@link #setDisplayGamma(NativeSurface, float, float, float) setDisplayGamma} + * was called the first time. + * <p> + * While it is not explicitly required that this method be called before + * exiting manually, calling it is recommended because of the inevitable + * unspecified behavior during JVM teardown. + * </p> + */ + public abstract void resetDisplayGamma(final NativeSurface surface); + + /** + * Resets the gamma, brightness and contrast values of all modified + * displays to their original values before {@link #setDisplayGamma(NativeSurface, float, float, float) setDisplayGamma} + * was called the first time. + * <p> + * While it is not explicitly required that this method be called before + * exiting manually, calling it is recommended because of the inevitable + * unspecified behavior during JVM teardown. + * </p> + */ + public abstract void resetAllDisplayGamma(); + + protected abstract void resetAllDisplayGammaNoSync(); + + /** + * Retrieve the default <code>device</code> {@link AbstractGraphicsDevice#getConnection() connection}, + * {@link AbstractGraphicsDevice#getUnitID() unit ID} and {@link AbstractGraphicsDevice#getUniqueID() unique ID name}. for this factory<br> + * 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. + * 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. + * @see #getIsDeviceCompatible(AbstractGraphicsDevice) + */ + public abstract AbstractGraphicsDevice getDefaultDevice(); + + /** + * @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); + + protected final AbstractGraphicsDevice validateDevice(AbstractGraphicsDevice device) { + if(null==device) { + device = getDefaultDevice(); + if(null==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, + // since even the default device may not be used by this factory. + if( !getIsDeviceCompatible(device) ) { + if (GLProfile.DEBUG) { + System.err.println("Info: "+getClass().getSimpleName()+".validateDevice: device not compatible : "+device); + } + return null; + } + return device; + } + + /** + * 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 + * 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 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(final 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 glp {@link GLProfile} to identify the device's {@link GLRendererQuirks}, maybe {@code null} + * @param quirk the quirk to be tested, e.g. {@link GLRendererQuirks#NoDoubleBufferedPBuffer}. + * @throws IllegalArgumentException if the quirk is out of range + * @see #getRendererQuirks(AbstractGraphicsDevice, GLProfile) + * @see GLRendererQuirks + */ + public final boolean hasRendererQuirk(final AbstractGraphicsDevice device, final GLProfile glp, final int quirk) { + final GLRendererQuirks glrq = getRendererQuirks(device, glp); + 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. + * @param glp {@link GLProfile} to identify the device's {@link GLRendererQuirks}, maybe {@code null} + * @see GLContext#getRendererQuirks() + * @see GLRendererQuirks + */ + public abstract GLRendererQuirks getRendererQuirks(AbstractGraphicsDevice device, final GLProfile glp); + + /** + * Returns the sole GLDrawableFactory instance for the desktop (X11, WGL, ..) if exist or null + */ + public static GLDrawableFactory getDesktopFactory() { + GLProfile.initSingleton(); + return nativeOSFactory; + } + + /** + * Returns the sole GLDrawableFactory instance for EGL if exist or null + */ + public static GLDrawableFactory getEGLFactory() { + GLProfile.initSingleton(); + return eglFactory; + } + + /** + * 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(final GLProfile glProfile) throws GLException { + return getFactoryImpl(glProfile.getImplName()); + } + + protected static GLDrawableFactory getFactoryImpl(final String glProfileImplName) throws GLException { + if ( GLProfile.usesNativeGLES(glProfileImplName) ) { + if(null!=eglFactory) { + return eglFactory; + } + } else if(null!=nativeOSFactory) { + return nativeOSFactory; + } + throw new GLException("No GLDrawableFactory available for profile: "+glProfileImplName); + } + + protected static GLDrawableFactory getFactoryImpl(final AbstractGraphicsDevice device) throws GLException { + if(null != nativeOSFactory && nativeOSFactory.getIsDeviceCompatible(device)) { + return nativeOSFactory; + } + if(null != eglFactory && eglFactory.getIsDeviceCompatible(device)) { + return eglFactory; + } + throw new GLException("No native platform GLDrawableFactory, nor EGLDrawableFactory available: "+device); + } + + /** + * Returns an array of available GLCapabilities for the device.<br> + * The list is sorted by the native ID, ascending.<br> + * 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 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 com.jogamp.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 null; + } + protected abstract List<GLCapabilitiesImmutable> getAvailableCapabilitiesImpl(AbstractGraphicsDevice device); + + //---------------------------------------------------------------------- + // Methods to create high-level objects + + /** + * 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 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> + * 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 #canCreateGLPbuffer(AbstractGraphicsDevice, GLProfile) + * @see GLContext#isFBOAvailable(AbstractGraphicsDevice, GLProfile) + * @see com.jogamp.opengl.GLCapabilities#isOnscreen() + * @see com.jogamp.opengl.GLCapabilities#isFBO() + * @see com.jogamp.opengl.GLCapabilities#isPBuffer() + * @see GraphicsConfigurationFactory#chooseGraphicsConfiguration(CapabilitiesImmutable, CapabilitiesImmutable, CapabilitiesChooser, AbstractGraphicsScreen, int) + */ + public abstract GLDrawable createGLDrawable(NativeSurface target) + throws IllegalArgumentException, 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 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(com.jogamp.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 {@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 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 unrealized offscreen {@link GLDrawable} + * + * @throws GLException if any window system-specific errors caused + * 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(com.jogamp.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 GLDrawable createDummyDrawable(AbstractGraphicsDevice deviceReq, boolean createNewDevice, GLCapabilitiesImmutable capsRequested, GLCapabilitiesChooser chooser); + + /** + * 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 + * @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 boolean canCreateFBO(AbstractGraphicsDevice device, GLProfile glp); + + /** + * 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 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, GLProfile glp); + + //---------------------------------------------------------------------- + // Methods for interacting with third-party OpenGL libraries + + /** + * <P> Creates a GLContext object representing an existing OpenGL + * context in an external (third-party) OpenGL-based library. This + * GLContext object may be used to draw into this preexisting + * context using its {@link GL} and {@link + * com.jogamp.opengl.glu.GLU} objects. New contexts created through + * {@link GLDrawable}s may share textures and display lists with + * this external context. </P> + * + * <P> The underlying OpenGL context must be current on the current + * thread at the time this method is called. The user is responsible + * for the maintenance of the underlying OpenGL context; calls to + * <code>makeCurrent</code> and <code>release</code> on the returned + * GLContext object have no effect. If the underlying OpenGL context + * is destroyed, the <code>destroy</code> method should be called on + * the <code>GLContext</code>. A new <code>GLContext</code> object + * should be created for each newly-created underlying OpenGL + * context. + * + * @throws GLException if any window system-specific errors caused + * the creation of the external GLContext to fail. + */ + public abstract GLContext createExternalGLContext() + throws GLException; + + /** + * Returns true if it is possible to create an external GLDrawable + * object via {@link #createExternalGLDrawable}. + * + * @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); + + /** + * <P> Creates a {@link GLDrawable} object representing an existing + * OpenGL drawable in an external (third-party) OpenGL-based + * library. This GLDrawable object may be used to create new, + * fully-functional {@link GLContext}s on the OpenGL drawable. This + * is useful when interoperating with a third-party OpenGL-based + * library and it is essential to not perturb the state of the + * library's existing context, even to the point of not sharing + * textures or display lists with that context. </P> + * + * <P> An underlying OpenGL context must be current on the desired + * drawable and the current thread at the time this method is + * called. The user is responsible for the maintenance of the + * underlying drawable. If one or more contexts are created on the + * drawable using {@link GLDrawable#createContext}, and the drawable + * is deleted by the third-party library, the user is responsible + * for calling {@link GLContext#destroy} on these contexts. </P> + * + * <P> Calls to <code>setSize</code>, <code>getWidth</code> and + * <code>getHeight</code> are illegal on the returned GLDrawable. If + * these operations are required by the user, they must be performed + * by the third-party library. </P> + * + * <P> It is legal to create both an external GLContext and + * GLDrawable representing the same third-party OpenGL entities. + * This can be used, for example, to query current state information + * using the external GLContext and then create and set up new + * GLContexts using the external GLDrawable. </P> + * + * <P> This functionality may not be available on all platforms and + * {@link #canCreateExternalGLDrawable} should be called first to + * see if it is present. For example, on X11 platforms, this API + * requires the presence of GLX 1.3 or later. + * + * @throws GLException if any window system-specific errors caused + * the creation of the external GLDrawable to fail. + */ + public abstract GLDrawable createExternalGLDrawable() + throws GLException; +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLEventListener.java b/src/jogl/classes/com/jogamp/opengl/GLEventListener.java new file mode 100644 index 000000000..8c5dfd3b3 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLEventListener.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl; + +import java.util.EventListener; + +/** Declares events which client code can use to manage OpenGL + rendering into a {@link GLAutoDrawable}. At the time any of these + methods is called, the drawable has made its associated OpenGL + context current, so it is valid to make OpenGL calls. */ + +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> + + Note that this method may be called more than once if the underlying + OpenGL context for the GLAutoDrawable is destroyed and + recreated, for example if a GLCanvas is removed from the widget + 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> + + Called by the drawable before the OpenGL context is + destroyed by an external event, like a reconfiguration of the + {@link GLAutoDrawable} closing an attached window, + but also manually by calling {@link GLAutoDrawable#destroy destroy}.<P> + + 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, + 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 + GLAutoDrawable#setAutoSwapBufferMode setAutoSwapBufferMode} is + enabled. */ + public void display(GLAutoDrawable drawable); + + /** + * Called by the drawable during the first repaint after the + * component has been resized. + * <p> + * The client can update it's viewport associated data + * and view volume of the window appropriately. + * </p> + * <p> + * For efficiency the GL viewport has already been updated + * via <code>glViewport(x, y, width, height)</code> when this method is called. + * </p> + * + * @param drawable the triggering {@link GLAutoDrawable} + * @param x viewport x-coord in pixel units + * @param y viewport y-coord in pixel units + * @param width viewport width in pixel units + * @param height viewport height in pixel units + */ + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height); +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLException.java b/src/jogl/classes/com/jogamp/opengl/GLException.java new file mode 100644 index 000000000..843934ab2 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLException.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 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: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl; + +/** A generic exception for OpenGL errors used throughout the binding + as a substitute for {@link RuntimeException}. */ +@SuppressWarnings("serial") +public class GLException extends RuntimeException { + /** Constructs a GLException object. */ + public GLException() { + super(); + } + + /** Constructs a GLException object with the specified detail + message. */ + public GLException(final String message) { + super(message); + } + + /** Constructs a GLException object with the specified detail + message and root cause. */ + public GLException(final String message, final Throwable cause) { + super(message, cause); + } + + /** Constructs a GLException object with the specified root + cause. */ + public GLException(final Throwable cause) { + super(cause); + } + + /** + * Constructs a GLException object with the specified root + * cause with a decorating message including the current thread name. + * @since 2.2 + */ + public static GLException newGLException(final Throwable t) { + return new GLException("Caught "+t.getClass().getSimpleName()+": "+t.getMessage()+" on thread "+Thread.currentThread().getName(), t); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLFBODrawable.java b/src/jogl/classes/com/jogamp/opengl/GLFBODrawable.java new file mode 100644 index 000000000..69358743e --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLFBODrawable.java @@ -0,0 +1,227 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl; + +import com.jogamp.nativewindow.NativeWindowException; + +import com.jogamp.opengl.FBObject; +import com.jogamp.opengl.FBObject.Colorbuffer; +import com.jogamp.opengl.FBObject.ColorAttachment; +import com.jogamp.opengl.FBObject.TextureAttachment; +import com.jogamp.opengl.GLRendererQuirks; + +/** + * Platform-independent {@link GLDrawable} specialization, + * 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 Colorbuffer}s with one {@link FBObject framebuffer}. + * This would require mode selection and hence complicate the API. Besides, it would + * not support differentiation of read and write framebuffer and hence not be spec compliant. + * </p> + * <p> + * Actual swapping of the {@link Colorbuffer}s and/or {@link FBObject framebuffer} + * is performed either in the {@link jogamp.opengl.GLContextImpl#contextMadeCurrent(boolean) context current hook} + * or when {@link jogamp.opengl.GLDrawableImpl#swapBuffersImpl(boolean) swapping buffers}, whatever comes first. + * </p> + */ +public interface GLFBODrawable extends GLDrawable { + // public enum DoubleBufferMode { NONE, TEXTURE, FBO }; // TODO: Add or remove TEXTURE (only) DoubleBufferMode support + + /** FBO Mode Bit: Use a {@link TextureAttachment} for the {@link #getColorbuffer(int) render colorbuffer}, see {@link #setFBOMode(int)}. */ + public static final int FBOMODE_USE_TEXTURE = 1 << 0; + + /** + * @return <code>true</code> if initialized, i.e. a {@link GLContext} is bound and made current once, otherwise <code>false</code>. + */ + public boolean isInitialized(); + + /** + * Set the FBO mode bits used for FBO creation. + * <p> + * Default value is: {@link #FBOMODE_USE_TEXTURE}. + * </p> + * <p> + * If {@link GLRendererQuirks#BuggyColorRenderbuffer} is set, + * {@link #FBOMODE_USE_TEXTURE} is always added at initialization. + * </p> + * + * @param modeBits custom FBO mode bits like {@link #FBOMODE_USE_TEXTURE}. + * @throws IllegalStateException if already initialized, see {@link #isInitialized()}. + */ + void setFBOMode(final int modeBits) throws IllegalStateException; + + /** + * @return the used FBO mode bits, mutable via {@link #setFBOMode(int)} + */ + int getFBOMode(); + + /** + * Notify this instance about upstream size change + * to reconfigure the {@link FBObject}. + * @param gl GL context object bound to this drawable, will be made current during operation. + * A prev. current context will be make current after operation. + * @throws GLException if resize operation failed + */ + void resetSize(final GL gl) throws GLException; + + /** + * @return the used texture unit + */ + int getTextureUnit(); + + /** + * + * @param unit the texture unit to be used + */ + void setTextureUnit(final 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(final GL gl, final 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 IllegalStateException if already initialized, see {@link #isInitialized()}. + */ + int setNumBuffers(final int bufferCount) throws IllegalStateException, GLException; + + /** + * @return the number of buffers (FBO) being used. 1 if not using {@link GLCapabilities#getDoubleBuffered() double buffering}, + * 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(final int bufferName) throws IllegalArgumentException; + + /** + * Returns the named {@link Colorbuffer} instance. + * <p> + * If MSAA is being used, only the {@link GL#GL_FRONT} buffer is accessible + * and an exception is being thrown if {@link GL#GL_BACK} is being requested. + * </p> + * <p> + * Depending on the {@link #setFBOMode(int) fbo mode} the resulting {@link Colorbuffer} + * is either a {@link TextureAttachment} if {@link #FBOMODE_USE_TEXTURE} is set, + * otherwise a {@link ColorAttachment}. + * See {@link Colorbuffer#isTextureAttachment()}. + * </p> + * @param bufferName {@link GL#GL_FRONT} and {@link GL#GL_BACK} are valid buffer names + * @return the named {@link Colorbuffer} + * @throws IllegalArgumentException if using MSAA and {@link GL#GL_BACK} is requested or an illegal buffer name is being used + */ + Colorbuffer getColorbuffer(final int bufferName) throws IllegalArgumentException; + + /** Resizeable {@link GLFBODrawable} specialization */ + public interface Resizeable extends GLFBODrawable { + /** + * Resize this {@link GLFBODrawable}'s surface. + * <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 new width in pixel units + * @param newHeight new width in pixel units + * @throws NativeWindowException in case the surface could no be locked + * @throws GLException in case an error during the resize operation occurred + */ + void setSurfaceSize(GLContext context, int newWidth, int newHeight) throws NativeWindowException, GLException; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLOffscreenAutoDrawable.java b/src/jogl/classes/com/jogamp/opengl/GLOffscreenAutoDrawable.java new file mode 100644 index 000000000..a2d0f5fdb --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLOffscreenAutoDrawable.java @@ -0,0 +1,69 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl; + +import com.jogamp.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 #setSurfaceSize(int, int)} functionality. + * </p> + * <p> + * <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> + * To share a {@link GLContext} see the following note in the documentation overview: + * <a href="../../../overview-summary.html#SHARING">context sharing</a> + * as well as {@link GLSharedContextSetter}. + * </p> + */ +public interface GLOffscreenAutoDrawable extends GLAutoDrawable, GLSharedContextSetter { + + /** + * Resize this {@link GLAutoDrawable}'s surface + * @param newWidth new width in pixel units + * @param newHeight new height in pixel units + * @throws NativeWindowException in case the surface could no be locked + * @throws GLException in case of an error during the resize operation + */ + void setSurfaceSize(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/com/jogamp/opengl/GLPipelineFactory.java b/src/jogl/classes/com/jogamp/opengl/GLPipelineFactory.java new file mode 100644 index 000000000..8d8b0428b --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLPipelineFactory.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +package com.jogamp.opengl; + +import java.lang.reflect.*; +import java.util.StringTokenizer; + +import com.jogamp.common.util.ReflectionUtil; + +import jogamp.opengl.*; + +/** + * Factory for pipelining GL instances + */ +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. + * + * <p> + * Sample code which installs a Debug and Trace pipeline + * automatic w/ user defined interface, here: GL2ES2: + * <pre> + * gl = drawable.setGL( GLPipelineFactory.create("com.jogamp.opengl.Debug", GL2ES2.class, gl, null) ); + * gl = drawable.setGL( GLPipelineFactory.create("com.jogamp.opengl.Trace", GL2ES2.class, gl, new Object[] { System.err } ) ); + * </pre> + * or automatic w/ automatic defined class: + * <pre> + * gl = drawable.setGL( GLPipelineFactory.create("com.jogamp.opengl.Debug", null, gl, null) ); + * gl = drawable.setGL( GLPipelineFactory.create("com.jogamp.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> + * <li> For the <code>downstream</code> class and it's superclasses, do:</li> + * <ul> + * <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 upstream class is available use it, end loop.</li> + * </ul> + * </ul> + * </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 + * @param downstream is always the 1st argument for the upstream constructor + * @param additionalArgs additional arguments for the upstream constructor + */ + public static final GL create(final String pipelineClazzBaseName, final Class<?> reqInterface, final GL downstream, final Object[] additionalArgs) { + Class<?> downstreamClazz = downstream.getClass(); + Class<?> upstreamClazz = null; + Class<?> interfaceClazz = null; + + if(DEBUG) { + System.out.println("GLPipelineFactory: Start "+downstreamClazz.getName()+", req. Interface: "+reqInterface+" -> "+pipelineClazzBaseName); + } + + // For all classes: child -> parent + do { + // For all interfaces: right -> left == child -> parent + // It is important that this matches with the gluegen cfg file's 'Implements' clause ! + final Class<?>[] clazzes = downstreamClazz.getInterfaces(); + for(int i=clazzes.length-1; null==upstreamClazz && i>=0; i--) { + if(DEBUG) { + System.out.println("GLPipelineFactory: Try "+downstreamClazz.getName()+" Interface["+i+"]: "+clazzes[i].getName()); + } + if( reqInterface != null && !reqInterface.getName().equals(clazzes[i].getName()) ) { + if(DEBUG) { + System.out.println("GLPipelineFactory: requested Interface "+reqInterface+" is _not_ "+ clazzes[i].getName()); + } + continue; // not the requested one .. + } + if( ! clazzes[i].isInstance(downstream) ) { + if(DEBUG) { + System.out.println("GLPipelineFactory: "+downstream.getClass().getName() + " is _not_ instance of "+ clazzes[i].getName()); + } + continue; // not a compatible one + } else { + if(DEBUG) { + System.out.println("GLPipelineFactory: "+downstream.getClass().getName() + " _is_ instance of "+ clazzes[i].getName()); + } + } + upstreamClazz = getUpstreamClazz(clazzes[i], pipelineClazzBaseName); + if( null != upstreamClazz ) { + interfaceClazz = clazzes[i]; + } + } + + if(null==upstreamClazz) { + downstreamClazz = downstreamClazz.getSuperclass(); + } + } while (null!=downstreamClazz && null==upstreamClazz); + + + if(null==upstreamClazz) { + throw new GLException("No pipeline ("+pipelineClazzBaseName+"*) available for :"+downstream.getClass().getName()); + } + + if(DEBUG) { + System.out.println("GLPipelineFactory: Got : "+ upstreamClazz.getName()+", base interface: "+interfaceClazz.getName()); + } + + final Class<?>[] cstrArgTypes = new Class<?>[ 1 + ( ( null==additionalArgs ) ? 0 : additionalArgs.length ) ] ; + { + int i = 0; + cstrArgTypes[i++] = interfaceClazz; + for(int j=0; null!=additionalArgs && j<additionalArgs.length; j++) { + cstrArgTypes[i++] = additionalArgs[j].getClass(); + } + } + // throws exception if cstr not found! + final Constructor<?> cstr = ReflectionUtil.getConstructor(upstreamClazz, cstrArgTypes); + Object instance = null; + try { + final Object[] cstrArgs = new Object[ 1 + ( ( null==additionalArgs ) ? 0 : additionalArgs.length ) ] ; + { + int i = 0; + cstrArgs[i++] = downstream; + for(int j=0; null!=additionalArgs && j<additionalArgs.length; j++) { + cstrArgs[i++] = additionalArgs[j]; + } + } + instance = cstr.newInstance( cstrArgs ) ; + } catch (final Throwable t) { t.printStackTrace(); } + if(null==instance) { + throw new GLException("Error: Couldn't create instance of pipeline: "+upstreamClazz.getName()+ + " ( "+getArgsClassNameList(downstreamClazz, additionalArgs) +" )"); + } + if( ! (instance instanceof GL) ) { + throw new GLException("Error: "+upstreamClazz.getName()+" not an instance of GL"); + } + return (GL) instance; + } + + private static final String getArgsClassNameList(final Class<?> arg0, final Object[] args) { + final StringBuilder sb = new StringBuilder(); + sb.append(arg0.getName()); + if(args!=null) { + for(int j=0; j<args.length; j++) { + sb.append(", "); + sb.append(args[j].getClass().getName()); + } + } + return sb.toString(); + } + + private static final Class<?> getUpstreamClazz(final Class<?> downstreamClazz, final String pipelineClazzBaseName) { + final String downstreamClazzName = downstreamClazz.getName(); + + final StringTokenizer st = new StringTokenizer(downstreamClazzName, "."); + String downstreamClazzBaseName = downstreamClazzName; + while(st.hasMoreTokens()) { + downstreamClazzBaseName = st.nextToken(); + } + final String upstreamClazzName = pipelineClazzBaseName+downstreamClazzBaseName; + + Class<?> upstreamClazz = null; + try { + upstreamClazz = Class.forName(upstreamClazzName, true, GLPipelineFactory.class.getClassLoader()); + } catch (final Throwable e) { e.printStackTrace(); } + + return upstreamClazz; + } +} + diff --git a/src/jogl/classes/com/jogamp/opengl/GLProfile.java b/src/jogl/classes/com/jogamp/opengl/GLProfile.java new file mode 100644 index 000000000..a36a21ad5 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLProfile.java @@ -0,0 +1,2322 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + */ + +package com.jogamp.opengl; + +import jogamp.opengl.Debug; +import jogamp.opengl.GLDrawableFactoryImpl; +import jogamp.opengl.DesktopGLDynamicLookupHelper; + +import com.jogamp.common.ExceptionUtils; +import com.jogamp.common.GlueGenVersion; +import com.jogamp.common.jvm.JNILibLoaderBase; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.PropertyAccess; +import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.common.util.VersionUtil; +import com.jogamp.common.util.cache.TempJarCache; +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.GLRendererQuirks; +import com.jogamp.opengl.JoglVersion; + +import com.jogamp.nativewindow.AbstractGraphicsDevice; +import com.jogamp.nativewindow.NativeWindowFactory; +import com.jogamp.opengl.fixedfunc.GLPointerFunc; + +import java.lang.reflect.Constructor; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashMap; +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()}, + * or more specialized versions using the other static GetProfile methods. + */ +public class GLProfile { + + public static final boolean DEBUG; + + /** + * In case no native OpenGL core profiles are required + * and if one platform may have a buggy implementation, + * setting the property <code>jogl.disable.openglcore</code> disables querying possible existing native OpenGL core profiles. + * <p> + * This exclusion is disabled for {@link Platform.OSType#MACOS}. + * </p> + */ + public static final boolean disableOpenGLCore; + + /** + * In case the implementation of the <i>ARB_create_context</i> + * context creation extension is buggy on one platform, + * setting the property <code>jogl.disable.openglarbcontext</code> disables utilizing it. + * <p> + * This exclusion also disables {@link #disableOpenGLES OpenGL ES}. + * </p> + * <p> + * This exclusion is disabled for {@link Platform.OSType#MACOS}. + * </p> + */ + public static final boolean disableOpenGLARBContext; + + /** + * In case no OpenGL ES profiles are required + * and if one platform may have a buggy implementation, + * setting the property <code>jogl.disable.opengles</code> disables querying possible existing OpenGL ES profiles. + */ + public static final boolean disableOpenGLES; + + /** + * In case no OpenGL desktop profiles are required + * and if one platform may have a buggy implementation, + * setting the property <code>jogl.disable.opengldesktop</code> disables querying possible existing OpenGL desktop profiles. + */ + public static final boolean disableOpenGLDesktop; + + /** + * Disable surfaceless OpenGL context capability and its probing + * by setting the property <code>jogl.disable.surfacelesscontext</code>. + * <p> + * By default surfaceless OpenGL context capability is probed, + * i.e. whether an OpenGL context can be made current without a default framebuffer. + * </p> + * <p> + * If probing fails or if this property is set, the {@link GLRendererQuirks quirk} {@link GLRendererQuirks#NoSurfacelessCtx} + * is being set. + * </p> + */ + public static final boolean disableSurfacelessContext; + + /** + * We have to disable support for ANGLE, the D3D ES2 emulation on Windows provided w/ Firefox and Chrome. + * When run in the mentioned browsers, the eglInitialize(..) implementation crashes. + * <p> + * This can be overridden by explicitly enabling ANGLE on Windows by setting the property + * <code>jogl.enable.ANGLE</code>. + * </p> + */ + public static final boolean enableANGLE; + + static { + // Also initializes TempJarCache if shall be used. + Platform.initSingleton(); + final boolean isOSX = Platform.OSType.MACOS == Platform.getOSType(); + + DEBUG = Debug.debug("GLProfile"); + disableOpenGLCore = PropertyAccess.isPropertyDefined("jogl.disable.openglcore", true) && !isOSX; + disableOpenGLARBContext = PropertyAccess.isPropertyDefined("jogl.disable.openglarbcontext", true) && !isOSX; + disableOpenGLES = disableOpenGLARBContext || PropertyAccess.isPropertyDefined("jogl.disable.opengles", true); + disableOpenGLDesktop = PropertyAccess.isPropertyDefined("jogl.disable.opengldesktop", true); + disableSurfacelessContext = PropertyAccess.isPropertyDefined("jogl.disable.surfacelesscontext", true); + enableANGLE = PropertyAccess.isPropertyDefined("jogl.enable.ANGLE", true); + } + + /** + * @return <code>true</code> if JOGL has been initialized, i.e. manually via {@link #initSingleton()} or implicit, + * otherwise returns <code>false</code>. + * + * @since 2.2.1 + */ + public static boolean isInitialized() { + initLock.lock(); + try { + return initialized; + } finally { + initLock.unlock(); + } + } + + /** + * Static initialization of JOGL. + * + * <p> + * This method shall not need to be called for other reasons than having a defined initialization sequence. + * </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)}. + * <P> + * + * <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 justInitialized; + initLock.lock(); + try { + if(!initialized) { + initialized = true; + justInitialized = true; + if(DEBUG) { + System.err.println("GLProfile.initSingleton() - thread "+Thread.currentThread().getName()); + ExceptionUtils.dumpStack(System.err); + } + + 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()) { + final ClassLoader cl = GLProfile.class.getClassLoader(); + final String newtDebugClassName = "jogamp.newt.Debug"; + final Class<?>[] classesFromJavaJars = new Class<?>[] { jogamp.nativewindow.Debug.class, jogamp.opengl.Debug.class, null }; + if( ReflectionUtil.isClassAvailable(newtDebugClassName, cl) ) { + classesFromJavaJars[2] = ReflectionUtil.getClass(newtDebugClassName, false, cl); + } + JNILibLoaderBase.addNativeJarLibsJoglCfg(classesFromJavaJars); + } + initProfilesForDefaultDevices(); + return null; + } + }); + if( ReflectionUtil.DEBUG_STATS_FORNAME ) { + if( justInitialized ) { + System.err.println(ReflectionUtil.getForNameStats(null).toString()); + } + } + } else { + justInitialized = false; + } + } finally { + initLock.unlock(); + } + 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(final AbstractGraphicsDevice device) throws GLException { + getProfileMap(device, true); + } + + /** + * 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 com.jogamp.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 at a defined time. + * </p> + */ + public static void shutdown() { + initLock.lock(); + try { + if(initialized) { + initialized = false; + if(DEBUG) { + System.err.println("GLProfile.shutdown() - thread "+Thread.currentThread().getName()); + ExceptionUtils.dumpStack(System.err); + } + GLDrawableFactory.shutdown(); + } + } finally { + initLock.unlock(); + } + } + + // + // 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}, ..), + * or <code>[ null, GL ]</code> for the default profile. + * @return true if the profile is available for the device, otherwise false. + */ + public static boolean isAvailable(final AbstractGraphicsDevice device, final String profile) { + initSingleton(); + return isAvailableImpl(getProfileMap(device, false), profile); + } + private static boolean isAvailableImpl(final HashMap<String /*GLProfile_name*/, GLProfile> map, final 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}, ..), + * 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(final 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(final AbstractGraphicsDevice device) { + return glAvailabilityToString(device, null).toString(); + } + + public static StringBuilder glAvailabilityToString(final AbstractGraphicsDevice device, final StringBuilder sb) { + return glAvailabilityToString(device, sb, null, 0); + } + private static StringBuilder doIndent(final StringBuilder sb, final String indent, int indentCount) { + while(indentCount>0) { + sb.append(indent); + indentCount--; + } + return sb; + } + public static StringBuilder glAvailabilityToString(AbstractGraphicsDevice device, StringBuilder sb, final String indent, int indentCount) { + boolean avail; + if(null == sb) { + sb = new StringBuilder(); + } + 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("Natives"); + indentCount++; + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GL4bc+" ").append(indent); + } else { + 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); + } else { + 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); + } else { + 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); + } else { + 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); + } else { + 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(GLES2+" ").append(indent); + } else { + sb.append(", "+GLES2+" "); + } + 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); + } else { + 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("Count\t"+nativeCount+" / "+allCount); + indentCount--; + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("Common"); + indentCount++; + } else { + sb.append(", count "+nativeCount+" / "+allCount+"], Common["); + } + + if(useIndent) { + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append(GL4ES3+" ").append(indent); + } else { + sb.append(", "+GL4ES3+" "); + } + 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("Mappings"); + indentCount++; + } else { + sb.append("], Mappings["); + } + + int profileCount = 0; + + if(null != map) { + for (final Map.Entry<String,GLProfile> entry : map.entrySet()) { + if( GL_DEFAULT != 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) { + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("default "); + } else { + sb.append(", default "); + } + try { + sb.append(getDefault(device)); + } catch (final GLException gle) { + sb.append("n/a"); + } + } + if(useIndent) { + doIndent(sb.append(Platform.getNewline()), indent, indentCount).append("Count\t"+profileCount+" / "+allCount); + sb.append(Platform.getNewline()); + } else { + sb.append(", count "+profileCount+" / "+allCount+"]"); + } + + return sb; + } + + /** Uses the default device */ + public static String glAvailabilityToString() { + return glAvailabilityToString(null); + } + + // + // Public (user-visible) profiles + // + + /** The desktop OpenGL compatibility profile 4.x, with x >= 0, ie GL2 plus GL4.<br> + <code>bc</code> stands for backward compatibility. */ + public static final String GL4bc = "GL4bc"; // Implicitly intern(), see Bug 1059 + + /** The desktop OpenGL core profile 4.x, with x >= 0 */ + public static final String GL4 = "GL4"; // Implicitly intern(), see Bug 1059 + + /** The desktop OpenGL compatibility profile 3.x, with x >= 1, ie GL2 plus GL3.<br> + <code>bc</code> stands for backward compatibility. */ + public static final String GL3bc = "GL3bc"; // Implicitly intern(), see Bug 1059 + + /** The desktop OpenGL core profile 3.x, with x >= 1 */ + public static final String GL3 = "GL3"; // Implicitly intern(), see Bug 1059 + + /** The desktop OpenGL profile 1.x up to 3.0 */ + public static final String GL2 = "GL2"; // Implicitly intern(), see Bug 1059 + + /** The embedded OpenGL profile ES 1.x, with x >= 0 */ + public static final String GLES1 = "GLES1"; // Implicitly intern(), see Bug 1059 + + /** The embedded OpenGL profile ES 2.x, with x >= 0 */ + public static final String GLES2 = "GLES2"; // Implicitly intern(), see Bug 1059 + + /** The embedded OpenGL profile ES 3.x, with x >= 0 */ + public static final String GLES3 = "GLES3"; // Implicitly intern(), see Bug 1059 + + /** The intersection of the desktop GL2 and embedded ES1 profile */ + public static final String GL2ES1 = "GL2ES1"; // Implicitly intern(), see Bug 1059 + + /** The intersection of the desktop GL3, GL2 and embedded ES2 profile */ + public static final String GL2ES2 = "GL2ES2"; // Implicitly intern(), see Bug 1059 + + /** The intersection of the desktop GL3 and GL2 profile */ + public static final String GL2GL3 = "GL2GL3"; // Implicitly intern(), see Bug 1059 + + /** The intersection of the desktop GL4 and ES3 profile, available only if either ES3 or GL4 w/ <code>GL_ARB_ES3_compatibility</code> is available. */ + public static final String GL4ES3 = "GL4ES3"; // Implicitly intern(), see Bug 1059 + + /** The default profile, used for the device default profile map */ + private static final String GL_DEFAULT = "GL_DEFAULT"; // Implicitly intern(), see Bug 1059 + /** The default profile, used for the device default profile map */ + private static final String GL_GL = "GL"; // Implicitly intern(), see Bug 1059 + + /** + * All GL Profiles in the order of default detection. + * 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> + * <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, GLES3, GL4ES3, GL2GL3, GLES2, GL2ES2, GLES1, GL2ES1 }; + + /** + * Order of maximum profiles. + * + * <ul> + * <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, GLES3, GL2, GLES2, GLES1 }; + + /** + * Order of minimum profiles. + * + * <ul> + * <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, GLES3, GL3, GL3bc, GL4, GL4bc }; + + /** + * Order of minimum original desktop profiles. + * + * <ul> + * <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> + * <li> GL3bc </li> + * <li> GL2 </li> + * <li> GLES1 </li> + * </ul> + * + */ + public static final String[] GL_PROFILE_LIST_MAX_FIXEDFUNC = new String[] { GL4bc, GL3bc, GL2, GLES1 }; + + /** + * Order of maximum programmable shader profiles + * + * <ul> + * <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, 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} + * and favors hardware acceleration. + * @throws GLException if no profile is available for the device. + * @see #GL_PROFILE_LIST_ALL + */ + public static GLProfile getDefault(final AbstractGraphicsDevice device) { + final GLProfile glp = get(device, GL_DEFAULT); + return glp; + } + + /** 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> + * @throws GLException if no profile is available for the default device. + */ + public static GLProfile getDefault() { + return getDefault(defaultDevice); + } + + /** + * Returns the highest profile. + * It selects the first of the set: {@link GLProfile#GL_PROFILE_LIST_MAX} + * + * @throws GLException if no profile is available for the device. + * @see #GL_PROFILE_LIST_MAX + */ + public static GLProfile getMaximum(final AbstractGraphicsDevice device, final boolean favorHardwareRasterizer) + throws GLException + { + return get(device, GL_PROFILE_LIST_MAX, favorHardwareRasterizer); + } + + /** Uses the default device + * @throws GLException if no profile is available for the default device. + * @see #GL_PROFILE_LIST_MAX + */ + public static GLProfile getMaximum(final boolean favorHardwareRasterizer) + throws GLException + { + return get(GL_PROFILE_LIST_MAX, favorHardwareRasterizer); + } + + /** + * Returns the lowest profile. + * It selects the first of the set: {@link GLProfile#GL_PROFILE_LIST_MIN} + * + * @throws GLException if no desktop profile is available for the device. + * @see #GL_PROFILE_LIST_MIN + */ + public static GLProfile getMinimum(final AbstractGraphicsDevice device, final boolean favorHardwareRasterizer) + throws GLException + { + return get(device, GL_PROFILE_LIST_MIN, favorHardwareRasterizer); + } + + /** Uses the default device + * @throws GLException if no desktop profile is available for the default device. + * @see #GL_PROFILE_LIST_MIN + */ + public static GLProfile getMinimum(final boolean favorHardwareRasterizer) + throws GLException + { + return get(GL_PROFILE_LIST_MIN, favorHardwareRasterizer); + } + + + /** + * Returns the highest profile, implementing the fixed function pipeline. + * It selects the first of the set: {@link GLProfile#GL_PROFILE_LIST_MAX_FIXEDFUNC} + * + * @throws GLException if no fixed function profile is available for the device. + * @see #GL_PROFILE_LIST_MAX_FIXEDFUNC + */ + public static GLProfile getMaxFixedFunc(final AbstractGraphicsDevice device, final boolean favorHardwareRasterizer) + throws GLException + { + return get(device, GL_PROFILE_LIST_MAX_FIXEDFUNC, favorHardwareRasterizer); + } + + /** Uses the default device + * @throws GLException if no fixed function profile is available for the default device. + * @see #GL_PROFILE_LIST_MAX_FIXEDFUNC + */ + public static GLProfile getMaxFixedFunc(final boolean favorHardwareRasterizer) + throws GLException + { + return get(GL_PROFILE_LIST_MAX_FIXEDFUNC, favorHardwareRasterizer); + } + + /** + * Returns the highest profile, implementing the programmable shader pipeline. + * It selects the first of the set: {@link GLProfile#GL_PROFILE_LIST_MAX_PROGSHADER} + * + * @throws GLException if no programmable profile is available for the device. + * @see #GL_PROFILE_LIST_MAX_PROGSHADER + */ + public static GLProfile getMaxProgrammable(final AbstractGraphicsDevice device, final boolean favorHardwareRasterizer) + throws GLException + { + return get(device, GL_PROFILE_LIST_MAX_PROGSHADER, favorHardwareRasterizer); + } + + /** Uses the default device + * @throws GLException if no programmable profile is available for the default device. + * @see #GL_PROFILE_LIST_MAX_PROGSHADER + */ + public static GLProfile getMaxProgrammable(final boolean favorHardwareRasterizer) + throws GLException + { + 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(final AbstractGraphicsDevice device, final 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(final 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> + * GLProfile.get(device, GLProfile.GL2ES1).getImpl()); + * </pre> + * <p>Selection favors hardware rasterizer.</p> + * + * @throws GLException if no GL2ES1 compatible profile is available for the default device. + * @see #isGL2ES1() + * @see #get(AbstractGraphicsDevice, String) + * @see #getImpl() + */ + public static GLProfile getGL2ES1(final AbstractGraphicsDevice device) + throws GLException + { + return get(device, GL2ES1).getImpl(); + } + + /** + * Calls {@link #getGL2ES1(AbstractGraphicsDevice)} using the default device. + * <p>Selection favors hardware rasterizer.</p> + * @see #getGL2ES1(AbstractGraphicsDevice) + */ + public static GLProfile getGL2ES1() + throws GLException + { + return get(defaultDevice, GL2ES1).getImpl(); + } + + /** + * Returns the GL2ES2 profile implementation, hence compatible w/ GL2ES2.<br/> + * It returns: + * <pre> + * GLProfile.get(device, GLProfile.GL2ES2).getImpl()); + * </pre> + * <p>Selection favors hardware rasterizer.</p> + * + * @throws GLException if no GL2ES2 compatible profile is available for the default device. + * @see #isGL2ES2() + * @see #get(AbstractGraphicsDevice, String) + * @see #getImpl() + */ + public static GLProfile getGL2ES2(final AbstractGraphicsDevice device) + throws GLException + { + return get(device, GL2ES2).getImpl(); + } + + /** + * Calls {@link #getGL2ES2(AbstractGraphicsDevice)} using the default device. + * <p>Selection favors hardware rasterizer.</p> + * @see #getGL2ES2(AbstractGraphicsDevice) + */ + public static GLProfile getGL2ES2() + throws GLException + { + 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(final 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> + * GLProfile.get(device, GLProfile.GL2GL3).getImpl()); + * </pre> + * <p>Selection favors hardware rasterizer.</p> + * + * @throws GLException if no GL2GL3 compatible profile is available for the default device. + * @see #isGL2GL3() + * @see #get(AbstractGraphicsDevice, String) + * @see #getImpl() + */ + public static GLProfile getGL2GL3(final AbstractGraphicsDevice device) + throws GLException + { + return get(device, GL2GL3).getImpl(); + } + + /** + * Calls {@link #getGL2GL3(AbstractGraphicsDevice)} using the default device. + * <p>Selection favors hardware rasterizer.</p> + * @see #getGL2GL3(AbstractGraphicsDevice) + */ + public static GLProfile getGL2GL3() + throws GLException + { + return get(defaultDevice, GL2GL3).getImpl(); + } + + /** Returns a GLProfile object. + * verifies the given profile and chooses an appropriate implementation. + * A generic value of <code>null</code> or <code>GL</code> will result in + * 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}, ..), + * or <code>[ null, GL ]</code> for the default profile. + * @throws GLException if the requested profile is not available for the device. + */ + public static GLProfile get(final AbstractGraphicsDevice device, String profile) + throws GLException + { + if(null==profile || profile == GL_GL) { + profile = GL_DEFAULT; + } + final HashMap<String /*GLProfile_name*/, GLProfile> glpMap = getProfileMap(device, true); + final GLProfile glp = glpMap.get(profile); + if(null == glp) { + throw new GLException("Profile "+profile+" is not available on "+device+", but: "+glpMap.values()); + } + return glp; + } + + /** 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. + */ + public static GLProfile get(final String profile) + throws GLException + { + return get(defaultDevice, profile); + } + + /** + * Returns the first profile from the given list, + * where an implementation is available. + * + * @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. + * @throws GLException if the non of the requested profiles is available for the device. + */ + public static GLProfile get(final AbstractGraphicsDevice device, final String[] profiles, final boolean favorHardwareRasterizer) + throws GLException + { + GLProfile glProfileAny = null; + + final HashMap<String /*GLProfile_name*/, GLProfile> map = getProfileMap(device, true); + for(int i=0; i<profiles.length; i++) { + final GLProfile glProfile = map.get(profiles[i]); + if(null!=glProfile) { + if(!favorHardwareRasterizer) { + return glProfile; + } + if(glProfile.isHardwareRasterizer()) { + return 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. + * @throws GLException if the non of the requested profiles is available for the default device. + */ + public static GLProfile get(final String[] profiles, final boolean favorHardwareRasterizer) + throws GLException + { + return get(defaultDevice, profiles, favorHardwareRasterizer); + } + + /** Indicates whether the native OpenGL ES1 profile is in use. + * This requires an EGL interface. + */ + public static boolean usesNativeGLES1(final String profileImpl) { + return GLES1 == profileImpl; + } + + /** Indicates whether the native OpenGL ES3 or ES2 profile is in use. + * This requires an EGL, ES3 or ES2 compatible interface. + */ + public static boolean usesNativeGLES2(final String profileImpl) { + return GLES3 == profileImpl || GLES2 == profileImpl; + } + + /** Indicates whether the native OpenGL ES2 profile is in use. + * This requires an EGL, ES3 compatible interface. + */ + public static boolean usesNativeGLES3(final String profileImpl) { + return GLES3 == profileImpl; + } + + /** Indicates whether either of the native OpenGL ES profiles are in use. */ + public static boolean usesNativeGLES(final String profileImpl) { + return usesNativeGLES2(profileImpl) || usesNativeGLES1(profileImpl); + } + + /** @return {@link com.jogamp.nativewindow.NativeWindowFactory#isAWTAvailable()} and + JOGL's AWT part */ + public static boolean isAWTAvailable() { return isAWTAvailable; } + + public static String getGLTypeName(final int type) { + switch (type) { + case GL.GL_UNSIGNED_BYTE: + return "GL_UNSIGNED_BYTE"; + case GL.GL_BYTE: + return "GL_BYTE"; + case GL.GL_UNSIGNED_SHORT: + return "GL_UNSIGNED_SHORT"; + case GL.GL_SHORT: + return "GL_SHORT"; + case GL.GL_FLOAT: + return "GL_FLOAT"; + case GL.GL_FIXED: + return "GL_FIXED"; + case com.jogamp.opengl.GL2ES2.GL_INT: + return "GL_INT"; + case GL.GL_UNSIGNED_INT: + return "GL_UNSIGNED_INT"; + case com.jogamp.opengl.GL2GL3.GL_DOUBLE: + return "GL_DOUBLE"; + case com.jogamp.opengl.GL2.GL_2_BYTES: + return "GL_2_BYTES"; + case com.jogamp.opengl.GL2.GL_3_BYTES: + return "GL_3_BYTES"; + case com.jogamp.opengl.GL2.GL_4_BYTES: + return "GL_4_BYTES"; + } + return null; + } + + public static String getGLArrayName(final int array) { + switch(array) { + case GLPointerFunc.GL_VERTEX_ARRAY: + return "GL_VERTEX_ARRAY"; + case GLPointerFunc.GL_NORMAL_ARRAY: + return "GL_NORMAL_ARRAY"; + case GLPointerFunc.GL_COLOR_ARRAY: + return "GL_COLOR_ARRAY"; + case GLPointerFunc.GL_TEXTURE_COORD_ARRAY: + return "GL_TEXTURE_COORD_ARRAY"; + } + return null; + } + + public final String getGLImplBaseClassName() { + return getGLImplBaseClassName(getImplName()); + } + private static final String getGLImplBaseClassName(final 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(final boolean glObject) { + return getGLCtor(getImplName(), glObject); + } + private static final Constructor<?> getGLCtor(final String profileImpl, final 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(final Object o) { + if(this==o) { return true; } + if(o instanceof GLProfile) { + final GLProfile glp = (GLProfile)o; + return getName() == glp.getName() && getImplName() == glp.getImplName() ; // uses .intern()! + } + 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 + */ + public final void verifyEquality(final GLProfile glp) throws GLException { + if(!this.equals(glp)) { + throw new GLException("GLProfiles are not equal: "+this+" != "+glp); + } + } + + /** return this profiles name */ + public final String getName() { + return profile; + } + + /** return this profiles implementation, eg. GL2ES2 -> GL2, or GL3 -> GL3 */ + 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 + */ + public final String getImplName() { + return null != profileImpl ? profileImpl.getName() : getName(); + } + + /** Indicates whether this profile is capable of GL4bc. <p>Includes [ GL4bc ].</p> */ + public final boolean isGL4bc() { + return GL4bc == profile; + } + + /** Indicates whether this profile is capable of GL4. <p>Includes [ GL4bc, GL4 ].</p> */ + public final boolean isGL4() { + return isGL4bc() || GL4 == profile; + } + + /** Indicates whether this profile is capable of GL3bc. <p>Includes [ GL4bc, GL3bc ].</p> */ + public final boolean isGL3bc() { + return isGL4bc() || GL3bc == profile; + } + + /** Indicates whether this profile is capable of GL3. <p>Includes [ GL4bc, GL4, GL3bc, GL3 ].</p> */ + public final boolean isGL3() { + return isGL4() || isGL3bc() || GL3 == profile; + } + + /** Indicates whether this profile is capable of GL2 . <p>Includes [ GL4bc, GL3bc, GL2 ].</p> */ + public final boolean isGL2() { + return isGL3bc() || GL2 == profile; + } + + /** Indicates whether this profile is capable of GLES1. <p>Includes [ GLES1 ].</p> */ + public final boolean isGLES1() { + return GLES1 == profile; + } + + /** Indicates whether this profile is capable of GLES2. <p>Includes [ GLES2, GLES3 ].</p> */ + public final boolean isGLES2() { + 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> */ + public final boolean isGL2ES1() { + return GL2ES1 == profile || isGLES1() || isGL2(); + } + + /** 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 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 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() ; + } + + /** Indicates whether this profile uses the native OpenGL ES1 implementations. */ + public final boolean usesNativeGLES1() { + return GLES1 == getImplName(); + } + + /** Indicates whether this profile uses the native OpenGL ES2 implementations. */ + public final boolean usesNativeGLES2() { + 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 usesNativeGLES3() || usesNativeGLES2() || usesNativeGLES1(); + } + + /** + * General validation if type is a valid GL data type + * for the current profile + */ + public boolean isValidDataType(final int type, final boolean throwException) { + switch(type) { + case GL.GL_UNSIGNED_BYTE: + case GL.GL_BYTE: + case GL.GL_UNSIGNED_SHORT: + case GL.GL_SHORT: + case GL.GL_FLOAT: + case GL.GL_FIXED: + return true; + case com.jogamp.opengl.GL2ES2.GL_INT: + case GL.GL_UNSIGNED_INT: + if( isGL2ES2() ) { + return true; + } + case com.jogamp.opengl.GL2GL3.GL_DOUBLE: + if( isGL3() ) { + return true; + } + case com.jogamp.opengl.GL2.GL_2_BYTES: + case com.jogamp.opengl.GL2.GL_3_BYTES: + case com.jogamp.opengl.GL2.GL_4_BYTES: + if( isGL2() ) { + return true; + } + } + if(throwException) { + throw new GLException("Illegal data type on profile "+this+": "+type); + } + return false; + } + + public boolean isValidArrayDataType(final int index, final int comps, final int type, + final boolean isVertexAttribPointer, final boolean throwException) { + final String arrayName = getGLArrayName(index); + if( isGLES1() ) { + if(isVertexAttribPointer) { + if(throwException) { + throw new GLException("Illegal array type for "+arrayName+" on profile GLES1: VertexAttribPointer"); + } + return false; + } + switch(index) { + case GLPointerFunc.GL_VERTEX_ARRAY: + case GLPointerFunc.GL_TEXTURE_COORD_ARRAY: + switch(type) { + case GL.GL_BYTE: + case GL.GL_SHORT: + case GL.GL_FIXED: + case GL.GL_FLOAT: + break; + default: + if(throwException) { + throw new GLException("Illegal data type for "+arrayName+" on profile GLES1: "+type); + } + return false; + } + switch(comps) { + case 0: + case 2: + case 3: + case 4: + break; + default: + if(throwException) { + throw new GLException("Illegal component number for "+arrayName+" on profile GLES1: "+comps); + } + return false; + } + break; + case GLPointerFunc.GL_NORMAL_ARRAY: + switch(type) { + case GL.GL_BYTE: + case GL.GL_SHORT: + case GL.GL_FIXED: + case GL.GL_FLOAT: + break; + default: + if(throwException) { + throw new GLException("Illegal data type for "+arrayName+" on profile GLES1: "+type); + } + return false; + } + switch(comps) { + case 0: + case 3: + break; + default: + if(throwException) { + throw new GLException("Illegal component number for "+arrayName+" on profile GLES1: "+comps); + } + return false; + } + break; + case GLPointerFunc.GL_COLOR_ARRAY: + switch(type) { + case GL.GL_UNSIGNED_BYTE: + case GL.GL_FIXED: + case GL.GL_FLOAT: + break; + default: + if(throwException) { + throw new GLException("Illegal data type for "+arrayName+" on profile GLES1: "+type); + } + return false; + } + switch(comps) { + case 0: + case 4: + break; + default: + if(throwException) { + throw new GLException("Illegal component number for "+arrayName+" on profile GLES1: "+comps); + } + return false; + } + break; + } + } else if( isGLES2() ) { + // simply ignore !isVertexAttribPointer case, since it is simulated anyway .. + switch(type) { + case GL.GL_UNSIGNED_BYTE: + case GL.GL_BYTE: + case GL.GL_UNSIGNED_SHORT: + case GL.GL_SHORT: + case GL.GL_FLOAT: + case GL.GL_FIXED: + break; + default: + if(throwException) { + throw new GLException("Illegal data type for "+arrayName+" on profile GLES2: "+type); + } + return false; + } + /** unable to validate .. could be any valid type/component combination + switch(comps) { + case 0: + case 1: + case 2: + case 3: + case 4: + break; + default: + if(throwException) { + throw new GLException("Illegal component number for "+arrayName+" on profile GLES2: "+comps); + } + return false; + } */ + } else if( isGL2ES2() ) { + if(isVertexAttribPointer) { + switch(type) { + case GL.GL_UNSIGNED_BYTE: + case GL.GL_BYTE: + case GL.GL_UNSIGNED_SHORT: + case GL.GL_SHORT: + case GL.GL_FLOAT: + case com.jogamp.opengl.GL2ES2.GL_INT: + case GL.GL_UNSIGNED_INT: + case com.jogamp.opengl.GL2GL3.GL_DOUBLE: + break; + default: + if(throwException) { + throw new GLException("Illegal data type for "+arrayName+" on profile GL2: "+type); + } + return false; + } + switch(comps) { + case 0: + case 1: + case 2: + case 3: + case 4: + break; + default: + if(throwException) { + throw new GLException("Illegal component number for "+arrayName+" on profile GL2: "+comps); + } + return false; + } + } else { + switch(index) { + case GLPointerFunc.GL_VERTEX_ARRAY: + switch(type) { + case GL.GL_SHORT: + case GL.GL_FLOAT: + case com.jogamp.opengl.GL2ES2.GL_INT: + case com.jogamp.opengl.GL2GL3.GL_DOUBLE: + break; + default: + if(throwException) { + throw new GLException("Illegal data type for "+arrayName+" on profile GL2: "+type); + } + return false; + } + switch(comps) { + case 0: + case 2: + case 3: + case 4: + break; + default: + if(throwException) { + throw new GLException("Illegal component number for "+arrayName+" on profile GL2: "+comps); + } + return false; + } + break; + case GLPointerFunc.GL_NORMAL_ARRAY: + switch(type) { + case GL.GL_BYTE: + case GL.GL_SHORT: + case GL.GL_FLOAT: + case com.jogamp.opengl.GL2ES2.GL_INT: + case com.jogamp.opengl.GL2GL3.GL_DOUBLE: + break; + default: + if(throwException) { + throw new GLException("Illegal data type for "+arrayName+" on profile GL2: "+type); + } + return false; + } + switch(comps) { + case 0: + case 3: + break; + default: + if(throwException) { + throw new GLException("Illegal component number for "+arrayName+" on profile GLES1: "+comps); + } + return false; + } + break; + case GLPointerFunc.GL_COLOR_ARRAY: + switch(type) { + case GL.GL_UNSIGNED_BYTE: + case GL.GL_BYTE: + case GL.GL_UNSIGNED_SHORT: + case GL.GL_SHORT: + case GL.GL_FLOAT: + case com.jogamp.opengl.GL2ES2.GL_INT: + case GL.GL_UNSIGNED_INT: + case com.jogamp.opengl.GL2GL3.GL_DOUBLE: + break; + default: + if(throwException) { + throw new GLException("Illegal data type for "+arrayName+" on profile GL2: "+type); + } + return false; + } + switch(comps) { + case 0: + case 3: + case 4: + break; + default: + if(throwException) { + throw new GLException("Illegal component number for "+arrayName+" on profile GL2: "+comps); + } + return false; + } + break; + case GLPointerFunc.GL_TEXTURE_COORD_ARRAY: + switch(type) { + case GL.GL_SHORT: + case GL.GL_FLOAT: + case com.jogamp.opengl.GL2ES2.GL_INT: + case com.jogamp.opengl.GL2GL3.GL_DOUBLE: + break; + default: + if(throwException) { + throw new GLException("Illegal data type for "+arrayName+" on profile GL2: "+type); + } + return false; + } + switch(comps) { + case 0: + case 1: + case 2: + case 3: + case 4: + break; + default: + if(throwException) { + throw new GLException("Illegal component number for "+arrayName+" on profile GL2: "+comps); + } + return false; + } + break; + } + } + } + return true; + } + + @Override + public String toString() { + return "GLProfile[" + getName() + "/" + getImplName() + "."+(this.isHardwareRasterizer?"hw":"sw")+(isCustom?".custom":"")+"]"; + } + + private static /*final*/ boolean isAWTAvailable; + + private static /*final*/ boolean hasDesktopGLFactory; + private static /*final*/ boolean hasGL234Impl; + private static /*final*/ boolean hasEGLFactory; + 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 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, false, cl); + } catch (final Throwable t) { + if( DEBUG ) { + System.err.println("Caught: "+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() { + NativeWindowFactory.initSingleton(); + if(DEBUG) { + 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()); + } + + final ClassLoader classloader = GLProfile.class.getClassLoader(); + + isAWTAvailable = NativeWindowFactory.isAWTAvailable() && + ReflectionUtil.isClassAvailable("com.jogamp.opengl.awt.GLCanvas", classloader) ; // JOGL + + initGLCtorImpl(); + + // + // Iteration of desktop GL availability detection + // utilizing the detected GL version in the shared context. + // + // - Instantiate GLDrawableFactory incl its shared dummy drawable/context, + // which will register at GLContext .. + // + GLDrawableFactory.initSingleton(); + + Throwable t=null; + // if successfull it has a shared dummy drawable and context created + try { + desktopFactory = (GLDrawableFactoryImpl) GLDrawableFactory.getFactoryImpl(GL2); + if(null != desktopFactory) { + final DesktopGLDynamicLookupHelper glLookupHelper = (DesktopGLDynamicLookupHelper) desktopFactory.getGLDynamicLookupHelper(GL2); + if(null!=glLookupHelper) { + hasDesktopGLFactory = glLookupHelper.isLibComplete() && hasGL234Impl; + } + } + } catch (final LinkageError le) { + t=le; + } catch (final RuntimeException re) { + t=re; + } catch (final Throwable tt) { + t=tt; + } + if(DEBUG) { + if(null!=t) { + t.printStackTrace(); + } + } + + final AbstractGraphicsDevice defaultDesktopDevice; + if(null == desktopFactory) { + hasDesktopGLFactory = false; + hasGL234Impl = false; + defaultDesktopDevice = null; + if(DEBUG) { + System.err.println("Info: GLProfile.init - Desktop GLDrawable factory not available"); + } + } else { + defaultDesktopDevice = desktopFactory.getDefaultDevice(); + } + + if ( ReflectionUtil.isClassAvailable("jogamp.opengl.egl.EGLDrawableFactory", classloader) ) { + t=null; + try { + eglFactory = (GLDrawableFactoryImpl) GLDrawableFactory.getFactoryImpl(GLES2); + if(null != eglFactory) { + hasEGLFactory = true; + // update hasGLES1Impl, hasGLES3Impl based on EGL + hasGLES3Impl = null!=eglFactory.getGLDynamicLookupHelper(GLES2) && hasGLES3Impl; + hasGLES1Impl = null!=eglFactory.getGLDynamicLookupHelper(GLES1) && hasGLES1Impl; + } + } catch (final LinkageError le) { + t=le; + } catch (final SecurityException se) { + t=se; + } catch (final NullPointerException npe) { + t=npe; + } catch (final RuntimeException re) { + t=re; + } + if(DEBUG) { + if(null!=t) { + t.printStackTrace(); + } + } + } + + final AbstractGraphicsDevice defaultEGLDevice; + if(null == eglFactory) { + hasGLES3Impl = false; + hasGLES1Impl = false; + defaultEGLDevice = null; + if(DEBUG) { + System.err.println("Info: GLProfile.init - EGL GLDrawable factory not available"); + } + } else { + defaultEGLDevice = eglFactory.getDefaultDevice(); + } + + 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; + } + + // 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 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)); + } + } + + /** + * @param device the device for which profiles shall be initialized + * @return true if any profile for the device exists, otherwise false + */ + private static boolean initProfilesForDevice(final AbstractGraphicsDevice device) { + if(null == device) { + return false; + } + initLock.lock(); + try { + final GLDrawableFactory factory = GLDrawableFactory.getFactoryImpl(device); + factory.enterThreadCriticalZone(); + try { + return initProfilesForDeviceCritical(device); + } finally { + factory.leaveThreadCriticalZone(); + } + } finally { + initLock.unlock(); + } + } + private static boolean initProfilesForDeviceCritical(final AbstractGraphicsDevice device) { + final boolean isSet = GLContext.getAvailableGLVersionsSet(device); + + if(DEBUG) { + 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! + final String deviceKey = device.getUniqueID(); + final HashMap<String /*GLProfile_name*/, GLProfile> map = deviceConn2ProfileMap.get(deviceKey); + if( null == map ) { + throw new InternalError("GLContext Avail. GLVersion is set - but no profile map for device: "+device); + } + return null != map.get(GL_DEFAULT); + } + + 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 */); + + // Triggers eager initialization of share context in GLDrawableFactory for the device, + // hence querying all available GLProfiles + final Thread sharedResourceThread = desktopFactory.getSharedResourceThread(); + if(null != sharedResourceThread) { + initLock.addOwner(sharedResourceThread); + } + final boolean desktopSharedCtxAvail = desktopFactory.createSharedResource(device); + if(null != sharedResourceThread) { + initLock.removeOwner(sharedResourceThread); + } + if (DEBUG) { + System.err.println("GLProfile.initProfilesForDevice: "+device+": desktop Shared Ctx "+desktopSharedCtxAvail); + } + if(!desktopSharedCtxAvail) { + hasDesktopGLFactory = false; + } else if( !GLContext.getAvailableGLVersionsSet(device) ) { + throw new InternalError("Available GLVersions not set"); + } + addedDesktopProfile = computeProfileMap(device, false /* desktopCtxUndef*/, false /* esCtxUndef */); + } + + final boolean deviceIsEGLCompatible = hasEGLFactory && eglFactory.getIsDeviceCompatible(device); + + // 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, true /* desktopCtxUndef*/, true /* esCtxUndef */); + + // Triggers eager initialization of share context in GLDrawableFactory for the device, + // hence querying all available GLProfiles + final Thread sharedResourceThread = eglFactory.getSharedResourceThread(); + if(null != sharedResourceThread) { + initLock.addOwner(sharedResourceThread); + } + final boolean eglSharedCtxAvail = eglFactory.createSharedResource(device); + if(null != sharedResourceThread) { + initLock.removeOwner(sharedResourceThread); + } + 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) + // fails in some scenarios (eg VirtualBox 4.1.6) w/ EGL error 0x3001 (EGL_NOT_INITIALIZED). + hasEGLFactory = false; + hasGLES3Impl = false; + hasGLES1Impl = false; + } + if (DEBUG) { + System.err.println("GLProfile.initProfilesForDevice: "+device+": egl Shared Ctx "+eglSharedCtxAvail); + } + addedEGLProfile = computeProfileMap(device, false /* desktopCtxUndef*/, false /* esCtxUndef */); + } + + if( !addedDesktopProfile && !addedEGLProfile ) { + setProfileMap(device, new HashMap<String /*GLProfile_name*/, GLProfile>()); // empty + if(DEBUG) { + System.err.println("GLProfile: device could not be initialized: "+device); + System.err.println("GLProfile: compatible w/ desktop: "+deviceIsDesktopCompatible+ + ", egl "+deviceIsEGLCompatible); + System.err.println("GLProfile: desktoplFactory "+desktopFactory); + System.err.println("GLProfile: eglFactory "+eglFactory); + System.err.println("GLProfile: hasGLES1Impl "+hasGLES1Impl); + System.err.println("GLProfile: hasGLES3Impl "+hasGLES3Impl); + } + } + + GLContext.setAvailableGLVersionsSet(device, true); + + if (DEBUG) { + System.err.println("GLProfile.initProfilesForDevice: "+device.getConnection()+": added profile(s): desktop "+addedDesktopProfile+", egl "+addedEGLProfile); + System.err.println("GLProfile.initProfilesForDevice: "+device.getConnection()+": "+glAvailabilityToString(device)); + if(addedDesktopProfile) { + dumpGLInfo(desktopFactory, device); + final List<GLCapabilitiesImmutable> availCaps = desktopFactory.getAvailableCapabilities(device); + for(int i=0; i<availCaps.size(); i++) { + System.err.println(availCaps.get(i)); + } + } else if(addedEGLProfile) { + dumpGLInfo(eglFactory, device); + final List<GLCapabilitiesImmutable> availCaps = eglFactory.getAvailableCapabilities(device); + for(int i=0; i<availCaps.size(); i++) { + System.err.println(availCaps.get(i)); + } + } + } + + return addedDesktopProfile || addedEGLProfile; + } + + private static void dumpGLInfo(final GLDrawableFactoryImpl factory, final AbstractGraphicsDevice device) { + final GLContext ctx = factory.getOrCreateSharedContext(device); + if(null != ctx) { + System.err.println("GLProfile.dumpGLInfo: "+ctx); + ctx.makeCurrent(); + try { + System.err.println(JoglVersion.getGLInfo(ctx.getGL(), null)); + } finally { + ctx.release(); + } + } else { + System.err.println("GLProfile.dumpGLInfo: shared context n/a"); + } + } + + public static AbstractGraphicsDevice getDefaultDevice() { + initSingleton(); + return defaultDevice; + } + + private static String array2String(final String[] list) { + final StringBuilder msg = new StringBuilder(); + msg.append("["); + for (int i = 0; i < list.length; i++) { + if (i > 0) + msg.append(", "); + msg.append(list[i]); + } + msg.append("]"); + return msg.toString(); + } + + private static void glAvailabilityToString(final AbstractGraphicsDevice device, final StringBuilder sb, final int major, final int profile) { + final String str = GLContext.getAvailableGLVersionAsString(device, major, profile); + if(null==str) { + throw new GLException("Internal Error"); + } + sb.append("["); + sb.append(str); + sb.append("]"); + } + + private static boolean computeProfileMap(final AbstractGraphicsDevice device, final boolean desktopCtxUndef, final boolean esCtxUndef) { + if (DEBUG) { + System.err.println("GLProfile.init map "+device.getConnection()+", desktopCtxUndef "+desktopCtxUndef+", esCtxUndef "+esCtxUndef); + } + final boolean isHardwareRasterizer[] = new boolean[1]; + GLProfile defaultGLProfileAny = null; + GLProfile defaultGLProfileHW = null; + 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++) { + 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 ) ) { + glProfile = new GLProfile(profile, null, isHardwareRasterizer[0], false /* custom */); + } else { + final GLProfile _mglp = _mappedProfiles.get( profileImpl ); + if( null == _mglp ) { + throw new InternalError("XXX0 profile["+i+"]: "+profile+" -> profileImpl "+profileImpl+" !!! not mapped "); + } + glProfile = new GLProfile(profile, _mglp, isHardwareRasterizer[0], false /* custom */); + } + _mappedProfiles.put(profile, glProfile); + if (DEBUG) { + System.err.println("GLProfile.init map "+glProfile+" on device "+device.getConnection()); + } + 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 ) { + defaultGLProfileAny=glProfile; + if (DEBUG) { + System.err.println("GLProfile.init map defaultAny "+glProfile+" on device "+device.getConnection()); + } + } + } else { + if (DEBUG) { + System.err.println("GLProfile.init map *** no mapping for "+profile+" on device "+device.getConnection()); + } + } + } + if( null != defaultGLProfileHW ) { + _mappedProfiles.put(GL_DEFAULT, defaultGLProfileHW); + } else if( null != defaultGLProfileAny ) { + _mappedProfiles.put(GL_DEFAULT, defaultGLProfileAny); + } + setProfileMap(device, _mappedProfiles); + return _mappedProfiles.size() > 0; + } + + /** + * Returns the profile implementation + */ + private static String computeProfileImpl(final AbstractGraphicsDevice device, final String profile, final boolean desktopCtxUndef, final boolean esCtxUndef, final boolean isHardwareRasterizer[]) { + final boolean hardwareRasterizer[] = new boolean[1]; + if ( GL2ES1 == profile ) { + final boolean gles1Available; + final boolean gles1HWAvailable; + if( hasGLES1Impl ) { + gles1Available = esCtxUndef || GLContext.isGLES1Available(device, hardwareRasterizer); + gles1HWAvailable = gles1Available && hardwareRasterizer[0] ; + } else { + gles1Available = false; + gles1HWAvailable = false; + } + if(hasGL234Impl) { + final boolean gl3bcAvailable = GLContext.isGL3bcAvailable(device, hardwareRasterizer); + final boolean gl3bcHWAvailable = gl3bcAvailable && hardwareRasterizer[0] ; + final boolean gl2Available = GLContext.isGL2Available(device, hardwareRasterizer); + final boolean gl2HWAvailable = gl2Available && hardwareRasterizer[0] ; + final boolean glAnyHWAvailable = gl3bcHWAvailable || gl2HWAvailable || + gles1HWAvailable ; + + if( GLContext.isGL4bcAvailable(device, isHardwareRasterizer) && + ( isHardwareRasterizer[0] || !glAnyHWAvailable ) ) { + return GL4bc; + } + if( gl3bcAvailable && ( gl3bcHWAvailable || !glAnyHWAvailable ) ) { + isHardwareRasterizer[0] = gl3bcHWAvailable; + return GL3bc; + } + if( ( desktopCtxUndef || gl2Available ) && ( gl2HWAvailable || !glAnyHWAvailable ) ) { + isHardwareRasterizer[0] = gl2HWAvailable; + return GL2; + } + } + if( gles1Available ) { + isHardwareRasterizer[0] = gles1HWAvailable; + return GLES1; + } + } else if ( GL2ES2 == profile ) { + final boolean gles2Available, gles3Available; + final boolean gles2HWAvailable, gles3HWAvailable; + if( hasGLES3Impl ) { + gles2Available = esCtxUndef || GLContext.isGLES2Available(device, hardwareRasterizer); + gles2HWAvailable = gles2Available && hardwareRasterizer[0] ; + gles3Available = esCtxUndef || GLContext.isGLES3Available(device, hardwareRasterizer); + gles3HWAvailable = gles3Available && hardwareRasterizer[0] ; + } else { + gles2Available = false; + gles2HWAvailable = false; + gles3Available = false; + gles3HWAvailable = false; + } + if( hasGL234Impl ) { + final boolean gl4bcAvailable = GLContext.isGL4bcAvailable(device, hardwareRasterizer); + final boolean gl4bcHWAvailable = gl4bcAvailable && hardwareRasterizer[0] ; + final boolean gl3Available = GLContext.isGL3Available(device, hardwareRasterizer); + final boolean gl3HWAvailable = gl3Available && hardwareRasterizer[0] ; + final boolean gl3bcAvailable = GLContext.isGL3bcAvailable(device, hardwareRasterizer); + final boolean gl3bcHWAvailable = gl3bcAvailable && hardwareRasterizer[0] ; + final boolean gl2Available = GLContext.isGL2Available(device, hardwareRasterizer); + final boolean gl2HWAvailable = gl2Available && hardwareRasterizer[0] ; + final boolean glAnyHWAvailable = gl4bcHWAvailable || gl3HWAvailable || gl3bcHWAvailable || gl2HWAvailable || + gles3HWAvailable || gles2HWAvailable ; + + if( GLContext.isGL4Available(device, isHardwareRasterizer) && + ( isHardwareRasterizer[0] || !glAnyHWAvailable ) ) { + return GL4; + } + if( gl4bcAvailable && ( gl4bcHWAvailable || !glAnyHWAvailable ) ) { + isHardwareRasterizer[0] = gl4bcHWAvailable; + return GL4bc; + } + if( gl3Available && ( gl3HWAvailable || !glAnyHWAvailable ) ) { + isHardwareRasterizer[0] = gl3HWAvailable; + return GL3; + } + if( gl3bcAvailable && ( gl3bcHWAvailable || !glAnyHWAvailable ) ) { + isHardwareRasterizer[0] = gl3bcHWAvailable; + return GL3bc; + } + if( ( desktopCtxUndef || gl2Available ) && ( gl2HWAvailable || !glAnyHWAvailable ) ) { + isHardwareRasterizer[0] = gl2HWAvailable; + return GL2; + } + } + if( gles3Available && ( gles3HWAvailable || !gles2HWAvailable ) ) { + isHardwareRasterizer[0] = gles3HWAvailable; + return GLES3; + } + if( gles2Available ) { + isHardwareRasterizer[0] = gles2HWAvailable; + return GLES2; + } + } else if (GL4ES3 == 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 ) { + final boolean gl4bcAvailable = GLContext.isGL4bcAvailable(device, hardwareRasterizer); + final boolean gl4bcHWAvailable = gl4bcAvailable && hardwareRasterizer[0] ; + final boolean glAnyHWAvailable = gl4bcHWAvailable || + gles3HWAvailable; + + if( GLContext.isGL4Available(device, isHardwareRasterizer) && + ( isHardwareRasterizer[0] || !glAnyHWAvailable ) ) { + return GL4; + } + if( ( desktopCtxUndef || gl4bcAvailable ) && ( gl4bcHWAvailable || !glAnyHWAvailable ) ) { + isHardwareRasterizer[0] = gl4bcHWAvailable; + return GL4bc; + } + } + if(gles3Available) { + isHardwareRasterizer[0] = es3HardwareRasterizer[0]; + return GLES3; + } + } + } else if(GL2GL3 == profile) { + if(hasGL234Impl) { + final boolean gl4Available = GLContext.isGL4Available(device, hardwareRasterizer); + final boolean gl4HWAvailable = gl4Available && hardwareRasterizer[0] ; + final boolean gl3Available = GLContext.isGL3Available(device, hardwareRasterizer); + final boolean gl3HWAvailable = gl3Available && hardwareRasterizer[0] ; + final boolean gl3bcAvailable = GLContext.isGL3bcAvailable(device, hardwareRasterizer); + final boolean gl3bcHWAvailable = gl3bcAvailable && hardwareRasterizer[0] ; + final boolean gl2Available = GLContext.isGL2Available(device, hardwareRasterizer); + final boolean gl2HWAvailable = gl2Available && hardwareRasterizer[0] ; + final boolean glAnyHWAvailable = gl4HWAvailable || gl3HWAvailable || gl3bcHWAvailable || gl2HWAvailable; + + if( GLContext.isGL4bcAvailable(device, isHardwareRasterizer) && + ( isHardwareRasterizer[0] || !glAnyHWAvailable ) ) { + return GL4bc; + } + if( gl4Available && ( gl4HWAvailable || !glAnyHWAvailable ) ) { + isHardwareRasterizer[0] = gl4HWAvailable; + return GL4; + } + if( gl3bcAvailable && ( gl3bcHWAvailable || !glAnyHWAvailable ) ) { + isHardwareRasterizer[0] = gl3bcHWAvailable; + return GL3bc; + } + if( gl3Available && ( gl3HWAvailable || !glAnyHWAvailable ) ) { + isHardwareRasterizer[0] = gl3HWAvailable; + return GL3; + } + if( desktopCtxUndef || gl2Available ) { + isHardwareRasterizer[0] = gl2HWAvailable; + return GL2; + } + } + } else if(GL4bc == profile && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL4bcAvailable(device, isHardwareRasterizer))) { + return desktopCtxUndef ? GL4bc : GLContext.getAvailableGLProfileName(device, 4, GLContext.CTX_PROFILE_COMPAT); + } else if(GL4 == profile && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL4Available(device, isHardwareRasterizer))) { + return desktopCtxUndef ? GL4 : GLContext.getAvailableGLProfileName(device, 4, GLContext.CTX_PROFILE_CORE); + } else if(GL3bc == profile && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL3bcAvailable(device, isHardwareRasterizer))) { + return desktopCtxUndef ? GL3bc : GLContext.getAvailableGLProfileName(device, 3, GLContext.CTX_PROFILE_COMPAT); + } else if(GL3 == profile && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL3Available(device, isHardwareRasterizer))) { + return desktopCtxUndef ? GL3 : GLContext.getAvailableGLProfileName(device, 3, GLContext.CTX_PROFILE_CORE); + } else if(GL2 == profile && hasGL234Impl && ( desktopCtxUndef || GLContext.isGL2Available(device, isHardwareRasterizer))) { + return desktopCtxUndef ? GL2 : GLContext.getAvailableGLProfileName(device, 2, GLContext.CTX_PROFILE_COMPAT); + } else if(GLES3 == profile && hasGLES3Impl && ( esCtxUndef || GLContext.isGLES3Available(device, isHardwareRasterizer))) { + return esCtxUndef ? GLES3 : GLContext.getAvailableGLProfileName(device, 3, GLContext.CTX_PROFILE_ES); + } else if(GLES2 == profile && hasGLES3Impl && ( esCtxUndef || GLContext.isGLES2Available(device, isHardwareRasterizer))) { + return esCtxUndef ? GLES2 : GLContext.getAvailableGLProfileName(device, 2, GLContext.CTX_PROFILE_ES); + } else if(GLES1 == profile && hasGLES1Impl && ( esCtxUndef || GLContext.isGLES1Available(device, isHardwareRasterizer))) { + return esCtxUndef ? GLES1 : GLContext.getAvailableGLProfileName(device, 1, GLContext.CTX_PROFILE_ES); + } + return null; + } + + private static /*final*/ HashMap<String /*device_connection*/, HashMap<String /*GLProfile_name*/, GLProfile>> deviceConn2ProfileMap = + new HashMap<String /*device_connection*/, HashMap<String /*GLProfile_name*/, GLProfile>>(); + + /** + * This implementation support lazy initialization, while avoiding recursion/deadlocks.<br> + * If no mapping 'device -> GLProfiles-Map' exists yet, it triggers<br> + * - create empty mapping device -> GLProfiles-Map <br> + * - initialization<br< + * + * @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 + * @throws GLException if no profile for the given device is available. + */ + private static HashMap<String /*GLProfile_name*/, GLProfile> getProfileMap(AbstractGraphicsDevice device, final 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 ) { + return map; + } + if( !initProfilesForDevice(device) ) { + if( throwExceptionOnZeroProfile ) { + throw new GLException("No Profile available for "+device); + } else { + return null; + } + } + map = deviceConn2ProfileMap.get(deviceKey); + if( null == map && throwExceptionOnZeroProfile ) { + throw new InternalError("initProfilesForDevice(..) didn't setProfileMap(..) for "+device); + } + return map; + } + + private static void setProfileMap(final AbstractGraphicsDevice device, final HashMap<String /*GLProfile_name*/, GLProfile> mappedProfiles) { + synchronized ( deviceConn2ProfileMap ) { + deviceConn2ProfileMap.put(device.getUniqueID(), mappedProfiles); + } + } + + private GLProfile(final String profile, final GLProfile profileImpl, final boolean isHardwareRasterizer, final boolean isCustom) { + this.profile = profile; + this.profileImpl = profileImpl; + this.isHardwareRasterizer = isHardwareRasterizer; + this.isCustom = isCustom; + } + + public static GLProfile createCustomGLProfile(final String profile, final GLProfile profileImpl) { + return new GLProfile(profile, profileImpl, profileImpl.isHardwareRasterizer, true); + } + + private final GLProfile profileImpl; + private final String profile; + private final boolean isHardwareRasterizer; + private final boolean isCustom; +} diff --git a/src/jogl/classes/com/jogamp/opengl/GLRunnable.java b/src/jogl/classes/com/jogamp/opengl/GLRunnable.java new file mode 100644 index 000000000..97a72d6cd --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLRunnable.java @@ -0,0 +1,58 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl; + +/** + * <p> + * Declares a one-shot OpenGL command usable for injection + * via {@link GLAutoDrawable#invoke(boolean, com.jogamp.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)} + * methods has been called. + * </p> + * <p> + * The OpenGL context is current while executing the GLRunnable. + * </p> + * <p> + * This might be useful to inject OpenGL commands from an I/O event listener. + * </p> + */ +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. + * @see GLRunnable + */ + boolean run(GLAutoDrawable drawable); +} + diff --git a/src/jogl/classes/com/jogamp/opengl/GLRunnable2.java b/src/jogl/classes/com/jogamp/opengl/GLRunnable2.java new file mode 100644 index 000000000..4b0c63da0 --- /dev/null +++ b/src/jogl/classes/com/jogamp/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 com.jogamp.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/com/jogamp/opengl/GLSharedContextSetter.java b/src/jogl/classes/com/jogamp/opengl/GLSharedContextSetter.java new file mode 100644 index 000000000..852ebcaa7 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLSharedContextSetter.java @@ -0,0 +1,181 @@ +/** + * Copyright 2013 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl; + +import com.jogamp.opengl.GLRendererQuirks; + +/** + * 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. + * Subsequent shared {@link GLContext} w/ the <i>master</i> are referred as <i>slave</i> {@link GLContext}. + * </p> + * <p> + * Implementations of this interface control the <i>slave's</i> {@link GLContext} and {@link GLAutoDrawable} realization, + * i.e. the <i>slave</i> {@link GLAutoDrawable} will not be realized before their associated <i>master</i>. + * </p> + * <p> + * Using the nearest or same {@link GLCapabilitiesImmutable#getVisualID(com.jogamp.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> + * In general, destroying a <i>master</i> {@link GLContext} before their shared <i>slaves</i> + * shall be permissible, i.e. the OpenGL driver needs to handle pending destruction of shared resources. + * This is confirmed to work properly on most platform/driver combinations, + * see unit test <code>com.jogamp.opengl.test.junit.jogl.acore.TestSharedContextVBOES2NEWT3</code> and similar. + * </p> + * <p> + * However, to avoid scenarios with buggy drivers, users <i>may not</i> destroy the + * <i>master</i> {@link GLContext} before its shared <i>slave</i> {@link GLContext} instances + * <i>as long as they are using them</i>.<br> + * Otherwise the OpenGL driver may crash w/ SIGSEGV, due to using already destroyed shared resources, + * if not handling the pending destruction of the latter!<br> + * Either proper lifecycle synchronization is implemented, e.g. by notifying the <i>slaves</i> about the loss of the shared resources, + * <i>or</i> the <i>slaves</i> validate whether the resources are still valid. + * </p> + * <p> + * To simplify above lifecycle issues, one may use a {@link GLDrawableFactory#createDummyDrawable(com.jogamp.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="synchronization">GL Object Synchronization</a></h5> + * <p> + * Usually synchronization of shared GL objects should not be required, if the shared GL objects + * are created and immutable before concurrent usage. + * </p> + * <p> + * However, using drivers exposing {@link GLRendererQuirks#NeedSharedObjectSync} always + * require the user to synchronize access of shared GL objects. + * </p> + * <p> + * Synchronization can be avoided if accessing the shared GL objects + * exclusively via a queue or {@link com.jogamp.common.util.Ringbuffer Ringbuffer}, see GLMediaPlayerImpl as an example. + * </p> + * </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 { + /** + * 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> + * The <i>preferred method</i> of assigning a <i>shared context</i> is + * to {@link #setSharedAutoDrawable(GLAutoDrawable) set the shared GLAutoDrawable}, + * since this method also takes the {@link GLEventListener} + * {@link GLAutoDrawable#areAllGLEventListenerInitialized() initialization into account}. + * </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. <a href="GLAutoDrawable.html#initialization">initialization</a> + * 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 GLAutoDrawable#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/com/jogamp/opengl/GLUniformData.java b/src/jogl/classes/com/jogamp/opengl/GLUniformData.java new file mode 100644 index 000000000..44f7f29c7 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/GLUniformData.java @@ -0,0 +1,227 @@ + +package com.jogamp.opengl; + +import java.nio.*; + +import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.math.FloatUtil; + +public class GLUniformData { + + /** + * int atom + * + * Number of objects is 1 + * + */ + public GLUniformData(final String name, final int val) { + initScalar(name, 1, Integer.valueOf(val)); + } + + /** + * float atom + * + * Number of objects is 1 + * + */ + public GLUniformData(final String name, final float val) { + initScalar(name, 1, Float.valueOf(val)); + } + + /** + * Multiple IntBuffer Vector + * + * Number of objects is calculated by data.limit()/components + * + * @param components number of elements of one object, ie 4 for GL_FLOAT_VEC4, + */ + public GLUniformData(final String name, final int components, final IntBuffer data) { + initBuffer(name, components, data); + } + + /** + * Multiple FloatBuffer Vector + * + * Number of objects is calculated by data.limit()/components + * + * @param components number of elements of one object, ie 4 for GL_FLOAT_VEC4, + */ + public GLUniformData(final String name, final int components, final FloatBuffer data) { + initBuffer(name, components, data); + } + + private GLUniformData(final int components, final String name) { + initBuffer(name, components, null); + } + + public static GLUniformData creatEmptyVector(final String name, final int components) { + return new GLUniformData(components, name); + } + + public static GLUniformData creatEmptyMatrix(final String name, final int rows, final int columns) { + return new GLUniformData(name, rows, columns, null); + } + + /** + * Multiple FloatBuffer Matrix + * + * Number of objects is calculated by data.limit()/(rows*columns) + * + * @param rows the matrix rows + * @param column the matrix column + */ + public GLUniformData(final String name, final int rows, final int columns, final FloatBuffer data) { + initBuffer(name, rows, columns, data); + } + + public GLUniformData setData(final int data) { initScalar(Integer.valueOf(data)); return this; } + public GLUniformData setData(final float data) { initScalar(Float.valueOf(data)); return this; } + public GLUniformData setData(final IntBuffer data) { initBuffer(data); return this; } + public GLUniformData setData(final FloatBuffer data) { initBuffer(data); return this; } + + public int intValue() { return ((Integer)data).intValue(); }; + public float floatValue() { return ((Float)data).floatValue(); }; + 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 toString(null).toString(); + } + + private void initBuffer(final String name, final int rows, final int columns, final Buffer buffer) { + if( 2>rows || rows>4 || 2>columns || columns>4 ) { + throw new GLException("rowsXcolumns must be within [2..4]X[2..4], is: "+rows+"X"+columns); + } + this.name=name; + this.rows=rows; + this.columns=columns; + this.isMatrix=true; + this.location=-1; + initBuffer(buffer); + } + private void initScalar(final String name, final int components, final Object data) { + if( 1>components || components>4 ) { + throw new GLException("components must be within [1..4], is: "+components); + } + this.name=name; + this.columns=components; + this.rows=1; + this.isMatrix=false; + this.location=-1; + initScalar(data); + } + private void initBuffer(final String name, final int components, final Buffer buffer) { + if( 1>components || components>4 ) { + throw new GLException("components must be within [1..4], is: "+components); + } + this.name=name; + this.columns=components; + this.rows=1; + this.isMatrix=false; + this.location=-1; + initBuffer(buffer); + } + + private void initScalar(final Object data) { + if(data instanceof Buffer) { + initBuffer((Buffer)data); + } else if( null != data ) { + if(isMatrix) { + throw new GLException("Atom type not allowed for matrix : "+this); + } + this.count=1; + this.data=data; + } else { + this.count=0; + this.data=data; + } + } + + private void initBuffer(final Buffer buffer) { + if( null != buffer ) { + final int sz = rows*columns; + if(buffer.remaining()<sz || 0!=buffer.remaining()%sz) { + throw new GLException("remaining data buffer size invalid: buffer: "+buffer.toString()+"\n\t"+this); + } + this.count=buffer.remaining()/sz; + this.data=buffer; + } else { + this.count=0; + this.data=null; + } + } + + public String getName() { return name; } + + public int getLocation() { return location; } + + /** + * Sets the given location of the shader uniform. + * @return the given location + */ + public int setLocation(final 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(final GL2ES2 gl, final int program) { + location = gl.glGetUniformLocation(program, name); + return location; + } + + public Object getObject() { + return data; + } + public Buffer getBuffer() { + return (data instanceof Buffer)?(Buffer)data:null; + } + public boolean isBuffer() { + return (data instanceof Buffer); + } + public boolean isMatrix() { return isMatrix; } + + public int count() { return count; } + public int components() { return rows*columns; } + public int rows() { return rows; } + public int columns() { return columns; } + + private String name; + private int location; + private int rows, columns; + private int count; + private Object data; + private boolean isMatrix; +} diff --git a/src/jogl/classes/com/jogamp/opengl/Threading.java b/src/jogl/classes/com/jogamp/opengl/Threading.java new file mode 100644 index 000000000..852c8081f --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/Threading.java @@ -0,0 +1,226 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl; + +import jogamp.opengl.ThreadingImpl; + +/** This API provides access to the threading model for the implementation of + the classes in this package. + + <P> + + OpenGL is specified as a thread-safe API, but in practice there + are multithreading-related issues on most, if not all, of the + platforms which support it. For example, some OpenGL + implementations do not behave well when one context is made + current first on one thread, released, and then made current on a + second thread, although this is legal according to the OpenGL + specification. On other platforms there are other problems. + + <P> + + 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. + + <P> + + 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 + GLAutoDrawable and GLEventListener callback mechanism, it is + guaranteed to have the most correct single-threaded behavior on + all platforms. + + <P> + + Applications using the GLContext makeCurrent/release API directly + will inherently break this single-threaded model, as these methods + require that the OpenGL context be made current on the current + thread immediately. For applications wishing to integrate better + with an implementation that uses the single-threaded model, this + class provides public access to the mechanism used by the implementation. + + <P> + + Users can execute Runnables on the + internal thread used for performing OpenGL work, and query whether + the current thread is already this thread. Using these mechanisms + the user can move work from the current thread on to the internal + OpenGL thread if desired. + + <P> + + 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 + all platforms. + + <P> + + In addition to specifying programmatically whether the single + thread for OpenGL work is enabled, users may switch it on and off + using the system property <code>jogl.1thread</code>. Valid values + for this system property are: + + <PRE> + -Djogl.1thread=false Disable single-threading of OpenGL work, hence use multithreading. + -Djogl.1thread=true Enable single-threading of OpenGL work (default -- on a newly-created worker thread) + -Djogl.1thread=auto Select default single-threading behavior (currently on) + -Djogl.1thread=awt Enable single-threading of OpenGL work on AWT event dispatch thread (current default on all + 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> +*/ + +public class Threading { + public static enum Mode { + /** + * Full multithreaded OpenGL, + * i.e. any {@link Threading#invoke(boolean, Runnable, Object) invoke} + * {@link Threading#invokeOnOpenGLThread(boolean, Runnable) commands} + * will be issued on the current thread immediately. + */ + MT(0), + + /** Single-Threaded OpenGL on AWT EDT */ + ST_AWT(1), + + /** Single-Threaded OpenGL on dedicated worker thread. */ + ST_WORKER(2); + + public final int id; + + Mode(final int id){ + this.id = id; + } + } + + /** No reason to ever instantiate this class */ + private Threading() {} + + /** Returns the threading mode */ + public static Mode getMode() { + return ThreadingImpl.getMode(); + } + + /** If an implementation of the com.jogamp.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 + threading restrictions in their applications. Disabling + single-threading, for example, may have unintended consequences + on GLAutoDrawable implementations such as GLCanvas and GLJPanel. + 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. */ + public static final void disableSingleThreading() { + ThreadingImpl.disableSingleThreading(); + } + + /** Indicates whether OpenGL work is being automatically forced to a + single thread in this implementation. */ + public static final boolean isSingleThreaded() { + return ThreadingImpl.isSingleThreaded(); + } + + /** Indicates whether the current thread is the designated toolkit thread, + if such semantics exists. */ + public static final boolean isToolkitThread() throws GLException { + return ThreadingImpl.isToolkitThread(); + } + + /** + * Indicates whether the current thread is capable of + * performing OpenGL-related work. + * <p> + * Method always returns <code>true</code> + * if {@link #getMode()} == {@link Mode#MT} or {@link #isSingleThreaded()} == <code>false</code>. + * </p> + */ + public static final boolean isOpenGLThread() throws GLException { + return ThreadingImpl.isOpenGLThread(); + } + + /** Executes the passed Runnable on the single thread used for all + OpenGL work in this com.jogamp.opengl API implementation. It is + not specified exactly which thread is used for this + purpose. This method should only be called if the single-thread + model is in use and if the current thread is not the OpenGL + 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. + **/ + public static final void invokeOnOpenGLThread(final boolean wait, final Runnable r) throws GLException { + ThreadingImpl.invokeOnOpenGLThread(wait, r); + } + + /** + * If not {@link #isOpenGLThread()} + * <b>and</b> the <code>lock</code> is not being hold by this thread, + * invoke Runnable <code>r</code> on the OpenGL thread via {@link #invokeOnOpenGLThread(boolean, Runnable)}. + * <p> + * 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 r the Runnable to be executed + * @param lock optional lock object to be tested + * @throws GLException + */ + public static final void invoke(final boolean wait, final Runnable r, final Object lock) throws GLException { + if ( !isOpenGLThread() && + ( null == lock || !Thread.holdsLock(lock) ) ) { + invokeOnOpenGLThread(wait, r); + } else { + r.run(); + } + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/TraceGL2.java b/src/jogl/classes/com/jogamp/opengl/TraceGL2.java new file mode 100644 index 000000000..1609398e7 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/TraceGL2.java @@ -0,0 +1,23 @@ +package com.jogamp.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(final GL2 downstream, final PrintStream stream) { + super((GL4bc)downstream, stream); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/TraceGL3.java b/src/jogl/classes/com/jogamp/opengl/TraceGL3.java new file mode 100644 index 000000000..fd95d6392 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/TraceGL3.java @@ -0,0 +1,23 @@ +package com.jogamp.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(final GL3 downstream, final PrintStream stream) { + super((GL4bc)downstream, stream); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/TraceGL3bc.java b/src/jogl/classes/com/jogamp/opengl/TraceGL3bc.java new file mode 100644 index 000000000..7daa9e392 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/TraceGL3bc.java @@ -0,0 +1,23 @@ +package com.jogamp.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(final GL3bc downstream, final PrintStream stream) { + super((GL4bc)downstream, stream); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/TraceGL4.java b/src/jogl/classes/com/jogamp/opengl/TraceGL4.java new file mode 100644 index 000000000..b0c817105 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/TraceGL4.java @@ -0,0 +1,23 @@ +package com.jogamp.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(final GL4 downstream, final PrintStream stream) { + super((GL4bc)downstream, stream); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/TraceGLES2.java b/src/jogl/classes/com/jogamp/opengl/TraceGLES2.java new file mode 100644 index 000000000..7c7956b68 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/TraceGLES2.java @@ -0,0 +1,23 @@ +package com.jogamp.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(final GLES2 downstream, final PrintStream stream) { + super((GLES3)downstream, stream); + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/awt/AWTGLAutoDrawable.java b/src/jogl/classes/com/jogamp/opengl/awt/AWTGLAutoDrawable.java new file mode 100644 index 000000000..6e273e4e6 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/awt/AWTGLAutoDrawable.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl.awt; + +import com.jogamp.opengl.GLAutoDrawable; + +public interface AWTGLAutoDrawable extends GLAutoDrawable, ComponentEvents { + /** Requests a new width and height for this AWTGLAutoDrawable. */ + public void setSize(int width, int height); + + /** Schedules a repaint of the component at some point in the + future. */ + public void repaint(); +} diff --git a/src/jogl/classes/com/jogamp/opengl/awt/ComponentEvents.java b/src/jogl/classes/com/jogamp/opengl/awt/ComponentEvents.java new file mode 100644 index 000000000..996776c9b --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/awt/ComponentEvents.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl.awt; + +import com.jogamp.opengl.*; +import java.awt.event.*; +import java.beans.PropertyChangeListener; + +/** Factors out the listener manipulation for the events supported by + all of the {@link GLDrawable} implementations. Provided to reduce + clutter in the documentation for GLDrawable. */ + +public interface ComponentEvents { + public void addComponentListener(ComponentListener l); + public void removeComponentListener(ComponentListener l); + public void addFocusListener(FocusListener l); + public void removeFocusListener(FocusListener l); + public void addHierarchyBoundsListener(HierarchyBoundsListener l); + public void removeHierarchyBoundsListener(HierarchyBoundsListener l); + public void addHierarchyListener(HierarchyListener l); + public void removeHierarchyListener(HierarchyListener l); + public void addInputMethodListener(InputMethodListener l); + public void removeInputMethodListener(InputMethodListener l); + public void addKeyListener(KeyListener l); + public void removeKeyListener(KeyListener l); + public void addMouseListener(MouseListener l); + public void removeMouseListener(MouseListener l); + public void addMouseMotionListener(MouseMotionListener l); + public void removeMouseMotionListener(MouseMotionListener l); + public void addMouseWheelListener(MouseWheelListener l); + public void removeMouseWheelListener(MouseWheelListener l); + public void addPropertyChangeListener(PropertyChangeListener listener); + public void removePropertyChangeListener(PropertyChangeListener listener); + public void addPropertyChangeListener(String propertyName, + PropertyChangeListener listener); + public void removePropertyChangeListener(String propertyName, + PropertyChangeListener listener); +} diff --git a/src/jogl/classes/com/jogamp/opengl/awt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/awt/GLCanvas.java new file mode 100644 index 000000000..11d217535 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/awt/GLCanvas.java @@ -0,0 +1,1643 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl.awt; + +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 com.jogamp.nativewindow.AbstractGraphicsConfiguration; +import com.jogamp.nativewindow.OffscreenLayerOption; +import com.jogamp.nativewindow.ScalableSurface; +import com.jogamp.nativewindow.VisualIDHolder; +import com.jogamp.nativewindow.WindowClosingProtocol; +import com.jogamp.nativewindow.AbstractGraphicsDevice; +import com.jogamp.nativewindow.AbstractGraphicsScreen; +import com.jogamp.nativewindow.GraphicsConfigurationFactory; +import com.jogamp.nativewindow.NativeSurface; +import com.jogamp.nativewindow.NativeWindowFactory; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GLAnimatorControl; +import com.jogamp.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLCapabilities; +import com.jogamp.opengl.GLCapabilitiesChooser; +import com.jogamp.opengl.GLCapabilitiesImmutable; +import com.jogamp.opengl.GLContext; +import com.jogamp.opengl.GLDrawable; +import com.jogamp.opengl.GLDrawableFactory; +import com.jogamp.opengl.GLEventListener; +import com.jogamp.opengl.GLException; +import com.jogamp.opengl.GLOffscreenAutoDrawable; +import com.jogamp.opengl.GLProfile; +import com.jogamp.opengl.GLRunnable; +import com.jogamp.opengl.GLSharedContextSetter; +import com.jogamp.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.nativewindow.SurfaceScaleUtils; +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 +// GLEventListeners + +/** A heavyweight AWT component which provides OpenGL rendering + support. This is the primary implementation of an AWT {@link GLDrawable}; + {@link GLJPanel} is provided for compatibility with Swing user + interfaces when adding a heavyweight doesn't work either because + of Z-ordering or LayoutManager problems. + * + * <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> + * <ul> + * <li><pre>sun.java2d.opengl=false</pre></li> + * <li><pre>sun.java2d.noddraw=true</pre></li> + * </ul> + * This is especially true in case you want to utilize a GLProfile other than + * {@link GLProfile#GL2}, eg. using {@link GLProfile#getMaxFixedFunc()}.<br> + * On the other hand, if you like to experiment with GLJPanel's utilization + * of Java2D's OpenGL pipeline, you have to set them to + * <ul> + * <li><pre>sun.java2d.opengl=true</pre></li> + * <li><pre>sun.java2d.noddraw=true</pre></li> + * </ul> + * + * <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> + * utilizing the optional {@link java.awt.Toolkit} method <code>disableBeackgroundErase(java.awt.Canvas)</code>.<br> + * However if this does not give you the desired results, you may want to disable AWT background erase in general: + * <ul> + * <li><pre>sun.awt.noerasebackground=true</pre></li> + * </ul> + * + * <p> + * <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> + * To share a {@link GLContext} see the following note in the documentation overview: + * <a href="../../../../overview-summary.html#SHARING">context sharing</a> + * as well as {@link GLSharedContextSetter}. + * </p> + */ + +@SuppressWarnings("serial") +public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosingProtocol, OffscreenLayerOption, + AWTPrintLifecycle, GLSharedContextSetter, ScalableSurface { + + private static final boolean DEBUG = Debug.debug("GLCanvas"); + + private final RecursiveLock lock = LockFactory.createRecursiveLock(); + private final GLDrawableHelper helper = new GLDrawableHelper(); + private AWTGraphicsConfiguration awtConfig; + 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 + private final float[] minPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + private final float[] maxPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + private final float[] hasPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + final float[] reqPixelScale = new float[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + + // copy of the cstr args, mainly for recreation + private final GLCapabilitiesImmutable capsReqUser; + private final GLCapabilitiesChooser chooser; + private int additionalCtxCreationFlags = 0; + private final GraphicsDevice device; + private boolean shallUseOffscreenLayer = false; + + private volatile boolean isShowing; + private final HierarchyListener hierarchyListener = new HierarchyListener() { + @Override + public void hierarchyChanged(final HierarchyEvent e) { + isShowing = GLCanvas.this.isShowing(); + } + }; + + private final AWTWindowClosingProtocol awtWindowClosingProtocol = + new AWTWindowClosingProtocol(this, new Runnable() { + @Override + public void run() { + 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. + <p> + See details about <a href="#contextSharing">OpenGL context sharing</a>. + </p> + * @throws GLException if no default profile is available for the default desktop device. + */ + public GLCanvas() throws GLException { + this(null); + } + + /** Creates a new GLCanvas component with the requested set of + OpenGL capabilities, using the default OpenGL capabilities + selection mechanism, on the default screen device. + <p> + See details about <a href="#contextSharing">OpenGL context sharing</a>. + </p> + * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. + * @see GLCanvas#GLCanvas(com.jogamp.opengl.GLCapabilitiesImmutable, com.jogamp.opengl.GLCapabilitiesChooser, com.jogamp.opengl.GLContext, java.awt.GraphicsDevice) + */ + public GLCanvas(final GLCapabilitiesImmutable capsReqUser) throws GLException { + this(capsReqUser, null, null); + } + + /** 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 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. + <p> + See details about <a href="#contextSharing">OpenGL context sharing</a>. + </p> + * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. + */ + public GLCanvas(final GLCapabilitiesImmutable capsReqUser, + final GLCapabilitiesChooser chooser, + final GraphicsDevice device) + throws GLException + { + /* + * Determination of the native window is made in 'super.addNotify()', + * which creates the native peer using AWT's GraphicsConfiguration. + * GraphicsConfiguration is returned by this class overwritten + * 'getGraphicsConfiguration()', which returns our OpenGL compatible + * 'chosen' GraphicsConfiguration. + */ + super(); + + if(null==capsReqUser) { + this.capsReqUser = new GLCapabilities(GLProfile.getDefault(GLProfile.getDefaultDevice())); + } else { + // don't allow the user to change data + this.capsReqUser = (GLCapabilitiesImmutable) capsReqUser.cloneMutable(); + } + if( !this.capsReqUser.isOnscreen() ) { + setShallUseOffscreenLayer(true); // trigger offscreen layer - if supported + } + + if(null==device) { + final GraphicsConfiguration gc = super.getGraphicsConfiguration(); + if(null!=gc) { + this.device = gc.getDevice(); + } else { + this.device = null; + } + } else { + this.device = device; + } + + // instantiation will be issued in addNotify() + this.chooser = chooser; + + this.addHierarchyListener(hierarchyListener); + this.isShowing = isShowing(); + } + + @Override + public final void setSharedContext(final GLContext sharedContext) throws IllegalStateException { + helper.setSharedContext(this.context, sharedContext); + } + + @Override + public final void setSharedAutoDrawable(final GLAutoDrawable sharedAutoDrawable) throws IllegalStateException { + helper.setSharedAutoDrawable(this, sharedAutoDrawable); + } + + @Override + public final Object getUpstreamWidget() { + return this; + } + + @Override + public final RecursiveLock getUpstreamLock() { return lock; } + + @Override + public final boolean isThreadGLCapable() { return Threading.isOpenGLThread(); } + + @Override + public void setShallUseOffscreenLayer(final boolean v) { + shallUseOffscreenLayer = v; + } + + @Override + public final boolean getShallUseOffscreenLayer() { + return shallUseOffscreenLayer; + } + + @Override + public final boolean 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 + 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. + * - This canvas has been added to a component hierarchy and has + * an ancestor with a non-null GC, but the native peer has not + * yet been created. This means we can still choose the GC on + * 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. + */ + GraphicsConfiguration chosen = null != awtConfig ? awtConfig.getAWTGraphicsConfiguration() : null; + + if (gc != null && chosen != null && !chosen.equals(gc)) { + /* + * 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. + */ + final AWTGraphicsConfiguration config = chooseGraphicsConfiguration( (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities(), + (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities(), + chooser, gc.getDevice()); + final GraphicsConfiguration compatible = config.getAWTGraphicsConfiguration(); + final boolean equalCaps = config.getChosenCapabilities().equals(awtConfig.getChosenCapabilities()); + if(DEBUG) { + System.err.println(getThreadName()+": Info:"); + System.err.println("Created Config (n): HAVE GC "+chosen); + System.err.println("Created Config (n): THIS GC "+gc); + System.err.println("Created Config (n): Choosen GC "+compatible); + 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(); + } + + if (compatible != null) { + /* + * Save the new GC for equals test above, and to return to + * any outside callers of this method. + */ + chosen = compatible; + + if( !equalCaps && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) { + // complete destruction! + destroyImpl( true ); + // recreation! + awtConfig = config; + createJAWTDrawableAndContext(); + validateGLDrawable(); + } else { + awtConfig = config; + } + } + } + + /* + * If a compatible GC was not found in the block above, this will + * 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) { + /* + * The GC is null, which means we have no native peer, and are not + * part of a (realized) component hierarchy. So we return the + * desired visual that was selected in the constructor (possibly + * null). + */ + return chosen; + } + + /* + * Otherwise we have not explicitly selected a GC in the constructor, so + * just return what Canvas would have. + */ + return gc; + } + + @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(final boolean realized) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLDrawable _drawable = drawable; + if( null == _drawable || realized == _drawable.isRealized() || + realized && ( 0 >= _drawable.getSurfaceWidth() || 0 >= _drawable.getSurfaceHeight() ) ) { + 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); } + }; + + @Override + public final void setRealized(final 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() { + final GLDrawable _drawable = drawable; + return ( null != _drawable ) ? _drawable.isRealized() : false; + } + + @Override + public WindowClosingMode getDefaultCloseOperation() { + return awtWindowClosingProtocol.getDefaultCloseOperation(); + } + + @Override + public WindowClosingMode setDefaultCloseOperation(final WindowClosingMode op) { + return awtWindowClosingProtocol.setDefaultCloseOperation(op); + } + + @Override + public void display() { + if( !validateGLDrawable() ) { + if(DEBUG) { + System.err.println(getThreadName()+": Info: GLCanvas display - skipped GL render, drawable not valid yet"); + } + return; // not yet available .. + } + if( isShowing && !printActive ) { + Threading.invoke(true, displayOnEDTAction, getTreeLock()); + } + } + + /** + * {@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() { + destroyImpl( false ); + } + + protected void destroyImpl(final 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. + */ + @Override + public void paint(final Graphics g) { + if( Beans.isDesignTime() ) { + // Make GLCanvas behave better in NetBeans GUI builder + g.setColor(Color.BLACK); + g.fillRect(0, 0, getWidth(), getHeight()); + final FontMetrics fm = g.getFontMetrics(); + String name = getName(); + if (name == null) { + name = getClass().getName(); + final int idx = name.lastIndexOf('.'); + if (idx >= 0) { + name = name.substring(idx + 1); + } + } + final Rectangle2D bounds = fm.getStringBounds(name, g); + g.setColor(Color.WHITE); + g.drawString(name, + (int) ((getWidth() - bounds.getWidth()) / 2), + (int) ((getHeight() + bounds.getHeight()) / 2)); + } else if( !this.helper.isAnimatorAnimatingOnOtherThread() ) { + display(); + } + } + + /** 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 + function properly. <P> + + <B>Overrides:</B> + <DL><DD><CODE>addNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ + @SuppressWarnings("deprecation") + @Override + public void addNotify() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final boolean isBeansDesignTime = Beans.isDesignTime(); + + if(DEBUG) { + System.err.println(getThreadName()+": Info: addNotify - start, bounds: "+this.getBounds()+", isBeansDesignTime "+isBeansDesignTime); + // Thread.dumpStack(); + } + + 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"); + } + + // before native peer is valid: X11 + disableBackgroundErase(); + + // issues getGraphicsConfiguration() and creates the native peer + super.addNotify(); + + // after native peer is valid: Windows + disableBackgroundErase(); + + createJAWTDrawableAndContext(); + + // 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(); + } + } + + @Override + public final boolean setSurfaceScale(final float[] pixelScale) { + System.arraycopy(pixelScale, 0, reqPixelScale, 0, 2); + if( isRealized() && isShowing ) { + Threading.invoke(true, setSurfaceScaleOnEDTAction, getTreeLock()); + return true; + } else { + return false; + } + } + private final Runnable setSurfaceScaleOnEDTAction = new Runnable() { + @Override + public void run() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( null != drawable && drawable.isRealized() ) { + if( setSurfaceScaleImpl(jawtWindow) ) { + reshapeImpl(getWidth(), getHeight()); + if( !helper.isAnimatorAnimatingOnOtherThread() ) { + helper.invokeGL(drawable, context, displayAction, initAction); // display + } + } + } + } finally { + _lock.unlock(); + } + } }; + private final boolean setSurfaceScaleImpl(final ScalableSurface ns) { + if( ns.setSurfaceScale(reqPixelScale) ) { + ns.getCurrentSurfaceScale(hasPixelScale); + return true; + } else { + return false; + } + } + + private final boolean updatePixelScale() { + if( jawtWindow.hasPixelScaleChanged() ) { + jawtWindow.getMaximumSurfaceScale(maxPixelScale); + jawtWindow.getMinimumSurfaceScale(minPixelScale); + return setSurfaceScaleImpl(jawtWindow); + } else { + return false; + } + } + + @Override + public final float[] getRequestedSurfaceScale(final float[] result) { + System.arraycopy(reqPixelScale, 0, result, 0, 2); + return result; + } + + @Override + public final float[] getCurrentSurfaceScale(final float[] result) { + System.arraycopy(hasPixelScale, 0, result, 0, 2); + return result; + } + + @Override + public float[] getMinimumSurfaceScale(final float[] result) { + System.arraycopy(minPixelScale, 0, result, 0, 2); + return result; + } + + @Override + public float[] getMaximumSurfaceScale(final float[] result) { + System.arraycopy(maxPixelScale, 0, result, 0, 2); + return result; + } + + private void createJAWTDrawableAndContext() { + if ( !Beans.isDesignTime() ) { + jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(this, awtConfig); + jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); + jawtWindow.lockSurface(); + try { + jawtWindow.setSurfaceScale(reqPixelScale); + drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow); + createContextImpl(drawable); + jawtWindow.getCurrentSurfaceScale(hasPixelScale); + jawtWindow.getMinimumSurfaceScale(minPixelScale); + jawtWindow.getMaximumSurfaceScale(maxPixelScale); + } finally { + jawtWindow.unlockSurface(); + } + } + } + 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; + } + } + + 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.getSurfaceWidth() || 0 >= _drawable.getSurfaceHeight() ) { + 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 + super.removeNotify() in their removeNotify() method in order to + function properly. </p> + <p>User shall not call this method outside of EDT, read the AWT/Swing specs + about this.</p> + <B>Overrides:</B> + <DL><DD><CODE>removeNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ + @SuppressWarnings("deprecation") + @Override + public void removeNotify() { + if(DEBUG) { + System.err.println(getThreadName()+": Info: removeNotify - start"); + // Thread.dumpStack(); + } + + awtWindowClosingProtocol.removeClosingListener(); + + if( Beans.isDesignTime() ) { + super.removeNotify(); + } else { + try { + destroyImpl( true ); + } finally { + super.removeNotify(); + } + } + if(DEBUG) { + System.err.println(getThreadName()+": Info: removeNotify - end, peer: "+getPeer()); + } + } + + /** Overridden to cause {@link GLDrawableHelper#reshape} to be + 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> + + <B>Overrides:</B> + <DL><DD><CODE>reshape</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ + @SuppressWarnings("deprecation") + @Override + public void reshape(final int x, final int y, final int width, final int height) { + synchronized (getTreeLock()) { // super.reshape(..) claims tree lock, so we do extend it's lock over reshape + super.reshape(x, y, width, height); + reshapeImpl(width, height); + } + } + private void reshapeImpl(final int width, final int height) { + final int scaledWidth = SurfaceScaleUtils.scale(width, hasPixelScale[0]); + final int scaledHeight = SurfaceScaleUtils.scale(height, hasPixelScale[1]); + + if(DEBUG) { + final NativeSurface ns = getNativeSurface(); + final long nsH = null != ns ? ns.getSurfaceHandle() : 0; + System.err.println(getThreadName()+": GLCanvas.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+ + " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+getPixelScaleStr()+ + "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+getPixelScaleStr()+" -> "+scaledWidth+"x"+scaledHeight+ + " - 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, scaledWidth, scaledHeight); + if(_drawable != _drawableNew) { + // write back + drawable = _drawableNew; + } + } finally { + _lock.unlock(); + } + } + sendReshape = true; // async if display() doesn't get called below, but avoiding deadlock + } + } + + /** + * Overridden from Canvas to prevent the AWT's clearing of the + * canvas from interfering with the OpenGL rendering. + */ + @Override + public void update(final 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(final double scaleMatX, final double scaleMatY, final int numSamples, final int tileWidth, final 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() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( !validateGLDrawable() ) { + if(DEBUG) { + System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, drawable not valid yet"); + } + printActive = false; + return; // not yet available .. + } + if( !isVisible() ) { + if(DEBUG) { + System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, canvas not visible"); + } + 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 GLCapabilitiesImmutable gladCaps = getChosenGLCapabilities(); + final int printNumSamples = printAWTTiles.getNumSamples(gladCaps); + GLDrawable printDrawable = printGLAD.getDelegatedDrawable(); + final boolean reqNewGLADSamples = printNumSamples != gladCaps.getNumSamples(); + final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() || + printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight(); + final boolean reqNewGLADOnscrn = gladCaps.isOnscreen(); + + final GLCapabilities newGLADCaps = (GLCapabilities)gladCaps.cloneMutable(); + newGLADCaps.setDoubleBuffered(false); + newGLADCaps.setOnscreen(false); + if( printNumSamples != newGLADCaps.getNumSamples() ) { + newGLADCaps.setSampleBuffers(0 < printNumSamples); + newGLADCaps.setNumSamples(printNumSamples); + } + final boolean reqNewGLADSafe = GLDrawableUtil.isSwapGLContextSafe(getRequestedGLCapabilities(), gladCaps, newGLADCaps); + + final boolean reqNewGLAD = ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize ) && reqNewGLADSafe; + + if( DEBUG ) { + System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+", safe "+reqNewGLADSafe+"], "+ + ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+ + ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+ + ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+ + ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator); + } + if( reqNewGLAD ) { + final GLDrawableFactory factory = GLDrawableFactory.getFactory(newGLADCaps.getGLProfile()); + GLOffscreenAutoDrawable offGLAD = null; + try { + offGLAD = factory.createOffscreenAutoDrawable(null, newGLADCaps, null, + printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE, + printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE); + } catch (final GLException gle) { + if( DEBUG ) { + System.err.println("Caught: "+gle.getMessage()); + gle.printStackTrace(); + } + } + if( null != offGLAD ) { + printGLAD = offGLAD; + GLDrawableUtil.swapGLContextAndAllGLEventListener(GLCanvas.this, printGLAD); + printDrawable = printGLAD.getDelegatedDrawable(); + } + } + printAWTTiles.setGLOrientation(printGLAD.isGLOriented(), printGLAD.isGLOriented()); + printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0); + printAWTTiles.renderer.attachAutoDrawable(printGLAD); + if( DEBUG ) { + System.err.println("AWT print.setup "+printAWTTiles); + System.err.println("AWT print.setup AA "+printNumSamples+", "+newGLADCaps); + System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD); + System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable); + } + } finally { + _lock.unlock(); + } + } + }; + + @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() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( DEBUG ) { + System.err.println("AWT print.release "+printAWTTiles); + } + printAWTTiles.dispose(); + printAWTTiles= null; + if( printGLAD != GLCanvas.this ) { + GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLCanvas.this); + printGLAD.destroy(); + } + printGLAD = null; + if( null != printAnimator ) { + printAnimator.add(GLCanvas.this); + printAnimator = null; + } + sendReshape = true; // trigger reshape, i.e. gl-viewport and -listener - this component might got resized! + printActive = false; + display(); + } finally { + _lock.unlock(); + } + } + }; + + @Override + public void print(final 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 (final NoninvertibleTransformException nte) { + System.err.println("Caught: Inversion failed of: "+g2d.getTransform()); + nte.printStackTrace(); + } + if( DEBUG ) { + System.err.println("AWT print.X: "+printAWTTiles); + } + } + + @Override + public void addGLEventListener(final GLEventListener listener) { + helper.addGLEventListener(listener); + } + + @Override + public void addGLEventListener(final int index, final GLEventListener listener) throws IndexOutOfBoundsException { + helper.addGLEventListener(index, listener); + } + + @Override + public int getGLEventListenerCount() { + return helper.getGLEventListenerCount(); + } + + @Override + public GLEventListener getGLEventListener(final int index) throws IndexOutOfBoundsException { + return helper.getGLEventListener(index); + } + + @Override + public boolean areAllGLEventListenerInitialized() { + return helper.areAllGLEventListenerInitialized(); + } + + @Override + public boolean getGLEventListenerInitState(final GLEventListener listener) { + return helper.getGLEventListenerInitState(listener); + } + + @Override + public void setGLEventListenerInitState(final GLEventListener listener, final boolean initialized) { + helper.setGLEventListenerInitState(listener, initialized); + } + + @Override + public GLEventListener disposeGLEventListener(final GLEventListener listener, final boolean remove) { + final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove); + Threading.invoke(true, r, getTreeLock()); + return r.listener; + } + + @Override + public GLEventListener removeGLEventListener(final GLEventListener listener) { + return helper.removeGLEventListener(listener); + } + + @Override + public void setAnimator(final GLAnimatorControl animatorControl) { + helper.setAnimator(animatorControl); + } + + @Override + public GLAnimatorControl getAnimator() { + return helper.getAnimator(); + } + + @Override + public final Thread setExclusiveContextThread(final Thread t) throws GLException { + return helper.setExclusiveContextThread(t, context); + } + + @Override + public final Thread getExclusiveContextThread() { + return helper.getExclusiveContextThread(); + } + + @Override + public boolean invoke(final boolean wait, final GLRunnable glRunnable) throws IllegalStateException { + return helper.invoke(this, wait, glRunnable); + } + + @Override + public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) throws IllegalStateException { + return helper.invoke(this, wait, glRunnables); + } + + @Override + public void flushGLRunnables() { + helper.flushGLRunnables(); + } + + @Override + public GLContext setContext(final GLContext newCtx, final boolean destroyPrevCtx) { + final RecursiveLock _lock = lock; + _lock.lock(); + 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() ) { + return null; + } + final GLContext _context = context; + return (_context == null) ? null : _context.getGL(); + } + + @Override + public GL setGL(final GL gl) { + final GLContext _context = context; + if (_context != null) { + _context.setGL(gl); + return gl; + } + return null; + } + + + @Override + public void setAutoSwapBufferMode(final boolean onOrOff) { + helper.setAutoSwapBufferMode(onOrOff); + } + + @Override + public boolean getAutoSwapBufferMode() { + return helper.getAutoSwapBufferMode(); + } + + @Override + public void swapBuffers() { + Threading.invoke(true, swapBuffersOnEDTAction, getTreeLock()); + } + + @Override + public void setContextCreationFlags(final int flags) { + additionalCtxCreationFlags = flags; + final GLContext _context = context; + if(null != _context) { + _context.setContextCreationFlags(additionalCtxCreationFlags); + } + } + + @Override + public int getContextCreationFlags() { + return additionalCtxCreationFlags; + } + + @Override + public GLProfile getGLProfile() { + return capsReqUser.getGLProfile(); + } + + @Override + public GLCapabilitiesImmutable getChosenGLCapabilities() { + if( Beans.isDesignTime() ) { + return capsReqUser; + } else if( null == awtConfig ) { + throw new GLException("No AWTGraphicsConfiguration: "+this); + } + return (GLCapabilitiesImmutable)awtConfig.getChosenCapabilities(); + } + + @Override + public GLCapabilitiesImmutable getRequestedGLCapabilities() { + if( null == awtConfig ) { + return capsReqUser; + } + return (GLCapabilitiesImmutable)awtConfig.getRequestedCapabilities(); + } + + @Override + public int getSurfaceWidth() { + return SurfaceScaleUtils.scale(getWidth(), hasPixelScale[0]); + } + + @Override + public int getSurfaceHeight() { + return SurfaceScaleUtils.scale(getHeight(), hasPixelScale[1]); + } + + @Override + public boolean isGLOriented() { + final GLDrawable _drawable = drawable; + return null != _drawable ? _drawable.isGLOriented() : true; + } + + @Override + public NativeSurface getNativeSurface() { + final GLDrawable _drawable = drawable; + return (null != _drawable) ? _drawable.getNativeSurface() : null; + } + + @Override + public long getHandle() { + final GLDrawable _drawable = drawable; + return (null != _drawable) ? _drawable.getHandle() : 0; + } + + @Override + public GLDrawableFactory getFactory() { + final GLDrawable _drawable = drawable; + return (null != _drawable) ? _drawable.getFactory() : null; + } + + @Override + public String toString() { + final GLDrawable _drawable = drawable; + final int dw = (null!=_drawable) ? _drawable.getSurfaceWidth() : -1; + final int dh = (null!=_drawable) ? _drawable.getSurfaceHeight() : -1; + + return "AWT-GLCanvas[Realized "+isRealized()+ + ",\n\t"+((null!=_drawable)?_drawable.getClass().getName():"null-drawable")+ + ",\n\tFactory "+getFactory()+ + ",\n\thandle 0x"+Long.toHexString(getHandle())+ + ",\n\tDrawable size "+dw+"x"+dh+" surface["+getSurfaceWidth()+"x"+getSurfaceHeight()+"]"+ + ",\n\tAWT[pos "+getX()+"/"+getY()+", size "+getWidth()+"x"+getHeight()+ + ",\n\tvisible "+isVisible()+", displayable "+isDisplayable()+", showing "+isShowing+ + ",\n\t"+awtConfig+"]]"; + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private final String getPixelScaleStr() { return "["+hasPixelScale[0]+", "+hasPixelScale[1]+"]"; } + + private final Runnable destroyOnEDTAction = new Runnable() { + @Override + public void run() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final GLAnimatorControl animator = getAnimator(); + + if(DEBUG) { + System.err.println(getThreadName()+": Info: destroyOnEDTAction() - START, hasContext " + + (null!=context) + ", hasDrawable " + (null!=drawable)+", "+animator); + // Thread.dumpStack(); + } + + final boolean animatorPaused; + if(null!=animator) { + // can't remove us from animator for recreational addNotify() + animatorPaused = animator.pause(); + } else { + animatorPaused = false; + } + + GLException exceptionOnDisposeGL = null; + + // OLS will be detached by disposeGL's context destruction below + if( null != context ) { + if( context.isCreated() ) { + try { + helper.disposeGL(GLCanvas.this, context, true); + if(DEBUG) { + System.err.println(getThreadName()+": destroyOnEDTAction() - post ctx: "+context); + } + } catch (final GLException gle) { + exceptionOnDisposeGL = gle; + } + } + context = null; + } + + Throwable exceptionOnUnrealize = null; + if( null != drawable ) { + try { + drawable.setRealized(false); + if(DEBUG) { + System.err.println(getThreadName()+": destroyOnEDTAction() - post drawable: "+drawable); + } + } catch( final Throwable re ) { + exceptionOnUnrealize = re; + } + drawable = null; + } + + if(animatorPaused) { + animator.resume(); + } + + // throw exception in order of occurrence .. + if( null != exceptionOnDisposeGL ) { + throw exceptionOnDisposeGL; + } + if( null != exceptionOnUnrealize ) { + throw GLException.newGLException(exceptionOnUnrealize); + } + + if(DEBUG) { + System.err.println(getThreadName()+": dispose() - END, animator "+animator); + } + + } finally { + _lock.unlock(); + } + } + }; + + /** + * 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(com.jogamp.opengl.GLCapabilitiesImmutable, com.jogamp.opengl.GLCapabilitiesImmutable, com.jogamp.opengl.GLCapabilitiesChooser, java.awt.GraphicsDevice) + */ + 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; + } + hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + minPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + minPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + maxPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + maxPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + + 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; + } + final 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() { + helper.init(GLCanvas.this, !sendReshape); + } + }; + + private final Runnable displayAction = new Runnable() { + @Override + public void run() { + if (sendReshape) { + if(DEBUG) { + System.err.println(getThreadName()+": Reshape: "+getSurfaceWidth()+"x"+getSurfaceHeight()); + } + // Note: we ignore the given x and y within the parent component + // since we are drawing directly into this heavyweight component. + helper.reshape(GLCanvas.this, 0, 0, getSurfaceWidth(), getSurfaceHeight()); + sendReshape = false; + } + + helper.display(GLCanvas.this); + } + }; + + private final Runnable displayOnEDTAction = new Runnable() { + @Override + public void run() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( null != drawable && drawable.isRealized() ) { + if( GLCanvas.this.updatePixelScale() ) { + GLCanvas.this.reshapeImpl(getWidth(), getHeight()); + } + helper.invokeGL(drawable, context, displayAction, initAction); + } + } finally { + _lock.unlock(); + } + } + }; + + private final Runnable swapBuffersOnEDTAction = new Runnable() { + @Override + public void run() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( null != drawable && drawable.isRealized() ) { + drawable.swapBuffers(); + } + } finally { + _lock.unlock(); + } + } + }; + + private class DisposeGLEventListenerAction implements Runnable { + GLEventListener listener; + private final boolean remove; + private DisposeGLEventListenerAction(final GLEventListener listener, final boolean remove) { + this.listener = listener; + this.remove = remove; + } + + @Override + public void run() { + 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 + // in Java SE 6. This internal API is not available in previous + // releases, but the system property + // -Dsun.awt.noerasebackground=true can be specified to get similar + // results globally in previous releases. + private static boolean disableBackgroundEraseInitialized; + private static Method disableBackgroundEraseMethod; + private void disableBackgroundErase() { + if (!disableBackgroundEraseInitialized) { + try { + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() { + try { + Class<?> clazz = getToolkit().getClass(); + while (clazz != null && disableBackgroundEraseMethod == null) { + try { + disableBackgroundEraseMethod = + clazz.getDeclaredMethod("disableBackgroundErase", + new Class[] { Canvas.class }); + disableBackgroundEraseMethod.setAccessible(true); + } catch (final Exception e) { + clazz = clazz.getSuperclass(); + } + } + } catch (final Exception e) { + } + return null; + } + }); + } catch (final Exception e) { + } + disableBackgroundEraseInitialized = true; + if(DEBUG) { + System.err.println(getThreadName()+": GLCanvas: TK disableBackgroundErase method found: "+ + (null!=disableBackgroundEraseMethod)); + } + } + if (disableBackgroundEraseMethod != null) { + Throwable t=null; + try { + disableBackgroundEraseMethod.invoke(getToolkit(), new Object[] { this }); + } catch (final Exception e) { + t = e; + } + if(DEBUG) { + System.err.println(getThreadName()+": GLCanvas: TK disableBackgroundErase error: "+t); + } + } + } + + /** + * Issues the GraphicsConfigurationFactory's choosing facility within EDT, + * since resources created (X11: Display), must be destroyed in the same thread, where they have been created. + * + * @param capsChosen + * @param capsRequested + * @param chooser + * @param device + * @return the chosen AWTGraphicsConfiguration + * + * @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() ) { + return null; + } + + 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, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen, + capsRequested, + chooser, aScreen, VisualIDHolder.VID_UNDEFINED); + } else { + try { + final ArrayList<AWTGraphicsConfiguration> bucket = new ArrayList<AWTGraphicsConfiguration>(1); + EventQueue.invokeAndWait(new Runnable() { + @Override + public void run() { + final AWTGraphicsConfiguration c = (AWTGraphicsConfiguration) + GraphicsConfigurationFactory.getFactory(AWTGraphicsDevice.class, GLCapabilitiesImmutable.class).chooseGraphicsConfiguration(capsChosen, + capsRequested, + chooser, aScreen, VisualIDHolder.VID_UNDEFINED); + bucket.add(c); + } + }); + config = ( bucket.size() > 0 ) ? bucket.get(0) : null ; + } catch (final InvocationTargetException e) { + throw new GLException(e.getTargetException()); + } catch (final InterruptedException e) { + throw new GLException(e); + } + } + + if ( null == config ) { + throw new GLException("Error: Couldn't fetch AWTGraphicsConfiguration"); + } + + return config; + } + + protected static String getThreadName() { return Thread.currentThread().getName(); } + + /** + * A most simple JOGL AWT test entry + */ + public static void main(final String args[]) { + System.err.println(VersionUtil.getPlatformInfo()); + System.err.println(GlueGenVersion.getInstance()); + // System.err.println(NativeWindowVersion.getInstance()); + System.err.println(JoglVersion.getInstance()); + + 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"); + + final GLCanvas glCanvas = new GLCanvas(caps); + frame.add(glCanvas); + frame.setSize(128, 128); + + glCanvas.addGLEventListener(new GLEventListener() { + @Override + public void init(final GLAutoDrawable drawable) { + final GL gl = drawable.getGL(); + System.err.println(JoglVersion.getGLInfo(gl, null)); + } + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { } + @Override + public void display(final GLAutoDrawable drawable) { } + @Override + public void dispose(final GLAutoDrawable drawable) { } + }); + + try { + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + frame.setVisible(true); + }}); + } catch (final Throwable t) { + t.printStackTrace(); + } + glCanvas.display(); + try { + javax.swing.SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + frame.dispose(); + }}); + } catch (final Throwable t) { + t.printStackTrace(); + } + } + +} diff --git a/src/jogl/classes/com/jogamp/opengl/awt/GLJPanel.java b/src/jogl/classes/com/jogamp/opengl/awt/GLJPanel.java new file mode 100644 index 000000000..91b2f5e0c --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/awt/GLJPanel.java @@ -0,0 +1,2689 @@ +/* + * 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 + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package com.jogamp.opengl.awt; + +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.DataBufferInt; +import java.beans.Beans; +import java.nio.IntBuffer; +import java.util.List; + +import com.jogamp.nativewindow.AbstractGraphicsDevice; +import com.jogamp.nativewindow.NativeSurface; +import com.jogamp.nativewindow.ScalableSurface; +import com.jogamp.nativewindow.SurfaceUpdatedListener; +import com.jogamp.nativewindow.WindowClosingProtocol; +import com.jogamp.nativewindow.util.PixelFormat; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL2; +import com.jogamp.opengl.GL2ES3; +import com.jogamp.opengl.GL2GL3; +import com.jogamp.opengl.GLAnimatorControl; +import com.jogamp.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLCapabilities; +import com.jogamp.opengl.GLCapabilitiesChooser; +import com.jogamp.opengl.GLCapabilitiesImmutable; +import com.jogamp.opengl.GLContext; +import com.jogamp.opengl.GLDrawable; +import com.jogamp.opengl.GLDrawableFactory; +import com.jogamp.opengl.GLEventListener; +import com.jogamp.opengl.GLException; +import com.jogamp.opengl.GLFBODrawable; +import com.jogamp.opengl.GLOffscreenAutoDrawable; +import com.jogamp.opengl.GLProfile; +import com.jogamp.opengl.GLRunnable; +import com.jogamp.opengl.GLSharedContextSetter; +import com.jogamp.opengl.Threading; +import javax.swing.JPanel; + +import jogamp.nativewindow.SurfaceScaleUtils; +import jogamp.nativewindow.WrappedSurface; +import jogamp.nativewindow.jawt.JAWTUtil; +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.util.glsl.GLSLTextureRaster; + +import com.jogamp.common.util.PropertyAccess; +import com.jogamp.common.util.awt.AWTEDTExecutor; +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; +import com.jogamp.nativewindow.awt.AWTPrintLifecycle; +import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol; +import com.jogamp.opengl.FBObject; +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> + 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> + 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">A vertical-flip is required</a>, if the drawable {@link #isGLOriented()} and {@link #setSkipGLOrientationVerticalFlip(boolean) vertical flip is not skipped}.<br> + In this case this component performs the required vertical flip to bring the content from OpenGL's orientation into AWT's orientation.<br> + In case <a href="#fboGLSLVerticalFlip">GLSL based vertical-flip</a> is not available, + the CPU intensive {@link System#arraycopy(Object, int, Object, int, int) System.arraycopy(..)} is used line by line. + See details about <a href="#fboGLSLVerticalFlip">FBO and GLSL vertical flipping</a>. + </p> + <p> + For performance reasons, as well as for <a href="#bug842">GL state sideeffects</a>, + <b>{@link #setSkipGLOrientationVerticalFlip(boolean) skipping vertical flip} is highly recommended</b>! + </p> + <p> + The OpenGL path is concluded by copying the rendered pixels an {@link BufferedImage} via {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer) glReadPixels(..)} + for later Java2D composition. + </p> + <p> + Finally the Java2D compositioning takes place via via {@link Graphics#drawImage(java.awt.Image, int, int, int, int, java.awt.image.ImageObserver) Graphics.drawImage(...)} + on the prepared {@link BufferedImage} as described above. + </p> + <P> + * Please read <a href="GLCanvas.html#java2dgl">Java2D OpenGL Remarks</a>. + * </P> + * + <a name="fboGLSLVerticalFlip"><h5>FBO / GLSL Vertical Flip</h5></a> + If <a href="#verticalFlip">vertical flip is required</a>, + FBO is used, GLSL is available and {@link #setSkipGLOrientationVerticalFlip(boolean) vertical flip is not skipped}, a fragment shader is utilized + to flip the FBO texture vertically. This hardware-accelerated step can be disabled via system property <code>jogl.gljpanel.noglsl</code>. + <p> + The FBO / GLSL code path uses one texture-unit and binds the FBO texture to it's active texture-target, + 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> + <a name="bug842"><i>Warning (Bug 842)</i></a>: Certain GL states other than viewport and texture (see above) + influencing rendering, will also influence the GLSL vertical flip, e.g. {@link GL#glFrontFace(int) glFrontFace}({@link GL#GL_CCW}). + It is recommended to reset those states to default when leaving the {@link GLEventListener#display(GLAutoDrawable)} method! + We may change this behavior in the future, i.e. preserve all influencing states. + </p> + <p> + <a name="contextSharing"><h5>OpenGL Context Sharing</h5></a> + To share a {@link GLContext} see the following note in the documentation overview: + <a href="../../../../overview-summary.html#SHARING">context sharing</a> + as well as {@link GLSharedContextSetter}. + </p> +*/ + +@SuppressWarnings("serial") +public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosingProtocol, AWTPrintLifecycle, GLSharedContextSetter, ScalableSurface { + private static final boolean DEBUG; + private static final boolean DEBUG_FRAMES; + private static final boolean DEBUG_VIEWPORT; + private static final boolean USE_GLSL_TEXTURE_RASTERIZER; + private static final boolean SKIP_VERTICAL_FLIP_DEFAULT; + + /** 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_FRAMES = PropertyAccess.isPropertyDefined("jogl.debug.GLJPanel.Frames", true); + DEBUG_VIEWPORT = PropertyAccess.isPropertyDefined("jogl.debug.GLJPanel.Viewport", true); + USE_GLSL_TEXTURE_RASTERIZER = !PropertyAccess.isPropertyDefined("jogl.gljpanel.noglsl", true); + SKIP_VERTICAL_FLIP_DEFAULT = PropertyAccess.isPropertyDefined("jogl.gljpanel.noverticalflip", true); + boolean enabled = PropertyAccess.getBooleanProperty("sun.java2d.opengl", false); + java2dOGLEnabledByProp = enabled && !PropertyAccess.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 RecursiveLock lock = LockFactory.createRecursiveLock(); + + private final GLDrawableHelper helper; + private boolean autoSwapBufferMode; + + private volatile boolean isInitialized; + + // + // Data used for either pbuffers or pixmap-based offscreen surfaces + // + private AWTGLPixelBufferProvider customPixelBufferProvider = null; + /** Requested single buffered offscreen caps */ + private volatile GLCapabilitiesImmutable reqOffscreenCaps; + private volatile GLDrawableFactoryImpl factory; + private final GLCapabilitiesChooser chooser; + private int additionalCtxCreationFlags = 0; + + // Lazy reshape notification: reshapeWidth -> panelWidth -> backend.width + private boolean handleReshape = false; + private boolean sendReshape = true; + + private final float[] minPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + private final float[] maxPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + private final float[] hasPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; + private final float[] reqPixelScale = new float[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; + + /** For handling reshape events lazily: reshapeWidth -> panelWidth -> backend.width in pixel units (scaled) */ + private int reshapeWidth; + /** For handling reshape events lazily: reshapeHeight -> panelHeight -> backend.height in pixel units (scaled) */ + private int reshapeHeight; + + /** Scaled pixel width of the actual GLJPanel: reshapeWidth -> panelWidth -> backend.width */ + private int panelWidth = 0; + /** Scaled pixel height of the actual GLJPanel: reshapeHeight -> panelHeight -> backend.height */ + 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 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(final HierarchyEvent e) { + isShowing = GLJPanel.this.isShowing(); + } + }; + + private final AWTWindowClosingProtocol awtWindowClosingProtocol = + new AWTWindowClosingProtocol(this, new Runnable() { + @Override + public void run() { + GLJPanel.this.destroy(); + } + }, null); + + /** Creates a new GLJPanel component with a default set of OpenGL + capabilities and using the default OpenGL capabilities selection + mechanism. + <p> + See details about <a href="#contextSharing">OpenGL context sharing</a>. + </p> + * @throws GLException if no default profile is available for the default desktop device. + */ + public GLJPanel() throws GLException { + this(null); + } + + /** Creates a new GLJPanel component with the requested set of + OpenGL capabilities, using the default OpenGL capabilities + selection mechanism. + <p> + See details about <a href="#contextSharing">OpenGL context sharing</a>. + </p> + * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. + */ + public GLJPanel(final GLCapabilitiesImmutable userCapsRequest) throws GLException { + this(userCapsRequest, 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. + <p> + See details about <a href="#contextSharing">OpenGL context sharing</a>. + </p> + * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device. + */ + public GLJPanel(final GLCapabilitiesImmutable userCapsRequest, final GLCapabilitiesChooser chooser) + throws GLException + { + super(); + + // Works around problems on many vendors' cards; we don't need a + // back buffer for the offscreen surface anyway + { + GLCapabilities caps; + if (userCapsRequest != null) { + caps = (GLCapabilities) userCapsRequest.cloneMutable(); + } else { + caps = new GLCapabilities(GLProfile.getDefault(GLProfile.getDefaultDevice())); + } + caps.setDoubleBuffered(false); + reqOffscreenCaps = caps; + } + this.factory = GLDrawableFactoryImpl.getFactoryImpl( reqOffscreenCaps.getGLProfile() ); // pre-fetch, reqOffscreenCaps may changed + this.chooser = chooser; + + helper = new GLDrawableHelper(); + autoSwapBufferMode = helper.getAutoSwapBufferMode(); + + this.setFocusable(true); // allow keyboard input! + this.addHierarchyListener(hierarchyListener); + this.isShowing = isShowing(); + } + + /** + * 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(final boolean offthread) { + if( offthread ) { + new Thread(getThreadName()+"-GLJPanel_Init") { + public void run() { + if( !isInitialized ) { + initializeBackendImpl(); + } + } }.start(); + return true; + } else { + if( !isInitialized ) { + return initializeBackendImpl(); + } else { + return true; + } + } + } + + @Override + public final void setSharedContext(final GLContext sharedContext) throws IllegalStateException { + helper.setSharedContext(this.getContext(), sharedContext); + } + + @Override + public final void setSharedAutoDrawable(final 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(final 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 final RecursiveLock getUpstreamLock() { return lock; } + + @Override + public final boolean isThreadGLCapable() { return EventQueue.isDispatchThread(); } + + @Override + public void display() { + if( isShowing || ( printActive && isVisible() ) ) { + if (EventQueue.isDispatchThread()) { + // Want display() to be synchronous, so call paintImmediately() + paintImmediatelyAction.run(); + } else { + // Multithreaded redrawing of Swing components is not allowed, + // so do everything on the event dispatch thread + try { + EventQueue.invokeAndWait(paintImmediatelyAction); + } catch (final Exception e) { + throw new GLException(e); + } + } + } + } + + protected void dispose(final Runnable post) { + if(DEBUG) { + System.err.println(getThreadName()+": GLJPanel.dispose() - start"); + // Thread.dumpStack(); + } + + if (backend != null && backend.getContext() != null) { + final boolean animatorPaused; + final GLAnimatorControl animator = getAnimator(); + if(null!=animator) { + animatorPaused = animator.pause(); + } else { + animatorPaused = false; + } + + if(backend.getContext().isCreated()) { + Threading.invoke(true, disposeAction, getTreeLock()); + } + if(null != backend) { + // not yet destroyed due to backend.isUsingOwnThreadManagment() == true + backend.destroy(); + isInitialized = false; + } + if( null != post ) { + post.run(); + } + + if( animatorPaused ) { + animator.resume(); + } + } + + if(DEBUG) { + System.err.println(getThreadName()+": GLJPanel.dispose() - stop"); + } + } + + /** + * Just an alias for removeNotify + */ + @Override + public void destroy() { + removeNotify(); + } + + /** Overridden to cause OpenGL rendering to be performed during + repaint cycles. Subclasses which override this method must call + super.paintComponent() in their paintComponent() method in order + to function properly. <P> + + <DL><DD><CODE>paintComponent</CODE> in class <CODE>javax.swing.JComponent</CODE></DD></DL> */ + @Override + protected void paintComponent(final Graphics g) { + if (Beans.isDesignTime()) { + // Make GLJPanel behave better in NetBeans GUI builder + g.setColor(Color.BLACK); + g.fillRect(0, 0, getWidth(), getHeight()); + final FontMetrics fm = g.getFontMetrics(); + String name = getName(); + if (name == null) { + name = getClass().getName(); + final int idx = name.lastIndexOf('.'); + if (idx >= 0) { + name = name.substring(idx + 1); + } + } + final Rectangle2D bounds = fm.getStringBounds(name, g); + g.setColor(Color.WHITE); + g.drawString(name, + (int) ((getWidth() - bounds.getWidth()) / 2), + (int) ((getHeight() + bounds.getHeight()) / 2)); + return; + } + + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( !isInitialized ) { + initializeBackendImpl(); + } + + if (!isInitialized || printActive) { + return; + } + + // NOTE: must do this when the context is not current as it may + // involve destroying the pbuffer (current context) and + // re-creating it -- tricky to do properly while the context is + // current + if( !printActive ) { + updatePixelScale(backend); + if ( handleReshape ) { + handleReshape = false; + sendReshape = handleReshape(); + } + + if( isShowing ) { + updater.setGraphics(g); + backend.doPaintComponent(g); + } + } + } finally { + _lock.unlock(); + } + } + + private final void updateWrappedSurfaceScale(final GLDrawable d) { + final NativeSurface s = d.getNativeSurface(); + if( s instanceof WrappedSurface ) { + ((WrappedSurface)s).setSurfaceScale(hasPixelScale); + } + } + + @Override + public final boolean setSurfaceScale(final float[] pixelScale) { // HiDPI support + System.arraycopy(pixelScale, 0, reqPixelScale, 0, 2); + final Backend b = backend; + if ( isInitialized && null != b && isShowing ) { + if( isShowing || ( printActive && isVisible() ) ) { + if (EventQueue.isDispatchThread()) { + setSurfaceScaleAction.run(); + } else { + try { + EventQueue.invokeAndWait(setSurfaceScaleAction); + } catch (final Exception e) { + throw new GLException(e); + } + } + } + return true; + } else { + return false; + } + } + private final Runnable setSurfaceScaleAction = new Runnable() { + @Override + public void run() { + final Backend b = backend; + if( null != b && setSurfaceScaleImpl(b) ) { + if( !helper.isAnimatorAnimatingOnOtherThread() ) { + paintImmediatelyAction.run(); // display + } + } + } + }; + + private final boolean setSurfaceScaleImpl(final Backend b) { + if( SurfaceScaleUtils.setNewPixelScale(hasPixelScale, hasPixelScale, reqPixelScale, minPixelScale, maxPixelScale, DEBUG ? getClass().getSimpleName() : null) ) { + reshapeImpl(getWidth(), getHeight()); + updateWrappedSurfaceScale(b.getDrawable()); + return true; + } + return false; + } + + private final boolean updatePixelScale(final Backend b) { + if( JAWTUtil.getPixelScale(getGraphicsConfiguration(), minPixelScale, maxPixelScale) ) { + return setSurfaceScaleImpl(b); + } else { + return false; + } + } + + @Override + public final float[] getRequestedSurfaceScale(final float[] result) { + System.arraycopy(reqPixelScale, 0, result, 0, 2); + return result; + } + + @Override + public final float[] getCurrentSurfaceScale(final float[] result) { + System.arraycopy(hasPixelScale, 0, result, 0, 2); + return result; + } + + @Override + public float[] getMinimumSurfaceScale(final float[] result) { + System.arraycopy(minPixelScale, 0, result, 0, 2); + return result; + } + + @Override + public float[] getMaximumSurfaceScale(final float[] result) { + System.arraycopy(maxPixelScale, 0, result, 0, 2); + return result; + } + + /** 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 + function properly. <P> + + <DL><DD><CODE>addNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ + @Override + public void addNotify() { + super.addNotify(); + awtWindowClosingProtocol.addClosingListener(); + + // HiDPI support + JAWTUtil.getPixelScale(getGraphicsConfiguration(), minPixelScale, maxPixelScale); + SurfaceScaleUtils.setNewPixelScale(hasPixelScale, hasPixelScale, reqPixelScale, minPixelScale, maxPixelScale, DEBUG ? getClass().getSimpleName() : null); + + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel.addNotify()"); + } + } + + /** Overridden to track when this component is removed from a + container. Subclasses which override this method must call + super.removeNotify() in their removeNotify() method in order to + function properly. <P> + + <DL><DD><CODE>removeNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ + @Override + public void removeNotify() { + awtWindowClosingProtocol.removeClosingListener(); + + dispose(null); + hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + minPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + minPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + maxPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; + maxPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; + + super.removeNotify(); + } + + /** Overridden to cause {@link GLDrawableHelper#reshape} to be + 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> + * + * {@inheritDoc} + */ + @SuppressWarnings("deprecation") + @Override + public void reshape(final int x, final int y, final int width, final int height) { + super.reshape(x, y, width, height); + reshapeImpl(width, height); + } + + private void reshapeImpl(final int width, final int height) { + final int scaledWidth = SurfaceScaleUtils.scale(width, hasPixelScale[0]); + final int scaledHeight = SurfaceScaleUtils.scale(height, hasPixelScale[1]); + if( !printActive && ( handleReshape || scaledWidth != panelWidth || scaledHeight != panelHeight ) ) { + reshapeWidth = scaledWidth; + reshapeHeight = scaledHeight; + handleReshape = true; + } + if( DEBUG ) { + System.err.println(getThreadName()+": GLJPanel.reshape.0 "+this.getName()+" resize ["+(printActive?"printing":"paint")+ + "] [ this "+getWidth()+"x"+getHeight()+", pixelScale "+getPixelScaleStr()+ + ", panel "+panelWidth+"x"+panelHeight + + "] -> "+(handleReshape?"":"[skipped] ") + width+"x"+height+" * "+getPixelScaleStr()+ + " -> "+scaledWidth+"x"+scaledHeight+", reshapeSize "+reshapeWidth+"x"+reshapeHeight); + } + } + + private volatile boolean printActive = false; + private GLAnimatorControl printAnimator = null; + private GLAutoDrawable printGLAD = null; + private AWTTilePainter printAWTTiles = null; + + @Override + public void setupPrint(final double scaleMatX, final double scaleMatY, final int numSamples, final int tileWidth, final int tileHeight) { + printActive = true; + if( DEBUG ) { + System.err.printf(getThreadName()+": GLJPanel.setupPrint: scale %f / %f, samples %d, tileSz %d x %d%n", scaleMatX, scaleMatY, numSamples, tileWidth, tileHeight); + } + 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() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + 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( !isVisible() ) { + if(DEBUG) { + System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, panel not visible"); + } + printActive = false; + return; // not yet available .. + } + sendReshape = false; // clear reshape flag + handleReshape = false; // ditto + printAnimator = helper.getAnimator(); + if( null != printAnimator ) { + printAnimator.remove(GLJPanel.this); + } + + printGLAD = GLJPanel.this; // default: re-use + final GLCapabilitiesImmutable gladCaps = getChosenGLCapabilities(); + final int printNumSamples = printAWTTiles.getNumSamples(gladCaps); + GLDrawable printDrawable = printGLAD.getDelegatedDrawable(); + final boolean reqNewGLADSamples = printNumSamples != gladCaps.getNumSamples(); + final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() || + printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight(); + + final GLCapabilities newGLADCaps = (GLCapabilities)gladCaps.cloneMutable(); + newGLADCaps.setDoubleBuffered(false); + newGLADCaps.setOnscreen(false); + if( printNumSamples != newGLADCaps.getNumSamples() ) { + newGLADCaps.setSampleBuffers(0 < printNumSamples); + newGLADCaps.setNumSamples(printNumSamples); + } + final boolean reqNewGLADSafe = GLDrawableUtil.isSwapGLContextSafe(getRequestedGLCapabilities(), gladCaps, newGLADCaps); + + final boolean reqNewGLAD = ( reqNewGLADSamples || reqNewGLADSize ) && reqNewGLADSafe; + + if( DEBUG ) { + System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ samples "+reqNewGLADSamples+", size "+reqNewGLADSize+", safe "+reqNewGLADSafe+"], "+ + ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+ + ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+ + ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+ + ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator); + } + if( reqNewGLAD ) { + final GLDrawableFactory factory = GLDrawableFactory.getFactory(newGLADCaps.getGLProfile()); + GLOffscreenAutoDrawable offGLAD = null; + try { + offGLAD = factory.createOffscreenAutoDrawable(null, newGLADCaps, null, + printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE, + printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE); + } catch (final GLException gle) { + if( DEBUG ) { + System.err.println("Caught: "+gle.getMessage()); + gle.printStackTrace(); + } + } + if( null != offGLAD ) { + printGLAD = offGLAD; + GLDrawableUtil.swapGLContextAndAllGLEventListener(GLJPanel.this, printGLAD); + printDrawable = printGLAD.getDelegatedDrawable(); + } + } + printAWTTiles.setGLOrientation( !GLJPanel.this.skipGLOrientationVerticalFlip && printGLAD.isGLOriented(), printGLAD.isGLOriented() ); + printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0); + printAWTTiles.renderer.attachAutoDrawable(printGLAD); + if( DEBUG ) { + System.err.println("AWT print.setup "+printAWTTiles); + System.err.println("AWT print.setup AA "+printNumSamples+", "+newGLADCaps); + System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD); + System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable); + } + } finally { + _lock.unlock(); + } + } + }; + + @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() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if( DEBUG ) { + System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0 "+printAWTTiles); + } + printAWTTiles.dispose(); + printAWTTiles= null; + if( printGLAD != GLJPanel.this ) { + GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLJPanel.this); + printGLAD.destroy(); + } + printGLAD = null; + if( null != printAnimator ) { + printAnimator.add(GLJPanel.this); + printAnimator = null; + } + + // trigger reshape, i.e. gl-viewport and -listener - this component might got resized! + final int awtWidth = GLJPanel.this.getWidth(); + final int awtHeight= GLJPanel.this.getHeight(); + final int scaledAWTWidth = SurfaceScaleUtils.scale(awtWidth, hasPixelScale[0]); + final int scaledAWTHeight= SurfaceScaleUtils.scale(awtHeight, hasPixelScale[1]); + final GLDrawable drawable = GLJPanel.this.getDelegatedDrawable(); + if( scaledAWTWidth != panelWidth || scaledAWTHeight != panelHeight || + drawable.getSurfaceWidth() != panelWidth || drawable.getSurfaceHeight() != panelHeight ) { + // -> !( awtSize == panelSize == drawableSize ) + if ( DEBUG ) { + System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0: resize [printing] panel " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScaleStr()+ + ", draw "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight()+ + " -> " + awtWidth+"x"+awtHeight+" * "+getPixelScaleStr()+" -> "+scaledAWTWidth+"x"+scaledAWTHeight); + } + reshapeWidth = scaledAWTWidth; + reshapeHeight = scaledAWTHeight; + sendReshape = handleReshape(); // reshapeSize -> panelSize, backend reshape w/ GL reshape + } else { + sendReshape = true; // only GL reshape + } + printActive = false; + display(); + } finally { + _lock.unlock(); + } + } + }; + + @Override + public void print(final 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 (final NoninvertibleTransformException nte) { + System.err.println("Caught: Inversion failed of: "+g2d.getTransform()); + nte.printStackTrace(); + } + if( DEBUG ) { + System.err.println("AWT print.X: "+printAWTTiles); + } + } + @Override + protected void printComponent(final Graphics g) { + if( DEBUG ) { + System.err.println("AWT printComponent.X: "+printAWTTiles); + } + print(g); + } + + @Override + public void setOpaque(final boolean opaque) { + if (backend != null) { + backend.setOpaque(opaque); + } + super.setOpaque(opaque); + } + + @Override + public void addGLEventListener(final GLEventListener listener) { + helper.addGLEventListener(listener); + } + + @Override + public void addGLEventListener(final int index, final GLEventListener listener) { + helper.addGLEventListener(index, listener); + } + + @Override + public int getGLEventListenerCount() { + return helper.getGLEventListenerCount(); + } + + @Override + public GLEventListener getGLEventListener(final int index) throws IndexOutOfBoundsException { + return helper.getGLEventListener(index); + } + + @Override + public boolean areAllGLEventListenerInitialized() { + return helper.areAllGLEventListenerInitialized(); + } + + @Override + public boolean getGLEventListenerInitState(final GLEventListener listener) { + return helper.getGLEventListenerInitState(listener); + } + + @Override + public void setGLEventListenerInitState(final GLEventListener listener, final boolean initialized) { + helper.setGLEventListenerInitState(listener, initialized); + } + + @Override + public GLEventListener disposeGLEventListener(final GLEventListener listener, final 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 (final Exception e) { + throw new GLException(e); + } + } + return r.listener; + } + + @Override + public GLEventListener removeGLEventListener(final GLEventListener listener) { + return helper.removeGLEventListener(listener); + } + + @Override + public void setAnimator(final GLAnimatorControl animatorControl) { + helper.setAnimator(animatorControl); + } + + @Override + public GLAnimatorControl getAnimator() { + return helper.getAnimator(); + } + + @Override + public final Thread setExclusiveContextThread(final Thread t) throws GLException { + return helper.setExclusiveContextThread(t, getContext()); + } + + @Override + public final Thread getExclusiveContextThread() { + return helper.getExclusiveContextThread(); + } + + @Override + public boolean invoke(final boolean wait, final GLRunnable glRunnable) throws IllegalStateException { + return helper.invoke(this, wait, glRunnable); + } + + @Override + public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) throws IllegalStateException { + return helper.invoke(this, wait, glRunnables); + } + + @Override + public void flushGLRunnables() { + helper.flushGLRunnables(); + } + + @Override + public GLContext createContext(final GLContext shareWith) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final Backend b = backend; + if ( null == b ) { + return null; + } + return b.createContext(shareWith); + } finally { + _lock.unlock(); + } + } + + @Override + public void setRealized(final boolean realized) { + } + + @Override + public boolean isRealized() { + return isInitialized; + } + + @Override + public GLContext setContext(final GLContext newCtx, final boolean destroyPrevCtx) { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + final Backend b = backend; + if ( null == b ) { + return null; + } + final GLContext oldCtx = b.getContext(); + GLDrawableHelper.switchContext(b.getDrawable(), oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags); + b.setContext(newCtx); + return oldCtx; + } finally { + _lock.unlock(); + } + } + + + @Override + public final GLDrawable getDelegatedDrawable() { + final Backend b = backend; + if ( null == b ) { + return null; + } + return b.getDrawable(); + } + + @Override + public GLContext getContext() { + final Backend b = backend; + if ( null == b ) { + return null; + } + return b.getContext(); + } + + @Override + public GL getGL() { + if (Beans.isDesignTime()) { + return null; + } + final GLContext context = getContext(); + return (context == null) ? null : context.getGL(); + } + + @Override + public GL setGL(final GL gl) { + final GLContext context = getContext(); + if (context != null) { + context.setGL(gl); + return gl; + } + return null; + } + + @Override + public void setAutoSwapBufferMode(final 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() { + return autoSwapBufferMode; + } + + @Override + public void swapBuffers() { + if( isInitialized ) { + final Backend b = backend; + if ( null != b ) { + b.swapBuffers(); + } + } + } + + @Override + public void setContextCreationFlags(final int flags) { + additionalCtxCreationFlags = flags; + } + + @Override + public int getContextCreationFlags() { + 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 + (GL_COLOR_BUFFER_BIT) for correct rendering of the GLJPanel and + underlying widgets which may show through portions of the + GLJPanel with alpha values less than 1. Most Swing + implementations currently expect the GLJPanel to be completely + cleared (e.g., by <code>glClear(GL_COLOR_BUFFER_BIT | + GL_DEPTH_BUFFER_BIT)</code>), but for certain optimized Swing + implementations which use OpenGL internally, it may be possible + to perform OpenGL rendering using the GLJPanel into the same + OpenGL drawable as the Swing implementation uses. */ + public boolean shouldPreserveColorBufferIfTranslucent() { + return oglPipelineUsable(); + } + + @Override + public int getSurfaceWidth() { + return panelWidth; // scaled surface width in pixel units, current as-from reshape + } + + @Override + public int getSurfaceHeight() { + return panelHeight; // scaled surface height in pixel units, current as-from reshape + } + + /** + * {@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(); + } + + /** + * Skip {@link #isGLOriented()} based vertical flip, + * which usually is required by the offscreen backend, + * see details about <a href="#verticalFlip">vertical flip</a> + * and <a href="#fboGLSLVerticalFlip">FBO / GLSL vertical flip</a>. + * <p> + * If set to <code>true</code>, user needs to flip the OpenGL rendered scene + * <i>if {@link #isGLOriented()} == true</i>, e.g. via the projection matrix.<br/> + * See constraints of {@link #isGLOriented()}. + * </p> + */ + public final void setSkipGLOrientationVerticalFlip(final boolean v) { + skipGLOrientationVerticalFlip = v; + } + /** See {@link #setSkipGLOrientationVerticalFlip(boolean)}. */ + public final boolean getSkipGLOrientationVerticalFlip() { + return skipGLOrientationVerticalFlip; + } + + @Override + public GLCapabilitiesImmutable getChosenGLCapabilities() { + final Backend b = backend; + if ( null == b ) { + return null; + } + return b.getChosenGLCapabilities(); + } + + @Override + public final GLCapabilitiesImmutable getRequestedGLCapabilities() { + return reqOffscreenCaps; + } + + /** + * Set a new requested {@link GLCapabilitiesImmutable} for this GLJPanel + * allowing reconfiguration. + * <p> + * Method shall be invoked from the {@link #isThreadGLCapable() AWT-EDT thread}. + * In case it is not invoked on the AWT-EDT thread, an attempt is made to do so. + * </p> + * <p> + * Method will dispose a previous {@link #isRealized() realized} GLContext and offscreen backend! + * </p> + * @param caps new capabilities. + */ + public final void setRequestedGLCapabilities(final GLCapabilitiesImmutable caps) { + if( null == caps ) { + throw new IllegalArgumentException("null caps"); + } + Threading.invoke(true, + new Runnable() { + @Override + public void run() { + dispose( new Runnable() { + @Override + public void run() { + // switch to new caps and re-init backend + // after actual dispose, but before resume animator + reqOffscreenCaps = caps; + initializeBackendImpl(); + } } ); + } + }, getTreeLock()); + } + + @Override + public final GLProfile getGLProfile() { + return reqOffscreenCaps.getGLProfile(); + } + + @Override + public NativeSurface getNativeSurface() { + final Backend b = backend; + if ( null == b ) { + return null; + } + return b.getDrawable().getNativeSurface(); + } + + @Override + public long getHandle() { + 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(final int v) { + requestedTextureUnit = v; + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private final Object initSync = new Object(); + private boolean initializeBackendImpl() { + synchronized(initSync) { + if( !isInitialized ) { + if( handleReshape ) { + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel.createAndInitializeBackend.1: ["+(printActive?"printing":"paint")+"] "+ + panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr() + " -> " + + reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScaleStr()); + } + panelWidth = reshapeWidth; + panelHeight = reshapeHeight; + handleReshape = false; + } else { + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel.createAndInitializeBackend.0: ["+(printActive?"printing":"paint")+"] "+ + panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr()); + } + } + + if ( 0 >= panelWidth || 0 >= panelHeight ) { + return false; + } + + if ( null == backend ) { + if ( oglPipelineUsable() ) { + backend = new J2DOGLBackend(); + } else { + backend = new OffscreenBackend(customPixelBufferProvider); + } + isInitialized = false; + } + + if (!isInitialized) { + this.factory = GLDrawableFactoryImpl.getFactoryImpl( reqOffscreenCaps.getGLProfile() ); // reqOffscreenCaps may have changed + backend.initialize(); + } + return isInitialized; + } else { + return true; + } + } + } + + private final String getPixelScaleStr() { return "["+hasPixelScale[0]+", "+hasPixelScale[1]+"]"; } + + @Override + public WindowClosingMode getDefaultCloseOperation() { + return awtWindowClosingProtocol.getDefaultCloseOperation(); + } + + @Override + public WindowClosingMode setDefaultCloseOperation(final WindowClosingMode op) { + return awtWindowClosingProtocol.setDefaultCloseOperation(op); + } + + private boolean handleReshape() { + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel.handleReshape: "+ + panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr() + " -> " + + reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScaleStr()); + } + panelWidth = reshapeWidth; + panelHeight = reshapeHeight; + + 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 { + private Graphics g; + + public void setGraphics(final Graphics g) { + this.g = g; + } + + @Override + public void init(final GLAutoDrawable drawable) { + if (!backend.preGL(g)) { + return; + } + helper.init(GLJPanel.this, !sendReshape); + backend.postGL(g, false); + } + + @Override + public void dispose(final GLAutoDrawable drawable) { + helper.disposeAllGLEventListener(GLJPanel.this, false); + } + + @Override + public void display(final GLAutoDrawable drawable) { + if (!backend.preGL(g)) { + return; + } + if (sendReshape) { + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel.display: reshape(" + viewportX + "," + viewportY + " " + panelWidth + "x" + panelHeight + " @ scale "+getPixelScaleStr()+")"); + } + helper.reshape(GLJPanel.this, viewportX, viewportY, panelWidth, panelHeight); + sendReshape = false; + } + + helper.display(GLJPanel.this); + backend.postGL(g, true); + } + + public void plainPaint(final GLAutoDrawable drawable) { + helper.display(GLJPanel.this); + } + + @Override + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { + // This is handled above and dispatched directly to the appropriate context + } + } + + @Override + public String toString() { + final GLDrawable d = ( null != backend ) ? backend.getDrawable() : null; + return "AWT-GLJPanel[ drawableType "+ ( ( null != d ) ? d.getClass().getName() : "null" ) + + ", chosenCaps " + getChosenGLCapabilities() + + "]"; + } + + private final Runnable disposeAction = new Runnable() { + @Override + public void run() { + final RecursiveLock _lock = lock; + _lock.lock(); + try { + if ( null != backend ) { + final GLContext _context = backend.getContext(); + final boolean backendDestroy = !backend.isUsingOwnLifecycle(); + + GLException exceptionOnDisposeGL = null; + if( null != _context && _context.isCreated() ) { + try { + helper.disposeGL(GLJPanel.this, _context, !backendDestroy); + } catch (final GLException gle) { + exceptionOnDisposeGL = gle; + } + } + Throwable exceptionBackendDestroy = null; + if ( backendDestroy ) { + try { + backend.destroy(); + } catch( final Throwable re ) { + exceptionBackendDestroy = re; + } + backend = null; + isInitialized = false; + } + + // throw exception in order of occurrence .. + if( null != exceptionOnDisposeGL ) { + throw exceptionOnDisposeGL; + } + if( null != exceptionBackendDestroy ) { + throw GLException.newGLException(exceptionBackendDestroy); + } + } + } finally { + _lock.unlock(); + } + } + }; + + 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 class DisposeGLEventListenerAction implements Runnable { + GLEventListener listener; + private final boolean remove; + private DisposeGLEventListenerAction(final GLEventListener listener, final 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(final GL gl, final int which) { + final int[] tmp = new int[1]; + gl.glGetIntegerv(which, tmp, 0); + return tmp[0]; + } + + 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 + */ + static interface Backend { + /** 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 */ + public void destroy(); + + /** 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 + */ + public GLContext createContext(GLContext shareWith); + + /** Called to set the current backend's GLContext */ + public void setContext(GLContext ctx); + + /** Called to get the current backend's GLContext */ + public GLContext getContext(); + + /** Called to get the current backend's GLDrawable */ + public GLDrawable getDrawable(); + + /** 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 */ + 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 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); + + /** + * 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 */ + 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 + 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 IntBuffer readBackIntsForCPUVFlip; + + // Implementation using software rendering + private volatile GLDrawable offscreenDrawable; // volatile: avoid locking for read-only access + private boolean offscreenIsFBO; + private FBObject fboFlipped; + private GLSLTextureRaster glslTextureRaster; + + 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 final GLPixelStorageModes psm = new GLPixelStorageModes(); + + OffscreenBackend(final AWTGLPixelBufferProvider custom) { + if(null == custom) { + pixelBufferProvider = getSingleAWTGLPixelBufferProvider(); + } else { + pixelBufferProvider = custom; + } + if( pixelBufferProvider instanceof SingletonGLPixelBufferProvider ) { + useSingletonBuffer = true; + } else { + useSingletonBuffer = false; + } + } + + @Override + public final boolean isUsingOwnLifecycle() { return false; } + + @Override + public final void initialize() { + if(DEBUG) { + System.err.println(getThreadName()+": OffscreenBackend: initialize() - frameCount "+frameCount); + } + GLException glException = null; + try { + final GLContext[] shareWith = { null }; + if( helper.isSharedGLContextPending(shareWith) ) { + return; // pending .. + } + offscreenDrawable = factory.createOffscreenDrawable( + null /* default platform device */, + reqOffscreenCaps, + chooser, + panelWidth, panelHeight); + updateWrappedSurfaceScale(offscreenDrawable); + offscreenDrawable.setRealized(true); + if( DEBUG_FRAMES ) { + offscreenDrawable.getNativeSurface().addSurfaceUpdatedListener(new SurfaceUpdatedListener() { + @Override + public final void surfaceUpdated(final Object updater, final NativeSurface ns, final long when) { + System.err.println(getThreadName()+": OffscreenBackend.swapBuffers - frameCount "+frameCount); + } } ); + } + + // + // Pre context configuration + // + flipVertical = !GLJPanel.this.skipGLOrientationVerticalFlip && offscreenDrawable.isGLOriented(); + offscreenIsFBO = offscreenDrawable.getRequestedGLCapabilities().isFBO(); + final boolean useGLSLFlip_pre = flipVertical && offscreenIsFBO && reqOffscreenCaps.getGLProfile().isGL2ES2() && USE_GLSL_TEXTURE_RASTERIZER; + if( offscreenIsFBO && !useGLSLFlip_pre ) { + // Texture attachment only required for GLSL vertical flip, hence simply use a color-renderbuffer attachment. + ((GLFBODrawable)offscreenDrawable).setFBOMode(0); + } + + offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith[0]); + offscreenContext.setContextCreationFlags(additionalCtxCreationFlags); + if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) { + isInitialized = true; + helper.setAutoSwapBufferMode(false); // we handle swap-buffers, see handlesSwapBuffer() + + final GL gl = offscreenContext.getGL(); + // Remedy for Bug 1020, i.e. OSX/Nvidia's FBO needs to be cleared before blitting, + // otherwise first MSAA frame lacks antialiasing. + // Clearing of FBO is performed within GLFBODrawableImpl.initialize(..): + // gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + + final GLCapabilitiesImmutable chosenCaps = offscreenDrawable.getChosenGLCapabilities(); + final boolean glslCompliant = !offscreenContext.hasRendererQuirk(GLRendererQuirks.GLSLNonCompliant); + final boolean useGLSLFlip = useGLSLFlip_pre && gl.isGL2ES2() && glslCompliant; + if( DEBUG ) { + System.err.println(getThreadName()+": OffscreenBackend.initialize: useGLSLFlip "+useGLSLFlip+ + " [flip "+flipVertical+", isFBO "+offscreenIsFBO+", isGL2ES2 "+gl.isGL2ES2()+ + ", noglsl "+!USE_GLSL_TEXTURE_RASTERIZER+", glslNonCompliant "+!glslCompliant+ + ", isGL2ES2 " + gl.isGL2ES2()+"\n "+offscreenDrawable+"]"); + } + if( useGLSLFlip ) { + final GLFBODrawable fboDrawable = (GLFBODrawable) offscreenDrawable; + fboDrawable.setTextureUnit( GLJPanel.this.requestedTextureUnit ); + try { + fboFlipped = new FBObject(); + fboFlipped.init(gl, panelWidth, panelHeight, 0); + fboFlipped.attachColorbuffer(gl, 0, chosenCaps.getAlphaBits()>0); + // fboFlipped.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + gl.glClear(GL.GL_COLOR_BUFFER_BIT); // Bug 1020 (see above), cannot do in FBObject due to unknown 'first bind' state. + glslTextureRaster = new GLSLTextureRaster(fboDrawable.getTextureUnit(), true); + glslTextureRaster.init(gl.getGL2ES2()); + glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, panelWidth, panelHeight); + } catch (final 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; + } + } catch( final GLException gle ) { + glException = gle; + } finally { + if( !isInitialized ) { + if(null != offscreenContext) { + offscreenContext.destroy(); + offscreenContext = null; + } + if(null != offscreenDrawable) { + offscreenDrawable.setRealized(false); + offscreenDrawable = null; + } + } + if( null != glException ) { + throw new GLException("Caught GLException: "+glException.getMessage(), glException); + } + } + } + + @Override + public final void destroy() { + if(DEBUG) { + System.err.println(getThreadName()+": OffscreenBackend: destroy() - offscreenContext: "+(null!=offscreenContext)+" - offscreenDrawable: "+(null!=offscreenDrawable)+" - frameCount "+frameCount); + } + 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.setRealized(false); + offscreenDrawable = null; + if(null != adevice) { + adevice.close(); + } + } + offscreenIsFBO = false; + + if( null != readBackIntsForCPUVFlip ) { + readBackIntsForCPUVFlip.clear(); + readBackIntsForCPUVFlip = null; + } + if( null != pixelBuffer ) { + if( !useSingletonBuffer ) { + pixelBuffer.dispose(); + } + pixelBuffer = null; + } + alignedImage = null; + } + + @Override + public final void setOpaque(final boolean opaque) { + if ( opaque != isOpaque() && !useSingletonBuffer ) { + pixelBuffer.dispose(); + pixelBuffer = null; + alignedImage = null; + } + } + + @Override + public final boolean preGL(final Graphics g) { + // Empty in this implementation + return true; + } + + @Override + public final boolean handlesSwapBuffer() { + return true; + } + + @Override + public final void swapBuffers() { + final GLDrawable d = offscreenDrawable; + if( null != d ) { + d.swapBuffers(); + } + } + + @Override + public final void postGL(final Graphics g, final boolean isDisplay) { + if (isDisplay) { + if( DEBUG_FRAMES ) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: - frameCount "+frameCount); + } + + final GL gl = offscreenContext.getGL(); + + // + // Save TextureState ASAP, i.e. the user values for the used FBO texture-unit + // and the current active texture-unit (if not same) + // + final TextureState usrTexState, fboTexState; + final int fboTexUnit; + + if( offscreenIsFBO ) { + fboTexUnit = GL.GL_TEXTURE0 + ((GLFBODrawable)offscreenDrawable).getTextureUnit(); + 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 { + fboTexUnit = 0; + usrTexState = null; + fboTexState = null; + } + + + if( autoSwapBufferMode ) { + // Since we only use a single-buffer non-MSAA or double-buffered MSAA offscreenDrawable, + // we can always swap! + offscreenDrawable.swapBuffers(); + } + + final int componentCount; + final int alignment; + if( isOpaque() ) { + // w/o alpha + componentCount = 3; + alignment = 1; + } else { + // with alpha + componentCount = 4; + alignment = 4; + } + + final PixelFormat awtPixelFormat = pixelBufferProvider.getAWTPixelFormat(gl.getGLProfile(), componentCount); + final GLPixelAttributes pixelAttribs = pixelBufferProvider.getAttributes(gl, componentCount, true); + + if( useSingletonBuffer ) { // attempt to fetch the latest AWTGLPixelBuffer + pixelBuffer = (AWTGLPixelBuffer) ((SingletonGLPixelBufferProvider)pixelBufferProvider).getSingleBuffer(awtPixelFormat.comp, pixelAttribs, true); + } + if( null != pixelBuffer && pixelBuffer.requiresNewBuffer(gl, panelWidth, panelHeight, 0) ) { + pixelBuffer.dispose(); + pixelBuffer = null; + alignedImage = null; + } + final boolean DEBUG_INIT; + if ( null == pixelBuffer ) { + if (0 >= panelWidth || 0 >= panelHeight ) { + return; + } + pixelBuffer = pixelBufferProvider.allocate(gl, awtPixelFormat.comp, pixelAttribs, true, panelWidth, panelHeight, 1, 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)+", isGL2ES3 "+gl.isGL2ES3()); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" panelSize "+panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr()); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelAttribs "+pixelAttribs); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" awtPixelFormat "+awtPixelFormat); + DEBUG_INIT = true; + } else { + DEBUG_INIT = false; + } + } else { + DEBUG_INIT = false; + } + if( offscreenDrawable.getSurfaceWidth() != panelWidth || offscreenDrawable.getSurfaceHeight() != panelHeight ) { + throw new InternalError("OffscreenDrawable panelSize mismatch (reshape missed): panelSize "+panelWidth+"x"+panelHeight+" != drawable "+offscreenDrawable.getSurfaceWidth()+"x"+offscreenDrawable.getSurfaceHeight()+", 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()+" @ scale "+getPixelScaleStr()+", "+alignedImage+", pixelBuffer "+pixelBuffer.width+"x"+pixelBuffer.height+", "+pixelBuffer); + } + } + final IntBuffer readBackInts; + + 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; + } + + // Must now copy pixels from offscreen context into surface + if( DEBUG_FRAMES ) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.readPixels: - frameCount "+frameCount); + } + + // Save PACK modes, reset them to defaults and set alignment + psm.setPackAlignment(gl, alignment); + if( gl.isGL2ES3() ) { + final GL2ES3 gl2es3 = gl.getGL2ES3(); + psm.setPackRowLength(gl2es3, panelWidth); + gl2es3.glReadBuffer(gl2es3.getDefaultReadBuffer()); + if( DEBUG_INIT ) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.0: fboDrawable "+offscreenDrawable); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.0: isGL2ES3, readBuffer 0x"+Integer.toHexString(gl2es3.getDefaultReadBuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.0: def-readBuffer 0x"+Integer.toHexString(gl2es3.getDefaultReadBuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.0: def-readFBO 0x"+Integer.toHexString(gl2es3.getDefaultReadFramebuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.0: bound-readFBO 0x"+Integer.toHexString(gl2es3.getBoundFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER))); + } + } + + 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] || + panelWidth != usrViewport[2] || panelHeight != usrViewport[3]; + if( DEBUG_VIEWPORT ) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL: "+GLJPanel.this.getName()+" Viewport: change "+viewportChange+ + ", "+usrViewport[0]+"/"+usrViewport[1]+" "+usrViewport[2]+"x"+usrViewport[3]+ + " -> 0/0 "+panelWidth+"x"+panelHeight); + } + if( viewportChange ) { + gl.glViewport(0, 0, panelWidth, panelHeight); + } + + // perform vert-flipping via OpenGL/FBO + final GLFBODrawable fboDrawable = (GLFBODrawable)offscreenDrawable; + final FBObject.TextureAttachment fboTex = fboDrawable.getColorbuffer(GL.GL_FRONT).getTextureAttachment(); + + 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()); + if( DEBUG_INIT ) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: fboDrawable "+fboDrawable); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: read from fbo-rb "+fboFlipped.getReadFramebuffer()+", fbo "+fboFlipped); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: isGL2ES3, readBuffer 0x"+Integer.toHexString(gl.getDefaultReadBuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: def-readBuffer 0x"+Integer.toHexString(gl.getDefaultReadBuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: def-readFBO 0x"+Integer.toHexString(gl.getDefaultReadFramebuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: bound-readFBO 0x"+Integer.toHexString(gl.getBoundFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER))); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.1: "+GLJPanel.this.getName()+" pixelAttribs "+pixelAttribs); + } + gl.glReadPixels(0, 0, panelWidth, panelHeight, pixelAttribs.format, pixelAttribs.type, readBackInts); + + fboFlipped.unbind(gl); + if( DEBUG_INIT ) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.2: fboDrawable "+fboDrawable); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.2: read from fbo-rb "+fboFlipped.getReadFramebuffer()+", fbo "+fboFlipped); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.2: isGL2ES3, readBuffer 0x"+Integer.toHexString(gl.getDefaultReadBuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.2: def-readBuffer 0x"+Integer.toHexString(gl.getDefaultReadBuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.2: def-readFBO 0x"+Integer.toHexString(gl.getDefaultReadFramebuffer())); + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0.2: bound-readFBO 0x"+Integer.toHexString(gl.getBoundFramebuffer(GL2ES3.GL_READ_FRAMEBUFFER))); + } + if( viewportChange ) { + gl.glViewport(usrViewport[0], usrViewport[1], usrViewport[2], usrViewport[3]); + } + } 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( 0 != fboTexUnit ) { // implies offscreenIsFBO + fboTexState.restore(gl); + if( fboTexUnit != usrTexState.getUnit() ) { + usrTexState.restore(gl); + } + } + + // Restore saved modes. + psm.restore(gl); + + // Note: image will be drawn back in paintComponent() for + // correctness on all platforms + } + } + + @Override + public final int getTextureUnit() { + if(null != glslTextureRaster && null != offscreenDrawable) { // implies flippedVertical + return ((GLFBODrawable)offscreenDrawable).getTextureUnit(); + } + return -1; + } + + @Override + public final void doPaintComponent(final Graphics g) { + helper.invokeGL(offscreenDrawable, offscreenContext, updaterDisplayAction, updaterInitAction); + + if ( null != alignedImage ) { + if( DEBUG_FRAMES ) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.doPaintComponent.drawImage: - frameCount "+frameCount); + } + // Draw resulting image in one shot + g.drawImage(alignedImage, 0, 0, + SurfaceScaleUtils.scaleInv(alignedImage.getWidth(), hasPixelScale[0]), + SurfaceScaleUtils.scaleInv(alignedImage.getHeight(), hasPixelScale[1]), null); // Null ImageObserver since image data is ready. + } + frameCount++; + } + + @Override + public final void doPlainPaint() { + helper.invokeGL(offscreenDrawable, offscreenContext, updaterPlainDisplayAction, updaterInitAction); + } + + @Override + public final boolean handleReshape() { + GLDrawableImpl _drawable = (GLDrawableImpl)offscreenDrawable; + { + final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, offscreenContext, panelWidth, panelHeight); + if(_drawable != _drawableNew) { + // write back + _drawable = _drawableNew; + offscreenDrawable = _drawableNew; + updateWrappedSurfaceScale(offscreenDrawable); + } + } + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.handleReshape: " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScaleStr() + " -> " + _drawable.getSurfaceWidth()+"x"+_drawable.getSurfaceHeight()); + } + panelWidth = _drawable.getSurfaceWidth(); + panelHeight = _drawable.getSurfaceHeight(); + + if( null != glslTextureRaster ) { + if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) { + try { + final GL gl = offscreenContext.getGL(); + fboFlipped.reset(gl, panelWidth, panelHeight, 0); + glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, panelWidth, panelHeight); + } finally { + offscreenContext.release(); + } + } + } + return _drawable.isRealized(); + } + + @Override + public final GLContext createContext(final GLContext shareWith) { + return (null != offscreenDrawable) ? offscreenDrawable.createContext(shareWith) : null; + } + + @Override + public final void setContext(final GLContext ctx) { + offscreenContext=(GLContextImpl)ctx; + } + + @Override + public final GLContext getContext() { + return offscreenContext; + } + + @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(); + } + } + + class J2DOGLBackend implements Backend { + // Opaque Object identifier representing the Java2D surface we are + // drawing to; used to determine when to destroy and recreate JOGL + // context + private Object j2dSurface; + // Graphics object being used during Java2D update action + // (absolutely essential to cache this) + // No-op context representing the Java2D OpenGL context + private GLContext j2dContext; + // Context associated with no-op drawable representing the JOGL + // OpenGL context + private GLDrawable joglDrawable; + // The real OpenGL context JOGL uses to render + private GLContext joglContext; + // State captured from Java2D OpenGL context necessary in order to + // properly render into Java2D back buffer + 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 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 + // attachment and (b) actually create new depth buffer storage and + // attach it in order for the FBO to behave properly in our context. + private boolean checkedForFBObjectWorkarounds; + private boolean fbObjectWorkarounds; + private int[] frameBufferDepthBuffer; + private int[] frameBufferTexture; + private boolean createNewDepthBuffer; + // Current (as of this writing) ATI drivers have problems when the + // same FBO is bound in two different contexts. Here we check for + // this case and explicitly release the FBO from Java2D's context + // before switching to ours. Java2D will re-bind the FBO when it + // makes its context current the next time. Interestingly, if we run + // this code path on NVidia hardware, it breaks the rendering + // results -- no output is generated. This doesn't appear to be an + // interaction with the abovementioned NVidia-specific workarounds, + // as even if we disable that code the FBO is still reported as + // incomplete in our context. + private boolean checkedGLVendor; + private boolean vendorIsATI; + + // Holding on to this GraphicsConfiguration is a workaround for a + // problem in the Java 2D / JOGL bridge when FBOs are enabled; see + // comment related to Issue 274 below + private GraphicsConfiguration workaroundConfig; + + @Override + public final boolean isUsingOwnLifecycle() { return true; } + + @Override + public final void initialize() { + if(DEBUG) { + System.err.println(getThreadName()+": J2DOGL: initialize()"); + } + // No-op in this implementation; everything is done lazily + isInitialized = true; + } + + @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)); + } + if (joglContext != null) { + joglContext.destroy(); + joglContext = null; + } + joglDrawable = null; + if (j2dContext != null) { + j2dContext.destroy(); + j2dContext = null; + } + } + }); + } + + @Override + public final void setOpaque(final boolean opaque) { + // Empty in this implementation + } + + @Override + public final GLContext createContext(final 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; + } + + @Override + public final void setContext(final GLContext ctx) { + joglContext=ctx; + } + + @Override + public final GLContext getContext() { + return joglContext; + } + + @Override + public final GLDrawable getDrawable() { + return joglDrawable; + } + + @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); + } + + @Override + public final GLProfile getGLProfile() { + // FIXME: should do better than this; is it possible to query J2D's Profile ? + return GLProfile.getDefault(GLProfile.getDefaultDevice()); + } + + @Override + public final boolean handleReshape() { + // Empty in this implementation + return true; + } + + @Override + public final boolean preGL(final Graphics g) { + final GL2 gl = joglContext.getGL().getGL2(); + // Set up needed state in JOGL context from Java2D context + gl.glEnable(GL.GL_SCISSOR_TEST); + final Rectangle r = Java2D.getOGLScissorBox(g); + + if (r == null) { + if (DEBUG) { + System.err.println(getThreadName()+": Java2D.getOGLScissorBox() returned null"); + } + return false; + } + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel: gl.glScissor(" + r.x + ", " + r.y + ", " + r.width + ", " + r.height + ")"); + } + + gl.glScissor(r.x, r.y, r.width, r.height); + final Rectangle oglViewport = Java2D.getOGLViewport(g, panelWidth, panelHeight); + // If the viewport X or Y changes, in addition to the panel's + // width or height, we need to send a reshape operation to the + // client + if ((viewportX != oglViewport.x) || + (viewportY != oglViewport.y)) { + sendReshape = true; + if (DEBUG) { + System.err.println(getThreadName()+": Sending reshape because viewport changed"); + System.err.println(" viewportX (" + viewportX + ") ?= oglViewport.x (" + oglViewport.x + ")"); + System.err.println(" viewportY (" + viewportY + ") ?= oglViewport.y (" + oglViewport.y + ")"); + } + } + viewportX = oglViewport.x; + viewportY = oglViewport.y; + + // If the FBO option is active, bind to the FBO from the Java2D + // context. + // Note that all of the plumbing in the context sharing stuff will + // allow us to bind to this object since it's in our namespace. + if (Java2D.isFBOEnabled() && + Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) { + + // The texture target for Java2D's OpenGL pipeline when using FBOs + // -- either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB + final int fboTextureTarget = Java2D.getOGLTextureType(g); + + if (!checkedForFBObjectWorkarounds) { + checkedForFBObjectWorkarounds = true; + gl.glBindTexture(fboTextureTarget, 0); + gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, frameBuffer[0]); + final int status = gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER); + if (status != GL.GL_FRAMEBUFFER_COMPLETE) { + // Need to do workarounds + fbObjectWorkarounds = true; + createNewDepthBuffer = true; + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel: ERR GL_FRAMEBUFFER_BINDING: Discovered Invalid J2D FBO("+frameBuffer[0]+"): "+FBObject.getStatusString(status) + + ", frame_buffer_object workarounds to be necessary"); + } + } else { + // Don't need the frameBufferTexture temporary any more + frameBufferTexture = null; + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel: OK GL_FRAMEBUFFER_BINDING: "+frameBuffer[0]); + } + } + } + + if (fbObjectWorkarounds && createNewDepthBuffer) { + if (frameBufferDepthBuffer == null) + frameBufferDepthBuffer = new int[1]; + + // Create our own depth renderbuffer and associated storage + // If we have an old one, delete it + if (frameBufferDepthBuffer[0] != 0) { + gl.glDeleteRenderbuffers(1, frameBufferDepthBuffer, 0); + frameBufferDepthBuffer[0] = 0; + } + + gl.glBindTexture(fboTextureTarget, frameBufferTexture[0]); + final int[] width = new int[1]; + final int[] height = new int[1]; + gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2GL3.GL_TEXTURE_WIDTH, width, 0); + gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2GL3.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, GL.GL_DEPTH_COMPONENT24, width[0], height[0]); + + gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, 0); + createNewDepthBuffer = false; + } + + gl.glBindTexture(fboTextureTarget, 0); + gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, frameBuffer[0]); + + if (fbObjectWorkarounds) { + // Hook up the color and depth buffer attachment points for this framebuffer + gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, + GL.GL_COLOR_ATTACHMENT0, + fboTextureTarget, + frameBufferTexture[0], + 0); + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel: frameBufferDepthBuffer: " + frameBufferDepthBuffer[0]); + } + gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, + GL.GL_DEPTH_ATTACHMENT, + GL.GL_RENDERBUFFER, + frameBufferDepthBuffer[0]); + } + + if (DEBUG) { + final int status = gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER); + if (status != GL.GL_FRAMEBUFFER_COMPLETE) { + throw new GLException("Error: framebuffer was incomplete: status = 0x" + + Integer.toHexString(status)); + } + } + } else { + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel: Setting up drawBuffer " + drawBuffer[0] + + " and readBuffer " + readBuffer[0]); + } + + gl.glDrawBuffer(drawBuffer[0]); + gl.glReadBuffer(readBuffer[0]); + } + + return true; + } + + @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(final Graphics g, final 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 + final GL gl = joglContext.getGL(); + gl.glFinish(); + + if (Java2D.isFBOEnabled() && + Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) { + // Unbind the framebuffer from our context to work around + // apparent driver bugs or at least unspecified behavior causing + // OpenGL to run out of memory with certain cards and drivers + gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0); + } + } + + @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 + // to the on-screen window rather than its internal "scratch" + // pbuffer surface to which the FBO is attached. JOGL expects to + // find a stable OpenGL drawable (on Windows, an HDC) upon which + // it can create another OpenGL context. It turns out that, on + // Windows, when Java 2D makes its internal OpenGL context + // current against the window in order to put pixels on the + // screen, it gets the device context for the window, makes its + // context current, and releases the device context. This means + // that when JOGL's Runnable gets to run below, the HDC is + // already invalid. The workaround for this is to force Java 2D + // to make its context current to the scratch surface, which we + // can do by executing an empty Runnable with the "shared" + // context current. This will be fixed in a Java SE 6 update + // release, hopefully 6u2. + if (Java2D.isFBOEnabled()) { + if (workaroundConfig == null) { + workaroundConfig = GraphicsEnvironment. + getLocalGraphicsEnvironment(). + getDefaultScreenDevice(). + getDefaultConfiguration(); + } + 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"); + } + + // Create no-op context representing Java2D context + if (j2dContext == null) { + j2dContext = factory.createExternalGLContext(); + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel.Created External Context: "+j2dContext); + } + if (DEBUG) { +// j2dContext.setGL(new DebugGL2(j2dContext.getGL().getGL2())); + } + + // Check to see whether we can support the requested + // capabilities or need to fall back to a pbuffer + // FIXME: add more checks? + + j2dContext.makeCurrent(); + final GL gl = j2dContext.getGL(); + if ((getGLInteger(gl, GL.GL_RED_BITS) < reqOffscreenCaps.getRedBits()) || + (getGLInteger(gl, GL.GL_GREEN_BITS) < reqOffscreenCaps.getGreenBits()) || + (getGLInteger(gl, GL.GL_BLUE_BITS) < reqOffscreenCaps.getBlueBits()) || + // (getGLInteger(gl, GL.GL_ALPHA_BITS) < offscreenCaps.getAlphaBits()) || + (getGLInteger(gl, GL2.GL_ACCUM_RED_BITS) < reqOffscreenCaps.getAccumRedBits()) || + (getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) < reqOffscreenCaps.getAccumGreenBits()) || + (getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS) < reqOffscreenCaps.getAccumBlueBits()) || + (getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) < reqOffscreenCaps.getAccumAlphaBits()) || + // (getGLInteger(gl, GL2.GL_DEPTH_BITS) < offscreenCaps.getDepthBits()) || + (getGLInteger(gl, GL.GL_STENCIL_BITS) < reqOffscreenCaps.getStencilBits())) { + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel: Falling back to pbuffer-based support because Java2D context insufficient"); + System.err.println(" Available Required"); + System.err.println("GL_RED_BITS " + getGLInteger(gl, GL.GL_RED_BITS) + " " + reqOffscreenCaps.getRedBits()); + System.err.println("GL_GREEN_BITS " + getGLInteger(gl, GL.GL_GREEN_BITS) + " " + reqOffscreenCaps.getGreenBits()); + System.err.println("GL_BLUE_BITS " + getGLInteger(gl, GL.GL_BLUE_BITS) + " " + reqOffscreenCaps.getBlueBits()); + System.err.println("GL_ALPHA_BITS " + getGLInteger(gl, GL.GL_ALPHA_BITS) + " " + reqOffscreenCaps.getAlphaBits()); + System.err.println("GL_ACCUM_RED_BITS " + getGLInteger(gl, GL2.GL_ACCUM_RED_BITS) + " " + reqOffscreenCaps.getAccumRedBits()); + System.err.println("GL_ACCUM_GREEN_BITS " + getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) + " " + reqOffscreenCaps.getAccumGreenBits()); + System.err.println("GL_ACCUM_BLUE_BITS " + getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS) + " " + reqOffscreenCaps.getAccumBlueBits()); + System.err.println("GL_ACCUM_ALPHA_BITS " + getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) + " " + reqOffscreenCaps.getAccumAlphaBits()); + System.err.println("GL_DEPTH_BITS " + getGLInteger(gl, GL.GL_DEPTH_BITS) + " " + reqOffscreenCaps.getDepthBits()); + System.err.println("GL_STENCIL_BITS " + getGLInteger(gl, GL.GL_STENCIL_BITS) + " " + reqOffscreenCaps.getStencilBits()); + } + isInitialized = false; + backend = null; + java2DGLPipelineOK = false; + handleReshape = true; + j2dContext.destroy(); + j2dContext = null; + return; + } + } else { + j2dContext.makeCurrent(); + } + try { + captureJ2DState(j2dContext.getGL(), g); + final Object curSurface = Java2D.getOGLSurfaceIdentifier(g); + if (curSurface != null) { + if (j2dSurface != curSurface) { + if (joglContext != null) { + joglContext.destroy(); + joglContext = null; + joglDrawable = null; + sendReshape = true; + if (DEBUG) { + System.err.println(getThreadName()+": Sending reshape because surface changed"); + System.err.println("New surface = " + curSurface); + } + } + j2dSurface = curSurface; + if (DEBUG) { + System.err.print(getThreadName()+": Surface type: "); + final int surfaceType = Java2D.getOGLSurfaceType(g); + if (surfaceType == Java2D.UNDEFINED) { + System.err.println("UNDEFINED"); + } else if (surfaceType == Java2D.WINDOW) { + System.err.println("WINDOW"); + } else if (surfaceType == Java2D.PBUFFER) { + System.err.println("PBUFFER"); + } else if (surfaceType == Java2D.TEXTURE) { + System.err.println("TEXTURE"); + } else if (surfaceType == Java2D.FLIP_BACKBUFFER) { + System.err.println("FLIP_BACKBUFFER"); + } else if (surfaceType == Java2D.FBOBJECT) { + System.err.println("FBOBJECT"); + } else { + System.err.println("(Unknown surface type " + surfaceType + ")"); + } + } + } + if (joglContext == null) { + final AbstractGraphicsDevice device = j2dContext.getGLDrawable().getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); + if (factory.canCreateExternalGLDrawable(device)) { + joglDrawable = factory.createExternalGLDrawable(); + joglContext = joglDrawable.createContext(j2dContext); + if (DEBUG) { + System.err.println("-- Created External Drawable: "+joglDrawable); + System.err.println("-- Created Context: "+joglContext); + } + } + if (Java2D.isFBOEnabled() && + Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT && + fbObjectWorkarounds) { + createNewDepthBuffer = true; + } + } + helper.invokeGL(joglDrawable, joglContext, updaterDisplayAction, updaterInitAction); + } + } finally { + j2dContext.release(); + } + } + }); + } + + @Override + public final void doPlainPaint() { + helper.invokeGL(joglDrawable, joglContext, updaterPlainDisplayAction, updaterInitAction); + } + + private final void captureJ2DState(final GL gl, final Graphics g) { + gl.glGetIntegerv(GL2GL3.GL_DRAW_BUFFER, drawBuffer, 0); + gl.glGetIntegerv(GL2ES3.GL_READ_BUFFER, readBuffer, 0); + if (Java2D.isFBOEnabled() && + Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) { + gl.glGetIntegerv(GL.GL_FRAMEBUFFER_BINDING, frameBuffer, 0); + if(!gl.glIsFramebuffer(frameBuffer[0])) { + checkedForFBObjectWorkarounds=true; + fbObjectWorkarounds = true; + createNewDepthBuffer = true; + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel: Fetched ERR GL_FRAMEBUFFER_BINDING: "+frameBuffer[0]+" - NOT A FBO"+ + ", frame_buffer_object workarounds to be necessary"); + } + } else if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel: Fetched OK GL_FRAMEBUFFER_BINDING: "+frameBuffer[0]); + } + + if(fbObjectWorkarounds || !checkedForFBObjectWorkarounds) { + // See above for description of what we are doing here + if (frameBufferTexture == null) + frameBufferTexture = new int[1]; + + // Query the framebuffer for its color buffer so we can hook + // it back up in our context (should not be necessary) + gl.glGetFramebufferAttachmentParameteriv(GL.GL_FRAMEBUFFER, + GL.GL_COLOR_ATTACHMENT0, + GL.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + frameBufferTexture, 0); + if (DEBUG) { + System.err.println(getThreadName()+": GLJPanel: FBO COLOR_ATTACHMENT0: " + frameBufferTexture[0]); + } + } + + if (!checkedGLVendor) { + checkedGLVendor = true; + final String vendor = gl.glGetString(GL.GL_VENDOR); + + if ((vendor != null) && + vendor.startsWith("ATI")) { + vendorIsATI = true; + } + } + + if (vendorIsATI) { + // Unbind the FBO from Java2D's context as it appears that + // driver bugs on ATI's side are causing problems if the FBO is + // simultaneously bound to more than one context. Java2D will + // re-bind the FBO during the next validation of its context. + // Note: this breaks rendering at least on NVidia hardware + gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0); + } + } + } + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/fixedfunc/GLLightingFunc.java b/src/jogl/classes/com/jogamp/opengl/fixedfunc/GLLightingFunc.java new file mode 100644 index 000000000..cf7b9da46 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/fixedfunc/GLLightingFunc.java @@ -0,0 +1,70 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.fixedfunc; + +public interface GLLightingFunc { + public static final int GL_LIGHT0 = 0x4000; + public static final int GL_LIGHT1 = 0x4001; + public static final int GL_LIGHT2 = 0x4002; + public static final int GL_LIGHT3 = 0x4003; + public static final int GL_LIGHT4 = 0x4004; + public static final int GL_LIGHT5 = 0x4005; + public static final int GL_LIGHT6 = 0x4006; + public static final int GL_LIGHT7 = 0x4007; + public static final int GL_LIGHTING = 0xB50; + public static final int GL_AMBIENT = 0x1200; + public static final int GL_DIFFUSE = 0x1201; + public static final int GL_SPECULAR = 0x1202; + public static final int GL_POSITION = 0x1203; + public static final int GL_SPOT_DIRECTION = 0x1204; + public static final int GL_SPOT_EXPONENT = 0x1205; + public static final int GL_SPOT_CUTOFF = 0x1206; + public static final int GL_CONSTANT_ATTENUATION = 0x1207; + public static final int GL_LINEAR_ATTENUATION = 0x1208; + public static final int GL_QUADRATIC_ATTENUATION = 0x1209; + public static final int GL_EMISSION = 0x1600; + public static final int GL_SHININESS = 0x1601; + public static final int GL_AMBIENT_AND_DIFFUSE = 0x1602; + public static final int GL_COLOR_MATERIAL = 0xB57; + public static final int GL_NORMALIZE = 0xBA1; + + public static final int GL_FLAT = 0x1D00; + public static final int GL_SMOOTH = 0x1D01; + + public void glLightfv(int light, int pname, java.nio.FloatBuffer params); + public void glLightfv(int light, int pname, float[] params, int params_offset); + public void glMaterialf(int face, int pname, float param); + public void glMaterialfv(int face, int pname, java.nio.FloatBuffer params); + public void glMaterialfv(int face, int pname, float[] params, int params_offset); + public void glColor4f(float red, float green, float blue, float alpha); + public void glShadeModel(int mode); + +} + diff --git a/src/jogl/classes/com/jogamp/opengl/fixedfunc/GLMatrixFunc.java b/src/jogl/classes/com/jogamp/opengl/fixedfunc/GLMatrixFunc.java new file mode 100644 index 000000000..90f13faf1 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/fixedfunc/GLMatrixFunc.java @@ -0,0 +1,152 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.fixedfunc; + +import java.nio.*; + +import com.jogamp.opengl.GL; + +/** + * Subset of OpenGL fixed function pipeline's matrix operations. + */ +public interface GLMatrixFunc { + + 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: [c] = [c] x [m] + * @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: [c] = [c] x [m] + */ + 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/com/jogamp/opengl/fixedfunc/GLPointerFunc.java b/src/jogl/classes/com/jogamp/opengl/fixedfunc/GLPointerFunc.java new file mode 100644 index 000000000..852d4ebba --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/fixedfunc/GLPointerFunc.java @@ -0,0 +1,61 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.fixedfunc; + +import com.jogamp.opengl.*; + +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; + public static final int GL_TEXTURE_COORD_ARRAY = 0x8078; + + public void glEnableClientState(int arrayName); + public void glDisableClientState(int arrayName); + + public void glVertexPointer(GLArrayData array); + public void glVertexPointer(int size, int type, int stride, java.nio.Buffer pointer); + public void glVertexPointer(int size, int type, int stride, long pointer_buffer_offset); + + public void glColorPointer(GLArrayData array); + public void glColorPointer(int size, int type, int stride, java.nio.Buffer pointer); + public void glColorPointer(int size, int type, int stride, long pointer_buffer_offset); + public void glColor4f(float red, float green, float blue, float alpha); + + public void glNormalPointer(GLArrayData array); + public void glNormalPointer(int type, int stride, java.nio.Buffer pointer); + public void glNormalPointer(int type, int stride, long pointer_buffer_offset); + + public void glTexCoordPointer(GLArrayData array); + public void glTexCoordPointer(int size, int type, int stride, java.nio.Buffer pointer); + public void glTexCoordPointer(int size, int type, int stride, long pointer_buffer_offset); + +} + diff --git a/src/jogl/classes/com/jogamp/opengl/fixedfunc/GLPointerFuncUtil.java b/src/jogl/classes/com/jogamp/opengl/fixedfunc/GLPointerFuncUtil.java new file mode 100644 index 000000000..cb1ff3827 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/fixedfunc/GLPointerFuncUtil.java @@ -0,0 +1,68 @@ +/* + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.fixedfunc; + +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"; + public static final String mgl_MultiTexCoord = "mgl_MultiTexCoord" ; + public static final String mgl_InterleaveArray = "mgl_InterleaveArray" ; // magic name for interleaved arrays w/ sub-arrays + + /** + * @param glArrayIndex the fixed function array index + * @return default fixed function array name + */ + public static String getPredefinedArrayIndexName(final 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(final int glArrayIndex, final int multiTexCoordIndex) { + switch(glArrayIndex) { + case GLPointerFunc.GL_VERTEX_ARRAY: + return mgl_Vertex; + case GLPointerFunc.GL_NORMAL_ARRAY: + return mgl_Normal; + case GLPointerFunc.GL_COLOR_ARRAY: + return mgl_Color; + case GLPointerFunc.GL_TEXTURE_COORD_ARRAY: + if(0<=multiTexCoordIndex) { + return mgl_MultiTexCoord+multiTexCoordIndex; + } else { + return mgl_MultiTexCoord+multiTexCoordIndex; + } + } + return null; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/glu/GLUnurbs.java b/src/jogl/classes/com/jogamp/opengl/glu/GLUnurbs.java new file mode 100644 index 000000000..0dbd54674 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/glu/GLUnurbs.java @@ -0,0 +1,8 @@ +package com.jogamp.opengl.glu; + +/** + * Wrapper for a GLU NURBS object. + */ + +public interface GLUnurbs { +} diff --git a/src/jogl/classes/com/jogamp/opengl/glu/GLUquadric.java b/src/jogl/classes/com/jogamp/opengl/glu/GLUquadric.java new file mode 100644 index 000000000..45b2d054d --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/glu/GLUquadric.java @@ -0,0 +1,33 @@ +package com.jogamp.opengl.glu; + +import com.jogamp.opengl.GL; +import com.jogamp.opengl.util.ImmModeSink; + +/** + * Wrapper for a GLU quadric object. + */ + +public interface GLUquadric { + // enable/disables the Immediate Mode Sink module. + // This defaults to false for GLUgl2, + // and is always true for GLUes1. + public void enableImmModeSink(boolean val); + + public boolean isImmModeSinkEnabled(); + + // set Immediate Mode usage. + // This defaults to false at GLU creation time. + // If enabled rendering will happen immediately, + // otherwise rendering will be hold in the ImmModeSink + // object, to be rendered deferred. + public void setImmMode(boolean val); + + public boolean getImmMode(); + + // creates a new ImmModeSink (VBO Buffers) and + // returns the old vbo buffer with it's rendering result + public ImmModeSink replaceImmModeSink(); + + // gl may be null, then the GL client states are not disabled + public void resetImmModeSink(GL gl); +} diff --git a/src/jogl/classes/com/jogamp/opengl/glu/GLUtessellator.java b/src/jogl/classes/com/jogamp/opengl/glu/GLUtessellator.java new file mode 100644 index 000000000..ce9fda8e7 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/glu/GLUtessellator.java @@ -0,0 +1,66 @@ +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 2.0 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package com.jogamp.opengl.glu; + +/** + * The <b>GLUtessellator</b> object is used to hold the data, such as the + * vertices, edges and callback objects, to describe and tessellate complex + * polygons. A <b>GLUtessellator</b> object is used with the + * {@link GLU GLU} tessellator methods and + * {@link GLUtessellatorCallback GLU callbacks}. + * + * @author Eric Veach, July 1994 + * @author Java Port: Pepijn Van Eechhoudt, July 2003 + * @author Java Port: Nathan Parker Burg, August 2003 + */ +public interface GLUtessellator {} diff --git a/src/jogl/classes/com/jogamp/opengl/glu/GLUtessellatorCallback.java b/src/jogl/classes/com/jogamp/opengl/glu/GLUtessellatorCallback.java new file mode 100644 index 000000000..c616bca38 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/glu/GLUtessellatorCallback.java @@ -0,0 +1,356 @@ +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 2.0 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package com.jogamp.opengl.glu; + +/** + * <b>GLUtessellatorCallback</b> interface provides methods that the user will + * override to define the callbacks for a tessellation object. + * + * @author Eric Veach, July 1994 + * @author Java Port: Pepijn Van Eeckhoudt, July 2003 + * @author Java Port: Nathan Parker Burg, August 2003 + */ +public interface GLUtessellatorCallback { + /** + * The <b>begin</b> callback method is invoked like + * {@link com.jogamp.opengl.GL#glBegin glBegin} to indicate the start of a + * (triangle) primitive. The method takes a single argument of type int. If + * the <b>GLU_TESS_BOUNDARY_ONLY</b> property is set to <b>GL_FALSE</b>, then + * the argument is set to either <b>GL_TRIANGLE_FAN</b>, + * <b>GL_TRIANGLE_STRIP</b>, or <b>GL_TRIANGLES</b>. If the + * <b>GLU_TESS_BOUNDARY_ONLY</b> property is set to <b>GL_TRUE</b>, then the + * argument will be set to <b>GL_LINE_LOOP</b>. + * + * @param type + * Specifics the type of begin/end pair being defined. The following + * values are valid: <b>GL_TRIANGLE_FAN</b>, <b>GL_TRIANGLE_STRIP</b>, + * <b>GL_TRIANGLES</b> or <b>GL_LINE_LOOP</b>. + * + * @see GLU#gluTessCallback gluTessCallback + * @see #end end + * @see #begin begin + */ + public void begin(int type); + + /** + * The same as the {@link #begin begin} callback method except that + * it takes an additional reference argument. This reference is + * identical to the opaque reference provided when {@link + * GLU#gluTessBeginPolygon gluTessBeginPolygon} was called. + * + * @param type + * Specifics the type of begin/end pair being defined. The following + * values are valid: <b>GL_TRIANGLE_FAN</b>, <b>GL_TRIANGLE_STRIP</b>, + * <b>GL_TRIANGLES</b> or <b>GL_LINE_LOOP</b>. + * @param polygonData + * Specifics a reference to user-defined data. + * + * @see GLU#gluTessCallback gluTessCallback + * @see #endData endData + * @see #begin begin + */ + public void beginData(int type, Object polygonData); + + + /** + * The <b>edgeFlag</b> callback method is similar to + * {@link com.jogamp.opengl.GL#glEdgeFlag glEdgeFlag}. The method takes + * a single boolean boundaryEdge that indicates which edges lie on the + * polygon boundary. If the boundaryEdge is <b>GL_TRUE</b>, then each vertex + * that follows begins an edge that lies on the polygon boundary, that is, + * an edge that separates an interior region from an exterior one. If the + * boundaryEdge is <b>GL_FALSE</b>, then each vertex that follows begins an + * edge that lies in the polygon interior. The edge flag callback (if + * defined) is invoked before the first vertex callback.<P> + * + * Since triangle fans and triangle strips do not support edge flags, the + * begin callback is not called with <b>GL_TRIANGLE_FAN</b> or + * <b>GL_TRIANGLE_STRIP</b> if a non-null edge flag callback is provided. + * (If the callback is initialized to null, there is no impact on + * performance). Instead, the fans and strips are converted to independent + * triangles. + * + * @param boundaryEdge + * Specifics which edges lie on the polygon boundary. + * + * @see GLU#gluTessCallback gluTessCallback + * @see #edgeFlagData edgeFlagData + */ + public void edgeFlag(boolean boundaryEdge); + + + /** + * The same as the {@link #edgeFlag edgeFlage} callback method + * except that it takes an additional reference argument. This + * reference is identical to the opaque reference provided when + * {@link GLU#gluTessBeginPolygon gluTessBeginPolygon} was called. + * + * @param boundaryEdge + * Specifics which edges lie on the polygon boundary. + * @param polygonData + * Specifics a reference to user-defined data. + * + * @see GLU#gluTessCallback gluTessCallback + * @see #edgeFlag edgeFlag + */ + public void edgeFlagData(boolean boundaryEdge, Object polygonData); + + + /** + * The <b>vertex</b> callback method is invoked between the {@link + * #begin begin} and {@link #end end} callback methods. It is + * similar to {@link com.jogamp.opengl.GL#glVertex3f glVertex3f}, + * and it defines the vertices of the triangles created by the + * tessellation process. The method takes a reference as its only + * argument. This reference is identical to the opaque reference + * provided by the user when the vertex was described (see {@link + * GLU#gluTessVertex gluTessVertex}). + * + * @param vertexData + * Specifics a reference to the vertices of the triangles created + * by the tessellation process. + * + * @see GLU#gluTessCallback gluTessCallback + * @see #vertexData vertexData + */ + public void vertex(Object vertexData); + + + /** + * The same as the {@link #vertex vertex} callback method except + * that it takes an additional reference argument. This reference is + * identical to the opaque reference provided when {@link + * GLU#gluTessBeginPolygon gluTessBeginPolygon} was called. + * + * @param vertexData + * Specifics a reference to the vertices of the triangles created + * by the tessellation process. + * @param polygonData + * Specifics a reference to user-defined data. + * + * @see GLU#gluTessCallback gluTessCallback + * @see #vertex vertex + */ + public void vertexData(Object vertexData, Object polygonData); + + + /** + * The end callback serves the same purpose as + * {@link com.jogamp.opengl.GL#glEnd glEnd}. It indicates the end of a + * primitive and it takes no arguments. + * + * @see GLU#gluTessCallback gluTessCallback + * @see #begin begin + * @see #endData endData + */ + public void end(); + + + /** + * The same as the {@link #end end} callback method except that it + * takes an additional reference argument. This reference is + * identical to the opaque reference provided when {@link + * GLU#gluTessBeginPolygon gluTessBeginPolygon} was called. + * + * @param polygonData + * Specifics a reference to user-defined data. + * + * @see GLU#gluTessCallback gluTessCallback + * @see #beginData beginData + * @see #end end + */ + public void endData(Object polygonData); + + + /** + * The <b>combine</b> callback method is called to create a new vertex when + * the tessellation detects an intersection, or wishes to merge features. The + * method takes four arguments: an array of three elements each of type + * double, an array of four references, an array of four elements each of + * type float, and a reference to a reference.<P> + * + * The vertex is defined as a linear combination of up to four existing + * vertices, stored in <i>data</i>. The coefficients of the linear combination + * are given by <i>weight</i>; these weights always add up to 1. All vertex + * pointers are valid even when some of the weights are 0. <i>coords</i> gives + * the location of the new vertex.<P> + * + * The user must allocate another vertex, interpolate parameters using + * <i>data</i> and <i>weight</i>, and return the new vertex pointer in + * <i>outData</i>. This handle is supplied during rendering callbacks. The + * user is responsible for freeing the memory some time after + * {@link GLU#gluTessEndPolygon gluTessEndPolygon} is + * called.<P> + * + * For example, if the polygon lies in an arbitrary plane in 3-space, and a + * color is associated with each vertex, the <b>GLU_TESS_COMBINE</b> + * callback might look like this: + * </UL> + * <PRE> + * void myCombine(double[] coords, Object[] data, + * float[] weight, Object[] outData) + * { + * MyVertex newVertex = new MyVertex(); + * + * newVertex.x = coords[0]; + * newVertex.y = coords[1]; + * newVertex.z = coords[2]; + * newVertex.r = weight[0]*data[0].r + + * weight[1]*data[1].r + + * weight[2]*data[2].r + + * weight[3]*data[3].r; + * newVertex.g = weight[0]*data[0].g + + * weight[1]*data[1].g + + * weight[2]*data[2].g + + * weight[3]*data[3].g; + * newVertex.b = weight[0]*data[0].b + + * weight[1]*data[1].b + + * weight[2]*data[2].b + + * weight[3]*data[3].b; + * newVertex.a = weight[0]*data[0].a + + * weight[1]*data[1].a + + * weight[2]*data[2].a + + * weight[3]*data[3].a; + * outData = newVertex; + * }</PRE> + * + * @param coords + * Specifics the location of the new vertex. + * @param data + * Specifics the vertices used to create the new vertex. + * @param weight + * Specifics the weights used to create the new vertex. + * @param outData + * Reference user the put the coodinates of the new vertex. + * + * @see GLU#gluTessCallback gluTessCallback + * @see #combineData combineData + */ + public void combine(double[] coords, Object[] data, + float[] weight, Object[] outData); + + + /** + * The same as the {@link #combine combine} callback method except + * that it takes an additional reference argument. This reference is + * identical to the opaque reference provided when {@link + * GLU#gluTessBeginPolygon gluTessBeginPolygon} was called. + * + * @param coords + * Specifics the location of the new vertex. + * @param data + * Specifics the vertices used to create the new vertex. + * @param weight + * Specifics the weights used to create the new vertex. + * @param outData + * Reference user the put the coodinates of the new vertex. + * @param polygonData + * Specifics a reference to user-defined data. + * + * @see GLU#gluTessCallback gluTessCallback + * @see #combine combine + */ + public void combineData(double[] coords, Object[] data, + float[] weight, Object[] outData, + Object polygonData); + + + /** + * The <b>error</b> callback method is called when an error is encountered. + * The one argument is of type int; it indicates the specific error that + * occurred and will be set to one of <b>GLU_TESS_MISSING_BEGIN_POLYGON</b>, + * <b>GLU_TESS_MISSING_END_POLYGON</b>, <b>GLU_TESS_MISSING_BEGIN_CONTOUR</b>, + * <b>GLU_TESS_MISSING_END_CONTOUR</b>, <b>GLU_TESS_COORD_TOO_LARGE</b>, + * <b>GLU_TESS_NEED_COMBINE_CALLBACK</b> or <b>GLU_OUT_OF_MEMORY</b>. + * Character strings describing these errors can be retrieved with the + * {@link GLU#gluErrorString gluErrorString} call.<P> + * + * The GLU library will recover from the first four errors by inserting the + * missing call(s). <b>GLU_TESS_COORD_TOO_LARGE</b> indicates that some + * vertex coordinate exceeded the predefined constant + * <b>GLU_TESS_MAX_COORD</b> in absolute value, and that the value has been + * clamped. (Coordinate values must be small enough so that two can be + * multiplied together without overflow.) + * <b>GLU_TESS_NEED_COMBINE_CALLBACK</b> indicates that the tessellation + * detected an intersection between two edges in the input data, and the + * <b>GLU_TESS_COMBINE</b> or <b>GLU_TESS_COMBINE_DATA</b> callback was not + * provided. No output is generated. <b>GLU_OUT_OF_MEMORY</b> indicates that + * there is not enough memory so no output is generated. + * + * @param errnum + * Specifics the error number code. + * + * @see GLU#gluTessCallback gluTessCallback + * @see #errorData errorData + */ + public void error(int errnum); + + + /** + * The same as the {@link #error error} callback method except that + * it takes an additional reference argument. This reference is + * identical to the opaque reference provided when {@link + * GLU#gluTessBeginPolygon gluTessBeginPolygon} was called. + * + * @param errnum + * Specifics the error number code. + * @param polygonData + * Specifics a reference to user-defined data. + * + * @see GLU#gluTessCallback gluTessCallback + * @see #error error + */ + public void errorData(int errnum, Object polygonData); + + //void mesh(jogamp.opengl.tessellator.GLUmesh mesh); +} diff --git a/src/jogl/classes/com/jogamp/opengl/glu/GLUtessellatorCallbackAdapter.java b/src/jogl/classes/com/jogamp/opengl/glu/GLUtessellatorCallbackAdapter.java new file mode 100644 index 000000000..b9503f12e --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/glu/GLUtessellatorCallbackAdapter.java @@ -0,0 +1,96 @@ +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 2.0 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package com.jogamp.opengl.glu; + +/** + * The <b>GLUtessellatorCallbackAdapter</b> provides a default implementation of + * {@link GLUtessellatorCallback GLUtessellatorCallback} + * with empty callback methods. This class can be extended to provide user + * defined callback methods. + * + * @author Eric Veach, July 1994 + * @author Java Port: Pepijn Van Eechhoudt, July 2003 + * @author Java Port: Nathan Parker Burg, August 2003 + */ + +public class GLUtessellatorCallbackAdapter implements GLUtessellatorCallback { + @Override + public void begin(final int type) {} + @Override + public void edgeFlag(final boolean boundaryEdge) {} + @Override + public void vertex(final Object vertexData) {} + @Override + public void end() {} +// public void mesh(jogamp.opengl.tessellator.GLUmesh mesh) {} + @Override + public void error(final int errnum) {} + @Override + public void combine(final double[] coords, final Object[] data, + final float[] weight, final Object[] outData) {} + @Override + public void beginData(final int type, final Object polygonData) {} + @Override + public void edgeFlagData(final boolean boundaryEdge, + final Object polygonData) {} + @Override + public void vertexData(final Object vertexData, final Object polygonData) {} + @Override + public void endData(final Object polygonData) {} + @Override + public void errorData(final int errnum, final Object polygonData) {} + @Override + public void combineData(final double[] coords, final Object[] data, + final float[] weight, final Object[] outData, + final Object polygonData) {} +} |