diff options
Diffstat (limited to 'src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java')
-rw-r--r-- | src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java new file mode 100644 index 000000000..689047235 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -0,0 +1,549 @@ +/** + * Copyright 2011 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.swt; + +import javax.media.nativewindow.AbstractGraphicsDevice; +import javax.media.nativewindow.NativeSurface; +import javax.media.nativewindow.ProxySurface; +import javax.media.opengl.GL; +import javax.media.opengl.GLAnimatorControl; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLCapabilitiesChooser; +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 javax.media.opengl.Threading; + +import jogamp.nativewindow.swt.SWTAccessor; +import jogamp.opengl.GLContextImpl; +import jogamp.opengl.GLDrawableHelper; +import jogamp.opengl.ThreadingImpl; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +import com.jogamp.common.util.locks.LockFactory; +import com.jogamp.common.util.locks.RecursiveLock; + +/** + * Native SWT Canvas implementing GLAutoDrawable + */ +public class GLCanvas extends Canvas implements GLAutoDrawable { + + /* + * Flag for whether the SWT thread should be used for OpenGL calls when in single-threaded mode. This is controlled + * by the setting of the threading mode to worker (do not use SWT thread), awt (use SWT thread), or false (always use + * calling thread). + * + * @see Threading + * + * Now done dynamically to avoid early loading of gluegen library. + */ + //private static final boolean useSWTThread = ThreadingImpl.getMode() != ThreadingImpl.WORKER; + + /* GL Stuff */ + private final GLDrawableHelper drawableHelper = new GLDrawableHelper(); + private GLDrawable drawable; + private GLContext context; + + /* Native window surface */ + private AbstractGraphicsDevice device; + private final long nativeWindowHandle; + private final ProxySurface proxySurface; + + /* Construction parameters stored for GLAutoDrawable accessor methods */ + private int ctxCreationFlags = 0; + + private final GLCapabilitiesImmutable glCapsRequested; + + /* + * Lock for access to GLDrawable, as used in GLCanvas, + */ + private final RecursiveLock lock = LockFactory.createRecursiveLock(); + + /* Flag indicating whether an unprocessed reshape is pending. */ + private volatile boolean sendReshape; + + /* + * Invokes init(...) on all GLEventListeners. Assumes context is current when run. + */ + private final Runnable initAction = new Runnable() { + public void run() { + drawableHelper.init(GLCanvas.this); + } + }; + + /* + * Action to handle display in OpenGL, also processes reshape since they should be done at the same time. + * + * Assumes GLContext is current when run. + */ + private final Runnable displayAction = new Runnable() { + public void run() { + if (sendReshape) { + drawableHelper.reshape(GLCanvas.this, 0, 0, getWidth(), getHeight()); + sendReshape = false; + } + drawableHelper.display(GLCanvas.this); + } + }; + + /* Action to make specified context current prior to running displayAction */ + private final Runnable makeCurrentAndDisplayAction = new Runnable() { + public void run() { + drawableHelper.invokeGL(drawable, context, displayAction, initAction); + } + }; + + /* Swaps buffers, assuming the GLContext is current */ + private final Runnable swapBuffersAction = new Runnable() { + public void run() { + drawable.swapBuffers(); + } + }; + + /* Swaps buffers, making the GLContext current first */ + private final Runnable makeCurrentAndSwapBuffersAction = new Runnable() { + public void run() { + drawableHelper.invokeGL(drawable, context, swapBuffersAction, initAction); + } + }; + + /* + * Disposes of OpenGL resources + */ + private final Runnable disposeGLAction = new Runnable() { + public void run() { + drawableHelper.dispose(GLCanvas.this); + + if (null != context) { + context.makeCurrent(); // implicit wait for lock .. + context.destroy(); + context = null; + } + + if (null != drawable) { + drawable.setRealized(false); + drawable = null; + } + } + }; + + private final Runnable makeCurrentAndDisposeGLAction = new Runnable() { + public void run() { + drawableHelper.invokeGL(drawable, context, disposeGLAction, null); + } + }; + + private final Runnable disposeGraphicsDeviceAction = new Runnable() { + public void run() { + if (null != device) { + device.close(); + device = null; + } + } + }; + + /** + * Creates a new SWT GLCanvas. + * + * @param parent + * Required (non-null) parent Composite. + * @param style + * Optional SWT style bit-field. The {@link SWT#NO_BACKGROUND} bit is set before passing this up to the + * Canvas constructor, so OpenGL handles the background. + * @param caps + * Optional GLCapabilities. If not provided, the default capabilities for the default GLProfile for the + * graphics device determined by the parent Composite are used. Note that the GLCapabilities that are + * actually used may differ based on the capabilities of the graphics device. + * @param chooser + * Optional GLCapabilitiesChooser to customize the selection of the used GLCapabilities based on the + * requested GLCapabilities, and the available capabilities of the graphics device. + * @param shareWith + * Optional GLContext to share state (textures, vbos, shaders, etc.) with. + */ + public GLCanvas(final Composite parent, final int style, final GLCapabilities caps, + final GLCapabilitiesChooser chooser, final GLContext shareWith) { + /* NO_BACKGROUND required to avoid clearing bg in native SWT widget (we do this in the GL display) */ + super(parent, style | SWT.NO_BACKGROUND); + + SWTAccessor.setRealized(this, true); + + /* Get the nativewindow-Graphics Device associated with this control (which is determined by the parent Composite) */ + device = SWTAccessor.getDevice(this); + /* Native handle for the control, used to associate with GLContext */ + nativeWindowHandle = SWTAccessor.getWindowHandle(this); + + /* Select default GLCapabilities if none was provided, otherwise clone provided caps to ensure safety */ + final GLCapabilitiesImmutable fixedCaps = (caps == null) ? new GLCapabilities(GLProfile.getDefault(device)) + : (GLCapabilitiesImmutable) caps.cloneMutable(); + glCapsRequested = fixedCaps; + + final GLDrawableFactory glFactory = GLDrawableFactory.getFactory(fixedCaps.getGLProfile()); + + /* Create a NativeWindow proxy for the SWT canvas */ + proxySurface = glFactory.createProxySurface(device, nativeWindowHandle, fixedCaps, chooser); + + /* Associate a GL surface with the proxy */ + drawable = glFactory.createGLDrawable(proxySurface); + drawable.setRealized(true); + + context = drawable.createContext(shareWith); + + /* Register SWT listeners (e.g. PaintListener) to render/resize GL surface. */ + /* TODO: verify that these do not need to be manually de-registered when destroying the SWT component */ + addPaintListener(new PaintListener() { + public void paintControl(final PaintEvent arg0) { + if (!drawableHelper.isExternalAnimatorAnimating()) { + display(); + } + } + }); + + addControlListener(new ControlAdapter() { + @Override + public void controlResized(final ControlEvent arg0) { + /* Mark for OpenGL reshape next time the control is painted */ + sendReshape = true; + } + }); + } + + public void addGLEventListener(final GLEventListener arg0) { + drawableHelper.addGLEventListener(arg0); + } + + public void addGLEventListener(final int arg0, final GLEventListener arg1) throws IndexOutOfBoundsException { + drawableHelper.addGLEventListener(arg0, arg1); + } + + /** + * {@inheritDoc} + * <p> + * Also disposes of the SWT component. + */ + public void destroy() { + drawable.setRealized(false); + dispose(); + } + + public void display() { + runInGLThread(makeCurrentAndDisplayAction, displayAction); + } + + public GLAnimatorControl getAnimator() { + return drawableHelper.getAnimator(); + } + + public boolean getAutoSwapBufferMode() { + return drawableHelper.getAutoSwapBufferMode(); + } + + public GLContext getContext() { + return context; + } + + public int getContextCreationFlags() { + return ctxCreationFlags; + } + + public GL getGL() { + final GLContext ctx = getContext(); + return (ctx == null) ? null : ctx.getGL(); + } + + public void invoke(final boolean wait, final GLRunnable run) { + /* Queue task for running during the next display(). */ + drawableHelper.invoke(this, wait, run); + } + + public void removeGLEventListener(final GLEventListener arg0) { + drawableHelper.removeGLEventListener(arg0); + } + + public void setAnimator(final GLAnimatorControl arg0) throws GLException { + drawableHelper.setAnimator(arg0); + } + + public void setAutoSwapBufferMode(final boolean arg0) { + drawableHelper.setAutoSwapBufferMode(arg0); + } + + public void setContext(final GLContext ctx) { + this.context = ctx; + if (ctx instanceof GLContextImpl) { + ((GLContextImpl) ctx).setContextCreationFlags(ctxCreationFlags); + } + } + + public void setContextCreationFlags(final int arg0) { + ctxCreationFlags = arg0; + } + + public GL setGL(final GL arg0) { + final GLContext ctx = getContext(); + if (ctx != null) { + ctx.setGL(arg0); + return arg0; + } + return null; + } + + public GLContext createContext(final GLContext arg0) { + lock.lock(); + try { + final GLDrawable drawable = this.drawable; + return (drawable != null) ? drawable.createContext(arg0) : null; + } finally { + lock.unlock(); + } + } + + public GLCapabilitiesImmutable getChosenGLCapabilities() { + return (GLCapabilitiesImmutable)proxySurface.getGraphicsConfiguration().getChosenCapabilities(); + } + + /** + * Accessor for the GLCapabilities that were requested (via the constructor parameter). + * + * @return Non-null GLCapabilities. + */ + public GLCapabilitiesImmutable getRequestedGLCapabilities() { + return (GLCapabilitiesImmutable)proxySurface.getGraphicsConfiguration().getRequestedCapabilities(); + } + + public GLDrawableFactory getFactory() { + lock.lock(); + try { + final GLDrawable drawable = this.drawable; + return (drawable != null) ? drawable.getFactory() : null; + } finally { + lock.unlock(); + } + } + + public GLProfile getGLProfile() { + return glCapsRequested.getGLProfile(); + } + + public long getHandle() { + lock.lock(); + try { + final GLDrawable drawable = this.drawable; + return (drawable != null) ? drawable.getHandle() : 0; + } finally { + lock.unlock(); + } + } + + public int getHeight() { + return getClientArea().height; + } + + public NativeSurface getNativeSurface() { + lock.lock(); + try { + final GLDrawable drawable = this.drawable; + return (drawable != null) ? drawable.getNativeSurface() : null; + } finally { + lock.unlock(); + } + } + + public int getWidth() { + return getClientArea().width; + } + + public boolean isRealized() { + lock.lock(); + try { + final GLDrawable drawable = this.drawable; + return (drawable != null) ? drawable.isRealized() : false; + } finally { + lock.unlock(); + } + } + + public void setRealized(final boolean arg0) { + /* Intentionally empty */ + } + + public void swapBuffers() throws GLException { + runInGLThread(makeCurrentAndSwapBuffersAction, swapBuffersAction); + } + + // FIXME: API of update() method ? + public void update() { + // FIXME: display(); + } + + public void dispose() { + lock.lock(); + try { + final Display display = getDisplay(); + + if (null != context) { + boolean animatorPaused = false; + final GLAnimatorControl animator = getAnimator(); + if (null != animator) { + // can't remove us from animator for recreational addNotify() + animatorPaused = animator.pause(); + } + if (Threading.isSingleThreaded() && !Threading.isOpenGLThread()) { + runInDesignatedGLThread(makeCurrentAndDisposeGLAction); + } else if (context.isCreated()) { + drawableHelper.invokeGL(drawable, context, disposeGLAction, null); + } + + if (animatorPaused) { + animator.resume(); + } + } + if (display.getThread() == Thread.currentThread()) + disposeGraphicsDeviceAction.run(); + else + display.syncExec(disposeGraphicsDeviceAction); + } finally { + lock.unlock(); + } + super.dispose(); + } + + /** + * Determines whether the current thread is the appropriate thread to use the GLContext in. If we are using one of + * the single-threaded policies in {@link Threading}, than this is either the SWT event dispatch thread, or the + * OpenGL worker thread depending on the state of {@link #useSWTThread}. Otherwise this always returns true because + * the threading model is user defined. + * <p> + * TODO: should this be moved to {@link Threading}? + * + * @return true if the calling thread is the correct thread to execute OpenGL calls in, false otherwise. + */ + protected boolean isRenderThread() { + if (Threading.isSingleThreaded()) { + if (ThreadingImpl.getMode() != ThreadingImpl.WORKER) { + final Display display = getDisplay(); + return display != null && display.getThread() == Thread.currentThread(); + } + return Threading.isOpenGLThread(); + } + /* + * For multi-threaded rendering, the render thread is not defined... + */ + return true; + } + + /** + * Runs the specified action in the designated OpenGL thread. If the current thread is designated, then the + * syncAction is run synchronously, otherwise the asyncAction is dispatched to the appropriate worker thread. + * + * @param asyncAction + * The non-null action to dispatch to an OpenGL worker thread. This action should not assume that a + * GLContext is current when invoked. + * @param syncAction + * The non-null action to run synchronously if the current thread is designated to handle OpenGL calls. + * This action may assume the GLContext is current. + */ + private void runInGLThread(final Runnable asyncAction, final Runnable syncAction) { + if (Threading.isSingleThreaded() && !isRenderThread()) { + /* Run in designated GL thread */ + runInDesignatedGLThread(asyncAction); + } else { + /* Run in current thread... */ + drawableHelper.invokeGL(drawable, context, syncAction, initAction); + } + } + + /** + * Dispatches the specified runnable to the appropriate OpenGL worker thread (either the SWT event dispatch thread, + * or the OpenGL worker thread depending on the state of {@link #useSWTThread}). + * + * @param makeCurrentAndRunAction + * The non-null action to dispatch. + */ + private void runInDesignatedGLThread(final Runnable makeCurrentAndRunAction) { + if (ThreadingImpl.getMode() != ThreadingImpl.WORKER) { + final Display display = getDisplay(); + assert display.getThread() != Thread.currentThread() : "Incorrect use of thread dispatching."; + display.syncExec(makeCurrentAndRunAction); + } else { + Threading.invokeOnOpenGLThread(makeCurrentAndRunAction); + } + } + + + public static void main(final String[] args) { + final Display display = new Display(); + final Shell shell = new Shell(display); + shell.setSize(800,600); + shell.setLayout(new FillLayout()); + + final GLCanvas canvas = new GLCanvas(shell, + 0, null, null, null); + + canvas.addGLEventListener(new GLEventListener() { + + public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { + System.out.println("Reshape"); + } + + public void init(final GLAutoDrawable drawable) { + System.out.println("Init"); + } + + public void dispose(final GLAutoDrawable drawable) { + System.out.println("Dispose"); + } + + public void display(final GLAutoDrawable drawable) { + System.out.println("Display"); + } + }); + shell.setSize(500, 500); + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + display.dispose(); + } +} |