Jogamp
Bug 1054: GLContext: makeCurrent() needs a null-check of [mutable] drawable; Review...
authorSven Gothel <sgothel@jausoft.com>
Fri, 29 Aug 2014 22:21:00 +0000 (00:21 +0200)
committerSven Gothel <sgothel@jausoft.com>
Fri, 29 Aug 2014 22:21:00 +0000 (00:21 +0200)
'drawable' field of GLContextImpl is mutable via setGLDrawable(..),
which requires high-level locking as documented.

The required high-level locking allows us to _not_ add special
synchronization to this field (and drawableRead).

A simple null-check in makeCurrent() shall be sufficient,
plus ensuring mentioned high-level locking is applied.

GLContextImpl 'drawable' and 'drawableRead' synchronization:
  - commit ad79bd072b600a3f2416cc6f0c61e2925000069d check of null drawable is sufficient
  - Add GLAutoDrawable upstream-lock locking to:
    - AWT GLCanvas setupPrint/releasePrint
    - AWT GLJPanel (was missing)

Misc:
  - validate shared-context native-surface locking, throw exception if not successful
  - pixelDataEvaluated does not need to be synchronized, since it's being called while context is current, locking
  - GLDrawableHelper.recreateGLDrawable(..): Remove redundant glFinish() call

src/jogl/classes/javax/media/opengl/GLContext.java
src/jogl/classes/javax/media/opengl/awt/GLCanvas.java
src/jogl/classes/javax/media/opengl/awt/GLJPanel.java
src/jogl/classes/jogamp/opengl/GLContextImpl.java
src/jogl/classes/jogamp/opengl/GLDrawableHelper.java

