diff options
Diffstat (limited to 'src')
8 files changed, 147 insertions, 33 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java b/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java index b1bc8828b..7b5e6b8f1 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java +++ b/src/jogl/classes/com/jogamp/opengl/GLRendererQuirks.java @@ -363,8 +363,40 @@ public class GLRendererQuirks { */ public static final int NoPBufferWithAccum = 19; + /** + * Need GL objects (VBO, ..) to be synchronized when utilized + * concurrently from multiple threads via a shared GL context, + * otherwise driver crashes the VM. + * <p> + * Usually synchronization should not be required, if the shared GL objects + * are created and immutable before concurrent usage.<br> + * However, using drivers exposing this issue always require the user to + * synchronize access of shared GL objects. + * </p> + * <p> + * Synchronization can be avoided if accessing the shared GL objects + * exclusively via a queue or {@link com.jogamp.common.util.Ringbuffer Ringbuffer}, see GLMediaPlayerImpl as an example. + * </p> + * <p> + * Appears on: + * <ul> + * <li>Platform OSX + * <ul> + * <li>detected on OSX 10.9.5 first</li> + * <li>any driver</li> + * <li>enabled for all OSX versions</li> + * </ul> + * </li> + * </ul> + * </p> + * <p> + * See Bug 1088 - https://jogamp.org/bugzilla/show_bug.cgi?id=1088 + * </p> + */ + public static final int NeedSharedObjectSync = 20; + /** Return the number of known quirks. */ - public static final int getCount() { return 20; } + public static final int getCount() { return 21; } private static final String[] _names = new String[] { "NoDoubleBufferedPBuffer", "NoDoubleBufferedBitmap", "NoSetSwapInterval", "NoOffscreenBitmap", "NoSetSwapIntervalPostRetarget", "GLSLBuggyDiscard", @@ -372,7 +404,8 @@ public class GLRendererQuirks { "NeedCurrCtx4ARBPixFmtQueries", "NeedCurrCtx4ARBCreateContext", "NoFullFBOSupport", "GLSLNonCompliant", "GL4NeedsGL3Request", "GLSharedContextBuggy", "GLES3ViaEGLES2Config", "SingletonEGLDisplayOnly", - "NoMultiSamplingBuffers", "BuggyColorRenderbuffer", "NoPBufferWithAccum" + "NoMultiSamplingBuffers", "BuggyColorRenderbuffer", "NoPBufferWithAccum", + "NeedSharedObjectSync" }; private static final IdentityHashMap<String, GLRendererQuirks> stickyDeviceQuirks = new IdentityHashMap<String, GLRendererQuirks>(); diff --git a/src/jogl/classes/javax/media/opengl/GLSharedContextSetter.java b/src/jogl/classes/javax/media/opengl/GLSharedContextSetter.java index 526967d69..b8aef126b 100644 --- a/src/jogl/classes/javax/media/opengl/GLSharedContextSetter.java +++ b/src/jogl/classes/javax/media/opengl/GLSharedContextSetter.java @@ -28,6 +28,8 @@ package javax.media.opengl; +import com.jogamp.opengl.GLRendererQuirks; + /** * Adds capabilities to set a shared {@link GLContext} directly or via an {@link GLAutoDrawable}. * <p> @@ -88,6 +90,20 @@ package javax.media.opengl; glad.setVisible(true); // GLWindow creation .. * </pre> * </p> + * <h5><a name="synchronization">GL Object Synchronization</a></h5> + * <p> + * Usually synchronization of shared GL objects should not be required, if the shared GL objects + * are created and immutable before concurrent usage. + * </p> + * <p> + * However, using drivers exposing {@link GLRendererQuirks#NeedSharedObjectSync} always + * require the user to synchronize access of shared GL objects. + * </p> + * <p> + * Synchronization can be avoided if accessing the shared GL objects + * exclusively via a queue or {@link com.jogamp.common.util.Ringbuffer Ringbuffer}, see GLMediaPlayerImpl as an example. + * </p> + * </p> * <h5><a name="driverissues">Known Driver Issues</a></h5> * <h7><a name="intelmesa">Intel's Mesa >= 9.1.2 Backend for [Sandybridge/Ivybridge] on GNU/Linux</a></h7> * <p> diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java index bc8d3b7e4..45a4f2426 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java @@ -1785,6 +1785,13 @@ public abstract class GLContextImpl extends GLContext { } quirks.addQuirk( quirk ); } + { + final int quirk = GLRendererQuirks.NeedSharedObjectSync; + if(DEBUG) { + System.err.println("Quirk: "+GLRendererQuirks.toString(quirk)+": cause: OS "+Platform.getOSType()); + } + quirks.addQuirk( quirk ); + } if( Platform.getOSVersionNumber().compareTo(Platform.OSXVersion.Mavericks) >= 0 && 3==reqMajor && 4==major ) { final int quirk = GLRendererQuirks.GL4NeedsGL3Request; if(DEBUG) { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT2.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT2.java index a66aaf9d8..8d040222a 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT2.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT2.java @@ -107,12 +107,13 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { animator.start(); final GearsES2 g1 = new GearsES2(0); + g1.setSyncObjects(g1); // this is master, since rendered we must use it as sync final GLWindow f1 = createGLWindow(0, 0, g1); animator.add(f1); final InsetsImmutable insets = f1.getInsets(); final GearsES2 g2 = new GearsES2(0); - g2.setSharedGears(g1); + g2.setSharedGears(g1); // also uses master g1 as sync, if required final GLWindow f2 = createGLWindow(f1.getX()+width+insets.getTotalWidth(), f1.getY()+0, g2); f2.setSharedAutoDrawable(f1); @@ -120,7 +121,7 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { f2.setVisible(true); final GearsES2 g3 = new GearsES2(0); - g3.setSharedGears(g1); + g3.setSharedGears(g1); // also uses master g1 as sync, if required final GLWindow f3 = createGLWindow(f1.getX()+0, f1.getY()+height+insets.getTotalHeight(), g3); f3.setSharedAutoDrawable(f1); @@ -222,6 +223,7 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { public void asyncEachAnimator(final boolean destroyCleanOrder) throws InterruptedException { final Animator a1 = new Animator(); final GearsES2 g1 = new GearsES2(0); + g1.setSyncObjects(g1); // this is master, since rendered we must use it as sync final GLWindow f1 = createGLWindow(0, 0, g1); a1.add(f1); a1.start(); @@ -230,7 +232,7 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { final Animator a2 = new Animator(); final GearsES2 g2 = new GearsES2(0); - g2.setSharedGears(g1); + g2.setSharedGears(g1); // also uses master g1 as sync, if required final GLWindow f2 = createGLWindow(f1.getX()+width+insets.getTotalWidth(), f1.getY()+0, g2); f2.setSharedAutoDrawable(f1); @@ -240,7 +242,7 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { final Animator a3 = new Animator(); final GearsES2 g3 = new GearsES2(0); - g3.setSharedGears(g1); + g3.setSharedGears(g1); // also uses master g1 as sync, if required final GLWindow f3 = createGLWindow(f1.getX()+0, f1.getY()+height+insets.getTotalHeight(), g3); f3.setSharedAutoDrawable(f1); @@ -336,8 +338,10 @@ public class TestSharedContextVBOES2NEWT2 extends UITestCase { } static long duration = 1000; // ms + static boolean mainRun = false; public static void main(final String args[]) { + mainRun = true; for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { i++; diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT3.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT3.java index 852b7193e..a0d111c4d 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT3.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestSharedContextVBOES2NEWT3.java @@ -248,6 +248,7 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { public void asyncEachAnimator(final boolean destroyCleanOrder, final boolean useMappedBuffers) throws InterruptedException { final Animator a1 = new Animator(); final GearsES2 g1 = new GearsES2(0); + g1.setSyncObjects(g1); // this is master, since rendered we must use it as sync g1.setUseMappedBuffers(useMappedBuffers); g1.setValidateBuffers(true); final GLWindow f1 = createGLWindow(0, 0, g1); @@ -258,7 +259,7 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { final Animator a2 = new Animator(); final GearsES2 g2 = new GearsES2(0); - g2.setSharedGears(g1); + g2.setSharedGears(g1); // also uses master g1 as sync, if required final GLWindow f2 = createGLWindow(f1.getX()+width+insets.getTotalWidth(), f1.getY()+0, g2); f2.setSharedAutoDrawable(f1); @@ -288,7 +289,7 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { final Animator a3 = new Animator(); final GearsES2 g3 = new GearsES2(0); - g3.setSharedGears(g1); + g3.setSharedGears(g1); // also uses master g1 as sync, if required final GLWindow f3 = createGLWindow(f1.getX()+0, f1.getY()+height+insets.getTotalHeight(), g3); f3.setSharedAutoDrawable(f1); @@ -374,8 +375,10 @@ public class TestSharedContextVBOES2NEWT3 extends UITestCase { static long duration = 1000; // ms static long durationPostDestroy = 1000; // ms - ~60 frames post destroy + static boolean mainRun = false; public static void main(final String args[]) { + mainRun = true; for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { i++; diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/GearsES1.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/GearsES1.java index 78242944f..9d20495a0 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/GearsES1.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es1/GearsES1.java @@ -41,6 +41,7 @@ import com.jogamp.newt.event.KeyListener; import com.jogamp.newt.event.MouseAdapter; import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.MouseListener; +import com.jogamp.opengl.GLRendererQuirks; import com.jogamp.opengl.JoglVersion; import com.jogamp.opengl.test.junit.jogl.demos.GearsObject; import com.jogamp.opengl.util.glsl.fixedfunc.FixedFuncUtil; @@ -65,6 +66,7 @@ public class GearsES1 implements GLEventListener { private GearsObject gear1=null, gear2=null, gear3=null; private FloatBuffer gear1Color=GearsObject.red, gear2Color=GearsObject.green, gear3Color=GearsObject.blue; private GearsES1 sharedGears; + private Object syncObjects; private volatile boolean usesSharedGears = false; private boolean useMappedBuffers = false; private boolean validateBuffers = false; @@ -180,6 +182,13 @@ public class GearsES1 implements GLEventListener { System.err.println("gear1 reuse: "+gear1); System.err.println("gear2 reuse: "+gear2); System.err.println("gear3 reuse: "+gear3); + if( gl.getContext().hasRendererQuirk(GLRendererQuirks.NeedSharedObjectSync) ) { + syncObjects = sharedGears; + System.err.println("Shared GearsES1: Synchronized Objects due to quirk "+GLRendererQuirks.toString(GLRendererQuirks.NeedSharedObjectSync)); + } else { + syncObjects = new Object(); + System.err.println("Shared GearsES1: Unsynchronized Objects"); + } } else { gear1 = new GearsObjectES1(gl, useMappedBuffers, gear1Color, 1.0f, 4.0f, 1.0f, 20, 0.7f, validateBuffers); System.err.println("gear1 created: "+gear1); @@ -189,6 +198,8 @@ public class GearsES1 implements GLEventListener { gear3 = new GearsObjectES1(gl, useMappedBuffers, gear3Color, 1.3f, 2.0f, 0.5f, 10, 0.7f, validateBuffers); System.err.println("gear3 created: "+gear3); + + syncObjects = new Object(); } gl.glEnable(GLLightingFunc.GL_NORMALIZE); @@ -249,6 +260,8 @@ public class GearsES1 implements GLEventListener { gear2 = null; gear3.destroy(gl); gear3 = null; + sharedGears = null; + syncObjects = null; System.err.println(Thread.currentThread()+" GearsES1.dispose FIN"); } @@ -294,9 +307,11 @@ public class GearsES1 implements GLEventListener { gl.glRotatef(view_roty, 0.0f, 1.0f, 0.0f); gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f); - gear1.draw(gl, -3.0f, -2.0f, angle); - gear2.draw(gl, 3.1f, -2.0f, -2.0f * angle - 9.0f); - gear3.draw(gl, -3.1f, 4.2f, -2.0f * angle - 25.0f); + synchronized ( syncObjects ) { + gear1.draw(gl, -3.0f, -2.0f, angle); + gear2.draw(gl, 3.1f, -2.0f, -2.0f * angle - 9.0f); + gear3.draw(gl, -3.1f, 4.2f, -2.0f * angle - 25.0f); + } // Remember that every push needs a pop; this one is paired with // rotating the entire gear assembly diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java index 0f63566a2..a548d4ccf 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/es2/GearsES2.java @@ -30,6 +30,7 @@ import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.MouseListener; import com.jogamp.newt.event.PinchToZoomGesture; import com.jogamp.newt.event.GestureHandler.GestureEvent; +import com.jogamp.opengl.GLRendererQuirks; import com.jogamp.opengl.JoglVersion; import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.Quaternion; @@ -75,6 +76,7 @@ public class GearsES2 implements StereoGLEventListener, TileRendererBase.TileRen private float panX = 0.0f, panY = 0.0f, panZ=0.0f; private volatile GearsObjectES2 gear1=null, gear2=null, gear3=null; private GearsES2 sharedGears = null; + private Object syncObjects = null; private boolean useMappedBuffers = false; private boolean validateBuffers = false; private volatile boolean usesSharedGears = false; @@ -146,6 +148,10 @@ public class GearsES2 implements StereoGLEventListener, TileRendererBase.TileRen sharedGears = shared; } + public void setSyncObjects(final Object sync) { + syncObjects = sync; + } + /** * @return gear1 */ @@ -247,6 +253,13 @@ public class GearsES2 implements StereoGLEventListener, TileRendererBase.TileRen System.err.println("gear2 "+sid()+" created w/ share: "+sharedGears.getGear2()+" -> "+gear2); System.err.println("gear3 "+sid()+" created w/ share: "+sharedGears.getGear3()+" -> "+gear3); } + if( gl.getContext().hasRendererQuirk(GLRendererQuirks.NeedSharedObjectSync) ) { + syncObjects = sharedGears; + System.err.println("Shared GearsES2: Synchronized Objects due to quirk "+GLRendererQuirks.toString(GLRendererQuirks.NeedSharedObjectSync)); + } else if( null == syncObjects ) { + syncObjects = new Object(); + System.err.println("Shared GearsES2: Unsynchronized Objects"); + } } else { gear1 = new GearsObjectES2(gl, useMappedBuffers, st, gear1Color, 1.0f, 4.0f, 1.0f, 20, 0.7f, pmvMatrix, pmvMatrixUniform, colorU, validateBuffers); if(verbose) { @@ -262,6 +275,8 @@ public class GearsES2 implements StereoGLEventListener, TileRendererBase.TileRen if(verbose) { System.err.println("gear3 "+sid()+" created: "+gear2); } + if( null == syncObjects ) { + syncObjects = new Object(); } } @@ -461,6 +476,8 @@ public class GearsES2 implements StereoGLEventListener, TileRendererBase.TileRen colorU = null; st.destroy(gl); st = null; + sharedGears = null; + syncObjects = null; if(verbose) { System.err.println(Thread.currentThread()+" GearsES2.dispose "+sid()+" FIN"); @@ -534,9 +551,11 @@ public class GearsES2 implements StereoGLEventListener, TileRendererBase.TileRen pmvMatrix.glRotatef(view_roty, 0.0f, 1.0f, 0.0f); pmvMatrix.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f); - gear1.draw(gl, -3.0f, -2.0f, 1f * angle - 0f); - gear2.draw(gl, 3.1f, -2.0f, -2f * angle - 9.0f); - gear3.draw(gl, -3.1f, 4.2f, -2f * angle - 25.0f); + synchronized ( syncObjects ) { + gear1.draw(gl, -3.0f, -2.0f, 1f * angle - 0f); + gear2.draw(gl, 3.1f, -2.0f, -2f * angle - 9.0f); + gear3.draw(gl, -3.1f, 4.2f, -2f * angle - 25.0f); + } pmvMatrix.glPopMatrix(); st.useProgram(gl, false); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/Gears.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/Gears.java index 6683f0fbf..28328fcd2 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/Gears.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/Gears.java @@ -20,6 +20,7 @@ import com.jogamp.newt.event.MouseEvent; import com.jogamp.newt.event.MouseListener; import com.jogamp.newt.event.awt.AWTKeyAdapter; import com.jogamp.newt.event.awt.AWTMouseAdapter; +import com.jogamp.opengl.GLRendererQuirks; import com.jogamp.opengl.JoglVersion; import com.jogamp.opengl.util.TileRendererBase; @@ -33,6 +34,8 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList private float view_rotx = 20.0f, view_roty = 30.0f; private final float view_rotz = 0.0f; private int gear1=0, gear2=0, gear3=0; + private Gears sharedGears = null; + private Object syncObjects = null; private float angle = 0.0f; private boolean doRotate = true; private final int swapInterval; @@ -42,6 +45,7 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList private boolean doRotateBeforePrinting; private boolean verbose = true; private boolean flipVerticalInGLOrientation = false; + private volatile boolean isInit = false; // private boolean mouseRButtonDown = false; private int prevMouseX, prevMouseY; @@ -182,6 +186,13 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList System.err.println("gear1 list reused: "+gear1); System.err.println("gear2 list reused: "+gear2); System.err.println("gear3 list reused: "+gear3); + if( gl.getContext().hasRendererQuirk(GLRendererQuirks.NeedSharedObjectSync) ) { + syncObjects = sharedGears; + System.err.println("Shared Gears: Synchronized Objects due to quirk "+GLRendererQuirks.toString(GLRendererQuirks.NeedSharedObjectSync)); + } else { + syncObjects = new Object(); + System.err.println("Shared Gears: Unsynchronized Objects"); + } } else { gear1 = gl.glGenLists(1); gl.glNewList(gear1, GL2.GL_COMPILE); @@ -203,6 +214,8 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList gear(gl, 1.3f, 2.0f, 0.5f, 10, 0.7f); gl.glEndList(); System.err.println("gear3 list created: "+gear3); + + syncObjects = new Object(); } enableStates(gl, false); @@ -295,6 +308,8 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList gear1 = 0; gear2 = 0; gear3 = 0; + sharedGears = null; + syncObjects = null; } @Override @@ -355,25 +370,27 @@ public class Gears implements GLEventListener, TileRendererBase.TileRendererList gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f); // Place the first gear and call its display list - gl.glPushMatrix(); - gl.glTranslatef(-3.0f, -2.0f, 0.0f); - gl.glRotatef(angle, 0.0f, 0.0f, 1.0f); - gl.glCallList(gear1); - gl.glPopMatrix(); - - // Place the second gear and call its display list - gl.glPushMatrix(); - gl.glTranslatef(3.1f, -2.0f, 0.0f); - gl.glRotatef(-2.0f * angle - 9.0f, 0.0f, 0.0f, 1.0f); - gl.glCallList(gear2); - gl.glPopMatrix(); - - // Place the third gear and call its display list - gl.glPushMatrix(); - gl.glTranslatef(-3.1f, 4.2f, 0.0f); - gl.glRotatef(-2.0f * angle - 25.0f, 0.0f, 0.0f, 1.0f); - gl.glCallList(gear3); - gl.glPopMatrix(); + synchronized ( syncObjects ) { + gl.glPushMatrix(); + gl.glTranslatef(-3.0f, -2.0f, 0.0f); + gl.glRotatef(angle, 0.0f, 0.0f, 1.0f); + gl.glCallList(gear1); + gl.glPopMatrix(); + + // Place the second gear and call its display list + gl.glPushMatrix(); + gl.glTranslatef(3.1f, -2.0f, 0.0f); + gl.glRotatef(-2.0f * angle - 9.0f, 0.0f, 0.0f, 1.0f); + gl.glCallList(gear2); + gl.glPopMatrix(); + + // Place the third gear and call its display list + gl.glPushMatrix(); + gl.glTranslatef(-3.1f, 4.2f, 0.0f); + gl.glRotatef(-2.0f * angle - 25.0f, 0.0f, 0.0f, 1.0f); + gl.glCallList(gear3); + gl.glPopMatrix(); + } // Remember that every push needs a pop; this one is paired with // rotating the entire gear assembly |