javax.media.nativewindow.GraphicsConfigurationFactory Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2009 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 javax.media.nativewindow;
import com.jogamp.common.util.ReflectionUtil;
import jogamp.nativewindow.Debug;
import jogamp.nativewindow.DefaultGraphicsConfigurationFactoryImpl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Provides the mechanism by which the graphics configuration for a
* window can be chosen before the window is created. The graphics
* configuration decides parameters related to hardware accelerated rendering such
* as the OpenGL pixel format.
* On some window systems (EGL/OpenKODE and X11 in particular) it is necessary to
* choose the graphics configuration early at window creation time.
* Note that the selection of the graphics configuration is an algorithm which does not have
* strong dependencies on the particular Java window toolkit in use
* (e.g., AWT) and therefore it is strongly desirable to factor this
* functionality out of the core {@link NativeWindowFactory} so that
* new window toolkits can replace just the {@link
* NativeWindowFactory} and reuse the graphics configuration selection
* algorithm provided by, for example, an OpenGL binding.
*/
public abstract class GraphicsConfigurationFactory {
protected static final boolean DEBUG;
private static class DeviceCapsType {
public final Class> deviceType;
public final Class> capsType;
private final int hash32;
public DeviceCapsType(Class> deviceType, Class> capsType) {
this.deviceType = deviceType;
this.capsType = capsType;
// 31 * x == (x << 5) - x
int hash32 = 31 + deviceType.hashCode();
hash32 = ((hash32 << 5) - hash32) + capsType.hashCode();
this.hash32 = hash32;
}
@Override
public final int hashCode() {
return hash32;
}
@Override
public final boolean equals(Object obj) {
if(this == obj) { return true; }
if (obj instanceof DeviceCapsType) {
DeviceCapsType dct = (DeviceCapsType)obj;
return deviceType == dct.deviceType && capsType == dct.capsType;
}
return false;
}
@Override
public final String toString() {
return "DeviceCapsType["+deviceType.getName()+", "+capsType.getName()+"]";
}
}
private static final Map registeredFactories;
private static final DeviceCapsType defaultDeviceCapsType;
static boolean initialized = false;
static {
DEBUG = Debug.debug("GraphicsConfiguration");
if(DEBUG) {
System.err.println(Thread.currentThread().getName()+" - Info: GraphicsConfigurationFactory.");
// Thread.dumpStack();
}
registeredFactories = Collections.synchronizedMap(new HashMap());
defaultDeviceCapsType = new DeviceCapsType(AbstractGraphicsDevice.class, CapabilitiesImmutable.class);
}
public static synchronized void initSingleton() {
if(!initialized) {
initialized = true;
if(DEBUG) {
System.err.println(Thread.currentThread().getName()+" - GraphicsConfigurationFactory.initSingleton()");
}
// Register the default no-op factory for arbitrary
// AbstractGraphicsDevice implementations, including
// AWTGraphicsDevice instances -- the OpenGL binding will take
// care of handling AWTGraphicsDevices on X11 platforms (as
// well as X11GraphicsDevices in non-AWT situations)
registerFactory(defaultDeviceCapsType.deviceType, defaultDeviceCapsType.capsType, new DefaultGraphicsConfigurationFactoryImpl());
if (NativeWindowFactory.TYPE_X11 == NativeWindowFactory.getNativeWindowType(true)) {
try {
ReflectionUtil.callStaticMethod("jogamp.nativewindow.x11.X11GraphicsConfigurationFactory",
"registerFactory", null, null, GraphicsConfigurationFactory.class.getClassLoader());
} catch (Exception e) {
throw new RuntimeException(e);
}
if(NativeWindowFactory.isAWTAvailable()) {
try {
ReflectionUtil.callStaticMethod("jogamp.nativewindow.x11.awt.X11AWTGraphicsConfigurationFactory",
"registerFactory", null, null, GraphicsConfigurationFactory.class.getClassLoader());
} catch (Exception e) { /* n/a */ }
}
}
}
}
public static synchronized void shutdown() {
if(initialized) {
initialized = false;
if(DEBUG) {
System.err.println(Thread.currentThread().getName()+" - GraphicsConfigurationFactory.shutdown()");
}
registeredFactories.clear();
}
}
protected static String getThreadName() {
return Thread.currentThread().getName();
}
protected static String toHexString(int val) {
return "0x" + Integer.toHexString(val);
}
protected static String toHexString(long val) {
return "0x" + Long.toHexString(val);
}
/** Creates a new NativeWindowFactory instance. End users do not
need to call this method. */
protected GraphicsConfigurationFactory() {
}
/**
* Returns the graphics configuration factory for use with the
* given device and capability.
*
* @see #getFactory(Class, Class)
*/
public static GraphicsConfigurationFactory getFactory(AbstractGraphicsDevice device, CapabilitiesImmutable caps) {
if (device == null) {
throw new IllegalArgumentException("null device");
}
if (caps == null) {
throw new IllegalArgumentException("null caps");
}
return getFactory(device.getClass(), caps.getClass());
}
/**
* Returns the graphics configuration factory for use with the
* given device and capability class.
*
* Note: Registered device types maybe classes or interfaces, where capabilities types are interfaces only.
*
*
*
* Pseudo code for finding a suitable factory is:
*
For-All devT := getTopDownDeviceTypes(deviceType)
For-All capsT := getTopDownCapabilitiesTypes(capabilitiesType)
f = factory.get(devT, capsT);
if(f) { return f; }
end
end
*
*
*
* @param deviceType the minimum capabilities class type accepted, must implement or extend {@link AbstractGraphicsDevice}
* @param capabilitiesType the minimum capabilities class type accepted, must implement or extend {@link CapabilitiesImmutable}
*
* @throws IllegalArgumentException if the deviceType does not implement {@link AbstractGraphicsDevice} or
* capabilitiesType does not implement {@link CapabilitiesImmutable}
*/
public static GraphicsConfigurationFactory getFactory(Class> deviceType, Class> capabilitiesType)
throws IllegalArgumentException, NativeWindowException
{
if (!(defaultDeviceCapsType.deviceType.isAssignableFrom(deviceType))) {
throw new IllegalArgumentException("Given class must implement AbstractGraphicsDevice");
}
if (!(defaultDeviceCapsType.capsType.isAssignableFrom(capabilitiesType))) {
throw new IllegalArgumentException("Given capabilities class must implement CapabilitiesImmutable");
}
if(DEBUG) {
Thread.dumpStack();
System.err.println("GraphicsConfigurationFactory.getFactory: "+deviceType.getName()+", "+capabilitiesType.getName());
dumpFactories();
}
final List> deviceTypes = getAllAssignableClassesFrom(defaultDeviceCapsType.deviceType, deviceType, false);
if(DEBUG) {
System.err.println("GraphicsConfigurationFactory.getFactory() deviceTypes: " + deviceTypes);
}
final List> capabilitiesTypes = getAllAssignableClassesFrom(defaultDeviceCapsType.capsType, capabilitiesType, true);
if(DEBUG) {
System.err.println("GraphicsConfigurationFactory.getFactory() capabilitiesTypes: " + capabilitiesTypes);
}
for(int j=0; j interfaceDevice = deviceTypes.get(j);
for(int i=0; i interfaceCaps = capabilitiesTypes.get(i);
final DeviceCapsType dct = new DeviceCapsType(interfaceDevice, interfaceCaps);
final GraphicsConfigurationFactory factory = registeredFactories.get(dct);
if (factory != null) {
if(DEBUG) {
System.err.println("GraphicsConfigurationFactory.getFactory() found "+dct+" -> "+factory);
}
return factory;
}
}
}
// Return the default
final GraphicsConfigurationFactory factory = registeredFactories.get(defaultDeviceCapsType);
if(DEBUG) {
System.err.println("GraphicsConfigurationFactory.getFactory() DEFAULT "+defaultDeviceCapsType+" -> "+factory);
}
return factory;
}
private static ArrayList> getAllAssignableClassesFrom(Class> superClassOrInterface, Class> fromClass, boolean interfacesOnly) {
// Using a todo list avoiding a recursive loop!
final ArrayList> inspectClasses = new ArrayList>();
final ArrayList> resolvedInterfaces = new ArrayList>();
inspectClasses.add(fromClass);
for(int j=0; j clazz = inspectClasses.get(j);
getAllAssignableClassesFrom(superClassOrInterface, clazz, interfacesOnly, resolvedInterfaces, inspectClasses);
}
return resolvedInterfaces;
}
private static void getAllAssignableClassesFrom(Class> superClassOrInterface, Class> fromClass, boolean interfacesOnly, List> resolvedInterfaces, List> inspectClasses) {
final ArrayList> types = new ArrayList>();
if( superClassOrInterface.isAssignableFrom(fromClass) && !resolvedInterfaces.contains(fromClass)) {
if( !interfacesOnly || fromClass.isInterface() ) {
types.add(fromClass);
}
}
types.addAll(Arrays.asList(fromClass.getInterfaces()));
for(int i=0; i iface = types.get(i);
if( superClassOrInterface.isAssignableFrom(iface) && !resolvedInterfaces.contains(iface) ) {
resolvedInterfaces.add(iface);
if( !superClassOrInterface.equals(iface) && !inspectClasses.contains(iface) ) {
inspectClasses.add(iface); // safe add to todo list, avoiding a recursive nature
}
}
}
final Class> parentClass = fromClass.getSuperclass();
if( null != parentClass && superClassOrInterface.isAssignableFrom(parentClass) && !inspectClasses.contains(parentClass) ) {
inspectClasses.add(parentClass); // safe add to todo list, avoiding a recursive nature
}
}
private static void dumpFactories() {
Set dcts = registeredFactories.keySet();
int i=0;
for(Iterator iter = dcts.iterator(); iter.hasNext(); ) {
DeviceCapsType dct = iter.next();
System.err.println("Factory #"+i+": "+dct+" -> "+registeredFactories.get(dct));
i++;
}
}
/**
* Registers a GraphicsConfigurationFactory handling
* the given graphics device and capability class.
*
* This does not need to be called by end users, only implementors of new
* GraphicsConfigurationFactory subclasses.
*
*
*
* Note: Registered device types maybe classes or interfaces, where capabilities types are interfaces only.
*
*
* See {@link #getFactory(Class, Class)} for a description of the find algorithm.
*
* @param deviceType the minimum capabilities class type accepted, must implement or extend interface {@link AbstractGraphicsDevice}
* @param capabilitiesType the minimum capabilities class type accepted, must extend interface {@link CapabilitiesImmutable}
* @return the previous registered factory, or null if none
* @throws IllegalArgumentException if the given class does not implement AbstractGraphicsDevice
*/
protected static GraphicsConfigurationFactory registerFactory(Class> abstractGraphicsDeviceImplementor, Class> capabilitiesType, GraphicsConfigurationFactory factory)
throws IllegalArgumentException
{
if (!(defaultDeviceCapsType.deviceType.isAssignableFrom(abstractGraphicsDeviceImplementor))) {
throw new IllegalArgumentException("Given device class must implement AbstractGraphicsDevice");
}
if (!(defaultDeviceCapsType.capsType.isAssignableFrom(capabilitiesType))) {
throw new IllegalArgumentException("Given capabilities class must implement CapabilitiesImmutable");
}
final DeviceCapsType dct = new DeviceCapsType(abstractGraphicsDeviceImplementor, capabilitiesType);
final GraphicsConfigurationFactory prevFactory;
if(null == factory) {
prevFactory = registeredFactories.remove(dct);
if(DEBUG) {
System.err.println("GraphicsConfigurationFactory.registerFactory() remove "+dct+
", deleting: "+prevFactory);
}
} else {
prevFactory = registeredFactories.put(dct, factory);
if(DEBUG) {
System.err.println("GraphicsConfigurationFactory.registerFactory() put "+dct+" -> "+factory+
", overridding: "+prevFactory);
}
}
return prevFactory;
}
/**
* Selects a graphics configuration on the specified graphics
* device compatible with the supplied {@link Capabilities}. Some
* platforms (e.g.: X11, EGL, KD) require the graphics configuration
* to be specified when the native window is created.
* These architectures have seperated their device, screen, window and drawable
* context and hence are capable of quering the capabilities for each screen.
* A fully established window is not required.
*
* Other platforms (e.g. Windows, MacOSX) don't offer the mentioned seperation
* and hence need a fully established window and it's drawable.
* Here the validation of the capabilities is performed later.
* In this case, the AbstractGraphicsConfiguration implementation
* must allow an overwrite of the Capabilites, for example
* {@link DefaultGraphicsConfiguration#setChosenCapabilities DefaultGraphicsConfiguration.setChosenCapabilities(..)}.
*
*
*
* This method is mainly intended to be both used and implemented by the
* OpenGL binding.
*
* The concrete data type of the passed graphics device and
* returned graphics configuration must be specified in the
* documentation binding this particular API to the underlying
* window toolkit. The Reference Implementation accepts {@link
* com.jogamp.nativewindow.awt.AWTGraphicsDevice AWTGraphicsDevice} objects and returns {@link
* com.jogamp.nativewindow.awt.AWTGraphicsConfiguration AWTGraphicsConfiguration} objects. On
* X11 platforms where the AWT is not in use, it also accepts
* {@link com.jogamp.nativewindow.x11.X11GraphicsDevice
* X11GraphicsDevice} objects and returns {@link
* com.jogamp.nativewindow.x11.X11GraphicsConfiguration
* X11GraphicsConfiguration} objects.
*
* @param capsChosen the intermediate chosen capabilities to be refined by this implementation, may be equal to capsRequested
* @param capsRequested the original requested capabilities
* @param chooser the choosing implementation
* @param screen the referring Screen
* @param nativeVisualID if not {@link VisualIDHolder#VID_UNDEFINED} it reflects a pre-chosen visualID of the native platform's windowing system.
* @return the complete GraphicsConfiguration
*
* @throws IllegalArgumentException if the data type of the passed
* AbstractGraphicsDevice is not supported by this
* NativeWindowFactory.
* @throws NativeWindowException if any window system-specific errors caused
* the selection of the graphics configuration to fail.
*
* @see javax.media.nativewindow.GraphicsConfigurationFactory#chooseGraphicsConfiguration(Capabilities, CapabilitiesChooser, AbstractGraphicsScreen)
* @see javax.media.nativewindow.DefaultGraphicsConfiguration#setChosenCapabilities(Capabilities caps)
*/
public final AbstractGraphicsConfiguration
chooseGraphicsConfiguration(CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested,
CapabilitiesChooser chooser,
AbstractGraphicsScreen screen, int nativeVisualID)
throws IllegalArgumentException, NativeWindowException {
if(null==capsChosen) {
throw new NativeWindowException("Chosen Capabilities are null");
}
if(null==capsRequested) {
throw new NativeWindowException("Requested Capabilities are null");
}
if(null==screen) {
throw new NativeWindowException("Screen is null");
}
AbstractGraphicsDevice device = screen.getDevice();
if(null==device) {
throw new NativeWindowException("Screen's Device is null");
}
device.lock();
try {
return chooseGraphicsConfigurationImpl(capsChosen, capsRequested, chooser, screen, nativeVisualID);
} finally {
device.unlock();
}
}
protected abstract AbstractGraphicsConfiguration
chooseGraphicsConfigurationImpl(CapabilitiesImmutable capsChosen, CapabilitiesImmutable capsRequested,
CapabilitiesChooser chooser, AbstractGraphicsScreen screen, int nativeVisualID)
throws IllegalArgumentException, NativeWindowException;
}