index e10579d..9389cca 100644 (file)
@@ -326,6 +326,9 @@ public abstract class GLContext {
    * If the read-drawable has not been changed manually via {@link #setGLReadDrawable(GLDrawable)},
    * it equals to the write-drawable (default).
    * </p>
+   * <p>
+   * Method is only thread-safe while context is {@link #makeCurrent() made current}.
+   * </p>
    * @see #setGLDrawable(GLDrawable, boolean)
    * @see #setGLReadDrawable(GLDrawable)
    */
@@ -364,6 +367,9 @@ public abstract class GLContext {
    * If the read-drawable has not been changed manually via {@link #setGLReadDrawable(GLDrawable)},
    * it equals to the write-drawable (default).
    * </p>
+   * <p>
+   * Method is only thread-safe while context is {@link #makeCurrent() made current}.
+   * </p>
    * @see #isGLReadDrawableAvailable()
    * @see #setGLReadDrawable(GLDrawable)
    * @see #getGLReadDrawable()
@@ -1262,6 +1268,9 @@ public abstract class GLContext {
   /**
    * Return the framebuffer name bound to this context,
    * see {@link GL#glBindFramebuffer(int, int)}.
+   * <p>
+   * Method is only thread-safe while context is {@link #makeCurrent() made current}.
+   * </p>
    */
   public abstract int getBoundFramebuffer(int target);
 
@@ -1272,6 +1281,9 @@ public abstract class GLContext {
    * in case an framebuffer object ({@link com.jogamp.opengl.FBObject}) based drawable
    * is being used.
    * </p>
+   * <p>
+   * Method is only thread-safe while context is {@link #makeCurrent() made current}.
+   * </p>
    */
   public abstract int getDefaultDrawFramebuffer();
 
@@ -1282,6 +1294,9 @@ public abstract class GLContext {
    * in case an framebuffer object ({@link com.jogamp.opengl.FBObject}) based drawable
    * is being used.
    * </p>
+   * <p>
+   * Method is only thread-safe while context is {@link #makeCurrent() made current}.
+   * </p>
    */
   public abstract int getDefaultReadFramebuffer();
 
@@ -1306,13 +1321,26 @@ public abstract class GLContext {
    * Note-3: See {@link com.jogamp.opengl.util.GLDrawableUtil#swapBuffersBeforeRead(GLCapabilitiesImmutable) swapBuffersBeforeRead}
    * for read-pixels and swap-buffers implications.
    * </p>
+   * <p>
+   * Method is only thread-safe while context is {@link #makeCurrent() made current}.
+   * </p>
    */
   public abstract int getDefaultReadBuffer();
 
-  /** Get the default pixel data type, as required by e.g. {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer)}. */
+  /**
+   * Get the default pixel data type, as required by e.g. {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer)}.
+   * <p>
+   * Method is only thread-safe while context is {@link #makeCurrent() made current}.
+   * </p>
+   */
   public abstract int getDefaultPixelDataType();
 
-  /** Get the default pixel data format, as required by e.g. {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer)}. */
+  /**
+   * Get the default pixel data format, as required by e.g. {@link GL#glReadPixels(int, int, int, int, int, int, java.nio.Buffer)}.
+   * <p>
+   * Method is only thread-safe while context is {@link #makeCurrent() made current}.
+   * </p>
+   */
   public abstract int getDefaultPixelDataFormat();
 
   /**
index b8e518e..563158a 100644 (file)
@@ -842,68 +842,74 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
   private final Runnable setupPrintOnEDT = new Runnable() {
       @Override
       public void run() {
-          if( !validateGLDrawable() ) {
-              if(DEBUG) {
-                  System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, drawable not valid yet");
+          final RecursiveLock _lock = lock;
+          _lock.lock();
+          try {
+              if( !validateGLDrawable() ) {
+                  if(DEBUG) {
+                      System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, drawable not valid yet");
+                  }
+                  printActive = false;
+                  return; // not yet available ..
               }
-              printActive = false;
-              return; // not yet available ..
-          }
-          if( !isVisible() ) {
-              if(DEBUG) {
-                  System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, canvas not visible");
+              if( !isVisible() ) {
+                  if(DEBUG) {
+                      System.err.println(getThreadName()+": Info: GLCanvas setupPrint - skipped GL render, canvas not visible");
+                  }
+                  printActive = false;
+                  return; // not yet available ..
               }
-              printActive = false;
-              return; // not yet available ..
-          }
-          sendReshape = false; // clear reshape flag
-          printAnimator =  helper.getAnimator();
-          if( null != printAnimator ) {
-              printAnimator.remove(GLCanvas.this);
-          }
-          printGLAD = GLCanvas.this; // _not_ default, shall be replaced by offscreen GLAD
-          final GLCapabilitiesImmutable gladCaps = getChosenGLCapabilities();
-          final int printNumSamples = printAWTTiles.getNumSamples(gladCaps);
-          GLDrawable printDrawable = printGLAD.getDelegatedDrawable();
-          final boolean reqNewGLADSamples = printNumSamples != gladCaps.getNumSamples();
-          final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() ||
-                                         printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight();
-          final boolean reqNewGLADOnscrn = gladCaps.isOnscreen();
-
-          final GLCapabilities newGLADCaps = (GLCapabilities)gladCaps.cloneMutable();
-          newGLADCaps.setDoubleBuffered(false);
-          newGLADCaps.setOnscreen(false);
-          if( printNumSamples != newGLADCaps.getNumSamples() ) {
-              newGLADCaps.setSampleBuffers(0 < printNumSamples);
-              newGLADCaps.setNumSamples(printNumSamples);
-          }
-          final boolean reqNewGLADSafe = GLDrawableUtil.isSwapGLContextSafe(getRequestedGLCapabilities(), gladCaps, newGLADCaps);
+              sendReshape = false; // clear reshape flag
+              printAnimator =  helper.getAnimator();
+              if( null != printAnimator ) {
+                  printAnimator.remove(GLCanvas.this);
+              }
+              printGLAD = GLCanvas.this; // _not_ default, shall be replaced by offscreen GLAD
+              final GLCapabilitiesImmutable gladCaps = getChosenGLCapabilities();
+              final int printNumSamples = printAWTTiles.getNumSamples(gladCaps);
+              GLDrawable printDrawable = printGLAD.getDelegatedDrawable();
+              final boolean reqNewGLADSamples = printNumSamples != gladCaps.getNumSamples();
+              final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() ||
+                                             printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight();
+              final boolean reqNewGLADOnscrn = gladCaps.isOnscreen();
+
+              final GLCapabilities newGLADCaps = (GLCapabilities)gladCaps.cloneMutable();
+              newGLADCaps.setDoubleBuffered(false);
+              newGLADCaps.setOnscreen(false);
+              if( printNumSamples != newGLADCaps.getNumSamples() ) {
+                  newGLADCaps.setSampleBuffers(0 < printNumSamples);
+                  newGLADCaps.setNumSamples(printNumSamples);
+              }
+              final boolean reqNewGLADSafe = GLDrawableUtil.isSwapGLContextSafe(getRequestedGLCapabilities(), gladCaps, newGLADCaps);
 
-          final boolean reqNewGLAD = ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize ) && reqNewGLADSafe;
+              final boolean reqNewGLAD = ( reqNewGLADOnscrn || reqNewGLADSamples || reqNewGLADSize ) && reqNewGLADSafe;
 
-          if( DEBUG ) {
-              System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+", safe "+reqNewGLADSafe+"], "+
-                                 ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+
-                                 ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+
-                                 ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+
-                                 ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator);
-          }
-          if( reqNewGLAD ) {
-              final GLDrawableFactory factory = GLDrawableFactory.getFactory(newGLADCaps.getGLProfile());
-              printGLAD = factory.createOffscreenAutoDrawable(null, newGLADCaps, null,
-                      printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE,
-                      printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE);
-              GLDrawableUtil.swapGLContextAndAllGLEventListener(GLCanvas.this, printGLAD);
-              printDrawable = printGLAD.getDelegatedDrawable();
-          }
-          printAWTTiles.setGLOrientation(printGLAD.isGLOriented(), printGLAD.isGLOriented());
-          printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0);
-          printAWTTiles.renderer.attachAutoDrawable(printGLAD);
-          if( DEBUG ) {
-              System.err.println("AWT print.setup "+printAWTTiles);
-              System.err.println("AWT print.setup AA "+printNumSamples+", "+newGLADCaps);
-              System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD);
-              System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable);
+              if( DEBUG ) {
+                  System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ onscreen "+reqNewGLADOnscrn+", samples "+reqNewGLADSamples+", size "+reqNewGLADSize+", safe "+reqNewGLADSafe+"], "+
+                                     ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+
+                                     ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+
+                                     ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+
+                                     ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator);
+              }
+              if( reqNewGLAD ) {
+                  final GLDrawableFactory factory = GLDrawableFactory.getFactory(newGLADCaps.getGLProfile());
+                  printGLAD = factory.createOffscreenAutoDrawable(null, newGLADCaps, null,
+                          printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE,
+                          printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE);
+                  GLDrawableUtil.swapGLContextAndAllGLEventListener(GLCanvas.this, printGLAD);
+                  printDrawable = printGLAD.getDelegatedDrawable();
+              }
+              printAWTTiles.setGLOrientation(printGLAD.isGLOriented(), printGLAD.isGLOriented());
+              printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0);
+              printAWTTiles.renderer.attachAutoDrawable(printGLAD);
+              if( DEBUG ) {
+                  System.err.println("AWT print.setup "+printAWTTiles);
+                  System.err.println("AWT print.setup AA "+printNumSamples+", "+newGLADCaps);
+                  System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD);
+                  System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable);
+              }
+          } finally {
+              _lock.unlock();
           }
       }
   };
@@ -919,23 +925,29 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing
   private final Runnable releasePrintOnEDT = new Runnable() {
       @Override
       public void run() {
-          if( DEBUG ) {
-              System.err.println("AWT print.release "+printAWTTiles);
-          }
-          printAWTTiles.dispose();
-          printAWTTiles= null;
-          if( printGLAD != GLCanvas.this ) {
-              GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLCanvas.this);
-              printGLAD.destroy();
-          }
-          printGLAD = null;
-          if( null != printAnimator ) {
-              printAnimator.add(GLCanvas.this);
-              printAnimator = null;
+          final RecursiveLock _lock = lock;
+          _lock.lock();
+          try {
+              if( DEBUG ) {
+                  System.err.println("AWT print.release "+printAWTTiles);
+              }
+              printAWTTiles.dispose();
+              printAWTTiles= null;
+              if( printGLAD != GLCanvas.this ) {
+                  GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLCanvas.this);
+                  printGLAD.destroy();
+              }
+              printGLAD = null;
+              if( null != printAnimator ) {
+                  printAnimator.add(GLCanvas.this);
+                  printAnimator = null;
+              }
+              sendReshape = true; // trigger reshape, i.e. gl-viewport and -listener - this component might got resized!
+              printActive = false;
+              display();
+          } finally {
+              _lock.unlock();
           }
-          sendReshape = true; // trigger reshape, i.e. gl-viewport and -listener - this component might got resized!
-          printActive = false;
-          display();
       }
   };
 
index a95681e..e2ccc10 100644 (file)
@@ -239,7 +239,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
       return singleAWTGLPixelBufferProvider;
   }
 
-  /** Currently not used internally, exist merely to satisfy {@link #getUpstreamLock()}. */
   private final RecursiveLock lock = LockFactory.createRecursiveLock();
 
   private final GLDrawableHelper helper;
@@ -538,28 +537,34 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
       return;
     }
 
-    if( !isInitialized ) {
-        initializeBackendImpl();
-    }
-
-    if (!isInitialized || printActive) {
-      return;
-    }
+    final RecursiveLock _lock = lock;
+    _lock.lock();
+    try {
+        if( !isInitialized ) {
+            initializeBackendImpl();
+        }
 
-    // NOTE: must do this when the context is not current as it may
-    // involve destroying the pbuffer (current context) and
-    // re-creating it -- tricky to do properly while the context is
-    // current
-    if( !printActive ) {
-        if (handleReshape) {
-          handleReshape = false;
-          sendReshape = handleReshape();
+        if (!isInitialized || printActive) {
+          return;
         }
 
-        if( isShowing ) {
-          updater.setGraphics(g);
-          backend.doPaintComponent(g);
+        // NOTE: must do this when the context is not current as it may
+        // involve destroying the pbuffer (current context) and
+        // re-creating it -- tricky to do properly while the context is
+        // current
+        if( !printActive ) {
+            if (handleReshape) {
+              handleReshape = false;
+              sendReshape = handleReshape();
+            }
+
+            if( isShowing ) {
+              updater.setGraphics(g);
+              backend.doPaintComponent(g);
+            }
         }
+    } finally {
+        _lock.unlock();
     }
   }
 
@@ -689,72 +694,78 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
   private final Runnable setupPrintOnEDT = new Runnable() {
       @Override
       public void run() {
-          if( !isInitialized ) {
-              initializeBackendImpl();
-          }
-          if (!isInitialized) {
-              if(DEBUG) {
-                  System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, drawable not valid yet");
+          final RecursiveLock _lock = lock;
+          _lock.lock();
+          try {
+              if( !isInitialized ) {
+                  initializeBackendImpl();
               }
-              printActive = false;
-              return; // not yet available ..
-          }
-          if( !isVisible() ) {
-              if(DEBUG) {
-                  System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, panel not visible");
+              if (!isInitialized) {
+                  if(DEBUG) {
+                      System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, drawable not valid yet");
+                  }
+                  printActive = false;
+                  return; // not yet available ..
+              }
+              if( !isVisible() ) {
+                  if(DEBUG) {
+                      System.err.println(getThreadName()+": Info: GLJPanel setupPrint - skipped GL render, panel not visible");
+                  }
+                  printActive = false;
+                  return; // not yet available ..
+              }
+              sendReshape = false; // clear reshape flag
+              handleReshape = false; // ditto
+              printAnimator =  helper.getAnimator();
+              if( null != printAnimator ) {
+                  printAnimator.remove(GLJPanel.this);
               }
-              printActive = false;
-              return; // not yet available ..
-          }
-          sendReshape = false; // clear reshape flag
-          handleReshape = false; // ditto
-          printAnimator =  helper.getAnimator();
-          if( null != printAnimator ) {
-              printAnimator.remove(GLJPanel.this);
-          }
 
-          printGLAD = GLJPanel.this; // default: re-use
-          final GLCapabilitiesImmutable gladCaps = getChosenGLCapabilities();
-          final int printNumSamples = printAWTTiles.getNumSamples(gladCaps);
-          GLDrawable printDrawable = printGLAD.getDelegatedDrawable();
-          final boolean reqNewGLADSamples = printNumSamples != gladCaps.getNumSamples();
-          final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() ||
-                                         printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight();
-
-          final GLCapabilities newGLADCaps = (GLCapabilities)gladCaps.cloneMutable();
-          newGLADCaps.setDoubleBuffered(false);
-          newGLADCaps.setOnscreen(false);
-          if( printNumSamples != newGLADCaps.getNumSamples() ) {
-              newGLADCaps.setSampleBuffers(0 < printNumSamples);
-              newGLADCaps.setNumSamples(printNumSamples);
-          }
-          final boolean reqNewGLADSafe = GLDrawableUtil.isSwapGLContextSafe(getRequestedGLCapabilities(), gladCaps, newGLADCaps);
+              printGLAD = GLJPanel.this; // default: re-use
+              final GLCapabilitiesImmutable gladCaps = getChosenGLCapabilities();
+              final int printNumSamples = printAWTTiles.getNumSamples(gladCaps);
+              GLDrawable printDrawable = printGLAD.getDelegatedDrawable();
+              final boolean reqNewGLADSamples = printNumSamples != gladCaps.getNumSamples();
+              final boolean reqNewGLADSize = printAWTTiles.customTileWidth != -1 && printAWTTiles.customTileWidth != printDrawable.getSurfaceWidth() ||
+                                             printAWTTiles.customTileHeight != -1 && printAWTTiles.customTileHeight != printDrawable.getSurfaceHeight();
+
+              final GLCapabilities newGLADCaps = (GLCapabilities)gladCaps.cloneMutable();
+              newGLADCaps.setDoubleBuffered(false);
+              newGLADCaps.setOnscreen(false);
+              if( printNumSamples != newGLADCaps.getNumSamples() ) {
+                  newGLADCaps.setSampleBuffers(0 < printNumSamples);
+                  newGLADCaps.setNumSamples(printNumSamples);
+              }
+              final boolean reqNewGLADSafe = GLDrawableUtil.isSwapGLContextSafe(getRequestedGLCapabilities(), gladCaps, newGLADCaps);
 
-          final boolean reqNewGLAD = ( reqNewGLADSamples || reqNewGLADSize ) && reqNewGLADSafe;
+              final boolean reqNewGLAD = ( reqNewGLADSamples || reqNewGLADSize ) && reqNewGLADSafe;
 
-          if( DEBUG ) {
-              System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ samples "+reqNewGLADSamples+", size "+reqNewGLADSize+", safe "+reqNewGLADSafe+"], "+
-                                 ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+
-                                 ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+
-                                 ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+
-                                 ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator);
-          }
-          if( reqNewGLAD ) {
-              final GLDrawableFactory factory = GLDrawableFactory.getFactory(newGLADCaps.getGLProfile());
-              printGLAD = factory.createOffscreenAutoDrawable(null, newGLADCaps, null,
-                      printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE,
-                      printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE);
-              GLDrawableUtil.swapGLContextAndAllGLEventListener(GLJPanel.this, printGLAD);
-              printDrawable = printGLAD.getDelegatedDrawable();
-          }
-          printAWTTiles.setGLOrientation( !GLJPanel.this.skipGLOrientationVerticalFlip && printGLAD.isGLOriented(), printGLAD.isGLOriented() );
-          printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0);
-          printAWTTiles.renderer.attachAutoDrawable(printGLAD);
-          if( DEBUG ) {
-              System.err.println("AWT print.setup "+printAWTTiles);
-              System.err.println("AWT print.setup AA "+printNumSamples+", "+newGLADCaps);
-              System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD);
-              System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable);
+              if( DEBUG ) {
+                  System.err.println("AWT print.setup: reqNewGLAD "+reqNewGLAD+"[ samples "+reqNewGLADSamples+", size "+reqNewGLADSize+", safe "+reqNewGLADSafe+"], "+
+                                     ", drawableSize "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+
+                                     ", customTileSize "+printAWTTiles.customTileWidth+"x"+printAWTTiles.customTileHeight+
+                                     ", scaleMat "+printAWTTiles.scaleMatX+" x "+printAWTTiles.scaleMatY+
+                                     ", numSamples "+printAWTTiles.customNumSamples+" -> "+printNumSamples+", printAnimator "+printAnimator);
+              }
+              if( reqNewGLAD ) {
+                  final GLDrawableFactory factory = GLDrawableFactory.getFactory(newGLADCaps.getGLProfile());
+                  printGLAD = factory.createOffscreenAutoDrawable(null, newGLADCaps, null,
+                          printAWTTiles.customTileWidth != -1 ? printAWTTiles.customTileWidth : DEFAULT_PRINT_TILE_SIZE,
+                          printAWTTiles.customTileHeight != -1 ? printAWTTiles.customTileHeight : DEFAULT_PRINT_TILE_SIZE);
+                  GLDrawableUtil.swapGLContextAndAllGLEventListener(GLJPanel.this, printGLAD);
+                  printDrawable = printGLAD.getDelegatedDrawable();
+              }
+              printAWTTiles.setGLOrientation( !GLJPanel.this.skipGLOrientationVerticalFlip && printGLAD.isGLOriented(), printGLAD.isGLOriented() );
+              printAWTTiles.renderer.setTileSize(printDrawable.getSurfaceWidth(), printDrawable.getSurfaceHeight(), 0);
+              printAWTTiles.renderer.attachAutoDrawable(printGLAD);
+              if( DEBUG ) {
+                  System.err.println("AWT print.setup "+printAWTTiles);
+                  System.err.println("AWT print.setup AA "+printNumSamples+", "+newGLADCaps);
+                  System.err.println("AWT print.setup printGLAD: "+printGLAD.getSurfaceWidth()+"x"+printGLAD.getSurfaceHeight()+", "+printGLAD);
+                  System.err.println("AWT print.setup printDraw: "+printDrawable.getSurfaceWidth()+"x"+printDrawable.getSurfaceHeight()+", "+printDrawable);
+              }
+          } finally {
+              _lock.unlock();
           }
       }
   };
@@ -772,43 +783,49 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
   private final Runnable releasePrintOnEDT = new Runnable() {
       @Override
       public void run() {
-          if( DEBUG ) {
-              System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0 "+printAWTTiles);
-          }
-          printAWTTiles.dispose();
-          printAWTTiles= null;
-          if( printGLAD != GLJPanel.this ) {
-              GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLJPanel.this);
-              printGLAD.destroy();
-          }
-          printGLAD = null;
-          if( null != printAnimator ) {
-              printAnimator.add(GLJPanel.this);
-              printAnimator = null;
-          }
+          final RecursiveLock _lock = lock;
+          _lock.lock();
+          try {
+              if( DEBUG ) {
+                  System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0 "+printAWTTiles);
+              }
+              printAWTTiles.dispose();
+              printAWTTiles= null;
+              if( printGLAD != GLJPanel.this ) {
+                  GLDrawableUtil.swapGLContextAndAllGLEventListener(printGLAD, GLJPanel.this);
+                  printGLAD.destroy();
+              }
+              printGLAD = null;
+              if( null != printAnimator ) {
+                  printAnimator.add(GLJPanel.this);
+                  printAnimator = null;
+              }
 
-          // trigger reshape, i.e. gl-viewport and -listener - this component might got resized!
-          final int awtWidth = GLJPanel.this.getWidth();
-          final int awtHeight= GLJPanel.this.getHeight();
-          final int scaledAWTWidth = awtWidth * hasPixelScale[0];
-          final int scaledAWTHeight= awtHeight * hasPixelScale[1];
-          final GLDrawable drawable = GLJPanel.this.getDelegatedDrawable();
-          if( scaledAWTWidth != panelWidth || scaledAWTHeight != panelHeight ||
-              drawable.getSurfaceWidth() != panelWidth || drawable.getSurfaceHeight() != panelHeight ) {
-              // -> !( awtSize == panelSize == drawableSize )
-              if ( DEBUG ) {
-                  System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0: resizeWithinPrint panel " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScaleStr()+
-                          ", draw "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight()+
-                          " -> " + awtWidth+"x"+awtHeight+" * "+getPixelScaleStr()+" -> "+scaledAWTWidth+"x"+scaledAWTHeight);
+              // trigger reshape, i.e. gl-viewport and -listener - this component might got resized!
+              final int awtWidth = GLJPanel.this.getWidth();
+              final int awtHeight= GLJPanel.this.getHeight();
+              final int scaledAWTWidth = awtWidth * hasPixelScale[0];
+              final int scaledAWTHeight= awtHeight * hasPixelScale[1];
+              final GLDrawable drawable = GLJPanel.this.getDelegatedDrawable();
+              if( scaledAWTWidth != panelWidth || scaledAWTHeight != panelHeight ||
+                  drawable.getSurfaceWidth() != panelWidth || drawable.getSurfaceHeight() != panelHeight ) {
+                  // -> !( awtSize == panelSize == drawableSize )
+                  if ( DEBUG ) {
+                      System.err.println(getThreadName()+": GLJPanel.releasePrintOnEDT.0: resizeWithinPrint panel " +panelWidth+"x"+panelHeight + " @ scale "+getPixelScaleStr()+
+                              ", draw "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight()+
+                              " -> " + awtWidth+"x"+awtHeight+" * "+getPixelScaleStr()+" -> "+scaledAWTWidth+"x"+scaledAWTHeight);
+                  }
+                  reshapeWidth = scaledAWTWidth;
+                  reshapeHeight = scaledAWTHeight;
+                  sendReshape = handleReshape(); // reshapeSize -> panelSize, backend reshape w/ GL reshape
+              } else {
+                  sendReshape = true; // only GL reshape
               }
-              reshapeWidth = scaledAWTWidth;
-              reshapeHeight = scaledAWTHeight;
-              sendReshape = handleReshape(); // reshapeSize -> panelSize, backend reshape w/ GL reshape
-          } else {
-              sendReshape = true; // only GL reshape
+              printActive = false;
+              display();
+          } finally {
+              _lock.unlock();
           }
-          printActive = false;
-          display();
       }
   };
 
@@ -966,11 +983,17 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
 
   @Override
   public GLContext createContext(final GLContext shareWith) {
-    final Backend b = backend;
-    if ( null == b ) {
-        return null;
+    final RecursiveLock _lock = lock;
+    _lock.lock();
+    try {
+        final Backend b = backend;
+        if ( null == b ) {
+            return null;
+        }
+        return b.createContext(shareWith);
+    } finally {
+        _lock.unlock();
     }
-    return b.createContext(shareWith);
   }
 
   @Override
@@ -984,14 +1007,20 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
 
   @Override
   public GLContext setContext(final GLContext newCtx, final boolean destroyPrevCtx) {
-    final Backend b = backend;
-    if ( null == b ) {
-        return null;
+    final RecursiveLock _lock = lock;
+    _lock.lock();
+    try {
+        final Backend b = backend;
+        if ( null == b ) {
+            return null;
+        }
+        final GLContext oldCtx = b.getContext();
+        GLDrawableHelper.switchContext(b.getDrawable(), oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags);
+        b.setContext(newCtx);
+        return oldCtx;
+    } finally {
+        _lock.unlock();
     }
-    final GLContext oldCtx = b.getContext();
-    GLDrawableHelper.switchContext(b.getDrawable(), oldCtx, destroyPrevCtx, newCtx, additionalCtxCreationFlags);
-    b.setContext(newCtx);
-    return oldCtx;
   }
 
 
@@ -1342,36 +1371,42 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing
   private final Runnable disposeAction = new Runnable() {
     @Override
     public void run() {
-        if ( null != backend ) {
-            final GLContext _context = backend.getContext();
-            final boolean backendDestroy = !backend.isUsingOwnLifecycle();
-
-            GLException exceptionOnDisposeGL = null;
-            if( null != _context && _context.isCreated() ) {
-                try {
-                    helper.disposeGL(GLJPanel.this, _context, !backendDestroy);
-                } catch (final GLException gle) {
-                    exceptionOnDisposeGL = gle;
+        final RecursiveLock _lock = lock;
+        _lock.lock();
+        try {
+            if ( null != backend ) {
+                final GLContext _context = backend.getContext();
+                final boolean backendDestroy = !backend.isUsingOwnLifecycle();
+
+                GLException exceptionOnDisposeGL = null;
+                if( null != _context && _context.isCreated() ) {
+                    try {
+                        helper.disposeGL(GLJPanel.this, _context, !backendDestroy);
+                    } catch (final GLException gle) {
+                        exceptionOnDisposeGL = gle;
+                    }
                 }
-            }
-            Throwable exceptionBackendDestroy = null;
-            if ( backendDestroy ) {
-                try {
-                    backend.destroy();
-                } catch( final Throwable re ) {
-                    exceptionBackendDestroy = re;
+                Throwable exceptionBackendDestroy = null;
+                if ( backendDestroy ) {
+                    try {
+                        backend.destroy();
+                    } catch( final Throwable re ) {
+                        exceptionBackendDestroy = re;
+                    }
+                    backend = null;
+                    isInitialized = false;
                 }
-                backend = null;
-                isInitialized = false;
-            }
 
-            // throw exception in order of occurrence ..
-            if( null != exceptionOnDisposeGL ) {
-                throw exceptionOnDisposeGL;
-            }
-            if( null != exceptionBackendDestroy ) {
-                throw GLException.newGLException(exceptionBackendDestroy);
+                // throw exception in order of occurrence ..
+                if( null != exceptionOnDisposeGL ) {
+                    throw exceptionOnDisposeGL;
+                }
+                if( null != exceptionBackendDestroy ) {
+                    throw GLException.newGLException(exceptionBackendDestroy);
+                }
             }
+        } finally {
+            _lock.unlock();
         }
     }
   };
index c5a125a..e278afc 100644 (file)
@@ -108,10 +108,17 @@ public abstract class GLContextImpl extends GLContext {
   private final int[] boundFBOTarget = new int[] { 0, 0 }; // { draw, read }
   private int defaultVAO = 0;
 
+  /**
+   * <ul>
+   *   <li>[GLAutoDrawable.upstreamLock].lock()</li>
+   *   <li>drawable.surface.lock()</li>
+   *   <li>contextLock.lock()</li>
+   * </ul>
+   */
   protected GLDrawableImpl drawable;
   protected GLDrawableImpl drawableRead;
 
-  private volatile boolean pixelDataEvaluated;
+  private boolean pixelDataEvaluated;
   private int /* pixelDataInternalFormat, */ pixelDataFormat, pixelDataType;
 
   protected GL gl;
@@ -192,28 +199,28 @@ public abstract class GLContextImpl extends GLContext {
 
   @Override
   public final GLDrawable setGLReadDrawable(final GLDrawable read) {
-    // Validate constraints first!
-    if(!isGLReadDrawableAvailable()) {
-        throw new GLException("Setting read drawable feature not available");
-    }
-    final Thread currentThread = Thread.currentThread();
-    if( contextLock.isLockedByOtherThread() ) {
-        throw new GLException("GLContext current by other thread "+contextLock.getOwner().getName()+", operation not allowed on this thread "+currentThread.getName());
-    }
-    final boolean lockHeld = contextLock.isOwner(currentThread);
-    if( lockHeld && contextLock.getHoldCount() > 1 ) {
-        // would need to makeCurrent * holdCount
-        throw new GLException("GLContext is recursively locked - unsupported for setGLDrawable(..)");
-    }
-    if(lockHeld) {
-        release(false);
-    }
-    final GLDrawable old = drawableRead;
-    drawableRead = ( null != read ) ? (GLDrawableImpl) read : drawable;
-    if(lockHeld) {
-        makeCurrent();
-    }
-    return old;
+      // Validate constraints first!
+      if(!isGLReadDrawableAvailable()) {
+          throw new GLException("Setting read drawable feature not available");
+      }
+      final Thread currentThread = Thread.currentThread();
+      if( contextLock.isLockedByOtherThread() ) {
+          throw new GLException("GLContext current by other thread "+contextLock.getOwner().getName()+", operation not allowed on this thread "+currentThread.getName());
+      }
+      final boolean lockHeld = contextLock.isOwner(currentThread);
+      if( lockHeld && contextLock.getHoldCount() > 1 ) {
+          // would need to makeCurrent * holdCount
+          throw new GLException("GLContext is recursively locked - unsupported for setGLDrawable(..)");
+      }
+      if(lockHeld) {
+          release(false);
+      }
+      final GLDrawable old = drawableRead;
+      drawableRead = ( null != read ) ? (GLDrawableImpl) read : drawable;
+      if(lockHeld) {
+          makeCurrent();
+      }
+      return old;
   }
 
   @Override
@@ -223,46 +230,46 @@ public abstract class GLContextImpl extends GLContext {
 
   @Override
   public final GLDrawable setGLDrawable(final GLDrawable readWrite, final boolean setWriteOnly) {
-    // Validate constraints first!
-    final Thread currentThread = Thread.currentThread();
-    if( contextLock.isLockedByOtherThread() ) {
-        throw new GLException("GLContext current by other thread "+contextLock.getOwner().getName()+", operation not allowed on this thread "+currentThread.getName());
-    }
-    final boolean lockHeld = contextLock.isOwner(currentThread);
-    if( lockHeld && contextLock.getHoldCount() > 1 ) {
-        // would need to makeCurrent * holdCount
-        throw new GLException("GLContext is recursively locked - unsupported for setGLDrawable(..)");
-    }
-    if( drawable == readWrite && ( setWriteOnly || drawableRead == readWrite ) ) {
-        return drawable; // no change.
-    }
-    final GLDrawableImpl old = drawable;
-    if( isCreated() && null != old && old.isRealized() ) {
-        if(!lockHeld) {
-            makeCurrent();
-        }
-        // sync GL ctx w/ drawable's framebuffer before de-association
-        gl.glFinish();
-        associateDrawable(false);
-        if(!lockHeld) {
-            release(false);
-        }
-    }
-    if(lockHeld) {
-        release(false);
-    }
-    if( !setWriteOnly || drawableRead == drawable ) { // if !setWriteOnly || !explicitReadDrawable
-        drawableRead = (GLDrawableImpl) readWrite;
-    }
-    drawableRetargeted |= null != drawable && readWrite != drawable;
-    drawable = (GLDrawableImpl) readWrite ;
-    if( isCreated() && null != drawable && drawable.isRealized() ) {
-        makeCurrent(true); // implicit: associateDrawable(true)
-        if( !lockHeld ) {
-            release(false);
-        }
-    }
-    return old;
+      // Validate constraints first!
+      final Thread currentThread = Thread.currentThread();
+      if( contextLock.isLockedByOtherThread() ) {
+          throw new GLException("GLContext current by other thread "+contextLock.getOwner().getName()+", operation not allowed on this thread "+currentThread.getName());
+      }
+      final boolean lockHeld = contextLock.isOwner(currentThread);
+      if( lockHeld && contextLock.getHoldCount() > 1 ) {
+          // would need to makeCurrent * holdCount
+          throw new GLException("GLContext is recursively locked - unsupported for setGLDrawable(..)");
+      }
+      if( drawable == readWrite && ( setWriteOnly || drawableRead == readWrite ) ) {
+          return drawable; // no change.
+      }
+      final GLDrawableImpl old = drawable;
+      if( isCreated() && null != old && old.isRealized() ) {
+          if(!lockHeld) {
+              makeCurrent();
+          }
+          // sync GL ctx w/ drawable's framebuffer before de-association
+          gl.glFinish();
+          associateDrawable(false);
+          if(!lockHeld) {
+              release(false);
+          }
+      }
+      if(lockHeld) {
+          release(false);
+      }
+      if( !setWriteOnly || drawableRead == drawable ) { // if !setWriteOnly || !explicitReadDrawable
+          drawableRead = (GLDrawableImpl) readWrite;
+      }
+      drawableRetargeted |= null != drawable && readWrite != drawable;
+      drawable = (GLDrawableImpl) readWrite ;
+      if( isCreated() && null != drawable && drawable.isRealized() ) {
+          makeCurrent(true); // implicit: associateDrawable(true)
+          if( !lockHeld ) {
+              release(false);
+          }
+      }
+      return old;
   }
 
   @Override
@@ -365,8 +372,8 @@ public abstract class GLContextImpl extends GLContext {
           if( actualRelease ) {
               setCurrent(null);
           }
-          drawable.unlockSurface();
           contextLock.unlock();
+          drawable.unlockSurface();
           if( DEBUG_TRACE_SWITCH ) {
               final String msg = getThreadName() +": GLContext.ContextSwitch[release.X]: obj " + toHexString(hashCode()) + ", ctx "+toHexString(contextHandle)+", surf "+toHexString(drawable.getHandle())+" - "+(actualRelease?"switch":"keep  ")+" - "+contextLock;
               lastCtxReleaseStack = new Throwable(msg);
@@ -544,6 +551,7 @@ public abstract class GLContextImpl extends GLContext {
         }
         return CONTEXT_NOT_CURRENT;
     }
+
     // Note: the surface is locked within [makeCurrent .. swap .. release]
     final int lockRes = drawable.lockSurface();
     if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) {
@@ -670,7 +678,9 @@ public abstract class GLContextImpl extends GLContext {
         final GLContextImpl shareWith = (GLContextImpl) GLContextShareSet.getCreatedShare(this);
         final long shareWithHandle;
         if (null != shareWith) {
-            shareWith.getDrawableImpl().lockSurface();
+            if ( NativeSurface.LOCK_SURFACE_NOT_READY >= shareWith.drawable.lockSurface() ) {
+                throw new GLException("GLContextShareSet could not lock surface: "+shareWith.drawable);
+            }
             shareWithHandle = shareWith.getHandle();
             if (0 == shareWithHandle) {
                 throw new GLException("GLContextShareSet returned an invalid OpenGL context: "+this);
@@ -693,7 +703,7 @@ public abstract class GLContextImpl extends GLContext {
             }
         } finally {
             if (null != shareWith) {
-                shareWith.getDrawableImpl().unlockSurface();
+                shareWith.drawable.unlockSurface();
             }
         }
         if ( DEBUG_TRACE_SWITCH ) {
@@ -789,8 +799,8 @@ public abstract class GLContextImpl extends GLContext {
    *
    * The implementation <b>must</b> leave the context current.<br>
    *
-   * @param share the shared context or null
-   * @return the valid and current context if successful, or null
+   * @param sharedWithHandle the shared context handle or 0
+   * @return true if successful, or false
    * @throws GLException
    */
   protected abstract boolean createImpl(long sharedWithHandle) throws GLException ;
@@ -2151,35 +2161,31 @@ public abstract class GLContextImpl extends GLContext {
   }
 
   private final void evalPixelDataType() {
-    if(!pixelDataEvaluated) {
-        synchronized(this) {
-            if(!pixelDataEvaluated) {
-                boolean ok = false;
-                /* if(isGL2GL3() && 3 == components) {
-                    pixelDataInternalFormat=GL.GL_RGB;
-                    pixelDataFormat=GL.GL_RGB;
-                    pixelDataType = GL.GL_UNSIGNED_BYTE;
-                    ok = true;
-                } else */ if( isGLES2Compatible() || isExtensionAvailable(GLExtensions.OES_read_format) ) {
-                    final int[] glImplColorReadVals = new int[] { 0, 0 };
-                    gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_FORMAT, glImplColorReadVals, 0);
-                    gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_TYPE, glImplColorReadVals, 1);
-                    // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB;
-                    pixelDataFormat = glImplColorReadVals[0];
-                    pixelDataType = glImplColorReadVals[1];
-                    ok = 0 != pixelDataFormat && 0 != pixelDataType;
-                }
-                if( !ok ) {
-                    // RGBA read is safe for all GL profiles
-                    // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB;
-                    pixelDataFormat=GL.GL_RGBA;
-                    pixelDataType = GL.GL_UNSIGNED_BYTE;
-                }
-                // TODO: Consider:
-                // return gl.isGL2GL3()?GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV:GL.GL_UNSIGNED_SHORT_5_5_5_1;
-                pixelDataEvaluated = true;
-            }
-        }
+    if(!pixelDataEvaluated) { // only valid while context is made current
+        boolean ok = false;
+        /* if(isGL2GL3() && 3 == components) {
+            pixelDataInternalFormat=GL.GL_RGB;
+            pixelDataFormat=GL.GL_RGB;
+            pixelDataType = GL.GL_UNSIGNED_BYTE;
+            ok = true;
+        } else */ if( isGLES2Compatible() || isExtensionAvailable(GLExtensions.OES_read_format) ) {
+            final int[] glImplColorReadVals = new int[] { 0, 0 };
+            gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_FORMAT, glImplColorReadVals, 0);
+            gl.glGetIntegerv(GL.GL_IMPLEMENTATION_COLOR_READ_TYPE, glImplColorReadVals, 1);
+            // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB;
+            pixelDataFormat = glImplColorReadVals[0];
+            pixelDataType = glImplColorReadVals[1];
+            ok = 0 != pixelDataFormat && 0 != pixelDataType;
+        }
+        if( !ok ) {
+            // RGBA read is safe for all GL profiles
+            // pixelDataInternalFormat = (4 == components) ? GL.GL_RGBA : GL.GL_RGB;
+            pixelDataFormat=GL.GL_RGBA;
+            pixelDataType = GL.GL_UNSIGNED_BYTE;
+        }
+        // TODO: Consider:
+        // return gl.isGL2GL3()?GL2GL3.GL_UNSIGNED_INT_8_8_8_8_REV:GL.GL_UNSIGNED_SHORT_5_5_5_1;
+        pixelDataEvaluated = true;
     }
   }
 
index f91e1bd..25ff83f 100644 (file)
@@ -282,7 +282,6 @@ public class GLDrawableHelper {
           if( currentContext != context ) {
               context.makeCurrent();
           }
-          context.getGL().glFinish();
           context.setGLDrawable(null, true); // dis-associate
       }
 
@@ -300,7 +299,7 @@ public class GLDrawableHelper {
       }
 
       if(null != context) {
-          context.setGLDrawable(drawable, true); // re-association
+          context.setGLDrawable(drawable, true); // re-association, implicit glFinish() ctx/drawable sync
       }
 
       if( null != currentContext ) {
http://JogAmp.org git info: FAQ, tutorial and man pages.