From f6e6fab2a7ddfb5c9b614cb072c27ff697629161 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Thu, 14 Feb 2013 15:12:18 +0100 Subject: Fix OSX CALayer Bug 690 and Bug 691: Occasional Freeze on CVDisplayLinkStop; Layers and native GL-Context are _not_ Released ; Java Side wait for Main-Thread - Fix Bug 690: Occasional Freeze on CVDisplayLinkStop - NSOpenGLLayer.disableAnimation() shall not claim the renderLock mutex, since the CVDisplayLink callback could be waiting for the lock. This waiting callback could freeze the call to CVDisplayLinkStop. - Fix Bug 691: Layers and native GL-Context are _not_ Released - Following proper release cycle: Context unrealized: - JAWTWindow.detachSurfaceLayer() -> OSXUtil.RemoveCASublayer(..) - CGL.releaseNSOpenGLLayer(..) JAWTWindow.destroy() - MacOSXJAWTWindow.UnsetJAWTRootSurfaceLayer(..) - OSXUtil.DestroyCALayer(..) - 'Magic' CALayer release calls (w/o manual retain beforehand) at: - OSXUtil.RemoveCASublayer(..): [subLayer release] - MacOSXJAWTWindow.UnsetJAWTRootSurfaceLayer(..): [rootLayer release] - OSXUtil.DestroyCALayer(..): [rootLayer release] - 'Magic' NSOpenGLLayer's NSOpenGLContext dealloc: - [NSOpenGLContext clearDrawable] - CGLDestroyContext( [NSOpenGLContext CGLContextObj] ) - Java Side wait for Main-Thread - Waiting for the delegated Main-Thread on the Java side eases debugging and won't block the Main-Thread in native code. - Utilizing this for all CALayer calls Test case: TestGLCanvasAddRemove01SwingAWT --- .../jogamp/opengl/macosx/cgl/MacOSXCGLContext.java | 5 +- .../macosx/MacOSXWindowSystemInterface-calayer.m | 116 +++++++--- .../com/jogamp/nativewindow/awt/JAWTWindow.java | 14 +- .../com/jogamp/nativewindow/swt/SWTAccessor.java | 2 - .../nativewindow/jawt/macosx/MacOSXJAWTWindow.java | 57 ++++- .../jogamp/nativewindow/macosx/OSXUtil.java | 83 +++++-- src/nativewindow/native/macosx/OSXmisc.m | 170 ++++++++------ .../jogl/awt/TestGLCanvasAddRemove01SwingAWT.java | 257 +++++++++++++++++++++ .../junit/jogl/demos/es2/awt/TestGearsES2AWT.java | 11 +- 9 files changed, 581 insertions(+), 134 deletions(-) create mode 100644 src/test/com/jogamp/opengl/test/junit/jogl/awt/TestGLCanvasAddRemove01SwingAWT.java (limited to 'src') diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java index 838a0387d..0deaa2987 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java @@ -709,7 +709,10 @@ public abstract class MacOSXCGLContext extends GLContextImpl // still having a valid OLS attached to surface (parent OLS could have been removed) ols.detachSurfaceLayer(); } - CGL.releaseNSOpenGLLayer(nsOpenGLLayer); + OSXUtil.RunOnMainThread(true, new Runnable() { + public void run() { + CGL.releaseNSOpenGLLayer(nsOpenGLLayer); + } } ); if( null != gl3ShaderProgram ) { gl3ShaderProgram.destroy(MacOSXCGLContext.this.gl.getGL3()); gl3ShaderProgram = null; diff --git a/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m b/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m index b37930587..4334fc676 100644 --- a/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m +++ b/src/jogl/native/macosx/MacOSXWindowSystemInterface-calayer.m @@ -41,6 +41,8 @@ extern GLboolean glIsVertexArray (GLuint array); // // #define DBG_PERF 1 +// #define DBG_LIFECYCLE 1 + /** * Capture setView(NULL), which produces a 'invalid drawable' message * @@ -52,6 +54,10 @@ extern GLboolean glIsVertexArray (GLuint array); - (id)initWithFormat:(NSOpenGLPixelFormat *)format shareContext:(NSOpenGLContext *)share; - (void)setView:(NSView *)view; - (void)update; +#ifdef DBG_LIFECYCLE +- (id)retain; +- (oneway void)release; +#endif - (void)dealloc; @end @@ -82,11 +88,36 @@ extern GLboolean glIsVertexArray (GLuint array); DBG_PRINT("MyNSOpenGLContext.update.X\n"); } +#ifdef DBG_LIFECYCLE + +- (id)retain +{ + DBG_PRINT("MyNSOpenGLContext::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + // NSLog(@"MyNSOpenGLContext::retain: %@",[NSThread callStackSymbols]); + id o = [super retain]; + DBG_PRINT("MyNSOpenGLContext::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]); + return o; +} + +- (oneway void)release +{ + DBG_PRINT("MyNSOpenGLContext::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + [super release]; + // DBG_PRINT("MyNSOpenGLContext::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]); +} + +#endif + - (void)dealloc { - DBG_PRINT("MyNSOpenGLContext.dealloc: this.0 %p\n", self); + CGLContextObj cglCtx = [self CGLContextObj]; + DBG_PRINT("MyNSOpenGLContext::dealloc.0 %p (refcnt %d) - CGL-Ctx %p\n", self, (int)[self retainCount], cglCtx); + [self clearDrawable]; + if( NULL != cglCtx ) { + CGLDestroyContext( cglCtx ); + } [super dealloc]; - DBG_PRINT("MyNSOpenGLContext.dealloc.X: %p\n", self); + // DBG_PRINT("MyNSOpenGLContext.dealloc.X: %p\n", self); } @end @@ -95,6 +126,7 @@ extern GLboolean glIsVertexArray (GLuint array); { @private GLfloat gl_texCoords[8]; + NSOpenGLContext* myCtx; @protected NSOpenGLContext* parentCtx; @@ -150,6 +182,10 @@ extern GLboolean glIsVertexArray (GLuint array); - (void)pauseAnimation:(Bool)pause; - (void)deallocPBuffer; - (void)releaseLayer; +#ifdef DBG_LIFECYCLE +- (id)retain; +- (oneway void)release; +#endif - (void)dealloc; - (void)setSwapInterval:(int)interval; - (void)tick; @@ -209,6 +245,7 @@ static const GLfloat gl_verts[] = { pthread_mutex_init(&renderLock, &renderLockAttr); // recursive pthread_cond_init(&renderSignal, NULL); // no attribute + myCtx = NULL; { int i; for(i=0; i<8; i++) { @@ -418,15 +455,15 @@ static const GLfloat gl_verts[] = { { DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.0: %p (refcnt %d) - pfmt %p, parent %p\n", self, (int)[self retainCount], pixelFormat, parentCtx); - NSOpenGLContext * nctx = [[MyNSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:parentCtx]; - DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.X: new-ctx %p\n", nctx); - return nctx; + // NSLog(@"MyNSOpenGLLayer::openGLContextForPixelFormat: %@",[NSThread callStackSymbols]); + myCtx = [[MyNSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:parentCtx]; + DBG_PRINT("MyNSOpenGLLayer::openGLContextForPixelFormat.X: new-ctx %p\n", myCtx); + return myCtx; } - (void)disableAnimation { - DBG_PRINT("MyNSOpenGLLayer::disableAnimation: %p (refcnt %d) - displayLink %p\n", self, (int)[self retainCount], displayLink); - pthread_mutex_lock(&renderLock); + DBG_PRINT("MyNSOpenGLLayer::disableAnimation.0: %p (refcnt %d) - displayLink %p\n", self, (int)[self retainCount], displayLink); [self setAsynchronous: NO]; if(NULL != displayLink) { #ifdef HAS_CADisplayLink @@ -438,35 +475,63 @@ static const GLfloat gl_verts[] = { #endif displayLink = NULL; } - pthread_mutex_unlock(&renderLock); + DBG_PRINT("MyNSOpenGLLayer::disableAnimation.X: %p (refcnt %d) - displayLink %p\n", self, (int)[self retainCount], displayLink); } - (void)releaseLayer { DBG_PRINT("MyNSOpenGLLayer::releaseLayer.0: %p (refcnt %d)\n", self, (int)[self retainCount]); - pthread_mutex_lock(&renderLock); [self disableAnimation]; + pthread_mutex_lock(&renderLock); [self deallocPBuffer]; - [[self openGLContext] release]; + // [[self openGLContext] release]; + if( NULL != myCtx ) { + [myCtx release]; + // [myCtx dealloc]; + myCtx = NULL; + } + parentCtx = NULL; [parentPixelFmt release]; - [self release]; - DBG_PRINT("MyNSOpenGLLayer::releaseLayer.X: %p (refcnt %d)\n", self, (int)[self retainCount]); pthread_mutex_unlock(&renderLock); + [self release]; + DBG_PRINT("MyNSOpenGLLayer::releaseLayer.X: %p\n", self); } +#ifdef DBG_LIFECYCLE + +- (id)retain +{ + DBG_PRINT("MyNSOpenGLLayer::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + // NSLog(@"MyNSOpenGLLayer::retain: %@",[NSThread callStackSymbols]); + id o = [super retain]; + DBG_PRINT("MyNSOpenGLLayer::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]); + return o; +} + +- (oneway void)release +{ + DBG_PRINT("MyNSOpenGLLayer::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + // NSLog(@"MyNSOpenGLLayer::release: %@",[NSThread callStackSymbols]); + [super release]; + // DBG_PRINT("MyNSOpenGLLayer::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]); +} + +#endif + - (void)dealloc { DBG_PRINT("MyNSOpenGLLayer::dealloc.0 %p (refcnt %d)\n", self, (int)[self retainCount]); // NSLog(@"MyNSOpenGLLayer::dealloc: %@",[NSThread callStackSymbols]); - pthread_mutex_lock(&renderLock); [self disableAnimation]; + pthread_mutex_lock(&renderLock); [self deallocPBuffer]; + // [[self openGLContext] dealloc]; pthread_mutex_unlock(&renderLock); pthread_cond_destroy(&renderSignal); pthread_mutex_destroy(&renderLock); [super dealloc]; - DBG_PRINT("MyNSOpenGLLayer::dealloc.X %p\n", self); + // DBG_PRINT("MyNSOpenGLLayer::dealloc.X %p\n", self); } - (Bool)isGLSourceValid @@ -752,32 +817,27 @@ static const GLfloat gl_verts[] = { @end NSOpenGLLayer* createNSOpenGLLayer(NSOpenGLContext* ctx, int gl3ShaderProgramName, NSOpenGLPixelFormat* fmt, NSOpenGLPixelBuffer* p, uint32_t texID, Bool opaque, int texWidth, int texHeight) { - // This simply crashes after dealloc() has been called .. ie. ref-count -> 0 too early ? - // However using alloc/init, actual dealloc happens at JAWT destruction, hence too later IMHO. - // return [[MyNSOpenGLLayer layer] setupWithContext:ctx pixelFormat: fmt pbuffer: p texIDArg: (GLuint)texID - // opaque: opaque texWidth: texWidth texHeight: texHeight]; - return [[[MyNSOpenGLLayer alloc] init] setupWithContext:ctx gl3ShaderProgramName: (GLuint)gl3ShaderProgramName pixelFormat: fmt pbuffer: p texIDArg: (GLuint)texID opaque: opaque texWidth: texWidth texHeight: texHeight]; } void setNSOpenGLLayerSwapInterval(NSOpenGLLayer* layer, int interval) { - MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; [l setSwapInterval: interval]; [pool release]; } void waitUntilNSOpenGLLayerIsReady(NSOpenGLLayer* layer, long to_micros) { - MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; [l waitUntilRenderSignal: to_micros]; [pool release]; } void setNSOpenGLLayerNeedsDisplayFBO(NSOpenGLLayer* layer, uint32_t texID) { - MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; Bool shallDraw; // volatile OK @@ -799,8 +859,8 @@ void setNSOpenGLLayerNeedsDisplayFBO(NSOpenGLLayer* layer, uint32_t texID) { } void setNSOpenGLLayerNeedsDisplayPBuffer(NSOpenGLLayer* layer, NSOpenGLPixelBuffer* p) { - MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; Bool shallDraw; if( NO == [l isSamePBuffer: p] ) { @@ -825,16 +885,10 @@ void setNSOpenGLLayerNeedsDisplayPBuffer(NSOpenGLLayer* layer, NSOpenGLPixelBuff } void releaseNSOpenGLLayer(NSOpenGLLayer* layer) { - MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + MyNSOpenGLLayer* l = (MyNSOpenGLLayer*) layer; DBG_PRINT("MyNSOpenGLLayer::releaseNSOpenGLLayer.0: %p\n", l); - - if ( [NSThread isMainThread] == YES ) { - [l releaseLayer]; - } else { - [l performSelectorOnMainThread:@selector(releaseLayer) withObject:nil waitUntilDone:NO]; - } - + [l releaseLayer]; DBG_PRINT("MyNSOpenGLLayer::releaseNSOpenGLLayer.X: %p\n", l); [pool release]; } diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java index 8527a0200..007a917a6 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/awt/JAWTWindow.java @@ -205,7 +205,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, } try { if(DEBUG) { - System.err.println("JAWTWindow.attachSurfaceHandle(): 0x"+Long.toHexString(layerHandle) + ", bounds "+bounds); + System.err.println("JAWTWindow.attachSurfaceHandle(): "+toHexString(layerHandle) + ", bounds "+bounds); } attachSurfaceLayerImpl(layerHandle); offscreenSurfaceLayer = layerHandle; @@ -229,7 +229,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, } try { if(DEBUG) { - System.err.println("JAWTWindow.detachSurfaceHandle(): 0x"+Long.toHexString(offscreenSurfaceLayer)); + System.err.println("JAWTWindow.detachSurfaceHandle(): osh "+toHexString(offscreenSurfaceLayer)); } detachSurfaceLayerImpl(offscreenSurfaceLayer); offscreenSurfaceLayer = 0; @@ -341,7 +341,7 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, if(LOCK_SUCCESS == res && drawable_old != drawable) { res = LOCK_SURFACE_CHANGED; if(DEBUG) { - System.err.println("JAWTWindow: surface change 0x"+Long.toHexString(drawable_old)+" -> 0x"+Long.toHexString(drawable)); + System.err.println("JAWTWindow: surface change "+toHexString(drawable_old)+" -> "+toHexString(drawable)); // Thread.dumpStack(); } } @@ -549,8 +549,8 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, StringBuilder sb = new StringBuilder(); sb.append("JAWT-Window["+ - "windowHandle 0x"+Long.toHexString(getWindowHandle())+ - ", surfaceHandle 0x"+Long.toHexString(getSurfaceHandle())+ + "windowHandle "+toHexString(getWindowHandle())+ + ", surfaceHandle "+toHexString(getSurfaceHandle())+ ", bounds "+bounds+", insets "+insets+ ", shallUseOffscreenLayer "+shallUseOffscreenLayer+", isOffscreenLayerSurface "+isOffscreenLayerSurface); if(null!=component) { @@ -566,4 +566,8 @@ public abstract class JAWTWindow implements NativeWindow, OffscreenLayerSurface, return sb.toString(); } + + protected final String toHexString(long l) { + return "0x"+Long.toHexString(l); + } } diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java b/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java index 2b49f6745..5479f2495 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/swt/SWTAccessor.java @@ -38,7 +38,6 @@ import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import javax.media.nativewindow.AbstractGraphicsScreen; -import javax.media.nativewindow.DefaultGraphicsScreen; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeWindowFactory; @@ -49,7 +48,6 @@ import com.jogamp.common.util.VersionNumber; import com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice; import com.jogamp.nativewindow.windows.WindowsGraphicsDevice; import com.jogamp.nativewindow.x11.X11GraphicsDevice; -import com.jogamp.nativewindow.x11.X11GraphicsScreen; import jogamp.nativewindow.macosx.OSXUtil; import jogamp.nativewindow.x11.X11Lib; diff --git a/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java b/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java index 9e270d403..9b06cce1a 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java +++ b/src/nativewindow/classes/jogamp/nativewindow/jawt/macosx/MacOSXJAWTWindow.java @@ -51,6 +51,7 @@ import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.MutableSurface; import javax.media.nativewindow.util.Point; +import com.jogamp.common.util.Function; import com.jogamp.nativewindow.awt.JAWTWindow; import jogamp.nativewindow.jawt.JAWT; @@ -70,11 +71,40 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { } protected void invalidateNative() { + if(DEBUG) { + System.err.println("MacOSXJAWTWindow.invalidateNative(): osh-enabled "+isOffscreenLayerSurfaceEnabled()+ + ", osh-set "+offscreenSurfaceHandleSet+ + ", osh "+toHexString(offscreenSurfaceHandle)+ + ", rsh "+toHexString(rootSurfaceLayerHandle)+ + ", wh "+toHexString(windowHandle)); + } offscreenSurfaceHandle=0; offscreenSurfaceHandleSet=false; - if(isOffscreenLayerSurfaceEnabled()) { + if( isOffscreenLayerSurfaceEnabled() ) { if(0 != rootSurfaceLayerHandle) { + final JAWT jawt = getJAWT(); + if( null != jawt ) { + final JAWT_DrawingSurface ds = jawt.GetDrawingSurface(component); + if (ds != null) { + if ( 0 == ( ds.Lock() & JAWTFactory.JAWT_LOCK_ERROR ) ) { + JAWT_DrawingSurfaceInfo dsi = null; + try { + dsi = ds.GetDrawingSurfaceInfo(); + if(! UnsetJAWTRootSurfaceLayer(dsi.getBuffer(), rootSurfaceLayerHandle)) { + System.err.println("Error clearing JAWT rootSurfaceLayerHandle "+toHexString(rootSurfaceLayerHandle)); + } + } finally { + if ( null != dsi ) { + ds.FreeDrawingSurfaceInfo(dsi); + } + ds.Unlock(); + } + } + jawt.FreeDrawingSurface(ds); + } + } OSXUtil.DestroyCALayer(rootSurfaceLayerHandle); + rootSurfaceLayerHandle = 0; } if(0 != windowHandle) { @@ -107,7 +137,7 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { throw new java.lang.UnsupportedOperationException("Not using CALAYER"); } if(DEBUG) { - System.err.println("MacOSXJAWTWindow.setSurfaceHandle(): 0x"+Long.toHexString(surfaceHandle)); + System.err.println("MacOSXJAWTWindow.setSurfaceHandle(): "+toHexString(surfaceHandle)); } this.offscreenSurfaceHandle = surfaceHandle; this.offscreenSurfaceHandleSet = true; @@ -117,6 +147,7 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { // use offscreen if supported and [ applet or requested ] return JAWTUtil.getJAWT(getShallUseOffscreenLayer() || isApplet()); } + protected int lockSurfaceImpl() throws NativeWindowException { int ret = NativeWindow.LOCK_SURFACE_NOT_READY; ds = getJAWT().GetDrawingSurface(component); @@ -189,7 +220,7 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { } else { drawable = OSXUtil.GetNSView(windowHandle); if(0 == drawable) { - errMsg = "Null NSView of NSWindow 0x"+Long.toHexString(windowHandle); + errMsg = "Null NSView of NSWindow "+toHexString(windowHandle); } } if(null == errMsg) { @@ -204,8 +235,8 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { rootSurfaceLayerHandle = OSXUtil.CreateCALayer(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); if(0 == rootSurfaceLayerHandle) { errMsg = "Could not create root CALayer"; - } else if(!SetJAWTRootSurfaceLayer0(dsi.getBuffer(), rootSurfaceLayerHandle)) { - errMsg = "Could not set JAWT rootSurfaceLayerHandle 0x"+Long.toHexString(rootSurfaceLayerHandle); + } else if(!SetJAWTRootSurfaceLayer(dsi.getBuffer(), rootSurfaceLayerHandle)) { + errMsg = "Could not set JAWT rootSurfaceLayerHandle "+toHexString(rootSurfaceLayerHandle); } } } @@ -265,9 +296,23 @@ public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface { return getLocationOnScreenNonBlocking(storage, component); } protected Point getLocationOnScreenNativeImpl(final int x0, final int y0) { return null; } + + private static boolean SetJAWTRootSurfaceLayer(final Buffer jawtDrawingSurfaceInfoBuffer, final long caLayer) { + return OSXUtil.RunOnMainThread(true, new Function() { + public Boolean eval(Object... args) { + return Boolean.valueOf( SetJAWTRootSurfaceLayer0(jawtDrawingSurfaceInfoBuffer, caLayer) ); + } } ).booleanValue(); + } + + private static boolean UnsetJAWTRootSurfaceLayer(final Buffer jawtDrawingSurfaceInfoBuffer, final long caLayer) { + return OSXUtil.RunOnMainThread(true, new Function() { + public Boolean eval(Object... args) { + return Boolean.valueOf( UnsetJAWTRootSurfaceLayer0(jawtDrawingSurfaceInfoBuffer, caLayer) ); + } } ).booleanValue(); + } private static native boolean SetJAWTRootSurfaceLayer0(Buffer jawtDrawingSurfaceInfoBuffer, long caLayer); - // private static native boolean UnsetJAWTRootSurfaceLayer0(Buffer jawtDrawingSurfaceInfoBuffer, long caLayer); + private static native boolean UnsetJAWTRootSurfaceLayer0(Buffer jawtDrawingSurfaceInfoBuffer, long caLayer); // Variables for lockSurface/unlockSurface private JAWT_DrawingSurface ds; diff --git a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java index f06f97064..2e133c22f 100644 --- a/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java +++ b/src/nativewindow/classes/jogamp/nativewindow/macosx/OSXUtil.java @@ -32,6 +32,8 @@ import javax.media.nativewindow.NativeWindowFactory; import javax.media.nativewindow.util.Insets; import javax.media.nativewindow.util.Point; +import com.jogamp.common.util.Function; +import com.jogamp.common.util.FunctionTask; import com.jogamp.common.util.RunnableTask; import jogamp.nativewindow.Debug; @@ -137,23 +139,33 @@ public class OSXUtil implements ToolkitProperties { public static long CreateCALayer(int x, int y, int width, int height) { return CreateCALayer0(x, y, width, height); } - public static void AddCASublayer(long rootCALayer, long subCALayer) { + public static void AddCASublayer(final long rootCALayer, final long subCALayer) { if(0==rootCALayer || 0==subCALayer) { throw new IllegalArgumentException("rootCALayer 0x"+Long.toHexString(rootCALayer)+", subCALayer 0x"+Long.toHexString(subCALayer)); } - AddCASublayer0(rootCALayer, subCALayer); + RunOnMainThread(true, new Runnable() { + public void run() { + AddCASublayer0(rootCALayer, subCALayer); + } + }); } - public static void RemoveCASublayer(long rootCALayer, long subCALayer) { + public static void RemoveCASublayer(final long rootCALayer, final long subCALayer) { if(0==rootCALayer || 0==subCALayer) { throw new IllegalArgumentException("rootCALayer 0x"+Long.toHexString(rootCALayer)+", subCALayer 0x"+Long.toHexString(subCALayer)); } - RemoveCASublayer0(rootCALayer, subCALayer); + RunOnMainThread(true, new Runnable() { + public void run() { + RemoveCASublayer0(rootCALayer, subCALayer); + } } ); } - public static void DestroyCALayer(long caLayer) { + public static void DestroyCALayer(final long caLayer) { if(0==caLayer) { throw new IllegalArgumentException("caLayer 0x"+Long.toHexString(caLayer)); } - DestroyCALayer0(caLayer); + RunOnMainThread(true, new Runnable() { + public void run() { + DestroyCALayer0(caLayer); + } } ); } /** @@ -169,14 +181,14 @@ public class OSXUtil implements ToolkitProperties { if( IsMainThread0() ) { runnable.run(); // don't leave the JVM } else { - if( waitUntilDone ) { - // Utilize Java side lock/wait and simply pass the Runnable async to OSX main thread, - // otherwise we may freeze the OSX main thread. - Throwable throwable = null; - final Object sync = new Object(); - final RunnableTask rt = new RunnableTask( runnable, sync, true ); - synchronized(sync) { - RunOnMainThread0(rt); + // Utilize Java side lock/wait and simply pass the Runnable async to OSX main thread, + // otherwise we may freeze the OSX main thread. + Throwable throwable = null; + final Object sync = new Object(); + final RunnableTask rt = new RunnableTask( runnable, waitUntilDone ? sync : null, true ); + synchronized(sync) { + RunOnMainThread0(rt); + if( waitUntilDone ) { try { sync.wait(); } catch (InterruptedException ie) { @@ -188,10 +200,47 @@ public class OSXUtil implements ToolkitProperties { if(null!=throwable) { throw new RuntimeException(throwable); } - } - } else { - RunOnMainThread0(runnable); + } + } + } + } + + /** + * Run on OSX UI main thread. + *

