From 4dd44b985fe0541be3a3bcd9045d201ed3ca2cc5 Mon Sep 17 00:00:00 2001 From: Sven Gothel <sgothel@jausoft.com> Date: Sat, 15 Sep 2012 16:54:52 +0200 Subject: 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]. --- .../classes/jogamp/opengl/egl/EGLDrawable.java | 135 +++++++++------------ 1 file changed, 56 insertions(+), 79 deletions(-) (limited to 'src/jogl/classes/jogamp/opengl/egl/EGLDrawable.java') diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLDrawable.java b/src/jogl/classes/jogamp/opengl/egl/EGLDrawable.java index 0dba4bb09..167eebf3a 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLDrawable.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLDrawable.java @@ -36,7 +36,8 @@ package jogamp.opengl.egl; -import javax.media.nativewindow.MutableSurface; +import java.nio.IntBuffer; + import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.ProxySurface; @@ -46,10 +47,10 @@ import javax.media.opengl.GLException; import jogamp.opengl.GLDrawableImpl; import jogamp.opengl.GLDynamicLookupHelper; +import com.jogamp.common.nio.Buffers; import com.jogamp.nativewindow.egl.EGLGraphicsDevice; public abstract class EGLDrawable extends GLDrawableImpl { - private boolean ownEGLSurface = false; // for destruction protected EGLDrawable(EGLDrawableFactory factory, NativeSurface component) throws GLException { super(factory, component, false); @@ -58,21 +59,14 @@ public abstract class EGLDrawable extends GLDrawableImpl { @Override public abstract GLContext createContext(GLContext shareWith); - protected abstract long createSurface(EGLGraphicsConfiguration config, long nativeSurfaceHandle); + protected abstract long createSurface(EGLGraphicsConfiguration config, int width, int height, long nativeSurfaceHandle); - private final void recreateSurface() { - final EGLGraphicsConfiguration eglConfig = (EGLGraphicsConfiguration) surface.getGraphicsConfiguration(); - final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) eglConfig.getScreen().getDevice(); - if(DEBUG) { - System.err.println(getThreadName() + ": createSurface using "+eglConfig); - } - if( EGL.EGL_NO_SURFACE != surface.getSurfaceHandle() ) { - EGL.eglDestroySurface(eglDevice.getHandle(), surface.getSurfaceHandle()); - } + private final long createEGLSurface() { + final EGLWrappedSurface eglws = (EGLWrappedSurface) surface; + final EGLGraphicsConfiguration eglConfig = (EGLGraphicsConfiguration) eglws.getGraphicsConfiguration(); + final NativeSurface upstreamSurface = eglws.getUpstreamSurface(); - final EGLUpstreamSurfaceHook upstreamHook = (EGLUpstreamSurfaceHook) ((ProxySurface)surface).getUpstreamSurfaceHook(); - final NativeSurface upstreamSurface = upstreamHook.getUpstreamSurface(); - long eglSurface = createSurface(eglConfig, upstreamSurface.getSurfaceHandle()); + long eglSurface = createSurface(eglConfig, eglws.getWidth(), eglws.getHeight(), upstreamSurface.getSurfaceHandle()); int eglError0; if (EGL.EGL_NO_SURFACE == eglSurface) { @@ -86,7 +80,7 @@ public abstract class EGLDrawable extends GLDrawableImpl { if(DEBUG) { System.err.println(getThreadName() + ": Info: Creation of window surface w/ surface handle failed: "+eglConfig+", error "+toHexString(eglError0)+", retry w/ windowHandle"); } - eglSurface = createSurface(eglConfig, nw.getWindowHandle()); + eglSurface = createSurface(eglConfig, eglws.getWidth(), eglws.getHeight(), nw.getWindowHandle()); if (EGL.EGL_NO_SURFACE == eglSurface) { eglError0 = EGL.eglGetError(); } @@ -99,34 +93,53 @@ public abstract class EGLDrawable extends GLDrawableImpl { if (EGL.EGL_NO_SURFACE == eglSurface) { throw new GLException("Creation of window surface failed: "+eglConfig+", "+surface+", error "+toHexString(eglError0)); } - if(DEBUG) { - System.err.println(getThreadName() + ": setSurface using component: handle "+toHexString(surface.getSurfaceHandle())+" -> "+toHexString(eglSurface)); + System.err.println(getThreadName() + ": createEGLSurface handle "+toHexString(eglSurface)); } - - ((MutableSurface)surface).setSurfaceHandle(eglSurface); + return eglSurface; } @Override protected final void updateHandle() { - if(ownEGLSurface) { - recreateSurface(); + final EGLWrappedSurface eglws = (EGLWrappedSurface) surface; + if(DEBUG) { + System.err.println(getThreadName() + ": updateHandle of "+eglws); + } + if( eglws.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ) ) { + if( EGL.EGL_NO_SURFACE != eglws.getSurfaceHandle() ) { + throw new InternalError("Set surface but claimed to be invalid: "+eglws); + } + eglws.setSurfaceHandle( createEGLSurface() ); + } else if( EGL.EGL_NO_SURFACE == eglws.getSurfaceHandle() ) { + throw new InternalError("Nil surface but claimed to be valid: "+eglws); + } + } + + protected void destroyHandle() { + final EGLWrappedSurface eglws = (EGLWrappedSurface) surface; + if(DEBUG) { + System.err.println(getThreadName() + ": destroyHandle of "+eglws); + } + if( EGL.EGL_NO_SURFACE == eglws.getSurfaceHandle() ) { + throw new InternalError("Nil surface but claimed to be valid: "+eglws); + } + final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) eglws.getGraphicsConfiguration().getScreen().getDevice(); + if( eglws.containsUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ) ) { + EGL.eglDestroySurface(eglDevice.getHandle(), eglws.getSurfaceHandle()); + eglws.setSurfaceHandle(EGL.EGL_NO_SURFACE); } } - protected static boolean isValidEGLSurface(EGLGraphicsDevice eglDevice, NativeSurface surface) { - final long eglDisplayHandle = eglDevice.getHandle(); - if (EGL.EGL_NO_DISPLAY == eglDisplayHandle) { - throw new GLException("Invalid EGL display in EGLGraphicsDevice "+eglDevice); + protected static boolean isValidEGLSurface(long eglDisplayHandle, long surfaceHandle) { + if( 0 == surfaceHandle ) { + return false; } - boolean eglSurfaceValid = 0 != surface.getSurfaceHandle(); - if(eglSurfaceValid) { - int[] tmp = new int[1]; - eglSurfaceValid = EGL.eglQuerySurface(eglDisplayHandle, surface.getSurfaceHandle(), EGL.EGL_CONFIG_ID, tmp, 0); - if(!eglSurfaceValid) { - if(DEBUG) { - System.err.println(getThreadName() + ": EGLDrawable.isValidEGLSurface eglQuerySuface failed: "+toHexString(EGL.eglGetError())+", "+surface); - } + final IntBuffer val = Buffers.newDirectIntBuffer(1); + final boolean eglSurfaceValid = EGL.eglQuerySurface(eglDisplayHandle, surfaceHandle, EGL.EGL_CONFIG_ID, val); + if( !eglSurfaceValid ) { + final int eglErr = EGL.eglGetError(); + if(DEBUG) { + System.err.println(getThreadName() + ": EGLDrawable.isValidEGLSurface eglQuerySuface failed: error "+toHexString(eglErr)+", "+toHexString(surfaceHandle)); } } return eglSurfaceValid; @@ -134,55 +147,19 @@ public abstract class EGLDrawable extends GLDrawableImpl { @Override protected final void setRealizedImpl() { - final EGLGraphicsConfiguration eglConfig = (EGLGraphicsConfiguration) surface.getGraphicsConfiguration(); - final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) eglConfig.getScreen().getDevice(); - if (realized) { - final boolean eglSurfaceValid = isValidEGLSurface(eglDevice, surface); - if(eglSurfaceValid) { - // surface holds valid EGLSurface - if(DEBUG) { - System.err.println(getThreadName() + ": EGLDrawable.setRealizedImpl re-using component's EGLSurface: handle "+toHexString(surface.getSurfaceHandle())); - } - ownEGLSurface=false; - } else { - // EGLSurface is ours - subsequent updateHandle() will issue recreateSurface(); - // However .. let's validate the surface object first - if( ! (surface instanceof ProxySurface) ) { - throw new InternalError("surface not ProxySurface: "+surface.getClass().getName()+", "+surface); - } - final ProxySurface.UpstreamSurfaceHook upstreamHook = ((ProxySurface)surface).getUpstreamSurfaceHook(); - if( null == upstreamHook ) { - throw new InternalError("null upstreamHook of: "+surface); - } - if( ! (upstreamHook instanceof EGLUpstreamSurfaceHook) ) { - throw new InternalError("upstreamHook not EGLUpstreamSurfaceHook: Surface: "+surface.getClass().getName()+", "+surface+"; UpstreamHook: "+upstreamHook.getClass().getName()+", "+upstreamHook); - } - if( null == ((EGLUpstreamSurfaceHook)upstreamHook).getUpstreamSurface() ) { - throw new InternalError("null upstream surface"); - } - ownEGLSurface=true; - if(DEBUG) { - System.err.println(getThreadName() + ": EGLDrawable.setRealizedImpl owning EGLSurface"); - } - } - } else if (ownEGLSurface && surface.getSurfaceHandle() != EGL.EGL_NO_SURFACE) { - if(DEBUG) { - System.err.println(getThreadName() + ": EGLDrawable.setRealized(false): ownSurface "+ownEGLSurface+", "+eglDevice+", eglSurface: "+toHexString(surface.getSurfaceHandle())); - } - // Destroy the window surface - if (!EGL.eglDestroySurface(eglDevice.getHandle(), surface.getSurfaceHandle())) { - throw new GLException("Error destroying window surface (eglDestroySurface)"); - } - ((MutableSurface)surface).setSurfaceHandle(EGL.EGL_NO_SURFACE); + if(DEBUG) { + System.err.println(getThreadName() + ": EGLDrawable.setRealized("+realized+"): NOP - "+surface); } } @Override - protected final void swapBuffersImpl() { - final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) surface.getGraphicsConfiguration().getScreen().getDevice(); - // single-buffer is already filtered out @ GLDrawableImpl#swapBuffers() - if(!EGL.eglSwapBuffers(eglDevice.getHandle(), surface.getSurfaceHandle())) { - throw new GLException("Error swapping buffers, eglError "+toHexString(EGL.eglGetError())+", "+this); + protected final void swapBuffersImpl(boolean doubleBuffered) { + if(doubleBuffered) { + final EGLGraphicsDevice eglDevice = (EGLGraphicsDevice) surface.getGraphicsConfiguration().getScreen().getDevice(); + // single-buffer is already filtered out @ GLDrawableImpl#swapBuffers() + if(!EGL.eglSwapBuffers(eglDevice.getHandle(), surface.getSurfaceHandle())) { + throw new GLException("Error swapping buffers, eglError "+toHexString(EGL.eglGetError())+", "+this); + } } } -- cgit v1.2.3