summaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2012-09-15 16:54:52 +0200
committerSven Gothel <[email protected]>2012-09-15 16:54:52 +0200
commit4dd44b985fe0541be3a3bcd9045d201ed3ca2cc5 (patch)
treee14c927a1da9f089297aa291dcd69f2f39b4f15e /src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
parent9036376b7806a5fc61590bf49404eb71830de92f (diff)
Seamless Integration of an FBObject based GLFBODrawable as GLOffscreenAutoDrawable.FBO and as an OffscreenLayerSurface's drawable (OSX) - Fix Bugs 569 and 599
Summary: ========= The new FBObject based GLFBODrawable implementation allows the seamless utilization of FBO offscreen rendering in single buffer, double buffer and MSAA mode. The GLFBODrawable uses a parent drawable based on a dummy surface to allow a GLOffscreenAutoDrawable.FBO creation or a mutable surface supporting an existing offscreen layer surface (OSX CALayer). Offscreen GLDrawable's and GLOffscreenAutoDrawable's can be selected via the GLCapabilities. If simply !onscreen is selected in the caps instance w/o enabling FBO, PBuffer or Bitmap, the factory will automatically choose regarding availability: FBO > PBuffer > Bitmap Double buffering is supported in MSAA more (intrinsic) and explicit in non MSAA. It is preferred when delivering resources (texture id's or framebuffer names) to a shared GLContext. This is demonstrated in (emulates our OSX CALayer implementation): TestFBOOffThreadSharedContextMix2DemosES2NEWT, TestFBOOnThreadSharedContext1DemoES2NEWT and with the OSX JAWT OffscreenLayerSurface itself. FBO is the preferred choice. +++ Offscreen drawables can be resized while maintaining a bound GLContext (e.g. w/ GLAutoDrawable). Previously both, drawable and context, needed to be destroyed and recreated at offscreen resize. Common implementation in GLDrawableHelper is used in the implementations (NEWT's GLWindow, AWT GLCanvas, SWT GLCanvas). +++ Tested: ======= Manually run all unit tests on: - Linux x86_64 NVidia/AMD/Mesa3d(ES) - OSX x86_64 NVidia - Windows x86_64 NVidia - Android arm Mali-400/Tegra-2 No regressions. Disclaimer: =========== This feature is committed almost in one patch. Both previous commits were introducing / fixing the capabilities behavior: 90d45928186f2be99999461cfe45f76a783cc961 9036376b7806a5fc61590bf49404eb71830de92f I have to appologize for the huge size and impact (files and platforms) of this commit however, I could not find a better way to inject this feature in one sane piece. NativeWindow Details: ===================== Complete decoupling of platform impl. detail of surfaces implementing ProxySurface. Used to generalize dummy surfaces and EGL surfaces on top of a native platform surface. - ProxySurface.UpstreamSurfaceHook -> UpstreamSurfaceHook - abstract class ProxySurface -> interface ProxySurface + ProxySurfaceImpl - Misc. implementations JOGL Details: ===================== FBOObject: API Change / Simplification & Usability - Removed reference counter to remove complexity, allow user to choose. - Add 'dispose' flag for detachColorbuffer(..), allowing to keep attachment alive - Fix equals operation of Attachment - Check pre-exising GL errors - Interface Colobuffer gets lifecycle methods - Add static factory methods to create Attachments w/o FBObject instance - Reset: - Clip min size to 1 - Keep alive samplingSink, i.e. don't issue resetMSAATexture2DSink(..). It gets called at syncFramebuffer()/use(..) later on before actual usage. This allows the consumer to utilize the GL_FRONT buffer until (e.g.) swap. - misc bugfixes GLOffscreenAutoDrawable: API Change - Reloc and interfacing - class com.jogamp.opengl.OffscreenAutoDrawable -> javax.media.opengl.* interfaces GLOffscreenAutoDrawable extends GLAutoDrawable GLOffscreenAutoDrawable.FBO extends GLOffscreenAutoDrawable, GLFBODrawable - Added general implementation and FBO specialization - Replacing GLPBuffer (deprecated) .. usable for any offscreen GLDrawable via factory GLAutoDrawable: - Add 'GLDrawable getDelegatedDrawable()' - Refine documentation of setContext(..), remove disclaimer and fixme tags GLDrawableFactory: - Refine API doc and it's selection mechanism for offscreen. - Add createOffscreenDrawable(..) - Add createOffscreenAutoDrawable(..) - Add canCreateFBO(..) - Mark createGLPbuffer(..) deprectated Mark GLPBuffer deprecated New: GLFBODrawable extends GLDrawable GLCanvas (AWT and SWT): Add offscreen resize support w/o GLContext recreation GLAutoDrawableBase .. GLWindow: - Add offscreen resize support w/o GLContext recreation - Remove double swapBuffer call - GLBase/GLContext: - Add: - boolean hasBasicFBOSupport() - boolean hasFullFBOSupport() - int getMaxRenderbufferSamples() - boolean isTextureFormatBGRA8888Available() GLContext: Fix version detection and hasGLSL() - Version detection in setGLFunctionAvailability(..) - Query GL_VERSION ASAP and parse it and compare w/ given major/minor - Use parsed version if valid and lower than given _or_ given is invalid. - Use validated version for caching (procaddr, ..), version number, etc. - Fix hasGLSL() Since 'isGL2ES2()' is true if 'isGL2()' and the latter simply alows GL 1.*, we confine the result to a GL >= 2.0 on desktops. FIXME: May consider GL 1.5 w/ extensions. - return isGL2ES2(); + return isGLES2() || + isGL3() || + isGL2() && ctxMajorVersion>1 ; GLDrawableImpl: - Add 'associateContext(GLContext, boolean)' allowing impl. to have a (weak) reference list of bound context. This is was pulled up from the OSX specific drawable impl. - swapBuffersImpl() -> swapBuffersImpl(boolean doubleBuffered) and call it regardless of single buffering. This is required to propagate this event to impl. properly, i.e. FBODrawable requires a swap notification. - Clarify 'contextMadeCurrent(..)' protocol GLDrawableHelper: - Add resize and recreate offscreen drawable util method - Simplify required init/reshape calls for GLEventListener - GLGraphicsConfigurationUtil: - fixWinAttribBitsAndHwAccel: Reflect sharede context hw-accel bits - OSX has no offscreen bitmap, use pbuffer - use proper offscreen auto selection if offscreen and no modes are set EGL Context/Drawable/DrawableFactory: Abstract native platform code out of base classes - Use EGLWrappedSurface w/ UpstreamSurfaceHook to handle upstream (X11, WGL, ..) lifecycle - in case the EGL resource is hooked up on it. Invisible dummy surfaces: All platforms - size is now reduced to 64x64 and decoupled of actual generic mutable size - fix device lifecycle, no more leaks +++ OSX ==== Enable support for GLFBODrawableImpl in offscreen CALayer mode - NSOpenGLImpl: hooks to calayer native code - calayer code: - allows pbuffer and texures (FBO) - decouple size and draw calls avoiding flickering - enable auto resize of calayer tree MacOSXCGLContext: - NSOpenGLImpl: - Fix false pbuffer 'usage', validate the pointer - If !pbuffer, copy other window mode bits of caps - MacOSXCGLGraphicsConfiguration: - Only assume pbuffer if !onscreen - Remove reference of native pixelformat pointer Native code: - use 'respondsToSelector:' query before calling 'new' methods avoiding an error message where unsuported (prev. OSX versions) - if monitor refresh-rate is queried 0, set to default 60hz - add missing NSAutoreleasePool decoration +++ Android / NEWT: =============== Issue setVisible(..) w/o wait, i.e. queue on EDT, @Android surfaceChanged() callback. Otherwise we could deadlock: setVisible(..) -> EDT -> setVisibleImpl(..) -> 'GL-display'. the latter may may cause havoc while Android-EDT is blocked [until it's return].
Diffstat (limited to 'src/jogl/classes/jogamp/opengl/GLDrawableHelper.java')
-rw-r--r--src/jogl/classes/jogamp/opengl/GLDrawableHelper.java185
1 files changed, 155 insertions, 30 deletions
diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
index 090c5fe69..bdf0b6d74 100644
--- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
+++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java
@@ -43,12 +43,19 @@ package jogamp.opengl;
import java.util.ArrayList;
import java.util.HashSet;
+import javax.media.nativewindow.NativeSurface;
+import javax.media.nativewindow.NativeWindowException;
+import javax.media.nativewindow.ProxySurface;
+import javax.media.nativewindow.UpstreamSurfaceHook;
import javax.media.opengl.GLAnimatorControl;
import javax.media.opengl.GLAutoDrawable;
+import javax.media.opengl.GLCapabilities;
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.GLFBODrawable;
import javax.media.opengl.GLRunnable;
import com.jogamp.opengl.util.Animator;
@@ -108,24 +115,27 @@ public class GLDrawableHelper {
/**
* Associate a new context to the drawable and also propagates the context/drawable switch by
* calling {@link GLContext#setGLDrawable(GLDrawable, boolean) newCtx.setGLDrawable(drawable, true);}.
- * <p>
- * If the old context's drawable was an {@link GLAutoDrawable}, it's reference to the given drawable
- * is being cleared by calling
- * {@link GLAutoDrawable#setContext(GLContext) ((GLAutoDrawable)oldCtx.getGLDrawable()).setContext(null)}.
- * </p>
* <p>
* If the old or new context was current on this thread, it is being released before switching the drawable.
* </p>
+ * <p>
+ * Be aware that the old context is still bound to the drawable,
+ * and that one context can only bound to one drawable at one time!
+ * </p>
+ * <p>
+ * No locking is being performed on the drawable, caller is required to take care of it.
+ * </p>
*
* @param drawable the drawable which context is changed
- * @param newCtx the new context
* @param oldCtx the old context
- * @return true if the newt context was current, otherwise false
+ * @param newCtx the new context
+ * @param newCtxCreationFlags additional creation flags if newCtx is not null and not been created yet, see {@link GLContext#setContextCreationFlags(int)}
+ * @return true if the new context was current, otherwise false
*
* @see GLAutoDrawable#setContext(GLContext)
*/
- public final boolean switchContext(GLDrawable drawable, GLContext oldCtx, GLContext newCtx, int additionalCtxCreationFlags) {
- if(null != oldCtx && oldCtx.isCurrent()) {
+ public static final boolean switchContext(GLDrawable drawable, GLContext oldCtx, GLContext newCtx, int newCtxCreationFlags) {
+ if( null != oldCtx && oldCtx.isCurrent() ) {
oldCtx.release();
}
final boolean newCtxCurrent;
@@ -134,17 +144,135 @@ public class GLDrawableHelper {
if(newCtxCurrent) {
newCtx.release();
}
- newCtx.setContextCreationFlags(additionalCtxCreationFlags);
+ newCtx.setContextCreationFlags(newCtxCreationFlags);
newCtx.setGLDrawable(drawable, true); // propagate context/drawable switch
} else {
newCtxCurrent = false;
}
- if(null!=oldCtx && oldCtx.getGLDrawable() instanceof GLAutoDrawable) {
- ((GLAutoDrawable)oldCtx.getGLDrawable()).setContext(null);
- }
return newCtxCurrent;
}
+ /**
+ * If the drawable is not realized, OP is a NOP.
+ * <ul>
+ * <li>release context if current</li>
+ * <li>destroy old drawable</li>
+ * <li>create new drawable</li>
+ * <li>attach new drawable to context</li>
+ * <li>make context current, if it was current</li>
+ * </ul>
+ * <p>
+ * No locking is being performed, caller is required to take care of it.
+ * </p>
+ *
+ * @param drawable
+ * @param context maybe null
+ * @return the new drawable
+ */
+ public static final GLDrawableImpl recreateGLDrawable(GLDrawableImpl drawable, GLContext context) {
+ if( ! drawable.isRealized() ) {
+ return drawable;
+ }
+ final boolean contextCurrent = null != context && context.isCurrent();
+ final GLDrawableFactory factory = drawable.getFactory();
+ final NativeSurface surface = drawable.getNativeSurface();
+ final ProxySurface proxySurface = (surface instanceof ProxySurface) ? (ProxySurface)surface : null;
+
+ if(contextCurrent) {
+ context.release();
+ }
+
+ if(null != proxySurface) {
+ proxySurface.enableUpstreamSurfaceHookLifecycle(false);
+ }
+ try {
+ drawable.setRealized(false);
+ drawable = (GLDrawableImpl) factory.createGLDrawable(surface); // [2]
+ drawable.setRealized(true);
+ } finally {
+ if(null != proxySurface) {
+ proxySurface.enableUpstreamSurfaceHookLifecycle(true);
+ }
+ }
+
+ if(null != context) {
+ context.setGLDrawable(drawable, true); // re-association
+ }
+
+ if(contextCurrent) {
+ context.makeCurrent();
+ }
+ return drawable;
+ }
+
+ /**
+ * Performs resize operation on the given drawable, assuming it is offscreen.
+ * <p>
+ * The {@link GLDrawableImpl}'s {@link NativeSurface} is being locked during operation.
+ * In case the holder is an auto drawable or similar, it's lock shall be claimed by the caller.
+ * </p>
+ * <p>
+ * May recreate the drawable via {@link #recreateGLDrawable(GLDrawableImpl, GLContext)}
+ * in case of a a pbuffer- or pixmap-drawable.
+ * </p>
+ * <p>
+ * FBO drawables are resized w/o drawable destruction.
+ * </p>
+ * <p>
+ * Offscreen resize operation is validated w/ drawable size in the end.
+ * An exception is thrown if not successful.
+ * </p>
+ *
+ * @param drawable
+ * @param context
+ * @param newWidth the new width, it's minimum is capped to 1
+ * @param newHeight the new height, it's minimum is capped to 1
+ * @return the new drawable in case of an pbuffer/pixmap drawable, otherwise the passed drawable is being returned.
+ * @throws NativeWindowException is drawable is not offscreen or it's surface lock couldn't be claimed
+ * @throws GLException may be thrown a resize operation
+ */
+ public static final GLDrawableImpl resizeOffscreenDrawable(GLDrawableImpl drawable, GLContext context, int newWidth, int newHeight)
+ throws NativeWindowException, GLException
+ {
+ if(drawable.getChosenGLCapabilities().isOnscreen()) {
+ throw new NativeWindowException("Drawable is not offscreen: "+drawable);
+ }
+ final NativeSurface ns = drawable.getNativeSurface();
+ final int lockRes = ns.lockSurface();
+ if (NativeSurface.LOCK_SURFACE_NOT_READY >= lockRes) {
+ throw new NativeWindowException("Could not lock surface of drawable: "+drawable);
+ }
+ try {
+ if(0>=newWidth) { newWidth = 1; }
+ if(0>=newHeight) { newHeight = 1; }
+ // propagate new size
+ if(ns instanceof ProxySurface) {
+ final ProxySurface ps = (ProxySurface) ns;
+ final UpstreamSurfaceHook ush = ps.getUpstreamSurfaceHook();
+ if(ush instanceof UpstreamSurfaceHook.MutableSize) {
+ ((UpstreamSurfaceHook.MutableSize)ush).setSize(newWidth, newHeight);
+ } else if(DEBUG) { // we have to assume UpstreamSurfaceHook contains the new size already, hence size check @ bottom
+ System.err.println("GLDrawableHelper.resizeOffscreenDrawable: Drawable's offscreen ProxySurface n.a. UpstreamSurfaceHook.MutableSize, but "+ush.getClass().getName()+": "+ush);
+ }
+ } else if(DEBUG) { // we have to assume surface contains the new size already, hence size check @ bottom
+ System.err.println("GLDrawableHelper.resizeOffscreenDrawable: Drawable's offscreen surface n.a. ProxySurface, but "+ns.getClass().getName()+": "+ns);
+ }
+ if(drawable instanceof GLFBODrawable) {
+ if( null != context && context.isCreated() ) {
+ ((GLFBODrawable) drawable).resetSize(context.getGL());
+ }
+ } else {
+ drawable = GLDrawableHelper.recreateGLDrawable(drawable, context);
+ }
+ } finally {
+ ns.unlockSurface();
+ }
+ if(drawable.getWidth() != newWidth || drawable.getHeight() != newHeight) {
+ throw new InternalError("Incomplete resize operation: expected "+newWidth+"x"+newHeight+", has: "+drawable);
+ }
+ return drawable;
+ }
+
public final void addGLEventListener(GLEventListener listener) {
addGLEventListener(-1, listener);
}
@@ -196,15 +324,11 @@ public class GLDrawableHelper {
}
}
- private final boolean init(GLEventListener l, GLAutoDrawable drawable, boolean sendReshape) {
- if(listenersToBeInit.remove(l)) {
- l.init(drawable);
- if(sendReshape) {
- reshape(l, drawable, 0, 0, drawable.getWidth(), drawable.getHeight(), true /* setViewport */, false /* checkInit */);
- }
- return true;
+ private final void init(GLEventListener l, GLAutoDrawable drawable, boolean sendReshape) {
+ l.init(drawable);
+ if(sendReshape) {
+ reshape(l, drawable, 0, 0, drawable.getWidth(), drawable.getHeight(), true /* setViewport */, false /* checkInit */);
}
- return false;
}
/** The default init action to be called once after ctx is being created @ 1st makeCurrent(). */
@@ -214,14 +338,11 @@ public class GLDrawableHelper {
for (int i=0; i < _listeners.size(); i++) {
final GLEventListener listener = _listeners.get(i) ;
- // If make current ctx, invoked by invokGL(..), results in a new ctx, init gets called.
+ // If make ctx current, invoked by invokGL(..), results in a new ctx, init gets called.
// This may happen not just for initial setup, but for ctx recreation due to resource change (drawable/window),
- // hence the must always be initialized unconditional.
- listenersToBeInit.add(listener);
-
- if ( ! init( listener, drawable, true /* sendReshape */) ) {
- throw new GLException("GLEventListener "+listener+" already initialized: "+drawable);
- }
+ // hence it must be called unconditional, always.
+ listenersToBeInit.remove(listener); // remove if exist, avoiding dbl init
+ init( listener, drawable, true /* sendReshape */);
}
}
}
@@ -239,7 +360,9 @@ public class GLDrawableHelper {
final GLEventListener listener = _listeners.get(i) ;
// GLEventListener may need to be init,
// in case this one is added after the realization of the GLAutoDrawable
- init( listener, drawable, true /* sendReshape */) ;
+ if( listenersToBeInit.remove(listener) ) {
+ init( listener, drawable, true /* sendReshape */) ;
+ }
listener.display(drawable);
}
}
@@ -251,7 +374,9 @@ public class GLDrawableHelper {
// GLEventListener may need to be init,
// in case this one is added after the realization of the GLAutoDrawable
synchronized(listenersLock) {
- init( listener, drawable, false /* sendReshape */) ;
+ if( listenersToBeInit.remove(listener) ) {
+ init( listener, drawable, false /* sendReshape */) ;
+ }
}
}
if(setViewport) {