Jogamp
e712f2fa2d2399190f1766d4d19c433447a177d5
[jogl.git] / src / jogl / classes / javax / media / opengl / awt / GLJPanel.java
1 /*
2  * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
3  * Copyright (c) 2010 JogAmp Community. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * - Redistribution of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * - Redistribution in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of Sun Microsystems, Inc. or the names of
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * This software is provided "AS IS," without a warranty of any kind. ALL
21  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
22  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
23  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
24  * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
25  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
26  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
27  * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
28  * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
29  * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
30  * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
31  * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32  *
33  * You acknowledge that this software is not designed or intended for use
34  * in the design, construction, operation or maintenance of any nuclear
35  * facility.
36  *
37  * Sun gratefully acknowledges that this software was originally authored
38  * and developed by Kenneth Bradley Russell and Christopher John Kline.
39  */
40
41 package javax.media.opengl.awt;
42
43 import java.awt.Color;
44 import java.awt.EventQueue;
45 import java.awt.FontMetrics;
46 import java.awt.Graphics;
47 import java.awt.Graphics2D;
48 import java.awt.GraphicsConfiguration;
49 import java.awt.GraphicsEnvironment;
50 import java.awt.Rectangle;
51 import java.awt.event.HierarchyEvent;
52 import java.awt.event.HierarchyListener;
53 import java.awt.geom.NoninvertibleTransformException;
54 import java.awt.geom.Rectangle2D;
55 import java.awt.image.BufferedImage;
56 import java.awt.image.DataBufferInt;
57 import java.beans.Beans;
58 import java.nio.IntBuffer;
59 import java.util.List;
60
61 import javax.media.nativewindow.AbstractGraphicsDevice;
62 import javax.media.nativewindow.NativeSurface;
63 import javax.media.nativewindow.SurfaceUpdatedListener;
64 import javax.media.nativewindow.WindowClosingProtocol;
65 import javax.media.opengl.GL;
66 import javax.media.opengl.GL2;
67 import javax.media.opengl.GL2ES3;
68 import javax.media.opengl.GL2GL3;
69 import javax.media.opengl.GLAnimatorControl;
70 import javax.media.opengl.GLAutoDrawable;
71 import javax.media.opengl.GLCapabilities;
72 import javax.media.opengl.GLCapabilitiesChooser;
73 import javax.media.opengl.GLCapabilitiesImmutable;
74 import javax.media.opengl.GLContext;
75 import javax.media.opengl.GLDrawable;
76 import javax.media.opengl.GLDrawableFactory;
77 import javax.media.opengl.GLEventListener;
78 import javax.media.opengl.GLException;
79 import javax.media.opengl.GLFBODrawable;
80 import javax.media.opengl.GLProfile;
81 import javax.media.opengl.GLRunnable;
82 import javax.media.opengl.GLSharedContextSetter;
83 import javax.media.opengl.Threading;
84 import javax.swing.JPanel;
85
86 import jogamp.nativewindow.jawt.JAWTUtil;
87 import jogamp.opengl.Debug;
88 import jogamp.opengl.GLContextImpl;
89 import jogamp.opengl.GLDrawableFactoryImpl;
90 import jogamp.opengl.GLDrawableHelper;
91 import jogamp.opengl.GLDrawableImpl;
92 import jogamp.opengl.awt.AWTTilePainter;
93 import jogamp.opengl.awt.Java2D;
94 import jogamp.opengl.util.glsl.GLSLTextureRaster;
95
96 import com.jogamp.common.util.awt.AWTEDTExecutor;
97 import com.jogamp.nativewindow.awt.AWTPrintLifecycle;
98 import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol;
99 import com.jogamp.opengl.FBObject;
100 import com.jogamp.opengl.GLRendererQuirks;
101 import com.jogamp.opengl.util.GLPixelBuffer.GLPixelAttributes;
102 import com.jogamp.opengl.util.GLPixelBuffer.SingletonGLPixelBufferProvider;
103 import com.jogamp.opengl.util.GLDrawableUtil;
104 import com.jogamp.opengl.util.GLPixelStorageModes;
105 import com.jogamp.opengl.util.TileRenderer;
106 import com.jogamp.opengl.util.awt.AWTGLPixelBuffer;
107 import com.jogamp.opengl.util.awt.AWTGLPixelBuffer.AWTGLPixelBufferProvider;
108 import com.jogamp.opengl.util.awt.AWTGLPixelBuffer.SingleAWTGLPixelBufferProvider;
109 import com.jogamp.opengl.util.texture.TextureState;
110
111 /** A lightweight Swing component which provides OpenGL rendering
112     support. Provided for compatibility with Swing user interfaces
113     when adding a heavyweight doesn't work either because of
114     Z-ordering or LayoutManager problems.
115     <p>
116     The GLJPanel can be made transparent by creating it with a
117     GLCapabilities object with alpha bits specified and calling {@link
118     #setOpaque}(false). Pixels with resulting OpenGL alpha values less
119     than 1.0 will be overlaid on any underlying Swing rendering.
120     </p>
121     <p>
122     This component attempts to use hardware-accelerated rendering via FBO or pbuffers and
123     falls back on to software rendering if none of the former are available
124     using {@link GLDrawableFactory#createOffscreenDrawable(AbstractGraphicsDevice, GLCapabilitiesImmutable, GLCapabilitiesChooser, int, int) GLDrawableFactory.createOffscreenDrawable(..)}.<br/>
125     </p>
126     <p>
127     <a name="verticalFlip">
128     In case</a> the drawable {@link #isGLOriented()} and {@link #setSkipGLOrientationVerticalFlip(boolean) vertical flip is not skipped},
129     this component performs the required vertical flip to bring the content from OpenGL's orientation into AWT's orientation.
130     See details about <a href="#fboGLSLVerticalFlip">FBO and GLSL vertical flipping</a>.
131     </p>
132     <p>
133     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(..)}
134     for later Java2D composition.
135     </p>
136     <p>
137     In case {@link #setSkipGLOrientationVerticalFlip(boolean) vertical-flip is not skipped} and <a href="#fboGLSLVerticalFlip">GLSL based vertical-flip</a> is not performed,
138     {@link System#arraycopy(Object, int, Object, int, int) System.arraycopy(..)} is used line by line.
139     This step causes more CPU load per frame and is not hardware-accelerated.
140     </p>
141     <p>
142     Finally the Java2D compositioning takes place via via {@link Graphics#drawImage(java.awt.Image, int, int, int, int, java.awt.image.ImageObserver) Graphics.drawImage(...)}
143     on the prepared {@link BufferedImage} as described above.
144     </p>
145     <P>
146  *  Please read <a href="GLCanvas.html#java2dgl">Java2D OpenGL Remarks</a>.
147  *  </P>
148  *
149     <a name="fboGLSLVerticalFlip"><h5>FBO / GLSL Vertical Flip</h5></a>
150     In case FBO is used and GLSL is available and {@link #setSkipGLOrientationVerticalFlip(boolean) vertical flip is not skipped}, a fragment shader is utilized
151     to flip the FBO texture vertically. This hardware-accelerated step can be disabled via system property <code>jogl.gljpanel.noglsl</code>.
152     <p>
153     The FBO / GLSL code path uses one texture-unit and binds the FBO texture to it's active texture-target,
154     see {@link #setTextureUnit(int)} and {@link #getTextureUnit()}.
155     </p>
156     <p>
157     The active and dedicated texture-unit's {@link GL#GL_TEXTURE_2D} state is preserved via {@link TextureState}.
158     See also {@link Texture#textureCallOrder Order of Texture Commands}.
159     </p>
160     <p>
161     The current gl-viewport is preserved.
162     </p>
163     <p>
164     <i>Warning (Bug 842)</i>: Certain GL states other than viewport and texture (see above)
165     influencing rendering, will also influence the GLSL vertical flip, e.g. {@link GL#glFrontFace(int) glFrontFace}({@link GL#GL_CCW}).
166     It is recommended to reset those states to default when leaving the {@link GLEventListener#display(GLAutoDrawable)} method!
167     We may change this behavior in the future, i.e. preserve all influencing states.
168     </p>
169 */
170
171 @SuppressWarnings("serial")
172 public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosingProtocol, AWTPrintLifecycle, GLSharedContextSetter {
173   private static final boolean DEBUG;
174   private static final boolean DEBUG_VIEWPORT;
175   private static final boolean USE_GLSL_TEXTURE_RASTERIZER;
176   private static final boolean SKIP_VERTICAL_FLIP_DEFAULT;
177
178   /** Indicates whether the Java 2D OpenGL pipeline is requested by user. */
179   private static final boolean java2dOGLEnabledByProp;
180
181   /** Indicates whether the Java 2D OpenGL pipeline is enabled, resource-compatible and requested by user. */
182   private static final boolean useJava2DGLPipeline;
183
184   /** Indicates whether the Java 2D OpenGL pipeline's usage is error free. */
185   private static boolean java2DGLPipelineOK;
186
187   static {
188       Debug.initSingleton();
189       DEBUG = Debug.debug("GLJPanel");
190       DEBUG_VIEWPORT = Debug.isPropertyDefined("jogl.debug.GLJPanel.Viewport", true);
191       USE_GLSL_TEXTURE_RASTERIZER = !Debug.isPropertyDefined("jogl.gljpanel.noglsl", true);
192       SKIP_VERTICAL_FLIP_DEFAULT = Debug.isPropertyDefined("jogl.gljpanel.noverticalflip", true);
193       boolean enabled = Debug.getBooleanProperty("sun.java2d.opengl", false);
194       java2dOGLEnabledByProp = enabled && !Debug.isPropertyDefined("jogl.gljpanel.noogl", true);
195
196       enabled = false;
197       if( java2dOGLEnabledByProp ) {
198           // Force eager initialization of part of the Java2D class since
199           // otherwise it's likely it will try to be initialized while on
200           // the Queue Flusher Thread, which is not allowed
201           if (Java2D.isOGLPipelineResourceCompatible() && Java2D.isFBOEnabled()) {
202               if( null != Java2D.getShareContext(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()) ) {
203                   enabled = true;
204               }
205           }
206       }
207       useJava2DGLPipeline = enabled;
208       java2DGLPipelineOK = enabled;
209       if( DEBUG ) {
210           System.err.println("GLJPanel: DEBUG_VIEWPORT "+DEBUG_VIEWPORT);
211           System.err.println("GLJPanel: USE_GLSL_TEXTURE_RASTERIZER "+USE_GLSL_TEXTURE_RASTERIZER);
212           System.err.println("GLJPanel: SKIP_VERTICAL_FLIP_DEFAULT "+SKIP_VERTICAL_FLIP_DEFAULT);
213           System.err.println("GLJPanel: java2dOGLEnabledByProp "+java2dOGLEnabledByProp);
214           System.err.println("GLJPanel: useJava2DGLPipeline "+useJava2DGLPipeline);
215           System.err.println("GLJPanel: java2DGLPipelineOK "+java2DGLPipelineOK);
216       }
217   }
218
219   private static SingleAWTGLPixelBufferProvider singleAWTGLPixelBufferProvider = null;
220   private static synchronized SingleAWTGLPixelBufferProvider getSingleAWTGLPixelBufferProvider() {
221       if( null == singleAWTGLPixelBufferProvider ) {
222           singleAWTGLPixelBufferProvider = new SingleAWTGLPixelBufferProvider( true /* allowRowStride */ );
223       }
224       return singleAWTGLPixelBufferProvider;
225   }
226
227   private final GLDrawableHelper helper;
228   private boolean autoSwapBufferMode;
229
230   private volatile boolean isInitialized;
231
232   //
233   // Data used for either pbuffers or pixmap-based offscreen surfaces
234   //
235   private AWTGLPixelBufferProvider customPixelBufferProvider = null;
236   /** Single buffered offscreen caps */
237   private GLCapabilitiesImmutable offscreenCaps;
238   private final GLProfile             glProfile;
239   private final GLDrawableFactoryImpl factory;
240   private final GLCapabilitiesChooser chooser;
241   private int additionalCtxCreationFlags = 0;
242
243   // Lazy reshape notification: reshapeWidth -> panelWidth -> backend.width
244   private boolean handleReshape = false;
245   private boolean sendReshape = true;
246
247   private volatile int pixelScale;
248
249   // For handling reshape events lazily: reshapeWidth -> panelWidth -> backend.width
250   private int reshapeWidth;
251   private int reshapeHeight;
252
253   // Width of the actual GLJPanel: reshapeWidth -> panelWidth -> backend.width
254   private int panelWidth   = 0;
255   private int panelHeight  = 0;
256
257   // These are always set to (0, 0) except when the Java2D / OpenGL
258   // pipeline is active
259   private int viewportX;
260   private int viewportY;
261
262   private int requestedTextureUnit = 0; // default
263
264   // The backend in use
265   private volatile Backend backend;
266
267   private boolean skipGLOrientationVerticalFlip = SKIP_VERTICAL_FLIP_DEFAULT;
268
269   // Used by all backends either directly or indirectly to hook up callbacks
270   private final Updater updater = new Updater();
271
272   private boolean oglPipelineUsable() {
273       return null == customPixelBufferProvider &&  useJava2DGLPipeline && java2DGLPipelineOK;
274   }
275
276   private volatile boolean isShowing;
277   private final HierarchyListener hierarchyListener = new HierarchyListener() {
278       @Override
279       public void hierarchyChanged(HierarchyEvent e) {
280           isShowing = GLJPanel.this.isShowing();
281       }
282   };
283
284   private final AWTWindowClosingProtocol awtWindowClosingProtocol =
285           new AWTWindowClosingProtocol(this, new Runnable() {
286                 @Override
287                 public void run() {
288                     GLJPanel.this.destroy();
289                 }
290             }, null);
291
292   /** Creates a new GLJPanel component with a default set of OpenGL
293       capabilities and using the default OpenGL capabilities selection
294       mechanism.
295    * @throws GLException if no default profile is available for the default desktop device.
296    */
297   public GLJPanel() throws GLException {
298     this(null);
299   }
300
301   /** Creates a new GLJPanel component with the requested set of
302       OpenGL capabilities, using the default OpenGL capabilities
303       selection mechanism.
304    * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
305    */
306   public GLJPanel(GLCapabilitiesImmutable userCapsRequest) throws GLException {
307     this(userCapsRequest, null, null);
308   }
309
310   /** Creates a new GLJPanel component. The passed GLCapabilities
311       specifies the OpenGL capabilities for the component; if null, a
312       default set of capabilities is used. The GLCapabilitiesChooser
313       specifies the algorithm for selecting one of the available
314       GLCapabilities for the component; a DefaultGLCapabilitesChooser
315       is used if null is passed for this argument.
316     * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
317   */
318   public GLJPanel(GLCapabilitiesImmutable userCapsRequest, GLCapabilitiesChooser chooser)
319           throws GLException
320   {
321       this(userCapsRequest, chooser, null);
322   }
323
324   /** Creates a new GLJPanel component. The passed GLCapabilities
325       specifies the OpenGL capabilities for the component; if null, a
326       default set of capabilities is used. The GLCapabilitiesChooser
327       specifies the algorithm for selecting one of the available
328       GLCapabilities for the component; a DefaultGLCapabilitesChooser
329       is used if null is passed for this argument. The passed
330       GLContext specifies an OpenGL context with which to share
331       textures, display lists and other OpenGL state, and may be null
332       if sharing is not desired. See the note in the overview documentation on
333       <a href="../../../spec-overview.html#SHARING">context sharing</a>.
334       <P>
335       Note: Sharing cannot be enabled using J2D OpenGL FBO sharing,
336       since J2D GL Context must be shared and we can only share one context.
337     * @throws GLException if no GLCapabilities are given and no default profile is available for the default desktop device.
338     * @deprecated Use {@link #GLJPanel(GLCapabilitiesImmutable, GLCapabilitiesChooser)}
339     *             and set shared GLContext via {@link #setSharedContext(GLContext)} or {@link #setSharedAutoDrawable(GLAutoDrawable)}.
340     */
341   public GLJPanel(GLCapabilitiesImmutable userCapsRequest, GLCapabilitiesChooser chooser, GLContext shareWith)
342           throws GLException
343   {
344     super();
345
346     // Works around problems on many vendors' cards; we don't need a
347     // back buffer for the offscreen surface anyway
348     {
349         GLCapabilities caps;
350         if (userCapsRequest != null) {
351             caps = (GLCapabilities) userCapsRequest.cloneMutable();
352         } else {
353             caps = new GLCapabilities(GLProfile.getDefault(GLProfile.getDefaultDevice()));
354         }
355         caps.setDoubleBuffered(false);
356         offscreenCaps = caps;
357     }
358     this.glProfile = offscreenCaps.getGLProfile();
359     this.factory = GLDrawableFactoryImpl.getFactoryImpl(glProfile);
360     this.chooser = chooser;
361
362     helper = new GLDrawableHelper();
363     if( null != shareWith ) {
364         helper.setSharedContext(null, shareWith);
365     }
366     autoSwapBufferMode = helper.getAutoSwapBufferMode();
367
368     this.setFocusable(true); // allow keyboard input!
369     this.addHierarchyListener(hierarchyListener);
370     this.isShowing = isShowing();
371     this.pixelScale = 1;
372   }
373
374   /**
375    * Attempts to initialize the backend, if not initialized yet.
376    * <p>
377    * If backend is already initialized method returns <code>true</code>.
378    * </p>
379    * <p>
380    * If <code>offthread</code> is <code>true</code>, initialization will kicked off
381    * on a <i>short lived</i> arbitrary thread and method returns immediately.<br/>
382    * If platform supports such <i>arbitrary thread</i> initialization method returns
383    * <code>true</code>, otherwise <code>false</code>.
384    * </p>
385    * <p>
386    * If <code>offthread</code> is <code>false</code>, initialization be performed
387    * on the current thread and method returns after initialization.<br/>
388    * Method returns <code>true</code> if initialization was successful, otherwise <code>false</code>.
389    * <p>
390    * @param offthread
391    */
392   public final boolean initializeBackend(boolean offthread) {
393     if( offthread ) {
394         new Thread(getThreadName()+"-GLJPanel_Init") {
395             public void run() {
396               if( !isInitialized ) {
397                   initializeBackendImpl();
398               }
399             } }.start();
400         return true;
401     } else {
402         if( !isInitialized ) {
403             return initializeBackendImpl();
404         } else {
405             return true;
406         }
407     }
408   }
409
410   @Override
411   public final void setSharedContext(GLContext sharedContext) throws IllegalStateException {
412       helper.setSharedContext(this.getContext(), sharedContext);
413   }
414
415   @Override
416   public final void setSharedAutoDrawable(GLAutoDrawable sharedAutoDrawable) throws IllegalStateException {
417       helper.setSharedAutoDrawable(this, sharedAutoDrawable);
418   }
419
420   public AWTGLPixelBufferProvider getCustomPixelBufferProvider() { return customPixelBufferProvider; }
421
422   /**
423    * @param custom custom {@link AWTGLPixelBufferProvider}
424    * @throws IllegalArgumentException if <code>custom</code> is <code>null</code>
425    * @throws IllegalStateException if backend is already realized, i.e. this instanced already painted once.
426    */
427   public void setPixelBufferProvider(AWTGLPixelBufferProvider custom) throws IllegalArgumentException, IllegalStateException {
428       if( null == custom ) {
429           throw new IllegalArgumentException("Null PixelBufferProvider");
430       }
431       if( null != backend ) {
432           throw new IllegalStateException("Backend already realized.");
433       }
434       customPixelBufferProvider = custom;
435   }
436
437   @Override
438   public final Object getUpstreamWidget() {
439     return this;
440   }
441
442   @Override
443   public void display() {
444     if( isShowing || ( printActive && isVisible() ) ) {
445         if (EventQueue.isDispatchThread()) {
446           // Want display() to be synchronous, so call paintImmediately()
447           paintImmediately(0, 0, getWidth(), getHeight());
448         } else {
449           // Multithreaded redrawing of Swing components is not allowed,
450           // so do everything on the event dispatch thread
451           try {
452             EventQueue.invokeAndWait(paintImmediatelyAction);
453           } catch (Exception e) {
454             throw new GLException(e);
455           }
456         }
457     }
458   }
459
460   protected void dispose() {
461     if(DEBUG) {
462         System.err.println(getThreadName()+": GLJPanel.dispose() - start");
463         // Thread.dumpStack();
464     }
465
466     if (backend != null && backend.getContext() != null) {
467       boolean animatorPaused = false;
468       GLAnimatorControl animator =  getAnimator();
469       if(null!=animator) {
470         animatorPaused = animator.pause();
471       }
472
473       if(backend.getContext().isCreated()) {
474           Threading.invoke(true, disposeAction, getTreeLock());
475       }
476       if(null != backend) {
477           // not yet destroyed due to backend.isUsingOwnThreadManagment() == true
478           backend.destroy();
479           isInitialized = false;
480       }
481
482       if(animatorPaused) {
483         animator.resume();
484       }
485     }
486     pixelScale = 1;
487
488     if(DEBUG) {
489         System.err.println(getThreadName()+": GLJPanel.dispose() - stop");
490     }
491   }
492
493   /**
494    * Just an alias for removeNotify
495    */
496   @Override
497   public void destroy() {
498       removeNotify();
499   }
500
501   /** Overridden to cause OpenGL rendering to be performed during
502       repaint cycles. Subclasses which override this method must call
503       super.paintComponent() in their paintComponent() method in order
504       to function properly. <P>
505
506       <DL><DD><CODE>paintComponent</CODE> in class <CODE>javax.swing.JComponent</CODE></DD></DL> */
507   @Override
508   protected void paintComponent(final Graphics g) {
509     if (Beans.isDesignTime()) {
510       // Make GLJPanel behave better in NetBeans GUI builder
511       g.setColor(Color.BLACK);
512       g.fillRect(0, 0, getWidth(), getHeight());
513       FontMetrics fm = g.getFontMetrics();
514       String name = getName();
515       if (name == null) {
516         name = getClass().getName();
517         int idx = name.lastIndexOf('.');
518         if (idx >= 0) {
519           name = name.substring(idx + 1);
520         }
521       }
522       Rectangle2D bounds = fm.getStringBounds(name, g);
523       g.setColor(Color.WHITE);
524       g.drawString(name,
525                    (int) ((getWidth()  - bounds.getWidth())  / 2),
526                    (int) ((getHeight() + bounds.getHeight()) / 2));
527       return;
528     }
529
530     if( !isInitialized ) {
531         initializeBackendImpl();
532     }
533
534     if (!isInitialized || printActive) {
535       return;
536     }
537
538     // NOTE: must do this when the context is not current as it may
539     // involve destroying the pbuffer (current context) and
540     // re-creating it -- tricky to do properly while the context is
541     // current
542     if( !printActive ) {
543         if (handleReshape) {
544           handleReshape = false;
545           sendReshape = handleReshape();
546         }
547
548         if( isShowing ) {
549           updater.setGraphics(g);
550           backend.doPaintComponent(g);
551         }
552     }
553   }
554
555   /** Overridden to track when this component is added to a container.
556       Subclasses which override this method must call
557       super.addNotify() in their addNotify() method in order to
558       function properly. <P>
559
560       <DL><DD><CODE>addNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
561   @Override
562   public void addNotify() {
563     super.addNotify();
564     awtWindowClosingProtocol.addClosingListener();
565     {
566         final int s = JAWTUtil.getPixelScale(getGraphicsConfiguration());
567         pixelScale = 0 < s ? s : 1;
568     }
569     if (DEBUG) {
570         System.err.println(getThreadName()+": GLJPanel.addNotify()");
571     }
572   }
573
574   /** Overridden to track when this component is removed from a
575       container. Subclasses which override this method must call
576       super.removeNotify() in their removeNotify() method in order to
577       function properly. <P>
578
579       <DL><DD><CODE>removeNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */
580   @Override
581   public void removeNotify() {
582     awtWindowClosingProtocol.removeClosingListener();
583
584     dispose();
585     super.removeNotify();
586   }
587
588   /** Overridden to cause {@link GLDrawableHelper#reshape} to be
589       called on all registered {@link GLEventListener}s. Subclasses
590       which override this method must call super.reshape() in
591       their reshape() method in order to function properly. <P>
592    *
593    * {@inheritDoc}
594    */
595   @SuppressWarnings("deprecation")
596   @Override
597   public void reshape(int x, int y, int width, int height) {
598     super.reshape(x, y, width, height);
599
600     final int scale = getPixelScale();
601     final int scaledWidth = scale * width;
602     final int scaledHeight = scale * height;
603     if( DEBUG ) {
604         System.err.println(getThreadName()+": GLJPanel.reshape.0 "+this.getName()+" resize"+(printActive?"WithinPrint":"")+
605                 " [ this "+getWidth()+"x"+getHeight()+", pixelScale "+scale+
606                 ", panel "+panelWidth+"x"+panelHeight +
607                 ", reshape: " +reshapeWidth+"x"+reshapeHeight +
608                 "] -> "+(printActive?"[skipped] ":"") + width+"x"+height+" * "+scale+" -> "+scaledWidth+"x"+scaledHeight);
609     }
610     if( !printActive ) {
611         reshapeWidth = scaledWidth;
612         reshapeHeight = scaledHeight;
613         handleReshape = true;
614     }
615   }
616
617   private volatile boolean printActive = false;
618   private GLAnimatorControl printAnimator = null;
619   private GLAutoDrawable printGLAD = null;
620   private AWTTilePainter printAWTTiles = null;
621
622   @Override
623   public void setupPrint(double scaleMatX, double scaleMatY, int numSamples, int tileWidth, int tileHeight) {
624       printActive = true;
625       final int componentCount = isOpaque() ? 3 : 4;
626       final TileRenderer printRenderer = new TileRenderer();
627       printAWTTiles = new AWTTilePainter(printRenderer, componentCount, scaleMatX, scaleMatY, numSamples, tileWidth, tileHeight, DEBUG);
628       AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, setupPrintOnEDT);
629   }
630   private final Runnable setupPrintOnEDT = new Runnable() {
631       @Override
632       public void run() {
633           if( !isInitialized ) {
634               initializeBackendImpl();
635           }
636           if (!isInitialized) {
637               if(DEBUG) {
638                   System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, drawable not valid yet");
639               }
640               printActive = false;
641               return; // not yet available ..
642           }
643           if( !isVisible() ) {
644               if(DEBUG) {
645                   System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, panel not visible");
646               }
647               printActive = false;
648               return; // not yet available ..
649           }
650           sendReshape = false; // clear reshape flag
651           handleReshape = false; // ditto
652           printAnimator =  helper.getAnimator();
653           if( null != printAnimator ) {
654               printAnimator.remove(GLJPanel.this);
655           }
656
657           printGLAD = GLJPanel.this; // default: re-use
658           final GLCapabilities caps = (GLCapabilities)getChosenGLCapabilities().cloneMutable();
659           final int printNumSamples = printAWTTiles.getNumSamples(caps);
660           GLDrawable printDrawable = printGLAD.getDelegatedDrawable();
661           final boolean reqNewGLADSamples = printNumSamples != caps.getNumSamples();
662           final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() ||
663                                          printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight();
664           final boolean reqNewGLAD = reqNewGLADSamples || reqNewGLADSize ;
665           if( DEBUG ) {
666               System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ samples "+reqNewGLADSamples+", size "+reqNewGLADSize+"], "+
667                                  ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+
668                                  ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+
669                                  ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+
670                                  ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator);
671           }
672           if( reqNewGLAD ) {
673               caps.setDoubleBuffered(false);
674               caps.setOnscreen(false);
675               caps.setSampleBuffers(0 < printNumSamples);
676               caps.setNumSamples(printNumSamples);
677               final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile());
678               printGLAD = factory.createOffscreenAutoDrawable(null, caps, null,
679                       printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE,
680                       printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE);
681               GLDrawableUtil.swapGLContextAndAllGLEventListener(GLJPanel.this, printGLAD);
682               printDrawable = printGLAD.getDelegatedDrawable();
683           }
684           printAWTTiles.setGLOrientation( !GLJPanel.this.skipGLOrientationVerticalFlip && printGLAD.isGLOriented(), printGLAD.isGLOriented() );
685           printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0);
686           printAWTTiles.renderer.attachAutoDrawable(printGLAD);
687           if( DEBUG ) {
688               System.err.println("AWT print.setup "+printAWTTiles);
689               System.err.println("AWT print.setup AA "+printNumSamples+", "+caps);
690               System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD);
691               System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable);
692           }
693       }
694   };
695
696   @Override
697   public void releasePrint() {
698       if( !printActive ) {
699           throw new IllegalStateException("setupPrint() not called");
700       }
701       sendReshape = false; // clear reshape flag
702       handleReshape = false; // ditto
703       AWTEDTExecutor.singleton.invoke(getTreeLock(), true /* allowOnNonEDT */, true /* wait */, releasePrintOnEDT);
704   }
705
706   private final Runnable releasePrintOnEDT = new Runnable() {
707       @Override
708       public void run() {
709           if( DEBUG ) {
710               System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0 "+printAWTTiles);
711           }
712           printAWTTiles.dispose();
713           printAWTTiles= null;
714           if( printGLAD != GLJPanel.this ) {
715               GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLJPanel.this);
716               printGLAD.destroy();
717           }
718           printGLAD = null;
719           if( null != printAnimator ) {
720               printAnimator.add(GLJPanel.this);
721               printAnimator = null;
722           }
723
724           // trigger reshape, i.e. gl-viewport and -listener - this component might got resized!
725           final int awtWidth = GLJPanel.this.getWidth();
726           final int awtHeight= GLJPanel.this.getHeight();
727           final int scale = getPixelScale();
728           final int scaledAWTWidth = scale * awtWidth;
729           final int scaledAWTHeight= scale * awtHeight;
730           final GLDrawable drawable = GLJPanel.this.getDelegatedDrawable();
731           if( scaledAWTWidth != panelWidth || scaledAWTHeight != panelHeight ||
732               drawable.getSurfaceWidth() != panelWidth || drawable.getSurfaceHeight() != panelHeight ) {
733               // -> !( awtSize == panelSize == drawableSize )
734               if ( DEBUG ) {
735                   System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0: resizeWithinPrint panel " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScale()+
736                           ", draw "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight()+
737                           " -> " + awtWidth+"x"+awtHeight+" * "+scale+" -> "+scaledAWTWidth+"x"+scaledAWTHeight);
738               }
739               reshapeWidth = scaledAWTWidth;
740               reshapeHeight = scaledAWTHeight;
741               sendReshape = handleReshape(); // reshapeSize -> panelSize, backend reshape w/ GL reshape
742           } else {
743               sendReshape = true; // only GL reshape
744           }
745           printActive = false;
746           display();
747       }
748   };
749
750   @Override
751   public void print(Graphics graphics) {
752       if( !printActive ) {
753           throw new IllegalStateException("setupPrint() not called");
754       }
755       if(DEBUG && !EventQueue.isDispatchThread()) {
756           System.err.println(getThreadName()+": Warning: GLCanvas print - not called from AWT-EDT");
757           // we cannot dispatch print on AWT-EDT due to printing internal locking ..
758       }
759       sendReshape = false; // clear reshape flag
760       handleReshape = false; // ditto
761
762       final Graphics2D g2d = (Graphics2D)graphics;
763       try {
764           printAWTTiles.setupGraphics2DAndClipBounds(g2d, getWidth(), getHeight());
765           final TileRenderer tileRenderer = printAWTTiles.renderer;
766           if( DEBUG ) {
767               System.err.println("AWT print.0: "+tileRenderer);
768           }
769           if( !tileRenderer.eot() ) {
770               try {
771                   do {
772                       if( printGLAD != GLJPanel.this ) {
773                           tileRenderer.display();
774                       } else {
775                           backend.doPlainPaint();
776                       }
777                   } while ( !tileRenderer.eot() );
778                   if( DEBUG ) {
779                       System.err.println("AWT print.1: "+printAWTTiles);
780                   }
781               } finally {
782                   tileRenderer.reset();
783                   printAWTTiles.resetGraphics2D();
784               }
785           }
786       } catch (NoninvertibleTransformException nte) {
787           System.err.println("Catched: Inversion failed of: "+g2d.getTransform());
788           nte.printStackTrace();
789       }
790       if( DEBUG ) {
791           System.err.println("AWT print.X: "+printAWTTiles);
792       }
793   }
794   @Override
795   protected void printComponent(Graphics g) {
796       if( DEBUG ) {
797           System.err.println("AWT printComponent.X: "+printAWTTiles);
798       }
799       print(g);
800   }
801
802   @Override
803   public void setOpaque(boolean opaque) {
804     if (backend != null) {
805       backend.setOpaque(opaque);
806     }
807     super.setOpaque(opaque);
808   }
809
810   @Override
811   public void addGLEventListener(GLEventListener listener) {
812     helper.addGLEventListener(listener);
813   }
814
815   @Override
816   public void addGLEventListener(int index, GLEventListener listener) {
817     helper.addGLEventListener(index, listener);
818   }
819
820   @Override
821   public int getGLEventListenerCount() {
822       return helper.getGLEventListenerCount();
823   }
824
825   @Override
826   public GLEventListener getGLEventListener(int index) throws IndexOutOfBoundsException {
827       return helper.getGLEventListener(index);
828   }
829
830   @Override
831   public boolean areAllGLEventListenerInitialized() {
832      return helper.areAllGLEventListenerInitialized();
833   }
834
835   @Override
836   public boolean getGLEventListenerInitState(GLEventListener listener) {
837       return helper.getGLEventListenerInitState(listener);
838   }
839
840   @Override
841   public void setGLEventListenerInitState(GLEventListener listener, boolean initialized) {
842       helper.setGLEventListenerInitState(listener, initialized);
843   }
844
845   @Override
846   public GLEventListener disposeGLEventListener(GLEventListener listener, boolean remove) {
847     final DisposeGLEventListenerAction r = new DisposeGLEventListenerAction(listener, remove);
848     if (EventQueue.isDispatchThread()) {
849       r.run();
850     } else {
851       // Multithreaded redrawing of Swing components is not allowed,
852       // so do everything on the event dispatch thread
853       try {
854         EventQueue.invokeAndWait(r);
855       } catch (Exception e) {
856         throw new GLException(e);
857       }
858     }
859     return r.listener;
860   }
861
862   @Override
863   public GLEventListener removeGLEventListener(GLEventListener listener) {
864     return helper.removeGLEventListener(listener);
865   }
866
867   @Override
868   public void setAnimator(GLAnimatorControl animatorControl) {
869     helper.setAnimator(animatorControl);
870   }
871
872   @Override
873   public GLAnimatorControl getAnimator() {
874     return helper.getAnimator();
875   }
876
877   @Override
878   public final Thread setExclusiveContextThread(Thread t) throws GLException {
879       return helper.setExclusiveContextThread(t, getContext());
880   }
881
882   @Override
883   public final Thread getExclusiveContextThread() {
884       return helper.getExclusiveContextThread();
885   }
886
887   @Override
888   public boolean invoke(boolean wait, GLRunnable glRunnable) {
889     return helper.invoke(this, wait, glRunnable);
890   }
891
892   @Override
893   public boolean invoke(final boolean wait, final List<GLRunnable> glRunnables) {
894     return helper.invoke(this, wait, glRunnables);
895   }
896
897   @Override
898   public GLContext createContext(GLContext shareWith) {
899     final Backend b = backend;
900     if ( null == b ) {
901         return null;
902     }
903     return b.createContext(shareWith);
904   }
905
906   @Override
907   public void setRealized(boolean realized) {
908   }
909
910   @Override
911   public boolean isRealized() {
912       return isInitialized;
913   }
914
915   @Override
916   public GLContext setContext(GLContext newCtx, boolean destroyPrevCtx) {
917     final Backend b = backend;
918     if ( null == b ) {
919         return null;
920     }
921     final GLContext oldCtx = b.getContext();
922     GLDrawableHelper.switchContext(b.getDrawable(), oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags);
923     b.setContext(newCtx);
924     return oldCtx;
925   }
926
927
928   @Override
929   public final GLDrawable getDelegatedDrawable() {
930     final Backend b = backend;
931     if ( null == b ) {
932         return null;
933     }
934     return b.getDrawable();
935   }
936
937   @Override
938   public GLContext getContext() {
939     final Backend b = backend;
940     if ( null == b ) {
941         return null;
942     }
943     return b.getContext();
944   }
945
946   @Override
947   public GL getGL() {
948     if (Beans.isDesignTime()) {
949       return null;
950     }
951     GLContext context = getContext();
952     return (context == null) ? null : context.getGL();
953   }
954
955   @Override
956   public GL setGL(GL gl) {
957     GLContext context = getContext();
958     if (context != null) {
959       context.setGL(gl);
960       return gl;
961     }
962     return null;
963   }
964
965   @Override
966   public void setAutoSwapBufferMode(boolean enable) {
967     this.autoSwapBufferMode = enable;
968     boolean backendHandlesSwapBuffer = false;
969     if( isInitialized ) {
970         final Backend b = backend;
971         if ( null != b ) {
972             backendHandlesSwapBuffer= b.handlesSwapBuffer();
973         }
974     }
975     if( !backendHandlesSwapBuffer ) {
976         helper.setAutoSwapBufferMode(enable);
977     }
978   }
979
980   @Override
981   public boolean getAutoSwapBufferMode() {
982     return autoSwapBufferMode;
983   }
984
985   @Override
986   public void swapBuffers() {
987     if( isInitialized ) {
988         final Backend b = backend;
989         if ( null != b ) {
990             b.swapBuffers();
991         }
992     }
993   }
994
995   @Override
996   public void setContextCreationFlags(int flags) {
997     additionalCtxCreationFlags = flags;
998   }
999
1000   @Override
1001   public int getContextCreationFlags() {
1002     return additionalCtxCreationFlags;
1003   }
1004
1005   /** For a translucent GLJPanel (one for which {@link #setOpaque
1006       setOpaque}(false) has been called), indicates whether the
1007       application should preserve the OpenGL color buffer
1008       (GL_COLOR_BUFFER_BIT) for correct rendering of the GLJPanel and
1009       underlying widgets which may show through portions of the
1010       GLJPanel with alpha values less than 1.  Most Swing
1011       implementations currently expect the GLJPanel to be completely
1012       cleared (e.g., by <code>glClear(GL_COLOR_BUFFER_BIT |
1013       GL_DEPTH_BUFFER_BIT)</code>), but for certain optimized Swing
1014       implementations which use OpenGL internally, it may be possible
1015       to perform OpenGL rendering using the GLJPanel into the same
1016       OpenGL drawable as the Swing implementation uses. */
1017   public boolean shouldPreserveColorBufferIfTranslucent() {
1018     return oglPipelineUsable();
1019   }
1020
1021   @Override
1022   public int getSurfaceWidth() {
1023       return getWidth() * getPixelScale();
1024   }
1025
1026   @Override
1027   public int getSurfaceHeight() {
1028       return getHeight() * getPixelScale();
1029   }
1030
1031   /**
1032    * {@inheritDoc}
1033    * <p>
1034    * Method returns a valid value only <i>after</i>
1035    * the backend has been initialized, either {@link #initializeBackend(boolean) eagerly}
1036    * or manually via the first display call.<br/>
1037    * Method always returns a valid value when called from within a {@link GLEventListener}.
1038    * </p>
1039    */
1040   @Override
1041   public boolean isGLOriented() {
1042     final Backend b = backend;
1043     if ( null == b ) {
1044         return true;
1045     }
1046     return b.getDrawable().isGLOriented();
1047   }
1048
1049   /**
1050    * Set skipping {@link #isGLOriented()} based vertical flip,
1051    * which usually is required by the offscreen backend,
1052    * see details about <a href="#verticalFlip">vertical flip</a>
1053    * and <a href="#fboGLSLVerticalFlip">FBO / GLSL vertical flip</a>.
1054    * <p>
1055    * If set to <code>true</code>, user needs to flip the OpenGL rendered scene
1056    * <i>if {@link #isGLOriented()} == true</i>, e.g. via the PMV matrix.<br/>
1057    * See constraints of {@link #isGLOriented()}.
1058    * </p>
1059    */
1060   public final void setSkipGLOrientationVerticalFlip(boolean v) {
1061       skipGLOrientationVerticalFlip = v;
1062   }
1063   /** See {@link #setSkipGLOrientationVerticalFlip(boolean)}. */
1064   public final boolean getSkipGLOrientationVerticalFlip() {
1065       return skipGLOrientationVerticalFlip;
1066   }
1067
1068   @Override
1069   public GLCapabilitiesImmutable getChosenGLCapabilities() {
1070     final Backend b = backend;
1071     if ( null == b ) {
1072         return null;
1073     }
1074     return b.getChosenGLCapabilities();
1075   }
1076
1077   @Override
1078   public final GLProfile getGLProfile() {
1079     return glProfile;
1080   }
1081
1082   @Override
1083   public NativeSurface getNativeSurface() {
1084     final Backend b = backend;
1085     if ( null == b ) {
1086         return null;
1087     }
1088     return b.getDrawable().getNativeSurface();
1089   }
1090
1091   @Override
1092   public long getHandle() {
1093     final Backend b = backend;
1094     if ( null == b ) {
1095         return 0;
1096     }
1097     return b.getDrawable().getNativeSurface().getSurfaceHandle();
1098   }
1099
1100   @Override
1101   public final GLDrawableFactory getFactory() {
1102     return factory;
1103   }
1104
1105   /**
1106    * Returns the used texture unit, i.e. a value of [0..n], or -1 if non used.
1107    * <p>
1108    * If implementation uses a texture-unit, it will be known only after the first initialization, i.e. display call.
1109    * </p>
1110    * <p>
1111    * See <a href="#fboGLSLVerticalFlip">FBO / GLSL Vertical Flip</a>.
1112    * </p>
1113    */
1114   public final int getTextureUnit() {
1115     final Backend b = backend;
1116     if ( null == b ) {
1117         return -1;
1118     }
1119     return b.getTextureUnit();
1120   }
1121
1122   /**
1123    * Allows user to request a texture unit to be used,
1124    * must be called before the first initialization, i.e. {@link #display()} call.
1125    * <p>
1126    * Defaults to <code>0</code>.
1127    * </p>
1128    * <p>
1129    * See <a href="#fboGLSLVerticalFlip">FBO / GLSL Vertical Flip</a>.
1130    * </p>
1131    *
1132    * @param v requested texture unit
1133    * @see #getTextureUnit()
1134    */
1135   public final void setTextureUnit(int v) {
1136       requestedTextureUnit = v;
1137   }
1138
1139   //----------------------------------------------------------------------
1140   // Internals only below this point
1141   //
1142
1143   private final Object initSync = new Object();
1144   private boolean initializeBackendImpl() {
1145     synchronized(initSync) {
1146         if( !isInitialized ) {
1147             if ( 0 >= panelWidth || 0 >= panelHeight ) {
1148               // See whether we have a non-zero size yet and can go ahead with
1149               // initialization
1150               if (0 >= reshapeWidth || 0 >= reshapeHeight ) {
1151                   return false;
1152               }
1153
1154               if (DEBUG) {
1155                   System.err.println(getThreadName()+": GLJPanel.createAndInitializeBackend: " +
1156                           panelWidth+"x"+panelHeight+" @ scale "+getPixelScale() + " -> " +
1157                           reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScale());
1158               }
1159               // Pull down reshapeWidth and reshapeHeight into panelWidth and
1160               // panelHeight eagerly in order to complete initialization, and
1161               // force a reshape later
1162               panelWidth = reshapeWidth;
1163               panelHeight = reshapeHeight;
1164             }
1165
1166             if ( null == backend ) {
1167                 if ( oglPipelineUsable() ) {
1168                     backend = new J2DOGLBackend();
1169                 } else {
1170                     backend = new OffscreenBackend(glProfile, customPixelBufferProvider);
1171                 }
1172                 isInitialized = false;
1173             }
1174
1175             if (!isInitialized) {
1176                 backend.initialize();
1177             }
1178             return isInitialized;
1179         } else {
1180             return true;
1181         }
1182     }
1183   }
1184
1185   private final int getPixelScale() { return pixelScale; }
1186
1187   @Override
1188   public WindowClosingMode getDefaultCloseOperation() {
1189       return awtWindowClosingProtocol.getDefaultCloseOperation();
1190   }
1191
1192   @Override
1193   public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) {
1194       return awtWindowClosingProtocol.setDefaultCloseOperation(op);
1195   }
1196
1197   private boolean handleReshape() {
1198     if (DEBUG) {
1199       System.err.println(getThreadName()+": GLJPanel.handleReshape: "+
1200                          panelWidth+"x"+panelHeight+" @ scale "+getPixelScale() + " -> " +
1201                          reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScale());
1202     }
1203     panelWidth  = reshapeWidth;
1204     panelHeight = reshapeHeight;
1205
1206     return backend.handleReshape();
1207   }
1208
1209   // This is used as the GLEventListener for the pbuffer-based backend
1210   // as well as the callback mechanism for the other backends
1211   class Updater implements GLEventListener {
1212     private Graphics g;
1213
1214     public void setGraphics(Graphics g) {
1215       this.g = g;
1216     }
1217
1218     @Override
1219     public void init(GLAutoDrawable drawable) {
1220       if (!backend.preGL(g)) {
1221         return;
1222       }
1223       helper.init(GLJPanel.this, !sendReshape);
1224       backend.postGL(g, false);
1225     }
1226
1227     @Override
1228     public void dispose(GLAutoDrawable drawable) {
1229       helper.disposeAllGLEventListener(GLJPanel.this, false);
1230     }
1231
1232     @Override
1233     public void display(GLAutoDrawable drawable) {
1234       if (!backend.preGL(g)) {
1235         return;
1236       }
1237       if (sendReshape) {
1238         if (DEBUG) {
1239           System.err.println(getThreadName()+": GLJPanel.display: reshape(" + viewportX + "," + viewportY + " " + panelWidth + "x" + panelHeight + " @ scale "+getPixelScale()+")");
1240         }
1241         helper.reshape(GLJPanel.this, viewportX, viewportY, panelWidth, panelHeight);
1242         sendReshape = false;
1243       }
1244
1245       helper.display(GLJPanel.this);
1246       backend.postGL(g, true);
1247     }
1248
1249     public void plainPaint(GLAutoDrawable drawable) {
1250       helper.display(GLJPanel.this);
1251     }
1252
1253     @Override
1254     public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
1255       // This is handled above and dispatched directly to the appropriate context
1256     }
1257   }
1258
1259   @Override
1260   public String toString() {
1261     final GLDrawable d = ( null != backend ) ? backend.getDrawable() : null;
1262     return "AWT-GLJPanel[ drawableType "+ ( ( null != d ) ? d.getClass().getName() : "null" ) +
1263            ", chosenCaps " + getChosenGLCapabilities() +
1264            "]";
1265   }
1266
1267   private final Runnable disposeAction = new Runnable() {
1268     @Override
1269     public void run() {
1270         if ( null != backend ) {
1271             final GLContext _context = backend.getContext();
1272             final boolean backendDestroy = !backend.isUsingOwnLifecycle();
1273             if( null != _context && _context.isCreated() ) {
1274                 // Catch dispose GLExceptions by GLEventListener, just 'print' them
1275                 // so we can continue with the destruction.
1276                 try {
1277                     helper.disposeGL(GLJPanel.this, _context, !backendDestroy);
1278                 } catch (GLException gle) {
1279                     gle.printStackTrace();
1280                 }
1281             }
1282             if ( backendDestroy ) {
1283                 backend.destroy();
1284                 backend = null;
1285                 isInitialized = false;
1286             }
1287         }
1288     }
1289   };
1290
1291   private final Runnable updaterInitAction = new Runnable() {
1292     @Override
1293     public void run() {
1294       updater.init(GLJPanel.this);
1295     }
1296   };
1297
1298   private final Runnable updaterDisplayAction = new Runnable() {
1299     @Override
1300     public void run() {
1301       updater.display(GLJPanel.this);
1302     }
1303   };
1304
1305   private final Runnable updaterPlainDisplayAction = new Runnable() {
1306     @Override
1307     public void run() {
1308       updater.plainPaint(GLJPanel.this);
1309     }
1310   };
1311
1312   private final Runnable paintImmediatelyAction = new Runnable() {
1313     @Override
1314     public void run() {
1315       paintImmediately(0, 0, getWidth(), getHeight());
1316     }
1317   };
1318
1319   private class DisposeGLEventListenerAction implements Runnable {
1320       GLEventListener listener;
1321       private final boolean remove;
1322       private DisposeGLEventListenerAction(GLEventListener listener, boolean remove) {
1323           this.listener = listener;
1324           this.remove = remove;
1325       }
1326
1327       @Override
1328       public void run() {
1329           final Backend b = backend;
1330           if ( null != b ) {
1331               listener = helper.disposeGLEventListener(GLJPanel.this, b.getDrawable(), b.getContext(), listener, remove);
1332           }
1333       }
1334   };
1335
1336   private int getGLInteger(GL gl, int which) {
1337     int[] tmp = new int[1];
1338     gl.glGetIntegerv(which, tmp, 0);
1339     return tmp[0];
1340   }
1341
1342   protected static String getThreadName() { return Thread.currentThread().getName(); }
1343
1344   //----------------------------------------------------------------------
1345   // Implementations of the various backends
1346   //
1347
1348   /**
1349    * Abstraction of the different rendering backends: i.e., pure
1350    * software / pixmap rendering, pbuffer-based acceleration, Java 2D
1351    * JOGL bridge
1352    */
1353   static interface Backend {
1354     /** Create, Destroy, .. */
1355     public boolean isUsingOwnLifecycle();
1356
1357     /** Called each time the backend needs to initialize itself */
1358     public void initialize();
1359
1360     /** Called when the backend should clean up its resources */
1361     public void destroy();
1362
1363     /** Called when the opacity of the GLJPanel is changed */
1364     public void setOpaque(boolean opaque);
1365
1366     /**
1367      * Called to manually create an additional OpenGL context against
1368      * this GLJPanel
1369      */
1370     public GLContext createContext(GLContext shareWith);
1371
1372     /** Called to set the current backend's GLContext */
1373     public void setContext(GLContext ctx);
1374
1375     /** Called to get the current backend's GLContext */
1376     public GLContext getContext();
1377
1378     /** Called to get the current backend's GLDrawable */
1379     public GLDrawable getDrawable();
1380
1381     /** Returns the used texture unit, i.e. a value of [0..n], or -1 if non used. */
1382     public int getTextureUnit();
1383
1384     /** Called to fetch the "real" GLCapabilities for the backend */
1385     public GLCapabilitiesImmutable getChosenGLCapabilities();
1386
1387     /** Called to fetch the "real" GLProfile for the backend */
1388     public GLProfile getGLProfile();
1389
1390     /**
1391      * Called to handle a reshape event. When this is called, the
1392      * OpenGL context associated with the backend is not current, to
1393      * make it easier to destroy and re-create pbuffers if necessary.
1394      */
1395     public boolean handleReshape();
1396
1397     /**
1398      * Called before the OpenGL work is done in init() and display().
1399      * If false is returned, this render is aborted.
1400      */
1401     public boolean preGL(Graphics g);
1402
1403     /**
1404      * Return true if backend handles 'swap buffer' itself
1405      * and hence the helper's setAutoSwapBuffer(enable) shall not be called.
1406      * In this case {@link GLJPanel#autoSwapBufferMode} is being used
1407      * in the backend to determine whether to swap buffers or not.
1408      */
1409     public boolean handlesSwapBuffer();
1410
1411     /**
1412      * Shall invoke underlying drawable's swapBuffer.
1413      */
1414     public void swapBuffers();
1415
1416     /**
1417      * Called after the OpenGL work is done in init() and display().
1418      * The isDisplay argument indicates whether this was called on
1419      * behalf of a call to display() rather than init().
1420      */
1421     public void postGL(Graphics g, boolean isDisplay);
1422
1423     /** Called from within paintComponent() to initiate the render */
1424     public void doPaintComponent(Graphics g);
1425
1426     /** Called from print .. no backend update desired onscreen */
1427     public void doPlainPaint();
1428   }
1429
1430   // Base class used by both the software (pixmap) and pbuffer
1431   // backends, both of which rely on reading back the OpenGL frame
1432   // buffer and drawing it with a BufferedImage
1433   class OffscreenBackend implements Backend {
1434     private final AWTGLPixelBufferProvider pixelBufferProvider;
1435     private final boolean useSingletonBuffer;
1436     private AWTGLPixelBuffer pixelBuffer;
1437     private BufferedImage alignedImage;
1438
1439     // One of these is used to store the read back pixels before storing
1440     // in the BufferedImage
1441     protected IntBuffer readBackIntsForCPUVFlip;
1442
1443     // Implementation using software rendering
1444     private volatile GLDrawableImpl offscreenDrawable; // volatile: avoid locking for read-only access
1445     private boolean offscreenIsFBO;
1446     private FBObject fboFlipped;
1447     private GLSLTextureRaster glslTextureRaster;
1448
1449     private volatile GLContextImpl offscreenContext; // volatile: avoid locking for read-only access
1450     private boolean flipVertical;
1451     private int frameCount = 0;
1452
1453     // For saving/restoring of OpenGL state during ReadPixels
1454     private final GLPixelStorageModes psm =  new GLPixelStorageModes();
1455
1456     OffscreenBackend(GLProfile glp, AWTGLPixelBufferProvider custom) {
1457         if(null == custom) {
1458             pixelBufferProvider = getSingleAWTGLPixelBufferProvider();
1459         } else {
1460             pixelBufferProvider = custom;
1461         }
1462         if( pixelBufferProvider instanceof SingletonGLPixelBufferProvider ) {
1463             useSingletonBuffer = true;
1464         } else {
1465             useSingletonBuffer = false;
1466         }
1467     }
1468
1469     @Override
1470     public final boolean isUsingOwnLifecycle() { return false; }
1471
1472     @Override
1473     public final void initialize() {
1474       if(DEBUG) {
1475           System.err.println(getThreadName()+": OffscreenBackend: initialize() - frameCount "+frameCount);
1476       }
1477       try {
1478           final GLContext[] shareWith = { null };
1479           if( helper.isSharedGLContextPending(shareWith) ) {
1480               return; // pending ..
1481           }
1482           offscreenDrawable = (GLDrawableImpl) factory.createOffscreenDrawable(
1483                                                     null /* default platform device */,
1484                                                     offscreenCaps,
1485                                                     chooser,
1486                                                     panelWidth, panelHeight);
1487           offscreenDrawable.setRealized(true);
1488           if( DEBUG ) {
1489               offscreenDrawable.getNativeSurface().addSurfaceUpdatedListener(new SurfaceUpdatedListener() {
1490                   @Override
1491                   public final void surfaceUpdated(Object updater, NativeSurface ns, long when) {
1492                       System.err.println(getThreadName()+": OffscreenBackend.swapBuffers - frameCount "+frameCount);
1493                   } } );
1494           }
1495
1496           offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith[0]);
1497           offscreenContext.setContextCreationFlags(additionalCtxCreationFlags);
1498           if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) {
1499               isInitialized = true;
1500               helper.setAutoSwapBufferMode(false); // we handle swap-buffers, see handlesSwapBuffer()
1501
1502               final GL gl = offscreenContext.getGL();
1503               flipVertical = !GLJPanel.this.skipGLOrientationVerticalFlip && offscreenDrawable.isGLOriented();
1504               final GLCapabilitiesImmutable chosenCaps = offscreenDrawable.getChosenGLCapabilities();
1505               offscreenIsFBO = chosenCaps.isFBO();
1506               final boolean glslCompliant = !offscreenContext.hasRendererQuirk(GLRendererQuirks.GLSLNonCompliant);
1507               final boolean useGLSLFlip = flipVertical && offscreenIsFBO && gl.isGL2ES2() && USE_GLSL_TEXTURE_RASTERIZER && glslCompliant;
1508               if( DEBUG ) {
1509                   System.err.println(getThreadName()+": OffscreenBackend.initialize: useGLSLFlip "+useGLSLFlip+
1510                           " [flip "+flipVertical+", isFBO "+offscreenIsFBO+", isGL2ES2 "+gl.isGL2ES2()+
1511                           ", noglsl "+!USE_GLSL_TEXTURE_RASTERIZER+", glslNonCompliant "+!glslCompliant+
1512                           ", isGL2ES2 " + gl.isGL2ES2()+"]");
1513               }
1514               if( useGLSLFlip ) {
1515                   final GLFBODrawable fboDrawable = (GLFBODrawable) offscreenDrawable;
1516                   fboDrawable.setTextureUnit( GLJPanel.this.requestedTextureUnit );
1517                   try {
1518                       fboFlipped = new FBObject();
1519                       fboFlipped.reset(gl, fboDrawable.getSurfaceWidth(), fboDrawable.getSurfaceHeight(), 0, false);
1520                       fboFlipped.attachTexture2D(gl, 0, chosenCaps.getAlphaBits()>0);
1521                       // fboFlipped.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24);
1522                       glslTextureRaster = new GLSLTextureRaster(fboDrawable.getTextureUnit(), true);
1523                       glslTextureRaster.init(gl.getGL2ES2());
1524                       glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, fboDrawable.getSurfaceWidth(), fboDrawable.getSurfaceHeight());
1525                   } catch (Exception ex) {
1526                       ex.printStackTrace();
1527                       if(null != glslTextureRaster) {
1528                         glslTextureRaster.dispose(gl.getGL2ES2());
1529                         glslTextureRaster = null;
1530                       }
1531                       if(null != fboFlipped) {
1532                         fboFlipped.destroy(gl);
1533                         fboFlipped = null;
1534                       }
1535                   }
1536               } else {
1537                   fboFlipped = null;
1538                   glslTextureRaster = null;
1539               }
1540               offscreenContext.release();
1541           } else {
1542               isInitialized = false;
1543           }
1544       } finally {
1545           if( !isInitialized ) {
1546               if(null != offscreenContext) {
1547                   offscreenContext.destroy();
1548                   offscreenContext = null;
1549               }
1550               if(null != offscreenDrawable) {
1551                   offscreenDrawable.setRealized(false);
1552                   offscreenDrawable = null;
1553               }
1554           }
1555       }
1556     }
1557
1558     @Override
1559     public final void destroy() {
1560       if(DEBUG) {
1561           System.err.println(getThreadName()+": OffscreenBackend: destroy() - offscreenContext: "+(null!=offscreenContext)+" - offscreenDrawable: "+(null!=offscreenDrawable)+" - frameCount "+frameCount);
1562       }
1563       if ( null != offscreenContext && offscreenContext.isCreated() ) {
1564         if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) {
1565             try {
1566                 final GL gl = offscreenContext.getGL();
1567                 if(null != glslTextureRaster) {
1568                     glslTextureRaster.dispose(gl.getGL2ES2());
1569                 }
1570                 if(null != fboFlipped) {
1571                     fboFlipped.destroy(gl);
1572                 }
1573             } finally {
1574                 offscreenContext.destroy();
1575             }
1576         }
1577       }
1578       offscreenContext = null;
1579       glslTextureRaster = null;
1580       fboFlipped = null;
1581       offscreenContext = null;
1582
1583       if (offscreenDrawable != null) {
1584         final AbstractGraphicsDevice adevice = offscreenDrawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice();
1585         offscreenDrawable.setRealized(false);
1586         offscreenDrawable = null;
1587         if(null != adevice) {
1588             adevice.close();
1589         }
1590       }
1591       offscreenIsFBO = false;
1592
1593       if( null != readBackIntsForCPUVFlip ) {
1594           readBackIntsForCPUVFlip.clear();
1595           readBackIntsForCPUVFlip = null;
1596       }
1597       if( null != pixelBuffer ) {
1598           if( !useSingletonBuffer ) {
1599               pixelBuffer.dispose();
1600           }
1601           pixelBuffer = null;
1602       }
1603       alignedImage = null;
1604     }
1605
1606     @Override
1607     public final void setOpaque(boolean opaque) {
1608       if ( opaque != isOpaque() && !useSingletonBuffer ) {
1609           pixelBuffer.dispose();
1610           pixelBuffer = null;
1611           alignedImage = null;
1612       }
1613     }
1614
1615     @Override
1616     public final boolean preGL(Graphics g) {
1617       // Empty in this implementation
1618       return true;
1619     }
1620
1621     @Override
1622     public final boolean handlesSwapBuffer() {
1623         return true;
1624     }
1625
1626     @Override
1627     public final void swapBuffers() {
1628         final GLDrawable d = offscreenDrawable;
1629         if( null != d ) {
1630             d.swapBuffers();
1631         }
1632     }
1633
1634     @Override
1635     public final void postGL(Graphics g, boolean isDisplay) {
1636       if (isDisplay) {
1637         if(DEBUG) {
1638             System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: - frameCount "+frameCount);
1639         }
1640         if( autoSwapBufferMode ) {
1641             // Since we only use a single-buffer non-MSAA or double-buffered MSAA offscreenDrawable,
1642             // we can always swap!
1643             offscreenDrawable.swapBuffers();
1644         }
1645         final GL gl = offscreenContext.getGL();
1646
1647         final int componentCount;
1648         final int alignment;
1649         if( isOpaque() ) {
1650             // w/o alpha
1651             componentCount = 3;
1652             alignment = 1;
1653         } else {
1654             // with alpha
1655             componentCount = 4;
1656             alignment = 4;
1657         }
1658
1659         final GLPixelAttributes pixelAttribs = pixelBufferProvider.getAttributes(gl, componentCount);
1660
1661         if( useSingletonBuffer ) { // attempt to fetch the latest AWTGLPixelBuffer
1662             pixelBuffer = (AWTGLPixelBuffer) ((SingletonGLPixelBufferProvider)pixelBufferProvider).getSingleBuffer(pixelAttribs);
1663         }
1664         if( null != pixelBuffer && pixelBuffer.requiresNewBuffer(gl, panelWidth, panelHeight, 0) ) {
1665             pixelBuffer.dispose();
1666             pixelBuffer = null;
1667             alignedImage = null;
1668         }
1669         if ( null == pixelBuffer ) {
1670           if (0 >= panelWidth || 0 >= panelHeight ) {
1671               return;
1672           }
1673           pixelBuffer = pixelBufferProvider.allocate(gl, pixelAttribs, panelWidth, panelHeight, 1, true, 0);
1674           if(DEBUG) {
1675               System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelBufferProvider isSingletonBufferProvider "+useSingletonBuffer+", 0x"+Integer.toHexString(pixelBufferProvider.hashCode())+", "+pixelBufferProvider.getClass().getSimpleName());
1676               System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" pixelBuffer 0x"+Integer.toHexString(pixelBuffer.hashCode())+", "+pixelBuffer+", alignment "+alignment);
1677               System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" flippedVertical "+flipVertical+", glslTextureRaster "+(null!=glslTextureRaster));
1678               System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" panelSize "+panelWidth+"x"+panelHeight+" @ scale "+getPixelScale());
1679           }
1680         }
1681         if( offscreenDrawable.getSurfaceWidth() != panelWidth || offscreenDrawable.getSurfaceHeight() != panelHeight ) {
1682             throw new InternalError("OffscreenDrawable panelSize mismatch (reshape missed): panelSize "+panelWidth+"x"+panelHeight+" != drawable "+offscreenDrawable.getSurfaceWidth()+"x"+offscreenDrawable.getSurfaceHeight()+", on thread "+getThreadName());
1683         }
1684         if( null == alignedImage ||
1685             panelWidth != alignedImage.getWidth() || panelHeight != alignedImage.getHeight() ||
1686             !pixelBuffer.isDataBufferSource(alignedImage) ) {
1687             alignedImage = pixelBuffer.getAlignedImage(panelWidth, panelHeight);
1688             if(DEBUG) {
1689                 System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.0: "+GLJPanel.this.getName()+" new alignedImage "+alignedImage.getWidth()+"x"+alignedImage.getHeight()+" @ scale "+getPixelScale()+", "+alignedImage+", pixelBuffer "+pixelBuffer.width+"x"+pixelBuffer.height+", "+pixelBuffer);
1690             }
1691         }
1692         final IntBuffer readBackInts;
1693
1694         if( !flipVertical || null != glslTextureRaster ) {
1695            readBackInts = (IntBuffer) pixelBuffer.buffer;
1696         } else {
1697            if( null == readBackIntsForCPUVFlip || pixelBuffer.width * pixelBuffer.height > readBackIntsForCPUVFlip.remaining() ) {
1698                readBackIntsForCPUVFlip = IntBuffer.allocate(pixelBuffer.width * pixelBuffer.height);
1699            }
1700            readBackInts = readBackIntsForCPUVFlip;
1701         }
1702
1703         final TextureState usrTexState, fboTexState;
1704         final int fboTexUnit = GL.GL_TEXTURE0 + ( offscreenIsFBO ? ((GLFBODrawable)offscreenDrawable).getTextureUnit() : 0 );
1705
1706         if( offscreenIsFBO ) {
1707             usrTexState = new TextureState(gl, GL.GL_TEXTURE_2D);
1708             if( fboTexUnit != usrTexState.getUnit() ) {
1709                 // glActiveTexture(..) + glBindTexture(..) are implicit performed in GLFBODrawableImpl's
1710                 // swapBuffers/contextMadeCurent -> swapFBOImpl.
1711                 // We need to cache the texture unit's bound texture-id before it's overwritten.
1712                 gl.glActiveTexture(fboTexUnit);
1713                 fboTexState = new TextureState(gl, fboTexUnit, GL.GL_TEXTURE_2D);
1714             } else {
1715                 fboTexState = usrTexState;
1716             }
1717         } else {
1718             usrTexState = null;
1719             fboTexState = null;
1720         }
1721
1722         // Must now copy pixels from offscreen context into surface
1723         if(DEBUG) {
1724             System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL.readPixels: - frameCount "+frameCount);
1725         }
1726
1727         // Save PACK modes, reset them to defaults and set alignment
1728         psm.setPackAlignment(gl, alignment);
1729         if(gl.isGL2ES3()) {
1730             final GL2ES3 gl2es3 = gl.getGL2ES3();
1731             gl2es3.glPixelStorei(GL2ES3.GL_PACK_ROW_LENGTH, panelWidth);
1732             gl2es3.glReadBuffer(gl2es3.getDefaultReadBuffer());
1733         }
1734
1735         if(null != glslTextureRaster) { // implies flippedVertical
1736             final boolean viewportChange;
1737             final int[] usrViewport = new int[] { 0, 0, 0, 0 };
1738             gl.glGetIntegerv(GL.GL_VIEWPORT, usrViewport, 0);
1739             viewportChange = 0 != usrViewport[0] || 0 != usrViewport[1] ||
1740                              offscreenDrawable.getSurfaceWidth() != usrViewport[2] || offscreenDrawable.getSurfaceHeight() != usrViewport[3];
1741             if( DEBUG_VIEWPORT ) {
1742                 System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.postGL: "+GLJPanel.this.getName()+" Viewport: change "+viewportChange+
1743                          ", "+usrViewport[0]+"/"+usrViewport[1]+" "+usrViewport[2]+"x"+usrViewport[3]+
1744                          " -> 0/0 "+offscreenDrawable.getSurfaceWidth()+"x"+offscreenDrawable.getSurfaceHeight());
1745             }
1746             if( viewportChange ) {
1747                 gl.glViewport(0, 0, offscreenDrawable.getSurfaceWidth(), offscreenDrawable.getSurfaceHeight());
1748             }
1749
1750             // perform vert-flipping via OpenGL/FBO
1751             final GLFBODrawable fboDrawable = (GLFBODrawable)offscreenDrawable;
1752             final FBObject.TextureAttachment fboTex = fboDrawable.getTextureBuffer(GL.GL_FRONT);
1753
1754             fboFlipped.bind(gl);
1755
1756             // gl.glActiveTexture(GL.GL_TEXTURE0 + fboDrawable.getTextureUnit()); // implicit by GLFBODrawableImpl: swapBuffers/contextMadeCurent -> swapFBOImpl
1757             gl.glBindTexture(GL.GL_TEXTURE_2D, fboTex.getName());
1758             // gl.glClear(GL.GL_DEPTH_BUFFER_BIT); // fboFlipped runs w/o DEPTH!
1759
1760             glslTextureRaster.display(gl.getGL2ES2());
1761             gl.glReadPixels(0, 0, panelWidth, panelHeight, pixelAttribs.format, pixelAttribs.type, readBackInts);
1762
1763             fboFlipped.unbind(gl);
1764             if( viewportChange ) {
1765                 gl.glViewport(usrViewport[0], usrViewport[1], usrViewport[2], usrViewport[3]);
1766             }
1767             fboTexState.restore(gl);
1768         } else {
1769             gl.glReadPixels(0, 0, panelWidth, panelHeight, pixelAttribs.format, pixelAttribs.type, readBackInts);
1770
1771             if ( flipVertical ) {
1772                 // Copy temporary data into raster of BufferedImage for faster
1773                 // blitting Note that we could avoid this copy in the cases
1774                 // where !offscreenDrawable.isGLOriented(),
1775                 // but that's the software rendering path which is very slow anyway.
1776                 final BufferedImage image = alignedImage;
1777                 final int[] src = readBackInts.array();
1778                 final int[] dest = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
1779                 final int incr = panelWidth;
1780                 int srcPos = 0;
1781                 int destPos = (panelHeight - 1) * panelWidth;
1782                 for (; destPos >= 0; srcPos += incr, destPos -= incr) {
1783                   System.arraycopy(src, srcPos, dest, destPos, incr);
1784                 }
1785             }
1786         }
1787         if( offscreenIsFBO && fboTexUnit != usrTexState.getUnit() ) {
1788             usrTexState.restore(gl);
1789         }
1790
1791         // Restore saved modes.
1792         psm.restore(gl);
1793
1794         // Note: image will be drawn back in paintComponent() for
1795         // correctness on all platforms
1796       }
1797     }
1798
1799     @Override
1800     public final int getTextureUnit() {
1801         if(null != glslTextureRaster && null != offscreenDrawable) { // implies flippedVertical
1802             return ((GLFBODrawable)offscreenDrawable).getTextureUnit();
1803         }
1804         return -1;
1805     }
1806
1807     @Override
1808     public final void doPaintComponent(Graphics g) {
1809       helper.invokeGL(offscreenDrawable, offscreenContext, updaterDisplayAction, updaterInitAction);
1810
1811       if ( null != alignedImage ) {
1812         if( DEBUG ) {
1813             System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.doPaintComponent.drawImage: - frameCount "+frameCount);
1814         }
1815         // Draw resulting image in one shot
1816         g.drawImage(alignedImage, 0, 0, alignedImage.getWidth()/getPixelScale(), alignedImage.getHeight()/getPixelScale(), null); // Null ImageObserver since image data is ready.
1817       }
1818       frameCount++;
1819     }
1820
1821     @Override
1822     public final void doPlainPaint() {
1823       helper.invokeGL(offscreenDrawable, offscreenContext, updaterPlainDisplayAction, updaterInitAction);
1824     }
1825
1826     @Override
1827     public final boolean handleReshape() {
1828         GLDrawableImpl _drawable = offscreenDrawable;
1829         {
1830             final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, offscreenContext, panelWidth, panelHeight);
1831             if(_drawable != _drawableNew) {
1832                 // write back
1833                 _drawable = _drawableNew;
1834                 offscreenDrawable = _drawableNew;
1835             }
1836         }
1837         if (DEBUG) {
1838             System.err.println(getThreadName()+": GLJPanel.OffscreenBackend.handleReshape: " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScale() + " -> " + _drawable.getSurfaceWidth()+"x"+_drawable.getSurfaceHeight());
1839         }
1840         panelWidth = _drawable.getSurfaceWidth();
1841         panelHeight = _drawable.getSurfaceHeight();
1842
1843         if( null != glslTextureRaster ) {
1844             if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) {
1845                 try {
1846                     final GL gl = offscreenContext.getGL();
1847                     fboFlipped.reset(gl, _drawable.getSurfaceWidth(), _drawable.getSurfaceHeight(), 0, false);
1848                     glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, _drawable.getSurfaceWidth(), _drawable.getSurfaceHeight());
1849                 } finally {
1850                     offscreenContext.release();
1851                 }
1852             }
1853         }
1854         return _drawable.isRealized();
1855     }
1856
1857     @Override
1858     public final GLContext createContext(GLContext shareWith) {
1859       return (null != offscreenDrawable) ? offscreenDrawable.createContext(shareWith) : null;
1860     }
1861
1862     @Override
1863     public final void setContext(GLContext ctx) {
1864       offscreenContext=(GLContextImpl)ctx;
1865     }
1866
1867     @Override
1868     public final GLContext getContext() {
1869       return offscreenContext;
1870     }
1871
1872     @Override
1873     public final GLDrawable getDrawable() {
1874         return offscreenDrawable;
1875     }
1876
1877     @Override
1878     public final GLCapabilitiesImmutable getChosenGLCapabilities() {
1879       if (offscreenDrawable == null) {
1880         return null;
1881       }
1882       return offscreenDrawable.getChosenGLCapabilities();
1883     }
1884
1885     @Override
1886     public final GLProfile getGLProfile() {
1887       if (offscreenDrawable == null) {
1888         return null;
1889       }
1890       return offscreenDrawable.getGLProfile();
1891     }
1892   }
1893
1894   class J2DOGLBackend implements Backend {
1895     // Opaque Object identifier representing the Java2D surface we are
1896     // drawing to; used to determine when to destroy and recreate JOGL
1897     // context
1898     private Object j2dSurface;
1899     // Graphics object being used during Java2D update action
1900     // (absolutely essential to cache this)
1901     // No-op context representing the Java2D OpenGL context
1902     private GLContext j2dContext;
1903     // Context associated with no-op drawable representing the JOGL
1904     // OpenGL context
1905     private GLDrawable joglDrawable;
1906     // The real OpenGL context JOGL uses to render
1907     private GLContext  joglContext;
1908     // State captured from Java2D OpenGL context necessary in order to
1909     // properly render into Java2D back buffer
1910     private final int[] drawBuffer   = new int[1];
1911     private final int[] readBuffer   = new int[1];
1912     // This is required when the FBO option of the Java2D / OpenGL
1913     // pipeline is active
1914     private final int[] frameBuffer  = new int[1];
1915     // Current (as of this writing) NVidia drivers have a couple of bugs
1916     // relating to the sharing of framebuffer and renderbuffer objects
1917     // between contexts. It appears we have to (a) reattach the color
1918     // attachment and (b) actually create new depth buffer storage and
1919     // attach it in order for the FBO to behave properly in our context.
1920     private boolean checkedForFBObjectWorkarounds;
1921     private boolean fbObjectWorkarounds;
1922     private int[] frameBufferDepthBuffer;
1923     private int[] frameBufferTexture;
1924     private boolean createNewDepthBuffer;
1925     // Current (as of this writing) ATI drivers have problems when the
1926     // same FBO is bound in two different contexts. Here we check for
1927     // this case and explicitly release the FBO from Java2D's context
1928     // before switching to ours. Java2D will re-bind the FBO when it
1929     // makes its context current the next time. Interestingly, if we run
1930     // this code path on NVidia hardware, it breaks the rendering
1931     // results -- no output is generated. This doesn't appear to be an
1932     // interaction with the abovementioned NVidia-specific workarounds,
1933     // as even if we disable that code the FBO is still reported as
1934     // incomplete in our context.
1935     private boolean checkedGLVendor;
1936     private boolean vendorIsATI;
1937
1938     // Holding on to this GraphicsConfiguration is a workaround for a
1939     // problem in the Java 2D / JOGL bridge when FBOs are enabled; see
1940     // comment related to Issue 274 below
1941     private GraphicsConfiguration workaroundConfig;
1942
1943     @Override
1944     public final boolean isUsingOwnLifecycle() { return true; }
1945
1946     @Override
1947     public final void initialize() {
1948       if(DEBUG) {
1949           System.err.println(getThreadName()+": J2DOGL: initialize()");
1950       }
1951       // No-op in this implementation; everything is done lazily
1952       isInitialized = true;
1953     }
1954
1955     @Override
1956     public final void destroy() {
1957       Java2D.invokeWithOGLContextCurrent(null, new Runnable() {
1958           @Override
1959           public void run() {
1960             if(DEBUG) {
1961                 System.err.println(getThreadName()+": J2DOGL: destroy() - joglContext: "+(null!=joglContext)+" - joglDrawable: "+(null!=joglDrawable));
1962             }
1963             if (joglContext != null) {
1964               joglContext.destroy();
1965               joglContext = null;
1966             }
1967             joglDrawable = null;
1968             if (j2dContext != null) {
1969               j2dContext.destroy();
1970               j2dContext = null;
1971             }
1972           }
1973         });
1974     }
1975
1976     @Override
1977     public final void setOpaque(boolean opaque) {
1978       // Empty in this implementation
1979     }
1980
1981     @Override
1982     public final GLContext createContext(GLContext shareWith) {
1983       if(null != shareWith) {
1984           throw new GLException("J2DOGLBackend cannot create context w/ additional shared context, since it already needs to share the context w/ J2D.");
1985       }
1986       return (null != joglDrawable && null != j2dContext) ? joglDrawable.createContext(j2dContext) : null;
1987     }
1988
1989     @Override
1990     public final void setContext(GLContext ctx) {
1991         joglContext=ctx;
1992     }
1993
1994     @Override
1995     public final GLContext getContext() {
1996       return joglContext;
1997     }
1998
1999     @Override
2000     public final GLDrawable getDrawable() {
2001         return joglDrawable;
2002     }
2003
2004     @Override
2005     public final int getTextureUnit() { return -1; }
2006
2007     @Override
2008     public final GLCapabilitiesImmutable getChosenGLCapabilities() {
2009       // FIXME: should do better than this; is it possible to query J2D Caps ?
2010       return new GLCapabilities(null);
2011     }
2012
2013     @Override
2014     public final GLProfile getGLProfile() {
2015       // FIXME: should do better than this; is it possible to query J2D's Profile ?
2016       return GLProfile.getDefault(GLProfile.getDefaultDevice());
2017     }
2018
2019     @Override
2020     public final boolean handleReshape() {
2021       // Empty in this implementation
2022       return true;
2023     }
2024
2025     @Override
2026     public final boolean preGL(Graphics g) {
2027       final GL2 gl = joglContext.getGL().getGL2();
2028       // Set up needed state in JOGL context from Java2D context
2029       gl.glEnable(GL2.GL_SCISSOR_TEST);
2030       Rectangle r = Java2D.getOGLScissorBox(g);
2031
2032       if (r == null) {
2033         if (DEBUG) {
2034           System.err.println(getThreadName()+": Java2D.getOGLScissorBox() returned null");
2035         }
2036         return false;
2037       }
2038       if (DEBUG) {
2039         System.err.println(getThreadName()+": GLJPanel: gl.glScissor(" + r.x + ", " + r.y + ", " + r.width + ", " + r.height + ")");
2040       }
2041
2042       gl.glScissor(r.x, r.y, r.width, r.height);
2043       Rectangle oglViewport = Java2D.getOGLViewport(g, panelWidth, panelHeight);
2044       // If the viewport X or Y changes, in addition to the panel's
2045       // width or height, we need to send a reshape operation to the
2046       // client
2047       if ((viewportX != oglViewport.x) ||
2048           (viewportY != oglViewport.y)) {
2049         sendReshape = true;
2050         if (DEBUG) {
2051           System.err.println(getThreadName()+": Sending reshape because viewport changed");
2052           System.err.println("  viewportX (" + viewportX + ") ?= oglViewport.x (" + oglViewport.x + ")");
2053           System.err.println("  viewportY (" + viewportY + ") ?= oglViewport.y (" + oglViewport.y + ")");
2054         }
2055       }
2056       viewportX = oglViewport.x;
2057       viewportY = oglViewport.y;
2058
2059       // If the FBO option is active, bind to the FBO from the Java2D
2060       // context.
2061       // Note that all of the plumbing in the context sharing stuff will
2062       // allow us to bind to this object since it's in our namespace.
2063       if (Java2D.isFBOEnabled() &&
2064           Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) {
2065
2066         // The texture target for Java2D's OpenGL pipeline when using FBOs
2067         // -- either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB
2068         int fboTextureTarget = Java2D.getOGLTextureType(g);
2069
2070         if (!checkedForFBObjectWorkarounds) {
2071           checkedForFBObjectWorkarounds = true;
2072           gl.glBindTexture(fboTextureTarget, 0);
2073           gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, frameBuffer[0]);
2074           int status = gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER);
2075           if (status != GL.GL_FRAMEBUFFER_COMPLETE) {
2076               // Need to do workarounds
2077               fbObjectWorkarounds = true;
2078               createNewDepthBuffer = true;
2079               if (DEBUG) {
2080                   System.err.println(getThreadName()+": GLJPanel: ERR GL_FRAMEBUFFER_BINDING: Discovered Invalid J2D FBO("+frameBuffer[0]+"): "+FBObject.getStatusString(status) +
2081                                      ", frame_buffer_object workarounds to be necessary");
2082               }
2083           } else {
2084             // Don't need the frameBufferTexture temporary any more
2085             frameBufferTexture = null;
2086             if (DEBUG) {
2087               System.err.println(getThreadName()+": GLJPanel: OK GL_FRAMEBUFFER_BINDING: "+frameBuffer[0]);
2088             }
2089           }
2090         }
2091
2092         if (fbObjectWorkarounds && createNewDepthBuffer) {
2093           if (frameBufferDepthBuffer == null)
2094             frameBufferDepthBuffer = new int[1];
2095
2096           // Create our own depth renderbuffer and associated storage
2097           // If we have an old one, delete it
2098           if (frameBufferDepthBuffer[0] != 0) {
2099             gl.glDeleteRenderbuffers(1, frameBufferDepthBuffer, 0);
2100             frameBufferDepthBuffer[0] = 0;
2101           }
2102
2103           gl.glBindTexture(fboTextureTarget, frameBufferTexture[0]);
2104           int[] width = new int[1];
2105           int[] height = new int[1];
2106           gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2.GL_TEXTURE_WIDTH, width, 0);
2107           gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL2.GL_TEXTURE_HEIGHT, height, 0);
2108
2109           gl.glGenRenderbuffers(1, frameBufferDepthBuffer, 0);
2110           if (DEBUG) {
2111             System.err.println(getThreadName()+": GLJPanel: Generated frameBufferDepthBuffer " + frameBufferDepthBuffer[0] +
2112                                " with width " + width[0] + ", height " + height[0]);
2113           }
2114
2115           gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, frameBufferDepthBuffer[0]);
2116           // FIXME: may need a loop here like in Java2D
2117           gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, GL2GL3.GL_DEPTH_COMPONENT24, width[0], height[0]);
2118
2119           gl.glBindRenderbuffer(GL2.GL_RENDERBUFFER, 0);
2120           createNewDepthBuffer = false;
2121         }
2122
2123         gl.glBindTexture(fboTextureTarget, 0);
2124         gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, frameBuffer[0]);
2125
2126         if (fbObjectWorkarounds) {
2127           // Hook up the color and depth buffer attachment points for this framebuffer
2128           gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER,
2129                                     GL.GL_COLOR_ATTACHMENT0,
2130                                     fboTextureTarget,
2131                                     frameBufferTexture[0],
2132                                     0);
2133           if (DEBUG) {
2134             System.err.println(getThreadName()+": GLJPanel: frameBufferDepthBuffer: " + frameBufferDepthBuffer[0]);
2135           }
2136           gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER,
2137                                        GL.GL_DEPTH_ATTACHMENT,
2138                                        GL.GL_RENDERBUFFER,
2139                                        frameBufferDepthBuffer[0]);
2140         }
2141
2142         if (DEBUG) {
2143           int status = gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER);
2144           if (status != GL.GL_FRAMEBUFFER_COMPLETE) {
2145             throw new GLException("Error: framebuffer was incomplete: status = 0x" +
2146                                   Integer.toHexString(status));
2147           }
2148         }
2149       } else {
2150         if (DEBUG) {
2151           System.err.println(getThreadName()+": GLJPanel: Setting up drawBuffer " + drawBuffer[0] +
2152                              " and readBuffer " + readBuffer[0]);
2153         }
2154
2155         gl.glDrawBuffer(drawBuffer[0]);
2156         gl.glReadBuffer(readBuffer[0]);
2157       }
2158
2159       return true;
2160     }
2161
2162     @Override
2163     public final boolean handlesSwapBuffer() {
2164         return false;
2165     }
2166
2167     @Override
2168     public final void swapBuffers() {
2169         final GLDrawable d = joglDrawable;
2170         if( null != d ) {
2171             d.swapBuffers();
2172         }
2173     }
2174
2175     @Override
2176     public final void postGL(Graphics g, boolean isDisplay) {
2177       // Cause OpenGL pipeline to flush its results because
2178       // otherwise it's possible we will buffer up multiple frames'
2179       // rendering results, resulting in apparent mouse lag
2180       GL gl = joglContext.getGL();
2181       gl.glFinish();
2182
2183       if (Java2D.isFBOEnabled() &&
2184           Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) {
2185         // Unbind the framebuffer from our context to work around
2186         // apparent driver bugs or at least unspecified behavior causing
2187         // OpenGL to run out of memory with certain cards and drivers
2188         gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0);
2189       }
2190     }
2191
2192     @Override
2193     public final void doPaintComponent(final Graphics g) {
2194       // This is a workaround for an issue in the Java 2D / JOGL
2195       // bridge (reported by an end user as JOGL Issue 274) where Java
2196       // 2D can occasionally leave its internal OpenGL context current
2197       // to the on-screen window rather than its internal "scratch"
2198       // pbuffer surface to which the FBO is attached. JOGL expects to
2199       // find a stable OpenGL drawable (on Windows, an HDC) upon which
2200       // it can create another OpenGL context. It turns out that, on
2201       // Windows, when Java 2D makes its internal OpenGL context
2202       // current against the window in order to put pixels on the
2203       // screen, it gets the device context for the window, makes its
2204       // context current, and releases the device context. This means
2205       // that when JOGL's Runnable gets to run below, the HDC is
2206       // already invalid. The workaround for this is to force Java 2D
2207       // to make its context current to the scratch surface, which we
2208       // can do by executing an empty Runnable with the "shared"
2209       // context current. This will be fixed in a Java SE 6 update
2210       // release, hopefully 6u2.
2211       if (Java2D.isFBOEnabled()) {
2212         if (workaroundConfig == null) {
2213           workaroundConfig = GraphicsEnvironment.
2214             getLocalGraphicsEnvironment().
2215             getDefaultScreenDevice().
2216             getDefaultConfiguration();
2217         }
2218         Java2D.invokeWithOGLSharedContextCurrent(workaroundConfig, new Runnable() { @Override
2219         public void run() {}});
2220       }
2221
2222       Java2D.invokeWithOGLContextCurrent(g, new Runnable() {
2223           @Override
2224           public void run() {
2225             if (DEBUG) {
2226               System.err.println(getThreadName()+": GLJPanel.invokeWithOGLContextCurrent");
2227             }
2228
2229             // Create no-op context representing Java2D context
2230             if (j2dContext == null) {
2231               j2dContext = factory.createExternalGLContext();
2232               if (DEBUG) {
2233                 System.err.println(getThreadName()+": GLJPanel.Created External Context: "+j2dContext);
2234               }
2235               if (DEBUG) {
2236 //                j2dContext.setGL(new DebugGL2(j2dContext.getGL().getGL2()));
2237               }
2238
2239               // Check to see whether we can support the requested
2240               // capabilities or need to fall back to a pbuffer
2241               // FIXME: add more checks?
2242
2243               j2dContext.makeCurrent();
2244               GL gl = j2dContext.getGL();
2245               if ((getGLInteger(gl, GL.GL_RED_BITS)         < offscreenCaps.getRedBits())        ||
2246                   (getGLInteger(gl, GL.GL_GREEN_BITS)       < offscreenCaps.getGreenBits())      ||
2247                   (getGLInteger(gl, GL.GL_BLUE_BITS)        < offscreenCaps.getBlueBits())       ||
2248                   //                  (getGLInteger(gl, GL.GL_ALPHA_BITS)       < offscreenCaps.getAlphaBits())      ||
2249                   (getGLInteger(gl, GL2.GL_ACCUM_RED_BITS)   < offscreenCaps.getAccumRedBits())   ||
2250                   (getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) < offscreenCaps.getAccumGreenBits()) ||
2251                   (getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS)  < offscreenCaps.getAccumBlueBits())  ||
2252                   (getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) < offscreenCaps.getAccumAlphaBits()) ||
2253                   //          (getGLInteger(gl, GL2.GL_DEPTH_BITS)       < offscreenCaps.getDepthBits())      ||
2254                   (getGLInteger(gl, GL.GL_STENCIL_BITS)     < offscreenCaps.getStencilBits())) {
2255                 if (DEBUG) {
2256                   System.err.println(getThreadName()+": GLJPanel: Falling back to pbuffer-based support because Java2D context insufficient");
2257                   System.err.println("                    Available              Required");
2258                   System.err.println("GL_RED_BITS         " + getGLInteger(gl, GL.GL_RED_BITS)         + "              " + offscreenCaps.getRedBits());
2259                   System.err.println("GL_GREEN_BITS       " + getGLInteger(gl, GL.GL_GREEN_BITS)       + "              " + offscreenCaps.getGreenBits());
2260                   System.err.println("GL_BLUE_BITS        " + getGLInteger(gl, GL.GL_BLUE_BITS)        + "              " + offscreenCaps.getBlueBits());
2261                   System.err.println("GL_ALPHA_BITS       " + getGLInteger(gl, GL.GL_ALPHA_BITS)       + "              " + offscreenCaps.getAlphaBits());
2262                   System.err.println("GL_ACCUM_RED_BITS   " + getGLInteger(gl, GL2.GL_ACCUM_RED_BITS)   + "              " + offscreenCaps.getAccumRedBits());
2263                   System.err.println("GL_ACCUM_GREEN_BITS " + getGLInteger(gl, GL2.GL_ACCUM_GREEN_BITS) + "              " + offscreenCaps.getAccumGreenBits());
2264                   System.err.println("GL_ACCUM_BLUE_BITS  " + getGLInteger(gl, GL2.GL_ACCUM_BLUE_BITS)  + "              " + offscreenCaps.getAccumBlueBits());
2265                   System.err.println("GL_ACCUM_ALPHA_BITS " + getGLInteger(gl, GL2.GL_ACCUM_ALPHA_BITS) + "              " + offscreenCaps.getAccumAlphaBits());
2266                   System.err.println("GL_DEPTH_BITS       " + getGLInteger(gl, GL.GL_DEPTH_BITS)       + "              " + offscreenCaps.getDepthBits());
2267                   System.err.println("GL_STENCIL_BITS     " + getGLInteger(gl, GL.GL_STENCIL_BITS)     + "              " + offscreenCaps.getStencilBits());
2268                 }
2269                 isInitialized = false;
2270                 backend = null;
2271                 java2DGLPipelineOK = false;
2272                 handleReshape = true;
2273                 j2dContext.destroy();
2274                 j2dContext = null;
2275                 return;
2276               }
2277             } else {
2278               j2dContext.makeCurrent();
2279             }
2280             try {
2281               captureJ2DState(j2dContext.getGL(), g);
2282               Object curSurface = Java2D.getOGLSurfaceIdentifier(g);
2283               if (curSurface != null) {
2284                 if (j2dSurface != curSurface) {
2285                   if (joglContext != null) {
2286                     joglContext.destroy();
2287                     joglContext = null;
2288                     joglDrawable = null;
2289                     sendReshape = true;
2290                     if (DEBUG) {
2291                       System.err.println(getThreadName()+": Sending reshape because surface changed");
2292                       System.err.println("New surface = " + curSurface);
2293                     }
2294                   }
2295                   j2dSurface = curSurface;
2296                   if (DEBUG) {
2297                       System.err.print(getThreadName()+": Surface type: ");
2298                       int surfaceType = Java2D.getOGLSurfaceType(g);
2299                       if (surfaceType == Java2D.UNDEFINED) {
2300                         System.err.println("UNDEFINED");
2301                       } else if (surfaceType == Java2D.WINDOW) {
2302                         System.err.println("WINDOW");
2303                       } else if (surfaceType == Java2D.PBUFFER) {
2304                         System.err.println("PBUFFER");
2305                       } else if (surfaceType == Java2D.TEXTURE) {
2306                         System.err.println("TEXTURE");
2307                       } else if (surfaceType == Java2D.FLIP_BACKBUFFER) {
2308                         System.err.println("FLIP_BACKBUFFER");
2309                       } else if (surfaceType == Java2D.FBOBJECT) {
2310                         System.err.println("FBOBJECT");
2311                       } else {
2312                         System.err.println("(Unknown surface type " + surfaceType + ")");
2313                       }
2314                   }
2315                 }
2316                 if (joglContext == null) {
2317                   AbstractGraphicsDevice device = j2dContext.getGLDrawable().getNativeSurface().getGraphicsConfiguration().getScreen().getDevice();
2318                   if (factory.canCreateExternalGLDrawable(device)) {
2319                     joglDrawable = factory.createExternalGLDrawable();
2320                     joglContext = joglDrawable.createContext(j2dContext);
2321                     if (DEBUG) {
2322                         System.err.println("-- Created External Drawable: "+joglDrawable);
2323                         System.err.println("-- Created Context: "+joglContext);
2324                     }
2325                   }
2326                   if (Java2D.isFBOEnabled() &&
2327                       Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT &&
2328                       fbObjectWorkarounds) {
2329                     createNewDepthBuffer = true;
2330                   }
2331                 }
2332                 helper.invokeGL(joglDrawable, joglContext, updaterDisplayAction, updaterInitAction);
2333               }
2334             } finally {
2335               j2dContext.release();
2336             }
2337           }
2338         });
2339     }
2340
2341     @Override
2342     public final void doPlainPaint() {
2343       helper.invokeGL(joglDrawable, joglContext, updaterPlainDisplayAction, updaterInitAction);
2344     }
2345
2346     private final void captureJ2DState(GL gl, Graphics g) {
2347       gl.glGetIntegerv(GL2.GL_DRAW_BUFFER, drawBuffer, 0);
2348       gl.glGetIntegerv(GL2.GL_READ_BUFFER, readBuffer, 0);
2349       if (Java2D.isFBOEnabled() &&
2350           Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) {
2351         gl.glGetIntegerv(GL.GL_FRAMEBUFFER_BINDING, frameBuffer, 0);
2352         if(!gl.glIsFramebuffer(frameBuffer[0])) {
2353           checkedForFBObjectWorkarounds=true;
2354           fbObjectWorkarounds = true;
2355           createNewDepthBuffer = true;
2356           if (DEBUG) {
2357               System.err.println(getThreadName()+": GLJPanel: Fetched ERR GL_FRAMEBUFFER_BINDING: "+frameBuffer[0]+" - NOT A FBO"+
2358                                  ", frame_buffer_object workarounds to be necessary");
2359           }
2360         } else if (DEBUG) {
2361           System.err.println(getThreadName()+": GLJPanel: Fetched OK GL_FRAMEBUFFER_BINDING: "+frameBuffer[0]);
2362         }
2363
2364         if(fbObjectWorkarounds || !checkedForFBObjectWorkarounds) {
2365             // See above for description of what we are doing here
2366             if (frameBufferTexture == null)
2367                 frameBufferTexture = new int[1];
2368
2369             // Query the framebuffer for its color buffer so we can hook
2370             // it back up in our context (should not be necessary)
2371             gl.glGetFramebufferAttachmentParameteriv(GL.GL_FRAMEBUFFER,
2372                                                      GL.GL_COLOR_ATTACHMENT0,
2373                                                      GL.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
2374                                                      frameBufferTexture, 0);
2375             if (DEBUG) {
2376                 System.err.println(getThreadName()+": GLJPanel: FBO COLOR_ATTACHMENT0: " + frameBufferTexture[0]);
2377             }
2378         }
2379
2380         if (!checkedGLVendor) {
2381           checkedGLVendor = true;
2382           String vendor = gl.glGetString(GL.GL_VENDOR);
2383
2384           if ((vendor != null) &&
2385               vendor.startsWith("ATI")) {
2386             vendorIsATI = true;
2387           }
2388         }
2389
2390         if (vendorIsATI) {
2391           // Unbind the FBO from Java2D's context as it appears that
2392           // driver bugs on ATI's side are causing problems if the FBO is
2393           // simultaneously bound to more than one context. Java2D will
2394           // re-bind the FBO during the next validation of its context.
2395           // Note: this breaks rendering at least on NVidia hardware
2396           gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0);
2397         }
2398       }
2399     }
2400   }
2401 }
http://JogAmp.org git info: FAQ, tutorial and man pages.