diff options
author | Sven Gothel <[email protected]> | 2019-06-23 08:03:04 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2019-06-23 08:03:04 +0200 |
commit | bba73bc096250a3c7fc036d84b1ea054d1b70b06 (patch) | |
tree | ed02575eac2a46bd49627444dcce972946ae8d2e /src/jogl/classes/jogamp/opengl/ios/eagl/IOSEAGLContext.java | |
parent | 154e91978498d8b6db9ce34a1f06b298bcf4c361 (diff) |
iOS: Initial working commit supporting iOS (ipad pro 11)
using our OpenJFK 9 x86_64 and arm64 build.
Test demo class is 'com.jogamp.opengl.demos.ios.Hello',
residing in the new demo folder 'src/demos/com/jogamp/opengl/demos/ios/Hello.java'.
This commit does not yet include a working NEWT
specialization for iOS, but it shall followup soon.
Instead this commit demonstrates JOGL operating on
native UIWindow, UIView and CAEAGLLayer as provided by
Nativewindow's IOSUtil.
Test Video https://www.youtube.com/watch?v=Z4lUQNFTGMI
+++
Notable bug: The FBO used and sharing the COLORBUFFER RENDERBUFFER
memory resources with CAEAGLLayer to be displayed in the UIView
seemingly cannot handle GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24
or GL_DEPTH_COMPONENT32 depth buffer - none at all (Device + Simulation).
Therefor the default demo GLEventListener chosen here
don't require a depth buffer ;-)
This issue can hopefully be mitigated with other means
than using a flat FBO sink similar to FBO multisampling.
Diffstat (limited to 'src/jogl/classes/jogamp/opengl/ios/eagl/IOSEAGLContext.java')
-rw-r--r-- | src/jogl/classes/jogamp/opengl/ios/eagl/IOSEAGLContext.java | 477 |
1 files changed, 477 insertions, 0 deletions
diff --git a/src/jogl/classes/jogamp/opengl/ios/eagl/IOSEAGLContext.java b/src/jogl/classes/jogamp/opengl/ios/eagl/IOSEAGLContext.java new file mode 100644 index 000000000..57c20d465 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/ios/eagl/IOSEAGLContext.java @@ -0,0 +1,477 @@ +/** + * Copyright 2019 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.ios.eagl; + +import java.util.Map; + +import com.jogamp.nativewindow.AbstractGraphicsConfiguration; +import com.jogamp.nativewindow.AbstractGraphicsDevice; +import com.jogamp.nativewindow.MutableGraphicsConfiguration; +import com.jogamp.nativewindow.OffscreenLayerSurface; +import com.jogamp.opengl.FBObject; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GLCapabilitiesImmutable; +import com.jogamp.opengl.GLContext; +import com.jogamp.opengl.GLException; +import com.jogamp.opengl.GLFBODrawable; +import com.jogamp.opengl.GLProfile; + +import jogamp.nativewindow.ios.IOSUtil; +import jogamp.opengl.GLContextImpl; +import jogamp.opengl.GLDrawableImpl; +import jogamp.opengl.GLDynamicLookupHelper; +import jogamp.opengl.GLFBODrawableImpl; +import jogamp.opengl.GLFBODrawableImpl.SwapBufferContext; +import jogamp.opengl.DummyGLExtProcAddressTable; +import jogamp.opengl.ios.eagl.IOSEAGLDrawable.GLBackendType; + +import com.jogamp.common.os.Platform; +import com.jogamp.gluegen.runtime.ProcAddressTable; +import com.jogamp.gluegen.runtime.opengl.GLProcAddressResolver; +import com.jogamp.opengl.GLRendererQuirks; + +public class IOSEAGLContext extends GLContextImpl +{ + // Abstract interface for implementation of this context + protected interface GLBackendImpl { + /** Indicating CALayer, i.e. onscreen rendering using offscreen layer. */ + boolean isUsingCAEAGLLayer(); + long create(long share, int ctp, int major, int minor); + boolean destroy(long ctx); + void associateDrawable(boolean bound); + boolean makeCurrent(long ctx); + boolean release(long ctx); + } + + static boolean isGLProfileSupported(final int ctp, final int major, final int minor) { + if( 0 == ( CTX_PROFILE_ES & ctp ) ) { + // only ES profiles supported + return false; + } + return true; + } + static int GLProfile2EAGLProfileValue(final int ctp, final int major, final int minor) { + if(!isGLProfileSupported(ctp, major, minor)) { + throw new GLException("OpenGL profile not supported.0: "+getGLVersion(major, minor, ctp, "@GLProfile2EAGLProfileValue")); + } + switch( major ) { + case 1: + return EAGL.kEAGLRenderingAPIOpenGLES1; + case 2: + return EAGL.kEAGLRenderingAPIOpenGLES2; + case 3: + return EAGL.kEAGLRenderingAPIOpenGLES3; + } + throw new GLException("OpenGL profile not supported.1: "+getGLVersion(major, minor, ctp, "@GLProfile2EAGLProfileValue")); + } + + private boolean haveSetOpenGLMode = false; + private GLBackendType openGLMode = GLBackendType.CAEAGL_LAYER; + + // Implementation object (either NSOpenGL-based or CGL-based) + protected GLBackendImpl impl; + + // CGL extension functions. + private DummyGLExtProcAddressTable cglExtProcAddressTable; + + private int lastWidth, lastHeight; + + protected IOSEAGLContext(final GLDrawableImpl drawable, + final GLContext shareWith) { + super(drawable, shareWith); + initOpenGLImpl(getOpenGLMode()); + } + + @Override + protected void resetStates(final boolean isInit) { + // no inner state _cglExt = null; + super.resetStates(isInit); + } + + @Override + public Object getPlatformGLExtensions() { + return null; + } + + @Override + public final ProcAddressTable getPlatformExtProcAddressTable() { + return getCGLExtProcAddressTable(); + } + + public final DummyGLExtProcAddressTable getCGLExtProcAddressTable() { + return cglExtProcAddressTable; + } + + @Override + protected Map<String, String> getFunctionNameMap() { return null; } + + @Override + protected Map<String, String> getExtensionNameMap() { return null; } + + @Override + protected long createContextARBImpl(final long share, final boolean direct, final int ctp, final int major, final int minor) { + if(!isGLProfileSupported(ctp, major, minor)) { + if(DEBUG) { + System.err.println(getThreadName() + ": createContextARBImpl: Not supported "+getGLVersion(major, minor, ctp, "@creation on iOS "+Platform.getOSVersionNumber())); + } + return 0; + } + + // Will throw exception upon error + long ctx = impl.create(share, ctp, major, minor); + if(0 != ctx) { + if (!impl.makeCurrent(ctx)) { + if(DEBUG) { + System.err.println(getThreadName() + ": createContextARB couldn't make current "+getGLVersion(major, minor, ctp, "@creation")); + } + impl.release(ctx); + impl.destroy(ctx); + ctx = 0; + } else if(DEBUG) { + System.err.println(getThreadName() + ": createContextARBImpl: OK "+getGLVersion(major, minor, ctp, "@creation")+", share "+share+", direct "+direct+" on iOS "+Platform.getOSVersionNumber()); + } + } else if(DEBUG) { + System.err.println(getThreadName() + ": createContextARBImpl: NO "+getGLVersion(major, minor, ctp, "@creation on iOS "+Platform.getOSVersionNumber())); + } + return ctx; + } + + @Override + protected void destroyContextARBImpl(final long _context) { + impl.release(_context); + impl.destroy(_context); + } + + @Override + public final boolean isGLReadDrawableAvailable() { + return false; + } + + @Override + protected boolean createImpl(final long shareWithHandle) throws GLException { + final MutableGraphicsConfiguration config = (MutableGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration(); + final AbstractGraphicsDevice device = config.getScreen().getDevice(); + final GLCapabilitiesImmutable glCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); + final GLProfile glp = glCaps.getGLProfile(); + final boolean createContextARBAvailable = isCreateContextARBAvail(device); + if(DEBUG) { + System.err.println(getThreadName() + ": IOSEAGLContext.createImpl: START "+glCaps+", share "+toHexString(shareWithHandle)); + System.err.println(getThreadName() + ": Use ARB[avail["+getCreateContextARBAvailStr(device)+ + "] -> "+createContextARBAvailable+"]]"); + } + if( !glp.isGLES() ) { + throw new GLException("Desktop OpenGL profile not supported on iOS "+Platform.getOSVersionNumber()+": "+glp); + } + contextHandle = createContextARB(shareWithHandle, true); + return 0 != contextHandle; + } + + @Override + protected void makeCurrentImpl() throws GLException { + /** FIXME: won't work w/ special drawables (like FBO) - check for CGL mode regressions! + * + if (getOpenGLMode() != ((IOSEAGLDrawable)drawable).getOpenGLMode()) { + setOpenGLMode(((IOSEAGLDrawable)drawable).getOpenGLMode()); + } */ + if ( !impl.makeCurrent(contextHandle) ) { + throw new GLException("Error making Context current: "+this); + } + drawableUpdatedNotify(); + } + + @Override + protected void releaseImpl() throws GLException { + if (!impl.release(contextHandle)) { + throw new GLException("Error releasing OpenGL Context: "+this); + } + } + + @Override + protected void destroyImpl() throws GLException { + if(!impl.destroy(contextHandle)) { + throw new GLException("Error destroying OpenGL Context: "+this); + } + } + + @Override + protected void drawableUpdatedNotify() throws GLException { + if( drawable.getChosenGLCapabilities().isOnscreen() ) { + final int w = drawable.getSurfaceWidth(); + final int h = drawable.getSurfaceHeight(); + // final boolean sizeChanged = w != lastWidth || h != lastHeight; + if(drawable instanceof GLFBODrawable) { + final GLFBODrawable fbod = (GLFBODrawable) drawable; + final FBObject.Colorbuffer col = fbod.getColorbuffer(GL.GL_FRONT); // FIXME GL_BACK swap .. + final int renderbuffer = col.getName(); + EAGL.eaglPresentRenderbuffer(contextHandle, renderbuffer); + } + // TODO: Check for resize ... + lastWidth = w; + lastHeight = h; + } + } + + @Override + protected void associateDrawable(final boolean bound) { + // context stuff depends on drawable stuff + if(bound) { + final GLDrawableImpl drawable = getDrawableImpl(); + if( drawable instanceof GLFBODrawableImpl ) { + final GLFBODrawableImpl fboDrawable = (GLFBODrawableImpl) drawable; + fboDrawable.setSwapBufferContext(new SwapBufferContext() { + @Override + public void swapBuffers(final boolean doubleBuffered) { + EAGL.eaglPresentRenderbuffer(contextHandle, GL.GL_RENDERBUFFER); + } } ); + } + // FIXME: Need better way to inject the IOS EAGL Layer into FBObject + // FIXME: May want to implement optional injection of a BufferStorage SPI? + // FBObject.ColorAttachment.initialize(GL): EAGL.eaglBindDrawableStorageToRenderbuffer(contextHandle, GL.GL_RENDERBUFFER, eaglLayer); + final long eaglLayer = IOSUtil.GetCAEAGLLayer(drawable.getNativeSurface().getSurfaceHandle()); + System.err.println("EAGL: Ctx attach EAGLLayer 0x"+Long.toHexString(eaglLayer)); + attachObject("IOS_EAGL_LAYER", new Long(eaglLayer)); + + super.associateDrawable(true); // 1) init drawable stuff (FBO init, ..) + impl.associateDrawable(true); // 2) init context stuff + } else { + impl.associateDrawable(false); // 1) free context stuff + super.associateDrawable(false); // 2) free drawable stuff + + EAGL.eaglBindDrawableStorageToRenderbuffer(contextHandle, GL.GL_RENDERBUFFER, 0); + detachObject("IOS_EAGL_LAYER"); + } + } + + @Override + protected void copyImpl(final GLContext source, final int mask) throws GLException { + throw new GLException("copyImpl n/a: "+this); + } + + /** + * {@inheritDoc} + * <p> + * Ignoring {@code contextFQN}, using {@code iOS}-{@link AbstractGraphicsDevice#getUniqueID()}. + * </p> + */ + @Override + protected final void updateGLXProcAddressTable(final String contextFQN, final GLDynamicLookupHelper dlh) { + if( null == dlh ) { + throw new GLException("No GLDynamicLookupHelper for "+this); + } + final AbstractGraphicsConfiguration aconfig = drawable.getNativeSurface().getGraphicsConfiguration(); + final AbstractGraphicsDevice adevice = aconfig.getScreen().getDevice(); + final String key = "iOS-"+adevice.getUniqueID(); + if (DEBUG) { + System.err.println(getThreadName() + ": Initializing EAGL extension address table: "+key); + } + ProcAddressTable table = null; + synchronized(mappedContextTypeObjectLock) { + table = mappedGLXProcAddress.get( key ); + } + if(null != table) { + cglExtProcAddressTable = (DummyGLExtProcAddressTable) table; + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext CGL ProcAddressTable reusing key("+key+") -> "+toHexString(table.hashCode())); + } + } else { + cglExtProcAddressTable = new DummyGLExtProcAddressTable(new GLProcAddressResolver()); + resetProcAddressTable(getCGLExtProcAddressTable(), dlh); + synchronized(mappedContextTypeObjectLock) { + mappedGLXProcAddress.put(key, getCGLExtProcAddressTable()); + if(DEBUG) { + System.err.println(getThreadName() + ": GLContext CGL ProcAddressTable mapping key("+key+") -> "+toHexString(getCGLExtProcAddressTable().hashCode())); + } + } + } + } + + @Override + protected final StringBuilder getPlatformExtensionsStringImpl() { + return new StringBuilder(); + } + + // Support for "mode switching" as described in IOSEAGLDrawable + public void setOpenGLMode(final GLBackendType mode) { + if (mode == openGLMode) { + return; + } + if (haveSetOpenGLMode) { + throw new GLException("Can't switch between using EAGL and ... more than once"); + } + destroyImpl(); + ((IOSEAGLDrawable)drawable).setOpenGLMode(mode); + if (DEBUG) { + System.err.println("IOSEAGLContext: Switching context mode " + openGLMode + " -> " + mode); + } + initOpenGLImpl(mode); + openGLMode = mode; + haveSetOpenGLMode = true; + } + public final GLBackendType getOpenGLMode() { return openGLMode; } + + protected void initOpenGLImpl(final GLBackendType backend) { + switch (backend) { + case CAEAGL_LAYER: + impl = new CAEAGLLayerImpl(); + break; + default: + throw new InternalError("Illegal implementation mode " + backend); + } + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append(" ["); + super.append(sb); + sb.append("] "); + return sb.toString(); + } + + class CAEAGLLayerImpl implements GLBackendImpl { + private final OffscreenLayerSurface backingLayerHost = null; + + @Override + public boolean isUsingCAEAGLLayer() { return null != backingLayerHost; } + + /** Only returns a valid UIView. If !UIView, return null and mark isFBO or isSurfaceless. */ + private long getUIViewHandle(final boolean[] isFBO, final boolean[] isSurfaceless) { + final long uiViewHandle; + if(drawable instanceof GLFBODrawableImpl) { + uiViewHandle = 0; + isFBO[0] = true; + isSurfaceless[0] = false; + if(DEBUG) { + System.err.println("UI viewHandle.1: GLFBODrawableImpl drawable: isFBO "+isFBO[0]+", isSurfaceless "+isSurfaceless[0]+", "+drawable.getClass().getName()+",\n\t"+drawable); + } + } else { + final long drawableHandle = drawable.getHandle(); + final boolean isUIView = IOSUtil.isUIView(drawableHandle); + final boolean isUIWindow = IOSUtil.isUIWindow(drawableHandle); + isFBO[0] = false; + isSurfaceless[0] = false; + + if( isUIView ) { + uiViewHandle = drawableHandle; + } else if( isUIWindow ) { + uiViewHandle = IOSUtil.GetUIView(drawableHandle, true /* only EAGL */); + } else if( isSurfaceless() ) { + isSurfaceless[0] = true; + uiViewHandle = 0; + } else { + throw new GLException("Drawable's handle neither NSView, NSWindow nor PBuffer: drawableHandle "+toHexString(drawableHandle)+", isNSView "+isUIView+", isNSWindow "+isUIWindow+", isFBO "+isFBO[0]+", "+drawable.getClass().getName()+",\n\t"+drawable); + } + if(DEBUG) { + System.err.println("NS viewHandle.2: drawableHandle "+toHexString(drawableHandle)+" -> nsViewHandle "+toHexString(uiViewHandle)+": isNSView "+isUIView+", isNSWindow "+isUIWindow+", isFBO "+isFBO[0]+", isSurfaceless "+isSurfaceless[0]+", "+drawable.getClass().getName()+",\n\t"+drawable); + } + } + return uiViewHandle; + } + + @Override + public long create(final long share, final int ctp, final int major, final int minor) { + long ctx = 0; + final MutableGraphicsConfiguration config = (MutableGraphicsConfiguration) drawable.getNativeSurface().getGraphicsConfiguration(); + final GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable)config.getChosenCapabilities(); + // Create new context + if (DEBUG) { + System.err.println("Share context for EAGL-based context is " + toHexString(share)); + } + final boolean isFBO = drawable instanceof GLFBODrawableImpl; + final int api = GLProfile2EAGLProfileValue(ctp, major, minor); + if( 0 != share ) { + ctx = EAGL.eaglCreateContextShared(api, EAGL.eaglGetSharegroup(share)); + } else { + ctx = EAGL.eaglCreateContext(api); + } + if (0 != ctx) { + final GLCapabilitiesImmutable fixedCaps; + if( isFBO ) { + fixedCaps = chosenCaps; + } else { + if( DEBUG ) { + System.err.println("Warning: CAEAGLLayer w/ non FBO caps"); + } + fixedCaps = chosenCaps; + } + if(DEBUG) { + System.err.println("NS create backingLayerHost: "+backingLayerHost); + System.err.println("NS create share: "+share); + System.err.println("NS create drawable type: "+drawable.getClass().getName()); + System.err.println("NS create chosenCaps: "+chosenCaps); + System.err.println("NS create fixedCaps: "+fixedCaps); + System.err.println("NS create drawable native-handle: "+toHexString(drawable.getHandle())); + System.err.println("NS create surface native-handle: "+toHexString(drawable.getNativeSurface().getSurfaceHandle())); + // Thread.dumpStack(); + } + config.setChosenCapabilities(fixedCaps); + if(DEBUG) { + System.err.println("EAGL create fixedCaps: "+fixedCaps); + } + } + return ctx; + } + + @Override + public boolean destroy(final long ctx) { + return EAGL.eaglDeleteContext(ctx, true /* releaseOnMainThread */); + } + + @Override + public void associateDrawable(final boolean bound) { + } + + @Override + public boolean makeCurrent(final long ctx) { + return EAGL.eaglMakeCurrentContext(ctx); + } + + @Override + public boolean release(final long ctx) { + try { + if( hasRendererQuirk(GLRendererQuirks.GLFlushBeforeRelease) && null != IOSEAGLContext.this.getGLProcAddressTable() ) { + gl.glFlush(); + } + } catch (final GLException gle) { + if(DEBUG) { + System.err.println("IOSEAGLContext.CGLImpl.release: INFO: glFlush() caught exception:"); + gle.printStackTrace(); + } + } + return EAGL.eaglMakeCurrentContext(0); + } + } + + @Override + protected Integer setSwapIntervalImpl2(final int interval) { + // TODO + return null; + } +} |