/* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. * Copyright (c) 2010 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: * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistribution 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. * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use * in the design, construction, operation or maintenance of any nuclear * facility. * * Sun gratefully acknowledges that this software was originally authored * and developed by Kenneth Bradley Russell and Christopher John Kline. */ package jogamp.opengl.macosx.cgl; import java.nio.Buffer; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import java.util.HashMap; import java.util.HashSet; import java.util.List; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.DefaultGraphicsScreen; import javax.media.nativewindow.NativeSurface; import javax.media.nativewindow.ProxySurface; import javax.media.nativewindow.UpstreamSurfaceHook; import javax.media.opengl.GL; 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.GLException; import javax.media.opengl.GLProfile; import jogamp.nativewindow.WrappedSurface; import jogamp.nativewindow.macosx.OSXDummyUpstreamSurfaceHook; import jogamp.opengl.DesktopGLDynamicLookupHelper; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableFactoryImpl; import jogamp.opengl.GLDrawableImpl; import jogamp.opengl.GLDynamicLookupHelper; import jogamp.opengl.GLGraphicsConfigurationUtil; import jogamp.opengl.SharedResourceRunner; import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.ReflectionUtil; import com.jogamp.nativewindow.MutableGraphicsConfiguration; import com.jogamp.nativewindow.macosx.MacOSXGraphicsDevice; import com.jogamp.opengl.GLExtensions; import com.jogamp.opengl.GLRendererQuirks; public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl { private static DesktopGLDynamicLookupHelper macOSXCGLDynamicLookupHelper = null; public MacOSXCGLDrawableFactory() { super(); synchronized(MacOSXCGLDrawableFactory.class) { if(null==macOSXCGLDynamicLookupHelper) { DesktopGLDynamicLookupHelper tmp = null; try { tmp = new DesktopGLDynamicLookupHelper(new MacOSXCGLDynamicLibraryBundleInfo()); } catch (final GLException gle) { if(DEBUG) { gle.printStackTrace(); } } if(null!=tmp && tmp.isLibComplete()) { macOSXCGLDynamicLookupHelper = tmp; /** FIXME ?? CGL.getCGLProcAddressTable().reset(macOSXCGLDynamicLookupHelper); */ } } } defaultDevice = new MacOSXGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); if(null!=macOSXCGLDynamicLookupHelper) { // Register our GraphicsConfigurationFactory implementations // The act of constructing them causes them to be registered MacOSXCGLGraphicsConfigurationFactory.registerFactory(); if(GLProfile.isAWTAvailable()) { try { ReflectionUtil.callStaticMethod("jogamp.opengl.macosx.cgl.awt.MacOSXAWTCGLGraphicsConfigurationFactory", "registerFactory", null, null, getClass().getClassLoader()); } catch (final Exception jre) { /* n/a .. */ } } sharedMap = new HashMap(); } } @Override protected final boolean isComplete() { return null != macOSXCGLDynamicLookupHelper; } @Override protected final void shutdownImpl() { if( DEBUG ) { System.err.println("MacOSXCGLDrawableFactory.shutdown"); } if(null != sharedMap) { sharedMap.clear(); sharedMap = null; } defaultDevice = null; /** * Pulling away the native library may cause havoc .. * macOSXCGLDynamicLookupHelper.destroy(); */ macOSXCGLDynamicLookupHelper = null; } @Override public GLDynamicLookupHelper getGLDynamicLookupHelper(final int profile) { return macOSXCGLDynamicLookupHelper; } private HashMap sharedMap = new HashMap(); private MacOSXGraphicsDevice defaultDevice; static class SharedResource implements SharedResourceRunner.Resource { // private MacOSXCGLDrawable drawable; // private MacOSXCGLContext context; private final GLRendererQuirks glRendererQuirks; MacOSXGraphicsDevice device; boolean valid; boolean hasNPOTTextures; boolean hasRECTTextures; boolean hasAppleFloatPixels; SharedResource(final MacOSXGraphicsDevice device, final boolean valid, final boolean hasNPOTTextures, final boolean hasRECTTextures, final boolean hasAppletFloatPixels /* MacOSXCGLDrawable draw, MacOSXCGLContext ctx */, final GLRendererQuirks glRendererQuirks) { // drawable = draw; // this.context = ctx; this.glRendererQuirks = glRendererQuirks; this.device = device; this.valid = valid; this.hasNPOTTextures = hasNPOTTextures; this.hasRECTTextures = hasRECTTextures; this.hasAppleFloatPixels = hasAppletFloatPixels; } @Override public final boolean isAvailable() { return valid; } @Override public final MacOSXGraphicsDevice getDevice() { return device; } // final MacOSXCGLContext getContext() { return context; } final boolean isNPOTTextureAvailable() { return hasNPOTTextures; } final boolean isRECTTextureAvailable() { return hasRECTTextures; } final boolean isAppleFloatPixelsAvailable() { return hasAppleFloatPixels; } @Override public final AbstractGraphicsScreen getScreen() { return null; } @Override public final GLDrawableImpl getDrawable() { return null; } @Override public GLContextImpl getContext() { return null; } @Override public GLRendererQuirks getRendererQuirks(final GLProfile glp) { return glRendererQuirks; } } @Override public final AbstractGraphicsDevice getDefaultDevice() { return defaultDevice; } @Override public final boolean getIsDeviceCompatible(final AbstractGraphicsDevice device) { if(null!=macOSXCGLDynamicLookupHelper && device instanceof MacOSXGraphicsDevice) { return true; } return false; } private final HashSet devicesTried = new HashSet(); private boolean getDeviceTried(final String connection) { synchronized (devicesTried) { return devicesTried.contains(connection); } } private void addDeviceTried(final String connection) { synchronized (devicesTried) { devicesTried.add(connection); } } private void removeDeviceTried(final String connection) { synchronized (devicesTried) { devicesTried.remove(connection); } } @Override protected final SharedResource getOrCreateSharedResourceImpl(final AbstractGraphicsDevice adevice) { final String connection = adevice.getConnection(); SharedResource sr; synchronized(sharedMap) { sr = sharedMap.get(connection); } if(null==sr && !getDeviceTried(connection)) { addDeviceTried(connection); final MacOSXGraphicsDevice sharedDevice = new MacOSXGraphicsDevice(adevice.getUnitID()); GLRendererQuirks glRendererQuirks = null; boolean isValid = false; boolean hasNPOTTextures = false; boolean hasRECTTextures = false; boolean hasAppleFloatPixels = false; { final GLProfile glp = GLProfile.get(sharedDevice, GLProfile.GL_PROFILE_LIST_MIN_DESKTOP, false); if (null == glp) { throw new GLException("Couldn't get default GLProfile for device: "+sharedDevice); } final GLCapabilitiesImmutable caps = new GLCapabilities(glp); final GLDrawableImpl sharedDrawable = createOnscreenDrawableImpl(createDummySurfaceImpl(sharedDevice, false, caps, caps, null, 64, 64)); sharedDrawable.setRealized(true); final MacOSXCGLContext sharedContext = (MacOSXCGLContext) sharedDrawable.createContext(null); if (null == sharedContext) { throw new GLException("Couldn't create shared context for drawable: "+sharedDrawable); } try { isValid = GLContext.CONTEXT_NOT_CURRENT != sharedContext.makeCurrent(); // could cause exception if(isValid) { final GL gl = sharedContext.getGL(); hasNPOTTextures = gl.isNPOTTextureAvailable(); hasRECTTextures = gl.isExtensionAvailable(GLExtensions.EXT_texture_rectangle); hasAppleFloatPixels = gl.isExtensionAvailable(GLExtensions.APPLE_float_pixels); glRendererQuirks = sharedContext.getRendererQuirks(); } } catch (final GLException gle) { if (DEBUG) { System.err.println("MacOSXCGLDrawableFactory.createShared: INFO: makeCurrent caught exception:"); gle.printStackTrace(); } } finally { try { sharedContext.destroy(); } catch (final GLException gle) { if (DEBUG) { System.err.println("MacOSXCGLDrawableFactory.createShared: INFO: destroy caught exception:"); gle.printStackTrace(); } } } sharedDrawable.setRealized(false); } sr = new SharedResource(sharedDevice, isValid, hasNPOTTextures, hasRECTTextures, hasAppleFloatPixels, glRendererQuirks); synchronized(sharedMap) { sharedMap.put(connection, sr); } removeDeviceTried(connection); if (DEBUG) { System.err.println("MacOSXCGLDrawableFactory.createShared: device: " + sharedDevice); System.err.println("MacOSXCGLDrawableFactory.createShared: context: madeCurrent " + isValid + ", NPOT "+hasNPOTTextures+ ", RECT "+hasRECTTextures+", FloatPixels "+hasAppleFloatPixels+", "+glRendererQuirks); } } return sr; } @Override protected final Thread getSharedResourceThread() { return null; } @Override protected List getAvailableCapabilitiesImpl(final AbstractGraphicsDevice device) { return MacOSXCGLGraphicsConfiguration.getAvailableCapabilities(this, device); } @Override protected GLDrawableImpl createOnscreenDrawableImpl(final NativeSurface target) { if (target == null) { throw new IllegalArgumentException("Null target"); } return new MacOSXOnscreenCGLDrawable(this, target); } @Override protected GLDrawableImpl createOffscreenDrawableImpl(final NativeSurface target) { final MutableGraphicsConfiguration config = (MutableGraphicsConfiguration) target.getGraphicsConfiguration(); final GLCapabilitiesImmutable caps = (GLCapabilitiesImmutable) config.getChosenCapabilities(); if(!caps.isPBuffer()) { // Actual implementation is using PBuffer ... final GLCapabilities modCaps = (GLCapabilities) caps.cloneMutable(); modCaps.setPBuffer(true); modCaps.setBitmap(false); config.setChosenCapabilities(modCaps); return new MacOSXOffscreenCGLDrawable(this, target); } return new MacOSXPbufferCGLDrawable(this, target); } @Override public boolean canCreateGLPbuffer(final AbstractGraphicsDevice device, final GLProfile glp) { if( glp.isGL2() ) { // OSX only supports pbuffer w/ compatible, non-core, context. return true; } else { return false; } } @Override protected ProxySurface createMutableSurfaceImpl(final AbstractGraphicsDevice deviceReq, final boolean createNewDevice, final GLCapabilitiesImmutable capsChosen, final GLCapabilitiesImmutable capsRequested, final GLCapabilitiesChooser chooser, final UpstreamSurfaceHook upstreamHook) { final MacOSXGraphicsDevice device; if( createNewDevice || !(deviceReq instanceof MacOSXGraphicsDevice) ) { device = new MacOSXGraphicsDevice(deviceReq.getUnitID()); } else { device = (MacOSXGraphicsDevice)deviceReq; } final AbstractGraphicsScreen screen = new DefaultGraphicsScreen(device, 0); final MacOSXCGLGraphicsConfiguration config = MacOSXCGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsChosen, capsRequested, chooser, screen, true); if(null == config) { throw new GLException("Choosing GraphicsConfiguration failed w/ "+capsChosen+" on "+screen); } return new WrappedSurface(config, 0, upstreamHook, createNewDevice); } @Override public final ProxySurface createDummySurfaceImpl(final AbstractGraphicsDevice deviceReq, final boolean createNewDevice, GLCapabilitiesImmutable chosenCaps, final GLCapabilitiesImmutable requestedCaps, final GLCapabilitiesChooser chooser, final int width, final int height) { chosenCaps = GLGraphicsConfigurationUtil.fixOnscreenGLCapabilities(chosenCaps); return createMutableSurfaceImpl(deviceReq, createNewDevice, chosenCaps, requestedCaps, chooser, new OSXDummyUpstreamSurfaceHook(width, height)); } @Override public final ProxySurface createSurfacelessImpl(final AbstractGraphicsDevice deviceReq, final boolean createNewDevice, final GLCapabilitiesImmutable chosenCaps, final GLCapabilitiesImmutable requestedCaps, final GLCapabilitiesChooser chooser, final int width, final int height) { throw new UnsupportedOperationException("TODO"); } @Override protected ProxySurface createProxySurfaceImpl(final AbstractGraphicsDevice deviceReq, final int screenIdx, final long windowHandle, final GLCapabilitiesImmutable capsRequested, final GLCapabilitiesChooser chooser, final UpstreamSurfaceHook upstream) { final MacOSXGraphicsDevice device = new MacOSXGraphicsDevice(deviceReq.getUnitID()); final AbstractGraphicsScreen screen = new DefaultGraphicsScreen(device, screenIdx); final MacOSXCGLGraphicsConfiguration config = MacOSXCGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsRequested, capsRequested, chooser, screen, true); return new WrappedSurface(config, windowHandle, upstream, true); } @Override protected GLContext createExternalGLContextImpl() { return MacOSXExternalCGLContext.create(this); } @Override public boolean canCreateExternalGLDrawable(final AbstractGraphicsDevice device) { return false; } @Override protected GLDrawable createExternalGLDrawableImpl() { // FIXME throw new GLException("Not yet implemented"); } //------------------------------------------------------ // Gamma-related functionality // private static final int GAMMA_RAMP_LENGTH = 256; /** Returns the length of the computed gamma ramp for this OS and hardware. Returns 0 if gamma changes are not supported. */ @Override protected int getGammaRampLength(final NativeSurface surface) { return GAMMA_RAMP_LENGTH; } @Override protected boolean setGammaRamp(final NativeSurface surface, final float[] ramp) { final FloatBuffer rampNIO = Buffers.newDirectFloatBuffer(ramp); return CGL.setGammaRamp(ramp.length, rampNIO, rampNIO, rampNIO); } @Override protected Buffer getGammaRamp(final NativeSurface surface) { return ShortBuffer.allocate(0); // return a dummy gamma ramp default for reset } @Override protected void resetGammaRamp(final NativeSurface surface, final Buffer originalGammaRamp) { CGL.resetGammaRamp(); } @Override protected final void resetGammaRamp(final DeviceScreenID deviceScreenID, final Buffer originalGammaRamp) { CGL.resetGammaRamp(); } }