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