/** * Copyright 2014 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 jogamp.opengl.egl; import com.jogamp.nativewindow.AbstractGraphicsConfiguration; import com.jogamp.nativewindow.AbstractGraphicsDevice; import com.jogamp.nativewindow.AbstractGraphicsScreen; import com.jogamp.nativewindow.DefaultGraphicsScreen; import com.jogamp.nativewindow.NativeSurface; import com.jogamp.nativewindow.ProxySurface; import com.jogamp.nativewindow.UpstreamSurfaceHook; import com.jogamp.nativewindow.VisualIDHolder.VIDType; import com.jogamp.opengl.GLCapabilitiesImmutable; import com.jogamp.opengl.GLException; import jogamp.nativewindow.WrappedSurface; import com.jogamp.nativewindow.egl.EGLGraphicsDevice; import com.jogamp.opengl.egl.EGL; /** * <pre> * EGLSurface [ is_a -> WrappedSurface -> ProxySurfaceImpl -> ProxySurface -> MutableSurface -> NativeSurface] has_a * EGLUpstreamSurfaceHook [ is_a -> UpstreamSurfaceHook.MutableSize -> UpstreamSurfaceHook ] has_a * NativeSurface (e.g. native [X11, WGL, ..] surface, or WrappedSurface, ..) * </pre> */ public class EGLUpstreamSurfaceHook implements UpstreamSurfaceHook.MutableSize { private static final boolean DEBUG = EGLDrawableFactory.DEBUG; private final NativeSurface upstreamSurface; private final UpstreamSurfaceHook.MutableSize upstreamSurfaceHookMutableSize; public EGLUpstreamSurfaceHook(final NativeSurface upstream) { upstreamSurface = upstream; if(upstreamSurface instanceof ProxySurface) { final UpstreamSurfaceHook ush = ((ProxySurface)upstreamSurface).getUpstreamSurfaceHook(); if(ush instanceof UpstreamSurfaceHook.MutableSize) { // offscreen NativeSurface w/ MutableSize (default) upstreamSurfaceHookMutableSize = (UpstreamSurfaceHook.MutableSize) ush; } else { upstreamSurfaceHookMutableSize = null; } } else { upstreamSurfaceHookMutableSize = null; } } public EGLUpstreamSurfaceHook(final EGLGraphicsConfiguration cfg, final long handle, final UpstreamSurfaceHook upstream, final boolean ownsDevice) { this( new WrappedSurface(cfg, handle, upstream, ownsDevice) ); } static String getThreadName() { return Thread.currentThread().getName(); } /** * {@inheritDoc} * <p> * Returns the actual upstream {@link NativeSurface}, e.g. native X11 surface. * </p> */ @Override public final NativeSurface getUpstreamSurface() { return upstreamSurface; } @Override public final void setSurfaceSize(final int width, final int height) { if(null != upstreamSurfaceHookMutableSize) { upstreamSurfaceHookMutableSize.setSurfaceSize(width, height); } } @Override public final void create(final ProxySurface surface) { final String dbgPrefix; if(DEBUG) { dbgPrefix = getThreadName() + ": EGLUpstreamSurfaceHook.create( up "+upstreamSurface.getClass().getSimpleName()+" -> this "+surface.getClass().getSimpleName()+" ): "; System.err.println(dbgPrefix+this); } else { dbgPrefix = null; } if(upstreamSurface instanceof ProxySurface) { // propagate createNotify(..) so upstreamSurface will be created ((ProxySurface)upstreamSurface).createNotify(); } // lock upstreamSurface, so it can be used in case EGLDisplay is derived from it! if(NativeSurface.LOCK_SURFACE_NOT_READY >= upstreamSurface.lockSurface()) { throw new GLException("Could not lock: "+upstreamSurface); } try { evalUpstreamSurface(dbgPrefix, surface); } finally { upstreamSurface.unlockSurface(); } } private final void evalUpstreamSurface(final String dbgPrefix, final ProxySurface surface) { // // evaluate nature of upstreamSurface, may create EGL instances if required // boolean isEGLSurfaceValid = true; // assume yes final EGLGraphicsDevice eglDevice; final AbstractGraphicsConfiguration aConfig; { final AbstractGraphicsConfiguration surfaceConfig = surface.getGraphicsConfiguration(); final AbstractGraphicsDevice surfaceDevice = null != surfaceConfig ? surfaceConfig.getScreen().getDevice() : null; if(DEBUG) { System.err.println(dbgPrefix+"SurfaceDevice: "+surfaceDevice.getClass().getSimpleName()+", hash 0x"+Integer.toHexString(surfaceDevice.hashCode())+", "+surfaceDevice); System.err.println(dbgPrefix+"SurfaceConfig: "+surfaceConfig.getClass().getSimpleName()+", hash 0x"+Integer.toHexString(surfaceConfig.hashCode())+", "+surfaceConfig); } final AbstractGraphicsConfiguration upstreamConfig = upstreamSurface.getGraphicsConfiguration(); final AbstractGraphicsDevice upstreamDevice = upstreamConfig.getScreen().getDevice(); if(DEBUG) { System.err.println(dbgPrefix+"UpstreamDevice: "+upstreamDevice.getClass().getSimpleName()+", hash 0x"+Integer.toHexString(upstreamDevice.hashCode())+", "+upstreamDevice); System.err.println(dbgPrefix+"UpstreamConfig: "+upstreamConfig.getClass().getSimpleName()+", hash 0x"+Integer.toHexString(upstreamConfig.hashCode())+", "+upstreamConfig); } if( surfaceDevice instanceof EGLGraphicsDevice ) { eglDevice = (EGLGraphicsDevice) surfaceDevice; aConfig = surfaceConfig; if(DEBUG) { System.err.println(dbgPrefix+"Reusing this eglDevice: "+eglDevice+", using this config "+aConfig.getClass().getSimpleName()+" "+aConfig); } if(EGL.EGL_NO_DISPLAY == eglDevice.getHandle()) { eglDevice.open(); isEGLSurfaceValid = false; surface.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); } } else if( upstreamDevice instanceof EGLGraphicsDevice ) { eglDevice = (EGLGraphicsDevice) upstreamDevice; aConfig = upstreamConfig; if(DEBUG) { System.err.println(dbgPrefix+"Reusing upstream eglDevice: "+eglDevice+", using upstream config "+aConfig.getClass().getSimpleName()+" "+aConfig); } if(EGL.EGL_NO_DISPLAY == eglDevice.getHandle()) { eglDevice.open(); isEGLSurfaceValid = false; surface.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); } } else { eglDevice = EGLDisplayUtil.eglCreateEGLGraphicsDevice(upstreamSurface); eglDevice.open(); aConfig = upstreamConfig; isEGLSurfaceValid = false; surface.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_DEVICE ); } } final GLCapabilitiesImmutable capsRequested = (GLCapabilitiesImmutable) aConfig.getRequestedCapabilities(); final EGLGraphicsConfiguration eglConfig; if( aConfig instanceof EGLGraphicsConfiguration ) { // Config is already in EGL type - reuse .. final EGLGLCapabilities capsChosen = (EGLGLCapabilities) aConfig.getChosenCapabilities(); if( !isEGLSurfaceValid || !EGLGraphicsConfiguration.isEGLConfigValid(eglDevice.getHandle(), capsChosen.getEGLConfig()) ) { // 'refresh' the native EGLConfig handle capsChosen.setEGLConfig(EGLGraphicsConfiguration.EGLConfigId2EGLConfig(eglDevice.getHandle(), capsChosen.getEGLConfigID())); if( 0 == capsChosen.getEGLConfig() ) { throw new GLException("Refreshing native EGLConfig handle failed with error "+EGLContext.toHexString(EGL.eglGetError())+": "+eglDevice+", "+capsChosen+" of "+aConfig); } final AbstractGraphicsScreen eglScreen = new DefaultGraphicsScreen(eglDevice, aConfig.getScreen().getIndex()); eglConfig = new EGLGraphicsConfiguration(eglScreen, capsChosen, capsRequested, null); if(DEBUG) { System.err.println(dbgPrefix+"Refreshing eglConfig: "+eglConfig); } isEGLSurfaceValid = false; } else { eglConfig = (EGLGraphicsConfiguration) aConfig; if(DEBUG) { System.err.println(dbgPrefix+"Reusing eglConfig: "+eglConfig); } } } else { final AbstractGraphicsScreen eglScreen = new DefaultGraphicsScreen(eglDevice, aConfig.getScreen().getIndex()); eglConfig = EGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic( capsRequested, capsRequested, null, eglScreen, aConfig.getVisualID(VIDType.NATIVE), false /* forceTransparencyFlag */); if (null == eglConfig) { throw new GLException("Couldn't create EGLGraphicsConfiguration from "+eglScreen); } else if(DEBUG) { System.err.println(dbgPrefix+"Chosen eglConfig: "+eglConfig); } isEGLSurfaceValid = false; } surface.setGraphicsConfiguration(eglConfig); if( isEGLSurfaceValid ) { isEGLSurfaceValid = EGLSurface.isValidEGLSurfaceHandle(eglDevice.getHandle(), upstreamSurface.getSurfaceHandle()); } if( isEGLSurfaceValid ) { surface.setSurfaceHandle(upstreamSurface.getSurfaceHandle()); surface.clearUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); if(DEBUG) { System.err.println(dbgPrefix+"Fin: Already valid EGL surface - use as-is: "+upstreamSurface); } } else { surface.setSurfaceHandle(EGL.EGL_NO_SURFACE); surface.addUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); // create/destroy in EGLDrawable if(DEBUG) { System.err.println(dbgPrefix+"Fin: EGL surface n/a - TBD: "+upstreamSurface); } } } @Override public final void destroy(final ProxySurface surface) { if(DEBUG) { System.err.println(getThreadName() + ": EGLUpstreamSurfaceHook.destroy("+surface.getClass().getSimpleName()+"): "+this); } surface.clearUpstreamOptionBits( ProxySurface.OPT_PROXY_OWNS_UPSTREAM_SURFACE ); if(upstreamSurface instanceof ProxySurface) { ((ProxySurface)upstreamSurface).destroyNotify(); } } @Override public final int getSurfaceWidth(final ProxySurface s) { return upstreamSurface.getSurfaceWidth(); } @Override public final int getSurfaceHeight(final ProxySurface s) { return upstreamSurface.getSurfaceHeight(); } @Override public String toString() { final String us_s; final int sw, sh; if( null != upstreamSurface ) { us_s = upstreamSurface.getClass().getName() + ": 0x" + Long.toHexString(upstreamSurface.getSurfaceHandle()); sw = upstreamSurface.getSurfaceWidth(); sh = upstreamSurface.getSurfaceHeight(); } else { us_s = "nil"; sw = -1; sh = -1; } return "EGLUpstreamSurfaceHook[ "+ sw + "x" + sh + ", " + us_s+ "]"; } }