From 3334a924309a9361a448d69bc707d4cce416b430 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Thu, 28 Jun 2012 21:39:37 +0200 Subject: NEWT GLWindow multithreading fix and annotations (see commit bc7503c77892a9e14b10e8b8e9ce48b148c6fa4c). NEWT GLWindow multithreading fix: - Add required locking of display(), otherwise a drawable/context destruction of another threads could lead to [still] use asynced data. --- .../classes/com/jogamp/newt/opengl/GLWindow.java | 307 ++++++++++++++++----- src/newt/classes/jogamp/newt/WindowImpl.java | 7 + 2 files changed, 242 insertions(+), 72 deletions(-) diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index 1832d4e99..b94bc0f27 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -1,22 +1,22 @@ /* * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 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: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution 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. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -29,27 +29,54 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * */ package com.jogamp.newt.opengl; import java.io.PrintStream; -import com.jogamp.common.GlueGenVersion; -import com.jogamp.common.util.VersionUtil; -import com.jogamp.newt.*; -import com.jogamp.newt.event.*; - -import jogamp.newt.WindowImpl; - -import javax.media.nativewindow.*; +import javax.media.nativewindow.AbstractGraphicsConfiguration; +import javax.media.nativewindow.CapabilitiesChooser; +import javax.media.nativewindow.CapabilitiesImmutable; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.NativeWindow; +import javax.media.nativewindow.NativeWindowException; +import javax.media.nativewindow.NativeWindowFactory; +import javax.media.nativewindow.SurfaceUpdatedListener; import javax.media.nativewindow.util.InsetsImmutable; import javax.media.nativewindow.util.Point; -import javax.media.opengl.*; +import javax.media.opengl.FPSCounter; +import javax.media.opengl.GL; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesImmutable; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLDrawable; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; +import javax.media.opengl.GLRunnable; +import jogamp.newt.WindowImpl; import jogamp.opengl.FPSCounterImpl; import jogamp.opengl.GLDrawableHelper; + +import com.jogamp.common.GlueGenVersion; +import com.jogamp.common.util.VersionUtil; +import com.jogamp.newt.NewtFactory; +import com.jogamp.newt.Screen; +import com.jogamp.newt.Window; +import com.jogamp.newt.event.KeyListener; +import com.jogamp.newt.event.MouseListener; +import com.jogamp.newt.event.NEWTEvent; +import com.jogamp.newt.event.NEWTEventConsumer; +import com.jogamp.newt.event.WindowAdapter; +import com.jogamp.newt.event.WindowEvent; +import com.jogamp.newt.event.WindowListener; +import com.jogamp.newt.event.WindowUpdateEvent; import com.jogamp.opengl.JoglVersion; import com.jogamp.opengl.util.Animator; @@ -64,7 +91,14 @@ import com.jogamp.opengl.util.Animator; * To be able to use OpenGL commands from within such input {@link com.jogamp.newt.event.NEWTEventListener},
* you can inject {@link javax.media.opengl.GLRunnable} objects * via {@link #invoke(boolean, javax.media.opengl.GLRunnable)} to the OpenGL command stream.
+ *

*

+ * NOTE: [MT-0] Methods utilizing [volatile] drawable/context are not synchronized. + In case any of the methods are called outside of a locked state + extra care should be added. Maybe we shall expose locking facilities to the user. + However, since the user shall stick to the GLEventListener model while utilizing + GLAutoDrawable implementations, she is safe due to the implicit locked state. + *

*/ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSCounter { private final WindowImpl window; @@ -75,7 +109,7 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC protected GLWindow(Window window) { resetFPSCounter(); this.window = (WindowImpl) window; - ((WindowImpl)this.window).setHandleDestroyNotify(false); + this.window.setHandleDestroyNotify(false); window.addWindowListener(new WindowAdapter() { @Override public void windowRepaint(WindowUpdateEvent e) { @@ -121,13 +155,13 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC } /** - * Creates a new GLWindow attaching a new Window referencing a + * Creates a new GLWindow attaching a new Window referencing a * new default Screen and default Display with the given GLCapabilities. *

* The lifecycle of this Window's Screen and Display is handled via {@link Screen#addReference()} * and {@link Screen#removeReference()}. *

- * The default Display will be reused if already instantiated. + * The default Display will be reused if already instantiated. */ public static GLWindow create(GLCapabilitiesImmutable caps) { return new GLWindow(NewtFactory.createWindow(caps)); @@ -145,7 +179,7 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC return new GLWindow(NewtFactory.createWindow(screen, caps)); } - /** + /** * Creates a new GLWindow attaching the given window. *

* The lifecycle of this Window's Screen and Display is handled via {@link Screen#addReference()} @@ -156,13 +190,13 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC return new GLWindow(window); } - /** - * Creates a new GLWindow attaching a new child Window + /** + * Creates a new GLWindow attaching a new child Window * of the given parentNativeWindow with the given GLCapabilities. *

* The Display/Screen will be compatible with the parentNativeWindow, * or even identical in case it's a Newt Window. - * An already instantiated compatible Display will be reused. + * An already instantiated compatible Display will be reused. *

*

* The lifecycle of this Window's Screen and Display is handled via {@link Screen#addReference()} @@ -176,10 +210,12 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC //---------------------------------------------------------------------- // WindowClosingProtocol implementation // + @Override public WindowClosingMode getDefaultCloseOperation() { return window.getDefaultCloseOperation(); } + @Override public WindowClosingMode setDefaultCloseOperation(WindowClosingMode op) { return window.setDefaultCloseOperation(op); } @@ -188,10 +224,12 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC // Window Access // + @Override public CapabilitiesChooser setCapabilitiesChooser(CapabilitiesChooser chooser) { return window.setCapabilitiesChooser(chooser); } + @Override public final CapabilitiesImmutable getChosenCapabilities() { if (drawable == null) { return window.getChosenCapabilities(); @@ -200,153 +238,189 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC return drawable.getChosenGLCapabilities(); } + @Override public final CapabilitiesImmutable getRequestedCapabilities() { return window.getRequestedCapabilities(); } + @Override public final Window getDelegatedWindow() { return window.getDelegatedWindow(); } + @Override public final NativeWindow getParent() { return window.getParent(); } + @Override public final Screen getScreen() { return window.getScreen(); } + @Override public final void setTitle(String title) { window.setTitle(title); } + @Override public final String getTitle() { return window.getTitle(); } + @Override public final boolean isPointerVisible() { return window.isPointerVisible(); } - + + @Override public final void setPointerVisible(boolean mouseVisible) { - window.setPointerVisible(mouseVisible); + window.setPointerVisible(mouseVisible); } - + + @Override public final boolean isPointerConfined() { return window.isPointerConfined(); } - + + @Override public final void confinePointer(boolean grab) { window.confinePointer(grab); } - + + @Override public final void setUndecorated(boolean value) { window.setUndecorated(value); } + @Override public final void warpPointer(int x, int y) { window.warpPointer(x, y); } + @Override public final boolean isUndecorated() { return window.isUndecorated(); } + @Override public final void setAlwaysOnTop(boolean value) { window.setAlwaysOnTop(value); } - + + @Override public final boolean isAlwaysOnTop() { return window.isAlwaysOnTop(); } - + + @Override public final void setFocusAction(FocusRunnable focusAction) { window.setFocusAction(focusAction); } - + + @Override public void setKeyboardFocusHandler(KeyListener l) { window.setKeyboardFocusHandler(l); } - + + @Override public final void requestFocus() { window.requestFocus(); } + @Override public final void requestFocus(boolean wait) { - window.requestFocus(wait); + window.requestFocus(wait); } - + + @Override public boolean hasFocus() { return window.hasFocus(); } - public final InsetsImmutable getInsets() { + @Override + public final InsetsImmutable getInsets() { return window.getInsets(); } - + + @Override public final void setPosition(int x, int y) { window.setPosition(x, y); } - public void setTopLevelPosition(int x, int y) { + @Override + public void setTopLevelPosition(int x, int y) { window.setTopLevelPosition(x, y); } + @Override public final boolean setFullscreen(boolean fullscreen) { return window.setFullscreen(fullscreen); } + @Override public final boolean isFullscreen() { return window.isFullscreen(); } + @Override public final boolean isVisible() { return window.isVisible(); } @Override public final String toString() { - return "NEWT-GLWindow[ \n\tHelper: " + helper + ", \n\tDrawable: " + drawable + + return "NEWT-GLWindow[ \n\tHelper: " + helper + ", \n\tDrawable: " + drawable + ", \n\tContext: " + context + ", \n\tWindow: "+window+ /** ", \n\tFactory: "+factory+ */ "]"; } + @Override public final ReparentOperation reparentWindow(NativeWindow newParent) { return window.reparentWindow(newParent); } + @Override public final ReparentOperation reparentWindow(NativeWindow newParent, boolean forceDestroyCreate) { return window.reparentWindow(newParent, forceDestroyCreate); } + @Override public final boolean removeChild(NativeWindow win) { return window.removeChild(win); } + @Override public final boolean addChild(NativeWindow win) { return window.addChild(win); } - + //---------------------------------------------------------------------- // Window.LifecycleHook Implementation // + @Override public final void destroy() { window.destroy(); } + @Override public final void setVisible(boolean visible) { window.setVisible(visible); } + @Override public final void setSize(int width, int height) { window.setSize(width, height); } + @Override public void setTopLevelSize(int width, int height) { - window.setTopLevelSize(width, height); + window.setTopLevelSize(width, height); } - + + @Override public final boolean isNativeValid() { return window.isNativeValid(); } + @Override public Point getLocationOnScreen(Point storage) { return window.getLocationOnScreen(storage); } @@ -354,10 +428,12 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC // Hide methods here .. protected class GLLifecycleHook implements WindowImpl.LifecycleHook { + @Override public synchronized void destroyActionPreLock() { // nop } + @Override public synchronized void destroyActionInLock() { if(Window.DEBUG_IMPLEMENTATION) { String msg = "GLWindow.destroy() "+Thread.currentThread()+", start"; @@ -380,12 +456,13 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC } context = null; drawable = null; - + if(Window.DEBUG_IMPLEMENTATION) { System.err.println("GLWindow.destroy() "+Thread.currentThread()+", fin"); } } + @Override public synchronized void resetCounter() { if(Window.DEBUG_IMPLEMENTATION) { System.err.println("GLWindow.resetCounter() "+Thread.currentThread()); @@ -393,6 +470,7 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC GLWindow.this.resetFPSCounter(); } + @Override public synchronized void setVisibleActionPost(boolean visible, boolean nativeWindowCreated) { long t0; if(Window.DEBUG_IMPLEMENTATION) { @@ -421,15 +499,16 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC } drawable.setRealized(true); context = drawable.createContext(sharedContext); - context.setContextCreationFlags(additionalCtxCreationFlags); + context.setContextCreationFlags(additionalCtxCreationFlags); } if(Window.DEBUG_IMPLEMENTATION) { System.err.println("GLWindow.setVisibleActionPost("+visible+", "+nativeWindowCreated+") "+Thread.currentThread()+", fin: dt "+ (System.nanoTime()-t0)/1e6 +"ms"); } } - + private GLAnimatorControl savedAnimator = null; - + + @Override public synchronized boolean pauseRenderingAction() { boolean animatorPaused = false; savedAnimator = GLWindow.this.getAnimator(); @@ -439,6 +518,7 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC return animatorPaused; } + @Override public synchronized void resumeRenderingAction() { if ( null != savedAnimator && savedAnimator.isPaused() ) { savedAnimator.resume(); @@ -459,8 +539,9 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC // To make reshape events be sent immediately before a display event private boolean sendReshape=false; private boolean sendDestroy=false; - private FPSCounterImpl fpsCounter = new FPSCounterImpl(); + private final FPSCounterImpl fpsCounter = new FPSCounterImpl(); + @Override public GLDrawableFactory getFactory() { return factory; } @@ -477,17 +558,20 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC this.sharedContext = sharedContext; } + @Override public void setContext(GLContext newCtx) { context = newCtx; if(null != context) { context.setContextCreationFlags(additionalCtxCreationFlags); - } + } } + @Override public GLContext getContext() { return context; } + @Override public GL getGL() { if (context == null) { return null; @@ -495,6 +579,7 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC return context.getGL(); } + @Override public GL setGL(GL gl) { if (context != null) { context.setGL(gl); @@ -503,30 +588,35 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC return null; } + @Override public void addGLEventListener(GLEventListener listener) { if(null!=helper) { helper.addGLEventListener(listener); } } + @Override public void addGLEventListener(int index, GLEventListener listener) { if(null!=helper) { helper.addGLEventListener(index, listener); } } + @Override public void removeGLEventListener(GLEventListener listener) { if(null!=helper) { helper.removeGLEventListener(listener); } } + @Override public void setAnimator(GLAnimatorControl animatorControl) { if(null!=helper) { helper.setAnimator(animatorControl); } } + @Override public GLAnimatorControl getAnimator() { if(null!=helper) { return helper.getAnimator(); @@ -534,51 +624,61 @@ public class GLWindow implements GLAutoDrawable, Window, NEWTEventConsumer, FPSC return null; } + @Override public void invoke(boolean wait, GLRunnable glRunnable) { if(null!=helper) { helper.invoke(this, wait, glRunnable); } } + @Override public void display() { if( !isNativeValid() || !isVisible() ) { return; } - + if(sendDestroy || ( window.hasDeviceChanged() && GLAutoDrawable.SCREEN_CHANGE_ACTION_ENABLED ) ) { sendDestroy=false; destroy(); return; } - - if( null == context && 0