+ * 'waitUntilDone' is implemented on Java site via lock/wait on {@link FunctionTask} to not freeze OSX main thread. + *

+ * + * @param waitUntilDone + * @param func + */ + public static R RunOnMainThread(boolean waitUntilDone, Function func, A... args) { + if( IsMainThread0() ) { + return func.eval(args); // don't leave the JVM + } else { + // Utilize Java side lock/wait and simply pass the Runnable async to OSX main thread, + // otherwise we may freeze the OSX main thread. + Throwable throwable = null; + final Object sync = new Object(); + final FunctionTask rt = new FunctionTask( func, waitUntilDone ? sync : null, true ); + synchronized(sync) { + rt.setArgs(args); + RunOnMainThread0(rt); + if( waitUntilDone ) { + try { + sync.wait(); + } catch (InterruptedException ie) { + throwable = ie; + } + if(null==throwable) { + throwable = rt.getThrowable(); + } + if(null!=throwable) { + throw new RuntimeException(throwable); + } + } } + return rt.getResult(); } } diff --git a/src/nativewindow/native/macosx/OSXmisc.m b/src/nativewindow/native/macosx/OSXmisc.m index 72bb2338c..3fa320042 100644 --- a/src/nativewindow/native/macosx/OSXmisc.m +++ b/src/nativewindow/native/macosx/OSXmisc.m @@ -49,6 +49,16 @@ #define DBG_PRINT(...) #endif +// #define VERBOSE2 1 +// +#ifdef VERBOSE2 + #define DBG_PRINT2(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) +#else + #define DBG_PRINT2(...) +#endif + +// #define DBG_LIFECYCLE 1 + static const char * const ClazzNameRunnable = "java/lang/Runnable"; static jmethodID runnableRunID = NULL; @@ -324,7 +334,7 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_CreateCALayer0 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; CALayer* layer = [[CALayer alloc] init]; - DBG_PRINT("CALayer::CreateCALayer.0: %p %d/%d %dx%d (refcnt %d)\n", layer, (int)x, (int)y, (int)width, (int)height, (int)[layer retainCount]); + DBG_PRINT("CALayer::CreateCALayer.0: root %p %d/%d %dx%d (refcnt %d)\n", layer, (int)x, (int)y, (int)width, (int)height, (int)[layer retainCount]); // avoid zero size if(0 == width) { width = 32; } if(0 == height) { height = 32; } @@ -341,8 +351,8 @@ JNIEXPORT jlong JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_CreateCALayer0 [layer removeAllAnimations]; [layer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)]; [layer setNeedsDisplayOnBoundsChange: YES]; - DBG_PRINT("CALayer::CreateCALayer.1: %p %lf/%lf %lfx%lf\n", layer, lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height); - DBG_PRINT("CALayer::CreateCALayer.X: %p (refcnt %d)\n", layer, (int)[layer retainCount]); + DBG_PRINT("CALayer::CreateCALayer.1: root %p %lf/%lf %lfx%lf\n", layer, lRect.origin.x, lRect.origin.y, lRect.size.width, lRect.size.height); + DBG_PRINT("CALayer::CreateCALayer.X: root %p (refcnt %d)\n", layer, (int)[layer retainCount]); [pool release]; @@ -367,30 +377,28 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_AddCASublayer0 if(lRectRoot.origin.x!=0 || lRectRoot.origin.y!=0) { lRectRoot.origin.x = 0; lRectRoot.origin.y = 0; - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - [rootLayer setFrame: lRectRoot]; - }]; + [rootLayer setFrame: lRectRoot]; DBG_PRINT("CALayer::AddCASublayer0.1: Origin %p frame*: %lf/%lf %lfx%lf\n", rootLayer, lRectRoot.origin.x, lRectRoot.origin.y, lRectRoot.size.width, lRectRoot.size.height); } - DBG_PRINT("CALayer::AddCASublayer0.2: %p . %p %lf/%lf %lfx%lf (refcnt %d)\n", - rootLayer, subLayer, lRectRoot.origin.x, lRectRoot.origin.y, lRectRoot.size.width, lRectRoot.size.height, (int)[subLayer retainCount]); - - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - // simple 1:1 layout ! - [subLayer setFrame:lRectRoot]; - [rootLayer addSublayer:subLayer]; - - // no animations for add/remove/swap sublayers etc - // doesn't work: [layer removeAnimationForKey: kCAOnOrderIn, kCAOnOrderOut, kCATransition] - [rootLayer removeAllAnimations]; - [rootLayer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)]; - [rootLayer setNeedsDisplayOnBoundsChange: YES]; - [subLayer removeAllAnimations]; - [subLayer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)]; - [subLayer setNeedsDisplayOnBoundsChange: YES]; - }]; - DBG_PRINT("CALayer::AddCASublayer0.X: %p . %p (refcnt %d)\n", rootLayer, subLayer, (int)[subLayer retainCount]); + DBG_PRINT("CALayer::AddCASublayer0.2: root %p (refcnt %d) .sub %p %lf/%lf %lfx%lf (refcnt %d)\n", + rootLayer, (int)[rootLayer retainCount], + subLayer, lRectRoot.origin.x, lRectRoot.origin.y, lRectRoot.size.width, lRectRoot.size.height, (int)[subLayer retainCount]); + + // simple 1:1 layout ! + [subLayer setFrame:lRectRoot]; + [rootLayer addSublayer:subLayer]; + + // no animations for add/remove/swap sublayers etc + // doesn't work: [layer removeAnimationForKey: kCAOnOrderIn, kCAOnOrderOut, kCATransition] + [rootLayer removeAllAnimations]; + [rootLayer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)]; + [rootLayer setNeedsDisplayOnBoundsChange: YES]; + [subLayer removeAllAnimations]; + [subLayer setAutoresizingMask: (kCALayerWidthSizable|kCALayerHeightSizable)]; + [subLayer setNeedsDisplayOnBoundsChange: YES]; + DBG_PRINT("CALayer::AddCASublayer0.X: root %p (refcnt %d) .sub %p (refcnt %d)\n", + rootLayer, (int)[rootLayer retainCount], subLayer, (int)[subLayer retainCount]); JNF_COCOA_EXIT(env); } @@ -408,11 +416,12 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_RemoveCASublayer0 (void)rootLayer; // no warnings - DBG_PRINT("CALayer::RemoveCASublayer0.0: %p . %p (refcnt %d)\n", rootLayer, subLayer, (int)[subLayer retainCount]); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - [subLayer removeFromSuperlayer]; - }]; - DBG_PRINT("CALayer::RemoveCASublayer0.X: %p . %p\n", rootLayer, subLayer); + DBG_PRINT("CALayer::RemoveCASublayer0.0: root %p (refcnt %d) .sub %p (refcnt %d)\n", + rootLayer, (int)[rootLayer retainCount], subLayer, (int)[subLayer retainCount]); + [subLayer removeFromSuperlayer]; + [subLayer release]; + DBG_PRINT("CALayer::RemoveCASublayer0.X: root %p (refcnt %d) .sub %p (refcnt %d)\n", + rootLayer, (int)[rootLayer retainCount], subLayer, (int)[subLayer retainCount]); JNF_COCOA_EXIT(env); } @@ -427,11 +436,10 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_DestroyCALayer0 JNF_COCOA_ENTER(env); CALayer* layer = (CALayer*) ((intptr_t) caLayer); - DBG_PRINT("CALayer::DestroyCALayer0.0: %p (refcnt %d)\n", layer, (int)[layer retainCount]); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - [layer release]; // performs release! - }]; - DBG_PRINT("CALayer::DestroyCALayer0.X: %p\n", layer); + DBG_PRINT("CALayer::DestroyCALayer0.0: root %p (refcnt %d)\n", layer, (int)[layer retainCount]); + [layer release]; // Var.A + // [layer dealloc]; // Var.B -> SIGSEGV + DBG_PRINT("CALayer::DestroyCALayer0.X: root %p\n", layer); JNF_COCOA_EXIT(env); } @@ -450,12 +458,10 @@ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow return JNI_FALSE; } CALayer* layer = (CALayer*) (intptr_t) caLayer; - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - id surfaceLayers = (id )dsi->platformInfo; - DBG_PRINT("CALayer::SetJAWTRootSurfaceLayer.0: %p -> %p (refcnt %d)\n", surfaceLayers.layer, layer, (int)[layer retainCount]); - surfaceLayers.layer = layer; // already incr. retain count - DBG_PRINT("CALayer::SetJAWTRootSurfaceLayer.X: %p (refcnt %d)\n", layer, (int)[layer retainCount]); - }]; + id surfaceLayers = (id )dsi->platformInfo; + DBG_PRINT("CALayer::SetJAWTRootSurfaceLayer.0: pre %p -> root %p (refcnt %d)\n", surfaceLayers.layer, layer, (int)[layer retainCount]); + surfaceLayers.layer = layer; // already incr. retain count + DBG_PRINT("CALayer::SetJAWTRootSurfaceLayer.X: root %p (refcnt %d)\n", layer, (int)[layer retainCount]); JNF_COCOA_EXIT(env); return JNI_TRUE; } @@ -464,6 +470,7 @@ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow * Class: Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow * Method: UnsetJAWTRootSurfaceLayer0 * Signature: (JJ)Z + */ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow_UnsetJAWTRootSurfaceLayer0 (JNIEnv *env, jclass unused, jobject jawtDrawingSurfaceInfoBuffer, jlong caLayer) { @@ -474,23 +481,18 @@ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow return JNI_FALSE; } CALayer* layer = (CALayer*) (intptr_t) caLayer; - { - id surfaceLayers = (id )dsi->platformInfo; - if(layer != surfaceLayers.layer) { - NativewindowCommon_throwNewRuntimeException(env, "Attached layer %p doesn't match given layer %p\n", surfaceLayers.layer, layer); - return JNI_FALSE; - } + id surfaceLayers = (id )dsi->platformInfo; + if(layer != surfaceLayers.layer) { + NativewindowCommon_throwNewRuntimeException(env, "Attached layer %p doesn't match given layer %p\n", surfaceLayers.layer, layer); + return JNI_FALSE; } - // [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - id surfaceLayers = (id )dsi->platformInfo; - DBG_PRINT("CALayer::detachJAWTSurfaceLayer: (%p) %p -> NULL\n", layer, surfaceLayers.layer); - surfaceLayers.layer = NULL; - [layer release]; - // }]; + DBG_PRINT("CALayer::UnsetJAWTRootSurfaceLayer.0: root %p (refcnt %d) -> nil\n", layer, (int)[layer retainCount]); + surfaceLayers.layer = NULL; + [layer release]; // Var.A + DBG_PRINT("CALayer::UnsetJAWTRootSurfaceLayer.X: root %p (refcnt %d) -> nil\n", layer, (int)[layer retainCount]); JNF_COCOA_EXIT(env); return JNI_TRUE; } - */ @interface MainRunnable : NSObject @@ -503,6 +505,13 @@ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow - (id) initWithRunnable: (jobject)runnable jvmHandle: (JavaVM*)jvm jvmVersion: (int)jvmVers; - (void) jRun; +#ifdef DBG_LIFECYCLE +- (id)retain; +- (oneway void)release; +- (void)dealloc; +#endif + + @end @implementation MainRunnable @@ -519,20 +528,47 @@ JNIEXPORT jboolean JNICALL Java_jogamp_nativewindow_jawt_macosx_MacOSXJAWTWindow { int shallBeDetached = 0; JNIEnv* env = NativewindowCommon_GetJNIEnv(jvmHandle, jvmVersion, &shallBeDetached); - DBG_PRINT("MainRunnable.0 env: %d\n", (int)(NULL!=env)); + DBG_PRINT2("MainRunnable.1 env: %d\n", (int)(NULL!=env)); if(NULL!=env) { - DBG_PRINT("MainRunnable.1.0\n"); + DBG_PRINT2("MainRunnable.1.0\n"); (*env)->CallVoidMethod(env, runnableObj, runnableRunID); - DBG_PRINT("MainRunnable.1.1\n"); + DBG_PRINT2("MainRunnable.1.1\n"); + (*env)->DeleteGlobalRef(env, runnableObj); if (shallBeDetached) { - DBG_PRINT("MainRunnable.1.2\n"); + DBG_PRINT2("MainRunnable.1.3\n"); (*jvmHandle)->DetachCurrentThread(jvmHandle); } } - DBG_PRINT("MainRunnable.X\n"); + DBG_PRINT2("MainRunnable.X\n"); +} + +#ifdef DBG_LIFECYCLE + +- (id)retain +{ + DBG_PRINT2("MainRunnable::retain.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + id o = [super retain]; + DBG_PRINT2("MainRunnable::retain.X: %p (refcnt %d)\n", o, (int)[o retainCount]); + return o; +} + +- (oneway void)release +{ + DBG_PRINT2("MainRunnable::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + [super release]; + // DBG_PRINT2("MainRunnable::release.X: %p (refcnt %d)\n", self, (int)[self retainCount]); } +- (void)dealloc +{ + DBG_PRINT2("MainRunnable::dealloc.0 %p (refcnt %d)\n", self, (int)[self retainCount]); + [super dealloc]; + // DBG_PRINT2("MainRunnable.dealloc.X: %p\n", self); +} + +#endif + @end @@ -546,11 +582,11 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_RunOnMainThread0 { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - DBG_PRINT( "RunOnMainThread0: isMainThread %d, NSApp %d, NSApp-isRunning %d\n", + DBG_PRINT2( "RunOnMainThread0: isMainThread %d, NSApp %d, NSApp-isRunning %d\n", (int)([NSThread isMainThread]), (int)(NULL!=NSApp), (int)([NSApp isRunning])); if ( NO == [NSThread isMainThread] ) { - jobject runnableGlob = (*env)->NewGlobalRef(env, runnable); + jobject runnableObj = (*env)->NewGlobalRef(env, runnable); JavaVM *jvmHandle = NULL; int jvmVersion = 0; @@ -561,18 +597,18 @@ JNIEXPORT void JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_RunOnMainThread0 jvmVersion = (*env)->GetVersion(env); } - DBG_PRINT( "RunOnMainThread0.1.0\n"); - MainRunnable * mr = [[MainRunnable alloc] initWithRunnable: runnableGlob jvmHandle: jvmHandle jvmVersion: jvmVersion]; + DBG_PRINT2( "RunOnMainThread0.1.0\n"); + MainRunnable * mr = [[MainRunnable alloc] initWithRunnable: runnableObj jvmHandle: jvmHandle jvmVersion: jvmVersion]; [mr performSelectorOnMainThread:@selector(jRun) withObject:nil waitUntilDone:NO]; + DBG_PRINT2( "RunOnMainThread0.1.1\n"); [mr release]; - DBG_PRINT( "RunOnMainThread0.1.1\n"); + DBG_PRINT2( "RunOnMainThread0.1.2\n"); - (*env)->DeleteGlobalRef(env, runnableGlob); } else { - DBG_PRINT( "RunOnMainThread0.2\n"); + DBG_PRINT2( "RunOnMainThread0.2\n"); (*env)->CallVoidMethod(env, runnable, runnableRunID); } - DBG_PRINT( "RunOnMainThread0.X\n"); + DBG_PRINT2( "RunOnMainThread0.X\n"); [pool release]; } @@ -644,7 +680,7 @@ JNIEXPORT jint JNICALL Java_jogamp_nativewindow_macosx_OSXUtil_GetScreenRefreshR if(0 == res) { res = 60; // default .. (experienced on OSX 10.6.8) } - DBG_PRINT(stderr, "GetScreenRefreshRate.X: %d\n", (int)res); + DBG_PRINT("GetScreenRefreshRate.X: %d\n", (int)res); // [pool release]; JNF_COCOA_EXIT(env); return res; diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/awt/TestGLCanvasAddRemove01SwingAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/awt/TestGLCanvasAddRemove01SwingAWT.java new file mode 100644 index 000000000..76bb6362f --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/awt/TestGLCanvasAddRemove01SwingAWT.java @@ -0,0 +1,257 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.jogl.awt; + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; + +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.jogamp.opengl.test.junit.jogl.demos.gl2.Gears; +import com.jogamp.opengl.test.junit.util.UITestCase; + +public class TestGLCanvasAddRemove01SwingAWT extends UITestCase { + static long durationPerTest = 50; + static int addRemoveCount = 15; + static boolean shallUseOffscreenFBOLayer = false; + static boolean shallUseOffscreenPBufferLayer = false; + static GLProfile glp; + static int width, height; + static boolean waitForKey = false; + + @BeforeClass + public static void initClass() { + if(GLProfile.isAvailable(GLProfile.GL2)) { + glp = GLProfile.get(GLProfile.GL2); + Assert.assertNotNull(glp); + width = 640; + height = 480; + } else { + setTestSupported(false); + } + } + + @AfterClass + public static void releaseClass() { + } + + protected JPanel createParkingSlot(final JFrame[] top, final int width, final int height) + throws InterruptedException, InvocationTargetException + { + final JPanel[] jPanel = new JPanel[] { null }; + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + jPanel[0] = new JPanel(); + jPanel[0].setLayout(new BorderLayout()); + + final JFrame jFrame1 = new JFrame("Parking Slot"); + // jFrame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + jFrame1.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // equivalent to Frame, use windowClosing event! + jFrame1.getContentPane().add(jPanel[0]); + jFrame1.setSize(width, height); + + top[0] = jFrame1; + } } ); + return jPanel[0]; + } + + protected JPanel create(final JFrame[] top, final int width, final int height, final int num) + throws InterruptedException, InvocationTargetException + { + final JPanel[] jPanel = new JPanel[] { null }; + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + jPanel[0] = new JPanel(); + jPanel[0].setLayout(new BorderLayout()); + // jPanel[0].add(glcContainer[0], BorderLayout.CENTER); + + // final Container container2 = new Container(); + // container2.setLayout(new BorderLayout()); + // container2.add(jPanel1, BorderLayout.CENTER); + + final JPanel jPanel2 = new JPanel(); + jPanel2.setLayout(new BorderLayout()); + // jPanel2.add(container2, BorderLayout.CENTER); + jPanel2.add(jPanel[0], BorderLayout.CENTER); + + final JFrame jFrame1 = new JFrame("JFrame #"+num); + // jFrame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + jFrame1.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // equivalent to Frame, use windowClosing event! + jFrame1.getContentPane().add(jPanel2); + jFrame1.setSize(width, height); + + top[0] = jFrame1; + } } ); + return jPanel[0]; + } + + protected void add(final Container cont, final Component comp) + throws InterruptedException, InvocationTargetException + { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + cont.add(comp, BorderLayout.CENTER); + } } ); + } + + protected void remove(final Container cont, final Component comp) + throws InterruptedException, InvocationTargetException + { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + cont.remove(comp); + } } ); + } + protected void dispose(final GLCanvas glc) + throws InterruptedException, InvocationTargetException + { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + glc.destroy(); + } } ); + } + + protected void setVisible(final JFrame jFrame, final boolean visible) throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + if( visible ) { + jFrame.validate(); + jFrame.pack(); + } + jFrame.setVisible(visible); + } } ) ; + } + + protected void dispose(final JFrame jFrame) throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + jFrame.dispose(); + } } ) ; + } + + protected void runTestGL(GLCapabilities caps, int addRemoveOpCount) + throws AWTException, InterruptedException, InvocationTargetException + { + + // final JFrame[] parkingFrame = new JFrame[] { null }; + // final JPanel parkingPanel = createParkingSlot(parkingFrame, 2, 2); + + for(int i=0; i