/* * Copyright (c) 2008 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. */ package jogamp.opengl.x11.glx; import javax.media.nativewindow.AbstractGraphicsConfiguration; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.AbstractGraphicsScreen; import javax.media.nativewindow.CapabilitiesChooser; import javax.media.nativewindow.CapabilitiesImmutable; import javax.media.nativewindow.GraphicsConfigurationFactory; import javax.media.nativewindow.VisualIDHolder; import javax.media.opengl.DefaultGLCapabilitiesChooser; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesChooser; import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import com.jogamp.common.nio.PointerBuffer; import com.jogamp.nativewindow.x11.X11GraphicsDevice; import com.jogamp.nativewindow.x11.X11GraphicsScreen; import jogamp.nativewindow.x11.X11Lib; import jogamp.nativewindow.x11.XVisualInfo; import jogamp.opengl.GLGraphicsConfigurationFactory; import jogamp.opengl.GLGraphicsConfigurationUtil; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** Subclass of GraphicsConfigurationFactory used when non-AWT toolkits are used on X11 platforms. Toolkits will likely need to delegate to this one to change the accepted and returned types of the GraphicsDevice and GraphicsConfiguration abstractions. */ public class X11GLXGraphicsConfigurationFactory extends GLGraphicsConfigurationFactory { static VisualIDHolder.VIDComparator XVisualIDComparator = new VisualIDHolder.VIDComparator(VisualIDHolder.VIDType.X11_XVISUAL); static GraphicsConfigurationFactory fallbackX11GraphicsConfigurationFactory = null; static void registerFactory() { final GraphicsConfigurationFactory newFactory = new X11GLXGraphicsConfigurationFactory(); final GraphicsConfigurationFactory oldFactory = GraphicsConfigurationFactory.registerFactory(com.jogamp.nativewindow.x11.X11GraphicsDevice.class, newFactory); if(oldFactory == newFactory) { throw new InternalError("GraphicsConfigurationFactory lifecycle impl. error"); } if(null == oldFactory) { throw new InternalError("Missing fallback GraphicsConfigurationFactory"); } fallbackX11GraphicsConfigurationFactory = oldFactory; } private X11GLXGraphicsConfigurationFactory() { } protected AbstractGraphicsConfiguration chooseGraphicsConfigurationImpl( CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested, CapabilitiesChooser chooser, AbstractGraphicsScreen absScreen) { if (!(absScreen instanceof X11GraphicsScreen)) { throw new IllegalArgumentException("Only X11GraphicsScreen are allowed here"); } if ( !(capsChosen instanceof GLCapabilitiesImmutable) ) { throw new IllegalArgumentException("This NativeWindowFactory accepts only GLCapabilities objects - chosen"); } if ( !(capsRequested instanceof GLCapabilitiesImmutable)) { throw new IllegalArgumentException("This NativeWindowFactory accepts only GLCapabilities objects - requested"); } if (chooser != null && !(chooser instanceof GLCapabilitiesChooser)) { throw new IllegalArgumentException("This NativeWindowFactory accepts only GLCapabilitiesChooser objects"); } if(!GLXUtil.isGLXAvailableOnServer((X11GraphicsDevice)absScreen.getDevice())) { if(null != fallbackX11GraphicsConfigurationFactory) { if(DEBUG) { System.err.println("No GLX available, fallback to "+fallbackX11GraphicsConfigurationFactory.getClass().getSimpleName()+" for: "+absScreen); } return fallbackX11GraphicsConfigurationFactory.chooseGraphicsConfiguration(capsChosen, capsRequested, chooser, absScreen); } throw new InternalError("No GLX and no fallback GraphicsConfigurationFactory available for: "+absScreen); } return chooseGraphicsConfigurationStatic((GLCapabilitiesImmutable)capsChosen, (GLCapabilitiesImmutable)capsRequested, (GLCapabilitiesChooser)chooser, (X11GraphicsScreen)absScreen); } protected static List getAvailableCapabilities(X11GLXDrawableFactory factory, AbstractGraphicsDevice device) { X11GLXDrawableFactory.SharedResource sharedResource = factory.getOrCreateSharedResource(device); if(null == sharedResource) { throw new GLException("Shared resource for device n/a: "+device); } final X11GraphicsScreen sharedScreen = (X11GraphicsScreen) sharedResource.getScreen(); final boolean isMultisampleAvailable = factory.isGLXMultisampleAvailable(sharedScreen.getDevice()); final X11GLXDrawable sharedDrawable = (X11GLXDrawable) sharedResource.getDrawable(); final GLCapabilitiesImmutable capsChosen = sharedDrawable.getChosenGLCapabilities(); final GLProfile glp = capsChosen.getGLProfile(); List availableCaps = null; if( sharedResource.isGLXVersionGreaterEqualOneThree() ) { availableCaps = getAvailableGLCapabilitiesFBConfig(sharedScreen, glp, isMultisampleAvailable); } if( null == availableCaps || availableCaps.isEmpty() ) { availableCaps = getAvailableGLCapabilitiesXVisual(sharedScreen, glp, isMultisampleAvailable); } if( null != availableCaps && availableCaps.size() > 1 ) { Collections.sort(availableCaps, XVisualIDComparator); } return availableCaps; } static List getAvailableGLCapabilitiesFBConfig(X11GraphicsScreen x11Screen, GLProfile glProfile, boolean isMultisampleAvailable) { PointerBuffer fbcfgsL = null; // Utilizing FBConfig // AbstractGraphicsDevice absDevice = x11Screen.getDevice(); long display = absDevice.getHandle(); int screen = x11Screen.getIndex(); int[] count = { -1 }; ArrayList availableCaps = new ArrayList(); fbcfgsL = GLX.glXChooseFBConfig(display, screen, null, 0, count, 0); if (fbcfgsL == null || fbcfgsL.limit()<=0) { if(DEBUG) { System.err.println("X11GLXGraphicsConfiguration.getAvailableGLCapabilitiesFBConfig: Failed glXChooseFBConfig ("+x11Screen+"): "+fbcfgsL+", "+count[0]); } return null; } for (int i = 0; i < fbcfgsL.limit(); i++) { if( !X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities(availableCaps, glProfile, display, fbcfgsL.get(i), GLGraphicsConfigurationUtil.ALL_BITS, isMultisampleAvailable) ) { if(DEBUG) { System.err.println("X11GLXGraphicsConfiguration.getAvailableGLCapabilitiesFBConfig: FBConfig invalid (2): ("+x11Screen+"): fbcfg: "+toHexString(fbcfgsL.get(i))); } } } return availableCaps; } static List getAvailableGLCapabilitiesXVisual(X11GraphicsScreen x11Screen, GLProfile glProfile, boolean isMultisampleAvailable) { AbstractGraphicsDevice absDevice = x11Screen.getDevice(); long display = absDevice.getHandle(); int screen = x11Screen.getIndex(); int[] count = new int[1]; XVisualInfo template = XVisualInfo.create(); template.setScreen(screen); XVisualInfo[] infos = X11Lib.XGetVisualInfo(display, X11Lib.VisualScreenMask, template, count, 0); if (infos == null || infos.length<1) { throw new GLException("Error while enumerating available XVisualInfos"); } ArrayList availableCaps = new ArrayList(); for (int i = 0; i < infos.length; i++) { if( !X11GLXGraphicsConfiguration.XVisualInfo2GLCapabilities(availableCaps, glProfile, display, infos[i], GLGraphicsConfigurationUtil.ALL_BITS, isMultisampleAvailable) ) { if(DEBUG) { System.err.println("X11GLXGraphicsConfiguration.getAvailableGLCapabilitiesXVisual: XVisual invalid: ("+x11Screen+"): fbcfg: "+toHexString(infos[i].getVisualid())); } } } return availableCaps; } static X11GLXGraphicsConfiguration chooseGraphicsConfigurationStatic(GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsReq, GLCapabilitiesChooser chooser, X11GraphicsScreen x11Screen) { if (x11Screen == null) { throw new IllegalArgumentException("AbstractGraphicsScreen is null"); } if (capsChosen == null) { capsChosen = new GLCapabilities(null); } X11GraphicsDevice x11Device = (X11GraphicsDevice) x11Screen.getDevice(); X11GLXDrawableFactory factory = (X11GLXDrawableFactory) GLDrawableFactory.getDesktopFactory(); capsChosen = GLGraphicsConfigurationUtil.fixGLCapabilities( capsChosen, factory.canCreateGLPbuffer(x11Device) ); boolean usePBuffer = capsChosen.isPBuffer(); X11GLXGraphicsConfiguration res = null; if( factory.isGLXVersionGreaterEqualOneThree(x11Device) ) { res = chooseGraphicsConfigurationFBConfig(capsChosen, capsReq, chooser, x11Screen); } if(null==res) { if(usePBuffer) { throw new GLException("Error: Couldn't create X11GLXGraphicsConfiguration based on FBConfig for "+capsChosen); } res = chooseGraphicsConfigurationXVisual(capsChosen, capsReq, chooser, x11Screen); } if(null==res) { throw new GLException("Error: Couldn't create X11GLXGraphicsConfiguration based on FBConfig and XVisual for "+capsChosen); } if(DEBUG) { System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationStatic("+x11Screen+","+capsChosen+"): "+res); } return res; } static X11GLXGraphicsConfiguration fetchGraphicsConfigurationFBConfig(X11GraphicsScreen x11Screen, int fbID, GLProfile glp) { final AbstractGraphicsDevice absDevice = x11Screen.getDevice(); final long display = absDevice.getHandle(); final int screen = x11Screen.getIndex(); final long fbcfg = X11GLXGraphicsConfiguration.glXFBConfigID2FBConfig(display, screen, fbID); if( 0 == fbcfg || !X11GLXGraphicsConfiguration.GLXFBConfigValid( display, fbcfg ) ) { if(DEBUG) { System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: Failed - GLX FBConfig invalid: ("+x11Screen+","+toHexString(fbID)+"): fbcfg: "+toHexString(fbcfg)); } return null; } final X11GLXDrawableFactory factory = (X11GLXDrawableFactory) GLDrawableFactory.getDesktopFactory(); final X11GLCapabilities caps = X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities(glp, display, fbcfg, true, true, true, factory.isGLXMultisampleAvailable(absDevice)); return new X11GLXGraphicsConfiguration(x11Screen, caps, caps, new DefaultGLCapabilitiesChooser()); } private static X11GLXGraphicsConfiguration chooseGraphicsConfigurationFBConfig(GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsReq, GLCapabilitiesChooser chooser, X11GraphicsScreen x11Screen) { int recommendedIndex = -1; PointerBuffer fbcfgsL = null; GLProfile glProfile = capsChosen.getGLProfile(); boolean onscreen = capsChosen.isOnscreen(); boolean usePBuffer = capsChosen.isPBuffer(); // Utilizing FBConfig // AbstractGraphicsDevice absDevice = x11Screen.getDevice(); long display = absDevice.getHandle(); int screen = x11Screen.getIndex(); final X11GLXDrawableFactory factory = (X11GLXDrawableFactory) GLDrawableFactory.getDesktopFactory(); final boolean isMultisampleAvailable = factory.isGLXMultisampleAvailable(absDevice); int[] attribs = X11GLXGraphicsConfiguration.GLCapabilities2AttribList(capsChosen, true, isMultisampleAvailable, display, screen); int[] count = { -1 }; ArrayList/**/ availableCaps = new ArrayList(); final int winattrmask = GLGraphicsConfigurationUtil.getWinAttributeBits(onscreen, usePBuffer); // 1st choice: get GLCapabilities based on users GLCapabilities setting recommendedIndex as preferred choice fbcfgsL = GLX.glXChooseFBConfig(display, screen, attribs, 0, count, 0); if (fbcfgsL != null && fbcfgsL.limit()>0) { for (int i = 0; i < fbcfgsL.limit(); i++) { if( !X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities(availableCaps, glProfile, display, fbcfgsL.get(i), winattrmask, isMultisampleAvailable) ) { if(DEBUG) { System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: FBConfig invalid (1): ("+x11Screen+","+capsChosen+"): fbcfg: "+toHexString(fbcfgsL.get(i))); } } } if(availableCaps.size() > 0) { recommendedIndex = capsChosen.isBackgroundOpaque() ? 0 : -1; // only use recommended idx if not translucent if (DEBUG) { System.err.println("glXChooseFBConfig recommended fbcfg " + toHexString(fbcfgsL.get(0)) + ", idx " + recommendedIndex); System.err.println("user caps " + capsChosen); System.err.println("fbcfg caps " + availableCaps.get(0)); } } else if (DEBUG) { System.err.println("glXChooseFBConfig no caps for recommended fbcfg " + toHexString(fbcfgsL.get(0))); System.err.println("user caps " + capsChosen); } } // 2nd choice: get all GLCapabilities available, no preferred recommendedIndex available if( 0 == availableCaps.size() ) { // reset .. recommendedIndex = -1; fbcfgsL = GLX.glXChooseFBConfig(display, screen, null, 0, count, 0); if (fbcfgsL == null || fbcfgsL.limit()<=0) { if(DEBUG) { System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: Failed glXChooseFBConfig ("+x11Screen+","+capsChosen+"): "+fbcfgsL+", "+count[0]); } return null; } for (int i = 0; i < fbcfgsL.limit(); i++) { if( !X11GLXGraphicsConfiguration.GLXFBConfig2GLCapabilities(availableCaps, glProfile, display, fbcfgsL.get(i), winattrmask, isMultisampleAvailable) ) { if(DEBUG) { System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: FBConfig invalid (2): ("+x11Screen+"): fbcfg: "+toHexString(fbcfgsL.get(i))); } } } } int chosenIndex = chooseCapabilities(chooser, capsChosen, availableCaps, recommendedIndex); if ( 0 > chosenIndex ) { if (DEBUG) { System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationFBConfig: failed, return null"); Thread.dumpStack(); } return null; } X11GLCapabilities chosenCaps = (X11GLCapabilities) availableCaps.get(chosenIndex); return new X11GLXGraphicsConfiguration(x11Screen, chosenCaps, capsReq, chooser); } private static X11GLXGraphicsConfiguration chooseGraphicsConfigurationXVisual(GLCapabilitiesImmutable capsChosen, GLCapabilitiesImmutable capsReq, GLCapabilitiesChooser chooser, X11GraphicsScreen x11Screen) { if (chooser == null) { chooser = new DefaultGLCapabilitiesChooser(); } GLProfile glProfile = capsChosen.getGLProfile(); final int winattrmask = GLGraphicsConfigurationUtil.getWinAttributeBits(capsChosen.isOnscreen(), false /* pbuffer */); ArrayList availableCaps = new ArrayList(); int recommendedIndex = -1; AbstractGraphicsDevice absDevice = x11Screen.getDevice(); long display = absDevice.getHandle(); int screen = x11Screen.getIndex(); final X11GLXDrawableFactory factory = (X11GLXDrawableFactory) GLDrawableFactory.getDesktopFactory(); final boolean isMultisampleAvailable = factory.isGLXMultisampleAvailable(absDevice); int[] attribs = X11GLXGraphicsConfiguration.GLCapabilities2AttribList(capsChosen, false, isMultisampleAvailable, display, screen); // 1st choice: get GLCapabilities based on users GLCapabilities setting recommendedIndex as preferred choice XVisualInfo recommendedVis = GLX.glXChooseVisual(display, screen, attribs, 0); if (DEBUG) { System.err.print("glXChooseVisual recommended "); if (recommendedVis == null) { System.err.println("null visual"); } else { System.err.println("visual id " + toHexString(recommendedVis.getVisualid())); } } // 2nd choice: get all GLCapabilities available, preferred recommendedIndex might be available if 1st choice was successful int[] count = new int[1]; XVisualInfo template = XVisualInfo.create(); template.setScreen(screen); XVisualInfo[] infos = X11Lib.XGetVisualInfo(display, X11Lib.VisualScreenMask, template, count, 0); if (infos == null || infos.length<1) { throw new GLException("Error while enumerating available XVisualInfos"); } for (int i = 0; i < infos.length; i++) { if( !X11GLXGraphicsConfiguration.XVisualInfo2GLCapabilities(availableCaps, glProfile, display, infos[i], winattrmask, isMultisampleAvailable) ) { if(DEBUG) { System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationXVisual: XVisual invalid: ("+x11Screen+"): fbcfg: "+toHexString(infos[i].getVisualid())); } } else { // Attempt to find the visual chosenIndex by glXChooseVisual, if not translucent if (capsChosen.isBackgroundOpaque() && recommendedVis != null && recommendedVis.getVisualid() == infos[i].getVisualid()) { recommendedIndex = availableCaps.size() - 1; } } } int chosenIndex = chooseCapabilities(chooser, capsChosen, availableCaps, recommendedIndex); if ( 0 > chosenIndex ) { if (DEBUG) { System.err.println("X11GLXGraphicsConfiguration.chooseGraphicsConfigurationXVisual: failed, return null"); Thread.dumpStack(); } return null; } X11GLCapabilities chosenCaps = (X11GLCapabilities) availableCaps.get(chosenIndex); return new X11GLXGraphicsConfiguration(x11Screen, chosenCaps, capsReq, chooser); } }