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